From 5ed5b8d6a756bab362704a5be31c86fac6ec532e Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 17 Feb 2021 19:55:18 +0100 Subject: [PATCH 01/81] Nodes for IK and FK fix Edit logic node code fkcommit30_04 Upgrade bone IK node. Add comments --- Sources/armory/logicnode/BoneFKNode.hx | 15 ++++--- Sources/armory/logicnode/BoneIKNode.hx | 23 +++++++++-- blender/arm/logicnode/animation/LN_bone_ik.py | 41 +++++++++++++++++-- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/Sources/armory/logicnode/BoneFKNode.hx b/Sources/armory/logicnode/BoneFKNode.hx index 8d8cabb1..06541fe8 100644 --- a/Sources/armory/logicnode/BoneFKNode.hx +++ b/Sources/armory/logicnode/BoneFKNode.hx @@ -28,17 +28,20 @@ class BoneFKNode extends LogicNode { // Manipulating bone in world space var bone = anim.getBone(boneName); - m = anim.getBoneMat(bone); - w = anim.getAbsMat(bone); + /* m = anim.getBoneMat(bone); + w = anim.getAbsMat(bone); */ function moveBone() { - m.setFrom(w); + /* m.setFrom(w); m.multmat(transform); iw.getInverse(w); - m.multmat(iw); + m.multmat(iw); */ + //trace("Perform FK"); + anim.setBoneMatFromWorldMat(transform.clone(), bone); - // anim.removeUpdate(moveBone); - // notified = false; + anim.removeUpdate(moveBone); + //trace("FK removed"); + notified = false; } if (!notified) { diff --git a/Sources/armory/logicnode/BoneIKNode.hx b/Sources/armory/logicnode/BoneIKNode.hx index de187f0a..c22d77c7 100644 --- a/Sources/armory/logicnode/BoneIKNode.hx +++ b/Sources/armory/logicnode/BoneIKNode.hx @@ -7,6 +7,13 @@ import iron.math.Vec4; class BoneIKNode extends LogicNode { var goal: Vec4; + var pole: Vec4; + var poleEnabled: Bool; + var chainLength: Int; + var maxIterartions: Int; + var precision: Float; + var rollAngle: Float; + var notified = false; public function new(tree: LogicTree) { @@ -19,6 +26,12 @@ class BoneIKNode extends LogicNode { var object: Object = inputs[1].get(); var boneName: String = inputs[2].get(); goal = inputs[3].get(); + poleEnabled = inputs[4].get(); + pole = inputs[5].get(); + chainLength = inputs[6].get(); + maxIterartions = inputs[7].get(); + precision = inputs[8].get(); + rollAngle = inputs[9].get(); if (object == null || goal == null) return; var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null; @@ -26,11 +39,13 @@ class BoneIKNode extends LogicNode { var bone = anim.getBone(boneName); - function solveBone() { - anim.solveIK(bone, goal); + if(! poleEnabled) pole = null; - // anim.removeUpdate(solveBone); - // notified = false; + function solveBone() { + anim.solveIK(bone, goal, precision, maxIterartions, chainLength, pole, rollAngle); + + anim.removeUpdate(solveBone); + notified = false; } if (!notified) { diff --git a/blender/arm/logicnode/animation/LN_bone_ik.py b/blender/arm/logicnode/animation/LN_bone_ik.py index dd155015..83a55fc9 100644 --- a/blender/arm/logicnode/animation/LN_bone_ik.py +++ b/blender/arm/logicnode/animation/LN_bone_ik.py @@ -1,10 +1,29 @@ from arm.logicnode.arm_nodes import * class BoneIKNode(ArmLogicTreeNode): - """Applies inverse kinematics in the given object bone.""" + """Performs inverse kinematics on the selected armature with specified bone. + + @input Object: Armature on which IK should be performed. + + @input Bone: Effector or tip bone for the inverse kinematics + + @input Goal Position: Position in world coordinates the effector bone will track to + + @input Enable Pole: Bend IK solution towards pole location + + @input Pole Position: Location of the pole in world coordinates + + @input Chain Length: Number of bones to include in the IK solver including the effector. If set to 0, all bones from effector to the root bone of the armature will be considered. + + @input Max Iterations: Maximum allowed FABRIK iterations to solve for IK. For longer chains, more iterations are needed. + + @input Precision: Presition of IK to stop at. It is described as a tolerence in length. Typically 0.01 is a good value. + + @input Roll Angle: Roll the bones along their local axis with specified radians. set 0 for no extra roll. + """ bl_idname = 'LNBoneIKNode' bl_label = 'Bone IK' - arm_version = 1 + arm_version = 2 arm_section = 'armature' def init(self, context): @@ -12,6 +31,22 @@ class BoneIKNode(ArmLogicTreeNode): self.add_input('ArmNodeSocketAction', 'In') self.add_input('ArmNodeSocketObject', 'Object') self.add_input('NodeSocketString', 'Bone') - self.add_input('NodeSocketVector', 'Goal') + self.add_input('NodeSocketVector', 'Goal Position') + self.add_input('NodeSocketBool', 'Enable Pole') + self.add_input('NodeSocketVector', 'Pole Position') + self.add_input('NodeSocketInt', 'Chain Length') + self.add_input('NodeSocketInt', 'Max Iterations', 10) + self.add_input('NodeSocketFloat', 'Precision', 0.01) + self.add_input('NodeSocketFloat', 'Roll Angle') + self.add_output('ArmNodeSocketAction', 'Out') + + def get_replacement_node(self, node_tree: bpy.types.NodeTree): + if self.arm_version not in (0, 1): + raise LookupError() + + return NodeReplacement( + 'LNBoneIKNode', self.arm_version, 'LNBoneIKNode', 2, + in_socket_mapping={0:0, 1:1, 2:2, 3:3}, out_socket_mapping={0:0} + ) From 646d3a74cf617dddbf6779f77e739c9ac6d0d8ed Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 20 May 2021 18:04:18 +0200 Subject: [PATCH 02/81] Create new node to get if bones are FK IK --- .../logicnode/animation/LN_get_bone_fk_ik_only.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 blender/arm/logicnode/animation/LN_get_bone_fk_ik_only.py diff --git a/blender/arm/logicnode/animation/LN_get_bone_fk_ik_only.py b/blender/arm/logicnode/animation/LN_get_bone_fk_ik_only.py new file mode 100644 index 00000000..165b946d --- /dev/null +++ b/blender/arm/logicnode/animation/LN_get_bone_fk_ik_only.py @@ -0,0 +1,14 @@ +from arm.logicnode.arm_nodes import * + +class GetBoneFkIkOnlyNode(ArmLogicTreeNode): + """Get if a particular bone is animated by Forward kinematics or Inverse kinematics only.""" + bl_idname = 'LNGetBoneFkIkOnlyNode' + bl_label = 'Get Bone FK IK Only' + arm_version = 1 + arm_section = 'armature' + + def init(self, context): + super(GetBoneFkIkOnlyNode, self).init(context) + self.add_input('ArmNodeSocketObject', 'Object') + self.add_input('NodeSocketString', 'Bone') + self.add_output('NodeSocketBool', 'FK or IK only') \ No newline at end of file From 86316a10bd64bffd8bb3b4baca85b03c85ea32b0 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 20 May 2021 18:04:45 +0200 Subject: [PATCH 03/81] Create new node to get world transforms of bones --- .../logicnode/animation/LN_get_bone_transform.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 blender/arm/logicnode/animation/LN_get_bone_transform.py diff --git a/blender/arm/logicnode/animation/LN_get_bone_transform.py b/blender/arm/logicnode/animation/LN_get_bone_transform.py new file mode 100644 index 00000000..0d21ea23 --- /dev/null +++ b/blender/arm/logicnode/animation/LN_get_bone_transform.py @@ -0,0 +1,14 @@ +from arm.logicnode.arm_nodes import * + +class GetBoneTransformNode(ArmLogicTreeNode): + """Returns bone transform in world space.""" + bl_idname = 'LNGetBoneTransformNode' + bl_label = 'Get Bone Transform' + arm_version = 1 + arm_section = 'armature' + + def init(self, context): + super(GetBoneTransformNode, self).init(context) + self.add_input('ArmNodeSocketObject', 'Object') + self.add_input('NodeSocketString', 'Bone') + self.add_output('NodeSocketShader', 'Transform') \ No newline at end of file From e7c1855b81f1803c44cf353ac658ed0f2f38d0aa Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 20 May 2021 18:05:35 +0200 Subject: [PATCH 04/81] Create new node to set if bones are FK IK only --- .../animation/LN_set_bone_fk_ik_only.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 blender/arm/logicnode/animation/LN_set_bone_fk_ik_only.py diff --git a/blender/arm/logicnode/animation/LN_set_bone_fk_ik_only.py b/blender/arm/logicnode/animation/LN_set_bone_fk_ik_only.py new file mode 100644 index 00000000..2594183a --- /dev/null +++ b/blender/arm/logicnode/animation/LN_set_bone_fk_ik_only.py @@ -0,0 +1,17 @@ +from arm.logicnode.arm_nodes import * + +class SetBoneFkIkOnlyNode(ArmLogicTreeNode): + """Set particular bone to be animated by Forward kinematics or Inverse kinematics only. All other animations will be ignored""" + bl_idname = 'LNSetBoneFkIkOnlyNode' + bl_label = 'Set Bone FK IK Only' + arm_version = 1 + arm_section = 'armature' + + def init(self, context): + super(SetBoneFkIkOnlyNode, self).init(context) + self.add_input('ArmNodeSocketAction', 'In') + self.add_input('ArmNodeSocketObject', 'Object') + self.add_input('NodeSocketString', 'Bone') + self.add_input('NodeSocketBool', 'FK or IK only') + + self.add_output('ArmNodeSocketAction', 'Out') From 6ded7e61f4778f48c1cc70d19213913a17eec665 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 20 May 2021 18:06:45 +0200 Subject: [PATCH 05/81] Improve bone FK node to accept transform in world space --- Sources/armory/logicnode/BoneFKNode.hx | 28 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Sources/armory/logicnode/BoneFKNode.hx b/Sources/armory/logicnode/BoneFKNode.hx index 06541fe8..9d72c876 100644 --- a/Sources/armory/logicnode/BoneFKNode.hx +++ b/Sources/armory/logicnode/BoneFKNode.hx @@ -1,5 +1,7 @@ package armory.logicnode; +import iron.math.Quat; +import iron.math.Vec4; import iron.object.Object; import iron.object.BoneAnimation; import iron.math.Mat4; @@ -26,21 +28,27 @@ class BoneFKNode extends LogicNode { var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null; if (anim == null) anim = object.getParentArmature(object.name); - // Manipulating bone in world space + // Get bone in armature var bone = anim.getBone(boneName); - /* m = anim.getBoneMat(bone); - w = anim.getAbsMat(bone); */ function moveBone() { - /* m.setFrom(w); - m.multmat(transform); - iw.getInverse(w); - m.multmat(iw); */ - //trace("Perform FK"); - anim.setBoneMatFromWorldMat(transform.clone(), bone); + + var t2 = Mat4.identity(); + var loc= new Vec4(); + var rot = new Quat(); + var scl = new Vec4(); + //Set scale to Armature scale. Bone scaling not yet implemented + t2.setFrom(transform); + t2.decompose(loc, rot, scl); + scl = object.transform.world.getScale(); + t2.compose(loc, rot, scl); + + //Set the bone local transform from world transform + anim.setBoneMatFromWorldMat(t2, bone); + + //Remove this method from animation loop after FK anim.removeUpdate(moveBone); - //trace("FK removed"); notified = false; } From 43e145c3dae472d780fc8be535d339d39d14be41 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 20 May 2021 18:07:09 +0200 Subject: [PATCH 06/81] Add comments to bone IK node --- Sources/armory/logicnode/BoneIKNode.hx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/armory/logicnode/BoneIKNode.hx b/Sources/armory/logicnode/BoneIKNode.hx index c22d77c7..1f1cfd76 100644 --- a/Sources/armory/logicnode/BoneIKNode.hx +++ b/Sources/armory/logicnode/BoneIKNode.hx @@ -42,8 +42,10 @@ class BoneIKNode extends LogicNode { if(! poleEnabled) pole = null; function solveBone() { + //Solve IK anim.solveIK(bone, goal, precision, maxIterartions, chainLength, pole, rollAngle); + //Remove this method from animation loop after IK anim.removeUpdate(solveBone); notified = false; } From c53d04374d651b2ad66a01624edc1447f63df25c Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 20 May 2021 18:07:46 +0200 Subject: [PATCH 07/81] Implement node to get if controlled by FK or IK --- .../armory/logicnode/GetBoneFkIkOnlyNode.hx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx diff --git a/Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx b/Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx new file mode 100644 index 00000000..14c96489 --- /dev/null +++ b/Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx @@ -0,0 +1,31 @@ +package armory.logicnode; + +import iron.object.Object; +import iron.object.BoneAnimation; + +class GetBoneFkIkOnlyNode extends LogicNode { + + public function new(tree: LogicTree) { + super(tree); + } + + override function get(from: Int): Bool { + #if arm_skin + + var object: Object = inputs[0].get(); + var boneName: String = inputs[1].get(); + + if (object == null) return null; + var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null; + if (anim == null) anim = object.getParentArmature(object.name); + + // Get bone in armature + var bone = anim.getBone(boneName); + + //Get bone transform in world coordinates + return bone.is_IK_FK_only; + + + #end + } +} From 03baa49ecdd6b5160303dda27344e01a77fa5cfd Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 20 May 2021 18:08:11 +0200 Subject: [PATCH 08/81] Implement node to get bone transform in world space --- .../armory/logicnode/GetBoneTransformNode.hx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Sources/armory/logicnode/GetBoneTransformNode.hx diff --git a/Sources/armory/logicnode/GetBoneTransformNode.hx b/Sources/armory/logicnode/GetBoneTransformNode.hx new file mode 100644 index 00000000..098fd519 --- /dev/null +++ b/Sources/armory/logicnode/GetBoneTransformNode.hx @@ -0,0 +1,30 @@ +package armory.logicnode; + +import iron.object.Object; +import iron.object.BoneAnimation; +import iron.math.Mat4; + +class GetBoneTransformNode extends LogicNode { + + public function new(tree: LogicTree) { + super(tree); + } + + override function get(from: Int): Mat4 { + #if arm_skin + + var object: Object = inputs[0].get(); + var boneName: String = inputs[1].get(); + + if (object == null) return null; + var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null; + if (anim == null) anim = object.getParentArmature(object.name); + + // Get bone in armature + var bone = anim.getBone(boneName); + + return anim.getAbsWorldMat(bone); + + #end + } +} From a35cab548200de7b67c4d0aae2a1e49833540974 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 20 May 2021 18:08:43 +0200 Subject: [PATCH 09/81] Implement node to set bone animated by FK or IK only --- .../armory/logicnode/SetBoneFkIkOnlyNode.hx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx diff --git a/Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx b/Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx new file mode 100644 index 00000000..927fc84e --- /dev/null +++ b/Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx @@ -0,0 +1,35 @@ +package armory.logicnode; + +import iron.math.Quat; +import iron.math.Vec4; +import iron.object.Object; +import iron.object.BoneAnimation; + +class SetBoneFkIkOnlyNode extends LogicNode { + + public function new(tree: LogicTree) { + super(tree); + } + + override function run(from: Int) { + #if arm_skin + + var object: Object = inputs[1].get(); + var boneName: String = inputs[2].get(); + var fk_ik_only: Bool = inputs[3].get(); + + if (object == null) return; + var anim = object.animation != null ? cast(object.animation, BoneAnimation) : null; + if (anim == null) anim = object.getParentArmature(object.name); + + // Get bone in armature + var bone = anim.getBone(boneName); + + //Set bone animated by FK or IK only + bone.is_IK_FK_only = fk_ik_only; + + runOutput(0); + + #end + } +} From 20c6c52ae64823673435e2f93f1b10f2f4e57641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Sat, 22 May 2021 00:36:58 +0200 Subject: [PATCH 10/81] Merge node: add execution mode and output for input index --- Sources/armory/logicnode/MergeNode.hx | 20 +++++++++++++ blender/arm/logicnode/logic/LN_merge.py | 37 +++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/Sources/armory/logicnode/MergeNode.hx b/Sources/armory/logicnode/MergeNode.hx index 60eb6ca8..c2a22e6a 100644 --- a/Sources/armory/logicnode/MergeNode.hx +++ b/Sources/armory/logicnode/MergeNode.hx @@ -2,11 +2,31 @@ package armory.logicnode; class MergeNode extends LogicNode { + /** Execution mode. **/ + public var property0: String; + + var lastInputIndex = -1; + public function new(tree: LogicTree) { super(tree); + tree.notifyOnLateUpdate(lateUpdate); } override function run(from: Int) { + // Check if there already were executions on the same frame + if (lastInputIndex != -1 && property0 == "once_per_frame") { + return; + } + + lastInputIndex = from; runOutput(0); } + + override function get(from: Int): Dynamic { + return lastInputIndex; + } + + function lateUpdate() { + lastInputIndex = -1; + } } diff --git a/blender/arm/logicnode/logic/LN_merge.py b/blender/arm/logicnode/logic/LN_merge.py index 5c3b8a9f..0f4e6544 100644 --- a/blender/arm/logicnode/logic/LN_merge.py +++ b/blender/arm/logicnode/logic/LN_merge.py @@ -1,7 +1,23 @@ from arm.logicnode.arm_nodes import * + class MergeNode(ArmLogicTreeNode): - """Activates the output when any connected input is activated. + """Activates the output when at least one connected input is activated. + If multiple inputs are active, the behaviour is specified by the + `Execution Mode` option. + + @output Active Input Index: [*Available if Execution Mode is set to + Once Per Input*] The index of the last input that activated the output, + -1 if there was no execution yet on the current frame. + + @option Execution Mode: The node's behaviour if multiple inputs are + active on the same frame. + + - `Once Per Input`: If multiple inputs are active on one frame, activate + the output for each active input individually (simple forwarding). + + - `Once Per Frame`: If multiple inputs are active on one frame, + trigger the output only once. @option New: Add a new input socket. @option X Button: Remove the lowermost input socket.""" @@ -10,6 +26,21 @@ class MergeNode(ArmLogicTreeNode): arm_section = 'flow' arm_version = 1 + def update_exec_mode(self, context): + self.outputs['Active Input Index'].hide = self.property0 == 'once_per_frame' + + property0: EnumProperty( + name='Execution Mode', + description='The node\'s behaviour if multiple inputs are active on the same frame', + items=[('once_per_input', 'Once Per Input', + 'If multiple inputs are active on one frame, activate the' + ' output for each active input individually (simple forwarding)'), + ('once_per_frame', 'Once Per Frame', + 'If multiple inputs are active on one frame, trigger the output only once')], + default='once_per_input', + update=update_exec_mode, + ) + def __init__(self): super(MergeNode, self).__init__() array_nodes[str(id(self))] = self @@ -17,10 +48,12 @@ class MergeNode(ArmLogicTreeNode): def init(self, context): super(MergeNode, self).init(context) self.add_output('ArmNodeSocketAction', 'Out') + self.add_output('NodeSocketInt', 'Active Input Index') def draw_buttons(self, context, layout): - row = layout.row(align=True) + layout.prop(self, 'property0', text='') + row = layout.row(align=True) op = row.operator('arm.node_add_input', text='New', icon='PLUS', emboss=True) op.node_index = str(id(self)) op.socket_type = 'ArmNodeSocketAction' From 494e2336ac22ce549b6dc85f431c7ba59610b8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Sat, 22 May 2021 12:28:57 +0200 Subject: [PATCH 11/81] Node replacement: write original traceback to file also in case of update failures --- blender/arm/logicnode/replacement.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/blender/arm/logicnode/replacement.py b/blender/arm/logicnode/replacement.py index d062a7bc..abd016e5 100644 --- a/blender/arm/logicnode/replacement.py +++ b/blender/arm/logicnode/replacement.py @@ -254,7 +254,8 @@ def replace_all(): print(f"A node whose class doesn't exist was found in node tree \"{tree_name}\"", file=reportf) elif error_type == 'update failed': print(f"A node of type {node_class} in tree \"{tree_name}\" failed to be updated, " - f"because there is no (longer?) an update routine for this version of the node.", file=reportf) + f"because there is no (longer?) an update routine for this version of the node. Original exception:" + "\n" + tb + "\n", file=reportf) elif error_type == 'future version': print(f"A node of type {node_class} in tree \"{tree_name}\" seemingly comes from a future version of armory. " f"Please check whether your version of armory is up to date", file=reportf) From 11da953407b497fa9fe88dae02025910903ccdb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Sat, 22 May 2021 12:30:38 +0200 Subject: [PATCH 12/81] Merge node: add update routine --- blender/arm/logicnode/logic/LN_merge.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/blender/arm/logicnode/logic/LN_merge.py b/blender/arm/logicnode/logic/LN_merge.py index 0f4e6544..ce42ac80 100644 --- a/blender/arm/logicnode/logic/LN_merge.py +++ b/blender/arm/logicnode/logic/LN_merge.py @@ -24,7 +24,7 @@ class MergeNode(ArmLogicTreeNode): bl_idname = 'LNMergeNode' bl_label = 'Merge' arm_section = 'flow' - arm_version = 1 + arm_version = 2 def update_exec_mode(self, context): self.outputs['Active Input Index'].hide = self.property0 == 'once_per_frame' @@ -65,3 +65,24 @@ class MergeNode(ArmLogicTreeNode): return self.bl_label return f'{self.bl_label}: [{len(self.inputs)}]' + + def get_replacement_node(self, node_tree: bpy.types.NodeTree): + if self.arm_version not in (0, 1): + raise LookupError() + + newnode = node_tree.nodes.new('LNMergeNode') + newnode.property0 = self.property0 + + # Recreate all original inputs + array_nodes[str(id(newnode))] = newnode + for idx, input in enumerate(self.inputs): + bpy.ops.arm.node_add_input('EXEC_DEFAULT', node_index=str(id(newnode)), socket_type='ArmNodeSocketAction') + + for link in input.links: + node_tree.links.new(link.from_socket, newnode.inputs[idx]) + + # Recreate outputs + for link in self.outputs[0].links: + node_tree.links.new(newnode.outputs[0], link.to_socket) + + return newnode From 7b5294121d37ac6f12c07faf6efa2af09c91fe2b Mon Sep 17 00:00:00 2001 From: Lubos Lenco Date: Sun, 23 May 2021 22:19:48 +0200 Subject: [PATCH 13/81] is_IK_FK_only -> is_ik_fk_only --- Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx b/Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx index 927fc84e..4d9b59fb 100644 --- a/Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx +++ b/Sources/armory/logicnode/SetBoneFkIkOnlyNode.hx @@ -26,7 +26,7 @@ class SetBoneFkIkOnlyNode extends LogicNode { var bone = anim.getBone(boneName); //Set bone animated by FK or IK only - bone.is_IK_FK_only = fk_ik_only; + bone.is_ik_fk_only = fk_ik_only; runOutput(0); From bdc2d91c1eb30c0981f125d99f018e4c033c8d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Wed, 26 May 2021 12:42:11 +0200 Subject: [PATCH 14/81] Fix instancing on mobile and solid renderpaths `gl_Position` was set before the instancing code in the vertex shader, this was the same issue as already fixed for desktop renderpaths in https://github.com/armory3d/armory/pull/2141 --- blender/arm/material/make_mesh.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/blender/arm/material/make_mesh.py b/blender/arm/material/make_mesh.py index 00932ef0..275fa0e0 100644 --- a/blender/arm/material/make_mesh.py +++ b/blender/arm/material/make_mesh.py @@ -296,8 +296,6 @@ def make_forward_mobile(con_mesh): vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);') frag.ins = vert.outs - make_attrib.write_vertpos(vert) - frag.add_include('compiled.inc') frag.write('vec3 basecol;') frag.write('float roughness;') @@ -344,6 +342,8 @@ def make_forward_mobile(con_mesh): make_attrib.write_norpos(con_mesh, vert) frag.write_attrib('vec3 n = normalize(wnormal);') + make_attrib.write_vertpos(vert) + frag.add_include('std/math.glsl') frag.add_include('std/brdf.glsl') @@ -474,8 +474,6 @@ def make_forward_solid(con_mesh): vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);') frag.ins = vert.outs - make_attrib.write_vertpos(vert) - frag.add_include('compiled.inc') frag.write('vec3 basecol;') frag.write('float roughness;') @@ -512,6 +510,7 @@ def make_forward_solid(con_mesh): vert.write('vcolor = col.rgb;') make_attrib.write_norpos(con_mesh, vert, write_nor=False) + make_attrib.write_vertpos(vert) frag.add_out('vec4 fragColor') if blend and parse_opacity: From 7057ec4ba6bda06b0e60a694e6eabdeed48a60f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Thu, 27 May 2021 22:41:27 +0200 Subject: [PATCH 15/81] Fix mixing multiple cycles shaders in one material --- blender/arm/material/cycles.py | 2 ++ blender/arm/material/parser_state.py | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/blender/arm/material/cycles.py b/blender/arm/material/cycles.py index 1366b66f..20dbd83e 100644 --- a/blender/arm/material/cycles.py +++ b/blender/arm/material/cycles.py @@ -204,6 +204,8 @@ def parse_shader(node: bpy.types.Node, socket: bpy.types.NodeSocket) -> Tuple[st 'BSDF_VELVET': nodes_shader.parse_bsdfvelvet, } + state.reset_outs() + if node.type in node_parser_funcs: node_parser_funcs[node.type](node, socket, state) diff --git a/blender/arm/material/parser_state.py b/blender/arm/material/parser_state.py index bcbb59c8..8606e63f 100644 --- a/blender/arm/material/parser_state.py +++ b/blender/arm/material/parser_state.py @@ -64,6 +64,16 @@ class ParserState: self.out_opacity: floatstr = '1.0' self.out_emission: floatstr = '0.0' + def reset_outs(self): + """Reset the shader output values to their default values.""" + self.out_basecol = 'vec3(0.8)' + self.out_roughness = '0.0' + self.out_metallic = '0.0' + self.out_occlusion = '1.0' + self.out_specular = '1.0' + self.out_opacity = '1.0' + self.out_emission = '0.0' + def get_outs(self) -> Tuple[vec3str, floatstr, floatstr, floatstr, floatstr, floatstr, floatstr]: """Return the shader output values as a tuple.""" return (self.out_basecol, self.out_roughness, self.out_metallic, self.out_occlusion, self.out_specular, From 6d370950147e8da030af0b70f57448b3101fd595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Thu, 27 May 2021 22:56:07 +0200 Subject: [PATCH 16/81] Fix usage of layer weight node in shadowmap shaders --- blender/arm/material/make_depth.py | 2 -- blender/arm/material/make_finalize.py | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/blender/arm/material/make_depth.py b/blender/arm/material/make_depth.py index 03ae858d..aa1f0864 100644 --- a/blender/arm/material/make_depth.py +++ b/blender/arm/material/make_depth.py @@ -33,8 +33,6 @@ def make(context_id, rpasses, shadowmap=False): parse_custom_particle = (cycles.node_by_name(mat_state.nodes, 'ArmCustomParticleNode') is not None) if parse_opacity: - frag.write('vec3 n;') # Discard at compile time - frag.write('float dotNV;') frag.write('float opacity;') if con_depth.is_elem('bone'): diff --git a/blender/arm/material/make_finalize.py b/blender/arm/material/make_finalize.py index 1eca9455..d16d3d00 100644 --- a/blender/arm/material/make_finalize.py +++ b/blender/arm/material/make_finalize.py @@ -13,6 +13,23 @@ def make(con_mesh): if frag.contains('dotNV') and not frag.contains('float dotNV'): frag.write_init('float dotNV = max(dot(n, vVec), 0.0);') + # n is not always defined yet (in some shadowmap shaders e.g.) + if not frag.contains('vec3 n'): + vert.add_out('vec3 wnormal') + vert.add_uniform('mat3 N', '_normalMatrix') + vert.write_attrib('wnormal = normalize(N * vec3(nor.xy, pos.w));') + frag.write_attrib('vec3 n = normalize(wnormal);') + + # If not yet added, add nor vertex data + vertex_elems = con_mesh.data['vertex_elements'] + has_normals = False + for elem in vertex_elems: + if elem['name'] == 'nor': + has_normals = True + break + if not has_normals: + vertex_elems.append({'name': 'nor', 'data': 'short2norm'}) + write_wpos = False if frag.contains('vVec') and not frag.contains('vec3 vVec'): if tese != None: From ccc427c04a79d3ebec16049bc49723bd171bdfca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Thu, 27 May 2021 22:57:53 +0200 Subject: [PATCH 17/81] Cleanup --- Shaders/std/sky.glsl | 4 ++-- blender/arm/material/make_attrib.py | 4 +--- blender/arm/material/make_finalize.py | 17 ++++++++++------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Shaders/std/sky.glsl b/Shaders/std/sky.glsl index d3d04e53..b4465351 100644 --- a/Shaders/std/sky.glsl +++ b/Shaders/std/sky.glsl @@ -60,7 +60,7 @@ float random(vec2 coords) { vec3 nishita_lookupLUT(const float height, const float sunTheta) { vec2 coords = vec2( - sqrt(height * (1 / nishita_atmo_radius)), + sqrt(height * (1 / nishita_atmo_radius)), 0.5 + 0.5 * sign(sunTheta - HALF_PI) * sqrt(abs(sunTheta * (1 / HALF_PI) - 1)) ); return textureLod(nishitaLUT, coords, 0.0).rgb; @@ -125,7 +125,7 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa // Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the // inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith). float sunTheta = acos(dot(normalize(iPos), normalize(pSun))); - vec3 jODepth = nishita_lookupLUT(iHeight, sunTheta);// * vec3(14000000 / 255, 14000000 / 255, 2000000 / 255); + vec3 jODepth = nishita_lookupLUT(iHeight, sunTheta); // Apply dithering to reduce visible banding jODepth += mix(-1000, 1000, random(r.xy)); diff --git a/blender/arm/material/make_attrib.py b/blender/arm/material/make_attrib.py index 1d01e99b..6749d854 100644 --- a/blender/arm/material/make_attrib.py +++ b/blender/arm/material/make_attrib.py @@ -34,13 +34,11 @@ def write_vertpos(vert): def write_norpos(con_mesh: shader.ShaderContext, vert: shader.Shader, declare=False, write_nor=True): - prep = '' - if declare: - prep = 'vec3 ' is_bone = con_mesh.is_elem('bone') if is_bone: make_skin.skin_pos(vert) if write_nor: + prep = 'vec3 ' if declare else '' if is_bone: make_skin.skin_nor(vert, prep) else: diff --git a/blender/arm/material/make_finalize.py b/blender/arm/material/make_finalize.py index d16d3d00..afeeee8f 100644 --- a/blender/arm/material/make_finalize.py +++ b/blender/arm/material/make_finalize.py @@ -1,7 +1,10 @@ import bpy -import arm.material.make_tess as make_tess -def make(con_mesh): +import arm.material.make_tess as make_tess +from arm.material.shader import ShaderContext + + +def make(con_mesh: ShaderContext): vert = con_mesh.vert frag = con_mesh.frag geom = con_mesh.geom @@ -32,7 +35,7 @@ def make(con_mesh): write_wpos = False if frag.contains('vVec') and not frag.contains('vec3 vVec'): - if tese != None: + if tese is not None: tese.add_out('vec3 eyeDir') tese.add_uniform('vec3 eye', '_cameraPosition') tese.write('eyeDir = eye - wposition;') @@ -48,7 +51,7 @@ def make(con_mesh): export_wpos = False if frag.contains('wposition') and not frag.contains('vec3 wposition'): export_wpos = True - if tese != None: + if tese is not None: export_wpos = True if vert.contains('wposition'): write_wpos = True @@ -67,7 +70,7 @@ def make(con_mesh): vert.add_uniform('float posUnpack', link='_posUnpack') vert.write_attrib('mposition = spos.xyz * posUnpack;') - if tese != None: + if tese is not None: if frag_mpos: make_tess.interpolate(tese, 'mposition', 3, declare_out=True) elif tese.contains('mposition') and not tese.contains('vec3 mposition'): @@ -89,7 +92,7 @@ def make(con_mesh): vert.write_attrib('if (dim.y == 0) bposition.y = 0;') vert.write_attrib('if (dim.x == 0) bposition.x = 0;') - if tese != None: + if tese is not None: if frag_bpos: make_tess.interpolate(tese, 'bposition', 3, declare_out=True) elif tese.contains('bposition') and not tese.contains('vec3 bposition'): @@ -110,7 +113,7 @@ def make(con_mesh): vert.write('wtangent = normalize(N * tang.xyz);') vert.write_pre = False - if tese != None: + if tese is not None: if frag_wtan: make_tess.interpolate(tese, 'wtangent', 3, declare_out=True) elif tese.contains('wtangent') and not tese.contains('vec3 wtangent'): From 5eb8fc781f51a726ed2e5e75e64b4f403bfa2269 Mon Sep 17 00:00:00 2001 From: Lubos Lenco Date: Wed, 2 Jun 2021 21:18:47 +0200 Subject: [PATCH 18/81] Bump version --- blender/arm/props.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blender/arm/props.py b/blender/arm/props.py index 99497c80..2b23d334 100755 --- a/blender/arm/props.py +++ b/blender/arm/props.py @@ -11,7 +11,7 @@ import arm.proxy import arm.utils # Armory version -arm_version = '2021.5' +arm_version = '2021.6' arm_commit = '$Id$' def get_project_html5_copy(self): From 20f9f8a5f4b082e20a2aee198339c5e13c0ea2a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Sat, 5 Jun 2021 20:35:21 +0200 Subject: [PATCH 19/81] Add Select node Implements feature requests #2200 and #2201 --- Sources/armory/logicnode/SelectNode.hx | 37 +++++++ blender/arm/logicnode/arm_nodes.py | 46 +++++++-- blender/arm/logicnode/logic/LN_select.py | 122 +++++++++++++++++++++++ blender/arm/nodes_logic.py | 2 + 4 files changed, 198 insertions(+), 9 deletions(-) create mode 100644 Sources/armory/logicnode/SelectNode.hx create mode 100644 blender/arm/logicnode/logic/LN_select.py diff --git a/Sources/armory/logicnode/SelectNode.hx b/Sources/armory/logicnode/SelectNode.hx new file mode 100644 index 00000000..10ca5252 --- /dev/null +++ b/Sources/armory/logicnode/SelectNode.hx @@ -0,0 +1,37 @@ +package armory.logicnode; + +class SelectNode extends LogicNode { + + /** Execution mode. **/ + public var property0: String; + + var value: Dynamic = null; + + public function new(tree: LogicTree) { + super(tree); + } + + override function run(from: Int) { + // Get value according to the activated input (run() can only be called + // if the execution mode is from_input). + value = inputs[from + Std.int(inputs.length / 2)].get(); + + runOutput(0); + } + + override function get(from: Int): Dynamic { + if (property0 == "from_index") { + var index = inputs[0].get() + 2; + + // Return default value for invalid index + if (index < 2 || index >= inputs.length) { + return inputs[1].get(); + } + + return inputs[index].get(); + } + + // from_input + return value; + } +} diff --git a/blender/arm/logicnode/arm_nodes.py b/blender/arm/logicnode/arm_nodes.py index e99b8bd2..7582f966 100644 --- a/blender/arm/logicnode/arm_nodes.py +++ b/blender/arm/logicnode/arm_nodes.py @@ -270,6 +270,30 @@ class ArmNodeRemoveInputOutputButton(bpy.types.Operator): return{'FINISHED'} +class ArmNodeCallFuncButton(bpy.types.Operator): + """Operator that calls a function on a specified + node (used for dynamic callbacks).""" + bl_idname = 'arm.node_call_func' + bl_label = 'Execute' + bl_options = {'UNDO', 'INTERNAL'} + + node_index: StringProperty(name='Node Index', default='') + callback_name: StringProperty(name='Callback Name', default='') + + def execute(self, context): + node = array_nodes[self.node_index] + if hasattr(node, self.callback_name): + getattr(node, self.callback_name)() + else: + return {'CANCELLED'} + + # Reset to default again for subsequent calls of this operator + self.node_index = '' + self.callback_name = '' + + return {'FINISHED'} + + class ArmNodeSearch(bpy.types.Operator): bl_idname = "arm.node_search" bl_label = "Search..." @@ -460,12 +484,16 @@ def reset_globals(): category_items = OrderedDict() -bpy.utils.register_class(ArmNodeSearch) -bpy.utils.register_class(ArmNodeAddInputButton) -bpy.utils.register_class(ArmNodeAddInputValueButton) -bpy.utils.register_class(ArmNodeRemoveInputButton) -bpy.utils.register_class(ArmNodeRemoveInputValueButton) -bpy.utils.register_class(ArmNodeAddOutputButton) -bpy.utils.register_class(ArmNodeRemoveOutputButton) -bpy.utils.register_class(ArmNodeAddInputOutputButton) -bpy.utils.register_class(ArmNodeRemoveInputOutputButton) +REG_CLASSES = ( + ArmNodeSearch, + ArmNodeAddInputButton, + ArmNodeAddInputValueButton, + ArmNodeRemoveInputButton, + ArmNodeRemoveInputValueButton, + ArmNodeAddOutputButton, + ArmNodeRemoveOutputButton, + ArmNodeAddInputOutputButton, + ArmNodeRemoveInputOutputButton, + ArmNodeCallFuncButton +) +register, unregister = bpy.utils.register_classes_factory(REG_CLASSES) diff --git a/blender/arm/logicnode/logic/LN_select.py b/blender/arm/logicnode/logic/LN_select.py new file mode 100644 index 00000000..4ea3762a --- /dev/null +++ b/blender/arm/logicnode/logic/LN_select.py @@ -0,0 +1,122 @@ +from bpy.types import NodeSocketInterfaceInt + +from arm.logicnode.arm_nodes import * + + +class SelectNode(ArmLogicTreeNode): + """Selects one of multiple values (of arbitrary types) based on some + input state. The exact behaviour of this node is specified by the + `Execution Mode` option (see below). + + @output Out: [*Available if Execution Mode is set to From Input*] + Activated after the node was executed. + + @output Value: The last selected value. This value is not reset + until the next execution of this node. + + @option Execution Mode: Specifies the condition that determines + what value to choose. + + - `From Index`: Select the value at the given index. If there is + no value at that index, the value plugged in to the + `Default` input is used instead (`null` if unconnected). + + - `From Input`: This mode uses input pairs of one action socket + and one value socket. Depending on which action socket is + activated, the associated value socket (the value with the + same index as the activated action input) is forwarded to + the `Value` output. + + @option New: Add a new value to the list of values. + @option X Button: Remove the value with the highest index.""" + bl_idname = 'LNSelectNode' + bl_label = 'Select' + arm_version = 1 + min_inputs = 2 + + def update_exec_mode(self, context): + self.set_mode() + + property0: EnumProperty( + name='Execution Mode', + description="The node's behaviour.", + items=[ + ('from_index', 'From Index', 'Choose the value from the given index'), + ('from_input', 'From Input', 'Choose the value with the same position as the active input')], + default='from_index', + update=update_exec_mode, + ) + + # The number of choices, NOT of individual inputs. This needs to be + # a property in order to be saved with each individual node + num_choices: IntProperty(default=1, min=0) + + def __init__(self): + super().__init__() + array_nodes[str(id(self))] = self + + def init(self, context): + super().init(context) + + self.set_mode() + + def set_mode(self): + self.inputs.clear() + self.outputs.clear() + + if self.property0 == 'from_index': + self.add_input('NodeSocketInt', 'Index') + self.add_input('NodeSocketShader', 'Default') + self.num_choices = 0 + + # from_input + else: + # We could also start with index 1 here, but we need to use + # 0 for the "from_index" mode and it makes the code simpler + # if we stick to the same convention for both exec modes + self.add_input('ArmNodeSocketAction', 'Input 0') + self.add_input('NodeSocketShader', 'Value 0') + self.num_choices = 1 + + self.add_output('ArmNodeSocketAction', 'Out') + + self.add_output('NodeSocketShader', 'Value') + + def draw_buttons(self, context, layout): + layout.prop(self, 'property0', text='') + + row = layout.row(align=True) + + op = row.operator('arm.node_call_func', text='New', icon='PLUS', emboss=True) + op.node_index = str(id(self)) + op.callback_name = 'add_input_func' + + op = row.operator('arm.node_call_func', text='', icon='X', emboss=True) + op.node_index = str(id(self)) + op.callback_name = 'remove_input_func' + + def add_input_func(self): + if self.property0 == 'from_input': + self.add_input('ArmNodeSocketAction', f'Input {self.num_choices}') + + # Move new action input up to the end of all other action inputs + self.inputs.move(from_index=len(self.inputs) - 1, to_index=self.num_choices) + + self.add_input('NodeSocketShader', f'Value {self.num_choices}') + + self.num_choices += 1 + + def remove_input_func(self): + if self.property0 == 'from_input': + if len(self.inputs) > self.min_inputs: + self.inputs.remove(self.inputs[self.num_choices - 1]) + + if len(self.inputs) > self.min_inputs: + self.inputs.remove(self.inputs[-1]) + self.num_choices -= 1 + + def draw_label(self) -> str: + if self.num_choices == 0: + return self.bl_label + + return f'{self.bl_label}: [{self.num_choices}]' diff --git a/blender/arm/nodes_logic.py b/blender/arm/nodes_logic.py index 12f605fb..87b73869 100755 --- a/blender/arm/nodes_logic.py +++ b/blender/arm/nodes_logic.py @@ -363,6 +363,7 @@ class ReplaceNodesOperator(bpy.types.Operator): def register(): + arm.logicnode.arm_nodes.register() arm.logicnode.arm_sockets.register() bpy.utils.register_class(ArmLogicTree) @@ -403,3 +404,4 @@ def unregister(): bpy.utils.register_class(ARM_MT_NodeAddOverride.overridden_menu) arm.logicnode.arm_sockets.unregister() + arm.logicnode.arm_nodes.unregister() From 107dc730f4dc6a9a6013136002ebc53f67c27fd5 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Fri, 21 May 2021 22:28:36 +0200 Subject: [PATCH 20/81] Create and implement once per frame node --- Sources/armory/logicnode/OncePerFrameNode.hx | 20 +++++++++++++++++++ .../arm/logicnode/logic/LN_once_per_frame.py | 16 +++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 Sources/armory/logicnode/OncePerFrameNode.hx create mode 100644 blender/arm/logicnode/logic/LN_once_per_frame.py diff --git a/Sources/armory/logicnode/OncePerFrameNode.hx b/Sources/armory/logicnode/OncePerFrameNode.hx new file mode 100644 index 00000000..0e8fe32b --- /dev/null +++ b/Sources/armory/logicnode/OncePerFrameNode.hx @@ -0,0 +1,20 @@ +package armory.logicnode; + +class OncePerFrameNode extends LogicNode { + + var c = false; + + public function new(tree: LogicTree) { + super(tree); + tree.notifyOnUpdate(update); + } + + override function run(from: Int) { + if(c) runOutput(0); + c = false; + } + + function update() { + c = true; + } +} diff --git a/blender/arm/logicnode/logic/LN_once_per_frame.py b/blender/arm/logicnode/logic/LN_once_per_frame.py new file mode 100644 index 00000000..b7dba2f1 --- /dev/null +++ b/blender/arm/logicnode/logic/LN_once_per_frame.py @@ -0,0 +1,16 @@ +from arm.logicnode.arm_nodes import * + + +class OncePerFrameNode(ArmLogicTreeNode): + """Activates the output only once per frame if receives one or more inputs in that frame + If there is no input, there will be no output""" + bl_idname = 'LNOncePerFrameNode' + bl_label = 'Once Per Frame' + arm_section = 'flow' + arm_version = 1 + + def init(self, context): + super(OncePerFrameNode, self).init(context) + self.add_input('ArmNodeSocketAction', 'In') + + self.add_output('ArmNodeSocketAction', 'Out') From 0e6ba5b0b0ec9db6b5076aec1cfd4214286882ca Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 7 Jun 2021 13:49:12 +0200 Subject: [PATCH 21/81] reverse call order --- Sources/armory/logicnode/OncePerFrameNode.hx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/armory/logicnode/OncePerFrameNode.hx b/Sources/armory/logicnode/OncePerFrameNode.hx index 0e8fe32b..0e2aadd5 100644 --- a/Sources/armory/logicnode/OncePerFrameNode.hx +++ b/Sources/armory/logicnode/OncePerFrameNode.hx @@ -10,8 +10,10 @@ class OncePerFrameNode extends LogicNode { } override function run(from: Int) { - if(c) runOutput(0); - c = false; + if(c) { + c = false; + runOutput(0); + } } function update() { From b2619828ebcd44c126ff30f0a09ca46617151e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Wed, 9 Jun 2021 23:11:22 +0200 Subject: [PATCH 22/81] Fix rendering multiple movie textures Fixes https://github.com/armory3d/armory/issues/1562 --- Sources/armory/trait/internal/MovieTexture.hx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Sources/armory/trait/internal/MovieTexture.hx b/Sources/armory/trait/internal/MovieTexture.hx index 1a3dc03a..194d49d2 100644 --- a/Sources/armory/trait/internal/MovieTexture.hx +++ b/Sources/armory/trait/internal/MovieTexture.hx @@ -8,8 +8,7 @@ import iron.object.MeshObject; class MovieTexture extends Trait { var video: Video; - public static var image: Image; - public static var created = false; + var image: Image; var videoName: String; @@ -33,10 +32,7 @@ class MovieTexture extends Trait { this.videoName = videoName; - if (!created) { - created = true; - notifyOnInit(init); - } + notifyOnInit(init); } function init() { @@ -46,7 +42,7 @@ class MovieTexture extends Trait { image = Image.createRenderTarget(getPower2(video.width()), getPower2(video.height())); - var o = cast(object, iron.object.MeshObject); + var o = cast(object, MeshObject); o.materials[0].contexts[0].textures[0] = image; // Override diffuse texture notifyOnRender2D(render); }); From 251ad8e47eaafc6fedca6b9c3110b79b5864ebff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Thu, 10 Jun 2021 20:27:23 +0200 Subject: [PATCH 23/81] Cache and reuse movietexture render targets with same size --- Sources/armory/trait/internal/MovieTexture.hx | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Sources/armory/trait/internal/MovieTexture.hx b/Sources/armory/trait/internal/MovieTexture.hx index 194d49d2..02933f60 100644 --- a/Sources/armory/trait/internal/MovieTexture.hx +++ b/Sources/armory/trait/internal/MovieTexture.hx @@ -7,6 +7,15 @@ import iron.object.MeshObject; class MovieTexture extends Trait { + /** + Caches all render targets used by this trait for re-use when having + multiple videos of the same size. The lookup only takes place on trait + initialization. + + Map layout: `[width => [height => image]]` + **/ + static var imageCache: Map> = new Map(); + var video: Video; var image: Image; @@ -40,7 +49,19 @@ class MovieTexture extends Trait { video = vid; video.play(true); - image = Image.createRenderTarget(getPower2(video.width()), getPower2(video.height())); + var w = getPower2(video.width()); + var h = getPower2(video.height()); + + // Lazily fill the outer map + var hMap: Map = imageCache[w]; + if (hMap == null) { + imageCache[w] = new Map(); + } + + image = imageCache[w][h]; + if (image == null) { + imageCache[w][h] = image = Image.createRenderTarget(w, h); + } var o = cast(object, MeshObject); o.materials[0].contexts[0].textures[0] = image; // Override diffuse texture From 3288c3dcf5c4068cc6082ebd5b23d088427b9b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Thu, 10 Jun 2021 20:28:30 +0200 Subject: [PATCH 24/81] MovieTexture: add documentation --- Sources/armory/trait/internal/MovieTexture.hx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Sources/armory/trait/internal/MovieTexture.hx b/Sources/armory/trait/internal/MovieTexture.hx index 02933f60..3adee41e 100644 --- a/Sources/armory/trait/internal/MovieTexture.hx +++ b/Sources/armory/trait/internal/MovieTexture.hx @@ -2,9 +2,16 @@ package armory.trait.internal; import kha.Image; import kha.Video; + import iron.Trait; import iron.object.MeshObject; +/** + Replaces the diffuse texture of the first material of the trait's object + with a video texture. + + @see https://github.com/armory3d/armory_examples/tree/master/material_movie +**/ class MovieTexture extends Trait { /** From 1ece052aee02616f7a6e086f12eaf5120b5ec007 Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 13 Jun 2021 21:38:15 -0300 Subject: [PATCH 25/81] Fix GetBoneFkIkOnly node --- Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx b/Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx index 14c96489..e27dffa3 100644 --- a/Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx +++ b/Sources/armory/logicnode/GetBoneFkIkOnlyNode.hx @@ -23,7 +23,7 @@ class GetBoneFkIkOnlyNode extends LogicNode { var bone = anim.getBone(boneName); //Get bone transform in world coordinates - return bone.is_IK_FK_only; + return bone.is_ik_fk_only; #end From a5fa3445d9591adc4ee4aac9fd01c4bbdc520a85 Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 13 Jun 2021 22:43:13 -0300 Subject: [PATCH 26/81] Add new input nodes --- .../armory/logicnode/GetGamepadStartedNode.hx | 33 +++++++++++++++++++ .../logicnode/GetKeyboardStartedNode.hx | 32 ++++++++++++++++++ .../armory/logicnode/GetMouseStartedNode.hx | 32 ++++++++++++++++++ .../logicnode/input/LN_get_gamepad_started.py | 15 +++++++++ .../input/LN_get_keyboard_started.py | 14 ++++++++ .../logicnode/input/LN_get_mouse_started.py | 14 ++++++++ 6 files changed, 140 insertions(+) create mode 100644 Sources/armory/logicnode/GetGamepadStartedNode.hx create mode 100644 Sources/armory/logicnode/GetKeyboardStartedNode.hx create mode 100644 Sources/armory/logicnode/GetMouseStartedNode.hx create mode 100644 blender/arm/logicnode/input/LN_get_gamepad_started.py create mode 100644 blender/arm/logicnode/input/LN_get_keyboard_started.py create mode 100644 blender/arm/logicnode/input/LN_get_mouse_started.py diff --git a/Sources/armory/logicnode/GetGamepadStartedNode.hx b/Sources/armory/logicnode/GetGamepadStartedNode.hx new file mode 100644 index 00000000..f94b9e59 --- /dev/null +++ b/Sources/armory/logicnode/GetGamepadStartedNode.hx @@ -0,0 +1,33 @@ +package armory.logicnode; + +import iron.system.Input; + +class GetGamepadStartedNode extends LogicNode { + + var buttonStarted: Null; + + public function new(tree: LogicTree) { + super(tree); + } + + override function run(from: Int) { + var g = Input.getGamepad(inputs[0].get()); + + buttonStarted = null; + + for (b in Gamepad.buttons) { + if (g.started(b)) { + buttonStarted = b; + break; + } + } + + if (buttonStarted != null) { + runOutput(0); + } + } + + override function get(from: Int) { + return buttonStarted; + } +} diff --git a/Sources/armory/logicnode/GetKeyboardStartedNode.hx b/Sources/armory/logicnode/GetKeyboardStartedNode.hx new file mode 100644 index 00000000..ebe752e5 --- /dev/null +++ b/Sources/armory/logicnode/GetKeyboardStartedNode.hx @@ -0,0 +1,32 @@ +package armory.logicnode; + +import iron.system.Input; + +class GetKeyboardStartedNode extends LogicNode { + + var kb = Input.getKeyboard(); + var keyStarted: Null; + + public function new(tree: LogicTree) { + super(tree); + } + + override function run(from: Int) { + keyStarted = null; + + for (k in Keyboard.keys) { + if (kb.started(k)) { + keyStarted = k; + break; + } + } + + if (keyStarted != null) { + runOutput(0); + } + } + + override function get(from: Int) { + return keyStarted; + } +} diff --git a/Sources/armory/logicnode/GetMouseStartedNode.hx b/Sources/armory/logicnode/GetMouseStartedNode.hx new file mode 100644 index 00000000..6bdb0fd0 --- /dev/null +++ b/Sources/armory/logicnode/GetMouseStartedNode.hx @@ -0,0 +1,32 @@ +package armory.logicnode; + +import iron.system.Input; + +class GetKeyboardStartedNode extends LogicNode { + + var m = Input.getMouse(); + var buttonStarted: Null; + + public function new(tree: LogicTree) { + super(tree); + } + + override function run(from: Int) { + buttonStarted = null; + + for (b in Mouse.buttons) { + if (m.started(b)) { + buttonStarted = b; + break; + } + } + + if (buttonStarted != null) { + runOutput(0); + } + } + + override function get(from: Int) { + return buttonStarted; + } +} diff --git a/blender/arm/logicnode/input/LN_get_gamepad_started.py b/blender/arm/logicnode/input/LN_get_gamepad_started.py new file mode 100644 index 00000000..edfbc040 --- /dev/null +++ b/blender/arm/logicnode/input/LN_get_gamepad_started.py @@ -0,0 +1,15 @@ +from arm.logicnode.arm_nodes import * + +class GetGamepadStartedNode(ArmLogicTreeNode): + """.""" + bl_idname = 'LNGetGamepadStartedNode' + bl_label = 'Get Gamepad Started' + arm_version = 1 + + def init(self, context): + super(GetGamepadStartedNode, self).init(context) + self.add_input('ArmNodeSocketAction', 'In') + self.add_input('NodeSocketInt', 'Index') + + self.add_output('ArmNodeSocketAction', 'Out') + self.add_output('NodeSocketString', 'Button') diff --git a/blender/arm/logicnode/input/LN_get_keyboard_started.py b/blender/arm/logicnode/input/LN_get_keyboard_started.py new file mode 100644 index 00000000..0f655b04 --- /dev/null +++ b/blender/arm/logicnode/input/LN_get_keyboard_started.py @@ -0,0 +1,14 @@ +from arm.logicnode.arm_nodes import * + +class GetKeyboardStartedNode(ArmLogicTreeNode): + """.""" + bl_idname = 'LNGetKeyboardStartedNode' + bl_label = 'Get Keyboard Started' + arm_version = 1 + + def init(self, context): + super(GetKeyboardStartedNode, self).init(context) + self.add_input('ArmNodeSocketAction', 'In') + + self.add_output('ArmNodeSocketAction', 'Out') + self.add_output('NodeSocketString', 'Key') diff --git a/blender/arm/logicnode/input/LN_get_mouse_started.py b/blender/arm/logicnode/input/LN_get_mouse_started.py new file mode 100644 index 00000000..368aa433 --- /dev/null +++ b/blender/arm/logicnode/input/LN_get_mouse_started.py @@ -0,0 +1,14 @@ +from arm.logicnode.arm_nodes import * + +class GetMouseStartedNode(ArmLogicTreeNode): + """.""" + bl_idname = 'LNGetMouseStartedNode' + bl_label = 'Get Mouse Started' + arm_version = 1 + + def init(self, context): + super(GetMouseStartedNode, self).init(context) + self.add_input('ArmNodeSocketAction', 'In') + + self.add_output('ArmNodeSocketAction', 'Out') + self.add_output('NodeSocketString', 'Button') From f45304ea10b7bb7798040ba74d515de356d9e89c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Tue, 15 Jun 2021 15:24:58 +0200 Subject: [PATCH 27/81] Fix compilation of some nodes --- Sources/armory/logicnode/GetMouseStartedNode.hx | 2 +- Sources/armory/logicnode/OnCanvasElementNode.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/armory/logicnode/GetMouseStartedNode.hx b/Sources/armory/logicnode/GetMouseStartedNode.hx index 6bdb0fd0..8f2d5f32 100644 --- a/Sources/armory/logicnode/GetMouseStartedNode.hx +++ b/Sources/armory/logicnode/GetMouseStartedNode.hx @@ -2,7 +2,7 @@ package armory.logicnode; import iron.system.Input; -class GetKeyboardStartedNode extends LogicNode { +class GetMouseStartedNode extends LogicNode { var m = Input.getMouse(); var buttonStarted: Null; diff --git a/Sources/armory/logicnode/OnCanvasElementNode.hx b/Sources/armory/logicnode/OnCanvasElementNode.hx index 8b3ff763..0fae83e5 100644 --- a/Sources/armory/logicnode/OnCanvasElementNode.hx +++ b/Sources/armory/logicnode/OnCanvasElementNode.hx @@ -4,7 +4,7 @@ import armory.trait.internal.CanvasScript; import iron.Scene; #if arm_ui -import zui.Canvas.Anchor; +import armory.ui.Canvas.Anchor; #end class OnCanvasElementNode extends LogicNode { From 3c1264378b56df69454e62da5adddcca82f8581f Mon Sep 17 00:00:00 2001 From: Henrique Date: Sat, 19 Jun 2021 13:39:18 -0300 Subject: [PATCH 28/81] Cleanup in ui Ext --- Sources/armory/ui/Ext.hx | 308 +++++++++++++++++++-------------------- 1 file changed, 154 insertions(+), 154 deletions(-) diff --git a/Sources/armory/ui/Ext.hx b/Sources/armory/ui/Ext.hx index 0104ddd5..1197ef46 100644 --- a/Sources/armory/ui/Ext.hx +++ b/Sources/armory/ui/Ext.hx @@ -172,160 +172,160 @@ class Ext { Keycodes can be found here: http://api.kha.tech/kha/input/KeyCode.html **/ static function keycodeToString(keycode: Int): String { - switch (keycode) { - case -1: return "None"; - case KeyCode.Unknown: return "Unknown"; - case KeyCode.Back: return "Back"; - case KeyCode.Cancel: return "Cancel"; - case KeyCode.Help: return "Help"; - case KeyCode.Backspace: return "Backspace"; - case KeyCode.Tab: return "Tab"; - case KeyCode.Clear: return "Clear"; - case KeyCode.Return: return "Return"; - case KeyCode.Shift: return "Shift"; - case KeyCode.Control: return "Ctrl"; - case KeyCode.Alt: return "Alt"; - case KeyCode.Pause: return "Pause"; - case KeyCode.CapsLock: return "CapsLock"; - case KeyCode.Kana: return "Kana"; - // case KeyCode.Hangul: return "Hangul"; // Hangul == Kana - case KeyCode.Eisu: return "Eisu"; - case KeyCode.Junja: return "Junja"; - case KeyCode.Final: return "Final"; - case KeyCode.Hanja: return "Hanja"; - // case KeyCode.Kanji: return "Kanji"; // Kanji == Hanja - case KeyCode.Escape: return "Esc"; - case KeyCode.Convert: return "Convert"; - case KeyCode.NonConvert: return "NonConvert"; - case KeyCode.Accept: return "Accept"; - case KeyCode.ModeChange: return "ModeChange"; - case KeyCode.Space: return "Space"; - case KeyCode.PageUp: return "PageUp"; - case KeyCode.PageDown: return "PageDown"; - case KeyCode.End: return "End"; - case KeyCode.Home: return "Home"; - case KeyCode.Left: return "Left"; - case KeyCode.Up: return "Up"; - case KeyCode.Right: return "Right"; - case KeyCode.Down: return "Down"; - case KeyCode.Select: return "Select"; - case KeyCode.Print: return "Print"; - case KeyCode.Execute: return "Execute"; - case KeyCode.PrintScreen: return "PrintScreen"; - case KeyCode.Insert: return "Insert"; - case KeyCode.Delete: return "Delete"; - case KeyCode.Colon: return "Colon"; - case KeyCode.Semicolon: return "Semicolon"; - case KeyCode.LessThan: return "LessThan"; - case KeyCode.Equals: return "Equals"; - case KeyCode.GreaterThan: return "GreaterThan"; - case KeyCode.QuestionMark: return "QuestionMark"; - case KeyCode.At: return "At"; - case KeyCode.Win: return "Win"; - case KeyCode.ContextMenu: return "ContextMenu"; - case KeyCode.Sleep: return "Sleep"; - case KeyCode.Numpad0: return "Numpad0"; - case KeyCode.Numpad1: return "Numpad1"; - case KeyCode.Numpad2: return "Numpad2"; - case KeyCode.Numpad3: return "Numpad3"; - case KeyCode.Numpad4: return "Numpad4"; - case KeyCode.Numpad5: return "Numpad5"; - case KeyCode.Numpad6: return "Numpad6"; - case KeyCode.Numpad7: return "Numpad7"; - case KeyCode.Numpad8: return "Numpad8"; - case KeyCode.Numpad9: return "Numpad9"; - case KeyCode.Multiply: return "Multiply"; - case KeyCode.Add: return "Add"; - case KeyCode.Separator: return "Separator"; - case KeyCode.Subtract: return "Subtract"; - case KeyCode.Decimal: return "Decimal"; - case KeyCode.Divide: return "Divide"; - case KeyCode.F1: return "F1"; - case KeyCode.F2: return "F2"; - case KeyCode.F3: return "F3"; - case KeyCode.F4: return "F4"; - case KeyCode.F5: return "F5"; - case KeyCode.F6: return "F6"; - case KeyCode.F7: return "F7"; - case KeyCode.F8: return "F8"; - case KeyCode.F9: return "F9"; - case KeyCode.F10: return "F10"; - case KeyCode.F11: return "F11"; - case KeyCode.F12: return "F12"; - case KeyCode.F13: return "F13"; - case KeyCode.F14: return "F14"; - case KeyCode.F15: return "F15"; - case KeyCode.F16: return "F16"; - case KeyCode.F17: return "F17"; - case KeyCode.F18: return "F18"; - case KeyCode.F19: return "F19"; - case KeyCode.F20: return "F20"; - case KeyCode.F21: return "F21"; - case KeyCode.F22: return "F22"; - case KeyCode.F23: return "F23"; - case KeyCode.F24: return "F24"; - case KeyCode.NumLock: return "NumLock"; - case KeyCode.ScrollLock: return "ScrollLock"; - case KeyCode.WinOemFjJisho: return "WinOemFjJisho"; - case KeyCode.WinOemFjMasshou: return "WinOemFjMasshou"; - case KeyCode.WinOemFjTouroku: return "WinOemFjTouroku"; - case KeyCode.WinOemFjLoya: return "WinOemFjLoya"; - case KeyCode.WinOemFjRoya: return "WinOemFjRoya"; - case KeyCode.Circumflex: return "Circumflex"; - case KeyCode.Exclamation: return "Exclamation"; - case KeyCode.DoubleQuote: return "DoubleQuote"; - case KeyCode.Hash: return "Hash"; - case KeyCode.Dollar: return "Dollar"; - case KeyCode.Percent: return "Percent"; - case KeyCode.Ampersand: return "Ampersand"; - case KeyCode.Underscore: return "Underscore"; - case KeyCode.OpenParen: return "OpenParen"; - case KeyCode.CloseParen: return "CloseParen"; - case KeyCode.Asterisk: return "Asterisk"; - case KeyCode.Plus: return "Plus"; - case KeyCode.Pipe: return "Pipe"; - case KeyCode.HyphenMinus: return "HyphenMinus"; - case KeyCode.OpenCurlyBracket: return "OpenCurlyBracket"; - case KeyCode.CloseCurlyBracket: return "CloseCurlyBracket"; - case KeyCode.Tilde: return "Tilde"; - case KeyCode.VolumeMute: return "VolumeMute"; - case KeyCode.VolumeDown: return "VolumeDown"; - case KeyCode.VolumeUp: return "VolumeUp"; - case KeyCode.Comma: return "Comma"; - case KeyCode.Period: return "Period"; - case KeyCode.Slash: return "Slash"; - case KeyCode.BackQuote: return "BackQuote"; - case KeyCode.OpenBracket: return "OpenBracket"; - case KeyCode.BackSlash: return "BackSlash"; - case KeyCode.CloseBracket: return "CloseBracket"; - case KeyCode.Quote: return "Quote"; - case KeyCode.Meta: return "Meta"; - case KeyCode.AltGr: return "AltGr"; - case KeyCode.WinIcoHelp: return "WinIcoHelp"; - case KeyCode.WinIco00: return "WinIco00"; - case KeyCode.WinIcoClear: return "WinIcoClear"; - case KeyCode.WinOemReset: return "WinOemReset"; - case KeyCode.WinOemJump: return "WinOemJump"; - case KeyCode.WinOemPA1: return "WinOemPA1"; - case KeyCode.WinOemPA2: return "WinOemPA2"; - case KeyCode.WinOemPA3: return "WinOemPA3"; - case KeyCode.WinOemWSCTRL: return "WinOemWSCTRL"; - case KeyCode.WinOemCUSEL: return "WinOemCUSEL"; - case KeyCode.WinOemATTN: return "WinOemATTN"; - case KeyCode.WinOemFinish: return "WinOemFinish"; - case KeyCode.WinOemCopy: return "WinOemCopy"; - case KeyCode.WinOemAuto: return "WinOemAuto"; - case KeyCode.WinOemENLW: return "WinOemENLW"; - case KeyCode.WinOemBackTab: return "WinOemBackTab"; - case KeyCode.ATTN: return "ATTN"; - case KeyCode.CRSEL: return "CRSEL"; - case KeyCode.EXSEL: return "EXSEL"; - case KeyCode.EREOF: return "EREOF"; - case KeyCode.Play: return "Play"; - case KeyCode.Zoom: return "Zoom"; - case KeyCode.PA1: return "PA1"; - case KeyCode.WinOemClear: return "WinOemClear"; + return switch (keycode) { + default: String.fromCharCode(keycode); + case -1: "None"; + case KeyCode.Unknown: "Unknown"; + case KeyCode.Back: "Back"; + case KeyCode.Cancel: "Cancel"; + case KeyCode.Help: "Help"; + case KeyCode.Backspace: "Backspace"; + case KeyCode.Tab: "Tab"; + case KeyCode.Clear: "Clear"; + case KeyCode.Return: "Return"; + case KeyCode.Shift: "Shift"; + case KeyCode.Control: "Ctrl"; + case KeyCode.Alt: "Alt"; + case KeyCode.Pause: "Pause"; + case KeyCode.CapsLock: "CapsLock"; + case KeyCode.Kana: "Kana"; + // case KeyCode.Hangul: "Hangul"; // Hangul == Kana + case KeyCode.Eisu: "Eisu"; + case KeyCode.Junja: "Junja"; + case KeyCode.Final: "Final"; + case KeyCode.Hanja: "Hanja"; + // case KeyCode.Kanji: "Kanji"; // Kanji == Hanja + case KeyCode.Escape: "Esc"; + case KeyCode.Convert: "Convert"; + case KeyCode.NonConvert: "NonConvert"; + case KeyCode.Accept: "Accept"; + case KeyCode.ModeChange: "ModeChange"; + case KeyCode.Space: "Space"; + case KeyCode.PageUp: "PageUp"; + case KeyCode.PageDown: "PageDown"; + case KeyCode.End: "End"; + case KeyCode.Home: "Home"; + case KeyCode.Left: "Left"; + case KeyCode.Up: "Up"; + case KeyCode.Right: "Right"; + case KeyCode.Down: "Down"; + case KeyCode.Select: "Select"; + case KeyCode.Print: "Print"; + case KeyCode.Execute: "Execute"; + case KeyCode.PrintScreen: "PrintScreen"; + case KeyCode.Insert: "Insert"; + case KeyCode.Delete: "Delete"; + case KeyCode.Colon: "Colon"; + case KeyCode.Semicolon: "Semicolon"; + case KeyCode.LessThan: "LessThan"; + case KeyCode.Equals: "Equals"; + case KeyCode.GreaterThan: "GreaterThan"; + case KeyCode.QuestionMark: "QuestionMark"; + case KeyCode.At: "At"; + case KeyCode.Win: "Win"; + case KeyCode.ContextMenu: "ContextMenu"; + case KeyCode.Sleep: "Sleep"; + case KeyCode.Numpad0: "Numpad0"; + case KeyCode.Numpad1: "Numpad1"; + case KeyCode.Numpad2: "Numpad2"; + case KeyCode.Numpad3: "Numpad3"; + case KeyCode.Numpad4: "Numpad4"; + case KeyCode.Numpad5: "Numpad5"; + case KeyCode.Numpad6: "Numpad6"; + case KeyCode.Numpad7: "Numpad7"; + case KeyCode.Numpad8: "Numpad8"; + case KeyCode.Numpad9: "Numpad9"; + case KeyCode.Multiply: "Multiply"; + case KeyCode.Add: "Add"; + case KeyCode.Separator: "Separator"; + case KeyCode.Subtract: "Subtract"; + case KeyCode.Decimal: "Decimal"; + case KeyCode.Divide: "Divide"; + case KeyCode.F1: "F1"; + case KeyCode.F2: "F2"; + case KeyCode.F3: "F3"; + case KeyCode.F4: "F4"; + case KeyCode.F5: "F5"; + case KeyCode.F6: "F6"; + case KeyCode.F7: "F7"; + case KeyCode.F8: "F8"; + case KeyCode.F9: "F9"; + case KeyCode.F10: "F10"; + case KeyCode.F11: "F11"; + case KeyCode.F12: "F12"; + case KeyCode.F13: "F13"; + case KeyCode.F14: "F14"; + case KeyCode.F15: "F15"; + case KeyCode.F16: "F16"; + case KeyCode.F17: "F17"; + case KeyCode.F18: "F18"; + case KeyCode.F19: "F19"; + case KeyCode.F20: "F20"; + case KeyCode.F21: "F21"; + case KeyCode.F22: "F22"; + case KeyCode.F23: "F23"; + case KeyCode.F24: "F24"; + case KeyCode.NumLock: "NumLock"; + case KeyCode.ScrollLock: "ScrollLock"; + case KeyCode.WinOemFjJisho: "WinOemFjJisho"; + case KeyCode.WinOemFjMasshou: "WinOemFjMasshou"; + case KeyCode.WinOemFjTouroku: "WinOemFjTouroku"; + case KeyCode.WinOemFjLoya: "WinOemFjLoya"; + case KeyCode.WinOemFjRoya: "WinOemFjRoya"; + case KeyCode.Circumflex: "Circumflex"; + case KeyCode.Exclamation: "Exclamation"; + case KeyCode.DoubleQuote: "DoubleQuote"; + case KeyCode.Hash: "Hash"; + case KeyCode.Dollar: "Dollar"; + case KeyCode.Percent: "Percent"; + case KeyCode.Ampersand: "Ampersand"; + case KeyCode.Underscore: "Underscore"; + case KeyCode.OpenParen: "OpenParen"; + case KeyCode.CloseParen: "CloseParen"; + case KeyCode.Asterisk: "Asterisk"; + case KeyCode.Plus: "Plus"; + case KeyCode.Pipe: "Pipe"; + case KeyCode.HyphenMinus: "HyphenMinus"; + case KeyCode.OpenCurlyBracket: "OpenCurlyBracket"; + case KeyCode.CloseCurlyBracket: "CloseCurlyBracket"; + case KeyCode.Tilde: "Tilde"; + case KeyCode.VolumeMute: "VolumeMute"; + case KeyCode.VolumeDown: "VolumeDown"; + case KeyCode.VolumeUp: "VolumeUp"; + case KeyCode.Comma: "Comma"; + case KeyCode.Period: "Period"; + case KeyCode.Slash: "Slash"; + case KeyCode.BackQuote: "BackQuote"; + case KeyCode.OpenBracket: "OpenBracket"; + case KeyCode.BackSlash: "BackSlash"; + case KeyCode.CloseBracket: "CloseBracket"; + case KeyCode.Quote: "Quote"; + case KeyCode.Meta: "Meta"; + case KeyCode.AltGr: "AltGr"; + case KeyCode.WinIcoHelp: "WinIcoHelp"; + case KeyCode.WinIco00: "WinIco00"; + case KeyCode.WinIcoClear: "WinIcoClear"; + case KeyCode.WinOemReset: "WinOemReset"; + case KeyCode.WinOemJump: "WinOemJump"; + case KeyCode.WinOemPA1: "WinOemPA1"; + case KeyCode.WinOemPA2: "WinOemPA2"; + case KeyCode.WinOemPA3: "WinOemPA3"; + case KeyCode.WinOemWSCTRL: "WinOemWSCTRL"; + case KeyCode.WinOemCUSEL: "WinOemCUSEL"; + case KeyCode.WinOemATTN: "WinOemATTN"; + case KeyCode.WinOemFinish: "WinOemFinish"; + case KeyCode.WinOemCopy: "WinOemCopy"; + case KeyCode.WinOemAuto: "WinOemAuto"; + case KeyCode.WinOemENLW: "WinOemENLW"; + case KeyCode.WinOemBackTab: "WinOemBackTab"; + case KeyCode.ATTN: "ATTN"; + case KeyCode.CRSEL: "CRSEL"; + case KeyCode.EXSEL: "EXSEL"; + case KeyCode.EREOF: "EREOF"; + case KeyCode.Play: "Play"; + case KeyCode.Zoom: "Zoom"; + case KeyCode.PA1: "PA1"; + case KeyCode.WinOemClear: "WinOemClear"; } - return String.fromCharCode(keycode); } } From 66856e7ecc8a41653d674af9054b6e4f1cf4835b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Wed, 23 Jun 2021 20:19:08 +0200 Subject: [PATCH 29/81] Build/publish: add poll() function to prevent exception when executing from outside of UI If there was no exporter, calling `bpy.ops.arm.publish_project()` would result in an exception before. Now the call is simply ignored and a "poll failed" message is emitted instead. --- blender/arm/props_ui.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/blender/arm/props_ui.py b/blender/arm/props_ui.py index 880c7f9a..8086a3a7 100644 --- a/blender/arm/props_ui.py +++ b/blender/arm/props_ui.py @@ -626,7 +626,6 @@ class ARM_PT_ArmoryExporterPanel(bpy.types.Panel): row = layout.row(align=True) row.alignment = 'EXPAND' row.scale_y = 1.3 - row.enabled = wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0 row.operator("arm.build_project", icon="MOD_BUILD") # row.operator("arm.patch_project") row.operator("arm.publish_project", icon="EXPORT") @@ -1053,10 +1052,15 @@ class ArmoryStopButton(bpy.types.Operator): return{'FINISHED'} class ArmoryBuildProjectButton(bpy.types.Operator): - '''Build and compile project''' + """Build and compile project""" bl_idname = 'arm.build_project' bl_label = 'Build' + @classmethod + def poll(cls, context): + wrd = bpy.data.worlds['Arm'] + return wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0 + def execute(self, context): # Compare version Blender and Armory (major, minor) if not arm.utils.compare_version_blender_arm(): @@ -1093,10 +1097,15 @@ class ArmoryBuildProjectButton(bpy.types.Operator): return{'FINISHED'} class ArmoryPublishProjectButton(bpy.types.Operator): - '''Build project ready for publishing''' + """Build project ready for publishing.""" bl_idname = 'arm.publish_project' bl_label = 'Publish' + @classmethod + def poll(cls, context): + wrd = bpy.data.worlds['Arm'] + return wrd.arm_exporterlist_index >= 0 and len(wrd.arm_exporterlist) > 0 + def execute(self, context): # Compare version Blender and Armory (major, minor) if not arm.utils.compare_version_blender_arm(): From d18aede964337829aa5e92fdd6e02e8c7cd73202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Thu, 24 Jun 2021 14:59:32 +0200 Subject: [PATCH 30/81] run_proc: no thread in background mode and call `done` in main thread --- blender/arm/handlers.py | 21 +++++++++++++++++++++ blender/arm/make.py | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/blender/arm/handlers.py b/blender/arm/handlers.py index aa8460e8..eaaecfb8 100644 --- a/blender/arm/handlers.py +++ b/blender/arm/handlers.py @@ -1,5 +1,6 @@ import importlib import os +import queue import sys import bpy @@ -102,6 +103,24 @@ def always(): space.node_tree.arm_cached = False return 0.5 + +def poll_threads() -> float: + """Polls the thread callback queue and if a thread has finished, it + is joined with the main thread and the corresponding callback is + executed in the main thread. + """ + try: + thread, callback = make.thread_callback_queue.get(block=False) + except queue.Empty: + return 0.25 + + thread.join() + callback() + + # Quickly check if another thread has finished + return 0.01 + + appended_py_paths = [] context_screen = None @@ -181,7 +200,9 @@ def register(): bpy.app.handlers.load_post.append(on_load_post) bpy.app.handlers.depsgraph_update_post.append(on_depsgraph_update_post) # bpy.app.handlers.undo_post.append(on_undo_post) + bpy.app.timers.register(always, persistent=True) + bpy.app.timers.register(poll_threads, persistent=True) if arm.utils.get_fp() != '': appended_py_paths = [] diff --git a/blender/arm/make.py b/blender/arm/make.py index 001eb2d4..d7049831 100755 --- a/blender/arm/make.py +++ b/blender/arm/make.py @@ -10,6 +10,8 @@ import webbrowser import shlex import errno import math +from typing import Callable +from queue import Queue import bpy @@ -28,15 +30,42 @@ import arm.write_data as write_data scripts_mtime = 0 # Monitor source changes profile_time = 0 -def run_proc(cmd, done): - def fn(p, done): - p.wait() - if done != None: +# Queue of threads and their done callbacks. Item format: [thread, done] +thread_callback_queue = Queue(maxsize=0) + + +def run_proc(cmd, done: Callable) -> subprocess.Popen: + """Creates a subprocess with the given command and returns it. + + If Blender is not running in background mode, a thread is spawned + that waits until the subprocess has finished executing to not freeze + the UI, otherwise (in background mode) execution is blocked until + the subprocess has finished. + + If `done` is not `None`, it is called afterwards in the main thread. + """ + use_thread = not bpy.app.background + + def wait_for_proc(proc: subprocess.Popen): + proc.wait() + + if use_thread: + # Put the done callback into the callback queue so that it + # can be received by a polling function in the main thread + thread_callback_queue.put([threading.current_thread(), done], block=True) + else: done() + p = subprocess.Popen(cmd) - threading.Thread(target=fn, args=(p, done)).start() + + if use_thread: + threading.Thread(target=wait_for_proc, args=(p,)).start() + else: + wait_for_proc(p) + return p + def compile_shader_pass(res, raw_shaders_path, shader_name, defs, make_variants): os.chdir(raw_shaders_path + '/' + shader_name) From fce97a5ddfebc33421a8c85d81a8207fdb599dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Thu, 24 Jun 2021 15:03:30 +0200 Subject: [PATCH 31/81] Cleanup --- blender/arm/handlers.py | 7 ++++--- blender/arm/make.py | 11 +++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/blender/arm/handlers.py b/blender/arm/handlers.py index eaaecfb8..ccb8b274 100644 --- a/blender/arm/handlers.py +++ b/blender/arm/handlers.py @@ -90,16 +90,17 @@ def send_operator(op): else: # Rebuild make.patch() -def always(): + +def always() -> float: # Force ui redraw - if state.redraw_ui and context_screen != None: + if state.redraw_ui and context_screen is not None: for area in context_screen.areas: if area.type == 'VIEW_3D' or area.type == 'PROPERTIES': area.tag_redraw() state.redraw_ui = False # TODO: depsgraph.updates only triggers material trees space = arm.utils.logic_editor_space(context_screen) - if space != None: + if space is not None: space.node_tree.arm_cached = False return 0.5 diff --git a/blender/arm/make.py b/blender/arm/make.py index d7049831..0ac315b8 100755 --- a/blender/arm/make.py +++ b/blender/arm/make.py @@ -1,17 +1,16 @@ +import errno import glob import json import os +from queue import Queue +import shlex import shutil -import time import stat import subprocess import threading -import webbrowser -import shlex -import errno -import math +import time from typing import Callable -from queue import Queue +import webbrowser import bpy From dc34e48c521048782f0aa960b0a52b7d3bde878d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Thu, 24 Jun 2021 17:26:21 +0200 Subject: [PATCH 32/81] Unregister timers on exit/disabling addon --- blender/arm/handlers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blender/arm/handlers.py b/blender/arm/handlers.py index ccb8b274..b5020e53 100644 --- a/blender/arm/handlers.py +++ b/blender/arm/handlers.py @@ -220,6 +220,9 @@ def register(): def unregister(): + bpy.app.timers.unregister(poll_threads) + bpy.app.timers.unregister(always) + bpy.app.handlers.load_post.remove(on_load_post) bpy.app.handlers.depsgraph_update_post.remove(on_depsgraph_update_post) # bpy.app.handlers.undo_post.remove(on_undo_post) From a588396deac39485b164d603e53c5db5db19d783 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Sat, 5 Jun 2021 15:10:29 +0200 Subject: [PATCH 33/81] Update set material value param node --- .../material/LN_set_material_value_param.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/blender/arm/logicnode/material/LN_set_material_value_param.py b/blender/arm/logicnode/material/LN_set_material_value_param.py index 426807d1..c3691673 100644 --- a/blender/arm/logicnode/material/LN_set_material_value_param.py +++ b/blender/arm/logicnode/material/LN_set_material_value_param.py @@ -1,17 +1,27 @@ from arm.logicnode.arm_nodes import * class SetMaterialValueParamNode(ArmLogicTreeNode): - """TO DO.""" + """Set a float value material parameter to the specified object""" bl_idname = 'LNSetMaterialValueParamNode' bl_label = 'Set Material Value Param' arm_section = 'params' - arm_version = 1 + arm_version = 2 def init(self, context): super(SetMaterialValueParamNode, self).init(context) self.add_input('ArmNodeSocketAction', 'In') + self.add_input('ArmNodeSocketObject', 'Object') self.add_input('NodeSocketShader', 'Material') self.add_input('NodeSocketString', 'Node') self.add_input('NodeSocketFloat', 'Float') self.add_output('ArmNodeSocketAction', 'Out') + + def get_replacement_node(self, node_tree: bpy.types.NodeTree): + if self.arm_version not in (0, 1): + raise LookupError() + + return NodeReplacement( + 'LNSetMaterialValueParamNode', self.arm_version, 'LNSetMaterialValueParamNode', 2, + in_socket_mapping={0:0, 1:2, 2:3, 3:4}, out_socket_mapping={0:0} + ) From 6cf3299ffe9f5df44fb39791c87e34e711b0be06 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Sat, 5 Jun 2021 15:11:06 +0200 Subject: [PATCH 34/81] Implement and upgrade set material value parameter node --- .../logicnode/SetMaterialValueParamNode.hx | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/Sources/armory/logicnode/SetMaterialValueParamNode.hx b/Sources/armory/logicnode/SetMaterialValueParamNode.hx index 40729a64..ed969ae5 100644 --- a/Sources/armory/logicnode/SetMaterialValueParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialValueParamNode.hx @@ -6,7 +6,7 @@ import iron.object.Object; class SetMaterialValueParamNode extends LogicNode { static var registered = false; - static var map = new Map>>(); + static var map = new Map>>>(); public function new(tree: LogicTree) { super(tree); @@ -17,21 +17,36 @@ class SetMaterialValueParamNode extends LogicNode { } override function run(from: Int) { - var mat = inputs[1].get(); - if (mat == null) return; - var entry = map.get(mat); + var obj = inputs[1].get(); + var mat = inputs[2].get(); + if(obj == null) return; + + var matMap = map.get(obj); + if (matMap == null) { + matMap = new Map(); + map.set(obj, matMap); + } + + var entry = matMap.get(mat); if (entry == null) { entry = new Map(); - map.set(mat, entry); + matMap.set(mat, entry); } - entry.set(inputs[2].get(), inputs[3].get()); // Node name, value + + entry.set(inputs[3].get(), inputs[4].get()); // Node name, value runOutput(0); } static function floatLink(object: Object, mat: MaterialData, link: String): Null { + if(object == null) return null; if (mat == null) return null; - var entry = map.get(mat); + + var material = map.get(object); + if (material == null) return null; + + var entry = material.get(mat); if (entry == null) return null; + return entry.get(link); } } From a9f430c3742d6ae1c10bc744ac4f38944d72778e Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 7 Jun 2021 13:40:34 +0200 Subject: [PATCH 35/81] Add per object option --- .../logicnode/material/LN_set_material_value_param.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/blender/arm/logicnode/material/LN_set_material_value_param.py b/blender/arm/logicnode/material/LN_set_material_value_param.py index c3691673..d668c7f9 100644 --- a/blender/arm/logicnode/material/LN_set_material_value_param.py +++ b/blender/arm/logicnode/material/LN_set_material_value_param.py @@ -7,6 +7,12 @@ class SetMaterialValueParamNode(ArmLogicTreeNode): arm_section = 'params' arm_version = 2 + property0: BoolProperty( + name="Per Object", + description="Set property per object", + default=False + ) + def init(self, context): super(SetMaterialValueParamNode, self).init(context) self.add_input('ArmNodeSocketAction', 'In') @@ -16,6 +22,9 @@ class SetMaterialValueParamNode(ArmLogicTreeNode): self.add_input('NodeSocketFloat', 'Float') self.add_output('ArmNodeSocketAction', 'Out') + + 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): From aaa21bc01966cb18599d1a71116c91a103751a5a Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 7 Jun 2021 13:40:54 +0200 Subject: [PATCH 36/81] implement per object option --- .../logicnode/SetMaterialValueParamNode.hx | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Sources/armory/logicnode/SetMaterialValueParamNode.hx b/Sources/armory/logicnode/SetMaterialValueParamNode.hx index ed969ae5..ec33130b 100644 --- a/Sources/armory/logicnode/SetMaterialValueParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialValueParamNode.hx @@ -1,10 +1,13 @@ package armory.logicnode; +import iron.Scene; import iron.data.MaterialData; import iron.object.Object; class SetMaterialValueParamNode extends LogicNode { + public var property0: Bool; // per object + static var registered = false; static var map = new Map>>>(); @@ -17,14 +20,20 @@ class SetMaterialValueParamNode extends LogicNode { } override function run(from: Int) { - var obj = inputs[1].get(); - var mat = inputs[2].get(); - if(obj == null) return; + var object = inputs[1].get(); + if(object == null) return; - var matMap = map.get(obj); + var mat = inputs[2].get(); + if(mat == null) return; + + if(! property0){ + object = Scene.active.root; + } + + var matMap = map.get(object); if (matMap == null) { matMap = new Map(); - map.set(obj, matMap); + map.set(object, matMap); } var entry = matMap.get(mat); @@ -41,6 +50,10 @@ class SetMaterialValueParamNode extends LogicNode { if(object == null) return null; if (mat == null) return null; + if(! map.exists(object)){ + object = Scene.active.root; + } + var material = map.get(object); if (material == null) return null; From f542dc00cabb7643b5a020fbb8f31308569085e5 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 9 Jun 2021 19:09:02 +0200 Subject: [PATCH 37/81] update similar nodes --- .../material/LN_set_material_image_param.py | 23 +++++++++++++++++-- .../material/LN_set_material_rgb_param.py | 23 +++++++++++++++++-- .../material/LN_set_material_value_param.py | 2 +- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/blender/arm/logicnode/material/LN_set_material_image_param.py b/blender/arm/logicnode/material/LN_set_material_image_param.py index 777794c2..d442dc9a 100644 --- a/blender/arm/logicnode/material/LN_set_material_image_param.py +++ b/blender/arm/logicnode/material/LN_set_material_image_param.py @@ -1,17 +1,36 @@ from arm.logicnode.arm_nodes import * class SetMaterialImageParamNode(ArmLogicTreeNode): - """TO DO.""" + """Set a image or texture value material parameter to the specified object. If `per object` is disabled, value will be set to all objects with this material""" bl_idname = 'LNSetMaterialImageParamNode' bl_label = 'Set Material Image Param' arm_section = 'params' - arm_version = 1 + arm_version = 2 + + property0: BoolProperty( + name="Per Object", + description="Set property per object", + default=False + ) def init(self, context): super(SetMaterialImageParamNode, self).init(context) self.add_input('ArmNodeSocketAction', 'In') + self.add_input('ArmNodeSocketObject', 'Object') self.add_input('NodeSocketShader', 'Material') self.add_input('NodeSocketString', 'Node') self.add_input('NodeSocketString', 'Image') self.add_output('ArmNodeSocketAction', 'Out') + + 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() + + return NodeReplacement( + 'LNSetMaterialImageParamNode', self.arm_version, 'LNSetMaterialImageParamNode', 2, + in_socket_mapping={0:0, 1:2, 2:3, 3:4}, out_socket_mapping={0:0} + ) diff --git a/blender/arm/logicnode/material/LN_set_material_rgb_param.py b/blender/arm/logicnode/material/LN_set_material_rgb_param.py index 34948c9d..52ef94b9 100644 --- a/blender/arm/logicnode/material/LN_set_material_rgb_param.py +++ b/blender/arm/logicnode/material/LN_set_material_rgb_param.py @@ -1,17 +1,36 @@ from arm.logicnode.arm_nodes import * class SetMaterialRgbParamNode(ArmLogicTreeNode): - """TO DO.""" + """Set a color or vector value material parameter to the specified object. If `per object` is disabled, value will be set to all objects with this material""" bl_idname = 'LNSetMaterialRgbParamNode' bl_label = 'Set Material RGB Param' arm_section = 'params' - arm_version = 1 + arm_version = 2 + + property0: BoolProperty( + name="Per Object", + description="Set property per object", + default=False + ) def init(self, context): super(SetMaterialRgbParamNode, self).init(context) self.add_input('ArmNodeSocketAction', 'In') + self.add_input('ArmNodeSocketObject', 'Object') self.add_input('NodeSocketShader', 'Material') self.add_input('NodeSocketString', 'Node') self.add_input('NodeSocketColor', 'Color') self.add_output('ArmNodeSocketAction', 'Out') + + 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() + + return NodeReplacement( + 'LNSetMaterialRgbParamNode', self.arm_version, 'LNSetMaterialRgbParamNode', 2, + in_socket_mapping={0:0, 1:2, 2:3, 3:4}, out_socket_mapping={0:0} + ) diff --git a/blender/arm/logicnode/material/LN_set_material_value_param.py b/blender/arm/logicnode/material/LN_set_material_value_param.py index d668c7f9..5e99f14a 100644 --- a/blender/arm/logicnode/material/LN_set_material_value_param.py +++ b/blender/arm/logicnode/material/LN_set_material_value_param.py @@ -1,7 +1,7 @@ from arm.logicnode.arm_nodes import * class SetMaterialValueParamNode(ArmLogicTreeNode): - """Set a float value material parameter to the specified object""" + """Set a float value material parameter to the specified object. If `per object` is disabled, value will be set to all objects with this material""" bl_idname = 'LNSetMaterialValueParamNode' bl_label = 'Set Material Value Param' arm_section = 'params' From dbd348ad5dec85185051b171a837de80cc019741 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 9 Jun 2021 19:09:24 +0200 Subject: [PATCH 38/81] update node scripts --- .../logicnode/SetMaterialImageParamNode.hx | 38 +++++++--------- .../logicnode/SetMaterialRgbParamNode.hx | 37 ++++++++-------- .../logicnode/SetMaterialValueParamNode.hx | 43 +++---------------- 3 files changed, 39 insertions(+), 79 deletions(-) diff --git a/Sources/armory/logicnode/SetMaterialImageParamNode.hx b/Sources/armory/logicnode/SetMaterialImageParamNode.hx index abee9941..eea685a5 100644 --- a/Sources/armory/logicnode/SetMaterialImageParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialImageParamNode.hx @@ -1,40 +1,34 @@ package armory.logicnode; +import iron.Scene; import iron.data.MaterialData; import iron.object.Object; +import armory.trait.internal.UniformsManager; class SetMaterialImageParamNode extends LogicNode { - static var registered = false; - static var map = new Map>(); + public var property0: Bool; // per object + + var manager: UniformsManager; public function new(tree: LogicTree) { super(tree); - if (!registered) { - registered = true; - iron.object.Uniforms.externalTextureLinks.push(textureLink); - } + + manager = new UniformsManager(UniformType.Texture); } override function run(from: Int) { - var mat = inputs[1].get(); - if (mat == null) return; - var entry = map.get(mat); - if (entry == null) { - entry = new Map(); - map.set(mat, entry); + var object = inputs[1].get(); + if(object == null) return; + + var mat = inputs[2].get(); + if(mat == null) return; + + if(! property0){ + object = Scene.active.root; } - iron.data.Data.getImage(inputs[3].get(), function(image: kha.Image) { - entry.set(inputs[2].get(), image); // Node name, value - }); + manager.setTextureValue(mat, object, inputs[3].get(), inputs[4].get()); runOutput(0); } - - static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image { - if (mat == null) return null; - var entry = map.get(mat); - if (entry == null) return null; - return entry.get(link); - } } diff --git a/Sources/armory/logicnode/SetMaterialRgbParamNode.hx b/Sources/armory/logicnode/SetMaterialRgbParamNode.hx index 2db7bbc9..802a758e 100644 --- a/Sources/armory/logicnode/SetMaterialRgbParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialRgbParamNode.hx @@ -1,38 +1,35 @@ package armory.logicnode; +import iron.Scene; import iron.math.Vec4; import iron.data.MaterialData; import iron.object.Object; +import armory.trait.internal.UniformsManager; class SetMaterialRgbParamNode extends LogicNode { - static var registered = false; - static var map = new Map>(); + public var property0: Bool; // per object + + var manager: UniformsManager; public function new(tree: LogicTree) { super(tree); - if (!registered) { - registered = true; - iron.object.Uniforms.externalVec3Links.push(vec3Link); - } + + manager = new UniformsManager(UniformType.Vector); } override function run(from: Int) { - var mat = inputs[1].get(); - if (mat == null) return; - var entry = map.get(mat); - if (entry == null) { - entry = new Map(); - map.set(mat, entry); + var object = inputs[1].get(); + if(object == null) return; + + var mat = inputs[2].get(); + if(mat == null) return; + + if(! property0){ + object = Scene.active.root; } - entry.set(inputs[2].get(), inputs[3].get()); // Node name, value + + manager.setVec3Value(mat, object, inputs[3].get(), inputs[4].get()); runOutput(0); } - - static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 { - if (mat == null) return null; - var entry = map.get(mat); - if (entry == null) return null; - return entry.get(link); - } } diff --git a/Sources/armory/logicnode/SetMaterialValueParamNode.hx b/Sources/armory/logicnode/SetMaterialValueParamNode.hx index ec33130b..ea8e60c3 100644 --- a/Sources/armory/logicnode/SetMaterialValueParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialValueParamNode.hx @@ -3,20 +3,18 @@ package armory.logicnode; import iron.Scene; import iron.data.MaterialData; import iron.object.Object; +import armory.trait.internal.UniformsManager; class SetMaterialValueParamNode extends LogicNode { public var property0: Bool; // per object - - static var registered = false; - static var map = new Map>>>(); + + var manager: UniformsManager; public function new(tree: LogicTree) { super(tree); - if (!registered) { - registered = true; - iron.object.Uniforms.externalFloatLinks.push(floatLink); - } + + manager = new UniformsManager(UniformType.Float); } override function run(from: Int) { @@ -30,36 +28,7 @@ class SetMaterialValueParamNode extends LogicNode { object = Scene.active.root; } - var matMap = map.get(object); - if (matMap == null) { - matMap = new Map(); - map.set(object, matMap); - } - - var entry = matMap.get(mat); - if (entry == null) { - entry = new Map(); - matMap.set(mat, entry); - } - - entry.set(inputs[3].get(), inputs[4].get()); // Node name, value + manager.setFloatValue(mat, object, inputs[3].get(), inputs[4].get()); runOutput(0); } - - static function floatLink(object: Object, mat: MaterialData, link: String): Null { - if(object == null) return null; - if (mat == null) return null; - - if(! map.exists(object)){ - object = Scene.active.root; - } - - var material = map.get(object); - if (material == null) return null; - - var entry = material.get(mat); - if (entry == null) return null; - - return entry.get(link); - } } From e744fd901c3d37dcc270d8052598a0ef5f2ed19a Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 9 Jun 2021 19:09:45 +0200 Subject: [PATCH 39/81] create and implement uniforms manager remove node mapping Revert "remove node mapping" This reverts commit e70aa60e120e71236cba885bd7e0e5f1b6acf39d. --- .../armory/trait/internal/UniformsManager.hx | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 Sources/armory/trait/internal/UniformsManager.hx diff --git a/Sources/armory/trait/internal/UniformsManager.hx b/Sources/armory/trait/internal/UniformsManager.hx new file mode 100644 index 00000000..e5686f97 --- /dev/null +++ b/Sources/armory/trait/internal/UniformsManager.hx @@ -0,0 +1,161 @@ +package armory.trait.internal; + +import haxe.ds.Map; +import iron.math.Vec4; +import iron.data.MaterialData; +import iron.Scene; +import iron.object.Object; +import iron.object.Uniforms; + + +class UniformsManager{ + + static var floatsRegistered = false; + static var floatsMap = new Map>>>(); + + static var vectorsRegistered = false; + static var vectorsMap = new Map>>(); + + static var texturesRegistered = false; + static var texturesMap = new Map>>(); + + public function new(type: UniformType){ + switch (type){ + case Float:{ + if(! floatsRegistered){ + floatsRegistered = true; + Uniforms.externalFloatLinks.push(floatLink); + trace("Reg float"); + } + } + + case Vector:{ + if(! vectorsRegistered){ + vectorsRegistered = true; + Uniforms.externalVec3Links.push(vec3Link); + } + } + + case Texture:{ + if(! texturesRegistered){ + texturesRegistered = true; + Uniforms.externalTextureLinks.push(textureLink); + } + } + } + } + + public function setFloatValue(material: MaterialData, object: Object, link: String, value: Null){ + + var map = floatsMap; + + var matMap = map.get(object); + if (matMap == null) { + matMap = new Map(); + map.set(object, matMap); + } + + var entry = matMap.get(material); + if (entry == null) { + entry = new Map(); + matMap.set(material, entry); + } + + entry.set(link, value); // parameter name, value + } + + public function setVec3Value(material: MaterialData, object: Object, link: String, value: Vec4){ + + var map = vectorsMap; + + var matMap = map.get(object); + if (matMap == null) { + matMap = new Map(); + map.set(object, matMap); + } + + var entry = matMap.get(material); + if (entry == null) { + entry = new Map(); + matMap.set(material, entry); + } + + entry.set(link, value); // parameter name, value + } + + public function setTextureValue(material: MaterialData, object: Object, link: String, value: kha.Image){ + + var map = texturesMap; + + var matMap = map.get(object); + if (matMap == null) { + matMap = new Map(); + map.set(object, matMap); + } + + var entry = matMap.get(material); + if (entry == null) { + entry = new Map(); + matMap.set(material, entry); + } + + entry.set(link, value); // parameter name, value + } + + static function floatLink(object: Object, mat: MaterialData, link: String): Null { + if(object == null) return null; + if (mat == null) return null; + + if(! floatsMap.exists(object)){ + object = Scene.active.root; + } + + var material = floatsMap.get(object); + if (material == null) return null; + + var entry = material.get(mat); + if (entry == null) return null; + + return entry.get(link); + } + + static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 { + if(object == null) return null; + if (mat == null) return null; + + if(! vectorsMap.exists(object)){ + object = Scene.active.root; + } + + var material = vectorsMap.get(object); + if (material == null) return null; + + var entry = material.get(mat); + if (entry == null) return null; + + return entry.get(link); + } + + static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image { + if(object == null) return null; + if (mat == null) return null; + + if(! texturesMap.exists(object)){ + object = Scene.active.root; + } + + var material = texturesMap.get(object); + if (material == null) return null; + + var entry = material.get(mat); + if (entry == null) return null; + + return entry.get(link); + } +} + +@:enum abstract UniformType(Int) from Int to Int { + var Float = 0; + var Vector = 1; + var Texture = 2; +} \ No newline at end of file From 39bd32f9d3c55f380299a1b0d69c0af61282aaff Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 9 Jun 2021 20:01:29 +0200 Subject: [PATCH 40/81] get image from node --- Sources/armory/logicnode/SetMaterialImageParamNode.hx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/armory/logicnode/SetMaterialImageParamNode.hx b/Sources/armory/logicnode/SetMaterialImageParamNode.hx index eea685a5..dcea7d31 100644 --- a/Sources/armory/logicnode/SetMaterialImageParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialImageParamNode.hx @@ -28,7 +28,12 @@ class SetMaterialImageParamNode extends LogicNode { object = Scene.active.root; } - manager.setTextureValue(mat, object, inputs[3].get(), inputs[4].get()); + var img = inputs[4].get(); + if(img == null) return; + iron.data.Data.getImage(img, function(image: kha.Image) { + manager.setTextureValue(mat, object, inputs[3].get(), image); + }); + runOutput(0); } } From ea4f88aca826a2952d870b22a0901a211a7989ae Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Wed, 9 Jun 2021 20:01:44 +0200 Subject: [PATCH 41/81] remove debug trace --- Sources/armory/trait/internal/UniformsManager.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/armory/trait/internal/UniformsManager.hx b/Sources/armory/trait/internal/UniformsManager.hx index e5686f97..970ba840 100644 --- a/Sources/armory/trait/internal/UniformsManager.hx +++ b/Sources/armory/trait/internal/UniformsManager.hx @@ -25,7 +25,6 @@ class UniformsManager{ if(! floatsRegistered){ floatsRegistered = true; Uniforms.externalFloatLinks.push(floatLink); - trace("Reg float"); } } From 39922bc0f3f8f1ec497b0596e4aedba53747fe84 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 14 Jun 2021 13:31:00 +0200 Subject: [PATCH 42/81] move uniforms manager to iron --- .../armory/trait/internal/UniformsManager.hx | 160 ------------------ 1 file changed, 160 deletions(-) delete mode 100644 Sources/armory/trait/internal/UniformsManager.hx diff --git a/Sources/armory/trait/internal/UniformsManager.hx b/Sources/armory/trait/internal/UniformsManager.hx deleted file mode 100644 index 970ba840..00000000 --- a/Sources/armory/trait/internal/UniformsManager.hx +++ /dev/null @@ -1,160 +0,0 @@ -package armory.trait.internal; - -import haxe.ds.Map; -import iron.math.Vec4; -import iron.data.MaterialData; -import iron.Scene; -import iron.object.Object; -import iron.object.Uniforms; - - -class UniformsManager{ - - static var floatsRegistered = false; - static var floatsMap = new Map>>>(); - - static var vectorsRegistered = false; - static var vectorsMap = new Map>>(); - - static var texturesRegistered = false; - static var texturesMap = new Map>>(); - - public function new(type: UniformType){ - switch (type){ - case Float:{ - if(! floatsRegistered){ - floatsRegistered = true; - Uniforms.externalFloatLinks.push(floatLink); - } - } - - case Vector:{ - if(! vectorsRegistered){ - vectorsRegistered = true; - Uniforms.externalVec3Links.push(vec3Link); - } - } - - case Texture:{ - if(! texturesRegistered){ - texturesRegistered = true; - Uniforms.externalTextureLinks.push(textureLink); - } - } - } - } - - public function setFloatValue(material: MaterialData, object: Object, link: String, value: Null){ - - var map = floatsMap; - - var matMap = map.get(object); - if (matMap == null) { - matMap = new Map(); - map.set(object, matMap); - } - - var entry = matMap.get(material); - if (entry == null) { - entry = new Map(); - matMap.set(material, entry); - } - - entry.set(link, value); // parameter name, value - } - - public function setVec3Value(material: MaterialData, object: Object, link: String, value: Vec4){ - - var map = vectorsMap; - - var matMap = map.get(object); - if (matMap == null) { - matMap = new Map(); - map.set(object, matMap); - } - - var entry = matMap.get(material); - if (entry == null) { - entry = new Map(); - matMap.set(material, entry); - } - - entry.set(link, value); // parameter name, value - } - - public function setTextureValue(material: MaterialData, object: Object, link: String, value: kha.Image){ - - var map = texturesMap; - - var matMap = map.get(object); - if (matMap == null) { - matMap = new Map(); - map.set(object, matMap); - } - - var entry = matMap.get(material); - if (entry == null) { - entry = new Map(); - matMap.set(material, entry); - } - - entry.set(link, value); // parameter name, value - } - - static function floatLink(object: Object, mat: MaterialData, link: String): Null { - if(object == null) return null; - if (mat == null) return null; - - if(! floatsMap.exists(object)){ - object = Scene.active.root; - } - - var material = floatsMap.get(object); - if (material == null) return null; - - var entry = material.get(mat); - if (entry == null) return null; - - return entry.get(link); - } - - static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 { - if(object == null) return null; - if (mat == null) return null; - - if(! vectorsMap.exists(object)){ - object = Scene.active.root; - } - - var material = vectorsMap.get(object); - if (material == null) return null; - - var entry = material.get(mat); - if (entry == null) return null; - - return entry.get(link); - } - - static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image { - if(object == null) return null; - if (mat == null) return null; - - if(! texturesMap.exists(object)){ - object = Scene.active.root; - } - - var material = texturesMap.get(object); - if (material == null) return null; - - var entry = material.get(mat); - if (entry == null) return null; - - return entry.get(link); - } -} - -@:enum abstract UniformType(Int) from Int to Int { - var Float = 0; - var Vector = 1; - var Texture = 2; -} \ No newline at end of file From 15da6ccf58395658258f1c73da811a6f87782f49 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 14 Jun 2021 13:32:00 +0200 Subject: [PATCH 43/81] Upgrade nodes to include object. Add documnetation --- .../material/LN_set_material_image_param.py | 29 ++++++++++++------- .../material/LN_set_material_rgb_param.py | 29 ++++++++++++------- .../material/LN_set_material_value_param.py | 28 +++++++++++------- 3 files changed, 54 insertions(+), 32 deletions(-) diff --git a/blender/arm/logicnode/material/LN_set_material_image_param.py b/blender/arm/logicnode/material/LN_set_material_image_param.py index d442dc9a..e50482c6 100644 --- a/blender/arm/logicnode/material/LN_set_material_image_param.py +++ b/blender/arm/logicnode/material/LN_set_material_image_param.py @@ -1,36 +1,43 @@ from arm.logicnode.arm_nodes import * class SetMaterialImageParamNode(ArmLogicTreeNode): - """Set a image or texture value material parameter to the specified object. If `per object` is disabled, value will be set to all objects with this material""" + """Set an image value material parameter to the specified object. + + @seeNode Get Scene Root + + @input Object: Object whose material parameter should change. Use `Get Scene Root` node to set paramter globally. + + @input Per Object: + - `Enabled`: Set material parameter specific to this object. Global parameter will be ignored. + - `Disabled`: Set parameter globally, including this object. + + @input Material: Material whose parameter to be set. + + @input Node: Name of the parameter. + + @input Image: Name of the image. + """ bl_idname = 'LNSetMaterialImageParamNode' bl_label = 'Set Material Image Param' arm_section = 'params' arm_version = 2 - property0: BoolProperty( - name="Per Object", - description="Set property per object", - default=False - ) - def init(self, context): super(SetMaterialImageParamNode, self).init(context) self.add_input('ArmNodeSocketAction', 'In') self.add_input('ArmNodeSocketObject', 'Object') + self.add_input('NodeSocketBool', 'Per Object') self.add_input('NodeSocketShader', 'Material') self.add_input('NodeSocketString', 'Node') self.add_input('NodeSocketString', 'Image') self.add_output('ArmNodeSocketAction', 'Out') - 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() return NodeReplacement( 'LNSetMaterialImageParamNode', self.arm_version, 'LNSetMaterialImageParamNode', 2, - in_socket_mapping={0:0, 1:2, 2:3, 3:4}, out_socket_mapping={0:0} + in_socket_mapping={0:0, 1:3, 2:4, 3:5}, out_socket_mapping={0:0} ) diff --git a/blender/arm/logicnode/material/LN_set_material_rgb_param.py b/blender/arm/logicnode/material/LN_set_material_rgb_param.py index 52ef94b9..67d52f5d 100644 --- a/blender/arm/logicnode/material/LN_set_material_rgb_param.py +++ b/blender/arm/logicnode/material/LN_set_material_rgb_param.py @@ -1,36 +1,43 @@ from arm.logicnode.arm_nodes import * class SetMaterialRgbParamNode(ArmLogicTreeNode): - """Set a color or vector value material parameter to the specified object. If `per object` is disabled, value will be set to all objects with this material""" + """Set a color or vector value material parameter to the specified object. + + @seeNode Get Scene Root + + @input Object: Object whose material parameter should change. Use `Get Scene Root` node to set paramter globally. + + @input Per Object: + - `Enabled`: Set material parameter specific to this object. Global parameter will be ignored. + - `Disabled`: Set parameter globally, including this object. + + @input Material: Material whose parameter to be set. + + @input Node: Name of the parameter. + + @input Color: Color or vector input. + """ bl_idname = 'LNSetMaterialRgbParamNode' bl_label = 'Set Material RGB Param' arm_section = 'params' arm_version = 2 - property0: BoolProperty( - name="Per Object", - description="Set property per object", - default=False - ) - def init(self, context): super(SetMaterialRgbParamNode, self).init(context) self.add_input('ArmNodeSocketAction', 'In') self.add_input('ArmNodeSocketObject', 'Object') + self.add_input('NodeSocketBool', 'Per Object') self.add_input('NodeSocketShader', 'Material') self.add_input('NodeSocketString', 'Node') self.add_input('NodeSocketColor', 'Color') self.add_output('ArmNodeSocketAction', 'Out') - 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() return NodeReplacement( 'LNSetMaterialRgbParamNode', self.arm_version, 'LNSetMaterialRgbParamNode', 2, - in_socket_mapping={0:0, 1:2, 2:3, 3:4}, out_socket_mapping={0:0} + in_socket_mapping={0:0, 1:3, 2:4, 3:5}, out_socket_mapping={0:0} ) diff --git a/blender/arm/logicnode/material/LN_set_material_value_param.py b/blender/arm/logicnode/material/LN_set_material_value_param.py index 5e99f14a..d1218b05 100644 --- a/blender/arm/logicnode/material/LN_set_material_value_param.py +++ b/blender/arm/logicnode/material/LN_set_material_value_param.py @@ -1,30 +1,38 @@ from arm.logicnode.arm_nodes import * class SetMaterialValueParamNode(ArmLogicTreeNode): - """Set a float value material parameter to the specified object. If `per object` is disabled, value will be set to all objects with this material""" + """Set a float value material parameter to the specified object. + + @seeNode Get Scene Root + + @input Object: Object whose material parameter should change. Use `Get Scene Root` node to set paramter globally. + + @input Per Object: + - `Enabled`: Set material parameter specific to this object. Global parameter will be ignored. + - `Disabled`: Set parameter globally, including this object. + + @input Material: Material whose parameter to be set. + + @input Node: Name of the parameter. + + @input Float: float value. + """ bl_idname = 'LNSetMaterialValueParamNode' bl_label = 'Set Material Value Param' arm_section = 'params' arm_version = 2 - property0: BoolProperty( - name="Per Object", - description="Set property per object", - default=False - ) - def init(self, context): super(SetMaterialValueParamNode, self).init(context) self.add_input('ArmNodeSocketAction', 'In') self.add_input('ArmNodeSocketObject', 'Object') + self.add_input('NodeSocketBool', 'Per Object') self.add_input('NodeSocketShader', 'Material') self.add_input('NodeSocketString', 'Node') self.add_input('NodeSocketFloat', 'Float') self.add_output('ArmNodeSocketAction', 'Out') - 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): @@ -32,5 +40,5 @@ class SetMaterialValueParamNode(ArmLogicTreeNode): return NodeReplacement( 'LNSetMaterialValueParamNode', self.arm_version, 'LNSetMaterialValueParamNode', 2, - in_socket_mapping={0:0, 1:2, 2:3, 3:4}, out_socket_mapping={0:0} + in_socket_mapping={0:0, 1:3, 2:4, 3:5}, out_socket_mapping={0:0} ) From c44e2cf555e303a704e428c0a90f70a9b5b43899 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 14 Jun 2021 13:33:35 +0200 Subject: [PATCH 44/81] Pass default vector value when adding uniform --- blender/arm/material/cycles_nodes/nodes_input.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/blender/arm/material/cycles_nodes/nodes_input.py b/blender/arm/material/cycles_nodes/nodes_input.py index 69072bcd..f4b99f63 100644 --- a/blender/arm/material/cycles_nodes/nodes_input.py +++ b/blender/arm/material/cycles_nodes/nodes_input.py @@ -92,7 +92,13 @@ def parse_attribute(node: bpy.types.ShaderNodeAttribute, out_socket: bpy.types.N def parse_rgb(node: bpy.types.ShaderNodeRGB, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str: if node.arm_material_param: nn = 'param_' + c.node_name(node.name) - state.curshader.add_uniform(f'vec3 {nn}', link=f'{node.name}') + v = out_socket.default_value + value = [] + value.append(float(v[0])) + value.append(float(v[1])) + value.append(float(v[2])) + is_arm_mat_param = True + state.curshader.add_uniform(f'vec3 {nn}', link=f'{node.name}', default_value = value, is_arm_mat_param = is_arm_mat_param) return nn else: return c.to_vec3(out_socket.default_value) From fa44147ee6f031d3fab3a3ed496a454c2b69dd2f Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 14 Jun 2021 13:34:30 +0200 Subject: [PATCH 45/81] Pass default float value when adding uniform --- blender/arm/material/cycles_nodes/nodes_input.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/blender/arm/material/cycles_nodes/nodes_input.py b/blender/arm/material/cycles_nodes/nodes_input.py index f4b99f63..335e56f6 100644 --- a/blender/arm/material/cycles_nodes/nodes_input.py +++ b/blender/arm/material/cycles_nodes/nodes_input.py @@ -357,7 +357,9 @@ def parse_lightpath(node: bpy.types.ShaderNodeLightPath, out_socket: bpy.types.N def parse_value(node: bpy.types.ShaderNodeValue, out_socket: bpy.types.NodeSocket, state: ParserState) -> floatstr: if node.arm_material_param: nn = 'param_' + c.node_name(node.name) - state.curshader.add_uniform('float {0}'.format(nn), link='{0}'.format(node.name)) + value = c.to_vec1(node.outputs[0].default_value) + is_arm_mat_param = True + state.curshader.add_uniform('float {0}'.format(nn), link='{0}'.format(node.name), default_value=value, is_arm_mat_param=is_arm_mat_param) return nn else: return c.to_vec1(node.outputs[0].default_value) From 340f7e8af4a261ad72bc78b5e69d30fa919b1c8a Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 14 Jun 2021 13:35:08 +0200 Subject: [PATCH 46/81] Pass default image file name when adding uniform --- blender/arm/material/cycles_nodes/nodes_texture.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/blender/arm/material/cycles_nodes/nodes_texture.py b/blender/arm/material/cycles_nodes/nodes_texture.py index 516d555b..9952a0cb 100644 --- a/blender/arm/material/cycles_nodes/nodes_texture.py +++ b/blender/arm/material/cycles_nodes/nodes_texture.py @@ -114,15 +114,22 @@ def parse_tex_image(node: bpy.types.ShaderNodeTexImage, out_socket: bpy.types.No tex_name = c.node_name(node.name) tex = c.make_texture(node, tex_name) - tex_link = node.name if node.arm_material_param else None + tex_link = None + tex_default_file = None + is_arm_mat_param = None + if node.arm_material_param: + tex_link = node.name + is_arm_mat_param = True + if tex['file'] is not None: + tex_default_file = tex['file'] if tex is not None: state.curshader.write_textures += 1 if use_color_out: to_linear = node.image is not None and node.image.colorspace_settings.name == 'sRGB' - res = f'{c.texture_store(node, tex, tex_name, to_linear, tex_link=tex_link)}.rgb' + res = f'{c.texture_store(node, tex, tex_name, to_linear, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.rgb' else: - res = f'{c.texture_store(node, tex, tex_name, tex_link=tex_link)}.a' + res = f'{c.texture_store(node, tex, tex_name, tex_link=tex_link, default_value=tex_default_file, is_arm_mat_param=is_arm_mat_param)}.a' state.curshader.write_textures -= 1 return res From 975ec76f38533bb380b9bf0a0f3cb981ad7c6d4a Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 14 Jun 2021 13:35:49 +0200 Subject: [PATCH 47/81] Set default image file --- blender/arm/material/cycles.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blender/arm/material/cycles.py b/blender/arm/material/cycles.py index 20dbd83e..625c24c2 100644 --- a/blender/arm/material/cycles.py +++ b/blender/arm/material/cycles.py @@ -560,7 +560,7 @@ def to_uniform(inp: bpy.types.NodeSocket): def store_var_name(node: bpy.types.Node): return node_name(node.name) + '_store' -def texture_store(node, tex, tex_name, to_linear=False, tex_link=None): +def texture_store(node, tex, tex_name, to_linear=False, tex_link=None, default_value=None, is_arm_mat_param=None): curshader = state.curshader tex_store = store_var_name(node) @@ -569,7 +569,7 @@ def texture_store(node, tex, tex_name, to_linear=False, tex_link=None): state.parsed.add(tex_store) mat_bind_texture(tex) state.con.add_elem('tex', 'short2norm') - curshader.add_uniform('sampler2D {0}'.format(tex_name), link=tex_link) + curshader.add_uniform('sampler2D {0}'.format(tex_name), link=tex_link, default_value=default_value, is_arm_mat_param=is_arm_mat_param) triplanar = node.projection == 'BOX' if node.inputs[0].is_linked: uv_name = parse_vector_input(node.inputs[0]) From 5a591fa15a2691af1c2e1139fe3429cf812aa22f Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 14 Jun 2021 13:36:30 +0200 Subject: [PATCH 48/81] set default vector and float parameter --- blender/arm/material/shader.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/blender/arm/material/shader.py b/blender/arm/material/shader.py index be2af682..8e107628 100644 --- a/blender/arm/material/shader.py +++ b/blender/arm/material/shader.py @@ -109,19 +109,27 @@ class ShaderContext: def get(self): return self.data - def add_constant(self, ctype, name, link=None): + def add_constant(self, ctype, name, link=None, default_value=None, is_arm_mat_param=None): for c in self.constants: if c['name'] == name: return - c = { 'name': name, 'type': ctype } - if link != None: + c = { 'name': name, 'type': ctype} + if link is not None: c['link'] = link + if default_value is not None: + if ctype == 'float': + c['float'] = default_value + if ctype == 'vec3': + c['vec3'] = default_value + if is_arm_mat_param is not None: + c['is_arm_parameter'] = 'true' self.constants.append(c) def add_texture_unit(self, name, link=None, is_image=None, addr_u=None, addr_v=None, - filter_min=None, filter_mag=None, mipmap_filter=None): + filter_min=None, filter_mag=None, mipmap_filter=None, + default_value=None, is_arm_mat_param=None): for c in self.tunits: if c['name'] == name: return @@ -141,6 +149,10 @@ class ShaderContext: c['filter_mag'] = filter_mag if mipmap_filter is not None: c['mipmap_filter'] = mipmap_filter + if default_value is not None: + c['default_image_file'] = default_value + if is_arm_mat_param is not None: + c['is_arm_parameter'] = 'true' self.tunits.append(c) @@ -238,7 +250,7 @@ class Shader: def add_uniform(self, s, link=None, included=False, top=False, tex_addr_u=None, tex_addr_v=None, tex_filter_min=None, tex_filter_mag=None, - tex_mipmap_filter=None): + tex_mipmap_filter=None, default_value=None, is_arm_mat_param=None): ar = s.split(' ') # layout(RGBA8) image3D voxels utype = ar[-2] @@ -257,7 +269,8 @@ class Shader: self.context.add_texture_unit( uname, link, is_image, tex_addr_u, tex_addr_v, - tex_filter_min, tex_filter_mag, tex_mipmap_filter) + tex_filter_min, tex_filter_mag, tex_mipmap_filter, + default_value=default_value, is_arm_mat_param=is_arm_mat_param) else: # Prefer vec4[] for d3d to avoid padding if ar[0] == 'float' and '[' in ar[1]: @@ -266,7 +279,7 @@ class Shader: elif ar[0] == 'vec4' and '[' in ar[1]: ar[0] = 'floats' ar[1] = ar[1].split('[', 1)[0] - self.context.add_constant(ar[0], ar[1], link=link) + self.context.add_constant(ar[0], ar[1], link=link, default_value=default_value, is_arm_mat_param=is_arm_mat_param) if top: if not included and s not in self.uniforms_top: self.uniforms_top.append(s) From eab7fcbbe6ad54b6ca826ff0d044b6f9e590840b Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Mon, 14 Jun 2021 13:37:10 +0200 Subject: [PATCH 49/81] update node implementations --- .../logicnode/SetMaterialImageParamNode.hx | 19 +++++++++---------- .../logicnode/SetMaterialRgbParamNode.hx | 18 ++++++++---------- .../logicnode/SetMaterialValueParamNode.hx | 19 +++++++++---------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/Sources/armory/logicnode/SetMaterialImageParamNode.hx b/Sources/armory/logicnode/SetMaterialImageParamNode.hx index dcea7d31..aa91932e 100644 --- a/Sources/armory/logicnode/SetMaterialImageParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialImageParamNode.hx @@ -3,35 +3,34 @@ package armory.logicnode; import iron.Scene; import iron.data.MaterialData; import iron.object.Object; -import armory.trait.internal.UniformsManager; +import iron.data.UniformsManager; class SetMaterialImageParamNode extends LogicNode { - - public var property0: Bool; // per object - var manager: UniformsManager; - public function new(tree: LogicTree) { super(tree); - manager = new UniformsManager(UniformType.Texture); } override function run(from: Int) { var object = inputs[1].get(); if(object == null) return; - var mat = inputs[2].get(); + var perObject = inputs[2].get(); + if(perObject == null) perObject = false; + + var mat = inputs[3].get(); if(mat == null) return; - if(! property0){ + if(! perObject){ + UniformsManager.removeObjectFromMap(object, Texture); object = Scene.active.root; } - var img = inputs[4].get(); + var img = inputs[5].get(); if(img == null) return; iron.data.Data.getImage(img, function(image: kha.Image) { - manager.setTextureValue(mat, object, inputs[3].get(), image); + UniformsManager.setTextureValue(mat, object, inputs[4].get(), image); }); runOutput(0); diff --git a/Sources/armory/logicnode/SetMaterialRgbParamNode.hx b/Sources/armory/logicnode/SetMaterialRgbParamNode.hx index 802a758e..575cbb96 100644 --- a/Sources/armory/logicnode/SetMaterialRgbParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialRgbParamNode.hx @@ -4,32 +4,30 @@ import iron.Scene; import iron.math.Vec4; import iron.data.MaterialData; import iron.object.Object; -import armory.trait.internal.UniformsManager; +import iron.data.UniformsManager; class SetMaterialRgbParamNode extends LogicNode { - - public var property0: Bool; // per object - var manager: UniformsManager; - public function new(tree: LogicTree) { super(tree); - - manager = new UniformsManager(UniformType.Vector); } override function run(from: Int) { var object = inputs[1].get(); if(object == null) return; - var mat = inputs[2].get(); + var perObject = inputs[2].get(); + if(perObject == null) perObject = false; + + var mat = inputs[3].get(); if(mat == null) return; - if(! property0){ + if(! perObject){ + UniformsManager.removeObjectFromMap(object, Vector); object = Scene.active.root; } - manager.setVec3Value(mat, object, inputs[3].get(), inputs[4].get()); + UniformsManager.setVec3Value(mat, object, inputs[4].get(), inputs[5].get()); runOutput(0); } } diff --git a/Sources/armory/logicnode/SetMaterialValueParamNode.hx b/Sources/armory/logicnode/SetMaterialValueParamNode.hx index ea8e60c3..0c6da7ab 100644 --- a/Sources/armory/logicnode/SetMaterialValueParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialValueParamNode.hx @@ -3,32 +3,31 @@ package armory.logicnode; import iron.Scene; import iron.data.MaterialData; import iron.object.Object; -import armory.trait.internal.UniformsManager; +import iron.data.UniformsManager; class SetMaterialValueParamNode extends LogicNode { - - public var property0: Bool; // per object - var manager: UniformsManager; - public function new(tree: LogicTree) { super(tree); - - manager = new UniformsManager(UniformType.Float); } override function run(from: Int) { var object = inputs[1].get(); if(object == null) return; - var mat = inputs[2].get(); + var perObject = inputs[2].get(); + if(perObject == null) perObject = false; + + var mat = inputs[3].get(); if(mat == null) return; - if(! property0){ + if(! perObject){ + UniformsManager.removeObjectFromMap(object, Float); object = Scene.active.root; } - manager.setFloatValue(mat, object, inputs[3].get(), inputs[4].get()); + UniformsManager.setFloatValue(mat, object, inputs[4].get(), inputs[5].get()); runOutput(0); } + } From 936f11ed8eb193714395364b9357c10f16717435 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Tue, 15 Jun 2021 16:22:42 +0200 Subject: [PATCH 50/81] Move uniforms manager back to armory --- .../armory/trait/internal/UniformsManager.hx | 267 ++++++++++++++++++ .../material/LN_set_material_image_param.py | 2 +- .../material/LN_set_material_rgb_param.py | 2 +- .../material/LN_set_material_value_param.py | 2 +- 4 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 Sources/armory/trait/internal/UniformsManager.hx diff --git a/Sources/armory/trait/internal/UniformsManager.hx b/Sources/armory/trait/internal/UniformsManager.hx new file mode 100644 index 00000000..b41f8f83 --- /dev/null +++ b/Sources/armory/trait/internal/UniformsManager.hx @@ -0,0 +1,267 @@ +package iron.data; + +import kha.Image; +import iron.math.Vec4; +import iron.data.MaterialData; +import iron.Scene; +import iron.object.Object; +import iron.object.Uniforms; + + +class UniformsManager{ + + static var floatsRegistered = false; + static var floatsMap = new Map>>>(); + + static var vectorsRegistered = false; + static var vectorsMap = new Map>>(); + + static var texturesRegistered = false; + static var texturesMap = new Map>>(); + + // Helper method to register float, vec3 and texture getter functions + static function register(type: UniformType){ + + switch (type){ + case Float:{ + if(! floatsRegistered){ + floatsRegistered = true; + Uniforms.externalFloatLinks.push(floatLink); + } + } + + case Vector:{ + if(! vectorsRegistered){ + vectorsRegistered = true; + Uniforms.externalVec3Links.push(vec3Link); + } + } + + case Texture:{ + if(! texturesRegistered){ + texturesRegistered = true; + Uniforms.externalTextureLinks.push(textureLink); + } + } + } + } + + // Register and map shader uniforms if it is an armory shader parameter + public static function registerShaderUniforms(material: MaterialData) { + + if(! floatsMap.exists(Scene.active.root)) floatsMap.set(Scene.active.root, null); + if(! vectorsMap.exists(Scene.active.root)) vectorsMap.set(Scene.active.root, null); + if(! texturesMap.exists(Scene.active.root)) texturesMap.set(Scene.active.root, null); + + for(context in material.shader.raw.contexts){ // For each context in shader + for (constant in context.constants){ // For each constant in the context + if(constant.is_arm_parameter){ // Chack if armory parameter + + var object = Scene.active.root; // Map default uniforms to scene root + + switch (constant.type){ + case "float":{ + var link = constant.link; + var value:Float = constant.float; + setFloatValue(material, object, link, value); + register(Float); + } + + case "vec3":{ + + var vec = new Vec4(); + vec.x = constant.vec3.get(0); + vec.y = constant.vec3.get(1); + vec.z = constant.vec3.get(2); + + setVec3Value(material, object, constant.link, vec); + register(Vector); + + } + } + } + } + for (texture in context.texture_units){ + if(texture.is_arm_parameter){ // Chack if armory parameter + + var object = Scene.active.root; // Map default texture to scene root + + iron.data.Data.getImage(texture.default_image_file, function(image: kha.Image) { + setTextureValue(material, object, texture.link, image); + + }); + register(Texture); + + } + + } + } + + } + + // Method to set map Object -> Material -> Link -> FLoat + public static function setFloatValue(material: MaterialData, object: Object, link: String, value: Null){ + + if(object == null || material == null || link == null) return; + + var map = floatsMap; + + var matMap = map.get(object); + if (matMap == null) { + matMap = new Map(); + map.set(object, matMap); + } + + var entry = matMap.get(material); + if (entry == null) { + entry = new Map(); + matMap.set(material, entry); + } + + entry.set(link, value); // parameter name, value + } + + // Method to set map Object -> Material -> Link -> Vec3 + public static function setVec3Value(material: MaterialData, object: Object, link: String, value: Vec4){ + + if(object == null || material == null || link == null) return; + + var map = vectorsMap; + + var matMap = map.get(object); + if (matMap == null) { + matMap = new Map(); + map.set(object, matMap); + } + + var entry = matMap.get(material); + if (entry == null) { + entry = new Map(); + matMap.set(material, entry); + } + + entry.set(link, value); // parameter name, value + } + + // Method to set map Object -> Material -> Link -> Texture + public static function setTextureValue(material: MaterialData, object: Object, link: String, value: kha.Image){ + + if(object == null || material == null || link == null) return; + + var map = texturesMap; + + var matMap = map.get(object); + if (matMap == null) { + matMap = new Map(); + map.set(object, matMap); + } + + var entry = matMap.get(material); + if (entry == null) { + entry = new Map(); + matMap.set(material, entry); + } + + entry.set(link, value); // parameter name, value + } + + // Mehtod to get object specific material parameter float value + static function floatLink(object: Object, mat: MaterialData, link: String): Null { + + if(object == null || mat == null) return null; + + if(! floatsMap.exists(object)){ + object = Scene.active.root; + } + + var material = floatsMap.get(object); + if (material == null) return null; + + var entry = material.get(mat); + if (entry == null) return null; + + return entry.get(link); + } + + // Mehtod to get object specific material parameter vec3 value + static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 { + + if(object == null || mat == null) return null; + + if(! vectorsMap.exists(object)){ + object = Scene.active.root; + } + + var material = vectorsMap.get(object); + if (material == null) return null; + + var entry = material.get(mat); + if (entry == null) return null; + + return entry.get(link); + } + + // Mehtod to get object specific material parameter texture value + static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image { + + if(object == null || mat == null) return null; + + if(! texturesMap.exists(object)){ + object = Scene.active.root; + } + + var material = texturesMap.get(object); + if (material == null) return null; + + var entry = material.get(mat); + if (entry == null) return null; + + return entry.get(link); + } + + // Returns complete map of float value material paramets + public static function getFloatsMap():Map>>>{ + + return floatsMap; + } + + // Returns complete map of vec3 value material paramets + public static function getVectorsMap():Map>>{ + + return vectorsMap; + } + + // Returns complete map of texture value material paramets + public static function getTexturesMap():Map>>{ + + return texturesMap; + } + + // Remove all object specific material paramenter keys + public static function removeObjectFromAllMaps(object: Object) { + + floatsMap.remove(object); + vectorsMap.remove(object); + texturesMap.remove(object); + + } + + // Remove object specific material paramenter keys + public static function removeObjectFromMap(object: Object, type: UniformType) { + + switch (type){ + case Float: floatsMap.remove(object); + + case Vector: vectorsMap.remove(object); + + case Texture: texturesMap.remove(object); + } + + } +} + +@:enum abstract UniformType(Int) from Int to Int { + var Float = 0; + var Vector = 1; + var Texture = 2; +} \ No newline at end of file diff --git a/blender/arm/logicnode/material/LN_set_material_image_param.py b/blender/arm/logicnode/material/LN_set_material_image_param.py index e50482c6..1002e94e 100644 --- a/blender/arm/logicnode/material/LN_set_material_image_param.py +++ b/blender/arm/logicnode/material/LN_set_material_image_param.py @@ -5,7 +5,7 @@ class SetMaterialImageParamNode(ArmLogicTreeNode): @seeNode Get Scene Root - @input Object: Object whose material parameter should change. Use `Get Scene Root` node to set paramter globally. + @input Object: Object whose material parameter should change. Use `Get Scene Root` node to set parameter globally. @input Per Object: - `Enabled`: Set material parameter specific to this object. Global parameter will be ignored. diff --git a/blender/arm/logicnode/material/LN_set_material_rgb_param.py b/blender/arm/logicnode/material/LN_set_material_rgb_param.py index 67d52f5d..f99c7ff7 100644 --- a/blender/arm/logicnode/material/LN_set_material_rgb_param.py +++ b/blender/arm/logicnode/material/LN_set_material_rgb_param.py @@ -5,7 +5,7 @@ class SetMaterialRgbParamNode(ArmLogicTreeNode): @seeNode Get Scene Root - @input Object: Object whose material parameter should change. Use `Get Scene Root` node to set paramter globally. + @input Object: Object whose material parameter should change. Use `Get Scene Root` node to set parameter globally. @input Per Object: - `Enabled`: Set material parameter specific to this object. Global parameter will be ignored. diff --git a/blender/arm/logicnode/material/LN_set_material_value_param.py b/blender/arm/logicnode/material/LN_set_material_value_param.py index d1218b05..439f54aa 100644 --- a/blender/arm/logicnode/material/LN_set_material_value_param.py +++ b/blender/arm/logicnode/material/LN_set_material_value_param.py @@ -5,7 +5,7 @@ class SetMaterialValueParamNode(ArmLogicTreeNode): @seeNode Get Scene Root - @input Object: Object whose material parameter should change. Use `Get Scene Root` node to set paramter globally. + @input Object: Object whose material parameter should change. Use `Get Scene Root` node to set parameter globally. @input Per Object: - `Enabled`: Set material parameter specific to this object. Global parameter will be ignored. From 5a2f952f896137c13a28e898d3d8636e11be14b2 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 24 Jun 2021 21:32:48 +0200 Subject: [PATCH 51/81] Add uniforms manager to mesh objects --- blender/arm/exporter.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index ed408ae7..5546b834 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -2460,6 +2460,13 @@ Make sure the mesh only has tris/quads.""") else: self.material_to_object_dict[mat] = [bobject] self.material_to_arm_object_dict[mat] = [o] + + # Add UniformsManager trait + if type is NodeType.MESH: + uniformManager = {} + uniformManager['type'] = 'Script' + uniformManager['class_name'] = 'armory.trait.internal.UniformsManager' + o['traits'].append(uniformManager) # Export constraints if len(bobject.constraints) > 0: From ee43724b97e2b3bfdd2ea5cee791520eaa3d7c52 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 24 Jun 2021 21:33:21 +0200 Subject: [PATCH 52/81] Change imports --- Sources/armory/logicnode/SetMaterialImageParamNode.hx | 2 +- Sources/armory/logicnode/SetMaterialRgbParamNode.hx | 2 +- Sources/armory/logicnode/SetMaterialValueParamNode.hx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/armory/logicnode/SetMaterialImageParamNode.hx b/Sources/armory/logicnode/SetMaterialImageParamNode.hx index aa91932e..6b0c0ed8 100644 --- a/Sources/armory/logicnode/SetMaterialImageParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialImageParamNode.hx @@ -3,7 +3,7 @@ package armory.logicnode; import iron.Scene; import iron.data.MaterialData; import iron.object.Object; -import iron.data.UniformsManager; +import armory.trait.internal.UniformsManager; class SetMaterialImageParamNode extends LogicNode { diff --git a/Sources/armory/logicnode/SetMaterialRgbParamNode.hx b/Sources/armory/logicnode/SetMaterialRgbParamNode.hx index 575cbb96..f0be3552 100644 --- a/Sources/armory/logicnode/SetMaterialRgbParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialRgbParamNode.hx @@ -4,7 +4,7 @@ import iron.Scene; import iron.math.Vec4; import iron.data.MaterialData; import iron.object.Object; -import iron.data.UniformsManager; +import armory.trait.internal.UniformsManager; class SetMaterialRgbParamNode extends LogicNode { diff --git a/Sources/armory/logicnode/SetMaterialValueParamNode.hx b/Sources/armory/logicnode/SetMaterialValueParamNode.hx index 0c6da7ab..8523bb8c 100644 --- a/Sources/armory/logicnode/SetMaterialValueParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialValueParamNode.hx @@ -3,7 +3,7 @@ package armory.logicnode; import iron.Scene; import iron.data.MaterialData; import iron.object.Object; -import iron.data.UniformsManager; +import armory.trait.internal.UniformsManager; class SetMaterialValueParamNode extends LogicNode { From 112c00a649f580b182c3d42e7c687d8f83d413f6 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 24 Jun 2021 21:33:53 +0200 Subject: [PATCH 53/81] Modify unifroms manager --- .../armory/trait/internal/UniformsManager.hx | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/Sources/armory/trait/internal/UniformsManager.hx b/Sources/armory/trait/internal/UniformsManager.hx index b41f8f83..549c6d34 100644 --- a/Sources/armory/trait/internal/UniformsManager.hx +++ b/Sources/armory/trait/internal/UniformsManager.hx @@ -1,5 +1,7 @@ -package iron.data; +package armory.trait.internal; +import iron.object.MeshObject; +import iron.Trait; import kha.Image; import iron.math.Vec4; import iron.data.MaterialData; @@ -8,7 +10,7 @@ import iron.object.Object; import iron.object.Uniforms; -class UniformsManager{ +class UniformsManager extends Trait{ static var floatsRegistered = false; static var floatsMap = new Map>>>(); @@ -19,6 +21,52 @@ class UniformsManager{ static var texturesRegistered = false; static var texturesMap = new Map>>(); + static var sceneRemoveInitalized = false; + + public var unifromExists = false; + + public function new(){ + super(); + + notifyOnInit(init); + + notifyOnRemove(removeObject); + + if(! sceneRemoveInitalized){ + + Scene.active.notifyOnRemove(removeScene); + } + } + + function init() { + var materials = cast(object, MeshObject).materials; + + for (material in materials){ + + var exists = registerShaderUniforms(material); + if(exists) { + unifromExists = true; + } + + } + + if(! unifromExists) { + + this.remove(); + } + } + + static function removeScene(){ + + removeObjectFromAllMaps(Scene.active.root); + } + + function removeObject() { + + removeObjectFromAllMaps(object); + + } + // Helper method to register float, vec3 and texture getter functions static function register(type: UniformType){ @@ -47,7 +95,9 @@ class UniformsManager{ } // Register and map shader uniforms if it is an armory shader parameter - public static function registerShaderUniforms(material: MaterialData) { + public static function registerShaderUniforms(material: MaterialData) : Bool { + + var unifromExist = false; if(! floatsMap.exists(Scene.active.root)) floatsMap.set(Scene.active.root, null); if(! vectorsMap.exists(Scene.active.root)) vectorsMap.set(Scene.active.root, null); @@ -57,6 +107,7 @@ class UniformsManager{ for (constant in context.constants){ // For each constant in the context if(constant.is_arm_parameter){ // Chack if armory parameter + unifromExist = true; var object = Scene.active.root; // Map default uniforms to scene root switch (constant.type){ @@ -84,6 +135,7 @@ class UniformsManager{ for (texture in context.texture_units){ if(texture.is_arm_parameter){ // Chack if armory parameter + unifromExist = true; var object = Scene.active.root; // Map default texture to scene root iron.data.Data.getImage(texture.default_image_file, function(image: kha.Image) { @@ -96,6 +148,8 @@ class UniformsManager{ } } + + return unifromExist; } From f2cf3bdeda8013507ca604612cab9f91577310ba Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 24 Jun 2021 23:12:15 +0200 Subject: [PATCH 54/81] Add new property to set if constraint is relative --- blender/arm/props.py | 1 + blender/arm/props_ui.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/blender/arm/props.py b/blender/arm/props.py index 2b23d334..23e3f170 100755 --- a/blender/arm/props.py +++ b/blender/arm/props.py @@ -286,6 +286,7 @@ def init_properties(): default=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False), size=20, subtype='LAYER') + bpy.types.Object.arm_relative_physics_constraint = BoolProperty(name="Relative Physics Constraint", description="Add physics constraint relative to the parent object or collection when spawned", default=False) bpy.types.Object.arm_animation_enabled = BoolProperty(name="Animation", description="Enable skinning & timeline animation", default=True) bpy.types.Object.arm_tilesheet = StringProperty(name="Tilesheet", description="Set tilesheet animation", default='') bpy.types.Object.arm_tilesheet_action = StringProperty(name="Tilesheet Action", description="Set startup action", default='') diff --git a/blender/arm/props_ui.py b/blender/arm/props_ui.py index 880c7f9a..d69886d1 100644 --- a/blender/arm/props_ui.py +++ b/blender/arm/props_ui.py @@ -211,8 +211,11 @@ class ARM_PT_PhysicsPropsPanel(bpy.types.Panel): layout.prop(obj, 'arm_rb_trigger') layout.prop(obj, 'arm_rb_ccd') - if obj.soft_body != None: + if obj.soft_body is not None: layout.prop(obj, 'arm_soft_body_margin') + + if obj.rigid_body_constraint is not None: + layout.prop(obj, 'arm_relative_physics_constraint') # Menu in data region class ARM_PT_DataPropsPanel(bpy.types.Panel): From ac48fd0bc9ae79b517683abf89273f9cb01368bb Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 24 Jun 2021 23:12:51 +0200 Subject: [PATCH 55/81] Implement adding of relative physics constraint --- .../bullet/PhysicsConstraintExportHelper.hx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Sources/armory/trait/physics/bullet/PhysicsConstraintExportHelper.hx b/Sources/armory/trait/physics/bullet/PhysicsConstraintExportHelper.hx index 11a0ac24..bb536bf2 100644 --- a/Sources/armory/trait/physics/bullet/PhysicsConstraintExportHelper.hx +++ b/Sources/armory/trait/physics/bullet/PhysicsConstraintExportHelper.hx @@ -18,8 +18,9 @@ class PhysicsConstraintExportHelper extends iron.Trait { var breakingThreshold: Float; var limits: Array; var constraintAdded: Bool = false; + var relativeConstraint: Bool = false; - public function new(body1: String, body2: String, type: Int, disableCollisions: Bool, breakingThreshold: Float, limits: Array = null) { + public function new(body1: String, body2: String, type: Int, disableCollisions: Bool, breakingThreshold: Float, relatieConstraint: Bool = false, limits: Array = null) { super(); this.body1 = body1; @@ -27,14 +28,26 @@ class PhysicsConstraintExportHelper extends iron.Trait { this.type = type; this.disableCollisions = disableCollisions; this.breakingThreshold = breakingThreshold; + this.relativeConstraint = relatieConstraint; this.limits = limits; notifyOnInit(init); notifyOnUpdate(update); } function init() { - var target1 = Scene.active.getChild(body1); - var target2 = Scene.active.getChild(body2); + var target1; + var target2; + + if(relativeConstraint) { + + target1 = object.parent.getChild(body1); + target2 = object.parent.getChild(body2); + } + else { + + target1 = Scene.active.getChild(body1); + target2 = Scene.active.getChild(body2); + } object.addTrait(new PhysicsConstraint(target1, target2, type, disableCollisions, breakingThreshold, limits)); constraintAdded = true; } From 31a2c9c4d3e8b059a268fcc5f2d0384854bb0497 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 24 Jun 2021 23:13:29 +0200 Subject: [PATCH 56/81] Modify export of physics constraint --- blender/arm/exporter.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index ed408ae7..1f9b0213 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -2440,7 +2440,7 @@ Make sure the mesh only has tris/quads.""") # Rigid body constraint rbc = bobject.rigid_body_constraint if rbc is not None and rbc.enabled: - self.add_rigidbody_constraint(o, rbc) + self.add_rigidbody_constraint(o, bobject, rbc) # Camera traits if type is NodeType.CAMERA: @@ -2747,7 +2747,7 @@ Make sure the mesh only has tris/quads.""") o['traits'].append(out_trait) @staticmethod - def add_rigidbody_constraint(o, rbc): + def add_rigidbody_constraint(o, bobject, rbc): rb1 = rbc.object1 rb2 = rbc.object2 if rb1 is None or rb2 is None: @@ -2766,7 +2766,8 @@ Make sure the mesh only has tris/quads.""") "'" + rb1.name + "'", "'" + rb2.name + "'", str(rbc.disable_collisions).lower(), - str(breaking_threshold) + str(breaking_threshold), + str(bobject.arm_relative_physics_constraint).lower() ] } if rbc.type == "FIXED": From dfad6902afd7cb18ed61c6690a0740187b09cc5d Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 27 Jun 2021 18:18:04 -0300 Subject: [PATCH 57/81] Add Relative Parent option to location nodes --- Sources/armory/logicnode/GetLocationNode.hx | 16 +++++++++++++++- Sources/armory/logicnode/SetLocationNode.hx | 15 +++++++++++++++ .../logicnode/VectorToObjectOrientationNode.hx | 4 +--- .../logicnode/WorldVectorToLocalSpaceNode.hx | 4 +--- .../transform/LN_get_object_location.py | 10 +++++++++- .../transform/LN_set_object_location.py | 10 +++++++++- .../transform/LN_vector_to_object_orientation.py | 5 ++--- .../transform/LN_world_vector_to_local_space.py | 5 ++--- 8 files changed, 54 insertions(+), 15 deletions(-) diff --git a/Sources/armory/logicnode/GetLocationNode.hx b/Sources/armory/logicnode/GetLocationNode.hx index 961f8280..e5f5bee1 100644 --- a/Sources/armory/logicnode/GetLocationNode.hx +++ b/Sources/armory/logicnode/GetLocationNode.hx @@ -1,6 +1,7 @@ package armory.logicnode; import iron.object.Object; +import iron.math.Vec4; class GetLocationNode extends LogicNode { @@ -10,9 +11,22 @@ class GetLocationNode extends LogicNode { override function get(from: Int): Dynamic { var object: Object = inputs[0].get(); + var relative: Bool = inputs[1].get(); if (object == null) return null; - return object.transform.world.getLoc(); + var loc = object.transform.world.getLoc(); + + if (relative) { + loc.sub(object.parent.transform.world.getLoc()); + + var vec = new Vec4(); + vec.x = loc.dot(object.parent.transform.right()); + vec.y = loc.dot(object.parent.transform.look()); + vec.z = loc.dot(object.parent.transform.up()); + loc.setFrom(vec); + } + + return loc; } } diff --git a/Sources/armory/logicnode/SetLocationNode.hx b/Sources/armory/logicnode/SetLocationNode.hx index 709e457b..382b0264 100644 --- a/Sources/armory/logicnode/SetLocationNode.hx +++ b/Sources/armory/logicnode/SetLocationNode.hx @@ -2,10 +2,13 @@ package armory.logicnode; import iron.object.Object; import iron.math.Vec4; +import iron.math.Quat; import armory.trait.physics.RigidBody; class SetLocationNode extends LogicNode { + var quat = new Quat(); + public function new(tree: LogicTree) { super(tree); } @@ -13,9 +16,21 @@ class SetLocationNode extends LogicNode { override function run(from: Int) { var object: Object = inputs[1].get(); var vec: Vec4 = inputs[2].get(); + var relative: Bool = inputs[3].get(); if (object == null || vec == null) return; + if (!relative) { + vec.sub(object.parent.transform.world.getLoc()); // Remove parent location influence + + // Convert vec to parent local space + var vec1 = new Vec4(); + vec1.x = vec.dot(object.parent.transform.right()); + vec1.y = vec.dot(object.parent.transform.look()); + vec1.z = vec.dot(object.parent.transform.up()); + vec.setFrom(vec1); + } + object.transform.loc.setFrom(vec); object.transform.buildMatrix(); diff --git a/Sources/armory/logicnode/VectorToObjectOrientationNode.hx b/Sources/armory/logicnode/VectorToObjectOrientationNode.hx index 2a72590a..27a4b994 100644 --- a/Sources/armory/logicnode/VectorToObjectOrientationNode.hx +++ b/Sources/armory/logicnode/VectorToObjectOrientationNode.hx @@ -3,8 +3,6 @@ package armory.logicnode; import iron.object.Object; import iron.math.Vec4; -using armory.object.TransformExtension; - class VectorToObjectOrientationNode extends LogicNode { public function new(tree: LogicTree) { @@ -18,7 +16,7 @@ class VectorToObjectOrientationNode extends LogicNode { if (object == null || vec == null) return null; - return object.transform.worldVecToOrientation(vec); + return vec.applyQuat(object.transform.rot); } } diff --git a/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx b/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx index 493ed615..44e12f15 100644 --- a/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx +++ b/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx @@ -3,8 +3,6 @@ package armory.logicnode; import iron.math.Vec4; import iron.object.Object; -using armory.object.TransformExtension; - class WorldVectorToLocalSpaceNode extends LogicNode { public function new(tree: LogicTree) { @@ -17,7 +15,7 @@ class WorldVectorToLocalSpaceNode extends LogicNode { if (object == null || worldVec == null) return null; - var localVec: Vec4 = new Vec4(); + var localVec = new Vec4(); localVec.x = worldVec.dot(object.transform.right()); localVec.y = worldVec.dot(object.transform.look()); diff --git a/blender/arm/logicnode/transform/LN_get_object_location.py b/blender/arm/logicnode/transform/LN_get_object_location.py index c708ba9a..62fdb9fb 100644 --- a/blender/arm/logicnode/transform/LN_get_object_location.py +++ b/blender/arm/logicnode/transform/LN_get_object_location.py @@ -1,7 +1,14 @@ from arm.logicnode.arm_nodes import * class GetLocationNode(ArmLogicTreeNode): - """Returns the current location of the given object in world coordinates.""" + """Get the location of the given object in world coordinates. + + @input Parent Relative: If enabled, transforms the world coordinates into object parent local coordinates + + @seeNode Set Object Location + @seeNode World Vector to Local Space + @seeNode Vector to Object Orientation + """ bl_idname = 'LNGetLocationNode' bl_label = 'Get Object Location' arm_section = 'location' @@ -10,5 +17,6 @@ class GetLocationNode(ArmLogicTreeNode): def init(self, context): super(GetLocationNode, self).init(context) self.add_input('ArmNodeSocketObject', 'Object') + self.add_input('NodeSocketBool', 'Parent Relative') self.add_output('NodeSocketVector', 'Location') diff --git a/blender/arm/logicnode/transform/LN_set_object_location.py b/blender/arm/logicnode/transform/LN_set_object_location.py index 48afdffb..02bebaf5 100644 --- a/blender/arm/logicnode/transform/LN_set_object_location.py +++ b/blender/arm/logicnode/transform/LN_set_object_location.py @@ -1,7 +1,14 @@ from arm.logicnode.arm_nodes import * class SetLocationNode(ArmLogicTreeNode): - """Sets the location of the given object.""" + """Set the location of the given object in world coordinates. + + @input Parent Relative: If enabled, transforms the world coordinates into object parent local coordinates + + @seeNode Get Object Location + @seeNode World Vector to Local Space + @seeNode Vector to Object Orientation + """ bl_idname = 'LNSetLocationNode' bl_label = 'Set Object Location' arm_section = 'location' @@ -12,5 +19,6 @@ class SetLocationNode(ArmLogicTreeNode): self.add_input('ArmNodeSocketAction', 'In') self.add_input('ArmNodeSocketObject', 'Object') self.add_input('NodeSocketVector', 'Location') + self.add_input('NodeSocketBool', 'Parent Relative', default_value=True) self.add_output('ArmNodeSocketAction', 'Out') diff --git a/blender/arm/logicnode/transform/LN_vector_to_object_orientation.py b/blender/arm/logicnode/transform/LN_vector_to_object_orientation.py index e1402b6a..7c4bbcb0 100644 --- a/blender/arm/logicnode/transform/LN_vector_to_object_orientation.py +++ b/blender/arm/logicnode/transform/LN_vector_to_object_orientation.py @@ -1,10 +1,9 @@ from arm.logicnode.arm_nodes import * class VectorToObjectOrientationNode(ArmLogicTreeNode): - """Converts the given world vector to a vector oriented by the given object. - The object scale is taken in count. + """Transform world coordinates into object oriented coordinates (in other words: apply object rotation to it). - @seeNode World Vector To Object Space + @seeNode World Vector to Object Space @seeNode Get World Orientation @seeNode Vector From Transform """ diff --git a/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py b/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py index 61497f81..7d011fad 100644 --- a/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py +++ b/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py @@ -1,10 +1,9 @@ from arm.logicnode.arm_nodes import * class WorldVectorToLocalSpaceNode(ArmLogicTreeNode): - """Converts the given world vector to a object space vector. - The object scale is taken in count. + """Transform world coordinates into object local coordinates. - @seeNode Vector To Object Orientation + @seeNode Vector to Object Orientation @seeNode Get World Orientation @seeNode Vector From Transform """ From f940e8566e56bb17bfbd8d38dcc8d329cff29bf8 Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 27 Jun 2021 18:21:59 -0300 Subject: [PATCH 58/81] Remove unused quat --- Sources/armory/logicnode/GetLocationNode.hx | 5 +++-- Sources/armory/logicnode/SetLocationNode.hx | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/armory/logicnode/GetLocationNode.hx b/Sources/armory/logicnode/GetLocationNode.hx index e5f5bee1..216647e7 100644 --- a/Sources/armory/logicnode/GetLocationNode.hx +++ b/Sources/armory/logicnode/GetLocationNode.hx @@ -18,8 +18,9 @@ class GetLocationNode extends LogicNode { var loc = object.transform.world.getLoc(); if (relative) { - loc.sub(object.parent.transform.world.getLoc()); - + loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence + + // Convert vec to parent local space var vec = new Vec4(); vec.x = loc.dot(object.parent.transform.right()); vec.y = loc.dot(object.parent.transform.look()); diff --git a/Sources/armory/logicnode/SetLocationNode.hx b/Sources/armory/logicnode/SetLocationNode.hx index 382b0264..3c312890 100644 --- a/Sources/armory/logicnode/SetLocationNode.hx +++ b/Sources/armory/logicnode/SetLocationNode.hx @@ -2,7 +2,6 @@ package armory.logicnode; import iron.object.Object; import iron.math.Vec4; -import iron.math.Quat; import armory.trait.physics.RigidBody; class SetLocationNode extends LogicNode { From 13c1e0508d0c2a52d7e1ea3b99583546cfe78628 Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 27 Jun 2021 18:26:13 -0300 Subject: [PATCH 59/81] Fix for null parent --- Sources/armory/logicnode/GetLocationNode.hx | 2 +- Sources/armory/logicnode/SetLocationNode.hx | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/armory/logicnode/GetLocationNode.hx b/Sources/armory/logicnode/GetLocationNode.hx index 216647e7..df9c3360 100644 --- a/Sources/armory/logicnode/GetLocationNode.hx +++ b/Sources/armory/logicnode/GetLocationNode.hx @@ -17,7 +17,7 @@ class GetLocationNode extends LogicNode { var loc = object.transform.world.getLoc(); - if (relative) { + if (relative && object.parent != null) { loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence // Convert vec to parent local space diff --git a/Sources/armory/logicnode/SetLocationNode.hx b/Sources/armory/logicnode/SetLocationNode.hx index 3c312890..91e3f5a5 100644 --- a/Sources/armory/logicnode/SetLocationNode.hx +++ b/Sources/armory/logicnode/SetLocationNode.hx @@ -6,8 +6,6 @@ import armory.trait.physics.RigidBody; class SetLocationNode extends LogicNode { - var quat = new Quat(); - public function new(tree: LogicTree) { super(tree); } @@ -19,7 +17,7 @@ class SetLocationNode extends LogicNode { if (object == null || vec == null) return; - if (!relative) { + if (!relative && object.parent != null) { vec.sub(object.parent.transform.world.getLoc()); // Remove parent location influence // Convert vec to parent local space From 34b816b4d950c79f8f3ce5cea7de346d7d6153bf Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 27 Jun 2021 18:27:16 -0300 Subject: [PATCH 60/81] Remove default value --- blender/arm/logicnode/transform/LN_set_object_location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blender/arm/logicnode/transform/LN_set_object_location.py b/blender/arm/logicnode/transform/LN_set_object_location.py index 02bebaf5..2f2d02c6 100644 --- a/blender/arm/logicnode/transform/LN_set_object_location.py +++ b/blender/arm/logicnode/transform/LN_set_object_location.py @@ -19,6 +19,6 @@ class SetLocationNode(ArmLogicTreeNode): self.add_input('ArmNodeSocketAction', 'In') self.add_input('ArmNodeSocketObject', 'Object') self.add_input('NodeSocketVector', 'Location') - self.add_input('NodeSocketBool', 'Parent Relative', default_value=True) + self.add_input('NodeSocketBool', 'Parent Relative') self.add_output('ArmNodeSocketAction', 'Out') From 3d49edee7164a475884822e394bc68bf6c8210de Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 27 Jun 2021 19:13:00 -0300 Subject: [PATCH 61/81] Add Subtract option to World Vector to Local Space node --- Sources/armory/logicnode/GetLocationNode.hx | 9 ++++----- Sources/armory/logicnode/SetLocationNode.hx | 9 ++++----- Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx | 5 +++++ .../transform/LN_world_vector_to_local_space.py | 1 + 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Sources/armory/logicnode/GetLocationNode.hx b/Sources/armory/logicnode/GetLocationNode.hx index df9c3360..58ebbbad 100644 --- a/Sources/armory/logicnode/GetLocationNode.hx +++ b/Sources/armory/logicnode/GetLocationNode.hx @@ -21,11 +21,10 @@ class GetLocationNode extends LogicNode { loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence // Convert vec to parent local space - var vec = new Vec4(); - vec.x = loc.dot(object.parent.transform.right()); - vec.y = loc.dot(object.parent.transform.look()); - vec.z = loc.dot(object.parent.transform.up()); - loc.setFrom(vec); + var dotX = vec.dot(object.parent.transform.right()); + var dotY = vec.dot(object.parent.transform.look()); + var dotZ = vec.dot(object.parent.transform.up()); + loc.set(dotX, dotY, dotZ); } return loc; diff --git a/Sources/armory/logicnode/SetLocationNode.hx b/Sources/armory/logicnode/SetLocationNode.hx index 91e3f5a5..35b65cb0 100644 --- a/Sources/armory/logicnode/SetLocationNode.hx +++ b/Sources/armory/logicnode/SetLocationNode.hx @@ -21,11 +21,10 @@ class SetLocationNode extends LogicNode { vec.sub(object.parent.transform.world.getLoc()); // Remove parent location influence // Convert vec to parent local space - var vec1 = new Vec4(); - vec1.x = vec.dot(object.parent.transform.right()); - vec1.y = vec.dot(object.parent.transform.look()); - vec1.z = vec.dot(object.parent.transform.up()); - vec.setFrom(vec1); + var dotX = vec.dot(object.parent.transform.right()); + var dotY = vec.dot(object.parent.transform.look()); + var dotZ = vec.dot(object.parent.transform.up()); + vec.set(dotX, dotY, dotZ); } object.transform.loc.setFrom(vec); diff --git a/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx b/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx index 44e12f15..985c7de2 100644 --- a/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx +++ b/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx @@ -12,11 +12,16 @@ class WorldVectorToLocalSpaceNode extends LogicNode { override function get(from: Int): Vec4 { var object: Object = inputs[0].get(); var worldVec: Vec4 = inputs[1].get(); + var sub: Bool = inputs[2].get(); if (object == null || worldVec == null) return null; var localVec = new Vec4(); + if (sub) { + localVec.sub(object.transform.world.getLoc()); + } + localVec.x = worldVec.dot(object.transform.right()); localVec.y = worldVec.dot(object.transform.look()); localVec.z = worldVec.dot(object.transform.up()); diff --git a/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py b/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py index 7d011fad..e1461132 100644 --- a/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py +++ b/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py @@ -16,5 +16,6 @@ class WorldVectorToLocalSpaceNode(ArmLogicTreeNode): super(WorldVectorToLocalSpaceNode, self).init(context) self.add_input('ArmNodeSocketObject', 'Object') self.add_input('NodeSocketVector', 'World') + self.add_input('NodeSocketBool', 'Subtract', default_value=True) self.add_output('NodeSocketVector', 'Local') From 7515a20d93fd5e5fa698f041975327b09a92ba29 Mon Sep 17 00:00:00 2001 From: Henrique Date: Sun, 27 Jun 2021 20:47:55 -0300 Subject: [PATCH 62/81] Keep compatibility --- Sources/armory/logicnode/GetLocationNode.hx | 20 ++++++++++--------- Sources/armory/logicnode/SetLocationNode.hx | 19 ++++++++++-------- .../logicnode/WorldVectorToLocalSpaceNode.hx | 10 +++++++--- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/Sources/armory/logicnode/GetLocationNode.hx b/Sources/armory/logicnode/GetLocationNode.hx index 58ebbbad..0683350c 100644 --- a/Sources/armory/logicnode/GetLocationNode.hx +++ b/Sources/armory/logicnode/GetLocationNode.hx @@ -1,7 +1,6 @@ package armory.logicnode; import iron.object.Object; -import iron.math.Vec4; class GetLocationNode extends LogicNode { @@ -11,20 +10,23 @@ class GetLocationNode extends LogicNode { override function get(from: Int): Dynamic { var object: Object = inputs[0].get(); - var relative: Bool = inputs[1].get(); if (object == null) return null; var loc = object.transform.world.getLoc(); - if (relative && object.parent != null) { - loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence + if (inputs.length > 1) { // Keep compatibility + var relative: Bool = inputs[1].get(); - // Convert vec to parent local space - var dotX = vec.dot(object.parent.transform.right()); - var dotY = vec.dot(object.parent.transform.look()); - var dotZ = vec.dot(object.parent.transform.up()); - loc.set(dotX, dotY, dotZ); + if (relative && object.parent != null) { + loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence + + // Convert vec to parent local space + var dotX = vec.dot(object.parent.transform.right()); + var dotY = vec.dot(object.parent.transform.look()); + var dotZ = vec.dot(object.parent.transform.up()); + loc.set(dotX, dotY, dotZ); + } } return loc; diff --git a/Sources/armory/logicnode/SetLocationNode.hx b/Sources/armory/logicnode/SetLocationNode.hx index 35b65cb0..f578f813 100644 --- a/Sources/armory/logicnode/SetLocationNode.hx +++ b/Sources/armory/logicnode/SetLocationNode.hx @@ -13,18 +13,21 @@ class SetLocationNode extends LogicNode { override function run(from: Int) { var object: Object = inputs[1].get(); var vec: Vec4 = inputs[2].get(); - var relative: Bool = inputs[3].get(); if (object == null || vec == null) return; - if (!relative && object.parent != null) { - vec.sub(object.parent.transform.world.getLoc()); // Remove parent location influence + if (inputs.length > 3) { // Keep compatibility + var relative: Bool = inputs[3].get(); - // Convert vec to parent local space - var dotX = vec.dot(object.parent.transform.right()); - var dotY = vec.dot(object.parent.transform.look()); - var dotZ = vec.dot(object.parent.transform.up()); - vec.set(dotX, dotY, dotZ); + if (!relative && object.parent != null) { + vec.sub(object.parent.transform.world.getLoc()); // Remove parent location influence + + // Convert vec to parent local space + var dotX = vec.dot(object.parent.transform.right()); + var dotY = vec.dot(object.parent.transform.look()); + var dotZ = vec.dot(object.parent.transform.up()); + vec.set(dotX, dotY, dotZ); + } } object.transform.loc.setFrom(vec); diff --git a/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx b/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx index 985c7de2..e774f75b 100644 --- a/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx +++ b/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx @@ -12,14 +12,18 @@ class WorldVectorToLocalSpaceNode extends LogicNode { override function get(from: Int): Vec4 { var object: Object = inputs[0].get(); var worldVec: Vec4 = inputs[1].get(); - var sub: Bool = inputs[2].get(); + if (object == null || worldVec == null) return null; var localVec = new Vec4(); - if (sub) { - localVec.sub(object.transform.world.getLoc()); + if (inputs.length > 2) { // Keep compatibility + var sub: Bool = inputs[2].get(); + + if (sub) { + localVec.sub(object.transform.world.getLoc()); + } } localVec.x = worldVec.dot(object.transform.right()); From c7e1f5d0a9a2d9a543699366e768032a4202205e Mon Sep 17 00:00:00 2001 From: Henrique Date: Mon, 28 Jun 2021 16:12:05 -0300 Subject: [PATCH 63/81] Fix nodes --- Sources/armory/logicnode/GetLocationNode.hx | 19 +++++++--------- Sources/armory/logicnode/SetLocationNode.hx | 22 +++++++++---------- .../logicnode/WorldVectorToLocalSpaceNode.hx | 10 +-------- .../transform/LN_get_object_location.py | 7 +++++- .../transform/LN_set_object_location.py | 7 +++++- .../LN_world_vector_to_local_space.py | 1 - 6 files changed, 31 insertions(+), 35 deletions(-) diff --git a/Sources/armory/logicnode/GetLocationNode.hx b/Sources/armory/logicnode/GetLocationNode.hx index 0683350c..00684473 100644 --- a/Sources/armory/logicnode/GetLocationNode.hx +++ b/Sources/armory/logicnode/GetLocationNode.hx @@ -10,23 +10,20 @@ class GetLocationNode extends LogicNode { override function get(from: Int): Dynamic { var object: Object = inputs[0].get(); + var relative: Bool = inputs[1].get(); if (object == null) return null; var loc = object.transform.world.getLoc(); - if (inputs.length > 1) { // Keep compatibility - var relative: Bool = inputs[1].get(); + if (relative && object.parent != null) { + loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence - if (relative && object.parent != null) { - loc.sub(object.parent.transform.world.getLoc()); // Add parent location influence - - // Convert vec to parent local space - var dotX = vec.dot(object.parent.transform.right()); - var dotY = vec.dot(object.parent.transform.look()); - var dotZ = vec.dot(object.parent.transform.up()); - loc.set(dotX, dotY, dotZ); - } + // Convert loc to parent local space + var dotX = loc.dot(object.parent.transform.right()); + var dotY = loc.dot(object.parent.transform.look()); + var dotZ = loc.dot(object.parent.transform.up()); + loc.set(dotX, dotY, dotZ); } return loc; diff --git a/Sources/armory/logicnode/SetLocationNode.hx b/Sources/armory/logicnode/SetLocationNode.hx index f578f813..4f879586 100644 --- a/Sources/armory/logicnode/SetLocationNode.hx +++ b/Sources/armory/logicnode/SetLocationNode.hx @@ -13,21 +13,19 @@ class SetLocationNode extends LogicNode { override function run(from: Int) { var object: Object = inputs[1].get(); var vec: Vec4 = inputs[2].get(); + var relative: Bool = inputs[3].get(); if (object == null || vec == null) return; - if (inputs.length > 3) { // Keep compatibility - var relative: Bool = inputs[3].get(); + if (!relative && object.parent != null) { + var loc = vec.clone(); + loc.sub(object.parent.transform.world.getLoc()); // Remove parent location influence - if (!relative && object.parent != null) { - vec.sub(object.parent.transform.world.getLoc()); // Remove parent location influence - - // Convert vec to parent local space - var dotX = vec.dot(object.parent.transform.right()); - var dotY = vec.dot(object.parent.transform.look()); - var dotZ = vec.dot(object.parent.transform.up()); - vec.set(dotX, dotY, dotZ); - } + // Convert vec to parent local space + var dotX = loc.dot(object.parent.transform.right()); + var dotY = loc.dot(object.parent.transform.look()); + var dotZ = loc.dot(object.parent.transform.up()); + vec.set(dotX, dotY, dotZ); } object.transform.loc.setFrom(vec); @@ -40,4 +38,4 @@ class SetLocationNode extends LogicNode { runOutput(0); } -} +} \ No newline at end of file diff --git a/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx b/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx index e774f75b..75f128c1 100644 --- a/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx +++ b/Sources/armory/logicnode/WorldVectorToLocalSpaceNode.hx @@ -13,18 +13,10 @@ class WorldVectorToLocalSpaceNode extends LogicNode { var object: Object = inputs[0].get(); var worldVec: Vec4 = inputs[1].get(); - if (object == null || worldVec == null) return null; var localVec = new Vec4(); - - if (inputs.length > 2) { // Keep compatibility - var sub: Bool = inputs[2].get(); - - if (sub) { - localVec.sub(object.transform.world.getLoc()); - } - } + localVec.sub(object.transform.world.getLoc()); localVec.x = worldVec.dot(object.transform.right()); localVec.y = worldVec.dot(object.transform.look()); diff --git a/blender/arm/logicnode/transform/LN_get_object_location.py b/blender/arm/logicnode/transform/LN_get_object_location.py index 62fdb9fb..76458b5b 100644 --- a/blender/arm/logicnode/transform/LN_get_object_location.py +++ b/blender/arm/logicnode/transform/LN_get_object_location.py @@ -12,7 +12,7 @@ class GetLocationNode(ArmLogicTreeNode): bl_idname = 'LNGetLocationNode' bl_label = 'Get Object Location' arm_section = 'location' - arm_version = 1 + arm_version = 2 def init(self, context): super(GetLocationNode, self).init(context) @@ -20,3 +20,8 @@ class GetLocationNode(ArmLogicTreeNode): self.add_input('NodeSocketBool', 'Parent Relative') self.add_output('NodeSocketVector', 'Location') + + def get_replacement_node(self, node_tree: bpy.types.NodeTree): + if self.arm_version not in (0, 1): + raise LookupError() + return NodeReplacement.Identity(self) \ No newline at end of file diff --git a/blender/arm/logicnode/transform/LN_set_object_location.py b/blender/arm/logicnode/transform/LN_set_object_location.py index 2f2d02c6..5a2b9824 100644 --- a/blender/arm/logicnode/transform/LN_set_object_location.py +++ b/blender/arm/logicnode/transform/LN_set_object_location.py @@ -12,7 +12,7 @@ class SetLocationNode(ArmLogicTreeNode): bl_idname = 'LNSetLocationNode' bl_label = 'Set Object Location' arm_section = 'location' - arm_version = 1 + arm_version = 2 def init(self, context): super(SetLocationNode, self).init(context) @@ -22,3 +22,8 @@ class SetLocationNode(ArmLogicTreeNode): self.add_input('NodeSocketBool', 'Parent Relative') self.add_output('ArmNodeSocketAction', 'Out') + + def get_replacement_node(self, node_tree: bpy.types.NodeTree): + if self.arm_version not in (0, 1): + raise LookupError() + return NodeReplacement.Identity(self) \ No newline at end of file diff --git a/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py b/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py index e1461132..7d011fad 100644 --- a/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py +++ b/blender/arm/logicnode/transform/LN_world_vector_to_local_space.py @@ -16,6 +16,5 @@ class WorldVectorToLocalSpaceNode(ArmLogicTreeNode): super(WorldVectorToLocalSpaceNode, self).init(context) self.add_input('ArmNodeSocketObject', 'Object') self.add_input('NodeSocketVector', 'World') - self.add_input('NodeSocketBool', 'Subtract', default_value=True) self.add_output('NodeSocketVector', 'Local') From 7504d5d92b22fa3c7e3f9ffa93501bf2f405561b Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 1 Jul 2021 15:32:53 +0200 Subject: [PATCH 64/81] Change input type from shader to vector --- blender/arm/logicnode/navmesh/LN_go_to_location.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blender/arm/logicnode/navmesh/LN_go_to_location.py b/blender/arm/logicnode/navmesh/LN_go_to_location.py index eba3cf97..7f7d6e9d 100644 --- a/blender/arm/logicnode/navmesh/LN_go_to_location.py +++ b/blender/arm/logicnode/navmesh/LN_go_to_location.py @@ -10,7 +10,7 @@ class GoToLocationNode(ArmLogicTreeNode): super(GoToLocationNode, self).init(context) self.add_input('ArmNodeSocketAction', 'In') self.add_input('ArmNodeSocketObject', 'Object') - self.add_input('NodeSocketShader', 'Location') + self.add_input('NodeSocketVector', 'Location') self.add_output('ArmNodeSocketAction', 'Out') From 9e493f6e79f71f712c5e312cd5117d3cfde40bd4 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Thu, 1 Jul 2021 15:33:28 +0200 Subject: [PATCH 65/81] do not remove trait at init --- Sources/armory/trait/internal/UniformsManager.hx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/Sources/armory/trait/internal/UniformsManager.hx b/Sources/armory/trait/internal/UniformsManager.hx index 549c6d34..ed1ba2d5 100644 --- a/Sources/armory/trait/internal/UniformsManager.hx +++ b/Sources/armory/trait/internal/UniformsManager.hx @@ -47,24 +47,17 @@ class UniformsManager extends Trait{ if(exists) { unifromExists = true; } - - } - - if(! unifromExists) { - - this.remove(); } } - static function removeScene(){ + static function removeScene() { removeObjectFromAllMaps(Scene.active.root); } function removeObject() { - removeObjectFromAllMaps(object); - + removeObjectFromAllMaps(object); } // Helper method to register float, vec3 and texture getter functions @@ -310,7 +303,6 @@ class UniformsManager extends Trait{ case Texture: texturesMap.remove(object); } - } } From cb800729d2a8947397b52aa302736af7623d0db1 Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Sat, 3 Jul 2021 20:12:10 +0200 Subject: [PATCH 66/81] Add check Add a check to see if input socket is linked to correct output socket --- Sources/armory/logicnode/LogicNode.hx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Sources/armory/logicnode/LogicNode.hx b/Sources/armory/logicnode/LogicNode.hx index 9e5e367c..b7b78b1d 100644 --- a/Sources/armory/logicnode/LogicNode.hx +++ b/Sources/armory/logicnode/LogicNode.hx @@ -38,12 +38,16 @@ class LogicNode { **/ function runOutput(i: Int) { if (i >= outputs.length) return; - for (o in outputs[i]) { + for (output in outputs[i]) { // Check which input activated the node - for (j in 0...o.inputs.length) { - if (o.inputs[j].node == this) { - o.run(j); - break; + for (j in 0...output.inputs.length) { + // Check if the node is connected to the current node + if (output.inputs[j].node == this) { + // Check if the input socekt is linked to current output socket + if (output.inputs[j].from == i) { + output.run(j); + break; + } } } } @@ -60,6 +64,7 @@ class LogicNodeInput { @:allow(armory.logicnode.LogicNode) var node: LogicNode; + @:allow(armory.logicnode.LogicNode) var from: Int; // Socket index public function new(node: LogicNode, from: Int) { From e7da337530932b36cd7c9ef49567ae9df87c9a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Mon, 5 Jul 2021 19:25:47 +0200 Subject: [PATCH 67/81] UI Canvas: use font from asset for all element types --- Sources/armory/ui/Canvas.hx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Sources/armory/ui/Canvas.hx b/Sources/armory/ui/Canvas.hx index 2c66cf68..dc798adc 100644 --- a/Sources/armory/ui/Canvas.hx +++ b/Sources/armory/ui/Canvas.hx @@ -58,18 +58,18 @@ class Canvas { var rotated = element.rotation != null && element.rotation != 0; if (rotated) ui.g.pushRotation(element.rotation, ui._x + scaled(element.width) / 2, ui._y + scaled(element.height) / 2); + var font = ui.ops.font; + var fontAsset = isFontAsset(element.asset); + if (fontAsset) ui.ops.font = getAsset(canvas, element.asset); + switch (element.type) { case Text: - var font = ui.ops.font; var size = ui.fontSize; - var fontAsset = element.asset != null && StringTools.endsWith(element.asset, ".ttf"); - if (fontAsset) ui.ops.font = getAsset(canvas, element.asset); ui.fontSize = scaled(element.height); ui.t.TEXT_COL = getColor(element.color_text, getTheme(canvas.theme).TEXT_COL); ui.text(getText(canvas, element), element.alignment); - ui.ops.font = font; ui.fontSize = size; case Button: @@ -90,7 +90,6 @@ class Canvas { case Image: var image = getAsset(canvas, element.asset); - var fontAsset = element.asset != null && StringTools.endsWith(element.asset, ".ttf"); if (image != null && !fontAsset) { ui.imageScrollAlign = false; var tint = element.color != null ? element.color : 0xffffffff; @@ -218,6 +217,8 @@ class Canvas { case Empty: } + ui.ops.font = font; + if (element.children != null) { for (id in element.children) { drawElement(ui, canvas, elemById(canvas, id), scaled(element.x) + px, scaled(element.y) + py); @@ -257,6 +258,10 @@ class Canvas { return Std.int(f * _ui.SCALE()); } + static inline function isFontAsset(assetName: Null): Bool { + return assetName != null && StringTools.endsWith(assetName.toLowerCase(), ".ttf"); + } + public static inline function getColor(color: Null, defaultColor: Int): Int { return color != null ? color : defaultColor; } From dc6753c2ca3bda21fd1a4f16972e6a7ed5d03e08 Mon Sep 17 00:00:00 2001 From: Lubos Lenco Date: Tue, 6 Jul 2021 10:46:44 +0200 Subject: [PATCH 68/81] Bump version --- blender/arm/props.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blender/arm/props.py b/blender/arm/props.py index 23e3f170..5533755f 100755 --- a/blender/arm/props.py +++ b/blender/arm/props.py @@ -11,7 +11,7 @@ import arm.proxy import arm.utils # Armory version -arm_version = '2021.6' +arm_version = '2021.7' arm_commit = '$Id$' def get_project_html5_copy(self): From 801668a0c2cf874835a801b2e20bb9854507001e Mon Sep 17 00:00:00 2001 From: N8n5h Date: Tue, 6 Jul 2021 10:39:07 -0300 Subject: [PATCH 69/81] Fix compiling error with shadow map atlas shadow size option Added 512 option to Inc so it doesn't fails compilation because of missing option as explained here https://github.com/armory3d/armory/issues/2252#issue-937328497 --- Sources/armory/renderpath/Inc.hx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Sources/armory/renderpath/Inc.hx b/Sources/armory/renderpath/Inc.hx index ad44d2d8..0e37fdef 100644 --- a/Sources/armory/renderpath/Inc.hx +++ b/Sources/armory/renderpath/Inc.hx @@ -715,7 +715,9 @@ class ShadowMapAtlas { public static inline function getMaxAtlasSize(type: String): Int { #if arm_shadowmap_atlas_single_map - #if (rp_shadowmap_atlas_max_size == 1024) + #if (rp_shadowmap_atlas_max_size == 512) + return 512; + #elseif (rp_shadowmap_atlas_max_size == 1024) return 1024; #elseif (rp_shadowmap_atlas_max_size == 2048) return 2048; @@ -742,7 +744,9 @@ class ShadowMapAtlas { #end } case "spot": { - #if (rp_shadowmap_atlas_max_size_spot == 1024) + #if (rp_shadowmap_atlas_max_size_spot == 512) + return 512; + #elseif (rp_shadowmap_atlas_max_size_spot == 1024) return 1024; #elseif (rp_shadowmap_atlas_max_size_spot == 2048) return 2048; @@ -755,7 +759,9 @@ class ShadowMapAtlas { #end } case "sun": { - #if (rp_shadowmap_atlas_max_size_sun == 1024) + #if (rp_shadowmap_atlas_max_size_sun == 512) + return 512; + #elseif (rp_shadowmap_atlas_max_size_sun == 1024) return 1024; #elseif (rp_shadowmap_atlas_max_size_sun == 2048) return 2048; @@ -768,7 +774,9 @@ class ShadowMapAtlas { #end } default: { - #if (rp_shadowmap_atlas_max_size == 1024) + #if (rp_shadowmap_atlas_max_size == 512) + return 512; + #elseif (rp_shadowmap_atlas_max_size == 1024) return 1024; #elseif (rp_shadowmap_atlas_max_size == 2048) return 2048; From 919512fad0c4f8e0b9838b5a04cb95781615f042 Mon Sep 17 00:00:00 2001 From: Henrique Date: Wed, 7 Jul 2021 16:33:20 -0300 Subject: [PATCH 70/81] Improve InputMap and add nodes to it --- .../armory/logicnode/GetInputMapKeyNode.hx | 24 + Sources/armory/logicnode/OnInputMapNode.hx | 35 ++ .../armory/logicnode/SetInputMapKeyNode.hx | 47 ++ Sources/armory/system/InputMap.hx | 452 ++++++------------ .../logicnode/input/LN_get_input_map_key.py | 15 + .../arm/logicnode/input/LN_on_input_map.py | 16 + .../logicnode/input/LN_set_input_map_key.py | 28 ++ 7 files changed, 322 insertions(+), 295 deletions(-) create mode 100644 Sources/armory/logicnode/GetInputMapKeyNode.hx create mode 100644 Sources/armory/logicnode/OnInputMapNode.hx create mode 100644 Sources/armory/logicnode/SetInputMapKeyNode.hx create mode 100644 blender/arm/logicnode/input/LN_get_input_map_key.py create mode 100644 blender/arm/logicnode/input/LN_on_input_map.py create mode 100644 blender/arm/logicnode/input/LN_set_input_map_key.py diff --git a/Sources/armory/logicnode/GetInputMapKeyNode.hx b/Sources/armory/logicnode/GetInputMapKeyNode.hx new file mode 100644 index 00000000..251a6d12 --- /dev/null +++ b/Sources/armory/logicnode/GetInputMapKeyNode.hx @@ -0,0 +1,24 @@ +package armory.logicnode; + +import armory.system.InputMap; + +class GetInputMapKeyNode extends LogicNode { + + public function new(tree: LogicTree) { + super(tree); + } + + override function get(from: Int): Dynamic { + var inputMap = inputs[0].get(); + var key = inputs[1].get(); + + var k = InputMap.getInputMapKey(inputMap, key); + + if (k != null) { + if (from == 0) return k.scale; + else if (from == 1) return k.deadzone; + } + + return null; + } +} diff --git a/Sources/armory/logicnode/OnInputMapNode.hx b/Sources/armory/logicnode/OnInputMapNode.hx new file mode 100644 index 00000000..3e354f29 --- /dev/null +++ b/Sources/armory/logicnode/OnInputMapNode.hx @@ -0,0 +1,35 @@ +package armory.logicnode; + +import armory.system.InputMap; + +class OnInputMapNode extends LogicNode { + + var inputMap: Null; + + public function new(tree: LogicTree) { + super(tree); + + tree.notifyOnUpdate(update); + } + + function update() { + var i = inputs[0].get(); + + inputMap = InputMap.getInputMap(i); + + if (inputMap != null) { + if (inputMap.started()) { + runOutput(0); + } + + if (inputMap.released()) { + runOutput(1); + } + } + } + + override function get(from: Int): Dynamic { + if (from == 2) return inputMap.value(); + else return inputMap.lastKeyPressed; + } +} diff --git a/Sources/armory/logicnode/SetInputMapKeyNode.hx b/Sources/armory/logicnode/SetInputMapKeyNode.hx new file mode 100644 index 00000000..a449575d --- /dev/null +++ b/Sources/armory/logicnode/SetInputMapKeyNode.hx @@ -0,0 +1,47 @@ +package armory.logicnode; + +import armory.system.InputMap; + +class SetInputMapKeyNode extends LogicNode { + + public var property0: String; + + public function new(tree: LogicTree) { + super(tree); + } + + override function run(from: Int) { + var inputMap = inputs[1].get(); + var key = inputs[2].get(); + var scale = inputs[3].get(); + var deadzone = inputs[4].get(); + var index = inputs[5].get(); + + var i = InputMap.getInputMap(inputMap); + + if (i == null) { + i = InputMap.addInputMap(inputMap); + } + + var k = InputMap.getInputMapKey(inputMap, key); + + if (k == null) { + switch(property0) { + case "keyboard": k = i.addKeyboard(key, scale); + case "mouse": k = i.addMouse(key, scale, deadzone); + case "gamepad": { + k = i.addGamepad(key, scale, deadzone); + k.setIndex(index); + } + } + + } else { + k.key = key; + k.scale = scale; + k.deadzone = deadzone; + k.setIndex(index); + } + + runOutput(0); + } +} diff --git a/Sources/armory/system/InputMap.hx b/Sources/armory/system/InputMap.hx index 79b998e1..9b631cf9 100644 --- a/Sources/armory/system/InputMap.hx +++ b/Sources/armory/system/InputMap.hx @@ -4,347 +4,209 @@ import kha.FastFloat; import iron.system.Input; class InputMap { - var commands = new Map>>(); + + static var inputMaps(default, null) = new Map(); + + public var inputs(default, null) = new Array(); + public var lastKeyPressed(default, null) = ""; public function new() {} - public function addKeyboard(config: String) { - var command = new KeyboardCommand(); - return addCustomCommand(command, config); + public static function getInputMap(inputMap: String): Null { + if (inputMaps.exists(inputMap)) { + return inputMaps[inputMap]; + } + + return null; } - public function addGamepad(config: String) { - var command = new GamepadCommand(); - return addCustomCommand(command, config); + public static function addInputMap(inputMap: String): InputMap { + return inputMaps[inputMap] = new InputMap(); } - public function addCustomCommand(command: InputCommand, config: String) { - if (commands[config] == null) commands[config] = new Array(); - commands[config].push(command); - return command; - } -} - -class ActionMap extends InputMap { - - public inline function started(config: String) { - var started = false; - - for (c in commands[config]) { - if (c.started()) { - started = true; - break; + public static function getInputMapKey(inputMap: String, key: String): Null { + if (inputMaps.exists(inputMap)) { + for (i in inputMaps[inputMap].inputs) { + if (i.key == key) { + return i; + } } } - return started; + return null; } - public inline function released(config: String) { - var released = false; + public function addKeyboard(key: String, scale: FastFloat = 1.0): InputMapKey { + return addInput(new KeyboardKey(key, scale)); + } - for (c in commands[config]) { - if (c.released()) { - released = true; - break; - } + public function addMouse(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey { + return addInput(new MouseKey(key, scale, deadzone)); + } + + public function addGamepad(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey { + return addInput(new GamepadKey(key, scale, deadzone)); + } + + public function addInput(input: InputMapKey): InputMapKey { + inputs.push(input); + return input; + } + + public function removeInput(input: InputMapKey): Bool { + return inputs.remove(input); + } + + public function value(): FastFloat { + var v = 0.0; + + for (i in inputs) { + v += i.value(); } - return released; - } -} - -class AxisMap extends InputMap { - var scale: FastFloat = 1.0; - - public inline function getAxis(config: String) { - var axis = 0.0; - - for (c in commands[config]) { - var tempAxis = c.getAxis(); - - if (tempAxis != 0.0 && tempAxis != axis) { - axis += tempAxis; - scale = c.getScale(); - } - } - - return axis; - } - - public inline function getScale() { - return scale; - } -} - -class InputCommand { - var keys = new Array(); - var modifiers = new Array(); - var displacementKeys = new Array(); - var displacementModifiers = new Array(); - var deadzone: FastFloat = 0.0; - var scale: FastFloat = 1.0; - - public function new() {} - - public function setKeys(keys: Array) { - return this.keys = keys; - } - - public function setMods(modifiers: Array) { - return this.modifiers = modifiers; - } - - public function setDisplacementKeys(keys: Array) { - return displacementKeys = keys; - } - - public function setDisplacementMods(modifiers: Array) { - return displacementModifiers = modifiers; - } - - public function setDeadzone(deadzone: FastFloat) { - return this.deadzone = deadzone; - } - - public function setScale(scale: FastFloat) { - return this.scale = scale; - } - - public function getScale() { - return scale; + return v; } public function started() { + for (i in inputs) { + if (i.started()) { + lastKeyPressed = i.key; + return true; + } + } + return false; } public function released() { + for (i in inputs) { + if (i.released()) { + lastKeyPressed = i.key; + return true; + } + } + + return false; + } +} + +class InputMapKey { + + public var key: String; + public var scale: FastFloat; + public var deadzone: FastFloat; + + public function new(key: String, scale = 1.0, deadzone = 0.0) { + this.key = key.toLowerCase(); + this.scale = scale; + this.deadzone = deadzone; + } + + public function started(): Bool { return false; } - public function getAxis(): FastFloat { + public function released(): Bool { + return false; + } + + public function value(): FastFloat { return 0.0; } -} -class KeyboardCommand extends InputCommand { - var keyboard = Input.getKeyboard(); - var mouse = Input.getMouse(); + public function setIndex(index: Int) {} - public inline override function started() { - for (k in keys) { - if (keyboard.started(k)) { - for (m in modifiers) { - if (!keyboard.down(m)) return false; - } + function evalDeadzone(value: FastFloat): FastFloat { + var v = 0.0; - for (m in displacementModifiers) { - if (!mouse.down(m)) return false; - } + if (value > deadzone) { + v = value - deadzone; - return true; - } + } else if (value < -deadzone) { + v = value + deadzone; } - for (k in displacementKeys) { - if (mouse.started(k)) { - for (m in modifiers) { - if (!keyboard.down(m)) return false; - } - - for (m in displacementModifiers) { - if (!mouse.down(m)) return false; - } - - return true; - } - } - - return false; + return v * scale; } - public inline override function released() { - for (k in keys) { - if (keyboard.released(k)) { - for (m in modifiers) { - if (!keyboard.down(m)) return false; - } + function evalPressure(value: FastFloat): FastFloat { + var v = value - deadzone; - for (m in displacementModifiers) { - if (!mouse.down(m)) return false; - } + if (v > 0.0) { + v /= (1.0 - deadzone); - return true; - } + } else { + v = 0.0; } - - for (k in displacementKeys) { - if (mouse.released(k)) { - for (m in modifiers) { - if (!keyboard.down(m)) return false; - } - - for (m in displacementModifiers) { - if (!mouse.down(m)) return false; - } - - return true; - } - } - - return false; - } - - public inline override function getAxis() { - var axis = 0.0; - var movementX = mouse.movementX; - var movementY = mouse.movementY; - var wheelDelta = mouse.wheelDelta; - - for (k in keys) { - if (keyboard.down(k)) { - axis++; - break; - } - } - - for (m in modifiers) { - if (keyboard.down(m)) { - axis --; - break; - } - } - - for (k in displacementKeys) { - switch (k) { - case "moved x": if (movementX > deadzone) axis++; - case "moved y": if (movementY > deadzone) axis--; - case "wheel": if (wheelDelta < -deadzone) axis++; - case "movement x": if (movementX > deadzone) return movementX - deadzone; - case "movement y": if (movementY > deadzone) return movementY - deadzone; - default: { - if (mouse.down(k)) { - axis ++; - break; - } - } - } - } - - for (m in displacementModifiers) { - switch (m) { - case "moved x": if (movementX < -deadzone) axis--; - case "moved y": if (movementY < -deadzone) axis++; - case "wheel": if (wheelDelta > deadzone) axis--; - case "movement x": if (movementX < -deadzone) return movementX + deadzone; - case "movement y": if (movementY < -deadzone) return movementY + deadzone; - default: { - if (mouse.down(m)) { - axis --; - break; - } - } - } - } - - return axis > 1 ? 1 : axis < -1 ? -1 : axis; + + return v; } } -class GamepadCommand extends InputCommand { - var gamepad = Input.getGamepad(0); +class KeyboardKey extends InputMapKey { + + var kb = Input.getKeyboard(); public inline override function started() { - for (k in keys) { - if (gamepad.started(k)) { - for (m in modifiers) { - if (gamepad.down(m) < deadzone) return false; - } - - return true; - } - } - - return false; + return kb.started(key); } public inline override function released() { - for (k in keys) { - if (gamepad.released(k)) { - for (m in modifiers) { - if (gamepad.down(m) < deadzone) return false; - } - - return true; - } - } - - return false; + return kb.released(key); } - public inline override function getAxis() { - var axis = 0.0; - var rsMovementX = gamepad.rightStick.movementX; - var rsMovementY = gamepad.rightStick.movementY; - var lsMovementX = gamepad.leftStick.movementX; - var lsMovementY = gamepad.leftStick.movementY; - var rtPressure = gamepad.down("r2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0; - var ltPressure = gamepad.down("l2") > 0.0 ? (gamepad.down("r2") - deadzone) / (1 - deadzone) : 0.0; - - for (k in keys) { - switch(k) { - case "rtPressure": axis += rtPressure; - case "ltPressure": axis += ltPressure; - default: { - if (gamepad.down(k) > deadzone) { - axis++; - break; - } - } - } - } - - for (m in modifiers) { - switch (m) { - case "rtPressure": axis -= rtPressure; - case "ltPressure": axis -= ltPressure; - default: { - if (gamepad.down(m) > deadzone) { - axis--; - break; - } - } - } - } - - for (k in displacementKeys) { - switch(k) { - case "rs moved x": if (rsMovementX > deadzone) axis++; - case "rs moved y": if (rsMovementY > deadzone) axis++; - case "ls moved x": if (lsMovementX > deadzone) axis++; - case "ls moved y": if (lsMovementY > deadzone) axis++; - case "rs movement x": if (rsMovementX > deadzone) return rsMovementX - deadzone; - case "rs movement y": if (rsMovementY > deadzone) return rsMovementY - deadzone; - case "ls movement x": if (lsMovementX > deadzone) return lsMovementX - deadzone; - case "ls movement y": if (lsMovementY > deadzone) return lsMovementY - deadzone; - } - } - - for (m in displacementModifiers) { - switch (m) { - case "rs moved x": if (rsMovementX < -deadzone) axis--; - case "rs moved y": if (rsMovementY < -deadzone) axis--; - case "ls moved x": if (lsMovementX < -deadzone) axis--; - case "ls moved y": if (lsMovementY < -deadzone) axis--; - case "rs movement x": if (rsMovementX < -deadzone) return rsMovementX + deadzone; - case "rs movement y": if (rsMovementY < -deadzone) return rsMovementY + deadzone; - case "ls movement x": if (lsMovementX < -deadzone) return lsMovementX + deadzone; - case "ls movement y": if (lsMovementY < -deadzone) return lsMovementY + deadzone; - - } - } - - return axis > 1 ? 1 : axis < -1 ? -1 : axis; + public inline override function value(): FastFloat { + return kb.down(key) ? scale : 0.0; } -} \ No newline at end of file +} + +class MouseKey extends InputMapKey { + + var m = Input.getMouse(); + + public inline override function started() { + return m.started(key); + } + + public inline override function released() { + return m.released(key); + } + + public override function value(): FastFloat { + return switch (key) { + case "movement x": evalDeadzone(m.movementX); + case "movement y": evalDeadzone(m.movementY); + case "wheel": evalDeadzone(m.wheelDelta); + default: m.down(key) ? scale : 0.0; + } + } +} + +class GamepadKey extends InputMapKey { + + var g = Input.getGamepad(); + + public inline override function started() { + return g.started(key); + } + + public inline override function released() { + return g.released(key); + } + + public override function value(): FastFloat { + return switch(key) { + case "ls movement x": evalDeadzone(g.leftStick.movementX); + case "ls movement y": evalDeadzone(g.leftStick.movementY); + case "rs movement x": evalDeadzone(g.rightStick.movementX); + case "rs movement y": evalDeadzone(g.rightStick.movementY); + case "lt pressure": evalDeadzone(evalPressure(g.down("l2"))); + case "rt pressure": evalDeadzone(evalPressure(g.down("r2"))); + default: evalDeadzone(g.down(key)); + } + } + + public override function setIndex(index: Int) { + g = Input.getGamepad(index); + } +} diff --git a/blender/arm/logicnode/input/LN_get_input_map_key.py b/blender/arm/logicnode/input/LN_get_input_map_key.py new file mode 100644 index 00000000..12d98935 --- /dev/null +++ b/blender/arm/logicnode/input/LN_get_input_map_key.py @@ -0,0 +1,15 @@ +from arm.logicnode.arm_nodes import * + +class GetInputMapKeyNode(ArmLogicTreeNode): + """Get key data if it exists in the input map.""" + bl_idname = 'LNGetInputMapKeyNode' + bl_label = 'Get Input Map Key' + arm_version = 1 + + def init(self, context): + super(GetInputMapKeyNode, self).init(context) + self.add_input('NodeSocketString', 'Input Map') + self.add_input('NodeSocketString', 'Key') + + self.add_output('NodeSocketFloat', 'Scale', default_value = 1.0) + self.add_output('NodeSocketFloat', 'Deadzone') \ No newline at end of file diff --git a/blender/arm/logicnode/input/LN_on_input_map.py b/blender/arm/logicnode/input/LN_on_input_map.py new file mode 100644 index 00000000..0cc540f5 --- /dev/null +++ b/blender/arm/logicnode/input/LN_on_input_map.py @@ -0,0 +1,16 @@ +from arm.logicnode.arm_nodes import * + +class OnInputMapNode(ArmLogicTreeNode): + """Send a signal if any input map key is started or released.""" + bl_idname = 'LNOnInputMapNode' + bl_label = 'On Input Map' + arm_version = 1 + + def init(self, context): + super(OnInputMapNode, self).init(context) + self.add_input('NodeSocketString', 'Input Map') + + self.add_output('ArmNodeSocketAction', 'Started') + self.add_output('ArmNodeSocketAction', 'Released') + self.add_output('NodeSocketFloat', 'Value') + self.add_output('NodeSocketString', 'Key Pressed') \ No newline at end of file diff --git a/blender/arm/logicnode/input/LN_set_input_map_key.py b/blender/arm/logicnode/input/LN_set_input_map_key.py new file mode 100644 index 00000000..a2157745 --- /dev/null +++ b/blender/arm/logicnode/input/LN_set_input_map_key.py @@ -0,0 +1,28 @@ +from arm.logicnode.arm_nodes import * + +class SetInputMapKeyNode(ArmLogicTreeNode): + """Set input map key.""" + bl_idname = 'LNSetInputMapKeyNode' + bl_label = 'Set Input Map Key' + arm_version = 1 + + property0: EnumProperty( + items = [('keyboard', 'Keyboard', 'Keyboard input'), + ('mouse', 'Mouse', 'Mouse input'), + ('gamepad', 'Gamepad', 'Gamepad input')], + name='', default='keyboard') + + def init(self, context): + super(SetInputMapKeyNode, self).init(context) + self.add_input('ArmNodeSocketAction', 'In') + self.add_input('NodeSocketString', 'Input Map') + self.add_input('NodeSocketString', 'Key') + self.add_input('NodeSocketFloat', 'Scale', default_value=1.0) + self.add_input('NodeSocketFloat', 'Deadzone') + self.add_input('NodeSocketInt', 'Index') + + self.add_output('ArmNodeSocketAction', 'Out') + + + def draw_buttons(self, context, layout): + layout.prop(self, 'property0') \ No newline at end of file From 43a574eb15b40d18dc6abedadfdd4a220ddd207d Mon Sep 17 00:00:00 2001 From: Henrique Date: Wed, 7 Jul 2021 16:51:07 -0300 Subject: [PATCH 71/81] Remove unnecessary read access --- Sources/armory/system/InputMap.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/armory/system/InputMap.hx b/Sources/armory/system/InputMap.hx index 9b631cf9..88b47ec9 100644 --- a/Sources/armory/system/InputMap.hx +++ b/Sources/armory/system/InputMap.hx @@ -5,7 +5,7 @@ import iron.system.Input; class InputMap { - static var inputMaps(default, null) = new Map(); + static var inputMaps = new Map(); public var inputs(default, null) = new Array(); public var lastKeyPressed(default, null) = ""; From daee309ad851c761f52c186fd1caa40c70553af0 Mon Sep 17 00:00:00 2001 From: Henrique Date: Wed, 7 Jul 2021 17:08:01 -0300 Subject: [PATCH 72/81] Add Remove Input Map Key node --- .../armory/logicnode/RemoveInputMapKeyNode.hx | 23 +++++++++++++++++++ .../input/LN_remove_input_map_key.py | 15 ++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 Sources/armory/logicnode/RemoveInputMapKeyNode.hx create mode 100644 blender/arm/logicnode/input/LN_remove_input_map_key.py diff --git a/Sources/armory/logicnode/RemoveInputMapKeyNode.hx b/Sources/armory/logicnode/RemoveInputMapKeyNode.hx new file mode 100644 index 00000000..ddd69518 --- /dev/null +++ b/Sources/armory/logicnode/RemoveInputMapKeyNode.hx @@ -0,0 +1,23 @@ +package armory.logicnode; + +import armory.system.InputMap; + +class RemoveInputMapKeyNode extends LogicNode { + + public function new(tree: LogicTree) { + super(tree); + } + + override function run(from: Int) { + var inputMap = inputs[1].get(); + var key = inputs[2].get(); + + var k = InputMap.getInputMapKey(inputMap, key); + + if (k != null) { + if (InputMap.getInputMap(inputMap).removeInput(k)) { + runOutput(0); + } + } + } +} diff --git a/blender/arm/logicnode/input/LN_remove_input_map_key.py b/blender/arm/logicnode/input/LN_remove_input_map_key.py new file mode 100644 index 00000000..8821ef05 --- /dev/null +++ b/blender/arm/logicnode/input/LN_remove_input_map_key.py @@ -0,0 +1,15 @@ +from arm.logicnode.arm_nodes import * + +class RemoveInputMapKeyNode(ArmLogicTreeNode): + """Remove input map key.""" + bl_idname = 'LNRemoveInputMapKeyNode' + bl_label = 'Remove Input Map Key' + arm_version = 1 + + def init(self, context): + super(RemoveInputMapKeyNode, self).init(context) + self.add_input('ArmNodeSocketAction', 'In') + self.add_input('NodeSocketString', 'Input Map') + self.add_input('NodeSocketString', 'Key') + + self.add_output('ArmNodeSocketAction', 'Out') \ No newline at end of file From 55cf2896a2690993fe13306156289ca2779225aa Mon Sep 17 00:00:00 2001 From: Henrique Date: Wed, 7 Jul 2021 17:11:56 -0300 Subject: [PATCH 73/81] Cleanup --- .../armory/logicnode/RemoveInputMapKeyNode.hx | 2 +- .../armory/logicnode/SetInputMapKeyNode.hx | 1 - Sources/armory/system/InputMap.hx | 26 +++++++++---------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Sources/armory/logicnode/RemoveInputMapKeyNode.hx b/Sources/armory/logicnode/RemoveInputMapKeyNode.hx index ddd69518..9a304ead 100644 --- a/Sources/armory/logicnode/RemoveInputMapKeyNode.hx +++ b/Sources/armory/logicnode/RemoveInputMapKeyNode.hx @@ -15,7 +15,7 @@ class RemoveInputMapKeyNode extends LogicNode { var k = InputMap.getInputMapKey(inputMap, key); if (k != null) { - if (InputMap.getInputMap(inputMap).removeInput(k)) { + if (InputMap.getInputMap(inputMap).removeKey(k)) { runOutput(0); } } diff --git a/Sources/armory/logicnode/SetInputMapKeyNode.hx b/Sources/armory/logicnode/SetInputMapKeyNode.hx index a449575d..e657456d 100644 --- a/Sources/armory/logicnode/SetInputMapKeyNode.hx +++ b/Sources/armory/logicnode/SetInputMapKeyNode.hx @@ -36,7 +36,6 @@ class SetInputMapKeyNode extends LogicNode { } } else { - k.key = key; k.scale = scale; k.deadzone = deadzone; k.setIndex(index); diff --git a/Sources/armory/system/InputMap.hx b/Sources/armory/system/InputMap.hx index 88b47ec9..6df4c721 100644 --- a/Sources/armory/system/InputMap.hx +++ b/Sources/armory/system/InputMap.hx @@ -7,7 +7,7 @@ class InputMap { static var inputMaps = new Map(); - public var inputs(default, null) = new Array(); + public var keys(default, null) = new Array(); public var lastKeyPressed(default, null) = ""; public function new() {} @@ -26,7 +26,7 @@ class InputMap { public static function getInputMapKey(inputMap: String, key: String): Null { if (inputMaps.exists(inputMap)) { - for (i in inputMaps[inputMap].inputs) { + for (i in inputMaps[inputMap].keys) { if (i.key == key) { return i; } @@ -37,30 +37,30 @@ class InputMap { } public function addKeyboard(key: String, scale: FastFloat = 1.0): InputMapKey { - return addInput(new KeyboardKey(key, scale)); + return addKey(new KeyboardKey(key, scale)); } public function addMouse(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey { - return addInput(new MouseKey(key, scale, deadzone)); + return addKey(new MouseKey(key, scale, deadzone)); } public function addGamepad(key: String, scale: FastFloat = 1.0, deadzone: FastFloat = 0.0): InputMapKey { - return addInput(new GamepadKey(key, scale, deadzone)); + return addKey(new GamepadKey(key, scale, deadzone)); } - public function addInput(input: InputMapKey): InputMapKey { - inputs.push(input); - return input; + public function addKey(key: InputMapKey): InputMapKey { + keys.push(key); + return key; } - public function removeInput(input: InputMapKey): Bool { - return inputs.remove(input); + public function removeKey(key: InputMapKey): Bool { + return keys.remove(key); } public function value(): FastFloat { var v = 0.0; - for (i in inputs) { + for (i in keys) { v += i.value(); } @@ -68,7 +68,7 @@ class InputMap { } public function started() { - for (i in inputs) { + for (i in keys) { if (i.started()) { lastKeyPressed = i.key; return true; @@ -79,7 +79,7 @@ class InputMap { } public function released() { - for (i in inputs) { + for (i in keys) { if (i.released()) { lastKeyPressed = i.key; return true; From db5aed465e56a5850ffd93439e8ca5ca5a019c20 Mon Sep 17 00:00:00 2001 From: Henrique Date: Thu, 8 Jul 2021 16:10:45 -0300 Subject: [PATCH 74/81] Add static method to remove keys --- .../armory/logicnode/RemoveInputMapKeyNode.hx | 8 ++--- Sources/armory/system/InputMap.hx | 36 +++++++++++++------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Sources/armory/logicnode/RemoveInputMapKeyNode.hx b/Sources/armory/logicnode/RemoveInputMapKeyNode.hx index 9a304ead..44708c6d 100644 --- a/Sources/armory/logicnode/RemoveInputMapKeyNode.hx +++ b/Sources/armory/logicnode/RemoveInputMapKeyNode.hx @@ -12,12 +12,8 @@ class RemoveInputMapKeyNode extends LogicNode { var inputMap = inputs[1].get(); var key = inputs[2].get(); - var k = InputMap.getInputMapKey(inputMap, key); - - if (k != null) { - if (InputMap.getInputMap(inputMap).removeKey(k)) { - runOutput(0); - } + if (InputMap.removeInputMapKey(inputMap, key)) { + runOutput(0); } } } diff --git a/Sources/armory/system/InputMap.hx b/Sources/armory/system/InputMap.hx index 6df4c721..2bd2b493 100644 --- a/Sources/armory/system/InputMap.hx +++ b/Sources/armory/system/InputMap.hx @@ -26,9 +26,9 @@ class InputMap { public static function getInputMapKey(inputMap: String, key: String): Null { if (inputMaps.exists(inputMap)) { - for (i in inputMaps[inputMap].keys) { - if (i.key == key) { - return i; + for (k in inputMaps[inputMap].keys) { + if (k.key == key) { + return k; } } } @@ -36,6 +36,20 @@ class InputMap { return null; } + public static function removeInputMapKey(inputMap: String, key: String): Bool { + if (inputMaps.exists(inputMap)) { + var i = inputMaps[inputMap]; + + for (k in i.keys) { + if (k.key == key) { + return i.removeKey(k); + } + } + } + + return false; + } + public function addKeyboard(key: String, scale: FastFloat = 1.0): InputMapKey { return addKey(new KeyboardKey(key, scale)); } @@ -60,17 +74,17 @@ class InputMap { public function value(): FastFloat { var v = 0.0; - for (i in keys) { - v += i.value(); + for (k in keys) { + v += k.value(); } return v; } public function started() { - for (i in keys) { - if (i.started()) { - lastKeyPressed = i.key; + for (k in keys) { + if (k.started()) { + lastKeyPressed = k.key; return true; } } @@ -79,9 +93,9 @@ class InputMap { } public function released() { - for (i in keys) { - if (i.released()) { - lastKeyPressed = i.key; + for (k in keys) { + if (k.released()) { + lastKeyPressed = k.key; return true; } } From b452acaebb7841cde267336dddf9b2b51e462315 Mon Sep 17 00:00:00 2001 From: tong Date: Fri, 9 Jul 2021 22:04:50 +0200 Subject: [PATCH 75/81] Replace deprecated Std.is with Std.isOfType --- Sources/armory/logicnode/CompareNode.hx | 4 ++-- Sources/armory/logicnode/GateNode.hx | 4 ++-- Sources/armory/logicnode/GetTraitNameNode.hx | 4 ++-- Sources/armory/logicnode/PauseTraitNode.hx | 2 +- Sources/armory/logicnode/ResumeTraitNode.hx | 2 +- Sources/armory/logicnode/SetParentNode.hx | 2 +- Sources/armory/logicnode/SetTraitPausedNode.hx | 2 +- Sources/armory/trait/FollowCamera.hx | 2 +- Sources/armory/trait/physics/bullet/RigidBody.hx | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/armory/logicnode/CompareNode.hx b/Sources/armory/logicnode/CompareNode.hx index 88dc97be..cfc02823 100644 --- a/Sources/armory/logicnode/CompareNode.hx +++ b/Sources/armory/logicnode/CompareNode.hx @@ -19,9 +19,9 @@ class CompareNode extends LogicNode { switch (property0) { case "Equal": - cond = Std.is(v1, Vec4) ? v1.equals(v2) : v1 == v2; + cond = Std.isOfType(v1, Vec4) ? v1.equals(v2) : v1 == v2; case "Almost Equal": - cond = Std.is(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1; + cond = Std.isOfType(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1; case "Greater": cond = v1 > v2; case "Greater Equal": diff --git a/Sources/armory/logicnode/GateNode.hx b/Sources/armory/logicnode/GateNode.hx index ca310419..ae0014e8 100644 --- a/Sources/armory/logicnode/GateNode.hx +++ b/Sources/armory/logicnode/GateNode.hx @@ -18,9 +18,9 @@ class GateNode extends LogicNode { switch (property0) { case "Equal": - cond = Std.is(v1, Vec4) ? v1.equals(v2) : v1 == v2; + cond = Std.isOfType(v1, Vec4) ? v1.equals(v2) : v1 == v2; case "Almost Equal": - cond = Std.is(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1; + cond = Std.isOfType(v1, Vec4) ? v1.almostEquals(v2, property1) : Math.abs(v1 - v2) < property1; case "Greater": cond = v1 > v2; case "Greater Equal": diff --git a/Sources/armory/logicnode/GetTraitNameNode.hx b/Sources/armory/logicnode/GetTraitNameNode.hx index dd1ded50..ca04f375 100644 --- a/Sources/armory/logicnode/GetTraitNameNode.hx +++ b/Sources/armory/logicnode/GetTraitNameNode.hx @@ -16,12 +16,12 @@ class GetTraitNameNode extends LogicNode { case 0: { // Check CanvasScript var cname = cast Type.resolveClass("armory.trait.internal.CanvasScript"); - if (Std.is(trait, cname)) { + if (Std.isOfType(trait, cname)) { return trait.cnvName; } // Check WasmScript var cname = cast Type.resolveClass("armory.trait.internal.WasmScript"); - if (Std.is(trait, cname)) { + if (Std.isOfType(trait, cname)) { return trait.wasmName; } // Other diff --git a/Sources/armory/logicnode/PauseTraitNode.hx b/Sources/armory/logicnode/PauseTraitNode.hx index a040f5d1..7dc3c084 100644 --- a/Sources/armory/logicnode/PauseTraitNode.hx +++ b/Sources/armory/logicnode/PauseTraitNode.hx @@ -8,7 +8,7 @@ class PauseTraitNode extends LogicNode { override function run(from: Int) { var trait: Dynamic = inputs[1].get(); - if (trait == null || !Std.is(trait, LogicTree)) return; + if (trait == null || !Std.isOfType(trait, LogicTree)) return; cast(trait, LogicTree).pause(); diff --git a/Sources/armory/logicnode/ResumeTraitNode.hx b/Sources/armory/logicnode/ResumeTraitNode.hx index c3c8b26b..c8a62011 100644 --- a/Sources/armory/logicnode/ResumeTraitNode.hx +++ b/Sources/armory/logicnode/ResumeTraitNode.hx @@ -8,7 +8,7 @@ class ResumeTraitNode extends LogicNode { override function run(from: Int) { var trait: Dynamic = inputs[1].get(); - if (trait == null || !Std.is(trait, LogicTree)) return; + if (trait == null || !Std.isOfType(trait, LogicTree)) return; cast(trait, LogicTree).resume(); diff --git a/Sources/armory/logicnode/SetParentNode.hx b/Sources/armory/logicnode/SetParentNode.hx index 8d37720d..81045a1e 100644 --- a/Sources/armory/logicnode/SetParentNode.hx +++ b/Sources/armory/logicnode/SetParentNode.hx @@ -14,7 +14,7 @@ class SetParentNode extends LogicNode { var parent: Object; var isUnparent = false; - if (Std.is(inputs[2].node, ObjectNode)) { + if (Std.isOfType(inputs[2].node, ObjectNode)) { var parentNode = cast(inputs[2].node, ObjectNode); isUnparent = parentNode.objectName == ""; } diff --git a/Sources/armory/logicnode/SetTraitPausedNode.hx b/Sources/armory/logicnode/SetTraitPausedNode.hx index 87099948..53327e1f 100644 --- a/Sources/armory/logicnode/SetTraitPausedNode.hx +++ b/Sources/armory/logicnode/SetTraitPausedNode.hx @@ -10,7 +10,7 @@ class SetTraitPausedNode extends LogicNode { var trait: Dynamic = inputs[1].get(); var paused: Bool = inputs[2].get(); - if (trait == null || !Std.is(trait, LogicTree)) return; + if (trait == null || !Std.isOfType(trait, LogicTree)) return; paused ? cast(trait, LogicTree).pause() : cast(trait, LogicTree).resume(); diff --git a/Sources/armory/trait/FollowCamera.hx b/Sources/armory/trait/FollowCamera.hx index 6cfdf84e..99df3c70 100644 --- a/Sources/armory/trait/FollowCamera.hx +++ b/Sources/armory/trait/FollowCamera.hx @@ -33,7 +33,7 @@ class FollowCamera extends iron.Trait { trace("FollowCamera error, unable to set target object"); } - if (Std.is(object, iron.object.CameraObject)) { + if (Std.isOfType(object, iron.object.CameraObject)) { disabled = true; trace("FollowCamera error, this trait should not be placed directly on a camera objet. It should be placed on another object such as an Empty. The camera should be placed as a child to the Empty object with offset, creating a camera boom."); } diff --git a/Sources/armory/trait/physics/bullet/RigidBody.hx b/Sources/armory/trait/physics/bullet/RigidBody.hx index 5ec1eb83..3c48adc7 100644 --- a/Sources/armory/trait/physics/bullet/RigidBody.hx +++ b/Sources/armory/trait/physics/bullet/RigidBody.hx @@ -149,7 +149,7 @@ class RigidBody extends iron.Trait { if (ready) return; ready = true; - if (!Std.is(object, MeshObject)) return; // No mesh data + if (!Std.isOfType(object, MeshObject)) return; // No mesh data transform = object.transform; physics = armory.trait.physics.PhysicsWorld.active; From 28d21bf35ffc94cb3269e3f159f8b1e03dc8a479 Mon Sep 17 00:00:00 2001 From: Henrique Date: Fri, 9 Jul 2021 21:42:12 -0300 Subject: [PATCH 76/81] Fix nullability --- Sources/armory/trait/internal/DebugConsole.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/armory/trait/internal/DebugConsole.hx b/Sources/armory/trait/internal/DebugConsole.hx index 25bfdc6e..cf7da8cf 100755 --- a/Sources/armory/trait/internal/DebugConsole.hx +++ b/Sources/armory/trait/internal/DebugConsole.hx @@ -594,7 +594,7 @@ class DebugConsole extends Trait { } function drawTiles(tile: ShadowMapTile, atlas: ShadowMapAtlas, atlasVisualSize: Float) { - var color = kha.Color.fromFloats(0.1, 0.1, 0.1); + var color: Null = kha.Color.fromFloats(0.1, 0.1, 0.1); var borderColor = color; var tileScale = (tile.size / atlas.sizew) * atlasVisualSize; //* 0.95; var x = (tile.coordsX / atlas.sizew) * atlasVisualSize; From e0ff256f40ffaf724969432122f787d4319c9abd Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Sat, 10 Jul 2021 19:20:56 +0200 Subject: [PATCH 77/81] make boolean objects nullable for static targets --- Sources/armory/logicnode/SetMaterialImageParamNode.hx | 4 +++- Sources/armory/logicnode/SetMaterialRgbParamNode.hx | 4 +++- Sources/armory/logicnode/SetMaterialValueParamNode.hx | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/armory/logicnode/SetMaterialImageParamNode.hx b/Sources/armory/logicnode/SetMaterialImageParamNode.hx index 6b0c0ed8..c74e4553 100644 --- a/Sources/armory/logicnode/SetMaterialImageParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialImageParamNode.hx @@ -13,10 +13,12 @@ class SetMaterialImageParamNode extends LogicNode { } override function run(from: Int) { + var perObject: Null; + var object = inputs[1].get(); if(object == null) return; - var perObject = inputs[2].get(); + perObject = inputs[2].get(); if(perObject == null) perObject = false; var mat = inputs[3].get(); diff --git a/Sources/armory/logicnode/SetMaterialRgbParamNode.hx b/Sources/armory/logicnode/SetMaterialRgbParamNode.hx index f0be3552..a5289b06 100644 --- a/Sources/armory/logicnode/SetMaterialRgbParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialRgbParamNode.hx @@ -13,10 +13,12 @@ class SetMaterialRgbParamNode extends LogicNode { } override function run(from: Int) { + var perObject: Null; + var object = inputs[1].get(); if(object == null) return; - var perObject = inputs[2].get(); + perObject = inputs[2].get(); if(perObject == null) perObject = false; var mat = inputs[3].get(); diff --git a/Sources/armory/logicnode/SetMaterialValueParamNode.hx b/Sources/armory/logicnode/SetMaterialValueParamNode.hx index 8523bb8c..737365cf 100644 --- a/Sources/armory/logicnode/SetMaterialValueParamNode.hx +++ b/Sources/armory/logicnode/SetMaterialValueParamNode.hx @@ -12,10 +12,12 @@ class SetMaterialValueParamNode extends LogicNode { } override function run(from: Int) { + var perObject: Null; + var object = inputs[1].get(); if(object == null) return; - var perObject = inputs[2].get(); + perObject = inputs[2].get(); if(perObject == null) perObject = false; var mat = inputs[3].get(); From c0333db44fc8d59868417a8744d0fdf9032da7ca Mon Sep 17 00:00:00 2001 From: QuantumCoderQC Date: Sat, 10 Jul 2021 19:22:26 +0200 Subject: [PATCH 78/81] Fix typos. Fix fast float --- .../armory/trait/internal/UniformsManager.hx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/armory/trait/internal/UniformsManager.hx b/Sources/armory/trait/internal/UniformsManager.hx index ed1ba2d5..b33624d9 100644 --- a/Sources/armory/trait/internal/UniformsManager.hx +++ b/Sources/armory/trait/internal/UniformsManager.hx @@ -23,7 +23,7 @@ class UniformsManager extends Trait{ static var sceneRemoveInitalized = false; - public var unifromExists = false; + public var uniformExists = false; public function new(){ super(); @@ -45,7 +45,7 @@ class UniformsManager extends Trait{ var exists = registerShaderUniforms(material); if(exists) { - unifromExists = true; + uniformExists = true; } } } @@ -90,7 +90,7 @@ class UniformsManager extends Trait{ // Register and map shader uniforms if it is an armory shader parameter public static function registerShaderUniforms(material: MaterialData) : Bool { - var unifromExist = false; + var uniformExist = false; if(! floatsMap.exists(Scene.active.root)) floatsMap.set(Scene.active.root, null); if(! vectorsMap.exists(Scene.active.root)) vectorsMap.set(Scene.active.root, null); @@ -98,15 +98,15 @@ class UniformsManager extends Trait{ for(context in material.shader.raw.contexts){ // For each context in shader for (constant in context.constants){ // For each constant in the context - if(constant.is_arm_parameter){ // Chack if armory parameter + if(constant.is_arm_parameter){ // Check if armory parameter - unifromExist = true; + uniformExist = true; var object = Scene.active.root; // Map default uniforms to scene root switch (constant.type){ case "float":{ var link = constant.link; - var value:Float = constant.float; + var value = constant.float; setFloatValue(material, object, link, value); register(Float); } @@ -126,9 +126,9 @@ class UniformsManager extends Trait{ } } for (texture in context.texture_units){ - if(texture.is_arm_parameter){ // Chack if armory parameter + if(texture.is_arm_parameter){ // Check if armory parameter - unifromExist = true; + uniformExist = true; var object = Scene.active.root; // Map default texture to scene root iron.data.Data.getImage(texture.default_image_file, function(image: kha.Image) { @@ -142,7 +142,7 @@ class UniformsManager extends Trait{ } } - return unifromExist; + return uniformExist; } From b3162d8f6ef07dd32adbc860d72e26c8c3c0ae2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Sat, 10 Jul 2021 22:41:19 +0200 Subject: [PATCH 79/81] Fix visual artifacts caused by invalid gbuffer2 on OpenGL --- .../deferred_light/deferred_light.frag.glsl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Shaders/deferred_light/deferred_light.frag.glsl b/Shaders/deferred_light/deferred_light.frag.glsl index fe81fad3..fbe01534 100644 --- a/Shaders/deferred_light/deferred_light.frag.glsl +++ b/Shaders/deferred_light/deferred_light.frag.glsl @@ -21,7 +21,9 @@ uniform sampler2D gbufferD; uniform sampler2D gbuffer0; uniform sampler2D gbuffer1; +#ifdef _gbuffer2 uniform sampler2D gbuffer2; +#endif #ifdef _VoxelAOvar uniform sampler3D voxels; @@ -206,7 +208,9 @@ void main() { vec3 v = normalize(eye - p); float dotNV = max(dot(n, v), 0.0); +#ifdef _gbuffer2 vec4 g2 = textureLod(gbuffer2, texCoord, 0.0); +#endif #ifdef _MicroShadowing occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel @@ -221,14 +225,16 @@ void main() { vec3 envl = shIrradiance(n, shirr); - if (g2.b < 0.5) { - envl = envl; - } else { - envl = vec3(1.0); - } + #ifdef _gbuffer2 + if (g2.b < 0.5) { + envl = envl; + } else { + envl = vec3(1.0); + } + #endif #ifdef _EnvTex - envl /= PI; + envl /= PI; #endif #else vec3 envl = vec3(1.0); From cfa941eab4097e720a52883c6fa83a98289e682b Mon Sep 17 00:00:00 2001 From: tong Date: Sat, 17 Jul 2021 18:49:37 +0200 Subject: [PATCH 80/81] Fix fetch trait prop type --- blender/arm/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blender/arm/utils.py b/blender/arm/utils.py index 7b3af738..f790036c 100755 --- a/blender/arm/utils.py +++ b/blender/arm/utils.py @@ -300,7 +300,7 @@ script_warnings: Dict[str, List[Tuple[str, str]]] = {} # Script name -> List of # See https://regex101.com/r/bbrCzN/8 RX_MODIFIERS = r'(?P(?:public\s+|private\s+|static\s+|inline\s+|final\s+)*)?' # Optional modifiers RX_IDENTIFIER = r'(?P[_$a-z]+[_a-z0-9]*)' # Variable name, follow Haxe rules -RX_TYPE = r'(?::\s+(?P[_a-z]+[\._a-z0-9]*))?' # Optional type annotation +RX_TYPE = r'(?:\s*:\s*(?P[_a-z]+[\._a-z0-9]*))?' # Optional type annotation RX_VALUE = r'(?:\s*=\s*(?P(?:\".*\")|(?:[^;]+)|))?' # Optional default value PROP_REGEX_RAW = fr'@prop\s+{RX_MODIFIERS}(?Pvar|final)\s+{RX_IDENTIFIER}{RX_TYPE}{RX_VALUE};' From 9ff726bac1c8d9f6a3410401592640d4b0ca0899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Br=C3=BCckner?= Date: Wed, 21 Jul 2021 00:01:27 +0200 Subject: [PATCH 81/81] Fix usage of multiple UV maps on mobile render path --- blender/arm/material/make_attrib.py | 37 +++++++++++++++++++++++++++-- blender/arm/material/make_mesh.py | 35 ++------------------------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/blender/arm/material/make_attrib.py b/blender/arm/material/make_attrib.py index 6749d854..bc23a620 100644 --- a/blender/arm/material/make_attrib.py +++ b/blender/arm/material/make_attrib.py @@ -1,9 +1,12 @@ +from typing import Optional + import arm.material.cycles as cycles import arm.material.mat_state as mat_state import arm.material.make_skin as make_skin import arm.material.make_particle as make_particle import arm.material.make_inst as make_inst -import arm.material.shader as shader +import arm.material.make_tess as make_tess +from arm.material.shader import Shader, ShaderContext import arm.utils @@ -33,7 +36,7 @@ def write_vertpos(vert): vert.write('gl_Position = WVP * spos;') -def write_norpos(con_mesh: shader.ShaderContext, vert: shader.Shader, declare=False, write_nor=True): +def write_norpos(con_mesh: ShaderContext, vert: Shader, declare=False, write_nor=True): is_bone = con_mesh.is_elem('bone') if is_bone: make_skin.skin_pos(vert) @@ -45,3 +48,33 @@ def write_norpos(con_mesh: shader.ShaderContext, vert: shader.Shader, declare=Fa vert.write_attrib(prep + 'wnormal = normalize(N * vec3(nor.xy, pos.w));') if con_mesh.is_elem('ipos'): make_inst.inst_pos(con_mesh, vert) + + +def write_tex_coords(con_mesh: ShaderContext, vert: Shader, frag: Shader, tese: Optional[Shader]): + rpdat = arm.utils.get_rp() + + if con_mesh.is_elem('tex'): + vert.add_out('vec2 texCoord') + vert.add_uniform('float texUnpack', link='_texUnpack') + if mat_state.material.arm_tilesheet_flag: + if mat_state.material.arm_particle_flag and rpdat.arm_particles == 'On': + make_particle.write_tilesheet(vert) + else: + vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset') + vert.write_attrib('texCoord = tex * texUnpack + tilesheetOffset;') + else: + vert.write_attrib('texCoord = tex * texUnpack;') + + if tese is not None: + tese.write_pre = True + make_tess.interpolate(tese, 'texCoord', 2, declare_out=frag.contains('texCoord')) + tese.write_pre = False + + if con_mesh.is_elem('tex1'): + vert.add_out('vec2 texCoord1') + vert.add_uniform('float texUnpack', link='_texUnpack') + vert.write_attrib('texCoord1 = tex1 * texUnpack;') + if tese is not None: + tese.write_pre = True + make_tess.interpolate(tese, 'texCoord1', 2, declare_out=frag.contains('texCoord1')) + tese.write_pre = False diff --git a/blender/arm/material/make_mesh.py b/blender/arm/material/make_mesh.py index 275fa0e0..db9a2c28 100644 --- a/blender/arm/material/make_mesh.py +++ b/blender/arm/material/make_mesh.py @@ -132,31 +132,7 @@ def make_base(con_mesh, parse_opacity): if not is_displacement and not vattr_written: make_attrib.write_vertpos(vert) - if con_mesh.is_elem('tex'): - vert.add_out('vec2 texCoord') - vert.add_uniform('float texUnpack', link='_texUnpack') - if mat_state.material.arm_tilesheet_flag: - if mat_state.material.arm_particle_flag and rpdat.arm_particles == 'On': - make_particle.write_tilesheet(vert) - else: - vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset') - vert.write_attrib('texCoord = tex * texUnpack + tilesheetOffset;') - else: - vert.write_attrib('texCoord = tex * texUnpack;') - - if tese is not None: - tese.write_pre = True - make_tess.interpolate(tese, 'texCoord', 2, declare_out=frag.contains('texCoord')) - tese.write_pre = False - - if con_mesh.is_elem('tex1'): - vert.add_out('vec2 texCoord1') - vert.add_uniform('float texUnpack', link='_texUnpack') - vert.write_attrib('texCoord1 = tex1 * texUnpack;') - if tese is not None: - tese.write_pre = True - make_tess.interpolate(tese, 'texCoord1', 2, declare_out=frag.contains('texCoord1')) - tese.write_pre = False + make_attrib.write_tex_coords(con_mesh, vert, frag, tese) if con_mesh.is_elem('col'): vert.add_out('vec3 vcolor') @@ -318,14 +294,7 @@ def make_forward_mobile(con_mesh): opac = mat_state.material.arm_discard_opacity frag.write('if (opacity < {0}) discard;'.format(opac)) - if con_mesh.is_elem('tex'): - vert.add_out('vec2 texCoord') - vert.add_uniform('float texUnpack', link='_texUnpack') - if mat_state.material.arm_tilesheet_flag: - vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset') - vert.write('texCoord = tex * texUnpack + tilesheetOffset;') - else: - vert.write('texCoord = tex * texUnpack;') + make_attrib.write_tex_coords(con_mesh, vert, frag, tese) if con_mesh.is_elem('col'): vert.add_out('vec3 vcolor')