use std::path::Path;
use std::sync::{Arc, Mutex};
use cxx::UniquePtr;
#[cxx::bridge(namespace = "rust_usd")]
mod ffi {
unsafe extern "C++" {
include!("usd_bridge.h");
type Stage;
type Prim;
type Mesh;
type VariantSet;
type Primvar;
type Material;
type Shader;
fn open_stage(path: &str) -> Result<UniquePtr<Stage>>;
fn open_stage_with_load(path: &str, load_all: bool) -> Result<UniquePtr<Stage>>;
fn open_for_painting(asset_path: &str, edit_layer_path: &str)
-> Result<UniquePtr<Stage>>;
fn copy_prim(prim: &Prim) -> UniquePtr<Prim>;
fn copy_mesh(mesh: &Mesh) -> UniquePtr<Mesh>;
fn install_preferred_resolver();
fn register_plugin_directory(dir: &str);
fn register_uri_scheme(scheme: &str);
fn clear_uri_schemes();
fn pseudo_root(self: &Stage) -> UniquePtr<Prim>;
fn prim_at_path(self: &Stage, sdf_path: &str) -> UniquePtr<Prim>;
fn all_meshes(self: &Stage) -> UniquePtr<CxxVector<Mesh>>;
fn load_path(self: &Stage, sdf_path: &str);
fn unload_path(self: &Stage, sdf_path: &str);
fn save_edit_layer(self: &Stage);
fn edit_layer_path(self: &Stage) -> String;
fn define_prim(self: &Stage, sdf_path: &str, type_name: &str) -> UniquePtr<Prim>;
fn create_material(self: &Stage, sdf_path: &str) -> UniquePtr<Material>;
fn material_at_path(self: &Stage, sdf_path: &str) -> UniquePtr<Material>;
fn path(self: &Prim) -> String;
fn type_name(self: &Prim) -> String;
fn is_mesh(self: &Prim) -> bool;
fn children(self: &Prim) -> UniquePtr<CxxVector<Prim>>;
fn as_mesh(self: &Prim) -> UniquePtr<Mesh>;
fn variant_set_names(self: &Prim) -> UniquePtr<CxxVector<CxxString>>;
fn get_variant_set(self: &Prim, name: &str) -> UniquePtr<VariantSet>;
fn name(self: &VariantSet) -> String;
fn variant_names(self: &VariantSet) -> UniquePtr<CxxVector<CxxString>>;
fn selection(self: &VariantSet) -> String;
fn set_selection(self: &VariantSet, variant_name: &str) -> bool;
fn clear_selection(self: &VariantSet);
fn has_authored_selection(self: &VariantSet) -> bool;
fn prim_path(self: &Mesh) -> String;
fn subdivision_scheme(self: &Mesh) -> String;
fn points(self: &Mesh) -> UniquePtr<CxxVector<f32>>;
fn face_vertex_counts(self: &Mesh) -> UniquePtr<CxxVector<i32>>;
fn face_vertex_indices(self: &Mesh) -> UniquePtr<CxxVector<i32>>;
fn normals(self: &Mesh) -> UniquePtr<CxxVector<f32>>;
fn normals_interpolation(self: &Mesh) -> String;
fn orientation(self: &Mesh) -> String;
fn st(self: &Mesh) -> UniquePtr<CxxVector<f32>>;
fn st_indices(self: &Mesh) -> UniquePtr<CxxVector<i32>>;
fn local_to_world(self: &Mesh) -> UniquePtr<CxxVector<f32>>;
fn bound_texture_paths(self: &Mesh) -> UniquePtr<CxxVector<CxxString>>;
fn primvar_names(self: &Mesh) -> UniquePtr<CxxVector<CxxString>>;
fn primvar(self: &Mesh, name: &str) -> UniquePtr<Primvar>;
fn create_primvar_float(self: &Mesh, name: &str, values: &[f32], interpolation: &str) -> bool;
fn create_primvar_int(self: &Mesh, name: &str, values: &[i32], interpolation: &str) -> bool;
fn create_primvar_vec2f(self: &Mesh, name: &str, values: &[f32], interpolation: &str) -> bool;
fn create_primvar_vec3f(self: &Mesh, name: &str, values: &[f32], interpolation: &str) -> bool;
fn create_primvar_color3f(self: &Mesh, name: &str, values: &[f32], interpolation: &str) -> bool;
fn remove_primvar(self: &Mesh, name: &str) -> bool;
fn name(self: &Primvar) -> String;
fn interpolation(self: &Primvar) -> String;
fn type_name(self: &Primvar) -> String;
fn has_authored_value(self: &Primvar) -> bool;
fn is_indexed(self: &Primvar) -> bool;
fn indices(self: &Primvar) -> UniquePtr<CxxVector<i32>>;
fn as_float_array(self: &Primvar) -> UniquePtr<CxxVector<f32>>;
fn as_int_array(self: &Primvar) -> UniquePtr<CxxVector<i32>>;
fn as_vec2f_array(self: &Primvar) -> UniquePtr<CxxVector<f32>>;
fn as_vec3f_array(self: &Primvar) -> UniquePtr<CxxVector<f32>>;
fn bind_material(self: &Mesh, material: &Material) -> bool;
fn path(self: &Material) -> String;
fn create_shader(self: &Material, name: &str, shader_id: &str) -> UniquePtr<Shader>;
fn connect_surface(self: &Material, source: &Shader) -> bool;
fn path(self: &Shader) -> String;
fn shader_id(self: &Shader) -> String;
fn set_input_float(self: &Shader, name: &str, value: f32) -> bool;
fn set_input_color3f(self: &Shader, name: &str, r: f32, g: f32, b: f32) -> bool;
fn set_input_asset(self: &Shader, name: &str, asset_path: &str) -> bool;
fn set_input_token(self: &Shader, name: &str, value: &str) -> bool;
fn connect_input(self: &Shader, input_name: &str, source: &Shader, output_name: &str) -> bool;
fn declare_output(self: &Shader, output_name: &str, type_name: &str) -> bool;
}
extern "Rust" {
fn uri_resolve(uri: &str) -> String;
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum InitialLoadSet {
All,
None,
}
pub trait AssetResolver: Send + Sync {
fn resolve(&self, uri: &str) -> Option<String>;
}
static URI_RESOLVER: Mutex<Option<Arc<dyn AssetResolver>>> = Mutex::new(None);
pub fn install_uri_resolver<R: AssetResolver + 'static>(schemes: &[&str], resolver: R) {
*URI_RESOLVER.lock().expect("URI_RESOLVER mutex poisoned") = Some(Arc::new(resolver));
if let Err(e) = ensure_plugin_registered() {
eprintln!("rust-usd: failed to register resolver plugInfo: {}", e);
return;
}
ffi::clear_uri_schemes();
for s in schemes {
ffi::register_uri_scheme(s);
}
ffi::install_preferred_resolver();
}
pub fn install_forge_resolver<R: AssetResolver + 'static>(resolver: R) {
install_uri_resolver(&["forge"], resolver);
}
fn uri_resolve(uri: &str) -> String {
let resolver = {
let guard = URI_RESOLVER.lock().expect("URI_RESOLVER mutex poisoned");
guard.clone()
};
match resolver.and_then(|r| r.resolve(uri)) {
Some(path) => path,
None => String::new(),
}
}
fn ensure_plugin_registered() -> std::io::Result<()> {
use std::sync::atomic::{AtomicBool, Ordering};
static REGISTERED: AtomicBool = AtomicBool::new(false);
if REGISTERED.swap(true, Ordering::AcqRel) {
return Ok(());
}
let exe = std::env::current_exe()?;
let plugin_dir = std::env::temp_dir().join(format!(
"rust-usd-resolver-plug-{}",
std::process::id()
));
std::fs::create_dir_all(&plugin_dir)?;
let exe_escaped = exe
.to_string_lossy()
.replace('\\', "\\\\")
.replace('"', "\\\"");
let plug_info = format!(
r#"{{
"Plugins": [
{{
"Info": {{
"Types": {{
"rust_usd::RustUriResolver": {{
"bases": ["ArDefaultResolver"]
}}
}}
}},
"Name": "rustUsdUriResolver",
"LibraryPath": "{}",
"Type": "library"
}}
]
}}
"#,
exe_escaped
);
std::fs::write(plugin_dir.join("plugInfo.json"), plug_info)?;
ffi::register_plugin_directory(plugin_dir.to_string_lossy().as_ref());
Ok(())
}
pub struct Stage {
inner: UniquePtr<ffi::Stage>,
}
impl Stage {
pub fn open(path: impl AsRef<Path>) -> Result<Self, cxx::Exception> {
let _ = ensure_plugin_registered();
let path_str = path.as_ref().to_string_lossy();
Ok(Self {
inner: ffi::open_stage(&path_str)?,
})
}
pub fn open_with_load_set(
path: impl AsRef<Path>,
load: InitialLoadSet,
) -> Result<Self, cxx::Exception> {
let _ = ensure_plugin_registered();
let load_all = matches!(load, InitialLoadSet::All);
let path_str = path.as_ref().to_string_lossy();
Ok(Self {
inner: ffi::open_stage_with_load(&path_str, load_all)?,
})
}
pub fn open_for_painting(
asset_path: impl AsRef<Path>,
edit_layer_path: impl AsRef<Path>,
) -> Result<Self, cxx::Exception> {
let _ = ensure_plugin_registered();
let asset = asset_path.as_ref().to_string_lossy();
let edits = edit_layer_path.as_ref().to_string_lossy();
Ok(Self {
inner: ffi::open_for_painting(&asset, &edits)?,
})
}
pub fn load(&self, sdf_path: &str) {
self.inner.load_path(sdf_path);
}
pub fn unload(&self, sdf_path: &str) {
self.inner.unload_path(sdf_path);
}
pub fn pseudo_root(&self) -> Prim {
Prim {
inner: self.inner.pseudo_root(),
}
}
pub fn prim_at_path(&self, sdf_path: &str) -> Option<Prim> {
let p = self.inner.prim_at_path(sdf_path);
if p.is_null() {
None
} else {
Some(Prim { inner: p })
}
}
pub fn meshes(&self) -> Vec<Mesh> {
self.inner
.all_meshes()
.iter()
.map(|m| Mesh {
inner: ffi::copy_mesh(m),
})
.collect()
}
pub fn save_edit_layer(&self) {
self.inner.save_edit_layer();
}
pub fn edit_layer_path(&self) -> String {
self.inner.edit_layer_path()
}
pub fn define_prim(&self, sdf_path: &str, type_name: &str) -> Option<Prim> {
let p = self.inner.define_prim(sdf_path, type_name);
if p.is_null() {
None
} else {
Some(Prim { inner: p })
}
}
pub fn create_material(&self, sdf_path: &str) -> Option<Material> {
let m = self.inner.create_material(sdf_path);
if m.is_null() {
None
} else {
Some(Material { inner: m })
}
}
pub fn material_at_path(&self, sdf_path: &str) -> Option<Material> {
let m = self.inner.material_at_path(sdf_path);
if m.is_null() {
None
} else {
Some(Material { inner: m })
}
}
}
pub struct Prim {
inner: UniquePtr<ffi::Prim>,
}
impl Prim {
pub fn path(&self) -> String {
self.inner.path()
}
pub fn type_name(&self) -> String {
self.inner.type_name()
}
pub fn is_mesh(&self) -> bool {
self.inner.is_mesh()
}
pub fn as_mesh(&self) -> Option<Mesh> {
let m = self.inner.as_mesh();
if m.is_null() {
None
} else {
Some(Mesh { inner: m })
}
}
pub fn children(&self) -> Vec<Prim> {
self.inner
.children()
.iter()
.map(|p| Prim {
inner: ffi::copy_prim(p),
})
.collect()
}
pub fn variant_set_names(&self) -> Vec<String> {
self.inner
.variant_set_names()
.iter()
.map(|s| s.to_string())
.collect()
}
pub fn variant_set(&self, name: &str) -> Option<VariantSet> {
let v = self.inner.get_variant_set(name);
if v.is_null() {
None
} else {
Some(VariantSet { inner: v })
}
}
}
pub struct VariantSet {
inner: UniquePtr<ffi::VariantSet>,
}
impl VariantSet {
pub fn name(&self) -> String {
self.inner.name()
}
pub fn variants(&self) -> Vec<String> {
self.inner
.variant_names()
.iter()
.map(|s| s.to_string())
.collect()
}
pub fn selection(&self) -> Option<String> {
let s = self.inner.selection();
if s.is_empty() {
None
} else {
Some(s)
}
}
pub fn set_selection(&self, variant: &str) -> bool {
self.inner.set_selection(variant)
}
pub fn clear_selection(&self) {
self.inner.clear_selection();
}
pub fn has_authored_selection(&self) -> bool {
self.inner.has_authored_selection()
}
}
pub struct Mesh {
inner: UniquePtr<ffi::Mesh>,
}
impl Mesh {
pub fn prim_path(&self) -> String {
self.inner.prim_path()
}
pub fn subdivision_scheme(&self) -> String {
self.inner.subdivision_scheme()
}
pub fn points(&self) -> Vec<f32> {
cxx_to_vec(self.inner.points())
}
pub fn points_xyz(&self) -> Vec<[f32; 3]> {
self.points()
.chunks_exact(3)
.map(|c| [c[0], c[1], c[2]])
.collect()
}
pub fn face_vertex_counts(&self) -> Vec<i32> {
cxx_to_vec(self.inner.face_vertex_counts())
}
pub fn face_vertex_counts_u32(&self) -> Vec<u32> {
self.face_vertex_counts()
.into_iter()
.map(|v| v as u32)
.collect()
}
pub fn face_vertex_indices(&self) -> Vec<i32> {
cxx_to_vec(self.inner.face_vertex_indices())
}
pub fn face_vertex_indices_u32(&self) -> Vec<u32> {
self.face_vertex_indices()
.into_iter()
.map(|v| v as u32)
.collect()
}
pub fn normals(&self) -> Vec<f32> {
cxx_to_vec(self.inner.normals())
}
pub fn normals_xyz(&self) -> Vec<[f32; 3]> {
self.normals()
.chunks_exact(3)
.map(|c| [c[0], c[1], c[2]])
.collect()
}
pub fn normals_interpolation(&self) -> String {
self.inner.normals_interpolation()
}
pub fn orientation(&self) -> String {
self.inner.orientation()
}
pub fn st(&self) -> Vec<f32> {
cxx_to_vec(self.inner.st())
}
pub fn st_uv(&self) -> Vec<[f32; 2]> {
self.st()
.chunks_exact(2)
.map(|c| [c[0], c[1]])
.collect()
}
pub fn st_indices(&self) -> Vec<i32> {
cxx_to_vec(self.inner.st_indices())
}
pub fn st_indices_u32(&self) -> Vec<u32> {
self.st_indices().into_iter().map(|v| v as u32).collect()
}
pub fn local_to_world(&self) -> [[f32; 4]; 4] {
let v = self.inner.local_to_world();
let mut m = [[0.0f32; 4]; 4];
for r in 0..4 {
for c in 0..4 {
m[r][c] = *v.get(r * 4 + c).expect("local_to_world returned <16 floats");
}
}
m
}
pub fn bound_texture_paths(&self) -> Vec<String> {
self.inner
.bound_texture_paths()
.iter()
.map(|s| s.to_string())
.collect()
}
pub fn primvar_names(&self) -> Vec<String> {
self.inner
.primvar_names()
.iter()
.map(|s| s.to_string())
.collect()
}
pub fn primvar(&self, name: &str) -> Option<Primvar> {
let p = self.inner.primvar(name);
if p.is_null() {
None
} else {
Some(Primvar { inner: p })
}
}
pub fn create_primvar_float(&self, name: &str, values: &[f32], interpolation: &str) -> bool {
self.inner.create_primvar_float(name, values, interpolation)
}
pub fn create_primvar_int(&self, name: &str, values: &[i32], interpolation: &str) -> bool {
self.inner.create_primvar_int(name, values, interpolation)
}
pub fn create_primvar_vec2f(&self, name: &str, values: &[f32], interpolation: &str) -> bool {
self.inner.create_primvar_vec2f(name, values, interpolation)
}
pub fn create_primvar_vec3f(&self, name: &str, values: &[f32], interpolation: &str) -> bool {
self.inner.create_primvar_vec3f(name, values, interpolation)
}
pub fn create_primvar_color3f(&self, name: &str, values: &[f32], interpolation: &str) -> bool {
self.inner.create_primvar_color3f(name, values, interpolation)
}
pub fn remove_primvar(&self, name: &str) -> bool {
self.inner.remove_primvar(name)
}
pub fn bind_material(&self, material: &Material) -> bool {
self.inner.bind_material(&material.inner)
}
}
pub struct Material {
inner: UniquePtr<ffi::Material>,
}
impl Material {
pub fn path(&self) -> String {
self.inner.path()
}
pub fn create_shader(&self, name: &str, shader_id: &str) -> Option<Shader> {
let s = self.inner.create_shader(name, shader_id);
if s.is_null() {
None
} else {
Some(Shader { inner: s })
}
}
pub fn connect_surface(&self, source: &Shader) -> bool {
self.inner.connect_surface(&source.inner)
}
}
pub struct Shader {
inner: UniquePtr<ffi::Shader>,
}
impl Shader {
pub fn path(&self) -> String {
self.inner.path()
}
pub fn shader_id(&self) -> String {
self.inner.shader_id()
}
pub fn set_input_float(&self, name: &str, value: f32) -> bool {
self.inner.set_input_float(name, value)
}
pub fn set_input_color3f(&self, name: &str, r: f32, g: f32, b: f32) -> bool {
self.inner.set_input_color3f(name, r, g, b)
}
pub fn set_input_asset(&self, name: &str, asset_path: &str) -> bool {
self.inner.set_input_asset(name, asset_path)
}
pub fn set_input_token(&self, name: &str, value: &str) -> bool {
self.inner.set_input_token(name, value)
}
pub fn connect_input(&self, input_name: &str, source: &Shader, output_name: &str) -> bool {
self.inner
.connect_input(input_name, &source.inner, output_name)
}
pub fn declare_output(&self, output_name: &str, type_name: &str) -> bool {
self.inner.declare_output(output_name, type_name)
}
}
pub struct Primvar {
inner: UniquePtr<ffi::Primvar>,
}
impl Primvar {
pub fn name(&self) -> String {
self.inner.name()
}
pub fn interpolation(&self) -> String {
self.inner.interpolation()
}
pub fn type_name(&self) -> String {
self.inner.type_name()
}
pub fn has_authored_value(&self) -> bool {
self.inner.has_authored_value()
}
pub fn is_indexed(&self) -> bool {
self.inner.is_indexed()
}
pub fn indices(&self) -> Vec<i32> {
cxx_to_vec(self.inner.indices())
}
pub fn as_float_array(&self) -> Vec<f32> {
cxx_to_vec(self.inner.as_float_array())
}
pub fn as_int_array(&self) -> Vec<i32> {
cxx_to_vec(self.inner.as_int_array())
}
pub fn as_vec2f_array(&self) -> Vec<f32> {
cxx_to_vec(self.inner.as_vec2f_array())
}
pub fn as_vec3f_array(&self) -> Vec<f32> {
cxx_to_vec(self.inner.as_vec3f_array())
}
}
fn cxx_to_vec<T: cxx::vector::VectorElement + Copy>(v: UniquePtr<cxx::CxxVector<T>>) -> Vec<T> {
v.iter().copied().collect()
}