- Introduced new enum items for depth plane and surface models with caching. - Added MOGE2_MESH_JOB to operators for converting images to surfaces. - Implemented surface mesh building and exporting in RunJob operator. - Enhanced UI to include export format options for MoGe-2. - Created validation tools for MoGe-2 surface assets and depth planes. - Updated geometry nodes to support new surface generation features. - Refactored existing tests to accommodate new functionality and ensure stability.
215 lines
7.2 KiB
Python
215 lines
7.2 KiB
Python
from pathlib import Path
|
|
import sys
|
|
|
|
import bpy
|
|
import numpy as np
|
|
|
|
|
|
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
|
|
|
|
from snapform.image_mesh import create_matching_camera, load_geometry_node_group
|
|
|
|
|
|
def set_input(modifier, group, name, value):
|
|
item = next(
|
|
item
|
|
for item in group.interface.items_tree
|
|
if item.item_type == "SOCKET"
|
|
and item.in_out == "INPUT"
|
|
and item.name == name
|
|
)
|
|
modifier[item.identifier] = value
|
|
|
|
|
|
def add_face_attributes(mesh, count, background_values=None):
|
|
confidence = mesh.attributes.new("o_face_confidence", "FLOAT", "FACE")
|
|
confidence.data.foreach_set("value", [1.0] * count)
|
|
background = mesh.attributes.new("o_background_face", "BOOLEAN", "FACE")
|
|
background.data.foreach_set(
|
|
"value",
|
|
background_values or [False] * count,
|
|
)
|
|
|
|
|
|
def main():
|
|
bpy.ops.wm.read_factory_settings(use_empty=True)
|
|
group = load_geometry_node_group()
|
|
assert load_geometry_node_group() == group
|
|
assert group.is_modifier
|
|
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"
|
|
assert group.asset_data is None
|
|
assert not any(node.bl_idname == "GeometryNodeGroup" for node in group.nodes)
|
|
assert not any(node.bl_idname == "GeometryNodeTriangulate" for node in group.nodes)
|
|
group_inputs = [
|
|
node for node in group.nodes if node.bl_idname == "NodeGroupInput"
|
|
]
|
|
assert len(group_inputs) >= 7
|
|
for node in group_inputs:
|
|
visible_outputs = [
|
|
output for output in node.outputs if output.name and not output.hide
|
|
]
|
|
assert 1 <= len(visible_outputs) <= 4
|
|
uv_stores = [
|
|
node
|
|
for node in group.nodes
|
|
if node.bl_idname == "GeometryNodeStoreNamedAttribute"
|
|
and node.inputs["Name"].default_value == "UVMap"
|
|
]
|
|
assert len(uv_stores) == 1
|
|
assert uv_stores[0].data_type == "FLOAT2"
|
|
panels = {
|
|
item.name: item
|
|
for item in group.interface.items_tree
|
|
if item.item_type == "PANEL"
|
|
}
|
|
assert not panels["Culling"].default_closed
|
|
assert panels["Options"].default_closed
|
|
assert [item.name for item in panels["Culling"].interface_items] == [
|
|
"Confidence",
|
|
"Stretch",
|
|
]
|
|
assert [item.name for item in panels["Options"].interface_items] == [
|
|
"Fix Topology",
|
|
]
|
|
smooth = next(
|
|
item
|
|
for item in group.interface.items_tree
|
|
if item.item_type == "SOCKET"
|
|
and item.in_out == "INPUT"
|
|
and item.name == "Smooth"
|
|
)
|
|
assert smooth.default_value == 0.5
|
|
for name, default_value in (("Confidence", 0.0), ("Stretch", 10.0)):
|
|
item = next(
|
|
item
|
|
for item in panels["Culling"].interface_items
|
|
if item.name == name
|
|
)
|
|
assert item.min_value == 0.0
|
|
assert item.max_value == 100.0
|
|
assert item.default_value == default_value
|
|
assert item.subtype == "PERCENTAGE"
|
|
if bpy.app.version >= (5, 0, 0):
|
|
assert group.library is not None
|
|
|
|
mesh = bpy.data.meshes.new("Validation Mesh")
|
|
mesh.from_pydata(
|
|
[(0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0)],
|
|
[],
|
|
[(0, 2, 1), (1, 2, 3)],
|
|
)
|
|
edge_stretch = mesh.attributes.new("o_edge_stretch", "FLOAT", "EDGE")
|
|
edge_stretch.data.foreach_set("value", [0.05] * len(mesh.edges))
|
|
uv = mesh.uv_layers.new(name="UVMap")
|
|
uv.data.foreach_set("uv", [0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1])
|
|
add_face_attributes(mesh, 2, [False, True])
|
|
|
|
obj = bpy.data.objects.new("Validation Object", mesh)
|
|
bpy.context.scene.collection.objects.link(obj)
|
|
modifier = obj.modifiers.new(name=group.name, type="NODES")
|
|
modifier.node_group = group
|
|
modifier.show_group_selector = False
|
|
assert not modifier.show_group_selector
|
|
set_input(modifier, group, "Resolution", 0.000001)
|
|
set_input(modifier, group, "Relax Iterations", 1)
|
|
set_input(modifier, group, "Fix Topology", 0)
|
|
set_input(modifier, group, "Smooth", 0.0)
|
|
evaluated = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
|
result = evaluated.to_mesh()
|
|
try:
|
|
assert len(result.polygons) == 2
|
|
assert "UVMap" in result.attributes
|
|
assert result.attributes["UVMap"].data_type == "FLOAT2"
|
|
finally:
|
|
evaluated.to_mesh_clear()
|
|
|
|
set_input(modifier, group, "Stretch", 0.0)
|
|
stretch_item = next(
|
|
item
|
|
for item in group.interface.items_tree
|
|
if item.item_type == "SOCKET" and item.name == "Stretch"
|
|
)
|
|
assert modifier[stretch_item.identifier] == 0.0
|
|
obj.update_tag(refresh={"DATA"})
|
|
bpy.context.view_layer.update()
|
|
culled = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
|
culled_result = culled.to_mesh()
|
|
try:
|
|
assert len(culled_result.polygons) == 0
|
|
assert len(culled_result.vertices) == 0
|
|
finally:
|
|
culled.to_mesh_clear()
|
|
|
|
size = 10
|
|
dense_vertices = [
|
|
(x * 0.1, y * 0.1, 0.0)
|
|
for y in range(size)
|
|
for x in range(size)
|
|
]
|
|
dense_faces = []
|
|
for y in range(size - 1):
|
|
for x in range(size - 1):
|
|
a = y * size + x
|
|
b = a + 1
|
|
c = a + size
|
|
d = c + 1
|
|
dense_faces.extend(((a, c, b), (b, c, d)))
|
|
dense_mesh = bpy.data.meshes.new("Dense Validation Mesh")
|
|
dense_mesh.from_pydata(dense_vertices, [], dense_faces)
|
|
dense_uv = dense_mesh.uv_layers.new(name="UVMap")
|
|
dense_uv.data.foreach_set("uv", [0.0] * (len(dense_mesh.loops) * 2))
|
|
add_face_attributes(dense_mesh, len(dense_faces))
|
|
dense_object = bpy.data.objects.new("Dense Validation Object", dense_mesh)
|
|
bpy.context.scene.collection.objects.link(dense_object)
|
|
dense_modifier = dense_object.modifiers.new(name=group.name, type="NODES")
|
|
dense_modifier.node_group = group
|
|
set_input(dense_modifier, group, "Resolution", 0.22)
|
|
dense_evaluated = dense_object.evaluated_get(
|
|
bpy.context.evaluated_depsgraph_get()
|
|
)
|
|
dense_result = dense_evaluated.to_mesh()
|
|
try:
|
|
assert 4 < len(dense_result.polygons) < len(dense_faces)
|
|
assert all(polygon.normal.z < -0.01 for polygon in dense_result.polygons)
|
|
finally:
|
|
dense_evaluated.to_mesh_clear()
|
|
|
|
source = bpy.data.objects.new("Validation Source", None)
|
|
bpy.context.scene.collection.objects.link(source)
|
|
source.location = (2.0, 3.0, 4.0)
|
|
bpy.context.view_layer.update()
|
|
obj.matrix_world = source.matrix_world.copy()
|
|
bpy.context.view_layer.update()
|
|
camera = create_matching_camera(
|
|
bpy.context,
|
|
source,
|
|
obj,
|
|
"Validation",
|
|
np.asarray(
|
|
((100.0, 0.0, 50.0), (0.0, 100.0, 50.0), (0.0, 0.0, 1.0))
|
|
),
|
|
(101, 101),
|
|
{"scale_x": 1.0, "scale_z": 1.0, "location": (0.0, 0.0, 2.0)},
|
|
)
|
|
assert camera.parent == obj
|
|
camera_location = tuple(
|
|
round(value, 5) for value in camera.matrix_world.translation
|
|
)
|
|
assert camera_location == (
|
|
2.0,
|
|
3.0,
|
|
6.0,
|
|
)
|
|
print(f"Validated Blender {bpy.app.version_string}: {group.name}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|