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.
This commit is contained in:
Kelsi 2026-02-05 17:30:08 -08:00
parent 125cf588bb
commit 12a7604450
3 changed files with 62 additions and 0 deletions

View file

@ -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

View file

@ -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.

View file

@ -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<int>(std::floor(p.x / SPATIAL_CELL_SIZE)),