bevy_xpbd icon indicating copy to clipboard operation
bevy_xpbd copied to clipboard

Fix real part of quaternion derivatives

Open unpairedbracket opened this issue 1 year ago • 0 comments
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.

unpairedbracket avatar Aug 13 '24 00:08 unpairedbracket