Expand description
MDL binary model support. MDL binary model reader and writer.
MDL is the 3D model format used by the Odyssey engine (KotOR/KotOR2). Each model consists of two files: an MDL file containing the node tree, controllers, and metadata, and a companion MDX file containing interleaved per-vertex attribute data.
The binary variant uses a 12-byte wrapper followed by a memory-mapped content blob. All internal offsets are content-relative (byte 0 = wrapper end = on-disk byte 12).
§File Layout
MDL file: MDX file:
+------------------------------+ +------------------------------+
| Wrapper (12 bytes) | | Mesh 1 vertex data |
| - zero_marker (u32) | | (stride * vertex_count bytes)|
| - mdl_content_size (u32) | | + terminator (1 stride row) |
| - mdx_file_size (u32) | +- - - - - - - - - - - - - - -+
+------------------------------+ | padding to 16-byte boundary |
| Geometry Header (80 bytes) | +------------------------------+
| Model Header (116 bytes) | | Mesh 2 vertex data |
+------------------------------+ | + terminator |
| Name Offset Array (u32[]) | +- - - - - - - - - - - - - - -+
| Name Strings (null-term) | | ... |
+------------------------------+ +------------------------------+
| Animation Header Array | | Last mesh vertex data |
| (136 bytes * anim_count) | | + terminator (no final pad) |
+------------------------------+ +------------------------------+
| Node Tree (recursive DFS) |
| - Node header (80 bytes) |
| - Type-specific extra data |
| - Controller keys + data |
| - Children (recurse) |
+------------------------------+
| Animation Node Trees |
| (one tree per animation) |
+------------------------------+§Wrapper (12 bytes)
0x00..0x04 zero_marker (u32, always 0)
0x04..0x08 mdl_content_size (u32, bytes after wrapper)
0x08..0x0C mdx_file_size (u32, companion MDX total size)§Geometry Header (80 bytes, content +0x00..+0x4F)
0x00..0x04 fn_ptr1 (u32, leaked toolset vtable pointer)
0x04..0x08 fn_ptr2 (u32, leaked toolset vtable pointer)
0x08..0x28 model_name (char[32], null-terminated)
0x28..0x2C root_node_ptr (u32, content-relative)
0x2C..0x30 node_count (u32)
0x30..0x3C runtime_arr1 (12 bytes, zeros on disk)
0x3C..0x48 runtime_arr2 (12 bytes, zeros on disk)
0x48..0x4C ref_count (u32, zero on disk)
0x4C..0x4D model_type (u8, always 2 for geometry)
0x4D..0x50 padding (3 bytes)§Model Header (116 bytes, content +0x50..+0xC3)
0x50 classification (u8: 0=Other, 1=Effect, 2=Tile, 4=Character, 8=Door)
0x51 subclassification (u8)
0x52 unknown_52 (u8, always 0 in vanilla)
0x53 affected_by_fog (u8, 0 or 1)
0x54..0x58 num_child_models (u32, always 0 in vanilla K1)
0x58..0x64 animation_arr (CExoArrayList: ptr/count/alloc)
0x64..0x68 supermodel_ref (u32, always 0 in vanilla K1)
0x68..0x74 bounding_min (3x f32)
0x74..0x80 bounding_max (3x f32)
0x80..0x84 radius (f32, bounding sphere)
0x84..0x88 animation_scale (f32, default 1.0)
0x88..0xA8 supermodel_name (char[32], null-terminated)
0xA8..0xAC off_anim_root (u32, content-relative; usually = root_node_ptr)
0xAC..0xB0 padding (4 bytes)
0xB0..0xB4 mdx_size (u32, total MDX data size)
0xB4..0xB8 mdx_offset (u32, always 0 in vanilla K1)
0xB8..0xBC name_offsets_ptr (u32, content-relative -> u32[] name pointers)
0xBC..0xC0 name_count (u32)§Node Header (80 bytes)
0x00..0x02 type_flags (u16, bitfield: see node_flags module)
0x02..0x04 node_number (u16, name_index for geometry / anim mapping for anims)
0x04..0x06 name_index (u16, index into name table)
0x06..0x08 padding (u16)
0x08..0x0C off_root (u32, always 0 in vanilla)
0x0C..0x10 off_parent (u32, content-relative pointer to parent)
0x10..0x1C position (3x f32: x, y, z)
0x1C..0x2C orientation (4x f32: w, x, y, z quaternion)
0x2C..0x38 children_arr (CExoArrayList: ptr/count/alloc)
0x38..0x44 ctrl_key_arr (CExoArrayList: ptr/count/alloc)
0x44..0x50 ctrl_data_arr (CExoArrayList: ptr/count/alloc)§TriMesh Extra Header (332 bytes, node +0x50..+0x19B)
+0x00..+0x08 fn_ptr stubs (2x u32, leaked toolset pointers)
+0x08..+0x14 face_arr (CExoArrayList -> MaxFace[32B] array)
+0x14..+0x54 bounding/color (bbox, bsphere, diffuse, ambient, transparency)
+0x58..+0x98 texture names (texture_0[32], texture_1[32])
+0x98..+0xD4 CExoArrayList[5] (index buffer submission system)
+0xD4..+0xE8 shared index data (offset, pool, size, indices_per_face, padding)
+0xE8..+0xFC UV animation (animate_uv, direction, jitter, speed)
+0xFC..+0x100 vertex_stride (u32, bytes per MDX vertex)
+0x100..+0x130 MDX layout (flags + 12 attribute byte offsets)
+0x130..+0x140 mesh properties (vertex_count, channel_count, flags)
+0x13C..+0x144 total_surface_area (f32)
+0x144..+0x148 mdx_data_offset (u32, byte offset into MDX file)
+0x148..+0x14C vert_array_offset (u32, content-relative -> position data)§Animation Header (136 bytes)
0x00..0x08 fn_ptrs (2x u32, leaked toolset pointers)
0x08..0x28 name (char[32], null-terminated)
0x28..0x2C root_node_ptr (u32, content-relative)
0x2C..0x30 node_count (u32)
0x30..0x48 runtime arrays (24 bytes, zeros on disk)
0x48..0x4C ref_count (u32, zero on disk)
0x4C..0x50 model_type+pad (u8=5 + 3 bytes padding)
0x50..0x54 length (f32, duration in seconds)
0x54..0x58 transition (f32, blend time in seconds)
0x58..0x78 anim_root (char[32], geometry node name)
0x78..0x84 event_arr (CExoArrayList: ptr/count/alloc)
0x84..0x88 padding (4 bytes)Binary format offsets verified against swkotor.exe via Ghidra
decompilation. See docs/notes/mdl_mdx.md for full details.
Re-exports§
pub use ascii_reader::read_mdl_ascii;pub use ascii_reader::read_mdl_ascii_from_str;pub use ascii_writer::write_mdl_ascii;pub use ascii_writer::write_mdl_ascii_to_string;pub use ascii_writer::MdlAsciiError;pub use controllers::MdlController;pub use controllers::MdlControllerType;pub use controllers::MdlKey;pub use reader::read_mdl;pub use reader::read_mdl_from_bytes;pub use types::AabbNode;pub use types::MdlAabb;pub use types::MdlAnimMesh;pub use types::MdlCamera;pub use types::MdlDangly;pub use types::MdlEmitter;pub use types::MdlFace;pub use types::MdlLight;pub use types::MdlMesh;pub use types::MdlNodeData;pub use types::MdlReference;pub use types::MdlSaber;pub use types::MdlSkin;pub use writer::write_mdl;pub use writer::write_mdl_to_vec;pub use writer::write_mdl_with_mdx_to_vec;
Modules§
- ascii_
names - ASCII name registry for controllers, classifications, and node types. ASCII MDL name registry for controllers, classifications, and node types.
- ascii_
reader - ASCII MDL reader. ASCII MDL reader.
- ascii_
writer - ASCII MDL writer. ASCII MDL writer.
- controllers
- MDL controller types and keyframe structures. MDL controller types and keyframe structures.
- node_
flags - Bitflags for node type identification in the on-disk binary format.
- orientation
- Quaternion and axis-angle conversion utilities. Quaternion and axis-angle conversion utilities for MDL orientation data.
- reader
- MDL binary reader. MDL binary reader.
- types
- Node-specific data types. Node-specific data types for the MDL format.
- writer
- MDL binary writer. MDL binary writer.
Structs§
- Mdl
- The high-level MDL container.
- MdlAnim
Event - An event that fires at a specific time during an animation.
- MdlAnim
Node - A node in an animation’s node tree.
- MdlAnimation
- A named animation sequence.
- MdlNode
- A node in the MDL hierarchy.
- MdlWrite
Result - Result of writing an MDL model with its companion MDX vertex data.
Enums§
- MdlError
- MDL binary parsing error type.
Functions§
- assign_
inverted_ counters - Assign inverted counter values to all mesh nodes in DFS tree order.