VTX: Difference between revisions
SirYodaJedi (talk | contribs) No edit summary |
|||
(19 intermediate revisions by 7 users not shown) | |||
Line 1: | Line 1: | ||
[[VTX]] is the extension for | {{src topicon}} | ||
[[VTX]] is the extension for {{src|4}}'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 (Source)|MDL]]. | |||
VTX files use a two-part file extension; the first part varies depending upon the renderer, although [[StudioMDL]] usually makes nearly identical VTX files for {{mono|dx80}}, {{mono|dx90}}, and {{mono|sw}}. | |||
<div style="display:flex"> | |||
{| class="wikitable" | |||
|+ style="color:#ddd" | PC formats | |||
|- | |||
! Extension !! Used when? | |||
|- | |||
| {{mono|.dx90.vtx}} || [[dxlevel]] ≥ 90 | |||
|- | |||
| {{mono|.dx80.vtx}} || [[dxlevel]] ≥ 80 | |||
|- | |||
| {{mono|.sw.vtx}} || [[dxlevel]] < 80 | |||
|- | |||
|} | |||
| |||
{| class="wikitable" | |||
|+ style="color:#ddd" | Console formats | |||
|- | |||
! Extension !! Console | |||
|- | |||
| {{mono|.xbox.vtx}} || {{xbox|2}} | |||
|- | |||
| {{mono|.360.vtx}}<br>{{mono|.dx90.360.vtx}} || {{360|2}} | |||
|}</div> | |||
The {{l4dseries|4.1}} originally used ''just'' .VTX files, as of the Sacrifice update for these two games they now use the <code>.dx90.vtx</code> for the Mac OSX and re-used the .VTX only for {{portal2|4.1}} {{clarify}}. | |||
{{modernConfirm|<ul> | |||
<li> What mesh file do other platforms, like {{ps3|4.1}} and {{android|4.1}} use? | |||
</ul>}} | |||
=File Structure= | {{warning|In games that support DX8, if a map is compiled with the parameter -staticproplighting and a prop doesn't have a .dx80.vtx file, the game will crash when loading the map.{{code fix|This can be fixed by modifying vrad to look for the dx90.vtx instead}}}} | ||
==Header== | |||
== File Structure == | |||
=== Header === | |||
<source lang="cpp"> | <source lang="cpp"> | ||
// this structure is in <mod folder>/src/public/optimize.h | // this structure is in <mod folder>/src/public/optimize.h | ||
Line 33: | Line 64: | ||
This is the header structure for the current VERSION7 .vtx file | This is the header structure for the current VERSION7 .vtx file | ||
===Body array=== | ====Body array==== | ||
The body array is a list of <b>BodyPartHeader_t</b> objects. | The body array is a list of <b>BodyPartHeader_t</b> objects. | ||
* Size: <b>numBodyParts</b>. | * Size: <b>numBodyParts</b>. | ||
Line 39: | Line 70: | ||
{{note|Since this value is in the header it can be interpreted as an absolute location}} | {{note|Since this value is in the header it can be interpreted as an absolute location}} | ||
==BodyPartHeader_t== | ===BodyPartHeader_t=== | ||
<source lang="cpp"> | <source lang="cpp"> | ||
struct BodyPartHeader_t | struct BodyPartHeader_t | ||
Line 49: | Line 80: | ||
</source> | </source> | ||
===Model array=== | ====Model array==== | ||
The model array is a list of <b>ModelHeader_t</b> objects. | The model array is a list of <b>ModelHeader_t</b> objects. | ||
* Size: <b>numModels</b>. | * Size: <b>numModels</b>. | ||
* Location: <b>bodyPartOffset</b>. | * Location: <b>bodyPartOffset</b>. | ||
==ModelHeader_t== | ===ModelHeader_t=== | ||
<source lang="cpp"> | <source lang="cpp"> | ||
// This maps one to one with models in the mdl file. | // This maps one to one with models in the mdl file. | ||
Line 65: | Line 96: | ||
</source> | </source> | ||
===LOD Mesh Array=== | ====LOD Mesh Array==== | ||
The LOD mesh array is a list of <b>ModelLODHeader_t</b> objects. | The LOD mesh array is a list of <b>ModelLODHeader_t</b> objects. | ||
* Size: <b>numLODS</b>. | * Size: <b>numLODS</b>. | ||
* Location: <b>lodOffset</b>. | * Location: <b>lodOffset</b>. | ||
==ModelLODHeader_t== | ===ModelLODHeader_t=== | ||
<source lang="cpp"> | <source lang="cpp"> | ||
Line 83: | Line 114: | ||
</source> | </source> | ||
===Mesh array=== | ====Mesh array==== | ||
The mesh array is a list of <b>MeshHeader_t</b> objects. | The mesh array is a list of <b>MeshHeader_t</b> objects. | ||
* Size: <b>numMeshes</b>. | * Size: <b>numMeshes</b>. | ||
* Location: <b>meshOffset</b>. | * Location: <b>meshOffset</b>. | ||
===Switch Point=== | ====Switch Point==== | ||
The point at which the engine should switch to this LOD mesh is defined by <b>switchPoint</b>. | The point at which the engine should switch to this LOD mesh is defined by <b>switchPoint</b>. | ||
==MeshHeader_t== | ===MeshHeader_t=== | ||
<source lang="cpp"> | <source lang="cpp"> | ||
struct MeshHeader_t | struct MeshHeader_t | ||
Line 102: | Line 133: | ||
</source> | </source> | ||
===Strip Group Array=== | ====Strip Group Array==== | ||
The strip group array is a list of <b>StripGroupHeader_t</b> objects. | The strip group array is a list of <b>StripGroupHeader_t</b> objects. | ||
* Size: <b>numStripGroups</b>. | * Size: <b>numStripGroups</b>. | ||
* Location: <b>stripGroupHeaderOffset</b>. | * Location: <b>stripGroupHeaderOffset</b>. | ||
===Flags=== | ====Flags==== | ||
The unsigned char <b>flags</b> value can be read from this table: | The unsigned char <b>flags</b> value can be read from this table: | ||
Line 123: | Line 154: | ||
|} | |} | ||
==StripGroupHeader_t== | ===StripGroupHeader_t=== | ||
<source lang="cpp"> | <source lang="cpp"> | ||
struct StripGroupHeader_t | struct StripGroupHeader_t | ||
Line 139: | Line 170: | ||
unsigned char flags; | unsigned char flags; | ||
//if | // The following fields are only present if MDL version is >=49 | ||
// Points to an array of unsigned shorts (16 bits each) | |||
int numTopologyIndices; | |||
int topologyOffset; | |||
}; | }; | ||
</source> | </source> | ||
===Vertex & Indices arrays=== | MDL versions 49 and above (found in {{csgo|2}}) have an extra two int fields (totaling 8 bytes). This is not reflected in the VTX header version, which remains at 7. | ||
Indices Array - This is a set of unsigned short integers that | {{TODO|What do these indices do?}} | ||
====Vertex & Indices arrays==== | |||
Indices Array - This is a set of unsigned short integers that index the position of the real vertex data in the .VVD's vertex array | |||
* Size: <b>numIndices</b> | * Size: <b>numIndices</b> | ||
* Location: <b>indexOffset</b> | * Location: <b>indexOffset</b> | ||
Line 152: | Line 189: | ||
* Location: <b>vertOffset</b> | * Location: <b>vertOffset</b> | ||
===Strip Array=== | ====Strip Array==== | ||
The strip array is a list of <b>StripHeader_T</b> objects | The strip array is a list of <b>StripHeader_T</b> objects | ||
* Size: <b>numStrips</b> | * Size: <b>numStrips</b> | ||
* Location: <b>stripOffset</b> | * Location: <b>stripOffset</b> | ||
==StripHeader_t== | ===StripHeader_t=== | ||
<source lang="cpp"> | <source lang="cpp"> | ||
// A strip is a piece of a stripgroup which is divided by bones | // A strip is a piece of a stripgroup which is divided by bones | ||
Line 174: | Line 211: | ||
int numBoneStateChanges; | int numBoneStateChanges; | ||
int boneStateChangeOffset; | int boneStateChangeOffset; | ||
// MDL Version 49 and up only | |||
int numTopologyIndices; | |||
int topologyOffset; | |||
}; | }; | ||
</source> | </source> | ||
===Indices & Vertex Groupings=== | Like in StripGroupHeader_t, the last eight bytes/two int fields are present if the MDL file's header shows version 49 or higher. Presumably, ({{TODO|Verify}}) these index into the parent strip group's list of topology indices, similar to vertices and indices. | ||
Each group (Indices and | |||
====Indices & Vertex Groupings==== | |||
Each group (Indices and Vertices respectively), specify what position to read from the vertex pool, as well as the indices pool. These pools come from the parent StripGroupHeader object | |||
===Vertex_t=== | ===Vertex_t=== | ||
Line 198: | Line 241: | ||
<b>origMeshVertID</b> defines the index of this vertex that is to be read from the linked .VVD file's vertex array | <b>origMeshVertID</b> defines the index of this vertex that is to be read from the linked .VVD file's vertex array | ||
{{note|This value needs to be added to the total vertices read, since it is relative to the mesh and won't work as an absolute key.}} | {{note|This value needs to be added to the total vertices read, since it is relative to the mesh and won't work as an absolute key.}} | ||
When parsing, note that a Vertex contains nine bytes of information, but will usually be padded to 10 bytes. Within the VTX file, the length of each vertex's data will still be 9. For example, one should call in C: | |||
<source lang="c">fread(vertexBuf, vertexCount, 9, fileptr);</source> | |||
Instead of: | |||
<source lang="c">fread(vertexBuf, vertexCount, sizeof(Vertex_t), fileptr);</source> | |||
The latter sample will cause data corruption, unless your model has only one vertex (unlikely). | |||
==See also== | ==See also== | ||
Line 204: | Line 253: | ||
*[https://github.com/geotavros/vtx2dx90vtx/releases VTX to DX90.VTX or DX80.VTX Batch Converter] | *[https://github.com/geotavros/vtx2dx90vtx/releases VTX to DX90.VTX or DX80.VTX Batch Converter] | ||
[[Category: | [[Category:File formats]] | ||
[[Category:Modeling]] | [[Category:Modeling]] | ||
Latest revision as of 05:54, 1 April 2025
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.
VTX files use a two-part file extension; the first part varies depending upon the renderer, although StudioMDL usually makes nearly identical VTX files for dx80, dx90, and sw.
Extension | Used when? |
---|---|
.dx90.vtx | dxlevel ≥ 90 |
.dx80.vtx | dxlevel ≥ 80 |
.sw.vtx | dxlevel < 80 |
Extension | Console |
---|---|
.xbox.vtx | ![]() |
.360.vtx .dx90.360.vtx |
![]() |
The Left 4 Dead series 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 [Clarify].

- What mesh file do other platforms, like
PlayStation 3 and
Android use?


File Structure
Header
// 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 maxBonesPerTri;
int maxBonesPerVert;
// must match checkSum in the .mdl
int checkSum;
int numLODs; // Also specified in ModelHeader_t's and should match
// Offset to materialReplacementList Array. one of these for each LOD, 8 in total
int materialReplacementListOffset;
//Defines the size and location of the body part array
int numBodyParts;
int bodyPartOffset;
};
This is the header structure for the current VERSION7 .vtx file
Body array
The body array is a list of BodyPartHeader_t objects.
- Size: numBodyParts.
- Location: bodyPartOffset.

BodyPartHeader_t
struct BodyPartHeader_t
{
//Model array
int numModels;
int modelOffset;
};
Model array
The model array is a list of ModelHeader_t objects.
- Size: numModels.
- Location: bodyPartOffset.
ModelHeader_t
// This maps one to one with models in the mdl file.
struct ModelHeader_t
{
//LOD mesh array
int numLODs; //This is also specified in FileHeader_t
int lodOffset;
};
LOD Mesh Array
The LOD mesh array is a list of ModelLODHeader_t objects.
- Size: numLODS.
- Location: lodOffset.
ModelLODHeader_t
struct ModelLODHeader_t
{
//Mesh array
int numMeshes;
int meshOffset;
float switchPoint;
};
Mesh array
The mesh array is a list of MeshHeader_t objects.
- Size: numMeshes.
- Location: meshOffset.
Switch Point
The point at which the engine should switch to this LOD mesh is defined by switchPoint.
MeshHeader_t
struct MeshHeader_t
{
int numStripGroups;
int stripGroupHeaderOffset;
unsigned char flags;
};
Strip Group Array
The strip group array is a list of StripGroupHeader_t objects.
- Size: numStripGroups.
- Location: stripGroupHeaderOffset.
Flags
The unsigned char flags value can be read from this table:
Value | Meaning |
---|---|
0x01 | STRIPGROUP_IS_FLEXED |
0x02 | STRIPGROUP_IS_HWSKINNED |
0x04 | STRIPGROUP_IS_DELTA_FLEXED |
0x08 | STRIPGROUP_SUPPRESS_HW_MORPH |
StripGroupHeader_t
struct StripGroupHeader_t
{
// These are the arrays of all verts and indices for this mesh. strips index into this.
int numVerts;
int vertOffset;
int numIndices;
int indexOffset;
int numStrips;
int stripOffset;
unsigned char flags;
// The following fields are only present if MDL version is >=49
// Points to an array of unsigned shorts (16 bits each)
int numTopologyIndices;
int topologyOffset;
};
MDL versions 49 and above (found in Counter-Strike: Global Offensive) have an extra two int fields (totaling 8 bytes). This is not reflected in the VTX header version, which remains at 7.
Vertex & Indices arrays
Indices Array - This is a set of unsigned short integers that index the position of the real vertex data in the .VVD's vertex array
- Size: numIndices
- Location: indexOffset
Vertex Array - The vertex array inside the .VTX file holds some extra information related to skinning
- Size: numVerts
- Location: vertOffset
Strip Array
The strip array is a list of StripHeader_T objects
- Size: numStrips
- Location: stripOffset
StripHeader_t
// A strip is a piece of a stripgroup which is divided by bones
struct StripHeader_t
{
int numIndices;
int indexOffset;
int numVerts;
int vertOffset;
short numBones;
unsigned char flags;
int numBoneStateChanges;
int boneStateChangeOffset;
// MDL Version 49 and up only
int numTopologyIndices;
int topologyOffset;
};
Like in StripGroupHeader_t, the last eight bytes/two int fields are present if the MDL file's header shows version 49 or higher. Presumably, (
) these index into the parent strip group's list of topology indices, similar to vertices and indices.
Indices & Vertex Groupings
Each group (Indices and Vertices respectively), specify what position to read from the vertex pool, as well as the indices pool. These pools come from the parent StripGroupHeader object
Vertex_t
struct Vertex_t
{
// these index into the mesh's vert[origMeshVertID]'s bones
unsigned char boneWeightIndex[3];
unsigned char numBones;
unsigned short origMeshVertID;
// for sw skinned verts, these are indices into the global list of bones
// for hw skinned verts, these are hardware bone indices
char boneID[3];
};
origMeshVertID defines the index of this vertex that is to be read from the linked .VVD file's vertex array

When parsing, note that a Vertex contains nine bytes of information, but will usually be padded to 10 bytes. Within the VTX file, the length of each vertex's data will still be 9. For example, one should call in C:
fread(vertexBuf, vertexCount, 9, fileptr);
Instead of:
fread(vertexBuf, vertexCount, sizeof(Vertex_t), fileptr);
The latter sample will cause data corruption, unless your model has only one vertex (unlikely).