forked from MoonsideGames/MoonTools.Bonk
add sweep test
parent
d23238bcfc
commit
9777429d07
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using MoonTools.Core.Structs;
|
||||||
|
|
||||||
|
namespace MoonTools.Core.Bonk
|
||||||
|
{
|
||||||
|
public struct SweepResult<T, U> where T : IEquatable<T> where U : struct, IShape2D
|
||||||
|
{
|
||||||
|
public static SweepResult<T, U> False = new SweepResult<T, U>();
|
||||||
|
|
||||||
|
public bool Hit { get; }
|
||||||
|
public Vector2 Motion { get; }
|
||||||
|
public T ID { get; }
|
||||||
|
public U Shape { get; }
|
||||||
|
public Transform2D Transform { get; }
|
||||||
|
|
||||||
|
public SweepResult(bool hit, Vector2 motion, T id, U shape, Transform2D transform)
|
||||||
|
{
|
||||||
|
Hit = hit;
|
||||||
|
Motion = motion;
|
||||||
|
ID = id;
|
||||||
|
Shape = shape;
|
||||||
|
Transform = transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using MoonTools.Core.Structs;
|
||||||
|
|
||||||
|
namespace MoonTools.Core.Bonk
|
||||||
|
{
|
||||||
|
public static class SweepTest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a sweep test on rectangles.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="spatialHash">A spatial hash.</param>
|
||||||
|
/// <param name="rectangle"></param>
|
||||||
|
/// <param name="transform">A transform by which to transform the IHasAABB2D.</param>
|
||||||
|
/// <param name="ray">Given in world-space.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static SweepResult<T, Rectangle> Rectangle<T>(SpatialHash<T> spatialHash, Rectangle rectangle, Transform2D transform, Vector2 ray) where T : IEquatable<T>
|
||||||
|
{
|
||||||
|
var transformedAABB = rectangle.TransformedAABB(transform);
|
||||||
|
var sweepBox = SweepBox(transformedAABB, ray);
|
||||||
|
|
||||||
|
var shortestDistance = float.MaxValue;
|
||||||
|
var nearestID = default(T);
|
||||||
|
Rectangle? nearestRectangle = null;
|
||||||
|
Transform2D? nearestTransform = null;
|
||||||
|
|
||||||
|
foreach (var (id, shape, shapeTransform) in spatialHash.Retrieve(sweepBox))
|
||||||
|
{
|
||||||
|
if (shape is Rectangle otherRectangle)
|
||||||
|
{
|
||||||
|
var otherTransformedAABB = otherRectangle.TransformedAABB(shapeTransform);
|
||||||
|
float xInvEntry, yInvEntry;
|
||||||
|
|
||||||
|
if (ray.X > 0)
|
||||||
|
{
|
||||||
|
xInvEntry = shapeTransform.Position.X - (transform.Position.X + transformedAABB.Width);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xInvEntry = (shapeTransform.Position.X + otherTransformedAABB.Width) - transform.Position.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ray.Y > 0)
|
||||||
|
{
|
||||||
|
yInvEntry = shapeTransform.Position.Y - (transform.Position.Y + transformedAABB.Height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yInvEntry = (shapeTransform.Position.Y + otherTransformedAABB.Height) - shapeTransform.Position.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float xEntry, yEntry;
|
||||||
|
|
||||||
|
if (ray.X == 0)
|
||||||
|
{
|
||||||
|
xEntry = float.MinValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xEntry = xInvEntry / ray.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ray.Y == 0)
|
||||||
|
{
|
||||||
|
yEntry = float.MinValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yEntry = yInvEntry / ray.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entryTime = Math.Max(xEntry, yEntry);
|
||||||
|
|
||||||
|
if (entryTime > 0 && entryTime < 1)
|
||||||
|
{
|
||||||
|
if (entryTime < shortestDistance)
|
||||||
|
{
|
||||||
|
shortestDistance = entryTime;
|
||||||
|
nearestID = id;
|
||||||
|
nearestRectangle = rectangle;
|
||||||
|
nearestTransform = shapeTransform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nearestRectangle.HasValue)
|
||||||
|
{
|
||||||
|
return new SweepResult<T, Rectangle>(true, ray * shortestDistance, nearestID, nearestRectangle.Value, nearestTransform.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return SweepResult<T, Rectangle>.False;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AABB SweepBox(AABB aabb, Vector2 ray)
|
||||||
|
{
|
||||||
|
return new AABB(
|
||||||
|
Math.Min(aabb.Min.X, aabb.Min.X + ray.X),
|
||||||
|
Math.Min(aabb.Min.Y, aabb.Min.Y + ray.Y),
|
||||||
|
Math.Max(aabb.Max.X, aabb.Max.X + ray.X),
|
||||||
|
Math.Max(aabb.Max.Y, aabb.Max.Y + ray.Y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
using System.Numerics;
|
||||||
|
using FluentAssertions;
|
||||||
|
using MoonTools.Core.Bonk;
|
||||||
|
using MoonTools.Core.Structs;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Tests
|
||||||
|
{
|
||||||
|
class SweepTestTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void SweepsThrough()
|
||||||
|
{
|
||||||
|
var rectangle = new Rectangle(4, 4);
|
||||||
|
var transform = new Transform2D(new Position2D(-6, 0));
|
||||||
|
|
||||||
|
var otherRectangle = new Rectangle(4, 4);
|
||||||
|
var otherTransform = new Transform2D(new Position2D(6, 0));
|
||||||
|
|
||||||
|
var farthestRectangle = new Rectangle(4, 4);
|
||||||
|
var farthestTransform = new Transform2D(new Position2D(12, 0));
|
||||||
|
|
||||||
|
var spatialHash = new SpatialHash<int>(16);
|
||||||
|
spatialHash.Insert(1, otherRectangle, otherTransform);
|
||||||
|
spatialHash.Insert(2, farthestRectangle, farthestTransform);
|
||||||
|
|
||||||
|
SweepTest.Rectangle(spatialHash, rectangle, transform, new Vector2(12, 0)).Should().Be(
|
||||||
|
new SweepResult<int, Rectangle>(true, new Vector2(8, 0), 1, otherRectangle, otherTransform)
|
||||||
|
);
|
||||||
|
|
||||||
|
SweepTest.Rectangle(spatialHash, rectangle, transform, new Vector2(-12, 0)).Hit.Should().BeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue