165 lines
4.7 KiB
Python
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"}
|