Nous explorons le format G3DB exploité dans nombre de jeux programmés en Java sous Android s’appuyant sur la bibliothèque LibGDX. Cette étude sera l’occasion de découvrir le format universel binaire JSON, conçu pour sa compacité tout en conservant une facilité d’analyse bien connue de ce format de données. Nous aboutirons ainsi à un modèle 3D exploitable dans tout logiciel de conception assistée par ordinateur et conclurons par l’impression des divers éléments de la maquette et son assemblage.
Étant en train d’étudier les signaux émis par le RADAR spatioporté Sentinel-1 [1], nous désirions obtenir un modèle 3D du satellite pour illustrer son fonctionnement. L’ESA nous informe que le seul modèle à leur disposition n’est pas diffusable au public, mais est un modèle exact d’ingénierie qui contient des éléments de conception secrets qui imposent de signer un accord de confidentialité. La boîte Lego de ce satellite fait partie d’une collection produite en petite série et revendue à un prix déraisonnable. La société qui a été contractée par l’ESA pour développer l’application Sentinel pour Android (figure 1) nous informe ne pas pouvoir diffuser le modèle 3D qui est inclus dans l’application. Parmi les options qui restent, nous pouvons nous rouler par terre en criant très fort jusqu’à ce qu’un des interlocuteurs craque, une solution qui a fait ses preuves par le passé, mais qui devient moins crédible à presque 50 ans (quoi que...), nous pouvons sélectionner la solution capitaliste de dépenser une somme déraisonnable d’argent qui ne nous apprendra rien, ou nous pouvons tenter de décoder le format des modèles 3D inclus dans l’application Android afin de générer un modèle exploitable, par exemple pour une impression 3D. C’est naturellement la troisième solution que nous allons aborder ici.
1. Le format G3DB des modèles 3D dans les jeux Android
Le ficher APK (Android Package) de l’application Sentinel pour Android se télécharge sur n’importe quel site web approprié, par exemple https://m.apkpure.com/copernicus-sentinel/esa.sentinel (112 Mo pour la version 4.19) dont le risque de cheval de Troie dans le code Java importe peu puisque nous ne l’exécuterons pas, et se décompresse/désarchive au moyen de unzip une première fois, puis une seconde fois pour esa.sentinel.apk. L’emplacement des modèles 3D des satellites de la série Sentinel se trouve dans assets/model et pour la version qui nous intéresse assets/model/sentinel1int qui ne contient pas toutes les annotations de assets/model/sentinel1, qui ne nous intéressent pas ici. Ce répertoire contient un certain nombre de textures au format JPEG qui ne seront pas utiles, et des modèles 3D des diverses parties du satellite au format G3DB. C’est donc ce format de fichiers qu’il nous faut apprendre à analyser.
Nous découvrons en parcourant le Web qu’il s’agit d’un format utilisé par les jeux développés pour Android s’appuyant sur une bibliothèque LibGDX (https://libgdx.com/) en Java. Un outil de conversion est disponible pour produire ces fichiers sur https://github.com/libgdx/fbx-conv, mais uniquement pour générer ces fichiers, pas pour les lire. N’étant pas familier avec le développement sous Android et encore moins en Java, l’utilisation de cette bibliothèque pour rédiger un convertisseur vers un format exploitable – lisible dans FreeCAD, Blender, MeshLab ou CloudCompare, puis Cura pour impression – semble vouée à l’échec. Néanmoins, un outil multiplateforme sur https://github.com/ASneakyFox/libgdx-fbxconv-gui permet au moins de visualiser les modèles sous GNU/Linux et de valider leur contenu, toujours sans permettre d’exporter vers un format exploitable, puisque s’appuyant sur fbx-conv mentionné auparavant pour les conversions. Heureusement, https://github.com/libgdx/fbx-conv/wiki nous en dit un peu plus sur le format de stockage : il s’agit de JSON au format binaire contenant divers champs décrivant la pièce, mais aussi un certain nombre de séparateurs pour définir des blocs de données (de la forme {} ou []) qui semblent empêcher l’utilisation d’outils de lecture de fichiers JSON binaires tels que fournis par Python (bibliothèque py-ubjson). Si tout échoue, nous allons finir par rédiger notre propre décodeur de fichiers.
2. Le format JSON binaire
Les formats binaires ne sont jamais très sympathiques puisque souffrant du problème d’endianness, à savoir l’ordre des octets dans un mot de plus de 8 bits. Java étant conçu pour échanger des informations au travers d’Internet (on tend à oublier que le langage a été développé par Sun Microsystems), il est naturel de représenter les données au format de son transport sur un réseau compatible Internet, donc en big endian tel que le confirme https://universal-binary-json.readthedocs.io/en/latest/spec.html. Pour rappel, big endian signifie que l’octet de poids le plus fort est à l’adresse la plus faible, donc le format le plus simple à lire pour un Occidental qui lit de gauche à droite. Ainsi, une observation du fichier binaire sera de la forme :
Ceci nous montre une alternance de codes lisibles en ASCII (version ou POSITION), des séparateurs de blocs sous forme d’accolades et de crochets, et des valeurs en binaire. La description du format Universal Binary JSON mentionnée ci-dessus nous permet de comprendre que s indique une chaîne de caractères dont l’argument suivant est le nombre d’éléments dans la chaîne (un octet pour s ou quatre octets pour la taille de S) tandis que A indique le début d’un tableau. Cette documentation n’est pas claire sur le fait que l’argument qui suit A est la nature du contenu du tableau, ici un d qui signifie « nombre à virgule flottante » codé sur 4 octets. Ainsi donc à l’adresse 0x4B, Ad.... représentant 41640001a3f4 signifie un tableau (A) de 107508 (0x001a3f4) nombres à virgule flottante (d). Par ailleurs, juste au-dessus, aux adresses 0x30 à 0x3F, nous avions appris que ces sommets (vertices à l’adresse 0x43) sont décrits par deux paramètres que sont sa position (POSITION) et son orientation (NORMAL) donc 2× 3 coordonnées telles que le décrit https://github.com/libgdx/fbx-conv/wiki/Version-0.1-%28libgdx-0.9.9%29 dans sa section « Attributes ». Heureusement, 107508 est bien multiple de 6, donc le tableau représente les attributs de 107508/6=17918 points.
Plus loin dans le fichier toujours analysé avec hexedit ou xxd, nous verrons que ces sommets sont regroupés en facettes triangulaires :
Ceci en commençant par la description d’une pièce (parts) d’identifiant fournie comme chaîne de caractères (s), ici nommée shape1_part2, formée d’assemblages de triangles (types.TRIANGLES qui sont tous deux des chaînes de caractères s) et finalement, un tableau A d’entiers i codés sur 16 bits. Nous constatons ensuite que les indices codés sur 16 bits commencent à 0 et s’incrémentent, laissant supposer une cohérence entre l’ordre des sommets et leur utilisation en facettes.
Fort de ces connaissances, nous pourrons générer pour chaque objet un fichier au format STL (STereoLithography, compris par tous les outils de gestion de pièces décrites dans l’espace) qui justement assemble des facettes triangulaires tel que nous avions appris à le faire pour tracer les diagrammes de rayonnement d’antennes [2]. Cette procédure est automatisée dans le programme proposé sur https://github.com/jmfriedt/g3db2stl/. On notera que chaque fichier STL créé pour chaque pièce est nommé du nom de cette pièce, unique dans chaque fichier G3DB, mais commun à divers fichiers. Il est donc judicieux d’analyser chaque fichier G3DB dans un sous-répertoire, pour éviter d’écraser les fichiers STL générés lors des analyses des fichiers précédents. Nous constatons à l’issue de ce traitement que les fichiers 3D exploitables sont conformes aux attentes du modèle 3D visible dans l’application Android, par exemple en chargeant chaque fichier STL dans MeshLab (figure 2).
3. Assemblage de pièces
Il reste un point de détail : un satellite est formé de nombreuses pièces, et chaque pièce a une position, orientation et taille dans l’espace. Ces informations sont issues de l’analyse des fichiers G3DB dans les champs :
à savoir ici rotation, scale et translation qui prennent comme arguments des nombres représentés en virgules flottantes d, quatre pour une rotation représentée sous forme de quaternion (axe de rotation et angle) ou trois pour homothétie et translation. Ce résultat est à peine plus simple à lire en sortie de notre outil de décodage sous la forme :
Fort des connaissances acquises sur la programmation de FreeCAD au moyen de scripts Python [3], il nous reste à importer les pièces au format STL dans FreeCAD et à les orienter/positionner en scriptant dans la console Python de FreeCAD. En effet, il ne semble pas possible de fournir les paramètres de rotation dans les menus de FreeCAD sous forme de quaternion, alors que cette représentation est celle utilisée en interne comme par tout logiciel de gestion de structures 3D pour s’appuyer sur l’algèbre associée telle que décrite par Hamilton (le mathématicien mort en 1865, pas le conducteur de voitures). Ayant chargé les pièces, nous les positionnons dans l’espace par un script de la forme :
ce que nous répétons pour chaque objet formant le satellite. En commentaire, nous avons laissé l’interprétation des données du quaternion q1 sous forme de vecteur directeur r.Axis et d’angle de rotation r.Angle pour valider la bonne compréhension de ces paramètres. À l’issue de ces traitements, nous obtenons un modèle complet (figure 2) avec des pièces aux positions relatives correctes et que nous espérons exploitables pour une impression 3D, par exemple en exportant un fichier STL combinant tous les éléments pour être importé dans Cura (figure 3), l’outil proposé pour commander les imprimantes 3D Ultimaker, dans notre cas une Ultimaker 2+.
L’impression n’est pas aussi triviale qu’il pourrait sembler, puisque des structures lévitent dans le vide et ne seront pas imprimables en l’état (figure 3, gauche) et le modèle graphique est discontinu, puisque par exemple les cellules photovoltaïques des panneaux solaires ne sont pas reliées entre elles (figure 3, droite). Un peu de manipulation du modèle sera donc nécessaire avant son impression.
4. Impression de la maquette
L’impression 3D directe s’avère décevante, avec un volume excessif de supports pour les éléments suspendus et des panneaux solaires trop fins et séparés en cellules photovoltaïques individuelles. Il faut donc éditer le modèle 3D et le rendre imprimable. Étant incapables de comprendre comment éditer les mailles (mesh) dans FreeCAD, nous nous tournons vers Blender qui va nous permettre d’une part de modifier le modèle, quitte à perdre un peu en réalisme, pour le rendre imprimable d’une part, et d’autre part de couper les parties suspendues dans le vide qui ont nécessité tant de support inextricable de la structure principale après impression.
Nous amenons des modifications structurelles telles que supporter les panneaux solaires par un parallélépipède rectangle pour soutenir les éléments individuels, épaissir les bras de maintien des panneaux solaires (extrusion en mode édition de Blender – touche <Tab>) et leur ajouter un point d’attache (Add > Mesh > Cube), et coupons les éléments du satellite qui seraient suspendus dans le vide lors de l’impression en les incluant dans un cube et par opération booléenne d’intersection suivant la séquence Add Modifier sur l’icône en forme de clé anglaise en ayant sélectionné le corps du satellite, Boolean et Intersect avec le cube qui englobe la partie qui nous intéresse (figure 4). La sauvegarde du STL résultant s’obtient par File > Export > STL en cochant l’option Selection Only (figure 4). Nous nous retrouvons donc avec un fichier STL des panneaux solaires, un pour le corps, et un contenant les divers éléments additionnels que sont les antennes et autres éléments dépassant du corps. Ces divers fichiers STL sont chargés dans Cura 4.9.0 pour génération du G-code en vue de l’impression. Le premier résultat d’impression est à nouveau décevant, car l’antenne du RADAR n’est pas plane et la jupe ne retient la structure que sur une surface insuffisante, la buse emportant la pièce au premier défaut de positionnement. Pour pallier ce problème, Olivier Testault propose d’abaisser la structure de quelques dixièmes de millimètres sous le plateau d’impression pour garantir une jupe qui maintienne correctement la pièce en place. Fort de ce conseil, l’impression des diverses pièces s’avère excellente, au crédit de Cura qui a réussi à interpréter des fichiers STL qui n’étaient nullement prévus pour une impression (figure 5).
Les diverses pièces sont finalement assemblées par collage à la colle cyanoacrylate parfaitement compatible avec l’acide polylactique (PLA) qui sert à l’impression 3D. À notre grande surprise – la publicité de Super Glue-3 de https://www.dailymotion.com/video/x8ili9 n’était peut-être pas complètement mensongère – aucun renfort n’est nécessaire pour maintenir les panneaux solaires en place, malgré la surface de collage de seulement quelques millimètres carrés. Bien entendu, on ne déposera jamais la colle du tube sur la surface à encoller, mais nous déposerons quelques gouttes sur un support sacrifié en vue d’étaler le volume nécessaire sur la surface à encoller avec un cure-dent (figure 6, gauche).
Conclusion
Partant d’une archive d’application Android contenant des modèles 3D de satellites, nous avons exploré le format de stockage G3DB, découvert le format binaire universel JSON, plus compact que son homologue textuel ASCII, et fini par obtenir un modèle cohérent de satellite dans un format exploitable dans un outil de conception par ordinateur de pièces mécaniques tel que FreeCAD. Néanmoins, la manipulation des mailles (mesh) représentant le satellite en vue de les rendre compatibles avec une fabrication par imprimante 3D et les découper en éléments individuels s’est avérée plus aisée avec Blender, qui nous a permis d’atteindre l’objectif de réaliser une maquette de l’objet convoité (figure 6).
Le lecteur intéressé par les autres Sentinel – 2 à 5 actuellement bien que seuls les modèles de 2, 3 et 5P ne soient disponibles dans l’application Android à la date de cette rédaction – est encouragé à reproduire la démarche pour démontrer la compréhension de la procédure suivie. Le modèle de Sentinel-6 directement lisible par Blender est disponible sur le site de la NASA sur https://nasa3d.arc.nasa.gov/detail/Sentinel-6.
L’ensemble des développements, partant du programme de conversion de G3DB vers STL jusqu’au modèle imprimable de Sentinel-1 en passant par le script FreeCAD pour aligner et orienter les pièces, est accessible sur https://github.com/jmfriedt/g3db2stl/.
Remerciements
É. Carry (FEMTO-ST/Temps-Fréquence, Besançon) a fourni l’infrastructure pour l’impression 3D des maquettes. O. Testault (Fablab Des Fabriques, Besançon) a fourni les solutions à nos problèmes d’impression 3D et en particulier d’adhésion de l’antenne du RADAR au plateau de l’imprimante. C. Plantard (ESA/ESTEC, Noordwijk, Hollande) a transmis nos questions concernant l’architecture de Sentinel-1 et la disponibilité d’un modèle librement exploitable. On pourra argumenter qu’un enseignant-chercheur de l’université a vraiment beaucoup trop de temps libre pour se permettre ce type d’activité : nous rétorquerons que cette démarche d’analyse pour atteindre un objectif concret est au cœur de nos enseignements et une source de motivation pour aiguiser la curiosité des étudiants. Jusqu’à preuve du contraire...
Références
[1] J.-M Friedt, « Analyse des données brutes des RADAR spatioportés Sentinel-1 : traitement des signaux radiofréquences », Hackable (à paraître).
[2] J.-M Friedt, É. Carry, O. Testault, « Petites antennes réalisées par impression additive : de la conception à la visualisation des diagrammes de rayonnement (en vrai et en virtuel) », Hackable n°31, p.80–96 (oct.-nov. 2019) : https://connect.ed-diamond.com/Hackable/HK-031/Petites-antennes-realisees-par-impression-additive-de-la-conception-a-la-visualisation-des-diagrammes-de-rayonnement-en-vrai-et-en-virtuel
[3] J.-M Friedt, O. Testault, É. Carry, « Dessiner des carrés avec des ronds : simulation d’un ordinateur mécanique en scriptant sous FreeCAD », Hackable n°35 (oct.-déc. 2020) : https://connect.ed-diamond.com/Hackable/HK-035/Simulation-d-un-ordinateur-mecanique-en-scriptant-sous-FreeCAD