> For the complete documentation index, see [llms.txt](https://docs.bestway.com.ua/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bestway.com.ua/foundational-knowledge/gem-rts-binary-file-formats/ply-format-.ply.md).

# PLY format (.ply)

## Overview

The `.ply` is a chunk-based format used to store meshes in engines of the GEM family.

{% hint style="warning" %}
Chunk loading must continue until the end of the file.
{% endhint %}

{% hint style="warning" %}
Because chunk size is not stored, it must be calculated by the parser based on the chunk data structure.
{% endhint %}

Each file may contain:

* static meshes;
* skinned meshes;
* landscape meshes;
* terrain grids;
* adjacency data;
* shadow data;
* multiple UV channels;
* tangent space;
* skinning
* materials and textures

**File structure**

```
EPLY | BPLY | PM01
chunk ID loop
   INDX | IND4
   VERT
   BAS2
   InfG
   BNDS
   ADJA
   SHDW
   MROR
   MARK
   SKIN
   MESH | SBM1 | SUBM
```

{% hint style="warning" %}
The order of chunks inside the loop is not fixed, and some of them may not be present. \
The parser must determine the chunk type from its FourCC value and process the data accordingly.
{% endhint %}

## File header

Each `.ply` file begins with one of the following identifiers:

| Name | Type      | Size | Description                         |
| ---- | --------- | ---- | ----------------------------------- |
| EPLY | uint8\[4] | 4    | Regular mesh with 16-bit indices    |
| BPLY | uint8\[4] | 4    | Landscape mesh with 32-bit indices. |
| PM01 | uint8\[4] | 4    | GEM3 mesh format                    |

The starting FourCC is a file format identifier, it is not a regular chunk. After reading the starting FourCC, the parser reads the rest of the file as a sequence of chunks.

## Chunk IDs

| FourCC         | Purpose                 |
| -------------- | ----------------------- |
| INDX           | Mesh indices (16-bit)   |
| IND4           | Mesh indices (32-bit)   |
| VERT           | Vertex data             |
| BAS2           | Terrain grid basis      |
| InfG           | Terrain grid            |
| BNDS           | Bounding box            |
| ADJA           | Adjacency               |
| SHDW           | Shadow data             |
| MROR           | Mirrored mesh flag      |
| MARK           | Markup data             |
| SKIN           | Bone map                |
| MESH           | Legacy submesh          |
| SBM1           | GEM RTS 1.1+ submesh    |
| SUBM (GEM RTS) | GEM3 / GEM RTS submesh  |
| SUBM (AS2)     | Assault Squad 2 submesh |

{% hint style="warning" %}
There are two incompatible variants of the `SUBM` chunk. The chunk structure depends on the resource starting FourCC value. The parser must choose the appropriate `SUBM` variant according to the starting FourCC.
{% endhint %}

## Chunk ID loading loop

### Chunk `INDX` | `IND4`

**Data structure**

```
INDX | IND4
   index_count
   indices[index_count]
```

#### Chunk `INDX`

<table><thead><tr><th width="130.00390625">Name</th><th width="180.3828125">Type</th><th width="83.71875">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>INDX</code><br>Mesh indices</td></tr><tr><td>index_count</td><td>int32</td><td>4</td><td>Number of indices</td></tr><tr><td>indices</td><td>uint16[index_count]</td><td>2</td><td>Index array. <br>Index size is determined by the starting chunk ID</td></tr></tbody></table>

#### Chunk `IND4`

<table><thead><tr><th width="136.6328125">Name</th><th width="184.02734375">Type</th><th width="82.171875">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>IND4</code><br>Mesh indices</td></tr><tr><td>index_count</td><td>int32</td><td>4</td><td>Number of indices</td></tr><tr><td>indices</td><td>uint32[index_count]</td><td>4</td><td>Index array. <br>Index size is determined by the starting chunk ID</td></tr></tbody></table>

### Chunk `VERT`

```
VERT
   vertex_count
   vertex_size
   vertex_flags

   vertices[vertex_count]
       vertex data
```

<table><thead><tr><th width="128.1015625">Name</th><th width="179.78515625">Type</th><th width="94.51953125">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>VERT</code></td></tr><tr><td>vertex_count</td><td>int32</td><td>4</td><td>Number of vertices</td></tr><tr><td>vertex_size</td><td>uint16</td><td>2</td><td>Size of a single vertex in bytes. <br>The engine primarily relies on this value even if the size calculated from flags is smaller</td></tr><tr><td>vertex_flags</td><td>uint16</td><td>2</td><td>Vertex flag bit mask</td></tr><tr><td>vertices</td><td>struct[vertex_count]</td><td></td><td>Vertex array. <br>Vertex structure depends on the submesh format</td></tr></tbody></table>

#### FVF

Vertex flags are combined using a bit mask.

<table><thead><tr><th width="119.578125">Bit</th><th>Description</th></tr></thead><tbody><tr><td>0x0001</td><td>Full color. <br>If the flag is absent, vertex colors are multiplied by 2 after loading</td></tr><tr><td>0x0002</td><td>Optimized vertex order. <br>If the flag is absent, the engine may attempt to optimize the mesh automatically after loading</td></tr><tr><td>0x0004</td><td>Color verified. <br>*) Not used in GEM RTS</td></tr></tbody></table>

