Files
Bioxel/mednodes/custom_nodes/nodes.py
2024-04-09 22:19:38 +08:00

165 lines
4.7 KiB
Python

import bpy
def add_driver(target_prop, var_sources, expression):
driver = target_prop.driver_add("default_value")
is_vector = isinstance(driver, list)
drivers = driver if is_vector else [driver]
for i, driver in enumerate(drivers):
for j, var_source in enumerate(var_sources):
source = var_source['source']
prop = var_source['prop']
var = driver.driver.variables.new()
var.name = f"var{j}"
var.targets[0].id_type = source.id_type
var.targets[0].id = source
var.targets[0].data_path = f'["{prop}"][{i}]'\
if is_vector else f'["{prop}"]'
driver.driver.expression = expression
def add_direct_driver(target, target_prop, source, source_prop):
target_prop = target.inputs.get(target_prop)
drivers = [
{
"source": source,
"prop": source_prop
}
]
expression = "var0"
add_driver(target_prop, drivers, expression)
class AddCustomNode():
nodes_file: bpy.props.StringProperty(
name="nodes_file",
subtype='FILE_PATH',
default=""
) # type: ignore
node_type: bpy.props.StringProperty(
name='node_type',
description='',
default='',
subtype='NONE',
maxlen=0
) # type: ignore
node_label: bpy.props.StringProperty(
name='node_label',
default=''
) # type: ignore
node_driver: bpy.props.StringProperty(
name='node_driver',
default=''
) # type: ignore
node_description: bpy.props.StringProperty(
name="node_description",
description="",
default="Add custom node group.",
subtype="NONE"
) # type: ignore
node_link: bpy.props.BoolProperty(
name='node_link',
default=True
) # type: ignore
@classmethod
def description(cls, context, properties):
return properties.node_description
def assign_node_tree(self, node):
node.node_tree = bpy.data.node_groups[self.node_type]
node.width = 200.0
node.label = self.node_label or self.node_type
node.name = self.node_type
if self.node_driver:
source_prop = self.node_driver.split(":")[0]
target_prop = self.node_driver.split(":")[1]
add_direct_driver(
target=node,
target_prop=target_prop,
source=bpy.context.active_object,
source_prop=source_prop
)
return node
def append_node_tree(self, node_type):
# try to get node from current file if exists
node_tree = bpy.data.node_groups.get(node_type)
# if not exists, get it from asset file.
if not node_tree:
bpy.ops.wm.append(
'EXEC_DEFAULT',
directory=f"{self.nodes_file}/NodeTree",
filename=node_type,
link=False,
use_recursive=True
)
node_tree = bpy.data.node_groups.get(node_type)
if node_tree:
# self.recursive_append_material(node_tree)
return node_tree
else:
raise RuntimeError('No custom node found')
def recursive_append_material(self, node_tree):
for child in node_tree.nodes:
material_socket = child.inputs.get('Material')
if material_socket:
print(material_socket.default_value)
try:
self.recursive_append_material(child.node_tree)
except:
...
def add_node(self, node_tree):
self.append_node_tree(self.node_type)
node = node_tree.new("GeometryNodeGroup")
self.assign_node_tree(node)
return node
class CUSTOMNODES_OT_Add_Custom_Node(bpy.types.Operator, AddCustomNode):
bl_idname = "customnodes.add_custom_node"
bl_label = "Add Custom Node"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
self.append_node_tree(self.node_type)
# intended to be called upon button press in the node tree
prev_context = bpy.context.area.type
bpy.context.area.type = 'NODE_EDITOR'
# actually invoke the operator to add a node to the current node tree
# use_transform=True ensures it appears where the user's mouse is and is currently
# being moved so the user can place it where they wish
bpy.ops.node.add_node(
'INVOKE_DEFAULT',
type='GeometryNodeGroup',
use_transform=True
)
bpy.context.area.type = prev_context
node = bpy.context.active_node
self.assign_node_tree(node)
return {"FINISHED"}