Files
2025-05-29 22:31:40 +03:00

220 lines
9.0 KiB
C#

#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<int> firstTriangle;
private NativeArray<int> numTriangles;
private NativeArray<float> restVolumes;
private NativeArray<float2> 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<int>();
this.firstTriangle = firstTriangle.AsNativeArray<int>();
this.numTriangles = numTriangles.AsNativeArray<int>();
this.restVolumes = restVolumes.AsNativeArray<float>();
this.pressureStiffness = pressureStiffness.AsNativeArray<float2>();
this.lambdas = lambdas.AsNativeArray<float>();
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<int> triangles;
[ReadOnly] public NativeArray<int> firstTriangle;
[ReadOnly] public NativeArray<int> numTriangles;
[ReadOnly] public NativeArray<float> restVolumes;
[ReadOnly] public NativeArray<float2> pressureStiffness;
public NativeArray<float> lambdas;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<float> invMasses;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> gradients;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<int> counts;
[ReadOnly] public float deltaTimeSqr;
public void Execute(int i)
{
float compliance = pressureStiffness[i].y / deltaTimeSqr;
NativeList<int> particleIndices = new NativeList<int>(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<int> triangles;
[ReadOnly] public NativeArray<int> firstTriangle;
[ReadOnly] public NativeArray<int> numTriangles;
[ReadOnly] public float sorFactor;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> 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