www.pudn.com > FrustumCulling.rar > Main.cpp
//***********************************************************************// // // // - "Talk to me like I'm a 3 year old!" Programming Lessons - // // // // $Author: DigiBen digiben@gametutorials.com // // // // $Program: Frustum Culling // // // // $Description: Demonstrates checking if shapes are in view // // // // $Date: 8/28/01 // // // //***********************************************************************// #include "main.h" // This includes our header file #include "Camera.h" #include/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * // This tutorial demonstrates how to tell if an object is inside of your frustum or not. // What is a frustum? A frustum is the camera's view. If something does NOT appear // on the screen, then it is not inside of the frustum. Think of it as a skewed box. // It is skewed because the farther away, the more you can see. It has 6 sides. // There is the right, left, top, bottom, near and far. Anything rendered outside // of these planes, is not shown. You need to know how to test this because it speeds // up your programs, especially when there are a lot of objects, and/or a large world. // You don't want to send you whole world down the 3D pipeline, that would kill your // frame rate, so you want to test to see if the object would even NEED to be drawn // before you send it to be rendered. Certainly this is useless if you have very few objects, // depending on their size of course. // // CONTROLS: // MOUSE MOVEMENT - Looks around // C, SPACE BAR, Mouse LEFT CLICK- Turns culling ON/OFF // +/- - Increase and decreases the amount of spheres // ESCAPE - quits // You can tell how many spheres are being rendered out of how // many possible in the windows title bar. You will notice the huge difference // from 1000 possible spheres when frustum culling is turned off. It barely moves. #define MAX_SPHERES 1000 // This holds the maximum amount of spheres. (use + and - to change amount) #define MAX_DISTANCE 30 // This is the distance that the spheres disappear on either side #define SPHERE_SPEED 0.2f // This is the speed the spheres move #define MAX_RADIUS 5 // This is the spheres radius*10 (really it's 0.5) // The camera object we have here is from the camera tutorial series CCamera g_Camera; // This is our global camera object CFrustum g_Frustum; // This is our global frustum object bool g_bIgnoreFrustum = false; // We want to check against the frustum initially // You can change this variable by the +/- keys. int g_MaxSpheres = MAX_SPHERES / 2; // Set the spheres on screen to the max/2 allowed. Sphere g_Spheres[MAX_SPHERES]; // This is our structure that holds the sphere data /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ///// ///// This function initializes the game window. ///// ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* void Init() { InitializeGL(SCREEN_WIDTH, SCREEN_HEIGHT); // Init OpenGL with the global rect /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * SDL_ShowCursor(SDL_DISABLE); // Turn the cursor off for camera movement g_Camera.PositionCamera(0, 0, 1, 0, 0, 0, 0, 1, 0); // Here we turn on lighting, but just leave the defaults so at // least we have some good shading on the spheres. glEnable(GL_LIGHT0); // Turn on a light glEnable(GL_LIGHTING); // Turn on lighting // Once lighting is turned on, we need to enable this to use color. glEnable(GL_COLOR_MATERIAL); // Allow us to use color for the spheres srand(SDL_GetTicks()); // Seed the random numbers // This is where we initialize the sphere's data. We randomly assign color // and place them around the camera. for(int i = 0; i < MAX_SPHERES; i++) { // Assign the radius and position - No farther away than 30 from the camera g_Spheres[i].radius = (rand() % MAX_RADIUS) * 0.1f; g_Spheres[i].xPos = (rand() % (MAX_DISTANCE * 10)) * 0.1f; g_Spheres[i].yPos = (rand() % (MAX_DISTANCE * 10)) * 0.1f; g_Spheres[i].zPos = (rand() % (MAX_DISTANCE * 10)) * 0.1f; // We have a 50/50 chance of putting the sphere on the left side and behind the camera. // This is because we are centered at the origin if(rand() % 2) g_Spheres[i].xPos = -g_Spheres[i].xPos; if(rand() % 2) g_Spheres[i].yPos = -g_Spheres[i].yPos; if(rand() % 2) g_Spheres[i].zPos = -g_Spheres[i].zPos; // Assign the sphere's color randomly g_Spheres[i].r = rand() % 256; // Get a random red color g_Spheres[i].g = rand() % 256; // Get a random green color g_Spheres[i].b = rand() % 256; // Get a random blue color } /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * } ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ///// ///// This function renders the entire scene. ///// ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* void RenderScene() { /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * int spheresRendered = 0; // This will hold how many spheres are being rendered char strText[255]={0}; // This will hold the window title info glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer glLoadIdentity(); // Reset The matrix // Give OpenGL our camera position g_Camera.Look(); // We don't need to calculate this every frame, only when the camera view changes. // I just did it every frame anyway. In this case it isn't a big deal. g_Frustum.CalculateFrustum(); // Calculate the frustum each frame // If you are unfamiliar with Quadrics, see the tutorial on Quadrics at www.GameTutorials.com. // They basically allow you to draw circles and cylinders fast and easily. GLUquadricObj *pObj = gluNewQuadric(); // Get a Quadric off the stack // Loop through all of our allowed spheres and render them to the screen if in the frustum. for(int i = 0; i < g_MaxSpheres; i++) // g_MaxSpheres varies. { g_Spheres[i].zPos += SPHERE_SPEED; // Increase the Z position of the sphere. // Below we check if the sphere needs to be draw or not. If g_bIgnoreFrustum is TRUE, // it draws it regardless (which is SLOOOooOoW). We just pass in the (X, Y, Z) // and the radius of the sphere to find out if it is inside of the frustum. if(g_bIgnoreFrustum || g_Frustum.SphereInFrustum(g_Spheres[i].xPos, g_Spheres[i].yPos, g_Spheres[i].zPos, g_Spheres[i].radius)) { // Set the sphere's color glColor3ub(g_Spheres[i].r, g_Spheres[i].g, g_Spheres[i].b); // Create a new scope before positiong the sphere so we don't effect the other spheres. glPushMatrix(); // Position the sphere on the screen at it's XYZ position. glTranslatef(g_Spheres[i].xPos, g_Spheres[i].yPos, g_Spheres[i].zPos); // Create a sphere with the desired radius chosen in the beginning. gluSphere(pObj, g_Spheres[i].radius, 20, 20); // Draw the sphere with a radius of 0.5 glPopMatrix(); // Close the scope of this matrix spheresRendered++; // Increase the amount of spheres rendered } // Here we check to see if the sphere went out of our range, // If so, we need to set it back again with a new random position. if(g_Spheres[i].zPos > MAX_DISTANCE) { // Give the sphere a new random position back in the beginning. g_Spheres[i].xPos = (rand() % (MAX_DISTANCE * 10)) * 0.1f; g_Spheres[i].yPos = (rand() % (MAX_DISTANCE * 10)) * 0.1f; g_Spheres[i].zPos = -MAX_DISTANCE; // Send it to the back again. // Give a 50/50 chance for the sphere to be to the left/right or above/below the XY axis. // This is because we are centered at the origin if(rand() % 2) g_Spheres[i].xPos = -g_Spheres[i].xPos; if(rand() % 2) g_Spheres[i].yPos = -g_Spheres[i].yPos; } } // Since I didn't want to add more code for a rendered font, I decided to just // render the frustum information in the title bar of the window. // The information tells you how many spheres were rendered and out of how many. // Use +/- to increase and decrease the max spheres tested. sprintf(strText, "www.GameTutorials.com - Spheres Rendered: %d / %d", spheresRendered, g_MaxSpheres); SDL_WM_SetCaption(strText,"GameTutorials"); // Change the window title bar /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * SDL_GL_SwapBuffers(); // Swap the backbuffers to the foreground gluDeleteQuadric(pObj); // Free the Quadric } //////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\* //////// //////// This function handles the main game loop //////// //////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\* void MainLoop(void) { bool done = false; // is our job done ? not yet ! SDL_Event event; while(! done) // as long as our job's not done { while( SDL_PollEvent(& event) ) // look for events (like keystrokes, resizing etc.) { switch ( event.type ) // what kind of event have we got ? { case SDL_QUIT : // if user wishes to quit done = true; // this implies our job is done break; case SDL_KEYDOWN : // if the user has pressed a key HandleKeyPressEvent( & event. key.keysym ); // callback for handling keystrokes, arg is key pressed break; case SDL_VIDEORESIZE : // if there is a resize event // request SDL to resize the window to the size and depth etc. that we specify MainWindow = SDL_SetVideoMode( event.resize.w, event.resize.h, SCREEN_DEPTH, VideoFlags ); SizeOpenGLScreen(event.resize.w, event.resize.h); // now resize the OpenGL viewport if(MainWindow == NULL) // if window resize has failed { cerr << " Failed resizing SDL window : " << SDL_GetError() << endl; // report error Quit(0); } break; default: // any other event break; // nothing to do } // switch } // while( SDL_ ... /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * // Render the scene every frame, and also check for mouse movement g_Camera.Update(); // Update the camera data RenderScene(); // Render the scene every frame /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// * } // while( ! done) } ///////////////////////////////////////////////////////////////////////////////// // // * QUICK NOTES * // // This is the largest tutorial we have created so far. Not a lot of concept, // but a lot of code. The only code you need to look at is in RenderScene() // and Frustum.cpp. That is the good stuff :). The rest of it is just to // make it a little more interesting. I toiled over whether I should just have // a bunch of spheres going back and forth on the screen, but I eventually decided // it would be better if there was some camera movement so it didn't look fixed. // You can use the mouse to move the camera around, as well as left click to turn // culling on and off. You can also add and subtract the spheres with '+'/'-'. // Like Usual, Escape quits. I took the Camera code from the Camera3 tutorial. // This tutorial was based off the quadric tutorial. // // Let's go over a brief overview of the things we learned here: // // 1) First we need to abstract the frustum from OpenGL. To do that we need the // projection and modelview matrix. To get the projection matrix we use: // // glGetFloatv( GL_PROJECTION_MATRIX, /* An Array of 16 floats */ ); // Then, to get the modelview matrix we use: // // glGetFloatv( GL_MODELVIEW_MATRIX, /* An Array of 16 floats */ ); // // These 2 functions gives us an array of 16 floats (The matrix). // // 2) Next, we need to combine these 2 matrices. We do that by matrix multiplication. // // 3) Now that we have the 2 matrixes combined, we can abstract the sides of the frustum. // This will give us the normal and the distance from the plane to the origin (ABC and D). // // 4) After abstracting a side, we want to normalize the plane data. (A B C and D). // // 5) Now we have our frustum, and we can check points against it using the plane equation. // Once again, the plane equation (A*x + B*y + C*z + D = 0) says that if, point (X,Y,Z) // times the normal of the plane (A,B,C), plus the distance of the plane from origin, // will equal 0 if the point (X, Y, Z) lies on that plane. If it is behind the plane // it will be a negative distance, if it's in front of the plane (the way the normal is facing) // it will be a positive number. // // // If you need more help on the plane equation and why this works, download our // Ray Plane Intersection Tutorial at www.GameTutorials.com. // // That's pretty much it with frustums. There is a lot more we could talk about, but // I don't want to complicate this tutorial more than I already have. // // I want to thank Mark Morley for his tutorial on Frustum. Most of everything I got // here comes from his teaching. If you want more in-depth, visit his tutorial at: // // http://www.markmorley.com/opengl/frustumculling.html // // Good luck! // // // Ben Humphrey (DigiBen) // Game Programmer // DigiBen@GameTutorials.com // Co-Web Host of www.GameTutorials.com // //