I'm in the case I need to transform a Quaternion coordinate system to a concrete one, let me explain.
I'm implementing an orientation tracker for VR headsets, I have the orientation of the device and it workd as expected. Also I've implemented a function to recenter the device, this function takes the current orientation, obtains the conjugated quaternion and stores it as the zero pose, so when the orientation is retrieved the current orientation quaternion is multiplied by the zero pose and returns the current position. This also works as expected.
Next I've implemented a mouse emulator based on this orientation, I have a Plane composed by (p, n) being p a point in the plane and n the plane's normal (basically (0, 0, 100), (0,0,-1)). To retrieve the mouse position I project a ray (p=(0,0,0),n=((0,0,1) * q)) to the plane and take the (x,y) values of the intersecting point as the mouse coordinates. This also works, but only if the headset axes are aligned to the world axes.
For example, if the user has the headset correctly worn when the pose is reset (take current pose conjugate as the zero pose) then if you look up the mouse moves up, if you move right it moves right, and even if you roll your head the tracking is correct, but if the user has the headset wrongly placed (inclined to some side) when the zero pose is reset then the movement is incorrect as the axes don't match.
I hope this image explains it better than me.
This is the code for calculating the mouse position (it includes a low-filter as smoothing):
public void UpdateParameters(float ScreenDistance, Vector2 ScreenSize, Vector2 ScreenResolution, float SmoothingFactor)
{
smoothFactor = SmoothingFactor;
size = ScreenSize;
resolution = ScreenResolution;
screenZero = resolution / 2;
xScale = ScreenResolution.X / ScreenSize.X;
yScale = ScreenResolution.Y / ScreenSize.Y;
pointOnPlane = new Vector3(0, 0, ScreenDistance);
}
public void UpdateInput(Quaternion Orientation)
{
Vector3 rotatedNormal = Vector3.Normalize(Vector3.Transform(rayNormal, Orientation));
float T = Vector3.Dot(planeNormal, pointOnPlane) / Vector3.Dot(planeNormal, rotatedNormal);
Vector3 pointInPlane = rotatedNormal * T;
int x = (int)(pointInPlane.X * xScale + screenZero.X);
int y = (int)(pointInPlane.Y * yScale + screenZero.Y);
Vector2 np = new Vector2(x, y);
Vector2 p = new Vector2(prevX, prevY);
Vector2 ip = Vector2.Lerp(p, np, smoothFactor);
x = (int)ip.X;
y = (int)ip.Y;
if (x != prevX || y != prevY)
{
prevX = x;
prevY = y;
if (MouseMove != null)
MouseMove(this, new MouseEventArgs { X = x, Y = y });
}
}
And this is the code to calculate the zero pose:
if (recenter)
{
ZeroPose = Quaternion.Conjugate(fusion.Quaternion);
recenter = false;
}
So the question, how can I transform the arbitary HMD axes to world axes in order to get the mouse movement right?
Thanks in advance.