LNXSDK/leenkx/blender/lnx/logicnode/lnx_sockets.py

773 lines
27 KiB
Python
Raw Normal View History

from math import radians, pi, cos, sin, sqrt
2025-01-22 16:18:30 +01:00
from typing import Type
import bpy
from bpy.props import *
from bpy.types import NodeSocket
import mathutils
import lnx.node_utils
import lnx.utils
if lnx.is_reload(__name__):
lnx.node_utils = lnx.reload_module(lnx.node_utils)
lnx.utils = lnx.reload_module(lnx.utils)
else:
lnx.enable_reload(__name__)
# See Blender sources: /source/blender/editors/space_node/drawnode.cc
# Permalink for 3.2.2: https://github.com/blender/blender/blob/bcfdb14560e77891d674c2701a5071a7c07baba3/source/blender/editors/space_node/drawnode.cc#L1152-L1167
socket_colors = {
'LnxNodeSocketAction': (0.8, 0.3, 0.3, 1),
'LnxNodeSocketAnimAction': (0.8, 0.8, 0.8, 1),
'LnxRotationSocket': (0.68, 0.22, 0.62, 1),
'LnxNodeSocketArray': (0.8, 0.4, 0.0, 1),
'LnxBoolSocket': (0.80, 0.65, 0.84, 1.0),
'LnxColorSocket': (0.78, 0.78, 0.16, 1.0),
'LnxDynamicSocket': (0.39, 0.78, 0.39, 1.0),
'LnxFloatSocket': (0.63, 0.63, 0.63, 1.0),
'LnxIntSocket': (0.059, 0.522, 0.149, 1),
'LnxNodeSocketObject': (0.15, 0.55, 0.75, 1),
'LnxStringSocket': (0.44, 0.70, 1.00, 1.0),
'LnxVectorSocket': (0.39, 0.39, 0.78, 1.0),
'LnxAnySocket': (0.9, 0.9, 0.9, 1),
'LnxNodeSocketAnimTree': (0.3, 0.1, 0.0, 1.0),
'LnxFactorSocket': (0.631, 0.631, 0.631, 1.0),
'LnxBlendSpaceSocket': (0.631, 0.631, 0.631, 1.0)
2025-01-22 16:18:30 +01:00
}
def _on_update_socket(self, context):
self.node.on_socket_val_update(context, self)
class LnxCustomSocket(NodeSocket):
"""
A custom socket that can be used to define more socket types for
logic node packs. Do not use this type directly (it is not
registered)!
"""
bl_idname = 'LnxCustomSocket'
bl_label = 'Custom Socket'
# note: trying to use the `type` property will fail. All custom nodes will have "VALUE" as a type, because it is the default.
lnx_socket_type = 'NONE'
# please also declare a property named "default_value_raw" of lnx_socket_type isn't "NONE"
def get_default_value(self):
"""Override this for values of unconnected input sockets."""
return None
def on_node_update(self):
"""Called when the update() method of the corresponding node is called."""
pass
def copy_defaults(self, socket):
"""Called when this socket default values are to be copied to the given socket"""
pass
class LnxActionSocket(LnxCustomSocket):
bl_idname = 'LnxNodeSocketAction'
bl_label = 'Action Socket'
lnx_socket_type = 'NONE'
def draw(self, context, layout, node, text):
layout.label(text=self.name)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
class LnxAnimActionSocket(LnxCustomSocket):
bl_idname = 'LnxNodeSocketAnimAction'
bl_label = 'Action Socket'
lnx_socket_type = 'STRING'
default_value_get: PointerProperty(name='Action', type=bpy.types.Action) # legacy version of the line after this one
default_value_raw: PointerProperty(name='Action', type=bpy.types.Action, update=_on_update_socket)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
2025-01-22 16:18:30 +01:00
if self.default_value_get is not None:
self.default_value_raw = self.default_value_get
self.default_value_get = None
def get_default_value(self):
if self.default_value_raw is None:
return ''
if self.default_value_raw.name not in bpy.data.actions:
return self.default_value_raw.name
name = lnx.utils.asset_name(bpy.data.actions[self.default_value_raw.name])
return lnx.utils.safestr(name)
def draw(self, context, layout, node, text):
if self.is_output:
layout.label(text=self.name)
elif self.is_linked:
layout.label(text=self.name)
else:
row = layout.row(align=True)
layout.prop_search(self, 'default_value_raw', bpy.data, 'actions', icon='NONE', text=self.name)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def copy_defaults(self, socket):
if socket.bl_idname == self.bl_idname:
socket.default_value_raw = self.default_value_raw
class LnxRotationSocket(LnxCustomSocket):
bl_idname = 'LnxRotationSocket'
bl_label = 'Rotation Socket'
lnx_socket_type = 'ROTATION' # the internal representation is a quaternion, AKA a '4D vector' (using mathutils.Vector((x,y,z,w)))
def get_default_value(self):
if self.default_value_raw is None:
return mathutils.Vector((0.0,0.0,0.0,1.0))
else:
return self.default_value_raw
def on_unit_update(self, context):
if self.default_value_unit == 'Rad':
fac = pi/180 # deg->rad conversion
else:
fac = 180/pi # rad->deg conversion
if self.default_value_mode == 'AxisAngle':
self.default_value_s3 *= fac
elif self.default_value_mode == 'EulerAngles':
self.default_value_s0 *= fac
self.default_value_s1 *= fac
self.default_value_s2 *= fac
self.do_update_raw(context)
def on_mode_update(self, context):
if self.default_value_mode == 'Quaternion':
summ = abs(self.default_value_s0)
summ+= abs(self.default_value_s1)
summ+= abs(self.default_value_s2)
summ+= abs(self.default_value_s3)
if summ<0.01:
self.default_value_s3 = 1.0
elif self.default_value_mode == 'AxisAngle':
summ = abs(self.default_value_s0)
summ+= abs(self.default_value_s1)
summ+= abs(self.default_value_s2)
if summ<1E-5:
self.default_value_s3 = 0.0
self.do_update_raw(context)
@staticmethod
def convert_to_quaternion(vec3_val, scalar_val, mode, unit, order):
'''Converts Euler or Axis-Angle representation to a Quaternion Vector'''
if mode == 'Quaternion':
qx, qy, qz = vec3_val[0], vec3_val[1], vec3_val[2]
qw = scalar_val
ql = sqrt(qx**2 + qy**2 + qz**2 + qw**2)
if abs(ql) < 1E-5:
qx, qy, qz, qw = 0.0, 0.0, 0.0, 1.0
2025-01-22 16:18:30 +01:00
else:
qx /= ql
qy /= ql
qz /= ql
qw /= ql
return mathutils.Vector((qx, qy, qz, qw))
elif mode == 'EulerAngles':
x, y, z = vec3_val.to_tuple()
if unit == 'Deg':
x, y, z = radians(x), radians(y), radians(z)
angles_ordered = [0.0, 0.0, 0.0]
for i, axis in enumerate(order):
if axis == 'X':
angles_ordered[i] = x
elif axis == 'Y':
angles_ordered[i] = y
elif axis == 'Z':
angles_ordered[i] = z
eul = mathutils.Euler(angles_ordered, order)
quat = eul.to_quaternion()
return mathutils.Vector((quat.x, quat.y, quat.z, quat.w))
2025-01-22 16:18:30 +01:00
elif mode == 'AxisAngle':
axis = vec3_val.normalized().to_tuple()
angle = scalar_val
if unit == 'Deg':
angle = radians(angle)
quat = mathutils.Quaternion(axis, angle)
return mathutils.Vector((quat.x, quat.y, quat.z, quat.w))
print(f"Warning: Invalid mode '{mode}' in convert_to_quaternion")
return mathutils.Vector((0.0, 0.0, 0.0, 1.0))
2025-01-22 16:18:30 +01:00
def do_update_raw(self, context):
if self.default_value_mode == 'Quaternion':
# Directly construct the quaternion vector from s0, s1, s2, s3 (x, y, z, w)
vec3_val = mathutils.Vector((
self.default_value_s0, # X component or Euler X or Axis X
self.default_value_s1, # Y component or Euler Y or Axis Y
self.default_value_s2 # Z component or Euler Z or Axis Z
))
scalar_val = self.default_value_s3 # W component or Axis Angle
# Always call the unified conversion function
# The result will be in (x, y, z, w) order
self.default_value_raw = self.convert_to_quaternion(
vec3_val,
scalar_val,
self.default_value_mode,
self.default_value_unit,
self.default_value_order
)
else:
# Handle EulerAngles and AxisAngle using the conversion helper
vec3_val = mathutils.Vector((
self.default_value_s0,
self.default_value_s1,
self.default_value_s2
))
# s3 is used for AxisAngle angle, irrelevant for Euler
scalar_val = self.default_value_s3
self.default_value_raw = self.convert_to_quaternion(
vec3_val, # Vector part (Euler angles X,Y,Z or Axis X,Y,Z)
scalar_val, # Scalar part (Axis angle)
self.default_value_mode, # Mode ('EulerAngles' or 'AxisAngle')
self.default_value_unit, # Unit ('Rad' or 'Deg')
self.default_value_order # Order ('XYZ', 'ZYX', etc. - used only for Euler)
)
2025-01-22 16:18:30 +01:00
def draw(self, context, layout, node, text):
if (self.is_output or self.is_linked):
layout.label(text=self.name)
else:
coll1 = layout.column(align=True)
coll1.label(text=self.name)
bx=coll1.box()
coll = bx.column(align=True)
coll.prop(self, 'default_value_mode')
if self.default_value_mode in ('EulerAngles', 'AxisAngle'):
coll.prop(self, 'default_value_unit')
if self.default_value_mode == 'EulerAngles':
coll.prop(self, 'default_value_order')
coll.prop(self, 'default_value_s0', text='X')
coll.prop(self, 'default_value_s1', text='Y')
coll.prop(self, 'default_value_s2', text='Z')
elif self.default_value_mode == 'Quaternion':
coll.prop(self, 'default_value_s0', text='X')
coll.prop(self, 'default_value_s1', text='Y')
coll.prop(self, 'default_value_s2', text='Z')
coll.prop(self, 'default_value_s3', text='W')
elif self.default_value_mode == 'AxisAngle':
coll.prop(self, 'default_value_s0', text='X')
coll.prop(self, 'default_value_s1', text='Y')
coll.prop(self, 'default_value_s2', text='Z')
coll.separator()
coll.prop(self, 'default_value_s3', text='Angle')
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
default_value_mode: EnumProperty(
items=[('EulerAngles', 'Euler Angles', 'Euler Angles'),
('AxisAngle', 'Axis/Angle', 'Axis/Angle'),
('Quaternion', 'Quaternion', 'Quaternion')],
name='', default='EulerAngles',
update=on_mode_update)
default_value_unit: EnumProperty(
items=[('Deg', 'Degrees', 'Degrees'),
('Rad', 'Radians', 'Radians')],
name='', default='Rad',
update=on_unit_update)
default_value_order: EnumProperty(
items=[('XYZ','XYZ','XYZ'),
('XZY','XZY (legacy Leenkx euler order)','XZY (legacy Leenkx euler order)'),
('YXZ','YXZ','YXZ'),
('YZX','YZX','YZX'),
('ZXY','ZXY','ZXY'),
('ZYX','ZYX','ZYX')],
name='', default='XYZ',
update=do_update_raw
2025-01-22 16:18:30 +01:00
)
2025-01-22 16:18:30 +01:00
default_value_s0: FloatProperty(update=do_update_raw)
default_value_s1: FloatProperty(update=do_update_raw)
default_value_s2: FloatProperty(update=do_update_raw)
default_value_s3: FloatProperty(update=do_update_raw)
default_value_raw: FloatVectorProperty(
name='Value',
description='Raw quaternion obtained for the default value of a LnxRotationSocket socket',
size=4, default=(0,0,0,1),
update = _on_update_socket
)
def copy_defaults(self, socket):
if socket.bl_idname == self.bl_idname:
socket.default_value_mode = self.default_value_mode
socket.default_value_unit = self.default_value_unit
socket.default_value_order = self.default_value_order
socket.default_value_s0 = self.default_value_s0
socket.default_value_s1 = self.default_value_s1
socket.default_value_s2 = self.default_value_s2
socket.default_value_s3 = self.default_value_s3
socket.default_value_raw = self.default_value_raw
class LnxArraySocket(LnxCustomSocket):
bl_idname = 'LnxNodeSocketArray'
bl_label = 'Array Socket'
lnx_socket_type = 'NONE'
def draw(self, context, layout, node, text):
layout.label(text=self.name)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
class LnxBoolSocket(LnxCustomSocket):
bl_idname = 'LnxBoolSocket'
bl_label = 'Boolean Socket'
lnx_socket_type = 'BOOLEAN'
default_value_raw: BoolProperty(
name='Value',
description='Input value used for unconnected socket',
update=_on_update_socket
)
def draw(self, context, layout, node, text):
draw_socket_layout(self, layout)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def get_default_value(self):
return self.default_value_raw
def copy_defaults(self, socket):
if socket.bl_idname == self.bl_idname:
socket.default_value_raw = self.default_value_raw
class LnxColorSocket(LnxCustomSocket):
bl_idname = 'LnxColorSocket'
bl_label = 'Color Socket'
lnx_socket_type = 'RGBA'
default_value_raw: FloatVectorProperty(
name='Value',
size=4,
subtype='COLOR',
min=0.0,
max=1.0,
default=[0.0, 0.0, 0.0, 1.0],
description='Input value used for unconnected socket',
update=_on_update_socket
)
def draw(self, context, layout, node, text):
draw_socket_layout_split(self, layout)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def get_default_value(self):
return self.default_value_raw
def copy_defaults(self, socket):
if socket.bl_idname == self.bl_idname:
socket.default_value_raw = self.default_value_raw
class LnxDynamicSocket(LnxCustomSocket):
bl_idname = 'LnxDynamicSocket'
bl_label = 'Dynamic Socket'
lnx_socket_type = 'NONE'
def draw(self, context, layout, node, text):
layout.label(text=self.name)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
class LnxAnySocket(LnxCustomSocket):
bl_idname = 'LnxAnySocket'
bl_label = 'Any Socket'
lnx_socket_type = 'NONE'
# Callback function when socket label is changed
def on_disp_label_update(self, context):
node = self.node
if node.bl_idname == 'LNGroupInputsNode' or node.bl_idname == 'LNGroupOutputsNode':
if not node.invalid_link:
node.socket_name_update(self)
self.on_node_update()
self.name = self.display_label
display_label: StringProperty(
name='display_label',
description='Property to store socket display name',
update=on_disp_label_update)
display_color: FloatVectorProperty(
name='Color',
size=4,
subtype='COLOR',
min=0.0,
max=1.0,
default=socket_colors['LnxAnySocket']
)
def draw(self, context, layout, node, text):
layout.label(text=self.display_label)
def draw_color(self, context, node):
return self.display_color
def on_node_update(self):
# Cache name and color of connected socket
if self.is_output:
c_node, c_socket = lnx.node_utils.output_get_connected_node(self)
else:
c_node, c_socket = lnx.node_utils.input_get_connected_node(self)
if c_node is None:
self.display_color = socket_colors[self.__class__.bl_idname]
else:
if self.display_label == '':
self.display_label = c_socket.name
self.display_color = c_socket.draw_color(bpy.context, c_node)
class LnxFloatSocket(LnxCustomSocket):
bl_idname = 'LnxFloatSocket'
bl_label = 'Float Socket'
lnx_socket_type = 'VALUE'
default_value_raw: FloatProperty(
name='Value',
description='Input value used for unconnected socket',
precision=3,
update=_on_update_socket
)
def draw(self, context, layout, node, text):
draw_socket_layout(self, layout)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def get_default_value(self):
return self.default_value_raw
def copy_defaults(self, socket):
if socket.bl_idname == self.bl_idname:
socket.default_value_raw = self.default_value_raw
class LnxIntSocket(LnxCustomSocket):
bl_idname = 'LnxIntSocket'
bl_label = 'Integer Socket'
lnx_socket_type = 'INT'
default_value_raw: IntProperty(
name='Value',
description='Input value used for unconnected socket',
update=_on_update_socket
)
def draw(self, context, layout, node, text):
draw_socket_layout(self, layout)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def get_default_value(self):
return self.default_value_raw
def copy_defaults(self, socket):
if socket.bl_idname == self.bl_idname:
socket.default_value_raw = self.default_value_raw
class LnxObjectSocket(LnxCustomSocket):
bl_idname = 'LnxNodeSocketObject'
bl_label = 'Object Socket'
lnx_socket_type = 'OBJECT'
default_value_get: PointerProperty(name='Object', type=bpy.types.Object) # legacy version of the line after this one
default_value_raw: PointerProperty(name='Object', type=bpy.types.Object, update=_on_update_socket)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
2025-01-22 16:18:30 +01:00
if self.default_value_get is not None:
self.default_value_raw = self.default_value_get
self.default_value_get = None
def get_default_value(self):
if self.default_value_raw is None:
return ''
if self.default_value_raw.name not in bpy.data.objects:
return self.default_value_raw.name
return lnx.utils.asset_name(bpy.data.objects[self.default_value_raw.name])
def draw(self, context, layout, node, text):
if self.is_output:
layout.label(text=self.name)
elif self.is_linked:
layout.label(text=self.name)
else:
row = layout.row(align=True)
row.prop_search(self, 'default_value_raw', context.scene, 'objects', icon='NONE', text=self.name)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def copy_defaults(self, socket):
if socket.bl_idname == self.bl_idname:
socket.default_value_raw = self.default_value_raw
class LnxStringSocket(LnxCustomSocket):
bl_idname = 'LnxStringSocket'
bl_label = 'String Socket'
lnx_socket_type = 'STRING'
default_value_raw: StringProperty(
name='Value',
description='Input value used for unconnected socket',
update=_on_update_socket
)
def draw(self, context, layout, node, text):
draw_socket_layout_split(self, layout)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def get_default_value(self):
return self.default_value_raw
def copy_defaults(self, socket):
if socket.bl_idname == self.bl_idname:
socket.default_value_raw = self.default_value_raw
class LnxVectorSocket(LnxCustomSocket):
bl_idname = 'LnxVectorSocket'
bl_label = 'Vector Socket'
lnx_socket_type = 'VECTOR'
default_value_raw: FloatVectorProperty(
name='Value',
size=3,
precision=3,
description='Input value used for unconnected socket',
update=_on_update_socket
)
def draw(self, context, layout, node, text):
if not self.is_output and not self.is_linked:
col = layout.column(align=True)
col.label(text=self.name + ":")
col.prop(self, 'default_value_raw', text='')
else:
layout.label(text=self.name)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def get_default_value(self):
return self.default_value_raw
def copy_defaults(self, socket):
if socket.bl_idname == self.bl_idname:
socket.default_value_raw = self.default_value_raw
class LnxAnimTreeSocket(LnxCustomSocket):
bl_idname = 'LnxNodeSocketAnimTree'
bl_label = 'Animation Tree Socket'
lnx_socket_type = 'NONE'
def draw(self, context, layout, node, text):
layout.label(text=self.name)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
class LnxFactorSocket(LnxCustomSocket):
bl_idname = 'LnxFactorSocket'
2025-01-22 16:18:30 +01:00
bl_label = 'Factor Socket'
lnx_socket_type = 'FACTOR'
default_value_raw: FloatProperty(
name='Factor',
description='Input value used for unconnected socket in the range [0 , 1]',
precision=3,
min = 0.0,
max = 1.0,
update=_on_update_socket
)
def draw(self, context, layout, node, text):
draw_socket_layout(self, layout)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def get_default_value(self):
return self.default_value_raw
class LnxBlendSpaceSocket(LnxCustomSocket):
bl_idname = 'LnxBlendSpaceSocket'
2025-01-22 16:18:30 +01:00
bl_label = 'Blend Space Socket'
lnx_socket_type = 'FACTOR'
default_value_raw: FloatProperty(
name='Factor',
description='Input value used for unconnected socket in the range [0 , 1]',
precision=3,
min = 0.0,
max = 1.0,
update=_on_update_socket
)
def draw(self, context, layout, node, text):
draw_socket_layout(self, layout)
def draw_color(self, context, node):
return socket_colors[self.bl_idname]
def get_default_value(self):
return self.default_value_raw
def set_default_value(self, value):
self.default_value_raw = value
def draw_socket_layout(socket: bpy.types.NodeSocket, layout: bpy.types.UILayout, prop_name='default_value_raw'):
if not socket.is_output and not socket.is_linked:
layout.prop(socket, prop_name, text=socket.name)
else:
layout.label(text=socket.name)
def draw_socket_layout_split(socket: bpy.types.NodeSocket, layout: bpy.types.UILayout, prop_name='default_value_raw'):
if not socket.is_output and not socket.is_linked:
# Blender layouts use 0.4 splits
layout = layout.split(factor=0.4, align=True)
layout.label(text=socket.name)
if not socket.is_output and not socket.is_linked:
layout.prop(socket, prop_name, text='')
if bpy.app.version < (4, 1, 0):
def _make_socket_interface(interface_name: str, bl_idname: str) -> Type[bpy.types.NodeSocketInterface]:
"""Create a socket interface class that is used by Blender for node
groups. We currently don't use real node groups, but without these
classes Blender will (incorrectly) draw the socket borders in light grey.
"""
def draw(self, context, layout):
pass
def draw_color(self, context):
# This would be used if we were using "real" node groups
return 0, 0, 0, 1
cls = type(
interface_name,
(bpy.types.NodeSocketInterface, ), {
'bl_socket_idname': bl_idname,
'draw': draw,
'draw_color': draw_color,
}
)
return cls
else:
def _make_socket_interface(interface_name: str, bl_idname: str) -> Type[bpy.types.NodeTreeInterfaceSocket]:
"""Create a socket interface class that is used by Blender for node
groups. We currently don't use real node groups, but without these
classes Blender will (incorrectly) draw the socket borders in light grey.
"""
def draw(self, context, layout):
pass
def draw_color(self, context):
# This would be used if we were using "real" node groups
return 0, 0, 0, 1
cls = type(
interface_name,
(bpy.types.NodeTreeInterfaceSocket, ), {
'bl_socket_idname': bl_idname,
'draw': draw,
'draw_color': draw_color,
}
)
return cls
LnxActionSocketInterface = _make_socket_interface('LnxActionSocketInterface', 'LnxNodeSocketAction')
LnxAnimSocketInterface = _make_socket_interface('LnxAnimSocketInterface', 'LnxNodeSocketAnimAction')
LnxRotationSocketInterface = _make_socket_interface('LnxRotationSocketInterface', 'LnxRotationSocket')
LnxArraySocketInterface = _make_socket_interface('LnxArraySocketInterface', 'LnxNodeSocketArray')
LnxBoolSocketInterface = _make_socket_interface('LnxBoolSocketInterface', 'LnxBoolSocket')
LnxColorSocketInterface = _make_socket_interface('LnxColorSocketInterface', 'LnxColorSocket')
LnxDynamicSocketInterface = _make_socket_interface('LnxDynamicSocketInterface', 'LnxDynamicSocket')
LnxFloatSocketInterface = _make_socket_interface('LnxFloatSocketInterface', 'LnxFloatSocket')
LnxIntSocketInterface = _make_socket_interface('LnxIntSocketInterface', 'LnxIntSocket')
LnxObjectSocketInterface = _make_socket_interface('LnxObjectSocketInterface', 'LnxNodeSocketObject')
LnxStringSocketInterface = _make_socket_interface('LnxStringSocketInterface', 'LnxStringSocket')
LnxVectorSocketInterface = _make_socket_interface('LnxVectorSocketInterface', 'LnxVectorSocket')
LnxAnySocketInterface = _make_socket_interface('LnxAnySocketInterface', 'LnxAnySocket')
LnxAnimTreeSocketInterface = _make_socket_interface('LnxAnimTreeSocketInterface', 'LnxNodeSocketAnimTree')
LnxFactorSocketInterface = _make_socket_interface('LnxFactorSocketInterface', 'LnxFactorSocket')
LnxBlendSpaceSocketInterface = _make_socket_interface('LnxBlendSpaceSocketInterface', 'LnxBlendSpaceSocket')
2025-01-22 16:18:30 +01:00
__REG_CLASSES = (
LnxActionSocketInterface,
LnxAnimSocketInterface,
LnxRotationSocketInterface,
LnxArraySocketInterface,
LnxBoolSocketInterface,
LnxColorSocketInterface,
LnxDynamicSocketInterface,
LnxFloatSocketInterface,
LnxIntSocketInterface,
LnxObjectSocketInterface,
LnxStringSocketInterface,
LnxVectorSocketInterface,
LnxAnySocketInterface,
LnxAnimTreeSocketInterface,
LnxFactorSocketInterface,
LnxBlendSpaceSocketInterface,
2025-01-22 16:18:30 +01:00
LnxActionSocket,
LnxAnimActionSocket,
LnxRotationSocket,
LnxArraySocket,
LnxBoolSocket,
LnxColorSocket,
LnxDynamicSocket,
LnxFloatSocket,
LnxIntSocket,
LnxObjectSocket,
LnxStringSocket,
LnxVectorSocket,
LnxAnySocket,
LnxAnimTreeSocket,
LnxFactorSocket,
LnxBlendSpaceSocket,
2025-01-22 16:18:30 +01:00
)
register, unregister = bpy.utils.register_classes_factory(__REG_CLASSES)