Update Files

This commit is contained in:
2025-01-22 16:18:30 +01:00
parent ed4603cf95
commit a36294b518
16718 changed files with 2960346 additions and 0 deletions

View File

@ -0,0 +1,107 @@
import importlib
import inspect
import pkgutil
import sys
import lnx
import lnx.logicnode.lnx_nodes as lnx_nodes
from lnx.logicnode.lnx_props import *
import lnx.logicnode.lnx_sockets as lnx_sockets
from lnx.logicnode.replacement import NodeReplacement
from lnx.logicnode.lnx_advanced_draw import *
if lnx.is_reload(__name__):
lnx_nodes = lnx.reload_module(lnx_nodes)
lnx.logicnode.lnx_props = lnx.reload_module(lnx.logicnode.lnx_props)
from lnx.logicnode.lnx_props import *
lnx_sockets = lnx.reload_module(lnx_sockets)
lnx.logicnode.replacement = lnx.reload_module(lnx.logicnode.replacement)
from lnx.logicnode.replacement import NodeReplacement
HAS_RELOADED = True
else:
lnx.enable_reload(__name__)
def init_categories():
"""Register default node menu categories."""
lnx_nodes.add_category('Logic', icon='OUTLINER', section="basic",
description="Logic nodes are used to control execution flow using branching, loops, gates etc.")
lnx_nodes.add_category('Event', icon='INFO', section="basic")
lnx_nodes.add_category('Input', icon='GREASEPENCIL', section="basic")
lnx_nodes.add_category('Native', icon='MEMORY', section="basic",
description="The Native category contains nodes which interact with the system (Input/Output functionality, etc.) or Haxe.")
lnx_nodes.add_category('Browser', icon='URL', section="basic")
lnx_nodes.add_category('Camera', icon='OUTLINER_OB_CAMERA', section="data")
lnx_nodes.add_category('Material', icon='MATERIAL', section="data")
lnx_nodes.add_category('Light', icon='LIGHT', section="data")
lnx_nodes.add_category('World', icon='WORLD', section="data")
lnx_nodes.add_category('Object', icon='OBJECT_DATA', section="data")
lnx_nodes.add_category('Scene', icon='SCENE_DATA', section="data")
lnx_nodes.add_category('Trait', icon='NODETREE', section="data")
lnx_nodes.add_category('Network', icon='WORLD', section="data")
lnx_nodes.add_category('Leenkx', icon='ORIENTATION_CURSOR', section="data")
lnx_nodes.add_category('Animation', icon='SEQUENCE', section="motion")
lnx_nodes.add_category('Navmesh', icon='UV_VERTEXSEL', section="motion")
lnx_nodes.add_category('Transform', icon='TRANSFORM_ORIGINS', section="motion")
lnx_nodes.add_category('Physics', icon='PHYSICS', section="motion")
lnx_nodes.add_category('Array', icon='MOD_ARRAY', section="values")
lnx_nodes.add_category('Map', icon='SHORTDISPLAY', section="values")
lnx_nodes.add_category('Database', icon='MESH_CYLINDER', section="values")
lnx_nodes.add_category('Math', icon='FORCE_HARMONIC', section="values")
lnx_nodes.add_category('Random', icon='SEQ_HISTOGRAM', section="values")
lnx_nodes.add_category('String', icon='SORTALPHA', section="values")
lnx_nodes.add_category('Variable', icon='OPTIONS', section="values")
lnx_nodes.add_category('HTML', icon='SEQ_STRIP_META', section="graphics")
lnx_nodes.add_category('Draw', icon='GREASEPENCIL', section="graphics")
lnx_nodes.add_category('Canvas', icon='RENDERLAYERS', section="graphics",
description="Note: To get the canvas, be sure that the node(s) and the canvas (UI) is attached to the same object.")
lnx_nodes.add_category('Postprocess', icon='FREEZE', section="graphics")
lnx_nodes.add_category('Renderpath', icon='STICKY_UVS_LOC', section="graphics")
lnx_nodes.add_category('Sound', icon='OUTLINER_OB_SPEAKER', section="sound")
lnx_nodes.add_category('3D_Audio', icon='SPEAKER', section="sound")
lnx_nodes.add_category('Miscellaneous', icon='RESTRICT_COLOR_ON', section="misc")
lnx_nodes.add_category('Custom', icon='PLUGIN', section="misc")
lnx_nodes.add_category('Layout', icon='SEQ_STRIP_DUPLICATE', section="misc")
# Make sure that logic node extension packs are displayed at the end
# of the menu by default unless they declare it otherwise
lnx_nodes.add_category_section('default')
def init_nodes(base_path=__path__, base_package=__package__, subpackages_only=False):
"""Calls the `on_register()` method on all logic nodes in a given
`base_package` and all its sub-packages relative to the given
`base_path`, in order to initialize them and to register them to Leenkx.
Be aware that calling this function will import all modules in the
given package, so module-level code will be executed.
If `subpackages_only` is true, modules directly inside the root of
the base package are not searched and imported.
"""
for loader, module_name, is_pkg in pkgutil.walk_packages(base_path, base_package + '.'):
if is_pkg:
# The package must be loaded as well so that the modules from that package can be accessed (see the
# pkgutil.walk_packages documentation for more information on this)
loader.find_module(module_name).load_module(module_name)
# Only look at modules in sub packages if specified
elif not subpackages_only or module_name.rsplit('.', 1)[0] != base_package:
if 'HAS_RELOADED' not in globals() or module_name not in sys.modules:
_module = importlib.import_module(module_name)
else:
# Reload the module if the SDK was reloaded at least once
_module = importlib.reload(sys.modules[module_name])
for name, obj in inspect.getmembers(_module, inspect.isclass):
if name in ("LnxLogicTreeNode", "LnxLogicVariableNodeMixin"):
continue
if issubclass(obj, lnx_nodes.LnxLogicTreeNode):
obj.on_register()

View File

@ -0,0 +1,32 @@
from lnx.logicnode.lnx_nodes import *
class AnimActionNode(LnxLogicTreeNode):
"""Samples a given action."""
bl_idname = 'LNAnimActionNode'
bl_label = 'Action'
lnx_version = 2
property0: HaxeStringProperty('property0', name = '', default = '')
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimAction', 'Action')
self.add_input('LnxBoolSocket', 'Is Looped')
self.add_output('LnxNodeSocketAnimTree', 'Action')
def draw_buttons(self, context, layout):
layout.label(text='Action ID:')
layout.prop(self, 'property0')
def draw_label(self) -> str:
return f'{self.bl_label}: {self.property0}'
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement(
'LNAnimActionNode', self.lnx_version, 'LNAnimActionNode', 2,
in_socket_mapping={}, out_socket_mapping={}
)

View File

@ -0,0 +1,12 @@
from lnx.logicnode.lnx_nodes import *
class ActionNameNode(LnxLogicTreeNode):
"""Stores the given action name."""
bl_idname = 'LNActionNameNode'
bl_label = 'Action Name'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAnimAction', 'Action')
self.add_output('LnxNodeSocketAnimAction', 'Action')

