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