Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 2 of 2

Thread: Cube rotated by quaternion based trackball

  1. #1
    Junior Member Newbie
    Join Date
    Dec 2010
    Posts
    2

    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.

    Code :
    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&amp; cNewRot, const Quaternion<GLfloat>&amp; 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;
    }


    And here the drawing and mouse functions:

    Code :
    // 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);
    	glLoadIdentity();
     
    	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();
    }

  2. #2
    Junior Member Newbie
    Join Date
    Dec 2010
    Posts
    2

    Re: Cube rotated by quaternion based trackball

    For future readers: Yes, this is the way to go, apart from a few adjustments

    1) I had a bug in Quaternion:perator* (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:

    Code :
    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •