First off, the way to picture the Arcball is a sphere centered in the middle of your viewing space. You're looking down at it from the top, so you only see the outer surface. The way to make this happen is:
- Normalize the screen coordinates so that the X and Y coordinates are in [-1,1] (what I'm going to call view coordinates) and that the X and Y directions are to the right and up, respectively. (Usually in windows, the direction with increasing Y might be flipped).
- We want a sphere with radius 1, so that means that sqrt(x*x + y*y + z*z) = 1. Thus, z = sqrt(1 - x*x - y*y). Make sure this vector is normalized so the click point maps to the sphere's surface.
- When we click for the first time, let's call that vector U = (u1, u2, u3).
- If we want to change the rotation on mouse drag, then every time we move the mouse, we need to capture a drag vector V = (v1, v2, v3).
- To get the rotation axis W for these vectors, we can define W = U x V, the cross product.
- To get the rotation angle a, we can define a = acos(U . V), the dot product.
- Since the dot product might give us a value in the domain of Math.acos, we can use the min(1.0, U . V) function to keep it in the bounds.
Now we get into the heart of this post. In previous posts, I detailed rotation in 3D in 2 ways: with matrices and with quaternions. Now, I said that matrix multiplication isn't as numerically stable as quaternions. I learned that one the hard way. When doing the 7 matrix multiplies, I got some weird values and I couldn't hunt the problem down. Plus, it wasn't as easy as quaternions. So, I used the quaternion method I detailed below to do all my rotation and translation. But, there is one trick I did that allowed me to get a good, proper rotation matrix.
The trick is that I simply rotated the X, Y and Z unit vectors around W. If the quaternion rotation is accurate (which it is), then the vectors should stay orthogonal to each other, so no weird scaling and skewing. I constructed a rotation matrix from these vectors as follows:
R = [a a a 0; b b b 0; c c c 0; 0 0 0 1]
This seems to give me what I need. I know the 4th column is related to the "translation" of the origin, but I'm not there yet. Hope this helps anyone.