Particles are the basic building blocks of Obi. Each particle has the following properties:
Each ObiActor has an array for each of these properties, that contains the rest (initial) data for all the particles of that actor. These arrays are serialized by Unity when you save a prefab or a scene that contains the actor.
Each ObiSolver also has an array for each property, that stores the current data for all particles in use by any actor managed by the solver.
Actor particle indices run from 0 to the amount of particles in that actor. Solver particle indices run from 0 to solver.maxParticles, as they simulate multiple actors. You can map from actor particle indices to solver particle indices using the actor's particleIndices array.
// solver index of the first particle in the actor. int particleSolverIndex = actor.particleIndices[0]; // use it to get the particle's current velocity. Vector3 velocity = solver.velocities[particleSolverIndex];
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 local-space renderable positions/orientations directly:
Vector3 pos = solver.renderablePositions[actor.particleIndices[0]];
Or you can call actor.GetParticlePosition(particleIndex) (respectively actor.GetParticleOrientation(particleIndex)) at any time for any actor managed by the solver, to get a renderable particle position conveniently expressed in world space.
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; Vector3 com = Vector3.zero; float massAccumulator = 0; // To iterate over all particles in an actor, you can use the length of any property array. // They are all the same length. In this case, we use the invMasses array. for (int i = 0; i < actor.invMasses.Length; ++i){ if (actor.invMasses[i] > 0){ massAccumulator += 1.0f / actor.invMasses[i]; com += actor.GetParticlePosition(i) / actor.invMasses[i]; } } com /= massAccumulator; 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 actor's local space. Since we are interested in drawing the velocities in world space, Gizmos.matrix has been set to the actor'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.InSolver) return; Gizmos.color = Color.red; Gizmos.matrix = actor.ActorLocalToWorldMatrix; for (int i = 0; i < actor.velocities.Length; ++i){ int indexInSolver = actor.particleIndices[i]; Gizmos.DrawRay(solver.positions[indexInSolver], solver.velocities[indexInSolver] * 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.InSolver){ for (int i = 0; i < actor.velocities.Length; ++i){ // if the particle is visually close enough to the anchor, fix it. if (Vector3.Distance(actor.GetParticlePosition(i), anchor.position) < anchorRadius){ int indexInSolver = actor.particleIndices[i]; solver.velocities[indexInSolver] = Vector3.zero; solver.invMasses[indexInSolver] = 0; } } } } }