Copyright Tristan Aubrey-Jones December 2007.
/* COMP3004: COURSEWORK 1
* Tristan Aubrey-Jones (taj105)
* -------------------------------
*/
#include
#include
#include
#include
#include
#include
#include
// the value of PI
const GLfloat PI = 3.14159265;
// size of display area
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
// global view mode in {a,b,c,d,e}
// changed by event loop, and tested by
// renderer
static char viewmode = 'a';
// method declarations
void initgl(int, char**);
void run();
bool process_event(SDL_Event*);
void draw_scene(void);
void draw_scene_a(void);
void draw_scene_b(void);
void draw_scene_c(void);
void draw_scene_d(void);
void draw_scene_e(void);
void sphere_wiremesh();
void sphere_spikes();
void sphere_solid_lit();
void cone_wiremesh();
void cone_solid_lit();
class Sphere {
public:
void draw(void);
};
void Sphere::draw(void) {
printf("drawing a sphere.");
}
// CONTROL
// program entry point
int main(int argc, char *argv[])
{
// setup graphics library
initgl(argc, argv);
// set window manager title bar
SDL_WM_SetCaption( "COMP3004 Coursework 1", "cwk1" );
// sets up a parallel projection on the SDL window
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// run
run();
// end
return 0;
}
// set up opengl and sdl
// --------------------------------------------------------------
// TAKEN FROM COMP3004 COLOURED CUBE NOTES EXAMPLE
// https://secure.ecs.soton.ac.uk/notes/comp3004/lectures/code.html
// VERBATIM BEGINS HERE
void initgl(int argc, char* argv[])
{
int rgb_size[3];
int bpp;
int i;
Uint32 video_flags = SDL_OPENGL;
if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
fprintf(stderr,"Couldn't initialize SDL: %s\n",SDL_GetError());
exit( 1 );
}
/* See if we should detect the display depth */
if ( bpp == 0 ) {
if ( SDL_GetVideoInfo()->vfmt->BitsPerPixel <= 8 ) {
bpp = 8;
} else {
bpp = 16; /* More doesn't seem to work */
}
}
for ( i=1; argv[i]; ++i ) {
if ( strcmp(argv[i], "-fullscreen") == 0 ) {
video_flags |= SDL_FULLSCREEN;
}
}
bpp = 32;
/* Initialize the display */
switch (bpp) {
case 8:
rgb_size[0] = 3;
rgb_size[1] = 3;
rgb_size[2] = 2;
break;
case 15:
case 16:
rgb_size[0] = 5;
rgb_size[1] = 5;
rgb_size[2] = 5;
break;
default:
rgb_size[0] = 8;
rgb_size[1] = 8;
rgb_size[2] = 8;
break;
}
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, rgb_size[0] ); // Sets bits per channel
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, rgb_size[1] );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, rgb_size[2] );
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); // 3 channels per pixel (R, G and B)
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 ); // ?
SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 1 );
SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 );
SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 );
if ( SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, bpp, video_flags) == NULL ) {
fprintf(stderr, "Couldn't set GL mode: %s\n", SDL_GetError());
SDL_Quit();
exit(1);
}
}
// --------------------------------------------------------------------
// VERBATIM ENDS HERE
// gets the time in seconds (since SDL init)
inline GLfloat time_in_s() {
return ((GLfloat)SDL_GetTicks()) / 1000.0;
}
// main program body
void run()
{
// setup initial conditions
// set GL_PROJECTION matrix
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
// left, right, bottom, top, nearVal, farVal
glOrtho( -2.0, 2.0, -2.0, 2.0, -20.0, 20.0 );
// set GL_MODELVIEW matrix
glMatrixMode( GL_MODELVIEW );
glLoadIdentity( );
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glShadeModel(GL_SMOOTH);
// get start time
GLfloat T0 = time_in_s();
// main animation loop
bool running = true;
while (running)
{
// clear surface, ready to draw again
glClearColor( 0.0, 0.0, 0.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// change model view to animate
glMatrixMode( GL_MODELVIEW );
GLfloat T = time_in_s();
GLfloat Td = T - T0; T0 = T;
glRotatef(Td * 40.0, 0.2, 1.0, 0.0);
// draw the scene
draw_scene();
glFlush();
SDL_GL_SwapBuffers( );
// check for errors
GLenum glError = glGetError();
if(glError != GL_NO_ERROR)
fprintf( stderr, "comp3004/cwk1/opengl/error: %d\n", glError);
char* sdlError = SDL_GetError();
if (sdlError != NULL && (*sdlError) != 0)
fprintf(stderr, "comp3004/cwk1/sdl/error: %s\n", sdlError);
// handle events
SDL_Event event;
while (SDL_PollEvent(&event))
running = process_event(&event);
}
// cleanup
SDL_Quit();
}
// EVENT HANDLING
// handle an SDL event
// returns true if program is to keep running, or false to quit
bool process_event(SDL_Event *event)
{
// key event
if (event->type == SDL_KEYDOWN)
{
// if ESC then quit
if (event->key.keysym.sym == SDLK_ESCAPE) return false;
// switch on key
switch(event->key.keysym.sym){
// Q => Quit
case(SDLK_q): return false;
// DISPLAY MODES
case(SDLK_a): viewmode = 'a'; break;
case(SDLK_b): viewmode = 'b'; break;
case(SDLK_c): viewmode = 'c'; break;
case(SDLK_d): viewmode = 'd'; break;
case(SDLK_e): viewmode = 'e'; break;
}
}
// if is a quit message
else if (event->type == SDL_QUIT)
return false;
// otherwise keep runing
return true;
}
// DRAWING
// calculates 3D cross product
inline GLfloat crossproduct_x(GLfloat ay, GLfloat az, GLfloat by, GLfloat bz)
{
return (ay * bz) - (az * by);
}
inline GLfloat crossproduct_y(GLfloat ax, GLfloat az, GLfloat bx, GLfloat bz)
{
return (az * bx) - (ax * bz);
}
inline GLfloat crossproduct_z(GLfloat ax, GLfloat ay, GLfloat bx, GLfloat by)
{
return (ax * by) - (ay * bx);
}
// calculate the dot product of two vectors
inline GLfloat dotproduct(GLfloat xa, GLfloat ya, GLfloat za,
GLfloat xb, GLfloat yb, GLfloat zb)
{
return (xa * xb) + (ya * yb) + (za * zb);
}
// length of vector
inline GLfloat length(GLfloat x, GLfloat y, GLfloat z)
{
return sqrt(x*x + y*y + z*z);
}
// calculates the light intensity at a given point, on a surface
// xl,yl,zl = direction to light source
// xs,ys,zs = surface normal
// r = value of full intensity
inline GLfloat incident_light(GLfloat r, GLfloat xl, GLfloat yl, GLfloat zl,
GLfloat xs, GLfloat ys, GLfloat zs)
{
// calculate light intensity
return r * dotproduct(xl,yl,zl, xs,ys,zs) /
(length(xl,yl,zl) * length(xs,ys,zs));
}
// the position of the global light source
const GLfloat LIGHT_X = 0.0, LIGHT_Y = 0.0, LIGHT_Z = -2000.0;
// calculates the light intensity at a given point
inline GLfloat light_intensity(GLfloat r, GLfloat x, GLfloat y, GLfloat z,
GLfloat nx, GLfloat ny, GLfloat nz)
{
// naturally range is -1.0,1.0 so must make positive and half it
// to get 0.0,1.0
GLfloat v = 1.0 - ((incident_light(r, LIGHT_X-x, LIGHT_Y-y, LIGHT_Z-z, nx, ny, nz) + r) / 2.0);
fprintf(stderr, "%f\n", v);
return v;
}
// init sphere constants
const GLfloat SPHERE_LONFROM = 0.0;
const GLfloat SPHERE_LONTO = (2 * PI) + 0.1;
const GLfloat SPHERE_LONSTEP = PI / 10;
const GLfloat SPHERE_LATFROM = 0.0;
const GLfloat SPHERE_LATTO = PI + 0.1;
const GLfloat SPHERE_LATSTEP = PI / 10;
// draws a wiremesh sphere
void sphere_wiremesh()
{
// constants
const GLfloat r = 1.0;
const GLfloat x0 = 0.0;
const GLfloat y0 = 0.0;
const GLfloat z0 = 0.0;
// iterate through all latitudes, drawing sphere from bottom
// to top
for (GLfloat lat = SPHERE_LATFROM; lat < SPHERE_LATTO; lat += SPHERE_LATSTEP) {
// start wire line
glBegin(GL_LINE_LOOP);
// set colour to white
glColor3f(1.0, 1.0, 1.0);
// draw all vertices in longitudal ring
for (GLfloat lon = SPHERE_LONFROM; lon < SPHERE_LONTO; lon += SPHERE_LONSTEP) {
// calculate cartesian position of vertex
GLfloat x = x0 + r * cos(lon) * sin(lat);
GLfloat y = y0 + r * sin(lon) * sin(lat);
GLfloat z = z0 + r * cos(lat);
// draw vertex
glVertex3f(x, y, z);
}
// end wire line
glEnd();
}
// draw all vertices in longitudal ring
for (GLfloat lon = SPHERE_LONFROM; lon < SPHERE_LONTO; lon += SPHERE_LONSTEP) {
// start wire line
glBegin(GL_LINE_STRIP);
// iterate through all latitudes, drawing sphere from bottom
// to top
for (GLfloat lat = SPHERE_LATFROM; lat < SPHERE_LATTO; lat += SPHERE_LATSTEP) {
// calculate cartesian position of vertex
GLfloat x = x0 + r * cos(lon) * sin(lat);
GLfloat y = y0 + r * sin(lon) * sin(lat);
GLfloat z = z0 + r * cos(lat);
// draw vertex
glVertex3f(x, y, z);
}
// end wire line
glEnd();
}
}
// draw short lines at normal to each vertex on sphere
// so that it looks like a hedgehog
void sphere_spikes()
{
// size
const GLfloat r = 1.0;
const GLfloat x0 = 0.0;
const GLfloat y0 = 0.0;
const GLfloat z0 = 0.0;
// iterate through all latitudes, drawing sphere from bottom
// to top
glBegin(GL_LINES);
for (GLfloat lat = SPHERE_LATFROM; lat < SPHERE_LATTO; lat += SPHERE_LATSTEP) {
// draw all vertices in longitudal ring
for (GLfloat lon = SPHERE_LONFROM; lon < SPHERE_LONTO; lon += SPHERE_LONSTEP) {
// calculate cartesian position of vertex
GLfloat x = x0 + r * cos(lon) * sin(lat);
GLfloat y = y0 + r * sin(lon) * sin(lat);
GLfloat z = z0 + r * cos(lat);
// calculate normal vector
GLfloat x_ = (x - x0) * 0.2;
GLfloat y_ = (y - y0) * 0.2;
GLfloat z_ = (z - z0) * 0.2;
// draw line
glVertex3f(x, y, z);
glVertex3f(x + x_, y + y_, z + z_);
}
}
glEnd();
}
// draws a solid sphere of quadrelaterals, with
// lighting calculations
void sphere_solid_lit()
{
// set constants, to move use model view transpose
const GLfloat x0 = 0.0, y0 = 0.0, z0 = 0.0, r = 1.0;
// draw all sections around a longitudal ring
for (GLfloat lat = SPHERE_LATFROM; lat < SPHERE_LATTO; lat += SPHERE_LATSTEP) {
// start wire line
glBegin(GL_QUAD_STRIP);
// iterate through all latitudes, drawing sphere from bottom
// to top
for (GLfloat lon = SPHERE_LONFROM; lon <= SPHERE_LONTO; lon += SPHERE_LONSTEP) {
// calculate cartesian position of vertex
GLfloat x = x0 + r * cos(lon) * sin(lat);
GLfloat y = y0 + r * sin(lon) * sin(lat);
GLfloat z = z0 + r * cos(lat);
// calculate normal vector
GLfloat x_ = (x - x0) * 0.2;
GLfloat y_ = (y - y0) * 0.2;
GLfloat z_ = (z - z0) * 0.2;
// set colour, based on light intensity
GLfloat i = light_intensity(r, x, y, z, x_, y_, z_);
glColor3f(x*i,y*i,z*i);
// draw vertex
glVertex3f(x, y, z);
// calculate cartesian position of vertex
x = x0 + r * cos(lon) * sin(lat + SPHERE_LATSTEP);
y = y0 + r * sin(lon) * sin(lat+ SPHERE_LATSTEP);
z = z0 + r * cos(lat+ SPHERE_LATSTEP);
// calculate normal vector
x_ = (x - x0) * 0.2;
y_ = (y - y0) * 0.2;
z_ = (z - z0) * 0.2;
// set colour, based on light intensity
i = light_intensity(r, x, y, z, x_, y_, z_);
glColor3f(x*i,y*i,z*i);
// draw vertex
glVertex3f(x, y, z);
}
// end wire line
glEnd();
}
}
// cone constants
const GLfloat CONE_ANGFROM = 0.0;
const GLfloat CONE_ANGTO = (2 * PI) + 0.1;
const GLfloat CONE_ANGSTEP = PI / 10;
// draws a cone, with the base' center point at x0,y0,z0, with a
// base radius of r, and a height of h
void cone_wiremesh()
{
// constants
const GLfloat x0 = 0.0, y0 = 0.0, z0 = 0.0, r = 0.5, h = 1.0;
// draw base
glBegin(GL_TRIANGLE_FAN);
glColor3f(1.0,1.0,1.0);
glVertex3f(x0, y0, z0); // center of base
for (GLfloat ang = CONE_ANGFROM; ang <= CONE_ANGTO; ang += CONE_ANGSTEP)
{
// calculate point on circumference of base
GLfloat x = x0 + r * cos(ang);
GLfloat z = z0 + r * sin(ang);
GLfloat y = y0;
// draw point
glVertex3f(x, y, z);
}
glEnd();
// draw rest
glBegin(GL_LINES);
for (GLfloat ang = CONE_ANGFROM; ang <= CONE_ANGTO; ang += CONE_ANGSTEP)
{
// calculate point on circumference of base
GLfloat x = x0 + r * cos(ang);
GLfloat z = z0 + r * sin(ang);
GLfloat y = y0;
// draw from base, to top
glVertex3f(x, y, z);
glVertex3f(x0, y0+h, z0);
}
glEnd();
}
// draws a solid cone, which is lit using the lighting calculations
void cone_solid_lit()
{
// constants
const GLfloat x0 = 0.0, y0 = 0.0, z0 = 0.0, r = 0.5, h = 1.0;
// draw base
glBegin(GL_TRIANGLE_FAN);
GLfloat i = light_intensity(r, x0, y0, z0, 0.0, -1.0, 0.0);
glColor3f(i,i,i);
glVertex3f(x0, y0, z0); // center of base
for (GLfloat ang = CONE_ANGFROM; ang <= CONE_ANGTO; ang += CONE_ANGSTEP)
{
// calculate point on circumference of base
GLfloat x = x0 + r * cos(ang);
GLfloat z = z0 + r * sin(ang);
GLfloat y = y0;
// set colour, based on light intensity
GLfloat i = light_intensity(r, x, y, z, 0.0, -1.0, 0.0);
glColor3f(i,i,i);
// draw point
glVertex3f(x, y, z);
}
glEnd();
// draw faces
// (cant do as triangle fan, as need different colours for the
// top point, to make the faces different colours, otherwise top
// of cone is always grey)
glBegin(GL_TRIANGLE_STRIP);
GLfloat yt = y0 + h;
GLfloat a = CONE_ANGFROM;
for (GLfloat b = CONE_ANGFROM + CONE_ANGSTEP;
b <= CONE_ANGTO + CONE_ANGSTEP; b += CONE_ANGSTEP)
{
// calculate points on circumference of base
GLfloat xa = x0 + r * cos(a);
GLfloat za = z0 + r * sin(a);
GLfloat ya = y0;
GLfloat xb = x0 + r * cos(b);
GLfloat zb = z0 + r * sin(b);
GLfloat yb = y0;
// calculate normal to face
GLfloat x_ = -crossproduct_x(ya - yt, za - z0, yb - yt, zb - z0);
GLfloat y_ = -crossproduct_y(xa - x0, za - z0, xb - x0, zb - z0);
GLfloat z_ = -crossproduct_z(xa - x0, ya - yt, xb - x0, yb - yt);
// draw triangle
// top point
GLfloat i = light_intensity(r*2, (xa+xb)/2.0, (y0+yt)/2.0, (za+zb)/2.0, x_, y_, z_);
glColor3f(i,i,i);
glVertex3f(x0, yt, z0);
// bottom point
i = light_intensity(r*2, xb, y0, zb, x_, y_, z_);
glColor3f(i,i,i);
glVertex3f(xb, yb, zb);
// make this b, the last a
a = b;
}
glEnd();
}
// draws a simple wireframe sphere
void draw_scene_a(void)
{
// draw sphere
sphere_wiremesh();
}
// draws a simple wire frame cone
void draw_scene_b(void)
{
cone_wiremesh();
}
// draws a "hedgehog" sphere
void draw_scene_c(void)
{
// set up position and radius
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// draw wiremesh, and spikes
sphere_wiremesh();
sphere_spikes();
glPopMatrix();
}
// draw a sphere with lighting calculations
void draw_scene_d(void)
{
sphere_solid_lit();
}
// animate spheres, and cones on a regular path
void draw_scene_e(void)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// position and draw cone 1
glTranslatef(0.5, 0.0, 0.5);
cone_solid_lit();
glPopMatrix(); glPushMatrix();
// position and raw cone 1
glTranslatef(1.5, 0.0, 1.0);
glScalef(0.5, 0.5, 0.5);
cone_solid_lit();
glPopMatrix(); glPushMatrix();
// position and draw sphere
glTranslatef(-0.5, -0.0, -0.5);
glScalef(0.5, 0.5, 0.5);
sphere_solid_lit();
// position and draw second sphere
glTranslatef(-1.5, -0.0, -0.5);
glScalef(0.2, 0.2, 0.2);
sphere_solid_lit();
glPopMatrix();
}
// draws the scene
void draw_scene(void)
{
switch(viewmode) {
// part A
case 'a': draw_scene_a(); break;
// part B
case 'b': draw_scene_b(); break;
// part C
case 'c': draw_scene_c(); break;
// part D
case 'd': draw_scene_d(); break;
// part E
case 'e': draw_scene_e(); break;
}
}