Loading Meshes
The first of the mesh−related helper functions is load_mesh. Actually there
are three versions of the load_mesh function. The first version is used to load
a mesh from an .X file using the D3DXLoadMeshFromX function. That means all
meshes contained within the .X file are compressed into a single mesh object,
which is subsequently stored in a D3DXMESHCONTAINER_EX object.
All iterations of the load_mesh function contain pointers to a valid 3D
device object, the directory path to where your meshes' textures are stored, and
the mesh loading flags and optional flexible vertex format that the load_mesh
functions use to clone the meshes after loading. That means you can force your
loaded meshes to use specific vertex formats!
Here's the prototype of the first load_mesh function:
HRESULT load_mesh(D3DXMESHCONTAINER_EX**
ret_mesh_container,
IDirect3DDevice9* device,
const char* filename,
const char* texture_path,
DWORD new_fvf,
DWORD load_flags);
The first load_mesh function takes a pointer to a D3DXMESHCONTAINER_EX object
pointer that you want to use for storing the loaded mesh data. Notice I said
pointer to a pointer. The load_mesh function will allocate the appropriate
objects for you and store the pointers in the pointer you pass to load_mesh.
This is similar to the way the InitD3D function stores the Direct3D and 3D
device object pointers.
Also, you must pass a valid 3D device object to the load_mesh function (as
the device pointer)−you use this device object to
create the mesh container and texture buffers. The mesh that you want to load is
specified as filename, and the directory in which
your textures are located is specified in texture_path.
This texture directory path is prefixed to any texture file names as they are
loaded.
Finally, there are new_fvf and
load_flags. You use the
new_fvf parameter to force the mesh being loaded to use a specific FVF.
For instance, if you only wanted to use 3D coordinates and normals, then you
would set new_fvf to (D3DFVF_XYZ|D3DFVF_NORMAL).
The load_mesh function will use CloneMeshFVF to clone the mesh using the
specific FVF you specified.
The load_flags parameter is used to set the mesh
loading flags as specified by the D3DXLoadMeshFromX function in the DX SDK
documents. The default value for this parameter is D3DXMESH_SYSTEMMEM, meaning
that the mesh is loaded into system memory (as opposed to hardware memory).
Here is implement of load_mesh function:
HRESULT load_mesh(D3DXMESHCONTAINER_EX** ret_mesh_container,
IDirect3DDevice9* device,
const char* filename,
const char* texture_path,
DWORD new_fvf,
DWORD load_flags)
{
// error checking
if(ret_mesh_container == NULL || device == NULL || filename == NULL || texture_path == NULL)
return E_FAIL;
// use system memory if converting FVF
if(new_fvf)
load_flags = D3DXMESH_SYSTEMMEM;
// load the mesh using D3DX routines
ID3DXBuffer* material_buffer;
ID3DXBuffer* adj_buffer;
DWORD num_materials;
ID3DXMesh* mesh;
HRESULT hr = D3DXLoadMeshFromX(filename, load_flags, device, &adj_buffer, &material_buffer, NULL,
&num_materials, &mesh);
if(FAILED(hr))
return hr;
// convert to new FVF first as needed
if(new_fvf)
{
ID3DXMesh* clone_mesh;
hr = mesh->CloneMeshFVF(load_flags, new_fvf, device, &clone_mesh);
if(FAILED(hr))
{
release_com(adj_buffer);
release_com(material_buffer);
release_com(mesh);
return hr;
}
// free prior mesh and store new pointer
release_com(mesh);
mesh = clone_mesh; clone_mesh = NULL;
}
D3DXMESHCONTAINER_EX* mesh_container = new D3DXMESHCONTAINER_EX;
*ret_mesh_container = mesh_container;
// store mesh name (filename), type, and mesh pointer.
mesh_container->Name = strdup(filename);
mesh_container->MeshData.Type = D3DXMESHTYPE_MESH;
mesh_container->MeshData.pMesh = mesh;
mesh = NULL;
// store adjacency information
DWORD adj_buffer_size = adj_buffer->GetBufferSize();
if(adj_buffer_size)
{
mesh_container->pAdjacency = new DWORD[adj_buffer_size];
memcpy(mesh_container->pAdjacency, adj_buffer->GetBufferPointer(), adj_buffer_size);
}
release_com(adj_buffer);
// build material list
mesh_container->NumMaterials = num_materials;
if(num_materials == 0)
{
// create a default material
mesh_container->NumMaterials = 1;
mesh_container->pMaterials = new D3DXMATERIAL[1];
mesh_container->textures = new IDirect3DTexture9*[1];
ZeroMemory(mesh_container->pMaterials, sizeof(D3DXMATERIAL));
mesh_container->pMaterials[0].MatD3D.Diffuse.r = 1.0f;
mesh_container->pMaterials[0].MatD3D.Diffuse.g = 1.0f;
mesh_container->pMaterials[0].MatD3D.Diffuse.b = 1.0f;
mesh_container->pMaterials[0].MatD3D.Diffuse.a = 1.0f;
mesh_container->pMaterials[0].MatD3D.Ambient = mesh_container->pMaterials[0].MatD3D.Diffuse;
mesh_container->pMaterials[0].MatD3D.Specular = mesh_container->pMaterials[0].MatD3D.Diffuse;
mesh_container->pMaterials[0].pTextureFilename = NULL;
mesh_container->textures[0] = NULL;
}
else
{
// load the materials
D3DXMATERIAL* xmaterials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();
mesh_container->pMaterials = new D3DXMATERIAL[mesh_container->NumMaterials];
mesh_container->textures = new IDirect3DTexture9*[mesh_container->NumMaterials];
for(DWORD i = 0; i < mesh_container->NumMaterials; i++)
{
mesh_container->pMaterials[i].MatD3D = xmaterials[i].MatD3D;
mesh_container->pMaterials[i].MatD3D.Ambient = mesh_container->pMaterials[i].MatD3D.Diffuse;
mesh_container->textures[i] = NULL;
// load the texture if one exists
if(xmaterials[i].pTextureFilename)
{
char texture_file[MAX_PATH];
sprintf(texture_file, "%s%s", texture_path, xmaterials[i].pTextureFilename);
D3DXCreateTextureFromFile(device, texture_file, &mesh_container->textures[i]);
}
}
}
release_com(material_buffer);
mesh_container->MeshData.pMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT, NULL, NULL, NULL, NULL);
mesh_container = NULL;
return S_OK;
}
And that's it for the first load_mesh function! Let's check out how to use it.
Suppose you want to load a mesh (from a file called Mesh.x) using the load_mesh
function just shown. To demonstrate the ability to specify a new FVF, specify
that you want to use XYZ components, normals, and texture coordinates for your
mesh. Also, suppose your textures are in a subdirectory called \textures. As for
the mesh loading flags, leave those alone to allow the mesh to load into system
memory (as per the default flag shown in the prototype). Here's the code:
// Instance the mesh object
D3DXMESHCONTAINER_EX *Mesh = NULL;
// Load a mesh − notice the pointer to the mesh object
load_mesh(&Mesh, pD3DDevice, "Mesh.x", "..\\Textures\\",
(D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1));
Once the mesh has been loaded, you can access the mesh object via the
Mesh−>MeshData.pMesh object pointer. Also, material data is stored in
Mesh−>pMaterials, and texture data is stored in Mesh−>textures. The number of
materials a mesh uses is stored in Mesh−>>NumMaterials. To render a loaded mesh,
you can use the following code:
// pMesh = pointer to D3DXMESHCONTAINER_EX object
// Go through all material subsets
for(DWORD i=0;i<pMesh−>NumMaterials;i++) {
// Set material and texture
pD3DDevice−>SetMaterial(&pMesh−>pMaterials[i].MatD3D);
pD3DDevice−>SetTexture(0, pMesh−>pTextures[i]);
// Draw the mesh subset
pDrawMesh−>DrawSubset(i);
}