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.
This commit is contained in:
142
tools/node_assets/cutout_inflation.py
Normal file
142
tools/node_assets/cutout_inflation.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""Estimate existing front volume and add continuous normal-directed inflation."""
|
||||
from cutout_volume import node
|
||||
|
||||
|
||||
def _math(group, operation, a, b, location):
|
||||
result = node(group, "ShaderNodeMath", location)
|
||||
result.operation = operation
|
||||
for index, value in enumerate((a, b)):
|
||||
if hasattr(value, "node"):
|
||||
group.links.new(value, result.inputs[index])
|
||||
else:
|
||||
result.inputs[index].default_value = value
|
||||
return result.outputs[0]
|
||||
|
||||
|
||||
def _read(group, 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(group, 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
|
||||
group.links.new(geometry, result.inputs["Geometry"])
|
||||
group.links.new(value, result.inputs["Value"])
|
||||
return result.outputs["Geometry"]
|
||||
|
||||
|
||||
def build_front_fields(group, geometry):
|
||||
"""Fit structural amplitude over neighbouring points before creating the shell."""
|
||||
links = group.links
|
||||
|
||||
def math(operation, a, b, x, y):
|
||||
return _math(group, operation, a, b, (4800 + x, -2500 + y))
|
||||
|
||||
def blur(value, x, y, weight=None):
|
||||
result = node(group, "GeometryNodeBlurAttribute", (4800 + x, -2500 + y))
|
||||
result.data_type = "FLOAT"
|
||||
result.inputs["Iterations"].default_value = 256
|
||||
links.new(value, result.inputs["Value"])
|
||||
if weight is not None:
|
||||
links.new(weight, result.inputs["Weight"])
|
||||
return result.outputs[0]
|
||||
|
||||
profile_node = node(group, "GeometryNodeInputNamedAttribute", (4800, -2500))
|
||||
profile_node.data_type = "FLOAT"
|
||||
profile_node.inputs["Name"].default_value = "o_balloon"
|
||||
profile = profile_node.outputs["Attribute"]
|
||||
rim = _read(group, "rim", (4800, -3050))
|
||||
interior = math("SUBTRACT", 1, rim, 240, -550)
|
||||
position = node(group, "GeometryNodeInputPosition", (4800, -1900))
|
||||
axes = node(group, "ShaderNodeSeparateXYZ", (5040, -1900))
|
||||
links.new(position.outputs[0], axes.inputs[0])
|
||||
depth = axes.outputs["Z"]
|
||||
depth_slow = blur(depth, 480, 600, interior)
|
||||
profile_slow = blur(profile, 480, 0, interior)
|
||||
depth_bump = math("SUBTRACT", depth, depth_slow, 720, 600)
|
||||
profile_bump = math("SUBTRACT", profile, profile_slow, 720, 0)
|
||||
numerator = math("MULTIPLY", depth_bump, profile_bump, 960, 600)
|
||||
denominator = math("MULTIPLY", profile_bump, profile_bump, 960, 0)
|
||||
numerator = blur(numerator, 1200, 600)
|
||||
denominator = blur(denominator, 1200, 0)
|
||||
denominator = math("MAXIMUM", denominator, 1e-10, 1440, 0)
|
||||
structure = math("DIVIDE", numerator, denominator, 1680, 600)
|
||||
structure = math("MAXIMUM", structure, 0, 1920, 600)
|
||||
carrier = _store(group, geometry, "structure", structure, (7300, 0))
|
||||
|
||||
normal = _read(group, "front_normal", (7100, -600), "FLOAT_VECTOR")
|
||||
controls = node(group, "NodeGroupInput", (6860, -1100))
|
||||
# Keep the accepted broad front-direction filter at the default of 50;
|
||||
# zero disables both this filter and the initial front/back normal filters.
|
||||
spread = _math(group, "MULTIPLY", controls.outputs["Normal Smooth"], 256 / 50, (7100, -1100))
|
||||
smooth = node(group, "GeometryNodeBlurAttribute", (7340, -600))
|
||||
smooth.data_type = "FLOAT_VECTOR"
|
||||
links.new(normal, smooth.inputs["Value"])
|
||||
links.new(spread, smooth.inputs["Iterations"])
|
||||
normalize = node(group, "ShaderNodeVectorMath", (7580, -600))
|
||||
normalize.operation = "NORMALIZE"
|
||||
links.new(smooth.outputs[0], normalize.inputs[0])
|
||||
carrier = _store(group, carrier, "inflation_normal", normalize.outputs[0], (7770, 0), "FLOAT_VECTOR")
|
||||
return carrier
|
||||
|
||||
|
||||
def build_inflation_position(group, position, distance, uniform):
|
||||
"""Grow both modes continuously while leaving the rear cap in place."""
|
||||
previous = set(group.nodes)
|
||||
links = group.links
|
||||
|
||||
def math(operation, a, b, x, y):
|
||||
return _math(group, operation, a, b, (x, y))
|
||||
|
||||
structure = _read(group, "structure", (14000, -4300))
|
||||
control = node(group, "NodeGroupInput", (13760, -5000))
|
||||
a = math("MULTIPLY", control.outputs["Thickness"], control.outputs["Front Inflation"], 14240, -5000)
|
||||
# A shifted cubic blend preserves continuous growth and height order.
|
||||
shifted = math("ADD", a, 0.75, 14480, -5000)
|
||||
quadratic = math("MULTIPLY", a, shifted, 14720, -5000)
|
||||
quadratic = math("ADD", quadratic, 0.1875, 14960, -5000)
|
||||
q = math("MULTIPLY", a, quadratic, 15200, -5000)
|
||||
k = math("ADD", structure, 0.25, 14240, -4300)
|
||||
square = math("MULTIPLY", k, k, 14480, -4300)
|
||||
cube = math("MULTIPLY", square, k, 14720, -4300)
|
||||
total = math("ADD", cube, q, 14960, -4300)
|
||||
root = math("POWER", total, 1 / 3, 15200, -4300)
|
||||
root_square = math("MULTIPLY", root, root, 15440, -4000)
|
||||
product = math("MULTIPLY", root, k, 15440, -4300)
|
||||
denominator = math("ADD", root_square, product, 15680, -4300)
|
||||
denominator = math("ADD", denominator, square, 15920, -4300)
|
||||
response = math("DIVIDE", q, denominator, 15920, -4800)
|
||||
profile = node(group, "GeometryNodeInputNamedAttribute", (15920, -3700))
|
||||
profile.data_type = "FLOAT"
|
||||
profile.inputs["Name"].default_value = "o_balloon"
|
||||
balloon = math("MULTIPLY", profile.outputs["Attribute"], response, 16160, -4250)
|
||||
uniform_control = node(group, "NodeGroupInput", (15920, -5100))
|
||||
uniform_amount = math("MULTIPLY", uniform_control.outputs["Uniform Thickness"], uniform_control.outputs["Front Inflation"], 16160, -5100)
|
||||
selected = node(group, "GeometryNodeSwitch", (16400, -4550))
|
||||
selected.input_type = "FLOAT"
|
||||
links.new(uniform, selected.inputs["Switch"])
|
||||
links.new(balloon, selected.inputs["False"])
|
||||
links.new(uniform_amount, selected.inputs["True"])
|
||||
displacement = selected.outputs[0]
|
||||
height = _read(group, "thickness", (14000, -5500))
|
||||
layer = math("DIVIDE", distance, height, 14240, -5500)
|
||||
layer = math("ADD", 1, layer, 14480, -5500)
|
||||
layer = math("MAXIMUM", layer, 0, 14720, -5500)
|
||||
layer = math("MINIMUM", layer, 1, 14960, -5500)
|
||||
displacement = math("MULTIPLY", displacement, layer, 16640, -4550)
|
||||
normal = _read(group, "inflation_normal", (16640, -4000), "FLOAT_VECTOR")
|
||||
offset = node(group, "ShaderNodeVectorMath", (16880, -4550))
|
||||
offset.operation = "SCALE"
|
||||
links.new(normal, offset.inputs[0])
|
||||
links.new(displacement, offset.inputs[3])
|
||||
result = node(group, "ShaderNodeVectorMath", (17120, -4550))
|
||||
result.operation = "ADD"
|
||||
links.new(position, result.inputs[0])
|
||||
links.new(offset.outputs[0], result.inputs[1])
|
||||
for item in set(group.nodes) - previous:
|
||||
item.location.y = -3550 + (item.location.y + 3700) * 0.7
|
||||
return result.outputs[0]
|
||||
Reference in New Issue
Block a user