openusd 0.8.0

Rust native USD library
Documentation
//! Native Rust implementation of [Universal Scene Description](https://openusd.org) (USD).
//!
//! The crate aims for full feature parity with the C++ reference
//! implementation, and is structured to mirror its layout, so developers
//! familiar with the C++ codebase will find roughly the same modules, types, and
//! primitives ([`sdf`], [`pcp`], [`usd`], …).
//!
//! All major file formats are supported — text (`.usda`), binary (`.usdc`),
//! and archives (`.usdz`) — with no C++ dependencies.
//!
//! Typed views for the domain schemas (`UsdGeom`, `UsdShade`, `UsdSkel`, and
//! friends) build on this crate and ship separately as
//! [`openusd-schemas`](https://docs.rs/openusd-schemas).
//!
//! # Modules
//!
//! | Module | Purpose |
//! |--------|---------|
//! | [`sdf`] | Core data model — [`Value`](sdf::Value), [`Path`](sdf::Path), [`Spec`](sdf::Spec), [`AbstractData`](sdf::AbstractData) trait, mutable [`Data`](sdf::Data), and schema field keys. |
//! | [`usda`] | Text format. [`usda::parse`] / [`usda::read_file`] parse and [`TextWriter`](usda::TextWriter) emits `.usda` files. |
//! | [`usdc`] | Binary format. [`CrateData`](usdc::CrateData) parses and [`CrateWriter`](usdc::CrateWriter) emits `.usdc` files. |
//! | [`usdz`] | Archive format. [`Archive`](usdz::Archive) reads and [`ArchiveWriter`](usdz::ArchiveWriter) emits `.usdz` packages. |
//! | [`ar`] | Asset resolution. [`Resolver`](ar::Resolver) trait maps asset paths (`@...@`) to physical locations; [`DefaultResolver`](ar::DefaultResolver) searches the filesystem. |
//! | [`pcp`] | Prim Cache Population — the composition engine. Implements LIVRPS strength ordering, per-prim index caching, and namespace mapping via [`MapFunction`](pcp::MapFunction). |
//! | [`usd`] | Composed stage API. [`Stage`](usd::Stage) merges opinions across layers using [LIVERPS](https://docs.nvidia.com/learn-openusd/latest/creating-composition-arcs/strength-ordering/what-is-liverps.html) strength ordering, and [`SchemaRegistry`](usd::SchemaRegistry) supplies schema property fallbacks. Schema views generated by `openusd-build` join a crate through [`include_schema!`]. |
//! | [`usd_utils`] | Utilities over layers and stages (C++ `UsdUtils`). [`compute_all_dependencies`](usd_utils::compute_all_dependencies) lists the layers and assets a layer reaches, [`create_new_usdz_package`](usd_utils::create_new_usdz_package) packages them into a `.usdz`, and [`modify_asset_paths`](usd_utils::modify_asset_paths) rewrites the asset paths a layer authors. |
//! | [`kind`] | Kinds. [`Registry`](kind::Registry) holds the built-in model hierarchy and the kinds a site declares, and answers which kinds are models, groups and components. |
//! | [`gf`] | Graphics foundations — linear algebra types (`Vec3f`, `Matrix4d`, …) and the [`Interval`](gf::Interval) a time-sample query is asked over. |
//! | [`tf`] | Tools foundation — [`Token`](tf::Token), the interned-identifier string. |
//!
//! # Quick start
//!
//! Open a stage and walk its composed prims (`DEFAULT` prunes inactive,
//! unloaded, and abstract subtrees):
//!
//! ```no_run
//! use openusd::usd;
//!
//! let stage = usd::Stage::open("scene.usda")?;
//! stage.traverse(usd::PrimPredicate::DEFAULT, |prim_path| {
//!     println!("{prim_path}");
//! })?;
//! # Ok::<(), openusd::Error>(())
//! ```
//!
//! Query the composed scene through `UsdPrim` / `UsdAttribute`-style handles
//! and resolve an attribute's value across all composition arcs:
//!
//! ```no_run
//! use openusd::usd;
//!
//! let stage = usd::Stage::open("scene.usda")?;
//!
//! let sphere = stage.prim("/World/Sphere")?;
//! println!("type: {:?}", sphere.type_name()?);
//!
//! let radius = stage.attribute("/World/Sphere.radius")?;
//! if let Some(r) = radius.get::<f64>()? {
//!     println!("radius = {r}");
//! }
//! # Ok::<(), openusd::Error>(())
//! ```
//!
//! Author a scene in memory, then read a value back — no files involved:
//!
//! ```
//! use openusd::usd;
//!
//! let stage = usd::Stage::builder().in_memory("root.usda")?;
//!
//! stage.define_prim("/World")?.set_type_name("Xform")?;
//! stage.define_prim("/World/Sphere")?.set_type_name("Sphere")?;
//! stage.set_default_prim("World")?;
//!
//! stage.create_attribute("/World/Sphere.radius", "double")?.set(2.5_f64)?;
//! let radius = stage.attribute("/World/Sphere.radius")?.get::<f64>()?;
//! assert_eq!(radius, Some(2.5));
//!
//! # Ok::<(), openusd::Error>(())
//! ```
//!
//! Configure how a stage is opened through [`StageBuilder`](usd::StageBuilder) —
//! restrict population to a subtree, leave payloads unloaded, and inspect any
//! recoverable composition errors collected while loading:
//!
//! ```no_run
//! use openusd::usd;
//!
//! let stage = usd::Stage::builder()
//!     .mask(usd::StagePopulationMask::new(["/World/Hero"])?)
//!     .load(usd::InitialLoadSet::LoadNone)
//!     .open("scene.usda")?;
//!
//! // Recoverable composition errors discovered so far: the root layer stack at
//! // open, plus reference/payload diagnostics that accrue as prims are traversed.
//! for err in stage.composition_errors() {
//!     eprintln!("warning: {err}");
//! }
//! # Ok::<(), openusd::Error>(())
//! ```

