PDA

View Full Version : Trackball using quaternion



Snow Spider
07-29-2012, 03:42 PM
Hi,

I'm trying to implement trackball interface described in this page: http://www.opengl.org/wiki/Trackball

The following code was produced according to the same mathematical principle in "Sit and Spin" section of that page.



// Window size
int window_width = 800, window_height = 600;
float window_aspect = (float)window_width / (float)window_height;

Quat lastQuat;
Quat rotQuat;
Quat curQuat;

Vec3 center(0.0,0.0,0.0);

float xi, yi;
bool leftButtonDown = false;
bool middleButtonDown = false;
bool rightButtonDown = false;

float z(float x, float y){
float x2 = x*x;
float y2 = y*y;
float r2 = 1.0;
if(x2 + y2 <= r2 * 0.5){
return sqrt( r2 - (x2 + y2) );
}
else{
return r2 * 0.5 / sqrt(x2 + y2);
}
}

Vec3 trackballProject(float x, float y){
x = x - window_width * 0.5 - center[0];
y = y - window_height * 0.5 - center[1];
return Vec3(x, y, z(x,y)).normalize();
}

void trackballRotate(float x1, float y1, float x2, float y2){
Vec3 v1 = trackballProject(x1, y1);
Vec3 v2 = trackballProject(x2, y2);
Vec3 normal = v1 ^ v2; // cross product of v1 and v2
float theta = acos(v1*v2); // dot product of v1 and v2
rotQuat.fromAxis(normal, theta);
curQuat = lastQuat * rotQuat; // quaternion multiplication
lastQuat = curQuat;
}

void initGL(){ // lighting & depth settings here
//...
}

void drawScene(){
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

glBegin(GL_TRIANGLES);
{
// draw whatever I want
}
glEnd();

glFlush();
}

void drawRotatedScene(){
glMatrixMode(GL_MODELVIEW);
glPushMatrix(); { // prevents auto-rotation after releasing mouse button
glMultMatrixf((GLfloat*)curQuat.getMatrix().mat);
drawScene();
} glPopMatrix();
}

void display(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawRotatedScene();
glutSwapBuffers();
}

void resize(int w, int h){
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (float)w / (float)h, 0.01, 100.0);

// Place the camera down the Z axis looking at the origin.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

gluLookAt(0, 0, 2.4 + 1.0,
0, 0, 0,
0, 1, 0);
}

void handleKeyPress(unsigned char key, int x, int y){
switch (key) {
case 27: //Escape key
exit(0);
}
glutPostRedisplay();
}

void mouseButton(int button, int state, int x, int y){
y = window_height - y;

if(button == GLUT_LEFT_BUTTON){ // orbitCam
if(state == GLUT_UP){
leftButtonDown = false;
xi = -1;
yi = -1;
}
else{
leftButtonDown = true;
xi = x;
yi = y;
}
}
if(button == GLUT_MIDDLE_BUTTON){ // panObj
if(state == GLUT_UP){
middleButtonDown = false;
xi = -1;
yi = -1;
}
else{
middleButtonDown = true;
xi = x;
yi = y;
}
}
if(button == GLUT_RIGHT_BUTTON){ // zoom
if(state == GLUT_UP){
rightButtonDown = false;
xi = -1;
yi = -1;
}
else{
rightButtonDown = true;
xi = x;
yi = y;
}
}

glutPostRedisplay(); // let glut know to redraw the screen
}

void mouseMotion(int x, int y){
y = window_height - y;

if(leftButtonDown){
trackballRotate(xi, yi, x, y);

float deltaX = x - xi;
float deltaY = y - yi;
rotY += (deltaX * 0.1);
rotX += (deltaY * 0.1);
xi = x;
yi = y;
}

if(middleButtonDown){
float deltaX = x - xi;
float deltaY = y - yi;
shiftX += (deltaX * 0.1);
shiftY += (deltaY * 0.1);
xi = x;
yi = y;
}

if(rightButtonDown){
float deltaY = y - yi;
zoom += (deltaY * 0.1);
yi = y;
}


glutPostRedisplay(); // let glut know to redraw the screen
}

void idle(){
glutPostRedisplay();
}

int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowPosition(0, 0);
glutInitWindowSize(window_width, window_height);

glutCreateWindow("Global Thermonuclear War");
//scene_spin_context.setWindow(scene_window, scene_animate);

initGL();

glutDisplayFunc(display);
glutKeyboardFunc(handleKeyPress);
glutReshapeFunc(resize);
glutMouseFunc(mouseButton);
glutMotionFunc(mouseMotion);
glutIdleFunc(idle);

glutMainLoop();
return 0;
}




// Convert from Axis Angle
void Quat::fromAxis(const Vec3 &v, float angle){
float sinAngle;
angle *= 0.5f;
Vec3 vn(v);
vn.normalize();

sinAngle = sin(angle);

x = (vn.x * sinAngle);
y = (vn.y * sinAngle);
z = (vn.z * sinAngle);
w = cos(angle);
}


When I click & drag on the viewport, however, the object appears only to rotate about the z-axis, which is more like a carousel than a trackball. I never get to see the other side of the object.
I believe my calculations are exactly the same as the formulae in the Wiki. I feintly suspect that the axis of rotation (called "normal" in my code) is incorrect, but I don't know where to begin.
Any suggestion will be much appreciated.

felixrulz
07-31-2012, 11:23 PM
I'm not sure if this will help but you could try this page: http://nehe.gamedev.net/tutorial/arcball_rotation/19003/