Mesh Rendering

A mesh is a collection of data used to display a 3D model onscreen. A mesh contains all the vertices, indices, and texture details necessary for rendering the model, plus any additional details that may be used during rendering. Commonly used additional details include animation frames, subset meshes, and materials.

Developers frequently design unique Mesh classes that are tailor-made to meet the needs of individual projects. This reduces memory footprint and improves performance. Many 3D modeling programs include a tool to help you import models from their program into your unique Mesh format.

The following sections describe how to render meshes on the Xbox One dev kit.

Mesh contents

The contents of your mesh will vary depending on the needs of your project. The objects listed below are the most common data types for meshes to contain.

Vertex Buffer

A vertex is a point in 3D space. A vertex buffer is an array of vertices that define the shape of a model. If your mesh has animations, you will need a unique vertex buffer array for each frame of animation. To make rendering easier, each vertex buffer for a particular model should have an identical number of vertices, arranged in the same order. That way, your mesh will only need a single index buffer that can be used for every frame of animation.

Index Buffer

An index is a combination of three vertices that defines a polygon in 3D space. An index buffer is the series of polygons that makes up the 3D model to render.

Mesh Array

The mesh array is a struct containing the major details of the mesh, similar to a header. Mesh arrays facilitate easier communication between your meshes and the rest of your project. The mesh array is a useful place to store an identifier or name, so that you can easily find and render specific meshes.

C++

struct MeshArray
{
    CHAR    Name[ MAX_MESH_NAME ];
    BYTE    NumVertexBuffers;
    UINT    VertexBuffers[ MAX_VERTEX_STREAMS ];
    UINT    IndexBuffer;
    UINT    NumSubsets;
    UINT    NumFrameInfluences; //aka bones

    FLOAT   BoundingBoxCenter[ 3 ];
    FLOAT   BoundingBoxExtents[ 3 ];

    union
    {
        UINT64 SubsetOffset;    //Offset to list of subsets (This also forces the union to 64bits)
        UINT* pSubsets;        //Pointer to list of subsets
    };
    union
    {
        UINT64 FrameInfluenceOffset;  //Offset to list of frame influences (This also forces the union to 64bits)
        UINT* pFrameInfluences;      //Pointer to list of frame influences
    };
};  

Subsets

Meshes are commonly composed of multiple smaller meshes, known as Subsets. This is useful when you want to render an object with multiple material properties. For example, the body of a car may be shiny and reflective, while the tires have a duller, matte appearance. Subsets can be used for a variety of creative purposes, including advanced animation techniques, environmental destruction, and memory footprint reduction.

C++

struct Subset
{
    CHAR Name[ MAX_SUBSET_NAME ];
    UINT MaterialID;
    UINT PrimitiveType;
    UINT64 IndexStart;
    UINT64 IndexCount;
    UINT64 VertexStart;
    UINT64 VertexCount;
};  

Materials

The Material class defines rendering properties that apply to the current Mesh or Subset. Materials typically define lighting and texturing properties. This is where you define qualities of the mesh, such as whether the mesh is shiny or dull, transparent or opaque.

C++

struct Material
{
    CHAR    Name[ MAX_MATERIAL_NAME ];

    // Use MaterialInstancePath
    CHAR    MaterialInstancePath[ MAX_MATERIAL_PATH ];

    // Or fall back to d3d8-type materials
    CHAR    m_strDiffuseTexture[ MAX_TEXTURE_NAME ];
    CHAR    m_strNormalTexture[ MAX_TEXTURE_NAME ];
    CHAR    m_strSpecularTexture[ MAX_TEXTURE_NAME ];

    FLOAT   Diffuse[ 4 ];
    FLOAT   Ambient[ 4 ];
    FLOAT   Specular[ 4 ];
    FLOAT   Emissive[ 4 ];
    FLOAT   Power;

    union
    {
        UINT64 Force64_1;            //Force the union to 64bits
        ID3D11Texture2D*            m_pDiffuseTexture;
    };
    union
    {
        UINT64 Force64_2;            //Force the union to 64bits
        ID3D11Texture2D*            m_pNormalTexture;
    };
    union
    {
        UINT64 Force64_3;            //Force the union to 64bits
        ID3D11Texture2D*            m_pSpecularTexture;
    };

    union
    {
        UINT64 Force64_4;            //Force the union to 64bits
        ID3D11ShaderResourceView*    m_pDiffuseSRV;
    };
    union
    {
        UINT64 Force64_5;            //Force the union to 64bits
        ID3D11ShaderResourceView*    m_pNormalSRV;
    };
    union
    {
        UINT64 Force64_6;            //Force the union to 64bits
        ID3D11ShaderResourceView*    m_pSpecularSRV;
    };
};  

Frames

If your mesh is going to be animated, then you will need to define the separate frames of animation. This is typically done by having a unique array of vertices for each frame of animation. (If every frame has the same amount of vertices in the same order, you can use a shared set of indices for each frame). Animation frames are generally played in a sequence, so you may want to include pointers to the next/previous frames in the animation. Frames can apply to the entire mesh, or you can apply animations to specific subsets.

C++

struct Frame
{
    CHAR Name[ MAX_FRAME_NAME ];
    UINT Mesh;
    UINT ParentFrame;
    UINT ChildFrame;
    UINT SiblingFrame;
    XMFLOAT4X4 Matrix;
    UINT AnimationDataIndex;        //Used to index which set of keyframes transforms this frame
};  

