You should finish up with six rotating cuboids of different sizes and positions (shown below).
We haven't really added a lot of code in this tutorial, but we have re-organised it into classes. Take some time to familiarise yourself with the new structure, you'll see that most of the functions are unchanged. Full screen rendering and depth buffers are pretty straightforward, now that we've created them we don't really need to worry about them any further. In the next tutorial we will look into matrix transformations. We touched on them in the last tutorial, but this time we will go a little further and demonstrate the power of matrix transformations.
DirectX Tutorial 5: Matrix Transformations
In this tutorial we will look more into matrix transformations. With matrix transformations you can rotate, scale and move vertices (and therefore objects). We will see how to rotate five cubes, all in a different way. One around the x axis, one around the y axis, one around the z axis, one around a user defined axis and we'll scale and rotate another cube around all three axis (x, y and z). You can download the full source code by clicking the "Download Source" link above.
Well, matrices are quite an advanced maths topic so I'll try my best to explain the basics. A matrix is a grid of numbers that we can use in 3D graphics to modify the position of vertices (points in 3D space). Some of the main uses for matrices in 3D graphics are to rotate, scale and translate (move) vertices. So, what does a matrix look like? In DirectX, a matrix is a 4x4 grid of numbers; Fig 5.1 below shows an example matrix that will scale a vertex by five units:
Fig 5.1
OK, so how can a matrix change the position values of a vertex? To modify the x, y and z values of a vertex you need to multiply them by the matrix. Fig 5.2 below shows an example of multiplying a vertex by a matrix:
Fig 5.2
This is a pretty simple calculation, all you need to do is multiply each of the x, y and z values by the columns one by one. Each column will give you one of the new values for the vertex. You may notice that in the "current vertex" in our example, we have added a 1 after the z value. This is simply to balance the vertex against the matrix. You need to have the same number of values, as there are columns for the transformation to work correctly.
So, all you need to rotate, scale or translate (move) a vertex is the correct matrix. Luckily for you, DirectX has a number of functions for generating these common matrices. What if you want to scale AND rotate some vertices? Well, first you need to have two matrices, one for the rotation and one for the scale. Then you need to multiply the two matrices (for scale and rotate) together to form one new matrix that scales and rotates. This new matrix is then applied to the vertices. You must be careful to have the matrices in the right order. MatrixA X MatrixB will give you a different answer than MatrixB X MatrixA. Fig 5.3 shows how to multiply two matrices together:
Fig 5.3
To multiply two matrices together you need to multiply each row in the first matrix with each column in the second. In the example above, we have multiplied the first row (in the first matrix) by each column (in the second matrix). These four calculations will give us the top row of our answer matrix. To calculate the other three rows, simply multiply the second, third and forth rows in the first matrix by each column in the second. As I said above, DirectX has a built in function for multiplying two matrices, so don't worry too much about this!
In the code for this tutorial, we will have 5 cubes in different positions all rotating differently. Here is a walkthtough of the code and how it works.
Step 1: Create objects
The first thing we need to do is create and define our five cubes. I have defined 5 cubes as member variables and modified the InitialiseGame method of CGame to create and set their centre position. Their default size is 10x10x10 which I haven't changed. The InitialiseGame method is now as follows:
bool CGame::InitialiseGame() {
//Setup games objects here
m_pCube1 = new CCuboid(m_pD3DDevice);
m_pCube1->SetPosition(-27.0, 0.0, 0.0);
m_pCube2 = new CCuboid(m_pD3DDevice);
m_pCube2->SetPosition(-9.0, 0.0, 0.0);
m_pCube3 = new CCuboid(m_pD3DDevice);
m_pCube3->SetPosition(9.0, 0.0, 0.0);
m_pCube4 = new CCuboid(m_pD3DDevice);
m_pCube4->SetPosition(27.0, 0.0, 0.0);
m_pCube5 = new CCuboid(m_pD3DDevice);
m_pCube5->SetPosition(0.0, 15.0, 0.0);
return true;
}
Fig 5.4 below shows the initial positions of our five cubes. We would normally create all of these objects at the origin, and the translate them. But for the purpose of this tutorial, we'll create them in the positions show below. The centre coordinates are shown with each cube.
Fig 5.4
Step 2: Create Transformation Matrices
The next step is to create our transformation matrices. We want our cubes to all rotate differently, so we need a different transformation matrix for each one. Cube 1, 2 and 3 will rotate around the x, y, and z axis respectively. Cube 4 will rotate around a user defined axis and cube 5 will rotate around the x, y and z axis, and it will be enlarged (scaled).
To create the x, y and z rotation matrices, we will use the DirectX functions D3DXMatrixRotationX, D3DXMatrixRotationY and D3DXMatrixRotationZ. The first parameter is a pointer to a D3DXMATRIX structure that will hold the rotation matrix, the second parameter is the angle to rotate in radians. The code snippet below, taken from our new Render method, shows these calls.
//Create the rotation transformation matrices around the x, y and z axis
D3DXMatrixRotationX(&matRotationX, timeGetTime()/400.0f);
D3DXMatrixRotationY(&matRotationY, timeGetTime()/400.0f);
D3DXMatrixRotationZ(&matRotationZ, timeGetTime()/400.0f);
The next thing to do is create the rotation matrix around a user define axis. To do this, we will use the D3DXMatrixRotationAxis function of DirectX. The first parameter is a pointer to a D3DXMATRIX structure that will hold the rotation matrix. The second parameter is a pointer to a D3DXVECTOR3 structure that defines our user defined axis. We want our axis to be a 45 degree angle between the x and y axis, so we define our access by using the following vector (1, 1, 0). Fig 5.5 below shows how to define our axis. The third parameter is the angle to rotate in radians.
//Create the rotation transformation matrices around our user defined axis
D3DXMatrixRotationAxis(&matRotationUser1, &D3DXVECTOR3(1.0f, 1.0f, 0.0f), timeGetTime()/400.0f);
Fig 5.5
In addition to rotation, we will need to create some other matrices. We will need a matrix to scale cube 5 so that it is 50% larger. We will also need some matrices to translate (move) our cube to the origin and back again (I'll explain why later). So, we use the following code to create these matrices:
//Create the translation (move) matrices