Simulation Architecture Proposal¶
The purpose of this document is to present my view on the following topic: What is a good high level architecture for simulations in Blender. It is not about individual solvers.
First, what is a simulation? For me, a simulation is any history-dependent "effect". That means, you can't calculate e.g. the state of the scene at frame 100, without knowing the state in frame 99. This property differentiates simulations from keyframe-animation.
So, why do we need a new architecture/framework for simulations in Blender? Currently most simulations are evaluated as part of the modifier stack, although there are completely different than other modifiers. Simulations often involve multiple objects. However, currently some simulation types are very decentralized, so it is hard to get an overview of what simulations are ongoing in any scene. Furthermore, to me at least, simulations feel like a big hack put on top of modifiers, which were not designed to support this kind of behavior. Having simulations as "first-class-citizens" in Blender can simplify code, provide better user interaction and just feels like the right way to go.
Decentralized vs. Centralized¶
A fully decentralized simulation system would be nice. That would mean that you simply give your objects some physical properties, and they will behave and interact correctly. However, that would require a very fast solver, that could simulate everything we need. Unfortunately, such a solver does not exist, and will probably never exist.
In practice we have to setup smaller simulation subsystems and pick the right solver for every problem. I think that defining these small subsystems in a decentralized manner can get very complicated. Especially, because we need some settings and options per subsystem. E.g. the selected solver, the quality and we might also want to bake subsystems separately.
Therefore, it would be better to have a centralized system. In practice that would mean that the user would have to create and manage separate Simulation Worlds.
Every scene in Blender can have zero or more simulation worlds. Every simulation world has a simulation description. This description can be a node tree, or a classical user interface; it does not really matter for this document. The description defines which objects are part of the simulation and how they interact. Any object can be active or passive. An active object might be modified by the simulation, while a passive object will never be modified.
Every object can only be active in at most one simulation world. However, it can be passive in an arbitrary number of worlds.
Every simulation world maintains the current state for the simulation and is responsible for managing caches.
Every simulation world appears as one node in the depsgraph. It depends on the final evaluated state of all the passive objects and on the pre-sim evaluated state of all active objects. When it runs, it generates the post-sim evaluated state of all objects that are active in this simulation.
The evaluation of an object that is not active in any simulation world stays the same as it is now. If an object is active in a simulation, its evaluation procedure changes. Such an object has the following stages:
- Original: As in DNA.
- Pre-Sim: Original object with e.g. some modifiers/functions applied on it.
- Post-Sim: Output of the simulation that belongs to the original object.
- Final: There can be additional modifiers/functions that are applied after the simulation to create the final object.
Variable Object Amounts¶
Usually, all objects the simulation outputs have a 1-to-1 mapping to original objects. However, there are scenarios where this is not the case. Three cases have to be considered:
- The output of the simulation does not contain an object for some active source object.
- An original object is split up into multiple objects.
- A simulated object cannot be mapped to any original object at all.
The first case is relatively easy to handle. When there is no object, the following steps in the depsgraph evaluation are either skipped or performed on a dummy object.
The second case can be handled relatively easily as well. The final evaluation can just be applied to all objects. Or the objects are somehow combined into a single object, but that might be difficult, when they have different types.
The third case is a bit tricky. I can think of two possible solutions.
- The generated objects are owned by the scene or the simulation world that created them. This might require something like a modifier stack for every generated object, or at least for different groups of objects.
- There are empty original objects, that only serve as container for objects that are generated later. I prefer this approach, because it gives generated objects a better place for settings. Furthermore, this way, they can be selected.