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