DirectXTutorial.com
The Ultimate DirectX Tutorial
Lesson 3:  Loading Textured Meshes
Log In
Lesson Overview

It's difficult to make a spectacular game without textures.  Fortunately, meshes include the ability to have textures attached to the .x file, so you don't have to manually add each texture.

Unfortunately, however, you do need to add some code of your own, and this lesson will cover how to do that.

Loading and Displaying Textures

In this lesson, we are going to use the model "airplane 2.x".  This airplane, if run using the code from the previous lesson, will show something like this:

Image 3.1 - The Airplane 2, Untextured

Image 3.1 - The Airplane 2, Untextured

However, if you view this particular mesh using the mesh viewer provided with DirectX, you will find that it actually looks quite different.  It looks like this:

Image 3.2 - The Airplane 2, Textured

Image 3.2 - The Airplane 2, Textured

This means that our code, so far, does not actually show textures when they are available.  For this we will need to enter additional code.

There are two simple steps to adding this code:

1.  Load the textures while loading the mesh.
2.  Set the subset's texture before each subset is drawn.

Let's look into each of these in detail.


1.  Load the textures while loading the mesh.

To load a texture, what we first need to do is get the filename of the texture.  This is loaded when the mesh is loaded, and stuck away in the material buffer.  Our task will be to extract it, read it, and load the texture.

Here is the code that handles the material information once the mesh is loaded.  Added in and in bold is the new code that handles the texture.

D3DMATERIAL9* materal;    // a pointer to a material buffer
LPDIRECT3DTEXTURE9* texture;    // a pointer to a texture

// retrieve the pointer to the buffer containing the material information
D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufShipMaterial->GetBufferPointer();

// create a new material buffer and texture for each material in the mesh
material = new D3DMATERIAL9[numMaterials];
texture = new LPDIRECT3DTEXTURE9[numMaterials];

for(DWORD i = 0; i < numMaterials; i++)    // for each material...
{
    material[i] = tempMaterials[i].MatD3D;    // get the material info...
    material[i].Ambient = material[i].Diffuse;    // and make ambient the same as diffuse
    USES_CONVERSION;    // allows certain string conversions
    // if there is a texture to load, load it
    if(FAILED(D3DXCreateTextureFromFile(d3ddev,
                                        CA2W(tempMaterials[i].pTextureFilename),
                                        &texture[i])))
        texture[i] = NULL;    // if there is no texture, set the texture to NULL

}

Well, about half of this is devoted to texture loading and the other half to string management, but I will go over it all at the same time.

LPDIRECT3DTEXTURE9* texture;

This the pointer to the textures, as we will see in the next command.  It is placed in the beginning of your program, along with the *material declaration, so it can be global.

texture = new LPDIRECT3DTEXTURE9[numMaterials];

Each material has its own buffer, if it has any at all.  If you look at the Airplane 2, you will find that there are a few subsets with no textures, but we'll get to that later.  Whether or not the subset has a texture, we will set aside a slot in this texture array.  This line just creates a blank texture for each subset.

USES_CONVERSION;

This is a macro that enables us to use certain string conversions.  In the next line, we will need to convert a LPSTR to an LPCWSTR.  This macro enables the macro we will use to do this.

Note that using this macro requires a new header file called "atlbase.h".  We will add this in the finished program for this lesson.

if(FAILED(...

This checks whether or not a texture name was available for the subset.  As mentioned, not all subsets have textures.  If there is no texture, there will be no filename.  If this happens, the function D3DXCreateTextureFromFile() will return a failure.  If this is the case, we go on to the next line,  texture[i] = NULL;  in which case we will be able to detect it later.  More on this in the next step.

D3DXCreateTextureFromFile()

We've seen this one before.  It simply loads the texture from the file whose name is provided.

The first parameter is simply the Direct3D Device, d3ddev.

The second parameter is the filename.  The filename for the appropriate subset is located in tempMaterials[i].pTextureFilename.  However, this filename is in an LPSTR format.  In Visual C++ 2005, this string needs to be in an LPCWSTR format.  We can't unfortunately, cast it normally.  We will have to use a macro called CA2W to convert it.  Therefore we get the line for this parameter as:

CA2W(tempMaterials[i].pTextureFilename),

This gets the string holding the filename of the texture, converts it to a wide string, and uses it to load the texture.

The third parameter simply holds the texture pointer in which to place the texture.

texture[i] = NULL;

This last line is only run if there is no texture, in which case we simply initialize the texture to NULL.  We'll see why this is important in the next step.


2.  Set the subset's texture before each subset is drawn.

The code for setting the texture should be quite familiar to you, and is nowhere near as tedious as loading it.  Here is the code for displaying the model, with the texture code in bold.

// draw the spaceship
for(DWORD i = 0; i < numMaterials; i++)    // loop through each subset
{
    d3ddev->SetMaterial(&material[i]);    // set the material for the subset
    if(texture[i] != NULL)    // if the subset has a texture (if texture is not NULL)
        d3ddev->SetTexture(0, texture[i]);    // ...then set the texture


    meshSpaceship->DrawSubset(i);    // draw the subset
}

This part is simple.  All we do is check to see if there is a texture.  If there was a texture, we set it to NULL (back in step 1).  If it isn't NULL, then we go on and set the texture.  I'm not going to go over each line because there are two simple lines and we've gone over them before.

The Finished Program

That's all there is to loading textured meshes.  Now let's take a look at the whole program.

This program uses the 3D model "airplane 2.x", which must be accompanied by the two texture files "bihull.bmp" and "wings.bmp".

[Show Code]

If you run this, you should get something like this:

Image 2.3 - The Spaceship In Action

Image 2.3 - The Biplane In Action
Summary

All Right!  Now we can load up textured models and display them!  With a higher quality of 3D graphics under your belt, it's time to get to work.  Next, we'll go into animated models, as well as models created by your game (rather than an external 3D modeler).

While I build those lessons, do these exercises, and get very good using meshes.

1.  Find another textured model and load it.
2.  Get both the airplane and the other model on the screen at the same time.
3.  Make the propeller spin.
4.  Make your own texture to use on this model.

Next Tutorial:  Game Display

GO! GO! GO!