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 "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;
}