Skip to main content

falcon_library/model3d/
off.rs

1//! OFF (Object File Format), ASCII: an `OFF` line, `<vertices> <faces>
2//! <edges>`, one `x y z` line per vertex and one `n a b c …` line per face.
3//!
4//! This reader exists for the exporter's round-trip test. It is not wired
5//! into file-type detection, thumbnails or the viewer's loader, so falcon
6//! does not open `.off` files. Polygons are fan-triangulated; per-face
7//! colour values after the indices are ignored.
8
9use super::{Mesh, Model, ParseError, Result};
10
11pub fn parse(bytes: &[u8]) -> Result<Model> {
12    let text = std::str::from_utf8(bytes).map_err(|_| ParseError("OFF is not text".into()))?;
13    let mut lines = text.lines().map(|l| l.split('#').next().unwrap_or("").trim()).filter(|l| !l.is_empty());
14    let bad = |what: &str| ParseError(format!("OFF: {what}"));
15    if lines.next() != Some("OFF") {
16        return Err(bad("missing OFF header"));
17    }
18    let counts: Vec<usize> = lines.next().ok_or_else(|| bad("missing counts"))?.split_whitespace().map_while(|t| t.parse().ok()).collect();
19    let (&nv, &nf) = (counts.first().ok_or_else(|| bad("bad counts"))?, counts.get(1).ok_or_else(|| bad("bad counts"))?);
20    let mut mesh = Mesh::default();
21    for _ in 0..nv {
22        let line = lines.next().ok_or_else(|| bad("too few vertices"))?;
23        let v: Vec<f32> = line.split_whitespace().take(3).map_while(|t| t.parse().ok()).collect();
24        let [x, y, z] = v[..] else { return Err(bad("bad vertex")) };
25        mesh.positions.push([x, y, z]);
26    }
27    for _ in 0..nf {
28        let line = lines.next().ok_or_else(|| bad("too few faces"))?;
29        let mut t = line.split_whitespace();
30        let n: usize = t.next().and_then(|s| s.parse().ok()).ok_or_else(|| bad("bad face"))?;
31        let ix: Vec<u32> = t.take(n).map_while(|s| s.parse().ok()).collect();
32        if ix.len() != n {
33            return Err(bad("bad face"));
34        }
35        for k in 1..n.saturating_sub(1) {
36            mesh.indices.extend([ix[0], ix[k], ix[k + 1]]);
37        }
38    }
39    Model { meshes: vec![mesh] }.finish()
40}