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