Interoperability · OpenUSD / USDZ
OpenUSD — the digital-twin spoke
USD is the scene description Omniverse and industrial digital-twin pipelines read
natively — and, as usdz, the only thing iOS AR Quick Look opens. NativeCAD writes
both off the same tessellation bridge as glTF: exact crease normals, the same UV unwrap, the same
per-body naming — just spelled as a USD stage instead of a glTF scene.
Export — cad_export_body format:"usda" | "usdz"
usda writes the human-readable text layer; usdz writes the package:
a zip that is STORED (never deflated) with every payload 64-byte aligned and the
USD layer first — the alignment rules AR Quick Look silently rejects a package over. Every write is
verified in-process (zip walk + usda re-parse) before the file is handed back.
Each body becomes one UsdGeomMesh per material group — points, face counts and
indices, per-vertex normals, and primvars:st from the same MeshUv unwrap glTF uses —
under one root Xform, with UsdPreviewSurface materials converted from the
same material objects as glTF plus a constant primvars:displayColor fallback. Units are
declared exactly once: metersPerUnit 0.001 with points left in kernel
millimetres, never a scale op as well. A body name that is not a legal USD prim name is sanitised
(vane#3 → vane_3) with the original kept verbatim in
customData.kernelId.
| Option | Kind | What it does |
|---|---|---|
upAxis | behavior | Which axis the stage declares as up — USD admits exactly "Z" and
"Y". Z (default for usda) is the kernel's own axis,
so points ship untouched with no rotation anywhere in the file. Y (default for
usdz, because AR Quick Look fixes Y as up) adds one root
xformOp:rotateX = -90 — never a silent re-write of the geometry. |
bakeTransforms | behavior | Folds the up-axis rotation (and the unit scale) into the point data itself instead of stage metadata and a root op — for consumers that ignore both. |
tolerance / maxTriangles | speed | The standard tessellation dials, same as every mesh spoke. |
of | skips translation | One named body, or the whole document. |
What v1 deliberately does not write
One self-contained layer, one root Xform, one mesh prim per body: no USD component
tree, references, instancing, sublayers or payloads yet — the glTF assembly walk has no USD
counterpart so far, and faking a hierarchy would be worse than declaring none. No
usdc binary crate, no textures, no skeletal or animated USD. And like every mesh
spoke, no PMI: a USD stage here carries appearance and structure, not tolerances.
Try it
USDZ nativecad-plate.usdz 468 KB USDA nativecad-plate.usda 468 KB
The kernel-authored sample plate, written by this
exporter from the same nativecad-plate.step every other sample comes from: one mesh,
4,564 triangles, 3,214 points (SceneReady game UVs since CADSF-101195: primvars:st metric, 1 uv = 1 m, and primvars:st1, a packed 0–1 atlas), extent [−30, −20, 0] … [30, 20, 6] in
millimetres. Open the .usda in any editor — the whole stage is readable text, and its
Z-up form carries no xformOp at all. AirDrop the .usdz to an iPhone and
it opens in AR Quick Look. Or export your own over MCP:
cad_export_body format:"usdz".
The package is barely larger than the layer it carries, which is the STORED rule
made visible: a usdz is its usda — here the Y-up one, 97 bytes
longer for the root xformOp:rotateX — plus a zip header, the padding that puts the
payload on a 64-byte boundary and a central directory. No compression, ever.
usdc binary crate (a text layer is
several times a crate for the same mesh — see the sample sizes above) · USD references and
instancing for assemblies (the component tree) · textures baked from material groups.