Diagnostic Library
Preflight combines standard compliant OpenUSD validation with RealityKit-specific heuristics. This means we flag issues that are technically valid USD but will fail on Apple Vision Pro.
Because RealityKit is a realtime engine, it has stricter constraints than offline renderers like Arnold or Renderman. Preflight acts as the “Pre-Compiler” for your 3D assets, catching these platform-specific failures before you build to device.
For validator parity/reference metadata, see the USDChecker Validator Catalog.
Validation Runtime Paths
Section titled “Validation Runtime Paths”Preflight runs a single shared validation engine across all user-facing surfaces:
- Preflight app UI: uses the shared validation engine (
USDAdvancedClient.validateStage) including internal strict checks. - Preflight MCP tools: read/execute through the running app and use the same validation engine.
preflighttoolCLI:validateuses the same shared validation engine path.
This keeps diagnostics aligned across app, MCP, and CLI.
Internal Strict Checks (In-Process)
Section titled “Internal Strict Checks (In-Process)”In non-lightweight profiles, Preflight runs strict in-process checks that previously depended on external usdchecker invocation:
- Per-path unresolved dependency errors (
fix:cleanupMissingReference). - Missing
MaterialBindingAPIon prims that authormaterial:binding(fix:applyMissingSchema). - Empty
material:bindingrelationships (fix:assignMaterial). - Invalid/non-material binding targets (
fix:assignMaterial). - Primvar reader
inputs:varnametoken/string mismatch (fix:fixShaderPropertyType). - Missing
upAxismetadata in strict mode (fix:setUpAxis).
These are merged with custom validators and deduplicated for scoring.
Canonical Adjudication (Single Outcome)
Section titled “Canonical Adjudication (Single Outcome)”Preflight may observe the same underlying issue from multiple sources (for example: internal strict checks and usdchecker-parity style diagnostics). To avoid conflicting rows and unstable health scoring, validation now passes through a canonical adjudication step before UI/MCP/CLI consumption.
How Canonicalization Works
Section titled “How Canonicalization Works”- Normalize each diagnostic message:
- Strip source prefixes like
[internal-checker]and[usdchecker]. - Strip leading
Error:,Warning:,Info:. - Collapse whitespace and case-normalize.
- Strip source prefixes like
- Group by
(primPath, normalizedMessage). - Choose a single representative per group with deterministic priority:
- Higher severity wins (
error > warning > info > success). - If same severity, diagnosis with an attached fix wins.
- If still tied, source preference is
internal-checker > usdchecker > unknown.
- Higher severity wins (
- Keep provenance as metadata (
sources) for traceability, while presenting one canonical diagnosis row/outcome.
Health Index Coupling
Section titled “Health Index Coupling”The USD Health Score and category breakdown consume the same canonical stream. This guarantees:
- The issue count users see is the count scored by health.
- App UI, MCP, and CLI agree on severity and actionable counts.
- Duplicate source reports do not double-penalize a model.
In short: multiple signal sources are allowed, but only one adjudicated outcome is scored and shown per issue.
Production Guardrails
Section titled “Production Guardrails”The health index is calibrated as a production-usability signal, not a cosmetic grade:
- Catastrophic load/import failures (for example
OpenUSD failed to open file,failed to open stage,no importer can load) force score to 0.0. - Any unresolved error caps score to the Blocked range (
<= 4.5). - Errors are not downgraded to warnings in render scoring.
- Categories are only scored when applicable to the asset (for example animation is excluded when no animation schema/data is present).
- Additional issues inside the same category still reduce score, so fixing them moves the index incrementally.
For product-level disclosure policy (what is public vs proprietary), see Validation Transparency Policy.
Core Health Categories
Section titled “Core Health Categories”The main analysis covers 5 pillars of usability. These checks run on every detailed validation pass.
| Category | Icon | Focus | Impact |
|---|---|---|---|
| Loadability | doc.badge.gearshape | Core file structure, corruption, and root prims. | Critical. If this fails, the file won’t open. |
| Materials | paintpalette | Shaders, textures, UV mappings, and binding APIs. | Visual. Models appear pink, black, or invisible. |
| Scale | arrow.up.and.down | Coordinate systems (Z-up vs Y-up) and real-world units. | UX. Models appear sideways or sized wrong. |
| Animation | figure.walk | Skeletal rigs, joint paths, and morph targets. | Functional. Characters stay static in T-pose. |
| Dependencies | link | External asset references and .usdz packaging. | Deployment. Assets work on Mac but fail on device. |
🔴 Loadability (Critical)
Section titled “🔴 Loadability (Critical)”These errors prevent the file from opening in RealityKit entirely.
Multiple root prims detected
Section titled “Multiple root prims detected”The Issue: RealityKit prefers a single root entity. Having multiple roots (e.g., Camera, Light, Mesh all at top level) causes unpredictable anchoring.
The Fix: Use the “Set Default Prim” surgery to wrap everything or designate a main content root.
OpenUSD failed to open file
Section titled “OpenUSD failed to open file”The Issue: The file itself is corrupted or uses a schema version not supported by Apple’s USD library.
The Fix: Re-export from your DCC (Blender/Maya) or use usdcat to inspect the raw file.
Unsupported Geometry
Section titled “Unsupported Geometry”The Issue: The file contains geometry types not supported by RealityKit.
NurbsPatchBasisCurves(Hair strands often export as curves)Points(Point clouds) The Symptom: Objects are invisible. The Fix: Convert curves/hair to polygon meshes in your DCC before exporting.
PointInstancer Detected
Section titled “PointInstancer Detected”The Issue: RealityKit does not support the USD PointInstancer schema (often used for vegetation or crowds).
The Symptom: Instanced geometry will appear collapsed at (0,0,0) or be completely invisible.
The Fix: “Realize” or “Make Instances Real” in your DCC to convert them to individual meshes.
Unsupported Lights
Section titled “Unsupported Lights”The Issue: Standard USD lights (DiskLight, DomeLight, CylinderLight, GeometryLight) are ignored by RealityKit.
The Symptom: Scene appears unlit if relying on these sources.
The Fix: Use Image Based Lighting (IBL) via .exr environments for global illumination.
🎨 Materials
Section titled “🎨 Materials”Issues that cause “Pink Meshes” or invisible surfaces.
Missing UVs
Section titled “Missing UVs”The Issue: A mesh has geometry but no texture coordinates (primvars:st or primvars:uv).
The Symptom: Geometry renders pink or black when textured.
The Fix: Use fix:addUVs to apply a planar projection (emergency fix) or unwrap properly in Blender.
Unsupported Shader Type
Section titled “Unsupported Shader Type”The Issue: You are using a MaterialX node not supported by RealityKit (e.g., ND_standard_surface_surfaceshader).
The Fix: Convert to UsdPreviewSurface or use the MaterialX Standard Library subset supported by visionOS 2.0.
Missing Info:id
Section titled “Missing Info:id”The Issue: A shader definition is missing its unique identifier token.
The Fix: Ensure your material export settings are correct; the shader must declare its type (e.g., info:id = "UsdPreviewSurface").
Missing MaterialBindingAPI (usdShadeValidators:MaterialBindingApiAppliedValidator.MissingMaterialBindingAPI)
Section titled “Missing MaterialBindingAPI (usdShadeValidators:MaterialBindingApiAppliedValidator.MissingMaterialBindingAPI)”The Issue: A prim has material binding relationships, but does not declare MaterialBindingAPI in its applied schemas.
Why it matters (beyond passing usdchecker):
- Some tools/renderers recover from this implicitly, others do not.
- Schema-incomplete authoring can be dropped or rewritten incorrectly during import/export round-trips.
- Cross-tool behavior becomes nondeterministic (“works in app A, broken in app B”).
The Fix: Apply
MaterialBindingAPIon affected prims (fix:applyMissingSchemawithMaterialBindingAPI) so bindings are explicit and portable.
ShaderSdrCompliance.MismatchedPropertyType (usdShadeValidators:ShaderSdrCompliance.MismatchedPropertyType)
Section titled “ShaderSdrCompliance.MismatchedPropertyType (usdShadeValidators:ShaderSdrCompliance.MismatchedPropertyType)”The Issue: A shader input is authored with the wrong USD type (for example, token instead of string on inputs:varname).
Why it matters (beyond passing usdchecker):
- Some runtimes auto-coerce types, others reject or misread them.
- Primvar lookup can fail in stricter consumers (for example UV set name resolution), causing incorrect or missing texture sampling.
- Type drift increases pipeline fragility when files are translated, optimized, or re-authored.
The Fix: Normalize authored input types to SDR-expected types using
fix:fixShaderPropertyType.
PhysicsMeshCollisionAPI
Section titled “PhysicsMeshCollisionAPI”The Issue: Complex mesh colliders are detected. The Symptom: While supported, mesh colliders have a heavy performance cost compared to primitive colliders (Box, Sphere, Capsule). The Fix: Replace complex collision meshes with simple proxy geometry.
📏 Scale & Orientation
Section titled “📏 Scale & Orientation”Issues that make models look wrong in AR.
Up-Axis Mismatch (Z-Up)
Section titled “Up-Axis Mismatch (Z-Up)”The Issue: Blender exports Z-up by default. RealityKit is Y-up.
The Symptom: Character lies flat on the ground.
The Fix: Use fix:setUpAxis:Y surgery. This rotates the root prim -90° X and updates the stage metadata.
Unusual metersPerUnit
Section titled “Unusual metersPerUnit”The Issue: Your model is scaled in centimeters (0.01) but metadata says 1.0.
The Symptom: Model is 100x too large or too small.
The Fix: Use fix:setMetersPerUnit to align the metadata with the geometry.
🏃 Animation
Section titled “🏃 Animation”Issues that cause static “T-poses”.
Skeleton Path Mismatch
Section titled “Skeleton Path Mismatch”The Issue: The most common error. The animation clip targets joints like /root/hips, but the skeleton defines them as /Armature/root/hips.
The Symptom: Model stays in bind pose; animation plays but nothing moves.
The Fix: Use fix:remapSkeleton. This identifies the common prefix difference and rebases the animation paths.
Static Keyframe
Section titled “Static Keyframe”The Issue: An animation curve has only 1 keyframe. The Symptom: RealityKit optimizes it away, treating it as a static pose. The Fix: Ensure export includes specific frame ranges (e.g., 1-60).
High Joint Influence Count
Section titled “High Joint Influence Count”The Issue: A single vertex is influenced by more than 4 bones. The Symptom: Degraded performance; RealityKit works best with a maximum of 4 influences per vertex. The Fix: Limit influences to 4 in your DCC’s skinning settings (“Linear Blend Skinning”).
Blend Shape + Skeleton Conflict
Section titled “Blend Shape + Skeleton Conflict”The Issue: The same mesh has both blend shapes (morph targets) and a skeleton. The Symptom: In some RealityKit contexts, playing one animation type may override or block the other. The Fix: Ensure animations are grouped correctly or bake complex deformations.
Non-Standard FPS
Section titled “Non-Standard FPS”The Issue: Animation frame rate is not 24, 30, or 60 fps. The Symptom: Potential stuttering or playback speed mismatch. The Fix: Resample animation curves to standard frame rates during export.
🔗 Dependencies
Section titled “🔗 Dependencies”Packaging issues.
External file references detected
Section titled “External file references detected”The Issue: The USD file points to /Users/name/Desktop/texture.png.
The Symptom: Texture loads on your Mac but fails on Vision Pro.
The Fix: Use bundle usdz to package all dependencies into a single archive.
⚡ Optional: Optimization Profile
Section titled “⚡ Optional: Optimization Profile”These checks are NOT part of the standard health score. They are strictly performance recommendations for maintaining 90fps on Vision Pro.
You must run Preflight with --profile optimization to see them.
High Draw Call Count
Section titled “High Draw Call Count”The Issue: Scene has more than 50 individual meshes.
The Symptom: CPU bottleneck during rendering.
The Fix: Combine static meshes into a single object or use PointInstancer (if supported by your renderer, note RealityKit limitation above).
High Material Count
Section titled “High Material Count”The Issue: Scene uses more than 20 unique materials. The Symptom: Increased memory usage and state switching. The Fix: Bake textures into atlases and share materials across meshes.