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.

OptionKindWhat it does
upAxisbehavior 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.
bakeTransformsbehavior 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 / maxTrianglesspeed The standard tessellation dials, same as every mesh spoke.
ofskips 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.

Roadmap: the 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.