// // Demonstrates different ways of rendering a mesh // Draws a number of balls in various ways // Also shows loading of OpenGL functions not in opengl32.dll // under Win32 // // by Jarno van der Linden jvan006@cs.auckland.ac.nz // #include #include #include #include #include #include #include #ifndef M_PI #define M_PI 3.1415926536 #endif const int numlat = 100; const int numlon = 200; typedef enum { DRAWMODE_NOTHING, DRAWMODE_IMMEDIATE, DRAWMODE_STRIPS_IMMEDIATE, DRAWMODE_SINGLESTRIP_IMMEDIATE, DRAWMODE_STRIPS_DISPLAYLIST, DRAWMODE_SINGLESTRIP_DISPLAYLIST, DRAWMODE_VERTEXARRAY, DRAWMODE_VERTEXARRAY_DISPLAYLIST, NUM_DRAWMODES } DrawMode; const std::string drawmodename[NUM_DRAWMODES] = { "nothing", "immediate", "immediate strips", "immediate single strip", "displaylist strips", "displaylist single strip", "vertex array", "vertex array in displaylist" }; DrawMode drawmode = DRAWMODE_IMMEDIATE; // Vertices of a unit radius sphere // Note that for a unit radius sphere, the normals are // equal to the vertex positions float ball[numlat+1][numlon+1][3]; // The number of balls to draw int numballs = 30; // To rotate the balls around a bit to make it more interesting float rot[3] = { 0, 0, 0 }; // Functions not provided by opengl32.dll PFNGLLOCKARRAYSEXTPROC glLockArraysEXT; PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements; // Compute a vertex point of a unit radius sphere void SpherePoint(int latidx, int lonidx, float v[3]) { float lat = M_PI/2 - (float)latidx / numlat * M_PI; float lon = (float)lonidx / numlon * 2*M_PI; v[0] = cos(lat) * sin(lon); v[1] = sin(lat); v[2] = cos(lat) * cos(lon); } // Get the vertices of a ball void CreateBall(void) { for(int lat = 0; lat <= numlat; lat++) { for(int lon = 0; lon <= numlon; lon++) { SpherePoint(lat, lon, ball[lat][lon]); } } } // Draw a ball as triangles using immediate mode void DrawImmediate(void) { glBegin(GL_TRIANGLES); for(int lat = 0; lat < numlat; lat++) { for(int lon = 0; lon < numlon; lon++) { glNormal3fv(ball[lat][lon]); glVertex3fv(ball[lat][lon]); glNormal3fv(ball[lat+1][lon]); glVertex3fv(ball[lat+1][lon]); glNormal3fv(ball[lat+1][lon+1]); glVertex3fv(ball[lat+1][lon+1]); glNormal3fv(ball[lat+1][lon+1]); glVertex3fv(ball[lat+1][lon+1]); glNormal3fv(ball[lat][lon+1]); glVertex3fv(ball[lat][lon+1]); glNormal3fv(ball[lat][lon]); glVertex3fv(ball[lat][lon]); } } glEnd(); } // Draw a ball as triangle strips using immediate mode // Each ring at a latitude is rendered as a separate strip // // lon = 0 1 2 numlon+1 // lat 0---2---4---6 // | /| /| /| // | / | / | / | // |/ |/ |/ | // lat+1 1---3---5---7 // lat+1 8--10--12--14 // | /| /| /| // | / | / | / | // |/ |/ |/ | // lat+2 9--11--13--15 // void DrawStripsImmediate(void) { for(int lat = 0; lat < numlat; lat++) { glBegin(GL_TRIANGLE_STRIP); for(int lon = 0; lon <= numlon; lon++) { glNormal3fv(ball[lat][lon]); glVertex3fv(ball[lat][lon]); glNormal3fv(ball[lat+1][lon]); glVertex3fv(ball[lat+1][lon]); } glEnd(); } } // Draw a ball as a single triangle strip using immediate mode // The latitude rings are joined together using repeated vertices // to make a zero-sized triangle, move down to the next ring, and // reverse direction. This method works on any grid mesh. // // lon = 0 1 2 numlon+1 // // lat 0------2------4------6 // | / | / | / | // | / | / | / | // |/ |/ |/ | // lat+1 1&17---3&15---5&13---7&8&11 // | / | / | / | // | / | / | / | // |/ |/ |/ | // lat+2 16-----14-----12-----9&10 // void DrawSingleStripImmediate(void) { glBegin(GL_TRIANGLE_STRIP); for(int lat = 0; lat < numlat; lat++) { for(int lon = 0; lon <= numlon; lon++) { glNormal3fv(ball[lat][lon]); glVertex3fv(ball[lat][lon]); glNormal3fv(ball[lat+1][lon]); glVertex3fv(ball[lat+1][lon]); } lat++; glNormal3fv(ball[lat][numlon]); glVertex3fv(ball[lat][numlon]); glNormal3fv(ball[lat+1][numlon]); glVertex3fv(ball[lat+1][numlon]); for(int lon = numlon; lon >= 0; lon--) { glNormal3fv(ball[lat+1][lon]); glVertex3fv(ball[lat+1][lon]); glNormal3fv(ball[lat][lon]); glVertex3fv(ball[lat][lon]); } } glEnd(); } // Draw a ball as triangle strips in a display list void DrawStripsDisplayList(void) { static bool first = true; static int list; if(first) { first = false; list = glGenLists(1); glNewList(list, GL_COMPILE); DrawStripsImmediate(); glEndList(); } glCallList(list); } // Draw a ball as a single triangle strip in a display list void DrawSingleStripDisplayList(void) { static bool first = true; static int list; if(first) { first = false; list = glGenLists(1); glNewList(list, GL_COMPILE); DrawSingleStripImmediate(); glEndList(); } glCallList(list); } // Draw a ball as a vertex array void DrawVertexArray(void) { static bool first = true; static unsigned int ballidx[numlat*(numlon+1)*2 + numlat*2]; if(first) { first = false; int i = 0; for(int lat = 0; lat < numlat; lat++) { for(int lon = 0; lon <= numlon; lon++) { ballidx[i++] = lat*(numlon+1) + lon; ballidx[i++] = (lat+1)*(numlon+1) + lon; } lat++; ballidx[i++] = lat*(numlon+1) + numlon; ballidx[i++] = (lat+1)*(numlon+1) + numlon; for(int lon = numlon; lon >= 0; lon--) { ballidx[i++] = (lat+1)*(numlon+1) + lon; ballidx[i++] = lat*(numlon+1) + lon; } } glVertexPointer(3, GL_FLOAT, 0, ball); glNormalPointer(GL_FLOAT, 0, ball); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); if(glLockArraysEXT) glLockArraysEXT(0, (numlat+1)*(numlon+1)); } if(glDrawRangeElements) glDrawRangeElements(GL_TRIANGLE_STRIP, 0, (numlat+1)*(numlon+1), numlat*(numlon+1)*2 + numlat*2, GL_UNSIGNED_INT, ballidx); else glDrawElements(GL_TRIANGLE_STRIP, numlat*(numlon+1)*2 + numlat*2, GL_UNSIGNED_INT, ballidx); } // Draw a ball as a vertex array in a display list void DrawVertexArrayDisplayList(void) { static bool first = true; static int list; if(first) { first = false; list = glGenLists(1); glNewList(list, GL_COMPILE); DrawVertexArray(); glEndList(); } glCallList(list); } float RangeRand(float minv, float maxv) { return (float)rand() / RAND_MAX * (maxv - minv) + minv; } void Display(void) { static clock_t lasttime = clock(); static int framecount = 0; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0,0,-20); glRotatef(rot[1], 0, 1, 0); glRotatef(rot[0], 1, 0, 0); glRotatef(rot[2], 0, 0, 1); srand(42); for(int t = 0; t < numballs; t++) { glPushMatrix(); glTranslatef(RangeRand(-5,5), RangeRand(-5,5), RangeRand(-5,5)); glScalef(RangeRand(0.7,1.3), RangeRand(0.7,1.3), RangeRand(0.7,1.3)); glColor3f(RangeRand(0.2,0.8),RangeRand(0.2,0.8),RangeRand(0.2,0.8)); switch(drawmode) { case DRAWMODE_NOTHING: break; case DRAWMODE_IMMEDIATE: DrawImmediate(); break; case DRAWMODE_STRIPS_IMMEDIATE: DrawStripsImmediate(); break; case DRAWMODE_SINGLESTRIP_IMMEDIATE: DrawSingleStripImmediate(); break; case DRAWMODE_STRIPS_DISPLAYLIST: DrawStripsDisplayList(); break; case DRAWMODE_SINGLESTRIP_DISPLAYLIST: DrawSingleStripDisplayList(); break; case DRAWMODE_VERTEXARRAY: DrawVertexArray(); break; case DRAWMODE_VERTEXARRAY_DISPLAYLIST: DrawVertexArrayDisplayList(); break; } glPopMatrix(); } glutSwapBuffers(); // Output statistics every 3 seconds framecount++; clock_t thistime = clock(); float deltatime = (float)(thistime - lasttime) / CLOCKS_PER_SEC; if(deltatime > 3) { float fps = framecount/deltatime; float tps = numballs*numlat*numlon*2 * fps; std::cerr << "Mode: " << drawmodename[drawmode] << ", frames/sec: " << fps << ", tris/sec: " << tps << std::endl; framecount = 0; lasttime = thistime; } } void Reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, (float)w/h, 1, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void Key(unsigned char key, int x, int y) { switch(key) { case 27: case 'q': exit(EXIT_SUCCESS); case ' ': drawmode = (DrawMode)((drawmode + 1) % NUM_DRAWMODES); break; case '+': numballs += 10; break; case '-': numballs -= 10; if(numballs < 10) numballs = 10; break; default: return; } glutPostRedisplay(); } void Idle(void) { rot[0] += 1; rot[1] += 1.3f; rot[2] += 1.7f; glutPostRedisplay(); } void InitOpenGL(void) { GLfloat light_ambient[] = {1.0, 1.0, 1.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[] = {0.0, 0.0, 1.0, 0.0}; GLfloat global_ambient[] = {0.5, 0.5, 0.5, 1.0}; GLfloat mat_ambient[] = {0.0, 0.0, 0.0, 1.0}; GLfloat mat_diffuse[] = {1.0, 1.0, 1.0, 1.0}; GLfloat mat_specular[] = {0.0, 0.0, 0.0, 1.0}; glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glColorMaterial(GL_FRONT, GL_DIFFUSE); glEnable(GL_COLOR_MATERIAL); glClearColor(0.0, 0.0, 0.0, 0.0); CreateBall(); // Obtain pointers to extra OpenGL functions glLockArraysEXT = (PFNGLLOCKARRAYSEXTPROC)wglGetProcAddress("glLockArraysEXT"); glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)wglGetProcAddress("glDrawRangeElements"); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); glutInitWindowSize(512, 512); glutCreateWindow("Object Render Demo"); glutReshapeFunc(Reshape); glutKeyboardFunc(Key); glutDisplayFunc(Display); glutIdleFunc(Idle); InitOpenGL(); glutMainLoop(); return EXIT_SUCCESS; }