Added node replacement routines for previous commits
(and fixed a couple bugs along the way)
This commit is contained in:
parent
9b1393ea41
commit
bd67667a6e
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue