- Implemented the "O Image Relief Plane" Geometry Nodes group to create a fixed-base image relief using depth images and various parameters. - Created the "O Shadeless" shader node group to display color without scene lighting while preserving shadows and reflections. - Introduced a preview feature for node groups, allowing for interactive visualization of node arrangements in an HTML format. - Added a template HTML file for rendering node previews with customizable views and controls.
121 lines
3.6 KiB
Python
121 lines
3.6 KiB
Python
"""Behavior checks for face separation and connected Depth Surface shells."""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from collections import Counter
|
|
import bpy
|
|
import numpy as np
|
|
|
|
|
|
ROOT = Path(__file__).parents[1]
|
|
|
|
sys.path.insert(0, str(ROOT / "src"))
|
|
|
|
|
|
from node_asset_test_support import CUTOUT_BUILDERS
|
|
|
|
|
|
def surface(*, step=6.0, size=1.0, resolution=2, wrapper=False):
|
|
bpy.ops.wm.read_factory_settings(use_empty=True)
|
|
group = CUTOUT_BUILDERS["DEPTH_SOLID"]()
|
|
# Two columns with a complete depth jump along their common boundary.
|
|
points = [
|
|
(x * size / resolution, y * size / resolution, 0)
|
|
for y in range(resolution + 1)
|
|
for x in range(2 * resolution + 1)
|
|
]
|
|
stride = 2 * resolution + 1
|
|
faces = [
|
|
(
|
|
y * stride + x,
|
|
y * stride + x + 1,
|
|
(y + 1) * stride + x + 1,
|
|
(y + 1) * stride + x,
|
|
)
|
|
for y in range(resolution)
|
|
for x in range(2 * resolution)
|
|
]
|
|
mesh = bpy.data.meshes.new("Step")
|
|
mesh.from_pydata(points, [], faces)
|
|
uv = mesh.uv_layers.new(name="UVMap")
|
|
for loop in mesh.loops:
|
|
x, y, _ = points[loop.vertex_index]
|
|
uv.data[loop.index].uv = (x / (2 * size), y / size)
|
|
obj = bpy.data.objects.new("Step", mesh)
|
|
bpy.context.collection.objects.link(obj)
|
|
image = bpy.data.images.new("Camera", width=64, height=32, float_buffer=True)
|
|
image.colorspace_settings.name = "Non-Color"
|
|
image.alpha_mode = "CHANNEL_PACKED"
|
|
yy, xx = np.mgrid[:32, :64]
|
|
pixels = np.ones((32, 64, 4), np.float32)
|
|
pixels[..., 0] = (xx + 0.5) / 32
|
|
pixels[..., 1] = -(yy + 0.5) / 32
|
|
pixels[..., 2] = 1 + (xx >= 32) * step
|
|
image.pixels.foreach_set(pixels.ravel())
|
|
modifier = obj.modifiers.new("Depth Surface", "NODES")
|
|
modifier.node_group = group
|
|
inputs = {
|
|
s.name: s.identifier
|
|
for s in group.interface.items_tree
|
|
if s.item_type == "SOCKET" and s.in_out == "INPUT"
|
|
}
|
|
|
|
def set_value(name, value):
|
|
modifier[inputs[name]] = value
|
|
obj.update_tag(refresh={"DATA"})
|
|
bpy.context.view_layer.update()
|
|
|
|
for name, value in (
|
|
("Depth Image", image),
|
|
("Uniform Scale", size),
|
|
("Reference Depth", size),
|
|
("Depth Scale", 1.0),
|
|
("Thickness", 0.0),
|
|
("Front Inflation", 0.0),
|
|
("Split Threshold", 0.0),
|
|
("Mode", 1),
|
|
("Smooth", 0),
|
|
):
|
|
set_value(name, value)
|
|
if wrapper:
|
|
set_value("Cleanup Threshold", 0.0)
|
|
set_value("Inward Axis", 0)
|
|
set_value("Smooth", 0)
|
|
return obj, set_value
|
|
|
|
|
|
def evaluated(obj):
|
|
result = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
|
mesh = result.to_mesh()
|
|
try:
|
|
return np.array([v.co[:] for v in mesh.vertices]), [
|
|
tuple(p.vertices) for p in mesh.polygons
|
|
]
|
|
finally:
|
|
result.to_mesh_clear()
|
|
|
|
|
|
def assert_closed(faces):
|
|
edges = Counter(
|
|
tuple(sorted((a, b))) for f in faces for a, b in zip(f, (*f[1:], f[0]))
|
|
)
|
|
assert edges and set(edges.values()) == {2}, Counter(edges.values())
|
|
|
|
|
|
def component_count(faces):
|
|
adjacency = {}
|
|
for face in faces:
|
|
for a, b in zip(face, (*face[1:], face[0])):
|
|
adjacency.setdefault(a, set()).add(b)
|
|
adjacency.setdefault(b, set()).add(a)
|
|
remaining = set(adjacency)
|
|
count = 0
|
|
while remaining:
|
|
count += 1
|
|
pending = [remaining.pop()]
|
|
while pending:
|
|
for neighbor in adjacency[pending.pop()] & remaining:
|
|
remaining.remove(neighbor)
|
|
pending.append(neighbor)
|
|
return count
|