Loads brick.ppm into a texture and displays it. Toggle it by pressing t.
main.cpp:
// Alexandri Zavodny // CSE 40166: Computer Graphics, Fall 2010 // Example: Simple texturing example: use 't' to enable textures and rotate // using an arcball camera model. #include <math.h> #include <GL/glut.h> #include <stdio.h> #include <stdlib.h> #include <string> #include <vector> using namespace std; // GLOBAL VARIABLES //////////////////////////////////////////////////////////// static size_t windowWidth = 640; static size_t windowHeight = 480; static float aspectRatio; GLint leftMouseButton, rightMouseButton; //status of the mouse buttons int mouseX = 0, mouseY = 0; //last known X and Y of the mouse float cameraTheta, cameraPhi, cameraRadius; //camera position in spherical coordinates float x, y, z; //camera position in cartesian coordinates //some global variables for our textures unsigned char *brickTexData; //the actual image data GLuint brickTexHandle; //a handle to the texture in our OpenGL context int brickTexWidth, brickTexHeight; //variables for the image width/height bool useTexturing = false; //toggled with 't' // readPPM() /////////////////////////////////////////////////////////////////// // // This function reads an ASCII PPM, returning true if the function succeeds and // false if it fails. If it succeeds, the variables imageWidth and // imageHeight will hold the width and height of the read image, respectively. // // It's not terribly robust. // // Returns the image as an unsigned character array containing // imageWidth*imageHeight*3 entries (for that many bytes of storage). // // NOTE: this function expects imageData to be UNALLOCATED, and will allocate // memory itself. If the function fails (returns false), imageData // will be set to NULL and any allocated memory will be automatically deallocated. // //////////////////////////////////////////////////////////////////////////////// bool readPPM(string filename, int &imageWidth, int &imageHeight, unsigned char* &imageData) { FILE *fp = fopen(filename.c_str(), "r"); int temp, maxValue; fscanf(fp, "P%d", &temp); if(temp != 3) { fprintf(stderr, "Error: PPM file is not of correct format! (Must be P3, is P%d.)\n", temp); fclose(fp); return false; } //got the file header right... fscanf(fp, "%d", &imageWidth); fscanf(fp, "%d", &imageHeight); fscanf(fp, "%d", &maxValue); //now that we know how big it is, allocate the buffer... imageData = new unsigned char[imageWidth*imageHeight*3]; if(!imageData) { fprintf(stderr, "Error: couldn't allocate image memory. Dimensions: %d x %d.\n", imageWidth, imageHeight); fclose(fp); return false; } //and read the data in. for(int j = 0; j < imageHeight; j++) { for(int i = 0; i < imageWidth; i++) { fscanf(fp, "%hhd", &imageData[(j*imageWidth+i)*3+0]); fscanf(fp, "%hhd", &imageData[(j*imageWidth+i)*3+1]); fscanf(fp, "%hhd", &imageData[(j*imageWidth+i)*3+2]); } } fclose(fp); return true; } // registerOpenGLTexture() ///////////////////////////////////////////////////// // // Attempts to register an image buffer with OpenGL. Sets the texture handle // appropriately upon success, and uses a number of default texturing // values. This function only provides the basics: just sets up this image // as an unrepeated 2D texture. // //////////////////////////////////////////////////////////////////////////////// bool registerOpenGLTexture(unsigned char *imageData, int texWidth, int texHeight, GLuint &texHandle) { //first off, get a real texture handle. glGenTextures(1, &texHandle); //make sure that future texture functions affect this handle glBindTexture(GL_TEXTURE_2D, texHandle); //set this texture's color to be multiplied by the surface colors -- // GL_MODULATE instead of GL_REPLACE allows us to take advantage of OpenGL lighting glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //set the minification ang magnification functions to be linear; not perfect // but much better than nearest-texel (GL_NEAREST). glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //Set the texture to repeat in S and T -- though it doesn't matter here // because our texture coordinates are always in [0,0] to [1,1]. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); //actually transfer the texture to the GPU! glTexImage2D(GL_TEXTURE_2D, //still working with 2D textures, obv. 0, //not using mipmapping, so this is the highest level. GL_RGB, //we're going to provide OpenGL with R, G, and B components... texWidth, //...of this width... texHeight, //...and this height. 0, //give it a border of 0 pixels (none) GL_RGB, //and store it, internally, as RGB (OpenGL will convert to floats between 0.0 and 1.0f) GL_UNSIGNED_BYTE, //this is the format our data is in, and finally, imageData); //there's the data! //whoops! i guess this function can't fail. in an ideal world, there would //be checks with glGetError() that we could use to determine if this failed. return true; } // recomputeOrientation() ////////////////////////////////////////////////////// // // This function updates the camera's position in cartesian coordinates based // on its position in spherical coordinates. Should be called every time // cameraTheta, cameraPhi, or cameraRadius is updated. // //////////////////////////////////////////////////////////////////////////////// void recomputeOrientation() { x = cameraRadius * sinf(cameraTheta)*sinf(cameraPhi); z = cameraRadius * -cosf(cameraTheta)*sinf(cameraPhi); y = cameraRadius * -cosf(cameraPhi); glutPostRedisplay(); } // resizeWindow() ////////////////////////////////////////////////////////////// // // GLUT callback for window resizing. Resets GL_PROJECTION matrix and viewport. // //////////////////////////////////////////////////////////////////////////////// void resizeWindow(int w, int h) { aspectRatio = w / (float)h; windowWidth = w; windowHeight = h; //update the viewport to fill the window glViewport(0, 0, w, h); //update the projection matrix with the new window properties glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0,aspectRatio,0.1,100000); } // mouseCallback() ///////////////////////////////////////////////////////////// // // GLUT callback for mouse clicks. We save the state of the mouse button // when this is called so that we can check the status of the mouse // buttons inside the motion callback (whether they are up or down). // //////////////////////////////////////////////////////////////////////////////// void mouseCallback(int button, int state, int thisX, int thisY) { //update the left and right mouse button states, if applicable if(button == GLUT_LEFT_BUTTON) leftMouseButton = state; else if(button == GLUT_RIGHT_BUTTON) rightMouseButton = state; //and update the last seen X and Y coordinates of the mouse mouseX = thisX; mouseY = thisY; } // mouseMotion() /////////////////////////////////////////////////////////////// // // GLUT callback for mouse movement. We update cameraPhi, cameraTheta, and/or // cameraRadius based on how much the user has moved the mouse in the // X or Y directions (in screen space) and whether they have held down // the left or right mouse buttons. If the user hasn't held down any // buttons, the function just updates the last seen mouse X and Y coords. // //////////////////////////////////////////////////////////////////////////////// void mouseMotion(int x, int y) { if(leftMouseButton == GLUT_DOWN) { cameraTheta += (x - mouseX)*0.005; cameraPhi += (y - mouseY)*0.005; // make sure that phi stays within the range (0, M_PI) if(cameraPhi <= 0) cameraPhi = 0+0.001; if(cameraPhi >= M_PI) cameraPhi = M_PI-0.001; recomputeOrientation(); //update camera (x,y,z) based on (radius,theta,phi) } else if(rightMouseButton == GLUT_DOWN) { double totalChangeSq = (x - mouseX) + (y - mouseY); cameraRadius += totalChangeSq*0.01; //limit the camera radius to some reasonable values so the user can't get lost if(cameraRadius < 2.0) cameraRadius = 2.0; if(cameraRadius > 10.0) cameraRadius = 10.0; recomputeOrientation(); //update camera (x,y,z) based on (radius,theta,phi) } mouseX = x; mouseY = y; } // initScene() ///////////////////////////////////////////////////////////////// // // A basic scene initialization function; should be called once after the // OpenGL context has been created. Doesn't need to be called further. // //////////////////////////////////////////////////////////////////////////////// void initScene() { glEnable(GL_DEPTH_TEST); float lightCol[4] = { 1, 1, 1, 1}; float ambientCol[4] = {0.3, 0.3, 0.3, 1.0}; glLightfv(GL_LIGHT0,GL_DIFFUSE,lightCol); glLightfv(GL_LIGHT0, GL_AMBIENT, ambientCol); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glShadeModel(GL_SMOOTH); //glShadeModel(GL_FLAT); } // renderScene() /////////////////////////////////////////////////////////////// // // GLUT callback for scene rendering. Sets up the modelview matrix, renders // a teapot to the back buffer, and switches the back buffer with the // front buffer (what the user sees). // //////////////////////////////////////////////////////////////////////////////// void renderScene(void) { //update the modelview matrix based on the camera's position glMatrixMode(GL_MODELVIEW); //make sure we aren't changing the projection matrix! glLoadIdentity(); gluLookAt(x, y, z, //camera is located at (x,y,z) 0, 0, 0, //camera is looking at (0,0,0) 0.0f,1.0f,0.0f); //up vector is (0,1,0) (positive Y) //update the light position after we set the modelview matrix; //lights get transformed by the modelview matrix just like every other point! float lPosition[4] = { 10, 10, 10, 1 }; glLightfv(GL_LIGHT0,GL_POSITION,lPosition); //clear the render buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //enable texture if necessary if(useTexturing) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, brickTexHandle); } else { glDisable(GL_TEXTURE_2D); } //draw a simple quad in the XY plane, with texture coordinates and normals for lighting. glBegin(GL_QUADS); glNormal3f(0,0,1); glTexCoord2f(0,0); glVertex3f(-2.5,1.6125,0); glNormal3f(0,0,1); glTexCoord2f(1,0); glVertex3f(2.5,1.6125,0); glNormal3f(0,0,1); glTexCoord2f(1,1); glVertex3f(2.5,-1.6125,0); glNormal3f(0,0,1); glTexCoord2f(0,1); glVertex3f(-2.5,-1.6125,0); glEnd(); //push the back buffer to the screen glutSwapBuffers(); } // normalKeys() //////////////////////////////////////////////////////////////// // // GLUT keyboard callback. // //////////////////////////////////////////////////////////////////////////////// void normalKeys(unsigned char key, int x, int y) { if(key == 'q' || key == 'Q') exit(0); if(key == 't' || key == 'T') useTexturing = !useTexturing; } // main() ////////////////////////////////////////////////////////////////////// // // Program entry point. Does not process command line arguments. // //////////////////////////////////////////////////////////////////////////////// int main(int argc, char **argv) { //create a double-buffered GLUT window at (50,50) with predefined windowsize glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(50,50); glutInitWindowSize(windowWidth,windowHeight); glutCreateWindow("it's a briiiick (doo dooo do) wall (doo dooo do)"); //give the camera a 'pretty' starting point! cameraRadius = 7.0f; cameraTheta = 2.80; cameraPhi = 2.0; recomputeOrientation(); //register callback functions... glutKeyboardFunc(normalKeys); glutDisplayFunc(renderScene); glutIdleFunc(renderScene); glutReshapeFunc(resizeWindow); glutMouseFunc(mouseCallback); glutMotionFunc(mouseMotion); //do some basic OpenGL setup initScene(); //read in our file and register our texture bool success = readPPM("brick.ppm", brickTexWidth, brickTexHeight, brickTexData); //NOTE THAT THIS NEEDS TO HAPPEN AFTER THE OPENGL CONTEXT HAS BEEN INITIALIZED. // which it has, thankfully. if(success) registerOpenGLTexture(brickTexData, brickTexWidth, brickTexHeight, brickTexHandle); //and enter the GLUT loop, never to exit. glutMainLoop(); return(0); }