- 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.
137 lines
4.1 KiB
Python
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()
|