Mdl format specification

Type: Mdl

Mdl is the 3D model format of Silent Hill 2 (PC). It describes geometry, textures, skeleton data, and facial animation. This documentation does not describe the PS2 mdl format.

Offset ID Type
0 header FileHeader
64 model_data Model
header.texture_header_offset texture_data TextureData

Type: FileHeader

Offset ID Type Note
0 no_texture_id u1 Indicates whether this model reuses a texture from another model (1), or if it has its own texture (0). Equal to 1 for models with "notex" in the filename, e.g. "hll_jms_notex.mdl".
1 padding
4 character_id u4le Internal ID used by the game engine for this model.
8 texture_count u4le Number of textures in this model.
12 texture_header_offset u4le Absolute byte offset to the start of texture data.
16 model_header_offset u4le Absolute byte offset to the start of general model data.
20 kg1_header_offset u4le Absolute byte offset to the start of embedded shadow data.

Type: Model

Model container. All offsets are relative to the start of this header.

Offset ID Type Note
0 magic 0x03 0x00 0xff 0xff is the start of the model header.
4 model_version u4le
8 initial_matrices_offset u4le Offset to initial bone matrices in model space.
12 bone_count u4le Number of bones.
16 skeleton_data_offset u4le Offset to skeleton data.
20 bone_pairs_count u4le See bone_pair for more info.
24 bone_pairs_offset u4le Offset to bone pairs, specified in pairs of bytes.
28 default_pcms_offset u4le Offset to default parent-child matrices for bone pairs.
32 opaque_primitive_headers_count u4le Number of opaque model parts.
36 opaque_primitive_headers_offset u4le Offset to headers describing each primitive.
40 transparent_primitive_headers_count u4le See transparent_primitive_header for more info.
44 transparent_primitive_headers_offset u4le See transparent_primitive_header for more info.
48 texture_blocks_count u4le Number of texture blocks.
52 texture_blocks_offset u4le Offset to the texture blocks.
56 texture_id_count u4le Number of unique texture IDs used by the game engine.
60 texture_id_offset u4le Offset to texture ID data.
64 junk_padding_offset u4le See junk_padding.
68 cluster_node_count u4le Number of morph vertices for this object.
72 cluster_node_offset u4le Offset to morph vertex data for this object.
76 cluster_count u4le Number of morph targets for this object.
80 cluster_offset u4le Offset to morph targets for this object.
84 func_data_count u4le Unknown.
88 func_data_offset u4le Unknown.
92 hit_offset u4le Unknown.
96 box_offset u4le Unknown.
100 flag u4le Unknown flag.
104 relative_matrices_offset u4le Unknown.
108 relative_trans_offset u4le Unknown.
112 reserved
128 opaque_vertex_count u4le Number of opaque vertices.
132 opaque_vertex_data_offset u4le Offset to opaque vertex data.
136 transparent_vertex_count u4le Number of transparent vertices.
140 transparent_vertex_data_offset u4le Offset to transparent vertex data.
144 opaque_triangle_index_offset u4le Offset to opaque triangle index data.
148 transparent_triangle_index_offset u4le Offset to transparent triangle index data.
152 opaque_cluster_map_count u4le Unknown original name.
156 transparent_cluster_map_count u4le Unknown original name.
160 cluster_map_offset u4le Unknown original name.
164 pad0
176 initial_matrices TransformationMatrix Matrices that represent the pose of each bone in model space. This is an array of matrices where initial_matrices[i]` goes with bone `i.
(computed) skeleton_tree u1 A graph having a tree structure that represents the skeleton. This is an array of indices where bone i is the parent of skeleton_tree[i]`. If `skeleton_tree[i]` is 255, then the bone `i is parented to a hardcoded root node.
(computed) pad1 u1
(computed) bone_pairs BonePair
(computed) pad2 u1
(computed) default_pcms_matrices TransformationMatrix Matrices that represent relative transformations between bones. For index i`, let `parent` equal `bone_pairs[i].parent` and `child equal bone_pairs[i].child`. Then `default_pcms_matrices[i] is equal to inverse(initial_matrices[child]) * initial_matrices[parent].
(computed) texture_metadata TextureMetadata
(junk_padding_offset + 64) junk_padding A sequence of copied bytes used to pad out the file in the PC version. It starts copying from the start of this header until it reaches the cluster node data. On the PS2 version, this space is used for a block called "texture_id_params".
(((cluster_node_offset + (cluster_node_count * 6)) + 64) + cluster_node_padding_amount) cluster_node_normals S2Vector
(opaque_primitive_headers_offset + 64) geometry Geometry Container for all model parts.
(cluster_node_offset + 64) cluster_nodes S2Vector Morph vertices and normals for facial animation.
(cluster_map_offset + 64) cluster_maps ClusterMaps Unknown original name. Describes how morphs influence geometry.
(cluster_offset + 64) clusters Cluster Morph targets for facial animation.