View File

@ -0,0 +1,25 @@
from lnx.logicnode.lnx_nodes import *
class BlendActionNode(LnxLogicTreeNode):
"""Interpolates between the two given actions."""
bl_idname = 'LNBlendActionNode'
bl_label = 'Blend Action'
lnx_version = 2
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimTree', 'Action 1')
self.add_input('LnxNodeSocketAnimTree', 'Action 2')
self.add_input('ArmFactorSocket', 'Factor', default_value = 0.5)
self.add_input('LnxIntSocket', 'Bone Group', default_value = -1)
self.add_output('LnxNodeSocketAnimTree', 'Result')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement(
'LNBlendActionNode', self.lnx_version, 'LNBlendActionNode', 2,
in_socket_mapping={}, out_socket_mapping={}
)

View File

@ -0,0 +1,198 @@
from lnx.logicnode.lnx_advanced_draw import *
from lnx.logicnode.lnx_nodes import *
from bpy.props import *
from bpy.types import Node
class BlendSpaceNode(LnxLogicTreeNode):
"""Activates the output when the given event is received.
@seeNode Send Event to Object
@seeNode Send Event"""
bl_idname = 'LNBlendSpaceNode'
bl_label = 'Blend Space'
lnx_version = 1
lnx_section = 'custom'
property2: HaxeBoolProperty(
'property2',
name="Enable or Disable",
description="A bool property",
default = False
)
advanced_draw_run: BoolProperty(
name = "Advance draw enabled",
description="",
default = False
)
def stop_modal(self):
self.property2 = False
def my_float_update(self, context):
if self.property2:
self.set_x_y_socket()
property0: HaxeFloatVectorProperty(
'property0',
name = "Point Coordionates",
description="",
default = (0.0, 0.0,
0.0, 1.0,
1.0, 1.0,
1.0, 0.0,
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
0.0, 0.0,
0.5, 0.5),
size = 22,
update = my_float_update
)
active_point_index: IntProperty(
default = -1
)
show_numbers: BoolProperty(
name = "Show Point Numbers",
description="",
default = False
)
active_point_index_ref: IntProperty(
default = 0
)
gui_bounds: FloatVectorProperty(
name = "GUI bounds",
description = "",
default = (0.0, 0.0, 0.0),
size = 3
)
point_size: FloatProperty(
name = "Point Size",
description = "",
default = 0.015
)
property1: HaxeBoolVectorProperty(
'property1',
name = "Point enabled for view",
description = "",
default = (True,True,True,True, False, False, False, False, False, False, True),
size = 11
)
draw_handler_dict = {}
modal_handler_dict = {}
def __init__(self):
array_nodes[str(id(self))] = self
if self.advanced_draw_run:
self.add_advanced_draw()
def create_blend_space(self):
self.blend_space = BlendSpaceGUI(self)
def free(self):
self.remove_advanced_draw()
def get_blend_space_points(self):
if bpy.context.space_data.edit_tree == self.get_tree():
return self.blend_space.points
def draw_advanced(self):
if bpy.context.space_data.edit_tree == self.get_tree():
self.blend_space.draw()
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketArray', 'Actions')
self.add_input('ArmBlendSpaceSocket', 'Cursor X')
self.add_input('ArmBlendSpaceSocket', 'Cursor Y')
self.add_output('LnxNodeSocketAnimTree', 'Out')
def add_advanced_draw(self):
self.advanced_draw_run = True
handler = self.draw_handler_dict.get(str(self.as_pointer()))
if handler is None:
self.create_blend_space()
editor = getattr(bpy.types, 'SpaceNodeEditor')
handler = editor.draw_handler_add(self.draw_advanced, (), 'WINDOW', 'POST_VIEW')
self.draw_handler_dict[str(self.as_pointer())] = handler
self.property2 = False
def remove_advanced_draw(self):
self.advanced_draw_run = False
handler = self.draw_handler_dict.get(str(self.as_pointer()))
if handler is not None:
editor = getattr(bpy.types, 'SpaceNodeEditor')
editor.draw_handler_remove(handler, 'WINDOW')
self.draw_handler_dict.pop(str(self.as_pointer()))
def set_x_y_cursor(self):
self.property0[20] = self.inputs[2].get_default_value()
self.property0[21] = self.inputs[3].get_default_value()
def set_x_y_socket(self):
self.inputs[2].set_default_value(self.property0[20])
self.inputs[3].set_default_value(self.property0[21])
def add_point(self):
for i in range(len(self.property1)):
if not self.property1[i]:
self.property1[i] = True
self.property0[i * 2] = 0.5
self.property0[i * 2 + 1] = 0.5
break
def remove_point(self):
for i in range(len(self.property1) - 2, 2, -1):
if self.property1[i]:
self.property1[i] = False
self.active_point_index_ref = i - 1
break
def draw_buttons(self, context, layout):
row = layout.row(align=True)
row.alignment = 'EXPAND'
op = row.operator('lnx.node_call_func', text='Show', icon='FULLSCREEN_ENTER', emboss=True, depress = self.advanced_draw_run)
op.node_index = str(id(self))
op.callback_name = 'add_advanced_draw'
op = row.operator('lnx.node_call_func', text='Hide', icon='FULLSCREEN_EXIT', emboss=True, depress = not self.advanced_draw_run)
op.node_index = str(id(self))
op.callback_name = 'remove_advanced_draw'
if self.advanced_draw_run:
col = layout.column()
row = col.row(align=True)
op = row.operator('lnx.blend_space_operator', text = 'Edit', icon = 'EDITMODE_HLT', emboss = True, depress = self.property2)
op.node_index = str(id(self))
op = row.operator('lnx.node_call_func', text = 'Exit Edit', icon = 'OBJECT_DATAMODE', emboss = True, depress = not self.property2)
op.node_index = str(id(self))
op.callback_name = 'stop_modal'
layout.prop(self, 'show_numbers')
if self.property2:
col = layout.column()
row = col.row(align=True)
op = row.operator('lnx.node_call_func', text = 'Add Point', icon = 'PLUS', emboss = True)
op.node_index = str(id(self))
op.callback_name = 'add_point'
op = row.operator('lnx.node_call_func', text = 'Remove Point', icon = 'X', emboss = True)
op.node_index = str(id(self))
op.callback_name = 'remove_point'
cl =layout.column()
actie_point = self.active_point_index_ref
pos = ", Pos = " + str(round(self.property0[actie_point * 2], 2)) + ", " + str(round(self.property0[actie_point * 2 + 1], 2))
if actie_point > 9:
cl.label(text = "Selected: Cursor" + pos)
else:
cl.label(text = "Selected: " + str(self.active_point_index_ref + 1) + pos)
else:
self.set_x_y_cursor()

View File

