diff --git a/Sources/arm/nodes/Logic.hx b/Sources/arm/nodes/Logic.hx new file mode 100644 index 00000000..43274fcb --- /dev/null +++ b/Sources/arm/nodes/Logic.hx @@ -0,0 +1,222 @@ +package arm.nodes; + +import zui.Nodes; + +class Logic { + + static var nodes:Array; + static var links:Array; + + static var parsed_nodes:Array = null; + static var parsed_labels:Map = null; + static var nodeMap:Map; + + public static var packageName = "arm.nodes.logic"; + + public static function getNode(id: Int): TNode { + for (n in nodes) if (n.id == id) return n; + return null; + } + + public static function getLink(id: Int): TNodeLink { + for (l in links) if (l.id == id) return l; + return null; + } + + public static function getInputLink(inp: TNodeSocket): TNodeLink { + for (l in links) { + if (l.to_id == inp.node_id) { + var node = getNode(inp.node_id); + if (node.inputs.length <= l.to_socket) return null; + if (node.inputs[l.to_socket] == inp) return l; + } + } + return null; + } + + public static function getOutputLinks(out: TNodeSocket): Array { + var res: Array = []; + for (l in links) { + if (l.from_id == out.node_id) { + var node = getNode(out.node_id); + if (node.outputs.length <= l.from_socket) continue; + if (node.outputs[l.from_socket] == out) res.push(l); + } + } + return res; + } + + static function safesrc(s:String):String { + return StringTools.replace(s, ' ', ''); + } + + static function node_name(node:TNode):String { + var s = safesrc(node.name) + node.id; + return s; + } + + static var tree:LogicTree; + public static function parse(canvas:TNodeCanvas, onAdd = true):LogicTree { + + nodes = canvas.nodes; + links = canvas.links; + + parsed_nodes = []; + parsed_labels = new Map(); + nodeMap = new Map(); + var root_nodes = get_root_nodes(canvas); + + tree = new LogicTree(); + if (onAdd) { + tree.notifyOnAdd(function() { + for (node in root_nodes) build_node(node); + }); + } + else { + for (node in root_nodes) build_node(node); + } + return tree; + } + + static function build_node(node:TNode):String { + + // Get node name + var name = node_name(node); + + // Check if node already exists + if (parsed_nodes.indexOf(name) != -1) { + return name; + } + + parsed_nodes.push(name); + + // Create node + var v = createClassInstance(node.type, [tree]); + nodeMap.set(name, v); + + // Properties + for (i in 0...5) { + for (b in node.buttons) { + if (b.name == 'property' + i) { + Reflect.setProperty(v, b.name, b.data[b.default_value]); + } + } + } + + // Create inputs + var inp_node:LogicNode = null; + var inp_from = 0; + for (i in 0...node.inputs.length) { + var inp = node.inputs[i]; + // Is linked - find node + var l = getInputLink(inp); + if (l != null) { + var n = getNode(l.from_id); + var socket = n.outputs[l.from_socket]; + inp_node = nodeMap.get(build_node(n)); + for (i in 0...n.outputs.length) { + if (n.outputs[i] == socket) { + inp_from = i; + break; + } + } + } + // Not linked - create node with default values + else { + inp_node = build_default_node(inp); + inp_from = 0; + } + // Add input + v.addInput(inp_node, inp_from); + } + + // Create outputs + for (out in node.outputs) { + var outNodes:Array = []; + var ls = getOutputLinks(out); + if (ls != null && ls.length > 0) { + for (l in ls) { + var n = getNode(l.to_id); + var out_name = build_node(n); + outNodes.push(nodeMap.get(out_name)); + } + } + // Not linked - create node with default values + else { + outNodes.push(build_default_node(out)); + } + // Add outputs + v.addOutputs(outNodes); + } + + return name; + } + + static function get_root_nodes(node_group:TNodeCanvas):Array { + var roots:Array = []; + for (node in node_group.nodes) { + // if (node.bl_idname == 'NodeUndefined') { + // arm.log.warn('Undefined logic nodes in ' + node_group.name) + // return [] + // } + var linked = false; + for (out in node.outputs) { + var ls = getOutputLinks(out); + if (ls != null && ls.length > 0) { + linked = true; + break; + } + } + if (!linked) { // Assume node with no connected outputs as roots + roots.push(node); + } + } + return roots; + } + + static function build_default_node(inp:TNodeSocket):LogicNode { + + var v:LogicNode = null; + + if (inp.type == 'OBJECT') { + v = createClassInstance('ObjectNode', [tree, inp.default_value]); + } + else if (inp.type == 'ANIMACTION') { + v = createClassInstance('StringNode', [tree, inp.default_value]); + } + else if (inp.type == 'VECTOR') { + if (inp.default_value == null) inp.default_value = [0, 0, 0]; // TODO + v = createClassInstance('VectorNode', [tree, inp.default_value[0], inp.default_value[1], inp.default_value[2]]); + } + else if (inp.type == 'RGBA') { + if (inp.default_value == null) inp.default_value = [0, 0, 0]; // TODO + v = createClassInstance('ColorNode', [tree, inp.default_value[0], inp.default_value[1], inp.default_value[2], inp.default_value[3]]); + } + else if (inp.type == 'RGB') { + if (inp.default_value == null) inp.default_value = [0, 0, 0]; // TODO + v = createClassInstance('ColorNode', [tree, inp.default_value[0], inp.default_value[1], inp.default_value[2]]); + } + else if (inp.type == 'VALUE') { + v = createClassInstance('FloatNode', [tree, inp.default_value]); + } + else if (inp.type == 'INT') { + v = createClassInstance('IntegerNode', [tree, inp.default_value]); + } + else if (inp.type == 'BOOLEAN') { + v = createClassInstance('BooleanNode', [tree, inp.default_value]); + } + else if (inp.type == 'STRING') { + v = createClassInstance('StringNode', [tree, inp.default_value]); + } + else { // ACTION, ARRAY + v = createClassInstance('NullNode', [tree]); + } + return v; + } + + static function createClassInstance(className:String, args:Array):Dynamic { + var cname = Type.resolveClass(packageName + '.' + className); + if (cname == null) return null; + return Type.createInstance(cname, args); + } +} diff --git a/Sources/arm/nodes/LogicNode.hx b/Sources/arm/nodes/LogicNode.hx new file mode 100644 index 00000000..baa5e5db --- /dev/null +++ b/Sources/arm/nodes/LogicNode.hx @@ -0,0 +1,71 @@ +package arm.nodes; + +class LogicNode { + + var tree:LogicTree; + var inputs:Array = []; + var outputs:Array> = []; + + public function new(tree:LogicTree) { + this.tree = tree; + } + + public function addInput(node:LogicNode, from:Int) { + inputs.push(new LogicNodeInput(node, from)); + } + + public function addOutputs(nodes:Array) { + outputs.push(nodes); + } + + /** + * Called when this node is activated. + * @param from impulse index + */ + function run(from:Int) {} + + /** + * Call to activate node connected to the output. + * @param i output index + */ + function runOutput(i:Int) { + if (i >= outputs.length) return; + for (o 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; + } + } + } + } + + @:allow(arm.nodes.LogicNodeInput) + function get(from:Int):Dynamic { return this; } + + @:allow(arm.nodes.LogicNodeInput) + function set(value:Dynamic) { } +} + +class LogicNodeInput { + + @:allow(arm.nodes.LogicNode) + var node:LogicNode; + var from:Int; // Socket index + + public function new(node:LogicNode, from:Int) { + this.node = node; + this.from = from; + } + + @:allow(arm.nodes.LogicNode) + function get():Dynamic { + return node.get(from); + } + + @:allow(arm.nodes.LogicNode) + function set(value:Dynamic) { + node.set(value); + } +} diff --git a/Sources/arm/nodes/LogicTree.hx b/Sources/arm/nodes/LogicTree.hx new file mode 100644 index 00000000..88665b0d --- /dev/null +++ b/Sources/arm/nodes/LogicTree.hx @@ -0,0 +1,30 @@ +package arm.nodes; + +class LogicTree extends iron.Trait { + + public var loopBreak = false; // Trigger break from loop nodes + + public function new() { + super(); + } + + public function add() {} + + var paused = false; + + public function pause() { + if (paused) return; + paused = true; + + if (_update != null) for (f in _update) iron.App.removeUpdate(f); + if (_lateUpdate != null) for (f in _lateUpdate) iron.App.removeLateUpdate(f); + } + + public function resume() { + if (!paused) return; + paused = false; + + if (_update != null) for (f in _update) iron.App.notifyOnUpdate(f); + if (_lateUpdate != null) for (f in _lateUpdate) iron.App.notifyOnLateUpdate(f); + } +} diff --git a/Sources/arm/nodes/brush/BooleanNode.hx b/Sources/arm/nodes/brush/BooleanNode.hx index f09efaa7..f0af9f17 100644 --- a/Sources/arm/nodes/brush/BooleanNode.hx +++ b/Sources/arm/nodes/brush/BooleanNode.hx @@ -1,8 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; - @:keep class BooleanNode extends LogicNode { diff --git a/Sources/arm/nodes/brush/BrushOutputNode.hx b/Sources/arm/nodes/brush/BrushOutputNode.hx index 8931e00c..4207fb96 100644 --- a/Sources/arm/nodes/brush/BrushOutputNode.hx +++ b/Sources/arm/nodes/brush/BrushOutputNode.hx @@ -1,7 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; import arm.ui.UITrait; import arm.ui.UIView2D; import arm.Tool; diff --git a/Sources/arm/nodes/brush/ColorNode.hx b/Sources/arm/nodes/brush/ColorNode.hx index 1138ffa0..cd20a1e9 100644 --- a/Sources/arm/nodes/brush/ColorNode.hx +++ b/Sources/arm/nodes/brush/ColorNode.hx @@ -1,8 +1,6 @@ package arm.nodes.brush; import iron.math.Vec4; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; @:keep class ColorNode extends LogicNode { diff --git a/Sources/arm/nodes/brush/FloatNode.hx b/Sources/arm/nodes/brush/FloatNode.hx index f4867a9c..c43670e4 100644 --- a/Sources/arm/nodes/brush/FloatNode.hx +++ b/Sources/arm/nodes/brush/FloatNode.hx @@ -1,8 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; - @:keep class FloatNode extends LogicNode { diff --git a/Sources/arm/nodes/brush/InputNode.hx b/Sources/arm/nodes/brush/InputNode.hx index 57559784..30d3ea75 100644 --- a/Sources/arm/nodes/brush/InputNode.hx +++ b/Sources/arm/nodes/brush/InputNode.hx @@ -1,7 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; import iron.math.Vec4; @:keep diff --git a/Sources/arm/nodes/brush/IntegerNode.hx b/Sources/arm/nodes/brush/IntegerNode.hx index 5b048547..6d0a25dc 100644 --- a/Sources/arm/nodes/brush/IntegerNode.hx +++ b/Sources/arm/nodes/brush/IntegerNode.hx @@ -1,8 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; - @:keep class IntegerNode extends LogicNode { diff --git a/Sources/arm/nodes/brush/MathNode.hx b/Sources/arm/nodes/brush/MathNode.hx index f100a9d1..beaa199c 100644 --- a/Sources/arm/nodes/brush/MathNode.hx +++ b/Sources/arm/nodes/brush/MathNode.hx @@ -1,7 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; import iron.math.Vec4; @:keep diff --git a/Sources/arm/nodes/brush/NullNode.hx b/Sources/arm/nodes/brush/NullNode.hx index fede7440..2dab9d32 100644 --- a/Sources/arm/nodes/brush/NullNode.hx +++ b/Sources/arm/nodes/brush/NullNode.hx @@ -1,8 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; - @:keep class NullNode extends LogicNode { diff --git a/Sources/arm/nodes/brush/ObjectNode.hx b/Sources/arm/nodes/brush/ObjectNode.hx index 91f189a2..e31b7751 100644 --- a/Sources/arm/nodes/brush/ObjectNode.hx +++ b/Sources/arm/nodes/brush/ObjectNode.hx @@ -1,8 +1,6 @@ package arm.nodes.brush; import iron.object.Object; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; @:keep class ObjectNode extends LogicNode { diff --git a/Sources/arm/nodes/brush/SeparateVectorNode.hx b/Sources/arm/nodes/brush/SeparateVectorNode.hx index 4e21f25c..04d5e25f 100644 --- a/Sources/arm/nodes/brush/SeparateVectorNode.hx +++ b/Sources/arm/nodes/brush/SeparateVectorNode.hx @@ -1,7 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; import iron.math.Vec4; @:keep diff --git a/Sources/arm/nodes/brush/StringNode.hx b/Sources/arm/nodes/brush/StringNode.hx index 809122bf..a6deb1ff 100644 --- a/Sources/arm/nodes/brush/StringNode.hx +++ b/Sources/arm/nodes/brush/StringNode.hx @@ -1,8 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; - @:keep class StringNode extends LogicNode { diff --git a/Sources/arm/nodes/brush/TimeNode.hx b/Sources/arm/nodes/brush/TimeNode.hx index 6484828c..501819a6 100644 --- a/Sources/arm/nodes/brush/TimeNode.hx +++ b/Sources/arm/nodes/brush/TimeNode.hx @@ -1,9 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; -import arm.ui.*; - @:keep class TimeNode extends LogicNode { @@ -14,6 +10,6 @@ class TimeNode extends LogicNode { override function get(from:Int):Dynamic { if (from == 0) return iron.system.Time.time(); else if (from == 1) return iron.system.Time.delta; - else return UITrait.inst.brushTime; + else return arm.ui.UITrait.inst.brushTime; } } diff --git a/Sources/arm/nodes/brush/VectorMathNode.hx b/Sources/arm/nodes/brush/VectorMathNode.hx index f2485f0e..3f50d183 100644 --- a/Sources/arm/nodes/brush/VectorMathNode.hx +++ b/Sources/arm/nodes/brush/VectorMathNode.hx @@ -1,7 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; import iron.math.Vec4; @:keep diff --git a/Sources/arm/nodes/brush/VectorNode.hx b/Sources/arm/nodes/brush/VectorNode.hx index 28e18e90..87a2108e 100644 --- a/Sources/arm/nodes/brush/VectorNode.hx +++ b/Sources/arm/nodes/brush/VectorNode.hx @@ -1,7 +1,5 @@ package arm.nodes.brush; -import armory.logicnode.LogicNode; -import armory.logicnode.LogicTree; import iron.math.Vec4; @:keep