ferravex_graphics/
cooked.rs1use crate::{Color, Mesh, MeshError, Vec3, Vertex};
3impl Mesh {
4 pub fn to_cooked(&self) -> Result<Vec<u8>, MeshError> {
5 self.validate()?;
6 let mut out = b"GMSH0001".to_vec();
7 out.extend((self.vertices.len() as u32).to_le_bytes());
8 out.extend((self.indices.len() as u32).to_le_bytes());
9 for v in &self.vertices {
10 for f in v
11 .position
12 .to_array()
13 .into_iter()
14 .chain(v.normal.to_array())
15 .chain(v.color.to_array())
16 {
17 out.extend(f.to_le_bytes());
18 }
19 }
20 for i in &self.indices {
21 out.extend(i.to_le_bytes());
22 }
23 Ok(out)
24 }
25 pub fn from_cooked(bytes: &[u8]) -> Result<Self, MeshError> {
26 if bytes.len() < 16 || &bytes[..8] != b"GMSH0001" {
27 return Err(MeshError::InvalidTriangleCount);
28 }
29 let vertices = u32::from_le_bytes(bytes[8..12].try_into().unwrap()) as usize;
30 let indices = u32::from_le_bytes(bytes[12..16].try_into().unwrap()) as usize;
31 if vertices > 1_000_000 || indices > 6_000_000 {
32 return Err(MeshError::TooLarge);
33 }
34 if bytes.len() != 16 + vertices * 40 + indices * 4 {
35 return Err(MeshError::InvalidTriangleCount);
36 }
37 let mut result = Mesh {
38 vertices: Vec::with_capacity(vertices),
39 indices: Vec::with_capacity(indices),
40 };
41 for row in bytes[16..16 + vertices * 40].as_chunks::<40>().0 {
42 let f = |i: usize| f32::from_le_bytes(row[i * 4..i * 4 + 4].try_into().unwrap());
43 result.vertices.push(Vertex::new(
44 Vec3::new(f(0), f(1), f(2)),
45 Vec3::new(f(3), f(4), f(5)),
46 Color::new(f(6), f(7), f(8), f(9)),
47 ));
48 }
49 for row in bytes[16 + vertices * 40..].as_chunks::<4>().0 {
50 result.indices.push(u32::from_le_bytes(*row));
51 }
52 result.validate()?;
53 Ok(result)
54 }
55}
56#[cfg(test)]
57mod tests {
58 use super::*;
59 #[test]
60 fn cooked_mesh_roundtrip_rejects_truncation_bad_counts_indices_and_floats() {
61 let mesh = Mesh::cuboid(Vec3::ONE, Color::from_hex(0xcabb99));
62 let bytes = mesh.to_cooked().unwrap();
63 assert_eq!(Mesh::from_cooked(&bytes).unwrap(), mesh);
64 for end in 0..bytes.len() {
65 assert!(Mesh::from_cooked(&bytes[..end]).is_err());
66 }
67 let mut bad = bytes.clone();
68 bad[16..20].copy_from_slice(&f32::NAN.to_le_bytes());
69 assert_eq!(Mesh::from_cooked(&bad), Err(MeshError::NonFiniteVertex));
70 let mut bad = bytes.clone();
71 let last = bad.len() - 4;
72 bad[last..].copy_from_slice(&u32::MAX.to_le_bytes());
73 assert_eq!(Mesh::from_cooked(&bad), Err(MeshError::IndexOutOfBounds));
74 let mut bad = bytes;
75 bad[8..12].copy_from_slice(&u32::MAX.to_le_bytes());
76 assert_eq!(Mesh::from_cooked(&bad), Err(MeshError::TooLarge));
77 }
78}