Refactor depth surface and image cutout nodes for improved functionality and clarity

- Removed deprecated tests and code related to shell wall connections and coincident sheets.
- Updated attribute handling in depth surface and image cutout groups, including new vector attribute functions.
- Enhanced the inflation logic in the image depth cutout group to better manage shell and balloon behaviors.
- Improved parameter naming and descriptions for clarity in the user interface.
- Streamlined the geometry processing logic to ensure better performance and maintainability.
This commit is contained in:
2026-09-17 17:11:31 +08:00
parent 0aeb61f818
commit 2811c7bf3a
10 changed files with 217 additions and 556 deletions

View File

@@ -1,8 +1,8 @@
"""Face separation and connected thickness for Depth Surface assets."""
from .nodes import (
boolean_node, compare_node, evaluate_field, store_float_attribute,
store_boolean_attribute, remove_attribute_pattern, sample_field,
boolean_node, compare_node, evaluate_field, store_boolean_attribute,
remove_attribute_pattern, sample_field,
)
from .smoothing import edge_boundary_field
@@ -318,8 +318,7 @@ def build_surface_camera(group, original, separated, cut_vertex, strength):
return evaluate_field(group, choose.outputs["Output"], "FLOAT_VECTOR", "POINT")
def thicken_depth_surface(group, geometry, thickness, normal_smooth=None, *,
back_mask=None, weld_zero_thickness=False):
def thicken_depth_surface(group, geometry, thickness, normal_smooth=None):
"""Connect caps with single quads by source vertex and cap identity."""
nodes, links = group.nodes, group.links
position = nodes.new("GeometryNodeInputPosition")
@@ -378,29 +377,12 @@ def thicken_depth_surface(group, geometry, thickness, normal_smooth=None, *,
links.new(rear, back_parts.inputs["Geometry"])
links.new(rim, back_parts.inputs["Geometry"])
rear = back_parts.outputs["Geometry"]
if back_mask is not None:
rear = store_float_attribute(group, rear, back_mask, 1.0)
for value in (
saved,
rear,
):
links.new(value, join.inputs["Geometry"])
construction = join.outputs["Geometry"]
if weld_zero_thickness:
positive = compare_node(group, "GREATER_THAN", source_thickness, 1e-12)
flattened = nodes.new("ShaderNodeCombineXYZ")
for axis in ("X", "Z"):
links.new(layer_axes.outputs[axis], flattened.inputs[axis])
collapsed = nodes.new("GeometryNodeSwitch")
collapsed.input_type = "FLOAT"
collapsed.inputs["False"].default_value = 0.0
links.new(positive, collapsed.inputs["Switch"])
links.new(layer_axes.outputs["Y"], collapsed.inputs["True"])
links.new(collapsed.outputs[0], flattened.inputs["Y"])
collapse = nodes.new("GeometryNodeSetPosition")
links.new(construction, collapse.inputs["Geometry"])
links.new(flattened.outputs[0], collapse.inputs["Position"])
construction = collapse.outputs["Geometry"]
merge = nodes.new("GeometryNodeMergeByDistance")
merge.mode = "ALL"
merge.inputs["Distance"].default_value = 0.1

View File

@@ -138,6 +138,13 @@ def read_float_attribute(group, name):
return node.outputs["Attribute"]
def read_vector_attribute(group, name):
node = group.nodes.new("GeometryNodeInputNamedAttribute")
node.data_type = "FLOAT_VECTOR"
node.inputs["Name"].default_value = name
return node.outputs["Attribute"]
def store_float_attribute(group, geometry, name, value, domain="POINT"):
node = group.nodes.new("GeometryNodeStoreNamedAttribute")
node.domain, node.data_type = domain, "FLOAT"
@@ -150,6 +157,15 @@ def store_float_attribute(group, geometry, name, value, domain="POINT"):
return node.outputs["Geometry"]
def store_vector_attribute(group, geometry, name, value, domain="POINT"):
node = group.nodes.new("GeometryNodeStoreNamedAttribute")
node.domain, node.data_type = domain, "FLOAT_VECTOR"
node.inputs["Name"].default_value = name
group.links.new(geometry, node.inputs["Geometry"])
group.links.new(value, node.inputs["Value"])
return node.outputs["Geometry"]
def evaluate_field(group, value, data_type, domain):
"""Evaluate a field on an explicit domain before downstream adaptation."""
node = group.nodes.new("GeometryNodeFieldOnDomain")
@@ -184,26 +200,12 @@ def store_boolean_attribute(group, geometry, name, value, domain="POINT"):
return node.outputs["Geometry"], read.outputs["Attribute"]
def remove_attributes(group, geometry, *names, enabled=None):
"""Remove only the explicitly owned temporary attributes."""
original = geometry
for name in names:
node = group.nodes.new("GeometryNodeRemoveAttribute")
node.inputs["Name"].default_value = name
group.links.new(geometry, node.inputs["Geometry"])
geometry = node.outputs["Geometry"]
if enabled is not None:
choose = group.nodes.new("GeometryNodeSwitch")
choose.input_type = "GEOMETRY"
group.links.new(enabled, choose.inputs["Switch"])
group.links.new(original, choose.inputs["False"])
group.links.new(geometry, choose.inputs["True"])
geometry = choose.outputs["Output"]
return geometry
def remove_attribute_pattern(group, geometry, pattern):
"""Remove every attribute matching an internal wildcard pattern."""
"""Remove every attribute matching an internal wildcard pattern.
Always clean internal attributes with a pattern: naming an attribute that a
branch never stored makes the Remove Named Attribute node warn.
"""
node = group.nodes.new("GeometryNodeRemoveAttribute")
node.inputs["Name"].default_value = pattern
node.pattern_mode = "WILDCARD"