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:

vectors.cpp

home Home   up Up   ( Download )


#include "vectors.h" /** * Vec3 class * Holds a 3D vector. Is not functional, so the * result of an operation is placed in "this" object * so that we dont have allocation and destruction problems. */ // sets the value to the normal vector to the plane // defined by the points a, b, and c void Vec3::faceNormal(const Vec3* a, const Vec3* b, const Vec3* c) { // calculate two edge vectors Vec3 v0(b); Vec3 v1(c); v0.subtract(a); v1.subtract(a); // calculate dot product of the two set(&v1); cross(&v0); norm(); } /** * Mat4 class * Holds a 4 dimensional vector. */ // constructor // makes identity matrix Mat4::Mat4() { setI(); } // copies matrix Mat4::Mat4(const Mat4* v) { for (int i = 0; i < 16; i++) mat[i] = v->mat[i]; } // set a row of the matrix void Mat4::set(int row, float values[]) { int s = (row*4); for (int i=0; i < 4; i++) mat[s+i] = values[i]; } void Mat4::set(int row, Vec3* val) { int s = (row*4); mat[s] = val->x; mat[s+1] = val->y; mat[s+2] = val->z; } // make the identity matrix void Mat4::setI() { for (int i = 0; i < 16; i++) { if (i % 5 == 0) mat[i] = 1.0f; else mat[i] = 0.0f; } } // multiplies matrix by // scalar a // M = a * M void Mat4::multiply(float a) { for (int i = 0; i < 16; i++) mat[i] *= a; } // multiplies the matrix by // 4D row vector a // M = a * M void Mat4::multiply(float a[]) { // save this, so doesnt change Mat4 b(this); // for every cell in the result for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++) { // sum up the terms from a and b float v = 0.0; for (int i = 0; i < 4; i++) { v += a[i] * b.get(i, c); } // assign set(r, c, v); } } } // multiplies this matrix by matrix A // so that // M = A * M void Mat4::multiply(Mat4* A) { // save this, so doesnt change Mat4 b(this); // for every cell in the result for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++) { // sum up the terms from a and b float v = 0.0; for (int i = 0; i < 4; i++) { v += A->get(r, i) * b.get(i, c); } // assign set(r, c, v); } } } // transform this vector, using this // matrix void Mat4::transform(Vec3* v) { // save a copy as homogeneous float a[] = {v->x, v->y, v->z, 1}; // for each cell in the new vector for (int r = 0; r < 3; r++) { // sum float s = 0.0; for (int i = 0; i < 4; i++) { s += get(r, i) * a[i]; } v->set(r, s); } } // transform all of the vertices in this // array void Mat4::transform(Vec3 v[], int n) { for (int i = 0; i < n; i++) transform(&v[i]); } // transform this hyperplane by the // transformation matrix void Mat4::transformPlane(Vec3* A, Vec3* N) { // put N into a form, // where it is a coordinate to be // transformed. Vec3 p(A); p.add(N); // tranform the points transform(&p); transform(A); // make N relative again p.subtract(A); } // scale void Mat4::scale(float x, float y, float z) { // create transformation matrix Mat4 m; m.mat[0] = x; m.mat[5] = y; m.mat[10] = z; // multiply this matrix by that multiply(&m); } // scale matrix void Mat4::scale(const Vec3* v) { // create transormation matrix Mat4 m; m.mat[0] = v->x; m.mat[5] = v->y; m.mat[10] = v->z; // multiply this matrix by that multiply(&m); } // translate void Mat4::translate(float x, float y, float z) { // create translation matrix Mat4 m; m.mat[3] = x; m.mat[7] = y; m.mat[11] = z; // multiply this matrix by that multiply(&m); } // rotate matrix void Mat4::rotate(double angle, double x, double y, double z) { // change angle to radians angle *= PI / 180.0; // normalize vector if need be double l = sqrt(x*x + y*y + z*z); if (l != 1.0) { x /= l; y /= l; z /= l; } // create rotation matrix Mat4 m; double c = cos(angle), s = sin(angle); // row 0 m.mat[0] = (x*x)*(1-c)+c; m.mat[1] = (x*y)*(1-c)-(z*s); m.mat[2] = (x*z)*(1-c)+(y*s); m.mat[3] = 0; // row 1 m.mat[4] = (y*x)*(1-c)+(z*s); m.mat[5] = (y*y)*(1-c)+c; m.mat[6] = (y*z)*(1-c)-(x*s); m.mat[7] = 0; // row 2 m.mat[8] = (x*z)*(1-c)-(y*s); m.mat[9] = (y*z)*(1-c)+(x*s); m.mat[10] = (z*z)*(1-c)+c; m.mat[11] = 0; // row 3 m.mat[12] = 0; m.mat[13] = 0; m.mat[14] = 0; m.mat[15] = 1; // multiply this by that multiply(&m); } // display void Mat4::print() { for (int i = 0; i < 16; i++) { printf("%f ", mat[i]); if (i % 4 == 3) printf("\n"); } } /* * HyperPlane class * Represents a 3D plane, against which * lines can be clipped. */ // 3 points on the plane HyperPlane::HyperPlane(const Vec3* p1, const Vec3* p2, const Vec3* p3) { set(p1, p2, p3); } // set by copying another void HyperPlane::set(HyperPlane* hp) { N.set(&hp->N); A.set(&hp->A); } // set using these points void HyperPlane::set(const Vec3* p1, const Vec3* p2, const Vec3* p3) { N.faceNormal(p1, p2, p3); A.set(p1); } // clipping // which side is the point on? // 0 - lies on plane // < 0 - outside (away from normal) // > 0 - inside (side of normal direction) GLfloat HyperPlane::side(const Vec3* p) { Vec3 V(p); V.subtract(&A); return V.dot(&N); } // same side bool HyperPlane::sameSide(const Vec3* p1, const Vec3* p2) { GLfloat a = side(p1), b = side(p2); return (a > 0 && b > 0) || (a < 0 && b < 0) || (a == 0 && b == 0); }