### GEOMETRY TESTS WITH OPENGL…

Here i will try to explain you graphical adjustments in opengl. We start with our includes as always:

#include <GLTools.h> #include <GLMatrixStack.h> #include <GLFrame.h> #include <GLFrustum.h> #include <GLGeometryTransform.h> #include <math.h> #ifdef __APPLE__ #include <glut/glut.h> #else #define FREEGLUT_STATIC #include <GL/glut.h> #endif

Because matrix manipulation is such an important part of 3D graphics, almost every programmer’s toolbox contains a set of functions or classes for creating and manipulating them. In fact the math3d library contains a rich assortment of functions for this purpose. Transformations are often applied in a hierarchical manner, with one or more objects being drawn relative to one another. This would require a great deal of matrix construction and management by your client code to traverse a complex scene in 3D space. Traditionally, a matrix stack has been employed to facilitate this, and the GLTools library builds such as utility class on top of the math3d matrix functions. This class is called GLMatrixStack. Readers familiar with the now deprecated OpenGL matrix stacks in the compatibility profile will find this class familiar. The constructor of the class allows you to specify the maximum depth of the stack, with the default stack depth being 64. This matrix stack is also initialized to have the identity matrix already on the stack.

GLMatrixStack::GLMatrixStack(int iStackDepth = 64);

You can load the identity matrix on the top matrix by calling LoadIdentity.

void GLMatrixStack::LoadIdentity(void);

Or you can load an arbitrary matrix on top of the stack.

void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);

In addition you can multiply a matrix by the top of the matrix stack. The result of the multiplication is then stored at the top of the stack.

void GLMatrixStack::MultMatrix(const M3DMatrix44f);

Finally, getting the top value off the matrix stack is simply done with GetMatrix, which comes with two overloads suitable for use with the GLShaderManager or just getting a copy of top matrix.

const M3DMatrix44f& GLMatrixStack::GetMatrix(void);

void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix);

The GLMatrixStack class contains three overrides that even allow you to use the GLFrame class instead of a full matrix.

void GLMatrixStack::LoadMatrix(GLFrame& frame); void GLMatrixStack::MultMatrix(GLFrame& frame); void GLMatrixStack::PushMatrix(GLFrame& frame);

The GLMatrixStack class also has built-in support for creating rotations, translating, and scaling matrices. The appropriate functions are listed here.

void MatrixStack::Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); void MatrixStack::Translate(GLfloat x, GLfloat y, GLfloat z); void MatrixStack::Scale(GLfloat x, GLfloat y, GLfloat z);

These functions work similarly to their lower level math3d counterparts, with one exception. The Rotate function takes degrees instead of radians to more closely mimic the now deprecated OpenGL function glRotate. All three of these functions create the appropriate transformation matrix and then multiply it by the top of the matrix stack, essentially adding the transformation to the current matrix (remember you add transformations by multiplying the matrices).

GLMatrixStack modelViewMatix; GLMatrixStack projectionMatrix;

A simple and flexible way to represent a frame of reference is to use a data structure (or class in C++) that contains a position in space, a vector that points forward, and a vector that points upward. Using these quantities, you can uniquely identify a given position and orientation in space. The class from the GLTools library, GLFrame, makes use of the math3d library and stores this information all in one place:GLFrame!

Using a frame of reference such as this to represent an object’s position and orientation is a powerful mechanism. To begin with, you can use this data directly to create a 4 x 4 transformation matrix.

The GLFrame class contains a function that retrieves an appropriately conditioned camera matrix:

void GetCameraMatrix(M3DMatrix44f m, bool bRotationOnly = false);

GLFrame viewFrame;

In an orthographic projection everything that falls within this space is displayed on-screen, and there is really no concept of a camera or eye coordinate system. To set this up, we call the GLFrustum method, SetOrthographic.

GLFrustum::SetOrthographic(GLfloat xMin, GLfloat xMax, GLfloat yMin, GLfloat yMax, GLfloat zMin, GLfloat zMax);

GLFrustum viewFrustum; // sets up the projection matrix for us. GLTriangleBatch torusBatch;

