2426 lines
91 KiB
C#
2426 lines
91 KiB
C#
/**
|
|
\mainpage Obi documentation
|
|
|
|
Introduction:
|
|
-------------
|
|
|
|
Obi is a position-based dynamics framework for unity. It enables the simulation of cloth, ropes and fluid in realtime, complete with two-way
|
|
rigidbody interaction.
|
|
|
|
Features:
|
|
-------------------
|
|
|
|
- Particles can be pinned both in local space and to rigidbodies (kinematic or not).
|
|
- Realistic wind forces.
|
|
- Rigidbodies react to particle dynamics, and particles reach to each other and to rigidbodies too.
|
|
- Easy prefab instantiation, particle-based actors can be translated, scaled and rotated.
|
|
- Custom editor tools.
|
|
|
|
*/
|
|
|
|
using UnityEngine;
|
|
using Unity.Profiling;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Collections;
|
|
|
|
namespace Obi
|
|
{
|
|
|
|
/**
|
|
* ObiSolver simulates particles and constraints, provided by a list of ObiActor. Particles belonging to different solvers won't interact with each other in any way.
|
|
*/
|
|
[AddComponentMenu("Physics/Obi/Obi Solver", 800)]
|
|
[ExecuteInEditMode]
|
|
[DisallowMultipleComponent]
|
|
public sealed class ObiSolver : MonoBehaviour
|
|
{
|
|
static ProfilerMarker m_StateInterpolationPerfMarker = new ProfilerMarker("ApplyStateInterpolation");
|
|
static ProfilerMarker m_UpdateVisibilityPerfMarker = new ProfilerMarker("UpdateVisibility");
|
|
static ProfilerMarker m_GetSolverBoundsPerfMarker = new ProfilerMarker("GetSolverBounds");
|
|
static ProfilerMarker m_TestBoundsPerfMarker = new ProfilerMarker("TestBoundsAgainstCameras");
|
|
static ProfilerMarker m_GetAllCamerasPerfMarker = new ProfilerMarker("GetAllCameras");
|
|
static ProfilerMarker m_PushActiveParticles = new ProfilerMarker("PushActiveParticles");
|
|
static ProfilerMarker m_UpdateColliderWorld = new ProfilerMarker("UpdateColliderWorld");
|
|
static ProfilerMarker m_PushSimplices = new ProfilerMarker("PushSimplices");
|
|
static ProfilerMarker m_PushDeformableEdges = new ProfilerMarker("PushDeformableEdges");
|
|
static ProfilerMarker m_PushDeformableTriangles = new ProfilerMarker("PushDeformableTriangles");
|
|
|
|
public enum BackendType
|
|
{
|
|
[InspectorName("Compute (GPU)")]
|
|
Compute,
|
|
[InspectorName("Burst (CPU)")]
|
|
Burst
|
|
}
|
|
|
|
public enum Synchronization
|
|
{
|
|
Asynchronous,
|
|
Synchronous,
|
|
SynchronousFixed
|
|
}
|
|
|
|
[Serializable]
|
|
public class ParticleInActor
|
|
{
|
|
public ObiActor actor;
|
|
public int indexInActor;
|
|
|
|
public ParticleInActor()
|
|
{
|
|
actor = null;
|
|
indexInActor = -1;
|
|
}
|
|
|
|
public ParticleInActor(ObiActor actor, int indexInActor)
|
|
{
|
|
this.actor = actor;
|
|
this.indexInActor = indexInActor;
|
|
}
|
|
}
|
|
|
|
public class SpatialQuery
|
|
{
|
|
public ObiNativeQueryShapeList shapes;
|
|
public ObiNativeAffineTransformList transforms;
|
|
public ObiNativeQueryResultList results;
|
|
public Action callback;
|
|
public bool synchronous = false;
|
|
|
|
public bool isValid => shapes != null && transforms != null && results != null && shapes.count > 0 && transforms.count > 0;
|
|
public bool done => results.noReadbackInFlight;
|
|
|
|
public SpatialQuery(ObiNativeQueryShapeList shapes, ObiNativeAffineTransformList transforms, ObiNativeQueryResultList results, Action callback = null, bool synchronous = false)
|
|
{
|
|
this.shapes = shapes;
|
|
this.transforms = transforms;
|
|
this.results = results;
|
|
this.callback = callback;
|
|
this.synchronous = synchronous;
|
|
}
|
|
|
|
public void WaitForCompletion()
|
|
{
|
|
results.WaitForReadback();
|
|
}
|
|
}
|
|
|
|
public delegate void SolverCallback(ObiSolver solver);
|
|
public delegate void SolverStepCallback(ObiSolver solver, float timeToSimulate, float substepTime);
|
|
public delegate void CollisionCallback(ObiSolver solver, ObiNativeContactList contacts);
|
|
public delegate void SpatialQueryCallback(ObiSolver solver, ObiNativeQueryResultList results);
|
|
|
|
public event CollisionCallback OnCollision;
|
|
public event CollisionCallback OnParticleCollision;
|
|
public event SpatialQueryCallback OnSpatialQueryResults;
|
|
public event SolverCallback OnAdvection;
|
|
|
|
public event SolverCallback OnInitialize;
|
|
public event SolverCallback OnTeardown;
|
|
public event SolverCallback OnUpdateParameters;
|
|
public event SolverCallback OnParticleCountChanged;
|
|
|
|
public event SolverStepCallback OnSimulationStart; /**< Called at the start of physics simulation, before updating active particles, constraints, etc.*/
|
|
public event SolverCallback OnRequestReadback;
|
|
public event SolverStepCallback OnSimulationEnd; /**< Called at the end of physics simulation.*/
|
|
public event SolverStepCallback OnInterpolate; /**< Called every frame after interpolation, right before updating rendering.*/
|
|
|
|
[Tooltip("If enabled, will force the solver to keep simulating even when not visible from any camera.")]
|
|
public bool simulateWhenInvisible = true;
|
|
|
|
private IObiBackend m_SimulationBackend =
|
|
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
|
new BurstBackend();
|
|
#else
|
|
new NullBackend();
|
|
#endif
|
|
|
|
[SerializeField] private BackendType m_Backend = BackendType.Burst;
|
|
private ObiRenderSystemStack m_RenderSystems = new ObiRenderSystemStack(3);
|
|
|
|
[Min(1)]
|
|
public int substeps = 4;
|
|
|
|
[Min(0)]
|
|
public int maxStepsPerFrame = 1;
|
|
|
|
public Synchronization synchronization = Synchronization.Asynchronous;
|
|
|
|
public Oni.SolverParameters parameters = new Oni.SolverParameters(Oni.SolverParameters.Interpolation.None,
|
|
new Vector4(0, -9.81f, 0, 0));
|
|
|
|
[Min(32)]
|
|
[SerializeField]
|
|
private uint m_MaxSurfaceChunks = 32768;
|
|
public uint maxSurfaceChunks
|
|
{
|
|
set
|
|
{
|
|
// make sure anytime active particles need to be updated, simplices will be updated too:
|
|
m_MaxSurfaceChunks = value;
|
|
dirtyRendering |= (int)Oni.RenderingSystemType.Fluid;
|
|
}
|
|
get { return m_MaxSurfaceChunks; }
|
|
}
|
|
|
|
public uint usedSurfaceChunks
|
|
{
|
|
get {
|
|
var system = GetRenderSystem(Oni.RenderingSystemType.Fluid) as ISurfaceChunkUser;
|
|
if (system == null)
|
|
return 0;
|
|
return system.usedChunkCount;
|
|
}
|
|
}
|
|
|
|
public uint maxQueryResults = 8192;
|
|
public uint maxFoamParticles = 8192;
|
|
public uint maxParticleNeighbors = 128;
|
|
public uint maxParticleContacts = 6;
|
|
|
|
public Vector3 gravity = new Vector3(0, -9.81f, 0);
|
|
public Space gravitySpace = Space.Self;
|
|
|
|
public Vector3 ambientWind = new Vector3(0, 0, 0);
|
|
public Space windSpace = Space.Self;
|
|
|
|
[Min(1)]
|
|
public int foamSubsteps = 1;
|
|
|
|
[Tooltip("Foam particles can stretch along the direction of their velocity. This parameter controls the maximum amount of stretch.")]
|
|
[Range(0, 3)]
|
|
public float maxFoamVelocityStretch = 0.3f;
|
|
|
|
[Tooltip("Determines how foam particles fade in/out during its lifetime.")]
|
|
[MinMax(0, 1)]
|
|
public Vector2 foamFade = new Vector2(0.05f, 0.8f);
|
|
|
|
[Tooltip("Determines the utilization % range in which particles age faster.")]
|
|
[MinMax(0, 1)]
|
|
public Vector2 foamAccelAgingRange = new Vector2(0.5f, 0.8f);
|
|
|
|
[Tooltip("Determines the utilization % range in which particles age faster.")]
|
|
[Min(1)]
|
|
public float foamAccelAging = 4;
|
|
|
|
[Tooltip("How much does world-space linear inertia affect particles in the solver.")]
|
|
[Range(0, 1)]
|
|
public float worldLinearInertiaScale = 0; /**< how much does world-space linear inertia affect particles in the solver*/
|
|
|
|
[Tooltip("How much does world-space angular inertia affect particles in the solver.")]
|
|
[Range(0, 1)]
|
|
public float worldAngularInertiaScale = 0; /**< how much does world-space angular inertia affect particles in the solver.*/
|
|
|
|
[HideInInspector] [NonSerialized] public List<ObiActor> actors = new List<ObiActor>();
|
|
[HideInInspector] [NonSerialized] private ParticleInActor[] m_ParticleToActor;
|
|
|
|
[HideInInspector] [NonSerialized] private Queue<ObiActor> addBuffer = new Queue<ObiActor>(); /**< actors pending insertion into the solver.*/
|
|
|
|
private ObiNativeIntList freeList;
|
|
private Stack<int> freeGroupIDs = new Stack<int>();
|
|
|
|
[NonSerialized] public ObiNativeIntList deformableTriangles;
|
|
[NonSerialized] public ObiNativeIntList deformableEdges;
|
|
[NonSerialized] public ObiNativeVector2List deformableUVs;
|
|
|
|
[NonSerialized] private ObiNativeIntList m_Points; /**< 0-simplices*/
|
|
[NonSerialized] private ObiNativeIntList m_Edges; /**< 1-simplices*/
|
|
[NonSerialized] private ObiNativeIntList m_Triangles; /**< 2-simplices*/
|
|
[NonSerialized] public SimplexCounts m_SimplexCounts;
|
|
|
|
[NonSerialized] private IObiJobHandle simulationHandle;
|
|
[NonSerialized] private Synchronization bufferedSynchronization;
|
|
[NonSerialized] private int steps = 0;
|
|
[NonSerialized] private float substepTime = 0;
|
|
[NonSerialized] private float simulatedTime = 0;
|
|
[NonSerialized] private float accumulatedTime = 0;
|
|
|
|
public float timeSinceSimulationStart { get; private set; } = 0;
|
|
|
|
[HideInInspector] [NonSerialized] public bool dirtyDeformableTriangles = true;
|
|
[HideInInspector] [NonSerialized] public bool dirtyDeformableEdges = true;
|
|
[HideInInspector] [NonSerialized] public Oni.SimplexType dirtySimplices = Oni.SimplexType.All;
|
|
[HideInInspector] [NonSerialized] public int dirtyRendering = 0;
|
|
[HideInInspector] [NonSerialized] public int dirtyConstraints = 0;
|
|
|
|
public bool synchronousSpatialQueries = false;
|
|
|
|
private bool m_dirtyActiveParticles = true;
|
|
public bool dirtyActiveParticles
|
|
{
|
|
set
|
|
{
|
|
m_dirtyActiveParticles = value;
|
|
}
|
|
get { return m_dirtyActiveParticles; }
|
|
}
|
|
|
|
private Bounds m_Bounds = new Bounds();
|
|
private Bounds m_BoundsWS = new Bounds();
|
|
private Plane[] planes = new Plane[6];
|
|
private Camera[] sceneCameras = new Camera[1];
|
|
|
|
// constraints:
|
|
[NonSerialized] private IObiConstraints[] m_Constraints = new IObiConstraints[Oni.ConstraintTypeCount];
|
|
|
|
// constraint parameters:
|
|
public Oni.ConstraintParameters distanceConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Sequential, 1);
|
|
public Oni.ConstraintParameters bendingConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Parallel, 1);
|
|
public Oni.ConstraintParameters particleCollisionConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Sequential, 1);
|
|
public Oni.ConstraintParameters particleFrictionConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Parallel, 1);
|
|
public Oni.ConstraintParameters collisionConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Sequential, 1);
|
|
public Oni.ConstraintParameters frictionConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Parallel, 1);
|
|
public Oni.ConstraintParameters skinConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Sequential, 1);
|
|
public Oni.ConstraintParameters volumeConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Parallel, 1);
|
|
public Oni.ConstraintParameters shapeMatchingConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Parallel, 1);
|
|
public Oni.ConstraintParameters tetherConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Parallel, 1);
|
|
public Oni.ConstraintParameters pinConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Parallel, 1);
|
|
public Oni.ConstraintParameters stitchConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Parallel, 1);
|
|
public Oni.ConstraintParameters densityConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Parallel, 1);
|
|
public Oni.ConstraintParameters stretchShearConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Sequential, 1);
|
|
public Oni.ConstraintParameters bendTwistConstraintParameters = new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Sequential, 1);
|
|
public Oni.ConstraintParameters chainConstraintParameters = new Oni.ConstraintParameters(false, Oni.ConstraintParameters.EvaluationOrder.Sequential, 1);
|
|
|
|
// rigidbodies:
|
|
ObiNativeVector4List m_RigidbodyLinearVelocities;
|
|
ObiNativeVector4List m_RigidbodyAngularVelocities;
|
|
|
|
// colors:
|
|
[NonSerialized] private ObiNativeColorList m_Colors;
|
|
|
|
// cell indices:
|
|
[NonSerialized] private ObiNativeInt4List m_CellCoords;
|
|
|
|
// status:
|
|
[NonSerialized] private ObiNativeIntList m_ActiveParticles;
|
|
[NonSerialized] private ObiNativeIntList m_Simplices;
|
|
|
|
// positions:
|
|
[NonSerialized] private ObiNativeVector4List m_Positions;
|
|
[NonSerialized] private ObiNativeVector4List m_PrevPositions;
|
|
[NonSerialized] private ObiNativeVector4List m_RestPositions;
|
|
|
|
[NonSerialized] private ObiNativeVector4List m_StartPositions;
|
|
[NonSerialized] private ObiNativeVector4List m_EndPositions;
|
|
[NonSerialized] private ObiNativeVector4List m_RenderablePositions;
|
|
|
|
// orientations:
|
|
[NonSerialized] private ObiNativeQuaternionList m_Orientations;
|
|
[NonSerialized] private ObiNativeQuaternionList m_PrevOrientations;
|
|
[NonSerialized] private ObiNativeQuaternionList m_RestOrientations;
|
|
|
|
[NonSerialized] private ObiNativeQuaternionList m_StartOrientations;
|
|
[NonSerialized] private ObiNativeQuaternionList m_EndOrientations;
|
|
[NonSerialized] private ObiNativeQuaternionList m_RenderableOrientations; /**< renderable particle orientations.*/
|
|
|
|
// velocities:
|
|
[NonSerialized] private ObiNativeVector4List m_Velocities;
|
|
[NonSerialized] private ObiNativeVector4List m_AngularVelocities;
|
|
|
|
// masses tensors:
|
|
[NonSerialized] private ObiNativeFloatList m_InvMasses;
|
|
[NonSerialized] private ObiNativeFloatList m_InvRotationalMasses;
|
|
|
|
// external forces:
|
|
[NonSerialized] private ObiNativeVector4List m_ExternalForces;
|
|
[NonSerialized] private ObiNativeVector4List m_ExternalTorques;
|
|
[NonSerialized] private ObiNativeVector4List m_Wind;
|
|
|
|
// deltas:
|
|
[NonSerialized] private ObiNativeVector4List m_PositionDeltas;
|
|
[NonSerialized] private ObiNativeQuaternionList m_OrientationDeltas;
|
|
[NonSerialized] private ObiNativeIntList m_PositionConstraintCounts;
|
|
[NonSerialized] private ObiNativeIntList m_OrientationConstraintCounts;
|
|
|
|
// particle collisions:
|
|
[NonSerialized] private ObiNativeIntList m_CollisionMaterials;
|
|
[NonSerialized] private ObiNativeIntList m_Phases;
|
|
[NonSerialized] private ObiNativeIntList m_Filters;
|
|
|
|
// particle shape:
|
|
[NonSerialized] private ObiNativeVector4List m_PrincipalRadii;
|
|
[NonSerialized] private ObiNativeVector4List m_RenderableRadii;
|
|
[NonSerialized] private ObiNativeVector4List m_Normals;
|
|
|
|
// fluids:
|
|
[NonSerialized] private ObiNativeFloatList m_Life;
|
|
[NonSerialized] private ObiNativeVector4List m_FluidData;
|
|
[NonSerialized] private ObiNativeVector4List m_FluidMaterials; /**< fluidRadius / surfTension / viscosity / vorticity */
|
|
[NonSerialized] private ObiNativeVector4List m_FluidInterface; /**< drag / pressure / buoyancy / miscibility */
|
|
[NonSerialized] private ObiNativeVector4List m_UserData;
|
|
[NonSerialized] private ObiNativeMatrix4x4List m_Anisotropy;
|
|
|
|
// foam particles:
|
|
[NonSerialized] private ObiNativeVector4List m_FoamPositions; /**< xyz = position, w = amount of neighbors*/
|
|
[NonSerialized] private ObiNativeVector4List m_FoamVelocities; /**< xyz = velocity, w = buoyancy*/
|
|
[NonSerialized] private ObiNativeVector4List m_FoamColors;
|
|
[NonSerialized] private ObiNativeVector4List m_FoamAttributes; /**< life, aging rate, size, drag*/
|
|
[NonSerialized] private ObiNativeIntList m_FoamCount;
|
|
|
|
// contacts:
|
|
[NonSerialized] private ObiNativeContactList m_ColliderContacts;
|
|
[NonSerialized] private ObiNativeContactList m_ParticleContacts;
|
|
[NonSerialized] private ObiNativeEffectiveMassesList m_ContactEffectiveMasses;
|
|
[NonSerialized] private ObiNativeEffectiveMassesList m_ParticleContactEffectiveMasses;
|
|
|
|
// queries:
|
|
[NonSerialized] private ObiNativeQueryShapeList m_BufferedQueryShapes;
|
|
[NonSerialized] private ObiNativeAffineTransformList m_BufferedQueryTransforms;
|
|
[NonSerialized] private ObiNativeQueryShapeList m_QueryShapes;
|
|
[NonSerialized] private ObiNativeAffineTransformList m_QueryTransforms;
|
|
[NonSerialized] private ObiNativeQueryResultList m_QueryResults;
|
|
|
|
public ISolverImpl implementation { get; private set; }
|
|
|
|
public bool initialized
|
|
{
|
|
get { return implementation != null; }
|
|
}
|
|
|
|
public IObiBackend simulationBackend
|
|
{
|
|
get { return m_SimulationBackend; }
|
|
}
|
|
|
|
public BackendType backendType
|
|
{
|
|
set
|
|
{
|
|
if (m_Backend != value)
|
|
{
|
|
m_Backend = value;
|
|
UpdateBackend();
|
|
}
|
|
}
|
|
get { return m_Backend; }
|
|
}
|
|
|
|
public SimplexCounts simplexCounts
|
|
{
|
|
get { return m_SimplexCounts; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Solver bounds expressed in world space.
|
|
/// </summary>
|
|
public UnityEngine.Bounds bounds
|
|
{
|
|
get { return m_BoundsWS; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Solver bounds expressed in the solver's local space.
|
|
/// </summary>
|
|
public UnityEngine.Bounds localBounds
|
|
{
|
|
get { return m_Bounds; }
|
|
}
|
|
|
|
public bool isVisible { get; private set; } = true;
|
|
|
|
public float maxScale { get; private set; } = 1;
|
|
|
|
public bool simulationInFlight { get; private set; } = false;
|
|
|
|
public int pendingQueryCount => bufferedQueryShapes.count;
|
|
|
|
public int allocParticleCount
|
|
{
|
|
get { return particleToActor.Count(s => s != null && s.actor != null); }
|
|
}
|
|
|
|
public int activeParticleCount => activeParticles.count;
|
|
|
|
public int contactCount
|
|
{
|
|
get { return (backendType == BackendType.Burst || OnCollision != null) ? colliderContacts.count : 0; }
|
|
}
|
|
|
|
public int particleContactCount
|
|
{
|
|
get { return (backendType == BackendType.Burst || OnParticleCollision != null) ? particleContacts.count : 0; }
|
|
}
|
|
|
|
public ParticleInActor[] particleToActor
|
|
{
|
|
get
|
|
{
|
|
if (m_ParticleToActor == null)
|
|
m_ParticleToActor = new ParticleInActor[0];
|
|
|
|
return m_ParticleToActor;
|
|
}
|
|
}
|
|
|
|
public ObiNativeIntList activeParticles
|
|
{
|
|
get
|
|
{
|
|
if (m_ActiveParticles == null)
|
|
m_ActiveParticles = new ObiNativeIntList();
|
|
|
|
return m_ActiveParticles;
|
|
}
|
|
}
|
|
|
|
#region Simplices
|
|
public ObiNativeIntList simplices
|
|
{
|
|
get
|
|
{
|
|
if (m_Simplices == null)
|
|
m_Simplices = new ObiNativeIntList();
|
|
|
|
return m_Simplices;
|
|
}
|
|
}
|
|
|
|
public ObiNativeIntList points
|
|
{
|
|
get
|
|
{
|
|
if (m_Points == null)
|
|
m_Points = new ObiNativeIntList(8);
|
|
|
|
return m_Points;
|
|
}
|
|
}
|
|
|
|
public ObiNativeIntList edges
|
|
{
|
|
get
|
|
{
|
|
if (m_Edges == null)
|
|
m_Edges = new ObiNativeIntList(8);
|
|
|
|
return m_Edges;
|
|
}
|
|
}
|
|
|
|
public ObiNativeIntList triangles
|
|
{
|
|
get
|
|
{
|
|
if (m_Triangles == null)
|
|
m_Triangles = new ObiNativeIntList(8);
|
|
|
|
return m_Triangles;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Rigidbodies
|
|
public ObiNativeVector4List rigidbodyLinearDeltas
|
|
{
|
|
get
|
|
{
|
|
if (m_RigidbodyLinearVelocities == null)
|
|
{
|
|
m_RigidbodyLinearVelocities = new ObiNativeVector4List();
|
|
}
|
|
return m_RigidbodyLinearVelocities;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List rigidbodyAngularDeltas
|
|
{
|
|
get
|
|
{
|
|
if (m_RigidbodyAngularVelocities == null)
|
|
{
|
|
m_RigidbodyAngularVelocities = new ObiNativeVector4List();
|
|
}
|
|
return m_RigidbodyAngularVelocities;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public ObiNativeColorList colors
|
|
{
|
|
get
|
|
{
|
|
if (m_Colors == null)
|
|
{
|
|
m_Colors = new ObiNativeColorList();
|
|
}
|
|
return m_Colors;
|
|
}
|
|
}
|
|
|
|
public ObiNativeInt4List cellCoords
|
|
{
|
|
get
|
|
{
|
|
if (m_CellCoords == null)
|
|
{
|
|
m_CellCoords = new ObiNativeInt4List(8, 16, new VInt4(int.MaxValue));
|
|
}
|
|
return m_CellCoords;
|
|
}
|
|
}
|
|
|
|
#region Position arrays
|
|
|
|
public ObiNativeVector4List positions
|
|
{
|
|
get
|
|
{
|
|
if (m_Positions == null)
|
|
m_Positions = new ObiNativeVector4List();
|
|
return m_Positions;
|
|
}
|
|
}
|
|
|
|
|
|
public ObiNativeVector4List prevPositions
|
|
{
|
|
get
|
|
{
|
|
if (m_PrevPositions == null)
|
|
m_PrevPositions = new ObiNativeVector4List();
|
|
return m_PrevPositions;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List restPositions
|
|
{
|
|
get
|
|
{
|
|
if (m_RestPositions == null)
|
|
m_RestPositions = new ObiNativeVector4List();
|
|
return m_RestPositions;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List startPositions
|
|
{
|
|
get
|
|
{
|
|
if (m_StartPositions == null)
|
|
m_StartPositions = new ObiNativeVector4List();
|
|
return m_StartPositions;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List endPositions
|
|
{
|
|
get
|
|
{
|
|
if (m_EndPositions == null)
|
|
m_EndPositions = new ObiNativeVector4List();
|
|
return m_EndPositions;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List renderablePositions
|
|
{
|
|
get
|
|
{
|
|
if (m_RenderablePositions == null)
|
|
m_RenderablePositions = new ObiNativeVector4List();
|
|
return m_RenderablePositions;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Orientation arrays
|
|
|
|
public ObiNativeQuaternionList orientations
|
|
{
|
|
get
|
|
{
|
|
if (m_Orientations == null)
|
|
m_Orientations = new ObiNativeQuaternionList();
|
|
return m_Orientations;
|
|
}
|
|
}
|
|
|
|
public ObiNativeQuaternionList prevOrientations
|
|
{
|
|
get
|
|
{
|
|
if (m_PrevOrientations == null)
|
|
m_PrevOrientations = new ObiNativeQuaternionList();
|
|
return m_PrevOrientations;
|
|
}
|
|
}
|
|
|
|
public ObiNativeQuaternionList restOrientations
|
|
{
|
|
get
|
|
{
|
|
if (m_RestOrientations == null)
|
|
m_RestOrientations = new ObiNativeQuaternionList();
|
|
return m_RestOrientations;
|
|
}
|
|
}
|
|
|
|
|
|
public ObiNativeQuaternionList startOrientations
|
|
{
|
|
get
|
|
{
|
|
if (m_StartOrientations == null)
|
|
m_StartOrientations = new ObiNativeQuaternionList();
|
|
return m_StartOrientations;
|
|
}
|
|
}
|
|
|
|
public ObiNativeQuaternionList endOrientations
|
|
{
|
|
get
|
|
{
|
|
if (m_EndOrientations == null)
|
|
m_EndOrientations = new ObiNativeQuaternionList();
|
|
return m_EndOrientations;
|
|
}
|
|
}
|
|
|
|
|
|
public ObiNativeQuaternionList renderableOrientations
|
|
{
|
|
get
|
|
{
|
|
if (m_RenderableOrientations == null)
|
|
m_RenderableOrientations = new ObiNativeQuaternionList();
|
|
return m_RenderableOrientations;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Velocity arrays
|
|
|
|
public ObiNativeVector4List velocities
|
|
{
|
|
get
|
|
{
|
|
if (m_Velocities == null)
|
|
m_Velocities = new ObiNativeVector4List();
|
|
return m_Velocities;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List angularVelocities
|
|
{
|
|
get
|
|
{
|
|
if (m_AngularVelocities == null)
|
|
m_AngularVelocities = new ObiNativeVector4List();
|
|
return m_AngularVelocities;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Mass arrays
|
|
|
|
public ObiNativeFloatList invMasses
|
|
{
|
|
get
|
|
{
|
|
if (m_InvMasses == null)
|
|
m_InvMasses = new ObiNativeFloatList();
|
|
return m_InvMasses;
|
|
}
|
|
}
|
|
|
|
public ObiNativeFloatList invRotationalMasses
|
|
{
|
|
get
|
|
{
|
|
if (m_InvRotationalMasses == null)
|
|
m_InvRotationalMasses = new ObiNativeFloatList();
|
|
return m_InvRotationalMasses;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region External forces
|
|
|
|
public ObiNativeVector4List externalForces
|
|
{
|
|
get
|
|
{
|
|
if (m_ExternalForces == null)
|
|
m_ExternalForces = new ObiNativeVector4List();
|
|
return m_ExternalForces;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List externalTorques
|
|
{
|
|
get
|
|
{
|
|
if (m_ExternalTorques == null)
|
|
m_ExternalTorques = new ObiNativeVector4List();
|
|
return m_ExternalTorques;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List wind
|
|
{
|
|
get
|
|
{
|
|
if (m_Wind == null)
|
|
m_Wind = new ObiNativeVector4List();
|
|
return m_Wind;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Deltas
|
|
|
|
public ObiNativeVector4List positionDeltas
|
|
{
|
|
get
|
|
{
|
|
if (m_PositionDeltas == null)
|
|
m_PositionDeltas = new ObiNativeVector4List();
|
|
return m_PositionDeltas;
|
|
}
|
|
}
|
|
|
|
public ObiNativeQuaternionList orientationDeltas
|
|
{
|
|
get
|
|
{
|
|
if (m_OrientationDeltas == null)
|
|
m_OrientationDeltas = new ObiNativeQuaternionList(8, 16, new Quaternion(0, 0, 0, 0));
|
|
return m_OrientationDeltas;
|
|
}
|
|
}
|
|
|
|
public ObiNativeIntList positionConstraintCounts
|
|
{
|
|
get
|
|
{
|
|
if (m_PositionConstraintCounts == null)
|
|
m_PositionConstraintCounts = new ObiNativeIntList();
|
|
return m_PositionConstraintCounts;
|
|
}
|
|
}
|
|
|
|
public ObiNativeIntList orientationConstraintCounts
|
|
{
|
|
get
|
|
{
|
|
if (m_OrientationConstraintCounts == null)
|
|
m_OrientationConstraintCounts = new ObiNativeIntList();
|
|
return m_OrientationConstraintCounts;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Shape and phase
|
|
|
|
public ObiNativeIntList collisionMaterials
|
|
{
|
|
get
|
|
{
|
|
if (m_CollisionMaterials == null)
|
|
m_CollisionMaterials = new ObiNativeIntList();
|
|
return m_CollisionMaterials;
|
|
}
|
|
}
|
|
|
|
public ObiNativeIntList phases
|
|
{
|
|
get
|
|
{
|
|
if (m_Phases == null)
|
|
m_Phases = new ObiNativeIntList();
|
|
return m_Phases;
|
|
}
|
|
}
|
|
|
|
public ObiNativeIntList filters
|
|
{
|
|
get
|
|
{
|
|
if (m_Filters == null)
|
|
m_Filters = new ObiNativeIntList();
|
|
return m_Filters;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List renderableRadii
|
|
{
|
|
get
|
|
{
|
|
if (m_RenderableRadii == null)
|
|
m_RenderableRadii = new ObiNativeVector4List();
|
|
return m_RenderableRadii;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List principalRadii
|
|
{
|
|
get
|
|
{
|
|
if (m_PrincipalRadii == null)
|
|
m_PrincipalRadii = new ObiNativeVector4List();
|
|
return m_PrincipalRadii;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List normals
|
|
{
|
|
get
|
|
{
|
|
if (m_Normals == null)
|
|
m_Normals = new ObiNativeVector4List();
|
|
return m_Normals;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Fluid properties
|
|
|
|
public ObiNativeFloatList life
|
|
{
|
|
get
|
|
{
|
|
if (m_Life == null)
|
|
m_Life = new ObiNativeFloatList();
|
|
return m_Life;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List fluidData
|
|
{
|
|
get
|
|
{
|
|
if (m_FluidData == null)
|
|
m_FluidData = new ObiNativeVector4List();
|
|
return m_FluidData;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List userData
|
|
{
|
|
get
|
|
{
|
|
if (m_UserData == null)
|
|
m_UserData = new ObiNativeVector4List();
|
|
return m_UserData;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List fluidInterface
|
|
{
|
|
get
|
|
{
|
|
if (m_FluidInterface == null)
|
|
m_FluidInterface = new ObiNativeVector4List();
|
|
return m_FluidInterface;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List fluidMaterials
|
|
{
|
|
get
|
|
{
|
|
if (m_FluidMaterials == null)
|
|
m_FluidMaterials = new ObiNativeVector4List();
|
|
return m_FluidMaterials;
|
|
}
|
|
}
|
|
|
|
public ObiNativeMatrix4x4List anisotropies
|
|
{
|
|
get
|
|
{
|
|
if (m_Anisotropy == null)
|
|
m_Anisotropy = new ObiNativeMatrix4x4List();
|
|
return m_Anisotropy;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List foamPositions
|
|
{
|
|
get
|
|
{
|
|
if (m_FoamPositions == null)
|
|
m_FoamPositions = new ObiNativeVector4List();
|
|
return m_FoamPositions;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List foamVelocities
|
|
{
|
|
get
|
|
{
|
|
if (m_FoamVelocities == null)
|
|
m_FoamVelocities = new ObiNativeVector4List();
|
|
return m_FoamVelocities;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List foamColors
|
|
{
|
|
get
|
|
{
|
|
if (m_FoamColors == null)
|
|
m_FoamColors = new ObiNativeVector4List();
|
|
return m_FoamColors;
|
|
}
|
|
}
|
|
|
|
public ObiNativeVector4List foamAttributes
|
|
{
|
|
get
|
|
{
|
|
if (m_FoamAttributes == null)
|
|
m_FoamAttributes = new ObiNativeVector4List();
|
|
return m_FoamAttributes;
|
|
}
|
|
}
|
|
|
|
public ObiNativeIntList foamCount
|
|
{
|
|
get
|
|
{
|
|
if (m_FoamCount == null)
|
|
{
|
|
m_FoamCount = new ObiNativeIntList();
|
|
m_FoamCount.ResizeUninitialized(9);
|
|
|
|
// post-emission particle dispatch (4 floats), post-update particle dispatch (4 floats),
|
|
// plus 1 extra float for storing currently alive particles while updating/killing.
|
|
m_FoamCount.CopyFrom(new int[] { 0, 1, 1, 0, 0, 1, 1, 0, 0 }, 0, 0, 9);
|
|
}
|
|
return m_FoamCount;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Contacts
|
|
|
|
public ObiNativeContactList colliderContacts
|
|
{
|
|
get
|
|
{
|
|
if (m_ColliderContacts == null)
|
|
m_ColliderContacts = new ObiNativeContactList();
|
|
return m_ColliderContacts;
|
|
}
|
|
}
|
|
|
|
public ObiNativeContactList particleContacts
|
|
{
|
|
get
|
|
{
|
|
if (m_ParticleContacts == null)
|
|
m_ParticleContacts = new ObiNativeContactList();
|
|
return m_ParticleContacts;
|
|
}
|
|
}
|
|
|
|
public ObiNativeEffectiveMassesList contactEffectiveMasses
|
|
{
|
|
get
|
|
{
|
|
if (m_ContactEffectiveMasses == null)
|
|
m_ContactEffectiveMasses = new ObiNativeEffectiveMassesList();
|
|
return m_ContactEffectiveMasses;
|
|
}
|
|
}
|
|
|
|
public ObiNativeEffectiveMassesList particleContactEffectiveMasses
|
|
{
|
|
get
|
|
{
|
|
if (m_ParticleContactEffectiveMasses == null)
|
|
m_ParticleContactEffectiveMasses = new ObiNativeEffectiveMassesList();
|
|
return m_ParticleContactEffectiveMasses;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Queries
|
|
|
|
private ObiNativeQueryShapeList bufferedQueryShapes
|
|
{
|
|
get
|
|
{
|
|
if (m_BufferedQueryShapes == null)
|
|
m_BufferedQueryShapes = new ObiNativeQueryShapeList();
|
|
return m_BufferedQueryShapes;
|
|
}
|
|
}
|
|
|
|
private ObiNativeAffineTransformList bufferedQueryTransforms
|
|
{
|
|
get
|
|
{
|
|
if (m_BufferedQueryTransforms == null)
|
|
m_BufferedQueryTransforms = new ObiNativeAffineTransformList(8);
|
|
return m_BufferedQueryTransforms;
|
|
}
|
|
}
|
|
|
|
private ObiNativeQueryShapeList queryShapes
|
|
{
|
|
get
|
|
{
|
|
if (m_QueryShapes == null)
|
|
m_QueryShapes = new ObiNativeQueryShapeList();
|
|
return m_QueryShapes;
|
|
}
|
|
}
|
|
|
|
private ObiNativeAffineTransformList queryTransforms
|
|
{
|
|
get
|
|
{
|
|
if (m_QueryTransforms == null)
|
|
m_QueryTransforms = new ObiNativeAffineTransformList(8);
|
|
return m_QueryTransforms;
|
|
}
|
|
}
|
|
|
|
public ObiNativeQueryResultList queryResults
|
|
{
|
|
get
|
|
{
|
|
if (m_QueryResults == null)
|
|
m_QueryResults = new ObiNativeQueryResultList();
|
|
return m_QueryResults;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public void OnEnable()
|
|
{
|
|
bufferedSynchronization = synchronization;
|
|
accumulatedTime = 0;
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
// first fixed update this frame:
|
|
if (steps++ == 0)
|
|
{
|
|
// Signal the start of a frame, so we know the world needs to be updated this frame.
|
|
ObiColliderWorld.GetInstance().FrameStart();
|
|
|
|
// Wait for the previous frame's simulation to end and GPU data to be available.
|
|
if (bufferedSynchronization == Synchronization.Asynchronous)
|
|
CompleteSimulation();
|
|
}
|
|
|
|
if (bufferedSynchronization == Synchronization.SynchronousFixed)
|
|
{
|
|
// Update collider world, making sure it will also update after FixedUpdate() for solvers not using fixed sync.
|
|
ObiColliderWorld.GetInstance().UpdateWorld(Time.fixedDeltaTime);
|
|
ObiColliderWorld.GetInstance().FrameStart();
|
|
|
|
// kick off this step's simulation, and immediately wait for it to complete:
|
|
StartSimulation(Time.fixedDeltaTime, 1);
|
|
CompleteSimulation();
|
|
}
|
|
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
// Make sure ObiColliderWorld updates after all solvers have called CompleteSimulation() on their FixedUpdate.
|
|
// This way we can be sure no physics updates are in flight.
|
|
if (steps > 0 && bufferedSynchronization != Synchronization.SynchronousFixed)
|
|
{
|
|
ObiColliderWorld.GetInstance().UpdateWorld(Time.fixedDeltaTime * steps);
|
|
}
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
var scale = transform.lossyScale;
|
|
maxScale = Mathf.Max(Mathf.Max(scale.x, scale.y), scale.z);
|
|
|
|
// Accumulate amount of time to simulate (duration of the frame - time already simulated)
|
|
if (Application.isPlaying)
|
|
accumulatedTime += Time.deltaTime - Time.fixedDeltaTime * steps;
|
|
else
|
|
{
|
|
// if in editor, we don't accumulate any simulation time
|
|
// and just update solver bounds before rendering/simulation.
|
|
accumulatedTime = 0;
|
|
UpdateBounds();
|
|
}
|
|
|
|
if (bufferedSynchronization == Synchronization.Asynchronous ||
|
|
bufferedSynchronization == Synchronization.SynchronousFixed)
|
|
Render(accumulatedTime);
|
|
|
|
// if in play mode, kick off this frame's simulation.
|
|
if (Application.isPlaying && bufferedSynchronization != Synchronization.SynchronousFixed)
|
|
StartSimulation(Time.fixedDeltaTime, steps);
|
|
|
|
if (bufferedSynchronization == Synchronization.Synchronous)
|
|
{
|
|
// if the simulation has been stepped this frame,
|
|
// sychronously wait for completion before rendering.
|
|
if (steps > 0)
|
|
CompleteSimulation();
|
|
|
|
Render(accumulatedTime);
|
|
}
|
|
|
|
// Reset step counter to zero, now that
|
|
// simulation tasks for this frame have been dispatched.
|
|
steps = 0;
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
// Remove all actors from the solver. This will trigger Teardown() when the last actor is removed.
|
|
while (actors.Count > 0)
|
|
RemoveActor(actors[actors.Count - 1]);
|
|
}
|
|
|
|
private void CreateBackend()
|
|
{
|
|
switch (m_Backend)
|
|
{
|
|
|
|
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
|
case BackendType.Burst: m_SimulationBackend = new BurstBackend(); break;
|
|
#endif
|
|
case BackendType.Compute:
|
|
|
|
if (SystemInfo.supportsComputeShaders)
|
|
m_SimulationBackend = new ComputeBackend();
|
|
else
|
|
goto default;
|
|
break;
|
|
|
|
default:
|
|
Debug.LogWarning("The Burst backend depends on the following packages: Mathematics, Collections, Jobs and Burst. Please install the required dependencies. Simulation will fall back to the compute backend, if possible.");
|
|
if (SystemInfo.supportsComputeShaders)
|
|
m_SimulationBackend = new ComputeBackend();
|
|
else
|
|
{
|
|
Debug.LogError("This platform doesn't support compute shaders. Please switch to the Burst backend.");
|
|
m_SimulationBackend = new NullBackend();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void Initialize()
|
|
{
|
|
if (!initialized)
|
|
{
|
|
CreateBackend();
|
|
|
|
substepTime = Time.fixedDeltaTime / substeps;
|
|
|
|
// Set up local actor and particle buffers:
|
|
actors = new List<ObiActor>();
|
|
freeList = new ObiNativeIntList();
|
|
m_ParticleToActor = new ParticleInActor[0];
|
|
|
|
deformableUVs = new ObiNativeVector2List();
|
|
deformableTriangles = new ObiNativeIntList();
|
|
deformableEdges = new ObiNativeIntList();
|
|
|
|
// Create constraints:
|
|
m_Constraints[(int)Oni.ConstraintType.Distance] = new ObiDistanceConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.Bending] = new ObiBendConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.Aerodynamics] = new ObiAerodynamicConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.StretchShear] = new ObiStretchShearConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.BendTwist] = new ObiBendTwistConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.Chain] = new ObiChainConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.ShapeMatching] = new ObiShapeMatchingConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.Volume] = new ObiVolumeConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.Tether] = new ObiTetherConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.Skin] = new ObiSkinConstraintsData();
|
|
m_Constraints[(int)Oni.ConstraintType.Pin] = new ObiPinConstraintsData();
|
|
|
|
// Create the solver:
|
|
implementation = m_SimulationBackend.CreateSolver(this, 0);
|
|
|
|
// Set data arrays:
|
|
implementation.ParticleCountChanged(this);
|
|
implementation.SetRigidbodyArrays(this);
|
|
OnParticleCountChanged?.Invoke(this);
|
|
|
|
// Initialize moving transform:
|
|
InitializeTransformFrame();
|
|
|
|
// Initial collider world update:
|
|
ObiColliderWorld.GetInstance().FrameStart();
|
|
ObiColliderWorld.GetInstance().UpdateWorld(0);
|
|
|
|
OnInitialize?.Invoke(this);
|
|
|
|
// Set initial parameter values:
|
|
PushSolverParameters();
|
|
|
|
#if UNITY_EDITOR
|
|
ObiActorEditorSelectionHandler.SolverInitialized(this);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public void Teardown()
|
|
{
|
|
if (initialized)
|
|
{
|
|
CompleteSimulation();
|
|
|
|
// Clear all constraints:
|
|
PushConstraints();
|
|
|
|
// Destroy the solver:
|
|
m_SimulationBackend.DestroySolver(implementation);
|
|
implementation = null;
|
|
|
|
// Free particle / rigidbody memory:
|
|
FreeParticleArrays();
|
|
FreeRigidbodyArrays();
|
|
|
|
freeList.Dispose();
|
|
|
|
// Reset bounds:
|
|
m_Bounds = new Bounds();
|
|
|
|
OnTeardown?.Invoke(this);
|
|
|
|
#if UNITY_EDITOR
|
|
ObiActorEditorSelectionHandler.SolverTeardown(this);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public void UpdateBackend()
|
|
{
|
|
// remove all actors, this will trigger a teardown:
|
|
List<ObiActor> temp = new List<ObiActor>(actors);
|
|
foreach (ObiActor actor in temp)
|
|
actor.RemoveFromSolver();
|
|
|
|
// re-add all actors.
|
|
// Solver will be re-initialized on adding the first one.
|
|
foreach (ObiActor actor in temp)
|
|
actor.AddToSolver();
|
|
}
|
|
|
|
private void FreeRigidbodyArrays()
|
|
{
|
|
rigidbodyLinearDeltas.Dispose();
|
|
rigidbodyAngularDeltas.Dispose();
|
|
|
|
m_RigidbodyLinearVelocities = null;
|
|
m_RigidbodyAngularVelocities = null;
|
|
}
|
|
|
|
public void EnsureRigidbodyArraysCapacity(int count)
|
|
{
|
|
if (initialized && (count > rigidbodyLinearDeltas.count || !rigidbodyLinearDeltas.isCreated))
|
|
{
|
|
rigidbodyLinearDeltas.ResizeInitialized(count);
|
|
rigidbodyAngularDeltas.ResizeInitialized(count);
|
|
|
|
implementation.SetRigidbodyArrays(this);
|
|
}
|
|
}
|
|
|
|
private void FreeParticleArrays()
|
|
{
|
|
activeParticles.Dispose();
|
|
simplices.Dispose();
|
|
points.Dispose();
|
|
edges.Dispose();
|
|
triangles.Dispose();
|
|
|
|
colors.Dispose();
|
|
cellCoords.Dispose();
|
|
startPositions.Dispose();
|
|
endPositions.Dispose();
|
|
startOrientations.Dispose();
|
|
endOrientations.Dispose();
|
|
positions.Dispose();
|
|
prevPositions.Dispose();
|
|
restPositions.Dispose();
|
|
velocities.Dispose();
|
|
orientations.Dispose();
|
|
prevOrientations.Dispose();
|
|
restOrientations.Dispose();
|
|
angularVelocities.Dispose();
|
|
invMasses.Dispose();
|
|
invRotationalMasses.Dispose();
|
|
principalRadii.Dispose();
|
|
collisionMaterials.Dispose();
|
|
phases.Dispose();
|
|
filters.Dispose();
|
|
renderablePositions.Dispose();
|
|
renderableOrientations.Dispose();
|
|
renderableRadii.Dispose();
|
|
fluidInterface.Dispose();
|
|
fluidMaterials.Dispose();
|
|
foamPositions.Dispose();
|
|
foamVelocities.Dispose();
|
|
foamColors.Dispose();
|
|
foamAttributes.Dispose();
|
|
foamCount.Dispose();
|
|
anisotropies.Dispose();
|
|
life.Dispose();
|
|
fluidData.Dispose();
|
|
userData.Dispose();
|
|
externalForces.Dispose();
|
|
externalTorques.Dispose();
|
|
wind.Dispose();
|
|
positionDeltas.Dispose();
|
|
orientationDeltas.Dispose();
|
|
positionConstraintCounts.Dispose();
|
|
orientationConstraintCounts.Dispose();
|
|
normals.Dispose();
|
|
colliderContacts.Dispose();
|
|
particleContacts.Dispose();
|
|
contactEffectiveMasses.Dispose();
|
|
particleContactEffectiveMasses.Dispose();
|
|
|
|
bufferedQueryShapes.Dispose();
|
|
bufferedQueryTransforms.Dispose();
|
|
queryShapes.Dispose();
|
|
queryTransforms.Dispose();
|
|
queryResults.Dispose();
|
|
|
|
deformableUVs.Dispose();
|
|
deformableTriangles.Dispose();
|
|
deformableEdges.Dispose();
|
|
|
|
m_ActiveParticles = null;
|
|
m_Simplices = null;
|
|
m_Points = null;
|
|
m_Edges = null;
|
|
m_Triangles = null;
|
|
|
|
m_Colors = null;
|
|
m_CellCoords = null;
|
|
m_Positions = null;
|
|
m_RestPositions = null;
|
|
m_PrevPositions = null;
|
|
m_StartPositions = null;
|
|
m_EndPositions = null;
|
|
m_RenderablePositions = null;
|
|
m_Orientations = null;
|
|
m_RestOrientations = null;
|
|
m_PrevOrientations = null;
|
|
m_StartOrientations = null;
|
|
m_EndOrientations = null;
|
|
m_RenderableOrientations = null;
|
|
m_Velocities = null;
|
|
m_AngularVelocities = null;
|
|
m_InvMasses = null;
|
|
m_InvRotationalMasses = null;
|
|
m_ExternalForces = null;
|
|
m_ExternalTorques = null;
|
|
m_Wind = null;
|
|
m_PositionDeltas = null;
|
|
m_OrientationDeltas = null;
|
|
m_PositionConstraintCounts = null;
|
|
m_OrientationConstraintCounts = null;
|
|
m_CollisionMaterials = null;
|
|
m_Phases = null;
|
|
m_Filters = null;
|
|
m_RenderableRadii = null;
|
|
m_PrincipalRadii = null;
|
|
m_Normals = null;
|
|
m_Life = null;
|
|
m_FluidData = null;
|
|
m_UserData = null;
|
|
m_FluidInterface = null;
|
|
m_FluidMaterials = null;
|
|
m_FoamPositions = null;
|
|
m_FoamVelocities = null;
|
|
m_FoamColors = null;
|
|
m_FoamAttributes = null;
|
|
m_FoamCount = null;
|
|
m_Anisotropy = null;
|
|
m_ColliderContacts = null;
|
|
m_ParticleContacts = null;
|
|
m_ContactEffectiveMasses = null;
|
|
m_ParticleContactEffectiveMasses = null;
|
|
|
|
m_BufferedQueryShapes = null;
|
|
m_BufferedQueryTransforms = null;
|
|
m_QueryShapes = null;
|
|
m_QueryTransforms = null;
|
|
m_QueryResults = null;
|
|
|
|
deformableUVs = null;
|
|
deformableTriangles = null;
|
|
deformableEdges = null;
|
|
}
|
|
|
|
private void EnsureParticleArraysCapacity(int count)
|
|
{
|
|
// only resize if the count is larger than the current amount of particles:
|
|
if (count >= positions.count)
|
|
{
|
|
colors.ResizeInitialized(count, Color.white);
|
|
startPositions.ResizeInitialized(count);
|
|
endPositions.ResizeInitialized(count);
|
|
positions.ResizeInitialized(count);
|
|
prevPositions.ResizeInitialized(count);
|
|
restPositions.ResizeInitialized(count);
|
|
startOrientations.ResizeInitialized(count, Quaternion.identity);
|
|
endOrientations.ResizeInitialized(count, Quaternion.identity);
|
|
orientations.ResizeInitialized(count, Quaternion.identity);
|
|
prevOrientations.ResizeInitialized(count, Quaternion.identity);
|
|
restOrientations.ResizeInitialized(count, Quaternion.identity);
|
|
renderablePositions.ResizeInitialized(count);
|
|
renderableOrientations.ResizeInitialized(count, Quaternion.identity);
|
|
velocities.ResizeInitialized(count);
|
|
angularVelocities.ResizeInitialized(count);
|
|
invMasses.ResizeInitialized(count);
|
|
invRotationalMasses.ResizeInitialized(count);
|
|
principalRadii.ResizeInitialized(count);
|
|
collisionMaterials.ResizeInitialized(count);
|
|
phases.ResizeInitialized(count);
|
|
filters.ResizeInitialized(count);
|
|
renderableRadii.ResizeInitialized(count);
|
|
fluidInterface.ResizeInitialized(count);
|
|
fluidMaterials.ResizeInitialized(count);
|
|
anisotropies.ResizeInitialized(count);
|
|
life.ResizeInitialized(count);
|
|
fluidData.ResizeInitialized(count);
|
|
userData.ResizeInitialized(count);
|
|
externalForces.ResizeInitialized(count);
|
|
externalTorques.ResizeInitialized(count);
|
|
wind.ResizeInitialized(count);
|
|
positionDeltas.ResizeInitialized(count);
|
|
orientationDeltas.ResizeInitialized(count, new Quaternion(0, 0, 0, 0));
|
|
positionConstraintCounts.ResizeInitialized(count);
|
|
orientationConstraintCounts.ResizeInitialized(count);
|
|
normals.ResizeInitialized(count);
|
|
}
|
|
|
|
if (count >= m_ParticleToActor.Length)
|
|
{
|
|
Array.Resize(ref m_ParticleToActor, count * 2);
|
|
}
|
|
}
|
|
|
|
private void UpdateFoamParticleCapacity()
|
|
{
|
|
if (maxFoamParticles != foamPositions.count)
|
|
{
|
|
foamPositions.ResizeUninitialized((int)maxFoamParticles);
|
|
foamVelocities.ResizeUninitialized((int)maxFoamParticles);
|
|
foamColors.ResizeUninitialized((int)maxFoamParticles);
|
|
foamAttributes.ResizeUninitialized((int)maxFoamParticles);
|
|
foamCount[3] = Mathf.Min(foamCount[3], (int)maxFoamParticles);
|
|
|
|
implementation.MaxFoamParticleCountChanged(this);
|
|
}
|
|
}
|
|
|
|
private void AllocateParticles(ObiNativeIntList particleIndices)
|
|
{
|
|
|
|
// If attempting to allocate more particles than we have:
|
|
if (particleIndices.count > freeList.count)
|
|
{
|
|
int grow = particleIndices.count - freeList.count;
|
|
|
|
// append new free indices:
|
|
for (int i = 0; i < grow; ++i)
|
|
freeList.Add(positions.count + i);
|
|
|
|
// grow particle arrays:
|
|
EnsureParticleArraysCapacity(positions.count + particleIndices.count);
|
|
}
|
|
|
|
// determine first particle in the free list to use:
|
|
int first = freeList.count - particleIndices.count;
|
|
|
|
// copy free indices to the input array:
|
|
particleIndices.CopyFrom(freeList, first, 0, particleIndices.count);
|
|
|
|
// shorten the free list:
|
|
freeList.ResizeUninitialized(first);
|
|
|
|
}
|
|
|
|
private void FreeParticles(ObiNativeIntList particleIndices)
|
|
{
|
|
freeList.AddRange(particleIndices);
|
|
}
|
|
|
|
private void CollisionCallbacks()
|
|
{
|
|
if (OnCollision != null)
|
|
{
|
|
colliderContacts.WaitForReadback();
|
|
OnCollision.Invoke(this, colliderContacts);
|
|
}
|
|
if (OnParticleCollision != null)
|
|
{
|
|
particleContacts.WaitForReadback();
|
|
OnParticleCollision.Invoke(this, particleContacts);
|
|
}
|
|
if (OnAdvection != null)
|
|
{
|
|
foamPositions.WaitForReadback();
|
|
foamVelocities.WaitForReadback();
|
|
foamAttributes.WaitForReadback();
|
|
foamColors.WaitForReadback();
|
|
foamCount.WaitForReadback();
|
|
|
|
OnAdvection.Invoke(this);
|
|
|
|
|
|
foamPositions.Upload();
|
|
foamVelocities.Upload();
|
|
foamAttributes.Upload();
|
|
foamColors.Upload();
|
|
foamCount.Upload();
|
|
}
|
|
}
|
|
|
|
public void StartSimulation(float stepDelta, int simulationSteps)
|
|
{
|
|
if (simulationSteps > 0)
|
|
{
|
|
// Complete previous simulation call, if any:
|
|
CompleteSimulation();
|
|
|
|
simulatedTime = stepDelta * simulationSteps; // physics time that has been simulated by Unity this frame. Might be more than the time we actually simulate, due to maxStepsPerFrame.
|
|
substepTime = stepDelta / substeps; // duration of a substep.
|
|
|
|
// only update buffered synchronization before starting a new step.
|
|
bufferedSynchronization = synchronization;
|
|
|
|
// AddActor() calls are buffered, new actors should be inserted as this particular point in time:
|
|
while (addBuffer.TryDequeue(out ObiActor actor))
|
|
InsertBufferedActor(actor);
|
|
|
|
if (initialized && maxStepsPerFrame > 0)
|
|
{
|
|
simulationInFlight = true;
|
|
|
|
int frameSubsteps = Mathf.Min(maxStepsPerFrame, simulationSteps) * substeps; // amount of substeps *actually* simulated this frame.
|
|
float timeToSimulate = frameSubsteps * substepTime; // amount of time we need to simulate, might be less than simulatedTime.
|
|
|
|
UpdateFoamParticleCapacity();
|
|
|
|
// Update collision materials/rigidbodies after adding new actors to make sure collision materials are up to date.
|
|
// Also call it before SimulationStart, so that constraints referencing rigidbodies (such as Pin constraints in attachments)
|
|
// use handle data that's up to date.
|
|
using (m_UpdateColliderWorld.Auto())
|
|
{
|
|
ObiColliderWorld.GetInstance().UpdateCollisionMaterials();
|
|
EnsureRigidbodyArraysCapacity(ObiColliderWorld.GetInstance().rigidbodyHandles.Count);
|
|
}
|
|
|
|
// We need SimulationStart to be called before PushConstraints for updating pin constraints.
|
|
OnSimulationStart?.Invoke(this, timeToSimulate, substepTime);
|
|
foreach (ObiActor actor in actors)
|
|
actor.SimulationStart(timeToSimulate, substepTime);
|
|
|
|
// Update the active particles array:
|
|
PushActiveParticles();
|
|
|
|
// Update the simplices array:
|
|
PushSimplices();
|
|
|
|
// Update deformable triangles/edges arrays:
|
|
PushDeformableTriangles();
|
|
PushDeformableEdges();
|
|
|
|
// Update constraint batches:
|
|
PushConstraints();
|
|
|
|
// Update parameters:
|
|
parameters.gravity = gravitySpace == Space.World ? transform.InverseTransformVector(gravity) : gravity;
|
|
parameters.ambientWind = windSpace == Space.World ? transform.InverseTransformVector(ambientWind) : ambientWind;
|
|
implementation.SetParameters(parameters);
|
|
|
|
// Notify render systems that a step has started:
|
|
m_RenderSystems.Step();
|
|
|
|
// CPU -> GPU data transfer
|
|
implementation.PushData();
|
|
|
|
// Update inertial reference frame:
|
|
simulationHandle = UpdateTransformFrame(simulatedTime);
|
|
|
|
// Calculate bounds:
|
|
simulationHandle = implementation.UpdateBounds(simulationHandle, simulatedTime);
|
|
|
|
// Perform collision detection:
|
|
if (simulateWhenInvisible || isVisible)
|
|
{
|
|
simulationHandle = implementation.CollisionDetection(simulationHandle, simulatedTime);
|
|
simulationHandle?.Complete(); // complete here, since several jobs need fluidParticles.Length. TODO: use deferred jobs.
|
|
}
|
|
|
|
// Perform queued queries. This ensures queries "see" the same state as collision callbacks, and ensures no
|
|
// data races (queries performed while the simulation is running).
|
|
FlushSpatialQueries();
|
|
|
|
// Divide each step into multiple substeps:
|
|
float timeLeft = simulatedTime;
|
|
for (int i = 0; i < frameSubsteps; ++i)
|
|
{
|
|
// Only update the solver if it is visible, or if we must simulate even when invisible.
|
|
if ((simulateWhenInvisible || isVisible) && initialized)
|
|
{
|
|
simulationHandle = implementation.Substep(simulationHandle, stepDelta, substepTime, simulationSteps, timeLeft);
|
|
}
|
|
timeLeft -= substepTime;
|
|
}
|
|
|
|
timeSinceSimulationStart += timeToSimulate;
|
|
|
|
// Request GPU data to be brought back to the CPU.
|
|
RequestReadback();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void FlushSpatialQueries()
|
|
{
|
|
while (bufferedQueryShapes.count > 0)
|
|
{
|
|
// copy buffered queries to the lists used for performing the queries:
|
|
queryShapes.ResizeUninitialized(bufferedQueryShapes.count);
|
|
queryTransforms.ResizeUninitialized(bufferedQueryTransforms.count);
|
|
|
|
queryShapes.CopyFrom(bufferedQueryShapes);
|
|
queryTransforms.CopyFrom(bufferedQueryTransforms);
|
|
|
|
bufferedQueryShapes.Clear();
|
|
bufferedQueryTransforms.Clear();
|
|
|
|
implementation.SpatialQuery(queryShapes, queryTransforms, queryResults);
|
|
queryResults.Readback();
|
|
|
|
if (synchronousSpatialQueries)
|
|
{
|
|
// Wait for query results right now and trigger query results event:
|
|
queryResults.WaitForReadback();
|
|
OnSpatialQueryResults?.Invoke(this, queryResults);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CompleteSimulation()
|
|
{
|
|
// if the solver is not yet initialized or there's no previous call to SimulationStart, return.
|
|
if (!initialized || !simulationInFlight)
|
|
return;
|
|
|
|
// Make sure previous simulation call has completed.
|
|
simulationHandle?.Complete();
|
|
|
|
// Update physics state for rendering, and wait for GPU readbacks to finish.
|
|
implementation.FinishSimulation();
|
|
|
|
// Trigger simulation end callback, after GPU readbacks are completed but before query/collision callbacks.
|
|
OnSimulationEnd?.Invoke(this, simulatedTime, substepTime);
|
|
foreach (ObiActor actor in actors)
|
|
actor.SimulationEnd(simulatedTime, substepTime);
|
|
|
|
// Update rigidbody velocities with the simulation results:
|
|
ObiColliderWorld.GetInstance().UpdateRigidbodyVelocities(this);
|
|
|
|
// Trigger spatial query results:
|
|
if (!synchronousSpatialQueries)
|
|
{
|
|
queryResults.WaitForReadback();
|
|
OnSpatialQueryResults?.Invoke(this, queryResults);
|
|
}
|
|
|
|
// Trigger collision callbacks now that GPU data (including rigidbody velocity deltas) is available.
|
|
CollisionCallbacks();
|
|
|
|
simulationInFlight = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Performs physics state interpolation and updates rendering.
|
|
/// </summary>
|
|
/// <param name="unsimulatedTime"> Remaining time that could not be simulated during this frame (in seconds). This is used to interpolate physics state. </param>
|
|
public void Render(float unsimulatedTime)
|
|
{
|
|
if (!initialized)
|
|
return;
|
|
|
|
// Only perform interpolation if the solver is visible, or if we must simulate even when invisible.
|
|
if (simulateWhenInvisible || isVisible)
|
|
{
|
|
using (m_StateInterpolationPerfMarker.Auto())
|
|
{
|
|
// interpolate physics state:
|
|
simulationHandle = implementation.ApplyInterpolation(simulationHandle, startPositions, startOrientations, Time.fixedDeltaTime, unsimulatedTime);
|
|
simulationHandle?.Complete();
|
|
}
|
|
}
|
|
|
|
// test bounds against all cameras to update visibility.
|
|
UpdateVisibility();
|
|
|
|
OnInterpolate?.Invoke(this, simulatedTime, substepTime);
|
|
|
|
foreach (ObiActor actor in actors)
|
|
actor.Interpolate(simulatedTime, substepTime);
|
|
|
|
if (!Application.isPlaying)
|
|
{
|
|
// in-editor, actors update their positions/orientations in Interpolate when transformed,
|
|
// so we must copy them to the GPU:
|
|
positions.Upload();
|
|
orientations.Upload();
|
|
renderablePositions.Upload();
|
|
renderableOrientations.Upload();
|
|
}
|
|
|
|
// Update render systems if dirty:
|
|
if (dirtyRendering != 0)
|
|
{
|
|
m_RenderSystems.Setup(dirtyRendering);
|
|
dirtyRendering = 0;
|
|
}
|
|
|
|
// Only render if visible:
|
|
if (simulateWhenInvisible || isVisible)
|
|
m_RenderSystems.Render();
|
|
}
|
|
|
|
private void UpdateBounds()
|
|
{
|
|
// While in-editor, update active particles and simplices so that
|
|
// solver bounds are correct.
|
|
if (initialized)
|
|
{
|
|
PushActiveParticles();
|
|
PushSimplices();
|
|
|
|
simulationHandle = UpdateTransformFrame(0);
|
|
simulationHandle = implementation.UpdateBounds(simulationHandle, 0);
|
|
simulationHandle?.Complete();
|
|
}
|
|
}
|
|
|
|
private void RequestReadback()
|
|
{
|
|
if (!initialized)
|
|
return;
|
|
|
|
OnRequestReadback?.Invoke(this);
|
|
foreach (ObiActor actor in actors)
|
|
actor.RequestReadback();
|
|
|
|
implementation.RequestReadback();
|
|
|
|
// We must read the entire contacts buffer instead of the amount of contacts the CPU
|
|
// has from last frame, since we need to get both the counter value and the contacts data on the same
|
|
// frame. Alternative would be to read back amount of contacts from last frame, but that
|
|
// means we could find invalid/uninitialized contacts if the amount of contacts decreases from one frame to the next.
|
|
if (OnCollision != null)
|
|
colliderContacts.Readback();
|
|
if (OnParticleCollision != null)
|
|
particleContacts.Readback();
|
|
|
|
if (OnAdvection != null)
|
|
{
|
|
foamPositions.Readback();
|
|
foamVelocities.Readback();
|
|
foamAttributes.Readback();
|
|
foamColors.Readback();
|
|
foamCount.Readback();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an actor to the solver.
|
|
/// </summary>
|
|
/// Attemps to add the actor to this solver returning whether this was successful or not. In case the actor was already added, or had no reference to a blueprint, this operation will return false.
|
|
/// If this was the first actor added to the solver, will attempt to initialize the solver.
|
|
/// While in play mode, if the actor is sucessfully added to the solver, will also call actor.LoadBlueprint().
|
|
/// <param name="actor"> An actor.</param>
|
|
/// <returns>
|
|
/// Whether the actor was sucessfully added.
|
|
/// </returns>
|
|
public bool AddActor(ObiActor actor)
|
|
{
|
|
if (actor == null || actors == null || actor.sourceBlueprint == null || actor.sourceBlueprint.empty || actors.Contains(actor) || addBuffer.Contains(actor))
|
|
return false;
|
|
|
|
// in-editor, we insert actors right away since the simulation is not running,
|
|
// yet we need to perform rendering.
|
|
|
|
if (!Application.isPlaying)
|
|
InsertBufferedActor(actor);
|
|
else
|
|
addBuffer.Enqueue(actor);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to remove an actor from this solver, and returns whether this was sucessful or not.
|
|
/// </summary>
|
|
/// Will only reurn true if the actor had been previously added successfully to this solver.
|
|
/// If the actor is sucessfully removed from the solver, will also call actor.UnloadBlueprint(). Once the last actor is removed from the solver,
|
|
/// this method will attempt to tear down the solver.
|
|
/// <param name="actor"> An actor.</param>
|
|
/// <returns>
|
|
/// Whether the actor was sucessfully removed.
|
|
/// </returns>
|
|
public bool RemoveActor(ObiActor actor)
|
|
{
|
|
if (actor == null)
|
|
return false;
|
|
|
|
// remove from add buffer: TODO: use list instead of queue.
|
|
addBuffer = new Queue<ObiActor>(addBuffer.Where(s => s != actor));
|
|
|
|
// Find actor index in our actors array:
|
|
int index = actors.IndexOf(actor);
|
|
|
|
// If we are in charge of this actor indeed, perform all steps necessary to release it.
|
|
if (index >= 0)
|
|
{
|
|
actor.UnloadBlueprint(this);
|
|
|
|
for (int i = 0; i < actor.solverIndices.count; ++i)
|
|
particleToActor[actor.solverIndices[i]] = null;
|
|
|
|
FreeParticles(actor.solverIndices);
|
|
freeGroupIDs.Push(actor.groupID);
|
|
|
|
actors.RemoveAt(index);
|
|
|
|
actor.solverIndices.Dispose();
|
|
actor.solverIndices = null;
|
|
|
|
for (int i = 0; i < actor.solverBatchOffsets.Length; ++i)
|
|
actor.solverBatchOffsets[i].Clear();
|
|
|
|
// If this was the last actor in the solver, tear it down:
|
|
if (actors.Count == 0)
|
|
Teardown();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void InsertBufferedActor(ObiActor actor)
|
|
{
|
|
if (actor == null)
|
|
return;
|
|
|
|
// If the solver is not initialized yet, do so:
|
|
Initialize();
|
|
|
|
if (actor.solverIndices == null)
|
|
actor.solverIndices = new ObiNativeIntList();
|
|
actor.solverIndices.ResizeUninitialized(actor.sourceBlueprint.particleCount);
|
|
|
|
AllocateParticles(actor.solverIndices);
|
|
|
|
for (int i = 0; i < actor.solverIndices.count; ++i)
|
|
particleToActor[actor.solverIndices[i]] = new ParticleInActor(actor, i);
|
|
|
|
actors.Add(actor);
|
|
|
|
if (freeGroupIDs.Count == 0)
|
|
freeGroupIDs.Push(actors.Count);
|
|
actor.groupID = freeGroupIDs.Pop();
|
|
|
|
actor.LoadBlueprint(this);
|
|
|
|
implementation.ParticleCountChanged(this);
|
|
OnParticleCountChanged?.Invoke(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates solver parameters.
|
|
/// </summary>
|
|
/// Call this after modifying solver or constraint parameters.
|
|
public void PushSolverParameters()
|
|
{
|
|
if (!initialized)
|
|
return;
|
|
|
|
implementation.SetParameters(parameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Distance, ref distanceConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Bending, ref bendingConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.ParticleCollision, ref particleCollisionConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.ParticleFriction, ref particleFrictionConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Collision, ref collisionConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Friction, ref frictionConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Density, ref densityConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Skin, ref skinConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Volume, ref volumeConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.ShapeMatching, ref shapeMatchingConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Tether, ref tetherConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Pin, ref pinConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Stitch, ref stitchConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.StretchShear, ref stretchShearConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.BendTwist, ref bendTwistConstraintParameters);
|
|
|
|
implementation.SetConstraintGroupParameters(Oni.ConstraintType.Chain, ref chainConstraintParameters);
|
|
|
|
if (OnUpdateParameters != null)
|
|
OnUpdateParameters(this);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the parameters used by a given constraint type.
|
|
/// </summary>
|
|
/// If you know the type of the constraints at runtime,
|
|
/// this is the same as directly accessing the appropiate public Oni.ConstraintParameters struct in the solver.
|
|
/// <param name="constraintType"> Type of the constraints whose parameters will be returned by this method.</param>
|
|
/// <returns>
|
|
/// Parameters for the constraints of the specified type.
|
|
/// </returns>
|
|
public Oni.ConstraintParameters GetConstraintParameters(Oni.ConstraintType constraintType)
|
|
{
|
|
switch (constraintType)
|
|
{
|
|
case Oni.ConstraintType.Distance: return distanceConstraintParameters;
|
|
case Oni.ConstraintType.Bending: return bendingConstraintParameters;
|
|
case Oni.ConstraintType.ParticleCollision: return particleCollisionConstraintParameters;
|
|
case Oni.ConstraintType.ParticleFriction: return particleFrictionConstraintParameters;
|
|
case Oni.ConstraintType.Collision: return collisionConstraintParameters;
|
|
case Oni.ConstraintType.Friction: return frictionConstraintParameters;
|
|
case Oni.ConstraintType.Skin: return skinConstraintParameters;
|
|
case Oni.ConstraintType.Volume: return volumeConstraintParameters;
|
|
case Oni.ConstraintType.ShapeMatching: return shapeMatchingConstraintParameters;
|
|
case Oni.ConstraintType.Tether: return tetherConstraintParameters;
|
|
case Oni.ConstraintType.Pin: return pinConstraintParameters;
|
|
case Oni.ConstraintType.Stitch: return stitchConstraintParameters;
|
|
case Oni.ConstraintType.Density: return densityConstraintParameters;
|
|
case Oni.ConstraintType.StretchShear: return stretchShearConstraintParameters;
|
|
case Oni.ConstraintType.BendTwist: return bendTwistConstraintParameters;
|
|
case Oni.ConstraintType.Chain: return chainConstraintParameters;
|
|
|
|
default: return new Oni.ConstraintParameters(true, Oni.ConstraintParameters.EvaluationOrder.Sequential, 1);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the runtime representation of constraints of a given type being simulated by this solver.
|
|
/// </summary>
|
|
/// <param name="type"> Type of the constraints that will be returned by this method.</param>
|
|
/// <returns>
|
|
/// The runtime constraints of the type speficied.
|
|
/// </returns>
|
|
public IObiConstraints GetConstraintsByType(Oni.ConstraintType type)
|
|
{
|
|
int index = (int)type;
|
|
if (m_Constraints != null && index >= 0 && index < m_Constraints.Length)
|
|
return m_Constraints[index];
|
|
return null;
|
|
}
|
|
|
|
private void PushActiveParticles()
|
|
{
|
|
if (dirtyActiveParticles)
|
|
{
|
|
using (m_PushActiveParticles.Auto())
|
|
{
|
|
activeParticles.Clear();
|
|
for (int i = 0; i < actors.Count; ++i)
|
|
{
|
|
if (actors[i].isActiveAndEnabled)
|
|
activeParticles.AddRange(actors[i].solverIndices, actors[i].activeParticleCount);
|
|
}
|
|
|
|
implementation.SetActiveParticles(activeParticles);
|
|
|
|
dirtyActiveParticles = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PushDeformableTriangles()
|
|
{
|
|
if (dirtyDeformableTriangles)
|
|
{
|
|
using (m_PushDeformableTriangles.Auto())
|
|
{
|
|
deformableTriangles.Clear();
|
|
deformableUVs.Clear();
|
|
|
|
for (int i = 0; i < actors.Count; ++i)
|
|
{
|
|
ObiActor currentActor = actors[i];
|
|
if (currentActor.isActiveAndEnabled)
|
|
{
|
|
currentActor.ProvideDeformableTriangles(deformableTriangles, deformableUVs);
|
|
}
|
|
}
|
|
|
|
implementation.SetDeformableTriangles(deformableTriangles, deformableUVs);
|
|
|
|
dirtyDeformableTriangles = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PushDeformableEdges()
|
|
{
|
|
if (dirtyDeformableEdges)
|
|
{
|
|
using (m_PushDeformableEdges.Auto())
|
|
{
|
|
deformableEdges.Clear();
|
|
|
|
for (int i = 0; i < actors.Count; ++i)
|
|
{
|
|
ObiActor currentActor = actors[i];
|
|
if (currentActor.isActiveAndEnabled)
|
|
{
|
|
currentActor.ProvideDeformableEdges(deformableEdges);
|
|
}
|
|
}
|
|
|
|
implementation.SetDeformableEdges(deformableEdges);
|
|
|
|
dirtyDeformableEdges = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PushSimplices()
|
|
{
|
|
|
|
if (dirtySimplices != Oni.SimplexType.None)
|
|
{
|
|
using (m_PushSimplices.Auto())
|
|
{
|
|
simplices.Clear();
|
|
|
|
if ((dirtySimplices & Oni.SimplexType.Point) != 0)
|
|
points.Clear();
|
|
|
|
if ((dirtySimplices & Oni.SimplexType.Edge) != 0)
|
|
edges.Clear();
|
|
|
|
if ((dirtySimplices & Oni.SimplexType.Triangle) != 0)
|
|
triangles.Clear();
|
|
|
|
for (int i = 0; i < actors.Count; ++i)
|
|
{
|
|
var currentActor = actors[i];
|
|
|
|
if (currentActor.isActiveAndEnabled && currentActor.isLoaded)
|
|
{
|
|
//simplex based contacts
|
|
if (currentActor.surfaceCollisions)
|
|
{
|
|
if (currentActor.sharedBlueprint.points != null && (dirtySimplices & Oni.SimplexType.Point) != 0)
|
|
for (int j = 0; j < currentActor.sharedBlueprint.points.Length; ++j)
|
|
{
|
|
int actorIndex = currentActor.sharedBlueprint.points[j];
|
|
|
|
if (actorIndex < currentActor.activeParticleCount)
|
|
points.Add(currentActor.solverIndices[actorIndex]);
|
|
}
|
|
|
|
if (currentActor.sharedBlueprint.edges != null && (dirtySimplices & Oni.SimplexType.Edge) != 0)
|
|
for (int j = 0; j < currentActor.sharedBlueprint.edges.Length / 2; ++j)
|
|
{
|
|
int actorIndex1 = currentActor.sharedBlueprint.edges[j * 2];
|
|
int actorIndex2 = currentActor.sharedBlueprint.edges[j * 2 + 1];
|
|
|
|
if (actorIndex1 < currentActor.activeParticleCount && actorIndex2 < currentActor.activeParticleCount)
|
|
{
|
|
edges.Add(currentActor.solverIndices[actorIndex1]);
|
|
edges.Add(currentActor.solverIndices[actorIndex2]);
|
|
}
|
|
}
|
|
|
|
if (currentActor.sharedBlueprint.triangles != null && (dirtySimplices & Oni.SimplexType.Triangle) != 0)
|
|
for (int j = 0; j < currentActor.sharedBlueprint.triangles.Length / 3; ++j)
|
|
{
|
|
int actorIndex1 = currentActor.sharedBlueprint.triangles[j * 3];
|
|
int actorIndex2 = currentActor.sharedBlueprint.triangles[j * 3 + 1];
|
|
int actorIndex3 = currentActor.sharedBlueprint.triangles[j * 3 + 2];
|
|
|
|
if (actorIndex1 < currentActor.activeParticleCount &&
|
|
actorIndex2 < currentActor.activeParticleCount &&
|
|
actorIndex3 < currentActor.activeParticleCount)
|
|
{
|
|
triangles.Add(currentActor.solverIndices[actorIndex1]);
|
|
triangles.Add(currentActor.solverIndices[actorIndex2]);
|
|
triangles.Add(currentActor.solverIndices[actorIndex3]);
|
|
}
|
|
}
|
|
}
|
|
// particle based contacts
|
|
else if ((dirtySimplices & Oni.SimplexType.Point) != 0)
|
|
{
|
|
// generate a point simplex out of each active particle:
|
|
points.AddRange(currentActor.solverIndices, currentActor.activeParticleCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
simplices.EnsureCapacity(points.count + edges.count + triangles.count);
|
|
simplices.AddRange(triangles);
|
|
simplices.AddRange(edges);
|
|
simplices.AddRange(points);
|
|
|
|
m_SimplexCounts = new SimplexCounts(points.count, edges.count / 2, triangles.count / 3);
|
|
|
|
cellCoords.ResizeInitialized(m_SimplexCounts.simplexCount);
|
|
|
|
implementation.SetSimplices(simplices, m_SimplexCounts);
|
|
|
|
dirtySimplices = Oni.SimplexType.None;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PushConstraints()
|
|
{
|
|
if (dirtyConstraints != 0)
|
|
{
|
|
// Clear all dirty constraints:
|
|
for (int i = 0; i < Oni.ConstraintTypeCount; ++i)
|
|
if (m_Constraints[i] != null && ((1 << i) & dirtyConstraints) != 0)
|
|
m_Constraints[i].Clear();
|
|
|
|
// Iterate over all actors, merging their batches together:
|
|
for (int k = 0; k < actors.Count; ++k)
|
|
{
|
|
if (actors[k].isLoaded)
|
|
{
|
|
for (int i = 0; i < Oni.ConstraintTypeCount; ++i)
|
|
if (m_Constraints[i] != null && ((1 << i) & dirtyConstraints) != 0)
|
|
{
|
|
var constraints = actors[k].GetConstraintsByType((Oni.ConstraintType)i);
|
|
m_Constraints[i].Merge(actors[k], constraints);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Readd the constraints to the solver:
|
|
for (int i = 0; i < Oni.ConstraintTypeCount; ++i)
|
|
if (m_Constraints[i] != null && ((1 << i) & dirtyConstraints) != 0)
|
|
m_Constraints[i].AddToSolver(this);
|
|
|
|
// Reset the dirty flag:
|
|
dirtyConstraints = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates solver bounds, then checks if they're visible from at least one camera. If so, sets isVisible to true, false otherwise.
|
|
*/
|
|
private void UpdateVisibility()
|
|
{
|
|
|
|
using (m_UpdateVisibilityPerfMarker.Auto())
|
|
{
|
|
using (m_GetSolverBoundsPerfMarker.Auto())
|
|
{
|
|
// get bounds in solver space:
|
|
Vector3 min = Vector3.zero, max = Vector3.zero;
|
|
implementation.GetBounds(ref min, ref max);
|
|
m_Bounds.SetMinMax(min, max);
|
|
}
|
|
|
|
if (m_Bounds.AreValid())
|
|
{
|
|
using (m_TestBoundsPerfMarker.Auto())
|
|
{
|
|
// transform bounds to world space:
|
|
m_BoundsWS = m_Bounds.Transform(transform.localToWorldMatrix);
|
|
|
|
using (m_GetAllCamerasPerfMarker.Auto())
|
|
{
|
|
Array.Resize(ref sceneCameras, Camera.allCamerasCount);
|
|
Camera.GetAllCameras(sceneCameras);
|
|
}
|
|
|
|
foreach (Camera cam in sceneCameras)
|
|
{
|
|
GeometryUtility.CalculateFrustumPlanes(cam, planes);
|
|
if (GeometryUtility.TestPlanesAABB(planes, m_BoundsWS))
|
|
{
|
|
if (!isVisible)
|
|
{
|
|
isVisible = true;
|
|
foreach (ObiActor actor in actors)
|
|
actor.OnSolverVisibilityChanged(isVisible);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isVisible)
|
|
{
|
|
isVisible = false;
|
|
foreach (ObiActor actor in actors)
|
|
actor.OnSolverVisibilityChanged(isVisible);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitializeTransformFrame()
|
|
{
|
|
Vector4 translation = transform.position;
|
|
Vector4 scale = transform.lossyScale;
|
|
Quaternion rotation = transform.rotation;
|
|
|
|
implementation.InitializeFrame(translation, scale, rotation);
|
|
}
|
|
|
|
private IObiJobHandle UpdateTransformFrame(float dt)
|
|
{
|
|
Vector4 translation = transform.position;
|
|
Vector4 scale = transform.lossyScale;
|
|
Quaternion rotation = transform.rotation;
|
|
|
|
implementation.UpdateFrame(translation, scale, rotation, dt);
|
|
return implementation.ApplyFrame(worldLinearInertiaScale, worldAngularInertiaScale, dt);
|
|
}
|
|
|
|
public void RegisterRenderSystem(IRenderSystem renderSystem)
|
|
{
|
|
m_RenderSystems.RegisterRenderSystem(renderSystem);
|
|
}
|
|
|
|
public void UnregisterRenderSystem(IRenderSystem renderSystem)
|
|
{
|
|
m_RenderSystems.UnregisterRenderSystem(renderSystem);
|
|
}
|
|
|
|
public RenderSystem<T> GetRenderSystem<T>() where T : ObiRenderer<T>
|
|
{
|
|
return m_RenderSystems.GetRenderSystem<T>();
|
|
}
|
|
|
|
public IRenderSystem GetRenderSystem(Oni.RenderingSystemType type)
|
|
{
|
|
return m_RenderSystems.GetRenderSystem(type);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enqueues a generic spatial query to be performed during the next physics update.
|
|
/// If called when the solver is yet uninitialized,
|
|
/// the query will be ignored and this method will return -1.
|
|
/// </summary>
|
|
/// <param name="shape"> Query shape to test against all simplices in the solver.</param>
|
|
/// <param name="transform"> Transform to apply to the query shape.</param>
|
|
/// <returns>
|
|
/// Index of the query in the queue. Use the queryIndex member of each query result to correlate each result to the query that spawned it. For instance:
|
|
/// a query result with queryIndex 5, belongs to the query shape at index 5 in the queue.
|
|
/// </returns>
|
|
public int EnqueueSpatialQuery(QueryShape shape, AffineTransform transform)
|
|
{
|
|
// if the solver is not initialized, bail out.
|
|
if (!initialized)
|
|
return -1;
|
|
|
|
int index = bufferedQueryShapes.count;
|
|
bufferedQueryShapes.Add(shape);
|
|
bufferedQueryTransforms.Add(transform);
|
|
return index;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enqueues multiple generic spatial query to be performed during the next physics update.
|
|
/// If called when the solver is yet uninitialized,
|
|
/// the query will be ignored and this method will return -1.
|
|
/// </summary>
|
|
/// <param name="shapes"> Query shapes to test against all simplices in the solver.</param>
|
|
/// <param name="transforms"> Transforms to apply to the query shapes.</param>
|
|
/// <returns>
|
|
/// Index of the first query in the queue. Use the queryIndex member of each query result to correlate each result to the query that spawned it. For instance:
|
|
/// a query result with queryIndex 5, belongs to the query shape at index 5 in the queue.
|
|
/// </returns>
|
|
public int EnqueueSpatialQueries(ObiNativeQueryShapeList shapes, ObiNativeAffineTransformList transforms)
|
|
{
|
|
// if the solver is not initialized or input is not ok, bail out.
|
|
if (!initialized || shapes == null || transforms == null || shapes.count != transforms.count)
|
|
return -1;
|
|
|
|
int index = bufferedQueryShapes.count;
|
|
bufferedQueryShapes.AddRange(shapes);
|
|
bufferedQueryTransforms.AddRange(transforms);
|
|
return index;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enqueues a raycast to be performed during the next physics update.
|
|
/// If called when the solver is yet uninitialized,
|
|
/// the query will be ignored and this method will return -1.
|
|
/// </summary>
|
|
/// <param name="ray"> Ray to cast against all simplices in the solver. Expressed in world space.</param>
|
|
/// <param name="filter"> Filter (mask, category) used to filter out collisions against certain simplices. </param>
|
|
/// <param name="maxDistance"> Ray length. </param>
|
|
/// <param name="rayThickness">
|
|
/// Ray thickness. If the ray hits a simplex, hitInfo will contain a point on the simplex.
|
|
/// If it merely passes near the simplex (within its thickness distance, but no actual hit), it will contain the point on the ray closest to the simplex surface. </param>
|
|
/// <returns>
|
|
/// Index of the query in the queue. Use the queryIndex member of each query result to correlate each result to the query that spawned it. For instance:
|
|
/// a query result with queryIndex 5, belongs to the query shape at index 5 in the queue.
|
|
/// </returns>
|
|
public int EnqueueRaycast(Ray ray, int filter, float maxDistance = 100, float rayThickness = 0)
|
|
{
|
|
// if the solver is not initialized or simulation is currently underway, bail out.
|
|
if (!initialized)
|
|
return -1;
|
|
|
|
int index = bufferedQueryShapes.count;
|
|
|
|
bufferedQueryShapes.Add(new QueryShape
|
|
{
|
|
type = QueryShape.QueryType.Ray,
|
|
center = ray.origin,
|
|
size = ray.direction * maxDistance,
|
|
contactOffset = rayThickness,
|
|
maxDistance = 0.0001f,
|
|
filter = filter
|
|
});
|
|
|
|
bufferedQueryTransforms.Add(new AffineTransform(Vector4.zero, Quaternion.identity, Vector4.one));
|
|
|
|
return index;
|
|
}
|
|
|
|
}
|
|
|
|
}
|