feat: Implement weight cutout boundary smoothing for image depth cutout
- Added a new proposal for boundary smoothing in the weight cutout process, differentiating between Split edges and Outline smoothing requirements. - Introduced a new capability `cutout-boundary-smoothing` to control smoothing strength and default values. - Updated specifications to define the behavior of boundary-specific smoothing strength and default settings. - Created tasks to verify the implementation of boundary behavior and asset updates. - Developed comprehensive tests for depth symmetry and normal scaling to ensure expected behavior and performance. - Introduced new attributes for normal mapping to facilitate persistent face attributes sharing between depth symmetry and image materials. - Built a new node group for O Image Depth Symmetry, integrating the new smoothing capabilities and ensuring compatibility with existing workflows.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""Materialize the joint calculation and connection plan in geometry node trees."""
|
||||
"""Materialize the joint calculation and connection plan in node trees."""
|
||||
|
||||
from .branches import calculation_blocks
|
||||
from .dimensions import estimate_node_dimensions, visible_socket
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Identify connected field calculations by their nearest geometry consumers."""
|
||||
"""Identify connected calculations by their nearest geometry or shader consumers."""
|
||||
|
||||
from collections import defaultdict
|
||||
from graphlib import TopologicalSorter
|
||||
@@ -17,7 +17,8 @@ def calculation_blocks(nodes, edges):
|
||||
i
|
||||
for i, n in enumerate(nodes)
|
||||
if n.bl_idname == "NodeGroupOutput"
|
||||
or any(s.type == "GEOMETRY" and s.is_linked for s in n.outputs)
|
||||
or n.bl_idname in {"ShaderNodeNormalMap", "ShaderNodeBump", "ShaderNodeDisplacement"}
|
||||
or any(s.type in {"GEOMETRY", "SHADER"} and s.is_linked for s in n.outputs)
|
||||
or getattr(n, "paired_output", None) is not None
|
||||
}
|
||||
inputs = {i for i, n in enumerate(nodes) if n.bl_idname == "NodeGroupInput"}
|
||||
|
||||
@@ -74,6 +74,19 @@
|
||||
"ShaderNodeMix": 3,
|
||||
"ShaderNodeSeparateXYZ": 0,
|
||||
"ShaderNodeValue": 1,
|
||||
"ShaderNodeVectorMath": 1
|
||||
"ShaderNodeVectorMath": 1,
|
||||
"FunctionNodeAlignEulerToVector": 2,
|
||||
"ShaderNodeVectorRotate": 2,
|
||||
"ShaderNodeAttribute": 2,
|
||||
"ShaderNodeFloatCurve": 12,
|
||||
"ShaderNodeBump": 2,
|
||||
"ShaderNodeNormalMap": 2,
|
||||
"ShaderNodeDisplacement": 1,
|
||||
"ShaderNodeBsdfDiffuse": 1,
|
||||
"ShaderNodeBsdfTransparent": 0,
|
||||
"ShaderNodeEmission": 0,
|
||||
"ShaderNodeLightPath": 0,
|
||||
"ShaderNodeMixShader": 0,
|
||||
"ShaderNodeGroup": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ def estimate_node_dimensions(node):
|
||||
inputs = [s for s in node.inputs if visible_socket(s)]
|
||||
outputs = [s for s in node.outputs if visible_socket(s)]
|
||||
width = RULES["wide_width"] if (
|
||||
"Attribute" in node.bl_idname or node.bl_idname == "GeometryNodeGroup"
|
||||
"Attribute" in node.bl_idname or node.bl_idname in {"GeometryNodeGroup", "ShaderNodeGroup", "ShaderNodeFloatCurve"}
|
||||
) else RULES["width"]
|
||||
if node.hide:
|
||||
height = max(RULES["collapsed_height"], 16 + 12 * max(len(inputs), len(outputs)))
|
||||
@@ -44,7 +44,7 @@ def estimate_node_dimensions(node):
|
||||
controls = RULES["controls"][node.bl_idname]
|
||||
for collection in ("repeat_items", "state_items", "input_items", "main_items", "generation_items", "enum_items"):
|
||||
controls += len(getattr(node, collection, ()))
|
||||
if node.bl_idname == "GeometryNodeGroup" and node.node_tree:
|
||||
if node.bl_idname in {"GeometryNodeGroup", "ShaderNodeGroup"} and node.node_tree:
|
||||
controls += sum(item.item_type == "PANEL" for item in node.node_tree.interface.items_tree)
|
||||
cursor = RULES["header"] + RULES["padding"]
|
||||
sockets = {}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from .branches import calculation_blocks
|
||||
|
||||
|
||||
SHARED_GEOMETRY_USES = 3
|
||||
|
||||
@@ -84,5 +86,9 @@ def geometry_paths(node_group):
|
||||
|
||||
def node_roles(node_group):
|
||||
"""Classify by contribution to the result, not by available socket types."""
|
||||
if node_group.bl_idname == "ShaderNodeTree":
|
||||
nodes = list(node_group.nodes)
|
||||
_, core = calculation_blocks(nodes, list(node_group.links))
|
||||
return {node: "shader" if i in core else "field" for i, node in enumerate(nodes)}
|
||||
core, _ = geometry_paths(node_group)
|
||||
return {node: "geometry" if node in core else "field" for node in node_group.nodes}
|
||||
|
||||
@@ -90,7 +90,7 @@ def plan_layout(nodes, edges, size, blocks, core):
|
||||
continue
|
||||
sy = items[u]["outputs"][e][1] if a in members else 16.0
|
||||
ty = items[v]["inputs"][e][1] if b in members else 16.0
|
||||
weight = 3.0 if edge_types[e] == "GEOMETRY" else 1.0
|
||||
weight = 3.0 if edge_types[e] in {"GEOMETRY", "SHADER"} else 1.0
|
||||
links.append((e, u, v, sy, ty, weight))
|
||||
parents = {k: set() for k in items}
|
||||
children = {k: set() for k in items}
|
||||
@@ -360,7 +360,7 @@ def plan_layout(nodes, edges, size, blocks, core):
|
||||
u, v = node_atom[a], node_atom[b]
|
||||
sy = seed[a][1] + size[a].sockets[edges[e].from_socket] - atoms[u]["preferred"]
|
||||
ty = seed[b][1] + size[b].sockets[edges[e].to_socket] - atoms[v]["preferred"]
|
||||
weight = 3.0 if edge_types[e] == "GEOMETRY" else 1.0
|
||||
weight = 3.0 if edge_types[e] in {"GEOMETRY", "SHADER"} else 1.0
|
||||
if e in edge_bus:
|
||||
weight *= 0.15
|
||||
sequence = [(u, sy), (v, ty)] if e not in edge_bus else [(u, sy), (edge_bus[e], 12.0), (v, ty)]
|
||||
|
||||
Reference in New Issue
Block a user