 World Transform Introduction You can get by just using the D3DX functions to do your Translation, Rotation and Scaling but it doesn't hurt to know a little more about how they work. Each of these 3 transforms will be discussed and then we'll go over how to combine them. This lesson also shows how to render the same object multiple times as if they were separate objects. Translation If nothing ever moved in a game or graphics demo, it would be pretty boring. Translation is movement along the axis without changing orientation. You can create a Translation matrix using the D3DX function D3DXMatrixTranslation. By setting the resulting matrix as your World Matrix you can make your model move around. ```D3DXMATRIX *D3DXMatrixTranslation( D3DXMATRIX *p_out, FLOAT x, FLOAT y, FLOAT z ); ``` The coordinates you provide to the function are used as offsets. Assuming that your model is centered on its origin (0,0,0), then the x,y,z coordinates you supply will be it's location in World Space. Essentially these offsets are added to each and every vertex that passes through it. Based on the information in the SDK, I've written my own function to generate a Translation Matrix. It's very easy to do. ```D3DXMATRIX *dhMatrixTranslation(D3DXMATRIX *p_out,float p_x,float p_y,float p_z){ p_out->_11 = 1.0f; p_out->_12 = 0.0f; p_out->_13 = 0.0f; p_out->_14 = 0.0f; p_out->_21 = 0.0f; p_out->_22 = 1.0f; p_out->_23 = 0.0f; p_out->_24 = 0.0f; p_out->_31 = 0.0f; p_out->_32 = 0.0f; p_out->_33 = 1.0f; p_out->_34 = 0.0f; p_out->_41 = p_x; p_out->_42 = p_y; p_out->_43 = p_z; p_out->_44 = 1.0f; return p_out; } ``` As you can see there is a diagonal where all values are set to 1.0. If all other values were set to 0, then this would be the Identity Matrix. However, 3 values are set based on parameters passed in. If you happened to pass in all zeros, then as you would expect, the resulting matrix is Identity. To emphasize the changes required from the Identity matrix, the function could also be written like this: ```D3DXMATRIX *dhMatrixTranslation2(D3DXMATRIX *p_out,float p_x,float p_y,float p_z){ D3DXMatrixIdentity(p_out); p_out->_41 = p_x; p_out->_42 = p_y; p_out->_43 = p_z; return p_out; } ``` To create a Translation Matrix that would move an object to the left of the origin by 5 units, we could use the following code. ```D3DXMatrix trans_matrix; D3DXMatrixTranslation(&trans_matrix,//Matrix -5, //X offset 0, //Y offset 0); //Z offset g_d3d_device->SetTransform(D3DTS_WORLD,&trans_matrix); ``` Whether you use D3DXMatrixTranslation or either one of the functions I wrote, the result would be the same. Any or all of X, Y, and Z offsets can be used, though this example only uses one of them. Rotation Rotation causes the vertices to orbit around the object's origin. Here is the code to create a matrix that would rotate the vertices around the X axis. ```D3DXMATRIX *dhMatrixRotationX(D3DXMATRIX *p_out, float p_angle ){ float my_sin, my_cos; my_sin=(float)sin(p_angle); my_cos=(float)cos(p_angle); p_out->_11 = 1.0f; p_out->_12 = 0.0f; p_out->_13 = 0.0f; p_out->_14 = 0.0f; p_out->_21 = 0.0f; p_out->_22 = my_cos; p_out->_23 = my_sin; p_out->_24 = 0.0f; p_out->_31 = 0.0f; p_out->_32 = -my_sin; p_out->_33 = my_cos; p_out->_34 = 0.0f; p_out->_41 = 0.0f; p_out->_42 = 0.0f; p_out->_43 = 0.0f; p_out->_44 = 1.0f; return p_out; } ``` Again, this is simply the Identity matrix with a few changes. The code for rotation around the Y and Z axes are very similar so I won't show it here. They can be found in the code download for those who are interested. Note: All angles are given in radians, not degrees. To rotate about more than one axis at a time, you could either combine the matrices (as we'll show a bit later in this lesson) or use the D3DXMatrixRotationYawPitchRoll function. As you probably guessed, there are D3DX versions for each of the individual rotations as well: D3DXMatrixRotationX, D3DXMatrixRotationY, and D3DXMatrixRotationZ. ```D3DXMATRIX *D3DXMatrixRotationYawPitchRoll( D3DXMATRIX *p_out, //Result Matrix FLOAT Yaw, //Y-axis rotation FLOAT Pitch, //X-axis rotation FLOAT Roll //Z-axis rotation ); ``` The order these rotations are performed is: Roll (z), Pitch(x), then Yaw(y). Scaling Scaling is when you change the size of an object. The parameters passed to the functions are multipliers. If you set the X scale to 1.0 then the size doesn't change. If you set it to 2.0 then it will double in size along the X-axis, and 0.5 will make it half as large. Generally you should try to have your models in the same units as your world, and so scaling is rarely needed. However it may come in handy from time to time, so we'll cover it briefly. Here are 2 versions of code you could use to generate a Scaling Matrix. ```D3DXMATRIX *dhMatrixScaling(D3DXMATRIX *p_out, float p_x,float p_y, float p_z){ p_out->_11 = p_x; p_out->_12 = 0.0f; p_out->_13 = 0.0f; p_out->_14 = 0.0f; p_out->_21 = 0.0f; p_out->_22 = p_y; p_out->_23 = 0.0f; p_out->_24 = 0.0f; p_out->_31 = 0.0f; p_out->_32 = 0.0f; p_out->_33 = p_z; p_out->_34 = 0.0f; p_out->_41 = 0.0f; p_out->_42 = 0.0f; p_out->_43 = 0.0f; p_out->_44 = 1.0f; return p_out; } D3DXMATRIX *dhMatrixScaling2(D3DXMATRIX *p_out, float p_x,float p_y, float p_z){ D3DXMatrixIdentity(p_out); p_out->_11 = p_x; p_out->_22 = p_y; p_out->_33 = p_z; return p_out; } ``` Of course there is the D3DX version called D3DXMatrixScaling, so there's no need to write your own. Since there are 3 different scaling values, you can cause an object to scale by any or all of the 3 axes. Combining Matrices It will often happen that you want to Translate as well as Rotate and object. To do this, you create your separate matrices and then combine them by multiplying them together. It is very important to note that order matters. The order that you multiply the matrices is the order that the transformations will be applied. Typically you want rotation to be performed first, then translation. This will change the object's orientation and then offset it within the world. Doing it in the opposite order would cause the object to be offset into the world, and then revolve around the origin. To multiply 2 matrices, use D3DXMatrixMultiply. ```D3DXMATRIX *D3DXMatrixMultiply( D3DXMATRIX *pOut, //Result Matrix const D3DXMATRIX *pM1, //Source Matrix #1 const D3DXMATRIX *pM2 //Source Matrix #2 ); ``` Here is a small sample showing how to make a Translation Matrix and a Rotation Matrix, then combine them and set the result as the World Matrix. ```D3DXMATRIX world_matrix; D3DXMATRIX translation_matrix; D3DXMATRIX rotation_matrix; //3 units to the right and 10 units down the Z-axis D3DXMatrixTranslation(&translation_matrix, 3, //X 0, //Y 10); //Z D3DXMatrixRotationX(&rotation_matrix,D3DX_PI/2); //Rotate 90 degrees around the X-axis D3DXMatrixMultiply(&world_matrix,&translation_matrix,&rotation_matrix); g_d3d_device->SetTransform(D3DTS_WORLD,&world_matrix); ``` Drawing Multiple Times In this lesson we use very simple geometry, a triangle. But even if you have a lot of models you may still use the same objects multiple times just in different locations and with different orientations. A lot of people seem to get confused by the idea of drawing the same geometry in multiple locations, but it couldn't be much easier. If we have 2 translation matrices (trans1 and trans2) and we want to draw our triangle in the 2 different locations represented by those matrices, we simply set one matrix then draw, then set the other and draw again. ```D3DXMATRIX trans1,trans2; //Assume these are already initialized g_d3d_device->SetTransform(D3DTS_WORLD,&trans1); g_d3d_device->DrawPrimitiveUP(D3DPT_TRIANGLELIST,1,triangle,sizeof(tri_vertex)); g_d3d_device->SetTransform(D3DTS_WORLD,&trans2); g_d3d_device->DrawPrimitiveUP(D3DPT_TRIANGLELIST,1,triangle,sizeof(tri_vertex)); ``` When using DrawPrimitive you also have to set the stream with SetStreamSource, but the idea is the same. About The Example Code In the sample code we'll use the Matrix functions provided by D3DX since that is what you will typically do in your projects. However my versions of the functions are included as well and since they take the same parameters as the D3DX functions it's trivial to change them to use my functions if you wish. To show Scaling, a triangle will be drawn with varying scaling values. You'll notice that the X and Y values for scaling increase at different rates, this is to show that scaling does not have to be uniform. Since the triangle has no depth, there is no Z scaling. The animation technique used here is very simple. Static variables are used to track the animation state. The biggest problem with this method is that it's frame dependant. On a fast PC it will got much faster than it does on a slower PC. In a later lesson I'll show how to do time-based animation. To show Translation, a triangle will move back and forth across the top of the window. A constant value is used for the Y offset while the X value is animated. For Rotation, we'll have a triangle rotate in the middle of the screen around the Y axis. As mentioned before, triangles viewed from behind are normally culled. Since our triangle will be showing its back to the camera half of the time we probably want to prevent it from being culled. In our init_scene function we add a call to SetRenderState to turn culling off. If you comment this line out then the triangle will disappear when its back is facing the camera. Matrix Multiplication is shown by combining a Rotation Matrix with a Translation Matrix. This triangle moves back and forth across the bottom of the window while spinning on the Z-axis. As mentioned above, the order that you multiply these matrices is important. First we rotate the triangle, then we translate it. Try changing the order that they're multiplied to see what happens. Even though the same triangle model is used, we can draw it in difference locations and orientations just by setting a different World Matrix. Remember, the World Matrix specifies how the vertices are to be transformed but do not change the vertices themselves. The only way the vertex data itself will be changed is if you Lock the vertex buffer and edited the values. Wrapping Up Though we covered a lot of ground, none of it was too complex. It may take a little while for some of the ideas to really sink in, but if you experiment with the code for a bit you should have no problem at all. Lesson Downloads Source and Executable for C++ (126K) Source for C++ (11K) To compile this lesson you will also need the Drunken Hyena Common Code MSDN Links For Functions/Concepts Discussed Here Back