Loading mesh data

Mesh data is generally loaded from a file. You will need to choose a file format that works best for your project. (Or, more likely, create a unique file format based on your implementation.) All of the data for your Mesh must be contained in this file, including vertices, indices, frames of animation, and material properties. To be parsed properly, the information must be well-arranged.

Due to the lengthy and implementation-specific nature of Mesh data file I/O, we will not provide an example of a loading function here. For a real-world implementation of Mesh file I/O, see the RenderMesh_110 sample available in the XDK samples on XGD. In particular, see the files Mesh.h and Mesh.cpp.

Note The RenderMesh sample loads Meshes using the .sdkmesh format. The .sdkmesh format was designed specifically to be used in SDK samples, and therefore is not an ideal format for a shipping title. You should avoid this format in favor of a destination format that meets the specific needs of your application.

Rendering mesh data

Meshes are rendered in the same manner as any other 3D object. You will need a pointer to the local Device Context (ID3D11DeviceContext*). Fill the device context’s vertex and index buffers with relevant data from the mesh, set the primitive topology, and set any shader resources as necessary. If your Mesh class utilizes animation frames or subset meshes, you will need to account for that in your Mesh class’s Render function.

Mesh class render function:

C++

_Use_decl_annotations_
VOID XSF::Mesh::RenderMesh( D3DDeviceContext* pCtx, UINT uMesh, const RenderingOptions& options )
{
    VERBOSEATGPROFILETHIS;

    using namespace Detail;

    MeshArray* pMesh = &m_pMeshArray[uMesh];

    UINT Strides[ D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT ];
    UINT Offsets[ D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT ];
    ID3D11Buffer* pVB[ D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT ];

    if( pMesh->NumVertexBuffers > D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT )
        return;

    for( UINT i = 0; i < pMesh->NumVertexBuffers; i++ )
    {
        pVB[ i ] = m_pVertexBufferArray[ pMesh->VertexBuffers[ i ] ].pVB;
        Strides[ i ] = ( UINT )m_pVertexBufferArray[ pMesh->VertexBuffers[ i ] ].StrideBytes;
        Offsets[ i ] = 0;
    }

    IBHeader* pIndexBufferArray = m_pIndexBufferArray;

    ID3D11Buffer* pIB = pIndexBufferArray[ pMesh->IndexBuffer ].pIB;
    DXGI_FORMAT ibFormat = ( pIndexBufferArray[ pMesh->IndexBuffer ].IndexType == IT_16BIT ) ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;

    pCtx->IASetVertexBuffers( 0, pMesh->NumVertexBuffers, pVB, Strides, Offsets );
    pCtx->IASetIndexBuffer( pIB, ibFormat, 0 );

    Subset* pSubset = nullptr;
    Material* pMat = nullptr;

    for( UINT subset = 0; subset < pMesh->NumSubsets; subset++ )
    {
        pSubset = &m_pSubsetArray[ pMesh->pSubsets[subset] ];

        pCtx->IASetPrimitiveTopology( options.enableTess ? D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST : ( D3D11_PRIMITIVE_TOPOLOGY )pSubset->PrimitiveType );

        pMat = &m_pMaterialArray[ pSubset->MaterialID ];
        if( options.uDiffuseSlot != INVALID_SAMPLER_SLOT && !IsErrorResource( pMat->m_pDiffuseSRV ) )
            pCtx->PSSetShaderResources( options.uDiffuseSlot, 1, &pMat->m_pDiffuseSRV );
        if( options.uNormalSlot != INVALID_SAMPLER_SLOT && !IsErrorResource( pMat->m_pNormalSRV ) )
            pCtx->PSSetShaderResources( options.uNormalSlot, 1, &pMat->m_pNormalSRV );
        if( options.uSpecularSlot != INVALID_SAMPLER_SLOT && !IsErrorResource( pMat->m_pSpecularSRV ) )
            pCtx->PSSetShaderResources( options.uSpecularSlot, 1, &pMat->m_pSpecularSRV );

        const UINT IndexCount = ( UINT )pSubset->IndexCount;
        const UINT IndexStart = ( UINT )pSubset->IndexStart;
        const UINT VertexStart = ( UINT )pSubset->VertexStart;

        pCtx->DrawIndexedInstanced( IndexCount, options.numInstances, IndexStart, VertexStart, 0 );
    }
}  

Main render function, calling mesh class render function:

C++

XSF::D3DDeviceContext* const pCtx = GetImmediateContext();

// meshes shaders and input layout and samplers
pCtx->VSSetShader( m_spVS, nullptr, 0 );
pCtx->PSSetShader( m_spPS, nullptr, 0 );
pCtx->IASetInputLayout( m_spIL );
pCtx->PSSetSamplers( 0, 1, m_spSS.GetAddressOf() );

//  Render the city
XSF_ERROR_IF_FAILED( XSF::ReplaceDynamicConstantBufferContents( pCtx, m_spCB, 64, &m_matViewProj ) );
pCtx->VSSetConstantBuffers( 0, 1, m_spCB.GetAddressOf() );

XSF::Mesh::RenderingOptions meshOptions( 0, 1, 2 );

m_meshColumn.RenderMesh(pCtx, 0, meshOptions);  

RenderMesh sample at XGD

For a working example of mesh rendering, download ‘RenderMesh’ from the XDK Samples on the Xbox Game Developer (XGD) site. For additional instructions, see Running the XDK Samples.

See also

DirectX