Files
HauntedBloodlines/Assets/Obi/Scripts/Common/Solver/ObiSolver.cs
2025-05-29 22:31:40 +03:00

1835 lines
68 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;
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");
public enum BackendType
{
Oni,
Burst
}
public class ObiCollisionEventArgs : System.EventArgs
{
public ObiList<Oni.Contact> contacts = new ObiList<Oni.Contact>(); /**< collision contacts.*/
}
[Serializable]
public class ParticleInActor
{
public ObiActor actor;
public int indexInActor;
public ParticleInActor()
{
this.actor = null;
this.indexInActor = -1;
}
public ParticleInActor(ObiActor actor, int indexInActor)
{
this.actor = actor;
this.indexInActor = indexInActor;
}
}
public delegate void SolverCallback(ObiSolver solver);
public delegate void SolverStepCallback(ObiSolver solver, float stepTime);
public delegate void CollisionCallback(ObiSolver solver, ObiCollisionEventArgs contacts);
public event CollisionCallback OnCollision;
public event CollisionCallback OnParticleCollision;
public event SolverCallback OnUpdateParameters;
public event SolverCallback OnPrepareFrame;
public event SolverStepCallback OnPrepareStep;
public event SolverStepCallback OnBeginStep;
public event SolverStepCallback OnSubstep;
public event SolverCallback OnEndStep;
public event SolverCallback OnInterpolate;
[Tooltip("If enabled, will force the solver to keep simulating even when not visible from any camera.")]
public bool simulateWhenInvisible = true; /**< Whether to keep simulating the cloth when its not visible by any camera.*/
private ISolverImpl m_SolverImpl;
private IObiBackend m_SimulationBackend =
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
new BurstBackend();
#elif (OBI_ONI_SUPPORTED)
new OniBackend();
#else
new NullBackend();
#endif
[SerializeField] private BackendType m_Backend = BackendType.Burst;
public Oni.SolverParameters parameters = new Oni.SolverParameters(Oni.SolverParameters.Interpolation.None,
new Vector4(0, -9.81f, 0, 0));
public Vector3 gravity = new Vector3(0, -9.81f, 0);
public Space gravitySpace = Space.Self;
[Range(0, 1)]
public float worldLinearInertiaScale = 0; /**< how much does world-space linear inertia affect the actor. This only applies when the solver has "simulateInLocalSpace" enabled.*/
[Range(0, 1)]
public float worldAngularInertiaScale = 0; /**< how much does world-space angular inertia affect the actor. This only applies when the solver has "simulateInLocalSpace" enabled.*/
[HideInInspector] [NonSerialized] public List<ObiActor> actors = new List<ObiActor>();
[HideInInspector] [NonSerialized] public ParticleInActor[] m_ParticleToActor;
private ObiNativeIntList freeList;
private List<int> points = new List<int>(); /**< 0-simplices*/
private List<int> edges = new List<int>(); /**< 1-simplices*/
private List<int> triangles = new List<int>(); /**< 2-simplices*/
private SimplexCounts m_SimplexCounts;
[HideInInspector][NonSerialized] public bool dirtySimplices = true;
[HideInInspector][NonSerialized] public int dirtyConstraints = 0;
private bool m_dirtyActiveParticles = true;
public bool dirtyActiveParticles
{
set
{
// make sure anytime active particles need to be updated, simplices will be updated too:
m_dirtyActiveParticles = value;
dirtySimplices |= m_dirtyActiveParticles;
}
get { return m_dirtyActiveParticles; }
}
private ObiCollisionEventArgs collisionArgs = new ObiCollisionEventArgs();
private ObiCollisionEventArgs particleCollisionArgs = new ObiCollisionEventArgs();
private int m_contactCount;
private int m_particleContactCount;
private float m_MaxScale = 1;
private UnityEngine.Bounds bounds = new UnityEngine.Bounds();
private Plane[] planes = new Plane[6];
private Camera[] sceneCameras = new Camera[1];
private bool isVisible = true;
// 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_RestPositions;
[NonSerialized] private ObiNativeVector4List m_PrevPositions;
[NonSerialized] private ObiNativeVector4List m_StartPositions;
[NonSerialized] private ObiNativeVector4List m_RenderablePositions;
// orientations
[NonSerialized] private ObiNativeQuaternionList m_Orientations;
[NonSerialized] private ObiNativeQuaternionList m_RestOrientations;
[NonSerialized] private ObiNativeQuaternionList m_PrevOrientations;
[NonSerialized] private ObiNativeQuaternionList m_StartOrientations;
[NonSerialized] private ObiNativeQuaternionList m_RenderableOrientations; /**< renderable particle orientations.*/
// velocities
[NonSerialized] private ObiNativeVector4List m_Velocities;
[NonSerialized] private ObiNativeVector4List m_AngularVelocities;
// masses/inertia tensors
[NonSerialized] private ObiNativeFloatList m_InvMasses;
[NonSerialized] private ObiNativeFloatList m_InvRotationalMasses;
[NonSerialized] private ObiNativeVector4List m_InvInertiaTensors;
// 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_Anisotropies;
[NonSerialized] private ObiNativeVector4List m_PrincipalRadii;
[NonSerialized] private ObiNativeVector4List m_Normals;
// fluids
[NonSerialized] private ObiNativeVector4List m_Vorticities;
[NonSerialized] private ObiNativeVector4List m_FluidData;
[NonSerialized] private ObiNativeVector4List m_UserData;
[NonSerialized] private ObiNativeFloatList m_SmoothingRadii;
[NonSerialized] private ObiNativeFloatList m_Buoyancies;
[NonSerialized] private ObiNativeFloatList m_RestDensities;
[NonSerialized] private ObiNativeFloatList m_Viscosities;
[NonSerialized] private ObiNativeFloatList m_SurfaceTension;
[NonSerialized] private ObiNativeFloatList m_VortConfinement;
[NonSerialized] private ObiNativeFloatList m_AtmosphericDrag;
[NonSerialized] private ObiNativeFloatList m_AtmosphericPressure;
[NonSerialized] private ObiNativeFloatList m_Diffusion;
public ISolverImpl implementation
{
get { return m_SolverImpl; }
}
public bool initialized
{
get { return m_SolverImpl != 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; }
}
public UnityEngine.Bounds Bounds
{
get { return bounds; }
}
public bool IsVisible
{
get { return isVisible; }
}
public float maxScale
{
get { return m_MaxScale; }
}
public int allocParticleCount
{
get { return particleToActor.Count(s => s != null && s.actor != null); }
}
public int contactCount
{
get { return m_contactCount; }
}
public int particleContactCount
{
get { return m_particleContactCount; }
}
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;
}
}
public ObiNativeIntList simplices
{
get
{
if (m_Simplices == null)
m_Simplices = new ObiNativeIntList();
return m_Simplices;
}
}
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;
}
}
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 restPositions
{
get
{
if (m_RestPositions == null)
m_RestPositions = new ObiNativeVector4List();
return m_RestPositions;
}
}
public ObiNativeVector4List prevPositions
{
get
{
if (m_PrevPositions == null)
m_PrevPositions = new ObiNativeVector4List();
return m_PrevPositions;
}
}
public ObiNativeVector4List startPositions
{
get
{
if (m_StartPositions == null)
m_StartPositions = new ObiNativeVector4List();
return m_StartPositions;
}
}
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 restOrientations
{
get
{
if (m_RestOrientations == null)
m_RestOrientations = new ObiNativeQuaternionList();
return m_RestOrientations;
}
}
public ObiNativeQuaternionList prevOrientations
{
get
{
if (m_PrevOrientations == null)
m_PrevOrientations = new ObiNativeQuaternionList();
return m_PrevOrientations;
}
}
public ObiNativeQuaternionList startOrientations
{
get
{
if (m_StartOrientations == null)
m_StartOrientations = new ObiNativeQuaternionList();
return m_StartOrientations;
}
}
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;
}
}
public ObiNativeVector4List invInertiaTensors
{
get
{
if (m_InvInertiaTensors == null)
m_InvInertiaTensors = new ObiNativeVector4List();
return m_InvInertiaTensors;
}
}
#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 anisotropies
{
get
{
if (m_Anisotropies == null)
m_Anisotropies = new ObiNativeVector4List();
return m_Anisotropies;
}
}
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 ObiNativeVector4List vorticities
{
get
{
if (m_Vorticities == null)
m_Vorticities = new ObiNativeVector4List();
return m_Vorticities;
}
}
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 ObiNativeFloatList smoothingRadii
{
get
{
if (m_SmoothingRadii == null)
m_SmoothingRadii = new ObiNativeFloatList();
return m_SmoothingRadii;
}
}
public ObiNativeFloatList buoyancies
{
get
{
if (m_Buoyancies == null)
m_Buoyancies = new ObiNativeFloatList();
return m_Buoyancies;
}
}
public ObiNativeFloatList restDensities
{
get
{
if (m_RestDensities == null)
m_RestDensities = new ObiNativeFloatList();
return m_RestDensities;
}
}
public ObiNativeFloatList viscosities
{
get
{
if (m_Viscosities == null)
m_Viscosities = new ObiNativeFloatList();
return m_Viscosities;
}
}
public ObiNativeFloatList surfaceTension
{
get
{
if (m_SurfaceTension == null)
m_SurfaceTension = new ObiNativeFloatList();
return m_SurfaceTension;
}
}
public ObiNativeFloatList vortConfinement
{
get
{
if (m_VortConfinement == null)
m_VortConfinement = new ObiNativeFloatList();
return m_VortConfinement;
}
}
public ObiNativeFloatList atmosphericDrag
{
get
{
if (m_AtmosphericDrag == null)
m_AtmosphericDrag = new ObiNativeFloatList();
return m_AtmosphericDrag;
}
}
public ObiNativeFloatList atmosphericPressure
{
get
{
if (m_AtmosphericPressure == null)
m_AtmosphericPressure = new ObiNativeFloatList();
return m_AtmosphericPressure;
}
}
public ObiNativeFloatList diffusion
{
get
{
if (m_Diffusion == null)
m_Diffusion = new ObiNativeFloatList();
return m_Diffusion;
}
}
#endregion
void Update()
{
var scale = transform.lossyScale;
m_MaxScale = Mathf.Max(Mathf.Max(scale.x, scale.y), scale.z);
}
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
#if (OBI_ONI_SUPPORTED)
case BackendType.Oni: m_SimulationBackend = new OniBackend(); break;
#endif
default:
#if (OBI_ONI_SUPPORTED)
m_SimulationBackend = new OniBackend();
#else
m_SimulationBackend = new NullBackend();
#endif
break;
}
}
public void Initialize()
{
if (!initialized)
{
CreateBackend();
// Set up local actor and particle buffers:
actors = new List<ObiActor>();
freeList = new ObiNativeIntList();
m_ParticleToActor = new ParticleInActor[0];
// 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:
m_SolverImpl = m_SimulationBackend.CreateSolver(this, 0);
// Set data arrays:
m_SolverImpl.ParticleCountChanged(this);
m_SolverImpl.SetRigidbodyArrays(this);
// Initialize moving transform:
InitializeTransformFrame();
// Set initial parameter values:
PushSolverParameters();
}
}
public void Teardown()
{
if (initialized)
{
// Clear all constraints:
PushConstraints();
// Destroy the Oni solver:
m_SimulationBackend.DestroySolver(m_SolverImpl);
m_SolverImpl = null;
// Free particle / rigidbody memory:
FreeParticleArrays();
FreeRigidbodyArrays();
freeList.Dispose();
}
}
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.
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.ResizeInitialized(count);
rigidbodyAngularDeltas.ResizeInitialized(count);
m_SolverImpl.SetRigidbodyArrays(this);
}
}
private void FreeParticleArrays()
{
activeParticles.Dispose();
simplices.Dispose();
colors.Dispose();
cellCoords.Dispose();
startPositions.Dispose();
startOrientations.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();
anisotropies.Dispose();
smoothingRadii.Dispose();
buoyancies.Dispose();
restDensities.Dispose();
viscosities.Dispose();
surfaceTension.Dispose();
vortConfinement.Dispose();
atmosphericDrag.Dispose();
atmosphericPressure.Dispose();
diffusion.Dispose();
vorticities.Dispose();
fluidData.Dispose();
userData.Dispose();
externalForces.Dispose();
externalTorques.Dispose();
wind.Dispose();
positionDeltas.Dispose();
orientationDeltas.Dispose();
positionConstraintCounts.Dispose();
orientationConstraintCounts.Dispose();
normals.Dispose();
invInertiaTensors.Dispose();
m_ActiveParticles = null;
m_Simplices = null;
m_Colors = null;
m_CellCoords = null;
m_Positions = null;
m_RestPositions = null;
m_PrevPositions = null;
m_StartPositions = null;
m_RenderablePositions = null;
m_Orientations = null;
m_RestOrientations = null;
m_PrevOrientations = null;
m_StartOrientations = null;
m_RenderableOrientations = null;
m_Velocities = null;
m_AngularVelocities = null;
m_InvMasses = null;
m_InvRotationalMasses = null;
m_InvInertiaTensors = 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_Anisotropies = null;
m_PrincipalRadii = null;
m_Normals = null;
m_Vorticities = null;
m_FluidData = null;
m_UserData = null;
m_SmoothingRadii = null;
m_Buoyancies = null;
m_RestDensities = null;
m_Viscosities = null;
m_SurfaceTension = null;
m_VortConfinement = null;
m_AtmosphericDrag = null;
m_AtmosphericPressure = null;
m_Diffusion = 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);
startPositions.ResizeInitialized(count);
positions.ResizeInitialized(count);
prevPositions.ResizeInitialized(count);
restPositions.ResizeInitialized(count);
startOrientations.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);
anisotropies.ResizeInitialized(count * 3);
smoothingRadii.ResizeInitialized(count);
buoyancies.ResizeInitialized(count);
restDensities.ResizeInitialized(count);
viscosities.ResizeInitialized(count);
surfaceTension.ResizeInitialized(count);
vortConfinement.ResizeInitialized(count);
atmosphericDrag.ResizeInitialized(count);
atmosphericPressure.ResizeInitialized(count);
diffusion.ResizeInitialized(count);
vorticities.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);
invInertiaTensors.ResizeInitialized(count);
m_SolverImpl.ParticleCountChanged(this);
}
if (count >= m_ParticleToActor.Length)
{
Array.Resize(ref m_ParticleToActor, count * 2);
}
}
private void AllocateParticles(int[] particleIndices)
{
// If attempting to allocate more particles than we have:
if (particleIndices.Length > freeList.count)
{
int grow = particleIndices.Length - 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.Length);
}
// determine first particle in the free list to use:
int first = freeList.count - particleIndices.Length;
// copy free indices to the input array:
freeList.CopyTo(particleIndices, first, particleIndices.Length);
// shorten the free list:
freeList.ResizeUninitialized(first);
}
private void FreeParticles(int[] particleIndices)
{
freeList.AddRange(particleIndices);
}
/// <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)
return false;
if ((actors == null || !actors.Contains(actor)) && actor.sourceBlueprint != null)
{
// If the solver is not initialized yet, do so:
Initialize();
actor.solverIndices = new int[actor.sourceBlueprint.particleCount];
AllocateParticles(actor.solverIndices);
for (int i = 0; i < actor.solverIndices.Length; ++i)
particleToActor[actor.solverIndices[i]] = new ObiSolver.ParticleInActor(actor, i);
actors.Add(actor);
actor.LoadBlueprint(this);
}
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;
// 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.Length; ++i)
particleToActor[actor.solverIndices[i]] = null;
FreeParticles(actor.solverIndices);
actors.RemoveAt(index);
actor.solverIndices = null;
// If this was the last actor in the solver, tear it down:
if (actors.Count == 0)
Teardown();
return true;
}
return false;
}
/// <summary>
/// Updates solver parameters.
/// </summary>
/// Call this after modifying solver or constraint parameters.
public void PushSolverParameters()
{
if (!initialized)
return;
m_SolverImpl.SetParameters(parameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Distance, ref distanceConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Bending, ref bendingConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.ParticleCollision, ref particleCollisionConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.ParticleFriction, ref particleFrictionConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Collision, ref collisionConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Friction, ref frictionConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Density, ref densityConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Skin, ref skinConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Volume, ref volumeConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.ShapeMatching, ref shapeMatchingConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Tether, ref tetherConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Pin, ref pinConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.Stitch, ref stitchConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.StretchShear, ref stretchShearConstraintParameters);
m_SolverImpl.SetConstraintGroupParameters(Oni.ConstraintType.BendTwist, ref bendTwistConstraintParameters);
m_SolverImpl.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()
{
activeParticles.Clear();
for (int i = 0; i < actors.Count; ++i)
{
ObiActor currentActor = actors[i];
if (currentActor.isActiveAndEnabled)
{
for (int j = 0; j < currentActor.activeParticleCount; ++j)
activeParticles.Add(currentActor.solverIndices[j]);
}
}
implementation.SetActiveParticles(activeParticles);
dirtyActiveParticles = false;
}
private void PushSimplices()
{
simplices.Clear();
points.Clear();
edges.Clear();
triangles.Clear();
for (int i = 0; i < actors.Count; ++i)
{
ObiActor currentActor = actors[i];
if (currentActor.isActiveAndEnabled && currentActor.isLoaded)
{
//simplex based contacts
if (currentActor.surfaceCollisions)
{
if (currentActor.blueprint.points != null)
for (int j = 0; j < currentActor.blueprint.points.Length; ++j)
{
int actorIndex = currentActor.blueprint.points[j];
if (actorIndex < currentActor.activeParticleCount)
points.Add(currentActor.solverIndices[actorIndex]);
}
if (currentActor.blueprint.edges != null)
for (int j = 0; j < currentActor.blueprint.edges.Length / 2; ++j)
{
int actorIndex1 = currentActor.blueprint.edges[j * 2];
int actorIndex2 = currentActor.blueprint.edges[j * 2 + 1];
if (actorIndex1 < currentActor.activeParticleCount && actorIndex2 < currentActor.activeParticleCount)
{
edges.Add(currentActor.solverIndices[actorIndex1]);
edges.Add(currentActor.solverIndices[actorIndex2]);
}
}
if (currentActor.blueprint.triangles != null)
for (int j = 0; j < currentActor.blueprint.triangles.Length / 3; ++j)
{
int actorIndex1 = currentActor.blueprint.triangles[j * 3];
int actorIndex2 = currentActor.blueprint.triangles[j * 3 + 1];
int actorIndex3 = currentActor.blueprint.triangles[j * 3 + 2]; // TODO: +1: degenerate triangles. check out!
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
{
// generate a point simplex out of each active particle:
for (int j = 0; j < currentActor.activeParticleCount; ++j)
points.Add(currentActor.solverIndices[j]);
}
}
}
simplices.capacity = points.Count + edges.Count + triangles.Count;
simplices.AddRange(points);
simplices.AddRange(edges);
simplices.AddRange(triangles);
m_SimplexCounts = new SimplexCounts(points.Count, edges.Count / 2, triangles.Count / 3);
cellCoords.ResizeInitialized(m_SimplexCounts.simplexCount);
m_SolverImpl.SetSimplices(simplices, m_SimplexCounts);
dirtySimplices = false;
}
private void PushConstraints()
{
// 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;
}
/**
* Checks if any particle in the solver is 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;
m_SolverImpl.GetBounds(ref min, ref max);
bounds.SetMinMax(min, max);
}
if (bounds.AreValid())
{
using (m_TestBoundsPerfMarker.Auto())
{
// transform bounds to world space:
bounds = 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, bounds))
{
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;
m_SolverImpl.InitializeFrame(translation, scale, rotation);
}
private void UpdateTransformFrame(float dt)
{
Vector4 translation = transform.position;
Vector4 scale = transform.lossyScale;
Quaternion rotation = transform.rotation;
m_SolverImpl.UpdateFrame(translation, scale, rotation, dt);
m_SolverImpl.ApplyFrame(worldLinearInertiaScale, worldAngularInertiaScale, dt);
}
public void PrepareFrame()
{
if (OnPrepareFrame != null)
OnPrepareFrame(this);
foreach (ObiActor actor in actors)
actor.PrepareFrame();
}
/// <summary>
/// Signals the start of a new time step.
/// </summary>
/// Pushes active particles (if dirtyActiveParticles is true), and runtime constraints (if dirtyConstraints != 0).
/// Updates the solver's nertial reference frame. Calls begin step callbacks.
/// Finally, it schedules execution of simulation tasks at the beginng on a physics step (most notably, collision detection), and returns a handle to the job that will perform them.
/// <param name="stepTime"> Duration of the entire time step (in seconds).</param>
/// <returns>
/// A handle to the job.
/// </returns>
public IObiJobHandle BeginStep(float stepTime)
{
if (!isActiveAndEnabled || !initialized)
return null;
if (OnPrepareStep != null)
OnPrepareStep(this, stepTime);
foreach (ObiActor actor in actors)
actor.PrepareStep(stepTime);
// Update the active particles array:
if (dirtyActiveParticles)
PushActiveParticles();
// Update the simplices array:
if (dirtySimplices)
PushSimplices();
// Update constraint batches:
if (dirtyConstraints != 0)
PushConstraints();
// Update inertial frame:
UpdateTransformFrame(stepTime);
// Update gravity:
parameters.gravity = gravitySpace == Space.World ? transform.InverseTransformVector(gravity) : gravity;
if (initialized)
m_SolverImpl.SetParameters(parameters);
// Copy positions / orientations at the start of the step, for interpolation:
startPositions.CopyFrom(positions);
startOrientations.CopyFrom(orientations);
if (OnBeginStep != null)
OnBeginStep(this, stepTime);
foreach (ObiActor actor in actors)
actor.BeginStep(stepTime);
// Perform collision detection:
if (simulateWhenInvisible || isVisible)
return m_SolverImpl.CollisionDetection(stepTime);
return null;
}
/// <summary>
/// Schedules the job to advance the simulation a given amount of time, then returns a handle to this job.
/// </summary>
/// <param name="substepTime"> Amount of time to advance (in seconds).</param>
/// <returns>
/// A handle to the job.
/// </returns>
public IObiJobHandle Substep(float stepTime, float substepTime, int index)
{
// Only update the solver if it is visible, or if we must simulate even when invisible.
if (isActiveAndEnabled && (simulateWhenInvisible || isVisible) && initialized)
{
if (OnSubstep != null)
OnSubstep(this, substepTime);
foreach (ObiActor actor in actors)
actor.Substep(substepTime);
// Update the solver (this is internally split in tasks so multiple solvers can be updated in parallel)
return m_SolverImpl.Substep(stepTime, substepTime, index);
}
return null;
}
/// <summary>
/// Wraps up a simulation step: resets external forces and calls collision callbacks.
/// </summary>
/// <param name="substepTime"> Size of the last substep performed this step (in seconds).</param>
public void EndStep(float substepTime)
{
if (!initialized)
return;
m_contactCount = implementation.GetConstraintCount(Oni.ConstraintType.Collision);
m_particleContactCount = implementation.GetConstraintCount(Oni.ConstraintType.ParticleCollision);
if (OnCollision != null)
{
collisionArgs.contacts.SetCount(m_contactCount);
if (m_contactCount > 0)
implementation.GetCollisionContacts(collisionArgs.contacts.Data, m_contactCount);
OnCollision(this, collisionArgs);
}
if (OnParticleCollision != null)
{
particleCollisionArgs.contacts.SetCount(m_particleContactCount);
if (m_particleContactCount > 0)
implementation.GetParticleCollisionContacts(particleCollisionArgs.contacts.Data, m_particleContactCount);
OnParticleCollision(this, particleCollisionArgs);
}
m_SolverImpl.ResetForces();
if (OnEndStep != null)
OnEndStep(this);
foreach (ObiActor actor in actors)
actor.EndStep(substepTime);
}
/// <summary>
/// Finalizes the frame by performing physics state interpolation.
/// </summary>
/// This is usually used for mesh generation, rendering setup and other tasks that must take place after all physics steps for this frame are done.
/// <param name="stepTime"> Duration of this time step (in seconds). Note this is the entire timestep, not just the ast substep.</param>
/// <param name="unsimulatedTime"> Remaining time that could not be simulated during this step (in seconds). This is used to interpolate physics state. </param>
public void Interpolate(float stepTime, float unsimulatedTime)
{
if (!isActiveAndEnabled || !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:
m_SolverImpl.ApplyInterpolation(startPositions, startOrientations, stepTime, unsimulatedTime);
}
}
UpdateVisibility();
if (OnInterpolate != null)
OnInterpolate(this);
foreach (ObiActor actor in actors)
actor.Interpolate();
}
public void ReleaseJobHandles()
{
if (!initialized)
return;
m_SolverImpl.ReleaseJobHandles();
}
/// <summary>
/// Performs multiple spatial queries in parallel against all simplices in the solver, and returns a list of results.
/// </summary>
/// All other query/raycast methods are built on top of this one. Use it when you need maximum flexibility/performance.
/// <param name="shapes"> List of query shapes to test against all simplices in the solver.</param>
/// <param name="transforms"> List of transforms, must have the same size as the shapes list. </param>
/// <param name="results">
/// This list will contain results for all queries, in no specific order.
/// 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 input shapes list.
/// </param>
public void SpatialQuery(ObiNativeQueryShapeList shapes, ObiNativeAffineTransformList transforms, ObiNativeQueryResultList results)
{
if (!initialized || shapes == null || transforms == null || results == null || shapes.count != transforms.count)
return;
m_SolverImpl.SpatialQuery(shapes, transforms, results);
}
/// <summary>
/// Performs a single spatial queries against all simplices in the solver, and returns a list of results.
/// </summary>
/// <param name="shape"> Query shape to test against all simplices in the solver.</param>
/// <param name="transform"> Transform applied to the query shape. </param>
/// <returns>
/// An array that contains the query results.
/// </returns>
public QueryResult[] SpatialQuery(QueryShape shape, AffineTransform transform)
{
if (!initialized)
return null;
var queries = new ObiNativeQueryShapeList();
var transforms = new ObiNativeAffineTransformList();
var results = new ObiNativeQueryResultList();
queries.Add(shape);
transforms.Add(transform);
m_SolverImpl.SpatialQuery(queries, transforms, results);
var resultsArray = results.ToArray();
queries.Dispose();
transforms.Dispose();
results.Dispose();
return resultsArray;
}
/// <summary>
/// Performs a single raycast against all simplices in the solver, and returns the result.
/// </summary>
/// <param name="ray"> Ray to cast against all simplices in the solver. Expressed in world space.</param>
/// <param name="hitInfo"> Struct containing hit info, if any. </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>
/// Whether the ray hit anything. If the ray did not hit, the hitInfo will contain a simplexIndex of -1 and a distance equal to maxDistance.
/// </returns>
public bool Raycast(Ray ray, out QueryResult hitInfo, int filter, float maxDistance = 100, float rayThickness = 0)
{
var result = Raycast(new List<Ray> { ray }, filter, maxDistance, rayThickness);
if (result != null && result.Length > 0 && result[0].simplexIndex >= 0)
{
hitInfo = result[0];
return true;
}
else
{
hitInfo = new QueryResult() { distance = maxDistance };
return false;
}
}
/// <summary>
/// Performs multiple raycasts in parallel against all simplices in the solver, and returns the results.
/// </summary>
/// <param name="rays"> List of rays 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>
/// This list will contain results for all raycasts, in no specific order.
/// Use the queryIndex member of each query result to correlate each result to the raycast that spawned it. For instance:
/// a query result with queryIndex 5, belongs to the raycast at index 5 in the input rays list.
/// </returns>
public QueryResult[] Raycast(List<Ray> rays, int filter, float maxDistance = 100, float rayThickness = 0)
{
if (!initialized)
return null;
var queries = new ObiNativeQueryShapeList();
var transforms = new ObiNativeAffineTransformList();
var results = new ObiNativeQueryResultList();
var resultArray = new QueryResult[rays.Count];
for (int i = 0; i < rays.Count; ++i)
{
queries.Add(new QueryShape()
{
type = QueryShape.QueryType.Ray,
center = rays[i].origin,
size = rays[i].origin + rays[i].direction * maxDistance,
contactOffset = rayThickness,
maxDistance = 0.0005f,
filter = filter
});
transforms.Add(new AffineTransform(Vector4.zero, Quaternion.identity, Vector4.one));
resultArray[i] = new QueryResult { distance = maxDistance, simplexIndex = -1, queryIndex = -1 };
}
m_SolverImpl.SpatialQuery(queries, transforms, results);
Matrix4x4 solver2World = transform.localToWorldMatrix;
for (int i = 0; i < results.count; ++i)
{
int rayIndex = results[i].queryIndex;
var pointWS = solver2World.MultiplyPoint3x4(results[i].queryPoint + results[i].normal * results[i].distance);
if (results[i].distance <= 0.001f)
{
// project the hit on the ray:
float rayDistance = (pointWS.x - rays[rayIndex].origin.x) * rays[rayIndex].direction.x +
(pointWS.y - rays[rayIndex].origin.y) * rays[rayIndex].direction.y +
(pointWS.z - rays[rayIndex].origin.z) * rays[rayIndex].direction.z;
// keep the closest hit:
if (rayDistance < resultArray[rayIndex].distance)
{
resultArray[rayIndex] = results[i];
resultArray[rayIndex].distance = rayDistance;
resultArray[rayIndex].queryPoint = rays[rayIndex].origin + rays[rayIndex].direction * rayDistance;
}
}
}
queries.Dispose();
transforms.Dispose();
results.Dispose();
return resultArray;
}
}
}