physis/model/
file_operations.rs

1// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use crate::ByteSpan;
5use crate::model::MDL;
6use binrw::{BinReaderExt, BinResult, BinWriterExt};
7use half::f16;
8use std::io::Cursor;
9
10/// Maximum value of byte, used to divide and multiply floats in that space [0.0..1.0] to [0..255]
11const MAX_BYTE_FLOAT: f32 = u8::MAX as f32;
12
13impl MDL {
14    pub(crate) fn read_byte_float4(cursor: &mut Cursor<ByteSpan>) -> Option<[f32; 4]> {
15        Some([
16            (f32::from(cursor.read_le::<u8>().ok()?) / MAX_BYTE_FLOAT),
17            (f32::from(cursor.read_le::<u8>().ok()?) / MAX_BYTE_FLOAT),
18            (f32::from(cursor.read_le::<u8>().ok()?) / MAX_BYTE_FLOAT),
19            (f32::from(cursor.read_le::<u8>().ok()?) / MAX_BYTE_FLOAT),
20        ])
21    }
22
23    pub(crate) fn write_byte_float4<T: BinWriterExt>(
24        cursor: &mut T,
25        vec: &[f32; 4],
26    ) -> BinResult<()> {
27        cursor.write_le::<[u8; 4]>(&[
28            (vec[0] * MAX_BYTE_FLOAT).round() as u8,
29            (vec[1] * MAX_BYTE_FLOAT).round() as u8,
30            (vec[2] * MAX_BYTE_FLOAT).round() as u8,
31            (vec[3] * MAX_BYTE_FLOAT).round() as u8,
32        ])
33    }
34
35    pub(crate) fn write_byte_float42<T: BinWriterExt>(
36        cursor: &mut T,
37        vec: &[f32; 4],
38    ) -> BinResult<()> {
39        cursor.write_le::<[u8; 4]>(&[
40            (vec[0]).round() as u8,
41            (vec[1]).round() as u8,
42            (vec[2]).round() as u8,
43            (vec[3]).round() as u8,
44        ])
45    }
46
47    pub(crate) fn read_tangent(cursor: &mut Cursor<ByteSpan>) -> Option<[f32; 4]> {
48        Some([
49            (f32::from(cursor.read_le::<u8>().ok()?) * 2.0 / MAX_BYTE_FLOAT - 1.0),
50            (f32::from(cursor.read_le::<u8>().ok()?) * 2.0 / MAX_BYTE_FLOAT - 1.0),
51            (f32::from(cursor.read_le::<u8>().ok()?) * 2.0 / MAX_BYTE_FLOAT - 1.0),
52            if (f32::from(cursor.read_le::<u8>().ok()?) * 2.0 / MAX_BYTE_FLOAT - 1.0) == 1.0 {
53                1.0
54            } else {
55                -1.0
56            },
57        ])
58    }
59
60    pub(crate) fn write_tangent<T: BinWriterExt>(cursor: &mut T, vec: &[f32; 4]) -> BinResult<()> {
61        cursor.write_le::<[u8; 4]>(&[
62            ((vec[0] + 1.0) * (MAX_BYTE_FLOAT / 2.0)).round() as u8,
63            ((vec[1] + 1.0) * (MAX_BYTE_FLOAT / 2.0)).round() as u8,
64            ((vec[2] + 1.0) * (MAX_BYTE_FLOAT / 2.0)).round() as u8,
65            if vec[3] > 0.0 { 255 } else { 0 },
66        ]) // SqEx uses 0 as -1, not 1
67    }
68
69    pub(crate) fn read_half4(cursor: &mut Cursor<ByteSpan>) -> Option<[f32; 4]> {
70        Some([
71            f16::from_bits(cursor.read_le::<u16>().ok()?).to_f32(),
72            f16::from_bits(cursor.read_le::<u16>().ok()?).to_f32(),
73            f16::from_bits(cursor.read_le::<u16>().ok()?).to_f32(),
74            f16::from_bits(cursor.read_le::<u16>().ok()?).to_f32(),
75        ])
76    }
77
78    pub(crate) fn write_half4<T: BinWriterExt>(cursor: &mut T, vec: &[f32; 4]) -> BinResult<()> {
79        cursor.write_le::<[u16; 4]>(&[
80            f16::from_f32(vec[0]).to_bits(),
81            f16::from_f32(vec[1]).to_bits(),
82            f16::from_f32(vec[2]).to_bits(),
83            f16::from_f32(vec[3]).to_bits(),
84        ])
85    }
86
87    pub(crate) fn read_half2(cursor: &mut Cursor<ByteSpan>) -> Option<[f32; 2]> {
88        Some([
89            f16::from_bits(cursor.read_le::<u16>().ok()?).to_f32(),
90            f16::from_bits(cursor.read_le::<u16>().ok()?).to_f32(),
91        ])
92    }
93
94    #[allow(dead_code)] // We will eventually use this
95    pub(crate) fn write_half2<T: BinWriterExt>(cursor: &mut T, vec: &[f32; 2]) -> BinResult<()> {
96        cursor.write_le::<[u16; 2]>(&[
97            f16::from_f32(vec[0]).to_bits(),
98            f16::from_f32(vec[1]).to_bits(),
99        ])
100    }
101
102    pub(crate) fn read_byte4(cursor: &mut Cursor<ByteSpan>) -> BinResult<[u8; 4]> {
103        cursor.read_le::<[u8; 4]>()
104    }
105
106    pub(crate) fn write_byte4<T: BinWriterExt>(cursor: &mut T, vec: &[u8; 4]) -> BinResult<()> {
107        cursor.write_le::<[u8; 4]>(vec)
108    }
109
110    pub(crate) fn read_single3(cursor: &mut Cursor<ByteSpan>) -> BinResult<[f32; 3]> {
111        cursor.read_le::<[f32; 3]>()
112    }
113
114    pub(crate) fn write_single3<T: BinWriterExt>(cursor: &mut T, vec: &[f32; 3]) -> BinResult<()> {
115        cursor.write_le::<[f32; 3]>(vec)
116    }
117
118    pub(crate) fn read_single4(cursor: &mut Cursor<ByteSpan>) -> BinResult<[f32; 4]> {
119        cursor.read_le::<[f32; 4]>()
120    }
121
122    pub(crate) fn write_single4<T: BinWriterExt>(cursor: &mut T, vec: &[f32; 4]) -> BinResult<()> {
123        cursor.write_le::<[f32; 4]>(vec)
124    }
125
126    pub(crate) fn read_unsigned_short4(cursor: &mut Cursor<ByteSpan>) -> BinResult<[u16; 4]> {
127        cursor.read_le::<[u16; 4]>()
128    }
129
130    pub(crate) fn pad_slice<const N: usize>(small_slice: &[f32; N], fill: f32) -> [f32; 4] {
131        let mut bigger_slice: [f32; 4] = [fill, fill, fill, fill];
132        bigger_slice[..N].copy_from_slice(&small_slice[..N]);
133        bigger_slice
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use crate::model::MDL;
140    use std::io::Cursor;
141
142    macro_rules! assert_delta {
143        ($x:expr, $y:expr, $d:expr) => {
144            for i in 0..4 {
145                if !($x[i] - $y[i] < $d || $y[i] - $x[i] < $d) {
146                    panic!();
147                }
148            }
149        };
150    }
151
152    #[test]
153    fn byte_float4() {
154        let a = [0.0, 1.0, 0.5, 0.25];
155
156        let mut v = vec![];
157        let mut cursor = Cursor::new(&mut v);
158
159        MDL::write_byte_float4(&mut cursor, &a).unwrap();
160
161        let mut read_cursor = Cursor::new(v.as_slice());
162
163        let b = MDL::read_byte_float4(&mut read_cursor).unwrap();
164        assert_delta!(b, a, 0.1);
165    }
166
167    #[test]
168    fn half4() {
169        let a = [0.0, 1.0, 0.5, 0.25];
170
171        let mut v = vec![];
172        let mut cursor = Cursor::new(&mut v);
173
174        MDL::write_half4(&mut cursor, &a).unwrap();
175
176        let mut read_cursor = Cursor::new(v.as_slice());
177        assert_eq!(MDL::read_half4(&mut read_cursor).unwrap(), a);
178    }
179
180    #[test]
181    fn half2() {
182        let a = [0.0, 1.0];
183
184        let mut v = vec![];
185        let mut cursor = Cursor::new(&mut v);
186
187        MDL::write_half2(&mut cursor, &a).unwrap();
188
189        let mut read_cursor = Cursor::new(v.as_slice());
190        assert_eq!(MDL::read_half2(&mut read_cursor).unwrap(), a);
191    }
192
193    #[test]
194    fn uint() {
195        let a = [5u8, 0u8, 3u8, 15u8];
196
197        let mut v = vec![];
198        let mut cursor = Cursor::new(&mut v);
199
200        MDL::write_byte4(&mut cursor, &a).unwrap();
201
202        let mut read_cursor = Cursor::new(v.as_slice());
203        assert_eq!(MDL::read_byte4(&mut read_cursor).unwrap(), a);
204    }
205
206    #[test]
207    fn single3() {
208        let a = [3.0, 0.0, -1.0];
209
210        let mut v = vec![];
211        let mut cursor = Cursor::new(&mut v);
212
213        MDL::write_single3(&mut cursor, &a).unwrap();
214
215        let mut read_cursor = Cursor::new(v.as_slice());
216        assert_eq!(MDL::read_single3(&mut read_cursor).unwrap(), a);
217    }
218
219    #[test]
220    fn single4() {
221        let a = [3.0, 0.0, -1.0, 12.0];
222
223        let mut v = vec![];
224        let mut cursor = Cursor::new(&mut v);
225
226        MDL::write_single4(&mut cursor, &a).unwrap();
227
228        let mut read_cursor = Cursor::new(v.as_slice());
229        assert_eq!(MDL::read_single4(&mut read_cursor).unwrap(), a);
230    }
231
232    #[test]
233    fn tangent() {
234        let a = [1.0, 0.5, -0.5, 1.0];
235
236        let mut v = vec![];
237        let mut cursor = Cursor::new(&mut v);
238
239        MDL::write_tangent(&mut cursor, &a).unwrap();
240
241        let mut read_cursor = Cursor::new(v.as_slice());
242        let tangent = MDL::read_tangent(&mut read_cursor).unwrap();
243        assert_delta!(tangent, a, 0.001);
244    }
245
246    #[test]
247    fn pad_slice() {
248        let a = [3.0, 0.0, -1.0];
249        let b = [3.0, 0.0, -1.0, 1.0];
250
251        assert_eq!(MDL::pad_slice(&a, 1.0), b);
252    }
253}