Last active
November 18, 2018 02:41
-
-
Save zicklag/2f6eeabd4f1802349b99793082b95c4a to your computer and use it in GitHub Desktop.
Patch for Function Nodes in Armory
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/Sources/armory/logicnode/CallFunctionNode.hx b/Sources/armory/logicnode/CallFunctionNode.hx | |
index d53fabae..b032dd95 100644 | |
--- a/Sources/armory/logicnode/CallFunctionNode.hx | |
+++ b/Sources/armory/logicnode/CallFunctionNode.hx | |
@@ -15,8 +15,15 @@ class CallFunctionNode extends LogicNode { | |
if (object == null) return; | |
var funName:String = inputs[2].get(); | |
+ var args:Array<Dynamic> = []; | |
+ | |
+ for (i in 3...inputs.length) { | |
+ args.push(inputs[i].get()); | |
+ } | |
- result = Reflect.callMethod(object, Reflect.field(object, funName), null); | |
+ var func = Reflect.field(object, funName); | |
+ if (func != null) | |
+ result = Reflect.callMethod(object, func, args); | |
runOutputs(0); | |
} | |
diff --git a/Sources/armory/logicnode/FunctionNode.hx b/Sources/armory/logicnode/FunctionNode.hx | |
new file mode 100644 | |
index 00000000..65950224 | |
--- /dev/null | |
+++ b/Sources/armory/logicnode/FunctionNode.hx | |
@@ -0,0 +1,22 @@ | |
+package armory.logicnode; | |
+ | |
+class FunctionNode extends LogicNode { | |
+ | |
+ @:allow(armory.logicnode.LogicTree) | |
+ var args:Array<Dynamic> = []; | |
+ @:allow(armory.logicnode.LogicTree) | |
+ var result:Dynamic; | |
+ | |
+ public function new(tree:LogicTree) { | |
+ super(tree); | |
+ } | |
+ | |
+ @:allow(armory.logicnode.LogicTree) | |
+ override function run(from:Int) { | |
+ runOutput(0); | |
+ } | |
+ | |
+ override function get(from:Int) { | |
+ return this.args[from - 1]; | |
+ } | |
+} | |
diff --git a/Sources/armory/logicnode/FunctionOutputNode.hx b/Sources/armory/logicnode/FunctionOutputNode.hx | |
new file mode 100644 | |
index 00000000..1a10722c | |
--- /dev/null | |
+++ b/Sources/armory/logicnode/FunctionOutputNode.hx | |
@@ -0,0 +1,16 @@ | |
+package armory.logicnode; | |
+ | |
+class FunctionOutputNode extends LogicNode { | |
+ | |
+ @:allow(armory.logicnode.LogicTree) | |
+ var result:Dynamic; | |
+ | |
+ public function new(tree:LogicTree) { | |
+ super(tree); | |
+ } | |
+ | |
+ override function run(from:Int) { | |
+ this.result = inputs[1].get(); | |
+ runOutput(0); | |
+ } | |
+} | |
diff --git a/blender/arm/logicnode/action_call_function.py b/blender/arm/logicnode/action_call_function.py | |
new file mode 100644 | |
index 00000000..7eab4d65 | |
--- /dev/null | |
+++ b/blender/arm/logicnode/action_call_function.py | |
@@ -0,0 +1,33 @@ | |
+import bpy | |
+from bpy.props import * | |
+from bpy.types import Node, NodeSocket | |
+from arm.logicnode.arm_nodes import * | |
+ | |
+class CallFunctionNode(Node, ArmLogicTreeNode): | |
+ '''Call Haxe function node''' | |
+ bl_idname = 'LNCallFunctionNode' | |
+ bl_label = 'Call Function' | |
+ bl_icon = 'GAME' | |
+ min_inputs = 3 | |
+ | |
+ def __init__(self): | |
+ array_nodes[str(id(self))] = self | |
+ | |
+ def init(self, context): | |
+ self.inputs.new('ArmNodeSocketAction', 'In') | |
+ self.inputs.new('NodeSocketShader', 'Trait/Any') | |
+ self.inputs.new('NodeSocketString', 'Function') | |
+ self.outputs.new('ArmNodeSocketAction', 'Out') | |
+ self.outputs.new('NodeSocketShader', 'Result') | |
+ | |
+ def draw_buttons(self, context, layout): | |
+ row = layout.row(align=True) | |
+ op = row.operator('arm.node_add_input', text='Add Arg', icon='PLUS', emboss=True) | |
+ op.node_index = str(id(self)) | |
+ op.socket_type = 'NodeSocketShader' | |
+ op.name_format = "Arg {0}" | |
+ op.index_name_offset = -2 | |
+ op2 = row.operator('arm.node_remove_input', text='', icon='X', emboss=True) | |
+ op2.node_index = str(id(self)) | |
+ | |
+add_node(CallFunctionNode, category='Action') | |
diff --git a/blender/arm/logicnode/arm_nodes.py b/blender/arm/logicnode/arm_nodes.py | |
index db257d68..eb43cc24 100644 | |
--- a/blender/arm/logicnode/arm_nodes.py | |
+++ b/blender/arm/logicnode/arm_nodes.py | |
@@ -122,11 +122,13 @@ class ArmNodeAddInputButton(bpy.types.Operator): | |
bl_label = 'Add Input' | |
node_index = StringProperty(name='Node Index', default='') | |
socket_type = StringProperty(name='Socket Type', default='NodeSocketShader') | |
+ name_format = StringProperty(name='Name Format', default='Input {0}') | |
+ index_name_offset = IntProperty(name='Index Name Offset', default=0) | |
def execute(self, context): | |
global array_nodes | |
inps = array_nodes[self.node_index].inputs | |
- inps.new(self.socket_type, 'Input ' + str(len(inps))) | |
+ inps.new(self.socket_type, self.name_format.format(str(len(inps) + self.index_name_offset))) | |
return{'FINISHED'} | |
class ArmNodeAddInputValueButton(bpy.types.Operator): | |
@@ -178,11 +180,13 @@ class ArmNodeAddOutputButton(bpy.types.Operator): | |
bl_label = 'Add Output' | |
node_index = StringProperty(name='Node Index', default='') | |
socket_type = StringProperty(name='Socket Type', default='NodeSocketShader') | |
+ name_format = StringProperty(name='Name Format', default='Output {0}') | |
+ index_name_offset = IntProperty(name='Index Name Offset', default=0) | |
def execute(self, context): | |
global array_nodes | |
outs = array_nodes[self.node_index].outputs | |
- outs.new(self.socket_type, 'Output ' + str(len(outs))) | |
+ outs.new(self.socket_type, self.name_format.format(str(len(outs) + self.index_name_offset))) | |
return{'FINISHED'} | |
class ArmNodeRemoveOutputButton(bpy.types.Operator): | |
diff --git a/blender/arm/logicnode/logic_function.py b/blender/arm/logicnode/logic_function.py | |
new file mode 100644 | |
index 00000000..6e5cb9ea | |
--- /dev/null | |
+++ b/blender/arm/logicnode/logic_function.py | |
@@ -0,0 +1,33 @@ | |
+import bpy | |
+from bpy.props import * | |
+from bpy.types import Node, NodeSocket | |
+from arm.logicnode.arm_nodes import * | |
+ | |
+class FunctionNode(Node, ArmLogicTreeNode): | |
+ '''Function node''' | |
+ bl_idname = 'LNFunctionNode' | |
+ bl_label = 'Function' | |
+ bl_icon = 'CURVE_PATH' | |
+ min_outputs = 1 | |
+ | |
+ def __init__(self): | |
+ array_nodes[str(id(self))] = self | |
+ | |
+ def init(self, context): | |
+ self.outputs.new('ArmNodeSocketAction', 'Out') | |
+ | |
+ function_name = StringProperty(name="Name") | |
+ | |
+ def draw_buttons(self, context, layout): | |
+ row = layout.row(align=True) | |
+ row.prop(self, 'function_name') | |
+ row = layout.row(align=True) | |
+ op = row.operator('arm.node_add_output', text='Add Arg', icon='PLUS', emboss=True) | |
+ op.node_index = str(id(self)) | |
+ op.socket_type = 'NodeSocketShader' | |
+ op.name_format = "Arg {0}" | |
+ op.index_name_offset = 0 | |
+ op2 = row.operator('arm.node_remove_output', text='', icon='X', emboss=True) | |
+ op2.node_index = str(id(self)) | |
+ | |
+add_node(FunctionNode, category='Logic') | |
diff --git a/blender/arm/logicnode/logic_function_output.py b/blender/arm/logicnode/logic_function_output.py | |
new file mode 100644 | |
index 00000000..dfc5fd90 | |
--- /dev/null | |
+++ b/blender/arm/logicnode/logic_function_output.py | |
@@ -0,0 +1,22 @@ | |
+import bpy | |
+from bpy.props import * | |
+from bpy.types import Node, NodeSocket | |
+from arm.logicnode.arm_nodes import * | |
+ | |
+class FunctionOutputNode(Node, ArmLogicTreeNode): | |
+ '''Function output node''' | |
+ bl_idname = 'LNFunctionOutputNode' | |
+ bl_label = 'Function Output' | |
+ bl_icon = 'CURVE_PATH' | |
+ | |
+ def init(self, context): | |
+ self.inputs.new('ArmNodeSocketAction', 'In') | |
+ self.inputs.new('NodeSocketShader', 'Value') | |
+ | |
+ function_name = StringProperty(name="Name") | |
+ | |
+ def draw_buttons(self, context, layout): | |
+ row = layout.row(align=True) | |
+ row.prop(self, 'function_name') | |
+ | |
+add_node(FunctionOutputNode, category='Logic') | |
diff --git a/blender/arm/make_logic.py b/blender/arm/make_logic.py | |
index 833cba1b..72beb9b4 100755 | |
--- a/blender/arm/make_logic.py | |
+++ b/blender/arm/make_logic.py | |
@@ -6,6 +6,8 @@ from arm.exporter import ArmoryExporter | |
parsed_nodes = [] | |
parsed_ids = dict() # Sharing node data | |
+function_nodes = dict() | |
+function_node_outputs = dict() | |
group_name = '' | |
def get_logic_trees(): | |
@@ -32,9 +34,13 @@ def build(): | |
def build_node_tree(node_group): | |
global parsed_nodes | |
global parsed_ids | |
+ global function_nodes | |
+ global function_node_outputs | |
global group_name | |
parsed_nodes = [] | |
parsed_ids = dict() | |
+ function_nodes = dict() | |
+ function_node_outputs = dict() | |
root_nodes = get_root_nodes(node_group) | |
pack_path = arm.utils.safestr(bpy.data.worlds['Arm'].arm_project_package) | |
@@ -54,15 +60,39 @@ def build_node_tree(node_group): | |
with open(file, 'w') as f: | |
f.write('package ' + pack_path + '.node;\n\n') | |
f.write('@:keep class ' + group_name + ' extends armory.logicnode.LogicTree {\n\n') | |
- f.write('\tpublic function new() { super();') | |
+ f.write('\tvar functionNodes:Map<String, armory.logicnode.FunctionNode>;\n\n') | |
+ f.write('\tvar functionOutputNodes:Map<String, armory.logicnode.FunctionOutputNode>;\n\n') | |
+ f.write('\tpublic function new() {\n') | |
+ f.write('\t\tsuper();\n') | |
if bpy.data.worlds['Arm'].arm_play_console: | |
- f.write(' name = "' + group_name + '";') | |
- f.write(' notifyOnAdd(add); }\n\n') | |
+ f.write('\t\tname = "' + group_name + '";\n') | |
+ f.write('\t\tthis.functionNodes = new Map();\n') | |
+ f.write('\t\tthis.functionOutputNodes = new Map();\n') | |
+ f.write('\t\tnotifyOnAdd(add);\n') | |
+ f.write('\t}\n\n') | |
f.write('\toverride public function add() {\n') | |
for node in root_nodes: | |
build_node(node, f) | |
f.write('\t}\n') | |
- f.write('}\n') | |
+ | |
+ # Create node functions | |
+ for node_name in function_nodes: | |
+ node = function_nodes[node_name] | |
+ function_name = node.function_name | |
+ f.write('\n\tpublic function ' + function_name + '(') | |
+ for i in range(0, len(node.outputs) - 1): | |
+ if i != 0: f.write(', ') | |
+ f.write('arg' + str(i) + ':Dynamic') | |
+ f.write(') {\n') | |
+ f.write('\t\tvar functionNode = this.functionNodes["' + node_name + '"];\n') | |
+ f.write('\t\tfunctionNode.args = [];\n') | |
+ for i in range(0, len(node.outputs) - 1): | |
+ f.write('\t\tfunctionNode.args.push(arg' + str(i) + ');\n') | |
+ f.write('\t\tfunctionNode.run(0);\n') | |
+ if function_node_outputs.get(function_name) != None: | |
+ f.write('\t\treturn this.functionOutputNodes["' + function_node_outputs[function_name] + '"].result;\n') | |
+ f.write('\t}\n\n') | |
+ f.write('}') | |
node_group.is_cached = True | |
def build_node(node, f): | |
@@ -94,6 +124,15 @@ def build_node(node, f): | |
node_type = node.bl_idname[2:] # Discard 'LN'TimeNode prefix | |
f.write('\t\tvar ' + name + ' = new armory.logicnode.' + node_type + '(this);\n') | |
+ # Handle Function Nodes | |
+ if node_type == 'FunctionNode': | |
+ f.write('\t\tthis.functionNodes.set("' + name + '", ' + name + ');\n') | |
+ function_nodes[name] = node | |
+ elif node_type == 'FunctionOutputNode': | |
+ f.write('\t\tthis.functionOutputNodes.set("' + name + '", ' + name + ');\n') | |
+ # Index function output name by corresponding function name | |
+ function_node_outputs[node.function_name] = name | |
+ | |
# Watch in debug console | |
if node.arm_watch and bpy.data.worlds['Arm'].arm_play_console: | |
f.write('\t\t' + name + '.name = "' + name[1:] + '";\n') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package armory.logicnode; | |
import iron.object.Object; | |
class CallFunctionNode extends LogicNode { | |
var result:Dynamic; | |
public function new(tree:LogicTree) { | |
super(tree); | |
} | |
override function run() { | |
var object:Object = inputs[1].get(); | |
if (object == null) return; | |
var funName:String = inputs[2].get(); | |
var args:Array<Dynamic> = []; | |
for (i in 3...inputs.length) { | |
args.push(inputs[i].get()); | |
} | |
var func = Reflect.field(object, funName); | |
if (func != null) | |
result = Reflect.callMethod(object, func, args); | |
runOutputs(0); | |
} | |
override function get(from:Int):Dynamic { | |
return result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment