Fawkes API  Fawkes Development Version
trackball.cpp
1 
2 /***************************************************************************
3  * trackball.cpp - Smooth mouse movements for OpenGL window
4  *
5  * Created: Fri Apr 01 19:56:31 2011
6  * Copyright 2011 Tim Niemueller [www.niemueller.de]
7  *
8  * The code has is based on the OpenGL example "smooth" by Nate Robins
9  * It states:
10  * "Simple trackball-like motion adapted (ripped off) from projtex.c
11  * (written by David Yu and David Blythe). See the SIGGRAPH '96
12  * Advanced OpenGL course notes."
13  *
14  ****************************************************************************/
15 
16 /* This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24  * GNU Library General Public License for more details.
25  *
26  * Read the full text in the LICENSE.GPL file in the doc directory.
27  */
28 
29 #include "trackball.h"
30 
31 #include <GL/glut.h>
32 #include <cmath>
33 
34 
35 // globals
36 static GLuint tb_lasttime;
37 static GLfloat tb_lastposition[3];
38 
39 static GLfloat tb_angle = 0.0;
40 static GLfloat tb_axis[3];
41 static GLfloat tb_transform[4][4];
42 
43 static GLuint tb_width;
44 static GLuint tb_height;
45 
46 static GLint tb_button = -1;
47 static GLboolean tb_tracking = GL_FALSE;
48 static GLboolean tb_animate = GL_TRUE;
49 
50 static void (* tb_original_idle_func)();
51 
52 // functions
53 static void
54 _tbPointToVector(int x, int y, int width, int height, float v[3])
55 {
56  float d, a;
57 
58  // project x, y onto a hemi-sphere centered within width, height.
59  v[0] = (2.0 * x - width) / width;
60  v[1] = (height - 2.0 * y) / height;
61  d = sqrt(v[0] * v[0] + v[1] * v[1]);
62  v[2] = cos((3.14159265 / 2.0) * ((d < 1.0) ? d : 1.0));
63  a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
64  v[0] *= a;
65  v[1] *= a;
66  v[2] *= a;
67 }
68 
69 static void
70 _tbAnimate(void)
71 {
72  tb_original_idle_func();
73  glutPostRedisplay();
74 }
75 
76 void
77 _tbStartMotion(int x, int y, int button, int time)
78 {
79  if (tb_button == -1) return;
80 
81  tb_tracking = GL_TRUE;
82  tb_lasttime = time;
83  _tbPointToVector(x, y, tb_width, tb_height, tb_lastposition);
84 }
85 
86 void
87 _tbStopMotion(int button, unsigned time)
88 {
89  if (tb_button == -1) return;
90 
91  tb_tracking = GL_FALSE;
92 
93  if (time == tb_lasttime && tb_animate) {
94  glutIdleFunc(_tbAnimate);
95  } else {
96  tb_angle = 0.0;
97  if (tb_animate)
98  glutIdleFunc(tb_original_idle_func);
99  }
100 }
101 
102 void
103 tbAnimate(GLboolean animate, void (* idle_func)())
104 {
105  tb_animate = animate;
106  tb_original_idle_func = idle_func;
107 }
108 
109 void
110 tbInit(GLuint button)
111 {
112  tb_button = button;
113  tb_angle = 0.0;
114 
115  // put the identity in the trackball transform
116  glPushMatrix();
117  glLoadIdentity();
118  glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform);
119  glPopMatrix();
120 }
121 
122 void
123 tbMatrix()
124 {
125  if (tb_button == -1) return;
126 
127  glPushMatrix();
128  glLoadIdentity();
129  glRotatef(tb_angle, tb_axis[0], tb_axis[1], tb_axis[2]);
130  glMultMatrixf((GLfloat *)tb_transform);
131  glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform);
132  glPopMatrix();
133 
134  glMultMatrixf((GLfloat *)tb_transform);
135 }
136 
137 void
138 tbReshape(int width, int height)
139 {
140  if (tb_button == -1) return;
141 
142  tb_width = width;
143  tb_height = height;
144 }
145 
146 void
147 tbMouse(int button, int state, int x, int y)
148 {
149  if (tb_button == -1) return;
150 
151  if (state == GLUT_DOWN && button == tb_button)
152  _tbStartMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME));
153  else if (state == GLUT_UP && button == tb_button)
154  _tbStopMotion(button, glutGet(GLUT_ELAPSED_TIME));
155 }
156 
157 void
158 tbMotion(int x, int y)
159 {
160  GLfloat current_position[3], dx, dy, dz;
161 
162  if (tb_button == -1) return;
163 
164  if (tb_tracking == GL_FALSE)
165  return;
166 
167  _tbPointToVector(x, y, tb_width, tb_height, current_position);
168 
169  /* calculate the angle to rotate by (directly proportional to the
170  length of the mouse movement */
171  dx = current_position[0] - tb_lastposition[0];
172  dy = current_position[1] - tb_lastposition[1];
173  dz = current_position[2] - tb_lastposition[2];
174  tb_angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz);
175 
176  // calculate the axis of rotation (cross product)
177  tb_axis[0] = tb_lastposition[1] * current_position[2] -
178  tb_lastposition[2] * current_position[1];
179  tb_axis[1] = tb_lastposition[2] * current_position[0] -
180  tb_lastposition[0] * current_position[2];
181  tb_axis[2] = tb_lastposition[0] * current_position[1] -
182  tb_lastposition[1] * current_position[0];
183 
184  // reset for next time
185  tb_lasttime = glutGet(GLUT_ELAPSED_TIME);
186  tb_lastposition[0] = current_position[0];
187  tb_lastposition[1] = current_position[1];
188  tb_lastposition[2] = current_position[2];
189 
190  // remember to draw new position
191  glutPostRedisplay();
192 }