You can probably guess that having a matrix stack for both the modelview matrix and the projection matrix carries a lot of advantages. Very often you will also need to retrieve both of these matrices and multiply them to get the modelview projection matrix. Another useful matrix is the normal matrix, which is used for lighting computations and is derived from the modelview matrix. Another utility class, GLGeometryTransform keeps track of these two matrix stacks for you and quickly retrieves the top of either matrix stack, the modelview projection matrix, or the normal matrix.

GLGeometryTransform transformPipeline; // to manage our matrix stacks. GLShaderManager shaderManager; void ProcessMenu(int value); void RenderScene(void); void SetupRC(); void SpecialKeys(int key, int x, int y); void ChangeSize(int w, int h) // Flags for effects int iCull = 0; int iDepth = 0; // Reset flags as appropriate in response to menu selections void ProcessMenu(int value) { switch(value) { case 1: iDepth = !iDepth; break; case 2: iCull = !iCull; break;

Polygons (triangles) don’t have to be solid. By default, polygons are drawn solid, but you can change this behavior by specifying that polygons are to be drawn as outlines or just points (only the vertices are plotted). The function glPolygonMode allows polygons to be rendered as filled solids, as outlines, or as points only. In addition, you can apply this rendering mode to both sides of the polygons or only to the front or back. void glPolygonMode(GLenum face, GLenum mode);

Like in face culling, the face parameter can be GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK. The mode parameter can be GL_FILL (the default), GL_LINE, or GL_POINT.

case 3: glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; case 4: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); break; case 5: glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); break; } glutPostRedisplay(); } // Called to draw scene void RenderScene(void) { // Clear the window and the depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

By default, every point, line, or triangle you render is rasterized on-screen and in the order in which you specify when you assemble the primitive batch. This can sometimes be problematic. One problem that can occur is if you draw a solid object made up of many triangles, the triangles drawn first can be drawn over by triangles drawn afterward. For example, let’s say you have an object such as a torus (donut shaped object) made up of many triangles. Some of those triangles are on the back side of the torus, and some on the front sides. You can’t see the back sides—at least you aren’t supposed to see the backsides (omitting for the moment the special case of transparent geometry). Depending on your orientation, the order in which the triangles are drawn may simply make a mess of things. Figure 3.16 shows the output of the sample program GeoTest (short for Geometry Test Program) with the torus rotated slightly (use the arrow keys to see this yourself). One potential solution to this problem would be to sort the triangles and render the ones farther away first and then render the nearer triangles on top of them. This is called the painters algorithm and is very inefficient in computer graphics for two reasons. One is that you must write to every pixel twice wherever any geometry overlaps, and writing to memory slows things down. The second is that sorting individual triangles would be prohibitively expensive.

One of the reasons OpenGL makes a distinction between the front and back sides of trian- gles is for culling. Back face culling can significantly improve performance and corrects problems.

This is very efficient, as a whole triangle is thrown away in the primitive assembly stage of rendering, and no wasteful or inappropriate rasterization is performed. General face culling is turned on like this:

glEnable(GL_CULL_FACE);

Note, we did not say whether to cull the front or back of anything. That is controlled by another function, glCullFace.

void glCullFace(GLenum mode);

Valid values for the mode parameter are GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK. To throw away the insides of opaque (nontransparent) geometry takes two lines of code then.

glCullFace(GL_BACK);

glEnable(GL_CULL_FACE);

if(iCull) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);

Depth testing is another effective technique for hidden surface removal. The concept is simple: When a pixel is drawn, it is assigned a value (called the z value) that denotes its distance from the viewer’s perspective. Later, when another pixel needs to be drawn to that screen location, the new pixel’s z value is compared to that of the pixel that is already stored there. If the new pixel’s z value is higher, it is closer to the viewer and thus in front of the previous pixel, so the previous pixel is obscured by the new pixel. If the new pixel’s z value is lower, it must be behind the existing pixel and thus is not obscured. This maneuver is accomplished internally by a depth buffer with storage for a depth value for every pixel on the screen. Almost all the samples in this book use depth testing. You should request a depth buf fer when you set up your OpenGL window wi th GLUT. For example, you can request a color and a depth buffer like this:

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);

