Files
Fishing2/Assets/Obi/Scripts/Common/Backends/Burst/Solver/BurstSolverImpl.cs
2026-01-22 22:08:21 +08:00

340 lines
54 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
{
public ObiSolver abstraction { get; }
public int particleCount
{
get { return abstraction.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(); }
}
public uint activeFoamParticleCount { private set; get; }
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];
// job handle:
private BurstJobHandle jobHandle;
// particle contact generation:
public ParticleGrid particleGrid;
public NativeArray<BatchData> particleBatchData;
// fluid interaction generation:
public NativeArray<FluidInteraction> fluidInteractions;
public NativeArray<BatchData> fluidBatchData;
// collider contact generation:
private BurstColliderWorld colliderGrid;
// deformable triangles:
private NativeArray<int> deformableTriangles;
private NativeArray<float2> deformableUVs;
// deformable edges:
private NativeArray<int> deformableEdges;
// simplices:
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<int> activeParticles;
public NativeArray<int> deadParticles;
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> 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<float4> colors;
public NativeArray<int> collisionMaterials;
public NativeArray<int> phases;
public NativeArray<int> filters;
public NativeArray<float4> renderableRadii;
public NativeArray<float4> principalRadii;
public NativeArray<float4> normals;
private NativeArray<float4> tangents;
public NativeArray<float> life;
public NativeArray<float4> fluidData;
public NativeArray<float4> userData;
public NativeArray<float4> fluidInterface;
public NativeArray<float4> fluidMaterials;
public NativeArray<float4> fluidMaterials2;
public NativeArray<float4x4> anisotropies;
// aux foam data:
public NativeArray<float4> auxPositions;
public NativeArray<float4> auxVelocities;
public NativeArray<float4> auxColors;
public NativeArray<float4> auxAttributes;
public NativeArray<int4> cellCoords;
public NativeArray<BurstAabb> simplexBounds;
public NativeArray<BurstAabb> reducedBounds;
public BurstAabb solverBounds;
private ConstraintSorter<BurstContact> contactSorter;
public BurstSolverImpl(ObiSolver solver)
{
this.abstraction = solver;
jobHandle = new BurstJobHandle();
contactSorter = new ConstraintSorter<BurstContact>();
// Initialize collision world:
GetOrCreateColliderWorld();
colliderGrid.IncreaseReferenceCount();
// 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);
constraints[(int)Oni.ConstraintType.Pinhole] = new BurstPinholeConstraints(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 (simplexBounds.IsCreated)
simplexBounds.Dispose();
if (reducedBounds.IsCreated)
reducedBounds.Dispose();
if (tangents.IsCreated)
tangents.Dispose();
if (particleBatchData.IsCreated)
particleBatchData.Dispose();
if (fluidInteractions.IsCreated)
fluidInteractions.Dispose();
if (fluidBatchData.IsCreated)
fluidBatchData.Dispose();
if (auxPositions.IsCreated)
auxPositions.Dispose();
if (auxVelocities.IsCreated)
auxVelocities.Dispose();
if (auxColors.IsCreated)
auxColors.Dispose();
if (auxAttributes.IsCreated)
auxAttributes.Dispose();
}
// 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 IObiJobHandle 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,
wind = wind,
ambientWind = new float4(abstraction.parameters.ambientWind, 0),
inertialFrame = inertialFrame,
deltaTime = deltaTime,
inertialWind = abstraction.windSpace == Space.World
};
jobHandle.jobHandle = applyInertialForces.Schedule(activeParticleCount, 64);
return jobHandle;
}
public void SetDeformableTriangles(ObiNativeIntList indices, ObiNativeVector2List uvs)
{
deformableTriangles = indices.AsNativeArray<int>();
deformableUVs = uvs.AsNativeArray<float2>();
}
public void SetDeformableEdges(ObiNativeIntList indices)
{
deformableEdges = indices.AsNativeArray<int>();
}
public void SetSimplices(ObiNativeIntList simplices, SimplexCounts counts)
{
this.simplices = simplices.AsNativeArray<int>();
this.simplexCounts = counts;
cellCoords = abstraction.cellCoords.AsNativeArray<int4>();
if (simplexBounds.IsCreated)
simplexBounds.Dispose();
simplexBounds = new NativeArray<BurstAabb>(counts.simplexCount, Allocator.Persistent);
if (reducedBounds.IsCreated)
reducedBounds.Dispose();
reducedBounds = new NativeArray<BurstAabb>(counts.simplexCount, Allocator.Persistent);
}
public void SetActiveParticles(ObiNativeIntList activeIndices)
{
activeParticles = activeIndices.AsNativeArray<int>();
}
public IObiJobHandle UpdateBounds(IObiJobHandle inputDeps, float stepTime)
{
BurstJobHandle burstHandle = inputDeps as BurstJobHandle;
if (burstHandle == null)
return inputDeps;
// calculate bounding boxes for all simplices:
var boundsJob = new CalculateSimplexBoundsJob()
{
radii = principalRadii,
fluidMaterials = fluidMaterials,
positions = positions,
velocities = velocities,
simplices = simplices,
simplexCounts = simplexCounts,
particleMaterialIndices = collisionMaterials,
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
parameters = abstraction.parameters,
simplexBounds = simplexBounds,
reducedBounds = reducedBounds,
dt = stepTime
};
burstHandle.jobHandle = boundsJob.Schedule(simplexCounts.simplexCount, 64, burstHandle.jobHandle);
// parallel reduction:
int chunkSize = 4;
int chunks = simplexCounts.simplexCount;
int stride = 1;
while (chunks > 1)
{
var reductionJob = new BoundsReductionJob()
{
bounds = reducedBounds,
stride = stride,
size = chunkSize,
};
burstHandle.jobHandle = reductionJob.Schedule(chunks, 1, burstHandle.jobHandle);
chunks = (int)math.ceil(chunks / (float)chunkSize);
stride *= chunkSize;
}
var countRef = abstraction.deadParticles.GetCountReference(Allocator.TempJob);
var lifetimeJob = new UpdateParticleLifetimesJob
{
activeParticles = activeParticles,
life = life,
deadParticles = deadParticles,
deadParticleCount = countRef,
dt = stepTime,
};
burstHandle.jobHandle = lifetimeJob.Schedule(activeParticleCount, 64, burstHandle.jobHandle);
burstHandle.jobHandle.Complete();
abstraction.deadParticles.count = countRef.Value;
countRef.Dispose();
return burstHandle;
}
public void GetBounds(ref Vector3 min, ref Vector3 max)
{
// update solver bounds struct:
if (reducedBounds.IsCreated && reducedBounds.Length > 0)
{
solverBounds.min = reducedBounds[0].min;
solverBounds.max = reducedBounds[0].max;
}
min = solverBounds.min.xyz;
max = solverBounds.max.xyz;
}
public int GetConstraintCount(Oni.ConstraintType type)
{
if ((int)type > 0 && (int)type < constraints.Length)
return constraints[(int)type].GetConstraintCount();
return 0;
}
public void SetParameters(Oni.SolverParameters parameters)
{
}
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)
{
deadParticles = abstraction.deadParticles.AsNativeArray<int>(abstraction.deadParticles.capacity);
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>();
colors = abstraction.colors.AsNativeArray<float4>();
velocities = abstraction.velocities.AsNativeArray<float4>();
angularVelocities = abstraction.angularVelocities.AsNativeArray<float4>();
invMasses = abstraction.invMasses.AsNativeArray<float>();
invRotationalMasses = abstraction.invRotationalMasses.AsNativeArray<float>();
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>();
renderableRadii = abstraction.renderableRadii.AsNativeArray<float4>();
principalRadii = abstraction.principalRadii.AsNativeArray<float4>();
normals = abstraction.normals.AsNativeArray<float4>();
life = abstraction.life.AsNativeArray<float>();
fluidData = abstraction.fluidData.AsNativeArray<float4>();
userData = abstraction.userData.AsNativeArray<float4>();
fluidInterface = abstraction.fluidInterface.AsNativeArray<float4>();
fluidMaterials = abstraction.fluidMaterials.AsNativeArray<float4>();
fluidMaterials2 = abstraction.fluidMaterials2.AsNativeArray<float4>();
anisotropies = abstraction.anisotropies.AsNativeArray<float4x4>();
cellCoords = abstraction.cellCoords.AsNativeArray<int4>();
if (tangents.IsCreated)
tangents.Dispose();
tangents = new NativeArray<float4>(normals.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
}
public void MaxFoamParticleCountChanged(ObiSolver solver)
{
if (auxPositions.IsCreated)
auxPositions.Dispose();
if (auxVelocities.IsCreated)
auxVelocities.Dispose();
if (auxColors.IsCreated)
auxColors.Dispose();
if (auxAttributes.IsCreated)
auxAttributes.Dispose();
auxPositions = new NativeArray<float4>((int)abstraction.maxFoamParticles, Allocator.Persistent);
auxVelocities = new NativeArray<float4>((int)abstraction.maxFoamParticles, Allocator.Persistent);
auxColors = new NativeArray<float4>((int)abstraction.maxFoamParticles, Allocator.Persistent);
auxAttributes = new NativeArray<float4>((int)abstraction.maxFoamParticles, Allocator.Persistent);
}
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 void FinishSimulation()
{
// Wipe all forces to zero. However we can't wipe wind here, since we
// need wind values during interpolation to calculate rope normals.
abstraction.externalForces.WipeToZero();
abstraction.externalTorques.WipeToZero();
// store current end positions as the start positions for the next step.
abstraction.startPositions.CopyFrom(abstraction.endPositions);
abstraction.startOrientations.CopyFrom(abstraction.endOrientations);
abstraction.endPositions.CopyFrom(abstraction.positions);
abstraction.endOrientations.CopyFrom(abstraction.orientations);
}
public void PushData()
{
}
public void RequestReadback()
{
}
public IObiJobHandle CollisionDetection(IObiJobHandle inputDeps, float stepTime)
{
BurstJobHandle burstHandle = inputDeps as BurstJobHandle;
if (burstHandle == null)
return inputDeps;
burstHandle.jobHandle = FindFluidParticles(burstHandle.jobHandle);
burstHandle.jobHandle = GenerateContacts(burstHandle.jobHandle, stepTime);
return burstHandle;
}
protected JobHandle FindFluidParticles(JobHandle inputDeps)
{
var d = constraints[(int)Oni.ConstraintType.Density] as BurstDensityConstraints;
// Update positions:
var findFluidJob = new FindFluidParticlesJob()
{
activeParticles = activeParticles,
phases = phases,
fluidParticles = d.fluidParticles,
};
return findFluidJob.Schedule(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();
if (particleBatchData.IsCreated)
particleBatchData.Dispose();
// get constraint parameters for constraint types that depend on broadphases:
var collisionParameters = abstraction.GetConstraintParameters(Oni.ConstraintType.Collision);
var particleCollisionParameters = abstraction.GetConstraintParameters(Oni.ConstraintType.ParticleCollision);
var densityParameters = abstraction.GetConstraintParameters(Oni.ConstraintType.Density);
// if no enabled constraints that require broadphase info, skip it entirely.
if (collisionParameters.enabled ||
particleCollisionParameters.enabled ||
densityParameters.enabled)
{
// 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, 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:
particleBatchData = new NativeArray<BatchData>(maxBatches, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
fluidInteractions = new NativeArray<FluidInteraction>(particleGrid.fluidInteractionQueue.Count, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
fluidBatchData = new NativeArray<BatchData>(maxBatches, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
// allocate effective mass arrays:
abstraction.contactEffectiveMasses.ResizeUninitialized(colliderGrid.colliderContactQueue.Count);
abstraction.particleContactEffectiveMasses.ResizeUninitialized(particleGrid.particleContactQueue.Count);
// 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);
abstraction.particleContacts.ResizeUninitialized(particleGrid.particleContactQueue.Count);
DequeueIntoArrayJob<BurstContact> dequeueParticleContacts = new DequeueIntoArrayJob<BurstContact>
{
InputQueue = particleGrid.particleContactQueue,
OutputArray = rawParticleContacts
};
abstraction.colliderContacts.ResizeUninitialized(colliderGrid.colliderContactQueue.Count);
DequeueIntoArrayJob<BurstContact> dequeueColliderContacts = new DequeueIntoArrayJob<BurstContact>
{
InputQueue = colliderGrid.colliderContactQueue,
OutputArray = abstraction.colliderContacts.AsNativeArray<BurstContact>()
};
DequeueIntoArrayJob<FluidInteraction> dequeueFluidInteractions = new DequeueIntoArrayJob<FluidInteraction>
{
InputQueue = particleGrid.fluidInteractionQueue,
OutputArray = rawFluidInteractions
};
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 = abstraction.particleContacts.AsNativeArray<BurstContact>(),
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();
inputDeps = colliderGrid.ApplyForceZones(this, deltaTime, inputDeps);
}
return inputDeps;
}
public IObiJobHandle Substep(IObiJobHandle handle, float stepTime, float substepTime, int steps, float timeLeft)
{
BurstJobHandle burstHandle = handle as BurstJobHandle;
if (burstHandle == null)
return handle;
// Apply aerodynamics
burstHandle.jobHandle = constraints[(int)Oni.ConstraintType.Aerodynamics].Project(burstHandle.jobHandle, stepTime, substepTime, steps, timeLeft);
// Predict positions:
var predictPositions = new PredictPositionsJob()
{
activeParticles = activeParticles,
phases = phases,
buoyancies = fluidInterface,
externalForces = externalForces,
inverseMasses = invMasses,
positions = positions,
previousPositions = prevPositions,
velocities = velocities,
externalTorques = externalTorques,
inverseRotationalMasses = invRotationalMasses,
orientations = orientations,
previousOrientations = prevOrientations,
angularVelocities = angularVelocities,
gravity = new float4(abstraction.parameters.gravity, 0),
deltaTime = substepTime,
is2D = abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D
};
burstHandle.jobHandle = predictPositions.Schedule(activeParticles.Length, 128, burstHandle.jobHandle);
// Project position constraints:
burstHandle.jobHandle = ApplyConstraints(burstHandle.jobHandle, stepTime, substepTime, steps, timeLeft);
// Enforce solver boundary limits:
burstHandle.jobHandle = EnforceLimits(burstHandle.jobHandle);
// 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
};
burstHandle.jobHandle = updateVelocitiesJob.Schedule(activeParticles.Length, 128, burstHandle.jobHandle);
// calculate particle velocity correction:
burstHandle.jobHandle = CalculateVelocityCorrections(burstHandle.jobHandle, substepTime);
// Update diffuse particles:
int substepsLeft = (int)math.round(timeLeft / substepTime);
int foamPadding = (int)math.ceil(abstraction.substeps / (float)abstraction.foamSubsteps);
if (substepsLeft % foamPadding == 0)
burstHandle.jobHandle = UpdateDiffuseParticles(burstHandle.jobHandle, substepTime * foamPadding);
// correct particle velocities:
burstHandle.jobHandle = ApplyVelocityCorrections(burstHandle.jobHandle, substepTime);
// update particle positions:
var updatePositionsJob = new UpdatePositionsJob
{
activeParticles = activeParticles,
positions = positions,
previousPositions = prevPositions,
velocities = velocities,
orientations = orientations,
previousOrientations = prevOrientations,
angularVelocities = angularVelocities,
velocityScale = math.pow(1 - math.saturate(abstraction.parameters.damping), substepTime),
sleepThreshold = abstraction.parameters.sleepThreshold,
maxVelocity = abstraction.parameters.maxVelocity,
maxAngularVelocity = abstraction.parameters.maxAngularVelocity
};
burstHandle.jobHandle = updatePositionsJob.Schedule(activeParticles.Length, 128, burstHandle.jobHandle);
return burstHandle;
}
private JobHandle CalculateVelocityCorrections(JobHandle inputDeps, float deltaTime)
{
var densityParameters = abstraction.GetConstraintParameters(Oni.ConstraintType.Density);
if (densityParameters.enabled)
{
var d = constraints[(int)Oni.ConstraintType.Density] as BurstDensityConstraints;
if (d != null)
{
return d.CalculateVelocityCorrections(inputDeps, deltaTime);
}
}
return inputDeps;
}
private JobHandle ApplyVelocityCorrections(JobHandle inputDeps, float deltaTime)
{
var densityParameters = abstraction.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 steps, float timeLeft)
{
// calculate max amount of iterations required, and initialize constraints..
int maxIterations = 0;
for (int i = 0; i < Oni.ConstraintTypeCount; ++i)
{
var parameters = abstraction.GetConstraintParameters((Oni.ConstraintType)i);
if (parameters.enabled)
{
maxIterations = math.max(maxIterations, parameters.iterations);
inputDeps = constraints[i].Initialize(inputDeps, stepTime, substepTime, steps, timeLeft);
}
}
// calculate iteration paddings:
for (int i = 0; i < Oni.ConstraintTypeCount; ++i)
{
var parameters = abstraction.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 = abstraction.GetConstraintParameters((Oni.ConstraintType)j);
if (parameters.enabled && i % padding[j] == 0)
inputDeps = constraints[j].Project(inputDeps, stepTime, substepTime, steps, timeLeft);
}
}
}
// final iteration, all groups together:
for (int i = 0; i < Oni.ConstraintTypeCount; ++i)
{
if (i != (int)Oni.ConstraintType.Aerodynamics)
{
var parameters = abstraction.GetConstraintParameters((Oni.ConstraintType)i);
if (parameters.enabled && parameters.iterations > 0)
inputDeps = constraints[i].Project(inputDeps, stepTime, substepTime, steps, timeLeft);
}
}
// 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 = abstraction.GetConstraintParameters(Oni.ConstraintType.ParticleCollision);
if (param.enabled && param.iterations > 0)
inputDeps = constraints[(int)Oni.ConstraintType.ParticleCollision].Project(inputDeps, stepTime, substepTime, steps, timeLeft);
param = abstraction.GetConstraintParameters(Oni.ConstraintType.Collision);
if (param.enabled && param.iterations > 0)
inputDeps = constraints[(int)Oni.ConstraintType.Collision].Project(inputDeps, stepTime, substepTime, steps, timeLeft);
return inputDeps;
}
private JobHandle EnforceLimits(JobHandle inputDeps)
{
if (!abstraction.useLimits) return inputDeps;
var boundaryLimits = new EnforceLimitsJob
{
activeParticles = activeParticles,
positions = positions,
prevPositions = prevPositions,
life = life,
phases = phases,
boundaryLimits = new BurstAabb(new float4(abstraction.boundaryLimits.min, 0), new float4(abstraction.boundaryLimits.max, 0)),
killOffLimits = abstraction.killOffLimitsParticles
};
return boundaryLimits.Schedule(activeParticleCount, 64, inputDeps);
}
public IObiJobHandle ApplyInterpolation(IObiJobHandle inputDeps, ObiNativeVector4List startPositions, ObiNativeQuaternionList startOrientations, float stepTime, float unsimulatedTime)
{
if (inputDeps == null)
inputDeps = new BurstJobHandle();
BurstJobHandle burstHandle = inputDeps as BurstJobHandle;
if (burstHandle == null)
return inputDeps;
// Interpolate particle positions and orientations.
var interpolate = new InterpolationJob
{
positions = positions,
endPositions = abstraction.endPositions.AsNativeArray<float4>(),
startPositions = startPositions.AsNativeArray<float4>(),
renderablePositions = renderablePositions,
orientations = orientations,
endOrientations = abstraction.endOrientations.AsNativeArray<quaternion>(),
startOrientations = startOrientations.AsNativeArray<quaternion>(),
renderableOrientations = renderableOrientations,
principalRadii = principalRadii,
renderableRadii = renderableRadii,
blendFactor = stepTime > 0 ? unsimulatedTime / stepTime : 0,
interpolationMode = abstraction.parameters.interpolation
};
burstHandle.jobHandle = interpolate.Schedule(abstraction.positions.count, 128, burstHandle.jobHandle);
// Update deformable triangle normals
var resetNormals = new ResetNormals()
{
phases = phases,
normals = normals,
tangents = tangents
};
burstHandle.jobHandle = resetNormals.Schedule(normals.Length, 128, burstHandle.jobHandle);
// Update deformable triangle normals
var updateTriNormals = new UpdateTriangleNormalsJob()
{
renderPositions = renderablePositions,
deformableTriangles = deformableTriangles,
deformableTriangleUVs = deformableUVs,
normals = normals,
tangents = tangents
};
burstHandle.jobHandle = updateTriNormals.Schedule(deformableTriangles.Length / 3, 1, burstHandle.jobHandle);
// Update deformable edge normals
var updateEdgeNormals = new UpdateEdgeNormalsJob()
{
renderPositions = renderablePositions,
velocities = velocities,
deformableEdges = deformableEdges,
wind = wind,
normals = normals,
};
burstHandle.jobHandle = updateEdgeNormals.Schedule(deformableEdges.Length / 2, 1, burstHandle.jobHandle);
// Update deformable triangle orientations
var updateOrientations = new RenderableOrientationFromNormals()
{
phases = phases,
normals = normals,
tangents = tangents,
renderableOrientations = renderableOrientations
};
burstHandle.jobHandle = updateOrientations.Schedule(normals.Length, 128, burstHandle.jobHandle);
// project renderable position/orientation of pinned particles:
var pinparam = abstraction.GetConstraintParameters(Oni.ConstraintType.Pin);
if (pinparam.enabled && pinparam.iterations > 0)
{
var d = constraints[(int)Oni.ConstraintType.Pin] as BurstPinConstraints;
if (Application.isPlaying && d != null)
burstHandle.jobHandle = d.ProjectRenderablePositions(burstHandle.jobHandle);
}
//make sure density constraints are enabled, otherwise particles have no neighbors and neighbor lists will be uninitialized.
var param = abstraction.GetConstraintParameters(Oni.ConstraintType.Density);
if (param.enabled && param.iterations > 0)
{
// Fluid laplacian/anisotropy (only if we're in play mode, in-editor we have no particlegrid/sorted data).
var d = constraints[(int)Oni.ConstraintType.Density] as BurstDensityConstraints;
if (Application.isPlaying && d != null)
burstHandle.jobHandle = d.CalculateAnisotropyLaplacianSmoothing(burstHandle.jobHandle);
}
return burstHandle;
}
private unsafe JobHandle UpdateDiffuseParticles(JobHandle inputDeps, float deltaTime)
{
var system = abstraction.GetRenderSystem<ObiFoamGenerator>() as BurstFoamRenderSystem;
if (system != null)
{
int* dispatchPtr = (int*)abstraction.foamCount.AddressOfElement(0);
for (int i = 0; i < system.renderers.Count; ++i)
{
float seed = Time.frameCount % 16535 + UnityEngine.Random.value;
if (system.renderers[i] is ObiFoamEmitter)
{
var emitter = system.renderers[i] as ObiFoamEmitter;
int particlesToEmit = emitter.GetParticleNumberToEmit(deltaTime);
var emitJob = new EmitParticlesJob
{
outputPositions = abstraction.foamPositions.AsNativeArray<float4>(),
outputVelocities = abstraction.foamVelocities.AsNativeArray<float4>(),
outputColors = abstraction.foamColors.AsNativeArray<float4>(),
outputAttributes = abstraction.foamAttributes.AsNativeArray<float4>(),
dispatchBuffer = abstraction.foamCount.AsNativeArray<int>(),
emitterShape = (uint)emitter.shape,
emitterPosition = new float4(emitter.shapeTransform != null ? abstraction.transform.InverseTransformPoint(emitter.shapeTransform.position) : Vector3.zero,0),
emitterRotation = emitter.shapeTransform != null ? emitter.shapeTransform.rotation * Quaternion.Inverse(abstraction.transform.rotation) : Quaternion.identity,
emitterSize = new float4(emitter.shapeSize,0),
randomSeed = seed,
buoyancy = system.renderers[i].buoyancy,
drag = system.renderers[i].drag,
airdrag = math.pow(1 - math.saturate(system.renderers[i].atmosphericDrag), deltaTime),
airAging = system.renderers[i].airAging,
particleSize = system.renderers[i].size,
sizeRandom = system.renderers[i].sizeRandom,
lifetime = system.renderers[i].lifetime,
lifetimeRandom = system.renderers[i].lifetimeRandom,
foamColor = (Vector4)system.renderers[i].color,
deltaTime = deltaTime
};
inputDeps = emitJob.Schedule(particlesToEmit, 128, inputDeps);
}
else
{
var emitJob = new GenerateParticlesJob
{
// when the actor gets removed from solver, solverIndices is destroyed and
// this job may still be running. As a solution, create a temporary copy of the array.
activeParticles = new NativeArray<int>(system.renderers[i].actor.solverIndices.AsNativeArray<int>(), Allocator.TempJob),
positions = positions,
velocities = velocities,
angularVelocities = angularVelocities,
principalRadii = principalRadii,
fluidData = fluidData,
outputPositions = abstraction.foamPositions.AsNativeArray<float4>(),
outputVelocities = abstraction.foamVelocities.AsNativeArray<float4>(),
outputColors = abstraction.foamColors.AsNativeArray<float4>(),
outputAttributes = abstraction.foamAttributes.AsNativeArray<float4>(),
dispatchBuffer = abstraction.foamCount.AsNativeArray<int>(),
randomSeed = seed,
vorticityRange = system.renderers[i].vorticityRange,
velocityRange = system.renderers[i].velocityRange,
foamGenerationRate = system.renderers[i].foamGenerationRate,
potentialIncrease = system.renderers[i].foamPotential,
potentialDiffusion = math.pow(1 - math.saturate(system.renderers[i].foamPotentialDiffusion), deltaTime),
buoyancy = system.renderers[i].buoyancy,
drag = system.renderers[i].drag,
airdrag = math.pow(1 - math.saturate(system.renderers[i].atmosphericDrag), deltaTime),
isosurface = system.renderers[i].isosurface,
airAging = system.renderers[i].airAging,
particleSize = system.renderers[i].size,
sizeRandom = system.renderers[i].sizeRandom,
lifetime = system.renderers[i].lifetime,
lifetimeRandom = system.renderers[i].lifetimeRandom,
foamColor = (Vector4)system.renderers[i].color,
deltaTime = deltaTime
};
inputDeps = emitJob.Schedule(system.renderers[i].actor.activeParticleCount, 128, inputDeps);
}
}
var updateJob = new UpdateParticlesJob
{
positions = positions,
orientations = renderableOrientations,
principalRadii = renderableRadii,
velocities = velocities,
angularVelocities = angularVelocities,
fluidData = fluidData,
fluidMaterial = fluidMaterials,
simplices = simplices,
simplexCounts = simplexCounts,
grid = particleGrid.grid,
gridLevels = particleGrid.grid.populatedLevels.GetKeyArray(Allocator.TempJob),
densityKernel = new Poly6Kernel(abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
inputPositions = abstraction.foamPositions.AsNativeArray<float4>(),
inputVelocities = abstraction.foamVelocities.AsNativeArray<float4>(),
inputColors = abstraction.foamColors.AsNativeArray<float4>(),
inputAttributes = abstraction.foamAttributes.AsNativeArray<float4>(),
outputPositions = auxPositions,
outputVelocities = auxVelocities,
outputColors = auxColors,
outputAttributes = auxAttributes,
dispatchBuffer = abstraction.foamCount.AsNativeArray<int>(),
parameters = abstraction.parameters,
agingOverPopulation = new Vector3(abstraction.foamAccelAgingRange.x, abstraction.foamAccelAgingRange.y, abstraction.foamAccelAging),
minFluidNeighbors = abstraction.foamMinNeighbors,
currentAliveParticles = dispatchPtr[3],
deltaTime = deltaTime
};
inputDeps = IJobParallelForDeferExtensions.Schedule(updateJob, &dispatchPtr[3], 64, inputDeps);
var copyJob = new CopyJob
{
inputPositions = auxPositions,
inputVelocities = auxVelocities,
inputColors = auxColors,
inputAttributes = auxAttributes,
outputPositions = abstraction.foamPositions.AsNativeArray<float4>(),
outputVelocities = abstraction.foamVelocities.AsNativeArray<float4>(),
outputColors = abstraction.foamColors.AsNativeArray<float4>(),
outputAttributes = abstraction.foamAttributes.AsNativeArray<float4>(),
dispatchBuffer = abstraction.foamCount.AsNativeArray<int>()
};
inputDeps = IJobParallelForDeferExtensions.Schedule(copyJob, &dispatchPtr[7], 256, inputDeps);
activeFoamParticleCount = (uint)dispatchPtr[3];
}
return inputDeps;
}
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>()
};
dequeueQueryResults.Schedule().Complete();
resultsQueue.Dispose();
}
public int GetParticleGridSize()
{
return particleGrid.grid.usedCells.Length;
}
public void GetParticleGrid(ObiNativeAabbList cells)
{
particleGrid.GetCells(cells);
}
}
}
#endif