Nov 30 2017
Today's task is to load a "real" 3D model.
I downloaded a free model of a German Tiger tank and saved it in Tutorials\Models\Tank\Tiger\. The zipfile contained the model in various formats and the textures as a collection of BMP bitmaps. The ".obj" is a text file containing all the vertices, faces, and texture maps and is the lingua franca that is best supported by the tools.
DirectXMesh
is a project by Chuck Walbourn (the author of DirectXTK) to convert
OBJ files to binary-optimized CMO, VBO, or SDKMESH files that can
be loaded directly into a DirectXTK Model object. I cloned the
git repo into DirectX/DX11/DirectXMesh/:
git clone https://github.com/Microsoft/DirectXMesh.git .
The solution contains two projects, the DirectXMesh library and a
command line utility meshconvert. Both projects built (using the
DirectXMesh_Desktop_2015 target) without any problems. I copied
meshconvert.exe to a convenient location (DirectX/Bin/).
I converted the Tiger.obj file to Tiger.sdkmesh:
meshconvert -n -y Tiger.obj
The -n creates the vertex normals. -y overwrites any existing file.
I didn't want to spend much time, if any, writing new code for this
experiment so I leveraged the
RenderModel
example from the DirectXTK tutorials. I simply copied Tiger.sdkmesh
into the source directory with cup._obj and replaced "cup.cmo" with
"Tiger.sdkmesh" in the code.
//m_model= Model::CreateFromCMO(m_d3dDevice.Get(),L"cup.cmo",*m_fxFactory);
m_model= Model::CreateFromSDKMESH(m_d3dDevice.Get(),L"Tiger.sdkmesh",*m_fxFactory);
A quick scan of the original Tiger.obj file let me know that the
vertices were generally in the range of +/- 2000. I updated the camera
location and projection values to move the camera outside the model
and make sure the model was within the clipping planes. The model is
centered on (0,0,0).
// TODO: Initialize windows-size dependent objects here.
m_view= Matrix::CreateLookAt(Vector3(2500.0f,2500.0f,2500.0f),Vector3::Zero,Vector3::UnitY);
m_proj= Matrix::CreatePerspectiveFieldOfView(XM_PI/4.0f,float(backBufferWidth)/float(backBufferHeight),100.0f,5000.0f);
I recompiled the project, ran it, and saw nothing but cauliflower blue. :(
There were no error messages or warnings, and the program was happily rendering 60 frames per second. The number of things that could be wrong are legion:
I spent quite a while playing around with the camera position and FoV parameters without any hint. I removed the textures from the problem by setting the wireframe flag in the model->Draw() call.
Then I remembered the problems I had turning on the lights (Direct3D Lighting). The model simply disappeared when I tried to enable lights without setting the input layout correctly. I commented out the code that enabled the lighting and fog effects and was rewarded with a spinning wireframe tank! It even rendered as a solid (with no texture)!
Comparing the Tiger.obj file to the simple Cup._obj file from the RenderModel example makes me think the Tiger model is not complete. The textures are present but not linked into the model. The cup has a "mtllib" line that references the "cup.mtl" file, which contains a reference to the image file "cup.jpg". The Tiger file has no mtllib line and the texture file names do not appear anywhere in the file. This makes me think I need to use some external CAD program (Blender? Alibre?) to attach one of the skins to the model.
Braynzarsoft.net has a tutorial explaining how to read an OBJ model directly.
Rather than diving into the gory details of writing yet another OBJ parser or learning how to use a CAD program, I am trying another model. I can buy a small model with the assumption that a commercially produced model will be more complete, or I can try another free one.
This firetruck model
compiled using meshconvert and crashed during load with
"EffectFactory could not find texture file 'Opengreen.dds'"
There is a file named "Opengreen.tga" in the model package.
I built texconv,
converted OpelGreen.tga to OpelGreen.dds
texconv OpenGreen.tga
and copied OpelGreen.dds to the RenderModel directory. Repeated this
for "aufbau_dl_rund.jpg", "kotfluegel_lf_rund.jpg", "reifen.tga",
"kasten_dl.jpg", "leiter_park.jpg", "kabine_dl_rund.jpg", "geraete.tga",
"details_dl_rund.tga". I was rewarded with a skinned but slightly
messed up firetruck.
This proves that I can read textured models. I need to run meshconvert on the OBJ to generate the SDKMESH (vertices, normals, faces, and texture references), texconv on each referenced texture image to generate the DDS, and copy the SDKMESH and DDS files into the working directory.
Using the dl_rund.obj firetruck as a guide, I revisted my original Tiger model and hacked the OBJ file (all source data files should be text!). First I created a new "Tiger_Snow.mtl" materials file, copying from dl_run.mtl. The fact that all the parts use the same values tells me the information is about color and not space. Reading the Braynzarsoft tutorial confirms this. "K" is color information, "Ka" is ambient color, "Kd" is diffuse color, "Ks" is specular color, and "Ns" is specular strength. The "map_Kd" is the reference to the diffused color texture map. The texture maps are stored separately from the mesh definition, so the path in the map_Kd line should be the relative path from the working directory when my program is running. I am putting everything in the same directory to make life simpler during these experiments.
I add the reference to the new material library to the top of the
Tiger.obj file:
mtllib Tiger_Snow.mtl
Then I search the OBJ file for all lines beginning with "usemtl" and
make sure I have a matching "newmtl" section in the library.
I rebuild the sdkmesh file:
meshconvert Tiger.obj
Convert all the referenced texture files:
texconv tex\MG_body.bmp
texconv tex\Tank_Body_Snow.bmp
texconv tex\Track.bmp
Note that the original BMP textures are in a tex subdirectory while
the converted DDS files are written to the current directory. I then
copy the mesh "Tiger.sdkmesh" and all the DDS files (*.DDS) to the
RenderModel directory.
I changed the model name back to "Tiger.sdkmesh" and now I have a fully realized Tiger!
Unfortunately, meshconvert does not preserve the path to the texture file and DirectXTK EffectFactory only looks in the current directory which forces me to put everything in the working directory. I will need to modify EffectFactory.cpp to let me specify a path to the DDS files.
This provides my "Known Good Reference" point to start exploring. I was able to enable the lighting model, it was the fog effect that was causing problems. This was because the original "cup" model was scaled to 1.0 while the Tiger is about 2000 units long. I moved the fog parameters out to SetFogStart(100.0f) and SetFogEnd(5000.0f) and the Tiger emerged.
Now I just need a model that works...
The prices at CGTrader change every time I look at the page.