Add new tests and functionality for image processing and geometry handling

- Introduced color_image.py for preparing standalone color images for tests.
- Added hdr_image.py to create HDR textures for testing.
- Implemented test_image_depth_panorama.py to evaluate panorama topology, UVs, and depth discontinuities.
- Created test_shadeless.py to validate the opacity and HDR color endpoints of the Shadeless group.
- Developed moge3_export.py for exporting dynamic MoGe-3 backbone and ONNX graphs.
- Added material.py to inherit material slots onto generated geometry.
- Built image_depth_panorama.py to construct a quad panorama with depth separation and sampling.
This commit is contained in:
2026-09-10 14:42:17 +08:00
parent 27ca25224a
commit 489003c2cc
136 changed files with 5758 additions and 1083 deletions

View File

@@ -1,54 +1,37 @@
from pathlib import Path
from contextlib import nullcontext
from types import SimpleNamespace
from unittest.mock import Mock, patch
from tests.support.blender import BlenderTestCase
class PlaneOperatorsTest(BlenderTestCase):
def test_plane_depth_off_accepts_animated_image_without_server(self):
operator = SimpleNamespace(
depth_mode="OFF",
remove_background=False,
shadeless=False,
profile="SURFACE",
mesh_detail=64,
thickness=0.1,
inflation=0.1,
algorithm="POISSON",
)
source = SimpleNamespace(data=SimpleNamespace(name="Sequence"))
context = SimpleNamespace(object=source, scene=object())
with (
patch.object(self.convert_to_plane, "video_total_frames", return_value=24),
patch.object(
self.convert_to_plane,
"image_user_settings",
return_value=(1, 0, 24, True),
),
patch.object(self.convert_to_plane, "create_image_material", return_value="Material"),
patch.object(self.convert_to_plane, "create_color_image", return_value="Color"),
patch.object(self.convert_to_plane, "create_plane_object") as build_mesh,
):
result = self.convert_to_plane.ConvertToPlane.execute(operator, context)
self.assertEqual(result, {"FINISHED"})
build_mesh.assert_called_once()
def test_plane_rejects_animated_images_before_creating_resources(self):
for kind in ("MOVIE", "SEQUENCE"):
for duration in (1, 24):
source = SimpleNamespace(data=SimpleNamespace(source=kind, frame_duration=duration))
operator = SimpleNamespace(report=Mock())
with patch.object(self.convert_to_plane, "material_color_image") as prepare:
result = self.convert_to_plane.ConvertToPlane.execute(
operator, SimpleNamespace(object=source))
self.assertEqual(result, {"CANCELLED"})
prepare.assert_not_called()
operator.report.assert_called_once_with({"ERROR"}, "Material conversion supports static images only")
def test_depth_plane_rejects_animation_and_requests_ai_setup(self):
operator = SimpleNamespace(mesh_detail=6, report=Mock())
context = SimpleNamespace(object=SimpleNamespace(data=object()))
with (
patch.object(self.convert_to_plane, "video_total_frames", return_value=2),
patch.object(self.convert_to_plane, "require_static_color_image", side_effect=ValueError("Material conversion supports static images only")),
patch.object(self.convert_to_plane, "invoke_ai_setup_if_needed") as setup,
):
self.assertEqual(self.convert_to_plane.ConvertToDepthPlane.execute(operator, context), {"CANCELLED"})
setup.assert_not_called()
with (
patch.object(self.convert_to_plane, "video_total_frames", return_value=1),
patch.object(self.convert_to_plane, "require_static_color_image"),
patch.object(self.convert_to_plane, "invoke_ai_setup_if_needed", return_value={"FINISHED"}) as setup,
patch.object(self.convert_to_plane, "prepare_image_input") as prepare,
patch.object(self.convert_to_plane, "prepare_material_color_input") as prepare,
):
self.assertEqual(self.convert_to_plane.ConvertToDepthPlane.execute(operator, context), {"FINISHED"})
setup.assert_called_once()
@@ -58,14 +41,15 @@ class PlaneOperatorsTest(BlenderTestCase):
def test_depth_and_relief_start_jobs_with_their_plane_type(self):
for plane_type, operator_class in (("DEPTH", self.convert_to_plane.ConvertToDepthPlane), ("RELIEF", self.convert_to_plane.ConvertToReliefPlane)):
module = self.convert_to_plane
source = SimpleNamespace(name="Source", data=object())
source = SimpleNamespace(name="Source", as_pointer=lambda: 1,
data=SimpleNamespace(as_pointer=lambda: 2))
context = SimpleNamespace(object=source, scene=object())
operator = SimpleNamespace(mesh_detail=4, report=Mock())
generate = Mock(return_value={"FINISHED"})
with (
patch.object(module, "video_total_frames", return_value=1),
patch.object(module, "invoke_ai_setup_if_needed", return_value=None),
patch.object(module, "prepare_image_input", return_value=(Path("input.png"), True)),
patch.object(module, "prepare_material_color_input", return_value=Path("input.png")),
patch.object(module, "material_analysis_input", return_value=Path("input.png")),
patch.object(module, "depth_plane_settings", return_value=("moge", 5)),
patch.object(module, "configured_max_ai_input_size", return_value=1024),
patch.object(module.bpy, "ops", SimpleNamespace(anyimage=SimpleNamespace(generate_depth_plane=generate)), create=True),
@@ -94,15 +78,15 @@ class PlaneOperatorsTest(BlenderTestCase):
directory=Path("result"),
file=lambda key: files[key],
)
operator = SimpleNamespace(source_object_name="Source", mesh_detail=6, plane_type=plane_type)
operator = SimpleNamespace(source_object_name="Source", color_path="color.png", mesh_detail=6, plane_type=plane_type)
context = SimpleNamespace(scene=object())
ops = SimpleNamespace(
ed=SimpleNamespace(undo_push=Mock(return_value={"FINISHED"}))
)
with (
patch.object(module, "require_image_empty", return_value=source),
patch.object(module, "load_color_result_image", return_value=color_image),
patch.object(module, "require_conversion_source", return_value=source),
patch.object(module, "material_color_image", return_value=nullcontext(color_image)),
patch.object(module, "load_depth_metadata", return_value=metadata),
patch.object(module, "load_depth_result_image", return_value=depth_image) as load_depth,
patch.object(
@@ -165,8 +149,8 @@ class PlaneOperatorsTest(BlenderTestCase):
)
with (
patch.object(module, "require_image_empty", return_value=source),
patch.object(module, "load_color_result_image", return_value=color_image),
patch.object(module, "require_conversion_source", return_value=source),
patch.object(module, "material_color_image", return_value=nullcontext(color_image)),
patch.object(
module,
"load_depth_metadata",
@@ -186,13 +170,13 @@ class PlaneOperatorsTest(BlenderTestCase):
module.create_depth_plane_from_result(
SimpleNamespace(scene=object()),
result,
SimpleNamespace(source_object_name="Source", mesh_detail=6, plane_type="DEPTH"),
SimpleNamespace(source_object_name="Source", color_path="color.png", mesh_detail=6, plane_type="DEPTH"),
)
remove_material.assert_called_once_with(material, do_unlink=True)
self.assertEqual(
[entry.args[0] for entry in remove_image.call_args_list],
[color_image, depth_image, normal_image],
[depth_image, normal_image],
)
self.assertTrue(
all(