Add tests for foreground preservation and refactor geometry node asset

- Implement test to ensure foreground preserves source RGB and Ben2 alpha in WorkerTest.
- Remove outdated tests related to background distance and original background depth.
- Update geometry node asset to reflect new naming conventions and structure.
- Introduce validation for depth plane asset, ensuring correct input/output configurations and attribute handling.
- Adjust confidence and edge parameters in geometry node asset to support a wider range of values.
This commit is contained in:
2026-08-11 23:25:51 +08:00
parent 07d836d9ad
commit 7cd5681a8b
15 changed files with 1296 additions and 660 deletions

View File

@@ -0,0 +1,249 @@
from pathlib import Path
import sys
import bpy
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
from snapform.image_depth import (
DEPTH_PLANE_MATERIAL_NODE_GROUP_NAME,
SURFACE_MATERIAL_NODE_GROUP_NAME,
add_depth_plane_processing,
create_material,
load_depth_plane_node_group,
modifier_input_identifier,
)
def main():
bpy.ops.wm.read_factory_settings(use_empty=True)
group = load_depth_plane_node_group()
background_depth = next(
item
for item in group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == "Background Depth"
)
assert background_depth.subtype == "DISTANCE"
geometry_input = next(
item
for item in group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == "Geometry"
)
assert geometry_input.structure_type == "SINGLE"
all_inputs = [
(item.name, getattr(item.parent, "name", None), item.hide_in_modifier)
for item in group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
]
visible_inputs = [name for name, parent, hidden in all_inputs if not hidden]
assert visible_inputs[:6] == [
"Geometry",
"Displacement",
"Depth Scale",
"Subdivision",
"Remove Background",
"Background Depth",
]
remove_background = next(
item
for item in group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == "Remove Background"
)
assert remove_background.socket_type == "NodeSocketBool"
assert remove_background.is_panel_toggle
assert remove_background.parent.name == "Remove Background"
assert background_depth.parent == remove_background.parent
depth_scale = next(
item
for item in group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == "Depth Scale"
)
assert depth_scale.default_value == 1.0
assert depth_scale.min_value < 0.0
assert depth_scale.structure_type == "SINGLE"
stores = {
node.inputs["Name"].default_value: node
for node in group.nodes
if node.bl_idname == "GeometryNodeStoreNamedAttribute"
}
attribute_names = (
"o_remove_background",
"o_material_displacement",
"o_background_depth",
"o_depth_scale",
)
for name in attribute_names:
assert stores[name].domain == "POINT"
assert not any(
node.bl_idname == "GeometryNodeGeometryToInstance"
for node in group.nodes
)
mesh = bpy.data.meshes.new("Depth Plane")
mesh.from_pydata(
[(-1, -1, 0), (1, -1, 0), (-1, 1, 0), (1, 1, 0)],
[],
[(0, 1, 3, 2)],
)
obj = bpy.data.objects.new("Depth Plane", mesh)
bpy.context.scene.collection.objects.link(obj)
depth = bpy.data.images.new("Depth", width=2, height=2, float_buffer=True)
depth.pixels[:] = [0.25, 0.25, 0.25, 1.0] * 4
foreground = bpy.data.images.new("Foreground", width=2, height=2)
foreground.pixels[:] = [0.8, 0.2, 0.1, 1.0] * 4
foreground.alpha_mode = "CHANNEL_PACKED"
material = create_material(
"Depth Plane",
foreground,
1.0,
displacement_image=depth,
foreground_image=foreground,
)
mesh.materials.append(material)
material_group = bpy.data.node_groups[DEPTH_PLANE_MATERIAL_NODE_GROUP_NAME]
interface_inputs = [
item.name
for item in material_group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
]
interface_outputs = [
item.name
for item in material_group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "OUTPUT"
]
assert interface_inputs == [
"Color",
"Alpha",
"Depth",
"Bump Scale",
]
assert interface_outputs == [
"Color",
"Roughness",
"Alpha",
"Normal",
"Displacement",
]
depth_input = next(
item
for item in material_group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == "Depth"
)
assert depth_input.socket_type == "NodeSocketFloat"
bump_scale = next(
item
for item in material_group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == "Bump Scale"
)
assert bump_scale.default_value == 1.0
assert bump_scale.min_value < 0.0
material_group_nodes = [
node
for node in material.node_tree.nodes
if node.bl_idname == "ShaderNodeGroup"
and node.node_tree == material_group
]
assert len(material_group_nodes) == 1
assert material_group_nodes[0].name == "O DA3 Depth Plane"
attributes = [
node
for node in material_group.nodes
if node.bl_idname == "ShaderNodeAttribute"
]
assert len(attributes) == 4
assert all(node.attribute_type == "GEOMETRY" for node in attributes)
assert not any(
node.bl_idname == "ShaderNodeAttribute"
for node in material.node_tree.nodes
)
forbidden_outer_nodes = {
"ShaderNodeAttribute",
"ShaderNodeBump",
"ShaderNodeDisplacement",
"ShaderNodeFloatCurve",
"ShaderNodeMath",
"ShaderNodeMix",
}
assert not any(
node.bl_idname in forbidden_outer_nodes
for node in material.node_tree.nodes
)
surface_material = create_material("Surface", foreground, 1.0)
surface_group = bpy.data.node_groups[SURFACE_MATERIAL_NODE_GROUP_NAME]
surface_group_nodes = [
node
for node in surface_material.node_tree.nodes
if node.bl_idname == "ShaderNodeGroup"
and node.node_tree == surface_group
]
assert len(surface_group_nodes) == 1
assert surface_group_nodes[0].name == "O DA3 Surface"
surface_depth_input = next(
item
for item in surface_group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == "Depth"
)
assert surface_depth_input.socket_type == "NodeSocketFloat"
assert not any(
node.bl_idname in forbidden_outer_nodes
for node in surface_material.node_tree.nodes
)
modifier = add_depth_plane_processing(obj, depth, foreground, material)
modifier[modifier_input_identifier(group, "Depth Scale")] = -2.0
for name, expected in (
("Frame Start", 1),
("Frames", 1),
("Cyclic", False),
):
assert modifier[modifier_input_identifier(group, name)] == expected
image_textures = [
node
for node in group.nodes
if node.bl_idname == "GeometryNodeImageTexture"
]
assert len(image_textures) == 2
assert all(node.inputs["Frame"].is_linked for node in image_textures)
assert image_textures[0].inputs["Frame"].links[0].from_socket == (
image_textures[1].inputs["Frame"].links[0].from_socket
)
floored_modulo = [
node
for node in group.nodes
if node.bl_idname == "ShaderNodeMath"
and node.operation == "FLOORED_MODULO"
]
assert len(floored_modulo) == 1
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
result = bpy.ops.object.modifier_apply(modifier=modifier.name)
assert result == {"FINISHED"}
assert not obj.modifiers
assert obj.type == "MESH"
assert len(obj.data.vertices) > 4
for name in attribute_names:
assert obj.data.attributes[name].domain == "POINT"
assert all(polygon.use_smooth for polygon in obj.data.polygons)
assert material in obj.data.materials[:]
print("Depth plane asset validation passed")
if __name__ == "__main__":
main()