3D Shapes (using OpenGL and SDL)

Copyright Tristan Aubrey-Jones December 2007.

main.cpp

home Home   up Up   ( Download )


/* 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; } }