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