feat: Add new Geometry Nodes groups for image relief and shadeless shader
- 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.
This commit is contained in:
106
tools/nodes/common/nodes.py
Normal file
106
tools/nodes/common/nodes.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""Build interfaces and basic nodes shared by asset definitions."""
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
||||
def prepare_node_group(node_group):
|
||||
"""Hide unused outputs and discard disconnected interface nodes."""
|
||||
for node in list(node_group.nodes):
|
||||
for socket in node.outputs:
|
||||
socket.hide = not socket.is_linked
|
||||
if node.bl_idname == "NodeGroupInput" and not any(socket.is_linked for socket in node.outputs):
|
||||
node_group.nodes.remove(node)
|
||||
|
||||
|
||||
def interface_socket(group, name, in_out, socket_type, parent=None):
|
||||
socket = group.interface.new_socket(
|
||||
name=name,
|
||||
in_out=in_out,
|
||||
socket_type=socket_type,
|
||||
parent=parent,
|
||||
)
|
||||
if in_out == "INPUT" and hasattr(socket, "structure_type"):
|
||||
socket.structure_type = "SINGLE"
|
||||
return socket
|
||||
|
||||
|
||||
def group_input(nodes, visible_names):
|
||||
node = nodes.new("NodeGroupInput")
|
||||
visible = set(visible_names)
|
||||
for output in node.outputs:
|
||||
output.hide = output.name not in visible
|
||||
return node
|
||||
|
||||
|
||||
def float_input(
|
||||
group,
|
||||
name,
|
||||
default,
|
||||
minimum=0.0,
|
||||
maximum=None,
|
||||
subtype=None,
|
||||
parent=None,
|
||||
):
|
||||
socket = interface_socket(group, name, "INPUT", "NodeSocketFloat", parent)
|
||||
socket.default_value = default
|
||||
socket.min_value = minimum
|
||||
if maximum is not None:
|
||||
socket.max_value = maximum
|
||||
if subtype is not None:
|
||||
socket.subtype = subtype
|
||||
return socket
|
||||
|
||||
|
||||
def bool_input(group, name, default=False, parent=None):
|
||||
socket = interface_socket(group, name, "INPUT", "NodeSocketBool", parent)
|
||||
socket.default_value = default
|
||||
return socket
|
||||
|
||||
|
||||
def vector_input(group, name, default, subtype=None, parent=None):
|
||||
socket = interface_socket(group, name, "INPUT", "NodeSocketVector", parent)
|
||||
socket.default_value = default
|
||||
if subtype is not None:
|
||||
socket.subtype = subtype
|
||||
return socket
|
||||
|
||||
|
||||
def math_node(nodes, operation):
|
||||
node = nodes.new("ShaderNodeMath")
|
||||
node.operation = operation
|
||||
return node
|
||||
|
||||
|
||||
def menu_switch(group, control, names, kind):
|
||||
selector = next((node for node in group.nodes if node.bl_idname == "GeometryNodeMenuSwitch" and node.inputs["Menu"].is_linked and node.inputs["Menu"].links[0].from_socket.name == control), None)
|
||||
if selector is None:
|
||||
selector = group.nodes.new("GeometryNodeMenuSwitch")
|
||||
selector.data_type = "BOOLEAN"
|
||||
for item, name in zip(selector.enum_items, names):
|
||||
item.name = name
|
||||
selector.inputs[names[0]].default_value = False
|
||||
selector.inputs[names[1]].default_value = True
|
||||
local = group_input(group.nodes, {control})
|
||||
group.links.new(local.outputs[control], selector.inputs["Menu"])
|
||||
node = group.nodes.new("GeometryNodeSwitch")
|
||||
node.input_type = kind
|
||||
group.links.new(selector.outputs[0], node.inputs["Switch"])
|
||||
return SimpleNamespace(inputs={names[0]: node.inputs["False"], names[1]: node.inputs["True"]}, outputs=node.outputs)
|
||||
|
||||
|
||||
def read_float_attribute(group, name):
|
||||
node = group.nodes.new("GeometryNodeInputNamedAttribute")
|
||||
node.data_type = "FLOAT"
|
||||
node.inputs["Name"].default_value = name
|
||||
return node.outputs["Attribute"]
|
||||
|
||||
|
||||
def store_float_attribute(group, geometry, name, value, domain="POINT"):
|
||||
node = group.nodes.new("GeometryNodeStoreNamedAttribute")
|
||||
node.domain, node.data_type = domain, "FLOAT"
|
||||
node.inputs["Name"].default_value = name
|
||||
group.links.new(geometry, node.inputs["Geometry"])
|
||||
if isinstance(value, (int, float)):
|
||||
node.inputs["Value"].default_value = value
|
||||
else:
|
||||
group.links.new(value, node.inputs["Value"])
|
||||
return node.outputs["Geometry"]
|
||||
Reference in New Issue
Block a user