From 12a7604450c2759f0d647eece64e5af73ca04f0b Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 5 Feb 2026 17:30:08 -0800 Subject: [PATCH] Automate WMO floor cache pre-computation Add precomputeFloorCache() method that samples all WMO bounds at load time and populates the persistent floor height grid. Called after terrain streaming completes (initial spawn and teleport) when cache is empty. --- include/rendering/wmo_renderer.hpp | 3 ++ src/core/application.cpp | 11 +++++++ src/rendering/wmo_renderer.cpp | 48 ++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) 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)),