rework spatial hash to store arbitrary data
parent
5223e92aa8
commit
b892da7749
|
@ -7,17 +7,20 @@ namespace MoonWorks.Collision.Fixed
|
|||
/// Used to quickly check if two shapes are potentially overlapping.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that will be used to uniquely identify shape-transform pairs.</typeparam>
|
||||
public class SpatialHash2D<T, U> where T : struct, System.IEquatable<T> where U : IHasAABB2D
|
||||
public class SpatialHash2D<T, U> where T : struct, System.IEquatable<T>
|
||||
{
|
||||
private readonly Fix64 cellSize;
|
||||
|
||||
private readonly Dictionary<long, HashSet<T>> hashDictionary = new Dictionary<long, HashSet<T>>();
|
||||
private readonly Dictionary<T, (U, Transform2D, uint)> IDLookup = new Dictionary<T, (U, Transform2D, uint)>();
|
||||
private readonly Dictionary<T, AABB2D> IDBoxLookup = new Dictionary<T, AABB2D>();
|
||||
private readonly Dictionary<T, U> IDDataLookup = new Dictionary<T, U>();
|
||||
|
||||
public int MinX { get; private set; } = 0;
|
||||
public int MaxX { get; private set; } = 0;
|
||||
public int MinY { get; private set; } = 0;
|
||||
public int MaxY { get; private set; } = 0;
|
||||
private readonly HashSet<T> DynamicIDs = new HashSet<T>();
|
||||
|
||||
private int MinX;
|
||||
private int MaxX;
|
||||
private int MinY;
|
||||
private int MaxY;
|
||||
|
||||
private Queue<HashSet<T>> hashSetPool = new Queue<HashSet<T>>();
|
||||
|
||||
|
@ -35,12 +38,11 @@ namespace MoonWorks.Collision.Fixed
|
|||
/// Inserts an element into the SpatialHash.
|
||||
/// </summary>
|
||||
/// <param name="id">A unique ID for the shape-transform pair.</param>
|
||||
/// <param name="shape"></param>
|
||||
/// <param name="transform2D"></param>
|
||||
/// <param name="collisionGroups">A bitmask value specifying the groups this object belongs to.</param>
|
||||
public void Insert(T id, U shape, Transform2D transform2D, uint collisionGroups = uint.MaxValue)
|
||||
public void Insert(T id, AABB2D aabb, Transform2D transform2D, U data, bool dynamic = true)
|
||||
{
|
||||
var box = AABB2D.Transformed(shape.AABB, transform2D);
|
||||
Remove(id);
|
||||
|
||||
var box = AABB2D.Transformed(aabb, transform2D);
|
||||
var minHash = Hash(box.Min);
|
||||
var maxHash = Hash(box.Max);
|
||||
|
||||
|
@ -52,19 +54,26 @@ namespace MoonWorks.Collision.Fixed
|
|||
}
|
||||
|
||||
hashDictionary[key].Add(id);
|
||||
IDLookup[id] = (shape, transform2D, collisionGroups);
|
||||
IDDataLookup[id] = data;
|
||||
}
|
||||
|
||||
MinX = System.Math.Min(MinX, minHash.Item1);
|
||||
MinY = System.Math.Min(MinY, minHash.Item2);
|
||||
MaxX = System.Math.Max(MaxX, maxHash.Item1);
|
||||
MaxY = System.Math.Max(MaxY, maxHash.Item2);
|
||||
|
||||
if (dynamic)
|
||||
{
|
||||
DynamicIDs.Add(id);
|
||||
}
|
||||
|
||||
IDBoxLookup[id] = box;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all the potential collisions of a shape-transform pair. Excludes any shape-transforms with the given ID.
|
||||
/// </summary>
|
||||
public RetrieveEnumerator Retrieve<V>(T id, V hasAABB, Transform2D transform2D, uint collisionMask = uint.MaxValue) where V : IHasAABB2D
|
||||
public RetrieveEnumerator Retrieve<V>(T id, V hasAABB, Transform2D transform2D) where V : IHasAABB2D
|
||||
{
|
||||
var box = AABB2D.Transformed(hasAABB.AABB, transform2D);
|
||||
var (minX, minY) = Hash(box.Min);
|
||||
|
@ -78,18 +87,17 @@ namespace MoonWorks.Collision.Fixed
|
|||
return new RetrieveEnumerator(
|
||||
this,
|
||||
Keys(minX, minY, maxX, maxY),
|
||||
id,
|
||||
collisionMask
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all the potential collisions of a shape-transform pair.
|
||||
/// </summary>
|
||||
public RetrieveEnumerator Retrieve<V>(V hasAABB, Transform2D transform2D, uint collisionMask = uint.MaxValue) where V : IHasAABB2D
|
||||
public RetrieveEnumerator Retrieve<V>(V hasAABB, Transform2D transform2D) where V : IHasAABB2D
|
||||
{
|
||||
var box = AABB2D.Transformed(hasAABB.AABB, transform2D);
|
||||
return Retrieve(box, collisionMask);
|
||||
return Retrieve(box);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -97,7 +105,7 @@ namespace MoonWorks.Collision.Fixed
|
|||
/// </summary>
|
||||
/// <param name="aabb">A transformed AABB.</param>
|
||||
/// <returns></returns>
|
||||
public RetrieveEnumerator Retrieve(AABB2D aabb, uint collisionMask = uint.MaxValue)
|
||||
public RetrieveEnumerator Retrieve(T id, AABB2D aabb)
|
||||
{
|
||||
var (minX, minY) = Hash(aabb.Min);
|
||||
var (maxX, maxY) = Hash(aabb.Max);
|
||||
|
@ -110,14 +118,29 @@ namespace MoonWorks.Collision.Fixed
|
|||
return new RetrieveEnumerator(
|
||||
this,
|
||||
Keys(minX, minY, maxX, maxY),
|
||||
collisionMask
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
public void Update(T id, U shape, Transform2D transform2D, uint collisionGroups = uint.MaxValue)
|
||||
/// <summary>
|
||||
/// Retrieves objects based on a pre-transformed AABB.
|
||||
/// </summary>
|
||||
/// <param name="aabb">A transformed AABB.</param>
|
||||
/// <returns></returns>
|
||||
public RetrieveEnumerator Retrieve(AABB2D aabb)
|
||||
{
|
||||
Remove(id);
|
||||
Insert(id, shape, transform2D, collisionGroups);
|
||||
var (minX, minY) = Hash(aabb.Min);
|
||||
var (maxX, maxY) = Hash(aabb.Max);
|
||||
|
||||
if (minX < MinX) { minX = MinX; }
|
||||
if (maxX > MaxX) { maxX = MaxX; }
|
||||
if (minY < MinY) { minY = MinY; }
|
||||
if (maxY > MaxY) { maxY = MaxY; }
|
||||
|
||||
return new RetrieveEnumerator(
|
||||
this,
|
||||
Keys(minX, minY, maxX, maxY)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -125,24 +148,24 @@ namespace MoonWorks.Collision.Fixed
|
|||
/// </summary>
|
||||
public void Remove(T id)
|
||||
{
|
||||
if (IDLookup.TryGetValue(id, out var data))
|
||||
if (IDBoxLookup.TryGetValue(id, out var aabb))
|
||||
{
|
||||
var (shape, transform, collisionGroups) = data;
|
||||
|
||||
var box = AABB2D.Transformed(shape.AABB, transform);
|
||||
var minHash = Hash(box.Min);
|
||||
var maxHash = Hash(box.Max);
|
||||
var minHash = Hash(aabb.Min);
|
||||
var maxHash = Hash(aabb.Max);
|
||||
|
||||
foreach (var key in Keys(minHash.Item1, minHash.Item2, maxHash.Item1, maxHash.Item2))
|
||||
{
|
||||
if (hashDictionary.ContainsKey(key))
|
||||
if (hashDictionary.TryGetValue(key, out HashSet<T> value))
|
||||
{
|
||||
hashDictionary[key].Remove(id);
|
||||
value.Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
IDLookup.Remove(id);
|
||||
IDDataLookup.Remove(id);
|
||||
IDBoxLookup.Remove(id);
|
||||
}
|
||||
|
||||
DynamicIDs.Remove(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -155,7 +178,19 @@ namespace MoonWorks.Collision.Fixed
|
|||
hash.Clear();
|
||||
}
|
||||
|
||||
IDLookup.Clear();
|
||||
IDDataLookup.Clear();
|
||||
IDBoxLookup.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes
|
||||
/// </summary>
|
||||
public void ClearDynamic()
|
||||
{
|
||||
foreach (var id in DynamicIDs)
|
||||
{
|
||||
Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
private static long MakeLong(int left, int right)
|
||||
|
@ -239,15 +274,13 @@ namespace MoonWorks.Collision.Fixed
|
|||
private bool HashSetEnumeratorActive;
|
||||
private HashSet<T> Duplicates;
|
||||
private T? ID;
|
||||
private uint CollisionMask;
|
||||
|
||||
public RetrieveEnumerator GetEnumerator() => this;
|
||||
|
||||
internal RetrieveEnumerator(
|
||||
SpatialHash2D<T, U> spatialHash,
|
||||
KeysEnumerator keysEnumerator,
|
||||
T id,
|
||||
uint collisionMask
|
||||
T id
|
||||
) {
|
||||
SpatialHash = spatialHash;
|
||||
KeysEnumerator = keysEnumerator;
|
||||
|
@ -255,13 +288,11 @@ namespace MoonWorks.Collision.Fixed
|
|||
HashSetEnumeratorActive = false;
|
||||
Duplicates = SpatialHash.AcquireHashSet();
|
||||
ID = id;
|
||||
CollisionMask = collisionMask;
|
||||
}
|
||||
|
||||
internal RetrieveEnumerator(
|
||||
SpatialHash2D<T, U> spatialHash,
|
||||
KeysEnumerator keysEnumerator,
|
||||
uint collisionMask
|
||||
KeysEnumerator keysEnumerator
|
||||
) {
|
||||
SpatialHash = spatialHash;
|
||||
KeysEnumerator = keysEnumerator;
|
||||
|
@ -269,7 +300,6 @@ namespace MoonWorks.Collision.Fixed
|
|||
HashSetEnumeratorActive = false;
|
||||
Duplicates = SpatialHash.AcquireHashSet();
|
||||
ID = null;
|
||||
CollisionMask = collisionMask;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
|
@ -293,7 +323,6 @@ namespace MoonWorks.Collision.Fixed
|
|||
|
||||
// conditions
|
||||
var t = HashSetEnumerator.Current;
|
||||
var collisionGroups = SpatialHash.IDLookup[t].Item3;
|
||||
|
||||
if (Duplicates.Contains(t))
|
||||
{
|
||||
|
@ -302,7 +331,7 @@ namespace MoonWorks.Collision.Fixed
|
|||
|
||||
if (ID.HasValue)
|
||||
{
|
||||
if (ID.Value.Equals(t) || (CollisionMask & collisionGroups) == 0)
|
||||
if (ID.Value.Equals(t))
|
||||
{
|
||||
return MoveNext();
|
||||
}
|
||||
|
@ -312,13 +341,13 @@ namespace MoonWorks.Collision.Fixed
|
|||
return true;
|
||||
}
|
||||
|
||||
public (T, U, Transform2D, uint) Current
|
||||
public (T, U) Current
|
||||
{
|
||||
get
|
||||
{
|
||||
var t = HashSetEnumerator.Current;
|
||||
var (u, transform, groups) = SpatialHash.IDLookup[t];
|
||||
return (t, u, transform, groups);
|
||||
var u = SpatialHash.IDDataLookup[t];
|
||||
return (t, u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue