diff --git a/src/pipeline/wmo_loader.cpp b/src/pipeline/wmo_loader.cpp index 8241f2cc..1c32f0e7 100644 --- a/src/pipeline/wmo_loader.cpp +++ b/src/pipeline/wmo_loader.cpp @@ -426,6 +426,8 @@ bool WMOLoader::loadGroup(const std::vector& groupData, } // Read MOGP header + // NOTE: In WMO group files, the MOGP data starts directly at flags + // (groupName/descriptiveGroupName are handled by the root WMO's MOGI chunk). uint32_t mogpOffset = offset; group.flags = read(groupData, mogpOffset); bool isInterior = (group.flags & 0x2000) != 0; diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index ba9f45a0..9be92c8d 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -1198,6 +1198,20 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { gpuModel.batches.push_back(bgpu); } + // Detect particle emitter volume models: box mesh (24 verts, 36 indices) + // with disproportionately large bounds. These are invisible bounding volumes + // that only exist to spawn particles — their mesh should never be rendered. + if (!isInvisibleTrap && gpuModel.vertexCount <= 24 && gpuModel.indexCount <= 36 + && !model.particleEmitters.empty()) { + glm::vec3 size = gpuModel.boundMax - gpuModel.boundMin; + float maxDim = std::max({size.x, size.y, size.z}); + if (maxDim > 5.0f) { + gpuModel.isInvisibleTrap = true; + LOG_DEBUG("M2 emitter volume hidden: '", model.name, "' size=(", + size.x, " x ", size.y, " x ", size.z, ")"); + } + } + models[modelId] = std::move(gpuModel); LOG_DEBUG("Loaded M2 model: ", model.name, " (", models[modelId].vertexCount, " vertices, ", diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index 9f102b8c..51a23305 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -371,6 +371,7 @@ bool WMORenderer::loadModel(const pipeline::WMOModel& model, uint32_t id) { } } + bool alphaTest = false; uint32_t blendMode = 0; if (batch.materialId < modelData.materialBlendModes.size()) { @@ -1206,9 +1207,11 @@ void WMORenderer::render(const Camera& camera, const glm::mat4& view, const glm: for (uint32_t gi : dl.visibleGroups) { const auto& group = model.groups[gi]; - // Skip groups with SHOW_SKYBOX flag (0x20000) — these are transparent - // sky windows meant to show the skybox behind them, not solid geometry - if (group.groupFlags & 0x20000) { + // Skip non-renderable groups: + // 0x20000 = SHOW_SKYBOX (transparent sky windows) + // 0x4000000 = ANTIPORTAL (occlusion planes, not visible geometry) + // 0x8000000 = disables batch rendering + if (group.groupFlags & (0x20000 | 0x4000000 | 0x8000000)) { continue; } @@ -1218,6 +1221,7 @@ void WMORenderer::render(const Camera& camera, const glm::mat4& view, const glm: continue; } + // STORMWIND.WMO specific fix: LOD shell visibility control // Combination of distance culling + backface culling for best results bool isLODShell = false;