# Cube rotated by quaternion based trackball

I’m trying to implement a trackball camera controller according to the OpenGL wiki:

http://www.opengl.org/wiki/Trackball

Now I think I’m almost there. The cube rotates a little bit, but it keeps snapping back to it’s original position. The idea is to store the complete rotation in the member quaternion (m_cCurrentRot) of the trackball. Is this the wrong approach? I saw other approaches keeping the current rotation in the Modelview matrix. However, when I combine that approach with a translation, it gets messy.

Here are the relevant code snippets. Let me know if you need more.

``````
void Trackball::move(int iOldX, int iOldY, int iNewX, int iNewY, Matrix4x4& cRotMat) {
// see if there is a rotation
if(iOldX == iNewX && iOldY == iNewY)
return;

// obtaining trackball coordinates, v1 is new, v2 is old

Vector<GLfloat, 3> v1;
v1 = iNewX - (m_uiWidth / 2);
v1 = iNewY - (m_uiHeight / 2);
v1 = projectOnSphere(v1, v1);
v1.normalize();

Vector<GLfloat, 3> v2;
v2 = iOldX - (m_uiWidth / 2);
v2 = iOldY - (m_uiHeight / 2);
v2 = projectOnSphere(v2, v2);
v2.normalize();

// axis of rotation
Vector<GLfloat, 3> axis = cross(v1, v2);

// angle of rotation
double theta = std::acos(v1 * v2);

// quaternion representing the rotation
Quaternion<GLfloat> cQuaternion;
cQuaternion = std::cos(0.5 * theta);
cQuaternion = std::sin(0.5 * theta) * axis;
cQuaternion = std::sin(0.5 * theta) * axis;
cQuaternion = std::sin(0.5 * theta) * axis;
cQuaternion.normalize();

// adding rotation to existing rotations via multiplication of quaternions
m_cCurrentRot = cQuaternion * m_cCurrentRot;
m_cCurrentRot.normalize();
m_cCurrentRot.print();
buildRotMatrix(cRotMat, m_cCurrentRot);
}

void Trackball::buildRotMatrix(Matrix4x4& cNewRot, const Quaternion<GLfloat>& cQ) {

// http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Conversion_to_and_from_the_matrix_representation
cNewRot.set(
// row 0
cQ * cQ + cQ * cQ - cQ * cQ - cQ * cQ,
2.0f * (cQ * cQ - cQ * cQ),
2.0 * (cQ * cQ + cQ * cQ),
0.0f,

// row 1
2.0f * (cQ * cQ + cQ * cQ),
cQ * cQ - cQ * cQ + cQ * cQ - cQ * cQ,
2.0f * (cQ * cQ - cQ * cQ),
0.0f,

// row 2
2.0f * (cQ * cQ - cQ * cQ),
2.0f * (cQ * cQ + cQ * cQ),
cQ * cQ - cQ * cQ - cQ * cQ + cQ * cQ,
0.0f,

// row 3
0.0f,
0.0f,
0.0f,
1.0f
);

//	cNewRot.print();
}

GLfloat Trackball::projectOnSphere(int iX, int iY) {
GLfloat glfZ;
GLfloat glfXxYy = iX * iX + iY * iY;

// inside sphere
}
else {
// outside of sphere, using hyperbolic-sheet
}

return glfZ;
}

``````

And here the drawing and mouse functions:

``````
// windows width, height and trackball radius
Trackball cTrackball(800, 800, 400.0f);
Matrix4x4 cRotMat(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);

void drawGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glTranslatef(0.0f, 0.0f, -10.0f);

glMultMatrixf(cRotMat);

renderCube(1.0);

glutSwapBuffers();
}

void mouseEvent(int iButton, int iState, int iX, int iY) {
iOldX = iX;
iOldY = iY;
}

void mouseMotion(int iX, int iY) {

cTrackball.move(iOldX, iOldY, iX, iY, cRotMat);

glutPostRedisplay();
}

``````

For future readers: Yes, this is the way to go, apart from a few adjustments 1. I had a bug in Quaternion::operator* (not shown above)
2. Coordinates were scaled to [-1, 1] and a trackball size of 0.8 was used.
3. In the mouseMotion function the old coordinates need to be updated:
``````
void RenWin::mouseMotion(int iX, int iY) {
m_cTrackball.move(m_iOldX, m_iOldY, iX, iY, m_cRotMat);
m_iOldX = iX;
m_iOldY = iY;
glutPostRedisplay();
}

``````

Another tip: Make sure your default constructor initializes the quaternion to (1, 0, 0, 0), which means no rotation.