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:

primitives.cpp

home Home   up Up   ( Download )


#include "primitives.h" // start drawing face void FaceInfo::Begin() { glColor3f(cr, cg, cb); if (t != NULL) t->Begin(); } void FaceInfo::Vertex(int i) { if (t != NULL) { // which coordinates float x, y; if (i < 2) { x = tl; } else { x = tr; } if (i % 2 == 0) y = tb; else y = tt; // map texture coordinate glTexCoord2f(x, y); } } // stop drawing face void FaceInfo::End() { if (t != NULL) t->End(); } // init all faces void FaceInfo::initBox(FaceInfo* array, float r, float g, float b) { // init all faces to this colour for (int i = 0; i < 6; i++) { array[i].cr = r; array[i].cg = g; array[i].cb = b; } } void FaceInfo::initBox(FaceInfo* array, Texture* tex) { // init all faces to this texture for (int i = 0; i < 6; i++) { array[i].t = tex; array[i].tl = tex->minX(); array[i].tr = tex->maxX(); array[i].tt = tex->maxY(); array[i].tb = tex->minY(); array[i].cr = 1.0; array[i].cg = 1.0; array[i].cb = 1.0; } } /** * Primitives class * contains methods for drawing primitive objects. */ // draw sphere void Primitives::drawSphere() { GLUquadricObj *quadobj; quadobj = gluNewQuadric(); gluQuadricNormals(quadobj, GL_SMOOTH); gluQuadricTexture(quadobj, GL_TRUE); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); gluSphere(quadobj, 1.0, 15, 10); gluDeleteQuadric(quadobj); } // draw a cylinder void Primitives::drawCylinder() { GLUquadricObj *quadobj; quadobj = gluNewQuadric(); gluQuadricNormals(quadobj, GL_SMOOTH); gluQuadricTexture(quadobj, GL_TRUE); gluCylinder(quadobj, 1.0, 1.0, 1.0, 15, 10); gluDeleteQuadric(quadobj); } // draw a disk void Primitives::drawDisk() { GLUquadricObj *quadobj; quadobj = gluNewQuadric(); gluQuadricNormals(quadobj, GL_SMOOTH); gluQuadricTexture(quadobj, GL_TRUE); gluDisk(quadobj, 0.0, 1.0, 15, 10); gluDeleteQuadric(quadobj); } // draw a unit square in the // x,y plane void Primitives::drawSquare() { glBegin(GL_QUADS); // surface normal glNormal3f(0.0, 0.0, 1.0); // BL glTexCoord2f(0.0, 0.0); glVertex3f(0.0, 0.0, 0.0); // TL glTexCoord2f(0.0, 1.0); glVertex3f(0.0, 1.0, 0.0); // TR glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 0.0); // BR glTexCoord2f(1.0, 0.0); glVertex3f(1.0, 0.0, 0.0); glEnd(); } // draws a box // vertices - an array of 8 vertices void Primitives::drawBox(Vec3 va[], Vec3 na[]) { // front and back glBegin(GL_QUADS); for (int t = 0; t < 4; t++) { int i = 4 + t; glNormal3f(na[i].x, na[i].y, na[i].z); glVertex3f(va[i].x, va[i].y, va[i].z); } for (int t = 3; t >= 0; t--) { int i = t; glNormal3f(na[i].x, na[i].y, na[i].z); glVertex3f(va[i].x, va[i].y, va[i].z); } glEnd(); // draw all the faces glFrontFace(GL_CCW); // sides glBegin(GL_QUAD_STRIP); for (int s = 0; s <= 4; s++) { int i = s % 4; // v 1 glNormal3f(na[i].x, na[i].y, na[i].z); glVertex3f(va[i].x, va[i].y, va[i].z); // v 2 i += 4; glNormal3f(na[i].x, na[i].y, na[i].z); glVertex3f(va[i].x, va[i].y, va[i].z); } glEnd(); } // draw box with textured faces // face - face info array going, with 6 faces // front, back, bottom, right, top, left void Primitives::drawBox(Vec3 va[], Vec3 na[], FaceInfo fa[]) { // front and back glBegin(GL_QUADS); fa[0].Begin(); for (int t = 0; t < 4; t++) { int i = 4 + t; fa[0].Vertex(t); glNormal3f(na[i].x, na[i].y, na[i].z); glVertex3f(va[i].x, va[i].y, va[i].z); } fa[0].End(); fa[1].Begin(); for (int t = 3; t >= 0; t--) { int i = t; fa[1].Vertex(t); glNormal3f(na[i].x, na[i].y, na[i].z); glVertex3f(va[i].x, va[i].y, va[i].z); } fa[1].End(); glEnd(); // draw all the faces glFrontFace(GL_CCW); // sides int f = 2; int v = 0; glBegin(GL_QUAD_STRIP); for (int s = 0; s <= 4; s++) { int i = s % 4; // v 1 fa[f].Begin(); fa[f].Vertex(v); v++; glNormal3f(na[i].x, na[i].y, na[i].z); glVertex3f(va[i].x, va[i].y, va[i].z); // v 2 i += 4; glNormal3f(na[i].x, na[i].y, na[i].z); glVertex3f(va[i].x, va[i].y, va[i].z); fa[f].Vertex(v); v = (v + 1) % 4; fa[f].End(); f++; if (f >= 6) f = 2; } glEnd(); } // initializes an array of vertices to // those for a 1x1 cube void Primitives::initBox(Vec3 va[]) { for (int z = 0; z <= 1; z++) { int s = z * 4; va[s].set(0.0, 0.0, (float)z); va[s+1].set(0.0, 1.0, (float)z); va[s+2].set(1.0, 1.0, (float)z); va[s+3].set(1.0, 0.0, (float)z); } } // calculate the normals for each box vertex void Primitives::boxNormals(Vec3 va[], Vec3 na[]) { // the vertices that are // adjacent to every vertex const int cnrs[] = { 4, 1, 3, 5, 2, 0, 1, 6, 3, 0, 2, 7, 7, 5, 0, 6, 1, 4, 2, 5, 7, 3, 6, 4 }; // for every vertex for (int i = 0; i < 8; i++) { // get adjacent vertices int a = cnrs[i*3]; int b = cnrs[i*3+1]; int c = cnrs[i*3+2]; // sum normal to each plane Vec3 sum; HyperPlane hp; // face 1 hp.set(&va[i], &va[a], &va[b]); sum.set(&hp.N); // face 2 hp.set(&va[i], &va[c], &va[a]); sum.add(&hp.N); // face 3 hp.set(&va[i], &va[b], &va[c]); sum.add(&hp.N); // average and normalize sum.divide(-3.0f); sum.norm(); na[i].set(&sum); } }