Conversation
Wrap the stage's shared state in `Rc<StageInner>` (mirroring C++ UsdStageRefPtr) with a `Deref` to it, so `Stage` clones cheaply and all interior mutability stays in per-field cells on `StageInner`. `Prim`, `Attribute`, `Relationship`, and `VariantSets` now own a `Stage` clone instead of borrowing `&'s Stage`, dropping the `'s` lifetime from the composed-handle API. Handles are `Clone` and can be stored past the call that produced them; the schema author modules drop the propagated lifetime mechanically. `Stage::initial_load_set` and `population_mask` lose `const` (field access now goes through the non-const `Rc`/`Deref`).
Introduce `usd::SchemaBase` (C++ `UsdSchemaBase`): a view wraps a `Prim` and exposes `prim()`, `path()`, `stage()`, `from_prim` / `into_prim`, and a required `KIND` const (`usd::SchemaKind`) driving the `is_concrete` / `is_typed` / `is_*_api_schema` queries. A blanket `From<S: SchemaBase> for Prim` unwraps any view to its prim.
Replace the freestanding read_*/define_* functions and decoded Read* structs with typed Prim newtype views mirroring the C++ UsdGeom class hierarchy: a SchemaBase → Imageable → Xformable → Boundable → Gprim → PointBased → Curves trait chain, with concrete views (Mesh, GeomSubset, Points, TetMesh, BasisCurves, NurbsCurves, HermiteCurves, NurbsPatch, PointInstancer) joining the existing Camera/Xform/Scope/shapes. Each view declares its chain via the impl_geom_schema! macro and exposes C++-style foo_attr()/create_foo_attr() handle pairs; authoring now lives on the view instead of a separate author/ module. Add Attribute::get_metadata for reading primvar interpolation through the handle, and From<scalar> conversions on sdf::Value so set() takes bare scalars. Drop the find_geom_prims/read_kind helpers and the decoded Read* structs; the token enums move into the geom module root.
Convert the freestanding read_*/define_*/apply_* functions and decoded Read* structs into typed Prim views, mirroring the geom migration. Lights are UsdGeom prims, so the lux feature now enables geom and reuses its Imageable/Xformable/Boundable traits: a `Light` interface trait carries the common UsdLuxLightAPI inputs, BoundableLight/NonboundableLight are the abstract bases, and the concrete lights, LightFilter, and the LightAPI/ ShapingAPI/ShadowAPI/LightListAPI applied schemas are newtype views with C++-style foo_attr()/create_foo_attr() handle pairs. Each view declares its chain via the impl_lux_schema! macro (including an applied_api arm for the single-apply API views). Drop find_lux_prims/ is_light_type and the Read* structs; keep the token enums. Collapse the module to tokens/traits/lights, with the shared helpers and macro in mod.
Attribute::get / get_at / get_metadata are now generic over T: TryFrom<sdf::Value>, so reads decode straight to the Rust type (attr.get::<f32>()?) or to sdf::Value for the raw value, mirroring C++ UsdAttribute::Get's templated out-type. Add From<T> for sdf::Value over scalars, fixed-size vectors/matrices, and their Vec<…> array forms, so set() takes bare values (set(vec![4]), set(vec![1.0_f64, …])). Types whose representation maps to several variants ([*;4] floats, Vec<String>) are omitted so the wrong variant is never chosen silently.
Convert the media schema to typed Prim views mirroring geom/lux: SpatialAudio (a geom::Xformable) and the AssetPreviewsAPI single-apply view, dropping the read_*/define_*/Read* surface. media now enables the geom feature, since SpatialAudio is UsdGeomXformable-derived. Add sdf::TimeCode (= SdfTimeCode), a value newtype over f64 with From/TryFrom<Value>, so timecode attributes read/write typed via get::<sdf::TimeCode>() / set(sdf::TimeCode(..)). Settle the schema-module convention: per family, mod.rs (glue + macro + helpers + token enums), tokens.rs, schema.rs (grouped view structs), and optional traits.rs. Fold geom's internal.rs into mod.rs, rename lux lights.rs to schema.rs, merge shade preview.rs into types.rs, and drop decorative box-drawing divider comments (with a CLAUDE.md rule).
Convert schemas::proc to the trait-view newtype pattern, completing the geom/lux/media/proc family migration: GenerativeProcedural is now a geom::Boundable view (proc gains the geom feature) replacing the old reader/author functions. Consolidate the schema-view helpers the migrated families had each copied: the get-gates (get_typed / get_typed_any / get_with_api) move into schemas::common with a unified slice-based get_with_api, and the one-liner authoring/apply wrappers (create / create_uniform / create_uniform_token / apply_api) are inlined at their call sites via the Attribute builder.
Make schema attribute get/set ergonomic. Each geom token enum gains From<Enum> for sdf::Value and TryFrom<sdf::Value>, so authoring is attr.set(SubdivisionScheme::Loop)? and reading is attr.get::<SubdivisionScheme>()?. Add TryFrom<Value> for the float vec-array payloads (Vec<[f32; 2/3/4]>) so point/normal/UV attributes extract directly via get::<Vec<[f32; 3]>>(). Refresh the README example to show reading a geom::Mesh through the typed view, dropping the older field-read and in-memory authoring snippets.
There was a problem hiding this comment.
Pull request overview
This PR overhauls the Rust schema surface to mirror the C++ OpenUSD “view over prim” model: schemas become typed, trait-composed views over usd::Prim with foo_attr() / create_foo_attr() accessors, and the stage/handle model is refactored so composed handles are cheaply cloneable and storable.
Changes:
- Introduces
usd::SchemaBase/usd::SchemaKindas the shared foundation for schema views and trait-based schema hierarchies. - Refactors
usd::Stageinto a cheapRc-backed handle (Stage(Rc<StageInner>)), removing lifetimes fromPrim/Attribute/Relationshiphandles. - Migrates
UsdGeom,UsdLux,UsdMedia, andUsdProcto the new trait-view model; updates tests, docs, and feature dependencies accordingly.
Reviewed changes
Copilot reviewed 90 out of 90 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/proc_reader.rs | Updates integration test to exercise new UsdProc view API. |
| tests/media_reader.rs | Updates integration tests to use UsdMedia views and adds token round-trip checks. |
| src/usd/stage.rs | Refactors Stage into an Rc handle and removes lifetimes from returned composed handles. |
| src/usd/schema.rs | Adds SchemaBase/SchemaKind trait foundation for view hierarchy. |
| src/usd/mod.rs | Wires new schema module and re-exports schema foundation types. |
| src/usd/collection.rs | Adapts collection helpers to lifetime-free Prim/Relationship handles. |
| src/sdf/value.rs | Expands typed conversions and adds From<T> for Value for unambiguous Rust types. |
| src/sdf/mod.rs | Introduces sdf::TimeCode as a first-class value type for timecode attributes. |
| src/schemas/vol/author.rs | Updates authoring helpers to lifetime-free Prim handles. |
| src/schemas/ui/author.rs | Updates UI authoring helpers to lifetime-free Prim/Attribute handles. |
| src/schemas/skel/author/skeleton.rs | Updates skel authoring handle types to lifetime-free Prim. |
| src/schemas/skel/author/skel_root.rs | Updates skel root authoring API to return lifetime-free Prim. |
| src/schemas/skel/author/skel_animation.rs | Updates skel animation authoring handles to lifetime-free Prim. |
| src/schemas/skel/author/blend_shape.rs | Updates blend shape authoring handles to lifetime-free Prim. |
| src/schemas/skel/author/binding.rs | Updates skel binding authoring handles to lifetime-free Prim. |
| src/schemas/shade/shader.rs | Updates shader authoring handles to lifetime-free Prim/Attribute. |
| src/schemas/shade/preview.rs | Removes old preview-surface reader module (moved/re-exposed elsewhere). |
| src/schemas/shade/mod.rs | Adjusts exports to re-home preview-surface APIs under types. |
| src/schemas/shade/material.rs | Updates material/node-graph authoring handles to lifetime-free Prim/Attribute. |
| src/schemas/shade/connectable.rs | Updates connectable authoring helpers to lifetime-free Prim/Attribute. |
| src/schemas/render/author/var.rs | Updates render authoring handles to lifetime-free Prim. |
| src/schemas/render/author/settings.rs | Updates render settings authoring handles and shared trait impls. |
| src/schemas/render/author/product.rs | Updates render product authoring handles and shared trait impls. |
| src/schemas/render/author/pass.rs | Updates render pass authoring handles to lifetime-free Prim. |
| src/schemas/render/author/base.rs | Updates shared render-settings base trait to lifetime-free Prim. |
| src/schemas/proc/types.rs | Removes old decoded read-struct type (replaced by views). |
| src/schemas/proc/schema.rs | Adds GenerativeProcedural schema view with attr/create accessors. |
| src/schemas/proc/read.rs | Removes old read_* API (replaced by typed view getters). |
| src/schemas/proc/mod.rs | Re-structures proc module around views + macro-based trait-chain impl. |
| src/schemas/proc/author.rs | Removes old authoring handle API (replaced by view authoring). |
| src/schemas/physics/author/scene.rs | Updates physics scene authoring handles to lifetime-free Prim. |
| src/schemas/physics/author/rigid_body.rs | Updates physics rigid-body/mass authoring handles to lifetime-free Prim. |
| src/schemas/physics/author/limit_drive.rs | Updates multi-apply physics authoring handles to lifetime-free Prim. |
| src/schemas/physics/author/joint.rs | Updates physics joint authoring handles and shared setter trait. |
| src/schemas/physics/author/groups.rs | Updates physics group/filter authoring handles to lifetime-free Prim. |
| src/schemas/physics/author/collision.rs | Updates collision/material authoring handles to lifetime-free Prim. |
| src/schemas/mod.rs | Updates schema family overview to reflect trait-view migrations. |
| src/schemas/media/types.rs | Removes old decoded types/read struct (replaced by views + enums in mod). |
| src/schemas/media/read.rs | Removes old read_* API (replaced by typed view getters). |
| src/schemas/media/previews.rs | Removes old AssetPreviews free functions (replaced by API view). |
| src/schemas/media/mod.rs | Re-structures media module around views + token enums + trait-chain macro. |
| src/schemas/media/author.rs | Removes old authoring handle API (replaced by view authoring). |
| src/schemas/lux/traits.rs | Introduces trait interfaces for shared light surfaces (Light, marker bases). |
| src/schemas/lux/author/shaping.rs | Removes old shaping/shadow authoring handles (replaced by views). |
| src/schemas/lux/author/nonboundable.rs | Removes old nonboundable light authoring helpers (replaced by views). |
| src/schemas/lux/author/mod.rs | Removes old lux authoring module surface (replaced by views). |
| src/schemas/lux/author/light_list.rs | Removes old LightList authoring helper (replaced by views). |
| src/schemas/lux/author/light_api.rs | Removes old LightAPI authoring helper (replaced by views). |
| src/schemas/lux/author/dome.rs | Removes old dome light authoring helpers (replaced by views). |
| src/schemas/lux/author/common.rs | Removes old lux authoring internals (replaced by view authoring). |
| src/schemas/lux/author/boundable.rs | Removes old boundable light authoring helpers (replaced by views). |
| src/schemas/geom/xform.rs | Removes old freestanding xform reader (replaced by trait/view model). |
| src/schemas/geom/tokens.rs | Adds additional geom token constants used by new trait/view accessors. |
| src/schemas/geom/read.rs | Removes old freestanding geom readers (replaced by trait/view model). |
| src/schemas/geom/points.rs | Adds Points/TetMesh concrete views built on PointBased chain. |
| src/schemas/geom/pointbased.rs | Adds PointBased trait with shared point-geometry accessors. |
| src/schemas/geom/imageable.rs | Adds Imageable trait with accessors + composed compute helpers. |
| src/schemas/geom/grouping.rs | Adds Xform/Scope concrete views. |
| src/schemas/geom/gprim.rs | Adds Gprim trait with shared gprim accessors. |
| src/schemas/geom/boundable.rs | Adds Boundable trait with extent accessors. |
| src/schemas/geom/author/xform.rs | Removes old xform authoring helpers (replaced by view authoring). |
| src/schemas/geom/author/shapes.rs | Removes old shape authoring helpers (replaced by views). |
| src/schemas/geom/author/mod.rs | Removes old geom authoring module surface (replaced by views). |
| src/schemas/geom/author/mesh.rs | Removes old mesh/subset authoring helpers (replaced by views). |
| src/schemas/geom/author/imageable.rs | Removes old imageable authoring helpers (replaced by views). |
| src/schemas/geom/author/common.rs | Removes old geom authoring internals (replaced by views). |
| src/schemas/geom/author/camera.rs | Removes old camera authoring helper (replaced by view). |
| src/schemas/common.rs | Updates shared helpers; adds view “get” gates (get_typed, get_with_api, etc.). |
| ROADMAP.md | Updates roadmap entries to reflect trait-view migration status. |
| README.md | Updates examples to show reading via schema views and Stage API changes. |
| CLAUDE.md | Updates contributor notes/docs to reflect new Stage/schema view architecture. |
| Cargo.toml | Makes lux/media/proc depend on geom; adds derive_more dependency. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Maksym Pavlenko <pavlenko.maksym@gmail.com>
The per-file token() decode helper in the reader tests is obsolete now that String: TryFrom<Value> accepts both Token and String. Replace each call site with get::<String>() / get_metadata::<String>() and remove the helper from all four reader test files.
Convert schemas::vol to the trait-view pattern, completing the geom/lux/media/proc/vol family migration: Volume is a geom::Gprim and the field prims build on geom::Xformable via the FieldBase / FieldAsset interface traits, with concrete OpenVDBAsset / Field3DAsset views. Replaces the old reader/author functions and gains the geom feature. Hoist the impl_token_value! macro (From/TryFrom<Value> for token enums) from geom into schemas::common so vol's VectorDataRoleHint reuses it instead of hand-rolling the conversions. Also drop a couple of redundant self.0 derefs in the vol and media views.
Rewrite the foo_attr() getter doc comments across geom, lux, media, and proc to the template used for vol: a plain description of what the attribute is and is for, the class-qualified C++ getter, and the value type plus the get::<T>() call to read it. Drops the old terse "attribute handle" one-liners; no code changes.
Convert schemas::physics to the trait-view pattern. The typed prims
(Scene, CollisionGroup, the generic Joint and its five subtypes via the
shared JointBase interface) and the seven single-apply API schemas
follow the established newtype-view pattern. DriveAPI and LimitAPI are
the first multiple-apply views: a {prim, name} newtype keyed by a DOF
instance, with apply/get/get_all (C++ Apply/Get/GetAll) and attributes
at drive:<dof>:physics:* / limit:<dof>:physics:*.
Replaces the old read_*/*Author functions; the JointAxis/DriveType/
CollisionApprox token enums reuse the shared impl_token_value! macro.
The integration test moves to the view API against the fixture.
Convert the UsdShade schema family from read_*/author helpers to the trait-view newtype pattern. A `Connectable` interface trait carries the `inputs:`/`outputs:` surface (plus `connectability`, `renderType`, and the `Attribute::connect_to` connection helper) and backs the typed `Shader` / `NodeGraph` / `Material` views; `MaterialBindingAPI` becomes a single-apply view keeping the direct/collection binding resolution, and `Material::compute_surface_source` + `read_preview_surface` stay as connection-following computation. Add a `base_name` helper for the inputs:/outputs: namespace. Dissolve the bespoke `find_shade_prims` walk (no UsdShade analog) in favour of stage traversal gated through the typed `get`, mirroring C++ `prim.IsA<UsdShadeMaterial>()`.
Convert UsdSkel from read_*/author helpers to the trait-view newtype pattern, gaining skel = ["geom"]: SkelRoot and Skeleton are geom Boundable prims, SkelAnimation and BlendShape are typed, and SkelBindingAPI is single-apply. Decoded getters (Skeleton::joints / bind_transforms, SkelBindingAPI::joint_indices / interpolation, …) own the numeric coercion the toolkit needs. Keep the time-independent object model (Topology, AnimMapper, SkeletonResolver, SkinningResolver, SkelAnimQuery, discover_bindings, the LBS / blend-shape math), rewired to build from the views via from_skeleton / from_binding constructors. Dissolve the bespoke find_skel_prims / find_skel_roots / discover_skeletons buckets in favour of stage traversal gated through the typed get. This completes the schema trait-view remodel — every family is now on the view pattern. Also drop the hidden `# Ok::<(), Box<dyn Error + Send + Sync>>(())` return line from every schema mod.rs `# Example`, switching the bodies to `.unwrap()` so the source reads as plainly as the rendered docs.
The trait-view migration left several schemas::common helpers without callers: every family now inlines attribute authoring via the Attribute builder and reads through view getters. Remove the author_* helpers, varying_attribute, and the read_asset/read_f64/read_int readers (kept only by the module-wide allow(dead_code)), and refresh the module doc. Keep the live get_typed/get_typed_any/get_with_api gates, read_token, value_as_asset_str, and the impl_token_value\! macro.
|
This is great - it's exactly the shape we landed on, and seeing it across all 10 families at once is convincing. The super-trait chain reads really naturally. The cross-family bit (lights inheriting the geom Xformable/Boundable surface) is the part I wasn't sure how you'd model, and pulling it in via the geom feature is clean. The generic And I'll hold off on namespace editing until this lands and build it on the new Prim views, so it doesn't fight the reshape. No real notes - happy to see it go in. |
This PR introduces a new schema API that is much closer to its C++
counterpart. Instead of freestanding
read_*/*Authorfunctionsreturning decoded structs, each schema is modelled as a typed "view"
into a
Prim— a newtype wrapper exposing C++-stylefoo_attr()/create_foo_attr()handle pairs.The C++ library models fairly deep schema hierarchies, and each subtype
carries a bunch of common properties we want to reuse. A geometry object
should have
Xformableproperties so it can be transformed; everyXformableis in turnImageable(visibility / purpose), and so on. Werepresent that chain with super-traits:
A concrete view (Mesh, Camera, Sphere, …) declares its place in
the chain via an impl__schema! macro and inherits every
ancestor's accessors for free.
The other interesting case is cross-family inheritance: UsdLux lights
derive from UsdGeomBoundable / UsdGeomXformable, so a light should
expose the geom transform/extent surface too. This models that directly
— lux, media, and proc enable the geom feature and build on its
trait chain, so e.g. all lights inherit the geom properties.
usd::SchemaBaseis the root of the view hierarchy (mirrorsUsdSchemaBase): a view wraps a Prim, declares its SchemaKind, and
unwraps back via into_prim.
Stagebecomes a cheap reference-counted handle instead of a borrowwith a lifetime, which is far easier to work with — Prim /
Attribute / Relationship now own a Stage clone and are Clone,
storable independently of the call that produced them. It is Rc +
per-field cells today, but Stage was refactored so swapping in
Arc/Mutex for a multithreaded environment is straightforward (in future).
Schemas migrated:
UsdGeomUsdLuxUsdMediaUsdPhysicsUsdProcUsdRenderUsdShadeUsdSkelUsdUiUsdVolExamples:
Geom — author and read back through the trait chain:
Lights example:
Generic over any
Xformable:It's very likely that I missed bunch of APIs at each level, but that should be easy to address on as needed basis.
cc: @bresilla WDYT?