-
Notifications
You must be signed in to change notification settings - Fork 0
/
CollisionUtils.cs
199 lines (156 loc) · 6.1 KB
/
CollisionUtils.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace MazeGamePillaPilla
{
static class CollisionUtils
{
public struct Range
{
public float min;
public float max;
public Range(float min, float max)
{
this.min = min;
this.max = max;
}
}
private static Random rng = new Random();
private static Range Get1dProjectionOntoAxis(this IIntersectable intersectable, Vector2 axis)
{
float min = float.PositiveInfinity, max = float.NegativeInfinity;
foreach (Vector2 vertex in intersectable.GetVertices())
{
float projection = Vector2.Dot(vertex, axis);
if (projection < min) min = projection;
if (projection > max) max = projection;
}
return new Range(min, max);
}
private static bool IsThere1dProjectionOverlap(Range A, Range B)
{
return !(A.min > B.max || A.max < B.min);
}
// Should be called after IsThere1dProjectionOverlap
private static float Get1dProjectionOverlap(Range A, Range B)
{
return A.min < B.min ? A.max - B.min : A.min - B.max;
}
public static bool AabbAabbIntersectionTest(this IIntersectable A, IIntersectable B)
{
Rectangle a = A.GetAABB();
Rectangle b = B.GetAABB();
return !(a.Right < b.Left || a.Left > b.Right || a.Bottom < b.Top || a.Top > b.Bottom);
}
public static bool SatIntersectionTest(this IIntersectable A, IIntersectable B)
{
if (A.GetSatProjectionAxes().Length == 0 || B.GetSatProjectionAxes().Length == 0) return false;
List<Vector2> axes = new List<Vector2>();
axes.AddRange(A.GetSatProjectionAxes());
axes.AddRange(B.GetSatProjectionAxes());
foreach (Vector2 axis in axes)
{
Range projectionA = A.Get1dProjectionOntoAxis(axis);
Range projectionB = B.Get1dProjectionOntoAxis(axis);
if (!IsThere1dProjectionOverlap(projectionA, projectionB))
{
return false;
}
}
return true;
}
public static Vector2? SatIntersectionTestGetMtv(this IIntersectable A, IIntersectable B)
{
if (A.GetSatProjectionAxes().Length == 0 || B.GetSatProjectionAxes().Length == 0) return null;
List<Vector2> axes = new List<Vector2>();
axes.AddRange(A.GetSatProjectionAxes());
axes.AddRange(B.GetSatProjectionAxes());
axes = new HashSet<Vector2>(axes).ToList(); // remove duplicated axes
float minOverlap = float.PositiveInfinity;
Vector2? minOverlapAxis = null;
foreach (Vector2 axis in axes)
{
Range projectionA = A.Get1dProjectionOntoAxis(axis);
Range projectionB = B.Get1dProjectionOntoAxis(axis);
if (!IsThere1dProjectionOverlap(projectionA, projectionB))
{
return null;
}
float overlap = Get1dProjectionOverlap(projectionA, projectionB);
if (Math.Abs(overlap) < Math.Abs(minOverlap))
{
minOverlap = overlap;
minOverlapAxis = axis;
}
}
if (minOverlapAxis == null) return null;
return minOverlapAxis * minOverlap;
}
public static bool RayMapIntersectionTest(VisibilityRay ray, Cell[,] maze)
{
// TODO
// Optimize testing only against the possible instersection cells instead of the whole maze.
// Use the Bresenham Line-Drawing Algorithm to find de cells to check.
// http://playtechs.blogspot.cz/2007/03/raytracing-on-grid.html
foreach (Cell cell in maze)
{
if (ray.AabbAabbIntersectionTest(cell))
{
if (ray.SatIntersectionTest(cell))
{
return true;
}
}
}
return false;
}
public static bool AabbMapIntersectionTest(this IIntersectable item, Cell[,] maze)
{
foreach (Cell cell in item.GetSurroundingCells(maze))
{
if (item.AabbAabbIntersectionTest(cell))
{
if (item.SatIntersectionTest(cell))
{
return true;
}
}
}
return false;
}
private static Cell[] GetSurroundingCells(this IIntersectable item, Cell[,] maze)
{
Rectangle aabb = item.GetAABB();
List<Cell> cells = new List<Cell>();
int currentCellX = aabb.Center.X / Tile.Size;
int currentCellY = aabb.Center.Y / Tile.Size;
int smallX = currentCellX - 1;
int largeX = currentCellX + 1;
int smallY = currentCellY - 1;
int largeY = currentCellY + 1;
for (int y = smallY; y <= largeY; y++)
{
for (int x = smallX; x <= largeX; x++)
{
Cell cell = maze[y, x];
if (cell.Tile.Id != 0)
{
cells.Add(cell);
}
}
}
return cells.ToArray();
}
public static void SpawnInAnEmptyPosition(this ISpawnable itemToSpawn, Cell[,] maze)
{
IIntersectable intersectable = (IIntersectable)itemToSpawn;
do
{
itemToSpawn.SetPosition(
rng.Next(2 * Tile.Size, (maze.GetLength(1) - 1) * Tile.Size),
rng.Next(2 * Tile.Size, (maze.GetLength(0) - 1) * Tile.Size));
} while (intersectable.AabbMapIntersectionTest(maze));
}
}
}