Scripting Particles

Particles are the basic building blocks of Obi. Each particle has the following properties:

Activation flag
A boolean value that indicates if the particle is being updated by the solver or not. Inactive particles are particles that have been optimized out of the simulation, or that have been reserved for later use (when a cloth tears, or a rope increases its length, for instance).
Position
Position of the particles in the actor (in local space).
Rest position
Rest position of the particles in the actor. These are used to ignore inter-particle collisions between particles that were already intersecting in the actor's rest configuration.
Orientation
Orientation of the particles in the actor (in local space).
Rest orientation
Rest orientation of the particles in the actor.
Velocity
Particle velocities (in local space).
Angular velocity
Particle angular velocities (in local space).
Inverse mass
Inverse mass for each particle. An inverse mass of 0 means the particle's mass is infinite, so its position will be unaffected by dynamics (allowing you to override it manually).
Inverse rotational mass
Inverse rotational mass for each particle. An inverse mass of 0 means the particle's rotational mass is infinite, so its orientation will be unaffected by dynamics (allowing you to override it manually).
Principal radii
Particle radius used for collision detection, in each of the ellipsoid principal axis, sorted in descending magnitude.
Phase
Integer value that is used to determine collision phase and particle type (solid or fluid).
Color
Particle color, only used by ObiParticleRenderer and ObiFluidRenderer.

Each ObiSolver has an array for each one of these properties, that stores the current data for all particles in use by any actor managed by the solver. All properties are expressed in the solver's local space.

Actor particle indices run from 0 to the amount of particles in that actor. However, since particle data for an actor might be scattered across the solver arrays, you need to map from actor index to solver index. You can map from actor particle indices to solver particle indices using the actor's solverIndices array.

// solver index of the first particle in the actor.
int solverIndex = actor.solverIndices[0];

// use it to get the particle's current velocity.
Vector3 velocity = solver.velocities[solverIndex];
			

Some examples

Particle positions, orientations and linear/angular velocities are usually the only properties you´ll be interested in retrieving, since all other properties are not modified by the solver during simulation.

The solver will also generate renderable particle positions and orientations at the end of every frame. You can access them like this:

Vector3 pos = solver.renderablePositions[actor.solverIndices[0]];
			

The next example is a component that will calculate the center of mass of an actor as the mass-weighted sum of all particle positions, and render it using OnDrawGizmos()

using UnityEngine;
using Obi;

[RequireComponent(typeof(ObiActor))]
public class ActorCOM : MonoBehaviour {

	ObiActor actor;

	void Awake(){
		actor = GetComponent<ObiActor>();
	}

	void OnDrawGizmos(){

		if (actor == null || !actor.InSolver)
			return;

		Gizmos.color = Color.red;
		Vector4 com = Vector4.zero;
		float massAccumulator = 0;

		// Iterate over all particles in an actor:
		for (int i = 0; i < actor.solverIndices.Length; ++i){

			// retrieve the particle index in the solver:
			int solverIndex = actor.solverIndices[i];

			// look up the inverse mass of this particle:
			float invMass = actor.solver.invMasses[solverIndex];

			// accumulate it:
			if (invMass > 0)
			{
				massAccumulator += 1.0f / invMass;
				com += actor.solver.positions[solverIndex] / invMass;
			}

		}

		com /= massAccumulator;
		actor.solver.t
		Gizmos.DrawWireSphere(com,0.1f);
	}
}
			

Here's a sample script to visualize an actor's particle velocities using OnDrawGizmos(). Positions and velocities are expressed in the solver's local space. Since we are interested in drawing the velocities in world space, Gizmos.matrix has been set to the solver's local-to-world matrix.

using UnityEngine;
using Obi;

[RequireComponent(typeof(ObiActor))]
public class VelocityVisualizer : MonoBehaviour {

	ObiActor actor;

	void Awake(){
		actor = GetComponent<ObiActor>();
	}

	void OnDrawGizmos(){

		if (actor == null || !actor.isLoaded)
			return;

		Gizmos.color = Color.red;
		Gizmos.matrix = actor.solver.transform.localToWorldMatrix;

		for (int i = 0; i < actor.solverIndices.Length; ++i){
			int solverIndex = actor.solverIndices[i];
			Gizmos.DrawRay(solver.positions[solverIndex],
				       solver.velocities[solverIndex] * Time.fixedDeltaTime);
		}
	}
}
			

The following script fixes in place all particles that are near a given anchor transform.

using UnityEngine;
using System.Collections;
using Obi;

[RequireComponent(typeof(ObiActor))]
public class DistanceAnchor : MonoBehaviour {

	ObiActor actor;
	public Transform anchor;
	public float anchorRadius = 0.5f;

	void Awake(){
		actor = GetComponent<ObiActor>();
	}

	void Start(){
		if (actor.isLoaded){
			for (int i = 0; i < actor.velocities.Length; ++i){
					int solverIndex = actor.solverIndices[i];
				// if the particle is visually close enough to the anchor, fix it.
				if (Vector3.Distance(actor.GetParticlePosition(solverIndex), anchor.position) < anchorRadius){
					solver.velocities[solverIndex] = Vector3.zero;
					solver.invMasses[solverIndex] = 0;
				}
			}
		}
	}
}