BlueBoat gltf Walkthrough

This page documents the workflow applied to the BlueBoat USV model in the bluerobotics_models]() repo. The purpose is to illustrate the workflow applied to a minimal exaple.

Step 1: Commission

The commission contains three parts.

The commission gets it’s own PR - https://github.com/HonuRobotics/bluerobotics_models/pull/84

Step 2: Author 3D asset

The individual models and .blend assembly are included in https://github.com/HonuRobotics/bluerobotics_models/pull/83

Step 3: Fill the manifest from the commission

The integrator uses gltf_robotics.manifest and the commission YAML file to write the meta data manifest into each delivered file. from the commission.

The inputs are the commission bluerobotics_parts/commissions/commission-blueboat.yaml from PR #84 and the three <part>_visual.glb files from PR #83. The step runs once PR #84 is approved, on PR #83’s branch, so the stamped files land in the delivery PR.

From the bluerobotics_models root, with the commission available on the branch:

GLTF=~/maritime_ws/tools/gltf-robotics
M=bluerobotics_parts/models
PYTHONPATH=$GLTF/src python3 -m gltf_robotics.manifest \
    bluerobotics_parts/commissions/commission-blueboat.yaml \
    $M/blueboat_chassis/blueboat_chassis_visual.glb \
    $M/m200_weedless_prop_cw/m200_weedless_prop_cw_visual.glb \
    $M/m200_weedless_prop_ccw/m200_weedless_prop_ccw_visual.glb

With gltf-robotics installed (pip install -e ~/maritime_ws/tools/gltf-robotics), gltf-manifest replaces the PYTHONPATH=… python3 -m gltf_robotics.manifest prefix.

The command finds each part’s block by the file name (<part>_visual.glb → <part>), so the commission’s part keys must match the delivered names exactly. A dry run on 2026-10-17 with the PR #84 and PR #83 heads, writing to a scratch directory with -o, gave:

blueboat_chassis_visual.glb: manifest written, 11 properties, role base
m200_weedless_prop_cw_visual.glb: manifest written, 10 properties, role component
m200_weedless_prop_ccw_visual.glb: manifest written, 10 properties, role component

