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