- 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.
67 lines
2.1 KiB
Python
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
|