A MoonBit library for reading, validating, and processing glTF/GLB 3D asset files. Provides node tree traversal, mesh/material/animation metadata extraction, resource dependency checking, and optimization reporting.
moon update
moon check --target all --deny-warn
moon test --target native --deny-warn
moon test --target wasm-gc --deny-warn
moon build --target native --deny-warnmoon run --target native cmd/main benchmarks/minimal.gltf
moon run --target native cmd/main benchmarks/minimal.gltf --jsonmoon add hzfhzf89/moonbit-gltf-tools///|
import {
"hzfhzf89/moonbit-gltf-tools/gltf",
}
///|
fn inspect_asset(json : String) raise {
let document = @gltf.parse_gltf_string(json)
let report = @gltf.build_default_asset_report(document)
println("triangles=\\{report.geometry.triangles}")
println("diagnostics=\\{report.diagnostics.errors}")
}moon run --target native cmd/main path/to/asset.gltf
moon run --target native cmd/main path/to/asset.glb
moon run --target native cmd/main path/to/asset.gltf --json.
├── lib.mbt # root facade exports
├── gltf/
│ ├── types.mbt # glTF 2.0 data model
│ ├── parser.mbt # JSON document parser
│ ├── glb.mbt # GLB container handling
│ ├── validator.mbt # reference and structure validation
│ ├── diagnostics.mbt # configurable diagnostic engine
│ ├── accessor.mbt # accessor and buffer layout analysis
│ ├── scene.mbt # scene graph and resource analysis
│ ├── transform.mbt # TRS/world-transform evaluation
│ ├── mesh_analysis.mbt # geometry cost analysis
│ ├── analyzer.mbt # optimization suggestions
│ ├── report.mbt # unified asset reports
│ ├── quality.mbt # quality gates
│ ├── uri.mbt # URI classification and safety checks
│ ├── binary_reader.mbt # bounded reads and accessor decoding
│ ├── binary_writer.mbt # aligned GLB construction and inspection
│ ├── geometry_pipeline.mbt # geometry metrics and budget checks
│ ├── topology_analysis.mbt # manifold, boundary, and component analysis
│ ├── meshlet_pipeline.mbt # bounded meshlet and LOD planning
│ ├── cache_optimizer.mbt # vertex-cache metrics and reordering
│ ├── spatial_query.mbt # bounds, ray, frustum, and nearest queries
│ ├── instance_batching.mbt # transform and draw-batch planning
│ ├── streaming_scheduler.mbt # progressive resource scheduling
│ ├── semantic_validation.mbt # policy-driven semantic validation
│ ├── resource_pipeline.mbt # resource manifests and package plans
│ ├── animation_pipeline.mbt # track sampling and clip budgets
│ ├── skin_pipeline.mbt # joint and weight analysis
│ └── report_formats.mbt # JSON, Markdown, and CI annotations
├── cmd/main/main.mbt # native command-line entry point
├── benchmarks/ # deterministic benchmark fixtures
└── .github/workflows/ # cross-platform CImoon run --target native cmd/main benchmarks/minimal.gltf --jsonmoon fmt
moon check --target all --deny-warn
moon test --target all --deny-warn
moon build --target native --deny-warn
moon infotest "analyzer test" {
let doc = @gltf.GltfDocument::{
version: "2.0",
generator: None,
copyright: None,
scenes: [{ name: Some("Scene"), nodes: [0] }],
nodes: [
{
name: Some("Root"),
children: [],
mesh: Some(0),
skin: None,
camera: None,
translation: None,
rotation: None,
scale: None,
},
],
meshes: [
{
name: Some("Mesh"),
primitives: [
{
attributes: { "POSITION": 0 },
indices: None,
material: Some(0),
mode: Triangles,
},
],
},
],
materials: [
{
name: Some("UsedMaterial"),
pbr_metallic_roughness: {
base_color_factor: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
base_color_texture: None,
metallic_factor: 1.0,
roughness_factor: 1.0,
metallic_roughness_texture: None,
},
normal_texture: None,
occlusion_texture: None,
emissive_texture: None,
emissive_factor: { x: 0.0, y: 0.0, z: 0.0 },
alpha_mode: Opaque,
alpha_cutoff: 0.5,
double_sided: false,
},
{
name: Some("UnusedMaterial"),
pbr_metallic_roughness: {
base_color_factor: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
base_color_texture: None,
metallic_factor: 1.0,
roughness_factor: 1.0,
metallic_roughness_texture: None,
},
normal_texture: None,
occlusion_texture: None,
emissive_texture: None,
emissive_factor: { x: 0.0, y: 0.0, z: 0.0 },
alpha_mode: Opaque,
alpha_cutoff: 0.5,
double_sided: false,
},
],
textures: [],
images: [],
samplers: [],
accessors: [
{
name: None,
buffer_view: None,
byte_offset: 0,
component_type: Float,
normalized: false,
count: 100, // 100个顶点
accessor_type: Vec3,
max_values: [],
min_values: [],
},
],
buffer_views: [],
buffers: [],
animations: [],
skins: [],
default_scene: Some(0),
}
let report = @gltf.analyze_optimization(doc)
inspect(report.total_vertices, content="100")
inspect(report.draw_calls, content="1")
debug_inspect(report.unused_materials, content="[1]")
}test "parse minimal gltf" {
let json_str =
#|{"asset":{"version":"2.0"},"scenes":[],"nodes":[],"meshes":[],
#|"materials":[],"textures":[],"images":[],"samplers":[],
#|"accessors":[],"bufferViews":[],"buffers":[],"animations":[],"skins":[]}
let doc = @gltf.parse_gltf_string(json_str)
inspect(doc.version, content="2.0")
}test "node tree basic" {
let doc = @gltf.GltfDocument::{
version: "2.0",
generator: None,
copyright: None,
scenes: [{ name: Some("Scene"), nodes: [0] }],
nodes: [
{
name: Some("Root"),
children: [1],
mesh: None,
skin: None,
camera: None,
translation: None,
rotation: None,
scale: None,
},
{
name: Some("Child"),
children: [],
mesh: Some(0),
skin: None,
camera: None,
translation: None,
rotation: None,
scale: None,
},
],
meshes: [],
materials: [],
textures: [],
images: [],
samplers: [],
accessors: [],
buffer_views: [],
buffers: [],
animations: [],
skins: [],
default_scene: Some(0),
}
let info = @gltf.traverse_node_tree(doc)
inspect(info.total_nodes, content="2")
inspect(info.max_depth, content="2")
inspect(info.mesh_node_count, content="1")
}test "parse glb header mock" {
// 一个极简 of GLB 模拟字节流(12字节头部 + JSON chunk 头部 + 数据)
// 头部: magic="glTF"(0x46546C67), version=2, length=32
// JSON chunk: length=8, type="JSON"(0x4E4F534A), data=`{}` (padded with spaces to 8 bytes)
let glb_bytes = Bytes::from_array([
0x67, 0x6c, 0x54, 0x46, // magic
0x02, 0x00, 0x00, 0x00, // version
0x20, 0x00, 0x00, 0x00, // total length (32)
0x08, 0x00, 0x00, 0x00, // chunk 0 length (8)
0x4a, 0x53, 0x4f, 0x4e, // chunk 0 type (JSON)
0x7b, 0x7d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // chunk 0 data: "{}" with spaces
])
let glb = @gltf.unpack_glb(glb_bytes)
inspect(glb.json_str.trim(), content="{}")
}test "validation invalid node index" {
let doc = @gltf.GltfDocument::{
version: "2.0",
generator: None,
copyright: None,
scenes: [{ name: Some("Scene"), nodes: [99] }], // 99 是无效的 node 索引
nodes: [
{
name: Some("Root"),
children: [],
mesh: None,
skin: None,
camera: None,
translation: None,
rotation: None,
scale: None,
},
],
meshes: [],
materials: [],
textures: [],
images: [],
samplers: [],
accessors: [],
buffer_views: [],
buffers: [],
animations: [],
skins: [],
default_scene: Some(0),
}
let report = @gltf.validate_document(doc)
inspect(report.is_valid, content="false")
inspect(report.errors.length(), content="1")
}Install
Download zipA MoonBit library for reading, validating, and processing glTF/GLB 3D asset files. Provides node tree traversal, mesh/material/animation metadata extraction, resource dependency checking, and optimization reporting.