Node Tree Overview
Components and data nodes
There are two different kinds of nodes, based on their input/output sockets, in particle node trees:
Components are nodes that have at least one particle data socket (displayed with a white square instead of circle)
Data nodes have primitive data sockets, which includes integers, floating point numbers, vectors, colors, etc.
Particle sockets define the flow of particles through nodes. Their connections define the order in which operations are applied to the particle system and to which subset of the particles. A particle output is simply a reference to a particle set, which is modified by a node that receives it as input.
Data sockets on the other hand provide additional parameters for components. Many components will have some additional inputs to control their inner workings, to which a data socket can be linked for flexible calculations.
Here are some examples to visualize these concepts:
A very simple particle node tree with just two components. The emitter node creates new particles based on an emission rate (particles per second), which are then rendered as billboards. Numeric input parameters are either constants or animated/driven values here.
Adding forces and collisions to the simulation can be done by either using traditional effectors from scene objects (force fields and colliders) or by adding explicit force and collision nodes. The latter use object references too, but all physical settings are defined in the node tree, e.g. force field strength or collider friction.
Data nodes are a way of calculating parameters more flexibly with a wide range of possible functions and inputs. Here we use a Time node to import the current time and/or frame value into the node tree. Object Data is used to import some object properties, effectively making that object a controller for the particles. Object references can also be used as inputs in node “groups” of self-contained particle effects (commonly called “assets”), to avoid fixed references to other data blocks.
The final value used for emission rate is calculated with a couple of math nodes. In this example the emission rate varies periodically (using the sine function), while the overall strength can be controlled by the object’s Z value (height).
Data flow concepts
An important restriction in data flow trees is that particle outputs can only have one active target at a time, unlike data sockets where an output can be used for multiple inputs at once. The reason for this is that unlike primitive (integer, float, etc.) data, the particle set data is only a reference to the particles, not a full copy of the data. If multiple nodes were to manipulate the same particle data output, the result would depend on the order of execution and thus be ambiguous!
However, this does not mean that a node’s output cannot be linked to several nodes in the tree – just that only one of them can use the data while the other one must be deactivated. The currently active set of nodes is determined by the active output.
Different particle streams can be merged together using a Merge node. This creates a single output stream, so all input streams can use the same components further downstream.
A particle stream can also be split into several parts in a couple of ways. The most common method would be putting particles into one of two disjoint streams based on a boolean (true/false) condition. Another useful split node could be taking a selection value (integer) in addition to the input stream and distribute it among several outputs based on the selector. The counterpart node to this would take several input streams, but only output one of them based on the selection value (comparable to Mux/Demux components)
Most of the data inputs in the above examples are singular values, which do not depend in any way on the particles:
- Numeric constants as convenient inputs to nodes
- Context data, such as the current frame number or overall simulation time
- Scene data, e.g. an object’s position or the world’s sky color
All these have in common that they don’t depend on the particle state, they use the same value for each of the particles. That does not mean these values are actual constants as they can still vary over time by means of keyframe animation, drivers, other simulation systems or simply user input! Things get more interesting (and complicated) when using actual particle variables. These can be common properties like the particle position and velocity, but also custom attributes defined by components or the user.
Reading particle data as part of a data node setup uses an input node like the one on the right. Properties and custom attributes can be added to the outputs one-by-one as needed, for instance only the “Position” and “Age” properties of particles are exposed here. This avoids huge nodes with all possibly used outputs (compare: render layer nodes in compositor). This UI feature also works for scene data nodes, like object or world properties.
Note that the attribute read node does not have a particle stream input! It will always read the data from the current particle state, i.e. depending on which component uses the final result. In the following example both the Gravity and the Force component make use of the data calculated from the particle age – but their results may differ based on their particle input states!
The motivation for this design is to avoid situations where calculation results would have to be stored implicitly to be available in other places of the tree. This would be hard for the artist to detect and control and could lead to unexpected behavior and memory problems. If such intermediate calculation results are needed for some reason, they can be stored in a temporary attribute (read about storing attributes below).
TODO: Should it be possible to also modify core properties, like position or age, this way? This could lead to bad conflicts, so it might really be better to restrict writing to custom attributes.
At the heart of the whole node tree particle system there are two different evaluation modes, namely
Calculate the next iteration of the particle state, based on the previous state.
Generate renderable geometry for a renderer, assign materials, etc.
Each component node in the node tree implements one of these modes.