Files
AnyImage/src/anyimage/common/object.py
nan 4035f5c9e4 Refactor geometry generation to standardize Blender object coordinates
- Unified the canonical local frame for generated geometries (Plane, Depth Plane, Relief Plane, Cutout, Depth Cutout) to align with Blender's object coordinate system, setting X as horizontal, Z as up, and +Y as depth.
- Removed the `Depth Axis` parameter from `O Image Cutout` and `O Image Depth Cutout`, simplifying the geometry generation process.
- Updated the depth direction defaults to use +Y instead of -Z for `O Image Relief Plane` and Depth Symmetry.
- Adjusted the geometry nodes to ensure thickness, projection, and shell displacements operate along the Y-axis.
- Implemented new capabilities for handling object space normals to align with the new canonical frame.
- Enhanced the Depth Symmetry functionality to mirror across the canonical Y-axis and perform a final rotation to maintain the correct orientation.
- Added tests to validate the new coordinate system and ensure compatibility with existing assets.
2026-09-15 13:33:37 +08:00

67 lines
2.1 KiB
Python

"""Shared Blender Object and Geometry Nodes Modifier helpers."""
import bpy
def blender_frame_rotation():
"""Map Image Empty local axes to the canonical Blender object frame."""
from mathutils import Matrix
return Matrix(
(
(1.0, 0.0, 0.0, 0.0),
(0.0, 0.0, -1.0, 0.0),
(0.0, 1.0, 0.0, 0.0),
(0.0, 0.0, 0.0, 1.0),
)
)
def modifier_input_identifier(node_group, name, *, subtype=None):
for item in node_group.interface.items_tree:
if item.item_type != "SOCKET" or item.in_out != "INPUT":
continue
if item.name == name and (subtype is None or getattr(item, "subtype", None) == subtype):
return item.identifier
raise RuntimeError(f"Geometry Nodes input is missing: {name}")
def _modifier_input_slots(modifier):
"""Return the runtime input collection, or None for the legacy API."""
properties = getattr(modifier, "properties", None)
if properties is None:
return None
return getattr(properties, "inputs", None)
def set_modifier_input(modifier, identifier, value):
"""Assign a value to a Geometry Nodes modifier input socket."""
inputs = _modifier_input_slots(modifier)
if inputs is not None:
input_slot = getattr(inputs, identifier)
value_property = input_slot.bl_rna.properties["value"]
if value_property.type == "ENUM" and type(value) is int:
value = value_property.enum_items[value].identifier
input_slot.value = value
else:
modifier[identifier] = value
def finalize_object_result(
context,
source_object,
result_object,
*,
keep_source=False,
):
"""Activate a result object and optionally replace its source object."""
source_name = source_object.name
for selected in context.selected_objects:
selected.select_set(False)
result_object.select_set(True)
context.view_layer.objects.active = result_object
if not keep_source:
bpy.data.objects.remove(source_object, do_unlink=True)
result_object.name = source_name
return result_object