#### Vertices struct\[vertex\_count]

<table><thead><tr><th width="160.82421875">Name</th><th width="107.59375">Type</th><th width="69.90625">Size</th><th>Description</th></tr></thead><tbody><tr><td>position</td><td>float[3]</td><td>12</td><td>Vertex position in local coordinates</td></tr><tr><td>normal</td><td>float[3]</td><td>12</td><td>Normal vector in local coordinates</td></tr><tr><td>tangent</td><td>float[3]</td><td>12</td><td>Tangent vector in local coordinates. Optional</td></tr><tr><td>uv</td><td>float[2]</td><td>8</td><td>Texture UV coordinates. Multiple UV sets are supported</td></tr><tr><td>bone_indices</td><td>uint8[4]</td><td>4</td><td>Indices into the local submesh bone remap</td></tr><tr><td>bone_weights</td><td>uint8[4]</td><td>4</td><td>Bone weights stored in UNORM8 format (0..1)</td></tr></tbody></table>

### Chunk `BAS2`

Terrain grid basis.

```
BAS2
   polygons
       center
       axis_x
       axis_y

   texture
       center
       axis_x
       axis_y
```

| Name             | Type      | Size | Description                                         |
| ---------------- | --------- | ---- | --------------------------------------------------- |
| FourCC           | uint8\[4] | 4    | `BAS2`                                              |
| polygons.center  | float\[2] | 8    | Origin of the triangle generation coordinate system |
| polygons.axis\_x | float\[2] | 8    | X axis of the triangle generation coordinate system |
| polygons.axis\_y | float\[2] | 8    | Y axis of the triangle generation coordinate system |
| texture.center   | float\[2] | 8    | Origin of the UV coordinate system                  |
| texture.axis\_x  | float\[2] | 8    | X axis of UV space                                  |
| texture.axis\_y  | float\[2] | 8    | Y axis of UV space                                  |

### Chunk `InfG`

Terrain triangle grid.

```
InfG
   grid_start_xy
   grid_end_xy
   face_count

   faces[face_count]
       submesh : 15
       invalid : 1
```

<table><thead><tr><th width="134.56640625">Name</th><th width="174.63671875">Type</th><th width="80.75390625">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>InfG</code> <br>Terrain grid</td></tr><tr><td>grid_start_xy</td><td>int32[2]</td><td>8</td><td>Grid starting coordinates</td></tr><tr><td>grid_end_xy</td><td>int32[2]</td><td>8</td><td>Grid ending coordinates</td></tr><tr><td>face_count</td><td>uint32</td><td>4</td><td>Number of triangles</td></tr><tr><td>faces</td><td>uint16[face_count]</td><td></td><td>Triangle data array</td></tr></tbody></table>

#### Bit layout of `faces`

<table><thead><tr><th width="130.953125">Bit</th><th>Description / Value</th></tr></thead><tbody><tr><td>0x7FFF</td><td>Submesh index <code>submesh: 15</code></td></tr><tr><td>0x8000</td><td>Invalid triangle flag <code>invalid: 1</code></td></tr></tbody></table>

