physis/model/
mod.rs

1// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#![allow(clippy::unnecessary_fallible_conversions)] // This wrongly trips on binrw code
5
6mod 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    // NOTE: these are most likely a combination of other flags
68    Unknown1 = 0x10,
69    Unknown2 = 0x5,  // TODO: seen in some bgparts
70    Unknown3 = 0xE4, // TODO: seen in some bgparts
71    Unknown4 = 0xE5, // TODO: seen in some bgparts
72    Unknown5 = 0x6,  // TODO: seen in some bgparts
73    Unknown6 = 0x3,  // TODO: seen in some bgparts
74    Unknown7 = 0x60, // TODO: seen in some bgparts
75}
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    // unused on win32 according to lumina devs
171    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    // align to 4 bytes
238    // TODO: use br align_to?
239    #[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    // TODO: try to unify these fields?
355    #[br(if(file_header.version <= 0x1000005))]
356    submesh_bone_map_size: u32,
357
358    // hehe, Dawntrail made this u16 instead of u32. fun?
359    #[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    // TODO: these are still wrong on Dawntrail!
370    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 bitangent1: [f32; 4], // TODO: need to figure out what the heck this could be
398    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/// Corresponds to a "Mesh" in an LOD
441#[derive(Debug, Clone)]
442pub struct Part {
443    mesh_index: u16,
444    pub vertices: Vec<Vertex>,
445    /// Indexed by VertexElement::stream
446    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                                    // Used for... terrain..?
673                                    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                    // Adapted from https://github.com/xivdev/Penumbra/blob/master/Penumbra/Import/Models/Export/MeshExporter.cs
729                    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                        // TODO: read the entire vertex data into a buffer
798                        // Handle the offsets within Novus itself
799                        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        // Update vertex count in header
866        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        // TODO: this is assuming they are added in order
897        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        // update values
928        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            // still slightly off?
953            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            // TODO: this can definitely be written better
968            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        // update lod values
980        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            // vertex
991            lod.vertex_data_offset = data_offset + overall_offset;
992            overall_offset += lod.vertex_buffer_size;
993
994            // index
995            lod.index_data_offset = data_offset + overall_offset;
996            overall_offset += lod.index_buffer_size;
997
998            // edge, but unused?
999            //lod.edge_geometry_data_offset = data_offset + overall_offset;
1000            //overall_offset += lod.edge_geometry_size;
1001
1002            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            // write file header
1033            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()?; // TODO: WRONG!
1093                                    }
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)] // TODO
1163                                    match element.vertex_type {
1164                                        /*VertexType::ByteFloat4 => {
1165                                            MDL::write_tangent(&mut cursor, &vert.binormal).ok()?;
1166                                        }*/
1167                                        _ => {
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        // From https://github.com/Ottermandias/Penumbra.GameData/blob/44021b93e6901c84b739bbf4d1c6350f4486cdbf/Files/MdlFile.cs#L11
1210        self.vertex_declaration_count as u32 * NUM_VERTICES * VERTEX_ELEMENT_SIZE as u32
1211    }
1212}
1213
1214// TODO: From Xande, need to be cleaned up :)
1215impl ModelData {
1216    pub fn calculate_runtime_size(&self) -> u32 {
1217        2   //StringCount
1218        + 2 // Unknown
1219        + 4 //StringSize
1220        + self.header.string_size
1221        + 56 //ModelHeader
1222        + (self.element_ids.len() as u32 * 32)
1223        + (3 * 60) // 3 Lods
1224        //+ ( /*file.ModelHeader.ExtraLodEnabled ? 40*/ 0 )
1225        + 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 // SubmeshBoneMapSize
1237        + self.submesh_bone_map.len() as u32 * 2
1238        + self.padding_amount as u32 + 1          // PaddingAmount and Padding
1239        + (4 * 32) // 4 BoundingBoxes
1240        + (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        // There should be no changes
1313        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        // There should be no changes
1339        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        // file header
1352        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        // model header
1370        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        // Feeding it invalid data should not panic
1380        MDL::from_existing(&read(d).unwrap());
1381    }
1382}