修改水
This commit is contained in:
@@ -11,13 +11,8 @@ namespace Obi
|
||||
bool usesOrientedParticles { get; }
|
||||
|
||||
int GetParticleRuntimeIndex(int index); // returns solver or blueprint index, depending on implementation.
|
||||
|
||||
Vector3 GetParticlePosition(int index);
|
||||
Quaternion GetParticleOrientation(int index);
|
||||
|
||||
Vector3 GetParticleRestPosition(int index);
|
||||
Quaternion GetParticleRestOrientation(int index);
|
||||
|
||||
void GetParticleAnisotropy(int index, ref Vector4 b1, ref Vector4 b2, ref Vector4 b3);
|
||||
float GetParticleMaxRadius(int index);
|
||||
Color GetParticleColor(int index);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ namespace Obi
|
||||
{
|
||||
public class BurstBackend : IObiBackend
|
||||
{
|
||||
#region Solver
|
||||
#region Solver
|
||||
public ISolverImpl CreateSolver(ObiSolver solver, int capacity)
|
||||
{
|
||||
return new BurstSolverImpl(solver);
|
||||
@@ -20,7 +20,8 @@ namespace Obi
|
||||
if (solver != null)
|
||||
solver.Destroy();
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -39,9 +39,7 @@ namespace Obi
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 DifferentiateAngular(quaternion rotation, quaternion prevRotation, float dt)
|
||||
{
|
||||
quaternion deltaq = math.mul(rotation, math.inverse(prevRotation));
|
||||
float sign = deltaq.value.w >= 0 ? 1 : -1;
|
||||
return new float4(sign * deltaq.value.xyz * 2.0f / dt, 0);
|
||||
return new float4((math.mul(rotation, math.inverse(prevRotation)).value * 2.0f / dt).xyz, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,22 @@ namespace Obi
|
||||
{
|
||||
public class BurstJobHandle : IObiJobHandle
|
||||
{
|
||||
public JobHandle jobHandle { get; set; } = new JobHandle();
|
||||
private JobHandle handle = new JobHandle();
|
||||
|
||||
public BurstJobHandle SetHandle(JobHandle newHandle)
|
||||
{
|
||||
handle = newHandle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Complete()
|
||||
{
|
||||
jobHandle.Complete();
|
||||
handle.Complete();
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
jobHandle = new JobHandle();
|
||||
handle = new JobHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Runtime.CompilerServices;
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using System.Threading;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
namespace Obi
|
||||
{
|
||||
public static class BurstMath
|
||||
{
|
||||
|
||||
@@ -20,83 +19,23 @@ namespace Obi
|
||||
public const float one = 1;
|
||||
public static readonly float golden = (math.sqrt(5.0f) + 1) / 2.0f;
|
||||
|
||||
public static unsafe void AddRange<T, U>(this NativeList<T> dst, U[] array) where T : unmanaged where U : unmanaged
|
||||
{
|
||||
var tsize = sizeof(T);
|
||||
var usize = sizeof(U);
|
||||
|
||||
if (tsize == usize)
|
||||
{
|
||||
int dstIndex = dst.Length;
|
||||
dst.ResizeUninitialized(dst.Length + array.Length);
|
||||
|
||||
fixed (U* srcPtr = array)
|
||||
{
|
||||
var dstPtr = (T*)dst.GetUnsafePtr() + dstIndex;
|
||||
UnsafeUtility.MemCpy(dstPtr, srcPtr, usize * array.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void AddReplicate<T, U>(this NativeList<T> dst, U value, int length) where T : unmanaged where U : unmanaged
|
||||
{
|
||||
var tsize = sizeof(T);
|
||||
var usize = sizeof(U);
|
||||
|
||||
if (tsize == usize)
|
||||
{
|
||||
int dstIndex = dst.Length;
|
||||
dst.ResizeUninitialized(dst.Length + length);
|
||||
|
||||
var dstPtr = (T*)dst.GetUnsafePtr() + dstIndex;
|
||||
var srcPtr = UnsafeUtility.AddressOf(ref value);
|
||||
UnsafeUtility.MemCpyReplicate(dstPtr, srcPtr, tsize, length);
|
||||
}
|
||||
}
|
||||
|
||||
public static float AtomicAdd(ref float location, float value)
|
||||
{
|
||||
float newCurrentValue = location;
|
||||
while (true)
|
||||
{
|
||||
float currentValue = newCurrentValue;
|
||||
float newValue = currentValue + value;
|
||||
newCurrentValue = Interlocked.CompareExchange(ref location, newValue, currentValue);
|
||||
if (newCurrentValue.Equals(currentValue))
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
|
||||
// multiplies a column vector by a row vector.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe static void AtomicAdd(NativeArray<float4> array, int p, float4 data)
|
||||
public static float3x3 multrnsp(float4 column, float4 row)
|
||||
{
|
||||
float4* arr = (float4*)array.GetUnsafePtr();
|
||||
AtomicAdd(ref arr[p].x, data.x);
|
||||
AtomicAdd(ref arr[p].y, data.y);
|
||||
AtomicAdd(ref arr[p].z, data.z);
|
||||
AtomicAdd(ref arr[p].w, data.w);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe static void AtomicAdd(NativeArray<float> array, int p, float data)
|
||||
{
|
||||
float* arr = (float*)array.GetUnsafePtr();
|
||||
AtomicAdd(ref arr[p], data);
|
||||
return new float3x3(column[0] * row[0], column[0] * row[1], column[0] * row[2],
|
||||
column[1] * row[0], column[1] * row[1], column[1] * row[2],
|
||||
column[2] * row[0], column[2] * row[1], column[2] * row[2]);
|
||||
}
|
||||
|
||||
// multiplies a column vector by a row vector.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float3x3 multrnsp(in float4 column, in float4 row)
|
||||
public static float4x4 multrnsp4(float4 column, float4 row)
|
||||
{
|
||||
return new float3x3(column.xyz * row[0], column.xyz * row[1], column.xyz * row[2]);
|
||||
}
|
||||
|
||||
// multiplies a column vector by a row vector.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4x4 multrnsp4(in float4 column, float4 row)
|
||||
{
|
||||
row[3] = 0;
|
||||
return new float4x4(column * row[0], column * row[1], column * row[2], float4.zero);
|
||||
return new float4x4(column[0] * row[0], column[0] * row[1], column[0] * row[2], 0,
|
||||
column[1] * row[0], column[1] * row[1], column[1] * row[2], 0,
|
||||
column[2] * row[0], column[2] * row[1], column[2] * row[2], 0,
|
||||
0, 0, 0, 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -108,33 +47,6 @@ namespace Obi
|
||||
return math.dot(onto, vector) * onto / len;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float3 project(this float3 vector, float3 onto)
|
||||
{
|
||||
float len = math.lengthsq(onto);
|
||||
if (len < epsilon)
|
||||
return float3.zero;
|
||||
return math.dot(onto, vector) * onto / len;
|
||||
}
|
||||
|
||||
/*[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 GetInvParticleInertiaTensor(float4 principalRadii, float invRotationalMass)
|
||||
{
|
||||
float4 sqrRadii = principalRadii * principalRadii;
|
||||
return new float4(5 * invRotationalMass / math.max(new float3(sqrRadii[1] + sqrRadii[2],
|
||||
sqrRadii[0] + sqrRadii[2],
|
||||
sqrRadii[0] + sqrRadii[1]), epsilon), 0);
|
||||
}*/
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 GetParticleInertiaTensor(float4 principalRadii, float invRotationalMass)
|
||||
{
|
||||
float4 sqrRadii = principalRadii * principalRadii;
|
||||
return 0.2f / (invRotationalMass + epsilon) * new float4(sqrRadii[1] + sqrRadii[2],
|
||||
sqrRadii[0] + sqrRadii[2],
|
||||
sqrRadii[0] + sqrRadii[1], 0);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4x4 TransformInertiaTensor(float4 tensor, quaternion rotation)
|
||||
{
|
||||
@@ -171,41 +83,30 @@ namespace Obi
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
NativeArray<float4> linearDeltas,
|
||||
NativeArray<float4> angularDeltas,
|
||||
BurstInertialFrame solverToWorld)
|
||||
BurstAffineTransform solverToWorld)
|
||||
{
|
||||
float4 linear = rigidbodies[rigidbodyIndex].velocity + linearDeltas[rigidbodyIndex];
|
||||
float4 angular = rigidbodies[rigidbodyIndex].angularVelocity + angularDeltas[rigidbodyIndex];
|
||||
float4 r = solverToWorld.frame.TransformPoint(point) - rigidbodies[rigidbodyIndex].com;
|
||||
float4 r = solverToWorld.TransformPoint(point) - rigidbodies[rigidbodyIndex].com;
|
||||
|
||||
// calculate rigidbody velocity:
|
||||
float4 wsRigidbodyVelocity = linear + new float4(math.cross(angular.xyz, r.xyz), 0);
|
||||
|
||||
// calculate solver velocity:
|
||||
float4 wsSolverVelocity = solverToWorld.velocity + new float4(math.cross(solverToWorld.angularVelocity.xyz, point.xyz), 0);
|
||||
|
||||
// convert the resulting velocity back to solver space:
|
||||
return solverToWorld.frame.InverseTransformVector(wsRigidbodyVelocity - wsSolverVelocity);
|
||||
// Point is assumed to be expressed in solver space. Since rigidbodies are expressed in world space, we need to convert the
|
||||
// point to world space, and convert the resulting velocity back to solver space.
|
||||
return solverToWorld.InverseTransformVector(linear + new float4(math.cross(angular.xyz, r.xyz), 0));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 GetRigidbodyVelocityAtPoint(int rigidbodyIndex,
|
||||
float4 point,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
BurstInertialFrame solverToWorld)
|
||||
BurstAffineTransform solverToWorld)
|
||||
{
|
||||
float4 linear = rigidbodies[rigidbodyIndex].velocity;
|
||||
float4 angular = rigidbodies[rigidbodyIndex].angularVelocity;
|
||||
float4 r = solverToWorld.frame.TransformPoint(point) - rigidbodies[rigidbodyIndex].com;
|
||||
|
||||
// calculate rigidbody velocity:
|
||||
float4 wsRigidbodyVelocity = linear + new float4(math.cross(angular.xyz, r.xyz), 0);
|
||||
|
||||
// calculate solver velocity:
|
||||
float4 wsSolverVelocity = solverToWorld.velocity + new float4(math.cross(solverToWorld.angularVelocity.xyz, point.xyz), 0);
|
||||
float4 r = solverToWorld.TransformPoint(point) - rigidbodies[rigidbodyIndex].com;
|
||||
|
||||
// Point is assumed to be expressed in solver space. Since rigidbodies are expressed in world space, we need to convert the
|
||||
// point to world space, and convert the resulting velocity back to solver space.
|
||||
return solverToWorld.frame.InverseTransformVector(wsRigidbodyVelocity - wsSolverVelocity);
|
||||
return solverToWorld.InverseTransformVector(linear + new float4(math.cross(angular.xyz, r.xyz), 0));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -219,12 +120,8 @@ namespace Obi
|
||||
{
|
||||
float4 impulseWS = solverToWorld.TransformVector(impulse);
|
||||
float4 r = solverToWorld.TransformPoint(point) - rigidbodies[rigidbodyIndex].com;
|
||||
|
||||
float4 linearDelta = rigidbodies[rigidbodyIndex].inverseMass * impulseWS;
|
||||
float4 angularDelta = math.mul(rigidbodies[rigidbodyIndex].inverseInertiaTensor, new float4(math.cross(r.xyz, impulseWS.xyz), 0));
|
||||
|
||||
AtomicAdd(linearDeltas, rigidbodyIndex, linearDelta);
|
||||
AtomicAdd(angularDeltas, rigidbodyIndex, angularDelta);
|
||||
linearDeltas[rigidbodyIndex] += rigidbodies[rigidbodyIndex].inverseMass * impulseWS;
|
||||
angularDeltas[rigidbodyIndex] += math.mul(rigidbodies[rigidbodyIndex].inverseInertiaTensor, new float4(math.cross(r.xyz, impulseWS.xyz), 0));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -240,7 +137,7 @@ namespace Obi
|
||||
|
||||
// convert quaternion delta to angular acceleration:
|
||||
quaternion newRotation = math.normalize(new quaternion(rotationWS.value + deltaWS.value));
|
||||
AtomicAdd(angularDeltas, rigidbodyIndex, BurstIntegration.DifferentiateAngular(newRotation, rotationWS, dt));
|
||||
angularDeltas[rigidbodyIndex] += BurstIntegration.DifferentiateAngular(newRotation, rotationWS, dt);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -250,15 +147,6 @@ namespace Obi
|
||||
if (dot < 0) normal -= 2 * dot * forward;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void OneSidedNormal2(float4 xij, ref float4 nij)
|
||||
{
|
||||
float dot = math.dot(xij.xyz, nij.xyz);
|
||||
if (dot < 0)
|
||||
nij = xij - 2 * dot * nij;
|
||||
else nij = xij;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float EllipsoidRadius(float4 normSolverDirection, quaternion orientation, float3 radii)
|
||||
{
|
||||
@@ -269,20 +157,15 @@ namespace Obi
|
||||
|
||||
public static quaternion ExtractRotation(float4x4 matrix, quaternion rotation, int iterations)
|
||||
{
|
||||
return ExtractRotation((float3x3)matrix, rotation, iterations);
|
||||
}
|
||||
|
||||
public static quaternion ExtractRotation(float3x3 matrix, quaternion rotation, int iterations)
|
||||
{
|
||||
float3x3 R;
|
||||
float4x4 R;
|
||||
for (int i = 0; i < iterations; ++i)
|
||||
{
|
||||
R = rotation.toMatrix3();
|
||||
float3 omega = (math.cross(R.c0, matrix.c0) + math.cross(R.c1, matrix.c1) + math.cross(R.c2, matrix.c2)) /
|
||||
(math.abs(math.dot(R.c0, matrix.c0) + math.dot(R.c1, matrix.c1) + math.dot(R.c2, matrix.c2)) + epsilon);
|
||||
R = rotation.toMatrix();
|
||||
float3 omega = (math.cross(R.c0.xyz, matrix.c0.xyz) + math.cross(R.c1.xyz, matrix.c1.xyz) + math.cross(R.c2.xyz, matrix.c2.xyz)) /
|
||||
(math.abs(math.dot(R.c0.xyz, matrix.c0.xyz) + math.dot(R.c1.xyz, matrix.c1.xyz) + math.dot(R.c2.xyz, matrix.c2.xyz)) + BurstMath.epsilon);
|
||||
|
||||
float w = math.length(omega);
|
||||
if (w < epsilon)
|
||||
if (w < BurstMath.epsilon)
|
||||
break;
|
||||
|
||||
rotation = math.normalize(math.mul(quaternion.AxisAngle((1.0f / w) * omega, w), rotation));
|
||||
@@ -320,25 +203,6 @@ namespace Obi
|
||||
0, 0, 0, 1);
|
||||
}
|
||||
|
||||
public static float3x3 toMatrix3(this quaternion q)
|
||||
{
|
||||
float xx = q.value.x * q.value.x;
|
||||
float xy = q.value.x * q.value.y;
|
||||
float xz = q.value.x * q.value.z;
|
||||
float xw = q.value.x * q.value.w;
|
||||
|
||||
float yy = q.value.y * q.value.y;
|
||||
float yz = q.value.y * q.value.z;
|
||||
float yw = q.value.y * q.value.w;
|
||||
|
||||
float zz = q.value.z * q.value.z;
|
||||
float zw = q.value.z * q.value.w;
|
||||
|
||||
return new float3x3(1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw),
|
||||
2 * (xy + zw), 1 - 2 * (xx + zz), 2 * (yz - xw),
|
||||
2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4x4 asDiagonal(this float4 v)
|
||||
{
|
||||
@@ -348,11 +212,6 @@ namespace Obi
|
||||
0, 0, 0, v.w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modulo operator that also follows intuition for negative arguments. That is , -1 mod 3 = 2, not -1.
|
||||
*/
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float3 nfmod(float3 a, float3 b)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 diagonal(this float4x4 value)
|
||||
{
|
||||
@@ -403,8 +262,8 @@ namespace Obi
|
||||
V.c0 = V0;
|
||||
V.c1 = V1;
|
||||
V.c2 = V2;
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
static float3 unitOrthogonal(this float3 input)
|
||||
{
|
||||
// Find a vector to cross() the input with.
|
||||
@@ -419,9 +278,9 @@ namespace Obi
|
||||
float invnm = 1 / math.length(input.yz);
|
||||
return new float3(0, -input.z * invnm, input.y * invnm);
|
||||
}
|
||||
|| !(input.y < input.z * epsilon))
|
||||
{
|
||||
float invnm = 1 / math.length(input.xy);
|
||||
}
|
||||
|
||||
// D is symmetric, S is an eigen value
|
||||
static float3 EigenVector(float3x3 D, float S)
|
||||
{
|
||||
// Compute a cofactor matrix of D - sI.
|
||||
@@ -458,8 +317,7 @@ namespace Obi
|
||||
{
|
||||
V[0] = 1; return V;
|
||||
}
|
||||
else
|
||||
index = 2;
|
||||
else if (index == 0)
|
||||
{
|
||||
V[0] = c0p[0]; V[1] = c1p[0]; V[2] = c2p[0];
|
||||
}
|
||||
@@ -472,8 +330,8 @@ namespace Obi
|
||||
V = c2p;
|
||||
}
|
||||
return math.normalize(V);
|
||||
}
|
||||
else if (index == 1)
|
||||
}
|
||||
|
||||
static float3 EigenValues(float3x3 D)
|
||||
{
|
||||
float one_third = 1 / 3.0f;
|
||||
@@ -532,8 +390,8 @@ namespace Obi
|
||||
}
|
||||
|
||||
return new float3(e2, e1, e0);
|
||||
e2 = aux;
|
||||
}
|
||||
}
|
||||
|
||||
public struct CachedTri
|
||||
{
|
||||
public float4 vertex;
|
||||
@@ -554,8 +412,8 @@ namespace Obi
|
||||
data[2] = math.dot(edge1, edge1);
|
||||
data[3] = data[0] * data[2] - data[1] * data[1];
|
||||
}
|
||||
{
|
||||
vertex = v1;
|
||||
}
|
||||
|
||||
public static float4 NearestPointOnTri(in CachedTri tri,
|
||||
float4 p,
|
||||
out float4 bary)
|
||||
@@ -718,21 +576,7 @@ namespace Obi
|
||||
float4 ap = p - a;
|
||||
float4 ab = b - a;
|
||||
|
||||
bary = new float4(1 - (t0 + t1), t0, t1,0);
|
||||
return tri.vertex + t0 * tri.edge0 + t1 * tri.edge1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 NearestPointOnEdge(float4 a, float4 b, float4 p, out float mu, bool clampToSegment = true)
|
||||
{
|
||||
float4 ap = p - a;
|
||||
float4 ab = b - a;
|
||||
|
||||
mu = math.dot(ap, ab) / (math.dot(ab, ab) + epsilon);
|
||||
|
||||
if (clampToSegment)
|
||||
mu = math.saturate(mu);
|
||||
|
||||
mu = math.dot(ap, ab) / math.dot(ab, ab);
|
||||
|
||||
if (clampToSegment)
|
||||
mu = math.saturate(mu);
|
||||
@@ -835,178 +679,6 @@ namespace Obi
|
||||
return center;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 BarycenterForSimplexOfSize(int simplexSize)
|
||||
{
|
||||
float value = 1f / simplexSize;
|
||||
float4 center = float4.zero;
|
||||
for (int i = 0; i < simplexSize; ++i)
|
||||
center[i] = value;
|
||||
return center;
|
||||
}
|
||||
|
||||
// https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
|
||||
|
||||
// "Insert" a 0 bit after each of the 16 low bits of x
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint Part1By1(uint x)
|
||||
{
|
||||
x &= 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
|
||||
x = (x ^ (x << 8)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
|
||||
x = (x ^ (x << 4)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
|
||||
x = (x ^ (x << 2)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
|
||||
x = (x ^ (x << 1)) & 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
// "Insert" two 0 bits after each of the 10 low bits of x
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint Part1By2(uint x)
|
||||
{
|
||||
x &= 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210
|
||||
x = (x ^ (x << 16)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210
|
||||
x = (x ^ (x << 8)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210
|
||||
x = (x ^ (x << 4)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10
|
||||
x = (x ^ (x << 2)) & 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0
|
||||
return x;
|
||||
}
|
||||
|
||||
// Inverse of Part1By1 - "delete" all odd-indexed bits
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint Compact1By1(uint x)
|
||||
{
|
||||
x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
|
||||
x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
|
||||
x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
|
||||
x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
|
||||
x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
|
||||
return x;
|
||||
}
|
||||
|
||||
// Inverse of Part1By2 - "delete" all bits not at positions divisible by 3
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint Compact1By2(uint x)
|
||||
{
|
||||
x &= 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0
|
||||
x = (x ^ (x >> 2)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10
|
||||
x = (x ^ (x >> 4)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210
|
||||
x = (x ^ (x >> 8)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210
|
||||
x = (x ^ (x >> 16)) & 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210
|
||||
return x;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint EncodeMorton2(uint2 coords)
|
||||
{
|
||||
return (Part1By1(coords.y) << 1) + Part1By1(coords.x);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint EncodeMorton3(uint3 coords)
|
||||
{
|
||||
return (Part1By2(coords.z) << 2) + (Part1By2(coords.y) << 1) + Part1By2(coords.x);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint3 DecodeMorton2(uint code)
|
||||
{
|
||||
return new uint3(Compact1By1(code >> 0), Compact1By1(code >> 1), 0);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint3 DecodeMorton3(uint code)
|
||||
{
|
||||
return new uint3(Compact1By2(code >> 0), Compact1By2(code >> 1), Compact1By2(code >> 2));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 UnpackFloatRGBA(float v)
|
||||
{
|
||||
uint rgba = math.asuint(v);
|
||||
float r = ((rgba & 0xff000000) >> 24) / 255f;
|
||||
float g = ((rgba & 0x00ff0000) >> 16) / 255f;
|
||||
float b = ((rgba & 0x0000ff00) >> 8) / 255f;
|
||||
float a = (rgba & 0x000000ff) / 255f;
|
||||
return new float4(r, g, b, a);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float PackFloatRGBA(float4 enc)
|
||||
{
|
||||
uint rgba = ((uint)(enc.x * 255f) << 24) +
|
||||
((uint)(enc.y * 255f) << 16) +
|
||||
((uint)(enc.z * 255f) << 8) +
|
||||
(uint)(enc.w * 255f);
|
||||
return math.asfloat(rgba);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float2 UnpackFloatRG(float v)
|
||||
{
|
||||
uint rgba = math.asuint(v);
|
||||
float r = ((rgba & 0xffff0000) >> 16) / 65535f;
|
||||
float g = (rgba & 0x0000ffff) / 65535f;
|
||||
return new float2(r, g);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float PackFloatRG(float2 enc)
|
||||
{
|
||||
uint rgba = ((uint)(enc.x * 65535f) << 16) +
|
||||
(uint)(enc.y * 65535f);
|
||||
return math.asfloat(rgba);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static float2 OctWrap(float2 v)
|
||||
{
|
||||
return (1.0f - math.abs(v.yx)) * new float2(v.x >= 0.0f ? 1.0f : -1.0f, v.y >= 0.0f ? 1.0f : -1.0f);
|
||||
}
|
||||
|
||||
// use octahedral encoding to reduce to 2 coords, then pack them as two 16 bit values in a 32 bit float.
|
||||
public static float OctEncode(float3 n)
|
||||
{
|
||||
n /= math.abs(n.x) + math.abs(n.y) + math.abs(n.z);
|
||||
n.xy = n.z >= 0.0 ? n.xy : OctWrap(n.xy);
|
||||
n.xy = n.xy * 0.5f + 0.5f;
|
||||
|
||||
uint nx = (uint)(n.x * 0xffff);
|
||||
uint ny = (uint)(n.y * 0xffff);
|
||||
return math.asfloat((nx << 16) | (ny & 0xffff));
|
||||
}
|
||||
|
||||
public static float3 OctDecode(float k)
|
||||
{
|
||||
uint d = math.asuint(k);
|
||||
float2 f = new float2((d >> 16) / 65535f, (d & 0xffff) / 65535f) * 2f - 1f;
|
||||
|
||||
float3 n = new float3(f.x, f.y, 1.0f - math.abs(f.x) - math.abs(f.y));
|
||||
float t = math.saturate(-n.z);
|
||||
n.x += n.x >= 0.0f ? -t : t;
|
||||
n.y += n.y >= 0.0f ? -t : t;
|
||||
return math.normalize(n);
|
||||
}
|
||||
|
||||
public static float Remap01(float value, float min_, float max_)
|
||||
{
|
||||
return (math.min(value, max_) - math.min(value, min_)) / (max_ - min_);
|
||||
}
|
||||
|
||||
public static float3 Sort(this float3 f)
|
||||
{
|
||||
float aux;
|
||||
if (f.x > f.y)
|
||||
{
|
||||
aux = f.x;
|
||||
f.x = f.y;
|
||||
f.y = aux;
|
||||
}
|
||||
if (f.x > f.z)
|
||||
{
|
||||
aux = f.x;
|
||||
f.x = f.z;
|
||||
f.z = aux;
|
||||
public static unsafe void RemoveRangeBurst<T>(this NativeList<T> list, int index, int count)
|
||||
where T : unmanaged
|
||||
{
|
||||
@@ -1029,57 +701,6 @@ namespace Obi
|
||||
list.RemoveAtSwapBack(list.Length - 1);
|
||||
}
|
||||
}
|
||||
byte* basePtr = (byte*)list.GetUnsafePtr();
|
||||
|
||||
UnsafeUtility.MemMove(basePtr + (index * elemSize), basePtr + ((index + count) * elemSize), elemSize * (list.Length - count - index));
|
||||
|
||||
// No easy way to change length so we just loop this unfortunately.
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
list.RemoveAtSwapBack(list.Length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
//https://www.shadertoy.com/view/4djSRW
|
||||
public static float3 Hash33(float3 p3)
|
||||
{
|
||||
p3 = math.frac(p3 * new float3(.1031f, .1030f, .0973f));
|
||||
p3 += math.dot(p3, p3.yxz + 33.33f);
|
||||
return math.frac((p3.xxy + p3.yxx) * p3.zyx);
|
||||
}
|
||||
|
||||
public static float Hash13(float3 p3)
|
||||
{
|
||||
p3 = math.frac(p3 * .1031f);
|
||||
p3 += math.dot(p3, p3.zyx + 31.32f);
|
||||
return math.frac((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
public static float2 Hash21(float p)
|
||||
{
|
||||
float3 p3 = math.frac(new float3(p) * new float3(.1031f, .1030f, .0973f));
|
||||
p3 += math.dot(p3, p3.yzx + 33.33f);
|
||||
return math.frac((p3.xx + p3.yz) * p3.zy);
|
||||
}
|
||||
|
||||
public static float3 Hash31(float p)
|
||||
{
|
||||
float3 p3 = math.frac(new float3(p) * new float3(.1031f, .1030f, .0973f));
|
||||
p3 += math.dot(p3, p3.yzx + 33.33f);
|
||||
return math.frac((p3.xxy + p3.yzz) * p3.zyx);
|
||||
}
|
||||
|
||||
public static void RandomInCylinder(float seed, float4 pos, float4 dir, float length, float radius, out float4 position, out float3 velocity)
|
||||
{
|
||||
float3 rand = Hash31(seed);
|
||||
|
||||
float3 b1 = dir.xyz;
|
||||
float3 b2 = math.normalizesafe(math.cross(b1, new float3(1, 0, 0)));
|
||||
float3 b3 = math.cross(b2, b1);
|
||||
|
||||
float theta = rand.y * 2 * math.PI;
|
||||
float2 disc = radius * math.sqrt(rand.x) * new float2(math.cos(theta), math.sin(theta));
|
||||
|
||||
velocity = b2 * disc.x + b3 * disc.y;
|
||||
position = new float4(pos.xyz + b1 * length * rand.z + velocity, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,15 +1,14 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstBox : BurstLocalOptimization.IDistanceFunction
|
||||
public struct BurstBox : BurstLocalOptimization.IDistanceFunction, IBurstCollider
|
||||
{
|
||||
public BurstColliderShape shape;
|
||||
public BurstAffineTransform colliderToSolver;
|
||||
public float dt;
|
||||
|
||||
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
|
||||
{
|
||||
@@ -19,7 +18,7 @@ namespace Obi
|
||||
// clamp the point to the surface of the box:
|
||||
point = colliderToSolver.InverseTransformPointUnscaled(point) - center;
|
||||
|
||||
if (shape.is2D)
|
||||
if (shape.is2D != 0)
|
||||
point[2] = 0;
|
||||
|
||||
// get minimum distance for each axis:
|
||||
@@ -56,100 +55,39 @@ namespace Obi
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(projectedPoint.normal);
|
||||
}
|
||||
|
||||
public static JobHandle GenerateContacts(ObiColliderWorld world,
|
||||
BurstSolverImpl solver,
|
||||
NativeList<Oni.ContactPair> contactPairs,
|
||||
NativeQueue<BurstContact> contactQueue,
|
||||
NativeArray<int> contactOffsetsPerType,
|
||||
float deltaTime,
|
||||
JobHandle inputDeps)
|
||||
public void Contacts(int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> velocities,
|
||||
NativeArray<float4> radii,
|
||||
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.Box + 1] - contactOffsetsPerType[(int)Oni.ShapeType.Box];
|
||||
if (pairCount == 0) return inputDeps;
|
||||
|
||||
var job = new GenerateBoxContactsJob
|
||||
{
|
||||
contactPairs = contactPairs,
|
||||
|
||||
positions = solver.positions,
|
||||
orientations = solver.orientations,
|
||||
velocities = solver.velocities,
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
|
||||
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
|
||||
contactsQueue = contactQueue.AsParallelWriter(),
|
||||
|
||||
worldToSolver = solver.worldToSolver,
|
||||
deltaTime = deltaTime,
|
||||
parameters = solver.abstraction.parameters,
|
||||
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.Box]
|
||||
};
|
||||
|
||||
inputDeps = job.Schedule(pairCount, 8, inputDeps);
|
||||
return inputDeps;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct GenerateBoxContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
|
||||
|
||||
// particle arrays:
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
// collider arrays:
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
|
||||
[WriteOnly]
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
|
||||
|
||||
// auxiliar data:
|
||||
[ReadOnly] public int firstPair;
|
||||
[ReadOnly] public BurstAffineTransform worldToSolver;
|
||||
[ReadOnly] public float deltaTime;
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int simplexIndex = contactPairs[firstPair + i].bodyA;
|
||||
int colliderIndex = contactPairs[firstPair + i].bodyB;
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
|
||||
|
||||
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
|
||||
|
||||
BurstBox shape = new BurstBox { colliderToSolver = colliderToSolver, shape = shapes[colliderIndex] };
|
||||
|
||||
var co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref shape, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out _, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
|
||||
contactsQueue.Enqueue(new BurstContact
|
||||
{
|
||||
bodyA = simplexIndex,
|
||||
bodyB = colliderIndex,
|
||||
pointA = simplexBary,
|
||||
pointB = colliderPoint.point,
|
||||
normal = colliderPoint.normal * shape.shape.sign
|
||||
});
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstBox>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,22 +1,21 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstCapsule : BurstLocalOptimization.IDistanceFunction
|
||||
public struct BurstCapsule : BurstLocalOptimization.IDistanceFunction, IBurstCollider
|
||||
{
|
||||
public BurstColliderShape shape;
|
||||
public BurstAffineTransform colliderToSolver;
|
||||
public float dt;
|
||||
|
||||
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
|
||||
{
|
||||
float4 center = shape.center * colliderToSolver.scale;
|
||||
point = colliderToSolver.InverseTransformPointUnscaled(point) - center;
|
||||
|
||||
if (shape.is2D)
|
||||
if (shape.is2D != 0)
|
||||
point[2] = 0;
|
||||
|
||||
int direction = (int)shape.size.z;
|
||||
@@ -37,100 +36,38 @@ namespace Obi
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
}
|
||||
|
||||
public static JobHandle GenerateContacts(ObiColliderWorld world,
|
||||
BurstSolverImpl solver,
|
||||
NativeList<Oni.ContactPair> contactPairs,
|
||||
NativeQueue<BurstContact> contactQueue,
|
||||
NativeArray<int> contactOffsetsPerType,
|
||||
float deltaTime,
|
||||
JobHandle inputDeps)
|
||||
public void Contacts(int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> velocities,
|
||||
NativeArray<float4> radii,
|
||||
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.Capsule + 1] - contactOffsetsPerType[(int)Oni.ShapeType.Capsule];
|
||||
if (pairCount == 0) return inputDeps;
|
||||
|
||||
var job = new GenerateCapsuleContactsJob
|
||||
{
|
||||
contactPairs = contactPairs,
|
||||
|
||||
positions = solver.positions,
|
||||
orientations = solver.orientations,
|
||||
velocities = solver.velocities,
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
|
||||
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
|
||||
contactsQueue = contactQueue.AsParallelWriter(),
|
||||
|
||||
worldToSolver = solver.worldToSolver,
|
||||
deltaTime = deltaTime,
|
||||
parameters = solver.abstraction.parameters,
|
||||
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.Capsule]
|
||||
};
|
||||
|
||||
inputDeps = job.Schedule(pairCount, 8, inputDeps);
|
||||
return inputDeps;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct GenerateCapsuleContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
|
||||
|
||||
// particle arrays:
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
// collider arrays:
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
|
||||
[WriteOnly]
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
|
||||
|
||||
// auxiliar data:
|
||||
[ReadOnly] public int firstPair;
|
||||
[ReadOnly] public BurstAffineTransform worldToSolver;
|
||||
[ReadOnly] public float deltaTime;
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int simplexIndex = contactPairs[firstPair + i].bodyA;
|
||||
int colliderIndex = contactPairs[firstPair + i].bodyB;
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
|
||||
|
||||
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
|
||||
|
||||
BurstCapsule shape = new BurstCapsule { colliderToSolver = colliderToSolver, shape = shapes[colliderIndex] };
|
||||
|
||||
var co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref shape, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out _, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
|
||||
contactsQueue.Enqueue(new BurstContact
|
||||
{
|
||||
bodyA = simplexIndex,
|
||||
bodyB = colliderIndex,
|
||||
pointA = simplexBary,
|
||||
pointB = colliderPoint.point,
|
||||
normal = colliderPoint.normal * shape.shape.sign
|
||||
});
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstCapsule>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -14,28 +14,12 @@ namespace Obi
|
||||
|
||||
public ColliderShape.ShapeType type;
|
||||
public float contactOffset;
|
||||
public int dataIndex; // index of the associated collider data in the collision world.
|
||||
public int dataIndex;
|
||||
public int rigidbodyIndex; // index of the associated rigidbody in the collision world.
|
||||
public int materialIndex; // index of the associated material in the collision world.
|
||||
public int forceZoneIndex; // index of the associated force zone in the collision world.
|
||||
public int filter;
|
||||
public int flags; // first bit whether the collider is 2D (1) or 3D (0), second bit whether it's a trigger (1) or regular collider (0),
|
||||
// third bit determines whether shape is inverted or not.
|
||||
|
||||
public bool is2D
|
||||
{
|
||||
get => (flags & 1) != 0;
|
||||
set => flags |= value ? 1 : 0;
|
||||
}
|
||||
public bool isTrigger
|
||||
{
|
||||
get => (flags & 1 << 1) != 0 || forceZoneIndex >= 0;
|
||||
set => flags |= value ? 1 << 1 : 0;
|
||||
}
|
||||
public float sign
|
||||
{
|
||||
get => (flags & 1 << 2) != 0 ? -1 : 1;
|
||||
}
|
||||
public int flags; // for now, only used for trigger (1) or regular collider (0).
|
||||
public int is2D; // whether the collider is 2D (1) or 3D (0).
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4,8 +4,6 @@ using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Threading;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
@@ -20,32 +18,24 @@ namespace Obi
|
||||
public int entity;
|
||||
}
|
||||
|
||||
public int referenceCount { get; private set; } = 0;
|
||||
public int colliderCount { get; private set; } = 0;
|
||||
private int refCount = 0;
|
||||
private int colliderCount = 0;
|
||||
|
||||
private NativeMultilevelGrid<int> grid;
|
||||
private NativeQueue<MovingCollider> movingColliders;
|
||||
|
||||
private NativeArray<int> colliderTypeCounts;
|
||||
private NativeQueue<Oni.ContactPair> contactPairQueue;
|
||||
public NativeList<Oni.ContactPair> contactPairs;
|
||||
public NativeArray<int> contactOffsetsPerType;
|
||||
|
||||
public NativeQueue<BurstContact> colliderContactQueue;
|
||||
|
||||
public ObiNativeCellSpanList cellSpans;
|
||||
|
||||
public int referenceCount { get { return refCount; } }
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
|
||||
this.grid = new NativeMultilevelGrid<int>(1000, Allocator.Persistent);
|
||||
this.movingColliders = new NativeQueue<MovingCollider>(Allocator.Persistent);
|
||||
this.colliderContactQueue = new NativeQueue<BurstContact>(Allocator.Persistent);
|
||||
|
||||
this.contactPairQueue = new NativeQueue<Oni.ContactPair>(Allocator.Persistent);
|
||||
this.colliderTypeCounts = new NativeArray<int>(Oni.ColliderShapeTypeCount, Allocator.Persistent);
|
||||
this.contactOffsetsPerType = new NativeArray<int>(Oni.ColliderShapeTypeCount + 1, Allocator.Persistent);
|
||||
this.contactPairs = new NativeList<Oni.ContactPair>(Allocator.Persistent);
|
||||
|
||||
this.cellSpans = new ObiNativeCellSpanList();
|
||||
|
||||
ObiColliderWorld.GetInstance().RegisterImplementation(this);
|
||||
@@ -57,30 +47,23 @@ namespace Obi
|
||||
|
||||
grid.Dispose();
|
||||
movingColliders.Dispose();
|
||||
|
||||
colliderTypeCounts.Dispose();
|
||||
contactPairQueue.Dispose();
|
||||
contactPairs.Dispose();
|
||||
contactOffsetsPerType.Dispose();
|
||||
|
||||
colliderContactQueue.Dispose();
|
||||
|
||||
cellSpans.Dispose();
|
||||
}
|
||||
|
||||
public void IncreaseReferenceCount()
|
||||
{
|
||||
referenceCount++;
|
||||
refCount++;
|
||||
}
|
||||
public void DecreaseReferenceCount()
|
||||
{
|
||||
if (--referenceCount <= 0 && gameObject != null)
|
||||
if (--refCount <= 0 && gameObject != null)
|
||||
DestroyImmediate(gameObject);
|
||||
}
|
||||
|
||||
public void SetColliders(ObiNativeColliderShapeList shapes, ObiNativeAabbList bounds, ObiNativeAffineTransformList transforms)
|
||||
public void SetColliders(ObiNativeColliderShapeList shapes, ObiNativeAabbList bounds, ObiNativeAffineTransformList transforms, int count)
|
||||
{
|
||||
colliderCount = shapes.count;
|
||||
colliderCount = count;
|
||||
|
||||
// insert new empty cellspans at the end if needed:
|
||||
while (colliderCount > cellSpans.count)
|
||||
@@ -91,10 +74,6 @@ namespace Obi
|
||||
{
|
||||
}
|
||||
|
||||
public void SetForceZones(ObiNativeForceZoneList rigidbody)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetCollisionMaterials(ObiNativeCollisionMaterialList materials)
|
||||
{
|
||||
|
||||
@@ -183,7 +162,7 @@ namespace Obi
|
||||
new int4(GridHash.Quantize(velocityBounds.max.xyz, cellSize), level));
|
||||
|
||||
// if the collider is 2D, project it to the z = 0 cells.
|
||||
if (shapes[i].is2D)
|
||||
if (shapes[i].is2D != 0)
|
||||
{
|
||||
newSpan.min[2] = 0;
|
||||
newSpan.max[2] = 0;
|
||||
@@ -194,7 +173,7 @@ namespace Obi
|
||||
if (i >= colliderCount || cellIndices[i] != newSpan)
|
||||
{
|
||||
// Add the collider to the list of moving colliders:
|
||||
movingColliders.Enqueue(new MovingCollider
|
||||
movingColliders.Enqueue(new MovingCollider()
|
||||
{
|
||||
oldSpan = cellIndices[i],
|
||||
newSpan = newSpan,
|
||||
@@ -251,7 +230,6 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<int> filters;
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
@@ -265,27 +243,47 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
[ReadOnly] public NativeArray<BurstAabb> bounds;
|
||||
|
||||
// distance field data:
|
||||
[ReadOnly] public NativeArray<DistanceFieldHeader> distanceFieldHeaders;
|
||||
[ReadOnly] public NativeArray<BurstDFNode> distanceFieldNodes;
|
||||
|
||||
// triangle mesh data:
|
||||
[ReadOnly] public NativeArray<TriangleMeshHeader> triangleMeshHeaders;
|
||||
[ReadOnly] public NativeArray<BIHNode> bihNodes;
|
||||
[ReadOnly] public NativeArray<Triangle> triangles;
|
||||
[ReadOnly] public NativeArray<float3> vertices;
|
||||
|
||||
// edge mesh data:
|
||||
[ReadOnly] public NativeArray<EdgeMeshHeader> edgeMeshHeaders;
|
||||
[ReadOnly] public NativeArray<BIHNode> edgeBihNodes;
|
||||
[ReadOnly] public NativeArray<Edge> edges;
|
||||
[ReadOnly] public NativeArray<float2> edgeVertices;
|
||||
|
||||
// height field data:
|
||||
[ReadOnly] public NativeArray<HeightFieldHeader> heightFieldHeaders;
|
||||
[ReadOnly] public NativeArray<float> heightFieldSamples;
|
||||
|
||||
// output contacts queue:
|
||||
[WriteOnly]
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeQueue<Oni.ContactPair>.ParallelWriter contactPairQueue;
|
||||
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeArray<int> colliderTypeCounts;
|
||||
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
|
||||
|
||||
// auxiliar data:
|
||||
[ReadOnly] public BurstAffineTransform solverToWorld;
|
||||
[ReadOnly] public BurstAffineTransform worldToSolver;
|
||||
[ReadOnly] public float deltaTime;
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(i, out int simplexSize);
|
||||
BurstAabb simplexBoundsSS = simplexBounds[i];
|
||||
|
||||
// get all colliders overlapped by the cell bounds, in all grid levels:
|
||||
BurstAabb simplexBoundsWS = simplexBounds[i].Transformed(solverToWorld);
|
||||
BurstAabb simplexBoundsWS = simplexBoundsSS.Transformed(solverToWorld);
|
||||
NativeList<int> candidates = new NativeList<int>(16,Allocator.Temp);
|
||||
|
||||
// max size of the simplex bounds in cells:
|
||||
// max size of the particle bounds in cells:
|
||||
int3 maxSize = new int3(10);
|
||||
bool is2D = parameters.mode == Oni.SolverParameters.Mode.Mode2D;
|
||||
|
||||
@@ -362,203 +360,139 @@ namespace Obi
|
||||
|
||||
if (shouldCollide && simplexBoundsWS.IntersectsAabb(in colliderBoundsWS, is2D))
|
||||
{
|
||||
// increment the amount of contacts for this shape type:
|
||||
Interlocked.Increment(ref ((int*)colliderTypeCounts.GetUnsafePtr())[(int)shape.type]);
|
||||
|
||||
// enqueue a new contact pair:
|
||||
contactPairQueue.Enqueue(new Oni.ContactPair{
|
||||
bodyA = i,
|
||||
bodyB = c
|
||||
});
|
||||
// generate contacts for the collider:
|
||||
BurstAffineTransform colliderToSolver = worldToSolver * transforms[c];
|
||||
GenerateContacts(in shape, in colliderToSolver, c, rb, i, simplexStart, simplexSize, simplexBoundsSS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct PrefixSumJob : IJob
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> array;
|
||||
public NativeArray<int> sum;
|
||||
|
||||
public void Execute()
|
||||
private void GenerateContacts(in BurstColliderShape shape,
|
||||
in BurstAffineTransform colliderToSolver,
|
||||
int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
in BurstAabb simplexBoundsSS)
|
||||
{
|
||||
sum[0] = 0;
|
||||
for (int i = 1; i < sum.Length; ++i)
|
||||
sum[i] = sum[i - 1] + array[i-1];
|
||||
}
|
||||
}
|
||||
float4x4 solverToCollider;
|
||||
BurstAabb simplexBoundsCS;
|
||||
|
||||
[BurstCompile]
|
||||
struct SortContactPairsByShape : IJob
|
||||
{
|
||||
public NativeQueue<Oni.ContactPair> contactPairQueue;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<int> start; // prefix sum
|
||||
public NativeArray<int> count;
|
||||
|
||||
public NativeList<Oni.ContactPair> contactPairs;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
contactPairs.ResizeUninitialized(contactPairQueue.Count);
|
||||
|
||||
while (!contactPairQueue.IsEmpty())
|
||||
switch (shape.type)
|
||||
{
|
||||
var pair = contactPairQueue.Dequeue();
|
||||
int shapeType = (int)shapes[pair.bodyB].type;
|
||||
case ColliderShape.ShapeType.Sphere:
|
||||
BurstSphere sphereShape = new BurstSphere() { colliderToSolver = colliderToSolver, shape = shape, dt = deltaTime };
|
||||
sphereShape.Contacts(colliderIndex, rigidbodyIndex, rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBoundsSS,
|
||||
simplexIndex, simplexStart, simplexSize, contactsQueue, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
break;
|
||||
case ColliderShape.ShapeType.Box:
|
||||
BurstBox boxShape = new BurstBox() { colliderToSolver = colliderToSolver, shape = shape, dt = deltaTime };
|
||||
boxShape.Contacts(colliderIndex, rigidbodyIndex, rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBoundsSS,
|
||||
simplexIndex, simplexStart, simplexSize, contactsQueue, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
break;
|
||||
case ColliderShape.ShapeType.Capsule:
|
||||
BurstCapsule capsuleShape = new BurstCapsule(){colliderToSolver = colliderToSolver,shape = shape, dt = deltaTime };
|
||||
capsuleShape.Contacts(colliderIndex, rigidbodyIndex, rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBoundsSS,
|
||||
simplexIndex, simplexStart, simplexSize, contactsQueue, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
break;
|
||||
case ColliderShape.ShapeType.SignedDistanceField:
|
||||
|
||||
// write the pair directly at its position in the sorted array:
|
||||
contactPairs[start[shapeType] + (--count[shapeType])] = pair;
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
BurstDistanceField distanceFieldShape = new BurstDistanceField()
|
||||
{
|
||||
colliderToSolver = colliderToSolver,
|
||||
solverToWorld = solverToWorld,
|
||||
shape = shape,
|
||||
distanceFieldHeaders = distanceFieldHeaders,
|
||||
dfNodes = distanceFieldNodes,
|
||||
dt = deltaTime,
|
||||
collisionMargin = parameters.collisionMargin
|
||||
};
|
||||
|
||||
distanceFieldShape.Contacts(colliderIndex, rigidbodyIndex, rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBoundsSS,
|
||||
simplexIndex, simplexStart, simplexSize, contactsQueue, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
|
||||
break;
|
||||
case ColliderShape.ShapeType.Heightmap:
|
||||
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
// invert a full matrix here to accurately represent collider bounds scale.
|
||||
solverToCollider = math.inverse(float4x4.TRS(colliderToSolver.translation.xyz, colliderToSolver.rotation, colliderToSolver.scale.xyz));
|
||||
simplexBoundsCS = simplexBoundsSS.Transformed(solverToCollider);
|
||||
|
||||
BurstHeightField heightmapShape = new BurstHeightField()
|
||||
{
|
||||
colliderToSolver = colliderToSolver,
|
||||
solverToWorld = solverToWorld,
|
||||
shape = shape,
|
||||
header = heightFieldHeaders[shape.dataIndex],
|
||||
heightFieldSamples = heightFieldSamples,
|
||||
collisionMargin = parameters.collisionMargin,
|
||||
dt = deltaTime
|
||||
};
|
||||
|
||||
heightmapShape.Contacts(colliderIndex, rigidbodyIndex, rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBoundsCS,
|
||||
simplexIndex, simplexStart, simplexSize, contactsQueue, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
|
||||
break;
|
||||
case ColliderShape.ShapeType.TriangleMesh:
|
||||
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
// invert a full matrix here to accurately represent collider bounds scale.
|
||||
solverToCollider = math.inverse(float4x4.TRS(colliderToSolver.translation.xyz, colliderToSolver.rotation, colliderToSolver.scale.xyz));
|
||||
simplexBoundsCS = simplexBoundsSS.Transformed(solverToCollider);
|
||||
|
||||
BurstTriangleMesh triangleMeshShape = new BurstTriangleMesh()
|
||||
{
|
||||
colliderToSolver = colliderToSolver,
|
||||
solverToWorld = solverToWorld,
|
||||
shape = shape,
|
||||
header = triangleMeshHeaders[shape.dataIndex],
|
||||
bihNodes = bihNodes,
|
||||
triangles = triangles,
|
||||
vertices = vertices,
|
||||
collisionMargin = parameters.collisionMargin,
|
||||
dt = deltaTime
|
||||
};
|
||||
|
||||
triangleMeshShape.Contacts(colliderIndex, rigidbodyIndex, rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBoundsCS,
|
||||
simplexIndex, simplexStart, simplexSize, contactsQueue, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
|
||||
break;
|
||||
case ColliderShape.ShapeType.EdgeMesh:
|
||||
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
// invert a full matrix here to accurately represent collider bounds scale.
|
||||
solverToCollider = math.inverse(float4x4.TRS(colliderToSolver.translation.xyz, colliderToSolver.rotation, colliderToSolver.scale.xyz));
|
||||
simplexBoundsCS = simplexBoundsSS.Transformed(solverToCollider);
|
||||
|
||||
BurstEdgeMesh edgeMeshShape = new BurstEdgeMesh()
|
||||
{
|
||||
colliderToSolver = colliderToSolver,
|
||||
shape = shape,
|
||||
header = edgeMeshHeaders[shape.dataIndex],
|
||||
edgeBihNodes = edgeBihNodes,
|
||||
edges = edges,
|
||||
vertices = edgeVertices,
|
||||
dt = deltaTime
|
||||
};
|
||||
|
||||
edgeMeshShape.Contacts(colliderIndex, rigidbodyIndex, rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBoundsCS,
|
||||
simplexIndex, simplexStart, simplexSize, contactsQueue, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct ApplyForceZonesJob : IJobParallelFor
|
||||
{
|
||||
// particle arrays:
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> externalForces;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> wind;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> velocities;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> colors;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float> life;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
// collider arrays:
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<ForceZone> forceZones;
|
||||
|
||||
// contacts
|
||||
[ReadOnly] public NativeArray<BurstContact> contacts;
|
||||
|
||||
// auxiliar data:
|
||||
[ReadOnly] public BurstAffineTransform worldToSolver;
|
||||
[ReadOnly] public float deltaTime;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
var contact = contacts[i];
|
||||
int forceZoneIndex = shapes[contact.bodyB].forceZoneIndex;
|
||||
|
||||
if (forceZoneIndex >= 0)
|
||||
{
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
|
||||
float distance = -math.dot(positions[particleIndex] - contact.pointB, contact.normal);
|
||||
if (distance < 0) continue;
|
||||
|
||||
float4 axis = (worldToSolver * transforms[contact.bodyB]).TransformDirection(new float4(0, 0, 1, 0));
|
||||
|
||||
// calculate falloff region based on min/max distances:
|
||||
float falloff = 1;
|
||||
float range = forceZones[forceZoneIndex].maxDistance - forceZones[forceZoneIndex].minDistance;
|
||||
if (math.abs(range) > BurstMath.epsilon)
|
||||
falloff = math.pow(math.saturate((distance - forceZones[forceZoneIndex].minDistance) / range), forceZones[forceZoneIndex].falloffPower);
|
||||
|
||||
float forceIntensity = forceZones[forceZoneIndex].intensity * falloff;
|
||||
float dampIntensity = forceZones[forceZoneIndex].damping * falloff;
|
||||
|
||||
// tint particles:
|
||||
float mix = math.pow(1 - math.saturate(forceZones[forceZoneIndex].color.a * falloff), deltaTime);
|
||||
colors[particleIndex] = math.lerp((Vector4)forceZones[forceZoneIndex].color, colors[particleIndex], mix);
|
||||
|
||||
// calculate force direction, depending on the type of the force field:
|
||||
float4 result = float4.zero;
|
||||
switch (forceZones[forceZoneIndex].type)
|
||||
{
|
||||
case ForceZone.ZoneType.Radial:
|
||||
result = contact.normal * forceIntensity;
|
||||
break;
|
||||
case ForceZone.ZoneType.Vortex:
|
||||
result = new float4(math.cross(axis.xyz * forceIntensity, contact.normal.xyz).xyz, 0);
|
||||
break;
|
||||
case ForceZone.ZoneType.Directional:
|
||||
result = axis * forceIntensity;
|
||||
break;
|
||||
default:
|
||||
BurstMath.AtomicAdd(life, particleIndex, -forceIntensity * deltaTime);
|
||||
continue;
|
||||
}
|
||||
|
||||
// apply damping:
|
||||
switch (forceZones[forceZoneIndex].dampingDir)
|
||||
{
|
||||
case ForceZone.DampingDirection.ForceDirection:
|
||||
{
|
||||
float4 forceDir = math.normalizesafe(result);
|
||||
result -= forceDir * math.dot(velocities[particleIndex], forceDir) * dampIntensity;
|
||||
}
|
||||
break;
|
||||
case ForceZone.DampingDirection.SurfaceDirection:
|
||||
result -= contact.normal * math.dot(velocities[particleIndex], contact.normal) * dampIntensity;
|
||||
break;
|
||||
default:
|
||||
result -= velocities[particleIndex] * dampIntensity;
|
||||
break;
|
||||
}
|
||||
|
||||
if (invMasses[particleIndex] > 0)
|
||||
{
|
||||
switch (forceZones[forceZoneIndex].mode)
|
||||
{
|
||||
case ForceZone.ForceMode.Acceleration:
|
||||
BurstMath.AtomicAdd(externalForces, particleIndex, result / simplexSize / invMasses[particleIndex]);
|
||||
break;
|
||||
case ForceZone.ForceMode.Force:
|
||||
BurstMath.AtomicAdd(externalForces, particleIndex, result / simplexSize);
|
||||
break;
|
||||
case ForceZone.ForceMode.Wind:
|
||||
BurstMath.AtomicAdd(wind, particleIndex, result / simplexSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public JobHandle ApplyForceZones(BurstSolverImpl solver, float deltaTime, JobHandle inputDeps)
|
||||
{
|
||||
var world = ObiColliderWorld.GetInstance();
|
||||
|
||||
var applyForceFieldsJob = new ApplyForceZonesJob
|
||||
{
|
||||
contacts = solver.abstraction.colliderContacts.AsNativeArray<BurstContact>(),
|
||||
|
||||
positions = solver.positions,
|
||||
velocities = solver.velocities,
|
||||
externalForces = solver.externalForces,
|
||||
wind = solver.wind,
|
||||
invMasses = solver.invMasses,
|
||||
life = solver.life,
|
||||
colors = solver.colors,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
|
||||
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
forceZones = world.forceZones.AsNativeArray<ForceZone>(),
|
||||
|
||||
worldToSolver = solver.worldToSolver,
|
||||
deltaTime = deltaTime,
|
||||
};
|
||||
|
||||
return applyForceFieldsJob.Schedule(solver.abstraction.colliderContacts.count, 64, inputDeps);
|
||||
}
|
||||
|
||||
public JobHandle GenerateContacts(BurstSolverImpl solver, float deltaTime, JobHandle inputDeps)
|
||||
{
|
||||
@@ -575,7 +509,6 @@ namespace Obi
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
filters = solver.filters,
|
||||
particleMaterialIndices = solver.collisionMaterials,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
@@ -587,46 +520,33 @@ namespace Obi
|
||||
collisionMaterials = world.collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
bounds = world.colliderAabbs.AsNativeArray<BurstAabb>(),
|
||||
|
||||
contactPairQueue = contactPairQueue.AsParallelWriter(),
|
||||
colliderTypeCounts = colliderTypeCounts,
|
||||
distanceFieldHeaders = world.distanceFieldContainer.headers.AsNativeArray<DistanceFieldHeader>(),
|
||||
distanceFieldNodes = world.distanceFieldContainer.dfNodes.AsNativeArray<BurstDFNode>(),
|
||||
|
||||
triangleMeshHeaders = world.triangleMeshContainer.headers.AsNativeArray<TriangleMeshHeader>(),
|
||||
bihNodes = world.triangleMeshContainer.bihNodes.AsNativeArray<BIHNode>(),
|
||||
triangles = world.triangleMeshContainer.triangles.AsNativeArray<Triangle>(),
|
||||
vertices = world.triangleMeshContainer.vertices.AsNativeArray<float3>(),
|
||||
|
||||
edgeMeshHeaders = world.edgeMeshContainer.headers.AsNativeArray<EdgeMeshHeader>(),
|
||||
edgeBihNodes = world.edgeMeshContainer.bihNodes.AsNativeArray<BIHNode>(),
|
||||
edges = world.edgeMeshContainer.edges.AsNativeArray<Edge>(),
|
||||
edgeVertices = world.edgeMeshContainer.vertices.AsNativeArray<float2>(),
|
||||
|
||||
heightFieldHeaders = world.heightFieldContainer.headers.AsNativeArray<HeightFieldHeader>(),
|
||||
heightFieldSamples = world.heightFieldContainer.samples.AsNativeArray<float>(),
|
||||
|
||||
contactsQueue = colliderContactQueue.AsParallelWriter(),
|
||||
solverToWorld = solver.solverToWorld,
|
||||
worldToSolver = solver.worldToSolver,
|
||||
deltaTime = deltaTime,
|
||||
parameters = solver.abstraction.parameters
|
||||
};
|
||||
|
||||
inputDeps = generateColliderContactsJob.Schedule(solver.simplexCounts.simplexCount, 16, inputDeps);
|
||||
return generateColliderContactsJob.Schedule(solver.simplexCounts.simplexCount, 16, inputDeps);
|
||||
|
||||
var prefixSumJob = new PrefixSumJob
|
||||
{
|
||||
array = colliderTypeCounts,
|
||||
sum = contactOffsetsPerType
|
||||
};
|
||||
inputDeps = prefixSumJob.Schedule(inputDeps);
|
||||
|
||||
var sortPairsJob = new SortContactPairsByShape
|
||||
{
|
||||
contactPairQueue = contactPairQueue,
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
start = contactOffsetsPerType,
|
||||
count = colliderTypeCounts,
|
||||
contactPairs = contactPairs
|
||||
};
|
||||
inputDeps = sortPairsJob.Schedule(inputDeps);
|
||||
|
||||
inputDeps.Complete();
|
||||
|
||||
inputDeps = BurstSphere.GenerateContacts(world,solver,contactPairs,colliderContactQueue,contactOffsetsPerType,deltaTime,inputDeps);
|
||||
inputDeps = BurstBox.GenerateContacts(world,solver,contactPairs,colliderContactQueue, contactOffsetsPerType,deltaTime,inputDeps);
|
||||
inputDeps = BurstCapsule.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
|
||||
inputDeps = BurstDistanceField.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
|
||||
inputDeps = BurstTriangleMesh.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
|
||||
inputDeps = BurstHeightField.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
|
||||
inputDeps = BurstEdgeMesh.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
|
||||
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,15 +1,17 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstDistanceField : BurstLocalOptimization.IDistanceFunction
|
||||
public struct BurstDistanceField : BurstLocalOptimization.IDistanceFunction, IBurstCollider
|
||||
{
|
||||
public BurstColliderShape shape;
|
||||
public BurstAffineTransform colliderToSolver;
|
||||
public BurstAffineTransform solverToWorld;
|
||||
|
||||
public float dt;
|
||||
public float collisionMargin;
|
||||
|
||||
public NativeArray<DistanceFieldHeader> distanceFieldHeaders;
|
||||
public NativeArray<BurstDFNode> dfNodes;
|
||||
@@ -18,143 +20,47 @@ namespace Obi
|
||||
{
|
||||
point = colliderToSolver.InverseTransformPoint(point);
|
||||
|
||||
if (shape.is2D)
|
||||
if (shape.is2D != 0)
|
||||
point[2] = 0;
|
||||
|
||||
var header = distanceFieldHeaders[shape.dataIndex];
|
||||
float4 sample = DFTraverse(point, in header);
|
||||
float4 sample = DFTraverse(point, 0, in header, in dfNodes);
|
||||
float4 normal = new float4(math.normalize(sample.xyz), 0);
|
||||
|
||||
projectedPoint.point = colliderToSolver.TransformPoint(point - normal * (sample[3] - shape.contactOffset));
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
}
|
||||
|
||||
private float4 DFTraverse(float4 particlePosition, in DistanceFieldHeader header)
|
||||
public void Contacts(int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> velocities,
|
||||
NativeArray<float4> radii,
|
||||
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
var stack = new NativeArray<int>(12, Allocator.Temp);
|
||||
int stackTop = 0;
|
||||
|
||||
stack[stackTop++] = 0;
|
||||
|
||||
while (stackTop > 0)
|
||||
{
|
||||
int nodeIndex = stack[--stackTop];
|
||||
var node = dfNodes[header.firstNode + nodeIndex];
|
||||
|
||||
// if the child node exists, recurse down the df octree:
|
||||
if (node.firstChild >= 0)
|
||||
stack[stackTop++] = node.firstChild + node.GetOctant(particlePosition);
|
||||
else
|
||||
return node.SampleWithGradient(particlePosition);
|
||||
}
|
||||
return float4.zero;
|
||||
}
|
||||
|
||||
public static JobHandle GenerateContacts(ObiColliderWorld world,
|
||||
BurstSolverImpl solver,
|
||||
NativeList<Oni.ContactPair> contactPairs,
|
||||
NativeQueue<BurstContact> contactQueue,
|
||||
NativeArray<int> contactOffsetsPerType,
|
||||
float deltaTime,
|
||||
JobHandle inputDeps)
|
||||
{
|
||||
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.SignedDistanceField + 1] - contactOffsetsPerType[(int)Oni.ShapeType.SignedDistanceField];
|
||||
if (pairCount == 0) return inputDeps;
|
||||
|
||||
var job = new GenerateDistanceFieldContactsJob
|
||||
{
|
||||
contactPairs = contactPairs,
|
||||
|
||||
positions = solver.positions,
|
||||
orientations = solver.orientations,
|
||||
velocities = solver.velocities,
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
|
||||
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
|
||||
distanceFieldHeaders = world.distanceFieldContainer.headers.AsNativeArray<DistanceFieldHeader>(),
|
||||
distanceFieldNodes = world.distanceFieldContainer.dfNodes.AsNativeArray<BurstDFNode>(),
|
||||
|
||||
contactsQueue = contactQueue.AsParallelWriter(),
|
||||
|
||||
solverToWorld = solver.inertialFrame,
|
||||
worldToSolver = solver.worldToSolver,
|
||||
deltaTime = deltaTime,
|
||||
parameters = solver.abstraction.parameters,
|
||||
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.SignedDistanceField]
|
||||
};
|
||||
|
||||
inputDeps = job.Schedule(pairCount, 1, inputDeps);
|
||||
return inputDeps;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct GenerateDistanceFieldContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
|
||||
|
||||
// particle arrays:
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
// collider arrays:
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
|
||||
// distance field data:
|
||||
[ReadOnly] public NativeArray<DistanceFieldHeader> distanceFieldHeaders;
|
||||
[ReadOnly] public NativeArray<BurstDFNode> distanceFieldNodes;
|
||||
|
||||
[WriteOnly]
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
|
||||
|
||||
// auxiliar data:
|
||||
[ReadOnly] public int firstPair;
|
||||
[ReadOnly] public BurstInertialFrame solverToWorld;
|
||||
[ReadOnly] public BurstAffineTransform worldToSolver;
|
||||
[ReadOnly] public float deltaTime;
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
|
||||
int simplexIndex = contactPairs[firstPair + i].bodyA;
|
||||
int colliderIndex = contactPairs[firstPair + i].bodyB;
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
|
||||
if (shapes[colliderIndex].dataIndex < 0) return;
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
|
||||
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
|
||||
|
||||
BurstDistanceField dfShape = new BurstDistanceField()
|
||||
{
|
||||
colliderToSolver = colliderToSolver,
|
||||
shape = shapes[colliderIndex],
|
||||
distanceFieldHeaders = distanceFieldHeaders,
|
||||
dfNodes = distanceFieldNodes
|
||||
};
|
||||
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
var co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref dfShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 simplexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstDistanceField>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 simplexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
|
||||
float4 velocity = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
@@ -170,18 +76,31 @@ namespace Obi
|
||||
rbVelocity = BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, colliderPoint.point, rigidbodies, solverToWorld);
|
||||
|
||||
float dAB = math.dot(simplexPoint - colliderPoint.point, colliderPoint.normal);
|
||||
float vel = math.dot(velocity - rbVelocity, colliderPoint.normal);
|
||||
float vel = math.dot(velocity - rbVelocity, colliderPoint.normal);
|
||||
|
||||
//if (vel * deltaTime + dAB <= simplexRadius + shapes[colliderIndex].contactOffset + parameters.collisionMargin)
|
||||
contactsQueue.Enqueue(new BurstContact
|
||||
{
|
||||
bodyA = simplexIndex,
|
||||
bodyB = colliderIndex,
|
||||
pointA = simplexBary,
|
||||
pointB = colliderPoint.point,
|
||||
normal = colliderPoint.normal * dfShape.shape.sign
|
||||
});
|
||||
if (vel * dt + dAB <= simplexRadius + shape.contactOffset + collisionMargin)
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
|
||||
private static float4 DFTraverse(float4 particlePosition,
|
||||
int nodeIndex,
|
||||
in DistanceFieldHeader header,
|
||||
in NativeArray<BurstDFNode> dfNodes)
|
||||
{
|
||||
var node = dfNodes[header.firstNode + nodeIndex];
|
||||
|
||||
// if the child node exists, recurse down the df octree:
|
||||
if (node.firstChild >= 0)
|
||||
{
|
||||
int octant = node.GetOctant(particlePosition);
|
||||
return DFTraverse(particlePosition, node.firstChild + octant, in header, in dfNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
return node.SampleWithGradient(particlePosition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstEdgeMesh : BurstLocalOptimization.IDistanceFunction
|
||||
public struct BurstEdgeMesh : BurstLocalOptimization.IDistanceFunction, IBurstCollider
|
||||
{
|
||||
|
||||
public BurstColliderShape shape;
|
||||
public BurstAffineTransform colliderToSolver;
|
||||
public int dataOffset;
|
||||
public float dt;
|
||||
|
||||
public EdgeMeshHeader header;
|
||||
public NativeArray<BIHNode> edgeBihNodes;
|
||||
@@ -22,12 +21,12 @@ namespace Obi
|
||||
{
|
||||
point = colliderToSolver.InverseTransformPointUnscaled(point);
|
||||
|
||||
if (shape.is2D)
|
||||
if (shape.is2D != 0)
|
||||
point[2] = 0;
|
||||
|
||||
Edge t = edges[header.firstEdge + dataOffset];
|
||||
float4 v1 = (new float4(vertices[header.firstVertex + t.i1], 0, 0) + shape.center) * colliderToSolver.scale;
|
||||
float4 v2 = (new float4(vertices[header.firstVertex + t.i2], 0, 0) + shape.center) * colliderToSolver.scale;
|
||||
float4 v1 = (new float4(vertices[header.firstVertex + t.i1], 0) + shape.center) * colliderToSolver.scale;
|
||||
float4 v2 = (new float4(vertices[header.firstVertex + t.i2], 0) + shape.center) * colliderToSolver.scale;
|
||||
|
||||
float4 nearestPoint = BurstMath.NearestPointOnEdge(v1, v2, point, out float mu);
|
||||
float4 normal = math.normalizesafe(point - nearestPoint);
|
||||
@@ -36,175 +35,90 @@ namespace Obi
|
||||
projectedPoint.point = colliderToSolver.TransformPointUnscaled(nearestPoint + normal * shape.contactOffset);
|
||||
}
|
||||
|
||||
public static JobHandle GenerateContacts(ObiColliderWorld world,
|
||||
BurstSolverImpl solver,
|
||||
NativeList<Oni.ContactPair> contactPairs,
|
||||
NativeQueue<BurstContact> contactQueue,
|
||||
NativeArray<int> contactOffsetsPerType,
|
||||
float deltaTime,
|
||||
JobHandle inputDeps)
|
||||
|
||||
public void Contacts(int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> velocities,
|
||||
NativeArray<float4> radii,
|
||||
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.EdgeMesh + 1] - contactOffsetsPerType[(int)Oni.ShapeType.EdgeMesh];
|
||||
if (pairCount == 0) return inputDeps;
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
var job = new GenerateEdgeMeshContactsJob
|
||||
{
|
||||
contactPairs = contactPairs,
|
||||
|
||||
positions = solver.positions,
|
||||
orientations = solver.orientations,
|
||||
velocities = solver.velocities,
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
simplexBounds = solver.simplexBounds,
|
||||
|
||||
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
|
||||
edgeMeshHeaders = world.edgeMeshContainer.headers.AsNativeArray<EdgeMeshHeader>(),
|
||||
edgeBihNodes = world.edgeMeshContainer.bihNodes.AsNativeArray<BIHNode>(),
|
||||
edges = world.edgeMeshContainer.edges.AsNativeArray<Edge>(),
|
||||
edgeVertices = world.edgeMeshContainer.vertices.AsNativeArray<float2>(),
|
||||
|
||||
contactsQueue = contactQueue.AsParallelWriter(),
|
||||
|
||||
solverToWorld = solver.solverToWorld,
|
||||
worldToSolver = solver.worldToSolver,
|
||||
deltaTime = deltaTime,
|
||||
parameters = solver.abstraction.parameters,
|
||||
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.TriangleMesh]
|
||||
};
|
||||
|
||||
inputDeps = job.Schedule(pairCount, 1, inputDeps);
|
||||
return inputDeps;
|
||||
BIHTraverse(colliderIndex, simplexIndex, simplexStart, simplexSize,
|
||||
positions, orientations, radii, simplices, in simplexBounds, 0, contacts, optimizationIterations, optimizationTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct GenerateEdgeMeshContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
|
||||
|
||||
// particle arrays:
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
[ReadOnly] public NativeArray<BurstAabb> simplexBounds;
|
||||
|
||||
// collider arrays:
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
|
||||
// edge mesh data:
|
||||
[ReadOnly] public NativeArray<EdgeMeshHeader> edgeMeshHeaders;
|
||||
[ReadOnly] public NativeArray<BIHNode> edgeBihNodes;
|
||||
[ReadOnly] public NativeArray<Edge> edges;
|
||||
[ReadOnly] public NativeArray<float2> edgeVertices;
|
||||
|
||||
[WriteOnly]
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
|
||||
|
||||
// auxiliar data:
|
||||
[ReadOnly] public int firstPair;
|
||||
[ReadOnly] public BurstAffineTransform solverToWorld;
|
||||
[ReadOnly] public BurstAffineTransform worldToSolver;
|
||||
[ReadOnly] public float deltaTime;
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
|
||||
public void Execute(int i)
|
||||
private void BIHTraverse(int colliderIndex,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> radii,
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int nodeIndex,
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
int simplexIndex = contactPairs[firstPair + i].bodyA;
|
||||
int colliderIndex = contactPairs[firstPair + i].bodyB;
|
||||
var shape = shapes[colliderIndex];
|
||||
var node = edgeBihNodes[header.firstNode + nodeIndex];
|
||||
|
||||
if (shape.dataIndex < 0)
|
||||
return;
|
||||
|
||||
var header = edgeMeshHeaders[shape.dataIndex];
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
|
||||
var simplexBound = simplexBounds[simplexIndex];
|
||||
|
||||
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
|
||||
|
||||
// invert a full matrix here to accurately represent collider bounds scale.
|
||||
var solverToCollider = math.inverse(float4x4.TRS(colliderToSolver.translation.xyz, colliderToSolver.rotation, colliderToSolver.scale.xyz));
|
||||
var simplexBoundsCS = simplexBound.Transformed(solverToCollider);
|
||||
|
||||
float4 marginCS = new float4((shape.contactOffset + parameters.collisionMargin) / colliderToSolver.scale.xyz, 0);
|
||||
|
||||
BurstEdgeMesh edgeMeshShape = new BurstEdgeMesh()
|
||||
if (node.firstChild >= 0)
|
||||
{
|
||||
colliderToSolver = colliderToSolver,
|
||||
shape = shape,
|
||||
header = header,
|
||||
edgeBihNodes = edgeBihNodes,
|
||||
edges = edges,
|
||||
vertices = edgeVertices
|
||||
};
|
||||
// visit min node:
|
||||
if (simplexBounds.min[node.axis] <= node.min + shape.center[node.axis])
|
||||
BIHTraverse(colliderIndex, simplexIndex, simplexStart, simplexSize,
|
||||
positions, orientations, radii, simplices, in simplexBounds,
|
||||
node.firstChild, contacts, optimizationIterations, optimizationTolerance);
|
||||
|
||||
NativeQueue<int> queue = new NativeQueue<int>(Allocator.Temp);
|
||||
|
||||
queue.Enqueue(0);
|
||||
|
||||
while (!queue.IsEmpty())
|
||||
// visit max node:
|
||||
if (simplexBounds.max[node.axis] >= node.max + shape.center[node.axis])
|
||||
BIHTraverse(colliderIndex, simplexIndex, simplexStart, simplexSize,
|
||||
positions, orientations, radii, simplices, in simplexBounds,
|
||||
node.firstChild + 1, contacts, optimizationIterations, optimizationTolerance);
|
||||
}
|
||||
else
|
||||
{
|
||||
int nodeIndex = queue.Dequeue();
|
||||
var node = edgeBihNodes[header.firstNode + nodeIndex];
|
||||
|
||||
// leaf node:
|
||||
if (node.firstChild < 0)
|
||||
// check for contact against all triangles:
|
||||
for (dataOffset = node.start; dataOffset < node.start + node.count; ++dataOffset)
|
||||
{
|
||||
// check for contact against all triangles:
|
||||
for (int dataOffset = node.start; dataOffset < node.start + node.count; ++dataOffset)
|
||||
Edge t = edges[header.firstEdge + dataOffset];
|
||||
float4 v1 = new float4(vertices[header.firstVertex + t.i1], 0) + shape.center;
|
||||
float4 v2 = new float4(vertices[header.firstVertex + t.i2], 0) + shape.center;
|
||||
BurstAabb edgeBounds = new BurstAabb(v1, v2, shape.contactOffset + 0.01f);
|
||||
|
||||
if (edgeBounds.IntersectsAabb(simplexBounds, shape.is2D != 0))
|
||||
{
|
||||
Edge t = edges[header.firstEdge + dataOffset];
|
||||
float4 v1 = new float4(edgeVertices[header.firstVertex + t.i1], 0, 0) + shape.center;
|
||||
float4 v2 = new float4(edgeVertices[header.firstVertex + t.i2], 0, 0) + shape.center;
|
||||
BurstAabb edgeBounds = new BurstAabb(v1, v2, marginCS);
|
||||
var co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
|
||||
if (edgeBounds.IntersectsAabb(simplexBoundsCS, shape.is2D))
|
||||
{
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstEdgeMesh>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
edgeMeshShape.dataOffset = dataOffset;
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref edgeMeshShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
|
||||
contactsQueue.Enqueue(new BurstContact(){
|
||||
bodyA = simplexIndex,
|
||||
bodyB = colliderIndex,
|
||||
pointA = simplexBary,
|
||||
pointB = colliderPoint.point,
|
||||
normal = colliderPoint.normal * edgeMeshShape.shape.sign
|
||||
});
|
||||
}
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
}
|
||||
else // check min and/or max children:
|
||||
{
|
||||
// visit min node:
|
||||
if (simplexBoundsCS.min[node.axis] <= node.min)
|
||||
queue.Enqueue(node.firstChild);
|
||||
|
||||
// visit max node:
|
||||
if (simplexBoundsCS.max[node.axis] >= node.max)
|
||||
queue.Enqueue(node.firstChild + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstHeightField : BurstLocalOptimization.IDistanceFunction
|
||||
public struct BurstHeightField : BurstLocalOptimization.IDistanceFunction, IBurstCollider
|
||||
{
|
||||
|
||||
public BurstColliderShape shape;
|
||||
public BurstAffineTransform colliderToSolver;
|
||||
public BurstAffineTransform solverToWorld;
|
||||
public float dt;
|
||||
public float collisionMargin;
|
||||
|
||||
public BurstMath.CachedTri tri;
|
||||
public float4 triNormal;
|
||||
@@ -21,7 +23,7 @@ namespace Obi
|
||||
{
|
||||
point = colliderToSolver.InverseTransformPoint(point);
|
||||
|
||||
float4 nearestPoint = BurstMath.NearestPointOnTri(tri, point, out _);
|
||||
float4 nearestPoint = BurstMath.NearestPointOnTri(tri, point, out float4 bary);
|
||||
float4 normal = math.normalizesafe(point - nearestPoint);
|
||||
|
||||
// flip the contact normal if it points below ground: (doesn't work with holes)
|
||||
@@ -31,123 +33,30 @@ namespace Obi
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
}
|
||||
|
||||
public static JobHandle GenerateContacts(ObiColliderWorld world,
|
||||
BurstSolverImpl solver,
|
||||
NativeList<Oni.ContactPair> contactPairs,
|
||||
NativeQueue<BurstContact> contactQueue,
|
||||
NativeArray<int> contactOffsetsPerType,
|
||||
float deltaTime,
|
||||
JobHandle inputDeps)
|
||||
public void Contacts(int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> velocities,
|
||||
NativeArray<float4> radii,
|
||||
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.Heightmap + 1] - contactOffsetsPerType[(int)Oni.ShapeType.Heightmap];
|
||||
if (pairCount == 0) return inputDeps;
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
var job = new GenerateHeightFieldContactsJob
|
||||
{
|
||||
contactPairs = contactPairs,
|
||||
triNormal = float4.zero;
|
||||
|
||||
positions = solver.positions,
|
||||
orientations = solver.orientations,
|
||||
velocities = solver.velocities,
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
simplexBounds = solver.simplexBounds,
|
||||
|
||||
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
|
||||
heightFieldHeaders = world.heightFieldContainer.headers.AsNativeArray<HeightFieldHeader>(),
|
||||
heightFieldSamples = world.heightFieldContainer.samples.AsNativeArray<float>(),
|
||||
|
||||
contactsQueue = contactQueue.AsParallelWriter(),
|
||||
|
||||
solverToWorld = solver.inertialFrame,
|
||||
worldToSolver = solver.worldToSolver,
|
||||
deltaTime = deltaTime,
|
||||
parameters = solver.abstraction.parameters,
|
||||
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.Heightmap]
|
||||
};
|
||||
|
||||
inputDeps = job.Schedule(pairCount, 1, inputDeps);
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct GenerateHeightFieldContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
|
||||
|
||||
// particle arrays:
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
[ReadOnly] public NativeArray<BurstAabb> simplexBounds;
|
||||
|
||||
// collider arrays:
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
|
||||
// height field data:
|
||||
[ReadOnly] public NativeArray<HeightFieldHeader> heightFieldHeaders;
|
||||
[ReadOnly] public NativeArray<float> heightFieldSamples;
|
||||
|
||||
[WriteOnly]
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
|
||||
|
||||
// auxiliar data:
|
||||
[ReadOnly] public int firstPair;
|
||||
[ReadOnly] public BurstInertialFrame solverToWorld;
|
||||
[ReadOnly] public BurstAffineTransform worldToSolver;
|
||||
[ReadOnly] public float deltaTime;
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
|
||||
int simplexIndex = contactPairs[firstPair + i].bodyA;
|
||||
int colliderIndex = contactPairs[firstPair + i].bodyB;
|
||||
var shape = shapes[colliderIndex];
|
||||
|
||||
if (shape.dataIndex < 0)
|
||||
return;
|
||||
|
||||
var header = heightFieldHeaders[shape.dataIndex];
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
|
||||
var simplexBound = simplexBounds[simplexIndex];
|
||||
|
||||
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
|
||||
|
||||
// invert a full matrix here to accurately represent collider bounds scale.
|
||||
var solverToCollider = math.inverse(float4x4.TRS(colliderToSolver.translation.xyz, colliderToSolver.rotation, colliderToSolver.scale.xyz));
|
||||
var simplexBoundsCS = simplexBound.Transformed(solverToCollider);
|
||||
|
||||
BurstHeightField triangleMeshShape = new BurstHeightField()
|
||||
{
|
||||
colliderToSolver = colliderToSolver,
|
||||
shape = shapes[colliderIndex],
|
||||
header = heightFieldHeaders[shapes[colliderIndex].dataIndex],
|
||||
heightFieldSamples = heightFieldSamples
|
||||
};
|
||||
|
||||
float4 triNormal = float4.zero;
|
||||
|
||||
var co = new BurstContact { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
var co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
|
||||
int resolutionU = (int)shape.center.x;
|
||||
int resolutionV = (int)shape.center.y;
|
||||
@@ -157,8 +66,8 @@ namespace Obi
|
||||
float cellHeight = shape.size.z / (resolutionV - 1);
|
||||
|
||||
// calculate particle bounds min/max cells:
|
||||
int2 min = new int2((int)math.floor(simplexBoundsCS.min[0] / cellWidth), (int)math.floor(simplexBoundsCS.min[2] / cellHeight));
|
||||
int2 max = new int2((int)math.floor(simplexBoundsCS.max[0] / cellWidth), (int)math.floor(simplexBoundsCS.max[2] / cellHeight));
|
||||
int2 min = new int2((int)math.floor(simplexBounds.min[0] / cellWidth), (int)math.floor(simplexBounds.min[2] / cellHeight));
|
||||
int2 max = new int2((int)math.floor(simplexBounds.max[0] / cellWidth), (int)math.floor(simplexBounds.max[2] / cellHeight));
|
||||
|
||||
for (int su = min[0]; su <= max[0]; ++su)
|
||||
{
|
||||
@@ -197,11 +106,11 @@ namespace Obi
|
||||
float4 v2 = new float4(max_x, h4, max_z, 0);
|
||||
float4 v3 = new float4(min_x, h1, min_z, 0);
|
||||
|
||||
triangleMeshShape.tri.Cache(v1, v2, v3);
|
||||
tri.Cache(v1, v2, v3);
|
||||
triNormal.xyz = math.normalizesafe(math.cross((v2 - v1).xyz, (v3 - v1).xyz));
|
||||
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref triangleMeshShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out convexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstHeightField>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
float4 velocity = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
@@ -219,12 +128,12 @@ namespace Obi
|
||||
float dAB = math.dot(convexPoint - colliderPoint.point, colliderPoint.normal);
|
||||
float vel = math.dot(velocity - rbVelocity, colliderPoint.normal);
|
||||
|
||||
if (vel * deltaTime + dAB <= simplexRadius + shape.contactOffset + parameters.collisionMargin)
|
||||
if (vel * dt + dAB <= simplexRadius + shape.contactOffset + collisionMargin)
|
||||
{
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal * triangleMeshShape.shape.sign;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
contactsQueue.Enqueue(co);
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
|
||||
// ------contact against the second triangle------:
|
||||
@@ -232,11 +141,11 @@ namespace Obi
|
||||
v2 = new float4(max_x, h4, max_z, 0);
|
||||
v3 = new float4(max_x, h2, min_z, 0);
|
||||
|
||||
triangleMeshShape.tri.Cache(v1, v2, v3);
|
||||
tri.Cache(v1, v2, v3);
|
||||
triNormal.xyz = math.normalizesafe(math.cross((v2 - v1).xyz, (v3 - v1).xyz));
|
||||
|
||||
colliderPoint = BurstLocalOptimization.Optimize(ref triangleMeshShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out convexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
colliderPoint = BurstLocalOptimization.Optimize<BurstHeightField>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
velocity = float4.zero;
|
||||
simplexRadius = 0;
|
||||
@@ -254,19 +163,21 @@ namespace Obi
|
||||
dAB = math.dot(convexPoint - colliderPoint.point, colliderPoint.normal);
|
||||
vel = math.dot(velocity - rbVelocity, colliderPoint.normal);
|
||||
|
||||
if (vel * deltaTime + dAB <= simplexRadius + shape.contactOffset + parameters.collisionMargin)
|
||||
if (vel * dt + dAB <= simplexRadius + shape.contactOffset + collisionMargin)
|
||||
{
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal * triangleMeshShape.shape.sign;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
|
||||
contactsQueue.Enqueue(co);
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ namespace Obi
|
||||
convexRadii += radii[particle] * convexBary[j];
|
||||
convexOrientation.value += orientations[particle].value * convexBary[j];
|
||||
}
|
||||
convexPoint.w = 0;
|
||||
}
|
||||
|
||||
public static SurfacePoint Optimize<T>(ref T function,
|
||||
@@ -113,7 +112,6 @@ namespace Obi
|
||||
{
|
||||
int particle = simplices[simplexStart + j];
|
||||
float4 candidate = positions[particle] - convexPoint;
|
||||
candidate.w = 0;
|
||||
|
||||
// here, we adjust the candidate by projecting it to the engrosed simplex's surface:
|
||||
candidate -= pointInFunction.normal * (radii[particle].x - convexThickness.x);
|
||||
@@ -178,8 +176,6 @@ namespace Obi
|
||||
|
||||
float4 candidateC = positions[simplices[simplexStart]] - pointInFunction.point;
|
||||
float4 candidateD = positions[simplices[simplexStart + 1]] - pointInFunctionD.point;
|
||||
candidateC.w = 0;
|
||||
candidateD.w = 0;
|
||||
|
||||
candidateC -= pointInFunction.normal * (radii[simplices[simplexStart]].x - convexThickness.x);
|
||||
candidateD -= pointInFunctionD.normal * (radii[simplices[simplexStart + 1]].x - convexThicknessD.x);
|
||||
|
||||
@@ -24,9 +24,9 @@ namespace Obi
|
||||
{
|
||||
if (simplexSize == 3)
|
||||
{
|
||||
tri.Cache(new float4(positions[simplices[simplexStart]].xyz,0),
|
||||
new float4(positions[simplices[simplexStart + 1]].xyz,0),
|
||||
new float4(positions[simplices[simplexStart + 2]].xyz,0));
|
||||
tri.Cache(positions[simplices[simplexStart]],
|
||||
positions[simplices[simplexStart + 1]],
|
||||
positions[simplices[simplexStart + 2]]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,17 +35,16 @@ namespace Obi
|
||||
switch (simplexSize)
|
||||
{
|
||||
case 1:
|
||||
default:
|
||||
{
|
||||
float4 p1 = positions[simplices[simplexStart]]; p1.w = 0;
|
||||
float4 p1 = positions[simplices[simplexStart]];
|
||||
projectedPoint.bary = new float4(1, 0, 0, 0);
|
||||
projectedPoint.point = p1;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
float4 p1 = positions[simplices[simplexStart]]; p1.w = 0;
|
||||
float4 p2 = positions[simplices[simplexStart + 1]]; p2.w = 0;
|
||||
float4 p1 = positions[simplices[simplexStart]];
|
||||
float4 p2 = positions[simplices[simplexStart + 1]];
|
||||
BurstMath.NearestPointOnEdge(p1, p2, point, out float mu);
|
||||
projectedPoint.bary = new float4(1 - mu, mu, 0, 0);
|
||||
projectedPoint.point = p1 * projectedPoint.bary[0] + p2 * projectedPoint.bary[1];
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstSphere : BurstLocalOptimization.IDistanceFunction
|
||||
public struct BurstSphere : BurstLocalOptimization.IDistanceFunction, IBurstCollider
|
||||
{
|
||||
public BurstColliderShape shape;
|
||||
public BurstAffineTransform colliderToSolver;
|
||||
public float dt;
|
||||
|
||||
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
|
||||
{
|
||||
float4 center = shape.center * colliderToSolver.scale;
|
||||
point = colliderToSolver.InverseTransformPointUnscaled(point) - center;
|
||||
|
||||
if (shape.is2D)
|
||||
if (shape.is2D != 0)
|
||||
point[2] = 0;
|
||||
|
||||
float radius = shape.size.x * math.cmax(colliderToSolver.scale.xyz);
|
||||
@@ -28,98 +27,36 @@ namespace Obi
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
}
|
||||
|
||||
public static JobHandle GenerateContacts(ObiColliderWorld world,
|
||||
BurstSolverImpl solver,
|
||||
NativeList<Oni.ContactPair> contactPairs,
|
||||
NativeQueue<BurstContact> contactQueue,
|
||||
NativeArray<int> contactOffsetsPerType,
|
||||
float deltaTime,
|
||||
JobHandle inputDeps)
|
||||
public void Contacts(int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> velocities,
|
||||
NativeArray<float4> radii,
|
||||
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.Sphere + 1] - contactOffsetsPerType[(int)Oni.ShapeType.Sphere];
|
||||
if (pairCount == 0) return inputDeps;
|
||||
|
||||
var job = new GenerateSphereContactsJob
|
||||
{
|
||||
contactPairs = contactPairs,
|
||||
|
||||
positions = solver.positions,
|
||||
orientations = solver.orientations,
|
||||
velocities = solver.velocities,
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
|
||||
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
|
||||
contactsQueue = contactQueue.AsParallelWriter(),
|
||||
|
||||
worldToSolver = solver.worldToSolver,
|
||||
deltaTime = deltaTime,
|
||||
parameters = solver.abstraction.parameters,
|
||||
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.Sphere]
|
||||
};
|
||||
|
||||
inputDeps = job.Schedule(pairCount, 8, inputDeps);
|
||||
return inputDeps;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct GenerateSphereContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
|
||||
|
||||
// particle arrays:
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
// collider arrays:
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
|
||||
[WriteOnly]
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
|
||||
|
||||
// auxiliar data:
|
||||
[ReadOnly] public int firstPair;
|
||||
[ReadOnly] public BurstAffineTransform worldToSolver;
|
||||
[ReadOnly] public float deltaTime;
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int simplexIndex = contactPairs[firstPair + i].bodyA;
|
||||
int colliderIndex = contactPairs[firstPair + i].bodyB;
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
|
||||
|
||||
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
|
||||
|
||||
BurstSphere shape = new BurstSphere { colliderToSolver = colliderToSolver, shape = shapes[colliderIndex] };
|
||||
|
||||
var co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref shape, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out _, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
|
||||
contactsQueue.Enqueue(new BurstContact {
|
||||
bodyA = simplexIndex,
|
||||
bodyB = colliderIndex,
|
||||
pointA = simplexBary,
|
||||
pointB = colliderPoint.point,
|
||||
normal = colliderPoint.normal * shape.shape.sign
|
||||
});
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstSphere>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstTriangleMesh : BurstLocalOptimization.IDistanceFunction
|
||||
public struct BurstTriangleMesh : BurstLocalOptimization.IDistanceFunction, IBurstCollider
|
||||
{
|
||||
public BurstColliderShape shape;
|
||||
public BurstAffineTransform colliderToSolver;
|
||||
public BurstAffineTransform solverToWorld;
|
||||
|
||||
public BurstMath.CachedTri tri;
|
||||
public TriangleMeshHeader header;
|
||||
public NativeArray<BIHNode> bihNodes;
|
||||
public NativeArray<Triangle> triangles;
|
||||
public NativeArray<float3> vertices;
|
||||
|
||||
public float dt;
|
||||
public float collisionMargin;
|
||||
|
||||
private BurstMath.CachedTri tri;
|
||||
|
||||
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
|
||||
{
|
||||
point = colliderToSolver.InverseTransformPointUnscaled(point);
|
||||
|
||||
if (shape.is2D)
|
||||
if (shape.is2D != 0)
|
||||
point[2] = 0;
|
||||
|
||||
float4 nearestPoint = BurstMath.NearestPointOnTri(tri, point, out float4 bary);
|
||||
@@ -27,194 +34,116 @@ namespace Obi
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
}
|
||||
|
||||
public static JobHandle GenerateContacts(ObiColliderWorld world,
|
||||
BurstSolverImpl solver,
|
||||
NativeList<Oni.ContactPair> contactPairs,
|
||||
NativeQueue<BurstContact> contactQueue,
|
||||
NativeArray<int> contactOffsetsPerType,
|
||||
float deltaTime,
|
||||
JobHandle inputDeps)
|
||||
public void Contacts(int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> velocities,
|
||||
NativeArray<float4> radii,
|
||||
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.TriangleMesh + 1] - contactOffsetsPerType[(int)Oni.ShapeType.TriangleMesh];
|
||||
if (pairCount == 0) return inputDeps;
|
||||
|
||||
var job = new GenerateTriangleMeshContactsJob
|
||||
{
|
||||
contactPairs = contactPairs,
|
||||
|
||||
positions = solver.positions,
|
||||
orientations = solver.orientations,
|
||||
velocities = solver.velocities,
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
simplexBounds = solver.simplexBounds,
|
||||
|
||||
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
|
||||
triangleMeshHeaders = world.triangleMeshContainer.headers.AsNativeArray<TriangleMeshHeader>(),
|
||||
bihNodes = world.triangleMeshContainer.bihNodes.AsNativeArray<BIHNode>(),
|
||||
triangles = world.triangleMeshContainer.triangles.AsNativeArray<Triangle>(),
|
||||
vertices = world.triangleMeshContainer.vertices.AsNativeArray<float3>(),
|
||||
|
||||
contactsQueue = contactQueue.AsParallelWriter(),
|
||||
|
||||
solverToWorld = solver.inertialFrame,
|
||||
worldToSolver = solver.worldToSolver,
|
||||
deltaTime = deltaTime,
|
||||
parameters = solver.abstraction.parameters,
|
||||
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.TriangleMesh]
|
||||
};
|
||||
|
||||
inputDeps = job.Schedule(pairCount, 1, inputDeps);
|
||||
return inputDeps;
|
||||
BIHTraverse(colliderIndex, rigidbodyIndex, simplexIndex, simplexStart, simplexSize,
|
||||
rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBounds, 0, contacts, optimizationIterations, optimizationTolerance);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct GenerateTriangleMeshContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
|
||||
|
||||
// particle arrays:
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
[ReadOnly] public NativeArray<BurstAabb> simplexBounds;
|
||||
|
||||
// collider arrays:
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
|
||||
// triangle mesh data:
|
||||
[ReadOnly] public NativeArray<TriangleMeshHeader> triangleMeshHeaders;
|
||||
[ReadOnly] public NativeArray<BIHNode> bihNodes;
|
||||
[ReadOnly] public NativeArray<Triangle> triangles;
|
||||
[ReadOnly] public NativeArray<float3> vertices;
|
||||
|
||||
[WriteOnly]
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
|
||||
|
||||
// auxiliar data:
|
||||
[ReadOnly] public int firstPair;
|
||||
[ReadOnly] public BurstInertialFrame solverToWorld;
|
||||
[ReadOnly] public BurstAffineTransform worldToSolver;
|
||||
[ReadOnly] public float deltaTime;
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
|
||||
public void Execute(int i)
|
||||
private void BIHTraverse(int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> velocities,
|
||||
NativeArray<float4> radii,
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int nodeIndex,
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
int simplexIndex = contactPairs[firstPair + i].bodyA;
|
||||
int colliderIndex = contactPairs[firstPair + i].bodyB;
|
||||
var shape = shapes[colliderIndex];
|
||||
var node = bihNodes[header.firstNode + nodeIndex];
|
||||
|
||||
if (shape.dataIndex < 0)
|
||||
return;
|
||||
if (node.firstChild >= 0)
|
||||
{
|
||||
// visit min node:
|
||||
if (simplexBounds.min[node.axis] <= node.min)
|
||||
BIHTraverse(colliderIndex, rigidbodyIndex, simplexIndex, simplexStart, simplexSize,
|
||||
rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBounds,
|
||||
node.firstChild, contacts, optimizationIterations, optimizationTolerance);
|
||||
|
||||
int rigidbodyIndex = shape.rigidbodyIndex;
|
||||
var header = triangleMeshHeaders[shape.dataIndex];
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
|
||||
var simplexBound = simplexBounds[simplexIndex];
|
||||
|
||||
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
|
||||
|
||||
// invert a full matrix here to accurately represent collider bounds scale.
|
||||
var solverToCollider = math.inverse(float4x4.TRS(colliderToSolver.translation.xyz, colliderToSolver.rotation, colliderToSolver.scale.xyz));
|
||||
var simplexBoundsCS = simplexBound.Transformed(solverToCollider);
|
||||
|
||||
float4 marginCS = new float4((shape.contactOffset + parameters.collisionMargin) / colliderToSolver.scale.xyz, 0);
|
||||
|
||||
BurstTriangleMesh triangleMeshShape = new BurstTriangleMesh()
|
||||
// visit max node:
|
||||
if (simplexBounds.max[node.axis] >= node.max)
|
||||
BIHTraverse(colliderIndex, rigidbodyIndex, simplexIndex, simplexStart, simplexSize,
|
||||
rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBounds,
|
||||
node.firstChild + 1, contacts, optimizationIterations, optimizationTolerance);
|
||||
}
|
||||
else
|
||||
{
|
||||
colliderToSolver = colliderToSolver,
|
||||
shape = shape
|
||||
};
|
||||
|
||||
NativeQueue<int> queue = new NativeQueue<int>(Allocator.Temp);
|
||||
|
||||
queue.Enqueue(0);
|
||||
|
||||
while (!queue.IsEmpty())
|
||||
{
|
||||
int nodeIndex = queue.Dequeue();
|
||||
var node = bihNodes[header.firstNode + nodeIndex];
|
||||
|
||||
// leaf node:
|
||||
if (node.firstChild < 0)
|
||||
// check for contact against all triangles:
|
||||
for (int dataOffset = node.start; dataOffset < node.start + node.count; ++dataOffset)
|
||||
{
|
||||
// check for contact against all triangles:
|
||||
for (int dataOffset = node.start; dataOffset < node.start + node.count; ++dataOffset)
|
||||
Triangle t = triangles[header.firstTriangle + dataOffset];
|
||||
float4 v1 = new float4(vertices[header.firstVertex + t.i1], 0);
|
||||
float4 v2 = new float4(vertices[header.firstVertex + t.i2], 0);
|
||||
float4 v3 = new float4(vertices[header.firstVertex + t.i3], 0);
|
||||
BurstAabb triangleBounds = new BurstAabb(v1, v2, v3, shape.contactOffset + collisionMargin);
|
||||
|
||||
if (triangleBounds.IntersectsAabb(simplexBounds, shape.is2D != 0))
|
||||
{
|
||||
Triangle t = triangles[header.firstTriangle + dataOffset];
|
||||
float4 v1 = new float4(vertices[header.firstVertex + t.i1], 0);
|
||||
float4 v2 = new float4(vertices[header.firstVertex + t.i2], 0);
|
||||
float4 v3 = new float4(vertices[header.firstVertex + t.i3], 0);
|
||||
BurstAabb triangleBounds = new BurstAabb(v1, v2, v3, marginCS);
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
tri.Cache(v1 * colliderToSolver.scale, v2 * colliderToSolver.scale, v3 * colliderToSolver.scale);
|
||||
|
||||
if (triangleBounds.IntersectsAabb(simplexBoundsCS, shape.is2D))
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstTriangleMesh>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 simplexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
float4 velocity = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
simplexRadius += radii[particleIndex].x * simplexBary[j];
|
||||
velocity += velocities[particleIndex] * simplexBary[j];
|
||||
}
|
||||
|
||||
triangleMeshShape.tri.Cache(v1 * colliderToSolver.scale, v2 * colliderToSolver.scale, v3 * colliderToSolver.scale);
|
||||
float4 rbVelocity = float4.zero;
|
||||
if (rigidbodyIndex >= 0)
|
||||
rbVelocity = BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, colliderPoint.point, rigidbodies, solverToWorld);
|
||||
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref triangleMeshShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 simplexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
float dAB = math.dot(simplexPoint - colliderPoint.point, colliderPoint.normal);
|
||||
float vel = math.dot(velocity - rbVelocity, colliderPoint.normal);
|
||||
|
||||
float4 velocity = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
if (vel * dt + dAB <= simplexRadius + shape.contactOffset + collisionMargin)
|
||||
{
|
||||
contacts.Enqueue(new BurstContact()
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
simplexRadius += radii[particleIndex].x * simplexBary[j];
|
||||
velocity += velocities[particleIndex] * simplexBary[j];
|
||||
}
|
||||
|
||||
float4 rbVelocity = float4.zero;
|
||||
if (rigidbodyIndex >= 0)
|
||||
rbVelocity = BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, colliderPoint.point, rigidbodies, solverToWorld);
|
||||
|
||||
float dAB = math.dot(simplexPoint - colliderPoint.point, colliderPoint.normal);
|
||||
float vel = math.dot(velocity - rbVelocity, colliderPoint.normal);
|
||||
|
||||
if (vel * deltaTime + dAB <= simplexRadius + shape.contactOffset + parameters.collisionMargin)
|
||||
{
|
||||
contactsQueue.Enqueue(new BurstContact()
|
||||
{
|
||||
bodyA = simplexIndex,
|
||||
bodyB = colliderIndex,
|
||||
pointA = simplexBary,
|
||||
pointB = colliderPoint.point,
|
||||
normal = colliderPoint.normal * triangleMeshShape.shape.sign
|
||||
});
|
||||
}
|
||||
bodyA = simplexIndex,
|
||||
bodyB = colliderIndex,
|
||||
pointA = simplexBary,
|
||||
pointB = colliderPoint.point,
|
||||
normal = colliderPoint.normal,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else // check min and/or max children:
|
||||
{
|
||||
// visit min node:
|
||||
if (simplexBoundsCS.min[node.axis] <= node.min)
|
||||
queue.Enqueue(node.firstChild);
|
||||
|
||||
// visit max node:
|
||||
if (simplexBoundsCS.max[node.axis] >= node.max)
|
||||
queue.Enqueue(node.firstChild + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
interface IBurstCollider
|
||||
{
|
||||
void Contacts(int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> velocities,
|
||||
NativeArray<float4> radii,
|
||||
|
||||
NativeArray<int> simplices,
|
||||
in BurstAabb simplexBounds,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
|
||||
NativeQueue<BurstContact>.ParallelWriter contacts,
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71db1a3a2698147deb04f0264e4a728f
|
||||
guid: 424c135125b644408aeda26a57bd6e40
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -26,12 +26,12 @@ namespace Obi
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new AerodynamicConstraintsBatchJob()
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Obi
|
||||
applyConstraints.particleIndices = this.particleIndices;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
projectConstraints.positions = solverImplementation.positions;
|
||||
projectConstraints.invMasses = solverImplementation.invMasses;
|
||||
@@ -97,6 +97,7 @@ namespace Obi
|
||||
float4 bendVector = positions[p3] - (positions[p1] + positions[p2] + positions[p3]) / 3.0f;
|
||||
float bend = math.length(bendVector);
|
||||
|
||||
|
||||
float constraint = bend - restBends[i];
|
||||
|
||||
constraint = math.max(0, constraint - stiffnesses[i].x) +
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Obi
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new BendTwistConstraintsBatchJob()
|
||||
{
|
||||
@@ -107,11 +107,12 @@ namespace Obi
|
||||
quaternion omega_plus;
|
||||
omega_plus.value = omega.value + rest.value; //delta Omega with - omega_0
|
||||
omega.value -= rest.value; //delta Omega with + omega_0
|
||||
|
||||
if (math.lengthsq(omega.value) > math.lengthsq(omega_plus.value))
|
||||
omega = omega_plus;
|
||||
|
||||
// plasticity
|
||||
if (math.lengthsq(omega.value.xyz) > plasticity[i].x * plasticity[i].x)
|
||||
if (math.lengthsq(omega.value) > plasticity[i].x * plasticity[i].x)
|
||||
{
|
||||
rest.value += omega.value * plasticity[i].y * deltaTime;
|
||||
restDarboux[i] = rest;
|
||||
|
||||
@@ -24,7 +24,11 @@ namespace Obi
|
||||
|
||||
public bool enabled
|
||||
{
|
||||
set { m_Enabled = value; }
|
||||
set
|
||||
{
|
||||
if (m_Enabled != value)
|
||||
m_Enabled = value;
|
||||
}
|
||||
get { return m_Enabled; }
|
||||
}
|
||||
|
||||
@@ -46,17 +50,22 @@ namespace Obi
|
||||
protected NativeArray<int> particleIndices;
|
||||
protected NativeArray<float> lambdas;
|
||||
|
||||
public virtual JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public virtual JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
if (lambdas.IsCreated)
|
||||
{
|
||||
inputDeps = new ClearLambdasJob {lambdas = lambdas}.Schedule(lambdas.Length, 256, inputDeps);
|
||||
// no need for jobs here, memclear is faster and we don't pay scheduling overhead.
|
||||
unsafe
|
||||
{
|
||||
UnsafeUtility.MemClear(NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(lambdas),
|
||||
lambdas.Length * UnsafeUtility.SizeOf<float>());
|
||||
}
|
||||
}
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
// implemented by concrete constraint subclasses.
|
||||
public abstract JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft);
|
||||
public abstract JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps);
|
||||
public abstract JobHandle Apply(JobHandle inputDeps, float substepTime);
|
||||
|
||||
public virtual void Destroy()
|
||||
@@ -95,16 +104,6 @@ namespace Obi
|
||||
counts[particleIndex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ClearLambdasJob : IJobParallelFor
|
||||
{
|
||||
public NativeArray<float> lambdas;
|
||||
public void Execute(int i)
|
||||
{
|
||||
lambdas[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -9,8 +9,8 @@ namespace Obi
|
||||
{
|
||||
public interface IBurstConstraintsImpl : IConstraints
|
||||
{
|
||||
JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft);
|
||||
JobHandle Project(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft);
|
||||
JobHandle Initialize(JobHandle inputDeps, float substepTime);
|
||||
JobHandle Project(JobHandle inputDeps, float stepTime, float substepTime, int substeps);
|
||||
void Dispose();
|
||||
|
||||
IConstraintsBatchImpl CreateConstraintsBatch();
|
||||
@@ -63,14 +63,14 @@ namespace Obi
|
||||
return count;
|
||||
}
|
||||
|
||||
public JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
// initialize all batches in parallel:
|
||||
if (batches.Count > 0)
|
||||
{
|
||||
NativeArray<JobHandle> deps = new NativeArray<JobHandle>(batches.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
deps[i] = batches[i].enabled ? batches[i].Initialize(inputDeps, stepTime, substepTime, steps, timeLeft) : inputDeps;
|
||||
deps[i] = batches[i].enabled ? batches[i].Initialize(inputDeps, substepTime) : inputDeps;
|
||||
|
||||
JobHandle result = JobHandle.CombineDependencies(deps);
|
||||
deps.Dispose();
|
||||
@@ -81,7 +81,7 @@ namespace Obi
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public JobHandle Project(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public JobHandle Project(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
UnityEngine.Profiling.Profiler.BeginSample("Project");
|
||||
|
||||
@@ -90,11 +90,11 @@ namespace Obi
|
||||
switch(parameters.evaluationOrder)
|
||||
{
|
||||
case Oni.ConstraintParameters.EvaluationOrder.Sequential:
|
||||
inputDeps = EvaluateSequential(inputDeps, stepTime, substepTime, steps, timeLeft);
|
||||
inputDeps = EvaluateSequential(inputDeps, stepTime, substepTime, substeps);
|
||||
break;
|
||||
|
||||
case Oni.ConstraintParameters.EvaluationOrder.Parallel:
|
||||
inputDeps = EvaluateParallel(inputDeps, stepTime, substepTime, steps, timeLeft);
|
||||
inputDeps = EvaluateParallel(inputDeps, stepTime, substepTime, substeps);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -103,14 +103,14 @@ namespace Obi
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
protected virtual JobHandle EvaluateSequential(JobHandle inputDeps, float stepTime, float substepTime,int steps, float timeLeft)
|
||||
protected virtual JobHandle EvaluateSequential(JobHandle inputDeps, float stepTime, float substepTime,int substeps)
|
||||
{
|
||||
// evaluate and apply all batches:
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].Evaluate(inputDeps, stepTime, substepTime, steps, timeLeft);
|
||||
inputDeps = batches[i].Evaluate(inputDeps, stepTime, substepTime, substeps);
|
||||
inputDeps = batches[i].Apply(inputDeps, substepTime);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
@@ -119,13 +119,13 @@ namespace Obi
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
protected virtual JobHandle EvaluateParallel(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
protected virtual JobHandle EvaluateParallel(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
// evaluate all batches:
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].Evaluate(inputDeps, stepTime, substepTime, steps, timeLeft);
|
||||
inputDeps = batches[i].Evaluate(inputDeps, stepTime, substepTime, substeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Obi
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
|
||||
var projectConstraints = new ChainConstraintsBatchJob()
|
||||
@@ -88,14 +88,12 @@ namespace Obi
|
||||
{
|
||||
int numEdges = numIndices[c] - 1;
|
||||
int first = firstIndex[c];
|
||||
float minLength = restLengths[c].x;
|
||||
float minLength = restLengths[c].y;
|
||||
float maxLength = restLengths[c].y;
|
||||
|
||||
// (ni:constraint gradient, di:desired lenght)
|
||||
NativeArray<float4> ni = new NativeArray<float4>(numEdges, Allocator.Temp);
|
||||
|
||||
// calculate ai (subdiagonals), bi (diagonals) and ci (superdiagonals):
|
||||
NativeArray<float3> diagonals = new NativeArray<float3>(numEdges, Allocator.Temp);
|
||||
NativeArray<float> di = new NativeArray<float>(numEdges, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < numEdges; ++i)
|
||||
{
|
||||
@@ -106,10 +104,20 @@ namespace Obi
|
||||
float4 diff = p1 - p2;
|
||||
|
||||
float distance = math.length(diff);
|
||||
float correction = 0;
|
||||
|
||||
if (distance >= maxLength)
|
||||
correction = distance - maxLength;
|
||||
else if (distance <= minLength)
|
||||
correction = distance - minLength;
|
||||
|
||||
di[i] = correction;
|
||||
ni[i] = new float4(diff/(distance + BurstMath.epsilon));
|
||||
}
|
||||
|
||||
// calculate ai, bi and ci (superdiagonals):
|
||||
// calculate ai (subdiagonals), bi (diagonals) and ci (superdiagonals):
|
||||
NativeArray<float3> diagonals = new NativeArray<float3>(numEdges, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < numEdges; ++i)
|
||||
{
|
||||
int edge = first + i;
|
||||
@@ -127,47 +135,34 @@ namespace Obi
|
||||
-w__i * math.dot(n_i_, n__i));// ci
|
||||
}
|
||||
|
||||
NativeArray<float2> sweep = new NativeArray<float2>(numEdges, Allocator.Temp);
|
||||
|
||||
// solve step #1, forward sweep:
|
||||
// reuse diagonals.xy to store sweep results ci_ and di_:
|
||||
for (int i = 0; i < numEdges; ++i)
|
||||
{
|
||||
int edge = first + i;
|
||||
float4 p1 = positions[particleIndices[edge]];
|
||||
float4 p2 = positions[particleIndices[edge + 1]];
|
||||
|
||||
float cip_ = (i > 0) ? diagonals[i - 1].x : 0;
|
||||
float dip_ = (i > 0) ? diagonals[i - 1].y : 0;
|
||||
float cip_ = (i > 0) ? sweep[i - 1].x : 0;
|
||||
float dip_ = (i > 0) ? sweep[i - 1].y : 0;
|
||||
float den = diagonals[i].y - cip_ * diagonals[i].x;
|
||||
|
||||
float3 d = diagonals[i];
|
||||
if (math.abs(den) > BurstMath.epsilon)
|
||||
if (den != 0)
|
||||
{
|
||||
float distance = math.distance(p1, p2);
|
||||
float correction = 0;
|
||||
|
||||
if (distance >= maxLength)
|
||||
correction = distance - maxLength;
|
||||
else if (distance <= minLength)
|
||||
correction = distance - minLength;
|
||||
|
||||
d.xy = new float2(d.z / den,
|
||||
(correction - dip_ * d.x) / den);
|
||||
|
||||
sweep[i] = new float2((diagonals[i].z / den),
|
||||
(di[i] - dip_ * diagonals[i].x) / den);
|
||||
}
|
||||
else
|
||||
d.xy = float2.zero;
|
||||
|
||||
diagonals[i] = d;
|
||||
sweep[i] = float2.zero;
|
||||
}
|
||||
|
||||
// solve step #2, backward sweep. reuse diagonals.z to store solution xi:
|
||||
// solve step #2, backward sweep:
|
||||
NativeArray<float> xi = new NativeArray<float>(numEdges, Allocator.Temp);
|
||||
for (int i = numEdges - 1; i >= 0; --i)
|
||||
{
|
||||
float xi_ = (i < numEdges - 1) ? diagonals[i + 1].z : 0;
|
||||
int edge = first + i;
|
||||
|
||||
float3 d = diagonals[i];
|
||||
d.z = d.y - d.x * xi_;
|
||||
diagonals[i] = d;
|
||||
float xi_ = (i < numEdges - 1) ? xi[i + 1] : 0;
|
||||
xi[i] = sweep[i].y - sweep[i].x * xi_;
|
||||
}
|
||||
|
||||
// calculate deltas:
|
||||
@@ -178,11 +173,11 @@ namespace Obi
|
||||
float4 ni__ = (i > 0) ? ni[i - 1] : float4.zero;
|
||||
float4 n_i_ = (i < numIndices[c] - 1) ? ni[i] : float4.zero;
|
||||
|
||||
float xi_ = (i > 0) ? diagonals[i - 1].z : 0;
|
||||
float nxi = (i < numIndices[c] - 1) ? diagonals[i].z : 0;
|
||||
float xi_ = (i > 0) ? xi[i - 1] : 0;
|
||||
float nxi = (i < numIndices[c] - 1) ? xi[i] : 0;
|
||||
|
||||
int p = particleIndices[index];
|
||||
deltas[p] += invMasses[p] * (ni__ * xi_ - n_i_ * nxi);
|
||||
deltas[p] -= invMasses[p] * (-ni__ * xi_ + n_i_ * nxi);
|
||||
counts[p]++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace Obi
|
||||
|
||||
public override int GetConstraintCount()
|
||||
{
|
||||
return ((BurstSolverImpl)solver).abstraction.colliderContacts.count;
|
||||
if (!((BurstSolverImpl)solver).colliderContacts.IsCreated)
|
||||
return 0;
|
||||
return ((BurstSolverImpl)solver).colliderContacts.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Obi
|
||||
m_ConstraintType = Oni.ConstraintType.Collision;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var updateContacts = new UpdateContactsJob()
|
||||
{
|
||||
@@ -26,7 +26,7 @@ namespace Obi
|
||||
velocities = solverImplementation.velocities,
|
||||
radii = solverImplementation.principalRadii,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
invRotationalMasses = solverImplementation.invRotationalMasses,
|
||||
invInertiaTensors = solverImplementation.invInertiaTensors,
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
|
||||
@@ -39,14 +39,13 @@ namespace Obi
|
||||
rigidbodyLinearDeltas = solverImplementation.abstraction.rigidbodyLinearDeltas.AsNativeArray<float4>(),
|
||||
rigidbodyAngularDeltas = solverImplementation.abstraction.rigidbodyAngularDeltas.AsNativeArray<float4>(),
|
||||
|
||||
contacts = ((BurstSolverImpl)constraints.solver).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
|
||||
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.contactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).colliderContacts,
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame
|
||||
};
|
||||
return updateContacts.Schedule(((BurstSolverImpl)constraints.solver).abstraction.colliderContacts.count, 128, inputDeps);
|
||||
return updateContacts.Schedule(((BurstSolverImpl)constraints.solver).colliderContacts.Length, 128, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
@@ -73,13 +72,11 @@ namespace Obi
|
||||
deltas = solverAbstraction.positionDeltas.AsNativeArray<float4>(),
|
||||
counts = solverAbstraction.positionConstraintCounts.AsNativeArray<int>(),
|
||||
|
||||
contacts = ((BurstSolverImpl)constraints.solver).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
|
||||
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.contactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).colliderContacts,
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
|
||||
constraintParameters = parameters,
|
||||
solverParameters = solverAbstraction.parameters,
|
||||
steps = steps,
|
||||
timeLeft = timeLeft,
|
||||
substeps = substeps,
|
||||
stepTime = stepTime,
|
||||
substepTime = substepTime
|
||||
};
|
||||
@@ -92,7 +89,7 @@ namespace Obi
|
||||
|
||||
var applyConstraints = new ApplyCollisionConstraintsBatchJob()
|
||||
{
|
||||
contacts = ((BurstSolverImpl)constraints.solver).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).colliderContacts,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
@@ -111,7 +108,7 @@ namespace Obi
|
||||
|
||||
/**
|
||||
* Updates contact data (such as contact distance) at the beginning of each substep. This is
|
||||
* necessary because contacts are generated only once at the beginning of each step, not every substep.
|
||||
* necessary because contacts are generalted only once at the beginning of each step, not every substep.
|
||||
*/
|
||||
[BurstCompile]
|
||||
public struct UpdateContactsJob : IJobParallelFor
|
||||
@@ -121,7 +118,7 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> invRotationalMasses;
|
||||
[ReadOnly] public NativeArray<float4> invInertiaTensors;
|
||||
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
@@ -136,14 +133,12 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<float4> rigidbodyLinearDeltas;
|
||||
[ReadOnly] public NativeArray<float4> rigidbodyAngularDeltas;
|
||||
|
||||
public NativeArray<ContactEffectiveMasses> effectiveMasses;
|
||||
public NativeArray<BurstContact> contacts;
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
var contact = contacts[i];
|
||||
var efMasses = effectiveMasses[i];
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);
|
||||
|
||||
@@ -155,7 +150,7 @@ namespace Obi
|
||||
float4 simplexPrevPosition = float4.zero;
|
||||
quaternion simplexPrevOrientation = new quaternion(0, 0, 0, 0);
|
||||
float simplexInvMass = 0;
|
||||
float simplexInvRotationalMass = 0;
|
||||
float4 simplexInvInertia = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
@@ -165,7 +160,7 @@ namespace Obi
|
||||
simplexPrevPosition += prevPositions[particleIndex] * contact.pointA[j];
|
||||
simplexPrevOrientation.value += prevOrientations[particleIndex].value * contact.pointA[j];
|
||||
simplexInvMass += invMasses[particleIndex] * contact.pointA[j];
|
||||
simplexInvRotationalMass = invRotationalMasses[particleIndex] * contact.pointA[j];
|
||||
simplexInvInertia += invInertiaTensors[particleIndex] * contact.pointA[j];
|
||||
simplexRadius += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
|
||||
}
|
||||
|
||||
@@ -173,7 +168,7 @@ namespace Obi
|
||||
int rigidbodyIndex = shapes[contact.bodyB].rigidbodyIndex;
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
relativeVelocity -= BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame);
|
||||
relativeVelocity -= BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
|
||||
int bMaterialIndex = shapes[contact.bodyB].materialIndex;
|
||||
rollingContacts |= bMaterialIndex >= 0 ? collisionMaterials[bMaterialIndex].rollingContacts > 0 : false;
|
||||
@@ -185,21 +180,17 @@ namespace Obi
|
||||
// calculate contact point in A's surface:
|
||||
float4 contactPoint = contact.pointB + contact.normal * contact.distance;
|
||||
|
||||
// calculate contact tangent (first friction direction) using relative velocity:
|
||||
contact.CalculateTangent(relativeVelocity);
|
||||
// update contact orthonormal basis:
|
||||
contact.CalculateBasis(relativeVelocity);
|
||||
|
||||
// calculate A's contact mass.
|
||||
float4 invInertiaTensor = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadius, simplexInvRotationalMass) + new float4(BurstMath.epsilon));
|
||||
efMasses.CalculateContactMassesA(simplexInvMass, invInertiaTensor, simplexPrevPosition, simplexPrevOrientation, contactPoint, contact.normal, contact.tangent, contact.bitangent, rollingContacts);
|
||||
contact.CalculateContactMassesA(simplexInvMass, simplexInvInertia, simplexPrevPosition, simplexPrevOrientation, contactPoint, rollingContacts);
|
||||
|
||||
// calculate B's contact mass.
|
||||
if (rigidbodyIndex >= 0)
|
||||
efMasses.CalculateContactMassesB(rigidbodies[rigidbodyIndex], inertialFrame.frame, contact.pointB, contact.normal, contact.tangent, contact.bitangent);
|
||||
else
|
||||
efMasses.ClearContactMassesB();
|
||||
contact.CalculateContactMassesB(rigidbodies[rigidbodyIndex], inertialFrame.frame);
|
||||
|
||||
contacts[i] = contact;
|
||||
effectiveMasses[i] = efMasses;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,14 +220,12 @@ namespace Obi
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
public NativeArray<BurstContact> contacts;
|
||||
[ReadOnly] public NativeArray<ContactEffectiveMasses> effectiveMasses;
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
[ReadOnly] public Oni.ConstraintParameters constraintParameters;
|
||||
[ReadOnly] public Oni.SolverParameters solverParameters;
|
||||
[ReadOnly] public float stepTime;
|
||||
[ReadOnly] public float substepTime;
|
||||
[ReadOnly] public float timeLeft;
|
||||
[ReadOnly] public int steps;
|
||||
[ReadOnly] public int substeps;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
@@ -248,15 +237,12 @@ namespace Obi
|
||||
int colliderIndex = contact.bodyB;
|
||||
|
||||
// Skip contacts involving triggers:
|
||||
if (shapes[colliderIndex].isTrigger)
|
||||
if (shapes[colliderIndex].flags > 0)
|
||||
continue;
|
||||
|
||||
// Get the rigidbody index (might be < 0, in that case there's no rigidbody present)
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
|
||||
float frameEnd = stepTime * steps;
|
||||
float substepsToEnd = timeLeft / substepTime;
|
||||
|
||||
// Combine collision materials (use material from first particle in simplex)
|
||||
BurstCollisionMaterial material = CombineCollisionMaterials(simplices[simplexStart], colliderIndex);
|
||||
|
||||
@@ -276,24 +262,24 @@ namespace Obi
|
||||
}
|
||||
|
||||
// project position to the end of the full step:
|
||||
float4 posA = math.lerp(simplexPrevPosition, simplexPosition, substepsToEnd);
|
||||
float4 posA = math.lerp(simplexPrevPosition, simplexPosition, substeps);
|
||||
posA += -contact.normal * simplexRadius;
|
||||
|
||||
float4 posB = contact.pointB;
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
posB += BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame) * frameEnd;
|
||||
posB += BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame) * stepTime;
|
||||
|
||||
// adhesion:
|
||||
float lambda = contact.SolveAdhesion(effectiveMasses[i].TotalNormalInvMass, posA, posB, material.stickDistance, material.stickiness, stepTime);
|
||||
float lambda = contact.SolveAdhesion(posA, posB, material.stickDistance, material.stickiness, stepTime);
|
||||
|
||||
// depenetration:
|
||||
lambda += contact.SolvePenetration(effectiveMasses[i].TotalNormalInvMass, posA, posB, solverParameters.maxDepenetration * stepTime);
|
||||
lambda += contact.SolvePenetration(posA, posB, solverParameters.maxDepenetration * stepTime);
|
||||
|
||||
// Apply normal impulse to both simplex and rigidbody:
|
||||
if (math.abs(lambda) > BurstMath.epsilon)
|
||||
{
|
||||
float4 delta = lambda * contact.normal * BurstMath.BaryScale(contact.pointA) / substepsToEnd;
|
||||
float4 delta = lambda * contact.normal * BurstMath.BaryScale(contact.pointA) / substeps;
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
@@ -312,8 +298,8 @@ namespace Obi
|
||||
}
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstMath.ApplyImpulse(rigidbodyIndex, -lambda / frameEnd * contact.normal, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
{
|
||||
BurstMath.ApplyImpulse(rigidbodyIndex, -lambda / stepTime * contact.normal, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ namespace Obi
|
||||
|
||||
public override int GetConstraintCount()
|
||||
{
|
||||
return ((BurstSolverImpl)solver).abstraction.colliderContacts.count;
|
||||
if (!((BurstSolverImpl)solver).colliderContacts.IsCreated)
|
||||
return 0;
|
||||
return ((BurstSolverImpl)solver).colliderContacts.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,16 @@ namespace Obi
|
||||
m_ConstraintType = Oni.ConstraintType.Friction;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
if (!((BurstSolverImpl)constraints.solver).colliderContacts.IsCreated)
|
||||
return inputDeps;
|
||||
|
||||
var projectConstraints = new FrictionConstraintsBatchJob()
|
||||
{
|
||||
positions = solverImplementation.positions,
|
||||
@@ -30,7 +33,7 @@ namespace Obi
|
||||
prevOrientations = solverImplementation.prevOrientations,
|
||||
|
||||
invMasses = solverImplementation.invMasses,
|
||||
invRotationalMasses = solverImplementation.invRotationalMasses,
|
||||
invInertiaTensors = solverImplementation.invInertiaTensors,
|
||||
radii = solverImplementation.principalRadii,
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
|
||||
@@ -49,10 +52,9 @@ namespace Obi
|
||||
orientationDeltas = solverImplementation.orientationDeltas,
|
||||
orientationCounts = solverImplementation.orientationConstraintCounts,
|
||||
|
||||
contacts = ((BurstSolverImpl)constraints.solver).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
|
||||
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.contactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).colliderContacts,
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
|
||||
steps = steps,
|
||||
substeps = substeps,
|
||||
stepTime = stepTime,
|
||||
substepTime = substepTime
|
||||
};
|
||||
@@ -61,11 +63,14 @@ namespace Obi
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
if (!((BurstSolverImpl)constraints.solver).colliderContacts.IsCreated)
|
||||
return inputDeps;
|
||||
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
var applyConstraints = new ApplyCollisionConstraintsBatchJob()
|
||||
{
|
||||
contacts = ((BurstSolverImpl)constraints.solver).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).colliderContacts,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
@@ -91,7 +96,7 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<quaternion> prevOrientations;
|
||||
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> invRotationalMasses;
|
||||
[ReadOnly] public NativeArray<float4> invInertiaTensors;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
|
||||
@@ -113,11 +118,10 @@ namespace Obi
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> orientationCounts;
|
||||
|
||||
public NativeArray<BurstContact> contacts;
|
||||
[ReadOnly] public NativeArray<ContactEffectiveMasses> effectiveMasses;
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
[ReadOnly] public float stepTime;
|
||||
[ReadOnly] public float substepTime;
|
||||
[ReadOnly] public int steps;
|
||||
[ReadOnly] public int substeps;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
@@ -130,7 +134,7 @@ namespace Obi
|
||||
int colliderIndex = contact.bodyB;
|
||||
|
||||
// Skip contacts involving triggers:
|
||||
if (shapes[colliderIndex].isTrigger)
|
||||
if (shapes[colliderIndex].flags > 0)
|
||||
continue;
|
||||
|
||||
// Get the rigidbody index (might be < 0, in that case there's no rigidbody present)
|
||||
@@ -145,9 +149,9 @@ namespace Obi
|
||||
float4 prevPositionA = float4.zero;
|
||||
float4 linearVelocityA = float4.zero;
|
||||
float4 angularVelocityA = float4.zero;
|
||||
float invRotationalMassA = 0;
|
||||
float4 invInertiaTensorA = float4.zero;
|
||||
quaternion orientationA = new quaternion(0, 0, 0, 0);
|
||||
float4 simplexRadiiA = float4.zero;
|
||||
float simplexRadiusA = 0;
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
@@ -155,17 +159,17 @@ namespace Obi
|
||||
prevPositionA += prevPositions[particleIndex] * contact.pointA[j];
|
||||
linearVelocityA += BurstIntegration.DifferentiateLinear(positions[particleIndex],prevPositions[particleIndex], substepTime) * contact.pointA[j];
|
||||
angularVelocityA += BurstIntegration.DifferentiateAngular(orientations[particleIndex], prevOrientations[particleIndex], substepTime) * contact.pointA[j];
|
||||
invRotationalMassA += invRotationalMasses[particleIndex] * contact.pointA[j];
|
||||
invInertiaTensorA += invInertiaTensors[particleIndex] * contact.pointA[j];
|
||||
orientationA.value += orientations[particleIndex].value * contact.pointA[j];
|
||||
simplexRadiiA += radii[particleIndex] * contact.pointA[j];
|
||||
simplexRadiusA += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
|
||||
}
|
||||
|
||||
|
||||
float4 relativeVelocity = linearVelocityA;
|
||||
|
||||
// Add particle angular velocity if rolling contacts are enabled:
|
||||
if (material.rollingContacts > 0)
|
||||
{
|
||||
rA = -contact.normal * BurstMath.EllipsoidRadius(contact.normal, orientationA, simplexRadiiA.xyz);
|
||||
rA = -contact.normal * simplexRadiusA;
|
||||
relativeVelocity += new float4(math.cross(angularVelocityA.xyz, rA.xyz), 0);
|
||||
}
|
||||
|
||||
@@ -174,11 +178,11 @@ namespace Obi
|
||||
{
|
||||
// Note: unlike rA, that is expressed in solver space, rB is expressed in world space.
|
||||
rB = inertialFrame.frame.TransformPoint(contact.pointB) - rigidbodies[rigidbodyIndex].com;
|
||||
relativeVelocity -= BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame);
|
||||
relativeVelocity -= BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
}
|
||||
|
||||
// Determine impulse magnitude:
|
||||
float2 impulses = contact.SolveFriction(effectiveMasses[i].TotalTangentInvMass, effectiveMasses[i].TotalBitangentInvMass, relativeVelocity, material.staticFriction, material.dynamicFriction, stepTime);
|
||||
float2 impulses = contact.SolveFriction(relativeVelocity, material.staticFriction, material.dynamicFriction, stepTime);
|
||||
|
||||
if (math.abs(impulses.x) > BurstMath.epsilon || math.abs(impulses.y) > BurstMath.epsilon)
|
||||
{
|
||||
@@ -190,7 +194,8 @@ namespace Obi
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
deltas[particleIndex] += (tangentImpulse * effectiveMasses[i].tangentInvMassA + bitangentImpulse * effectiveMasses[i].bitangentInvMassA) * substepTime * contact.pointA[j] * baryScale;
|
||||
//(tangentImpulse * contact.tangentInvMassA + bitangentImpulse * contact.bitangentInvMassA) * dt;
|
||||
deltas[particleIndex] += (tangentImpulse * contact.tangentInvMassA + bitangentImpulse * contact.bitangentInvMassA) * substepTime * contact.pointA[j] * baryScale;
|
||||
counts[particleIndex]++;
|
||||
}
|
||||
|
||||
@@ -203,8 +208,7 @@ namespace Obi
|
||||
if (material.rollingContacts > 0)
|
||||
{
|
||||
// Calculate angular velocity deltas due to friction impulse:
|
||||
float4 invInertiaTensor = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadiiA, invRotationalMassA) + new float4(BurstMath.epsilon));
|
||||
float4x4 solverInertiaA = BurstMath.TransformInertiaTensor(invInertiaTensor, orientationA);
|
||||
float4x4 solverInertiaA = BurstMath.TransformInertiaTensor(invInertiaTensorA, orientationA);
|
||||
|
||||
float4 angVelDeltaA = math.mul(solverInertiaA, new float4(math.cross(rA.xyz, totalImpulse.xyz), 0));
|
||||
float4 angVelDeltaB = float4.zero;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using Unity.Jobs;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
@@ -11,6 +12,10 @@ namespace Obi
|
||||
{
|
||||
public NativeList<int> fluidParticles;
|
||||
|
||||
public NativeArray<float4> eta;
|
||||
public NativeArray<float4> smoothPositions;
|
||||
public NativeArray<float3x3> anisotropies;
|
||||
|
||||
public BurstDensityConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.Density)
|
||||
{
|
||||
fluidParticles = new NativeList<int>(Allocator.Persistent);
|
||||
@@ -34,12 +39,12 @@ namespace Obi
|
||||
batch.Destroy();
|
||||
}
|
||||
|
||||
protected override JobHandle EvaluateSequential(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
protected override JobHandle EvaluateSequential(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
return EvaluateParallel(inputDeps, stepTime, substepTime, steps, timeLeft);
|
||||
return EvaluateParallel(inputDeps, stepTime, substepTime, substeps);
|
||||
}
|
||||
|
||||
protected override JobHandle EvaluateParallel(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
protected override JobHandle EvaluateParallel(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
inputDeps = UpdateInteractions(inputDeps);
|
||||
|
||||
@@ -48,41 +53,15 @@ namespace Obi
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].Evaluate(inputDeps, stepTime, substepTime, steps, timeLeft);
|
||||
inputDeps = batches[i].Evaluate(inputDeps, stepTime, substepTime, substeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
// calculate per-particle density lambdas:
|
||||
// calculate per-particle lambdas:
|
||||
inputDeps = CalculateLambdas(inputDeps, substepTime);
|
||||
|
||||
// calculate viscosity/vorticity:
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].ViscosityAndVorticity(inputDeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
// apply viscosity/vorticity positional deltas:
|
||||
var app = new ApplyPositionDeltasJob()
|
||||
{
|
||||
fluidParticles = fluidParticles,
|
||||
positions = m_Solver.positions,
|
||||
deltas = m_Solver.positionDeltas,
|
||||
counts = m_Solver.positionConstraintCounts,
|
||||
anisotropies = m_Solver.anisotropies,
|
||||
normals = m_Solver.normals,
|
||||
fluidData = m_Solver.fluidData,
|
||||
matchingRotations = m_Solver.orientationDeltas,
|
||||
linearFromAngular = m_Solver.restPositions,
|
||||
};
|
||||
|
||||
inputDeps = app.Schedule(fluidParticles.Length, 64, inputDeps);
|
||||
|
||||
// apply density positional deltas:
|
||||
// then apply them:
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
@@ -95,23 +74,29 @@ namespace Obi
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public JobHandle CalculateVelocityCorrections(JobHandle inputDeps, float deltaTime)
|
||||
public JobHandle ApplyVelocityCorrections(JobHandle inputDeps, float deltaTime)
|
||||
{
|
||||
eta = new NativeArray<float4>(((BurstSolverImpl)solver).particleCount, Allocator.TempJob);
|
||||
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].CalculateNormals(inputDeps, deltaTime);
|
||||
inputDeps = batches[i].CalculateViscosityAndNormals(inputDeps, deltaTime);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
return inputDeps;
|
||||
}
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].CalculateVorticity(inputDeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
public JobHandle ApplyVelocityCorrections(JobHandle inputDeps, float deltaTime)
|
||||
{
|
||||
inputDeps = ApplyAtmosphere(inputDeps, deltaTime);
|
||||
inputDeps = ApplyVorticityAndAtmosphere(inputDeps, deltaTime);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
|
||||
return inputDeps;
|
||||
@@ -121,7 +106,10 @@ namespace Obi
|
||||
{
|
||||
// if the constraints are deactivated or we need no anisotropy:
|
||||
if (((BurstSolverImpl)solver).abstraction.parameters.maxAnisotropy <= 1)
|
||||
return inputDeps;
|
||||
return IdentityAnisotropy(inputDeps);
|
||||
|
||||
smoothPositions = new NativeArray<float4>(((BurstSolverImpl)solver).particleCount, Allocator.TempJob);
|
||||
anisotropies = new NativeArray<float3x3>(((BurstSolverImpl)solver).particleCount, Allocator.TempJob);
|
||||
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
@@ -151,11 +139,8 @@ namespace Obi
|
||||
// clear existing fluid data:
|
||||
var clearData = new ClearFluidDataJob()
|
||||
{
|
||||
fluidParticles = fluidParticles,
|
||||
fluidData = m_Solver.fluidData,
|
||||
massCenters = m_Solver.normals,
|
||||
prevMassCenters = m_Solver.renderablePositions,
|
||||
moments = m_Solver.anisotropies
|
||||
fluidParticles = fluidParticles.AsDeferredJobArray(),
|
||||
fluidData = ((BurstSolverImpl)solver).abstraction.fluidData.AsNativeArray<float4>(),
|
||||
};
|
||||
|
||||
inputDeps = clearData.Schedule(fluidParticles.Length, 64, inputDeps);
|
||||
@@ -165,7 +150,7 @@ namespace Obi
|
||||
{
|
||||
pairs = m_Solver.fluidInteractions,
|
||||
positions = m_Solver.positions,
|
||||
fluidMaterials = m_Solver.fluidMaterials,
|
||||
radii = m_Solver.smoothingRadii,
|
||||
densityKernel = new Poly6Kernel(((BurstSolverImpl)solver).abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
gradientKernel = new SpikyKernel(((BurstSolverImpl)solver).abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
};
|
||||
@@ -176,65 +161,64 @@ namespace Obi
|
||||
private JobHandle CalculateLambdas(JobHandle inputDeps, float deltaTime)
|
||||
{
|
||||
// calculate lagrange multipliers:
|
||||
var calculateLambdas = new CalculateLambdasJob
|
||||
var calculateLambdas = new CalculateLambdasJob()
|
||||
{
|
||||
fluidParticles = fluidParticles,
|
||||
positions = m_Solver.positions,
|
||||
prevPositions = m_Solver.prevPositions,
|
||||
matchingRotations = m_Solver.restPositions.Reinterpret<quaternion>(),
|
||||
principalRadii = m_Solver.principalRadii,
|
||||
fluidMaterials = m_Solver.fluidMaterials,
|
||||
fluidParticles = fluidParticles.AsDeferredJobArray(),
|
||||
invMasses = m_Solver.invMasses,
|
||||
radii = m_Solver.smoothingRadii,
|
||||
restDensities = m_Solver.restDensities,
|
||||
surfaceTension = m_Solver.surfaceTension,
|
||||
densityKernel = new Poly6Kernel(m_Solver.abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
gradientKernel = new SpikyKernel(m_Solver.abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
fluidData = m_Solver.fluidData,
|
||||
|
||||
massCenters = m_Solver.normals,
|
||||
prevMassCenters = m_Solver.renderablePositions,
|
||||
moments = m_Solver.anisotropies,
|
||||
|
||||
deltas = m_Solver.positionDeltas,
|
||||
counts = m_Solver.positionConstraintCounts,
|
||||
|
||||
solverParams = m_Solver.abstraction.parameters
|
||||
normals = m_Solver.normals,
|
||||
vorticity = m_Solver.vorticities,
|
||||
fluidData = m_Solver.fluidData
|
||||
};
|
||||
|
||||
return calculateLambdas.Schedule(fluidParticles.Length,64,inputDeps);
|
||||
}
|
||||
|
||||
private JobHandle ApplyAtmosphere(JobHandle inputDeps, float deltaTime)
|
||||
private JobHandle ApplyVorticityAndAtmosphere(JobHandle inputDeps, float deltaTime)
|
||||
{
|
||||
var conf = new ApplyAtmosphereJob
|
||||
// calculate lagrange multipliers:
|
||||
var conf = new ApplyVorticityConfinementAndAtmosphere()
|
||||
{
|
||||
fluidParticles = fluidParticles,
|
||||
fluidParticles = fluidParticles.AsDeferredJobArray(),
|
||||
wind = m_Solver.wind,
|
||||
fluidInterface = m_Solver.fluidInterface,
|
||||
fluidMaterials2 = m_Solver.fluidMaterials2,
|
||||
principalRadii = m_Solver.principalRadii,
|
||||
vorticities = m_Solver.vorticities,
|
||||
eta = eta,
|
||||
atmosphericDrag = m_Solver.athmosphericDrag,
|
||||
atmosphericPressure = m_Solver.athmosphericPressure,
|
||||
vorticityConfinement = m_Solver.vortConfinement,
|
||||
restDensities = m_Solver.restDensities,
|
||||
normals = m_Solver.normals,
|
||||
fluidData = m_Solver.fluidData,
|
||||
velocities = m_Solver.velocities,
|
||||
angularVelocities = m_Solver.angularVelocities,
|
||||
vorticity = m_Solver.restOrientations.Reinterpret<float4>(),
|
||||
vorticityAccelerations = m_Solver.orientationDeltas.Reinterpret<float4>(),
|
||||
linearAccelerations = m_Solver.positionDeltas,
|
||||
linearFromAngular = m_Solver.restPositions,
|
||||
angularDiffusion = m_Solver.anisotropies,
|
||||
positions = m_Solver.positions,
|
||||
prevPositions = m_Solver.prevPositions,
|
||||
dt = deltaTime,
|
||||
solverParams = m_Solver.abstraction.parameters
|
||||
dt = deltaTime
|
||||
};
|
||||
|
||||
return conf.Schedule(fluidParticles.Length, 64, inputDeps);
|
||||
}
|
||||
|
||||
private JobHandle IdentityAnisotropy(JobHandle inputDeps)
|
||||
{
|
||||
var idAnisotropy = new IdentityAnisotropyJob()
|
||||
{
|
||||
fluidParticles = fluidParticles.AsDeferredJobArray(),
|
||||
principalAxes = m_Solver.anisotropies,
|
||||
radii = m_Solver.principalRadii
|
||||
};
|
||||
|
||||
return idAnisotropy.Schedule(fluidParticles.Length, 64, inputDeps);
|
||||
}
|
||||
|
||||
private JobHandle AverageSmoothPositions(JobHandle inputDeps)
|
||||
{
|
||||
var average = new AverageSmoothPositionsJob()
|
||||
{
|
||||
fluidParticles = fluidParticles,
|
||||
fluidParticles = fluidParticles.AsDeferredJobArray(),
|
||||
renderablePositions = m_Solver.renderablePositions,
|
||||
anisotropies = m_Solver.anisotropies
|
||||
smoothPositions = smoothPositions
|
||||
};
|
||||
|
||||
return average.Schedule(fluidParticles.Length, 64, inputDeps);
|
||||
@@ -244,16 +228,13 @@ namespace Obi
|
||||
{
|
||||
var average = new AverageAnisotropyJob()
|
||||
{
|
||||
fluidParticles = fluidParticles,
|
||||
fluidParticles = fluidParticles.AsDeferredJobArray(),
|
||||
renderablePositions = m_Solver.renderablePositions,
|
||||
renderableOrientations = m_Solver.renderableOrientations,
|
||||
smoothPositions = smoothPositions,
|
||||
principalRadii = m_Solver.principalRadii,
|
||||
anisotropies = m_Solver.anisotropies,
|
||||
anisotropies = anisotropies,
|
||||
maxAnisotropy = m_Solver.abstraction.parameters.maxAnisotropy,
|
||||
renderableRadii = m_Solver.renderableRadii,
|
||||
fluidData = m_Solver.fluidData,
|
||||
life = m_Solver.life,
|
||||
solverParams = m_Solver.abstraction.parameters
|
||||
principalAxes = m_Solver.anisotropies
|
||||
};
|
||||
|
||||
return average.Schedule(fluidParticles.Length, 64, inputDeps);
|
||||
@@ -262,19 +243,12 @@ namespace Obi
|
||||
[BurstCompile]
|
||||
public struct ClearFluidDataJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> massCenters;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevMassCenters;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> moments;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int p = fluidParticles[i];
|
||||
fluidData[p] = float4.zero;
|
||||
massCenters[p] = float4.zero;
|
||||
prevMassCenters[p] = float4.zero;
|
||||
moments[p] = float4x4.zero;
|
||||
fluidData[fluidParticles[i]] = float4.zero;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +256,7 @@ namespace Obi
|
||||
public struct UpdateInteractionsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials;
|
||||
[ReadOnly] public NativeArray<float> radii;
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
[ReadOnly] public SpikyKernel gradientKernel;
|
||||
|
||||
@@ -295,16 +269,16 @@ namespace Obi
|
||||
var pair = pairs[i];
|
||||
|
||||
// calculate normalized gradient vector:
|
||||
pair.gradient = new float4((positions[pair.particleA] - positions[pair.particleB]).xyz,0);
|
||||
pair.gradient = (positions[pair.particleA] - positions[pair.particleB]);
|
||||
float distance = math.length(pair.gradient);
|
||||
pair.gradient /= distance + math.FLT_MIN_NORMAL;
|
||||
|
||||
// calculate and store average density and gradient kernels:
|
||||
pair.avgKernel = (densityKernel.W(distance, fluidMaterials[pair.particleA].x) +
|
||||
densityKernel.W(distance, fluidMaterials[pair.particleB].x)) * 0.5f;
|
||||
pair.avgKernel = (densityKernel.W(distance, radii[pair.particleA]) +
|
||||
densityKernel.W(distance, radii[pair.particleB])) * 0.5f;
|
||||
|
||||
pair.avgGradient = (gradientKernel.W(distance, fluidMaterials[pair.particleA].x) +
|
||||
gradientKernel.W(distance, fluidMaterials[pair.particleB].x)) * 0.5f;
|
||||
pair.avgGradient = (gradientKernel.W(distance, radii[pair.particleA]) +
|
||||
gradientKernel.W(distance, radii[pair.particleB])) * 0.5f;
|
||||
|
||||
pairs[i] = pair;
|
||||
}
|
||||
@@ -313,239 +287,164 @@ namespace Obi
|
||||
[BurstCompile]
|
||||
public struct CalculateLambdasJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials;
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> radii;
|
||||
[ReadOnly] public NativeArray<float> restDensities;
|
||||
[ReadOnly] public NativeArray<float> surfaceTension;
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
[ReadOnly] public SpikyKernel gradientKernel;
|
||||
|
||||
[ReadOnly] public Oni.SolverParameters solverParams;
|
||||
[ReadOnly] public SpikyKernel gradientKernel;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> normals;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> vorticity;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> massCenters;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevMassCenters;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> moments;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<quaternion> matchingRotations;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
float restVolume = math.pow(principalRadii[i].x * 2, 3 - (int)solverParams.mode);
|
||||
float4 data = fluidData[i];
|
||||
|
||||
float grad = restVolume * gradientKernel.W(0, fluidMaterials[i].x);
|
||||
|
||||
// self particle contribution to density, gradient and mass centers:
|
||||
data += new float4(densityKernel.W(0, fluidMaterials[i].x), 0, grad, grad * grad + data[2] * data[2]);
|
||||
massCenters[i] += new float4(positions[i].xyz, 1) / positions[i].w;
|
||||
prevMassCenters[i] += new float4(prevPositions[i].xyz, 1) / positions[i].w;
|
||||
|
||||
// usually, we'd weight density by mass (density contrast formulation) by dividing by invMass. Then, multiply by invMass when
|
||||
// calculating the state equation (density / restDensity - 1, restDensity = mass / volume, so density * invMass * restVolume - 1
|
||||
// We end up with density / invMass * invMass * restVolume - 1, invMass cancels out.
|
||||
float constraint = math.max(0, data[0] * restVolume - 1) * fluidMaterials[i].w;
|
||||
|
||||
// calculate lambda:
|
||||
data[1] = -constraint / (positions[i].w * data[3] + math.FLT_MIN_NORMAL);
|
||||
|
||||
fluidData[i] = data;
|
||||
|
||||
// get total neighborhood mass:
|
||||
float M = massCenters[i][3];
|
||||
massCenters[i] /= massCenters[i][3];
|
||||
prevMassCenters[i] /= prevMassCenters[i][3];
|
||||
|
||||
// update moments:
|
||||
moments[i] += (BurstMath.multrnsp4(positions[i], prevPositions[i]) + float4x4.identity * math.pow(principalRadii[i].x, 2) * 0.001f) / positions[i].w;
|
||||
moments[i] -= M * BurstMath.multrnsp4(massCenters[i], prevMassCenters[i]);
|
||||
|
||||
// extract neighborhood orientation delta:
|
||||
matchingRotations[i] = BurstMath.ExtractRotation(moments[i], quaternion.identity, 5);
|
||||
|
||||
// viscosity and vorticity:
|
||||
float4 viscGoal = new float4(massCenters[i].xyz + math.rotate(matchingRotations[i], (prevPositions[i] - prevMassCenters[i]).xyz), 0);
|
||||
deltas[i] += (viscGoal - positions[i]) * fluidMaterials[i].z;
|
||||
|
||||
counts[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ApplyPositionDeltasJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<int> fluidParticles;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> normals;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> anisotropies;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<quaternion> matchingRotations;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> linearFromAngular;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
if (counts[i] > 0)
|
||||
{
|
||||
positions[i] += new float4(deltas[i].xyz,0) / counts[i];
|
||||
deltas[i] = float4.zero;
|
||||
counts[i] = 0;
|
||||
}
|
||||
|
||||
normals[i] = float4.zero;
|
||||
anisotropies[i] = float4x4.zero;
|
||||
linearFromAngular[i] = float4.zero;
|
||||
matchingRotations[i] = new quaternion(0, 0, 0, 0);
|
||||
vorticity[i] = float4.zero;
|
||||
|
||||
// zero out fluidData.z in preparation to accumulate relative velocity.
|
||||
float4 data = fluidData[i];
|
||||
data.z = 0;
|
||||
|
||||
float grad = gradientKernel.W(0, radii[i]) / invMasses[i] / restDensities[i];
|
||||
|
||||
// self particle contribution to density and gradient:
|
||||
data += new float4(densityKernel.W(0, radii[i]), 0, grad, grad * grad + data[2] * data[2]);
|
||||
|
||||
// weight by mass:
|
||||
data[0] /= invMasses[i];
|
||||
|
||||
// evaluate density constraint (clamp pressure):
|
||||
float constraint = math.max(-0.5f * surfaceTension[i], data[0] / restDensities[i] - 1);
|
||||
|
||||
// calculate lambda:
|
||||
data[1] = -constraint / (invMasses[i] * data[3] + math.FLT_MIN_NORMAL);
|
||||
|
||||
fluidData[i] = data;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ApplyAtmosphereJob : IJobParallelFor
|
||||
public struct ApplyVorticityConfinementAndAtmosphere : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float4> wind;
|
||||
[ReadOnly] public NativeArray<float4> fluidInterface;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials2;
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public NativeArray<float4> vorticities;
|
||||
[ReadOnly] public NativeArray<float> atmosphericDrag;
|
||||
[ReadOnly] public NativeArray<float> atmosphericPressure;
|
||||
[ReadOnly] public NativeArray<float> vorticityConfinement;
|
||||
[ReadOnly] public NativeArray<float> restDensities;
|
||||
[ReadOnly] public NativeArray<float4> normals;
|
||||
[ReadOnly] public NativeArray<float4> fluidData;
|
||||
[ReadOnly] public NativeArray<float4> linearFromAngular;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevPositions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> linearAccelerations;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> vorticityAccelerations;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> vorticity;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> angularDiffusion;
|
||||
[DeallocateOnJobCompletion] [ReadOnly] public NativeArray<float4> eta;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> angularVelocities;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> velocities;
|
||||
|
||||
[ReadOnly] public float dt;
|
||||
[ReadOnly] public Oni.SolverParameters solverParams;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
float restVolume = math.pow(principalRadii[i].x * 2, 3 - (int)solverParams.mode);
|
||||
|
||||
//atmospheric drag:
|
||||
float4 velocityDiff = velocities[i] - wind[i];
|
||||
|
||||
// particles near the surface should experience drag:
|
||||
velocities[i] -= fluidInterface[i].x * velocityDiff * math.max(0, 1 - fluidData[i][0] * restVolume) * dt;
|
||||
velocities[i] -= atmosphericDrag[i] * velocityDiff * math.max(0, 1 - fluidData[i][0] / restDensities[i]) * dt;
|
||||
|
||||
// ambient pressure:
|
||||
velocities[i] += fluidInterface[i].y * normals[i] * dt;
|
||||
velocities[i] += atmosphericPressure[i] * normals[i] * dt;
|
||||
|
||||
// angular accel due to baroclinity:
|
||||
angularVelocities[i] += new float4(fluidMaterials2[i].z * math.cross(-normals[i].xyz, -velocityDiff.xyz), 0) * dt;
|
||||
angularVelocities[i] -= fluidMaterials2[i].w * angularDiffusion[i].c0;
|
||||
// apply vorticity confinement:
|
||||
velocities[i] += new float4(math.cross(math.normalizesafe(eta[i]).xyz,vorticities[i].xyz), 0) * vorticityConfinement[i] * dt;
|
||||
}
|
||||
}
|
||||
|
||||
// micropolar vorticity:
|
||||
velocities[i] += fluidMaterials2[i].x * linearAccelerations[i] * dt;
|
||||
vorticity[i] += fluidMaterials2[i].x * (vorticityAccelerations[i] * 0.5f - vorticity[i]) * dt;
|
||||
vorticity[i] -= fluidMaterials2[i].y * angularDiffusion[i].c1;
|
||||
[BurstCompile]
|
||||
public struct IdentityAnisotropyJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
linearAccelerations[i] = float4.zero;
|
||||
vorticityAccelerations[i] = float4.zero;
|
||||
angularDiffusion[i] = float4x4.zero;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> principalAxes;
|
||||
|
||||
// we want to add together linear and angular velocity fields and use result to advect particles without modifying either field:
|
||||
positions[i] += new float4(linearFromAngular[i].xyz * dt,0);
|
||||
prevPositions[i] += new float4(linearFromAngular[i].xyz * dt, 0);
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
// align the principal axes of the particle with the solver axes:
|
||||
principalAxes[i * 3] = new float4(1,0,0,radii[i].x);
|
||||
principalAxes[i * 3 + 1] = new float4(0,1,0,radii[i].x);
|
||||
principalAxes[i * 3 + 2] = new float4(0,0,1,radii[i].x);
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct AverageSmoothPositionsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4x4> anisotropies;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> smoothPositions;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
var smoothPos = anisotropies[i];
|
||||
|
||||
if (smoothPos.c3.w > 0)
|
||||
smoothPos.c3 /= smoothPos.c3.w;
|
||||
if (smoothPositions[i].w > 0)
|
||||
smoothPositions[i] /= smoothPositions[i].w;
|
||||
else
|
||||
smoothPos.c3.xyz = renderablePositions[i].xyz;
|
||||
|
||||
anisotropies[i] = smoothPos;
|
||||
smoothPositions[i] = renderablePositions[i];
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct AverageAnisotropyJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeList<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public float maxAnisotropy;
|
||||
[ReadOnly] public NativeArray<float4x4> anisotropies;
|
||||
[ReadOnly] public NativeArray<float> life;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
[ReadOnly]
|
||||
[DeallocateOnJobCompletion]
|
||||
public NativeArray<float4> smoothPositions;
|
||||
|
||||
[ReadOnly]
|
||||
[DeallocateOnJobCompletion]
|
||||
public NativeArray<float3x3> anisotropies;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> renderablePositions;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<quaternion> renderableOrientations;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> renderableRadii;
|
||||
|
||||
[ReadOnly] public Oni.SolverParameters solverParams;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> principalAxes;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
if (anisotropies[i].c3.w > 0 && (anisotropies[i].c0[0] + anisotropies[i].c1[1] + anisotropies[i].c2[2]) > 0.01f)
|
||||
if (smoothPositions[i].w > 0 && (anisotropies[i].c0[0] + anisotropies[i].c1[1] + anisotropies[i].c2[2]) > 0.01f)
|
||||
{
|
||||
float3 singularValues;
|
||||
float3x3 u;
|
||||
BurstMath.EigenSolve(math.float3x3(anisotropies[i] / anisotropies[i].c3.w), out singularValues, out u);
|
||||
BurstMath.EigenSolve(anisotropies[i] / smoothPositions[i].w, out singularValues, out u);
|
||||
|
||||
float max = singularValues[0];
|
||||
float3 s = math.max(singularValues,new float3(max / maxAnisotropy)) / max * principalRadii[i].x;
|
||||
|
||||
renderableOrientations[i] = quaternion.LookRotationSafe(u.c2,u.c1);
|
||||
renderableRadii[i] = new float4(s.xyz,1);
|
||||
principalAxes[i * 3] = new float4(u.c0, s.x);
|
||||
principalAxes[i * 3 + 1] = new float4(u.c1, s.y);
|
||||
principalAxes[i * 3 + 2] = new float4(u.c2, s.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
float radius = principalRadii[i].x / maxAnisotropy;
|
||||
renderableOrientations[i] = quaternion.identity;
|
||||
renderableRadii[i] = new float4(radius,radius,radius,1);
|
||||
|
||||
float4 data = fluidData[i];
|
||||
data.x = 1 / math.pow(math.abs(radius * 2), 3 - (int)solverParams.mode); // normal volume of an isolated particle.
|
||||
fluidData[i] = data;
|
||||
principalAxes[i * 3] = new float4(1, 0, 0, radius);
|
||||
principalAxes[i * 3 + 1] = new float4(0, 1, 0, radius);
|
||||
principalAxes[i * 3 + 2] = new float4(0, 0, 1, radius);
|
||||
}
|
||||
|
||||
renderablePositions[i] = math.lerp(renderablePositions[i], anisotropies[i].c3, math.min((maxAnisotropy - 1)/3.0f,1));
|
||||
|
||||
// inactive particles have radii.w == 0, set it right away for particles killed during this frame
|
||||
// to keep them from being rendered during this frame instead of waiting to do it at the start of next sim step:
|
||||
float4 radii = renderableRadii[i];
|
||||
radii.w = life[i] <= 0 ? 0 : radii.w;
|
||||
renderableRadii[i] = radii;
|
||||
renderablePositions[i] = smoothPositions[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#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
|
||||
{
|
||||
@@ -17,12 +19,12 @@ namespace Obi
|
||||
m_ConstraintType = Oni.ConstraintType.Density;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
|
||||
// update densities and gradients:
|
||||
@@ -30,16 +32,13 @@ namespace Obi
|
||||
{
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
positions = solverImplementation.positions,
|
||||
prevPositions = solverImplementation.prevPositions,
|
||||
principalRadii = solverImplementation.principalRadii,
|
||||
fluidMaterials = solverImplementation.fluidMaterials,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
restDensities = solverImplementation.restDensities,
|
||||
diffusion = solverImplementation.diffusion,
|
||||
userData = solverImplementation.userData,
|
||||
fluidData = solverImplementation.fluidData,
|
||||
moments = solverImplementation.anisotropies,
|
||||
massCenters = solverImplementation.normals,
|
||||
prevMassCenters = solverImplementation.renderablePositions,
|
||||
densityKernel = new Poly6Kernel(solverAbstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
batchData = batchData,
|
||||
solverParams = solverAbstraction.parameters
|
||||
dt = substepTime
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
@@ -53,14 +52,15 @@ namespace Obi
|
||||
// update densities and gradients:
|
||||
var apply = new ApplyDensityConstraintsJob()
|
||||
{
|
||||
principalRadii = solverImplementation.principalRadii,
|
||||
fluidMaterials = solverImplementation.fluidMaterials,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
radii = solverImplementation.smoothingRadii,
|
||||
restDensities = solverImplementation.restDensities,
|
||||
surfaceTension = solverImplementation.surfaceTension,
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
densityKernel = new Poly6Kernel(solverAbstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
positions = solverImplementation.positions,
|
||||
fluidData = solverImplementation.fluidData,
|
||||
batchData = batchData,
|
||||
solverParams = solverAbstraction.parameters,
|
||||
sorFactor = parameters.SORFactor
|
||||
};
|
||||
|
||||
@@ -68,54 +68,36 @@ namespace Obi
|
||||
return apply.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public JobHandle CalculateNormals(JobHandle inputDeps, float deltaTime)
|
||||
public JobHandle CalculateViscosityAndNormals(JobHandle inputDeps, float deltaTime)
|
||||
{
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
|
||||
var vorticity = new NormalsJob()
|
||||
var viscosity = new NormalsViscosityAndVorticityJob()
|
||||
{
|
||||
invMasses = solverImplementation.invMasses,
|
||||
positions = solverImplementation.positions,
|
||||
principalRadii = solverImplementation.principalRadii,
|
||||
fluidMaterials = solverImplementation.fluidMaterials,
|
||||
fluidMaterials2 = solverImplementation.fluidMaterials2,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
radii = solverImplementation.smoothingRadii,
|
||||
restDensities = solverImplementation.restDensities,
|
||||
viscosities = solverImplementation.viscosities,
|
||||
fluidData = solverImplementation.fluidData,
|
||||
fluidInterface = solverImplementation.fluidInterface,
|
||||
velocities = solverImplementation.velocities,
|
||||
angularVelocities = solverImplementation.angularVelocities,
|
||||
|
||||
vorticityAccelerations = solverImplementation.orientationDeltas.Reinterpret<float4>(),
|
||||
vorticity = solverImplementation.restOrientations.Reinterpret<float4>(),
|
||||
linearAccelerations = solverImplementation.positionDeltas,
|
||||
linearFromAngular = solverImplementation.restPositions,
|
||||
angularDiffusion = solverImplementation.anisotropies,
|
||||
|
||||
userData = solverImplementation.userData,
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
velocities = solverImplementation.velocities,
|
||||
vorticities = solverImplementation.vorticities,
|
||||
normals = solverImplementation.normals,
|
||||
densityKernel = new Poly6Kernel(solverAbstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
gradKernel = new SpikyKernel(solverAbstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
solverParams = solverAbstraction.parameters,
|
||||
batchData = batchData,
|
||||
dt = deltaTime,
|
||||
batchData = batchData
|
||||
};
|
||||
|
||||
return vorticity.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return viscosity.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public JobHandle ViscosityAndVorticity(JobHandle inputDeps)
|
||||
public JobHandle CalculateVorticity(JobHandle inputDeps)
|
||||
{
|
||||
var eta = new ViscosityVorticityJob()
|
||||
var eta = new CalculateVorticityEta()
|
||||
{
|
||||
positions = solverImplementation.positions,
|
||||
prevPositions = solverImplementation.prevPositions,
|
||||
matchingRotations = solverImplementation.restPositions.Reinterpret<quaternion>(),
|
||||
invMasses = solverImplementation.invMasses,
|
||||
restDensities = solverImplementation.restDensities,
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
massCenters = solverImplementation.normals,
|
||||
prevMassCenters = solverImplementation.renderablePositions,
|
||||
fluidParams = solverImplementation.fluidMaterials,
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
vorticities = solverImplementation.vorticities,
|
||||
eta = ((BurstDensityConstraints)this.constraints).eta,
|
||||
batchData = batchData
|
||||
};
|
||||
|
||||
@@ -128,8 +110,8 @@ namespace Obi
|
||||
var accumulateSmooth = new AccumulateSmoothPositionsJob()
|
||||
{
|
||||
renderablePositions = solverImplementation.renderablePositions,
|
||||
anisotropies = solverImplementation.anisotropies,
|
||||
fluidMaterials = solverImplementation.fluidMaterials,
|
||||
smoothPositions = ((BurstDensityConstraints)this.constraints).smoothPositions,
|
||||
radii = solverImplementation.smoothingRadii,
|
||||
densityKernel = new Poly6Kernel(solverAbstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
batchData = batchData
|
||||
@@ -144,7 +126,8 @@ namespace Obi
|
||||
var accumulateAnisotropy = new AccumulateAnisotropyJob()
|
||||
{
|
||||
renderablePositions = solverImplementation.renderablePositions,
|
||||
anisotropies = solverImplementation.anisotropies,
|
||||
smoothPositions = ((BurstDensityConstraints)this.constraints).smoothPositions,
|
||||
anisotropies = ((BurstDensityConstraints)this.constraints).anisotropies,
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
batchData = batchData
|
||||
};
|
||||
@@ -157,174 +140,16 @@ namespace Obi
|
||||
public struct UpdateDensitiesJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials;
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> moments;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> massCenters;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevMassCenters;
|
||||
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
[ReadOnly] public BatchData batchData;
|
||||
|
||||
[ReadOnly] public Oni.SolverParameters solverParams;
|
||||
|
||||
public void Execute(int workItemIndex)
|
||||
{
|
||||
int start, end;
|
||||
batchData.GetConstraintRange(workItemIndex, out start, out end);
|
||||
|
||||
for (int i = start; i < end; ++i)
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
float restVolumeA = math.pow(principalRadii[pair.particleA].x * 2, 3 - (int)solverParams.mode);
|
||||
float restVolumeB = math.pow(principalRadii[pair.particleB].x * 2, 3 - (int)solverParams.mode);
|
||||
|
||||
float gradA = restVolumeB * pair.avgGradient;
|
||||
float gradB = restVolumeA * pair.avgGradient;
|
||||
|
||||
float vA = restVolumeB / restVolumeA;
|
||||
float vB = restVolumeA / restVolumeB;
|
||||
|
||||
// accumulate pbf data (density, gradients):
|
||||
fluidData[pair.particleA] += new float4(vA * pair.avgKernel, 0, gradA, gradA * gradA);
|
||||
fluidData[pair.particleB] += new float4(vB * pair.avgKernel, 0, gradB, gradB * gradB);
|
||||
|
||||
// accumulate masses for COMs and moment matrices:
|
||||
float wAvg = pair.avgKernel / ((densityKernel.W(0, fluidMaterials[pair.particleA].x) + densityKernel.W(0, fluidMaterials[pair.particleB].x)) * 0.5f);
|
||||
|
||||
massCenters[pair.particleA] += wAvg * new float4(positions[pair.particleB].xyz, 1) / positions[pair.particleB].w;
|
||||
massCenters[pair.particleB] += wAvg * new float4(positions[pair.particleA].xyz, 1) / positions[pair.particleA].w;
|
||||
|
||||
prevMassCenters[pair.particleA] += wAvg * new float4(prevPositions[pair.particleB].xyz, 1) / positions[pair.particleB].w;
|
||||
prevMassCenters[pair.particleB] += wAvg * new float4(prevPositions[pair.particleA].xyz, 1) / positions[pair.particleA].w;
|
||||
|
||||
moments[pair.particleA] += wAvg * (BurstMath.multrnsp4(positions[pair.particleB], prevPositions[pair.particleB]) + float4x4.identity * math.pow(principalRadii[pair.particleB].x, 2) * 0.001f) / positions[pair.particleB].w;
|
||||
moments[pair.particleB] += wAvg * (BurstMath.multrnsp4(positions[pair.particleA], prevPositions[pair.particleA]) + float4x4.identity * math.pow(principalRadii[pair.particleA].x, 2) * 0.001f) / positions[pair.particleA].w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ApplyDensityConstraintsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials;
|
||||
[ReadOnly] public NativeArray<FluidInteraction> pairs;
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
[ReadOnly] public CohesionKernel cohesionKernel;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
[ReadOnly] public float sorFactor;
|
||||
[ReadOnly] public Oni.SolverParameters solverParams;
|
||||
|
||||
public void Execute(int workItemIndex)
|
||||
{
|
||||
int start, end;
|
||||
batchData.GetConstraintRange(workItemIndex, out start, out end);
|
||||
|
||||
for (int i = start; i < end; ++i)
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
float restVolumeA = math.pow(principalRadii[pair.particleA].x * 2, 3 - (int)solverParams.mode);
|
||||
float restVolumeB = math.pow(principalRadii[pair.particleB].x * 2, 3 - (int)solverParams.mode);
|
||||
|
||||
float dist = math.length(positions[pair.particleA].xyz - positions[pair.particleB].xyz); // TODO: FIX! we cant read positions while we are writing to them.
|
||||
|
||||
// calculate tensile instability correction factor:
|
||||
float cAvg = (cohesionKernel.W(dist, fluidMaterials[pair.particleA].x * 1.4f) + cohesionKernel.W(dist, fluidMaterials[pair.particleB].x * 1.4f)) * 0.5f;
|
||||
float st = 0.2f * cAvg * (1 - math.saturate(math.abs(fluidMaterials[pair.particleA].y - fluidMaterials[pair.particleB].y))) * (fluidMaterials[pair.particleA].y + fluidMaterials[pair.particleB].y) * 0.5f;
|
||||
float scorrA = -st / (positions[pair.particleA].w * fluidData[pair.particleA][3] + math.FLT_MIN_NORMAL);
|
||||
float scorrB = -st / (positions[pair.particleB].w * fluidData[pair.particleB][3] + math.FLT_MIN_NORMAL);
|
||||
|
||||
// calculate position delta:
|
||||
float4 delta = pair.gradient * pair.avgGradient * ((fluidData[pair.particleA][1] + scorrA) * restVolumeB + (fluidData[pair.particleB][1] + scorrB) * restVolumeA) * sorFactor;
|
||||
delta.w = 0;
|
||||
positions[pair.particleA] += delta * positions[pair.particleA].w;
|
||||
positions[pair.particleB] -= delta * positions[pair.particleB].w;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ViscosityVorticityJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<quaternion> matchingRotations;
|
||||
[ReadOnly] public NativeArray<float4> fluidParams;
|
||||
[ReadOnly] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> massCenters;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevMassCenters;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
|
||||
public void Execute(int workItemIndex)
|
||||
{
|
||||
int start, end;
|
||||
batchData.GetConstraintRange(workItemIndex, out start, out end);
|
||||
|
||||
for (int i = start; i < end; ++i)
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
float visc = math.min(fluidParams[pair.particleA].z, fluidParams[pair.particleB].z);
|
||||
|
||||
// viscosity:
|
||||
float4 goalA = new float4(massCenters[pair.particleB].xyz + math.rotate(matchingRotations[pair.particleB], (prevPositions[pair.particleA] - prevMassCenters[pair.particleB]).xyz), 0);
|
||||
float4 goalB = new float4(massCenters[pair.particleA].xyz + math.rotate(matchingRotations[pair.particleA], (prevPositions[pair.particleB] - prevMassCenters[pair.particleA]).xyz), 0);
|
||||
deltas[pair.particleA] += (goalA - positions[pair.particleA]) * visc;
|
||||
deltas[pair.particleB] += (goalB - positions[pair.particleB]) * visc;
|
||||
|
||||
counts[pair.particleA]++;
|
||||
counts[pair.particleB]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct NormalsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> angularVelocities;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> vorticity;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials2;
|
||||
[ReadOnly] public NativeArray<float4> fluidInterface;
|
||||
[ReadOnly] public NativeArray<float> restDensities;
|
||||
[ReadOnly] public NativeArray<float> diffusion;
|
||||
[ReadOnly] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> userData;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> normals;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> linearAccelerations;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> vorticityAccelerations;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> linearFromAngular;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> angularDiffusion;
|
||||
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
[ReadOnly] public SpikyKernel gradKernel;
|
||||
[ReadOnly] public BatchData batchData;
|
||||
[ReadOnly] public Oni.SolverParameters solverParams;
|
||||
|
||||
[ReadOnly] public float dt;
|
||||
|
||||
public void Execute(int workItemIndex)
|
||||
@@ -336,70 +161,143 @@ namespace Obi
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
float restVolumeA = math.pow(principalRadii[pair.particleA].x * 2, 3 - (int)solverParams.mode);
|
||||
float restVolumeB = math.pow(principalRadii[pair.particleB].x * 2, 3 - (int)solverParams.mode);
|
||||
float restVolumeA = 1.0f / invMasses[pair.particleA] / restDensities[pair.particleA];
|
||||
float restVolumeB = 1.0f / invMasses[pair.particleB] / restDensities[pair.particleB];
|
||||
|
||||
float invDensityA = invMasses[pair.particleA] / fluidData[pair.particleA].x;
|
||||
float invDensityB = invMasses[pair.particleB] / fluidData[pair.particleB].x;
|
||||
float gradA = restVolumeB * pair.avgGradient;
|
||||
float gradB = restVolumeA * pair.avgGradient;
|
||||
|
||||
float3 relVel = velocities[pair.particleA].xyz - velocities[pair.particleB].xyz;
|
||||
float3 relAng = angularVelocities[pair.particleA].xyz - angularVelocities[pair.particleB].xyz;
|
||||
float3 relVort = vorticity[pair.particleA].xyz - vorticity[pair.particleB].xyz;
|
||||
float4 d = new float4((positions[pair.particleA] - positions[pair.particleB]).xyz,0);
|
||||
float dist = math.length(d);
|
||||
float vA = restVolumeB / restVolumeA;
|
||||
float vB = restVolumeA / restVolumeB;
|
||||
|
||||
float avgGrad = (gradKernel.W(dist, fluidMaterials[pair.particleA].x) +
|
||||
gradKernel.W(dist, fluidMaterials[pair.particleB].x)) * 0.5f;
|
||||
float avgKern = (densityKernel.W(dist, fluidMaterials[pair.particleA].x) +
|
||||
densityKernel.W(dist, fluidMaterials[pair.particleB].x)) * 0.5f;
|
||||
float avgNorm = (densityKernel.W(0, fluidMaterials[pair.particleA].x) +
|
||||
densityKernel.W(0, fluidMaterials[pair.particleB].x)) * 0.5f;
|
||||
// accumulate pbf data (density, gradients):
|
||||
fluidData[pair.particleA] += new float4(vA * pair.avgKernel, 0, gradA, gradA * gradA);
|
||||
fluidData[pair.particleB] += new float4(vB * pair.avgKernel, 0, gradB, gradB * gradB);
|
||||
|
||||
// property diffusion:
|
||||
float diffusionSpeed = (fluidInterface[pair.particleA].w + fluidInterface[pair.particleB].w) * avgKern * dt;
|
||||
float4 userDelta = (userData[pair.particleB] - userData[pair.particleA]) * solverParams.diffusionMask * diffusionSpeed;
|
||||
userData[pair.particleA] += restVolumeB / restVolumeA * userDelta;
|
||||
userData[pair.particleB] -= restVolumeA / restVolumeB * userDelta;
|
||||
float diffusionSpeed = (diffusion[pair.particleA] + diffusion[pair.particleB]) * pair.avgKernel * dt;
|
||||
float4 userDelta = (userData[pair.particleB] - userData[pair.particleA]) * diffusionSpeed;
|
||||
userData[pair.particleA] += vA * userDelta;
|
||||
userData[pair.particleB] -= vB * userDelta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ApplyDensityConstraintsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> radii;
|
||||
[ReadOnly] public NativeArray<float> restDensities;
|
||||
[ReadOnly] public NativeArray<float> surfaceTension;
|
||||
[ReadOnly] public NativeArray<FluidInteraction> pairs;
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
[ReadOnly] public float sorFactor;
|
||||
|
||||
public void Execute(int workItemIndex)
|
||||
{
|
||||
int start, end;
|
||||
batchData.GetConstraintRange(workItemIndex, out start, out end);
|
||||
|
||||
for (int i = start; i < end; ++i)
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
float restVolumeA = 1.0f / invMasses[pair.particleA] / restDensities[pair.particleA];
|
||||
float restVolumeB = 1.0f / invMasses[pair.particleB] / restDensities[pair.particleB];
|
||||
|
||||
// calculate tensile instability correction factor:
|
||||
float wAvg = pair.avgKernel / ((densityKernel.W(0, radii[pair.particleA]) + densityKernel.W(0, radii[pair.particleB])) * 0.5f);
|
||||
float scorrA = -(0.001f + 0.2f * surfaceTension[pair.particleA]) * wAvg / (invMasses[pair.particleA] * fluidData[pair.particleA][3]);
|
||||
float scorrB = -(0.001f + 0.2f * surfaceTension[pair.particleB]) * wAvg / (invMasses[pair.particleB] * fluidData[pair.particleB][3]);
|
||||
|
||||
// calculate position delta:
|
||||
float4 delta = pair.gradient * pair.avgGradient * ((fluidData[pair.particleA][1] + scorrA) * restVolumeB + (fluidData[pair.particleB][1] + scorrB) * restVolumeA) * sorFactor;
|
||||
positions[pair.particleA] += delta * invMasses[pair.particleA];
|
||||
positions[pair.particleB] -= delta * invMasses[pair.particleB];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct NormalsViscosityAndVorticityJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> radii;
|
||||
[ReadOnly] public NativeArray<float> restDensities;
|
||||
[ReadOnly] public NativeArray<float> viscosities;
|
||||
[ReadOnly] public NativeArray<float4> fluidData;
|
||||
[ReadOnly] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> velocities;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> vorticities;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> normals;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
|
||||
public void Execute(int workItemIndex)
|
||||
{
|
||||
int start, end;
|
||||
batchData.GetConstraintRange(workItemIndex, out start, out end);
|
||||
|
||||
for (int i = start; i < end; ++i)
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
float restVolumeA = 1.0f / invMasses[pair.particleA] / restDensities[pair.particleA];
|
||||
float restVolumeB = 1.0f / invMasses[pair.particleB] / restDensities[pair.particleB];
|
||||
|
||||
// XSPH viscosity:
|
||||
float viscosityCoeff = math.min(viscosities[pair.particleA], viscosities[pair.particleB]);
|
||||
float4 relVelocity = velocities[pair.particleB] - velocities[pair.particleA];
|
||||
float4 viscosity = viscosityCoeff * relVelocity * pair.avgKernel;
|
||||
velocities[pair.particleA] += viscosity * restVolumeB;
|
||||
velocities[pair.particleB] -= viscosity * restVolumeA;
|
||||
|
||||
// calculate vorticity:
|
||||
float4 vgrad = pair.gradient * pair.avgGradient;
|
||||
float4 vorticity = new float4(math.cross(relVelocity.xyz,vgrad.xyz),0);
|
||||
vorticities[pair.particleA] += vorticity * restVolumeB;
|
||||
vorticities[pair.particleB] += vorticity * restVolumeA;
|
||||
|
||||
// calculate color field normal:
|
||||
float4 normGrad = d / (dist + BurstMath.epsilon);
|
||||
float4 vgrad = normGrad * avgGrad;
|
||||
float radius = (fluidMaterials[pair.particleA].x + fluidMaterials[pair.particleB].x) * 0.5f;
|
||||
normals[pair.particleA] += vgrad * radius * restVolumeB;
|
||||
normals[pair.particleB] -= vgrad * radius * restVolumeA;
|
||||
float radius = (radii[pair.particleA] + radii[pair.particleB]) * 0.5f;
|
||||
normals[pair.particleA] += vgrad * radius / invMasses[pair.particleB] / fluidData[pair.particleB][0];
|
||||
normals[pair.particleB] -= vgrad * radius / invMasses[pair.particleA] / fluidData[pair.particleA][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// measure relative velocity for foam generation:
|
||||
float4 dataA = fluidData[pair.particleA];
|
||||
float4 dataB = fluidData[pair.particleB];
|
||||
float relVelMag = math.length(relVel) + BurstMath.epsilon;
|
||||
float avgVelDiffKernel = 1 - math.min(1, dist / (radius + BurstMath.epsilon));
|
||||
float rv = relVelMag * (1 - math.dot(relVel / relVelMag, normGrad.xyz)) * avgVelDiffKernel;
|
||||
dataA.z += rv;
|
||||
dataB.z += rv;
|
||||
fluidData[pair.particleA] = dataA;
|
||||
fluidData[pair.particleB] = dataB;
|
||||
[BurstCompile]
|
||||
public struct CalculateVorticityEta : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> vorticities;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> restDensities;
|
||||
|
||||
// micropolar: curl of linear/angular velocity:
|
||||
float3 velCross = math.cross(relVel, vgrad.xyz);
|
||||
float3 vortCross = math.cross(relVort, vgrad.xyz);
|
||||
linearAccelerations[pair.particleA] += new float4(vortCross / invMasses[pair.particleB] * invDensityA, 0);
|
||||
linearAccelerations[pair.particleB] += new float4(vortCross / invMasses[pair.particleA] * invDensityB, 0);
|
||||
vorticityAccelerations[pair.particleA] += new float4(velCross / invMasses[pair.particleB] * invDensityA, 0);
|
||||
vorticityAccelerations[pair.particleB] += new float4(velCross / invMasses[pair.particleA] * invDensityB, 0);
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<FluidInteraction> pairs;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> eta;
|
||||
|
||||
// angular diffusion:
|
||||
float4x4 diffA = angularDiffusion[pair.particleA];
|
||||
float4x4 diffB = angularDiffusion[pair.particleB];
|
||||
diffA.c0 += new float4(relAng * avgKern / invMasses[pair.particleB] * invDensityA, 0);
|
||||
diffB.c0 -= new float4(relAng * avgKern / invMasses[pair.particleA] * invDensityB, 0);
|
||||
diffA.c1 += new float4(relVort * avgKern / invMasses[pair.particleB] * invDensityA, 0);
|
||||
diffB.c1 -= new float4(relVort * avgKern / invMasses[pair.particleA] * invDensityB, 0);
|
||||
angularDiffusion[pair.particleA] = diffA;
|
||||
angularDiffusion[pair.particleB] = diffB;
|
||||
[ReadOnly] public BatchData batchData;
|
||||
|
||||
// linear velocity due to baroclinity:
|
||||
linearFromAngular[pair.particleA] += new float4(math.cross(angularVelocities[pair.particleB].xyz, d.xyz) * avgKern / avgNorm, 0);
|
||||
linearFromAngular[pair.particleB] -= new float4(math.cross(angularVelocities[pair.particleA].xyz, d.xyz) * avgKern / avgNorm, 0);
|
||||
public void Execute(int workItemIndex)
|
||||
{
|
||||
int start, end;
|
||||
batchData.GetConstraintRange(workItemIndex, out start, out end);
|
||||
|
||||
for (int i = start; i < end; ++i)
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
float4 vgrad = pair.gradient * pair.avgGradient;
|
||||
eta[pair.particleA] += math.length(vorticities[pair.particleA]) * vgrad / invMasses[pair.particleB] / restDensities[pair.particleB];
|
||||
eta[pair.particleB] -= math.length(vorticities[pair.particleB]) * vgrad / invMasses[pair.particleA] / restDensities[pair.particleA];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,10 +306,10 @@ namespace Obi
|
||||
public struct AccumulateSmoothPositionsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials;
|
||||
[ReadOnly] public NativeArray<float> radii;
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4x4> anisotropies;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> smoothPositions;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
@@ -425,19 +323,14 @@ namespace Obi
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
float distance = math.length((renderablePositions[pair.particleA] - renderablePositions[pair.particleB]).xyz);
|
||||
float4 gradient = (renderablePositions[pair.particleA] - renderablePositions[pair.particleB]);
|
||||
float distance = math.length(gradient);
|
||||
|
||||
pair.avgKernel = (densityKernel.W(distance, fluidMaterials[pair.particleA].x) +
|
||||
densityKernel.W(distance, fluidMaterials[pair.particleB].x)) * 0.5f;
|
||||
pair.avgKernel = (densityKernel.W(distance, radii[pair.particleA]) +
|
||||
densityKernel.W(distance, radii[pair.particleB])) * 0.5f;
|
||||
|
||||
var A = anisotropies[pair.particleA];
|
||||
var B = anisotropies[pair.particleB];
|
||||
|
||||
A.c3 += new float4(renderablePositions[pair.particleB].xyz,1) * pair.avgKernel;
|
||||
B.c3 += new float4(renderablePositions[pair.particleA].xyz,1) * pair.avgKernel;
|
||||
|
||||
anisotropies[pair.particleA] = A;
|
||||
anisotropies[pair.particleB] = B;
|
||||
smoothPositions[pair.particleA] += new float4(renderablePositions[pair.particleB].xyz,1) * pair.avgKernel;
|
||||
smoothPositions[pair.particleB] += new float4(renderablePositions[pair.particleA].xyz,1) * pair.avgKernel;
|
||||
|
||||
pairs[i] = pair;
|
||||
}
|
||||
@@ -448,9 +341,10 @@ namespace Obi
|
||||
public struct AccumulateAnisotropyJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
[ReadOnly] public NativeArray<float4> smoothPositions;
|
||||
[ReadOnly] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4x4> anisotropies;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float3x3> anisotropies;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
|
||||
@@ -463,15 +357,15 @@ namespace Obi
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
float4 distanceA = (renderablePositions[pair.particleB] - anisotropies[pair.particleA].c3) * pair.avgKernel;
|
||||
float4 distanceB = (renderablePositions[pair.particleA] - anisotropies[pair.particleB].c3) * pair.avgKernel;
|
||||
float4 distanceA = renderablePositions[pair.particleB] - smoothPositions[pair.particleA];
|
||||
float4 distanceB = renderablePositions[pair.particleA] - smoothPositions[pair.particleB];
|
||||
|
||||
anisotropies[pair.particleA] += BurstMath.multrnsp4(distanceA,distanceA);
|
||||
anisotropies[pair.particleB] += BurstMath.multrnsp4(distanceB,distanceB);
|
||||
anisotropies[pair.particleA] += BurstMath.multrnsp(distanceA,distanceA) * pair.avgKernel;
|
||||
anisotropies[pair.particleB] += BurstMath.multrnsp(distanceB,distanceB) * pair.avgKernel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,15 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct CohesionKernel
|
||||
{
|
||||
public float W(float r, float h)
|
||||
{
|
||||
return math.cos(math.min(r, h) * 3 * math.PI / (2 * h));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -39,7 +39,7 @@ namespace Obi
|
||||
applyConstraints.particleIndices = this.particleIndices;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
projectConstraints.positions = solverImplementation.positions;
|
||||
projectConstraints.invMasses = solverImplementation.invMasses;
|
||||
@@ -62,7 +62,7 @@ namespace Obi
|
||||
return applyConstraints.Schedule(m_ConstraintCount, 64, inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
[BurstCompile]
|
||||
public struct DistanceConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
|
||||
@@ -25,7 +25,9 @@ namespace Obi
|
||||
|
||||
public override int GetConstraintCount()
|
||||
{
|
||||
return ((BurstSolverImpl)solver).abstraction.particleContacts.count;
|
||||
if (!((BurstSolverImpl)solver).particleContacts.IsCreated)
|
||||
return 0;
|
||||
return ((BurstSolverImpl)solver).particleContacts.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Obi
|
||||
this.batchData = batchData;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var updateContacts = new UpdateParticleContactsJob()
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace Obi
|
||||
velocities = solverImplementation.velocities,
|
||||
radii = solverImplementation.principalRadii,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
invRotationalMasses = solverImplementation.invRotationalMasses,
|
||||
invInertiaTensors = solverImplementation.invInertiaTensors,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
@@ -41,8 +41,7 @@ namespace Obi
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
|
||||
contacts = ((BurstSolverImpl)constraints.solver).abstraction.particleContacts.AsNativeArray<BurstContact>(),
|
||||
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.particleContactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).particleContacts,
|
||||
batchData = batchData
|
||||
};
|
||||
|
||||
@@ -50,7 +49,7 @@ namespace Obi
|
||||
return updateContacts.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
@@ -61,7 +60,6 @@ namespace Obi
|
||||
invMasses = solverImplementation.invMasses,
|
||||
radii = solverImplementation.principalRadii,
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
fluidInterface = solverImplementation.fluidInterface,
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
@@ -69,9 +67,7 @@ namespace Obi
|
||||
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
userData = solverImplementation.userData,
|
||||
contacts = solverAbstraction.particleContacts.AsNativeArray<BurstContact>(),
|
||||
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.particleContactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).particleContacts,
|
||||
batchData = batchData,
|
||||
|
||||
constraintParameters = parameters,
|
||||
@@ -90,7 +86,7 @@ namespace Obi
|
||||
|
||||
var applyConstraints = new ApplyBatchedCollisionConstraintsBatchJob()
|
||||
{
|
||||
contacts = solverAbstraction.particleContacts.AsNativeArray<BurstContact>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).particleContacts,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
@@ -121,7 +117,7 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> invRotationalMasses;
|
||||
[ReadOnly] public NativeArray<float4> invInertiaTensors;
|
||||
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
@@ -130,7 +126,6 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<ContactEffectiveMasses> effectiveMasses;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstContact> contacts;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
@@ -143,7 +138,6 @@ namespace Obi
|
||||
for (int i = start; i < end; ++i)
|
||||
{
|
||||
var contact = contacts[i];
|
||||
var efMasses = effectiveMasses[i];
|
||||
|
||||
int simplexStartA = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSizeA);
|
||||
int simplexStartB = simplexCounts.GetSimplexStartAndSize(contact.bodyB, out int simplexSizeB);
|
||||
@@ -153,14 +147,14 @@ namespace Obi
|
||||
quaternion simplexPrevOrientationA = new quaternion(0, 0, 0, 0);
|
||||
float simplexRadiusA = 0;
|
||||
float simplexInvMassA = 0;
|
||||
float simplexInvRotationalMassA = 0;
|
||||
float4 simplexInvInertiaA = float4.zero;
|
||||
|
||||
float4 simplexVelocityB = float4.zero;
|
||||
float4 simplexPrevPositionB = float4.zero;
|
||||
quaternion simplexPrevOrientationB = new quaternion(0, 0, 0, 0);
|
||||
float simplexRadiusB = 0;
|
||||
float simplexInvMassB = 0;
|
||||
float simplexInvRotationalMassB = 0;
|
||||
float4 simplexInvInertiaB = float4.zero;
|
||||
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
{
|
||||
@@ -169,7 +163,7 @@ namespace Obi
|
||||
simplexPrevPositionA += prevPositions[particleIndex] * contact.pointA[j];
|
||||
simplexPrevOrientationA.value += prevOrientations[particleIndex].value * contact.pointA[j];
|
||||
simplexInvMassA += invMasses[particleIndex] * contact.pointA[j];
|
||||
simplexInvRotationalMassA += invRotationalMasses[particleIndex] * contact.pointA[j];
|
||||
simplexInvInertiaA += invInertiaTensors[particleIndex] * contact.pointA[j];
|
||||
simplexRadiusA += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
|
||||
}
|
||||
|
||||
@@ -180,21 +174,20 @@ namespace Obi
|
||||
simplexPrevPositionB += prevPositions[particleIndex] * contact.pointB[j];
|
||||
simplexPrevOrientationB.value += prevOrientations[particleIndex].value * contact.pointB[j];
|
||||
simplexInvMassB += invMasses[particleIndex] * contact.pointB[j];
|
||||
simplexInvRotationalMassB += invRotationalMasses[particleIndex] * contact.pointB[j];
|
||||
simplexInvInertiaB += invInertiaTensors[particleIndex] * contact.pointB[j];
|
||||
simplexRadiusB += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointB[j];
|
||||
}
|
||||
|
||||
simplexPrevPositionA.w = 0;
|
||||
simplexPrevPositionB.w = 0;
|
||||
|
||||
// update contact distance
|
||||
float4 contactPointA = simplexPrevPositionA - contact.normal * simplexRadiusA;
|
||||
float4 contactPointB = simplexPrevPositionB + contact.normal * simplexRadiusB;
|
||||
float dAB = math.dot(simplexPrevPositionA - simplexPrevPositionB, contact.normal);
|
||||
contact.distance = dAB - (simplexRadiusA + simplexRadiusB);
|
||||
|
||||
contact.distance = math.dot(contactPointA - contactPointB, contact.normal);
|
||||
// calculate contact points:
|
||||
float4 contactPointA = simplexPrevPositionB + contact.normal * (contact.distance + simplexRadiusB);
|
||||
float4 contactPointB = simplexPrevPositionA - contact.normal * (contact.distance + simplexRadiusA);
|
||||
|
||||
// update contact basis:
|
||||
contact.CalculateTangent(simplexVelocityA - simplexVelocityB);
|
||||
contact.CalculateBasis(simplexVelocityA - simplexVelocityB);
|
||||
|
||||
// update contact masses:
|
||||
int aMaterialIndex = particleMaterialIndices[simplices[simplexStartA]];
|
||||
@@ -202,14 +195,10 @@ namespace Obi
|
||||
bool rollingContacts = (aMaterialIndex >= 0 ? collisionMaterials[aMaterialIndex].rollingContacts > 0 : false) |
|
||||
(bMaterialIndex >= 0 ? collisionMaterials[bMaterialIndex].rollingContacts > 0 : false);
|
||||
|
||||
float4 invInertiaTensorA = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadiusA, simplexInvRotationalMassA) + new float4(BurstMath.epsilon));
|
||||
float4 invInertiaTensorB = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadiusB, simplexInvRotationalMassB) + new float4(BurstMath.epsilon));
|
||||
|
||||
efMasses.CalculateContactMassesA(simplexInvMassA, invInertiaTensorA, simplexPrevPositionA, simplexPrevOrientationA, contactPointA, contact.normal, contact.tangent, contact.bitangent, rollingContacts);
|
||||
efMasses.CalculateContactMassesB(simplexInvMassB, invInertiaTensorB, simplexPrevPositionB, simplexPrevOrientationB, contactPointB, contact.normal, contact.tangent, contact.bitangent, rollingContacts);
|
||||
contact.CalculateContactMassesA(simplexInvMassA, simplexInvInertiaA, simplexPrevPositionA, simplexPrevOrientationA, contactPointA, rollingContacts);
|
||||
contact.CalculateContactMassesB(simplexInvMassB, simplexInvInertiaB, simplexPrevPositionB, simplexPrevOrientationB, contactPointB, rollingContacts);
|
||||
|
||||
contacts[i] = contact;
|
||||
effectiveMasses[i] = efMasses;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,7 +210,6 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<float4> fluidInterface;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
|
||||
// simplex arrays:
|
||||
@@ -230,10 +218,8 @@ namespace Obi
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> userData;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstContact> contacts;
|
||||
[ReadOnly] public NativeArray<ContactEffectiveMasses> effectiveMasses;
|
||||
|
||||
[ReadOnly] public Oni.ConstraintParameters constraintParameters;
|
||||
[ReadOnly] public Oni.SolverParameters solverParameters;
|
||||
@@ -254,42 +240,34 @@ namespace Obi
|
||||
int simplexStartA = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSizeA);
|
||||
int simplexStartB = simplexCounts.GetSimplexStartAndSize(contact.bodyB, out int simplexSizeB);
|
||||
|
||||
|
||||
// Combine collision materials:
|
||||
BurstCollisionMaterial material = CombineCollisionMaterials(simplices[simplexStartA], simplices[simplexStartB]);
|
||||
|
||||
float4 simplexPositionA = float4.zero, simplexPositionB = float4.zero;
|
||||
float simplexRadiusA = 0, simplexRadiusB = 0;
|
||||
float4 simplexUserDataA = float4.zero, simplexUserDataB = float4.zero;
|
||||
float miscibility = 0;
|
||||
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartA + j];
|
||||
simplexPositionA += positions[particleIndex] * contact.pointA[j];
|
||||
simplexRadiusA += BurstMath.EllipsoidRadius(contact.normal, orientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
|
||||
simplexUserDataA += userData[particleIndex] * contact.pointA[j];
|
||||
miscibility += fluidInterface[particleIndex].w * contact.pointA[j];
|
||||
}
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartB + j];
|
||||
simplexPositionB += positions[particleIndex] * contact.pointB[j];
|
||||
simplexRadiusB += BurstMath.EllipsoidRadius(contact.normal, orientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
|
||||
simplexUserDataB += userData[particleIndex] * contact.pointB[j];
|
||||
miscibility += fluidInterface[particleIndex].w * contact.pointB[j];
|
||||
}
|
||||
|
||||
simplexPositionA.w = 0;
|
||||
simplexPositionB.w = 0;
|
||||
|
||||
float4 posA = simplexPositionA - contact.normal * simplexRadiusA;
|
||||
float4 posB = simplexPositionB + contact.normal * simplexRadiusB;
|
||||
|
||||
// adhesion:
|
||||
float lambda = contact.SolveAdhesion(effectiveMasses[i].TotalNormalInvMass, posA, posB, material.stickDistance, material.stickiness, substepTime);
|
||||
float lambda = contact.SolveAdhesion(posA, posB, material.stickDistance, material.stickiness, substepTime);
|
||||
|
||||
// depenetration:
|
||||
lambda += contact.SolvePenetration(effectiveMasses[i].TotalNormalInvMass, posA, posB, solverParameters.maxDepenetration * substepTime);
|
||||
lambda += contact.SolvePenetration(posA, posB, solverParameters.maxDepenetration * substepTime);
|
||||
|
||||
// Apply normal impulse to both particles (w/ shock propagation):
|
||||
if (math.abs(lambda) > BurstMath.epsilon)
|
||||
@@ -314,27 +292,20 @@ namespace Obi
|
||||
}
|
||||
}
|
||||
|
||||
// property diffusion:
|
||||
if (contact.distance < solverParameters.collisionMargin)
|
||||
{
|
||||
float diffusionSpeed = miscibility * 0.5f * substepTime;
|
||||
float4 userDelta = (simplexUserDataB - simplexUserDataA) * solverParameters.diffusionMask * diffusionSpeed;
|
||||
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
userData[simplices[simplexStartA + j]] += userDelta * contact.pointA[j];
|
||||
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
userData[simplices[simplexStartB + j]] -= userDelta * contact.pointB[j];
|
||||
}
|
||||
|
||||
// Apply position deltas immediately, if using sequential evaluation:
|
||||
if (constraintParameters.evaluationOrder == Oni.ConstraintParameters.EvaluationOrder.Sequential)
|
||||
{
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
ApplyPositionDelta(simplices[simplexStartA + j], constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
{
|
||||
int particleIndex = simplices[simplexStartA + j];
|
||||
BurstConstraintsBatchImpl.ApplyPositionDelta(particleIndex, constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
}
|
||||
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
ApplyPositionDelta(simplices[simplexStartB + j], constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
{
|
||||
int particleIndex = simplices[simplexStartB + j];
|
||||
BurstConstraintsBatchImpl.ApplyPositionDelta(particleIndex, constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
}
|
||||
}
|
||||
|
||||
contacts[i] = contact;
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace Obi
|
||||
|
||||
public override int GetConstraintCount()
|
||||
{
|
||||
return ((BurstSolverImpl)solver).abstraction.particleContacts.count;
|
||||
if (!((BurstSolverImpl)solver).particleContacts.IsCreated)
|
||||
return 0;
|
||||
return ((BurstSolverImpl)solver).particleContacts.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,15 @@ namespace Obi
|
||||
this.batchData = batchData;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
if (!((BurstSolverImpl)constraints.solver).particleContacts.IsCreated)
|
||||
return inputDeps;
|
||||
|
||||
var projectConstraints = new ParticleFrictionConstraintsBatchJob()
|
||||
{
|
||||
@@ -40,7 +42,7 @@ namespace Obi
|
||||
prevOrientations = solverImplementation.prevOrientations,
|
||||
|
||||
invMasses = solverImplementation.invMasses,
|
||||
invRotationalMasses = solverImplementation.invRotationalMasses,
|
||||
invInertiaTensors = solverImplementation.invInertiaTensors,
|
||||
radii = solverImplementation.principalRadii,
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
@@ -52,8 +54,7 @@ namespace Obi
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
orientationDeltas = solverImplementation.orientationDeltas,
|
||||
orientationCounts = solverImplementation.orientationConstraintCounts,
|
||||
contacts = ((BurstSolverImpl)constraints.solver).abstraction.particleContacts.AsNativeArray<BurstContact>(),
|
||||
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.particleContactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).particleContacts,
|
||||
|
||||
batchData = batchData,
|
||||
substepTime = substepTime,
|
||||
@@ -65,12 +66,14 @@ namespace Obi
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
if (!((BurstSolverImpl)constraints.solver).particleContacts.IsCreated)
|
||||
return inputDeps;
|
||||
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
var applyConstraints = new ApplyBatchedCollisionConstraintsBatchJob()
|
||||
{
|
||||
contacts = solverAbstraction.particleContacts.AsNativeArray<BurstContact>(),
|
||||
contacts = ((BurstSolverImpl)constraints.solver).particleContacts,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
@@ -99,7 +102,7 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<quaternion> prevOrientations;
|
||||
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> invRotationalMasses;
|
||||
[ReadOnly] public NativeArray<float4> invInertiaTensors;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
@@ -115,7 +118,6 @@ namespace Obi
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> orientationCounts;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstContact> contacts;
|
||||
[ReadOnly] public NativeArray<ContactEffectiveMasses> effectiveMasses;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
[ReadOnly] public float substepTime;
|
||||
@@ -138,16 +140,16 @@ namespace Obi
|
||||
float4 prevPositionA = float4.zero;
|
||||
float4 linearVelocityA = float4.zero;
|
||||
float4 angularVelocityA = float4.zero;
|
||||
float invRotationalMassA = 0;
|
||||
float4 invInertiaTensorA = float4.zero;
|
||||
quaternion orientationA = new quaternion(0, 0, 0, 0);
|
||||
float4 simplexRadiiA = float4.zero;
|
||||
float simplexRadiusA = 0;
|
||||
|
||||
float4 prevPositionB = float4.zero;
|
||||
float4 linearVelocityB = float4.zero;
|
||||
float4 angularVelocityB = float4.zero;
|
||||
float invRotationalMassB = 0;
|
||||
float4 invInertiaTensorB = float4.zero;
|
||||
quaternion orientationB = new quaternion(0, 0, 0, 0);
|
||||
float4 simplexRadiiB = float4.zero;
|
||||
float simplexRadiusB = 0;
|
||||
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
{
|
||||
@@ -155,9 +157,9 @@ namespace Obi
|
||||
prevPositionA += prevPositions[particleIndex] * contact.pointA[j];
|
||||
linearVelocityA += BurstIntegration.DifferentiateLinear(positions[particleIndex], prevPositions[particleIndex], substepTime) * contact.pointA[j];
|
||||
angularVelocityA += BurstIntegration.DifferentiateAngular(orientations[particleIndex], prevOrientations[particleIndex], substepTime) * contact.pointA[j];
|
||||
invRotationalMassA += invRotationalMasses[particleIndex] * contact.pointA[j];
|
||||
invInertiaTensorA += invInertiaTensors[particleIndex] * contact.pointA[j];
|
||||
orientationA.value += orientations[particleIndex].value * contact.pointA[j];
|
||||
simplexRadiiA += radii[particleIndex] * contact.pointA[j];
|
||||
simplexRadiusA += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
|
||||
}
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
{
|
||||
@@ -165,9 +167,9 @@ namespace Obi
|
||||
prevPositionB += prevPositions[particleIndex] * contact.pointB[j];
|
||||
linearVelocityB += BurstIntegration.DifferentiateLinear(positions[particleIndex], prevPositions[particleIndex], substepTime) * contact.pointB[j];
|
||||
angularVelocityB += BurstIntegration.DifferentiateAngular(orientations[particleIndex], prevOrientations[particleIndex], substepTime) * contact.pointB[j];
|
||||
invRotationalMassB += invRotationalMasses[particleIndex] * contact.pointB[j];
|
||||
invInertiaTensorB += invInertiaTensors[particleIndex] * contact.pointB[j];
|
||||
orientationB.value += orientations[particleIndex].value * contact.pointB[j];
|
||||
simplexRadiiB += radii[particleIndex] * contact.pointB[j];
|
||||
simplexRadiusB += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointB[j];
|
||||
}
|
||||
|
||||
float4 rA = float4.zero, rB = float4.zero;
|
||||
@@ -175,8 +177,8 @@ namespace Obi
|
||||
// Consider angular velocities if rolling contacts are enabled:
|
||||
if (material.rollingContacts > 0)
|
||||
{
|
||||
rA = -contact.normal * BurstMath.EllipsoidRadius(contact.normal, orientationA, simplexRadiiA.xyz);
|
||||
rB = contact.normal * BurstMath.EllipsoidRadius(contact.normal, orientationB, simplexRadiiB.xyz);
|
||||
rA = -contact.normal * simplexRadiusA;
|
||||
rB = contact.normal * simplexRadiusB;
|
||||
|
||||
linearVelocityA += new float4(math.cross(angularVelocityA.xyz, rA.xyz), 0);
|
||||
linearVelocityB += new float4(math.cross(angularVelocityB.xyz, rB.xyz), 0);
|
||||
@@ -185,8 +187,8 @@ namespace Obi
|
||||
// Calculate relative velocity:
|
||||
float4 relativeVelocity = linearVelocityA - linearVelocityB;
|
||||
|
||||
// Calculate friction impulses (in the tangent and bitangent directions):
|
||||
float2 impulses = contact.SolveFriction(effectiveMasses[i].TotalTangentInvMass, effectiveMasses[i].TotalBitangentInvMass, relativeVelocity, material.staticFriction, material.dynamicFriction, substepTime);
|
||||
// Calculate friction impulses (in the tangent and bitangent ddirections):
|
||||
float2 impulses = contact.SolveFriction(relativeVelocity, material.staticFriction, material.dynamicFriction, substepTime);
|
||||
|
||||
// Apply friction impulses to both particles:
|
||||
if (math.abs(impulses.x) > BurstMath.epsilon || math.abs(impulses.y) > BurstMath.epsilon)
|
||||
@@ -199,7 +201,7 @@ namespace Obi
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartA + j];
|
||||
deltas[particleIndex] += (tangentImpulse * effectiveMasses[i].tangentInvMassA + bitangentImpulse * effectiveMasses[i].bitangentInvMassA) * substepTime * contact.pointA[j] * baryScale;
|
||||
deltas[particleIndex] += (tangentImpulse * contact.tangentInvMassA + bitangentImpulse * contact.bitangentInvMassA) * substepTime * contact.pointA[j] * baryScale;
|
||||
counts[particleIndex]++;
|
||||
}
|
||||
|
||||
@@ -207,16 +209,13 @@ namespace Obi
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartB + j];
|
||||
deltas[particleIndex] -= (tangentImpulse * effectiveMasses[i].tangentInvMassB + bitangentImpulse * effectiveMasses[i].bitangentInvMassB) * substepTime * contact.pointB[j] * baryScale;
|
||||
deltas[particleIndex] -= (tangentImpulse * contact.tangentInvMassB + bitangentImpulse * contact.bitangentInvMassB) * substepTime * contact.pointB[j] * baryScale;
|
||||
counts[particleIndex]++;
|
||||
}
|
||||
|
||||
// Rolling contacts:
|
||||
if (material.rollingContacts > 0)
|
||||
{
|
||||
float4 invInertiaTensorA = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadiiA, invRotationalMassA) + new float4(BurstMath.epsilon));
|
||||
float4 invInertiaTensorB = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadiiB, invRotationalMassB) + new float4(BurstMath.epsilon));
|
||||
|
||||
// Calculate angular velocity deltas due to friction impulse:
|
||||
float4x4 solverInertiaA = BurstMath.TransformInertiaTensor(invInertiaTensorA, orientationA);
|
||||
float4x4 solverInertiaB = BurstMath.TransformInertiaTensor(invInertiaTensorB, orientationB);
|
||||
@@ -253,7 +252,7 @@ namespace Obi
|
||||
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartB + j];
|
||||
int particleIndex = simplices[simplexStartB+ j];
|
||||
quaternion qB = orientationDeltas[particleIndex];
|
||||
qB.value += orientationDeltaB.value;
|
||||
orientationDeltas[particleIndex] = qB;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
@@ -22,20 +21,6 @@ namespace Obi
|
||||
batches.Remove(batch as BurstPinConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
|
||||
public JobHandle ProjectRenderablePositions(JobHandle inputDeps)
|
||||
{
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].ProjectRenderablePositions(inputDeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
return inputDeps;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -6,7 +6,6 @@ using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
@@ -34,29 +33,7 @@ namespace Obi
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
{
|
||||
var clearPins = new ClearPinsJob
|
||||
{
|
||||
colliderIndices = colliderIndices,
|
||||
shapes = ObiColliderWorld.GetInstance().colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
rigidbodies = ObiColliderWorld.GetInstance().rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
};
|
||||
inputDeps = clearPins.Schedule(m_ConstraintCount, 128, inputDeps);
|
||||
|
||||
var updatePins = new UpdatePinsJob
|
||||
{
|
||||
colliderIndices = colliderIndices,
|
||||
shapes = ObiColliderWorld.GetInstance().colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
rigidbodies = ObiColliderWorld.GetInstance().rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
};
|
||||
inputDeps = updatePins.Schedule(m_ConstraintCount, 128, inputDeps);
|
||||
|
||||
// clear lambdas:
|
||||
return base.Initialize(inputDeps, stepTime, substepTime, steps, timeLeft);
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new PinConstraintsBatchJob()
|
||||
{
|
||||
@@ -86,13 +63,12 @@ namespace Obi
|
||||
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
|
||||
stepTime = stepTime,
|
||||
steps = steps,
|
||||
substepTime = substepTime,
|
||||
timeLeft = timeLeft,
|
||||
substeps = substeps,
|
||||
activeConstraintCount = m_ConstraintCount
|
||||
};
|
||||
|
||||
return projectConstraints.Schedule(m_ConstraintCount, 16, inputDeps);
|
||||
return projectConstraints.Schedule(inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
@@ -118,78 +94,8 @@ namespace Obi
|
||||
return applyConstraints.Schedule(inputDeps);
|
||||
}
|
||||
|
||||
public JobHandle ProjectRenderablePositions(JobHandle inputDeps)
|
||||
{
|
||||
var project = new ProjectRenderablePositionsJob()
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
colliderIndices = colliderIndices,
|
||||
offsets = offsets,
|
||||
stiffnesses = stiffnesses,
|
||||
restDarboux = restDarbouxVectors,
|
||||
|
||||
transforms = ObiColliderWorld.GetInstance().colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
|
||||
renderablePositions = solverImplementation.renderablePositions,
|
||||
renderableOrientations = solverImplementation.renderableOrientations,
|
||||
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
|
||||
};
|
||||
|
||||
return project.Schedule(m_ConstraintCount, 16, inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public unsafe struct ClearPinsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> colliderIndices;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int colliderIndex = colliderIndices[i];
|
||||
|
||||
// no collider to pin to, so ignore the constraint.
|
||||
if (colliderIndex < 0)
|
||||
return;
|
||||
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstRigidbody* arr = (BurstRigidbody*)rigidbodies.GetUnsafePtr();
|
||||
Interlocked.Exchange(ref arr[rigidbodyIndex].constraintCount, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public unsafe struct UpdatePinsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> colliderIndices;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int colliderIndex = colliderIndices[i];
|
||||
|
||||
// no collider to pin to, so ignore the constraint.
|
||||
if (colliderIndex < 0)
|
||||
return;
|
||||
|
||||
// Increment the amount of constraints affecting this rigidbody for mass splitting:
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstRigidbody* arr = (BurstRigidbody*)rigidbodies.GetUnsafePtr();
|
||||
Interlocked.Increment(ref arr[rigidbodyIndex].constraintCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public unsafe struct PinConstraintsBatchJob : IJobParallelFor
|
||||
public unsafe struct PinConstraintsBatchJob : IJob
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public NativeArray<int> colliderIndices;
|
||||
@@ -197,7 +103,7 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<float4> offsets;
|
||||
[ReadOnly] public NativeArray<float2> stiffnesses;
|
||||
[ReadOnly] public NativeArray<quaternion> restDarboux;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> lambdas;
|
||||
public NativeArray<float4> lambdas;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
@@ -208,8 +114,8 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> rigidbodyLinearDeltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> rigidbodyAngularDeltas;
|
||||
public NativeArray<float4> rigidbodyLinearDeltas;
|
||||
public NativeArray<float4> rigidbodyAngularDeltas;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
@@ -219,113 +125,112 @@ namespace Obi
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
[ReadOnly] public float stepTime;
|
||||
[ReadOnly] public float substepTime;
|
||||
[ReadOnly] public float timeLeft;
|
||||
[ReadOnly] public int steps;
|
||||
[ReadOnly] public int substeps;
|
||||
[ReadOnly] public int activeConstraintCount;
|
||||
|
||||
public void Execute(int i)
|
||||
public void Execute()
|
||||
{
|
||||
int particleIndex = particleIndices[i];
|
||||
int colliderIndex = colliderIndices[i];
|
||||
|
||||
// no collider to pin to, so ignore the constraint.
|
||||
if (colliderIndex < 0)
|
||||
return;
|
||||
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
|
||||
float frameEnd = stepTime * steps;
|
||||
float substepsToEnd = timeLeft / substepTime;
|
||||
|
||||
// calculate time adjusted compliances
|
||||
float2 compliances = stiffnesses[i].xy / (substepTime * substepTime);
|
||||
|
||||
// project particle position to the end of the full step:
|
||||
float4 particlePosition = math.lerp(prevPositions[particleIndex], positions[particleIndex], substepsToEnd);
|
||||
|
||||
// express pin offset in world space:
|
||||
float4 worldPinOffset = transforms[colliderIndex].TransformPoint(offsets[i]);
|
||||
float4 predictedPinOffset = worldPinOffset;
|
||||
quaternion predictedRotation = transforms[colliderIndex].rotation;
|
||||
|
||||
float rigidbodyLinearW = 0;
|
||||
float rigidbodyAngularW = 0;
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
for (int i = 0; i < activeConstraintCount; ++i)
|
||||
{
|
||||
var rigidbody = rigidbodies[rigidbodyIndex];
|
||||
int particleIndex = particleIndices[i];
|
||||
int colliderIndex = colliderIndices[i];
|
||||
|
||||
// predict offset point position using rb velocity at that point (can't integrate transform since position != center of mass)
|
||||
float4 velocityAtPoint = BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, inertialFrame.frame.InverseTransformPoint(worldPinOffset), rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame);
|
||||
predictedPinOffset = BurstIntegration.IntegrateLinear(predictedPinOffset, inertialFrame.frame.TransformVector(velocityAtPoint), frameEnd);
|
||||
// no collider to pin to, so ignore the constraint.
|
||||
if (colliderIndex < 0)
|
||||
continue;
|
||||
|
||||
// predict rotation at the end of the step:
|
||||
predictedRotation = BurstIntegration.IntegrateAngular(predictedRotation, rigidbody.angularVelocity + rigidbodyAngularDeltas[rigidbodyIndex], stepTime);
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
|
||||
// calculate linear and angular rigidbody effective masses (mass splitting: multiply by constraint count)
|
||||
rigidbodyLinearW = rigidbody.inverseMass * rigidbody.constraintCount;
|
||||
rigidbodyAngularW = BurstMath.RotationalInvMass(rigidbody.inverseInertiaTensor,
|
||||
worldPinOffset - rigidbody.com,
|
||||
math.normalizesafe(inertialFrame.frame.TransformPoint(particlePosition) - predictedPinOffset)) * rigidbody.constraintCount;
|
||||
// calculate time adjusted compliances
|
||||
float2 compliances = stiffnesses[i].xy / (substepTime * substepTime);
|
||||
|
||||
}
|
||||
// project particle position to the end of the full step:
|
||||
float4 particlePosition = math.lerp(prevPositions[particleIndex], positions[particleIndex], substeps);
|
||||
|
||||
// Transform pin position to solver space for constraint solving:
|
||||
predictedPinOffset = inertialFrame.frame.InverseTransformPoint(predictedPinOffset);
|
||||
predictedRotation = math.mul(math.conjugate(inertialFrame.frame.rotation), predictedRotation);
|
||||
// express pin offset in world space:
|
||||
float4 worldPinOffset = transforms[colliderIndex].TransformPoint(offsets[i]);
|
||||
float4 predictedPinOffset = worldPinOffset;
|
||||
quaternion predictedRotation = transforms[colliderIndex].rotation;
|
||||
|
||||
float4 gradient = particlePosition - predictedPinOffset;
|
||||
float constraint = math.length(gradient);
|
||||
float4 gradientDir = gradient / (constraint + BurstMath.epsilon);
|
||||
|
||||
float4 lambda = lambdas[i];
|
||||
float linearDLambda = (-constraint - compliances.x * lambda.w) / (invMasses[particleIndex] + rigidbodyLinearW + rigidbodyAngularW + compliances.x + BurstMath.epsilon);
|
||||
lambda.w += linearDLambda;
|
||||
float4 correction = linearDLambda * gradientDir;
|
||||
|
||||
deltas[particleIndex] += correction * invMasses[particleIndex] / substepsToEnd;
|
||||
counts[particleIndex]++;
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstMath.ApplyImpulse(rigidbodyIndex,
|
||||
-correction / frameEnd,
|
||||
inertialFrame.frame.InverseTransformPoint(worldPinOffset),
|
||||
rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
}
|
||||
|
||||
if (rigidbodyAngularW > 0 || invRotationalMasses[particleIndex] > 0)
|
||||
{
|
||||
// bend/twist constraint:
|
||||
quaternion omega = math.mul(math.conjugate(orientations[particleIndex]), predictedRotation); //darboux vector
|
||||
|
||||
quaternion omega_plus;
|
||||
omega_plus.value = omega.value + restDarboux[i].value; //delta Omega with - omega_0
|
||||
omega.value -= restDarboux[i].value; //delta Omega with + omega_0
|
||||
if (math.lengthsq(omega.value) > math.lengthsq(omega_plus.value))
|
||||
omega = omega_plus;
|
||||
|
||||
float3 dlambda = (omega.value.xyz - compliances.y * lambda.xyz) / (compliances.y + invRotationalMasses[particleIndex] + rigidbodyAngularW + BurstMath.epsilon);
|
||||
lambda.xyz += dlambda;
|
||||
|
||||
//discrete Darboux vector does not have vanishing scalar part
|
||||
quaternion dlambdaQ = new quaternion(dlambda[0], dlambda[1], dlambda[2], 0);
|
||||
|
||||
quaternion orientDelta = orientationDeltas[particleIndex];
|
||||
orientDelta.value += math.mul(predictedRotation, dlambdaQ).value * invRotationalMasses[particleIndex] / substepsToEnd;
|
||||
orientationDeltas[particleIndex] = orientDelta;
|
||||
orientationCounts[particleIndex]++;
|
||||
float rigidbodyLinearW = 0;
|
||||
float rigidbodyAngularW = 0;
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstMath.ApplyDeltaQuaternion(rigidbodyIndex,
|
||||
predictedRotation,
|
||||
-math.mul(orientations[particleIndex], dlambdaQ).value * rigidbodyAngularW,
|
||||
rigidbodyAngularDeltas, inertialFrame.frame, frameEnd);
|
||||
}
|
||||
}
|
||||
var rigidbody = rigidbodies[rigidbodyIndex];
|
||||
|
||||
lambdas[i] = lambda;
|
||||
// predict offset point position:
|
||||
float4 velocityAtPoint = BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, inertialFrame.frame.InverseTransformPoint(worldPinOffset), rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
predictedPinOffset = BurstIntegration.IntegrateLinear(predictedPinOffset, inertialFrame.frame.TransformVector(velocityAtPoint), stepTime);
|
||||
|
||||
// predict rotation at the end of the step:
|
||||
predictedRotation = BurstIntegration.IntegrateAngular(predictedRotation, rigidbody.angularVelocity + rigidbodyAngularDeltas[rigidbodyIndex], stepTime);
|
||||
|
||||
// calculate linear and angular rigidbody weights:
|
||||
rigidbodyLinearW = rigidbody.inverseMass;
|
||||
rigidbodyAngularW = BurstMath.RotationalInvMass(rigidbody.inverseInertiaTensor,
|
||||
worldPinOffset - rigidbody.com,
|
||||
math.normalizesafe(inertialFrame.frame.TransformPoint(particlePosition) - predictedPinOffset));
|
||||
|
||||
}
|
||||
|
||||
// Transform pin position to solver space for constraint solving:
|
||||
predictedPinOffset = inertialFrame.frame.InverseTransformPoint(predictedPinOffset);
|
||||
predictedRotation = math.mul(math.conjugate(inertialFrame.frame.rotation), predictedRotation);
|
||||
|
||||
float4 gradient = particlePosition - predictedPinOffset;
|
||||
float constraint = math.length(gradient);
|
||||
float4 gradientDir = gradient / (constraint + BurstMath.epsilon);
|
||||
|
||||
float4 lambda = lambdas[i];
|
||||
float linearDLambda = (-constraint - compliances.x * lambda.w) / (invMasses[particleIndex] + rigidbodyLinearW + rigidbodyAngularW + compliances.x + BurstMath.epsilon);
|
||||
lambda.w += linearDLambda;
|
||||
float4 correction = linearDLambda * gradientDir;
|
||||
|
||||
deltas[particleIndex] += correction * invMasses[particleIndex] / substeps;
|
||||
counts[particleIndex]++;
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstMath.ApplyImpulse(rigidbodyIndex,
|
||||
-correction / stepTime * 1,
|
||||
inertialFrame.frame.InverseTransformPoint(worldPinOffset),
|
||||
rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
}
|
||||
|
||||
if (rigidbodyAngularW > 0 || invRotationalMasses[particleIndex] > 0)
|
||||
{
|
||||
// bend/twist constraint:
|
||||
quaternion omega = math.mul(math.conjugate(orientations[particleIndex]), predictedRotation); //darboux vector
|
||||
|
||||
quaternion omega_plus;
|
||||
omega_plus.value = omega.value + restDarboux[i].value; //delta Omega with - omega_0
|
||||
omega.value -= restDarboux[i].value; //delta Omega with + omega_0
|
||||
if (math.lengthsq(omega.value) > math.lengthsq(omega_plus.value))
|
||||
omega = omega_plus;
|
||||
|
||||
float3 dlambda = (omega.value.xyz - compliances.y * lambda.xyz) / new float3(compliances.y + invRotationalMasses[particleIndex] + rigidbodyAngularW + BurstMath.epsilon);
|
||||
lambda.xyz += dlambda;
|
||||
|
||||
//discrete Darboux vector does not have vanishing scalar part
|
||||
quaternion dlambdaQ = new quaternion(dlambda[0], dlambda[1], dlambda[2], 0);
|
||||
|
||||
quaternion orientDelta = orientationDeltas[particleIndex];
|
||||
orientDelta.value += math.mul(predictedRotation, dlambdaQ).value * invRotationalMasses[particleIndex] / substeps;
|
||||
orientationDeltas[particleIndex] = orientDelta;
|
||||
orientationCounts[particleIndex]++;
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstMath.ApplyDeltaQuaternion(rigidbodyIndex,
|
||||
predictedRotation,
|
||||
-math.mul(orientations[particleIndex], dlambdaQ).value * rigidbodyAngularW,
|
||||
rigidbodyAngularDeltas, inertialFrame.frame, stepTime);
|
||||
}
|
||||
}
|
||||
|
||||
lambdas[i] = lambda;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,40 +275,6 @@ namespace Obi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ProjectRenderablePositionsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public NativeArray<int> colliderIndices;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> offsets;
|
||||
[ReadOnly] public NativeArray<float2> stiffnesses;
|
||||
[ReadOnly] public NativeArray<quaternion> restDarboux;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> renderablePositions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<quaternion> renderableOrientations;
|
||||
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int particleIndex = particleIndices[i];
|
||||
int colliderIndex = colliderIndices[i];
|
||||
|
||||
// no collider to pin to or projection deactivated, so ignore the constraint.
|
||||
if (colliderIndex < 0 || offsets[i].w < 0.5f)
|
||||
return;
|
||||
|
||||
BurstAffineTransform attachmentMatrix = inertialFrame.frame.Inverse() * transforms[colliderIndex];
|
||||
|
||||
renderablePositions[particleIndex] = attachmentMatrix.TransformPoint(offsets[i]);
|
||||
if (stiffnesses[i].y < 10000)
|
||||
renderableOrientations[particleIndex] = math.mul(attachmentMatrix.rotation, restDarboux[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 744a58a1ca5364e0a86c93e04770c0b8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,26 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstPinholeConstraints : BurstConstraintsImpl<BurstPinholeConstraintsBatch>
|
||||
{
|
||||
public BurstPinholeConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.Pinhole)
|
||||
{
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstPinholeConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstPinholeConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,490 +0,0 @@
|
||||
#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.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstPinholeConstraintsBatch : BurstConstraintsBatchImpl, IPinholeConstraintsBatchImpl
|
||||
{
|
||||
private NativeArray<int> colliderIndices;
|
||||
private NativeArray<float4> offsets;
|
||||
private NativeArray<float> edgeMus;
|
||||
private NativeArray<int2> edgeRanges;
|
||||
private NativeArray<float2> edgeRangeMus;
|
||||
private NativeArray<float> parameters;
|
||||
private NativeArray<float> relativeVelocities;
|
||||
|
||||
public BurstPinholeConstraintsBatch(BurstPinholeConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.Pinhole;
|
||||
}
|
||||
|
||||
public void SetPinholeConstraints(ObiNativeIntList particleIndices, ObiNativeIntList colliderIndices, ObiNativeVector4List offsets, ObiNativeFloatList edgeMus, ObiNativeIntList edgeRanges, ObiNativeFloatList edgeRangeMus, ObiNativeFloatList parameters, ObiNativeFloatList relativeVelocities, ObiNativeFloatList lambdas, int count)
|
||||
{
|
||||
this.particleIndices = particleIndices.AsNativeArray<int>();
|
||||
this.colliderIndices = colliderIndices.AsNativeArray<int>();
|
||||
this.offsets = offsets.AsNativeArray<float4>();
|
||||
this.edgeMus = edgeMus.AsNativeArray<float>();
|
||||
this.edgeRanges = edgeRanges.AsNativeArray<int2>();
|
||||
this.edgeRangeMus = edgeRangeMus.AsNativeArray<float2>();
|
||||
this.parameters = parameters.AsNativeArray<float>();
|
||||
this.relativeVelocities = relativeVelocities.AsNativeArray<float>();
|
||||
this.lambdas = lambdas.AsNativeArray<float>();
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
{
|
||||
var clearPins = new ClearPinsJob
|
||||
{
|
||||
colliderIndices = colliderIndices,
|
||||
shapes = ObiColliderWorld.GetInstance().colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
rigidbodies = ObiColliderWorld.GetInstance().rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
};
|
||||
inputDeps = clearPins.Schedule(m_ConstraintCount, 128, inputDeps);
|
||||
|
||||
var updatePins = new UpdatePinsJob
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
colliderIndices = colliderIndices,
|
||||
offsets = offsets,
|
||||
edgeMus = edgeMus,
|
||||
edgeRangeMus = edgeRangeMus,
|
||||
relativeVelocities = relativeVelocities,
|
||||
parameters = parameters,
|
||||
edgeRanges = edgeRanges,
|
||||
|
||||
positions = solverImplementation.positions,
|
||||
prevPositions = solverImplementation.prevPositions,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
|
||||
deformableEdges = solverImplementation.abstraction.deformableEdges.AsNativeArray<int>(),
|
||||
|
||||
shapes = ObiColliderWorld.GetInstance().colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
transforms = ObiColliderWorld.GetInstance().colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
rigidbodies = ObiColliderWorld.GetInstance().rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
rigidbodyLinearDeltas = solverImplementation.abstraction.rigidbodyLinearDeltas.AsNativeArray<float4>(),
|
||||
rigidbodyAngularDeltas = solverImplementation.abstraction.rigidbodyAngularDeltas.AsNativeArray<float4>(),
|
||||
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
|
||||
stepTime = stepTime,
|
||||
steps = steps,
|
||||
substepTime = substepTime,
|
||||
timeLeft = timeLeft,
|
||||
activeConstraintCount = m_ConstraintCount
|
||||
};
|
||||
inputDeps = updatePins.Schedule(m_ConstraintCount, 128, inputDeps);
|
||||
|
||||
// clear lambdas:
|
||||
return base.Initialize(inputDeps, stepTime, substepTime, steps, timeLeft);
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
{
|
||||
var projectConstraints = new PinholeConstraintsBatchJob()
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
colliderIndices = colliderIndices,
|
||||
offsets = offsets,
|
||||
edgeMus = edgeMus,
|
||||
parameters = parameters,
|
||||
lambdas = lambdas,
|
||||
|
||||
positions = solverImplementation.positions,
|
||||
prevPositions = solverImplementation.prevPositions,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
|
||||
deformableEdges = solverImplementation.abstraction.deformableEdges.AsNativeArray<int>(),
|
||||
|
||||
shapes = ObiColliderWorld.GetInstance().colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
transforms = ObiColliderWorld.GetInstance().colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
rigidbodies = ObiColliderWorld.GetInstance().rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
rigidbodyLinearDeltas = solverImplementation.abstraction.rigidbodyLinearDeltas.AsNativeArray<float4>(),
|
||||
rigidbodyAngularDeltas = solverImplementation.abstraction.rigidbodyAngularDeltas.AsNativeArray<float4>(),
|
||||
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
|
||||
stepTime = stepTime,
|
||||
steps = steps,
|
||||
substepTime = substepTime,
|
||||
timeLeft = timeLeft,
|
||||
activeConstraintCount = m_ConstraintCount
|
||||
};
|
||||
|
||||
return projectConstraints.Schedule(m_ConstraintCount, 16, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var cparameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
var applyConstraints = new ApplyPinholeConstraintsBatchJob()
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
deformableEdges = solverImplementation.abstraction.deformableEdges.AsNativeArray<int>(),
|
||||
|
||||
positions = solverImplementation.positions,
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
|
||||
sorFactor = cparameters.SORFactor,
|
||||
activeConstraintCount = m_ConstraintCount,
|
||||
};
|
||||
|
||||
return applyConstraints.Schedule(inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public unsafe struct ClearPinsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> colliderIndices;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int colliderIndex = colliderIndices[i];
|
||||
|
||||
// no collider to pin to, so ignore the constraint.
|
||||
if (colliderIndex < 0)
|
||||
return;
|
||||
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstRigidbody* arr = (BurstRigidbody*)rigidbodies.GetUnsafePtr();
|
||||
Interlocked.Exchange(ref arr[rigidbodyIndex].constraintCount, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public unsafe struct UpdatePinsJob : IJobParallelFor
|
||||
{
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> particleIndices;
|
||||
|
||||
[ReadOnly] public NativeArray<int2> edgeRanges;
|
||||
[ReadOnly] public NativeArray<float2> edgeRangeMus;
|
||||
[ReadOnly] public NativeArray<float4> offsets;
|
||||
[ReadOnly] public NativeArray<float> parameters; // compliance, friction, motor speed, motor force, clamp behavior.
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float> edgeMus;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float> relativeVelocities;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
|
||||
[ReadOnly] public NativeArray<int> deformableEdges;
|
||||
|
||||
[ReadOnly] public NativeArray<int> colliderIndices;
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> rigidbodyLinearDeltas;
|
||||
[ReadOnly] public NativeArray<float4> rigidbodyAngularDeltas;
|
||||
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
[ReadOnly] public float stepTime;
|
||||
[ReadOnly] public float substepTime;
|
||||
[ReadOnly] public float timeLeft;
|
||||
[ReadOnly] public int steps;
|
||||
[ReadOnly] public int activeConstraintCount;
|
||||
|
||||
private bool IsEdgeValid(int edgeIndex, int nextEdgeIndex, float mix)
|
||||
{
|
||||
return mix < 0 ? deformableEdges[nextEdgeIndex * 2 + 1] == deformableEdges[edgeIndex * 2] :
|
||||
deformableEdges[nextEdgeIndex * 2] == deformableEdges[edgeIndex * 2 + 1];
|
||||
}
|
||||
|
||||
private bool ClampToRange(int i, int edgeIndex, ref float mix)
|
||||
{
|
||||
bool clamped = false;
|
||||
if (edgeIndex == edgeRanges[i].x && mix < edgeRangeMus[i].x)
|
||||
{
|
||||
mix = edgeRangeMus[i].x;
|
||||
clamped = true;
|
||||
}
|
||||
if (edgeIndex == edgeRanges[i].y && mix > edgeRangeMus[i].y)
|
||||
{
|
||||
mix = edgeRangeMus[i].y;
|
||||
clamped = true;
|
||||
}
|
||||
return clamped;
|
||||
}
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int edgeIndex = particleIndices[i];
|
||||
int colliderIndex = colliderIndices[i];
|
||||
|
||||
// if no collider or edge, ignore the constraint.
|
||||
if (colliderIndex < 0 || edgeIndex < 0)
|
||||
return;
|
||||
|
||||
// Increment the amount of constraints affecting this rigidbody for mass splitting:
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstRigidbody* arr = (BurstRigidbody*)rigidbodies.GetUnsafePtr();
|
||||
Interlocked.Increment(ref arr[rigidbodyIndex].constraintCount);
|
||||
}
|
||||
|
||||
float frameEnd = stepTime * steps;
|
||||
float substepsToEnd = timeLeft / substepTime;
|
||||
|
||||
int p1 = deformableEdges[edgeIndex * 2];
|
||||
int p2 = deformableEdges[edgeIndex * 2 + 1];
|
||||
int edgeCount = math.max(0, edgeRanges[i].y - edgeRanges[i].x + 1);
|
||||
|
||||
// express pin offset in world space:
|
||||
float4 worldPinOffset = transforms[colliderIndex].TransformPoint(offsets[i]);
|
||||
float4 predictedPinOffset = worldPinOffset;
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
// predict offset point position using rb velocity at that point (can't integrate transform since position != center of mass)
|
||||
float4 pointVelocity = BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, inertialFrame.frame.InverseTransformPoint(worldPinOffset), rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame);
|
||||
predictedPinOffset = BurstIntegration.IntegrateLinear(predictedPinOffset, inertialFrame.frame.TransformVector(pointVelocity), frameEnd);
|
||||
}
|
||||
|
||||
// transform pinhole position to solver space for constraint solving:
|
||||
float4 solverPredictedOffset = inertialFrame.frame.InverseTransformPoint(predictedPinOffset);
|
||||
|
||||
// get current edge data:
|
||||
float4 particlePosition1 = math.lerp(prevPositions[p1], positions[p1], substepsToEnd);
|
||||
float4 particlePosition2 = math.lerp(prevPositions[p2], positions[p2], substepsToEnd);
|
||||
float edgeLength = math.length(particlePosition1 - particlePosition2) + BurstMath.epsilon;
|
||||
BurstMath.NearestPointOnEdge(particlePosition1, particlePosition2, solverPredictedOffset, out float mix, false);
|
||||
|
||||
// calculate current relative velocity between rope and pinhole:
|
||||
float velocity = (mix - edgeMus[i]) / substepTime * edgeLength; // vel = pos / time.
|
||||
relativeVelocities[i] = velocity;
|
||||
|
||||
//apply motor force:
|
||||
float targetAccel = (parameters[i * 5 + 2] - velocity) / substepTime; // accel = vel / time.
|
||||
float maxAccel = parameters[i * 5 + 3] * math.max(math.lerp(invMasses[p1], invMasses[p2], mix), BurstMath.epsilon); // accel = force / mass. Guard against inf*0
|
||||
velocity += math.clamp(targetAccel, -maxAccel, maxAccel) * substepTime;
|
||||
|
||||
// calculate new position by adding motor acceleration:
|
||||
float corrMix = edgeMus[i] + velocity * substepTime / edgeLength;
|
||||
|
||||
// apply artificial friction by interpolating predicted position and corrected position.
|
||||
mix = math.lerp(mix, corrMix, parameters[i * 5 + 1]);
|
||||
|
||||
// move to an adjacent edge if needed:
|
||||
if (!ClampToRange(i, edgeIndex, ref mix) && (mix < 0 || mix > 1))
|
||||
{
|
||||
bool clampOnEnd = parameters[i * 5 + 4] > 0.5f;
|
||||
|
||||
// calculate distance we need to travel along edge chain:
|
||||
float distToTravel = math.length(particlePosition1 - particlePosition2) * (mix < 0 ? -mix : mix - 1);
|
||||
|
||||
int nextEdgeIndex;
|
||||
for (int k = 0; k < 10; ++k) // look up to 10 edges away.
|
||||
{
|
||||
// calculate index of next edge:
|
||||
nextEdgeIndex = mix < 0 ? edgeIndex - 1 : edgeIndex + 1;
|
||||
nextEdgeIndex = edgeRanges[i].x + (int)BurstMath.nfmod(nextEdgeIndex - edgeRanges[i].x, edgeCount);
|
||||
|
||||
// see if it's valid
|
||||
if (!IsEdgeValid(edgeIndex, nextEdgeIndex, mix))
|
||||
{
|
||||
// disable constraint if needed
|
||||
if (!clampOnEnd) { particleIndices[i] = -1; return; }
|
||||
|
||||
// otherwise clamp:
|
||||
mix = math.saturate(mix);
|
||||
break;
|
||||
}
|
||||
|
||||
// advance to next edge:
|
||||
edgeIndex = nextEdgeIndex;
|
||||
p1 = deformableEdges[edgeIndex * 2];
|
||||
p2 = deformableEdges[edgeIndex * 2 + 1];
|
||||
particlePosition1 = math.lerp(prevPositions[p1], positions[p1], substepsToEnd);
|
||||
particlePosition2 = math.lerp(prevPositions[p2], positions[p2], substepsToEnd);
|
||||
edgeLength = math.length(particlePosition1 - particlePosition2) + BurstMath.epsilon;
|
||||
|
||||
// stop if we reached target edge:
|
||||
if (distToTravel <= edgeLength)
|
||||
{
|
||||
mix = mix < 0 ? 1 - math.saturate(distToTravel / edgeLength) : math.saturate(distToTravel / edgeLength);
|
||||
ClampToRange(i, edgeIndex, ref mix);
|
||||
break;
|
||||
}
|
||||
|
||||
// stop if we reached end of range:
|
||||
if (ClampToRange(i, edgeIndex, ref mix))
|
||||
break;
|
||||
|
||||
distToTravel -= edgeLength;
|
||||
}
|
||||
}
|
||||
|
||||
edgeMus[i] = mix;
|
||||
particleIndices[i] = edgeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public unsafe struct PinholeConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public NativeArray<int> colliderIndices;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> offsets;
|
||||
[ReadOnly] public NativeArray<float> parameters; // compliance, friction, motor speed, motor force, clamp behavior.
|
||||
[ReadOnly] public NativeArray<float> edgeMus;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float> lambdas;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
|
||||
[ReadOnly] public NativeArray<int> deformableEdges;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> rigidbodyLinearDeltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> rigidbodyAngularDeltas;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
[ReadOnly] public float stepTime;
|
||||
[ReadOnly] public float substepTime;
|
||||
[ReadOnly] public float timeLeft;
|
||||
[ReadOnly] public int steps;
|
||||
[ReadOnly] public int activeConstraintCount;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int edgeIndex = particleIndices[i];
|
||||
int colliderIndex = colliderIndices[i];
|
||||
|
||||
// if no collider or edge, ignore the constraint.
|
||||
if (edgeIndex < 0 || colliderIndex < 0)
|
||||
return;
|
||||
|
||||
float frameEnd = stepTime * steps;
|
||||
float substepsToEnd = timeLeft / substepTime;
|
||||
|
||||
// calculate time adjusted compliance
|
||||
float compliance = parameters[i * 5] / (substepTime * substepTime);
|
||||
|
||||
int p1 = deformableEdges[edgeIndex * 2];
|
||||
int p2 = deformableEdges[edgeIndex * 2 + 1];
|
||||
|
||||
// calculate projection on current edge:
|
||||
float mix = edgeMus[i];
|
||||
float4 particlePosition1 = math.lerp(prevPositions[p1], positions[p1], substepsToEnd);
|
||||
float4 particlePosition2 = math.lerp(prevPositions[p2], positions[p2], substepsToEnd);
|
||||
float4 projection = math.lerp(particlePosition1, particlePosition2, mix);
|
||||
|
||||
// express pin offset in world space:
|
||||
float4 worldPinOffset = transforms[colliderIndex].TransformPoint(offsets[i]);
|
||||
float4 predictedPinOffset = worldPinOffset;
|
||||
|
||||
float rigidbodyLinearW = 0;
|
||||
float rigidbodyAngularW = 0;
|
||||
|
||||
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
var rigidbody = rigidbodies[rigidbodyIndex];
|
||||
|
||||
// predict offset point position using rb velocity at that point (can't integrate transform since position != center of mass)
|
||||
float4 pointVelocity = BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, inertialFrame.frame.InverseTransformPoint(worldPinOffset), rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame);
|
||||
predictedPinOffset = BurstIntegration.IntegrateLinear(predictedPinOffset, inertialFrame.frame.TransformVector(pointVelocity), frameEnd);
|
||||
|
||||
// calculate linear and angular rigidbody effective masses (mass splitting: multiply by constraint count)
|
||||
rigidbodyLinearW = rigidbody.inverseMass * rigidbody.constraintCount;
|
||||
rigidbodyAngularW = BurstMath.RotationalInvMass(rigidbody.inverseInertiaTensor,
|
||||
worldPinOffset - rigidbody.com,
|
||||
math.normalizesafe(inertialFrame.frame.TransformPoint(projection) - predictedPinOffset)) * rigidbody.constraintCount;
|
||||
}
|
||||
|
||||
// Transform pinhole position to solver space for constraint solving:
|
||||
predictedPinOffset = inertialFrame.frame.InverseTransformPoint(predictedPinOffset);
|
||||
|
||||
float4 gradient = projection - predictedPinOffset;
|
||||
float constraint = math.length(gradient);
|
||||
float4 gradientDir = gradient / (constraint + BurstMath.epsilon);
|
||||
|
||||
float lambda = (-constraint - compliance * lambdas[i]) / (math.lerp(invMasses[p1], invMasses[p2], mix) + rigidbodyLinearW + rigidbodyAngularW + compliance + BurstMath.epsilon);
|
||||
lambdas[i] += lambda;
|
||||
float4 correction = lambda * gradientDir;
|
||||
|
||||
float baryScale = BurstMath.BaryScale(new float4(1 - mix, mix, 0, 0));
|
||||
|
||||
deltas[p1] += correction * baryScale * invMasses[p1] * (1 - mix) / substepsToEnd;
|
||||
counts[p1]++;
|
||||
|
||||
deltas[p2] += correction * baryScale * invMasses[p2] * mix / substepsToEnd;
|
||||
counts[p2]++;
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstMath.ApplyImpulse(rigidbodyIndex,
|
||||
-correction / frameEnd,
|
||||
inertialFrame.frame.InverseTransformPoint(worldPinOffset),
|
||||
rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ApplyPinholeConstraintsBatchJob : IJob
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public float sorFactor;
|
||||
|
||||
[ReadOnly] public NativeArray<int> deformableEdges;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
[ReadOnly] public int activeConstraintCount;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
for (int i = 0; i < activeConstraintCount; ++i)
|
||||
{
|
||||
int edgeIndex = particleIndices[i];
|
||||
if (edgeIndex < 0) continue;
|
||||
|
||||
int p1 = deformableEdges[edgeIndex * 2];
|
||||
int p2 = deformableEdges[edgeIndex * 2 + 1];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -23,8 +23,6 @@ namespace Obi
|
||||
private NativeArray<float4x4> linearTransforms;
|
||||
private NativeArray<float4x4> plasticDeformations;
|
||||
|
||||
private bool m_RecalculateRestShape = false;
|
||||
|
||||
public BurstShapeMatchingConstraintsBatch(BurstShapeMatchingConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
@@ -69,37 +67,13 @@ namespace Obi
|
||||
Aqq.Dispose();
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
if (m_RecalculateRestShape)
|
||||
{
|
||||
m_RecalculateRestShape = false;
|
||||
|
||||
var calculateRest = new ShapeMatchingCalculateRestJob()
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
firstIndex = firstIndex,
|
||||
numIndices = numIndices,
|
||||
restComs = restComs,
|
||||
coms = coms,
|
||||
Aqq = Aqq,
|
||||
deformation = plasticDeformations,
|
||||
|
||||
restPositions = solverAbstraction.restPositions.AsNativeArray<float4>(),
|
||||
restOrientations = solverAbstraction.restOrientations.AsNativeArray<quaternion>(),
|
||||
principalRadii = solverAbstraction.principalRadii.AsNativeArray<float4>(),
|
||||
invMasses = solverAbstraction.invMasses.AsNativeArray<float>(),
|
||||
invRotationalMasses = solverAbstraction.invRotationalMasses.AsNativeArray<float>(),
|
||||
};
|
||||
|
||||
inputDeps = calculateRest.Schedule(numIndices.Length, 64, inputDeps);
|
||||
}
|
||||
|
||||
var projectConstraints = new ShapeMatchingConstraintsBatchJob()
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
@@ -120,7 +94,7 @@ namespace Obi
|
||||
restOrientations = solverImplementation.restOrientations,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
invRotationalMasses = solverImplementation.invRotationalMasses,
|
||||
principalRadii = solverImplementation.principalRadii,
|
||||
invInertiaTensors = solverImplementation.invInertiaTensors,
|
||||
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
@@ -153,7 +127,25 @@ namespace Obi
|
||||
|
||||
public void CalculateRestShapeMatching()
|
||||
{
|
||||
m_RecalculateRestShape = true;
|
||||
var deps = ((BurstSolverImpl)constraints.solver).RecalculateInertiaTensors(new JobHandle());
|
||||
|
||||
var calculateRest = new ShapeMatchingCalculateRestJob()
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
firstIndex = firstIndex,
|
||||
numIndices = numIndices,
|
||||
restComs = restComs,
|
||||
coms = coms,
|
||||
Aqq = Aqq,
|
||||
deformation = plasticDeformations,
|
||||
|
||||
restPositions = solverAbstraction.restPositions.AsNativeArray<float4>(),
|
||||
restOrientations = solverAbstraction.restOrientations.AsNativeArray<quaternion>(),
|
||||
invMasses = solverAbstraction.invMasses.AsNativeArray<float>(),
|
||||
invInertiaTensors = solverAbstraction.invInertiaTensors.AsNativeArray<float4>(),
|
||||
};
|
||||
|
||||
calculateRest.Schedule(numIndices.Length, 64, deps).Complete();
|
||||
}
|
||||
|
||||
protected static void RecalculateRestData(int i,
|
||||
@@ -164,10 +156,9 @@ namespace Obi
|
||||
ref NativeArray<float4x4> deformation,
|
||||
ref NativeArray<int> numIndices,
|
||||
ref NativeArray<float> invMasses,
|
||||
ref NativeArray<float> invRotationalMasses,
|
||||
ref NativeArray<float4> restPositions,
|
||||
ref NativeArray<quaternion> restOrientations,
|
||||
ref NativeArray<float4> principalRadii)
|
||||
ref NativeArray<float4> invInertiaTensors)
|
||||
{
|
||||
int k = 0;
|
||||
float maximumMass = 10000;
|
||||
@@ -194,7 +185,7 @@ namespace Obi
|
||||
particleR[3][3] = 0;
|
||||
|
||||
_Rqq += math.mul(particleR,
|
||||
math.mul(BurstMath.GetParticleInertiaTensor(principalRadii[k], invRotationalMasses[k]).asDiagonal(),
|
||||
math.mul(math.rcp(invInertiaTensors[k] + new float4(BurstMath.epsilon)).asDiagonal(),
|
||||
math.transpose(particleR))
|
||||
);
|
||||
|
||||
@@ -234,8 +225,7 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<float4> restPositions;
|
||||
[ReadOnly] public NativeArray<quaternion> restOrientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> invRotationalMasses;
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public NativeArray<float4> invInertiaTensors;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
@@ -247,10 +237,9 @@ namespace Obi
|
||||
ref deformation,
|
||||
ref numIndices,
|
||||
ref invMasses,
|
||||
ref invRotationalMasses,
|
||||
ref restPositions,
|
||||
ref restOrientations,
|
||||
ref principalRadii);
|
||||
ref invInertiaTensors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +265,7 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<quaternion> restOrientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> invRotationalMasses;
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public NativeArray<float4> invInertiaTensors;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<quaternion> orientations;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
@@ -309,7 +298,7 @@ namespace Obi
|
||||
particleRT[3][3] = 0;
|
||||
|
||||
Rpq += math.mul(particleR,
|
||||
math.mul(BurstMath.GetParticleInertiaTensor(principalRadii[k], invRotationalMasses[k]).asDiagonal(),
|
||||
math.mul(math.rcp(invInertiaTensors[k] + new float4(BurstMath.epsilon)).asDiagonal(),
|
||||
math.transpose(particleRT))
|
||||
);
|
||||
|
||||
@@ -338,7 +327,7 @@ namespace Obi
|
||||
linearTransforms[i] = math.mul(Apq_def, Aqq[i]);
|
||||
|
||||
// extract rotation from transform matrix, using warmstarting and few iterations:
|
||||
constraintOrientations[i] = BurstMath.ExtractRotation(Apq_def, constraintOrientations[i], 5);
|
||||
constraintOrientations[i] = BurstMath.ExtractRotation(Apq_def, constraintOrientations[i], 2);
|
||||
|
||||
// finally, obtain rotation matrix:
|
||||
float4x4 R = constraintOrientations[i].toMatrix();
|
||||
@@ -411,10 +400,9 @@ namespace Obi
|
||||
ref deformation,
|
||||
ref numIndices,
|
||||
ref invMasses,
|
||||
ref invRotationalMasses,
|
||||
ref restPositions,
|
||||
ref restOrientations,
|
||||
ref principalRadii);
|
||||
ref invInertiaTensors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,10 +418,9 @@ namespace Obi
|
||||
ref deformation,
|
||||
ref numIndices,
|
||||
ref invMasses,
|
||||
ref invRotationalMasses,
|
||||
ref restPositions,
|
||||
ref restOrientations,
|
||||
ref principalRadii);
|
||||
ref invInertiaTensors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Obi
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new SkinConstraintsBatchJob()
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Obi
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new StitchConstraintsBatchJob()
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Obi
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new StretchShearConstraintsBatchJob()
|
||||
{
|
||||
@@ -126,8 +126,8 @@ namespace Obi
|
||||
// subtract third director vector (0,0,1):
|
||||
gamma[2] -= 1;
|
||||
|
||||
float3 W = new float3((w1 + w2) / (restLengths[i] + BurstMath.epsilon) + invRotationalMasses[q] * 4.0f * restLengths[i]);
|
||||
float3 dlambda = (gamma - compliances * lambdas[i]) / (W + compliances + BurstMath.epsilon);
|
||||
float3 W = new float3((w1 + w2) / (restLengths[i] + BurstMath.epsilon) + invRotationalMasses[q] * 4.0f * restLengths[i] + BurstMath.epsilon);
|
||||
float3 dlambda = (gamma - compliances * lambdas[i]) / (compliances + W);
|
||||
lambdas[i] += dlambda;
|
||||
|
||||
// convert lambda delta lambda back to world space:
|
||||
@@ -142,10 +142,7 @@ namespace Obi
|
||||
// calculate rotation delta:
|
||||
quaternion rotDelta = math.mul(new quaternion(dlambda[0], dlambda[1], dlambda[2], 0.0f),q_e_3_bar);
|
||||
rotDelta.value *= 2.0f * invRotationalMasses[q] * restLengths[i];
|
||||
|
||||
quaternion orDelta = orientationDeltas[q];
|
||||
orDelta.value += rotDelta.value;
|
||||
orientationDeltas[q] = orDelta;
|
||||
orientationDeltas[q] = rotDelta;
|
||||
|
||||
counts[p1]++;
|
||||
counts[p2]++;
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Obi
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new TetherConstraintsBatchJob()
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Obi
|
||||
}
|
||||
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new VolumeConstraintsBatchJob()
|
||||
{
|
||||
@@ -106,7 +106,10 @@ namespace Obi
|
||||
{
|
||||
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;
|
||||
@@ -114,12 +117,21 @@ namespace Obi
|
||||
int i2 = triangles[v + 1];
|
||||
int i3 = triangles[v + 2];
|
||||
|
||||
gradients[i1] = new float4(0, 0, 0, 1);
|
||||
gradients[i2] = new float4(0, 0, 0, 1);
|
||||
gradients[i3] = new float4(0, 0, 0, 1);
|
||||
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;
|
||||
}
|
||||
|
||||
float volume = 0;
|
||||
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;
|
||||
@@ -131,28 +143,14 @@ namespace Obi
|
||||
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 this triangle's volume contribution:
|
||||
volume += math.dot(math.cross(positions[i1].xyz, positions[i2].xyz), positions[i3].xyz) / 6.0f;
|
||||
}
|
||||
|
||||
// calculate constraint denominator (G(Cj)*inv(M)):
|
||||
float denominator = 0;
|
||||
for (int j = 0; j < numTriangles[i]; ++j)
|
||||
for (int j = 0; j < particleCount; ++j)
|
||||
{
|
||||
int v = (firstTriangle[i] + j) * 3;
|
||||
int i1 = triangles[v];
|
||||
int i2 = triangles[v + 1];
|
||||
int i3 = triangles[v + 2];
|
||||
|
||||
denominator += invMasses[i1] * math.lengthsq(gradients[i1].xyz) * gradients[i1].w;
|
||||
gradients[i1] = new float4(gradients[i1].xyz,0);
|
||||
|
||||
denominator += invMasses[i2] * math.lengthsq(gradients[i2].xyz) * gradients[i2].w;
|
||||
gradients[i2] = new float4(gradients[i2].xyz, 0);
|
||||
|
||||
denominator += invMasses[i3] * math.lengthsq(gradients[i3].xyz) * gradients[i3].w;
|
||||
gradients[i3] = new float4(gradients[i3].xyz, 0);
|
||||
int p = particleIndices[j];
|
||||
denominator += invMasses[p] * math.lengthsq(gradients[p]);
|
||||
}
|
||||
|
||||
// equality constraint: volume - pressure * rest volume = 0
|
||||
@@ -163,21 +161,11 @@ namespace Obi
|
||||
lambdas[i] += dlambda;
|
||||
|
||||
// calculate position deltas:
|
||||
for (int j = 0; j < numTriangles[i]; ++j)
|
||||
for (int j = 0; j < particleCount; ++j)
|
||||
{
|
||||
int v = (firstTriangle[i] + j) * 3;
|
||||
int i1 = triangles[v];
|
||||
int i2 = triangles[v + 1];
|
||||
int i3 = triangles[v + 2];
|
||||
|
||||
deltas[i1] += dlambda * invMasses[i1] * gradients[i1];
|
||||
counts[i1]++;
|
||||
|
||||
deltas[i2] += dlambda * invMasses[i2] * gradients[i2];
|
||||
counts[i2]++;
|
||||
|
||||
deltas[i3] += dlambda * invMasses[i3] * gradients[i3];
|
||||
counts[i3]++;
|
||||
int p = particleIndices[j];
|
||||
deltas[p] += dlambda * invMasses[p] * gradients[p];
|
||||
counts[p]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,16 +24,16 @@ namespace Obi
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public BurstAabb(float4 v1, float4 v2, float4 v3, float4 margin)
|
||||
public BurstAabb(float4 v1, float4 v2, float4 v3, float margin)
|
||||
{
|
||||
min = math.min(math.min(v1, v2), v3) - margin;
|
||||
max = math.max(math.max(v1, v2), v3) + margin;
|
||||
min = math.min(math.min(v1, v2), v3) - new float4(margin, margin, margin, 0);
|
||||
max = math.max(math.max(v1, v2), v3) + new float4(margin, margin, margin, 0);
|
||||
}
|
||||
|
||||
public BurstAabb(float4 v1, float4 v2, float4 margin)
|
||||
public BurstAabb(float2 v1, float2 v2, float margin)
|
||||
{
|
||||
min = math.min(v1, v2) - margin;
|
||||
max = math.max(v1, v2) + margin;
|
||||
min = new float4(math.min(v1, v2) - new float2(margin, margin),0,0);
|
||||
max = new float4(math.max(v1, v2) + new float2(margin, margin),0,0);
|
||||
}
|
||||
|
||||
public BurstAabb(float4 previousPosition, float4 position, float radius)
|
||||
@@ -50,7 +50,7 @@ namespace Obi
|
||||
|
||||
public float MaxAxisLength()
|
||||
{
|
||||
return math.cmax((max - min).xyz);
|
||||
return math.cmax(max - min);
|
||||
}
|
||||
|
||||
public void EncapsulateParticle(float4 position, float radius)
|
||||
|
||||
@@ -36,13 +36,6 @@ namespace Obi
|
||||
1 / scale);
|
||||
}
|
||||
|
||||
public BurstAffineTransform Integrate(float4 linearVelocity, float4 angularVelocity, float dt)
|
||||
{
|
||||
return new BurstAffineTransform(BurstIntegration.IntegrateLinear(translation, linearVelocity, dt),
|
||||
BurstIntegration.IntegrateAngular(rotation, angularVelocity, dt),
|
||||
scale);
|
||||
}
|
||||
|
||||
public BurstAffineTransform Interpolate(BurstAffineTransform other, float translationalMu, float rotationalMu, float scaleMu)
|
||||
{
|
||||
return new BurstAffineTransform(math.lerp(translation, other.translation, translationalMu),
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Obi
|
||||
|
||||
switch (frictionCombineMode)
|
||||
{
|
||||
default: // average
|
||||
case Oni.MaterialCombineMode.Average:
|
||||
result.dynamicFriction = (a.dynamicFriction + b.dynamicFriction) * 0.5f;
|
||||
result.staticFriction = (a.staticFriction + b.staticFriction) * 0.5f;
|
||||
result.rollingFriction = (a.rollingFriction + b.rollingFriction) * 0.5f;
|
||||
@@ -37,22 +37,22 @@ namespace Obi
|
||||
result.rollingFriction = math.min(a.rollingFriction, b.rollingFriction);
|
||||
break;
|
||||
|
||||
case Oni.MaterialCombineMode.Multiply:
|
||||
result.dynamicFriction = a.dynamicFriction * b.dynamicFriction;
|
||||
result.staticFriction = a.staticFriction * b.staticFriction;
|
||||
result.rollingFriction = a.rollingFriction * b.rollingFriction;
|
||||
break;
|
||||
|
||||
case Oni.MaterialCombineMode.Maximum:
|
||||
result.dynamicFriction = math.max(a.dynamicFriction, b.dynamicFriction);
|
||||
result.staticFriction = math.max(a.staticFriction, b.staticFriction);
|
||||
result.rollingFriction = math.max(a.rollingFriction, b.rollingFriction);
|
||||
break;
|
||||
|
||||
case Oni.MaterialCombineMode.Multiply:
|
||||
result.dynamicFriction = a.dynamicFriction * b.dynamicFriction;
|
||||
result.staticFriction = a.staticFriction * b.staticFriction;
|
||||
result.rollingFriction = a.rollingFriction * b.rollingFriction;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (stickCombineMode)
|
||||
{
|
||||
default: // average
|
||||
case Oni.MaterialCombineMode.Average:
|
||||
result.stickiness = (a.stickiness + b.stickiness) * 0.5f;
|
||||
break;
|
||||
|
||||
@@ -60,13 +60,13 @@ namespace Obi
|
||||
result.stickiness = math.min(a.stickiness, b.stickiness);
|
||||
break;
|
||||
|
||||
case Oni.MaterialCombineMode.Multiply:
|
||||
result.stickiness = a.stickiness * b.stickiness;
|
||||
break;
|
||||
|
||||
case Oni.MaterialCombineMode.Maximum:
|
||||
result.stickiness = math.max(a.stickiness, b.stickiness);
|
||||
break;
|
||||
|
||||
case Oni.MaterialCombineMode.Multiply:
|
||||
result.stickiness = a.stickiness * b.stickiness;
|
||||
break;
|
||||
}
|
||||
|
||||
result.stickDistance = math.max(a.stickDistance, b.stickDistance);
|
||||
|
||||
@@ -38,11 +38,6 @@ namespace Obi
|
||||
angularAcceleration = float4.zero;
|
||||
}
|
||||
|
||||
public float4 VelocityAtPoint(float4 point)
|
||||
{
|
||||
return velocity + new float4(math.cross(angularVelocity.xyz, (point - prevFrame.translation).xyz), 0);
|
||||
}
|
||||
|
||||
public void Update(float4 position, float4 scale, quaternion rotation, float dt)
|
||||
{
|
||||
prevFrame = frame;
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System.Collections.Generic;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstPrefixSum
|
||||
{
|
||||
private int inputSize;
|
||||
private const int numBlocks = 8;
|
||||
|
||||
private NativeArray<int> blockSums;
|
||||
|
||||
public BurstPrefixSum(int inputSize)
|
||||
{
|
||||
this.inputSize = inputSize;
|
||||
blockSums = new NativeArray<int>(numBlocks, Allocator.Persistent);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (blockSums.IsCreated)
|
||||
blockSums.Dispose();
|
||||
}
|
||||
|
||||
public unsafe JobHandle Sum(NativeArray<int> input, NativeArray<int> result, int* count, JobHandle inputDeps)
|
||||
{
|
||||
|
||||
// calculate partial prefix sums, one per block:
|
||||
var job = new BlockSumJob
|
||||
{
|
||||
input = input,
|
||||
output = result,
|
||||
blocks = blockSums,
|
||||
count = count
|
||||
};
|
||||
inputDeps = job.Schedule(numBlocks, 1, inputDeps);
|
||||
|
||||
var job3 = new BlockSum
|
||||
{
|
||||
blocks = blockSums
|
||||
};
|
||||
inputDeps = job3.Schedule(inputDeps);
|
||||
|
||||
// add the scanned partial block sums to the result:
|
||||
var job2 = new PrefixSumJob
|
||||
{
|
||||
prefixBlocks = blockSums,
|
||||
output = result,
|
||||
count = count
|
||||
};
|
||||
return job2.Schedule(numBlocks, 1, inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct BlockSumJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> input;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> output;
|
||||
public NativeArray<int> blocks;
|
||||
|
||||
[ReadOnly] [NativeDisableUnsafePtrRestriction] public int* count;
|
||||
|
||||
public void Execute(int block)
|
||||
{
|
||||
int length = *count + 1; // add 1 to get total sum in last element+1
|
||||
int blockSize = (int)math.ceil(length / (float)numBlocks);
|
||||
|
||||
int start = block * blockSize;
|
||||
int end = math.min(start + blockSize, length);
|
||||
|
||||
output[start] = 0;
|
||||
|
||||
if (blockSize == 0) { blocks[block] = 0; return; }
|
||||
|
||||
for (int i = start + 1; i < end; ++i)
|
||||
output[i] = output[i - 1] + input[i - 1];
|
||||
|
||||
blocks[block] = output[end - 1] + input[end - 1];
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct BlockSum : IJob
|
||||
{
|
||||
public NativeArray<int> blocks;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
int aux = blocks[0];
|
||||
blocks[0] = 0;
|
||||
|
||||
for (int i = 1; i < blocks.Length; ++i)
|
||||
{
|
||||
int a = blocks[i];
|
||||
blocks[i] = blocks[i - 1] + aux;
|
||||
aux = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct PrefixSumJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> prefixBlocks;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> output;
|
||||
|
||||
[ReadOnly] [NativeDisableUnsafePtrRestriction] public int* count;
|
||||
|
||||
public void Execute(int block)
|
||||
{
|
||||
int length = *count + 1; // add 1 to get total sum in last element+1
|
||||
int blockSize = (int)math.ceil(length / (float)numBlocks);
|
||||
|
||||
int start = block * blockSize;
|
||||
int end = math.min(start + blockSize, length);
|
||||
|
||||
for (int i = start; i < end; ++i)
|
||||
output[i] += prefixBlocks[block];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -9,7 +9,7 @@ namespace Obi
|
||||
public float4 size;
|
||||
public QueryShape.QueryType type;
|
||||
public float contactOffset;
|
||||
public float maxDistance;
|
||||
public float distance;
|
||||
public int filter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Obi
|
||||
public float4 com;
|
||||
public float inverseMass;
|
||||
|
||||
public int constraintCount;
|
||||
private int pad0;
|
||||
private int pad1;
|
||||
private int pad2;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
@@ -9,7 +9,7 @@ using Unity.Jobs;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class ConstraintSorter<T> where T : unmanaged, IConstraint
|
||||
public class ConstraintSorter<T> where T : unmanaged, IConstraint
|
||||
{
|
||||
|
||||
public struct ConstraintComparer<K> : IComparer<K> where K : IConstraint
|
||||
@@ -28,7 +28,7 @@ namespace Obi
|
||||
public JobHandle SortConstraints(int particleCount,
|
||||
NativeArray<T> constraints,
|
||||
ref NativeArray<T> sortedConstraints,
|
||||
JobHandle handle)
|
||||
JobHandle handle)
|
||||
{
|
||||
// Count the amount of digits in the largest particle index that can be referenced by a constraint:
|
||||
NativeArray<int> totalCountUpToDigit = new NativeArray<int>(particleCount + 1, Allocator.TempJob);
|
||||
@@ -59,16 +59,16 @@ namespace Obi
|
||||
{
|
||||
InOutArray = sortedConstraints,
|
||||
NextElementIndex = totalCountUpToDigit,
|
||||
comparer = new ConstraintComparer<T>()
|
||||
comparer = new ConstraintComparer<T>()
|
||||
}.Schedule(totalCountUpToDigit.Length, numPerBatch, handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct CountSortPerFirstParticleJob : IJob
|
||||
public struct CountSortPerFirstParticleJob : IJob
|
||||
{
|
||||
[ReadOnly][NativeDisableContainerSafetyRestriction] public NativeArray<T> input;
|
||||
[ReadOnly] [NativeDisableContainerSafetyRestriction] public NativeArray<T> input;
|
||||
public NativeArray<T> output;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] public NativeArray<int> digitCount;
|
||||
@@ -117,7 +117,7 @@ namespace Obi
|
||||
[NativeDisableContainerSafetyRestriction] public NativeArray<T> InOutArray;
|
||||
|
||||
// Typically lastDigitIndex is resulting RadixSortPerBodyAJob.digitCount. nextElementIndex[i] = index of first element with bodyA index == i + 1
|
||||
[NativeDisableContainerSafetyRestriction][DeallocateOnJobCompletion] public NativeArray<int> NextElementIndex;
|
||||
[NativeDisableContainerSafetyRestriction] [DeallocateOnJobCompletion] public NativeArray<int> NextElementIndex;
|
||||
|
||||
[ReadOnly] public ConstraintComparer<T> comparer;
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
namespace Obi
|
||||
using System;
|
||||
|
||||
public interface IConstraint
|
||||
{
|
||||
public interface IConstraint
|
||||
{
|
||||
int GetParticleCount();
|
||||
int GetParticle(int index);
|
||||
}
|
||||
int GetParticleCount();
|
||||
int GetParticle(int index);
|
||||
}
|
||||
#endif
|
||||
@@ -1,76 +1,118 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
public struct GridHash
|
||||
{
|
||||
public struct GridHash
|
||||
public readonly static int3[] cellOffsets3D =
|
||||
{
|
||||
public readonly static int3[] cellOffsets3D =
|
||||
{
|
||||
new int3(1,0,0),
|
||||
new int3(0,1,0),
|
||||
new int3(1,1,0),
|
||||
new int3(0,0,1),
|
||||
new int3(1,0,1),
|
||||
new int3(0,1,1),
|
||||
new int3(1,1,1),
|
||||
new int3(-1,1,0),
|
||||
new int3(-1,-1,1),
|
||||
new int3(0,-1,1),
|
||||
new int3(1,-1,1),
|
||||
new int3(-1,0,1),
|
||||
new int3(-1,1,1)
|
||||
};
|
||||
new int3(1,0,0),
|
||||
new int3(0,1,0),
|
||||
new int3(1,1,0),
|
||||
new int3(0,0,1),
|
||||
new int3(1,0,1),
|
||||
new int3(0,1,1),
|
||||
new int3(1,1,1),
|
||||
new int3(-1,1,0),
|
||||
new int3(-1,-1,1),
|
||||
new int3(0,-1,1),
|
||||
new int3(1,-1,1),
|
||||
new int3(-1,0,1),
|
||||
new int3(-1,1,1)
|
||||
};
|
||||
|
||||
public readonly static int3[] cellOffsets =
|
||||
{
|
||||
new int3(0, 0, 0),
|
||||
new int3(-1, 0, 0),
|
||||
new int3(0, -1, 0),
|
||||
new int3(0, 0, -1),
|
||||
new int3(1, 0, 0),
|
||||
new int3(0, 1, 0),
|
||||
new int3(0, 0, 1)
|
||||
};
|
||||
public readonly static int3[] cellOffsets =
|
||||
{
|
||||
new int3(0, 0, 0),
|
||||
new int3(-1, 0, 0),
|
||||
new int3(0, -1, 0),
|
||||
new int3(0, 0, -1),
|
||||
new int3(1, 0, 0),
|
||||
new int3(0, 1, 0),
|
||||
new int3(0, 0, 1)
|
||||
};
|
||||
|
||||
public readonly static int2[] cell2DOffsets =
|
||||
{
|
||||
new int2(0, 0),
|
||||
new int2(-1, 0),
|
||||
new int2(0, -1),
|
||||
new int2(1, 0),
|
||||
new int2(0, 1),
|
||||
};
|
||||
public readonly static int2[] cell2DOffsets =
|
||||
{
|
||||
new int2(0, 0),
|
||||
new int2(-1, 0),
|
||||
new int2(0, -1),
|
||||
new int2(1, 0),
|
||||
new int2(0, 1),
|
||||
};
|
||||
|
||||
public static int3 Quantize(float3 v, float cellSize)
|
||||
public static int Hash(float3 v, float cellSize)
|
||||
{
|
||||
return Hash(Quantize(v, cellSize));
|
||||
}
|
||||
|
||||
public static int3 Quantize(float3 v, float cellSize)
|
||||
{
|
||||
return new int3(math.floor(v / cellSize));
|
||||
}
|
||||
|
||||
public static int Hash(float2 v, float cellSize)
|
||||
{
|
||||
return Hash(Quantize(v, cellSize));
|
||||
}
|
||||
|
||||
public static int2 Quantize(float2 v, float cellSize)
|
||||
{
|
||||
return new int2(math.floor(v / cellSize));
|
||||
}
|
||||
|
||||
public static int Hash(int3 grid)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return new int3(math.floor(v / cellSize));
|
||||
// Simple int3 hash based on a pseudo mix of :
|
||||
// 1) https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||
// 2) https://en.wikipedia.org/wiki/Jenkins_hash_function
|
||||
int hash = grid.x;
|
||||
hash = (hash * 397) ^ grid.y;
|
||||
hash = (hash * 397) ^ grid.z;
|
||||
hash += hash << 3;
|
||||
hash ^= hash >> 11;
|
||||
hash += hash << 15;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public static int2 Quantize(float2 v, float cellSize)
|
||||
public static int Hash(int2 grid)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return new int2(math.floor(v / cellSize));
|
||||
// Simple int3 hash based on a pseudo mix of :
|
||||
// 1) https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
|
||||
// 2) https://en.wikipedia.org/wiki/Jenkins_hash_function
|
||||
int hash = grid.x;
|
||||
hash = (hash * 397) ^ grid.y;
|
||||
hash += hash << 3;
|
||||
hash ^= hash >> 11;
|
||||
hash += hash << 15;
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Hash(in int4 cellIndex, int maxCells)
|
||||
{
|
||||
const int p1 = 73856093;
|
||||
const int p2 = 19349663;
|
||||
const int p3 = 83492791;
|
||||
const int p4 = 10380569;
|
||||
return math.abs(p1 * cellIndex.x ^ p2 * cellIndex.y ^ p3 * cellIndex.z ^ p4 * cellIndex.w) % maxCells;
|
||||
}
|
||||
public static ulong Hash(ulong hash, ulong key)
|
||||
{
|
||||
const ulong m = 0xc6a4a7935bd1e995UL;
|
||||
const int r = 47;
|
||||
|
||||
public static int Hash(in int3 cellIndex, int maxCells)
|
||||
{
|
||||
const int p1 = 73856093;
|
||||
const int p2 = 19349663;
|
||||
const int p3 = 83492791;
|
||||
return ((p1 * cellIndex.x ^ p2 * cellIndex.y ^ p3 * cellIndex.z) & 0x7fffffff) % maxCells;
|
||||
ulong h = hash;
|
||||
ulong k = key;
|
||||
|
||||
/*var index = cellIndex - new int3(-32, -32, -32);
|
||||
return index.x + index.y * 64 + index.z * 64 * 64;*/
|
||||
}
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
|
||||
h ^= h >> r;
|
||||
h *= m;
|
||||
h ^= h >> r;
|
||||
|
||||
return h;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -23,9 +23,7 @@ namespace Obi
|
||||
public unsafe struct NativeMultilevelGrid<T> : IDisposable where T : unmanaged, IEquatable<T>
|
||||
{
|
||||
|
||||
public const float minSize = 0.01f; // minimum cell size is 1 centimeter, enough for very small particles.
|
||||
public const int minLevel = -6; // grid level for minSize.
|
||||
public const int maxLevel = 17;
|
||||
public const float minSize = 0.01f;
|
||||
|
||||
/**
|
||||
* A cell in the multilevel grid. Coords are 4-dimensional, the 4th component is the grid level.
|
||||
@@ -60,7 +58,7 @@ namespace Obi
|
||||
{
|
||||
get
|
||||
{
|
||||
return contents.ElementAt(index);
|
||||
return UnsafeUtility.ReadArrayElement<K>(contents.Ptr, index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +69,11 @@ namespace Obi
|
||||
|
||||
public bool Remove(K entity)
|
||||
{
|
||||
int index = contents.IndexOf(entity);
|
||||
//int index = contents.IndexOf(entity);
|
||||
int index = -1;
|
||||
for (int i = 0; i < contents.Length; ++i)
|
||||
if (contents[i].Equals(entity)) { index = i; break; }
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
contents.RemoveAtSwapBack(index);
|
||||
@@ -86,15 +88,15 @@ namespace Obi
|
||||
}
|
||||
}
|
||||
|
||||
public NativeParallelHashMap<int4, int> grid;
|
||||
public NativeHashMap<int4, int> grid;
|
||||
public NativeList<Cell<T>> usedCells;
|
||||
public NativeParallelHashMap<int, int> populatedLevels;
|
||||
public NativeHashMap<int, int> populatedLevels;
|
||||
|
||||
public NativeMultilevelGrid(int capacity, Allocator label)
|
||||
{
|
||||
grid = new NativeParallelHashMap<int4, int>(capacity, label);
|
||||
grid = new NativeHashMap<int4, int>(capacity, label);
|
||||
usedCells = new NativeList<Cell<T>>(label);
|
||||
populatedLevels = new NativeParallelHashMap<int, int>(10, label);
|
||||
populatedLevels = new NativeHashMap<int, int>(10, label);
|
||||
}
|
||||
|
||||
public int CellCount
|
||||
@@ -170,16 +172,14 @@ namespace Obi
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GridLevelForSize(float size)
|
||||
{
|
||||
// the magic number is 1/log(2), used because log_a(x) = log_b(x) / log_b(a)
|
||||
// level is clamped between MIN_LEVEL and MAX_LEVEL, then remapped to (0, MAX_LEVEL - MIN_LEVEL)
|
||||
// this allows us to avoid InterlockedMax issues on GPU, since it doesn't work on negative numbers on some APIs.
|
||||
return math.clamp((int)math.ceil(math.log(size) * 1.44269504089f), minLevel, maxLevel) - minLevel;
|
||||
// the magic number is 1/log(2)
|
||||
return (int)math.ceil(math.log(math.max(size,minSize)) * 1.44269504089f);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float CellSizeOfLevel(int level)
|
||||
{
|
||||
return math.exp2(level + minLevel);
|
||||
return math.exp2(level);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,7 +187,7 @@ namespace Obi
|
||||
*/
|
||||
public static int4 GetParentCellCoords(int4 cellCoords, int level)
|
||||
{
|
||||
float decimation = math.exp2(level - cellCoords[3]);
|
||||
float decimation = CellSizeOfLevel(level - cellCoords[3]);
|
||||
int4 cell = (int4)math.floor((float4)cellCoords / decimation);
|
||||
cell[3] = level;
|
||||
return cell;
|
||||
|
||||
@@ -4,7 +4,6 @@ using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
@@ -21,14 +20,34 @@ namespace Obi
|
||||
public NativeQueue<BurstContact> particleContactQueue;
|
||||
public NativeQueue<FluidInteraction> fluidInteractionQueue;
|
||||
|
||||
|
||||
[BurstCompile]
|
||||
struct CalculateCellCoords : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<BurstAabb> simplexBounds;
|
||||
public NativeArray<int4> cellCoords;
|
||||
[ReadOnly] public bool is2D;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int level = NativeMultilevelGrid<int>.GridLevelForSize(simplexBounds[i].AverageAxisLength());
|
||||
float cellSize = NativeMultilevelGrid<int>.CellSizeOfLevel(level);
|
||||
|
||||
// get new particle cell coordinate:
|
||||
int4 newCellCoord = new int4(GridHash.Quantize(simplexBounds[i].center.xyz, cellSize), level);
|
||||
|
||||
// if the solver is 2D, project the particle to the z = 0 cell.
|
||||
if (is2D) newCellCoord[2] = 0;
|
||||
|
||||
cellCoords[i] = newCellCoord;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct UpdateGrid : IJob
|
||||
{
|
||||
public NativeMultilevelGrid<int> grid;
|
||||
[ReadOnly] public NativeArray<BurstAabb> simplexBounds;
|
||||
public NativeArray<int4> cellCoords;
|
||||
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
[ReadOnly] public NativeArray<int4> cellCoords;
|
||||
[ReadOnly] public int simplexCount;
|
||||
|
||||
public void Execute()
|
||||
@@ -37,17 +56,6 @@ namespace Obi
|
||||
|
||||
for (int i = 0; i < simplexCount; ++i)
|
||||
{
|
||||
int level = NativeMultilevelGrid<int>.GridLevelForSize(simplexBounds[i].MaxAxisLength());
|
||||
float cellSize = NativeMultilevelGrid<int>.CellSizeOfLevel(level);
|
||||
|
||||
// get new cell coordinate:
|
||||
int4 newCellCoord = new int4(GridHash.Quantize(simplexBounds[i].center.xyz, cellSize), level);
|
||||
|
||||
// if the solver is 2D, project to the z = 0 cell.
|
||||
if (parameters.mode == Oni.SolverParameters.Mode.Mode2D) newCellCoord[2] = 0;
|
||||
|
||||
cellCoords[i] = newCellCoord;
|
||||
|
||||
// add to new cell:
|
||||
int cellIndex = grid.GetOrCreateCell(cellCoords[i]);
|
||||
var newCell = grid.usedCells[cellIndex];
|
||||
@@ -61,7 +69,6 @@ namespace Obi
|
||||
public struct GenerateParticleParticleContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeMultilevelGrid<int> grid;
|
||||
|
||||
[DeallocateOnJobCompletion]
|
||||
[ReadOnly] public NativeArray<int> gridLevels;
|
||||
|
||||
@@ -73,13 +80,14 @@ namespace Obi
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<float4> normals;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials;
|
||||
[ReadOnly] public NativeArray<float> fluidRadii;
|
||||
[ReadOnly] public NativeArray<int> phases;
|
||||
[ReadOnly] public NativeArray<int> filters;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
[ReadOnly] public NativeArray<BurstAabb> simplexBounds;
|
||||
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
@@ -157,7 +165,7 @@ namespace Obi
|
||||
}
|
||||
|
||||
// neighboring cells in levels above the current one:
|
||||
int levelIndex = gridLevels.IndexOf<int, int>(cellCoords.w);
|
||||
int levelIndex = gridLevels.IndexOf<int,int>(cellCoords.w);
|
||||
if (levelIndex >= 0)
|
||||
{
|
||||
levelIndex++;
|
||||
@@ -194,10 +202,10 @@ namespace Obi
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
group = math.max(group, ObiUtils.GetGroupFromPhase(phases[particleIndex]));
|
||||
flags |= ObiUtils.GetFlagsFromPhase(phases[particleIndex]);
|
||||
category |= filters[particleIndex] & ObiUtils.FilterCategoryBitmask;
|
||||
mask |= (filters[particleIndex] & ObiUtils.FilterMaskBitmask) >> 16;
|
||||
group = math.max(group, ObiUtils.GetGroupFromPhase(phases[particleIndex]));
|
||||
restPositionsEnabled |= restPositions[particleIndex].w > 0.5f;
|
||||
}
|
||||
|
||||
@@ -206,6 +214,10 @@ namespace Obi
|
||||
|
||||
private void InteractionTest(int A, int B, ref BurstSimplex simplexShape)
|
||||
{
|
||||
// skip the pair if their bounds don't intersect:
|
||||
if (!simplexBounds[A].IntersectsAabb(simplexBounds[B]))
|
||||
return;
|
||||
|
||||
// get the start index and size of each simplex:
|
||||
int simplexStartA = simplexCounts.GetSimplexStartAndSize(A, out int simplexSizeA);
|
||||
int simplexStartB = simplexCounts.GetSimplexStartAndSize(B, out int simplexSizeB);
|
||||
@@ -235,14 +247,17 @@ namespace Obi
|
||||
// if all simplices are fluid, check their smoothing radii:
|
||||
if ((flagsA & ObiUtils.ParticleFlags.Fluid) != 0 && (flagsB & ObiUtils.ParticleFlags.Fluid) != 0)
|
||||
{
|
||||
// for fluid we only consider the first particle in each simplex.
|
||||
int particleA = simplices[simplexStartA];
|
||||
int particleB = simplices[simplexStartB];
|
||||
|
||||
// Calculate particle center distance:
|
||||
float d2 = math.lengthsq(positions[particleA].xyz - positions[particleB].xyz);
|
||||
// for fluid we only consider the first particle in each simplex.
|
||||
float4 predictedPositionA = positions[particleA] + velocities[particleA] * dt;
|
||||
float4 predictedPositionB = positions[particleB] + velocities[particleB] * dt;
|
||||
|
||||
float fluidDistance = math.max(fluidMaterials[particleA].x, fluidMaterials[particleB].x) + collisionMargin;
|
||||
// Calculate particle center distance:
|
||||
float d2 = math.lengthsq(predictedPositionA - predictedPositionB);
|
||||
|
||||
float fluidDistance = math.max(fluidRadii[particleA], fluidRadii[particleB]);
|
||||
if (d2 <= fluidDistance * fluidDistance)
|
||||
{
|
||||
fluidInteractionsQueue.Enqueue(new FluidInteraction { particleA = particleA, particleB = particleB });
|
||||
@@ -284,7 +299,7 @@ namespace Obi
|
||||
|
||||
// compare distance along contact normal with radius.
|
||||
if (math.dot(simplexPoint - restPoint.point, restPoint.normal) < simplexRadiusA + simplexRadiusB)
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSizeA);
|
||||
@@ -295,16 +310,13 @@ namespace Obi
|
||||
simplices, simplexStartA, simplexSizeA, ref simplexBary, out simplexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
simplexRadiusA = 0; simplexRadiusB = 0;
|
||||
float4 velocityA = float4.zero, velocityB = float4.zero, normalA = float4.zero, normalB = float4.zero;
|
||||
float invMassA = 0, invMassB = 0;
|
||||
float4 velocityA = float4.zero, velocityB = float4.zero, normalB = float4.zero;
|
||||
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartA + j];
|
||||
simplexRadiusA += radii[particleIndex].x * simplexBary[j];
|
||||
velocityA += velocities[particleIndex] * simplexBary[j];
|
||||
normalA += (normals[particleIndex].w < 0 ? new float4(math.rotate(orientations[particleIndex],normals[particleIndex].xyz), normals[particleIndex].w) : normals[particleIndex]) * simplexBary[j];
|
||||
invMassA += invMasses[particleIndex] * simplexBary[j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
@@ -312,53 +324,111 @@ namespace Obi
|
||||
int particleIndex = simplices[simplexStartB + j];
|
||||
simplexRadiusB += radii[particleIndex].x * surfacePoint.bary[j];
|
||||
velocityB += velocities[particleIndex] * surfacePoint.bary[j];
|
||||
normalB += (normals[particleIndex].w < 0 ? new float4(math.rotate(orientations[particleIndex], normals[particleIndex].xyz), normals[particleIndex].w) : normals[particleIndex]) * surfacePoint.bary[j];
|
||||
invMassB += invMasses[particleIndex] * simplexBary[j];
|
||||
normalB += normals[particleIndex] * surfacePoint.bary[j];
|
||||
}
|
||||
|
||||
// no contact between fixed simplices:
|
||||
//if (!(invMassA > 0 || invMassB > 0))
|
||||
// return;
|
||||
|
||||
float dAB = math.dot(simplexPoint - surfacePoint.point, surfacePoint.normal);
|
||||
float vel = math.dot(velocityA - velocityB, surfacePoint.normal);
|
||||
float vel = math.dot(velocityA - velocityB, surfacePoint.normal);
|
||||
|
||||
// check if the projected velocity along the contact normal will get us within collision distance.
|
||||
if (vel * dt + dAB <= simplexRadiusA + simplexRadiusB + collisionMargin)
|
||||
{
|
||||
// adapt collision normal for one-sided simplices:
|
||||
if ((flagsB & ObiUtils.ParticleFlags.OneSided) != 0 && categoryA < categoryB)
|
||||
BurstMath.OneSidedNormal(normalB, ref surfacePoint.normal);
|
||||
BurstMath.OneSidedNormal(normalB, ref surfacePoint.normal);
|
||||
|
||||
// during inter-collision, if either particle contains SDF data and they overlap:
|
||||
if (groupA != groupB && (normalB.w < 0 || normalA.w < 0) && dAB * 1.05f <= simplexRadiusA + simplexRadiusB)
|
||||
{
|
||||
// as normal, pick SDF gradient belonging to least penetration distance:
|
||||
float4 nij = normalB;
|
||||
if (normalB.w >= 0 || (normalA.w < 0 && normalB.w < normalA.w))
|
||||
nij = new float4(-normalA.xyz, normalA.w);
|
||||
|
||||
// for boundary particles, use one sided sphere normal:
|
||||
if (math.abs(nij.w) <= math.max(simplexRadiusA, simplexRadiusB) * 1.5f)
|
||||
BurstMath.OneSidedNormal(nij, ref surfacePoint.normal);
|
||||
else
|
||||
surfacePoint.normal = nij;
|
||||
}
|
||||
|
||||
surfacePoint.normal.w = 0;
|
||||
contactsQueue.Enqueue(new BurstContact
|
||||
contactsQueue.Enqueue(new BurstContact()
|
||||
{
|
||||
bodyA = A,
|
||||
bodyB = B,
|
||||
pointA = simplexBary,
|
||||
pointB = surfacePoint.bary,
|
||||
normal = surfacePoint.normal
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct InterpolateDiffusePropertiesJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeMultilevelGrid<int> grid;
|
||||
|
||||
[DeallocateOnJobCompletion]
|
||||
[ReadOnly] public NativeArray<int4> cellOffsets;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> properties;
|
||||
[ReadOnly] public NativeArray<float4> diffusePositions;
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
|
||||
public NativeArray<float4> diffuseProperties;
|
||||
public NativeArray<int> neighbourCount;
|
||||
|
||||
[DeallocateOnJobCompletion]
|
||||
[ReadOnly] public NativeArray<int> gridLevels;
|
||||
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
[ReadOnly] public bool mode2D;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
neighbourCount[p] = 0;
|
||||
float4 diffuseProperty = float4.zero;
|
||||
float kernelSum = 0;
|
||||
|
||||
int offsetCount = mode2D ? 4 : 8;
|
||||
|
||||
float4 solverDiffusePosition = inertialFrame.frame.InverseTransformPoint(diffusePositions[p]);
|
||||
|
||||
for (int k = 0; k < gridLevels.Length; ++k)
|
||||
{
|
||||
int l = gridLevels[k];
|
||||
float radius = NativeMultilevelGrid<int>.CellSizeOfLevel(l);
|
||||
|
||||
float4 cellCoords = math.floor(solverDiffusePosition / radius);
|
||||
|
||||
cellCoords[3] = 0;
|
||||
if (mode2D)
|
||||
cellCoords[2] = 0;
|
||||
|
||||
float4 posInCell = solverDiffusePosition - (cellCoords * radius + new float4(radius * 0.5f));
|
||||
int4 quadrant = (int4)math.sign(posInCell);
|
||||
|
||||
quadrant[3] = l;
|
||||
|
||||
for (int i = 0; i < offsetCount; ++i)
|
||||
{
|
||||
int cellIndex;
|
||||
if (grid.TryGetCellIndex((int4)cellCoords + cellOffsets[i] * quadrant, out cellIndex))
|
||||
{
|
||||
var cell = grid.usedCells[cellIndex];
|
||||
for (int n = 0; n < cell.Length; ++n)
|
||||
{
|
||||
float4 r = solverDiffusePosition - positions[cell[n]];
|
||||
r[3] = 0;
|
||||
if (mode2D)
|
||||
r[2] = 0;
|
||||
|
||||
float d = math.length(r);
|
||||
if (d <= radius)
|
||||
{
|
||||
float w = densityKernel.W(d, radius);
|
||||
kernelSum += w;
|
||||
diffuseProperty += properties[cell[n]] * w;
|
||||
neighbourCount[p]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kernelSum > BurstMath.epsilon)
|
||||
diffuseProperties[p] = diffuseProperty / kernelSum;
|
||||
}
|
||||
}
|
||||
|
||||
public ParticleGrid()
|
||||
{
|
||||
this.grid = new NativeMultilevelGrid<int>(1000, Allocator.Persistent);
|
||||
@@ -366,15 +436,22 @@ namespace Obi
|
||||
this.fluidInteractionQueue = new NativeQueue<FluidInteraction>(Allocator.Persistent);
|
||||
}
|
||||
|
||||
public void Update(BurstSolverImpl solver, JobHandle inputDeps)
|
||||
public void Update(BurstSolverImpl solver, float deltaTime, JobHandle inputDeps)
|
||||
{
|
||||
var calculateCells = new CalculateCellCoords
|
||||
{
|
||||
simplexBounds = solver.simplexBounds,
|
||||
cellCoords = solver.cellCoords,
|
||||
is2D = solver.abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D,
|
||||
};
|
||||
|
||||
inputDeps = calculateCells.Schedule(solver.simplexCounts.simplexCount, 4, inputDeps);
|
||||
|
||||
var updateGrid = new UpdateGrid
|
||||
{
|
||||
grid = grid,
|
||||
simplexBounds = solver.simplexBounds,
|
||||
simplexCount = solver.simplexCounts.simplexCount,
|
||||
cellCoords = solver.cellCoords,
|
||||
parameters = solver.abstraction.parameters
|
||||
simplexCount = solver.simplexCounts.simplexCount
|
||||
};
|
||||
updateGrid.Schedule(inputDeps).Complete();
|
||||
}
|
||||
@@ -395,12 +472,13 @@ namespace Obi
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
normals = solver.normals,
|
||||
fluidMaterials = solver.fluidMaterials,
|
||||
fluidRadii = solver.smoothingRadii,
|
||||
phases = solver.phases,
|
||||
filters = solver.filters,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
simplexBounds = solver.simplexBounds,
|
||||
|
||||
particleMaterialIndices = solver.abstraction.collisionMaterials.AsNativeArray<int>(),
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
@@ -416,11 +494,49 @@ namespace Obi
|
||||
return generateParticleContactsJob.Schedule(grid.CellCount, 1);
|
||||
}
|
||||
|
||||
public JobHandle InterpolateDiffuseProperties(BurstSolverImpl solver,
|
||||
NativeArray<float4> properties,
|
||||
NativeArray<float4> diffusePositions,
|
||||
NativeArray<float4> diffuseProperties,
|
||||
NativeArray<int> neighbourCount,
|
||||
int diffuseCount)
|
||||
{
|
||||
|
||||
NativeArray<int4> offsets = new NativeArray<int4>(8, Allocator.TempJob);
|
||||
offsets[0] = new int4(0, 0, 0, 1);
|
||||
offsets[1] = new int4(1, 0, 0, 1);
|
||||
offsets[2] = new int4(0, 1, 0, 1);
|
||||
offsets[3] = new int4(1, 1, 0, 1);
|
||||
offsets[4] = new int4(0, 0, 1, 1);
|
||||
offsets[5] = new int4(1, 0, 1, 1);
|
||||
offsets[6] = new int4(0, 1, 1, 1);
|
||||
offsets[7] = new int4(1, 1, 1, 1);
|
||||
|
||||
var interpolateDiffusePropertiesJob = new InterpolateDiffusePropertiesJob
|
||||
{
|
||||
grid = grid,
|
||||
positions = solver.abstraction.positions.AsNativeArray<float4>(),
|
||||
cellOffsets = offsets,
|
||||
properties = properties,
|
||||
diffusePositions = diffusePositions,
|
||||
diffuseProperties = diffuseProperties,
|
||||
neighbourCount = neighbourCount,
|
||||
densityKernel = new Poly6Kernel(solver.abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
|
||||
gridLevels = grid.populatedLevels.GetKeyArray(Allocator.TempJob),
|
||||
inertialFrame = solver.inertialFrame,
|
||||
mode2D = solver.abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D
|
||||
};
|
||||
|
||||
return interpolateDiffusePropertiesJob.Schedule(diffuseCount, 64);
|
||||
}
|
||||
|
||||
public JobHandle SpatialQuery(BurstSolverImpl solver,
|
||||
NativeArray<BurstQueryShape> shapes,
|
||||
NativeArray<BurstAffineTransform> transforms,
|
||||
NativeQueue<BurstQueryResult> results)
|
||||
{
|
||||
var world = ObiColliderWorld.GetInstance();
|
||||
|
||||
var job = new SpatialQueryJob
|
||||
{
|
||||
grid = grid,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Mathematics;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
@@ -8,25 +9,34 @@ namespace Obi
|
||||
public float4 pointA; // point A, expressed as simplex barycentric coords for simplices, as a solver-space position for colliders.
|
||||
public float4 pointB; // point B, expressed as simplex barycentric coords for simplices, as a solver-space position for colliders.
|
||||
|
||||
public float4 normal; // contact normal on bodyB's surface.
|
||||
public float4 tangent; // contact tangent on bodyB's surface.
|
||||
public float4 normal;
|
||||
public float4 tangent;
|
||||
public float4 bitangent;
|
||||
|
||||
public float distance; // distance between bodyA's and bodyB's surface.
|
||||
public float distance;
|
||||
|
||||
public float normalLambda;
|
||||
public float tangentLambda;
|
||||
public float bitangentLambda;
|
||||
public float stickLambda;
|
||||
public float rollingFrictionImpulse;
|
||||
float normalLambda;
|
||||
float tangentLambda;
|
||||
float bitangentLambda;
|
||||
float stickLambda;
|
||||
float rollingFrictionImpulse;
|
||||
|
||||
public int bodyA;
|
||||
public int bodyB;
|
||||
|
||||
public float normalInvMassA;
|
||||
public float tangentInvMassA;
|
||||
public float bitangentInvMassA;
|
||||
|
||||
public float normalInvMassB;
|
||||
public float tangentInvMassB;
|
||||
public float bitangentInvMassB;
|
||||
|
||||
public double pad0; // padding to ensure correct alignment to 128 bytes.
|
||||
|
||||
public int GetParticleCount() { return 2; }
|
||||
public int GetParticle(int index) { return index == 0 ? bodyA : bodyB; }
|
||||
|
||||
public float4 bitangent => math.normalizesafe(new float4(math.cross(normal.xyz, tangent.xyz), 0));
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return bodyA + "," + bodyB;
|
||||
@@ -40,15 +50,85 @@ namespace Obi
|
||||
return first;
|
||||
}
|
||||
|
||||
public void CalculateTangent(float4 relativeVelocity)
|
||||
public float TotalNormalInvMass
|
||||
{
|
||||
tangent = math.normalizesafe(relativeVelocity - math.dot(relativeVelocity, normal) * normal);
|
||||
get { return normalInvMassA + normalInvMassB; }
|
||||
}
|
||||
|
||||
public float SolveAdhesion(float normalMass, float4 posA, float4 posB, float stickDistance, float stickiness, float dt)
|
||||
public float TotalTangentInvMass
|
||||
{
|
||||
get { return tangentInvMassA + tangentInvMassB; }
|
||||
}
|
||||
|
||||
public float TotalBitangentInvMass
|
||||
{
|
||||
get { return bitangentInvMassA + bitangentInvMassB; }
|
||||
}
|
||||
|
||||
public void CalculateBasis(float4 relativeVelocity)
|
||||
{
|
||||
tangent = math.normalizesafe(relativeVelocity - math.dot(relativeVelocity, normal) * normal);
|
||||
bitangent = math.normalizesafe(new float4(math.cross(normal.xyz, tangent.xyz),0));
|
||||
}
|
||||
|
||||
public void CalculateContactMassesA(float invMass,
|
||||
float4 inverseInertiaTensor,
|
||||
float4 position,
|
||||
quaternion orientation,
|
||||
float4 contactPoint,
|
||||
bool rollingContacts)
|
||||
{
|
||||
// initialize inverse linear masses:
|
||||
normalInvMassA = tangentInvMassA = bitangentInvMassA = invMass;
|
||||
|
||||
if (rollingContacts)
|
||||
{
|
||||
float4 rA = contactPoint - position;
|
||||
float4x4 solverInertiaA = BurstMath.TransformInertiaTensor(inverseInertiaTensor, orientation);
|
||||
|
||||
normalInvMassA += BurstMath.RotationalInvMass(solverInertiaA, rA, normal);
|
||||
tangentInvMassA += BurstMath.RotationalInvMass(solverInertiaA, rA, tangent);
|
||||
bitangentInvMassA += BurstMath.RotationalInvMass(solverInertiaA, rA, bitangent);
|
||||
}
|
||||
}
|
||||
|
||||
public void CalculateContactMassesB(float invMass,
|
||||
float4 inverseInertiaTensor,
|
||||
float4 position,
|
||||
quaternion orientation,
|
||||
float4 contactPoint,
|
||||
bool rollingContacts)
|
||||
{
|
||||
// initialize inverse linear masses:
|
||||
normalInvMassB = tangentInvMassB = bitangentInvMassB = invMass;
|
||||
|
||||
if (rollingContacts)
|
||||
{
|
||||
float4 rB = contactPoint - position;
|
||||
float4x4 solverInertiaB = BurstMath.TransformInertiaTensor(inverseInertiaTensor, orientation);
|
||||
|
||||
normalInvMassB += BurstMath.RotationalInvMass(solverInertiaB, rB, normal);
|
||||
tangentInvMassB += BurstMath.RotationalInvMass(solverInertiaB, rB, tangent);
|
||||
bitangentInvMassB += BurstMath.RotationalInvMass(solverInertiaB, rB, bitangent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void CalculateContactMassesB(in BurstRigidbody rigidbody, in BurstAffineTransform solver2World)
|
||||
{
|
||||
float4 rB = solver2World.TransformPoint(pointB) - rigidbody.com;
|
||||
|
||||
// initialize inverse linear masses:
|
||||
normalInvMassB = tangentInvMassB = bitangentInvMassB = rigidbody.inverseMass;
|
||||
normalInvMassB += BurstMath.RotationalInvMass(rigidbody.inverseInertiaTensor, rB, normal);
|
||||
tangentInvMassB += BurstMath.RotationalInvMass(rigidbody.inverseInertiaTensor, rB, tangent);
|
||||
bitangentInvMassB += BurstMath.RotationalInvMass(rigidbody.inverseInertiaTensor, rB, bitangent);
|
||||
}
|
||||
|
||||
public float SolveAdhesion(float4 posA, float4 posB, float stickDistance, float stickiness, float dt)
|
||||
{
|
||||
|
||||
if (normalMass <= 0 || stickDistance <= 0 || stickiness <= 0 || dt <= 0)
|
||||
if (TotalNormalInvMass <= 0 || stickDistance <= 0 || stickiness <= 0 || dt <= 0)
|
||||
return 0;
|
||||
|
||||
distance = math.dot(posA - posB, normal);
|
||||
@@ -57,7 +137,7 @@ namespace Obi
|
||||
float constraint = stickiness * (1 - math.max(distance / stickDistance, 0)) * dt;
|
||||
|
||||
// calculate lambda multiplier:
|
||||
float dlambda = -constraint / normalMass;
|
||||
float dlambda = -constraint / TotalNormalInvMass;
|
||||
|
||||
// accumulate lambda:
|
||||
float newStickinessLambda = math.min(stickLambda + dlambda, 0);
|
||||
@@ -69,9 +149,10 @@ namespace Obi
|
||||
return lambdaChange;
|
||||
}
|
||||
|
||||
public float SolvePenetration(float normalMass, float4 posA, float4 posB, float maxDepenetrationDelta)
|
||||
public float SolvePenetration(float4 posA, float4 posB, float maxDepenetrationDelta)
|
||||
{
|
||||
if (normalMass <= 0)
|
||||
|
||||
if (TotalNormalInvMass <= 0)
|
||||
return 0;
|
||||
|
||||
//project position delta to normal vector:
|
||||
@@ -81,7 +162,7 @@ namespace Obi
|
||||
float maxProjection = math.max(-distance - maxDepenetrationDelta, 0);
|
||||
|
||||
// calculate lambda multiplier:
|
||||
float dlambda = -(distance + maxProjection) / normalMass;
|
||||
float dlambda = -(distance + maxProjection) / TotalNormalInvMass;
|
||||
|
||||
// accumulate lambda:
|
||||
float newLambda = math.max(normalLambda + dlambda, 0);
|
||||
@@ -93,11 +174,11 @@ namespace Obi
|
||||
return lambdaChange;
|
||||
}
|
||||
|
||||
public float2 SolveFriction(float tangentMass, float bitangentMass, float4 relativeVelocity, float staticFriction, float dynamicFriction, float dt)
|
||||
public float2 SolveFriction(float4 relativeVelocity, float staticFriction, float dynamicFriction, float dt)
|
||||
{
|
||||
float2 lambdaChange = float2.zero;
|
||||
|
||||
if (tangentMass <= 0 || bitangentMass <= 0 ||
|
||||
if (TotalTangentInvMass <= 0 || TotalBitangentInvMass <= 0 ||
|
||||
(dynamicFriction <= 0 && staticFriction <= 0) || (normalLambda <= 0 && stickLambda <= 0))
|
||||
return lambdaChange;
|
||||
|
||||
@@ -110,7 +191,7 @@ namespace Obi
|
||||
float staticFrictionCone = normalLambda / dt * staticFriction;
|
||||
|
||||
// tangent impulse:
|
||||
float tangentLambdaDelta = -tangentPosDelta / tangentMass;
|
||||
float tangentLambdaDelta = -tangentPosDelta / TotalTangentInvMass;
|
||||
float newTangentLambda = tangentLambda + tangentLambdaDelta;
|
||||
|
||||
if (math.abs(newTangentLambda) > staticFrictionCone)
|
||||
@@ -120,7 +201,7 @@ namespace Obi
|
||||
tangentLambda = newTangentLambda;
|
||||
|
||||
// bitangent impulse:
|
||||
float bitangentLambdaDelta = -bitangentPosDelta / bitangentMass;
|
||||
float bitangentLambdaDelta = -bitangentPosDelta / TotalBitangentInvMass;
|
||||
float newBitangentLambda = bitangentLambda + bitangentLambdaDelta;
|
||||
|
||||
if (math.abs(newBitangentLambda) > staticFrictionCone)
|
||||
@@ -158,6 +239,6 @@ namespace Obi
|
||||
|
||||
return rolling_impulse_change;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -10,9 +10,9 @@ namespace Obi
|
||||
public float4 queryPoint; // point B, expressed as simplex barycentric coords for simplices, as a solver-space position for colliders.
|
||||
public float4 normal;
|
||||
public float distance;
|
||||
public float distanceAlongRay;
|
||||
public int simplexIndex;
|
||||
public int queryIndex;
|
||||
public int pad0; // padding to ensure correct alignment.
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -67,29 +67,17 @@ namespace Obi
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
var co = new BurstQueryResult { simplexIndex = simplexIndex, queryIndex = shapeIndex };
|
||||
var co = new BurstQueryResult() { simplexIndex = simplexIndex, queryIndex = shapeIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
float4 simplexPrevPosition = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
simplexPrevPosition += positions[particleIndex] * simplexBary[j];
|
||||
simplexRadius += BurstMath.EllipsoidRadius(colliderPoint.normal, orientations[particleIndex], radii[particleIndex].xyz) * simplexBary[j];
|
||||
}
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstBoxQuery>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
co.queryPoint = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.simplexBary = simplexBary;
|
||||
co.distance = math.dot(simplexPrevPosition - colliderPoint.point, colliderPoint.normal) - simplexRadius;
|
||||
|
||||
if (co.distance <= shape.maxDistance)
|
||||
results.Enqueue(co);
|
||||
results.Enqueue(co);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
@@ -18,7 +17,7 @@ namespace Obi
|
||||
|
||||
// express ray in simplex space (ellipsoid == scaled sphere)
|
||||
float4 rayOrigin = math.mul(colliderToSimplex, new float4(shape.center.xyz,1));
|
||||
float4 rayDirection = math.normalizesafe(math.mul(colliderToSimplex, new float4(shape.size.xyz,0)));
|
||||
float4 rayDirection = math.normalizesafe(math.mul(colliderToSimplex, new float4((shape.size - shape.center).xyz,0)));
|
||||
|
||||
float rayDistance = ObiUtils.RaySphereIntersection(rayOrigin.xyz, rayDirection.xyz, float3.zero, 1);
|
||||
|
||||
@@ -26,7 +25,7 @@ namespace Obi
|
||||
{
|
||||
point = colliderToSolver.InverseTransformPointUnscaled(point);
|
||||
|
||||
float4 centerLine = BurstMath.NearestPointOnEdge(shape.center * colliderToSolver.scale, (shape.center + shape.size) * colliderToSolver.scale, point, out float mu);
|
||||
float4 centerLine = BurstMath.NearestPointOnEdge(shape.center * colliderToSolver.scale, shape.size * colliderToSolver.scale, point, out float mu);
|
||||
float4 centerToPoint = point - centerLine;
|
||||
float distanceToCenter = math.length(centerToPoint);
|
||||
|
||||
@@ -58,33 +57,17 @@ namespace Obi
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
var co = new BurstQueryResult { simplexIndex = simplexIndex, queryIndex = shapeIndex };
|
||||
var co = new BurstQueryResult() { simplexIndex = simplexIndex, queryIndex = shapeIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstRay>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
float4 simplexPrevPosition = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
simplexPrevPosition += positions[particleIndex] * simplexBary[j];
|
||||
simplexRadius += BurstMath.EllipsoidRadius(colliderPoint.normal, orientations[particleIndex], radii[particleIndex].xyz) * simplexBary[j];
|
||||
}
|
||||
|
||||
co.queryPoint = colliderPoint.point;
|
||||
co.queryPoint = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.simplexBary = simplexBary;
|
||||
co.distance = math.dot(simplexPrevPosition - colliderPoint.point, colliderPoint.normal) - simplexRadius;
|
||||
|
||||
if (co.distance <= shape.maxDistance)
|
||||
{
|
||||
float4 pointOnRay = colliderPoint.point + colliderPoint.normal * co.distance;
|
||||
co.distanceAlongRay = math.dot(pointOnRay.xyz - shape.center.xyz, math.normalizesafe(shape.size.xyz));
|
||||
results.Enqueue(co);
|
||||
}
|
||||
results.Enqueue(co);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,29 +39,17 @@ namespace Obi
|
||||
int optimizationIterations,
|
||||
float optimizationTolerance)
|
||||
{
|
||||
var co = new BurstQueryResult { simplexIndex = simplexIndex, queryIndex = shapeIndex };
|
||||
var co = new BurstQueryResult() { simplexIndex = simplexIndex, queryIndex = shapeIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
|
||||
var colliderPoint = BurstLocalOptimization.Optimize(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
float4 simplexPrevPosition = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
simplexPrevPosition += positions[particleIndex] * simplexBary[j];
|
||||
simplexRadius += BurstMath.EllipsoidRadius(colliderPoint.normal, orientations[particleIndex], radii[particleIndex].xyz) * simplexBary[j];
|
||||
}
|
||||
var colliderPoint = BurstLocalOptimization.Optimize<BurstSphereQuery>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out float4 convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
co.queryPoint = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.simplexBary = simplexBary;
|
||||
co.distance = math.dot(simplexPrevPosition - colliderPoint.point, colliderPoint.normal) - simplexRadius;
|
||||
|
||||
if (co.distance <= shape.maxDistance)
|
||||
results.Enqueue(co);
|
||||
results.Enqueue(co);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Obi
|
||||
{
|
||||
var cell = grid.usedCells[c];
|
||||
|
||||
// calculate thickened grid bounds:
|
||||
// calculate thickedned grid bounds:
|
||||
float size = NativeMultilevelGrid<int>.CellSizeOfLevel(cell.Coords.w);
|
||||
float4 cellPos = (float4)cell.Coords * size;
|
||||
BurstAabb cellBounds = new BurstAabb(cellPos - new float4(size), cellPos + new float4(2 * size));
|
||||
@@ -85,7 +85,7 @@ namespace Obi
|
||||
|
||||
private BurstAabb CalculateShapeAABB(in BurstQueryShape shape)
|
||||
{
|
||||
float offset = shape.contactOffset + shape.maxDistance;
|
||||
float offset = shape.contactOffset + shape.distance;
|
||||
switch (shape.type)
|
||||
{
|
||||
case QueryShape.QueryType.Sphere:
|
||||
@@ -93,7 +93,7 @@ namespace Obi
|
||||
case QueryShape.QueryType.Box:
|
||||
return new BurstAabb(shape.center - shape.size*0.5f - offset, shape.center + shape.size * 0.5f + offset);
|
||||
case QueryShape.QueryType.Ray:
|
||||
return new BurstAabb(shape.center, shape.center + shape.size, offset);
|
||||
return new BurstAabb(shape.center, shape.size, offset);
|
||||
}
|
||||
return new BurstAabb();
|
||||
}
|
||||
@@ -108,22 +108,57 @@ namespace Obi
|
||||
switch (shape.type)
|
||||
{
|
||||
case QueryShape.QueryType.Sphere:
|
||||
BurstSphereQuery sphereShape = new BurstSphereQuery { colliderToSolver = shapeToSolver, shape = shape};
|
||||
BurstSphereQuery sphereShape = new BurstSphereQuery() { colliderToSolver = shapeToSolver, shape = shape};
|
||||
sphereShape.Query(shapeIndex, positions, orientations, radii, simplices,
|
||||
simplexIndex, simplexStart, simplexSize, results, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
break;
|
||||
case QueryShape.QueryType.Box:
|
||||
BurstBoxQuery boxShape = new BurstBoxQuery { colliderToSolver = shapeToSolver, shape = shape};
|
||||
BurstBoxQuery boxShape = new BurstBoxQuery() { colliderToSolver = shapeToSolver, shape = shape};
|
||||
boxShape.Query(shapeIndex, positions, orientations, radii, simplices,
|
||||
simplexIndex, simplexStart, simplexSize, results, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
break;
|
||||
case QueryShape.QueryType.Ray:
|
||||
BurstRay rayShape = new BurstRay { colliderToSolver = shapeToSolver, shape = shape };
|
||||
BurstRay rayShape = new BurstRay() { colliderToSolver = shapeToSolver, shape = shape };
|
||||
rayShape.Query(shapeIndex, positions, orientations, radii, simplices,
|
||||
simplexIndex, simplexStart, simplexSize, results, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct CalculateQueryDistances : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<quaternion> prevOrientations;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
public NativeArray<BurstQueryResult> queryResults;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
var result = queryResults[i];
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(result.simplexIndex, out int simplexSize);
|
||||
|
||||
float4 simplexPrevPosition = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
simplexPrevPosition += prevPositions[particleIndex] * result.simplexBary[j];
|
||||
simplexRadius += BurstMath.EllipsoidRadius(result.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * result.simplexBary[j];
|
||||
}
|
||||
|
||||
// update contact distance
|
||||
result.distance = math.dot(simplexPrevPosition - result.queryPoint, result.normal) - simplexRadius;
|
||||
queryResults[i] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,62 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[BurstCompile]
|
||||
struct BuildParticleMeshDataJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public NativeArray<int> rendererIndices;
|
||||
[ReadOnly] public NativeArray<ParticleRendererData> rendererData;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
[ReadOnly] public NativeArray<quaternion> renderableOrientations;
|
||||
[ReadOnly] public NativeArray<float4> renderableRadii;
|
||||
[ReadOnly] public NativeArray<float4> colors;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<ParticleVertex> vertices;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> indices;
|
||||
|
||||
[ReadOnly] public int firstParticle;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int p = particleIndices[firstParticle + i];
|
||||
int r = rendererIndices[firstParticle + i];
|
||||
|
||||
ParticleVertex v = new ParticleVertex();
|
||||
|
||||
v.pos = new float4(renderablePositions[p].xyz, 1);
|
||||
v.color = colors[p] * (Vector4)rendererData[r].color;
|
||||
v.b1 = new float4(math.mul(renderableOrientations[p], new float3(1, 0, 0)), renderableRadii[p][0] * renderableRadii[p][3] * rendererData[r].radiusScale);
|
||||
v.b2 = new float4(math.mul(renderableOrientations[p], new float3(0, 1, 0)), renderableRadii[p][1] * renderableRadii[p][3] * rendererData[r].radiusScale);
|
||||
v.b3 = new float4(math.mul(renderableOrientations[p], new float3(0, 0, 1)), renderableRadii[p][2] * renderableRadii[p][3] * rendererData[r].radiusScale);
|
||||
|
||||
v.offset = new float3(1, 1, 0);
|
||||
vertices[i * 4] = v;
|
||||
|
||||
v.offset = new float3(-1, 1, 0);
|
||||
vertices[i * 4 + 1] = v;
|
||||
|
||||
v.offset = new float3(-1, -1, 0);
|
||||
vertices[i * 4 + 2] = v;
|
||||
|
||||
v.offset = new float3(1, -1, 0);
|
||||
vertices[i * 4 + 3] = v;
|
||||
|
||||
indices[i * 6] = (i * 4 + 2);
|
||||
indices[i * 6 + 1] = (i * 4 + 1);
|
||||
indices[i * 6 + 2] = (i * 4);
|
||||
|
||||
indices[i * 6 + 3] = (i * 4 + 3);
|
||||
indices[i * 6 + 4] = (i * 4 + 2);
|
||||
indices[i * 6 + 5] = (i * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cacb4a3597ee245a6b40358728c300e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,247 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if (SRP_UNIVERSAL)
|
||||
using UnityEngine.Rendering.Universal;
|
||||
#endif
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
public class BurstFoamRenderSystem : ObiFoamRenderSystem
|
||||
{
|
||||
protected NativeArray<float2> sortHandles;
|
||||
|
||||
protected struct SortHandleComparer : IComparer<float2>
|
||||
{
|
||||
public int Compare(float2 a, float2 b)
|
||||
{
|
||||
return b.y.CompareTo(a.y);
|
||||
}
|
||||
}
|
||||
|
||||
protected SortHandleComparer comparer = new SortHandleComparer();
|
||||
|
||||
public BurstFoamRenderSystem(ObiSolver solver) : base(solver)
|
||||
{
|
||||
#if (SRP_UNIVERSAL)
|
||||
if (GraphicsSettings.currentRenderPipeline is UniversalRenderPipelineAsset)
|
||||
renderBatch = new ProceduralRenderBatch<DiffuseParticleVertex>(0, Resources.Load<Material>("ObiMaterials/URP/Fluid/FoamParticlesURP"), new RenderBatchParams(true));
|
||||
else
|
||||
#endif
|
||||
renderBatch = new ProceduralRenderBatch<DiffuseParticleVertex>(0, Resources.Load<Material>("ObiMaterials/Fluid/FoamParticles"), new RenderBatchParams(true));
|
||||
ReallocateRenderBatch();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
if (sortHandles.IsCreated)
|
||||
sortHandles.Dispose();
|
||||
}
|
||||
|
||||
private void ReallocateRenderBatch()
|
||||
{
|
||||
// in case the amount of particles allocated does not match
|
||||
// the amount requested by the solver, reallocate
|
||||
if (!sortHandles.IsCreated || m_Solver.foamPositions.count * 4 != renderBatch.vertexCount)
|
||||
{
|
||||
renderBatch.Dispose();
|
||||
renderBatch.vertexCount = m_Solver.foamPositions.count * 4;
|
||||
renderBatch.triangleCount = m_Solver.foamPositions.count * 2;
|
||||
renderBatch.Initialize(layout);
|
||||
|
||||
if (sortHandles.IsCreated)
|
||||
sortHandles.Dispose();
|
||||
sortHandles = new NativeArray<float2>(m_Solver.foamPositions.count, Allocator.Persistent);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Step()
|
||||
{
|
||||
}
|
||||
|
||||
public override unsafe void Render()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
|
||||
var solver = m_Solver.implementation as BurstSolverImpl;
|
||||
|
||||
ReallocateRenderBatch();
|
||||
|
||||
foreach (Camera camera in cameras)
|
||||
{
|
||||
if (camera == null)
|
||||
continue;
|
||||
|
||||
JobHandle inputDeps = new JobHandle();
|
||||
var sortJob = sortHandles.Slice(0, m_Solver.foamCount[3]).SortJob(comparer);
|
||||
|
||||
//Clear all triangle indices to zero:
|
||||
UnsafeUtility.MemClear(
|
||||
NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(renderBatch.triangles),
|
||||
UnsafeUtility.SizeOf<int>() * renderBatch.triangles.Length);
|
||||
|
||||
var projectJob = new ProjectOnSortAxisJob
|
||||
{
|
||||
inputPositions = solver.abstraction.foamPositions.AsNativeArray<float4>(),
|
||||
sortHandles = sortHandles,
|
||||
sortAxis = solver.abstraction.transform.InverseTransformDirection(camera.transform.forward)
|
||||
};
|
||||
|
||||
inputDeps = projectJob.Schedule(m_Solver.foamCount[3], 256, inputDeps);
|
||||
|
||||
inputDeps = sortJob.Schedule(inputDeps);
|
||||
|
||||
var sortParticlesJob = new SortParticles
|
||||
{
|
||||
sortHandles = sortHandles,
|
||||
inputPositions = solver.abstraction.foamPositions.AsNativeArray<float4>(),
|
||||
inputVelocities = solver.abstraction.foamVelocities.AsNativeArray<float4>(),
|
||||
inputColors = solver.abstraction.foamColors.AsNativeArray<float4>(),
|
||||
inputAttributes = solver.abstraction.foamAttributes.AsNativeArray<float4>(),
|
||||
|
||||
outputPositions = solver.auxPositions,
|
||||
outputVelocities = solver.auxVelocities,
|
||||
outputColors = solver.auxColors,
|
||||
outputAttributes = solver.auxAttributes
|
||||
};
|
||||
|
||||
inputDeps = sortParticlesJob.Schedule(m_Solver.foamCount[3], 256, inputDeps);
|
||||
|
||||
var meshJob = new BuildFoamMeshDataJob
|
||||
{
|
||||
inputPositions = solver.auxPositions,
|
||||
inputVelocities = solver.auxVelocities,
|
||||
inputColors = solver.auxColors,
|
||||
inputAttributes = solver.auxAttributes,
|
||||
|
||||
vertices = renderBatch.vertices,
|
||||
indices = renderBatch.triangles,
|
||||
};
|
||||
|
||||
inputDeps = meshJob.Schedule(m_Solver.foamCount[3], 128, inputDeps);
|
||||
|
||||
inputDeps.Complete();
|
||||
|
||||
renderBatch.mesh.SetVertexBufferData(renderBatch.vertices, 0, 0, renderBatch.vertexCount, 0, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontNotifyMeshUsers);
|
||||
renderBatch.mesh.SetIndexBufferData(renderBatch.triangles, 0, 0, renderBatch.triangleCount * 3, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices);
|
||||
|
||||
matProps.SetFloat("_FadeDepth", 0);
|
||||
matProps.SetFloat("_VelocityStretching", m_Solver.maxFoamVelocityStretch);
|
||||
matProps.SetFloat("_RadiusScale", m_Solver.foamRadiusScale);
|
||||
matProps.SetFloat("_FadeIn", m_Solver.foamFade.x);
|
||||
matProps.SetFloat("_FadeOut", m_Solver.foamFade.y);
|
||||
matProps.SetFloat("_ScatterDensity", m_Solver.foamVolumeDensity);
|
||||
matProps.SetFloat("_AmbientDensity", m_Solver.foamAmbientDensity);
|
||||
matProps.SetColor("_ScatterColor", m_Solver.foamScatterColor);
|
||||
matProps.SetColor("_AmbientColor", m_Solver.foamAmbientColor);
|
||||
|
||||
var rp = renderBatch.renderParams;
|
||||
rp.worldBounds = m_Solver.bounds;
|
||||
rp.camera = camera;
|
||||
rp.matProps = matProps;
|
||||
|
||||
Graphics.RenderMesh(rp, renderBatch.mesh, 0, m_Solver.transform.localToWorldMatrix, m_Solver.transform.localToWorldMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct ProjectOnSortAxisJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> inputPositions;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float2> sortHandles;
|
||||
|
||||
public float3 sortAxis;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
sortHandles[i] = new float2(i, math.dot(inputPositions[i].xyz, sortAxis));
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct SortParticles : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float2> sortHandles;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> inputPositions;
|
||||
[ReadOnly] public NativeArray<float4> inputVelocities;
|
||||
[ReadOnly] public NativeArray<float4> inputColors;
|
||||
[ReadOnly] public NativeArray<float4> inputAttributes;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> outputPositions;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> outputVelocities;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> outputColors;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> outputAttributes;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int o = (int)sortHandles[i].x;
|
||||
outputPositions[i] = inputPositions[o];
|
||||
outputVelocities[i] = inputVelocities[o];
|
||||
outputColors[i] = inputColors[o];
|
||||
outputAttributes[i] = inputAttributes[o];
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct BuildFoamMeshDataJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> inputPositions;
|
||||
[ReadOnly] public NativeArray<float4> inputVelocities;
|
||||
[ReadOnly] public NativeArray<float4> inputColors;
|
||||
[ReadOnly] public NativeArray<float4> inputAttributes;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<DiffuseParticleVertex> vertices;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> indices;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
DiffuseParticleVertex v = new DiffuseParticleVertex();
|
||||
|
||||
v.pos = new float4(inputPositions[i].xyz, 1);
|
||||
v.color = inputColors[i];
|
||||
v.velocity = inputVelocities[i];
|
||||
v.attributes = inputAttributes[i];
|
||||
|
||||
v.offset = new float3(1, 1, 0);
|
||||
vertices[i * 4] = v;
|
||||
|
||||
v.offset = new float3(-1, 1, 0);
|
||||
vertices[i * 4 + 1] = v;
|
||||
|
||||
v.offset = new float3(-1, -1, 0);
|
||||
vertices[i * 4 + 2] = v;
|
||||
|
||||
v.offset = new float3(1, -1, 0);
|
||||
vertices[i * 4 + 3] = v;
|
||||
|
||||
indices[i * 6] = (i * 4 + 2);
|
||||
indices[i * 6 + 1] = (i * 4 + 1);
|
||||
indices[i * 6 + 2] = (i * 4);
|
||||
|
||||
indices[i * 6 + 3] = (i * 4 + 3);
|
||||
indices[i * 6 + 4] = (i * 4 + 2);
|
||||
indices[i * 6 + 5] = (i * 4);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 555e1cc2122984a93afc0f6f31fa173b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,100 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
public class BurstInstancedParticleRenderSystem : ObiInstancedParticleRenderSystem
|
||||
{
|
||||
|
||||
public BurstInstancedParticleRenderSystem(ObiSolver solver) : base(solver)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Render()
|
||||
{
|
||||
using (m_RenderMarker.Auto())
|
||||
{
|
||||
var instanceTransformsJob = new InstancedParticleTransforms
|
||||
{
|
||||
activeParticles = activeParticles.AsNativeArray<int>(),
|
||||
rendererData = rendererData.AsNativeArray<ParticleRendererData>(),
|
||||
rendererIndex = rendererIndex.AsNativeArray<int>(),
|
||||
instanceTransforms = instanceTransforms.AsNativeArray<float4x4>(),
|
||||
instanceColors = instanceColors.AsNativeArray<float4>(),
|
||||
|
||||
renderablePositions = m_Solver.renderablePositions.AsNativeArray<float4>(),
|
||||
renderableOrientations = m_Solver.renderableOrientations.AsNativeArray<quaternion>(),
|
||||
renderableRadii = m_Solver.renderableRadii.AsNativeArray<float4>(),
|
||||
colors = m_Solver.colors.AsNativeArray<float4>(),
|
||||
solverToWorld = m_Solver.transform.localToWorldMatrix
|
||||
};
|
||||
|
||||
instanceTransformsJob.Schedule(activeParticles.count, 32).Complete();
|
||||
|
||||
var mpb = new MaterialPropertyBlock();
|
||||
|
||||
//Draw instances:
|
||||
for (int i = 0; i < batchList.Count; i++)
|
||||
{
|
||||
var batch = batchList[i];
|
||||
|
||||
if (batch.instanceCount > 0)
|
||||
{
|
||||
// workaround for RenderMeshInstanced bug
|
||||
// (https://forum.unity.com/threads/gpu-instanced-custom-properties-dont-take-unity_baseinstanceid-into-account.1520602/)
|
||||
// also, no NativeArray<> overload :(
|
||||
mpb.SetVectorArray("_Colors", instanceColors.AsNativeArray<Vector4>().Slice(batch.firstInstance, batch.instanceCount).ToArray());
|
||||
|
||||
var rp = batch.renderParams;
|
||||
rp.material = batch.material;
|
||||
rp.worldBounds = m_Solver.bounds;
|
||||
rp.matProps = mpb;
|
||||
|
||||
// TODO: use generic overload to pass matrix + instance color.
|
||||
Graphics.RenderMeshInstanced(rp, batch.mesh, 0, instanceTransforms.AsNativeArray<Matrix4x4>(), batch.instanceCount, batch.firstInstance);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct InstancedParticleTransforms : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> activeParticles;
|
||||
[ReadOnly] public NativeArray<ParticleRendererData> rendererData;
|
||||
[ReadOnly] public NativeArray<int> rendererIndex;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
[ReadOnly] public NativeArray<quaternion> renderableOrientations;
|
||||
[ReadOnly] public NativeArray<float4> renderableRadii;
|
||||
[ReadOnly] public NativeArray<float4> colors;
|
||||
[ReadOnly] public float4x4 solverToWorld;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4x4> instanceTransforms;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> instanceColors;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int p = activeParticles[i];
|
||||
|
||||
Matrix4x4 tfrm = float4x4.TRS(renderablePositions[p].xyz,
|
||||
renderableOrientations[p],
|
||||
renderableRadii[p].xyz * renderableRadii[p][3] * rendererData[rendererIndex[i]].radiusScale);
|
||||
|
||||
instanceTransforms[i] = math.mul(solverToWorld, tfrm);
|
||||
|
||||
instanceColors[i] = colors[p] * (Vector4)rendererData[rendererIndex[i]].color;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3896a2253db543bcaa92126d83a7ba0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,58 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstParticleRenderSystem : ObiParticleRenderSystem
|
||||
{
|
||||
public BurstParticleRenderSystem(ObiSolver solver) : base(solver)
|
||||
{
|
||||
m_Solver = solver;
|
||||
}
|
||||
|
||||
public override void Render()
|
||||
{
|
||||
using (m_RenderMarker.Auto())
|
||||
{
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
{
|
||||
var batch = batchList[i];
|
||||
|
||||
var buildArraysJob = new BuildParticleMeshDataJob
|
||||
{
|
||||
particleIndices = activeParticles.AsNativeArray<int>(),
|
||||
rendererIndices = rendererIndex.AsNativeArray<int>(),
|
||||
rendererData = rendererData.AsNativeArray<ParticleRendererData>(),
|
||||
|
||||
renderablePositions = m_Solver.renderablePositions.AsNativeArray<float4>(),
|
||||
renderableOrientations = m_Solver.renderableOrientations.AsNativeArray<quaternion>(),
|
||||
renderableRadii = m_Solver.renderableRadii.AsNativeArray<float4>(),
|
||||
colors = m_Solver.colors.AsNativeArray<float4>(),
|
||||
|
||||
vertices = batch.vertices,
|
||||
indices = batch.triangles,
|
||||
|
||||
firstParticle = batch.firstParticle,
|
||||
};
|
||||
|
||||
buildArraysJob.Schedule(batch.vertexCount / 4, 32).Complete();
|
||||
|
||||
batch.mesh.SetVertexBufferData(batch.vertices, 0, 0, batch.vertexCount, 0, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontNotifyMeshUsers);
|
||||
batch.mesh.SetIndexBufferData(batch.triangles, 0, 0, batch.triangleCount * 3, MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontValidateIndices);
|
||||
|
||||
var rp = batch.renderParams;
|
||||
rp.worldBounds = m_Solver.bounds;
|
||||
|
||||
Graphics.RenderMesh(rp, batch.mesh, 0, m_Solver.transform.localToWorldMatrix, m_Solver.transform.localToWorldMatrix);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1d1ddc20ab7148e3ac806e3559ba2b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,10 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68fb3be53914247609d4e6da4f249e52
|
||||
labels:
|
||||
- ObiRope
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,139 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
public class BurstChainRopeRenderSystem : ObiChainRopeRenderSystem
|
||||
{
|
||||
protected Matrix4x4[] transformsArray = new Matrix4x4[1023];
|
||||
|
||||
public BurstChainRopeRenderSystem(ObiSolver solver) : base(solver)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
public override void Render()
|
||||
{
|
||||
using (m_RenderMarker.Auto())
|
||||
{
|
||||
// generate raw frames using parallel transport
|
||||
var instanceTransformsJob = new InstanceTransforms()
|
||||
{
|
||||
rendererData = rendererData.AsNativeArray<ChainRendererData>(),
|
||||
chunkData = chunkData.AsNativeArray<ChunkData>(),
|
||||
modifiers = modifiers.AsNativeArray<ObiRopeChainRenderer.LinkModifier>(),
|
||||
elements = elements.AsNativeArray<int2>(),
|
||||
|
||||
instanceTransforms = instanceTransforms.AsNativeArray<float4x4>(),
|
||||
instanceColors = instanceColors.AsNativeArray<float4>(),
|
||||
|
||||
renderablePositions = m_Solver.renderablePositions.AsNativeArray<float4>(),
|
||||
renderableOrientations = m_Solver.renderableOrientations.AsNativeArray<quaternion>(),
|
||||
principalRadii = m_Solver.principalRadii.AsNativeArray<float4>(),
|
||||
colors = m_Solver.colors.AsNativeArray<float4>(),
|
||||
solverToWorld = m_Solver.transform.localToWorldMatrix,
|
||||
};
|
||||
|
||||
instanceTransformsJob.Schedule(chunkData.count, 8).Complete();
|
||||
|
||||
//Draw instances:
|
||||
for (int i = 0; i < batchList.Count; i++)
|
||||
{
|
||||
var batch = batchList[i];
|
||||
|
||||
var rp = batch.renderParams;
|
||||
rp.material = batch.material;
|
||||
rp.worldBounds = m_Solver.bounds;
|
||||
|
||||
Graphics.RenderMeshInstanced(rp, batch.mesh, 0, instanceTransforms.AsNativeArray<Matrix4x4>(), batch.instanceCount, batch.firstInstance);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct InstanceTransforms : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<ChainRendererData> rendererData;
|
||||
[ReadOnly] public NativeArray<ChunkData> chunkData;
|
||||
[ReadOnly] public NativeArray<ObiRopeChainRenderer.LinkModifier> modifiers;
|
||||
[ReadOnly] public NativeArray<int2> elements;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
[ReadOnly] public NativeArray<quaternion> renderableOrientations;
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public NativeArray<float4> colors;
|
||||
[ReadOnly] public float4x4 solverToWorld;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4x4> instanceTransforms;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> instanceColors;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int firstIndex = i > 0 ? chunkData[i - 1].offset : 0;
|
||||
int elementCount = chunkData[i].offset - firstIndex;
|
||||
|
||||
var rendererIndex = chunkData[i].rendererIndex;
|
||||
var renderer = rendererData[rendererIndex];
|
||||
|
||||
float3 rendScale = ((float4)renderer.scale).xyz;
|
||||
|
||||
int firstModifier = rendererIndex > 0 ? rendererData[rendererIndex - 1].modifierOffset : 0;
|
||||
int modifierCount = renderer.modifierOffset - firstModifier;
|
||||
|
||||
var modifier = new ObiRopeChainRenderer.LinkModifier();
|
||||
modifier.Clear();
|
||||
|
||||
BurstPathFrame frame = new BurstPathFrame();
|
||||
frame.Reset();
|
||||
|
||||
float twist = -renderer.twist * elementCount * renderer.twistAnchor;
|
||||
frame.SetTwist(twist);
|
||||
|
||||
// parallel transport:
|
||||
for (int m = 0; m < elementCount; ++m)
|
||||
{
|
||||
if (modifierCount > 0)
|
||||
modifier = modifiers[firstModifier + m % modifierCount];
|
||||
|
||||
int index = firstIndex + m;
|
||||
float4 pos = renderablePositions[elements[index].x];
|
||||
float4 nextPos = renderablePositions[elements[index].y];
|
||||
float4 vector = nextPos - pos;
|
||||
float3 tangent = math.normalizesafe(vector.xyz);
|
||||
|
||||
if (renderer.usesOrientedParticles == 1)
|
||||
{
|
||||
frame.Transport(nextPos.xyz, tangent, math.rotate(renderableOrientations[elements[index].x], new float3(0, 1, 0)), twist);
|
||||
twist += renderer.twist;
|
||||
}
|
||||
else
|
||||
frame.Transport(nextPos.xyz, tangent, renderer.twist);
|
||||
|
||||
var rotation = quaternion.LookRotationSafe(frame.tangent, frame.normal);
|
||||
var position = (pos + vector * 0.5f).xyz + math.mul(rotation, modifier.translation);
|
||||
var scale = principalRadii[elements[index].x].x * 2 * rendScale * modifier.scale;
|
||||
|
||||
rotation = math.mul(rotation, quaternion.Euler(math.radians(modifier.rotation)));
|
||||
|
||||
instanceTransforms[index] = math.mul(solverToWorld,float4x4.TRS(position,rotation,scale));
|
||||
instanceColors[index] = (colors[elements[index].x] + colors[elements[index].y]) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c63ccd17bfc14b40a403c1c9a0be2c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,215 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstExtrudedRopeRenderSystem : ObiExtrudedRopeRenderSystem
|
||||
{
|
||||
public BurstExtrudedRopeRenderSystem(ObiSolver solver) : base(solver)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
|
||||
// Initialize each batch:
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
batchList[i].Initialize(layout, false);
|
||||
}
|
||||
|
||||
public override void Render()
|
||||
{
|
||||
if (pathSmootherSystem == null)
|
||||
return;
|
||||
|
||||
using (m_RenderMarker.Auto())
|
||||
{
|
||||
var handles = new NativeArray<JobHandle>(batchList.Count, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
{
|
||||
var batch = batchList[i];
|
||||
|
||||
var meshJob = new BuildExtrudedMesh
|
||||
{
|
||||
pathSmootherIndices = pathSmootherIndices.AsNativeArray<int>(),
|
||||
chunkOffsets = pathSmootherSystem.chunkOffsets.AsNativeArray<int>(),
|
||||
|
||||
frames = pathSmootherSystem.smoothFrames.AsNativeArray<BurstPathFrame>(),
|
||||
frameOffsets = pathSmootherSystem.smoothFrameOffsets.AsNativeArray<int>(),
|
||||
frameCounts = pathSmootherSystem.smoothFrameCounts.AsNativeArray<int>(),
|
||||
|
||||
sectionData = sectionData.AsNativeArray<float2>(),
|
||||
sectionOffsets = sectionOffsets.AsNativeArray<int>(),
|
||||
sectionIndices = sectionIndices.AsNativeArray<int>(),
|
||||
|
||||
vertexOffsets = vertexOffsets.AsNativeArray<int>(),
|
||||
triangleOffsets = triangleOffsets.AsNativeArray<int>(),
|
||||
triangleCounts = triangleCounts.AsNativeArray<int>(),
|
||||
|
||||
pathData = pathSmootherSystem.pathData.AsNativeArray<BurstPathSmootherData>(),
|
||||
rendererData = rendererData.AsNativeArray<BurstExtrudedMeshData>(),
|
||||
|
||||
vertices = batch.vertices,
|
||||
tris = batch.triangles,
|
||||
|
||||
firstRenderer = batch.firstRenderer
|
||||
};
|
||||
handles[i] = meshJob.Schedule(batch.rendererCount, 1);
|
||||
}
|
||||
|
||||
JobHandle.CombineDependencies(handles).Complete();
|
||||
handles.Dispose();
|
||||
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
{
|
||||
var batch = batchList[i];
|
||||
|
||||
batch.mesh.SetVertexBufferData(batch.vertices, 0, 0, batch.vertexCount, 0, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontRecalculateBounds);
|
||||
batch.mesh.SetIndexBufferData(batch.triangles, 0, 0, batch.triangleCount * 3, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontRecalculateBounds);
|
||||
|
||||
var rp = batch.renderParams;
|
||||
rp.worldBounds = m_Solver.bounds;
|
||||
|
||||
Graphics.RenderMesh(rp, batch.mesh, 0, m_Solver.transform.localToWorldMatrix, m_Solver.transform.localToWorldMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct BuildExtrudedMesh : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> pathSmootherIndices;
|
||||
[ReadOnly] public NativeArray<int> chunkOffsets;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstPathFrame> frames;
|
||||
[ReadOnly] public NativeArray<int> frameOffsets;
|
||||
[ReadOnly] public NativeArray<int> frameCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<float2> sectionData;
|
||||
[ReadOnly] public NativeArray<int> sectionOffsets;
|
||||
[ReadOnly] public NativeArray<int> sectionIndices;
|
||||
|
||||
[ReadOnly] public NativeArray<int> vertexOffsets;
|
||||
[ReadOnly] public NativeArray<int> triangleOffsets;
|
||||
[ReadOnly] public NativeArray<int> triangleCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstExtrudedMeshData> rendererData;
|
||||
[ReadOnly] public NativeArray<BurstPathSmootherData> pathData;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<ProceduralRopeVertex> vertices;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> tris;
|
||||
|
||||
[ReadOnly] public int firstRenderer;
|
||||
|
||||
public void Execute(int u)
|
||||
{
|
||||
int k = firstRenderer + u;
|
||||
int s = pathSmootherIndices[k];
|
||||
|
||||
float3 vertex = float3.zero;
|
||||
float3 normal = float3.zero;
|
||||
float4 texTangent = float4.zero;
|
||||
|
||||
int tri = 0;
|
||||
int sectionIndex = 0;
|
||||
int sectionStart = sectionOffsets[sectionIndices[k]];
|
||||
int sectionSegments = (sectionOffsets[sectionIndices[k] + 1] - sectionStart) - 1;
|
||||
int verticesPerSection = sectionSegments + 1; // the last vertex in each section must be duplicated, due to uv wraparound.
|
||||
|
||||
float smoothLength = 0;
|
||||
for (int i = chunkOffsets[s]; i < chunkOffsets[s + 1]; ++i)
|
||||
smoothLength += pathData[i].smoothLength;
|
||||
|
||||
float vCoord = -rendererData[k].uvScale.y * pathData[chunkOffsets[s]].restLength * rendererData[k].uvAnchor;
|
||||
float actualToRestLengthRatio = smoothLength / pathData[chunkOffsets[s]].restLength;
|
||||
|
||||
int firstVertex = vertexOffsets[k];
|
||||
int firstTriangle = triangleOffsets[k];
|
||||
|
||||
// clear out triangle indices for this rope:
|
||||
for (int i = firstTriangle; i < firstTriangle + triangleCounts[k]; ++i)
|
||||
{
|
||||
int offset = i * 3;
|
||||
tris[offset] = 0;
|
||||
tris[offset+1] = 0;
|
||||
tris[offset+2] = 0;
|
||||
}
|
||||
|
||||
// for each chunk in the rope:
|
||||
for (int i = chunkOffsets[s]; i < chunkOffsets[s + 1]; ++i)
|
||||
{
|
||||
int firstFrame = frameOffsets[i];
|
||||
int frameCount = frameCounts[i];
|
||||
|
||||
for (int f = 0; f < frameCount; ++f)
|
||||
{
|
||||
// Calculate previous and next curve indices:
|
||||
int prevIndex = firstFrame + math.max(f - 1, 0);
|
||||
int index = firstFrame + f;
|
||||
|
||||
// advance v texcoord:
|
||||
vCoord += rendererData[k].uvScale.y * (math.distance(frames[index].position, frames[prevIndex].position) /
|
||||
(rendererData[k].normalizeV == 1 ? smoothLength : actualToRestLengthRatio));
|
||||
|
||||
// calculate section thickness and scale the basis vectors by it:
|
||||
float sectionThickness = frames[index].thickness * rendererData[k].thicknessScale;
|
||||
|
||||
// Loop around each segment:
|
||||
int nextSectionIndex = sectionIndex + 1;
|
||||
for (int j = 0; j <= sectionSegments; ++j)
|
||||
{
|
||||
// make just one copy of the section vertex:
|
||||
float2 sectionVertex = sectionData[sectionStart + j];
|
||||
|
||||
// calculate normal using section vertex, curve normal and binormal:
|
||||
normal.x = (sectionVertex.x * frames[index].normal.x + sectionVertex.y * frames[index].binormal.x) * sectionThickness;
|
||||
normal.y = (sectionVertex.x * frames[index].normal.y + sectionVertex.y * frames[index].binormal.y) * sectionThickness;
|
||||
normal.z = (sectionVertex.x * frames[index].normal.z + sectionVertex.y * frames[index].binormal.z) * sectionThickness;
|
||||
|
||||
// offset curve position by normal:
|
||||
vertex.x = frames[index].position.x + normal.x;
|
||||
vertex.y = frames[index].position.y + normal.y;
|
||||
vertex.z = frames[index].position.z + normal.z;
|
||||
|
||||
// cross(normal, curve tangent)
|
||||
texTangent.xyz = math.cross(normal, frames[index].tangent);
|
||||
texTangent.w = -1;
|
||||
|
||||
vertices[firstVertex + sectionIndex * verticesPerSection + j] = new ProceduralRopeVertex
|
||||
{
|
||||
pos = vertex,
|
||||
normal = normal,
|
||||
tangent = texTangent,
|
||||
color = frames[index].color,
|
||||
uv = new float2(j / (float)sectionSegments * rendererData[k].uvScale.x, vCoord)
|
||||
};
|
||||
|
||||
if (j < sectionSegments && f < frameCount - 1)
|
||||
{
|
||||
int offset = firstTriangle * 3;
|
||||
tris[offset + tri++] = (firstVertex + sectionIndex * verticesPerSection + j);
|
||||
tris[offset + tri++] = (firstVertex + nextSectionIndex * verticesPerSection + j);
|
||||
tris[offset + tri++] = (firstVertex + sectionIndex * verticesPerSection + (j + 1));
|
||||
|
||||
tris[offset + tri++] = (firstVertex + sectionIndex * verticesPerSection + (j + 1));
|
||||
tris[offset + tri++] = (firstVertex + nextSectionIndex * verticesPerSection + j);
|
||||
tris[offset + tri++] = (firstVertex + nextSectionIndex * verticesPerSection + (j + 1));
|
||||
}
|
||||
}
|
||||
sectionIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10b89fddf68724bc9ad3305774d86ee1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,215 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstLineRopeRenderSystem : ObiLineRopeRenderSystem
|
||||
{
|
||||
public BurstLineRopeRenderSystem(ObiSolver solver) : base(solver)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
|
||||
// Initialize each batch:
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
batchList[i].Initialize(layout, false);
|
||||
}
|
||||
|
||||
public override void Render(){}
|
||||
|
||||
public override void RenderFromCamera(Camera camera)
|
||||
{
|
||||
if (pathSmootherSystem == null)
|
||||
return;
|
||||
|
||||
using (m_RenderMarker.Auto())
|
||||
{
|
||||
var handles = new NativeArray<JobHandle>(batchList.Count, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
{
|
||||
var batch = batchList[i];
|
||||
|
||||
var meshJob = new BuildLineMesh
|
||||
{
|
||||
pathSmootherIndices = pathSmootherIndices.AsNativeArray<int>(),
|
||||
chunkOffsets = pathSmootherSystem.chunkOffsets.AsNativeArray<int>(),
|
||||
|
||||
frames = pathSmootherSystem.smoothFrames.AsNativeArray<BurstPathFrame>(),
|
||||
frameOffsets = pathSmootherSystem.smoothFrameOffsets.AsNativeArray<int>(),
|
||||
frameCounts = pathSmootherSystem.smoothFrameCounts.AsNativeArray<int>(),
|
||||
|
||||
vertexOffsets = vertexOffsets.AsNativeArray<int>(),
|
||||
triangleOffsets = triangleOffsets.AsNativeArray<int>(),
|
||||
triangleCounts = triangleCounts.AsNativeArray<int>(),
|
||||
|
||||
pathData = pathSmootherSystem.pathData.AsNativeArray<BurstPathSmootherData>(),
|
||||
rendererData = rendererData.AsNativeArray<BurstLineMeshData>(),
|
||||
|
||||
vertices = batch.vertices,
|
||||
tris = batch.triangles,
|
||||
|
||||
firstRenderer = batch.firstRenderer,
|
||||
localSpaceCamera = m_Solver.transform.InverseTransformPoint(camera.transform.position)
|
||||
};
|
||||
handles[i] = meshJob.Schedule(batch.rendererCount, 1);
|
||||
}
|
||||
|
||||
JobHandle.CombineDependencies(handles).Complete();
|
||||
handles.Dispose();
|
||||
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
{
|
||||
var batch = batchList[i];
|
||||
|
||||
batch.mesh.SetVertexBufferData(batch.vertices, 0, 0, batch.vertexCount, 0, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontRecalculateBounds);
|
||||
batch.mesh.SetIndexBufferData(batch.triangles, 0, 0, batch.triangleCount * 3, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontRecalculateBounds);
|
||||
|
||||
var rp = batch.renderParams;
|
||||
rp.worldBounds = m_Solver.bounds;
|
||||
|
||||
Graphics.RenderMesh(rp, batch.mesh, 0, m_Solver.transform.localToWorldMatrix, m_Solver.transform.localToWorldMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct BuildLineMesh : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> pathSmootherIndices;
|
||||
[ReadOnly] public NativeArray<int> chunkOffsets;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstPathFrame> frames;
|
||||
[ReadOnly] public NativeArray<int> frameOffsets;
|
||||
[ReadOnly] public NativeArray<int> frameCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<int> vertexOffsets;
|
||||
[ReadOnly] public NativeArray<int> triangleOffsets;
|
||||
[ReadOnly] public NativeArray<int> triangleCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstLineMeshData> rendererData;
|
||||
[ReadOnly] public NativeArray<BurstPathSmootherData> pathData;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<ProceduralRopeVertex> vertices;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> tris;
|
||||
|
||||
[ReadOnly] public int firstRenderer;
|
||||
|
||||
[ReadOnly] public float3 localSpaceCamera;
|
||||
|
||||
public void Execute(int u)
|
||||
{
|
||||
int k = firstRenderer + u;
|
||||
int s = pathSmootherIndices[k];
|
||||
|
||||
float3 vertex = float3.zero;
|
||||
float3 normal = float3.zero;
|
||||
float4 bitangent = float4.zero;
|
||||
|
||||
int tri = 0;
|
||||
int sectionIndex = 0;
|
||||
int firstVertex = vertexOffsets[k];
|
||||
int firstTriangle = triangleOffsets[k];
|
||||
|
||||
float smoothLength = 0;
|
||||
for (int i = chunkOffsets[s]; i < chunkOffsets[s + 1]; ++i)
|
||||
smoothLength += pathData[i].smoothLength;
|
||||
|
||||
float vCoord = -rendererData[k].uvScale.y * pathData[chunkOffsets[s]].restLength * rendererData[k].uvAnchor;
|
||||
float actualToRestLengthRatio = smoothLength / pathData[chunkOffsets[s]].restLength;
|
||||
|
||||
// clear out triangle indices for this rope:
|
||||
for (int i = firstTriangle; i < firstTriangle + triangleCounts[k]; ++i)
|
||||
{
|
||||
int offset = i * 3;
|
||||
tris[offset] = 0;
|
||||
tris[offset+1] = 0;
|
||||
tris[offset+2] = 0;
|
||||
}
|
||||
|
||||
// for each chunk in the rope:
|
||||
for (int i = chunkOffsets[s]; i < chunkOffsets[s + 1]; ++i)
|
||||
{
|
||||
int firstFrame = frameOffsets[i];
|
||||
int frameCount = frameCounts[i];
|
||||
|
||||
for (int f = 0; f < frameCount; ++f)
|
||||
{
|
||||
// Calculate previous and next curve indices:
|
||||
int prevIndex = firstFrame + math.max(f - 1, 0);
|
||||
int index = firstFrame + f;
|
||||
|
||||
// advance v texcoord:
|
||||
vCoord += rendererData[k].uvScale.y * (math.distance(frames[index].position, frames[prevIndex].position) /
|
||||
(rendererData[k].normalizeV == 1 ? smoothLength : actualToRestLengthRatio));
|
||||
|
||||
// calculate section thickness and scale the basis vectors by it:
|
||||
float sectionThickness = frames[index].thickness * rendererData[k].thicknessScale;
|
||||
|
||||
normal.x = frames[index].position.x - localSpaceCamera.x;
|
||||
normal.y = frames[index].position.y - localSpaceCamera.y;
|
||||
normal.z = frames[index].position.z - localSpaceCamera.z;
|
||||
normal = math.normalize(normal);
|
||||
|
||||
bitangent.x = -(normal.y * frames[index].tangent.z - normal.z * frames[index].tangent.y);
|
||||
bitangent.y = -(normal.z * frames[index].tangent.x - normal.x * frames[index].tangent.z);
|
||||
bitangent.z = -(normal.x * frames[index].tangent.y - normal.y * frames[index].tangent.x);
|
||||
bitangent.xyz = math.normalize(bitangent.xyz);
|
||||
bitangent.w = 1;
|
||||
|
||||
vertex.x = frames[index].position.x - bitangent.x * sectionThickness;
|
||||
vertex.y = frames[index].position.y - bitangent.y * sectionThickness;
|
||||
vertex.z = frames[index].position.z - bitangent.z * sectionThickness;
|
||||
|
||||
vertices[firstVertex + sectionIndex * 2] = new ProceduralRopeVertex
|
||||
{
|
||||
pos = vertex,
|
||||
normal = -normal,
|
||||
tangent = bitangent,
|
||||
color = frames[index].color,
|
||||
uv = new float2(0, vCoord)
|
||||
};
|
||||
|
||||
vertex.x = frames[index].position.x + bitangent.x * sectionThickness;
|
||||
vertex.y = frames[index].position.y + bitangent.y * sectionThickness;
|
||||
vertex.z = frames[index].position.z + bitangent.z * sectionThickness;
|
||||
|
||||
vertices[firstVertex + sectionIndex * 2 + 1] = new ProceduralRopeVertex
|
||||
{
|
||||
pos = vertex,
|
||||
normal = -normal,
|
||||
tangent = bitangent,
|
||||
color = frames[index].color,
|
||||
uv = new float2(1, vCoord)
|
||||
};
|
||||
|
||||
if (f < frameCount - 1)
|
||||
{
|
||||
int offset = firstTriangle * 3;
|
||||
tris[offset + tri++] = firstVertex + sectionIndex * 2;
|
||||
tris[offset + tri++] = firstVertex + (sectionIndex + 1) * 2;
|
||||
tris[offset + tri++] = firstVertex + sectionIndex * 2 + 1;
|
||||
|
||||
tris[offset + tri++] = firstVertex + sectionIndex * 2 + 1;
|
||||
tris[offset + tri++] = firstVertex + (sectionIndex + 1) * 2;
|
||||
tris[offset + tri++] = firstVertex + (sectionIndex + 1) * 2 + 1;
|
||||
}
|
||||
|
||||
sectionIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f950cc46a13e4273856e2ec96c59eb9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,272 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstMeshRopeRenderSystem : ObiMeshRopeRenderSystem
|
||||
{
|
||||
public BurstMeshRopeRenderSystem(ObiSolver solver) : base(solver)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void CloseBatches()
|
||||
{
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
batchList[i].Initialize(sortedRenderers, meshData, meshIndices, layout, false);
|
||||
|
||||
base.CloseBatches();
|
||||
}
|
||||
|
||||
public override void Render()
|
||||
{
|
||||
if (pathSmootherSystem == null)
|
||||
return;
|
||||
|
||||
using (m_RenderMarker.Auto())
|
||||
{
|
||||
var handles = new NativeArray<JobHandle>(batchList.Count, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
{
|
||||
var batch = batchList[i];
|
||||
|
||||
var meshJob = new BuildRopeMeshJob
|
||||
{
|
||||
chunkOffsets = pathSmootherSystem.chunkOffsets.AsNativeArray<int>(),
|
||||
pathSmootherIndices = pathSmootherIndices.AsNativeArray<int>(),
|
||||
|
||||
frames = pathSmootherSystem.smoothFrames.AsNativeArray<BurstPathFrame>(),
|
||||
frameOffsets = pathSmootherSystem.smoothFrameOffsets.AsNativeArray<int>(),
|
||||
frameCounts = pathSmootherSystem.smoothFrameCounts.AsNativeArray<int>(),
|
||||
|
||||
vertexOffsets = vertexOffsets.AsNativeArray<int>(),
|
||||
|
||||
meshIndices = meshIndices.AsNativeArray<int>(),
|
||||
meshData = meshData.meshData.AsNativeArray<MeshDataBatch.MeshData>(),
|
||||
|
||||
rendererData = rendererData.AsNativeArray<BurstMeshData>(),
|
||||
pathData = pathSmootherSystem.pathData.AsNativeArray<BurstPathSmootherData>(),
|
||||
|
||||
sortedIndices = sortedIndices.AsNativeArray<int>(),
|
||||
sortedOffsets = sortedOffsets.AsNativeArray<int>(),
|
||||
|
||||
positions = meshData.restPositions.AsNativeArray<float3>(),
|
||||
normals = meshData.restNormals.AsNativeArray<float3>(),
|
||||
tangents = meshData.restTangents.AsNativeArray<float4>(),
|
||||
colors = meshData.restColors.AsNativeArray<float4>(),
|
||||
|
||||
vertices = batch.dynamicVertexData.AsNativeArray<RopeMeshVertex>(),
|
||||
|
||||
firstRenderer = batch.firstRenderer
|
||||
|
||||
};
|
||||
handles[i] = meshJob.Schedule(batch.rendererCount, 1);
|
||||
}
|
||||
|
||||
JobHandle.CombineDependencies(handles).Complete();
|
||||
handles.Dispose();
|
||||
|
||||
for (int i = 0; i < batchList.Count; ++i)
|
||||
{
|
||||
var batch = batchList[i];
|
||||
|
||||
batch.mesh.SetVertexBufferData(batch.dynamicVertexData.AsNativeArray<DynamicBatchVertex>(), 0, 0, batch.vertexCount, 0, MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontRecalculateBounds | MeshUpdateFlags.DontResetBoneBounds | MeshUpdateFlags.DontNotifyMeshUsers);
|
||||
|
||||
var rp = batch.renderParams;
|
||||
rp.worldBounds = m_Solver.bounds;
|
||||
|
||||
for (int m = 0; m < batch.materials.Length; ++m)
|
||||
{
|
||||
rp.material = batch.materials[m];
|
||||
Graphics.RenderMesh(rp, batch.mesh, m, m_Solver.transform.localToWorldMatrix, m_Solver.transform.localToWorldMatrix);
|
||||
|
||||
// Unity bug: Graphics.RenderMesh consistently crashes when existing play mode (seems fixed in 2021.3.4f1)
|
||||
// https://issuetracker.unity3d.com/issues/the-editor-crashes-on-exit-when-using-graphics-dot-rendermesh
|
||||
//renderParams.material = batch.materials[m];
|
||||
//renderParams.camera = null;
|
||||
//Graphics.RenderMesh(renderParams, batch.mesh, m, m_Solver.transform.localToWorldMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct BuildRopeMeshJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> pathSmootherIndices;
|
||||
[ReadOnly] public NativeArray<int> chunkOffsets;
|
||||
[ReadOnly] public NativeArray<BurstPathFrame> frames;
|
||||
[ReadOnly] public NativeArray<int> frameOffsets;
|
||||
[ReadOnly] public NativeArray<int> frameCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<int> vertexOffsets;
|
||||
|
||||
[ReadOnly] public NativeArray<int> meshIndices;
|
||||
[ReadOnly] public NativeArray<MeshDataBatch.MeshData> meshData;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstMeshData> rendererData;
|
||||
[ReadOnly] public NativeArray<BurstPathSmootherData> pathData;
|
||||
|
||||
[ReadOnly] public NativeArray<int> sortedIndices;
|
||||
[ReadOnly] public NativeArray<int> sortedOffsets;
|
||||
|
||||
[ReadOnly] public NativeArray<float3> positions;
|
||||
[ReadOnly] public NativeArray<float3> normals;
|
||||
[ReadOnly] public NativeArray<float4> tangents;
|
||||
[ReadOnly] public NativeArray<float4> colors;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<RopeMeshVertex> vertices;
|
||||
|
||||
[ReadOnly] public int firstRenderer;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int rendererIndex = firstRenderer + i;
|
||||
int pathIndex = pathSmootherIndices[rendererIndex];
|
||||
var renderer = rendererData[rendererIndex];
|
||||
|
||||
// get mesh data:
|
||||
var mesh = meshData[meshIndices[rendererIndex]];
|
||||
var sortedOffset = sortedOffsets[rendererIndex];
|
||||
|
||||
// get index of first output vertex:
|
||||
int firstOutputVertex = vertexOffsets[rendererIndex];
|
||||
|
||||
// get index of first chunk, ignore others (no support for tearing):
|
||||
int chunkIndex = chunkOffsets[pathIndex];
|
||||
|
||||
// get first frame and frame count:
|
||||
int firstFrame = frameOffsets[chunkIndex];
|
||||
int lastFrame = firstFrame + frameCounts[chunkIndex] - 1;
|
||||
|
||||
// get mesh deform axis:
|
||||
int axis = (int)renderer.axis;
|
||||
|
||||
// initialize scale vector:
|
||||
float3 actualScale = (Vector3)renderer.scale;
|
||||
|
||||
// calculate stretch ratio:
|
||||
float stretchRatio = renderer.stretchWithRope == 1 ? pathData[chunkIndex].smoothLength / pathData[chunkIndex].restLength : 1;
|
||||
|
||||
// squashing factor, makes mesh thinner when stretched and thicker when compresssed.
|
||||
float squashing = math.clamp(1 + renderer.volumeScaling * (1 / math.max(stretchRatio, 0.01f) - 1), 0.01f, 2);
|
||||
|
||||
// calculate scale along swept axis so that the mesh spans the entire lenght of the rope if required.
|
||||
if (renderer.spanEntireLength == 1)
|
||||
{
|
||||
float totalMeshLength = renderer.meshSizeAlongAxis * renderer.instances;
|
||||
float totalSpacing = renderer.instanceSpacing * (renderer.instances - 1);
|
||||
actualScale[axis] = pathData[chunkIndex].restLength / (totalMeshLength + totalSpacing);
|
||||
}
|
||||
|
||||
// adjust axis lenght by stretch ratio:
|
||||
actualScale[axis] *= stretchRatio;
|
||||
|
||||
// init loop variables:
|
||||
float lengthAlongAxis = renderer.offset;
|
||||
int index = firstFrame;
|
||||
int nextIndex = firstFrame + 1;
|
||||
int prevIndex = firstFrame;
|
||||
float nextMagnitude = math.distance(frames[index].position, frames[nextIndex].position);
|
||||
float prevMagnitude = nextMagnitude;
|
||||
|
||||
for (int k = 0; k < renderer.instances; ++k)
|
||||
{
|
||||
for (int j = 0; j < mesh.vertexCount; ++j)
|
||||
{
|
||||
int currVIndex = mesh.firstVertex + sortedIndices[sortedOffset + j];
|
||||
int prevVIndex = mesh.firstVertex + sortedIndices[sortedOffset + math.max(0,j - 1)];
|
||||
|
||||
// calculate how much we've advanced in the sort axis since the last vertex:
|
||||
lengthAlongAxis += (positions[currVIndex][axis] - positions[prevVIndex][axis]) * actualScale[axis];
|
||||
|
||||
// check if we have moved to a new section of the curve:
|
||||
BurstPathFrame frame;
|
||||
if (lengthAlongAxis < 0)
|
||||
{
|
||||
while (-lengthAlongAxis > prevMagnitude && index > firstFrame)
|
||||
{
|
||||
lengthAlongAxis += prevMagnitude;
|
||||
index = math.max(index - 1, firstFrame);
|
||||
nextIndex = math.min(index + 1, lastFrame);
|
||||
prevIndex = math.max(index - 1, firstFrame);
|
||||
nextMagnitude = math.distance(frames[index].position, frames[nextIndex].position);
|
||||
prevMagnitude = math.distance(frames[index].position, frames[prevIndex].position);
|
||||
}
|
||||
|
||||
var offset = float3.zero;
|
||||
if (index == prevIndex)
|
||||
{
|
||||
offset = frames[index].position - frames[nextIndex].position;
|
||||
prevMagnitude = math.length(offset);
|
||||
}
|
||||
|
||||
frame = InterpolateFrames(frames[index], frames[prevIndex], offset, -lengthAlongAxis / prevMagnitude);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (lengthAlongAxis > nextMagnitude && index < lastFrame)
|
||||
{
|
||||
lengthAlongAxis -= nextMagnitude;
|
||||
index = math.min(index + 1, lastFrame);
|
||||
nextIndex = math.min(index + 1, lastFrame);
|
||||
prevIndex = math.max(index - 1, firstFrame);
|
||||
nextMagnitude = math.distance(frames[index].position, frames[nextIndex].position);
|
||||
prevMagnitude = math.distance(frames[index].position, frames[prevIndex].position);
|
||||
}
|
||||
|
||||
var offset = float3.zero;
|
||||
if (index == nextIndex)
|
||||
{
|
||||
offset = frames[index].position - frames[prevIndex].position;
|
||||
nextMagnitude = math.length(offset);
|
||||
}
|
||||
|
||||
frame = InterpolateFrames(frames[index], frames[nextIndex], offset, lengthAlongAxis / nextMagnitude);
|
||||
}
|
||||
|
||||
// update basis matrix:
|
||||
var basis = frame.ToMatrix(axis);
|
||||
|
||||
// calculate vertex offset from curve:
|
||||
float3 offsetFromCurve = positions[currVIndex] * actualScale * frame.thickness * squashing;
|
||||
offsetFromCurve[axis] = 0;
|
||||
|
||||
// write modified vertex data:
|
||||
vertices[firstOutputVertex + sortedIndices[sortedOffset + j]] = new RopeMeshVertex
|
||||
{
|
||||
pos = frame.position + math.mul(basis, offsetFromCurve),
|
||||
normal = math.mul(basis, normals[currVIndex]),
|
||||
tangent = new float4(math.mul(basis, tangents[currVIndex].xyz), tangents[currVIndex].w),
|
||||
color = colors[currVIndex] * frame.color,
|
||||
};
|
||||
}
|
||||
|
||||
firstOutputVertex += mesh.vertexCount;
|
||||
lengthAlongAxis += renderer.instanceSpacing * actualScale[axis];
|
||||
}
|
||||
}
|
||||
|
||||
BurstPathFrame InterpolateFrames(BurstPathFrame a, BurstPathFrame b, float3 bOffset, float t)
|
||||
{
|
||||
// this offset is used to displace a copy of the first and last frames of the path,
|
||||
// to ensure meshes extrude correctly prior to the first or past the last frame.
|
||||
b.position += bOffset;
|
||||
var interp = (1 - t) * a + t * b;
|
||||
|
||||
// (no need to renormalize tangent, since offsetFromCurve[axis] = 0)
|
||||
interp.normal = math.normalize(interp.normal);
|
||||
interp.binormal = math.normalize(interp.binormal);
|
||||
return interp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1578f081848d045dba76cd147f835e7c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,170 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct BurstPathFrame
|
||||
{
|
||||
public enum Axis
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Z = 2
|
||||
}
|
||||
|
||||
public float3 position;
|
||||
public float3 tangent;
|
||||
public float3 normal;
|
||||
public float3 binormal;
|
||||
public float4 color;
|
||||
public float thickness;
|
||||
|
||||
public BurstPathFrame(float3 position, float3 tangent, float3 normal, float3 binormal, float4 color, float thickness)
|
||||
{
|
||||
this.position = position;
|
||||
this.normal = normal;
|
||||
this.tangent = tangent;
|
||||
this.binormal = binormal;
|
||||
this.color = color;
|
||||
this.thickness = thickness;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
position = float3.zero;
|
||||
tangent = new float3(0, 0, 1);
|
||||
normal = new float3(0, 1, 0);
|
||||
binormal = new float3(1, 0, 0);
|
||||
color = new float4(1, 1, 1, 1);
|
||||
thickness = 0;
|
||||
}
|
||||
|
||||
public static BurstPathFrame operator +(BurstPathFrame c1, BurstPathFrame c2)
|
||||
{
|
||||
return new BurstPathFrame(c1.position + c2.position, c1.tangent + c2.tangent, c1.normal + c2.normal, c1.binormal + c2.binormal, c1.color + c2.color, c1.thickness + c2.thickness);
|
||||
}
|
||||
|
||||
public static BurstPathFrame operator *(float f, BurstPathFrame c)
|
||||
{
|
||||
return new BurstPathFrame(c.position * f, c.tangent * f, c.normal * f, c.binormal * f, c.color * f, c.thickness * f);
|
||||
}
|
||||
|
||||
public static void WeightedSum(float w1, float w2, float w3, in BurstPathFrame c1, in BurstPathFrame c2, in BurstPathFrame c3, ref BurstPathFrame sum)
|
||||
{
|
||||
sum.position.x = c1.position.x * w1 + c2.position.x * w2 + c3.position.x * w3;
|
||||
sum.position.y = c1.position.y * w1 + c2.position.y * w2 + c3.position.y * w3;
|
||||
sum.position.z = c1.position.z * w1 + c2.position.z * w2 + c3.position.z * w3;
|
||||
|
||||
sum.tangent.x = c1.tangent.x * w1 + c2.tangent.x * w2 + c3.tangent.x * w3;
|
||||
sum.tangent.y = c1.tangent.y * w1 + c2.tangent.y * w2 + c3.tangent.y * w3;
|
||||
sum.tangent.z = c1.tangent.z * w1 + c2.tangent.z * w2 + c3.tangent.z * w3;
|
||||
|
||||
sum.normal.x = c1.normal.x * w1 + c2.normal.x * w2 + c3.normal.x * w3;
|
||||
sum.normal.y = c1.normal.y * w1 + c2.normal.y * w2 + c3.normal.y * w3;
|
||||
sum.normal.z = c1.normal.z * w1 + c2.normal.z * w2 + c3.normal.z * w3;
|
||||
|
||||
sum.binormal.x = c1.binormal.x * w1 + c2.binormal.x * w2 + c3.binormal.x * w3;
|
||||
sum.binormal.y = c1.binormal.y * w1 + c2.binormal.y * w2 + c3.binormal.y * w3;
|
||||
sum.binormal.z = c1.binormal.z * w1 + c2.binormal.z * w2 + c3.binormal.z * w3;
|
||||
|
||||
sum.color.x = c1.color.x * w1 + c2.color.x * w2 + c3.color.x * w3;
|
||||
sum.color.y = c1.color.y * w1 + c2.color.y * w2 + c3.color.y * w3;
|
||||
sum.color.z = c1.color.z * w1 + c2.color.z * w2 + c3.color.z * w3;
|
||||
sum.color.w = c1.color.w * w1 + c2.color.w * w2 + c3.color.w * w3;
|
||||
|
||||
sum.thickness = c1.thickness * w1 + c2.thickness * w2 + c3.thickness * w3;
|
||||
}
|
||||
|
||||
public void SetTwist(float twist)
|
||||
{
|
||||
quaternion twistQ = quaternion.AxisAngle(tangent, math.radians(twist));
|
||||
normal = math.mul(twistQ, normal);
|
||||
binormal = math.mul(twistQ, binormal);
|
||||
}
|
||||
|
||||
public static quaternion FromToRotation(float3 aFrom, float3 aTo)
|
||||
{
|
||||
float3 axis = math.cross(aFrom, aTo);
|
||||
float angle = math.acos(math.clamp(math.dot(math.normalize(aFrom), math.normalize(aTo)), -1f, 1f));
|
||||
return quaternion.AxisAngle(math.normalize(axis), angle);
|
||||
}
|
||||
|
||||
public void SetTwistAndTangent(float twist, float3 tangent)
|
||||
{
|
||||
this.tangent = tangent;
|
||||
normal = math.normalize(new float3(tangent.y, tangent.x, 0));
|
||||
binormal = math.cross(normal, tangent);
|
||||
|
||||
quaternion twistQ = quaternion.AxisAngle(tangent, math.radians(twist));
|
||||
normal = math.mul(twistQ, normal);
|
||||
binormal = math.mul(twistQ, binormal);
|
||||
}
|
||||
|
||||
public void Transport(in BurstPathFrame frame, float twist)
|
||||
{
|
||||
// Calculate delta rotation:
|
||||
quaternion rotQ = Quaternion.FromToRotation(tangent, frame.tangent);
|
||||
quaternion twistQ = quaternion.AxisAngle(frame.tangent, math.radians(twist));
|
||||
quaternion finalQ = math.mul(twistQ, rotQ);
|
||||
|
||||
// Rotate previous frame axes to obtain the new ones:
|
||||
normal = math.mul(finalQ, normal);
|
||||
binormal = math.mul(finalQ, binormal);
|
||||
tangent = frame.tangent;
|
||||
position = frame.position;
|
||||
thickness = frame.thickness;
|
||||
color = frame.color;
|
||||
}
|
||||
|
||||
public void Transport(float3 newPosition, float3 newTangent, float twist)
|
||||
{
|
||||
// Calculate delta rotation:
|
||||
quaternion rotQ = Quaternion.FromToRotation(tangent, newTangent);
|
||||
quaternion twistQ = quaternion.AxisAngle(newTangent, math.radians(twist));
|
||||
quaternion finalQ = math.mul(twistQ, rotQ);
|
||||
|
||||
// Rotate previous frame axes to obtain the new ones:
|
||||
normal = math.mul(finalQ, normal);
|
||||
binormal = math.mul(finalQ, binormal);
|
||||
tangent = newTangent;
|
||||
position = newPosition;
|
||||
|
||||
}
|
||||
|
||||
// Transport, hinting the normal.
|
||||
public void Transport(float3 newPosition, float3 newTangent, float3 newNormal, float twist)
|
||||
{
|
||||
normal = math.mul(quaternion.AxisAngle(newTangent, math.radians(twist)), newNormal);
|
||||
tangent = newTangent;
|
||||
binormal = math.cross(normal, tangent);
|
||||
position = newPosition;
|
||||
}
|
||||
|
||||
public float3x3 ToMatrix(int mainAxis)
|
||||
{
|
||||
float3x3 basis = new float3x3();
|
||||
|
||||
int xo = (mainAxis) % 3;
|
||||
int yo = (mainAxis + 1) % 3;
|
||||
int zo = (mainAxis + 2) % 3;
|
||||
|
||||
basis[xo] = tangent;
|
||||
basis[yo] = binormal;
|
||||
basis[zo] = normal;
|
||||
|
||||
return basis;
|
||||
}
|
||||
|
||||
public void DebugDraw(float size)
|
||||
{
|
||||
Debug.DrawRay(position, binormal * size, Color.red);
|
||||
Debug.DrawRay(position, normal * size, Color.green);
|
||||
Debug.DrawRay(position, tangent * size, Color.blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4827bd4bd4feb46bfbe458174871b8ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,63 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstPathSmootherRenderSystem : ObiPathSmootherRenderSystem
|
||||
{
|
||||
public BurstPathSmootherRenderSystem(ObiSolver solver) : base(solver)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Render()
|
||||
{
|
||||
using (m_RenderMarker.Auto())
|
||||
{
|
||||
base.Render();
|
||||
|
||||
// generate raw frames using parallel transport
|
||||
var parallelTransportJob = new ParallelTransportJob
|
||||
{
|
||||
pathFrames = rawFrames.AsNativeArray<BurstPathFrame>(),
|
||||
frameOffsets = rawFrameOffsets.AsNativeArray<int>(),
|
||||
particleIndices = particleIndices.AsNativeArray<int>(),
|
||||
renderablePositions = m_Solver.renderablePositions.AsNativeArray<float4>(),
|
||||
renderableOrientations = m_Solver.renderableOrientations.AsNativeArray<quaternion>(),
|
||||
principalRadii = m_Solver.principalRadii.AsNativeArray<float4>(),
|
||||
colors = m_Solver.colors.AsNativeArray<float4>(),
|
||||
pathData = pathData.AsNativeArray<BurstPathSmootherData>()
|
||||
};
|
||||
|
||||
var handle = parallelTransportJob.Schedule(rawFrameOffsets.count, 4);
|
||||
|
||||
// throw away unneeded frames using decimation
|
||||
var decimationJob = new DecimateChunksJob
|
||||
{
|
||||
inputFrames = rawFrames.AsNativeArray<BurstPathFrame>(),
|
||||
inputFrameOffsets = rawFrameOffsets.AsNativeArray<int>(),
|
||||
outputFrameCounts = decimatedFrameCounts.AsNativeArray<int>(),
|
||||
pathData = pathData.AsNativeArray<BurstPathSmootherData>()
|
||||
};
|
||||
|
||||
handle = decimationJob.Schedule(rawFrameOffsets.count, 4, handle);
|
||||
|
||||
// smooth chunks:
|
||||
var chaikinJob = new ChaikinSmoothChunksJob()
|
||||
{
|
||||
inputFrames = rawFrames.AsNativeArray<BurstPathFrame>(),
|
||||
inputFrameOffsets = rawFrameOffsets.AsNativeArray<int>(),
|
||||
inputFrameCounts = decimatedFrameCounts.AsNativeArray<int>(),
|
||||
outputFrames = smoothFrames.AsNativeArray<BurstPathFrame>(),
|
||||
outputFrameOffsets = smoothFrameOffsets.AsNativeArray<int>(),
|
||||
outputFrameCounts = smoothFrameCounts.AsNativeArray<int>(),
|
||||
pathData = pathData.AsNativeArray<BurstPathSmootherData>()
|
||||
};
|
||||
|
||||
chaikinJob.Schedule(rawFrameOffsets.count, 4, handle).Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be61f37ff31ff43ebb9bc8a55bd3edc7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,93 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[BurstCompile]
|
||||
struct ChaikinSmoothChunksJob : IJobParallelFor
|
||||
{
|
||||
[NativeDisableParallelForRestriction] public NativeArray<BurstPathFrame> inputFrames;
|
||||
[ReadOnly] public NativeArray<int> inputFrameOffsets;
|
||||
[ReadOnly] public NativeArray<int> inputFrameCounts;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<BurstPathFrame> outputFrames;
|
||||
[ReadOnly] public NativeArray<int> outputFrameOffsets;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> outputFrameCounts;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<BurstPathSmootherData> pathData;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int firstInputIndex = i > 0 ? inputFrameOffsets[i - 1] : 0;
|
||||
int inputFrameCount = inputFrameCounts[i];
|
||||
|
||||
int firstOutputIndex = outputFrameOffsets[i];
|
||||
|
||||
int k = (int)pathData[i].smoothing;
|
||||
|
||||
// No work to do. just copy the input to the output:
|
||||
if (k == 0)
|
||||
{
|
||||
outputFrameCounts[i] = inputFrameCount;
|
||||
for (int j = 0; j < inputFrameCount; ++j)
|
||||
outputFrames[firstOutputIndex + j] = inputFrames[firstInputIndex + j];
|
||||
}
|
||||
else
|
||||
{
|
||||
// precalculate some quantities:
|
||||
int pCount = (int)math.pow(2, k);
|
||||
int n0 = inputFrameCount - 1;
|
||||
float twoRaisedToMinusKPlus1 = math.pow(2, -(k + 1));
|
||||
float twoRaisedToMinusK = math.pow(2, -k);
|
||||
float twoRaisedToMinus2K = math.pow(2, -2 * k);
|
||||
float twoRaisedToMinus2KMinus1 = math.pow(2, -2 * k - 1);
|
||||
|
||||
outputFrameCounts[i] = (inputFrameCount - 2) * pCount + 2;
|
||||
|
||||
// calculate initial curve points:
|
||||
outputFrames[firstOutputIndex] = (0.5f + twoRaisedToMinusKPlus1) * inputFrames[firstInputIndex] + (0.5f - twoRaisedToMinusKPlus1) * inputFrames[firstInputIndex + 1];
|
||||
outputFrames[firstOutputIndex + pCount * n0 - pCount + 1] = (0.5f - twoRaisedToMinusKPlus1) * inputFrames[firstInputIndex + n0 - 1] + (0.5f + twoRaisedToMinusKPlus1) * inputFrames[firstInputIndex + n0];
|
||||
|
||||
// calculate internal points:
|
||||
for (int j = 1; j <= pCount; ++j)
|
||||
{
|
||||
// precalculate coefficients:
|
||||
float F = 0.5f - twoRaisedToMinusKPlus1 - (j - 1) * (twoRaisedToMinusK - j * twoRaisedToMinus2KMinus1);
|
||||
float G = 0.5f + twoRaisedToMinusKPlus1 + (j - 1) * (twoRaisedToMinusK - j * twoRaisedToMinus2K);
|
||||
float H = (j - 1) * j * twoRaisedToMinus2KMinus1;
|
||||
|
||||
for (int l = 1; l < n0; ++l)
|
||||
{
|
||||
BurstPathFrame.WeightedSum(F, G, H,
|
||||
in GetElementAsRef(inputFrames, firstInputIndex + l - 1),
|
||||
in GetElementAsRef(inputFrames, firstInputIndex + l),
|
||||
in GetElementAsRef(inputFrames, firstInputIndex + l + 1),
|
||||
ref GetElementAsRef(outputFrames, firstOutputIndex + (l - 1) * pCount + j));
|
||||
}
|
||||
}
|
||||
|
||||
// make first and last curve points coincide with original points:
|
||||
outputFrames[firstOutputIndex] = inputFrames[firstInputIndex];
|
||||
outputFrames[firstOutputIndex + outputFrameCounts[i] - 1] = inputFrames[firstInputIndex + inputFrameCount - 1];
|
||||
}
|
||||
|
||||
var data = pathData[i];
|
||||
data.smoothLength = 0;
|
||||
for (int j = firstOutputIndex + 1; j < firstOutputIndex + outputFrameCounts[i]; ++j)
|
||||
data.smoothLength += math.distance(outputFrames[j - 1].position, outputFrames[j].position);
|
||||
|
||||
pathData[i] = data;
|
||||
|
||||
}
|
||||
|
||||
private static unsafe ref T GetElementAsRef<T>(NativeArray<T> array, int index) where T : unmanaged
|
||||
{
|
||||
return ref UnsafeUtility.ArrayElementAsRef<T>(array.GetUnsafePtr(), index);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b479f3e1bd184363a9259c4d737b611
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,79 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[BurstCompile]
|
||||
struct DecimateChunksJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> inputFrameOffsets;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<BurstPathFrame> inputFrames;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> outputFrameCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstPathSmootherData> pathData;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
|
||||
int firstInputIndex = i > 0 ? inputFrameOffsets[i - 1] : 0;
|
||||
int inputFrameCount = inputFrameOffsets[i] - firstInputIndex;
|
||||
|
||||
// no decimation, no work to do, just return:
|
||||
if (pathData[i].decimation < 0.00001f || inputFrameCount < 3)
|
||||
{
|
||||
outputFrameCounts[i] = inputFrameCount;
|
||||
return;
|
||||
}
|
||||
|
||||
float scaledThreshold = pathData[i].decimation * pathData[i].decimation * 0.01f;
|
||||
|
||||
int start = 0;
|
||||
int end = inputFrameCount - 1;
|
||||
outputFrameCounts[i] = 0;
|
||||
|
||||
while (start < end)
|
||||
{
|
||||
// add starting point:
|
||||
inputFrames[firstInputIndex + outputFrameCounts[i]++] = inputFrames[firstInputIndex + start];
|
||||
|
||||
var newEnd = end;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int maxDistanceIndex = 0;
|
||||
float maxDistance = 0;
|
||||
|
||||
// find the point that's furthest away from the current segment:
|
||||
for (int k = start + 1; k < newEnd; k++)
|
||||
{
|
||||
var nearest = BurstMath.NearestPointOnEdge(inputFrames[firstInputIndex + start].position,
|
||||
inputFrames[firstInputIndex + newEnd].position,
|
||||
inputFrames[firstInputIndex + k].position, out _);
|
||||
float d = math.lengthsq(nearest - inputFrames[firstInputIndex + k].position);
|
||||
|
||||
if (d > maxDistance)
|
||||
{
|
||||
maxDistanceIndex = k;
|
||||
maxDistance = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxDistance <= scaledThreshold)
|
||||
break;
|
||||
|
||||
newEnd = maxDistanceIndex;
|
||||
}
|
||||
|
||||
start = newEnd;
|
||||
}
|
||||
|
||||
// add the last point:
|
||||
inputFrames[firstInputIndex + outputFrameCounts[i]++] = inputFrames[firstInputIndex + end];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e3177e6c6643442db096e939f5d7109
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,89 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[BurstCompile]
|
||||
struct ParallelTransportJob : IJobParallelFor
|
||||
{
|
||||
[NativeDisableParallelForRestriction] public NativeArray<BurstPathFrame> pathFrames;
|
||||
[ReadOnly] public NativeArray<int> frameOffsets;
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
[ReadOnly] public NativeArray<quaternion> renderableOrientations;
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public NativeArray<float4> colors;
|
||||
[ReadOnly] public NativeArray<BurstPathSmootherData> pathData;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
BurstPathFrame nextFrame = new BurstPathFrame();
|
||||
BurstPathFrame currFrame = new BurstPathFrame();
|
||||
BurstPathFrame prevFrame = new BurstPathFrame();
|
||||
|
||||
nextFrame.Reset();
|
||||
currFrame.Reset();
|
||||
prevFrame.Reset();
|
||||
|
||||
int firstIndex = i > 0 ? frameOffsets[i - 1] : 0;
|
||||
int frameCount = frameOffsets[i] - firstIndex;
|
||||
|
||||
// initialize current and previous frame:
|
||||
PathFrameFromParticle(ref currFrame, particleIndices[firstIndex], pathData[i].usesOrientedParticles == 1, false);
|
||||
prevFrame = currFrame;
|
||||
|
||||
// parallel transport:
|
||||
for (int m = 1; m <= frameCount; ++m)
|
||||
{
|
||||
int index = firstIndex + math.min(m, frameCount - 1);
|
||||
int pIndex = particleIndices[index];
|
||||
|
||||
// generate curve frame from particle:
|
||||
PathFrameFromParticle(ref nextFrame, pIndex, pathData[i].usesOrientedParticles == 1);
|
||||
|
||||
if (pathData[i].usesOrientedParticles == 1)
|
||||
{
|
||||
// copy frame directly.
|
||||
prevFrame = currFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
// perform parallel transport, using forward / backward average to calculate tangent.
|
||||
// if the average is too small, reuse the previous frame tangent.
|
||||
currFrame.tangent = math.normalizesafe((currFrame.position - prevFrame.position) +
|
||||
(nextFrame.position - currFrame.position), prevFrame.tangent);
|
||||
prevFrame.Transport(currFrame, pathData[i].twist);
|
||||
}
|
||||
|
||||
// advance current frame:
|
||||
currFrame = nextFrame;
|
||||
pathFrames[firstIndex + m - 1] = prevFrame;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void PathFrameFromParticle(ref BurstPathFrame frame, int particleIndex, bool useOrientedParticles, bool interpolateOrientation = false)
|
||||
{
|
||||
// Update current frame values from particles:
|
||||
frame.position = renderablePositions[particleIndex].xyz;
|
||||
frame.thickness = principalRadii[particleIndex][0];
|
||||
frame.color = colors[particleIndex];
|
||||
|
||||
// Use particle orientation if possible:
|
||||
if (useOrientedParticles)
|
||||
{
|
||||
quaternion current = renderableOrientations[particleIndex];
|
||||
quaternion previous = renderableOrientations[math.max(0, particleIndex - 1)];
|
||||
float4x4 average = (interpolateOrientation ? math.slerp(current, previous, 0.5f) : current).toMatrix();
|
||||
frame.normal = average.c1.xyz;
|
||||
frame.binormal = average.c0.xyz;
|
||||
frame.tangent = average.c2.xyz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c4018fb5d53646be9ef6c91f07c7eb6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -24,12 +24,8 @@ namespace Obi
|
||||
[ReadOnly] public float worldAngularInertiaScale;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> velocities;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> wind;
|
||||
|
||||
[ReadOnly] public float deltaTime;
|
||||
[ReadOnly] public float4 ambientWind;
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
[ReadOnly] public bool inertialWind;
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
@@ -44,14 +40,6 @@ namespace Obi
|
||||
|
||||
velocities[i] -= (inertialAccel * worldLinearInertiaScale + angularAccel * worldAngularInertiaScale) * deltaTime;
|
||||
}
|
||||
|
||||
wind[i] = ambientWind;
|
||||
|
||||
if (inertialWind)
|
||||
{
|
||||
float4 wsPos = inertialFrame.frame.TransformPoint(positions[i]);
|
||||
wind[i] -= inertialFrame.frame.InverseTransformVector(inertialFrame.VelocityAtPoint(wsPos));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,52 +10,18 @@ using System.Collections;
|
||||
namespace Obi
|
||||
{
|
||||
[BurstCompile]
|
||||
struct CalculateSimplexBoundsJob : IJobParallelFor
|
||||
struct ParticleToBoundsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<float4> fluidMaterials;
|
||||
[ReadOnly] public NativeArray<int> activeParticles;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
public NativeArray<BurstAabb> simplexBounds;
|
||||
public NativeArray<BurstAabb> reducedBounds;
|
||||
|
||||
[ReadOnly] public Oni.SolverParameters parameters;
|
||||
[ReadOnly] public float dt;
|
||||
public NativeArray<BurstAabb> bounds;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(i, out int simplexSize);
|
||||
|
||||
var sxBounds = new BurstAabb(float.MaxValue, float.MinValue);
|
||||
var soBounds = new BurstAabb(float.MaxValue, float.MinValue);
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int p = simplices[simplexStart + j];
|
||||
|
||||
int m = particleMaterialIndices[p];
|
||||
float solidRadius = radii[p].x + (m >= 0 ? collisionMaterials[m].stickDistance : 0);
|
||||
|
||||
// Expand simplex bounds, using both the particle's original position and its velocity.
|
||||
// Add collision margin for both fluid neighborhood too (prevents explosions at high pressures due to neighborhood deficiency)
|
||||
sxBounds.EncapsulateParticle(positions[p],
|
||||
BurstIntegration.IntegrateLinear(positions[p], velocities[p], dt * parameters.particleCCD),
|
||||
math.max(solidRadius, fluidMaterials[p].x * 0.5f) + parameters.collisionMargin);
|
||||
|
||||
soBounds.EncapsulateParticle(positions[p],
|
||||
BurstIntegration.IntegrateLinear(positions[p], velocities[p], dt),
|
||||
solidRadius);
|
||||
}
|
||||
|
||||
simplexBounds[i] = sxBounds;
|
||||
reducedBounds[i] = soBounds;
|
||||
int p = activeParticles[i];
|
||||
bounds[i] = new BurstAabb(positions[p] - radii[p].x, positions[p] + radii[p].x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
[BurstCompile]
|
||||
struct BuildSimplexAabbs : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<float> fluidRadii;
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
[ReadOnly] public float collisionMargin;
|
||||
[ReadOnly] public float continuousCollisionDetection;
|
||||
[ReadOnly] public float dt;
|
||||
|
||||
public NativeArray<BurstAabb> simplexBounds;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(i, out int simplexSize);
|
||||
|
||||
var bounds = new BurstAabb(float.MaxValue, float.MinValue);
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int p = simplices[simplexStart + j];
|
||||
|
||||
// Find this particle's stick distance:
|
||||
int m = particleMaterialIndices[p];
|
||||
float stickDistance = m >= 0 ? collisionMaterials[m].stickDistance : 0;
|
||||
|
||||
// Expand simplex bounds, using both the particle's original position and its velocity:
|
||||
bounds.EncapsulateParticle(positions[p], positions[p] + velocities[p] * continuousCollisionDetection * dt,
|
||||
math.max(radii[p].x + stickDistance, fluidRadii[p] * 0.5f) + collisionMargin);
|
||||
}
|
||||
|
||||
simplexBounds[i] = bounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2bcf975ae59e4fcf9412ec328a3e2b9
|
||||
guid: 7834dd4a6de2346b5b36154d6aaa531a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
File diff suppressed because one or more lines are too long
@@ -1,42 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[BurstCompile]
|
||||
struct EnforceLimitsJob : IJobParallelFor
|
||||
{
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> prevPositions;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float> life;
|
||||
[ReadOnly] public NativeArray<int> phases;
|
||||
|
||||
[ReadOnly] public NativeArray<int> activeParticles;
|
||||
[ReadOnly] public bool killOffLimits;
|
||||
|
||||
[ReadOnly] public BurstAabb boundaryLimits;
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
int i = activeParticles[index];
|
||||
|
||||
float4 pos = positions[i];
|
||||
float4 prevPos = prevPositions[i];
|
||||
|
||||
bool outside = math.any(math.step(pos, boundaryLimits.min).xyz + math.step(boundaryLimits.max, pos).xyz);
|
||||
|
||||
if ((phases[i] & (int)ObiUtils.ParticleFlags.Isolated) != 0)
|
||||
life[i] = outside && killOffLimits ? 0 : life[i];
|
||||
|
||||
pos.xyz = math.clamp(pos, boundaryLimits.min, boundaryLimits.max).xyz;
|
||||
prevPos.xyz = math.clamp(prevPos, boundaryLimits.min, boundaryLimits.max).xyz;
|
||||
|
||||
positions[i] = pos;
|
||||
prevPositions[i] = prevPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user