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.