rust-usd 0.0.4

Rust bindings to OpenUSD (pxr C++): stage open, prim/mesh attrs, variants, sublayer authoring, UsdShade read+write, ArResolver hook.
// Two ways to resolve `forge://` (or any custom URI scheme) through
// rust-usd:
//
//   1. The Rust trait route shown below: implement AssetResolver and call
//      install_forge_resolver before any Stage::open. rust-usd registers a
//      tiny plugInfo.json in $TMPDIR on first call so USD's plug system
//      can reach the type.
//
//   2. The pipeline route: ship a C++ ArResolver plugin somewhere your
//      asset pipeline already knows about (or rely on one that's already
//      there) and point PXR_PLUGINPATH_NAME at its directory before launch.
//      In that setup Stage::open("forge://asset/foo") works without any
//      install_forge_resolver call — USD finds and uses the existing
//      resolver. The Rust trait is for the case where you want resolution
//      logic to live in your application instead of as a separate plugin.

use std::env;
use std::process;

use rust_usd::{install_forge_resolver, AssetResolver, Stage};

/// Maps `forge://asset/cube` to `examples/forge_target.usda` next to this
/// example's working directory. Real consumers would consult an asset cache
/// or backend service here.
struct LocalForgeMap;

impl AssetResolver for LocalForgeMap {
    fn resolve(&self, uri: &str) -> Option<String> {
        let suffix = uri.strip_prefix("forge://asset/")?;
        let cwd = env::current_dir().ok()?;
        let candidate = cwd.join("examples").join(format!("forge_target_{}.usda", suffix));
        if candidate.exists() {
            return Some(candidate.to_string_lossy().into_owned());
        }
        // Fall back to the single test target so the demo works without
        // per-suffix files.
        let single = cwd.join("examples/forge_target.usda");
        single.exists().then(|| single.to_string_lossy().into_owned())
    }
}

fn main() {
    install_forge_resolver(LocalForgeMap);

    let stage = Stage::open("examples/forge_demo.usda").unwrap_or_else(|e| {
        eprintln!("failed to open forge_demo.usda: {}", e.what());
        process::exit(1);
    });

    let meshes = stage.meshes();
    println!(
        "forge_demo.usda yielded {} mesh(es) after forge:// resolution:",
        meshes.len()
    );
    for mesh in meshes {
        println!("  {}", mesh.prim_path());
    }

    if stage.meshes().is_empty() {
        eprintln!("no meshes — resolver may not have been wired up");
        process::exit(2);
    }
}