fix(camera): use orbit rotation directly, normalize quaternion each frame

Replace looking_at + with_rotation with a direct rotation assignment —
orbit.rotation already encodes the correct look direction so the
intermediate looking_at call was redundant and discarded. Also re-normalize
the quaternion every frame to prevent floating-point drift from repeated
multiplications.
This commit is contained in:
2026-06-10 10:01:57 -04:00
parent 376d6a5913
commit 9395dfbede

View File

@@ -105,6 +105,12 @@ pub fn orbit_camera_control(
// (local-axis rotation) — this preserves a stable horizon line as
// long as `rotation` doesn't already pitch past 90°.
orbit.rotation = yaw * orbit.rotation * pitch;
// Re-normalize to counteract floating-point drift from repeated
// multiplications. Without this the quaternion gradually loses unit
// length, introducing an implicit scale that manifests as visual
// stretching / skewing of the scene.
orbit.rotation = orbit.rotation.normalize();
}
} else {
mouse_motion.clear();
@@ -125,11 +131,13 @@ pub fn orbit_camera_control(
// the offset is `rotation * +Z * distance`.
let offset = orbit.rotation * Vec3::new(0.0, 0.0, orbit.distance);
let position = orbit.target + offset;
*transform = Transform::from_translation(position)
.looking_at(orbit.target, Vec3::Y)
// Re-apply rotation to recover the roll component that `looking_at`
// would otherwise discard (matters when the camera is upside-down).
.with_rotation(orbit.rotation);
// Use `orbit.rotation` directly — it already encodes the correct look
// direction (rotation * -Z points toward the target). Do NOT call
// `looking_at` then `with_rotation`; that computes a correct rotation
// only to discard it, and hides the fact that the raw quaternion is the
// sole source of truth.
*transform = Transform::from_translation(position).with_rotation(orbit.rotation);
}
/// Resource: set by UI buttons (e.g. "Center View") to request the orbit camera