@ -0,0 +1,24 @@
from lnx.logicnode.lnx_nodes import *
class BoneFKNode(LnxLogicTreeNode):
"""Applies forward kinematics in the given object bone."""
bl_idname = 'LNBoneFKNode'
bl_label = 'Bone FK'
lnx_version = 2
lnx_section = 'armature'
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimTree', 'Action Tree')
self.add_input('LnxStringSocket', 'Bone')
self.add_input('LnxDynamicSocket', 'Transform')
self.add_output('LnxNodeSocketAnimTree', 'Action Tree')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement(
'LNBoneFKNode', self.lnx_version, 'LNBoneFKNode', 2,
in_socket_mapping={}, out_socket_mapping={}
)

View File

@ -0,0 +1,77 @@
from lnx.logicnode.lnx_nodes import *
class BoneIKNode(LnxLogicTreeNode):
"""Performs inverse kinematics on the selected armature with specified bone.
@input Object: Armature on which IK should be performed.
@input Bone: Effector or tip bone for the inverse kinematics
@input Goal Position: Position in world coordinates the effector bone will track to
@input Enable Pole: Bend IK solution towards pole location
@input Pole Position: Location of the pole in world coordinates
@input Chain Length: Number of bones to include in the IK solver including the effector. If set to 0, all bones from effector to the root bone of the armature will be considered.
@input Max Iterations: Maximum allowed FABRIK iterations to solve for IK. For longer chains, more iterations are needed.
@input Precision: Presition of IK to stop at. It is described as a tolerence in length. Typically 0.01 is a good value.
@input Roll Angle: Roll the bones along their local axis with specified radians. set 0 for no extra roll.
"""
bl_idname = 'LNBoneIKNode'
bl_label = 'Bone IK'
lnx_version = 3
lnx_section = 'armature'
NUM_STATIC_INS = 9
def update_advanced(self, context):
self.update_sockets(context)
property0: HaxeEnumProperty(
'property0',
items = [('2 Bone', '2 Bone', '2 Bone'),
('FABRIK', 'FABRIK', 'FABRIK')],
name='', default='2 Bone', update=update_advanced)
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimTree', 'Action')
self.add_input('LnxStringSocket', 'Bone')
self.add_input('LnxVectorSocket', 'Goal Position')
self.add_input('LnxBoolSocket', 'Enable Pole')
self.add_input('LnxVectorSocket', 'Pole Position')
self.add_input('LnxFloatSocket', 'Roll Angle')
self.add_input('ArmFactorSocket', 'Influence', default_value = 1.0)
self.add_input('LnxIntSocket', 'Bone Group', default_value = 0)
self.add_output('LnxNodeSocketAnimTree', 'Result')
self.update_sockets(context)
def update_sockets(self, context):
remove_list = []
for i in range(BoneIKNode.NUM_STATIC_INS, len(self.inputs)):
remove_list.append(self.inputs[i])
for i in remove_list:
self.inputs.remove(i)
if self.property0 == 'FABRIK':
self.add_input('LnxIntSocket', 'Chain Length')
self.add_input('LnxIntSocket', 'Max Iterations', 10)
self.add_input('LnxFloatSocket', 'Precision', 0.01)
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1, 2):
raise LookupError()
return NodeReplacement(
'LNBoneIKNode', self.lnx_version, 'LNBoneIKNode', 3,
in_socket_mapping={}, out_socket_mapping={}
)

View File

@ -0,0 +1,27 @@
from lnx.logicnode.lnx_nodes import *
class EvaluateRootMotionNode(LnxLogicTreeNode):
"""Calculates the root motion bone in an armature object."""
bl_idname = 'LNEvaluateRootMotionNode'
bl_label = 'Evaluate Root Motion'
lnx_version = 1
property0: HaxeEnumProperty(
'property0',
items = [('X', 'X', 'X'),
('Y', 'Y', 'Y'),
('Z', 'Z', 'Z')],
name='', default='Y')
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Reset')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimTree', 'Action')
self.add_input('LnxStringSocket', 'Bone')
self.add_output('LnxNodeSocketAnimTree', 'Result')
def draw_buttons(self, context, layout):
layout.label(text='Root Motion Lock axis:')
layout.prop(self, 'property0')

View File

@ -0,0 +1,33 @@
from lnx.logicnode.lnx_nodes import *
class AnimationStateNode(LnxLogicTreeNode):
"""Returns the information about the current action of the given object."""
bl_idname = 'LNAnimationStateNode'
bl_label = 'Get Action State'
lnx_version = 2
property0: HaxeStringProperty('property0', name='', default='')
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxNodeSocketAction', 'On Complete')
self.add_output('LnxBoolSocket', 'Initialized')
self.add_output('LnxStringSocket', 'Action Name')
self.add_output('LnxIntSocket', 'Current Frame')
self.add_output('LnxBoolSocket', 'Is Paused')
self.add_output('LnxFloatSocket', 'Speed')
self.add_output('LnxIntSocket', 'Total Frames')
def draw_buttons(self, context, layout):
layout.label(text='Action ID:')
layout.prop(self, 'property0')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement(
'LNAnimationStateNode', self.lnx_version, 'LNAnimationStateNode', 2,
in_socket_mapping={}, out_socket_mapping={}
)

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class GetBoneFkIkOnlyNode(LnxLogicTreeNode):
"""Get if a particular bone is animated by Forward kinematics or Inverse kinematics only."""
bl_idname = 'LNGetBoneFkIkOnlyNode'
bl_label = 'Get Bone FK IK Only'
lnx_version = 1
lnx_section = 'armature'
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Bone')
self.add_output('LnxBoolSocket', 'FK or IK only')

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class GetBoneTransformNode(LnxLogicTreeNode):
"""Returns bone transform in world space."""
bl_idname = 'LNGetBoneTransformNode'
bl_label = 'Get Bone Transform'
lnx_version = 1
lnx_section = 'armature'
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Bone')
self.add_output('LnxDynamicSocket', 'Transform')

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class GetRootMotionNode(LnxLogicTreeNode):
"""Gets root motion of an armature object"""
bl_idname = 'LNGetRootMotionNode'
bl_label = 'Get Root Motion'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxStringSocket', 'Bone')
self.add_output('LnxVectorSocket', 'Velocity')
self.add_output('LnxRotationSocket', 'Rotation')

View File

@ -0,0 +1,37 @@
from lnx.logicnode.lnx_nodes import *
class GetTilesheetStateNode(LnxLogicTreeNode):
"""Returns the information about the current tilesheet of the given object.
@output Active Tilesheet: Current active tilesheet.
@output Active Action: Current action in the tilesheet.
@output Frame: Frame offset with 0 as the first frame of the active action.
@output Absolute Frame: Absolute frame index in this tilesheet.
@output Is Paused: Tilesheet action paused.
"""
bl_idname = 'LNGetTilesheetStateNode'
bl_label = 'Get Tilesheet State'
lnx_version = 2
lnx_section = 'tilesheet'
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxStringSocket', 'Active Tilesheet')
self.add_output('LnxStringSocket', 'Active Action')
self.add_output('LnxIntSocket', 'Frame')
self.add_output('LnxIntSocket', 'Absolute Frame')
self.add_output('LnxBoolSocket', 'Is Paused')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement(
'LNGetTilesheetStateNode', self.lnx_version, 'LNGetTilesheetStateNode', 2,
in_socket_mapping={}, out_socket_mapping={0:1, 1:3, 2:4}
)

