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[0] = iNewX - (m_uiWidth / 2);
v1[1] = iNewY - (m_uiHeight / 2);
v1[2] = projectOnSphere(v1[0], v1[1]);
v1.normalize();
Vector<GLfloat, 3> v2;
v2[0] = iOldX - (m_uiWidth / 2);
v2[1] = iOldY - (m_uiHeight / 2);
v2[2] = projectOnSphere(v2[0], v2[1]);
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[0] = std::cos(0.5 * theta);
cQuaternion[1] = std::sin(0.5 * theta) * axis[0];
cQuaternion[2] = std::sin(0.5 * theta) * axis[1];
cQuaternion[3] = std::sin(0.5 * theta) * axis[2];
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) {
// [url]http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Conversion_to_and_from_the_matrix_representation[/url]
cNewRot.set(
// row 0
cQ[0] * cQ[0] + cQ[1] * cQ[1] - cQ[2] * cQ[2] - cQ[3] * cQ[3],
2.0f * (cQ[1] * cQ[2] - cQ[0] * cQ[3]),
2.0 * (cQ[1] * cQ[3] + cQ[0] * cQ[2]),
0.0f,
// row 1
2.0f * (cQ[1] * cQ[2] + cQ[0] * cQ[3]),
cQ[0] * cQ[0] - cQ[1] * cQ[1] + cQ[2] * cQ[2] - cQ[3] * cQ[3],
2.0f * (cQ[2] * cQ[3] - cQ[0] * cQ[1]),
0.0f,
// row 2
2.0f * (cQ[1] * cQ[3] - cQ[0] * cQ[2]),
2.0f * (cQ[2] * cQ[3] + cQ[0] * cQ[1]),
cQ[0] * cQ[0] - cQ[1] * cQ[1] - cQ[2] * cQ[2] + cQ[3] * cQ[3],
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;
if(glfXxYy <= (m_glfRadius * m_glfRadius * 0.5)) {
// inside sphere
glfZ = sqrt(m_glfRadius * m_glfRadius - glfXxYy);
}
else {
// outside of sphere, using hyperbolic-sheet
glfZ = (0.5 * m_glfRadius * m_glfRadius) / sqrt(glfXxYy);
}
return glfZ;
}