If enabled, do depth comparisons and update the depth buffer. Note that even if the depth buffer exists and the depth mask is non-zero, the depth buffer is not updated if the depth test is disabled. See glDepthFunc and glDepthRange. If you do not have a depth buffer, then enabling depth testing will just be ignored.

// Enable depth testing if flag is set if(iDepth) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);

The real value of a matrix class is the ability to save the state by pushing it and then restoring the state later by popping it. With the GLMatrixStack class, you can save the current matrix value by pushing the matrix on the stack with the PushMatrix function. This actually copies the current matrix value and places the new value at the top of the stack. Likewise, PopMatrix removes the top matrix and restores the value underneath. There are several overloads for each of these:

void GLMatrixStack::PushMatrix(void); void PushMatrix(const M3DMatrix44f mMatrix); void PushMatrix(GLFrame& frame); void GLMatrixStack::PopMatrix(void);

In addition to pushing the current matrix on the stack, you can also push an arbitrary matrix on the top of the stack via the M3DMatrix44f data type or the GLFrame class. Next, in the RenderScene function, we begin rendering our geometry by first saving the modelview matrix, which has been set to the identity matrix by default.

// Save the current modelview matrix (the identity matrix) modelViewMatix.PushMatrix(viewFrame); GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f }; //shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed); shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed); torusBatch.Draw();

The final matrix is then passed to the shader as a uniform, and the torus batch is submit- ted to render the object. Rather than getting the current modelview matrix and the projec- tion matrix and then multiplying them, we can now simply ask the transformPipeline for the concatenated matrix. This makes our code much cleaner, less cluttered, and easier to read. This transformation matrix is still at the top of our stack, so we remove it, restor- ing identity by calling PopMatrix.

modelViewMatix.PopMatrix(); glutSwapBuffers(); } // This function does any needed initialization on the rendering context. void SetupRC() { // Black background glClearColor(0.3f, 0.3f, 0.3f, 1.0f ); shaderManager.InitializeStockShaders(); viewFrame.MoveForward(7.0f); // Make the torus gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26); glPointSize(4.0f); } void SpecialKeys(int key, int x, int y) { if(key == GLUT_KEY_UP) viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f); if(key == GLUT_KEY_DOWN) viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f); if(key == GLUT_KEY_LEFT) viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f); if(key == GLUT_KEY_RIGHT) viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f); // Refresh the Window glutPostRedisplay(); }

n the ChangeSize function, we set up our perspective projection. Because this is where we get notified of the window’s dimensions (or if they change), this is a reasonable place to put this code.

void ChangeSize(int w, int h) { // Prevent a divide by zero if(h == 0) h = 1; // Set Viewport to window dimensions glViewport(0, 0, w, h);

The viewFrustum instance of the GLFrustum class sets up the projection matrix for us, and then we load that into our projection matrix object projectionMatrix.

viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f); //Create the projection matrix, and load it on the projection matrix stack projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());

The last thing we do here is initialize the GLGeometryTransform instance transformPipeline by setting its internal pointers to our instances of the modelview matrix stack and projection matrix stacks. We really could have done this in the SetupRC function as well, but resetting them when the window changes size does no harm, and it keeps our matrix and pipeline setup all in one place.

transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix); } // Main entry point for GLUT based programs int main(int argc, char* argv[]) { gltSetWorkingDirectory(argv[0]); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(800, 600); glutCreateWindow("Geometry Test Program"); glutReshapeFunc(ChangeSize); glutSpecialFunc(SpecialKeys); glutDisplayFunc(RenderScene); // Create the Menu glutCreateMenu(ProcessMenu); glutAddMenuEntry("Toggle depth test",1); glutAddMenuEntry("Toggle cull backface",2); glutAddMenuEntry("Set Fill Mode", 3); glutAddMenuEntry("Set Line Mode", 4); glutAddMenuEntry("Set Point Mode", 5); glutAttachMenu(GLUT_RIGHT_BUTTON); GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); return 1; } SetupRC(); glutMainLoop(); return 0; }