Quaternion issue

I’m developing a system whereby one is able to create keyframes at different points in time. This keyframe (array) includes the quaternion values of an element at a particular time:

array[1] = {
//...
"rotation": [o.quaternion._x, o.quaternion._y, o.quaternion._z, o.quaternion._w],
//...
 } 

As a video plays, the system tweens the variables between the two keyframes (sok[a][1] and sok[b][1] like so:

q.x = sok[a][1].rotation[0] + (sok[b][1].rotation[0] - sok[a][1].rotation[0]) * percent * -1
q.y = sok[a][1].rotation[1] + (sok[b][1].rotation[1] - sok[a][1].rotation[1]) * percent * -1
q.z = sok[a][1].rotation[2] + (sok[b][1].rotation[2] - sok[a][1].rotation[2]) * percent * -1
q.w = sok[a][1].rotation[3] + (sok[b][1].rotation[3] - sok[a][1].rotation[3]) * percent * -1
object[c][o].quaternion._x = q.x
object[c][o].quaternion._y = q.y
object[c][o].quaternion._z = q.z
object[c][o].quaternion._w = q.w

The problem is that as the object turns it appears to “sqeeze” in shape and does not continue to rotate gradually in the same direction but takes a ‘short cut.’

*Update: the issue is not related to the simultaneous tweening of the object’s scale. (I turned off scale tweening completely to test). Not sure if the issue is related to the rotation of the object by the TransformControls or our method used to tween the quaternion.

Any thoughts are appreciated.

This happens because of the way Quaternions operate. You can’t linearly interpolate them as you would with regular vectors. Quaternion’s are internally coupled in order to represent a specific rotation state. To represent the correct behavior you need to apply a Spherical Linear Interpolation.

Three.js offers a utility function Quaternion.slerp that can be used for this purpose. In your use case it is recommended the use of the static method.

THREE.Quaternion.slerp( startQuaternion, endQuaternion, mesh.quaternion, t );
1 Like

Thanks for the tip Sciecode.
I actually resolved the issue without slerp ***

var q = new THREE.Quaternion().normalize();    
q.x = sok[a][1].rotation[0] + (sok[b][1].rotation[0] - sok[a][1].rotation[0]) * percent * -1
q.y = sok[a][1].rotation[1] + (sok[b][1].rotation[1] - sok[a][1].rotation[1]) * percent * -1
q.z = sok[a][1].rotation[2] + (sok[b][1].rotation[2] - sok[a][1].rotation[2]) * percent * -1
q.w = sok[a][1].rotation[3] + (sok[b][1].rotation[3] - sok[a][1].rotation[3]) * percent * -1
q.normalize() // ***
object[c][o].rotation.setFromQuaternion( q ) // ***