Skip to content

feat: nested usdz reading and UsdUtils packaging - #141

Open
bresilla wants to merge 5 commits into
mxpv:mainfrom
bresilla:feat/usdz-packaging
Open

bresilla wants to merge 5 commits into
mxpv:mainfrom
bresilla:feat/usdz-packaging

Conversation

@bresilla

@bresilla bresilla commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Third one from the batch (after #139 and #140), the USDZ side. I needed to save an edited stage as a .usdz with everything it uses, so I ported the parts of C++ UsdUtils that do that, plus reading packages nested in packages, which C++ already handles. One thing per commit again: 1 and 2 are standalone, 4 builds on 3, and 5 builds on 3 and 4.

feat(usdz): read packages nested in packages: C++ opens outer.usdz[inner.usdz] and composes a reference to a package inside a package, here it was refused. The resolver now reads one bracket level at a time, and a nested package anchors to its default layer (outer.usdz[inner.usdz[first.usda]]). Archive::read on a .usdz entry reads that package's default layer, so the NestedPackage error is gone.

perf(usdz): find default layer without full read: the TODO(perf) in UsdzFileFormat::resolve_layer, it read the whole package just to list the central directory. Now it opens the asset and lets ZipArchive read only the directory.

feat(usd_utils): compute all dependencies: a new usd_utils module (C++ UsdUtils) with compute_all_dependencies, the port of UsdUtilsComputeAllDependencies. It takes the stage because its resolver and already open layers play the part of the C++ global resolver and layer registry, so unsaved edits are seen like in C++. It follows every variant and skips deleted list-op items, and on the same scene it gives the same layers, assets and unresolved paths as C++ 25.05. Variable expressions, UDIM patterns and clip templates, which C++ expands, return an error for now.

feat(usd_utils): create new usdz package: the port of UsdUtilsCreateNewUsdzPackage, with the C++ layout. The root goes first (or under first_layer_name), layers keep their format, ./ and ../ paths that stay inside the package keep their place, and everything else moves into 0/, 1/, ... per source directory with its path rewritten. A referenced .usdz is stored whole, deleted references are packaged too so the deletes still match, a missing layer is skipped and returned, and a missing texture fails the package. The one difference: C++ 25.05 writes moved paths as N/file.usda even from a layer in a subdirectory (and in one case points at the wrong number), so its own package ends up with broken references. Here they are written relative to the layer, and C++ opens the result with everything composing.

feat(usd_utils): modify asset paths: the port of UsdUtilsModifyAssetPaths. It visits the same paths C++ does (once per distinct path, clip templates and expressions as authored), and an empty result removes the path like in C++. The only difference is that C++ drops every sublayer offset as soon as one sublayer path changes, while here they stay with their sublayers. This is what I use to export a layer to another directory: anchor its paths, then export.

One question: are you ok with a usd_utils module for this? I went with it because it mirrors C++ UsdUtils, and I assume more of it will come over the same way (flattening helpers, stage cache, asset localization and so on), so it felt like the right home rather than putting these on Stage or in usdz. If you'd rather have them somewhere else, tell me and I'll move them.

Copilot AI balanced review requested due to automatic review settings October 4, 2026 16:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Dependency filtering has correctness gaps, and invalid first-layer names can produce unreadable packages.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Adds nested USDZ support and a C++-aligned usd_utils module for dependency discovery, asset-path rewriting, and package creation.

Changes:

  • Resolves and reads nested USDZ packages efficiently.
  • Adds dependency discovery and asset-path modification utilities.
  • Packages stages and dependencies into new USDZ archives.
File Description
crates/​openusd/​src/​ar.rs Reads recursively nested package entries.
crates/​openusd/​src/​error.rs Adds dependency errors.
crates/​openusd/​src/​lib.rs Exposes usd_utils.
crates/​openusd/​src/​sdf/​layer_registry.rs Exposes resolver-backed asset opening internally.
crates/​openusd/​src/​usd_utils/​dependencies.rs Implements dependency discovery and path rewriting.
crates/​openusd/​src/​usd_utils/​mod.rs Defines the utility module API.
crates/​openusd/​src/​usd_utils/​package.rs Implements USDZ dependency packaging.
crates/​openusd/​src/​usdz/​mod.rs Anchors nested packages and optimizes default-layer lookup.
crates/​openusd/​src/​usdz/​reader.rs Reads nested package default layers.
crates/​openusd/​src/​usdz/​writer.rs Documents the new packaging utility.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

continue;
};
let anchor = layer.anchor_location();
visit_asset_paths(layer.data(), layer.identifier(), Visit::STRICT, |asset| {
Comment on lines +43 to +46
let name = match first_layer_name {
Some(name) => name.to_owned(),
None => file_name(layer.resolved_path().ok_or_else(unresolved)?),
};
@bresilla
bresilla force-pushed the feat/usdz-packaging branch from b1432b6 to 4e6f5c8 Compare October 4, 2026 17:39
@bresilla

bresilla commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

PS: maybe some of this should go in another crate??? since you divided into "core", "schema" and "build"... maybe one called "utils"??? But at the moment its just a module usd_utils

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants