rendering: fix WMO portal AABB transform for rotated WMOs

isPortalVisible() was computing the world-space AABB by directly
transforming pMin/pMax with the model matrix. This is incorrect for
rotated WMOs — when the model matrix includes rotations, components can
be swapped or negated, yielding an inverted AABB (worldMin.x >
worldMax.x) that causes frustum.intersectsAABB() to fail.

Fix: transform all 8 corners of the portal bounding box and take the
component-wise min/max, which gives the correct world-space AABB for any
rotation/scale. This was the root cause of portals being incorrectly
culled in rotated WMO instances (e.g. many dungeon and city WMOs).

Also squash the earlier spline-speed no-op fix (parse guid + float
instead of consuming the full packet for SMSG_SPLINE_SET_FLIGHT_SPEED
and friends) into this commit.
This commit is contained in:
Kelsi 2026-03-10 11:51:43 -07:00
parent 8152314ba8
commit 1180f0227c

View file

@ -2049,12 +2049,25 @@ bool WMORenderer::isPortalVisible(const ModelData& model, uint16_t portalIndex,
}
center /= static_cast<float>(portal.vertexCount);
// Transform bounds to world space for frustum test
glm::vec4 worldMin = modelMatrix * glm::vec4(pMin, 1.0f);
glm::vec4 worldMax = modelMatrix * glm::vec4(pMax, 1.0f);
// Transform all 8 corners to world space to build the correct world AABB.
// Direct transform of pMin/pMax is wrong for rotated WMOs — the matrix can
// swap or negate components, inverting min/max and causing frustum test failures.
const glm::vec3 corners[8] = {
{pMin.x, pMin.y, pMin.z}, {pMax.x, pMin.y, pMin.z},
{pMin.x, pMax.y, pMin.z}, {pMax.x, pMax.y, pMin.z},
{pMin.x, pMin.y, pMax.z}, {pMax.x, pMin.y, pMax.z},
{pMin.x, pMax.y, pMax.z}, {pMax.x, pMax.y, pMax.z},
};
glm::vec3 worldMin( std::numeric_limits<float>::max());
glm::vec3 worldMax(-std::numeric_limits<float>::max());
for (const auto& c : corners) {
glm::vec3 wc = glm::vec3(modelMatrix * glm::vec4(c, 1.0f));
worldMin = glm::min(worldMin, wc);
worldMax = glm::max(worldMax, wc);
}
// Check if portal AABB intersects frustum (more robust than point test)
return frustum.intersectsAABB(glm::vec3(worldMin), glm::vec3(worldMax));
return frustum.intersectsAABB(worldMin, worldMax);
}
void WMORenderer::getVisibleGroupsViaPortals(const ModelData& model,