mxmxmx2tiles 0.1.0

Convert Bentley ContextCapture 3mx photogrammetry data to Cesium 3D Tiles
Documentation
use std::process::exit;
use tmx::{glb, tmxb};

fn main() {
    let args: Vec<String> = std::env::args().collect();
    if args.len() != 3 {
        eprintln!("usage: {} <input.3mxb> <output.glb>", args[0]);
        exit(2);
    }
    let input = &args[1];
    let output = &args[2];

    let data = std::fs::read(input).unwrap_or_else(|e| {
        eprintln!("read {input}: {e}");
        exit(1);
    });

    let parsed = tmxb::parse(&data).unwrap_or_else(|e| {
        eprintln!("parse: {e}");
        exit(1);
    });

    println!(
        "3mxb: {} textures, {} geometries",
        parsed.textures.len(),
        parsed.geometries.len()
    );

    let mut prims = Vec::new();
    let mut total_v = 0usize;
    let mut total_t = 0usize;

    for g in &parsed.geometries {
        let mesh = match openctm::decode(&g.ctm) {
            Ok(m) => m,
            Err(e) => {
                eprintln!("  CTM decode {} failed: {e}", g.id);
                continue;
            }
        };
        total_v += mesh.positions.len();
        total_t += mesh.indices.len() / 3;
        let jpeg = g
            .texture_id
            .as_ref()
            .and_then(|tid| parsed.textures.get(tid))
            .cloned();
        println!(
            "  {}: {} verts, {} tris, normals={}, uvs={}, tex={}",
            g.id,
            mesh.positions.len(),
            mesh.indices.len() / 3,
            mesh.normals.is_some(),
            mesh.uvs.is_some(),
            jpeg.is_some(),
        );
        let uvs = mesh
            .uvs
            .map(|uvs| uvs.iter().map(|t| [t[0], 1.0 - t[1]]).collect());
        prims.push(glb::Prim {
            positions: mesh.positions,
            normals: mesh.normals,
            uvs,
            indices: mesh.indices,
            jpeg,
        });
    }

    if prims.is_empty() {
        eprintln!("no geometry decoded");
        exit(1);
    }

    let glb = glb::write(&prims);
    std::fs::write(output, &glb).unwrap_or_else(|e| {
        eprintln!("write {output}: {e}");
        exit(1);
    });

    println!(
        "wrote {output}: {} primitives, {total_v} verts, {total_t} tris, {} bytes",
        prims.len(),
        glb.len()
    );
}