2019-11-23 20:27:39 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2015-2016 Chukong Technologies Inc.
|
|
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
http://www.cocos2d-x.org
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
2021-12-25 10:04:45 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
THE SOFTWARE.
|
|
|
|
****************************************************************************/
|
|
|
|
#include "navmesh/CCNavMeshUtils.h"
|
|
|
|
#if CC_USE_NAVMESH
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
# include "recast/DetourCommon.h"
|
|
|
|
# include "recast/DetourNavMeshBuilder.h"
|
|
|
|
# include "lz4/lz4.h"
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
NS_CC_BEGIN
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
LinearAllocator::LinearAllocator(const int cap) : buffer(nullptr), capacity(0), top(0), high(0)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
resize(cap);
|
|
|
|
}
|
|
|
|
|
|
|
|
LinearAllocator::~LinearAllocator()
|
|
|
|
{
|
|
|
|
dtFree(buffer);
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
void LinearAllocator::free(void* /*ptr*/) {}
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
void* LinearAllocator::alloc(const int size)
|
|
|
|
{
|
|
|
|
if (!buffer)
|
|
|
|
return nullptr;
|
|
|
|
if (top + size > capacity)
|
|
|
|
return nullptr;
|
|
|
|
unsigned char* mem = &buffer[top];
|
|
|
|
top += size;
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LinearAllocator::reset()
|
|
|
|
{
|
|
|
|
high = dtMax(high, top);
|
2021-12-25 10:04:45 +08:00
|
|
|
top = 0;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void LinearAllocator::resize(const int cap)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (buffer)
|
|
|
|
dtFree(buffer);
|
|
|
|
buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
|
2019-11-23 20:27:39 +08:00
|
|
|
capacity = cap;
|
|
|
|
}
|
|
|
|
|
2021-05-05 19:49:30 +08:00
|
|
|
int LZ4Compressor::maxCompressedSize(const int bufferSize)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-05-05 19:49:30 +08:00
|
|
|
return LZ4_compressBound(bufferSize);
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
dtStatus cocos2d::LZ4Compressor::decompress(const unsigned char* compressed,
|
|
|
|
const int compressedSize,
|
|
|
|
unsigned char* buffer,
|
|
|
|
const int maxBufferSize,
|
|
|
|
int* bufferSize)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
*bufferSize = LZ4_decompress_safe((const char*)compressed, (char*)buffer, compressedSize, maxBufferSize);
|
2019-11-23 20:27:39 +08:00
|
|
|
return *bufferSize < 0 ? DT_FAILURE : DT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
dtStatus cocos2d::LZ4Compressor::compress(const unsigned char* buffer,
|
|
|
|
const int bufferSize,
|
|
|
|
unsigned char* compressed,
|
|
|
|
const int maxCompressedSize,
|
|
|
|
int* compressedSize)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
*compressedSize = LZ4_compress_default((const char*)buffer, (char*)compressed, bufferSize, maxCompressedSize);
|
2019-11-23 20:27:39 +08:00
|
|
|
return DT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
MeshProcess::MeshProcess(const GeomData* geom) : data(geom) {}
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
MeshProcess::~MeshProcess() {}
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
void MeshProcess::process(struct dtNavMeshCreateParams* params, unsigned char* polyAreas, unsigned short* polyFlags)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
// Update poly flags from areas.
|
|
|
|
for (int i = 0; i < params->polyCount; ++i)
|
|
|
|
{
|
|
|
|
if (polyAreas[i] == DT_TILECACHE_WALKABLE_AREA)
|
|
|
|
polyAreas[i] = 0;
|
|
|
|
|
|
|
|
if (polyAreas[i] == 0)
|
|
|
|
polyFlags[i] = 1;
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
// if (polyAreas[i] == SAMPLE_POLYAREA_GROUND ||
|
2019-11-23 20:27:39 +08:00
|
|
|
// polyAreas[i] == SAMPLE_POLYAREA_GRASS ||
|
|
|
|
// polyAreas[i] == SAMPLE_POLYAREA_ROAD)
|
|
|
|
//{
|
|
|
|
// polyFlags[i] = SAMPLE_POLYFLAGS_WALK;
|
2021-12-25 10:04:45 +08:00
|
|
|
// }
|
|
|
|
// else if (polyAreas[i] == SAMPLE_POLYAREA_WATER)
|
2019-11-23 20:27:39 +08:00
|
|
|
//{
|
|
|
|
// polyFlags[i] = SAMPLE_POLYFLAGS_SWIM;
|
2021-12-25 10:04:45 +08:00
|
|
|
// }
|
|
|
|
// else if (polyAreas[i] == SAMPLE_POLYAREA_DOOR)
|
2019-11-23 20:27:39 +08:00
|
|
|
//{
|
|
|
|
// polyFlags[i] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR;
|
2021-12-25 10:04:45 +08:00
|
|
|
// }
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pass in off-mesh connections.
|
2021-12-25 10:04:45 +08:00
|
|
|
params->offMeshConVerts = data->offMeshConVerts;
|
|
|
|
params->offMeshConRad = data->offMeshConRads;
|
|
|
|
params->offMeshConDir = data->offMeshConDirs;
|
|
|
|
params->offMeshConAreas = data->offMeshConAreas;
|
|
|
|
params->offMeshConFlags = data->offMeshConFlags;
|
2019-11-23 20:27:39 +08:00
|
|
|
params->offMeshConUserID = data->offMeshConId;
|
2021-12-25 10:04:45 +08:00
|
|
|
params->offMeshConCount = data->offMeshConCount;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
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*/)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
// 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;
|
2021-12-25 10:04:45 +08:00
|
|
|
navQuery->findStraightPath(startPos, endPos, path, pathSize, steerPath, steerPathFlags, steerPathPolys, &nsteerPath,
|
|
|
|
MAX_STEER_POINTS);
|
2019-11-23 20:27:39 +08:00
|
|
|
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]);
|
2021-12-25 10:04:45 +08:00
|
|
|
steerPos[1] = startPos[1];
|
2019-11-23 20:27:39 +08:00
|
|
|
steerPosFlag = steerPathFlags[ns];
|
2021-12-25 10:04:45 +08:00
|
|
|
steerPosRef = steerPathPolys[ns];
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = nullptr;
|
2021-12-25 10:04:45 +08:00
|
|
|
const dtPoly* poly = nullptr;
|
2019-11-23 20:27:39 +08:00
|
|
|
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;
|
2021-12-25 10:04:45 +08:00
|
|
|
int cut = 0;
|
|
|
|
for (int i = dtMin(maxLookAhead, npath) - 1; i > 1 && cut == 0; i--)
|
|
|
|
{
|
2019-11-23 20:27:39 +08:00
|
|
|
for (int j = 0; j < nneis; j++)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
if (path[i] == neis[j])
|
|
|
|
{
|
2019-11-23 20:27:39 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fixupCorridor(dtPolyRef* path, const int npath, const int maxPath, const dtPolyRef* visited, const int nvisited)
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
int furthestPath = -1;
|
2019-11-23 20:27:39 +08:00
|
|
|
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])
|
|
|
|
{
|
2021-12-25 10:04:45 +08:00
|
|
|
furthestPath = i;
|
2019-11-23 20:27:39 +08:00
|
|
|
furthestVisited = j;
|
2021-12-25 10:04:45 +08:00
|
|
|
found = true;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
// If no intersection found just return current path.
|
2019-11-23 20:27:39 +08:00
|
|
|
if (furthestPath == -1 || furthestVisited == -1)
|
|
|
|
return npath;
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
// Concatenate paths.
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
// Adjust beginning of the buffer to include the visited.
|
2021-12-25 10:04:45 +08:00
|
|
|
const int req = nvisited - furthestVisited;
|
2019-11-23 20:27:39 +08:00
|
|
|
const int orig = dtMin(furthestPath + 1, npath);
|
2021-12-25 10:04:45 +08:00
|
|
|
int size = dtMax(0, npath - orig);
|
2019-11-23 20:27:39 +08:00
|
|
|
if (req + size > maxPath)
|
|
|
|
size = maxPath - req;
|
|
|
|
if (size)
|
2021-12-25 10:04:45 +08:00
|
|
|
memmove(path + req, path + orig, size * sizeof(dtPolyRef));
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
// Store visited
|
|
|
|
for (int i = 0; i < req; ++i)
|
|
|
|
path[i] = visited[(nvisited - 1) - i];
|
|
|
|
|
|
|
|
return req + size;
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
2021-12-25 10:04:45 +08:00
|
|
|
return (dx * dx + dz * dz) < r * r && fabsf(dy) < h;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_CC_END
|
|
|
|
|
2021-12-25 10:04:45 +08:00
|
|
|
#endif // CC_USE_NAVMESH
|