"""Build a shared center on the original Cutout mesh.""" from .nodes import evaluate_field, group_input, sample_field def vector(group, operation, first, second=None): node = group.nodes.new("ShaderNodeVectorMath") node.operation = operation for value, socket in ((first, node.inputs[0]), (second, node.inputs[3 if operation == "SCALE" else 1])): if value is None: continue if hasattr(value, "node"): group.links.new(value, socket) else: socket.default_value = value return node.outputs[0] def axes(group, value): node = group.nodes.new("ShaderNodeSeparateXYZ") group.links.new(value, node.inputs[0]) return node.outputs def combine(group, x, y, z=0): node = group.nodes.new("ShaderNodeCombineXYZ") for value, socket in zip((x, y, z), node.inputs): if hasattr(value, "node"): group.links.new(value, socket) else: socket.default_value = value return node.outputs[0] def set_position(group, geometry, position): node = group.nodes.new("GeometryNodeSetPosition") group.links.new(geometry, node.inputs["Geometry"]) group.links.new(position, node.inputs["Position"]) return node.outputs[0] def sample_surface(group, geometry, value, data_type, domain_position, query): """Keep point indices on points before the face interpolation adapts domains.""" field = evaluate_field(group, sample_field(group, geometry, value, data_type), data_type, "POINT") flat = set_position(group, geometry, domain_position) sample = group.nodes.new("GeometryNodeSampleNearestSurface") sample.data_type = data_type group.links.new(flat, sample.inputs["Mesh"]) group.links.new(field, sample.inputs["Value"]) group.links.new(query, sample.inputs["Sample Position"]) return sample.outputs[0] def build_center_surface(group, unsplit): """Smooth the unsplit displaced surface without moving its XY coordinates.""" nodes, links = group.nodes, group.links controls = group_input(nodes, {"Center Smooth"}) position = nodes.new("GeometryNodeInputPosition").outputs[0] components = axes(group, position) uv = nodes.new("GeometryNodeInputNamedAttribute") uv.data_type = "FLOAT_VECTOR" uv.inputs["Name"].default_value = "UVMap" coordinates = uv.outputs["Attribute"] blur = nodes.new("GeometryNodeBlurAttribute") blur.data_type = "FLOAT" links.new(components["Z"], blur.inputs["Value"]) links.new(controls.outputs["Center Smooth"], blur.inputs["Iterations"]) support = set_position(group, unsplit, combine(group, components["X"], components["Y"], blur.outputs[0])) height = sample_surface(group, support, components["Z"], "FLOAT", coordinates, coordinates) normal = nodes.new("GeometryNodeInputNormal").outputs[0] normal_blur = nodes.new("GeometryNodeBlurAttribute") normal_blur.data_type = "FLOAT_VECTOR" links.new(evaluate_field(group, normal, "FLOAT_VECTOR", "POINT"), normal_blur.inputs["Value"]) links.new(controls.outputs["Center Smooth"], normal_blur.inputs["Iterations"]) normal = normal_blur.outputs[0] normal = vector(group, "NORMALIZE", sample_surface(group, support, normal, "FLOAT_VECTOR", coordinates, coordinates)) return {"geometry": support, "preview": support, "height": height, "normal": normal}