moonbit-gltf-tools

    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.

    gltf
    glb
    3d
    mesh
    asset
    parser
    validation
    Download zip
    Author
    Version
    0.1.2
    License
    Apache-2.0
    Last updated
    2 months ago
    Downloads
    50

    #moonbit-gltf-tools

    CI License

    A MoonBit toolkit for inspecting, validating, and optimizing glTF 2.0 and GLB assets. It is designed for asset importers, build pipelines, CI checks, and offline diagnostics—not for rendering.

    #Project positioning

    moonbit-gltf-tools helps catch malformed or inefficient 3D assets before they reach a runtime or an editor. The library keeps parsing and analysis deterministic: external URIs are reported as dependencies and are not read implicitly during parsing.

    The project currently focuses on:

    • glTF 2.0 JSON documents and GLB containers;
    • structural validation and actionable diagnostics;
    • scene graph, transform, resource, accessor, and mesh analysis;
    • binary accessor decoding, GLB writing, semantic validation, and extension audits;
    • topology quality, meshlet planning, vertex-cache analysis, spatial queries, instancing, and progressive resource streaming;
    • machine-readable reports for automation;
    • a native CLI for local inspection and CI integration.

    #Core capabilities

    #Parsing

    • Parse glTF 2.0 JSON into strongly typed MoonBit data structures.
    • Unpack GLB headers, JSON chunks, and optional BIN chunks.
    • Decode the core scene, mesh, material, texture, image, animation, skin, accessor, buffer, and buffer view structures.

    #Validation and diagnostics

    • Validate reference bounds across scenes, nodes, meshes, materials, buffers, accessors, animations, and skins.
    • Detect scene graph cycles and invalid child references.
    • Check accessor element sizes, byte strides, alignment, buffer view windows, and normalization constraints.
    • Produce configurable diagnostic summaries with severity, code, path, message, and remediation hints.

    #Analysis and optimization

    • Traverse scene graphs and evaluate inherited TRS transforms.
    • Estimate vertices, indices, triangles, draw calls, attribute slots, and vertex memory.
    • Report indexed geometry ratios, missing vertex attributes, material usage, animation channels, and skinning information.
    • Audit embedded and external resource dependencies.
    • Apply configurable quality gates for general and mobile-oriented pipelines.
    • Build deterministic optimization plans for geometry, materials, resources, animation, skinning, and runtime budgets.

    #Reporting

    • Build a single AssetReport from the parser, validator, scene, resource, geometry, and quality-analysis passes.
    • Export stable debug representations for CLI and automation consumers.
    • Inspect URI safety, media types, data URIs, and common asset extensions.

    #Quick start

    #Prerequisites

    Install the current stable MoonBit toolchain and ensure moon is available on your PATH.

    #Build and test the repository

    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-warn

    #Run the sample asset

    The repository includes a small deterministic fixture at benchmarks/minimal.gltf.

    moon run --target native cmd/main benchmarks/minimal.gltf moon run --target native cmd/main benchmarks/minimal.gltf --json

    The first command prints a human-readable inspection report. The --json variant prints the machine-readable asset report for scripts and CI jobs.

    #Library usage

    Add the package to a MoonBit project with:

    moon add hzfhzf89/moonbit-gltf-tools

    Import the public gltf package:

    ///|
    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}")
    }

    For GLB input, call @gltf.unpack_glb first and pass the returned JSON string to @gltf.parse_gltf_string.

    #CLI

    The CLI accepts a .gltf or .glb path:

    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

    The CLI reports:

    1. structural validation results;
    2. scene graph and resource dependency summaries;
    3. mesh and animation metadata;
    4. optimization suggestions;
    5. quality-gate score and violations.

    Use --json when another tool should consume the complete AssetReport.

    #Architecture

    . ├── 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 CI

    The implementation is organized as independent analysis passes over the typed document model. Applications can use individual passes or compose them through build_asset_report. Supporting passes cover attribute validation, derived geometry metrics and sampling, LOD quality, material sampling, resource integrity, animation compression, scene scheduling/snapshots, runtime budgets, asset diffs, extension audits, normalization, and release-quality matrices.

    #Benchmarks

    The benchmark protocol and recorded local baseline are maintained in benchmarks documentation. Run the CLI benchmark fixture with:

    moon run --target native cmd/main benchmarks/minimal.gltf --json

    Recorded local baseline (2026-08-24): on Windows PowerShell with Moonc 0.10.9 and the native backend, five end-to-end samples were 6763.41, 1081.78, 620.03, 575.32, and 475.71 ms; the median was 620.03 ms. The first sample included build/cache warm-up. The fixture contains one scene and one node, and the JSON report contains zero diagnostics. These figures are host-dependent; historical samples and the exact protocol are documented in BENCHMARKS.md.

    Benchmark comparisons should record the MoonBit version, target backend, operating system, CPU, sample count, and whether the run includes a cold build. Wall-clock results are host-dependent and should not be treated as universal performance guarantees.

    #Testing

    The test suite covers parser behavior, GLB boundaries, reference validation, scene cycles, accessor layouts, URI classification, transforms, mesh analysis, quality gates, and report generation.

    Recommended local checks:

    moon fmt moon check --target all --deny-warn moon test --target all --deny-warn moon build --target native --deny-warn moon info

    moon info regenerates the public interface snapshots in pkg.generated.mbti files. Review those files when public APIs change.

    #CI

    The GitHub Actions workflow runs on Ubuntu, macOS, and Windows. It installs Node.js and the official stable MoonBit toolchain (verified locally at Moonc v0.10.9), then runs:

    • moon check --target all --deny-warn;
    • native build verification;
    • formatting and public-interface drift checks;
    • moon test --target all --deny-warn.

    See the CI workflow at .github/workflows/test.yml.

    #License

    Apache License 2.0. See LICENSE.

    GltfDocument

    完整的 glTF 文档,包含所有资产数据

    analyze_optimization

    对 glTF 模型进行深度分析,找出低效配置和冗余资源,并输出优化建议报告

    Example

    test "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]")
    }

    bytes_to_utf8_string

    fn bytes_to_utf8_string(bytes : Bytes) -> String

    classify_uri

    fn classify_uri(value : String) ->
    UriKind

    collect_resource_deps

    分析 glTF 文档的资源依赖

    inspect_uri

    fn inspect_uri(value : String) ->
    UriInfo

    parse_gltf_string

    将 JSON 对象解析为完整 GltfDocument

    Example

    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")
    }

    quality_violation_lines

    fn quality_violation_lines(gate :
    QualityGate
    ) -> Array[String]

    report_to_json

    fn report_to_json(report :
    AssetReport
    ) -> String

    traverse_node_tree

    遍历 glTF 场景图,收集节点树信息

    Example

    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")
    }

    unpack_glb

    解析 GLB 二进制流数据

    Example

    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="{}")
    }

    validate_document

    校验 glTF 文档完整性与规范合规性

    Example

    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")
    }

    Source Files