Difference between revisions of "VTX"

From Valve Developer Community
Jump to: navigation, search
m (Added note about header size. Error possibly due to the removal of inline code)
(Rewrote the page with all missing information covered, and added extra clarification. Removed the dead links)
Line 3: Line 3:
 
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''.
 
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''.
  
 +
=File Structure=
 
==Header==
 
==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 15:
 
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>
 +
This is the header structure for the current VERSION7 .vtx file
 +
 
 +
===Body array===
 +
The body array is a list of <b>BodyPartHeader_t</b> objects.
 +
 
 +
Size is specified by: <b>numBodyParts</b>.
 +
 
 +
Location is specified by <b>bodyPartOffset</b>. This is relative to the header, which is alwasy 0x0 and can therefore the offset can allways be treated as a static address.
 +
 
 +
==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 is specified by: <b>numModels</b>.
 +
 
 +
Location is specified by <b>bodyPartOffset</b>. This is a relative offset, from the start of the <b>BodyPartHeader_t</b> object.
 +
 
 +
==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>
 
</source>
  
{{Note|This header is 36 bytes in size. Data seems to start at 73 bytes (0x49)}}
+
===LOD Mesh Array===
{{TODO|Add information about the data that is missing here}}
+
The LOD mesh array is a list of <b>ModelLODHeader_t</b> objects.
  
==Data Structure==
+
Size is specified by: <b>numLODS</b>
  
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.
+
Location is specified by <b>lodOffset</b>. This is again a relative offset, from the start of the <b>Modelheader_t</b> object.
  
The 25 byte block contains the following data:
+
==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 is specified by: <b>numMeshes</b>.
 +
 +
Location is specified by <b>meshOffset</b>. Relative offset from the start of the <b>ModelLODHeader_t</b> object.
 +
 +
===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>StripHeader_t</b> objects.
 +
 +
Size is specified by: <b>numStripGroups</b>
 +
 +
Location is specified by <b>stripGroupHeaderOffset</b>. Relative from <b>MeshHeader_t</b> object.
 +
 +
===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
+
|0x01 || STRIPGROUP_IS_FLEXED
 
|-
 
|-
|4 || uInt32 || 4 || Offset to vertex number table
+
|0x02 || STRIPGROUP_IS_HWSKINNED
 
|-
 
|-
|8 || uInt32 || 4 || Number of vertexes to draw faces
+
|0x04 || STRIPGROUP_IS_DELTA_FLEXED
 
|-
 
|-
|12 || uInt32 || 4 || Offset to sequence to draw faces
+
|0x08 || STRIPGROUP_SUPPRESS_HW_MORPH
|-
 
|16 || uInt32 || 4 || Unknown offset reference (possibly related to vertex faces)
 
|-
 
|20 || uInt32 || 4 || Offset to (unknown) reference
 
|-
 
|24 || byte || 1 || Unknown
 
 
|}
 
|}
  
 +
==StripHeader_t==
 +
<source lang="cpp">
 +
// A strip is a piece of a stripgroup which is divided by bones
 +
struct StripHeader_t
 +
{
 +
//Indices array
 +
int numIndices;
 +
int indexOffset;
  
 +
//Vertices array
 +
int numVerts;   
 +
int vertOffset;
  
{{Note|All offsets are calculated from the position of the start of the block.}}
+
short numBones;
  
===Vertex Number Table===
+
unsigned char flags;
  
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.
+
int numBoneStateChanges;
 
+
int boneStateChangeOffset;
===Vertex Sequence===
+
};
 +
</source>
  
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.
+
===Indices array===
 +
The indices array is a list of <b>unsigned short</b> indices.
  
===Face Draw Order===
+
Size is specified by: <b>numIndices</b>.
  
The draw sequence can be constructed with the following statement done using PHP.
+
Location is specified by <b>indexOffset</b>. Relative from <b>StripHeader_t</b> object.
  
<source lang="php">
+
===Vertices array===
//$vert_seq contains the 16-bit int keys.
+
The vertex array is a list of <b>Vertex_t</b> objects.
//$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) {
+
Size is specified by: <b>numVerts</b>.
array_push($faces, $vert_data[$vid]);
 
}
 
</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.
+
Location is specified by <b>vertOffset</b>. Relative from <b>StripHeader_t</b> object.
  
===Decompile Example Script===
+
===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;
  
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.
+
unsigned short origMeshVertID;
  
[http://www.hamaloo.co.uk/dl.php?f=mdlunpack001.zip Download] (Broken Link)
+
// for sw skinned verts, these are indices into the global list of bones
 
+
// for hw skinned verts, these are hardware bone indices
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.
+
char boneID[3];
 +
};
 +
</source>
  
[http://web.archive.org/web/20130105065011/http://www.hamaloo.co.uk/mdlunpack/ MDL Unpack Web Site]
+
<b>origMeshVertID</b> defines the index of this vertex that is to be read from the linked .VVD file's vertex array
  
 
==See also==
 
==See also==

Revision as of 09:41, 2 December 2017

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.

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 is specified by: numBodyParts.

Location is specified by bodyPartOffset. This is relative to the header, which is alwasy 0x0 and can therefore the offset can allways be treated as a static address.

BodyPartHeader_t

struct BodyPartHeader_t
{
	//Model array
	int numModels;
	int modelOffset;
};

Model array

The model array is a list of ModelHeader_t objects.

Size is specified by: numModels.

Location is specified by bodyPartOffset. This is a relative offset, from the start of the BodyPartHeader_t object.

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 is specified by: numLODS

Location is specified by lodOffset. This is again a relative offset, from the start of the Modelheader_t object.

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 is specified by: numMeshes.

Location is specified by meshOffset. Relative offset from the start of the ModelLODHeader_t object.

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

Size is specified by: numStripGroups

Location is specified by stripGroupHeaderOffset. Relative from MeshHeader_t object.

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

StripHeader_t

// A strip is a piece of a stripgroup which is divided by bones 
struct StripHeader_t
{
	//Indices array
	int numIndices;
	int indexOffset;

	//Vertices array
	int numVerts;    
	int vertOffset;

	short numBones;

	unsigned char flags;

	int numBoneStateChanges;
	int boneStateChangeOffset;
};

Indices array

The indices array is a list of unsigned short indices.

Size is specified by: numIndices.

Location is specified by indexOffset. Relative from StripHeader_t object.

Vertices array

The vertex array is a list of Vertex_t objects.

Size is specified by: numVerts.

Location is specified by vertOffset. Relative from StripHeader_t 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

See also