diff --git a/assets/shaders/wmo.frag.glsl b/assets/shaders/wmo.frag.glsl index a4bae057..c2b3b1cd 100644 --- a/assets/shaders/wmo.frag.glsl +++ b/assets/shaders/wmo.frag.glsl @@ -29,6 +29,9 @@ layout(set = 1, binding = 1) uniform WMOMaterial { float heightMapVariance; float normalMapStrength; int isLava; + float wmoAmbientR; + float wmoAmbientG; + float wmoAmbientB; }; layout(set = 1, binding = 2) uniform sampler2D uNormalHeightMap; @@ -185,7 +188,13 @@ void main() { } else if (unlit != 0) { result = texColor.rgb * shadow; } else if (isInterior != 0) { - vec3 mocv = max(VertColor.rgb, vec3(0.5)); + // WMO interior: vertex colors (MOCV) are pre-baked lighting from the artist. + // The MOHD ambient color tints/floors the vertex colors so dark spots don't + // go completely black, matching the WoW client's interior shading. + vec3 wmoAmbient = vec3(wmoAmbientR, wmoAmbientG, wmoAmbientB); + // Clamp ambient to at least 0.3 to avoid total darkness when MOHD color is zero + wmoAmbient = max(wmoAmbient, vec3(0.3)); + vec3 mocv = max(VertColor.rgb, wmoAmbient); result = texColor.rgb * mocv * shadow; } else { vec3 ldir = normalize(-lightDir.xyz); diff --git a/assets/shaders/wmo.frag.spv b/assets/shaders/wmo.frag.spv index 524dbd1e..3507f3c9 100644 Binary files a/assets/shaders/wmo.frag.spv and b/assets/shaders/wmo.frag.spv differ diff --git a/include/pipeline/wmo_loader.hpp b/include/pipeline/wmo_loader.hpp index ed2cf149..4b952b5b 100644 --- a/include/pipeline/wmo_loader.hpp +++ b/include/pipeline/wmo_loader.hpp @@ -185,6 +185,7 @@ struct WMOModel { uint32_t nDoodadDefs; uint32_t nDoodadSets; + glm::vec3 ambientColor; // MOHD ambient color (used for interior group lighting) glm::vec3 boundingBoxMin; glm::vec3 boundingBoxMax; diff --git a/include/rendering/wmo_renderer.hpp b/include/rendering/wmo_renderer.hpp index 4546d41c..50261865 100644 --- a/include/rendering/wmo_renderer.hpp +++ b/include/rendering/wmo_renderer.hpp @@ -353,7 +353,9 @@ private: float heightMapVariance; // 40 (low variance = skip POM) float normalMapStrength; // 44 (0=flat, 1=full, 2=exaggerated) int32_t isLava; // 48 (1=lava/magma UV scroll) - float pad[3]; // 52-60 padding to 64 bytes + float wmoAmbientR; // 52 (interior ambient color R) + float wmoAmbientG; // 56 (interior ambient color G) + float wmoAmbientB; // 60 (interior ambient color B) }; // 64 bytes total /** @@ -472,6 +474,7 @@ private: std::vector groups; glm::vec3 boundingBoxMin; glm::vec3 boundingBoxMax; + glm::vec3 wmoAmbientColor{0.5f, 0.5f, 0.5f}; // From MOHD, used for interior lighting bool isLowPlatform = false; // Doodad templates (M2 models placed in WMO, stored for instancing) diff --git a/src/pipeline/wmo_loader.cpp b/src/pipeline/wmo_loader.cpp index 22a4df42..076e4579 100644 --- a/src/pipeline/wmo_loader.cpp +++ b/src/pipeline/wmo_loader.cpp @@ -109,7 +109,11 @@ WMOModel WMOLoader::load(const std::vector& wmoData) { model.nDoodadDefs = read(wmoData, offset); model.nDoodadSets = read(wmoData, offset); - [[maybe_unused]] uint32_t ambColor = read(wmoData, offset); // Ambient color (BGRA) + uint32_t ambColor = read(wmoData, offset); // Ambient color (BGRA) + // Unpack BGRA bytes to normalized [0,1] RGB + model.ambientColor.r = ((ambColor >> 16) & 0xFF) / 255.0f; + model.ambientColor.g = ((ambColor >> 8) & 0xFF) / 255.0f; + model.ambientColor.b = ((ambColor >> 0) & 0xFF) / 255.0f; [[maybe_unused]] uint32_t wmoID = read(wmoData, offset); model.boundingBoxMin.x = read(wmoData, offset); diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index 51d8c2a2..4d52fd76 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -414,6 +414,7 @@ bool WMORenderer::loadModel(const pipeline::WMOModel& model, uint32_t id) { modelData.id = id; modelData.boundingBoxMin = model.boundingBoxMin; modelData.boundingBoxMax = model.boundingBoxMax; + modelData.wmoAmbientColor = model.ambientColor; { glm::vec3 ext = model.boundingBoxMax - model.boundingBoxMin; float horiz = std::max(ext.x, ext.y); @@ -681,6 +682,9 @@ bool WMORenderer::loadModel(const pipeline::WMOModel& model, uint32_t id) { matData.heightMapVariance = mb.heightMapVariance; matData.normalMapStrength = normalMapStrength_; matData.isLava = mb.isLava ? 1 : 0; + matData.wmoAmbientR = modelData.wmoAmbientColor.r; + matData.wmoAmbientG = modelData.wmoAmbientColor.g; + matData.wmoAmbientB = modelData.wmoAmbientColor.b; if (matBuf.info.pMappedData) { memcpy(matBuf.info.pMappedData, &matData, sizeof(matData)); }