DirectX build-in primitives: drawing the box
In this tutorial we are going to draw a box in a wireframe mode.
The source code that comes with this tutorial (see Downloads section above) extends the previous solution.
To see all the tutorials that belong to this project click on the project name above.
After creating a DirectX device in the previous tutorial we are now ready to use it to draw geometry to build our 3D world. In this project we limit ourselves to the basic geometry like the box on a picture above.
If you run the executable file provided with this tutorial (see downloads section above) you will notice that resizing the window will also resize the entire 3D world in the same ratio like in the picture bellow:
In some cases you might want to avoid this distortion.
In the
Box navigation, viewport and HUD tutorial we discuss how can you achieve that by using a DirectX viewport.
Before we can proceed it is important to realize the following:
Each geometrical shape we are going to draw with DirectX has a so called mesh and a matrix associated with it.
Mesh is a geometrical definition of an object (for example triangle has 3 vertices).
Matrix represents a physical position of the object in our 3D world.
So if we would like to place our object in a certain location or move it to an other location we actually perform a matrix transformation (a very well known phenomenon from the Linear Algebra). The result of a matrix transformation will be repositioning each vertex of an object.
Perhaps you have already noticed that each shape in DirectX world is build from triangles. In other words triangle is used as a building block to form all the other shapes inclusive spheres. Since we are going to use a whireframe mode to render our box in this example, you can clearly see the triangles that make up the box object.
Now imagine that you have to define position of each vertex to form a box. That would be a lot of work, isn't it? In order to avoid this effort we are going to use the build-in DirectX functions that can draw simple primitives. With a growing complexity of objects that can include hundreds of vertices it would no longer be feasible to build the mesh of each object and calculate each vertex position by hand. In a more advanced project in order to build a realistic 3D environment you will be using a 3D editor (like Maya or 3DMax for example) to build each object and then export its mesh definition to a format that DirectX can 'understand'. Fortunately for us this process isn't very different from the way we are going to work with our box in this example. Just remember that the mesh of an object has always to be defined, and that you can obtain it from different sources. Since mesh is a DirectX object once you have it, you can do with it whatever you like, no matter what geometrical shape it represents.
After this little theory discussion we can look at the source code. We begin with a definition of a Boolean isWireframe global variable. Then we define a mesh to hold the vertices of the box we are going to draw.
bool isWireframe = true;
LPD3DXMESH meshBox;
Inside the initCamera() method we slightly modify the initial position of our camera:
D3DXVECTOR3 eye(0, 5, -10);
We move the camera 5 units up and closer to the target then in the previous tutorial. This will give as a more perspective view of our world.
The following code does check if we have to render in a wireframe mode and does modify the device's rendering state accordingly.
if(isWireframe)
directXDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
else
directXDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
Inside the initScene() method we add the following:
D3DXCreateBox(
directXDevice,
2.0f, // width
2.0f, // height
2.0f, // depth
&meshBox,
0);
You can read this statement as loading the mesh of a box while box vertices are predefined (box is a build-in primitive in DirectX environment). Notice that we could build our box with some 3D editor and export its mesh and then load it here by using a different DirectX function. This is something we are going to do in a more advanced projects. The D3DXCreateBox method also allows us to define the width, height and depth of a box. The mesh of a box is returned into the meshBox global variable we have defined before.
DirectX has a number of build-in primitives you can use in the similar matter like creating a box. Some of them are listed bellow (for the entire list, please advise the DirectX SDK documentation):
D3DXCreateCylinder()
D3DXCreateLine()
D3DXCreateFont()
D3DXCreatePolygon()
D3DXCreateSphere()
The drawScene() method is a place where we are going to draw all our geometry. Normally we would make a separate method to draw each object and then call it from drawScene() method. Since we only want to draw a box in this example we will write all the code between the BeginScene() and EndScene() methods.
Here is how it looks like:
D3DXMATRIX matrix;
D3DXMatrixTranslation(&matrix, 0.0f, 0.0f, 0.0f);
directXDevice->SetTransform(D3DTS_WORLD, &matrix);
// draw the object using the previously created world matrix.
meshBox->DrawSubset(0); // box has only 1 subset (AttribId = 0)
First we define the matrix. We want our box to appear in the center of the world. That's why we set the coordinates to 0. we then translate the matrix with SetTransform() method which takes D3DTS_WORLD constant as a parameter and returns the transformed matrix into the matrix variable defined before.
We then draw our object by calling the DrawSubset(0) method of the meshBox. We provide 0 as a parameter because our box has only 1 subset to draw.
It is always important to clean the memory from objects when the time is right. In this example we perform clean-up when we exit the application. That means that we can use the cleanup() method and add there the following code to release meshBox from the memory:
if(meshBox){
meshBox->Release();
meshBox = NULL;
}
In the
next tutorial we are going to animate this box and let it move up and down on the screen.