mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Everything 'dtAlloc', 'rcAlloc', 'dtAlloc', 'dtFree', got renamed into 'rdAlloc', 'rdFree'. There were a lot of object allocators that used these suffixes which were not accounted for causing those to be renamed. Everything has been renamed back to their original names (excluding the actual rdAlloc/rdFree functions as these were supposed to be renamed). No code logic was changed in this commit. The accidental renaming was caused in commit fa8d89d287752782ebdd5d9563f04fa72ef0bee9
818 lines
21 KiB
C++
818 lines
21 KiB
C++
#include "DetourTileCache/Include/DetourTileCache.h"
|
|
#include "DetourTileCache/Include/DetourTileCacheBuilder.h"
|
|
#include "Detour/Include/DetourNavMeshBuilder.h"
|
|
#include "Detour/Include/DetourNavMesh.h"
|
|
#include "Detour/Include/DetourCommon.h"
|
|
#include "Detour/Include/DetourMath.h"
|
|
#include "Shared/Include/SharedAlloc.h"
|
|
#include "Shared/Include/SharedAssert.h"
|
|
#include <string.h>
|
|
#include <new>
|
|
|
|
dtTileCache* dtAllocTileCache()
|
|
{
|
|
void* mem = rdAlloc(sizeof(dtTileCache), RD_ALLOC_PERM);
|
|
if (!mem) return 0;
|
|
return new(mem) dtTileCache;
|
|
}
|
|
|
|
void dtFreeTileCache(dtTileCache* tc)
|
|
{
|
|
if (!tc) return;
|
|
tc->~dtTileCache();
|
|
rdFree(tc);
|
|
}
|
|
|
|
static bool contains(const dtCompressedTileRef* a, const int n, const dtCompressedTileRef v)
|
|
{
|
|
for (int i = 0; i < n; ++i)
|
|
if (a[i] == v)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
inline int computeTileHash(int x, int y, const int mask)
|
|
{
|
|
const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
|
|
const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
|
|
unsigned int n = h1 * x + h2 * y;
|
|
return (int)(n & mask);
|
|
}
|
|
|
|
|
|
struct NavMeshTileBuildContext
|
|
{
|
|
inline NavMeshTileBuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {}
|
|
inline ~NavMeshTileBuildContext() { purge(); }
|
|
void purge()
|
|
{
|
|
dtFreeTileCacheLayer(alloc, layer);
|
|
layer = 0;
|
|
dtFreeTileCacheContourSet(alloc, lcset);
|
|
lcset = 0;
|
|
dtFreeTileCachePolyMesh(alloc, lmesh);
|
|
lmesh = 0;
|
|
}
|
|
struct dtTileCacheLayer* layer;
|
|
struct dtTileCacheContourSet* lcset;
|
|
struct dtTileCachePolyMesh* lmesh;
|
|
struct dtTileCacheAlloc* alloc;
|
|
};
|
|
|
|
|
|
dtTileCache::dtTileCache() :
|
|
m_tileLutSize(0),
|
|
m_tileLutMask(0),
|
|
m_posLookup(0),
|
|
m_nextFreeTile(0),
|
|
m_tiles(0),
|
|
m_saltBits(0),
|
|
m_tileBits(0),
|
|
m_talloc(0),
|
|
m_tcomp(0),
|
|
m_tmproc(0),
|
|
m_obstacles(0),
|
|
m_nextFreeObstacle(0),
|
|
m_nreqs(0),
|
|
m_nupdate(0)
|
|
{
|
|
memset(&m_params, 0, sizeof(m_params));
|
|
memset(m_reqs, 0, sizeof(ObstacleRequest) * MAX_REQUESTS);
|
|
}
|
|
|
|
dtTileCache::~dtTileCache()
|
|
{
|
|
for (int i = 0; i < m_params.maxTiles; ++i)
|
|
{
|
|
if (m_tiles[i].flags & DT_COMPRESSEDTILE_FREE_DATA)
|
|
{
|
|
rdFree(m_tiles[i].data);
|
|
m_tiles[i].data = 0;
|
|
}
|
|
}
|
|
rdFree(m_obstacles);
|
|
m_obstacles = 0;
|
|
rdFree(m_posLookup);
|
|
m_posLookup = 0;
|
|
rdFree(m_tiles);
|
|
m_tiles = 0;
|
|
m_nreqs = 0;
|
|
m_nupdate = 0;
|
|
}
|
|
|
|
const dtCompressedTile* dtTileCache::getTileByRef(dtCompressedTileRef ref) const
|
|
{
|
|
if (!ref)
|
|
return 0;
|
|
unsigned int tileIndex = decodeTileIdTile(ref);
|
|
unsigned int tileSalt = decodeTileIdSalt(ref);
|
|
if ((int)tileIndex >= m_params.maxTiles)
|
|
return 0;
|
|
const dtCompressedTile* tile = &m_tiles[tileIndex];
|
|
if (tile->salt != tileSalt)
|
|
return 0;
|
|
return tile;
|
|
}
|
|
|
|
|
|
dtStatus dtTileCache::init(const dtTileCacheParams* params,
|
|
dtTileCacheAlloc* talloc,
|
|
dtTileCacheCompressor* tcomp,
|
|
dtTileCacheMeshProcess* tmproc)
|
|
{
|
|
m_talloc = talloc;
|
|
m_tcomp = tcomp;
|
|
m_tmproc = tmproc;
|
|
m_nreqs = 0;
|
|
memcpy(&m_params, params, sizeof(m_params));
|
|
|
|
// Alloc space for obstacles.
|
|
m_obstacles = (dtTileCacheObstacle*)rdAlloc(sizeof(dtTileCacheObstacle)*m_params.maxObstacles, RD_ALLOC_PERM);
|
|
if (!m_obstacles)
|
|
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
memset(m_obstacles, 0, sizeof(dtTileCacheObstacle)*m_params.maxObstacles);
|
|
m_nextFreeObstacle = 0;
|
|
for (int i = m_params.maxObstacles-1; i >= 0; --i)
|
|
{
|
|
m_obstacles[i].salt = 1;
|
|
m_obstacles[i].next = m_nextFreeObstacle;
|
|
m_nextFreeObstacle = &m_obstacles[i];
|
|
}
|
|
|
|
// Init tiles
|
|
m_tileLutSize = dtNextPow2(m_params.maxTiles/4);
|
|
if (!m_tileLutSize) m_tileLutSize = 1;
|
|
m_tileLutMask = m_tileLutSize-1;
|
|
|
|
m_tiles = (dtCompressedTile*)rdAlloc(sizeof(dtCompressedTile)*m_params.maxTiles, RD_ALLOC_PERM);
|
|
if (!m_tiles)
|
|
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
m_posLookup = (dtCompressedTile**)rdAlloc(sizeof(dtCompressedTile*)*m_tileLutSize, RD_ALLOC_PERM);
|
|
if (!m_posLookup)
|
|
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
memset(m_tiles, 0, sizeof(dtCompressedTile)*m_params.maxTiles);
|
|
memset(m_posLookup, 0, sizeof(dtCompressedTile*)*m_tileLutSize);
|
|
m_nextFreeTile = 0;
|
|
for (int i = m_params.maxTiles-1; i >= 0; --i)
|
|
{
|
|
m_tiles[i].salt = 1;
|
|
m_tiles[i].next = m_nextFreeTile;
|
|
m_nextFreeTile = &m_tiles[i];
|
|
}
|
|
|
|
// Init ID generator values.
|
|
m_tileBits = dtIlog2(dtNextPow2((unsigned int)m_params.maxTiles));
|
|
m_saltBits = 32 - m_tileBits;
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
int dtTileCache::getTilesAt(const int tx, const int ty, dtCompressedTileRef* tiles, const int maxTiles) const
|
|
{
|
|
int n = 0;
|
|
|
|
// Find tile based on hash.
|
|
int h = computeTileHash(tx,ty,m_tileLutMask);
|
|
dtCompressedTile* tile = m_posLookup[h];
|
|
while (tile)
|
|
{
|
|
if (tile->header &&
|
|
tile->header->tx == tx &&
|
|
tile->header->ty == ty)
|
|
{
|
|
if (n < maxTiles)
|
|
tiles[n++] = getTileRef(tile);
|
|
}
|
|
tile = tile->next;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
dtCompressedTile* dtTileCache::getTileAt(const int tx, const int ty, const int tlayer)
|
|
{
|
|
// Find tile based on hash.
|
|
int h = computeTileHash(tx,ty,m_tileLutMask);
|
|
dtCompressedTile* tile = m_posLookup[h];
|
|
while (tile)
|
|
{
|
|
if (tile->header &&
|
|
tile->header->tx == tx &&
|
|
tile->header->ty == ty &&
|
|
tile->header->tlayer == tlayer)
|
|
{
|
|
return tile;
|
|
}
|
|
tile = tile->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
dtCompressedTileRef dtTileCache::getTileRef(const dtCompressedTile* tile) const
|
|
{
|
|
if (!tile) return 0;
|
|
const unsigned int it = (unsigned int)(tile - m_tiles);
|
|
return (dtCompressedTileRef)encodeTileId(tile->salt, it);
|
|
}
|
|
|
|
dtObstacleRef dtTileCache::getObstacleRef(const dtTileCacheObstacle* ob) const
|
|
{
|
|
if (!ob) return 0;
|
|
const unsigned int idx = (unsigned int)(ob - m_obstacles);
|
|
return encodeObstacleId(ob->salt, idx);
|
|
}
|
|
|
|
const dtTileCacheObstacle* dtTileCache::getObstacleByRef(dtObstacleRef ref)
|
|
{
|
|
if (!ref)
|
|
return 0;
|
|
unsigned int idx = decodeObstacleIdObstacle(ref);
|
|
if ((int)idx >= m_params.maxObstacles)
|
|
return 0;
|
|
const dtTileCacheObstacle* ob = &m_obstacles[idx];
|
|
unsigned int salt = decodeObstacleIdSalt(ref);
|
|
if (ob->salt != salt)
|
|
return 0;
|
|
return ob;
|
|
}
|
|
|
|
dtStatus dtTileCache::addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result)
|
|
{
|
|
// Make sure the data is in right format.
|
|
dtTileCacheLayerHeader* header = (dtTileCacheLayerHeader*)data;
|
|
if (header->magic != DT_TILECACHE_MAGIC)
|
|
return DT_FAILURE | DT_WRONG_MAGIC;
|
|
if (header->version != DT_TILECACHE_VERSION)
|
|
return DT_FAILURE | DT_WRONG_VERSION;
|
|
|
|
// Make sure the location is free.
|
|
if (getTileAt(header->tx, header->ty, header->tlayer))
|
|
return DT_FAILURE;
|
|
|
|
// Allocate a tile.
|
|
dtCompressedTile* tile = 0;
|
|
if (m_nextFreeTile)
|
|
{
|
|
tile = m_nextFreeTile;
|
|
m_nextFreeTile = tile->next;
|
|
tile->next = 0;
|
|
}
|
|
|
|
// Make sure we could allocate a tile.
|
|
if (!tile)
|
|
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
|
|
// Insert tile into the position lut.
|
|
int h = computeTileHash(header->tx, header->ty, m_tileLutMask);
|
|
tile->next = m_posLookup[h];
|
|
m_posLookup[h] = tile;
|
|
|
|
// Init tile.
|
|
const int headerSize = dtAlign4(sizeof(dtTileCacheLayerHeader));
|
|
tile->header = (dtTileCacheLayerHeader*)data;
|
|
tile->data = data;
|
|
tile->dataSize = dataSize;
|
|
tile->compressed = tile->data + headerSize;
|
|
tile->compressedSize = tile->dataSize - headerSize;
|
|
tile->flags = flags;
|
|
|
|
if (result)
|
|
*result = getTileRef(tile);
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
dtStatus dtTileCache::removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize)
|
|
{
|
|
if (!ref)
|
|
return DT_FAILURE | DT_INVALID_PARAM;
|
|
unsigned int tileIndex = decodeTileIdTile(ref);
|
|
unsigned int tileSalt = decodeTileIdSalt(ref);
|
|
if ((int)tileIndex >= m_params.maxTiles)
|
|
return DT_FAILURE | DT_INVALID_PARAM;
|
|
dtCompressedTile* tile = &m_tiles[tileIndex];
|
|
if (tile->salt != tileSalt)
|
|
return DT_FAILURE | DT_INVALID_PARAM;
|
|
|
|
// Remove tile from hash lookup.
|
|
const int h = computeTileHash(tile->header->tx,tile->header->ty,m_tileLutMask);
|
|
dtCompressedTile* prev = 0;
|
|
dtCompressedTile* cur = m_posLookup[h];
|
|
while (cur)
|
|
{
|
|
if (cur == tile)
|
|
{
|
|
if (prev)
|
|
prev->next = cur->next;
|
|
else
|
|
m_posLookup[h] = cur->next;
|
|
break;
|
|
}
|
|
prev = cur;
|
|
cur = cur->next;
|
|
}
|
|
|
|
// Reset tile.
|
|
if (tile->flags & DT_COMPRESSEDTILE_FREE_DATA)
|
|
{
|
|
// Owns data
|
|
rdFree(tile->data);
|
|
tile->data = 0;
|
|
tile->dataSize = 0;
|
|
if (data) *data = 0;
|
|
if (dataSize) *dataSize = 0;
|
|
}
|
|
else
|
|
{
|
|
if (data) *data = tile->data;
|
|
if (dataSize) *dataSize = tile->dataSize;
|
|
}
|
|
|
|
tile->header = 0;
|
|
tile->data = 0;
|
|
tile->dataSize = 0;
|
|
tile->compressed = 0;
|
|
tile->compressedSize = 0;
|
|
tile->flags = 0;
|
|
|
|
// Update salt, salt should never be zero.
|
|
tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1);
|
|
if (tile->salt == 0)
|
|
tile->salt++;
|
|
|
|
// Add to free list.
|
|
tile->next = m_nextFreeTile;
|
|
m_nextFreeTile = tile;
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
|
|
dtStatus dtTileCache::addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result)
|
|
{
|
|
if (m_nreqs >= MAX_REQUESTS)
|
|
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
|
|
|
dtTileCacheObstacle* ob = 0;
|
|
if (m_nextFreeObstacle)
|
|
{
|
|
ob = m_nextFreeObstacle;
|
|
m_nextFreeObstacle = ob->next;
|
|
ob->next = 0;
|
|
}
|
|
if (!ob)
|
|
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
|
|
unsigned short salt = ob->salt;
|
|
memset(ob, 0, sizeof(dtTileCacheObstacle));
|
|
ob->salt = salt;
|
|
ob->state = DT_OBSTACLE_PROCESSING;
|
|
ob->type = DT_OBSTACLE_CYLINDER;
|
|
dtVcopy(ob->cylinder.pos, pos);
|
|
ob->cylinder.radius = radius;
|
|
ob->cylinder.height = height;
|
|
|
|
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
|
memset(req, 0, sizeof(ObstacleRequest));
|
|
req->action = REQUEST_ADD;
|
|
req->ref = getObstacleRef(ob);
|
|
|
|
if (result)
|
|
*result = req->ref;
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result)
|
|
{
|
|
if (m_nreqs >= MAX_REQUESTS)
|
|
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
|
|
|
dtTileCacheObstacle* ob = 0;
|
|
if (m_nextFreeObstacle)
|
|
{
|
|
ob = m_nextFreeObstacle;
|
|
m_nextFreeObstacle = ob->next;
|
|
ob->next = 0;
|
|
}
|
|
if (!ob)
|
|
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
|
|
unsigned short salt = ob->salt;
|
|
memset(ob, 0, sizeof(dtTileCacheObstacle));
|
|
ob->salt = salt;
|
|
ob->state = DT_OBSTACLE_PROCESSING;
|
|
ob->type = DT_OBSTACLE_BOX;
|
|
dtVcopy(ob->box.bmin, bmin);
|
|
dtVcopy(ob->box.bmax, bmax);
|
|
|
|
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
|
memset(req, 0, sizeof(ObstacleRequest));
|
|
req->action = REQUEST_ADD;
|
|
req->ref = getObstacleRef(ob);
|
|
|
|
if (result)
|
|
*result = req->ref;
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
dtStatus dtTileCache::addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result)
|
|
{
|
|
if (m_nreqs >= MAX_REQUESTS)
|
|
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
|
|
|
dtTileCacheObstacle* ob = 0;
|
|
if (m_nextFreeObstacle)
|
|
{
|
|
ob = m_nextFreeObstacle;
|
|
m_nextFreeObstacle = ob->next;
|
|
ob->next = 0;
|
|
}
|
|
if (!ob)
|
|
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
|
|
unsigned short salt = ob->salt;
|
|
memset(ob, 0, sizeof(dtTileCacheObstacle));
|
|
ob->salt = salt;
|
|
ob->state = DT_OBSTACLE_PROCESSING;
|
|
ob->type = DT_OBSTACLE_ORIENTED_BOX;
|
|
dtVcopy(ob->orientedBox.center, center);
|
|
dtVcopy(ob->orientedBox.halfExtents, halfExtents);
|
|
|
|
float coshalf= cosf(0.5f*yRadians);
|
|
float sinhalf = sinf(-0.5f*yRadians);
|
|
ob->orientedBox.rotAux[0] = coshalf*sinhalf;
|
|
ob->orientedBox.rotAux[1] = coshalf*coshalf - 0.5f;
|
|
|
|
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
|
memset(req, 0, sizeof(ObstacleRequest));
|
|
req->action = REQUEST_ADD;
|
|
req->ref = getObstacleRef(ob);
|
|
|
|
if (result)
|
|
*result = req->ref;
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
dtStatus dtTileCache::removeObstacle(const dtObstacleRef ref)
|
|
{
|
|
if (!ref)
|
|
return DT_SUCCESS;
|
|
if (m_nreqs >= MAX_REQUESTS)
|
|
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
|
|
|
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
|
memset(req, 0, sizeof(ObstacleRequest));
|
|
req->action = REQUEST_REMOVE;
|
|
req->ref = ref;
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
dtStatus dtTileCache::queryTiles(const float* bmin, const float* bmax,
|
|
dtCompressedTileRef* results, int* resultCount, const int maxResults) const
|
|
{
|
|
const int MAX_TILES = 32;
|
|
dtCompressedTileRef tiles[MAX_TILES];
|
|
|
|
int n = 0;
|
|
|
|
const float tw = m_params.width * m_params.cs;
|
|
const float th = m_params.height * m_params.cs;
|
|
const int tx0 = (int)dtMathFloorf((bmin[0]-m_params.orig[0]) / tw);
|
|
const int tx1 = (int)dtMathFloorf((bmax[0]-m_params.orig[0]) / tw);
|
|
const int ty0 = (int)dtMathFloorf((bmin[1]-m_params.orig[1]) / th);
|
|
const int ty1 = (int)dtMathFloorf((bmax[1]-m_params.orig[1]) / th);
|
|
|
|
for (int ty = ty0; ty <= ty1; ++ty)
|
|
{
|
|
for (int tx = tx0; tx <= tx1; ++tx)
|
|
{
|
|
const int ntiles = getTilesAt(tx,ty,tiles,MAX_TILES);
|
|
|
|
for (int i = 0; i < ntiles; ++i)
|
|
{
|
|
const dtCompressedTile* tile = &m_tiles[decodeTileIdTile(tiles[i])];
|
|
float tbmin[3], tbmax[3];
|
|
calcTightTileBounds(tile->header, tbmin, tbmax);
|
|
|
|
if (dtOverlapBounds(bmin,bmax, tbmin,tbmax))
|
|
{
|
|
if (n < maxResults)
|
|
results[n++] = tiles[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*resultCount = n;
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
|
|
bool* upToDate)
|
|
{
|
|
if (m_nupdate == 0)
|
|
{
|
|
// Process requests.
|
|
for (int i = 0; i < m_nreqs; ++i)
|
|
{
|
|
ObstacleRequest* req = &m_reqs[i];
|
|
|
|
unsigned int idx = decodeObstacleIdObstacle(req->ref);
|
|
if ((int)idx >= m_params.maxObstacles)
|
|
continue;
|
|
dtTileCacheObstacle* ob = &m_obstacles[idx];
|
|
unsigned int salt = decodeObstacleIdSalt(req->ref);
|
|
if (ob->salt != salt)
|
|
continue;
|
|
|
|
if (req->action == REQUEST_ADD)
|
|
{
|
|
// Find touched tiles.
|
|
float bmin[3], bmax[3];
|
|
getObstacleBounds(ob, bmin, bmax);
|
|
|
|
int ntouched = 0;
|
|
queryTiles(bmin, bmax, ob->touched, &ntouched, DT_MAX_TOUCHED_TILES);
|
|
ob->ntouched = (unsigned char)ntouched;
|
|
// Add tiles to update list.
|
|
ob->npending = 0;
|
|
for (int j = 0; j < ob->ntouched; ++j)
|
|
{
|
|
if (m_nupdate < MAX_UPDATE)
|
|
{
|
|
if (!contains(m_update, m_nupdate, ob->touched[j]))
|
|
m_update[m_nupdate++] = ob->touched[j];
|
|
ob->pending[ob->npending++] = ob->touched[j];
|
|
}
|
|
}
|
|
}
|
|
else if (req->action == REQUEST_REMOVE)
|
|
{
|
|
// Prepare to remove obstacle.
|
|
ob->state = DT_OBSTACLE_REMOVING;
|
|
// Add tiles to update list.
|
|
ob->npending = 0;
|
|
for (int j = 0; j < ob->ntouched; ++j)
|
|
{
|
|
if (m_nupdate < MAX_UPDATE)
|
|
{
|
|
if (!contains(m_update, m_nupdate, ob->touched[j]))
|
|
m_update[m_nupdate++] = ob->touched[j];
|
|
ob->pending[ob->npending++] = ob->touched[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_nreqs = 0;
|
|
}
|
|
|
|
dtStatus status = DT_SUCCESS;
|
|
// Process updates
|
|
if (m_nupdate)
|
|
{
|
|
// Build mesh
|
|
const dtCompressedTileRef ref = m_update[0];
|
|
status = buildNavMeshTile(ref, navmesh);
|
|
m_nupdate--;
|
|
if (m_nupdate > 0)
|
|
memmove(m_update, m_update+1, m_nupdate*sizeof(dtCompressedTileRef));
|
|
|
|
// Update obstacle states.
|
|
for (int i = 0; i < m_params.maxObstacles; ++i)
|
|
{
|
|
dtTileCacheObstacle* ob = &m_obstacles[i];
|
|
if (ob->state == DT_OBSTACLE_PROCESSING || ob->state == DT_OBSTACLE_REMOVING)
|
|
{
|
|
// Remove handled tile from pending list.
|
|
for (int j = 0; j < (int)ob->npending; j++)
|
|
{
|
|
if (ob->pending[j] == ref)
|
|
{
|
|
ob->pending[j] = ob->pending[(int)ob->npending-1];
|
|
ob->npending--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If all pending tiles processed, change state.
|
|
if (ob->npending == 0)
|
|
{
|
|
if (ob->state == DT_OBSTACLE_PROCESSING)
|
|
{
|
|
ob->state = DT_OBSTACLE_PROCESSED;
|
|
}
|
|
else if (ob->state == DT_OBSTACLE_REMOVING)
|
|
{
|
|
ob->state = DT_OBSTACLE_EMPTY;
|
|
// Update salt, salt should never be zero.
|
|
ob->salt = (ob->salt+1) & ((1<<16)-1);
|
|
if (ob->salt == 0)
|
|
ob->salt++;
|
|
// Return obstacle to free list.
|
|
ob->next = m_nextFreeObstacle;
|
|
m_nextFreeObstacle = ob;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (upToDate)
|
|
*upToDate = m_nupdate == 0 && m_nreqs == 0;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh)
|
|
{
|
|
const int MAX_TILES = 32;
|
|
dtCompressedTileRef tiles[MAX_TILES];
|
|
const int ntiles = getTilesAt(tx,ty,tiles,MAX_TILES);
|
|
|
|
for (int i = 0; i < ntiles; ++i)
|
|
{
|
|
dtStatus status = buildNavMeshTile(tiles[i], navmesh);
|
|
if (dtStatusFailed(status))
|
|
return status;
|
|
}
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh)
|
|
{
|
|
rdAssert(m_talloc);
|
|
rdAssert(m_tcomp);
|
|
|
|
unsigned int idx = decodeTileIdTile(ref);
|
|
if (idx > (unsigned int)m_params.maxTiles)
|
|
return DT_FAILURE | DT_INVALID_PARAM;
|
|
const dtCompressedTile* tile = &m_tiles[idx];
|
|
unsigned int salt = decodeTileIdSalt(ref);
|
|
if (tile->salt != salt)
|
|
return DT_FAILURE | DT_INVALID_PARAM;
|
|
|
|
m_talloc->reset();
|
|
|
|
NavMeshTileBuildContext bc(m_talloc);
|
|
const int walkableClimbVx = (int)(m_params.walkableClimb / m_params.ch);
|
|
dtStatus status;
|
|
|
|
// Decompress tile layer data.
|
|
status = dtDecompressTileCacheLayer(m_talloc, m_tcomp, tile->data, tile->dataSize, &bc.layer);
|
|
if (dtStatusFailed(status))
|
|
return status;
|
|
|
|
// Rasterize obstacles.
|
|
for (int i = 0; i < m_params.maxObstacles; ++i)
|
|
{
|
|
const dtTileCacheObstacle* ob = &m_obstacles[i];
|
|
if (ob->state == DT_OBSTACLE_EMPTY || ob->state == DT_OBSTACLE_REMOVING)
|
|
continue;
|
|
if (contains(ob->touched, ob->ntouched, ref))
|
|
{
|
|
if (ob->type == DT_OBSTACLE_CYLINDER)
|
|
{
|
|
dtMarkCylinderArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
|
|
ob->cylinder.pos, ob->cylinder.radius, ob->cylinder.height, 0);
|
|
}
|
|
else if (ob->type == DT_OBSTACLE_BOX)
|
|
{
|
|
dtMarkBoxArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
|
|
ob->box.bmin, ob->box.bmax, 0);
|
|
}
|
|
else if (ob->type == DT_OBSTACLE_ORIENTED_BOX)
|
|
{
|
|
dtMarkBoxArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
|
|
ob->orientedBox.center, ob->orientedBox.halfExtents, ob->orientedBox.rotAux, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build navmesh
|
|
status = dtBuildTileCacheRegions(m_talloc, *bc.layer, walkableClimbVx);
|
|
if (dtStatusFailed(status))
|
|
return status;
|
|
|
|
bc.lcset = dtAllocTileCacheContourSet(m_talloc);
|
|
if (!bc.lcset)
|
|
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
status = dtBuildTileCacheContours(m_talloc, *bc.layer, walkableClimbVx,
|
|
m_params.maxSimplificationError, *bc.lcset);
|
|
if (dtStatusFailed(status))
|
|
return status;
|
|
|
|
bc.lmesh = dtAllocTileCachePolyMesh(m_talloc);
|
|
if (!bc.lmesh)
|
|
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
status = dtBuildTileCachePolyMesh(m_talloc, *bc.lcset, *bc.lmesh);
|
|
if (dtStatusFailed(status))
|
|
return status;
|
|
|
|
// Early out if the mesh tile is empty.
|
|
if (!bc.lmesh->npolys)
|
|
{
|
|
// Remove existing tile.
|
|
navmesh->removeTile(navmesh->getTileRefAt(tile->header->tx,tile->header->ty,tile->header->tlayer),0,0);
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
dtNavMeshCreateParams params;
|
|
memset(¶ms, 0, sizeof(params));
|
|
params.verts = bc.lmesh->verts;
|
|
params.vertCount = bc.lmesh->nverts;
|
|
params.polys = bc.lmesh->polys;
|
|
params.polyAreas = bc.lmesh->areas;
|
|
params.polyFlags = bc.lmesh->flags;
|
|
params.polyCount = bc.lmesh->npolys;
|
|
params.nvp = DT_VERTS_PER_POLYGON;
|
|
params.walkableHeight = m_params.walkableHeight;
|
|
params.walkableRadius = m_params.walkableRadius;
|
|
params.walkableClimb = m_params.walkableClimb;
|
|
params.tileX = tile->header->tx;
|
|
params.tileY = tile->header->ty;
|
|
params.tileLayer = tile->header->tlayer;
|
|
params.cs = m_params.cs;
|
|
params.ch = m_params.ch;
|
|
params.buildBvTree = false;
|
|
dtVcopy(params.bmin, tile->header->bmin);
|
|
dtVcopy(params.bmax, tile->header->bmax);
|
|
|
|
if (m_tmproc)
|
|
{
|
|
m_tmproc->process(¶ms, bc.lmesh->areas, bc.lmesh->flags);
|
|
}
|
|
|
|
unsigned char* navData = 0;
|
|
int navDataSize = 0;
|
|
if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize))
|
|
return DT_FAILURE;
|
|
|
|
// Remove existing tile.
|
|
navmesh->removeTile(navmesh->getTileRefAt(tile->header->tx,tile->header->ty,tile->header->tlayer),0,0);
|
|
|
|
// Add new tile, or leave the location empty.
|
|
if (navData)
|
|
{
|
|
// Let the navmesh own the data.
|
|
status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0);
|
|
if (dtStatusFailed(status))
|
|
{
|
|
rdFree(navData);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
return DT_SUCCESS;
|
|
}
|
|
|
|
void dtTileCache::calcTightTileBounds(const dtTileCacheLayerHeader* header, float* bmin, float* bmax) const
|
|
{
|
|
const float cs = m_params.cs;
|
|
bmin[0] = header->bmin[0] + header->minx*cs;
|
|
bmin[1] = header->bmin[1] + header->miny*cs;
|
|
bmin[2] = header->bmin[2];
|
|
bmax[0] = header->bmin[0] + (header->maxx+1)*cs;
|
|
bmax[1] = header->bmax[1] + (header->maxy+1)*cs;
|
|
bmax[2] = header->bmin[2];
|
|
}
|
|
|
|
void dtTileCache::getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const
|
|
{
|
|
if (ob->type == DT_OBSTACLE_CYLINDER)
|
|
{
|
|
const dtObstacleCylinder &cl = ob->cylinder;
|
|
|
|
bmin[0] = cl.pos[0] - cl.radius;
|
|
bmin[1] = cl.pos[1] - cl.radius;
|
|
bmin[2] = cl.pos[2];
|
|
bmax[0] = cl.pos[0] + cl.radius;
|
|
bmax[1] = cl.pos[1] + cl.radius;
|
|
bmax[2] = cl.pos[2] + cl.height;
|
|
}
|
|
else if (ob->type == DT_OBSTACLE_BOX)
|
|
{
|
|
dtVcopy(bmin, ob->box.bmin);
|
|
dtVcopy(bmax, ob->box.bmax);
|
|
}
|
|
else if (ob->type == DT_OBSTACLE_ORIENTED_BOX)
|
|
{
|
|
const dtObstacleOrientedBox &orientedBox = ob->orientedBox;
|
|
|
|
float maxr = 1.41f*dtMax(orientedBox.halfExtents[0], orientedBox.halfExtents[1]);
|
|
bmin[0] = orientedBox.center[0] - maxr;
|
|
bmax[0] = orientedBox.center[0] + maxr;
|
|
bmin[1] = orientedBox.center[1] - maxr;
|
|
bmax[1] = orientedBox.center[1] + maxr;
|
|
bmin[2] = orientedBox.center[2] - orientedBox.halfExtents[2];
|
|
bmax[2] = orientedBox.center[2] + orientedBox.halfExtents[2];
|
|
}
|
|
}
|