JiNanCementPlantForUnity/Assets/3rdParty/Obi2/Scripts/Actors/ObiActor.cs

459 lines
14 KiB
C#

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Obi{
/**
* Represents a group of related particles. ObiActor does not make
* any assumptions about the relationship between these particles, except that they get allocated
* and released together.
*/
[ExecuteInEditMode]
[DisallowMultipleComponent]
public abstract class ObiActor : MonoBehaviour, IObiSolverClient
{
public class ObiActorSolverArgs : EventArgs{
private ObiSolver solver;
public ObiSolver Solver{
get{return solver;}
}
public ObiActorSolverArgs(ObiSolver solver){
this.solver = solver;
}
}
public enum TetherType{
AnchorToFixed,
Hierarchical
}
public event EventHandler<ObiActorSolverArgs> OnAddedToSolver;
public event EventHandler<ObiActorSolverArgs> OnRemovedFromSolver;
[Range(0,1)]
public float worldVelocityScale = 0; /**< how much does world-space velocity affect the actor. This only applies when the solver has "simulateInLocalSpace" enabled.*/
[HideInInspector] public ObiCollisionMaterial collisionMaterial;
[HideInInspector][NonSerialized] public int[] particleIndices; /**< indices of allocated particles in the solver.*/
protected Dictionary<Oni.ConstraintType,ObiBatchedConstraints> constraints = new Dictionary<Oni.ConstraintType,ObiBatchedConstraints>(); /**< list of constraint components used by this actor.*/
[HideInInspector] public bool[] active; /**< Particle activation status.*/
[HideInInspector] public Vector3[] positions; /**< Particle positions.*/
[HideInInspector] public Vector4[] restPositions; /**< Particle rest positions, used to filter collisions.*/
[HideInInspector] public Vector3[] velocities; /**< Particle velocities.*/
[HideInInspector] public float[] invMasses; /**< Particle inverse masses*/
[HideInInspector] public float[] solidRadii; /**< Particle solid radii (physical radius of each particle)*/
[HideInInspector] public int[] phases; /**< Particle phases.*/
[HideInInspector] public Color[] colors = null; /**< Particle colors (not used by all actors, can be null)*/
[HideInInspector] public int[] deformableTriangles = new int[0]; /**< Indices of deformable triangles (3 per triangle)*/
[NonSerialized] protected int trianglesOffset = 0; /**< Offset of deformable trtiangles in curent solver*/
private bool inSolver = false;
protected bool initializing = false;
[HideInInspector][SerializeField] protected ObiSolver solver;
[HideInInspector][SerializeField] protected bool selfCollisions = false;
[HideInInspector][SerializeField] protected bool initialized = false;
public ObiSolver Solver{
get{return solver;}
set{
if (solver != value){
RemoveFromSolver(null);
solver = value;
}
}
}
public ObiCollisionMaterial CollisionMaterial{
get{return collisionMaterial;}
set{
if (collisionMaterial != value){
collisionMaterial = value;
PushDataToSolver(ParticleData.COLLISION_MATERIAL);
}
}
}
public bool Initializing{
get{return initializing;}
}
public bool Initialized{
get{return initialized;}
}
public bool InSolver{
get{return inSolver;}
}
public virtual bool SelfCollisions{
get{return selfCollisions;}
set{
if (value != selfCollisions){
selfCollisions = value;
UpdateParticlePhases();
}
}
}
public virtual Matrix4x4 ActorLocalToWorldMatrix{
get{
return transform.localToWorldMatrix;
}
}
public virtual Matrix4x4 ActorWorldToLocalMatrix{
get{
return transform.worldToLocalMatrix;
}
}
/**
* If true, it means external forces aren't applied to the particles directly. For instance,
* cloth uses aerodynamic constraints to do so, and fluid uses drag.
*/
public virtual bool UsesCustomExternalForces{
get{return false;}
}
public virtual void Awake(){
}
/**
* Since Awake is not guaranteed to be called before OnEnable, we must add the mesh to the solver here.
*/
public virtual void Start(){
if (Application.isPlaying)
AddToSolver(null);
}
public virtual void OnDestroy(){
RemoveFromSolver(null);
}
public virtual void DestroyRequiredComponents(){
#if UNITY_EDITOR
foreach (ObiBatchedConstraints c in constraints.Values)
GameObject.DestroyImmediate(c);
#endif
}
/**
* Flags all particles allocated by this actor as active or inactive depending on the "active array".
* The solver will then only simulate the active ones.
*/
public virtual void OnEnable(){
// build constraints dictionary:
constraints.Clear();
ObiBatchedConstraints[] constraintComponents = GetComponents<ObiBatchedConstraints>();
foreach(ObiBatchedConstraints c in constraintComponents){
constraints[c.GetConstraintType()] = c;
c.GrabActor();
if (c.isActiveAndEnabled)
c.OnEnable();
}
if (!InSolver) return;
// update active status of all particles in the actor:
solver.UpdateActiveParticles();
}
/**
* Flags all particles allocated by this actor as inactive, so the solver will not include them
* in the simulation. To "teleport" the actor to a new position, disable it and then pull positions
* and velocities from the solver. Move it to the new position, and enable it.
*/
public virtual void OnDisable(){
if (!InSolver) return;
// flag all the actor's particles as disabled:
solver.UpdateActiveParticles();
// pull current position / velocity data from solver:
PullDataFromSolver(ParticleData.POSITIONS | ParticleData.VELOCITIES);
// disable constraints:
foreach (ObiBatchedConstraints c in constraints.Values)
c.OnDisable();
}
/**
* Resets the actor to its original state.
*/
public virtual void ResetActor(){
}
/**
* Updates particle phases in the solver.
*/
public virtual void UpdateParticlePhases(){
if (!InSolver) return;
for(int i = 0; i < phases.Length; i++){
phases[i] = Oni.MakePhase(Oni.GetGroupFromPhase(phases[i]),selfCollisions?Oni.ParticlePhase.SelfCollide:0);
}
PushDataToSolver(ParticleData.PHASES);
}
/**
* Adds this actor to a solver. No simulation will take place for this actor
* unless it has been added to a solver. Returns true if the actor was succesfully added,
* false if it was already added or couldn't add it for any other reason.
*/
public virtual bool AddToSolver(object info){
if (solver != null && !InSolver){
// Allocate particles in the solver:
if (!solver.AddActor(this,positions.Length)){
Debug.LogWarning("Obi: Solver could not allocate enough particles for this actor. Please increase max particles.");
return false;
}
inSolver = true;
// Update particle phases before sending data to the solver, as layers/flags settings might have changed.
UpdateParticlePhases();
// find our offset in the deformable triangles array.
trianglesOffset = Oni.GetDeformableTriangleCount(solver.OniSolver);
// Send deformable triangle indices to the solver:
UpdateDeformableTriangles();
// Send our particle data to the solver:
PushDataToSolver(ParticleData.ALL);
// Add constraints to solver:
foreach (ObiBatchedConstraints c in constraints.Values)
c.AddToSolver(null);
if (OnAddedToSolver != null)
OnAddedToSolver(this,new ObiActorSolverArgs(solver));
return true;
}
return false;
}
public void UpdateDeformableTriangles(){
// Send deformable triangle indices to the solver:
int[] solverTriangles = new int[deformableTriangles.Length];
for (int i = 0; i < deformableTriangles.Length; ++i)
{
solverTriangles[i] = particleIndices[deformableTriangles[i]];
}
Oni.SetDeformableTriangles(solver.OniSolver,solverTriangles,solverTriangles.Length/3,trianglesOffset);
}
/**
* Removes this actor from its current solver, if any.
*/
public virtual bool RemoveFromSolver(object info){
if (solver != null && InSolver){
// remove constraints from solver:
foreach (ObiBatchedConstraints c in constraints.Values)
c.RemoveFromSolver(null);
// remove rest positions:
Vector4[] noRest = {new Vector4(0,0,0,1)};
for (int i = 0; i < particleIndices.Length; ++i){
Oni.SetRestPositions(solver.OniSolver,noRest,1,particleIndices[i]);
}
int index = solver.RemoveActor(this);
particleIndices = null;
// update other actor's triangle offset:
for (int i = index; i < solver.actors.Count; i++){
solver.actors[i].trianglesOffset -= deformableTriangles.Length/3;
}
// remove triangles:
Oni.RemoveDeformableTriangles(solver.OniSolver,deformableTriangles.Length/3,trianglesOffset);
inSolver = false;
if (OnRemovedFromSolver != null)
OnRemovedFromSolver(this,new ObiActorSolverArgs(solver));
return true;
}
return false;
}
/**
* Sends local particle data to the solver.
*/
public virtual void PushDataToSolver(ParticleData data = ParticleData.NONE){
if (!InSolver) return;
Matrix4x4 l2sTransform;
if (Solver.simulateInLocalSpace)
l2sTransform = Solver.transform.worldToLocalMatrix * ActorLocalToWorldMatrix;
else
l2sTransform = ActorLocalToWorldMatrix;
for (int i = 0; i < particleIndices.Length; i++){
int k = particleIndices[i];
if ((data & ParticleData.POSITIONS) != 0 && i < positions.Length)
Oni.SetParticlePositions(solver.OniSolver,new Vector4[]{l2sTransform.MultiplyPoint3x4(positions[i])},1,k);
if ((data & ParticleData.VELOCITIES) != 0 && i < velocities.Length)
Oni.SetParticleVelocities(solver.OniSolver,new Vector4[]{l2sTransform.MultiplyVector(velocities[i])},1,k);
if ((data & ParticleData.INV_MASSES) != 0 && i < invMasses.Length)
Oni.SetParticleInverseMasses(solver.OniSolver,new float[]{invMasses[i]},1,k);
if ((data & ParticleData.SOLID_RADII) != 0 && i < solidRadii.Length)
Oni.SetParticleSolidRadii(solver.OniSolver,new float[]{solidRadii[i]},1,k);
if ((data & ParticleData.PHASES) != 0 && i < phases.Length)
Oni.SetParticlePhases(solver.OniSolver,new int[]{phases[i]},1,k);
if ((data & ParticleData.REST_POSITIONS) != 0 && i < restPositions.Length)
Oni.SetRestPositions(solver.OniSolver,new Vector4[]{restPositions[i]},1,k);
}
if ((data & ParticleData.COLLISION_MATERIAL) != 0){
IntPtr[] materials = new IntPtr[particleIndices.Length];
for (int i = 0; i < particleIndices.Length; i++)
materials[i] = collisionMaterial != null ? collisionMaterial.OniCollisionMaterial : IntPtr.Zero;
Oni.SetCollisionMaterials(solver.OniSolver,materials,particleIndices,particleIndices.Length);
}
if ((data & ParticleData.ACTIVE_STATUS) != 0)
solver.UpdateActiveParticles();
}
/**
* Retrieves particle simulation data from the solver. Common uses are
* retrieving positions and velocities to set the initial status of the simulation,
* or retrieving solver-generated data such as tensions, densities, etc.
*/
public virtual void PullDataFromSolver(ParticleData data = ParticleData.NONE){
if (!InSolver) return;
for (int i = 0; i < particleIndices.Length; i++){
int k = particleIndices[i];
if ((data & ParticleData.POSITIONS) != 0){
Vector4[] ssPosition = {positions[i]};
Oni.GetParticlePositions(solver.OniSolver,ssPosition,1,k);
positions[i] = transform.InverseTransformPoint(ssPosition[0]);
}
if ((data & ParticleData.VELOCITIES) != 0){
Vector4[] ssVelocity = {velocities[i]};
Oni.GetParticleVelocities(solver.OniSolver,ssVelocity,1,k);
velocities[i] = transform.InverseTransformVector(ssVelocity[0]);
}
}
}
/**
* Returns the position of a particle in world space.
* Works both when the actor is managed by a solver and when it isn't.
*/
public Vector3 GetParticlePosition(int index){
if (InSolver)
{
if (!solver)
return Vector2.zero;
return solver.renderablePositions[particleIndices[index]];
}
else
return ActorLocalToWorldMatrix.MultiplyPoint3x4(positions[index]);
}
public virtual bool GenerateTethers(TetherType type){
return true;
}
public void ClearTethers(){
ObiBatchedConstraints tethers;
if (constraints.TryGetValue(Oni.ConstraintType.Tether,out tethers)){
((ObiTetherConstraints)tethers).Clear();
}
}
public virtual void OnSolverPreInterpolation(){
}
/**
* Transforms the position of fixed particles from local space to simulation space and feeds them
* to the solver. This is performed just before performing simulation each frame.
*/
public virtual void OnSolverStepBegin(){
// check if any of the involved transforms has changed since last time:
if (!transform.hasChanged && !Solver.transform.hasChanged)
return;
transform.hasChanged = false;
Solver.transform.hasChanged = false;
// apparently checking whether the actor is enabled or not doesn't take a despreciable amount of time.
bool actorEnabled = this.enabled;
int particleCount = particleIndices.Length;
Vector4[] simulationPosition = {Vector4.zero};
// build local to simulation space transform:
Matrix4x4 l2sTransform;
if (Solver.simulateInLocalSpace)
l2sTransform = Solver.transform.worldToLocalMatrix * ActorLocalToWorldMatrix;
else
l2sTransform = ActorLocalToWorldMatrix;
Matrix4x4 delta = Solver.transform.worldToLocalMatrix * Solver.LastTransform;
// transform particles:
for(int i = 0; i < particleCount; i++){
if (!actorEnabled || invMasses[i] == 0){
simulationPosition[0] = l2sTransform.MultiplyPoint3x4(positions[i]);
Oni.SetParticlePositions(solver.OniSolver,simulationPosition,1,particleIndices[i]);
}else if (Solver.simulateInLocalSpace){
Oni.GetParticlePositions(solver.OniSolver,simulationPosition,1,particleIndices[i]);
simulationPosition[0] = Vector3.Lerp(simulationPosition[0],delta.MultiplyPoint3x4(simulationPosition[0]),worldVelocityScale);
Oni.SetParticlePositions(solver.OniSolver,simulationPosition,1,particleIndices[i]);
}
}
}
public virtual void OnSolverStepEnd(){
}
public virtual void OnSolverFrameBegin(){
}
public virtual void OnSolverFrameEnd(){
}
public virtual bool ReadParticlePropertyFromTexture(Texture2D source,Action<int,Color> onReadProperty){return false;}
}
}