3D Underwater World (using OpenGL and SDL)

Copyright Tristan Aubrey-Jones January 2008.

This is an example OpenGL SDL application which creates an animated 3D underwater world (screenshot), with "Thunderbird 4" with headlights moving on a spline path, a randomly generated sand terrain, sunken ship/submarine/treasure chest models, and swaying fish and seaweed. It is designed to demonstrate basic OpenGL features, and the structure is as follows:

game.cpp

home Home   up Up   ( Download )


#include "game.h" #include "vectors.h" #include "splines.h" // the amount to increase the // angular velocity of the camera // on each key press #define CAM_PANH_INC 50.0 #define CAM_PANV_INC 50.0 // the amount to increment the // forward speed on each key press #ifdef DEBUG #define CAM_FWDV_INC 20.0 #else #define CAM_FWDV_INC 15.0 #endif // constructor Game::Game(): running(false), // not running world(new World()), // create a new world // CAMERA camView(45, (float)SCREEN_WIDTH/(float)SCREEN_HEIGHT, 0.1, VISIBLE_DISTANCE), // camera perspective camFwdV(0.0), // the initial camera speed camFwdA(0.0), // acceleration of camera speed camHorizAV(0.0), // angluar velocity (deg per s) in xz plane when turning camVertAV(0.0), // anglular v (deg per s) in the zy plane when turning // TOUR tourRunning(false), // tour starts disabled. tourPeriod(30), // length of tour in seconds tourPosPath(10), // path of cam positions tourObjPath(19) // path of observed objects { // init tour paths /*tourPosPath.set(0, 0, 5, 0); tourObjPath.set(0, 100, 0, 0); tourPosPath.set(1, 100, 5, 0); tourObjPath.set(1, 100, 0, 100); tourPosPath.set(2, 100, 5, 100); tourObjPath.set(2, 0, 0, 100); tourPosPath.set(3, 0, 5, 100); tourObjPath.set(3, 0, 0, 0);*/ // start - approaching treasure tourPosPath.set(0, 35, 15, 20); tourPosPath.set(1, 35, 15, 20); tourPosPath.set(2, 35, 15, 20); tourPosPath.set(3, 60, 25, 20); tourPosPath.set(4, 50, 15, 30); tourPosPath.set(5, 40, 5, 40); tourPosPath.set(6, 20, 25, 50); tourPosPath.set(7, 20, 25, 30); tourPosPath.set(8, 10, 10, 10); tourPosPath.set(9, 20, 7, 15); /*tourPosPath.set(4, 25, 5+5.5, 50); tourPosPath.set(5, 25, 5+5.5, 50); tourPosPath.set(6, 25, 5+5.5, 50); tourPosPath.set(7, 25, 5+5.5, 50); tourPosPath.set(8, 25, 5+5.5, 50); tourPosPath.set(9, 25, 5+5.5, 50);*/ // start - approaching treasure const float y = 5; tourObjPath.set(0, 30, y+5.5, 30); tourObjPath.set(1, 32, y+5, 32); tourObjPath.set(2, 34, y+4.5, 34); tourObjPath.set(3, 36, y+4, 36); tourObjPath.set(4, 38, y+3.5, 38); tourObjPath.set(5, 40, y+3, 40); tourObjPath.set(6, 55, y+5, 35); // turning right toward uboat tourObjPath.set(7, 60, y+5, 10); tourObjPath.set(8, 75, y+5, 10); // along side of uboat tourObjPath.set(9, 80, y+3, 20); tourObjPath.set(10, 70, y+3, 35); tourObjPath.set(11, 60, y+3, 50); tourObjPath.set(12, 50, y+3, 70); tourObjPath.set(13, 40, y+3, 85); // heading for ship tourObjPath.set(14, 25, y+5, 85); tourObjPath.set(15, 15, y+5, 80); tourObjPath.set(16, 15, y+5, 70); tourObjPath.set(17, 35, y+15, 50); // going past ship tourObjPath.set(18, 10, y+15, 40); // move camera to starting position #ifdef BIRDSEYE Vec3 cop(50.0, 125.0, 50); Vec3 above(51.0, 125.0, 50); Vec3 obj(50, 0.0, 50); #elif defined(SIDEON) Vec3 cop(5.0, 6.0, 5.0); Vec3 above(5.0, 7.0, 5.0); Vec3 obj(5.0, 6.0, 20.0); #else Vec3 cop(10.0, 5.0, -10); Vec3 above(10.0, 6.0, -10); Vec3 obj(5.0, 0.0, 5.0); #endif camView.set(&cop, &obj, &above); } // destructor Game::~Game() { delete world; world = NULL; } // move to predefined image location void Game::viewPicture() { // stop camera moving camFwdV = 0.0; camFwdA = 0.0; camHorizAV = 0.0; camVertAV = 0.0; // move camera to location Vec3 pos(40,10,5), obj(30, 15, 100), above(0, 1, 0); above.add(&pos); camView.set(&pos, &obj, &above); // move time to correct point world->gotoPicTime(); } // start and stop automatic tour void Game::startTour() { // reset all animation reset(); // stop camera moving (so stopped at end of tour) camFwdV = 0.0; camFwdA = 0.0; camHorizAV = 0.0; camVertAV = 0.0; // move camera to start position tourStep(0.0); // enable tour tourRunning = true; } void Game::stopTour() { // disable tour tourRunning = false; } // move along the tour path void Game::tourStep(GLfloat dT) { // update the camera view to the new position tourPosPath.move(dT / tourPeriod); tourObjPath.move(dT / tourPeriod); Vec3 pos, obj, above; tourPosPath.getNow(&pos, &above); tourObjPath.getNow(&obj, &above); above.set(&pos); above.add(0, 1, 0); camView.set(&pos, &obj, &above); } void Game::reset() { world->reset(); tourPosPath.reset(); tourObjPath.reset(); } // manual camera movement // update camera orientation void Game::camUpdate(GLfloat dT) { // update camera speed camFwdV += (camFwdA * dT); // update camera rotation if (camHorizAV != 0.0) { //glRotatef(camHorizAV*dT, 0.0, 1.0, 0.0); // about y axiz camView.rotateHoriz(camHorizAV*dT); } if (camVertAV != 0.0) { //glRotatef(camVertAV*dT, 1.0, 0.0, 0.0); // about x axis camView.rotateVert(camVertAV*dT); } // update camera position if (camFwdV != 0.0) { camView.moveForward(camFwdV * dT); //glTranslatef(0.0, 0.0, camFwdV * dT); // going towards z axis } // collision detect for user movement Vec3 result; Vec3 pos; camView.getPosition(&pos); Vec3 dir; camView.getForwardVector(&dir); if (world->collisionDetect(&pos, &dir, &result)) { // try to move backwards by correct amount // calculate distance to reverse result.subtract(&pos); float l = result.length(); if (camFwdV > 0) l = -l; camView.moveForward(l); // kill the forward velocity (go slightly backward) if (camFwdV < 0) camFwdV = 0.1; else camFwdV = -0.1; // if that hasnt helped then translate if (world->collisionDetect(&pos, &dir, &result)) { Vec3 obj(&result); obj.add(&dir); Vec3 above(&result); above.add(0, 1, 0); camView.set(&result, &obj, &above); } } } // shows help file void Game::showHelp() { system("pause| type help.txt"); } // handles SDL events void Game::handleEvent(SDL_Event* event) { // key presses if (event->type == SDL_KEYDOWN) { // if ESC then quit if (event->key.keysym.sym == SDLK_ESCAPE) exit(); else { // switch on character switch (event->key.keysym.sym) { // Q => Quit case(SDLK_q): exit(); break; case(SDLK_h): showHelp(); break; // RESET case(SDLK_r): world->reset(); if (tourRunning) startTour(); break; // start and stop tour mode case(SDLK_t): startTour(); break; case(SDLK_e): stopTour(); break; // move to predefined picture location case(SDLK_p): if (!tourRunning) viewPicture(); break; // camera horizontal pan case(SDLK_LEFT): camHorizAV -= CAM_PANH_INC; break; case(SDLK_RIGHT): camHorizAV += CAM_PANH_INC; break; // camera elevation case(SDLK_PAGEUP): camVertAV += CAM_PANV_INC; break; case(SDLK_PAGEDOWN): camVertAV -= CAM_PANV_INC; break; // camera forward velocity case(SDLK_UP): camFwdA += CAM_FWDV_INC; break; case(SDLK_DOWN): camFwdA -= CAM_FWDV_INC; break; // stop all motion case(SDLK_SPACE): camFwdA = 0; camFwdV = 0; break; } } } // key releases else if (event->type == SDL_KEYUP) { switch (event->key.keysym.sym) { // camera horizontal pan case(SDLK_LEFT): camHorizAV = 0; break; case(SDLK_RIGHT): camHorizAV = 0; break; // camera elevation case(SDLK_PAGEUP): camVertAV = 0; break; case(SDLK_PAGEDOWN): camVertAV = 0; break; // camera forward velocity case(SDLK_UP): #ifdef DEBUG camFwdV = 0; #endif camFwdA = 0; break; case(SDLK_DOWN): #ifdef DEBUG camFwdV = 0; #endif camFwdA = 0; break; } } // if is a quit message else if (event->type == SDL_QUIT) exit(); } // starts the game void Game::run() { // main loop viewPicture(); GLfloat T = getTime(); running = true; while (running) { // progress simulation GLfloat Tn = getTime(); GLfloat dT = Tn - T; world->simulate(dT); if (tourRunning) tourStep(dT); else camUpdate(dT); T = Tn; // clear scene, ready to draw again glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // render visible scene int tCount = world->draw(&camView); #ifdef DEBUG glPushMatrix(); camView.lookAt(); glColor3f(0.0, 1.0, 0.0); tourPosPath.draw(); glPopMatrix(); #endif //printf("triangles: %i\n", tCount); glFlush(); SDL_GL_SwapBuffers(); // check for errors GLenum glError = glGetError(); if(glError != GL_NO_ERROR) fprintf( stderr, "comp3004/cwk3/opengl/error: %d\n", glError); char* sdlError = SDL_GetError(); if (sdlError != NULL && (*sdlError) != 0) fprintf(stderr, "comp3004/cwk3/sdl/error: %s\n", sdlError); // handle events SDL_Event event; while (SDL_PollEvent(&event)) handleEvent(&event); } } // exit void Game::exit() { running = false; }