rust-usd 0.0.4

Rust bindings to OpenUSD (pxr C++): stage open, prim/mesh attrs, variants, sublayer authoring, UsdShade read+write, ArResolver hook.
#pragma once

#include "rust/cxx.h"

#include <pxr/usd/usd/stage.h>
#include <pxr/usd/usd/prim.h>
#include <pxr/usd/usd/variantSets.h>
#include <pxr/usd/usdGeom/mesh.h>
#include <pxr/usd/usdGeom/primvar.h>
#include <pxr/usd/usdShade/material.h>
#include <pxr/usd/usdShade/shader.h>

#include <cstdint>
#include <memory>
#include <string>
#include <vector>

namespace rust_usd {

struct Mesh;
struct VariantSet;
struct Primvar;
struct Material;
struct Shader;

struct Prim {
    pxr::UsdPrim prim;

    rust::String path() const;
    rust::String type_name() const;
    bool is_mesh() const;
    std::unique_ptr<std::vector<Prim>> children() const;
    std::unique_ptr<Mesh> as_mesh() const;

    std::unique_ptr<std::vector<std::string>> variant_set_names() const;
    std::unique_ptr<VariantSet> get_variant_set(rust::Str name) const;
};

struct Primvar {
    pxr::UsdGeomPrimvar primvar;

    explicit Primvar(pxr::UsdGeomPrimvar p) : primvar(std::move(p)) {}

    rust::String name() const;             // Primvar name without "primvars:" prefix
    rust::String interpolation() const;    // "constant" | "uniform" | "varying" | "vertex" | "faceVarying"
    rust::String type_name() const;        // "float[]", "color3f[]", "float3[]", etc.
    bool has_authored_value() const;
    bool is_indexed() const;
    std::unique_ptr<std::vector<int32_t>> indices() const;

    // Empty vector means "not authored or wrong type" — disambiguate via has_authored_value.
    std::unique_ptr<std::vector<float>> as_float_array() const;
    std::unique_ptr<std::vector<int32_t>> as_int_array() const;
    std::unique_ptr<std::vector<float>> as_vec2f_array() const;  // flattened [u,v, u,v, ...]
    std::unique_ptr<std::vector<float>> as_vec3f_array() const;  // covers float3 + color3f
};

struct Mesh {
    pxr::UsdGeomMesh mesh;

    rust::String prim_path() const;
    rust::String subdivision_scheme() const;

    std::unique_ptr<std::vector<float>> points() const;
    std::unique_ptr<std::vector<int32_t>> face_vertex_counts() const;
    std::unique_ptr<std::vector<int32_t>> face_vertex_indices() const;

    std::unique_ptr<std::vector<float>> normals() const;
    rust::String normals_interpolation() const;
    // UsdGeomMesh::orientation — "rightHanded" (default; CCW face
    // winding) or "leftHanded" (CW). Consumers that back-face cull
    // (e.g. wgpu's default Ccw front_face) must flip the index order
    // when leftHanded, otherwise the visible side gets culled and
    // the hidden side renders with reversed normals.
    rust::String orientation() const;
    std::unique_ptr<std::vector<float>> st() const;
    std::unique_ptr<std::vector<int32_t>> st_indices() const;

    std::unique_ptr<std::vector<float>> local_to_world() const;
    std::unique_ptr<std::vector<std::string>> bound_texture_paths() const;

    // Primvar enumeration + lookup.
    std::unique_ptr<std::vector<std::string>> primvar_names() const;
    std::unique_ptr<Primvar> primvar(rust::Str name) const;

    // Primvar authoring — writes land in the stage's current edit target.
    bool create_primvar_float(rust::Str name, rust::Slice<const float> values, rust::Str interpolation) const;
    bool create_primvar_int(rust::Str name, rust::Slice<const int32_t> values, rust::Str interpolation) const;
    bool create_primvar_vec2f(rust::Str name, rust::Slice<const float> values, rust::Str interpolation) const;
    bool create_primvar_vec3f(rust::Str name, rust::Slice<const float> values, rust::Str interpolation) const;
    bool create_primvar_color3f(rust::Str name, rust::Slice<const float> values, rust::Str interpolation) const;
    bool remove_primvar(rust::Str name) const;

