garbage optimization changes
parent
c43df10c2a
commit
74f729dcb9
|
@ -0,0 +1,33 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MoonWorks.Collision.Fixed
|
||||||
|
{
|
||||||
|
public class Collider<T> : IHasAABB2D where T : struct, IShape2D
|
||||||
|
{
|
||||||
|
private readonly T[] Shapes;
|
||||||
|
|
||||||
|
public ReadOnlySpan<T>.Enumerator GetEnumerator() => new ReadOnlySpan<T>(Shapes).GetEnumerator();
|
||||||
|
|
||||||
|
public AABB2D AABB { get; }
|
||||||
|
|
||||||
|
public Collider(T shape)
|
||||||
|
{
|
||||||
|
Shapes = new T[1] { shape };
|
||||||
|
AABB = shape.AABB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collider(T[] shapes)
|
||||||
|
{
|
||||||
|
Shapes = new T[shapes.Length];
|
||||||
|
Array.Copy(shapes, Shapes, shapes.Length);
|
||||||
|
|
||||||
|
var aabb = new AABB2D();
|
||||||
|
foreach (var shape in Shapes)
|
||||||
|
{
|
||||||
|
aabb = aabb.Compose(shape.AABB);
|
||||||
|
}
|
||||||
|
|
||||||
|
AABB = aabb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using MoonWorks.Math.Fixed;
|
|
||||||
|
|
||||||
namespace MoonWorks.Collision.Fixed
|
|
||||||
{
|
|
||||||
public interface ICollidable
|
|
||||||
{
|
|
||||||
IEnumerable<IShape2D> Shapes { get; }
|
|
||||||
AABB2D AABB { get; }
|
|
||||||
AABB2D TransformedAABB(Transform2D transform);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace MoonWorks.Collision.Fixed
|
||||||
|
{
|
||||||
|
public interface IHasAABB2D
|
||||||
|
{
|
||||||
|
AABB2D AABB { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
using MoonWorks.Math.Fixed;
|
using MoonWorks.Math.Fixed;
|
||||||
|
|
||||||
namespace MoonWorks.Collision.Fixed
|
namespace MoonWorks.Collision.Fixed
|
||||||
{
|
{
|
||||||
public interface IShape2D : ICollidable, System.IEquatable<IShape2D>
|
public interface IShape2D : IHasAABB2D, System.IEquatable<IShape2D>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A Minkowski support function. Gives the farthest point on the edge of a shape along the given direction.
|
/// A Minkowski support function. Gives the farthest point on the edge of a shape along the given direction.
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
using MoonWorks.Math.Fixed;
|
using MoonWorks.Math.Fixed;
|
||||||
|
|
||||||
namespace MoonWorks.Collision.Fixed
|
namespace MoonWorks.Collision.Fixed
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A Minkowski difference between two shapes.
|
/// A Minkowski difference between two shapes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct MinkowskiDifference : System.IEquatable<MinkowskiDifference>
|
public struct MinkowskiDifference<T, U> where T : IShape2D where U : IShape2D
|
||||||
{
|
{
|
||||||
private IShape2D ShapeA { get; }
|
private T ShapeA { get; }
|
||||||
private Transform2D TransformA { get; }
|
private Transform2D TransformA { get; }
|
||||||
private IShape2D ShapeB { get; }
|
private U ShapeB { get; }
|
||||||
private Transform2D TransformB { get; }
|
private Transform2D TransformB { get; }
|
||||||
|
|
||||||
public MinkowskiDifference(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)
|
public MinkowskiDifference(T shapeA, Transform2D transformA, U shapeB, Transform2D transformB)
|
||||||
{
|
{
|
||||||
ShapeA = shapeA;
|
ShapeA = shapeA;
|
||||||
TransformA = transformA;
|
TransformA = transformA;
|
||||||
|
@ -24,34 +24,5 @@ namespace MoonWorks.Collision.Fixed
|
||||||
{
|
{
|
||||||
return ShapeA.Support(direction, TransformA) - ShapeB.Support(-direction, TransformB);
|
return ShapeA.Support(direction, TransformA) - ShapeB.Support(-direction, TransformB);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object other)
|
|
||||||
{
|
|
||||||
return other is MinkowskiDifference minkowskiDifference && Equals(minkowskiDifference);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(MinkowskiDifference other)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
ShapeA == other.ShapeA &&
|
|
||||||
TransformA == other.TransformA &&
|
|
||||||
ShapeB == other.ShapeB &&
|
|
||||||
TransformB == other.TransformB;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return System.HashCode.Combine(ShapeA, TransformA, ShapeB, TransformB);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(MinkowskiDifference a, MinkowskiDifference b)
|
|
||||||
{
|
|
||||||
return a.Equals(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(MinkowskiDifference a, MinkowskiDifference b)
|
|
||||||
{
|
|
||||||
return !(a == b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using MoonWorks.Math.Fixed;
|
using MoonWorks.Math.Fixed;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace MoonWorks.Collision.Fixed
|
namespace MoonWorks.Collision.Fixed
|
||||||
{
|
{
|
||||||
|
@ -11,63 +12,168 @@ namespace MoonWorks.Collision.Fixed
|
||||||
public int Index;
|
public int Index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TestCollision(ICollidable collidableA, Transform2D transformA, ICollidable collidableB, Transform2D transformB)
|
public static bool TestCollision<T, U>(Collider<T> colliderA, in Transform2D transformA, Collider<U> colliderB, in Transform2D transformB) where T : struct, IShape2D where U : struct, IShape2D
|
||||||
{
|
{
|
||||||
foreach (var shapeA in collidableA.Shapes)
|
foreach (var shapeA in colliderA)
|
||||||
{
|
{
|
||||||
foreach (var shapeB in collidableB.Shapes)
|
foreach (var shapeB in colliderB)
|
||||||
{
|
{
|
||||||
if (TestCollision(shapeA, transformA, shapeB, transformB))
|
if (TestCollision(shapeA, transformA, shapeB, transformB))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)
|
public static bool TestCollision<T, U>(Collider<T> collider, in Transform2D transformA, U shape, in Transform2D transformB) where T : struct, IShape2D where U : struct, IShape2D
|
||||||
{
|
{
|
||||||
// If we can use a fast path check, let's do that!
|
foreach (var colliderShape in collider)
|
||||||
if (shapeA is Rectangle rectangleA && shapeB is Rectangle rectangleB && transformA.IsAxisAligned && transformB.IsAxisAligned)
|
|
||||||
{
|
{
|
||||||
return TestRectangleOverlap(rectangleA, transformA, rectangleB, transformB);
|
if (TestCollision(colliderShape, transformA, shape, transformB))
|
||||||
}
|
{
|
||||||
else if (shapeA is Point && shapeB is Rectangle && transformB.IsAxisAligned)
|
return true;
|
||||||
{
|
}
|
||||||
return TestPointRectangleOverlap((Point) shapeA, transformA, (Rectangle) shapeB, transformB);
|
}
|
||||||
}
|
|
||||||
else if (shapeA is Rectangle && shapeB is Point && transformA.IsAxisAligned)
|
return false;
|
||||||
{
|
}
|
||||||
return TestPointRectangleOverlap((Point) shapeB, transformB, (Rectangle) shapeA, transformA);
|
|
||||||
}
|
public static bool TestCollision<T, U>(U shape, in Transform2D transformA, Collider<T> collider, in Transform2D transformB) where T : struct, IShape2D where U : struct, IShape2D
|
||||||
else if (shapeA is Rectangle && shapeB is Circle && transformA.IsAxisAligned && transformB.IsUniformScale)
|
{
|
||||||
{
|
return TestCollision(collider, transformB, shape, transformA);
|
||||||
return TestCircleRectangleOverlap((Circle) shapeB, transformB, (Rectangle) shapeA, transformA);
|
}
|
||||||
}
|
|
||||||
else if (shapeA is Circle && shapeB is Rectangle && transformA.IsUniformScale && transformB.IsAxisAligned)
|
public static bool TestCollision<T, U>(in T shapeA, in Transform2D transformA, in U shapeB, in Transform2D transformB) where T : struct, IShape2D where U : struct, IShape2D
|
||||||
{
|
{
|
||||||
return TestCircleRectangleOverlap((Circle) shapeA, transformA, (Rectangle) shapeB, transformB);
|
if (shapeA is Circle circle)
|
||||||
}
|
{
|
||||||
else if (shapeA is Circle && shapeB is Point && transformA.IsUniformScale)
|
if (shapeB is Circle circleB)
|
||||||
{
|
{
|
||||||
return TestCirclePointOverlap((Circle) shapeA, transformA, (Point) shapeB, transformB);
|
return TestCollision(circle, transformA, circleB, transformB);
|
||||||
}
|
}
|
||||||
else if (shapeA is Point && shapeB is Circle && transformB.IsUniformScale)
|
else if (shapeB is Point pointB)
|
||||||
{
|
{
|
||||||
return TestCirclePointOverlap((Circle) shapeB, transformB, (Point) shapeA, transformA);
|
return TestCollision(circle, transformA, pointB, transformB);
|
||||||
}
|
}
|
||||||
else if (shapeA is Circle circleA && shapeB is Circle circleB && transformA.IsUniformScale && transformB.IsUniformScale)
|
else if (shapeB is Rectangle rectangleB)
|
||||||
{
|
{
|
||||||
return TestCircleOverlap(circleA, transformA, circleB, transformB);
|
return TestCollision(circle, transformA, rectangleB, transformB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (shapeA is Point point)
|
||||||
|
{
|
||||||
|
if (shapeB is Circle circleB)
|
||||||
|
{
|
||||||
|
return TestCollision(point, transformA, circleB, transformB);
|
||||||
|
}
|
||||||
|
else if (shapeB is Point pointB)
|
||||||
|
{
|
||||||
|
return TestCollision(point, transformA, pointB, transformB);
|
||||||
|
}
|
||||||
|
else if (shapeB is Rectangle rectangleB)
|
||||||
|
{
|
||||||
|
return TestCollision(point, transformA, rectangleB, transformB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (shapeA is Rectangle rectangle)
|
||||||
|
{
|
||||||
|
if (shapeB is Circle circleB)
|
||||||
|
{
|
||||||
|
return TestCollision(rectangle, transformA, circleB, transformB);
|
||||||
|
}
|
||||||
|
else if (shapeB is Point pointB)
|
||||||
|
{
|
||||||
|
return TestCollision(rectangle, transformA, pointB, transformB);
|
||||||
|
}
|
||||||
|
else if (shapeB is Rectangle rectangleB)
|
||||||
|
{
|
||||||
|
return TestCollision(rectangle, transformA, rectangleB, transformB);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sad, we can't do a fast path optimization. Time for a simplex reduction.
|
|
||||||
return FindCollisionSimplex(shapeA, transformA, shapeB, transformB).Item1;
|
return FindCollisionSimplex(shapeA, transformA, shapeB, transformB).Item1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TestRectangleOverlap(Rectangle rectangleA, Transform2D transformA, Rectangle rectangleB, Transform2D transformB)
|
public static bool TestCollision(in Rectangle rectangleA, in Transform2D transformA, in Rectangle rectangleB, in Transform2D transformB)
|
||||||
|
{
|
||||||
|
if (transformA.IsAxisAligned && transformB.IsAxisAligned)
|
||||||
|
{
|
||||||
|
return TestRectangleOverlap(rectangleA, transformA, rectangleB, transformB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FindCollisionSimplex(rectangleA, transformA, rectangleB, transformB).Item1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TestCollision(in Point point, in Transform2D transformA, in Rectangle rectangle, in Transform2D transformB)
|
||||||
|
{
|
||||||
|
if (transformB.IsAxisAligned)
|
||||||
|
{
|
||||||
|
return TestPointRectangleOverlap(point, transformA, rectangle, transformB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FindCollisionSimplex(point, transformA, rectangle, transformB).Item1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TestCollision(in Rectangle rectangle, in Transform2D transformA, in Point point, in Transform2D transformB)
|
||||||
|
{
|
||||||
|
return TestCollision(point, transformB, rectangle, transformA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TestCollision(in Rectangle rectangle, in Transform2D transformA, in Circle circle, in Transform2D transformB)
|
||||||
|
{
|
||||||
|
if (transformA.IsAxisAligned && transformB.IsUniformScale)
|
||||||
|
{
|
||||||
|
return TestCircleRectangleOverlap(circle, transformB, rectangle, transformA);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FindCollisionSimplex(rectangle, transformA, circle, transformB).Item1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TestCollision(in Circle circle, in Transform2D transformA, in Rectangle rectangle, in Transform2D transformB)
|
||||||
|
{
|
||||||
|
return TestCollision(rectangle, transformB, circle, transformA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TestCollision(in Circle circle, in Transform2D transformA, in Point point, in Transform2D transformB)
|
||||||
|
{
|
||||||
|
if (transformA.IsUniformScale)
|
||||||
|
{
|
||||||
|
return TestCirclePointOverlap(circle, transformA, point, transformB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FindCollisionSimplex(circle, transformA, point, transformB).Item1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TestCollision(in Point point, in Transform2D transformA, in Circle circle, in Transform2D transformB)
|
||||||
|
{
|
||||||
|
return TestCollision(circle, transformB, point, transformA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TestCollision(in Circle circleA, in Transform2D transformA, in Circle circleB, in Transform2D transformB)
|
||||||
|
{
|
||||||
|
if (transformA.IsUniformScale && transformB.IsUniformScale)
|
||||||
|
{
|
||||||
|
return TestCircleOverlap(circleA, transformA, circleB, transformB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FindCollisionSimplex(circleA, transformA, circleB, transformB).Item1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool TestRectangleOverlap(in Rectangle rectangleA, in Transform2D transformA, in Rectangle rectangleB, in Transform2D transformB)
|
||||||
{
|
{
|
||||||
var firstAABB = rectangleA.TransformedAABB(transformA);
|
var firstAABB = rectangleA.TransformedAABB(transformA);
|
||||||
var secondAABB = rectangleB.TransformedAABB(transformB);
|
var secondAABB = rectangleB.TransformedAABB(transformB);
|
||||||
|
@ -75,7 +181,8 @@ namespace MoonWorks.Collision.Fixed
|
||||||
return firstAABB.Left < secondAABB.Right && firstAABB.Right > secondAABB.Left && firstAABB.Top < secondAABB.Bottom && firstAABB.Bottom > secondAABB.Top;
|
return firstAABB.Left < secondAABB.Right && firstAABB.Right > secondAABB.Left && firstAABB.Top < secondAABB.Bottom && firstAABB.Bottom > secondAABB.Top;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TestPointRectangleOverlap(Point point, Transform2D pointTransform, Rectangle rectangle, Transform2D rectangleTransform)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool TestPointRectangleOverlap(in Point point, in Transform2D pointTransform, in Rectangle rectangle, in Transform2D rectangleTransform)
|
||||||
{
|
{
|
||||||
var transformedPoint = pointTransform.Position;
|
var transformedPoint = pointTransform.Position;
|
||||||
var AABB = rectangle.TransformedAABB(rectangleTransform);
|
var AABB = rectangle.TransformedAABB(rectangleTransform);
|
||||||
|
@ -83,7 +190,8 @@ namespace MoonWorks.Collision.Fixed
|
||||||
return transformedPoint.X > AABB.Left && transformedPoint.X < AABB.Right && transformedPoint.Y < AABB.Bottom && transformedPoint.Y > AABB.Top;
|
return transformedPoint.X > AABB.Left && transformedPoint.X < AABB.Right && transformedPoint.Y < AABB.Bottom && transformedPoint.Y > AABB.Top;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TestCirclePointOverlap(Circle circle, Transform2D circleTransform, Point point, Transform2D pointTransform)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool TestCirclePointOverlap(in Circle circle, in Transform2D circleTransform, in Point point, in Transform2D pointTransform)
|
||||||
{
|
{
|
||||||
var circleCenter = circleTransform.Position;
|
var circleCenter = circleTransform.Position;
|
||||||
var circleRadius = circle.Radius * circleTransform.Scale.X;
|
var circleRadius = circle.Radius * circleTransform.Scale.X;
|
||||||
|
@ -97,7 +205,8 @@ namespace MoonWorks.Collision.Fixed
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// NOTE: The rectangle must be axis aligned, and the scaling of the circle must be uniform.
|
/// NOTE: The rectangle must be axis aligned, and the scaling of the circle must be uniform.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool TestCircleRectangleOverlap(Circle circle, Transform2D circleTransform, Rectangle rectangle, Transform2D rectangleTransform)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool TestCircleRectangleOverlap(in Circle circle, in Transform2D circleTransform, in Rectangle rectangle, in Transform2D rectangleTransform)
|
||||||
{
|
{
|
||||||
var circleCenter = circleTransform.Position;
|
var circleCenter = circleTransform.Position;
|
||||||
var circleRadius = circle.Radius * circleTransform.Scale.X;
|
var circleRadius = circle.Radius * circleTransform.Scale.X;
|
||||||
|
@ -113,7 +222,8 @@ namespace MoonWorks.Collision.Fixed
|
||||||
return distanceSquared < (circleRadius * circleRadius);
|
return distanceSquared < (circleRadius * circleRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TestCircleOverlap(Circle circleA, Transform2D transformA, Circle circleB, Transform2D transformB)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool TestCircleOverlap(in Circle circleA, in Transform2D transformA, in Circle circleB, in Transform2D transformB)
|
||||||
{
|
{
|
||||||
var radiusA = circleA.Radius * transformA.Scale.X;
|
var radiusA = circleA.Radius * transformA.Scale.X;
|
||||||
var radiusB = circleB.Radius * transformB.Scale.Y;
|
var radiusB = circleB.Radius * transformB.Scale.Y;
|
||||||
|
@ -127,9 +237,14 @@ namespace MoonWorks.Collision.Fixed
|
||||||
return distanceSquared < radiusSumSquared;
|
return distanceSquared < radiusSumSquared;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (bool, Simplex2D) FindCollisionSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)
|
public static bool TestPointOverlap(in Point pointA, in Transform2D transformA, in Point pointB, in Transform2D transformB)
|
||||||
{
|
{
|
||||||
var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB);
|
return transformA.Position == transformB.Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (bool, Simplex2D) FindCollisionSimplex<T, U>(T shapeA, Transform2D transformA, U shapeB, Transform2D transformB) where T : IShape2D where U : IShape2D
|
||||||
|
{
|
||||||
|
var minkowskiDifference = new MinkowskiDifference<T, U>(shapeA, transformA, shapeB, transformB);
|
||||||
var c = minkowskiDifference.Support(Vector2.UnitX);
|
var c = minkowskiDifference.Support(Vector2.UnitX);
|
||||||
var b = minkowskiDifference.Support(-Vector2.UnitX);
|
var b = minkowskiDifference.Support(-Vector2.UnitX);
|
||||||
return Check(minkowskiDifference, c, b);
|
return Check(minkowskiDifference, c, b);
|
||||||
|
@ -208,12 +323,13 @@ namespace MoonWorks.Collision.Fixed
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Vector2 CalculateSupport(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, Vector2 direction)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static Vector2 CalculateSupport<T, U>(T shapeA, Transform2D Transform2DA, U shapeB, Transform2D Transform2DB, Vector2 direction) where T : IShape2D where U : IShape2D
|
||||||
{
|
{
|
||||||
return shapeA.Support(direction, Transform2DA) - shapeB.Support(-direction, Transform2DB);
|
return shapeA.Support(direction, Transform2DA) - shapeB.Support(-direction, Transform2DB);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (bool, Simplex2D) Check(MinkowskiDifference minkowskiDifference, Vector2 c, Vector2 b)
|
private static (bool, Simplex2D) Check<T, U>(MinkowskiDifference<T, U> minkowskiDifference, Vector2 c, Vector2 b) where T : IShape2D where U : IShape2D
|
||||||
{
|
{
|
||||||
var cb = c - b;
|
var cb = c - b;
|
||||||
var c0 = -c;
|
var c0 = -c;
|
||||||
|
@ -221,7 +337,7 @@ namespace MoonWorks.Collision.Fixed
|
||||||
return DoSimplex(minkowskiDifference, new Simplex2D(b, c), d);
|
return DoSimplex(minkowskiDifference, new Simplex2D(b, c), d);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (bool, Simplex2D) DoSimplex(MinkowskiDifference minkowskiDifference, Simplex2D simplex, Vector2 direction)
|
private static (bool, Simplex2D) DoSimplex<T, U>(MinkowskiDifference<T, U> minkowskiDifference, Simplex2D simplex, Vector2 direction) where T : IShape2D where U : IShape2D
|
||||||
{
|
{
|
||||||
var a = minkowskiDifference.Support(direction);
|
var a = minkowskiDifference.Support(direction);
|
||||||
var notPastOrigin = Vector2.Dot(a, direction) < Fix64.Zero;
|
var notPastOrigin = Vector2.Dot(a, direction) < Fix64.Zero;
|
||||||
|
@ -320,7 +436,8 @@ namespace MoonWorks.Collision.Fixed
|
||||||
return collinear ? new Vector2(a.Y, -a.X) : d;
|
return collinear ? new Vector2(a.Y, -a.X) : d;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool SameDirection(Vector2 a, Vector2 b)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static bool SameDirection(Vector2 a, Vector2 b)
|
||||||
{
|
{
|
||||||
return Vector2.Dot(a, b) > Fix64.Zero;
|
return Vector2.Dot(a, b) > Fix64.Zero;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using MoonWorks.Math.Fixed;
|
using MoonWorks.Math.Fixed;
|
||||||
|
|
||||||
namespace MoonWorks.Collision.Fixed
|
namespace MoonWorks.Collision.Fixed
|
||||||
|
@ -10,13 +9,6 @@ namespace MoonWorks.Collision.Fixed
|
||||||
{
|
{
|
||||||
public Fix64 Radius { get; }
|
public Fix64 Radius { get; }
|
||||||
public AABB2D AABB { get; }
|
public AABB2D AABB { get; }
|
||||||
public IEnumerable<IShape2D> Shapes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
yield return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Circle(Fix64 radius)
|
public Circle(Fix64 radius)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MoonWorks.Math.Fixed;
|
using MoonWorks.Math.Fixed;
|
||||||
|
|
||||||
namespace MoonWorks.Collision.Fixed
|
namespace MoonWorks.Collision.Fixed
|
||||||
|
@ -13,14 +13,6 @@ namespace MoonWorks.Collision.Fixed
|
||||||
|
|
||||||
public AABB2D AABB { get; }
|
public AABB2D AABB { get; }
|
||||||
|
|
||||||
public IEnumerable<IShape2D> Shapes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
yield return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Line(Vector2 start, Vector2 end)
|
public Line(Vector2 start, Vector2 end)
|
||||||
{
|
{
|
||||||
Start = start;
|
Start = start;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MoonWorks.Math.Fixed;
|
using MoonWorks.Math.Fixed;
|
||||||
|
|
||||||
namespace MoonWorks.Collision.Fixed
|
namespace MoonWorks.Collision.Fixed
|
||||||
|
@ -54,33 +54,6 @@ namespace MoonWorks.Collision.Fixed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Vector2> Vertices
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
yield return (Vector2) a;
|
|
||||||
if (b.HasValue) { yield return (Vector2) b; }
|
|
||||||
if (c.HasValue) { yield return (Vector2) c; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2 Support(Vector2 direction, Transform2D transform)
|
|
||||||
{
|
|
||||||
var maxDotProduct = Fix64.MinValue;
|
|
||||||
var maxVertex = a;
|
|
||||||
foreach (var vertex in Vertices)
|
|
||||||
{
|
|
||||||
var transformed = Vector2.Transform(vertex, transform.TransformMatrix);
|
|
||||||
var dot = Vector2.Dot(transformed, direction);
|
|
||||||
if (dot > maxDotProduct)
|
|
||||||
{
|
|
||||||
maxVertex = transformed;
|
|
||||||
maxDotProduct = dot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return maxVertex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Insert(Vector2 point, int index)
|
public void Insert(Vector2 point, int index)
|
||||||
{
|
{
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
|
@ -120,7 +93,7 @@ namespace MoonWorks.Collision.Fixed
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return System.HashCode.Combine(Vertices);
|
return System.HashCode.Combine(a, b, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(Simplex2D a, Simplex2D b)
|
public static bool operator ==(Simplex2D a, Simplex2D b)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MoonWorks.Math.Fixed;
|
using MoonWorks.Math.Fixed;
|
||||||
|
|
||||||
namespace MoonWorks.Collision.Fixed
|
namespace MoonWorks.Collision.Fixed
|
||||||
|
@ -7,13 +7,12 @@ namespace MoonWorks.Collision.Fixed
|
||||||
/// Used to quickly check if two shapes are potentially overlapping.
|
/// Used to quickly check if two shapes are potentially overlapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type that will be used to uniquely identify shape-transform pairs.</typeparam>
|
/// <typeparam name="T">The type that will be used to uniquely identify shape-transform pairs.</typeparam>
|
||||||
public class SpatialHash2D<T> where T : System.IEquatable<T>
|
public class SpatialHash2D<T, U> where T : struct, System.IEquatable<T> where U : IHasAABB2D
|
||||||
{
|
{
|
||||||
private readonly Fix64 cellSize;
|
private readonly Fix64 cellSize;
|
||||||
|
|
||||||
private readonly Dictionary<long, HashSet<T>> hashDictionary = new Dictionary<long, HashSet<T>>();
|
private readonly Dictionary<long, HashSet<T>> hashDictionary = new Dictionary<long, HashSet<T>>();
|
||||||
// FIXME: this ICollidable causes boxing which triggers garbage collection
|
private readonly Dictionary<T, (U, Transform2D, uint)> IDLookup = new Dictionary<T, (U, Transform2D, uint)>();
|
||||||
private readonly Dictionary<T, (ICollidable, Transform2D, uint)> IDLookup = new Dictionary<T, (ICollidable, Transform2D, uint)>();
|
|
||||||
|
|
||||||
public int MinX { get; private set; } = 0;
|
public int MinX { get; private set; } = 0;
|
||||||
public int MaxX { get; private set; } = 0;
|
public int MaxX { get; private set; } = 0;
|
||||||
|
@ -39,9 +38,9 @@ namespace MoonWorks.Collision.Fixed
|
||||||
/// <param name="shape"></param>
|
/// <param name="shape"></param>
|
||||||
/// <param name="transform2D"></param>
|
/// <param name="transform2D"></param>
|
||||||
/// <param name="collisionGroups">A bitmask value specifying the groups this object belongs to.</param>
|
/// <param name="collisionGroups">A bitmask value specifying the groups this object belongs to.</param>
|
||||||
public void Insert(T id, ICollidable shape, Transform2D transform2D, uint collisionGroups = uint.MaxValue)
|
public void Insert(T id, U shape, Transform2D transform2D, uint collisionGroups = uint.MaxValue)
|
||||||
{
|
{
|
||||||
var box = shape.TransformedAABB(transform2D);
|
var box = AABB2D.Transformed(shape.AABB, transform2D);
|
||||||
var minHash = Hash(box.Min);
|
var minHash = Hash(box.Min);
|
||||||
var maxHash = Hash(box.Max);
|
var maxHash = Hash(box.Max);
|
||||||
|
|
||||||
|
@ -65,11 +64,9 @@ namespace MoonWorks.Collision.Fixed
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all the potential collisions of a shape-transform pair. Excludes any shape-transforms with the given ID.
|
/// Retrieves all the potential collisions of a shape-transform pair. Excludes any shape-transforms with the given ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<(T, ICollidable, Transform2D, uint)> Retrieve(T id, ICollidable shape, Transform2D transform2D, uint collisionMask = uint.MaxValue)
|
public RetrieveEnumerator Retrieve<V>(T id, V hasAABB, Transform2D transform2D, uint collisionMask = uint.MaxValue) where V : IHasAABB2D
|
||||||
{
|
{
|
||||||
var returned = AcquireHashSet();
|
var box = AABB2D.Transformed(hasAABB.AABB, transform2D);
|
||||||
|
|
||||||
var box = shape.TransformedAABB(transform2D);
|
|
||||||
var (minX, minY) = Hash(box.Min);
|
var (minX, minY) = Hash(box.Min);
|
||||||
var (maxX, maxY) = Hash(box.Max);
|
var (maxX, maxY) = Hash(box.Max);
|
||||||
|
|
||||||
|
@ -78,64 +75,21 @@ namespace MoonWorks.Collision.Fixed
|
||||||
if (minY < MinY) { minY = MinY; }
|
if (minY < MinY) { minY = MinY; }
|
||||||
if (maxY > MaxY) { maxY = MaxY; }
|
if (maxY > MaxY) { maxY = MaxY; }
|
||||||
|
|
||||||
foreach (var key in Keys(minX, minY, maxX, maxY))
|
return new RetrieveEnumerator(
|
||||||
{
|
this,
|
||||||
if (hashDictionary.ContainsKey(key))
|
Keys(minX, minY, maxX, maxY),
|
||||||
{
|
id,
|
||||||
foreach (var t in hashDictionary[key])
|
collisionMask
|
||||||
{
|
);
|
||||||
if (!returned.Contains(t))
|
|
||||||
{
|
|
||||||
var (otherShape, otherTransform, collisionGroups) = IDLookup[t];
|
|
||||||
if (!id.Equals(t) && ((collisionGroups & collisionMask) > 0) && AABB2D.TestOverlap(box, otherShape.TransformedAABB(otherTransform)))
|
|
||||||
{
|
|
||||||
returned.Add(t);
|
|
||||||
yield return (t, otherShape, otherTransform, collisionGroups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeHashSet(returned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all the potential collisions of a shape-transform pair.
|
/// Retrieves all the potential collisions of a shape-transform pair.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<(T, ICollidable, Transform2D, uint)> Retrieve(ICollidable shape, Transform2D transform2D, uint collisionMask = uint.MaxValue)
|
public RetrieveEnumerator Retrieve<V>(V hasAABB, Transform2D transform2D, uint collisionMask = uint.MaxValue) where V : IHasAABB2D
|
||||||
{
|
{
|
||||||
var returned = AcquireHashSet();
|
var box = AABB2D.Transformed(hasAABB.AABB, transform2D);
|
||||||
|
return Retrieve(box, collisionMask);
|
||||||
var box = shape.TransformedAABB(transform2D);
|
|
||||||
var (minX, minY) = Hash(box.Min);
|
|
||||||
var (maxX, maxY) = Hash(box.Max);
|
|
||||||
|
|
||||||
if (minX < MinX) { minX = MinX; }
|
|
||||||
if (maxX > MaxX) { maxX = MaxX; }
|
|
||||||
if (minY < MinY) { minY = MinY; }
|
|
||||||
if (maxY > MaxY) { maxY = MaxY; }
|
|
||||||
|
|
||||||
foreach (var key in Keys(minX, minY, maxX, maxY))
|
|
||||||
{
|
|
||||||
if (hashDictionary.ContainsKey(key))
|
|
||||||
{
|
|
||||||
foreach (var t in hashDictionary[key])
|
|
||||||
{
|
|
||||||
if (!returned.Contains(t))
|
|
||||||
{
|
|
||||||
var (otherShape, otherTransform, collisionGroups) = IDLookup[t];
|
|
||||||
if (((collisionGroups & collisionMask) > 0) && AABB2D.TestOverlap(box, otherShape.TransformedAABB(otherTransform)))
|
|
||||||
{
|
|
||||||
returned.Add(t);
|
|
||||||
yield return (t, otherShape, otherTransform, collisionGroups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeHashSet(returned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -143,10 +97,8 @@ namespace MoonWorks.Collision.Fixed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="aabb">A transformed AABB.</param>
|
/// <param name="aabb">A transformed AABB.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public IEnumerable<(T, ICollidable, Transform2D, uint)> Retrieve(AABB2D aabb, uint collisionMask = uint.MaxValue)
|
public RetrieveEnumerator Retrieve(AABB2D aabb, uint collisionMask = uint.MaxValue)
|
||||||
{
|
{
|
||||||
var returned = AcquireHashSet();
|
|
||||||
|
|
||||||
var (minX, minY) = Hash(aabb.Min);
|
var (minX, minY) = Hash(aabb.Min);
|
||||||
var (maxX, maxY) = Hash(aabb.Max);
|
var (maxX, maxY) = Hash(aabb.Max);
|
||||||
|
|
||||||
|
@ -155,28 +107,14 @@ namespace MoonWorks.Collision.Fixed
|
||||||
if (minY < MinY) { minY = MinY; }
|
if (minY < MinY) { minY = MinY; }
|
||||||
if (maxY > MaxY) { maxY = MaxY; }
|
if (maxY > MaxY) { maxY = MaxY; }
|
||||||
|
|
||||||
foreach (var key in Keys(minX, minY, maxX, maxY))
|
return new RetrieveEnumerator(
|
||||||
{
|
this,
|
||||||
if (hashDictionary.ContainsKey(key))
|
Keys(minX, minY, maxX, maxY),
|
||||||
{
|
collisionMask
|
||||||
foreach (var t in hashDictionary[key])
|
);
|
||||||
{
|
|
||||||
if (!returned.Contains(t))
|
|
||||||
{
|
|
||||||
var (otherShape, otherTransform, collisionGroups) = IDLookup[t];
|
|
||||||
if (((collisionGroups & collisionMask) > 0) && AABB2D.TestOverlap(aabb, otherShape.TransformedAABB(otherTransform)))
|
|
||||||
{
|
|
||||||
yield return (t, otherShape, otherTransform, collisionGroups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeHashSet(returned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(T id, ICollidable shape, Transform2D transform2D, uint collisionGroups = uint.MaxValue)
|
public void Update(T id, U shape, Transform2D transform2D, uint collisionGroups = uint.MaxValue)
|
||||||
{
|
{
|
||||||
Remove(id);
|
Remove(id);
|
||||||
Insert(id, shape, transform2D, collisionGroups);
|
Insert(id, shape, transform2D, collisionGroups);
|
||||||
|
@ -189,7 +127,7 @@ namespace MoonWorks.Collision.Fixed
|
||||||
{
|
{
|
||||||
var (shape, transform, collisionGroups) = IDLookup[id];
|
var (shape, transform, collisionGroups) = IDLookup[id];
|
||||||
|
|
||||||
var box = shape.TransformedAABB(transform);
|
var box = AABB2D.Transformed(shape.AABB, transform);
|
||||||
var minHash = Hash(box.Min);
|
var minHash = Hash(box.Min);
|
||||||
var maxHash = Hash(box.Max);
|
var maxHash = Hash(box.Max);
|
||||||
|
|
||||||
|
@ -222,18 +160,12 @@ namespace MoonWorks.Collision.Fixed
|
||||||
return ((long) left << 32) | ((uint) right);
|
return ((long) left << 32) | ((uint) right);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<long> Keys(int minX, int minY, int maxX, int maxY)
|
internal static KeysEnumerator Keys(int minX, int minY, int maxX, int maxY)
|
||||||
{
|
{
|
||||||
for (var i = minX; i <= maxX; i++)
|
return new KeysEnumerator(minX, minY, maxX, maxY);
|
||||||
{
|
|
||||||
for (var j = minY; j <= maxY; j++)
|
|
||||||
{
|
|
||||||
yield return MakeLong(i, j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashSet<T> AcquireHashSet()
|
internal HashSet<T> AcquireHashSet()
|
||||||
{
|
{
|
||||||
if (hashSetPool.Count == 0)
|
if (hashSetPool.Count == 0)
|
||||||
{
|
{
|
||||||
|
@ -245,9 +177,147 @@ namespace MoonWorks.Collision.Fixed
|
||||||
return hashSet;
|
return hashSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FreeHashSet(HashSet<T> hashSet)
|
internal void FreeHashSet(HashSet<T> hashSet)
|
||||||
{
|
{
|
||||||
hashSetPool.Enqueue(hashSet);
|
hashSetPool.Enqueue(hashSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal ref struct KeysEnumerator
|
||||||
|
{
|
||||||
|
private int MinX;
|
||||||
|
private int MinY;
|
||||||
|
private int MaxX;
|
||||||
|
private int MaxY;
|
||||||
|
private int i, j;
|
||||||
|
|
||||||
|
public KeysEnumerator GetEnumerator() => this;
|
||||||
|
|
||||||
|
public KeysEnumerator(int minX, int minY, int maxX, int maxY)
|
||||||
|
{
|
||||||
|
MinX = minX;
|
||||||
|
MinY = minY;
|
||||||
|
MaxX = maxX;
|
||||||
|
MaxY = maxY;
|
||||||
|
i = minX;
|
||||||
|
j = minY - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (j < MaxY)
|
||||||
|
{
|
||||||
|
j += 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (i < MaxX)
|
||||||
|
{
|
||||||
|
i += 1;
|
||||||
|
j = MinY;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return MakeLong(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref struct RetrieveEnumerator
|
||||||
|
{
|
||||||
|
public SpatialHash2D<T, U> SpatialHash;
|
||||||
|
private KeysEnumerator KeysEnumerator;
|
||||||
|
private HashSet<T>.Enumerator HashSetEnumerator;
|
||||||
|
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
|
||||||
|
) {
|
||||||
|
SpatialHash = spatialHash;
|
||||||
|
KeysEnumerator = keysEnumerator;
|
||||||
|
HashSetEnumerator = default;
|
||||||
|
HashSetEnumeratorActive = false;
|
||||||
|
Duplicates = SpatialHash.AcquireHashSet();
|
||||||
|
ID = id;
|
||||||
|
CollisionMask = collisionMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal RetrieveEnumerator(
|
||||||
|
SpatialHash2D<T, U> spatialHash,
|
||||||
|
KeysEnumerator keysEnumerator,
|
||||||
|
uint collisionMask
|
||||||
|
) {
|
||||||
|
SpatialHash = spatialHash;
|
||||||
|
KeysEnumerator = keysEnumerator;
|
||||||
|
HashSetEnumerator = default;
|
||||||
|
HashSetEnumeratorActive = false;
|
||||||
|
Duplicates = SpatialHash.AcquireHashSet();
|
||||||
|
ID = null;
|
||||||
|
CollisionMask = collisionMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (!HashSetEnumeratorActive || !HashSetEnumerator.MoveNext())
|
||||||
|
{
|
||||||
|
if (!KeysEnumerator.MoveNext())
|
||||||
|
{
|
||||||
|
SpatialHash.FreeHashSet(Duplicates);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SpatialHash.hashDictionary.TryGetValue(KeysEnumerator.Current, out var hashset))
|
||||||
|
{
|
||||||
|
HashSetEnumerator = hashset.GetEnumerator();
|
||||||
|
HashSetEnumeratorActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// conditions
|
||||||
|
var t = HashSetEnumerator.Current;
|
||||||
|
var collisionGroups = SpatialHash.IDLookup[t].Item3;
|
||||||
|
|
||||||
|
if (Duplicates.Contains(t))
|
||||||
|
{
|
||||||
|
return MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ID.HasValue)
|
||||||
|
{
|
||||||
|
if (ID.Value.Equals(t) || (CollisionMask & collisionGroups) == 0)
|
||||||
|
{
|
||||||
|
return MoveNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Duplicates.Add(t);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public (T, U, Transform2D, uint) Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var t = HashSetEnumerator.Current;
|
||||||
|
var (u, transform, groups) = SpatialHash.IDLookup[t];
|
||||||
|
return (t, u, transform, groups);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue