| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- #include "CRecastHelper.h"
- #include <iostream>
- #include <string>
- #include "../../../../Detour/Include/DetourCommon.h"
- #include "../../../../Detour/Include/DetourNavMesh.h"
- #include "../../../../Detour/Include/DetourNavMeshQuery.h"
- #include "../../../../Recast/Include/Recast.h"
- using namespace std;
- /////////////////////////////////////////////////
- // local functions
- /////////////////////////////////////////////////
- enum SamplePolyFlags
- {
- SAMPLE_POLYFLAGS_WALK = 0x01, // Ability to walk (ground, grass, road)
- SAMPLE_POLYFLAGS_SWIM = 0x02, // Ability to swim (water).
- SAMPLE_POLYFLAGS_DOOR = 0x04, // Ability to move through doors.
- SAMPLE_POLYFLAGS_JUMP = 0x08, // Ability to jump.
- SAMPLE_POLYFLAGS_DISABLED = 0x10, // Disabled polygon
- SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
- };
- static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';
- static const int NAVMESHSET_VERSION = 1;
- struct NavMeshSetHeader
- {
- int magic;
- int version;
- int numTiles;
- dtNavMeshParams params;
- };
- struct NavMeshTileHeader
- {
- dtTileRef tileRef;
- int dataSize;
- };
- dtNavMesh* loadMap(const char* path)
- {
- FILE* fp = NULL;
- errno_t error = fopen_s(&fp, path, "rb");
- if (!fp) return 0;
- // Read header.
- NavMeshSetHeader header;
- size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
- if (readLen != 1)
- {
- fclose(fp);
- return 0;
- }
- if (header.magic != NAVMESHSET_MAGIC)
- {
- fclose(fp);
- return 0;
- }
- if (header.version != NAVMESHSET_VERSION)
- {
- fclose(fp);
- return 0;
- }
- dtNavMesh* mesh = dtAllocNavMesh();
- if (!mesh)
- {
- fclose(fp);
- return 0;
- }
- dtStatus status = mesh->init(&header.params);
- if (dtStatusFailed(status))
- {
- fclose(fp);
- return 0;
- }
- // Read tiles.
- for (int i = 0; i < header.numTiles; ++i)
- {
- NavMeshTileHeader tileHeader;
- readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
- if (readLen != 1)
- {
- fclose(fp);
- return 0;
- }
- if (!tileHeader.tileRef || !tileHeader.dataSize)
- break;
- unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
- if (!data) break;
- memset(data, 0, tileHeader.dataSize);
- readLen = fread(data, tileHeader.dataSize, 1, fp);
- if (readLen != 1)
- {
- dtFree(data);
- fclose(fp);
- return 0;
- }
- mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
- }
- fclose(fp);
- return mesh;
- }
- inline bool inRange(const float* v1, const float* v2, const float r, const float h)
- {
- const float dx = v2[0] - v1[0];
- const float dy = v2[1] - v1[1];
- const float dz = v2[2] - v1[2];
- return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
- }
- static int fixupCorridor(dtPolyRef* path, const int npath, const int maxPath,
- const dtPolyRef* visited, const int nvisited)
- {
- int furthestPath = -1;
- int furthestVisited = -1;
- // Find furthest common polygon.
- for (int i = npath - 1; i >= 0; --i)
- {
- bool found = false;
- for (int j = nvisited - 1; j >= 0; --j)
- {
- if (path[i] == visited[j])
- {
- furthestPath = i;
- furthestVisited = j;
- found = true;
- }
- }
- if (found)
- break;
- }
- // If no intersection found just return current path.
- if (furthestPath == -1 || furthestVisited == -1)
- return npath;
- // Concatenate paths.
- // Adjust beginning of the buffer to include the visited.
- const int req = nvisited - furthestVisited;
- const int orig = rcMin(furthestPath + 1, npath);
- int size = rcMax(0, npath - orig);
- if (req + size > maxPath)
- size = maxPath - req;
- if (size)
- memmove(path + req, path + orig, size * sizeof(dtPolyRef));
- // Store visited
- for (int i = 0; i < req; ++i)
- path[i] = visited[(nvisited - 1) - i];
- return req + size;
- }
- // This function checks if the path has a small U-turn, that is,
- // a polygon further in the path is adjacent to the first polygon
- // in the path. If that happens, a shortcut is taken.
- // This can happen if the target (T) location is at tile boundary,
- // and we're (S) approaching it parallel to the tile edge.
- // The choice at the vertex can be arbitrary,
- // +---+---+
- // |:::|:::|
- // +-S-+-T-+
- // |:::| | <-- the step can end up in here, resulting U-turn path.
- // +---+---+
- static int fixupShortcuts(dtPolyRef* path, int npath, dtNavMeshQuery* navQuery)
- {
- if (npath < 3)
- return npath;
- // Get connected polygons
- static const int maxNeis = 16;
- dtPolyRef neis[maxNeis];
- int nneis = 0;
- const dtMeshTile* tile = 0;
- const dtPoly* poly = 0;
- if (dtStatusFailed(navQuery->getAttachedNavMesh()->getTileAndPolyByRef(path[0], &tile, &poly)))
- return npath;
- for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
- {
- const dtLink* link = &tile->links[k];
- if (link->ref != 0)
- {
- if (nneis < maxNeis)
- neis[nneis++] = link->ref;
- }
- }
- // If any of the neighbour polygons is within the next few polygons
- // in the path, short cut to that polygon directly.
- static const int maxLookAhead = 6;
- int cut = 0;
- for (int i = dtMin(maxLookAhead, npath) - 1; i > 1 && cut == 0; i--) {
- for (int j = 0; j < nneis; j++)
- {
- if (path[i] == neis[j]) {
- cut = i;
- break;
- }
- }
- }
- if (cut > 1)
- {
- int offset = cut - 1;
- npath -= offset;
- for (int i = 1; i < npath; i++)
- path[i] = path[i + offset];
- }
- return npath;
- }
- static bool getSteerTarget(dtNavMeshQuery* navQuery, const float* startPos, const float* endPos,
- const float minTargetDist,
- const dtPolyRef* path, const int pathSize,
- float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef,
- float* outPoints = 0, int* outPointCount = 0)
- {
- // Find steer target.
- static const int MAX_STEER_POINTS = 3;
- float steerPath[MAX_STEER_POINTS * 3];
- unsigned char steerPathFlags[MAX_STEER_POINTS];
- dtPolyRef steerPathPolys[MAX_STEER_POINTS];
- int nsteerPath = 0;
- navQuery->findStraightPath(startPos, endPos, path, pathSize,
- steerPath, steerPathFlags, steerPathPolys, &nsteerPath, MAX_STEER_POINTS);
- if (!nsteerPath)
- return false;
- if (outPoints && outPointCount)
- {
- *outPointCount = nsteerPath;
- for (int i = 0; i < nsteerPath; ++i)
- dtVcopy(&outPoints[i * 3], &steerPath[i * 3]);
- }
- // Find vertex far enough to steer to.
- int ns = 0;
- while (ns < nsteerPath)
- {
- // Stop at Off-Mesh link or when point is further than slop away.
- if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
- !inRange(&steerPath[ns * 3], startPos, minTargetDist, 1000.0f))
- break;
- ns++;
- }
- // Failed to find good point to steer to.
- if (ns >= nsteerPath)
- return false;
- dtVcopy(steerPos, &steerPath[ns * 3]);
- steerPos[1] = startPos[1];
- steerPosFlag = steerPathFlags[ns];
- steerPosRef = steerPathPolys[ns];
- return true;
- }
- /////////////////////////////////////////////////
- // class CRecast
- /////////////////////////////////////////////////
- CRecast::CRecast()
- {
- m_navMesh = NULL;
- m_navQuery = NULL;
- }
- bool CRecast::LoadMap(const char* path)
- {
- m_navMesh = loadMap(path);
- m_navQuery = dtAllocNavMeshQuery();
- if (!m_navMesh)
- return false;
- m_navQuery->init(m_navMesh, 2048);
- return true;
- }
- void CRecast::FreeMap()
- {
- dtFreeNavMeshQuery(m_navQuery);
- }
- unsigned int DT_COORD_INVALID = 1 << 13; // 新增错误码:传入坐标错误
- /// <summary>
- /// 寻路
- /// </summary>
- /// <param name="spos">float[3] 起点三维坐标</param>
- /// <param name="epos">float[3] 终点三维坐标</param>
- /// <returns>寻路的结果</returns>
- dtStatus CRecast::FindPath(const float* spos, const float* epos)
- {
- dtVcopy(m_spos, spos);
- dtVcopy(m_epos, epos);
- m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
- m_filter.setExcludeFlags(0);
- m_navQuery->findNearestPoly(spos, m_polyPickExt, &m_filter, &m_startRef, 0);
- m_navQuery->findNearestPoly(epos, m_polyPickExt, &m_filter, &m_endRef, 0);
- if (!m_startRef || !m_endRef)
- {
- return DT_FAILURE| DT_COORD_INVALID;
- }
- // 开始寻路
- dtStatus status = m_navQuery->findPath(m_startRef, m_endRef, spos, epos, &m_filter, m_polys, &m_npolys, MAX_POLYS);
- return status;
- }
- dtStatus CRecast::Raycast(const float* spos, const float* epos)
- {
- dtVcopy(m_spos, spos);
- dtVcopy(m_epos, epos);
- m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
- m_filter.setExcludeFlags(0);
- m_navQuery->findNearestPoly(spos, m_polyPickExt, &m_filter, &m_startRef, 0);
- m_navQuery->findNearestPoly(epos, m_polyPickExt, &m_filter, &m_endRef, 0);
- if (!m_startRef || !m_endRef)
- {
- return DT_FAILURE | DT_COORD_INVALID;
- }
- float hit_param = FLT_MAX;
- float hit_normal[3] = {0.0f};
- memset(&m_npolys, 0, sizeof(dtPolyRef) * MAX_POLYS);
- m_npolys = 0;
- // float target[3] = {m_epos[0], m_epos[1], m_epos[2]};
- dtPolyRef target_ref;
- float nearest[3] = {0};
- dtStatus status = m_navQuery->findNearestPoly(
- m_epos, m_polyPickExt, &m_filter, &target_ref, nearest);
- if (dtStatusFailed(status)) return false;
- if (!m_navQuery->isValidPolyRef(target_ref, &m_filter)) return false;
- // fix 目标点高度
- m_epos[1] = nearest[1];
- // 开始射线检测
- status = m_navQuery->raycast(m_startRef, m_spos, m_epos, &m_filter, &hit_param, hit_normal, m_polys, &m_npolys, MAX_POLYS);
- if (dtStatusFailed(status)) return status;
- //计算碰撞点逻辑
- if (0.0f < hit_param && hit_param < 1.0f)
- {
- float delta[3];
- for (int i = 0; i < 3; ++i)
- {
- delta[i] = m_epos[i] - m_spos[i];
- m_hitPos[i] = m_spos[i] + delta[i] * hit_param;
- }
- }
- else if (0.000001f > hit_param)
- {
- memcpy(m_hitPos, m_spos, sizeof(m_hitPos));
- }
- else
- {
- memcpy(m_hitPos, m_epos, sizeof(m_hitPos));
- }
- return status;
- }
- void CRecast::Smooth(float STEP_SIZE, float SLOP)
- {
- m_nsmoothPath = 0;
- if (m_npolys)
- {
- // Iterate over the path to find smooth path on the detail mesh surface.
- dtPolyRef polys[MAX_POLYS];
- memcpy(polys, m_polys, sizeof(dtPolyRef) * m_npolys);
- int npolys = m_npolys;
- float iterPos[3], targetPos[3];
- m_navQuery->closestPointOnPoly(m_startRef, m_spos, iterPos, 0);
- m_navQuery->closestPointOnPoly(polys[npolys - 1], m_epos, targetPos, 0);
- if(STEP_SIZE == 0) STEP_SIZE = 0.5f;
- if(SLOP == 0) SLOP = 0.01f;
- m_nsmoothPath = 0;
- dtVcopy(&m_smoothPath[m_nsmoothPath * 3], iterPos);
- m_nsmoothPath++;
- // Move towards target a small advancement at a time until target reached or
- // when ran out of memory to store the path.
- while (npolys && m_nsmoothPath < MAX_SMOOTH)
- {
- // Find location to steer towards.
- float steerPos[3];
- unsigned char steerPosFlag;
- dtPolyRef steerPosRef;
- if (!getSteerTarget(m_navQuery, iterPos, targetPos, SLOP,
- polys, npolys, steerPos, steerPosFlag, steerPosRef))
- break;
- bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END) ? true : false;
- bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ? true : false;
- // Find movement delta.
- float delta[3], len;
- dtVsub(delta, steerPos, iterPos);
- len = dtMathSqrtf(dtVdot(delta, delta));
- // If the steer target is end of path or off-mesh link, do not move past the location.
- if ((endOfPath || offMeshConnection) && len < STEP_SIZE)
- len = 1;
- else
- len = STEP_SIZE / len;
- float moveTgt[3];
- dtVmad(moveTgt, iterPos, delta, len);
- // Move
- float result[3];
- dtPolyRef visited[16];
- int nvisited = 0;
- m_navQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &m_filter,
- result, visited, &nvisited, 16);
- npolys = fixupCorridor(polys, npolys, MAX_POLYS, visited, nvisited);
- npolys = fixupShortcuts(polys, npolys, m_navQuery);
- // BUG FIX:原来是h=0, 这里改为目的地的高度,否则如果getPolyHeight()没有得到数值(这是有可能的),则高度为0,就错了。Aug.2.2020. Liu Gang.
- // 原因:如果这里的getPolyHeight()获取失败,则h就为零了,而下一个循环,就会在上面的getSteerTarget()中结束,这里就是最后一个节点,高度为零
- float h = result[1];
- m_navQuery->getPolyHeight(polys[0], result, &h);
- result[1] = h;
- dtVcopy(iterPos, result);
- // Handle end of path and off-mesh links when close enough.
- if (endOfPath && inRange(iterPos, steerPos, SLOP, 1.0f))
- {
- // Reached end of path.
- dtVcopy(iterPos, targetPos);
- if (m_nsmoothPath < MAX_SMOOTH)
- {
- dtVcopy(&m_smoothPath[m_nsmoothPath * 3], iterPos);
- m_nsmoothPath++;
- }
- break;
- }
- else if (offMeshConnection && inRange(iterPos, steerPos, SLOP, 1.0f))
- {
- // Reached off-mesh connection.
- float startPos[3], endPos[3];
- // Advance the path up to and over the off-mesh connection.
- dtPolyRef prevRef = 0, polyRef = polys[0];
- int npos = 0;
- while (npos < npolys && polyRef != steerPosRef)
- {
- prevRef = polyRef;
- polyRef = polys[npos];
- npos++;
- }
- for (int i = npos; i < npolys; ++i)
- polys[i - npos] = polys[i];
- npolys -= npos;
- // Handle the connection.
- dtStatus status = m_navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos);
- if (dtStatusSucceed(status))
- {
- if (m_nsmoothPath < MAX_SMOOTH)
- {
- dtVcopy(&m_smoothPath[m_nsmoothPath * 3], startPos);
- m_nsmoothPath++;
- // Hack to make the dotted path not visible during off-mesh connection.
- if (m_nsmoothPath & 1)
- {
- dtVcopy(&m_smoothPath[m_nsmoothPath * 3], startPos);
- m_nsmoothPath++;
- }
- }
- // Move position at the other side of the off-mesh link.
- dtVcopy(iterPos, endPos);
- float eh = 0.0f;
- m_navQuery->getPolyHeight(polys[0], iterPos, &eh);
- iterPos[1] = eh;
- }
- }
- // Store results.
- if (m_nsmoothPath < MAX_SMOOTH)
- {
- dtVcopy(&m_smoothPath[m_nsmoothPath * 3], iterPos);
- m_nsmoothPath++;
- }
- }
- }
- }
- float* CRecast::fixPosition(const float* pos) {
- dtPolyRef posRef;
- m_filter.setIncludeFlags(SAMPLE_POLYFLAGS_ALL ^ SAMPLE_POLYFLAGS_DISABLED);
- m_filter.setExcludeFlags(0);
- float nearest[3] = {0};
- dtStatus status = m_navQuery->findNearestPoly(pos, m_polyPickExt, &m_filter,
- &posRef, nearest);
- if (dtStatusFailed(status)) return nullptr;
- if (!posRef) return nullptr; // 有时候返回成功,但是也没有找到对应的多边形,实际上还是出错了
- memcpy(m_fixPos, nearest, sizeof(m_fixPos));
- return m_fixPos;
- //float h = 0;
- //status = m_navQuery->getPolyHeight(posRef, nearest, &h);
- //if (!dtStatusFailed(status))
- //{
- // m_fixPos[1] = h;
- //}
- //return m_fixPos;
- }
- /////////////////////////////////////////////////
- // class CRecastHelper
- /////////////////////////////////////////////////
- CRecastHelper::~CRecastHelper()
- {
- for (map<int, CRecast*>::iterator iter = m_mapRecast.begin(); iter != m_mapRecast.end(); iter++)
- {
- delete iter->second;
- }
- m_mapRecast.clear();
- }
- bool CRecastHelper::LoadMap(int id, const char* path)
- {
- CRecast* recast = new CRecast();
- bool ret = recast->LoadMap(path);
- if (!ret)
- return false;
- m_mapRecast[id] = recast;
- return true;
- }
- void CRecastHelper::FreeMap(int id)
- {
- m_mapRecast[id]->FreeMap();
- delete m_mapRecast[id];
- m_mapRecast.erase(id);
- }
|