# MSH Файл `*.msh` является NRes-контейнером. Geometry, узлы, slots, batches, animation и служебные streams лежат в entries с разными `type_id`. ## Entry map ```text type 1 nodes and slot selection type 2 header 0x8C + Slot68 records type 3 positions float3 type 4 packed normals type 5 packed UV0 type 6 index buffer u16 type 7 triangle descriptors type 8 animation keys type 9 service stream type 10 strings and node names type 13 Batch20 records type 15 auxiliary stream type 17 auxiliary data type 18 rare stream type 19 animation frame map type 20 rare auxiliary table ``` Reader ищет entries по type, но сохраняет исходный порядок для roundtrip. ## Node and slot selection Type 1 обычно состоит из records по 38 bytes: ```c struct Node38 { uint16_t hdr0; uint16_t parent_or_link; uint16_t anim_map_start; uint16_t fallback_key; uint16_t slot_index[15]; }; ``` `slot_index[lod * 5 + group]` выбирает geometry slot. `0xFFFF` означает отсутствие геометрии для комбинации LOD/group. ## Slot and batch Type 2 содержит header `0x8C`, затем `Slot68`: ```c struct Slot68 { uint16_t tri_start; uint16_t tri_count; uint16_t batch_start; uint16_t batch_count; float aabb_min[3]; float aabb_max[3]; float sphere_center[3]; float sphere_radius; uint32_t opaque[5]; }; ``` Type 13 задаёт draw ranges: ```c #pragma pack(push, 1) struct Batch20 { uint16_t batch_flags; uint16_t material_index; uint16_t opaque4; uint16_t opaque6; uint16_t index_count; uint32_t index_start; uint16_t opaque14; uint32_t base_vertex; }; #pragma pack(pop) ``` Index check выполняется как `base_vertex + index < vertex_count` для всего используемого slice.