void DynamicNavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest) { if (!debug || !navMesh_ || !node_) return; const Matrix3x4& worldTransform = node_->GetWorldTransform(); const dtNavMesh* navMesh = navMesh_; for (int z = 0; z < numTilesZ_; ++z) { for (int x = 0; x < numTilesX_; ++x) { // Get the layers from the tile-cache const dtMeshTile* tiles[TILECACHE_MAXLAYERS]; int tileCount = navMesh->getTilesAt(x, z, tiles, TILECACHE_MAXLAYERS); for (int i = 0; i < tileCount; ++i) { const dtMeshTile* tile = tiles[i]; if (!tile) continue; for (int i = 0; i < tile->header->polyCount; ++i) { dtPoly* poly = tile->polys + i; for (unsigned j = 0; j < poly->vertCount; ++j) { debug->AddLine( worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[j] * 3]), worldTransform * *reinterpret_cast<const Vector3*>(&tile->verts[poly->verts[(j + 1) % poly->vertCount] * 3]), Color::YELLOW, depthTest ); } } } } } Scene* scene = GetScene(); if (scene) { // Draw Obstacle components if (drawObstacles_) { PODVector<Node*> obstacles; scene->GetChildrenWithComponent<Obstacle>(obstacles, true); for (unsigned i = 0; i < obstacles.Size(); ++i) { Obstacle* obstacle = obstacles[i]->GetComponent<Obstacle>(); if (obstacle && obstacle->IsEnabledEffective()) obstacle->DrawDebugGeometry(debug, depthTest); } } // Draw OffMeshConnection components if (drawOffMeshConnections_) { PODVector<Node*> connections; scene->GetChildrenWithComponent<OffMeshConnection>(connections, true); for (unsigned i = 0; i < connections.Size(); ++i) { OffMeshConnection* connection = connections[i]->GetComponent<OffMeshConnection>(); if (connection && connection->IsEnabledEffective()) connection->DrawDebugGeometry(debug, depthTest); } } // Draw NavArea components if (drawNavAreas_) { PODVector<Node*> areas; scene->GetChildrenWithComponent<NavArea>(areas, true); for (unsigned i = 0; i < areas.Size(); ++i) { NavArea* area = areas[i]->GetComponent<NavArea>(); if (area && area->IsEnabledEffective()) area->DrawDebugGeometry(debug, depthTest); } } } }
bool DynamicNavigationMesh::Build() { PROFILE(BuildNavigationMesh); // Release existing navigation data and zero the bounding box ReleaseNavigationMesh(); if (!node_) return false; if (!node_->GetWorldScale().Equals(Vector3::ONE)) LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended"); Vector<NavigationGeometryInfo> geometryList; CollectGeometries(geometryList); if (geometryList.Empty()) return true; // Nothing to do // Build the combined bounding box for (unsigned i = 0; i < geometryList.Size(); ++i) boundingBox_.Merge(geometryList[i].boundingBox_); // Expand bounding box by padding boundingBox_.min_ -= padding_; boundingBox_.max_ += padding_; { PROFILE(BuildNavigationMesh); // Calculate number of tiles int gridW = 0, gridH = 0; float tileEdgeLength = (float)tileSize_ * cellSize_; rcCalcGridSize(&boundingBox_.min_.x_, &boundingBox_.max_.x_, cellSize_, &gridW, &gridH); numTilesX_ = (gridW + tileSize_ - 1) / tileSize_; numTilesZ_ = (gridH + tileSize_ - 1) / tileSize_; // Calculate max. number of tiles and polygons, 22 bits available to identify both tile & polygon within tile unsigned maxTiles = NextPowerOfTwo(numTilesX_ * numTilesZ_) * TILECACHE_MAXLAYERS; unsigned tileBits = 0; unsigned temp = maxTiles; while (temp > 1) { temp >>= 1; ++tileBits; } unsigned maxPolys = 1 << (22 - tileBits); dtNavMeshParams params; rcVcopy(params.orig, &boundingBox_.min_.x_); params.tileWidth = tileEdgeLength; params.tileHeight = tileEdgeLength; params.maxTiles = maxTiles; params.maxPolys = maxPolys; navMesh_ = dtAllocNavMesh(); if (!navMesh_) { LOGERROR("Could not allocate navigation mesh"); return false; } if (dtStatusFailed(navMesh_->init(¶ms))) { LOGERROR("Could not initialize navigation mesh"); ReleaseNavigationMesh(); return false; } dtTileCacheParams tileCacheParams; memset(&tileCacheParams, 0, sizeof(tileCacheParams)); rcVcopy(tileCacheParams.orig, &boundingBox_.min_.x_); tileCacheParams.ch = cellHeight_; tileCacheParams.cs = cellSize_; tileCacheParams.width = tileSize_; tileCacheParams.height = tileSize_; tileCacheParams.maxSimplificationError = edgeMaxError_; tileCacheParams.maxTiles = numTilesX_ * numTilesZ_ * TILECACHE_MAXLAYERS; tileCacheParams.maxObstacles = maxObstacles_; // Settings from NavigationMesh tileCacheParams.walkableClimb = agentMaxClimb_; tileCacheParams.walkableHeight = agentHeight_; tileCacheParams.walkableRadius = agentRadius_; tileCache_ = dtAllocTileCache(); if (!tileCache_) { LOGERROR("Could not allocate tile cache"); ReleaseNavigationMesh(); return false; } if (dtStatusFailed(tileCache_->init(&tileCacheParams, allocator_, compressor_, meshProcessor_))) { LOGERROR("Could not initialize tile cache"); ReleaseNavigationMesh(); return false; } // Build each tile unsigned numTiles = 0; for (int z = 0; z < numTilesZ_; ++z) { for (int x = 0; x < numTilesX_; ++x) { TileCacheData tiles[TILECACHE_MAXLAYERS]; int layerCt = BuildTile(geometryList, x, z, tiles); for (int i = 0; i < layerCt; ++i) { dtCompressedTileRef tileRef; int status = tileCache_->addTile(tiles[i].data, tiles[i].dataSize, DT_COMPRESSEDTILE_FREE_DATA, &tileRef); if (dtStatusFailed(status)) { dtFree(tiles[i].data); tiles[i].data = 0x0; } } ++numTiles; } for (int x = 0; x < numTilesX_; ++x) tileCache_->buildNavMeshTilesAt(x, z, navMesh_); } // For a full build it's necessary to update the nav mesh // not doing so will cause dependent components to crash, like DetourCrowdManager tileCache_->update(0, navMesh_); LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles"); // Send a notification event to concerned parties that we've been fully rebuilt { using namespace NavigationMeshRebuilt; VariantMap& buildEventParams = GetContext()->GetEventDataMap(); buildEventParams[P_NODE] = node_; buildEventParams[P_MESH] = this; SendEvent(E_NAVIGATION_MESH_REBUILT, buildEventParams); } // Scan for obstacles to insert into us PODVector<Node*> obstacles; GetScene()->GetChildrenWithComponent<Obstacle>(obstacles, true); for (unsigned i = 0; i < obstacles.Size(); ++i) { Obstacle* obs = obstacles[i]->GetComponent<Obstacle>(); if (obs && obs->IsEnabledEffective()) AddObstacle(obs); } return true; } }