添加插件

This commit is contained in:
2025-11-10 00:08:26 +08:00
parent 4059c207c0
commit 76f80db694
2814 changed files with 436400 additions and 178 deletions

View File

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

View File

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

View File

@@ -0,0 +1,25 @@
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);
Vector3 GetParticleRestPosition(int index);
Quaternion GetParticleRestOrientation(int index);
void GetParticleAnisotropy(int index, ref Vector4 b1, ref Vector4 b2, ref Vector4 b3);
float GetParticleMaxRadius(int index);
Color GetParticleColor(int index);
}
}

View File

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

File diff suppressed because it is too large Load Diff

View 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:

View File

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

View File

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

View File

@@ -0,0 +1,26 @@
#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

View File

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

View File

@@ -0,0 +1,48 @@
#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)
{
quaternion deltaq = math.mul(rotation, math.inverse(prevRotation));
float sign = deltaq.value.w >= 0 ? 1 : -1;
return new float4(sign * deltaq.value.xyz * 2.0f / dt, 0);
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,22 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using Unity.Jobs;
namespace Obi
{
public class BurstJobHandle : IObiJobHandle
{
public JobHandle jobHandle { get; set; } = new JobHandle();
public void Complete()
{
jobHandle.Complete();
}
public void Release()
{
jobHandle = new JobHandle();
}
}
}
#endif

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,155 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace Obi
{
public struct BurstBox : BurstLocalOptimization.IDistanceFunction
{
public BurstColliderShape shape;
public BurstAffineTransform colliderToSolver;
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)
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 static JobHandle GenerateContacts(ObiColliderWorld world,
BurstSolverImpl solver,
NativeList<Oni.ContactPair> contactPairs,
NativeQueue<BurstContact> contactQueue,
NativeArray<int> contactOffsetsPerType,
float deltaTime,
JobHandle inputDeps)
{
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.Box + 1] - contactOffsetsPerType[(int)Oni.ShapeType.Box];
if (pairCount == 0) return inputDeps;
var job = new GenerateBoxContactsJob
{
contactPairs = contactPairs,
positions = solver.positions,
orientations = solver.orientations,
velocities = solver.velocities,
invMasses = solver.invMasses,
radii = solver.principalRadii,
simplices = solver.simplices,
simplexCounts = solver.simplexCounts,
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
contactsQueue = contactQueue.AsParallelWriter(),
worldToSolver = solver.worldToSolver,
deltaTime = deltaTime,
parameters = solver.abstraction.parameters,
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.Box]
};
inputDeps = job.Schedule(pairCount, 8, inputDeps);
return inputDeps;
}
}
[BurstCompile]
struct GenerateBoxContactsJob : IJobParallelFor
{
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
// particle arrays:
[ReadOnly] public NativeArray<float4> velocities;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<quaternion> orientations;
[ReadOnly] public NativeArray<float> invMasses;
[ReadOnly] public NativeArray<float4> radii;
// simplex arrays:
[ReadOnly] public NativeArray<int> simplices;
[ReadOnly] public SimplexCounts simplexCounts;
// collider arrays:
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
// auxiliar data:
[ReadOnly] public int firstPair;
[ReadOnly] public BurstAffineTransform worldToSolver;
[ReadOnly] public float deltaTime;
[ReadOnly] public Oni.SolverParameters parameters;
public void Execute(int i)
{
int simplexIndex = contactPairs[firstPair + i].bodyA;
int colliderIndex = contactPairs[firstPair + i].bodyB;
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
BurstBox shape = new BurstBox { colliderToSolver = colliderToSolver, shape = shapes[colliderIndex] };
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
var colliderPoint = BurstLocalOptimization.Optimize(ref shape, positions, orientations, radii, simplices, simplexStart, simplexSize,
ref simplexBary, out _, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
contactsQueue.Enqueue(new BurstContact
{
bodyA = simplexIndex,
bodyB = colliderIndex,
pointA = simplexBary,
pointB = colliderPoint.point,
normal = colliderPoint.normal * shape.shape.sign
});
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,136 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace Obi
{
public struct BurstCapsule : BurstLocalOptimization.IDistanceFunction
{
public BurstColliderShape shape;
public BurstAffineTransform colliderToSolver;
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)
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 static JobHandle GenerateContacts(ObiColliderWorld world,
BurstSolverImpl solver,
NativeList<Oni.ContactPair> contactPairs,
NativeQueue<BurstContact> contactQueue,
NativeArray<int> contactOffsetsPerType,
float deltaTime,
JobHandle inputDeps)
{
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.Capsule + 1] - contactOffsetsPerType[(int)Oni.ShapeType.Capsule];
if (pairCount == 0) return inputDeps;
var job = new GenerateCapsuleContactsJob
{
contactPairs = contactPairs,
positions = solver.positions,
orientations = solver.orientations,
velocities = solver.velocities,
invMasses = solver.invMasses,
radii = solver.principalRadii,
simplices = solver.simplices,
simplexCounts = solver.simplexCounts,
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
contactsQueue = contactQueue.AsParallelWriter(),
worldToSolver = solver.worldToSolver,
deltaTime = deltaTime,
parameters = solver.abstraction.parameters,
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.Capsule]
};
inputDeps = job.Schedule(pairCount, 8, inputDeps);
return inputDeps;
}
}
[BurstCompile]
struct GenerateCapsuleContactsJob : IJobParallelFor
{
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
// particle arrays:
[ReadOnly] public NativeArray<float4> velocities;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<quaternion> orientations;
[ReadOnly] public NativeArray<float> invMasses;
[ReadOnly] public NativeArray<float4> radii;
// simplex arrays:
[ReadOnly] public NativeArray<int> simplices;
[ReadOnly] public SimplexCounts simplexCounts;
// collider arrays:
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
// auxiliar data:
[ReadOnly] public int firstPair;
[ReadOnly] public BurstAffineTransform worldToSolver;
[ReadOnly] public float deltaTime;
[ReadOnly] public Oni.SolverParameters parameters;
public void Execute(int i)
{
int simplexIndex = contactPairs[firstPair + i].bodyA;
int colliderIndex = contactPairs[firstPair + i].bodyB;
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
BurstCapsule shape = new BurstCapsule { colliderToSolver = colliderToSolver, shape = shapes[colliderIndex] };
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
var colliderPoint = BurstLocalOptimization.Optimize(ref shape, positions, orientations, radii, simplices, simplexStart, simplexSize,
ref simplexBary, out _, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
contactsQueue.Enqueue(new BurstContact
{
bodyA = simplexIndex,
bodyB = colliderIndex,
pointA = simplexBary,
pointB = colliderPoint.point,
normal = colliderPoint.normal * shape.shape.sign
});
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,41 @@
#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; // index of the associated collider data in the collision world.
public int rigidbodyIndex; // index of the associated rigidbody in the collision world.
public int materialIndex; // index of the associated material in the collision world.
public int forceZoneIndex; // index of the associated force zone in the collision world.
public int filter;
public int flags; // first bit whether the collider is 2D (1) or 3D (0), second bit whether it's a trigger (1) or regular collider (0),
// third bit determines whether shape is inverted or not.
public bool is2D
{
get => (flags & 1) != 0;
set => flags |= value ? 1 : 0;
}
public bool isTrigger
{
get => (flags & 1 << 1) != 0 || forceZoneIndex >= 0;
set => flags |= value ? 1 << 1 : 0;
}
public float sign
{
get => (flags & 1 << 2) != 0 ? -1 : 1;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,632 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using UnityEngine;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Burst;
using System.Threading;
using Unity.Collections.LowLevel.Unsafe;
namespace Obi
{
public class BurstColliderWorld : MonoBehaviour, IColliderWorldImpl
{
struct MovingCollider
{
public BurstCellSpan oldSpan;
public BurstCellSpan newSpan;
public int entity;
}
public int referenceCount { get; private set; } = 0;
public int colliderCount { get; private set; } = 0;
private NativeMultilevelGrid<int> grid;
private NativeQueue<MovingCollider> movingColliders;
private NativeArray<int> colliderTypeCounts;
private NativeQueue<Oni.ContactPair> contactPairQueue;
public NativeList<Oni.ContactPair> contactPairs;
public NativeArray<int> contactOffsetsPerType;
public NativeQueue<BurstContact> colliderContactQueue;
public ObiNativeCellSpanList cellSpans;
public void Awake()
{
this.grid = new NativeMultilevelGrid<int>(1000, Allocator.Persistent);
this.movingColliders = new NativeQueue<MovingCollider>(Allocator.Persistent);
this.colliderContactQueue = new NativeQueue<BurstContact>(Allocator.Persistent);
this.contactPairQueue = new NativeQueue<Oni.ContactPair>(Allocator.Persistent);
this.colliderTypeCounts = new NativeArray<int>(Oni.ColliderShapeTypeCount, Allocator.Persistent);
this.contactOffsetsPerType = new NativeArray<int>(Oni.ColliderShapeTypeCount + 1, Allocator.Persistent);
this.contactPairs = new NativeList<Oni.ContactPair>(Allocator.Persistent);
this.cellSpans = new ObiNativeCellSpanList();
ObiColliderWorld.GetInstance().RegisterImplementation(this);
}
public void OnDestroy()
{
ObiColliderWorld.GetInstance().UnregisterImplementation(this);
grid.Dispose();
movingColliders.Dispose();
colliderTypeCounts.Dispose();
contactPairQueue.Dispose();
contactPairs.Dispose();
contactOffsetsPerType.Dispose();
colliderContactQueue.Dispose();
cellSpans.Dispose();
}
public void IncreaseReferenceCount()
{
referenceCount++;
}
public void DecreaseReferenceCount()
{
if (--referenceCount <= 0 && gameObject != null)
DestroyImmediate(gameObject);
}
public void SetColliders(ObiNativeColliderShapeList shapes, ObiNativeAabbList bounds, ObiNativeAffineTransformList transforms)
{
colliderCount = shapes.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 SetForceZones(ObiNativeForceZoneList 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)
{
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;
[ReadOnly] public NativeArray<int> particleMaterialIndices;
// 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;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeQueue<Oni.ContactPair>.ParallelWriter contactPairQueue;
[NativeDisableParallelForRestriction]
public NativeArray<int> colliderTypeCounts;
// auxiliar data:
[ReadOnly] public BurstAffineTransform solverToWorld;
[ReadOnly] public float deltaTime;
[ReadOnly] public Oni.SolverParameters parameters;
public void Execute(int i)
{
int simplexStart = simplexCounts.GetSimplexStartAndSize(i, out int simplexSize);
// get all colliders overlapped by the cell bounds, in all grid levels:
BurstAabb simplexBoundsWS = simplexBounds[i].Transformed(solverToWorld);
NativeList<int> candidates = new NativeList<int>(16,Allocator.Temp);
// max size of the simplex 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))
{
// increment the amount of contacts for this shape type:
Interlocked.Increment(ref ((int*)colliderTypeCounts.GetUnsafePtr())[(int)shape.type]);
// enqueue a new contact pair:
contactPairQueue.Enqueue(new Oni.ContactPair{
bodyA = i,
bodyB = c
});
}
}
}
}
}
}
[BurstCompile]
struct PrefixSumJob : IJob
{
[ReadOnly] public NativeArray<int> array;
public NativeArray<int> sum;
public void Execute()
{
sum[0] = 0;
for (int i = 1; i < sum.Length; ++i)
sum[i] = sum[i - 1] + array[i-1];
}
}
[BurstCompile]
struct SortContactPairsByShape : IJob
{
public NativeQueue<Oni.ContactPair> contactPairQueue;
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
[ReadOnly] public NativeArray<int> start; // prefix sum
public NativeArray<int> count;
public NativeList<Oni.ContactPair> contactPairs;
public void Execute()
{
contactPairs.ResizeUninitialized(contactPairQueue.Count);
while (!contactPairQueue.IsEmpty())
{
var pair = contactPairQueue.Dequeue();
int shapeType = (int)shapes[pair.bodyB].type;
// write the pair directly at its position in the sorted array:
contactPairs[start[shapeType] + (--count[shapeType])] = pair;
}
}
}
[BurstCompile]
unsafe struct ApplyForceZonesJob : IJobParallelFor
{
// particle arrays:
[NativeDisableParallelForRestriction] public NativeArray<float4> externalForces;
[NativeDisableParallelForRestriction] public NativeArray<float4> wind;
[NativeDisableParallelForRestriction] public NativeArray<float4> velocities;
[NativeDisableParallelForRestriction] public NativeArray<float4> colors;
[NativeDisableParallelForRestriction] public NativeArray<float> life;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<float> invMasses;
// simplex arrays:
[ReadOnly] public NativeArray<int> simplices;
[ReadOnly] public SimplexCounts simplexCounts;
// collider arrays:
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
[ReadOnly] public NativeArray<ForceZone> forceZones;
// contacts
[ReadOnly] public NativeArray<BurstContact> contacts;
// auxiliar data:
[ReadOnly] public BurstAffineTransform worldToSolver;
[ReadOnly] public float deltaTime;
public void Execute(int i)
{
var contact = contacts[i];
int forceZoneIndex = shapes[contact.bodyB].forceZoneIndex;
if (forceZoneIndex >= 0)
{
int simplexStart = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);
for (int j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
float distance = -math.dot(positions[particleIndex] - contact.pointB, contact.normal);
if (distance < 0) continue;
float4 axis = (worldToSolver * transforms[contact.bodyB]).TransformDirection(new float4(0, 0, 1, 0));
// calculate falloff region based on min/max distances:
float falloff = 1;
float range = forceZones[forceZoneIndex].maxDistance - forceZones[forceZoneIndex].minDistance;
if (math.abs(range) > BurstMath.epsilon)
falloff = math.pow(math.saturate((distance - forceZones[forceZoneIndex].minDistance) / range), forceZones[forceZoneIndex].falloffPower);
float forceIntensity = forceZones[forceZoneIndex].intensity * falloff;
float dampIntensity = forceZones[forceZoneIndex].damping * falloff;
// tint particles:
float mix = math.pow(1 - math.saturate(forceZones[forceZoneIndex].color.a * falloff), deltaTime);
colors[particleIndex] = math.lerp((Vector4)forceZones[forceZoneIndex].color, colors[particleIndex], mix);
// calculate force direction, depending on the type of the force field:
float4 result = float4.zero;
switch (forceZones[forceZoneIndex].type)
{
case ForceZone.ZoneType.Radial:
result = contact.normal * forceIntensity;
break;
case ForceZone.ZoneType.Vortex:
result = new float4(math.cross(axis.xyz * forceIntensity, contact.normal.xyz).xyz, 0);
break;
case ForceZone.ZoneType.Directional:
result = axis * forceIntensity;
break;
default:
BurstMath.AtomicAdd(life, particleIndex, -forceIntensity * deltaTime);
continue;
}
// apply damping:
switch (forceZones[forceZoneIndex].dampingDir)
{
case ForceZone.DampingDirection.ForceDirection:
{
float4 forceDir = math.normalizesafe(result);
result -= forceDir * math.dot(velocities[particleIndex], forceDir) * dampIntensity;
}
break;
case ForceZone.DampingDirection.SurfaceDirection:
result -= contact.normal * math.dot(velocities[particleIndex], contact.normal) * dampIntensity;
break;
default:
result -= velocities[particleIndex] * dampIntensity;
break;
}
if (invMasses[particleIndex] > 0)
{
switch (forceZones[forceZoneIndex].mode)
{
case ForceZone.ForceMode.Acceleration:
BurstMath.AtomicAdd(externalForces, particleIndex, result / simplexSize / invMasses[particleIndex]);
break;
case ForceZone.ForceMode.Force:
BurstMath.AtomicAdd(externalForces, particleIndex, result / simplexSize);
break;
case ForceZone.ForceMode.Wind:
BurstMath.AtomicAdd(wind, particleIndex, result / simplexSize);
break;
}
}
}
}
}
}
public JobHandle ApplyForceZones(BurstSolverImpl solver, float deltaTime, JobHandle inputDeps)
{
var world = ObiColliderWorld.GetInstance();
var applyForceFieldsJob = new ApplyForceZonesJob
{
contacts = solver.abstraction.colliderContacts.AsNativeArray<BurstContact>(),
positions = solver.positions,
velocities = solver.velocities,
externalForces = solver.externalForces,
wind = solver.wind,
invMasses = solver.invMasses,
life = solver.life,
colors = solver.colors,
simplices = solver.simplices,
simplexCounts = solver.simplexCounts,
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
forceZones = world.forceZones.AsNativeArray<ForceZone>(),
worldToSolver = solver.worldToSolver,
deltaTime = deltaTime,
};
return applyForceFieldsJob.Schedule(solver.abstraction.colliderContacts.count, 64, inputDeps);
}
public JobHandle GenerateContacts(BurstSolverImpl solver, float deltaTime, JobHandle inputDeps)
{
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,
particleMaterialIndices = solver.collisionMaterials,
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>(),
contactPairQueue = contactPairQueue.AsParallelWriter(),
colliderTypeCounts = colliderTypeCounts,
solverToWorld = solver.solverToWorld,
deltaTime = deltaTime,
parameters = solver.abstraction.parameters
};
inputDeps = generateColliderContactsJob.Schedule(solver.simplexCounts.simplexCount, 16, inputDeps);
var prefixSumJob = new PrefixSumJob
{
array = colliderTypeCounts,
sum = contactOffsetsPerType
};
inputDeps = prefixSumJob.Schedule(inputDeps);
var sortPairsJob = new SortContactPairsByShape
{
contactPairQueue = contactPairQueue,
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
start = contactOffsetsPerType,
count = colliderTypeCounts,
contactPairs = contactPairs
};
inputDeps = sortPairsJob.Schedule(inputDeps);
inputDeps.Complete();
inputDeps = BurstSphere.GenerateContacts(world,solver,contactPairs,colliderContactQueue,contactOffsetsPerType,deltaTime,inputDeps);
inputDeps = BurstBox.GenerateContacts(world,solver,contactPairs,colliderContactQueue, contactOffsetsPerType,deltaTime,inputDeps);
inputDeps = BurstCapsule.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
inputDeps = BurstDistanceField.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
inputDeps = BurstTriangleMesh.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
inputDeps = BurstHeightField.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
inputDeps = BurstEdgeMesh.GenerateContacts(world, solver, contactPairs, colliderContactQueue, contactOffsetsPerType, deltaTime, inputDeps);
return inputDeps;
}
}
}
#endif

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,188 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace Obi
{
public struct BurstDistanceField : BurstLocalOptimization.IDistanceFunction
{
public BurstColliderShape shape;
public BurstAffineTransform colliderToSolver;
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)
point[2] = 0;
var header = distanceFieldHeaders[shape.dataIndex];
float4 sample = DFTraverse(point, in header);
float4 normal = new float4(math.normalize(sample.xyz), 0);
projectedPoint.point = colliderToSolver.TransformPoint(point - normal * (sample[3] - shape.contactOffset));
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
}
private float4 DFTraverse(float4 particlePosition, in DistanceFieldHeader header)
{
var stack = new NativeArray<int>(12, Allocator.Temp);
int stackTop = 0;
stack[stackTop++] = 0;
while (stackTop > 0)
{
int nodeIndex = stack[--stackTop];
var node = dfNodes[header.firstNode + nodeIndex];
// if the child node exists, recurse down the df octree:
if (node.firstChild >= 0)
stack[stackTop++] = node.firstChild + node.GetOctant(particlePosition);
else
return node.SampleWithGradient(particlePosition);
}
return float4.zero;
}
public static JobHandle GenerateContacts(ObiColliderWorld world,
BurstSolverImpl solver,
NativeList<Oni.ContactPair> contactPairs,
NativeQueue<BurstContact> contactQueue,
NativeArray<int> contactOffsetsPerType,
float deltaTime,
JobHandle inputDeps)
{
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.SignedDistanceField + 1] - contactOffsetsPerType[(int)Oni.ShapeType.SignedDistanceField];
if (pairCount == 0) return inputDeps;
var job = new GenerateDistanceFieldContactsJob
{
contactPairs = contactPairs,
positions = solver.positions,
orientations = solver.orientations,
velocities = solver.velocities,
invMasses = solver.invMasses,
radii = solver.principalRadii,
simplices = solver.simplices,
simplexCounts = solver.simplexCounts,
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
distanceFieldHeaders = world.distanceFieldContainer.headers.AsNativeArray<DistanceFieldHeader>(),
distanceFieldNodes = world.distanceFieldContainer.dfNodes.AsNativeArray<BurstDFNode>(),
contactsQueue = contactQueue.AsParallelWriter(),
solverToWorld = solver.inertialFrame,
worldToSolver = solver.worldToSolver,
deltaTime = deltaTime,
parameters = solver.abstraction.parameters,
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.SignedDistanceField]
};
inputDeps = job.Schedule(pairCount, 1, inputDeps);
return inputDeps;
}
}
[BurstCompile]
struct GenerateDistanceFieldContactsJob : IJobParallelFor
{
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
// particle arrays:
[ReadOnly] public NativeArray<float4> velocities;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<quaternion> orientations;
[ReadOnly] public NativeArray<float> invMasses;
[ReadOnly] public NativeArray<float4> radii;
// simplex arrays:
[ReadOnly] public NativeArray<int> simplices;
[ReadOnly] public SimplexCounts simplexCounts;
// collider arrays:
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
// distance field data:
[ReadOnly] public NativeArray<DistanceFieldHeader> distanceFieldHeaders;
[ReadOnly] public NativeArray<BurstDFNode> distanceFieldNodes;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
// auxiliar data:
[ReadOnly] public int firstPair;
[ReadOnly] public BurstInertialFrame solverToWorld;
[ReadOnly] public BurstAffineTransform worldToSolver;
[ReadOnly] public float deltaTime;
[ReadOnly] public Oni.SolverParameters parameters;
public void Execute(int i)
{
int simplexIndex = contactPairs[firstPair + i].bodyA;
int colliderIndex = contactPairs[firstPair + i].bodyB;
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
if (shapes[colliderIndex].dataIndex < 0) return;
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
BurstDistanceField dfShape = new BurstDistanceField()
{
colliderToSolver = colliderToSolver,
shape = shapes[colliderIndex],
distanceFieldHeaders = distanceFieldHeaders,
dfNodes = distanceFieldNodes
};
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
var colliderPoint = BurstLocalOptimization.Optimize(ref dfShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
ref simplexBary, out float4 simplexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
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 * deltaTime + dAB <= simplexRadius + shapes[colliderIndex].contactOffset + parameters.collisionMargin)
contactsQueue.Enqueue(new BurstContact
{
bodyA = simplexIndex,
bodyB = colliderIndex,
pointA = simplexBary,
pointB = colliderPoint.point,
normal = colliderPoint.normal * dfShape.shape.sign
});
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,211 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace Obi
{
public struct BurstEdgeMesh : BurstLocalOptimization.IDistanceFunction
{
public BurstColliderShape shape;
public BurstAffineTransform colliderToSolver;
public int dataOffset;
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)
point[2] = 0;
Edge t = edges[header.firstEdge + dataOffset];
float4 v1 = (new float4(vertices[header.firstVertex + t.i1], 0, 0) + shape.center) * colliderToSolver.scale;
float4 v2 = (new float4(vertices[header.firstVertex + t.i2], 0, 0) + shape.center) * colliderToSolver.scale;
float4 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 static JobHandle GenerateContacts(ObiColliderWorld world,
BurstSolverImpl solver,
NativeList<Oni.ContactPair> contactPairs,
NativeQueue<BurstContact> contactQueue,
NativeArray<int> contactOffsetsPerType,
float deltaTime,
JobHandle inputDeps)
{
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.EdgeMesh + 1] - contactOffsetsPerType[(int)Oni.ShapeType.EdgeMesh];
if (pairCount == 0) return inputDeps;
var job = new GenerateEdgeMeshContactsJob
{
contactPairs = contactPairs,
positions = solver.positions,
orientations = solver.orientations,
velocities = solver.velocities,
invMasses = solver.invMasses,
radii = solver.principalRadii,
simplices = solver.simplices,
simplexCounts = solver.simplexCounts,
simplexBounds = solver.simplexBounds,
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
edgeMeshHeaders = world.edgeMeshContainer.headers.AsNativeArray<EdgeMeshHeader>(),
edgeBihNodes = world.edgeMeshContainer.bihNodes.AsNativeArray<BIHNode>(),
edges = world.edgeMeshContainer.edges.AsNativeArray<Edge>(),
edgeVertices = world.edgeMeshContainer.vertices.AsNativeArray<float2>(),
contactsQueue = contactQueue.AsParallelWriter(),
solverToWorld = solver.solverToWorld,
worldToSolver = solver.worldToSolver,
deltaTime = deltaTime,
parameters = solver.abstraction.parameters,
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.TriangleMesh]
};
inputDeps = job.Schedule(pairCount, 1, inputDeps);
return inputDeps;
}
}
[BurstCompile]
struct GenerateEdgeMeshContactsJob : IJobParallelFor
{
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
// particle arrays:
[ReadOnly] public NativeArray<float4> velocities;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<quaternion> orientations;
[ReadOnly] public NativeArray<float> invMasses;
[ReadOnly] public NativeArray<float4> radii;
// simplex arrays:
[ReadOnly] public NativeArray<int> simplices;
[ReadOnly] public SimplexCounts simplexCounts;
[ReadOnly] public NativeArray<BurstAabb> simplexBounds;
// collider arrays:
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
// edge mesh data:
[ReadOnly] public NativeArray<EdgeMeshHeader> edgeMeshHeaders;
[ReadOnly] public NativeArray<BIHNode> edgeBihNodes;
[ReadOnly] public NativeArray<Edge> edges;
[ReadOnly] public NativeArray<float2> edgeVertices;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
// auxiliar data:
[ReadOnly] public int firstPair;
[ReadOnly] public BurstAffineTransform solverToWorld;
[ReadOnly] public BurstAffineTransform worldToSolver;
[ReadOnly] public float deltaTime;
[ReadOnly] public Oni.SolverParameters parameters;
public void Execute(int i)
{
int simplexIndex = contactPairs[firstPair + i].bodyA;
int colliderIndex = contactPairs[firstPair + i].bodyB;
var shape = shapes[colliderIndex];
if (shape.dataIndex < 0)
return;
var header = edgeMeshHeaders[shape.dataIndex];
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
var simplexBound = simplexBounds[simplexIndex];
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
// invert a full matrix here to accurately represent collider bounds scale.
var solverToCollider = math.inverse(float4x4.TRS(colliderToSolver.translation.xyz, colliderToSolver.rotation, colliderToSolver.scale.xyz));
var simplexBoundsCS = simplexBound.Transformed(solverToCollider);
float4 marginCS = new float4((shape.contactOffset + parameters.collisionMargin) / colliderToSolver.scale.xyz, 0);
BurstEdgeMesh edgeMeshShape = new BurstEdgeMesh()
{
colliderToSolver = colliderToSolver,
shape = shape,
header = header,
edgeBihNodes = edgeBihNodes,
edges = edges,
vertices = edgeVertices
};
NativeQueue<int> queue = new NativeQueue<int>(Allocator.Temp);
queue.Enqueue(0);
while (!queue.IsEmpty())
{
int nodeIndex = queue.Dequeue();
var node = edgeBihNodes[header.firstNode + nodeIndex];
// leaf node:
if (node.firstChild < 0)
{
// check for contact against all triangles:
for (int dataOffset = node.start; dataOffset < node.start + node.count; ++dataOffset)
{
Edge t = edges[header.firstEdge + dataOffset];
float4 v1 = new float4(edgeVertices[header.firstVertex + t.i1], 0, 0) + shape.center;
float4 v2 = new float4(edgeVertices[header.firstVertex + t.i2], 0, 0) + shape.center;
BurstAabb edgeBounds = new BurstAabb(v1, v2, marginCS);
if (edgeBounds.IntersectsAabb(simplexBoundsCS, shape.is2D))
{
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
edgeMeshShape.dataOffset = dataOffset;
var colliderPoint = BurstLocalOptimization.Optimize(ref edgeMeshShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
ref simplexBary, out float4 convexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
contactsQueue.Enqueue(new BurstContact(){
bodyA = simplexIndex,
bodyB = colliderIndex,
pointA = simplexBary,
pointB = colliderPoint.point,
normal = colliderPoint.normal * edgeMeshShape.shape.sign
});
}
}
}
else // check min and/or max children:
{
// visit min node:
if (simplexBoundsCS.min[node.axis] <= node.min)
queue.Enqueue(node.firstChild);
// visit max node:
if (simplexBoundsCS.max[node.axis] >= node.max)
queue.Enqueue(node.firstChild + 1);
}
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,273 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace Obi
{
public struct BurstHeightField : BurstLocalOptimization.IDistanceFunction
{
public BurstColliderShape shape;
public BurstAffineTransform colliderToSolver;
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 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 static JobHandle GenerateContacts(ObiColliderWorld world,
BurstSolverImpl solver,
NativeList<Oni.ContactPair> contactPairs,
NativeQueue<BurstContact> contactQueue,
NativeArray<int> contactOffsetsPerType,
float deltaTime,
JobHandle inputDeps)
{
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.Heightmap + 1] - contactOffsetsPerType[(int)Oni.ShapeType.Heightmap];
if (pairCount == 0) return inputDeps;
var job = new GenerateHeightFieldContactsJob
{
contactPairs = contactPairs,
positions = solver.positions,
orientations = solver.orientations,
velocities = solver.velocities,
invMasses = solver.invMasses,
radii = solver.principalRadii,
simplices = solver.simplices,
simplexCounts = solver.simplexCounts,
simplexBounds = solver.simplexBounds,
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
heightFieldHeaders = world.heightFieldContainer.headers.AsNativeArray<HeightFieldHeader>(),
heightFieldSamples = world.heightFieldContainer.samples.AsNativeArray<float>(),
contactsQueue = contactQueue.AsParallelWriter(),
solverToWorld = solver.inertialFrame,
worldToSolver = solver.worldToSolver,
deltaTime = deltaTime,
parameters = solver.abstraction.parameters,
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.Heightmap]
};
inputDeps = job.Schedule(pairCount, 1, inputDeps);
return inputDeps;
}
}
[BurstCompile]
struct GenerateHeightFieldContactsJob : IJobParallelFor
{
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
// particle arrays:
[ReadOnly] public NativeArray<float4> velocities;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<quaternion> orientations;
[ReadOnly] public NativeArray<float> invMasses;
[ReadOnly] public NativeArray<float4> radii;
// simplex arrays:
[ReadOnly] public NativeArray<int> simplices;
[ReadOnly] public SimplexCounts simplexCounts;
[ReadOnly] public NativeArray<BurstAabb> simplexBounds;
// collider arrays:
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
// height field data:
[ReadOnly] public NativeArray<HeightFieldHeader> heightFieldHeaders;
[ReadOnly] public NativeArray<float> heightFieldSamples;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
// auxiliar data:
[ReadOnly] public int firstPair;
[ReadOnly] public BurstInertialFrame solverToWorld;
[ReadOnly] public BurstAffineTransform worldToSolver;
[ReadOnly] public float deltaTime;
[ReadOnly] public Oni.SolverParameters parameters;
public void Execute(int i)
{
int simplexIndex = contactPairs[firstPair + i].bodyA;
int colliderIndex = contactPairs[firstPair + i].bodyB;
var shape = shapes[colliderIndex];
if (shape.dataIndex < 0)
return;
var header = heightFieldHeaders[shape.dataIndex];
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
var simplexBound = simplexBounds[simplexIndex];
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
// invert a full matrix here to accurately represent collider bounds scale.
var solverToCollider = math.inverse(float4x4.TRS(colliderToSolver.translation.xyz, colliderToSolver.rotation, colliderToSolver.scale.xyz));
var simplexBoundsCS = simplexBound.Transformed(solverToCollider);
BurstHeightField triangleMeshShape = new BurstHeightField()
{
colliderToSolver = colliderToSolver,
shape = shapes[colliderIndex],
header = heightFieldHeaders[shapes[colliderIndex].dataIndex],
heightFieldSamples = heightFieldSamples
};
float4 triNormal = float4.zero;
var co = new BurstContact { bodyA = simplexIndex, bodyB = colliderIndex };
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(simplexBoundsCS.min[0] / cellWidth), (int)math.floor(simplexBoundsCS.min[2] / cellHeight));
int2 max = new int2((int)math.floor(simplexBoundsCS.max[0] / cellWidth), (int)math.floor(simplexBoundsCS.max[2] / cellHeight));
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);
triangleMeshShape.tri.Cache(v1, v2, v3);
triNormal.xyz = math.normalizesafe(math.cross((v2 - v1).xyz, (v3 - v1).xyz));
var colliderPoint = BurstLocalOptimization.Optimize(ref triangleMeshShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
ref simplexBary, out convexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
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 * deltaTime + dAB <= simplexRadius + shape.contactOffset + parameters.collisionMargin)
{
co.pointB = colliderPoint.point;
co.normal = colliderPoint.normal * triangleMeshShape.shape.sign;
co.pointA = simplexBary;
contactsQueue.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);
triangleMeshShape.tri.Cache(v1, v2, v3);
triNormal.xyz = math.normalizesafe(math.cross((v2 - v1).xyz, (v3 - v1).xyz));
colliderPoint = BurstLocalOptimization.Optimize(ref triangleMeshShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
ref simplexBary, out convexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
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 * deltaTime + dAB <= simplexRadius + shape.contactOffset + parameters.collisionMargin)
{
co.pointB = colliderPoint.point;
co.normal = colliderPoint.normal * triangleMeshShape.shape.sign;
co.pointA = simplexBary;
contactsQueue.Enqueue(co);
}
}
}
}
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,207 @@
#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];
}
convexPoint.w = 0;
}
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;
candidate.w = 0;
// here, we adjust the candidate by projecting it to the engrosed simplex's surface:
candidate -= pointInFunction.normal * (radii[particle].x - convexThickness.x);
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.w = 0;
candidateD.w = 0;
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

