[Bf-blender-cvs] [120ea11] object_nodes: Improved python API for constructing the internal node graph.
Lukas Tönne
noreply at git.blender.org
Wed Dec 2 12:05:56 CET 2015
Commit: 120ea1179e6d3584c34c26b9be7121b22b3a5473
Author: Lukas Tönne
Date: Wed Dec 2 11:29:44 2015 +0100
Branches: object_nodes
https://developer.blender.org/rB120ea1179e6d3584c34c26b9be7121b22b3a5473
Improved python API for constructing the internal node graph.
Socket connections can now be handled consistently through single py objects,
so the compilation code becomes more concise and readable.
===================================================================
M release/scripts/startup/bl_operators/object_nodes.py
M source/blender/blenvm/BVM_api.h
M source/blender/blenvm/compile/bvm_nodegraph.cc
M source/blender/blenvm/compile/bvm_nodegraph.h
M source/blender/blenvm/intern/bvm_api.cc
M source/blender/makesrna/intern/rna_blenvm.c
===================================================================
diff --git a/release/scripts/startup/bl_operators/object_nodes.py b/release/scripts/startup/bl_operators/object_nodes.py
index bb90568..de6939c 100644
--- a/release/scripts/startup/bl_operators/object_nodes.py
+++ b/release/scripts/startup/bl_operators/object_nodes.py
@@ -24,6 +24,7 @@ from bpy.types import Operator, ObjectNode, NodeTree, Node, NodeSocket
from bpy.props import *
from nodeitems_utils import NodeCategory, NodeItem
from mathutils import *
+from collections import OrderedDict
###############################################################################
# Socket Types
@@ -108,79 +109,146 @@ node_categories = [
]
###############################################################################
-# Compiler class for converting nodes
+# Utility dict type that works like RNA collections,
+# i.e. accepts both string keys and integer indices
+class StringDict(OrderedDict):
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ return super().__getitem__(list(super().keys())[key])
+ else:
+ return super().__getitem__(key)
+
+ def __contains__(self, key):
+ if isinstance(key, int):
+ return super().__contains__(list(super().keys())[key])
+ else:
+ return super().__contains__(key)
+
+# Wrapper classes to make constructing node graphs as convenient as possible
+# RNA does not allow collections of temporary (node,socket) pairs,
+# so we use python wrappers to pass them around as a single object
+
+class InputWrapper:
+ def __init__(self, gnode, ginput):
+ self.gnode = gnode
+ self.ginput = ginput
+
+ def link(self, from_output):
+ self.gnode.set_input_link(self.ginput, from_output.gnode, from_output.goutput)
+
+ def set_value(self, value):
+ base_type = self.ginput.typedesc.base_type
+ if base_type == 'FLOAT':
+ self.gnode.set_value_float(self.ginput, value)
+ elif base_type == 'FLOAT3':
+ self.gnode.set_value_float3(self.ginput, value)
+ elif base_type == 'FLOAT4':
+ self.gnode.set_value_float4(self.ginput, value)
+ elif base_type == 'INT':
+ self.gnode.set_value_int(self.ginput, value)
+ elif base_type == 'MATRIX44':
+ self.gnode.set_value_matrix44(self.ginput, value)
+
+class OutputWrapper:
+ def __init__(self, gnode, goutput):
+ self.gnode = gnode
+ self.goutput = goutput
+
+class NodeWrapper:
+ def __init__(self, gnode):
+ self.gnode = gnode
+ self.inputs = StringDict([ (i.name, InputWrapper(self.gnode, i)) for i in self.gnode.inputs ])
+ self.outputs = StringDict([ (o.name, OutputWrapper(self.gnode, o)) for o in self.gnode.outputs ])
+
+# Compiler class for converting nodes
class NodeCompiler:
def __init__(self, context, graph):
self.context = context
self.graph = graph
- self.current_node = None
- self.input_map = dict()
- self.output_map = dict()
+ self.bnode = None
+ self.bnode_inputs = None
+ self.bnode_outputs = None
- def add_node(self, type, name):
+ def add_node(self, type, name=""):
node = self.graph.add_node(type, name)
if node is None:
raise Exception("Can not add node of type %r" % type)
- return node
-
- def map_input(self, index, node, name):
- socket = self.current_node.inputs[index]
- key = (self.current_node, socket)
- if key not in self.input_map:
- pairs = set()
- self.input_map[key] = pairs
- else:
- pairs = self.input_map[key]
- pairs.add((node, name))
-
- if isinstance(socket, bpy.types.NodeSocketFloat):
- node.set_value_float(node.inputs[name], socket.default_value)
- elif isinstance(socket, bpy.types.NodeSocketVector):
- node.set_value_float3(node.inputs[name], socket.default_value)
- elif isinstance(socket, bpy.types.NodeSocketColor):
- node.set_value_float4(node.inputs[name], socket.default_value)
- elif isinstance(socket, bpy.types.NodeSocketInt):
- node.set_value_int(node.inputs[name], socket.default_value)
-
- def map_output(self, index, node, name):
- socket = self.current_node.outputs[index]
- key = (self.current_node, socket)
- if key in self.output_map:
- raise KeyError("Node output {}:{} is already mapped".format(self.current_node.name, socket.name))
- self.output_map[key] = (node, name)
-
- def add_link(self, blink):
- (from_node, from_socket) = self.output_map.get((blink.from_node, blink.from_socket), (None, None))
- to_set = self.input_map.get((blink.to_node, blink.to_socket), set())
- if from_node and from_socket:
- for (to_node, to_socket) in to_set:
- self.graph.add_link(from_node, from_socket, to_node, to_socket)
-
- def add_link_internal(self, from_node, from_name, to_node, to_name):
- self.graph.add_link(from_node, from_name, to_node, to_name)
-
- def set_output(self, name, node, socket):
+ return NodeWrapper(node)
+
+ def graph_output(self, name):
out_node, out_socket = self.graph.get_output(name)
- self.graph.add_link(node, socket, out_node, out_socket)
+ return InputWrapper(out_node, out_node.inputs[out_socket])
+
+ def map_input(self, key, socket):
+ if key not in self.bnode_inputs:
+ raise KeyError("Input %r not found in node %r" % (key, self.bnode))
+ socket.link(self.bnode_inputs[key])
+ def map_output(self, key, socket):
+ if key not in self.bnode_outputs:
+ raise KeyError("Output %r not found in node %r" % (key, self.bnode))
+ self.bnode_outputs[key].link(socket)
class NodeTreeBase():
def bvm_compile(self, context, graph):
+ def socket_type_to_bvm(socket):
+ if isinstance(socket, bpy.types.NodeSocketFloat):
+ return 'FLOAT'
+ elif isinstance(socket, bpy.types.NodeSocketVector):
+ return 'FLOAT3'
+ elif isinstance(socket, bpy.types.NodeSocketColor):
+ return 'FLOAT4'
+ elif isinstance(socket, bpy.types.NodeSocketInt):
+ return 'INT'
+ elif isinstance(socket, bpy.types.GeometrySocket):
+ return 'MESH'
+
comp = NodeCompiler(context, graph)
+ input_map = dict()
+ output_map = dict()
+
for bnode in self.nodes:
if not bnode.is_registered_node_type():
continue
- comp.current_node = bnode
+
+ # proxies for inputs/outputs
+ bnode_inputs = StringDict()
+ for binput in bnode.inputs:
+ itype = socket_type_to_bvm(binput)
+
+ proxy = comp.add_node("PASS_%s" % itype)
+ bnode_inputs[binput.identifier] = proxy.outputs[0]
+ input_map[(bnode, binput)] = proxy.inputs[0]
+
+ if hasattr(binput, "default_value"):
+ proxy.inputs[0].set_value(binput.default_value)
+
+ bnode_outputs = StringDict()
+ for boutput in bnode.outputs:
+ otype = socket_type_to_bvm(boutput)
+
+ proxy = comp.add_node("PASS_%s" % otype)
+ bnode_outputs[boutput.identifier] = proxy.inputs[0]
+ output_map[(bnode, boutput)] = proxy.outputs[0]
+
+ comp.bnode = bnode
+ comp.bnode_inputs = bnode_inputs
+ comp.bnode_outputs = bnode_outputs
bnode.compile(comp)
- comp.current_node = None
+
+ comp.bnode = None
+ comp.bnode_inputs = None
+ comp.bnode_outputs = None
for blink in self.links:
if not blink.is_valid:
continue
- comp.add_link(blink)
+ src = (blink.from_node, blink.from_socket)
+ dst = (blink.to_node, blink.to_socket)
+ input_map[dst].link(output_map[src])
###############################################################################
# Generic Nodes
@@ -200,7 +268,7 @@ class IterationNode(CommonNodeBase, ObjectNode):
def compile(self, compiler):
node = compiler.add_node("ITERATION", self.name)
- compiler.map_output(0, node, "value")
+ compiler.map_output(0, node.outputs[0])
class MathNode(CommonNodeBase, ObjectNode):
'''Math '''
@@ -249,8 +317,8 @@ class MathNode(CommonNodeBase, ObjectNode):
if is_binary:
# binary mode
- compiler.map_input(0, node, "value_a")
- compiler.map_input(1, node, "value_b")
+ compiler.map_input(0, node.inputs[0])
+ compiler.map_input(1, node.inputs[1])
else:
# unary mode
socket_a = self.inputs[0]
@@ -258,11 +326,11 @@ class MathNode(CommonNodeBase, ObjectNode):
linked_a = (not socket_a.hide) and socket_a.is_linked
linked_b = (not socket_a.hide) and socket_a.is_linked
if linked_a or (not linked_b):
- compiler.map_input(0, node, "value")
+ compiler.map_input(0, node.inputs[0])
else:
- compiler.map_input(1, node, "value")
+ compiler.map_input(1, node.inputs[0])
- compiler.map_output(0, node, "value")
+ compiler.map_output(0, node.outputs[0])
class VectorMathNode(CommonNodeBase, ObjectNode):
@@ -301,8 +369,8 @@ class VectorMathNode(CommonNodeBase, ObjectNode):
if is_binary:
# binary node
- compiler.map_input(0, node, "value_a")
- compiler.map_input(1, node, "value_b")
+ compiler.map_input(0, node.inputs[0])
+ compiler.map_input(1, node.inputs[1])
else:
# unary node
socket_a = self.inputs[0]
@@ -310,17 +378,17 @@ class VectorMathNode(CommonNodeBase, ObjectNode):
linked_a = (not socket_a.hide) and socket_a.is_linked
linked_b = (not socket_a.hide) and socket_a.is_linked
if linked_a o
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list