重新导入obi
This commit is contained in:
8
Assets/Obi/Scripts/Common.meta
Normal file
8
Assets/Obi/Scripts/Common.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e288a2209cca94fbfaf8c2fb1c51506b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Obi/Scripts/Common/Actors.meta
Normal file
8
Assets/Obi/Scripts/Common/Actors.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df93aa6716fd8485b9e0d6e29b00d432
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Assets/Obi/Scripts/Common/Actors/IObiParticleCollection.cs
Normal file
20
Assets/Obi/Scripts/Common/Actors/IObiParticleCollection.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
// Interface for classes that hold a collection of particles. Contains method to get common particle properties.
|
||||
public interface IObiParticleCollection
|
||||
{
|
||||
int particleCount { get; }
|
||||
int activeParticleCount { get; }
|
||||
bool usesOrientedParticles { get; }
|
||||
|
||||
int GetParticleRuntimeIndex(int index); // returns solver or blueprint index, depending on implementation.
|
||||
Vector3 GetParticlePosition(int index);
|
||||
Quaternion GetParticleOrientation(int index);
|
||||
void GetParticleAnisotropy(int index, ref Vector4 b1, ref Vector4 b2, ref Vector4 b3);
|
||||
float GetParticleMaxRadius(int index);
|
||||
Color GetParticleColor(int index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0281a989d5aa445b0bcb43597806adf7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1233
Assets/Obi/Scripts/Common/Actors/ObiActor.cs
Normal file
1233
Assets/Obi/Scripts/Common/Actors/ObiActor.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Assets/Obi/Scripts/Common/Actors/ObiActor.cs.meta
Normal file
12
Assets/Obi/Scripts/Common/Actors/ObiActor.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4602f0908b7ed409dbe198de06df8955
|
||||
timeCreated: 1449041314
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Obi/Scripts/Common/Backends.meta
Normal file
8
Assets/Obi/Scripts/Common/Backends.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c24459c92f72479e883a382c415ea21
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Obi/Scripts/Common/Backends/Burst.meta
Normal file
8
Assets/Obi/Scripts/Common/Backends/Burst.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ccea30afd7eb40d988c19314f42b11c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
27
Assets/Obi/Scripts/Common/Backends/Burst/BurstBackend.cs
Normal file
27
Assets/Obi/Scripts/Common/Backends/Burst/BurstBackend.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Collections;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstBackend : IObiBackend
|
||||
{
|
||||
#region Solver
|
||||
public ISolverImpl CreateSolver(ObiSolver solver, int capacity)
|
||||
{
|
||||
return new BurstSolverImpl(solver);
|
||||
}
|
||||
public void DestroySolver(ISolverImpl solver)
|
||||
{
|
||||
if (solver != null)
|
||||
solver.Destroy();
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3c1f86ca79494b9198874007d195a38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
46
Assets/Obi/Scripts/Common/Backends/Burst/BurstIntegration.cs
Normal file
46
Assets/Obi/Scripts/Common/Backends/Burst/BurstIntegration.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
|
||||
using Unity.Mathematics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public static class BurstIntegration
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 IntegrateLinear(float4 position, float4 velocity, float dt)
|
||||
{
|
||||
return position + velocity * dt;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 DifferentiateLinear(float4 position, float4 prevPosition, float dt)
|
||||
{
|
||||
return (position - prevPosition) / dt;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static quaternion AngularVelocityToSpinQuaternion(quaternion rotation, float4 angularVelocity, float dt)
|
||||
{
|
||||
var delta = new quaternion(angularVelocity.x,
|
||||
angularVelocity.y,
|
||||
angularVelocity.z, 0);
|
||||
|
||||
return new quaternion(0.5f * math.mul(delta,rotation).value * dt);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static quaternion IntegrateAngular(quaternion rotation, float4 angularVelocity, float dt)
|
||||
{
|
||||
rotation.value += AngularVelocityToSpinQuaternion(rotation,angularVelocity, dt).value;
|
||||
return math.normalize(rotation);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 DifferentiateAngular(quaternion rotation, quaternion prevRotation, float dt)
|
||||
{
|
||||
return new float4((math.mul(rotation, math.inverse(prevRotation)).value * 2.0f / dt).xyz, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba69cdb85ae0f4523810cb349ded03d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Assets/Obi/Scripts/Common/Backends/Burst/BurstJobHandle.cs
Normal file
28
Assets/Obi/Scripts/Common/Backends/Burst/BurstJobHandle.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstJobHandle : IObiJobHandle
|
||||
{
|
||||
private JobHandle handle = new JobHandle();
|
||||
|
||||
public BurstJobHandle SetHandle(JobHandle newHandle)
|
||||
{
|
||||
handle = newHandle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Complete()
|
||||
{
|
||||
handle.Complete();
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
handle = new JobHandle();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc827231eb963458fad0d395a186f910
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
706
Assets/Obi/Scripts/Common/Backends/Burst/BurstMath.cs
Normal file
706
Assets/Obi/Scripts/Common/Backends/Burst/BurstMath.cs
Normal file
@@ -0,0 +1,706 @@
|
||||
#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;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public static class BurstMath
|
||||
{
|
||||
|
||||
public const float epsilon = 0.0000001f;
|
||||
public const float zero = 0;
|
||||
public const float one = 1;
|
||||
public static readonly float golden = (math.sqrt(5.0f) + 1) / 2.0f;
|
||||
|
||||
// multiplies a column vector by a row vector.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float3x3 multrnsp(float4 column, float4 row)
|
||||
{
|
||||
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 float4x4 multrnsp4(float4 column, float4 row)
|
||||
{
|
||||
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)]
|
||||
public static float4 project(this float4 vector, float4 onto)
|
||||
{
|
||||
float len = math.lengthsq(onto);
|
||||
if (len < epsilon)
|
||||
return float4.zero;
|
||||
return math.dot(onto, vector) * onto / len;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4x4 TransformInertiaTensor(float4 tensor, quaternion rotation)
|
||||
{
|
||||
float4x4 rotMatrix = rotation.toMatrix();
|
||||
return math.mul(rotMatrix, math.mul(tensor.asDiagonal(), math.transpose(rotMatrix)));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float RotationalInvMass(float4x4 inverseInertiaTensor, float4 point, float4 direction)
|
||||
{
|
||||
float4 cr = math.mul(inverseInertiaTensor, new float4(math.cross(point.xyz, direction.xyz), 0));
|
||||
return math.dot(math.cross(cr.xyz, point.xyz), direction.xyz);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 GetParticleVelocityAtPoint(float4 position, float4 prevPosition, float4 point, float dt)
|
||||
{
|
||||
// no angular velocity, so calculate and return linear velocity only:
|
||||
return BurstIntegration.DifferentiateLinear(position, prevPosition, dt);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 GetParticleVelocityAtPoint(float4 position, float4 prevPosition, quaternion orientation, quaternion prevOrientation, float4 point, float dt)
|
||||
{
|
||||
// calculate both linear and angular velocities:
|
||||
float4 linearVelocity = BurstIntegration.DifferentiateLinear(position, prevPosition, dt);
|
||||
float4 angularVelocity = BurstIntegration.DifferentiateAngular(orientation, prevOrientation, dt);
|
||||
return linearVelocity + new float4(math.cross(angularVelocity.xyz, (point - prevPosition).xyz), 0);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 GetRigidbodyVelocityAtPoint(int rigidbodyIndex,
|
||||
float4 point,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
NativeArray<float4> linearDeltas,
|
||||
NativeArray<float4> angularDeltas,
|
||||
BurstAffineTransform solverToWorld)
|
||||
{
|
||||
float4 linear = rigidbodies[rigidbodyIndex].velocity + linearDeltas[rigidbodyIndex];
|
||||
float4 angular = rigidbodies[rigidbodyIndex].angularVelocity + angularDeltas[rigidbodyIndex];
|
||||
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.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,
|
||||
BurstAffineTransform solverToWorld)
|
||||
{
|
||||
float4 linear = rigidbodies[rigidbodyIndex].velocity;
|
||||
float4 angular = rigidbodies[rigidbodyIndex].angularVelocity;
|
||||
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.InverseTransformVector(linear + new float4(math.cross(angular.xyz, r.xyz), 0));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ApplyImpulse(int rigidbodyIndex,
|
||||
float4 impulse,
|
||||
float4 point,
|
||||
NativeArray<BurstRigidbody> rigidbodies,
|
||||
NativeArray<float4> linearDeltas,
|
||||
NativeArray<float4> angularDeltas,
|
||||
BurstAffineTransform solverToWorld)
|
||||
{
|
||||
float4 impulseWS = solverToWorld.TransformVector(impulse);
|
||||
float4 r = solverToWorld.TransformPoint(point) - rigidbodies[rigidbodyIndex].com;
|
||||
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)]
|
||||
public static void ApplyDeltaQuaternion(int rigidbodyIndex,
|
||||
quaternion rotation,
|
||||
quaternion delta,
|
||||
NativeArray<float4> angularDeltas,
|
||||
BurstAffineTransform solverToWorld,
|
||||
float dt)
|
||||
{
|
||||
quaternion rotationWS = math.mul(solverToWorld.rotation, rotation);
|
||||
quaternion deltaWS = math.mul(solverToWorld.rotation, delta);
|
||||
|
||||
// convert quaternion delta to angular acceleration:
|
||||
quaternion newRotation = math.normalize(new quaternion(rotationWS.value + deltaWS.value));
|
||||
angularDeltas[rigidbodyIndex] += BurstIntegration.DifferentiateAngular(newRotation, rotationWS, dt);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void OneSidedNormal(float4 forward, ref float4 normal)
|
||||
{
|
||||
float dot = math.dot(normal.xyz, forward.xyz);
|
||||
if (dot < 0) normal -= 2 * dot * forward;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float EllipsoidRadius(float4 normSolverDirection, quaternion orientation, float3 radii)
|
||||
{
|
||||
float3 localDir = math.mul(math.conjugate(orientation), normSolverDirection.xyz);
|
||||
float sqrNorm = math.lengthsq(localDir / radii);
|
||||
return sqrNorm > epsilon ? math.sqrt(1 / sqrNorm) : radii.x;
|
||||
}
|
||||
|
||||
public static quaternion ExtractRotation(float4x4 matrix, quaternion rotation, int iterations)
|
||||
{
|
||||
float4x4 R;
|
||||
for (int i = 0; i < iterations; ++i)
|
||||
{
|
||||
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 < BurstMath.epsilon)
|
||||
break;
|
||||
|
||||
rotation = math.normalize(math.mul(quaternion.AxisAngle((1.0f / w) * omega, w), rotation));
|
||||
}
|
||||
return rotation;
|
||||
}
|
||||
|
||||
// decomposes a quaternion in swing and twist around a given axis:
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SwingTwist(quaternion q, float3 twistAxis, out quaternion swing, out quaternion twist)
|
||||
{
|
||||
float dot = math.dot(q.value.xyz, twistAxis);
|
||||
float3 p = twistAxis * dot;
|
||||
twist = math.normalizesafe(new quaternion(p[0], p[1], p[2], q.value.w));
|
||||
swing = math.mul(q, math.conjugate(twist));
|
||||
}
|
||||
|
||||
public static float4x4 toMatrix(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 float4x4(1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0,
|
||||
2 * (xy + zw), 1 - 2 * (xx + zz), 2 * (yz - xw), 0,
|
||||
2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy), 0,
|
||||
0, 0, 0, 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4x4 asDiagonal(this float4 v)
|
||||
{
|
||||
return new float4x4(v.x, 0, 0, 0,
|
||||
0, v.y, 0, 0,
|
||||
0, 0, v.z, 0,
|
||||
0, 0, 0, v.w);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float4 diagonal(this float4x4 value)
|
||||
{
|
||||
return new float4(value.c0[0], value.c1[1], value.c2[2], value.c3[3]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float frobeniusNorm(this float4x4 m)
|
||||
{
|
||||
return math.sqrt(math.lengthsq(m.c0) + math.lengthsq(m.c1) + math.lengthsq(m.c2) + math.lengthsq(m.c3));
|
||||
}
|
||||
|
||||
public static void EigenSolve(float3x3 D, out float3 S, out float3x3 V)
|
||||
{
|
||||
// D is symmetric
|
||||
// S is a vector whose elements are eigenvalues
|
||||
// V is a matrix whose columns are eigenvectors
|
||||
S = EigenValues(D);
|
||||
float3 V0, V1, V2;
|
||||
|
||||
if (S[0] - S[1] > S[1] - S[2])
|
||||
{
|
||||
V0 = EigenVector(D, S[0]);
|
||||
if (S[1] - S[2] < math.FLT_MIN_NORMAL)
|
||||
{
|
||||
V2 = V0.unitOrthogonal();
|
||||
}
|
||||
else
|
||||
{
|
||||
V2 = EigenVector(D, S[2]); V2 -= V0 * math.dot(V0, V2); V2 = math.normalize(V2);
|
||||
}
|
||||
V1 = math.cross(V2, V0);
|
||||
}
|
||||
else
|
||||
{
|
||||
V2 = EigenVector(D, S[2]);
|
||||
if (S[0] - S[1] < math.FLT_MIN_NORMAL)
|
||||
{
|
||||
V1 = V2.unitOrthogonal();
|
||||
}
|
||||
else
|
||||
{
|
||||
V1 = EigenVector(D, S[1]); V1 -= V2 * math.dot(V2, V1); V1 = math.normalize(V1);
|
||||
}
|
||||
V0 = math.cross(V1, V2);
|
||||
}
|
||||
|
||||
V.c0 = V0;
|
||||
V.c1 = V1;
|
||||
V.c2 = V2;
|
||||
}
|
||||
|
||||
static float3 unitOrthogonal(this float3 input)
|
||||
{
|
||||
// Find a vector to cross() the input with.
|
||||
if (!(input.x < input.z * epsilon)
|
||||
|| !(input.y < input.z * epsilon))
|
||||
{
|
||||
float invnm = 1 / math.length(input.xy);
|
||||
return new float3(-input.y * invnm, input.x * invnm, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
float invnm = 1 / math.length(input.yz);
|
||||
return new float3(0, -input.z * invnm, input.y * invnm);
|
||||
}
|
||||
}
|
||||
|
||||
// D is symmetric, S is an eigen value
|
||||
static float3 EigenVector(float3x3 D, float S)
|
||||
{
|
||||
// Compute a cofactor matrix of D - sI.
|
||||
float3 c0 = D.c0; c0[0] -= S;
|
||||
float3 c1 = D.c1; c1[1] -= S;
|
||||
float3 c2 = D.c2; c2[2] -= S;
|
||||
|
||||
// Upper triangular matrix
|
||||
float3 c0p = new float3(c1[1] * c2[2] - c2[1] * c2[1], 0, 0);
|
||||
float3 c1p = new float3(c2[1] * c2[0] - c1[0] * c2[2], c0[0] * c2[2] - c2[0] * c2[0], 0);
|
||||
float3 c2p = new float3(c1[0] * c2[1] - c1[1] * c2[0], c1[0] * c2[0] - c0[0] * c2[1], c0[0] * c1[1] - c1[0] * c1[0]);
|
||||
|
||||
// Get a column vector with a largest norm (non-zero).
|
||||
float C01s = c1p[0] * c1p[0];
|
||||
float C02s = c2p[0] * c2p[0];
|
||||
float C12s = c2p[1] * c2p[1];
|
||||
float3 norm = new float3(c0p[0] * c0p[0] + C01s + C02s,
|
||||
C01s + c1p[1] * c1p[1] + C12s,
|
||||
C02s + C12s + c2p[2] * c2p[2]);
|
||||
|
||||
// index of largest:
|
||||
int index = 0;
|
||||
if (norm[0] > norm[1] && norm[0] > norm[2])
|
||||
index = 0;
|
||||
else if (norm[1] > norm[0] && norm[1] > norm[2])
|
||||
index = 1;
|
||||
else
|
||||
index = 2;
|
||||
|
||||
float3 V = float3.zero;
|
||||
|
||||
// special case
|
||||
if (norm[index] < math.FLT_MIN_NORMAL)
|
||||
{
|
||||
V[0] = 1; return V;
|
||||
}
|
||||
else if (index == 0)
|
||||
{
|
||||
V[0] = c0p[0]; V[1] = c1p[0]; V[2] = c2p[0];
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
V[0] = c1p[0]; V[1] = c1p[1]; V[2] = c2p[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
V = c2p;
|
||||
}
|
||||
return math.normalize(V);
|
||||
}
|
||||
|
||||
static float3 EigenValues(float3x3 D)
|
||||
{
|
||||
float one_third = 1 / 3.0f;
|
||||
float one_sixth = 1 / 6.0f;
|
||||
float three_sqrt = math.sqrt(3.0f);
|
||||
|
||||
float3 c0 = D.c0;
|
||||
float3 c1 = D.c1;
|
||||
float3 c2 = D.c2;
|
||||
|
||||
float m = one_third * (c0[0] + c1[1] + c2[2]);
|
||||
|
||||
// K is D - I*diag(S)
|
||||
float K00 = c0[0] - m;
|
||||
float K11 = c1[1] - m;
|
||||
float K22 = c2[2] - m;
|
||||
|
||||
float K01s = c1[0] * c1[0];
|
||||
float K02s = c2[0] * c2[0];
|
||||
float K12s = c2[1] * c2[1];
|
||||
|
||||
float q = 0.5f * (K00 * (K11 * K22 - K12s) - K22 * K01s - K11 * K02s) + c1[0] * c2[1] * c0[2];
|
||||
float p = one_sixth * (K00 * K00 + K11 * K11 + K22 * K22 + 2 * (K01s + K02s + K12s));
|
||||
|
||||
float p_sqrt = math.sqrt(p);
|
||||
|
||||
float tmp = p * p * p - q * q;
|
||||
float phi = one_third * math.atan2(math.sqrt(math.max(0, tmp)), q);
|
||||
float phi_c = math.cos(phi);
|
||||
float phi_s = math.sin(phi);
|
||||
float sqrt_p_c_phi = p_sqrt * phi_c;
|
||||
float sqrt_p_3_s_phi = p_sqrt * three_sqrt * phi_s;
|
||||
|
||||
float e0 = m + 2 * sqrt_p_c_phi;
|
||||
float e1 = m - sqrt_p_c_phi - sqrt_p_3_s_phi;
|
||||
float e2 = m - sqrt_p_c_phi + sqrt_p_3_s_phi;
|
||||
|
||||
float aux;
|
||||
if (e0 > e1)
|
||||
{
|
||||
aux = e0;
|
||||
e0 = e1;
|
||||
e1 = aux;
|
||||
}
|
||||
if (e0 > e2)
|
||||
{
|
||||
aux = e0;
|
||||
e0 = e2;
|
||||
e2 = aux;
|
||||
}
|
||||
if (e1 > e2)
|
||||
{
|
||||
aux = e1;
|
||||
e1 = e2;
|
||||
e2 = aux;
|
||||
}
|
||||
|
||||
return new float3(e2, e1, e0);
|
||||
}
|
||||
|
||||
public struct CachedTri
|
||||
{
|
||||
public float4 vertex;
|
||||
public float4 edge0;
|
||||
public float4 edge1;
|
||||
public float4 data;
|
||||
|
||||
public void Cache(float4 v1,
|
||||
float4 v2,
|
||||
float4 v3)
|
||||
{
|
||||
vertex = v1;
|
||||
edge0 = v2 - v1;
|
||||
edge1 = v3 - v1;
|
||||
data = float4.zero;
|
||||
data[0] = math.dot(edge0, edge0);
|
||||
data[1] = math.dot(edge0, edge1);
|
||||
data[2] = math.dot(edge1, edge1);
|
||||
data[3] = data[0] * data[2] - data[1] * data[1];
|
||||
}
|
||||
}
|
||||
|
||||
public static float4 NearestPointOnTri(in CachedTri tri,
|
||||
float4 p,
|
||||
out float4 bary)
|
||||
{
|
||||
float4 v0 = tri.vertex - p;
|
||||
float b0 = math.dot(tri.edge0, v0);
|
||||
float b1 = math.dot(tri.edge1, v0);
|
||||
float t0 = tri.data[1] * b1 - tri.data[2] * b0;
|
||||
float t1 = tri.data[1] * b0 - tri.data[0] * b1;
|
||||
|
||||
if (t0 + t1 <= tri.data[3])
|
||||
{
|
||||
if (t0 < zero)
|
||||
{
|
||||
if (t1 < zero) // region 4
|
||||
{
|
||||
if (b0 < zero)
|
||||
{
|
||||
t1 = zero;
|
||||
if (-b0 >= tri.data[0]) // V0
|
||||
t0 = one;
|
||||
else // E01
|
||||
t0 = -b0 / tri.data[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
t0 = zero;
|
||||
if (b1 >= zero) // V0
|
||||
t1 = zero;
|
||||
else if (-b1 >= tri.data[2]) // V2
|
||||
t1 = one;
|
||||
else // E20
|
||||
t1 = -b1 / tri.data[2];
|
||||
}
|
||||
}
|
||||
else // region 3
|
||||
{
|
||||
t0 = zero;
|
||||
if (b1 >= zero) // V0
|
||||
t1 = zero;
|
||||
else if (-b1 >= tri.data[2]) // V2
|
||||
t1 = one;
|
||||
else // E20
|
||||
t1 = -b1 / tri.data[2];
|
||||
}
|
||||
}
|
||||
else if (t1 < zero) // region 5
|
||||
{
|
||||
t1 = zero;
|
||||
if (b0 >= zero) // V0
|
||||
t0 = zero;
|
||||
else if (-b0 >= tri.data[0]) // V1
|
||||
t0 = one;
|
||||
else // E01
|
||||
t0 = -b0 / tri.data[0];
|
||||
}
|
||||
else // region 0, interior
|
||||
{
|
||||
float invDet = one / tri.data[3];
|
||||
t0 *= invDet;
|
||||
t1 *= invDet;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float tmp0, tmp1, numer, denom;
|
||||
|
||||
if (t0 < zero) // region 2
|
||||
{
|
||||
tmp0 = tri.data[1] + b0;
|
||||
tmp1 = tri.data[2] + b1;
|
||||
if (tmp1 > tmp0)
|
||||
{
|
||||
numer = tmp1 - tmp0;
|
||||
denom = tri.data[0] - 2 * tri.data[1] + tri.data[2];
|
||||
if (numer >= denom) // V1
|
||||
{
|
||||
t0 = one;
|
||||
t1 = zero;
|
||||
}
|
||||
else // E12
|
||||
{
|
||||
t0 = numer / denom;
|
||||
t1 = one - t0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
t0 = zero;
|
||||
if (tmp1 <= zero) // V2
|
||||
t1 = one;
|
||||
else if (b1 >= zero) // V0
|
||||
t1 = zero;
|
||||
else // E20
|
||||
t1 = -b1 / tri.data[2];
|
||||
}
|
||||
}
|
||||
else if (t1 < zero) // region 6
|
||||
{
|
||||
tmp0 = tri.data[1] + b1;
|
||||
tmp1 = tri.data[0] + b0;
|
||||
if (tmp1 > tmp0)
|
||||
{
|
||||
numer = tmp1 - tmp0;
|
||||
denom = tri.data[0] - 2 * tri.data[1] + tri.data[2];
|
||||
if (numer >= denom) // V2
|
||||
{
|
||||
t1 = one;
|
||||
t0 = zero;
|
||||
}
|
||||
else // E12
|
||||
{
|
||||
t1 = numer / denom;
|
||||
t0 = one - t1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
t1 = zero;
|
||||
if (tmp1 <= zero) // V1
|
||||
t0 = one;
|
||||
else if (b0 >= zero) // V0
|
||||
t0 = zero;
|
||||
else // E01
|
||||
t0 = -b0 / tri.data[0];
|
||||
}
|
||||
}
|
||||
else // region 1
|
||||
{
|
||||
numer = tri.data[2] + b1 - tri.data[1] - b0;
|
||||
if (numer <= zero) // V2
|
||||
{
|
||||
t0 = zero;
|
||||
t1 = one;
|
||||
}
|
||||
else
|
||||
{
|
||||
denom = tri.data[0] - 2 * tri.data[1] + tri.data[2];
|
||||
if (numer >= denom) // V1
|
||||
{
|
||||
t0 = one;
|
||||
t1 = zero;
|
||||
}
|
||||
else // 12
|
||||
{
|
||||
t0 = numer / denom;
|
||||
t1 = one - t0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (clampToSegment)
|
||||
mu = math.saturate(mu);
|
||||
|
||||
return a + ab * mu;
|
||||
}
|
||||
|
||||
public static float4 NearestPointsTwoEdges(float4 a, float4 b, float4 c, float4 d, out float mu1, out float mu2)
|
||||
{
|
||||
float4 dc = d - c;
|
||||
float lineDirSqrMag = math.dot(dc, dc);
|
||||
float4 inPlaneA = a - (math.dot(a - c, dc) / lineDirSqrMag * dc);
|
||||
float4 inPlaneB = b - (math.dot(b - c, dc) / lineDirSqrMag * dc);
|
||||
|
||||
float4 inPlaneBA = inPlaneB - inPlaneA;
|
||||
float t = math.dot(c - inPlaneA, inPlaneBA) / math.dot(inPlaneBA, inPlaneBA);
|
||||
|
||||
//t = (inPlaneA != inPlaneB) ? t : 0f; // Zero's t if parallel
|
||||
float4 segABtoLineCD = math.lerp(a, b, math.saturate(t));
|
||||
|
||||
float4 segCDtoSegAB = NearestPointOnEdge(c, d, segABtoLineCD, out mu1);
|
||||
float4 segABtoSegCD = NearestPointOnEdge(a, b, segCDtoSegAB, out mu2);
|
||||
|
||||
return segCDtoSegAB;
|
||||
}
|
||||
|
||||
public static float4 BaryCoords(in float4 A,
|
||||
in float4 B,
|
||||
in float4 C,
|
||||
in float4 P)
|
||||
{
|
||||
|
||||
// Compute vectors
|
||||
float4 v0 = C - A;
|
||||
float4 v1 = B - A;
|
||||
float4 v2 = P - A;
|
||||
|
||||
// Compute dot products
|
||||
float dot00 = math.dot(v0, v0);
|
||||
float dot01 = math.dot(v0, v1);
|
||||
float dot02 = math.dot(v0, v2);
|
||||
float dot11 = math.dot(v1, v1);
|
||||
float dot12 = math.dot(v1, v2);
|
||||
|
||||
// Compute barycentric coordinates
|
||||
float det = dot00 * dot11 - dot01 * dot01;
|
||||
if (math.abs(det) > epsilon)
|
||||
{
|
||||
float u = (dot11 * dot02 - dot01 * dot12) / det;
|
||||
float v = (dot00 * dot12 - dot01 * dot02) / det;
|
||||
return new float4(1 - u - v, v, u, 0);
|
||||
}
|
||||
return float4.zero;
|
||||
|
||||
}
|
||||
|
||||
public static float4 BaryCoords2(in float4 A,
|
||||
in float4 B,
|
||||
in float4 P)
|
||||
{
|
||||
float4 v0 = P - A;
|
||||
float4 v1 = B - A;
|
||||
float y = math.sqrt(math.dot(v0, v0) / (math.dot(v1, v1) + epsilon));
|
||||
return new float4(1 - y, y, 0, 0);
|
||||
}
|
||||
|
||||
public static float4 BaryIntrpl(in float4 p1, in float4 p2, in float4 p3, in float4 coords)
|
||||
{
|
||||
return coords[0] * p1 + coords[1] * p2 + coords[2] * p3;
|
||||
}
|
||||
|
||||
public static float4 BaryIntrpl(in float4 p1, in float4 p2, in float4 coords)
|
||||
{
|
||||
return coords[0] * p1 + coords[1] * p2;
|
||||
}
|
||||
|
||||
public static float BaryIntrpl(float p1, float p2, float p3, float4 coords)
|
||||
{
|
||||
return coords[0] * p1 + coords[1] * p2 + coords[2] * p3;
|
||||
}
|
||||
|
||||
public static float BaryIntrpl(float p1, float p2, float4 coords)
|
||||
{
|
||||
return coords[0] * p1 + coords[1] * p2;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float BaryScale(float4 coords)
|
||||
{
|
||||
return 1.0f / math.dot(coords, coords);
|
||||
}
|
||||
|
||||
[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;
|
||||
}
|
||||
|
||||
public static unsafe void RemoveRangeBurst<T>(this NativeList<T> list, int index, int count)
|
||||
where T : unmanaged
|
||||
{
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
if ((uint)index >= (uint)list.Length)
|
||||
{
|
||||
throw new IndexOutOfRangeException(
|
||||
$"Index {index} is out of range in NativeList of '{list.Length}' Length.");
|
||||
}
|
||||
#endif
|
||||
|
||||
int elemSize = UnsafeUtility.SizeOf<T>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
11
Assets/Obi/Scripts/Common/Backends/Burst/BurstMath.cs.meta
Normal file
11
Assets/Obi/Scripts/Common/Backends/Burst/BurstMath.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cc365339e47244d7b217576a7d95ca8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Obi/Scripts/Common/Backends/Burst/Collisions.meta
Normal file
8
Assets/Obi/Scripts/Common/Backends/Burst/Collisions.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71085a94c3367475ab31b12e483c6479
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,93 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
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)
|
||||
{
|
||||
float4 center = shape.center * colliderToSolver.scale;
|
||||
float4 size = shape.size * colliderToSolver.scale * 0.5f;
|
||||
|
||||
// clamp the point to the surface of the box:
|
||||
point = colliderToSolver.InverseTransformPointUnscaled(point) - center;
|
||||
|
||||
if (shape.is2D != 0)
|
||||
point[2] = 0;
|
||||
|
||||
// get minimum distance for each axis:
|
||||
float4 distances = size - math.abs(point);
|
||||
|
||||
if (distances.x >= 0 && distances.y >= 0 && distances.z >= 0)
|
||||
{
|
||||
// find minimum distance in all three axes and the axis index:
|
||||
float min = float.MaxValue;
|
||||
int axis = 0;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (distances[i] < min)
|
||||
{
|
||||
min = distances[i];
|
||||
axis = i;
|
||||
}
|
||||
}
|
||||
|
||||
projectedPoint.normal = float4.zero;
|
||||
projectedPoint.point = point;
|
||||
|
||||
projectedPoint.normal[axis] = point[axis] > 0 ? 1 : -1;
|
||||
projectedPoint.point[axis] = size[axis] * projectedPoint.normal[axis];
|
||||
}
|
||||
else
|
||||
{
|
||||
projectedPoint.point = math.clamp(point, -size, size);
|
||||
projectedPoint.normal = math.normalizesafe(point - projectedPoint.point);
|
||||
}
|
||||
|
||||
projectedPoint.point = colliderToSolver.TransformPointUnscaled(projectedPoint.point + center + projectedPoint.normal * shape.contactOffset);
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(projectedPoint.normal);
|
||||
}
|
||||
|
||||
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 co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
|
||||
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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f09926d9bc8604475a3bce5e593165b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,73 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
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 != 0)
|
||||
point[2] = 0;
|
||||
|
||||
int direction = (int)shape.size.z;
|
||||
float radius = shape.size.x * math.max(colliderToSolver.scale[(direction + 1) % 3],
|
||||
colliderToSolver.scale[(direction + 2) % 3]);
|
||||
|
||||
float height = math.max(radius, shape.size.y * 0.5f * colliderToSolver.scale[direction]);
|
||||
float4 halfVector = float4.zero;
|
||||
halfVector[direction] = height - radius;
|
||||
|
||||
float4 centerLine = BurstMath.NearestPointOnEdge(-halfVector, halfVector, point, out float mu);
|
||||
float4 centerToPoint = point - centerLine;
|
||||
float distanceToCenter = math.length(centerToPoint);
|
||||
|
||||
float4 normal = centerToPoint / (distanceToCenter + BurstMath.epsilon);
|
||||
|
||||
projectedPoint.point = colliderToSolver.TransformPointUnscaled(center + centerLine + normal * (radius + shape.contactOffset));
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
}
|
||||
|
||||
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 co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
|
||||
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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30a6f80040171467eb7e40b869d0aa44
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
public struct BurstColliderShape
|
||||
{
|
||||
public float4 center;
|
||||
public float4 size; /**< box: size of the box in each axis.
|
||||
sphere: radius of sphere (x,y,z),
|
||||
capsule: radius (x), height(y), direction (z, can be 0, 1 or 2).
|
||||
heightmap: width (x axis), height (y axis) and depth (z axis) in world units.*/
|
||||
|
||||
public ColliderShape.ShapeType type;
|
||||
public float contactOffset;
|
||||
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 filter;
|
||||
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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 707535693276c450e8ba4504defa09eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,552 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
|
||||
public class BurstColliderWorld : MonoBehaviour, IColliderWorldImpl
|
||||
{
|
||||
struct MovingCollider
|
||||
{
|
||||
public BurstCellSpan oldSpan;
|
||||
public BurstCellSpan newSpan;
|
||||
public int entity;
|
||||
}
|
||||
|
||||
private int refCount = 0;
|
||||
private int colliderCount = 0;
|
||||
|
||||
private NativeMultilevelGrid<int> grid;
|
||||
private NativeQueue<MovingCollider> movingColliders;
|
||||
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.cellSpans = new ObiNativeCellSpanList();
|
||||
|
||||
ObiColliderWorld.GetInstance().RegisterImplementation(this);
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
ObiColliderWorld.GetInstance().UnregisterImplementation(this);
|
||||
|
||||
grid.Dispose();
|
||||
movingColliders.Dispose();
|
||||
colliderContactQueue.Dispose();
|
||||
cellSpans.Dispose();
|
||||
}
|
||||
|
||||
public void IncreaseReferenceCount()
|
||||
{
|
||||
refCount++;
|
||||
}
|
||||
public void DecreaseReferenceCount()
|
||||
{
|
||||
if (--refCount <= 0 && gameObject != null)
|
||||
DestroyImmediate(gameObject);
|
||||
}
|
||||
|
||||
public void SetColliders(ObiNativeColliderShapeList shapes, ObiNativeAabbList bounds, ObiNativeAffineTransformList transforms, int count)
|
||||
{
|
||||
colliderCount = count;
|
||||
|
||||
// insert new empty cellspans at the end if needed:
|
||||
while (colliderCount > cellSpans.count)
|
||||
cellSpans.Add(new CellSpan(new VInt4(10000), new VInt4(10000)));
|
||||
}
|
||||
|
||||
public void SetRigidbodies(ObiNativeRigidbodyList rigidbody)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetCollisionMaterials(ObiNativeCollisionMaterialList materials)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void SetTriangleMeshData(ObiNativeTriangleMeshHeaderList headers, ObiNativeBIHNodeList nodes, ObiNativeTriangleList triangles, ObiNativeVector3List vertices)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetEdgeMeshData(ObiNativeEdgeMeshHeaderList headers, ObiNativeBIHNodeList nodes, ObiNativeEdgeList edges, ObiNativeVector2List vertices)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetDistanceFieldData(ObiNativeDistanceFieldHeaderList headers, ObiNativeDFNodeList nodes) { }
|
||||
public void SetHeightFieldData(ObiNativeHeightFieldHeaderList headers, ObiNativeFloatList samples) { }
|
||||
|
||||
public void UpdateWorld(float deltaTime)
|
||||
{
|
||||
var world = ObiColliderWorld.GetInstance();
|
||||
|
||||
var identifyMoving = new IdentifyMovingColliders
|
||||
{
|
||||
movingColliders = movingColliders.AsParallelWriter(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(cellSpans.count),
|
||||
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
collisionMaterials = world.collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
bounds = world.colliderAabbs.AsNativeArray<BurstAabb>(cellSpans.count),
|
||||
cellIndices = cellSpans.AsNativeArray<BurstCellSpan>(),
|
||||
colliderCount = colliderCount,
|
||||
dt = deltaTime
|
||||
};
|
||||
JobHandle movingHandle = identifyMoving.Schedule(cellSpans.count, 128);
|
||||
|
||||
var updateMoving = new UpdateMovingColliders
|
||||
{
|
||||
movingColliders = movingColliders,
|
||||
grid = grid,
|
||||
colliderCount = colliderCount
|
||||
};
|
||||
|
||||
updateMoving.Schedule(movingHandle).Complete();
|
||||
|
||||
// remove tail from the current spans array:
|
||||
if (colliderCount < cellSpans.count)
|
||||
cellSpans.count -= cellSpans.count - colliderCount;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct IdentifyMovingColliders : IJobParallelFor
|
||||
{
|
||||
[WriteOnly]
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeQueue<MovingCollider>.ParallelWriter movingColliders;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
public NativeArray<BurstAabb> bounds;
|
||||
|
||||
public NativeArray<BurstCellSpan> cellIndices;
|
||||
[ReadOnly] public int colliderCount;
|
||||
[ReadOnly] public float dt;
|
||||
|
||||
// Iterate over all colliders and store those whose cell span has changed.
|
||||
public void Execute(int i)
|
||||
{
|
||||
BurstAabb velocityBounds = bounds[i];
|
||||
|
||||
int rb = shapes[i].rigidbodyIndex;
|
||||
|
||||
// Expand bounds by rigidbody's linear velocity
|
||||
// (check against out of bounds rigidbody access, can happen when a destroyed collider references a rigidbody that has just been destroyed too)
|
||||
if (rb >= 0 && rb < rigidbodies.Length)
|
||||
velocityBounds.Sweep(rigidbodies[rb].velocity * dt);
|
||||
|
||||
// Expand bounds by collision material's stick distance:
|
||||
if (shapes[i].materialIndex >= 0)
|
||||
velocityBounds.Expand(collisionMaterials[shapes[i].materialIndex].stickDistance);
|
||||
|
||||
float size = velocityBounds.AverageAxisLength();
|
||||
int level = NativeMultilevelGrid<int>.GridLevelForSize(size);
|
||||
float cellSize = NativeMultilevelGrid<int>.CellSizeOfLevel(level);
|
||||
|
||||
// get new collider bounds cell coordinates:
|
||||
BurstCellSpan newSpan = new BurstCellSpan(new int4(GridHash.Quantize(velocityBounds.min.xyz, cellSize), level),
|
||||
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 != 0)
|
||||
{
|
||||
newSpan.min[2] = 0;
|
||||
newSpan.max[2] = 0;
|
||||
}
|
||||
|
||||
// if the collider is at the tail (removed), we will only remove it from its current cellspan.
|
||||
// if the new cellspan and the current one are different, we must remove it from its current cellspan and add it to its new one.
|
||||
if (i >= colliderCount || cellIndices[i] != newSpan)
|
||||
{
|
||||
// Add the collider to the list of moving colliders:
|
||||
movingColliders.Enqueue(new MovingCollider()
|
||||
{
|
||||
oldSpan = cellIndices[i],
|
||||
newSpan = newSpan,
|
||||
entity = i
|
||||
});
|
||||
|
||||
// Update previous coords:
|
||||
cellIndices[i] = newSpan;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
struct UpdateMovingColliders : IJob
|
||||
{
|
||||
public NativeQueue<MovingCollider> movingColliders;
|
||||
public NativeMultilevelGrid<int> grid;
|
||||
[ReadOnly] public int colliderCount;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
while (movingColliders.Count > 0)
|
||||
{
|
||||
MovingCollider movingCollider = movingColliders.Dequeue();
|
||||
|
||||
// remove from old cells:
|
||||
grid.RemoveFromCells(movingCollider.oldSpan, movingCollider.entity);
|
||||
|
||||
// insert in new cells, as long as the index is below the amount of colliders.
|
||||
// otherwise, the collider is at the "tail" and there's no need to add it back.
|
||||
if (movingCollider.entity < colliderCount)
|
||||
grid.AddToCells(movingCollider.newSpan, movingCollider.entity);
|
||||
}
|
||||
|
||||
// remove all empty cells from the grid:
|
||||
grid.RemoveEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct GenerateContactsJob : IJobParallelFor
|
||||
{
|
||||
//collider grid:
|
||||
[ReadOnly] public NativeMultilevelGrid<int> colliderGrid;
|
||||
|
||||
[DeallocateOnJobCompletion]
|
||||
[ReadOnly] public NativeArray<int> gridLevels;
|
||||
|
||||
// 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;
|
||||
[ReadOnly] public NativeArray<int> filters;
|
||||
|
||||
// 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<BurstCollisionMaterial> collisionMaterials;
|
||||
[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<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 = simplexBoundsSS.Transformed(solverToWorld);
|
||||
NativeList<int> candidates = new NativeList<int>(16,Allocator.Temp);
|
||||
|
||||
// max size of the particle bounds in cells:
|
||||
int3 maxSize = new int3(10);
|
||||
bool is2D = parameters.mode == Oni.SolverParameters.Mode.Mode2D;
|
||||
|
||||
for (int l = 0; l < gridLevels.Length; ++l)
|
||||
{
|
||||
float cellSize = NativeMultilevelGrid<int>.CellSizeOfLevel(gridLevels[l]);
|
||||
|
||||
int3 minCell = GridHash.Quantize(simplexBoundsWS.min.xyz, cellSize);
|
||||
int3 maxCell = GridHash.Quantize(simplexBoundsWS.max.xyz, cellSize);
|
||||
maxCell = minCell + math.min(maxCell - minCell, maxSize);
|
||||
|
||||
for (int x = minCell[0]; x <= maxCell[0]; ++x)
|
||||
{
|
||||
for (int y = minCell[1]; y <= maxCell[1]; ++y)
|
||||
{
|
||||
// for 2D mode, project each cell at z == 0 and check them too. This way we ensure 2D colliders
|
||||
// (which are inserted in cells with z == 0) are accounted for in the broadphase.
|
||||
if (is2D)
|
||||
{
|
||||
if (colliderGrid.TryGetCellIndex(new int4(x, y, 0, gridLevels[l]), out int cellIndex))
|
||||
{
|
||||
var colliderCell = colliderGrid.usedCells[cellIndex];
|
||||
candidates.AddRange(colliderCell.ContentsPointer, colliderCell.Length);
|
||||
}
|
||||
}
|
||||
|
||||
for (int z = minCell[2]; z <= maxCell[2]; ++z)
|
||||
{
|
||||
if (colliderGrid.TryGetCellIndex(new int4(x, y, z, gridLevels[l]), out int cellIndex))
|
||||
{
|
||||
var colliderCell = colliderGrid.usedCells[cellIndex];
|
||||
candidates.AddRange(colliderCell.ContentsPointer, colliderCell.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates.Length > 0)
|
||||
{
|
||||
// make sure each candidate collider only shows up once in the array:
|
||||
NativeArray<int> uniqueCandidates = candidates.AsArray();
|
||||
uniqueCandidates.Sort();
|
||||
int uniqueCount = uniqueCandidates.Unique();
|
||||
|
||||
// iterate over candidate colliders, generating contacts for each one
|
||||
for (int k = 0; k < uniqueCount; ++k)
|
||||
{
|
||||
int c = uniqueCandidates[k];
|
||||
if (c < shapes.Length)
|
||||
{
|
||||
BurstColliderShape shape = shapes[c];
|
||||
BurstAabb colliderBoundsWS = bounds[c];
|
||||
int rb = shape.rigidbodyIndex;
|
||||
|
||||
// Expand bounds by rigidbody's linear velocity:
|
||||
if (rb >= 0)
|
||||
colliderBoundsWS.Sweep(rigidbodies[rb].velocity * deltaTime);
|
||||
|
||||
// Expand bounds by collision material's stick distance:
|
||||
if (shape.materialIndex >= 0)
|
||||
colliderBoundsWS.Expand(collisionMaterials[shape.materialIndex].stickDistance);
|
||||
|
||||
// check if any simplex particle and the collider should collide:
|
||||
bool shouldCollide = false;
|
||||
var colliderCategory = shape.filter & ObiUtils.FilterCategoryBitmask;
|
||||
var colliderMask = (shape.filter & ObiUtils.FilterMaskBitmask) >> 16;
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
var simplexCategory = filters[simplices[simplexStart + j]] & ObiUtils.FilterCategoryBitmask;
|
||||
var simplexMask = (filters[simplices[simplexStart + j]] & ObiUtils.FilterMaskBitmask) >> 16;
|
||||
shouldCollide |= (simplexCategory & colliderMask) != 0 && (simplexMask & colliderCategory) != 0;
|
||||
}
|
||||
|
||||
if (shouldCollide && simplexBoundsWS.IntersectsAabb(in colliderBoundsWS, is2D))
|
||||
{
|
||||
// generate contacts for the collider:
|
||||
BurstAffineTransform colliderToSolver = worldToSolver * transforms[c];
|
||||
GenerateContacts(in shape, in colliderToSolver, c, rb, i, simplexStart, simplexSize, simplexBoundsSS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateContacts(in BurstColliderShape shape,
|
||||
in BurstAffineTransform colliderToSolver,
|
||||
int colliderIndex,
|
||||
int rigidbodyIndex,
|
||||
int simplexIndex,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
in BurstAabb simplexBoundsSS)
|
||||
{
|
||||
float4x4 solverToCollider;
|
||||
BurstAabb simplexBoundsCS;
|
||||
|
||||
switch (shape.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:
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public JobHandle GenerateContacts(BurstSolverImpl solver, float deltaTime, JobHandle inputDeps)
|
||||
{
|
||||
var world = ObiColliderWorld.GetInstance();
|
||||
|
||||
var generateColliderContactsJob = new GenerateContactsJob
|
||||
{
|
||||
colliderGrid = grid,
|
||||
gridLevels = grid.populatedLevels.GetKeyArray(Allocator.TempJob),
|
||||
|
||||
positions = solver.positions,
|
||||
orientations = solver.orientations,
|
||||
velocities = solver.velocities,
|
||||
invMasses = solver.invMasses,
|
||||
radii = solver.principalRadii,
|
||||
filters = solver.filters,
|
||||
|
||||
simplices = solver.simplices,
|
||||
simplexCounts = solver.simplexCounts,
|
||||
simplexBounds = solver.simplexBounds,
|
||||
|
||||
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
collisionMaterials = world.collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
bounds = world.colliderAabbs.AsNativeArray<BurstAabb>(),
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
return generateColliderContactsJob.Schedule(solver.simplexCounts.simplexCount, 16, inputDeps);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1a161c4294214a4fbcb7e9e94800494
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstDFNode
|
||||
{
|
||||
public float4 distancesA;
|
||||
public float4 distancesB;
|
||||
public float4 center;
|
||||
public int firstChild;
|
||||
|
||||
// add 12 bytes of padding to ensure correct memory alignment:
|
||||
private int pad0;
|
||||
private int pad1;
|
||||
private int pad2;
|
||||
|
||||
public float4 SampleWithGradient(float4 position)
|
||||
{
|
||||
float4 nPos = GetNormalizedPos(position);
|
||||
|
||||
// trilinear interpolation of distance:
|
||||
float4 x = distancesA + (distancesB - distancesA) * nPos[0];
|
||||
float2 y = x.xy + (x.zw - x.xy) * nPos[1];
|
||||
float distance = y[0] + (y[1] - y[0]) * nPos[2];
|
||||
|
||||
// gradient estimation:
|
||||
// x == 0
|
||||
float2 a = distancesA.xy + (distancesA.zw - distancesA.xy) * nPos[1];
|
||||
float x0 = a[0] + (a[1] - a[0]) * nPos[2];
|
||||
|
||||
// x == 1
|
||||
a = distancesB.xy + (distancesB.zw - distancesB.xy) * nPos[1];
|
||||
float x1 = a[0] + (a[1] - a[0]) * nPos[2];
|
||||
|
||||
// y == 0
|
||||
float y0 = x[0] + (x[1] - x[0]) * nPos[2];
|
||||
|
||||
// y == 1
|
||||
float y1 = x[2] + (x[3] - x[2]) * nPos[2];
|
||||
|
||||
return new float4(x1 - x0, y1 - y0, y[1] - y[0], distance);
|
||||
|
||||
}
|
||||
|
||||
public float4 GetNormalizedPos(float4 position)
|
||||
{
|
||||
float4 corner = center - new float4(center[3]);
|
||||
return (position - corner) / (center[3] * 2);
|
||||
}
|
||||
|
||||
public int GetOctant(float4 position)
|
||||
{
|
||||
int index = 0;
|
||||
if (position[0] > center[0]) index |= 4;
|
||||
if (position[1] > center[1]) index |= 2;
|
||||
if (position[2] > center[2]) index |= 1;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5a1060edd11d4d268313295cdfe778b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
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;
|
||||
|
||||
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
|
||||
{
|
||||
point = colliderToSolver.InverseTransformPoint(point);
|
||||
|
||||
if (shape.is2D != 0)
|
||||
point[2] = 0;
|
||||
|
||||
var header = distanceFieldHeaders[shape.dataIndex];
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
var co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
|
||||
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;
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
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 * 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 965f98194011e4cd9b8c1400b59565d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,125 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
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;
|
||||
public NativeArray<Edge> edges;
|
||||
public NativeArray<float2> vertices;
|
||||
|
||||
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
|
||||
{
|
||||
point = colliderToSolver.InverseTransformPointUnscaled(point);
|
||||
|
||||
if (shape.is2D != 0)
|
||||
point[2] = 0;
|
||||
|
||||
Edge t = edges[header.firstEdge + dataOffset];
|
||||
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);
|
||||
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
projectedPoint.point = colliderToSolver.TransformPointUnscaled(nearestPoint + normal * shape.contactOffset);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
BIHTraverse(colliderIndex, simplexIndex, simplexStart, simplexSize,
|
||||
positions, orientations, radii, simplices, in simplexBounds, 0, contacts, optimizationIterations, optimizationTolerance);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var node = edgeBihNodes[header.firstNode + nodeIndex];
|
||||
|
||||
if (node.firstChild >= 0)
|
||||
{
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
{
|
||||
// check for contact against all triangles:
|
||||
for (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))
|
||||
{
|
||||
var co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
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);
|
||||
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91bd73e80795e494292d0b2fbf2e5e7f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,184 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
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;
|
||||
|
||||
public HeightFieldHeader header;
|
||||
public NativeArray<float> heightFieldSamples;
|
||||
|
||||
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
|
||||
{
|
||||
point = colliderToSolver.InverseTransformPoint(point);
|
||||
|
||||
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)
|
||||
//BurstMath.OneSidedNormal(triNormal, ref normal);
|
||||
|
||||
projectedPoint.point = colliderToSolver.TransformPoint(nearestPoint + normal * shape.contactOffset);
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (shape.dataIndex < 0) return;
|
||||
|
||||
triNormal = float4.zero;
|
||||
|
||||
var co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
|
||||
int resolutionU = (int)shape.center.x;
|
||||
int resolutionV = (int)shape.center.y;
|
||||
|
||||
// calculate terrain cell size:
|
||||
float cellWidth = shape.size.x / (resolutionU - 1);
|
||||
float cellHeight = shape.size.z / (resolutionV - 1);
|
||||
|
||||
// calculate particle bounds min/max cells:
|
||||
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)
|
||||
{
|
||||
if (su >= 0 && su < resolutionU - 1)
|
||||
{
|
||||
for (int sv = min[1]; sv <= max[1]; ++sv)
|
||||
{
|
||||
if (sv >= 0 && sv < resolutionV - 1)
|
||||
{
|
||||
// calculate neighbor sample indices:
|
||||
int csu1 = math.clamp(su + 1, 0, resolutionU - 1);
|
||||
int csv1 = math.clamp(sv + 1, 0, resolutionV - 1);
|
||||
|
||||
// sample heights:
|
||||
float h1 = heightFieldSamples[header.firstSample + sv * resolutionU + su] * shape.size.y;
|
||||
float h2 = heightFieldSamples[header.firstSample + sv * resolutionU + csu1] * shape.size.y;
|
||||
float h3 = heightFieldSamples[header.firstSample + csv1 * resolutionU + su] * shape.size.y;
|
||||
float h4 = heightFieldSamples[header.firstSample + csv1 * resolutionU + csu1] * shape.size.y;
|
||||
|
||||
if (h1 < 0) continue;
|
||||
h1 = math.abs(h1);
|
||||
h2 = math.abs(h2);
|
||||
h3 = math.abs(h3);
|
||||
h4 = math.abs(h4);
|
||||
|
||||
float min_x = su * shape.size.x / (resolutionU - 1);
|
||||
float max_x = csu1 * shape.size.x / (resolutionU - 1);
|
||||
float min_z = sv * shape.size.z / (resolutionV - 1);
|
||||
float max_z = csv1 * shape.size.z / (resolutionV - 1);
|
||||
|
||||
float4 convexPoint;
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
|
||||
// ------contact against the first triangle------:
|
||||
float4 v1 = new float4(min_x, h3, max_z, 0);
|
||||
float4 v2 = new float4(max_x, h4, max_z, 0);
|
||||
float4 v3 = new float4(min_x, h1, min_z, 0);
|
||||
|
||||
tri.Cache(v1, v2, v3);
|
||||
triNormal.xyz = math.normalizesafe(math.cross((v2 - v1).xyz, (v3 - v1).xyz));
|
||||
|
||||
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;
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
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(convexPoint - colliderPoint.point, colliderPoint.normal);
|
||||
float vel = math.dot(velocity - rbVelocity, colliderPoint.normal);
|
||||
|
||||
if (vel * dt + dAB <= simplexRadius + shape.contactOffset + collisionMargin)
|
||||
{
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
|
||||
// ------contact against the second triangle------:
|
||||
v1 = new float4(min_x, h1, min_z, 0);
|
||||
v2 = new float4(max_x, h4, max_z, 0);
|
||||
v3 = new float4(max_x, h2, min_z, 0);
|
||||
|
||||
tri.Cache(v1, v2, v3);
|
||||
triNormal.xyz = math.normalizesafe(math.cross((v2 - v1).xyz, (v3 - v1).xyz));
|
||||
|
||||
colliderPoint = BurstLocalOptimization.Optimize<BurstHeightField>(ref this, positions, orientations, radii, simplices, simplexStart, simplexSize,
|
||||
ref simplexBary, out convexPoint, optimizationIterations, optimizationTolerance);
|
||||
|
||||
velocity = float4.zero;
|
||||
simplexRadius = 0;
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
simplexRadius += radii[particleIndex].x * simplexBary[j];
|
||||
velocity += velocities[particleIndex] * simplexBary[j];
|
||||
}
|
||||
|
||||
rbVelocity = float4.zero;
|
||||
if (rigidbodyIndex >= 0)
|
||||
rbVelocity = BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, colliderPoint.point, rigidbodies, solverToWorld);
|
||||
|
||||
dAB = math.dot(convexPoint - colliderPoint.point, colliderPoint.normal);
|
||||
vel = math.dot(velocity - rbVelocity, colliderPoint.normal);
|
||||
|
||||
if (vel * dt + dAB <= simplexRadius + shape.contactOffset + collisionMargin)
|
||||
{
|
||||
co.pointB = colliderPoint.point;
|
||||
co.normal = colliderPoint.normal;
|
||||
co.pointA = simplexBary;
|
||||
|
||||
contacts.Enqueue(co);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 780062b6393e3476e947fb10fc9b4f3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,203 @@
|
||||
#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;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public static class BurstLocalOptimization
|
||||
{
|
||||
|
||||
/**
|
||||
* point in the surface of a signed distance field.
|
||||
*/
|
||||
public struct SurfacePoint
|
||||
{
|
||||
public float4 bary;
|
||||
public float4 point;
|
||||
public float4 normal;
|
||||
}
|
||||
|
||||
public interface IDistanceFunction
|
||||
{
|
||||
void Evaluate(float4 point, float4 radii, quaternion orientation, ref SurfacePoint projectedPoint);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void GetInterpolatedSimplexData(int simplexStart,
|
||||
int simplexSize,
|
||||
NativeArray<int> simplices,
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> radii,
|
||||
float4 convexBary,
|
||||
out float4 convexPoint,
|
||||
out float4 convexRadii,
|
||||
out quaternion convexOrientation)
|
||||
{
|
||||
convexPoint = float4.zero;
|
||||
convexRadii = float4.zero;
|
||||
convexOrientation = new quaternion(0, 0, 0, 0);
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particle = simplices[simplexStart + j];
|
||||
convexPoint += positions[particle] * convexBary[j];
|
||||
convexRadii += radii[particle] * convexBary[j];
|
||||
convexOrientation.value += orientations[particle].value * convexBary[j];
|
||||
}
|
||||
}
|
||||
|
||||
public static SurfacePoint Optimize<T>(ref T function,
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> radii,
|
||||
NativeArray<int> simplices,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
ref float4 convexBary,
|
||||
out float4 convexPoint,
|
||||
int maxIterations = 16,
|
||||
float tolerance = 0.004f) where T : struct, IDistanceFunction
|
||||
{
|
||||
var pointInFunction = new SurfacePoint();
|
||||
|
||||
// get cartesian coordinates of the initial guess:
|
||||
GetInterpolatedSimplexData(simplexStart, simplexSize, simplices, positions, orientations, radii, convexBary, out convexPoint, out float4 convexThickness, out quaternion convexOrientation);
|
||||
|
||||
// for a 0-simplex (point), perform a single evaluation:
|
||||
if (simplexSize == 1 || maxIterations < 1)
|
||||
function.Evaluate(convexPoint, convexThickness, convexOrientation, ref pointInFunction);
|
||||
|
||||
// for a 1-simplex (edge), perform golden ratio search:
|
||||
else if (simplexSize == 2)
|
||||
GoldenSearch(ref function, simplexStart, simplexSize, positions, orientations, radii, simplices, ref convexPoint, ref convexThickness, ref convexOrientation, ref convexBary, ref pointInFunction, maxIterations, tolerance * 10);
|
||||
|
||||
// for higher-order simplices, use general Frank-Wolfe convex optimization:
|
||||
else
|
||||
FrankWolfe(ref function, simplexStart, simplexSize, positions, orientations, radii, simplices, ref convexPoint, ref convexThickness, ref convexOrientation, ref convexBary, ref pointInFunction, maxIterations, tolerance);
|
||||
|
||||
return pointInFunction;
|
||||
}
|
||||
|
||||
// Frank-Wolfe convex optimization algorithm. Returns closest point to a simplex in a signed distance function.
|
||||
private static void FrankWolfe<T>(ref T function,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> radii,
|
||||
NativeArray<int> simplices,
|
||||
ref float4 convexPoint,
|
||||
ref float4 convexThickness,
|
||||
ref quaternion convexOrientation,
|
||||
ref float4 convexBary,
|
||||
ref SurfacePoint pointInFunction,
|
||||
int maxIterations,
|
||||
float tolerance) where T : struct, IDistanceFunction
|
||||
{
|
||||
for (int i = 0; i < maxIterations; ++i)
|
||||
{
|
||||
// sample target function:
|
||||
function.Evaluate(convexPoint, convexThickness, convexOrientation, ref pointInFunction);
|
||||
|
||||
// find descent direction:
|
||||
int descent = 0;
|
||||
float gap = float.MinValue;
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particle = simplices[simplexStart + j];
|
||||
float4 candidate = positions[particle] - convexPoint;
|
||||
|
||||
// here, we adjust the candidate by projecting it to the engrosed simplex's surface:
|
||||
candidate -= pointInFunction.normal * (radii[particle].x - convexThickness.x);
|
||||
|
||||
float corr = math.dot(-pointInFunction.normal, candidate);
|
||||
if (corr > gap)
|
||||
{
|
||||
descent = j;
|
||||
gap = corr;
|
||||
}
|
||||
}
|
||||
|
||||
// if the duality gap is below tolerance threshold, stop iterating.
|
||||
if (gap < tolerance)
|
||||
break;
|
||||
|
||||
// update the barycentric coords using 2/(i+2) as the step factor
|
||||
float step = 0.3f * 2.0f / (i + 2);
|
||||
convexBary *= 1 - step;
|
||||
convexBary[descent] += step;
|
||||
|
||||
// get cartesian coordinates of current solution:
|
||||
GetInterpolatedSimplexData(simplexStart, simplexSize, simplices, positions, orientations, radii, convexBary, out convexPoint, out convexThickness, out convexOrientation);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GoldenSearch<T>(ref T function,
|
||||
int simplexStart,
|
||||
int simplexSize,
|
||||
NativeArray<float4> positions,
|
||||
NativeArray<quaternion> orientations,
|
||||
NativeArray<float4> radii,
|
||||
NativeArray<int> simplices,
|
||||
ref float4 convexPoint,
|
||||
ref float4 convexThickness,
|
||||
ref quaternion convexOrientation,
|
||||
ref float4 convexBary,
|
||||
ref SurfacePoint pointInFunction,
|
||||
int maxIterations,
|
||||
float tolerance) where T : struct, IDistanceFunction
|
||||
{
|
||||
var pointInFunctionD = new SurfacePoint();
|
||||
float4 convexPointD, convexThicknessD;
|
||||
quaternion convexOrientationD;
|
||||
|
||||
float gr = (math.sqrt(5.0f) + 1) / 2.0f;
|
||||
float u = 0, v = 1;
|
||||
float c = v - (v - u) / gr;
|
||||
float d = u + (v - u) / gr;
|
||||
|
||||
for (int i = 0; i < maxIterations; ++i)
|
||||
{
|
||||
// if the gap is below tolerance threshold, stop iterating.
|
||||
if (math.abs(v - u) < tolerance * (math.abs(c) + math.abs(d)))
|
||||
break;
|
||||
|
||||
GetInterpolatedSimplexData(simplexStart, simplexSize, simplices, positions, orientations, radii, new float4(c, 1 - c, 0, 0), out convexPoint, out convexThickness, out convexOrientation);
|
||||
GetInterpolatedSimplexData(simplexStart, simplexSize, simplices, positions, orientations, radii, new float4(d, 1 - d, 0, 0), out convexPointD, out convexThicknessD, out convexOrientationD);
|
||||
|
||||
function.Evaluate(convexPoint, convexThickness, convexOrientation, ref pointInFunction);
|
||||
function.Evaluate(convexPointD, convexThicknessD, convexOrientationD, ref pointInFunctionD);
|
||||
|
||||
float4 candidateC = positions[simplices[simplexStart]] - pointInFunction.point;
|
||||
float4 candidateD = positions[simplices[simplexStart + 1]] - pointInFunctionD.point;
|
||||
|
||||
candidateC -= pointInFunction.normal * (radii[simplices[simplexStart]].x - convexThickness.x);
|
||||
candidateD -= pointInFunctionD.normal * (radii[simplices[simplexStart + 1]].x - convexThicknessD.x);
|
||||
|
||||
if (math.dot(-pointInFunction.normal, candidateC) < math.dot(-pointInFunctionD.normal, candidateD))
|
||||
v = d;
|
||||
else
|
||||
u = c;
|
||||
|
||||
c = v - (v - u) / gr;
|
||||
d = u + (v - u) / gr;
|
||||
}
|
||||
|
||||
float mid = (v + u) * 0.5f;
|
||||
convexBary.x = mid;
|
||||
convexBary.y = (1 - mid);
|
||||
GetInterpolatedSimplexData(simplexStart, simplexSize, simplices, positions, orientations, radii, convexBary, out convexPoint, out convexThickness, out convexOrientation);
|
||||
function.Evaluate(convexPoint, convexThickness, convexOrientation, ref pointInFunction);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0353c4eae8ca4ec78f2db34845cd5cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,64 @@
|
||||
#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;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstSimplex : BurstLocalOptimization.IDistanceFunction
|
||||
{
|
||||
public NativeArray<float4> positions;
|
||||
public NativeArray<float4> radii;
|
||||
public NativeArray<int> simplices;
|
||||
|
||||
public int simplexStart;
|
||||
public int simplexSize;
|
||||
|
||||
private BurstMath.CachedTri tri;
|
||||
|
||||
public void CacheData()
|
||||
{
|
||||
if (simplexSize == 3)
|
||||
{
|
||||
tri.Cache(positions[simplices[simplexStart]],
|
||||
positions[simplices[simplexStart + 1]],
|
||||
positions[simplices[simplexStart + 2]]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
|
||||
{
|
||||
switch (simplexSize)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
float4 p1 = positions[simplices[simplexStart]];
|
||||
projectedPoint.bary = new float4(1, 0, 0, 0);
|
||||
projectedPoint.point = p1;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
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];
|
||||
|
||||
}break;
|
||||
case 3:
|
||||
projectedPoint.point = BurstMath.NearestPointOnTri(tri, point, out projectedPoint.bary);
|
||||
break;
|
||||
}
|
||||
|
||||
projectedPoint.normal = math.normalizesafe(point - projectedPoint.point);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d84f433832a8c4a90ba92ac471d18e44
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,64 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
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 != 0)
|
||||
point[2] = 0;
|
||||
|
||||
float radius = shape.size.x * math.cmax(colliderToSolver.scale.xyz);
|
||||
float distanceToCenter = math.length(point);
|
||||
|
||||
float4 normal = point / (distanceToCenter + BurstMath.epsilon);
|
||||
|
||||
projectedPoint.point = colliderToSolver.TransformPointUnscaled(center + normal * (radius + shape.contactOffset));
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
}
|
||||
|
||||
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 co = new BurstContact() { bodyA = simplexIndex, bodyB = colliderIndex };
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38081dcc3e28a4a3dba1e8e5d042a262
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,149 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BurstTriangleMesh : BurstLocalOptimization.IDistanceFunction, IBurstCollider
|
||||
{
|
||||
public BurstColliderShape shape;
|
||||
public BurstAffineTransform colliderToSolver;
|
||||
public BurstAffineTransform solverToWorld;
|
||||
|
||||
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 != 0)
|
||||
point[2] = 0;
|
||||
|
||||
float4 nearestPoint = BurstMath.NearestPointOnTri(tri, point, out float4 bary);
|
||||
float4 normal = math.normalizesafe(point - nearestPoint);
|
||||
|
||||
projectedPoint.point = colliderToSolver.TransformPointUnscaled(nearestPoint + normal * shape.contactOffset);
|
||||
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
BIHTraverse(colliderIndex, rigidbodyIndex, simplexIndex, simplexStart, simplexSize,
|
||||
rigidbodies, positions, orientations, velocities, radii, simplices, in simplexBounds, 0, contacts, optimizationIterations, optimizationTolerance);
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var node = bihNodes[header.firstNode + nodeIndex];
|
||||
|
||||
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);
|
||||
|
||||
// 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
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
|
||||
tri.Cache(v1 * colliderToSolver.scale, v2 * colliderToSolver.scale, v3 * colliderToSolver.scale);
|
||||
|
||||
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)
|
||||
{
|
||||
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 * dt + dAB <= simplexRadius + shape.contactOffset + collisionMargin)
|
||||
{
|
||||
contacts.Enqueue(new BurstContact()
|
||||
{
|
||||
bodyA = simplexIndex,
|
||||
bodyB = colliderIndex,
|
||||
pointA = simplexBary,
|
||||
pointB = colliderPoint.point,
|
||||
normal = colliderPoint.normal,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a65b8b437c2548c0b04b7ddb58a3970
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 424c135125b644408aeda26a57bd6e40
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2aff1726dad4479cb41e9f94e28473f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4c2af25bbe8a4c44941ea5486c18f71
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstAerodynamicConstraints : BurstConstraintsImpl<BurstAerodynamicConstraintsBatch>
|
||||
{
|
||||
public BurstAerodynamicConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.Aerodynamics)
|
||||
{
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstAerodynamicConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstAerodynamicConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64e54848bb85248c8b0c31991ef094e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,112 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstAerodynamicConstraintsBatch : BurstConstraintsBatchImpl, IAerodynamicConstraintsBatchImpl
|
||||
{
|
||||
private NativeArray<float> aerodynamicCoeffs;
|
||||
|
||||
public BurstAerodynamicConstraintsBatch(BurstAerodynamicConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.Aerodynamics;
|
||||
}
|
||||
|
||||
public void SetAerodynamicConstraints(ObiNativeIntList particleIndices, ObiNativeFloatList aerodynamicCoeffs, int count)
|
||||
{
|
||||
this.particleIndices = particleIndices.AsNativeArray<int>();
|
||||
this.aerodynamicCoeffs = aerodynamicCoeffs.AsNativeArray<float>();
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new AerodynamicConstraintsBatchJob()
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
aerodynamicCoeffs = aerodynamicCoeffs,
|
||||
positions = solverImplementation.positions,
|
||||
velocities = solverImplementation.velocities,
|
||||
normals = solverImplementation.normals,
|
||||
wind = solverImplementation.wind,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
deltaTime = substepTime
|
||||
};
|
||||
|
||||
return projectConstraints.Schedule(m_ConstraintCount, 32, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct AerodynamicConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] [NativeDisableParallelForRestriction] public NativeArray<float> aerodynamicCoeffs;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> normals;
|
||||
[ReadOnly] public NativeArray<float4> wind;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public NativeArray<float4> velocities;
|
||||
|
||||
[ReadOnly] public float deltaTime;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int p = particleIndices[i];
|
||||
|
||||
float area = aerodynamicCoeffs[i * 3];
|
||||
float dragCoeff = aerodynamicCoeffs[i * 3 + 1];
|
||||
float liftCoeff = aerodynamicCoeffs[i * 3 + 2];
|
||||
|
||||
if (invMasses[p] > 0)
|
||||
{
|
||||
float4 relVelocity = velocities[p] - wind[p];
|
||||
float rvSqrMag = math.lengthsq(relVelocity);
|
||||
|
||||
if (rvSqrMag < BurstMath.epsilon)
|
||||
return;
|
||||
|
||||
float4 rvNorm = relVelocity / math.sqrt(rvSqrMag);
|
||||
|
||||
// calculate surface normal (always facing wind)
|
||||
float4 surfNormal = normals[p] * math.sign(math.dot(normals[p], rvNorm));
|
||||
|
||||
// aerodynamic_factor was originally multiplied by air_density. The density is now premultiplied in lift and drag.
|
||||
float aerodynamicFactor = 0.5f * rvSqrMag * area;
|
||||
float attackAngle = math.dot(surfNormal,rvNorm);
|
||||
|
||||
float3 liftDirection = math.normalizesafe(math.cross(math.cross(surfNormal.xyz, rvNorm.xyz), rvNorm.xyz));
|
||||
|
||||
//drag:
|
||||
velocities[p] += (-dragCoeff * rvNorm +
|
||||
|
||||
// lift:
|
||||
liftCoeff * new float4(liftDirection.xyz,0)) *
|
||||
|
||||
// scale
|
||||
attackAngle * math.min(aerodynamicFactor * invMasses[p] * deltaTime, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9db1828d66ec4f65aa37304663982af
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93818b1e63d1143c580b09480a2c0a1c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstBendConstraints : BurstConstraintsImpl<BurstBendConstraintsBatch>
|
||||
{
|
||||
public BurstBendConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.Bending)
|
||||
{
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstBendConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstBendConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c5e3cbf9187d4d39a1bc1a2e2386b00
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,169 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstBendConstraintsBatch : BurstConstraintsBatchImpl, IBendConstraintsBatchImpl
|
||||
{
|
||||
private NativeArray<float> restBends;
|
||||
private NativeArray<float2> stiffnesses;
|
||||
private NativeArray<float2> plasticity;
|
||||
|
||||
BendConstraintsBatchJob projectConstraints;
|
||||
ApplyBendConstraintsBatchJob applyConstraints;
|
||||
|
||||
public BurstBendConstraintsBatch(BurstBendConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.Bending;
|
||||
}
|
||||
|
||||
public void SetBendConstraints(ObiNativeIntList particleIndices, ObiNativeFloatList restBends, ObiNativeVector2List bendingStiffnesses, ObiNativeVector2List plasticity, ObiNativeFloatList lambdas, int count)
|
||||
{
|
||||
this.particleIndices = particleIndices.AsNativeArray<int>();
|
||||
this.restBends = restBends.AsNativeArray<float>();
|
||||
this.stiffnesses = bendingStiffnesses.AsNativeArray<float2>();
|
||||
this.plasticity = plasticity.AsNativeArray<float2>();
|
||||
this.lambdas = lambdas.AsNativeArray<float>();
|
||||
m_ConstraintCount = count;
|
||||
|
||||
projectConstraints.particleIndices = this.particleIndices;
|
||||
projectConstraints.restBends = this.restBends;
|
||||
projectConstraints.stiffnesses = this.stiffnesses;
|
||||
projectConstraints.plasticity = this.plasticity;
|
||||
projectConstraints.lambdas = this.lambdas;
|
||||
|
||||
applyConstraints.particleIndices = this.particleIndices;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
projectConstraints.positions = solverImplementation.positions;
|
||||
projectConstraints.invMasses = solverImplementation.invMasses;
|
||||
projectConstraints.deltas = solverImplementation.positionDeltas;
|
||||
projectConstraints.counts = solverImplementation.positionConstraintCounts;
|
||||
projectConstraints.deltaTime = substepTime;
|
||||
|
||||
return projectConstraints.Schedule(m_ConstraintCount, 32, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
applyConstraints.positions = solverImplementation.positions;
|
||||
applyConstraints.deltas = solverImplementation.positionDeltas;
|
||||
applyConstraints.counts = solverImplementation.positionConstraintCounts;
|
||||
applyConstraints.sorFactor = parameters.SORFactor;
|
||||
|
||||
return applyConstraints.Schedule(m_ConstraintCount, 64, inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct BendConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public NativeArray<float2> stiffnesses;
|
||||
[ReadOnly] public NativeArray<float2> plasticity; //plastic yield, creep
|
||||
public NativeArray<float> restBends;
|
||||
public NativeArray<float> lambdas;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
[ReadOnly] public float deltaTime;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int p1 = particleIndices[i * 3];
|
||||
int p2 = particleIndices[i * 3 + 1];
|
||||
int p3 = particleIndices[i * 3 + 2];
|
||||
|
||||
float w1 = invMasses[p1];
|
||||
float w2 = invMasses[p2];
|
||||
float w3 = invMasses[p3];
|
||||
|
||||
float wsum = w1 + w2 + 2 * w3;
|
||||
|
||||
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) +
|
||||
math.min(0, constraint + stiffnesses[i].x);
|
||||
|
||||
// plasticity:
|
||||
if (math.abs(constraint) > plasticity[i].x)
|
||||
restBends[i] += constraint * plasticity[i].y * deltaTime;
|
||||
|
||||
// calculate time adjusted compliance
|
||||
float compliance = stiffnesses[i].y / (deltaTime * deltaTime);
|
||||
|
||||
// since the third particle moves twice the amount of the other 2, the modulus of its gradient is 2:
|
||||
float dlambda = (-constraint - compliance * lambdas[i]) / (wsum + compliance + BurstMath.epsilon);
|
||||
float4 correction = dlambda * bendVector / (bend + BurstMath.epsilon);
|
||||
|
||||
lambdas[i] += dlambda;
|
||||
|
||||
deltas[p1] -= correction * 2 * w1;
|
||||
deltas[p2] -= correction * 2 * w2;
|
||||
deltas[p3] += correction * 4 * w3;
|
||||
|
||||
counts[p1]++;
|
||||
counts[p2]++;
|
||||
counts[p3]++;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ApplyBendConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public float sorFactor;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int p1 = particleIndices[i * 3];
|
||||
int p2 = particleIndices[i * 3 + 1];
|
||||
int p3 = particleIndices[i * 3 + 2];
|
||||
|
||||
if (counts[p1] > 0)
|
||||
{
|
||||
positions[p1] += deltas[p1] * sorFactor / counts[p1];
|
||||
deltas[p1] = float4.zero;
|
||||
counts[p1] = 0;
|
||||
}
|
||||
|
||||
if (counts[p2] > 0)
|
||||
{
|
||||
positions[p2] += deltas[p2] * sorFactor / counts[p2];
|
||||
deltas[p2] = float4.zero;
|
||||
counts[p2] = 0;
|
||||
}
|
||||
|
||||
if (counts[p3] > 0)
|
||||
{
|
||||
positions[p3] += deltas[p3] * sorFactor / counts[p3];
|
||||
deltas[p3] = float4.zero;
|
||||
counts[p3] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ca9c1fed63774363964743eb64fb68b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e62fc9e22b02847ba86a32d3d0f375da
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstBendTwistConstraints : BurstConstraintsImpl<BurstBendTwistConstraintsBatch>
|
||||
{
|
||||
public BurstBendTwistConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.BendTwist)
|
||||
{
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstBendTwistConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstBendTwistConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59166e10c311e43e283064f9c03b525c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,182 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstBendTwistConstraintsBatch : BurstConstraintsBatchImpl, IBendTwistConstraintsBatchImpl
|
||||
{
|
||||
private NativeArray<int> orientationIndices;
|
||||
private NativeArray<quaternion> restDarboux;
|
||||
private NativeArray<float3> stiffnesses;
|
||||
private NativeArray<float2> plasticity;
|
||||
|
||||
public BurstBendTwistConstraintsBatch(BurstBendTwistConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.BendTwist;
|
||||
}
|
||||
|
||||
public void SetBendTwistConstraints(ObiNativeIntList orientationIndices, ObiNativeQuaternionList restDarboux, ObiNativeVector3List stiffnesses, ObiNativeVector2List plasticity, ObiNativeFloatList lambdas, int count)
|
||||
{
|
||||
this.orientationIndices = orientationIndices.AsNativeArray<int>();
|
||||
this.restDarboux = restDarboux.AsNativeArray<quaternion>();
|
||||
this.stiffnesses = stiffnesses.AsNativeArray<float3>();
|
||||
this.plasticity = plasticity.AsNativeArray<float2>();
|
||||
this.lambdas = lambdas.AsNativeArray<float>();
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var projectConstraints = new BendTwistConstraintsBatchJob()
|
||||
{
|
||||
orientationIndices = orientationIndices,
|
||||
restDarboux = restDarboux,
|
||||
stiffnesses = stiffnesses,
|
||||
plasticity = plasticity,
|
||||
lambdas = lambdas.Reinterpret<float, float3>(),
|
||||
|
||||
orientations = solverImplementation.orientations,
|
||||
invRotationalMasses = solverImplementation.invRotationalMasses,
|
||||
|
||||
orientationDeltas = solverImplementation.orientationDeltas,
|
||||
orientationCounts = solverImplementation.orientationConstraintCounts ,
|
||||
|
||||
deltaTime = substepTime
|
||||
};
|
||||
|
||||
return projectConstraints.Schedule(m_ConstraintCount, 32, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
var applyConstraints = new ApplyBendTwistConstraintsBatchJob()
|
||||
{
|
||||
orientationIndices = orientationIndices,
|
||||
|
||||
orientations = solverImplementation.orientations,
|
||||
orientationDeltas = solverImplementation.orientationDeltas,
|
||||
orientationCounts = solverImplementation.orientationConstraintCounts,
|
||||
|
||||
sorFactor = parameters.SORFactor
|
||||
};
|
||||
|
||||
return applyConstraints.Schedule(m_ConstraintCount, 64, inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct BendTwistConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> orientationIndices;
|
||||
[ReadOnly] public NativeArray<float3> stiffnesses;
|
||||
[ReadOnly] public NativeArray<float2> plasticity;
|
||||
public NativeArray<quaternion> restDarboux;
|
||||
public NativeArray<float3> lambdas;
|
||||
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<float> invRotationalMasses;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<quaternion> orientationDeltas;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<int> orientationCounts;
|
||||
|
||||
[ReadOnly] public float deltaTime;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int q1 = orientationIndices[i * 2];
|
||||
int q2 = orientationIndices[i * 2 + 1];
|
||||
|
||||
float w1 = invRotationalMasses[q1];
|
||||
float w2 = invRotationalMasses[q2];
|
||||
|
||||
// calculate time adjusted compliance
|
||||
float3 compliances = stiffnesses[i] / (deltaTime * deltaTime);
|
||||
|
||||
// rest and current darboux vectors
|
||||
quaternion rest = restDarboux[i];
|
||||
quaternion omega = math.mul(math.conjugate(orientations[q1]), orientations[q2]);
|
||||
|
||||
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) > plasticity[i].x * plasticity[i].x)
|
||||
{
|
||||
rest.value += omega.value * plasticity[i].y * deltaTime;
|
||||
restDarboux[i] = rest;
|
||||
}
|
||||
|
||||
float3 dlambda = (omega.value.xyz - compliances * lambdas[i]) / (compliances + new float3(w1 + w2 + BurstMath.epsilon));
|
||||
|
||||
//discrete Darboux vector does not have vanishing scalar part
|
||||
quaternion dlambdaQ = new quaternion(dlambda[0], dlambda[1], dlambda[2],0);
|
||||
|
||||
quaternion d1 = orientationDeltas[q1];
|
||||
quaternion d2 = orientationDeltas[q2];
|
||||
|
||||
d1.value += math.mul(orientations[q2], dlambdaQ).value * w1;
|
||||
d2.value -= math.mul(orientations[q1], dlambdaQ).value * w2;
|
||||
|
||||
orientationDeltas[q1] = d1;
|
||||
orientationDeltas[q2] = d2;
|
||||
|
||||
orientationCounts[q1]++;
|
||||
orientationCounts[q2]++;
|
||||
|
||||
lambdas[i] += dlambda;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ApplyBendTwistConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> orientationIndices;
|
||||
[ReadOnly] public float sorFactor;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<quaternion> orientations;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<quaternion> orientationDeltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> orientationCounts;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int p1 = orientationIndices[i * 2];
|
||||
int p2 = orientationIndices[i * 2 + 1];
|
||||
|
||||
if (orientationCounts[p1] > 0)
|
||||
{
|
||||
quaternion q = orientations[p1];
|
||||
q.value += orientationDeltas[p1].value * sorFactor / orientationCounts[p1];
|
||||
orientations[p1] = math.normalize(q);
|
||||
|
||||
orientationDeltas[p1] = new quaternion(0, 0, 0, 0);
|
||||
orientationCounts[p1] = 0;
|
||||
}
|
||||
|
||||
if (orientationCounts[p2] > 0)
|
||||
{
|
||||
quaternion q = orientations[p2];
|
||||
q.value += orientationDeltas[p2].value * sorFactor / orientationCounts[p2];
|
||||
orientations[p2] = math.normalize(q);
|
||||
|
||||
orientationDeltas[p2] = new quaternion(0, 0, 0, 0);
|
||||
orientationCounts[p2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6235d2e315a94287ab8a219496d7961
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,109 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Burst;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public abstract class BurstConstraintsBatchImpl : IConstraintsBatchImpl
|
||||
{
|
||||
protected IBurstConstraintsImpl m_Constraints;
|
||||
protected Oni.ConstraintType m_ConstraintType;
|
||||
|
||||
protected bool m_Enabled = true;
|
||||
protected int m_ConstraintCount = 0;
|
||||
|
||||
public Oni.ConstraintType constraintType
|
||||
{
|
||||
get { return m_ConstraintType; }
|
||||
}
|
||||
|
||||
public bool enabled
|
||||
{
|
||||
set
|
||||
{
|
||||
if (m_Enabled != value)
|
||||
m_Enabled = value;
|
||||
}
|
||||
get { return m_Enabled; }
|
||||
}
|
||||
|
||||
public IConstraints constraints
|
||||
{
|
||||
get { return m_Constraints; }
|
||||
}
|
||||
|
||||
public ObiSolver solverAbstraction
|
||||
{
|
||||
get { return ((BurstSolverImpl)m_Constraints.solver).abstraction; }
|
||||
}
|
||||
|
||||
public BurstSolverImpl solverImplementation
|
||||
{
|
||||
get { return (BurstSolverImpl)m_Constraints.solver; }
|
||||
}
|
||||
|
||||
protected NativeArray<int> particleIndices;
|
||||
protected NativeArray<float> lambdas;
|
||||
|
||||
public virtual JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
if (lambdas.IsCreated)
|
||||
{
|
||||
// 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 substeps);
|
||||
public abstract JobHandle Apply(JobHandle inputDeps, float substepTime);
|
||||
|
||||
public virtual void Destroy()
|
||||
{
|
||||
// clean resources allocated by the batch, no need for a default implementation.
|
||||
}
|
||||
|
||||
public void SetConstraintCount(int constraintCount)
|
||||
{
|
||||
m_ConstraintCount = constraintCount;
|
||||
}
|
||||
public int GetConstraintCount()
|
||||
{
|
||||
return m_ConstraintCount;
|
||||
}
|
||||
|
||||
public static void ApplyPositionDelta(int particleIndex, float sorFactor, ref NativeArray<float4> positions, ref NativeArray<float4> deltas, ref NativeArray<int> counts)
|
||||
{
|
||||
if (counts[particleIndex] > 0)
|
||||
{
|
||||
positions[particleIndex] += deltas[particleIndex] * sorFactor / counts[particleIndex];
|
||||
deltas[particleIndex] = float4.zero;
|
||||
counts[particleIndex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ApplyOrientationDelta(int particleIndex, float sorFactor, ref NativeArray<quaternion> orientations, ref NativeArray<quaternion> deltas, ref NativeArray<int> counts)
|
||||
{
|
||||
if (counts[particleIndex] > 0)
|
||||
{
|
||||
quaternion q = orientations[particleIndex];
|
||||
q.value += deltas[particleIndex].value * sorFactor / counts[particleIndex];
|
||||
orientations[particleIndex] = math.normalize(q);
|
||||
|
||||
deltas[particleIndex] = new quaternion(0, 0, 0, 0);
|
||||
counts[particleIndex] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09a081eed828341979cf470e2eca6ee0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,145 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public interface IBurstConstraintsImpl : IConstraints
|
||||
{
|
||||
JobHandle Initialize(JobHandle inputDeps, float substepTime);
|
||||
JobHandle Project(JobHandle inputDeps, float stepTime, float substepTime, int substeps);
|
||||
void Dispose();
|
||||
|
||||
IConstraintsBatchImpl CreateConstraintsBatch();
|
||||
void RemoveBatch(IConstraintsBatchImpl batch);
|
||||
}
|
||||
|
||||
public abstract class BurstConstraintsImpl<T> : IBurstConstraintsImpl where T : BurstConstraintsBatchImpl
|
||||
{
|
||||
protected BurstSolverImpl m_Solver;
|
||||
public List<T> batches = new List<T>();
|
||||
|
||||
protected Oni.ConstraintType m_ConstraintType;
|
||||
|
||||
public Oni.ConstraintType constraintType
|
||||
{
|
||||
get { return m_ConstraintType; }
|
||||
}
|
||||
|
||||
public ISolverImpl solver
|
||||
{
|
||||
get { return m_Solver; }
|
||||
}
|
||||
|
||||
public BurstConstraintsImpl(BurstSolverImpl solver, Oni.ConstraintType constraintType)
|
||||
{
|
||||
this.m_ConstraintType = constraintType;
|
||||
this.m_Solver = solver;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public abstract IConstraintsBatchImpl CreateConstraintsBatch();
|
||||
|
||||
|
||||
public abstract void RemoveBatch(IConstraintsBatchImpl batch);
|
||||
|
||||
|
||||
public virtual int GetConstraintCount()
|
||||
{
|
||||
int count = 0;
|
||||
if (batches == null) return count;
|
||||
|
||||
foreach (T batch in batches)
|
||||
if (batch != null)
|
||||
count += batch.GetConstraintCount();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
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, substepTime) : inputDeps;
|
||||
|
||||
JobHandle result = JobHandle.CombineDependencies(deps);
|
||||
deps.Dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public JobHandle Project(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
UnityEngine.Profiling.Profiler.BeginSample("Project");
|
||||
|
||||
var parameters = m_Solver.abstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
switch(parameters.evaluationOrder)
|
||||
{
|
||||
case Oni.ConstraintParameters.EvaluationOrder.Sequential:
|
||||
inputDeps = EvaluateSequential(inputDeps, stepTime, substepTime, substeps);
|
||||
break;
|
||||
|
||||
case Oni.ConstraintParameters.EvaluationOrder.Parallel:
|
||||
inputDeps = EvaluateParallel(inputDeps, stepTime, substepTime, substeps);
|
||||
break;
|
||||
}
|
||||
|
||||
UnityEngine.Profiling.Profiler.EndSample();
|
||||
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
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, substeps);
|
||||
inputDeps = batches[i].Apply(inputDeps, substepTime);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
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, substeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
|
||||
// then apply them:
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].Apply(inputDeps, substepTime);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c1b834b5430e4d3790a5979367cd915
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60c7b29b5d2db47a68bfc9b00847af01
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstChainConstraints : BurstConstraintsImpl<BurstChainConstraintsBatch>
|
||||
{
|
||||
public BurstChainConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.Chain)
|
||||
{
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstChainConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstChainConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea608f10fe5d24cb392ca8dcd960a920
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,217 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstChainConstraintsBatch : BurstConstraintsBatchImpl, IChainConstraintsBatchImpl
|
||||
{
|
||||
private NativeArray<int> firstIndex;
|
||||
private NativeArray<int> numIndices;
|
||||
private NativeArray<float2> restLengths;
|
||||
|
||||
public BurstChainConstraintsBatch(BurstChainConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.Chain;
|
||||
}
|
||||
|
||||
public void SetChainConstraints(ObiNativeIntList particleIndices, ObiNativeVector2List restLengths, ObiNativeIntList firstIndex, ObiNativeIntList numIndices, int count)
|
||||
{
|
||||
this.particleIndices = particleIndices.AsNativeArray<int>();
|
||||
this.firstIndex = firstIndex.AsNativeArray<int>();
|
||||
this.numIndices = numIndices.AsNativeArray<int>();
|
||||
this.restLengths = restLengths.AsNativeArray<float2>();
|
||||
m_ConstraintCount = count;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
|
||||
var projectConstraints = new ChainConstraintsBatchJob()
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
firstIndex = firstIndex,
|
||||
numIndices = numIndices,
|
||||
restLengths = restLengths,
|
||||
|
||||
positions = solverImplementation.positions,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts
|
||||
};
|
||||
|
||||
return projectConstraints.Schedule(m_ConstraintCount, 4, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
var applyConstraints = new ApplyChainConstraintsBatchJob()
|
||||
{
|
||||
particleIndices = particleIndices,
|
||||
firstIndex = firstIndex,
|
||||
numIndices = numIndices,
|
||||
|
||||
positions = solverImplementation.positions,
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
|
||||
sorFactor = parameters.SORFactor
|
||||
};
|
||||
|
||||
return applyConstraints.Schedule(m_ConstraintCount, 8, inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ChainConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public NativeArray<int> firstIndex;
|
||||
[ReadOnly] public NativeArray<int> numIndices;
|
||||
[ReadOnly] public NativeArray<float2> restLengths;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
public void Execute(int c)
|
||||
{
|
||||
int numEdges = numIndices[c] - 1;
|
||||
int first = firstIndex[c];
|
||||
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);
|
||||
NativeArray<float> di = new NativeArray<float>(numEdges, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < numEdges; ++i)
|
||||
{
|
||||
int edge = first + i;
|
||||
|
||||
float4 p1 = positions[particleIndices[edge]];
|
||||
float4 p2 = positions[particleIndices[edge+1]];
|
||||
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 (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;
|
||||
|
||||
float w_i_ = invMasses[particleIndices[edge]];
|
||||
float w__i = invMasses[particleIndices[edge+1]];
|
||||
|
||||
float4 ni__ = (i > 0) ? ni[i - 1] : float4.zero;
|
||||
float4 n_i_ = ni[i];
|
||||
float4 n__i = (i < numEdges - 1) ? ni[i + 1] : float4.zero;
|
||||
|
||||
diagonals[i] = new float3(
|
||||
-w_i_ * math.dot(n_i_, ni__), // ai
|
||||
w_i_ + w__i, // bi
|
||||
-w__i * math.dot(n_i_, n__i));// ci
|
||||
}
|
||||
|
||||
NativeArray<float2> sweep = new NativeArray<float2>(numEdges, Allocator.Temp);
|
||||
|
||||
// solve step #1, forward sweep:
|
||||
for (int i = 0; i < numEdges; ++i)
|
||||
{
|
||||
int edge = first + i;
|
||||
|
||||
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;
|
||||
|
||||
if (den != 0)
|
||||
{
|
||||
sweep[i] = new float2((diagonals[i].z / den),
|
||||
(di[i] - dip_ * diagonals[i].x) / den);
|
||||
}
|
||||
else
|
||||
sweep[i] = float2.zero;
|
||||
}
|
||||
|
||||
// solve step #2, backward sweep:
|
||||
NativeArray<float> xi = new NativeArray<float>(numEdges, Allocator.Temp);
|
||||
for (int i = numEdges - 1; i >= 0; --i)
|
||||
{
|
||||
int edge = first + i;
|
||||
|
||||
float xi_ = (i < numEdges - 1) ? xi[i + 1] : 0;
|
||||
xi[i] = sweep[i].y - sweep[i].x * xi_;
|
||||
}
|
||||
|
||||
// calculate deltas:
|
||||
for (int i = 0; i < numIndices[c]; ++i)
|
||||
{
|
||||
int index = first + i;
|
||||
|
||||
float4 ni__ = (i > 0) ? ni[i - 1] : float4.zero;
|
||||
float4 n_i_ = (i < numIndices[c] - 1) ? ni[i] : float4.zero;
|
||||
|
||||
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);
|
||||
counts[p]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ApplyChainConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public NativeArray<int> firstIndex;
|
||||
[ReadOnly] public NativeArray<int> numIndices;
|
||||
[ReadOnly] public float sorFactor;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int first = firstIndex[i];
|
||||
int last = first + numIndices[i];
|
||||
|
||||
for (int k = first; k < last; ++k)
|
||||
{
|
||||
int p = particleIndices[k];
|
||||
if (counts[p] > 0)
|
||||
{
|
||||
positions[p] += deltas[p] * sorFactor / counts[p];
|
||||
deltas[p] = float4.zero;
|
||||
counts[p] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 832ac0a67ec764ee6a1e9a69fc1092e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d7d6ac7f5b664d5cb7860b843e47fff
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
#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]
|
||||
public struct ApplyCollisionConstraintsBatchJob : IJob
|
||||
{
|
||||
[ReadOnly] public NativeArray<BurstContact> contacts;
|
||||
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<quaternion> orientations;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<quaternion> orientationDeltas;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> orientationCounts;
|
||||
|
||||
[ReadOnly] public Oni.ConstraintParameters constraintParameters;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
for (int i = 0; i < contacts.Length; ++i)
|
||||
{
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(contacts[i].bodyA, out int simplexSize);
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
BurstConstraintsBatchImpl.ApplyPositionDelta(particleIndex, constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
BurstConstraintsBatchImpl.ApplyOrientationDelta(particleIndex, constraintParameters.SORFactor, ref orientations, ref orientationDeltas, ref orientationCounts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f97613844b24a40899f10722bcfc4cf8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstColliderCollisionConstraints : BurstConstraintsImpl<BurstColliderCollisionConstraintsBatch>
|
||||
{
|
||||
public BurstColliderCollisionConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.Collision)
|
||||
{
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstColliderCollisionConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstColliderCollisionConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
|
||||
public override int GetConstraintCount()
|
||||
{
|
||||
if (!((BurstSolverImpl)solver).colliderContacts.IsCreated)
|
||||
return 0;
|
||||
return ((BurstSolverImpl)solver).colliderContacts.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e31847a0e619545c0bc70258b413a42e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,330 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstColliderCollisionConstraintsBatch : BurstConstraintsBatchImpl, IColliderCollisionConstraintsBatchImpl
|
||||
{
|
||||
public BurstColliderCollisionConstraintsBatch(BurstColliderCollisionConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.Collision;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var updateContacts = new UpdateContactsJob()
|
||||
{
|
||||
prevPositions = solverImplementation.prevPositions,
|
||||
prevOrientations = solverImplementation.prevOrientations,
|
||||
velocities = solverImplementation.velocities,
|
||||
radii = solverImplementation.principalRadii,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
invInertiaTensors = solverImplementation.invInertiaTensors,
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
|
||||
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>(),
|
||||
|
||||
contacts = ((BurstSolverImpl)constraints.solver).colliderContacts,
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame
|
||||
};
|
||||
return updateContacts.Schedule(((BurstSolverImpl)constraints.solver).colliderContacts.Length, 128, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
var projectConstraints = new CollisionConstraintsBatchJob()
|
||||
{
|
||||
positions = solverImplementation.positions,
|
||||
prevPositions = solverImplementation.prevPositions,
|
||||
orientations = solverImplementation.orientations,
|
||||
prevOrientations = solverImplementation.prevOrientations,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
radii = solverImplementation.principalRadii,
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
|
||||
shapes = ObiColliderWorld.GetInstance().colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
transforms = ObiColliderWorld.GetInstance().colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
rigidbodies = ObiColliderWorld.GetInstance().rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
rigidbodyLinearDeltas = solverImplementation.abstraction.rigidbodyLinearDeltas.AsNativeArray<float4>(),
|
||||
rigidbodyAngularDeltas = solverImplementation.abstraction.rigidbodyAngularDeltas.AsNativeArray<float4>(),
|
||||
|
||||
deltas = solverAbstraction.positionDeltas.AsNativeArray<float4>(),
|
||||
counts = solverAbstraction.positionConstraintCounts.AsNativeArray<int>(),
|
||||
|
||||
contacts = ((BurstSolverImpl)constraints.solver).colliderContacts,
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
|
||||
constraintParameters = parameters,
|
||||
solverParameters = solverAbstraction.parameters,
|
||||
substeps = substeps,
|
||||
stepTime = stepTime,
|
||||
substepTime = substepTime
|
||||
};
|
||||
return projectConstraints.Schedule(inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
var applyConstraints = new ApplyCollisionConstraintsBatchJob()
|
||||
{
|
||||
contacts = ((BurstSolverImpl)constraints.solver).colliderContacts,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
|
||||
positions = solverImplementation.positions,
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
orientations = solverImplementation.orientations,
|
||||
orientationDeltas = solverImplementation.orientationDeltas,
|
||||
orientationCounts = solverImplementation.orientationConstraintCounts,
|
||||
constraintParameters = parameters
|
||||
};
|
||||
|
||||
return applyConstraints.Schedule(inputDeps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates contact data (such as contact distance) at the beginning of each substep. This is
|
||||
* necessary because contacts are generalted only once at the beginning of each step, not every substep.
|
||||
*/
|
||||
[BurstCompile]
|
||||
public struct UpdateContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<quaternion> prevOrientations;
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> invInertiaTensors;
|
||||
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
[ReadOnly] public NativeArray<float4> rigidbodyLinearDeltas;
|
||||
[ReadOnly] public NativeArray<float4> rigidbodyAngularDeltas;
|
||||
|
||||
public NativeArray<BurstContact> contacts;
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
var contact = contacts[i];
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);
|
||||
|
||||
// get the material from the first particle in the simplex:
|
||||
int aMaterialIndex = particleMaterialIndices[simplices[simplexStart]];
|
||||
bool rollingContacts = aMaterialIndex >= 0 ? collisionMaterials[aMaterialIndex].rollingContacts > 0 : false;
|
||||
|
||||
float4 relativeVelocity = float4.zero;
|
||||
float4 simplexPrevPosition = float4.zero;
|
||||
quaternion simplexPrevOrientation = new quaternion(0, 0, 0, 0);
|
||||
float simplexInvMass = 0;
|
||||
float4 simplexInvInertia = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
relativeVelocity += velocities[particleIndex] * contact.pointA[j];
|
||||
simplexPrevPosition += prevPositions[particleIndex] * contact.pointA[j];
|
||||
simplexPrevOrientation.value += prevOrientations[particleIndex].value * contact.pointA[j];
|
||||
simplexInvMass += invMasses[particleIndex] * contact.pointA[j];
|
||||
simplexInvInertia += invInertiaTensors[particleIndex] * contact.pointA[j];
|
||||
simplexRadius += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
|
||||
}
|
||||
|
||||
// if there's a rigidbody present, subtract its velocity from the relative velocity:
|
||||
int rigidbodyIndex = shapes[contact.bodyB].rigidbodyIndex;
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// update contact distance
|
||||
contact.distance = math.dot(simplexPrevPosition - contact.pointB, contact.normal) - simplexRadius;
|
||||
|
||||
// calculate contact point in A's surface:
|
||||
float4 contactPoint = contact.pointB + contact.normal * contact.distance;
|
||||
|
||||
// update contact orthonormal basis:
|
||||
contact.CalculateBasis(relativeVelocity);
|
||||
|
||||
// calculate A's contact mass.
|
||||
contact.CalculateContactMassesA(simplexInvMass, simplexInvInertia, simplexPrevPosition, simplexPrevOrientation, contactPoint, rollingContacts);
|
||||
|
||||
// calculate B's contact mass.
|
||||
if (rigidbodyIndex >= 0)
|
||||
contact.CalculateContactMassesB(rigidbodies[rigidbodyIndex], inertialFrame.frame);
|
||||
|
||||
contacts[i] = contact;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct CollisionConstraintsBatchJob : IJob
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<quaternion> prevOrientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
public NativeArray<float4> rigidbodyLinearDeltas;
|
||||
public NativeArray<float4> rigidbodyAngularDeltas;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
public NativeArray<BurstContact> contacts;
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
[ReadOnly] public Oni.ConstraintParameters constraintParameters;
|
||||
[ReadOnly] public Oni.SolverParameters solverParameters;
|
||||
[ReadOnly] public float stepTime;
|
||||
[ReadOnly] public float substepTime;
|
||||
[ReadOnly] public int substeps;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
for (int i = 0; i < contacts.Length; ++i)
|
||||
{
|
||||
var contact = contacts[i];
|
||||
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);
|
||||
int colliderIndex = contact.bodyB;
|
||||
|
||||
// Skip contacts involving triggers:
|
||||
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;
|
||||
|
||||
// Combine collision materials (use material from first particle in simplex)
|
||||
BurstCollisionMaterial material = CombineCollisionMaterials(simplices[simplexStart], colliderIndex);
|
||||
|
||||
// Get relative velocity at contact point.
|
||||
// As we do not consider true ellipses for collision detection, particle contact points are never off-axis.
|
||||
// So particle angular velocity does not contribute to normal impulses, and we can skip it.
|
||||
float4 simplexPosition = float4.zero;
|
||||
float4 simplexPrevPosition = float4.zero;
|
||||
float simplexRadius = 0;
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
simplexPosition += positions[particleIndex] * contact.pointA[j];
|
||||
simplexPrevPosition += prevPositions[particleIndex] * contact.pointA[j];
|
||||
simplexRadius += BurstMath.EllipsoidRadius(contact.normal, orientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
|
||||
}
|
||||
|
||||
// project position to the end of the full step:
|
||||
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.frame) * stepTime;
|
||||
|
||||
// adhesion:
|
||||
float lambda = contact.SolveAdhesion(posA, posB, material.stickDistance, material.stickiness, stepTime);
|
||||
|
||||
// depenetration:
|
||||
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) / substeps;
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
deltas[particleIndex] += delta * invMasses[particleIndex] * contact.pointA[j];
|
||||
counts[particleIndex]++;
|
||||
}
|
||||
|
||||
// Apply position deltas immediately, if using sequential evaluation:
|
||||
if (constraintParameters.evaluationOrder == Oni.ConstraintParameters.EvaluationOrder.Sequential)
|
||||
{
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
BurstConstraintsBatchImpl.ApplyPositionDelta(particleIndex, constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
}
|
||||
}
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstMath.ApplyImpulse(rigidbodyIndex, -lambda / stepTime * contact.normal, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
}
|
||||
}
|
||||
|
||||
contacts[i] = contact;
|
||||
}
|
||||
}
|
||||
|
||||
private BurstCollisionMaterial CombineCollisionMaterials(int entityA, int entityB)
|
||||
{
|
||||
// Combine collision materials:
|
||||
int particleMaterialIndex = particleMaterialIndices[entityA];
|
||||
int colliderMaterialIndex = shapes[entityB].materialIndex;
|
||||
|
||||
if (colliderMaterialIndex >= 0 && particleMaterialIndex >= 0)
|
||||
return BurstCollisionMaterial.CombineWith(collisionMaterials[particleMaterialIndex], collisionMaterials[colliderMaterialIndex]);
|
||||
else if (particleMaterialIndex >= 0)
|
||||
return collisionMaterials[particleMaterialIndex];
|
||||
else if (colliderMaterialIndex >= 0)
|
||||
return collisionMaterials[colliderMaterialIndex];
|
||||
|
||||
return new BurstCollisionMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 273c5561da8534560bb9b9e8d32ae092
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstColliderFrictionConstraints : BurstConstraintsImpl<BurstColliderFrictionConstraintsBatch>
|
||||
{
|
||||
public BurstColliderFrictionConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.Friction)
|
||||
{
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstColliderFrictionConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstColliderFrictionConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
|
||||
public override int GetConstraintCount()
|
||||
{
|
||||
if (!((BurstSolverImpl)solver).colliderContacts.IsCreated)
|
||||
return 0;
|
||||
return ((BurstSolverImpl)solver).colliderContacts.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00f91403e75564dc6ace566943caa172
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,284 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstColliderFrictionConstraintsBatch : BurstConstraintsBatchImpl, IColliderFrictionConstraintsBatchImpl
|
||||
{
|
||||
public BurstColliderFrictionConstraintsBatch(BurstColliderFrictionConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.Friction;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
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,
|
||||
prevPositions = solverImplementation.prevPositions,
|
||||
orientations = solverImplementation.orientations,
|
||||
prevOrientations = solverImplementation.prevOrientations,
|
||||
|
||||
invMasses = solverImplementation.invMasses,
|
||||
invInertiaTensors = solverImplementation.invInertiaTensors,
|
||||
radii = solverImplementation.principalRadii,
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
|
||||
shapes = ObiColliderWorld.GetInstance().colliderShapes.AsNativeArray<BurstColliderShape>(),
|
||||
transforms = ObiColliderWorld.GetInstance().colliderTransforms.AsNativeArray<BurstAffineTransform>(),
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
rigidbodies = ObiColliderWorld.GetInstance().rigidbodies.AsNativeArray<BurstRigidbody>(),
|
||||
rigidbodyLinearDeltas = solverImplementation.abstraction.rigidbodyLinearDeltas.AsNativeArray<float4>(),
|
||||
rigidbodyAngularDeltas = solverImplementation.abstraction.rigidbodyAngularDeltas.AsNativeArray<float4>(),
|
||||
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
orientationDeltas = solverImplementation.orientationDeltas,
|
||||
orientationCounts = solverImplementation.orientationConstraintCounts,
|
||||
|
||||
contacts = ((BurstSolverImpl)constraints.solver).colliderContacts,
|
||||
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
|
||||
substeps = substeps,
|
||||
stepTime = stepTime,
|
||||
substepTime = substepTime
|
||||
};
|
||||
return projectConstraints.Schedule(inputDeps);
|
||||
}
|
||||
|
||||
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).colliderContacts,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
|
||||
positions = solverImplementation.positions,
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
orientations = solverImplementation.orientations,
|
||||
orientationDeltas = solverImplementation.orientationDeltas,
|
||||
orientationCounts = solverImplementation.orientationConstraintCounts,
|
||||
constraintParameters = parameters
|
||||
};
|
||||
|
||||
return applyConstraints.Schedule(inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct FrictionConstraintsBatchJob : IJob
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<quaternion> prevOrientations;
|
||||
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> invInertiaTensors;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
|
||||
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
|
||||
public NativeArray<float4> rigidbodyLinearDeltas;
|
||||
public NativeArray<float4> rigidbodyAngularDeltas;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<quaternion> orientationDeltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> orientationCounts;
|
||||
|
||||
public NativeArray<BurstContact> contacts;
|
||||
[ReadOnly] public BurstInertialFrame inertialFrame;
|
||||
[ReadOnly] public float stepTime;
|
||||
[ReadOnly] public float substepTime;
|
||||
[ReadOnly] public int substeps;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
for (int i = 0; i < contacts.Length; ++i)
|
||||
{
|
||||
var contact = contacts[i];
|
||||
|
||||
// Get the indices of the particle and collider involved in this contact:
|
||||
int simplexStart = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);
|
||||
int colliderIndex = contact.bodyB;
|
||||
|
||||
// Skip contacts involving triggers:
|
||||
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;
|
||||
|
||||
// Combine collision materials (use material from first particle in simplex)
|
||||
BurstCollisionMaterial material = CombineCollisionMaterials(simplices[simplexStart], colliderIndex);
|
||||
|
||||
// Calculate relative velocity:
|
||||
float4 rA = float4.zero, rB = float4.zero;
|
||||
|
||||
float4 prevPositionA = float4.zero;
|
||||
float4 linearVelocityA = float4.zero;
|
||||
float4 angularVelocityA = float4.zero;
|
||||
float4 invInertiaTensorA = float4.zero;
|
||||
quaternion orientationA = new quaternion(0, 0, 0, 0);
|
||||
float simplexRadiusA = 0;
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
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];
|
||||
invInertiaTensorA += invInertiaTensors[particleIndex] * contact.pointA[j];
|
||||
orientationA.value += orientations[particleIndex].value * 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 * simplexRadiusA;
|
||||
relativeVelocity += new float4(math.cross(angularVelocityA.xyz, rA.xyz), 0);
|
||||
}
|
||||
|
||||
// Subtract rigidbody velocity:
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
// 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.frame);
|
||||
}
|
||||
|
||||
// Determine impulse magnitude:
|
||||
float2 impulses = contact.SolveFriction(relativeVelocity, material.staticFriction, material.dynamicFriction, stepTime);
|
||||
|
||||
if (math.abs(impulses.x) > BurstMath.epsilon || math.abs(impulses.y) > BurstMath.epsilon)
|
||||
{
|
||||
float4 tangentImpulse = impulses.x * contact.tangent;
|
||||
float4 bitangentImpulse = impulses.y * contact.bitangent;
|
||||
float4 totalImpulse = tangentImpulse + bitangentImpulse;
|
||||
|
||||
float baryScale = BurstMath.BaryScale(contact.pointA);
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
//(tangentImpulse * contact.tangentInvMassA + bitangentImpulse * contact.bitangentInvMassA) * dt;
|
||||
deltas[particleIndex] += (tangentImpulse * contact.tangentInvMassA + bitangentImpulse * contact.bitangentInvMassA) * substepTime * contact.pointA[j] * baryScale;
|
||||
counts[particleIndex]++;
|
||||
}
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
BurstMath.ApplyImpulse(rigidbodyIndex, -totalImpulse, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame.frame);
|
||||
}
|
||||
|
||||
// Rolling contacts:
|
||||
if (material.rollingContacts > 0)
|
||||
{
|
||||
// Calculate angular velocity deltas due to friction impulse:
|
||||
float4x4 solverInertiaA = BurstMath.TransformInertiaTensor(invInertiaTensorA, orientationA);
|
||||
|
||||
float4 angVelDeltaA = math.mul(solverInertiaA, new float4(math.cross(rA.xyz, totalImpulse.xyz), 0));
|
||||
float4 angVelDeltaB = float4.zero;
|
||||
|
||||
// Final angular velocities, after adding the deltas:
|
||||
angularVelocityA += angVelDeltaA;
|
||||
float4 angularVelocityB = float4.zero;
|
||||
|
||||
// Calculate weights (inverse masses):
|
||||
float invMassA = math.length(math.mul(solverInertiaA, math.normalizesafe(angularVelocityA)));
|
||||
float invMassB = 0;
|
||||
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
angVelDeltaB = math.mul(-rigidbodies[rigidbodyIndex].inverseInertiaTensor, new float4(math.cross(rB.xyz, totalImpulse.xyz), 0));
|
||||
angularVelocityB = rigidbodies[rigidbodyIndex].angularVelocity + angVelDeltaB;
|
||||
invMassB = math.length(math.mul(rigidbodies[rigidbodyIndex].inverseInertiaTensor, math.normalizesafe(angularVelocityB)));
|
||||
}
|
||||
|
||||
// Calculate rolling axis and angular velocity deltas:
|
||||
float4 rollAxis = float4.zero;
|
||||
float rollingImpulse = contact.SolveRollingFriction(angularVelocityA, angularVelocityB, material.rollingFriction, invMassA, invMassB, ref rollAxis);
|
||||
angVelDeltaA += rollAxis * rollingImpulse * invMassA;
|
||||
angVelDeltaB -= rollAxis * rollingImpulse * invMassB;
|
||||
|
||||
// Apply orientation delta to particles:
|
||||
quaternion orientationDelta = BurstIntegration.AngularVelocityToSpinQuaternion(orientationA, angVelDeltaA, substepTime);
|
||||
|
||||
for (int j = 0; j < simplexSize; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStart + j];
|
||||
quaternion qA = orientationDeltas[particleIndex];
|
||||
qA.value += orientationDelta.value;
|
||||
orientationDeltas[particleIndex] = qA;
|
||||
orientationCounts[particleIndex]++;
|
||||
}
|
||||
|
||||
// Apply angular velocity delta to rigidbody:
|
||||
if (rigidbodyIndex >= 0)
|
||||
{
|
||||
float4 angularDelta = rigidbodyAngularDeltas[rigidbodyIndex];
|
||||
angularDelta += angVelDeltaB;
|
||||
rigidbodyAngularDeltas[rigidbodyIndex] = angularDelta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contacts[i] = contact;
|
||||
}
|
||||
}
|
||||
|
||||
private BurstCollisionMaterial CombineCollisionMaterials(int entityA, int entityB)
|
||||
{
|
||||
// Combine collision materials:
|
||||
int particleMaterialIndex = particleMaterialIndices[entityA];
|
||||
int colliderMaterialIndex = shapes[entityB].materialIndex;
|
||||
|
||||
if (colliderMaterialIndex >= 0 && particleMaterialIndex >= 0)
|
||||
return BurstCollisionMaterial.CombineWith(collisionMaterials[particleMaterialIndex], collisionMaterials[colliderMaterialIndex]);
|
||||
else if (particleMaterialIndex >= 0)
|
||||
return collisionMaterials[particleMaterialIndex];
|
||||
else if (colliderMaterialIndex >= 0)
|
||||
return collisionMaterials[colliderMaterialIndex];
|
||||
|
||||
return new BurstCollisionMaterial();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9abf9b1d2ee1a4d09b965103ae78fe53
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d2e0de656b27431f9501fe6ffacff67
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,452 @@
|
||||
#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.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstDensityConstraints : BurstConstraintsImpl<BurstDensityConstraintsBatch>
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstDensityConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
fluidParticles.Dispose();
|
||||
}
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstDensityConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
|
||||
protected override JobHandle EvaluateSequential(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
return EvaluateParallel(inputDeps, stepTime, substepTime, substeps);
|
||||
}
|
||||
|
||||
protected override JobHandle EvaluateParallel(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
inputDeps = UpdateInteractions(inputDeps);
|
||||
|
||||
// evaluate all batches as a chain of dependencies:
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].Evaluate(inputDeps, stepTime, substepTime, substeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
// calculate per-particle lambdas:
|
||||
inputDeps = CalculateLambdas(inputDeps, substepTime);
|
||||
|
||||
// then apply them:
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].Apply(inputDeps, substepTime);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
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].CalculateViscosityAndNormals(inputDeps, deltaTime);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].CalculateVorticity(inputDeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
inputDeps = ApplyVorticityAndAtmosphere(inputDeps, deltaTime);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public JobHandle CalculateAnisotropyLaplacianSmoothing(JobHandle inputDeps)
|
||||
{
|
||||
// if the constraints are deactivated or we need no anisotropy:
|
||||
if (((BurstSolverImpl)solver).abstraction.parameters.maxAnisotropy <= 1)
|
||||
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)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].AccumulateSmoothPositions(inputDeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
inputDeps = AverageSmoothPositions(inputDeps);
|
||||
|
||||
for (int i = 0; i < batches.Count; ++i)
|
||||
{
|
||||
if (batches[i].enabled)
|
||||
{
|
||||
inputDeps = batches[i].AccumulateAnisotropy(inputDeps);
|
||||
m_Solver.ScheduleBatchedJobsIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
return AverageAnisotropy(inputDeps);
|
||||
}
|
||||
|
||||
private JobHandle UpdateInteractions(JobHandle inputDeps)
|
||||
{
|
||||
// clear existing fluid data:
|
||||
var clearData = new ClearFluidDataJob()
|
||||
{
|
||||
fluidParticles = fluidParticles.AsDeferredJobArray(),
|
||||
fluidData = ((BurstSolverImpl)solver).abstraction.fluidData.AsNativeArray<float4>(),
|
||||
};
|
||||
|
||||
inputDeps = clearData.Schedule(fluidParticles.Length, 64, inputDeps);
|
||||
|
||||
// update fluid interactions:
|
||||
var updateInteractions = new UpdateInteractionsJob()
|
||||
{
|
||||
pairs = m_Solver.fluidInteractions,
|
||||
positions = m_Solver.positions,
|
||||
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),
|
||||
};
|
||||
|
||||
return updateInteractions.Schedule(((BurstSolverImpl)solver).fluidInteractions.Length, 64, inputDeps);
|
||||
}
|
||||
|
||||
private JobHandle CalculateLambdas(JobHandle inputDeps, float deltaTime)
|
||||
{
|
||||
// calculate lagrange multipliers:
|
||||
var calculateLambdas = new CalculateLambdasJob()
|
||||
{
|
||||
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),
|
||||
normals = m_Solver.normals,
|
||||
vorticity = m_Solver.vorticities,
|
||||
fluidData = m_Solver.fluidData
|
||||
};
|
||||
|
||||
return calculateLambdas.Schedule(fluidParticles.Length,64,inputDeps);
|
||||
}
|
||||
|
||||
private JobHandle ApplyVorticityAndAtmosphere(JobHandle inputDeps, float deltaTime)
|
||||
{
|
||||
// calculate lagrange multipliers:
|
||||
var conf = new ApplyVorticityConfinementAndAtmosphere()
|
||||
{
|
||||
fluidParticles = fluidParticles.AsDeferredJobArray(),
|
||||
wind = m_Solver.wind,
|
||||
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,
|
||||
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.AsDeferredJobArray(),
|
||||
renderablePositions = m_Solver.renderablePositions,
|
||||
smoothPositions = smoothPositions
|
||||
};
|
||||
|
||||
return average.Schedule(fluidParticles.Length, 64, inputDeps);
|
||||
}
|
||||
|
||||
private JobHandle AverageAnisotropy(JobHandle inputDeps)
|
||||
{
|
||||
var average = new AverageAnisotropyJob()
|
||||
{
|
||||
fluidParticles = fluidParticles.AsDeferredJobArray(),
|
||||
renderablePositions = m_Solver.renderablePositions,
|
||||
smoothPositions = smoothPositions,
|
||||
principalRadii = m_Solver.principalRadii,
|
||||
anisotropies = anisotropies,
|
||||
maxAnisotropy = m_Solver.abstraction.parameters.maxAnisotropy,
|
||||
principalAxes = m_Solver.anisotropies
|
||||
};
|
||||
|
||||
return average.Schedule(fluidParticles.Length, 64, inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ClearFluidDataJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
fluidData[fluidParticles[i]] = float4.zero;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct UpdateInteractionsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float> radii;
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
[ReadOnly] public SpikyKernel gradientKernel;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
// calculate normalized gradient vector:
|
||||
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, radii[pair.particleA]) +
|
||||
densityKernel.W(distance, radii[pair.particleB])) * 0.5f;
|
||||
|
||||
pair.avgGradient = (gradientKernel.W(distance, radii[pair.particleA]) +
|
||||
gradientKernel.W(distance, radii[pair.particleB])) * 0.5f;
|
||||
|
||||
pairs[i] = pair;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct CalculateLambdasJob : IJobParallelFor
|
||||
{
|
||||
[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;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> normals;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> vorticity;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
normals[i] = float4.zero;
|
||||
vorticity[i] = float4.zero;
|
||||
|
||||
float4 data = fluidData[i];
|
||||
|
||||
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 ApplyVorticityConfinementAndAtmosphere : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float4> wind;
|
||||
[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;
|
||||
|
||||
[DeallocateOnJobCompletion] [ReadOnly] public NativeArray<float4> eta;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> velocities;
|
||||
|
||||
[ReadOnly] public float dt;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
//atmospheric drag:
|
||||
float4 velocityDiff = velocities[i] - wind[i];
|
||||
|
||||
// particles near the surface should experience drag:
|
||||
velocities[i] -= atmosphericDrag[i] * velocityDiff * math.max(0, 1 - fluidData[i][0] / restDensities[i]) * dt;
|
||||
|
||||
// ambient pressure:
|
||||
velocities[i] += atmosphericPressure[i] * normals[i] * dt;
|
||||
|
||||
// apply vorticity confinement:
|
||||
velocities[i] += new float4(math.cross(math.normalizesafe(eta[i]).xyz,vorticities[i].xyz), 0) * vorticityConfinement[i] * dt;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct IdentityAnisotropyJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> principalAxes;
|
||||
|
||||
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 NativeArray<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> smoothPositions;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
if (smoothPositions[i].w > 0)
|
||||
smoothPositions[i] /= smoothPositions[i].w;
|
||||
else
|
||||
smoothPositions[i] = renderablePositions[i];
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct AverageAnisotropyJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> fluidParticles;
|
||||
[ReadOnly] public NativeArray<float4> principalRadii;
|
||||
[ReadOnly] public float maxAnisotropy;
|
||||
|
||||
[ReadOnly]
|
||||
[DeallocateOnJobCompletion]
|
||||
public NativeArray<float4> smoothPositions;
|
||||
|
||||
[ReadOnly]
|
||||
[DeallocateOnJobCompletion]
|
||||
public NativeArray<float3x3> anisotropies;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> renderablePositions;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> principalAxes;
|
||||
|
||||
public void Execute(int p)
|
||||
{
|
||||
int i = fluidParticles[p];
|
||||
|
||||
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(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;
|
||||
|
||||
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;
|
||||
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] = smoothPositions[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df96ac4db14c846ea99a6ebc5771098f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,371 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstDensityConstraintsBatch : BurstConstraintsBatchImpl, IDensityConstraintsBatchImpl
|
||||
{
|
||||
public BatchData batchData;
|
||||
|
||||
public BurstDensityConstraintsBatch(BurstDensityConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.Density;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
return inputDeps;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
|
||||
// update densities and gradients:
|
||||
var updateDensities = new UpdateDensitiesJob()
|
||||
{
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
positions = solverImplementation.positions,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
restDensities = solverImplementation.restDensities,
|
||||
diffusion = solverImplementation.diffusion,
|
||||
userData = solverImplementation.userData,
|
||||
fluidData = solverImplementation.fluidData,
|
||||
batchData = batchData,
|
||||
dt = substepTime
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return updateDensities.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
// update densities and gradients:
|
||||
var apply = new ApplyDensityConstraintsJob()
|
||||
{
|
||||
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,
|
||||
sorFactor = parameters.SORFactor
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return apply.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public JobHandle CalculateViscosityAndNormals(JobHandle inputDeps, float deltaTime)
|
||||
{
|
||||
var viscosity = new NormalsViscosityAndVorticityJob()
|
||||
{
|
||||
positions = solverImplementation.positions,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
radii = solverImplementation.smoothingRadii,
|
||||
restDensities = solverImplementation.restDensities,
|
||||
viscosities = solverImplementation.viscosities,
|
||||
fluidData = solverImplementation.fluidData,
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
velocities = solverImplementation.velocities,
|
||||
vorticities = solverImplementation.vorticities,
|
||||
normals = solverImplementation.normals,
|
||||
batchData = batchData
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return viscosity.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public JobHandle CalculateVorticity(JobHandle inputDeps)
|
||||
{
|
||||
var eta = new CalculateVorticityEta()
|
||||
{
|
||||
invMasses = solverImplementation.invMasses,
|
||||
restDensities = solverImplementation.restDensities,
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
vorticities = solverImplementation.vorticities,
|
||||
eta = ((BurstDensityConstraints)this.constraints).eta,
|
||||
batchData = batchData
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return eta.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public JobHandle AccumulateSmoothPositions(JobHandle inputDeps)
|
||||
{
|
||||
var accumulateSmooth = new AccumulateSmoothPositionsJob()
|
||||
{
|
||||
renderablePositions = solverImplementation.renderablePositions,
|
||||
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
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return accumulateSmooth.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public JobHandle AccumulateAnisotropy(JobHandle inputDeps)
|
||||
{
|
||||
var accumulateAnisotropy = new AccumulateAnisotropyJob()
|
||||
{
|
||||
renderablePositions = solverImplementation.renderablePositions,
|
||||
smoothPositions = ((BurstDensityConstraints)this.constraints).smoothPositions,
|
||||
anisotropies = ((BurstDensityConstraints)this.constraints).anisotropies,
|
||||
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
|
||||
batchData = batchData
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return accumulateAnisotropy.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct UpdateDensitiesJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> restDensities;
|
||||
[ReadOnly] public NativeArray<float> diffusion;
|
||||
[ReadOnly] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> userData;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
|
||||
|
||||
[ReadOnly] public BatchData batchData;
|
||||
|
||||
[ReadOnly] public float dt;
|
||||
|
||||
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];
|
||||
|
||||
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);
|
||||
|
||||
// property diffusion:
|
||||
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:
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct CalculateVorticityEta : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> vorticities;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float> restDensities;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<FluidInteraction> pairs;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> eta;
|
||||
|
||||
[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];
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct AccumulateSmoothPositionsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
[ReadOnly] public NativeArray<float> radii;
|
||||
[ReadOnly] public Poly6Kernel densityKernel;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> smoothPositions;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[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];
|
||||
|
||||
float4 gradient = (renderablePositions[pair.particleA] - renderablePositions[pair.particleB]);
|
||||
float distance = math.length(gradient);
|
||||
|
||||
pair.avgKernel = (densityKernel.W(distance, radii[pair.particleA]) +
|
||||
densityKernel.W(distance, radii[pair.particleB])) * 0.5f;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct AccumulateAnisotropyJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> renderablePositions;
|
||||
[ReadOnly] public NativeArray<float4> smoothPositions;
|
||||
[ReadOnly] public NativeArray<FluidInteraction> pairs;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float3x3> anisotropies;
|
||||
|
||||
[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];
|
||||
|
||||
float4 distanceA = renderablePositions[pair.particleB] - smoothPositions[pair.particleA];
|
||||
float4 distanceB = renderablePositions[pair.particleA] - smoothPositions[pair.particleB];
|
||||
|
||||
anisotropies[pair.particleA] += BurstMath.multrnsp(distanceA,distanceA) * pair.avgKernel;
|
||||
anisotropies[pair.particleB] += BurstMath.multrnsp(distanceB,distanceB) * pair.avgKernel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8292e6ef6129f47abaee4fb2cb49055e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a128a7c745c84794a944362f49011fc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct Poly6Kernel
|
||||
{
|
||||
public float norm;
|
||||
public bool norm2D;
|
||||
|
||||
public Poly6Kernel(bool norm2D)
|
||||
{
|
||||
this.norm2D = norm2D;
|
||||
if (norm2D)
|
||||
norm = 4.0f / math.PI;
|
||||
else
|
||||
norm = 315.0f / (64.0f * math.PI);
|
||||
}
|
||||
|
||||
public float W(float r, float h)
|
||||
{
|
||||
float h2 = h * h;
|
||||
float h4 = h2 * h2;
|
||||
float h8 = h4 * h4;
|
||||
|
||||
float rl = math.min(r, h);
|
||||
float hr = h2 - rl * rl;
|
||||
|
||||
if (norm2D)
|
||||
return norm / h8 * hr * hr * hr;
|
||||
return norm / (h8 * h) * hr * hr * hr;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a3990134524143ac852b488554f6d4e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct SpikyKernel
|
||||
{
|
||||
public float norm;
|
||||
public bool norm2D;
|
||||
|
||||
public SpikyKernel(bool norm2D)
|
||||
{
|
||||
this.norm2D = norm2D;
|
||||
if (norm2D)
|
||||
norm = -30.0f / math.PI;
|
||||
else
|
||||
norm = -45.0f / math.PI;
|
||||
}
|
||||
|
||||
public float W(float r, float h)
|
||||
{
|
||||
float h2 = h * h;
|
||||
float h4 = h2 * h2;
|
||||
|
||||
float rl = math.min(r, h);
|
||||
float hr = h - rl;
|
||||
|
||||
if (norm2D)
|
||||
return norm / (h4 * h) * hr * hr;
|
||||
return norm / (h4 * h2) * hr * hr;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a407989bfa0664e9ab75773d1808f549
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 725436c3a327442cd8b06889111f9b8f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstDistanceConstraints : BurstConstraintsImpl<BurstDistanceConstraintsBatch>
|
||||
{
|
||||
public BurstDistanceConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.Distance)
|
||||
{
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstDistanceConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstDistanceConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af06abf9c29a04274842dbcb65a2a753
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,146 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstDistanceConstraintsBatch : BurstConstraintsBatchImpl, IDistanceConstraintsBatchImpl
|
||||
{
|
||||
private NativeArray<float> restLengths;
|
||||
private NativeArray<float2> stiffnesses;
|
||||
|
||||
DistanceConstraintsBatchJob projectConstraints;
|
||||
ApplyDistanceConstraintsBatchJob applyConstraints;
|
||||
|
||||
public BurstDistanceConstraintsBatch(BurstDistanceConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.Distance;
|
||||
}
|
||||
|
||||
public void SetDistanceConstraints(ObiNativeIntList particleIndices, ObiNativeFloatList restLengths, ObiNativeVector2List stiffnesses, ObiNativeFloatList lambdas, int count)
|
||||
{
|
||||
this.particleIndices = particleIndices.AsNativeArray<int>();
|
||||
this.restLengths = restLengths.AsNativeArray<float>();
|
||||
this.stiffnesses = stiffnesses.AsNativeArray<float2>();
|
||||
this.lambdas = lambdas.AsNativeArray<float>();
|
||||
m_ConstraintCount = count;
|
||||
|
||||
projectConstraints.particleIndices = this.particleIndices;
|
||||
projectConstraints.restLengths = this.restLengths;
|
||||
projectConstraints.stiffnesses = this.stiffnesses;
|
||||
projectConstraints.lambdas = this.lambdas;
|
||||
|
||||
applyConstraints.particleIndices = this.particleIndices;
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
projectConstraints.positions = solverImplementation.positions;
|
||||
projectConstraints.invMasses = solverImplementation.invMasses;
|
||||
projectConstraints.deltas = solverImplementation.positionDeltas;
|
||||
projectConstraints.counts = solverImplementation.positionConstraintCounts;
|
||||
projectConstraints.deltaTimeSqr = substepTime * substepTime;
|
||||
|
||||
return projectConstraints.Schedule(m_ConstraintCount, 32, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
applyConstraints.positions = solverImplementation.positions;
|
||||
applyConstraints.deltas = solverImplementation.positionDeltas;
|
||||
applyConstraints.counts = solverImplementation.positionConstraintCounts;
|
||||
applyConstraints.sorFactor = parameters.SORFactor;
|
||||
|
||||
return applyConstraints.Schedule(m_ConstraintCount, 64, inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct DistanceConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public NativeArray<float> restLengths;
|
||||
[ReadOnly] public NativeArray<float2> stiffnesses;
|
||||
public NativeArray<float> lambdas;
|
||||
|
||||
[ReadOnly] public NativeArray<float4> positions;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
[ReadOnly] public float deltaTimeSqr;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int p1 = particleIndices[i * 2];
|
||||
int p2 = particleIndices[i * 2 + 1];
|
||||
|
||||
float w1 = invMasses[p1];
|
||||
float w2 = invMasses[p2];
|
||||
|
||||
// calculate time adjusted compliance
|
||||
float compliance = stiffnesses[i].x / deltaTimeSqr;
|
||||
|
||||
// calculate position and lambda deltas:
|
||||
float4 distance = positions[p1] - positions[p2];
|
||||
float d = math.length(distance);
|
||||
|
||||
// calculate constraint value:
|
||||
float constraint = d - restLengths[i];
|
||||
constraint -= math.max(math.min(constraint, 0), -stiffnesses[i].y);
|
||||
|
||||
// calculate lambda and position deltas:
|
||||
float dlambda = (-constraint - compliance * lambdas[i]) / (w1 + w2 + compliance + BurstMath.epsilon);
|
||||
float4 delta = dlambda * distance / (d + BurstMath.epsilon);
|
||||
|
||||
lambdas[i] += dlambda;
|
||||
|
||||
deltas[p1] += delta * w1;
|
||||
deltas[p2] -= delta * w2;
|
||||
|
||||
counts[p1]++;
|
||||
counts[p2]++;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ApplyDistanceConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<int> particleIndices;
|
||||
[ReadOnly] public float sorFactor;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
int p1 = particleIndices[i * 2];
|
||||
int p2 = particleIndices[i * 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
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31ee5bf24da6847f0b93a69dbbdecf25
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66103ca5acc86407cab9aa94d1e6d1fd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,62 @@
|
||||
#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]
|
||||
public struct ApplyBatchedCollisionConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<BurstContact> contacts;
|
||||
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
|
||||
[NativeDisableParallelForRestriction] public NativeArray<quaternion> orientations;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<quaternion> orientationDeltas;
|
||||
[NativeDisableParallelForRestriction] public NativeArray<int> orientationCounts;
|
||||
|
||||
[ReadOnly] public Oni.ConstraintParameters constraintParameters;
|
||||
[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)
|
||||
{
|
||||
int simplexStartA = simplexCounts.GetSimplexStartAndSize(contacts[i].bodyA, out int simplexSizeA);
|
||||
int simplexStartB = simplexCounts.GetSimplexStartAndSize(contacts[i].bodyB, out int simplexSizeB);
|
||||
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartA + j];
|
||||
BurstConstraintsBatchImpl.ApplyPositionDelta(particleIndex, constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
BurstConstraintsBatchImpl.ApplyOrientationDelta(particleIndex, constraintParameters.SORFactor, ref orientations, ref orientationDeltas, ref orientationCounts);
|
||||
}
|
||||
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartB + j];
|
||||
BurstConstraintsBatchImpl.ApplyPositionDelta(particleIndex, constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
BurstConstraintsBatchImpl.ApplyOrientationDelta(particleIndex, constraintParameters.SORFactor, ref orientations, ref orientationDeltas, ref orientationCounts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96623cff9b30e4504a4a6dbfe3d19ace
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstParticleCollisionConstraints : BurstConstraintsImpl<BurstParticleCollisionConstraintsBatch>
|
||||
{
|
||||
public BurstParticleCollisionConstraints(BurstSolverImpl solver) : base(solver, Oni.ConstraintType.ParticleCollision)
|
||||
{
|
||||
}
|
||||
|
||||
public override IConstraintsBatchImpl CreateConstraintsBatch()
|
||||
{
|
||||
var dataBatch = new BurstParticleCollisionConstraintsBatch(this);
|
||||
batches.Add(dataBatch);
|
||||
return dataBatch;
|
||||
}
|
||||
|
||||
public override void RemoveBatch(IConstraintsBatchImpl batch)
|
||||
{
|
||||
batches.Remove(batch as BurstParticleCollisionConstraintsBatch);
|
||||
batch.Destroy();
|
||||
}
|
||||
|
||||
public override int GetConstraintCount()
|
||||
{
|
||||
if (!((BurstSolverImpl)solver).particleContacts.IsCreated)
|
||||
return 0;
|
||||
return ((BurstSolverImpl)solver).particleContacts.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ef36a05943714c1db8f9ee0b28e9c51
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,335 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using UnityEngine;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class BurstParticleCollisionConstraintsBatch : BurstConstraintsBatchImpl, IParticleCollisionConstraintsBatchImpl
|
||||
{
|
||||
public BatchData batchData;
|
||||
|
||||
public BurstParticleCollisionConstraintsBatch(BurstParticleCollisionConstraints constraints)
|
||||
{
|
||||
m_Constraints = constraints;
|
||||
m_ConstraintType = Oni.ConstraintType.ParticleCollision;
|
||||
}
|
||||
|
||||
public BurstParticleCollisionConstraintsBatch(BatchData batchData) : base()
|
||||
{
|
||||
this.batchData = batchData;
|
||||
}
|
||||
|
||||
public override JobHandle Initialize(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var updateContacts = new UpdateParticleContactsJob()
|
||||
{
|
||||
prevPositions = solverImplementation.prevPositions,
|
||||
prevOrientations = solverImplementation.prevOrientations,
|
||||
velocities = solverImplementation.velocities,
|
||||
radii = solverImplementation.principalRadii,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
invInertiaTensors = solverImplementation.invInertiaTensors,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
|
||||
contacts = ((BurstSolverImpl)constraints.solver).particleContacts,
|
||||
batchData = batchData
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return updateContacts.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int substeps)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
var projectConstraints = new ParticleCollisionConstraintsBatchJob()
|
||||
{
|
||||
positions = solverImplementation.positions,
|
||||
orientations = solverImplementation.orientations,
|
||||
invMasses = solverImplementation.invMasses,
|
||||
radii = solverImplementation.principalRadii,
|
||||
particleMaterialIndices = solverImplementation.collisionMaterials,
|
||||
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
contacts = ((BurstSolverImpl)constraints.solver).particleContacts,
|
||||
batchData = batchData,
|
||||
|
||||
constraintParameters = parameters,
|
||||
solverParameters = solverImplementation.abstraction.parameters,
|
||||
gravity = new float4(solverImplementation.abstraction.parameters.gravity, 0),
|
||||
substepTime = substepTime,
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return projectConstraints.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
public override JobHandle Apply(JobHandle inputDeps, float substepTime)
|
||||
{
|
||||
var parameters = solverAbstraction.GetConstraintParameters(m_ConstraintType);
|
||||
|
||||
var applyConstraints = new ApplyBatchedCollisionConstraintsBatchJob()
|
||||
{
|
||||
contacts = ((BurstSolverImpl)constraints.solver).particleContacts,
|
||||
|
||||
simplices = solverImplementation.simplices,
|
||||
simplexCounts = solverImplementation.simplexCounts,
|
||||
|
||||
positions = solverImplementation.positions,
|
||||
deltas = solverImplementation.positionDeltas,
|
||||
counts = solverImplementation.positionConstraintCounts,
|
||||
orientations = solverImplementation.orientations,
|
||||
orientationDeltas = solverImplementation.orientationDeltas,
|
||||
orientationCounts = solverImplementation.orientationConstraintCounts,
|
||||
batchData = batchData,
|
||||
constraintParameters = parameters,
|
||||
};
|
||||
|
||||
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
|
||||
return applyConstraints.Schedule(batchData.workItemCount, batchCount, inputDeps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates contact data (contact distance and frame) at the beginning of each substep. This is
|
||||
* necessary because contacts are generated only once at the beginning of each step, not every substep.
|
||||
*/
|
||||
[BurstCompile]
|
||||
public struct UpdateParticleContactsJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<float4> prevPositions;
|
||||
[ReadOnly] public NativeArray<quaternion> prevOrientations;
|
||||
[ReadOnly] public NativeArray<float4> velocities;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> invInertiaTensors;
|
||||
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstContact> contacts;
|
||||
|
||||
[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 contact = contacts[i];
|
||||
|
||||
int simplexStartA = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSizeA);
|
||||
int simplexStartB = simplexCounts.GetSimplexStartAndSize(contact.bodyB, out int simplexSizeB);
|
||||
|
||||
float4 simplexVelocityA = float4.zero;
|
||||
float4 simplexPrevPositionA = float4.zero;
|
||||
quaternion simplexPrevOrientationA = new quaternion(0, 0, 0, 0);
|
||||
float simplexRadiusA = 0;
|
||||
float simplexInvMassA = 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;
|
||||
float4 simplexInvInertiaB = float4.zero;
|
||||
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartA + j];
|
||||
simplexVelocityA += velocities[particleIndex] * contact.pointA[j];
|
||||
simplexPrevPositionA += prevPositions[particleIndex] * contact.pointA[j];
|
||||
simplexPrevOrientationA.value += prevOrientations[particleIndex].value * contact.pointA[j];
|
||||
simplexInvMassA += invMasses[particleIndex] * contact.pointA[j];
|
||||
simplexInvInertiaA += invInertiaTensors[particleIndex] * contact.pointA[j];
|
||||
simplexRadiusA += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartB + j];
|
||||
simplexVelocityB += velocities[particleIndex] * contact.pointB[j];
|
||||
simplexPrevPositionB += prevPositions[particleIndex] * contact.pointB[j];
|
||||
simplexPrevOrientationB.value += prevOrientations[particleIndex].value * contact.pointB[j];
|
||||
simplexInvMassB += invMasses[particleIndex] * contact.pointB[j];
|
||||
simplexInvInertiaB += invInertiaTensors[particleIndex] * contact.pointB[j];
|
||||
simplexRadiusB += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointB[j];
|
||||
}
|
||||
|
||||
// update contact distance
|
||||
float dAB = math.dot(simplexPrevPositionA - simplexPrevPositionB, contact.normal);
|
||||
contact.distance = dAB - (simplexRadiusA + simplexRadiusB);
|
||||
|
||||
// calculate contact points:
|
||||
float4 contactPointA = simplexPrevPositionB + contact.normal * (contact.distance + simplexRadiusB);
|
||||
float4 contactPointB = simplexPrevPositionA - contact.normal * (contact.distance + simplexRadiusA);
|
||||
|
||||
// update contact basis:
|
||||
contact.CalculateBasis(simplexVelocityA - simplexVelocityB);
|
||||
|
||||
// update contact masses:
|
||||
int aMaterialIndex = particleMaterialIndices[simplices[simplexStartA]];
|
||||
int bMaterialIndex = particleMaterialIndices[simplices[simplexStartB]];
|
||||
bool rollingContacts = (aMaterialIndex >= 0 ? collisionMaterials[aMaterialIndex].rollingContacts > 0 : false) |
|
||||
(bMaterialIndex >= 0 ? collisionMaterials[bMaterialIndex].rollingContacts > 0 : false);
|
||||
|
||||
contact.CalculateContactMassesA(simplexInvMassA, simplexInvInertiaA, simplexPrevPositionA, simplexPrevOrientationA, contactPointA, rollingContacts);
|
||||
contact.CalculateContactMassesB(simplexInvMassB, simplexInvInertiaB, simplexPrevPositionB, simplexPrevOrientationB, contactPointB, rollingContacts);
|
||||
|
||||
contacts[i] = contact;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct ParticleCollisionConstraintsBatchJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<quaternion> orientations;
|
||||
[ReadOnly] public NativeArray<float> invMasses;
|
||||
[ReadOnly] public NativeArray<float4> radii;
|
||||
[ReadOnly] public NativeArray<int> particleMaterialIndices;
|
||||
[ReadOnly] public NativeArray<BurstCollisionMaterial> collisionMaterials;
|
||||
|
||||
// simplex arrays:
|
||||
[ReadOnly] public NativeArray<int> simplices;
|
||||
[ReadOnly] public SimplexCounts simplexCounts;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
|
||||
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstContact> contacts;
|
||||
|
||||
[ReadOnly] public Oni.ConstraintParameters constraintParameters;
|
||||
[ReadOnly] public Oni.SolverParameters solverParameters;
|
||||
[ReadOnly] public float4 gravity;
|
||||
[ReadOnly] public float substepTime;
|
||||
|
||||
[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 contact = contacts[i];
|
||||
|
||||
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;
|
||||
|
||||
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];
|
||||
}
|
||||
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];
|
||||
}
|
||||
|
||||
float4 posA = simplexPositionA - contact.normal * simplexRadiusA;
|
||||
float4 posB = simplexPositionB + contact.normal * simplexRadiusB;
|
||||
|
||||
// adhesion:
|
||||
float lambda = contact.SolveAdhesion(posA, posB, material.stickDistance, material.stickiness, substepTime);
|
||||
|
||||
// depenetration:
|
||||
lambda += contact.SolvePenetration(posA, posB, solverParameters.maxDepenetration * substepTime);
|
||||
|
||||
// Apply normal impulse to both particles (w/ shock propagation):
|
||||
if (math.abs(lambda) > BurstMath.epsilon)
|
||||
{
|
||||
float shock = solverParameters.shockPropagation * math.dot(contact.normal, math.normalizesafe(gravity));
|
||||
float4 delta = lambda * contact.normal;
|
||||
|
||||
float baryScale = BurstMath.BaryScale(contact.pointA);
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartA + j];
|
||||
deltas[particleIndex] += delta * invMasses[particleIndex] * contact.pointA[j] * baryScale * (1 - shock);
|
||||
counts[particleIndex]++;
|
||||
}
|
||||
|
||||
baryScale = BurstMath.BaryScale(contact.pointB);
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartB + j];
|
||||
deltas[particleIndex] -= delta * invMasses[particleIndex] * contact.pointB[j] * baryScale * (1 + shock);
|
||||
counts[particleIndex]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply position deltas immediately, if using sequential evaluation:
|
||||
if (constraintParameters.evaluationOrder == Oni.ConstraintParameters.EvaluationOrder.Sequential)
|
||||
{
|
||||
for (int j = 0; j < simplexSizeA; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartA + j];
|
||||
BurstConstraintsBatchImpl.ApplyPositionDelta(particleIndex, constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
}
|
||||
|
||||
for (int j = 0; j < simplexSizeB; ++j)
|
||||
{
|
||||
int particleIndex = simplices[simplexStartB + j];
|
||||
BurstConstraintsBatchImpl.ApplyPositionDelta(particleIndex, constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
|
||||
}
|
||||
}
|
||||
|
||||
contacts[i] = contact;
|
||||
}
|
||||
}
|
||||
|
||||
private BurstCollisionMaterial CombineCollisionMaterials(int entityA, int entityB)
|
||||
{
|
||||
// Combine collision materials:
|
||||
int aMaterialIndex = particleMaterialIndices[entityA];
|
||||
int bMaterialIndex = particleMaterialIndices[entityB];
|
||||
|
||||
if (aMaterialIndex >= 0 && bMaterialIndex >= 0)
|
||||
return BurstCollisionMaterial.CombineWith(collisionMaterials[aMaterialIndex], collisionMaterials[bMaterialIndex]);
|
||||
else if (aMaterialIndex >= 0)
|
||||
return collisionMaterials[aMaterialIndex];
|
||||
else if (bMaterialIndex >= 0)
|
||||
return collisionMaterials[bMaterialIndex];
|
||||
|
||||
return new BurstCollisionMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user