View File

@ -0,0 +1,29 @@
from lnx.logicnode.lnx_nodes import *
class OnActionMarkerNode(LnxLogicTreeNode):
"""Activates the output when the object action reaches the action marker."""
bl_idname = 'LNOnActionMarkerNode'
bl_label = 'On Action Marker'
bl_width_default = 250
lnx_version = 2
property0: HaxeStringProperty('property0', name='Action ID', default='')
property1: HaxeStringProperty('property1', name='Marker', default='')
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_output('LnxNodeSocketAction', 'Out')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
layout.prop(self, 'property1')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement(
'LNOnActionMarkerNode', self.lnx_version, 'LNOnActionMarkerNode', 2,
in_socket_mapping={}, out_socket_mapping={}
)

View File

@ -0,0 +1,12 @@
from lnx.logicnode.lnx_nodes import *
class OnAnimationTreeUpdateNode(LnxLogicTreeNode):
"""Execute output when animation tree is updated"""
bl_idname = 'LNOnAnimationTreeUpdateNode'
bl_label = 'On Animation Tree Update'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAnimTree', 'Action Tree')
self.add_output('LnxNodeSocketAnimTree', 'Action Tree')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,28 @@
from lnx.logicnode.lnx_nodes import *
class OneShotActionNode(LnxLogicTreeNode):
"""Introduce one loop of animation in the current tree."""
bl_idname = 'LNOneShotActionNode'
bl_label = 'One Shot Action'
bl_width_default = 250
lnx_version = 1
property0: HaxeStringProperty('property0', name = 'Action ID', default = '')
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Start')
self.add_input('LnxNodeSocketAction', 'Stop')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimTree', 'Main Action')
self.add_input('LnxNodeSocketAnimAction', 'One Shot')
self.add_input('LnxBoolSocket', 'Restart', default_value = True)
self.add_input('LnxFloatSocket', 'Blend In Time', default_value = 1.0)
self.add_input('LnxFloatSocket', 'Blend Out Time', default_value = 1.0)
self.add_input('LnxIntSocket', 'Bone Group', default_value = -1)
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketAction', 'Done')
self.add_output('LnxNodeSocketAnimTree', 'Result')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,45 @@
from lnx.logicnode.lnx_nodes import *
class OneShotActionMultiNode(LnxLogicTreeNode):
"""Introduce one loop of animation in the current tree using the selected action."""
bl_idname = 'LNOneShotActionMultiNode'
bl_label = 'One Shot Action Multi'
bl_width_default = 250
lnx_version = 1
min_inputs = 10
def __init__(self):
super(OneShotActionMultiNode, self).__init__()
array_nodes[self.get_id_str()] = self
property0: HaxeStringProperty('property0', name = 'Action ID', default = '')
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Start')
self.add_input('LnxIntSocket', 'Index', default_value = 0)
self.add_input('LnxNodeSocketAction', 'Stop')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxBoolSocket', 'Restart', default_value = True)
self.add_input('LnxFloatSocket', 'Blend In Time', default_value = 1.0)
self.add_input('LnxFloatSocket', 'Blend Out Time', default_value = 1.0)
self.add_input('LnxIntSocket', 'Bone Group', default_value = -1)
self.add_input('LnxNodeSocketAnimTree', 'Main Action')
self.add_input('LnxNodeSocketAnimAction', 'Action 0')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketAction', 'Done')
self.add_output('LnxNodeSocketAnimTree', 'Result')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
row = layout.row(align=True)
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
op.node_index = self.get_id_str()
op.socket_type = 'LnxNodeSocketAnimAction'
op.name_format = 'Action {0}'
op.index_name_offset = -9
column = row.column(align=True)
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
op.node_index = self.get_id_str()
if len(self.inputs) == self.min_inputs:
column.enabled = False

View File

@ -0,0 +1,51 @@
from lnx.logicnode.lnx_nodes import *
class PlayActionFromNode(LnxLogicTreeNode):
"""
Plays animation action, that starts from given frame (0 is the first), and ends at given frame (-1 for last frame).
@input In: Activates the node logic.
@input Object: States object/armature to run the animation action on.
@input Action: States animation action to be played.
@input Start Frame: Sets frame the animation should start at from 0.
@input End Frame: Sets frame the animation should end at. HINT: Set to "-1" if you want the total frames length of the animation.
@input Blend: Sets rate to blend multiple animations together.
@input Speed: Sets rate the animation plays at.
@input Loop: Sets whether the animation should rewind itself after finishing.
@output Out: Executes whenever the node is run.
@output Done: Executes whenever the played animation is finished. (Only triggers if looping is false.)
"""
bl_idname = 'LNPlayActionFromNode'
bl_label = 'Play Action From'
lnx_version = 3
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimAction', 'Action')
self.add_input('LnxIntSocket', 'Start Frame')
self.add_input('LnxIntSocket', 'End Frame')
self.add_input('LnxFloatSocket', 'Blend', default_value = 0.25)
self.add_input('LnxFloatSocket', 'Speed', default_value = 1.0)
self.add_input('LnxBoolSocket', 'Loop', default_value = False)
self.add_input('LnxBoolSocket', 'Reverse', default_value = False)
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketAction', 'Done')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version in (0, 1):
return NodeReplacement(
'LNPlayActionFromNode', self.lnx_version, 'LNPlayActionFromNode', 2,
in_socket_mapping={0:0, 1:1, 2:2, 3:3, 4:4}, out_socket_mapping={0:0, 1:1}
)
if self.lnx_version == 2:
return NodeReplacement(
'LNPlayActionFromNode', self.lnx_version, 'LNPlayActionFromNode', 3,
in_socket_mapping={0:0, 1:1, 2:2, 3:3, 4:5, 5:6, 6:7}, out_socket_mapping={0:0, 1:1}
)
raise LookupError()

View File