View File

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

View File

@@ -0,0 +1,65 @@
#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(new float4(positions[simplices[simplexStart]].xyz,0),
new float4(positions[simplices[simplexStart + 1]].xyz,0),
new float4(positions[simplices[simplexStart + 2]].xyz,0));
}
}
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
{
switch (simplexSize)
{
case 1:
default:
{
float4 p1 = positions[simplices[simplexStart]]; p1.w = 0;
projectedPoint.bary = new float4(1, 0, 0, 0);
projectedPoint.point = p1;
}
break;
case 2:
{
float4 p1 = positions[simplices[simplexStart]]; p1.w = 0;
float4 p2 = positions[simplices[simplexStart + 1]]; p2.w = 0;
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

View File

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

View File

@@ -0,0 +1,127 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace Obi
{
public struct BurstSphere : BurstLocalOptimization.IDistanceFunction
{
public BurstColliderShape shape;
public BurstAffineTransform colliderToSolver;
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)
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 static JobHandle GenerateContacts(ObiColliderWorld world,
BurstSolverImpl solver,
NativeList<Oni.ContactPair> contactPairs,
NativeQueue<BurstContact> contactQueue,
NativeArray<int> contactOffsetsPerType,
float deltaTime,
JobHandle inputDeps)
{
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.Sphere + 1] - contactOffsetsPerType[(int)Oni.ShapeType.Sphere];
if (pairCount == 0) return inputDeps;
var job = new GenerateSphereContactsJob
{
contactPairs = contactPairs,
positions = solver.positions,
orientations = solver.orientations,
velocities = solver.velocities,
invMasses = solver.invMasses,
radii = solver.principalRadii,
simplices = solver.simplices,
simplexCounts = solver.simplexCounts,
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
contactsQueue = contactQueue.AsParallelWriter(),
worldToSolver = solver.worldToSolver,
deltaTime = deltaTime,
parameters = solver.abstraction.parameters,
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.Sphere]
};
inputDeps = job.Schedule(pairCount, 8, inputDeps);
return inputDeps;
}
}
[BurstCompile]
struct GenerateSphereContactsJob : IJobParallelFor
{
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
// particle arrays:
[ReadOnly] public NativeArray<float4> velocities;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<quaternion> orientations;
[ReadOnly] public NativeArray<float> invMasses;
[ReadOnly] public NativeArray<float4> radii;
// simplex arrays:
[ReadOnly] public NativeArray<int> simplices;
[ReadOnly] public SimplexCounts simplexCounts;
// collider arrays:
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
// auxiliar data:
[ReadOnly] public int firstPair;
[ReadOnly] public BurstAffineTransform worldToSolver;
[ReadOnly] public float deltaTime;
[ReadOnly] public Oni.SolverParameters parameters;
public void Execute(int i)
{
int simplexIndex = contactPairs[firstPair + i].bodyA;
int colliderIndex = contactPairs[firstPair + i].bodyB;
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
BurstSphere shape = new BurstSphere { colliderToSolver = colliderToSolver, shape = shapes[colliderIndex] };
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
var colliderPoint = BurstLocalOptimization.Optimize(ref shape, positions, orientations, radii, simplices, simplexStart, simplexSize,
ref simplexBary, out _, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
contactsQueue.Enqueue(new BurstContact {
bodyA = simplexIndex,
bodyB = colliderIndex,
pointA = simplexBary,
pointB = colliderPoint.point,
normal = colliderPoint.normal * shape.shape.sign
});
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,220 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace Obi
{
public struct BurstTriangleMesh : BurstLocalOptimization.IDistanceFunction
{
public BurstColliderShape shape;
public BurstAffineTransform colliderToSolver;
public BurstMath.CachedTri tri;
public void Evaluate(float4 point, float4 radii, quaternion orientation, ref BurstLocalOptimization.SurfacePoint projectedPoint)
{
point = colliderToSolver.InverseTransformPointUnscaled(point);
if (shape.is2D)
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 static JobHandle GenerateContacts(ObiColliderWorld world,
BurstSolverImpl solver,
NativeList<Oni.ContactPair> contactPairs,
NativeQueue<BurstContact> contactQueue,
NativeArray<int> contactOffsetsPerType,
float deltaTime,
JobHandle inputDeps)
{
int pairCount = contactOffsetsPerType[(int)Oni.ShapeType.TriangleMesh + 1] - contactOffsetsPerType[(int)Oni.ShapeType.TriangleMesh];
if (pairCount == 0) return inputDeps;
var job = new GenerateTriangleMeshContactsJob
{
contactPairs = contactPairs,
positions = solver.positions,
orientations = solver.orientations,
velocities = solver.velocities,
invMasses = solver.invMasses,
radii = solver.principalRadii,
simplices = solver.simplices,
simplexCounts = solver.simplexCounts,
simplexBounds = solver.simplexBounds,
transforms = world.colliderTransforms.AsNativeArray<BurstAffineTransform>(),
shapes = world.colliderShapes.AsNativeArray<BurstColliderShape>(),
rigidbodies = world.rigidbodies.AsNativeArray<BurstRigidbody>(),
triangleMeshHeaders = world.triangleMeshContainer.headers.AsNativeArray<TriangleMeshHeader>(),
bihNodes = world.triangleMeshContainer.bihNodes.AsNativeArray<BIHNode>(),
triangles = world.triangleMeshContainer.triangles.AsNativeArray<Triangle>(),
vertices = world.triangleMeshContainer.vertices.AsNativeArray<float3>(),
contactsQueue = contactQueue.AsParallelWriter(),
solverToWorld = solver.inertialFrame,
worldToSolver = solver.worldToSolver,
deltaTime = deltaTime,
parameters = solver.abstraction.parameters,
firstPair = contactOffsetsPerType[(int)Oni.ShapeType.TriangleMesh]
};
inputDeps = job.Schedule(pairCount, 1, inputDeps);
return inputDeps;
}
}
[BurstCompile]
struct GenerateTriangleMeshContactsJob : IJobParallelFor
{
[ReadOnly] public NativeList<Oni.ContactPair> contactPairs;
// particle arrays:
[ReadOnly] public NativeArray<float4> velocities;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<quaternion> orientations;
[ReadOnly] public NativeArray<float> invMasses;
[ReadOnly] public NativeArray<float4> radii;
// simplex arrays:
[ReadOnly] public NativeArray<int> simplices;
[ReadOnly] public SimplexCounts simplexCounts;
[ReadOnly] public NativeArray<BurstAabb> simplexBounds;
// collider arrays:
[ReadOnly] public NativeArray<BurstAffineTransform> transforms;
[ReadOnly] public NativeArray<BurstColliderShape> shapes;
[ReadOnly] public NativeArray<BurstRigidbody> rigidbodies;
// triangle mesh data:
[ReadOnly] public NativeArray<TriangleMeshHeader> triangleMeshHeaders;
[ReadOnly] public NativeArray<BIHNode> bihNodes;
[ReadOnly] public NativeArray<Triangle> triangles;
[ReadOnly] public NativeArray<float3> vertices;
[WriteOnly]
[NativeDisableParallelForRestriction]
public NativeQueue<BurstContact>.ParallelWriter contactsQueue;
// auxiliar data:
[ReadOnly] public int firstPair;
[ReadOnly] public BurstInertialFrame solverToWorld;
[ReadOnly] public BurstAffineTransform worldToSolver;
[ReadOnly] public float deltaTime;
[ReadOnly] public Oni.SolverParameters parameters;
public void Execute(int i)
{
int simplexIndex = contactPairs[firstPair + i].bodyA;
int colliderIndex = contactPairs[firstPair + i].bodyB;
var shape = shapes[colliderIndex];
if (shape.dataIndex < 0)
return;
int rigidbodyIndex = shape.rigidbodyIndex;
var header = triangleMeshHeaders[shape.dataIndex];
int simplexStart = simplexCounts.GetSimplexStartAndSize(simplexIndex, out int simplexSize);
var simplexBound = simplexBounds[simplexIndex];
BurstAffineTransform colliderToSolver = worldToSolver * transforms[colliderIndex];
// invert a full matrix here to accurately represent collider bounds scale.
var solverToCollider = math.inverse(float4x4.TRS(colliderToSolver.translation.xyz, colliderToSolver.rotation, colliderToSolver.scale.xyz));
var simplexBoundsCS = simplexBound.Transformed(solverToCollider);
float4 marginCS = new float4((shape.contactOffset + parameters.collisionMargin) / colliderToSolver.scale.xyz, 0);
BurstTriangleMesh triangleMeshShape = new BurstTriangleMesh()
{
colliderToSolver = colliderToSolver,
shape = shape
};
NativeQueue<int> queue = new NativeQueue<int>(Allocator.Temp);
queue.Enqueue(0);
while (!queue.IsEmpty())
{
int nodeIndex = queue.Dequeue();
var node = bihNodes[header.firstNode + nodeIndex];
// leaf node:
if (node.firstChild < 0)
{
// check for contact against all triangles:
for (int dataOffset = node.start; dataOffset < node.start + node.count; ++dataOffset)
{
Triangle t = triangles[header.firstTriangle + dataOffset];
float4 v1 = new float4(vertices[header.firstVertex + t.i1], 0);
float4 v2 = new float4(vertices[header.firstVertex + t.i2], 0);
float4 v3 = new float4(vertices[header.firstVertex + t.i3], 0);
BurstAabb triangleBounds = new BurstAabb(v1, v2, v3, marginCS);
if (triangleBounds.IntersectsAabb(simplexBoundsCS, shape.is2D))
{
float4 simplexBary = BurstMath.BarycenterForSimplexOfSize(simplexSize);
triangleMeshShape.tri.Cache(v1 * colliderToSolver.scale, v2 * colliderToSolver.scale, v3 * colliderToSolver.scale);
var colliderPoint = BurstLocalOptimization.Optimize(ref triangleMeshShape, positions, orientations, radii, simplices, simplexStart, simplexSize,
ref simplexBary, out float4 simplexPoint, parameters.surfaceCollisionIterations, parameters.surfaceCollisionTolerance);
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 * deltaTime + dAB <= simplexRadius + shape.contactOffset + parameters.collisionMargin)
{
contactsQueue.Enqueue(new BurstContact()
{
bodyA = simplexIndex,
bodyB = colliderIndex,
pointA = simplexBary,
pointB = colliderPoint.point,
normal = colliderPoint.normal * triangleMeshShape.shape.sign
});
}
}
}
}
else // check min and/or max children:
{
// visit min node:
if (simplexBoundsCS.min[node.axis] <= node.min)
queue.Enqueue(node.firstChild);
// visit max node:
if (simplexBoundsCS.max[node.axis] >= node.max)
queue.Enqueue(node.firstChild + 1);
}
}
}
}
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 stepTime, float substepTime, int steps, float timeLeft)
{
return inputDeps;
}
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
{
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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,168 @@
#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 steps, float timeLeft)
{
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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,181 @@
#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 steps, float timeLeft)
{
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.xyz) > 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

View File

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

View File

@@ -0,0 +1,110 @@
#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 { 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 stepTime, float substepTime, int steps, float timeLeft)
{
if (lambdas.IsCreated)
{
inputDeps = new ClearLambdasJob {lambdas = lambdas}.Schedule(lambdas.Length, 256, inputDeps);
}
return inputDeps;
}
// implemented by concrete constraint subclasses.
public abstract JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft);
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;
}
}
[BurstCompile]
public struct ClearLambdasJob : IJobParallelFor
{
public NativeArray<float> lambdas;
public void Execute(int i)
{
lambdas[i] = 0;
}
}
}
}
#endif

View File

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

View File

@@ -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 stepTime, float substepTime, int steps, float timeLeft);
JobHandle Project(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft);
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 stepTime, float substepTime, int steps, float timeLeft)
{
// initialize all batches in parallel:
if (batches.Count > 0)
{
NativeArray<JobHandle> deps = new NativeArray<JobHandle>(batches.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
for (int i = 0; i < batches.Count; ++i)
deps[i] = batches[i].enabled ? batches[i].Initialize(inputDeps, stepTime, substepTime, steps, timeLeft) : inputDeps;
JobHandle result = JobHandle.CombineDependencies(deps);
deps.Dispose();
return result;
}
return inputDeps;
}
public JobHandle Project(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
{
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, steps, timeLeft);
break;
case Oni.ConstraintParameters.EvaluationOrder.Parallel:
inputDeps = EvaluateParallel(inputDeps, stepTime, substepTime, steps, timeLeft);
break;
}
UnityEngine.Profiling.Profiler.EndSample();
return inputDeps;
}
protected virtual JobHandle EvaluateSequential(JobHandle inputDeps, float stepTime, float substepTime,int steps, float timeLeft)
{
// evaluate and apply all batches:
for (int i = 0; i < batches.Count; ++i)
{
if (batches[i].enabled)
{
inputDeps = batches[i].Evaluate(inputDeps, stepTime, substepTime, steps, timeLeft);
inputDeps = batches[i].Apply(inputDeps, substepTime);
m_Solver.ScheduleBatchedJobsIfNeeded();
}
}
return inputDeps;
}
protected virtual JobHandle EvaluateParallel(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
{
// evaluate all batches:
for (int i = 0; i < batches.Count; ++i)
if (batches[i].enabled)
{
inputDeps = batches[i].Evaluate(inputDeps, stepTime, substepTime, steps, timeLeft);
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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,222 @@
#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 steps, float timeLeft)
{
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].x;
float maxLength = restLengths[c].y;
// (ni:constraint gradient, di:desired lenght)
NativeArray<float4> ni = new NativeArray<float4>(numEdges, Allocator.Temp);
// calculate ai (subdiagonals), bi (diagonals) and ci (superdiagonals):
NativeArray<float3> diagonals = new NativeArray<float3>(numEdges, Allocator.Temp);
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);
ni[i] = new float4(diff/(distance + BurstMath.epsilon));
}
// calculate ai, bi and ci (superdiagonals):
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
}
// solve step #1, forward sweep:
// reuse diagonals.xy to store sweep results ci_ and di_:
for (int i = 0; i < numEdges; ++i)
{
int edge = first + i;
float4 p1 = positions[particleIndices[edge]];
float4 p2 = positions[particleIndices[edge + 1]];
float cip_ = (i > 0) ? diagonals[i - 1].x : 0;
float dip_ = (i > 0) ? diagonals[i - 1].y : 0;
float den = diagonals[i].y - cip_ * diagonals[i].x;
float3 d = diagonals[i];
if (math.abs(den) > BurstMath.epsilon)
{
float distance = math.distance(p1, p2);
float correction = 0;
if (distance >= maxLength)
correction = distance - maxLength;
else if (distance <= minLength)
correction = distance - minLength;
d.xy = new float2(d.z / den,
(correction - dip_ * d.x) / den);
}
else
d.xy = float2.zero;
diagonals[i] = d;
}
// solve step #2, backward sweep. reuse diagonals.z to store solution xi:
for (int i = numEdges - 1; i >= 0; --i)
{
float xi_ = (i < numEdges - 1) ? diagonals[i + 1].z : 0;
float3 d = diagonals[i];
d.z = d.y - d.x * xi_;
diagonals[i] = d;
}
// 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) ? diagonals[i - 1].z : 0;
float nxi = (i < numIndices[c] - 1) ? diagonals[i].z : 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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,31 @@
#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()
{
return ((BurstSolverImpl)solver).abstraction.colliderContacts.count;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,344 @@
#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 stepTime, float substepTime, int steps, float timeLeft)
{
var updateContacts = new UpdateContactsJob()
{
prevPositions = solverImplementation.prevPositions,
prevOrientations = solverImplementation.prevOrientations,
velocities = solverImplementation.velocities,
radii = solverImplementation.principalRadii,
invMasses = solverImplementation.invMasses,
invRotationalMasses = solverImplementation.invRotationalMasses,
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).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.contactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame
};
return updateContacts.Schedule(((BurstSolverImpl)constraints.solver).abstraction.colliderContacts.count, 128, inputDeps);
}
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
{
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).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.contactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
constraintParameters = parameters,
solverParameters = solverAbstraction.parameters,
steps = steps,
timeLeft = timeLeft,
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).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
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 generated 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<float> invRotationalMasses;
[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<ContactEffectiveMasses> effectiveMasses;
public NativeArray<BurstContact> contacts;
[ReadOnly] public BurstInertialFrame inertialFrame;
public void Execute(int i)
{
var contact = contacts[i];
var efMasses = effectiveMasses[i];
int simplexStart = simplexCounts.GetSimplexStartAndSize(contact.bodyA, out int simplexSize);
// 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;
float simplexInvRotationalMass = 0;
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];
simplexInvRotationalMass = invRotationalMasses[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);
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;
// calculate contact tangent (first friction direction) using relative velocity:
contact.CalculateTangent(relativeVelocity);
// calculate A's contact mass.
float4 invInertiaTensor = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadius, simplexInvRotationalMass) + new float4(BurstMath.epsilon));
efMasses.CalculateContactMassesA(simplexInvMass, invInertiaTensor, simplexPrevPosition, simplexPrevOrientation, contactPoint, contact.normal, contact.tangent, contact.bitangent, rollingContacts);
// calculate B's contact mass.
if (rigidbodyIndex >= 0)
efMasses.CalculateContactMassesB(rigidbodies[rigidbodyIndex], inertialFrame.frame, contact.pointB, contact.normal, contact.tangent, contact.bitangent);
else
efMasses.ClearContactMassesB();
contacts[i] = contact;
effectiveMasses[i] = efMasses;
}
}
[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 NativeArray<ContactEffectiveMasses> effectiveMasses;
[ReadOnly] public BurstInertialFrame inertialFrame;
[ReadOnly] public Oni.ConstraintParameters constraintParameters;
[ReadOnly] public Oni.SolverParameters solverParameters;
[ReadOnly] public float stepTime;
[ReadOnly] public float substepTime;
[ReadOnly] public float timeLeft;
[ReadOnly] public int steps;
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].isTrigger)
continue;
// Get the rigidbody index (might be < 0, in that case there's no rigidbody present)
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
float frameEnd = stepTime * steps;
float substepsToEnd = timeLeft / substepTime;
// Combine collision materials (use material from first particle in simplex)
BurstCollisionMaterial material = CombineCollisionMaterials(simplices[simplexStart], colliderIndex);
// 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, substepsToEnd);
posA += -contact.normal * simplexRadius;
float4 posB = contact.pointB;
if (rigidbodyIndex >= 0)
posB += BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, contact.pointB, rigidbodies, rigidbodyLinearDeltas, rigidbodyAngularDeltas, inertialFrame) * frameEnd;
// adhesion:
float lambda = contact.SolveAdhesion(effectiveMasses[i].TotalNormalInvMass, posA, posB, material.stickDistance, material.stickiness, stepTime);
// depenetration:
lambda += contact.SolvePenetration(effectiveMasses[i].TotalNormalInvMass, posA, posB, solverParameters.maxDepenetration * stepTime);
// Apply normal impulse to both simplex and rigidbody:
if (math.abs(lambda) > BurstMath.epsilon)
{
float4 delta = lambda * contact.normal * BurstMath.BaryScale(contact.pointA) / substepsToEnd;
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 / frameEnd * 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

View File

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

View File

@@ -0,0 +1,33 @@
#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()
{
return ((BurstSolverImpl)solver).abstraction.colliderContacts.count;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,280 @@
#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 stepTime, float substepTime, int steps, float timeLeft)
{
return inputDeps;
}
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
{
var projectConstraints = new FrictionConstraintsBatchJob()
{
positions = solverImplementation.positions,
prevPositions = solverImplementation.prevPositions,
orientations = solverImplementation.orientations,
prevOrientations = solverImplementation.prevOrientations,
invMasses = solverImplementation.invMasses,
invRotationalMasses = solverImplementation.invRotationalMasses,
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).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.contactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
inertialFrame = ((BurstSolverImpl)constraints.solver).inertialFrame,
steps = steps,
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).abstraction.colliderContacts.AsNativeArray<BurstContact>(),
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<float> invRotationalMasses;
[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 NativeArray<ContactEffectiveMasses> effectiveMasses;
[ReadOnly] public BurstInertialFrame inertialFrame;
[ReadOnly] public float stepTime;
[ReadOnly] public float substepTime;
[ReadOnly] public int steps;
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].isTrigger)
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;
float invRotationalMassA = 0;
quaternion orientationA = new quaternion(0, 0, 0, 0);
float4 simplexRadiiA = float4.zero;
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];
invRotationalMassA += invRotationalMasses[particleIndex] * contact.pointA[j];
orientationA.value += orientations[particleIndex].value * contact.pointA[j];
simplexRadiiA += radii[particleIndex] * contact.pointA[j];
}
float4 relativeVelocity = linearVelocityA;
// Add particle angular velocity if rolling contacts are enabled:
if (material.rollingContacts > 0)
{
rA = -contact.normal * BurstMath.EllipsoidRadius(contact.normal, orientationA, simplexRadiiA.xyz);
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);
}
// Determine impulse magnitude:
float2 impulses = contact.SolveFriction(effectiveMasses[i].TotalTangentInvMass, effectiveMasses[i].TotalBitangentInvMass, 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];
deltas[particleIndex] += (tangentImpulse * effectiveMasses[i].tangentInvMassA + bitangentImpulse * effectiveMasses[i].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:
float4 invInertiaTensor = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadiiA, invRotationalMassA) + new float4(BurstMath.epsilon));
float4x4 solverInertiaA = BurstMath.TransformInertiaTensor(invInertiaTensor, 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

View File

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

View File

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

View File

@@ -0,0 +1,553 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
namespace Obi
{
public class BurstDensityConstraints : BurstConstraintsImpl<BurstDensityConstraintsBatch>
{
public NativeList<int> fluidParticles;
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 steps, float timeLeft)
{
return EvaluateParallel(inputDeps, stepTime, substepTime, steps, timeLeft);
}
protected override JobHandle EvaluateParallel(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
{
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, steps, timeLeft);
m_Solver.ScheduleBatchedJobsIfNeeded();
}
}
// calculate per-particle density lambdas:
inputDeps = CalculateLambdas(inputDeps, substepTime);
// calculate viscosity/vorticity:
for (int i = 0; i < batches.Count; ++i)
{
if (batches[i].enabled)
{
inputDeps = batches[i].ViscosityAndVorticity(inputDeps);
m_Solver.ScheduleBatchedJobsIfNeeded();
}
}
// apply viscosity/vorticity positional deltas:
var app = new ApplyPositionDeltasJob()
{
fluidParticles = fluidParticles,
positions = m_Solver.positions,
deltas = m_Solver.positionDeltas,
counts = m_Solver.positionConstraintCounts,
anisotropies = m_Solver.anisotropies,
normals = m_Solver.normals,
fluidData = m_Solver.fluidData,
matchingRotations = m_Solver.orientationDeltas,
linearFromAngular = m_Solver.restPositions,
};
inputDeps = app.Schedule(fluidParticles.Length, 64, inputDeps);
// apply density positional deltas:
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 CalculateVelocityCorrections(JobHandle inputDeps, float deltaTime)
{
for (int i = 0; i < batches.Count; ++i)
{
if (batches[i].enabled)
{
inputDeps = batches[i].CalculateNormals(inputDeps, deltaTime);
m_Solver.ScheduleBatchedJobsIfNeeded();
}
}
return inputDeps;
}
public JobHandle ApplyVelocityCorrections(JobHandle inputDeps, float deltaTime)
{
inputDeps = ApplyAtmosphere(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 inputDeps;
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,
fluidData = m_Solver.fluidData,
massCenters = m_Solver.normals,
prevMassCenters = m_Solver.renderablePositions,
moments = m_Solver.anisotropies
};
inputDeps = clearData.Schedule(fluidParticles.Length, 64, inputDeps);
// update fluid interactions:
var updateInteractions = new UpdateInteractionsJob()
{
pairs = m_Solver.fluidInteractions,
positions = m_Solver.positions,
fluidMaterials = m_Solver.fluidMaterials,
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,
positions = m_Solver.positions,
prevPositions = m_Solver.prevPositions,
matchingRotations = m_Solver.restPositions.Reinterpret<quaternion>(),
principalRadii = m_Solver.principalRadii,
fluidMaterials = m_Solver.fluidMaterials,
densityKernel = new Poly6Kernel(m_Solver.abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
gradientKernel = new SpikyKernel(m_Solver.abstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
fluidData = m_Solver.fluidData,
massCenters = m_Solver.normals,
prevMassCenters = m_Solver.renderablePositions,
moments = m_Solver.anisotropies,
deltas = m_Solver.positionDeltas,
counts = m_Solver.positionConstraintCounts,
solverParams = m_Solver.abstraction.parameters
};
return calculateLambdas.Schedule(fluidParticles.Length,64,inputDeps);
}
private JobHandle ApplyAtmosphere(JobHandle inputDeps, float deltaTime)
{
var conf = new ApplyAtmosphereJob
{
fluidParticles = fluidParticles,
wind = m_Solver.wind,
fluidInterface = m_Solver.fluidInterface,
fluidMaterials2 = m_Solver.fluidMaterials2,
principalRadii = m_Solver.principalRadii,
normals = m_Solver.normals,
fluidData = m_Solver.fluidData,
velocities = m_Solver.velocities,
angularVelocities = m_Solver.angularVelocities,
vorticity = m_Solver.restOrientations.Reinterpret<float4>(),
vorticityAccelerations = m_Solver.orientationDeltas.Reinterpret<float4>(),
linearAccelerations = m_Solver.positionDeltas,
linearFromAngular = m_Solver.restPositions,
angularDiffusion = m_Solver.anisotropies,
positions = m_Solver.positions,
prevPositions = m_Solver.prevPositions,
dt = deltaTime,
solverParams = m_Solver.abstraction.parameters
};
return conf.Schedule(fluidParticles.Length, 64, inputDeps);
}
private JobHandle AverageSmoothPositions(JobHandle inputDeps)
{
var average = new AverageSmoothPositionsJob()
{
fluidParticles = fluidParticles,
renderablePositions = m_Solver.renderablePositions,
anisotropies = m_Solver.anisotropies
};
return average.Schedule(fluidParticles.Length, 64, inputDeps);
}
private JobHandle AverageAnisotropy(JobHandle inputDeps)
{
var average = new AverageAnisotropyJob()
{
fluidParticles = fluidParticles,
renderablePositions = m_Solver.renderablePositions,
renderableOrientations = m_Solver.renderableOrientations,
principalRadii = m_Solver.principalRadii,
anisotropies = m_Solver.anisotropies,
maxAnisotropy = m_Solver.abstraction.parameters.maxAnisotropy,
renderableRadii = m_Solver.renderableRadii,
fluidData = m_Solver.fluidData,
life = m_Solver.life,
solverParams = m_Solver.abstraction.parameters
};
return average.Schedule(fluidParticles.Length, 64, inputDeps);
}
[BurstCompile]
public struct ClearFluidDataJob : IJobParallelFor
{
[ReadOnly] public NativeList<int> fluidParticles;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> massCenters;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevMassCenters;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> moments;
public void Execute(int i)
{
int p = fluidParticles[i];
fluidData[p] = float4.zero;
massCenters[p] = float4.zero;
prevMassCenters[p] = float4.zero;
moments[p] = float4x4.zero;
}
}
[BurstCompile]
public struct UpdateInteractionsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<float4> fluidMaterials;
[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 = new float4((positions[pair.particleA] - positions[pair.particleB]).xyz,0);
float distance = math.length(pair.gradient);
pair.gradient /= distance + math.FLT_MIN_NORMAL;
// calculate and store average density and gradient kernels:
pair.avgKernel = (densityKernel.W(distance, fluidMaterials[pair.particleA].x) +
densityKernel.W(distance, fluidMaterials[pair.particleB].x)) * 0.5f;
pair.avgGradient = (gradientKernel.W(distance, fluidMaterials[pair.particleA].x) +
gradientKernel.W(distance, fluidMaterials[pair.particleB].x)) * 0.5f;
pairs[i] = pair;
}
}
[BurstCompile]
public struct CalculateLambdasJob : IJobParallelFor
{
[ReadOnly] public NativeList<int> fluidParticles;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<float4> prevPositions;
[ReadOnly] public NativeArray<float4> principalRadii;
[ReadOnly] public NativeArray<float4> fluidMaterials;
[ReadOnly] public Poly6Kernel densityKernel;
[ReadOnly] public SpikyKernel gradientKernel;
[ReadOnly] public Oni.SolverParameters solverParams;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> massCenters;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevMassCenters;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> moments;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<quaternion> matchingRotations;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
public void Execute(int p)
{
int i = fluidParticles[p];
float restVolume = math.pow(principalRadii[i].x * 2, 3 - (int)solverParams.mode);
float4 data = fluidData[i];
float grad = restVolume * gradientKernel.W(0, fluidMaterials[i].x);
// self particle contribution to density, gradient and mass centers:
data += new float4(densityKernel.W(0, fluidMaterials[i].x), 0, grad, grad * grad + data[2] * data[2]);
massCenters[i] += new float4(positions[i].xyz, 1) / positions[i].w;
prevMassCenters[i] += new float4(prevPositions[i].xyz, 1) / positions[i].w;
// usually, we'd weight density by mass (density contrast formulation) by dividing by invMass. Then, multiply by invMass when
// calculating the state equation (density / restDensity - 1, restDensity = mass / volume, so density * invMass * restVolume - 1
// We end up with density / invMass * invMass * restVolume - 1, invMass cancels out.
float constraint = math.max(0, data[0] * restVolume - 1) * fluidMaterials[i].w;
// calculate lambda:
data[1] = -constraint / (positions[i].w * data[3] + math.FLT_MIN_NORMAL);
fluidData[i] = data;
// get total neighborhood mass:
float M = massCenters[i][3];
massCenters[i] /= massCenters[i][3];
prevMassCenters[i] /= prevMassCenters[i][3];
// update moments:
moments[i] += (BurstMath.multrnsp4(positions[i], prevPositions[i]) + float4x4.identity * math.pow(principalRadii[i].x, 2) * 0.001f) / positions[i].w;
moments[i] -= M * BurstMath.multrnsp4(massCenters[i], prevMassCenters[i]);
// extract neighborhood orientation delta:
matchingRotations[i] = BurstMath.ExtractRotation(moments[i], quaternion.identity, 5);
// viscosity and vorticity:
float4 viscGoal = new float4(massCenters[i].xyz + math.rotate(matchingRotations[i], (prevPositions[i] - prevMassCenters[i]).xyz), 0);
deltas[i] += (viscGoal - positions[i]) * fluidMaterials[i].z;
counts[i]++;
}
}
[BurstCompile]
public struct ApplyPositionDeltasJob : IJobParallelFor
{
[ReadOnly] public NativeList<int> fluidParticles;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> normals;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> anisotropies;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<quaternion> matchingRotations;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> linearFromAngular;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
public void Execute(int p)
{
int i = fluidParticles[p];
if (counts[i] > 0)
{
positions[i] += new float4(deltas[i].xyz,0) / counts[i];
deltas[i] = float4.zero;
counts[i] = 0;
}
normals[i] = float4.zero;
anisotropies[i] = float4x4.zero;
linearFromAngular[i] = float4.zero;
matchingRotations[i] = new quaternion(0, 0, 0, 0);
// zero out fluidData.z in preparation to accumulate relative velocity.
float4 data = fluidData[i];
data.z = 0;
fluidData[i] = data;
}
}
[BurstCompile]
public struct ApplyAtmosphereJob : IJobParallelFor
{
[ReadOnly] public NativeList<int> fluidParticles;
[ReadOnly] public NativeArray<float4> wind;
[ReadOnly] public NativeArray<float4> fluidInterface;
[ReadOnly] public NativeArray<float4> fluidMaterials2;
[ReadOnly] public NativeArray<float4> principalRadii;
[ReadOnly] public NativeArray<float4> normals;
[ReadOnly] public NativeArray<float4> fluidData;
[ReadOnly] public NativeArray<float4> linearFromAngular;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> positions;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevPositions;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> linearAccelerations;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> vorticityAccelerations;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> vorticity;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> angularDiffusion;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> angularVelocities;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> velocities;
[ReadOnly] public float dt;
[ReadOnly] public Oni.SolverParameters solverParams;
public void Execute(int p)
{
int i = fluidParticles[p];
float restVolume = math.pow(principalRadii[i].x * 2, 3 - (int)solverParams.mode);
//atmospheric drag:
float4 velocityDiff = velocities[i] - wind[i];
// particles near the surface should experience drag:
velocities[i] -= fluidInterface[i].x * velocityDiff * math.max(0, 1 - fluidData[i][0] * restVolume) * dt;
// ambient pressure:
velocities[i] += fluidInterface[i].y * normals[i] * dt;
// angular accel due to baroclinity:
angularVelocities[i] += new float4(fluidMaterials2[i].z * math.cross(-normals[i].xyz, -velocityDiff.xyz), 0) * dt;
angularVelocities[i] -= fluidMaterials2[i].w * angularDiffusion[i].c0;
// micropolar vorticity:
velocities[i] += fluidMaterials2[i].x * linearAccelerations[i] * dt;
vorticity[i] += fluidMaterials2[i].x * (vorticityAccelerations[i] * 0.5f - vorticity[i]) * dt;
vorticity[i] -= fluidMaterials2[i].y * angularDiffusion[i].c1;
linearAccelerations[i] = float4.zero;
vorticityAccelerations[i] = float4.zero;
angularDiffusion[i] = float4x4.zero;
// we want to add together linear and angular velocity fields and use result to advect particles without modifying either field:
positions[i] += new float4(linearFromAngular[i].xyz * dt,0);
prevPositions[i] += new float4(linearFromAngular[i].xyz * dt, 0);
}
}
[BurstCompile]
public struct AverageSmoothPositionsJob : IJobParallelFor
{
[ReadOnly] public NativeList<int> fluidParticles;
[ReadOnly] public NativeArray<float4> renderablePositions;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4x4> anisotropies;
public void Execute(int p)
{
int i = fluidParticles[p];
var smoothPos = anisotropies[i];
if (smoothPos.c3.w > 0)
smoothPos.c3 /= smoothPos.c3.w;
else
smoothPos.c3.xyz = renderablePositions[i].xyz;
anisotropies[i] = smoothPos;
}
}
[BurstCompile]
public struct AverageAnisotropyJob : IJobParallelFor
{
[ReadOnly] public NativeList<int> fluidParticles;
[ReadOnly] public NativeArray<float4> principalRadii;
[ReadOnly] public float maxAnisotropy;
[ReadOnly] public NativeArray<float4x4> anisotropies;
[ReadOnly] public NativeArray<float> life;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> renderablePositions;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<quaternion> renderableOrientations;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> renderableRadii;
[ReadOnly] public Oni.SolverParameters solverParams;
public void Execute(int p)
{
int i = fluidParticles[p];
if (anisotropies[i].c3.w > 0 && (anisotropies[i].c0[0] + anisotropies[i].c1[1] + anisotropies[i].c2[2]) > 0.01f)
{
float3 singularValues;
float3x3 u;
BurstMath.EigenSolve(math.float3x3(anisotropies[i] / anisotropies[i].c3.w), out singularValues, out u);
float max = singularValues[0];
float3 s = math.max(singularValues,new float3(max / maxAnisotropy)) / max * principalRadii[i].x;
renderableOrientations[i] = quaternion.LookRotationSafe(u.c2,u.c1);
renderableRadii[i] = new float4(s.xyz,1);
}
else
{
float radius = principalRadii[i].x / maxAnisotropy;
renderableOrientations[i] = quaternion.identity;
renderableRadii[i] = new float4(radius,radius,radius,1);
float4 data = fluidData[i];
data.x = 1 / math.pow(math.abs(radius * 2), 3 - (int)solverParams.mode); // normal volume of an isolated particle.
fluidData[i] = data;
}
renderablePositions[i] = math.lerp(renderablePositions[i], anisotropies[i].c3, math.min((maxAnisotropy - 1)/3.0f,1));
// inactive particles have radii.w == 0, set it right away for particles killed during this frame
// to keep them from being rendered during this frame instead of waiting to do it at the start of next sim step:
float4 radii = renderableRadii[i];
radii.w = life[i] <= 0 ? 0 : radii.w;
renderableRadii[i] = radii;
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,477 @@
#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 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 stepTime, float substepTime, int steps, float timeLeft)
{
return inputDeps;
}
public override JobHandle Evaluate(JobHandle inputDeps, float stepTime, float substepTime, int steps, float timeLeft)
{
// update densities and gradients:
var updateDensities = new UpdateDensitiesJob()
{
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
positions = solverImplementation.positions,
prevPositions = solverImplementation.prevPositions,
principalRadii = solverImplementation.principalRadii,
fluidMaterials = solverImplementation.fluidMaterials,
fluidData = solverImplementation.fluidData,
moments = solverImplementation.anisotropies,
massCenters = solverImplementation.normals,
prevMassCenters = solverImplementation.renderablePositions,
densityKernel = new Poly6Kernel(solverAbstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
batchData = batchData,
solverParams = solverAbstraction.parameters
};
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()
{
principalRadii = solverImplementation.principalRadii,
fluidMaterials = solverImplementation.fluidMaterials,
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
densityKernel = new Poly6Kernel(solverAbstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
positions = solverImplementation.positions,
fluidData = solverImplementation.fluidData,
batchData = batchData,
solverParams = solverAbstraction.parameters,
sorFactor = parameters.SORFactor
};
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
return apply.Schedule(batchData.workItemCount, batchCount, inputDeps);
}
public JobHandle CalculateNormals(JobHandle inputDeps, float deltaTime)
{
int batchCount = batchData.isLast ? batchData.workItemCount : 1;
var vorticity = new NormalsJob()
{
invMasses = solverImplementation.invMasses,
positions = solverImplementation.positions,
principalRadii = solverImplementation.principalRadii,
fluidMaterials = solverImplementation.fluidMaterials,
fluidMaterials2 = solverImplementation.fluidMaterials2,
fluidData = solverImplementation.fluidData,
fluidInterface = solverImplementation.fluidInterface,
velocities = solverImplementation.velocities,
angularVelocities = solverImplementation.angularVelocities,
vorticityAccelerations = solverImplementation.orientationDeltas.Reinterpret<float4>(),
vorticity = solverImplementation.restOrientations.Reinterpret<float4>(),
linearAccelerations = solverImplementation.positionDeltas,
linearFromAngular = solverImplementation.restPositions,
angularDiffusion = solverImplementation.anisotropies,
userData = solverImplementation.userData,
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
normals = solverImplementation.normals,
densityKernel = new Poly6Kernel(solverAbstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
gradKernel = new SpikyKernel(solverAbstraction.parameters.mode == Oni.SolverParameters.Mode.Mode2D),
solverParams = solverAbstraction.parameters,
batchData = batchData,
dt = deltaTime,
};
return vorticity.Schedule(batchData.workItemCount, batchCount, inputDeps);
}
public JobHandle ViscosityAndVorticity(JobHandle inputDeps)
{
var eta = new ViscosityVorticityJob()
{
positions = solverImplementation.positions,
prevPositions = solverImplementation.prevPositions,
matchingRotations = solverImplementation.restPositions.Reinterpret<quaternion>(),
pairs = ((BurstSolverImpl)constraints.solver).fluidInteractions,
massCenters = solverImplementation.normals,
prevMassCenters = solverImplementation.renderablePositions,
fluidParams = solverImplementation.fluidMaterials,
deltas = solverImplementation.positionDeltas,
counts = solverImplementation.positionConstraintCounts,
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,
anisotropies = solverImplementation.anisotropies,
fluidMaterials = solverImplementation.fluidMaterials,
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,
anisotropies = solverImplementation.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<float4> prevPositions;
[ReadOnly] public NativeArray<float4> fluidMaterials;
[ReadOnly] public NativeArray<float4> principalRadii;
[ReadOnly] public NativeArray<FluidInteraction> pairs;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> moments;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> massCenters;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevMassCenters;
[ReadOnly] public Poly6Kernel densityKernel;
[ReadOnly] public BatchData batchData;
[ReadOnly] public Oni.SolverParameters solverParams;
public void Execute(int workItemIndex)
{
int start, end;
batchData.GetConstraintRange(workItemIndex, out start, out end);
for (int i = start; i < end; ++i)
{
var pair = pairs[i];
float restVolumeA = math.pow(principalRadii[pair.particleA].x * 2, 3 - (int)solverParams.mode);
float restVolumeB = math.pow(principalRadii[pair.particleB].x * 2, 3 - (int)solverParams.mode);
float gradA = restVolumeB * pair.avgGradient;
float gradB = restVolumeA * pair.avgGradient;
float vA = restVolumeB / restVolumeA;
float vB = restVolumeA / restVolumeB;
// accumulate pbf data (density, gradients):
fluidData[pair.particleA] += new float4(vA * pair.avgKernel, 0, gradA, gradA * gradA);
fluidData[pair.particleB] += new float4(vB * pair.avgKernel, 0, gradB, gradB * gradB);
// accumulate masses for COMs and moment matrices:
float wAvg = pair.avgKernel / ((densityKernel.W(0, fluidMaterials[pair.particleA].x) + densityKernel.W(0, fluidMaterials[pair.particleB].x)) * 0.5f);
massCenters[pair.particleA] += wAvg * new float4(positions[pair.particleB].xyz, 1) / positions[pair.particleB].w;
massCenters[pair.particleB] += wAvg * new float4(positions[pair.particleA].xyz, 1) / positions[pair.particleA].w;
prevMassCenters[pair.particleA] += wAvg * new float4(prevPositions[pair.particleB].xyz, 1) / positions[pair.particleB].w;
prevMassCenters[pair.particleB] += wAvg * new float4(prevPositions[pair.particleA].xyz, 1) / positions[pair.particleA].w;
moments[pair.particleA] += wAvg * (BurstMath.multrnsp4(positions[pair.particleB], prevPositions[pair.particleB]) + float4x4.identity * math.pow(principalRadii[pair.particleB].x, 2) * 0.001f) / positions[pair.particleB].w;
moments[pair.particleB] += wAvg * (BurstMath.multrnsp4(positions[pair.particleA], prevPositions[pair.particleA]) + float4x4.identity * math.pow(principalRadii[pair.particleA].x, 2) * 0.001f) / positions[pair.particleA].w;
}
}
}
[BurstCompile]
public struct ApplyDensityConstraintsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float4> principalRadii;
[ReadOnly] public NativeArray<float4> fluidMaterials;
[ReadOnly] public NativeArray<FluidInteraction> pairs;
[ReadOnly] public Poly6Kernel densityKernel;
[ReadOnly] public CohesionKernel cohesionKernel;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> positions;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
[ReadOnly] public BatchData batchData;
[ReadOnly] public float sorFactor;
[ReadOnly] public Oni.SolverParameters solverParams;
public void Execute(int workItemIndex)
{
int start, end;
batchData.GetConstraintRange(workItemIndex, out start, out end);
for (int i = start; i < end; ++i)
{
var pair = pairs[i];
float restVolumeA = math.pow(principalRadii[pair.particleA].x * 2, 3 - (int)solverParams.mode);
float restVolumeB = math.pow(principalRadii[pair.particleB].x * 2, 3 - (int)solverParams.mode);
float dist = math.length(positions[pair.particleA].xyz - positions[pair.particleB].xyz); // TODO: FIX! we cant read positions while we are writing to them.
// calculate tensile instability correction factor:
float cAvg = (cohesionKernel.W(dist, fluidMaterials[pair.particleA].x * 1.4f) + cohesionKernel.W(dist, fluidMaterials[pair.particleB].x * 1.4f)) * 0.5f;
float st = 0.2f * cAvg * (1 - math.saturate(math.abs(fluidMaterials[pair.particleA].y - fluidMaterials[pair.particleB].y))) * (fluidMaterials[pair.particleA].y + fluidMaterials[pair.particleB].y) * 0.5f;
float scorrA = -st / (positions[pair.particleA].w * fluidData[pair.particleA][3] + math.FLT_MIN_NORMAL);
float scorrB = -st / (positions[pair.particleB].w * fluidData[pair.particleB][3] + math.FLT_MIN_NORMAL);
// calculate position delta:
float4 delta = pair.gradient * pair.avgGradient * ((fluidData[pair.particleA][1] + scorrA) * restVolumeB + (fluidData[pair.particleB][1] + scorrB) * restVolumeA) * sorFactor;
delta.w = 0;
positions[pair.particleA] += delta * positions[pair.particleA].w;
positions[pair.particleB] -= delta * positions[pair.particleB].w;
}
}
}
[BurstCompile]
public struct ViscosityVorticityJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<float4> prevPositions;
[ReadOnly] public NativeArray<quaternion> matchingRotations;
[ReadOnly] public NativeArray<float4> fluidParams;
[ReadOnly] public NativeArray<FluidInteraction> pairs;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> massCenters;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> prevMassCenters;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> deltas;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
[ReadOnly] public BatchData batchData;
public void Execute(int workItemIndex)
{
int start, end;
batchData.GetConstraintRange(workItemIndex, out start, out end);
for (int i = start; i < end; ++i)
{
var pair = pairs[i];
float visc = math.min(fluidParams[pair.particleA].z, fluidParams[pair.particleB].z);
// viscosity:
float4 goalA = new float4(massCenters[pair.particleB].xyz + math.rotate(matchingRotations[pair.particleB], (prevPositions[pair.particleA] - prevMassCenters[pair.particleB]).xyz), 0);
float4 goalB = new float4(massCenters[pair.particleA].xyz + math.rotate(matchingRotations[pair.particleA], (prevPositions[pair.particleB] - prevMassCenters[pair.particleA]).xyz), 0);
deltas[pair.particleA] += (goalA - positions[pair.particleA]) * visc;
deltas[pair.particleB] += (goalB - positions[pair.particleB]) * visc;
counts[pair.particleA]++;
counts[pair.particleB]++;
}
}
}
[BurstCompile]
public struct NormalsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float> invMasses;
[ReadOnly] public NativeArray<float4> velocities;
[ReadOnly] public NativeArray<float4> angularVelocities;
[ReadOnly] public NativeArray<float4> positions;
[ReadOnly] public NativeArray<float4> vorticity;
[ReadOnly] public NativeArray<float4> principalRadii;
[ReadOnly] public NativeArray<float4> fluidMaterials;
[ReadOnly] public NativeArray<float4> fluidMaterials2;
[ReadOnly] public NativeArray<float4> fluidInterface;
[ReadOnly] public NativeArray<FluidInteraction> pairs;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> fluidData;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> userData;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4> normals;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> linearAccelerations;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> vorticityAccelerations;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4> linearFromAngular;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<float4x4> angularDiffusion;
[ReadOnly] public Poly6Kernel densityKernel;
[ReadOnly] public SpikyKernel gradKernel;
[ReadOnly] public BatchData batchData;
[ReadOnly] public Oni.SolverParameters solverParams;
[ReadOnly] public float dt;
public void Execute(int workItemIndex)
{
int start, end;
batchData.GetConstraintRange(workItemIndex, out start, out end);
for (int i = start; i < end; ++i)
{
var pair = pairs[i];
float restVolumeA = math.pow(principalRadii[pair.particleA].x * 2, 3 - (int)solverParams.mode);
float restVolumeB = math.pow(principalRadii[pair.particleB].x * 2, 3 - (int)solverParams.mode);
float invDensityA = invMasses[pair.particleA] / fluidData[pair.particleA].x;
float invDensityB = invMasses[pair.particleB] / fluidData[pair.particleB].x;
float3 relVel = velocities[pair.particleA].xyz - velocities[pair.particleB].xyz;
float3 relAng = angularVelocities[pair.particleA].xyz - angularVelocities[pair.particleB].xyz;
float3 relVort = vorticity[pair.particleA].xyz - vorticity[pair.particleB].xyz;
float4 d = new float4((positions[pair.particleA] - positions[pair.particleB]).xyz,0);
float dist = math.length(d);
float avgGrad = (gradKernel.W(dist, fluidMaterials[pair.particleA].x) +
gradKernel.W(dist, fluidMaterials[pair.particleB].x)) * 0.5f;
float avgKern = (densityKernel.W(dist, fluidMaterials[pair.particleA].x) +
densityKernel.W(dist, fluidMaterials[pair.particleB].x)) * 0.5f;
float avgNorm = (densityKernel.W(0, fluidMaterials[pair.particleA].x) +
densityKernel.W(0, fluidMaterials[pair.particleB].x)) * 0.5f;
// property diffusion:
float diffusionSpeed = (fluidInterface[pair.particleA].w + fluidInterface[pair.particleB].w) * avgKern * dt;
float4 userDelta = (userData[pair.particleB] - userData[pair.particleA]) * solverParams.diffusionMask * diffusionSpeed;
userData[pair.particleA] += restVolumeB / restVolumeA * userDelta;
userData[pair.particleB] -= restVolumeA / restVolumeB * userDelta;
// calculate color field normal:
float4 normGrad = d / (dist + BurstMath.epsilon);
float4 vgrad = normGrad * avgGrad;
float radius = (fluidMaterials[pair.particleA].x + fluidMaterials[pair.particleB].x) * 0.5f;
normals[pair.particleA] += vgrad * radius * restVolumeB;
normals[pair.particleB] -= vgrad * radius * restVolumeA;
// measure relative velocity for foam generation:
float4 dataA = fluidData[pair.particleA];
float4 dataB = fluidData[pair.particleB];
float relVelMag = math.length(relVel) + BurstMath.epsilon;
float avgVelDiffKernel = 1 - math.min(1, dist / (radius + BurstMath.epsilon));
float rv = relVelMag * (1 - math.dot(relVel / relVelMag, normGrad.xyz)) * avgVelDiffKernel;
dataA.z += rv;
dataB.z += rv;
fluidData[pair.particleA] = dataA;
fluidData[pair.particleB] = dataB;
// micropolar: curl of linear/angular velocity:
float3 velCross = math.cross(relVel, vgrad.xyz);
float3 vortCross = math.cross(relVort, vgrad.xyz);
linearAccelerations[pair.particleA] += new float4(vortCross / invMasses[pair.particleB] * invDensityA, 0);
linearAccelerations[pair.particleB] += new float4(vortCross / invMasses[pair.particleA] * invDensityB, 0);
vorticityAccelerations[pair.particleA] += new float4(velCross / invMasses[pair.particleB] * invDensityA, 0);
vorticityAccelerations[pair.particleB] += new float4(velCross / invMasses[pair.particleA] * invDensityB, 0);
// angular diffusion:
float4x4 diffA = angularDiffusion[pair.particleA];
float4x4 diffB = angularDiffusion[pair.particleB];
diffA.c0 += new float4(relAng * avgKern / invMasses[pair.particleB] * invDensityA, 0);
diffB.c0 -= new float4(relAng * avgKern / invMasses[pair.particleA] * invDensityB, 0);
diffA.c1 += new float4(relVort * avgKern / invMasses[pair.particleB] * invDensityA, 0);
diffB.c1 -= new float4(relVort * avgKern / invMasses[pair.particleA] * invDensityB, 0);
angularDiffusion[pair.particleA] = diffA;
angularDiffusion[pair.particleB] = diffB;
// linear velocity due to baroclinity:
linearFromAngular[pair.particleA] += new float4(math.cross(angularVelocities[pair.particleB].xyz, d.xyz) * avgKern / avgNorm, 0);
linearFromAngular[pair.particleB] -= new float4(math.cross(angularVelocities[pair.particleA].xyz, d.xyz) * avgKern / avgNorm, 0);
}
}
}
[BurstCompile]
public struct AccumulateSmoothPositionsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float4> renderablePositions;
[ReadOnly] public NativeArray<float4> fluidMaterials;
[ReadOnly] public Poly6Kernel densityKernel;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4x4> anisotropies;
[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];
float distance = math.length((renderablePositions[pair.particleA] - renderablePositions[pair.particleB]).xyz);
pair.avgKernel = (densityKernel.W(distance, fluidMaterials[pair.particleA].x) +
densityKernel.W(distance, fluidMaterials[pair.particleB].x)) * 0.5f;
var A = anisotropies[pair.particleA];
var B = anisotropies[pair.particleB];
A.c3 += new float4(renderablePositions[pair.particleB].xyz,1) * pair.avgKernel;
B.c3 += new float4(renderablePositions[pair.particleA].xyz,1) * pair.avgKernel;
anisotropies[pair.particleA] = A;
anisotropies[pair.particleB] = B;
pairs[i] = pair;
}
}
}
[BurstCompile]
public struct AccumulateAnisotropyJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float4> renderablePositions;
[ReadOnly] public NativeArray<FluidInteraction> pairs;
[NativeDisableContainerSafetyRestriction][NativeDisableParallelForRestriction] public NativeArray<float4x4> 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] - anisotropies[pair.particleA].c3) * pair.avgKernel;
float4 distanceB = (renderablePositions[pair.particleA] - anisotropies[pair.particleB].c3) * pair.avgKernel;
anisotropies[pair.particleA] += BurstMath.multrnsp4(distanceA,distanceA);
anisotropies[pair.particleB] += BurstMath.multrnsp4(distanceB,distanceB);
}
}
}
}
}
#endif

