1#![allow(clippy::unnecessary_fallible_conversions)] mod file_operations;
7
8pub mod vertex_declarations;
9
10use std::io::{Cursor, Seek, SeekFrom};
11use std::mem::size_of;
12
13use binrw::BinReaderExt;
14use binrw::{BinRead, VecArgs};
15use binrw::{BinWrite, BinWriterExt, binrw};
16
17use crate::common_file_operations::{read_bool_from, write_bool_as};
18use crate::{ByteBuffer, ByteSpan};
19use vertex_declarations::{
20 VERTEX_ELEMENT_SIZE, VertexDeclaration, VertexType, VertexUsage, vertex_element_parser,
21 vertex_element_writer,
22};
23
24pub const NUM_VERTICES: u32 = 17;
25
26#[binrw]
27#[derive(Debug, Clone, PartialEq)]
28#[brw(little)]
29pub struct ModelFileHeader {
30 pub version: u32,
31
32 pub stack_size: u32,
33 pub runtime_size: u32,
34
35 pub vertex_declaration_count: u16,
36 pub material_count: u16,
37
38 pub vertex_offsets: [u32; 3],
39 pub index_offsets: [u32; 3],
40 pub vertex_buffer_size: [u32; 3],
41 pub index_buffer_size: [u32; 3],
42
43 pub lod_count: u8,
44
45 #[br(map = read_bool_from::<u8>)]
46 #[bw(map = write_bool_as::<u8>)]
47 pub index_buffer_streaming_enabled: bool,
48 #[br(map = read_bool_from::<u8>)]
49 #[bw(map = write_bool_as::<u8>)]
50 #[brw(pad_after = 1)]
51 pub has_edge_geometry: bool,
52}
53
54#[binrw]
55#[brw(repr = u8)]
56#[derive(Debug, Clone, PartialEq)]
57enum ModelFlags1 {
58 DustOcclusionEnabled = 0x80,
59 SnowOcclusionEnabled = 0x40,
60 RainOcclusionEnabled = 0x20,
61 Unknown1 = 0x10,
62 LightingReflectionEnabled = 0x08,
63 WavingAnimationDisabled = 0x04,
64 LightShadowDisabled = 0x02,
65 ShadowDisabled = 0x01,
66 Unknown2 = 0x5, }
68
69#[binrw]
70#[brw(repr = u8)]
71#[derive(Debug, Clone, PartialEq)]
72enum ModelFlags2 {
73 None = 0x0,
74 Unknown2 = 0x80,
75 BgUvScrollEnabled = 0x40,
76 EnableForceNonResident = 0x20,
77 ExtraLodEnabled = 0x10,
78 ShadowMaskEnabled = 0x08,
79 ForceLodRangeEnabled = 0x04,
80 EdgeGeometryEnabled = 0x02,
81 Unknown3 = 0x01,
82}
83
84#[binrw]
85#[derive(Debug, Clone, PartialEq)]
86#[br(import { vertex_declaration_count: u16 })]
87#[allow(dead_code)]
88pub struct ModelHeader {
89 #[br(args(vertex_declaration_count), parse_with = vertex_element_parser)]
90 #[bw(write_with = vertex_element_writer)]
91 pub vertex_declarations: Vec<VertexDeclaration>,
92
93 #[brw(pad_after = 2)]
94 string_count: u16,
95 string_size: u32,
96
97 #[br(count = string_size)]
98 strings: Vec<u8>,
99
100 radius: f32,
101
102 mesh_count: u16,
103 attribute_count: u16,
104 submesh_count: u16,
105 material_count: u16,
106 bone_count: u16,
107 bone_table_count: u16,
108 shape_count: u16,
109 shape_mesh_count: u16,
110 shape_value_count: u16,
111
112 lod_count: u8,
113
114 flags1: ModelFlags1,
115
116 element_id_count: u16,
117 terrain_shadow_mesh_count: u8,
118
119 flags2: ModelFlags2,
120
121 model_clip_out_of_distance: f32,
122 shadow_clip_out_of_distance: f32,
123
124 unknown4: u16,
125
126 terrain_shadow_submesh_count: u16,
127
128 unknown5: u8,
129
130 bg_change_material_index: u8,
131 bg_crest_change_material_index: u8,
132
133 unknown6: u8,
134 unknown7: u16,
135 unknown8: u16,
136 #[brw(pad_after = 6)]
137 unknown9: u16,
138}
139
140#[binrw]
141#[derive(Debug, Clone, PartialEq)]
142#[allow(dead_code)]
143struct MeshLod {
144 mesh_index: u16,
145 mesh_count: u16,
146
147 model_lod_range: f32,
148 texture_lod_range: f32,
149
150 water_mesh_index: u16,
151 water_mesh_count: u16,
152
153 shadow_mesh_index: u16,
154 shadow_mesh_count: u16,
155
156 terrain_shadow_mesh_count: u16,
157 terrain_shadow_mesh_index: u16,
158
159 vertical_fog_mesh_index: u16,
160 vertical_fog_mesh_count: u16,
161
162 edge_geometry_size: u32,
164 edge_geometry_data_offset: u32,
165
166 #[brw(pad_after = 4)]
167 polygon_count: u32,
168
169 vertex_buffer_size: u32,
170 index_buffer_size: u32,
171 vertex_data_offset: u32,
172 index_data_offset: u32,
173}
174
175#[binrw]
176#[derive(Debug, Clone, PartialEq)]
177#[allow(dead_code)]
178struct Mesh {
179 #[brw(pad_after = 2)]
180 vertex_count: u16,
181 index_count: u32,
182
183 material_index: u16,
184 submesh_index: u16,
185 submesh_count: u16,
186
187 bone_table_index: u16,
188 start_index: u32,
189
190 vertex_buffer_offsets: [u32; 3],
191 vertex_buffer_strides: [u8; 3],
192
193 vertex_stream_count: u8,
194}
195
196#[binrw]
197#[derive(Debug, Clone, PartialEq)]
198#[allow(dead_code)]
199struct Submesh {
200 index_offset: u32,
201 index_count: u32,
202
203 attribute_index_mask: u32,
204
205 bone_start_index: u16,
206 bone_count: u16,
207}
208
209#[binrw]
210#[derive(Debug, Clone, PartialEq)]
211#[allow(dead_code)]
212struct BoneTable {
213 bone_indices: [u16; 64],
214
215 #[brw(pad_after = 3)]
216 bone_count: u8,
217}
218
219#[binrw]
220#[derive(Debug, Clone, PartialEq)]
221#[allow(dead_code)]
222struct BoneTableV2 {
223 #[br(pad_before = 2)]
224 bone_count: u16,
225
226 #[br(count = bone_count)]
227 bone_indices: Vec<u16>,
228
229 #[br(if(bone_count % 2 == 0))]
232 padding: u16,
233}
234
235#[binrw]
236#[derive(Debug, Clone, PartialEq)]
237#[allow(dead_code)]
238struct BoundingBox {
239 min: [f32; 4],
240 max: [f32; 4],
241}
242
243#[binrw]
244#[derive(Debug, Clone, PartialEq)]
245#[allow(dead_code)]
246struct TerrainShadowMesh {
247 index_count: u32,
248 start_index: u32,
249 vertex_buffer_offset: u32,
250 vertex_count: u16,
251 submesh_index: u16,
252 submesh_count: u16,
253 vertex_buffer_stride: u8,
254 padding: u8,
255}
256
257#[binrw]
258#[derive(Debug, Clone, PartialEq)]
259#[allow(dead_code)]
260struct TerrainShadowSubmesh {
261 index_offset: u32,
262 index_count: u32,
263 unknown1: u16,
264 unknown2: u16,
265}
266
267#[binrw]
268#[derive(Debug, Clone, PartialEq)]
269#[allow(dead_code)]
270struct ShapeStruct {
271 string_offset: u32,
272 shape_mesh_start_index: [u16; 3],
273 shape_mesh_count: [u16; 3],
274}
275
276#[binrw]
277#[derive(Debug, Clone, PartialEq)]
278#[allow(dead_code)]
279struct ShapeMesh {
280 mesh_index_offset: u32,
281 shape_value_count: u32,
282 shape_value_offset: u32,
283}
284
285#[binrw]
286#[derive(Debug, Clone, PartialEq)]
287#[allow(dead_code)]
288struct ShapeValue {
289 base_indices_index: u16,
290 replacing_vertex_index: u16,
291}
292
293#[binrw]
294#[derive(Debug, Clone, PartialEq)]
295#[allow(dead_code)]
296#[br(import {file_header: &ModelFileHeader})]
297#[brw(little)]
298pub struct ModelData {
299 #[br(args { vertex_declaration_count: file_header.vertex_declaration_count })]
300 pub header: ModelHeader,
301
302 #[br(count = header.element_id_count)]
303 element_ids: Vec<ElementId>,
304
305 #[br(count = 3)]
306 lods: Vec<MeshLod>,
307
308 #[br(count = header.mesh_count)]
309 meshes: Vec<Mesh>,
310
311 #[br(count = header.attribute_count)]
312 attribute_name_offsets: Vec<u32>,
313
314 #[br(count = header.terrain_shadow_mesh_count)]
315 terrain_shadow_meshes: Vec<TerrainShadowMesh>,
316
317 #[br(count = header.submesh_count)]
318 submeshes: Vec<Submesh>,
319
320 #[br(count = header.terrain_shadow_submesh_count)]
321 terrain_shadow_submeshes: Vec<TerrainShadowSubmesh>,
322
323 #[br(count = header.material_count)]
324 material_name_offsets: Vec<u32>,
325
326 #[br(count = header.bone_count)]
327 bone_name_offsets: Vec<u32>,
328
329 #[br(count = header.bone_table_count)]
330 #[br(if(file_header.version <= 0x1000005))]
331 bone_tables: Vec<BoneTable>,
332
333 #[br(count = header.bone_table_count)]
334 #[br(if(file_header.version >= 0x1000006))]
335 bone_tables_v2: Vec<BoneTableV2>,
336
337 #[br(count = header.shape_count)]
338 shapes: Vec<ShapeStruct>,
339
340 #[br(count = header.shape_mesh_count)]
341 shape_meshes: Vec<ShapeMesh>,
342
343 #[br(count = header.shape_value_count)]
344 shape_values: Vec<ShapeValue>,
345
346 #[br(if(file_header.version <= 0x1000005))]
348 submesh_bone_map_size: u32,
349
350 #[br(if(file_header.version >= 0x1000006))]
352 submesh_bone_map_size_v2: u16,
353
354 #[br(count = if file_header.version >= 0x1000006 { (submesh_bone_map_size_v2 / 2) as u32 } else { submesh_bone_map_size / 2 } )]
355 submesh_bone_map: Vec<u16>,
356
357 padding_amount: u8,
358 #[br(count = padding_amount)]
359 unknown_padding: Vec<u8>,
360
361 bounding_box: BoundingBox,
363 model_bounding_box: BoundingBox,
364 water_bounding_box: BoundingBox,
365 vertical_fog_bounding_box: BoundingBox,
366
367 #[br(count = header.bone_count)]
368 bone_bounding_boxes: Vec<BoundingBox>,
369}
370
371#[binrw]
372#[derive(Debug, Clone, PartialEq)]
373#[allow(dead_code)]
374struct ElementId {
375 element_id: u32,
376 parent_bone_name: u32,
377 translate: [f32; 3],
378 rotate: [f32; 3],
379}
380
381#[derive(Clone, Copy, PartialEq, Debug)]
382#[repr(C)]
383pub struct Vertex {
384 pub position: [f32; 3],
385 pub uv0: [f32; 2],
386 pub uv1: [f32; 2],
387 pub normal: [f32; 3],
388 pub bitangent: [f32; 4],
389 pub color: [f32; 4],
391
392 pub bone_weight: [f32; 4],
393 pub bone_id: [u8; 4],
394}
395
396impl Default for Vertex {
397 fn default() -> Self {
398 Self {
399 position: [0.0; 3],
400 uv0: [0.0; 2],
401 uv1: [0.0; 2],
402 normal: [0.0; 3],
403 bitangent: [0.0; 4],
404 color: [0.0; 4],
405 bone_weight: [0.0; 4],
406 bone_id: [0u8; 4],
407 }
408 }
409}
410
411#[derive(Debug, Clone, Copy)]
412#[repr(C)]
413pub struct NewShapeValue {
414 pub base_index: u32,
415 pub replacing_vertex: Vertex,
416}
417
418#[derive(Debug, Clone, Copy)]
419#[repr(C)]
420pub struct SubMesh {
421 submesh_index: usize,
422 pub index_count: u32,
423 pub index_offset: u32,
424}
425
426#[derive(Debug, Clone)]
427pub struct Shape {
428 pub name: String,
429 pub morphed_vertices: Vec<Vertex>,
430}
431
432#[derive(Debug, Clone)]
434pub struct Part {
435 mesh_index: u16,
436 pub vertices: Vec<Vertex>,
437 pub vertex_streams: Vec<Vec<u8>>,
439 pub vertex_stream_strides: Vec<usize>,
440 pub indices: Vec<u16>,
441 pub material_index: u16,
442 pub submeshes: Vec<SubMesh>,
443 pub shapes: Vec<Shape>,
444}
445
446#[derive(Debug, Clone)]
447pub struct Lod {
448 pub parts: Vec<Part>,
449}
450
451#[derive(Debug, Clone)]
452pub struct MDL {
453 file_header: ModelFileHeader,
454 pub model_data: ModelData,
455
456 pub lods: Vec<Lod>,
457 pub affected_bone_names: Vec<String>,
458 pub material_names: Vec<String>,
459}
460
461impl MDL {
462 pub fn from_existing(buffer: ByteSpan) -> Option<MDL> {
463 let mut cursor = Cursor::new(buffer);
464 let model_file_header = ModelFileHeader::read(&mut cursor).ok()?;
465
466 let model = ModelData::read_args(
467 &mut cursor,
468 binrw::args! { file_header: &model_file_header },
469 )
470 .ok()?;
471
472 let mut affected_bone_names = vec![];
473
474 for offset in &model.bone_name_offsets {
475 let mut offset = *offset;
476 let mut string = String::new();
477
478 let mut next_char = model.header.strings[offset as usize] as char;
479 while next_char != '\0' {
480 string.push(next_char);
481 offset += 1;
482 next_char = model.header.strings[offset as usize] as char;
483 }
484
485 affected_bone_names.push(string);
486 }
487
488 let mut material_names = vec![];
489
490 for offset in &model.material_name_offsets {
491 let mut offset = *offset;
492 let mut string = String::new();
493
494 let mut next_char = model.header.strings[offset as usize] as char;
495 while next_char != '\0' {
496 string.push(next_char);
497 offset += 1;
498 next_char = model.header.strings[offset as usize] as char;
499 }
500
501 material_names.push(string);
502 }
503
504 let mut lods = vec![];
505
506 for i in 0..model.header.lod_count {
507 let mut parts = vec![];
508
509 for j in model.lods[i as usize].mesh_index
510 ..model.lods[i as usize].mesh_index + model.lods[i as usize].mesh_count
511 {
512 let declaration = &model.header.vertex_declarations[j as usize];
513 let vertex_count = model.meshes[j as usize].vertex_count;
514 let material_index = model.meshes[j as usize].material_index;
515
516 let mut vertices: Vec<Vertex> = vec![Vertex::default(); vertex_count as usize];
517
518 for k in 0..vertex_count {
519 for element in &declaration.elements {
520 cursor
521 .seek(SeekFrom::Start(
522 (model.lods[i as usize].vertex_data_offset
523 + model.meshes[j as usize].vertex_buffer_offsets
524 [element.stream as usize]
525 + element.offset as u32
526 + model.meshes[j as usize].vertex_buffer_strides
527 [element.stream as usize]
528 as u32
529 * k as u32) as u64,
530 ))
531 .ok()?;
532
533 match element.vertex_usage {
534 VertexUsage::Position => match element.vertex_type {
535 VertexType::Single4 => {
536 vertices[k as usize].position.clone_from_slice(
537 &MDL::read_single4(&mut cursor).unwrap()[0..3],
538 );
539 }
540 VertexType::Half4 => {
541 vertices[k as usize].position.clone_from_slice(
542 &MDL::read_half4(&mut cursor).unwrap()[0..3],
543 );
544 }
545 VertexType::Single3 => {
546 vertices[k as usize].position =
547 MDL::read_single3(&mut cursor).unwrap();
548 }
549 _ => {
550 panic!(
551 "Unexpected vertex type for position: {:#?}",
552 element.vertex_type
553 );
554 }
555 },
556 VertexUsage::BlendWeights => match element.vertex_type {
557 VertexType::ByteFloat4 => {
558 vertices[k as usize].bone_weight =
559 MDL::read_byte_float4(&mut cursor).unwrap();
560 }
561 VertexType::Byte4 => {
562 vertices[k as usize].bone_weight =
563 MDL::read_tangent(&mut cursor).unwrap();
564 }
565 VertexType::UnsignedShort4 => {
566 let bytes = MDL::read_unsigned_short4(&mut cursor).unwrap();
567 vertices[k as usize].bone_weight = [
568 f32::from(bytes[0]),
569 f32::from(bytes[1]),
570 f32::from(bytes[2]),
571 f32::from(bytes[3]),
572 ];
573 }
574 _ => {
575 panic!(
576 "Unexpected vertex type for blendweight: {:#?}",
577 element.vertex_type
578 );
579 }
580 },
581 VertexUsage::BlendIndices => match element.vertex_type {
582 VertexType::Byte4 => {
583 vertices[k as usize].bone_id =
584 MDL::read_byte4(&mut cursor).unwrap();
585 }
586 VertexType::UnsignedShort4 => {
587 let shorts = MDL::read_unsigned_short4(&mut cursor).unwrap();
588 vertices[k as usize].bone_id = [
589 shorts[0] as u8,
590 shorts[1] as u8,
591 shorts[2] as u8,
592 shorts[3] as u8,
593 ];
594 }
595 _ => {
596 panic!(
597 "Unexpected vertex type for blendindice: {:#?}",
598 element.vertex_type
599 );
600 }
601 },
602 VertexUsage::Normal => match element.vertex_type {
603 VertexType::Half4 => {
604 vertices[k as usize].normal.clone_from_slice(
605 &MDL::read_half4(&mut cursor).unwrap()[0..3],
606 );
607 }
608 VertexType::Single3 => {
609 vertices[k as usize].normal =
610 MDL::read_single3(&mut cursor).unwrap();
611 }
612 _ => {
613 panic!(
614 "Unexpected vertex type for normal: {:#?}",
615 element.vertex_type
616 );
617 }
618 },
619 VertexUsage::UV => match element.vertex_type {
620 VertexType::ByteFloat4 => {
621 let combined = MDL::read_byte_float4(&mut cursor).unwrap();
622
623 vertices[k as usize].uv0.clone_from_slice(&combined[0..2]);
624 vertices[k as usize].uv1.clone_from_slice(&combined[2..4]);
625 }
626 VertexType::Half4 => {
627 let combined = MDL::read_half4(&mut cursor).unwrap();
628
629 vertices[k as usize].uv0.clone_from_slice(&combined[0..2]);
630 vertices[k as usize].uv1.clone_from_slice(&combined[2..4]);
631 }
632 VertexType::Single4 => {
633 let combined = MDL::read_single4(&mut cursor).unwrap();
634
635 vertices[k as usize].uv0.clone_from_slice(&combined[0..2]);
636 vertices[k as usize].uv1.clone_from_slice(&combined[2..4]);
637 }
638 VertexType::Half2 => {
639 let combined = MDL::read_half2(&mut cursor).unwrap();
640
641 vertices[k as usize].uv0.clone_from_slice(&combined[0..2]);
642 }
643 _ => {
644 panic!(
645 "Unexpected vertex type for uv: {:#?}",
646 element.vertex_type
647 );
648 }
649 },
650 VertexUsage::BiTangent => match element.vertex_type {
651 VertexType::ByteFloat4 => {
652 vertices[k as usize].bitangent =
653 MDL::read_tangent(&mut cursor).unwrap();
654 }
655 _ => {
656 panic!(
657 "Unexpected vertex type for bitangent: {:#?}",
658 element.vertex_type
659 );
660 }
661 },
662 VertexUsage::Tangent => {
663 match element.vertex_type {
664 VertexType::ByteFloat4 => {}
666 _ => {
667 panic!(
668 "Unexpected vertex type for tangent: {:#?}",
669 element.vertex_type
670 );
671 }
672 }
673 }
674 VertexUsage::Color => match element.vertex_type {
675 VertexType::ByteFloat4 => {
676 vertices[k as usize].color =
677 MDL::read_byte_float4(&mut cursor).unwrap();
678 }
679 _ => {
680 panic!(
681 "Unexpected vertex type for color: {:#?}",
682 element.vertex_type
683 );
684 }
685 },
686 }
687 }
688 }
689
690 cursor
691 .seek(SeekFrom::Start(
692 (model_file_header.index_offsets[i as usize]
693 + (model.meshes[j as usize].start_index * size_of::<u16>() as u32))
694 as u64,
695 ))
696 .ok()?;
697
698 let index_count = model.meshes[j as usize].index_count as usize;
699 let indices: Vec<u16> = cursor
700 .read_le_args(VecArgs::builder().count(index_count).finalize())
701 .ok()?;
702
703 let mut submeshes: Vec<SubMesh> =
704 Vec::with_capacity(model.meshes[j as usize].submesh_count as usize);
705 for i in 0..model.meshes[j as usize].submesh_count {
706 submeshes.push(SubMesh {
707 submesh_index: model.meshes[j as usize].submesh_index as usize + i as usize,
708 index_count: model.submeshes
709 [model.meshes[j as usize].submesh_index as usize + i as usize]
710 .index_count,
711 index_offset: model.submeshes
712 [model.meshes[j as usize].submesh_index as usize + i as usize]
713 .index_offset,
714 });
715 }
716
717 let mut shapes = vec![];
718
719 for shape in &model.shapes {
720 let affected_shape_mesh: Vec<&ShapeMesh> = model
722 .shape_meshes
723 .iter()
724 .skip(shape.shape_mesh_start_index[i as usize] as usize)
725 .take(shape.shape_mesh_count[i as usize] as usize)
726 .filter(|shape_mesh| {
727 shape_mesh.mesh_index_offset == model.meshes[j as usize].start_index
728 })
729 .collect();
730
731 let shape_values: Vec<&ShapeValue> = affected_shape_mesh
732 .iter()
733 .flat_map(|shape_mesh| {
734 model
735 .shape_values
736 .iter()
737 .skip(shape_mesh.shape_value_offset as usize)
738 .take(shape_mesh.shape_value_count as usize)
739 })
740 .filter(|shape_value| {
741 shape_value.base_indices_index
742 >= model.meshes[j as usize].start_index as u16
743 && shape_value.base_indices_index
744 < (model.meshes[j as usize].start_index
745 + model.meshes[j as usize].index_count)
746 as u16
747 })
748 .collect();
749
750 let mut morphed_vertices = vec![Vertex::default(); vertices.len()];
751
752 if !shape_values.is_empty() {
753 for shape_value in shape_values {
754 let old_vertex =
755 vertices[indices[shape_value.base_indices_index as usize] as usize];
756 let new_vertex = vertices[shape_value.replacing_vertex_index as usize];
757 let vertex = &mut morphed_vertices
758 [indices[shape_value.base_indices_index as usize] as usize];
759
760 vertex.position[0] = new_vertex.position[0] - old_vertex.position[0];
761 vertex.position[1] = new_vertex.position[1] - old_vertex.position[1];
762 vertex.position[2] = new_vertex.position[2] - old_vertex.position[2];
763 }
764
765 let mut offset = shape.string_offset;
766 let mut string = String::new();
767
768 let mut next_char = model.header.strings[offset as usize] as char;
769 while next_char != '\0' {
770 string.push(next_char);
771 offset += 1;
772 next_char = model.header.strings[offset as usize] as char;
773 }
774
775 shapes.push(Shape {
776 name: string,
777 morphed_vertices,
778 });
779 }
780 }
781
782 let mut vertex_streams = vec![];
783 let mut vertex_stream_strides = vec![];
784 let mesh = &model.meshes[j as usize];
785 for stream in 0..mesh.vertex_stream_count {
786 let mut vertex_data = vec![];
787 let stride = mesh.vertex_buffer_strides[stream as usize];
788 for z in 0..mesh.vertex_count {
789 cursor
792 .seek(SeekFrom::Start(
793 (model.lods[i as usize].vertex_data_offset
794 + model.meshes[j as usize].vertex_buffer_offsets
795 [stream as usize]
796 + (z as u32 * stride as u32))
797 as u64,
798 ))
799 .ok()?;
800
801 for _ in 0..stride {
802 vertex_data.push(cursor.read_le::<u8>().ok()?);
803 }
804 }
805
806 vertex_streams.push(vertex_data);
807 vertex_stream_strides
808 .push(mesh.vertex_buffer_strides[stream as usize] as usize);
809 }
810
811 parts.push(Part {
812 mesh_index: j,
813 vertices,
814 indices,
815 material_index,
816 submeshes,
817 shapes,
818 vertex_streams,
819 vertex_stream_strides,
820 });
821 }
822
823 lods.push(Lod { parts });
824 }
825
826 Some(MDL {
827 file_header: model_file_header,
828 model_data: model,
829 lods,
830 affected_bone_names,
831 material_names,
832 })
833 }
834
835 pub fn replace_vertices(
836 &mut self,
837 lod_index: usize,
838 part_index: usize,
839 vertices: &[Vertex],
840 indices: &[u16],
841 submeshes: &[SubMesh],
842 ) {
843 let part = &mut self.lods[lod_index].parts[part_index];
844
845 part.vertices = Vec::from(vertices);
846 part.indices = Vec::from(indices);
847
848 for (i, submesh) in part.submeshes.iter().enumerate() {
849 if i < submeshes.len() {
850 self.model_data.submeshes[submesh.submesh_index].index_offset =
851 submeshes[i].index_offset;
852 self.model_data.submeshes[submesh.submesh_index].index_count =
853 submeshes[i].index_count;
854 }
855 }
856
857 self.model_data.meshes[part.mesh_index as usize].vertex_count = part.vertices.len() as u16;
859 self.model_data.meshes[part.mesh_index as usize].index_count = part.indices.len() as u32;
860
861 self.update_headers();
862 }
863
864 pub fn remove_shape_meshes(&mut self) {
865 self.model_data.shape_meshes.clear();
866 self.model_data.shape_values.clear();
867
868 for lod in 0..3 {
869 for shape in &mut self.model_data.shapes {
870 shape.shape_mesh_count[lod] = 0;
871 shape.shape_mesh_start_index[lod] = 0;
872 }
873 }
874
875 self.update_headers();
876 }
877
878 pub fn add_shape_mesh(
879 &mut self,
880 lod_index: usize,
881 shape_index: usize,
882 shape_mesh_index: usize,
883 part_index: usize,
884 shape_values: &[NewShapeValue],
885 ) {
886 let part = &mut self.lods[lod_index].parts[part_index];
887
888 if shape_mesh_index == 0 {
890 self.model_data.shapes[shape_index].shape_mesh_start_index[lod_index] =
891 self.model_data.shape_meshes.len() as u16;
892 }
893
894 self.model_data.shape_meshes.push(ShapeMesh {
895 mesh_index_offset: self.model_data.meshes[part.mesh_index as usize].start_index,
896 shape_value_count: shape_values.len() as u32,
897 shape_value_offset: self.model_data.shape_values.len() as u32,
898 });
899
900 for shape_value in shape_values {
901 part.vertices.push(shape_value.replacing_vertex);
902
903 self.model_data.shape_values.push(ShapeValue {
904 base_indices_index: self.model_data.meshes[part.mesh_index as usize].start_index
905 as u16
906 + shape_value.base_index as u16,
907 replacing_vertex_index: self.model_data.meshes[part.mesh_index as usize].start_index
908 as u16
909 + (part.vertices.len() - 1) as u16,
910 })
911 }
912
913 self.model_data.shapes[shape_index].shape_mesh_count[lod_index] += 1;
914
915 self.update_headers();
916 }
917
918 pub(crate) fn update_headers(&mut self) {
919 for i in 0..self.file_header.lod_count {
921 let mut vertex_offset = 0;
922
923 for j in self.model_data.lods[i as usize].mesh_index
924 ..self.model_data.lods[i as usize].mesh_index
925 + self.model_data.lods[i as usize].mesh_count
926 {
927 let mesh = &mut self.model_data.meshes[j as usize];
928
929 mesh.start_index =
930 self.model_data.submeshes[mesh.submesh_index as usize].index_offset;
931
932 for i in 0..mesh.vertex_stream_count as usize {
933 mesh.vertex_buffer_offsets[i] = vertex_offset;
934 vertex_offset +=
935 mesh.vertex_count as u32 * mesh.vertex_buffer_strides[i] as u32;
936 }
937 }
938 }
939
940 for lod in &mut self.model_data.lods {
941 let mut total_vertex_buffer_size = 0;
942 let mut total_index_buffer_size = 0;
943
944 for j in lod.mesh_index..lod.mesh_index + lod.mesh_count {
946 let vertex_count = self.model_data.meshes[j as usize].vertex_count;
947 let index_count = self.model_data.meshes[j as usize].index_count;
948
949 let mut total_vertex_stride: u32 = 0;
950 for i in 0..self.model_data.meshes[j as usize].vertex_stream_count as usize {
951 total_vertex_stride +=
952 self.model_data.meshes[j as usize].vertex_buffer_strides[i] as u32;
953 }
954
955 total_vertex_buffer_size += vertex_count as u32 * total_vertex_stride;
956 total_index_buffer_size += index_count * size_of::<u16>() as u32;
957 }
958
959 let mut index_padding = total_index_buffer_size % 16;
961 if index_padding == 0 {
962 index_padding = 16;
963 } else {
964 index_padding = 16 - index_padding;
965 }
966
967 lod.vertex_buffer_size = total_vertex_buffer_size;
968 lod.index_buffer_size = total_index_buffer_size.wrapping_add(index_padding);
969 }
970
971 self.file_header.stack_size = self.file_header.calculate_stack_size();
973 self.file_header.runtime_size = self.model_data.calculate_runtime_size();
974
975 let data_offset = self.file_header.runtime_size
976 + size_of::<ModelFileHeader>() as u32
977 + self.file_header.stack_size;
978
979 let mut overall_offset: u32 = 0;
980
981 for lod in &mut self.model_data.lods {
982 lod.vertex_data_offset = data_offset + overall_offset;
984 overall_offset += lod.vertex_buffer_size;
985
986 lod.index_data_offset = data_offset + overall_offset;
988 overall_offset += lod.index_buffer_size;
989
990 lod.edge_geometry_data_offset = lod.index_data_offset;
995 }
996
997 for i in 0..self.lods.len() {
998 self.file_header.vertex_buffer_size[i] = self.model_data.lods[i].vertex_buffer_size;
999 }
1000
1001 for i in 0..self.lods.len() {
1002 self.file_header.vertex_offsets[i] = self.model_data.lods[i].vertex_data_offset;
1003 }
1004
1005 for i in 0..self.lods.len() {
1006 self.file_header.index_buffer_size[i] = self.model_data.lods[i].index_buffer_size;
1007 }
1008
1009 for i in 0..self.lods.len() {
1010 self.file_header.index_offsets[i] = self.model_data.lods[i].index_data_offset;
1011 }
1012
1013 self.model_data.header.shape_count = self.model_data.shapes.len() as u16;
1014 self.model_data.header.shape_mesh_count = self.model_data.shape_meshes.len() as u16;
1015 self.model_data.header.shape_value_count = self.model_data.shape_values.len() as u16;
1016 }
1017
1018 pub fn write_to_buffer(&self) -> Option<ByteBuffer> {
1019 let mut buffer = ByteBuffer::new();
1020
1021 {
1022 let mut cursor = Cursor::new(&mut buffer);
1023
1024 self.file_header.write(&mut cursor).ok()?;
1026
1027 self.model_data.write(&mut cursor).ok()?;
1028
1029 for (l, lod) in self.lods.iter().enumerate() {
1030 for part in lod.parts.iter() {
1031 let declaration =
1032 &self.model_data.header.vertex_declarations[part.mesh_index as usize];
1033
1034 for (k, vert) in part.vertices.iter().enumerate() {
1035 for element in &declaration.elements {
1036 cursor
1037 .seek(SeekFrom::Start(
1038 (self.model_data.lods[l].vertex_data_offset
1039 + self.model_data.meshes[part.mesh_index as usize]
1040 .vertex_buffer_offsets
1041 [element.stream as usize]
1042 + element.offset as u32
1043 + self.model_data.meshes[part.mesh_index as usize]
1044 .vertex_buffer_strides
1045 [element.stream as usize]
1046 as u32
1047 * k as u32) as u64,
1048 ))
1049 .ok()?;
1050
1051 match element.vertex_usage {
1052 VertexUsage::Position => match element.vertex_type {
1053 VertexType::Single4 => {
1054 MDL::write_single4(
1055 &mut cursor,
1056 &MDL::pad_slice(&vert.position, 1.0),
1057 )
1058 .ok()?;
1059 }
1060 VertexType::Half4 => {
1061 MDL::write_half4(
1062 &mut cursor,
1063 &MDL::pad_slice(&vert.position, 1.0),
1064 )
1065 .ok()?;
1066 }
1067 VertexType::Single3 => {
1068 MDL::write_single3(&mut cursor, &vert.position).ok()?;
1069 }
1070 _ => {
1071 panic!(
1072 "Unexpected vertex type for position: {:#?}",
1073 element.vertex_type
1074 );
1075 }
1076 },
1077 VertexUsage::BlendWeights => match element.vertex_type {
1078 VertexType::ByteFloat4 => {
1079 MDL::write_byte_float4(&mut cursor, &vert.bone_weight)
1080 .ok()?;
1081 }
1082 VertexType::Byte4 => {
1083 MDL::write_byte_float42(&mut cursor, &vert.bone_weight)
1084 .ok()?; }
1086 _ => {
1087 panic!(
1088 "Unexpected vertex type for blendweight: {:#?}",
1089 element.vertex_type
1090 );
1091 }
1092 },
1093 VertexUsage::BlendIndices => match element.vertex_type {
1094 VertexType::Byte4 => {
1095 MDL::write_byte4(&mut cursor, &vert.bone_id).ok()?;
1096 }
1097 _ => {
1098 panic!(
1099 "Unexpected vertex type for blendindice: {:#?}",
1100 element.vertex_type
1101 );
1102 }
1103 },
1104 VertexUsage::Normal => match element.vertex_type {
1105 VertexType::Half4 => {
1106 MDL::write_half4(
1107 &mut cursor,
1108 &MDL::pad_slice(&vert.normal, 0.0),
1109 )
1110 .ok()?;
1111 }
1112 VertexType::Single3 => {
1113 MDL::write_single3(&mut cursor, &vert.normal).ok()?;
1114 }
1115 _ => {
1116 panic!(
1117 "Unexpected vertex type for normal: {:#?}",
1118 element.vertex_type
1119 );
1120 }
1121 },
1122 VertexUsage::UV => match element.vertex_type {
1123 VertexType::Half4 => {
1124 let combined =
1125 [vert.uv0[0], vert.uv0[1], vert.uv1[0], vert.uv1[1]];
1126
1127 MDL::write_half4(&mut cursor, &combined).ok()?;
1128 }
1129 VertexType::Single4 => {
1130 let combined =
1131 [vert.uv0[0], vert.uv0[1], vert.uv1[0], vert.uv1[1]];
1132
1133 MDL::write_single4(&mut cursor, &combined).ok()?;
1134 }
1135 _ => {
1136 panic!(
1137 "Unexpected vertex type for uv: {:#?}",
1138 element.vertex_type
1139 );
1140 }
1141 },
1142 VertexUsage::BiTangent => match element.vertex_type {
1143 VertexType::ByteFloat4 => {
1144 MDL::write_tangent(&mut cursor, &vert.bitangent).ok()?;
1145 }
1146 _ => {
1147 panic!(
1148 "Unexpected vertex type for bitangent: {:#?}",
1149 element.vertex_type
1150 );
1151 }
1152 },
1153 VertexUsage::Tangent => {
1154 #[allow(clippy::match_single_binding)] match element.vertex_type {
1156 _ => {
1160 panic!(
1161 "Unexpected vertex type for tangent: {:#?}",
1162 element.vertex_type
1163 );
1164 }
1165 }
1166 }
1167 VertexUsage::Color => match element.vertex_type {
1168 VertexType::ByteFloat4 => {
1169 MDL::write_byte_float4(&mut cursor, &vert.color).ok()?;
1170 }
1171 _ => {
1172 panic!(
1173 "Unexpected vertex type for color: {:#?}",
1174 element.vertex_type
1175 );
1176 }
1177 },
1178 }
1179 }
1180 }
1181
1182 cursor
1183 .seek(SeekFrom::Start(
1184 (self.file_header.index_offsets[l]
1185 + (self.model_data.meshes[part.mesh_index as usize].start_index
1186 * size_of::<u16>() as u32)) as u64,
1187 ))
1188 .ok()?;
1189
1190 cursor.write_le(&part.indices).ok()?;
1191 }
1192 }
1193 }
1194
1195 Some(buffer)
1196 }
1197}
1198
1199impl ModelFileHeader {
1200 pub fn calculate_stack_size(&self) -> u32 {
1201 self.vertex_declaration_count as u32 * NUM_VERTICES * VERTEX_ELEMENT_SIZE as u32
1203 }
1204}
1205
1206impl ModelData {
1208 pub fn calculate_runtime_size(&self) -> u32 {
1209 2 + 2 + 4 + self.header.string_size
1213 + 56 + (self.element_ids.len() as u32 * 32)
1215 + (3 * 60) + self.meshes.len() as u32 * 36
1218 + self.attribute_name_offsets.len() as u32 * size_of::<u32>() as u32
1219 + self.header.terrain_shadow_mesh_count as u32 * 20
1220 + self.header.submesh_count as u32 * 16
1221 + self.header.terrain_shadow_submesh_count as u32 * 10
1222 + self.material_name_offsets.len() as u32 * size_of::<u32>() as u32
1223 + self.bone_name_offsets.len() as u32 * size_of::<u32>() as u32
1224 + self.bone_tables.len() as u32 * 132
1225 + self.header.shape_count as u32 * 16
1226 + self.header.shape_mesh_count as u32 * 12
1227 + self.header.shape_value_count as u32 * 4
1228 + 4 + self.submesh_bone_map.len() as u32 * 2
1230 + self.padding_amount as u32 + 1 + (4 * 32) + (self.header.bone_count as u32 * 32)
1233 }
1234}
1235
1236#[cfg(test)]
1237mod tests {
1238 use std::fs::read;
1239 use std::mem::size_of;
1240 use std::path::PathBuf;
1241
1242 use vertex_declarations::VERTEX_ELEMENT_SIZE;
1243
1244 use super::*;
1245
1246 #[test]
1247 fn test_file_header_size() {
1248 assert_eq!(0x44, size_of::<ModelFileHeader>());
1249 }
1250
1251 #[test]
1252 fn test_vertex_element_size() {
1253 assert_eq!(8, VERTEX_ELEMENT_SIZE);
1254 }
1255
1256 #[test]
1257 fn test_stack_size() {
1258 let example_header = ModelFileHeader {
1259 version: 0,
1260 stack_size: 0,
1261 runtime_size: 0,
1262 vertex_declaration_count: 6,
1263 material_count: 0,
1264 vertex_offsets: [0; 3],
1265 index_offsets: [0; 3],
1266 vertex_buffer_size: [0; 3],
1267 index_buffer_size: [0; 3],
1268 lod_count: 0,
1269 index_buffer_streaming_enabled: false,
1270 has_edge_geometry: false,
1271 };
1272
1273 assert_eq!(816, example_header.calculate_stack_size());
1274
1275 let example_header2 = ModelFileHeader {
1276 version: 0,
1277 stack_size: 0,
1278 runtime_size: 0,
1279 vertex_declaration_count: 2,
1280 material_count: 0,
1281 vertex_offsets: [0; 3],
1282 index_offsets: [0; 3],
1283 vertex_buffer_size: [0; 3],
1284 index_buffer_size: [0; 3],
1285 lod_count: 0,
1286 index_buffer_streaming_enabled: false,
1287 has_edge_geometry: false,
1288 };
1289
1290 assert_eq!(272, example_header2.calculate_stack_size());
1291 }
1292
1293 #[test]
1294 fn test_update_headers() {
1295 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1296 d.push("resources/tests");
1297 d.push("c0201e0038_top_zeroed.mdl");
1298
1299 let mut mdl = MDL::from_existing(&read(d).unwrap()).unwrap();
1300 let old_mdl = mdl.clone();
1301
1302 mdl.update_headers();
1303
1304 assert_eq!(mdl.file_header, old_mdl.file_header);
1306 assert_eq!(mdl.model_data, old_mdl.model_data);
1307 }
1308
1309 #[test]
1310 fn test_update_vertices() {
1311 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1312 d.push("resources/tests");
1313 d.push("c0201e0038_top_zeroed.mdl");
1314
1315 let mut mdl = MDL::from_existing(&read(d).unwrap()).unwrap();
1316 let old_mdl = mdl.clone();
1317
1318 for l in 0..old_mdl.lods.len() {
1319 for p in 0..old_mdl.lods[l].parts.len() {
1320 mdl.replace_vertices(
1321 l,
1322 p,
1323 &old_mdl.lods[l].parts[p].vertices,
1324 &old_mdl.lods[l].parts[p].indices,
1325 &old_mdl.lods[l].parts[p].submeshes,
1326 );
1327 }
1328 }
1329
1330 assert_eq!(mdl.file_header, old_mdl.file_header);
1332 assert_eq!(mdl.model_data, old_mdl.model_data);
1333 }
1334
1335 #[test]
1336 fn test_parsing() {
1337 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1338 d.push("resources/tests");
1339 d.push("c0201e0038_top_zeroed.mdl");
1340
1341 let mdl = MDL::from_existing(&read(d).unwrap()).unwrap();
1342
1343 assert_eq!(mdl.file_header.version, 16777221);
1345 assert_eq!(mdl.file_header.stack_size, 816);
1346 assert_eq!(
1347 mdl.file_header.stack_size,
1348 mdl.file_header.calculate_stack_size()
1349 );
1350 assert_eq!(mdl.file_header.runtime_size, 12544);
1351 assert_eq!(
1352 mdl.file_header.runtime_size,
1353 mdl.model_data.calculate_runtime_size()
1354 );
1355 assert_eq!(mdl.file_header.vertex_declaration_count, 6);
1356 assert_eq!(mdl.file_header.material_count, 2);
1357 assert_eq!(mdl.file_header.lod_count, 3);
1358 assert!(!mdl.file_header.index_buffer_streaming_enabled);
1359 assert!(!mdl.file_header.has_edge_geometry);
1360
1361 assert_eq!(mdl.model_data.header.radius, 1.5340779);
1363 }
1364
1365 #[test]
1366 fn test_invalid() {
1367 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1368 d.push("resources/tests");
1369 d.push("random");
1370
1371 MDL::from_existing(&read(d).unwrap());
1373 }
1374}