Matrix Stack

The previous program used a matrix to translate and rotate a torus. This program will do the same transformations, but the matrix will no longer be the only matrix. It will be the topmost matrix in a stack of matrices. Somewhat anticlimactically, the matrices below it in the stack will not be used.

The modelView in threedimensions.cpp is a stack of matrices, initially consisting of one identity matrix. The identity matrix keeps the torus in its default position at the origin, and in its default attitude lying in the XY plane.

The PushMatrix member function of the modelView creates a copy of the topmost matrix in the stack, and pushes this copy onto the stack. The stack now consists of two identity matrices. Conversely, PopMatrix removes and destroys the topmost matrix. The stack now consists of one identity matrix again. The asserts check for overflow and underflow after each push and pop, even though the stack has room for 64 matrices.

The Translate and Rotate member functions change the topmost matrix on the stack. Originally an identity matrix, this matrix now translates the torus away from the observer and rotates it. The GetMatrix member function returns the topmost matrix.

The projectMatrix is another stack of matrices, initially consisting of one identity matrix. The LoadMatrix in reshape replaces this identity matrix with a matrix that causes closer objects to appear bigger and farther objects to appear smaller.

Source code in GL.zip

  1. threedimensions.cpp
  2. main.m
  3. Class GLAppDelegate
  4. Class GLViewController
  5. Class EAGLView

Create the project

Same as the previous program, but with a different threedimensions.cpp file.

Things to try

  1. A GLGeometryTransform is an object that can hold (the addresses of) the two stacks of matrices. We’ll put it in now, even though we don’t need it yet. Define a GLGeometryTransform along with the other global objects at the top of the threedimensions.cpp file, and include <GLGeometryTransform.h>.
    GLGeometryTransform geometryTransform;
    
    At the end of the reshape function, store the addresses of the two stacks into the geometryTransform.
    	geometryTransform.SetMatrixStacks(modelView, projection);
    
    Change the call to UseStackShader in the display function to the following.
    	shaderManager.UseStockShader(
    		GLT_SHADER_POINT_LIGHT_DIFF,
    		geometryTransform.GetModelViewMatrix(),
    		geometryTransform.GetProjectionMatrix(),
    		lightPosition,
    		red
    	);
    

  2. Keep the original torus, but add a sphere moving in lockstep with it.
    GLTriangleBatch sphere;	//at the top of threedimensions.cpp
    
    	gltMakeSphere(sphere, .25, 40, 40);	//in SetupRC
    
    void display(GLfloat theta)
    {
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	static const M3DVector4f lightPosition = {0.0f, 10.0f, 5.0f, 1.0f};
    	static const GLfloat red[] = {1.0f, 0.0f, 0.0f, 1.0f};
    
    	modelView.PushMatrix();
    		modelView.Translate(0.0f, 0.0f, -7.0f);
    		modelView.Rotate(theta, 0.0f, 1.0f, 0.0f);
    
    		shaderManager.UseStockShader(
    			GLT_SHADER_POINT_LIGHT_DIFF,
    			geometryTransform.GetModelViewMatrix(),
    			geometryTransform.GetProjectionMatrix(),
    			lightPosition,
    			red
    		);
    		torus.Draw();
    
    		modelView.PushMatrix();
    			modelView.Translate(0.0f, 0.0f, 1.0f);
    
    			shaderManager.UseStockShader(
    				GLT_SHADER_POINT_LIGHT_DIFF,
    				geometryTransform.GetModelViewMatrix(),
    				geometryTransform.GetProjectionMatrix(),
    				lightPosition,
    				red
    			);
    			sphere.Draw();
    
    		modelView.PopMatrix();
    	modelView.PopMatrix();
    }
    

  3. What about a snowman? Adjust the frustum so that the visible distance goes from 0.01f to 1000.0f.
    GLTriangleBatch sphere;	//at the top of threedimensions.cpp
    
    gltMakeSphere(sphere, 1.0f, 40, 40);	//in SetupRC
    
    void useStockShader() {
    	static const M3DVector4f lightPosition = {0.0f, 10.0f, 5.0f, 1.0f};
    	static const GLfloat snow[] = {1.0f, 0.980392f, 0.980392f, 1.0f};
    
    	shaderManager.UseStockShader(
    		 GLT_SHADER_POINT_LIGHT_DIFF,
    		 geometryTransform.GetModelViewMatrix(),
    		 geometryTransform.GetProjectionMatrix(),
    		 lightPosition,
    		 snow
    	);
    
    	sphere.Draw();
    }
    
    void display(GLfloat theta)
    {
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    	//ground
    	modelView.PushMatrix();
    		modelView.Translate(0.0f, -1000.1f, 0.0f);
    		modelView.Scale(1000.0f, 1000.0f, 1000.0f);
    		useStockShader();
    	modelView.PopMatrix();
    
    	//snow man
    	modelView.PushMatrix();
    
    		//This translation will apply to the abdomen, thorax, and head.
    		modelView.Translate(0.0f, 0.0f, -7.0f);
    
    		//abdomen
    		modelView.PushMatrix();
    			modelView.Scale(0.5f, 0.5f, 0.5f);
    			useStockShader();
    		modelView.PopMatrix();
    
    		//This translation will apply to the thorax and head.
    		modelView.Translate(0.0f, 0.6f, 0.0f);
    
    		//thorax
    		modelView.PushMatrix();
    			modelView.Scale(0.4f, 0.4f, 0.4f);
    			useStockShader();
    		modelView.PopMatrix();
    
    		//head
    		modelView.PushMatrix();
    			modelView.Translate(0.0f, 0.5f, 0.0f);
    			modelView.Scale(0.25f, 0.25f, 0.25f);
    			useStockShader();
    		modelView.PopMatrix();
    	modelView.PopMatrix();
    }
    
    Give him a carrot nose and coal-lump eyes.