I'm having trouble figuring out the proper conversions from Euler angles to Quaternions. The Eulers come from BVH files, which use a right-handed coordinate system (Y is up, Z is forward) and can have any rotation order specified. The Quaternions are in Unity, which is left-handed (Y is up, Z is back).
I'm composing the Quaternions from three AngleAxis
rotations, but messing up the order somewhere. So far I can't find understandable and generalized information for doing these types of conversions.
public enum AxisOrder
{
XYZ, XZY, YXZ, YZX, ZXY, ZYX, None
}
public Quaternion EulerToQuat(Vector3 eulerAngles, AxisOrder rotationOrder)
{
// I've tried various combinations of axis angles here, but would
// like to understand what's wrong rather than brute-force
// every combination
var xRot = Quaternion.AngleAxis(eulerAngles.x, Vector3.left);
var yRot = Quaternion.AngleAxis(eulerAngles.y, Vector3.down);
var zRot = Quaternion.AngleAxis(eulerAngles.z, Vector3.back);
switch (rotationOrder)
{
case AxisOrder.XYZ: return (xRot * yRot) * zRot;
case AxisOrder.XZY: return (xRot * zRot) * yRot;
case AxisOrder.YXZ: return (yRot * xRot) * zRot;
case AxisOrder.YZX: return (yRot * zRot) * xRot;
case AxisOrder.ZXY: return (zRot * xRot) * yRot;
case AxisOrder.ZYX: return (zRot * yRot) * xRot;
}
return Quaternion.identity;
}
...and converting translation data like so (I believe this is working):
public static Vector3 BvhToUnityTranslation(float xPos, float yPos, float zPos)
{
return new Vector3(xPos, yPos, -zPos);
}
If anyone can help me better understand how to conceptualize coordinate system conversions, and where I'm going wrong, I'd greatly appreciate it.
Answer
Three points we need to consider:
Matrices and quaternions in Unity multiply from right to left, if we think of each subsequent rotation being applied with regard to the world axes:
output space or vector = <-- transformation * <--- input space or vector
As discussed below, if BVH's "XYZ" order means "rotate about the world X axis, then the world Y axis, then the world Z axis" you'd stack your rotations in Unity right-to-left: Z * Y * X
But, it sounds like BVH's XYZ means "rotate about the local X axis, then the local Y axis, then the local Z axis" which means in Unity we'd match the order left-to-right: X * Y * Z
When switching the handedness of the coordinate system, all rotation angles are negated (if we keep the axis of rotation consistent)
The axes map up like so, based on the conversation above:
direction Unity BVH
----------------------------
right x+ x-
up y+ y+
forward z+ z+
So we should be able to get correct conversion between the two spaces with a few small changes:
public Quaternion BvhToUnityRotation(Vector3 eulerAngles, AxisOrder rotationOrder)
{
// BVH's x+ axis is Unity's left (x-)
var xRot = Quaternion.AngleAxis(-eulerAngles.x, Vector3.left);
// Unity & BVH agree on the y & z axes
var yRot = Quaternion.AngleAxis(-eulerAngles.y, Vector3.up);
var zRot = Quaternion.AngleAxis(-eulerAngles.z, Vector3.forward);
switch (rotationOrder)
{
// Reproduce rotation order (no need for parentheses - it's associative)
case AxisOrder.XYZ: return xRot * yRot * zRot;
case AxisOrder.XZY: return xRot * zRot * yRot;
case AxisOrder.YXZ: return yRot * xRot * zRot;
case AxisOrder.YZX: return yRot * zRot * xRot;
case AxisOrder.ZXY: return zRot * xRot * yRot;
case AxisOrder.ZYX: return zRot * yRot * xRot;
}
return Quaternion.identity;
}
And:
public static Vector3 BvhToUnityTranslation(float xPos, float yPos, float zPos)
{
// Flipping x axis instead.
return new Vector3(-xPos, yPos, zPos);
}
No comments:
Post a Comment