From 1d2c1d2fa74272e43c670481e9fcf538821cb3a1 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Thu, 17 Jul 2025 19:44:46 +0200 Subject: [PATCH] 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 --- source/blender/io/usd/intern/usd_skel_convert.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/blender/io/usd/intern/usd_skel_convert.cc b/source/blender/io/usd/intern/usd_skel_convert.cc index 9066cf07180..32ae7108752 100644 --- a/source/blender/io/usd/intern/usd_skel_convert.cc +++ b/source/blender/io/usd/intern/usd_skel_convert.cc @@ -221,6 +221,7 @@ void import_skeleton_curves(Main *bmain, } /* Set the curve samples. */ + blender::Array 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();