Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
6a26546
openspec: propose moho-import (S2: Crust's importer reads through Moho)
doubleailes Oct 25, 2026
50d4023
openspec: moho-import reports memory and time instead of gating on them
doubleailes Oct 25, 2026
fcd6c4a
openspec: moho-import builds on Moho's closed gaps M1 to M7
doubleailes Oct 25, 2026
5ecc3a2
openspec: moho-import drops the direct importer instead of a route sw…
doubleailes Oct 25, 2026
b49800f
build(deps): pin openusd by rev, the commit Moho pins
doubleailes Oct 25, 2026
5170b7c
build(deps): depend on Moho, the scene index, at the rev pinning our …
doubleailes Oct 25, 2026
4423095
crust-core: a compile-time guard that Moho and crust share one openusd
doubleailes Oct 25, 2026
99c5674
CLAUDE.md: openusd pinned by rev with Moho, the bump order, co-develo…
doubleailes Oct 25, 2026
717a0df
crust-core tests: one primvar fixture file per live test
doubleailes Oct 25, 2026
aa87554
crust-core: import parity against baselines from the direct importer
doubleailes Oct 25, 2026
d31cacc
build(deps): bump moho to fc391a5, the merge of scene-index-gaps
doubleailes Oct 25, 2026
0cbce22
parity: image baselines from the direct importer, and the image run
doubleailes Oct 25, 2026
57fb50f
scripts: import_parity.sh, large-scene parity against a recorded base…
doubleailes Oct 25, 2026
3ee92f7
parity: name the working space in the digest, not its interned id
doubleailes Oct 25, 2026
eff978d
crust-render tests: a five-subtree stage imports alike streamed and not
doubleailes Oct 25, 2026
36537fe
usd-scene-import design: the index import's baselines (ALab, Moana, s…
doubleailes Oct 25, 2026
aa56094
crust-core: the import walks Moho's scene index, families bridged
doubleailes Oct 25, 2026
4054ebc
crust-core: stage facts behind index/stage_facts.rs; time range from …
doubleailes Oct 25, 2026
995e270
crust-core tests: the render-source fixture authors one /Render prim …
doubleailes Oct 25, 2026
6803536
parity: prototype numbers are the one listed exception
doubleailes Oct 25, 2026
2e8bfd8
crust-core: render settings read from the scene index
doubleailes Oct 25, 2026
b48dfb5
crust-core: products, vars and the precomp's settings read from the i…
doubleailes Oct 25, 2026
e9accc3
crust-core tests: a render pass applying all four collections
doubleailes Oct 25, 2026
982a2ca
crust-core: the active render pass read from the scene index
doubleailes Oct 25, 2026
e97881e
crust-core: cameras read from the scene index
doubleailes Oct 25, 2026
f5fe582
openspec: moho-import task 4.5 done (the live settings refresh reads …
doubleailes Oct 25, 2026
c703e99
parity: baselines for the samples that need files outside the repository
doubleailes Oct 25, 2026
4568088
crust-core: meshes, implicit shapes and spheres read from the scene i…
doubleailes Oct 26, 2026
6a1a16c
crust-core: basis and NURBS curves read from the scene index
doubleailes Oct 26, 2026
b377629
crust-core: crust:volume:* regions read from the scene index
doubleailes Oct 26, 2026
06cbff6
crust-core: material bindings read from the scene index
doubleailes Oct 26, 2026
640cf8f
scripts: import_parity.sh quotes its Python without apostrophes
doubleailes Oct 26, 2026
2119841
crust-core tests: point instancers and native instances nested
doubleailes Oct 26, 2026
af4cfa1
crust-core: bridged readers name instance proxies through their proto…
doubleailes Oct 26, 2026
61ee0b5
crust-core tests: a binding inside an instanced point-instancer proto…
doubleailes Oct 26, 2026
848457c
crust-core: a bridged proxy's binding is named in the proxy's namespace
doubleailes Oct 26, 2026
30de0a1
openspec: moho-import records the gaps found migrating, and a progres…
doubleailes Oct 26, 2026
649ce49
openspec: moho-import does not import undefined prims, its one behavi…
doubleailes Oct 26, 2026
c9bf19a
openspec: moho-import D3a names the one ALab camera undefined prims drop
doubleailes Oct 26, 2026
e92598e
parity: the image run lists the guided Cornell box, nondeterministic …
doubleailes Oct 26, 2026
3e09457
openspec: moho-import records M10, a network node outside a chunk's mask
doubleailes Oct 26, 2026
89ebbe1
crust: the .mtlx reference arc is read in stage_facts (9.3)
doubleailes Oct 26, 2026
87816b4
deps: moho at 788b1ac (M8, M9, M10) and openusd repointed to Moho's f…
doubleailes Oct 26, 2026
ca5e7ce
usd-scene-import design: ALab and Moana after the repin to openusd d7…
doubleailes Oct 26, 2026
a824551
crust: material networks and precomp graphs read the scene index (9.2)
doubleailes Oct 26, 2026
08c33d3
crust: materials outside a streamed chunk come from a side input (9.4)
doubleailes Oct 26, 2026
b122b8e
crust: material provenance and the live material rebuild read the ind…
doubleailes Oct 26, 2026
cd6b753
usd-scene-import design: ALab with materials on the index (9.6)
doubleailes Oct 26, 2026
e637edd
usd-scene-import design: ALab's doubled memory is a cause to find in …
doubleailes Oct 26, 2026
40980d2
crust: lights read the scene index (10.1)
doubleailes Oct 26, 2026
ac5cdc6
crust: light links judge the scene index's membership expressions (10.2)
doubleailes Oct 26, 2026
5668963
crust: lights and display filters are typed by IsA, as the typed gets…
doubleailes Oct 26, 2026
abf290f
crust: transforms and their motion read the scene index (5.1, 5.2)
doubleailes Oct 26, 2026
7c9a907
parity: an inexact shutter is the second listed exception, with a fix…
doubleailes Oct 26, 2026
7e1c513
test: an uncomposable transform stack's warning text, against the dir…
doubleailes Oct 26, 2026
86b4abd
crust: the live light rebuild reads its transform from the index too …
doubleailes Oct 26, 2026
a248442
openspec: moho-import 11.1 is done (regions on the index, the volume …
doubleailes Oct 26, 2026
219bc95
crust: point instancers and adaptive placement counts read the scene …
doubleailes Oct 26, 2026
9625b6b
crust: the walk and the prototype walk dispatch geometry by IsA on th…
doubleailes Oct 26, 2026
cb7e57b
crust: prototypes are found through Moho's instancers (12.1 in part)
doubleailes Oct 26, 2026
ff152cf
deps: moho at e64f228 (M11 re-rooting, nested point instancers, openu…
doubleailes Oct 26, 2026
9292efe
parity: the image run also lists warnings.rs's determinism test, for …
doubleailes Oct 26, 2026
b2ded84
deps: crust reaches openusd only through moho
doubleailes Oct 26, 2026
7166f72
crust: prototype prims and their materials read Moho's propagated pro…
doubleailes Oct 26, 2026
ec16bf5
crust: the walk reads the instancing chain, chain::standard's and cha…
doubleailes Oct 26, 2026
82f6767
crust: one chain per input, moho::chain::input (12.1)
doubleailes Oct 26, 2026
2157506
crust: adaptive placements counted per propagated prototype (12.2)
doubleailes Oct 26, 2026
135251e
crust: the walk visits prims by path, with no stage prim (12.3 in part)
doubleailes Oct 26, 2026
d9b8eb5
crust: the prototype walk reads only the instancing chain (12.3 in part)
doubleailes Oct 26, 2026
7c77705
crust: cameras and the live rebuilds compose transforms on the index …
doubleailes Oct 26, 2026
d7ab23d
crust: the bridge goes; readers read the scene index alone (12.3)
doubleailes Oct 26, 2026
523203d
openspec: moho-import 12.3 is done; the index module says what is rea…
doubleailes Oct 26, 2026
205fbc0
crust: the choice of subtrees to stream is read in stage_facts
doubleailes Oct 26, 2026
899f835
crust: two instancers of one instanced asset share its prototype (D3c)
doubleailes Oct 27, 2026
28f0cd9
crust: a fixture for point-instancer targets inside an instance (M12)
doubleailes Oct 27, 2026
abf6934
openspec: moho-import records ALab's cross-instancer reuse (D3c)
doubleailes Oct 27, 2026
15a3d8a
crust: bump moho to d09cc94 (M12, point-instancer targets inside inst…
doubleailes Oct 27, 2026
33c9fd6
crust: a point-instancer copy names its instance's materials (M12)
doubleailes Oct 27, 2026
a92b974
scripts: import_parity.sh always rebuilds the release binary
doubleailes Oct 27, 2026
376cd4a
crust: the time is passed, and prototype keys carry no epoch (13.1)
doubleailes Oct 27, 2026
76ad284
crust: the stage readers crust ls alone uses live in listing.rs (13.1)
doubleailes Oct 27, 2026
b56f41c
docs: CLAUDE.md's time and prototype-key rules follow the index (13.2)
doubleailes Oct 27, 2026
f910a4f
openspec: moho-import 13.1 is done
doubleailes Oct 27, 2026
ba62887
scripts: check_images.sh always rebuilds the release binary
doubleailes Oct 27, 2026
933ce47
crust: material cache comments say what keys it, now the epoch is gone
doubleailes Oct 27, 2026
6cedd7e
openspec: moho-import records 12.4, 14.2 and 14.3
doubleailes Oct 27, 2026
543ec8c
docs: the architecture map and the import's module table name Moho (1…
doubleailes Oct 27, 2026
b490941
openspec: the usd-scene-import record describes the import through Mo…
doubleailes Oct 27, 2026
3edf624
site: a page on the USD import, through Moho (15.3)
doubleailes Oct 27, 2026
58b1a25
openspec: one import route, through Moho, is implemented (15.4)
doubleailes Oct 27, 2026
c0f9abf
hydra: the CLI side of the hdCrust checks can run under the sysroot l…
doubleailes Oct 27, 2026
9028daa
openspec: moho-import 14.1 and 14.4 are done
doubleailes Oct 27, 2026
f58c950
crust: sample_imports_are_deterministic skips a sample whose external…
doubleailes Oct 27, 2026
ab55f84
scripts: the parity image run fails on any import difference and on a…
doubleailes Oct 27, 2026
b5ee1de
crust: a relative asset path is anchored on the layer that authors it
doubleailes Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
32 changes: 25 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,25 @@ beside the behavioural `spec.md` for the same capability.
Crust Render is a toy, physically-based path tracer written in safe Rust (edition 2024),
inspired by PBRT, *Ray Tracing in One Weekend*, and Autodesk Standard Surface / OpenPBR.
Scenes are loaded exclusively from **USD** (`.usda` / `.usdc` / `.usdz`) via the pure-Rust
[`openusd`](https://github.com/mxpv/openusd) crate (its GitHub `main`, patched over 0.8 in
the workspace `Cargo.toml`; typed schemas in `openusd-schemas`, which every stage must be
opened with — `usd_import::stage_builder()`). Do not add loaders for any other scene or mesh format.
[`openusd`](https://github.com/mxpv/openusd) crate (typed schemas in `openusd-schemas`,
which every stage must be opened with — `usd_import::stage_builder()`). The importer is
moving onto [Moho](https://github.com/doubleailes/Moho), a Hydra-2.0-style scene index over
openusd that Mantle's IPR and Obsidian also consume (`openspec/changes/moho-import`). Do not add loaders for any other scene or mesh format.

**openusd only through Moho.** No crate depends on `openusd` or `openusd-schemas`
itself: every crate names them as `moho::openusd` and `moho::openusd_schemas` (a file
adds `use moho::openusd;`), so Moho's pin is the stack's and the build carries one
openusd by construction; `crust-core/tests/one_openusd.rs` fails if a manifest names
openusd directly. openusd's features are Moho's: `moho/mmap`, `moho/serde`, and the
schema families (`skel`, `proc`, `media`, …; `geom`, `lux`, `render`, `shade`, `vol` are
always on). `moho` is pinned by `rev` in `[workspace.dependencies]`; bump in order:
**Moho, then crust-render, then Mantle**. To co-develop with a local Moho checkout, pass a Cargo config
file with a path patch rather than editing `.cargo/config.toml` (it is tracked):

```bash
printf '[patch."https://github.com/doubleailes/Moho"]\nmoho = { path = "../Moho/crates/moho" }\n' > /tmp/moho.toml
cargo --config /tmp/moho.toml test -p crust-core # rewrites Cargo.lock's moho entry: restore it before committing
```

## Where things are documented

Expand Down Expand Up @@ -144,8 +160,9 @@ with CMake against OpenUSD 26 from `hydra/pixi.toml`, outside `cargo build`. Imp
it — a wrapper adds a vtable hop per texel fetch that LTO cannot remove.
- `scene/usd_import/` siblings share through `pub(super)` and import each other
explicitly — no `use super::*`, so a file's `use` block is its real dependency list.
- The USD import is single-threaded and reads the time code from a thread-local
(`EvalTimeScope`); parallelising it means threading the time explicitly.
- The USD import reads USD through Moho's scene index, which owns the time: a reader
takes the index's time, or a time code passed to it explicitly (`crust ls`, the
stage facts). No reader keeps the time in a thread-local.
- No RNG outside `openqmc`: stratified draws through keyed sub-domains (`K_*` in
`tracer/path.rs`), incidental ones through `draw_rnd` / `domain.rng()`.

Expand Down Expand Up @@ -176,8 +193,9 @@ without the other (full list: `docs/architecture.md` § Invariants):
an MCP edit applied in place ↔ a full re-import of the same layer (`crust_core::live`
rebuilds through the import's own functions; a material property the import bakes
into geometry or a world flag belongs in `Signature`, in `rt_world.rs`).
- Anything keyed on a prototype path is scoped by the stage epoch
(`ImportCaches::epoch`): `/__Prototype_N` is renumbered per masked stage.
- Anything keyed on a prototype is keyed by Moho's propagated prototype path (or a
point instancer's copy), which is stable across a streamed import's stages, never
by openusd's `/__Prototype_N`, which is renumbered per masked stage.
- `Material::resolve` must return exactly what per-query shading would (pinned for every
material by `crust-core/tests/resolve.rs`); build it with `Resolution::new`, which takes
the emission from the parameters *before* `into_resolved` — the type allows no other order.
Expand Down
25 changes: 16 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 10 additions & 16 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,16 @@ half = "2.7"
# converts them into the working space (`crust_core::color`). From crates.io
# (doubleailes/ocio-rs#2); the committed `Cargo.lock` pins the exact version.
ocio = "0.2.0"
# The USD reader and its typed schema views, versioned in lockstep. Declared
# once so crust-core (the import) and crust-render (the MCP session's override
# layer, the probe examples) can never name different versions of the stage
# they share; `[patch.crates-io]` below swaps both for GitHub `main`. No
# default features (openusd has none since 0.8): each member adds the ones it
# needs — crust-core's `mmap` and schema families, crust-render's `serde`.
openusd = { version = "0.8", default-features = false }
openusd-schemas = { version = "0.8", default-features = false }
# Moho, the Hydra-2.0-style scene index over openusd that the import reads the
# stage through, pinned by rev. It is also the only way crust reaches openusd:
# every crate names `moho::openusd` and `moho::openusd_schemas`, so Moho's pin
# is the stack's and there is one openusd in the build by construction
# (`crust-core/tests/one_openusd.rs` fails if a crate depends on openusd
# directly). openusd's features are Moho's: `mmap`, `serde`, and the schema
# families `media`, `physics`, `proc`, `semantics`, `skel`, `ui` (`geom`, `lux`,
# `render`, `shade`, `vol` are always on). Bump order: Moho, then crust-render,
# then Mantle.
moho = { git = "https://github.com/doubleailes/Moho", rev = "d09cc946def933d3f0b3aa66c3b5249aff7c91d4" }

# The hot loops are spread across crates: the integrator in `crust-core`
# calls the kernel in `crust-rt`, which calls `glam`'s SIMD vector ops.
Expand Down Expand Up @@ -142,11 +144,3 @@ codegen-units = 1
inherits = "release"
panic = "unwind"

# openusd tracks its GitHub `main` rather than the 0.8.0 release, to take its
# fixes as they land. A patch rather than a `git =` in `[workspace.dependencies]`,
# so the requirement stays the release's `0.8` and dropping this section is the
# whole revert to it. Cargo.lock pins the commit; `cargo update -p
# openusd -p openusd-schemas` moves it.
[patch.crates-io]
openusd = { git = "https://github.com/mxpv/openusd", branch = "main" }
openusd-schemas = { git = "https://github.com/mxpv/openusd", branch = "main" }
31 changes: 19 additions & 12 deletions crates/crust-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ jit = ["dep:crust-jit"]
# `unsafe`, which this feature alone compiles. Off here and on in the
# renderer, like `jit`; `CRUST_USD_MMAP=0` turns it off at run time.
# openusd's own `mmap`, and with it `memmap2`, is compiled in only with this
# feature: openusd has no default features since 0.8.
mmap = ["openusd/mmap"]
# feature, forwarded through Moho: openusd has no default features since 0.8.
mmap = ["moho/mmap"]
# Test-only: every import is compared with the baseline recorded from the
# direct importer (`src/parity.rs`, the `moho-import` change). Turned on for
# this crate's tests by the dev-dependency on itself below, and for
# crust-render's by its own; never by a build that ships.
parity = []

[dependencies]
utils = { path = "../utils" }
Expand All @@ -35,16 +40,16 @@ crust-rt = { path = "../crust-rt" }
rayon.workspace = true
glam.workspace = true
tracing.workspace = true
openusd.workspace = true
# The typed schema views (UsdGeom / UsdLux / UsdShade / UsdRender) left the
# core crate in openusd 0.7 and ship separately, versioned in lockstep. UsdSkel,
# UsdVol, UsdProc and UsdMedia are compiled in for their types alone: the import
# composes a prim's xformOps only when the schema registry knows it is
# Xformable, and a SkelRoot, Volume, GenerativeProcedural or SpatialAudio from a
# family left out is unknown, so its transform would be dropped. With them,
# every Xformable type C++ USD defines is known (the remaining families,
# UsdPhysics, UsdUI and UsdSemantics, define none).
openusd-schemas = { workspace = true, features = ["geom", "lux", "shade", "render", "skel", "vol", "proc", "media"] }
# The scene index the import reads the stage through (`scene/usd_import/`), and
# crust's one way to openusd and its typed schema views (`moho::openusd`,
# `moho::openusd_schemas`). UsdGeom, UsdLux, UsdShade, UsdRender and UsdVol are
# always on in Moho; UsdSkel, UsdProc and UsdMedia are compiled in for their
# types alone: the import composes a prim's xformOps only when the schema
# registry knows it is Xformable, and a SkelRoot, GenerativeProcedural or
# SpatialAudio from a family left out is unknown, so its transform would be
# dropped. With them, every Xformable type C++ USD defines is known (UsdPhysics,
# UsdUI and UsdSemantics define none).
moho = { workspace = true, features = ["skel", "proc", "media"] }
# Pure-Rust OpenSubdiv port (Far topology refinement + Sdc rules) driving
# Catmull-Clark subdivision at USD import: zero dependencies,
# `forbid(unsafe_code)`. Pinned to a release tag; the committed `Cargo.lock`
Expand All @@ -68,6 +73,8 @@ serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"

[dev-dependencies]
# The import-parity check (`parity` feature) on for every test of this crate.
crust-core = { path = ".", features = ["parity"] }
criterion = "0.8"
# `std` for `tracing::subscriber::with_default`, which the import tests use
# to check which warnings a stage's render settings produce.
Expand Down
3 changes: 3 additions & 0 deletions crates/crust-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ mod lux;
mod material;
mod medium;
pub mod names;
/// Import parity against the direct importer's baselines; test-only.
#[cfg(feature = "parity")]
pub mod parity;
mod pdf;
pub mod place;
pub mod precomp;
Expand Down
43 changes: 42 additions & 1 deletion crates/crust-core/src/live.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,52 @@ pub struct Provenance {
pub(crate) stage: StageFacts,
}

#[cfg(feature = "parity")]
impl Provenance {
/// Writes the provenance into an import-parity digest
/// ([`crate::parity`]), maps sorted by key so it is stable text.
pub(crate) fn parity_digest(&self, out: &mut String) {
use std::fmt::Write as _;
let mut materials: Vec<_> = self.materials.iter().collect();
materials.sort_by(|a, b| a.0.cmp(b.0));
for (path, origin) in materials {
let _ = writeln!(out, "provenance material {path}: {origin:?}");
}
for read in &self.material_reads {
let _ = writeln!(out, "provenance material read: {read:?}");
}
for source in &self.arc_sources {
let _ = writeln!(out, "provenance arc source: {source}");
}
let mut lights: Vec<_> = self.lights.iter().collect();
lights.sort_by(|a, b| a.0.cmp(b.0));
for (path, origin) in lights {
let _ = writeln!(out, "provenance light {path}: {origin:?}");
}
for (ids, path) in &self.geometry {
let _ = writeln!(out, "provenance geometry {ids:?}: {path}");
}
for mesh in &self.meshes {
let _ = writeln!(out, "provenance mesh: {mesh:?}");
}
for (i, slot) in self.mesh_slots.iter().enumerate() {
let _ = writeln!(out, "provenance mesh slot {i}: {slot:?}");
}
let _ = writeln!(out, "provenance spheres: {:?}", self.spheres);
let _ = writeln!(out, "provenance volumes: {:?}", self.volumes);
// The working space by name: `Space` is an id interned per process,
// in whatever order the process met the spaces.
let mut stage = self.stage.clone();
let working = stage.working.take().map(|w| w.name().to_owned());
let _ = writeln!(out, "provenance stage: {stage:?} working {working:?}");
}
}

/// The slots one material fills.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct MaterialOrigin {
/// Every `World` slot whose material is this prim's: by `Arc` identity,
/// every streamed epoch's `Arc` of it included.
/// every streamed chunk's `Arc` of it included.
pub slots: Vec<u32>,
/// Whether a mesh displaced by it: its geometry depends on its inputs.
pub displaced: bool,
Expand Down
Loading
Loading