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