// The generated schema data spells its paths from the crate root, as it does
// in any consumer; this is what makes those paths resolve inside the crate
// that defines them. It is for that one included file: hand-written code here
// still reaches its own modules through `crate::`.
extern crate self as openusd;

use std::sync::atomic::{AtomicU64, Ordering};

pub mod ar;
mod error;
pub mod gf;
pub mod kind;
pub mod pcp;
pub mod sdf;
pub mod tf;
pub mod usd;
pub mod usd_utils;
pub mod usda;
pub mod usdc;
pub mod usdz;

pub use error::{Error, Result};

/// An id no other call in this process returns, each greater than the last
/// the calling thread was given. Everything in the crate that needs a
/// process-unique id takes it from here: the ids of unrelated things come
/// from the one sequence, so each kind sees ids that rise with gaps between
/// them.
pub(crate) fn next_id() -> u64 {
    static NEXT: AtomicU64 = AtomicU64::new(0);
    NEXT.fetch_add(1, Ordering::Relaxed)
}

/// Includes the items `openusd-build` generated for one schema library into
/// the enclosing module, the way `prost::include_proto!` does. `$library` is
/// the library's `libraryName` (`"usdGeom"`), which names the generated file
/// in `OUT_DIR`; `OUT_DIR` is read at the expansion site, so the invoking
/// crate must have a build script. The expansion is a bare `include!`: the
/// generated file holds items only (no inner attributes or module docs),
/// spells every path in full so it sits beside whatever the module imports,
/// and the module is the consumer's, one library per module.
///
/// `openusd-build` ships in this repository and is not published yet, so a
/// crate outside it has nothing to generate with for now.
///
/// ```ignore
/// // build.rs
/// openusd_build::configure()
///     .search_path("schemas")
///     .schema("schemas/usdGeom/schema.usda")
///     .generate()?;
///
/// // src/lib.rs
/// pub mod geom {
///     openusd::include_schema!("usdGeom");
/// }
/// ```
#[macro_export]
macro_rules! include_schema {
    ($library:literal) => {
        // Types the literal, so a non-string one fails here at the call site
        // instead of as a file `concat!` could not find.
        const _: &str = $library;
        include!(concat!(env!("OUT_DIR"), "/", $library, ".rs"));
    };
}