Skip to content

feat(exporting): add OpenUSD USDA and USDZ geometry export - #516

Open
JarrettSJohnson wants to merge 15 commits into
masterfrom
codex/issue-497-usdz-export
Open

JarrettSJohnson wants to merge 15 commits into
masterfrom
codex/issue-497-usdz-export

Conversation

@JarrettSJohnson

@JarrettSJohnson JarrettSJohnson commented Jul 25, 2026 •

Copy link
Copy Markdown
Member

Summary

Implements the OpenUSD part of #497: export of the displayed geometry as USDA (for 3D software) and USDZ (for AR viewers), via cmd.save, cmd.get_usda and the File > Export menu. No external tools are needed.

USDA (cmd.save("x.usda"), cmd.get_usda())

  • Coordinates in Å with metersPerUnit = 1e-10, like the COLLADA export. Camera space, or model space with geometry_export_mode=1. Blender applies that scale on import by default; its "Apply Unit Conversion Scale" option turns it off (noted in the get_usda help).
  • Spheres, sticks, cones and ellipsoids are tessellated into one mesh, since Blender's USD importer drops the material of analytic prims. Size is close to the COLLADA export (660-atom spheres scene: 10.8 MB vs 11.2 MB).

USDZ (cmd.save("x.usdz"))

  • One stored, 64-byte aligned scene.usda layer. AR Quick Look accepts ASCII layers, as Mol*'s USDZ export also relies on.
  • Scaled to fit within 1 m and set centered on the ground, so it is usable in AR.
  • Spheres, cylinders, capsules and cones stay compact analytic UsdGeom prims where possible. Transparent or open-ended solids become meshes, with domes instead of buried hemispheres.
  • Written through the atomic save path from Don't truncate existing file when save fails #521.

Colors and materials

  • AR Quick Look ignores primvar readers, so displayColor cannot drive the material (confirmed on device: such files render translucent gray). Every color gets a constant UsdPreviewSurface material instead, bound per face with GeomSubsets on meshes. displayColor / displayOpacity are still written, as indexed primvars.
  • Colors are quantized to 64 levels per channel, and each triangle takes its first corner's color, which keeps ramps and spectra to a few hundred materials at most (ubiquitin ramp surface: 127).
  • Ramp colors are resolved at each vertex.
  • Only transparent materials carry an opacity input, since renderers such as Hydra Storm treat any material with a connected opacity as translucent.
  • Two-color sticks and cones are split halfway, like PyMOL's half bonds, since a face takes a single material.

Testing

  • 34 tests in testing/tests/api/exporting_geom.py; the full testing/tests/api suite passes.
  • All exported scenes (gly and ubiquitin as sticks, spheres, cartoon, surface, ramp surface, transparent sticks, ellipsoids, CGO solids) pass the OpenUSD 26.08 validators, including the USDZ package checks.
  • USDA rendered in Blender 5.2: correct colors, shading and ellipsoid normals.
  • USDZ checked in AR Quick Look on iOS 26.6.2: colors, opaque and transparent geometry render correctly.

Known limitations

  • Per-triangle colors show as faceting on surfaces with fast-changing ramps. Smooth gradients would need a texture, a possible follow-up.
  • Blender ignores constant material opacity, so transparent geometry renders solid there.
  • Transparent sticks make larger USDZ files, since they are exported as meshes.
  • Labels are not exported.

🤖 Generated with Claude Code

JarrettSJohnson and others added 15 commits July 24, 2026 16:18
AR Quick Look ignores primvar readers, so displayColor never reached the
material and everything rendered translucent gray on device. Colors now
come from one constant UsdPreviewSurface material per quantized color and
opacity, bound per face through GeomSubsets on meshes. Opaque materials
carry no opacity input, which renderers would treat as translucent.

- USDZ stores the ASCII layer directly, no usdcat dependency, and the
  scene is scaled to fit within one meter and stands on the ground
- USDA tessellates all solids into one mesh, since Blender's importer
  drops the material of analytic prims; USDZ keeps analytic prims
- Ramp colors are resolved at each vertex instead of exported black
- Two-color solids split halfway, as each face takes one material
- Indexed color primvars and 6 significant digits keep USDA size close
  to the COLLADA export
- Revert unrelated .gitignore and Scene.h changes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build the package in memory and return it from the save function, so
that save() writes it with _write_file_atomic like any other format and
a failed export no longer truncates an existing file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant