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*.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.
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_.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.
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: Maths - Conversion Quaternion to Euler - Martin Baker – 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!