Add comprehensive tests for cutout and mesh functionalities

- Introduced `test_cutout.py` to validate cutout pie menu behavior, image lasso options, and mesh detail calculations.
- Added `test_mesh.py` to ensure correct tangent normal rasterization, plane source topology, and depth surface evaluations.
- Created `test_mesh_blender.py` to verify material node groups, tangent baking, and local cutout building from Blender images.
- Implemented tests for handling alpha regions and ensuring geometry nodes maintain expected behavior.
This commit is contained in:
2026-08-26 10:42:29 +08:00
parent 0cf04a13c6
commit 2b79a342b0
46 changed files with 5554 additions and 645 deletions

View File

@@ -99,13 +99,14 @@ def main():
material_group = material_node_group(
surface_group_name=BALLOON_MATERIAL_NODE_GROUP_NAME,
)
normal_map = next(
node
normal_maps = {
node.space
for node in material_group.nodes
if node.bl_idname == "ShaderNodeNormalMap"
)
assert normal_map.space == "TANGENT"
assert abs(input_socket(material_group, "Normal Strength").default_value - 0.5) < 1e-6
}
assert normal_maps == {"TANGENT", "OBJECT"}
assert input_socket(material_group, "Object Space").default_value is False
assert input_socket(material_group, "Normal Scale").default_value == 1.0
source_image = bpy.data.images.new("Reuse Source", 2, 1)
shared_normal = bpy.data.images.new("Shared Normal", 2, 1)
@@ -115,8 +116,6 @@ def main():
material = bpy.data.materials.new("Unrelated Material Name")
mark_balloon_material(material, source_image, shared_normal)
assert reusable_balloon_material(
"unused",
"unused",
source_image,
normal_required=True,
) is material

View File

