971 lines
42 KiB
C#
971 lines
42 KiB
C#
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
|
using UnityEngine;
|
|
using Unity.Jobs;
|
|
using Unity.Mathematics;
|
|
using Unity.Collections;
|
|
|
|
namespace Obi
|
|
{
|
|
public class BurstSolverImpl : ISolverImpl
|
|
{
|
|
ObiSolver m_Solver;
|
|
|
|
public ObiSolver abstraction
|
|
{
|
|
get { return m_Solver; }
|
|
}
|
|
|
|
public int particleCount
|
|
{
|
|
get { return m_Solver.positions.count; }
|
|
}
|
|
|
|
public int activeParticleCount
|
|
{
|
|
get { return abstraction.activeParticles.count; }
|
|
}
|
|
|
|
public BurstInertialFrame inertialFrame
|
|
{
|
|
get { return m_InertialFrame; }
|
|
}
|
|
|
|
public BurstAffineTransform solverToWorld
|
|
{
|
|
get { return m_InertialFrame.frame; }
|
|
}
|
|
|
|
public BurstAffineTransform worldToSolver
|
|
{
|
|
get { return m_InertialFrame.frame.Inverse(); }
|
|
}
|
|
|
|
private const int maxBatches = 17;
|
|
|
|
private ConstraintBatcher<ContactProvider> collisionConstraintBatcher;
|
|
private ConstraintBatcher<FluidInteractionProvider> fluidConstraintBatcher;
|
|
|
|
// Per-type constraints array:
|
|
private IBurstConstraintsImpl[] constraints;
|
|
|
|
// Per-type iteration padding array:
|
|
private int[] padding = new int[Oni.ConstraintTypeCount];
|
|
|
|
// Pool job handles to avoid runtime alloc:
|
|
private JobHandlePool<BurstJobHandle> jobHandlePool;
|
|
|
|
// particle contact generation:
|
|
public ParticleGrid particleGrid;
|
|
public NativeArray<BurstContact> particleContacts;
|
|
public NativeArray<BatchData> particleBatchData;
|
|
|
|
// fluid interaction generation:
|
|
public NativeArray<FluidInteraction> fluidInteractions;
|
|
public NativeArray<BatchData> fluidBatchData;
|
|
|
|
// collider contact generation:
|
|
private BurstColliderWorld colliderGrid;
|
|
public NativeArray<BurstContact> colliderContacts;
|
|
|
|
// misc data:
|
|
public NativeArray<int> activeParticles;
|
|
private NativeList<int> deformableTriangles;
|
|
|
|
public NativeArray<int> simplices;
|
|
public SimplexCounts simplexCounts;
|
|
|
|
private BurstInertialFrame m_InertialFrame; // local to world inertial frame.
|
|
private int scheduledJobCounter = 0;
|
|
|
|
// cached particle data arrays (just wrappers over raw unmanaged data held by the abstract solver)
|
|
public NativeArray<float4> positions;
|
|
public NativeArray<float4> restPositions;
|
|
public NativeArray<float4> prevPositions;
|
|
public NativeArray<float4> renderablePositions;
|
|
|
|
public NativeArray<quaternion> orientations;
|
|
public NativeArray<quaternion> restOrientations;
|
|
public NativeArray<quaternion> prevOrientations;
|
|
public NativeArray<quaternion> renderableOrientations;
|
|
|
|
public NativeArray<float4> velocities;
|
|
public NativeArray<float4> angularVelocities;
|
|
|
|
public NativeArray<float> invMasses;
|
|
public NativeArray<float> invRotationalMasses;
|
|
public NativeArray<float4> invInertiaTensors;
|
|
|
|
public NativeArray<float4> externalForces;
|
|
public NativeArray<float4> externalTorques;
|
|
public NativeArray<float4> wind;
|
|
|
|
public NativeArray<float4> positionDeltas;
|
|
public NativeArray<quaternion> orientationDeltas;
|
|
public NativeArray<int> positionConstraintCounts;
|
|
public NativeArray<int> orientationConstraintCounts;
|
|
|
|
public NativeArray<int> collisionMaterials;
|
|
public NativeArray<int> phases;
|
|
public NativeArray<int> filters;
|
|
public NativeArray<float4> anisotropies;
|
|
public NativeArray<float4> principalRadii;
|
|
public NativeArray<float4> normals;
|
|
|
|
public NativeArray<float4> vorticities;
|
|
public NativeArray<float4> fluidData;
|
|
public NativeArray<float4> userData;
|
|
public NativeArray<float> smoothingRadii;
|
|
public NativeArray<float> buoyancies;
|
|
public NativeArray<float> restDensities;
|
|
public NativeArray<float> viscosities;
|
|
public NativeArray<float> surfaceTension;
|
|
public NativeArray<float> vortConfinement;
|
|
public NativeArray<float> athmosphericDrag;
|
|
public NativeArray<float> athmosphericPressure;
|
|
public NativeArray<float> diffusion;
|
|
|
|
public NativeArray<int4> cellCoords;
|
|
public NativeArray<BurstAabb> simplexBounds;
|
|
|
|
private ConstraintSorter<BurstContact> contactSorter;
|
|
|
|
public BurstSolverImpl(ObiSolver solver)
|
|
{
|
|
this.m_Solver = solver;
|
|
|
|
jobHandlePool = new JobHandlePool<BurstJobHandle>(4);
|
|
contactSorter = new ConstraintSorter<BurstContact>();
|
|
|
|
// Initialize collision world:
|
|
GetOrCreateColliderWorld();
|
|
colliderGrid.IncreaseReferenceCount();
|
|
|
|
deformableTriangles = new NativeList<int>(64, Allocator.Persistent);
|
|
|
|
// Initialize contact generation acceleration structure:
|
|
particleGrid = new ParticleGrid();
|
|
|
|
// Initialize constraint batcher:
|
|
collisionConstraintBatcher = new ConstraintBatcher<ContactProvider>(maxBatches);
|
|
fluidConstraintBatcher = new ConstraintBatcher<FluidInteractionProvider>(maxBatches);
|
|
|
|
// Initialize constraint arrays:
|
|
constraints = new IBurstConstraintsImpl[Oni.ConstraintTypeCount];
|
|
constraints[(int)Oni.ConstraintType.Tether] = new BurstTetherConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Volume] = new BurstVolumeConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Chain] = new BurstChainConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Bending] = new BurstBendConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Distance] = new BurstDistanceConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.ShapeMatching] = new BurstShapeMatchingConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.BendTwist] = new BurstBendTwistConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.StretchShear] = new BurstStretchShearConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Pin] = new BurstPinConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.ParticleCollision] = new BurstParticleCollisionConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Density] = new BurstDensityConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Collision] = new BurstColliderCollisionConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Skin] = new BurstSkinConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Aerodynamics] = new BurstAerodynamicConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Stitch] = new BurstStitchConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.ParticleFriction] = new BurstParticleFrictionConstraints(this);
|
|
constraints[(int)Oni.ConstraintType.Friction] = new BurstColliderFrictionConstraints(this);
|
|
|
|
var c = constraints[(int)Oni.ConstraintType.Collision] as BurstColliderCollisionConstraints;
|
|
c.CreateConstraintsBatch();
|
|
|
|
var f = constraints[(int)Oni.ConstraintType.Friction] as BurstColliderFrictionConstraints;
|
|
f.CreateConstraintsBatch();
|
|
}
|
|
|
|
public void Destroy()
|
|
{
|
|
for (int i = 0; i < constraints.Length; ++i)
|
|
if (constraints[i] != null)
|
|
constraints[i].Dispose();
|
|
|
|
// Get rid of particle and collider grids:
|
|
particleGrid.Dispose();
|
|
|
|
if (colliderGrid != null)
|
|
colliderGrid.DecreaseReferenceCount();
|
|
|
|
collisionConstraintBatcher.Dispose();
|
|
fluidConstraintBatcher.Dispose();
|
|
|
|
if (deformableTriangles.IsCreated)
|
|
deformableTriangles.Dispose();
|
|
if (simplexBounds.IsCreated)
|
|
simplexBounds.Dispose();
|
|
|
|
if (particleContacts.IsCreated)
|
|
particleContacts.Dispose();
|
|
if (particleBatchData.IsCreated)
|
|
particleBatchData.Dispose();
|
|
if (fluidInteractions.IsCreated)
|
|
fluidInteractions.Dispose();
|
|
if (fluidBatchData.IsCreated)
|
|
fluidBatchData.Dispose();
|
|
if (colliderContacts.IsCreated)
|
|
colliderContacts.Dispose();
|
|
|
|
}
|
|
|
|
public void ReleaseJobHandles()
|
|
{
|
|
jobHandlePool.ReleaseAll();
|
|
}
|
|
|
|
// Utility function to count scheduled jobs. Call it once per job.
|
|
// Will JobHandle.ScheduleBatchedJobs once there's a good bunch of scheduled jobs.
|
|
public void ScheduleBatchedJobsIfNeeded()
|
|
{
|
|
if (scheduledJobCounter++ > 16)
|
|
{
|
|
scheduledJobCounter = 0;
|
|
JobHandle.ScheduleBatchedJobs();
|
|
}
|
|
}
|
|
|
|
private void GetOrCreateColliderWorld()
|
|
{
|
|
colliderGrid = GameObject.FindObjectOfType<BurstColliderWorld>();
|
|
if (colliderGrid == null)
|
|
{
|
|
var world = new GameObject("BurstCollisionWorld", typeof(BurstColliderWorld));
|
|
colliderGrid = world.GetComponent<BurstColliderWorld>();
|
|
}
|
|
}
|
|
|
|
public void InitializeFrame(Vector4 translation, Vector4 scale, Quaternion rotation)
|
|
{
|
|
m_InertialFrame = new BurstInertialFrame(translation, scale, rotation);
|
|
}
|
|
|
|
public void UpdateFrame(Vector4 translation, Vector4 scale, Quaternion rotation, float deltaTime)
|
|
{
|
|
m_InertialFrame.Update(translation, scale, rotation, deltaTime);
|
|
}
|
|
|
|
public void ApplyFrame(float worldLinearInertiaScale, float worldAngularInertiaScale, float deltaTime)
|
|
{
|
|
// inverse linear part:
|
|
float4x4 linear = float4x4.TRS(float3.zero, inertialFrame.frame.rotation, math.rcp(inertialFrame.frame.scale.xyz));
|
|
float4x4 linearInv = math.transpose(linear);
|
|
|
|
// non-inertial frame accelerations:
|
|
float4 angularVel = math.mul(linearInv, math.mul(float4x4.Scale(inertialFrame.angularVelocity.xyz), linear)).diagonal();
|
|
float4 eulerAccel = math.mul(linearInv, math.mul(float4x4.Scale(inertialFrame.angularAcceleration.xyz), linear)).diagonal();
|
|
float4 inertialAccel = math.mul(linearInv, inertialFrame.acceleration);
|
|
|
|
var applyInertialForces = new ApplyInertialForcesJob()
|
|
{
|
|
activeParticles = activeParticles,
|
|
positions = positions,
|
|
velocities = velocities,
|
|
invMasses = invMasses,
|
|
angularVel = angularVel,
|
|
inertialAccel = inertialAccel,
|
|
eulerAccel = eulerAccel,
|
|
worldLinearInertiaScale = worldLinearInertiaScale,
|
|
worldAngularInertiaScale = worldAngularInertiaScale,
|
|
deltaTime = deltaTime,
|
|
};
|
|
|
|
applyInertialForces.Schedule(activeParticleCount, 64).Complete();
|
|
}
|
|
|
|
public int GetDeformableTriangleCount()
|
|
{
|
|
return deformableTriangles.Length / 3;
|
|
}
|
|
|
|
public void SetDeformableTriangles(int[] indices, int num, int destOffset)
|
|
{
|
|
if (destOffset + num >= deformableTriangles.Length / 3)
|
|
deformableTriangles.ResizeUninitialized((destOffset + num) * 3);
|
|
for (int i = 0; i < num * 3; ++i)
|
|
deformableTriangles[i + destOffset * 3] = indices[i];
|
|
}
|
|
|
|
public int RemoveDeformableTriangles(int num, int sourceOffset)
|
|
{
|
|
if (deformableTriangles.IsCreated)
|
|
{
|
|
int amount = deformableTriangles.Length / 3;
|
|
|
|
if (num < 0)
|
|
{
|
|
deformableTriangles.Clear();
|
|
return amount;
|
|
}
|
|
|
|
int set = ClampArrayAccess(amount, num, sourceOffset);
|
|
int end = sourceOffset + set;
|
|
|
|
// TODO: replace by built in method in 0.9.0
|
|
deformableTriangles.RemoveRangeBurst(sourceOffset * 3, (end - sourceOffset) * 3 );
|
|
|
|
return set;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public void SetSimplices(ObiNativeIntList simplices, SimplexCounts counts)
|
|
{
|
|
this.simplices = simplices.AsNativeArray<int>();
|
|
this.simplexCounts = counts;
|
|
|
|
if (simplexBounds.IsCreated)
|
|
simplexBounds.Dispose();
|
|
|
|
simplexBounds = new NativeArray<BurstAabb>(counts.simplexCount, Allocator.Persistent);
|
|
cellCoords = abstraction.cellCoords.AsNativeArray<int4>();
|
|
}
|
|
|
|
public void SetActiveParticles(ObiNativeIntList indices)
|
|
{
|
|
activeParticles = indices.AsNativeArray<int>();
|
|
}
|
|
|
|
int ClampArrayAccess(int size, int num, int offset)
|
|
{
|
|
return math.min(num, math.max(0, size - offset));
|
|
}
|
|
|
|
public JobHandle RecalculateInertiaTensors(JobHandle inputDeps)
|
|
{
|
|
var updateInertiaTensors = new UpdateInertiaTensorsJob()
|
|
{
|
|
activeParticles = activeParticles,
|
|
inverseMasses = abstraction.invMasses.AsNativeArray<float>(),
|
|
inverseRotationalMasses = abstraction.invRotationalMasses.AsNativeArray<float>(),
|
|
principalRadii = abstraction.principalRadii.AsNativeArray<float4>(),
|
|
inverseInertiaTensors = abstraction.invInertiaTensors.AsNativeArray<float4>(),
|
|
};
|
|
|
|
return updateInertiaTensors.Schedule(activeParticleCount, 128, inputDeps);
|
|
}
|
|
|
|
public void GetBounds(ref Vector3 min, ref Vector3 max)
|
|
{
|
|
int chunkSize = 4;
|
|
NativeArray<BurstAabb> bounds = new NativeArray<BurstAabb>(activeParticleCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
|
|
var particleBoundsJob = new ParticleToBoundsJob()
|
|
{
|
|
activeParticles = activeParticles,
|
|
positions = positions,
|
|
radii = principalRadii,
|
|
bounds = bounds
|
|
};
|
|
|
|
JobHandle reduction = particleBoundsJob.Schedule(activeParticles.Length, 64);
|
|
|
|
// parallel reduction:
|
|
int chunks = bounds.Length;
|
|
int stride = 1;
|
|
|
|
while (chunks > 1)
|
|
{
|
|
var reductionJob = new BoundsReductionJob()
|
|
{
|
|
bounds = bounds,
|
|
stride = stride,
|
|
size = chunkSize,
|
|
};
|
|
reduction = reductionJob.Schedule(chunks, 1, reduction);
|
|
|
|
chunks = (int)math.ceil(chunks / (float)chunkSize);
|
|
stride *= chunkSize;
|
|
}
|
|
|
|
reduction.Complete();
|
|
|
|
// the parallel reduction leaves the final bounds in the first entry:
|
|
if (bounds.Length > 0)
|
|
{
|
|
min = bounds[0].min.xyz;
|
|
max = bounds[0].max.xyz;
|
|
}
|
|
|
|
bounds.Dispose();
|
|
}
|
|
|
|
public void ResetForces()
|
|
{
|
|
abstraction.externalForces.WipeToZero();
|
|
abstraction.externalTorques.WipeToZero();
|
|
abstraction.wind.WipeToZero();
|
|
|
|
// We're at the end of a whole step (not a substep), so dispose of contact buffers:
|
|
if (particleContacts.IsCreated)
|
|
particleContacts.Dispose();
|
|
if (particleBatchData.IsCreated)
|
|
particleBatchData.Dispose();
|
|
if (colliderContacts.IsCreated)
|
|
colliderContacts.Dispose();
|
|
}
|
|
|
|
public int GetConstraintCount(Oni.ConstraintType type)
|
|
{
|
|
if ((int)type > 0 && (int)type < constraints.Length)
|
|
return constraints[(int)type].GetConstraintCount();
|
|
return 0;
|
|
}
|
|
|
|
public void GetCollisionContacts(Oni.Contact[] contacts, int count)
|
|
{
|
|
NativeArray<Oni.Contact>.Copy(colliderContacts.Reinterpret<Oni.Contact>(),0, contacts,0,count);
|
|
}
|
|
|
|
public void GetParticleCollisionContacts(Oni.Contact[] contacts, int count)
|
|
{
|
|
NativeArray<Oni.Contact>.Copy(particleContacts.Reinterpret<Oni.Contact>(),0, contacts,0,count);
|
|
}
|
|
|
|
public void SetParameters(Oni.SolverParameters parameters)
|
|
{
|
|
// No need to implement. This backend grabs parameters from the abstraction when it needs them.
|
|
}
|
|
|
|
public void SetConstraintGroupParameters(Oni.ConstraintType type, ref Oni.ConstraintParameters parameters)
|
|
{
|
|
// No need to implement. This backend grabs parameters from the abstraction when it needs them.
|
|
}
|
|
|
|
public void ParticleCountChanged(ObiSolver solver)
|
|
{
|
|
positions = abstraction.positions.AsNativeArray<float4>();
|
|
restPositions = abstraction.restPositions.AsNativeArray<float4>();
|
|
prevPositions = abstraction.prevPositions.AsNativeArray<float4>();
|
|
renderablePositions = abstraction.renderablePositions.AsNativeArray<float4>();
|
|
|
|
orientations = abstraction.orientations.AsNativeArray<quaternion>();
|
|
restOrientations = abstraction.restOrientations.AsNativeArray<quaternion>();
|
|
prevOrientations = abstraction.prevOrientations.AsNativeArray<quaternion>();
|
|
renderableOrientations = abstraction.renderableOrientations.AsNativeArray<quaternion>();
|
|
|
|
velocities = abstraction.velocities.AsNativeArray<float4>();
|
|
angularVelocities = abstraction.angularVelocities.AsNativeArray<float4>();
|
|
|
|
invMasses = abstraction.invMasses.AsNativeArray<float>();
|
|
invRotationalMasses = abstraction.invRotationalMasses.AsNativeArray<float>();
|
|
invInertiaTensors = abstraction.invInertiaTensors.AsNativeArray<float4>();
|
|
|
|
externalForces = abstraction.externalForces.AsNativeArray<float4>();
|
|
externalTorques = abstraction.externalTorques.AsNativeArray<float4>();
|
|
wind = abstraction.wind.AsNativeArray<float4>();
|
|
|
|
positionDeltas = abstraction.positionDeltas.AsNativeArray<float4>();
|
|
orientationDeltas = abstraction.orientationDeltas.AsNativeArray<quaternion>();
|
|
positionConstraintCounts = abstraction.positionConstraintCounts.AsNativeArray<int>();
|
|
orientationConstraintCounts = abstraction.orientationConstraintCounts.AsNativeArray<int>();
|
|
|
|
collisionMaterials = abstraction.collisionMaterials.AsNativeArray<int>();
|
|
phases = abstraction.phases.AsNativeArray<int>();
|
|
filters = abstraction.filters.AsNativeArray<int>();
|
|
anisotropies = abstraction.anisotropies.AsNativeArray<float4>();
|
|
principalRadii = abstraction.principalRadii.AsNativeArray<float4>();
|
|
normals = abstraction.normals.AsNativeArray<float4>();
|
|
|
|
vorticities = abstraction.vorticities.AsNativeArray<float4>();
|
|
fluidData = abstraction.fluidData.AsNativeArray<float4>();
|
|
userData = abstraction.userData.AsNativeArray<float4>();
|
|
smoothingRadii = abstraction.smoothingRadii.AsNativeArray<float>();
|
|
buoyancies = abstraction.buoyancies.AsNativeArray<float>();
|
|
restDensities = abstraction.restDensities.AsNativeArray<float>();
|
|
viscosities = abstraction.viscosities.AsNativeArray<float>();
|
|
surfaceTension = abstraction.surfaceTension.AsNativeArray<float>();
|
|
vortConfinement = abstraction.vortConfinement.AsNativeArray<float>();
|
|
athmosphericDrag = abstraction.atmosphericDrag.AsNativeArray<float>();
|
|
athmosphericPressure = abstraction.atmosphericPressure.AsNativeArray<float>();
|
|
diffusion = abstraction.diffusion.AsNativeArray<float>();
|
|
}
|
|
|
|
public void SetRigidbodyArrays(ObiSolver solver)
|
|
{
|
|
// No need to implement. This backend grabs arrays from the abstraction when it needs them.
|
|
}
|
|
|
|
public IConstraintsBatchImpl CreateConstraintsBatch(Oni.ConstraintType type)
|
|
{
|
|
return constraints[(int)type].CreateConstraintsBatch();
|
|
}
|
|
|
|
public void DestroyConstraintsBatch(IConstraintsBatchImpl batch)
|
|
{
|
|
if (batch != null)
|
|
constraints[(int)batch.constraintType].RemoveBatch(batch);
|
|
}
|
|
|
|
public IObiJobHandle CollisionDetection(float stepTime)
|
|
{
|
|
var fluidHandle = FindFluidParticles();
|
|
|
|
var inertiaUpdate = RecalculateInertiaTensors(fluidHandle);
|
|
|
|
return jobHandlePool.Borrow().SetHandle(GenerateContacts(inertiaUpdate, stepTime));
|
|
}
|
|
|
|
protected JobHandle FindFluidParticles()
|
|
{
|
|
var d = constraints[(int)Oni.ConstraintType.Density] as BurstDensityConstraints;
|
|
|
|
// Update positions:
|
|
var findFluidJob = new FindFluidParticlesJob()
|
|
{
|
|
activeParticles = activeParticles,
|
|
phases = m_Solver.phases.AsNativeArray<int>(),
|
|
fluidParticles = d.fluidParticles,
|
|
};
|
|
|
|
return findFluidJob.Schedule();
|
|
}
|
|
|
|
protected JobHandle UpdateSimplexBounds(JobHandle inputDeps, float deltaTime)
|
|
{
|
|
var buildAabbs = new BuildSimplexAabbs
|
|
{
|
|
radii = principalRadii,
|
|
fluidRadii = smoothingRadii,
|
|
positions = positions,
|
|
velocities = velocities,
|
|
|
|
simplices = simplices,
|
|
simplexCounts = simplexCounts,
|
|
simplexBounds = simplexBounds,
|
|
|
|
particleMaterialIndices = abstraction.collisionMaterials.AsNativeArray<int>(),
|
|
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
|
collisionMargin = abstraction.parameters.collisionMargin,
|
|
continuousCollisionDetection = abstraction.parameters.continuousCollisionDetection,
|
|
dt = deltaTime,
|
|
};
|
|
return buildAabbs.Schedule(simplexCounts.simplexCount, 32, inputDeps);
|
|
}
|
|
|
|
protected JobHandle GenerateContacts(JobHandle inputDeps, float deltaTime)
|
|
{
|
|
// Dispose of previous fluid interactions.
|
|
// We need fluid data during interpolation, for anisotropic fluid particles. For this reason,
|
|
// we can't dispose of these arrays in ResetForces() at the end of each full step. They must use persistent allocation.
|
|
|
|
if (fluidInteractions.IsCreated)
|
|
fluidInteractions.Dispose();
|
|
if (fluidBatchData.IsCreated)
|
|
fluidBatchData.Dispose();
|
|
|
|
// get constraint parameters for constraint types that depend on broadphases:
|
|
var collisionParameters = m_Solver.GetConstraintParameters(Oni.ConstraintType.Collision);
|
|
var particleCollisionParameters = m_Solver.GetConstraintParameters(Oni.ConstraintType.ParticleCollision);
|
|
var densityParameters = m_Solver.GetConstraintParameters(Oni.ConstraintType.Density);
|
|
|
|
// if no enabled constraints that require broadphase info, skip it entirely.
|
|
if (collisionParameters.enabled ||
|
|
particleCollisionParameters.enabled ||
|
|
densityParameters.enabled)
|
|
{
|
|
// update the bounding box of each simplex:
|
|
inputDeps = UpdateSimplexBounds(inputDeps, deltaTime);
|
|
|
|
|
|
// generate particle-particle and particle-collider interactions in parallel:
|
|
JobHandle generateParticleInteractionsHandle = inputDeps, generateContactsHandle = inputDeps;
|
|
|
|
// particle-particle interactions (contacts, fluids)
|
|
if (particleCollisionParameters.enabled || densityParameters.enabled)
|
|
{
|
|
particleGrid.Update(this, deltaTime, inputDeps);
|
|
generateParticleInteractionsHandle = particleGrid.GenerateContacts(this, deltaTime);
|
|
}
|
|
|
|
// particle-collider interactions (contacts)
|
|
if (collisionParameters.enabled)
|
|
{
|
|
generateContactsHandle = colliderGrid.GenerateContacts(this, deltaTime, inputDeps);
|
|
}
|
|
|
|
JobHandle.CombineDependencies(generateParticleInteractionsHandle, generateContactsHandle).Complete();
|
|
|
|
// allocate arrays for interactions and batch data:
|
|
particleContacts = new NativeArray<BurstContact>(particleGrid.particleContactQueue.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
|
particleBatchData = new NativeArray<BatchData>(maxBatches, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
|
|
|
fluidInteractions = new NativeArray<FluidInteraction>(particleGrid.fluidInteractionQueue.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
fluidBatchData = new NativeArray<BatchData>(maxBatches, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
|
|
colliderContacts = new NativeArray<BurstContact>(colliderGrid.colliderContactQueue.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
|
|
|
// dequeue contacts/interactions into temporary arrays:
|
|
var rawParticleContacts = new NativeArray<BurstContact>(particleGrid.particleContactQueue.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
|
var sortedParticleContacts = new NativeArray<BurstContact>(particleGrid.particleContactQueue.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
|
var rawFluidInteractions = new NativeArray<FluidInteraction>(particleGrid.fluidInteractionQueue.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
|
|
|
DequeueIntoArrayJob<BurstContact> dequeueParticleContacts = new DequeueIntoArrayJob<BurstContact>()
|
|
{
|
|
InputQueue = particleGrid.particleContactQueue,
|
|
OutputArray = rawParticleContacts
|
|
};
|
|
|
|
DequeueIntoArrayJob<FluidInteraction> dequeueFluidInteractions = new DequeueIntoArrayJob<FluidInteraction>()
|
|
{
|
|
InputQueue = particleGrid.fluidInteractionQueue,
|
|
OutputArray = rawFluidInteractions
|
|
};
|
|
|
|
DequeueIntoArrayJob<BurstContact> dequeueColliderContacts = new DequeueIntoArrayJob<BurstContact>()
|
|
{
|
|
InputQueue = colliderGrid.colliderContactQueue,
|
|
OutputArray = colliderContacts
|
|
};
|
|
|
|
var dequeueHandle = JobHandle.CombineDependencies(dequeueParticleContacts.Schedule(), dequeueFluidInteractions.Schedule(), dequeueColliderContacts.Schedule());
|
|
|
|
// Sort contacts for jitter-free gauss-seidel (sequential) solving:
|
|
dequeueHandle = contactSorter.SortConstraints(simplexCounts.simplexCount, rawParticleContacts, ref sortedParticleContacts, dequeueHandle);
|
|
|
|
ContactProvider contactProvider = new ContactProvider()
|
|
{
|
|
contacts = sortedParticleContacts,
|
|
sortedContacts = particleContacts,
|
|
simplices = simplices,
|
|
simplexCounts = simplexCounts
|
|
};
|
|
|
|
FluidInteractionProvider fluidProvider = new FluidInteractionProvider()
|
|
{
|
|
interactions = rawFluidInteractions,
|
|
sortedInteractions = fluidInteractions,
|
|
};
|
|
|
|
// batch particle contacts:
|
|
var activeParticleBatchCount = new NativeArray<int>(1, Allocator.TempJob);
|
|
var particleBatchHandle = collisionConstraintBatcher.BatchConstraints(ref contactProvider, particleCount, ref particleBatchData, ref activeParticleBatchCount, dequeueHandle);
|
|
|
|
// batch fluid interactions:
|
|
var activeFluidBatchCount = new NativeArray<int>(1, Allocator.TempJob);
|
|
var fluidBatchHandle = fluidConstraintBatcher.BatchConstraints(ref fluidProvider, particleCount, ref fluidBatchData, ref activeFluidBatchCount, dequeueHandle);
|
|
|
|
JobHandle.CombineDependencies(particleBatchHandle, fluidBatchHandle).Complete();
|
|
|
|
// Generate particle contact/friction batches:
|
|
var pc = constraints[(int)Oni.ConstraintType.ParticleCollision] as BurstParticleCollisionConstraints;
|
|
var pf = constraints[(int)Oni.ConstraintType.ParticleFriction] as BurstParticleFrictionConstraints;
|
|
|
|
for (int i = 0; i < pc.batches.Count; ++i)
|
|
pc.batches[i].enabled = false;
|
|
|
|
for (int i = 0; i < pf.batches.Count; ++i)
|
|
pf.batches[i].enabled = false;
|
|
|
|
for (int i = 0; i < activeParticleBatchCount[0]; ++i)
|
|
{
|
|
// create extra batches if not enough:
|
|
if (i == pc.batches.Count)
|
|
{
|
|
pc.CreateConstraintsBatch();
|
|
pf.CreateConstraintsBatch();
|
|
}
|
|
|
|
pc.batches[i].enabled = true;
|
|
pf.batches[i].enabled = true;
|
|
|
|
(pc.batches[i] as BurstParticleCollisionConstraintsBatch).batchData = particleBatchData[i];
|
|
(pf.batches[i] as BurstParticleFrictionConstraintsBatch ).batchData = particleBatchData[i];
|
|
}
|
|
|
|
// Generate fluid interaction batches:
|
|
var dc = constraints[(int)Oni.ConstraintType.Density] as BurstDensityConstraints;
|
|
|
|
for (int i = 0; i < dc.batches.Count; ++i)
|
|
dc.batches[i].enabled = false;
|
|
|
|
for (int i = 0; i < activeFluidBatchCount[0]; ++i)
|
|
{
|
|
// create extra batches if not enough:
|
|
if (i == dc.batches.Count)
|
|
dc.CreateConstraintsBatch();
|
|
|
|
dc.batches[i].enabled = true;
|
|
|
|
(dc.batches[i] as BurstDensityConstraintsBatch).batchData = fluidBatchData[i];
|
|
}
|
|
|
|
// dispose of temporary buffers:
|
|
rawParticleContacts.Dispose();
|
|
rawFluidInteractions.Dispose();
|
|
sortedParticleContacts.Dispose();
|
|
activeParticleBatchCount.Dispose();
|
|
activeFluidBatchCount.Dispose();
|
|
|
|
}
|
|
|
|
return inputDeps;
|
|
}
|
|
|
|
public IObiJobHandle Substep(float stepTime, float substepTime, int substeps)
|
|
{
|
|
// Apply aerodynamics
|
|
JobHandle aerodynamicsHandle = constraints[(int)Oni.ConstraintType.Aerodynamics].Project(new JobHandle(), stepTime, substepTime, substeps);
|
|
|
|
// Predict positions:
|
|
var predictPositions = new PredictPositionsJob()
|
|
{
|
|
activeParticles = activeParticles,
|
|
phases = phases,
|
|
buoyancies = buoyancies,
|
|
|
|
externalForces = externalForces,
|
|
inverseMasses = invMasses,
|
|
positions = positions,
|
|
previousPositions = prevPositions,
|
|
velocities = velocities,
|
|
|
|
externalTorques = externalTorques,
|
|
inverseRotationalMasses = invRotationalMasses,
|
|
orientations = orientations,
|
|
previousOrientations = prevOrientations,
|
|
angularVelocities = angularVelocities,
|
|
|
|
gravity = new float4(m_Solver.parameters.gravity, 0),
|
|
deltaTime = substepTime,
|
|
is2D = abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D
|
|
};
|
|
|
|
JobHandle predictPositionsHandle = predictPositions.Schedule(activeParticles.Length, 128, aerodynamicsHandle);
|
|
|
|
// Project position constraints:
|
|
JobHandle projectionHandle = ApplyConstraints(predictPositionsHandle, stepTime, substepTime, substeps);
|
|
|
|
// Update velocities:
|
|
var updateVelocitiesJob = new UpdateVelocitiesJob()
|
|
{
|
|
activeParticles = activeParticles,
|
|
|
|
inverseMasses = invMasses,
|
|
previousPositions = prevPositions,
|
|
positions = positions,
|
|
velocities = velocities,
|
|
|
|
inverseRotationalMasses = invRotationalMasses,
|
|
previousOrientations = prevOrientations,
|
|
orientations = orientations,
|
|
angularVelocities = angularVelocities,
|
|
|
|
deltaTime = substepTime,
|
|
is2D = abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D
|
|
};
|
|
|
|
JobHandle updateVelocitiesHandle = updateVelocitiesJob.Schedule(activeParticles.Length, 128, projectionHandle);
|
|
|
|
// velocity constraints:
|
|
JobHandle velocityCorrectionsHandle = ApplyVelocityCorrections(updateVelocitiesHandle, substepTime);
|
|
|
|
// Update positions:
|
|
var updatePositionsJob = new UpdatePositionsJob()
|
|
{
|
|
activeParticles = activeParticles,
|
|
positions = positions,
|
|
previousPositions = prevPositions,
|
|
velocities = velocities,
|
|
orientations = orientations,
|
|
previousOrientations = prevOrientations,
|
|
angularVelocities = angularVelocities,
|
|
velocityScale = math.pow(1 - math.clamp(m_Solver.parameters.damping, 0, 1), substepTime),
|
|
sleepThreshold = m_Solver.parameters.sleepThreshold
|
|
};
|
|
|
|
JobHandle updatePositionsHandle = updatePositionsJob.Schedule(activeParticles.Length, 128, velocityCorrectionsHandle);
|
|
|
|
return jobHandlePool.Borrow().SetHandle(updatePositionsHandle);
|
|
}
|
|
|
|
private JobHandle ApplyVelocityCorrections(JobHandle inputDeps, float deltaTime)
|
|
{
|
|
var densityParameters = m_Solver.GetConstraintParameters(Oni.ConstraintType.Density);
|
|
|
|
if (densityParameters.enabled)
|
|
{
|
|
var d = constraints[(int)Oni.ConstraintType.Density] as BurstDensityConstraints;
|
|
if (d != null)
|
|
{
|
|
return d.ApplyVelocityCorrections(inputDeps, deltaTime);
|
|
}
|
|
}
|
|
return inputDeps;
|
|
}
|
|
|
|
private JobHandle ApplyConstraints(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
|
{
|
|
|
|
// calculate max amount of iterations required, and initialize constraints..
|
|
int maxIterations = 0;
|
|
for (int i = 0; i < Oni.ConstraintTypeCount; ++i)
|
|
{
|
|
var parameters = m_Solver.GetConstraintParameters((Oni.ConstraintType)i);
|
|
if (parameters.enabled)
|
|
{
|
|
maxIterations = math.max(maxIterations, parameters.iterations);
|
|
inputDeps = constraints[i].Initialize(inputDeps, substepTime);
|
|
}
|
|
}
|
|
|
|
// calculate iteration paddings:
|
|
for (int i = 0; i < Oni.ConstraintTypeCount; ++i)
|
|
{
|
|
var parameters = m_Solver.GetConstraintParameters((Oni.ConstraintType)i);
|
|
if (parameters.enabled && parameters.iterations > 0)
|
|
padding[i] = (int)math.ceil(maxIterations / (float)parameters.iterations);
|
|
else
|
|
padding[i] = maxIterations;
|
|
}
|
|
|
|
// perform projection iterations:
|
|
for (int i = 1; i < maxIterations; ++i)
|
|
{
|
|
for (int j = 0; j < Oni.ConstraintTypeCount; ++j)
|
|
{
|
|
if (j != (int)Oni.ConstraintType.Aerodynamics)
|
|
{
|
|
var parameters = m_Solver.GetConstraintParameters((Oni.ConstraintType)j);
|
|
if (parameters.enabled && i % padding[j] == 0)
|
|
inputDeps = constraints[j].Project(inputDeps, stepTime, substepTime, substeps);
|
|
}
|
|
}
|
|
}
|
|
|
|
// final iteration, all groups together:
|
|
for (int i = 0; i < Oni.ConstraintTypeCount; ++i)
|
|
{
|
|
if (i != (int)Oni.ConstraintType.Aerodynamics)
|
|
{
|
|
var parameters = m_Solver.GetConstraintParameters((Oni.ConstraintType)i);
|
|
if (parameters.enabled && parameters.iterations > 0)
|
|
inputDeps = constraints[i].Project(inputDeps, stepTime, substepTime, substeps);
|
|
}
|
|
}
|
|
|
|
// Despite friction constraints being applied after collision (since coulomb friction depends on normal impulse)
|
|
// we perform a collision iteration right at the end to ensure the final state meets the Signorini-Fichera conditions.
|
|
var param = m_Solver.GetConstraintParameters(Oni.ConstraintType.ParticleCollision);
|
|
if (param.enabled && param.iterations > 0)
|
|
inputDeps = constraints[(int)Oni.ConstraintType.ParticleCollision].Project(inputDeps, stepTime, substepTime, substeps);
|
|
param = m_Solver.GetConstraintParameters(Oni.ConstraintType.Collision);
|
|
if (param.enabled && param.iterations > 0)
|
|
inputDeps = constraints[(int)Oni.ConstraintType.Collision].Project(inputDeps, stepTime, substepTime, substeps);
|
|
|
|
return inputDeps;
|
|
}
|
|
|
|
public void ApplyInterpolation(ObiNativeVector4List startPositions, ObiNativeQuaternionList startOrientations, float stepTime, float unsimulatedTime)
|
|
{
|
|
|
|
// Interpolate particle positions and orientations.
|
|
var interpolate = new InterpolationJob()
|
|
{
|
|
positions = positions,
|
|
startPositions = startPositions.AsNativeArray<float4>(),
|
|
renderablePositions = renderablePositions,
|
|
|
|
orientations = orientations,
|
|
startOrientations = startOrientations.AsNativeArray<quaternion>(),
|
|
renderableOrientations = renderableOrientations,
|
|
|
|
deltaTime = stepTime,
|
|
unsimulatedTime = unsimulatedTime,
|
|
interpolationMode = m_Solver.parameters.interpolation
|
|
};
|
|
|
|
JobHandle jobHandle = interpolate.Schedule(m_Solver.positions.count, 128);
|
|
|
|
// Update deformable triangle normals
|
|
var updateNormals = new UpdateNormalsJob()
|
|
{
|
|
renderPositions = renderablePositions,
|
|
deformableTriangles = deformableTriangles,
|
|
normals = normals
|
|
};
|
|
|
|
jobHandle = updateNormals.Schedule(jobHandle);
|
|
|
|
// fluid laplacian/anisotropy:
|
|
var d = constraints[(int)Oni.ConstraintType.Density] as BurstDensityConstraints;
|
|
if (d != null)
|
|
jobHandle = d.CalculateAnisotropyLaplacianSmoothing(jobHandle);
|
|
|
|
// update axis:
|
|
var updatePrincipalAxis = new UpdatePrincipalAxisJob()
|
|
{
|
|
activeParticles = activeParticles,
|
|
renderableOrientations = renderableOrientations,
|
|
phases = phases,
|
|
principalRadii = principalRadii,
|
|
principalAxis = anisotropies,
|
|
};
|
|
|
|
jobHandle = updatePrincipalAxis.Schedule(activeParticles.Length, 128, jobHandle);
|
|
|
|
jobHandle.Complete();
|
|
|
|
}
|
|
|
|
public void InterpolateDiffuseProperties(ObiNativeVector4List properties,
|
|
ObiNativeVector4List diffusePositions,
|
|
ObiNativeVector4List diffuseProperties,
|
|
ObiNativeIntList neighbourCount,
|
|
int diffuseCount)
|
|
{
|
|
particleGrid.InterpolateDiffuseProperties(this,
|
|
properties.AsNativeArray<float4>(),
|
|
diffusePositions.AsNativeArray<float4>(),
|
|
diffuseProperties.AsNativeArray<float4>(),
|
|
neighbourCount.AsNativeArray<int>(),
|
|
diffuseCount).Complete();
|
|
}
|
|
|
|
public void SpatialQuery(ObiNativeQueryShapeList shapes, ObiNativeAffineTransformList transforms, ObiNativeQueryResultList results)
|
|
{
|
|
var resultsQueue = new NativeQueue<BurstQueryResult>(Allocator.Persistent);
|
|
|
|
particleGrid.SpatialQuery(this,
|
|
shapes.AsNativeArray<BurstQueryShape>(),
|
|
transforms.AsNativeArray<BurstAffineTransform>(),
|
|
resultsQueue).Complete();
|
|
|
|
int count = resultsQueue.Count;
|
|
results.ResizeUninitialized(count);
|
|
|
|
var dequeueQueryResults = new DequeueIntoArrayJob<BurstQueryResult>()
|
|
{
|
|
InputQueue = resultsQueue,
|
|
OutputArray = results.AsNativeArray<BurstQueryResult>()
|
|
};
|
|
|
|
var dequeueHandle = dequeueQueryResults.Schedule();
|
|
|
|
var distanceJob = new CalculateQueryDistances()
|
|
{
|
|
prevPositions = prevPositions,
|
|
prevOrientations = prevOrientations,
|
|
radii = principalRadii,
|
|
simplices = simplices,
|
|
simplexCounts = simplexCounts,
|
|
queryResults = results.AsNativeArray<BurstQueryResult>()
|
|
};
|
|
|
|
distanceJob.Schedule(count, 16, dequeueHandle).Complete();
|
|
|
|
resultsQueue.Dispose();
|
|
}
|
|
|
|
public int GetParticleGridSize()
|
|
{
|
|
return particleGrid.grid.usedCells.Length;
|
|
}
|
|
public void GetParticleGrid(ObiNativeAabbList cells)
|
|
{
|
|
particleGrid.GetCells(cells);
|
|
}
|
|
}
|
|
}
|
|
#endif
|