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:

regulargrid.cpp

home Home   up Up   ( Download )


#include "regulargrid.h" #include "vectors.h" #include "clipping.h" /* * RegularGrid class * ---------------------- * Used to hold regular polygon meshes for holding * landscapes etc... * ------------------------- * Tristan Aubrey-Jones 18/12/2007 */ // constructor // xsz - width of the grid in elements // zsz - depth of the grid in elemtents RegularGrid::RegularGrid(int xsz, int zsz, Vec3 sz, Texture* tex): w(xsz), d(zsz), sz(sz), tex(tex) { // allocate grid grid = new RegularGridVertex[w*d]; // clear contents to default clear(); } // destructor RegularGrid::~RegularGrid() { delete [] grid; grid = NULL; } // sets all of the vertices in the grid to the same value // v - the vertex value void RegularGrid::setAll(RegularGridVertex v) { for (int x = 0; x < w; x++) { for (int z = 0; z < d; z++) { setV(x,z,v); } } } // computes the normal value for each vertex // by averaging the normal vector to each adjacent face void RegularGrid::computeNormals() { // width and depth float i = (w-1)/sz.x; float k = sz.y; float j = (d-1)/sz.z; // for every vertex in the mesh for (int x = 0; x < w; x++) { for (int z = 0; z < d; z++) { // get this vertex Vec3 v((float)x/i, get(x,z).y, (float)z/j); // get the 4 adjacent ones Vec3 v1((float)x/i, get(x,z-1).y*k, (float)(z-1)/j); Vec3 v2((float)x/i, get(x,z+1).y*k, (float)(z+1)/j); Vec3 v3((float)(x-1)/i, get(x-1,z).y*k, (float)z/j); Vec3 v4((float)(x+1)/i, get(x+1,z).y*k, (float)z/j); // compute normals for each adjacent triangle // and sum ready to average Vec3 ns; ns.faceNormal(&v, &v1, &v3); // BL ns.up(); Vec3 n; n.faceNormal(&v, &v1, &v4); // BR n.up(); ns.add(&n); n.faceNormal(&v, &v2, &v3); // TL n.up(); ns.add(&n); n.faceNormal(&v, &v2, &v4); // TR n.up(); ns.add(&n); // average ns.divide(4.0); n.set(&ns); n.norm(); // assign RegularGridVertex h = getV(x,z); h.normal.set(&n); setV(x,z,h); } } } // draw // draws the grid in the unit cubed int RegularGrid::draw(CameraView* view) { // triangle count int tCount = 0; // width and depth float i = (w-1)/sz.x; float k = sz.y; float j = (d-1)/sz.z; // if texture bool t = (tex != NULL); if (t) tex->Begin(); // winding for face culling glFrontFace(GL_CW); // enumerate along x doing triangle strips for z for (int x0 = 0; x0 < w-1; x0++) { int x1 = x0+1; // if there is a view defined only draw the parts // that are in view. if (false && view != NULL) { int z0 = 0; for (int z1 = 1; z1 < d; z1++) { // get points RegularGridVertex va[4]; va[0] = getV(x0,z0); va[1] = getV(x1,z0); va[2] = getV(x0,z1); va[3] = getV(x1,z1); // calculate real world vertices Vec3 pa[4]; pa[0].set((float)x0/i, va[0].y*k, (float)z0/j); pa[1].set((float)x1/i, va[1].y*k, (float)z0/j); pa[2].set((float)x0/i, va[2].y*k, (float)z1/j); pa[3].set((float)x1/i, va[3].y*k, (float)z1/j); // if in view bool inView = false; for (int i = 0; i < 4; i++) if (view->inView(&pa[i])) inView = true; if (inView) { // draw triangle strip glBegin(GL_TRIANGLE_STRIP); for (int i = 0; i < 4; i++) { // texture if (t) { switch(i) { case 0: if (z1 % 2 == 0) glTexCoord2f(tex->minX(), tex->maxY()); else glTexCoord2f(tex->minX(), tex->minY()); break; case 1: if (z1 % 2 == 1) glTexCoord2f(tex->maxX(), tex->minY()); else glTexCoord2f(tex->maxX(), tex->maxY()); break; case 2: if (z1 % 2 == 1) glTexCoord2f(tex->minX(), tex->maxY()); else glTexCoord2f(tex->minX(), tex->minY()); break; case 3: if (z1 % 2 == 0) glTexCoord2f(tex->maxX(), tex->minY()); else glTexCoord2f(tex->maxX(), tex->maxY()); break; } } // vertex glNormal3f(va[i].normal.x, va[i].normal.y, va[i].normal.z); glVertex3f(pa[i].x, pa[i].y, pa[i].z); } glEnd(); tCount += 2; } // next z0 = z1; } } // otherwise draw whole thing a strip at a time else { glBegin(GL_TRIANGLE_STRIP); // starting vertex RegularGridVertex v = getV(x0,0); Vec3 p((float)x0/i, v.y*k, 0.0 ); glNormal3f(v.normal.x, v.normal.y, v.normal.z); if (t) glTexCoord2f(tex->minX(), tex->minY()); glVertex3f(p.x, p.y, p.z); // triangle strip int z0 = 0; for (int z1 = 1; z1 < d; z1++) { // right vertex if (t) { if (z1 % 2 == 1) glTexCoord2f(tex->maxX(), tex->minY()); else glTexCoord2f(tex->maxX(), tex->maxY()); } v = getV(x1,z0); p.set((float)x1/i, v.y*k, (float)z0/j); glNormal3f(v.normal.x, v.normal.y, v.normal.z); glVertex3f(p.x, p.y, p.z); // diag vertex if (t) { if (z1 % 2 == 1) glTexCoord2f(tex->minX(), tex->maxY()); else glTexCoord2f(tex->minX(), tex->minY()); } v = getV(x0,z1); p.set((float)x0/i, v.y*k, (float)z1/j); glNormal3f(v.normal.x, v.normal.y, v.normal.z); glVertex3f(p.x, p.y, p.z); // 2 triangles drawn tCount += 2; // next z0 = z1; } // last vertex if (t) glTexCoord2f(tex->maxX(), tex->minY()); v = getV(x1,z0); p.set((float)x1/i, v.y*k, (float)z0/j); glNormal3f(v.normal.x, v.normal.y, v.normal.z); glVertex3f(p.x, p.y, p.z); glEnd(); } } // if texture if (t) tex->End(); // triangle count return tCount; }