- Implemented the "O Image Relief Plane" Geometry Nodes group to create a fixed-base image relief using depth images and various parameters. - Created the "O Shadeless" shader node group to display color without scene lighting while preserving shadows and reflections. - Introduced a preview feature for node groups, allowing for interactive visualization of node arrangements in an HTML format. - Added a template HTML file for rendering node previews with customizable views and controls.
72 lines
3.3 KiB
Python
72 lines
3.3 KiB
Python
"""Reload saved assets and evaluate geometry in the target Blender process."""
|
|
from math import isfinite
|
|
from pathlib import Path
|
|
|
|
import bpy
|
|
|
|
|
|
ASSET_PATH = Path(__file__).resolve().parents[2] / "src/anyimage/assets/O_AnyImage.blend"
|
|
GEOMETRY_NAMES = {
|
|
"O Image Plane", "O Image Depth Plane", "O Image Relief Plane",
|
|
"O Image Cutout", "O Image Depth Cutout", "O Image Depth Balloon",
|
|
}
|
|
SHADER_NAMES = {"O Image Layer", "O Image Depth Layer", "O Shadeless"}
|
|
|
|
|
|
def check_asset_library(asset_path):
|
|
"""Check library completeness and finite, nonempty evaluated geometry."""
|
|
with bpy.data.libraries.load(str(asset_path), link=True) as (source, target):
|
|
assert set(source.node_groups) == GEOMETRY_NAMES | SHADER_NAMES
|
|
target.node_groups = list(source.node_groups)
|
|
depth_image = bpy.data.images.new("Asset Check Depth", width=8, height=8, float_buffer=True)
|
|
depth_image.colorspace_settings.name = "Non-Color"
|
|
depth_image.pixels.foreach_set([
|
|
value for y in range(8) for x in range(8)
|
|
for value in ((x + 0.5) / 8 - 0.5, (y + 0.5) / 8 - 0.5, 2.0, 1.0)
|
|
])
|
|
for node_group in target.node_groups:
|
|
expected_type = "GeometryNodeTree" if node_group.name in GEOMETRY_NAMES else "ShaderNodeTree"
|
|
assert node_group.bl_idname == expected_type and node_group.library is not None, node_group.name
|
|
if expected_type == "ShaderNodeTree":
|
|
continue
|
|
mesh = bpy.data.meshes.new("Asset Check Plane")
|
|
mesh.from_pydata([(-0.5, -0.5, 0), (0.5, -0.5, 0), (0.5, 0.5, 0), (-0.5, 0.5, 0)], [], [(0, 1, 2, 3)])
|
|
uv = mesh.uv_layers.new(name="UVMap")
|
|
for loop in mesh.loops:
|
|
position = mesh.vertices[loop.vertex_index].co
|
|
uv.data[loop.index].uv = (position.x + 0.5, position.y + 0.5)
|
|
profile = mesh.attributes.new("o_balloon", "FLOAT", "POINT")
|
|
for value in profile.data:
|
|
value.value = 0.2
|
|
obj = bpy.data.objects.new("Asset Check", mesh)
|
|
bpy.context.collection.objects.link(obj)
|
|
modifier = obj.modifiers.new("Asset Check", "NODES")
|
|
modifier.node_group = node_group
|
|
values = {"Subdivide": 0, "Thickness": 0.2, "Reference Depth": 2.0,
|
|
"Depth Image": depth_image, "Uniform Scale": 1.0, "Normal Smooth": 0}
|
|
for item in node_group.interface.items_tree:
|
|
if item.item_type == "SOCKET" and item.in_out == "INPUT" and item.name in values:
|
|
modifier[item.identifier] = values[item.name]
|
|
obj.update_tag(refresh={"DATA"})
|
|
bpy.context.view_layer.update()
|
|
evaluated = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
|
result = evaluated.to_mesh()
|
|
try:
|
|
assert len(result.vertices) and len(result.polygons), node_group.name
|
|
assert all(isfinite(value) for vertex in result.vertices for value in vertex.co), node_group.name
|
|
finally:
|
|
evaluated.to_mesh_clear()
|
|
bpy.data.objects.remove(obj, do_unlink=True)
|
|
bpy.data.meshes.remove(mesh)
|
|
bpy.data.images.remove(depth_image)
|
|
print(f"Checked {len(target.node_groups)} saved node groups in Blender {bpy.app.version_string}")
|
|
|
|
|
|
def main():
|
|
bpy.ops.wm.read_factory_settings(use_empty=True)
|
|
check_asset_library(ASSET_PATH)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|