Tutorial 6: Using Meshes

Complicated geometry is usually modeled using 3-D modeling software and saved to a file. An example of this is the .x file format. Microsoft Direct3D uses meshes to load the objects from these files. The Meshes tutorial project introduces the topic of meshes and shows how to load, render, and unload a mesh.

A mesh contains the data for a complex model. It is an abstract data container that contains resources such as textures and materials, and attributes such as position data and adjacency data. Although meshes are somewhat complicated, Direct3D contains methods that make using meshes easier.

Path

Source location: (SDK root)\Samples\Managed\Direct3D\Tutorials\Tutorial6

Procedure

Note: For information about initializing Direct3D, handling Microsoft Windows messages, rendering, or shutting down, see Tutorial 1: Creating a Device.

Tutorial 5: Using Texture Maps created texture on the Direct3D object. This tutorial adds to the Tutorial 5 code with procedures for handling a mesh from a file, but it drops the OnCreateDevice application-defined method and related device creation calls that were used to create a vertex buffer. The procedure used here handles vertex buffers implicitly within mesh method calls.

Initializing a Mesh Object

This tutorial project initializes multidimensional material and texture arrays as follows. Note also the using declarations of System.ComponentModelLeave Site and System.IOLeave Site namespaces.

          [C#]
          using System;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Direct3D = Microsoft.DirectX.Direct3D;
public class Meshes : Form
{
    Device device = null;              // Rendering device
    Mesh mesh = null;                  // Mesh object in system memory
    Direct3D.Material[] meshMaterials; // Materials for the mesh
    Texture[] meshTextures;            // Textures for the mesh
    PresentParameters presentParams = new PresentParameters();
    .
    .
    .
}

The application-defined OnResetDevice method initializes an ExtendedMaterial object that is used to capture the mesh file data to a Material structure. The method sets the directory path to the mesh file, initializes a device, and turns on the z-buffer and white ambient lighting.

          [C#]
          public void OnResetDevice(object sender, EventArgs e)
{
    ExtendedMaterial[] materials = null;
    
    // Set the directory up to load the right data, because the
    // default build location is Bin\debug or Bin\release.
    Directory.SetCurrentDirectory(Application.StartupPath +  @"\..\..\");
    
    Device dev = (Device)sender;
    
    // Turn on the z-buffer.
    dev.RenderState.ZBufferEnable = true;
    
    // Turn on ambient lighting.
    dev.RenderState.Ambient = System.Drawing.Color.White;
    .
    .
    .
}

Loading a Mesh Object

As shown in the following code fragment, the OnResetDevice method next loads a mesh that represents a texture-mapped 3-D tiger from the tiger.x file. In this Mesh.FromFile method call, the SystemMemory constant of the MeshFlags enumeration indicates that the mesh is to be loaded into system RAM not typically accessible by the device. After the mesh is loaded, a meshMaterials Material object has its members filled with the Material structures of the materials ExtendedMaterial object that came from the mesh file. The material is also set to the ambient color. Finally, a meshTextures Texture object is loaded with the texture the file with a call to the TextureLoader.FromFile method. Both meshMaterials and meshTextures are initialized to the dimension (LengthLeave Site) of the materials structure from the file.

          [C#]
          public void OnResetDevice(object sender, EventArgs e)
{
    .
    .
    .
    // Load the mesh from the specified file.
    mesh = Mesh.FromFile("tiger.x",
                         MeshFlags.SystemMemory,
                         device,
                         out materials);
    if (meshTextures == null)
    {
        // Extract the material properties and texture names.
        meshTextures  = new Texture[materials.Length];
        meshMaterials = new Direct3D.Material[materials.Length];
        for( int i=0; i<materials.Length; i++ )
        {
            meshMaterials[i] = materials[i].Material3D;
            
            // Set the ambient color for the material. Direct3D
            // does not do this by default.
            meshMaterials[i].Ambient = meshMaterials[i].Diffuse;
            
            // Create the texture.
            meshTextures[i] = TextureLoader.FromFile(dev,
                                       materials[i].TextureFilename);
        }
    }
}

Rendering a Mesh Object

After the mesh has been loaded, the private Render method is called to render the mesh object. It first begins the scene and calls the SetupMatrices application-defined method, as was done in Tutorial 5. To render the mesh, it is divided into subsets, one for each material that was loaded. As shown in the following code fragment, a loop is run to render each material subset. The loop does the following for each material:

  • The Material property of the device is set to the meshMaterials Material structure.
  • The device texture stage 0 is set to the meshTextures Texture structure.
  • The material subset is drawn with the DrawSubset method, which inherits from the BaseMesh class.
          [C#]
          private void Render()
{
    .
    .
    .
    for( int i=0; i<meshMaterials.Length; i++ )
    {
        // Set the material and texture for this subset.
        device.Material = meshMaterials[i];
        device.SetTexture(0, meshTextures[i]);
        
        // Draw the mesh subset.
        mesh.DrawSubset(i);
    }
    
    // End the scene.
    device.EndScene();
    device.Present();
}