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