Obi models everything as a set of particles and constraints. Particles are freely-moving lumps of matter, and constraints are rules that control their behavior.
Each constraint takes a set of particles and (optionally) some information about the "outside" world as input: colliders, rigidbodies, wind, athmospheric pressure, etc. Then it modifies the particles' positions so that they satisfy a certain condition.
For instance, some constraints might try to keep two particles within a certain distance from each other (distance constraints). Other constraints will try to ensure that a particle cannot go inside a collider (collision constraints), or place the particle in a position consistent with the air flow around it (aerodynamic constraints)
Obi uses a simulation paradigm known as position-based dynamics, or PBD for short. In PBD, forces and velocities have a somewhat secondary role in simulation, and positions are used instead.
At the beginng of every simulation step Obi predicts a new, tentative position for each particle, according to its velocity and the simulation's timestep length. This tentative position probably violates many of the constraints: it could be inside a collider, or far away from other particles linked to it trough distance constraints.
So, this position needs to be adjusted so that it meets all conditions imposed by the constraints affecting that particle. By adjusting the tentative position, we are also indirectly adjusting the particle's velocity.
If we repeat this process every timestep -predict tentative position, correct tentative position, advance to corrected position-, we get something like this:
Sometimes, enforcing a constraint can violate another, and this makes it difficult to find a new position that meets all constraints. Obi will try to find a global solution to all constraints in an iterative fashion. With each iteration, we will get a better solution, closer to satisfying all constraints simultaneously.
There's two ways Obi can iterate over all constraints: in a sequential or parallel fashion. In sequential mode, each constraint is evaluated and the resulting adjustments to particle positions immediately applied, before advancing to the next constraint. Because of this, the order in which constraints are iterated has a slight impact on the final result. In parallel mode, all constraints are evaluated in a first pass. Then in a second pass, adjustments for each particle are averaged and applied. Because of this, parallel mode is order-independent, however it approaches the ground-truth solution more slowly.
In the following animations, three particles (A, B and C) generate two collision constraints which are then solved. This all happens during a single simulation step:
Each additional iteration will get your simulation closer to the ground-truth, but will also slightly erode performance. So the amount of iterations acts as a slider between performance -few iterations- and quality -many iterations-.
In most cases, larger simulations (those that have more constraints, like long/high-resolution ropes) need a higher amount of iterations.
An insufficiently high iteration count will almost always manifest as some sort of unwanted softness/stretchiness, depending on which constraints could not be fully satisfied:
Once all iterations for this step have been carried out and the particle position has been adjusted, a new velocity is calculated using position differentiation, and a new simulation step can start. Here's an animation showing the complete process over multiple steps:
A very effective way to reduce the amount of iterations we need to ensure constraints are satisfied is to reduce the simulation's timestep length. This can be accomplished either by increasing the amount of substeps in our solver, or decreasing Unity's fixed timestep (found in ProjectSettings->Time). Intuitively speaking, taking smaller steps when advancing the simulation causes the tentative position calculated at the beginning of each step to be closer to the valid position we start from. This way, we need less iterations to arrive at a new valid position.
Unlike other engines, Obi allows you to set the amount of iterations spent in each type of constraint individually. Each one will affect the simulation in a different way, depending on what the specific type of constraint does, so you can really fine tune your simulation:
Collision constraints try to keep particles outside of colliders. High iteration counts will yield more robust collision detection when there are multiple colllisions per particle.
Collision constraints try to reduce the tangential velocity of particles upon collision. High iteration counts will yield more accurate friction calculations.
Identical to collision constraints, but for when collisions happen between particles.
Identical to friction constraints, but for when collisions happen between particles.
Each distance constraint tries to keep two particles at a fixed distance form each other. These are responsible for the elasticity of cloth and ropes. High iteration counts will allow them to reach higher stiffnesses, so your ropes/cloth will be less stretchy.
A pin constraint will apply forces to a particle and a rigidbody so that they maintain their relative position. They are created and used by dynamic attachments. High iteration counts will reduce the amount of drift at the pin location, making the attachment more robust.
Sticth constraints try to keep 2 particles on top of each other. They are created and used by stitchers. High iteration counts will reduce the amount of drift at the stitch location, making it more robust.
Each volume constraint takes a group of particles positioned at the vertices of a mesh, and tries to maintain the mesh volume. Used to inflate cloth and create balloons. High iteration counts will allow the ballons to reach higher pressures, and keep their shape more easily.
This is the only type of constraint that doesn't have an iteration count. They are always applied only once, that is enough. Used to simulate wind.
Each bend constraint will work on three particles, trying to get them in a straight line. These are used to make cloth and ropes resistant to bending. As with distance constraints, high iteration counts will allow them to reach higher stiffness.
These constraints are used to reduce stretching of cloth, when increasing the amount of distance constraint iterations would be too expensive. Generally 1-4 iterations are enough for tether constraints.
Skin constraints are used to keep skeletally animated cloth close to its skinned shape. They are mostly used for character clothing. Generally 1-2 iterations are enough for skin constraints.
Each density constraint tries to keep the amount of mass in the neighborhood around a particle constant. This will push out particles getting too close (as that would increase the mass) and pull in particles going away (which results in surface tension effects). Used to simulate fluids. High iteration counts will make the fluid more incompressible, so it will behave less like jelly.
Each shape matching constraint records a rest shape for a group of particles, then adjusts their positions so that they maintain this shape as closely as possible. These are used by softbodies.
Stretch/shear constraints adjust the position of a pair of particles along the axis of a reference frame, determined by a rotation quaternion. These are used by rods and bones.
Bend/twist constraints adjust the orientation of a pair of particles to prevent both bending and twisting. These are used by rods and bones.
Chain constraints take a list of particles and try to maintain their total length using a direct, non-iterative solver. These are used by rods.