#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS) using UnityEngine; using Unity.Jobs; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; using Unity.Burst; using System.Collections; namespace Obi { public class BurstVolumeConstraintsBatch : BurstConstraintsBatchImpl, IVolumeConstraintsBatchImpl { private NativeArray firstTriangle; private NativeArray numTriangles; private NativeArray restVolumes; private NativeArray pressureStiffness; public BurstVolumeConstraintsBatch(BurstVolumeConstraints constraints) { m_Constraints = constraints; m_ConstraintType = Oni.ConstraintType.Volume; } public void SetVolumeConstraints(ObiNativeIntList triangles, ObiNativeIntList firstTriangle, ObiNativeIntList numTriangles, ObiNativeFloatList restVolumes, ObiNativeVector2List pressureStiffness, ObiNativeFloatList lambdas, int count) { this.particleIndices = triangles.AsNativeArray(); this.firstTriangle = firstTriangle.AsNativeArray(); this.numTriangles = numTriangles.AsNativeArray(); this.restVolumes = restVolumes.AsNativeArray(); this.pressureStiffness = pressureStiffness.AsNativeArray(); this.lambdas = lambdas.AsNativeArray(); m_ConstraintCount = count; } public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps) { var projectConstraints = new VolumeConstraintsBatchJob() { triangles = particleIndices, firstTriangle = firstTriangle, numTriangles = numTriangles, restVolumes = restVolumes, pressureStiffness = pressureStiffness, lambdas = lambdas, positions = solverImplementation.positions, invMasses = solverImplementation.invMasses, gradients = solverImplementation.fluidData, // reuse fluidData for temp gradients. deltas = solverImplementation.positionDeltas, counts = solverImplementation.positionConstraintCounts, deltaTimeSqr = substepTime * substepTime }; return projectConstraints.Schedule(m_ConstraintCount, 4, inputDeps); } public override JobHandle Apply(JobHandle inputDeps, float substepTime) { var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType); var applyConstraints = new ApplyVolumeConstraintsBatchJob() { triangles = particleIndices, firstTriangle = firstTriangle, numTriangles = numTriangles, positions = solverImplementation.positions, deltas = solverImplementation.positionDeltas, counts = solverImplementation.positionConstraintCounts, sorFactor = parameters.SORFactor }; return applyConstraints.Schedule(m_ConstraintCount, 8, inputDeps); } [BurstCompile] public struct VolumeConstraintsBatchJob : IJobParallelFor { [ReadOnly] public NativeArray triangles; [ReadOnly] public NativeArray firstTriangle; [ReadOnly] public NativeArray numTriangles; [ReadOnly] public NativeArray restVolumes; [ReadOnly] public NativeArray pressureStiffness; public NativeArray lambdas; [ReadOnly] public NativeArray positions; [ReadOnly] public NativeArray invMasses; [NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray gradients; [NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray deltas; [NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray counts; [ReadOnly] public float deltaTimeSqr; public void Execute(int i) { float compliance = pressureStiffness[i].y / deltaTimeSqr; NativeList particleIndices = new NativeList(numTriangles[i] * 3, Allocator.Temp); // calculate volume: float volume = 0; for (int j = 0; j < numTriangles[i]; ++j) { int v = (firstTriangle[i] + j) * 3; int i1 = triangles[v]; int i2 = triangles[v + 1]; int i3 = triangles[v + 2]; particleIndices.Add(i1); particleIndices.Add(i2); particleIndices.Add(i3); //calculate this triangle's volume contribution: volume += math.dot(math.cross(positions[i1].xyz, positions[i2].xyz), positions[i3].xyz) / 6.0f; } particleIndices.Sort(); int particleCount = particleIndices.AsArray().Unique(); // accumulate particle gradients: for (int j = 0; j < particleCount; ++j) gradients[particleIndices[j]] = float4.zero; for (int j = 0; j < numTriangles[i]; ++j) { int v = (firstTriangle[i] + j) * 3; int i1 = triangles[v]; int i2 = triangles[v + 1]; int i3 = triangles[v + 2]; //accumulate gradient for each particle: gradients[i1] += new float4(math.cross(positions[i2].xyz, positions[i3].xyz), 0); gradients[i2] += new float4(math.cross(positions[i3].xyz, positions[i1].xyz), 0); gradients[i3] += new float4(math.cross(positions[i1].xyz, positions[i2].xyz), 0); } // calculate constraint denominator (G(Cj)*inv(M)): float denominator = 0; for (int j = 0; j < particleCount; ++j) { int p = particleIndices[j]; denominator += invMasses[p] * math.lengthsq(gradients[p]); } // equality constraint: volume - pressure * rest volume = 0 float constraint = volume - pressureStiffness[i].x * restVolumes[i]; // calculate lagrange multiplier delta: float dlambda = (-constraint - compliance * lambdas[i]) / (denominator + compliance + BurstMath.epsilon); lambdas[i] += dlambda; // calculate position deltas: for (int j = 0; j < particleCount; ++j) { int p = particleIndices[j]; deltas[p] += dlambda * invMasses[p] * gradients[p]; counts[p]++; } } } [BurstCompile] public struct ApplyVolumeConstraintsBatchJob : IJobParallelFor { [ReadOnly] public NativeArray triangles; [ReadOnly] public NativeArray firstTriangle; [ReadOnly] public NativeArray numTriangles; [ReadOnly] public float sorFactor; [NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray positions; [NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray deltas; [NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray counts; public void Execute(int i) { for (int j = 0; j < numTriangles[i]; ++j) { int v = (firstTriangle[i] + j) * 3; int p1 = triangles[v]; int p2 = triangles[v + 1]; int p3 = triangles[v + 2]; if (counts[p1] > 0) { positions[p1] += deltas[p1] * sorFactor / counts[p1]; deltas[p1] = float4.zero; counts[p1] = 0; } if (counts[p2] > 0) { positions[p2] += deltas[p2] * sorFactor / counts[p2]; deltas[p2] = float4.zero; counts[p2] = 0; } if (counts[p3] > 0) { positions[p3] += deltas[p3] * sorFactor / counts[p3]; deltas[p3] = float4.zero; counts[p3] = 0; } } } } } } #endif