Type: BonePair

Each vertex specifies up to three indices of bone pairs. For each pair, the child bone can have a weighted influence on the bone, while the parent should be the vertex's parent bone.

Offset ID Type
0 parent u1
1 child u1

Type: SpriteHeader

Offset ID Type
0 sprite_id u4le
4 x u2le
6 y u2le
8 width u2le
10 height u2le
12 format u1→TextureFormat
13 unknown0 u1
14 importance u2le
16 data_size u4le
20 all_size u4le
24 pad
28 unknown1 u1
29 unknown2 u1
30 end_magic u2le

Enum: texture_format

ID Name
0 dxt1
1 dxt2
2 dxt3
3 dxt4
4 dxt5
8 paletted
24 rgbx8
32 rgba8

Type: ClusterMapping

Unknown original IDs for all properties.

Offset ID Type
0 source_start_index u2le
2 target_start_index u2le
4 count u2le

Type: OpaqueVertexData

Offset ID Type Note
0 x f4le The x-coordinate of the vertex.
4 y f4le The y-coordinate of the vertex.
8 z f4le The z-coordinate of the vertex.
12 bone_weight_0 f4le The first bone weight of the vertex.
16 bone_weight_1 f4le The second bone weight of the vertex.
20 bone_weight_2 f4le The third bone weight of the vertex.
24 bone_weight_3 f4le The fourth bone weight of the vertex.
28 normals s2le
34 alignment u2le
36 u f4le The texture coordinate along the horizontal axis (x), from 0 to 1.
40 v f4le The texture coordinate along the vertical axis (y), from 0 to 1.
44 bone_index_0 u1 The first bone index. This indexes into the primitive bone array, not the overall skeleton array!
45 bone_index_1 u1 The first bone pair index.
46 bone_index_2 u1 The second bone pair index.
47 bone_index_3 u1 The third bone pair index.

Type: ClusterData

Offset ID Type
0 vector S2Vector
6 vertex_index s2le

Type: TransparentPrimitiveHeader

On the PS2 version, this field is called "n_vu0_parts", suggesting that these were handled by the VU0 coprocessor, while the opaque primitive headers were handled by the VU1 coprocessor.

Offset ID Type Note
0 pad0
4 texture_index_count u4le
8 texture_index_offset u4le
12 marker_offset u4le
16 unknown_count u4le
20 unknown_section0
44 pad1 u4le
48 unknown_floats0 f4le
60 pad2 u4le
64 unknown_floats1 f4le
76 unknown_section1
96 primitive_start_index u4le Offset into the triangle index array where the primitive begins.
100 primitive_length u4le The length of the primitive in the triangle index array.
104 primitive_index u4le Appears to be an array index for this primitive header.
108 texture_index u4le
112 pad3
124 sampler_states

Type: OpaquePrimitiveHeaderWrapper

Offset ID Type
0 primitive_header_size u4le
4 body OpaquePrimitiveHeader

Type: Cluster

Offset ID Type
0 node_count u4le
4 offset u4le
(offset + 64) data ClusterDataList

Type: TextureMetadata

Offset ID Type
0 main_texture_ids u4le
(computed) pad
(computed) texture_pairs TexturePair

Type: S2Vector

Offset ID Type
0 x s2le
2 y s2le
4 z s2le

Type: TexturePair

Offset ID Type
0 texture_index u4le
4 sprite_id u4le

Type: ClusterDataList

Offset ID Type
0 vertices ClusterData
(computed) alignment
(computed) normals ClusterData

Type: ClusterMaps

Offset ID Type
0 opaque ClusterMapping
(computed) transparent ClusterMapping

Type: TransparentPrimitiveHeaderWrapper

Offset ID Type
0 transparent_primitive_header_size u4le
4 body TransparentPrimitiveHeader

Type: TextureContainer

Offset ID Type
0 texture_id u4le
4 width u2le
6 height u2le
8 width2 u2le
10 height2 u2le
12 sprite_count u2le
14 unknown_section
32 sprite_headers SpriteHeader
(computed) data

Type: IndexList

Offset ID Type
0 array u2le

Type: TransparentVertexData

