void dtNavMesh::queryTiles(const float bmin[3], const float bmax[3], dtTileCallback callback, void * userdata) const { const int tx0 = (int)dtMathFloorf((bmin[0]-m_params.orig[0]) / m_tileWidth); const int tx1 = (int)dtMathFloorf((bmax[0]-m_params.orig[0]) / m_tileWidth); const int ty0 = (int)dtMathFloorf((bmin[2]-m_params.orig[2]) / m_tileHeight); const int ty1 = (int)dtMathFloorf((bmax[2]-m_params.orig[2]) / m_tileHeight); for (int ty = ty0; ty <= ty1; ++ty) { for (int tx = tx0; tx <= tx1; ++tx) { enum { MaxTiles = 32 }; const dtMeshTile * tmpTiles[ MaxTiles ]; const int ntiles = getTilesAt(tx,ty,tmpTiles,MaxTiles); for (int i = 0; i < ntiles; ++i) { if (dtOverlapBounds(bmin,bmax, tmpTiles[ i ]->header->bmin, tmpTiles[ i ]->header->bmax)) { callback( tmpTiles[i], userdata ); } } } } }
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; }
int dtNavMesh::getNeighbourTilesAt(const int x, const int y, const int side, dtMeshTile** tiles, const int maxTiles) const { int nx = x, ny = y; switch (side) { case 0: nx++; break; case 1: nx++; ny++; break; case 2: ny++; break; case 3: nx--; ny++; break; case 4: nx--; break; case 5: nx--; ny--; break; case 6: ny--; break; case 7: nx++; ny--; break; }; return getTilesAt(nx, ny, tiles, maxTiles); }
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)floorf((bmin[0]-m_params.orig[0]) / tw); const int tx1 = (int)floorf((bmax[0]-m_params.orig[0]) / tw); const int ty0 = (int)floorf((bmin[2]-m_params.orig[2]) / th); const int ty1 = (int)floorf((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; }
/// @par /// /// The add operation will fail if the data is in the wrong format, the allocated tile /// space is full, or there is a tile already at the specified reference. /// /// The lastRef parameter is used to restore a tile with the same tile /// reference it had previously used. In this case the #dtPolyRef's for the /// tile will be restored to the same values they were before the tile was /// removed. /// /// @see dtCreateNavMeshData, #removeTile dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result) { // Make sure the data is in right format. dtMeshHeader* header = (dtMeshHeader*)data; if (header->magic != DT_NAVMESH_MAGIC) return DT_FAILURE | DT_WRONG_MAGIC; if (header->version != DT_NAVMESH_VERSION) return DT_FAILURE | DT_WRONG_VERSION; // Make sure the location is free. if (getTileAt(header->x, header->y, header->layer)) return DT_FAILURE; // Allocate a tile. dtMeshTile* tile = 0; if (!lastRef) { if (m_nextFree) { tile = m_nextFree; m_nextFree = tile->next; tile->next = 0; } } else { // Try to relocate the tile to specific index with same salt. int tileIndex = (int)decodePolyIdTile((dtPolyRef)lastRef); if (tileIndex >= m_maxTiles) return DT_FAILURE | DT_OUT_OF_MEMORY; // Try to find the specific tile id from the free list. dtMeshTile* target = &m_tiles[tileIndex]; dtMeshTile* prev = 0; tile = m_nextFree; while (tile && tile != target) { prev = tile; tile = tile->next; } // Could not find the correct location. if (tile != target) return DT_FAILURE | DT_OUT_OF_MEMORY; // Remove from freelist if (!prev) m_nextFree = tile->next; else prev->next = tile->next; // Restore salt. tile->salt = decodePolyIdSalt((dtPolyRef)lastRef); } // 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->x, header->y, m_tileLutMask); tile->next = m_posLookup[h]; m_posLookup[h] = tile; // Patch header pointers. const int headerSize = dtAlign4(sizeof(dtMeshHeader)); const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount); const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount); const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount)); const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount); const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount); const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount); const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount); const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount); unsigned char* d = data + headerSize; tile->verts = (float*)d; d += vertsSize; tile->polys = (dtPoly*)d; d += polysSize; tile->links = (dtLink*)d; d += linksSize; tile->detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize; tile->detailVerts = (float*)d; d += detailVertsSize; tile->detailTris = (unsigned char*)d; d += detailTrisSize; tile->bvTree = (dtBVNode*)d; d += bvtreeSize; tile->offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize; // If there are no items in the bvtree, reset the tree pointer. if (!bvtreeSize) tile->bvTree = 0; // Build links freelist tile->linksFreeList = 0; tile->links[header->maxLinkCount-1].next = DT_NULL_LINK; for (int i = 0; i < header->maxLinkCount-1; ++i) tile->links[i].next = i+1; // Init tile. tile->header = header; tile->data = data; tile->dataSize = dataSize; tile->flags = flags; connectIntLinks(tile); baseOffMeshLinks(tile); // Create connections with neighbour tiles. static const int MAX_NEIS = 32; dtMeshTile* neis[MAX_NEIS]; int nneis; // Connect with layers in current tile. nneis = getTilesAt(header->x, header->y, neis, MAX_NEIS); for (int j = 0; j < nneis; ++j) { if (neis[j] != tile) { connectExtLinks(tile, neis[j], -1); connectExtLinks(neis[j], tile, -1); } connectExtOffMeshLinks(tile, neis[j], -1); connectExtOffMeshLinks(neis[j], tile, -1); } // Connect with neighbour tiles. for (int i = 0; i < 8; ++i) { nneis = getNeighbourTilesAt(header->x, header->y, i, neis, MAX_NEIS); for (int j = 0; j < nneis; ++j) { connectExtLinks(tile, neis[j], i); connectExtLinks(neis[j], tile, dtOppositeTile(i)); connectExtOffMeshLinks(tile, neis[j], i); connectExtOffMeshLinks(neis[j], tile, dtOppositeTile(i)); } } if (result) *result = getTileRef(tile); return DT_SUCCESS; }
/// @par /// /// This function returns the data for the tile so that, if desired, /// it can be added back to the navigation mesh at a later point. /// /// @see #addTile dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSize) { if (!ref) return DT_FAILURE | DT_INVALID_PARAM; unsigned int tileIndex = decodePolyIdTile((dtPolyRef)ref); unsigned int tileSalt = decodePolyIdSalt((dtPolyRef)ref); if ((int)tileIndex >= m_maxTiles) return DT_FAILURE | DT_INVALID_PARAM; dtMeshTile* tile = &m_tiles[tileIndex]; if (tile->salt != tileSalt) return DT_FAILURE | DT_INVALID_PARAM; // Remove tile from hash lookup. int h = computeTileHash(tile->header->x,tile->header->y,m_tileLutMask); dtMeshTile* prev = 0; dtMeshTile* 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; } // Remove connections to neighbour tiles. // Create connections with neighbour tiles. static const int MAX_NEIS = 32; dtMeshTile* neis[MAX_NEIS]; int nneis; // Connect with layers in current tile. nneis = getTilesAt(tile->header->x, tile->header->y, neis, MAX_NEIS); for (int j = 0; j < nneis; ++j) { if (neis[j] == tile) continue; unconnectExtLinks(neis[j], tile); } // Connect with neighbour tiles. for (int i = 0; i < 8; ++i) { nneis = getNeighbourTilesAt(tile->header->x, tile->header->y, i, neis, MAX_NEIS); for (int j = 0; j < nneis; ++j) unconnectExtLinks(neis[j], tile); } // Reset tile. if (tile->flags & DT_TILE_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->flags = 0; tile->linksFreeList = 0; tile->polys = 0; tile->verts = 0; tile->links = 0; tile->detailMeshes = 0; tile->detailVerts = 0; tile->detailTris = 0; tile->bvTree = 0; tile->offMeshCons = 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_nextFree; m_nextFree = tile; return DT_SUCCESS; }