LNXSDK/leenkx/blender/lnx/logicnode/lnx_sockets.py
2025-01-22 16:18:30 +01:00

760 lines
26 KiB
Python

from math import pi, cos, sin, sqrt
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),
'ArmFactorSocket': (0.631, 0.631, 0.631, 1.0),
'ArmBlendSpaceSocket': (0.631, 0.631, 0.631, 1.0)
}
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):
super().__init__()
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(part1,part2,param1,param2,param3):
"""converts a representation of rotation into a quaternion.
``part1`` is a vector, ``part2`` is a scalar or None,
``param1`` is in ('Quaternion', 'EulerAngles', 'AxisAngle'),
``param2`` is in ('Rad','Deg') for both EulerAngles and AxisAngle,
``param3`` is a len-3 string like "XYZ", for EulerAngles """
if param1=='Quaternion':
qx, qy, qz = part1[0], part1[1], part1[2]
qw = part2
# need to normalize the quaternion for a rotation (having it be 0 is not an option)
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
else:
qx /= ql
qy /= ql
qz /= ql
qw /= ql
return mathutils.Vector((qx,qy,qz,qw))
elif param1 == 'AxisAngle':
if param2 == 'Deg':
angle = part2 * pi/180
else:
angle = part2
cang, sang = cos(angle/2), sin(angle/2)
x,y,z = part1[0], part1[1], part1[2]
veclen = sqrt(x**2+y**2+z**2)
if veclen<1E-5:
return mathutils.Vector((0.0,0.0,0.0,1.0))
else:
return mathutils.Vector((
x/veclen * sang,
y/veclen * sang,
z/veclen * sang,
cang
))
else: # param1 == 'EulerAngles'
x,y,z = part1[0], part1[1], part1[2]
if param2 == 'Deg':
x *= pi/180
y *= pi/180
z *= pi/180
cx, sx = cos(x/2), sin(x/2)
cy, sy = cos(y/2), sin(y/2)
cz, sz = cos(z/2), sin(z/2)
qw, qx, qy, qz = 1.0,0.0,0.0,0.0
for direction in param3[::-1]:
qwi, qxi,qyi,qzi = {'X': (cx,sx,0,0), 'Y': (cy,0,sy,0), 'Z': (cz,0,0,sz)}[direction]
qw = qw*qwi -qx*qxi -qy*qyi -qz*qzi
qx = qx*qwi +qw*qxi +qy*qzi -qz*qyi
qy = qy*qwi +qw*qyi +qz*qxi -qx*qzi
qz = qz*qwi +qw*qzi +qx*qyi -qy*qxi
return mathutils.Vector((qx,qy,qz,qw))
def do_update_raw(self, context):
part1 = mathutils.Vector((
self.default_value_s0,
self.default_value_s1,
self.default_value_s2, 1
))
part2 = self.default_value_s3
self.default_value_raw = self.convert_to_quaternion(
part1,
self.default_value_s3,
self.default_value_mode,
self.default_value_unit,
self.default_value_order
)
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'
)
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):
super().__init__()
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 ArmFactorSocket(LnxCustomSocket):
bl_idname = 'ArmFactorSocket'
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 ArmBlendSpaceSocket(LnxCustomSocket):
bl_idname = 'ArmBlendSpaceSocket'
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')
ArmFactorSocketInterface = _make_socket_interface('ArmFactorSocketInterface', 'ArmFactorSocket')
ArmBlendSpaceSocketInterface = _make_socket_interface('ArmBlendSpaceSocketInterface', 'ArmBlendSpaceSocket')
__REG_CLASSES = (
LnxActionSocketInterface,
LnxAnimSocketInterface,
LnxRotationSocketInterface,
LnxArraySocketInterface,
LnxBoolSocketInterface,
LnxColorSocketInterface,
LnxDynamicSocketInterface,
LnxFloatSocketInterface,
LnxIntSocketInterface,
LnxObjectSocketInterface,
LnxStringSocketInterface,
LnxVectorSocketInterface,
LnxAnySocketInterface,
LnxAnimTreeSocketInterface,
ArmFactorSocketInterface,
ArmBlendSpaceSocketInterface,
LnxActionSocket,
LnxAnimActionSocket,
LnxRotationSocket,
LnxArraySocket,
LnxBoolSocket,
LnxColorSocket,
LnxDynamicSocket,
LnxFloatSocket,
LnxIntSocket,
LnxObjectSocket,
LnxStringSocket,
LnxVectorSocket,
LnxAnySocket,
LnxAnimTreeSocket,
ArmFactorSocket,
ArmBlendSpaceSocket,
)
register, unregister = bpy.utils.register_classes_factory(__REG_CLASSES)