From e7a6933411bcfb3ea76301be568098f37a71c8fb Mon Sep 17 00:00:00 2001 From: treeform Date: Mon, 21 Sep 2026 08:45:28 -0700 Subject: [PATCH] Fix uninitialized roll in toAngles toAngles(Vec3) never assigned result.z and toAngles(Mat4) skipped it in the gimbal lock branch. Release builds use noinit, so those fields were uninitialized memory and debug and release builds returned different bits for the same input. Found by floaty's cross build digest test. Co-Authored-By: Claude Fable 5.1 --- src/vmath.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vmath.nim b/src/vmath.nim index 62d3bfe..52fa876 100644 --- a/src/vmath.nim +++ b/src/vmath.nim @@ -1666,12 +1666,14 @@ proc toAngles*[T](a: GVec3[T]): GVec3[T] = ## roll (z rotation) - always 0 in vector case ## All angles assume radians. if a == gvec3[T](T(0), T(0), T(0)): - return + return gvec3[T](T(0), T(0), T(0)) let yaw = -arctan2(a.x, a.z) pitch = -arctan2(sqrt(a.x*a.x + a.z*a.z), a.y) + T(PI/2) + # Every field is assigned because release builds use noinit. result.x = pitch.fixAngle result.y = yaw.fixAngle + result.z = T(0) proc toAngles*[T](origin, target: GVec3[T]): GVec3[T] = ## Gives Euler angles from origin to target @@ -1691,8 +1693,10 @@ proc toAngles*[T](m: GMat4[T]): GVec3[T] = let sy = clamp(-m[2, 1], T(-1), T(1)) result.x = arcsin(sy) if abs(sy) > T(0.9999999): - # Degenerate case (gimbal lock). + # Degenerate case (gimbal lock). Roll is folded into yaw and set to + # zero explicitly because release builds use noinit. result.y = arctan2(-m[0, 2], m[0, 0]) + result.z = T(0) else: # Normal case. result.y = arctan2(m[2, 0], m[2, 2])