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 "clipping.h"
// constructor that copies
FrustrumClipper::FrustrumClipper(FrustrumClipper* f):
nr(f->nr), fr(f->fr),
left(f->left), right(f->right),
top(f->top), bottom(f->bottom)
{
}
// set to this value
void FrustrumClipper::set(FrustrumClipper* f)
{
nr.set(&(f->nr));
fr.set(&(f->fr));
left.set(&(f->left));
right.set(&(f->right));
top.set(&(f->top));
bottom.set(&(f->bottom));
}
// transform this using a transformation matrix
void FrustrumClipper::transform(Mat4* t)
{
nr.transform(t);
fr.transform(t);
left.transform(t);
right.transform(t);
top.transform(t);
bottom.transform(t);
}
// inside the frustrum?
bool FrustrumClipper::inside(const Vec3* p)
{
return nr.side(p) > 0 && // beyond near
fr.side(p) > 0 && // before far
left.side(p) > 0 && // to the right of left
right.side(p) > 0 && // to the left of right
bottom.side(p) > 0 && // above bottom
top.side(p) > 0; // below the top
}
// sets the frustrum's clipping planes
// to the frustrum defined by these
// parameters
void FrustrumClipper::set(float viewingAngle,
float aspectRatio, float nearD, float farD,
const Vec3* pos, const Vec3* obj, const Vec3* above)
{
// calculate side
// as the normal to the up, forward plane
HyperPlane ufp(pos, obj, above);
Vec3 side(&ufp.N);
side.norm();
// up vector
Vec3 up(above);
up.subtract(pos);
up.norm();
// direction vector
Vec3 dir(obj);
dir.subtract(pos);
dir.norm();
// start point
Vec3 cstart(&dir);
cstart.multiply(nearD);
cstart.add(pos);
// end point
Vec3 cend(&dir);
cend.multiply(farD);
cend.add(pos);
// near (front) clipping plane
nr.A.set(&cstart);
nr.N.set(&dir);
// far (back) clipping plane
fr.A.set(&cend);
fr.N.set(&dir);
fr.N.multiply(-1);
// calculate vert distance from far center
// to vertical points
float ha = (viewingAngle / 2) * (PI / 180.0);
float fvh = farD * tan(ha);
Vec3 vt(up);
vt.multiply(fvh);
// calcuate horizontal distance from center
// based on aspect ratio
float fhw = fvh * aspectRatio;
Vec3 hz(&side);
hz.multiply(fhw);
// init points at other end to far center
Vec3 ftr(&cend); ftr.add(&vt); ftr.add(&hz);
Vec3 ftl(&cend); ftl.add(&vt); ftl.subtract(&hz);
vt.multiply(2.0); // increase at bottom to solve weird
hz.multiply(1.5); // lack of clipping at bottom
Vec3 fbr(&cend); fbr.subtract(&vt); fbr.add(&hz);
Vec3 fbl(&cend); fbl.subtract(&vt); fbl.subtract(&hz);
// make side planes
left.set(pos, &fbl, &ftl);
right.set(pos, &ftr, &fbr);
// make top and bottom planes
top.set(pos, &ftl, &ftr);
bottom.set(pos, &fbr, &fbl);
}
// constructor
CameraView::CameraView(GLfloat viewingAngle,
GLfloat aspectRatio,
GLfloat nearD,
GLfloat farD):
viewingAngle(viewingAngle),
aspectRatio(aspectRatio),
nearD(nearD),
farD(farD)
{
}
// set location and orientation
void CameraView::set(const Vec3* ppos,
const Vec3* pobj,
const Vec3* pabove)
{
// set params
pos.set(ppos);
obj.set(pobj);
above.set(pabove);
// calculate side
// as the normal to the up, forward plane
HyperPlane ufp(&pos, &obj, &above);
side.set(&ufp.N);
side.norm();
// initialize clipping frustrum
// to slightly bigger than the actual
// viewing perspective
frust.set(viewingAngle, aspectRatio,
nearD, farD, ppos, pobj, pabove);
}
// set the cammera orientation to the
// camera's (should happen in MODEL_VIEW)
void CameraView::lookAt()
{
// calculate up from above
Vec3 up(&above);
up.subtract(&pos);
// move camera
gluLookAt(pos.x, pos.y, pos.z,
obj.x, obj.y, obj.z,
up.x, up.y, up.z);
}
// rotate horizontally
void CameraView::rotateHoriz(float angle)
{
// calculate up vector from above and pos
Vec3 up(&above);
up.subtract(&pos);
// transformation matrix to rotate by
// angle around the point pos in the
// side/forward plane
Mat4 m;
m.translate(-pos.x, -pos.y, -pos.z);
m.rotate(-angle, 0, 1, 0);
m.translate(pos.x, pos.y, pos.z);
// move object looked at
m.transform(&obj);
// pos stays the same
// upward direction moves
m.transform(&above);
// transform frustrum
//frust.transform(&m);
frust.set(viewingAngle, aspectRatio,
nearD, farD, &pos, &obj, &above);
// calculate side
// as the normal to the up, forward plane
HyperPlane ufp(&pos, &obj, &above);
side.set(&ufp.N);
}
// rotate vertically
void CameraView::rotateVert(float angle)
{
// transformation matrix to rotate
// by angle, around the point pos
// in the up/forward plane
Mat4 m;
m.translate(-pos.x, -pos.y, -pos.z);
m.rotate(-angle, &side);
m.translate(pos.x, pos.y, pos.z);
// move object looked at
m.transform(&obj);
// move upward direction
m.transform(&above);
// pos stays same
// side stays the same
// transform frustrum
//frust.transform(&m);
frust.set(viewingAngle, aspectRatio,
nearD, farD, &pos, &obj, &above);
}
// move forward
void CameraView::moveForward(float d)
{
// transformation matrix to translate
// by distance d in the direction of
// obj-pos
Mat4 m;
Vec3 dir(&obj);
dir.subtract(&pos);
dir.norm();
dir.multiply(d);
m.translate(&dir);
// move pos
m.transform(&pos);
// move obj
m.transform(&obj);
// move above
m.transform(&above);
// side stays the same
// transform frustrum
//frust.transform(&m);
frust.set(viewingAngle, aspectRatio,
nearD, farD, &pos, &obj, &above);
}
// move up
void CameraView::moveUp(float dist)
{
// transformation matrix to translate
// by distance d in the direction of
// obj-pos
Mat4 m;
Vec3 dir(0.0, dist, 0.0);
m.translate(&dir);
// move pos
m.transform(&pos);
// move obj
m.transform(&obj);
// move above
m.transform(&above);
// side stays the same
// transform frustrum
frust.set(viewingAngle, aspectRatio,
nearD, farD, &pos, &obj, &above);
}
// get position
void CameraView::getPosition(Vec3* r)
{
r->set(&pos);
}
// get forward vector
void CameraView::getForwardVector(Vec3* r)
{
r->set(&obj);
r->subtract(&pos);
}