1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
mod raw;

use crate::{read_relative, ModelError, ReadRelative, Readable};
use itertools::Either;
use raw::*;
pub use raw::{MeshFlags, StripFlags, StripGroupFlags, Vertex};
use std::ops::Range;

pub const MDL_VERSION: i32 = 7;

type Result<T> = std::result::Result<T, ModelError>;

/// The vtx file contains the mesh data for each mesh in an mdl, indexing into the vvd file
#[derive(Debug, Clone)]
pub struct Vtx {
    pub header: VtxHeader,
    pub body_parts: Vec<BodyPart>,
}

impl Vtx {
    pub fn read(data: &[u8]) -> Result<Self> {
        let header = <VtxHeader as Readable>::read(data)?;
        Ok(Vtx {
            body_parts: read_relative(data, header.body_indexes())?,
            header,
        })
    }
}

#[derive(Debug, Clone)]
pub struct BodyPart {
    pub models: Vec<Model>,
}

impl ReadRelative for BodyPart {
    type Header = BodyPartHeader;

    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
        Ok(BodyPart {
            models: read_relative(data, header.model_indexes())?,
        })
    }
}

#[derive(Debug, Clone)]
pub struct Model {
    pub lods: Vec<ModelLod>,
}

impl ReadRelative for Model {
    type Header = ModelHeader;

    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
        Ok(Model {
            lods: read_relative(data, header.lod_indexes())?,
        })
    }
}

#[derive(Debug, Clone)]
pub struct ModelLod {
    pub meshes: Vec<Mesh>,
    pub switch_point: f32,
}

impl ReadRelative for ModelLod {
    type Header = ModelLodHeader;

    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
        Ok(ModelLod {
            meshes: read_relative(data, header.mesh_indexes())?,
            switch_point: header.switch_point,
        })
    }
}

#[derive(Debug, Clone)]
pub struct Mesh {
    pub strip_groups: Vec<StripGroup>,
    pub flags: MeshFlags,
}

impl ReadRelative for Mesh {
    type Header = MeshHeader;

    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
        Ok(Mesh {
            strip_groups: read_relative(data, header.strip_group_indexes())?,
            flags: header.flags,
        })
    }
}

#[derive(Debug, Clone)]
pub struct StripGroup {
    // todo topologies
    pub indices: Vec<u16>,
    pub vertices: Vec<Vertex>,
    pub strips: Vec<Strip>,
    pub flags: StripGroupFlags,
}

impl ReadRelative for StripGroup {
    type Header = StripGroupHeader;

    fn read(data: &[u8], header: Self::Header) -> Result<Self> {
        Ok(StripGroup {
            vertices: read_relative(data, header.vertex_indexes())?,
            strips: read_relative(data, header.strip_indexes())?,
            indices: read_relative(data, header.index_indexes())?,
            flags: header.flags,
        })
    }
}

#[derive(Debug, Clone)]
pub struct Strip {
    // todo bone state changes
    vertices: Range<usize>,
    pub flags: StripFlags,
    indices: Range<usize>,
}

impl ReadRelative for Strip {
    type Header = StripHeader;

    fn read(_data: &[u8], header: Self::Header) -> Result<Self> {
        Ok(Strip {
            vertices: header.vertex_indexes(),
            indices: header.index_indexes(),
            flags: header.flags,
        })
    }
}

impl Strip {
    pub fn vertices(&self) -> impl Iterator<Item = usize> + 'static {
        self.vertices.clone()
    }

    pub fn indices(&self) -> impl Iterator<Item = usize> + 'static {
        if self.flags.contains(StripFlags::IS_TRI_STRIP) {
            let offset = self.indices.start;
            Either::Left((0..self.indices.len()).flat_map(move |i| {
                let cw = i & 1;
                let idx = offset + i;
                [idx, idx + 1 - cw, idx + 2 - cw].into_iter().rev()
            }))
        } else {
            Either::Right(self.indices.clone().rev())
        }
    }
}