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:
@@ -8,6 +8,7 @@ import numpy as np
|
||||
import pytest
|
||||
|
||||
from tests.support.depth_surface import surface, evaluated, assert_closed, component_count
|
||||
from tools.nodes.common.boundary_smoothing import cut_boundary_influence
|
||||
|
||||
|
||||
def diagonal_surface(triangles=False):
|
||||
@@ -51,6 +52,66 @@ def edge_band(faces, seeds, rings=2):
|
||||
return selected
|
||||
|
||||
|
||||
def test_cutout_split_and_outline_use_distinct_influence():
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
points, faces, split = [], [], []
|
||||
for side in range(2):
|
||||
offset = len(points)
|
||||
for y in range(9):
|
||||
for x in range(5):
|
||||
points.append((side * 4 + x, y, 0))
|
||||
split.append(x == (4 if side == 0 else 0))
|
||||
faces.extend(
|
||||
(
|
||||
offset + y * 5 + x,
|
||||
offset + y * 5 + x + 1,
|
||||
offset + (y + 1) * 5 + x + 1,
|
||||
offset + (y + 1) * 5 + x,
|
||||
)
|
||||
for y in range(8)
|
||||
for x in range(4)
|
||||
)
|
||||
mesh = bpy.data.meshes.new("Boundary influence")
|
||||
mesh.from_pydata(points, [], faces)
|
||||
mesh.attributes.new("test_split", "BOOLEAN", "POINT").data.foreach_set("value", split)
|
||||
obj = bpy.data.objects.new("Boundary influence", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
group = bpy.data.node_groups.new("Boundary influence", "GeometryNodeTree")
|
||||
for direction in ("INPUT", "OUTPUT"):
|
||||
group.interface.new_socket(name="Geometry", in_out=direction, socket_type="NodeSocketGeometry")
|
||||
source = group.nodes.new("NodeGroupInput")
|
||||
split_attribute = group.nodes.new("GeometryNodeInputNamedAttribute")
|
||||
split_attribute.data_type = "BOOLEAN"
|
||||
split_attribute.inputs["Name"].default_value = "test_split"
|
||||
store = group.nodes.new("GeometryNodeStoreNamedAttribute")
|
||||
store.data_type, store.domain = "FLOAT", "POINT"
|
||||
store.inputs["Name"].default_value = "test_influence"
|
||||
group.links.new(source.outputs["Geometry"], store.inputs["Geometry"])
|
||||
group.links.new(cut_boundary_influence(group, split_attribute.outputs["Attribute"]), store.inputs["Value"])
|
||||
output = group.nodes.new("NodeGroupOutput")
|
||||
group.links.new(store.outputs["Geometry"], output.inputs["Geometry"])
|
||||
obj.modifiers.new("Boundary influence", "NODES").node_group = group
|
||||
evaluated_obj = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
||||
result = evaluated_obj.to_mesh()
|
||||
try:
|
||||
influence = np.array([value.value for value in result.attributes["test_influence"].data])
|
||||
finally:
|
||||
evaluated_obj.to_mesh_clear()
|
||||
np.testing.assert_allclose(influence[4 * 5], 0.1, atol=1e-6)
|
||||
np.testing.assert_allclose(influence[4 * 5 + 4], 1.0, atol=1e-6)
|
||||
np.testing.assert_allclose(influence[4], 1.0, atol=1e-6)
|
||||
|
||||
|
||||
def test_cutout_boundary_smooth_defaults_to_four():
|
||||
obj, _ = surface()
|
||||
socket = next(
|
||||
item for item in obj.modifiers[0].node_group.interface.items_tree
|
||||
if item.item_type == "SOCKET" and item.in_out == "INPUT" and item.name == "Boundary Smooth"
|
||||
)
|
||||
assert socket.default_value == 4
|
||||
assert (socket.min_value, socket.max_value) == (0, 16)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("triangles", [False, True])
|
||||
def test_smoothing_reduces_stairs_without_leaving_two_ring_band(triangles):
|
||||
obj, set_value = diagonal_surface(triangles)
|
||||
|
||||
Reference in New Issue
Block a user