using UnityEngine; using UnityEngine.Rendering; namespace Obi { public class ComputeSolverImpl : 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 m_Solver.activeParticles.count; } } public int deformableTriangleCount { get { return m_Solver.deformableTriangles.count / 3; } } public int deformableEdgeCount { get { return m_Solver.deformableEdges.count / 2; } } public InertialFrame inertialFrame { get { return m_InertialFrame; } } public uint activeFoamParticleCount { private set; get; } // Per-type constraints array: IComputeConstraintsImpl[] constraints; // Per-type iteration padding array: private int[] padding = new int[Oni.ConstraintTypeCount]; // job handle: private ComputeJobHandle jobHandle; // particle contact generation: public ComputeParticleGrid particleGrid; // collider contact generation: public ComputeColliderWorld colliderGrid; // spatial queries: public SpatialQueries spatialQueries; // misc data: private InertialFrame m_InertialFrame; // cached particle data arrays (just wrappers over raw unmanaged data held by the abstract solver) public GraphicsBuffer deadIndicesBuffer; public GraphicsBuffer positionsBuffer; public GraphicsBuffer orientationsBuffer; public GraphicsBuffer startPositionsBuffer; public GraphicsBuffer endPositionsBuffer; public GraphicsBuffer startOrientationsBuffer; public GraphicsBuffer endOrientationsBuffer; public GraphicsBuffer restPositionsBuffer; public GraphicsBuffer prevPositionsBuffer; public GraphicsBuffer restOrientationsBuffer; public GraphicsBuffer prevOrientationsBuffer; public GraphicsBuffer renderablePositionsBuffer; public GraphicsBuffer renderableOrientationsBuffer; public GraphicsBuffer renderableRadiiBuffer; public GraphicsBuffer colorsBuffer; public GraphicsBuffer collisionMaterialIndexBuffer; public GraphicsBuffer principalRadiiBuffer; public GraphicsBuffer velocitiesBuffer; public GraphicsBuffer invMassesBuffer; public GraphicsBuffer phasesBuffer; public GraphicsBuffer filtersBuffer; public GraphicsBuffer angularVelocitiesBuffer; public GraphicsBuffer invRotationalMassesBuffer; public GraphicsBuffer externalForcesBuffer; public GraphicsBuffer externalTorquesBuffer; public GraphicsBuffer windBuffer; public GraphicsBuffer lifeBuffer; public GraphicsBuffer fluidDataBuffer; public GraphicsBuffer userDataBuffer; public GraphicsBuffer fluidMaterialsBuffer; public GraphicsBuffer fluidMaterials2Buffer; public GraphicsBuffer fluidInterfaceBuffer; public GraphicsBuffer anisotropiesBuffer; public GraphicsBuffer auxPositions; public GraphicsBuffer auxVelocities; public GraphicsBuffer auxColors; public GraphicsBuffer auxAttributes; public GraphicsBuffer auxOffsetInCell; public GraphicsBuffer auxSortedToOriginal; public GraphicsBuffer normalsBuffer; public GraphicsBuffer cellCoordsBuffer; public GraphicsBuffer positionDeltasIntBuffer; public GraphicsBuffer orientationDeltasIntBuffer; public GraphicsBuffer positionConstraintCountBuffer; public GraphicsBuffer orientationConstraintCountBuffer; public GraphicsBuffer activeParticlesBuffer; public GraphicsBuffer fluidDispatchBuffer; public GraphicsBuffer tangentsIntBuffer; public GraphicsBuffer deformableEdgesBuffer; public GraphicsBuffer deformableTrianglesBuffer; public GraphicsBuffer solverToWorldBuffer; public GraphicsBuffer worldToSolverBuffer; public GraphicsBuffer inertialFrameBuffer; private AffineTransform[] solverToWorldArray; private AffineTransform[] worldToSolverArray; private InertialFrame[] inertialFrameArray; public GraphicsBuffer rigidbodyLinearDeltasBuffer; public GraphicsBuffer rigidbodyAngularDeltasBuffer; public GraphicsBuffer rigidbodyLinearDeltasIntBuffer; public GraphicsBuffer rigidbodyAngularDeltasIntBuffer; public GraphicsBuffer reducedBounds; // simplices: public SimplexCounts simplexCounts; public GraphicsBuffer simplices; public GraphicsBuffer simplexBounds; public Aabb solverBounds; private AsyncGPUReadbackRequest boundsRequest; private ComputeShader solverShader; private int applyInertialForcesKernel; private int applyRigidbodyDeltasKernel; private int predictPositionsKernel; private int updateVelocitiesKernel; private int updatePositionsKernel; private int updateLifetimesKernel; private int enforceLimitsKernel; private int interpolateKernel; private ComputeShader boundsShader; private int simplexBoundsKernel; private int editSimplexBoundsKernel; private int boundsReductionKernel; private ComputeShader deformableTrisShader; private int resetNormalsKernel; private int updateNormalsKernel; private int updateEdgeNormalsKernel; private int orientationFromNormalsKernel; private ComputeShader foamShader; private int sortDataKernel; private int emitShapeFoamKernel; private int emitFoamKernel; private int copyAliveKernel; private int updateFoamKernel; private int integrateFoamKernel; private int copyKernel; private ComputeShader foamDensityShader; private int clearGridKernel; private int insertGridKernel; private int sortByGridKernel; private int computeDensityKernel; private int applyDensityKernel; private ComputeShader foamCollisionShader; private int solveDiffuseContactsKernel; public ComputeSolverImpl(ObiSolver solver) { this.m_Solver = solver; jobHandle = new ComputeJobHandle(); solverBounds = new Aabb(solver.transform.position - Vector3.one, solver.transform.position + Vector3.one); solver.queryResults.ResizeUninitialized((int)abstraction.maxQueryResults); solver.queryResults.SafeAsComputeBuffer(GraphicsBuffer.Target.Counter); solver.foamCount.AsComputeBuffer(GraphicsBuffer.Target.IndirectArguments); solver.foamPositions.AsComputeBuffer(); solver.foamVelocities.AsComputeBuffer(); solver.foamColors.AsComputeBuffer(); solver.foamAttributes.AsComputeBuffer(); solverShader = GameObject.Instantiate(Resources.Load("Compute/Solver")); applyInertialForcesKernel = solverShader.FindKernel("ApplyInertialForces"); applyRigidbodyDeltasKernel = solverShader.FindKernel("ApplyRigidbodyDeltas"); updateLifetimesKernel = solverShader.FindKernel("UpdateLifetimes"); predictPositionsKernel = solverShader.FindKernel("PredictPositions"); updateVelocitiesKernel = solverShader.FindKernel("UpdateVelocities"); updatePositionsKernel = solverShader.FindKernel("UpdatePositions"); enforceLimitsKernel = solverShader.FindKernel("EnforceLimits"); interpolateKernel = solverShader.FindKernel("Interpolate"); boundsShader = GameObject.Instantiate(Resources.Load("Compute/BoundsReduction")); simplexBoundsKernel = boundsShader.FindKernel("RuntimeSimplexBounds"); editSimplexBoundsKernel = boundsShader.FindKernel("EditSimplexBounds"); boundsReductionKernel = boundsShader.FindKernel("Reduce"); deformableTrisShader = GameObject.Instantiate(Resources.Load("Compute/DeformableTriangles")); resetNormalsKernel = deformableTrisShader.FindKernel("ResetNormals"); updateNormalsKernel = deformableTrisShader.FindKernel("UpdateNormals"); updateEdgeNormalsKernel = deformableTrisShader.FindKernel("UpdateEdgeNormals"); orientationFromNormalsKernel = deformableTrisShader.FindKernel("OrientationFromNormals"); foamShader = GameObject.Instantiate(Resources.Load("Compute/FluidFoam")); sortDataKernel = foamShader.FindKernel("SortFluidData"); emitShapeFoamKernel = foamShader.FindKernel("EmitShape"); emitFoamKernel = foamShader.FindKernel("Emit"); copyAliveKernel = foamShader.FindKernel("CopyAliveCount"); updateFoamKernel = foamShader.FindKernel("Update"); copyKernel = foamShader.FindKernel("Copy"); integrateFoamKernel = foamShader.FindKernel("Integrate"); foamDensityShader = GameObject.Instantiate(Resources.Load("Compute/FluidFoamDensity")); clearGridKernel = foamDensityShader.FindKernel("Clear"); insertGridKernel = foamDensityShader.FindKernel("InsertInGrid"); sortByGridKernel = foamDensityShader.FindKernel("SortByGrid"); computeDensityKernel = foamDensityShader.FindKernel("ComputeDensity"); applyDensityKernel = foamDensityShader.FindKernel("ApplyDensity"); foamCollisionShader = GameObject.Instantiate(Resources.Load("Compute/FluidFoamCollisions")); solveDiffuseContactsKernel = foamCollisionShader.FindKernel("SolveDiffuseContacts"); solverToWorldBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, 48); solverToWorldArray = new AffineTransform[1]; worldToSolverBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, 48); worldToSolverArray = new AffineTransform[1]; inertialFrameBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, 160); inertialFrameArray = new InertialFrame[1]; fluidDispatchBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 4, sizeof(uint)); // Initialize collision world: GetOrCreateColliderWorld(); colliderGrid.IncreaseReferenceCount(); // Initialize contact generation acceleration structure: particleGrid = new ComputeParticleGrid(); // Initialize spatial query system. spatialQueries = new SpatialQueries(solver.maxQueryResults); // Initialize constraint arrays: constraints = new IComputeConstraintsImpl[Oni.ConstraintTypeCount]; constraints[(int)Oni.ConstraintType.Tether] = new ComputeTetherConstraints(this); constraints[(int)Oni.ConstraintType.Volume] = new ComputeVolumeConstraints(this); constraints[(int)Oni.ConstraintType.Chain] = new ComputeChainConstraints(this); constraints[(int)Oni.ConstraintType.Bending] = new ComputeBendConstraints(this); constraints[(int)Oni.ConstraintType.Distance] = new ComputeDistanceConstraints(this); constraints[(int)Oni.ConstraintType.ShapeMatching] = new ComputeShapeMatchingConstraints(this); constraints[(int)Oni.ConstraintType.BendTwist] = new ComputeBendTwistConstraints(this); constraints[(int)Oni.ConstraintType.StretchShear] = new ComputeStretchShearConstraints(this); constraints[(int)Oni.ConstraintType.Pin] = new ComputePinConstraints(this); constraints[(int)Oni.ConstraintType.Pinhole] = new ComputePinholeConstraints(this); constraints[(int)Oni.ConstraintType.Skin] = new ComputeSkinConstraints(this); constraints[(int)Oni.ConstraintType.Aerodynamics] = new ComputeAerodynamicConstraints(this); constraints[(int)Oni.ConstraintType.Stitch] = new ComputeStitchConstraints(this); constraints[(int)Oni.ConstraintType.ParticleCollision] = new ComputeParticleCollisionConstraints(this); constraints[(int)Oni.ConstraintType.ParticleCollision].CreateConstraintsBatch(); constraints[(int)Oni.ConstraintType.Collision] = new ComputeColliderCollisionConstraints(this); constraints[(int)Oni.ConstraintType.Collision].CreateConstraintsBatch(); constraints[(int)Oni.ConstraintType.ParticleFriction] = new ComputeParticleFrictionConstraints(this); constraints[(int)Oni.ConstraintType.ParticleFriction].CreateConstraintsBatch(); constraints[(int)Oni.ConstraintType.Friction] = new ComputeColliderFrictionConstraints(this); constraints[(int)Oni.ConstraintType.Friction].CreateConstraintsBatch(); constraints[(int)Oni.ConstraintType.Density] = new ComputeDensityConstraints(this); constraints[(int)Oni.ConstraintType.Density].CreateConstraintsBatch(); } public void Destroy() { reducedBounds?.Dispose(); solverToWorldBuffer?.Dispose(); worldToSolverBuffer?.Dispose(); inertialFrameBuffer?.Dispose(); fluidDispatchBuffer?.Dispose(); for (int i = 0; i < constraints.Length; ++i) if (constraints[i] != null) constraints[i].Dispose(); // Get rid of particle and collider grids/queries: particleGrid?.Dispose(); // cannot use null-coalescing because this is a GameObject, and Unity overrides != if (colliderGrid != null) colliderGrid.DecreaseReferenceCount(); spatialQueries?.Dispose(); positionDeltasIntBuffer?.Dispose(); orientationDeltasIntBuffer?.Dispose(); rigidbodyLinearDeltasIntBuffer?.Dispose(); rigidbodyAngularDeltasIntBuffer?.Dispose(); tangentsIntBuffer?.Dispose(); simplexBounds?.Dispose(); auxPositions?.Dispose(); auxVelocities?.Dispose(); auxColors?.Dispose(); auxAttributes?.Dispose(); auxOffsetInCell?.Dispose(); auxSortedToOriginal?.Dispose(); } private void GetOrCreateColliderWorld() { colliderGrid = GameObject.FindObjectOfType(); if (colliderGrid == null) { var world = new GameObject("ComputeCollisionWorld", typeof(ComputeColliderWorld)); colliderGrid = world.GetComponent(); } } public void PushData() { // Send data to the GPU: abstraction.positions.Upload(); abstraction.orientations.Upload(); abstraction.velocities.Upload(); abstraction.angularVelocities.Upload(); abstraction.colors.Upload(); abstraction.startPositions.Upload(); abstraction.startOrientations.Upload(); abstraction.endPositions.Upload(); abstraction.endOrientations.Upload(); abstraction.restPositions.Upload(); abstraction.restOrientations.Upload(); abstraction.normals.Upload(); abstraction.principalRadii.Upload(); abstraction.invMasses.Upload(); abstraction.invRotationalMasses.Upload(); abstraction.phases.Upload(); abstraction.filters.Upload(); abstraction.externalForces.Upload(); abstraction.externalTorques.Upload(); abstraction.wind.Upload(); abstraction.life.Upload(); abstraction.fluidData.Upload(); abstraction.userData.Upload(); abstraction.fluidInterface.Upload(); abstraction.fluidMaterials.Upload(); abstraction.fluidMaterials2.Upload(); rigidbodyLinearDeltasIntBuffer.SetData(abstraction.rigidbodyLinearDeltas.AsNativeArray()); rigidbodyAngularDeltasIntBuffer.SetData(abstraction.rigidbodyAngularDeltas.AsNativeArray()); } public void RequestReadback() { // Copy rigidbody deltas (int) to output buffers (float), then request readback: solverShader.SetBuffer(applyRigidbodyDeltasKernel, "linearDeltasAsInt", rigidbodyLinearDeltasIntBuffer); solverShader.SetBuffer(applyRigidbodyDeltasKernel, "angularDeltasAsInt", rigidbodyAngularDeltasIntBuffer); solverShader.SetBuffer(applyRigidbodyDeltasKernel, "linearDeltas", rigidbodyLinearDeltasBuffer); solverShader.SetBuffer(applyRigidbodyDeltasKernel, "angularDeltas", rigidbodyAngularDeltasBuffer); solverShader.SetInt("particleCount", abstraction.rigidbodyLinearDeltas.count); int threadGroups = ComputeMath.ThreadGroupCount(abstraction.rigidbodyLinearDeltas.count, 128); solverShader.Dispatch(applyRigidbodyDeltasKernel, threadGroups, 1, 1); abstraction.rigidbodyLinearDeltas.Readback(); abstraction.rigidbodyAngularDeltas.Readback(); // begin particle data async GPU -> CPU transfer. // by default, only positions and velocities are read back. // ObiActors can request whatever data they need in RequestReadback, // then wait for it in SimulationEnd. abstraction.positions.Readback(); abstraction.velocities.Readback(); // begin constraint data async GPU -> CPU transfer. var sm = constraints[(int)Oni.ConstraintType.ShapeMatching] as ComputeShapeMatchingConstraints; if (sm != null) sm.RequestDataReadback(); // needed for tearing. var dm = constraints[(int)Oni.ConstraintType.Distance] as ComputeDistanceConstraints; if (dm != null) dm.RequestDataReadback(); var pm = constraints[(int)Oni.ConstraintType.Pin] as ComputePinConstraints; if (pm != null) pm.RequestDataReadback(); var phm = constraints[(int)Oni.ConstraintType.Pinhole] as ComputePinholeConstraints; if (phm != null) phm.RequestDataReadback(); } public void InitializeFrame(Vector4 translation, Vector4 scale, Quaternion rotation) { m_InertialFrame = new InertialFrame(translation, scale, rotation); } public void UpdateFrame(Vector4 translation, Vector4 scale, Quaternion rotation, float deltaTime) { m_InertialFrame.Update(translation, scale, rotation, deltaTime); solverToWorldArray[0] = m_InertialFrame.frame; solverToWorldBuffer.SetData(solverToWorldArray); worldToSolverArray[0] = m_InertialFrame.frame.Inverse(); worldToSolverBuffer.SetData(worldToSolverArray); inertialFrameArray[0] = m_InertialFrame; inertialFrameBuffer.SetData(inertialFrameArray); } public IObiJobHandle ApplyFrame(float worldLinearInertiaScale, float worldAngularInertiaScale, float deltaTime) { if (activeParticleCount > 0) { // inverse linear part: Matrix4x4 linear = Matrix4x4.TRS(Vector3.zero, inertialFrame.frame.rotation, new Vector3(1 / inertialFrame.frame.scale.x, 1 / inertialFrame.frame.scale.y, 1 / inertialFrame.frame.scale.z)); Matrix4x4 linearInv = Matrix4x4.Transpose(linear); // non-inertial frame accelerations: Vector4 angularVel = (linearInv * Matrix4x4.Scale(inertialFrame.angularVelocity) * linear).Diagonal(); Vector4 eulerAccel = (linearInv * Matrix4x4.Scale(inertialFrame.angularAcceleration) * linear).Diagonal(); Vector4 inertialAccel = linearInv * inertialFrame.acceleration; int threadGroups = ComputeMath.ThreadGroupCount(activeParticleCount, 128); solverShader.SetInt("particleCount", activeParticleCount); solverShader.SetFloat("deltaTime", deltaTime); solverShader.SetFloat("worldLinearInertiaScale", abstraction.worldLinearInertiaScale); solverShader.SetFloat("worldAngularInertiaScale", abstraction.worldAngularInertiaScale); solverShader.SetVector("angularVel", angularVel); solverShader.SetVector("eulerAccel", eulerAccel); solverShader.SetVector("inertialAccel", inertialAccel); solverShader.SetVector("ambientWind", abstraction.parameters.ambientWind); solverShader.SetBool("inertialWind", abstraction.windSpace == Space.World); solverShader.SetBuffer(applyInertialForcesKernel, "activeParticles", activeParticlesBuffer); solverShader.SetBuffer(applyInertialForcesKernel, "positions", positionsBuffer); solverShader.SetBuffer(applyInertialForcesKernel, "velocities", velocitiesBuffer); solverShader.SetBuffer(applyInertialForcesKernel, "invMasses", invMassesBuffer); solverShader.SetBuffer(applyInertialForcesKernel, "wind", windBuffer); solverShader.SetBuffer(applyInertialForcesKernel, "inertialSolverFrame", inertialFrameBuffer); solverShader.Dispatch(applyInertialForcesKernel, threadGroups, 1, 1); } return jobHandle; } public void SetDeformableTriangles(ObiNativeIntList indices, ObiNativeVector2List uvs) { if (indices.count > 0) { deformableTrianglesBuffer = indices.AsComputeBuffer(); var deformableUVsBuffer = uvs.AsComputeBuffer(); deformableTrisShader.SetBuffer(updateNormalsKernel, "deformableTriangles", deformableTrianglesBuffer); deformableTrisShader.SetBuffer(updateNormalsKernel, "deformableTriangleUVs", deformableUVsBuffer); deformableTrisShader.SetInt("triangleCount", deformableTriangleCount); } } public void SetDeformableEdges(ObiNativeIntList indices) { if (indices.count > 0) { deformableEdgesBuffer = indices.AsComputeBuffer(); deformableTrisShader.SetBuffer(updateEdgeNormalsKernel, "deformableEdges", deformableEdgesBuffer); deformableTrisShader.SetInt("edgeCount", deformableEdgeCount); } } public void SetSimplices(ObiNativeIntList simplices, SimplexCounts counts) { this.simplexCounts = counts; if (simplices.count > 0) { boundsShader.SetInt("pointCount", simplexCounts.pointCount); boundsShader.SetInt("edgeCount", simplexCounts.edgeCount); boundsShader.SetInt("triangleCount", simplexCounts.triangleCount); this.simplices = simplices.AsComputeBuffer(); cellCoordsBuffer = abstraction.cellCoords.AsComputeBuffer(); if (simplexBounds == null || counts.simplexCount > simplexBounds.count) { simplexBounds?.Dispose(); simplexBounds = new GraphicsBuffer(GraphicsBuffer.Target.Structured, counts.simplexCount * 2, 32); reducedBounds?.Dispose(); reducedBounds = new GraphicsBuffer(GraphicsBuffer.Target.Structured, ComputeMath.NextMultiple(counts.simplexCount * 2, 256), 32); } // Even though we usually store simplices for collision detection, the grid is reused for fluid meshing // so the capacity we set should be at least the total amount of particles in the solver. if (particleGrid != null) { if (particleGrid.SetCapacity(Mathf.Max(counts.simplexCount, particleCount), (uint)Mathf.Max(1, abstraction.maxParticleContacts), (uint)Mathf.Max(1, abstraction.maxParticleNeighbors))) { // resize to maximum number of contacts: abstraction.colliderContacts.ResizeUninitialized(particleGrid.contactPairs.count); abstraction.colliderContacts.SafeAsComputeBuffer(GraphicsBuffer.Target.Counter); abstraction.particleContacts.ResizeUninitialized(particleGrid.contactPairs.count); abstraction.particleContacts.SafeAsComputeBuffer(GraphicsBuffer.Target.Counter); abstraction.contactEffectiveMasses.ResizeUninitialized(particleGrid.contactPairs.count); abstraction.contactEffectiveMasses.SafeAsComputeBuffer(); abstraction.particleContactEffectiveMasses.ResizeUninitialized(particleGrid.contactPairs.count); abstraction.particleContactEffectiveMasses.SafeAsComputeBuffer(); } } } else this.simplices = null; } public void SetActiveParticles(ObiNativeIntList indices) { // TODO: indices.computebuffer has been deleted when adding an item. We now need to // update the compute buffer if needed. if (indices.computeBuffer == null || indices.computeBuffer.count != indices.capacity) { //Debug.Log("create"); activeParticlesBuffer = indices.AsComputeBuffer(indices.capacity); } else { //Debug.Log("update"); indices.UploadFullCapacity(); //unmaps the entire memory buffer up to capacity. } if (activeParticlesBuffer != null) { solverShader.SetBuffer(predictPositionsKernel, "activeParticles", activeParticlesBuffer); solverShader.SetBuffer(updateVelocitiesKernel, "activeParticles", activeParticlesBuffer); solverShader.SetBuffer(updatePositionsKernel, "activeParticles", activeParticlesBuffer); solverShader.SetBuffer(enforceLimitsKernel, "activeParticles", activeParticlesBuffer); } } public IObiJobHandle UpdateBounds(IObiJobHandle inputDeps, float stepTime) { if (activeParticleCount > 0 && reducedBounds != null) { boundsShader.SetFloat("deltaTime", stepTime); boundsShader.SetFloat("collisionMargin", abstraction.parameters.collisionMargin); boundsShader.SetFloat("particleCCD", abstraction.parameters.particleCCD); int boundsCount = simplexCounts.simplexCount; int threadGroups = ComputeMath.ThreadGroupCount(boundsCount, 256); // at edit time, the collision materials buffer will be null since // the collider world is not updated. if (colliderGrid.materialsBuffer != null) { boundsShader.SetBuffer(simplexBoundsKernel, "simplexBounds", simplexBounds); boundsShader.SetBuffer(simplexBoundsKernel, "simplices", simplices); boundsShader.SetBuffer(simplexBoundsKernel, "reducedBounds", reducedBounds); boundsShader.SetBuffer(simplexBoundsKernel, "activeParticles", activeParticlesBuffer); boundsShader.SetBuffer(simplexBoundsKernel, "positions", positionsBuffer); boundsShader.SetBuffer(simplexBoundsKernel, "velocities", velocitiesBuffer); boundsShader.SetBuffer(simplexBoundsKernel, "principalRadii", principalRadiiBuffer); boundsShader.SetBuffer(simplexBoundsKernel, "fluidMaterials", fluidMaterialsBuffer); boundsShader.SetBuffer(simplexBoundsKernel, "collisionMaterials", colliderGrid.materialsBuffer); boundsShader.SetBuffer(simplexBoundsKernel, "collisionMaterialIndices", collisionMaterialIndexBuffer); boundsShader.Dispatch(simplexBoundsKernel, threadGroups, 1, 1); } else { boundsShader.SetBuffer(editSimplexBoundsKernel, "simplexBounds", simplexBounds); boundsShader.SetBuffer(editSimplexBoundsKernel, "simplices", simplices); boundsShader.SetBuffer(editSimplexBoundsKernel, "reducedBounds", reducedBounds); boundsShader.SetBuffer(editSimplexBoundsKernel, "activeParticles", activeParticlesBuffer); boundsShader.SetBuffer(editSimplexBoundsKernel, "positions", positionsBuffer); boundsShader.SetBuffer(editSimplexBoundsKernel, "velocities", velocitiesBuffer); boundsShader.SetBuffer(editSimplexBoundsKernel, "principalRadii", principalRadiiBuffer); boundsShader.SetBuffer(editSimplexBoundsKernel, "fluidMaterials", fluidMaterialsBuffer); boundsShader.Dispatch(editSimplexBoundsKernel, threadGroups, 1, 1); } boundsShader.SetBuffer(boundsReductionKernel, "reducedBounds", reducedBounds); do { boundsShader.Dispatch(boundsReductionKernel, threadGroups, 1, 1); threadGroups = ComputeMath.ThreadGroupCount(boundsCount, 256); boundsCount /= 256; } while (threadGroups > 1); boundsRequest = AsyncGPUReadback.Request(reducedBounds, 32, 0); // update lifetimes: solverShader.SetFloat("deltaTime",stepTime); solverShader.SetBuffer(updateLifetimesKernel, "activeParticles", activeParticlesBuffer); solverShader.SetBuffer(updateLifetimesKernel, "life", lifeBuffer); solverShader.SetBuffer(updateLifetimesKernel, "deadParticles", deadIndicesBuffer); solverShader.Dispatch(updateLifetimesKernel, ComputeMath.ThreadGroupCount(activeParticleCount, 128), 1, 1); } return inputDeps; } public void GetBounds(ref Vector3 min, ref Vector3 max) { // wait for last pending bounds async request: boundsRequest.WaitForCompletion(); if (boundsRequest.done && !boundsRequest.hasError) solverBounds = boundsRequest.GetData(0)[0]; min = solverBounds.min; max = solverBounds.max; } public int GetConstraintCount(Oni.ConstraintType type) { /*if ((int)type > 0 && (int)type < constraints.Length) { int count = 0; for (int i = 0; i < constraints[(int)type].Count; ++i) { count += constraints[(int)type][i].GetConstraintCount(); } return count; } return 0;*/ return 0; } public void SetParameters(Oni.SolverParameters parameters) { // These should be better passed using a constant buffer, but constant buffers do not work in 2021 :( //https://issuetracker.unity3d.com/issues/compute-shader-is-not-using-defined-constants-when-setting-data-with-setconstantbuffer solverShader.SetInt("mode", (int)parameters.mode); solverShader.SetInt("interpolation", (int)parameters.interpolation); solverShader.SetVector("gravity", parameters.gravity); solverShader.SetFloat("damping", parameters.damping); solverShader.SetFloat("sleepThreshold", parameters.sleepThreshold); solverShader.SetFloat("collisionMargin", parameters.collisionMargin); solverShader.SetFloat("maxVelocity", parameters.maxVelocity); solverShader.SetFloat("maxAngularVelocity", parameters.maxAngularVelocity); } 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) { deadIndicesBuffer = abstraction.deadParticles.AsComputeBuffer(abstraction.deadParticles.capacity, GraphicsBuffer.Target.Counter); colorsBuffer = abstraction.colors.AsComputeBuffer(); positionsBuffer = abstraction.positions.AsComputeBuffer(); orientationsBuffer = abstraction.orientations.AsComputeBuffer(); startPositionsBuffer = abstraction.startPositions.AsComputeBuffer(); endPositionsBuffer = abstraction.endPositions.AsComputeBuffer(); startOrientationsBuffer = abstraction.startOrientations.AsComputeBuffer(); endOrientationsBuffer = abstraction.endOrientations.AsComputeBuffer(); restPositionsBuffer = abstraction.restPositions.AsComputeBuffer(); restOrientationsBuffer = abstraction.restOrientations.AsComputeBuffer(); prevPositionsBuffer = abstraction.prevPositions.AsComputeBuffer(); prevOrientationsBuffer = abstraction.prevOrientations.AsComputeBuffer(); renderablePositionsBuffer = abstraction.renderablePositions.AsComputeBuffer(); renderableOrientationsBuffer = abstraction.renderableOrientations.AsComputeBuffer(); renderableRadiiBuffer = abstraction.renderableRadii.AsComputeBuffer(); collisionMaterialIndexBuffer = abstraction.collisionMaterials.AsComputeBuffer(); angularVelocitiesBuffer = abstraction.angularVelocities.AsComputeBuffer(); invRotationalMassesBuffer = abstraction.invRotationalMasses.AsComputeBuffer(); externalForcesBuffer = abstraction.externalForces.AsComputeBuffer(); externalTorquesBuffer = abstraction.externalTorques.AsComputeBuffer(); windBuffer = abstraction.wind.AsComputeBuffer(); velocitiesBuffer = abstraction.velocities.AsComputeBuffer(); principalRadiiBuffer = abstraction.principalRadii.AsComputeBuffer(); invMassesBuffer = abstraction.invMasses.AsComputeBuffer(); phasesBuffer = abstraction.phases.AsComputeBuffer(); filtersBuffer = abstraction.filters.AsComputeBuffer(); lifeBuffer = abstraction.life.AsComputeBuffer(); fluidDataBuffer = abstraction.fluidData.AsComputeBuffer(); userDataBuffer = abstraction.userData.AsComputeBuffer(); fluidInterfaceBuffer = abstraction.fluidInterface.AsComputeBuffer(); fluidMaterialsBuffer = abstraction.fluidMaterials.AsComputeBuffer(); fluidMaterials2Buffer = abstraction.fluidMaterials2.AsComputeBuffer(); anisotropiesBuffer = abstraction.anisotropies.AsComputeBuffer(); normalsBuffer = abstraction.normals.AsComputeBuffer(); positionConstraintCountBuffer = abstraction.positionConstraintCounts.AsComputeBuffer(); orientationConstraintCountBuffer = abstraction.orientationConstraintCounts.AsComputeBuffer(); if (positionDeltasIntBuffer != null) { positionDeltasIntBuffer.Dispose(); positionDeltasIntBuffer = null; } if (abstraction.positionDeltas.count > 0) { positionDeltasIntBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, abstraction.positionDeltas.count, abstraction.positionDeltas.stride); positionDeltasIntBuffer.SetData(new Vector4[abstraction.positionDeltas.count]); } if (orientationDeltasIntBuffer != null) { orientationDeltasIntBuffer.Dispose(); orientationDeltasIntBuffer = null; } if (abstraction.orientationDeltas.count > 0) { orientationDeltasIntBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, abstraction.orientationDeltas.count, abstraction.orientationDeltas.stride); orientationDeltasIntBuffer.SetData(new Vector4[abstraction.orientationDeltas.count]); } if (tangentsIntBuffer != null) { tangentsIntBuffer.Dispose(); tangentsIntBuffer = null; } if (abstraction.normals.count > 0) { tangentsIntBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, abstraction.normals.count, abstraction.normals.stride); tangentsIntBuffer.SetData(new VInt4[abstraction.normals.count]); } if (positionsBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "positions", positionsBuffer); if (prevPositionsBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "prevPositions", prevPositionsBuffer); if (orientationsBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "orientations", orientationsBuffer); if (prevOrientationsBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "prevOrientations", prevOrientationsBuffer); if (velocitiesBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "velocities", velocitiesBuffer); if (invMassesBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "invMasses", invMassesBuffer); if (angularVelocitiesBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "angularVelocities", angularVelocitiesBuffer); if (invRotationalMassesBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "invRotationalMasses", invRotationalMassesBuffer); if (externalForcesBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "externalForces", externalForcesBuffer); if (externalTorquesBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "externalTorques", externalTorquesBuffer); if (phasesBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "phases", phasesBuffer); if (fluidInterfaceBuffer != null) solverShader.SetBuffer(predictPositionsKernel, "buoyancies", fluidInterfaceBuffer); if (positionsBuffer != null) solverShader.SetBuffer(updateVelocitiesKernel, "positions", positionsBuffer); if (prevPositionsBuffer != null) solverShader.SetBuffer(updateVelocitiesKernel, "prevPositions", prevPositionsBuffer); if (orientationsBuffer != null) solverShader.SetBuffer(updateVelocitiesKernel, "orientations", orientationsBuffer); if (prevOrientationsBuffer != null) solverShader.SetBuffer(updateVelocitiesKernel, "prevOrientations", prevOrientationsBuffer); if (velocitiesBuffer != null) solverShader.SetBuffer(updateVelocitiesKernel, "velocities", velocitiesBuffer); if (angularVelocitiesBuffer != null) solverShader.SetBuffer(updateVelocitiesKernel, "angularVelocities", angularVelocitiesBuffer); if (invMassesBuffer != null) solverShader.SetBuffer(updateVelocitiesKernel, "invMasses", invMassesBuffer); if (invRotationalMassesBuffer != null) solverShader.SetBuffer(updateVelocitiesKernel, "invRotationalMasses", invRotationalMassesBuffer); if (positionsBuffer != null) solverShader.SetBuffer(updatePositionsKernel, "positions", positionsBuffer); if (prevPositionsBuffer != null) solverShader.SetBuffer(updatePositionsKernel, "prevPositions", prevPositionsBuffer); if (orientationsBuffer != null) solverShader.SetBuffer(updatePositionsKernel, "orientations", orientationsBuffer); if (prevOrientationsBuffer != null) solverShader.SetBuffer(updatePositionsKernel, "prevOrientations", prevOrientationsBuffer); if (velocitiesBuffer != null) solverShader.SetBuffer(updatePositionsKernel, "velocities", velocitiesBuffer); if (angularVelocitiesBuffer != null) solverShader.SetBuffer(updatePositionsKernel, "angularVelocities", angularVelocitiesBuffer); if (positionsBuffer != null) solverShader.SetBuffer(enforceLimitsKernel, "positions", positionsBuffer); if (prevPositionsBuffer != null) solverShader.SetBuffer(enforceLimitsKernel, "prevPositions", prevPositionsBuffer); if (lifeBuffer != null) solverShader.SetBuffer(enforceLimitsKernel, "life", lifeBuffer); if (phasesBuffer != null) solverShader.SetBuffer(enforceLimitsKernel, "phases", phasesBuffer); if (positionsBuffer != null) solverShader.SetBuffer(interpolateKernel, "positions", positionsBuffer); if (startPositionsBuffer != null) solverShader.SetBuffer(interpolateKernel, "startPositions", startPositionsBuffer); if (endPositionsBuffer != null) solverShader.SetBuffer(interpolateKernel, "endPositions", endPositionsBuffer); if (renderablePositionsBuffer != null) solverShader.SetBuffer(interpolateKernel, "renderablePositions", renderablePositionsBuffer); if (orientationsBuffer != null) solverShader.SetBuffer(interpolateKernel, "orientations", orientationsBuffer); if (startOrientationsBuffer != null) solverShader.SetBuffer(interpolateKernel, "startOrientations", startOrientationsBuffer); if (endOrientationsBuffer != null) solverShader.SetBuffer(interpolateKernel, "endOrientations", endOrientationsBuffer); if (renderableOrientationsBuffer != null) solverShader.SetBuffer(interpolateKernel, "renderableOrientations", renderableOrientationsBuffer); if (principalRadiiBuffer != null) solverShader.SetBuffer(interpolateKernel, "principalRadii", principalRadiiBuffer); if (renderableRadiiBuffer != null) solverShader.SetBuffer(interpolateKernel, "renderableRadii", renderableRadiiBuffer); } public void MaxFoamParticleCountChanged(ObiSolver solver) { auxPositions?.Dispose(); auxVelocities?.Dispose(); auxColors?.Dispose(); auxAttributes?.Dispose(); auxOffsetInCell?.Dispose(); auxSortedToOriginal?.Dispose(); if (m_Solver.maxFoamParticles > 0) { solver.foamPositions.AsComputeBuffer(); solver.foamVelocities.AsComputeBuffer(); solver.foamColors.AsComputeBuffer(); solver.foamAttributes.AsComputeBuffer(); auxPositions = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)m_Solver.maxFoamParticles, 16); auxVelocities = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)m_Solver.maxFoamParticles, 16); auxColors = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)m_Solver.maxFoamParticles, 16); auxAttributes = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)m_Solver.maxFoamParticles, 16); auxOffsetInCell = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)m_Solver.maxFoamParticles, 4); auxSortedToOriginal = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)m_Solver.maxFoamParticles, 4); } } public void SetRigidbodyArrays(ObiSolver solver) { rigidbodyLinearDeltasBuffer = solver.rigidbodyLinearDeltas.SafeAsComputeBuffer(); rigidbodyAngularDeltasBuffer = solver.rigidbodyAngularDeltas.SafeAsComputeBuffer(); if (rigidbodyLinearDeltasIntBuffer != null) { rigidbodyLinearDeltasIntBuffer.Dispose(); rigidbodyLinearDeltasIntBuffer = null; } if (rigidbodyAngularDeltasIntBuffer != null) { rigidbodyAngularDeltasIntBuffer.Dispose(); rigidbodyAngularDeltasIntBuffer = null; } rigidbodyLinearDeltasIntBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, rigidbodyLinearDeltasBuffer.count, solver.rigidbodyLinearDeltas.stride); rigidbodyLinearDeltasIntBuffer.SetData(new Vector4[rigidbodyLinearDeltasBuffer.count]); rigidbodyAngularDeltasIntBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, rigidbodyAngularDeltasBuffer.count, solver.rigidbodyAngularDeltas.stride); rigidbodyAngularDeltasIntBuffer.SetData(new Vector4[rigidbodyAngularDeltasBuffer.count]); } public IConstraintsBatchImpl CreateConstraintsBatch(Oni.ConstraintType type) { if (constraints[(int)type] != null) return constraints[(int)type].CreateConstraintsBatch(); return null; } public void DestroyConstraintsBatch(IConstraintsBatchImpl batch) { if (batch != null && constraints[(int)batch.constraintType] != null) constraints[(int)batch.constraintType].RemoveBatch(batch); } public void FinishSimulation() { // Make sure GPU->CPU readbacks have finished. // by default, only positions and velocities are read back. // ObiActors can request whatever data they need in RequestData, // then wait for it in SimulationEnd. abstraction.positions.WaitForReadback(); abstraction.velocities.WaitForReadback(); abstraction.rigidbodyLinearDeltas.WaitForReadback(); abstraction.rigidbodyAngularDeltas.WaitForReadback(); var sm = constraints[(int)Oni.ConstraintType.ShapeMatching] as ComputeShapeMatchingConstraints; if (sm != null) sm.WaitForReadback(); var dm = constraints[(int)Oni.ConstraintType.Distance] as ComputeDistanceConstraints; if (dm != null) dm.WaitForReadback(); var pm = constraints[(int)Oni.ConstraintType.Pin] as ComputePinConstraints; if (pm != null) pm.WaitForReadback(); var phm = constraints[(int)Oni.ConstraintType.Pinhole] as ComputePinholeConstraints; if (phm != null) phm.WaitForReadback(); abstraction.externalForces.WipeToZero(); abstraction.externalTorques.WipeToZero(); abstraction.externalForces.Upload(); abstraction.externalTorques.Upload(); // copy end to start positions. abstraction.startPositions.CopyFrom(abstraction.endPositions); abstraction.startOrientations.CopyFrom(abstraction.endOrientations); // now that we got position / orientation data in the CPU set them as this step's end positions / orientations. abstraction.endPositions.CopyFrom(abstraction.positions); abstraction.endOrientations.CopyFrom(abstraction.orientations); abstraction.startPositions.Upload(true); abstraction.startOrientations.Upload(true); abstraction.endPositions.Upload(true); abstraction.endOrientations.Upload(true); } public IObiJobHandle CollisionDetection(IObiJobHandle inputDeps, float stepTime) { var collisionParameters = abstraction.GetConstraintParameters(Oni.ConstraintType.Collision); var particleCollisionParameters = abstraction.GetConstraintParameters(Oni.ConstraintType.ParticleCollision); var densityParameters = abstraction.GetConstraintParameters(Oni.ConstraintType.Density); if (particleCollisionParameters.enabled || densityParameters.enabled) { UpdateDiffuseDensity(stepTime); UnityEngine.Profiling.Profiler.BeginSample("Build Simplex Grid"); particleGrid.BuildGrid(this, stepTime); UnityEngine.Profiling.Profiler.EndSample(); if (densityParameters.enabled) { UnityEngine.Profiling.Profiler.BeginSample("Generate Fluid Neighborhoods"); particleGrid.GenerateFluidNeighborhoods(this); UnityEngine.Profiling.Profiler.EndSample(); } if (particleCollisionParameters.enabled) { UnityEngine.Profiling.Profiler.BeginSample("Generate Particle Contacts"); particleGrid.GenerateContacts(this); UnityEngine.Profiling.Profiler.EndSample(); } } if (collisionParameters.enabled) { UnityEngine.Profiling.Profiler.BeginSample("Generate Collider Contacts"); colliderGrid.GenerateContacts(this, stepTime); UnityEngine.Profiling.Profiler.EndSample(); UnityEngine.Profiling.Profiler.BeginSample("Apply Force Zones"); colliderGrid.ApplyForceZones(this, stepTime); UnityEngine.Profiling.Profiler.EndSample(); } return inputDeps; } public IObiJobHandle Substep(IObiJobHandle handle, float stepTime, float substepTime, int steps, float timeLeft) { int threadGroups = ComputeMath.ThreadGroupCount(activeParticleCount, 128); solverShader.SetInt("particleCount", activeParticleCount); // if there's no active particles yet, don't do anything: if (activeParticleCount > 0) { solverShader.SetFloat("deltaTime", substepTime); solverShader.SetFloat("velocityScale", Mathf.Pow(1 - Mathf.Clamp(m_Solver.parameters.damping, 0, 1), substepTime)); // Predict positions: solverShader.Dispatch(predictPositionsKernel, threadGroups, 1, 1); ApplyConstraints(stepTime, substepTime, steps, timeLeft); EnforceLimits(threadGroups); // Update velocities: solverShader.Dispatch(updateVelocitiesKernel, threadGroups, 1, 1); // Calculate velocity adjustments (forces, etc): CalculateVelocityCorrections(stepTime, substepTime, steps, timeLeft); } // Update diffuse particles: (need to have calculated velocity corrections first, to measure vorticity/velocity) int substepsLeft = Mathf.RoundToInt(timeLeft / substepTime); int foamPadding = Mathf.CeilToInt(abstraction.substeps / (float)abstraction.foamSubsteps); if (substepsLeft % foamPadding == 0) { UpdateDiffuseParticles(substepTime * foamPadding); UpdateDiffuseCollisions(substepTime * foamPadding); IntegrateDiffuseParticles(substepTime * foamPadding); } // Update particle positions: if (activeParticleCount > 0) { UpdatePositions(substepTime, threadGroups); } return handle; } private void CalculateVelocityCorrections(float stepTime, float substepTime, int steps, float timeLeft) { // Apply aerodynamic constraints: constraints[(int)Oni.ConstraintType.Aerodynamics].Project(stepTime, substepTime, steps, timeLeft); var densityParameters = m_Solver.GetConstraintParameters(Oni.ConstraintType.Density); if (densityParameters.enabled) { var d = constraints[(int)Oni.ConstraintType.Density] as ComputeDensityConstraints; if (d != null) d.CalculateVelocityCorrections(substepTime); } } private void UpdatePositions(float deltaTime, int threadGroups) { var densityParameters = m_Solver.GetConstraintParameters(Oni.ConstraintType.Density); if (densityParameters.enabled) { var d = constraints[(int)Oni.ConstraintType.Density] as ComputeDensityConstraints; if (d != null) d.ApplyVelocityCorrections(deltaTime); } // Update positions: solverShader.Dispatch(updatePositionsKernel, threadGroups, 1, 1); } private void ApplyConstraints(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 = m_Solver.GetConstraintParameters((Oni.ConstraintType)i); if (parameters.enabled) { maxIterations = Mathf.Max(maxIterations, parameters.iterations); constraints[i].Initialize(stepTime, substepTime, steps, timeLeft); } } // 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] = Mathf.CeilToInt(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) constraints[j].Project(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 = m_Solver.GetConstraintParameters((Oni.ConstraintType)i); if (parameters.enabled && parameters.iterations > 0) constraints[i].Project(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 = m_Solver.GetConstraintParameters(Oni.ConstraintType.ParticleCollision); if (param.enabled && param.iterations > 0) constraints[(int)Oni.ConstraintType.ParticleCollision].Project(stepTime, substepTime, steps, timeLeft); param = m_Solver.GetConstraintParameters(Oni.ConstraintType.Collision); if (param.enabled && param.iterations > 0) constraints[(int)Oni.ConstraintType.Collision].Project(stepTime, substepTime, steps, timeLeft); } private void EnforceLimits(int threadGroups) { if (abstraction.useLimits) { // keep particles within bounds: solverShader.SetBool("killOffLimits", abstraction.killOffLimitsParticles); solverShader.SetVector("boundaryLimitsMin", abstraction.boundaryLimits.min); solverShader.SetVector("boundaryLimitsMax", abstraction.boundaryLimits.max); solverShader.Dispatch(enforceLimitsKernel, threadGroups, 1, 1); } } public IObiJobHandle ApplyInterpolation(IObiJobHandle inputDeps, ObiNativeVector4List startPositions, ObiNativeQuaternionList startOrientations, float stepTime, float unsimulatedTime) { if (particleCount <= 0) return inputDeps; int threadGroups = ComputeMath.ThreadGroupCount(particleCount, 128); solverShader.SetInt("particleCount", particleCount); solverShader.SetFloat("deltaTime", stepTime); solverShader.SetFloat("blendFactor", stepTime > 0 ? unsimulatedTime / stepTime : 0); solverShader.SetInt("interpolate", (int)m_Solver.parameters.interpolation); // Interpolate particle state: solverShader.Dispatch(interpolateKernel, threadGroups, 1, 1); // Reset normals: if ((deformableTriangleCount > 0 || deformableEdgeCount > 0) && normalsBuffer != null) { threadGroups = ComputeMath.ThreadGroupCount(normalsBuffer.count, 128); deformableTrisShader.SetInt("normalsCount", normalsBuffer.count); deformableTrisShader.SetBuffer(resetNormalsKernel, "phases", phasesBuffer); deformableTrisShader.SetBuffer(resetNormalsKernel, "normals", normalsBuffer); deformableTrisShader.SetBuffer(resetNormalsKernel, "tangentsInt", tangentsIntBuffer); deformableTrisShader.Dispatch(resetNormalsKernel, threadGroups, 1, 1); // Update deformable triangle normals if (deformableTriangleCount > 0) { threadGroups = ComputeMath.ThreadGroupCount(deformableTriangleCount, 128); deformableTrisShader.SetBuffer(updateNormalsKernel, "renderablePositions", renderablePositionsBuffer); deformableTrisShader.SetBuffer(updateNormalsKernel, "normalsInt", normalsBuffer); deformableTrisShader.SetBuffer(updateNormalsKernel, "tangentsInt", tangentsIntBuffer); deformableTrisShader.Dispatch(updateNormalsKernel, threadGroups, 1, 1); } // Update deformable edge normals if (deformableEdgeCount > 0) { threadGroups = ComputeMath.ThreadGroupCount(deformableEdgeCount, 128); deformableTrisShader.SetBuffer(updateEdgeNormalsKernel, "renderablePositions", renderablePositionsBuffer); deformableTrisShader.SetBuffer(updateEdgeNormalsKernel, "velocities", velocitiesBuffer); deformableTrisShader.SetBuffer(updateEdgeNormalsKernel, "wind", windBuffer); deformableTrisShader.SetBuffer(updateEdgeNormalsKernel, "normalsInt", normalsBuffer); deformableTrisShader.Dispatch(updateEdgeNormalsKernel, threadGroups, 1, 1); } // Update particle orientations threadGroups = ComputeMath.ThreadGroupCount(normalsBuffer.count, 128); deformableTrisShader.SetBuffer(orientationFromNormalsKernel, "phases", phasesBuffer); deformableTrisShader.SetBuffer(orientationFromNormalsKernel, "renderableOrientations", renderableOrientationsBuffer); deformableTrisShader.SetBuffer(orientationFromNormalsKernel, "normals", normalsBuffer); deformableTrisShader.SetBuffer(orientationFromNormalsKernel, "tangentsInt", tangentsIntBuffer); deformableTrisShader.Dispatch(orientationFromNormalsKernel, threadGroups, 1, 1); } // 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 ComputePinConstraints; if (Application.isPlaying && d != null) d.ProjectRenderablePositions(); } //make sure density constraints are enabled, otherwise particles have no neighbors and neighbor lists will be uninitialized. var param = m_Solver.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 ComputeDensityConstraints; if (Application.isPlaying && d != null) d.CalculateAnisotropyLaplacianSmoothing(); } return inputDeps; } private void UpdateDiffuseDensity(float deltaTime) { var system = abstraction.GetRenderSystem() as ComputeFoamRenderSystem; if (system != null && m_Solver.maxFoamParticles > 0 && particleGrid.cellCounts != null) { for (int i = 0; i < system.renderers.Count; ++i) { // solver indices compute buffer may be null if (system.renderers[i].pressure > 0 && system.renderers[i].actor.solverIndices?.computeBuffer != null) { float scale = 0.01f + Mathf.Clamp01(1 - system.renderers[i].density); float radius = system.renderers[i].size * scale; int cellThreadGroups = ComputeMath.ThreadGroupCount(particleGrid.cellCounts.count, 128); foamDensityShader.SetFloat("deltaTime", deltaTime); foamDensityShader.SetInt("maxCells", particleGrid.cellCounts.count); foamDensityShader.SetInt("maxFoamParticles", abstraction.foamPositions.computeBuffer.count); foamDensityShader.SetInt("mode", (int)abstraction.parameters.mode); foamDensityShader.SetFloat("pressure", system.renderers[i].pressure); foamDensityShader.SetFloat("particleRadius", radius); foamDensityShader.SetFloat("smoothingRadius", radius * 2 * system.renderers[i].smoothingRadius); foamDensityShader.SetFloat("surfaceTension", system.renderers[i].surfaceTension); foamDensityShader.SetFloat("viscosity", system.renderers[i].viscosity); foamDensityShader.SetVector("volumeLightDirection", system.renderers[i].volumeLight != null ? abstraction.transform.InverseTransformDirection(system.renderers[i].volumeLight.transform.forward) : Vector3.down); foamDensityShader.SetBuffer(clearGridKernel, "cellStart", particleGrid.cellOffsets); foamDensityShader.SetBuffer(clearGridKernel, "cellCounts", particleGrid.cellCounts); foamDensityShader.Dispatch(clearGridKernel, cellThreadGroups, 1, 1); foamDensityShader.SetBuffer(insertGridKernel, "inputPositions", abstraction.foamPositions.computeBuffer); foamDensityShader.SetBuffer(insertGridKernel, "offsetInCell", auxOffsetInCell); foamDensityShader.SetBuffer(insertGridKernel, "cellCounts", particleGrid.cellCounts); foamDensityShader.SetBuffer(insertGridKernel, "dispatch", abstraction.foamCount.computeBuffer); foamDensityShader.DispatchIndirect(insertGridKernel, abstraction.foamCount.computeBuffer); // prefix sum particleGrid.cellsPrefixSum.Sum(particleGrid.cellCounts, particleGrid.cellOffsets); foamDensityShader.SetBuffer(sortByGridKernel, "inputPositions", abstraction.foamPositions.computeBuffer); foamDensityShader.SetBuffer(sortByGridKernel, "inputVelocities", abstraction.foamVelocities.computeBuffer); foamDensityShader.SetBuffer(sortByGridKernel, "sortedPositions", auxPositions); foamDensityShader.SetBuffer(sortByGridKernel, "sortedVelocities", auxVelocities); foamDensityShader.SetBuffer(sortByGridKernel, "sortedToOriginal", auxSortedToOriginal); foamDensityShader.SetBuffer(sortByGridKernel, "offsetInCell", auxOffsetInCell); foamDensityShader.SetBuffer(sortByGridKernel, "cellStart", particleGrid.cellOffsets); foamDensityShader.SetBuffer(sortByGridKernel, "cellCounts", particleGrid.cellCounts); foamDensityShader.SetBuffer(sortByGridKernel, "dispatch", abstraction.foamCount.computeBuffer); foamDensityShader.DispatchIndirect(sortByGridKernel, abstraction.foamCount.computeBuffer); foamDensityShader.SetBuffer(computeDensityKernel, "inputPositions", abstraction.foamPositions.computeBuffer); foamDensityShader.SetBuffer(computeDensityKernel, "sortedPositions", auxPositions); foamDensityShader.SetBuffer(computeDensityKernel, "fluidData", auxColors); foamDensityShader.SetBuffer(computeDensityKernel, "cellStart", particleGrid.cellOffsets); foamDensityShader.SetBuffer(computeDensityKernel, "cellCounts", particleGrid.cellCounts); foamDensityShader.SetBuffer(computeDensityKernel, "dispatch", abstraction.foamCount.computeBuffer); foamDensityShader.DispatchIndirect(computeDensityKernel, abstraction.foamCount.computeBuffer); foamDensityShader.SetBuffer(applyDensityKernel, "inputPositions", abstraction.foamPositions.computeBuffer); foamDensityShader.SetBuffer(applyDensityKernel, "inputVelocities", abstraction.foamVelocities.computeBuffer); foamDensityShader.SetBuffer(applyDensityKernel, "sortedPositions", auxPositions); foamDensityShader.SetBuffer(applyDensityKernel, "sortedVelocities", auxVelocities); foamDensityShader.SetBuffer(applyDensityKernel, "sortedToOriginal", auxSortedToOriginal); foamDensityShader.SetBuffer(applyDensityKernel, "fluidData", auxColors); foamDensityShader.SetBuffer(applyDensityKernel, "cellStart", particleGrid.cellOffsets); foamDensityShader.SetBuffer(applyDensityKernel, "cellCounts", particleGrid.cellCounts); foamDensityShader.SetBuffer(applyDensityKernel, "dispatch", abstraction.foamCount.computeBuffer); foamDensityShader.DispatchIndirect(applyDensityKernel, abstraction.foamCount.computeBuffer); } } } else activeFoamParticleCount = 0; } private void UpdateDiffuseCollisions(float deltaTime) { if (!abstraction.foamCollisions) return; foamCollisionShader.SetFloat("radiusScale", 0.3f); foamCollisionShader.SetFloat("colliderCount", colliderGrid.colliderCount); foamCollisionShader.SetInt("maxCells", ComputeColliderWorld.maxCells); foamCollisionShader.SetInt("cellsPerCollider", ComputeColliderWorld.cellsPerCollider); foamCollisionShader.SetInt("shapeTypeCount", Oni.ColliderShapeTypeCount); foamCollisionShader.SetFloat("deltaTime", deltaTime); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "inputPositions", abstraction.foamPositions.computeBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "inputVelocities", abstraction.foamVelocities.computeBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "inputAttributes", abstraction.foamAttributes.computeBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "aabbs", colliderGrid.aabbsBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "transforms", colliderGrid.transformsBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "shapes", colliderGrid.shapesBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "rigidbodies", colliderGrid.rigidbodiesBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "sortedColliderIndices", colliderGrid.sortedColliderIndicesBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "levelPopulation", colliderGrid.levelPopulation); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "cellOffsets", colliderGrid.cellOffsetsBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "cellCounts", colliderGrid.cellCountsBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "triangleMeshHeaders", colliderGrid.triangleMeshHeaders); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "bihNodes", colliderGrid.bihNodes); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "triangles", colliderGrid.triangles); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "vertices", colliderGrid.vertices); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "edgeMeshHeaders", colliderGrid.edgeMeshHeaders); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "edgeBihNodes", colliderGrid.edgeBihNodes); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "edges", colliderGrid.edges); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "edgeVertices", colliderGrid.edgeVertices); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "distanceFieldHeaders", colliderGrid.distanceFieldHeaders); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "dfNodes", colliderGrid.dfNodes); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "heightFieldHeaders", colliderGrid.heightFieldHeaders); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "heightFieldSamples", colliderGrid.heightFieldSamples); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "worldToSolver", worldToSolverBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "solverToWorld", solverToWorldBuffer); foamCollisionShader.SetBuffer(solveDiffuseContactsKernel, "dispatch", abstraction.foamCount.computeBuffer); foamCollisionShader.DispatchIndirect(solveDiffuseContactsKernel, abstraction.foamCount.computeBuffer); } private void UpdateDiffuseParticles(float deltaTime) { var system = abstraction.GetRenderSystem() as ComputeFoamRenderSystem; if (system != null && m_Solver.maxFoamParticles > 0 && particleGrid.sortedLinearVel != null) { foamShader.SetFloat("deltaTime", deltaTime); foamShader.SetFloat("randomSeed", Time.frameCount % 16535 + Random.value); foamShader.SetVector("gravity", m_Solver.parameters.gravity * m_Solver.parameters.foamGravityScale); foamShader.SetVector("agingOverPopulation", new Vector3(m_Solver.foamAccelAgingRange.x, m_Solver.foamAccelAgingRange.y, m_Solver.foamAccelAging)); foamShader.SetInt("maxFoamParticles", abstraction.foamPositions.computeBuffer.count); foamShader.SetInt("minFluidNeighbors", abstraction.foamMinNeighbors); foamShader.SetInt("maxCells", particleGrid.maxCells); foamShader.SetInt("pointCount", simplexCounts.pointCount); foamShader.SetInt("edgeCount", simplexCounts.edgeCount); foamShader.SetInt("triangleCount", simplexCounts.triangleCount); foamShader.SetBuffer(sortDataKernel, "positions", positionsBuffer); foamShader.SetBuffer(sortDataKernel, "velocities", velocitiesBuffer); foamShader.SetBuffer(sortDataKernel, "angularVelocities", angularVelocitiesBuffer); foamShader.SetBuffer(sortDataKernel, "orientations", renderableOrientationsBuffer); foamShader.SetBuffer(sortDataKernel, "principalRadii", renderableRadiiBuffer); foamShader.SetBuffer(sortDataKernel, "sortedPositions", particleGrid.sortedPositions); foamShader.SetBuffer(sortDataKernel, "sortedVelocities", particleGrid.sortedLinearVel); foamShader.SetBuffer(sortDataKernel, "sortedAngularVelocities", particleGrid.sortedAngularVel); foamShader.SetBuffer(sortDataKernel, "sortedOrientations", particleGrid.sortedPrevPosOrientations); foamShader.SetBuffer(sortDataKernel, "sortedRadii", particleGrid.sortedPrincipalRadii); foamShader.SetBuffer(sortDataKernel, "sortedToOriginal", particleGrid.sortedFluidIndices); foamShader.SetBuffer(sortDataKernel, "dispatch", fluidDispatchBuffer); foamShader.DispatchIndirect(sortDataKernel, fluidDispatchBuffer); int threadGroups; foamShader.SetBuffer(emitFoamKernel, "positions", positionsBuffer); foamShader.SetBuffer(emitFoamKernel, "velocities", velocitiesBuffer); foamShader.SetBuffer(emitFoamKernel, "angularVelocities", angularVelocitiesBuffer); foamShader.SetBuffer(emitFoamKernel, "fluidData", fluidDataBuffer); foamShader.SetBuffer(emitFoamKernel, "principalRadii", principalRadiiBuffer); foamShader.SetBuffer(emitFoamKernel, "outputPositions", abstraction.foamPositions.computeBuffer); foamShader.SetBuffer(emitFoamKernel, "outputVelocities", abstraction.foamVelocities.computeBuffer); foamShader.SetBuffer(emitFoamKernel, "outputColors", abstraction.foamColors.computeBuffer); foamShader.SetBuffer(emitFoamKernel, "outputAttributes", abstraction.foamAttributes.computeBuffer); foamShader.SetBuffer(emitFoamKernel, "dispatch", abstraction.foamCount.computeBuffer); foamShader.SetBuffer(emitShapeFoamKernel, "outputPositions", abstraction.foamPositions.computeBuffer); foamShader.SetBuffer(emitShapeFoamKernel, "outputVelocities", abstraction.foamVelocities.computeBuffer); foamShader.SetBuffer(emitShapeFoamKernel, "outputColors", abstraction.foamColors.computeBuffer); foamShader.SetBuffer(emitShapeFoamKernel, "outputAttributes", abstraction.foamAttributes.computeBuffer); foamShader.SetBuffer(emitShapeFoamKernel, "dispatch", abstraction.foamCount.computeBuffer); for (int i = 0; i < system.renderers.Count; ++i) { // solver indices compute buffer may be null if (system.renderers[i].actor.solverIndices?.computeBuffer != null) { threadGroups = ComputeMath.ThreadGroupCount(system.renderers[i].actor.activeParticleCount, 128); foamShader.SetInt("activeParticleCount", system.renderers[i].actor.activeParticleCount); foamShader.SetVector("vorticityRange", system.renderers[i].vorticityRange); foamShader.SetVector("velocityRange", system.renderers[i].velocityRange); foamShader.SetFloat("foamGenerationRate", system.renderers[i].foamGenerationRate); foamShader.SetFloat("potentialIncrease", system.renderers[i].foamPotential); foamShader.SetFloat("potentialDiffusion", Mathf.Pow(1 - Mathf.Clamp01(system.renderers[i].foamPotentialDiffusion), deltaTime)); foamShader.SetFloat("buoyancy", system.renderers[i].buoyancy); foamShader.SetFloat("drag", system.renderers[i].drag); foamShader.SetFloat("airDrag", Mathf.Pow(1 - Mathf.Clamp01(system.renderers[i].atmosphericDrag), deltaTime)); foamShader.SetFloat("airAging", system.renderers[i].airAging); foamShader.SetFloat("isosurface", system.renderers[i].isosurface); foamShader.SetFloat("particleSize", system.renderers[i].size); foamShader.SetFloat("sizeRandom", system.renderers[i].sizeRandom); foamShader.SetFloat("lifetime", system.renderers[i].lifetime); foamShader.SetFloat("lifetimeRandom", system.renderers[i].lifetimeRandom); foamShader.SetVector("foamColor", system.renderers[i].color); if (system.renderers[i] is ObiFoamEmitter) { var emitter = system.renderers[i] as ObiFoamEmitter; int particlesToEmit = emitter.GetParticleNumberToEmit(deltaTime); threadGroups = ComputeMath.ThreadGroupCount(particlesToEmit, 128); foamShader.SetInt("particlesToEmit", particlesToEmit); foamShader.SetInt("emitterShape", (int)emitter.shape); foamShader.SetVector("emitterPosition", emitter.shapeTransform != null? abstraction.transform.InverseTransformPoint(emitter.shapeTransform.position) : Vector3.zero); foamShader.SetVector("emitterRotation", (emitter.shapeTransform != null ? emitter.shapeTransform.rotation * Quaternion.Inverse(abstraction.transform.rotation): Quaternion.identity).AsVector4()); foamShader.SetVector("emitterSize", emitter.shapeSize); foamShader.Dispatch(emitShapeFoamKernel, threadGroups, 1, 1); } else // generator { foamShader.SetBuffer(emitFoamKernel, "activeParticles", system.renderers[i].actor.solverIndices.computeBuffer); foamShader.Dispatch(emitFoamKernel, threadGroups, 1, 1); } } } foamShader.SetBuffer(copyAliveKernel, "dispatch", abstraction.foamCount.computeBuffer); foamShader.Dispatch(copyAliveKernel, 1, 1, 1); foamShader.SetBuffer(updateFoamKernel, "cellOffsets", particleGrid.cellOffsets); foamShader.SetBuffer(updateFoamKernel, "cellCounts", particleGrid.cellCounts); foamShader.SetBuffer(updateFoamKernel, "gridHashToSortedIndex", particleGrid.cellHashToMortonIndex); foamShader.SetBuffer(updateFoamKernel, "levelPopulation", particleGrid.levelPopulation); foamShader.SetBuffer(updateFoamKernel, "solverBounds", reducedBounds); foamShader.SetBuffer(updateFoamKernel, "positions", particleGrid.sortedPositions); foamShader.SetBuffer(updateFoamKernel, "orientations", particleGrid.sortedPrevPosOrientations); foamShader.SetBuffer(updateFoamKernel, "principalRadii", particleGrid.sortedPrincipalRadii); foamShader.SetBuffer(updateFoamKernel, "velocities", particleGrid.sortedLinearVel); foamShader.SetBuffer(updateFoamKernel, "angularVelocities", particleGrid.sortedAngularVel); foamShader.SetBuffer(updateFoamKernel, "fluidMaterial", particleGrid.sortedFluidMaterials); foamShader.SetBuffer(updateFoamKernel, "fluidData", particleGrid.sortedFluidData); foamShader.SetBuffer(updateFoamKernel, "fluidSimplices", particleGrid.sortedSimplexToFluid); foamShader.SetBuffer(updateFoamKernel, "sortedToOriginal", particleGrid.sortedFluidIndices); foamShader.SetBuffer(updateFoamKernel, "inputPositions", abstraction.foamPositions.computeBuffer); foamShader.SetBuffer(updateFoamKernel, "inputVelocities", abstraction.foamVelocities.computeBuffer); foamShader.SetBuffer(updateFoamKernel, "inputColors", abstraction.foamColors.computeBuffer); foamShader.SetBuffer(updateFoamKernel, "inputAttributes", abstraction.foamAttributes.computeBuffer); foamShader.SetBuffer(updateFoamKernel, "outputPositions", auxPositions); foamShader.SetBuffer(updateFoamKernel, "outputVelocities", auxVelocities); foamShader.SetBuffer(updateFoamKernel, "outputColors", auxColors); foamShader.SetBuffer(updateFoamKernel, "outputAttributes", auxAttributes); foamShader.SetBuffer(updateFoamKernel, "dispatch", abstraction.foamCount.computeBuffer); foamShader.DispatchIndirect(updateFoamKernel, abstraction.foamCount.computeBuffer); // copy aux buffers to solver buffers: foamShader.SetBuffer(copyKernel, "inputPositions", auxPositions); foamShader.SetBuffer(copyKernel, "inputVelocities", auxVelocities); foamShader.SetBuffer(copyKernel, "inputColors", auxColors); foamShader.SetBuffer(copyKernel, "inputAttributes", auxAttributes); foamShader.SetBuffer(copyKernel, "outputPositions", abstraction.foamPositions.computeBuffer); foamShader.SetBuffer(copyKernel, "outputVelocities", abstraction.foamVelocities.computeBuffer); foamShader.SetBuffer(copyKernel, "outputColors", abstraction.foamColors.computeBuffer); foamShader.SetBuffer(copyKernel, "outputAttributes", abstraction.foamAttributes.computeBuffer); foamShader.SetBuffer(copyKernel, "dispatch", abstraction.foamCount.computeBuffer); foamShader.DispatchIndirect(copyKernel, abstraction.foamCount.computeBuffer, 16); AsyncGPUReadback.Request(abstraction.foamCount.computeBuffer, 4, 12, (AsyncGPUReadbackRequest obj) => { if (obj.done && !obj.hasError) this.activeFoamParticleCount = obj.GetData()[0]; }); } else activeFoamParticleCount = 0; } private void IntegrateDiffuseParticles(float deltaTime) { foamShader.SetFloat("deltaTime", deltaTime); foamShader.SetBuffer(integrateFoamKernel, "outputPositions", abstraction.foamPositions.computeBuffer); foamShader.SetBuffer(integrateFoamKernel, "outputVelocities", abstraction.foamVelocities.computeBuffer); foamShader.SetBuffer(integrateFoamKernel, "dispatch", abstraction.foamCount.computeBuffer); foamShader.DispatchIndirect(integrateFoamKernel, abstraction.foamCount.computeBuffer); } public void SpatialQuery(ObiNativeQueryShapeList shapes, ObiNativeAffineTransformList transforms, ObiNativeQueryResultList results) { if (abstraction.queryResults.count != abstraction.maxQueryResults) { abstraction.queryResults.ResizeUninitialized((int)abstraction.maxQueryResults); abstraction.queryResults.SafeAsComputeBuffer(GraphicsBuffer.Target.Counter); } spatialQueries.SpatialQuery(this, shapes.SafeAsComputeBuffer(), transforms.SafeAsComputeBuffer(), results.computeBuffer); } public int GetParticleGridSize() { //return particleGrid.grid.usedCells.Length; return 0; } public void GetParticleGrid(ObiNativeAabbList cells) { //particleGrid.GetCells(cells); } } }