Offset ID Type Note
0 x f4le The x-coordinate of the vertex.
4 y f4le The y-coordinate of the vertex.
8 z f4le The z-coordinate of the vertex.
12 w f4le
16 bone_weights f4le
32 normal_x f4le The x-coordinate of the normal vector.
36 normal_y f4le The y-coordinate of the normal vector.
40 normal_z f4le The z-coordinate of the normal vector.
44 unknown1
48 u f4le The texture coordinate along the horizontal axis (x), from 0 to 1.
52 v f4le The texture coordinate along the vertical axis (y), from 0 to 1.
56 unknown2
64 bone_index u1
65 unknown3 u1
66 bone_pair_index0 u1
67 unknown4 u1
68 bone_pair_index1 u1
69 unknown5 u1
70 bone_pair_index2 u1
71 unknown6 u1

Type: OpaquePrimitiveHeader

Description for model parts. In this case, the primitives are triangle strips. The triangle list can contain degenerate triangles that are used to separate strips.

Offset ID Type Note
0 pad0
4 bone_count u4le Number of bones that this primitive depends on.
8 bone_indices_offset u4le Offset from this header to a bone list. See bone_indices.
12 bone_pairs_count u4le Number of bone pairs that this primitive depends on.
16 bone_pairs_offset u4le Offset to a bone pair indices list. See bone_pair_indices.
20 texture_index_count u4le Appears to be the texture indices for this primitive?
24 texture_index_offset u4le Appears to be the texture index offset for this primitive?
28 sampler_states_offset u4le From FF24, this is an offset to ADDRESSU, ADDRESSV, MAGFILTER and MINFILTER sampler states.
32 material_type u1→MaterialType See FrozenFish24's SH2MapTools/Sh2ModelMaterialEditor/Model.py#L75
33 unknown_byte0 u1 Possibly material-related, see material_type.
34 pose_index u1 If zero, this primitive is always visible. Otherwise, it may be hidden and swapped out at various times, e.g. for James's hands.
35 unknown_byte1 u1
36 backface_culling u4le
40 unknown_float0 f4le From FF24, reported to affect diffuse color somehow.
44 unknown_float1 f4le From FF24, reported to affect ambient color somehow.
48 specular_scale f4le From FF24, larger value = smaller specular.
52 unknown_section0 Unknown purpose.
60 pad1
64 diffuse_color f4le
76 pad2
80 ambient_color f4le
92 pad3
96 specular_color f4le
108 pad4
112 primitive_start_index u4le Offset into the triangle index array where the primitive begins.
116 primitive_length u4le The length of the primitive in the triangle index array.
120 pad5
124 bone_indices u2le The bone index array from this primitive. An important point is that the bone indices specified by a given vertex go into this array, not the overall skeleton array. Unclear why these are u2 if bones are u1?
(bone_pairs_offset - 4) bone_pair_indices IndexList A list of bone pair indices. See bone_pair.
(texture_index_offset - 4) texture_indices IndexList A list of texture indices? TODO
(sampler_states_offset - 4) sampler_states u1

Enum: material_type

ID Name
1 unlit
2 matte
3 matte_plus
4 glossy

Type: Geometry

Offset ID Type Note
0 opaque_primitive_headers OpaquePrimitiveHeaderWrapper
(computed) opaque_triangle_indices IndexList List of vertex indices, which represent triangle strips.
(_root.model_data.opaque_vertex_data_offset + 64) opaque_vertex_list OpaqueVertexData
(_root.model_data.transparent_primitive_headers_offset + 64) transparent_primitive_headers TransparentPrimitiveHeaderWrapper
(_root.model_data.transparent_triangle_index_offset + 64) transparent_triangle_indices IndexList List of vertex indices, which represent triangle strips.
(_root.model_data.transparent_vertex_data_offset + 64) transparent_vertex_list TransparentVertexData

Type: TextureData

Offset ID Type
0 magic 0x01 0x09 0x99 0x19 is the start of the texture header.
4 unknown
16 textures TextureContainer

Type: TransformationMatrix

Represents a 4x4 column-major transformation matrix.

Offset ID Type
0 rotation00 f4le
4 rotation10 f4le
8 rotation20 f4le
12 pad0 f4le
16 rotation01 f4le
20 rotation11 f4le
24 rotation21 f4le
28 pad1 f4le
32 rotation02 f4le
36 rotation12 f4le
40 rotation22 f4le
44 pad2 f4le
48 translation_x f4le
52 translation_y f4le
56 translation_z f4le
60 translation_w f4le