glTF feature matrix: importer, runtime and evidence
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Suite names were read at 009d40f5; their results were not executed. L7 rows are CNA's recorded campaigns.
This matrix keeps three questions apart for every glTF 2.0 feature: what the shared import core accepts or transforms, what the runtime Model represents, and which committed evidence reaches it. "Reported" means GltfImportReportEXT makes a loss visible; it never means the missing feature was implemented. Test suite names are the CnaTests suites under the glTF import tests unless stated; their existence was read at 009d40f5, not their results. The contracts behind each row are explained on the import core, vertex packing, skinning and animation and conformance pages.
Containers, scenes and geometry
| glTF feature | Importer and offline route | Runtime representation | Focused evidence |
|---|---|---|---|
.gltf / .glb | One cgltf 1.15 parse path; generated JSON and GLB twins in the corpus. | Direct Load<Model>, offline .cnj, and .cnb via the converter. | L0 manifest and digests (GltfFixtureCorpus), L1, 148 container pairs. |
| External and data resources | Buffer views, data: URIs and contained percent-decoded external files; scheme, drive-absolute, root-absolute, lexical and symbolic-link escapes refused before any external read. | In-memory image decode; textures and buffers owned by the model. | GltfUriContainment, GltfExternalBuffer, GltfImageSource. |
| Version and extensions | Conditional asset.version 2.0 check; alignment and overflow guards before cgltf_validate; unclaimed extensionsRequired refused, unclaimed extensionsUsed warned; meshopt refused. | Ordered diagnostics on the model. | GltfContainerValidation, GltfContainerRobustness, GltfExtensionRegistry, GltfLimitationsDoc, GltfVendoredCgltf; pinned Khronos validator gate in CI. |
| Scene graph and instancing | Default scene (else first; else every root node); parent-before-child walk; node matrix or TRS; shared-mesh placements kept mesh-local. | Synthetic Root plus one ModelBone per reachable node; one ModelMesh per placement. | L1–L4, GltfSceneSelection, GltfNodeHierarchy, GltfSceneGraphBones, GltfModelShape; D1–D3 fixed. |
| Multiple skins and groups | Every group extracted; the converter writes one named .cnj per group. | Every group in one model; Model::SkinsEXT maps each palette to its meshes; Tag aliases the first skin. | GltfSkinComposition; converter tests ImportsAllSkinsAsSeparateModels and the skinning-data parity case. |
| Accessors | Offsets, strides, normalization, sparse base and overrides (including a null base view), overflow-safe spans, index range proof. | Dense semantic streams, then packed buffers. | L2, GltfAccessorDecodeLock, GltfBufferAndWeightForm, GltfIndexDecode, GltfIndexForm; D4 fixed. |
| Vertex layouts | The selection table emits ten of the eleven canonical strides; unrepresentable data is named per row. | A canonical VertexDeclaration reaches the renderer; unknown strides refused. | L5 byte goldens (140 + 8 absences), GltfVertexLayoutTable, GltfStrideAndBuffer, RendererStrideConformance. |
| All seven topologies | Points and lines kept; loops closed into strips; strips and fans converted to lists; incomplete tails dropped and counted. | Per-part CNAEXT PrimitiveType; primitive count per topology. | GltfPrimitiveTopology, GltfDrawTopology; D5 fixed. |
| Normals and tangents | Flat normals computed when absent (vertices split); authored tangents kept, legacy pairs imported, otherwise generated (angle-weighted, not MikkTSpace). | Tangent slot on PBR layouts only. | GltfNormalTangentCorpus, GltfAttributeCoverage. |
| Mirrored placement | Negative composed determinant detected per placement. | Reported as mirrored-winding-unapplied; cull state is the application's. | GltfMirroring. |
Materials, animation and scene extras
| glTF feature | Importer and offline route | Runtime representation | Focused evidence |
|---|---|---|---|
| Core metallic-roughness | Chosen by material model; factors, seven texture slots, alpha mode and cutoff, double-sidedness, samplers, colour space, per-map transforms and two UV selectors kept. | PbrEffect / SkinnedPbrEffect with one shared parameter convention; factor-only materials stay PBR. | L3, L5, L6 (GltfConformanceL6, GltfPbrBrdf), GltfRendererPbrFallbackPolicy, L7 material fixtures; D7 fixed. |
| Unlit | KHR_materials_unlit leaves the PBR route. | BasicEffect with lighting off, or SkinnedEffect with white ambient and no directional light. | GltfUnlitMaterial. |
| Vertex colour | COLOR_0 as a base-colour multiplier on every route. | Strides 24, 56 (unlit) and 60, 80 (PBR); a renderer that cannot bind a colour-carrying PBR stride refuses by name. | Byte and pixel fixtures; RendererStrideConformance; GLTF-241 fixed. |
| Material variants | Source-order names and sparse primitive mappings, direct and offline. | Model-wide CNAEXT selection restores complete part state or the default. | GltfMaterialVariants; converter round trip. |
| Extension textures and factors | KHR_materials_specular factors transported; its two maps sampled by eight renderer families, not by METAL; KTX2 and WebP need a PNG/JPEG fallback; clearcoat, sheen and volume parsed but ignored by stock effects. | The registry qualifies each claim. | GltfExtensionRegistry, GltfMaterialExtensionsTest, GltfUnsupportedTexture, renderer inventories. |
| Texture transforms and samplers | Independent transform and sampler per sampled map; two source UV sets packed; a third reported as uv-set-mismatch. | Effect parameters and per-part sampler states. | GltfUvChannel, GltfSamplerMapping, multi-map L7 fixtures. |
| Skins | Joints reordered topologically and JOINTS_0 remapped; ancestry and mesh-node cancellation kept as root prefixes; more than 72 joints refused. | Every ModelSkinEXT plus the first-skin Tag; bind-pose palette installed at load. | GltfSkinSpaces, GltfSkinLadder, reversed joint order; D8 fixed. |
| Animation | Skeletal clips in palette indices; rigid clips in scene-node indices; union resampling; LINEAR, STEP and CUBICSPLINE. | SkinningData clips, or ModelAnimationsEXT on an unskinned model; mixed-carrier rigid drops reported. | GltfAnimationSampling, GltfAnimationRobustness, GltfRigidAnimation; D6 fixed. |
| Morph targets | Position, normal and tangent deltas; node or mesh default weights; weight tracks with unbaked cubic tangents; other morphed attributes dropped and reported. | MorphTargetDataEXT on parts; CPU blend and buffer re-upload using canonical offsets. | GltfMorphBlending, GltfMorphWeights, graphics MorphTargetEXTTests, converter parity. |
| Cameras | Perspective (including infinite far plane) and orthographic data with node placement. | Model::CamerasEXT on direct loads only. | GltfCameras. |
| Punctual lights | At most three, approximated as directional; kind, range, cone and clamped-intensity losses counted. | XNA IEffectLights slots on non-unlit effects. | GltfClipAndLight, GltfLightBudget, GltfLightingPolicy, L6 and L7. |
| Draco | Decoded only in a build with the decoder; required use refused otherwise; triangle lists and strips only. | Ordinary model buffers after decode. | GltfDracoParity, GltfDracoEncoderPin; CI runs Draco on and off. |
Route and oracle boundaries
The direct reader and the converter share semantic extraction but package the result differently: direct loading builds one model with every represented group at unit scale 1, while the converter can apply an explicit scale and writes one named .cnj per group. A .cnj reload proves the serialized boundary for the fields it carries (cameras are not among them); it is not a claim that JSON bytes and a live object are identical. A .cnb compiled from that .cnj (or directly from glTF by cna_tool_gltf_to_cnb, which runs the same compile) restores skeleton, clips, morph data and lights; its schema has no camera field and the import report is not stored, and the compiler refuses a model that uses KHR_materials_variants by name (CnbModelFromCnj.cpp, plan decision D9) instead of writing a quietly less capable asset. An .xnb built from glTF drops skeleton, clips, lights and morphs and downgrades a PBR material to the closest stock effect (base colour, emissive factor, specular colour factor and alpha survive; metallic and roughness factors and the normal, metallic-roughness, emissive, occlusion and specular maps do not), each with a named warning.
| Layer | State | Observation |
|---|---|---|
| L0 | implemented | corpus, manifest, helper integrity and the suite-to-rung partition |
| L1 | implemented | container structure, scene and declared relationships |
| L2 | implemented | decoded accessor values |
| L3 | implemented | semantic mesh, material, scene and diagnostic state |
| L4 | implemented | composed scene and skin coordinate spaces |
| L5 | implemented | packed vertex and index bytes: 140 goldens, 8 explicit absences |
| L6 | implemented | bound matrices, materials, maps, samplers, lights and palettes |
| L7 | recorded | four 148-disposition renderer campaigns (140 captures and 8 rejections each: OPENGLES3/EasyGL, VULKAN, SOFTWARE, DIRECTX11 under Wine and DXVK); the OPENGLES3 rung runs in CI through an external viewer |
All original D1–D8 records, and the later GLTF-241 record, are fixed and kept in the append-only ledger as regression history. CNA's plan records the broader GLTF ROBUST milestone at 9 of 12 rows: KHR_materials_specular texture sampling (only METAL among the current PBR renderers lacks it), native point and spot lighting, and a fourth renderer on the bounded external retake matrix remain open. The snapshot therefore supports strong, layer-qualified claims — not an unqualified promise about every file and renderer combination.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Architecture
- Content architecture
- Tests and validation
- Test labels (gltf-conformance)