/* -*- Mode:C; Coding:us-ascii-unix; fill-column:132 -*- */
/**********************************************************************************************************************************/
/**
@file rotAndScaleInteractive.c
@author Mitch Richling <http://www.mitchr.me/>
@Copyright Copyright 1998 by Mitch Richling. All rights reserved.
@brief Rotate interactively in an intuitive way.@EOL
@Keywords rotate interact mouse
@Std C99
The act of allowing a user to interactively rotate an object is more complex than it first appears. When a user moves a mouse
down they expect the object to rotate around an axis that is parallel to the bottom of the screen, and if they move to the right
they expect the object to rotate around an axis that is parallel to the side of the screen. The difficulty is that it is
non-trivial for beginners to figure out how to figure out what the appropriate rotation vectors are. This code demonstrates one
way to do that. It is possible that roundoff error can throw off this method. A more robust method is to use quaternion
rotation interpolation. A good example is displayed by the SGI GLUT example trackball.c
***********************************************************************************************************************************/
/**********************************************************************************************************************************/
#include <math.h> /* Math stuff ISOC */
#include <stdlib.h> /* Standard Lib ISOC */
#include <stdio.h> /* I/O lib ISOC */
/**********************************************************************************************************************************/
/* Apple puts GLUT into a framework named GLUT, while the rest of the world just sticks GLUT into the GL include directory... */
#ifdef __APPLE__
#include <GLUT/glut.h> /* Open GL Util APPLE */
#else
#include <GL/glut.h> /* Open GL Util OpenGL*/
#endif
/**********************************************************************************************************************************/
/* MODL 1 ==> a box with axis
2 ==> a Lorenz strange attractor
3 ==> a Lighted Surface with shading
*/
#ifndef MODL
#define MODL 1
#endif
#if MODL == 1
#include "boxNaxis.h"
#endif
#if MODL == 2
#include "lorenzC.h"
#endif
#if MODL == 3
#include "surf.h"
#endif
/**********************************************************************************************************************************/
/* Define to 1 for anti-aliasing, 0 for none */
#ifndef ANTIA
#if MODL != 3
#define ANTIA 1
#endif
#endif
#if MODL == 3
#define ANTIA 0
#endif
/**********************************************************************************************************************************/
/* Global state variables. */
enum interactionModeEnum {IMODE_SCALE, IMODE_ROTATE};
int xMS, yMS;
interactionModeEnum interactMode = IMODE_ROTATE;
/**********************************************************************************************************************************/
/* Upon a mouse click we store the coordinates into the global "last mouse" location. We do this to help the motion call back know
what the direction of motion is upon a first call. This also helps to keep the mouse pointer from getting confused by reseting
it upon a click. We also set the interactMode flag so the mouse motion callback can know if it is to rotate or scale. */
void mouseCall(int button, int state, int x, int y) {
if(button == GLUT_LEFT_BUTTON) {
if(state == GLUT_DOWN) {
xMS = x;
yMS = y;
} /* end if */
} /* end if */
} /* end func mouseCall */
/**********************************************************************************************************************************/
/* Quit if we get a 'q' or an escape key. */
void keyboardCall(unsigned char key, int x, int y) {
if((key=='q') || (key=='\e'))
exit(0);
} /* end func keyboardCall */
/**********************************************************************************************************************************/
/* On a mouse drag we rotate or scale (shift click). We compute the direction of mouse motion, find a vector 90 degrees off of
that, unproject it back into 3D space, and use that vector as the rotation vector. We use the delta in mouse motion to determine
the angle of rotation. If we are in scale mode, then we use the y-delta to determine the scale factor. */
void motionCall(int x, int y) {
float rz;
double x3d, y3d, z3d;
double x3d2, y3d2, z3d2;
int tx1, ty1, tx2, ty2;
GLint viewport[4];
GLdouble mvmatrix[16], projmatrix[16];
GLint yFlip;
int xdelta, ydelta;
glGetIntegerv (GL_VIEWPORT, viewport);
// Set the x/y delta, and pay attention to axis lock modifiers
xdelta = xMS-x;
ydelta = yMS-y;
/* Note that you simply use a z-depth to rotate along a vector orthogonal to the screen. If the delta is too big, or too small,
then we bail... */
if( (abs(xdelta)<(viewport[2]/2-5) && abs(ydelta)<(viewport[3]/2-5)) &&
(abs(xdelta)>=1 || abs(ydelta)>=1)) {
/* Magnitude of the angle (180 degrees per window width)... */
rz = -180.0*sqrt((xdelta)*(xdelta)+(ydelta)*(ydelta))/sqrt(viewport[2]*viewport[2]+viewport[3]*viewport[2]);
/* Figure out what our direction of movement is (90 degrees off) */
tx1 = viewport[2]/2;
ty1 = viewport[3]/2;
tx2 = tx1 + (ydelta);
ty2 = ty1 + (-xdelta);
/* Compute the world coordinate vectors corresponding to our rate of motion.. */
glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
yFlip = viewport[3] - (GLint) ty1 - 1;
gluUnProject ((GLdouble) tx1, (GLdouble) yFlip, 0.5, mvmatrix, projmatrix, viewport, &x3d2, &y3d2, &z3d2);
yFlip = viewport[3] - (GLint) ty2 - 1;
gluUnProject ((GLdouble) tx2, (GLdouble) yFlip, 0.5, mvmatrix, projmatrix, viewport, &x3d, &y3d, &z3d);
/* Subtract to compute the rotation vector */
x3d = x3d-x3d2;
y3d = y3d-y3d2;
z3d = z3d-z3d2; // Always zero!
/* Update the MODELVIEW matrix */
glMatrixMode(GL_MODELVIEW);
glRotatef(rz, x3d, y3d, z3d);
/* At this point we have a new MODELVIEW matrix, so we update the display */
glutPostRedisplay();
/* The global position must be updated even if we don't change anything.. */
xMS = x;
yMS = y;
}
} /* end func motionCall */
/**********************************************************************************************************************************/
/* How boring can a display update function be? This is just about it! */
void displayCall() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glCallList(1);
glutSwapBuffers();
} /* end func displayCall */
/**********************************************************************************************************************************/
/* Kick it off. Note we generate the geometry in main -- bad form, but this example is intended to illustrate how to interactively
rotate in an intuitive way...
*/
int main(int argc, char *argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(800, 800);
glutInitWindowPosition(10, 10);
glutCreateWindow("Interactive Rotate And Scale Demo Program");
glutDisplayFunc(displayCall);
glutMotionFunc(motionCall);
glutMouseFunc(mouseCall);
glutKeyboardFunc(keyboardCall);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(40, 1, 2*RADIUS, 10*RADIUS);
#if ANTIA
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POLYGON_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
#endif
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 0.0, 6.0*RADIUS, // Eye
0.0, 0.0, 0.0, // Look at
0.0, 1.0, 0.0); // Up
computeGeometry();
/* Start it up */
glutMainLoop();
return 0;
} /* end func main */
Generated by GNU Enscript 1.6.5.90.