    bool bind_material(const Material& material) const;
};

struct VariantSet {
    mutable pxr::UsdVariantSet variant_set;

    explicit VariantSet(pxr::UsdVariantSet v) : variant_set(std::move(v)) {}

    rust::String name() const;
    std::unique_ptr<std::vector<std::string>> variant_names() const;
    rust::String selection() const;
    bool set_selection(rust::Str variant_name) const;
    void clear_selection() const;
    bool has_authored_selection() const;
};

struct Stage {
    pxr::UsdStageRefPtr stage;

    std::unique_ptr<Prim> pseudo_root() const;
    std::unique_ptr<Prim> prim_at_path(rust::Str sdf_path) const;
    std::unique_ptr<std::vector<Mesh>> all_meshes() const;

    void load_path(rust::Str sdf_path) const;
    void unload_path(rust::Str sdf_path) const;

    void save_edit_layer() const;
    rust::String edit_layer_path() const;

    // UsdShade authoring entry points. Pair with open_for_painting so writes
    // land in the edit layer rather than the asset.
    std::unique_ptr<Prim> define_prim(rust::Str sdf_path, rust::Str type_name) const;
    std::unique_ptr<Material> create_material(rust::Str sdf_path) const;
    std::unique_ptr<Material> material_at_path(rust::Str sdf_path) const;
};

struct Shader {
    // Same rationale as VariantSet: UsdShadeShader's CreateInput/CreateOutput
    // aren't const, but the mutation lands on the layer rather than the
    // wrapper, so const member functions still need to call them.
    mutable pxr::UsdShadeShader shader;

    explicit Shader(pxr::UsdShadeShader s) : shader(std::move(s)) {}

    rust::String path() const;
    rust::String shader_id() const;

    // Constant inputs.
    bool set_input_float(rust::Str name, float value) const;
    bool set_input_color3f(rust::Str name, float r, float g, float b) const;
    bool set_input_asset(rust::Str name, rust::Str asset_path) const;
    bool set_input_token(rust::Str name, rust::Str value) const;

    // inputs:<input_name>.connect = <source_path>.outputs:<output_name>
    bool connect_input(rust::Str input_name, const Shader& source, rust::Str output_name) const;

    // type_name accepts: "float" | "float2" | "float3" | "color3f" | "asset" |
    //                    "token" | "int" | "string"
    bool declare_output(rust::Str output_name, rust::Str type_name) const;
};

struct Material {
    mutable pxr::UsdShadeMaterial material;

    explicit Material(pxr::UsdShadeMaterial m) : material(std::move(m)) {}

    rust::String path() const;

    // Define a child Shader prim under this material with `info:id = shader_id`.
    std::unique_ptr<Shader> create_shader(rust::Str name, rust::Str shader_id) const;

    // Wire this material's outputs:surface to source.outputs:surface.
    bool connect_surface(const Shader& source) const;
};

std::unique_ptr<Stage> open_stage(rust::Str path);
std::unique_ptr<Stage> open_stage_with_load(rust::Str path, bool load_all);
std::unique_ptr<Stage> open_for_painting(rust::Str asset_path, rust::Str edit_layer_path);

std::unique_ptr<Prim> copy_prim(const Prim& prim);
std::unique_ptr<Mesh> copy_mesh(const Mesh& mesh);

void install_preferred_resolver();
void register_plugin_directory(rust::Str dir);

// Register a URI scheme (e.g. "forge", "s3") that the Rust resolver claims.
// Paths beginning with `<scheme>://` get routed to the trait; everything else
// falls through to ArDefaultResolver. Repeated calls accumulate.
void register_uri_scheme(rust::Str scheme);

// Drop all currently registered URI schemes. install_uri_resolver calls this
// before re-registering so a second install replaces the first set.
void clear_uri_schemes();

}  // namespace rust_usd