@ -0,0 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class PlayAnimationTreeNode(LnxLogicTreeNode):
"""Plays a given animation tree."""
bl_idname = 'LNPlayAnimationTreeNode'
bl_label = 'Play Animation Tree'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimTree', 'Action Tree')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class PlayTilesheetNode(LnxLogicTreeNode):
"""Plays the given tilesheet action."""
bl_idname = 'LNPlayTilesheetNode'
bl_label = 'Play Tilesheet'
lnx_version = 1
lnx_section = 'tilesheet'
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Name')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketAction', 'Done')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class RemoveParentBoneNode(LnxLogicTreeNode):
"""Removes the given object parent to the given bone."""
bl_idname = 'LNRemoveParentBoneNode'
bl_label = 'Remove Parent Bone'
lnx_version = 1
lnx_section = 'armature'
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketObject', 'Parent')
self.add_input('LnxStringSocket', 'Bone', default_value='Bone')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,24 @@
from lnx.logicnode.lnx_nodes import *
class RestartActionNode(LnxLogicTreeNode):
"""Restarts the action"""
bl_idname = 'LNRestartActionNode'
bl_label = 'Restart Action'
bl_width_default = 200
lnx_version = 2
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Action ID')
self.add_output('LnxNodeSocketAction', 'Out')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement(
'LNRestartActionNode', self.lnx_version, 'LNRestartActionNode', 2,
in_socket_mapping={}, out_socket_mapping={}
)

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class SetActionFrameNode(LnxLogicTreeNode):
"""Sets the current action frame for the given object."""
bl_idname = 'LNSetActionFrameNode'
bl_label = 'Set Action Frame'
bl_width_default = 200
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Action ID')
self.add_input('LnxIntSocket', 'Frame')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,25 @@
from lnx.logicnode.lnx_nodes import *
class SetActionPausedNode(LnxLogicTreeNode):
"""Sets the action paused state of the given object."""
bl_idname = 'LNSetActionPausedNode'
bl_label = 'Set Action Paused'
bl_width_default = 200
lnx_version = 2
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Action ID')
self.add_input('LnxBoolSocket', 'Paused')
self.add_output('LnxNodeSocketAction', 'Out')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement(
'LNSetActionPausedNode', self.lnx_version, 'LNSetActionPausedNode', 2,
in_socket_mapping={}, out_socket_mapping={}
)

View File

@ -0,0 +1,25 @@
from lnx.logicnode.lnx_nodes import *
class SetActionSpeedNode(LnxLogicTreeNode):
"""Sets the current action playback speed of the given object."""
bl_idname = 'LNSetActionSpeedNode'
bl_label = 'Set Action Speed'
bl_width_default = 200
lnx_version = 2
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Action ID')
self.add_input('LnxFloatSocket', 'Speed', default_value=1.0)
self.add_output('LnxNodeSocketAction', 'Out')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement(
'LNSetActionSpeedNode', self.lnx_version, 'LNSetActionSpeedNode', 2,
in_socket_mapping={}, out_socket_mapping={}
)

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class SetActionTimeNode(LnxLogicTreeNode):
"""Sets the current action time for the given object."""
bl_idname = 'LNSetActionTimeNode'
bl_label = 'Set Action Time'
bl_width_default = 200
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Action ID')
self.add_input('LnxFloatSocket', 'Time')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class SetActiveTilesheetNode(LnxLogicTreeNode):
"""Set the active tilesheet."""
bl_idname = 'LNSetActiveTilesheetNode'
bl_label = 'Set Active Tilesheet'
lnx_version = 1
lnx_section = 'tilesheet'
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Tilesheet')
self.add_input('LnxStringSocket', 'Action')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class SetBoneFkIkOnlyNode(LnxLogicTreeNode):
"""Set particular bone to be animated by Forward kinematics or Inverse kinematics only. All other animations will be ignored"""
bl_idname = 'LNSetBoneFkIkOnlyNode'
bl_label = 'Set Bone FK IK Only'
lnx_version = 1
lnx_section = 'armature'
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'Bone')
self.add_input('LnxBoolSocket', 'FK or IK only')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class SetParentBoneNode(LnxLogicTreeNode):
"""Sets the given object parent to the given bone."""
bl_idname = 'LNSetParentBoneNode'
bl_label = 'Set Parent Bone'
lnx_version = 1
lnx_section = 'armature'
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketObject', 'Parent')
self.add_input('LnxStringSocket', 'Bone', default_value='Bone')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,14 @@
from lnx.logicnode.lnx_nodes import *
class SetParticleSpeedNode(LnxLogicTreeNode):
"""Sets the speed of the given particle source."""
bl_idname = 'LNSetParticleSpeedNode'
bl_label = 'Set Particle Speed'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxFloatSocket', 'Speed', default_value=1.0)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,17 @@
from lnx.logicnode.lnx_nodes import *
class SetTilesheetFrame(LnxLogicTreeNode):
"""Set the frame of the current tilesheet action.
@input Frame: Frame offset to set with 0 as the first frame of the active action.
"""
bl_idname = 'LNSetTilesheetFrameNode'
bl_label = 'Set Tilesheet Frame'
lnx_version = 1
lnx_section = 'tilesheet'
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxIntSocket', 'Frame')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,15 @@
from lnx.logicnode.lnx_nodes import *
class SetTilesheetPausedNode(LnxLogicTreeNode):
"""Sets the tilesheet paused state of the given object."""
bl_idname = 'LNSetTilesheetPausedNode'
bl_label = 'Set Tilesheet Paused'
lnx_section = 'tilesheet'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxBoolSocket', 'Paused')
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,54 @@
from lnx.logicnode.lnx_nodes import *
class SimpleFootIKNode(LnxLogicTreeNode):
"""Performs inverse kinematics on the selected armature with specified bone.
@input Object: Armature on which IK should be performed.
@input Bone: Effector or tip bone for the inverse kinematics
@input Goal Position: Position in world coordinates the effector bone will track to
@input Enable Pole: Bend IK solution towards pole location
@input Pole Position: Location of the pole in world coordinates
@input Chain Length: Number of bones to include in the IK solver including the effector. If set to 0, all bones from effector to the root bone of the armature will be considered.
@input Max Iterations: Maximum allowed FABRIK iterations to solve for IK. For longer chains, more iterations are needed.
@input Precision: Presition of IK to stop at. It is described as a tolerence in length. Typically 0.01 is a good value.
@input Roll Angle: Roll the bones along their local axis with specified radians. set 0 for no extra roll.
"""
bl_idname = 'LNSimpleFootIKNode'
bl_label = 'Foot IK Node'
lnx_version = 1
lnx_section = 'armature'
property0: HaxeStringProperty('property0', name = '', default = '')
property1: HaxeStringProperty('property1', name = '', default = '')
def draw_buttons(self, context, layout):
layout.label(text='Left Foot Name:')
layout.prop(self, 'property0')
layout.label(text='Right Foot Name:')
layout.prop(self, 'property1')
def lnx_init(self, context):
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimTree', 'Action')
self.add_input('LnxFloatSocket', 'Scan Height', default_value = 1.0)
self.add_input('LnxFloatSocket', 'Scan Depth', default_value = 1.0)
self.add_input('LnxIntSocket', 'Collision Mask', default_value = 1)
self.add_input('LnxFloatSocket', 'Height Offset', default_value = 0.0)
self.add_input('LnxFloatSocket', 'Foot Offset', default_value = 0.0)
self.add_input('LnxFloatSocket', 'Offset Threshold', default_value = 1.0)
self.add_input('LnxFloatSocket', 'Interp Speed', default_value = 0.1)
self.add_input('LnxIntSocket', 'Bone Group', default_value = -1)
self.add_input('LnxFloatSocket', 'Influence', default_value = 1.0)
self.add_input('LnxBoolSocket', 'Use Pole Targets', default_value = False)
self.add_input('LnxBoolSocket', 'Rotate Foot', default_value = False)
self.add_input('LnxNodeSocketArray', 'Pole And Direction Array')
self.add_output('LnxNodeSocketAnimTree', 'Result')

