mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 08:56:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			820 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			820 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "DetourTileCache.h"
 | 
						|
#include "DetourTileCacheBuilder.h"
 | 
						|
#include "DetourNavMeshBuilder.h"
 | 
						|
#include "DetourNavMesh.h"
 | 
						|
#include "DetourCommon.h"
 | 
						|
#include "DetourMath.h"
 | 
						|
#include "DetourAlloc.h"
 | 
						|
#include "DetourAssert.h"
 | 
						|
#include <string.h>
 | 
						|
#include <new>
 | 
						|
 | 
						|
dtTileCache* dtAllocTileCache()
 | 
						|
{
 | 
						|
	void* mem = dtAlloc(sizeof(dtTileCache), DT_ALLOC_PERM);
 | 
						|
	if (!mem) return 0;
 | 
						|
	return new(mem) dtTileCache;
 | 
						|
}
 | 
						|
 | 
						|
void dtFreeTileCache(dtTileCache* tc)
 | 
						|
{
 | 
						|
	if (!tc) return;
 | 
						|
	tc->~dtTileCache();
 | 
						|
	dtFree(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)
 | 
						|
		{
 | 
						|
			dtFree(m_tiles[i].data);
 | 
						|
			m_tiles[i].data = 0;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	dtFree(m_obstacles);
 | 
						|
	m_obstacles = 0;
 | 
						|
	dtFree(m_posLookup);
 | 
						|
	m_posLookup = 0;
 | 
						|
	dtFree(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*)dtAlloc(sizeof(dtTileCacheObstacle)*m_params.maxObstacles, DT_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*)dtAlloc(sizeof(dtCompressedTile)*m_params.maxTiles, DT_ALLOC_PERM);
 | 
						|
	if (!m_tiles)
 | 
						|
		return DT_FAILURE | DT_OUT_OF_MEMORY;
 | 
						|
	m_posLookup = (dtCompressedTile**)dtAlloc(sizeof(dtCompressedTile*)*m_tileLutSize, DT_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));
 | 
						|
	// Only allow 31 salt bits, since the salt mask is calculated using 32bit uint and it will overflow.
 | 
						|
	m_saltBits = dtMin((unsigned int)31, 32 - m_tileBits);
 | 
						|
	if (m_saltBits < 10)
 | 
						|
		return DT_FAILURE | DT_INVALID_PARAM;
 | 
						|
	
 | 
						|
	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
 | 
						|
		dtFree(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[2]-m_params.orig[2]) / th);
 | 
						|
	const int ty1 = (int)dtMathFloorf((bmax[2]-m_params.orig[2]) / 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)
 | 
						|
{	
 | 
						|
	dtAssert(m_talloc);
 | 
						|
	dtAssert(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))
 | 
						|
		{
 | 
						|
			dtFree(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];
 | 
						|
	bmin[2] = header->bmin[2] + header->miny*cs;
 | 
						|
	bmax[0] = header->bmin[0] + (header->maxx+1)*cs;
 | 
						|
	bmax[1] = header->bmax[1];
 | 
						|
	bmax[2] = header->bmin[2] + (header->maxy+1)*cs;
 | 
						|
}
 | 
						|
 | 
						|
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];
 | 
						|
		bmin[2] = cl.pos[2] - cl.radius;
 | 
						|
		bmax[0] = cl.pos[0] + cl.radius;
 | 
						|
		bmax[1] = cl.pos[1] + cl.height;
 | 
						|
		bmax[2] = cl.pos[2] + cl.radius;
 | 
						|
	}
 | 
						|
	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[2]);
 | 
						|
		bmin[0] = orientedBox.center[0] - maxr;
 | 
						|
		bmax[0] = orientedBox.center[0] + maxr;
 | 
						|
		bmin[1] = orientedBox.center[1] - orientedBox.halfExtents[1];
 | 
						|
		bmax[1] = orientedBox.center[1] + orientedBox.halfExtents[1];
 | 
						|
		bmin[2] = orientedBox.center[2] - maxr;
 | 
						|
		bmax[2] = orientedBox.center[2] + maxr;
 | 
						|
	}
 | 
						|
}
 |