From 81a9970c91b6525ad65c87423f0b72a24695fa9b Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 3 Apr 2026 16:11:45 -0700 Subject: [PATCH] fix(rendering): add warnings for silent texture fallbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M2 particle/ribbon/batch, terrain layer, and WMO material texture resolution paths were silently falling back to white textures when indices were out of range — making missing texture issues hard to diagnose. Add LOG_WARNING at each silent failure point with model name, index details, and array sizes. --- src/rendering/m2_renderer.cpp | 12 ++++++++++++ src/rendering/terrain_renderer.cpp | 16 ++++++++++++++++ src/rendering/wmo_renderer.cpp | 8 ++++++++ 3 files changed, 36 insertions(+) diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index a5d8f546..d7bae447 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -1238,6 +1238,10 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { uint16_t texIdx = model.particleEmitters[ei].texture; if (texIdx < allTextures.size() && allTextures[texIdx] != nullptr) { gpuModel.particleTextures[ei] = allTextures[texIdx]; + } else { + LOG_WARNING("M2 '", model.name, "' particle emitter[", ei, + "] texture index ", texIdx, " out of range (", allTextures.size(), + " textures) — using white fallback"); } } @@ -1279,6 +1283,11 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { ? model.textureLookup[texLookupIdx] : UINT32_MAX; if (texIdx < allTextures.size() && allTextures[texIdx] != nullptr) { gpuModel.ribbonTextures[ri] = allTextures[texIdx]; + } else { + LOG_WARNING("M2 '", model.name, "' ribbon emitter[", ri, + "] texLookup=", texLookupIdx, " resolved texIdx=", texIdx, + " out of range (", allTextures.size(), + " textures) — using white fallback"); } // Allocate descriptor set (reuse particleTexLayout_ = single sampler) if (particleTexLayout_ && materialDescPool_) { @@ -1348,6 +1357,9 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { bgpu.texFlags = static_cast(model.textures[texIdx].flags & 0x3); } } else if (!allTextures.empty()) { + LOG_WARNING("M2 '", model.name, "' batch textureIndex ", batch.textureIndex, + " out of range (textureLookup size=", model.textureLookup.size(), + ") — falling back to texture[0]"); tex = allTextures[0]; texFailed = !textureLoadFailed.empty() && textureLoadFailed[0]; if (!textureKeysLower.empty()) { diff --git a/src/rendering/terrain_renderer.cpp b/src/rendering/terrain_renderer.cpp index bdd554f9..0fe8816e 100644 --- a/src/rendering/terrain_renderer.cpp +++ b/src/rendering/terrain_renderer.cpp @@ -344,6 +344,9 @@ bool TerrainRenderer::loadTerrain(const pipeline::TerrainMesh& mesh, if (baseTexId < texturePaths.size()) { gpuChunk.baseTexture = loadTexture(texturePaths[baseTexId]); } else { + LOG_WARNING("Terrain[", tileX, ",", tileY, "] chunk[", x, ",", y, + "] base textureId ", baseTexId, " >= texturePaths size ", + texturePaths.size(), " — white fallback"); gpuChunk.baseTexture = whiteTexture.get(); } @@ -354,6 +357,11 @@ bool TerrainRenderer::loadTerrain(const pipeline::TerrainMesh& mesh, VkTexture* layerTex = whiteTexture.get(); if (layer.textureId < texturePaths.size()) { layerTex = loadTexture(texturePaths[layer.textureId]); + } else { + LOG_WARNING("Terrain[", tileX, ",", tileY, "] chunk[", x, ",", y, + "] layer[", i, "] textureId ", layer.textureId, + " >= texturePaths size ", texturePaths.size(), + " — white fallback"); } gpuChunk.layerTextures[li] = layerTex; @@ -445,6 +453,9 @@ bool TerrainRenderer::loadTerrainIncremental(const pipeline::TerrainMesh& mesh, if (baseTexId < texturePaths.size()) { gpuChunk.baseTexture = loadTexture(texturePaths[baseTexId]); } else { + LOG_WARNING("Terrain[", tileX, ",", tileY, "] chunk[", cx, ",", cy, + "] base textureId ", baseTexId, " >= texturePaths size ", + texturePaths.size(), " — white fallback"); gpuChunk.baseTexture = whiteTexture.get(); } @@ -455,6 +466,11 @@ bool TerrainRenderer::loadTerrainIncremental(const pipeline::TerrainMesh& mesh, VkTexture* layerTex = whiteTexture.get(); if (layer.textureId < texturePaths.size()) { layerTex = loadTexture(texturePaths[layer.textureId]); + } else { + LOG_WARNING("Terrain[", tileX, ",", tileY, "] chunk[", cx, ",", cy, + "] layer[", i, "] textureId ", layer.textureId, + " >= texturePaths size ", texturePaths.size(), + " — white fallback"); } gpuChunk.layerTextures[li] = layerTex; diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index 3d401496..0396868a 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -575,7 +575,15 @@ bool WMORenderer::loadModel(const pipeline::WMOModel& model, uint32_t id) { tex = modelData.textures[texIndex]; hasTexture = (tex != nullptr && tex != whiteTexture_.get()); if (!tex) tex = whiteTexture_.get(); + } else { + LOG_WARNING("WMO ", id, " batch materialId=", batch.materialId, + " texIndex=", texIndex, " >= textures size ", + modelData.textures.size(), " — white fallback"); } + } else { + LOG_WARNING("WMO ", id, " batch materialId=", batch.materialId, + " >= materialTextureIndices size ", + modelData.materialTextureIndices.size(), " — white fallback"); } bool alphaTest = false;