Files
AnyImage/tools/validate_balloon_asset.py
nan b5a2f9690c Implement Balloon Depth Sampling and Validation Tools
- Added depth.py for MoGe-2 depth sampling and balloon deformation functionality.
- Introduced validate_balloon_asset.py to validate balloon shape modifiers and material properties in Blender.
- Enhanced test_server.py with new unit tests for balloon depth analysis and job progress tracking.
- Updated existing tests to improve code clarity and maintainability.
2026-08-21 08:27:53 +08:00

137 lines
4.1 KiB
Python

from pathlib import Path
import sys
import bpy
PROJECT_ROOT = Path(__file__).parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "src"))
sys.path.insert(
0,
str(next((PROJECT_ROOT / "src" / "snapform" / "wheels").glob("blendjob-*.whl"))),
)
from snapform.operators.balloon.depth import (
BALLOON_DEPTH_ATTRIBUTE,
add_balloon_shape_modifier,
balloon_shape_node_group,
)
from snapform.operators.balloon.operator import (
mark_balloon_material,
merge_balloon_normal_image,
reusable_balloon_material,
)
from snapform.operators.material import (
BALLOON_MATERIAL_NODE_GROUP_NAME,
material_node_group,
set_modifier_input,
)
def input_socket(group, name):
return next(
item
for item in group.interface.items_tree
if item.item_type == "SOCKET"
and item.in_out == "INPUT"
and item.name == name
)
def main():
bpy.ops.wm.read_factory_settings(use_empty=True)
group = balloon_shape_node_group()
assert group.is_modifier
assert input_socket(group, "Double Sided Depth").default_value is False
smoother = next(
node
for node in group.nodes
if node.bl_idname == "ShaderNodeMapRange"
and node.interpolation_type == "SMOOTHERSTEP"
)
assert smoother.clamp
assert not any(
node.bl_idname == "ShaderNodeMath"
and node.operation == "MAXIMUM"
and node.label == "Clamp Depth"
for node in group.nodes
)
mesh = bpy.data.meshes.new("Balloon Validation Mesh")
mesh.from_pydata(
(
(-1.0, 0.0, 0.0),
(-0.5, 0.0, 0.0),
(0.0, 0.0, 0.0),
(1.0, 0.0, 0.0),
),
(),
(),
)
depth = mesh.attributes.new(BALLOON_DEPTH_ATTRIBUTE, "FLOAT", "POINT")
depth.data.foreach_set("value", (0.0, 2.0, 2.0, 2.0))
obj = bpy.data.objects.new("Balloon Validation", mesh)
bpy.context.scene.collection.objects.link(obj)
modifier = add_balloon_shape_modifier(obj, 1.0)
evaluated = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
result = evaluated.to_mesh()
try:
x = [vertex.co.x for vertex in result.vertices]
assert abs(x[0] + 1.0) < 1e-5
assert abs(x[1] + 1.25) < 1e-5
assert abs(x[2]) < 1e-5
assert abs(x[3] - 1.0) < 1e-5
finally:
evaluated.to_mesh_clear()
set_modifier_input(modifier, input_socket(group, "Thickness").identifier, 2.0)
obj.update_tag(refresh={"DATA"})
bpy.context.view_layer.update()
evaluated = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
result = evaluated.to_mesh()
try:
x = [vertex.co.x for vertex in result.vertices]
assert abs(x[0] + 1.0) < 1e-5
assert abs(x[3] - 2.0) < 1e-5
finally:
evaluated.to_mesh_clear()
material_group = material_node_group(
surface_group_name=BALLOON_MATERIAL_NODE_GROUP_NAME,
)
normal_map = next(
node
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
source_image = bpy.data.images.new("Reuse Source", 2, 1)
shared_normal = bpy.data.images.new("Shared Normal", 2, 1)
shared_normal.pixels.foreach_set(
(128 / 255, 128 / 255, 1, 1, 128 / 255, 128 / 255, 1, 1)
)
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
update_normal = bpy.data.images.new("Normal Update", 2, 1)
update_normal.pixels.foreach_set(
(1, 0.25, 0.75, 1, 128 / 255, 128 / 255, 1, 1)
)
merge_balloon_normal_image(shared_normal, update_normal)
pixels = list(shared_normal.pixels)
assert abs(pixels[0] - 1.0) < 1e-5
assert abs(pixels[4] - 128 / 255) < 1e-5
print(f"Validated Blender {bpy.app.version_string}: {group.name}")
if __name__ == "__main__":
main()