View File

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

View File

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

View File

@@ -0,0 +1,15 @@
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
using System;
using Unity.Mathematics;
namespace Obi
{
public struct CohesionKernel
{
public float W(float r, float h)
{
return math.cos(math.min(r, h) * 3 * math.PI / (2 * h));
}
}
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 steps, float timeLeft)
{
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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,32 @@
#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()
{
return ((BurstSolverImpl)solver).abstraction.particleContacts.count;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,364 @@
#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 stepTime, float substepTime, int steps, float timeLeft)
{
var updateContacts = new UpdateParticleContactsJob()
{
prevPositions = solverImplementation.prevPositions,
prevOrientations = solverImplementation.prevOrientations,
velocities = solverImplementation.velocities,
radii = solverImplementation.principalRadii,
invMasses = solverImplementation.invMasses,
invRotationalMasses = solverImplementation.invRotationalMasses,
simplices = solverImplementation.simplices,
simplexCounts = solverImplementation.simplexCounts,
particleMaterialIndices = solverImplementation.collisionMaterials,
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
contacts = ((BurstSolverImpl)constraints.solver).abstraction.particleContacts.AsNativeArray<BurstContact>(),
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.particleContactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
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 steps, float timeLeft)
{
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,
fluidInterface = solverImplementation.fluidInterface,
collisionMaterials = ObiColliderWorld.GetInstance().collisionMaterials.AsNativeArray<BurstCollisionMaterial>(),
simplices = solverImplementation.simplices,
simplexCounts = solverImplementation.simplexCounts,
deltas = solverImplementation.positionDeltas,
counts = solverImplementation.positionConstraintCounts,
userData = solverImplementation.userData,
contacts = solverAbstraction.particleContacts.AsNativeArray<BurstContact>(),
effectiveMasses = ((BurstSolverImpl)constraints.solver).abstraction.particleContactEffectiveMasses.AsNativeArray<ContactEffectiveMasses>(),
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 = solverAbstraction.particleContacts.AsNativeArray<BurstContact>(),
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<float> invRotationalMasses;
[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<ContactEffectiveMasses> effectiveMasses;
[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];
var efMasses = effectiveMasses[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;
float simplexInvRotationalMassA = 0;
float4 simplexVelocityB = float4.zero;
float4 simplexPrevPositionB = float4.zero;
quaternion simplexPrevOrientationB = new quaternion(0, 0, 0, 0);
float simplexRadiusB = 0;
float simplexInvMassB = 0;
float simplexInvRotationalMassB = 0;
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];
simplexInvRotationalMassA += invRotationalMasses[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];
simplexInvRotationalMassB += invRotationalMasses[particleIndex] * contact.pointB[j];
simplexRadiusB += BurstMath.EllipsoidRadius(contact.normal, prevOrientations[particleIndex], radii[particleIndex].xyz) * contact.pointB[j];
}
simplexPrevPositionA.w = 0;
simplexPrevPositionB.w = 0;
// update contact distance
float4 contactPointA = simplexPrevPositionA - contact.normal * simplexRadiusA;
float4 contactPointB = simplexPrevPositionB + contact.normal * simplexRadiusB;
contact.distance = math.dot(contactPointA - contactPointB, contact.normal);
// update contact basis:
contact.CalculateTangent(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);
float4 invInertiaTensorA = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadiusA, simplexInvRotationalMassA) + new float4(BurstMath.epsilon));
float4 invInertiaTensorB = math.rcp(BurstMath.GetParticleInertiaTensor(simplexRadiusB, simplexInvRotationalMassB) + new float4(BurstMath.epsilon));
efMasses.CalculateContactMassesA(simplexInvMassA, invInertiaTensorA, simplexPrevPositionA, simplexPrevOrientationA, contactPointA, contact.normal, contact.tangent, contact.bitangent, rollingContacts);
efMasses.CalculateContactMassesB(simplexInvMassB, invInertiaTensorB, simplexPrevPositionB, simplexPrevOrientationB, contactPointB, contact.normal, contact.tangent, contact.bitangent, rollingContacts);
contacts[i] = contact;
effectiveMasses[i] = efMasses;
}
}
}
[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<float4> fluidInterface;
[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<float4> userData;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<int> counts;
[NativeDisableContainerSafetyRestriction] [NativeDisableParallelForRestriction] public NativeArray<BurstContact> contacts;
[ReadOnly] public NativeArray<ContactEffectiveMasses> effectiveMasses;
[ReadOnly] public Oni.ConstraintParameters constraintParameters;
[ReadOnly] public Oni.SolverParameters solverParameters;
[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;
float4 simplexUserDataA = float4.zero, simplexUserDataB = float4.zero;
float miscibility = 0;
for (int j = 0; j < simplexSizeA; ++j)
{
int particleIndex = simplices[simplexStartA + j];
simplexPositionA += positions[particleIndex] * contact.pointA[j];
simplexRadiusA += BurstMath.EllipsoidRadius(contact.normal, orientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
simplexUserDataA += userData[particleIndex] * contact.pointA[j];
miscibility += fluidInterface[particleIndex].w * contact.pointA[j];
}
for (int j = 0; j < simplexSizeB; ++j)
{
int particleIndex = simplices[simplexStartB + j];
simplexPositionB += positions[particleIndex] * contact.pointB[j];
simplexRadiusB += BurstMath.EllipsoidRadius(contact.normal, orientations[particleIndex], radii[particleIndex].xyz) * contact.pointA[j];
simplexUserDataB += userData[particleIndex] * contact.pointB[j];
miscibility += fluidInterface[particleIndex].w * contact.pointB[j];
}
simplexPositionA.w = 0;
simplexPositionB.w = 0;
float4 posA = simplexPositionA - contact.normal * simplexRadiusA;
float4 posB = simplexPositionB + contact.normal * simplexRadiusB;
// adhesion:
float lambda = contact.SolveAdhesion(effectiveMasses[i].TotalNormalInvMass, posA, posB, material.stickDistance, material.stickiness, substepTime);
// depenetration:
lambda += contact.SolvePenetration(effectiveMasses[i].TotalNormalInvMass, 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]++;
}
}
// property diffusion:
if (contact.distance < solverParameters.collisionMargin)
{
float diffusionSpeed = miscibility * 0.5f * substepTime;
float4 userDelta = (simplexUserDataB - simplexUserDataA) * solverParameters.diffusionMask * diffusionSpeed;
for (int j = 0; j < simplexSizeA; ++j)
userData[simplices[simplexStartA + j]] += userDelta * contact.pointA[j];
for (int j = 0; j < simplexSizeB; ++j)
userData[simplices[simplexStartB + j]] -= userDelta * contact.pointB[j];
}
// Apply position deltas immediately, if using sequential evaluation:
if (constraintParameters.evaluationOrder == Oni.ConstraintParameters.EvaluationOrder.Sequential)
{
for (int j = 0; j < simplexSizeA; ++j)
ApplyPositionDelta(simplices[simplexStartA + j], constraintParameters.SORFactor, ref positions, ref deltas, ref counts);
for (int j = 0; j < simplexSizeB; ++j)
ApplyPositionDelta(simplices[simplexStartB + j], 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