From 39f4a433ffe814bfad8e0a540f146874367b40f9 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 08:55:49 -0700 Subject: [PATCH] fix(camera): NaN-safe getRight/getUp when forward ~= world up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If forward is parallel to (0,0,1) — camera staring straight up or down — the cross product is zero and glm::normalize returned NaN. That NaN flowed into glm::lookAt and produced a NaN view matrix. The editor camera clamps pitch to +/-89 so it doesn't trigger, but other call sites or scripted test paths could construct a Camera at +/-90 and immediately blow up. Length-check the cross and fall back to world +X / +Z. --- src/rendering/camera.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/rendering/camera.cpp b/src/rendering/camera.cpp index a169d9fc..bd3e9161 100644 --- a/src/rendering/camera.cpp +++ b/src/rendering/camera.cpp @@ -39,12 +39,20 @@ glm::vec3 Camera::getForward() const { } glm::vec3 Camera::getRight() const { - // Use Z-up for WoW coordinate system - return glm::normalize(glm::cross(getForward(), glm::vec3(0.0f, 0.0f, 1.0f))); + // Use Z-up for WoW coordinate system. If forward is parallel to the up + // axis (camera staring straight up/down), cross is zero and normalize + // returns NaN — fall back to world +X so view/proj stay finite. + glm::vec3 c = glm::cross(getForward(), glm::vec3(0.0f, 0.0f, 1.0f)); + float len = glm::length(c); + if (len < 1e-6f) return glm::vec3(1.0f, 0.0f, 0.0f); + return c / len; } glm::vec3 Camera::getUp() const { - return glm::normalize(glm::cross(getRight(), getForward())); + glm::vec3 c = glm::cross(getRight(), getForward()); + float len = glm::length(c); + if (len < 1e-6f) return glm::vec3(0.0f, 0.0f, 1.0f); + return c / len; } void Camera::setJitter(float jx, float jy) {