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.
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
"""Build O Image Cutout."""
|
||||
import bpy
|
||||
from mathutils import Matrix
|
||||
from anyimage.operators.cutout_tool.shape import CUTOUT_Z_TO_X, NORMAL_REDUCTION_ATTRIBUTE_NAME
|
||||
from anyimage.operators.cutout_tool.shape import NORMAL_REDUCTION_ATTRIBUTE_NAME
|
||||
from ..common.nodes import interface_socket, group_input, math_node, float_input, menu_switch, read_float_attribute, store_float_attribute, prepare_node_group
|
||||
from ..common.cutout import _extrude_layers, _island_cleanup, _offset_geometry_z, _remove_zero_thickness, _set_layer_z, _shade_output
|
||||
from ..common.cutout import _extrude_layers, _island_cleanup, _offset_geometry_y, _remove_zero_thickness, _set_layer_y, _shade_output
|
||||
|
||||
|
||||
NODE_GROUP_NAME = "O Image Cutout"
|
||||
|
||||
|
||||
def _set_segmented_layer_z(
|
||||
def _set_segmented_layer_y(
|
||||
group,
|
||||
geometry,
|
||||
front_z,
|
||||
@@ -56,7 +55,7 @@ def _set_segmented_layer_z(
|
||||
links.new(thickness, step.inputs[0])
|
||||
links.new(segment_limit.outputs[0], step.inputs[1])
|
||||
|
||||
front = _offset_geometry_z(group, geometry, front_z)
|
||||
front = _offset_geometry_y(group, geometry, front_z)
|
||||
repeat_output = nodes.new("GeometryNodeRepeatOutput")
|
||||
repeat_input = nodes.new("GeometryNodeRepeatInput")
|
||||
repeat_input.pair_with_output(repeat_output)
|
||||
@@ -84,7 +83,7 @@ def _set_segmented_layer_z(
|
||||
links.new(split_extrude.outputs["Inverted"], repeat_output.inputs["Geometry"])
|
||||
links.new(join_sides.outputs["Geometry"], repeat_output.inputs["Sides"])
|
||||
|
||||
positioned_side = _offset_geometry_z(
|
||||
positioned_side = _offset_geometry_y(
|
||||
group,
|
||||
repeat_output.outputs["Sides"],
|
||||
front_z,
|
||||
@@ -92,7 +91,7 @@ def _set_segmented_layer_z(
|
||||
flip_side = nodes.new("GeometryNodeFlipFaces")
|
||||
links.new(positioned_side, flip_side.inputs["Mesh"])
|
||||
|
||||
back = _offset_geometry_z(group, geometry, back_z)
|
||||
back = _offset_geometry_y(group, geometry, back_z)
|
||||
flip_back = nodes.new("GeometryNodeFlipFaces")
|
||||
links.new(back, flip_back.inputs["Mesh"])
|
||||
join = nodes.new("GeometryNodeJoinGeometry")
|
||||
@@ -113,9 +112,9 @@ def _build_shell(group, geometry, thickness):
|
||||
front.inputs[1].default_value = 0.0
|
||||
links.new(thickness, front.inputs[0])
|
||||
back = math_node(nodes, "MULTIPLY")
|
||||
back.inputs[1].default_value = -1.0
|
||||
back.inputs[1].default_value = 1.0
|
||||
links.new(thickness, back.inputs[0])
|
||||
shaped = _set_segmented_layer_z(
|
||||
shaped = _set_segmented_layer_y(
|
||||
group,
|
||||
geometry,
|
||||
front.outputs[0],
|
||||
@@ -132,16 +131,16 @@ def _build_balloon(group, geometry, thickness):
|
||||
front_scale = math_node(nodes, "MULTIPLY")
|
||||
links.new(profile, front_scale.inputs[0])
|
||||
links.new(thickness, front_scale.inputs[1])
|
||||
negative_back = math_node(nodes, "MULTIPLY")
|
||||
negative_back.inputs[1].default_value = -1.0
|
||||
links.new(front_scale.outputs[0], negative_back.inputs[0])
|
||||
negative_front = math_node(nodes, "MULTIPLY")
|
||||
negative_front.inputs[1].default_value = -1.0
|
||||
links.new(front_scale.outputs[0], negative_front.inputs[0])
|
||||
extrude = _extrude_layers(group, geometry)
|
||||
shaped = _set_layer_z(
|
||||
shaped = _set_layer_y(
|
||||
group,
|
||||
geometry,
|
||||
extrude,
|
||||
negative_front.outputs[0],
|
||||
front_scale.outputs[0],
|
||||
negative_back.outputs[0],
|
||||
)
|
||||
return _remove_zero_thickness(group, geometry, shaped, thickness)
|
||||
|
||||
@@ -160,7 +159,6 @@ def build_image_cutout_group():
|
||||
float_input(group, 'Shell Thickness', 0.0, maximum=1.0, subtype='DISTANCE').description = 'Add thickness behind the original surface while keeping the front surface in place.'
|
||||
options = group.interface.new_panel(name='Options', default_closed=True)
|
||||
float_input(group, 'Cleanup Threshold', 0.0, parent=options)
|
||||
interface_socket(group, 'Depth Axis', 'INPUT', 'NodeSocketMenu', options).menu_expanded = True
|
||||
interface_socket(group, 'Geometry', 'OUTPUT', 'NodeSocketGeometry')
|
||||
controls = group_input(group.nodes, set())
|
||||
balloon = _build_balloon(group, controls.outputs['Geometry'], controls.outputs['Thickness'])
|
||||
@@ -195,21 +193,13 @@ def build_image_cutout_group():
|
||||
reduction.inputs[0].default_value = 1.0
|
||||
group.links.new(scale, reduction.inputs[1])
|
||||
geometry = store_float_attribute(group, geometry, NORMAL_REDUCTION_ATTRIBUTE_NAME, reduction.outputs[0])
|
||||
orient = group.nodes.new('GeometryNodeTransform')
|
||||
orient.inputs['Rotation'].default_value = Matrix(CUTOUT_Z_TO_X).to_euler()
|
||||
group.links.new(geometry, orient.inputs['Geometry'])
|
||||
axis = menu_switch(group, 'Depth Axis', ('-Z', '+X'), 'GEOMETRY')
|
||||
group.links.new(geometry, axis.inputs['-Z'])
|
||||
group.links.new(orient.outputs[0], axis.inputs['+X'])
|
||||
_shade_output(group, axis.outputs[0])
|
||||
_shade_output(group, geometry)
|
||||
prepare_node_group(group)
|
||||
for item in group.interface.items_tree:
|
||||
if item.item_type != 'SOCKET' or item.in_out != 'INPUT':
|
||||
continue
|
||||
if item.name == 'Mode':
|
||||
item.default_value = 'Balloon'
|
||||
elif item.name == 'Depth Axis':
|
||||
item.default_value = '-Z'
|
||||
elif item.name == 'Shell Thickness':
|
||||
item.name = 'Thickness'
|
||||
return group
|
||||
|
||||
Reference in New Issue
Block a user