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