198 lines
5.2 KiB
Haxe
198 lines
5.2 KiB
Haxe
package armory.logicnode;
|
|
|
|
import armory.system.Cycles;
|
|
|
|
class LogicTree extends armory.Trait {
|
|
|
|
public var loopBreak = false; // Trigger break from loop nodes
|
|
|
|
public static var packageName = "armory.logicnode";
|
|
static var parsedNodes:Map<String, LogicNode> = null;
|
|
static var canvas:TNodeCanvas;
|
|
|
|
public function new() {
|
|
super();
|
|
}
|
|
|
|
public static function fromCanvas(_canvas:TNodeCanvas):LogicTree {
|
|
// notifyOnAdd(function({ }));
|
|
|
|
canvas = _canvas;
|
|
parsedNodes = new Map();
|
|
// parsedLabels = new Map();
|
|
|
|
var tree = new LogicTree();
|
|
var rootNodes = getRootNodes(canvas.nodes);
|
|
for (node in rootNodes) {
|
|
buildNode(tree, node);
|
|
}
|
|
return tree;
|
|
}
|
|
|
|
static function buildNode(tree:LogicTree, node:TNode):LogicNode {
|
|
// if (node.type == 'REROUTE') {
|
|
// return buildNode(tree, node.inputs[0].links[0].from_node);
|
|
// }
|
|
|
|
// Get node name
|
|
var name = '_' + safeSourceName(node.name);
|
|
|
|
// Link nodes using labels
|
|
// if (node.label != '') {
|
|
// if node.label in parsedLabels:
|
|
// return parsedLabels[node.label]
|
|
// parsedLabels[node.label] = name
|
|
// }
|
|
|
|
// Check if node already exists
|
|
if (parsedNodes.exists(name)) {
|
|
return parsedNodes.get(name);
|
|
}
|
|
|
|
// Create node
|
|
var lnode:LogicNode = createClassInstance(node.type, [tree]);
|
|
parsedNodes.set(name, lnode);
|
|
|
|
// Properties
|
|
// for (i in 0...5) {
|
|
// if hasattr(node, 'property' + str(i)):
|
|
// f.write('\t\t' + name + '.property' + str(i) + ' = "' + getattr(node, 'property' + str(i)) + '";\n')
|
|
// }
|
|
|
|
// Create inputs
|
|
for (inp in node.inputs) {
|
|
// Is linked - find node
|
|
var inpNode:LogicNode = null;
|
|
var inpFrom = 0;
|
|
var l = getInputLink(inp);
|
|
if (l != null) {
|
|
var n = getNode(l.from_id);
|
|
inpNode = buildNode(tree, n);
|
|
inpFrom = l.from_socket;
|
|
}
|
|
// Not linked - create node with default values
|
|
else {
|
|
inpNode = buildDefaultNode(inp, tree);
|
|
inpFrom = 0;
|
|
}
|
|
// Add input
|
|
lnode.addInput(inpNode, inpFrom);
|
|
}
|
|
|
|
// Create outputs
|
|
for (out in node.outputs) {
|
|
var outNodes = [];
|
|
var ls = getOutputLinks(out);
|
|
if (ls != null && ls.length > 0) {
|
|
for (l in ls) {
|
|
var n = getNode(l.to_id);
|
|
outNodes.push(buildNode(tree, n));
|
|
}
|
|
}
|
|
// Not linked - create node with default values
|
|
else {
|
|
outNodes.push(buildDefaultNode(out, tree));
|
|
}
|
|
// Add input
|
|
lnode.addOutputs(outNodes);
|
|
}
|
|
|
|
return lnode;
|
|
}
|
|
|
|
static function createClassInstance(className:String, args:Array<Dynamic>):Dynamic {
|
|
var cname = Type.resolveClass(packageName + '.' + className);
|
|
if (cname == null) return null;
|
|
return Type.createInstance(cname, args);
|
|
}
|
|
|
|
static function safeSourceName(s):String {
|
|
return s;
|
|
}
|
|
|
|
static function getRootNodes(nodes:Array<TNode>):Array<TNode> {
|
|
var roots:Array<TNode> = [];
|
|
for (node in nodes) {
|
|
// if (node.type == 'FRAME') continue;
|
|
var linked = false;
|
|
for (out in node.outputs) {
|
|
var ls = getOutputLinks(out);
|
|
if (ls != null && ls.length > 0) {
|
|
linked = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!linked) roots.push(node); // Assume node with no connected outputs as roots
|
|
}
|
|
return roots;
|
|
}
|
|
|
|
static function buildDefaultNode(inp:TNodeSocket, tree:LogicTree):LogicNode {
|
|
|
|
if (inp.type == 'OBJECT') {
|
|
return createClassInstance('ObjectNode', [tree, inp.default_value]);
|
|
}
|
|
else if (inp.type == 'VECTOR') {
|
|
return createClassInstance('VectorNode', [tree, inp.default_value[0], inp.default_value[1], inp.default_value[2]]);
|
|
}
|
|
else if (inp.type == 'RGBA') {
|
|
return createClassInstance('ColorNode', [tree, inp.default_value[0], inp.default_value[1], inp.default_value[2], inp.default_value[3]]);
|
|
}
|
|
else if (inp.type == 'RGB') {
|
|
return createClassInstance('ColorNode', [tree, inp.default_value[0], inp.default_value[1], inp.default_value[2]]);
|
|
}
|
|
else if (inp.type == 'VALUE') {
|
|
return createClassInstance('FloatNode', [tree, inp.default_value]);
|
|
}
|
|
else if (inp.type == 'INT') {
|
|
return createClassInstance('IntegerNode', [tree, inp.default_value]);
|
|
}
|
|
else if (inp.type == 'BOOLEAN') {
|
|
return createClassInstance('BooleanNode', [tree, inp.default_value]);
|
|
}
|
|
else if (inp.type == 'STRING') {
|
|
return createClassInstance('StringNode', [tree, inp.default_value]);
|
|
}
|
|
else { // ACTION
|
|
return createClassInstance('NullNode', [tree]);
|
|
}
|
|
}
|
|
|
|
// Merge with Cycles.hx
|
|
static function getNode(id: Int): TNode {
|
|
for (n in canvas.nodes) if (n.id == id) return n;
|
|
return null;
|
|
}
|
|
|
|
static function getLink(id: Int): TNodeLink {
|
|
for (l in canvas.links) if (l.id == id) return l;
|
|
return null;
|
|
}
|
|
|
|
static function getInputLink(inp: TNodeSocket): TNodeLink {
|
|
for (l in canvas.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;
|
|
}
|
|
|
|
static function getOutputLinks(out: TNodeSocket): Array<TNodeLink> {
|
|
var ls:Array<TNodeLink> = null;
|
|
for (l in canvas.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) {
|
|
if (ls == null) ls = [];
|
|
ls.push(l);
|
|
}
|
|
}
|
|
}
|
|
return ls;
|
|
}
|
|
}
|