diff --git a/include/rendering/m2_renderer.hpp b/include/rendering/m2_renderer.hpp index fb887d29..064038fe 100644 --- a/include/rendering/m2_renderer.hpp +++ b/include/rendering/m2_renderer.hpp @@ -360,8 +360,10 @@ private: GLuint id = 0; size_t approxBytes = 0; uint64_t lastUse = 0; + bool hasAlpha = true; }; std::unordered_map textureCache; + std::unordered_map textureHasAlphaById_; size_t textureCacheBytes_ = 0; uint64_t textureCacheCounter_ = 0; size_t textureCacheBudgetBytes_ = 2048ull * 1024 * 1024; // Default, overridden at init diff --git a/src/pipeline/blp_loader.cpp b/src/pipeline/blp_loader.cpp index eb03e6b4..e0df4639 100644 --- a/src/pipeline/blp_loader.cpp +++ b/src/pipeline/blp_loader.cpp @@ -167,13 +167,8 @@ BLPImage BLPLoader::loadBLP2(const uint8_t* data, size_t size) { return BLPImage(); } - // DXT1 with alphaDepth=0 has no meaningful alpha channel, but the DXT1 - // color-key mode can produce alpha=0 pixels. Force all alpha to 255. - if (header->alphaDepth == 0) { - for (int i = 0; i < pixelCount; i++) { - image.data[i * 4 + 3] = 255; - } - } + // Note: DXT1 may encode 1-bit transparency via the color-key mode (c0 <= c1). + // Do not override alpha based on alphaDepth; preserve whatever the DXT decompressor produced. return image; } diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index 771ba9cd..59a60109 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -659,6 +659,7 @@ void M2Renderer::shutdown() { textureCache.clear(); textureCacheBytes_ = 0; textureCacheCounter_ = 0; + textureHasAlphaById_.clear(); if (whiteTexture != 0) { glDeleteTextures(1, &whiteTexture); whiteTexture = 0; @@ -2420,8 +2421,19 @@ void M2Renderer::renderM2Particles(const glm::mat4& view, const glm::mat4& proj) for (auto& [key, group] : groups) { if (group.vertexData.empty()) continue; - // Set blend mode - if (group.blendType == 4) { + // Set blend mode. Many classic glow textures use a black background with no alpha, + // and expect additive blending so black contributes nothing. + bool texHasAlpha = true; + if (auto it = textureHasAlphaById_.find(group.texture); it != textureHasAlphaById_.end()) { + texHasAlpha = it->second; + } + + uint8_t blendType = group.blendType; + if ((blendType == 1 || blendType == 2) && !texHasAlpha) { + blendType = 4; // Treat as additive fallback + } + + if (blendType == 3 || blendType == 4) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Additive } else { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Alpha @@ -2717,6 +2729,15 @@ GLuint M2Renderer::loadTexture(const std::string& path) { return whiteTexture; } + // Track whether the texture actually uses alpha (any pixel with alpha < 255). + bool hasAlpha = false; + for (size_t i = 3; i < blp.data.size(); i += 4) { + if (blp.data[i] != 255) { + hasAlpha = true; + break; + } + } + GLuint textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); @@ -2738,9 +2759,11 @@ GLuint M2Renderer::loadTexture(const std::string& path) { e.id = textureID; size_t base = static_cast(blp.width) * static_cast(blp.height) * 4ull; e.approxBytes = base + (base / 3); + e.hasAlpha = hasAlpha; e.lastUse = ++textureCacheCounter_; textureCacheBytes_ += e.approxBytes; textureCache[key] = e; + textureHasAlphaById_[textureID] = hasAlpha; LOG_DEBUG("M2: Loaded texture: ", path, " (", blp.width, "x", blp.height, ")"); return textureID;