diff --git a/source/simulation2/components/CCmpPathfinder_Vertex.cpp b/source/simulation2/components/CCmpPathfinder_Vertex.cpp
index 6ed042c..e9063da 100644
a
|
b
|
inline static bool CheckVisibility(const CFixedVector2D& a, const CFixedVector2D
|
138 | 138 | CFixedVector2D abn = (b - a).Perpendicular(); |
139 | 139 | |
140 | 140 | // Edges of general non-axis-aligned shapes |
141 | | for (size_t i = 0; i < edges.size(); ++i) |
| 141 | for (const Edge& edge : edges) |
142 | 142 | { |
143 | | CFixedVector2D p0 = edges[i].p0; |
144 | | CFixedVector2D p1 = edges[i].p1; |
145 | | |
146 | | CFixedVector2D d = (p1 - p0).Perpendicular(); |
| 143 | CFixedVector2D d = (edge.p1 - edge.p0).Perpendicular(); |
147 | 144 | |
148 | 145 | // If 'a' is behind the edge, we can't cross |
149 | | fixed q = (a - p0).Dot(d); |
| 146 | fixed q = (a - edge.p0).Dot(d); |
150 | 147 | if (q < fixed::Zero()) |
151 | 148 | continue; |
152 | 149 | |
153 | 150 | // If 'b' is in front of the edge, we can't cross |
154 | | fixed r = (b - p0).Dot(d); |
| 151 | fixed r = (b - edge.p0).Dot(d); |
155 | 152 | if (r > fixed::Zero()) |
156 | 153 | continue; |
157 | 154 | |
158 | 155 | // The ray is crossing the infinitely-extended edge from in front to behind. |
159 | 156 | // Check the finite edge is crossing the infinitely-extended ray too. |
160 | 157 | // (Given the previous tests, it can only be crossing in one direction.) |
161 | | fixed s = (p0 - a).Dot(abn); |
| 158 | fixed s = (edge.p0 - a).Dot(abn); |
162 | 159 | if (s > fixed::Zero()) |
163 | 160 | continue; |
164 | 161 | |
165 | | fixed t = (p1 - a).Dot(abn); |
| 162 | fixed t = (edge.p1 - a).Dot(abn); |
166 | 163 | if (t < fixed::Zero()) |
167 | 164 | continue; |
168 | 165 | |
… |
… |
inline static bool CheckVisibilityLeft(const CFixedVector2D& a, const CFixedVect
|
184 | 181 | |
185 | 182 | CFixedVector2D abn = (b - a).Perpendicular(); |
186 | 183 | |
187 | | for (size_t i = 0; i < edges.size(); ++i) |
| 184 | for (const EdgeAA& edge : edges) |
188 | 185 | { |
189 | | if (b.X < edges[i].p0.X) |
| 186 | if (b.X < edge.p0.X) |
190 | 187 | continue; |
191 | 188 | |
192 | | CFixedVector2D p0 (edges[i].p0.X, edges[i].c1); |
193 | | fixed s = (p0 - a).Dot(abn); |
| 189 | CFixedVector2D p1 (edge.p0.X, edge.c1); |
| 190 | fixed s = (p1 - a).Dot(abn); |
194 | 191 | if (s > fixed::Zero()) |
195 | 192 | continue; |
196 | 193 | |
197 | | CFixedVector2D p1 (edges[i].p0.X, edges[i].p0.Y); |
198 | | fixed t = (p1 - a).Dot(abn); |
| 194 | fixed t = (edge.p0 - a).Dot(abn); |
199 | 195 | if (t < fixed::Zero()) |
200 | 196 | continue; |
201 | 197 | |
… |
… |
inline static bool CheckVisibilityRight(const CFixedVector2D& a, const CFixedVec
|
212 | 208 | |
213 | 209 | CFixedVector2D abn = (b - a).Perpendicular(); |
214 | 210 | |
215 | | for (size_t i = 0; i < edges.size(); ++i) |
| 211 | for (const EdgeAA& edge : edges) |
216 | 212 | { |
217 | | if (b.X > edges[i].p0.X) |
| 213 | if (b.X > edge.p0.X) |
218 | 214 | continue; |
219 | 215 | |
220 | | CFixedVector2D p0 (edges[i].p0.X, edges[i].c1); |
221 | | fixed s = (p0 - a).Dot(abn); |
| 216 | CFixedVector2D p1 (edge.p0.X, edge.c1); |
| 217 | fixed s = (p1 - a).Dot(abn); |
222 | 218 | if (s > fixed::Zero()) |
223 | 219 | continue; |
224 | 220 | |
225 | | CFixedVector2D p1 (edges[i].p0.X, edges[i].p0.Y); |
226 | | fixed t = (p1 - a).Dot(abn); |
| 221 | fixed t = (edge.p0 - a).Dot(abn); |
227 | 222 | if (t < fixed::Zero()) |
228 | 223 | continue; |
229 | 224 | |
… |
… |
inline static bool CheckVisibilityBottom(const CFixedVector2D& a, const CFixedVe
|
240 | 235 | |
241 | 236 | CFixedVector2D abn = (b - a).Perpendicular(); |
242 | 237 | |
243 | | for (size_t i = 0; i < edges.size(); ++i) |
| 238 | for (const EdgeAA& edge : edges) |
244 | 239 | { |
245 | | if (b.Y < edges[i].p0.Y) |
| 240 | if (b.Y < edge.p0.Y) |
246 | 241 | continue; |
247 | 242 | |
248 | | CFixedVector2D p0 (edges[i].p0.X, edges[i].p0.Y); |
249 | | fixed s = (p0 - a).Dot(abn); |
| 243 | fixed s = (edge.p0 - a).Dot(abn); |
250 | 244 | if (s > fixed::Zero()) |
251 | 245 | continue; |
252 | 246 | |
253 | | CFixedVector2D p1 (edges[i].c1, edges[i].p0.Y); |
| 247 | CFixedVector2D p1 (edge.c1, edge.p0.Y); |
254 | 248 | fixed t = (p1 - a).Dot(abn); |
255 | 249 | if (t < fixed::Zero()) |
256 | 250 | continue; |
… |
… |
inline static bool CheckVisibilityTop(const CFixedVector2D& a, const CFixedVecto
|
268 | 262 | |
269 | 263 | CFixedVector2D abn = (b - a).Perpendicular(); |
270 | 264 | |
271 | | for (size_t i = 0; i < edges.size(); ++i) |
| 265 | for (const EdgeAA& edge : edges) |
272 | 266 | { |
273 | | if (b.Y > edges[i].p0.Y) |
| 267 | if (b.Y > edge.p0.Y) |
274 | 268 | continue; |
275 | 269 | |
276 | | CFixedVector2D p0 (edges[i].p0.X, edges[i].p0.Y); |
277 | | fixed s = (p0 - a).Dot(abn); |
| 270 | fixed s = (edge.p0 - a).Dot(abn); |
278 | 271 | if (s > fixed::Zero()) |
279 | 272 | continue; |
280 | 273 | |
281 | | CFixedVector2D p1 (edges[i].c1, edges[i].p0.Y); |
| 274 | CFixedVector2D p1 (edge.c1, edge.p0.Y); |
282 | 275 | fixed t = (p1 - a).Dot(abn); |
283 | 276 | if (t < fixed::Zero()) |
284 | 277 | continue; |
… |
… |
inline static bool CheckVisibilityTop(const CFixedVector2D& a, const CFixedVecto
|
289 | 282 | return true; |
290 | 283 | } |
291 | 284 | |
| 285 | inline static bool CheckVisibilitySquares(const CFixedVector2D& start, const CFixedVector2D& end, const std::vector<EdgeAA>& Squares) |
| 286 | { |
| 287 | for (const EdgeAA& square : Squares) |
| 288 | { |
| 289 | CFixedVector2D a = start - square.p0; |
| 290 | CFixedVector2D b = end - square.p0; |
| 291 | fixed c = square.c1; |
| 292 | |
| 293 | if (-c <= a.X && a.X <= c && -c <= a.Y && a.Y <= c) |
| 294 | continue; // a is inside |
| 295 | |
| 296 | if ((a.X < -c && b.X < -c) || (a.X > c && b.X > c) || (a.Y < -c && b.Y < -c) || (a.Y > c && b.Y > c)) |
| 297 | continue; // ab is entirely above/below/side the square |
| 298 | |
| 299 | CFixedVector2D abp = (b - a).Perpendicular(); |
| 300 | fixed s0 = a.Dot(abp).Absolute(); |
| 301 | fixed s1 = (abp.X.Absolute() + abp.Y.Absolute()).Multiply(c); |
| 302 | if (s0 > s1) |
| 303 | continue; |
| 304 | |
| 305 | return false; |
| 306 | } |
| 307 | return true; |
| 308 | } |
| 309 | |
292 | 310 | typedef PriorityQueueHeap<u16, fixed, fixed> VertexPriorityQueue; |
293 | 311 | |
294 | 312 | /** |
… |
… |
struct SquareSort
|
548 | 566 | { |
549 | 567 | CFixedVector2D src; |
550 | 568 | SquareSort(CFixedVector2D src) : src(src) { } |
551 | | bool operator()(const Square& a, const Square& b) |
| 569 | bool operator()(const EdgeAA& a, const EdgeAA& b) |
552 | 570 | { |
553 | 571 | if ((a.p0 - src).CompareLength(b.p0 - src) < 0) |
554 | 572 | return true; |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
595 | 613 | // List of collision edges - paths must never cross these. |
596 | 614 | // (Edges are one-sided so intersections are fine in one direction, but not the other direction.) |
597 | 615 | std::vector<Edge> edges; |
598 | | std::vector<Square> edgeSquares; // axis-aligned squares; equivalent to 4 edges |
| 616 | std::vector<Square> edgeSquares; // axis-aligned rectangles; equivalent to 4 edges |
| 617 | std::vector<EdgeAA> aaSquares; // axis-aligned squares, p0 = center, c1 = hw = hh |
599 | 618 | |
600 | 619 | // Create impassable edges at the max-range boundary, so we can't escape the region |
601 | 620 | // where we're meant to be searching |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
643 | 662 | |
644 | 663 | // Change array capacities to reduce reallocations |
645 | 664 | vertexes.reserve(vertexes.size() + squares.size()*4); |
646 | | edgeSquares.reserve(edgeSquares.size() + squares.size()); // (assume most squares are AA) |
| 665 | edgeSquares.reserve(edgeSquares.size() + staticShapesNb); // (assume most squares are AA) |
| 666 | aaSquares.reserve(squares.size() - staticShapesNb); |
647 | 667 | |
648 | 668 | entity_pos_t pathfindClearance = clearance; |
649 | 669 | |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
685 | 705 | CFixedVector2D ev2(center.X + h0.Dot(u), center.Y - h0.Dot(v)); |
686 | 706 | CFixedVector2D ev3(center.X + h1.Dot(u), center.Y - h1.Dot(v)); |
687 | 707 | if (aa) |
688 | | edgeSquares.emplace_back(Square{ ev1, ev3 }); |
| 708 | if (h0.X == h0.Y) |
| 709 | aaSquares.emplace_back(EdgeAA{ center, h0.X }); |
| 710 | else |
| 711 | edgeSquares.emplace_back(Square{ ev1, ev3 }); |
689 | 712 | else |
690 | 713 | { |
691 | 714 | edges.emplace_back(Edge{ ev0, ev1 }); |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
698 | 721 | // to reduce the search space |
699 | 722 | } |
700 | 723 | |
701 | | // Clip out vertices that are inside an edgeSquare (i.e. trivially unreachable) |
702 | | for (size_t i = 0; i < edgeSquares.size(); ++i) |
| 724 | // Clip out vertices that are inside an aaSquare (i.e. trivially unreachable) |
| 725 | for (const EdgeAA& sq : aaSquares) |
703 | 726 | { |
704 | 727 | // If the start point is inside the square, ignore it |
705 | | if (start.p.X >= edgeSquares[i].p0.X && |
706 | | start.p.Y >= edgeSquares[i].p0.Y && |
707 | | start.p.X <= edgeSquares[i].p1.X && |
708 | | start.p.Y <= edgeSquares[i].p1.Y) |
| 728 | if ((start.p.X - sq.p0.X).Absolute() <= sq.c1 && |
| 729 | (start.p.Y - sq.p0.Y).Absolute() <= sq.c1) |
709 | 730 | continue; |
710 | 731 | |
711 | 732 | // Remove every non-start/goal vertex that is inside an edgeSquare; |
712 | 733 | // since remove() would be inefficient, just mark it as closed instead. |
713 | 734 | for (size_t j = 2; j < vertexes.size(); ++j) |
714 | | if (vertexes[j].p.X >= edgeSquares[i].p0.X && |
715 | | vertexes[j].p.Y >= edgeSquares[i].p0.Y && |
716 | | vertexes[j].p.X <= edgeSquares[i].p1.X && |
717 | | vertexes[j].p.Y <= edgeSquares[i].p1.Y) |
| 735 | if ((vertexes[j].p.X - sq.p0.X).Absolute() <= sq.c1 && |
| 736 | (vertexes[j].p.Y - sq.p0.Y).Absolute() <= sq.c1) |
718 | 737 | vertexes[j].status = Vertex::CLOSED; |
719 | 738 | } |
720 | 739 | |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
725 | 744 | { |
726 | 745 | #define PUSH_POINT(p) STMT(xz.push_back(p.X.ToFloat()); xz.push_back(p.Y.ToFloat())) |
727 | 746 | // Render the vertexes as little Pac-Man shapes to indicate quadrant direction |
728 | | for (size_t i = 0; i < vertexes.size(); ++i) |
| 747 | for (const Vertex& vtx : vertexes) |
729 | 748 | { |
730 | 749 | m_DebugOverlayShortPathLines.emplace_back(); |
731 | 750 | m_DebugOverlayShortPathLines.back().m_Color = CColor(1, 1, 0, 1); |
732 | 751 | |
733 | | float x = vertexes[i].p.X.ToFloat(); |
734 | | float z = vertexes[i].p.Y.ToFloat(); |
| 752 | float x = vtx.p.X.ToFloat(); |
| 753 | float z = vtx.p.Y.ToFloat(); |
735 | 754 | |
736 | 755 | float a0 = 0, a1 = 0; |
737 | 756 | // Get arc start/end angles depending on quadrant (if any) |
738 | | if (vertexes[i].quadInward == QUADRANT_BL) { a0 = -0.25f; a1 = 0.50f; } |
739 | | else if (vertexes[i].quadInward == QUADRANT_TR) { a0 = 0.25f; a1 = 1.00f; } |
740 | | else if (vertexes[i].quadInward == QUADRANT_TL) { a0 = -0.50f; a1 = 0.25f; } |
741 | | else if (vertexes[i].quadInward == QUADRANT_BR) { a0 = 0.00f; a1 = 0.75f; } |
| 757 | if (vtx.quadInward == QUADRANT_BL) { a0 = -0.25f; a1 = 0.50f; } |
| 758 | else if (vtx.quadInward == QUADRANT_TR) { a0 = 0.25f; a1 = 1.00f; } |
| 759 | else if (vtx.quadInward == QUADRANT_TL) { a0 = -0.50f; a1 = 0.25f; } |
| 760 | else if (vtx.quadInward == QUADRANT_BR) { a0 = 0.00f; a1 = 0.75f; } |
742 | 761 | |
743 | 762 | if (a0 == a1) |
744 | 763 | SimRender::ConstructCircleOnGround(GetSimContext(), x, z, 0.5f, |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
750 | 769 | } |
751 | 770 | |
752 | 771 | // Render the edges |
753 | | for (size_t i = 0; i < edges.size(); ++i) |
| 772 | for (const Edge& edg : edges) |
754 | 773 | { |
755 | 774 | m_DebugOverlayShortPathLines.emplace_back(); |
756 | 775 | m_DebugOverlayShortPathLines.back().m_Color = CColor(0, 1, 1, 1); |
757 | 776 | std::vector<float> xz; |
758 | | PUSH_POINT(edges[i].p0); |
759 | | PUSH_POINT(edges[i].p1); |
| 777 | PUSH_POINT(edg.p0); |
| 778 | PUSH_POINT(edg.p1); |
760 | 779 | |
761 | 780 | // Add an arrowhead to indicate the direction |
762 | | CFixedVector2D d = edges[i].p1 - edges[i].p0; |
| 781 | CFixedVector2D d = edg.p1 - edg.p0; |
763 | 782 | d.Normalize(fixed::FromInt(1)/8); |
764 | | CFixedVector2D p2 = edges[i].p1 - d*2; |
| 783 | CFixedVector2D p2 = edg.p1 - d*2; |
765 | 784 | CFixedVector2D p3 = p2 + d.Perpendicular(); |
766 | 785 | CFixedVector2D p4 = p2 - d.Perpendicular(); |
767 | 786 | PUSH_POINT(p3); |
768 | 787 | PUSH_POINT(p4); |
769 | | PUSH_POINT(edges[i].p1); |
| 788 | PUSH_POINT(edg.p1); |
770 | 789 | |
771 | 790 | SimRender::ConstructLineOnGround(GetSimContext(), xz, m_DebugOverlayShortPathLines.back(), true); |
772 | 791 | } |
773 | 792 | #undef PUSH_POINT |
774 | 793 | |
775 | | // Render the axis-aligned squares |
776 | | for (size_t i = 0; i < edgeSquares.size(); ++i) |
| 794 | // Render the axis-aligned rectangles |
| 795 | for (const Square& s : edgeSquares) |
777 | 796 | { |
778 | 797 | m_DebugOverlayShortPathLines.push_back(SOverlayLine()); |
779 | 798 | m_DebugOverlayShortPathLines.back().m_Color = CColor(0, 1, 1, 1); |
780 | 799 | std::vector<float> xz; |
781 | | Square s = edgeSquares[i]; |
782 | 800 | xz.push_back(s.p0.X.ToFloat()); |
783 | 801 | xz.push_back(s.p0.Y.ToFloat()); |
784 | 802 | xz.push_back(s.p0.X.ToFloat()); |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
791 | 809 | xz.push_back(s.p0.Y.ToFloat()); |
792 | 810 | SimRender::ConstructLineOnGround(GetSimContext(), xz, m_DebugOverlayShortPathLines.back(), true); |
793 | 811 | } |
| 812 | // Render the axis-aligned squares |
| 813 | for (const EdgeAA& sq : aaSquares) |
| 814 | { |
| 815 | m_DebugOverlayShortPathLines.push_back(SOverlayLine()); |
| 816 | m_DebugOverlayShortPathLines.back().m_Color = CColor(0, 1, 1, 1); |
| 817 | std::vector<float> xz; |
| 818 | CFixedVector2D sqp0 = sq.p0 - CFixedVector2D(sq.c1, sq.c1); |
| 819 | CFixedVector2D sqp1 = sq.p0 + CFixedVector2D(sq.c1, sq.c1); |
| 820 | xz.push_back(sqp0.X.ToFloat()); |
| 821 | xz.push_back(sqp0.Y.ToFloat()); |
| 822 | xz.push_back(sqp0.X.ToFloat()); |
| 823 | xz.push_back(sqp1.Y.ToFloat()); |
| 824 | xz.push_back(sqp1.X.ToFloat()); |
| 825 | xz.push_back(sqp1.Y.ToFloat()); |
| 826 | xz.push_back(sqp1.X.ToFloat()); |
| 827 | xz.push_back(sqp0.Y.ToFloat()); |
| 828 | xz.push_back(sqp0.X.ToFloat()); |
| 829 | xz.push_back(sqp0.Y.ToFloat()); |
| 830 | SimRender::ConstructLineOnGround(GetSimContext(), xz, m_DebugOverlayShortPathLines.back(), true); |
| 831 | } |
794 | 832 | } |
795 | 833 | |
796 | 834 | // Do an A* search over the vertex/visibility graph: |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
830 | 868 | // The heuristic based on distance is very rough, especially for squares that are further away; |
831 | 869 | // we're also only really interested in the closest squares since they are the only ones that block a lot of rays. |
832 | 870 | // Thus we only do a partial sort; the threshold is just a somewhat reasonable value. |
833 | | if (edgeSquares.size() > 8) |
834 | | std::partial_sort(edgeSquares.begin(), edgeSquares.begin() + 8, edgeSquares.end(), SquareSort(vertexes[curr.id].p)); |
| 871 | if (aaSquares.size() > 8) |
| 872 | std::partial_sort(aaSquares.begin(), aaSquares.begin() + 8, aaSquares.end(), SquareSort(vertexes[curr.id].p)); |
835 | 873 | |
836 | 874 | std::vector<Edge> edgesUnaligned; |
837 | 875 | std::vector<EdgeAA> edgesLeft; |
… |
… |
void CCmpPathfinder::ComputeShortPath(const IObstructionTestFilter& filter,
|
890 | 928 | } |
891 | 929 | |
892 | 930 | visible = visible && |
| 931 | CheckVisibilitySquares(vertexes[curr.id].p, npos, aaSquares) && |
893 | 932 | CheckVisibilityLeft(vertexes[curr.id].p, npos, edgesLeft) && |
894 | 933 | CheckVisibilityRight(vertexes[curr.id].p, npos, edgesRight) && |
895 | 934 | CheckVisibilityBottom(vertexes[curr.id].p, npos, edgesBottom) && |