The GSkinMesh (GSM) file format

Last updated: 2010-01-10, current with gsmhacking 3.1.50.

Character and weapon models in SWAT3 are stored in GSkinMesh format, a proprietary format created by Sierra. No documentation or source code for the .gsm file format is availble.

I have been reverse engineering the .gsm file format. Barring any mistakes in my work, I believe I have deciphered all of it.

The GSkinAnimation file format is also documented.

Sections of the .gsm file

The .gsm file is composed of fourteen sections:

  1. The .gsm file header
  2. Skeleton and bones information
  3. Vertex to bone assignments
  4. Vertex positions
  5. Blended positions
  6. Texture co-ordinates
  7. Effective vertices
  8. Triangles
  9. Triangle groups
  10. Shared triangle lists
  11. Textures information
  12. Materials information
  13. Grips information
  14. Grippers information

A note on co-ordinates

When exporting weapon models, the GSM exporter normalises the model's co-ordinate system relative to the position of the first bone in the first skeleton. Thus that bone is always at the origin. It does not do this when exporting character meshes.

Each bone's "position" is a vector from its parent in the co-ordinate system defined by the parent's rotation.

It is important to make the distinction between what you think a bone is and what 3D Studio (and hence the GSM) thinks a bone is. When you make a bone in 3D Studio you click the mouse at the location of the first endpoint of the bone, move to where you want the other end to be and click again. 3D Studio then draws a line between the two points.

You probably think that line is a bone. Wrong. The two endpoints are bones (or nodes) in 3D Studio's eyes. That's why you've got "bones" called Rifle_Root and Rifle_Bone01 in your weapon GSM.

This means that every GSM must have at least two bones. And it means using the root bone as the origin for the co-ordinate system is meaningful.

The sections in detail

Throughout this document, sizeof(int) and sizeof(float) are assumed to be 4, sizeof(short) is assumed to be 2 and all hex values are little-endian. Strings are not \0 terminated unless specified, since their lengths are always given. A float is a single precision signed floating point real.

If the above is meaningless to you then you are probably going to get bored very quickly anyway...

The .gsm file header

Description Size Comments or example
Magic 16 octets GSM magic is 0x72 0xF6 0x63 0x1E 0x5E 0x47 0xD2 0x11 0x87 0x99 0x00 0x20 0xAF 0xE6 0x36 0xEE
GSM exporter version char (minor) char (major) Always 0x05 0x04: version 4.5

Bones information

First we have a definition of the skeletons in the .gsm file (or Physiques, if you are a 3D Studio MAX modeller), then the bone data following that. Since the exporter will only let you export one model at a time, and since it crashes if you try to physique a model to more than one skeleton, it is tempting to speculate that all GSM files must have one skeleton, no more, no fewer. Indeed, making that assumption allows us to do some cool stuff very easily. Makes you wonder why they put the skeleton data in there, though.

Number of skeletons short Usually (always?) 0x01 0x00
Skeleton ID short First one is 0x00 0x00
Length of skeleton name short Example: 0x07 0x00 for SwatGuy
Skeleton name see above Example: SwatGuy
Number of bones in skeleton short 0x34 0x00 for SWT, 0x25 0x00 for TAC
Bone   See below
Bone
...

Each bone is defined like this:

Bone ID short First one is 0x00 0x00
Bone name length short Example: 0x0C 0x00 for Biped Pelvis
Bone name See above Example: Biped Pelvis
Bone link short Number of the bone this bone is connected to. 0xFF 0xFF for the root bone
X co-ordinate float  
Y co-ordinate float  
Z co-ordinate float  
Rotation quaternion X component float Bone's orientation determined by this quaternion
Rotation quaternion Y component float Bone's orientation determined by this quaternion
Rotation quaternion Z component float Bone's orientation determined by this quaternion
Rotation quaternion scalar component float Bone's orientation determined by this quaternion

Vertex to bone assignments section

Physiquing a mesh to a skeleton sets up links between vertices and bones. The game knows to move the vertices in a character's leg when his leg bones move because those vertices are mapped to that bone.

Number of assignments short  
Assignment   See below
Assignment
...

The GSM format classifies the assignments by groups of vertices, so that for example, "n vertices starting from vertex v map to bone b."

All vertices must be mapped to bones. Not all bones need have vertices mapped to them.

Bone ID short Must match an entry from the bones section
First vertex in assignment short Must be valid in the range of vertex positions
Number of vertices in assignment short This number of vertices are mapped to the bone
Padding short  

Sometimes (with character models) the number of assignments that are actually present is less than the number advertised in the assignment header. In this case, the magic marker 0xEE 0xEE is substituted for the missing assignment(s).

It seems this happens if and only if there are blended positions. In that case, the following information is present.

Index of first blended position short  
Number of blended positions int May actually be a short with null padding

Vertex positions

The vertex positions section is simply a list of co-ordinates, with a vertex in the mesh present at each co-ordinate.

Number of positions short  
Position   See below
Position
...

Position:

X co-ordinate float  
Y co-ordinate float  
Z co-ordinate float  

Blended positions

This section describes vertex blending. It is present even if there was no blended position header earlier, though in this case there will be no data stream.

Number of blended positions short  
Length of blended position stream short  
Blended position   Only present if number of positions is nonzero (See below)
Blended position
...

Each blended position lists 2 or more vertices (positions) and a weight.

Number of positions short  
Position ID short  
Weight float Normalised; sum of weights must be 1.0 or 0.0
Position ID
Weight
...

