Files
AnyImage/tools/nodes/common/cutout_center.py
nan a0dd6e4a88 feat: Add restore cutout depth extension with edge depth correction
- Introduced a new extension for restoring cutout depth with edge correction.
- Implemented edge depth extension algorithm in `edge_depth.py` to enhance depth accuracy near cutout edges.
- Updated `generate_moge_artifacts` to include an option for extending depth edges during depth generation.
- Modified cutout job processing to enable edge depth extension based on depth request.
- Added comprehensive tests for edge depth functionality, ensuring correct behavior under various scenarios.
- Updated existing node loading functions to remove asset revision dependencies, simplifying the loading process.
- Created new design and proposal documentation outlining the goals and decisions for the extension.
2026-09-13 20:36:52 +08:00

79 lines
3.3 KiB
Python

"""Build a shared center on the original Cutout mesh."""
from .nodes import evaluate_field, group_input, sample_field
def vector(group, operation, first, second=None):
node = group.nodes.new("ShaderNodeVectorMath")
node.operation = operation
for value, socket in ((first, node.inputs[0]), (second, node.inputs[3 if operation == "SCALE" else 1])):
if value is None:
continue
if hasattr(value, "node"):
group.links.new(value, socket)
else:
socket.default_value = value
return node.outputs[0]
def axes(group, value):
node = group.nodes.new("ShaderNodeSeparateXYZ")
group.links.new(value, node.inputs[0])
return node.outputs
def combine(group, x, y, z=0):
node = group.nodes.new("ShaderNodeCombineXYZ")
for value, socket in zip((x, y, z), node.inputs):
if hasattr(value, "node"):
group.links.new(value, socket)
else:
socket.default_value = value
return node.outputs[0]
def set_position(group, geometry, position):
node = group.nodes.new("GeometryNodeSetPosition")
group.links.new(geometry, node.inputs["Geometry"])
group.links.new(position, node.inputs["Position"])
return node.outputs[0]
def sample_surface(group, geometry, value, data_type, domain_position, query):
"""Keep point indices on points before the face interpolation adapts domains."""
field = evaluate_field(group, sample_field(group, geometry, value, data_type), data_type, "POINT")
flat = set_position(group, geometry, domain_position)
sample = group.nodes.new("GeometryNodeSampleNearestSurface")
sample.data_type = data_type
group.links.new(flat, sample.inputs["Mesh"])
group.links.new(field, sample.inputs["Value"])
group.links.new(query, sample.inputs["Sample Position"])
return sample.outputs[0]
def build_center_surface(group, unsplit):
"""Smooth the unsplit displaced surface without moving its XY coordinates."""
nodes, links = group.nodes, group.links
controls = group_input(nodes, {"Center Smooth"})
position = nodes.new("GeometryNodeInputPosition").outputs[0]
components = axes(group, position)
uv = nodes.new("GeometryNodeInputNamedAttribute")
uv.data_type = "FLOAT_VECTOR"
uv.inputs["Name"].default_value = "UVMap"
coordinates = uv.outputs["Attribute"]
blur = nodes.new("GeometryNodeBlurAttribute")
blur.data_type = "FLOAT"
links.new(components["Z"], blur.inputs["Value"])
links.new(controls.outputs["Center Smooth"], blur.inputs["Iterations"])
support = set_position(group, unsplit, combine(group, components["X"], components["Y"], blur.outputs[0]))
height = sample_surface(group, support, components["Z"], "FLOAT", coordinates, coordinates)
normal = nodes.new("GeometryNodeInputNormal").outputs[0]
normal_blur = nodes.new("GeometryNodeBlurAttribute")
normal_blur.data_type = "FLOAT_VECTOR"
links.new(evaluate_field(group, normal, "FLOAT_VECTOR", "POINT"), normal_blur.inputs["Value"])
links.new(controls.outputs["Center Smooth"], normal_blur.inputs["Iterations"])
normal = normal_blur.outputs[0]
normal = vector(group, "NORMALIZE", sample_surface(group, support, normal, "FLOAT_VECTOR", coordinates, coordinates))
return {"geometry": support, "preview": support, "height": height, "normal": normal}