Fix #141718: Discontinuous rotations on some USD skeleton imports

Due to ambiguity when decomposing the incoming skeleton joint matrix,
it's possible for the returned quaternion rotation to be "flipped" from
the perspective of prior/future frames due to the quaternion
double-cover property. This would manifest as glitchy animations and
subtly incorrect motion blur results during render.

This PR implements the traditional mitigation of comparing with the
prior frame's rotation data and negating the quat if necessary. An
alternate method to instead compare with the rest positions was
attempted but it still allowed flipped/discontinuous rotations for
spinning objects like wheels and propellers.

Pull Request: https://projects.blender.org/blender/blender/pulls/142026
This commit is contained in:
Jesse Yurkovich
2025-07-17 19:44:46 +02:00
committed by Jesse Yurkovich
parent 50ec3cb2d4
commit 1d2c1d2fa7

View File

@@ -221,6 +221,7 @@ void import_skeleton_curves(Main *bmain,
}
/* Set the curve samples. */
blender::Array<pxr::GfQuatf> prev_rot(joint_order.size());
uint bezt_index = 0;
for (const double frame : samples) {
pxr::VtMatrix4dArray joint_local_xforms;
@@ -251,6 +252,17 @@ void import_skeleton_curves(Main *bmain,
continue;
}
if (bezt_index > 0) {
/* Quaternion "neighborhood" check to prevent most cases of discontinuous rotations.
* Note: An alternate method, comparing to the rotation of the rest position rather than
* to the previous rotation, was attempted but yielded much worse results for joints
* representing objects that are supposed to spin, like wheels and propellers. */
if (pxr::GfDot(prev_rot[i], qrot) < 0.0f) {
qrot = -qrot;
}
}
prev_rot[i] = qrot;
const float re = qrot.GetReal();
const pxr::GfVec3f &im = qrot.GetImaginary();