Added node replacement routines for previous commits

(and fixed a couple bugs along the way)
This commit is contained in:
niacdoial 2021-08-12 20:58:35 +02:00
parent 9b1393ea41
commit bd67667a6e
14 changed files with 750 additions and 177 deletions

View file

@ -121,7 +121,14 @@ class ArmNodeAddInputButton(bpy.types.Operator):
def execute(self, context):
global array_nodes
inps = array_nodes[self.node_index].inputs
inps.new(self.socket_type, self.name_format.format(str(len(inps) + self.index_name_offset)))
socket_types = self.socket_type.split(';')
name_formats = self.name_format.split(';')
assert len(socket_types)==len(name_formats)
format_index = (len(inps) + self.index_name_offset) //len(socket_types)
for socket_type, name_format in zip(socket_types, name_formats):
inps.new(socket_type, name_format.format(str(format_index)))
# Reset to default again for subsequent calls of this operator
self.node_index = ''
@ -132,31 +139,34 @@ class ArmNodeAddInputButton(bpy.types.Operator):
return{'FINISHED'}
class ArmNodeAddInputValueButton(bpy.types.Operator):
"""Add new input"""
bl_idname = 'arm.node_add_input_value'
bl_label = 'Add Input'
node_index: StringProperty(name='Node Index', default='')
socket_type: StringProperty(name='Socket Type', default='NodeSocketShader')
"""Add new input"""
bl_idname = 'arm.node_add_input_value'
bl_label = 'Add Input'
node_index: StringProperty(name='Node Index', default='')
socket_type: StringProperty(name='Socket Type', default='NodeSocketShader')
def execute(self, context):
global array_nodes
inps = array_nodes[self.node_index].inputs
inps.new(self.socket_type, 'Value')
return{'FINISHED'}
def execute(self, context):
global array_nodes
inps = array_nodes[self.node_index].inputs
inps.new(self.socket_type, 'Value')
return{'FINISHED'}
class ArmNodeRemoveInputButton(bpy.types.Operator):
"""Remove last input"""
bl_idname = 'arm.node_remove_input'
bl_label = 'Remove Input'
node_index: StringProperty(name='Node Index', default='')
count: IntProperty(name='Number of inputs to remove', default=1, min=1)
min_inputs: IntProperty(name='Number of inputs to keep', default=0, min=0)
def execute(self, context):
global array_nodes
node = array_nodes[self.node_index]
inps = node.inputs
min_inps = 0 if not hasattr(node, 'min_inputs') else node.min_inputs
if len(inps) > min_inps:
inps.remove(inps.values()[-1])
min_inps = self.min_inputs if not hasattr(node, 'min_inputs') else node.min_inputs
if len(inps) >= min_inps + self.count:
for _ in range(self.count):
inps.remove(inps.values()[-1])
return{'FINISHED'}
class ArmNodeRemoveInputValueButton(bpy.types.Operator):
@ -164,13 +174,14 @@ class ArmNodeRemoveInputValueButton(bpy.types.Operator):
bl_idname = 'arm.node_remove_input_value'
bl_label = 'Remove Input'
node_index: StringProperty(name='Node Index', default='')
target_name: StringProperty(name='Name of socket to remove', default='Value')
def execute(self, context):
global array_nodes
node = array_nodes[self.node_index]
inps = node.inputs
min_inps = 0 if not hasattr(node, 'min_inputs') else node.min_inputs
if len(inps) > min_inps and inps[-1].name == 'Value':
if len(inps) > min_inps and inps[-1].name == self.target_name:
inps.remove(inps.values()[-1])
return{'FINISHED'}
@ -187,7 +198,14 @@ class ArmNodeAddOutputButton(bpy.types.Operator):
def execute(self, context):
global array_nodes
outs = array_nodes[self.node_index].outputs
outs.new(self.socket_type, self.name_format.format(str(len(outs) + self.index_name_offset)))
socket_types = self.socket_type.split(';')
name_formats = self.name_format.split(';')
assert len(socket_types)==len(name_formats)
format_index = (len(outs) + self.index_name_offset) //len(socket_types)
for socket_type, name_format in zip(socket_types, name_formats):
outs.new(socket_type, name_format.format(str(format_index)))
# Reset to default again for subsequent calls of this operator
self.node_index = ''
@ -202,14 +220,16 @@ class ArmNodeRemoveOutputButton(bpy.types.Operator):
bl_idname = 'arm.node_remove_output'
bl_label = 'Remove Output'
node_index: StringProperty(name='Node Index', default='')
count: IntProperty(name='Number of outputs to remove', default=1, min=1)
def execute(self, context):
global array_nodes
node = array_nodes[self.node_index]
outs = node.outputs
min_outs = 0 if not hasattr(node, 'min_outputs') else node.min_outputs
if len(outs) > min_outs:
outs.remove(outs.values()[-1])
if len(outs) >= min_outs + self.count:
for _ in range(self.count):
outs.remove(outs.values()[-1])
return{'FINISHED'}
class ArmNodeAddInputOutputButton(bpy.types.Operator):
@ -229,8 +249,21 @@ class ArmNodeAddInputOutputButton(bpy.types.Operator):
node = array_nodes[self.node_index]
inps = node.inputs
outs = node.outputs
inps.new(self.in_socket_type, self.in_name_format.format(str(len(inps) + self.in_index_name_offset)))
outs.new(self.out_socket_type, self.out_name_format.format(str(len(outs))))
in_socket_types = self.in_socket_type.split(';')
in_name_formats = self.in_name_format.split(';')
assert len(in_socket_types)==len(in_name_formats)
out_socket_types = self.out_socket_type.split(';')
out_name_formats = self.out_name_format.split(';')
assert len(out_socket_types)==len(out_name_formats)
in_format_index = (len(outs) + self.index_name_offset) // len(in_socket_types)
out_format_index = len(outs) // len(out_socket_types)
for socket_type, name_format in zip(in_socket_types, in_name_formats):
inps.new(socket_type, name_format.format(str(in_format_index)))
for socket_type, name_format in zip(out_socket_types, out_name_formats):
outs.new(socket_type, name_format.format(str(out_format_index)))
# Reset to default again for subsequent calls of this operator
self.node_index = ''
@ -247,7 +280,9 @@ class ArmNodeRemoveInputOutputButton(bpy.types.Operator):
bl_idname = 'arm.node_remove_input_output'
bl_label = 'Remove Input Output'
node_index: StringProperty(name='Node Index', default='')
in_count: IntProperty(name='Number of inputs to remove', default=1, min=1)
out_count: IntProperty(name='Number of inputs to remove', default=1, min=1)
def execute(self, context):
global array_nodes
node = array_nodes[self.node_index]
@ -255,10 +290,12 @@ class ArmNodeRemoveInputOutputButton(bpy.types.Operator):
outs = node.outputs
min_inps = 0 if not hasattr(node, 'min_inputs') else node.min_inputs
min_outs = 0 if not hasattr(node, 'min_outputs') else node.min_outputs
if len(inps) > min_inps:
inps.remove(inps.values()[-1])
if len(outs) > min_outs:
outs.remove(outs.values()[-1])
if len(inps) >= min_inps + self.in_count:
for _ in range(self.in_count):
inps.remove(inps.values()[-1])
if len(outs) >= min_outs + self.out_count:
for _ in range(self.out_count):
outs.remove(outs.values()[-1])
return{'FINISHED'}
@ -428,7 +465,10 @@ def deprecated(*alternatives: str, message=""):
def wrapper(cls: ArmLogicTreeNode) -> ArmLogicTreeNode:
cls.bl_label += ' (Deprecated)'
cls.bl_description = f'Deprecated. {cls.bl_description}'
if hasattr(cls, 'bl_description'):
cls.bl_description = f'Deprecated. {cls.bl_description}'
else:
cls.bl_description = 'Deprecated.'
cls.bl_icon = 'ERROR'
cls.arm_is_obsolete = True

