hydra-rs 0.0.1

Rust bindings to OpenUSD's Hydra rendering layer: scene-index ingestion, render-delegate enumeration, headless render to RGBA via Storm.
//! Rust bindings to OpenUSD's Hydra rendering layer.
//!
//! v0.0.1 surface: feed a USD stage path into a `UsdImagingStageSceneIndex`
//! and inspect what Hydra sees, plus enumerate registered render delegate
//! plugins. Designed to live alongside `rust-usd`; the two crates currently
//! interop via path strings (no shared cxx types yet).

use cxx::UniquePtr;

#[cxx::bridge(namespace = "hydra_rs")]
mod ffi {
    unsafe extern "C++" {
        include!("hydra_bridge.h");

        type SceneIndex;

        fn populate_from_path(usd_path: &str) -> Result<UniquePtr<SceneIndex>>;
        fn list_render_delegate_ids() -> UniquePtr<CxxVector<CxxString>>;
        fn render_to_rgba(
            usd_path: &str,
            render_delegate_id: &str,
            width: u32,
            height: u32,
        ) -> Result<UniquePtr<CxxVector<u8>>>;

        fn stage_root(self: &SceneIndex) -> String;
        fn prim_count(self: &SceneIndex) -> usize;
        fn prim_paths(self: &SceneIndex) -> UniquePtr<CxxVector<CxxString>>;
    }
}

/// Wraps a `UsdImagingStageSceneIndex` and the `UsdStage` that backs it.
pub struct SceneIndex {
    inner: UniquePtr<ffi::SceneIndex>,
}

impl SceneIndex {
    /// Open `path` as a USD stage and ingest it into a Hydra scene index.
    pub fn from_path(path: &str) -> Result<Self, cxx::Exception> {
        Ok(Self {
            inner: ffi::populate_from_path(path)?,
        })
    }

    /// The root layer identifier the scene index is anchored on.
    pub fn stage_root(&self) -> String {
        self.inner.stage_root()
    }

    /// Total prims Hydra sees, including the absolute root.
    pub fn prim_count(&self) -> usize {
        self.inner.prim_count()
    }

    /// All prim SDF paths visible through the scene index, depth-first.
    pub fn prim_paths(&self) -> Vec<String> {
        self.inner
            .prim_paths()
            .iter()
            .map(|s| s.to_string())
            .collect()
    }
}

/// Render delegate plugin IDs registered with USD's plug system in this
/// process (e.g. `"HdStormRendererPlugin"`, `"HdEmbreeRendererPlugin"`).
/// Calling this triggers plugin discovery if it hasn't happened yet.
pub fn list_render_delegates() -> Vec<String> {
    ffi::list_render_delegate_ids()
        .iter()
        .map(|s| s.to_string())
        .collect()
}

/// Render the USD stage at `usd_path` to a tightly-packed RGBA8 buffer of
/// size `width * height * 4`. Pass an empty string for `render_delegate` to
/// use the default (Storm on macOS/Linux/Windows). Camera is a hardcoded
/// free camera at (5, 5, 5) looking at origin — replace once we expose
/// matrix-based camera control.
pub fn render_to_rgba(
    usd_path: &str,
    render_delegate: &str,
    width: u32,
    height: u32,
) -> Result<Vec<u8>, cxx::Exception> {
    let bytes = ffi::render_to_rgba(usd_path, render_delegate, width, height)?;
    Ok(bytes.iter().copied().collect())
}