armory/blender/nodes_logic.py

294 lines
8.5 KiB
Python
Raw Normal View History

2015-10-30 13:23:09 +01:00
import bpy
from bpy.types import NodeTree, Node, NodeSocket
from bpy.props import *
2016-01-26 14:36:55 +01:00
import os
import sys
2015-10-30 13:23:09 +01:00
2016-01-11 21:10:33 +01:00
class CGTree(NodeTree):
2015-11-10 22:07:28 +01:00
'''Logic nodes'''
2016-08-14 21:08:01 +02:00
bl_idname = 'ArmLogicTreeType'
bl_label = 'Logic Node Tree'
2015-10-30 13:23:09 +01:00
bl_icon = 'GAME'
2016-08-14 21:08:01 +02:00
class ArmLogicTreeNode:
2015-10-30 13:23:09 +01:00
@classmethod
def poll(cls, ntree):
2016-08-14 21:08:01 +02:00
return ntree.bl_idname == 'ArmLogicTreeType'
2015-10-30 13:23:09 +01:00
2016-08-14 21:08:01 +02:00
class TransformNode(Node, ArmLogicTreeNode):
'''Transform node'''
2015-10-30 13:23:09 +01:00
bl_idname = 'TransformNodeType'
2016-08-14 21:08:01 +02:00
bl_label = 'Transform'
2015-10-30 13:23:09 +01:00
bl_icon = 'SOUND'
def init(self, context):
self.inputs.new('NodeSocketVector', "Position")
self.inputs.new('NodeSocketVector', "Rotation")
self.inputs.new('NodeSocketVector', "Scale")
2016-08-14 21:08:01 +02:00
self.inputs[-1].default_value = [1.0, 1.0, 1.0]
2015-10-30 13:23:09 +01:00
self.outputs.new('NodeSocketString', "Transform")
2016-08-14 21:08:01 +02:00
class TimeNode(Node, ArmLogicTreeNode):
2015-10-30 13:23:09 +01:00
'''Time node'''
bl_idname = 'TimeNodeType'
bl_label = 'Time'
bl_icon = 'TIME'
def init(self, context):
self.inputs.new('NodeSocketFloat', "Start")
self.inputs.new('NodeSocketFloat', "Stop")
2016-08-14 21:08:01 +02:00
self.inputs[-1].default_value = -1
2015-10-30 13:23:09 +01:00
self.inputs.new('NodeSocketBool', "Enabled")
2016-08-14 21:08:01 +02:00
self.inputs[-1].default_value = True
2015-10-30 13:23:09 +01:00
self.inputs.new('NodeSocketBool', "Loop")
self.inputs.new('NodeSocketBool', "Reflect")
self.outputs.new('NodeSocketFloat', "Time")
2016-08-14 21:08:01 +02:00
class VectorNode(Node, ArmLogicTreeNode):
2015-10-30 13:23:09 +01:00
bl_idname = 'VectorNodeType'
# Label for nice name display
bl_label = 'Vector'
# Icon identifier
bl_icon = 'CURVE_PATH'
def init(self, context):
self.inputs.new('NodeSocketFloat', "X")
self.inputs.new('NodeSocketFloat', "Y")
self.inputs.new('NodeSocketFloat', "Z")
self.outputs.new('NodeSocketVector', "Vector")
2016-08-14 21:08:01 +02:00
class ScaleValueNode(Node, ArmLogicTreeNode):
2015-10-30 13:23:09 +01:00
bl_idname = 'ScaleValueNodeType'
bl_label = 'ScaleValue'
bl_icon = 'CURVE_PATH'
def init(self, context):
self.inputs.new('NodeSocketFloat', "Factor")
2016-08-14 21:08:01 +02:00
self.inputs[-1].default_value = 1.0
2015-10-30 13:23:09 +01:00
self.inputs.new('NodeSocketFloat', "Value")
self.outputs.new('NodeSocketFloat', "Value")
2016-08-14 21:08:01 +02:00
class SineNode(Node, ArmLogicTreeNode):
2015-10-30 13:23:09 +01:00
bl_idname = 'SineNodeType'
bl_label = 'Sine'
bl_icon = 'CURVE_PATH'
def init(self, context):
self.inputs.new('NodeSocketFloat', "Value")
self.outputs.new('NodeSocketFloat', "Value")
2016-08-14 21:08:01 +02:00
class ThisNode(Node, ArmLogicTreeNode):
bl_idname = 'ThisNodeType'
bl_label = 'This'
bl_icon = 'GAME'
2015-10-30 13:23:09 +01:00
2016-08-14 21:08:01 +02:00
def init(self, context):
self.outputs.new('NodeSocketShader', "Target")
class PickerNode(Node, ArmLogicTreeNode):
bl_idname = 'PickerNodeType'
bl_label = 'Picker'
bl_icon = 'GAME'
property0 = StringProperty(name = "Object", default="")
def init(self, context):
self.outputs.new('NodeSocketShader', "Target")
2015-10-30 13:23:09 +01:00
def draw_buttons(self, context, layout):
2016-08-14 21:08:01 +02:00
layout.prop_search(self, "property0", context.scene, "objects", text = "")
2015-10-30 13:23:09 +01:00
2016-08-14 21:08:01 +02:00
class SetTransformNode(Node, ArmLogicTreeNode):
bl_idname = 'SetTransformNodeType'
bl_label = 'Set Transform'
bl_icon = 'GAME'
2015-10-30 13:23:09 +01:00
2016-08-14 21:08:01 +02:00
def init(self, context):
self.inputs.new('NodeSocketShader', "Target")
self.inputs.new('NodeSocketShader', "Transform")
class SetVisibleNode(Node, ArmLogicTreeNode):
bl_idname = 'SetVisibleNodeType'
bl_label = 'Set Visible'
bl_icon = 'GAME'
def init(self, context):
self.inputs.new('NodeSocketShader', "Target")
self.inputs.new('NodeSocketShader', "Bool")
class GreaterThanNode(Node, ArmLogicTreeNode):
bl_idname = 'GreaterThanNodeType'
bl_label = 'Greater Than'
bl_icon = 'GAME'
2015-10-30 13:23:09 +01:00
2016-08-14 21:08:01 +02:00
def init(self, context):
self.inputs.new('NodeSocketFloat', "Value 1")
self.inputs.new('NodeSocketFloat', "Value 2")
self.outputs.new('NodeSocketBool', "Bool")
### Node Categories ###
2015-10-30 13:23:09 +01:00
import nodeitems_utils
from nodeitems_utils import NodeCategory, NodeItem
2016-08-14 21:08:01 +02:00
class ObjectNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'ArmLogicTreeType'
class TypeNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'ArmLogicTreeType'
class MathNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
return context.space_data.tree_type == 'ArmLogicTreeType'
2015-10-30 13:23:09 +01:00
2016-08-14 21:08:01 +02:00
class LogicNodeCategory(NodeCategory):
2015-10-30 13:23:09 +01:00
@classmethod
def poll(cls, context):
2016-08-14 21:08:01 +02:00
return context.space_data.tree_type == 'ArmLogicTreeType'
2015-10-30 13:23:09 +01:00
node_categories = [
2016-08-14 21:08:01 +02:00
ObjectNodeCategory("LOGICTARGETNODES", "Target", items=[
NodeItem("ThisNodeType"),
NodeItem("PickerNodeType"),
NodeItem("SetTransformNodeType"),
NodeItem("SetVisibleNodeType"),
]),
TypeNodeCategory("LOGICTYPENODES", "Type", items=[
2015-10-30 13:23:09 +01:00
NodeItem("TransformNodeType"),
NodeItem("VectorNodeType"),
2016-08-14 21:08:01 +02:00
]),
MathNodeCategory("LOGICMATHNODES", "Math", items=[
NodeItem("TimeNodeType"),
2015-10-30 13:23:09 +01:00
NodeItem("ScaleValueNodeType"),
NodeItem("SineNodeType"),
2016-08-14 21:08:01 +02:00
]),
LogicNodeCategory("LOGICLOGICNODES", "Logic", items=[
NodeItem("GreaterThanNodeType"),
]),
]
2015-10-30 13:23:09 +01:00
def register():
2015-12-07 20:04:23 +01:00
bpy.utils.register_module(__name__)
2015-10-30 13:23:09 +01:00
try:
2016-08-14 21:08:01 +02:00
nodeitems_utils.register_node_categories("ARM_LOGIC_NODES", node_categories)
2015-10-30 13:23:09 +01:00
except:
pass
def unregister():
2016-08-14 21:08:01 +02:00
nodeitems_utils.unregister_node_categories("ARM_LOGIC_NODES")
2015-12-07 20:04:23 +01:00
bpy.utils.unregister_module(__name__)
2016-01-26 14:36:55 +01:00
# Generating node sources
def buildNodeTrees():
s = bpy.data.filepath.split(os.path.sep)
s.pop()
fp = os.path.sep.join(s)
os.chdir(fp)
# Make sure package dir exists
nodes_path = 'Sources/' + bpy.data.worlds['Arm'].ArmProjectPackage.replace(".", "/") + "/node"
2016-02-07 23:03:52 +01:00
if not os.path.exists(nodes_path):
os.makedirs(nodes_path)
2016-01-26 14:36:55 +01:00
# Export node scripts
for node_group in bpy.data.node_groups:
2016-08-14 21:08:01 +02:00
if node_group.bl_idname == 'ArmLogicTreeType': # Build only game trees
2016-02-08 12:03:20 +01:00
node_group.use_fake_user = True # Keep fake references for now
2016-01-26 14:36:55 +01:00
buildNodeTree(node_group)
def buildNodeTree(node_group):
path = 'Sources/' + bpy.data.worlds['Arm'].ArmProjectPackage.replace('.', '/') + '/node/'
2016-08-14 21:08:01 +02:00
node_group_name = node_group.name.replace('.', '_').replace(' ', '')
2016-01-26 14:36:55 +01:00
with open(path + node_group_name + '.hx', 'w') as f:
f.write('package ' + bpy.data.worlds['Arm'].ArmProjectPackage + '.node;\n\n')
2016-08-22 21:56:28 +02:00
f.write('import armory.logicnode.*;\n\n')
2016-07-10 00:51:39 +02:00
f.write('class ' + node_group_name + ' extends armory.trait.internal.NodeExecutor {\n\n')
2016-07-28 13:21:27 +02:00
f.write('\tpublic function new() { super(); notifyOnAdd(add); }\n\n')
2016-01-26 14:36:55 +01:00
f.write('\tfunction add() {\n')
# Make sure root node exists
2016-08-15 23:45:03 +02:00
roots = get_root_nodes(node_group)
2016-08-14 21:08:01 +02:00
created_nodes = []
for rn in roots:
name = '_' + rn.name.replace('.', '_').replace(' ', '')
buildNode(node_group, rn, f, created_nodes)
f.write('\n\t\tstart(' + name + ');\n\n')
2016-01-26 14:36:55 +01:00
f.write('\t}\n')
f.write('}\n')
def buildNode(node_group, node, f, created_nodes):
# Get node name
2016-08-14 21:08:01 +02:00
name = '_' + node.name.replace('.', '_').replace(' ', '')
2016-01-26 14:36:55 +01:00
# Check if node already exists
for n in created_nodes:
if n == name:
return name
# Create node
2016-08-14 21:08:01 +02:00
type = node.name.split(".")[0].replace(' ', '') + "Node"
2016-01-26 14:36:55 +01:00
f.write('\t\tvar ' + name + ' = new ' + type + '();\n')
created_nodes.append(name)
2016-08-14 21:08:01 +02:00
# Properties
if hasattr(node, "property0"):
f.write('\t\t' + name + '.property0 = "' + node.property0 + '";\n')
if hasattr(node, "property1"):
f.write('\t\t' + name + '.property1 = "' + node.property1 + '";\n')
if hasattr(node, "property2"):
f.write('\t\t' + name + '.property2 = "' + node.property2 + '";\n')
if hasattr(node, "property3"):
f.write('\t\t' + name + '.property3 = "' + node.property3 + '";\n')
if hasattr(node, "property4"):
f.write('\t\t' + name + '.property4 = "' + node.property4 + '";\n')
2016-01-26 14:36:55 +01:00
# Create inputs
for inp in node.inputs:
# Is linked - find node
inpname = ''
if inp.is_linked:
n = findNodeByLink(node_group, node, inp)
inpname = buildNode(node_group, n, f, created_nodes)
# Not linked - create node with default values
else:
2016-08-15 23:45:03 +02:00
inpname = build_default_node(inp)
2016-01-26 14:36:55 +01:00
# Add input
f.write('\t\t' + name + '.inputs.push(' + inpname + ');\n')
return name
def findNodeByLink(node_group, to_node, inp):
for link in node_group.links:
if link.to_node == to_node and link.to_socket == inp:
2016-08-14 21:08:01 +02:00
if link.from_node.bl_idname == 'NodeReroute': # Step through reroutes
return findNodeByLink(node_group, link.from_node, link.from_node.inputs[0])
2016-01-26 14:36:55 +01:00
return link.from_node
2016-08-15 23:45:03 +02:00
def get_root_nodes(node_group):
2016-08-14 21:08:01 +02:00
roots = []
2016-01-26 14:36:55 +01:00
for n in node_group.nodes:
2016-08-14 21:08:01 +02:00
if len(n.outputs) == 0: # Assume node with no outputs as roots
roots.append(n)
return roots
2016-01-26 14:36:55 +01:00
2016-08-15 23:45:03 +02:00
def build_default_node(inp):
2016-01-26 14:36:55 +01:00
inpname = ''
if inp.type == "VECTOR":
inpname = 'VectorNode.create(' + str(inp.default_value[0]) + ', ' + str(inp.default_value[1]) + ", " + str(inp.default_value[2]) + ')'
elif inp.type == "VALUE":
inpname = 'FloatNode.create(' + str(inp.default_value) + ')'
elif inp.type == 'BOOLEAN':
inpname = 'BoolNode.create(' + str(inp.default_value).lower() + ')'
return inpname