/* This program displays a sphere with a picture texture-mapped onto it. Scott D. Anderson Scott.Anderson@acm.org Fall 2000 original Fall 2003 adapted for TW */ #include #include #include #include #include #include bool wire = true; // wire-frame mode (vs filled) bool texture = false; // texture mapping (on or off) void set_view() { GLfloat mat_specular[]={1.0, 1.0, 1.0, 1.0}; GLfloat mat_diffuse[]={0.5, 1.0, 1.0, 1.0}; GLfloat mat_ambient[]={0.5, 1.0, 1.0, 1.0}; GLfloat mat_shininess={100.0}; GLfloat light_ambient[]={0.0, 0.0, 0.0, 1.0}; GLfloat light_diffuse[]={1.0, 1.0, 1.0, 1.0}; GLfloat light_specular[]={1.0, 1.0, 1.0, 1.0}; GLfloat light_position[]={10.0, 10.0, 10.0, 0.0}; glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialf(GL_FRONT, GL_SHININESS, mat_shininess); glEnable(GL_SMOOTH); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); /* automatic normalization of normals */ } void sphereInternal(GLfloat radius, GLfloat stacks, GLfloat slices, bool wire) { /* i is the longitude counter, j is the latitude counter. i goes from 0 to slices while longitude goes from 0 to 2PI. j goes from 0 to stacks while latitude goes from -PI to +PI, where 0,0 is on the positive x axis and negative latitude is the southern hemisphere (negative y). The outer loop is the latitude, the inner the longitude, so the inner is building a circle in an x,z plane. The algorithm builds strips of constant y (latitude) and the following conversion to Cartesian coordinates: x = cos(latitude)*cos(longitude) y = sin(latitude) z = cos(latitude)*sin(longitude) Because we are building strips, we need pairs of points, one for the hi latitude and the other for the lo latitude. In order to do wire frame, we need to remember the previous pair of points, so we can draw lines. For lighting, we also compute normals along the way, so we compute normals first, then multiply by the radius. */ int i, j; GLfloat lat_lo, lat_hi; // high and low latitude GLfloat cos_lat_lo, cos_lat_hi; // the cosine of those angles GLfloat nx, ny_lo, ny_hi, nz; // the normal vector is (nx, ny, nz) GLfloat longitude, cos_longitude, sin_longitude; GLfloat x_lo,y_lo,z_lo,x_hi,y_hi,z_hi; // coordinates of the two points GLfloat oldx_lo,oldy_lo,oldz_lo,oldx_hi,oldy_hi,oldz_hi; int prev=0; lat_lo = -M_PI/2; /* latitude of south pole */ cos_lat_lo = 0; /* radius at that latitude */ ny_lo = -1; /* same as sin_lat_lo */ for( j=1; j<=stacks; j++ ) { lat_hi = (M_PI * j)/stacks - M_PI/2.0; ny_hi = sin(lat_hi); cos_lat_hi = cos(lat_hi); prev = 0; glBegin(GL_LINES); for( i=0; i<=slices; i++ ) { longitude = (2*M_PI * i)/slices; cos_longitude = cos(longitude); sin_longitude = sin(longitude); nx = cos_lat_lo*cos_longitude; nz = cos_lat_lo*sin_longitude; x_lo=nx*radius; y_lo=ny_lo*radius; z_lo=nz*radius; glVertex3f(x_lo,y_lo,z_lo); nx = cos_lat_hi*cos_longitude; nz = cos_lat_hi*sin_longitude; x_hi=nx*radius; y_hi=ny_hi*radius; z_hi=nz*radius; glVertex3f(x_hi,y_hi,z_hi); if(prev) { glVertex3f(oldx_lo,oldy_lo,oldz_lo); glVertex3f(x_lo,y_lo,z_lo); glVertex3f(oldx_hi,oldy_hi,oldz_hi); glVertex3f(x_hi,y_hi,z_hi); } oldx_lo=x_lo; oldx_hi=x_hi; oldy_lo=y_lo; oldy_hi=y_hi; oldz_lo=z_lo; oldz_hi=z_hi; prev = 1; } glEnd(); lat_lo = lat_hi; ny_lo = ny_hi; cos_lat_lo = cos_lat_hi; } } void twWireSphere(GLfloat radius, GLfloat stacks, GLfloat slices) { // i is the longitude counter, j is the latitude counter // i goes from 0 to slices while longitude goes from 0 to 2PI // j goes from 0 to stacks while latitude goes from -PI to +PI, // where 0,0 is on the positive x axis and negative latitude // is the southern hemisphere (negative y). // This is done using int i, j; GLfloat lat_lo, lat_hi, cos_lat_lo, cos_lat_hi, nx, ny_lo, ny_hi, nz; GLfloat longitude, cos_longitude, sin_longitude; GLfloat x_lo,y_lo,z_lo,x_hi,y_hi,z_hi; GLfloat oldx_lo,oldy_lo,oldz_lo,oldx_hi,oldy_hi,oldz_hi; int prev=0; lat_lo = -M_PI/2; /* latitude of south pole */ cos_lat_lo = 0; /* radius at that latitude */ ny_lo = -1; /* same as sin_lat_lo */ for( j=1; j<=stacks; j++ ) { lat_hi = (M_PI * j)/stacks - M_PI/2.0; ny_hi = sin(lat_hi); cos_lat_hi = cos(lat_hi); prev = 0; glBegin(GL_LINES); for( i=0; i<=slices; i++ ) { longitude = (2*M_PI * i)/slices; cos_longitude = cos(longitude); sin_longitude = sin(longitude); nx = cos_lat_lo*cos_longitude; nz = cos_lat_lo*sin_longitude; x_lo=nx*radius; y_lo=ny_lo*radius; z_lo=nz*radius; glVertex3f(x_lo,y_lo,z_lo); nx = cos_lat_hi*cos_longitude; nz = cos_lat_hi*sin_longitude; x_hi=nx*radius; y_hi=ny_hi*radius; z_hi=nz*radius; glVertex3f(x_hi,y_hi,z_hi); if(prev) { glVertex3f(oldx_lo,oldy_lo,oldz_lo); glVertex3f(x_lo,y_lo,z_lo); glVertex3f(oldx_hi,oldy_hi,oldz_hi); glVertex3f(x_hi,y_hi,z_hi); } oldx_lo=x_lo; oldx_hi=x_hi; oldy_lo=y_lo; oldy_hi=y_hi; oldz_lo=z_lo; oldz_hi=z_hi; prev = 1; } glEnd(); lat_lo = lat_hi; ny_lo = ny_hi; cos_lat_lo = cos_lat_hi; } } void SDA_solid_sphere(GLfloat radius, GLfloat stacks, GLfloat slices) { // i is the longitude counter, j is the latitude counter // i goes from 0-slices while longitude goes from 0 to 2PI // j goes from 0-stacks while latitude goes from -PI to +PI, // where 0,0 is on the positive x axis and negative latitude // is the southern hemisphere (negative y). int i, j; GLfloat lat_lo, lat_hi, cos_lat_lo, cos_lat_hi, nx, ny_lo, ny_hi, nz; GLfloat longitude, cos_longitude, sin_longitude; lat_lo = -M_PI/2.0; /* latitude of south pole */ cos_lat_lo = 0; /* radius at that latitude */ ny_lo = -1; /* same as sin_lat_lo */ for( j=1; j<=stacks; j++ ) { lat_hi = (M_PI * j)/stacks - M_PI/2.0; ny_hi = sin(lat_hi); cos_lat_hi = cos(lat_hi); glBegin(GL_QUAD_STRIP); for( i=0; i<=slices; i++ ) { longitude = (2*M_PI * i)/slices; cos_longitude = cos(longitude); sin_longitude = sin(longitude); nx = cos_lat_lo*cos_longitude; nz = cos_lat_lo*sin_longitude; // The 1- here is because we go from east to west around the // globe, so we want the right hand edge of our texture to map // to the eastern silhouette, and so forth. glTexCoord2f(1-longitude/(2*M_PI),lat_lo/M_PI+0.5); glNormal3f(nx,ny_lo,nz); glVertex3f(nx*radius,ny_lo*radius,nz*radius); nx = cos_lat_hi*cos_longitude; nz = cos_lat_hi*sin_longitude; glTexCoord2f(1-longitude/(2*M_PI),lat_hi/M_PI+0.5); glNormal3f(nx,ny_hi,nz); glVertex3f(nx*radius,ny_hi*radius,nz*radius); } glEnd(); lat_lo = lat_hi; ny_lo = ny_hi; cos_lat_lo = cos_lat_hi; } } BITMAPINFO *TexInfo; /* Texture bitmap information */ GLubyte *TexBits; /* Texture bitmap pixel bits */ void display(void) { glPushAttrib(GL_ALL_ATTRIB_BITS); // save graphics state glClearColor (0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Update viewer position in modelview matrix */ set_view(); // printf("theta = (%g,%g,%g)\n",theta[0],theta[1],theta[2]); glRotatef(theta[0], 1.0, 0.0, 0.0); glRotatef(theta[1], 0.0, 1.0, 0.0); glRotatef(theta[2], 0.0, 0.0, 1.0); /* Define the 2D texture image. */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, 3, TexInfo->bmiHeader.biWidth, TexInfo->bmiHeader.biHeight, 0, //GL_BGR_EXT, GL_RGB, /* replaced preceding. SDA */ GL_UNSIGNED_BYTE, TexBits); glEnable(GL_DEPTH_TEST); if(wire) { glDisable(GL_LIGHTING); SDA_wire_sphere(5,8,8); } else if(texture) { glDisable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); SDA_solid_sphere(5,10,10); } else { glEnable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); SDA_solid_sphere(5,10,10); } glFlush(); glutSwapBuffers(); glPopAttrib(); } int main(int argc, char** argv) { if(argc>1) TexBits = LoadDIBitmap(argv[1], &TexInfo); else // default bmp file. TexBits = LoadDIBitmap("cokecan2.bmp", &TexInfo); printf("Image is %d by %d\n", TexInfo->bmiHeader.biWidth, TexInfo->bmiHeader.biHeight); glutInit(&argc,argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(win_width,win_height); glutCreateWindow(argv[0]); set_view(); glutDisplayFunc(display); glLineWidth(1.0); glutKeyboardFunc(keys); glutMouseFunc(mouse); glutReshapeFunc(reshape); glEnable(GL_DEPTH_TEST); reshape(win_width,win_height); glutMainLoop(); } /* ********************************************************************** Callbacks for mouse buttons and keyboard */ void mouse(int btn, int state, int x, int y) { if(state==GLUT_UP) return; if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN) axis = 0; if(btn==GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) axis = 1; if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN) axis = 2; theta[axis] += 3.0; if( theta[axis] > 360.0 ) theta[axis] -= 360.0; glutPostRedisplay(); } void keys(unsigned char key, int x, int y) { // x and y are unused /* Use x, X, y, Y, z, and Z keys to move viewer */ if(key == 'w') wire = !wire; if(key == 't') texture = !texture; if(key == 'p') printf("viewer=(%g,%g,%g)\n",viewer[0],viewer[1],viewer[2]); if(key == 'q') exit(0); if(key == 'x') viewer[0]-= 1.0; if(key == 'X') viewer[0]+= 1.0; if(key == 'y') viewer[1]-= 1.0; if(key == 'Y') viewer[1]+= 1.0; if(key == 'z') viewer[2]-= 1.0; if(key == 'Z') viewer[2]+= 1.0; glutPostRedisplay(); } /* ********************************************************************** Setting the viewport. To avoid distorting the image, we need to use letterboxing, and compare the aspect ratio of the window (ww/wh) with the aspect ratio of the image or projection (pw/ph). If the window is skinner than the image, we will have wasted space on top, and if the window is shorter than the image, we will have wasted space on the right. */ // Args are window width and window height void set_viewport(GLsizei ww, GLsizei wh) { GLint vh,vw; // viewport height and width // The following condition is equivalent to pw/ph > ww/wh, except it // avoids truncating division. if( pw*wh > ww*ph ) { // ignore hw, since window is too tall; waste space at the top vh = (ww*ph)/pw; vw = ww; } else { // ignore ww, since window is too wide; waste space at the right vh = wh; vw = (wh*pw)/ph; } // cout << "vh = " << vh << " and vw = " << vw << endl; glViewport(0,0,vw,vh); } /* ********************************************************************** Callbacks for reshape. Mainly need to reset the viewport, based on the new dimensions. */ void reshape(GLsizei ww, GLsizei wh) { win_width=ww; // Globals, so others can reset the viewport win_height=wh; set_viewport(ww,wh); }