This article's documentation is for anything that uses the Source engine. Click here for more information.

VTX: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
 
(26 intermediate revisions by 10 users not shown)
Line 1: Line 1:
[[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]].
{{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]].


It is currently found in <code>.sw.vtx</code> (Software), <code>.dx80.vtx</code> (DirectX 8.0) <code>.dx90.vtx</code> (DirectX 9.0) and <code>.xbox.vtx</code> (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 <code>.dx90.vtx</code> for the Mac OSX and re-used the .VTX only for ''Portal 2''.
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
|-
|}
&nbsp;&nbsp;
{| 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>}}
 
{{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">
#define OPTIMIZED_MODEL_FILE_VERSION 7
// this structure is in <mod folder>/src/public/optimize.h
// this structure is in <mod folder>/src/public/optimize.h
struct FileHeader_t
struct FileHeader_t
Line 16: Line 46:
int vertCacheSize;
int vertCacheSize;
unsigned short maxBonesPerStrip;
unsigned short maxBonesPerStrip;
unsigned short maxBonesPerFace;
unsigned short maxBonesPerTri;
int maxBonesPerVert;
int maxBonesPerVert;


// must match checkSum in the .mdl
// must match checkSum in the .mdl
long checkSum;
int checkSum;
 
int numLODs; // garymcthack - this is also specified in ModelHeader_t and should match
int numLODs; // Also specified in ModelHeader_t's and should match


// this is an offset to an array of 8 MaterialReplacementListHeader_t's, one of these for each LOD
// Offset to materialReplacementList Array. one of these for each LOD, 8 in total
int materialReplacementListOffset;
int materialReplacementListOffset;


        //Defines the size and location of the body part array
int numBodyParts;
int numBodyParts;
int bodyPartOffset; // offset to an array of BodyPartHeader_t's
int bodyPartOffset;
};
};
</source>
</source>
This is the header structure for the current VERSION7 .vtx file


==Data Structure==
====Body array====
The body array is a list of <b>BodyPartHeader_t</b> objects.
* Size: <b>numBodyParts</b>.
* Location: <b>bodyPartOffset</b>.
{{note|Since this value is in the header it can be interpreted as an absolute location}}


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.
===BodyPartHeader_t===
<source lang="cpp">
struct BodyPartHeader_t
{
//Model array
int numModels;
int modelOffset;
};
</source>
 
====Model array====
The model array is a list of <b>ModelHeader_t</b> objects.
* Size: <b>numModels</b>.
* Location: <b>bodyPartOffset</b>.
 
===ModelHeader_t===
<source lang="cpp">
// 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;
};
</source>


The 25 byte block contains the following data:
====LOD Mesh Array====
The LOD mesh array is a list of <b>ModelLODHeader_t</b> objects.
* Size: <b>numLODS</b>.
* Location: <b>lodOffset</b>.


===ModelLODHeader_t===
<source lang="cpp">
struct ModelLODHeader_t
{
    //Mesh array
int numMeshes;
int meshOffset;
float switchPoint;
};
</source>
====Mesh array====
The mesh array is a list of <b>MeshHeader_t</b> objects.
* Size: <b>numMeshes</b>.
* Location: <b>meshOffset</b>.
====Switch Point====
The point at which the engine should switch to this LOD mesh is defined by <b>switchPoint</b>.
===MeshHeader_t===
<source lang="cpp">
struct MeshHeader_t
{
int numStripGroups;
int stripGroupHeaderOffset;
unsigned char flags;
};
</source>
====Strip Group Array====
The strip group array is a list of <b>StripGroupHeader_t</b> objects.
* Size: <b>numStripGroups</b>.
* Location: <b>stripGroupHeaderOffset</b>.
====Flags====
The unsigned char <b>flags</b> value can be read from this table:
{| class="standard-table"
{| class="standard-table"
! Offset
! Value
! Datatype
! Meaning
! 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
|0x01 || STRIPGROUP_IS_FLEXED
|-
|-
|12 || uInt32 || 4 || Offset to sequence to draw faces
|0x02 || STRIPGROUP_IS_HWSKINNED
|-
|-
|16 || uInt32 || 4 || Unknown offset reference (possibly related to vertex faces)
|0x04 || STRIPGROUP_IS_DELTA_FLEXED
|-
|20 || uInt32 || 4 || Offset to (unknown) reference
|-
|-
|24 || byte || 1 || Unknown
|0x08 || STRIPGROUP_SUPPRESS_HW_MORPH
|}
|}


===StripGroupHeader_t===
<source lang="cpp">
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;
};
</source>


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.
{{TODO|What do these indices do?}}


{{Note|All offsets are calculated from the position of the start of the block.}}
====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>
* Location: <b>indexOffset</b>


===Vertex Number Table===
Vertex Array - The vertex array inside the .VTX file holds some extra information related to skinning
* Size: <b>numVerts</b>
* Location: <b>vertOffset</b>


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.
====Strip Array====
The strip array is a list of <b>StripHeader_T</b> objects
* Size: <b>numStrips</b>
* Location: <b>stripOffset</b>


===Vertex Sequence===
===StripHeader_t===
<source lang="cpp">
// A strip is a piece of a stripgroup which is divided by bones
struct StripHeader_t
{
int numIndices;
int indexOffset;


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.
int numVerts;   
int vertOffset;


===Face Draw Order===
short numBones;


The draw sequence can be constructed with the following statement done using PHP.
unsigned char flags;


<source lang="php">
int numBoneStateChanges;
//$vert_seq contains the 16-bit int keys.
int boneStateChangeOffset;
//$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) {
// MDL Version 49 and up only
array_push($faces, $vert_data[$vid]);
int numTopologyIndices;
}
int topologyOffset;
};
</source>
</source>


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.
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.


===Decompile Example Script===
====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


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.
===Vertex_t===
<source lang="cpp">
struct Vertex_t
{
// these index into the mesh's vert[origMeshVertID]'s bones
unsigned char boneWeightIndex[3];
unsigned char numBones;
 
unsigned short origMeshVertID;


[http://www.hamaloo.co.uk/dl.php?f=mdlunpack001.zip Download]
// 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];
};
</source>


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.
<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.}}


[http://www.hamaloo.co.uk/mdlunpack/ MDL Unpack Web Site]
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==


*[[Model Creation Overview]]
*[[Model Creation Overview]]
*[https://github.com/geotavros/vtx2dx90vtx VTX to DX90.VTX Converter]
*[https://github.com/geotavros/vtx2dx90vtx/releases VTX to DX90.VTX or DX80.VTX Batch Converter]
 
[[Category:File formats]]


[[Category:Glossary]]
[[Category:Modeling]]
[[Category:Modeling]]
[[Category:File formats]]

Latest revision as of 05:54, 1 April 2025

VTX is the extension for Source 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.

PC formats
Extension Used when?
.dx90.vtx dxlevel ≥ 90
.dx80.vtx dxlevel ≥ 80
.sw.vtx dxlevel < 80

  

Console formats
Extension Console
.xbox.vtx Original Xbox Original Xbox
.360.vtx
.dx90.360.vtx
Xbox 360 Xbox 360

The Left 4 Dead seriesLeft 4 Dead series 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 Portal 2 [Clarify].

Confirm:
Warning.pngWarning: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.
Cpp.pngCode Fix:This can be fixed by modifying vrad to look for the dx90.vtx instead

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.
Note.pngNote:Since this value is in the header it can be interpreted as an absolute location

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 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.

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: 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, (

Todo: Verify

) 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

Note.pngNote: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:

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).

See also