If the commission changes during review (for example, the prop datum Ryan is asked to check in PR #84), edit the YAML and rerun the same command; no re-export is needed.

Carried to Step 4, from gltf-summary on the same files:

  • Each prop file holds both props: two scenes, m200_weedless_prop_cw and m200_weedless_prop_ccw, differing only in which one the file opens. A delivery is one part per file.

  • Node names use the old suffix, <part>.visual, in all three files.

  • Chassis extent along glTF +Z (forward) is 1.192 m, against the commission’s 1.20 m length overall. Prop extent along glTF +Y is 0.111 m, against 112 mm diameter.

  • Prop origin reads x 0.50, y 0.50, z 0.46, close to the bounding-box midpoint, where the commission’s datum is the hub’s mating face. Check it against the datum in the Sample Viewer and Gazebo.

Step 4: Verify

Valid? Test with Khronos CLI gltf_validator

Run the validator tool on all three delivered glb files. For example, for the cassis part…

cd /home/bsb/maritime_ws/src/bluerobotics_models/bluerobotics_parts/models/blueboat_chassis
gltf_validator -o -a blueboat_chassis_visual.glb 

Results in

/home/bsb/maritime_ws/src/bluerobotics_models/bluerobotics_parts/models/blueboat_chassis/blueboat_chassis_visual.glb
Errors: 0, Warnings: 1, Infos: 0, Hints: 0
Time: 16ms

        Warnings:
                /meshes/0/primitives/0/material: Material requires a tangent space but the mesh primitive does not provide it. Runtime-generated tangent space may be non-portable across implementations.
    ...

The three delivered files from PR #83:

File

Errors

Warnings

Infos

blueboat_chassis_visual.glb

0

1

0

m200_weedless_prop_cw_visual.glb

0

2

0

m200_weedless_prop_ccw_visual.glb

0

2

0

All three are valid glTF: the gate is zero errors.

The tangent space warnings are expected and accepted because:

Every warning is MESH_PRIMITIVE_GENERATED_TANGENT_SPACE. These warnings are expected and acceptable:

  • A normal map stores its directions in tangent space, so the renderer needs a tangent at each vertex to apply it. glTF lets the file carry those tangents as a TANGENT attribute; when it does not, the renderer generates them itself, and the validator warns that generated tangents can differ between implementations.

  • Blender writes no tangents unless the exporter’s Tangents option is on

  • Neither Gazebo nor RViz reads them (Geometry), so the warning is expected and is accepted with this explanation.

Run on the files after Step 3, the validator adds one info, UNSUPPORTED_EXTENSION for KHR_xmp_json_ld: the validator cannot check the manifest’s extension, which is expected. The manifest itself is checked by gltf-check.

Why two (not one) errors for the prop files?

The duplicate warnings for the prop files are because each file is actually almost identical and each file contains. Each file contains BOTH mesh files (CW and CCW). Each prop file contains two scenes…

m200_weedless_prop_*_visual.glb:
  scene[0] 'm200_weedless_prop_ccw'
   └─ node[0] 'm200_weedless_prop_ccw.visual'
       └─ mesh[0] 'Cylinder.004'
           └─ primitive[0] ── material 'Prop-CCW-Mat'     ← warning 1

★ scene[1] 'm200_weedless_prop_cw'
   └─ node[1] 'm200_weedless_prop_cw.visual'
       └─ mesh[1] 'Cylinder.007'
           └─ primitive[0] ── material 'Prop-CW-Mat'      ← warning 2

The CCW glb opens the first scene. The CW glb also contains both scenes, but opens the second scene.

This means we have the same information twice. This also means that the files should fail future tests we check for compliance with the profile which requires one and only one scene per file to avoid this.

Intended? Visual check with Khronos sample viewer in browser

This is a qualitative test and requires human evaluation of the visuals.

  1. Open Khronos Sample Viewer

  2. Drag and drop the individual .glb files to upload to tool

  3. Checks

    • The default, initial view should show the front view of the model and the up direction of the model should be up. This is key because it verifies that the orientation of the mesh is consistent with the profile coordinate conventions.

    • Also rotate, zoom, etc. to visually expect.

Compliant? Custom gltf-check to enforce [profile]

gltf-check tests each file against the profile and passes when there are no FAIL lines and every WARN is understood. gltf-summary describes what is in the file, and its origin at line is checked against the commission’s datum. How to read the gltf-check output is in Checking a delivery.

Here both run on the files as delivered in PR #83, before Step 3 has written the manifest, because the commission (PR #84) is not yet merged. The manifest test therefore FAILs, and the Units test is OPEN because no dimension is cited; both are decided once Step 3 has run. For the chassis:

GLTF=~/maritime_ws/tools/gltf-robotics
cd ~/maritime_ws/src/bluerobotics_models/bluerobotics_parts/models/blueboat_chassis
PYTHONPATH=$GLTF/src python3 -m gltf_robotics.check.cli blueboat_chassis_visual.glb

(Can also run PYTHONPATH=$GLTF/src python3 -m gltf_robotics.check.cli -h for cli options.)

PYTHONPATH=$GLTF/src python3 -m gltf_robotics.summary blueboat_chassis_visual.glb


For the props:

```bash
cd ~/maritime_ws/src/bluerobotics_models/bluerobotics_parts/models/m200_weedless_prop_cw
PYTHONPATH=$GLTF/src python3 -m gltf_robotics.check.cli m200_weedless_prop_cw_visual.glb
PYTHONPATH=$GLTF/src python3 -m gltf_robotics.summary m200_weedless_prop_cw_visual.glb

cd ../m200_weedless_prop_ccw
PYTHONPATH=$GLTF/src python3 -m gltf_robotics.check.cli m200_weedless_prop_ccw_visual.glb
PYTHONPATH=$GLTF/src python3 -m gltf_robotics.summary m200_weedless_prop_ccw_visual.glb

With gltf-robotics installed, gltf-check and gltf-summary replace the PYTHONPATH=… python3 -m … prefixes. gltf-check -q drops the PASS and SKIP lines, -v lists every test with its full reason, and gltf-summary --explain appends how to read its output.

Every gltf-check run also writes the full record as Markdown to gltf-check-output.md in the current directory (--report FILE.md puts it elsewhere). For the whole delivery at once, the report has a matrix of the three parts side by side, then each file’s tests, reasons and scene/node/mesh tree:

cd ~/maritime_ws/src/bluerobotics_models/bluerobotics_parts/models
PYTHONPATH=$GLTF/src python3 -m gltf_robotics.check.cli \
    blueboat_chassis/blueboat_chassis_visual.glb \
    m200_weedless_prop_cw/m200_weedless_prop_cw_visual.glb \
    m200_weedless_prop_ccw/m200_weedless_prop_ccw_visual.glb

Results: