去掉obi,使用自写绳索
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct BatchLUT : IDisposable
|
||||
{
|
||||
public readonly int numBatches;
|
||||
public readonly NativeArray<ushort> batchIndex;
|
||||
|
||||
public BatchLUT(int numBatches)
|
||||
{
|
||||
this.numBatches = numBatches;
|
||||
|
||||
batchIndex = new NativeArray<ushort>(UInt16.MaxValue + 1, Allocator.Persistent, NativeArrayOptions.ClearMemory);
|
||||
const ushort end = UInt16.MaxValue;
|
||||
ushort numBits = (ushort)(numBatches - 1);
|
||||
|
||||
// For each entry in the table, compute the position of the first '0' bit in the index, starting from the less significant bit.
|
||||
// This is the index of the first batch where we can add the constraint to.
|
||||
|
||||
for (ushort value = 0; value < end; value++)
|
||||
{
|
||||
ushort valueCopy = value;
|
||||
for (ushort i = 0; i < numBits; i++)
|
||||
{
|
||||
if ((valueCopy & 1) == 0)
|
||||
{
|
||||
batchIndex[value] = i;
|
||||
break;
|
||||
}
|
||||
valueCopy >>= 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
batchIndex[end] = numBits;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
batchIndex.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ba613bda78be4a71be94b90f92fbde5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,216 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
public struct BatchData
|
||||
{
|
||||
public ushort batchID; // Batch identifier. All bits will be '0', except for the one at the position of the batch.
|
||||
|
||||
public int startIndex; // first constraint in the batch
|
||||
public int constraintCount; // amount of constraints in the batch.
|
||||
public int activeConstraintCount; // auxiliar counter used to sort the constraints in linear time.
|
||||
|
||||
public int workItemSize; // size of each work item.
|
||||
public int workItemCount; // number of work items.
|
||||
public bool isLast;
|
||||
|
||||
public BatchData(int index, int maxBatches)
|
||||
{
|
||||
batchID = (ushort)(1 << index);
|
||||
isLast = index == (maxBatches - 1);
|
||||
constraintCount = 0;
|
||||
activeConstraintCount = 0;
|
||||
|
||||
startIndex = 0;
|
||||
workItemSize = 0;
|
||||
workItemCount = 0;
|
||||
}
|
||||
|
||||
public void GetConstraintRange(int workItemIndex, out int start, out int end)
|
||||
{
|
||||
start = startIndex + workItemSize * workItemIndex;
|
||||
end = startIndex + math.min(constraintCount, workItemSize * (workItemIndex + 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public unsafe struct WorkItem
|
||||
{
|
||||
public const int minWorkItemSize = 64;
|
||||
public fixed int constraints[minWorkItemSize];
|
||||
public int constraintCount;
|
||||
|
||||
public bool Add(int constraintIndex)
|
||||
{
|
||||
// add the constraint to this work item.
|
||||
fixed (int* constraintIndices = constraints)
|
||||
{
|
||||
constraintIndices[constraintCount] = constraintIndex;
|
||||
}
|
||||
|
||||
// if we've completed the work item, close it and reuse for the next one.
|
||||
return (++constraintCount == minWorkItemSize);
|
||||
}
|
||||
}
|
||||
|
||||
public struct ConstraintBatcher<T> : IDisposable where T : struct, IConstraintProvider
|
||||
{
|
||||
public int maxBatches;
|
||||
private BatchLUT batchLUT; // look up table for batch indices.
|
||||
|
||||
public ConstraintBatcher(int maxBatches)
|
||||
{
|
||||
this.maxBatches = math.min(17, maxBatches);
|
||||
batchLUT = new BatchLUT(this.maxBatches);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
batchLUT.Dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear-time graph coloring using bitmasks and a look-up table. Used to organize contacts into batches for parallel processing.
|
||||
* input: array of unsorted constraints.
|
||||
* output:
|
||||
* - sorted constraint indices array.
|
||||
* - array of batchData, one per batch: startIndex, batchSize, workItemSize (at most == batchSize), numWorkItems
|
||||
* - number of active batches.
|
||||
*/
|
||||
|
||||
public JobHandle BatchConstraints(ref T constraintDesc,
|
||||
int particleCount,
|
||||
ref NativeArray<BatchData> batchData,
|
||||
ref NativeArray<int> activeBatchCount,
|
||||
JobHandle inputDeps)
|
||||
{
|
||||
if (activeBatchCount.Length != 1)
|
||||
return inputDeps;
|
||||
|
||||
var batchJob = new BatchContactsJob
|
||||
{
|
||||
batchMasks = new NativeArray<ushort>(particleCount, Allocator.TempJob, NativeArrayOptions.ClearMemory),
|
||||
batchIndices = new NativeArray<int>(constraintDesc.GetConstraintCount(), Allocator.TempJob, NativeArrayOptions.ClearMemory),
|
||||
lut = batchLUT,
|
||||
constraintDesc = constraintDesc,
|
||||
batchData = batchData,
|
||||
activeBatchCount = activeBatchCount,
|
||||
maxBatches = maxBatches
|
||||
};
|
||||
|
||||
return batchJob.Schedule(inputDeps);
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
private struct BatchContactsJob : IJob
|
||||
{
|
||||
[DeallocateOnJobCompletion]
|
||||
public NativeArray<ushort> batchMasks;
|
||||
|
||||
[DeallocateOnJobCompletion]
|
||||
public NativeArray<int> batchIndices;
|
||||
|
||||
[ReadOnly] public BatchLUT lut;
|
||||
public T constraintDesc;
|
||||
public NativeArray<BatchData> batchData;
|
||||
public NativeArray<int> activeBatchCount;
|
||||
|
||||
public int maxBatches;
|
||||
|
||||
public unsafe void Execute()
|
||||
{
|
||||
// Initialize batch data array
|
||||
for (int i = 0; i < batchData.Length; ++i)
|
||||
batchData[i] = new BatchData(i, maxBatches);
|
||||
|
||||
// temporary array containing an open work item for each batch.
|
||||
WorkItem* workItems = stackalloc WorkItem[maxBatches];
|
||||
for (int i = 0; i < maxBatches; i++)
|
||||
workItems[i] = new WorkItem();
|
||||
|
||||
int constraintCount = constraintDesc.GetConstraintCount();
|
||||
|
||||
// find a batch for each constraint:
|
||||
for (int i = 0; i < constraintCount; ++i)
|
||||
{
|
||||
// OR together the batch masks of all entities involved in the constraint:
|
||||
int batchMask = 0;
|
||||
for (int k = 0; k < constraintDesc.GetParticleCount(i); ++k)
|
||||
batchMask |= batchMasks[constraintDesc.GetParticle(i, k)];
|
||||
|
||||
// look up the first free batch index for this constraint:
|
||||
int batchIndex = batchIndices[i] = lut.batchIndex[batchMask];
|
||||
|
||||
// update the amount of constraints in the batch:
|
||||
var batch = batchData[batchIndex];
|
||||
batch.constraintCount++;
|
||||
batchData[batchIndex] = batch;
|
||||
|
||||
// add the constraint to the last work item of the batch:
|
||||
if (workItems[batchIndex].Add(i))
|
||||
{
|
||||
// if this work item does not belong to the last batch:
|
||||
if (batchIndex != maxBatches - 1)
|
||||
{
|
||||
// tag all entities in the work item with the batch mask to close it.
|
||||
// this way we know constraints referencing any of these entities can no longer be added to this batch.
|
||||
for (int j = 0; j < workItems[batchIndex].constraintCount; j++)
|
||||
{
|
||||
int constraint = workItems[batchIndex].constraints[j];
|
||||
|
||||
for (int k = 0; k < constraintDesc.GetParticleCount(constraint); ++k)
|
||||
batchMasks[constraintDesc.GetParticle(constraint, k)] |= batch.batchID;
|
||||
}
|
||||
}
|
||||
|
||||
// reuse the work item.
|
||||
workItems[batchIndex].constraintCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// fill batch data:
|
||||
activeBatchCount[0] = 0;
|
||||
int numConstraints = 0;
|
||||
for (int i = 0; i < batchData.Length; ++i)
|
||||
{
|
||||
var batch = batchData[i];
|
||||
|
||||
// bail out when we find the first empty batch:
|
||||
if (batch.constraintCount == 0)
|
||||
break;
|
||||
|
||||
// calculate work item size, count, and index of the first constraint
|
||||
batch.workItemSize = math.min(WorkItem.minWorkItemSize, batch.constraintCount);
|
||||
batch.workItemCount = (batch.constraintCount + batch.workItemSize - 1) / batch.workItemSize;
|
||||
batch.startIndex = numConstraints;
|
||||
|
||||
numConstraints += batch.constraintCount;
|
||||
activeBatchCount[0]++;
|
||||
|
||||
batchData[i] = batch;
|
||||
}
|
||||
|
||||
// write out sorted constraint indices:
|
||||
for (int i = 0; i < constraintCount; ++i)
|
||||
{
|
||||
var batch = batchData[batchIndices[i]];
|
||||
int sortedIndex = batch.startIndex + (batch.activeConstraintCount++);
|
||||
constraintDesc.WriteSortedConstraint(i, sortedIndex);
|
||||
batchData[batchIndices[i]] = batch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f15c420c952f4f689684489a7de8920
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,160 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class ConstraintSorter<T> where T : unmanaged, IConstraint
|
||||
{
|
||||
|
||||
public struct ConstraintComparer<K> : IComparer<K> where K : IConstraint
|
||||
{
|
||||
// Compares by Height, Length, and Width.
|
||||
public int Compare(K x, K y)
|
||||
{
|
||||
return x.GetParticle(1).CompareTo(y.GetParticle(1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a single-threaded count sort on the constraints array using the first particle index,
|
||||
* then multiple parallel sorts over slices of the original array sorting by the second particle index.
|
||||
*/
|
||||
public JobHandle SortConstraints(int particleCount,
|
||||
NativeArray<T> constraints,
|
||||
ref NativeArray<T> sortedConstraints,
|
||||
JobHandle handle)
|
||||
{
|
||||
// Count the amount of digits in the largest particle index that can be referenced by a constraint:
|
||||
NativeArray<int> totalCountUpToDigit = new NativeArray<int>(particleCount + 1, Allocator.TempJob);
|
||||
int numDigits = 0;
|
||||
int maxBodyIndex = particleCount - 1;
|
||||
{
|
||||
int val = maxBodyIndex;
|
||||
while (val > 0)
|
||||
{
|
||||
val >>= 1;
|
||||
numDigits++;
|
||||
}
|
||||
}
|
||||
|
||||
handle = new CountSortPerFirstParticleJob
|
||||
{
|
||||
input = constraints,
|
||||
output = sortedConstraints,
|
||||
maxDigits = numDigits,
|
||||
maxIndex = maxBodyIndex,
|
||||
digitCount = totalCountUpToDigit
|
||||
}.Schedule(handle);
|
||||
|
||||
// Sort sub arrays with default sort.
|
||||
int numPerBatch = math.max(1, maxBodyIndex / 32);
|
||||
|
||||
handle = new SortSubArraysJob
|
||||
{
|
||||
InOutArray = sortedConstraints,
|
||||
NextElementIndex = totalCountUpToDigit,
|
||||
comparer = new ConstraintComparer<T>()
|
||||
}.Schedule(totalCountUpToDigit.Length, numPerBatch, handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public struct CountSortPerFirstParticleJob : IJob
|
||||
{
|
||||
[ReadOnly][NativeDisableContainerSafetyRestriction] public NativeArray<T> input;
|
||||
public NativeArray<T> output;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction] public NativeArray<int> digitCount;
|
||||
|
||||
public int maxDigits;
|
||||
public int maxIndex;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
// no real need for a mask, just in case bad particle indices were passed that have more digits than maxDigits.
|
||||
int mask = (1 << maxDigits) - 1;
|
||||
|
||||
// Count digits
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
digitCount[input[i].GetParticle(0) & mask]++;
|
||||
}
|
||||
|
||||
// Calculate start index for each digit
|
||||
int prev = digitCount[0];
|
||||
digitCount[0] = 0;
|
||||
for (int i = 1; i <= maxIndex; i++)
|
||||
{
|
||||
int current = digitCount[i];
|
||||
digitCount[i] = digitCount[i - 1] + prev;
|
||||
prev = current;
|
||||
}
|
||||
|
||||
// Copy elements into buckets based on particle index
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
int index = digitCount[input[i].GetParticle(0) & mask]++;
|
||||
if (index == 1 && input.Length == 1)
|
||||
{
|
||||
output[0] = input[0];
|
||||
}
|
||||
output[index] = input[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sorts slices of an array in parallel
|
||||
[BurstCompile]
|
||||
public struct SortSubArraysJob : IJobParallelFor
|
||||
{
|
||||
[NativeDisableContainerSafetyRestriction] public NativeArray<T> InOutArray;
|
||||
|
||||
// Typically lastDigitIndex is resulting RadixSortPerBodyAJob.digitCount. nextElementIndex[i] = index of first element with bodyA index == i + 1
|
||||
[NativeDisableContainerSafetyRestriction][DeallocateOnJobCompletion] public NativeArray<int> NextElementIndex;
|
||||
|
||||
[ReadOnly] public ConstraintComparer<T> comparer;
|
||||
|
||||
public void Execute(int workItemIndex)
|
||||
{
|
||||
int startIndex = 0;
|
||||
if (workItemIndex > 0)
|
||||
{
|
||||
startIndex = NextElementIndex[workItemIndex - 1];
|
||||
}
|
||||
|
||||
if (startIndex < InOutArray.Length)
|
||||
{
|
||||
int length = NextElementIndex[workItemIndex] - startIndex;
|
||||
DefaultSortOfSubArrays(InOutArray, startIndex, length, comparer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DefaultSortOfSubArrays(NativeArray<T> inOutArray, int startIndex, int length, ConstraintComparer<T> comparer)
|
||||
{
|
||||
if (length > 2)
|
||||
{
|
||||
var slice = inOutArray.Slice(startIndex, length);
|
||||
slice.Sort(comparer);
|
||||
}
|
||||
else if (length == 2) // just swap:
|
||||
{
|
||||
if (inOutArray[startIndex].GetParticle(1) > inOutArray[startIndex + 1].GetParticle(1))
|
||||
{
|
||||
var temp = inOutArray[startIndex + 1];
|
||||
inOutArray[startIndex + 1] = inOutArray[startIndex];
|
||||
inOutArray[startIndex] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4cb7051d5b32442d79da18c47c1fbcc2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,41 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct ContactProvider : IConstraintProvider
|
||||
{
|
||||
public NativeArray<BurstContact> contacts;
|
||||
public NativeArray<BurstContact> sortedContacts;
|
||||
public NativeArray<int> simplices;
|
||||
public SimplexCounts simplexCounts;
|
||||
|
||||
public int GetConstraintCount()
|
||||
{
|
||||
return contacts.Length;
|
||||
}
|
||||
|
||||
public int GetParticleCount(int constraintIndex)
|
||||
{
|
||||
simplexCounts.GetSimplexStartAndSize(contacts[constraintIndex].bodyA, out int simplexSizeA);
|
||||
simplexCounts.GetSimplexStartAndSize(contacts[constraintIndex].bodyB, out int simplexSizeB);
|
||||
return simplexSizeA + simplexSizeB;
|
||||
}
|
||||
public int GetParticle(int constraintIndex, int index)
|
||||
{
|
||||
int simplexStartA = simplexCounts.GetSimplexStartAndSize(contacts[constraintIndex].bodyA, out int simplexSizeA);
|
||||
int simplexStartB = simplexCounts.GetSimplexStartAndSize(contacts[constraintIndex].bodyB, out int simplexSizeB);
|
||||
if (index < simplexSizeA)
|
||||
return simplices[simplexStartA + index];
|
||||
else
|
||||
return simplices[simplexStartB + index - simplexSizeA];
|
||||
}
|
||||
|
||||
public void WriteSortedConstraint(int constraintIndex, int sortedIndex)
|
||||
{
|
||||
sortedContacts[sortedIndex] = contacts[constraintIndex];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fec3d78324058457cb723a60fb4858a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,32 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct FluidInteractionProvider : IConstraintProvider
|
||||
{
|
||||
public NativeArray<FluidInteraction> interactions;
|
||||
public NativeArray<FluidInteraction> sortedInteractions;
|
||||
|
||||
public int GetConstraintCount()
|
||||
{
|
||||
return interactions.Length;
|
||||
}
|
||||
|
||||
public int GetParticleCount(int constraintIndex)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
public int GetParticle(int constraintIndex, int index)
|
||||
{
|
||||
return interactions[constraintIndex].GetParticle(index);
|
||||
}
|
||||
|
||||
public void WriteSortedConstraint(int constraintIndex, int sortedIndex)
|
||||
{
|
||||
sortedInteractions[sortedIndex] = interactions[constraintIndex];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66b0d6e3db3c14b929541a23187c393b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,10 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
namespace Obi
|
||||
{
|
||||
public interface IConstraint
|
||||
{
|
||||
int GetParticleCount();
|
||||
int GetParticle(int index);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49760f68f387c45b8b1e5482a6d0dabe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,12 +0,0 @@
|
||||
#if (OBI_BURST && OBI_MATHEMATICS && OBI_COLLECTIONS)
|
||||
namespace Obi
|
||||
{
|
||||
public interface IConstraintProvider
|
||||
{
|
||||
int GetConstraintCount();
|
||||
int GetParticleCount(int constraintIndex);
|
||||
int GetParticle(int constraintIndex, int index);
|
||||
void WriteSortedConstraint(int constraintIndex, int sortedIndex);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f9cbc3295db8411ba4ac8c9a75291ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user