340 lines
54 KiB
C#
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
|
|
|
|
|