diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index d3015792..0d6209bb 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -2064,9 +2064,15 @@ void M2Renderer::render(const Camera& camera, const glm::mat4& view, const glm:: // Apply per-batch blend mode from M2 material (only if changed) // 0=Opaque, 1=AlphaKey, 2=Alpha, 3=Add, 4=Mod, 5=Mod2x, 6=BlendAdd, 7=Screen + // Glow textures (colorKeyBlack) are designed for additive blending; + // override non-additive modes to prevent black backgrounds. + uint16_t effectiveBlend = batch.blendMode; + if (batch.colorKeyBlack && effectiveBlend >= 1 && effectiveBlend <= 2) { + effectiveBlend = 3; // Force additive for glow textures + } bool batchTransparent = false; - if (batch.blendMode != lastBlendMode) { - switch (batch.blendMode) { + if (effectiveBlend != lastBlendMode) { + switch (effectiveBlend) { case 0: // Opaque glBlendFunc(GL_ONE, GL_ZERO); break; @@ -2097,11 +2103,11 @@ void M2Renderer::render(const Camera& camera, const glm::mat4& view, const glm:: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; } - lastBlendMode = batch.blendMode; - shader->setUniform("uBlendMode", static_cast(batch.blendMode)); + lastBlendMode = effectiveBlend; + shader->setUniform("uBlendMode", static_cast(effectiveBlend)); } else { // Still need to know if batch is transparent for depth mask logic - batchTransparent = (batch.blendMode >= 2); + batchTransparent = (effectiveBlend >= 2); } // Disable depth writes for transparent/additive batches @@ -2940,12 +2946,26 @@ GLuint M2Renderer::loadTexture(const std::string& path, uint32_t texFlags) { // 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; - } + // Detect glow-like textures by pixel content: mostly dark with some bright pixels. + // These are flame/glow cards where black = transparent (designed for additive blend). + uint32_t totalPixels = 0; + uint32_t darkPixels = 0; + uint32_t brightPixels = 0; + for (size_t i = 0; i + 3 < blp.data.size(); i += 4) { + uint8_t r = blp.data[i], g = blp.data[i+1], b = blp.data[i+2]; + if (blp.data[i+3] != 255) hasAlpha = true; + uint8_t mx = std::max({r, g, b}); + totalPixels++; + if (mx < 26) darkPixels++; // near-black (<~0.1) + else if (mx > 180) brightPixels++; // bright (>~0.7) } + bool glowByContent = false; + if (totalPixels > 0) { + float darkRatio = static_cast(darkPixels) / totalPixels; + // Glow texture: >60% dark pixels with some bright pixels present + glowByContent = (darkRatio > 0.60f && brightPixels > 0); + } + bool colorKeyBlack = colorKeyBlackHint || glowByContent; GLuint textureID; glGenTextures(1, &textureID); @@ -2970,12 +2990,12 @@ GLuint M2Renderer::loadTexture(const std::string& path, uint32_t texFlags) { size_t base = static_cast(blp.width) * static_cast(blp.height) * 4ull; e.approxBytes = base + (base / 3); e.hasAlpha = hasAlpha; - e.colorKeyBlack = colorKeyBlackHint; + e.colorKeyBlack = colorKeyBlack; e.lastUse = ++textureCacheCounter_; textureCacheBytes_ += e.approxBytes; textureCache[key] = e; textureHasAlphaById_[textureID] = hasAlpha; - textureColorKeyBlackById_[textureID] = colorKeyBlackHint; + textureColorKeyBlackById_[textureID] = colorKeyBlack; LOG_DEBUG("M2: Loaded texture: ", path, " (", blp.width, "x", blp.height, ")"); return textureID;