I am provided with 3 quaternions. One for the shoulder, one for the elbow, and one for the hand. What I want is to obtain a single quaternion representing the rotation from the shoulder to the hand, essentially removing the elbow in the process. I have a diagram below to better illustrate this.

enter image description here

Assuming that both of the above sections (forearm and upper arm) are of the same length, how could this be calculated?

  • \$\begingroup\$ Do you have bone lengths as well, or you need just an orientation? \$\endgroup\$ – Kromster Nov 7 '18 at 5:41
  • \$\begingroup\$ @Kromster I really just need an orientation, I can assume the lengths to be arbitrary for my use case \$\endgroup\$ – simplyme Nov 7 '18 at 12:36
  • \$\begingroup\$ By arbitrary lengths you mean exactly what? Take a piece of paper and try to see how lengths drastically affect the result in 2D. \$\endgroup\$ – Kromster Nov 7 '18 at 12:42
  • \$\begingroup\$ @Kromster I meant that the lengths are the same and do not really matter, they could just be 1 unit in some unit. Not really looking for anything too exact. \$\endgroup\$ – simplyme Nov 7 '18 at 16:00

Assuming that both of the above sections (forearm and upper arm) are of the same length

Then let 1 unit of our coordinate system be the length of one arm segment. Let's also assume the joint quaternions we have are arranged so they point a unit "forward" vector along the length of the bone.

elbowOffsetFromShoulder = shoulderRotation * forward;

handOffsetFromShoulder = elbowOffsetFromShoulder + shoulderRotation * elbowRotation * forward;

Now we can form a quaternion that orients the forward vector to point from the shoulder to the hand with a LookRotation convenience method. This still leaves us with one degree of freedom: the twist of the rotation about the shoulder-hand line. We can arbitrarily choose to try to align the up vector of our resulting rotation as close as possible with the shoulder's local up.

shoulderToHandRotation = Quaternion.LookRotation(handOffsetFromShoulder, up);

If you're in an engine like Unity you'll have a convenience method like this already. If not, you can implement your own version, or construct a matrix with the desired orientation (eg. column 3 = normalize(handOffsetFromShoulder), column 2 = normalize(up - dot(up, column 3) * column 3, column 1 = cross(column 2, column3)) and convert the matrix to a quaternion.

Note that this quaternion generally points in a different direction than the forearm's orientation, so if you then stack a hand or pointer with a particular orientation on the end, it will need adjustment to stay aligned with its new parent, ie.

newHandRotation = Inverse(shoulderToHandRotation) * shoulderRotation * elbowRotation * handRotation;
  • \$\begingroup\$ This seems to be what I am looking for! Just to clarify however, would the forward vector be in relation from the torso toward the direction that the arm is in, or would it be the direction that the model is facing? Also, I am using Python/Blender’s mathutils to do this, would you happen to know of any premade implementations of LookRotation? \$\endgroup\$ – simplyme Nov 7 '18 at 16:12
  • \$\begingroup\$ The forward direction isn't in relation to any object, it's the abstract choice of which axis you've chosen to "mean" forward. Unity, for instance, adopts the convention that z+ is "forward" and y+ is "up". If for example in the upper arm's local coordinate space, the shoulder is at (0, 0, 0) and the elbow is at (0, 0, 6.3), then the "forward" direction is the z+ axis, and a unit vector in that direction is (0, 0, 1). As for doing this in Blender, the vector.toTrackQuat method looks promising, or you can construct a matrix as described above and use matrix.toQuat \$\endgroup\$ – DMGregory Nov 7 '18 at 16:27
  • \$\begingroup\$ Thanks! Final question, where exactly did you get that matrix from? \$\endgroup\$ – simplyme Nov 7 '18 at 17:18
  • \$\begingroup\$ I just built it following the rule that if you're transforming a vector like matrix * vector, then you can build the matrix out of columns corresponding to where you want the (1,0,0) (0,10) (0,0,1) basis vectors to end up after the transformation. I used Unity conventions to map the forward direction to the z axis (3rd column), up to y (2nd), right to x (1st). I subtracted a projection to ensure our up vector is perpendicular to z, and used the left-hand rule to construct a mutually-perpendicular right vector using a cross product. \$\endgroup\$ – DMGregory Nov 7 '18 at 17:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.