Thomas Fransolet 0310d28b5e Canal VR Meta Quest : projet Unity, viewer web et documentation
Premier commit du cinquième front. Trois parties :

- `unity/MyInfoMateVR/` : le projet Unity (6000.0.83f1, URP, Meta XR SDK 205),
  un APK unique pour tous les clients. Menu flottant à sélection au regard,
  appairage, chargement de scène GLB, POI, cache de contenu, télémétrie.
- `unity-overlay/` : les mêmes scripts à recopier sur un projet Unity neuf, avec
  les pièges rencontrés consignés dans son README.
- `viewer/` : viewer et éditeur de scène web autonome (Vite, TypeScript,
  three.js), partagé avec les autres fronts.
- `docs/` : état des lieux, setup Unity, décisions d'architecture et plan
  d'exécution en 9 étapes.

La scène est décrite par un `scene.json` poussé par `adb push` : l'app le
préfère à celui embarqué dans l'APK.

Les binaires (GLB, textures de l'échantillon Sponza, DLL Meta XR) passent par
Git LFS dès ce premier commit — les y faire entrer après coup demanderait de
réécrire l'historique. Les artefacts régénérés par l'éditeur et par CMake
(`Library/`, `.utmp/`, Burst debug) sont ignorés.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 15:26:07 +02:00

56 lines
2.3 KiB
C#

using UnityEngine;
namespace MyInfoMate.Vr.Scene
{
/// <summary>
/// Conversion glTF → Unity. <b>Le seul endroit du projet où elle existe.</b>
///
/// glTF est en main droite, Y-up, unités en mètres. Unity est en main
/// <b>gauche</b>. Passer de l'un à l'autre demande d'inverser un axe horizontal.
///
/// Le décor, lui, est converti par glTFast à l'import. Les coordonnées du
/// manifeste, elles, ne passent par aucun importeur. Si les deux conversions
/// ne sont pas la même, les objets placés sont en miroir par rapport au décor
/// — et c'est <b>invisible sur une scène symétrique</b>, donc découvert tard.
///
/// D'où <see cref="Axis"/> : la valeur par défaut est celle que documente
/// glTFast, mais elle se <b>vérifie</b> avec calibration.glb (voir
/// <see cref="CalibrationCheck"/>), elle ne se suppose pas. Si le test montre
/// un miroir, c'est cette seule ligne qui change.
/// </summary>
public static class GltfSpace
{
public enum Convention
{
NegateX,
NegateZ
}
public static Convention Axis = Convention.NegateX;
public static Vector3 ToUnity(Vector3 p) =>
Axis == Convention.NegateX
? new Vector3(-p.x, p.y, p.z)
: new Vector3(p.x, p.y, -p.z);
/// <summary>
/// Une réflexion inverse le sens des rotations : l'axe est réfléchi <i>et</i>
/// l'angle change de signe. Sur un quaternion, ça revient à inverser le signe
/// des deux composantes que la réflexion ne touche pas.
/// </summary>
public static Quaternion ToUnity(Quaternion q) =>
Axis == Convention.NegateX
? new Quaternion(q.x, -q.y, -q.z, q.w)
: new Quaternion(-q.x, -q.y, q.z, q.w);
/// <summary>Les coordonnées du manifeste arrivent en tableaux JSON.</summary>
public static Vector3 Position(float[] v) => ToUnity(new Vector3(v[0], v[1], v[2]));
public static Quaternion Rotation(float[] q) =>
ToUnity(new Quaternion(q[0], q[1], q[2], q[3]));
/// <summary>L'échelle est invariante par réflexion — pas de conversion.</summary>
public static Vector3 Scale(float[] s) => new Vector3(s[0], s[1], s[2]);
}
}