Design
A proposal for making complex node trees customizable using node groups parameters.
Node assets that are supposed to be used as building blocks require detail knowledge to tweak their internals. Some parameters can be exposed on the modifier level, but extending a system requires a different approach, as will be demonstrated below.
This proposal describes a mechanism to allow users to specify node groups at the highest level of a node asset, which are then used inside the asset without requiring users to touch the asset internals themselves.
Simon Thommes recently published the first set of hair hair assets to be shipped with Blender.
Simon breaks down the hair workflow into separate modifiers. This relies a lot on the fact that modifiers can be applied as a single self-contained step one after the other.
Simulations won't be able to use this organizational trick so easily. That's because simulations contain multiple loops, nested inside each other, and the parts that users would be interested in modifying and swapping out are located within those loops.
Learn More โ
Particle simulation prototype with a large simulation loop (click to enlarge)
The most obvious loop is the simulation stepping itself, sandwiched between the Simulation Input and Simulation Output nodes. This cannot be split into multiple node groups which then each go into their own modifier. In addition to this outer loop there may also be inner loops, depending on the purpose and complexity of the simulation.
In addition to having customizable parts inside loops there may also be initialization steps before or cleanup steps after the loop, which have to match parts inside the loop body.
Let's use particle emitters as a case study. A particle system should support an arbitrary number of emitters. There are many different ways a particle emitter might be implemented:
We can imagine an emitter as a node group that simply outputs a point cloud as geometry. Users could create such a node group quite easily without knowing a lot about the rest of the particle system.
Users should not need to dive deep into some obscure particle system to insert a new emitter. Instead, we want to decouple the declaration of such node groups from their evaluation.
A "function socket" encapsulates a node tree as a data type. It can be passed around a tree like other data types. It is ultimately passed to an Evaluate Function node, which takes the place of a regular node group. In place of the usual node group button it has a Function input socket.
The function can be generated by binding a node tree: If a node asset exposes a Function socket, users can select a node group as a default value. This provides an straightforward way to use node groups as a configuration tool for assets:
What function sockets are not:
The data type of a function socket is a closure: A combination of the node graph and a set of default values for inputs. The closure can be copied and passed through the node graph until it is evaluated.
The inputs and outputs of the Evaluate node are not directly related to any node tree. Users can freely chose which inputs to provide and which outputs are expected from an evaluation.
This means that a node tree can be unsuitable for a given evaluation! Specifically, inputs and outputs of the graph are optional: The Evaluate Function node can specify a subset of the graph inputs and any unspecified graph inputs will use a default or bound value. Likewise any unspecified graph outputs will simply be regarded as unused.
The evaluation node must, however, make sure that any specified outputs are provided by the graph it evaluates. Passing in a graph that does not have all required inputs or outputs constitutes a runtime error.