borgware-2d/simulator/trackball.c

235 lines
5.4 KiB
C

/*
* Simple trackball-like motion adapted (ripped off) from projtex.c
* (written by David Yu and David Blythe). See the SIGGRAPH '96
* Advanced OpenGL course notes.
*
*
* Usage:
*
* o call tbInit() in before any other tb call
* o call tbReshape() from the reshape callback
* o call tbMatrix() to get the trackball matrix rotation
* o call tbStartMotion() to begin trackball movememt
* o call tbStopMotion() to stop trackball movememt
* o call tbMotion() from the motion callback
* o call tbAnimate(GL_TRUE) if you want the trackball to continue
* spinning after the mouse button has been released
* o call tbAnimate(GL_FALSE) if you want the trackball to stop
* spinning after the mouse button has been released
*
* Typical setup:
*
*
void
init(void)
{
tbInit(GLUT_MIDDLE_BUTTON);
tbAnimate(GL_TRUE);
. . .
}
void
reshape(int width, int height)
{
tbReshape(width, height);
. . .
}
void
display(void)
{
glPushMatrix();
tbMatrix();
. . . draw the scene . . .
glPopMatrix();
}
void
mouse(int button, int state, int x, int y)
{
tbMouse(button, state, x, y);
. . .
}
void
motion(int x, int y)
{
tbMotion(x, y);
. . .
}
int
main(int argc, char** argv)
{
. . .
init();
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMotionFunc(motion);
. . .
}
*
* */
/* includes */
#include <math.h>
#include <assert.h>
#ifdef OSX_
# include <GLUT/glut.h>
#elif _WIN32
# include <GL/glut.h>
#else
# include <GL/glut.h>
#endif
#include "trackball.h"
/* globals */
static GLuint tb_lasttime;
static GLfloat tb_lastposition[3];
static GLfloat tb_angle = 0.0;
static GLfloat tb_axis[3];
static GLfloat tb_transform[4][4];
static GLuint tb_width;
static GLuint tb_height;
static GLint tb_button = -1;
static GLboolean tb_tracking = GL_FALSE;
static GLboolean tb_animate = GL_TRUE;
/* functions */
static void _tbPointToVector(int x, int y, int width, int height, float v[3])
{
float d, a;
/* project x, y onto a hemi-sphere centered within width, height. */
v[0] = (2.0 * x - width) / width;
v[1] = (height - 2.0 * y) / height;
d = sqrt(v[0] * v[0] + v[1] * v[1]);
v[2] = cos((3.14159265 / 2.0) * ((d < 1.0) ? d : 1.0));
a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
v[0] *= a;
v[1] *= a;
v[2] *= a;
}
static void _tbAnimate(void)
{
glutPostRedisplay();
}
void _tbStartMotion(int x, int y, int button, int time)
{
assert(tb_button != -1);
tb_tracking = GL_TRUE;
tb_lasttime = time;
_tbPointToVector(x, y, tb_width, tb_height, tb_lastposition);
}
void _tbStopMotion(int button, unsigned time)
{
assert(tb_button != -1);
tb_tracking = GL_FALSE;
if (time == tb_lasttime && tb_animate) {
glutIdleFunc(_tbAnimate);
} else {
tb_angle = 0.0;
if (tb_animate)
glutIdleFunc(0);
}
}
void tbAnimate(GLboolean animate)
{
tb_animate = animate;
}
void tbInit(GLuint button)
{
tb_button = button;
tb_angle = 0.0;
/* put the identity in the trackball transform */
glPushMatrix();
glLoadIdentity();
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform);
glPopMatrix();
}
void tbMatrix()
{
assert(tb_button != -1);
glPushMatrix();
glLoadIdentity();
glRotatef(tb_angle, -tb_axis[0], tb_axis[2], tb_axis[1]);
glMultMatrixf((GLfloat *)tb_transform);
glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform);
glPopMatrix();
glMultMatrixf((GLfloat *)tb_transform);
}
void tbReshape(int width, int height)
{
assert(tb_button != -1);
tb_width = width;
tb_height = height;
}
void tbMouse(int button, int state, int x, int y)
{
assert(tb_button != -1);
if (state == GLUT_DOWN && button == tb_button)
_tbStartMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME));
else if (state == GLUT_UP && button == tb_button)
_tbStopMotion(button, glutGet(GLUT_ELAPSED_TIME));
}
void tbMotion(int x, int y)
{
GLfloat current_position[3], dx, dy, dz;
assert(tb_button != -1);
if (tb_tracking == GL_FALSE)
return;
_tbPointToVector(x, y, tb_width, tb_height, current_position);
/* calculate the angle to rotate by (directly proportional to the
length of the mouse movement */
dx = current_position[0] - tb_lastposition[0];
dy = current_position[1] - tb_lastposition[1];
dz = current_position[2] - tb_lastposition[2];
tb_angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz);
/* calculate the axis of rotation (cross product) */
tb_axis[0] = tb_lastposition[1] * current_position[2] -
tb_lastposition[2] * current_position[1];
tb_axis[1] = tb_lastposition[2] * current_position[0] -
tb_lastposition[0] * current_position[2];
tb_axis[2] = tb_lastposition[0] * current_position[1] -
tb_lastposition[1] * current_position[0];
/* reset for next time */
tb_lasttime = glutGet(GLUT_ELAPSED_TIME);
tb_lastposition[0] = current_position[0];
tb_lastposition[1] = current_position[1];
tb_lastposition[2] = current_position[2];
/* remember to draw new position */
glutPostRedisplay();
}