forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
@ -0,0 +1,12 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class IntFromBooleanNode(LnxLogicTreeNode):
|
||||
"""Returns an int depending on the respective boolean state."""
|
||||
bl_idname = 'LNIntFromBooleanNode'
|
||||
bl_label = 'Boolean to Int'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.inputs.new('LnxBoolSocket', 'Bool')
|
||||
|
||||
self.outputs.new('LnxIntSocket', 'Int')
|
@ -0,0 +1,17 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class VectorFromBooleanNode(LnxLogicTreeNode):
|
||||
"""Returns a vector depending on the respective boolean state."""
|
||||
bl_idname = 'LNVectorFromBooleanNode'
|
||||
bl_label = 'Boolean to Vector'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.inputs.new('LnxBoolSocket', 'X')
|
||||
self.inputs.new('LnxBoolSocket', '-X')
|
||||
self.inputs.new('LnxBoolSocket', 'Y')
|
||||
self.inputs.new('LnxBoolSocket', '-Y')
|
||||
self.inputs.new('LnxBoolSocket', 'Z')
|
||||
self.inputs.new('LnxBoolSocket', '-Z')
|
||||
|
||||
self.outputs.new('LnxVectorSocket', 'Vector')
|
143
leenkx/blender/lnx/logicnode/miscellaneous/LN_call_group.py
Normal file
143
leenkx/blender/lnx/logicnode/miscellaneous/LN_call_group.py
Normal file
@ -0,0 +1,143 @@
|
||||
import bpy
|
||||
|
||||
import lnx.utils
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class CallGroupNode(LnxLogicTreeNode):
|
||||
"""Calls the given group of nodes."""
|
||||
bl_idname = 'LNCallGroupNode'
|
||||
bl_label = 'Call Node Group'
|
||||
lnx_section = 'group'
|
||||
lnx_version = 3
|
||||
|
||||
def __init__(self):
|
||||
self.register_id()
|
||||
|
||||
def lnx_init(self, context):
|
||||
pass
|
||||
|
||||
# Function to add input sockets and re-link sockets
|
||||
def update_inputs(self, tree, node, inp_sockets, in_links):
|
||||
count = 0
|
||||
for output in node.outputs:
|
||||
_, c_socket = lnx.node_utils.output_get_connected_node(output)
|
||||
if c_socket is not None:
|
||||
current_socket = self.add_input(c_socket.bl_idname, output.name)
|
||||
if(count < len(in_links)):
|
||||
# Preserve default values in input sockets
|
||||
inp_sockets[count].copy_defaults(current_socket)
|
||||
for link in in_links[count]:
|
||||
tree.links.new(link, current_socket)
|
||||
else:
|
||||
current_socket = self.add_input('LnxAnySocket', output.name)
|
||||
current_socket.display_label = output.name
|
||||
if(count < len(in_links)):
|
||||
for link in in_links[count]:
|
||||
tree.links.new(link, current_socket)
|
||||
count = count + 1
|
||||
|
||||
# Function to add output sockets and re-link sockets
|
||||
def update_outputs(self, tree, node, out_links):
|
||||
count = 0
|
||||
for input in node.inputs:
|
||||
_, c_socket = lnx.node_utils.input_get_connected_node(input)
|
||||
if c_socket is not None:
|
||||
current_socket = self.add_output(c_socket.bl_idname, input.name)
|
||||
if(count < len(out_links)):
|
||||
for link in out_links[count]:
|
||||
nlink = tree.links.new(current_socket, link)
|
||||
nlink.is_valid = True
|
||||
nlink.is_muted = False
|
||||
else:
|
||||
current_socket = self.add_output('LnxAnySocket', input.name)
|
||||
current_socket.display_label = input.name
|
||||
if(count < len(out_links)):
|
||||
for link in out_links[count]:
|
||||
tree.links.new(current_socket, link)
|
||||
count = count + 1
|
||||
|
||||
def remove_tree(self):
|
||||
self.group_tree = None
|
||||
|
||||
def update_sockets(self, context):
|
||||
# List to store from and to sockets of connected nodes
|
||||
from_socket_list = []
|
||||
to_socket_list = []
|
||||
inp_socket_list = []
|
||||
tree = self.get_tree()
|
||||
|
||||
# Loop through each input socket
|
||||
for inp in self.inputs:
|
||||
link_per_socket = []
|
||||
#Loop through each link to the socket
|
||||
for link in inp.links:
|
||||
link_per_socket.append(link.from_socket)
|
||||
from_socket_list.append(link_per_socket)
|
||||
inp_socket_list.append(inp)
|
||||
|
||||
# Loop through each output socket
|
||||
for out in self.outputs:
|
||||
link_per_socket = []
|
||||
# Loop through each link to the socket
|
||||
for link in out.links:
|
||||
link_per_socket.append(link.to_socket)
|
||||
to_socket_list.append(link_per_socket)
|
||||
|
||||
# Remove all output sockets
|
||||
for output in self.outputs:
|
||||
self.outputs.remove(output)
|
||||
# Search for Group Input/Output
|
||||
if self.group_tree is not None:
|
||||
for node in self.group_tree.nodes:
|
||||
if node.bl_idname == 'LNGroupInputsNode':
|
||||
# Update input sockets
|
||||
self.update_inputs(tree, node, inp_socket_list, from_socket_list)
|
||||
break
|
||||
for node in self.group_tree.nodes:
|
||||
if node.bl_idname == 'LNGroupOutputsNode':
|
||||
# Update output sockets
|
||||
self.update_outputs(tree, node, to_socket_list)
|
||||
break
|
||||
#Remove all old input sockets after setting defaults
|
||||
for inp in inp_socket_list:
|
||||
self.inputs.remove(inp)
|
||||
|
||||
# Prperty to store group tree pointer
|
||||
group_tree: PointerProperty(name='Group', type=bpy.types.NodeTree, update=update_sockets)
|
||||
|
||||
def draw_label(self) -> str:
|
||||
if self.group_tree is not None:
|
||||
return f'Group: {self.group_tree.name}'
|
||||
return self.bl_label
|
||||
|
||||
# Draw node UI
|
||||
def draw_buttons(self, context, layout):
|
||||
col = layout.column()
|
||||
row_name = col.row(align=True)
|
||||
row_add = col.row(align=True)
|
||||
row_ops = col.row()
|
||||
if self.group_tree is None:
|
||||
op = row_add.operator('lnx.add_group_tree', icon='PLUS', text='New Group')
|
||||
op.node_index = self.get_id_str()
|
||||
op = row_name.operator('lnx.search_group_tree', text='', icon='VIEWZOOM')
|
||||
op.node_index = self.get_id_str()
|
||||
if self.group_tree:
|
||||
row_name.prop(self.group_tree, 'name', text='')
|
||||
row_copy = row_name.split(align=True)
|
||||
row_copy.alignment = 'CENTER'
|
||||
fake_user = 1 if self.group_tree.use_fake_user else 0
|
||||
op = row_copy.operator('lnx.copy_group_tree', text=str(self.group_tree.users - fake_user))
|
||||
op.node_index = self.get_id_str()
|
||||
row_name.prop(self.group_tree, 'use_fake_user', text='')
|
||||
op = row_name.operator('lnx.unlink_group_tree', icon='X', text='')
|
||||
op.node_index = self.get_id_str()
|
||||
row_ops.enabled = not self.group_tree is None
|
||||
op = row_ops.operator('lnx.edit_group_tree', icon='FULLSCREEN_ENTER', text='Edit tree')
|
||||
op.node_index = self.get_id_str()
|
||||
|
||||
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('LNCallGroupNode')
|
@ -0,0 +1,17 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class DefaultIfNullNode(LnxLogicTreeNode):
|
||||
"""Returns the connected value only if it is not `null`, otherwise the `default` value is returned.
|
||||
|
||||
@input Value: the one that will be eventually null
|
||||
@input Default: will be returned in case the primary value is null
|
||||
"""
|
||||
bl_idname = 'LNDefaultIfNullNode'
|
||||
bl_label = 'Default if Null'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.inputs.new('LnxDynamicSocket', 'Value In')
|
||||
self.inputs.new('LnxDynamicSocket', 'Default')
|
||||
|
||||
self.outputs.new('LnxDynamicSocket', 'Value Out')
|
@ -0,0 +1,11 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class TimeNode(LnxLogicTreeNode):
|
||||
"""Returns the application execution time and the delta time."""
|
||||
bl_idname = 'LNTimeNode'
|
||||
bl_label = 'Get Application Time'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_output('LnxFloatSocket', 'Time')
|
||||
self.add_output('LnxFloatSocket', 'Delta')
|
@ -0,0 +1,36 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class GetDebugConsoleSettings(LnxLogicTreeNode):
|
||||
"""Return properties of the debug console.
|
||||
|
||||
@output Enabled: Whether the debug console is enabled.
|
||||
@output Visible: Whether the debug console is visible,
|
||||
or `false` if the debug console is disabled.
|
||||
@output Hovered: Whether the debug console is hovered by the mouse cursor,
|
||||
or `false` if the debug console is disabled.
|
||||
@output UI Scale: The scaling factor of the debug console user interface,
|
||||
or `1.0` if the debug console is disabled.
|
||||
@output Position: The initial position of the debug console.
|
||||
Possible values if the debug console is enabled: `"Left"`, `"Center"`, `"Right"`.
|
||||
If the debug console is disabled, the returned value is an empty string `""`.
|
||||
"""
|
||||
bl_idname = 'LNGetDebugConsoleSettings'
|
||||
bl_label = 'Get Debug Console Settings'
|
||||
lnx_version = 2
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_output('LnxBoolSocket', 'Enabled')
|
||||
self.add_output('LnxBoolSocket', 'Visible')
|
||||
self.add_output('LnxBoolSocket', 'Hovered')
|
||||
self.add_output('LnxFloatSocket', 'UI Scale')
|
||||
self.add_output('LnxStringSocket', 'Position')
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
if self.lnx_version not in (0, 1):
|
||||
raise LookupError()
|
||||
|
||||
return NodeReplacement(
|
||||
'LNGetDebugConsoleSettings', self.lnx_version, 'LNGetDebugConsoleSettings', 2,
|
||||
in_socket_mapping={}, out_socket_mapping={0: 1, 1: 3, 2: 4}
|
||||
)
|
@ -0,0 +1,15 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class DisplayInfoNode(LnxLogicTreeNode):
|
||||
"""Returns the current display resolution.
|
||||
|
||||
@seeNode Get Window Resolution
|
||||
"""
|
||||
bl_idname = 'LNDisplayInfoNode'
|
||||
bl_label = 'Get Display Resolution'
|
||||
lnx_section = 'screen'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_output('LnxIntSocket', 'Width')
|
||||
self.add_output('LnxIntSocket', 'Height')
|
10
leenkx/blender/lnx/logicnode/miscellaneous/LN_get_fps.py
Normal file
10
leenkx/blender/lnx/logicnode/miscellaneous/LN_get_fps.py
Normal file
@ -0,0 +1,10 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class GetFPSNode(LnxLogicTreeNode):
|
||||
"""Get the frames per second count."""
|
||||
bl_idname = 'LNGetFPSNode'
|
||||
bl_label = 'Get Frames Per Second'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_output('LnxIntSocket', 'Count')
|
@ -0,0 +1,15 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class WindowInfoNode(LnxLogicTreeNode):
|
||||
"""Returns the current window resolution.
|
||||
|
||||
@seeNode Get Display Resolution
|
||||
"""
|
||||
bl_idname = 'LNWindowInfoNode'
|
||||
bl_label = 'Get Window Resolution'
|
||||
lnx_section = 'screen'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_output('LnxIntSocket', 'Width')
|
||||
self.add_output('LnxIntSocket', 'Height')
|
217
leenkx/blender/lnx/logicnode/miscellaneous/LN_group_input.py
Normal file
217
leenkx/blender/lnx/logicnode/miscellaneous/LN_group_input.py
Normal file
@ -0,0 +1,217 @@
|
||||
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')
|
217
leenkx/blender/lnx/logicnode/miscellaneous/LN_group_output.py
Normal file
217
leenkx/blender/lnx/logicnode/miscellaneous/LN_group_output.py
Normal file
@ -0,0 +1,217 @@
|
||||
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 GroupOutputsNode(LnxLogicTreeNode):
|
||||
"""Output for a node group."""
|
||||
bl_idname = 'LNGroupOutputsNode'
|
||||
bl_label = 'Group Output Node'
|
||||
lnx_section = 'group'
|
||||
lnx_version = 3
|
||||
|
||||
def __init__(self):
|
||||
self.register_id()
|
||||
|
||||
# Active socket selected
|
||||
active_input: IntProperty(name='active_input', 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 == 'LNGroupOutputsNode':
|
||||
node_count += 1
|
||||
if node_count > 1:
|
||||
lnx.log.warn("Only one group output 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.inputs.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):
|
||||
to_socket = link.to_socket
|
||||
from_node = link.from_node
|
||||
from_socket = None
|
||||
# Recursively search for other socket in case of reroutes
|
||||
if from_node.type == 'REROUTE':
|
||||
_, from_socket = lnx.node_utils.input_get_connected_node(to_socket)
|
||||
else:
|
||||
from_socket = link.from_socket
|
||||
if from_socket is not None:
|
||||
index = self.get_socket_index(to_socket)
|
||||
# If socket connected to LnxAnySocket, link is invalid
|
||||
if from_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_output_socket(from_socket.bl_idname, index, link.to_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:
|
||||
out_socket = node.outputs[index]
|
||||
if out_socket.bl_idname == 'LnxAnySocket':
|
||||
out_socket.display_label = socket.display_label
|
||||
else:
|
||||
out_socket.name = socket.display_label
|
||||
|
||||
# Recursively search and remove invalid links
|
||||
def remove_invalid_links(self):
|
||||
for input in self.inputs:
|
||||
for link in input.links:
|
||||
if link.from_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_input > 0:
|
||||
self.inputs.move(self.active_input, self.active_input - 1)
|
||||
call_node_groups = self.get_call_group_nodes()
|
||||
for nodes in call_node_groups:
|
||||
nodes.outputs.move(self.active_input, self.active_input - 1)
|
||||
self.active_input = self.active_input - 1
|
||||
|
||||
# Function to move socket down and handle the same in related call group nodes
|
||||
def move_socket_down(self):
|
||||
if self.active_input < len(self.inputs) - 1:
|
||||
self.inputs.move(self.active_input, self.active_input + 1)
|
||||
call_node_groups = self.get_call_group_nodes()
|
||||
for nodes in call_node_groups:
|
||||
nodes.outputs.move(self.active_input, self.active_input + 1)
|
||||
self.active_input = self.active_input + 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_input('LnxAnySocket','',)
|
||||
call_group_nodes = self.get_call_group_nodes()
|
||||
for node in call_group_nodes:
|
||||
node.add_output('LnxAnySocket','')
|
||||
|
||||
# Function to remove a socket and handle the same in the related call group nodes
|
||||
def remove_socket(self):
|
||||
self.inputs.remove(self.inputs[-1])
|
||||
call_group_nodes = self.get_call_group_nodes()
|
||||
for node in call_group_nodes:
|
||||
node.outputs.remove(node.outputs[-1])
|
||||
if self.active_input > len(self.inputs) - 1:
|
||||
self.active_input = self.active_input - 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_input + 1
|
||||
self.insert_input('LnxAnySocket', index, '')
|
||||
call_group_nodes = self.get_call_group_nodes()
|
||||
for node in call_group_nodes:
|
||||
node.insert_output('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.inputs.remove(self.inputs[self.active_input])
|
||||
call_group_nodes = self.get_call_group_nodes()
|
||||
for node in call_group_nodes:
|
||||
node.outputs.remove(node.outputs[self.active_input])
|
||||
if self.active_input > len(self.inputs) - 1:
|
||||
self.active_input = len(self.inputs) - 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.outputs.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.inputs) > 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', 'IN', node, 'inputs', node, 'active_input')
|
||||
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.inputs) > 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('LNGroupOutputsNode')
|
@ -0,0 +1,71 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class RegularExpressionNode(LnxLogicTreeNode):
|
||||
"""
|
||||
The first argument is a string with a regular expression pattern, the second one is a string with flags.
|
||||
|
||||
@input RegExp Pattern: regular expression patterns such as
|
||||
- `.`: any character
|
||||
- `*`: repeat zero-or-more
|
||||
- `+`: repeat one-or-more
|
||||
- `?`: optional zero-or-one
|
||||
- `[A-Z0-9]`: character ranges
|
||||
- `[^\\r\\n\\t]`: character not-in-range
|
||||
- `(...)`: parenthesis to match groups of characters
|
||||
- `^`: beginning of the string (beginning of a line in multiline matching mode)
|
||||
- `$`: end of the string (end of a line in multiline matching mode)
|
||||
- `|`: "OR" statement.
|
||||
|
||||
@input RegExp Flags: possible flags are the following
|
||||
- `i`: case insensitive matching
|
||||
- `g`: global replace or split, see below
|
||||
- `m`: multiline matching, ^ and $ represent the beginning and end of a line
|
||||
- `s`: the dot . will also match newlines (not supported by C# and JavaScript versions before ES2018)
|
||||
- `u`: use UTF-8 matching (Neko and C++ targets only)
|
||||
|
||||
@input String: String to match, split or replace
|
||||
@input Replace: String to use when replace
|
||||
|
||||
@output Match: boolean result comparing the regular expression pattern with the string
|
||||
@output Matched: array containing list of matched patterns
|
||||
@output Split: array string of string splits using the pattern
|
||||
@output Replace: new string with the pattern replaced
|
||||
|
||||
"""
|
||||
bl_idname = 'LNRegularExpressionNode'
|
||||
bl_label = 'Regular Expression'
|
||||
|
||||
lnx_version = 1
|
||||
|
||||
def remove_extra_inputs(self, context):
|
||||
while len(self.outputs) > 0:
|
||||
self.outputs.remove(self.outputs[-1])
|
||||
if len(self.inputs) != 3:
|
||||
self.inputs.remove(self.inputs[-1])
|
||||
if self.property0 == 'Match':
|
||||
self.add_output('LnxBoolSocket', 'Match')
|
||||
self.add_output('LnxNodeSocketArray', 'Matched', is_var=False)
|
||||
if self.property0 == 'Split':
|
||||
self.add_output('LnxNodeSocketArray', 'Split', is_var=False)
|
||||
if self.property0 == 'Replace':
|
||||
self.add_input('LnxStringSocket', 'Replace')
|
||||
self.add_output('LnxStringSocket', 'String')
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('Match', 'Match', 'A regular expression is used to compare a string. Use () in the pattern to retrieve Matched groups'),
|
||||
('Split', 'Split', 'A regular expression can also be used to split a string into several substrings'),
|
||||
('Replace', 'Replace', 'A regular expression can also be used to replace a part of the string')],
|
||||
name='', default='Match', update=remove_extra_inputs)
|
||||
|
||||
def lnx_init(self, context):
|
||||
|
||||
self.add_input('LnxStringSocket', 'RegExp Pattern')
|
||||
self.add_input('LnxStringSocket', 'RegExp Flags')
|
||||
self.add_input('LnxStringSocket', 'String')
|
||||
|
||||
self.add_output('LnxBoolSocket', 'Match')
|
||||
self.add_output('LnxNodeSocketArray', 'Matched', is_var=False)
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
@ -0,0 +1,24 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SetDebugConsoleSettings(LnxLogicTreeNode):
|
||||
"""Sets the debug console settings."""
|
||||
bl_idname = 'LNSetDebugConsoleSettings'
|
||||
bl_label = 'Set Debug Console Settings'
|
||||
lnx_version = 1
|
||||
|
||||
property0: HaxeEnumProperty(
|
||||
'property0',
|
||||
items = [('left', 'Anchor Left', 'Anchor debug console in the top left'),
|
||||
('center', 'Anchor Center', 'Anchor debug console in the top center'),
|
||||
('right', 'Anchor Right', 'Anchor the debug console in the top right')],
|
||||
name='', default='right')
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxBoolSocket', 'Visible')
|
||||
self.add_input('LnxFloatSocket', 'Scale', default_value=1.0)
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property0')
|
@ -0,0 +1,13 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SetTimeScaleNode(LnxLogicTreeNode):
|
||||
"""Sets the global time scale."""
|
||||
bl_idname = 'LNSetTimeScaleNode'
|
||||
bl_label = 'Set Time Scale'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxFloatSocket', 'Scale', default_value=1.0)
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
14
leenkx/blender/lnx/logicnode/miscellaneous/LN_sleep.py
Normal file
14
leenkx/blender/lnx/logicnode/miscellaneous/LN_sleep.py
Normal file
@ -0,0 +1,14 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
class SleepNode(LnxLogicTreeNode):
|
||||
"""Waits a specified amount of seconds until passing
|
||||
through the incoming signal."""
|
||||
bl_idname = 'LNSleepNode'
|
||||
bl_label = 'Sleep'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'In')
|
||||
self.add_input('LnxFloatSocket', 'Time')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
50
leenkx/blender/lnx/logicnode/miscellaneous/LN_timer.py
Normal file
50
leenkx/blender/lnx/logicnode/miscellaneous/LN_timer.py
Normal file
@ -0,0 +1,50 @@
|
||||
from lnx.logicnode.lnx_nodes import *
|
||||
|
||||
|
||||
class TimerNode(LnxLogicTreeNode):
|
||||
"""Runs a timer with a specified amount of repetitions.
|
||||
|
||||
@input Start: Start the timer or continue if paused. In both cases,
|
||||
the values of `Duration` and `Repeat` are (re-)evaluated.
|
||||
@input Pause: Pause the timer.
|
||||
@input Stop: Stop and reset the timer. This does not activate any outputs.
|
||||
@input Duration: The time in seconds that the timer runs.
|
||||
@input Repeat: The number of times the timer will repeat, or 0 for infinite repetition.
|
||||
|
||||
@output Out: Activated after each repetition.
|
||||
@output Done: Activated after the last repetition (never activated if `Repeat` is 0).
|
||||
@output Running: Whether the timer is currently running.
|
||||
@output Time Passed: The time in seconds that has passed since the
|
||||
current repetition started, excluding pauses.
|
||||
@output Time Left: The time left in seconds until the timer is done
|
||||
or the next repetition starts.
|
||||
@output Progress: Percentage of the timer's progress of the current
|
||||
repetition (`Time Passed/Duration`).
|
||||
@output Repetitions: The index of the current repetition, starting at 0.
|
||||
"""
|
||||
bl_idname = 'LNTimerNode'
|
||||
bl_label = 'Timer'
|
||||
lnx_version = 1
|
||||
|
||||
def lnx_init(self, context):
|
||||
self.add_input('LnxNodeSocketAction', 'Start')
|
||||
self.add_input('LnxNodeSocketAction', 'Pause')
|
||||
self.add_input('LnxNodeSocketAction', 'Stop')
|
||||
self.add_input('LnxFloatSocket', 'Duration', default_value=1.0)
|
||||
self.add_input('LnxIntSocket', 'Repeat')
|
||||
|
||||
self.add_output('LnxNodeSocketAction', 'Out')
|
||||
self.add_output('LnxNodeSocketAction', 'Done')
|
||||
self.add_output('LnxBoolSocket', 'Running')
|
||||
self.add_output('LnxIntSocket', 'Time Passed')
|
||||
self.add_output('LnxIntSocket', 'Time Left')
|
||||
self.add_output('LnxFloatSocket', 'Progress')
|
||||
self.add_output('LnxFloatSocket', 'Repetitions')
|
||||
|
||||
def draw_label(self) -> str:
|
||||
inp_duration = self.inputs['Duration']
|
||||
inp_repeat = self.inputs['Repeat']
|
||||
if inp_duration.is_linked or inp_repeat.is_linked:
|
||||
return self.bl_label
|
||||
|
||||
return f'{self.bl_label}: {round(inp_duration.default_value_raw, 3)}s ({inp_repeat.default_value_raw} R.)'
|
5
leenkx/blender/lnx/logicnode/miscellaneous/__init__.py
Normal file
5
leenkx/blender/lnx/logicnode/miscellaneous/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from lnx.logicnode.lnx_nodes import add_node_section
|
||||
|
||||
add_node_section(name='group', category='Miscellaneous')
|
||||
add_node_section(name='screen', category='Miscellaneous')
|
||||
add_node_section(name='default', category='Miscellaneous')
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user