Refactor image mesh processing and background removal functionality

- Updated NODE_GROUP_NAME from "O Refine DA3 Surface" to "O DA3 Surface".
- Introduced `remove_surface_background` function to filter out background faces and compact geometry.
- Enhanced `build_image_mesh` to conditionally remove background faces based on a new property.
- Added `snapform_remove_background` and `snapform_keep_original` properties to operators for better control over background handling.
- Updated UI to reflect new properties and improved resolution scaling logic.
- Refactored tests to cover new background removal functionality and ensure proper geometry handling.
- Adjusted geometry nodes and validation scripts to remove references to background handling.
This commit is contained in:
2026-08-12 16:15:14 +08:00
parent 7cd5681a8b
commit c3a151ad2c
13 changed files with 477 additions and 271 deletions

View File

@@ -3,9 +3,8 @@ from pathlib import Path
import bpy
GROUP_NAME = "O Refine DA3 Surface"
GROUP_NAME = "O DA3 Surface"
DEPTH_PLANE_GROUP_NAME = "O DA3 Depth Plane"
REMOVE_BACKGROUND_ATTRIBUTE = "o_remove_background"
MATERIAL_DISPLACEMENT_ATTRIBUTE = "o_material_displacement"
BACKGROUND_DEPTH_ATTRIBUTE = "o_background_depth"
DEPTH_SCALE_ATTRIBUTE = "o_depth_scale"
@@ -424,14 +423,6 @@ def build_group():
edge.min_value = 0.0
edge.max_value = 100.0
edge.subtype = "PERCENTAGE"
background = new_socket(
interface,
"Background",
"INPUT",
"NodeSocketBool",
parent=culling_panel,
)
background.default_value = True
options_panel = interface.new_panel(
name="Options",
@@ -454,7 +445,6 @@ def build_group():
geometry_input = new_group_input(nodes, (-650.0, 50.0), {"Geometry"})
confidence_input = new_group_input(nodes, (-1800.0, 800.0), {"Confidence"})
stretch_input = new_group_input(nodes, (-1800.0, 50.0), {"Stretch"})
background_input = new_group_input(nodes, (-1800.0, -350.0), {"Background"})
remesh_input = new_group_input(
nodes,
(-100.0, -500.0),
@@ -523,25 +513,11 @@ def build_group():
links.new(edge_attribute.outputs["Attribute"], input_socket(edge_compare, "A", 0))
links.new(edge_reciprocal.outputs[0], input_socket(edge_compare, "B", 1))
background_attribute = nodes.new("GeometryNodeInputNamedAttribute")
background_attribute.data_type = "BOOLEAN"
background_attribute.inputs["Name"].default_value = "o_background_face"
background_attribute.location = (-1800.0, -200.0)
background_and = nodes.new("FunctionNodeBooleanMath")
background_and.operation = "AND"
background_and.location = (-1450.0, -200.0)
links.new(background_attribute.outputs["Attribute"], background_and.inputs[0])
links.new(background_input.outputs["Background"], background_and.inputs[1])
first_or = nodes.new("FunctionNodeBooleanMath")
first_or.operation = "OR"
first_or.location = (-950.0, 450.0)
links.new(confidence_compare.outputs["Result"], first_or.inputs[0])
links.new(edge_compare.outputs["Result"], first_or.inputs[1])
final_or = nodes.new("FunctionNodeBooleanMath")
final_or.operation = "OR"
final_or.location = (-750.0, 300.0)
links.new(first_or.outputs[0], final_or.inputs[0])
links.new(background_and.outputs[0], final_or.inputs[1])
full_stretch = new_compare(
nodes,
"FLOAT",
@@ -556,7 +532,7 @@ def build_group():
culling_selection = nodes.new("FunctionNodeBooleanMath")
culling_selection.operation = "OR"
culling_selection.location = (-550.0, 300.0)
links.new(final_or.outputs[0], culling_selection.inputs[0])
links.new(first_or.outputs[0], culling_selection.inputs[0])
links.new(full_stretch.outputs["Result"], culling_selection.inputs[1])
delete_geometry = nodes.new("GeometryNodeDeleteGeometry")
delete_geometry.domain = "FACE"
@@ -638,7 +614,7 @@ def build_group():
group.color_tag = "GEOMETRY"
group.description = (
"Cull DA3/BEN2 faces, repair merged topology, preserve boundaries, "
"Cull DA3 faces, repair merged topology, preserve boundaries, "
"and reproject UVs."
)
group.use_fake_user = True
@@ -707,6 +683,15 @@ def build_depth_plane_group():
"INPUT",
"NodeSocketMenu",
)
subdivision = new_socket(
interface,
"Subdivision",
"INPUT",
"NodeSocketInt",
)
subdivision.default_value = 5
subdivision.min_value = 0
subdivision.max_value = 10
displacement_strength = new_socket(
interface,
"Depth Scale",
@@ -717,39 +702,17 @@ def build_depth_plane_group():
displacement_strength.min_value = -10000.0
displacement_strength.max_value = 10000.0
displacement_strength.structure_type = "SINGLE"
subdivision = new_socket(
interface,
"Subdivision",
"INPUT",
"NodeSocketInt",
)
subdivision.default_value = 5
subdivision.min_value = 0
subdivision.max_value = 10
background_panel = interface.new_panel(
name="Remove Background",
default_closed=True,
)
remove_background = new_socket(
interface,
"Remove Background",
"INPUT",
"NodeSocketBool",
parent=background_panel,
)
remove_background.default_value = False
remove_background.is_panel_toggle = True
background_depth = new_socket(
interface,
"Background Depth",
"INPUT",
"NodeSocketFloat",
parent=background_panel,
)
background_depth.default_value = 0.0
background_depth.min_value = -10000.0
background_depth.max_value = 10000.0
background_depth.subtype = "DISTANCE"
background_depth.structure_type = "SINGLE"
data_panel = interface.new_panel(name="Data", default_closed=True)
depth_image = new_socket(
interface,
@@ -774,6 +737,7 @@ def build_depth_plane_group():
)
frame_data = []
for name, socket_type, default_value in (
("Frame", "NodeSocketInt", 1),
("Frame Start", "NodeSocketInt", 1),
("Frames", "NodeSocketInt", 1),
("Cyclic", "NodeSocketBool", False),
@@ -786,6 +750,8 @@ def build_depth_plane_group():
parent=data_panel,
)
socket.default_value = default_value
if name == "Frame":
socket.structure_type = "SINGLE"
if name == "Frames":
socket.min_value = 1
frame_data.append(socket)
@@ -809,6 +775,7 @@ def build_depth_plane_group():
nodes,
(50.0, 1750.0),
{
"Frame",
"Frame Start",
"Frames",
"Cyclic",
@@ -817,7 +784,7 @@ def build_depth_plane_group():
background_input = new_group_input(
nodes,
(350.0, 250.0),
{"Remove Background", "Background Depth"},
{"Background Depth"},
)
displacement_input = new_group_input(
nodes,
@@ -924,12 +891,10 @@ def build_depth_plane_group():
)
links.new(grid.outputs["UV Map"], depth_texture.inputs["Vector"])
links.new(grid.outputs["UV Map"], foreground_texture.inputs["Vector"])
scene_time = nodes.new("GeometryNodeInputSceneTime")
scene_time.location = (50.0, 2200.0)
sequence_frame = build_sequence_frame(
nodes,
links,
scene_time.outputs["Frame"],
frames_input.outputs["Frame"],
frames_input,
2200.0,
)
@@ -964,14 +929,6 @@ def build_depth_plane_group():
masked_depth.inputs[2],
)
links.new(clamp_foreground.outputs[0], masked_depth.inputs[3])
selected_depth = nodes.new("ShaderNodeMix")
selected_depth.data_type = "FLOAT"
selected_depth.location = (1000.0, 650.0)
remove_background_value = background_input.outputs["Remove Background"]
links.new(remove_background_value, selected_depth.inputs[0])
links.new(depth_texture.outputs["Color"], selected_depth.inputs[2])
links.new(masked_depth.outputs["Result"], selected_depth.inputs[3])
displacement_menu = nodes.new("GeometryNodeMenuSwitch")
displacement_menu.data_type = "FLOAT"
displacement_menu.enum_items.clear()
@@ -995,7 +952,7 @@ def build_depth_plane_group():
displacement_scale = nodes.new("ShaderNodeMath")
displacement_scale.operation = "MULTIPLY"
displacement_scale.location = (1200.0, 650.0)
links.new(selected_depth.outputs["Result"], displacement_scale.inputs[0])
links.new(masked_depth.outputs["Result"], displacement_scale.inputs[0])
geometry_strength = nodes.new("ShaderNodeMath")
geometry_strength.operation = "MULTIPLY"
geometry_strength.location = (1150.0, 450.0)
@@ -1024,11 +981,6 @@ def build_depth_plane_group():
shade_smooth.location = (1850.0, 650.0)
links.new(set_material.outputs["Geometry"], shade_smooth.inputs["Geometry"])
attribute_values = (
(
REMOVE_BACKGROUND_ATTRIBUTE,
"BOOLEAN",
remove_background_value,
),
(
MATERIAL_DISPLACEMENT_ATTRIBUTE,
"BOOLEAN",