Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
PathGenerator.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#include "Creature.h"
7#include "Log.h"
8#include "Map.h"
9#include "MMapFactory.h"
10#include "MMapManager.h"
11#include "PathGenerator.h"
12
13#include "DetourCommon.h"
14#include "DetourNavMeshQuery.h"
15
20 _endPosition(G3D::Vector3::zero()), _sourceUnit(owner), _navMesh(NULL),
21 _navMeshQuery(NULL)
22{
23 memset(_pathPolyRefs, 0, sizeof(_pathPolyRefs));
24 SF_LOG_DEBUG("maps", "++ PathGenerator::PathGenerator for %u \n", _sourceUnit->GetGUIDLow());
25
26 uint32 mapId = _sourceUnit->GetMapId();
28 {
30 _navMesh = mmap->GetNavMesh(mapId, _sourceUnit->GetTerrainSwaps());
31 _navMeshQuery = mmap->GetNavMeshQuery(mapId, _sourceUnit->GetInstanceId(), _sourceUnit->GetTerrainSwaps());
32 }
33
35}
36
38{
39 SF_LOG_DEBUG("maps", "++ PathGenerator::~PathGenerator() for %u \n", _sourceUnit->GetGUIDLow());
40}
41
42bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest)
43{
44 float x, y, z;
45 _sourceUnit->GetPosition(x, y, z);
46
47 if (!Skyfire::IsValidMapCoord(destX, destY, destZ) || !Skyfire::IsValidMapCoord(x, y, z))
48 return false;
49
50 G3D::Vector3 dest(destX, destY, destZ);
51 SetEndPosition(dest);
52
53 G3D::Vector3 start(x, y, z);
54 SetStartPosition(start);
55
56 _forceDestination = forceDest;
57
58 SF_LOG_DEBUG("maps", "++ PathGenerator::CalculatePath() for %u \n", _sourceUnit->GetGUIDLow());
59
60 // make sure navMesh works - we can run on map w/o mmap
61 // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
63 !HaveTile(start) || !HaveTile(dest))
64 {
67 return true;
68 }
69
71
72 BuildPolyPath(start, dest);
73 return true;
74}
75
76dtPolyRef PathGenerator::GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* point, float* distance) const
77{
78 if (!polyPath || !polyPathSize)
79 return INVALID_POLYREF;
80
81 dtPolyRef nearestPoly = INVALID_POLYREF;
82 float minDist2d = FLT_MAX;
83 float minDist3d = 0.0f;
84
85 for (uint32 i = 0; i < polyPathSize; ++i)
86 {
87 float closestPoint[VERTEX_SIZE];
88 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint)))
89 continue;
90
91 float d = dtVdist2DSqr(point, closestPoint);
92 if (d < minDist2d)
93 {
94 minDist2d = d;
95 nearestPoly = polyPath[i];
96 minDist3d = dtVdistSqr(point, closestPoint);
97 }
98
99 if (minDist2d < 1.0f) // shortcut out - close enough for us
100 break;
101 }
102
103 if (distance)
104 *distance = dtSqrt(minDist3d);
105
106 return (minDist2d < 3.0f) ? nearestPoly : INVALID_POLYREF;
107}
108
109dtPolyRef PathGenerator::GetPolyByLocation(float const* point, float* distance) const
110{
111 // first we check the current path
112 // if the current path doesn't contain the current poly,
113 // we need to use the expensive navMesh.findNearestPoly
114 dtPolyRef polyRef = GetPathPolyByPosition(_pathPolyRefs, _polyLength, point, distance);
115 if (polyRef != INVALID_POLYREF)
116 return polyRef;
117
118 // we don't have it in our old path
119 // try to get it by findNearestPoly()
120 // first try with low search box
121 float extents[VERTEX_SIZE] = { 3.0f, 5.0f, 3.0f }; // bounds of poly search area
122 float closestPoint[VERTEX_SIZE] = { 0.0f, 0.0f, 0.0f };
123 if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF)
124 {
125 *distance = dtVdist(closestPoint, point);
126 return polyRef;
127 }
128
129 // still nothing ..
130 // try with bigger search box
131 // Note that the extent should not overlap more than 128 polygons in the navmesh (see dtNavMeshQuery::findNearestPoly)
132 extents[1] = 50.0f;
133
134 if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF)
135 {
136 *distance = dtVdist(closestPoint, point);
137 return polyRef;
138 }
139
140 return INVALID_POLYREF;
141}
142
143void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos)
144{
145 // *** getting start/end poly logic ***
146
147 float distToStartPoly, distToEndPoly;
148 float startPoint[VERTEX_SIZE] = { startPos.y, startPos.z, startPos.x };
149 float endPoint[VERTEX_SIZE] = { endPos.y, endPos.z, endPos.x };
150
151 dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly);
152 dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly);
153
154 // we have a hole in our mesh
155 // make shortcut path and mark it as NOPATH ( with flying and swimming exception )
156 // its up to caller how he will use this info
157 if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF)
158 {
159 SF_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n");
161 bool path = _sourceUnit->GetTypeId() == TypeID::TYPEID_UNIT && _sourceUnit->ToCreature()->CanFly();
162
163 bool waterPath = _sourceUnit->GetTypeId() == TypeID::TYPEID_UNIT && _sourceUnit->ToCreature()->CanSwim();
164 if (waterPath)
165 {
166 // Check both start and end points, if they're both in water, then we can *safely* let the creature move
167 for (uint32 i = 0; i < _pathPoints.size(); ++i)
168 {
169 ZLiquidStatus status = _sourceUnit->GetBaseMap()->getLiquidStatus(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z, MAP_ALL_LIQUIDS, NULL);
170 // One of the points is not in the water, cancel movement.
171 if (status == LIQUID_MAP_NO_WATER)
172 {
173 waterPath = false;
174 break;
175 }
176 }
177 }
178
180 return;
181 }
182
183 // we may need a better number here
184 bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f);
185 if (farFromPoly)
186 {
187 SF_LOG_DEBUG("maps", "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly);
188
189 bool buildShotrcut = false;
190 if (_sourceUnit->GetTypeId() == TypeID::TYPEID_UNIT)
191 {
192 Creature* owner = (Creature*)_sourceUnit;
193
194 G3D::Vector3 const& p = (distToStartPoly > 7.0f) ? startPos : endPos;
195 if (_sourceUnit->GetBaseMap()->IsUnderWater(p.x, p.y, p.z))
196 {
197 SF_LOG_DEBUG("maps", "++ BuildPolyPath :: underWater case\n");
198 if (owner->CanSwim())
199 buildShotrcut = true;
200 }
201 else
202 {
203 SF_LOG_DEBUG("maps", "++ BuildPolyPath :: flying case\n");
204 if (owner->CanFly())
205 buildShotrcut = true;
206 }
207 }
208
209 if (buildShotrcut)
210 {
213 return;
214 }
215 else
216 {
217 float closestPoint[VERTEX_SIZE];
218 // we may want to use closestPointOnPolyBoundary instead
219 if (dtStatusSucceed(_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint)))
220 {
221 dtVcopy(endPoint, closestPoint);
222 SetActualEndPosition(G3D::Vector3(endPoint[2], endPoint[0], endPoint[1]));
223 }
224
226 }
227 }
228
229 // *** poly path generating logic ***
230 // start and end are on same polygon
231 // just need to move in straight line
232 if (startPoly == endPoly)
233 {
234 SF_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPoly == endPoly)\n");
235
237
238 _pathPolyRefs[0] = startPoly;
239 _polyLength = 1;
240
241 _type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL;
242 SF_LOG_DEBUG("maps", "++ BuildPolyPath :: path type %d\n", _type);
243 return;
244 }
245
246 // look for startPoly/endPoly in current path
248 bool startPolyFound = false;
249 bool endPolyFound = false;
250 uint32 pathStartIndex = 0;
251 uint32 pathEndIndex = 0;
252
253 if (_polyLength)
254 {
255 for (; pathStartIndex < _polyLength; ++pathStartIndex)
256 {
257 // here to carch few bugs
258 ASSERT(_pathPolyRefs[pathStartIndex] != INVALID_POLYREF);
259
260 if (_pathPolyRefs[pathStartIndex] == startPoly)
261 {
262 startPolyFound = true;
263 break;
264 }
265 }
266
267 for (pathEndIndex = _polyLength - 1; pathEndIndex > pathStartIndex; --pathEndIndex)
268 if (_pathPolyRefs[pathEndIndex] == endPoly)
269 {
270 endPolyFound = true;
271 break;
272 }
273 }
274
275 if (startPolyFound && endPolyFound)
276 {
277 SF_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n");
278
279 // we moved along the path and the target did not move out of our old poly-path
280 // our path is a simple subpath case, we have all the data we need
281 // just "cut" it out
282
283 _polyLength = pathEndIndex - pathStartIndex + 1;
284 memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _polyLength * sizeof(dtPolyRef));
285 }
286 else if (startPolyFound && !endPolyFound)
287 {
288 SF_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n");
289
290 // we are moving on the old path but target moved out
291 // so we have atleast part of poly-path ready
292
293 _polyLength -= pathStartIndex;
294
295 // try to adjust the suffix of the path instead of recalculating entire length
296 // at given interval the target cannot get too far from its last location
297 // thus we have less poly to cover
298 // sub-path of optimal path is optimal
299
300 // take ~80% of the original length
302 uint32 prefixPolyLength = uint32(_polyLength * 0.8f + 0.5f);
303 memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, prefixPolyLength * sizeof(dtPolyRef));
304
305 dtPolyRef suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];
306
307 // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
308 float suffixEndPoint[VERTEX_SIZE];
309 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)))
310 {
311 // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
312 // try to recover by using prev polyref
313 --prefixPolyLength;
314 suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1];
315 if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)))
316 {
317 // suffixStartPoly is still invalid, error state
320 return;
321 }
322 }
323
324 // generate suffix
325 uint32 suffixPolyLength = 0;
326 dtStatus dtResult = _navMeshQuery->findPath(
327 suffixStartPoly, // start polygon
328 endPoly, // end polygon
329 suffixEndPoint, // start position
330 endPoint, // end position
331 &_filter, // polygon search filter
332 _pathPolyRefs + prefixPolyLength - 1, // [out] path
333 (int*)&suffixPolyLength,
334 MAX_PATH_LENGTH - prefixPolyLength); // max number of polygons in output path
335
336 if (!suffixPolyLength || dtStatusFailed(dtResult))
337 {
338 // this is probably an error state, but we'll leave it
339 // and hopefully recover on the next Update
340 // we still need to copy our preffix
341 SF_LOG_ERROR("maps", "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow());
342 }
343
344 SF_LOG_DEBUG("maps", "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n", _polyLength, prefixPolyLength, suffixPolyLength);
345
346 // new path = prefix + suffix - overlap
347 _polyLength = prefixPolyLength + suffixPolyLength - 1;
348 }
349 else
350 {
351 SF_LOG_DEBUG("maps", "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n");
352
353 // either we have no path at all -> first run
354 // or something went really wrong -> we aren't moving along the path to the target
355 // just generate new path
356
357 // free and invalidate old path data
358 Clear();
359
360 dtStatus dtResult = _navMeshQuery->findPath(
361 startPoly, // start polygon
362 endPoly, // end polygon
363 startPoint, // start position
364 endPoint, // end position
365 &_filter, // polygon search filter
366 _pathPolyRefs, // [out] path
367 (int*)&_polyLength,
368 MAX_PATH_LENGTH); // max number of polygons in output path
369
370 if (!_polyLength || dtStatusFailed(dtResult))
371 {
372 // only happens if we passed bad data to findPath(), or navmesh is messed up
373 SF_LOG_ERROR("maps", "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow());
376 return;
377 }
378 }
379
380 // by now we know what type of path we can get
381 if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE))
383 else
385
386 // generate the point-path out of our up-to-date poly-path
387 BuildPointPath(startPoint, endPoint);
388}
389
390void PathGenerator::BuildPointPath(const float* startPoint, const float* endPoint)
391{
392 float pathPoints[MAX_POINT_PATH_LENGTH * VERTEX_SIZE];
393 uint32 pointCount = 0;
394 dtStatus dtResult = DT_FAILURE;
396 {
397 dtResult = _navMeshQuery->findStraightPath(
398 startPoint, // start position
399 endPoint, // end position
400 _pathPolyRefs, // current path
401 _polyLength, // lenth of current path
402 pathPoints, // [out] path corner points
403 NULL, // [out] flags
404 NULL, // [out] shortened path
405 (int*)&pointCount,
406 _pointPathLimit); // maximum number of points/polygons to use
407 }
408 else
409 {
410 dtResult = FindSmoothPath(
411 startPoint, // start position
412 endPoint, // end position
413 _pathPolyRefs, // current path
414 _polyLength, // length of current path
415 pathPoints, // [out] path corner points
416 (int*)&pointCount,
417 _pointPathLimit); // maximum number of points
418 }
419
420 if (pointCount < 2 || dtStatusFailed(dtResult))
421 {
422 // only happens if pass bad data to findStraightPath or navmesh is broken
423 // single point paths can be generated here
425 SF_LOG_DEBUG("maps", "++ PathGenerator::BuildPointPath FAILED! path sized %d returned\n", pointCount);
428 return;
429 }
430 else if (pointCount == _pointPathLimit)
431 {
432 SF_LOG_DEBUG("maps", "++ PathGenerator::BuildPointPath FAILED! path sized %d returned, lower than limit set to %d\n", pointCount, _pointPathLimit);
435 return;
436 }
437
438 _pathPoints.resize(pointCount);
439 for (uint32 i = 0; i < pointCount; ++i)
440 _pathPoints[i] = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]);
441
443
444 // first point is always our current location - we need the next one
445 SetActualEndPosition(_pathPoints[pointCount - 1]);
446
447 // force the given destination, if needed
448 if (_forceDestination &&
450 {
451 // we may want to keep partial subpath
453 {
455 _pathPoints[_pathPoints.size() - 1] = GetEndPosition();
456 }
457 else
458 {
461 }
462
464 }
465
466 SF_LOG_DEBUG("maps", "++ PathGenerator::BuildPointPath path type %d size %d poly-size %d\n", _type, pointCount, _polyLength);
467}
468
469float PathGenerator::ResolveTerrainZ(Unit const* unit, float x, float y, float z)
470{
471 Map const* map = unit->GetBaseMap();
472 uint32 const phase = unit->GetPhaseMask();
473 float ground = map->GetHeight(phase, x, y, z + 5.0f, true);
474 float floor = map->GetHeight(phase, x, y, z, true);
475
476 if (ground > INVALID_HEIGHT && floor > INVALID_HEIGHT)
477 z = fabs(ground - z) <= fabs(floor - z) ? ground : floor;
478 else if (floor > INVALID_HEIGHT)
479 z = floor;
480 else if (ground > INVALID_HEIGHT)
481 z = ground;
482
483 unit->UpdateAllowedPositionZ(x, y, z);
484 return z;
485}
486
488{
489 if (_pathPoints.size() < 2)
490 return;
491
492 if (_sourceUnit->GetTypeId() != TypeID::TYPEID_UNIT || _sourceUnit->CanFly())
493 return;
494
495 Creature const* creature = _sourceUnit->ToCreature();
496 if (!creature->CanWalk())
497 return;
498
499 if (CreatureTemplate const* info = creature->GetCreatureTemplate())
500 if ((info->InhabitType & INHABIT_AIR) && !(info->InhabitType & INHABIT_GROUND))
501 return;
502
503 Movement::PointsArray densified;
504 densified.reserve(_pathPoints.size() * 4);
505
506 for (size_t i = 0; i < _pathPoints.size(); ++i)
507 {
508 if (i == 0)
509 {
510 G3D::Vector3 const& p = _pathPoints[i];
511 densified.emplace_back(p.x, p.y, ResolveTerrainZ(_sourceUnit, p.x, p.y, p.z));
512 continue;
513 }
514
515 G3D::Vector3 const& prev = densified.back();
516 G3D::Vector3 const& next = _pathPoints[i];
517 float const dx = next.x - prev.x;
518 float const dy = next.y - prev.y;
519 float const dist2d = sqrt(dx * dx + dy * dy);
520
521 if (dist2d > SMOOTH_PATH_STEP_SIZE)
522 {
523 uint32 const steps = uint32(dist2d / SMOOTH_PATH_STEP_SIZE);
524 for (uint32 s = 1; s < steps; ++s)
525 {
526 float const t = float(s) / float(steps);
527 float x = prev.x + dx * t;
528 float y = prev.y + dy * t;
529 float z = prev.z + (next.z - prev.z) * t;
530 densified.emplace_back(x, y, ResolveTerrainZ(_sourceUnit, x, y, z));
531 }
532 }
533
534 densified.emplace_back(next.x, next.y, ResolveTerrainZ(_sourceUnit, next.x, next.y, next.z));
535 }
536
537 _pathPoints.swap(densified);
538}
539
541{
543
544 for (uint32 i = 0; i < _pathPoints.size(); ++i)
545 _sourceUnit->UpdateAllowedPositionZ(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z);
546}
547
549{
550 SF_LOG_DEBUG("maps", "++ BuildShortcut :: making shortcut\n");
551
552 Clear();
553
554 // make two point path, our curr pos is the start, and dest is the end
555 _pathPoints.resize(2);
556
557 // set start and a default next position
560
562
564}
565
567{
568 uint16 includeFlags = 0;
569 uint16 excludeFlags = 0;
570
571 if (_sourceUnit->GetTypeId() == TypeID::TYPEID_UNIT)
572 {
573 Creature* creature = (Creature*)_sourceUnit;
574 if (creature->CanWalk())
575 includeFlags |= NAV_GROUND; // walk
576
577 // creatures don't take environmental damage
578 if (creature->CanSwim())
579 includeFlags |= (NAV_WATER | NAV_MAGMA | NAV_SLIME); // swim
580 }
581 else // assume Player
582 {
583 // perfect support not possible, just stay 'safe'
584 includeFlags |= (NAV_GROUND | NAV_WATER | NAV_MAGMA | NAV_SLIME);
585 }
586
587 _filter.setIncludeFlags(includeFlags);
588 _filter.setExcludeFlags(excludeFlags);
589
590 UpdateFilter();
591}
592
594{
595 // allow creatures to cheat and use different movement types if they are moved
596 // forcefully into terrain they can't normally move in
597 if (_sourceUnit->IsInWater() || _sourceUnit->IsUnderWater())
598 {
599 uint16 includedFlags = _filter.getIncludeFlags();
600 includedFlags |= GetNavTerrain(_sourceUnit->GetPositionX(),
601 _sourceUnit->GetPositionY(),
602 _sourceUnit->GetPositionZ());
603
604 _filter.setIncludeFlags(includedFlags);
605 }
606}
607
608NavTerrain PathGenerator::GetNavTerrain(float x, float y, float z)
609{
610 LiquidData data;
611 ZLiquidStatus liquidStatus = _sourceUnit->GetBaseMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data);
612 if (liquidStatus == LIQUID_MAP_NO_WATER)
613 return NAV_GROUND;
614
615 switch (data.type_flags)
616 {
619 return NAV_WATER;
621 return NAV_MAGMA;
623 return NAV_SLIME;
624 default:
625 return NAV_GROUND;
626 }
627}
628
629bool PathGenerator::HaveTile(const G3D::Vector3& p) const
630{
631 int tx = -1, ty = -1;
632 float point[VERTEX_SIZE] = { p.y, p.z, p.x };
633
634 _navMesh->calcTileLoc(point, &tx, &ty);
635
639 if (tx < 0 || ty < 0)
640 return false;
641
642 return (_navMesh->getTileAt(tx, ty, 0) != NULL);
643}
644
645uint32 PathGenerator::FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited)
646{
647 int32 furthestPath = -1;
648 int32 furthestVisited = -1;
649
650 // Find furthest common polygon.
651 for (int32 i = npath - 1; i >= 0; --i)
652 {
653 bool found = false;
654 for (int32 j = nvisited - 1; j >= 0; --j)
655 {
656 if (path[i] == visited[j])
657 {
658 furthestPath = i;
659 furthestVisited = j;
660 found = true;
661 }
662 }
663 if (found)
664 break;
665 }
666
667 // If no intersection found just return current path.
668 if (furthestPath == -1 || furthestVisited == -1)
669 return npath;
670
671 // Concatenate paths.
672
673 // Adjust beginning of the buffer to include the visited.
674 uint32 req = nvisited - furthestVisited;
675 uint32 orig = uint32(furthestPath + 1) < npath ? furthestPath + 1 : npath;
676 uint32 size = npath > orig ? npath - orig : 0;
677 if (req + size > maxPath)
678 size = maxPath - req;
679
680 if (size)
681 memmove(path + req, path + orig, size * sizeof(dtPolyRef));
682
683 // Store visited
684 for (uint32 i = 0; i < req; ++i)
685 path[i] = visited[(nvisited - 1) - i];
686
687 return req + size;
688}
689
690bool PathGenerator::GetSteerTarget(float const* startPos, float const* endPos,
691 float minTargetDist, dtPolyRef const* path, uint32 pathSize,
692 float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef)
693{
694 // Find steer target.
695 static const uint32 MAX_STEER_POINTS = 3;
696 float steerPath[MAX_STEER_POINTS * VERTEX_SIZE];
697 unsigned char steerPathFlags[MAX_STEER_POINTS];
698 dtPolyRef steerPathPolys[MAX_STEER_POINTS];
699 uint32 nsteerPath = 0;
700 dtStatus dtResult = _navMeshQuery->findStraightPath(startPos, endPos, path, pathSize,
701 steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS);
702 if (!nsteerPath || dtStatusFailed(dtResult))
703 return false;
704
705 // Find vertex far enough to steer to.
706 uint32 ns = 0;
707 while (ns < nsteerPath)
708 {
709 // Stop at Off-Mesh link or when point is further than slop away.
710 if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
711 !InRangeYZX(&steerPath[ns * VERTEX_SIZE], startPos, minTargetDist, 1000.0f))
712 break;
713 ns++;
714 }
715 // Failed to find good point to steer to.
716 if (ns >= nsteerPath)
717 return false;
718
719 dtVcopy(steerPos, &steerPath[ns * VERTEX_SIZE]);
720 steerPos[1] = startPos[1]; // keep Z value
721 steerPosFlag = steerPathFlags[ns];
722 steerPosRef = steerPathPolys[ns];
723
724 return true;
725}
726
727dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPos,
728 dtPolyRef const* polyPath, uint32 polyPathSize,
729 float* smoothPath, int* smoothPathSize, uint32 maxSmoothPathSize)
730{
731 *smoothPathSize = 0;
732 uint32 nsmoothPath = 0;
733
734 dtPolyRef polys[MAX_PATH_LENGTH];
735 memcpy(polys, polyPath, sizeof(dtPolyRef) * polyPathSize);
736 uint32 npolys = polyPathSize;
737
738 float iterPos[VERTEX_SIZE], targetPos[VERTEX_SIZE];
739 if (dtStatusFailed(_navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos)))
740 return DT_FAILURE;
741
742 if (dtStatusFailed(_navMeshQuery->closestPointOnPolyBoundary(polys[npolys - 1], endPos, targetPos)))
743 return DT_FAILURE;
744
745 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], iterPos);
746 nsmoothPath++;
747
748 // Move towards target a small advancement at a time until target reached or
749 // when ran out of memory to store the path.
750 while (npolys && nsmoothPath < maxSmoothPathSize)
751 {
752 // Find location to steer towards.
753 float steerPos[VERTEX_SIZE];
754 unsigned char steerPosFlag;
755 dtPolyRef steerPosRef = INVALID_POLYREF;
756
757 if (!GetSteerTarget(iterPos, targetPos, SMOOTH_PATH_SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef))
758 break;
759
760 bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END);
761 bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION);
762
763 // Find movement delta.
764 float delta[VERTEX_SIZE];
765 dtVsub(delta, steerPos, iterPos);
766 float len = dtSqrt(dtVdot(delta, delta));
767 // If the steer target is end of path or off-mesh link, do not move past the location.
768 if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE)
769 len = 1.0f;
770 else
771 len = SMOOTH_PATH_STEP_SIZE / len;
772
773 float moveTgt[VERTEX_SIZE];
774 dtVmad(moveTgt, iterPos, delta, len);
775
776 // Move
777 float result[VERTEX_SIZE];
778 const static uint32 MAX_VISIT_POLY = 16;
779 dtPolyRef visited[MAX_VISIT_POLY];
780
781 uint32 nvisited = 0;
782 _navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY);
783 npolys = FixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited);
784
785 _navMeshQuery->getPolyHeight(polys[0], result, &result[1]);
786 result[1] += 0.5f;
787 dtVcopy(iterPos, result);
788
789 // Handle end of path and off-mesh links when close enough.
790 if (endOfPath && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
791 {
792 // Reached end of path.
793 dtVcopy(iterPos, targetPos);
794 if (nsmoothPath < maxSmoothPathSize)
795 {
796 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], iterPos);
797 nsmoothPath++;
798 }
799 break;
800 }
801 else if (offMeshConnection && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
802 {
803 // Advance the path up to and over the off-mesh connection.
804 dtPolyRef prevRef = INVALID_POLYREF;
805 dtPolyRef polyRef = polys[0];
806 uint32 npos = 0;
807 while (npos < npolys && polyRef != steerPosRef)
808 {
809 prevRef = polyRef;
810 polyRef = polys[npos];
811 npos++;
812 }
813
814 for (uint32 i = npos; i < npolys; ++i)
815 polys[i - npos] = polys[i];
816
817 npolys -= npos;
818
819 // Handle the connection.
820 float startPos[VERTEX_SIZE], endPos[VERTEX_SIZE];
821 if (dtStatusSucceed(_navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos)))
822 {
823 if (nsmoothPath < maxSmoothPathSize)
824 {
825 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], startPos);
826 nsmoothPath++;
827 }
828 // Move position at the other side of the off-mesh link.
829 dtVcopy(iterPos, endPos);
830 _navMeshQuery->getPolyHeight(polys[0], iterPos, &iterPos[1]);
831 iterPos[1] += 0.5f;
832 }
833 }
834
835 // Store results.
836 if (nsmoothPath < maxSmoothPathSize)
837 {
838 dtVcopy(&smoothPath[nsmoothPath * VERTEX_SIZE], iterPos);
839 nsmoothPath++;
840 }
841 }
842
843 *smoothPathSize = nsmoothPath;
844
845 // this is most likely a loop
846 return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE;
847}
848
849bool PathGenerator::InRangeYZX(const float* v1, const float* v2, float r, float h) const
850{
851 const float dx = v2[0] - v1[0];
852 const float dy = v2[1] - v1[1]; // elevation
853 const float dz = v2[2] - v1[2];
854 return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
855}
856
857bool PathGenerator::InRange(G3D::Vector3 const& p1, G3D::Vector3 const& p2, float r, float h) const
858{
859 G3D::Vector3 d = p1 - p2;
860 return (d.x * d.x + d.y * d.y) < r * r && fabsf(d.z) < h;
861}
862
863float PathGenerator::Dist3DSqr(G3D::Vector3 const& p1, G3D::Vector3 const& p2) const
864{
865 return (p1 - p2).squaredLength();
866}
@ INHABIT_AIR
Definition Creature.h:298
@ INHABIT_GROUND
Definition Creature.h:296
std::int32_t int32
Definition Define.h:73
std::uint32_t uint32
Definition Define.h:77
std::uint16_t uint16
Definition Define.h:78
#define ASSERT
Definition Errors.h:29
#define SF_LOG_DEBUG(filterType__,...)
Definition Log.h:134
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
ZLiquidStatus
Definition Map.h:114
@ LIQUID_MAP_NO_WATER
Definition Map.h:115
#define MAP_LIQUID_TYPE_MAGMA
Definition Map.h:125
#define MAP_LIQUID_TYPE_WATER
Definition Map.h:123
#define MAP_LIQUID_TYPE_OCEAN
Definition Map.h:124
#define MAP_LIQUID_TYPE_SLIME
Definition Map.h:126
#define MAP_ALL_LIQUIDS
Definition Map.h:128
#define INVALID_HEIGHT
Definition Map.h:142
@ TYPEID_UNIT
Definition Object.h:54
#define VERTEX_SIZE
#define INVALID_POLYREF
#define SMOOTH_PATH_SLOP
#define MAX_PATH_LENGTH
#define SMOOTH_PATH_STEP_SIZE
#define MAX_POINT_PATH_LENGTH
PathType
@ PATHFIND_NOT_USING_PATH
@ PATHFIND_NORMAL
@ PATHFIND_NOPATH
@ PATHFIND_SHORT
@ PATHFIND_SHORTCUT
@ PATHFIND_BLANK
@ PATHFIND_INCOMPLETE
NavTerrain
@ NAV_MAGMA
@ NAV_GROUND
@ NAV_SLIME
@ NAV_WATER
@ UNIT_STATE_IGNORE_PATHFINDING
Definition Unit.h:539
bool CanWalk() const
Definition Creature.h:456
bool CanFly() const OVERRIDE
Definition Creature.h:458
bool CanSwim() const
Definition Creature.h:457
CreatureTemplate const * GetCreatureTemplate() const
Definition Creature.h:524
static MMapManager * createOrGetMMapManager()
static bool IsPathfindingEnabled(uint32 mapId)
dtNavMeshQuery const * GetNavMeshQuery(uint32 mapId, uint32 instanceId, TerrainSet swaps)
dtNavMesh const * GetNavMesh(uint32 mapId, TerrainSet swaps)
Definition Map.h:238
float GetHeight(float x, float y, float z, bool checkVMap=true, float maxSearchDist=DEFAULT_HEIGHT_SEARCH) const
Definition Map.cpp:1959
PathGenerator(Unit const *owner)
bool HaveTile(G3D::Vector3 const &p) const
void SetActualEndPosition(G3D::Vector3 const &point)
G3D::Vector3 const & GetStartPosition() const
dtQueryFilter _filter
float Dist3DSqr(G3D::Vector3 const &p1, G3D::Vector3 const &p2) const
uint32 _pointPathLimit
dtStatus FindSmoothPath(float const *startPos, float const *endPos, dtPolyRef const *polyPath, uint32 polyPathSize, float *smoothPath, int *smoothPathSize, uint32 smoothPathMaxSize)
Unit const *const _sourceUnit
dtNavMeshQuery const * _navMeshQuery
G3D::Vector3 const & GetEndPosition() const
dtPolyRef GetPolyByLocation(float const *Point, float *Distance) const
void BuildPolyPath(G3D::Vector3 const &startPos, G3D::Vector3 const &endPos)
bool InRangeYZX(float const *v1, float const *v2, float r, float h) const
void SetStartPosition(G3D::Vector3 const &point)
uint32 FixupCorridor(dtPolyRef *path, uint32 npath, uint32 maxPath, dtPolyRef const *visited, uint32 nvisited)
dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]
Movement::PointsArray _pathPoints
bool InRange(G3D::Vector3 const &p1, G3D::Vector3 const &p2, float r, float h) const
NavTerrain GetNavTerrain(float x, float y, float z)
void BuildPointPath(float const *startPoint, float const *endPoint)
dtNavMesh const * _navMesh
dtPolyRef GetPathPolyByPosition(dtPolyRef const *polyPath, uint32 polyPathSize, float const *Point, float *Distance=NULL) const
bool CalculatePath(float destX, float destY, float destZ, bool forceDest=false)
bool GetSteerTarget(float const *startPos, float const *endPos, float minTargetDist, dtPolyRef const *path, uint32 pathSize, float *steerPos, unsigned char &steerPosFlag, dtPolyRef &steerPosRef)
void SetEndPosition(G3D::Vector3 const &point)
G3D::Vector3 const & GetActualEndPosition() const
static float ResolveTerrainZ(Unit const *unit, float x, float y, float z)
G3D::Vector3 _endPosition
Definition Unit.h:1367
uint32 GetPhaseMask() const
Definition Object.h:643
void UpdateAllowedPositionZ(float x, float y, float &z) const
Definition Object.cpp:1985
Map const * GetBaseMap() const
Definition Object.cpp:2544
std::vector< Vector3 > PointsArray
bool IsValidMapCoord(float c)
uint32 type_flags
Definition Map.h:135