feat: implement separate geometry handling for panorama mask regions
- Replace Delete Geometry with Separate Geometry in O Image Depth Panorama to manage low and high mask areas independently. - Adjust depth processing to ensure low mask regions are displaced to Dome Radius while high mask regions follow existing depth rules. - Update tests to validate new behavior, ensuring outputs align with expected results for both mask states. - Enhance material handling by introducing emission strength in the shadeless group.
This commit is contained in:
@@ -4,13 +4,11 @@ import math
|
||||
|
||||
import bpy
|
||||
|
||||
from ..common.validity import delete_invalid_faces
|
||||
|
||||
from ..common.depth_surface import (
|
||||
_math, split_depth_surface, build_surface_camera,
|
||||
)
|
||||
from ..common.nodes import (
|
||||
compare_node, float_input, group_input, interface_socket,
|
||||
boolean_node, compare_node, float_input, group_input, interface_socket,
|
||||
prepare_node_group, evaluate_field, remove_attribute_pattern,
|
||||
)
|
||||
from ..common.material_slots import inherit_material_slots
|
||||
@@ -51,7 +49,7 @@ def build_image_depth_panorama_group():
|
||||
float_input(group, "Depth Split", 0.1, maximum=1, subtype="FACTOR").description = "Split the mesh where depth changes abruptly. Scaled by Depth Scale: a flat projection stays whole, higher values detect smaller depth jumps."
|
||||
valid_only = interface_socket(group, "Depth Mask", "INPUT", "NodeSocketBool")
|
||||
valid_only.default_value = True
|
||||
valid_only.description = "Remove mesh areas marked invalid by the depth mask."
|
||||
valid_only.description = "Remove mesh areas marked invalid by the depth mask; with the mask off, they rest on the dome sphere."
|
||||
options = group.interface.new_panel("Options", default_closed=True)
|
||||
boundary_smooth = interface_socket(group, "Boundary Smooth", "INPUT", "NodeSocketInt", options)
|
||||
boundary_smooth.default_value, boundary_smooth.min_value, boundary_smooth.max_value = 2, 0, 16
|
||||
@@ -137,19 +135,16 @@ def build_image_depth_panorama_group():
|
||||
threshold_input = group_input(nodes, {"Mask Threshold"})
|
||||
invalid = compare_node(group, "LESS_THAN", face_alpha, threshold_input.outputs["Mask Threshold"])
|
||||
protected = uv_map.outputs["Geometry"]
|
||||
culled = delete_invalid_faces(group, protected, invalid)
|
||||
cull_controls = group_input(nodes, {"Depth Mask"})
|
||||
cull = nodes.new("GeometryNodeSwitch")
|
||||
cull.input_type = "GEOMETRY"
|
||||
links.new(cull_controls.outputs["Depth Mask"], cull.inputs["Switch"])
|
||||
links.new(culled, cull.inputs["True"])
|
||||
links.new(protected, cull.inputs["False"])
|
||||
separate = nodes.new("GeometryNodeSeparateGeometry")
|
||||
separate.domain = "FACE"
|
||||
links.new(protected, separate.inputs["Geometry"])
|
||||
links.new(invalid, separate.inputs["Selection"])
|
||||
controls = group_input(nodes, {"Depth Split", "Depth Scale"})
|
||||
strength = controls.outputs["Depth Split"]
|
||||
radial = nodes.new("ShaderNodeSeparateXYZ")
|
||||
links.new(face_camera, radial.inputs[0])
|
||||
geometry, corner_camera, cut_vertex = split_depth_surface(
|
||||
group, cull.outputs["Output"], face_camera, strength, 1.0, controls.outputs["Depth Scale"],
|
||||
group, separate.outputs["Inverted"], face_camera, strength, 1.0, controls.outputs["Depth Scale"],
|
||||
face_distance=radial.outputs["X"], face_center=face.outputs["Value"],
|
||||
)
|
||||
distance = build_surface_camera(group, pixel, corner_camera, cut_vertex, strength)
|
||||
@@ -157,16 +152,10 @@ def build_image_depth_panorama_group():
|
||||
links.new(distance, axes.inputs[0])
|
||||
distance = smooth_boundary_depth(group, axes.outputs["X"])
|
||||
dome_radius = group_input(nodes, {"Dome Radius"}).outputs["Dome Radius"]
|
||||
fallback = nodes.new("GeometryNodeSwitch")
|
||||
fallback.input_type = "FLOAT"
|
||||
links.new(invalid, fallback.inputs["Switch"])
|
||||
links.new(distance, fallback.inputs["False"])
|
||||
links.new(dome_radius, fallback.inputs["True"])
|
||||
controls = group_input(nodes, {"Depth Scale"})
|
||||
radius = _math(
|
||||
group, "ADD", dome_radius,
|
||||
_math(group, "MULTIPLY", controls.outputs["Depth Scale"],
|
||||
_math(group, "SUBTRACT", fallback.outputs[0], dome_radius)),
|
||||
_math(group, "SUBTRACT", distance, dome_radius)),
|
||||
)
|
||||
radial = nodes.new("ShaderNodeVectorMath")
|
||||
radial.operation = "SCALE"
|
||||
@@ -177,8 +166,41 @@ def build_image_depth_panorama_group():
|
||||
links.new(radial.outputs[0], deform.inputs["Position"])
|
||||
geometry = remove_attribute_pattern(group, deform.outputs[0], "_o_*")
|
||||
geometry = smooth_cut_boundary(group, geometry, cut_vertex, outline_scale=1.0)
|
||||
# The excluded faces rest on the dome sphere and relax along their own boundary.
|
||||
domed = nodes.new("ShaderNodeVectorMath")
|
||||
domed.operation = "SCALE"
|
||||
links.new(position, domed.inputs[0])
|
||||
links.new(dome_radius, domed.inputs[3])
|
||||
resting = nodes.new("GeometryNodeSetPosition")
|
||||
links.new(separate.outputs["Selection"], resting.inputs["Geometry"])
|
||||
links.new(domed.outputs[0], resting.inputs["Position"])
|
||||
resting = smooth_cut_boundary(group, resting.outputs["Geometry"], outline_scale=1.0)
|
||||
joined = nodes.new("GeometryNodeJoinGeometry")
|
||||
links.new(geometry, joined.inputs["Geometry"])
|
||||
links.new(resting, joined.inputs["Geometry"])
|
||||
# A flat projection without the mask is the dome sphere alone: no split, no seam.
|
||||
dome_sphere = nodes.new("GeometryNodeSetPosition")
|
||||
links.new(protected, dome_sphere.inputs["Geometry"])
|
||||
links.new(domed.outputs[0], dome_sphere.inputs["Position"])
|
||||
cull_controls = group_input(nodes, {"Depth Mask"})
|
||||
dome_only = boolean_node(
|
||||
group,
|
||||
"AND",
|
||||
boolean_node(group, "NOT", compare_node(group, "GREATER_THAN", controls.outputs["Depth Scale"], 0.0)),
|
||||
boolean_node(group, "NOT", cull_controls.outputs["Depth Mask"]),
|
||||
)
|
||||
unmasked = nodes.new("GeometryNodeSwitch")
|
||||
unmasked.input_type = "GEOMETRY"
|
||||
links.new(dome_only, unmasked.inputs["Switch"])
|
||||
links.new(joined.outputs["Geometry"], unmasked.inputs["False"])
|
||||
links.new(dome_sphere.outputs["Geometry"], unmasked.inputs["True"])
|
||||
cull = nodes.new("GeometryNodeSwitch")
|
||||
cull.input_type = "GEOMETRY"
|
||||
links.new(cull_controls.outputs["Depth Mask"], cull.inputs["Switch"])
|
||||
links.new(unmasked.outputs["Output"], cull.inputs["False"])
|
||||
links.new(geometry, cull.inputs["True"])
|
||||
flip = nodes.new("GeometryNodeFlipFaces")
|
||||
links.new(geometry, flip.inputs["Mesh"])
|
||||
links.new(cull.outputs["Output"], flip.inputs["Mesh"])
|
||||
smooth = nodes.new("GeometryNodeSetShadeSmooth")
|
||||
smooth.domain = "FACE"
|
||||
smooth.inputs["Shade Smooth"].default_value = True
|
||||
|
||||
Reference in New Issue
Block a user