"""Génère calibration.glb — le repère de calibration de l'étape E1. Trois branches de longueurs DIFFÉRENTES, aux couleurs de la convention habituelle : +X rouge (1.00 m) +Y vert (0.60 m) +Z bleu (0.30 m) Pourquoi trois longueurs différentes : un repère symétrique ne permet pas de voir une inversion d'axe. Ici, si la conversion glTF -> Unity se trompe de signe, la branche rouge part du mauvais côté par rapport à la bleue, et ça se voit sans mesurer. Le modèle est écrit en coordonnées glTF (main droite, Y-up, mètres) — c'est-à-dire exactement la convention du manifeste. C'est ce qui en fait un test valable : l'app place un marqueur AUX MÊMES coordonnées, sans passer par l'importeur. Usage : python3 make_calibration_glb.py [chemin/de/sortie.glb] """ import base64 import json import struct import sys from pathlib import Path AXES = [ # (nom, dimensions du pavé, couleur RGBA) ("AxisX", (1.00, 0.04, 0.04), (0.85, 0.15, 0.15, 1.0)), ("AxisY", (0.04, 0.60, 0.04), (0.15, 0.75, 0.20, 1.0)), ("AxisZ", (0.04, 0.04, 0.30), (0.15, 0.35, 0.90, 1.0)), ] FACES = [ ((0, 0, 1), [(0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]), ((0, 0, -1), [(1, 0, 0), (0, 0, 0), (0, 1, 0), (1, 1, 0)]), ((1, 0, 0), [(1, 0, 1), (1, 0, 0), (1, 1, 0), (1, 1, 1)]), ((-1, 0, 0), [(0, 0, 0), (0, 0, 1), (0, 1, 1), (0, 1, 0)]), ((0, 1, 0), [(0, 1, 1), (1, 1, 1), (1, 1, 0), (0, 1, 0)]), ((0, -1, 0), [(0, 0, 0), (1, 0, 0), (1, 0, 1), (0, 0, 1)]), ] def box(size): """Pavé dont un coin est à l'origine et qui s'étend vers +X, +Y, +Z.""" positions, normals, indices = [], [], [] for normal, corners in FACES: base = len(positions) for cx, cy, cz in corners: positions.append((cx * size[0], cy * size[1], cz * size[2])) normals.append(normal) indices += [base, base + 1, base + 2, base, base + 2, base + 3] return positions, normals, indices def main(out_path): buffer = bytearray() buffer_views, accessors, meshes, materials, nodes = [], [], [], [], [] def add_view(data, target): while len(buffer) % 4: buffer.append(0) offset = len(buffer) buffer.extend(data) buffer_views.append( {"buffer": 0, "byteOffset": offset, "byteLength": len(data), "target": target} ) return len(buffer_views) - 1 for name, size, color in AXES: positions, normals, indices = box(size) pos_view = add_view(b"".join(struct.pack("<3f", *p) for p in positions), 34962) nrm_view = add_view(b"".join(struct.pack("<3f", *n) for n in normals), 34962) idx_view = add_view(b"".join(struct.pack(" 1 else "calibration.glb")