Did the haxe part of the ongoing update.

Also fixed a ton of bugs in the python part.
NOTE: this requires a yet-to-be-done commit to Iron to work.
This commit is contained in:
niacdoial 2021-08-17 19:29:35 +02:00
parent bd67667a6e
commit 1d0a6d7955
No known key found for this signature in database
GPG key ID: D2B9B9FE6D7B1C8E
13 changed files with 151 additions and 163 deletions

View file

@ -19,7 +19,7 @@ class QuaternionMathNode extends LogicNode {
override function get(from: Int): Dynamic {
switch (property0) {
// 1 argument: Module, Normalize, GetEuler
// 1 argument: Module, Normalize
case "Module": {
var q: Quat = inputs[0].get();
if (q == null) return null;
@ -32,159 +32,106 @@ class QuaternionMathNode extends LogicNode {
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();
res_v = inputs[0].get();
res_f = inputs[1].get();
if (res_v == null || res_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var i = 1;
while (i < inputs.length) {
q2 = inputs[i].get();
if (q2 == null) return null;
res_q.add(q2);
while (2*i+1 < inputs.length) {
res_v = inputs[2*i].get();
res_f = inputs[2*i+1].get();
if (res_v == null || res_f == null) return null;
res_q.x += res_v.x;
res_q.y += res_v.y;
res_q.z += res_v.z;
res_q.w += res_f;
i++;
}
}
case "Subtract": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
var q2 = new Quat();
res_v = inputs[0].get();
res_f = inputs[1].get();
if (res_v == null || res_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var i = 1;
while (i < inputs.length) {
q2 = inputs[i].get();
if (q2 == null) return null;
res_q.sub(q2);
while (2*i+1 < inputs.length) {
res_v = inputs[2*i].get();
res_f = inputs[2*i+1].get();
if (res_v == null || res_f == null) return null;
res_q.x -= res_v.x;
res_q.y -= res_v.y;
res_q.z -= res_v.z;
res_q.w -= res_f;
i++;
}
}
case "Multiply": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
var q2 = new Quat();
res_v = inputs[0].get();
res_f = inputs[1].get();
if (res_v == null || res_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var i = 1;
while (i < inputs.length) {
q2 = inputs[i].get();
if (q2 == null) return null;
res_q.mult(q2);
while (2*i+1 < inputs.length) {
res_v = inputs[2*i].get();
res_f = inputs[2*i+1].get();
if (res_v == null || res_f == null) return null;
var temp_q = new Quat(res_v.x, res_v.y, res_v.z, res_f);
res_q.mult(temp_q);
i++;
}
}
case "MultiplyFloats": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
res_v = inputs[0].get();
res_f = inputs[1].get();
if (res_v == null || res_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, res_f);
var f: Float = 1.0;
var i = 1;
var i = 2;
while (i < inputs.length) {
f = inputs[i].get();
res_q.scale(f);
f *= inputs[i].get();
if (f == null) return null;
i++;
}
res_q.scale(f);
}
case "DotProduct": {
var q: Quat = inputs[0].get();
if (q == null) return null;
res_q.setFrom(q);
var q2 = new Quat();
case "DotProduct": { // what this does with more than 2 terms is not *remotely* intuitive. Heck, you could consider it a footgun!
res_v = inputs[0].get();
var temp_f = inputs[1].get();
if (res_v == null || temp_f == null) return null;
res_q.set(res_v.x, res_v.y, res_v.z, temp_f);
var i = 1;
while (i < inputs.length) {
q2 = inputs[i].get();
if (q2 == null) return null;
res_f = res_q.dot(q2);
while (2*i+1 < inputs.length) {
res_v = inputs[2*i].get();
temp_f = inputs[2*i+1].get();
if (res_v == null || temp_f == null) return null;
var temp_q = new Quat(res_v.x, res_v.y, res_v.z, temp_f);
res_f = res_q.dot(temp_q);
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;
return res_q;
}
case 1:
if (property1) {
return res_q.x;
} else {
if (property0 == "DotProduct" || property0 == "Module") {
return res_f;
} else {
return null;
}
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;
default: {
return null;
}
}
return null;
}
}

View file

@ -17,17 +17,21 @@ class RotationMathNode extends LogicNode {
}
override function get(from: Int): Dynamic {
var q: Quat = inputs[0].get();
if (q==null) return null;
//var q: Quat = inputs[0].get();
//if (q==null) return null;
var res_q: Quat = new Quat();
//var res_q: Quat = new Quat();
switch (property0) {
// 1 argument: Normalize, Inverse
case "Normalize": {
var q: Quat = inputs[0].get();
if (q==null) return null;
res_q.setFrom(q);
res_q = res_q.normalize();
}
case "Inverse": {
var q: Quat = inputs[0].get();
if (q==null) return null;
var modl = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
modl = -1/modl;
res_q.w = -q.w*modl;
@ -72,17 +76,19 @@ class RotationMathNode extends LogicNode {
// # 3 arguments: Lerp, Slerp, FromAxisAngle, FromEuler
case "Lerp": {
//var from = q;
var from: Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((f == null) || (to == null)) return null;
res_q = res_q.lerp(q, to, f);
if ((from == null) || (f == null) || (to == null)) return null;
res_q = res_q.lerp(from, to, f);
}
case "Slerp": {
//var from = q;
var from:Quat = inputs[0].get();
var to: Quat = inputs[1].get();
var f: Float = inputs[2].get();
if ((f == null) || (to == null)) return null;
res_q = res_q.slerp(q, to, f);
if ((from == null) || (f == null) || (to == null)) return null;
res_q = res_q.slerp(from, to, f);
}
}
return res_q;

View file

@ -38,6 +38,9 @@ class RotationNode extends LogicNode {
//var inp0 = inputs[0].get();
//var inp
//if (inputs[0].get())
if (inputs.length == 0){
return this.value;
}
switch (property0){
case "Quaternion": {

View file

@ -50,7 +50,7 @@ class SeparateRotationNode extends LogicNode {
case "Deg": return toDEG*aa_angle_cache;
}
}
case "Quat":
case "Quaternion":
switch(from){
case 0: return new Vec4(q.x,q.y,q.z);
case 1: return q.w;

View file

@ -7,6 +7,8 @@ import armory.trait.physics.RigidBody;
class SetRotationNode extends LogicNode {
public var property0: String; // UNUSED
public function new(tree: LogicTree) {
super(tree);
}

View file

@ -6,6 +6,7 @@ from typing import OrderedDict as ODict # Prevent naming conflicts
import bpy.types
from bpy.props import *
from nodeitems_utils import NodeItem
from arm.logicnode.arm_sockets import ArmCustomSocket
# Pass NodeReplacment forward to individual node modules that import arm_nodes
from arm.logicnode.replacement import NodeReplacement
@ -83,7 +84,13 @@ class ArmLogicTreeNode(bpy.types.Node):
socket = self.inputs.new(socket_type, socket_name)
if default_value is not None:
socket.default_value = default_value
if isinstance(socket, ArmCustomSocket):
if socket.arm_socket_type != 'NONE':
socket.default_value_raw = default_value
else:
raise ValueError('specified a default value for an input node that doesn\'t accept one')
else:
socket.default_value = default_value
if is_var and not socket.display_shape.endswith('_DOT'):
socket.display_shape += '_DOT'

View file

@ -97,7 +97,7 @@ class ArmRotationSocket(ArmCustomSocket):
def on_mode_update(self, context):
if self.default_value_mode == 'Quat':
if self.default_value_mode == 'Quaternion':
summ = abs(self.default_value_s0)
summ+= abs(self.default_value_s1)
summ+= abs(self.default_value_s2)
@ -116,10 +116,10 @@ class ArmRotationSocket(ArmCustomSocket):
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'),
``param1`` is in ('Quaternion', 'EulerAngles', 'AxisAngle'),
``param2`` is in ('Rad','Deg') for both EulerAngles and AxisAngle,
``param3`` is a len-3 string like "XYZ", for EulerAngles """
if param1 == 'Quat':
if param1=='Quaternion':
qx, qy, qz = part1[0], part1[1], part1[2]
qw = part2
# need to normalize the quaternion for a rotation (having it be 0 is not an option)
@ -183,7 +183,7 @@ class ArmRotationSocket(ArmCustomSocket):
part1,
self.default_value_s3,
self.default_value_mode,
self.defualt_value_unit,
self.default_value_unit,
self.default_value_order
)
@ -205,7 +205,7 @@ class ArmRotationSocket(ArmCustomSocket):
coll.prop(self, 'default_value_s0', text='X')
coll.prop(self, 'default_value_s1', text='Y')
coll.prop(self, 'default_value_s2', text='Z')
elif self.default_value_mode == 'Quat':
elif self.default_value_mode == 'Quaternion':
coll.prop(self, 'default_value_s0', text='X')
coll.prop(self, 'default_value_s1', text='Y')
coll.prop(self, 'default_value_s2', text='Z')
@ -223,7 +223,7 @@ class ArmRotationSocket(ArmCustomSocket):
default_value_mode: EnumProperty(
items=[('EulerAngles', 'Euler Angles', 'Euler Angles'),
('AxisAngle', 'Axis/Angle', 'Axis/Angle'),
('Quat', 'Quaternion', 'Quaternion')],
('Quaternion', 'Quaternion', 'Quaternion')],
name='', default='EulerAngles',
update=on_mode_update)

View file

@ -1,4 +1,5 @@
from arm.logicnode.arm_nodes import *
from arm.logicnode.arm_sockets import ArmRotationSocket as Rotation
class QuaternionMathNode(ArmLogicTreeNode):
"""Mathematical operations on quaternions."""
@ -194,7 +195,7 @@ class QuaternionMathNode(ArmLogicTreeNode):
for link in self.inputs[0].links: # 0 or 1
node_tree.links.new(link.from_socket, newself.inputs[0])
elif self.property0 == 'ToEuler':
elif self.property0 == 'FromEuler':
newself = node_tree.nodes.new('LNRotationNode')
ret.append(newself)
preconv = node_tree.nodes.new('LNVectorNode')
@ -220,7 +221,7 @@ class QuaternionMathNode(ArmLogicTreeNode):
newself.property1='Rad'
for link in self.inputs[0].links: # 0 or 1
node_tree.links.new(link.from_node, newself.inputs[0])
node_tree.links.new(link.from_socket, newself.inputs[0])
elif self.property0 == 'FromAxisAngle':
newself = node_tree.nodes.new('LNRotationNode')
ret.append(newself)
@ -229,22 +230,28 @@ class QuaternionMathNode(ArmLogicTreeNode):
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])
node_tree.links.new(link.from_socket, 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])
node_tree.links.new(link.from_socket, 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])
node_tree.links.new(link.from_socket, 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'):
if in2.bl_idname == 'ArmNodeSocketRotation':
in2.default_value_raw = Rotation.convert_to_quaternion(
in1.default_value,0,
'EulerAngles','Rad','XZY'
)
elif 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)
@ -264,15 +271,20 @@ class QuaternionMathNode(ArmLogicTreeNode):
convnode = node_tree.nodes.new('LNSeparateRotationNode')
convnode.property0 = 'Quaternion'
ret.append(convnode)
if i_in_2 >= len(newself.inputs):
newself.ensure_input_socket(i_in_2, 'NodeSocketVector', 'Quaternion %d XYZ'%(i_in_1))
newself.ensure_input_socket(i_in_2+1, 'NodeSocketFloat', 'Quaternion %d W'%(i_in_1), 1.0)
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':
i_in_2 +=2
i_in_1 +=1
elif in1.bl_idname == 'NodeSocketFloat':
for link in in1.links:
node_tree.links.new(link.from_socket, newself.inputs[i_in_2])
i_in_1 +=1
i_in_2 +=1
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)
@ -281,7 +293,8 @@ class QuaternionMathNode(ArmLogicTreeNode):
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)
out_sock_i = int( self.property0.endswith('Mat') )
node_tree.links.new(newself.outputs[out_sock_i], 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:
@ -328,13 +341,22 @@ class QuaternionMathNode(ArmLogicTreeNode):
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)
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)
for node in ret: # update the labels on the node's displays
if node.bl_idname == 'LNSeparateRotationNode':
node.on_property_update(None)
elif node.bl_idname == 'LNRotationNode':
node.on_property_update(None)
elif node.bl_idname == 'LNRotationMathNode':
node.on_update_operation(None)
elif node.bl_idname == 'LNQuaternionMathNode':
node.set_enum(node.get_enum())
return ret

View file

@ -1,6 +1,7 @@
from arm.logicnode.arm_nodes import *
from mathutils import Vector
class QuaternionMathNode(ArmLogicTreeNode):
class RotationMathNode(ArmLogicTreeNode):
"""Mathematical operations on rotations."""
bl_idname = 'LNRotationMathNode'
bl_label = 'Rotation Math'
@ -54,7 +55,7 @@ class QuaternionMathNode(ArmLogicTreeNode):
for socket in sink_sockets:
self.id_data.links.new(self.inputs[socket_number], socket)
def on_update_operation(self, context):
def on_property_update(self, context):
# Checking the selection of another operation
@ -73,13 +74,13 @@ class QuaternionMathNode(ArmLogicTreeNode):
self.ensure_input_socket(2, "NodeSocketFloat", "Interpolation factor")
elif self.property0 == 'FromTo':
self.ensure_input_socket(0, "ArmNodeSocketRotation", "From")
self.ensure_input_socket(1, "ArmNodeSocketRotation", "To")
self.ensure_input_socket(0, "NodeSocketVector", "From")
self.ensure_input_socket(1, "NodeSocketVector", "To")
# Rotation as argument 1:
if self.property0 in ('Compose','Lerp','Slerp'):
if self.inputs[1].bl_idname != "ArmNodeSocketRotation":
self.replace_input_socket(1, "ArmNodeSocketRotation", "Quaternion 2")
self.replace_input_socket(1, "ArmNodeSocketRotation", "Rotation 2")
if self.property0 == 'Compose':
self.inputs[1].name = "Inner quaternion"
# Float as argument 1:
@ -105,15 +106,15 @@ class QuaternionMathNode(ArmLogicTreeNode):
('FromTo', 'From To', 'From direction To direction'),
#('FromRotationMat', 'From Rotation Mat', 'From Rotation Mat')
],
name='', default='Compose', update=on_update_operation)
name='', default='Compose', update=on_property_update)
#def __init__(self):
# array_nodes[str(id(self))] = self
def init(self, context):
super(QuaternionMathNode, self).init(context)
self.add_input('ArmNodeSocketRotation', 'Quaternion 0', default_value=[0.0, 0.0, 0.0])
self.add_input('ArmNodeSocketRotation', 'Quaternion 1', default_value=[0.0, 0.0, 0.0])
super(RotationMathNode, self).init(context)
self.add_input('ArmNodeSocketRotation', 'Outer rotation', default_value=Vector((0.0, 0.0, 0.0, 1.0)) )
self.add_input('ArmNodeSocketRotation', 'Inner rotation', default_value=Vector((0.0, 0.0, 0.0, 1.0)))
self.add_output('ArmNodeSocketRotation', 'Result')
def draw_buttons(self, context, layout):

View file

@ -17,13 +17,13 @@ class SeparateRotationNode(ArmLogicTreeNode):
def on_property_update(self, context):
"""called by the EnumProperty, used to update the node socket labels"""
if self.property0 == "Quat":
if self.property0 == 'Quaternion':
self.outputs[0].name = "Quaternion XYZ"
self.outputs[1].name = "Quaternion W"
elif self.property0 == "EulerAngles":
elif self.property0 == 'EulerAngles':
self.outputs[0].name = "Euler Angles"
self.outputs[1].name = "[unused for Euler output]"
elif self.property0.startswith("AxisAngle"):
elif self.property0 == 'AxisAngle':
self.outputs[0].name = "Axis"
self.outputs[1].name = "Angle"
else: