bool TileCachedRecastMeshManager::addTile(const ObjectId id, const btCollisionShape& shape, const btTransform& transform, const AreaType areaType, const TilePosition& tilePosition, float border, std::map<TilePosition, CachedRecastMeshManager>& tiles) { auto tile = tiles.find(tilePosition); if (tile == tiles.end()) { auto tileBounds = makeTileBounds(mSettings, tilePosition); tileBounds.mMin -= osg::Vec2f(border, border); tileBounds.mMax += osg::Vec2f(border, border); tile = tiles.insert(std::make_pair(tilePosition, CachedRecastMeshManager(mSettings, tileBounds))).first; } return tile->second.addObject(id, shape, transform, areaType); }
bool TileCachedRecastMeshManager::addWater(const osg::Vec2i& cellPosition, const int cellSize, const btTransform& transform) { const auto border = getBorderSize(mSettings); auto& tilesPositions = mWaterTilesPositions[cellPosition]; bool result = false; if (cellSize == std::numeric_limits<int>::max()) { const auto tiles = mTiles.lock(); for (auto& tile : *tiles) { if (tile.second.addWater(cellPosition, cellSize, transform)) { tilesPositions.push_back(tile.first); result = true; } } } else { getTilesPositions(cellSize, transform, mSettings, [&] (const TilePosition& tilePosition) { const auto tiles = mTiles.lock(); auto tile = tiles->find(tilePosition); if (tile == tiles->end()) { auto tileBounds = makeTileBounds(mSettings, tilePosition); tileBounds.mMin -= osg::Vec2f(border, border); tileBounds.mMax += osg::Vec2f(border, border); tile = tiles->insert(std::make_pair(tilePosition, CachedRecastMeshManager(mSettings, tileBounds))).first; } if (tile->second.addWater(cellPosition, cellSize, transform)) { tilesPositions.push_back(tilePosition); result = true; } }); } if (result) ++mRevision; return result; }
UpdateNavMeshStatus updateNavMesh(const osg::Vec3f& agentHalfExtents, const RecastMesh* recastMesh, const TilePosition& changedTile, const TilePosition& playerTile, const std::vector<OffMeshConnection>& offMeshConnections, const Settings& settings, const SharedNavMeshCacheItem& navMeshCacheItem, NavMeshTilesCache& navMeshTilesCache) { log("update NavMesh with mutiple tiles:", " agentHeight=", std::setprecision(std::numeric_limits<float>::max_exponent10), getHeight(settings, agentHalfExtents), " agentMaxClimb=", std::setprecision(std::numeric_limits<float>::max_exponent10), getMaxClimb(settings), " agentRadius=", std::setprecision(std::numeric_limits<float>::max_exponent10), getRadius(settings, agentHalfExtents), " changedTile=", changedTile, " playerTile=", playerTile, " changedTileDistance=", getDistance(changedTile, playerTile)); const auto params = *navMeshCacheItem.lockConst()->getValue().getParams(); const osg::Vec3f origin(params.orig[0], params.orig[1], params.orig[2]); const auto x = changedTile.x(); const auto y = changedTile.y(); const auto removeTile = [&] { const auto locked = navMeshCacheItem.lock(); auto& navMesh = locked->getValue(); const auto tileRef = navMesh.getTileRefAt(x, y, 0); const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr)); if (removed) locked->removeUsedTile(changedTile); return makeUpdateNavMeshStatus(removed, false); }; if (!recastMesh) { log("ignore add tile: recastMesh is null"); return removeTile(); } auto recastMeshBounds = recastMesh->getBounds(); for (const auto& water : recastMesh->getWater()) { const auto waterBounds = getWaterBounds(water, settings, agentHalfExtents); recastMeshBounds.mMin.y() = std::min(recastMeshBounds.mMin.y(), waterBounds.mMin.y()); recastMeshBounds.mMax.y() = std::max(recastMeshBounds.mMax.y(), waterBounds.mMax.y()); } if (isEmpty(recastMeshBounds)) { log("ignore add tile: recastMesh is empty"); return removeTile(); } if (!shouldAddTile(changedTile, playerTile, params.maxTiles)) { log("ignore add tile: too far from player"); return removeTile(); } auto cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh, offMeshConnections); if (!cachedNavMeshData) { const auto tileBounds = makeTileBounds(settings, changedTile); const osg::Vec3f tileBorderMin(tileBounds.mMin.x(), recastMeshBounds.mMin.y() - 1, tileBounds.mMin.y()); const osg::Vec3f tileBorderMax(tileBounds.mMax.x(), recastMeshBounds.mMax.y() + 1, tileBounds.mMax.y()); auto navMeshData = makeNavMeshTileData(agentHalfExtents, *recastMesh, offMeshConnections, changedTile, tileBorderMin, tileBorderMax, settings); if (!navMeshData.mValue) { log("ignore add tile: NavMeshData is null"); return removeTile(); } try { cachedNavMeshData = navMeshTilesCache.set(agentHalfExtents, changedTile, *recastMesh, offMeshConnections, std::move(navMeshData)); } catch (const InvalidArgument&) { cachedNavMeshData = navMeshTilesCache.get(agentHalfExtents, changedTile, *recastMesh, offMeshConnections); } if (!cachedNavMeshData) { log("cache overflow"); const auto locked = navMeshCacheItem.lock(); auto& navMesh = locked->getValue(); const auto tileRef = navMesh.getTileRefAt(x, y, 0); const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr)); const auto addStatus = navMesh.addTile(navMeshData.mValue.get(), navMeshData.mSize, doNotTransferOwnership, 0, 0); if (dtStatusSucceed(addStatus)) { locked->setUsedTile(changedTile, std::move(navMeshData)); return makeUpdateNavMeshStatus(removed, true); } else { if (removed) locked->removeUsedTile(changedTile); log("failed to add tile with status=", WriteDtStatus {addStatus}); return makeUpdateNavMeshStatus(removed, false); } } } const auto locked = navMeshCacheItem.lock(); auto& navMesh = locked->getValue(); const auto tileRef = navMesh.getTileRefAt(x, y, 0); const auto removed = dtStatusSucceed(navMesh.removeTile(tileRef, nullptr, nullptr)); const auto addStatus = navMesh.addTile(cachedNavMeshData.get().mValue, cachedNavMeshData.get().mSize, doNotTransferOwnership, 0, 0); if (dtStatusSucceed(addStatus)) { locked->setUsedTile(changedTile, std::move(cachedNavMeshData)); return makeUpdateNavMeshStatus(removed, true); } else { if (removed) locked->removeUsedTile(changedTile); log("failed to add tile with status=", WriteDtStatus {addStatus}); return makeUpdateNavMeshStatus(removed, false); } }