diff --git a/source/maths/FixedVector2D.h b/source/maths/FixedVector2D.h
index d2728f4..37fd1fd 100644
a
|
b
|
public:
|
212 | 212 | return ret; |
213 | 213 | } |
214 | 214 | |
| 215 | // Is u.Dot(v.Perpendicular()) > 0 ? Avoid overflows, shifts, etc. |
| 216 | bool CrossGTZero(const CFixedVector2D& v) const |
| 217 | { |
| 218 | return (FIXED_MUL_I64_I32_I32(X.GetInternalValue(), v.Y.GetInternalValue()) > FIXED_MUL_I64_I32_I32(Y.GetInternalValue(), v.X.GetInternalValue())); |
| 219 | } |
| 220 | |
215 | 221 | CFixedVector2D Perpendicular() const |
216 | 222 | { |
217 | 223 | return CFixedVector2D(Y, -X); |
diff --git a/source/simulation2/components/CCmpPathfinder_Vertex.cpp b/source/simulation2/components/CCmpPathfinder_Vertex.cpp
index 5ab11b6..5af5ce4 100644
a
|
b
|
static const entity_pos_t EDGE_EXPAND_DELTA = entity_pos_t::FromInt(1)/16;
|
135 | 135 | */ |
136 | 136 | inline static bool CheckVisibility(const CFixedVector2D& a, const CFixedVector2D& b, const std::vector<Edge>& edges) |
137 | 137 | { |
138 | | CFixedVector2D abn = (b - a).Perpendicular(); |
| 138 | CFixedVector2D ab = b - a; |
139 | 139 | |
140 | 140 | // Edges of general non-axis-aligned shapes |
141 | 141 | for (size_t i = 0; i < edges.size(); ++i) |
… |
… |
inline static bool CheckVisibility(const CFixedVector2D& a, const CFixedVector2D
|
143 | 143 | CFixedVector2D p0 = edges[i].p0; |
144 | 144 | CFixedVector2D p1 = edges[i].p1; |
145 | 145 | |
146 | | CFixedVector2D d = (p1 - p0).Perpendicular(); |
| 146 | CFixedVector2D d = p1 - p0; |
147 | 147 | |
148 | 148 | // If 'a' is behind the edge, we can't cross |
149 | | fixed q = (a - p0).Dot(d); |
150 | | if (q < fixed::Zero()) |
| 149 | if ((p0 - a).CrossGTZero(d)) |
151 | 150 | continue; |
152 | 151 | |
153 | 152 | // If 'b' is in front of the edge, we can't cross |
154 | | fixed r = (b - p0).Dot(d); |
155 | | if (r > fixed::Zero()) |
| 153 | if ((b - p0).CrossGTZero(d)) |
156 | 154 | continue; |
157 | 155 | |
158 | 156 | // The ray is crossing the infinitely-extended edge from in front to behind. |
159 | 157 | // Check the finite edge is crossing the infinitely-extended ray too. |
160 | 158 | // (Given the previous tests, it can only be crossing in one direction.) |
161 | | fixed s = (p0 - a).Dot(abn); |
162 | | if (s > fixed::Zero()) |
| 159 | if ((p0 - a).CrossGTZero(ab)) |
163 | 160 | continue; |
164 | 161 | |
165 | | fixed t = (p1 - a).Dot(abn); |
166 | | if (t < fixed::Zero()) |
| 162 | if ((a - p1).CrossGTZero(ab)) |
167 | 163 | continue; |
168 | 164 | |
169 | 165 | return false; |
… |
… |
inline static bool CheckVisibilityLeft(const CFixedVector2D& a, const CFixedVect
|
182 | 178 | if (a.X >= b.X) |
183 | 179 | return true; |
184 | 180 | |
185 | | CFixedVector2D abn = (b - a).Perpendicular(); |
| 181 | CFixedVector2D ab = b - a; |
186 | 182 | |
187 | 183 | for (size_t i = 0; i < edges.size(); ++i) |
188 | 184 | { |
… |
… |
inline static bool CheckVisibilityLeft(const CFixedVector2D& a, const CFixedVect
|
190 | 186 | continue; |
191 | 187 | |
192 | 188 | CFixedVector2D p0 (edges[i].p0.X, edges[i].c1); |
193 | | fixed s = (p0 - a).Dot(abn); |
194 | | if (s > fixed::Zero()) |
| 189 | if ((p0 - a).CrossGTZero(ab)) |
195 | 190 | continue; |
196 | 191 | |
197 | 192 | CFixedVector2D p1 (edges[i].p0.X, edges[i].p0.Y); |
198 | | fixed t = (p1 - a).Dot(abn); |
199 | | if (t < fixed::Zero()) |
| 193 | if ((a - p1).CrossGTZero(ab)) |
200 | 194 | continue; |
201 | 195 | |
202 | 196 | return false; |
… |
… |
inline static bool CheckVisibilityRight(const CFixedVector2D& a, const CFixedVec
|
210 | 204 | if (a.X <= b.X) |
211 | 205 | return true; |
212 | 206 | |
213 | | CFixedVector2D abn = (b - a).Perpendicular(); |
| 207 | CFixedVector2D ab = b - a; |
214 | 208 | |
215 | 209 | for (size_t i = 0; i < edges.size(); ++i) |
216 | 210 | { |
… |
… |
inline static bool CheckVisibilityRight(const CFixedVector2D& a, const CFixedVec
|
218 | 212 | continue; |
219 | 213 | |
220 | 214 | CFixedVector2D p0 (edges[i].p0.X, edges[i].c1); |
221 | | fixed s = (p0 - a).Dot(abn); |
222 | | if (s > fixed::Zero()) |
| 215 | if ((p0 - a).CrossGTZero(ab)) |
223 | 216 | continue; |
224 | 217 | |
225 | 218 | CFixedVector2D p1 (edges[i].p0.X, edges[i].p0.Y); |
226 | | fixed t = (p1 - a).Dot(abn); |
227 | | if (t < fixed::Zero()) |
| 219 | if ((a - p1).CrossGTZero(ab)) |
228 | 220 | continue; |
229 | 221 | |
230 | 222 | return false; |
… |
… |
inline static bool CheckVisibilityBottom(const CFixedVector2D& a, const CFixedVe
|
238 | 230 | if (a.Y >= b.Y) |
239 | 231 | return true; |
240 | 232 | |
241 | | CFixedVector2D abn = (b - a).Perpendicular(); |
| 233 | CFixedVector2D ab = b - a; |
242 | 234 | |
243 | 235 | for (size_t i = 0; i < edges.size(); ++i) |
244 | 236 | { |
… |
… |
inline static bool CheckVisibilityBottom(const CFixedVector2D& a, const CFixedVe
|
246 | 238 | continue; |
247 | 239 | |
248 | 240 | CFixedVector2D p0 (edges[i].p0.X, edges[i].p0.Y); |
249 | | fixed s = (p0 - a).Dot(abn); |
250 | | if (s > fixed::Zero()) |
| 241 | if ((p0 - a).CrossGTZero(ab)) |
251 | 242 | continue; |
252 | 243 | |
253 | 244 | CFixedVector2D p1 (edges[i].c1, edges[i].p0.Y); |
254 | | fixed t = (p1 - a).Dot(abn); |
255 | | if (t < fixed::Zero()) |
| 245 | if ((a - p1).CrossGTZero(ab)) |
256 | 246 | continue; |
257 | 247 | |
258 | 248 | return false; |
… |
… |
inline static bool CheckVisibilityTop(const CFixedVector2D& a, const CFixedVecto
|
266 | 256 | if (a.Y <= b.Y) |
267 | 257 | return true; |
268 | 258 | |
269 | | CFixedVector2D abn = (b - a).Perpendicular(); |
| 259 | CFixedVector2D ab = b - a; |
270 | 260 | |
271 | 261 | for (size_t i = 0; i < edges.size(); ++i) |
272 | 262 | { |
… |
… |
inline static bool CheckVisibilityTop(const CFixedVector2D& a, const CFixedVecto
|
274 | 264 | continue; |
275 | 265 | |
276 | 266 | CFixedVector2D p0 (edges[i].p0.X, edges[i].p0.Y); |
277 | | fixed s = (p0 - a).Dot(abn); |
278 | | if (s > fixed::Zero()) |
| 267 | if ((p0 - a).CrossGTZero(ab)) |
279 | 268 | continue; |
280 | 269 | |
281 | 270 | CFixedVector2D p1 (edges[i].c1, edges[i].p0.Y); |
282 | | fixed t = (p1 - a).Dot(abn); |
283 | | if (t < fixed::Zero()) |
| 271 | if ((a - p1).CrossGTZero(ab)) |
284 | 272 | continue; |
285 | 273 | |
286 | 274 | return false; |
… |
… |
static void AddTerrainEdges(std::vector<Edge>& edges, std::vector<Vertex>& verte
|
360 | 348 | } |
361 | 349 | } |
362 | 350 | } |
363 | | |
364 | | // XXX rewrite this stuff |
365 | | |
366 | 351 | for (int j = j0; j < j1; ++j) |
367 | 352 | { |
368 | | std::vector<u16> segmentsR; |
369 | | std::vector<u16> segmentsL; |
| 353 | bool leftEdge = false, rightEdge = false; |
| 354 | int iStartL = 0, iStartR = 0; |
370 | 355 | |
371 | 356 | for (int i = i0; i <= i1; ++i) |
372 | 357 | { |
373 | 358 | bool a = IS_PASSABLE(grid.get(i, j+1), passClass); |
374 | 359 | bool b = IS_PASSABLE(grid.get(i, j), passClass); |
375 | | if (a && !b) |
376 | | segmentsL.push_back(i); |
377 | | if (b && !a) |
378 | | segmentsR.push_back(i); |
379 | | } |
380 | 360 | |
381 | | if (!segmentsR.empty()) |
382 | | { |
383 | | segmentsR.push_back(0); // sentinel value to simplify the loop |
384 | | u16 ia = segmentsR[0]; |
385 | | u16 ib = ia + 1; |
386 | | for (size_t n = 1; n < segmentsR.size(); ++n) |
| 361 | if ((b || !a) && leftEdge) // left edge ends here |
387 | 362 | { |
388 | | if (segmentsR[n] == ib) |
389 | | ++ib; |
390 | | else |
391 | | { |
392 | | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(ia), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
393 | | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(ib), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
394 | | edges.emplace_back(Edge{ v0, v1 }); |
395 | | |
396 | | ia = segmentsR[n]; |
397 | | ib = ia + 1; |
398 | | } |
| 363 | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 364 | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(iStartL), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 365 | edges.emplace_back(Edge{ v0, v1 }); |
| 366 | leftEdge = false; |
399 | 367 | } |
400 | | } |
401 | | |
402 | | if (!segmentsL.empty()) |
403 | | { |
404 | | segmentsL.push_back(0); // sentinel value to simplify the loop |
405 | | u16 ia = segmentsL[0]; |
406 | | u16 ib = ia + 1; |
407 | | for (size_t n = 1; n < segmentsL.size(); ++n) |
| 368 | else if (a && !b && !leftEdge) // left edge begins here |
408 | 369 | { |
409 | | if (segmentsL[n] == ib) |
410 | | ++ib; |
411 | | else |
412 | | { |
413 | | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(ib), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
414 | | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(ia), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
415 | | edges.emplace_back(Edge{ v0, v1 }); |
| 370 | iStartL = i; |
| 371 | leftEdge = true; |
| 372 | } |
416 | 373 | |
417 | | ia = segmentsL[n]; |
418 | | ib = ia + 1; |
419 | | } |
| 374 | if ((a || !b) && rightEdge) // right edge ends here |
| 375 | { |
| 376 | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(iStartR), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 377 | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(i), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 378 | edges.emplace_back(Edge{ v0, v1 }); |
| 379 | rightEdge = false; |
| 380 | } |
| 381 | else if (b && !a && !rightEdge) // right edge begins here |
| 382 | { |
| 383 | iStartR = i; |
| 384 | rightEdge = true; |
420 | 385 | } |
421 | 386 | } |
| 387 | if (leftEdge) // end remaining edges |
| 388 | { |
| 389 | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i1+1), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 390 | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(iStartL), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 391 | edges.emplace_back(Edge{ v0, v1 }); |
| 392 | } |
| 393 | if (rightEdge) |
| 394 | { |
| 395 | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(iStartR), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 396 | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(i1+1), fixed::FromInt(j+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 397 | edges.emplace_back(Edge{ v0, v1 }); |
| 398 | } |
422 | 399 | } |
423 | | |
424 | 400 | for (int i = i0; i < i1; ++i) |
425 | 401 | { |
426 | | std::vector<u16> segmentsU; |
427 | | std::vector<u16> segmentsD; |
| 402 | bool topEdge = false, bottomEdge = false; |
| 403 | int jStartT = 0, jStartB = 0; |
428 | 404 | |
429 | 405 | for (int j = j0; j <= j1; ++j) |
430 | 406 | { |
431 | 407 | bool a = IS_PASSABLE(grid.get(i+1, j), passClass); |
432 | 408 | bool b = IS_PASSABLE(grid.get(i, j), passClass); |
433 | | if (a && !b) |
434 | | segmentsU.push_back(j); |
435 | | if (b && !a) |
436 | | segmentsD.push_back(j); |
437 | | } |
438 | 409 | |
439 | | if (!segmentsU.empty()) |
440 | | { |
441 | | segmentsU.push_back(0); // sentinel value to simplify the loop |
442 | | u16 ja = segmentsU[0]; |
443 | | u16 jb = ja + 1; |
444 | | for (size_t n = 1; n < segmentsU.size(); ++n) |
| 410 | if ((b || !a) && bottomEdge) // bottom edge ends here |
445 | 411 | { |
446 | | if (segmentsU[n] == jb) |
447 | | ++jb; |
448 | | else |
449 | | { |
450 | | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(ja)).Multiply(Pathfinding::NAVCELL_SIZE); |
451 | | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(jb)).Multiply(Pathfinding::NAVCELL_SIZE); |
452 | | edges.emplace_back(Edge{ v0, v1 }); |
453 | | |
454 | | ja = segmentsU[n]; |
455 | | jb = ja + 1; |
456 | | } |
| 412 | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(j)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 413 | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(jStartB)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 414 | edges.emplace_back(Edge{ v0, v1 }); |
| 415 | bottomEdge = false; |
457 | 416 | } |
458 | | } |
459 | | |
460 | | if (!segmentsD.empty()) |
461 | | { |
462 | | segmentsD.push_back(0); // sentinel value to simplify the loop |
463 | | u16 ja = segmentsD[0]; |
464 | | u16 jb = ja + 1; |
465 | | for (size_t n = 1; n < segmentsD.size(); ++n) |
| 417 | else if (a && !b && !bottomEdge) // bottom edge begins here |
466 | 418 | { |
467 | | if (segmentsD[n] == jb) |
468 | | ++jb; |
469 | | else |
470 | | { |
471 | | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(jb)).Multiply(Pathfinding::NAVCELL_SIZE); |
472 | | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(ja)).Multiply(Pathfinding::NAVCELL_SIZE); |
473 | | edges.emplace_back(Edge{ v0, v1 }); |
| 419 | jStartB = i; |
| 420 | bottomEdge = true; |
| 421 | } |
474 | 422 | |
475 | | ja = segmentsD[n]; |
476 | | jb = ja + 1; |
477 | | } |
| 423 | if ((a || !b) && topEdge) // top edge ends here |
| 424 | { |
| 425 | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(jStartT)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 426 | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(j)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 427 | edges.emplace_back(Edge{ v0, v1 }); |
| 428 | topEdge = false; |
| 429 | } |
| 430 | else if (b && !a && !topEdge) // top edge begins here |
| 431 | { |
| 432 | jStartT = i; |
| 433 | topEdge = true; |
478 | 434 | } |
479 | 435 | } |
| 436 | if (bottomEdge) // end remaining edges |
| 437 | { |
| 438 | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(j1+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 439 | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(jStartB)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 440 | edges.emplace_back(Edge{ v0, v1 }); |
| 441 | } |
| 442 | if (topEdge) |
| 443 | { |
| 444 | CFixedVector2D v0 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(jStartT)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 445 | CFixedVector2D v1 = CFixedVector2D(fixed::FromInt(i+1), fixed::FromInt(j1+1)).Multiply(Pathfinding::NAVCELL_SIZE); |
| 446 | edges.emplace_back(Edge{ v0, v1 }); |
| 447 | } |
480 | 448 | } |
481 | 449 | } |
482 | 450 | |