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

88 lines
4.2 KiB
C#

using MyInfoMate.Vr.Boot;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace MyInfoMate.Vr.EditorTools
{
/// <summary>
/// Construit Boot.unity <b>par code</b> plutôt que de versionner un .unity écrit à
/// la main. Le YAML d'une scène Unity est plein de GUID et de fileID internes :
/// écrit à la main il produit une scène qui s'ouvre mais dont un réglage sur deux
/// est faux <i>en silence</i>. Ceci est lisible, rejouable et diffable.
///
/// Ce que ce script ne fait pas : ajouter le rig VR. Le Camera Rig vient du SDK
/// Meta et se pose par les Building Blocks — le référencer ici créerait une
/// dépendance de compilation sur un package importé à la main.
///
/// Deux variantes, et elles coexistent volontairement le temps de la validation :
/// <b>S1</b> prouve la chaîne technique avec des chemins en dur, <b>S2</b> prouve
/// le contrat de données en lisant <c>scene.json</c>. Tant que S1 n'est pas validé
/// au casque, pouvoir revenir à la scène minimale en un clic évite de confondre
/// un bug de rendu avec un bug de manifeste.
/// </summary>
public static class BuildBootScene
{
const string ScenePath = "Assets/Scenes/Boot.unity";
[MenuItem("MyInfoMate/Construire la scène Boot (S1 — chemins en dur)")]
public static void BuildS1() =>
Build(() => new GameObject("Bootstrap").AddComponent<S1Bootstrap>(),
"S1 (chemins en dur)");
[MenuItem("MyInfoMate/Construire la scène Boot (S2 — depuis scene.json)")]
public static void BuildS2() =>
Build(() => new GameObject("Bootstrap").AddComponent<S2Bootstrap>(),
"S2 (manifeste scene.json)");
/// <summary>
/// La troisième variante appartient à l'autre plan : le canal VR (E2/E3), pas la
/// piste Scène 3D. Elle ne charge aucun décor — elle appaire le casque et affiche
/// ce que le serveur renvoie. Le code PIN se pose dans l'inspecteur du Bootstrap
/// avant le build, tant que E1 n'a pas donné de pointeur.
/// </summary>
[MenuItem("MyInfoMate/Construire la scène Boot (E2-E3 — appairage et export)")]
public static void BuildPairing() =>
Build(() => new GameObject("Bootstrap").AddComponent<PairingBootstrap>(),
"E2-E3 (appairage et export)");
/// <summary>
/// L'app du canal VR telle qu'elle tient : appairage, cache, menu flottant au
/// regard, télémétrie. La variante précédente reste là pour isoler un problème
/// de réseau d'un problème de rendu.
/// </summary>
[MenuItem("MyInfoMate/Construire la scène Boot (E5 — menu flottant)")]
public static void BuildMenu() =>
Build(() => new GameObject("Bootstrap").AddComponent<MenuBootstrap>(),
"E5 (menu flottant)");
static void Build(System.Action addBootstrap, string label)
{
var scene = EditorSceneManager.NewScene(
NewSceneSetup.EmptyScene, NewSceneMode.Single);
var light = new GameObject("Directional Light").AddComponent<Light>();
light.type = LightType.Directional;
light.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
light.intensity = 1f;
addBootstrap();
System.IO.Directory.CreateDirectory("Assets/Scenes");
EditorSceneManager.SaveScene(scene, ScenePath);
EditorBuildSettings.scenes = new[] { new EditorBuildSettingsScene(ScenePath, true) };
// Enchaîné ici plutôt que laissé à ta mémoire : une scène reconstruite
// puis buildée sans ses shaders embarqués donne un casque magenta, et
// l'erreur se diagnostique mal parce que l'éditeur, lui, va bien.
IncludeRuntimeShaders.Include();
Debug.Log($"Boot.unity construite sur {label}. Reste à faire à la main : " +
"Meta → Tools → Building Blocks → Camera Rig, puis Hand Tracking " +
"si on veut le pincement (AimSelector le détecte tout seul, et " +
"retombe sur la visée à la tête s'il est absent).");
}
}
}