Live patch: add support for node property updates

This commit is contained in:
Moritz Brückner 2021-07-03 19:45:05 +02:00
parent 202138304a
commit 4387d774cc
73 changed files with 565 additions and 238 deletions

View file

@ -9,7 +9,7 @@ import armory.trait.physics.RigidBody;
class AddRigidBodyNode extends LogicNode {
public var property0: String;//Shape
public var property1: String;//Advanced
public var property1: Bool;//Advanced
public var object: Object;
public function new(tree: LogicTree) {
@ -38,8 +38,7 @@ class AddRigidBodyNode extends LogicNode {
var shape: Shape = 1;
if(property1 == 'true')
{
if (property1) {
margin = inputs[9].get();
marginLen = inputs[10].get();
linDamp = inputs[11].get();
@ -49,34 +48,29 @@ class AddRigidBodyNode extends LogicNode {
angVelThreshold = inputs[15].get();
group = inputs[16].get();
mask = inputs[17].get();
}
if (object == null) return;
#if arm_physics
var rb: RigidBody = object.getTrait(RigidBody);
if((group < 0) || (group > 32)) group = 1; //Limiting max groups to 32
if((mask < 0) || (mask > 32)) mask = 1; //Limiting max masks to 32
if(rb == null)
{
if ((group < 0) || (group > 32)) group = 1; //Limiting max groups to 32
if ((mask < 0) || (mask > 32)) mask = 1; //Limiting max masks to 32
if (rb == null) {
switch (property0){
case 'Box':
case "Box":
shape = Box;
case 'Sphere':
case "Sphere":
shape = Sphere;
case 'Capsule':
case "Capsule":
shape = Capsule;
case 'Cone':
case "Cone":
shape = Cone;
case 'Cylinder':
case "Cylinder":
shape = Cylinder;
case 'Convex Hull':
case "Convex Hull":
shape = ConvexHull;
case 'Mesh':
case "Mesh":
shape = Mesh;
}
@ -84,15 +78,14 @@ class AddRigidBodyNode extends LogicNode {
rb.animated = animated;
rb.staticObj = ! active;
rb.isTriggerObject(trigger);
if(property1 == 'true')
{
if (property1) {
rb.linearDamping = linDamp;
rb.angularDamping = angDamp;
if(margin) rb.collisionMargin = marginLen;
if(useDeactiv) {
if (margin) rb.collisionMargin = marginLen;
if (useDeactiv) {
rb.setUpDeactivation(true, linearVelThreshold, angVelThreshold, 0.0);
}
}
object.addTrait(rb);

View file

@ -3,7 +3,7 @@ package armory.logicnode;
class MathNode extends LogicNode {
public var property0: String; // Operation
public var property1: String; // Clamp
public var property1: Bool; // Clamp
public function new(tree: LogicTree) {
super(tree);
@ -80,8 +80,8 @@ class MathNode extends LogicNode {
}
}
// Clamp
if (property1 == "true") r = r < 0.0 ? 0.0 : (r > 1.0 ? 1.0 : r);
if (property1) r = r < 0.0 ? 0.0 : (r > 1.0 ? 1.0 : r);
return r;
}
}
}

View file

@ -6,7 +6,7 @@ class MixNode extends LogicNode {
public var property0: String; // Type
public var property1: String; // Ease
public var property2: String; // Clamp
public var property2: Bool; // Clamp
var ease: Float->Float = null;
@ -50,7 +50,9 @@ class MixNode extends LogicNode {
var v2: Float = inputs[2].get();
var f = v1 + (v2 - v1) * ease(k);
if (property2 == "true") f = f < 0 ? 0 : f > 1 ? 1 : f;
// Clamp
if (property2) f = f < 0 ? 0 : f > 1 ? 1 : f;
return f;
}
}

View file

@ -3,15 +3,14 @@ package armory.logicnode;
#if arm_physics
import armory.trait.physics.bullet.PhysicsConstraint.ConstraintAxis;
#end
import iron.object.Object;
class PhysicsConstraintNode extends LogicNode {
public var property0: String;//Linear or Angular
public var property1: String;//Axis
public var property2: String;//Is a spring
public var value1: Float;//Lower limit or Spring Stiffness
public var value2: Float;//Upper limit or Spring Damping
public var property0: String; //Linear or Angular
public var property1: String; //Axis
public var property2: Bool; //Is a spring
public var value1: Float; //Lower limit or Spring Stiffness
public var value2: Float; //Upper limit or Spring Damping
public var isAngular: Bool;
public var axis: ConstraintAxis;
public var isSpring: Bool;
@ -24,27 +23,13 @@ class PhysicsConstraintNode extends LogicNode {
value1 = inputs[0].get();
value2 = inputs[1].get();
if(property0 == 'Linear') {
isAngular = false;
}
else{
isAngular = true;
}
if(property2 == 'true'){
isSpring = true;
}
else {
isSpring = false;
}
isAngular = property0 != "Linear";
isSpring = property2;
switch (property1){
case 'X':
axis = X;
case 'Y':
axis = Y;
case 'Z':
axis = Z;
case "X": axis = X;
case "Y": axis = Y;
case "Z": axis = Z;
}
return this;

View file

@ -7,7 +7,7 @@ class VectorMixNode extends LogicNode {
public var property0: String; // Type
public var property1: String; // Ease
public var property2: String; // Clamp
public var property2: Bool; // Clamp
var v = new Vec4();
@ -57,7 +57,7 @@ class VectorMixNode extends LogicNode {
v.y = v1.y + (v2.y - v1.y) * f;
v.z = v1.z + (v2.z - v1.z) * f;
if (property2 == "true") v.clamp(0, 1);
if (property2) v.clamp(0, 1);
return v;
}
}

View file

@ -45,5 +45,15 @@ class LivePatch extends iron.Trait {
}
toNode.inputs[toIndex] = new LogicNodeInput(fromNode, fromIndex);
}
public static function patchUpdateNodeProp(treeName: String, nodeName: String, propName: String, value: Dynamic) {
var tree = LogicTree.nodeTrees[treeName];
if (tree == null) return;
var node = tree.nodes[nodeName];
if (node == null) return;
Reflect.setField(node, propName, value);
}
#end
}

View file

@ -153,8 +153,7 @@ def send_event(event_id: str, opt_data: Any = None):
# This event is called twice for a connection but we only need
# send it once
if node == link.from_node:
node_tree = node.get_tree()
tree_name = arm.node_utils.get_export_tree_name(node_tree)
tree_name = arm.node_utils.get_export_tree_name(node.get_tree())
# [1:] is used here because make_logic already uses that for
# node names if arm_debug is used
@ -167,6 +166,18 @@ def send_event(event_id: str, opt_data: Any = None):
js = f'LivePatch.patchCreateNodeLink("{tree_name}", "{from_node_name}", "{to_node_name}", "{from_index}", "{to_index}");'
write_patch(js)
if event_id == 'ln_update_prop':
node: ArmLogicTreeNode
prop_name: str
node, prop_name = opt_data
tree_name = arm.node_utils.get_export_tree_name(node.get_tree())
node_name = arm.node_utils.get_export_node_name(node)[1:]
value = arm.node_utils.haxe_format_prop(node, prop_name)
js = f'LivePatch.patchUpdateNodeProp("{tree_name}", "{node_name}", "{prop_name}", {value});'
write_patch(js)
def on_operator(operator_id: str):

View file

@ -1,6 +1,6 @@
import itertools
from collections import OrderedDict
from typing import Any, Generator, List, Optional, Type, Dict
from typing import Any, Generator, List, Optional, Type
from typing import OrderedDict as ODict # Prevent naming conflicts
import bpy.types
@ -8,7 +8,9 @@ from bpy.props import *
from nodeitems_utils import NodeItem
import arm # we cannot import arm.livepatch here or we have a circular import
# Pass NodeReplacement forward to individual node modules that import arm_nodes
# Pass custom property types and NodeReplacement forward to individual
# node modules that import arm_nodes
from arm.logicnode.arm_props import *
from arm.logicnode.replacement import NodeReplacement
import arm.node_utils
@ -52,6 +54,13 @@ class ArmLogicTreeNode(bpy.types.Node):
def get_tree(self):
return self.id_data
def on_prop_update(self, context: bpy.types.Context, prop_name: str):
"""Called if a property created with a function from the
arm_props module is changed. If the property has a custom update
function, it is called before `on_prop_update()`.
"""
arm.live_patch.send_event('ln_update_prop', (self, prop_name))
def insert_link(self, link: bpy.types.NodeLink):
"""Called on *both* nodes when a link between two nodes is created."""
arm.live_patch.send_event('ln_insert_link', (self, link))

View file

@ -0,0 +1,267 @@
"""Custom bpy property creators for logic nodes. Please be aware that
the code in this file is usually run once at registration and not for
each individual node instance when it is created.
The functions for creating typed properties wrap the private __haxe_prop
function to allow for IDE autocompletion.
Some default parameters in the signature of functions in this module are
mutable (common Python pitfall, be aware of this!), but because they
don't get accessed later it doesn't matter here and we keep it this way
for parity with the Blender API.
"""
from typing import Any, Callable, Sequence, Union
import bpy
from bpy.props import *
# Property parameter name `set` shadows built-in type `set`
__set = set
def __haxe_prop(prop_type: Callable, prop_name: str, *args, **kwargs) -> Any:
"""Declares a logic node property as a property that will be
used ingame for a logic node."""
update_callback: Callable = kwargs.get('update', None)
if update_callback is None:
def wrapper(self: bpy.types.Node, context: bpy.types.Context):
self.on_prop_update(context, prop_name)
kwargs['update'] = wrapper
else:
def wrapper(self: bpy.types.Node, context: bpy.types.Context):
update_callback(self, context)
self.on_prop_update(context, prop_name)
kwargs['update'] = wrapper
# Tags are not allowed on classes other than bpy.types.ID or
# bpy.types.Bone, remove them here to prevent registration errors
if 'tags' in kwargs:
del kwargs['tags']
return prop_type(*args, **kwargs)
def HaxeBoolProperty(
prop_name: str,
*, # force passing further arguments as keywords, see PEP 3102
name: str = "",
description: str = "",
default=False,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
subtype: str = 'NONE',
update=None,
get=None,
set=None
) -> 'bpy.types.BoolProperty':
"""Declares a new BoolProperty that has a counterpart with the given
prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(BoolProperty, **locals())
def HaxeBoolVectorProperty(
prop_name: str,
*,
name: str = "",
description: str = "",
default: list = (False, False, False),
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
subtype: str = 'NONE',
size: int = 3,
update=None,
get=None,
set=None
) -> list['bpy.types.BoolProperty']:
"""Declares a new BoolVectorProperty that has a counterpart with the
given prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(BoolVectorProperty, **locals())
def HaxeCollectionProperty(
prop_name: str,
*,
type=None,
name: str = "",
description: str = "",
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set()
) -> 'bpy.types.CollectionProperty':
"""Declares a new CollectionProperty that has a counterpart with the
given prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(CollectionProperty, **locals())
def HaxeEnumProperty(
prop_name: str,
*,
items: Sequence,
name: str = "",
description: str = "",
default: Union[str, set[str]] = None,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
update=None,
get=None,
set=None
) -> 'bpy.types.EnumProperty':
"""Declares a new EnumProperty that has a counterpart with the given
prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(EnumProperty, **locals())
def HaxeFloatProperty(
prop_name: str,
*,
name: str = "",
description: str = "",
default=0.0,
min: float = -3.402823e+38,
max: float = 3.402823e+38,
soft_min: float = -3.402823e+38,
soft_max: float = 3.402823e+38,
step: int = 3,
precision: int = 2,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
subtype: str = 'NONE',
unit: str = 'NONE',
update=None,
get=None,
set=None
) -> 'bpy.types.FloatProperty':
"""Declares a new FloatProperty that has a counterpart with the
given prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(FloatProperty, **locals())
def HaxeFloatVectorProperty(
prop_name: str,
*,
name: str = "",
description: str = "",
default: list = (0.0, 0.0, 0.0),
min: float = 'sys.float_info.min',
max: float = 'sys.float_info.max',
soft_min: float = 'sys.float_info.min',
soft_max: float = 'sys.float_info.max',
step: int = 3,
precision: int = 2,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
subtype: str = 'NONE',
unit: str = 'NONE',
size: int = 3,
update=None,
get=None,
set=None
) -> list['bpy.types.FloatProperty']:
"""Declares a new FloatVectorProperty that has a counterpart with the
given prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(FloatVectorProperty, **locals())
def HaxeIntProperty(
prop_name: str,
*,
name: str = "",
description: str = "",
default=0,
min: int = -2**31,
max: int = 2**31 - 1,
soft_min: int = -2**31,
soft_max: int = 2**31 - 1,
step: int = 1,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
subtype: str = 'NONE',
update=None,
get=None,
set=None
) -> 'bpy.types.IntProperty':
"""Declares a new IntProperty that has a counterpart with the given
prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(IntProperty, **locals())
def HaxeIntVectorProperty(
prop_name: str,
*,
name: str = "",
description: str = "",
default: list = (0, 0, 0),
min: int = -2**31,
max: int = 2**31 - 1,
soft_min: int = -2**31,
soft_max: int = 2**31 - 1,
step: int = 1,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
subtype: str = 'NONE',
size: int = 3,
update=None,
get=None,
set=None
) -> list['bpy.types.IntProperty']:
"""Declares a new IntVectorProperty that has a counterpart with the given
prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(IntVectorProperty, **locals())
def HaxePointerProperty(
prop_name: str,
*,
type=None,
name: str = "",
description: str = "",
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
poll=None,
update=None
) -> 'bpy.types.PointerProperty':
"""Declares a new PointerProperty that has a counterpart with the
given prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(PointerProperty, **locals())
def RemoveHaxeProperty(cls, attr: str):
RemoveProperty(cls, attr)
def HaxeStringProperty(
prop_name: str,
*,
name: str = "",
description: str = "",
default: str = "",
maxlen: int = 0,
options: set = {'ANIMATABLE'},
override: set = set(),
tags: set = set(),
subtype: str = 'NONE',
update=None,
get=None,
set=None
) -> 'bpy.types.StringProperty':
"""Declares a new StringProperty that has a counterpart with the
given prop_name (Python and Haxe names must be identical for now).
"""
return __haxe_prop(StringProperty, **locals())

View file

@ -6,16 +6,19 @@ class OnCanvasElementNode(ArmLogicTreeNode):
bl_label = 'On Canvas Element'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items=[('click', 'Click', 'Listen to mouse clicks'),
('hover', 'Hover', 'Listen to mouse hover')],
name='Listen to', default='click')
property1: EnumProperty(
property1: HaxeEnumProperty(
'property1',
items=[('started', 'Started', 'Started'),
('down', 'Down', 'Down'),
('released', 'Released', 'Released')],
name='Status', default='started')
property2: EnumProperty(
property2: HaxeEnumProperty(
'property2',
items=[('left', 'Left', 'Left mouse button'),
('middle', 'Middle', 'Middle mouse button'),
('right', 'Right', 'Right mouse button')],

View file

@ -11,7 +11,8 @@ class OnGamepadNode(ArmLogicTreeNode):
arm_section = 'gamepad'
arm_version = 2
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Down', 'Down', 'Down'),
('Started', 'Started', 'Started'),
('Released', 'Released', 'Released')],
@ -19,7 +20,8 @@ class OnGamepadNode(ArmLogicTreeNode):
# ('Moved Right', 'Moved Right', 'Moved Right'),],
name='', default='Started')
property1: EnumProperty(
property1: HaxeEnumProperty(
'property1',
items = [('cross', 'cross / a', 'cross / a'),
('circle', 'circle / b', 'circle / b'),
('square', 'square / x', 'square / x'),

View file

@ -11,13 +11,15 @@ class OnKeyboardNode(ArmLogicTreeNode):
arm_section = 'keyboard'
arm_version = 2
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Down', 'Down', 'Down'),
('Started', 'Started', 'Started'),
('Released', 'Released', 'Released')],
name='', default='Started')
property1: EnumProperty(
property1: HaxeEnumProperty(
'property1',
items = [('a', 'a', 'a'),
('b', 'b', 'b'),
('c', 'c', 'c'),

View file

@ -11,13 +11,15 @@ class OnMouseNode(ArmLogicTreeNode):
arm_section = 'mouse'
arm_version = 2
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Down', 'Down', 'Down'),
('Started', 'Started', 'Started'),
('Released', 'Released', 'Released'),
('Moved', 'Moved', 'Moved')],
name='', default='Down')
property1: EnumProperty(
property1: HaxeEnumProperty(
'property1',
items = [('left', 'left', 'left'),
('right', 'right', 'right'),
('middle', 'middle', 'middle')],

View file

@ -11,7 +11,8 @@ class OnSurfaceNode(ArmLogicTreeNode):
arm_section = 'surface'
arm_version = 2
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Touched', 'Touched', 'Touched'),
('Started', 'Started', 'Started'),
('Released', 'Released', 'Released'),

View file

@ -11,12 +11,13 @@ class OnVirtualButtonNode(ArmLogicTreeNode):
arm_section = 'virtual'
arm_version = 2
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Down', 'Down', 'Down'),
('Started', 'Started', 'Started'),
('Released', 'Released', 'Released')],
name='', default='Started')
property1: StringProperty(name='', default='button')
property1: HaxeStringProperty('property1', name='', default='button')
def init(self, context):
super(OnVirtualButtonNode, self).init(context)

View file

@ -10,7 +10,7 @@ class OnEventNode(ArmLogicTreeNode):
arm_version = 1
arm_section = 'custom'
property0: StringProperty(name='', default='')
property0: HaxeStringProperty('property0', name='', default='')
def init(self, context):
super(OnEventNode, self).init(context)

View file

@ -10,7 +10,8 @@ class OnUpdateNode(ArmLogicTreeNode):
bl_idname = 'LNOnUpdateNode'
bl_label = 'On Update'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Update', 'Update', 'Update'),
('Late Update', 'Late Update', 'Late Update'),
('Physics Pre-Update', 'Physics Pre-Update', 'Physics Pre-Update')],

View file

@ -15,7 +15,8 @@ class GamepadNode(ArmLogicTreeNode):
arm_version = 1
arm_section = 'gamepad'
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The gamepad button starts to be pressed'),
('down', 'Down', 'The gamepad button is pressed'),
('released', 'Released', 'The gamepad button stops being pressed')],
@ -23,7 +24,8 @@ class GamepadNode(ArmLogicTreeNode):
# ('Moved Right', 'Moved Right', 'Moved Right'),],
name='', default='down')
property1: EnumProperty(
property1: HaxeEnumProperty(
'property1',
items = [('cross', 'cross / a', 'cross / a'),
('circle', 'circle / b', 'circle / b'),
('square', 'square / x', 'square / x'),

View file

@ -7,13 +7,15 @@ class KeyboardNode(ArmLogicTreeNode):
arm_section = 'keyboard'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The keyboard button starts to be pressed'),
('down', 'Down', 'The keyboard button is pressed'),
('released', 'Released', 'The keyboard button stops being pressed')],
name='', default='down')
property1: EnumProperty(
property1: HaxeEnumProperty(
'property1',
items = [('a', 'a', 'a'),
('b', 'b', 'b'),
('c', 'c', 'c'),

View file

@ -7,13 +7,15 @@ class MouseNode(ArmLogicTreeNode):
arm_section = 'mouse'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The mouse button startes to be pressed'),
('down', 'Down', 'The mouse button is pressed'),
('released', 'Released', 'The mouse button stops being pressed'),
('moved', 'Moved', 'Moved')],
name='', default='down')
property1: EnumProperty(
property1: HaxeEnumProperty(
'property1',
items = [('left', 'Left', 'Left mouse button'),
('middle', 'Middle', 'Middle mouse button'),
('right', 'Right', 'Right mouse button')],

View file

@ -14,7 +14,8 @@ class SetCursorStateNode(ArmLogicTreeNode):
arm_section = 'mouse'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('hide locked', 'Hide Locked', 'The mouse cursor is hidden and locked'),
('hide', 'Hide', 'The mouse cursor is hidden'),
('lock', 'Lock', 'The mouse cursor is locked'),

View file

@ -7,7 +7,8 @@ class SurfaceNode(ArmLogicTreeNode):
arm_section = 'surface'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The screen surface starts to be touched'),
('down', 'Down', 'The screen surface is touched'),
('released', 'Released', 'The screen surface stops being touched'),

View file

@ -7,12 +7,13 @@ class VirtualButtonNode(ArmLogicTreeNode):
arm_section = 'virtual'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('started', 'Started', 'The virtual button starts to be pressed'),
('down', 'Down', 'The virtual button is pressed'),
('released', 'Released', 'The virtual button stops being pressed')],
name='', default='down')
property1: StringProperty(name='', default='button')
property1: HaxeStringProperty('property1', name='', default='button')
def init(self, context):
super(VirtualButtonNode, self).init(context)

View file

@ -20,7 +20,8 @@ class GateNode(ArmLogicTreeNode):
arm_version = 1
min_inputs = 3
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Equal', 'Equal', 'Equal'),
('Almost Equal', 'Almost Equal', 'Almost Equal'),
('Greater', 'Greater', 'Greater'),
@ -31,7 +32,7 @@ class GateNode(ArmLogicTreeNode):
('And', 'And', 'And')],
name='', default='Equal',
update=remove_extra_inputs)
property1: FloatProperty(name='Tolerance', description='Precision for float compare', default=0.0001)
property1: HaxeFloatProperty('property1', name='Tolerance', description='Precision for float compare', default=0.0001)
def __init__(self):
super(GateNode, self).__init__()

View file

@ -18,7 +18,7 @@ class MaterialNode(ArmLogicTreeNode):
return self.property0.name
return arm.utils.asset_name(bpy.data.materials[self.property0.name])
property0: PointerProperty(name='', type=bpy.types.Material)
property0: HaxePointerProperty('property0', name='', type=bpy.types.Material)
def init(self, context):
super(MaterialNode, self).init(context)

View file

@ -10,7 +10,8 @@ class CompareNode(ArmLogicTreeNode):
bl_idname = 'LNCompareNode'
bl_label = 'Compare'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Equal', 'Equal', 'Equal'),
('Almost Equal', 'Almost Equal', 'Almost Equal'),
('Greater', 'Greater', 'Greater'),
@ -22,7 +23,7 @@ class CompareNode(ArmLogicTreeNode):
name='', default='Equal',
update=remove_extra_inputs)
min_inputs = 2
property1: FloatProperty(name='Tolerance', description='Precision for float compare', default=0.0001)
property1: HaxeFloatProperty('property1', name='Tolerance', description='Precision for float compare', default=0.0001)
def __init__(self):
super(CompareNode, self).__init__()

View file

@ -64,7 +64,8 @@ class MathNode(ArmLogicTreeNode):
self.inputs.remove(self.inputs.values()[-1])
self['property0'] = value
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Add', 'Add', 'Add'),
('Multiply', 'Multiply', 'Multiply'),
('Sine', 'Sine', 'Sine'),
@ -92,11 +93,7 @@ class MathNode(ArmLogicTreeNode):
('Exponent', 'Exponent', 'Exponent')],
name='', default='Add', set=set_enum, get=get_enum)
@property
def property1(self):
return 'true' if self.property1_ else 'false'
property1_: BoolProperty(name='Clamp', default=False)
property1: HaxeBoolProperty('property1', name='Clamp', default=False)
def __init__(self):
array_nodes[str(id(self))] = self
@ -109,7 +106,7 @@ class MathNode(ArmLogicTreeNode):
self.add_output('NodeSocketFloat', 'Result')
def draw_buttons(self, context, layout):
layout.prop(self, 'property1_')
layout.prop(self, 'property1')
layout.prop(self, 'property0')
# Many arguments: Add, Subtract, Multiply, Divide
if (self.get_count_in(self.property0) == 0):

View file

@ -155,8 +155,8 @@ class MathExpressionNode(ArmLogicTreeNode):
def get_exp(self):
return self.get('property0', 'a + b')
property0: StringProperty(name='', description='Expression (operation: +, -, *, /, ^, (, ), %)', set=set_exp, get=get_exp)
property1: BoolProperty(name='Clamp', default=False)
property0: HaxeStringProperty('property0', name='', description='Expression (operation: +, -, *, /, ^, (, ), %)', set=set_exp, get=get_exp)
property1: HaxeBoolProperty('property1', name='Clamp', default=False)
def __init__(self):
array_nodes[str(id(self))] = self

View file

@ -7,7 +7,8 @@ class MatrixMathNode(ArmLogicTreeNode):
arm_section = 'matrix'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Multiply', 'Multiply', 'Multiply')],
name='', default='Multiply')

View file

@ -5,7 +5,8 @@ class MixNode(ArmLogicTreeNode):
bl_idname = 'LNMixNode'
bl_label = 'Mix'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Linear', 'Linear', 'Linear'),
('Sine', 'Sine', 'Sine'),
('Quad', 'Quad', 'Quad'),
@ -19,18 +20,15 @@ class MixNode(ArmLogicTreeNode):
('Elastic', 'Elastic', 'Elastic'),
],
name='', default='Linear')
property1: EnumProperty(
property1: HaxeEnumProperty(
'property1',
items = [('In', 'In', 'In'),
('Out', 'Out', 'Out'),
('InOut', 'InOut', 'InOut'),
],
name='', default='Out')
@property
def property2(self):
return 'true' if self.property2_ else 'false'
property2_: BoolProperty(name='Clamp', default=False)
property2: HaxeBoolProperty('property2', name='Clamp', default=False)
def init(self, context):
super(MixNode, self).init(context)
@ -41,6 +39,6 @@ class MixNode(ArmLogicTreeNode):
self.add_output('NodeSocketFloat', 'Result')
def draw_buttons(self, context, layout):
layout.prop(self, 'property2_')
layout.prop(self, 'property2')
layout.prop(self, 'property0')
layout.prop(self, 'property1')

View file

@ -7,7 +7,8 @@ class VectorMixNode(ArmLogicTreeNode):
arm_section = 'vector'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Linear', 'Linear', 'Linear'),
('Sine', 'Sine', 'Sine'),
('Quad', 'Quad', 'Quad'),
@ -21,18 +22,15 @@ class VectorMixNode(ArmLogicTreeNode):
('Elastic', 'Elastic', 'Elastic'),
],
name='', default='Linear')
property1: EnumProperty(
property1: HaxeEnumProperty(
'property1',
items = [('In', 'In', 'In'),
('Out', 'Out', 'Out'),
('InOut', 'InOut', 'InOut'),
],
name='', default='Out')
@property
def property2(self):
return 'true' if self.property2_ else 'false'
property2_: BoolProperty(name='Clamp', default=False)
property2: HaxeBoolProperty('property2', name='Clamp', default=False)
def init(self, context):
super(VectorMixNode, self).init(context)
@ -43,7 +41,7 @@ class VectorMixNode(ArmLogicTreeNode):
self.add_output('NodeSocketVector', 'Result')
def draw_buttons(self, context, layout):
layout.prop(self, 'property2_')
layout.prop(self, 'property2')
layout.prop(self, 'property0')
if self.property0 != 'Linear':
layout.prop(self, 'property1')

View file

@ -41,7 +41,7 @@ class QuaternionMathNode(ArmLogicTreeNode):
if (self.property0 == 'ToAxisAngle'):
self.add_output('NodeSocketFloat', 'To Axis Angle') # ToAxisAngle
property1: BoolProperty(name='Separator Out', default=False, set=set_bool, get=get_bool)
property1: HaxeBoolProperty('property1', name='Separator Out', default=False, set=set_bool, get=get_bool)
@staticmethod
def get_enum_id_value(obj, prop_name, value):
@ -132,7 +132,8 @@ class QuaternionMathNode(ArmLogicTreeNode):
self.add_output('NodeSocketFloat', 'Module')
self['property0'] = value
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Add', 'Add', 'Add'),
('Subtract', 'Subtract', 'Subtract'),
('DotProduct', 'Dot Product', 'Dot Product'),

View file

@ -10,12 +10,7 @@ class ScreenToWorldSpaceNode(ArmLogicTreeNode):
min_outputs = 2
max_outputs = 8
# Separator
@property
def property0(self):
return True if self.property0_ else False
property0_: BoolProperty(name='Separator Out', default=False)
property0: HaxeBoolProperty('property0', name='Separator Out', default=False)
def init(self, context):
super(ScreenToWorldSpaceNode, self).init(context)
@ -26,8 +21,8 @@ class ScreenToWorldSpaceNode(ArmLogicTreeNode):
self.add_output('NodeSocketVector', 'Direction')
def draw_buttons(self, context, layout):
layout.prop(self, 'property0_') # Separator Out
if self.property0_:
layout.prop(self, 'property0') # Separator Out
if self.property0:
if len(self.outputs) < self.max_outputs:
self.outputs.remove(self.outputs.values()[-1]) # Direction vector
self.add_output('NodeSocketFloat', 'X') # World X

View file

@ -42,7 +42,7 @@ class VectorMathNode(ArmLogicTreeNode):
if (self.property0 == 'Dot Product'):
self.add_output('NodeSocketFloat', 'Scalar') # Scalar
property1: BoolProperty(name='Separator Out', default=False, set=set_bool, get=get_bool)
property1: HaxeBoolProperty('property1', name='Separator Out', default=False, set=set_bool, get=get_bool)
@staticmethod
def get_enum_id_value(obj, prop_name, value):
@ -105,7 +105,8 @@ class VectorMathNode(ArmLogicTreeNode):
self.add_output('NodeSocketFloat', 'Length')
self['property0'] = value
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Add', 'Add', 'Add'),
('Dot Product', 'Dot Product', 'Dot Product'),
('Multiply', 'Multiply', 'Multiply'),

View file

@ -15,7 +15,7 @@ class CallGroupNode(ArmLogicTreeNode):
def property0(self):
return arm.utils.safesrc(bpy.data.worlds['Arm'].arm_project_package) + '.node.' + arm.utils.safesrc(self.property0_.name)
property0_: PointerProperty(name='Group', type=bpy.types.NodeTree)
property0_: HaxePointerProperty('property0', name='Group', type=bpy.types.NodeTree)
def init(self, context):
super(CallGroupNode, self).init(context)

View file

@ -6,7 +6,8 @@ class SetDebugConsoleSettings(ArmLogicTreeNode):
bl_label = 'Set Debug Console Settings'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('left', 'Anchor Left', 'Anchor debug console in the top left'),
('center', 'Anchor Center', 'Anchor debug console in the top center'),
('right', 'Anchor Right', 'Anchor the debug console in the top right')],

View file

@ -9,7 +9,7 @@ class ExpressionNode(ArmLogicTreeNode):
arm_version = 1
arm_section = 'haxe'
property0: StringProperty(name='', default='')
property0: HaxeStringProperty('property0', name='', default='')
def init(self, context):
super(ExpressionNode, self).init(context)

View file

@ -15,7 +15,7 @@ class ScriptNode(ArmLogicTreeNode):
return bpy.data.texts[self.property0_].as_string() if self.property0_ in bpy.data.texts else ''
property0_: StringProperty(name='Text', default='')
property0_: HaxeStringProperty('property0', name='Text', default='')
def init(self, context):
super(ScriptNode, self).init(context)

View file

@ -7,7 +7,8 @@ class GetChildNode(ArmLogicTreeNode):
arm_section = 'relations'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('By Name', 'By Name', 'By Name'),
('Contains', 'Contains', 'Contains'),
('Starts With', 'Starts With', 'Starts With'),

View file

@ -9,7 +9,7 @@ class MeshNode(ArmLogicTreeNode):
bl_label = 'Mesh'
arm_version = 1
property0_get: PointerProperty(name='', type=bpy.types.Mesh)
property0_get: HaxePointerProperty('property0_get', name='', type=bpy.types.Mesh)
def init(self, context):
super(MeshNode, self).init(context)

View file

@ -9,7 +9,8 @@ class SetVisibleNode(ArmLogicTreeNode):
arm_section = 'props'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('object', 'Object', 'All object componenets visibility'),
('mesh', 'Mesh', 'Mesh visibility only'),
('shadow', 'Shadow', 'Shadow visibility only'),

View file

@ -6,7 +6,8 @@ class SpawnObjectByNameNode(ArmLogicTreeNode):
bl_label = 'Spawn Object By Name'
arm_version = 1
property0: PointerProperty(
property0: HaxePointerProperty(
'property0',
type=bpy.types.Scene, name='Scene',
description='The scene from which to take the object')

View file

@ -59,18 +59,16 @@ class AddRigidBodyNode(ArmLogicTreeNode):
further down."""
self.update_sockets(context)
@property
def property1(self):
return 'true' if self.property1_ else 'false'
property1_: BoolProperty(
property1: HaxeBoolProperty(
'property1',
name="Advanced",
description="Show advanced options",
default=False,
update=update_advanced
)
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Box', 'Box', 'Box'),
('Sphere', 'Sphere', 'Sphere'),
('Capsule', 'Capsule', 'Capsule'),
@ -122,5 +120,5 @@ class AddRigidBodyNode(ArmLogicTreeNode):
def draw_buttons(self, context, layout):
layout.prop(self, "property1_")
layout.prop(self, 'property0')
layout.prop(self, "property1")
layout.prop(self, 'property0')

View file

@ -124,7 +124,8 @@ class AddPhysicsConstraintNode(ArmLogicTreeNode):
self['property0'] = value
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Fixed', 'Fixed', 'Fixed'),
('Point', 'Point', 'Point'),
('Hinge', 'Hinge', 'Hinge'),

View file

@ -16,7 +16,8 @@ class OnContactNode(ArmLogicTreeNode):
arm_section = 'contact'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('begin', 'Begin', 'The contact between the rigid bodies begins'),
('overlap', 'Overlap', 'The contact between the rigid bodies is happening'),
('end', 'End', 'The contact between the rigid bodies ends')],

View file

@ -7,7 +7,8 @@ class OnContactArrayNode(ArmLogicTreeNode):
arm_section = 'contact'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('begin', 'Begin', 'The contact between the rigid bodies begins'),
('overlap', 'Overlap', 'The contact between the rigid bodies is happening'),
('end', 'End', 'The contact between the rigid bodies ends')],

View file

@ -10,7 +10,8 @@ class OnVolumeTriggerNode(ArmLogicTreeNode):
bl_label = 'On Volume Trigger'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('begin', 'Begin', 'The contact between the rigid bodies begins'),
('overlap', 'Overlap', 'The contact between the rigid bodies is happening'),
('end', 'End', 'The contact between the rigid bodies ends')],

View file

@ -26,32 +26,21 @@ class PhysicsConstraintNode(ArmLogicTreeNode):
def update_spring(self, context):
self.update_sockets(context)
property0: EnumProperty(
items = [('Linear', 'Linear', 'Linear'),
('Angular', 'Angular', 'Angular')],
property0: HaxeEnumProperty(
'property0',
items=[('Linear', 'Linear', 'Linear'),
('Angular', 'Angular', 'Angular')],
name='Type', default='Linear')
@property
def property1(self):
if(self.property1_ == 'X'):
return 'X'
if(self.property1_ == 'Y'):
return 'Y'
if(self.property1_ == 'Z'):
return 'Z'
property1_: EnumProperty(
items = [('X', 'X', 'X'),
('Y', 'Y', 'Y'),
('Z', 'Z', 'Z')],
property1: HaxeEnumProperty(
'property1',
items=[('X', 'X', 'X'),
('Y', 'Y', 'Y'),
('Z', 'Z', 'Z')],
name='Axis', default='X')
@property
def property2(self):
if(self.property2_):
return 'true' if self.property2_ else 'false'
property2_: BoolProperty(
property2: HaxeBoolProperty(
'property2',
name="Spring",
description="Is a spring constraint",
default=False,
@ -66,14 +55,13 @@ class PhysicsConstraintNode(ArmLogicTreeNode):
self.add_input('NodeSocketFloat', 'Lower limit')
self.add_input('NodeSocketFloat', 'Upper limit')
self.add_output('NodeSocketShader', 'Constraint')
def update_sockets(self, context):
while (len(self.inputs) > 0):
def update_sockets(self, context):
while len(self.inputs) > 0:
self.inputs.remove(self.inputs.values()[-1])
# Add dynamic input sockets
if self.property2_:
if self.property2:
self.add_input('NodeSocketFloat', 'Stiffness', 10.0)
self.add_input('NodeSocketFloat', 'Damping', 0.5)
else:
@ -82,6 +70,5 @@ class PhysicsConstraintNode(ArmLogicTreeNode):
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
layout.prop(self, 'property1_')
layout.prop(self, 'property2_')
layout.prop(self, 'property1')
layout.prop(self, 'property2')

View file

@ -7,7 +7,8 @@ class SetActivationStateNode(ArmLogicTreeNode):
bl_icon = 'NONE'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('inactive', 'Inactive', 'The rigid body simulation is deactivated'),
('active', 'Active', 'The rigid body simulation is activated'),
('always active', 'Always Active', 'The rigid body simulation is never deactivated'),

View file

@ -12,7 +12,8 @@ class VolumeTriggerNode(ArmLogicTreeNode):
arm_section = 'misc'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('begin', 'Begin', 'The contact between the rigid bodies begins'),
('overlap', 'Overlap', 'The contact between the rigid bodies is happening'),
('end', 'End', 'The contact between the rigid bodies ends')],

View file

@ -32,7 +32,8 @@ class ColorgradingSetGlobalNode(ArmLogicTreeNode):
arm_version = 1
# TODO: RRESET FILE OPTION FOR THE BELOW
property0 : EnumProperty(
property0 : HaxeEnumProperty(
'property0',
items = [('RGB', 'RGB', 'RGB'),
('Uniform', 'Uniform', 'Uniform')],
name='Mode', default='Uniform', update=update_node)

View file

@ -32,7 +32,8 @@ class ColorgradingSetHighlightNode(ArmLogicTreeNode):
arm_version = 1
# TODO: RRESET FILE OPTION FOR THE BELOW
property0 : EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('RGB', 'RGB', 'RGB'),
('Uniform', 'Uniform', 'Uniform')],
name='Mode', default='Uniform', update=update_node)

View file

@ -32,7 +32,8 @@ class ColorgradingSetMidtoneNode(ArmLogicTreeNode):
arm_version = 1
# TODO: RRESET FILE OPTION FOR THE BELOW
property0 : EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('RGB', 'RGB', 'RGB'),
('Uniform', 'Uniform', 'Uniform')],
name='Mode', default='Uniform', update=update_node)

View file

@ -32,7 +32,8 @@ class ColorgradingSetShadowNode(ArmLogicTreeNode):
arm_version = 1
# TODO: RRESET FILE OPTION FOR THE BELOW
property0 : EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('RGB', 'RGB', 'RGB'),
('Uniform', 'Uniform', 'Uniform')],
name='Mode', default='Uniform', update=update_node)

View file

@ -5,7 +5,8 @@ class RpMSAANode(ArmLogicTreeNode):
bl_idname = 'LNRpMSAANode'
bl_label = 'Set MSAA Quality'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('1', '1', '1'),
('2', '2', '2'),
('4', '4', '4'),

View file

@ -5,7 +5,8 @@ class RpConfigNode(ArmLogicTreeNode):
bl_idname = 'LNRpConfigNode'
bl_label = 'Set Post Process Quality'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('SSGI', 'SSGI', 'SSGI'),
('SSR', 'SSR', 'SSR'),
('Bloom', 'Bloom', 'Bloom'),

View file

@ -21,7 +21,8 @@ class SetShaderUniformNode(ArmLogicTreeNode):
elif self.property0 in ('vec2', 'vec3', 'vec4'):
self.add_input('NodeSocketVector', 'Vector')
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('int', 'int', 'int'),
('float', 'float', 'float'),
('vec2', 'vec2', 'vec2'),

View file

@ -5,7 +5,8 @@ class RpShadowQualityNode(ArmLogicTreeNode):
bl_idname = 'LNRpShadowQualityNode'
bl_label = 'Set Shadows Quality'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('High', 'High', 'High'),
('Medium', 'Medium', 'Medium'),
('Low', 'Low', 'Low')

View file

@ -5,7 +5,8 @@ class RpSuperSampleNode(ArmLogicTreeNode):
bl_idname = 'LNRpSuperSampleNode'
bl_label = 'Set SSAA Quality'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('1', '1', '1'),
('1.5', '1.5', '1.5'),
('2', '2', '2'),

View file

@ -12,7 +12,7 @@ class GroupNode(ArmLogicTreeNode):
arm_section = 'collection'
arm_version = 1
property0: PointerProperty(name='', type=bpy.types.Collection)
property0: HaxePointerProperty('property0', name='', type=bpy.types.Collection)
def init(self, context):
super(GroupNode, self).init(context)

View file

@ -9,7 +9,7 @@ class SceneNode(ArmLogicTreeNode):
bl_label = 'Scene'
arm_version = 1
property0_get: PointerProperty(name='', type=bpy.types.Scene)
property0_get: HaxePointerProperty('property0_get', name='', type=bpy.types.Scene)
def init(self, context):
super(SceneNode, self).init(context)

View file

@ -28,7 +28,7 @@ class SpawnCollectionNode(ArmLogicTreeNode):
arm_section = 'collection'
arm_version = 1
property0: PointerProperty(name='Collection', type=bpy.types.Collection)
property0: HaxePointerProperty('property0', name='Collection', type=bpy.types.Collection)
def init(self, context):
super(SpawnCollectionNode, self).init(context)

View file

@ -31,25 +31,30 @@ class PlaySoundNode(ArmLogicTreeNode):
bl_width_default = 200
arm_version = 1
property0: PointerProperty(name='', type=bpy.types.Sound)
property1: BoolProperty(
property0: HaxePointerProperty('property0', name='', type=bpy.types.Sound)
property1: HaxeBoolProperty(
'property1',
name='Loop',
description='Play the sound in a loop',
default=False)
property2: BoolProperty(
property2: HaxeBoolProperty(
'property2',
name='Retrigger',
description='Play the sound from the beginning every time',
default=False)
property3: BoolProperty(
property3: HaxeBoolProperty(
'property3',
name='Use Custom Sample Rate',
description='If enabled, override the default sample rate',
default=False)
property4: IntProperty(
property4: HaxeIntProperty(
'property4',
name='Sample Rate',
description='Set the sample rate used to play this sound',
default=44100,
min=0)
property5: BoolProperty(
property5: HaxeBoolProperty(
'property5',
name='Stream',
description='Stream the sound from disk',
default=False

View file

@ -5,7 +5,8 @@ class CaseStringNode(ArmLogicTreeNode):
bl_idname = 'LNCaseStringNode'
bl_label = 'String Case'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Upper Case', 'Upper Case', 'Upper Case'),
('Lower Case', 'Lower Case', 'Lower Case'),
],

View file

@ -5,7 +5,8 @@ class ContainsStringNode(ArmLogicTreeNode):
bl_idname = 'LNContainsStringNode'
bl_label = 'String Contains'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Contains', 'Contains', 'Contains'),
('Starts With', 'Starts With', 'Starts With'),
('Ends With', 'Ends With', 'Ends With'),

View file

@ -8,7 +8,7 @@ class TraitNode(ArmLogicTreeNode):
bl_label = 'Trait'
arm_version = 1
property0: StringProperty(name='', default='')
property0: HaxeStringProperty('property0', name='', default='')
def init(self, context):
super(TraitNode, self).init(context)

View file

@ -7,7 +7,8 @@ class GetWorldNode(ArmLogicTreeNode):
arm_section = 'rotation'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Right', 'Right', 'The object right (X) direction'),
('Look', 'Look', 'The object look (Y) direction'),
('Up', 'Up', 'The object up (Z) direction')],

View file

@ -7,7 +7,8 @@ class LookAtNode(ArmLogicTreeNode):
arm_section = 'rotation'
arm_version = 1
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('X', ' X', 'X'),
('-X', '-X', '-X'),
('Y', ' Y', 'Y'),

View file

@ -40,10 +40,11 @@ class RotateObjectNode(ArmLogicTreeNode):
else:
layout.prop(self, 'property0')
property0: EnumProperty(
items = [('Euler Angles', 'Euler Angles', 'Euler Angles'),
('Angle Axies (Radians)', 'Angle Axies (Radians)', 'Angle Axies (Radians)'),
('Angle Axies (Degrees)', 'Angle Axies (Degrees)', 'Angle Axies (Degrees)'),
('Quaternion', 'Quaternion', 'Quaternion')],
property0: HaxeEnumProperty(
'property0',
items=[('Euler Angles', 'Euler Angles', 'Euler Angles'),
('Angle Axies (Radians)', 'Angle Axies (Radians)', 'Angle Axies (Radians)'),
('Angle Axies (Degrees)', 'Angle Axies (Degrees)', 'Angle Axies (Degrees)'),
('Quaternion', 'Quaternion', 'Quaternion')],
name='', default='Euler Angles',
update = on_property_update)
update=on_property_update)

View file

@ -33,7 +33,8 @@ class SetRotationNode(ArmLogicTreeNode):
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Euler Angles', 'Euler Angles', 'Euler Angles'),
('Angle Axies (Radians)', 'Angle Axies (Radians)', 'Angle Axies (Radians)'),
('Angle Axies (Degrees)', 'Angle Axies (Degrees)', 'Angle Axies (Degrees)'),

View file

@ -31,7 +31,8 @@ class VectorFromTransformNode(ArmLogicTreeNode):
def draw_buttons(self, context, layout):
layout.prop(self, 'property0')
property0: EnumProperty(
property0: HaxeEnumProperty(
'property0',
items = [('Right', 'Right', 'The transform right (X) direction'),
('Look', 'Look', 'The transform look (Y) direction'),
('Up', 'Up', 'The transform up (Z) direction'),

View file

@ -158,26 +158,9 @@ def build_node(node: bpy.types.Node, f: TextIO) -> Optional[str]:
f.write(f'\t\tthis.nodes["{name[1:]}"] = {name};\n')
# Properties
for i in range(0, 10):
prop_name = 'property' + str(i) + '_get'
prop_found = hasattr(node, prop_name)
if not prop_found:
prop_name = 'property' + str(i)
prop_found = hasattr(node, prop_name)
if prop_found:
prop = getattr(node, prop_name)
if isinstance(prop, str):
prop = '"' + str(prop) + '"'
elif isinstance(prop, bool):
prop = str(prop).lower()
elif hasattr(prop, 'name'): # PointerProperty
prop = '"' + str(prop.name) + '"'
else:
if prop is None:
prop = 'null'
else:
prop = str(prop)
f.write('\t\t' + name + '.property' + str(i) + ' = ' + prop + ';\n')
for prop_name in arm.node_utils.get_haxe_property_names(node):
prop = arm.node_utils.haxe_format_prop(node, prop_name)
f.write('\t\t' + name + '.' + prop_name + ' = ' + prop + ';\n')
# Create inputs
for inp in node.inputs:
@ -284,23 +267,16 @@ def build_default_node(inp: bpy.types.NodeSocket):
if is_custom_socket:
# ArmCustomSockets need to implement get_default_value()
default_value = inp.get_default_value()
if isinstance(default_value, str):
default_value = '"{:s}"'.format(default_value.replace('"', '\\"') )
default_value = arm.node_utils.haxe_format_socket_val(inp.get_default_value())
inp_type = inp.arm_socket_type # any custom socket's `type` is "VALUE". might as well have valuable type information for custom nodes as well.
else:
if hasattr(inp, 'default_value'):
default_value = inp.default_value
else:
default_value = None
if isinstance(default_value, str):
default_value = '"{:s}"'.format(default_value.replace('"', '\\"') )
default_value = arm.node_utils.haxe_format_socket_val(default_value)
inp_type = inp.type
# Don't write 'None' into the Haxe code
if default_value is None:
default_value = 'null'
if inp_type == 'VECTOR':
return f'new armory.logicnode.VectorNode(this, {default_value[0]}, {default_value[1]}, {default_value[2]})'
elif inp_type == 'RGBA':

View file

@ -1,4 +1,4 @@
from typing import Type, Union
from typing import Any, Generator, Type, Union
import bpy
from bpy.types import NodeSocket, NodeInputs, NodeOutputs
@ -83,6 +83,50 @@ def get_export_node_name(node: bpy.types.Node) -> str:
return '_' + arm.utils.safesrc(node.name)
def get_haxe_property_names(node: bpy.types.Node) -> Generator[str, None, None]:
"""Generator that yields the names of all node properties that have
a counterpart in the node's Haxe class.
"""
for i in range(0, 10):
prop_name = f'property{i}_get'
prop_found = hasattr(node, prop_name)
if not prop_found:
prop_name = f'property{i}'
prop_found = hasattr(node, prop_name)
if prop_found:
yield prop_name
def haxe_format_socket_val(socket_val: Any) -> str:
"""Formats a socket value to be valid Haxe syntax."""
if isinstance(socket_val, str):
socket_val = '"{:s}"'.format(socket_val.replace('"', '\\"'))
elif socket_val is None:
# Don't write 'None' into the Haxe code
socket_val = 'null'
return socket_val
def haxe_format_prop(node: bpy.types.Node, prop_name: str) -> str:
"""Formats a property value to be valid Haxe syntax."""
prop_value = getattr(node, prop_name)
if isinstance(prop_value, str):
prop_value = '"' + str(prop_value) + '"'
elif isinstance(prop_value, bool):
prop_value = str(prop_value).lower()
elif hasattr(prop_value, 'name'): # PointerProperty
prop_value = '"' + str(prop_value.name) + '"'
else:
if prop_value is None:
prop_value = 'null'
else:
prop_value = str(prop_value)
return prop_value
def nodetype_to_nodeitem(node_type: Type[bpy.types.Node]) -> NodeItem:
"""Create a NodeItem from a given node class."""
# Internal node types seem to have no bl_idname attribute