vmdl/mdl/
mod.rs

1mod raw;
2
3pub use raw::header::*;
4pub use raw::header2::*;
5pub use raw::*;
6use std::mem::size_of;
7
8use crate::vvd::Vertex;
9use crate::{
10    read_relative, read_relative_iter, read_single, FixedString, ModelError, ReadRelative,
11    Readable, Transform3x4, Vector,
12};
13
14type Result<T> = std::result::Result<T, ModelError>;
15
16#[derive(Debug, Clone)]
17pub struct Mdl {
18    pub name: FixedString<64>,
19    pub header: StudioHeader,
20    pub header2: Option<StudioHeader2>,
21    pub bones: Vec<Bone>,
22    pub bone_controllers: Vec<BoneController>,
23    pub body_table_by_name: Vec<u8>,
24    pub body_parts: Vec<BodyPart>,
25    pub textures: Vec<TextureInfo>,
26    pub texture_paths: Vec<String>,
27    pub skin_table: Vec<u16>,
28    pub surface_prop: String,
29    pub key_values: Option<String>,
30    pub local_animations: Vec<AnimationDescription>,
31    pub animation_block_source: String,
32    pub animation_blocks: Vec<AnimationBlock>,
33    pub animation_sequences: Vec<AnimationSequence>,
34    pub pose_parameters: Vec<PoseParameterDescription>,
35    pub attachments: Vec<StudioAttachment>,
36    pub hit_boxes: Vec<HitBoxSet>,
37}
38
39impl Mdl {
40    pub fn read(data: &[u8]) -> Result<Self> {
41        let header = <StudioHeader as Readable>::read(data)?;
42        let header2 = header
43            .header2_index()
44            .map(|index| read_single::<StudioHeader2, _>(data, index))
45            .transpose()?;
46        let name = header.name.try_into()?;
47        let mut textures = read_relative_iter(data, header.texture_indexes())
48            .collect::<Result<Vec<TextureInfo>>>()?;
49        let texture_dirs_indexes =
50            read_relative_iter(data, header.texture_dir_indexes()).collect::<Result<Vec<u32>>>()?;
51        let texture_paths = read_relative_iter::<String, _>(
52            data,
53            texture_dirs_indexes.into_iter().map(|index| index as usize),
54        )
55        .map(|path| path.map(|path| path.replace('\\', "/")))
56        .collect::<Result<Vec<_>>>()?;
57        for texture in textures.iter_mut() {
58            texture.search_paths = texture_paths.clone();
59        }
60
61        let skin_table = read_relative::<u16, _>(data, header.skin_reference_indexes())?;
62        let bones = read_relative(data, header.bone_indexes())?;
63        let bone_controllers = read_relative(data, header.bone_controller_indexes())?;
64        let body_table_by_name = read_relative(data, header.bone_table_by_name_indexes())?;
65
66        let surface_prop = read_single(data, header.surface_prop_index)?;
67        let key_values = (header.key_value_size > 0)
68            .then(|| read_single(data, header.key_value_index))
69            .transpose()?;
70        let mut local_animations: Vec<AnimationDescription> =
71            read_relative(data, header.local_animation_indexes())?;
72        local_animations
73            .iter_mut()
74            .flat_map(|desc| desc.animations.iter_mut())
75            .for_each(|animation| {
76                if let Some(bone) = bones.get(animation.bone as usize) {
77                    animation.set_scales(bone);
78                }
79            });
80        let animation_block_source: String = read_single(data, header.anim_blocks_name_index)?;
81        let animation_blocks = read_relative(data, header.animation_block_indexes())?;
82        let mut animation_sequences: Vec<AnimationSequence> =
83            read_relative(data, header.animation_sequence_indexes())?;
84        animation_sequences
85            .iter_mut()
86            .for_each(|seq| seq.bone_weights.truncate(bones.len()));
87
88        let pose_parameters = read_relative(data, header.local_pose_param_indexes())?;
89        let attachments = read_relative(data, header.attachment_indexes())?;
90        let hit_boxes = read_relative(data, header.hitbox_set_indexes())?;
91
92        Ok(Mdl {
93            name,
94            bones,
95            bone_controllers,
96            body_table_by_name,
97            body_parts: header
98                .body_part_indexes()
99                .map(|index| {
100                    let data = data.get(index..).ok_or(ModelError::OutOfBounds {
101                        data: "BodyPart",
102                        offset: index,
103                    })?;
104                    let header = <BodyPartHeader as Readable>::read(data)?;
105                    BodyPart::read(data, header)
106                })
107                .collect::<Result<_>>()?,
108            textures,
109            texture_paths,
110            skin_table,
111            header,
112            header2,
113            surface_prop,
114            key_values,
115            pose_parameters,
116            local_animations,
117            animation_block_source,
118            animation_blocks,
119            animation_sequences,
120            attachments,
121            hit_boxes,
122        })
123    }
124}
125
126#[derive(Debug, Clone)]
127pub struct BodyPart {
128    pub name_index: i32,
129    pub models: Vec<Model>,
130}
131
132impl ReadRelative for BodyPart {
133    type Header = BodyPartHeader;
134
135    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
136        Ok(BodyPart {
137            models: read_relative(data, header.model_indexes())?,
138            name_index: header.name_index,
139        })
140    }
141}
142
143#[derive(Debug, Clone)]
144pub struct Model {
145    pub name: FixedString<64>,
146    pub ty: i32,
147    pub bounding_radius: f32,
148    pub meshes: Vec<Mesh>,
149    /// Base offset of the model's vertices
150    pub vertex_offset: i32,
151}
152
153impl ReadRelative for Model {
154    type Header = ModelHeader;
155
156    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
157        Ok(Model {
158            meshes: read_relative(data, header.mesh_indexes())?,
159            name: header.name.try_into()?,
160            ty: header.ty,
161            bounding_radius: header.bounding_radius,
162            vertex_offset: header.vertex_index / (size_of::<Vertex>() as i32),
163        })
164    }
165}
166
167#[derive(Debug, Clone)]
168pub struct Mesh {
169    pub material: i32,
170    pub vertex_offset: i32,
171}
172
173impl ReadRelative for Mesh {
174    type Header = MeshHeader;
175
176    fn read(_data: &[u8], header: Self::Header) -> Result<Self> {
177        Ok(Mesh {
178            material: header.material,
179            vertex_offset: header.vertex_index,
180        })
181    }
182}
183
184#[derive(Debug, Clone)]
185pub struct TextureInfo {
186    pub name: String,
187    pub name_index: i32,
188    pub search_paths: Vec<String>,
189}
190
191impl ReadRelative for TextureInfo {
192    type Header = MeshTexture;
193
194    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
195        Ok(TextureInfo {
196            name: String::read(
197                data.get(header.name_index as usize..).unwrap_or_default(),
198                (),
199            )?
200            .replace('\\', "/"),
201            name_index: header.name_index,
202            search_paths: Vec::new(),
203        })
204    }
205}
206
207#[derive(Debug, Clone)]
208pub struct StudioAttachment {
209    pub name: String,
210    pub flags: AttachmentFlags,
211    pub local_bone: i32,
212    pub local: Transform3x4,
213}
214
215impl ReadRelative for StudioAttachment {
216    type Header = StudioAttachmentHeader;
217
218    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
219        Ok(StudioAttachment {
220            name: String::read(
221                data.get(header.name_index as usize..).unwrap_or_default(),
222                (),
223            )?
224            .replace('\\', "/"),
225            flags: header.flags,
226            local: header.local,
227            local_bone: header.local_bone,
228        })
229    }
230}
231
232#[derive(Debug, Clone)]
233pub struct HitBoxSet {
234    pub name: String,
235    pub boxes: Vec<BoundingBox>,
236}
237
238impl ReadRelative for HitBoxSet {
239    type Header = HitBoxSetHeader;
240
241    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
242        Ok(HitBoxSet {
243            name: String::read(
244                data.get(header.name_index as usize..).unwrap_or_default(),
245                (),
246            )?
247            .replace('\\', "/"),
248            boxes: read_relative(data, header.hitbox_indexes())?,
249        })
250    }
251}
252
253#[derive(Debug, Clone)]
254pub struct BoundingBox {
255    pub name: String,
256    pub bone: i32,
257    pub group: i32,
258    pub min: Vector,
259    pub max: Vector,
260}
261
262impl ReadRelative for BoundingBox {
263    type Header = BoundingBoxHeader;
264
265    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
266        Ok(BoundingBox {
267            name: String::read(
268                data.get(header.name_index as usize..).unwrap_or_default(),
269                (),
270            )?
271            .replace('\\', "/"),
272            bone: header.bone,
273            group: header.group,
274            min: header.bounding_box_min,
275            max: header.bounding_box_max,
276        })
277    }
278}