Tra­di­tion­al­ly, quater­nion slerp() in Mag­num was done like this:

\begin{array}{rcl} \theta & = & \arccos \left( \frac{q_A \cdot q_B}{|q_A| |q_B|} \right) = \arccos(q_A \cdot q_B) \\[5pt] q_{SLERP} & = & \cfrac{\sin((1 - t) \theta) q_A + \sin(t \theta) q_B}{\sin(\theta)} \end{array}

Noth­ing ob­vi­ous­ly wrong there, right? Or at least I thought so. While im­ple­ment­ing all sorts of in­ter­po­la­tion meth­ods for the new An­i­ma­tion frame­work that’s part of the up­com­ing Mag­num re­lease, I dis­cov­ered that the An­i­mat­ed Tri­an­gle glTF sam­ple is ro­tat­ing in a fun­ny way: first it goes three quar­ters of the way and then in­stead of fin­ish­ing the cir­cle it goes back­wards. Ap­par­ent­ly I was not the on­ly one with this prob­lem.

Hav­ing a flash­back to four years ago when I was im­ple­ment­ing a sim­ple an­i­ma­tion sys­tem for 2D touch­screen UIs, the first thing that came to my mind is that the an­i­ma­tion da­ta is wrong — in­stead of go­ing from 270° to 360°, the an­i­ma­tion re­quests to go from 270° to 0° and some­how the glTF play­ers and im­porters in­ter­pret that as go­ing for­ward in­stead of back­ward and no­body ques­tioned the be­hav­ior un­til now.

See­ing the com­ment about which view­er was used for ver­i­fi­ca­tion of the sam­ple, I first thought these im­ple­men­ta­tions were a bit “spe­cial” and it’s not usu­al to do it like this. Turns out I was wrong (of course) — the Short­est Path quater­nion slerp is the com­mon way:

\begin{array}{rcl} d & = & q_A \cdot q_B \\ {\color{m-info} q'_A} & {\color{m-info} =} & {\color{m-info} \begin{cases} {\color{m-default} \phantom{-}q_A}, & d \ge 0 \\ -q_A, & d < 0 \end{cases} }\\[15pt] \theta & = & \arccos({\color{m-info}|}d{\color{m-info}|}) \\ q_{SLERP} & = & \cfrac{\sin((1 - t) \theta) {\color{m-info} q'_A} + \sin(t \theta) q_B}{\sin(\theta)} \end{array}

But the seed of doubt had al­ready been plant­ed and so I was still un­sure if this is the right way to do it — the short­est-path choice ba­si­cal­ly takes one de­gree of free­dom away from the us­er. Googling around, I found var­i­ous peo­ple ask­ing how to by­pass the short­est path as­pect (Uni­ty Fo­rum, Red­dit r/Uni­ty3D) and get­ting ex­treme­ly dense replies along the lines of “you are ask­ing for the im­pos­si­ble”, “there are in­fi­nite ways to do that” or “go through Eu­ler an­gles in­stead”.

Spline quaternion interpolation and shortest path Be­sides the above, with an­i­ma­tions us­ing the glTF cu­bic Her­mite spline quater­nion in­ter­po­la­tion it’s not pos­si­ble to do any short­est-path ro­ta­tion like this — the in­ter­po­lat­ed ro­ta­tion has to fol­low the de­fined spline curve. In prac­tice that means switch­ing a spline-in­ter­po­lat­ed an­i­ma­tion track to a lin­ear in­ter­po­la­tion would to­tal­ly change its be­hav­ior — sud­den­ly ro­ta­tions would go the oth­er way in­stead of be­ing just less smooth.

Give the users a choice In or­der to pre­vent Mag­num users from such atroc­i­ties, I de­cid­ed to pro­vide sup­port for both short­est-path and non-short­est-path in­ter­po­la­tion. I named the func­tions Math::slerp() and Math::slerp­Short­est­Path() (and not slerp() and slerpNotShortestPath() ) to sug­gest the one with the longer name is do­ing some­thing ex­tra and might not be al­ways the best choice. Here’s the sta­tus of such sup­port in var­i­ous en­gines I checked. Hav­ing sup­port for ba­sic quater­nion lerp next to slerp is cru­cial, as it’s just a vec­tor in­ter­po­la­tion with a renor­mal­iza­tion step af­ter — a much faster op­er­a­tion suit­ed for cas­es where pre­ci­sion is not as im­por­tant (such as most an­i­ma­tions with dense keyframes): lerp lerp

SP slerp slerp

SP javax.vecmath [1] ✘ ✘ ✘ ✔ javagl [2] ✘ ✘ ✘ ✔ Unity 3D [3] ✘ ✔ ✘ ✔ Unreal Engine [4] ? • ✔ ✔ id Tech 4 (Doom 3) [5] ✘ ✘ ✘ ✔ three.js [6] ✘ ✘ ✘ ✔ GLM [7] • ✘ ✘ ✔ Eigen [8] ✘ ✘ ✘ ✔ Magnum::Math [9] ✔ ✔ ✔ ✔