draco-gltf 0.2.0

Load and save full glTF 2.0 and 2.1 draft scenes with Draco geometry
Documentation
//! Small deterministic interoperability helper used by CI and by humans.

use std::io;
use std::path::PathBuf;

use draco_gltf::{CompressionOptions, Import, MeshIndex, OutputFormat};

fn decoded_draco_stats(import: &Import) -> Result<(usize, usize), Box<dyn std::error::Error>> {
    let mut primitives = 0usize;
    let mut faces = 0usize;
    for primitive in import.draco_primitives() {
        let mesh = import.decode_draco_primitive(primitive)?;
        if mesh.num_faces() == 0 {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "Draco primitive decoded to zero faces",
            )
            .into());
        }
        primitives = primitives.checked_add(1).ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                "decoded primitive count overflow",
            )
        })?;
        faces = faces.checked_add(mesh.num_faces()).ok_or_else(|| {
            io::Error::new(io::ErrorKind::InvalidData, "decoded face count overflow")
        })?;
    }
    if primitives == 0 || faces == 0 {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "document contains no decodable Draco triangle faces",
        )
        .into());
    }
    Ok((primitives, faces))
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut args = std::env::args_os().skip(1);
    let input = PathBuf::from(args.next().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            "usage: gltf_tool <input.gltf|input.glb> [output.glb]",
        )
    })?);
    let output = args.next().map(PathBuf::from);
    if args.next().is_some() {
        return Err(io::Error::new(io::ErrorKind::InvalidInput, "too many arguments").into());
    }

    let mut import = draco_gltf::import(&input)?;
    if let Some(output) = output {
        let report = import.compress_primitive(MeshIndex(0), 0, CompressionOptions::default())?;
        let bytes = import.to_bytes(OutputFormat::GlbV2)?;
        std::fs::write(output, bytes)?;
        println!("compression_report={report:?}");
    } else {
        let (primitives, faces) = decoded_draco_stats(&import)?;
        println!("decoded_draco_primitives={primitives} decoded_faces={faces}");
    }
    Ok(())
}