diff --git a/include/rendering/wmo_renderer.hpp b/include/rendering/wmo_renderer.hpp index 93be44d2..3f8e9997 100644 --- a/include/rendering/wmo_renderer.hpp +++ b/include/rendering/wmo_renderer.hpp @@ -227,6 +227,9 @@ public: bool loadFloorCache(const std::string& filepath); size_t getFloorCacheSize() const { return precomputedFloorGrid.size(); } + // Pre-compute floor cache for all loaded WMO instances + void precomputeFloorCache(); + private: /** * WMO group GPU resources diff --git a/src/core/application.cpp b/src/core/application.cpp index 18adb956..d24bc81c 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1270,6 +1270,12 @@ void Application::startSinglePlayer() { LOG_INFO("Terrain streaming complete: ", terrainMgr->getLoadedTileCount(), " tiles loaded"); + // Pre-compute floor cache if it wasn't loaded from file + if (renderer->getWMORenderer() && renderer->getWMORenderer()->getFloorCacheSize() == 0) { + showStatus("Pre-computing collision cache..."); + renderer->getWMORenderer()->precomputeFloorCache(); + } + // Re-snap camera to ground now that all surrounding tiles are loaded // (the initial reset inside loadTestTerrain only had 1 tile). if (spawnSnapToGround && renderer->getCameraController()) { @@ -1396,6 +1402,11 @@ void Application::teleportTo(int presetIndex) { } LOG_INFO("Teleport terrain streaming complete: ", terrainMgr->getLoadedTileCount(), " tiles loaded"); + + // Pre-compute floor cache for the new area + if (renderer->getWMORenderer()) { + renderer->getWMORenderer()->precomputeFloorCache(); + } } // Floor-snapping presets use camera reset. WMO-floor presets keep explicit Z. diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index bab6553c..0057f531 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -589,6 +589,54 @@ bool WMORenderer::loadFloorCache(const std::string& filepath) { return true; } +void WMORenderer::precomputeFloorCache() { + if (instances.empty()) { + core::Logger::getInstance().info("precomputeFloorCache: no instances to precompute"); + return; + } + + size_t startSize = precomputedFloorGrid.size(); + size_t samplesChecked = 0; + + core::Logger::getInstance().info("Pre-computing floor cache for ", instances.size(), " WMO instances..."); + + for (const auto& instance : instances) { + // Get world bounds for this instance + const glm::vec3& boundsMin = instance.worldBoundsMin; + const glm::vec3& boundsMax = instance.worldBoundsMax; + + // Sample reference Z is above the structure + float refZ = boundsMax.z + 10.0f; + + // Iterate over grid points within the bounds + float startX = std::floor(boundsMin.x / FLOOR_GRID_CELL_SIZE) * FLOOR_GRID_CELL_SIZE; + float startY = std::floor(boundsMin.y / FLOOR_GRID_CELL_SIZE) * FLOOR_GRID_CELL_SIZE; + + for (float x = startX; x <= boundsMax.x; x += FLOOR_GRID_CELL_SIZE) { + for (float y = startY; y <= boundsMax.y; y += FLOOR_GRID_CELL_SIZE) { + // Sample at grid cell center + float sampleX = x + FLOOR_GRID_CELL_SIZE * 0.5f; + float sampleY = y + FLOOR_GRID_CELL_SIZE * 0.5f; + + // Check if already cached + uint64_t key = floorGridKey(sampleX, sampleY); + if (precomputedFloorGrid.find(key) != precomputedFloorGrid.end()) { + continue; // Already computed + } + + samplesChecked++; + + // getFloorHeight will compute and cache the result + getFloorHeight(sampleX, sampleY, refZ); + } + } + } + + size_t newEntries = precomputedFloorGrid.size() - startSize; + core::Logger::getInstance().info("Floor cache precompute complete: ", samplesChecked, " samples checked, ", + newEntries, " new entries, total ", precomputedFloorGrid.size()); +} + WMORenderer::GridCell WMORenderer::toCell(const glm::vec3& p) const { return GridCell{ static_cast(std::floor(p.x / SPATIAL_CELL_SIZE)),