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 
33 #include <cmath>
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)
80  return;
81 
82  tb_tracking = GL_TRUE;
83  tb_lasttime = time;
84  _tbPointToVector(x, y, tb_width, tb_height, tb_lastposition);
85 }
86 
87 void
88 _tbStopMotion(int button, unsigned time)
89 {
90  if (tb_button == -1)
91  return;
92 
93  tb_tracking = GL_FALSE;
94 
95  if (time == tb_lasttime && tb_animate) {
96  glutIdleFunc(_tbAnimate);
97  } else {
98  tb_angle = 0.0;
99  if (tb_animate)
100  glutIdleFunc(tb_original_idle_func);
101  }
102 }
103 
104 void
105 tbAnimate(GLboolean animate, void (*idle_func)())
106 {
107  tb_animate = animate;
108  tb_original_idle_func = idle_func;
109 }
110 
111 void
112 tbInit(GLuint button)
113 {
114  tb_button = button;
115  tb_angle = 0.0;
116 
117  // put the identity in the trackball transform
118  glPushMatrix();
119  glLoadIdentity();
120  glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform);
121  glPopMatrix();
122 }
123 
124 void
125 tbMatrix()
126 {
127  if (tb_button == -1)
128  return;
129 
130  glPushMatrix();
131  glLoadIdentity();
132  glRotatef(tb_angle, tb_axis[0], tb_axis[1], tb_axis[2]);
133  glMultMatrixf((GLfloat *)tb_transform);
134  glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform);
135  glPopMatrix();
136 
137  glMultMatrixf((GLfloat *)tb_transform);
138 }
139 
140 void
141 tbReshape(int width, int height)
142 {
143  if (tb_button == -1)
144  return;
145 
146  tb_width = width;
147  tb_height = height;
148 }
149 
150 void
151 tbMouse(int button, int state, int x, int y)
152 {
153  if (tb_button == -1)
154  return;
155 
156  if (state == GLUT_DOWN && button == tb_button)
157  _tbStartMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME));
158  else if (state == GLUT_UP && button == tb_button)
159  _tbStopMotion(button, glutGet(GLUT_ELAPSED_TIME));
160 }
161 
162 void
163 tbMotion(int x, int y)
164 {
165  GLfloat current_position[3], dx, dy, dz;
166 
167  if (tb_button == -1)
168  return;
169 
170  if (tb_tracking == GL_FALSE)
171  return;
172 
173  _tbPointToVector(x, y, tb_width, tb_height, current_position);
174 
175  /* calculate the angle to rotate by (directly proportional to the
176  length of the mouse movement */
177  dx = current_position[0] - tb_lastposition[0];
178  dy = current_position[1] - tb_lastposition[1];
179  dz = current_position[2] - tb_lastposition[2];
180  tb_angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz);
181 
182  // calculate the axis of rotation (cross product)
183  tb_axis[0] = tb_lastposition[1] * current_position[2] - tb_lastposition[2] * current_position[1];
184  tb_axis[1] = tb_lastposition[2] * current_position[0] - tb_lastposition[0] * current_position[2];
185  tb_axis[2] = tb_lastposition[0] * current_position[1] - tb_lastposition[1] * current_position[0];
186 
187  // reset for next time
188  tb_lasttime = glutGet(GLUT_ELAPSED_TIME);
189  tb_lastposition[0] = current_position[0];
190  tb_lastposition[1] = current_position[1];
191  tb_lastposition[2] = current_position[2];
192 
193  // remember to draw new position
194  glutPostRedisplay();
195 }