### Chunk `BNDS`

Axis-aligned mesh bounding box.

```
BNDS
   min
   max
```

<table><thead><tr><th width="113.6484375">Name</th><th width="105.11328125">Type</th><th width="80.0859375">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>BNDS</code><br>Mesh bounding box</td></tr><tr><td>min</td><td>float[3]</td><td>12</td><td>Minimum XYZ coordinates</td></tr><tr><td>max</td><td>float[3]</td><td>12</td><td>Maximum XYZ coordinates</td></tr></tbody></table>

### Chunk `ADJA`

Information about adjacent triangles.

\*) Not used in GEM RTS.

```
ADJA
   face_count
   adjacencies[face_count]
       neighbours[3]
```

<table><thead><tr><th width="125.87109375">Name</th><th width="167.296875">Type</th><th width="78.6640625">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>ADJA</code><br>Adjacency information</td></tr><tr><td>face_count</td><td>int32</td><td>4</td><td>Number of triangles</td></tr><tr><td>adjacencies</td><td>struct[face_count]</td><td></td><td>Triangle neighbour information</td></tr></tbody></table>

#### Adjacencies (struct)

<table><thead><tr><th width="110.2578125">Name</th><th width="97.23828125">Type</th><th width="82.93359375">Size</th><th>Description</th></tr></thead><tbody><tr><td>neighbours</td><td>uint16[3]</td><td>6</td><td>Indices of neighbouring triangles.<br> <code>0xFFFF</code> means no neighbour</td></tr></tbody></table>

### Chunk `SHDW`

Shadow information.

\*) Not used in GEM RTS.

```
SHDW
   face_count
   shadow_infos[face_count]
```

<table><thead><tr><th width="134.32421875">Name</th><th width="159.14453125">Type</th><th width="80.99609375">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>SHDW</code> <br>Shadow information</td></tr><tr><td>face_count</td><td>int32</td><td>4</td><td>Number of triangles</td></tr><tr><td>shadow_infos</td><td>uint8[face_count]</td><td></td><td><p>Shadow casting flags: </p><ul><li>0: does not cast shadows </li><li>1: casts shadows</li></ul></td></tr></tbody></table>

### Chunk `MROR`

Mirrored mesh flag.

<table><thead><tr><th width="90.2734375">Name</th><th width="115.2578125">Type</th><th width="94.84765625">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>MROR</code> <br>Indicates a mirrored mesh</td></tr></tbody></table>

### Chunk `MARK`

Triangle markup.

\*) Not used in GEM RTS.

```
MARK
   mark_size
   marks[mark_size]
```

<table><thead><tr><th width="118.75390625">Name</th><th width="155.1875">Type</th><th width="80.4296875">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>MARK</code></td></tr><tr><td>mark_size</td><td>uint32</td><td>4</td><td>Size of markup data</td></tr><tr><td>marks</td><td>uint8[mark_size]</td><td></td><td>Binary markup data</td></tr></tbody></table>

### Chunk `SKIN`

Bone map for a skinned mesh.

```
SKIN
   bone_count

   bone_names[bone_count]
       bone_name
```

<table><thead><tr><th width="129.66015625">Name</th><th width="179.41015625">Type</th><th width="82.44921875">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>SKIN</code></td></tr><tr><td>bone_count</td><td>uint32</td><td>4</td><td>Number of bones</td></tr><tr><td>bone_names</td><td>string8[bone_count]</td><td></td><td>Bone names</td></tr></tbody></table>

### Chunk `SUBM` (GEM RTS, GEM3)

Submesh description format used by GEM3 and GEM RTS models.

The file must start with the `PM01` format identifier.

```
SUBM
   vertex_format
   face_start
   face_count
   vertex_start
   vertex_count
   mesh_flags

   [if mesh_flags contains skin]
       bone_count
       bone_map[bone_count]

   material_name
```

