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:
@@ -105,6 +105,12 @@ pub fn orbit_camera_control(
|
|||||||
// (local-axis rotation) — this preserves a stable horizon line as
|
// (local-axis rotation) — this preserves a stable horizon line as
|
||||||
// long as `rotation` doesn't already pitch past 90°.
|
// long as `rotation` doesn't already pitch past 90°.
|
||||||
orbit.rotation = yaw * orbit.rotation * pitch;
|
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 {
|
} else {
|
||||||
mouse_motion.clear();
|
mouse_motion.clear();
|
||||||
@@ -125,11 +131,13 @@ pub fn orbit_camera_control(
|
|||||||
// the offset is `rotation * +Z * distance`.
|
// the offset is `rotation * +Z * distance`.
|
||||||
let offset = orbit.rotation * Vec3::new(0.0, 0.0, orbit.distance);
|
let offset = orbit.rotation * Vec3::new(0.0, 0.0, orbit.distance);
|
||||||
let position = orbit.target + offset;
|
let position = orbit.target + offset;
|
||||||
*transform = Transform::from_translation(position)
|
|
||||||
.looking_at(orbit.target, Vec3::Y)
|
// Use `orbit.rotation` directly — it already encodes the correct look
|
||||||
// Re-apply rotation to recover the roll component that `looking_at`
|
// direction (rotation * -Z points toward the target). Do NOT call
|
||||||
// would otherwise discard (matters when the camera is upside-down).
|
// `looking_at` then `with_rotation`; that computes a correct rotation
|
||||||
.with_rotation(orbit.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
|
/// Resource: set by UI buttons (e.g. "Center View") to request the orbit camera
|
||||||
|
|||||||
Reference in New Issue
Block a user