/////////////////////////////////////////////////////////////////////////////// // main.cpp // ======== // testing buffer object for vertex, GL_ARB_vertex_buffer_object extension // // AUTHOR: Song Ho Ahn (song.ahn@gmail.com) // CREATED: 2005-10-04 // UPDATED: 2014-04-25 /////////////////////////////////////////////////////////////////////////////// // in order to get function prototypes from glext.h, define GL_GLEXT_PROTOTYPES before including glext.h #define GL_GLEXT_PROTOTYPES #ifdef __APPLE__ #include #else #include #endif #include #include #include #include #include "glext.h" // OpenGL extensions #include "glInfo.h" // glInfo struct #include "teapot.h" // meshes of teapot #include "Timer.h" #include "WaveFunc.h" // GLUT CALLBACK functions void displayCB(); void reshapeCB(int w, int h); void timerCB(int millisec); void idleCB(); void keyboardCB(unsigned char key, int x, int y); void mouseCB(int button, int stat, int x, int y); void mouseMotionCB(int x, int y); // CALLBACK function when exit() called /////////////////////////////////////// void exitCB(); void initGL(); int initGLUT(int argc, char **argv); bool initSharedMem(); void clearSharedMem(); void initLights(); void setCamera(float posX, float posY, float posZ, float targetX, float targetY, float targetZ); GLuint createVBO(const void* data, int dataSize, GLenum target=GL_ARRAY_BUFFER_ARB, GLenum usage=GL_STATIC_DRAW_ARB); void deleteVBO(const GLuint vboId); void drawString(const char *str, int x, int y, float color[4], void *font); void drawString3D(const char *str, float pos[3], float color[4], void *font); void showInfo(); void updateVertices(float* vertices, float* srcVertices, float* srcNormals, int count, float time); void showFPS(); void toOrtho(); void toPerspective(); // constants const int SCREEN_WIDTH = 400; const int SCREEN_HEIGHT = 300; const float CAMERA_DISTANCE = 10.0f; const int TEXT_WIDTH = 8; const int TEXT_HEIGHT = 13; // global variables void *font = GLUT_BITMAP_8_BY_13; GLuint vboId1 = 0; // ID of VBO for vertex arrays (to store vertex coords and normals) GLuint vboId2 = 0; // ID of VBO for index array int screenWidth; int screenHeight; bool mouseLeftDown; bool mouseRightDown; float mouseX, mouseY; float cameraAngleX; float cameraAngleY; float cameraDistance; bool vboSupported, vboUsed; int drawMode = 0; Timer timer, t1, t2; float drawTime, updateTime; float* srcVertices; // pointer to copy of vertex array int vertexCount; // number of vertices // function pointers for VBO Extension // Windows needs to get function pointers from ICD OpenGL drivers, // because opengl32.dll does not support extensions higher than v1.1. #ifdef _WIN32 PFNGLGENBUFFERSARBPROC pglGenBuffersARB = 0; // VBO Name Generation Procedure PFNGLBINDBUFFERARBPROC pglBindBufferARB = 0; // VBO Bind Procedure PFNGLBUFFERDATAARBPROC pglBufferDataARB = 0; // VBO Data Loading Procedure PFNGLBUFFERSUBDATAARBPROC pglBufferSubDataARB = 0; // VBO Sub Data Loading Procedure PFNGLDELETEBUFFERSARBPROC pglDeleteBuffersARB = 0; // VBO Deletion Procedure PFNGLGETBUFFERPARAMETERIVARBPROC pglGetBufferParameterivARB = 0; // return various parameters of VBO PFNGLMAPBUFFERARBPROC pglMapBufferARB = 0; // map VBO procedure PFNGLUNMAPBUFFERARBPROC pglUnmapBufferARB = 0; // unmap VBO procedure #define glGenBuffersARB pglGenBuffersARB #define glBindBufferARB pglBindBufferARB #define glBufferDataARB pglBufferDataARB #define glBufferSubDataARB pglBufferSubDataARB #define glDeleteBuffersARB pglDeleteBuffersARB #define glGetBufferParameterivARB pglGetBufferParameterivARB #define glMapBufferARB pglMapBufferARB #define glUnmapBufferARB pglUnmapBufferARB #endif // function pointers for WGL_EXT_swap_control #ifdef _WIN32 typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void); PFNWGLSWAPINTERVALEXTPROC pwglSwapIntervalEXT = 0; PFNWGLGETSWAPINTERVALEXTPROC pwglGetSwapIntervalEXT = 0; #define wglSwapIntervalEXT pwglSwapIntervalEXT #define wglGetSwapIntervalEXT pwglGetSwapIntervalEXT #endif /////////////////////////////////////////////////////////////////////////////// int main(int argc, char **argv) { // init global vars initSharedMem(); // init GLUT and GL initGLUT(argc, argv); initGL(); // register exit callback atexit(exitCB); // get OpenGL info glInfo glInfo; glInfo.getInfo(); //glInfo.printSelf(); #ifdef _WIN32 // check VBO is supported by your video card if(glInfo.isExtensionSupported("GL_ARB_vertex_buffer_object")) { // get pointers to GL functions glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB"); glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB"); glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB"); glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB"); glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB"); glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB"); glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress("glMapBufferARB"); glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress("glUnmapBufferARB"); // check once again VBO extension if(glGenBuffersARB && glBindBufferARB && glBufferDataARB && glBufferSubDataARB && glMapBufferARB && glUnmapBufferARB && glDeleteBuffersARB && glGetBufferParameterivARB) { vboSupported = vboUsed = true; std::cout << "Video card supports GL_ARB_vertex_buffer_object." << std::endl; } else { vboSupported = vboUsed = false; std::cout << "Video card does NOT support GL_ARB_vertex_buffer_object." << std::endl; } } // check EXT_swap_control is supported if(glInfo.isExtensionSupported("WGL_EXT_swap_control")) { // get pointers to WGL functions wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT"); if(wglSwapIntervalEXT && wglGetSwapIntervalEXT) { // disable v-sync wglSwapIntervalEXT(0); std::cout << "Video card supports WGL_EXT_swap_control." << std::endl; } } #else // for linux, do not need to get function pointers, it is up-to-date if(glInfo.isExtensionSupported("GL_ARB_vertex_buffer_object")) { vboSupported = vboUsed = true; std::cout << "Video card supports GL_ARB_vertex_buffer_object." << std::endl; } else { vboSupported = vboUsed = false; std::cout << "Video card does NOT support GL_ARB_vertex_buffer_object." << std::endl; } #endif // print vertex array size in bytes std::cout << "Vertex Array: " << sizeof(teapotVertices) << " bytes\n"; std::cout << "Normal Array: " << sizeof(teapotNormals) << " bytes\n"; std::cout << " Index Array: " << sizeof(teapotIndices) << " bytes\n"; std::cout << std::endl; if(vboSupported) { int bufferSize; // create vertex buffer objects, you need to delete them when program exits // Try to put both vertex coords array and vertex normal array in the same buffer object. // glBufferDataARB with NULL pointer reserves only memory space. // Copy actual data with 2 calls of glBufferSubDataARB, one for vertex coords and one for normals. // target flag is GL_ARRAY_BUFFER_ARB, and usage flag is GL_STREAM_DRAW_ARB because we will update vertices every frame. glGenBuffersARB(1, &vboId1); glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId1); glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(teapotVertices)+sizeof(teapotNormals), 0, GL_STREAM_DRAW_ARB); glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(teapotVertices), teapotVertices); glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, sizeof(teapotVertices), sizeof(teapotNormals), teapotNormals); glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &bufferSize); std::cout << "Vertex and Normal Array in VBO: " << bufferSize << " bytes\n"; // create VBO for index array // Target of this VBO is GL_ELEMENT_ARRAY_BUFFER_ARB and usage is GL_STATIC_DRAW_ARB vboId2 = createVBO(teapotIndices, sizeof(teapotIndices), GL_ELEMENT_ARRAY_BUFFER_ARB, GL_STATIC_DRAW_ARB); glGetBufferParameterivARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &bufferSize); std::cout << "Index Array in VBO: " << bufferSize << " bytes\n"; } // start timer, the elapsed time will be used for updateVertices() timer.start(); // the last GLUT call (LOOP) // window will be shown and display callback is triggered by events // NOTE: this call never return main(). glutMainLoop(); /* Start GLUT event-processing loop */ return 0; } /////////////////////////////////////////////////////////////////////////////// // initialize GLUT for windowing /////////////////////////////////////////////////////////////////////////////// int initGLUT(int argc, char **argv) { // GLUT stuff for windowing // initialization openGL window. // it is called before any other GLUT routine glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL); // display mode glutInitWindowSize(screenWidth, screenHeight); // window size glutInitWindowPosition(100, 100); // window location // finally, create a window with openGL context // Window will not displayed until glutMainLoop() is called // it returns a unique ID int handle = glutCreateWindow(argv[0]); // param is the title of window // register GLUT callback functions glutDisplayFunc(displayCB); //glutTimerFunc(33, timerCB, 33); // redraw only every given millisec glutIdleFunc(idleCB); // redraw only every given millisec glutReshapeFunc(reshapeCB); glutKeyboardFunc(keyboardCB); glutMouseFunc(mouseCB); glutMotionFunc(mouseMotionCB); return handle; } /////////////////////////////////////////////////////////////////////////////// // initialize OpenGL // disable unused features /////////////////////////////////////////////////////////////////////////////// void initGL() { glShadeModel(GL_SMOOTH); // shading mathod: GL_SMOOTH or GL_FLAT glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment // enable /disable features glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); //glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); glEnable(GL_CULL_FACE); // track material ambient and diffuse from surface color, call it before glEnable(GL_COLOR_MATERIAL) glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glClearColor(0, 0, 0, 0); // background color glClearStencil(0); // clear stencil buffer glClearDepth(1.0f); // 0 is near, 1 is far glDepthFunc(GL_LEQUAL); initLights(); } /////////////////////////////////////////////////////////////////////////////// // write 2d text using GLUT // The projection matrix must be set to orthogonal before call this function. /////////////////////////////////////////////////////////////////////////////// void drawString(const char *str, int x, int y, float color[4], void *font) { glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT); // lighting and color mask glDisable(GL_LIGHTING); // need to disable lighting for proper text color glDisable(GL_TEXTURE_2D); glColor4fv(color); // set text color glRasterPos2i(x, y); // place text position // loop all characters in the string while(*str) { glutBitmapCharacter(font, *str); ++str; } glEnable(GL_TEXTURE_2D); glEnable(GL_LIGHTING); glPopAttrib(); } /////////////////////////////////////////////////////////////////////////////// // draw a string in 3D space /////////////////////////////////////////////////////////////////////////////// void drawString3D(const char *str, float pos[3], float color[4], void *font) { glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT); // lighting and color mask glDisable(GL_LIGHTING); // need to disable lighting for proper text color glDisable(GL_TEXTURE_2D); glColor4fv(color); // set text color glRasterPos3fv(pos); // place text position // loop all characters in the string while(*str) { glutBitmapCharacter(font, *str); ++str; } glEnable(GL_TEXTURE_2D); glEnable(GL_LIGHTING); glPopAttrib(); } /////////////////////////////////////////////////////////////////////////////// // initialize global variables /////////////////////////////////////////////////////////////////////////////// bool initSharedMem() { screenWidth = SCREEN_WIDTH; screenHeight = SCREEN_HEIGHT; mouseLeftDown = mouseRightDown = false; mouseX = mouseY = 0; cameraAngleX = cameraAngleY = 0.0f; cameraDistance = CAMERA_DISTANCE; drawMode = 0; // 0:fill, 1: wireframe, 2:points drawTime = updateTime = 0; // make a copy of vertex array // It will be used as src data for updateVertices() vertexCount = sizeof(teapotVertices) / (3 * sizeof(float)); srcVertices = new float[vertexCount * 3]; // copy data int count = vertexCount * 3; for(int i=0; i < count; ++i) srcVertices[i] = teapotVertices[i]; return true; } /////////////////////////////////////////////////////////////////////////////// // clean up shared memory /////////////////////////////////////////////////////////////////////////////// void clearSharedMem() { // clean up VBOs if(vboSupported) { deleteVBO(vboId1); deleteVBO(vboId2); vboId1 = vboId2 = 0; } // delete copy of vertex array delete [] srcVertices; } /////////////////////////////////////////////////////////////////////////////// // initialize lights /////////////////////////////////////////////////////////////////////////////// void initLights() { // set up light colors (ambient, diffuse, specular) GLfloat lightKa[] = {.2f, .2f, .2f, 1.0f}; // ambient light GLfloat lightKd[] = {.7f, .7f, .7f, 1.0f}; // diffuse light GLfloat lightKs[] = {1, 1, 1, 1}; // specular light glLightfv(GL_LIGHT0, GL_AMBIENT, lightKa); glLightfv(GL_LIGHT0, GL_DIFFUSE, lightKd); glLightfv(GL_LIGHT0, GL_SPECULAR, lightKs); // position the light float lightPos[4] = {0, 0, 20, 1}; // positional light glLightfv(GL_LIGHT0, GL_POSITION, lightPos); glEnable(GL_LIGHT0); // MUST enable each light source after configuration } /////////////////////////////////////////////////////////////////////////////// // set camera position and lookat direction /////////////////////////////////////////////////////////////////////////////// void setCamera(float posX, float posY, float posZ, float targetX, float targetY, float targetZ) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(posX, posY, posZ, targetX, targetY, targetZ, 0, 1, 0); // eye(x,y,z), focal(x,y,z), up(x,y,z) } /////////////////////////////////////////////////////////////////////////////// // generate vertex buffer object and bind it with its data // You must give 2 hints about data usage; target and mode, so that OpenGL can // decide which data should be stored and its location. // VBO works with 2 different targets; GL_ARRAY_BUFFER_ARB for vertex arrays // and GL_ELEMENT_ARRAY_BUFFER_ARB for index array in glDrawElements(). // The default target is GL_ARRAY_BUFFER_ARB. // By default, usage mode is set as GL_STATIC_DRAW_ARB. // Other usages are GL_STREAM_DRAW_ARB, GL_STREAM_READ_ARB, GL_STREAM_COPY_ARB, // GL_STATIC_DRAW_ARB, GL_STATIC_READ_ARB, GL_STATIC_COPY_ARB, // GL_DYNAMIC_DRAW_ARB, GL_DYNAMIC_READ_ARB, GL_DYNAMIC_COPY_ARB. /////////////////////////////////////////////////////////////////////////////// GLuint createVBO(const void* data, int dataSize, GLenum target, GLenum usage) { GLuint id = 0; // 0 is reserved, glGenBuffersARB() will return non-zero id if success glGenBuffersARB(1, &id); // create a vbo glBindBufferARB(target, id); // activate vbo id to use glBufferDataARB(target, dataSize, data, usage); // upload data to video card // check data size in VBO is same as input array, if not return 0 and delete VBO int bufferSize = 0; glGetBufferParameterivARB(target, GL_BUFFER_SIZE_ARB, &bufferSize); if(dataSize != bufferSize) { glDeleteBuffersARB(1, &id); id = 0; std::cout << "[createVBO()] Data size is mismatch with input array\n"; } return id; // return VBO id } /////////////////////////////////////////////////////////////////////////////// // destroy a VBO // If VBO id is not valid or zero, then OpenGL ignores it silently. /////////////////////////////////////////////////////////////////////////////// void deleteVBO(const GLuint vboId) { glDeleteBuffersARB(1, &vboId); } /////////////////////////////////////////////////////////////////////////////// // wobble the vertex in and out along normal /////////////////////////////////////////////////////////////////////////////// void updateVertices(float* dstVertices, float* srcVertices, float* srcNormals, int count, float time) { if(!dstVertices || !srcVertices || !srcNormals) return; WaveFunc wave; wave.func = FUNC_SIN; // sine wave function wave.amp = 0.08f; // amplitude wave.freq = 1.0f; // cycles/sec wave.phase = 0; // horizontal shift wave.offset = 0; // vertical shift float waveLength = 1.5f; float height; float x, y, z; for(int i=0; i < count; ++i) { // get source from original vertex array x = *srcVertices; ++srcVertices; y = *srcVertices; ++srcVertices; z = *srcVertices; ++srcVertices; // compute phase (horizontal shift) wave.phase = (x + y + z) / waveLength; height = wave.update(time); // update vertex coords *dstVertices = x + (height * *srcNormals); // x ++dstVertices; ++srcNormals; *dstVertices = y + (height * *srcNormals); // y ++dstVertices; ++srcNormals; *dstVertices = z + (height * *srcNormals); // z ++dstVertices; ++srcNormals; } } /////////////////////////////////////////////////////////////////////////////// // display info messages /////////////////////////////////////////////////////////////////////////////// void showInfo() { // backup current model-view matrix glPushMatrix(); // save current modelview matrix glLoadIdentity(); // reset modelview matrix // set to 2D orthogonal projection glMatrixMode(GL_PROJECTION); // switch to projection matrix glPushMatrix(); // save current projection matrix glLoadIdentity(); // reset projection matrix gluOrtho2D(0, screenWidth, 0, screenHeight); // set to orthogonal projection float color[4] = {1, 1, 1, 1}; std::stringstream ss; ss << "VBO: " << (vboUsed ? "on" : "off") << std::ends; // add 0(ends) at the end drawString(ss.str().c_str(), 1, screenHeight-TEXT_HEIGHT, color, font); ss.str(""); // clear buffer ss << std::fixed << std::setprecision(3); ss << "Updating Time: " << updateTime << " ms" << std::ends; drawString(ss.str().c_str(), 1, screenHeight-(2*TEXT_HEIGHT), color, font); ss.str(""); ss << "Drawing Time: " << drawTime << " ms" << std::ends; drawString(ss.str().c_str(), 1, screenHeight-(3*TEXT_HEIGHT), color, font); ss.str(""); ss << "Press SPACE key to toggle VBO on/off." << std::ends; drawString(ss.str().c_str(), 1, 1, color, font); // unset floating format ss << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield); // restore projection matrix glPopMatrix(); // restore to previous projection matrix // restore modelview matrix glMatrixMode(GL_MODELVIEW); // switch to modelview matrix glPopMatrix(); // restore to previous modelview matrix } /////////////////////////////////////////////////////////////////////////////// // display frame rates /////////////////////////////////////////////////////////////////////////////// void showFPS() { static Timer timer; static int count = 0; static std::string fps = "0.0 FPS"; double elapsedTime = 0.0;; // update fps every second ++count; elapsedTime = timer.getElapsedTime(); if(elapsedTime > 1.0) { std::stringstream ss; ss << std::fixed << std::setprecision(1); ss << (count / elapsedTime) << " FPS" << std::ends; // update fps string ss << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield); fps = ss.str(); count = 0; // reset counter timer.start(); // restart timer } // backup current model-view matrix glPushMatrix(); // save current modelview matrix glLoadIdentity(); // reset modelview matrix // set to 2D orthogonal projection glMatrixMode(GL_PROJECTION); // switch to projection matrix glPushMatrix(); // save current projection matrix glLoadIdentity(); // reset projection matrix gluOrtho2D(0, screenWidth, 0, screenHeight); // set to orthogonal projection float color[4] = {1, 1, 0, 1}; int textWidth = (int)fps.size() * TEXT_WIDTH; drawString(fps.c_str(), screenWidth-textWidth, screenHeight-TEXT_HEIGHT, color, font); // restore projection matrix glPopMatrix(); // restore to previous projection matrix // restore modelview matrix glMatrixMode(GL_MODELVIEW); // switch to modelview matrix glPopMatrix(); // restore to previous modelview matrix } /////////////////////////////////////////////////////////////////////////////// // set projection matrix as orthogonal /////////////////////////////////////////////////////////////////////////////// void toOrtho() { // set viewport to be the entire window glViewport(0, 0, (GLsizei)screenWidth, (GLsizei)screenHeight); // set orthographic viewing frustum glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, screenWidth, 0, screenHeight, -1, 1); // switch to modelview matrix in order to set scene glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /////////////////////////////////////////////////////////////////////////////// // set the projection matrix as perspective /////////////////////////////////////////////////////////////////////////////// void toPerspective() { // set viewport to be the entire window glViewport(0, 0, (GLsizei)screenWidth, (GLsizei)screenHeight); // set perspective viewing frustum glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0f, (float)(screenWidth)/screenHeight, 1.0f, 1000.0f); // FOV, AspectRatio, NearClip, FarClip // switch to modelview matrix in order to set scene glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } //============================================================================= // CALLBACKS //============================================================================= void displayCB() { // clear buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // save the initial ModelView matrix before modifying ModelView matrix glPushMatrix(); // tramsform camera glTranslatef(0, 0, -cameraDistance); glRotatef(cameraAngleX, 1, 0, 0); // pitch glRotatef(cameraAngleY, 0, 1, 0); // heading t1.start(); //============================================================== if(vboUsed) // draw teapot using VBOs { // bind VBOs with IDs and set the buffer offsets of the bound VBOs // When buffer object is bound with its ID, all pointers in gl*Pointer() // are treated as offset instead of real pointer. glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId1); // measure the elapsed time of updateVertices() t2.start(); //--------------------------------------------------------- // map the buffer object into client's memory // Note that glMapBufferARB() causes sync issue. // If GPU is working with this buffer, glMapBufferARB() will wait(stall) // for GPU to finish its job. float *ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB); if(ptr) { // wobble vertex in and out along normal updateVertices(ptr, srcVertices, teapotNormals, vertexCount, (float)timer.getElapsedTime()); glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); // release pointer to mapping buffer } t2.stop(); //---------------------------------------------------------- updateTime = (float)t2.getElapsedTimeInMilliSec(); // before draw, specify vertex and index arrays with their offsets glNormalPointer(GL_FLOAT, 0, (void*)sizeof(teapotVertices)); glVertexPointer(3, GL_FLOAT, 0, 0); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboId2); glIndexPointer(GL_UNSIGNED_SHORT, 0, 0); drawTeapotVBO(); // it is good idea to release VBOs with ID 0 after use. // Once bound with 0, all pointers in gl*Pointer() behave as real // pointer, so, normal vertex array operations are re-activated glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); } else // draw teapot using vertex array method { t2.start(); //--------------------------------------------------------- // wobbling vertices along normals updateVertices(teapotVertices, srcVertices, teapotNormals, vertexCount, (float)timer.getElapsedTime()); t2.stop(); //---------------------------------------------------------- updateTime = (float)t2.getElapsedTimeInMilliSec(); drawTeapot(); } t1.stop(); //=============================================================== drawTime = (float)t1.getElapsedTimeInMilliSec() - updateTime; // draw info messages showInfo(); showFPS(); glPopMatrix(); glutSwapBuffers(); } void reshapeCB(int w, int h) { screenWidth = w; screenHeight = h; toPerspective(); } void timerCB(int millisec) { glutTimerFunc(millisec, timerCB, millisec); glutPostRedisplay(); } void idleCB() { glutPostRedisplay(); } void keyboardCB(unsigned char key, int x, int y) { switch(key) { case 27: // ESCAPE exit(0); break; case ' ': if(vboSupported) vboUsed = !vboUsed; break; case 'd': // switch rendering modes (fill -> wire -> point) case 'D': drawMode = ++drawMode % 3; if(drawMode == 0) // fill mode { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); } else if(drawMode == 1) // wireframe mode { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); } else // point mode { glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); } break; default: ; } } void mouseCB(int button, int state, int x, int y) { mouseX = x; mouseY = y; if(button == GLUT_LEFT_BUTTON) { if(state == GLUT_DOWN) { mouseLeftDown = true; } else if(state == GLUT_UP) mouseLeftDown = false; } else if(button == GLUT_RIGHT_BUTTON) { if(state == GLUT_DOWN) { mouseRightDown = true; } else if(state == GLUT_UP) mouseRightDown = false; } } void mouseMotionCB(int x, int y) { if(mouseLeftDown) { cameraAngleY += (x - mouseX); cameraAngleX += (y - mouseY); mouseX = x; mouseY = y; } if(mouseRightDown) { cameraDistance -= (y - mouseY) * 0.2f; mouseY = y; } } void exitCB() { clearSharedMem(); }