/// @par /// /// Tile state includes non-structural data such as polygon flags, area ids, etc. /// @note The state data is only valid until the tile reference changes. /// @see #getTileStateSize, #restoreTileState dtStatus dtNavMesh::storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const { // Make sure there is enough space to store the state. const int sizeReq = getTileStateSize(tile); if (maxDataSize < sizeReq) return DT_FAILURE | DT_BUFFER_TOO_SMALL; dtTileState* tileState = (dtTileState*)data; data += dtAlign4(sizeof(dtTileState)); dtPolyState* polyStates = (dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount); // Store tile state. tileState->magic = DT_NAVMESH_STATE_MAGIC; tileState->version = DT_NAVMESH_STATE_VERSION; tileState->ref = getTileRef(tile); // Store per poly state. for (int i = 0; i < tile->header->polyCount; ++i) { const dtPoly* p = &tile->polys[i]; dtPolyState* s = &polyStates[i]; s->flags = p->flags; s->area = p->getArea(); } return DT_SUCCESS; }
/// @see #storeTileState int dtNavMesh::getTileStateSize(const dtMeshTile* tile) const { if (!tile) return 0; const int headerSize = dtAlign4(sizeof(dtTileState)); const int polyStateSize = dtAlign4(sizeof(dtPolyState) * tile->header->polyCount); return headerSize + polyStateSize; }
/// @par /// /// Tile state includes non-structural data such as polygon flags, area ids, etc. /// @note This function does not impact the tile's #dtTileRef and #dtPolyRef's. /// @see #storeTileState dtStatus dtNavMesh::restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize) { // Make sure there is enough space to store the state. const int sizeReq = getTileStateSize(tile); if (maxDataSize < sizeReq) return DT_FAILURE | DT_INVALID_PARAM; const dtTileState* tileState = (const dtTileState*)data; data += dtAlign4(sizeof(dtTileState)); const dtPolyState* polyStates = (const dtPolyState*)data; // data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount); // Check that the restore is possible. if (tileState->magic != DT_NAVMESH_STATE_MAGIC) return DT_FAILURE | DT_WRONG_MAGIC; if (tileState->version != DT_NAVMESH_STATE_VERSION) return DT_FAILURE | DT_WRONG_VERSION; if (tileState->ref != getTileRef(tile)) return DT_FAILURE | DT_INVALID_PARAM; // Restore per poly state. for (int i = 0; i < tile->header->polyCount; ++i) { dtPoly* p = &tile->polys[i]; const dtPolyState* s = &polyStates[i]; p->flags = s->flags; p->setArea(s->area); } return DT_SUCCESS; }
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 = TileCacheFunc::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; }
bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/) { // Make sure the data is in right format. dtMeshHeader* header = (dtMeshHeader*)data; if (header->magic != DT_NAVMESH_MAGIC) return false; if (header->version != DT_NAVMESH_VERSION) return false; // 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; float* verts = (float*)d; d += vertsSize; dtPoly* polys = (dtPoly*)d; d += polysSize; /*dtLink* links = (dtLink*)d;*/ d += linksSize; dtPolyDetail* detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize; float* detailVerts = (float*)d; d += detailVertsSize; /*unsigned char* detailTris = (unsigned char*)d;*/ d += detailTrisSize; dtBVNode* bvTree = (dtBVNode*)d; d += bvtreeSize; dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize; // Vertices for (int i = 0; i < header->vertCount*3; ++i) { swapEndian(&verts[i]); } // Polys for (int i = 0; i < header->polyCount; ++i) { dtPoly* p = &polys[i]; // poly->firstLink is update when tile is added, no need to swap. for (int j = 0; j < DT_VERTS_PER_POLYGON; ++j) { swapEndian(&p->verts[j]); swapEndian(&p->neis[j]); } swapEndian(&p->flags); } // Links are rebuild when tile is added, no need to swap. // Detail meshes for (int i = 0; i < header->detailMeshCount; ++i) { dtPolyDetail* pd = &detailMeshes[i]; swapEndian(&pd->vertBase); swapEndian(&pd->vertCount); swapEndian(&pd->triBase); swapEndian(&pd->triCount); } // Detail verts for (int i = 0; i < header->detailVertCount*3; ++i) { swapEndian(&detailVerts[i]); } // BV-tree for (int i = 0; i < header->bvNodeCount; ++i) { dtBVNode* node = &bvTree[i]; for (int j = 0; j < 3; ++j) { swapEndian(&node->bmin[j]); swapEndian(&node->bmax[j]); } swapEndian(&node->i); } // Off-mesh Connections. for (int i = 0; i < header->offMeshConCount; ++i) { dtOffMeshConnection* con = &offMeshCons[i]; for (int j = 0; j < 6; ++j) swapEndian(&con->pos[j]); swapEndian(&con->rad); swapEndian(&con->poly); } return true; }
bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize) { if (params->nvp > DT_VERTS_PER_POLYGON) return false; if (params->vertCount >= 0xffff) return false; if (!params->vertCount || !params->verts) return false; if (!params->polyCount || !params->polys) return false; if (!params->detailMeshes || !params->detailVerts || !params->detailTris) return false; const int nvp = params->nvp; // Classify off-mesh connection points. We store only the connections // whose start point is inside the tile. unsigned char* offMeshConClass = 0; int storedOffMeshConCount = 0; int offMeshConLinkCount = 0; if (params->offMeshConCount > 0) { offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP); if (!offMeshConClass) return false; for (int i = 0; i < params->offMeshConCount; ++i) { offMeshConClass[i*2+0] = classifyOffMeshPoint(¶ms->offMeshConVerts[(i*2+0)*3], params->bmin, params->bmax); offMeshConClass[i*2+1] = classifyOffMeshPoint(¶ms->offMeshConVerts[(i*2+1)*3], params->bmin, params->bmax); // Cound how many links should be allocated for off-mesh connections. if (offMeshConClass[i*2+0] == 0xff) offMeshConLinkCount++; if (offMeshConClass[i*2+1] == 0xff) offMeshConLinkCount++; if (offMeshConClass[i*2+0] == 0xff) storedOffMeshConCount++; } } // Off-mesh connectionss are stored as polygons, adjust values. const int totPolyCount = params->polyCount + storedOffMeshConCount; const int totVertCount = params->vertCount + storedOffMeshConCount*2; // Find portal edges which are at tile borders. int edgeCount = 0; int portalCount = 0; for (int i = 0; i < params->polyCount; ++i) { const unsigned short* p = ¶ms->polys[i*2*nvp]; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; int nj = j+1; if (nj >= nvp || p[nj] == MESH_NULL_IDX) nj = 0; const unsigned short* va = ¶ms->verts[p[j]*3]; const unsigned short* vb = ¶ms->verts[p[nj]*3]; edgeCount++; if (params->tileSize > 0) { if (va[0] == params->tileSize && vb[0] == params->tileSize) portalCount++; // x+ else if (va[2] == params->tileSize && vb[2] == params->tileSize) portalCount++; // z+ else if (va[0] == 0 && vb[0] == 0) portalCount++; // x- else if (va[2] == 0 && vb[2] == 0) portalCount++; // z- } } } const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2; // Find unique detail vertices. int uniqueDetailVertCount = 0; for (int i = 0; i < params->polyCount; ++i) { const unsigned short* p = ¶ms->polys[i*nvp*2]; int ndv = params->detailMeshes[i*4+1]; int nv = 0; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; nv++; } ndv -= nv; uniqueDetailVertCount += ndv; } // Calculate data size const int headerSize = dtAlign4(sizeof(dtMeshHeader)); const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount); const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount); const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount); const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount); const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount); const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*params->detailTriCount); const int bvTreeSize = dtAlign4(sizeof(dtBVNode)*params->polyCount*2); const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount); const int dataSize = headerSize + vertsSize + polysSize + linksSize + detailMeshesSize + detailVertsSize + detailTrisSize + bvTreeSize + offMeshConsSize; unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM); if (!data) { dtFree(offMeshConClass); return false; } memset(data, 0, dataSize); unsigned char* d = data; dtMeshHeader* header = (dtMeshHeader*)d; d += headerSize; float* navVerts = (float*)d; d += vertsSize; dtPoly* navPolys = (dtPoly*)d; d += polysSize; d += linksSize; dtPolyDetail* navDMeshes = (dtPolyDetail*)d; d += detailMeshesSize; float* navDVerts = (float*)d; d += detailVertsSize; unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize; dtBVNode* navBvtree = (dtBVNode*)d; d += bvTreeSize; dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshConsSize; // Store header header->magic = DT_NAVMESH_MAGIC; header->version = DT_NAVMESH_VERSION; header->x = params->tileX; header->y = params->tileY; header->userId = params->userId; header->polyCount = totPolyCount; header->vertCount = totVertCount; header->maxLinkCount = maxLinkCount; dtVcopy(header->bmin, params->bmin); dtVcopy(header->bmax, params->bmax); header->detailMeshCount = params->polyCount; header->detailVertCount = uniqueDetailVertCount; header->detailTriCount = params->detailTriCount; header->bvQuantFactor = 1.0f / params->cs; header->offMeshBase = params->polyCount; header->walkableHeight = params->walkableHeight; header->walkableRadius = params->walkableRadius; header->walkableClimb = params->walkableClimb; header->offMeshConCount = storedOffMeshConCount; header->bvNodeCount = params->polyCount*2; const int offMeshVertsBase = params->vertCount; const int offMeshPolyBase = params->polyCount; // Store vertices // Mesh vertices for (int i = 0; i < params->vertCount; ++i) { const unsigned short* iv = ¶ms->verts[i*3]; float* v = &navVerts[i*3]; v[0] = params->bmin[0] + iv[0] * params->cs; v[1] = params->bmin[1] + iv[1] * params->ch; v[2] = params->bmin[2] + iv[2] * params->cs; } // Off-mesh link vertices. int n = 0; for (int i = 0; i < params->offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i*2+0] == 0xff) { const float* linkv = ¶ms->offMeshConVerts[i*2*3]; float* v = &navVerts[(offMeshVertsBase + n*2)*3]; dtVcopy(&v[0], &linkv[0]); dtVcopy(&v[3], &linkv[3]); n++; } } // Store polygons // Mesh polys const unsigned short* src = params->polys; for (int i = 0; i < params->polyCount; ++i) { dtPoly* p = &navPolys[i]; p->vertCount = 0; p->flags = params->polyFlags[i]; p->area = params->polyAreas[i]; p->type = DT_POLYTYPE_GROUND; for (int j = 0; j < nvp; ++j) { if (src[j] == MESH_NULL_IDX) break; p->verts[j] = src[j]; p->neis[j] = (src[nvp+j]+1) & 0xffff; p->vertCount++; } src += nvp*2; } // Off-mesh connection vertices. n = 0; for (int i = 0; i < params->offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i*2+0] == 0xff) { dtPoly* p = &navPolys[offMeshPolyBase+n]; p->vertCount = 2; p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0); p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1); p->flags = params->offMeshConFlags[i]; p->area = params->offMeshConAreas[i]; p->type = DT_POLYTYPE_OFFMESH_CONNECTION; n++; } } // Store portal edges. if (params->tileSize > 0) { for (int i = 0; i < params->polyCount; ++i) { dtPoly* poly = &navPolys[i]; for (int j = 0; j < poly->vertCount; ++j) { int nj = j+1; if (nj >= poly->vertCount) nj = 0; const unsigned short* va = ¶ms->verts[poly->verts[j]*3]; const unsigned short* vb = ¶ms->verts[poly->verts[nj]*3]; if (va[0] == params->tileSize && vb[0] == params->tileSize) // x+ poly->neis[j] = DT_EXT_LINK | 0; else if (va[2] == params->tileSize && vb[2] == params->tileSize) // z+ poly->neis[j] = DT_EXT_LINK | 2; else if (va[0] == 0 && vb[0] == 0) // x- poly->neis[j] = DT_EXT_LINK | 4; else if (va[2] == 0 && vb[2] == 0) // z- poly->neis[j] = DT_EXT_LINK | 6; } } } // Store detail meshes and vertices. // The nav polygon vertices are stored as the first vertices on each mesh. // We compress the mesh data by skipping them and using the navmesh coordinates. unsigned short vbase = 0; for (int i = 0; i < params->polyCount; ++i) { dtPolyDetail& dtl = navDMeshes[i]; const int vb = params->detailMeshes[i*4+0]; const int ndv = params->detailMeshes[i*4+1]; const int nv = navPolys[i].vertCount; dtl.vertBase = vbase; dtl.vertCount = (unsigned short)(ndv-nv); dtl.triBase = params->detailMeshes[i*4+2]; dtl.triCount = params->detailMeshes[i*4+3]; // Copy vertices except the first 'nv' verts which are equal to nav poly verts. if (ndv-nv) { memcpy(&navDVerts[vbase*3], ¶ms->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv)); vbase += (unsigned short)(ndv-nv); } } // Store triangles. memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount); // Store and create BVtree. // TODO: take detail mesh into account! use byte per bbox extent? createBVTree(params->verts, params->vertCount, params->polys, params->polyCount, nvp, params->cs, params->ch, params->polyCount*2, navBvtree); // Store Off-Mesh connections. n = 0; for (int i = 0; i < params->offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i*2+0] == 0xff) { dtOffMeshConnection* con = &offMeshCons[n]; con->poly = (unsigned short)(offMeshPolyBase + n); // Copy connection end-points. const float* endPts = ¶ms->offMeshConVerts[i*2*3]; dtVcopy(&con->pos[0], &endPts[0]); dtVcopy(&con->pos[3], &endPts[3]); con->rad = params->offMeshConRad[i]; con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0; con->side = offMeshConClass[i*2+1]; n++; } } dtFree(offMeshConClass); *outData = data; *outDataSize = dataSize; return true; }
/// @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; }
static int calcLayerBufferSize(const int gridWidth, const int gridHeight) { const int headerSize = dtAlign4(sizeof(dtTileCacheLayerHeader)); const int gridSize = gridWidth * gridHeight; return headerSize + gridSize*4; }
/// @par /// /// The output data array is allocated using the detour allocator (dtAlloc()). The method /// used to free the memory will be determined by how the tile is added to the navigation /// mesh. /// /// @see dtNavMesh, dtNavMesh::addTile() bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize) { if (params->nvp > DT_VERTS_PER_POLYGON) return false; if (params->vertCount >= 0xffff) return false; if (!params->vertCount || !params->verts) return false; if (!params->polyCount || !params->polys) return false; const int nvp = params->nvp; // Classify off-mesh connection points. We store only the connections // whose start point is inside the tile. unsigned char* offMeshConClass = 0; int storedOffMeshConCount = 0; int offMeshConLinkCount = 0; if (params->offMeshConCount > 0) { offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP); if (!offMeshConClass) return false; // Find tight heigh bounds, used for culling out off-mesh start locations. float hmin = FLT_MAX; float hmax = -FLT_MAX; if (params->detailVerts && params->detailVertsCount) { for (int i = 0; i < params->detailVertsCount; ++i) { const float h = params->detailVerts[i*3+1]; hmin = dtMin(hmin,h); hmax = dtMax(hmax,h); } } else { for (int i = 0; i < params->vertCount; ++i) { const unsigned short* iv = ¶ms->verts[i*3]; const float h = params->bmin[1] + iv[1] * params->ch; hmin = dtMin(hmin,h); hmax = dtMax(hmax,h); } } hmin -= params->walkableClimb; hmax += params->walkableClimb; float bmin[3], bmax[3]; dtVcopy(bmin, params->bmin); dtVcopy(bmax, params->bmax); bmin[1] = hmin; bmax[1] = hmax; for (int i = 0; i < params->offMeshConCount; ++i) { const float* p0 = ¶ms->offMeshConVerts[(i*2+0)*3]; const float* p1 = ¶ms->offMeshConVerts[(i*2+1)*3]; offMeshConClass[i*2+0] = classifyOffMeshPoint(p0, bmin, bmax); offMeshConClass[i*2+1] = classifyOffMeshPoint(p1, bmin, bmax); // Zero out off-mesh start positions which are not even potentially touching the mesh. if (offMeshConClass[i*2+0] == 0xff) { if (p0[1] < bmin[1] || p0[1] > bmax[1]) offMeshConClass[i*2+0] = 0; } // Cound how many links should be allocated for off-mesh connections. if (offMeshConClass[i*2+0] == 0xff) offMeshConLinkCount++; if (offMeshConClass[i*2+1] == 0xff) offMeshConLinkCount++; if (offMeshConClass[i*2+0] == 0xff) storedOffMeshConCount++; } } // Off-mesh connectionss are stored as polygons, adjust values. const int totPolyCount = params->polyCount + storedOffMeshConCount; const int totVertCount = params->vertCount + storedOffMeshConCount*2; // Find portal edges which are at tile borders. int edgeCount = 0; int portalCount = 0; for (int i = 0; i < params->polyCount; ++i) { const unsigned short* p = ¶ms->polys[i*2*nvp]; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; edgeCount++; if (p[nvp+j] & 0x8000) { unsigned short dir = p[nvp+j] & 0xf; if (dir != 0xf) portalCount++; } } } const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2; // Find unique detail vertices. int uniqueDetailVertCount = 0; int detailTriCount = 0; if (params->detailMeshes) { // Has detail mesh, count unique detail vertex count and use input detail tri count. detailTriCount = params->detailTriCount; for (int i = 0; i < params->polyCount; ++i) { const unsigned short* p = ¶ms->polys[i*nvp*2]; int ndv = params->detailMeshes[i*4+1]; int nv = 0; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; nv++; } ndv -= nv; uniqueDetailVertCount += ndv; } } else { // No input detail mesh, build detail mesh from nav polys. uniqueDetailVertCount = 0; // No extra detail verts. detailTriCount = 0; for (int i = 0; i < params->polyCount; ++i) { const unsigned short* p = ¶ms->polys[i*nvp*2]; int nv = 0; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; nv++; } detailTriCount += nv-2; } } // Calculate data size const int headerSize = dtAlign4(sizeof(dtMeshHeader)); const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount); const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount); const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount); const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount); const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount); const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*detailTriCount); const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0; const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount); const int dataSize = headerSize + vertsSize + polysSize + linksSize + detailMeshesSize + detailVertsSize + detailTrisSize + bvTreeSize + offMeshConsSize; unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM); if (!data) { dtFree(offMeshConClass); return false; } memset(data, 0, dataSize); unsigned char* d = data; dtMeshHeader* header = (dtMeshHeader*)d; d += headerSize; float* navVerts = (float*)d; d += vertsSize; dtPoly* navPolys = (dtPoly*)d; d += polysSize; d += linksSize; dtPolyDetail* navDMeshes = (dtPolyDetail*)d; d += detailMeshesSize; float* navDVerts = (float*)d; d += detailVertsSize; unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize; dtBVNode* navBvtree = (dtBVNode*)d; d += bvTreeSize; dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshConsSize; // Store header header->magic = DT_NAVMESH_MAGIC; header->version = DT_NAVMESH_VERSION; header->x = params->tileX; header->y = params->tileY; header->layer = params->tileLayer; header->userId = params->userId; header->polyCount = totPolyCount; header->vertCount = totVertCount; header->maxLinkCount = maxLinkCount; dtVcopy(header->bmin, params->bmin); dtVcopy(header->bmax, params->bmax); header->detailMeshCount = params->polyCount; header->detailVertCount = uniqueDetailVertCount; header->detailTriCount = detailTriCount; header->bvQuantFactor = 1.0f / params->cs; header->offMeshBase = params->polyCount; header->walkableHeight = params->walkableHeight; header->walkableRadius = params->walkableRadius; header->walkableClimb = params->walkableClimb; header->offMeshConCount = storedOffMeshConCount; header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0; const int offMeshVertsBase = params->vertCount; const int offMeshPolyBase = params->polyCount; // Store vertices // Mesh vertices for (int i = 0; i < params->vertCount; ++i) { const unsigned short* iv = ¶ms->verts[i*3]; float* v = &navVerts[i*3]; v[0] = params->bmin[0] + iv[0] * params->cs; v[1] = params->bmin[1] + iv[1] * params->ch; v[2] = params->bmin[2] + iv[2] * params->cs; } // Off-mesh link vertices. int n = 0; for (int i = 0; i < params->offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i*2+0] == 0xff) { const float* linkv = ¶ms->offMeshConVerts[i*2*3]; float* v = &navVerts[(offMeshVertsBase + n*2)*3]; dtVcopy(&v[0], &linkv[0]); dtVcopy(&v[3], &linkv[3]); n++; } } // Store polygons // Mesh polys const unsigned short* src = params->polys; for (int i = 0; i < params->polyCount; ++i) { dtPoly* p = &navPolys[i]; p->vertCount = 0; p->flags = params->polyFlags[i]; p->setArea(params->polyAreas[i]); p->setType(DT_POLYTYPE_GROUND); for (int j = 0; j < nvp; ++j) { if (src[j] == MESH_NULL_IDX) break; p->verts[j] = src[j]; if (src[nvp+j] & 0x8000) { // Border or portal edge. unsigned short dir = src[nvp+j] & 0xf; if (dir == 0xf) // Border p->neis[j] = 0; else if (dir == 0) // Portal x- p->neis[j] = DT_EXT_LINK | 4; else if (dir == 1) // Portal z+ p->neis[j] = DT_EXT_LINK | 2; else if (dir == 2) // Portal x+ p->neis[j] = DT_EXT_LINK | 0; else if (dir == 3) // Portal z- p->neis[j] = DT_EXT_LINK | 6; } else { // Normal connection p->neis[j] = src[nvp+j]+1; } p->vertCount++; } src += nvp*2; } // Off-mesh connection vertices. n = 0; for (int i = 0; i < params->offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i*2+0] == 0xff) { dtPoly* p = &navPolys[offMeshPolyBase+n]; p->vertCount = 2; p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0); p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1); p->flags = params->offMeshConFlags[i]; p->setArea(params->offMeshConAreas[i]); p->setType(DT_POLYTYPE_OFFMESH_CONNECTION); n++; } } // Store detail meshes and vertices. // The nav polygon vertices are stored as the first vertices on each mesh. // We compress the mesh data by skipping them and using the navmesh coordinates. if (params->detailMeshes) { unsigned short vbase = 0; for (int i = 0; i < params->polyCount; ++i) { dtPolyDetail& dtl = navDMeshes[i]; const int vb = (int)params->detailMeshes[i*4+0]; const int ndv = (int)params->detailMeshes[i*4+1]; const int nv = navPolys[i].vertCount; dtl.vertBase = (unsigned int)vbase; dtl.vertCount = (unsigned char)(ndv-nv); dtl.triBase = (unsigned int)params->detailMeshes[i*4+2]; dtl.triCount = (unsigned char)params->detailMeshes[i*4+3]; // Copy vertices except the first 'nv' verts which are equal to nav poly verts. if (ndv-nv) { memcpy(&navDVerts[vbase*3], ¶ms->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv)); vbase += (unsigned short)(ndv-nv); } } // Store triangles. memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount); } else { // Create dummy detail mesh by triangulating polys. int tbase = 0; for (int i = 0; i < params->polyCount; ++i) { dtPolyDetail& dtl = navDMeshes[i]; const int nv = navPolys[i].vertCount; dtl.vertBase = 0; dtl.vertCount = 0; dtl.triBase = (unsigned int)tbase; dtl.triCount = (unsigned char)(nv-2); // Triangulate polygon (local indices). for (int j = 2; j < nv; ++j) { unsigned char* t = &navDTris[tbase*4]; t[0] = 0; t[1] = (unsigned char)(j-1); t[2] = (unsigned char)j; // Bit for each edge that belongs to poly boundary. t[3] = (1<<2); if (j == 2) t[3] |= (1<<0); if (j == nv-1) t[3] |= (1<<4); tbase++; } } } // Store and create BVtree. // TODO: take detail mesh into account! use byte per bbox extent? if (params->buildBvTree) { createBVTree(params->verts, params->vertCount, params->polys, params->polyCount, nvp, params->cs, params->ch, params->polyCount*2, navBvtree); } // Store Off-Mesh connections. n = 0; for (int i = 0; i < params->offMeshConCount; ++i) { // Only store connections which start from this tile. if (offMeshConClass[i*2+0] == 0xff) { dtOffMeshConnection* con = &offMeshCons[n]; con->poly = (unsigned short)(offMeshPolyBase + n); // Copy connection end-points. const float* endPts = ¶ms->offMeshConVerts[i*2*3]; dtVcopy(&con->pos[0], &endPts[0]); dtVcopy(&con->pos[3], &endPts[3]); con->rad = params->offMeshConRad[i]; con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0; con->side = offMeshConClass[i*2+1]; if (params->offMeshConUserID) con->userId = params->offMeshConUserID[i]; n++; } } dtFree(offMeshConClass); *outData = data; *outDataSize = dataSize; return true; }
/// @par /// /// The output data array is allocated using the detour allocator (dtAlloc()). The method /// used to free the memory will be determined by how the tile is added to the navigation /// mesh. /// /// @see dtNavMesh, dtNavMesh::addTile() bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize) { if (params->nvp > DT_VERTS_PER_POLYGON) return false; if (params->vertCount >= 0xffff) return false; if (!params->vertCount || !params->verts) return false; if (!params->polyCount || !params->polys) return false; const int nvp = params->nvp; // Classify off-mesh connection points. We store only the connections // whose start point is inside the tile. unsigned char* offMeshConClass = 0; int storedOffMeshConCount = 0; int offMeshConLinkCount = 0; int storedOffMeshSegCount = 0; if (params->offMeshConCount > 0) { offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP); if (!offMeshConClass) return false; memset(offMeshConClass, 0, sizeof(unsigned char)*params->offMeshConCount*2); // Find tight heigh bounds, used for culling out off-mesh start locations. float hmin = FLT_MAX; float hmax = -FLT_MAX; for (int i = 0; i < params->vertCount; ++i) { const unsigned short* iv = ¶ms->verts[i*3]; const float h = params->bmin[1] + iv[1] * params->ch; hmin = dtMin(hmin,h); hmax = dtMax(hmax,h); } if (params->detailVerts && params->detailVertsCount) { for (int i = 0; i < params->detailVertsCount; ++i) { const float h = params->detailVerts[i*3+1]; hmin = dtMin(hmin,h); hmax = dtMax(hmax,h); } } hmin -= params->walkableClimb; hmax += params->walkableClimb; float bmin[3], bmax[3]; dtVcopy(bmin, params->bmin); dtVcopy(bmax, params->bmax); bmin[1] = hmin; bmax[1] = hmax; float bverts[3*4]; bverts[ 0] = bmin[0]; bverts[ 2] = bmin[2]; bverts[ 3] = bmax[0]; bverts[ 5] = bmin[2]; bverts[ 6] = bmax[0]; bverts[ 8] = bmax[2]; bverts[ 9] = bmin[0]; bverts[11] = bmax[2]; for (int i = 0; i < params->offMeshConCount; ++i) { const dtOffMeshLinkCreateParams& offMeshCon = params->offMeshCons[i]; if (offMeshCon.type & DT_OFFMESH_CON_POINT) { offMeshConClass[i*2+0] = classifyOffMeshPoint(offMeshCon.vertsA0, bmin, bmax); offMeshConClass[i*2+1] = classifyOffMeshPoint(offMeshCon.vertsB0, bmin, bmax); // Zero out off-mesh start positions which are not even potentially touching the mesh. if (offMeshConClass[i*2+0] == 0xff) { if ((offMeshCon.vertsA0[1] - offMeshCon.snapHeight) < bmin[1] && (offMeshCon.vertsA0[1] + offMeshCon.snapHeight) > bmax[1]) { offMeshConClass[i * 2 + 0] = 0; } } // Count how many links should be allocated for off-mesh connections. if (offMeshConClass[i*2+0] == 0xff) offMeshConLinkCount++; if (offMeshConClass[i*2+1] == 0xff) offMeshConLinkCount++; if (offMeshConClass[i*2+0] == 0xff) storedOffMeshConCount++; } else if (offMeshCon.type & DT_OFFMESH_CON_SEGMENT) { int smin, smax; float tmin, tmax; if ((offMeshCon.vertsA0[1] >= bmin[1] && offMeshCon.vertsA0[1] <= bmax[1] && classifyOffMeshPoint(offMeshCon.vertsA0, bmin, bmax) == 0xff) || (offMeshCon.vertsA1[1] >= bmin[1] && offMeshCon.vertsA1[1] <= bmax[1] && classifyOffMeshPoint(offMeshCon.vertsA1, bmin, bmax) == 0xff) || (offMeshCon.vertsB0[1] >= bmin[1] && offMeshCon.vertsB0[1] <= bmax[1] && classifyOffMeshPoint(offMeshCon.vertsB0, bmin, bmax) == 0xff) || (offMeshCon.vertsB1[1] >= bmin[1] && offMeshCon.vertsB1[1] <= bmax[1] && classifyOffMeshPoint(offMeshCon.vertsB1, bmin, bmax) == 0xff) || dtIntersectSegmentPoly2D(offMeshCon.vertsA0, offMeshCon.vertsA1, bverts, 4, tmin, tmax, smin, smax) || dtIntersectSegmentPoly2D(offMeshCon.vertsB0, offMeshCon.vertsB1, bverts, 4, tmin, tmax, smin, smax)) { offMeshConClass[i*2] = 0xff; storedOffMeshSegCount++; } } } } // Off-mesh connections are stored as polygons, adjust values. const int firstSegVert = params->vertCount + storedOffMeshConCount*2; const int firstSegPoly = params->polyCount + storedOffMeshConCount; const int totPolyCount = firstSegPoly + storedOffMeshSegCount*DT_MAX_OFFMESH_SEGMENT_PARTS; const int totVertCount = firstSegVert + storedOffMeshSegCount*DT_MAX_OFFMESH_SEGMENT_PARTS*4; // Find portal edges which are at tile borders. int edgeCount = 0; int portalCount = 0; for (int i = 0; i < params->polyCount; ++i) { const unsigned short* p = ¶ms->polys[i*2*nvp]; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; edgeCount++; if (p[nvp+j] & 0x8000) { unsigned short dir = p[nvp+j] & 0xf; if (dir != 0xf) portalCount++; } } } // offmesh links will be added in dynamic array const int maxLinkCount = edgeCount + portalCount*2; // Find unique detail vertices. int uniqueDetailVertCount = 0; int detailTriCount = 0; if (params->detailMeshes) { // Has detail mesh, count unique detail vertex count and use input detail tri count. detailTriCount = params->detailTriCount; for (int i = 0; i < params->polyCount; ++i) { const unsigned short* p = ¶ms->polys[i*nvp*2]; int ndv = params->detailMeshes[i*4+1]; int nv = 0; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; nv++; } ndv -= nv; uniqueDetailVertCount += ndv; } } else { // No input detail mesh, build detail mesh from nav polys. uniqueDetailVertCount = 0; // No extra detail verts. detailTriCount = 0; for (int i = 0; i < params->polyCount; ++i) { const unsigned short* p = ¶ms->polys[i*nvp*2]; int nv = 0; for (int j = 0; j < nvp; ++j) { if (p[j] == MESH_NULL_IDX) break; nv++; } detailTriCount += nv-2; } } // Calculate data size const int headerSize = dtAlign4(sizeof(dtMeshHeader)); const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount); const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount); const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount); const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount); const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount); const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*detailTriCount); const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0; const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount); const int offMeshSegSize = dtAlign4(sizeof(dtOffMeshSegmentConnection)*storedOffMeshSegCount); const int clustersSize = dtAlign4(sizeof(dtCluster)*params->clusterCount); const int polyClustersSize = dtAlign4(sizeof(unsigned short)*params->polyCount); const int dataSize = headerSize + vertsSize + polysSize + linksSize + detailMeshesSize + detailVertsSize + detailTrisSize + bvTreeSize + offMeshConsSize + offMeshSegSize + clustersSize + polyClustersSize; unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM); if (!data) { dtFree(offMeshConClass); return false; } memset(data, 0, dataSize); unsigned char* d = data; dtMeshHeader* header = (dtMeshHeader*)d; d += headerSize; float* navVerts = (float*)d; d += vertsSize; dtPoly* navPolys = (dtPoly*)d; d += polysSize; d += linksSize; dtPolyDetail* navDMeshes = (dtPolyDetail*)d; d += detailMeshesSize; float* navDVerts = (float*)d; d += detailVertsSize; unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize; dtBVNode* navBvtree = (dtBVNode*)d; d += bvTreeSize; dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshConsSize; dtOffMeshSegmentConnection* offMeshSegs = (dtOffMeshSegmentConnection*)d; d += offMeshSegSize; dtCluster* clusters = (dtCluster*)d; d += clustersSize; unsigned short* polyClusters = (unsigned short*)d; d += polyClustersSize; // Store header header->magic = DT_NAVMESH_MAGIC; header->version = DT_NAVMESH_VERSION; header->x = params->tileX; header->y = params->tileY; header->layer = params->tileLayer; header->userId = params->userId; header->polyCount = totPolyCount; header->vertCount = totVertCount; header->maxLinkCount = maxLinkCount; dtVcopy(header->bmin, params->bmin); dtVcopy(header->bmax, params->bmax); header->detailMeshCount = params->polyCount; header->detailVertCount = uniqueDetailVertCount; header->detailTriCount = detailTriCount; header->bvQuantFactor = 1.0f / params->cs; header->offMeshBase = params->polyCount; header->offMeshSegPolyBase = firstSegPoly; header->offMeshSegVertBase = firstSegVert; header->walkableHeight = params->walkableHeight; header->walkableRadius = params->walkableRadius; header->walkableClimb = params->walkableClimb; header->offMeshConCount = storedOffMeshConCount; header->offMeshSegConCount = storedOffMeshSegCount; header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0; header->clusterCount = params->clusterCount; const int offMeshVertsBase = params->vertCount; const int offMeshPolyBase = params->polyCount; // Store vertices // Mesh vertices for (int i = 0; i < params->vertCount; ++i) { const unsigned short* iv = ¶ms->verts[i*3]; float* v = &navVerts[i*3]; v[0] = params->bmin[0] + iv[0] * params->cs; v[1] = params->bmin[1] + iv[1] * params->ch; v[2] = params->bmin[2] + iv[2] * params->cs; } // Off-mesh point link vertices. int n = 0; for (int i = 0; i < params->offMeshConCount; ++i) { const dtOffMeshLinkCreateParams& offMeshCon = params->offMeshCons[i]; // Only store connections which start from this tile. if ((offMeshConClass[i*2+0] == 0xff) && (offMeshCon.type & DT_OFFMESH_CON_POINT)) { float* v = &navVerts[(offMeshVertsBase + n*2)*3]; dtVcopy(&v[0], &offMeshCon.vertsA0[0]); dtVcopy(&v[3], &offMeshCon.vertsB0[0]); n++; } } // Store polygons // Mesh polys const unsigned short* src = params->polys; for (int i = 0; i < params->polyCount; ++i) { dtPoly* p = &navPolys[i]; p->vertCount = 0; p->flags = params->polyFlags[i]; p->setArea(params->polyAreas[i]); p->setType(DT_POLYTYPE_GROUND); for (int j = 0; j < nvp; ++j) { if (src[j] == MESH_NULL_IDX) break; p->verts[j] = src[j]; if (src[nvp+j] & 0x8000) { // Border or portal edge. unsigned short dir = src[nvp+j] & 0xf; if (dir == 0xf) // Border p->neis[j] = 0; else if (dir == 0) // Portal x- p->neis[j] = DT_EXT_LINK | 4; else if (dir == 1) // Portal z+ p->neis[j] = DT_EXT_LINK | 2; else if (dir == 2) // Portal x+ p->neis[j] = DT_EXT_LINK | 0; else if (dir == 3) // Portal z- p->neis[j] = DT_EXT_LINK | 6; } else { // Normal connection p->neis[j] = src[nvp+j]+1; } p->vertCount++; } src += nvp*2; } // Off-mesh point connection polygons. n = 0; int nseg = 0; for (int i = 0; i < params->offMeshConCount; ++i) { const dtOffMeshLinkCreateParams& offMeshCon = params->offMeshCons[i]; // Only store connections which start from this tile. if (offMeshConClass[i*2+0] == 0xff) { if (offMeshCon.type & DT_OFFMESH_CON_POINT) { dtPoly* p = &navPolys[offMeshPolyBase+n]; p->vertCount = 2; p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0); p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1); p->flags = offMeshCon.polyFlag; p->setArea(offMeshCon.area); p->setType(DT_POLYTYPE_OFFMESH_POINT); n++; } else { for (int j = 0; j < DT_MAX_OFFMESH_SEGMENT_PARTS; j++) { dtPoly* p = &navPolys[firstSegPoly+nseg]; p->vertCount = 0; p->flags = offMeshCon.polyFlag; p->setArea(offMeshCon.area); p->setType(DT_POLYTYPE_OFFMESH_SEGMENT); nseg++; } } } } // Store detail meshes and vertices. // The nav polygon vertices are stored as the first vertices on each mesh. // We compress the mesh data by skipping them and using the navmesh coordinates. if (params->detailMeshes) { unsigned short vbase = 0; for (int i = 0; i < params->polyCount; ++i) { dtPolyDetail& dtl = navDMeshes[i]; const int vb = (int)params->detailMeshes[i*4+0]; const int ndv = (int)params->detailMeshes[i*4+1]; const int nv = navPolys[i].vertCount; dtl.vertBase = (unsigned int)vbase; dtl.vertCount = (unsigned char)(ndv-nv); dtl.triBase = (unsigned int)params->detailMeshes[i*4+2]; dtl.triCount = (unsigned char)params->detailMeshes[i*4+3]; // Copy vertices except the first 'nv' verts which are equal to nav poly verts. if (ndv-nv) { memcpy(&navDVerts[vbase*3], ¶ms->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv)); vbase += (unsigned short)(ndv-nv); } } // Store triangles. memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount); } else { // Create dummy detail mesh by triangulating polys. int tbase = 0; for (int i = 0; i < params->polyCount; ++i) { dtPolyDetail& dtl = navDMeshes[i]; const int nv = navPolys[i].vertCount; dtl.vertBase = 0; dtl.vertCount = 0; dtl.triBase = (unsigned int)tbase; dtl.triCount = (unsigned char)(nv-2); // Triangulate polygon (local indices). for (int j = 2; j < nv; ++j) { unsigned char* t = &navDTris[tbase*4]; t[0] = 0; t[1] = (unsigned char)(j-1); t[2] = (unsigned char)j; // Bit for each edge that belongs to poly boundary. t[3] = (1<<2); if (j == 2) t[3] |= (1<<0); if (j == nv-1) t[3] |= (1<<4); tbase++; } } } // Store and create BVtree. if (params->buildBvTree) { createBVTree(params->verts, params->vertCount, params->polys, params->polyCount, nvp, navDMeshes, navDVerts, navDTris, params->bmin, params->cs, params->ch, params->polyCount*2, navBvtree); } // Store Off-Mesh connections. n = 0; nseg = 0; for (int i = 0; i < params->offMeshConCount; ++i) { const dtOffMeshLinkCreateParams& offMeshCon = params->offMeshCons[i]; // Only store connections which start from this tile. if (offMeshConClass[i*2+0] == 0xff) { if (offMeshCon.type & DT_OFFMESH_CON_POINT) { dtOffMeshConnection* con = &offMeshCons[n]; con->poly = (unsigned short)(offMeshPolyBase + n); // Copy connection end-points. dtVcopy(&con->pos[0], &offMeshCon.vertsA0[0]); dtVcopy(&con->pos[3], &offMeshCon.vertsB0[0]); con->rad = offMeshCon.snapRadius; con->height = offMeshCon.snapHeight; con->setFlags(offMeshCon.type); con->side = offMeshConClass[i*2+1] == 0xff ? DT_CONNECTION_INTERNAL : offMeshConClass[i*2+1]; if (offMeshCon.userID) con->userId = offMeshCon.userID; n++; } else { dtOffMeshSegmentConnection* con = &offMeshSegs[nseg]; dtVcopy(con->startA, &offMeshCon.vertsA0[0]); dtVcopy(con->endA, &offMeshCon.vertsA1[0]); dtVcopy(con->startB, &offMeshCon.vertsB0[0]); dtVcopy(con->endB, &offMeshCon.vertsB1[0]); con->rad = offMeshCon.snapRadius; con->height = offMeshCon.snapHeight; con->setFlags(offMeshCon.type); if (offMeshCon.userID) con->userId = offMeshCon.userID; nseg++; } } } dtFree(offMeshConClass); // Store clusters if (params->polyClusters) { memcpy(polyClusters, params->polyClusters, sizeof(unsigned short)*params->polyCount); } for (int i = 0; i < params->clusterCount; i++) { dtCluster& cluster = clusters[i]; cluster.firstLink = DT_NULL_LINK; cluster.numLinks = 0; dtVset(cluster.center, 0.f, 0.f, 0.f); // calculate center point: take from first poly for (int j = 0; j < params->polyCount; j++) { if (polyClusters[j] != i) { continue; } const dtPoly* poly = &navPolys[j]; float c[3] = { 0.0f, 0.0f, 0.0f }; for (int iv = 0; iv < poly->vertCount; iv++) { dtVadd(c, c, &navVerts[poly->verts[iv] * 3]); } dtVmad(cluster.center, cluster.center, c, 1.0f / poly->vertCount); break; } } *outData = data; *outDataSize = dataSize; return true; }
dtTileRef dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef) { // Make sure the data is in right format. dtMeshHeader* header = (dtMeshHeader*)data; if (header->magic != DT_NAVMESH_MAGIC) return 0; if (header->version != DT_NAVMESH_VERSION) return 0; // Make sure the location is free. if (getTileAt(header->x, header->y)) return 0; // Allocate a tile. dtMeshTile* tile = 0; if (!lastRef) { if (m_nextFree) { tile = m_nextFree; m_nextFree = tile->next; tile->next = 0; } } else { // TODO: Better error reporting! // Try to relocate the tile to specific index with same salt. int tileIndex = (int)decodePolyIdTile((dtPolyRef)lastRef); if (tileIndex >= m_maxTiles) return 0; // 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 0; // 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 0; // 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; // 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); connectIntOffMeshLinks(tile); // Create connections connections. for (int i = 0; i < 8; ++i) { dtMeshTile* nei = getNeighbourTileAt(header->x, header->y, i); if (nei) { connectExtLinks(tile, nei, i); connectExtLinks(nei, tile, opposite(i)); connectExtOffMeshLinks(tile, nei, i); connectExtOffMeshLinks(nei, tile, opposite(i)); } } return getTileRef(tile); }
/// @par /// /// @warning This function assumes that the header is in the correct endianess already. /// Call #dtNavMeshHeaderSwapEndian() first on the data if the data is expected to be in wrong endianess /// to start with. Call #dtNavMeshHeaderSwapEndian() after the data has been swapped if converting from /// native to foreign endianess. bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/) { // Make sure the data is in right format. dtMeshHeader* header = (dtMeshHeader*)data; if (header->magic != DT_NAVMESH_MAGIC) return false; if (header->version != DT_NAVMESH_VERSION) return false; // 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; float* verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize); dtPoly* polys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize); d += linksSize; // Ignore links; they technically should be endian-swapped but all their data is overwritten on load anyway. //dtLink* links = dtGetThenAdvanceBufferPointer<dtLink>(d, linksSize); dtPolyDetail* detailMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize); float* detailVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize); d += detailTrisSize; // Ignore detail tris; single bytes can't be endian-swapped. //unsigned char* detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize); dtBVNode* bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize); dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize); // Vertices for (int i = 0; i < header->vertCount*3; ++i) { dtSwapEndian(&verts[i]); } // Polys for (int i = 0; i < header->polyCount; ++i) { dtPoly* p = &polys[i]; // poly->firstLink is update when tile is added, no need to swap. for (int j = 0; j < DT_VERTS_PER_POLYGON; ++j) { dtSwapEndian(&p->verts[j]); dtSwapEndian(&p->neis[j]); } dtSwapEndian(&p->flags); } // Links are rebuild when tile is added, no need to swap. // Detail meshes for (int i = 0; i < header->detailMeshCount; ++i) { dtPolyDetail* pd = &detailMeshes[i]; dtSwapEndian(&pd->vertBase); dtSwapEndian(&pd->triBase); } // Detail verts for (int i = 0; i < header->detailVertCount*3; ++i) { dtSwapEndian(&detailVerts[i]); } // BV-tree for (int i = 0; i < header->bvNodeCount; ++i) { dtBVNode* node = &bvTree[i]; for (int j = 0; j < 3; ++j) { dtSwapEndian(&node->bmin[j]); dtSwapEndian(&node->bmax[j]); } dtSwapEndian(&node->i); } // Off-mesh Connections. for (int i = 0; i < header->offMeshConCount; ++i) { dtOffMeshConnection* con = &offMeshCons[i]; for (int j = 0; j < 6; ++j) dtSwapEndian(&con->pos[j]); dtSwapEndian(&con->rad); dtSwapEndian(&con->poly); } return true; }