| 3D Animation with OpenGL |
| #include "stdafx.h" #include "3DGallery.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // The one and only application object CWinApp theApp; #include <cstdio> #include <GL/glut.h> using namespace std; #include <cmath> //#include <stdlib.h> //#include <GL/glut.h> //Global variables GLfloat vertices[][3] = { {-20.0,-10.0,-20.0}, {20.0,-10.0,-20.0}, {20.0,10.0,-20.0}, {-20.0,10.0,-20.0}, {-20.0,-10.0,20.0}, {20.0,-10.0,20.0}, {20.0,10.0,20.0}, {-20.0,10.0,20.0}, {60.0,-10.0,80.0}, {60.0,10.0,80.0}, {-60.0,-10.0,80.0}, {-60.0,10.0,80.0}, {60.0,-10.0,40.0}, {60.0,10.0,40.0}, {-60.0,-10.0,40.0}, {-60.0,10.0,40.0}, {10.0,-10.0,40.0}, {10.0,10.0,40.0}, {-10.0,-10.0,40.0}, {-10.0,10.0,40.0}, {10.0,-10.0,20.0}, {10.0,10.0,20.0}, {-10.0,-10.0,20.0}, {-10.0,10.0,20.0}, {10.0,-10.0,40.0}, {10.0,10.0,40.0}, {-10.0,-10.0,40.0}, {-10.0,10.0,40.0}, {10.0,-10.0,20.0}, {10.0,10.0,20.0}, {-10.0,-10.0,20.0}, {-10.0,10.0,20.0}, }; GLfloat normals[][3] = {{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; GLfloat colors[][4] = {{0.0,0.0,0.0,1.0},{1.0,0.0,0.0,1.0}, {1.0,1.0,0.0,1.0}, {0.0,1.0,0.0,1.0}, {0.0,0.0,1.0,1.0}, {1.0,0.0,1.0,1.0}, {1.0,1.0,1.0,1.0}, {0.0,1.0,1.0,1.0}}; GLfloat eyeX = 0.0, eyeY = 0.0, eyeZ = 0.0; //Viewing coord origin GLfloat atX = 0.0, atY = 0.0, atZ = -1.0; //Look-at point GLfloat upX = 0.0, upY = 1.0, upZ = 0.0; //View-up vector //Near and far clipping planes GLfloat dnear = 2-1.0, dfar = 100.0; GLint bitMapSize = 512; GLubyte my_texels[512*4][512*4]; static GLdouble viewer[]= {eyeX, eyeY, eyeZ}; // initial viewer location static GLdouble look_at[] = {atX, atY, atZ}; // initial look at location const float pi = acos(-1.f); |
| Since I opted for MFC support, some of the header code was put in by Visual Studio (VS), including: #include "stdafx.h" which includes standard MS windows/MFC functions. CWinApp theApp; is also added by VS, along with 3DGallery.h (a header with the same name as the application itself) and the debug definitions. In this kind of code we may have variables defined in C++ syntax or in OpenGL syntax. The prefix 'GL' indicates an OpenGL variable, so the global variable GLfloat vertices[][3] is an OpenGL 2D array of float values which contains an unspecified number of 3-tuples or triplets, which are the (x,y,z) position vectors of the corners of the two rooms and joining corridor that make-up this scene. We also have 2D arrays holding normals and colours; note the GLfloats specifying the initial (x,y,z) coordinates of the 'eye' or viewer, since we are using a first-person view this is essentially the position of the camera. The at values tell the camera in which direction to look; whatever direction this is, the distance between the eye vector and the look at vector is kept fixed to 1 unit at all times. The view-up vector tells OpenGL the rotation of the camera, in this case the camera is level, so its up-vector points straight-up (in the +y direction). Note also the calculation of the constant pi (3.14...) as arccos(-1) - check it on a calculator! The code below follows straight on from the above: |
| void polygon(int a, int b, int c , int d , int e, short tex) { CBitmap Image; int imgSize = 512; bool Tile = false; try { switch (tex) { case 1: Image.LoadBitmap(IDB_BITMAP1); Image.GetBitmapBits((512*512)*4,my_texels); imgSize = 512; break; case 2: Image.LoadBitmap(IDB_BITMAP2); Image.GetBitmapBits((64*64)*4,my_texels); imgSize = 64; Tile = true; break; case 3: Image.LoadBitmap(IDB_BITMAP6); Image.GetBitmapBits((512*512)*4,my_texels); imgSize = 512; break; case 4: Image.LoadBitmap(IDB_BITMAP8); Image.GetBitmapBits((512*512)*4,my_texels); imgSize = 512; break; case 5: Image.LoadBitmap(IDB_BITMAP9); Image.GetBitmapBits((512*512)*4,my_texels); imgSize = 512; break; default: Image.LoadBitmap(IDB_BITMAP5); Image.GetBitmapBits((512*512)*4,my_texels); imgSize = 512; break; } } catch(...) { //Error accessing resources! } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //Default? glTexImage2D(GL_TEXTURE_2D, 0, 4, imgSize, imgSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, my_texels); glEnable(GL_TEXTURE_2D); if(Tile == false) { GLfloat specularCoeff[] = { 1.0, 1.0, 1.0, 1.0 }; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colors[e]); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularCoeff); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 25.0); glBegin(GL_POLYGON); //glColor3fv(colors[a]); glNormal3fv(normals[a]); glTexCoord2f(0.0, 0.0); glVertex3fv(vertices[a]); //glColor3fv(colors[b]); glNormal3fv(normals[b]); glTexCoord2f(1.0, 0.0); glVertex3fv(vertices[b]); //glColor3fv(colors[c]); glNormal3fv(normals[c]); glTexCoord2f(1.0, 1.0); glVertex3fv(vertices[c]); //glColor3fv(colors[d]); glNormal3fv(normals[d]); glTexCoord2f(0.0, 1.0); glVertex3fv(vertices[d]); glEnd(); } else //Tile == true { int numTilesX = 8; int numTilesZ = 8; //CBitmap largeImage; GLfloat specularCoeff[] = { 1.0, 1.0, 1.0, 1.0 }; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colors[e]); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularCoeff); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 25.0); for(int Z = 1; Z <= numTilesZ; Z++) { for(int X = -1; X <= numTilesX-1; X++) { double X0 = vertices[a][0]; double Y0 = vertices[a][1]; double Z0 = vertices[a][2]; double Xi = X0 + ((40*X)/numTilesX); double Zi = X0 + ((40*Z)/numTilesZ); double Xf = Xi + 40/numTilesX; double Zf = Zi - 40/numTilesZ; glBegin(GL_POLYGON); //glColor3fv(colors[a]); glNormal3fv(normals[a]); glTexCoord2f(0.0, 0.0); glVertex3f(Xi,Y0,Zi); glColor3fv(colors[b]); glNormal3fv(normals[b]); glTexCoord2f(1.0, 0.0); glVertex3f(Xf,Y0,Zi); //glColor3fv(colors[c]); glNormal3fv(normals[c]); glTexCoord2f(1.0, 1.0); glVertex3f(Xf,Y0,Zf); //glColor3fv(colors[d]); glNormal3fv(normals[d]); glTexCoord2f(0.0, 1.0); glVertex3f(Xi,Y0,Zf); glEnd(); } } } glDisable(GL_TEXTURE_2D); glFlush(); } void buildGraphic() { // int a,b,c,d,e,tex // a,b,c,d = vertices, colours and normals // e = material colour // tex = bitmap number polygon(0,3,2,1,1,1); //Far Wall polygon(2,3,7,6,2,1); //Roof polygon(0,4,7,3,1,4); //Left wall polygon(1,2,6,5,1,3); //Right wall //polygon(4,5,6,7,5,1); //Near wall //Doorway polygon(4,30,31,7,1,1); polygon(28,5,6,29,1,1); //Floor polygon(0,1,5,4,6,2); //Main room polygon(8,10,11,9,1,1); //Far wall //polygon(12,14,15,13,1,1); //Near wall //Doorway polygon(12,24,25,13,1,1); polygon(26,14,15,27,1,1); //Floor polygon(8,12,14,10,1,3); //Ceiling polygon(9,13,15,11,1,1); //Bridge polygon(30,28,24,26,1,5); //Floor polygon(26,30,31,27,1,1); //Left wall polygon(24,28,29,25,1,1); //Right wall polygon(31,29,25,27,1,1); //Ceiling } void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Update viewer position in modelview matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(viewer[0],viewer[1],viewer[2], look_at[0], look_at[1], look_at[2], upX, upY, upZ); //collisionDetection(); buildGraphic(); glutSwapBuffers(); glFlush(); } |
| void specialKeys(int specialkey, int x, int y) { double deltaX = look_at[0] - viewer[0]; double deltaZ = look_at[2] - viewer[2]; double newDeltaX, newDeltaZ; double angle = 18; double angleRad = (angle/360)*2*pi; if(specialkey == GLUT_KEY_LEFT) { //CCW rotation //Change delta newDeltaX = deltaX*cos(angleRad) + deltaZ*sin(angleRad); newDeltaZ = -deltaX*sin(angleRad) + deltaZ*cos(angleRad); //Change look_at look_at[0] = viewer[0] + newDeltaX; look_at[2] = viewer[2] + newDeltaZ; } if(specialkey == GLUT_KEY_RIGHT) { //CW rotation //Change delta newDeltaX = deltaX*cos(angleRad) - deltaZ*sin(angleRad); newDeltaZ = deltaX*sin(angleRad) + deltaZ*cos(angleRad); //Change look_at look_at[0] = viewer[0] + newDeltaX; look_at[2] = viewer[2] + newDeltaZ; } //Move in direction of look_at[] if(specialkey == GLUT_KEY_PAGE_UP) { viewer[0] = look_at[0]; viewer[2] = look_at[2]; look_at[0] = look_at[0] + deltaX; look_at[2] = look_at[2] + deltaZ; } if(specialkey == GLUT_KEY_PAGE_DOWN) { viewer[0] = viewer[0] - deltaX; viewer[2] = viewer[2] - deltaZ; look_at[0] = look_at[0] - deltaX; look_at[2] = look_at[2] - deltaZ; } if(specialkey == GLUT_KEY_UP) { //viewer[1] += 1.0; look_at[1] += 1.0; if(look_at[1] >= 20) { look_at[1] = 20; } } if(specialkey == GLUT_KEY_DOWN) { //viewer[1] -= 1.0; look_at[1] -= 1.0; if(look_at[1] <= -20) { look_at[1] = -20; } } display(); } void myReshape(int w, int h) { glViewport(0, 0, w, h); //Use a perspective view glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glFrustum(-2.0, 2.0, -2.0 * (GLfloat) h/ (GLfloat) w, 2.0* (GLfloat) h / (GLfloat) w, dnear, dfar); else glFrustum(-2.0, 2.0, -2.0 * (GLfloat) w/ (GLfloat) h, 2.0* (GLfloat) w / (GLfloat) h, dnear, dfar); glMatrixMode(GL_MODELVIEW); } void Lights(void) { //Global ambience GLfloat global_ambient[] = {2,0.2,0.2,1.0}; //A reddish light glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient); //Light source GLfloat light1PosType[] = { 9.0, 9.0, 9.0, 1.0 }; GLfloat light2PosType[] = { -9.0, 9.0, -9.0, 1.0 }; GLfloat ambientColor[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat diffuseColor[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat specularColor[] = { 1.0, 1.0, 1.0, 1.0 }; //Light colours glLightfv(GL_LIGHT1, GL_AMBIENT, ambientColor); glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuseColor); glLightfv(GL_LIGHT1, GL_SPECULAR, specularColor); // glLightfv(GL_LIGHT2, GL_AMBIENT, ambientColor); glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuseColor); glLightfv(GL_LIGHT2, GL_SPECULAR, specularColor); //Light attenuation glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.5-1); glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.75-0.75); glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.4+0.6); // glLightf(GL_LIGHT2, GL_CONSTANT_ATTENUATION, 1.5-1); glLightf(GL_LIGHT2, GL_LINEAR_ATTENUATION, 0.75-0.75); glLightf(GL_LIGHT2, GL_QUADRATIC_ATTENUATION, 0.4+0.6); //Switch light on! glEnable(GL_LIGHT1); glEnable(GL_LIGHT2); glEnable(GL_LIGHTING); } |
| int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; // initialize MFC and print and error on failure if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) { // TODO: change error code to suit your needs _tprintf(_T("Fatal Error: MFC initialization failed\n")); nRetCode = 1; } else { // TODO: code your application's behavior here. glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(600, 600); glutCreateWindow("Launch Bay"); glutReshapeFunc(myReshape); Lights(); glutDisplayFunc(display); glutSpecialFunc(specialKeys); glEnable(GL_DEPTH_TEST); glutMainLoop(); } return nRetCode; } |
| void buildProbe() { GLUquadricObj * sphere2; sphere2 = gluNewQuadric(); GLUquadricObj * cylinder_1; cylinder_1 = gluNewQuadric(); gluQuadricDrawStyle(sphere2, GLU_FILL); gluQuadricDrawStyle(cylinder_1, GLU_FILL); glColor3f(0.0,1.0,1.0); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, colors[6]); glPushMatrix(); glTranslatef(probeTranslate,5,60); gluSphere (sphere2,5,20,20); glPopMatrix(); glPushMatrix(); glTranslatef(probeTranslate,5,60); glRotatef(45,1,0,0); gluCylinder (cylinder_1,2,0.1,10,10,10); glPopMatrix(); glPushMatrix(); glTranslatef(probeTranslate,5,60); glRotatef(-45,1,0,0); glRotatef(180,0,1,0); gluCylinder (cylinder_1,2,0.1,10,10,10); glPopMatrix(); } |
| void mouse(int btn, int state, int x, int y) { switch(btn) { case GLUT_RIGHT_BUTTON: if(state == GLUT_DOWN) { _beginthread( animate, 0, (void*)0 ); } break; case GLUT_LEFT_BUTTON: if(state == GLUT_DOWN) { //does nothing yet! } break; default: break; } } |
| void animate(void * arg) { for(int i = 1; i <= 10000; i++) { Sleep(100); probeTranslate = probeTranslate + 0.1*i; glutPostRedisplay(); } } |
| //First add the following two global variables static int collisionWalls = 10; //Number of items i in array [i][6] below: static double collisionHull[10][6] = { //Inner walls parallel to x-axis //Xmin,Xmax,-Dz,Nx,Ny,Nz {-20,20,20,0,0,1},{-60,60,-80,0,0,-1},{10,20,-20,0,0,-1},{-20,-10,-20,0,0,-1}, {-60,-10,-40,0,0,1}, {10,60,-40,0,0,1}, //Inner walls parallel to z-axis //Zmin,Zmax,-Dx,Nx,Ny,Nz {20,40,-10,-1,0,0}, {20,40,10,1,0,0}, {-20,20,-20,-1,0,0}, {-20,20,20,1,0,0} }; //Add this global variable below the definitions for eyeX, eyeY and eyeZ //This is similar to the viewer[] array and will store the initial positions when the viewer is moving static GLdouble view_buffer[] = {eyeX, eyeY, eyeZ}; |
| //Modify the PAGE-UP and PAGE-DOWN key press functions in specialKeys() function //to check for collisions before moving viewer by calling the collisionDetection() function if(specialkey == GLUT_KEY_PAGE_UP) { view_buffer[0] = look_at[0]; view_buffer[2] = look_at[2]; if(collisionDetection() == true) { return; } else { viewer[0] = view_buffer[0]; viewer[2] = view_buffer[2]; look_at[0] = look_at[0] + deltaX; look_at[2] = look_at[2] + deltaZ; } } if(specialkey == GLUT_KEY_PAGE_DOWN) { view_buffer[0] = viewer[0] - deltaX; view_buffer[2] = viewer[2] - deltaZ; if(collisionDetection() == true) { return; } else { viewer[0] = view_buffer[0]; viewer[2] = view_buffer[2]; look_at[0] = look_at[0] - deltaX; look_at[2] = look_at[2] - deltaZ; } } |
| bool collisionDetection() { //return false; //Switch off //Test against each wall as a polygon, e.g. (z = -20, from x = -20 to x = 20) //Normal = (0,0,1), D = 20 //Viewer as sphere, radius = 2 double R = 2.0; double distance, initDistance, min, max, Dx, Dz, Nx, Nz; int index; for(int i = 0; i <= collisionWalls-1; i++) { min = collisionHull[i][0]; max = collisionHull[i][1]; Nx = collisionHull[i][3]; Nz = collisionHull[i][5]; index = 2*abs((int)Nx); if(view_buffer[0+index] < min || view_buffer[0+index] > max) { continue; } else if(view_buffer[0+index] > min && view_buffer[0+index] < max) { Dx = collisionHull[i][2] - Nx*R; Dz = collisionHull[i][2] - Nz*R; initDistance = Nz*(viewer[2] + Dz) + Nx*(viewer[0] + Dx); distance = Nz*(view_buffer[2] + Dz) + Nx*(view_buffer[0] + Dx); if(distance * initDistance <= 0) { return true; } else { continue; } } } return false; } |
| More OpenGL |