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::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    // unused on win32 according to lumina devs
162    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    // align to 4 bytes
229    // TODO: use br align_to?
230    #[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    // TODO: try to unify these fields?
346    #[br(if(file_header.version <= 0x1000005))]
347    submesh_bone_map_size: u32,
348
349    // hehe, Dawntrail made this u16 instead of u32. fun?
350    #[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    // TODO: these are still wrong on Dawntrail!
361    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 bitangent1: [f32; 4], // TODO: need to figure out what the heck this could be
389    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/// Corresponds to a "Mesh" in an LOD
432#[derive(Debug, Clone)]
433pub struct Part {
434    mesh_index: u16,
435    pub vertices: Vec<Vertex>,
436    /// Indexed by VertexElement::stream
437    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                                    // Used for... terrain..?
664                                    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                // TODO: optimize!
698                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                    // Adapted from https://github.com/xivdev/Penumbra/blob/master/Penumbra/Import/Models/Export/MeshExporter.cs
722                    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                        // TODO: read the entire vertex data into a buffer
791                        // Handle the offsets within Novus itself
792                        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        // Update vertex count in header
859        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        // TODO: this is assuming they are added in order
890        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        // update values
921        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            // still slightly off?
946            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            // TODO: this can definitely be written better
961            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        // update lod values
973        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            // vertex
984            lod.vertex_data_offset = data_offset + overall_offset;
985            overall_offset += lod.vertex_buffer_size;
986
987            // index
988            lod.index_data_offset = data_offset + overall_offset;
989            overall_offset += lod.index_buffer_size;
990
991            // edge, but unused?
992            //lod.edge_geometry_data_offset = data_offset + overall_offset;
993            //overall_offset += lod.edge_geometry_size;
994
995            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            // write file header
1026            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()?; // TODO: WRONG!
1086                                    }
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)] // TODO
1156                                    match element.vertex_type {
1157                                        /*VertexType::ByteFloat4 => {
1158                                            MDL::write_tangent(&mut cursor, &vert.binormal).ok()?;
1159                                        }*/
1160                                        _ => {
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        // From https://github.com/Ottermandias/Penumbra.GameData/blob/44021b93e6901c84b739bbf4d1c6350f4486cdbf/Files/MdlFile.cs#L11
1203        self.vertex_declaration_count as u32 * NUM_VERTICES * VERTEX_ELEMENT_SIZE as u32
1204    }
1205}
1206
1207// TODO: From Xande, need to be cleaned up :)
1208impl ModelData {
1209    pub fn calculate_runtime_size(&self) -> u32 {
1210        2   //StringCount
1211        + 2 // Unknown
1212        + 4 //StringSize
1213        + self.header.string_size
1214        + 56 //ModelHeader
1215        + (self.element_ids.len() as u32 * 32)
1216        + (3 * 60) // 3 Lods
1217        //+ ( /*file.ModelHeader.ExtraLodEnabled ? 40*/ 0 )
1218        + 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 // SubmeshBoneMapSize
1230        + self.submesh_bone_map.len() as u32 * 2
1231        + self.padding_amount as u32 + 1          // PaddingAmount and Padding
1232        + (4 * 32) // 4 BoundingBoxes
1233        + (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        // There should be no changes
1306        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        // There should be no changes
1332        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        // file header
1345        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        // model header
1363        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        // Feeding it invalid data should not panic
1373        MDL::from_existing(&read(d).unwrap());
1374    }
1375}