diff --git a/source/simulation2/components/CCmpPathfinder_Vertex.cpp b/source/simulation2/components/CCmpPathfinder_Vertex.cpp
index 5ab11b6..b42fb62 100644
a
|
b
|
|
39 | 39 | #include "simulation2/components/ICmpObstructionManager.h" |
40 | 40 | #include "simulation2/helpers/PriorityQueue.h" |
41 | 41 | #include "simulation2/helpers/Render.h" |
| 42 | #include "simulation2/helpers/Geometry.h" |
42 | 43 | |
43 | 44 | /* Quadrant optimisation: |
44 | 45 | * (loosely based on GPG2 "Optimizing Points-of-Visibility Pathfinding") |
… |
… |
inline static bool CheckVisibilityLeft(const CFixedVector2D& a, const CFixedVect
|
186 | 187 | |
187 | 188 | for (size_t i = 0; i < edges.size(); ++i) |
188 | 189 | { |
189 | | if (b.X < edges[i].p0.X) |
| 190 | if (a.X > edges[i].p0.X || b.X < edges[i].p0.X) |
190 | 191 | continue; |
191 | 192 | |
192 | 193 | CFixedVector2D p0 (edges[i].p0.X, edges[i].c1); |
… |
… |
inline static bool CheckVisibilityRight(const CFixedVector2D& a, const CFixedVec
|
214 | 215 | |
215 | 216 | for (size_t i = 0; i < edges.size(); ++i) |
216 | 217 | { |
217 | | if (b.X > edges[i].p0.X) |
| 218 | if (a.X < edges[i].p0.X || b.X > edges[i].p0.X) |
218 | 219 | continue; |
219 | 220 | |
220 | 221 | CFixedVector2D p0 (edges[i].p0.X, edges[i].c1); |
… |
… |
inline static bool CheckVisibilityBottom(const CFixedVector2D& a, const CFixedVe
|
242 | 243 | |
243 | 244 | for (size_t i = 0; i < edges.size(); ++i) |
244 | 245 | { |
245 | | if (b.Y < edges[i].p0.Y) |
| 246 | if (a.Y > edges[i].p0.Y || b.Y < edges[i].p0.Y) |
246 | 247 | continue; |
247 | 248 | |
248 | 249 | CFixedVector2D p0 (edges[i].p0.X, edges[i].p0.Y); |
… |
… |
inline static bool CheckVisibilityTop(const CFixedVector2D& a, const CFixedVecto
|
270 | 271 | |
271 | 272 | for (size_t i = 0; i < edges.size(); ++i) |
272 | 273 | { |
273 | | if (b.Y > edges[i].p0.Y) |
| 274 | if (a.Y < edges[i].p0.Y || b.Y > edges[i].p0.Y) |
274 | 275 | continue; |
275 | 276 | |
276 | 277 | CFixedVector2D p0 (edges[i].p0.X, edges[i].p0.Y); |
… |
… |
inline static bool CheckVisibilityTop(const CFixedVector2D& a, const CFixedVecto
|
289 | 290 | return true; |
290 | 291 | } |
291 | 292 | |
| 293 | inline static bool CheckVisibilityAASquares(const CFixedVector2D& a, const CFixedVector2D& b, const std::vector<Square> edgeSquares) |
| 294 | { |
| 295 | for (size_t i = 0; i < edgeSquares.size(); ++i) |
| 296 | { |
| 297 | CFixedVector2D offset = (edgeSquares[i].p0 + edgeSquares[i].p1)/2; |
| 298 | CFixedVector2D halfSize = (edgeSquares[i].p1 - edgeSquares[i].p0)/2; |
| 299 | if (Geometry::TestRayAASquare(a - offset, b - offset, halfSize)) |
| 300 | return false; |
| 301 | } |
| 302 | return true; |
| 303 | } |
| 304 | |
292 | 305 | typedef PriorityQueueHeap<u16, fixed, fixed> VertexPriorityQueue; |
293 | 306 | |
294 | 307 | /** |
… |
… |
static void AddTerrainEdges(std::vector<Edge>& edges, std::vector<Vertex>& verte
|
480 | 493 | } |
481 | 494 | } |
482 | 495 | |
483 | | static void SplitAAEdges(const CFixedVector2D& a, |
484 | | const std::vector<Edge>& edges, |
485 | | const std::vector<Square>& squares, |
| 496 | static void SplitAAEdges(const std::vector<Edge>& edges, |
486 | 497 | std::vector<Edge>& edgesUnaligned, |
487 | 498 | std::vector<EdgeAA>& edgesLeft, std::vector<EdgeAA>& edgesRight, |
488 | 499 | std::vector<EdgeAA>& edgesBottom, std::vector<EdgeAA>& edgesTop) |
489 | 500 | { |
490 | | edgesLeft.reserve(squares.size()); |
491 | | edgesRight.reserve(squares.size()); |
492 | | edgesBottom.reserve(squares.size()); |
493 | | edgesTop.reserve(squares.size()); |
494 | | |
495 | | for (const Square& square : squares) |
496 | | { |
497 | | if (a.X <= square.p0.X) |
498 | | edgesLeft.emplace_back(EdgeAA{ square.p0, square.p1.Y }); |
499 | | if (a.X >= square.p1.X) |
500 | | edgesRight.emplace_back(EdgeAA{ square.p1, square.p0.Y }); |
501 | | if (a.Y <= square.p0.Y) |
502 | | edgesBottom.emplace_back(EdgeAA{ square.p0, square.p1.X }); |
503 | | if (a.Y >= square.p1.Y) |
504 | | edgesTop.emplace_back(EdgeAA{ square.p1, square.p0.X }); |
505 | | } |
506 | | |
507 | 501 | for (const Edge& edge : edges) |
508 | 502 | { |
509 | 503 | if (edge.p0.X == edge.p1.X) |
510 | 504 | { |
511 | 505 | if (edge.p1.Y < edge.p0.Y) |
512 | | { |
513 | | if (!(a.X <= edge.p0.X)) |
514 | | continue; |
515 | 506 | edgesLeft.emplace_back(EdgeAA{ edge.p1, edge.p0.Y }); |
516 | | } |
517 | 507 | else |
518 | | { |
519 | | if (!(a.X >= edge.p0.X)) |
520 | | continue; |
521 | 508 | edgesRight.emplace_back(EdgeAA{ edge.p1, edge.p0.Y }); |
522 | | } |
523 | 509 | } |
524 | 510 | else if (edge.p0.Y == edge.p1.Y) |
525 | 511 | { |
526 | 512 | if (edge.p0.X < edge.p1.X) |
527 | | { |
528 | | if (!(a.Y <= edge.p0.Y)) |
529 | | continue; |
530 | 513 | edgesBottom.emplace_back(EdgeAA{ edge.p0, edge.p1.X }); |
531 | | } |
532 | 514 | else |
533 | | { |
534 | | if (!(a.Y >= edge.p0.Y)) |
535 | | continue; |
536 | 515 | edgesTop.emplace_back(EdgeAA{ edge.p0, edge.p1.X }); |
537 | | } |
538 | 516 | } |
539 | 517 | else |
540 | 518 | edgesUnaligned.push_back(edge); |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
793 | 771 | u16 idBest = START_VERTEX_ID; |
794 | 772 | fixed hBest = start.h; |
795 | 773 | |
| 774 | // Detect axis-aligned edges for faster visibility checking. |
| 775 | std::vector<Edge> edgesUnaligned; |
| 776 | std::vector<EdgeAA> edgesLeft; |
| 777 | std::vector<EdgeAA> edgesRight; |
| 778 | std::vector<EdgeAA> edgesBottom; |
| 779 | std::vector<EdgeAA> edgesTop; |
| 780 | SplitAAEdges(edges, edgesUnaligned, edgesLeft, edgesRight, edgesBottom, edgesTop); |
| 781 | |
796 | 782 | while (!open.empty()) |
797 | 783 | { |
798 | 784 | // Move best tile from open to closed |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
810 | 796 | // since they're more likely to block the rays |
811 | 797 | std::sort(edgeSquares.begin(), edgeSquares.end(), SquareSort(vertexes[curr.id].p)); |
812 | 798 | |
813 | | std::vector<Edge> edgesUnaligned; |
814 | | std::vector<EdgeAA> edgesLeft; |
815 | | std::vector<EdgeAA> edgesRight; |
816 | | std::vector<EdgeAA> edgesBottom; |
817 | | std::vector<EdgeAA> edgesTop; |
818 | | SplitAAEdges(vertexes[curr.id].p, edges, edgeSquares, edgesUnaligned, edgesLeft, edgesRight, edgesBottom, edgesTop); |
819 | | |
820 | 799 | // Check the lines to every other vertex |
821 | 800 | for (size_t n = 0; n < vertexes.size(); ++n) |
822 | 801 | { |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
871 | 850 | CheckVisibilityRight(vertexes[curr.id].p, npos, edgesRight) && |
872 | 851 | CheckVisibilityBottom(vertexes[curr.id].p, npos, edgesBottom) && |
873 | 852 | CheckVisibilityTop(vertexes[curr.id].p, npos, edgesTop) && |
874 | | CheckVisibility(vertexes[curr.id].p, npos, edgesUnaligned); |
| 853 | CheckVisibility(vertexes[curr.id].p, npos, edgesUnaligned) && |
| 854 | CheckVisibilityAASquares(vertexes[curr.id].p, npos, edgeSquares); |
875 | 855 | |
876 | 856 | /* |
877 | 857 | // Render the edges that we examine |
diff --git a/source/simulation2/helpers/Geometry.cpp b/source/simulation2/helpers/Geometry.cpp
index 4173a7a..0fcc5b3 100644
a
|
b
|
bool Geometry::TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, c
|
196 | 196 | /* |
197 | 197 | * We only consider collisions to be when the ray goes from outside to inside the shape (and possibly out again). |
198 | 198 | * Various cases to consider: |
199 | | * 'a' inside, 'b' inside -> no collision |
200 | | * 'a' inside, 'b' outside -> no collision |
201 | | * 'a' outside, 'b' inside -> collision |
202 | | * 'a' outside, 'b' outside -> depends; use separating axis theorem: |
| 199 | * 'a' inside -> no collision |
| 200 | * use separating axis theorem: |
203 | 201 | * if the ray's bounding box is outside the square -> no collision |
204 | 202 | * if the whole square is on the same side of the ray -> no collision |
205 | 203 | * otherwise -> collision |
… |
… |
bool Geometry::TestRaySquare(const CFixedVector2D& a, const CFixedVector2D& b, c
|
218 | 216 | fixed bu = b.Dot(u); |
219 | 217 | fixed bv = b.Dot(v); |
220 | 218 | |
221 | | if (-hw <= bu && bu <= hw && -hh <= bv && bv <= hh) // TODO: isn't this subsumed by the next checks? |
222 | | return true; // a is outside, b is inside |
223 | | |
224 | 219 | if ((au < -hw && bu < -hw) || (au > hw && bu > hw) || (av < -hh && bv < -hh) || (av > hh && bv > hh)) |
225 | 220 | return false; // ab is entirely above/below/side the square |
226 | 221 | |
227 | 222 | CFixedVector2D abp = (b - a).Perpendicular(); |
228 | | fixed s0 = abp.Dot((u.Multiply(hw) + v.Multiply(hh)) - a); |
229 | | fixed s1 = abp.Dot((u.Multiply(hw) - v.Multiply(hh)) - a); |
230 | | fixed s2 = abp.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a); |
231 | | fixed s3 = abp.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a); |
232 | | if (s0.IsZero() || s1.IsZero() || s2.IsZero() || s3.IsZero()) |
233 | | return true; // ray intersects the corner |
234 | | |
235 | | bool sign = (s0 < fixed::Zero()); |
236 | | if ((s1 < fixed::Zero()) != sign || (s2 < fixed::Zero()) != sign || (s3 < fixed::Zero()) != sign) |
237 | | return true; // ray cuts through the square |
238 | | |
239 | | return false; |
| 223 | fixed s0 = a.Dot(abp).Absolute(); |
| 224 | fixed s1 = u.Dot(abp).Absolute().Multiply(hw) + v.Dot(abp).Absolute().Multiply(hh); |
| 225 | return (s0 <= s1); |
240 | 226 | } |
241 | 227 | |
242 | 228 | // Exactly like TestRaySquare with u=(1,0), v=(0,1) |
… |
… |
bool Geometry::TestRayAASquare(const CFixedVector2D& a, const CFixedVector2D& b,
|
248 | 234 | if (-hw <= a.X && a.X <= hw && -hh <= a.Y && a.Y <= hh) |
249 | 235 | return false; // a is inside |
250 | 236 | |
251 | | if (-hw <= b.X && b.X <= hw && -hh <= b.Y && b.Y <= hh) // TODO: isn't this subsumed by the next checks? |
252 | | return true; // a is outside, b is inside |
253 | | |
254 | 237 | if ((a.X < -hw && b.X < -hw) || (a.X > hw && b.X > hw) || (a.Y < -hh && b.Y < -hh) || (a.Y > hh && b.Y > hh)) |
255 | 238 | return false; // ab is entirely above/below/side the square |
256 | 239 | |
257 | 240 | CFixedVector2D abp = (b - a).Perpendicular(); |
258 | | fixed s0 = abp.Dot(CFixedVector2D(hw, hh) - a); |
259 | | fixed s1 = abp.Dot(CFixedVector2D(hw, -hh) - a); |
260 | | fixed s2 = abp.Dot(CFixedVector2D(-hw, -hh) - a); |
261 | | fixed s3 = abp.Dot(CFixedVector2D(-hw, hh) - a); |
262 | | if (s0.IsZero() || s1.IsZero() || s2.IsZero() || s3.IsZero()) |
263 | | return true; // ray intersects the corner |
264 | | |
265 | | bool sign = (s0 < fixed::Zero()); |
266 | | if ((s1 < fixed::Zero()) != sign || (s2 < fixed::Zero()) != sign || (s3 < fixed::Zero()) != sign) |
267 | | return true; // ray cuts through the square |
268 | | |
269 | | return false; |
| 241 | fixed s0 = a.Dot(abp).Absolute(); |
| 242 | fixed s1 = abp.X.Absolute().Multiply(hw) + abp.Y.Absolute().Multiply(hh); |
| 243 | return (s0 <= s1); |
270 | 244 | } |
271 | 245 | |
272 | 246 | /** |
… |
… |
static bool SquareSAT(const CFixedVector2D& a, const CFixedVector2D& axis, const
|
279 | 253 | fixed hh = halfSize.Y; |
280 | 254 | |
281 | 255 | CFixedVector2D p = axis.Perpendicular(); |
282 | | if (p.Dot((u.Multiply(hw) + v.Multiply(hh)) - a) <= fixed::Zero()) |
283 | | return true; |
284 | | if (p.Dot((u.Multiply(hw) - v.Multiply(hh)) - a) <= fixed::Zero()) |
| 256 | fixed s0 = p.Dot(a); |
| 257 | if (s0 >= fixed::Zero()) |
285 | 258 | return true; |
286 | | if (p.Dot((-u.Multiply(hw) - v.Multiply(hh)) - a) <= fixed::Zero()) |
287 | | return true; |
288 | | if (p.Dot((-u.Multiply(hw) + v.Multiply(hh)) - a) <= fixed::Zero()) |
289 | | return true; |
290 | | |
291 | | return false; |
| 259 | s0 += u.Dot(p).Absolute().Multiply(hw) + v.Dot(p).Absolute().Multiply(hh); |
| 260 | return (s0 >= fixed::Zero()); |
292 | 261 | } |
293 | 262 | |
294 | 263 | bool Geometry::TestSquareSquare( |