VTX
VTX is the extension for Source's proprietary mesh strip format. It stores hardware optimized material, skinning and triangle strip/fan information for each LOD of each mesh in the MDL.
It is currently found in .sw.vtx
(Software), .dx80.vtx
(DirectX 8.0) .dx90.vtx
(DirectX 9.0) and .xbox.vtx
(XBox) flavors. Left 4 Dead and Left 4 Dead 2 originally used just .VTX files, as of the Sacrifice update for these two games they now use the .dx90.vtx
for the Mac OSX and re-used the .VTX only for Portal 2.
Header
#define OPTIMIZED_MODEL_FILE_VERSION 7
// this structure is in <mod folder>/src/public/optimize.h
struct FileHeader_t
{
// file version as defined by OPTIMIZED_MODEL_FILE_VERSION (currently 7)
int version;
// hardware params that affect how the model is to be optimized.
int vertCacheSize;
unsigned short maxBonesPerStrip;
unsigned short maxBonesPerFace;
int maxBonesPerVert;
// must match checkSum in the .mdl
long checkSum;
int numLODs; // garymcthack - this is also specified in ModelHeader_t and should match
// this is an offset to an array of 8 MaterialReplacementListHeader_t's, one of these for each LOD
int materialReplacementListOffset;
int numBodyParts;
int bodyPartOffset; // offset to an array of BodyPartHeader_t's
};
Data Structure
Data is read through a series of offsets which vary depending on how many textures the model uses, bodygroups, submodels and LoD. Each group of offsets will lead to a 25 byte block that contains all the face/triangle information to draw the mesh. For example, if the model has 3 LoD levels and the mesh uses 2 textures per mesh then there will be 6 separate mesh references.
The 25 byte block contains the following data:
Offset | Datatype | Size (bytes) | Description |
---|---|---|---|
0 | uInt32 | 4 | Number of vertexes |
4 | uInt32 | 4 | Offset to vertex number table |
8 | uInt32 | 4 | Number of vertexes to draw faces |
12 | uInt32 | 4 | Offset to sequence to draw faces |
16 | uInt32 | 4 | Unknown offset reference (possibly related to vertex faces) |
20 | uInt32 | 4 | Offset to (unknown) reference |
24 | byte | 1 | Unknown |

Vertex Number Table
This offset will lead to a table containing 9 byte blocks which contain the ID of the vert from the VVD file. The first 4 bytes are unknown but are usually 00 01 02 00 although can be different. The next 2 bytes contain the vert number. The remaining 3 bytes are unknown (maybe LoD related). This data needs to be stored into an array with the key starting at 0 to be read correctly.
Vertex Sequence
This offset will lead to a table containing 2 byte blocks which contain the order of verts to draw the faces correctly. The 16-bit int contains the array key.
Face Draw Order
The draw sequence can be constructed with the following statement done using PHP.
//$vert_seq contains the 16-bit int keys.
//$vert_data contains the 16-bit int vert number extracted from the 9 byte block.
//$faces contains the sorted vert numbers to draw the faces.
foreach($vert_seq as $vid) {
array_push($faces, $vert_data[$vid]);
}
The array $faces now contains the keys of the vertexes to be read from the VVD data assuming it is also stored in an array.
Decompile Example Script
Here is a link to a PHP script that will decompile a model can convert the reference mesh to SMD plain text. It will also partially re-construct a QC. It is not complete and does not work correctly on models with bodygroups, submodels and/or LoD. This script will only run on a Linux PHP server but the code can be modified to work on a Windows PHP server. Recommended for advanced users with programming/scripting knowledge only.
The script above is now active at the link below. It will decompile simple models such as props although it may be updated in the future to support more advanced models.