Refactor comparison operations in geometry nodes

- Introduced a new `compare_node` function to streamline comparison operations across various geometry nodes.
- Replaced instances of mathematical comparison nodes with the new `compare_node` function in multiple files, enhancing code readability and maintainability.
- Updated related logic in cutout, depth surface, and image processing groups to utilize the new comparison method.
- Ensured consistent handling of boolean logic through the introduction of a `boolean_node` function for combining boolean inputs.
This commit is contained in:
2026-09-16 21:42:02 +08:00
parent c62ec2cd48
commit 78bbff6351
19 changed files with 282 additions and 200 deletions

View File

@@ -1,6 +1,9 @@
"""Face separation and connected thickness for Depth Surface assets."""
from .nodes import evaluate_field, store_float_attribute, store_boolean_attribute, remove_attribute_pattern, sample_field
from .nodes import (
boolean_node, compare_node, evaluate_field, store_float_attribute,
store_boolean_attribute, remove_attribute_pattern, sample_field,
)
from .smoothing import edge_boundary_field
@@ -32,21 +35,7 @@ def _remove_quad_strip_faces(group, geometry):
nodes, links = group.nodes, group.links
def equals(value, count):
compare = nodes.new("FunctionNodeCompare")
compare.data_type, compare.operation = "INT", "EQUAL"
links.new(value, compare.inputs[2])
if isinstance(count, int):
compare.inputs[3].default_value = count
else:
links.new(count, compare.inputs[3])
return compare.outputs["Result"]
def combine(operation, a, b):
node = nodes.new("FunctionNodeIntegerMath")
node.operation = operation
links.new(a, node.inputs[0])
links.new(b, node.inputs[1])
return node.outputs[0]
return compare_node(group, "EQUAL", value, count, data_type="INT")
boundaries = []
for offset in range(4):
@@ -64,13 +53,13 @@ def _remove_quad_strip_faces(group, geometry):
neighbors = nodes.new("GeometryNodeInputMeshEdgeNeighbors")
links.new(neighbors.outputs["Face Count"], sample.inputs["Value"])
boundaries.append(equals(sample.outputs["Value"], 1))
opposite = combine(
"MAXIMUM",
combine("MULTIPLY", boundaries[0], boundaries[2]),
combine("MULTIPLY", boundaries[1], boundaries[3]),
opposite = boolean_node(
group, "OR",
boolean_node(group, "AND", boundaries[0], boundaries[2]),
boolean_node(group, "AND", boundaries[1], boundaries[3]),
)
face = nodes.new("GeometryNodeInputMeshFaceNeighbors")
selected = combine("MULTIPLY", equals(face.outputs["Vertex Count"], 4), opposite)
selected = boolean_node(group, "AND", equals(face.outputs["Vertex Count"], 4), opposite)
delete = nodes.new("GeometryNodeDeleteGeometry")
delete.domain, delete.mode = "FACE", "ALL"
links.new(geometry, delete.inputs["Geometry"])
@@ -83,25 +72,11 @@ def _remove_triangle_strip_faces(group, geometry):
nodes, links = group.nodes, group.links
def equals(value, count):
compare = nodes.new("FunctionNodeCompare")
compare.data_type, compare.operation = "INT", "EQUAL"
links.new(value, compare.inputs[2])
if isinstance(count, int):
compare.inputs[3].default_value = count
else:
links.new(count, compare.inputs[3])
return compare.outputs["Result"]
return compare_node(group, "EQUAL", value, count, data_type="INT")
def combine(operation, a, b):
node = nodes.new("FunctionNodeIntegerMath")
node.operation = operation
links.new(a, node.inputs[0])
links.new(b, node.inputs[1])
return node.outputs[0]
def sample(value, index, domain):
def sample(value, index, domain, data_type="INT"):
node = nodes.new("GeometryNodeFieldAtIndex")
node.data_type, node.domain = "INT", domain
node.data_type, node.domain = data_type, domain
links.new(value, node.inputs["Value"])
links.new(index, node.inputs["Index"])
return node.outputs["Value"]
@@ -138,16 +113,29 @@ def _remove_triangle_strip_faces(group, geometry):
sides.append((triangle, vertex(corner), vertex(tip), boundary(end), boundary(tip)))
a, b = sides
same_direction = equals(a[1], b[1])
same_order = combine("MAXIMUM", combine("MULTIPLY", a[3], b[3]), combine("MULTIPLY", a[4], b[4]))
reverse_order = combine("MAXIMUM", combine("MULTIPLY", a[3], b[4]), combine("MULTIPLY", a[4], b[3]))
pair = combine(
"MAXIMUM",
combine("MULTIPLY", same_direction, reverse_order),
combine("MULTIPLY", equals(same_direction, 0), same_order),
same_order = boolean_node(
group, "OR",
boolean_node(group, "AND", a[3], b[3]),
boolean_node(group, "AND", a[4], b[4]),
)
reverse_order = boolean_node(
group, "OR",
boolean_node(group, "AND", a[3], b[4]),
boolean_node(group, "AND", a[4], b[3]),
)
pair = boolean_node(
group, "OR",
boolean_node(group, "AND", same_direction, reverse_order),
boolean_node(group, "AND", boolean_node(group, "NOT", same_direction), same_order),
)
neighbors = nodes.new("GeometryNodeInputMeshEdgeNeighbors")
for condition in (equals(neighbors.outputs["Face Count"], 2), a[0], b[0], equals(equals(a[2], b[2]), 0)):
pair = combine("MULTIPLY", pair, condition)
for condition in (
equals(neighbors.outputs["Face Count"], 2),
a[0],
b[0],
boolean_node(group, "NOT", equals(a[2], b[2])),
):
pair = boolean_node(group, "AND", pair, condition)
face_index = nodes.new("GeometryNodeInputIndex")
corners = nodes.new("GeometryNodeCornersOfFace")
@@ -157,14 +145,21 @@ def _remove_triangle_strip_faces(group, geometry):
corner = offset_corner(corners.outputs["Corner Index"], offset)
edge = nodes.new("GeometryNodeEdgesOfCorner")
links.new(corner, edge.inputs["Corner Index"])
match = sample(pair, edge.outputs["Next Edge Index"], "EDGE")
paired = match if paired is None else combine("MAXIMUM", paired, match)
match = sample(pair, edge.outputs["Next Edge Index"], "EDGE", "BOOLEAN")
paired = match if paired is None else boolean_node(group, "OR", paired, match)
rim = boundary(corner)
boundary_count = rim if boundary_count is None else combine("ADD", boundary_count, rim)
selected = combine(
"MULTIPLY",
if boundary_count is None:
boundary_count = rim
else:
add = nodes.new("FunctionNodeIntegerMath")
add.operation = "ADD"
links.new(boundary_count, add.inputs[0])
links.new(rim, add.inputs[1])
boundary_count = add.outputs[0]
selected = boolean_node(
group, "AND",
equals(corners.outputs["Total"], 3),
combine("MAXIMUM", paired, equals(boundary_count, 3)),
boolean_node(group, "OR", paired, equals(boundary_count, 3)),
)
delete = nodes.new("GeometryNodeDeleteGeometry")
delete.domain, delete.mode = "FACE", "ALL"
@@ -233,24 +228,24 @@ def split_depth_surface(group, geometry, face_camera, strength, reference_depth,
reference = _math(group, "MAXIMUM", reference_depth, 1e-8)
# Uniform Scale cancels between the distance jump and local spacing correction.
# Cross multiplication also avoids dividing by the local distance.
selected = _math(
selected = compare_node(
group,
"GREATER_THAN",
_math(group, "MULTIPLY", _math(group, "MULTIPLY", delta, reference), strength),
_math(group, "MULTIPLY", _math(group, "MULTIPLY", distance.outputs["Value"], local_distance), 2.5),
)
selected = _math(
selected = boolean_node(
group,
"MULTIPLY",
"AND",
selected,
_math(group, "GREATER_THAN", distance.outputs["Value"], 0.0),
compare_node(group, "GREATER_THAN", distance.outputs["Value"], 0.0),
)
neighbors = nodes.new("GeometryNodeInputMeshEdgeNeighbors")
selected = _math(
selected = boolean_node(
group,
"MULTIPLY",
"AND",
selected,
_math(group, "COMPARE", neighbors.outputs["Face Count"], 2.0),
compare_node(group, "EQUAL", neighbors.outputs["Face Count"], 2, data_type="INT"),
)
cut_edges = nodes.new("GeometryNodeFieldOnDomain")
cut_edges.data_type = "FLOAT"
@@ -262,12 +257,12 @@ def split_depth_surface(group, geometry, face_camera, strength, reference_depth,
links.new(cut_edges.outputs["Value"], cut_vertices.inputs["Value"])
corners, cut = store_boolean_attribute(
group, geometry, CUT_ATTRIBUTE,
_math(group, "GREATER_THAN", cut_vertices.outputs["Value"], 0.0), domain="CORNER",
compare_node(group, "GREATER_THAN", cut_vertices.outputs["Value"], 0.0), domain="CORNER",
)
split = nodes.new("GeometryNodeSplitEdges")
links.new(corners, split.inputs["Mesh"])
links.new(selected, split.inputs["Selection"])
enabled = _math(group, "GREATER_THAN", strength, 0.0)
enabled = compare_node(group, "GREATER_THAN", strength, 0.0)
choose = nodes.new("GeometryNodeSwitch")
choose.input_type = "GEOMETRY"
links.new(enabled, choose.inputs["Switch"])
@@ -286,7 +281,7 @@ def build_surface_camera(group, original, separated, cut_vertex, strength):
cut_boundary = evaluate_field(group, edge_boundary_field(nodes, links), "BOOLEAN", "POINT")
enabled = nodes.new("GeometryNodeSwitch")
enabled.input_type = "BOOLEAN"
links.new(_math(group, "GREATER_THAN", strength, 0.0), enabled.inputs["Switch"])
links.new(compare_node(group, "GREATER_THAN", strength, 0.0), enabled.inputs["Switch"])
enabled.inputs["False"].default_value = False
links.new(cut_vertex, enabled.inputs["True"])
combined = nodes.new("FunctionNodeBooleanMath")
@@ -382,11 +377,16 @@ def thicken_depth_surface(group, geometry, thickness, normal_smooth=None, *,
links.new(value, join.inputs["Geometry"])
construction = join.outputs["Geometry"]
if weld_zero_thickness:
positive = _math(group, "GREATER_THAN", source_thickness, 1e-12)
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])
links.new(_math(group, "MULTIPLY", positive, layer_axes.outputs["Y"]), flattened.inputs["Y"])
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"])