Files
AnyImage/tools/node_assets/node_graph.py
nan 08309f719d feat: Implement cutout tool and depth generation features
- Added a new cutout tool for selecting regions in images, allowing users to choose mesh shapes based on selected areas.
- Introduced depth generation capabilities for rasterized images, including normal data extraction.
- Created jobs for generating depth planes and region data, enhancing the AI data generation workflow.
- Developed a selection refinement process for cutout regions, improving accuracy in generated outputs.
- Established a new region module for handling rasterized image regions and their associated AI generation tasks.
- Enhanced node graph builders for asset generation, facilitating smoother integration of new features.
2026-08-30 23:09:13 +08:00

182 lines
8.3 KiB
Python

"""Node graph builders used only while generating bundled assets."""
def group_input(nodes, location, visible_names):
node = nodes.new("NodeGroupInput")
node.location = location
visible = set(visible_names)
for output in node.outputs:
output.hide = output.name not in visible
return node
def edge_boundary_field(nodes, links, location):
x, y = location
vertex_neighbors = nodes.new("GeometryNodeInputMeshVertexNeighbors")
vertex_neighbors.location = (x, y + 150.0)
loose_vertex = nodes.new("FunctionNodeCompare")
loose_vertex.data_type = "INT"
loose_vertex.operation = "EQUAL"
loose_vertex_a = next(
socket for socket in loose_vertex.inputs if socket.identifier == "A_INT"
)
loose_vertex_b = next(
socket for socket in loose_vertex.inputs if socket.identifier == "B_INT"
)
loose_vertex_b.default_value = 1
loose_vertex.location = (x + 200.0, y + 150.0)
links.new(vertex_neighbors.outputs["Vertex Count"], loose_vertex_a)
edge_neighbors = nodes.new("GeometryNodeInputMeshEdgeNeighbors")
edge_neighbors.location = (x, y - 150.0)
boundary_edge = nodes.new("FunctionNodeCompare")
boundary_edge.data_type = "INT"
boundary_edge.operation = "EQUAL"
boundary_edge_a = next(
socket for socket in boundary_edge.inputs if socket.identifier == "A_INT"
)
boundary_edge_b = next(
socket for socket in boundary_edge.inputs if socket.identifier == "B_INT"
)
boundary_edge_b.default_value = 1
boundary_edge.location = (x + 200.0, y - 150.0)
links.new(edge_neighbors.outputs["Face Count"], boundary_edge_a)
edge_domain = nodes.new("GeometryNodeFieldOnDomain")
edge_domain.data_type = "BOOLEAN"
edge_domain.domain = "EDGE"
edge_domain.location = (x + 400.0, y - 150.0)
links.new(boundary_edge.outputs["Result"], edge_domain.inputs["Value"])
is_boundary = nodes.new("FunctionNodeBooleanMath")
is_boundary.operation = "OR"
is_boundary.location = (x + 600.0, y)
links.new(edge_domain.outputs["Value"], is_boundary.inputs[0])
links.new(loose_vertex.outputs["Result"], is_boundary.inputs[1])
return is_boundary.outputs["Boolean"]
def expand_smooth(
group, geometry, x_offset=0.0, iterations_name="Iterations"
):
nodes = group.nodes
links = group.links
existing_nodes = set(nodes)
iteration_controls = group_input(nodes, (550.0, 1150.0), {iterations_name})
weight_controls = group_input(nodes, (2300.0, 1450.0), {"Smooth"})
repeat_output = nodes.new("GeometryNodeRepeatOutput")
repeat_output.location = (3050.0, 550.0)
repeat_input = nodes.new("GeometryNodeRepeatInput")
repeat_input.pair_with_output(repeat_output)
repeat_output.repeat_items.new("GEOMETRY", "Geometry")
repeat_input.location = (550.0, 550.0)
links.new(
iteration_controls.outputs[iterations_name],
repeat_input.inputs["Iterations"],
)
links.new(geometry, repeat_input.inputs["Geometry"])
boundary_field = edge_boundary_field(nodes, links, (750.0, -350.0))
boundary_points = nodes.new("GeometryNodeFieldOnDomain")
boundary_points.data_type = "BOOLEAN"
boundary_points.domain = "POINT"
boundary_points.location = (1550.0, -350.0)
links.new(boundary_field, boundary_points.inputs["Value"])
boundary_geometry = nodes.new("GeometryNodeSeparateGeometry")
boundary_geometry.domain = "POINT"
boundary_geometry.location = (1750.0, -150.0)
links.new(repeat_input.outputs["Geometry"], boundary_geometry.inputs["Geometry"])
links.new(boundary_points.outputs["Value"], boundary_geometry.inputs["Selection"])
position = nodes.new("GeometryNodeInputPosition")
position.location = (750.0, 850.0)
smooth_all = nodes.new("GeometryNodeBlurAttribute")
smooth_all.data_type = "FLOAT_VECTOR"
smooth_all.inputs["Iterations"].default_value = 1
smooth_all.inputs["Weight"].default_value = 1.0
smooth_all.location = (950.0, 850.0)
links.new(position.outputs["Position"], smooth_all.inputs["Value"])
boundary_position = nodes.new("GeometryNodeInputPosition")
boundary_position.location = (1350.0, 250.0)
smooth_boundary = nodes.new("GeometryNodeBlurAttribute")
smooth_boundary.data_type = "FLOAT_VECTOR"
smooth_boundary.inputs["Iterations"].default_value = 1
smooth_boundary.inputs["Weight"].default_value = 0.5
smooth_boundary.location = (1550.0, 250.0)
links.new(boundary_position.outputs["Position"], smooth_boundary.inputs["Value"])
nearest_boundary = nodes.new("GeometryNodeSampleNearest")
nearest_boundary.domain = "POINT"
nearest_boundary.location = (1800.0, 150.0)
links.new(boundary_geometry.outputs["Selection"], nearest_boundary.inputs["Geometry"])
sample_boundary = nodes.new("GeometryNodeSampleIndex")
sample_boundary.data_type = "FLOAT_VECTOR"
sample_boundary.domain = "POINT"
sample_boundary.clamp = False
sample_boundary.location = (2050.0, 250.0)
links.new(boundary_geometry.outputs["Selection"], sample_boundary.inputs["Geometry"])
links.new(smooth_boundary.outputs["Value"], sample_boundary.inputs["Value"])
links.new(nearest_boundary.outputs["Index"], sample_boundary.inputs["Index"])
choose_boundary = nodes.new("GeometryNodeSwitch")
choose_boundary.input_type = "VECTOR"
choose_boundary.location = (2300.0, 350.0)
direct_boundary = edge_boundary_field(nodes, links, (750.0, 450.0))
links.new(direct_boundary, choose_boundary.inputs["Switch"])
links.new(smooth_all.outputs["Value"], choose_boundary.inputs["False"])
links.new(sample_boundary.outputs["Value"], choose_boundary.inputs["True"])
boundary_mix = nodes.new("ShaderNodeMix")
boundary_mix.data_type = "VECTOR"
boundary_mix.inputs[0].default_value = 1.0
boundary_mix.location = (2550.0, 400.0)
links.new(smooth_all.outputs["Value"], boundary_mix.inputs[4])
links.new(choose_boundary.outputs["Output"], boundary_mix.inputs[5])
normal = nodes.new("GeometryNodeInputNormal")
normal.location = (1350.0, 1050.0)
smooth_normal = nodes.new("GeometryNodeBlurAttribute")
smooth_normal.data_type = "FLOAT_VECTOR"
smooth_normal.inputs["Iterations"].default_value = 10
smooth_normal.inputs["Weight"].default_value = 1.0
smooth_normal.location = (1550.0, 1050.0)
links.new(normal.outputs["Normal"], smooth_normal.inputs["Value"])
normal_coherence = nodes.new("ShaderNodeVectorMath")
normal_coherence.operation = "LENGTH"
normal_coherence.location = (1800.0, 1050.0)
links.new(smooth_normal.outputs["Value"], normal_coherence.inputs[0])
sharp_switch = nodes.new("GeometryNodeSwitch")
sharp_switch.input_type = "FLOAT"
sharp_switch.inputs["Switch"].default_value = True
sharp_switch.inputs["False"].default_value = 1.0
sharp_switch.location = (2050.0, 1050.0)
links.new(normal_coherence.outputs["Value"], sharp_switch.inputs["True"])
smooth_weight = nodes.new("ShaderNodeMath")
smooth_weight.operation = "MULTIPLY"
smooth_weight.location = (2300.0, 1050.0)
links.new(weight_controls.outputs["Smooth"], smooth_weight.inputs[0])
links.new(sharp_switch.outputs["Output"], smooth_weight.inputs[1])
target_position = nodes.new("GeometryNodeSwitch")
target_position.input_type = "VECTOR"
target_position.inputs["Switch"].default_value = True
target_position.location = (2550.0, 700.0)
links.new(smooth_all.outputs["Value"], target_position.inputs["False"])
links.new(boundary_mix.outputs["Result"], target_position.inputs["True"])
current_position = nodes.new("GeometryNodeInputPosition")
current_position.location = (2300.0, 850.0)
mix_position = nodes.new("ShaderNodeMix")
mix_position.data_type = "VECTOR"
mix_position.location = (2800.0, 850.0)
links.new(smooth_weight.outputs[0], mix_position.inputs[0])
links.new(current_position.outputs["Position"], mix_position.inputs[4])
links.new(target_position.outputs["Output"], mix_position.inputs[5])
set_position = nodes.new("GeometryNodeSetPosition")
set_position.location = (2800.0, 550.0)
links.new(repeat_input.outputs["Geometry"], set_position.inputs["Geometry"])
links.new(mix_position.outputs["Result"], set_position.inputs["Position"])
links.new(set_position.outputs["Geometry"], repeat_output.inputs["Geometry"])
for node in set(nodes) - existing_nodes:
node.location.x += x_offset
return repeat_output.outputs["Geometry"]