/* Displacement Orientation Example By: Michael Letourneau NB: This program is released into the public domain with one caveat: Use of any code from this program should be appropriately cited within the work in which it is used. This program demonstrates how to rotate a 3D model to 'point' towards a given location in a 3D space. The program randomly chooses two points in 3D, one 'target' and one 'object'. A cube is drawn at the target. The object is represented by two triangles. When the object is drawn, the triangles always point towards the target, regardless of their respective locations. Pressing the 'r' key will randomly choose new points for the target and object and will cause the screen to be redrawn. Pressing 'q' or ESC will quit the program. NB: This program uses orthographic projection. Thus, there is no perspective applied to the drawing. This may cause things to look slightly out of place. Consult the pre-function comments for more information on how this orientation is accomplished. */ /* Preprocessor directives */ #if !defined(Linux) #include //Not Linux must be windows #endif #include #include #include #include #include #include #include #include /* Used to seed the random number */ #include /* generator. */ /* Macro definitions */ #define XL_BOUND -10000.0 /* Defines the limits on the size of the */ #define XU_BOUND 10000.0 /* 3D world. */ #define YL_BOUND -10000.0 #define YU_BOUND 10000.0 #define ZL_BOUND -10000.0 #define ZU_BOUND 10000.0 #if !defined(Linux) #define M_PI 3.14159265358979323846f //VS #endif #define WINDOW_SIZE 800 #define WORLD_MARGIN 1500.0 /* Function declarations */ void setLocations(void); void display (void); void keyboard(unsigned char, int, int); void drawObject (float, float); void drawcube (void); /* Type declarations */ struct LocationStruct { /* 3D point */ float x; float y; float z; }; typedef struct LocationStruct Location; /* Global data structure */ struct glob { Location object; Location target; int MotionFlag; int animateCounter; }; struct glob global; /* void init (void) This function seeds the RNG. */ void init (void) { // struct tms buf; //SGI //srand48(times(&buf)); //SGI srand( (unsigned)time( NULL ) ); //VS } /* double boundedRandom (double lowBound, double highBound) This function returns a randomly determined value in the range [lowBound, ~highBound). */ double boundedRandom (double lowBound, double highBound) { //return (drand48() * (highBound - lowBound)) + lowBound; //SGI return (double)(((double)rand()/RAND_MAX) * (highBound - lowBound) + lowBound); //VS } /* void display (void) This function is the GLUT display callback. It will determine the offset between the object and target, compute the necessary rotation angles for the object, and then call the functions necessary to draw the object and target. The rotations for the object are slightly obscure, but doing them in this manner ensures that OpenGL will draw the model correctly. (These are correct for a model in which 'forward' faces the +Z direction.) The first rotation is derived from the following expression: tan(theta1) = dx / dz (where dx, dy, and dz are the displacement from the target to the object) This rotation is performed around the Y axis. The second rotation is derived from the following expression, which takes into account the reorientation of the X and Z axes caused by the first rotation. tan(theta2) = dy / sqrt(dx*dx + dz*dz) (The (dx*dx + dz*dz) value is the length of the hypotenuse of the triangle used to determine the first rotation. It is because the orientation of the points is to a different set of axes after the first rotation is made.) */ void display (void) { float dx, dy, dz, theta1, theta2; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); dx = global.target.x - global.object.x; dy = global.target.y - global.object.y; dz = global.target.z - global.object.z; theta1 = ((atan2(dx, dz) / M_PI) * 180); theta2 = -((atan2(dy,sqrt(pow(dx, 2) + pow(dz, 2))) / M_PI) * 180); drawObject(theta1, theta2); drawcube(); glFlush(); } /* void setLocations (void) This function randomly locates the target and object points in the 3D world. */ void setLocations (void) { global.target.x = boundedRandom(XL_BOUND + WORLD_MARGIN, XU_BOUND - WORLD_MARGIN); global.target.y = boundedRandom(YL_BOUND + WORLD_MARGIN, YU_BOUND - WORLD_MARGIN); global.target.z = boundedRandom(ZL_BOUND + WORLD_MARGIN, ZU_BOUND - WORLD_MARGIN); global.object.x = boundedRandom(XL_BOUND + WORLD_MARGIN, XU_BOUND - WORLD_MARGIN); global.object.y = boundedRandom(YL_BOUND + WORLD_MARGIN, YU_BOUND - WORLD_MARGIN); global.object.z = boundedRandom(ZL_BOUND + WORLD_MARGIN, ZU_BOUND - WORLD_MARGIN); } /* void keyboard (unsigned char key, int x, int y) A highly sophisticated GLUT keyboard callback. */ void keyboard(unsigned char key, int x, int y) { switch (key) { case 'r': case 'R': setLocations(); glutPostRedisplay(); break; case 0x1B: case 'q': case 'Q': exit(0); break; } } /* void drawcube () This function draws a cube at the target location. It is given a slight rotation, just to add perspective. This function was adapted from Prof. Ross's cube models from 'rotate2.c' and 'rotate_light.c'. It returns no value. */ void drawcube () { Location loc = global.target; float size = 150.0; int i; int p[][3] = {{1,1,1}, {1,-1,1}, {-1,-1,1}, {-1,1,1}, {1,1,-1}, {1,-1,-1}, {-1,-1,-1}, {-1,1,-1}}; int e[][4] = {{0,3,2,1},{3,7,6,2},{7,4,5,6},{4,0,1,5}, {0,4,7,3},{1,2,6,5}}; float c[][3] = {{1.0,0,0},{0,1.0,0},{1.0,0.0,1.0}, {0,0,1.0},{.6,0,.6},{0,.6,.6}}; glPushMatrix(); glTranslatef(loc.x, loc.y, loc.z); glScalef(size, size, size); glRotatef(20.0, 1, 0, 0); glRotatef(25.0, 0, 1, 0); glRotatef(30.0, 0, 0, 1); for (i=0; i < 6; ++i) { glBegin(GL_QUADS); glColor3fv(c[i]); glVertex3iv(p[e[i][0]]); glVertex3iv(p[e[i][1]]); glVertex3iv(p[e[i][2]]); glVertex3iv(p[e[i][3]]); glEnd(); } glPopMatrix(); } /* void drawObject (float theta1, float theta2) This function will draw two triangles at the location given by the object's location. The trangles are deemed to 'point' in the +Z direction within the model. The points of the triangles which point in that direction are coloured white. The other two points are coloured red. OpenGL interpolates the colours in between. The parameters 'theta1' and 'theta2' are used as described in the 'display()' function to orient the model to point towards the target. Note that the coordinates for the model are constants. OpenGL takes care of all the math necessary to apply those rotations to the points so that they are located in the correct place. */ void drawObject (float theta1, float theta2) { Location loc = global.object; float size = 1000.0; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef(loc.x, loc.y, loc.z); glScalef(size, size, size); glRotatef(theta1, 0.0, 1.0, 0.0); glRotatef(theta2, 1.0, 0.0, 0.0); glBegin(GL_TRIANGLES); glColor3f(1.0, 1.0, 1.0); glVertex3f(0.0, 0.0, 1.0); glColor3f(1.0, 0.0, 0.0); glVertex3f(0.0, 1.0, 0.0); glVertex3f(0.0, -1.0, 0.0); glColor3f(1.0, 1.0, 1.0); glVertex3f(0.0, 0.0, 0.0); glColor3f(1.0, 0.0, 0.0); glVertex3f(0.0, 1.0, -1.0); glVertex3f(0.0, -1.0, -1.0); glEnd(); glPopMatrix(); } /* void main (int argc, char **argv) Main entry point for the program. (Exciting, isn't it?) */ main (int argc, char **argv) { glutInit(&argc, argv); glutInitWindowSize(WINDOW_SIZE, WINDOW_SIZE); glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH); glutCreateWindow("Oriented Triangles"); init(); setLocations(); printf("Oriented triangles. They point towards the cube.\n"); printf("(And I'll bet you were told it was impolite to point. :-P\n\n"); printf("Press 'r' to pick new points, or 'q' to quit.\n\n"); glutKeyboardFunc(keyboard); glutDisplayFunc(display); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.0, 0.0, 0.0, 1.0); glEnable(GL_DEPTH_TEST); glOrtho(XL_BOUND, XU_BOUND, YL_BOUND, YU_BOUND, ZL_BOUND, ZU_BOUND); glPushMatrix(); glutMainLoop(); }