diff --git a/Sources/armory/logicnode/VectorMathNode.hx b/Sources/armory/logicnode/VectorMathNode.hx index 88455735..74d5f9b3 100644 --- a/Sources/armory/logicnode/VectorMathNode.hx +++ b/Sources/armory/logicnode/VectorMathNode.hx @@ -4,49 +4,136 @@ import iron.math.Vec4; class VectorMathNode extends LogicNode { - public var property0: String; - var v = new Vec4(); + public var property0: String; // Operation + public var property1: Bool; // Separator Out + var res_v = new Vec4(); + var res_f = 0.0; public function new(tree: LogicTree) { super(tree); } override function get(from: Int): Dynamic { - var v1: Vec4 = inputs[0].get(); - var v2: Vec4 = inputs[1].get(); - if (v1 == null || v2 == null) return null; - v.setFrom(v1); - var f = 0.0; + var p1: Vec4 = inputs[0].get(); + if (p1 == null) return null; + res_v.setFrom(p1); switch (property0) { - case "Add": - v.add(v2); - case "Subtract": - v.sub(v2); - case "Average": - v.add(v2); - v.x *= 0.5; - v.y *= 0.5; - v.z *= 0.5; - case "Dot Product": - f = v.dot(v2); - v.set(f, f, f); - case "Cross Product": - v.cross(v2); - case "Normalize": - v.normalize(); - case "Multiply": - v.x *= v2.x; - v.y *= v2.y; - v.z *= v2.z; - case "Length": - f = v.length(); - case "Distance": - f = v.distanceTo(v2); - case "Reflect": - v.reflect(v2); + // 1 arguments: Normalize, Length + case "Normalize": + res_v.normalize(); + case "Length": + res_f = res_v.length(); + // 2 arguments: Distance, Reflect + case "Distance": { + var p2: Vec4 = inputs[1].get(); + if (p2 == null) return null; + res_f = res_v.distanceTo(p2); + } + case "Reflect": { + var p2: Vec4 = inputs[1].get(); + if (p2 == null) return null; + res_v.reflect(p2); + } + // Many arguments: Add, Subtract, Average, Dot Product, Cross Product, Multiply + case "Add": { + var p2 = new Vec4(); + var i = 1; + while (i < inputs.length) { + p2 = inputs[i].get(); + if (p2 == null) return null; + res_v.add(p2); + i++; + } + } + case "Subtract": { + var p2 = new Vec4(); + var i = 1; + while (i < inputs.length) { + p2 = inputs[i].get(); + if (p2 == null) return null; + res_v.sub(p2); + i++; + } + } + case "Average": { + var p2 = new Vec4(); + var i = 1; + while (i < inputs.length) { + p2 = inputs[i].get(); + if (p2 == null) return null; + res_v.add(p2); + res_v.mult(0.5); + i++; + } + } + case "Dot Product": { + var p2 = new Vec4(); + var i = 1; + while (i < inputs.length) { + p2 = inputs[i].get(); + if (p2 == null) return null; + res_f = res_v.dot(p2); + res_v.set(res_f, res_f, res_f); + i++; + } + } + case "Cross Product": { + var p2 = new Vec4(); + var i = 1; + while (i < inputs.length) { + p2 = inputs[i].get(); + if (p2 == null) return null; + res_v.cross(p2); + i++; + } + } + case "Multiply": { + var p2 = new Vec4(); + var i = 1; + while (i < inputs.length) { + p2 = inputs[i].get(); + if (p2 == null) return null; + res_v.x *= p2.x; + res_v.y *= p2.y; + res_v.z *= p2.z; + i++; + } + } + case "MultiplyFloats": { + var p2_f = 1.0; + var i = 1; + while (i < inputs.length) { + p2_f = inputs[i].get(); + if (p2_f == null) return null; + res_v.mult(p2_f); + i++; + } + } } - - if (from == 0) return v; - else return f; + // Return and check separator + switch (from) { + case 0: return res_v; + case 1: + if (property1) { + return res_v.x; + } else { + return res_f; + } + case 2: + if (property1) { + return res_v.y; + } else { + return res_f; + } + case 3: + if (property1) { + return res_v.z; + } else { + return res_f; + } + case 4: + if (property1) return res_f; + } + return null; } -} +} \ No newline at end of file diff --git a/blender/arm/logicnode/math/LN_vector_math.py b/blender/arm/logicnode/math/LN_vector_math.py index ab133712..558ec11a 100644 --- a/blender/arm/logicnode/math/LN_vector_math.py +++ b/blender/arm/logicnode/math/LN_vector_math.py @@ -1,32 +1,146 @@ from arm.logicnode.arm_nodes import * class VectorMathNode(ArmLogicTreeNode): - """Operates vectors. Some operations uses only the first input.""" + """Mathematical operations on vectors.""" bl_idname = 'LNVectorMathNode' bl_label = 'Vector Math' arm_section = 'vector' arm_version = 1 + min_outputs = 2 + max_outputs = 5 + + def get_bool(self): + return self.get('property1', False) + + def set_bool(self, value): + self['property1'] = value + if value: + if (self.property0 == 'Length') or (self.property0 == 'Distance') or (self.property0 == 'Dot Product'): + self.outputs.remove(self.outputs.values()[-1]) # Distance/Length/Scalar + self.add_output('NodeSocketFloat', 'X') # Result X + self.add_output('NodeSocketFloat', 'Y') # Result Y + self.add_output('NodeSocketFloat', 'Z') # Result Z + if (self.property0 == 'Length'): + self.add_output('NodeSocketFloat', 'Length') # Length + if (self.property0 == 'Distance'): + self.add_output('NodeSocketFloat', 'Distance') # Distance + if (self.property0 == 'Dot Product'): + self.add_output('NodeSocketFloat', 'Scalar') # Scalar + else: + if (self.property0 == 'Length') or (self.property0 == 'Distance') or (self.property0 == 'Dot Product'): + self.outputs.remove(self.outputs.values()[-1]) # Distance/Length/Scalar + 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 + if (self.property0 == 'Length'): + self.add_output('NodeSocketFloat', 'Length') # Length + if (self.property0 == 'Distance'): + self.add_output('NodeSocketFloat', 'Distance') # Distance + if (self.property0 == 'Dot Product'): + self.add_output('NodeSocketFloat', 'Scalar') # Scalar + + 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, + 'Average': 0, + 'Dot Product': 0, + 'Cross Product': 0, + 'Multiply': 0, + 'MultiplyFloats': 0, + 'Distance': 2, + 'Reflect': 2, + 'Normalize': 1, + 'Length': 1 + }.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: + if (select_prev == 'Distance') or (select_prev == 'Length') or (self.property0 == 'Dot Product'): + self.outputs.remove(self.outputs.values()[-1]) + # Many arguments: Add, Subtract, Average, Dot Product, Cross Product, Multiply, MultiplyFloats + if (self.get_count_in(select_current) == 0): + if (select_current == "MultiplyFloats") or (select_prev == "MultiplyFloats"): + while (len(self.inputs) > 1): + self.inputs.remove(self.inputs.values()[-1]) + if (select_current == "MultiplyFloats"): + self.add_input('NodeSocketFloat', 'Value ' + str(len(self.inputs))) + else: + while (len(self.inputs) < 2): + self.add_input('NodeSocketVector', 'Value ' + str(len(self.inputs))) + if (select_current == 'Dot Product'): + self.add_output('NodeSocketFloat', 'Scalar') + # 2 arguments: Distance, Reflect + if (self.get_count_in(select_current) == 2): + count = 2 + if (select_prev == "MultiplyFloats"): + count = 1 + while (len(self.inputs) > count): + self.inputs.remove(self.inputs.values()[-1]) + while (len(self.inputs) < 2): + self.add_input('NodeSocketVector', 'Value ' + str(len(self.inputs))) + if (select_current == 'Distance'): + self.add_output('NodeSocketFloat', 'Distance') + # 1 argument: Normalize, Length + if (self.get_count_in(select_current) == 1): + while (len(self.inputs) > 1): + self.inputs.remove(self.inputs.values()[-1]) + if (select_current == 'Length'): + self.add_output('NodeSocketFloat', 'Length') + self['property0'] = value property0: EnumProperty( items = [('Add', 'Add', 'Add'), ('Dot Product', 'Dot Product', 'Dot Product'), ('Multiply', 'Multiply', 'Multiply'), + ('MultiplyFloats', 'Multiply (Floats)', 'Multiply (Floats)'), ('Normalize', 'Normalize', 'Normalize'), ('Subtract', 'Subtract', 'Subtract'), ('Average', 'Average', 'Average'), ('Cross Product', 'Cross Product', 'Cross Product'), ('Length', 'Length', 'Length'), ('Distance', 'Distance', 'Distance'), - ('Reflect', 'Reflect', 'Reflect'), - ], - name='', default='Add') + ('Reflect', 'Reflect', 'Reflect')], + name='', default='Add', set=set_enum, get=get_enum) + + def __init__(self): + array_nodes[str(id(self))] = self def init(self, context): super(VectorMathNode, self).init(context) - self.add_input('NodeSocketVector', 'Vector 1', default_value=[0.0, 0.0, 0.0]) - self.add_input('NodeSocketVector', 'Vector 2', default_value=[0.0, 0.0, 0.0]) + self.add_input('NodeSocketVector', 'Value 0', default_value=[0.0, 0.0, 0.0]) + self.add_input('NodeSocketVector', 'Value 1', default_value=[0.0, 0.0, 0.0]) self.add_output('NodeSocketVector', 'Result') - self.add_output('NodeSocketFloat', 'Distance') def draw_buttons(self, context, layout): - layout.prop(self, 'property0') + 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)) + 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 \ No newline at end of file