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
36static GLuint tb_lasttime;
37static GLfloat tb_lastposition[3];
38
39static GLfloat tb_angle = 0.0;
40static GLfloat tb_axis[3];
41static GLfloat tb_transform[4][4];
42
43static GLuint tb_width;
44static GLuint tb_height;
45
46static GLint tb_button = -1;
47static GLboolean tb_tracking = GL_FALSE;
48static GLboolean tb_animate = GL_TRUE;
49
50static void (*tb_original_idle_func)();
51
52// functions
53static 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
69static void
70_tbAnimate(void)
71{
72 tb_original_idle_func();
73 glutPostRedisplay();
74}
75
76void
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
87void
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
104void
105tbAnimate(GLboolean animate, void (*idle_func)())
106{
107 tb_animate = animate;
108 tb_original_idle_func = idle_func;
109}
110
111void
112tbInit(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
124void
125tbMatrix()
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
140void
141tbReshape(int width, int height)
142{
143 if (tb_button == -1)
144 return;
145
146 tb_width = width;
147 tb_height = height;
148}
149
150void
151tbMouse(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
162void
163tbMotion(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}