Fix Stormwind bridge lamp glow: detect glow textures by pixel content,

force additive blending for colorKeyBlack batches

Two key changes:

1. Detect glow-like textures by analyzing pixel content during loading:
   textures that are >60% near-black with some bright pixels are flagged
   as colorKeyBlack regardless of filename. This catches glow textures
   like Stormwind street lamps whose paths don't contain keywords like
   "lamp" or "lantern".

2. Force additive blending (mode 3) for batches with colorKeyBlack
   textures that have blendMode 1 or 2. These textures are designed for
   additive blending where black = transparent, but some M2 files specify
   Alpha or AlphaKey blend modes which cause black areas to render as
   solid dark disks instead of being invisible.
This commit is contained in:
Kelsi 2026-02-19 18:27:21 -08:00
parent 32cc13d1b2
commit b2d43f702f

View file

@ -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<int>(batch.blendMode));
lastBlendMode = effectiveBlend;
shader->setUniform("uBlendMode", static_cast<int>(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<float>(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<size_t>(blp.width) * static_cast<size_t>(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;