@@ -285,14 +285,37 @@ def main():
and node.node_tree == surface_group
]
assert len(surface_group_nodes) == 1
assert surface_group_nodes[0].name == "O DA3 Surface Layer"
assert surface_group_nodes[0].name == "O Image Layer"
surface_inputs = [
item.name
for item in surface_group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
]
assert surface_inputs == ["Color", "Alpha", "Bump Scale"]
assert surface_inputs == [
"Color",
"Alpha",
"Normal",
"Normal Scale",
"Object Space",
"Bump Scale",
]
normal_input = next(
item
for item in surface_group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == "Normal"
)
assert tuple(normal_input.default_value) == (0.5, 0.5, 1.0, 1.0)
normal_scale = next(
item
for item in surface_group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == "Normal Scale"
)
assert normal_scale.default_value == 1.0
surface_outputs = [
item.name
for item in surface_group.interface.items_tree

View File

@@ -5,7 +5,6 @@ from pathlib import Path
import bpy
import numpy as np
PROJECT_ROOT = Path(__file__).parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "src"))
sys.path.insert(
@@ -43,7 +42,7 @@ def write_test_normal_texture(normal, output_path):
def main():
bpy.ops.wm.read_factory_settings(use_empty=True)
arguments = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
arguments = sys.argv[sys.argv.index("--") + 1 :] if "--" in sys.argv else []
if arguments:
validate_real_result(Path(arguments[0]), Path(arguments[1]))
return
@@ -142,14 +141,14 @@ def main():
depth_edge_socket = next(
item
for item in surface.modifiers[0].node_group.interface.items_tree
if getattr(item, "in_out", None) == "INPUT"
and item.name == "Depth Continuity"
if getattr(item, "in_out", None) == "INPUT" and item.name == "Depth Continuity"
)
assert depth_edge_socket.default_value == 50.0
assert depth_edge_socket.subtype == "PERCENTAGE"
assert depth_edge_socket.structure_type == "SINGLE"
culling_panel = next(
item for item in surface.modifiers[0].node_group.interface.items_tree
item
for item in surface.modifiers[0].node_group.interface.items_tree
if item.item_type == "PANEL" and item.name == "Culling"
)
assert [item.name for item in culling_panel.interface_items] == [
@@ -157,7 +156,8 @@ def main():
"Mask",
]
input_sockets = [
item for item in surface.modifiers[0].node_group.interface.items_tree
item
for item in surface.modifiers[0].node_group.interface.items_tree
if item.item_type == "SOCKET" and item.in_out == "INPUT"
]
assert all(item.structure_type == "SINGLE" for item in input_sockets)
@@ -179,22 +179,34 @@ def main():
assert MOGE_SURFACE_MATERIAL_NODE_GROUP_NAME in bpy.data.node_groups
material_group = bpy.data.node_groups[MOGE_SURFACE_MATERIAL_NODE_GROUP_NAME]
assert any(node.bl_idname == "ShaderNodeBump" for node in material_group.nodes)
normal_map = next(
node for node in material_group.nodes
if node.bl_idname == "ShaderNodeNormalMap"
)
assert normal_map.space == "OBJECT"
normal_maps = [
node for node in material_group.nodes if node.bl_idname == "ShaderNodeNormalMap"
]
assert {node.space for node in normal_maps} == {"TANGENT", "OBJECT"}
normal_strength = next(
item for item in material_group.interface.items_tree
if getattr(item, "in_out", None) == "INPUT"
and item.name == "Normal Strength"
item
for item in material_group.interface.items_tree
if getattr(item, "in_out", None) == "INPUT" and item.name == "Normal Scale"
)
assert normal_strength.default_value == 1.0
assert normal_strength.min_value == 0.0
assert normal_map.inputs["Strength"].is_linked
normal_input = next(
item
for item in material_group.interface.items_tree
if getattr(item, "in_out", None) == "INPUT" and item.name == "Normal"
)
assert tuple(normal_input.default_value) == (0.5, 0.5, 1.0, 1.0)
assert all(node.inputs["Strength"].is_linked for node in normal_maps)
layer = next(
node
for node in surface.data.materials[0].node_tree.nodes
if node.bl_idname == "ShaderNodeGroup" and node.node_tree == material_group
)
assert layer.inputs["Object Space"].default_value is True
assert not surface.data.has_custom_normals
color_texture = next(
node for node in surface.data.materials[0].node_tree.nodes
node
for node in surface.data.materials[0].node_tree.nodes
if node.bl_idname == "ShaderNodeTexImage" and node.image == foreground
)
assert color_texture.image.name == "MoGe Surface Test.color"
@@ -202,7 +214,8 @@ def main():
bpy.context.scene
)
normal_texture = next(
node for node in surface.data.materials[0].node_tree.nodes
node
for node in surface.data.materials[0].node_tree.nodes
if node.bl_idname == "ShaderNodeTexImage"
and node.image.colorspace_settings.name == "Non-Color"
)
@@ -243,12 +256,17 @@ def validate_real_result(results_path, image_path):
assert not surface.data.has_custom_normals
material_group = bpy.data.node_groups[MOGE_SURFACE_MATERIAL_NODE_GROUP_NAME]
assert any(node.bl_idname == "ShaderNodeBump" for node in material_group.nodes)
normal_map = next(
node for node in material_group.nodes
if node.bl_idname == "ShaderNodeNormalMap"
normal_maps = [
node for node in material_group.nodes if node.bl_idname == "ShaderNodeNormalMap"
]
assert {node.space for node in normal_maps} == {"TANGENT", "OBJECT"}
assert all(node.inputs["Strength"].is_linked for node in normal_maps)
layer = next(
node
for node in surface.data.materials[0].node_tree.nodes
if node.bl_idname == "ShaderNodeGroup" and node.node_tree == material_group
)
assert normal_map.space == "OBJECT"
assert normal_map.inputs["Strength"].is_linked
assert layer.inputs["Object Space"].default_value is True
attribute_names = set(surface.data.attributes.keys())
assert {"o_depth", "o_mask"}.issubset(attribute_names)
assert "o_depth_discontinuity" in attribute_names