odin_cgltf is an Odin-native glTF 2.0 parser/decoder/validator port of cgltf.
It supports:
.gltfJSON containers.glbbinary containers (JSON + BIN chunk)- typed decode into Odin structs
- validation of core glTF structural rules
- cgltf-style extension flags (including defaults that allow all extensions)
- Parse file bytes into a
Document(parse_file,parse_bytes) - Decode typed model data into
Decoded_Model(decode_document) - Validate model references/ranges/version (
validate_gltf,validate_gltf_with_options) - Track
extensionsUsedandextensionsRequiredas bitflags in decode output
- No GPU upload
- No image decode
- No Draco mesh decode implementation (the extension payload is parsed and validated structurally)
- No automatic external resource loading (
.bin, textures) into engine memory
Use this lifecycle in your 3D engine:
- Parse source file (
.gltfor.glb) - Decode typed model data
- Validate model
- Resolve buffers/images/material data into your engine asset types
- Destroy
Decoded_ModelandDocumentonce copied into engine-owned structures
package your_engine
import "core:fmt"
import gltf "path/to/odin_cgltf"
load_gltf_asset :: proc(path: string) -> bool {
doc, parse_err := gltf.parse_file(path)
if gltf.has_error(parse_err) {
fmt.printf("parse failed: %v\n", parse_err.status)
return false
}
defer gltf.destroy_document(&doc)
decoded, decode_err := gltf.decode_document(&doc)
if gltf.has_decode_error(decode_err) {
fmt.printf("decode failed: %v\n", decode_err.status)
return false
}
defer gltf.destroy_decoded_model(&decoded)
validation_err := gltf.validate_gltf(&doc, &decoded.model)
if gltf.has_validation_error(validation_err) {
fmt.printf("validation failed: %v\n", validation_err.status)
return false
}
// Build engine resources from decoded.model + buffer data.
return true
}For each model.buffers[i]:
- If source is
.glbandi == 0with emptyuri, bytes are indoc.bin_chunk. - Otherwise load
buffers[i].urifrom disk relative to the.gltffile directory.
Then resolve each accessor through:
accessor.bufferViewbufferViews[bufferView].byteOffsetaccessor.byteOffsetaccessor.componentType,accessor.type- optional
bufferViews[...].byteStride
This is where engines build vertex/index streams and mesh primitives.
Default validation is permissive:
- all known cgltf extension flags are allowed
- unknown required extension names are allowed
Use strict policy when needed:
options := gltf.Validation_Options{
allowed_extension_flags = gltf.Extension_Flags(
u32(gltf.CGLTF_EXTENSION_FLAG_MATERIALS_UNLIT) |
u32(gltf.CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM),
),
allow_unknown_required_extensions = false,
}
err := gltf.validate_gltf_with_options(&doc, &decoded.model, options)Extension bitfields are available on Decoded_Model:
used_extension_flagsrequired_extension_flags
The test suite covers:
- parser/decode/validation status paths
- manifest-based corpus runs over:
test/glTF-Sample-Assets/Modelstest/free_models
Run:
odin test .