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 3b43bd86; unless a sentence says otherwise, nothing here was built or executed. Suite names were read at c6dec91f and re-checked at 3b43bd86; 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 c6dec91f and re-checked at 3b43bd86, 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 every renderer family that shades PBR (DIRECTX9, EasyGL, FNA3D, SDL_GPU, SOFTWARE, WEBGPU); 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; scene ancestry kept as the root prefix, while the skinned mesh node's own transform is ignored, as glTF 2.0 requires; 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 | two 148-disposition renderer golden sets checked in (140 captures and 8 rejections each: OPENGLES3/EasyGL and SOFTWARE); 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. Native point and spot lighting remains unimplemented by design, and the bounded external retake matrix still needs passing runs on surviving renderers (the plan names SOFTWARE, SDL_GPU or WEBGPU, and DIRECTX9 under Wine for its Direct3D clause); the KHR_materials_specular row is still marked open in the plan, although CNA's limitations document (docs/gltf-limitations.md) now states that every PBR renderer samples both specular maps. 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)