Texture co-ordinates

These are UVW maps but with no W. The V part is inverted from the value in the 3D Studio material editor.

Number of co-ordinates short  
Co-ordinate   See below
Co-ordinate
...

Co-ordinates:

U float  
V float -1 x 3D Studio value

Effective vertices

Explanation forthcoming...

Number of effective vertices short  
Vertex   See below
Vertex
...

Vertices:

Texture co-ordinate ID short  
Position ID short If greater than number of positions, this refers to a blended position
Shared triangle list ID short  
Hit detection short Hit detection colour (as described in ModHQ FAQ) in RGB15 format.

A position ID is valid when it is less than the sum of the numbers of positions and blended positions in the mesh. If it is greater than the number of positions, subtract that number to get the blended position index.

To calculate a blended position, scale each the co-ordinate vector for position listed in the blend list by its associated weight and sum the resultant vectors.

Triangles

A triangle is defined as a triple (v1,v2,v3), where v1, v2 and v3 are entries from the effective vertices section. If you had only one triangle in the mesh, for example, you would have three effective vertices and your one triangle entry would be (0,1,2).

Number of triangles short  
Triangle   See below
Triangle
...

The triangles:

First vertex short ID <= number of effective vertices
Second vertex short ID <= number of effective vertices
Third vertex short ID <= number of effective vertices
Padding short  

Triangle groups

Triangle groups relate to material groups in 3D Studio. When you set a face to a specific ID you will create a corresponding triangle group entry in the GSM. The material IDs start from 0, though, and so are 1 less than the numbers given in MAX.

Similarly to the vertex to bone assignments section, the triangle groups section gives a triangle ID and a number. The triangle with the given ID and the next <number> triangles all belong to the specified group.

Number of triangle groups short  
Triangle group   See below
Triangle group
...

The groups:

Material ID short 1 less than ID given in MAX
First triangle short From triangles section
Number of triangles short  
Padding short  

Shared triangle lists

Explanation forthcoming...

Shared triangle list data stream length int Actually a number of shorts, not number of octets (doh!)
Number of shared triangle lists short  
Shared triangle list   See below
Shared triangle list
...

Each entry in the shared triangle list is exactly that, a list of triangle numbers:

Number of triangles short  
Triangle ID short  
Triangle ID
...

Textures information

First a count of the textures in the file:

Number of textures short Example: 0x13 0x00 for SWT_ELEM_HIGH (19 textures)
Texture   See below
Texture
...

The textures themselves are defined viz:

Length of texture name short Length of full path name to texture
Texture name See above Name is terminated with TWO \0 characters (eg <name> 0x00 0x00)

When you hex edit .gsm files, you are messing with these. Note that there is no texture ID field.

Materials information

This is where the fun really is. You can alter opacity and stuff to get cool effects like in the Ghost Recon mod.

Number of materials short Usually (though not necessarily) the same as the number of textures.
Material   See below
Material
...

The materials names are as defined in the 3D Studio MAX material editor...

Material ID short  
Material name length short  
Material name See above  
Number of maps short Number of textures mapped to this material
Map   See below (if number of maps is zero this is of course not present)
Map
...
Map type? short Not sure about this
Diffuse map red value float This is the material editor value
Diffuse map green value float This is the material editor value
Diffuse map blue value float This is the material editor value
Ambient map red value float This is the material editor value
Ambient map green value float This is the material editor value
Ambient map blue value float This is the material editor value
Specularity map red value float This is the material editor value
Specularity map green value float This is the material editor value
Specularity map blue value float This is the material editor value
Opacity falloff float This is the material editor value divided by 100 - in/out irrelevant
Transparency float This is the material editor value divided by 100
Two-sided char 0x01 if material is 2-sided, 0x00 otherwise
Self-illumination float This is the material editor value divided by 100
Shininess float This is the material editor value divided by 100
Shine strength float This is the material editor value divided by 100

Material maps look like this:

Map type short 0x01 = diffuse, 0x09 = transparency etc
Texture ID short From textures section

Grips information

When characters "hold" weapons the game works out where to place the object relative to the character by aligning grips with grippers. Grips are the helper objects attached to objects. Grippers are the helpers attached to characters (eg at the hands). Grips and grippers are matched by name.

Weapons must have grips. Characters must have grippers.

Number of grips short  
Grip   See below
Grip
...

Each grip follows:

Grip name length short Example 0x05 0x00 for Grip1
Grip name See above Example: Grip1
Transformation matrix element (0,0) float Defines grip's orientation in space
Transformation matrix element (0,1) float Defines grip's orientation in space
Transformation matrix element (0,2) float Defines grip's orientation in space
Transformation matrix element (1,0) float Defines grip's orientation in space
Transformation matrix element (1,1) float Defines grip's orientation in space
Transformation matrix element (1,2) float Defines grip's orientation in space
Transformation matrix element (2,0) float Defines grip's orientation in space
Transformation matrix element (2,1) float Defines grip's orientation in space
Transformation matrix element (2,2) float Defines grip's orientation in space
X co-ordinate float  
Y co-ordinate float  
Z co-ordinate float  
Bone ID that grip is attached to short Example: 0x1C 0x00 is Biped R Hand for the SWT mesh, so this gripper is the trigger hand gripper

Note that there is no grip ID, just the ID of the bone it is attached to. You must crosscheck with the bones section to figure out the correct value.

Grippers information

The actual gripper data stream is identical to that of grips. Only the context is different.

Feedback

Send any comments to swat3@furrycat.net.