<table><thead><tr><th width="156.01953125">Name</th><th width="116.69140625">Type</th><th width="84.8515625">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>SUBM</code></td></tr><tr><td>vertex_format</td><td>uint16</td><td>2</td><td>Vertex format bit mask</td></tr><tr><td>face_start</td><td>uint16</td><td>2</td><td>Index of the first triangle in the submesh</td></tr><tr><td>face_count</td><td>uint16</td><td>2</td><td>Number of triangles in the submesh</td></tr><tr><td>vertex_start</td><td>uint16</td><td>2</td><td>Index of the first vertex in the submesh. Not used in GEM RTS</td></tr><tr><td>vertex_count</td><td>uint16</td><td>2</td><td>Number of vertices in the submesh. Not used in GEM RTS</td></tr><tr><td>mesh_flags</td><td>uint16</td><td>2</td><td>Bit flags describing mesh properties</td></tr><tr><td>material_name</td><td>string8/24</td><td>1+</td><td>Material name</td></tr></tbody></table>

#### Bit field `vertex_format`&#x20;

<table><thead><tr><th width="119.0546875">Value</th><th width="123.96875">Type</th><th>Comment</th></tr></thead><tbody><tr><td>0x0001</td><td>float[3]</td><td>Position</td></tr><tr><td>0x0002</td><td>float[3]</td><td>Normal</td></tr><tr><td>0x0004</td><td>uint8[4]</td><td>Color 0, RGBA</td></tr><tr><td>0x0008</td><td>uint8[4]</td><td>Color 1, RGBA</td></tr><tr><td>0x0010</td><td>float[2]</td><td>Texture coordinates 0</td></tr><tr><td>0x0020</td><td>float[2]</td><td>Texture coordinates 1</td></tr><tr><td>0x0040</td><td>float[2]</td><td>Texture coordinates 2</td></tr><tr><td>0x0080</td><td>float[2]</td><td>Texture coordinates 3</td></tr><tr><td>0x0100</td><td>float[4]</td><td>Tangent</td></tr><tr><td>0x0200</td><td>uint8</td><td>Skinning with 2 bones</td></tr><tr><td>0x0400</td><td>uint8[2]</td><td>Skinning with 3 bones</td></tr><tr><td>0x0600</td><td>uint8[3]</td><td>Skinning with 4 bones. Both 0x0200 and 0x0400 bits are set simultaneously</td></tr><tr><td>0x0800</td><td>float</td><td>Wind amplitude</td></tr></tbody></table>

#### Bit mask for `mesh_flags`&#x20;

<table><thead><tr><th width="121.30078125">Value</th><th>Description</th></tr></thead><tbody><tr><td>0x0001</td><td>Double-sided mesh</td></tr><tr><td>0x0002</td><td>Skin</td></tr></tbody></table>

#### Skin data

Present only if the `0x0002` (`skin`) flag is set in `mesh_flags`.

```
bone_count
bone_map[bone_count]
```

<table><thead><tr><th width="120.35546875">Name</th><th width="169.41015625">Type</th><th width="115.30859375">Size</th><th>Description</th></tr></thead><tbody><tr><td>bone_count</td><td>uint8</td><td>1</td><td>Number of bones used by the skin</td></tr><tr><td>bone_map</td><td>uint8[bone_count]</td><td>bone_count</td><td>Bone indices in the bone map</td></tr></tbody></table>

{% hint style="info" %}
Vertex `bone_indices` reference entries in the local `bone_map` array rather than directly indexing the global `SKIN` chunk. This local remapping allows each submesh to use only the subset of bones required for its vertices.
{% endhint %}

#### Values of `bone_map`&#x20;

<table><thead><tr><th width="107.1015625">Value</th><th>Meaning</th></tr></thead><tbody><tr><td>0</td><td>Use the mesh's global transform matrix</td></tr><tr><td>N > 0</td><td>Use <code>SKIN.bones[N - 1]</code></td></tr></tbody></table>

### Chunk `SBM1`

GEM RTS 1.1+ submesh format used for map meshes.

```
SBM1

vertex_elements[] loop
   type_usage
   usage_index
   stream_index

tags[] loop
   tag_index
   tag_chunk

face_start
face_count
mesh_flags
material_name
```

