158 lines
5.7 KiB
Python
Executable file
158 lines
5.7 KiB
Python
Executable file
import os
|
|
import bpy
|
|
import arm.utils
|
|
import arm.log
|
|
|
|
parsed_nodes = []
|
|
parsed_labels = dict()
|
|
|
|
# Generating node sources
|
|
def build_node_trees():
|
|
os.chdir(arm.utils.get_fp())
|
|
|
|
# Make sure package dir exists
|
|
nodes_path = 'Sources/' + arm.utils.safestr(bpy.data.worlds['Arm'].arm_project_package).replace(".", "/") + "/node"
|
|
if not os.path.exists(nodes_path):
|
|
os.makedirs(nodes_path)
|
|
|
|
# Export node scripts
|
|
for node_group in bpy.data.node_groups:
|
|
if node_group.bl_idname == 'ArmLogicTreeType': # Build only logic trees
|
|
node_group.use_fake_user = True # Keep fake references for now
|
|
build_node_tree(node_group)
|
|
|
|
def build_node_tree(node_group):
|
|
global parsed_nodes
|
|
global parsed_labels
|
|
parsed_nodes = []
|
|
parsed_labels = dict()
|
|
root_nodes = get_root_nodes(node_group)
|
|
|
|
pack_path = arm.utils.safestr(bpy.data.worlds['Arm'].arm_project_package)
|
|
path = 'Sources/' + pack_path.replace('.', '/') + '/node/'
|
|
group_name = arm.utils.safesrc(node_group.name)
|
|
file = path + group_name + '.hx'
|
|
|
|
if node_group.is_cached and os.path.isfile(file):
|
|
return
|
|
|
|
with open(file, 'w') as f:
|
|
f.write('package ' + pack_path + '.node;\n\n')
|
|
# f.write('import armory.logicnode.*;\n\n')
|
|
f.write('@:keep class ' + group_name + ' extends armory.logicnode.LogicTree {\n\n')
|
|
f.write('\tpublic function new() { super(); notifyOnAdd(add); }\n\n')
|
|
f.write('\tfunction add() {\n')
|
|
for node in root_nodes:
|
|
build_node(node, f)
|
|
f.write('\t}\n')
|
|
f.write('}\n')
|
|
node_group.is_cached = True
|
|
|
|
def build_node(node, f):
|
|
global parsed_nodes
|
|
global parsed_labels
|
|
|
|
if node.type == 'REROUTE':
|
|
return build_node(node.inputs[0].links[0].from_node, f)
|
|
|
|
# Get node name
|
|
name = '_' + arm.utils.safesrc(node.name)
|
|
|
|
# Link nodes using labels
|
|
if node.label != '':
|
|
if node.label in parsed_labels:
|
|
return parsed_labels[node.label]
|
|
parsed_labels[node.label] = name
|
|
|
|
# Check if node already exists
|
|
if name in parsed_nodes:
|
|
return name
|
|
|
|
parsed_nodes.append(name)
|
|
|
|
# Create node
|
|
node_type = node.bl_idname[2:] # Discard 'LN'TimeNode prefix
|
|
f.write('\t\tvar ' + name + ' = new armory.logicnode.' + node_type + '(this);\n')
|
|
|
|
# Properties
|
|
for i in range(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
|
|
if inp.is_linked:
|
|
n = inp.links[0].from_node
|
|
socket = inp.links[0].from_socket
|
|
inp_name = build_node(n, f)
|
|
for i in range(0, len(n.outputs)):
|
|
if n.outputs[i] == socket:
|
|
inp_from = i
|
|
break
|
|
# Not linked - create node with default values
|
|
else:
|
|
inp_name = build_default_node(inp)
|
|
inp_from = 0
|
|
# Add input
|
|
f.write('\t\t' + name + '.addInput(' + inp_name + ', ' + str(inp_from) + ');\n')
|
|
|
|
# Create outputs
|
|
for out in node.outputs:
|
|
if out.is_linked:
|
|
out_name = ''
|
|
for l in out.links:
|
|
n = l.to_node
|
|
out_name += '[' if len(out_name) == 0 else ', '
|
|
out_name += build_node(n, f)
|
|
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')
|
|
|
|
return name
|
|
|
|
def get_root_nodes(node_group):
|
|
roots = []
|
|
for node in node_group.nodes:
|
|
if node.bl_idname == 'NodeUndefined':
|
|
arm.log.warn('Undefined logic nodes in ' + node_group.name)
|
|
return []
|
|
if node.type == 'FRAME':
|
|
continue
|
|
linked = False
|
|
for out in node.outputs:
|
|
if out.is_linked:
|
|
linked = True
|
|
break
|
|
if not linked: # Assume node with no connected outputs as roots
|
|
roots.append(node)
|
|
return roots
|
|
|
|
def build_default_node(inp):
|
|
inp_name = 'new armory.logicnode.NullNode(this)'
|
|
if inp.bl_idname == 'ArmNodeSocketAction':
|
|
return inp_name
|
|
if inp.bl_idname == 'ArmNodeSocketObject':
|
|
inp_name = 'new armory.logicnode.ObjectNode(this, "' + str(inp.default_value) + '")'
|
|
return inp_name
|
|
if inp.bl_idname == 'ArmNodeSocketAnimAction':
|
|
inp_name = 'new armory.logicnode.StringNode(this, "' + str(inp.default_value) + '")'
|
|
return inp_name
|
|
if inp.type == 'VECTOR':
|
|
inp_name = 'new armory.logicnode.VectorNode(this, ' + str(inp.default_value[0]) + ', ' + str(inp.default_value[1]) + ', ' + str(inp.default_value[2]) + ')'
|
|
elif inp.type == 'RGBA':
|
|
inp_name = 'new armory.logicnode.ColorNode(this, ' + str(inp.default_value[0]) + ', ' + str(inp.default_value[1]) + ', ' + str(inp.default_value[2]) + ', ' + str(inp.default_value[3]) + ')'
|
|
elif inp.type == 'RGB':
|
|
inp_name = 'new armory.logicnode.ColorNode(this, ' + str(inp.default_value[0]) + ', ' + str(inp.default_value[1]) + ', ' + str(inp.default_value[2]) + ')'
|
|
elif inp.type == 'VALUE':
|
|
inp_name = 'new armory.logicnode.FloatNode(this, ' + str(inp.default_value) + ')'
|
|
elif inp.type == 'INT':
|
|
inp_name = 'new armory.logicnode.IntegerNode(this, ' + str(inp.default_value) + ')'
|
|
elif inp.type == 'BOOLEAN':
|
|
inp_name = 'new armory.logicnode.BooleanNode(this, ' + str(inp.default_value).lower() + ')'
|
|
elif inp.type == 'STRING':
|
|
inp_name = 'new armory.logicnode.StringNode(this, "' + str(inp.default_value) + '")'
|
|
return inp_name
|