Live patch: handle deletion of node links
This commit is contained in:
parent
256d27e289
commit
aa21402221
|
@ -4,8 +4,8 @@ package armory.logicnode;
|
|||
class LogicNode {
|
||||
|
||||
var tree: LogicTree;
|
||||
var inputs: Array<LogicNodeInput> = [];
|
||||
var outputs: Array<Array<LogicNode>> = [];
|
||||
var inputs: Array<LogicNodeLink> = [];
|
||||
var outputs: Array<Array<LogicNodeLink>> = [];
|
||||
|
||||
#if (arm_debug || arm_patch)
|
||||
public var name = "";
|
||||
|
@ -22,14 +22,115 @@ class LogicNode {
|
|||
this.tree = tree;
|
||||
}
|
||||
|
||||
public function addInput(node: LogicNode, from: Int) {
|
||||
inputs.push(new LogicNodeInput(node, from));
|
||||
/**
|
||||
Resize the inputs array to a given size to minimize dynamic
|
||||
reallocation and over-allocation later.
|
||||
**/
|
||||
inline function preallocInputs(amount: Int) {
|
||||
this.inputs.resize(amount);
|
||||
}
|
||||
|
||||
public function addOutputs(nodes: Array<LogicNode>) {
|
||||
outputs.push(nodes);
|
||||
/**
|
||||
Resize the outputs array to a given size to minimize dynamic
|
||||
reallocation and over-allocation later.
|
||||
**/
|
||||
inline function preallocOutputs(amount: Int) {
|
||||
this.outputs.resize(amount);
|
||||
for (i in 0...outputs.length) {
|
||||
outputs[i] = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Add a link between to nodes to the tree.
|
||||
**/
|
||||
public static function addLink(fromNode: LogicNode, toNode: LogicNode, fromIndex: Int, toIndex: Int): LogicNodeLink {
|
||||
var link = new LogicNodeLink(fromNode, toNode, fromIndex, toIndex);
|
||||
|
||||
if (toNode.inputs.length <= toIndex) {
|
||||
toNode.inputs.resize(toIndex + 1);
|
||||
}
|
||||
toNode.inputs[toIndex] = link;
|
||||
|
||||
var fromNodeOuts = fromNode.outputs;
|
||||
var outLen = fromNodeOuts.length;
|
||||
if (outLen <= fromIndex) {
|
||||
fromNodeOuts.resize(fromIndex + 1);
|
||||
|
||||
// Initialize with empty arrays
|
||||
for (i in 0...(fromIndex - (outLen - 1))) {
|
||||
fromNodeOuts[i] = [];
|
||||
}
|
||||
}
|
||||
fromNodeOuts[fromIndex].push(link);
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
#if arm_patch
|
||||
/**
|
||||
Removes a link from the tree.
|
||||
**/
|
||||
static function removeLink(link: LogicNodeLink) {
|
||||
link.fromNode.outputs[link.fromIndex].remove(link);
|
||||
|
||||
// Reuse the same link and connect a default input node to it.
|
||||
// That's why this function is only available in arm_patch mode, we need
|
||||
// access to the link's type and value.
|
||||
link.fromNode = LogicNode.createSocketDefaultNode(link.toNode.tree, link.toType, link.toValue);
|
||||
link.fromIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Removes all inputs and their links from this node.
|
||||
Warning: this function changes the amount of node inputs to 0!
|
||||
**/
|
||||
function clearInputs() {
|
||||
for (link in inputs) {
|
||||
link.fromNode.outputs[link.fromIndex].remove(link);
|
||||
}
|
||||
inputs.resize(0);
|
||||
}
|
||||
|
||||
/**
|
||||
Removes all outputs and their links from this node.
|
||||
Warning: this function changes the amount of node inputs to 0!
|
||||
**/
|
||||
function clearOutputs() {
|
||||
for (links in outputs) {
|
||||
for (link in links) {
|
||||
var defaultNode = LogicNode.createSocketDefaultNode(tree, link.toType, link.toValue);
|
||||
link.fromNode = defaultNode;
|
||||
link.fromIndex = 0;
|
||||
defaultNode.outputs[0] = [link];
|
||||
}
|
||||
}
|
||||
outputs.resize(0);
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a default node for a socket so that get() and set() can be
|
||||
used without null checks.
|
||||
Loosely equivalent to `make_logic.build_default_node()` in Python.
|
||||
**/
|
||||
static inline function createSocketDefaultNode(tree: LogicTree, socketType: String, value: Dynamic): LogicNode {
|
||||
// Make sure to not add these nodes to the LogicTree.nodes array as they
|
||||
// won't be garbage collected then if unlinked later.
|
||||
return switch (socketType) {
|
||||
case "VECTOR": new armory.logicnode.VectorNode(tree, value[0], value[1], value[2]);
|
||||
case "RGBA": new armory.logicnode.ColorNode(tree, value[0], value[1], value[2], value[3]);
|
||||
case "RGB": new armory.logicnode.ColorNode(tree, value[0], value[1], value[2]);
|
||||
case "VALUE": new armory.logicnode.FloatNode(tree, value);
|
||||
case "INT": new armory.logicnode.IntegerNode(tree, value);
|
||||
case "BOOLEAN": new armory.logicnode.BooleanNode(tree, value);
|
||||
case "STRING": new armory.logicnode.StringNode(tree, value);
|
||||
case "NONE": new armory.logicnode.NullNode(tree);
|
||||
case "OBJECT": new armory.logicnode.ObjectNode(tree, value);
|
||||
default: new armory.logicnode.DynamicNode(tree, value);
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
Called when this node is activated.
|
||||
@param from impulse index
|
||||
|
@ -42,42 +143,45 @@ class LogicNode {
|
|||
**/
|
||||
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;
|
||||
}
|
||||
}
|
||||
for (outLink in outputs[i]) {
|
||||
outLink.toNode.run(outLink.toIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@:allow(armory.logicnode.LogicNodeInput)
|
||||
@:allow(armory.logicnode.LogicNodeLink)
|
||||
function get(from: Int): Dynamic { return this; }
|
||||
|
||||
@:allow(armory.logicnode.LogicNodeInput)
|
||||
@:allow(armory.logicnode.LogicNodeLink)
|
||||
function set(value: Dynamic) {}
|
||||
}
|
||||
|
||||
class LogicNodeInput {
|
||||
@:allow(armory.logicnode.LogicNode)
|
||||
@:allow(armory.logicnode.LogicTree)
|
||||
class LogicNodeLink {
|
||||
|
||||
@:allow(armory.logicnode.LogicNode)
|
||||
var node: LogicNode;
|
||||
var from: Int; // Socket index
|
||||
var fromNode: LogicNode;
|
||||
var toNode: LogicNode;
|
||||
var fromIndex: Int;
|
||||
var toIndex: Int;
|
||||
|
||||
public function new(node: LogicNode, from: Int) {
|
||||
this.node = node;
|
||||
this.from = from;
|
||||
#if arm_patch
|
||||
var fromType: String;
|
||||
var toType: String;
|
||||
var toValue: Dynamic;
|
||||
#end
|
||||
|
||||
inline function new(fromNode: LogicNode, toNode: LogicNode, fromIndex: Int, toIndex: Int) {
|
||||
this.fromNode = fromNode;
|
||||
this.toNode = toNode;
|
||||
this.fromIndex = fromIndex;
|
||||
this.toIndex = toIndex;
|
||||
}
|
||||
|
||||
@:allow(armory.logicnode.LogicNode)
|
||||
function get(): Dynamic {
|
||||
return node.get(from);
|
||||
inline function get(): Dynamic {
|
||||
return fromNode.get(fromIndex);
|
||||
}
|
||||
|
||||
@:allow(armory.logicnode.LogicNode)
|
||||
function set(value: Dynamic) {
|
||||
node.set(value);
|
||||
inline function set(value: Dynamic) {
|
||||
fromNode.set(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ class QuaternionNode extends LogicNode {
|
|||
super(tree);
|
||||
|
||||
if (x != null) {
|
||||
addInput(new FloatNode(tree, x), 0);
|
||||
addInput(new FloatNode(tree, y), 0);
|
||||
addInput(new FloatNode(tree, z), 0);
|
||||
addInput(new FloatNode(tree, w), 0);
|
||||
LogicNode.addLink(new FloatNode(tree, x), this, 0, 0);
|
||||
LogicNode.addLink(new FloatNode(tree, y), this, 0, 1);
|
||||
LogicNode.addLink(new FloatNode(tree, z), this, 0, 2);
|
||||
LogicNode.addLink(new FloatNode(tree, w), this, 0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,15 +27,15 @@ class QuaternionNode extends LogicNode {
|
|||
switch (from){
|
||||
case 0:
|
||||
return value;
|
||||
case 1:
|
||||
var value1 = new Vec4();
|
||||
case 1:
|
||||
var value1 = new Vec4();
|
||||
value1.x = value.x;
|
||||
value1.y = value.y;
|
||||
value1.z = value.z;
|
||||
value1.w = 0; // use 0 to avoid this vector being translated.
|
||||
return value1;
|
||||
case 2:
|
||||
return value.w;
|
||||
return value.w;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ class SetParentNode extends LogicNode {
|
|||
|
||||
var parent: Object;
|
||||
var isUnparent = false;
|
||||
if (Std.is(inputs[2].node, ObjectNode)) {
|
||||
var parentNode = cast(inputs[2].node, ObjectNode);
|
||||
if (Std.is(inputs[2].fromNode, ObjectNode)) {
|
||||
var parentNode = cast(inputs[2].fromNode, ObjectNode);
|
||||
isUnparent = parentNode.objectName == "";
|
||||
}
|
||||
if (isUnparent) parent = iron.Scene.active.root;
|
||||
|
@ -24,7 +24,7 @@ class SetParentNode extends LogicNode {
|
|||
if (object == null || parent == null || object.parent == parent) return;
|
||||
|
||||
object.parent.removeChild(object, isUnparent); // keepTransform
|
||||
|
||||
|
||||
#if arm_physics
|
||||
var rigidBody = object.getTrait(RigidBody);
|
||||
if (rigidBody != null) rigidBody.setActivationState(0);
|
||||
|
|
|
@ -10,9 +10,9 @@ class VectorNode extends LogicNode {
|
|||
super(tree);
|
||||
|
||||
if (x != null) {
|
||||
addInput(new FloatNode(tree, x), 0);
|
||||
addInput(new FloatNode(tree, y), 0);
|
||||
addInput(new FloatNode(tree, z), 0);
|
||||
LogicNode.addLink(new FloatNode(tree, x), this, 0, 0);
|
||||
LogicNode.addLink(new FloatNode(tree, y), this, 0, 1);
|
||||
LogicNode.addLink(new FloatNode(tree, z), this, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import armory.logicnode.LogicTree;
|
|||
|
||||
#if arm_patch @:expose("LivePatch") #end
|
||||
@:access(armory.logicnode.LogicNode)
|
||||
@:access(armory.logicnode.LogicNodeInput)
|
||||
@:access(armory.logicnode.LogicNodeLink)
|
||||
class LivePatch extends iron.Trait {
|
||||
|
||||
#if !arm_patch
|
||||
|
@ -40,11 +40,54 @@ class LivePatch extends iron.Trait {
|
|||
var toNode = tree.nodes[toNodeName];
|
||||
if (fromNode == null || toNode == null) return;
|
||||
|
||||
// Don't add a connection twice
|
||||
if (!fromNode.outputs[fromIndex].contains(toNode)) {
|
||||
fromNode.outputs[fromIndex].push(toNode);
|
||||
LogicNode.addLink(fromNode, toNode, fromIndex, toIndex);
|
||||
}
|
||||
|
||||
public static function patchSetNodeLinks(treeName: String, nodeName: String, inputDatas: Array<Dynamic>, outputDatas: Array<Array<Dynamic>>) {
|
||||
var tree = LogicTree.nodeTrees[treeName];
|
||||
if (tree == null) return;
|
||||
|
||||
var node = tree.nodes[nodeName];
|
||||
if (node == null) return;
|
||||
|
||||
node.clearInputs();
|
||||
node.clearOutputs();
|
||||
|
||||
for (inputData in inputDatas) {
|
||||
var fromNode: LogicNode;
|
||||
var fromIndex: Int;
|
||||
|
||||
if (inputData.isLinked) {
|
||||
fromNode = tree.nodes[inputData.fromNode];
|
||||
if (fromNode == null) continue;
|
||||
fromIndex = inputData.fromIndex;
|
||||
}
|
||||
else {
|
||||
fromNode = LogicNode.createSocketDefaultNode(node.tree, inputData.socketType, inputData.socketValue);
|
||||
fromIndex = 0;
|
||||
}
|
||||
|
||||
LogicNode.addLink(fromNode, node, fromIndex, inputData.toIndex);
|
||||
}
|
||||
|
||||
for (outputData in outputDatas) {
|
||||
for (linkData in outputData) {
|
||||
var toNode: LogicNode;
|
||||
var toIndex: Int;
|
||||
|
||||
if (linkData.isLinked) {
|
||||
toNode = tree.nodes[linkData.toNode];
|
||||
if (toNode == null) continue;
|
||||
toIndex = linkData.toIndex;
|
||||
}
|
||||
else {
|
||||
toNode = LogicNode.createSocketDefaultNode(node.tree, linkData.socketType, linkData.socketValue);
|
||||
toIndex = 0;
|
||||
}
|
||||
|
||||
LogicNode.addLink(node, toNode, linkData.fromIndex, toIndex);
|
||||
}
|
||||
}
|
||||
toNode.inputs[toIndex] = new LogicNodeInput(fromNode, fromIndex);
|
||||
}
|
||||
|
||||
public static function patchUpdateNodeProp(treeName: String, nodeName: String, propName: String, value: Dynamic) {
|
||||
|
@ -123,11 +166,12 @@ class LivePatch extends iron.Trait {
|
|||
|
||||
var i = 0;
|
||||
for (inputData in inputDatas) {
|
||||
newNode.addInput(createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), i++);
|
||||
LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (outputData in outputDatas) {
|
||||
newNode.addOutputs([createSocketDefaultNode(newNode.tree, outputData[0], outputData[1])]);
|
||||
LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,27 +195,14 @@ class LivePatch extends iron.Trait {
|
|||
|
||||
var i = 0;
|
||||
for (inputData in inputDatas) {
|
||||
newNode.addInput(createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), i++);
|
||||
LogicNode.addLink(LogicNode.createSocketDefaultNode(newNode.tree, inputData[0], inputData[1]), newNode, 0, i++);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (outputData in outputDatas) {
|
||||
newNode.addOutputs([createSocketDefaultNode(newNode.tree, outputData[0], outputData[1])]);
|
||||
LogicNode.addLink(newNode, LogicNode.createSocketDefaultNode(newNode.tree, outputData[0], outputData[1]), i++, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline function createSocketDefaultNode(tree: LogicTree, socketType: String, value: Dynamic): LogicNode {
|
||||
return switch (socketType) {
|
||||
case "VECTOR": new armory.logicnode.VectorNode(tree, value[0], value[1], value[2]);
|
||||
case "RGBA": new armory.logicnode.ColorNode(tree, value[0], value[1], value[2], value[3]);
|
||||
case "RGB": new armory.logicnode.ColorNode(tree, value[0], value[1], value[2]);
|
||||
case "VALUE": new armory.logicnode.FloatNode(tree, value);
|
||||
case "INT": new armory.logicnode.IntegerNode(tree, value);
|
||||
case "BOOLEAN": new armory.logicnode.BooleanNode(tree, value);
|
||||
case "STRING": new armory.logicnode.StringNode(tree, value);
|
||||
case "NONE": new armory.logicnode.NullNode(tree);
|
||||
case "OBJECT": new armory.logicnode.ObjectNode(tree, value);
|
||||
default: new armory.logicnode.DynamicNode(tree, value);
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
|
|
@ -269,6 +269,53 @@ def send_event(event_id: str, opt_data: Any = None):
|
|||
js = f'LivePatch.patchNodeCopy("{tree_name}", "{node_name}", "{newnode_name}", {props_list}, {inp_data}, {out_data});'
|
||||
write_patch(js)
|
||||
|
||||
elif event_id == 'ln_update_sockets':
|
||||
node: ArmLogicTreeNode = 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:]
|
||||
|
||||
inp_data = '['
|
||||
for idx, inp in enumerate(node.inputs):
|
||||
inp_data += '{'
|
||||
# is_linked can be true even if there are no links if the
|
||||
# user starts dragging a connection away before releasing
|
||||
# the mouse
|
||||
if inp.is_linked and len(inp.links) > 0:
|
||||
inp_data += 'isLinked: true,'
|
||||
inp_data += f'fromNode: "{arm.node_utils.get_export_node_name(inp.links[0].from_node)[1:]}",'
|
||||
inp_data += f'fromIndex: {arm.node_utils.get_socket_index(inp.links[0].from_node.outputs, inp.links[0].from_socket)},'
|
||||
else:
|
||||
inp_data += 'isLinked: false,'
|
||||
inp_data += f'socketType: "{inp.arm_socket_type}",'
|
||||
inp_data += f'socketValue: {arm.node_utils.haxe_format_socket_val(inp.get_default_value())},'
|
||||
|
||||
inp_data += f'toIndex: {idx}'
|
||||
inp_data += '},'
|
||||
inp_data += ']'
|
||||
|
||||
out_data = '['
|
||||
for idx, out in enumerate(node.outputs):
|
||||
out_data += '['
|
||||
for link in out.links:
|
||||
out_data += '{'
|
||||
if out.is_linked:
|
||||
out_data += 'isLinked: true,'
|
||||
out_data += f'toNode: "{arm.node_utils.get_export_node_name(link.to_node)[1:]}",'
|
||||
out_data += f'toIndex: {arm.node_utils.get_socket_index(link.to_node.inputs, link.to_socket)},'
|
||||
else:
|
||||
out_data += 'isLinked: false,'
|
||||
out_data += f'socketType: "{out.arm_socket_type}",'
|
||||
out_data += f'socketValue: {arm.node_utils.haxe_format_socket_val(out.get_default_value())},'
|
||||
|
||||
out_data += f'fromIndex: {idx}'
|
||||
out_data += '},'
|
||||
out_data += '],'
|
||||
out_data += ']'
|
||||
|
||||
js = f'LivePatch.patchSetNodeLinks("{tree_name}", "{node_name}", {inp_data}, {out_data});'
|
||||
write_patch(js)
|
||||
|
||||
|
||||
def on_operator(operator_id: str):
|
||||
"""As long as bpy.msgbus doesn't listen to changes made by
|
||||
|
|
|
@ -24,6 +24,10 @@ category_items: ODict[str, List['ArmNodeCategory']] = OrderedDict()
|
|||
|
||||
array_nodes = dict()
|
||||
|
||||
# See ArmLogicTreeNode.update()
|
||||
# format: [tree pointer => (num inputs, num input links, num outputs, num output links)]
|
||||
last_node_state: dict[int, tuple[int, int, int, int]] = {}
|
||||
|
||||
|
||||
class ArmLogicTreeNode(bpy.types.Node):
|
||||
arm_category = PKG_AS_CATEGORY
|
||||
|
@ -62,6 +66,40 @@ class ArmLogicTreeNode(bpy.types.Node):
|
|||
def get_tree(self):
|
||||
return self.id_data
|
||||
|
||||
def update(self):
|
||||
"""Called if the node was updated in some way, for example
|
||||
if socket connections change. This callback is not called if
|
||||
socket values were changed.
|
||||
"""
|
||||
def num_connected(sockets):
|
||||
return sum([socket.is_linked for socket in sockets])
|
||||
|
||||
# If a link between sockets is removed, there is currently no
|
||||
# _reliable_ way in the Blender API to check which connection
|
||||
# was removed (*).
|
||||
#
|
||||
# So instead we just check _if_ the number of links or sockets
|
||||
# has changed (the update function is called before and after
|
||||
# each link removal). Because we listen for those updates in
|
||||
# general, we automatically also listen to link creation events,
|
||||
# which is more stable than using the dedicated callback for
|
||||
# that (`insert_link()`), because adding links can remove other
|
||||
# links and we would need to react to that as well.
|
||||
#
|
||||
# (*) https://devtalk.blender.org/t/how-to-detect-which-link-was-deleted-by-user-in-node-editor
|
||||
|
||||
self_id = self.as_pointer()
|
||||
|
||||
current_state = (len(self.inputs), num_connected(self.inputs), len(self.outputs), num_connected(self.outputs))
|
||||
if self_id not in last_node_state:
|
||||
# Lazily initialize the last_node_state dict to also store
|
||||
# state for nodes that already exist in the tree
|
||||
last_node_state[self_id] = current_state
|
||||
|
||||
if last_node_state[self_id] != current_state:
|
||||
arm.live_patch.send_event('ln_update_sockets', self)
|
||||
last_node_state[self_id] = current_state
|
||||
|
||||
def free(self):
|
||||
"""Called before the node is deleted."""
|
||||
arm.live_patch.send_event('ln_delete', self)
|
||||
|
@ -84,7 +122,8 @@ class ArmLogicTreeNode(bpy.types.Node):
|
|||
|
||||
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))
|
||||
# arm.live_patch.send_event('ln_insert_link', (self, link))
|
||||
pass
|
||||
|
||||
def get_replacement_node(self, node_tree: bpy.types.NodeTree):
|
||||
# needs to be overridden by individual node classes with arm_version>1
|
||||
|
|
|
@ -68,6 +68,7 @@ def build_node_tree(node_group: 'arm.nodes_logic.ArmLogicTree'):
|
|||
|
||||
with open(file, 'w', encoding="utf-8") as f:
|
||||
f.write('package ' + pack_path + '.node;\n\n')
|
||||
f.write('@:access(armory.logicnode.LogicNode)')
|
||||
f.write('@:keep class ' + group_name + ' extends armory.logicnode.LogicTree {\n\n')
|
||||
f.write('\tvar functionNodes:Map<String, armory.logicnode.FunctionNode>;\n\n')
|
||||
f.write('\tvar functionOutputNodes:Map<String, armory.logicnode.FunctionOutputNode>;\n\n')
|
||||
|
@ -162,8 +163,12 @@ def build_node(node: bpy.types.Node, f: TextIO) -> Optional[str]:
|
|||
prop = arm.node_utils.haxe_format_prop_value(node, prop_name)
|
||||
f.write('\t\t' + name + '.' + prop_name + ' = ' + prop + ';\n')
|
||||
|
||||
# Avoid unnecessary input/output array resizes
|
||||
f.write(f'\t\t{name}.preallocInputs({len(node.inputs)});\n')
|
||||
f.write(f'\t\t{name}.preallocOutputs({len(node.outputs)});\n')
|
||||
|
||||
# Create inputs
|
||||
for inp in node.inputs:
|
||||
for idx, inp in enumerate(node.inputs):
|
||||
# True if the input is connected to a unlinked reroute
|
||||
# somewhere down the reroute line
|
||||
unconnected = False
|
||||
|
@ -191,34 +196,40 @@ def build_node(node: bpy.types.Node, f: TextIO) -> Optional[str]:
|
|||
for i in range(0, len(n.outputs)):
|
||||
if n.outputs[i] == socket:
|
||||
inp_from = i
|
||||
from_type = socket.arm_socket_type
|
||||
break
|
||||
|
||||
# Not linked -> create node with default values
|
||||
else:
|
||||
inp_name = build_default_node(inp)
|
||||
inp_from = 0
|
||||
from_type = inp.arm_socket_type
|
||||
|
||||
# The input is linked to a reroute, but the reroute is unlinked
|
||||
if unconnected:
|
||||
inp_name = build_default_node(inp)
|
||||
inp_from = 0
|
||||
from_type = inp.arm_socket_type
|
||||
|
||||
# Add input
|
||||
f.write('\t\t' + name + '.addInput(' + inp_name + ', ' + str(inp_from) + ');\n')
|
||||
f.write(f'\t\t{"var __link = " if wrd.arm_live_patch else ""}armory.logicnode.LogicNode.addLink({inp_name}, {name}, {inp_from}, {idx});\n')
|
||||
if wrd.arm_live_patch:
|
||||
to_type = inp.arm_socket_type
|
||||
f.write(f'\t\t__link.fromType = "{from_type}";')
|
||||
f.write(f'\t\t__link.toType = "{to_type}";')
|
||||
f.write(f'\t\t__link.toValue = {arm.node_utils.haxe_format_socket_val(inp.get_default_value())};')
|
||||
|
||||
# Create outputs
|
||||
for out in node.outputs:
|
||||
if out.is_linked:
|
||||
out_name = ''
|
||||
for node in collect_nodes_from_output(out, f):
|
||||
out_name += '[' if len(out_name) == 0 else ', '
|
||||
out_name += node
|
||||
out_name += ']'
|
||||
# Not linked - create node with default values
|
||||
else:
|
||||
out_name = '[' + build_default_node(out) + ']'
|
||||
# Add outputs
|
||||
f.write('\t\t' + name + '.addOutputs(' + out_name + ');\n')
|
||||
for idx, out in enumerate(node.outputs):
|
||||
# Linked outputs are already handled after iterating over inputs
|
||||
# above, so only unconnected outputs are handled here
|
||||
if not out.is_linked:
|
||||
f.write(f'\t\t{"var __link = " if wrd.arm_live_patch else ""}armory.logicnode.LogicNode.addLink({name}, {build_default_node(out)}, {idx}, 0);\n')
|
||||
if wrd.arm_live_patch:
|
||||
out_type = out.arm_socket_type
|
||||
f.write(f'\t\t__link.fromType = "{out_type}";')
|
||||
f.write(f'\t\t__link.toType = "{out_type}";')
|
||||
f.write(f'\t\t__link.toValue = {arm.node_utils.haxe_format_socket_val(out.get_default_value())};')
|
||||
|
||||
return name
|
||||
|
||||
|
|
Loading…
Reference in a new issue