Skip to main content

Module mdl

Module mdl 

Source
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.
MdlAnimEvent
An event that fires at a specific time during an animation.
MdlAnimNode
A node in an animation’s node tree.
MdlAnimation
A named animation sequence.
MdlNode
A node in the MDL hierarchy.
MdlWriteResult
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.