This is a class hierarchy for drawing simple meshes with support for loading models from Visual Studio 3D Starter Kit .CMO files, legacy DirectX SDK .SDKMESH files, and .VBO files. It is an implementation of a mesh renderer similar to
the XNA Game Studio Model,
NOTE: Support for loading keyframe animations is not yet included.
A Model consists of one or more ModelMesh instances. The ModelMesh instances can be shared by multiple instances of Model. A ModelMesh instance consists of one or more ModelMeshPart instances.
Each ModelMeshPart references an index buffer, a vertex buffer, an input layout, an
instance, and includes various metadata for drawing the geometry. Each ModelMeshPart represents a single material to be drawn at the same time (i.e. a submesh).
Rendering a model
Model instances can be loaded from either .CMO, .SDKMESH, or .VBO files, or from custom file formats. The Model loaders take an
instance to facilitate the sharing of
and textures between models. The default EffectFactory always returns built-in BasicEffect, SkinnedEffect, or DualTextureEffect instances. The DGSLEffectFactory can be used with .CMO files to load Visual Studio Shader Designer (DGSL) shaders through
Visual Studio 2012 and 2013 include a built-in content pipeline that can generate .CMO files from an Autodesk FBX, as well as DDS texture files from various bitmap image formats, as part of the build process. See the Visual Studio 3D Starter Kit for details.
for Windows 8.1,
for Windows 8.0.
DGSLEffectFactory fx( device );
// Can also use EffectFactory, but will ignore pixel shader material settings
auto teapot = Model::CreateFromCMO( device, L"teapot.cmo", fx );
For exception safety, the loaders return a std::unique_ptr.
The legacy DirectX SDK has an exporter that will generate .SDKMESH files from an Autodesk FBX. The latest version of this exporter tool can be obtained from
EffectFactory fx( device );
// Can also use DGSLEffectFactory, but will always use default materials
auto tiny = Model::CreateFromSDKMESH( device, L"tiny.sdkmesh", fx );
The VBO file format is a very simple geometry format containing a index buffer and a vertex buffer. It was originally introduced in the Windows 8.0 ResourceLoading sample, but can be generated by DirectXMesh's meshconvert utility
auto ship = Model::CreateFromVBO( device, L"ship.vbo" );
A Model instance also contains a name (a wide-character string) for tracking and application logic. Model can be copied to create a new Model instance which will have shared references to the same set of ModelMesh instances (i.e. a 'shallow' copy).
The Model::Draw functions provides a high-level, easy to use method for drawing models.
XMMATRIX local = XMMatrixTranslation( 1.f, 1.f, 1.f );
local = XMMatrixMultiply( world, local );
tiny->Draw( context, states, local, view, projection );
There are optional parameters for rendering in wireframe and to provide a custom state override callback.
Rather than using the standard Model::Draw, the ModelMesh::Draw method can be used on each mesh in turn listed in the Model::meshes collection. ModelMesh::Draw can be used to draw all the opaque parts or the alpha parts individually. The ModelMesh::PrepareForRendering
method can be used as a helper to setup common render state, or the developer can set up the state directly before calling ModelMesh::Draw. See
for an example.
ModelMesh::PrepareForRendering sets the blend state, depth stencil state, raster state, and sets a pixel shader sampler.
More detailed control over rendering can be had by skipping the use of Model::Draw and ModelMesh::Draw in favor of the ModelMeshPart::Draw method. Each Model::meshes collection can be scanned for each ModelMesh::meshParts collection to enumerate all ModelMeshPart
instances. For this version of draw, the ModelMeshPart::effect and ModelMeshPart::inputLayout can be used, or a custom effect override can be used instead (be sure to create the appropriate matching input layout for the custom effect beforehand using ModelMeshPart::CreateInputLayout).
for an example.
The Model loaders create an appropriate Effects instance for each ModelMeshPart in a mesh. Generally all effects in a mesh should use the same lighting and fog settings, which is facilitated by the Model::UpdateEffects method. This calls back for each unique
effect in the ModelMesh once.
auto lights = dynamic_cast<IEffectLights*>(effect);
if ( lights )
XMVECTOR dir = XMVector3Rotate( g_XMOne, quat );
lights->SetLightDirection( 0, dir );
auto fog = dynamic_cast<IEffectFog*>(effect);
if ( fog )
fog->SetFogStart(6); // assuming RH coordiantes
It is also possible to change the Effects instance used by a given part (such as when overriding the default effect put in place from a Model loader) by calling ModelMeshPart::ModifyEffect. This will regenerate the ModelMeshPart::inputLayout appropriately.
Be sure to call Model::Modified on all Model instances that reference the impacted ModelMesh instance to ensure the cache used by UpdateEffects is correctly updated. Model::Modified should also be called whenever a Model::meshes or ModelMesh::meshParts collection
As noted above, it is also possible to render part or all of a model using a custom effect as an override, rather than changing the effect referenced by the ModelMeshPart::effect directly. See
for an example.
Proper drawing of alpha-blended models can be a complicated procedure. Each ModelMeshPart has a bool value to indicate if the associated part is fully opaque (isAlpha is false), or has some level of alpha transparency (isAlpha is true). The Model::Draw routine
handles some basic logic for the rendering, first rendering the opaque parts, then rendering the alpha parts. More detailed control is provided by the ModelMesh::Draw method which can be used to draw all opaque parts of all meshes first, then go back and draw
all alpha parts of all meshes second. See
for an example.
To indicate the use of ‘straight’ alpha vs. ‘premultiplied’ alpha blending modes, ModelMesh::pmalpha is set by the various loaders functions controlled by a default parameter (which defaults false to indicate the texture files are using 'straight' alpha).
If you make use of DirectXTex's texconv tool with the -pmalpha switch, you should use pmalpha=true instead.
Custom render states
All the various Draw method provide a setCustomState callback which can be used to change the state just before the geometry is drawn.
tiny->Draw( context, states, local, view, projection, false, [&]()
ID3D11ShaderResourceView* srv = nullptr;
context->PSSetShaderResources( 0, 1, &srv );
Meshes are authored in a specific winding order, typically using the standard counter-clockwise winding common in graphics. The choice of viewing handedness (right-handed vs. left-handed coordinates) is largely a matter of preference and convenience, but it
impacts how the models are built and/or exported.
The Visual Studio 3D Starter Kit’s .CMO files assume the developer is using right-handed coordinates. DirectXTK’s default parameters assume you are using right-handed coordinates as well, so the ‘ccw’ parameter defaults to true. If using a .CMO with left-handed
coordinates, you should pass false for the ‘ccw’ parameter which will use clockwise winding instead. This makes the geometry visible, but could make textures on the model appear ‘flipped’ in U.
// When using LH coordinates
auto teapot = Model::CreateFromCMO( device, L"teapot.cmo", fx, false );
The legacy DirectX SDK’s .SDKMESH files assume the developer is using left-handed coordinates. DirectXTK’s default parameters assume you are using right-handed coordinates, so the ‘ccw’ parameter defaults to false which will use clockwise winding and potentially
have the ‘flipped in U’ texture problem. If using a .SDKMESH with left-handed coordinates, you should pass true for the ‘ccw’ parameter.
// When using LH coordinates
auto tiny = Model::CreateFromSDKMESH( device, L"tiny.sdkmesh", fx, true );
Feature Level Notes
If any ModelMeshPart makes use of 32-bit indices (i.e. ModelMeshPart:: indexFormat equals DXGI_FORMAT_R32_UINT) rather than 16-bit indices (DXGI_FORMAT_R16_UINT), then that model requires Feature Level 9.2 or greater.
If any ModelMeshPart uses adjacency (i.e. ModelMeshPart::primitiveType equals D3D_PRIMITIVE_TOPOLOGY_*_ADJ), then that model requires Feature Level 10.0 or greater. If using tessellation (i.e. D3D_PRIMITIVE_TOPOLOGY_?_CONTROL_POINT_PATCHLIST), then that model
requires Feature Level 11.0 or greater.
Keep in mind that there are maximum primitive count limits per ModelMeshPart based on feature level as well (65535 for Feature Level 9.1, 1048575 or Feature Level 9.2 and 9.3, and 4294967295 for Feature Level 10.0 or greater).
for more Feature Level notes.
The SDKMESH exporter http://go.microsoft.com/fwlink/?LinkId=226208
uses Autodesk FBX 2013.3.1 or later.
The VS 2012 and 2013 CMO exporter uses Autodesk FBX 2013.1. Recommended settings for exporting an FBX as a CMO include:
- Geometry: Smoothing Groups, TurboSmooth, Convert Deforming Dummies to Bones, Preserve edge orientation
- Animation: Bake Animation (Start=0, End=100, Step=1), Deformations, Skins, Morphs
- Units: Automatic
- Axis conversion: Y-up
- FBX File Format: Binary, FBX 2013
A VBO file does not contain any material or attribute information. The loader will create a default untextured BasicEffect when loading the model, or you can provide your own instance to use:
auto effect = std::make_shared<EnvironmentMapEffect>(device);
auto ship = Model::CreateFromVBO( device, L"ship.vbo", effect );
The ModelMeshPart is tied to a device, but not a device context. This means that Model creation/loading is ‘free threaded’. Drawing can be done on the immediate context or by a deferred context, but keep in mind device contexts are not ‘free threaded’. See
for some additional notes.