diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index 455b0b1c..f6f51f08 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -312,15 +312,23 @@ GLuint CharacterRenderer::loadTexture(const std::string& path) { } if (allWhitespace) return whiteTexture; + auto normalizeKey = [](std::string key) { + std::replace(key.begin(), key.end(), '/', '\\'); + std::transform(key.begin(), key.end(), key.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + return key; + }; + std::string key = normalizeKey(path); + // Check cache - auto it = textureCache.find(path); + auto it = textureCache.find(key); if (it != textureCache.end()) return it->second; if (!assetManager || !assetManager->isInitialized()) { return whiteTexture; } - auto blpImage = assetManager->loadTexture(path); + auto blpImage = assetManager->loadTexture(key); if (!blpImage.isValid()) { core::Logger::getInstance().warning("Failed to load texture: ", path); // Do not cache failures as white. Some asset reads can fail transiently and @@ -341,7 +349,7 @@ GLuint CharacterRenderer::loadTexture(const std::string& path) { applyAnisotropicFiltering(); glBindTexture(GL_TEXTURE_2D, 0); - textureCache[path] = texId; + textureCache[key] = texId; core::Logger::getInstance().info("Loaded character texture: ", path, " (", blpImage.width, "x", blpImage.height, ")"); return texId; } diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index b0cad4df..83bba1f2 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -2674,14 +2674,22 @@ void M2Renderer::cleanupUnusedModels() { } GLuint M2Renderer::loadTexture(const std::string& path) { + auto normalizeKey = [](std::string key) { + std::replace(key.begin(), key.end(), '/', '\\'); + std::transform(key.begin(), key.end(), key.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + return key; + }; + std::string key = normalizeKey(path); + // Check cache - auto it = textureCache.find(path); + auto it = textureCache.find(key); if (it != textureCache.end()) { return it->second; } // Load BLP texture - pipeline::BLPImage blp = assetManager->loadTexture(path); + pipeline::BLPImage blp = assetManager->loadTexture(key); if (!blp.isValid()) { LOG_WARNING("M2: Failed to load texture: ", path); // Don't cache failures — transient StormLib thread contention can @@ -2706,7 +2714,7 @@ GLuint M2Renderer::loadTexture(const std::string& path) { glBindTexture(GL_TEXTURE_2D, 0); - textureCache[path] = textureID; + textureCache[key] = textureID; LOG_DEBUG("M2: Loaded texture: ", path, " (", blp.width, "x", blp.height, ")"); return textureID; diff --git a/src/rendering/terrain_renderer.cpp b/src/rendering/terrain_renderer.cpp index 77a38ff9..9718d4ed 100644 --- a/src/rendering/terrain_renderer.cpp +++ b/src/rendering/terrain_renderer.cpp @@ -223,14 +223,22 @@ TerrainChunkGPU TerrainRenderer::uploadChunk(const pipeline::ChunkMesh& chunk) { } GLuint TerrainRenderer::loadTexture(const std::string& path) { + auto normalizeKey = [](std::string key) { + std::replace(key.begin(), key.end(), '/', '\\'); + std::transform(key.begin(), key.end(), key.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + return key; + }; + std::string key = normalizeKey(path); + // Check cache first - auto it = textureCache.find(path); + auto it = textureCache.find(key); if (it != textureCache.end()) { return it->second; } // Load BLP texture - pipeline::BLPImage blp = assetManager->loadTexture(path); + pipeline::BLPImage blp = assetManager->loadTexture(key); if (!blp.isValid()) { LOG_WARNING("Failed to load texture: ", path); // Do not cache failure as white: MPQ/file reads can fail transiently @@ -261,7 +269,7 @@ GLuint TerrainRenderer::loadTexture(const std::string& path) { glBindTexture(GL_TEXTURE_2D, 0); // Cache texture - textureCache[path] = textureID; + textureCache[key] = textureID; LOG_DEBUG("Loaded texture: ", path, " (", blp.width, "x", blp.height, ")"); @@ -269,9 +277,16 @@ GLuint TerrainRenderer::loadTexture(const std::string& path) { } void TerrainRenderer::uploadPreloadedTextures(const std::unordered_map& textures) { + auto normalizeKey = [](std::string key) { + std::replace(key.begin(), key.end(), '/', '\\'); + std::transform(key.begin(), key.end(), key.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + return key; + }; for (const auto& [path, blp] : textures) { + std::string key = normalizeKey(path); // Skip if already cached - if (textureCache.find(path) != textureCache.end()) continue; + if (textureCache.find(key) != textureCache.end()) continue; if (!blp.isValid()) { // Don't poison cache with white on invalid preload; allow fallback // path to retry loading this texture later. @@ -292,7 +307,7 @@ void TerrainRenderer::uploadPreloadedTextures(const std::unordered_map(std::tolower(c)); }); + return key; + }; + std::string key = normalizeKey(path); + // Check cache first - auto it = textureCache.find(path); + auto it = textureCache.find(key); if (it != textureCache.end()) { return it->second; } // Load BLP texture - pipeline::BLPImage blp = assetManager->loadTexture(path); + pipeline::BLPImage blp = assetManager->loadTexture(key); if (!blp.isValid()) { core::Logger::getInstance().warning("WMO: Failed to load texture: ", path); // Do not cache failures as white. MPQ reads can fail transiently @@ -1654,7 +1662,7 @@ GLuint WMORenderer::loadTexture(const std::string& path) { glBindTexture(GL_TEXTURE_2D, 0); // Cache it - textureCache[path] = textureID; + textureCache[key] = textureID; core::Logger::getInstance().debug("WMO: Loaded texture: ", path, " (", blp.width, "x", blp.height, ")"); return textureID;