feat(node_assets): add image plane geometry nodes and validation scripts

- Implemented `image_plane.py` to build the Image Plane Geometry Nodes group with customizable parameters for geometry, subdivide, and thickness.
- Created `image_plane_validation.py` to validate the functionality of the image plane, ensuring correct geometry generation and material assignment.
- Developed `material_layers.py` to construct shader node groups for image materials, including depth and normal handling.
- Authored `plane_layouts.py` to define node arrangements for image geometry assets.
- Introduced `shadeless.py` to create a shadeless shader node group that displays color without scene lighting while preserving shadows and reflections.
This commit is contained in:
2026-09-08 23:24:27 +08:00
parent 196b964efc
commit 4c902ed7db
155 changed files with 9700 additions and 10393 deletions

View File

@@ -12,14 +12,14 @@ sys.path.insert(
str(next((PROJECT_ROOT / "src" / "anyimage" / "wheels").glob("blendjob-*.whl"))),
)
from build_cutout_modifiers import (
from cutout import (
CUTOUT_NODE_GROUP_NAMES,
build_cutout_group,
)
from build_depth_plane import build_depth_plane_group
from build_material_layers import build_material_layer_group
from build_mesh_plane import build_mesh_plane_group
from build_shadeless import build_shadeless_group
from depth_plane import build_depth_plane_group
from material_layers import build_material_layer_group
from image_plane import build_image_plane_group
from shadeless import build_shadeless_group
OUTPUT_PATH = (
PROJECT_ROOT
/ "src"
@@ -32,8 +32,8 @@ OUTPUT_PATH = (
def main():
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.context.preferences.filepaths.save_version = 0
mesh_plane = build_mesh_plane_group()
build_depth_plane_group(mesh_plane)
image_plane = build_image_plane_group()
build_depth_plane_group(image_plane)
build_material_layer_group()
build_material_layer_group(depth_plane=True)
build_shadeless_group()

View File

@@ -35,7 +35,7 @@ def input_names(group):
def main():
bpy.ops.wm.read_factory_settings(use_empty=True)
mesh_plane = load_node_group("O Mesh Plane")
image_plane = load_node_group("O Image Plane")
depth_plane = load_node_group(DEPTH_PLANE_NODE_GROUP_NAME)
image_layer = load_node_group(IMAGE_MATERIAL_NODE_GROUP_NAME)
depth_layer = load_node_group(DEPTH_MATERIAL_NODE_GROUP_NAME)
@@ -44,7 +44,7 @@ def main():
name: load_node_group(name) for name in set(CUTOUT_NODE_GROUP_NAMES.values())
}
expected_names = {
"O Mesh Plane",
"O Image Plane",
DEPTH_PLANE_NODE_GROUP_NAME,
IMAGE_MATERIAL_NODE_GROUP_NAME,
DEPTH_MATERIAL_NODE_GROUP_NAME,
@@ -54,10 +54,10 @@ def main():
with bpy.data.libraries.load(str(node_asset_path()), link=False) as (source, _):
assert set(source.node_groups) == expected_names
assert mesh_plane.name == "O Mesh Plane"
assert mesh_plane.bl_idname == "GeometryNodeTree"
assert mesh_plane.is_modifier
assert input_names(mesh_plane) == [
assert image_plane.name == "O Image Plane"
assert image_plane.bl_idname == "GeometryNodeTree"
assert image_plane.is_modifier
assert input_names(image_plane) == [
"Geometry",
"Subdivide",
"Thickness",
@@ -66,19 +66,28 @@ def main():
assert depth_plane.is_modifier
assert input_names(depth_plane) == [
"Geometry",
"Mode",
"Subdivide",
"Thickness",
"Thickness",
"Depth Scale",
"Reference Depth",
"Normal Smooth",
"Uniform Scale",
"Depth Image",
]
thickness_inputs = [
item for item in depth_plane.interface.items_tree
if item.item_type == "SOCKET" and item.in_out == "INPUT" and item.name == "Thickness"
]
assert len({item.identifier for item in thickness_inputs}) == 2
assert all(item.default_value == 0 and item.subtype == "DISTANCE" for item in thickness_inputs)
depth_panels = [
item for item in depth_plane.interface.items_tree if item.item_type == "PANEL"
]
assert [panel.name for panel in depth_panels] == ["Options", "Data"]
assert [item.name for item in depth_panels[0].interface_items] == [
"Reference Depth"
"Reference Depth", "Normal Smooth"
]
assert [item.name for item in depth_panels[1].interface_items] == [
"Uniform Scale",
@@ -119,7 +128,7 @@ def main():
assert input_names(group)[0] == "Geometry"
public_groups = {
mesh_plane,
image_plane,
depth_plane,
image_layer,
depth_layer,

View File

@@ -10,7 +10,7 @@ from cutout_shapes import (
_build_depth_surface, _shade_output,
)
from cutout_shape_layouts import LAYOUTS
from cutout_depth_surface_layout import POSITIONS, INPUT_REGIONS, layout_geometry_references
from depth_cutout_layout import POSITIONS, INPUT_REGIONS, layout_geometry_references
from cutout_layout import layout_stage, finish_layout
from cutout_volume import build_volume_fields, shift_stage
from cutout_rounding import build_volume_rounding

View File

@@ -1,5 +1,5 @@
"""Construct the planar and depth-based Cutout shape stages."""
from cutout_depth_surface import store_surface_camera, remove_surface_attributes, split_depth_surface, thicken_depth_surface
from depth_surface import project_depth_surface, store_surface_camera, remove_surface_attributes, split_depth_surface, thicken_depth_surface
from anyimage.operators.cutout_tool.shape import BALLOON_ATTRIBUTE_NAME
@@ -561,53 +561,11 @@ def _build_depth_surface(
geometry, camera_position = store_surface_camera(
group, geometry, original_camera, split
)
scaled = nodes.new("ShaderNodeVectorMath")
scaled.operation = "SCALE"
scaled.location = (3100.0, -100.0)
links.new(camera_position, scaled.inputs[0])
links.new(uniform_scale, scaled.inputs[3])
separate = nodes.new("ShaderNodeSeparateXYZ")
separate.location = (3300.0, -100.0)
links.new(scaled.outputs["Vector"], separate.inputs["Vector"])
relative_depth = _math(nodes, "SUBTRACT", (3500.0, -100.0))
links.new(reference_depth, relative_depth.inputs[0])
links.new(separate.outputs["Z"], relative_depth.inputs[1])
front = _math(nodes, "MULTIPLY", (3700.0, -100.0))
links.new(relative_depth.outputs[0], front.inputs[0])
links.new(depth_scale, front.inputs[1])
negative_y = _math(nodes, "MULTIPLY", (3500.0, -600.0))
negative_y.inputs[1].default_value = -1.0
links.new(separate.outputs["Y"], negative_y.inputs[0])
projected = nodes.new("ShaderNodeCombineXYZ")
projected.location = (3700.0, -450.0)
links.new(separate.outputs["X"], projected.inputs["X"])
links.new(negative_y.outputs[0], projected.inputs["Y"])
current_position = nodes.new("GeometryNodeInputPosition")
current_position.location = (3500.0, -850.0)
blend = nodes.new("ShaderNodeMix")
blend.data_type = "VECTOR"
blend.clamp_factor = False
blend.location = (3900.0, -450.0)
links.new(depth_scale, blend.inputs[0])
links.new(current_position.outputs["Position"], blend.inputs[4])
links.new(projected.outputs["Vector"], blend.inputs[5])
blended_axes = nodes.new("ShaderNodeSeparateXYZ")
blended_axes.location = (4100.0, -500.0)
links.new(blend.outputs["Result"], blended_axes.inputs["Vector"])
surface_position = nodes.new("ShaderNodeCombineXYZ")
surface_position.location = (4100.0, -200.0)
links.new(blended_axes.outputs["X"], surface_position.inputs["X"])
links.new(blended_axes.outputs["Y"], surface_position.inputs["Y"])
links.new(front.outputs[0], surface_position.inputs["Z"])
project_geometry = nodes.new("GeometryNodeSetPosition")
project_geometry.location = (4400.0, 400.0)
links.new(geometry, project_geometry.inputs["Geometry"])
links.new(surface_position.outputs["Vector"], project_geometry.inputs["Position"])
geometry = project_geometry.outputs["Geometry"]
geometry = project_depth_surface(
group, geometry, camera_position, uniform_scale, reference_depth, depth_scale
)
shaped = thicken_depth_surface(group, geometry, thickness)
geometry = _remove_zero_thickness(
group, project_geometry.outputs["Geometry"], shaped, thickness, (11800.0, 400.0)
group, geometry, shaped, thickness, (11800.0, 400.0)
)
return remove_surface_attributes(group, geometry)

View File

@@ -2,7 +2,10 @@
import bpy
from geometry_layouts import layout_geometry
from plane_layouts import layout_geometry
from cutout_layout import finish_layout
from depth_plane_camera import build_camera_plane
from depth_plane_layout import layout_camera_plane
DEPTH_PLANE_NODE_GROUP_NAME = "O Image Depth Plane"
@@ -36,7 +39,7 @@ def _math(nodes, operation, location):
return node
def build_depth_plane_group(mesh_plane_group):
def build_depth_plane_group(image_plane_group):
group = bpy.data.node_groups.get(DEPTH_PLANE_NODE_GROUP_NAME)
if group is not None:
return group
@@ -47,14 +50,21 @@ def build_depth_plane_group(mesh_plane_group):
group.is_modifier = True
geometry = _interface_socket(group, "Geometry", "INPUT", "NodeSocketGeometry")
geometry.structure_type = "SINGLE"
mode = _interface_socket(group, "Mode", "INPUT", "NodeSocketMenu")
subdivide = _interface_socket(group, "Subdivide", "INPUT", "NodeSocketInt")
subdivide.default_value = 6
subdivide.min_value = 0
subdivide.max_value = 10
thickness = _interface_socket(group, "Thickness", "INPUT", "NodeSocketFloat")
thickness.default_value = 0.1
thickness.default_value = 0.0
thickness.min_value = 0.0
thickness.subtype = "DISTANCE"
thickness.description = "Base thickness used only by Relief mode"
camera_thickness = _interface_socket(group, "Camera Thickness", "INPUT", "NodeSocketFloat")
camera_thickness.default_value = 0.0
camera_thickness.min_value = 0.0
camera_thickness.subtype = "DISTANCE"
camera_thickness.description = "Inward shell distance used only by Camera mode"
depth_scale = _interface_socket(group, "Depth Scale", "INPUT", "NodeSocketFloat")
depth_scale.default_value = 1.0
depth_scale.min_value = 0.0
@@ -69,6 +79,11 @@ def build_depth_plane_group(mesh_plane_group):
base_plane_depth.default_value = 1.0
base_plane_depth.min_value = 0.0
base_plane_depth.subtype = "DISTANCE"
normal_smooth = _interface_socket(
group, "Normal Smooth", "INPUT", "NodeSocketInt", parent=options,
)
normal_smooth.default_value, normal_smooth.min_value, normal_smooth.max_value = 50, 0, 50
normal_smooth.description = "Smooth the Camera shell thickness direction"
data = group.interface.new_panel(name="Data", default_closed=True)
uniform_scale = _interface_socket(
group,
@@ -109,12 +124,12 @@ def build_depth_plane_group(mesh_plane_group):
)
group_output = nodes.new("NodeGroupOutput")
group_output.location = (1550.0, 500.0)
mesh_plane = nodes.new("GeometryNodeGroup")
mesh_plane.node_tree = mesh_plane_group
mesh_plane.location = (-1600.0, 500.0)
links.new(geometry_input.outputs["Geometry"], mesh_plane.inputs["Geometry"])
links.new(geometry_input.outputs["Subdivide"], mesh_plane.inputs["Subdivide"])
links.new(shape_input.outputs["Thickness"], mesh_plane.inputs["Thickness"])
image_plane = nodes.new("GeometryNodeGroup")
image_plane.node_tree = image_plane_group
image_plane.location = (-1600.0, 500.0)
links.new(geometry_input.outputs["Geometry"], image_plane.inputs["Geometry"])
links.new(geometry_input.outputs["Subdivide"], image_plane.inputs["Subdivide"])
links.new(shape_input.outputs["Thickness"], image_plane.inputs["Thickness"])
positive_half = _math(nodes, "MULTIPLY", (-1300.0, 300.0))
positive_half.inputs[1].default_value = 0.5
@@ -124,7 +139,7 @@ def build_depth_plane_group(mesh_plane_group):
links.new(positive_half.outputs[0], translation.inputs["Z"])
place_block = nodes.new("GeometryNodeTransform")
place_block.location = (-900.0, 500.0)
links.new(mesh_plane.outputs["Geometry"], place_block.inputs["Geometry"])
links.new(image_plane.outputs["Geometry"], place_block.inputs["Geometry"])
links.new(translation.outputs["Vector"], place_block.inputs["Translation"])
uv = nodes.new("GeometryNodeInputNamedAttribute")
@@ -141,7 +156,7 @@ def build_depth_plane_group(mesh_plane_group):
depth_components.location = (-450.0, -350.0)
links.new(texture.outputs["Color"], depth_components.inputs["Vector"])
metric_depth = _math(nodes, "MULTIPLY", (-200.0, -350.0))
links.new(depth_components.outputs["X"], metric_depth.inputs[0])
links.new(depth_components.outputs["Z"], metric_depth.inputs[0])
links.new(image_input.outputs["Uniform Scale"], metric_depth.inputs[1])
relative_depth = _math(nodes, "SUBTRACT", (50.0, -350.0))
links.new(shape_input.outputs["Reference Depth"], relative_depth.inputs[0])
@@ -205,8 +220,35 @@ def build_depth_plane_group(mesh_plane_group):
links.new(shade_smooth.outputs["Geometry"], group_output.inputs["Geometry"])
group.color_tag = "GEOMETRY"
group.description = (
"Build a single-sided depth terrain block with a fixed base."
"Build a fixed-base relief or camera-projected image shell."
)
group.use_fake_user = True
layout_geometry(group)
for node in nodes:
node.location.x += 3750
node.location.y += 2400
previous = set(nodes)
camera = build_camera_plane(group, image_plane_group)
layout_camera_plane(group, previous)
nodes.remove(shade_smooth)
# Align the Relief and Camera branch ends in menu order.
set_position.location.x = camera.node.location.x + 40
selector = nodes.new("GeometryNodeMenuSwitch")
selector.data_type = "GEOMETRY"
for item, name in zip(selector.enum_items, ("Relief", "Camera")):
item.name = name
selector.location = (6500, 1300)
controls = _group_input(nodes, (6250, 1550), {"Mode"})
links.new(controls.outputs["Mode"], selector.inputs["Menu"])
links.new(set_position.outputs["Geometry"], selector.inputs["Relief"])
links.new(camera, selector.inputs["Camera"])
mode.default_value = "Relief"
smooth = nodes.new("GeometryNodeSetShadeSmooth")
smooth.domain = "FACE"
smooth.location = (6750, 1300)
links.new(selector.outputs[0], smooth.inputs["Geometry"])
group_output.location = (7000, 1300)
links.new(smooth.outputs["Geometry"], group_output.inputs["Geometry"])
finish_layout(group)
camera_thickness.name = "Thickness"
return group

View File

@@ -0,0 +1,44 @@
"""Build the camera-projected branch of the complete-image depth plane."""
from node_graph import group_input as _group_input
from depth_surface import project_depth_surface, thicken_depth_surface, remove_surface_attributes
def build_camera_plane(group, image_plane_group):
nodes, links = group.nodes, group.links
controls = _group_input(nodes, (-300, 0), {"Geometry", "Subdivide"})
plane = nodes.new("GeometryNodeGroup")
plane.node_tree = image_plane_group
plane.inputs["Thickness"].default_value = 0
links.new(controls.outputs["Geometry"], plane.inputs["Geometry"])
links.new(controls.outputs["Subdivide"], plane.inputs["Subdivide"])
texture_input = _group_input(nodes, (-300, 700), {"Depth Image", "Uniform Scale"})
uv = nodes.new("GeometryNodeInputNamedAttribute")
uv.data_type = "FLOAT_VECTOR"
uv.inputs["Name"].default_value = "UVMap"
texture = nodes.new("GeometryNodeImageTexture")
texture.interpolation, texture.extension = "Linear", "EXTEND"
links.new(uv.outputs["Attribute"], texture.inputs["Vector"])
links.new(texture_input.outputs["Depth Image"], texture.inputs["Image"])
depth_input = _group_input(nodes, (500, 1000), {"Reference Depth", "Depth Scale"})
surface = project_depth_surface(
group, plane.outputs["Geometry"], texture.outputs["Color"],
texture_input.outputs["Uniform Scale"], depth_input.outputs["Reference Depth"],
depth_input.outputs["Depth Scale"],
)
shell_input = _group_input(nodes, (2000, 500), {"Camera Thickness", "Normal Smooth"})
shell = thicken_depth_surface(
group, surface, shell_input.outputs["Camera Thickness"],
normal_smooth=shell_input.outputs["Normal Smooth"],
)
positive = nodes.new("ShaderNodeMath")
positive.operation = "GREATER_THAN"
links.new(shell_input.outputs["Camera Thickness"], positive.inputs[0])
positive.inputs[1].default_value = 0
choose = nodes.new("GeometryNodeSwitch")
choose.input_type = "GEOMETRY"
links.new(positive.outputs[0], choose.inputs["Switch"])
links.new(surface, choose.inputs["False"])
links.new(shell, choose.inputs["True"])
clean = remove_surface_attributes(group, choose.outputs["Output"])
return clean

View File

@@ -0,0 +1,82 @@
"""Place projection fields above the rectangular surface and shell stages."""
from cutout_layout import route_geometry, layout_stage
def layout_camera_plane(group, previous):
stage = layout_stage(group, previous, {
"Group": (0, 0),
"Named Attribute": (-240, 760),
"Image Texture": (0, 760),
"Vector Math": (220, 760),
"Separate XYZ": (440, 760),
"Math": (660, 980),
"Math.001": (1320, 980),
"Math.002": (660, 620),
"Combine XYZ": (880, 760),
"Position": (880, 420),
"Mix": (1100, 760),
"Separate XYZ.001": (1320, 760),
"Combine XYZ.001": (1540, 880),
"Set Position": (1760, 0),
"Position.001": (1760, 400),
"Store Named Attribute": (2020, 0),
"Vector Math.001": (2020, 400),
"Set Position.001": (2280, 0),
"Index": (2280, 400),
"Store Named Attribute.001": (2540, 0),
"Combine XYZ.002": (3970, 820),
"Integer Math": (3750, 1220),
"Named Attribute.001": (3500, 1220),
"Integer Math.001": (3750, 980),
"Named Attribute.002": (3500, 980),
"Extrude Mesh": (2820, 0),
"Combine XYZ.003": (2540, 440),
"Separate Geometry": (3090, 0),
"Combine XYZ.004": (2820, -350),
"Set Position.002": (3090, -350),
"Flip Faces": (3370, -350),
"Flip Faces.001": (3370, 0),
"Join Geometry": (3650, 0),
"Set Position.003": (3970, 0),
"Merge by Distance": (4230, 0),
"Normal": (1540, -340),
"Blur Attribute": (1760, -340),
"Vector Math.003": (2020, -340),
"Store Named Attribute.002": (2020, 0),
"Vector Math.004": (4990, 700),
"Named Attribute.004": (4710, 1080),
"Position.002": (4230, 700),
"Separate XYZ.002": (4450, 700),
"Math.003": (4710, 700),
"Vector Math.002": (5210, 700),
"Named Attribute.003": (4990, 1080),
"Set Position.004": (5430, 0),
"Position.003": (3500, 620),
"Separate XYZ.003": (3750, 620),
"Math.004": (5430, 500),
"Switch": (5700, 0),
"Remove Named Attribute": (5960, 0),
"Set Shade Smooth": (6220, 0),
"Group Output": (6480, 0),
}, (
(("Group",), (-250, 0)),
(("Image Texture",), (-240, 1100)),
(("Vector Math",), (0, 1100)),
(("Math",), (440, 1120)),
(("Math.001", "Mix"), (880, 1160)),
(("Math.003",), (4450, 1040)),
(("Math.004",), (5210, 500)),
(("Blur Attribute",), (1540, -540)),
))
for name, node in stage.items():
if node.location.x >= 2020 and name not in {
"Store Named Attribute.002", "Vector Math.003"
}:
node.location.x += 260
base = route_geometry(group, stage["Store Named Attribute.001"].outputs["Geometry"],
((2770, 180), (3600, 180)))
group.links.new(base, stage["Join Geometry"].inputs["Geometry"])
bypass = route_geometry(group, stage["Set Position"].outputs["Geometry"],
((1970, -760), (5650, -760)))
group.links.new(bypass, stage["Switch"].inputs["False"])

View File

@@ -8,6 +8,7 @@ TEMPORARY_ATTRIBUTES = {
"camera": ("FLOAT_VECTOR", "POINT"),
"surface_position": ("FLOAT_VECTOR", "POINT"),
"source_vertex": ("INT", "POINT"),
"surface_normal": ("FLOAT_VECTOR", "POINT"),
}
@@ -168,11 +169,13 @@ def store_surface_camera(group, geometry, original, strength):
return geometry, _read(group, "camera")
def thicken_depth_surface(group, geometry, thickness):
def thicken_depth_surface(group, geometry, thickness, normal_smooth=None):
"""Connect caps with single quads by source vertex and cap identity."""
nodes, links = group.nodes, group.links
front_geometry = geometry
position = nodes.new("GeometryNodeInputPosition")
geometry = _store(group, geometry, "surface_position", position.outputs[0])
surface_store = geometry.node
flattened = nodes.new("ShaderNodeVectorMath")
flattened.operation = "MULTIPLY"
flattened.inputs[1].default_value = (1.0, 1.0, 0.0)
@@ -249,4 +252,80 @@ def thicken_depth_surface(group, geometry, thickness):
local_axes = nodes.new("ShaderNodeSeparateXYZ")
links.new(local_position.outputs[0], local_axes.inputs[0])
links.new(local_axes.outputs["Z"], identity.inputs["Z"])
if normal_smooth is not None:
normal = nodes.new("GeometryNodeInputNormal")
blur = nodes.new("GeometryNodeBlurAttribute")
blur.data_type = "FLOAT_VECTOR"
links.new(normal_smooth, blur.inputs["Iterations"])
links.new(normal.outputs[0], blur.inputs["Value"])
normalize = nodes.new("ShaderNodeVectorMath")
normalize.operation = "NORMALIZE"
links.new(blur.outputs[0], normalize.inputs[0])
front_geometry = _store(group, front_geometry, "surface_normal", normalize.outputs[0])
links.new(front_geometry, surface_store.inputs["Geometry"])
normal_offset = nodes.new("ShaderNodeVectorMath")
normal_offset.operation = "SCALE"
links.new(_read(group, "surface_normal"), normal_offset.inputs[0])
links.new(displacement.inputs["Z"].links[0].from_socket, normal_offset.inputs[3])
links.new(normal_offset.outputs[0], restored.inputs[1])
nodes.remove(displacement)
return result.outputs["Geometry"]
def _projection_math(nodes, operation, location):
node = nodes.new("ShaderNodeMath")
node.operation = operation
node.location = location
return node
def project_depth_surface(
group, geometry, camera_position, uniform_scale, reference_depth, depth_scale
):
"""Interpolate the rectangular plane into the model camera coordinates."""
nodes, links = group.nodes, group.links
scaled = nodes.new("ShaderNodeVectorMath")
scaled.operation = "SCALE"
scaled.location = (3100.0, -100.0)
links.new(camera_position, scaled.inputs[0])
links.new(uniform_scale, scaled.inputs[3])
separate = nodes.new("ShaderNodeSeparateXYZ")
separate.location = (3300.0, -100.0)
links.new(scaled.outputs["Vector"], separate.inputs["Vector"])
relative_depth = _projection_math(nodes, "SUBTRACT", (3500.0, -100.0))
links.new(reference_depth, relative_depth.inputs[0])
links.new(separate.outputs["Z"], relative_depth.inputs[1])
front = _projection_math(nodes, "MULTIPLY", (3700.0, -100.0))
links.new(relative_depth.outputs[0], front.inputs[0])
links.new(depth_scale, front.inputs[1])
negative_y = _projection_math(nodes, "MULTIPLY", (3500.0, -600.0))
negative_y.inputs[1].default_value = -1.0
links.new(separate.outputs["Y"], negative_y.inputs[0])
projected = nodes.new("ShaderNodeCombineXYZ")
projected.location = (3700.0, -450.0)
links.new(separate.outputs["X"], projected.inputs["X"])
links.new(negative_y.outputs[0], projected.inputs["Y"])
current_position = nodes.new("GeometryNodeInputPosition")
current_position.location = (3500.0, -850.0)
blend = nodes.new("ShaderNodeMix")
blend.data_type = "VECTOR"
blend.clamp_factor = False
blend.location = (3900.0, -450.0)
links.new(depth_scale, blend.inputs[0])
links.new(current_position.outputs["Position"], blend.inputs[4])
links.new(projected.outputs["Vector"], blend.inputs[5])
blended_axes = nodes.new("ShaderNodeSeparateXYZ")
blended_axes.location = (4100.0, -500.0)
links.new(blend.outputs["Result"], blended_axes.inputs["Vector"])
surface_position = nodes.new("ShaderNodeCombineXYZ")
surface_position.location = (4100.0, -200.0)
links.new(blended_axes.outputs["X"], surface_position.inputs["X"])
links.new(blended_axes.outputs["Y"], surface_position.inputs["Y"])
links.new(front.outputs[0], surface_position.inputs["Z"])
project_geometry = nodes.new("GeometryNodeSetPosition")
project_geometry.location = (4400.0, 400.0)
links.new(geometry, project_geometry.inputs["Geometry"])
links.new(surface_position.outputs["Vector"], project_geometry.inputs["Position"])
return project_geometry.outputs["Geometry"]

View File

@@ -1,11 +1,11 @@
"""Build the Mesh Plane Geometry Nodes group."""
"""Build the Image Plane Geometry Nodes group."""
import bpy
from geometry_layouts import layout_geometry
from plane_layouts import layout_geometry
MESH_PLANE_GROUP_NAME = "O Mesh Plane"
IMAGE_PLANE_GROUP_NAME = "O Image Plane"
def new_socket(interface, name, in_out, socket_type, parent=None):
@@ -54,8 +54,8 @@ def localize_dimensions(group):
)
def build_mesh_plane_group():
group = bpy.data.node_groups.new(MESH_PLANE_GROUP_NAME, "GeometryNodeTree")
def build_image_plane_group():
group = bpy.data.node_groups.new(IMAGE_PLANE_GROUP_NAME, "GeometryNodeTree")
group.is_modifier = True
interface = group.interface
geometry = new_socket(interface, "Geometry", "INPUT", "NodeSocketGeometry")

View File

@@ -15,9 +15,9 @@ from anyimage.common.object import (
modifier_input_identifier,
set_modifier_input,
)
from anyimage.operators.convert_to_plane.mesh_plane import (
add_mesh_plane_processing,
create_mesh_plane_mesh,
from anyimage.operators.convert_to_plane.image_plane import (
add_image_plane_processing,
create_image_plane_mesh,
)
@@ -29,15 +29,15 @@ def evaluated_mesh(obj):
def main():
bpy.ops.wm.read_factory_settings(use_empty=True)
mesh = create_mesh_plane_mesh("Mesh Plane", (-1.0, 1.0, -0.5, 0.5))
mesh = create_image_plane_mesh("Image Plane", (-1.0, 1.0, -0.5, 0.5))
assert len(mesh.polygons) == 1
material = bpy.data.materials.new("Mesh Plane Material")
material = bpy.data.materials.new("Image Plane Material")
mesh.materials.append(material)
obj = bpy.data.objects.new("Mesh Plane", mesh)
obj = bpy.data.objects.new("Image Plane", mesh)
bpy.context.scene.collection.objects.link(obj)
modifier = add_mesh_plane_processing(obj)
modifier = add_image_plane_processing(obj)
group = modifier.node_group
assert group.name == "O Mesh Plane"
assert group.name == "O Image Plane"
assert group.is_modifier
inputs = {
item.name: item

View File

@@ -3,7 +3,7 @@
from node_layout import arrange_nodes
LAYOUTS = {
"O Mesh Plane": (
"O Image Plane": (
{
"Bounding Box": (-2320, 440),
"Vector Math": (-2100, 650),