Add comprehensive tests for cutout and mesh functionalities
- Introduced `test_cutout.py` to validate cutout pie menu behavior, image lasso options, and mesh detail calculations. - Added `test_mesh.py` to ensure correct tangent normal rasterization, plane source topology, and depth surface evaluations. - Created `test_mesh_blender.py` to verify material node groups, tangent baking, and local cutout building from Blender images. - Implemented tests for handling alpha regions and ensuring geometry nodes maintain expected behavior.
This commit is contained in:
@@ -3,25 +3,31 @@ import math
|
||||
|
||||
import bpy
|
||||
|
||||
from .depth import (
|
||||
add_balloon_shape_modifier,
|
||||
assign_balloon_shape_attributes,
|
||||
balloon_depth_fields,
|
||||
create_balloon_base_plane,
|
||||
load_balloon_depth_data,
|
||||
)
|
||||
from .geometry import INFLATION, POINT_SPACING, triangulate_balloon
|
||||
from ...properties import (
|
||||
BEN2_MODEL_KEY,
|
||||
ai_ready,
|
||||
ai_setup_label,
|
||||
ai_status,
|
||||
)
|
||||
from ...properties import (
|
||||
balloon_contour_analysis_property as contour_analysis_property,
|
||||
)
|
||||
from ...properties import (
|
||||
balloon_depth_analysis_property as depth_analysis_property,
|
||||
)
|
||||
from ...properties import (
|
||||
balloon_depth_model_property as depth_model_property,
|
||||
)
|
||||
from ...properties import (
|
||||
balloon_mode_property as mode_property,
|
||||
)
|
||||
from ...properties import (
|
||||
balloon_resolution_property as resolution_property,
|
||||
)
|
||||
from ...properties import (
|
||||
balloon_reuse_material_property as reuse_material_property,
|
||||
)
|
||||
from ...properties import (
|
||||
balloon_thickness_property as thickness_property,
|
||||
)
|
||||
from ...runtime import JobOperatorBase
|
||||
@@ -44,12 +50,19 @@ from ..image import (
|
||||
video_total_frames,
|
||||
)
|
||||
from ..material import (
|
||||
BALLOON_MATERIAL_NODE_GROUP_NAME,
|
||||
TEXTURE_MATERIAL_NODE_GROUP_NAME,
|
||||
IMAGE_MATERIAL_NODE_GROUP_NAME,
|
||||
create_material,
|
||||
)
|
||||
from ..normal_map import bake_tangent_normal_image
|
||||
from ..surface import assign_vertex_uv
|
||||
|
||||
from .depth import (
|
||||
add_balloon_shape_modifier,
|
||||
assign_balloon_shape_attributes,
|
||||
balloon_depth_fields,
|
||||
create_balloon_base_plane,
|
||||
load_balloon_depth_data,
|
||||
)
|
||||
from .geometry import INFLATION, POINT_SPACING, triangulate_balloon
|
||||
|
||||
MIN_PATH_DISTANCE = 2.0
|
||||
BALLOON_MATERIAL_PROPERTY = "anyimage_balloon_material"
|
||||
@@ -151,7 +164,6 @@ def data_name_matches(name, base_name):
|
||||
return suffix != name and suffix.isdigit()
|
||||
|
||||
|
||||
|
||||
def marked_balloon_material(source_image, normal_required=False):
|
||||
return next(
|
||||
(
|
||||
@@ -160,9 +172,7 @@ def marked_balloon_material(source_image, normal_required=False):
|
||||
if callable(getattr(material, "get", None))
|
||||
and bool(material.get(BALLOON_MATERIAL_PROPERTY))
|
||||
and material.get(BALLOON_SOURCE_IMAGE_PROPERTY) is source_image
|
||||
and (
|
||||
material.get(BALLOON_NORMAL_IMAGE_PROPERTY) is not None
|
||||
)
|
||||
and (material.get(BALLOON_NORMAL_IMAGE_PROPERTY) is not None)
|
||||
== normal_required
|
||||
),
|
||||
None,
|
||||
@@ -227,6 +237,7 @@ BALLOON_AXIS_TO_SOURCE = (
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
)
|
||||
|
||||
|
||||
def orient_balloon_vertices(vertices):
|
||||
"""Rotate legacy XY/Z Balloon coordinates into a YZ/-X frame."""
|
||||
return [(-z, -x, y) for x, y, z in vertices]
|
||||
@@ -237,14 +248,16 @@ def orient_balloon_world_matrix(source_matrix):
|
||||
|
||||
return source_matrix @ Matrix(BALLOON_AXIS_TO_SOURCE)
|
||||
|
||||
|
||||
def orient_balloon_base_plane(local_plane):
|
||||
slope_x, slope_y, offset = local_plane
|
||||
return (slope_x, -slope_y, -offset)
|
||||
|
||||
|
||||
def extract_balloon_object(
|
||||
context,
|
||||
source_object,
|
||||
polygon,
|
||||
regions,
|
||||
matrix_world,
|
||||
algorithm,
|
||||
thickness=1.0,
|
||||
@@ -253,8 +266,10 @@ def extract_balloon_object(
|
||||
reuse_material=True,
|
||||
depth_data=None,
|
||||
normal_path=None,
|
||||
material_image=None,
|
||||
bake_normal_to_tangent=False,
|
||||
):
|
||||
image = source_object.data
|
||||
image = material_image or source_object.data
|
||||
image_size = tuple(image.size)
|
||||
local_bounds = plane_bounds(source_object)
|
||||
world_metric, world_z_scale = world_plane_metric(matrix_world)
|
||||
@@ -263,7 +278,7 @@ def extract_balloon_object(
|
||||
resolution,
|
||||
)
|
||||
vertices, faces, vertex_uv = triangulate_balloon(
|
||||
polygon,
|
||||
regions,
|
||||
image_size,
|
||||
local_bounds,
|
||||
spacing=point_spacing,
|
||||
@@ -315,7 +330,9 @@ def extract_balloon_object(
|
||||
finally:
|
||||
bpy.data.images.remove(update_normal)
|
||||
if material is None:
|
||||
color_image = reusable_color_image(color_name) if reuse_material else None
|
||||
color_image = material_image
|
||||
if color_image is None and reuse_material:
|
||||
color_image = reusable_color_image(color_name)
|
||||
if color_image is None:
|
||||
color_image = image.copy()
|
||||
color_image.name = color_name
|
||||
@@ -330,12 +347,8 @@ def extract_balloon_object(
|
||||
material = create_material(
|
||||
material_name,
|
||||
color_image,
|
||||
1.0,
|
||||
surface_group_name=(
|
||||
BALLOON_MATERIAL_NODE_GROUP_NAME
|
||||
if normal_image is not None
|
||||
else TEXTURE_MATERIAL_NODE_GROUP_NAME
|
||||
),
|
||||
0.0,
|
||||
surface_group_name=IMAGE_MATERIAL_NODE_GROUP_NAME,
|
||||
normal_image=normal_image,
|
||||
scene=context.scene,
|
||||
)
|
||||
@@ -369,7 +382,18 @@ def extract_balloon_object(
|
||||
vertices,
|
||||
local_base_plane,
|
||||
)
|
||||
add_balloon_shape_modifier(balloon, thickness, base_plane)
|
||||
add_balloon_shape_modifier(
|
||||
balloon,
|
||||
thickness,
|
||||
base_plane,
|
||||
)
|
||||
if bake_normal_to_tangent and normal_image is not None:
|
||||
bake_tangent_normal_image(
|
||||
context,
|
||||
source_object,
|
||||
balloon,
|
||||
normal_image,
|
||||
)
|
||||
if depth_data is not None:
|
||||
balloon["anyimage_balloon_depth_analysis"] = True
|
||||
balloon["anyimage_reference_depth"] = depth_data["reference_depth"]
|
||||
@@ -625,7 +649,7 @@ class ExtractBalloon(bpy.types.Operator):
|
||||
balloon = extract_balloon_object(
|
||||
context,
|
||||
source_object,
|
||||
polygon,
|
||||
((polygon,),),
|
||||
matrix_world,
|
||||
self.mode,
|
||||
thickness=self.thickness,
|
||||
@@ -710,7 +734,7 @@ class AnalyzeBalloon(JobOperatorBase, bpy.types.Operator):
|
||||
balloon = extract_balloon_object(
|
||||
context,
|
||||
source_object,
|
||||
polygon,
|
||||
((polygon,),),
|
||||
matrix_world,
|
||||
self.mode,
|
||||
thickness=self.thickness,
|
||||
@@ -719,6 +743,7 @@ class AnalyzeBalloon(JobOperatorBase, bpy.types.Operator):
|
||||
reuse_material=self.reuse_material,
|
||||
depth_data=depth_data,
|
||||
normal_path=(result.file("normal") if self.depth_analysis else None),
|
||||
bake_normal_to_tangent=self.depth_analysis,
|
||||
)
|
||||
balloon["anyimage_resolution_m"] = self.resolution
|
||||
if self.contour_analysis:
|
||||
|
||||
Reference in New Issue
Block a user