Files
AnyImage/tools/node_assets/cutout_rounding.py
nan 196b964efc Add cutout shell and volume node assets for depth volume processing
- Implemented `cutout_shell.py` to build front normals and shell positions for balloon geometry.
- Created `cutout_volume.py` to establish smooth center fields and manage balloon surface attributes.
- Introduced `pisa_sr_export.py` for exporting PiSA-SR components with dependencies and asset finalization.
2026-09-08 17:18:03 +08:00

102 lines
5.1 KiB
Python

"""Round depth volume around the smoothed center-surface normals."""
from cutout_volume import node, shift_stage
def build_volume_rounding(group, volume, offset):
previous = set(group.nodes)
links = group.links
def math(operation, a, b, location):
result = node(group, "ShaderNodeMath", location)
result.operation = operation
for index, value in enumerate((a, b)):
if isinstance(value, (int, float)):
result.inputs[index].default_value = value
else:
links.new(value, result.inputs[index])
return result.outputs[0]
def read(name, location, kind="FLOAT"):
result = node(group, "GeometryNodeInputNamedAttribute", location)
result.data_type = kind
result.inputs["Name"].default_value = "o_balloon_surface_" + name
return result.outputs["Attribute"]
def store(geometry, name, value, location, kind="FLOAT"):
result = node(group, "GeometryNodeStoreNamedAttribute", location)
result.data_type, result.domain = kind, "POINT"
result.inputs["Name"].default_value = "o_balloon_surface_" + name
links.new(geometry, result.inputs["Geometry"])
links.new(value, result.inputs["Value"])
return result.outputs["Geometry"]
control = node(group, "NodeGroupInput", (0, 1250))
profile = node(group, "GeometryNodeInputNamedAttribute", (0, 850))
profile.data_type = "FLOAT"
profile.inputs["Name"].default_value = "o_balloon"
balloon = profile.outputs["Attribute"]
maximum = node(group, "GeometryNodeAttributeStatistic", (240, 850))
maximum.data_type, maximum.domain = "FLOAT", "POINT"
links.new(volume, maximum.inputs["Geometry"])
links.new(balloon, maximum.inputs["Attribute"])
limit = math("MAXIMUM", maximum.outputs["Max"], 1e-8, (480, 850))
falloff = node(group, "ShaderNodeMapRange", (720, 850))
falloff.interpolation_type = "SMOOTHERSTEP"
falloff.inputs["To Min"].default_value = 1
falloff.inputs["To Max"].default_value = 0
links.new(balloon, falloff.inputs["Value"])
links.new(limit, falloff.inputs["From Max"])
weight = math("MULTIPLY", falloff.outputs[0], control.outputs["Edge Round"], (960, 850))
center = read("center", (0, 1900))
position = node(group, "GeometryNodeInputPosition", (0, 2250))
axes = node(group, "ShaderNodeSeparateXYZ", (240, 2250))
links.new(position.outputs[0], axes.inputs[0])
target = math("ADD", center, balloon, (480, 1900))
delta = math("SUBTRACT", target, axes.outputs["Z"], (720, 1900))
delta = math("MULTIPLY", delta, weight, (960, 1900))
enabled = math("MINIMUM", control.outputs["Thickness"], 1, (480, 1300))
enabled = math("MAXIMUM", enabled, 0, (720, 1300))
delta = math("MULTIPLY", delta, enabled, (1200, 1900))
carrier = store(volume, "edge_weight", weight, (1200, 0))
carrier = store(carrier, "front_delta", delta, (1440, 0))
rear = math("MULTIPLY", balloon, weight, (1200, 850))
rear = math("MULTIPLY", rear, control.outputs["Thickness"], (1440, 850))
thickness = read("thickness", (1440, 1250))
total = math("ADD", thickness, delta, (1680, 1250))
total = math("ADD", total, rear, (1920, 1250))
carrier = store(carrier, "thickness", total, (1920, 0))
# Evaluate normals on a separate center surface with identical point indices.
center_position = node(group, "ShaderNodeCombineXYZ", (480, -550))
for axis in ("X", "Y"):
links.new(axes.outputs[axis], center_position.inputs[axis])
links.new(center, center_position.inputs["Z"])
surface = node(group, "GeometryNodeSetPosition", (720, -550))
links.new(volume, surface.inputs["Geometry"])
links.new(center_position.outputs[0], surface.inputs["Position"])
normal = node(group, "GeometryNodeInputNormal", (720, -1000))
blur = node(group, "GeometryNodeBlurAttribute", (960, -1000))
blur.data_type = "FLOAT_VECTOR"
links.new(normal.outputs[0], blur.inputs["Value"])
local = node(group, "NodeGroupInput", (720, -1300))
links.new(local.outputs["Normal Smooth"], blur.inputs["Iterations"])
normalize = node(group, "ShaderNodeVectorMath", (1200, -1000))
normalize.operation = "NORMALIZE"
links.new(blur.outputs[0], normalize.inputs[0])
index = node(group, "GeometryNodeInputIndex", (1200, -1350))
sample = node(group, "GeometryNodeSampleIndex", (1440, -550))
sample.data_type, sample.domain = "FLOAT_VECTOR", "POINT"
links.new(surface.outputs[0], sample.inputs["Geometry"])
links.new(normalize.outputs[0], sample.inputs["Value"])
links.new(index.outputs[0], sample.inputs["Index"])
carrier = store(carrier, "normal", sample.outputs[0], (2160, 0), "FLOAT_VECTOR")
captured = read("front_delta", (2160, 850))
displacement = node(group, "ShaderNodeCombineXYZ", (2400, 850))
links.new(captured, displacement.inputs["Z"])
front = node(group, "GeometryNodeSetPosition", (2640, 0))
links.new(carrier, front.inputs["Geometry"])
links.new(displacement.outputs[0], front.inputs["Offset"])
shift_stage(group, previous, offset)
return front.outputs[0]