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:
#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;
}