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 |
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. |
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. |
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 |
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 |
ID | Name |
---|---|
0 | dxt1 |
1 | dxt2 |
2 | dxt3 |
3 | dxt4 |
4 | dxt5 |
8 | paletted |
24 | rgbx8 |
32 | rgba8 |
Unknown original IDs for all properties.
Offset | ID | Type |
---|---|---|
0 | source_start_index | u2le |
2 | target_start_index | u2le |
4 | count | u2le |
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. |
Offset | ID | Type |
---|---|---|
0 | vector | S2Vector |
6 | vertex_index | s2le |
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 |
Offset | ID | Type |
---|---|---|
0 | primitive_header_size | u4le |
4 | body | OpaquePrimitiveHeader |
Offset | ID | Type |
---|---|---|
0 | node_count | u4le |
4 | offset | u4le |
(offset + 64) | data | ClusterDataList |
Offset | ID | Type |
---|---|---|
0 | main_texture_ids | u4le |
(computed) | pad | |
(computed) | texture_pairs | TexturePair |
Offset | ID | Type |
---|---|---|
0 | x | s2le |
2 | y | s2le |
4 | z | s2le |
Offset | ID | Type |
---|---|---|
0 | texture_index | u4le |
4 | sprite_id | u4le |
Offset | ID | Type |
---|---|---|
0 | vertices | ClusterData |
(computed) | alignment | |
(computed) | normals | ClusterData |
Offset | ID | Type |
---|---|---|
0 | opaque | ClusterMapping |
(computed) | transparent | ClusterMapping |
Offset | ID | Type |
---|---|---|
0 | transparent_primitive_header_size | u4le |
4 | body | TransparentPrimitiveHeader |
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 |
Offset | ID | Type |
---|---|---|
0 | array | u2le |
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 |
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 |
ID | Name |
---|---|
1 | unlit |
2 | matte |
3 | matte_plus |
4 | glossy |
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 |
Offset | ID | Type |
---|---|---|
0 | magic | 0x01 0x09 0x99 0x19 is the start of the texture header. |
4 | unknown | |
16 | textures | TextureContainer |
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 |