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:

world.cpp

home Home   up Up   ( Download )


#include "world.h" #include "primitives.h" #include "vectors.h" #include "splines.h" // the time at which the picture was taken #define PIC_TIME 3.0 // number of static objects in scene #define STATIC_OBJECT_COUNT 3 #define GRASS_COUNT 30 #define FISH_COUNT 100 // random seed of terrain height variant #define TERRAIN_MAXHEIGHT 1.3 // world boundries #define WORLD_MINY 5.0 #define WORLD_MINX 0.0 #define WORLD_MINZ 0.0 #define WORLD_MAXY 200.0 #define WORLD_MAXX 100.0 #define WORLD_MAXZ 100.0 // size of floor base #define BASE_SIZE 150.0 // constructor World::World(): visibleDistance(VISIBLE_DISTANCE) // visible distance from eye { // load textures sandTex = new Texture("sand02.jpg"); //sandTex = new Texture("gold00.jpg"); // create floor terrain floorW = 100.0; floorD = 100.0; Vec3 sz(floorW, TERRAIN_MAXHEIGHT, floorD); floor = new TerrainGrid(6, 0.0, sz, sandTex); // initialize all world objects init(); // reset all world parameters to their starting // values reset(); } // destructor World::~World() { // destroy resources delete sandTex; delete floor; delete vehicle; // destroy all static objects for (int i = 0; i < STATIC_OBJECT_COUNT; i++) delete staticObject[i]; delete staticObject; // destroy all fish for (int i = 0; i < FISH_COUNT; i++) delete fish[i]; delete fish; // destroy grass for (int i = 0; i < GRASS_COUNT; i++) { delete grassObjects[i]; } delete grassObjects; } // init the world void World::init() { // init perspective glMatrixMode( GL_PROJECTION ); glLoadIdentity( ); gluPerspective( 45.0f, // viewing angle (GLfloat)SCREEN_WIDTH / (GLfloat)SCREEN_HEIGHT, // aspect ratio 0.1f, visibleDistance); // near, far clipping planes // init model view glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // initial opengl rendering state //glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glShadeModel(GL_SMOOTH); // setup lighting glEnable(GL_LIGHTING); GLfloat am[] = {0.0, 0.0, 0.0, 1.0}; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, am); // main light - the sun glEnable(GL_LIGHT0); GLfloat light0Pos[] = {SUN_X, SUN_Y, SUN_Z, 1.0}; glLightfv(GL_LIGHT0, GL_POSITION, light0Pos); GLfloat light0Ambient[] = {0.3, 0.3, 0.3, 1.0}; glLightfv(GL_LIGHT0, GL_AMBIENT, light0Ambient); GLfloat light0Diffuse[] = {SUN_INTENSITY, SUN_INTENSITY, SUN_INTENSITY, 1.0}; glLightfv(GL_LIGHT0, GL_DIFFUSE, light0Diffuse); GLfloat light0Specular[] = {SUN_INTENSITY, SUN_INTENSITY, SUN_INTENSITY, 1.0}; glLightfv(GL_LIGHT0, GL_SPECULAR, light0Specular); //glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 3); //glLightf (GL_LIGHT0, GL_SPOT_CUTOFF, 50.f); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_DIFFUSE); // setup "underwater" fog GLfloat fogColor[] = {0.0f, 0.729f, 0.67f, 1.0}; glClearColor(0.0f, 0.729f, 0.67f, 1.0 ); glFogfv(GL_FOG_COLOR, fogColor); glFogi(GL_FOG_MODE, GL_LINEAR); glHint(GL_FOG_HINT, GL_NICEST); glFogf(GL_FOG_START, visibleDistance/3); glFogf(GL_FOG_END, visibleDistance); #ifndef DEBUG glEnable(GL_FOG); #endif // init texture parameters glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // init landscape floor->clear(); // seed edges to 0 int sz = floor->width(); for (int a = 0; a < sz; a++) { floor->setHeight(a,0,0.0); floor->setHeight(a,sz-1,0.0); floor->setHeight(0,a,0.0); floor->setHeight(sz-1,a,0.0); } // generate terrain floor->generate(); // init thunderbird 4 vehicle = new VehicleObject(); // init static objects staticObject = new StaticObject*[STATIC_OBJECT_COUNT]; // uboat submarine staticObject[0] = new UBoatObject(); staticObject[0]->init(85, 7, 40, -30); // treasure chest staticObject[1] = new TreasureChestObject(); staticObject[1]->init(50, 2.5, 50, 45.0); //staticObject[1]->init(5, 3, 10, 0.0); // ship staticObject[2] = new ShipObject(); staticObject[2]->init(15, -2, 30, 20.0); // generate fish FishObject::initTextures(); fish = new FishObject*[FISH_COUNT]; for (int i = 0; i < FISH_COUNT; i++) { fish[i] = new FishObject(); } // generate grass grassObjects = new GrassObject*[GRASS_COUNT]; for (int i = 0; i < GRASS_COUNT; i++) { grassObjects[i] = new GrassObject(); } } // simulate the scene, updating the time // dT - the change in the world time in seconds // this will normally be the positive time // since the last simulate, but could be negative void World::simulate(GLfloat dT) { // update world time time += dT; // move vehicle vehicle->simulate(dT); // simulate fish for (int i = 0; i < FISH_COUNT; i++) { fish[i]->simulate(dT); } // wave fish for (int i = 0; i < GRASS_COUNT; i++) { grassObjects[i]->simulate(dT); } } // goto the time at which the picture was taken void World::gotoPicTime() { float dT = PIC_TIME - time; simulate(dT); } // resets the simulation to time 0 // and all objects to their state at time 0 void World::reset() { // set time to 0 time = 0.0; // set starting parameters vehicle->reset(); } // check for collision, and if // one occurs provides the point of // collision // position - vertex to check // direction - direction of vertex as it approached // result - when returns true contains the point of collision bool World::collisionDetect(Vec3* position, Vec3* direction, Vec3* result) { // save copy Vec3 pos(position); Vec3 dir(direction); // iterate to make sure that backing off // doesnt infringe another object bool boom = false; result->set(&pos); // check world boundries if (pos.y <= WORLD_MINY) { result->y = WORLD_MINY; boom = true; } if (pos.x <= WORLD_MINX) { result->x = WORLD_MINX; boom = true; } if (pos.z <= WORLD_MINZ) { result->z = WORLD_MINZ; boom = true; } if (pos.y >= WORLD_MAXY) { result->y = WORLD_MAXY; boom = true; } if (pos.x >= WORLD_MAXX) { result->x = WORLD_MAXX; boom = true; } if (pos.z >= WORLD_MAXZ) { result->z = WORLD_MAXZ; boom = true; } // check for terrain if (floor->collisionDetect(&pos, &dir, result)) boom = true; // check with static objects for (int i = 0; i < STATIC_OBJECT_COUNT; i++) { if (staticObject[i]->collisionDetect(&pos, &dir, result)) { boom = true; break; } } return boom; } // draws world that is currently // visible by the camera view int World::draw(CameraView* view) { // triangle count int tCount = 0; glMatrixMode(GL_MODELVIEW); glPushMatrix(); // orient camera view->lookAt(); // draw scene tCount += draw_landscape(view); #ifdef DEBUG glColor3f(1, 0, 0); vehicle->path.draw(); #endif // draw thunderbird 4 glTranslatef(5, 2, 5); vehicle->draw(view); // draw fish for (int i = 0; i < FISH_COUNT; i++) { fish[i]->draw(view); } // draw test spline /*SplineCurve c(4); c.set(0, 1, 1, 1); c.set(1, 1, 1, 2); c.set(2, 1, 1, 3); c.set(3, 1, 1, 4); c.set(4, 1, 1, 5); c.set(5, 1, 1, 6); c.set(6, 1, 1, 8); c.draw();*/ glPopMatrix(); // return triangle count return tCount; } // draw the landscape int World::draw_landscape(CameraView* view) { // triangle count int tCount = 0; // draw floor terrain map glColor3f(1.0, 1.0, 1.0); glPushMatrix(); tCount += floor->draw(view); // draw base square floor->tex->Begin(); glBegin(GL_QUADS); glTexCoord2f(floor->tex->minX()*BASE_SIZE*2, floor->tex->minY()*BASE_SIZE*2); glVertex3f(-BASE_SIZE, 0, -BASE_SIZE); glTexCoord2f(floor->tex->maxX()*BASE_SIZE*2, floor->tex->minY()*BASE_SIZE*2); glVertex3f(BASE_SIZE, 0, -BASE_SIZE); glTexCoord2f(floor->tex->maxX()*BASE_SIZE*2, floor->tex->maxY()*BASE_SIZE*2); glVertex3f(BASE_SIZE, 0, BASE_SIZE); glTexCoord2f(floor->tex->minX()*BASE_SIZE*2, floor->tex->maxY()*BASE_SIZE*2); glVertex3f(-BASE_SIZE, 0, BASE_SIZE); glEnd(); floor->tex->End(); glPopMatrix(); // draw grass for (int i = 0; i < GRASS_COUNT; i++) { grassObjects[i]->draw(view); } // draw static objects for (int i = 0; i < STATIC_OBJECT_COUNT; i++) staticObject[i]->draw(view); return tCount; }