View file

@ -112,12 +112,16 @@ class ArmRotationSocket(ArmCustomSocket):
self.default_value_s3 = 0.0
self.do_update_raw(context)
def do_update_raw(self, context):
if self.default_value_mode == 'Quat':
qx = self.default_value_s0
qy = self.default_value_s1
qz = self.default_value_s2
qw = self.default_value_s3
@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 ('Quat', 'EulerAngles', 'AxisAngle'),
``param2`` is in ('Rad','Deg') for both EulerAngles and AxisAngle,
``param3`` is a len-3 string like "XYZ", for EulerAngles """
if param1 == 'Quat':
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:
@ -127,49 +131,61 @@ class ArmRotationSocket(ArmCustomSocket):
qy /= ql
qz /= ql
qw /= ql
self.default_value_raw = mathutils.Vector((qx,qy,qz,qw))
elif self.default_value_mode == 'AxisAngle':
if self.default_value_unit == 'Deg':
angle = self.default_value_s3 * pi/180
return mathutils.Vector((qx,qy,qz,qw))
elif param1 == 'AxisAngle':
if param2 == 'Deg':
angle = part2 * pi/180
else:
angle = self.default_value_s3
angle = part2
cang, sang = cos(angle/2), sin(angle/2)
x = self.default_value_s0
y = self.default_value_s1
z = self.default_value_s2
x,y,z = part1[0], part1[1], part1[2]
veclen = sqrt(x**2+y**2+z**2)
if veclen<1E-5:
self.default_value_raw = mathutils.Vector((0.0,0.0,0.0,1.0))
return mathutils.Vector((0.0,0.0,0.0,1.0))
else:
self.default_value_raw = mathutils.Vector((
return mathutils.Vector((
x/veclen * sang,
y/veclen * sang,
z/veclen * sang,
cang
))
else:
if self.default_value_unit == 'Deg':
x = self.default_value_s0 * pi/180
y = self.default_value_s1 * pi/180
z = self.default_value_s2 * pi/180
else:
x = self.default_value_s0
y = self.default_value_s1
z = self.default_value_s2
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 self.default_value_order[::-1]:
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
self.default_value_raw = mathutils.Vector((qx,qy,qz,qw))
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.defualt_value_unit,
self.default_value_order
)
def draw(self, context, layout, node, text):

View file

@ -4,44 +4,46 @@ class QuaternionMathNode(ArmLogicTreeNode):
"""Mathematical operations on quaternions."""
bl_idname = 'LNQuaternionMathNode'
bl_label = 'Quaternion Math'
bl_description = 'Mathematical operations that can be performed on rotations, when represented as quaternions specifically'
arm_section = 'quaternions'
arm_version = 1
arm_version = 2
def get_bool(self):
return self.get('property1', False)
def set_bool(self, value):
self['property1'] = value
if value:
if ((self.property0 == 'Module') or (self.property0 == 'DotProduct') or (self.property0 == 'ToAxisAngle')) and (len(self.outputs) > 1):
self.outputs.remove(self.outputs.values()[-1]) # Module/DotProduct/ToAxisAngle
self.add_output('NodeSocketFloat', 'X') # Result X
self.add_output('NodeSocketFloat', 'Y') # Result Y
self.add_output('NodeSocketFloat', 'Z') # Result Z
self.add_output('NodeSocketFloat', 'W') # Result W
if (self.property0 == 'Module'):
self.add_output('NodeSocketFloat', 'Module') # Module
if (self.property0 == 'DotProduct'):
self.add_output('NodeSocketFloat', 'Scalar') # DotProduct
if (self.property0 == 'ToAxisAngle'):
self.add_output('NodeSocketFloat', 'To Axis Angle') # ToAxisAngle
def ensure_input_socket(self, socket_number, newclass, newname, default_value=None):
while len(self.inputs) < socket_number:
self.inputs.new('NodeSocketFloat', 'BOGUS')
if len(self.inputs) > socket_number:
if len(self.inputs[socket_number].links) == 1:
source_socket = self.inputs[socket_number].links[0].from_socket
else:
source_socket = None
if self.inputs[socket_number].bl_idname == newclass:
default_value = self.inputs[socket_number].default_value
self.inputs.remove(self.inputs[socket_number])
else:
if ((self.property0 == 'Module') or (self.property0 == 'DotProduct') or (self.property0 == 'ToAxisAngle')) and (len(self.outputs) > 1):
self.outputs.remove(self.outputs.values()[-1]) # Module/DotProduct/ToAxisAngle
# Remove X, Y, Z, W
for i in range(4):
if len(self.outputs) > 1:
self.outputs.remove(self.outputs.values()[-1])
else:
break
if (self.property0 == 'Module'):
self.add_output('NodeSocketFloat', 'Module') # Module
if (self.property0 == 'DotProduct'):
self.add_output('NodeSocketFloat', 'Scalar') # DotProduct
if (self.property0 == 'ToAxisAngle'):
self.add_output('NodeSocketFloat', 'To Axis Angle') # ToAxisAngle
source_socket = None
self.inputs.new(newclass, newname)
if default_value != None:
self.inputs[-1].default_value = default_value
self.inputs.move(len(self.inputs)-1, socket_number)
if source_socket is not None:
self.id_data.links.new(source_socket, self.inputs[socket_number])
def ensure_output_socket(self, socket_number, newclass, newname):
sink_sockets = []
while len(self.outputs) < socket_number:
self.outputs.new('NodeSocketFloat', 'BOGUS')
if len(self.outputs) > socket_number:
for link in self.inputs[socket_number].links:
sink_sockets.append(link.to_socket)
self.inputs.remove(self.inputs[socket_number])
property1: BoolProperty(name='Separator Out', default=False, set=set_bool, get=get_bool)
self.inputs.new(newclass, newname)
self.inputs.move(len(self.inputs)-1, socket_number)
for socket in sink_sockets:
self.id_data.links.new(self.inputs[socket_number], socket)
@staticmethod
def get_enum_id_value(obj, prop_name, value):
@ -75,62 +77,40 @@ class QuaternionMathNode(ArmLogicTreeNode):
# Checking the selection of another operation
select_current = self.get_enum_id_value(self, 'property0', value)
select_prev = self.property0
if select_prev != select_current:
# Remove
count = 0
if ((select_prev == 'Add') or (select_prev == 'Subtract') or (select_prev == 'Multiply') or (select_prev == 'DotProduct')) and ((select_current == 'Add') or (select_current == 'Subtract') or (select_current == 'Multiply') or (select_current == 'DotProduct')) or (((select_current == 'Lerp') or (select_current == 'Slerp')) and ((select_prev == 'Lerp') or (select_prev == 'Slerp'))):
count = len(self.inputs)
while (len(self.inputs) > count):
self.inputs.remove(self.inputs.values()[-1])
if (select_prev == 'DotProduct') or (select_prev == 'ToAxisAngle') or (select_prev == 'Module'):
self.outputs.remove(self.outputs.values()[-1])
# Many arguments: Add, Subtract, DotProduct, Multiply, MultiplyFloat
if (self.get_count_in(select_current) == 0):
if (select_current == "MultiplyFloats"):
self.add_input('NodeSocketVector', 'Quaternion ' + str(len(self.inputs)))
self.add_input('NodeSocketFloat', 'Value ' + str(len(self.inputs)))
else:
while (len(self.inputs) < 2):
self.add_input('NodeSocketVector', 'Quaternion ' + str(len(self.inputs)))
if (select_current == 'DotProduct'):
self.add_output('NodeSocketFloat', 'Scalar')
# 3 arguments: Lerp, Slerp, FromAxisAngle, FromEuler
if (self.get_count_in(select_current) == 3):
if (select_current == 'Lerp') or (select_current == 'Slerp'):
while (len(self.inputs) < 3):
self.add_input('NodeSocketVector', 'From')
self.add_input('NodeSocketVector', 'To')
self.add_input('NodeSocketFloat', 'T')
if (select_current == 'FromAxisAngle'):
self.add_input('NodeSocketVector', 'Quaternion')
self.add_input('NodeSocketVector', 'Axis')
self.add_input('NodeSocketFloat', 'Angle')
if (select_current == 'FromEuler'):
self.add_input('NodeSocketFloat', 'X')
self.add_input('NodeSocketFloat', 'Y')
self.add_input('NodeSocketFloat', 'Z')
# 2 arguments: FromTo, FromMat, FromRotationMat, ToAxisAngle
if (self.get_count_in(select_current) == 2):
if (select_current == 'FromTo'):
self.add_input('NodeSocketVector', 'Vector ' + str(len(self.inputs)))
self.add_input('NodeSocketVector', 'Vector ' + str(len(self.inputs)))
if (select_current == 'FromMat') or (select_current == 'FromRotationMat'):
self.add_input('NodeSocketVector', 'Quaternion')
self.add_input('NodeSocketShader', 'Matrix')
if (select_current == 'ToAxisAngle'):
self.add_input('NodeSocketVector', 'Quaternion')
self.add_input('NodeSocketVector', 'Axis')
self.add_output('NodeSocketFloat', 'Angle')
# 1 argument: Module, Normalize, GetEuler
if (self.get_count_in(select_current) == 1):
self.add_input('NodeSocketVector', 'Quaternion')
if (select_current == 'Module'):
self.add_output('NodeSocketFloat', 'Module')
if select_current in ('Add','Subtract','Multiply','DotProduct') \
and select_prev in ('Add','Subtract','Multiply','DotProduct'):
pass # same as select_current==select_prev for the sockets
elif select_prev != select_current:
if select_current in ('Add','Subtract','Multiply','DotProduct'):
for i in range( max(len(self.inputs)//2 ,2) ):
self.ensure_input_socket(2*i, 'NodeSocketVector', 'Quaternion %d XYZ'%i)
self.ensure_input_socket(2*i+1, 'NodeSocketFloat', 'Quaternion %d W'%i, default_value=1.0)
if len(self.inputs)%1:
self.inputs.remove(self.inputs[len(self.inputs)-1])
elif select_current == 'MultiplyFloats':
self.ensure_input_socket(0, 'NodeSocketVector', 'Quaternion XYZ')
self.ensure_input_socket(1, 'NodeSocketFloat', 'Quaternion W', default_value=1.0)
for i in range( max(len(self.inputs)-2 ,1) ):
self.ensure_input_socket(i+2, 'NodeSocketFloat', 'Value %d'%i)
elif select_current in ('Module', 'Normalize'):
self.ensure_input_socket(0, 'NodeSocketVector', 'Quaternion XYZ')
self.ensure_input_socket(1, 'NodeSocketFloat', 'Quaternion W', default_value=1.0)
while len(self.inputs)>2:
self.inputs.remove(self.inputs[2])
else:
raise ValueError('Internal code of LNQuaternionMathNode failed to deal correctly with math operation "%s". Please report this to the developers.' %select_current)
if select_current in ('Add','Subtract','Multiply','MultiplyFloats','Normalize'):
self.outputs[0].name = 'XYZ Out'
self.outputs[1].name = 'W Out'
else:
self.outputs[0].name = '[unused]'
self.outputs[1].name = 'Value Out'
self['property0'] = value
self['property0_proxy'] = value
property0: EnumProperty(
items = [('Add', 'Add', 'Add'),
@ -139,30 +119,35 @@ class QuaternionMathNode(ArmLogicTreeNode):
('Multiply', 'Multiply', 'Multiply'),
('MultiplyFloats', 'Multiply (Floats)', 'Multiply (Floats)'),
('Module', 'Module', 'Module'),
('Normalize', 'Normalize', 'Normalize'),
('Lerp', 'Lerp', 'Linearly interpolate'),
('Slerp', 'Slerp', 'Spherical linear interpolation'),
('FromTo', 'From To', 'From To'),
('FromMat', 'From Mat', 'From Mat'),
('FromRotationMat', 'From Rotation Mat', 'From Rotation Mat'),
('ToAxisAngle', 'To Axis Angle', 'To Axis Angle'),
('FromAxisAngle', 'From Axis Angle', 'From Axis Angle'),
('FromEuler', 'From Euler', 'From Euler'),
('GetEuler', 'To Euler', 'To Euler')],
name='', default='Add', set=set_enum, get=get_enum)
('Normalize', 'Normalize', 'Normalize'), #],
# NOTE: the unused parts need to exist to be read from an old version from the node.
# this is so dumb…
('Lerp', 'DO NOT USE',''),
('Slerp', 'DO NOT USE',''),
('FromTo', 'DO NOT USE',''),
('FromMat', 'DO NOT USE',''),
('FromRotationMat', 'DO NOT USE',''),
('ToAxisAngle', 'DO NOT USE',''),
('FromAxisAngle', 'DO NOT USE',''),
('FromEuler', 'DO NOT USE',''),
('GetEuler', 'DO NOT USE','')],
name='', default='Add') #, set=set_enum, get=get_enum)
def __init__(self):
super(QuaternionMathNode, self).__init__()
array_nodes[str(id(self))] = self
def init(self, context):
super(QuaternionMathNode, self).init(context)
self.add_input('NodeSocketVector', 'Quaternion 0', default_value=[0.0, 0.0, 0.0])
self.add_input('NodeSocketVector', 'Quaternion 1', default_value=[0.0, 0.0, 0.0])
self.add_output('NodeSocketVector', 'Result')
self.add_input('NodeSocketVector', 'Quaternion 0 XYZ', default_value=[0.0, 0.0, 0.0])
self.add_input('NodeSocketFloat', 'Quaternion 0 W', default_value=1)
self.add_input('NodeSocketVector', 'Quaternion 1 XYZ', default_value=[0.0, 0.0, 0.0])
self.add_input('NodeSocketFloat', 'Quaternion 1 W', default_value=1)
self.add_output('NodeSocketVector', 'Result XYZ', default_value=[0.0, 0.0, 0.0])
self.add_output('NodeSocketFloat', 'Result W', default_value=1)
def draw_buttons(self, context, layout):
layout.prop(self, 'property1') # Separator Out
layout.prop(self, 'property0') # Operation
layout.prop(self, 'property0_proxy') # Operation
# Buttons
if (self.get_count_in(self.property0) == 0):
row = layout.row(align=True)
@ -170,15 +155,201 @@ class QuaternionMathNode(ArmLogicTreeNode):
op = column.operator('arm.node_add_input', text='Add Value', icon='PLUS', emboss=True)
op.node_index = str(id(self))
if (self.property0 == 'Add') or (self.property0 == 'Subtract') or (self.property0 == 'Multiply') or (self.property0 == 'DotProduct'):
op.name_format = 'Quaternion {0}'
op.name_format = 'Quaternion {0} XYZ;Quaternion {0} W'
else:
op.name_format = 'Value {0}'
if (self.property0 == "MultiplyFloats"):
op.socket_type = 'NodeSocketFloat'
else:
op.socket_type = 'NodeSocketVector'
op.socket_type = 'NodeSocketVector;NodeSocketFloat'
column = row.column(align=True)
op = column.operator('arm.node_remove_input', text='', icon='X', emboss=True)
op.node_index = str(id(self))
if self.property0 != "MultiplyFloats":
op.count = 2
op.min_inputs = 4
else:
op.min_inputs = 2
if len(self.inputs) == 2:
column.enabled = False
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.arm_version not in (0, 1):
raise LookupError()
ret=[]
if self.property0 == 'GetEuler':
newself = node_tree.nodes.new('LNSeparateRotationNode')
ret.append(newself)
newself.property0='EulerAngles'
newself.property2='XZY'
newself.property1='Rad'
for link in self.inputs[0].links: # 0 or 1
node_tree.links.new(link.from_socket, newself.inputs[0])
elif self.property0 == 'ToEuler':
newself = node_tree.nodes.new('LNRotationNode')
ret.append(newself)
preconv = node_tree.nodes.new('LNVectorNode')
ret.append(preconv)
newself.property0='EulerAngles'
newself.property2='XZY'
newself.property1='Rad'
node_tree.links.new(preconv.outputs[0], newself.inputs[0])
preconv.inputs[0].default_value = self.inputs[0].default_value
for link in self.inputs[0].links: # 0 or 1
node_tree.links.new(link.from_socket, preconv.inputs[0])
preconv.inputs[1].default_value = self.inputs[1].default_value
for link in self.inputs[1].links: # 0 or 1
node_tree.links.new(link.from_socket, preconv.inputs[1])
preconv.inputs[2].default_value = self.inputs[2].default_value
for link in self.inputs[2].links: # 0 or 1
node_tree.links.new(link.from_socket, preconv.inputs[2])
elif self.property0 == 'ToAxisAngle':
newself = node_tree.nodes.new('LNSeparateRotationNode')
ret.append(newself)
newself.property0='AxisAngle'
newself.property1='Rad'
for link in self.inputs[0].links: # 0 or 1
node_tree.links.new(link.from_node, newself.inputs[0])
elif self.property0 == 'FromAxisAngle':
newself = node_tree.nodes.new('LNRotationNode')
ret.append(newself)
newself.property0='AxisAngle'
newself.property1='Rad'
newself.inputs[0].default_value = self.inputs[1].default_value
for link in self.inputs[1].links: # 0 or 1
node_tree.links.new(link.from_node, newself.inputs[0])
newself.inputs[1].default_value = self.inputs[2].default_value
for link in self.inputs[2].links: # 0 or 1
node_tree.links.new(link.from_node, newself.inputs[1])
elif self.property0 in ('FromMat','FromRotationMat'):
newself = node_tree.nodes.new('LNSeparateTransformNode')
ret.append(newself)
for link in self.inputs[1].links: # 0 or 1
node_tree.links.new(link.from_node, newself.inputs[0])
elif self.property0 in ('Lerp','Slerp','FromTo'):
newself = node_tree.nodes.new('LNRotationMathNode')
ret.append(newself)
newself.property0 = self.property0
for in1, in2 in zip(self.inputs, newself.inputs):
if in1.bl_idname in ('NodeSocketFloat', 'NodeSocketVector'):
in2.default_value = in1.default_value
for link in in1.links:
node_tree.links.new(link.from_socket, in2)
else:
newself = node_tree.nodes.new('LNQuaternionMathNode')
ret.append(newself)
newself.property0 = self.property0
# convert the inputs… this is going to be hard lmao.
i_in_1 = 0
i_in_2 = 0
while i_in_1 < len(self.inputs):
in1 = self.inputs[i_in_1]
if in1.bl_idname == 'NodeSocketVector':
# quaternion input: now two sockets, not one.
convnode = node_tree.nodes.new('LNSeparateRotationNode')
convnode.property0 = 'Quaternion'
ret.append(convnode)
node_tree.links.new(convnode.outputs[0], newself.inputs[i_in_2])
node_tree.links.new(convnode.outputs[1], newself.inputs[i_in_2+1])
for link in in1.links:
node_tree.links.new(link.from_socket, convnode.inputs[0])
i_in_2 +=1
i_in_1 +=2
elif in1.bl_idname == 'NodeSocketfloat':
for link in in1.links:
node_tree.links.new(link.from_socket, newself.inputs[i_in_2])
else:
raise ValueError('get_replacement_node() for is not LNQuaternionMathNode V1->V2 is not prepared to deal with an input socket of type %s. This is a bug to report to the developers' %in1.bl_idname)
# #### now that the input has been dealt with, let's deal with the output.
if self.property0 in ('FromEuler','FromMat','FromRotationMat','FromAxisAngle','Lerp','Slerp','FromTo'):
# the new self returns a rotation
for link in self.outputs[0].links:
node_tree.links.new(newself.outputs[0], link.to_socket)
elif self.property0 in ('DotProduct','Module'):
# new self returns a float
for link in self.outputs[1 + 4*int(self.property1)].links:
node_tree.links.new(newself.outputs[1], link.to_socket)
elif self.property0 in ('GetEuler', 'ToAxisAngle'):
# new self returns misc.
for link in self.outputs[0].links:
node_tree.links.new(newself.outputs[0], link.to_socket)
if self.property0 == 'ToAxisAngle':
for link in self.outputs[1 + 4*int(self.property1)].links:
node_tree.links.new(newself.outputs[1], link.to_socket)
if self.property1:
xlinks = self.outputs[1].links
ylinks = self.outputs[2].links
zlinks = self.outputs[3].links
if len(xlinks)>0 or len(ylinks)>0 or len(zlinks)>0:
conv = node_tree.nodes.new('LNSeparateVectorNode')
ret.append(conv)
node_tree.links.new(newself.outputs[0], conv.inputs[0])
for link in xlinks:
node_tree.links.new(conv.outputs[0], link.to_socket)
for link in ylinks:
node_tree.links.new(conv.outputs[1], link.to_socket)
for link in zlinks:
node_tree.links.new(conv.outputs[2], link.to_socket)
else:
# new self returns a proper quaternion XYZ/W
outlinks = self.outputs[0].links
if len(outlinks)>0:
conv = node_tree.nodes.new('LNRotationNode')
conv.property0='Quaternion'
ret.append(conv)
node_tree.links.new(newself.outputs[0], conv.inputs[0])
node_tree.links.new(newself.outputs[1], conv.inputs[1])
for link in outlinks:
node_tree.links.new(conv.outputs[0], link.to_socket)
if self.property1:
for link in self.outputs[4].links: # for W
node_tree.links.new(newself.outputs[1], link.to_socket)
xlinks = self.outputs[1].links
ylinks = self.outputs[2].links
zlinks = self.outputs[3].links
if len(xlinks)>0 or len(ylinks)>0 or len(zlinks)>0:
conv = node_tree.nodes.new('LNSeparateVectorNode')
ret.append(conv)
node_tree.links.new(newself.outputs[0], conv.inputs[0])
for link in xlinks:
node_tree.links.new(conv.outputs[0], link.to_socket)
for link in ylinks:
node_tree.links.new(conv.outputs[1], link.to_socket)
for link in zlinks:
node_tree.links.new(conv.outputs[2], link.to_socket)
return ret
# note: keep property1, so that it is actually readable for node conversion.
property1: BoolProperty(name='DEPRECATED', default=False)
# this is the version of property0 that is shown in the interface,
# even though the real property0 is the one used elsewhere.
# NOTE FOR FUTURE MAINTAINERS: the value of this proxy property does **not** matter, only the value of property0 does.
property0_proxy: EnumProperty(
items = [('Add', 'Add', 'Add'),
('Subtract', 'Subtract', 'Subtract'),
('DotProduct', 'Dot Product', 'Dot Product'),
('Multiply', 'Multiply', 'Multiply'),
('MultiplyFloats', 'Multiply (Floats)', 'Multiply (Floats)'),
('Module', 'Module', 'Module'),
('Normalize', 'Normalize', 'Normalize')],
name='', default='Add', set=set_enum, get=get_enum)

View file

@ -4,6 +4,7 @@ class QuaternionMathNode(ArmLogicTreeNode):
"""Mathematical operations on rotations."""
bl_idname = 'LNRotationMathNode'
bl_label = 'Rotation Math'
bl_description = 'Mathematical operations that can be performed on rotations, no matter their internal representation'
arm_section = 'quaternions'
arm_version = 1
@ -97,7 +98,7 @@ class QuaternionMathNode(ArmLogicTreeNode):
property0: EnumProperty(
items = [('Compose', 'Compose (multiply)', 'compose (multiply) two rotations. Note that order of the composition matters.'),
('Amplify', 'Amplify (multiply by float)', 'Amplify or diminish the effect of a rotation'),
('Normalize', 'Normalize', 'Normalize'),
#('Normalize', 'Normalize', 'Normalize'),
('Inverse', 'Get Inverse', 'from r, get the rotation r2 so that " r×r2=r2×r= <no rotation>" '),
('Lerp', 'Lerp', 'Linearly interpolation'),
('Slerp', 'Slerp', 'Spherical linear interpolation'),

View file

@ -1,11 +1,14 @@
from arm.logicnode.arm_nodes import *
@deprecated(message='Do not use quaternion sockets')
class SeparateQuaternionNode(ArmLogicTreeNode):
"""Splits the given quaternion into X, Y, Z and W."""
bl_idname = 'LNSeparateQuaternionNode'
bl_label = "Separate Quaternion"
bl_label = "Separate Quaternion (do not use: quaternions sockets have been phased out entirely)"
bl_description = "Separate a quaternion object (transported through a vector socket) into its four compoents."
arm_section = 'quaternions'
arm_version = 1
arm_version = 2 # deprecate
def init(self, context):
super(SeparateQuaternionNode, self).init(context)
@ -15,3 +18,29 @@ class SeparateQuaternionNode(ArmLogicTreeNode):
self.add_output('NodeSocketFloat', 'Y')
self.add_output('NodeSocketFloat', 'Z')
self.add_output('NodeSocketFloat', 'W')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.arm_version not in (0, 1):
raise LookupError()
# transition from version 1 to version 2[deprecated]
newself = node_tree.nodes.new('LNSeparateRotationNode')
separator = node_tree.nodes.new('LNSeparateVectorNode')
newself.property0 = 'Quaternion'
newself.property1 = 'Rad' # bogus
newself.property2 = 'XYZ' # bogus
for link in self.inputs[0].links:
node_tree.links.new(link.from_socket, newself.inputs[0])
node_tree.links.new(newself.outputs[0], separator.inputs[0])
for link in self.outputs[0].links:
node_tree.links.new(separator.outputs[0], link.to_socket)
for link in self.outputs[1].links:
node_tree.links.new(separator.outputs[1], link.to_socket)
for link in self.outputs[2].links:
node_tree.links.new(separator.outputs[2], link.to_socket)
for link in self.outputs[3].links:
node_tree.links.new(newself.outputs[1], link.to_socket)
return [newself, separator]

View file

@ -5,7 +5,7 @@ class GetRotationNode(ArmLogicTreeNode):
bl_idname = 'LNGetRotationNode'
bl_label = 'Get Object Rotation'
arm_section = 'rotation'
arm_version = 1
arm_version = 2
def init(self, context):
super(GetRotationNode, self).init(context)
@ -19,3 +19,72 @@ class GetRotationNode(ArmLogicTreeNode):
items = [('Local', 'Local', 'Local'),
('Global', 'Global', 'Global')],
name='', default='Local')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.arm_version not in (0, 1):
raise LookupError()
# transition from version 1 to version 2: make rotations their own sockets
# this transition is a mess, I know.
newself = self.id_data.nodes.new('LNGetRotationNode')
newself.property0 = 'Local'
newnodes = [newself]
if len(self.outputs[0].links)>0:
# euler (radians) needed
converter = self.id_data.nodes.new('LNSeparateRotationNode')
converter.property0 = "EulerAngles"
converter.property1 = "Rad"
converter.property2 = "XZY"
newnodes.append(converter)
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
for link in self.outputs[0].links:
self.id_data.links.new(converter.outputs[0], link.to_socket)
if len(self.outputs[4].links)>0 or len(self.outputs[5].links)>0:
# quaternion needed
converter = self.id_data.nodes.new('LNSeparateRotationNode')
converter.property0 = "Quaternion"
newnodes.append(converter)
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
for link in self.outputs[4].links:
self.id_data.links.new(converter.outputs[0], link.to_socket)
for link in self.outputs[5].links:
self.id_data.links.new(converter.outputs[1], link.to_socket)
if len(self.outputs[1].links)>0 or len(self.outputs[2].links)>0 or len(self.outputs[3].links)>0:
# axis/angle needed
converter = self.id_data.nodes.new('LNSeparateRotationNode')
converter.property0 = "AxisAngle"
converter.property1 = "Rad"
newnodes.append(converter)
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
for link in self.outputs[1].links:
self.id_data.links.new(converter.outputs[0], link.to_socket)
if len(self.outputs[3].links)==0 and len(self.outputs[2].links)==0:
pass
elif len(self.outputs[3].links)==0:
for link in self.outputs[2].links:
self.id_data.links.new(converter.outputs[1], link.to_socket)
elif len(self.outputs[2].links)==0:
converter.property1 = 'Deg'
for link in self.outputs[3].links:
self.id_data.links.new(converter.outputs[1], link.to_socket)
else:
for link in self.outputs[2].links:
self.id_data.links.new(converter.outputs[1], link.to_socket)
converter = self.id_data.nodes.new('LNSeparateRotationNode')
converter.property0 = "AxisAngle"
converter.property1 = "Deg"
converter.property2 = "XYZ" # bogus
newnodes.append(converter)
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
for link in self.outputs[3].links:
self.id_data.links.new(converter.outputs[1], link.to_socket)
return newnodes

View file

@ -5,7 +5,7 @@ class LookAtNode(ArmLogicTreeNode):
bl_idname = 'LNLookAtNode'
bl_label = 'Look At'
arm_section = 'rotation'
arm_version = 1
arm_version = 2
property0: EnumProperty(
items = [('X', ' X', 'X'),
@ -25,3 +25,23 @@ class LookAtNode(ArmLogicTreeNode):
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.arm_version not in (0, 1):
raise LookupError()
# transition from version 1 to version 2: make rotations their own sockets
# this transition is a mess, I know.
newself = self.id_data.nodes.new('LNLookAtNode')
converter = self.id_data.nodes.new('LNSeparateRotationNode')
self.id_data.links.new(newself.outputs[0], converter.inputs[0])
converter.property0 = 'EulerAngles'
converter.property1 = 'Rad'
converter.property2 = 'XZY'
for link in self.outputs[0].links:
self.id_data.links.new(converter.outputs[0], link.to_socket)
return [newself, converter]

View file

@ -1,11 +1,12 @@
from arm.logicnode.arm_nodes import *
from arm.logicnode.arm_sockets import ArmRotationSocket as Rotation
class RotateObjectNode(ArmLogicTreeNode):
"""Rotates the given object."""
bl_idname = 'LNRotateObjectNode'
bl_label = 'Rotate Object'
arm_section = 'rotation'
arm_version = 1
arm_version = 2
def init(self, context):
super().init(context)
@ -20,5 +21,59 @@ class RotateObjectNode(ArmLogicTreeNode):
property0: EnumProperty(
items = [('Local', 'Local F.O.R.', 'Frame of reference oriented with the object'),
('Global', 'Global/Parent F.O.R.', 'Frame of reference oriented with the object\'s parent or the world')],
('Global', 'Global/Parent F.O.R.',
'Frame of reference oriented with the object\'s parent or the world')],
name='', default='Local')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.arm_version not in (0, 1):
raise LookupError()
# transition from version 1 to version 2: make rotations their own sockets
# this transition is a mess, I know.
newself = self.id_data.nodes.new('LNRotateObjectNode')
inputnode = self.id_data.nodes.new('LNRotationNode')
self.id_data.links.new(inputnode.outputs[0], newself.inputs[2])
newself.inputs[1].default_value_raw = self.inputs[1].default_value_raw
inputnode.inputs[0].default_value = self.inputs[2].default_value
inputnode.inputs[1].default_value = self.inputs[3].default_value
if len(self.inputs[0].links) >0:
self.id_data.links.new(self.inputs[0].links[0].from_socket, newself.inputs[0])
if len(self.inputs[1].links) >0:
self.id_data.links.new(self.inputs[1].links[0].from_socket, newself.inputs[1])
if len(self.inputs[2].links) >0:
self.id_data.links.new(self.inputs[2].links[0].from_socket, inputnode.inputs[0])
if len(self.inputs[3].links) >0:
self.id_data.links.new(self.inputs[3].links[0].from_socket, inputnode.inputs[1])
# first, convert the default value
if self.property0 == 'Quaternion':
inputnode.property0 = 'Quaternion'
elif self.property0 == 'Euler Angles':
inputnode.property0 = 'EulerAngles'
inputnode.property1 = 'Rad'
inputnode.property2 = 'XZY' # legacy order
else: # starts with "Angle Axies"
inputnode.property0 = 'AxisAngle'
if 'Degrees' in self.property0:
inputnode.property1 = 'Deg'
else:
inputnode.property1 = 'Rad'
quat = Rotation.convert_to_quaternion(
self.inputs[2].default_value,
self.inputs[3].default_value,
inputnode.property0,
inputnode.property1,
inputnode.property2
)
newself.inputs[2].default_value_raw = quat
return [newself, inputnode]

View file

@ -40,7 +40,7 @@ class SeparateRotationNode(ArmLogicTreeNode):
property0: EnumProperty(
items = [('EulerAngles', 'Euler Angles', 'Euler Angles'),
('AxisAngle', 'Axis/Angle', 'Axis/Angle'),
('Quat', 'Quaternion', 'Quaternion')],
('Quaternion', 'Quaternion', 'Quaternion')],
name='', default='EulerAngles',
update=on_property_update)

View file

@ -4,7 +4,7 @@ class SeparateTransformNode(ArmLogicTreeNode):
"""Separates the transform of the given object."""
bl_idname = 'LNSeparateTransformNode'
bl_label = 'Separate Transform'
arm_version = 1
arm_version = 2
def init(self, context):
super(SeparateTransformNode, self).init(context)
@ -13,3 +13,34 @@ class SeparateTransformNode(ArmLogicTreeNode):
self.add_output('NodeSocketVector', 'Location')
self.add_output('ArmNodeSocketRotation', 'Rotation')
self.add_output('NodeSocketVector', 'Scale')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.arm_version not in (0, 1):
raise LookupError()
# transition from version 1 to version 2: make rotations their own sockets
# this transition is a mess, I know.
newself = self.id_data.nodes.new('LNSeparateTransformNode')
for link in self.outputs[0].links:
self.id_data.links.new(newself.outputs[0], link.to_socket)
for link in self.outputs[2].links:
self.id_data.links.new(newself.outputs[2], link.to_socket)
for link in self.inputs[0].links:
self.id_data.links.new(link.from_socket, newself.inputs[0])
ret = [newself]
rot_links = self.outputs[1].links
if len(rot_links) >0:
converter = self.id_data.nodes.new('LNSeparateRotationNode')
ret.append(converter)
self.id_data.links.new(newself.outputs[1], converter.inputs[0])
converter.property0 = 'EulerAngles'
converter.property1 = 'Rad'
converter.property2 = 'XZY'
for link in rot_links:
self.id_data.links.new(converter.outputs[0], link.to_socket)
return ret

View file

@ -1,11 +1,12 @@
from arm.logicnode.arm_nodes import *
from arm.logicnode.arm_sockets import ArmRotationSocket as Rotation
class SetRotationNode(ArmLogicTreeNode):
"""Sets the rotation of the given object."""
bl_idname = 'LNSetRotationNode'
bl_label = 'Set Object Rotation'
arm_section = 'rotation'
arm_version = 1
arm_version = 2
def init(self, context):
@ -16,10 +17,61 @@ class SetRotationNode(ArmLogicTreeNode):
self.add_output('ArmNodeSocketAction', 'Out')
# def draw_buttons(self, context, layout):
# layout.prop(self, 'property0')
# property0: EnumProperty(
# items = [('Local', 'Local', 'Local'),
# ('Global', 'Global', 'Global')],
# name='', default='Local')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.arm_version not in (0, 1):
raise LookupError()
# transition from version 1 to version 2: make rotations their own sockets
# this transition is a mess, I know.
newself = self.id_data.nodes.new('LNRotateObjectNode')
inputnode = self.id_data.nodes.new('LNRotationNode')
self.id_data.links.new(inputnode.outputs[0], newself.inputs[2])
newself.inputs[1].default_value_raw = self.inputs[1].default_value_raw
inputnode.inputs[0].default_value = self.inputs[2].default_value
inputnode.inputs[1].default_value = self.inputs[3].default_value
if len(self.inputs[0].links) >0:
self.id_data.links.new(self.inputs[0].links[0].from_socket, newself.inputs[0])
if len(self.inputs[1].links) >0:
self.id_data.links.new(self.inputs[1].links[0].from_socket, newself.inputs[1])
if len(self.inputs[2].links) >0:
self.id_data.links.new(self.inputs[2].links[0].from_socket, inputnode.inputs[0])
if len(self.inputs[3].links) >0:
self.id_data.links.new(self.inputs[3].links[0].from_socket, inputnode.inputs[1])
# first, convert the default value
if self.property0 == 'Quaternion':
inputnode.property0 = 'Quaternion'
elif self.property0 == 'Euler Angles':
inputnode.property0 = 'EulerAngles'
inputnode.property1 = 'Rad'
inputnode.property2 = 'XZY' # legacy order
elif self.property0.startswith("Angle Axies "):
inputnode.property0 = 'AxisAngle'
if 'Degrees' in self.property0:
inputnode.property1 = 'Deg'
else:
inputnode.property1 = 'Rad'
else:
raise ValueError('nonsensical value {:s} for property0 in SetObjectRotationNode/V1. please report this to the devs.'.format(self.property0))
quat = Rotation.convert_to_quaternion(
self.inputs[2].default_value,
self.inputs[3].default_value,
inputnode.property0,
inputnode.property1,
inputnode.property2
)
newself.inputs[2].default_value_raw = quat
return [newself, inputnode]
# note: this is unused, but kept here so that the 'property0' field can be read during node replacement
property0: EnumProperty(
items = [('Euler Angles', '',''),
('Angle Axies (Radians)', '', ''),
('Angle Axies (Degrees)', '', ''),
('Quaternion', '', '')],
name='', default='Euler Angles')

View file

@ -4,7 +4,7 @@ class TransformNode(ArmLogicTreeNode):
"""Stores the location, rotation and scale values as a transform."""
bl_idname = 'LNTransformNode'
bl_label = 'Transform'
arm_version = 1
arm_version = 2
def init(self, context):
super(TransformNode, self).init(context)
@ -13,3 +13,34 @@ class TransformNode(ArmLogicTreeNode):
self.add_input('NodeSocketVector', 'Scale', default_value=[1.0, 1.0, 1.0])
self.add_output('NodeSocketShader', 'Transform', is_var=True)
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.arm_version not in (0, 1):
raise LookupError()
# transition from version 1 to version 2: make rotations their own sockets
# this transition is a mess, I know.
newself = self.id_data.nodes.new('LNTransformNode')
ret = [newself]
for link in self.inputs[0].links:
self.id_data.links.new(link.from_socket, newself.inputs[0])
for link in self.inputs[2].links:
self.id_data.links.new(link.from_socket, newself.inputs[2])
for link in self.outputs[0].links:
self.id_data.links.new(newself.outputs[0], link.to_socket)
links_rot = self.inputs[1].links
if len(links_rot) > 0:
converter = self.id_data.nodes.new('LNRotationNode')
self.id_data.links.new(converter.outputs[0], newself.inputs[1])
converter.property0 = 'EulerAngles'
converter.property1 = 'Rad'
converter.property2 = 'XZY'
ret.append(converter)
for link in links_rot:
self.id_data.links.new(link.from_socket, converter.inputs[0])
return ret

View file

@ -1,11 +1,14 @@
from arm.logicnode.arm_nodes import *
from mathutils import Vector
@deprecated(message='Do not use quaternion sockets')
class QuaternionNode(ArmLogicTreeNode):
"""TO DO."""
bl_idname = 'LNQuaternionNode'
bl_label = 'Quaternion'
bl_label = 'Create a quaternion variable (transported through a vector socket)'
arm_section = 'quaternions'
arm_version = 1
arm_version = 2 # deprecate
def init(self, context):
super(QuaternionNode, self).init(context)
@ -17,3 +20,57 @@ class QuaternionNode(ArmLogicTreeNode):
self.add_output('NodeSocketVector', 'Quaternion')
self.add_output('NodeSocketVector', 'XYZ')
self.add_output('NodeSocketFloat', 'W')
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
if self.arm_version not in (0, 1):
raise LookupError()
# transition from version 1 to version 2[deprecated]
newnodes = []
rawlinks = self.outputs[0].links
xyzlinks = self.outputs[1].links
wlinks = self.outputs[2].links
if len(rawlinks)>0 or len(xyzlinks)>0:
xyzcomb = node_tree.nodes.new('LNVectorNode')
newnodes.append(xyzcomb)
xyzcomb.inputs[0].default_value = self.inputs[0].default_value
xyzcomb.inputs[1].default_value = self.inputs[1].default_value
xyzcomb.inputs[2].default_value = self.inputs[2].default_value
for link in self.inputs[0].links:
node_tree.links.new(link.from_socket, xyzcomb.inputs[0])
for link in self.inputs[1].links:
node_tree.links.new(link.from_socket, xyzcomb.inputs[1])
for link in self.inputs[2].links:
node_tree.links.new(link.from_socket, xyzcomb.inputs[2])
for link in xyzlinks:
node_tree.links.new(xyzcomb.outputs[0], link.to_socket)
if len(rawlinks)>0:
rotnode = node_tree.nodes.new('LNRotationNode')
newnodes.append(rotnode)
rotnode.property0 = 'Quaternion'
rotnode.inputs[0].default_value = Vector(
(self.inputs[0].default_value,
self.inputs[1].default_value,
self.inputs[2].default_value))
rotnode.inputs[1].default_value = self.inputs[3].default_value
node_tree.links.new(xyzcomb.outputs[0], rotnode.inputs[0])
for link in self.inputs[3].links: # 0 or 1
node_tree.links.new(link.from_socket, rotnode.inputs[1])
for link in rawlinks:
node_tree.links.new(rotnode.outputs[0], link.to_socket)
if len(self.inputs[3].links)>0:
fromval = self.inputs[3].links[0].from_socket
for link in self.outputs[2].links:
node_tree.links.new(fromval, link.to_socket)
else:
for link in self.outputs[2].links:
link.to_socket.default_value = self.inputs[3].default_value
return newnodes

View file

@ -4,6 +4,7 @@ class RotationNode(ArmLogicTreeNode):
"""A rotation, created from one of its possible mathematical representations"""
bl_idname = 'LNRotationNode'
bl_label = 'Rotation'
bl_description = 'Create a Rotation object, describing the difference between two orientations (internally represented as a quaternion for efficiency)'
#arm_section = 'rotation'
arm_version = 1