- Home /
How to convert quaternion to vector3 with specific multiplication order?
I'm working with a custom mesh data format (which I have no control over, so it's not an option to change it), and have had to do some mildly abnormal (for unity) stuff to make it display properly. Specifically, the bone rotations:
When importing my format, each bone rotation is provided as a Vector3, but it needs to be multiplied in a different order than Unity typically works with, as can be seen in my code below:
bones[i].localRotation =
Quaternion.Euler(new Vector3(0, y, 0)) *
Quaternion.Euler(new Vector3(0, 0, z)) *
Quaternion.Euler(new Vector3(x, 0, 0));
I should note that the code above is fully functional, and correct- the bones are all displaying/animating correctly. Also worth noting is that the custom mesh format I'm working with uses Positions/Scales that can be literally copied over into Unity and everything is oriented as would be expected in Unity, so this likely isn't an issue of the custom format having a different axis orientation.
However the problem I come across, with my limited understanding of Quaternions, is when I'm exporting from Unity into this custom mesh format. I need to take the Quaternion for the bone and somehow get back to the original Vector3 that is required by this custom mesh format.
I can see that Unity provides a couple of methods for getting a Vector3 back from a Quaternion, but they're all explicit in stating that it represents z x y rotation order, which is the wrong order from what I need! How can I calculate the Vector3 that I require from my Quaternion?
Adding clarification on exactly what my Unity app needs to do, regarding this question:
1) Import from a custom mesh format. This involves taking the Vector3 supplied to me and multiplying in an order foreign to Unity, to get a Quaternion that works great in Unity.
2) Export to the custom mesh format. This involves taking the Quaternion that works great in Unity, and "un-multiplying" in an order foreign to Unity, to get a Vector3 I can store back in the custom mesh format.
Since it's quite obvious that whatever creates the "content" of your "format" has different axis definitions than unity, why can't you simply swap the axis on import and export?
For instance, in blender the Z axis is up and the Y axis is forward. So on import, you'd do:
anyVector = Vector3(anyVector.x, anyVector.z, anyVector.y)
Also, aren't there enough 3d data formats out there? Is there a specific reason you're rolling your own, rather than using a widely supported, common format?
This isn't my own, it's somebody else's format. I'm just working with it, so my hands are tied regarding how it works. Also, the axes aren't different. All positions and scales match Unity's perfectly.
Answer by Nyxo · Jul 01, 2014 at 05:39 PM
All google searching (until last night) has always lead me to formulas catering towards one of two multiplication orders, neither of which actually applied to my situation. However, I eventually found a link from www.euclideanspace.com that presented a breakdown of the formula in such a way that the order of multiplication can be arbitrarily decided: http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/Quaternions.pdf
This gave me some general math formulas I could use to put together most of my solution, but for some reason I don't understand (since some of the math is a bit above my head) I found that the singularity checks weren't giving me the results I needed. However, other places on the site linking to that document give singularity checks that achieve the results I'm looking for: http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm -- All of this together has given me the complete formula I need to achieve my goal:
private Vector3 UnmultiplyQuaternion(Quaternion quaternion)
{
Vector3 ret;
var xx = quaternion.x * quaternion.x;
var xy = quaternion.x * quaternion.y;
var xz = quaternion.x * quaternion.z;
var xw = quaternion.x * quaternion.w;
var yy = quaternion.y * quaternion.y;
var yz = quaternion.y * quaternion.z;
var yw = quaternion.y * quaternion.w;
var zz = quaternion.z * quaternion.z;
var zw = quaternion.z * quaternion.w;
var check = zw + xy;
if (check.AlmostEquals(0.5f, 0.00001f))
check = 0.5f;
else if (check.AlmostEquals(-0.5f, 0.00001f))
check = -0.5f;
ret.y = Mathf.Atan2(2 * (yw - xz), 1 - 2 * (yy + zz));
ret.z = Mathf.Asin(2 * check);
ret.x = Mathf.Atan2(2 * (xw - yz), 1 - 2 * (zz + xx));
if (check == 0.5f)
{
ret.x = 0;
ret.y = 2 * Mathf.Atan2(quaternion.y, quaternion.w);
}
else if (check == -0.5f)
{
ret.x = 0;
ret.y = -2 * Mathf.Atan2(quaternion.y, quaternion.w);
}
return ret;
}
For the one other person on the planet who will ever need to do this, there's your answer!
Answer by Owen-Reynolds · Jun 30, 2014 at 04:53 PM
If I'm understanding, this foreign program gives you a vector3 of euler rotations, using an odd x,z,y rotation order (or y,z,x -- depending whether local/global. I always get it backwards.) So your hand-done math is merely applying the three euler rots in the correct, but odd, order.
Once you have a Quaternion, it doesn't matter how you got it. Like Quaternion.LookDirection() just computes the angle, no eulers or xyz order involved. You should be able to use bone[i].forward
the same as usual. Or Q*Vector3.forward
.
The only things yxz order means, is that Quaternion.Euler(a,b,c)
rotates by b on y, then a on local x ... . So if you want some other order, you have to break it up the way you did. I've had to do that a few times and it was fine.
Thing #2 is rotation.eularAngles
. For that, the computer says, "I'm an awesum Quaternion, and you want eulerAngles? Gah. Would you also like some rocks to count with? Well, here's EA's meant to be applied yxz. Which, BTW, is why I'm better -- no silly application order to remember."
That may be the confusion. EulerAngles is a Vector3, but not a direction or position vector. Just a handy way to store the three x,y,z rotations. So it's the one case where a Vector3 has a yxz order. And I've never seen a legit use for it.
Thanks for the rather in-depth description- it's been enlightening for me in a few areas.
However, unless I've grossly misunderstood your answer, I believe it doesn't actually address my question: How do I get my Quaternion back into that original Vector3 (which, as you said, is just a convenient way to store 3 separate rotations that need to be applied in a specific order)? I basically need to "undo" the custom order of multiplication that I did to get my Quaternion..
To add a bit of clarification, I'll describe what I need to do:
1) Import from a custom mesh format. This involves taking the Vector3 supplied to me and multiplying in an order foreign to Unity, to get a Quaternion that works great in Unity.
2) Export to the custom mesh format. This involves taking the Quaternion that works great in Unity, and "un-multiplying" in an order foreign to Unity, to get a Vector3 I can store back in the custom mesh format.
I'll add this clarification to my question.
Correct, I never mentioned any way to pull non-yxz eulerAngles out of a quat. Don't think there's any built-ins. Are you sure you need to do that?
I'd guess there are standard math tricks. yxz -> yzx. Hopefully not involving projections.
A trick in Unity (probably all game engines) is to think of eulerAngles as little as possible. So this is usually a "don't do it that way" thing.
Unfortunately, I don't have a choice. I -need- to import AND export this custom format.
Quaternions and eulerAngles are just general math -- not Unity3d things. A quick google showed some math sites with code for various Quaternion-to-xyz combinations.