<table><thead><tr><th width="164.7890625">Name</th><th width="111.14453125">Type</th><th width="76.7265625">Size</th><th>Description</th></tr></thead><tbody><tr><td>FourCC</td><td>uint8[4]</td><td>4</td><td><code>SBM1</code></td></tr><tr><td>vertex_elements</td><td>struct[]</td><td></td><td>Vertex element descriptors</td></tr><tr><td>tags</td><td>struct[]</td><td></td><td>List of custom vertex tags</td></tr><tr><td>face_start</td><td>int32</td><td>4</td><td>Index of the first triangle in the submesh</td></tr><tr><td>face_count</td><td>int32</td><td>4</td><td>Number of triangles in the submesh</td></tr><tr><td>mesh_flags</td><td>uint32</td><td>4</td><td>Mesh bit flags</td></tr><tr><td>material_name</td><td>string8/24</td><td>1+</td><td>Material name</td></tr></tbody></table>

#### Vertex format elements

The `vertex_elements[]` array describes the vertex layout.

The list does not contain an element count and is read sequentially until a terminator element is encountered.

The `type_usage` field stores both the element type and its semantic:

* `(type_usage & 0x0F)` specifies the element type.
* `(type_usage & 0xF0) >> 4` specifies the element usage.

The value `0xFF` is used as the list terminator and ends vertex format parsing.

#### Element structure

<table><thead><tr><th width="125.26953125">Name</th><th width="86.48828125">Type</th><th width="79.5859375">Size</th><th>Description</th></tr></thead><tbody><tr><td>type_usage</td><td>uint8</td><td>1</td><td>Element type and semantic</td></tr><tr><td>usage_index</td><td>uint8</td><td>1</td><td>Usage index</td></tr><tr><td>stream_index</td><td>uint8</td><td>1</td><td>Stream index</td></tr></tbody></table>

#### Element type

Determined by the lower four bits of `type_usage`.

<table><thead><tr><th width="86.5859375">Value</th><th width="112.00390625">Name</th><th width="90.15625">Type</th><th>Comment</th></tr></thead><tbody><tr><td>0</td><td>FLOAT1</td><td>float</td><td></td></tr><tr><td>1</td><td>FLOAT2</td><td>float[2]</td><td></td></tr><tr><td>2</td><td>FLOAT3</td><td>float[3]</td><td></td></tr><tr><td>3</td><td>FLOAT4</td><td>float[4]</td><td></td></tr><tr><td>4</td><td>SHORT2</td><td>int16[2]</td><td></td></tr><tr><td>5</td><td>BYTE4</td><td>uint8[4]</td><td></td></tr><tr><td>6</td><td>COLOR</td><td>uint8[4]</td><td>RGBA</td></tr><tr><td>7</td><td>HALF4</td><td>float[4]</td><td>Half-precision</td></tr><tr><td>8</td><td>BYTE4N</td><td>uint8[4]</td><td>UNORM8</td></tr></tbody></table>

#### Element usage

Determined by the upper four bits of `type_usage`.

```
usage = high nibble of type_usage
```

| Value | Description   |
| ----- | ------------- |
| 0     | POSITION      |
| 1     | NORMAL        |
| 2     | COLOR         |
| 3     | TEXCOORD      |
| 4     | BLEND WEIGHT  |
| 5     | BLEND INDICES |
| 6     | TANGENT       |

#### Vertex tags

The `tags[]` list contains additional user-defined tags for vertex elements.

The list does not contain an element count and is read sequentially until a terminator element is encountered.

The value `0xFF` is used as the tag list terminator.

#### Tag structure

| Name       | Type   | Size | Description          |
| ---------- | ------ | ---- | -------------------- |
| tag\_index | uint8  | 1    | Vertex element index |
| tag\_chunk | uint32 | 4    | Tag identifier       |

#### Bit mask for  `mesh_flags`&#x20;

