2025-01-22 16:18:30 +01:00

218 lines
8.6 KiB
Python

import bpy
import lnx.utils
import lnx.node_utils
from lnx.logicnode.lnx_nodes import *
import lnx.logicnode.miscellaneous.LN_call_group as LN_call_group
class GroupInputsNode(LnxLogicTreeNode):
"""Input for a node group."""
bl_idname = 'LNGroupInputsNode'
bl_label = 'Group Input Node'
lnx_section = 'group'
lnx_version = 3
def __init__(self):
self.register_id()
# Active socket selected
active_output: IntProperty(name='active_output', description='', default=0)
# Flag to store invalid links
invalid_link: BoolProperty(name='invalid_link', description='', default=False)
# Override copy prevention in certain situations such as copying entire group
copy_override: BoolProperty(name='copy override', description='', default=False)
def init(self, context):
tree = bpy.context.space_data.edit_tree
node_count = 0
for node in tree.nodes:
if node.bl_idname == 'LNGroupInputsNode':
node_count += 1
if node_count > 1:
lnx.log.warn("Only one group input node per node tree is allowed")
self.mute = True
else:
super().init(context)
# Prevent copying of group node
def copy(self, node):
if not self.copy_override:
self.mute = True
self.outputs.clear()
self.copy_override = False
def lnx_init(self, context):
if not self.mute:
self.add_socket()
# Called when link is created
def insert_link(self, link):
from_socket = link.from_socket
to_node = link.to_node
to_socket = None
# Recursively search for other socket in case of reroutes
if to_node.type == 'REROUTE':
_, to_socket = lnx.node_utils.output_get_connected_node(to_node.outputs[0])
else:
to_socket = link.to_socket
if to_socket is not None:
index = self.get_socket_index(from_socket)
# If socket connected to LnxAnySocket, link is invalid
if to_socket.bl_idname == 'LnxAnySocket':
self.invalid_link = True
else:
call_group_nodes = self.get_call_group_nodes()
for node in call_group_nodes:
# Change socket type according to the new link
node.change_input_socket(to_socket.bl_idname, index, link.from_socket.display_label)
# Use update method to remove invalid links
def update(self):
super().update()
if self.invalid_link:
self.remove_invalid_links()
# Called when name of the socket is changed
def socket_name_update(self, socket):
index = self.get_socket_index(socket)
# Update socket names of the related call group nodes
call_node_groups = self.get_call_group_nodes()
for node in call_node_groups:
inp_socket = node.inputs[index]
if inp_socket.bl_idname == 'LnxAnySocket':
inp_socket.display_label = socket.display_label
else:
inp_socket.name = socket.display_label
# Recursively search and remove invalid links
def remove_invalid_links(self):
for output in self.outputs:
for link in output.links:
if link.to_socket.bl_idname == 'LnxAnySocket':
tree = self.get_tree()
tree.links.remove(link)
break
self.invalid_link = False
# Function to move socket up and handle the same in related call group nodes
def move_socket_up(self):
if self.active_output > 0:
self.outputs.move(self.active_output, self.active_output - 1)
call_node_groups = self.get_call_group_nodes()
for nodes in call_node_groups:
nodes.inputs.move(self.active_output, self.active_output - 1)
self.active_output = self.active_output - 1
# Function to move socket down and handle the same in related call group nodes
def move_socket_down(self):
if self.active_output < len(self.outputs) - 1:
self.outputs.move(self.active_output, self.active_output + 1)
call_node_groups = self.get_call_group_nodes()
for nodes in call_node_groups:
nodes.inputs.move(self.active_output, self.active_output + 1)
self.active_output = self.active_output + 1
# Function to recursively get related call group nodes
def get_call_group_nodes(self):
call_group_nodes = []
# Return empty list if node is muted
if self.mute:
return call_group_nodes
for tree in bpy.data.node_groups:
if tree.bl_idname == "LnxLogicTreeType" or tree.bl_idname == "LnxGroupTree":
for node in tree.nodes:
if node.bl_idname == 'LNCallGroupNode':
if node.group_tree == self.get_tree():
call_group_nodes.append(node)
return call_group_nodes
# Function to add a socket and handle the same in the related call group nodes
def add_socket(self):
self.add_output('LnxAnySocket','')
call_group_nodes = self.get_call_group_nodes()
for node in call_group_nodes:
node.add_input('LnxAnySocket','')
# Function to remove a socket and handle the same in the related call group nodes
def remove_socket(self):
self.outputs.remove(self.outputs[-1])
call_group_nodes = self.get_call_group_nodes()
for node in call_group_nodes:
node.inputs.remove(node.inputs[-1])
if self.active_output > len(self.outputs) - 1:
self.active_output = self.active_output - 1
# Function to add a socket at certain index and
# handle the same in the related call group nodes
def add_socket_ext(self):
index = self.active_output + 1
self.insert_output('LnxAnySocket', index, '')
call_group_nodes = self.get_call_group_nodes()
for node in call_group_nodes:
node.insert_input('LnxAnySocket', index, '')
# Function to remove a socket at certain index and
# handle the same in the related call group nodes
def remove_socket_ext(self):
self.outputs.remove(self.outputs[self.active_output])
call_group_nodes = self.get_call_group_nodes()
for node in call_group_nodes:
node.inputs.remove(node.inputs[self.active_output])
if self.active_output > len(self.outputs) - 1:
self.active_output = len(self.outputs) - 1
# Handle deletion of group input node
def free(self):
call_group_nodes = self.get_call_group_nodes()
for node in call_group_nodes:
node.inputs.clear()
# Draw node UI
def draw_buttons(self, context, layout):
if self.mute:
layout.enabled = False
row = layout.row(align=True)
op = row.operator('lnx.node_call_func', text='New', icon='PLUS', emboss=True)
op.node_index = self.get_id_str()
op.callback_name = 'add_socket'
if len(self.outputs) > 1:
op2 = row.operator('lnx.node_call_func', text='', icon='X', emboss=True)
op2.node_index = self.get_id_str()
op2.callback_name = 'remove_socket'
# Draw side panel UI
def draw_buttons_ext(self, context, layout):
if self.mute:
layout.enabled = False
node = context.active_node
split = layout.row()
split.template_list('LNX_UL_InterfaceSockets', 'OUT', node, 'outputs', node, 'active_output')
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
props = add_remove_col.operator('lnx.node_call_func', icon='ADD', text="")
props.node_index = self.get_id_str()
props.callback_name = 'add_socket_ext'
if len(self.outputs) > 1:
props = add_remove_col.operator('lnx.node_call_func', icon='REMOVE', text="")
props.node_index = self.get_id_str()
props.callback_name = 'remove_socket_ext'
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator('lnx.node_call_func', icon='TRIA_UP', text="")
props.node_index = self.get_id_str()
props.callback_name = 'move_socket_up'
props = up_down_col.operator('lnx.node_call_func', icon='TRIA_DOWN', text="")
props.node_index = self.get_id_str()
props.callback_name = 'move_socket_down'
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1, 2):
raise LookupError()
return node_tree.nodes.new('LNGroupInputsNode')