From cfd257aa78be5cbd373f3cb1c3fd1b3d81cd2beb Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 08:51:31 -0700 Subject: [PATCH] fix(m2): skip NaN vertices when computing tight model bounds glm::min/max on NaN is implementation-defined, so a single bad vertex would propagate NaN into the camera-occlusion and culling AABB used by the runtime. WOM/M2 loaders already scrub but defense in depth catches anything they miss. Falls back to a unit box if every vertex is bad. --- src/rendering/m2_renderer.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index 9c78d3cc..f515c3cb 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -1170,9 +1170,20 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { tightMin = glm::vec3(std::numeric_limits::max()); tightMax = glm::vec3(-std::numeric_limits::max()); for (const auto& v : model.vertices) { + // Skip NaN-positioned vertices — would corrupt the bounds + // (glm::min on NaN is implementation-defined) and feed NaN + // into the camera-occlusion / culling AABB. + if (!std::isfinite(v.position.x) || !std::isfinite(v.position.y) || + !std::isfinite(v.position.z)) continue; tightMin = glm::min(tightMin, v.position); tightMax = glm::max(tightMax, v.position); } + // If all vertices were NaN (very unlikely after the loader scrub + // but defense in depth), fall back to a unit box around origin. + if (tightMin.x > tightMax.x) { + tightMin = glm::vec3(-1.0f); + tightMax = glm::vec3(1.0f); + } } // Classify model from name and geometry — pure function, no GPU dependencies.