View File

@ -0,0 +1,20 @@
from lnx.logicnode.lnx_nodes import *
class SwitchActionNode(LnxLogicTreeNode):
"""Switch between the two given actions with interpolation."""
bl_idname = 'LNSwitchActionNode'
bl_label = 'Switch Action'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Action 1')
self.add_input('LnxNodeSocketAction', 'Action 2')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxNodeSocketAnimTree', 'Action 1')
self.add_input('LnxNodeSocketAnimTree', 'Action 2')
self.add_input('LnxBoolSocket', 'Restart', default_value = True)
self.add_input('LnxFloatSocket', 'Time', default_value = 1.0)
self.add_input('LnxIntSocket', 'Bone Group', default_value = -1)
self.add_output('LnxNodeSocketAction', 'Done')
self.add_output('LnxNodeSocketAnimTree', 'Result')

View File

@ -0,0 +1,38 @@
from lnx.logicnode.lnx_nodes import *
class SwitchActionMultiNode(LnxLogicTreeNode):
"""Switch between the given actions with interpolation."""
bl_idname = 'LNSwitchActionMultiNode'
bl_label = 'Switch Action Multi'
lnx_version = 1
min_inputs = 8
def __init__(self):
super(SwitchActionMultiNode, self).__init__()
array_nodes[self.get_id_str()] = self
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'Switch')
self.add_input('LnxIntSocket', 'Switch To', default_value = 1)
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxBoolSocket', 'Restart', default_value = True)
self.add_input('LnxFloatSocket', 'Time', default_value = 1.0)
self.add_input('LnxIntSocket', 'Bone Group', default_value = -1)
self.add_input('LnxNodeSocketAnimTree', 'Action 0')
self.add_input('LnxNodeSocketAnimTree', 'Action 1')
self.add_output('LnxNodeSocketAction', 'Done')
self.add_output('LnxNodeSocketAnimTree', 'Result')
def draw_buttons(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
op.node_index = self.get_id_str()
op.socket_type = 'LnxNodeSocketAnimTree'
op.name_format = 'Action {0}'
op.index_name_offset = -6
column = row.column(align=True)
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
op.node_index = self.get_id_str()
if len(self.inputs) == self.min_inputs:
column.enabled = False

View File

@ -0,0 +1,21 @@
from lnx.logicnode.lnx_nodes import *
class SyncActionsNode(LnxLogicTreeNode):
"""Sync two actions when blending from one to the other such that Frame A and B in both actions align."""
bl_idname = 'LNSyncActionNode'
bl_label = 'Sync Action'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketObject', 'Object')
self.add_input('LnxStringSocket', 'From ID')
self.add_input('LnxIntSocket', 'Frame A', default_value=0)
self.add_input('LnxIntSocket', 'Frame B', default_value=-1)
self.add_input('LnxBoolSocket', 'Reset Speed', default_value=True)
self.add_input('LnxStringSocket', 'To ID')
self.add_input('LnxIntSocket', 'Frame A', default_value=0)
self.add_input('LnxIntSocket', 'Frame B', default_value=-1)
self.add_output('LnxNodeSocketAction', 'Out')

View File

@ -0,0 +1,5 @@
from lnx.logicnode.lnx_nodes import add_node_section
add_node_section(name='default', category='Animation')
add_node_section(name='tilesheet', category='Animation')
add_node_section(name='armature', category='Animation')

View File

@ -0,0 +1,50 @@
from lnx.logicnode.lnx_nodes import *
class ArrayNode(LnxLogicVariableNodeMixin, LnxLogicTreeNode):
"""Stores the given array as a variable."""
bl_idname = 'LNArrayNode'
bl_label = 'Array Dynamic'
lnx_version = 3
lnx_section = 'variable'
min_inputs = 0
def __init__(self):
self.register_id()
def lnx_init(self, context):
self.add_output('LnxNodeSocketArray', 'Array', is_var=True)
self.add_output('LnxIntSocket', 'Length')
def draw_content(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
op.node_index = self.get_id_str()
op.socket_type = 'LnxDynamicSocket'
column = row.column(align=True)
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
op.node_index = self.get_id_str()
if len(self.inputs) == self.min_inputs:
column.enabled = False
def draw_label(self) -> str:
if len(self.inputs) == self.min_inputs:
return super().draw_label()
return f'{super().draw_label()} [{len(self.inputs)}]'
def synchronize_from_master(self, master_node: LnxLogicVariableNodeMixin):
self.inputs.clear()
for i in range(len(master_node.inputs)):
inp = self.add_input('LnxDynamicSocket', master_node.inputs[i].name)
inp.hide = self.lnx_logic_id != ''
inp.enabled = self.lnx_logic_id == ''
inp.default_value_raw = master_node.inputs[i].get_default_value()
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 2):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -0,0 +1,46 @@
from lnx.logicnode.lnx_nodes import *
class ArrayAddNode(LnxLogicTreeNode):
"""Adds the given value to the given array.
@input Array: the array to manipulate.
@input Modify Original: if `false`, the input array is copied before adding the value.
@input Unique Values: if `true`, values may occur only once in that array (only primitive data types are supported).
"""
bl_idname = 'LNArrayAddNode'
bl_label = 'Array Add'
lnx_version = 5
min_inputs = 6
def __init__(self):
super(ArrayAddNode, self).__init__()
array_nodes[self.get_id_str()] = self
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxBoolSocket', 'Modify Original', default_value=True)
self.add_input('LnxBoolSocket', 'Unique Values')
self.add_input('LnxBoolSocket', 'Add First')
self.add_input('LnxDynamicSocket', 'Value')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketArray', 'Array')
def draw_buttons(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_add_input_value', text='Add Input', icon='PLUS', emboss=True)
op.node_index = self.get_id_str()
op.socket_type = 'LnxDynamicSocket'
column = row.column(align=True)
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
op.node_index = self.get_id_str()
if len(self.inputs) == self.min_inputs:
column.enabled = False
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 4):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -0,0 +1,50 @@
from lnx.logicnode.lnx_nodes import *
class BooleanArrayNode(LnxLogicVariableNodeMixin, LnxLogicTreeNode):
"""Stores an array of boolean elements as a variable."""
bl_idname = 'LNArrayBooleanNode'
bl_label = 'Array Boolean'
lnx_version = 3
lnx_section = 'variable'
min_inputs = 0
def __init__(self):
super(BooleanArrayNode, self).__init__()
self.register_id()
def lnx_init(self, context):
self.add_output('LnxNodeSocketArray', 'Array', is_var=True)
self.add_output('LnxIntSocket', 'Length')
def draw_content(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
op.node_index = self.get_id_str()
op.socket_type = 'LnxBoolSocket'
column = row.column(align=True)
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
op.node_index = self.get_id_str()
if len(self.inputs) == self.min_inputs:
column.enabled = False
def draw_label(self) -> str:
if len(self.inputs) == self.min_inputs:
return super().draw_label()
return f'{super().draw_label()} [{len(self.inputs)}]'
def synchronize_from_master(self, master_node: LnxLogicVariableNodeMixin):
self.inputs.clear()
for i in range(len(master_node.inputs)):
inp = self.add_input('LnxBoolSocket', master_node.inputs[i].name)
inp.hide = self.lnx_logic_id != ''
inp.enabled = self.lnx_logic_id == ''
inp.default_value_raw = master_node.inputs[i].get_default_value()
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 2):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -0,0 +1,50 @@
from lnx.logicnode.lnx_nodes import *
class ColorArrayNode(LnxLogicVariableNodeMixin, LnxLogicTreeNode):
"""Stores an array of color elements as a variable."""
bl_idname = 'LNArrayColorNode'
bl_label = 'Array Color'
lnx_version = 3
lnx_section = 'variable'
min_inputs = 0
def __init__(self):
super(ColorArrayNode, self).__init__()
self.register_id()
def lnx_init(self, context):
self.add_output('LnxNodeSocketArray', 'Array', is_var=True)
self.add_output('LnxIntSocket', 'Length')
def draw_content(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
op.node_index = self.get_id_str()
op.socket_type = 'LnxColorSocket'
column = row.column(align=True)
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
op.node_index = self.get_id_str()
if len(self.inputs) == self.min_inputs:
column.enabled = False
def draw_label(self) -> str:
if len(self.inputs) == self.min_inputs:
return super().draw_label()
return f'{super().draw_label()} [{len(self.inputs)}]'
def synchronize_from_master(self, master_node: LnxLogicVariableNodeMixin):
self.inputs.clear()
for i in range(len(master_node.inputs)):
inp = self.add_input('LnxColorSocket', master_node.inputs[i].name)
inp.hide = self.lnx_logic_id != ''
inp.enabled = self.lnx_logic_id == ''
inp.default_value_raw = master_node.inputs[i].get_default_value()
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 2):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -0,0 +1,15 @@
from lnx.logicnode.lnx_nodes import *
class ArrayCompareNode(LnxLogicTreeNode):
"""Compare arrays."""
bl_idname = 'LNArrayCompareNode'
bl_label = 'Array Compare'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxNodeSocketArray', 'Array')
self.add_output('LnxBoolSocket', 'Compare')

View File

@ -0,0 +1,15 @@
from lnx.logicnode.lnx_nodes import *
class ArrayConcatNode(LnxLogicTreeNode):
"""Join arrays."""
bl_idname = 'LNArrayConcatNode'
bl_label = 'Array Concat'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxNodeSocketArray', 'Array')
self.add_output('LnxNodeSocketArray', 'Array')
self.add_output('LnxIntSocket', 'Length')

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class ArrayContainsNode(LnxLogicTreeNode):
"""Returns whether the given array contains the given value."""
bl_idname = 'LNArrayInArrayNode'
bl_label = 'Array Contains'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxDynamicSocket', 'Value')
self.add_output('LnxBoolSocket', 'Contains')

View File

@ -0,0 +1,12 @@
from lnx.logicnode.lnx_nodes import *
class ArrayCountNode(LnxLogicTreeNode):
"""Returns an array with the item counts of the given array."""
bl_idname = 'LNArrayCountNode'
bl_label = 'Array Count'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_output('LnxNodeSocketArray', 'Count')

View File

@ -0,0 +1,31 @@
from lnx.logicnode.lnx_nodes import *
class ArrayDisplayNode(LnxLogicTreeNode):
"""Returns the display of the given array."""
bl_idname = 'LNArrayDisplayNode'
bl_label = 'Array Display'
lnx_version = 1
def remove_extra_inputs(self, context):
while len(self.inputs) > 2:
self.inputs.remove(self.inputs[-1])
if self.property0 == 'Item Field':
self.add_input('LnxStringSocket', 'Item Field')
if self.property0 == 'Item Property':
self.add_input('LnxStringSocket', 'Item Property')
property0: HaxeEnumProperty(
'property0',
items = [('Item', 'Item', 'Array Item'),
('Item Field', 'Item Field', 'Object Item Field, ie: name, uid, visible, parent, length, etc.'),
('Item Property', 'Item Property', 'Object Item Property')],
name='', default='Item', update=remove_extra_inputs)
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxStringSocket', 'Separator')
self.add_output('LnxStringSocket', 'Items')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class ArrayDistinctNode(LnxLogicTreeNode):
"""Returns the Distinct and Duplicated items of the given array."""
bl_idname = 'LNArrayDistinctNode'
bl_label = 'Array Distinct'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_output('LnxNodeSocketArray', 'Distinct')
self.add_output('LnxNodeSocketArray', 'Duplicated')

View File

@ -0,0 +1,49 @@
from lnx.logicnode.lnx_nodes import *
class ArrayFilterNode(LnxLogicTreeNode):
"""Returns an array with the filtered items of the given array."""
bl_idname = 'LNArrayFilterNode'
bl_label = 'Array Filter'
lnx_version = 1
def remove_extra_inputs(self, context):
while len(self.inputs) > 1:
self.inputs.remove(self.inputs[-1])
if self.property0 == 'Item Field':
self.add_input('LnxStringSocket', 'Item Field')
if self.property0 == 'Item Property':
self.add_input('LnxStringSocket', 'Item Property')
self.add_input('LnxDynamicSocket', 'Value')
if self.property1 == 'Between':
self.add_input('LnxDynamicSocket', 'Value')
property0: HaxeEnumProperty(
'property0',
items = [('Item', 'Item', 'Array Item'),
('Item Field', 'Item Field', 'Object Item Field, ie: Name, Uid, Visible, Parent, Length, etc.'),
('Item Property', 'Item Property', 'Object Item Property')],
name='', default='Item', update=remove_extra_inputs)
property1: HaxeEnumProperty(
'property1',
items = [('Equal', 'Equal', 'Equal'),
('Not Equal', 'Not Equal', 'Not Equal'),
('Greater', 'Greater', 'Greater'),
('Greater Equal', 'Greater Equal', 'Greater Equal'),
('Less', 'Less', 'Less'),
('Less Equal', 'Less Equal', 'Less Equal'),
('Between', 'Between', 'Input 1 Between Input 2 and Input 3 inclusive'),
('Contains', 'Contains', 'Contains'),
('Starts With', 'Starts With', 'Starts With'),
('Ends With', 'Ends With', 'Ends With')],
name='', default='Equal', update=remove_extra_inputs)
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxDynamicSocket', 'Value')
self.add_output('LnxNodeSocketArray', 'Array')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
layout.prop(self, 'property1')

View File

@ -0,0 +1,61 @@
from lnx.logicnode.lnx_nodes import *
class FloatArrayNode(LnxLogicVariableNodeMixin, LnxLogicTreeNode):
"""Stores an array of float elements as a variable."""
bl_idname = 'LNArrayFloatNode'
bl_label = 'Array Float'
lnx_version = 3
lnx_section = 'variable'
min_inputs = 0
def __init__(self):
super(FloatArrayNode, self).__init__()
self.register_id()
def lnx_init(self, context):
self.add_output('LnxNodeSocketArray', 'Array', is_var=True)
self.add_output('LnxIntSocket', 'Length')
def draw_content(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
op.node_index = self.get_id_str()
op.socket_type = 'LnxFloatSocket'
column = row.column(align=True)
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
op.node_index = self.get_id_str()
if len(self.inputs) == self.min_inputs:
column.enabled = False
def draw_label(self) -> str:
if len(self.inputs) == self.min_inputs:
return super().draw_label()
return f'{super().draw_label()} [{len(self.inputs)}]'
def synchronize_from_master(self, master_node: LnxLogicVariableNodeMixin):
self.inputs.clear()
for i in range(len(master_node.inputs)):
inp = self.add_input('LnxFloatSocket', master_node.inputs[i].name)
inp.hide = self.lnx_logic_id != ''
inp.enabled = self.lnx_logic_id == ''
inp.default_value_raw = master_node.inputs[i].get_default_value()
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version < 0 or self.lnx_version > 2:
raise LookupError()
newnode = node_tree.nodes.new(FloatArrayNode.bl_idname)
for inp_old in self.inputs:
inp_new = newnode.add_input('LnxFloatSocket', inp_old.name)
inp_new.hide = self.lnx_logic_id != ''
inp_new.enabled = self.lnx_logic_id != ''
inp_new.default_value_raw = inp_old.get_default_value()
NodeReplacement.replace_input_socket(node_tree, inp_old, inp_new)
NodeReplacement.replace_output_socket(node_tree, self.outputs[0], newnode.outputs[0])
NodeReplacement.replace_output_socket(node_tree, self.outputs[1], newnode.outputs[1])
return newnode

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class ArrayGetNode(LnxLogicTreeNode):
"""Returns the value of the given array at the given index."""
bl_idname = 'LNArrayGetNode'
bl_label = 'Array Get'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxIntSocket', 'Index')
self.add_output('LnxDynamicSocket', 'Value')

View File

@ -0,0 +1,13 @@
from lnx.logicnode.lnx_nodes import *
class ArrayGetPreviousNextNode(LnxLogicTreeNode):
"""Returns the previous or next value to be retrieve by looping the array according to the boolean condition."""
bl_idname = 'LNArrayGetPreviousNextNode'
bl_label = 'Array Get Previous/Next'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxBoolSocket', 'Previous: 0 / Next: 1')
self.add_output('LnxDynamicSocket', 'Value')

View File

@ -0,0 +1,12 @@
from lnx.logicnode.lnx_nodes import *
class ArrayGetNextNode(LnxLogicTreeNode):
"""Returns the next value to be retrieve by looping the array."""
bl_idname = 'LNArrayGetNextNode'
bl_label = 'Array Get Next'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_output('LnxDynamicSocket', 'Value')

View File

@ -0,0 +1,20 @@
from lnx.logicnode.lnx_nodes import *
class ArrayIndexNode(LnxLogicTreeNode):
"""Returns the array index of the given value."""
bl_idname = 'LNArrayIndexNode'
bl_label = 'Array Index'
lnx_version = 2
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxDynamicSocket', 'Value')
self.add_input('LnxIntSocket', 'From')
self.add_output('LnxIntSocket', 'Index')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 1):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -0,0 +1,16 @@
from lnx.logicnode.lnx_nodes import *
class ArrayInsertNode(LnxLogicTreeNode):
"""Inserts the value of the given array at the given index and increases the length of the array."""
bl_idname = 'LNArrayInsertNode'
bl_label = 'Array Insert'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketAction', 'In')
self.add_input('LnxNodeSocketArray', 'Array')
self.add_input('LnxIntSocket', 'Index')
self.add_input('LnxDynamicSocket', 'Value')
self.add_output('LnxNodeSocketAction', 'Out')
self.add_output('LnxNodeSocketArray', 'Array')

View File

@ -0,0 +1,50 @@
from lnx.logicnode.lnx_nodes import *
class IntegerArrayNode(LnxLogicVariableNodeMixin, LnxLogicTreeNode):
"""Stores an array of integer elements as a variable."""
bl_idname = 'LNArrayIntegerNode'
bl_label = 'Array Integer'
lnx_version = 4
lnx_section = 'variable'
min_inputs = 0
def __init__(self):
super(IntegerArrayNode, self).__init__()
self.register_id()
def lnx_init(self, context):
self.add_output('LnxNodeSocketArray', 'Array', is_var=True)
self.add_output('LnxIntSocket', 'Length')
def draw_content(self, context, layout):
row = layout.row(align=True)
op = row.operator('lnx.node_add_input', text='New', icon='PLUS', emboss=True)
op.node_index = self.get_id_str()
op.socket_type = 'LnxIntSocket'
column = row.column(align=True)
op = column.operator('lnx.node_remove_input', text='', icon='X', emboss=True)
op.node_index = self.get_id_str()
if len(self.inputs) == self.min_inputs:
column.enabled = False
def draw_label(self) -> str:
if len(self.inputs) == self.min_inputs:
return super().draw_label()
return f'{super().draw_label()} [{len(self.inputs)}]'
def synchronize_from_master(self, master_node: LnxLogicVariableNodeMixin):
self.inputs.clear()
for i in range(len(master_node.inputs)):
inp = self.add_input('LnxIntSocket', master_node.inputs[i].name)
inp.hide = self.lnx_logic_id != ''
inp.enabled = self.lnx_logic_id == ''
inp.default_value_raw = master_node.inputs[i].get_default_value()
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.lnx_version not in (0, 3):
raise LookupError()
return NodeReplacement.Identity(self)

View File

@ -0,0 +1,12 @@
from lnx.logicnode.lnx_nodes import *
class ArrayLengthNode(LnxLogicTreeNode):
"""Returns the length of the given array."""
bl_idname = 'LNArrayLengthNode'
bl_label = 'Array Length'
lnx_version = 1
def lnx_init(self, context):
self.add_input('LnxNodeSocketArray', 'Array')
self.add_output('LnxIntSocket', 'Length')

Some files were not shown because too many files have changed in this diff Show More