This article describes a graphics file format converter utility that may be useful to the community of DirectX developers.  The software and paper, were written by the author and instructor of the Managed DirectX course offered by DevelopMentor.

OBJ2DX

This is a simple utility written in C# that converts the Wavefront OBJ file format to the Microsoft DirectX .X file format. This software is freeware and placed in the public domain.

This converter was written to make it easy to import content from external modelers and animation programs into DirectX applications. It has both a GUI and operates in command line mode for batch conversion of lots of files.

To view the output, if the DirectX SDK is installed, double-click on the .X file. This will start the DxViewer program. If you don't see anything, click View, then Edges to force drawing of the polygons (see also the notes below).

OBJ is a very clear and reliable format that can even be hand edited. OBJ has no hierarchy (that is no matrices or cameras, no lights), and no animation. It is just an easy way to store geometry, in the form of polygons. The OBJ2DX program only accepts triangles, and creates one .X Mesh object from everything read in the OBJ file which makes it even simpler.

.X is a file format that is easily read into applications using the DirectX API, in a single API function call. The .X file can be quite complex, but this converter only stores the triangle mesh. Frames (matrices), animation curves, etc are not stored into the .X file output.

Here is an example character converted using OBJ2DX, containing about 2000 triangles.




Considerable file space saving can be obtained by sharing vertex position, normals, and texture UVs for adjacent triangles. But this depends on additional boundary representation information in the model. This converter takes the approach that nothing is shared, which is the most flexible, but also takes up the most space. For small models this isn't a problem.

For large files, the .X files can be optimized using a free utility (mview) from Microsoft that was packaged with the DirectX 9 December 2005 SDK.

When optimized using "partial weld" in Mview, the toy cat character size went from 763K to 235K in the text form .X file. The Mview utility can also output binary and compressed .X files; in this case the resulting size is 58K.

<>Mview also generates progressive meshes for automatic handling of various levels of detail in a DirectX application.

General Notes

Per vertex colors are not supported in either file format.

The OBJ file supports any combination of shared or unshared vertex position, normals, and texture UV coordinates using indexing. The .X file supports indexing for vertex position and normal vectors coordinates, but texture UVs are stored per vertex.

