hydra-rs 0.0.4

Rust bindings to OpenUSD's Hydra rendering layer: scene-index ingestion, render-delegate enumeration, headless render to RGBA via Storm.
use std::env;
use std::fs;
use std::process;

use hydra_rs::Renderer;

const DEFAULT_ASSET: &str = "examples/hydra_test.usda";
const OUT_PATH: &str = "examples/hydra_render.rgba";
const WIDTH: u32 = 256;
const HEIGHT: u32 = 256;

fn main() {
    let path = env::args().nth(1).unwrap_or_else(|| DEFAULT_ASSET.to_string());

    println!("rendering {} at {}x{} via Storm…", path, WIDTH, HEIGHT);

    let mut renderer = match Renderer::new(&path) {
        Ok(r) => r,
        Err(e) => {
            eprintln!("renderer construction failed: {}", e.what());
            process::exit(1);
        }
    };
    renderer.set_size(WIDTH, HEIGHT);
    println!("active delegate: {}", renderer.current_renderer());

    let pixels = match renderer.render() {
        Ok(p) => p,
        Err(e) => {
            eprintln!("render failed: {}", e.what());
            process::exit(1);
        }
    };

    let expected_len = (WIDTH * HEIGHT * 4) as usize;
    if pixels.len() != expected_len {
        eprintln!(
            "wrong pixel count: got {} bytes, expected {}",
            pixels.len(),
            expected_len
        );
        process::exit(2);
    }

    let mut sum = [0u64; 4];
    let mut non_background = 0usize;
    let bg_r = (0.1f32 * 255.0) as u8;
    let bg_g = bg_r;
    let bg_b = (0.15f32 * 255.0) as u8;
    let bg_tol: i32 = 6;
    for px in pixels.chunks_exact(4) {
        sum[0] += px[0] as u64;
        sum[1] += px[1] as u64;
        sum[2] += px[2] as u64;
        sum[3] += px[3] as u64;
        let dr = (px[0] as i32 - bg_r as i32).abs();
        let dg = (px[1] as i32 - bg_g as i32).abs();
        let db = (px[2] as i32 - bg_b as i32).abs();
        if dr > bg_tol || dg > bg_tol || db > bg_tol {
            non_background += 1;
        }
    }
    let n = (WIDTH * HEIGHT) as u64;
    let mean = [
        sum[0] as f32 / n as f32,
        sum[1] as f32 / n as f32,
        sum[2] as f32 / n as f32,
        sum[3] as f32 / n as f32,
    ];

    println!("rendered {} bytes ({} pixels)", pixels.len(), WIDTH * HEIGHT);
    println!(
        "mean RGBA = ({:.1}, {:.1}, {:.1}, {:.1})",
        mean[0], mean[1], mean[2], mean[3]
    );
    println!(
        "non-background pixels = {} / {} ({:.1}%)",
        non_background,
        n,
        non_background as f32 * 100.0 / n as f32
    );

    fs::write(OUT_PATH, &pixels).expect("write rgba");
    println!("wrote raw RGBA8 to {} ({}x{})", OUT_PATH, WIDTH, HEIGHT);

    if non_background == 0 {
        eprintln!("no non-background pixels — render produced an empty image");
        process::exit(2);
    }
    if mean[0] <= mean[1] || mean[0] <= mean[2] {
        eprintln!(
            "warning: mean R ({:.1}) is not greater than mean G/B — sphere may not have rendered red",
            mean[0]
        );
    }
}