bevy_xpbd
bevy_xpbd copied to clipboard
Fix real part of quaternion derivatives
trafficstars
Objective
The first-order approximations to quaternion rotations were wrong in a couple of places - rather than the angular velocity * dt/2 quaternions being purely imaginary they were being given a real part related to the real part of the existing quaternion. I think this got mis-translated in commit c1bcff1 from
let q = Quaternion::from_vec4(ang_vel.0.extend(0.0)) * rot.0;
let effective_dq = locked_axes
.apply_to_angular_velocity(delta_secs * 0.5 * q.xyz())
.extend(delta_secs * 0.5 * q.w);
// avoid triggering bevy's change detection unnecessarily
let delta = Quaternion::from_vec4(effective_dq);
if delta != Quaternion::from_xyzw(0.0, 0.0, 0.0, 0.0) {
rot.0 = (rot.0 + delta).normalize();
}
to
let delta_rot = Quaternion::from_vec4(
(ang_vel * delta_seconds / 2.0).extend(rot.w * delta_seconds / 2.0),
) * rot.0;
if delta_rot.w != 0.0 && delta_rot.is_finite() {
rot.0 = (rot.0 + delta_rot).normalize();
}
(Note in the "before" case, the .extend(delta_secs * 0.5 * q.w) is taking the w component from the rotation defined on the previous line (i.e. the 0.0 it's been extended by), not from rot!)
There are a few other places that do this quaternion maths: in the collider backend and the angular and positional constraints, and those all still extend by 0.0
Solution
- Change the two cases which extend by non-zero amounts to extend by zero. I've also rewritten the check to correctly identify zero angular velocity and not update the rotation in that case.