Hand editing the .X format can be tricky because the DirectX parser is very sensitive to line terminators (, and ;) and any errors are not reported by line number :o[ Also, vertex indices in the OBJ file are 1-indexed, but in the .X file they are 0-indexed.

Notes on OpenGL vs DirectX Conventions

This section discusses common problems encountered (and solutions) when converting models and textures from various tools into DirectX applications (such as DxViewer).  These problems and solutions provided
the motivation for some of the options in the program, as seen in the user-interface:



Have problems with inside-out or black models?

When using the standard DxViewer or Mview utilities, the DirectX clockwise winding or left-handed convention is used. This can cause models created with CCW/LH rule to appear inside out and/or black.

If your model is invisible in the DxViewer, try rotating 180 degrees around the Y axis, and also enable polygon edge drawing with the View button, show Edge toggle.

Models created for OpenGL usually have the convention that counter-clockwise (CCW) winding around the polygon perimeter defines an outside pointing normal vector. CCW winding is easy to remember since it is also a right-handed rule (thumb points along the normal vector and fingers curl around the perimeter). In a custom DirectX application one can program the RenderState to CullMode::CounterClockwise to properly cull CCW models; culling is sometimes called backface removal in modeling and viewing tools.

The .X model may appear black in a viewer, even if it is getting culled properly. Normal vectors may still point in the wrong direction (inside instead of outside). Unlike culling, there isn't a RenderState flag to flip normals. Normals should be flipped in the data preparation (e.g. using a switch in OBJ2DX), but they could also be flipped in a shader if using a custom HLSL shader.

The OBJ2DX converter has an option to flip triangles from one rule to the other if needed (it reorders the vertices). When the triangles are flipped it is usually necessary to also flip the normals direction, so there is another option. If the normals are facing the wrong way the model will appear black in the viewer.


Have problems with texture mapped images upside-down?

Another difference in the conventions is the location of 0,0 in the UV space of the texture image. For OpenGL 0,0 gets mapped to the bottom-left corner, and for DirectX 0,0 gets mapped to the top-left corner (like 2D window coordinates).

OBJ2DX provides a switch to flip the V coordinate using V' = 1.0 - V which will sometimes solve the problem depending on how the texture map is used (wrapping, repeating, etc may have problems). The best solution is to flip the texture image vertically using a program like Paint.NET rather than changing the UVs.

Similarly some content generating tools may require that the U coordinates also need to be flipped (especially if the model was converted from LH to RH). This is a equivalent to inverting an image by a mirror. OBJ2DX provides a U' = 1.0 - U which may help the problem, but again the best solution is to invert the texture image rather than changing UVs.

See the test0 files which contain a very simple model of two triangles, and a clear texture map with words, lines, and colors.

Here is what the texture map for the toy cat looks like:


Here is the test texture map used for checking coordinate mapping:


Comparison of OBJ and .X formats

The OBJ file for two triangles forming a square is:

# DirectX native format
# clockwise wound (left handed) square with tx map 0,0 at top left
v 0 0 0
v 1 0 0
v 0 1 0
v 1 1 0
vn 0 0 -1
vn 0 0 -1
vn 0 0 -1
vn 0 0 -1
vt 0 1 0
vt 1 1 0
vt 0 0 0
vt 1 0 0
f 2/2/2 1/1/1 3/3/3
f 3/3/3 4/4/4 2/2/2

The "v" lines declare points in space, one per line, and the following numbers are the X, Y, Z coordinates for that point.

Each "vn" line declares a normal vector.

Each "vt" line declares a texture coordinate; for 2D textures (almost always the case), only the first two numbers are used, which are the U, V image coordinate.

A polygon, or face, is declared with an "f" line. Each triplet defines the data for a vertex, as: pointIndex/normalVectorIndex/textureIndex The indices are 1-indexed, not zero-based like in C language.

OBJ files can also have a material specification but currently OBJ2DX ignores this information.

The corresponding output for this file in .X format is:

xof 0303txt 0032
Mesh {
MeshMaterialList {
1;
1;
0;
Material {
0.75; 0.75; 0.75; 1.0; ;
0.10; 0.75; 0.75; 0.75;
0.00; 0.00; 0.00;
TextureFilename { "default.png"; }
}
}
6;
1.0000000, 0.0000000, 0.0000000; ,
0.0000000, 0.0000000, 0.0000000; ,
0.0000000, 1.0000000, 0.0000000; ,
0.0000000, 1.0000000, 0.0000000; ,
1.0000000, 1.0000000, 0.0000000; ,
1.0000000, 0.0000000, 0.0000000; ,
2;
3; 0, 1, 2; ;
3; 3, 4, 5; ;
MeshNormals {
6;
0.0000000, 0.0000000, -1.0000000; ,
0.0000000, 0.0000000, -1.0000000; ,
0.0000000, 0.0000000, -1.0000000; ,
0.0000000, 0.0000000, -1.0000000; ,
0.0000000, 0.0000000, -1.0000000; ,
0.0000000, 0.0000000, -1.0000000; ,
2;
3; 0, 1, 2; ;
3; 3, 4, 5; ;
}
MeshTextureCoords {
6;
1.0000000; 1.0000000; ,
0.0000000; 1.0000000; ,
0.0000000; 0.0000000; ,
0.0000000; 0.0000000; ,
1.0000000; 0.0000000; ,
1.0000000; 1.0000000; ,
}
}

The first line header code is required for the DirectX parser.

The OBJ file is expanded by OBJ2DX so that all data is unshared in the .X file, even if it was shared in the OBJ.

Comment on the .X Files and DirectX

It seems that the .X file format is not evolving much. Part of this may be that much of the animation processing inside of DirectX for bones, frames, etc. was done in the CPU, not in HLSL shaders.

The modern way to do high performance animation and shading is to use shaders. On the effects side, the .FX file format specification for shaders has evolved, with the creation of the Standard Annotations and Semantics (SAS) to give tool writers and 3rd party exporters a guide to follow. There seems to be no equivalent SAS for .X files that would tie geometry to animation shaders.

The DxViewer utility (included in all the latest DirectX SDKs) will load both .X and .FX files allowing one to preview both a model and shaders at the same time.

Author Information

Dave Remba, is an instructor at DevelopMentor and teaches a course for developers on Managed DirectX.

more information is at: http://develop.com/directx

contact: daver@develop.com