<table><thead><tr><th width="128.30859375">Value</th><th>Description</th></tr></thead><tbody><tr><td>0x0001</td><td>Two-sided mesh (render without back-face culling)</td></tr><tr><td>0x0002</td><td>Use alpha (compatibility mode)</td></tr><tr><td>0x0004</td><td>Use realtime scene lighting</td></tr><tr><td>0x0008</td><td>Use player color lighting</td></tr><tr><td>0x0010</td><td>Skinned mesh</td></tr><tr><td>0x0020</td><td>Shadow volume mesh</td></tr><tr><td>0x0040</td><td>Mirrored mesh (negative scaling)</td></tr><tr><td>0x0080</td><td>Blend by second texture alpha</td></tr><tr><td>0x0100</td><td>Bump mapping (compatibility mode)</td></tr><tr><td>0x0200</td><td>Specular color is stored</td></tr><tr><td>0x0400</td><td>Uses materials instead of textures</td></tr><tr><td>0x0800</td><td>Sub-skin feature</td></tr><tr><td>0x1000</td><td>Two textures share a single texture coordinate set</td></tr><tr><td>0x2000</td><td>Uses VD (compatibility mode)</td></tr><tr><td>0x4000</td><td>Contains a lightmap</td></tr></tbody></table>

### Chunk `MESH`

