Hide M2 particle emitter volumes rendering as grey boxes

M2 models like OrgrimmarFloatingEmbers and OrgrimmarSmokeEmitter have a
simple box mesh (24 verts, 36 indices) meant only to define particle
emitter bounds. Their blendMode was 0 (opaque), causing them to render
as large grey boxes. Detect these by checking for box geometry with
particle emitters and large bounds (>5 units), then mark as invisible.
Also add ANTIPORTAL and batch-disable flag checks to WMO group filtering.
This commit is contained in:
Kelsi 2026-02-16 19:50:35 -08:00
parent 1cfe186c62
commit 6dd811a926
3 changed files with 23 additions and 3 deletions

View file

@ -426,6 +426,8 @@ bool WMOLoader::loadGroup(const std::vector<uint8_t>& 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<uint32_t>(groupData, mogpOffset);
bool isInterior = (group.flags & 0x2000) != 0;

View file

@ -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, ",

View file

@ -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;