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.
This commit is contained in:
136
tools/validate_balloon_asset.py
Normal file
136
tools/validate_balloon_asset.py
Normal file
@@ -0,0 +1,136 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user