CRecastHelper.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. #include "CRecastHelper.h"
  2. #include <iostream>
  3. #include <string>
  4. #include "../../../../Detour/Include/DetourCommon.h"
  5. #include "../../../../Detour/Include/DetourNavMesh.h"
  6. #include "../../../../Detour/Include/DetourNavMeshQuery.h"
  7. #include "../../../../Recast/Include/Recast.h"
  8. using namespace std;
  9. /////////////////////////////////////////////////
  10. // local functions
  11. /////////////////////////////////////////////////
  12. enum SamplePolyFlags
  13. {
  14. SAMPLE_POLYFLAGS_WALK = 0x01, // Ability to walk (ground, grass, road)
  15. SAMPLE_POLYFLAGS_SWIM = 0x02, // Ability to swim (water).
  16. SAMPLE_POLYFLAGS_DOOR = 0x04, // Ability to move through doors.
  17. SAMPLE_POLYFLAGS_JUMP = 0x08, // Ability to jump.
  18. SAMPLE_POLYFLAGS_DISABLED = 0x10, // Disabled polygon
  19. SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
  20. };
  21. static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';
  22. static const int NAVMESHSET_VERSION = 1;
  23. struct NavMeshSetHeader
  24. {
  25. int magic;
  26. int version;
  27. int numTiles;
  28. dtNavMeshParams params;
  29. };
  30. struct NavMeshTileHeader
  31. {
  32. dtTileRef tileRef;
  33. int dataSize;
  34. };
  35. dtNavMesh* loadMap(const char* path)
  36. {
  37. FILE* fp = NULL;
  38. errno_t error = fopen_s(&fp, path, "rb");
  39. if (!fp) return 0;
  40. // Read header.
  41. NavMeshSetHeader header;
  42. size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
  43. if (readLen != 1)
  44. {
  45. fclose(fp);
  46. return 0;
  47. }
  48. if (header.magic != NAVMESHSET_MAGIC)
  49. {
  50. fclose(fp);
  51. return 0;
  52. }
  53. if (header.version != NAVMESHSET_VERSION)
  54. {
  55. fclose(fp);
  56. return 0;
  57. }
  58. dtNavMesh* mesh = dtAllocNavMesh();
  59. if (!mesh)
  60. {
  61. fclose(fp);
  62. return 0;
  63. }
  64. dtStatus status = mesh->init(&header.params);
  65. if (dtStatusFailed(status))
  66. {
  67. fclose(fp);
  68. return 0;
  69. }
  70. // Read tiles.
  71. for (int i = 0; i < header.numTiles; ++i)
  72. {
  73. NavMeshTileHeader tileHeader;
  74. readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
  75. if (readLen != 1)
  76. {
  77. fclose(fp);
  78. return 0;
  79. }
  80. if (!tileHeader.tileRef || !tileHeader.dataSize)
  81. break;
  82. unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
  83. if (!data) break;
  84. memset(data, 0, tileHeader.dataSize);
  85. readLen = fread(data, tileHeader.dataSize, 1, fp);
  86. if (readLen != 1)
  87. {
  88. dtFree(data);
  89. fclose(fp);
  90. return 0;
  91. }
  92. mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
  93. }
  94. fclose(fp);
  95. return mesh;
  96. }
  97. inline bool inRange(const float* v1, const float* v2, const float r, const float h)
  98. {
  99. const float dx = v2[0] - v1[0];
  100. const float dy = v2[1] - v1[1];
  101. const float dz = v2[2] - v1[2];
  102. return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
  103. }
  104. static int fixupCorridor(dtPolyRef* path, const int npath, const int maxPath,
  105. const dtPolyRef* visited, const int nvisited)
  106. {
  107. int furthestPath = -1;
  108. int furthestVisited = -1;
  109. // Find furthest common polygon.
  110. for (int i = npath - 1; i >= 0; --i)
  111. {
  112. bool found = false;
  113. for (int j = nvisited - 1; j >= 0; --j)
  114. {
  115. if (path[i] == visited[j])
  116. {
  117. furthestPath = i;
  118. furthestVisited = j;
  119. found = true;
  120. }
  121. }
  122. if (found)
  123. break;
  124. }
  125. // If no intersection found just return current path.
  126. if (furthestPath == -1 || furthestVisited == -1)
  127. return npath;
  128. // Concatenate paths.
  129. // Adjust beginning of the buffer to include the visited.
  130. const int req = nvisited - furthestVisited;
  131. const int orig = rcMin(furthestPath + 1, npath);
  132. int size = rcMax(0, npath - orig);
  133. if (req + size > maxPath)
  134. size = maxPath - req;
  135. if (size)
  136. memmove(path + req, path + orig, size * sizeof(dtPolyRef));
  137. // Store visited
  138. for (int i = 0; i < req; ++i)
  139. path[i] = visited[(nvisited - 1) - i];
  140. return req + size;
  141. }
  142. // This function checks if the path has a small U-turn, that is,
  143. // a polygon further in the path is adjacent to the first polygon
  144. // in the path. If that happens, a shortcut is taken.
  145. // This can happen if the target (T) location is at tile boundary,
  146. // and we're (S) approaching it parallel to the tile edge.
  147. // The choice at the vertex can be arbitrary,
  148. // +---+---+
  149. // |:::|:::|
  150. // +-S-+-T-+
  151. // |:::| | <-- the step can end up in here, resulting U-turn path.
  152. // +---+---+
  153. static int fixupShortcuts(dtPolyRef* path, int npath, dtNavMeshQuery* navQuery)
  154. {
  155. if (npath < 3)
  156. return npath;
  157. // Get connected polygons
  158. static const int maxNeis = 16;
  159. dtPolyRef neis[maxNeis];
  160. int nneis = 0;
  161. const dtMeshTile* tile = 0;
  162. const dtPoly* poly = 0;
  163. if (dtStatusFailed(navQuery->getAttachedNavMesh()->getTileAndPolyByRef(path[0], &tile, &poly)))
  164. return npath;
  165. for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
  166. {
  167. const dtLink* link = &tile->links[k];
  168. if (link->ref != 0)
  169. {
  170. if (nneis < maxNeis)
  171. neis[nneis++] = link->ref;
  172. }
  173. }
  174. // If any of the neighbour polygons is within the next few polygons
  175. // in the path, short cut to that polygon directly.
  176. static const int maxLookAhead = 6;
  177. int cut = 0;
  178. for (int i = dtMin(maxLookAhead, npath) - 1; i > 1 && cut == 0; i--) {
  179. for (int j = 0; j < nneis; j++)
  180. {
  181. if (path[i] == neis[j]) {
  182. cut = i;
  183. break;
  184. }
  185. }
  186. }
  187. if (cut > 1)
  188. {
  189. int offset = cut - 1;
  190. npath -= offset;
  191. for (int i = 1; i < npath; i++)
  192. path[i] = path[i + offset];
  193. }
  194. return npath;
  195. }
  196. static bool getSteerTarget(dtNavMeshQuery* navQuery, const float* startPos, const float* endPos,
  197. const float minTargetDist,
  198. const dtPolyRef* path, const int pathSize,
  199. float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef,
  200. float* outPoints = 0, int* outPointCount = 0)
  201. {
  202. // Find steer target.
  203. static const int MAX_STEER_POINTS = 3;
  204. float steerPath[MAX_STEER_POINTS * 3];
  205. unsigned char steerPathFlags[MAX_STEER_POINTS];
  206. dtPolyRef steerPathPolys[MAX_STEER_POINTS];
  207. int nsteerPath = 0;
  208. navQuery->findStraightPath(startPos, endPos, path, pathSize,
  209. steerPath, steerPathFlags, steerPathPolys, &nsteerPath, MAX_STEER_POINTS);
  210. if (!nsteerPath)
  211. return false;
  212. if (outPoints && outPointCount)
  213. {
  214. *outPointCount = nsteerPath;
  215. for (int i = 0; i < nsteerPath; ++i)
  216. dtVcopy(&outPoints[i * 3], &steerPath[i * 3]);
  217. }
  218. // Find vertex far enough to steer to.
  219. int ns = 0;
  220. while (ns < nsteerPath)
  221. {
  222. // Stop at Off-Mesh link or when point is further than slop away.
  223. if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
  224. !inRange(&steerPath[ns * 3], startPos, minTargetDist, 1000.0f))
  225. break;
  226. ns++;
  227. }
  228. // Failed to find good point to steer to.
  229. if (ns >= nsteerPath)
  230. return false;
  231. dtVcopy(steerPos, &steerPath[ns * 3]);
  232. steerPos[1] = startPos[1];
  233. steerPosFlag = steerPathFlags[ns];
  234. steerPosRef = steerPathPolys[ns];
  235. return true;
  236. }
  237. /////////////////////////////////////////////////
  238. // class CRecast
  239. /////////////////////////////////////////////////
  240. CRecast::CRecast()
  241. {
  242. m_navMesh = NULL;
  243. m_navQuery = NULL;
  244. }
  245. bool CRecast::LoadMap(const char* path)
  246. {
  247. m_navMesh = loadMap(path);
  248. m_navQuery = dtAllocNavMeshQuery();
  249. if (!m_navMesh)
  250. return false;
  251. m_navQuery->init(m_navMesh, 2048);
  252. return true;
  253. }
  254. void CRecast::FreeMap()
  255. {
  256. dtFreeNavMeshQuery(m_navQuery);
  257. }
  258. unsigned int DT_COORD_INVALID = 1 << 13; // 新增错误码:传入坐标错误
  259. /// <summary>
  260. /// 寻路
  261. /// </summary>
  262. /// <param name="spos">float[3] 起点三维坐标</param>
  263. /// <param name="epos">float[3] 终点三维坐标</param>
  264. /// <returns>寻路的结果</returns>
  265. dtStatus CRecast::FindPath(const float* spos, const float* epos)
  266. {
  267. dtVcopy(m_spos, spos);
  268. dtVcopy(m_epos, epos);
  269. m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
  270. m_filter.setExcludeFlags(0);
  271. m_navQuery->findNearestPoly(spos, m_polyPickExt, &m_filter, &m_startRef, 0);
  272. m_navQuery->findNearestPoly(epos, m_polyPickExt, &m_filter, &m_endRef, 0);
  273. if (!m_startRef || !m_endRef)
  274. {
  275. return DT_FAILURE| DT_COORD_INVALID;
  276. }
  277. // 开始寻路
  278. dtStatus status = m_navQuery->findPath(m_startRef, m_endRef, spos, epos, &m_filter, m_polys, &m_npolys, MAX_POLYS);
  279. return status;
  280. }
  281. dtStatus CRecast::Raycast(const float* spos, const float* epos)
  282. {
  283. dtVcopy(m_spos, spos);
  284. dtVcopy(m_epos, epos);
  285. m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
  286. m_filter.setExcludeFlags(0);
  287. m_navQuery->findNearestPoly(spos, m_polyPickExt, &m_filter, &m_startRef, 0);
  288. m_navQuery->findNearestPoly(epos, m_polyPickExt, &m_filter, &m_endRef, 0);
  289. if (!m_startRef || !m_endRef)
  290. {
  291. return DT_FAILURE | DT_COORD_INVALID;
  292. }
  293. float hit_param = FLT_MAX;
  294. float hit_normal[3] = {0.0f};
  295. memset(&m_npolys, 0, sizeof(dtPolyRef) * MAX_POLYS);
  296. m_npolys = 0;
  297. // float target[3] = {m_epos[0], m_epos[1], m_epos[2]};
  298. dtPolyRef target_ref;
  299. float nearest[3] = {0};
  300. dtStatus status = m_navQuery->findNearestPoly(
  301. m_epos, m_polyPickExt, &m_filter, &target_ref, nearest);
  302. if (dtStatusFailed(status)) return false;
  303. if (!m_navQuery->isValidPolyRef(target_ref, &m_filter)) return false;
  304. // fix 目标点高度
  305. m_epos[1] = nearest[1];
  306. // 开始射线检测
  307. status = m_navQuery->raycast(m_startRef, m_spos, m_epos, &m_filter, &hit_param, hit_normal, m_polys, &m_npolys, MAX_POLYS);
  308. if (dtStatusFailed(status)) return status;
  309. //计算碰撞点逻辑
  310. if (0.0f < hit_param && hit_param < 1.0f)
  311. {
  312. float delta[3];
  313. for (int i = 0; i < 3; ++i)
  314. {
  315. delta[i] = m_epos[i] - m_spos[i];
  316. m_hitPos[i] = m_spos[i] + delta[i] * hit_param;
  317. }
  318. }
  319. else if (0.000001f > hit_param)
  320. {
  321. memcpy(m_hitPos, m_spos, sizeof(m_hitPos));
  322. }
  323. else
  324. {
  325. memcpy(m_hitPos, m_epos, sizeof(m_hitPos));
  326. }
  327. return status;
  328. }
  329. void CRecast::Smooth(float STEP_SIZE, float SLOP)
  330. {
  331. m_nsmoothPath = 0;
  332. if (m_npolys)
  333. {
  334. // Iterate over the path to find smooth path on the detail mesh surface.
  335. dtPolyRef polys[MAX_POLYS];
  336. memcpy(polys, m_polys, sizeof(dtPolyRef) * m_npolys);
  337. int npolys = m_npolys;
  338. float iterPos[3], targetPos[3];
  339. m_navQuery->closestPointOnPoly(m_startRef, m_spos, iterPos, 0);
  340. m_navQuery->closestPointOnPoly(polys[npolys - 1], m_epos, targetPos, 0);
  341. if(STEP_SIZE == 0) STEP_SIZE = 0.5f;
  342. if(SLOP == 0) SLOP = 0.01f;
  343. m_nsmoothPath = 0;
  344. dtVcopy(&m_smoothPath[m_nsmoothPath * 3], iterPos);
  345. m_nsmoothPath++;
  346. // Move towards target a small advancement at a time until target reached or
  347. // when ran out of memory to store the path.
  348. while (npolys && m_nsmoothPath < MAX_SMOOTH)
  349. {
  350. // Find location to steer towards.
  351. float steerPos[3];
  352. unsigned char steerPosFlag;
  353. dtPolyRef steerPosRef;
  354. if (!getSteerTarget(m_navQuery, iterPos, targetPos, SLOP,
  355. polys, npolys, steerPos, steerPosFlag, steerPosRef))
  356. break;
  357. bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END) ? true : false;
  358. bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ? true : false;
  359. // Find movement delta.
  360. float delta[3], len;
  361. dtVsub(delta, steerPos, iterPos);
  362. len = dtMathSqrtf(dtVdot(delta, delta));
  363. // If the steer target is end of path or off-mesh link, do not move past the location.
  364. if ((endOfPath || offMeshConnection) && len < STEP_SIZE)
  365. len = 1;
  366. else
  367. len = STEP_SIZE / len;
  368. float moveTgt[3];
  369. dtVmad(moveTgt, iterPos, delta, len);
  370. // Move
  371. float result[3];
  372. dtPolyRef visited[16];
  373. int nvisited = 0;
  374. m_navQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &m_filter,
  375. result, visited, &nvisited, 16);
  376. npolys = fixupCorridor(polys, npolys, MAX_POLYS, visited, nvisited);
  377. npolys = fixupShortcuts(polys, npolys, m_navQuery);
  378. // BUG FIX:原来是h=0, 这里改为目的地的高度,否则如果getPolyHeight()没有得到数值(这是有可能的),则高度为0,就错了。Aug.2.2020. Liu Gang.
  379. // 原因:如果这里的getPolyHeight()获取失败,则h就为零了,而下一个循环,就会在上面的getSteerTarget()中结束,这里就是最后一个节点,高度为零
  380. float h = result[1];
  381. m_navQuery->getPolyHeight(polys[0], result, &h);
  382. result[1] = h;
  383. dtVcopy(iterPos, result);
  384. // Handle end of path and off-mesh links when close enough.
  385. if (endOfPath && inRange(iterPos, steerPos, SLOP, 1.0f))
  386. {
  387. // Reached end of path.
  388. dtVcopy(iterPos, targetPos);
  389. if (m_nsmoothPath < MAX_SMOOTH)
  390. {
  391. dtVcopy(&m_smoothPath[m_nsmoothPath * 3], iterPos);
  392. m_nsmoothPath++;
  393. }
  394. break;
  395. }
  396. else if (offMeshConnection && inRange(iterPos, steerPos, SLOP, 1.0f))
  397. {
  398. // Reached off-mesh connection.
  399. float startPos[3], endPos[3];
  400. // Advance the path up to and over the off-mesh connection.
  401. dtPolyRef prevRef = 0, polyRef = polys[0];
  402. int npos = 0;
  403. while (npos < npolys && polyRef != steerPosRef)
  404. {
  405. prevRef = polyRef;
  406. polyRef = polys[npos];
  407. npos++;
  408. }
  409. for (int i = npos; i < npolys; ++i)
  410. polys[i - npos] = polys[i];
  411. npolys -= npos;
  412. // Handle the connection.
  413. dtStatus status = m_navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos);
  414. if (dtStatusSucceed(status))
  415. {
  416. if (m_nsmoothPath < MAX_SMOOTH)
  417. {
  418. dtVcopy(&m_smoothPath[m_nsmoothPath * 3], startPos);
  419. m_nsmoothPath++;
  420. // Hack to make the dotted path not visible during off-mesh connection.
  421. if (m_nsmoothPath & 1)
  422. {
  423. dtVcopy(&m_smoothPath[m_nsmoothPath * 3], startPos);
  424. m_nsmoothPath++;
  425. }
  426. }
  427. // Move position at the other side of the off-mesh link.
  428. dtVcopy(iterPos, endPos);
  429. float eh = 0.0f;
  430. m_navQuery->getPolyHeight(polys[0], iterPos, &eh);
  431. iterPos[1] = eh;
  432. }
  433. }
  434. // Store results.
  435. if (m_nsmoothPath < MAX_SMOOTH)
  436. {
  437. dtVcopy(&m_smoothPath[m_nsmoothPath * 3], iterPos);
  438. m_nsmoothPath++;
  439. }
  440. }
  441. }
  442. }
  443. float* CRecast::fixPosition(const float* pos) {
  444. dtPolyRef posRef;
  445. m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
  446. m_filter.setExcludeFlags(0);
  447. float nearest[3] = {0};
  448. dtStatus status = m_navQuery->findNearestPoly(pos, m_polyPickExt, &m_filter,
  449. &posRef, nearest);
  450. if (dtStatusFailed(status)) return nullptr;
  451. if (!posRef) return nullptr; // 有时候返回成功,但是也没有找到对应的多边形,实际上还是出错了
  452. memcpy(m_fixPos, nearest, sizeof(m_fixPos));
  453. return m_fixPos;
  454. //float h = 0;
  455. //status = m_navQuery->getPolyHeight(posRef, nearest, &h);
  456. //if (!dtStatusFailed(status))
  457. //{
  458. // m_fixPos[1] = h;
  459. //}
  460. //return m_fixPos;
  461. }
  462. /////////////////////////////////////////////////
  463. // class CRecastHelper
  464. /////////////////////////////////////////////////
  465. CRecastHelper::~CRecastHelper()
  466. {
  467. for (map<int, CRecast*>::iterator iter = m_mapRecast.begin(); iter != m_mapRecast.end(); iter++)
  468. {
  469. delete iter->second;
  470. }
  471. m_mapRecast.clear();
  472. }
  473. bool CRecastHelper::LoadMap(int id, const char* path)
  474. {
  475. CRecast* recast = new CRecast();
  476. bool ret = recast->LoadMap(path);
  477. if (!ret)
  478. return false;
  479. m_mapRecast[id] = recast;
  480. return true;
  481. }
  482. void CRecastHelper::FreeMap(int id)
  483. {
  484. m_mapRecast[id]->FreeMap();
  485. delete m_mapRecast[id];
  486. m_mapRecast.erase(id);
  487. }