Introduction

This article is my second of many articles describing the mechanics and elements of a 3D Engine. This article describes how to navigate 3D space programmatically through 4x4 matrix transforms. I discuss basic transforms, model transforms, view transformer and projection transforms. The purpose of the series is intended to consolidate a number of topics written in C# allowing non-game programmers to incorporate the power of 3D drawings into their application. While the vast majority of 3D Engines are used for game development, this type of tool is very useful for visual feedback and graphic data input. This tool began with a focus on simulation modeling but the intent is to maintain a level of performance that will allow real-time graphics.

Fortunately work has been really good; consequenty has work has pushed writing articles down the list priorities. This has took significantly longer than I had planned. I am not a computer programmer by profession and as such I would expect that ideas and algorithms expressed in this series to be rewritten and adapted to your application by “real” programmers.

Background

I am currently rewriting one of my company’s software models and have proposed revamping our GUI interface providing users a drawing interface. This series is a product of a rewrite of my original proof of concept application.

I am an Electrical Engineer with Lea+Elliott and as part of my responsibilies I develop and maintain our company's internal models. That being said, I am not a professional programmer so please keep this in mind as you look through my code.

Prior to Using the Code

Before you can use this code you need to download the Tao Framework to access the Tao namespaces. You also need to reference the AGE_Engine3D.dll, add the AGE_Engine3D to your project or copy the applicable .cs file and add them to your project.

The Basics

Both 3D APIs (DirectX and OpenGL) work with 4D vectors and 4x4 matrixes. I have seen different explanations but this is how I compose my matrix transforms. The basic 4x4 Matrix is a composite of a 3x3 matrixes and 3D vector.

These matrix transformations are combined to orient a model into the correct position to be displayed on screen. Unlike normal multiplication, matrix multiplication is not commutative. With matrixes, A*B does not necessary equal B*A. That being said, the order that these transforms are applied is extremely important. This is discussed more in the subsequent sections. [Note: These samples are written implementing the Tao OpenGL interface, but could very easily be modified to service DirectX or XNA interface.]

All transforms listed in this article are implemented in the AGE_Matrix44 class through static methods. The method's name is a good indication of its propose such as:

Simple Transforms

Scale Matrix (Hs)

The Scale Matrix is used to scale a model in one, two or three dimensions. It is composed of a 4x4 matrix with a 3D scaling vector on the diagonal. The scaling vector components represent a scaling in their respective dimension. It is in the form of:

Rotation Matrix (Hr)

There are three rotation matrixes that can be used to rotate a model around the X-Axis, Y-Axis, and Z-Axis. There are three variations of a 4x4 matrix with various arrangements on the M matrix mentioned above. It is in the form of:

Rotation Matrix via Quaternion (Hq)

I often use quaternion for creating my rotation matrixes. It is simple and intuitive. Creating a quaternion for rotation requires a vector identifying the axis of rotation and the angle of rotation. I believe it is commonly used in ArcBall(add hyperlink to) and other orbiting camera schemes. I will discuss it in another article but if you want to look at the code it is included.

publicstatic AGE_Matrix_44 HRotation(ref AGE_Quaternion Rotation)

Transpose Matrix (Ht)

The Transpose Matrix is used to move a model from one position to another. It is composed of a 4x4 identity matrix with a 3D translation vector in the 4th column. The translation vector represents a change in location. It is in the form of:

World Space (Hw)

The World Space Transform is the first transform usually applied to a model. This transform is normally used to scale and orient the model relative to its world. The Model is defined in a model space coordinate system and needs to be translated to the world coordinate system. Let's assume you have a model of a person and it normalized such that the model dimensions are within the range [-1, 1] with an origin of <0,0,0>. You could populate a world with actors referencing this single resource by applying different World Transforms. The World Transform is composed of basic transforms typical applied in this order.

The table (a.k.a. TheTable in the code below) shown in the animation above is a SpatialBaseContainer object. The follow code shows its creation and positioning in 3D space. Note that TheTable only references the table geometry, so if we could have many tables referencing the same geometry using different world transform populating.

The order transforms that are applied is important. Had I placed the table in the center of the room then rotated the table, it would be in a completely different spatial place. It would equate to a 322 feet error.

Child Models and Local Transforms

There are many cases where a model may have sub, child or leaf models. These are kin to a glass relative to its parent table. If the table is not already scaled and aligned with the parent node (a room) the table will have to be scaled, rotated and translated to its proper place via a local transform. This is accomplished by combining the basic transform in a specific order in the same matter as the world space transform. If a child model is allowed to change relative to its parent then each child model will have a separate the overall World Transform and its local transform. The Total World Transform would look something like this for various objects.

By specifying that the TheGlass is a child to TheTable, I only have to locate TheGlass relative to the table. The transforms applied to TheTable will be carried forward to TheGlass. I can also create children to TheGlass and locate them relative to TheGlass. The Following code and image demonstrate this concept.

View Space (Hv)

After a model is transformed to its position into World Space it will then be transformed in to View Space or Camera Space. The transform is characterized by Camera Location and the View direction. The Transform is in the form of:

Because the OpenGL uses a right-handed coordinate system the D vector is multiplied by -1 in the matrix Q construction, but for left-handed coordinate system this would not be necessary.[1]

Provided in the zip file are simple camera classes. In the sample application I used the AGE_WalkThroughCamera class allowing me to create a camera that could move through the building and actively update the camera's focus. This is accomplished by updating the Target and Eye locations, then recalculating the View Matrix. This creates the animation seen in the video above.

Projection Matrixes (Hproj and Hortho)

Perspective Transform

After a model is transformed to its position into View Space, it will then be transformed in to its final viewed position via projection transformation. There are two basic types of projection transforms that I am aware of: Orthographic, and Perspective. The Perspective projection mimics the way we perceive the real world. Objects that are closer appear larger and parallel lines converge at the horizon. Here is how the Perspective transform is constructed.

Orthographic Transform

The Orthographic projection is the somewhat the opposite of the Perspective projection. An object’s depth in the view plane has no bearing of size of the object and parallel lines remain parallel. Here is how the Orthographic transform is constructed.

How to use the Code

Once we have all of these different types of transforms how are they used? In each call to the rendering function the sample application [Implementing the OpenGL interface], the application resets the Projection Matrix (GL_PROJECTION) and the Model/View Matrix (GL_MODELVIEW). Since the Camera and Projection rarely change during each render call, I load both the Projection Matrix and View Matrix into the OpenGL’s Projection Matrix. I also load the identity matrix in the Model/View matrix which will be overloaded as each entity is rendered.

Now that the setup is complete the application cycles through all of my render-able objects and calls their rendering function. Each object is responsible for setting the Model/View Matrix appropriate to the objects requirements.

Share

About the Author

I am a licensed Electrical Engineer at Lea+Elliott, Inc. We specialize in the planning, procurement and implementation of transportation systems, with special emphasis on automated and emerging technologies.

It might have something to do with the Tao Framework(See Creating a Window - Building a 3D Engine[^]). The Age_Engine was dependent to the now defunct Tao Framework. If you don’t have the Tao framework installed then the AGE_Engine will not compile and thus the is no AGE_Engine3D.dll. I have/am migrating the Age_Engine to the OpenTK framework. You can use OpenTK to create a window/OpenGL control and the matrix math in this still valid or at least I am still using it.

As far as getting this example to work, it will have to wait until I can finish updating the Age_Engine which I can only work on as my real job allows.