修改水

This commit is contained in:
2026-01-01 22:00:33 +08:00
parent 040a222bd6
commit 9ceffccd39
1800 changed files with 103929 additions and 139495 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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);
}
}
}
}
}
}
}
}

View File

@@ -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);

View File

@@ -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];

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 71db1a3a2698147deb04f0264e4a728f
guid: 424c135125b644408aeda26a57bd6e40
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -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()
{

View File

@@ -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) +

View File

@@ -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;

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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]++;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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];
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 744a58a1ca5364e0a86c93e04770c0b8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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()
{

View File

@@ -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()
{

View File

@@ -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]++;

View File

@@ -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()
{

View File

@@ -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]++;
}
}
}

View File

@@ -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)

View File

@@ -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),

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -15,7 +15,7 @@ namespace Obi
public float4 com;
public float inverseMass;
public int constraintCount;
private int pad0;
private int pad1;
private int pad2;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: cacb4a3597ee245a6b40358728c300e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 555e1cc2122984a93afc0f6f31fa173b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: b3896a2253db543bcaa92126d83a7ba0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: d1d1ddc20ab7148e3ac806e3559ba2b8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 68fb3be53914247609d4e6da4f249e52
labels:
- ObiRope
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3c63ccd17bfc14b40a403c1c9a0be2c0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 10b89fddf68724bc9ad3305774d86ee1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 5f950cc46a13e4273856e2ec96c59eb9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 1578f081848d045dba76cd147f835e7c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 4827bd4bd4feb46bfbe458174871b8ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: be61f37ff31ff43ebb9bc8a55bd3edc7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 0b479f3e1bd184363a9259c4d737b611
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 6e3177e6c6643442db096e939f5d7109
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 7c4018fb5d53646be9ef6c91f07c7eb6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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));
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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