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:
@@ -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",
|
||||
|
||||
@@ -14,6 +14,7 @@ from snapform.image_depth import (
|
||||
load_depth_plane_node_group,
|
||||
modifier_input_identifier,
|
||||
)
|
||||
from snapform.image_mesh import load_geometry_node_group
|
||||
|
||||
|
||||
def main():
|
||||
@@ -27,6 +28,15 @@ def main():
|
||||
and item.name == "Background Depth"
|
||||
)
|
||||
assert background_depth.subtype == "DISTANCE"
|
||||
assert background_depth.structure_type == "SINGLE"
|
||||
frame_input = next(
|
||||
item
|
||||
for item in group.interface.items_tree
|
||||
if item.item_type == "SOCKET"
|
||||
and item.in_out == "INPUT"
|
||||
and item.name == "Frame"
|
||||
)
|
||||
assert frame_input.structure_type == "SINGLE"
|
||||
geometry_input = next(
|
||||
item
|
||||
for item in group.interface.items_tree
|
||||
@@ -45,22 +55,15 @@ def main():
|
||||
assert visible_inputs[:6] == [
|
||||
"Geometry",
|
||||
"Displacement",
|
||||
"Depth Scale",
|
||||
"Subdivision",
|
||||
"Remove Background",
|
||||
"Depth Scale",
|
||||
"Background Depth",
|
||||
]
|
||||
remove_background = next(
|
||||
item
|
||||
assert not getattr(background_depth.parent, "name", "")
|
||||
assert not any(
|
||||
item.name == "Remove Background"
|
||||
for item in group.interface.items_tree
|
||||
if item.item_type == "SOCKET"
|
||||
and item.in_out == "INPUT"
|
||||
and item.name == "Remove Background"
|
||||
)
|
||||
assert remove_background.socket_type == "NodeSocketBool"
|
||||
assert remove_background.is_panel_toggle
|
||||
assert remove_background.parent.name == "Remove Background"
|
||||
assert background_depth.parent == remove_background.parent
|
||||
depth_scale = next(
|
||||
item
|
||||
for item in group.interface.items_tree
|
||||
@@ -77,7 +80,6 @@ def main():
|
||||
if node.bl_idname == "GeometryNodeStoreNamedAttribute"
|
||||
}
|
||||
attribute_names = (
|
||||
"o_remove_background",
|
||||
"o_material_displacement",
|
||||
"o_background_depth",
|
||||
"o_depth_scale",
|
||||
@@ -160,14 +162,31 @@ def main():
|
||||
and node.node_tree == material_group
|
||||
]
|
||||
assert len(material_group_nodes) == 1
|
||||
assert material_group_nodes[0].name == "O DA3 Depth Plane"
|
||||
assert material_group_nodes[0].name == "O DA3 Depth Plane Layer"
|
||||
bump = next(
|
||||
node
|
||||
for node in material_group.nodes
|
||||
if node.bl_idname == "ShaderNodeBump"
|
||||
)
|
||||
bump_height_link = next(
|
||||
link
|
||||
for link in material_group.links
|
||||
if link.to_node == bump and link.to_socket == bump.inputs["Height"]
|
||||
)
|
||||
assert bump_height_link.from_node.bl_idname == "NodeGroupInput"
|
||||
assert bump_height_link.from_socket.name == "Color"
|
||||
attributes = [
|
||||
node
|
||||
for node in material_group.nodes
|
||||
if node.bl_idname == "ShaderNodeAttribute"
|
||||
]
|
||||
assert len(attributes) == 4
|
||||
assert len(attributes) == 3
|
||||
assert all(node.attribute_type == "GEOMETRY" for node in attributes)
|
||||
assert {node.attribute_name for node in attributes} == {
|
||||
"o_material_displacement",
|
||||
"o_background_depth",
|
||||
"o_depth_scale",
|
||||
}
|
||||
assert not any(
|
||||
node.bl_idname == "ShaderNodeAttribute"
|
||||
for node in material.node_tree.nodes
|
||||
@@ -193,7 +212,25 @@ def main():
|
||||
and node.node_tree == surface_group
|
||||
]
|
||||
assert len(surface_group_nodes) == 1
|
||||
assert surface_group_nodes[0].name == "O DA3 Surface"
|
||||
assert surface_group_nodes[0].name == "O DA3 Surface Layer"
|
||||
surface_outputs = [
|
||||
item.name
|
||||
for item in surface_group.interface.items_tree
|
||||
if item.item_type == "SOCKET"
|
||||
and item.in_out == "OUTPUT"
|
||||
]
|
||||
assert surface_outputs == ["Color", "Roughness", "Alpha", "Normal"]
|
||||
assert not any(
|
||||
node.bl_idname == "ShaderNodeDisplacement"
|
||||
for node in surface_group.nodes
|
||||
)
|
||||
refine_group = load_geometry_node_group()
|
||||
assert not any(
|
||||
item.item_type == "SOCKET"
|
||||
and item.in_out == "INPUT"
|
||||
and item.name == "Background"
|
||||
for item in refine_group.interface.items_tree
|
||||
)
|
||||
surface_depth_input = next(
|
||||
item
|
||||
for item in surface_group.interface.items_tree
|
||||
@@ -206,7 +243,13 @@ def main():
|
||||
node.bl_idname in forbidden_outer_nodes
|
||||
for node in surface_material.node_tree.nodes
|
||||
)
|
||||
modifier = add_depth_plane_processing(obj, depth, foreground, material)
|
||||
modifier = add_depth_plane_processing(
|
||||
obj,
|
||||
depth,
|
||||
foreground,
|
||||
material,
|
||||
animate_frame=True,
|
||||
)
|
||||
modifier[modifier_input_identifier(group, "Depth Scale")] = -2.0
|
||||
for name, expected in (
|
||||
("Frame Start", 1),
|
||||
@@ -214,6 +257,20 @@ def main():
|
||||
("Cyclic", False),
|
||||
):
|
||||
assert modifier[modifier_input_identifier(group, name)] == expected
|
||||
assert not any(
|
||||
node.bl_idname == "GeometryNodeInputSceneTime"
|
||||
for node in group.nodes
|
||||
)
|
||||
frame_identifier = modifier_input_identifier(group, "Frame")
|
||||
frame_driver = next(
|
||||
fcurve
|
||||
for fcurve in obj.animation_data.drivers
|
||||
if fcurve.data_path == (
|
||||
f'modifiers["{modifier.name}"]["{frame_identifier}"]'
|
||||
)
|
||||
)
|
||||
assert frame_driver.driver.type == "SCRIPTED"
|
||||
assert frame_driver.driver.expression == "frame"
|
||||
image_textures = [
|
||||
node
|
||||
for node in group.nodes
|
||||
|
||||
@@ -51,7 +51,7 @@ def main():
|
||||
group_inputs = [
|
||||
node for node in group.nodes if node.bl_idname == "NodeGroupInput"
|
||||
]
|
||||
assert len(group_inputs) >= 8
|
||||
assert len(group_inputs) >= 7
|
||||
for node in group_inputs:
|
||||
visible_outputs = [
|
||||
output for output in node.outputs if output.name and not output.hide
|
||||
@@ -75,7 +75,6 @@ def main():
|
||||
assert [item.name for item in panels["Culling"].interface_items] == [
|
||||
"Confidence",
|
||||
"Stretch",
|
||||
"Background",
|
||||
]
|
||||
assert [item.name for item in panels["Options"].interface_items] == [
|
||||
"Fix Topology",
|
||||
@@ -116,7 +115,7 @@ def main():
|
||||
evaluated = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
||||
result = evaluated.to_mesh()
|
||||
try:
|
||||
assert len(result.polygons) == 1
|
||||
assert len(result.polygons) == 2
|
||||
assert "UVMap" in result.attributes
|
||||
assert result.attributes["UVMap"].data_type == "FLOAT2"
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user