Quat Math
1. Move the Quaternion node from Transform to Variable 2. Move the Separate Quaternion node from Transform to Math 3. Added Quaternion Math Node Linked to PR - https://github.com/armory3d/iron/pull/108
This commit is contained in:
parent
d210cea6d8
commit
6b804477b1
190
Sources/armory/logicnode/QuaternionMathNode.hx
Normal file
190
Sources/armory/logicnode/QuaternionMathNode.hx
Normal file
|
@ -0,0 +1,190 @@
|
|||
package armory.logicnode;
|
||||
|
||||
import iron.math.Quat;
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Mat4;
|
||||
import kha.FastFloat;
|
||||
|
||||
class QuaternionMathNode extends LogicNode {
|
||||
|
||||
public var property0: String; // Operation
|
||||
public var property1: Bool; // Separator Out
|
||||
var res_q = new Quat();
|
||||
var res_v = new Vec4();
|
||||
var res_f: FastFloat = 0.0;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
switch (property0) {
|
||||
// 1 argument: Module, Normalize, GetEuler
|
||||
case "Module": {
|
||||
var q: Quat = inputs[0].get();
|
||||
if (q == null) return null;
|
||||
res_q.setFrom(q);
|
||||
res_f = res_q.module();
|
||||
}
|
||||
case "Normalize": {
|
||||
var q: Quat = inputs[0].get();
|
||||
if (q == null) return null;
|
||||
res_q.setFrom(q);
|
||||
res_q = res_q.normalize();
|
||||
}
|
||||
case "GetEuler": {
|
||||
var q: Quat = inputs[0].get();
|
||||
if (q == null) return null;
|
||||
res_q.setFrom(q);
|
||||
res_v = res_q.getEuler();
|
||||
}
|
||||
// 2 arguments: FromTo, FromMat, FromRotationMat, ToAxisAngle
|
||||
case "FromTo": {
|
||||
var v1: Vec4 = inputs[0].get();
|
||||
var v2: Vec4 = inputs[1].get();
|
||||
if ((v1 == null) || (v2 == null)) return null;
|
||||
res_q.fromTo(v1, v2);
|
||||
}
|
||||
case "FromMat": {
|
||||
var q: Quat = inputs[0].get();
|
||||
var m: Mat4 = inputs[1].get();
|
||||
if ((q == null) || (m == null)) return null;
|
||||
res_q.setFrom(q);
|
||||
res_q = res_q.fromMat(m);
|
||||
}
|
||||
case "FromRotationMat": {
|
||||
var q: Quat = inputs[0].get();
|
||||
var m: Mat4 = inputs[1].get();
|
||||
if ((q == null) || (m == null)) return null;
|
||||
res_q.setFrom(q);
|
||||
res_q = res_q.fromRotationMat(m);
|
||||
}
|
||||
case "ToAxisAngle": {
|
||||
var q: Quat = inputs[0].get();
|
||||
var v: Vec4 = inputs[1].get();
|
||||
if ((q == null) || (v == null)) return null;
|
||||
res_q.setFrom(q);
|
||||
res_f = res_q.toAxisAngle(v);
|
||||
}
|
||||
// # 3 arguments: Lerp, Slerp, FromAxisAngle, FromEuler
|
||||
case "Lerp": {
|
||||
var from: Quat = inputs[0].get();
|
||||
var to: Quat = inputs[1].get();
|
||||
var f: Float = inputs[2].get();
|
||||
if ((from == null) || (to == null)) return null;
|
||||
res_q = res_q.lerp(from, to, f);
|
||||
}
|
||||
case "Slerp": {
|
||||
var from: Quat = inputs[0].get();
|
||||
var to: Quat = inputs[1].get();
|
||||
var f: Float = inputs[2].get();
|
||||
if ((from == null) || (to == null)) return null;
|
||||
res_q = res_q.slerp(from, to, f);
|
||||
}
|
||||
case "FromAxisAngle": {
|
||||
var q: Quat = inputs[0].get();
|
||||
var axis: Vec4 = inputs[1].get();
|
||||
var angle: Float = inputs[2].get();
|
||||
if ((q == null) || (axis == null)) return null;
|
||||
res_q.setFrom(q);
|
||||
res_q = res_q.fromAxisAngle(axis, angle);
|
||||
}
|
||||
case "FromEuler": {
|
||||
var x: Float = inputs[0].get();
|
||||
var y: Float = inputs[1].get();
|
||||
var z: Float = inputs[2].get();
|
||||
res_q = res_q.fromEuler(x, y, z);
|
||||
}
|
||||
// Many arguments: Add, Subtract, DotProduct, Multiply
|
||||
case "Add": {
|
||||
var q: Quat = inputs[0].get();
|
||||
if (q == null) return null;
|
||||
res_q.setFrom(q);
|
||||
var q2 = new Quat();
|
||||
var i = 1;
|
||||
while (i < inputs.length) {
|
||||
q2 = inputs[i].get();
|
||||
if (q2 == null) return null;
|
||||
res_q.add(q2);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
case "Subtract": {
|
||||
var q: Quat = inputs[0].get();
|
||||
if (q == null) return null;
|
||||
res_q.setFrom(q);
|
||||
var q2 = new Quat();
|
||||
var i = 1;
|
||||
while (i < inputs.length) {
|
||||
q2 = inputs[i].get();
|
||||
if (q2 == null) return null;
|
||||
res_q.sub(q2);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
case "Multiply": {
|
||||
var q: Quat = inputs[0].get();
|
||||
if (q == null) return null;
|
||||
res_q.setFrom(q);
|
||||
var q2 = new Quat();
|
||||
var i = 1;
|
||||
while (i < inputs.length) {
|
||||
q2 = inputs[i].get();
|
||||
if (q2 == null) return null;
|
||||
res_q.mult(q2);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
case "MultiplyFloats": {
|
||||
var q: Quat = inputs[0].get();
|
||||
if (q == null) return null;
|
||||
res_q.setFrom(q);
|
||||
var f: Float = 1.0;
|
||||
var i = 1;
|
||||
while (i < inputs.length) {
|
||||
f = inputs[i].get();
|
||||
res_q.scale(f);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
case "DotProduct": {
|
||||
var q: Quat = inputs[0].get();
|
||||
if (q == null) return null;
|
||||
res_q.setFrom(q);
|
||||
var q2 = new Quat();
|
||||
var i = 1;
|
||||
while (i < inputs.length) {
|
||||
q2 = inputs[i].get();
|
||||
if (q2 == null) return null;
|
||||
res_f = res_q.dot(q2);
|
||||
res_q.set(res_f, res_f, res_f, res_f);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return and check separator
|
||||
switch (from) {
|
||||
case 0: {
|
||||
if (property0 == 'GetEuler')
|
||||
return res_v;
|
||||
else
|
||||
return res_q;
|
||||
}
|
||||
case 1:
|
||||
if (property1) {
|
||||
return res_q.x;
|
||||
} else {
|
||||
return res_f;
|
||||
}
|
||||
case 2:
|
||||
if (property1) return res_q.y;
|
||||
case 3:
|
||||
if (property1) return res_q.z;
|
||||
case 4:
|
||||
if (property1) return res_q.w;
|
||||
case 5:
|
||||
if (property1) return res_f;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
182
blender/arm/logicnode/math/LN_quaternion_math.py
Normal file
182
blender/arm/logicnode/math/LN_quaternion_math.py
Normal file
|
@ -0,0 +1,182 @@
|
|||
from arm.logicnode.arm_nodes import *
|
||||
|
||||
class QuaternionMathNode(ArmLogicTreeNode):
|
||||
"""Mathematical operations on quaternions."""
|
||||
bl_idname = 'LNQuaternionMathNode'
|
||||
bl_label = 'Quaternion Math'
|
||||
arm_section = 'quaternions'
|
||||
arm_version = 1
|
||||
|
||||
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'):
|
||||
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
|
||||
else:
|
||||
if (self.property0 == 'Module') or (self.property0 == 'DotProduct') or (self.property0 == 'ToAxisAngle'):
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Module/DotProduct/ToAxisAngle
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Result X
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Result Y
|
||||
self.outputs.remove(self.outputs.values()[-1]) # Result Z
|
||||
self.outputs.remove(self.outputs.values()[-1]) # 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
|
||||
|
||||
property1: BoolProperty(name='Separator Out', default=False, set=set_bool, get=get_bool)
|
||||
|
||||
@staticmethod
|
||||
def get_enum_id_value(obj, prop_name, value):
|
||||
return obj.bl_rna.properties[prop_name].enum_items[value].identifier
|
||||
|
||||
@staticmethod
|
||||
def get_count_in(operation_name):
|
||||
return {
|
||||
'Add': 0,
|
||||
'Subtract': 0,
|
||||
'DotProduct': 0,
|
||||
'Multiply': 0,
|
||||
'MultiplyFloats': 0,
|
||||
'Module': 1,
|
||||
'Normalize': 1,
|
||||
'GetEuler': 1,
|
||||
'FromTo': 2,
|
||||
'FromMat': 2,
|
||||
'FromRotationMat': 2,
|
||||
'ToAxisAngle': 2,
|
||||
'Lerp': 3,
|
||||
'Slerp': 3,
|
||||
'FromAxisAngle': 3,
|
||||
'FromEuler': 3
|
||||
}.get(operation_name, 0)
|
||||
|
||||
def get_enum(self):
|
||||
return self.get('property0', 0)
|
||||
|
||||
def set_enum(self, value):
|
||||
# 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')
|
||||
self['property0'] = value
|
||||
|
||||
property0: 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'),
|
||||
('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)
|
||||
|
||||
def __init__(self):
|
||||
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')
|
||||
|
||||
def draw_buttons(self, context, layout):
|
||||
layout.prop(self, 'property1') # Separator Out
|
||||
layout.prop(self, 'property0') # Operation
|
||||
# Buttons
|
||||
if (self.get_count_in(self.property0) == 0):
|
||||
row = layout.row(align=True)
|
||||
column = row.column(align=True)
|
||||
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}'
|
||||
else:
|
||||
op.name_format = 'Value {0}'
|
||||
if (self.property0 == "MultiplyFloats"):
|
||||
op.socket_type = 'NodeSocketFloat'
|
||||
else:
|
||||
op.socket_type = 'NodeSocketVector'
|
||||
column = row.column(align=True)
|
||||
op = column.operator('arm.node_remove_input', text='', icon='X', emboss=True)
|
||||
op.node_index = str(id(self))
|
||||
if len(self.inputs) == 2:
|
||||
column.enabled = False
|
Loading…
Reference in a new issue