Runtime logic builder
This commit is contained in:
parent
fb60e4aa96
commit
c8cdb4a77a
|
@ -1,10 +1,196 @@
|
|||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
12
Sources/armory/logicnode/ShutdownNode.hx
Normal file
12
Sources/armory/logicnode/ShutdownNode.hx
Normal file
|
@ -0,0 +1,12 @@
|
|||
package armory.logicnode;
|
||||
|
||||
class ShutdownNode extends LogicNode {
|
||||
|
||||
public function new(tree:LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run() {
|
||||
kha.System.requestShutdown();
|
||||
}
|
||||
}
|
|
@ -403,17 +403,17 @@ class Cycles {
|
|||
static var parsing_basecol:Bool;
|
||||
static var normal_written:Bool; // Normal socket is linked on shader node - overwrite fs normal
|
||||
|
||||
static function getNode(id: Int): TNode {
|
||||
public static function getNode(id: Int): TNode {
|
||||
for (n in nodes) if (n.id == id) return n;
|
||||
return null;
|
||||
}
|
||||
|
||||
static function getLink(id: Int): TNodeLink {
|
||||
public static function getLink(id: Int): TNodeLink {
|
||||
for (l in links) if (l.id == id) return l;
|
||||
return null;
|
||||
}
|
||||
|
||||
static function getInputLink(inp: TNodeSocket): TNodeLink {
|
||||
public static function getInputLink(inp: TNodeSocket): TNodeLink {
|
||||
for (l in links) {
|
||||
if (l.to_id == inp.node_id) {
|
||||
var node = getNode(inp.node_id);
|
||||
|
@ -424,6 +424,21 @@ class Cycles {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static function getOutputLinks(out: TNodeSocket): Array<TNodeLink> {
|
||||
var ls:Array<TNodeLink> = null;
|
||||
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) {
|
||||
if (ls == null) ls = [];
|
||||
ls.push(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ls;
|
||||
}
|
||||
|
||||
public static function parse(canvas:TNodeCanvas, _vert:Shader, _frag:Shader, _geom:Shader, _tesc:Shader, _tese:Shader):TShaderOut {
|
||||
|
||||
nodes = canvas.nodes;
|
||||
|
@ -607,14 +622,15 @@ class Cycles {
|
|||
sout.out_roughness = parse_value_input(node.inputs[1]);
|
||||
}
|
||||
|
||||
// elif node.type == 'BSDF_GLOSSY':
|
||||
// if parse_surface:
|
||||
// write_normal(node.inputs[2])
|
||||
// parsing_basecol = True
|
||||
// out_basecol = parse_vector_input(node.inputs[0])
|
||||
// parsing_basecol = False
|
||||
// out_roughness = parse_value_input(node.inputs[1])
|
||||
// out_metallic = '1.0'
|
||||
else if (node.type == 'BSDF_GLOSSY') {
|
||||
// if parse_surface:
|
||||
write_normal(node.inputs[2]);
|
||||
parsing_basecol = true;
|
||||
sout.out_basecol = parse_vector_input(node.inputs[0]);
|
||||
parsing_basecol = false;
|
||||
sout.out_roughness = parse_value_input(node.inputs[1]);
|
||||
sout.out_metallic = '1.0';
|
||||
}
|
||||
|
||||
// elif node.type == 'AMBIENT_OCCLUSION':
|
||||
// if parse_surface:
|
||||
|
|
16
blender/arm/logicnode/native_shutdown.py
Normal file
16
blender/arm/logicnode/native_shutdown.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
from bpy.types import Node, NodeSocket
|
||||
from arm.logicnode.arm_nodes import *
|
||||
|
||||
class ShutdownNode(Node, ArmLogicTreeNode):
|
||||
'''Shutdown node'''
|
||||
bl_idname = 'LNShutdownNode'
|
||||
bl_label = 'Shutdown'
|
||||
bl_icon = 'GAME'
|
||||
|
||||
def init(self, context):
|
||||
self.inputs.new('ArmNodeSocketAction', 'In')
|
||||
self.outputs.new('ArmNodeSocketAction', 'Out')
|
||||
|
||||
add_node(ShutdownNode, category='Native')
|
Loading…
Reference in a new issue