Legacy submesh format used in [*Faces of War*](https://bestway.com.ua/games/faces-of-war/) and [*Men of War*](https://bestway.com.ua/games/men-of-war/).

```
MESH
   fvf
   face_start
   face_count
   mesh_flags

   [if mesh_flags contains specular]
       specular_color

   [if mesh_flags contains use material]
       material_name

   [otherwise]
       texture data
           [if mesh_flags contains specular or bump]
               diffuse
                   [if mesh_flags contains specular]
                       specular_texture
                   [if mesh_flags contains bump]
                       bump_texture
           [otherwise]
               [if uv == 2 or mesh_flags contains "two textures with one texcoord"]
                   diffuse_0
                   diffuse_1
               [otherwise]
                   diffuse

   [if mesh_flags contains skinned mesh]
       bone_map_size
       bone_map[bone_map_size]
```

| Name        | Type      | Size | Description                                       |
| ----------- | --------- | ---- | ------------------------------------------------- |
| FourCC      | uint8\[4] | 4    | <p><code>MESH</code><br>Legacy submesh format</p> |
| fvf         | uint32    | 4    | Vertex format bit mask                            |
| face\_start | int32     | 4    | Index of the first triangle                       |
| face\_count | int32     | 4    | Number of triangles                               |
| mesh\_flags | uint32    | 4    | Mesh bit flags                                    |

#### Vertex format flags (FVF)

Bit mask describing the vertex format.

The `fvf` field contains a `uint32` bit mask. The values shown below represent vertex layouts encountered in the format. If a value not listed in the table is found in a file, it should be interpreted as a combination of the corresponding flags.

For example, the value `0x00000012` is stored in the file as the byte sequence `12 00 00 00` (Little-endian). If flags `0x00000002` and `0x00000010` correspond to `position` and `normal`, then `0x00000012` indicates that both fields are present in the vertex.

| Mask       | Description                                                      |
| ---------- | ---------------------------------------------------------------- |
| 0x002      | `position float[3]`                                              |
| 0x006      | `position float[3] blend_weight uint8 blend_indices uint8[4]`    |
| 0x008      | `position float[3] blend_weight uint8[2] blend_indices uint8[4]` |
| 0x00A      | `position float[3] blend_weight uint8[3] blend_indices uint8[4]` |
| 0x010      | `normal float[3]`                                                |
| 0x040      | `diffuse_color uint8[4]`                                         |
| 0x080      | `specular_color uint8[4]`                                        |
| 0xN00      | `texcoord_mask N × float[2]`                                     |
| 0x8000E000 | `tangent float[4]`                                               |

#### Bit mask for  `mesh_flags`&#x20;

<table><thead><tr><th width="119.578125">Value</th><th>Description</th></tr></thead><tbody><tr><td>0x0001</td><td>Two-sided mesh (render without back-face culling)</td></tr><tr><td>0x0002</td><td>Use alpha (compatibility mode)</td></tr><tr><td>0x0004</td><td>Use realtime scene lighting</td></tr><tr><td>0x0008</td><td>Use player color lighting</td></tr><tr><td>0x0010</td><td>Skinned mesh</td></tr><tr><td>0x0020</td><td>Shadow volume mesh</td></tr><tr><td>0x0040</td><td>Mirrored (negative scaling)</td></tr><tr><td>0x0080</td><td>Blend by second texture alpha</td></tr><tr><td>0x0100</td><td>Bump (compatibility mode)</td></tr><tr><td>0x0200</td><td>Specular</td></tr><tr><td>0x0400</td><td>Use material</td></tr><tr><td>0x0800</td><td>Sub-skin feature</td></tr><tr><td>0x1000</td><td>Two textures with one texcoord</td></tr><tr><td>0x2000</td><td>Using VD (compatibility mode)</td></tr><tr><td>0x4000</td><td>Has lightmap</td></tr></tbody></table>

#### Specular color

Present if `mesh_flags` contains `0x0200` (`specular`).

| Name            | Type      | Size | Description    |
| --------------- | --------- | ---- | -------------- |
| specular\_color | uint8\[4] | 4    | Specular color |

#### Material name

Present if `mesh_flags` contains `0x0400` (`use material`).

| Name           | Type       | Size | Description   |
| -------------- | ---------- | ---- | ------------- |
| material\_name | string8/24 | 1+   | Material name |

#### Texture data

Texture data is loaded when `mesh_flags` does not contain `0x0400` (`use material`). The structure depends on the `specular`, `bump`, number of texture coordinate sets (UV), and the `two textures with one texcoord` flag.

#### Texture data for standard material

Used when `mesh_flags` contains `specular` or `bump`.

<table><thead><tr><th width="162.1328125">Name</th><th width="104.28515625">Type</th><th width="76.80078125">Size</th><th>Description</th></tr></thead><tbody><tr><td>diffuse</td><td>string8/24</td><td>1+</td><td>Diffuse texture name</td></tr><tr><td>specular_texture</td><td>string8/24</td><td>1+</td><td>Specular texture name. Present if <code>mesh_flags</code> contains <code>specular</code></td></tr><tr><td>bump_texture</td><td>string8/24</td><td>1+</td><td>Normal map texture name. Present if <code>mesh_flags</code> contains <code>bump</code></td></tr></tbody></table>

#### Texture data for two textures

Used if the standard material path is not used and one of the following conditions is true:

* `uv == 2`
* `mesh_flags` contains `two textures with one texcoord`

| Name       | Type       | Size | Description            |
| ---------- | ---------- | ---- | ---------------------- |
| diffuse\_0 | string8/24 | 1+   | Diffuse texture 0 name |
| diffuse\_1 | string8/24 | 1+   | Diffuse texture 1 name |

#### Texture data for one texture

Used if the standard material path is not used and the conditions for using two textures are not met.

| Name    | Type       | Size | Description          |
| ------- | ---------- | ---- | -------------------- |
| diffuse | string8/24 | 1+   | Diffuse texture name |

#### Skin data

Skin data is present if `mesh_flags` contains `skinned mesh`.

<table><thead><tr><th width="152.015625">Name</th><th width="192.71484375">Type</th><th width="148.43359375">Size</th><th>Description</th></tr></thead><tbody><tr><td>bone_map_size</td><td>uint8</td><td>1</td><td>Size of the local bone remapping table</td></tr><tr><td>bone_map</td><td>uint8[bone_map_size]</td><td>bone_map_size</td><td>Local bone remapping table</td></tr></tbody></table>

The `bone_map` table is used to map submesh bones to the global bone map stored in the `SKIN` chunk.

Each array element contains the index of a bone from `SKIN`, incremented by 1.

Vertex `bone_index` values reference entries in the `bone_map` array rather than directly indexing bones in `SKIN`.

This allows a submesh to use only the subset of bones required by its vertices.

#### Values of `bone_map`&#x20;

<table><thead><tr><th width="88.81640625">Value</th><th>Meaning</th></tr></thead><tbody><tr><td>0</td><td>Use the mesh's global transform matrix</td></tr><tr><td>N > 0</td><td>Use <code>SKIN.bones[N - 1]</code></td></tr></tbody></table>

### Chunk `SUBM` (Assault Squad 2)

Submesh description format used by map meshes from *Assault Squad 2*. This format is used when the starting chunk is not `PM01`.

```
SUBM
   vertex_elements[]
       type_usage

   face_start
   face_count
   unknown[2]
   mesh_flags
   material_name
```

| Name             | Type       | Size | Description                                |
| ---------------- | ---------- | ---- | ------------------------------------------ |
| FourCC           | uint8\[4]  | 4    | SUBM                                       |
| vertex\_elements | struct\[]  |      | List of vertex format elements             |
| face\_start      | uint32     | 4    | Index of the first triangle in the submesh |
| face\_count      | uint32     | 4    | Number of triangles in the submesh         |
| unknown\[2]      | uint32\[2] | 8    | Unknown data                               |
| mesh\_flags      | uint32     | 4    | Mesh bit flags                             |
| material\_name   | string8    | 1+   | Material name                              |

#### Vertex elements info

The `vertex_elements[]` list describes the vertex layout.

The list does not contain an element count and is read sequentially until a terminator element is encountered.

The `type_usage` field stores both the element type and its semantic.

* `(type_usage & 0x0F)` determines the element type.
* `(type_usage & 0xF0) >> 4` determines the element usage.

The value `0xFF` is used as the list terminator and ends vertex format parsing.

| Name        | Type  | Size | Description               |
| ----------- | ----- | ---- | ------------------------- |
| type\_usage | uint8 | 1    | Element type and semantic |

#### Element type

Determined by the lower four bits of the `type_usage` field.

<table><thead><tr><th width="75.28515625">Value</th><th width="104.07421875">Name</th><th width="105.12109375">Type</th><th>Comment</th></tr></thead><tbody><tr><td>0</td><td>FLOAT1</td><td>float</td><td></td></tr><tr><td>1</td><td>FLOAT2</td><td>float[2]</td><td></td></tr><tr><td>2</td><td>FLOAT3</td><td>float[3]</td><td></td></tr><tr><td>3</td><td>FLOAT4</td><td>float[4]</td><td></td></tr><tr><td>4</td><td>SHORT2</td><td>int16[2]</td><td></td></tr><tr><td>5</td><td>BYTE4</td><td>uint8[4]</td><td></td></tr><tr><td>6</td><td>COLOR</td><td>uint8[4]</td><td>RGBA</td></tr><tr><td>7</td><td>HALF4</td><td>float[4]</td><td>Half-precision</td></tr><tr><td>8</td><td>BYTE4N</td><td>uint8[4]</td><td>UNORM8</td></tr></tbody></table>

#### Element usage

Determined by the upper four bits of the `type_usage` field.

<table><thead><tr><th width="102.59765625">Value</th><th>Description</th></tr></thead><tbody><tr><td>0</td><td>POSITION</td></tr><tr><td>1</td><td>NORMAL</td></tr><tr><td>2</td><td>COLOR</td></tr><tr><td>3</td><td>TEXCOORD</td></tr><tr><td>4</td><td>BLEND WEIGHT</td></tr><tr><td>5</td><td>BLEND INDICES</td></tr><tr><td>6</td><td>TANGENT</td></tr></tbody></table>

#### Bit mask for `mesh_flags`&#x20;

<table><thead><tr><th width="133.25390625">Value</th><th>Description</th></tr></thead><tbody><tr><td><code>0x0001</code></td><td>Two-sided mesh (render this mesh without culling)</td></tr><tr><td><code>0x0002</code></td><td>Use alpha (compatibility mode)</td></tr><tr><td><code>0x0004</code></td><td>Use realtime scene lighting</td></tr><tr><td><code>0x0008</code></td><td>Use player color lighting</td></tr><tr><td><code>0x0010</code></td><td>Skinned mesh</td></tr><tr><td><code>0x0020</code></td><td>Shadow volume mesh</td></tr><tr><td><code>0x0040</code></td><td>Mirrored (has negative scaling)</td></tr><tr><td><code>0x0080</code></td><td>Blend by second texture alpha</td></tr><tr><td><code>0x0100</code></td><td>Bump (compatibility mode)</td></tr><tr><td><code>0x0200</code></td><td>Specular color stored</td></tr><tr><td><code>0x0400</code></td><td>Format with material instead of textures</td></tr><tr><td><code>0x0800</code></td><td>Sub-skin feature</td></tr><tr><td><code>0x1000</code></td><td>Two textures with one texcoord</td></tr><tr><td><code>0x2000</code></td><td>Using VD (compatibility mode)</td></tr><tr><td><code>0x4000</code></td><td>Has lightmap</td></tr></tbody></table>
