Fawkes API  Fawkes Development Version
trackball.cpp
00001 
00002 /***************************************************************************
00003  *  trackball.cpp - Smooth mouse movements for OpenGL window
00004  *
00005  *  Created: Fri Apr 01 19:56:31 2011
00006  *  Copyright  2011  Tim Niemueller [www.niemueller.de]
00007  *
00008  *  The code has is based on the OpenGL example "smooth" by Nate Robins
00009  *  It states:
00010  *  "Simple trackball-like motion adapted (ripped off) from projtex.c
00011  *   (written by David Yu and David Blythe).  See the SIGGRAPH '96
00012  *   Advanced OpenGL course notes."
00013  *
00014  ****************************************************************************/
00015 
00016 /*  This program is free software; you can redistribute it and/or modify
00017  *  it under the terms of the GNU General Public License as published by
00018  *  the Free Software Foundation; either version 2 of the License, or
00019  *  (at your option) any later version.
00020  *
00021  *  This program is distributed in the hope that it will be useful,
00022  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00023  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00024  *  GNU Library General Public License for more details.
00025  *
00026  *  Read the full text in the LICENSE.GPL file in the doc directory.
00027  */
00028 
00029 #include "trackball.h"
00030 
00031 #include <GL/glut.h>
00032 #include <cmath>
00033 
00034 
00035 // globals
00036 static GLuint    tb_lasttime;
00037 static GLfloat   tb_lastposition[3];
00038 
00039 static GLfloat   tb_angle = 0.0;
00040 static GLfloat   tb_axis[3];
00041 static GLfloat   tb_transform[4][4];
00042 
00043 static GLuint    tb_width;
00044 static GLuint    tb_height;
00045 
00046 static GLint     tb_button = -1;
00047 static GLboolean tb_tracking = GL_FALSE;
00048 static GLboolean tb_animate = GL_TRUE;
00049 
00050 static void (*   tb_original_idle_func)();
00051 
00052 // functions
00053 static void
00054 _tbPointToVector(int x, int y, int width, int height, float v[3])
00055 {
00056   float d, a;
00057 
00058   // project x, y onto a hemi-sphere centered within width, height.
00059   v[0] = (2.0 * x - width) / width;
00060   v[1] = (height - 2.0 * y) / height;
00061   d = sqrt(v[0] * v[0] + v[1] * v[1]);
00062   v[2] = cos((3.14159265 / 2.0) * ((d < 1.0) ? d : 1.0));
00063   a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
00064   v[0] *= a;
00065   v[1] *= a;
00066   v[2] *= a;
00067 }
00068 
00069 static void
00070 _tbAnimate(void)
00071 {
00072   tb_original_idle_func();
00073   glutPostRedisplay();
00074 }
00075 
00076 void
00077 _tbStartMotion(int x, int y, int button, int time)
00078 {
00079   if (tb_button == -1) return;
00080 
00081   tb_tracking = GL_TRUE;
00082   tb_lasttime = time;
00083   _tbPointToVector(x, y, tb_width, tb_height, tb_lastposition);
00084 }
00085 
00086 void
00087 _tbStopMotion(int button, unsigned time)
00088 {
00089   if (tb_button == -1) return;
00090 
00091   tb_tracking = GL_FALSE;
00092 
00093   if (time == tb_lasttime && tb_animate) {
00094     glutIdleFunc(_tbAnimate);
00095   } else {
00096     tb_angle = 0.0;
00097     if (tb_animate)
00098       glutIdleFunc(tb_original_idle_func);
00099   }
00100 }
00101 
00102 void
00103 tbAnimate(GLboolean animate, void (* idle_func)())
00104 {
00105   tb_animate = animate;
00106   tb_original_idle_func = idle_func;
00107 }
00108 
00109 void
00110 tbInit(GLuint button)
00111 {
00112   tb_button = button;
00113   tb_angle = 0.0;
00114 
00115   // put the identity in the trackball transform
00116   glPushMatrix();
00117   glLoadIdentity();
00118   glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform);
00119   glPopMatrix();
00120 }
00121 
00122 void
00123 tbMatrix()
00124 {
00125   if (tb_button == -1) return;
00126 
00127   glPushMatrix();
00128   glLoadIdentity();
00129   glRotatef(tb_angle, tb_axis[0], tb_axis[1], tb_axis[2]);
00130   glMultMatrixf((GLfloat *)tb_transform);
00131   glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform);
00132   glPopMatrix();
00133 
00134   glMultMatrixf((GLfloat *)tb_transform);
00135 }
00136 
00137 void
00138 tbReshape(int width, int height)
00139 {
00140   if (tb_button == -1) return;
00141 
00142   tb_width  = width;
00143   tb_height = height;
00144 }
00145 
00146 void
00147 tbMouse(int button, int state, int x, int y)
00148 {
00149   if (tb_button == -1) return;
00150 
00151   if (state == GLUT_DOWN && button == tb_button)
00152     _tbStartMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME));
00153   else if (state == GLUT_UP && button == tb_button)
00154     _tbStopMotion(button, glutGet(GLUT_ELAPSED_TIME));
00155 }
00156 
00157 void
00158 tbMotion(int x, int y)
00159 {
00160   GLfloat current_position[3], dx, dy, dz;
00161 
00162   if (tb_button == -1) return;
00163 
00164   if (tb_tracking == GL_FALSE)
00165     return;
00166 
00167   _tbPointToVector(x, y, tb_width, tb_height, current_position);
00168 
00169   /* calculate the angle to rotate by (directly proportional to the
00170      length of the mouse movement */
00171   dx = current_position[0] - tb_lastposition[0];
00172   dy = current_position[1] - tb_lastposition[1];
00173   dz = current_position[2] - tb_lastposition[2];
00174   tb_angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz);
00175 
00176   // calculate the axis of rotation (cross product)
00177   tb_axis[0] = tb_lastposition[1] * current_position[2] - 
00178                tb_lastposition[2] * current_position[1];
00179   tb_axis[1] = tb_lastposition[2] * current_position[0] - 
00180                tb_lastposition[0] * current_position[2];
00181   tb_axis[2] = tb_lastposition[0] * current_position[1] - 
00182                tb_lastposition[1] * current_position[0];
00183 
00184   // reset for next time
00185   tb_lasttime = glutGet(GLUT_ELAPSED_TIME);
00186   tb_lastposition[0] = current_position[0];
00187   tb_lastposition[1] = current_position[1];
00188   tb_lastposition[2] = current_position[2];
00189 
00190   // remember to draw new position
00191   glutPostRedisplay();
00192 }