[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