bool DynamicNavigationMesh::Build(const BoundingBox& boundingBox) { URHO3D_PROFILE(BuildPartialNavigationMesh); if (!node_) return false; if (!navMesh_) { URHO3D_LOGERROR("Navigation mesh must first be built fully before it can be partially rebuilt"); return false; } if (!node_->GetWorldScale().Equals(Vector3::ONE)) URHO3D_LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended"); BoundingBox localSpaceBox = boundingBox.Transformed(node_->GetWorldTransform().Inverse()); float tileEdgeLength = (float)tileSize_ * cellSize_; Vector<NavigationGeometryInfo> geometryList; CollectGeometries(geometryList); int sx = Clamp((int)((localSpaceBox.min_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1); int sz = Clamp((int)((localSpaceBox.min_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1); int ex = Clamp((int)((localSpaceBox.max_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1); int ez = Clamp((int)((localSpaceBox.max_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1); unsigned numTiles = BuildTiles(geometryList, IntVector2(sx, sz), IntVector2(ex, ez)); URHO3D_LOGDEBUG("Rebuilt " + String(numTiles) + " tiles of the navigation mesh"); return true; }
bool DynamicNavigationMesh::Build(const BoundingBox& boundingBox) { PROFILE(BuildPartialNavigationMesh); if (!node_) return false; if (!navMesh_) { LOGERROR("Navigation mesh must first be built fully before it can be partially rebuilt"); return false; } if (!node_->GetWorldScale().Equals(Vector3::ONE)) LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended"); BoundingBox localSpaceBox = boundingBox.Transformed(node_->GetWorldTransform().Inverse()); float tileEdgeLength = (float)tileSize_ * cellSize_; Vector<NavigationGeometryInfo> geometryList; CollectGeometries(geometryList); int sx = Clamp((int)((localSpaceBox.min_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1); int sz = Clamp((int)((localSpaceBox.min_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1); int ex = Clamp((int)((localSpaceBox.max_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1); int ez = Clamp((int)((localSpaceBox.max_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1); unsigned numTiles = 0; for (int z = sz; z <= ez; ++z) { for (int x = sx; x <= ex; ++x) { dtCompressedTileRef existing[TILECACHE_MAXLAYERS]; const int existingCt = tileCache_->getTilesAt(x, z, existing, TILECACHE_MAXLAYERS); for (int i = 0; i < existingCt; ++i) { unsigned char* data = 0x0; if (!dtStatusFailed(tileCache_->removeTile(existing[i], &data, 0)) && data != 0x0) dtFree(data); } 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; } else { tileCache_->buildNavMeshTile(tileRef, navMesh_); ++numTiles; } } } } LOGDEBUG("Rebuilt " + String(numTiles) + " tiles of the navigation mesh"); return true; }
bool DynamicNavigationMesh::Allocate(const BoundingBox& boundingBox, unsigned maxTiles) { // Release existing navigation data and zero the bounding box ReleaseNavigationMesh(); if (!node_) return false; if (!node_->GetWorldScale().Equals(Vector3::ONE)) URHO3D_LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended"); boundingBox_ = boundingBox.Transformed(node_->GetWorldTransform().Inverse()); maxTiles = NextPowerOfTwo(maxTiles); // 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 polygons, 22 bits available to identify both tile & polygon within tile unsigned tileBits = LogBaseTwo(maxTiles); unsigned maxPolys = 1u << (22 - tileBits); dtNavMeshParams params; // NOLINT(hicpp-member-init) rcVcopy(params.orig, &boundingBox_.min_.x_); params.tileWidth = tileEdgeLength; params.tileHeight = tileEdgeLength; params.maxTiles = maxTiles; params.maxPolys = maxPolys; navMesh_ = dtAllocNavMesh(); if (!navMesh_) { URHO3D_LOGERROR("Could not allocate navigation mesh"); return false; } if (dtStatusFailed(navMesh_->init(¶ms))) { URHO3D_LOGERROR("Could not initialize navigation mesh"); ReleaseNavigationMesh(); return false; } dtTileCacheParams tileCacheParams; // NOLINT(hicpp-member-init) 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 = maxTiles * maxLayers_; tileCacheParams.maxObstacles = maxObstacles_; // Settings from NavigationMesh tileCacheParams.walkableClimb = agentMaxClimb_; tileCacheParams.walkableHeight = agentHeight_; tileCacheParams.walkableRadius = agentRadius_; tileCache_ = dtAllocTileCache(); if (!tileCache_) { URHO3D_LOGERROR("Could not allocate tile cache"); ReleaseNavigationMesh(); return false; } if (dtStatusFailed(tileCache_->init(&tileCacheParams, allocator_.Get(), compressor_.Get(), meshProcessor_.Get()))) { URHO3D_LOGERROR("Could not initialize tile cache"); ReleaseNavigationMesh(); return false; } URHO3D_LOGDEBUG("Allocated empty navigation mesh with max " + String(maxTiles) + " tiles"); // Scan for obstacles to insert into us PODVector<Node*> obstacles; GetScene()->GetChildrenWithComponent<Obstacle>(obstacles, true); for (unsigned i = 0; i < obstacles.Size(); ++i) { auto* obs = obstacles[i]->GetComponent<Obstacle>(); if (obs && obs->IsEnabledEffective()) AddObstacle(obs); } // 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); } return true; }