Files
AnyImage/tools/validate_geometry_nodes_asset.py
nan 835225d264 feat: Add surface generation and media handling
- Implemented surface generation functionality in `surface.py`, including geometry estimation and texture writing.
- Introduced media input handling in `input.py`, supporting image and video frame extraction.
- Added model management for geometry adapters in `models/__init__.py`.
- Created BEN2 and DA3 model inference logic in `ben2.py` and `da3.py`, respectively.
- Developed ONNX model inference utilities for BEN2 and DA3 in `onnx_ben2.py` and `onnx_da3.py`.
- Added support for MoGe-2 model inference in `moge2.py` with necessary ONNX utilities.
- Implemented output handling for depth and surface data in `depth.py` and `surface.py`.
- Created debug output utilities for DA3 and MoGe-2 predictions in `debug_da3.py` and `debug_moge2.py`.
- Enhanced ONNX runtime session management in `onnx_runtime.py`.
- Updated `__init__.py` files to include new modules and functions for better organization.
2026-08-18 23:27:10 +08:00

249 lines
8.2 KiB
Python

from pathlib import Path
import sys
import bpy
import numpy as np
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.blender.depth_plane import (
get_modifier_input,
set_modifier_input,
)
from snapform.blender.surface 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
)
set_modifier_input(modifier, item.identifier, value)
def add_face_attributes(mesh, count):
confidence = mesh.attributes.new("o_confidence", "FLOAT", "FACE")
confidence.data.foreach_set("value", [1.0] * 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"
input_sockets = [
item for item in 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)
assert [item.name for item in input_sockets[:4]] == [
"Geometry",
"Iterations",
"Resolution",
"Smooth",
]
iterations = next(item for item in input_sockets if item.name == "Iterations")
assert iterations.default_value == 2
assert iterations.min_value == 0
resolution = next(item for item in input_sockets if item.name == "Resolution")
assert abs(resolution.default_value - 0.1) < 1e-6
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] == [
"Depth Continuity",
"Confidence",
]
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),
("Depth Continuity", 50.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)],
)
discontinuity = mesh.attributes.new(
"o_depth_discontinuity",
"FLOAT",
"FACE",
)
discontinuity.data.foreach_set("value", [0.03] * len(mesh.polygons))
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)
assert "o_confidence" in mesh.attributes
assert not {
"o_background",
"o_background_face",
"o_face_confidence",
"o_confidence_percentile",
}.intersection(mesh.attributes.keys())
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, "Iterations", 0)
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, "Depth Continuity", 100.0)
discontinuity_item = next(
item
for item in group.interface.items_tree
if item.item_type == "SOCKET" and item.name == "Depth Continuity"
)
assert get_modifier_input(modifier, discontinuity_item.identifier) == 100.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()