Navigation
Le module Navigation permet à un curseur Inverse3 de déplacer l'espace de travail de l'application — parfois appelé locomotion à contrôle de vitesse ou « dérive façon manette de jeu ». Plutôt que de heurter une paroi dure à la limite de l'espace de travail physique, le curseur entre dans une coque souple où sa distance par rapport à un centre virtuel est associée à une vitesse qui fait glisser l'ensemble de l'espace de travail. Plus le curseur est poussé loin, plus la scène défile vite.
Le comportement principal (et actuellement le seul) est la navigation par bulle. La forme de la bulle est définie à l'aide d'une primitive SDF — voir Qu'est-ce qu'un SDF ? pour le concept.
Navigation par bulle — concept
Une bulle virtuelle est ancrée autour d'un point central dans l'espace de montage du dispositif. Le curseur évolue au sein de trois zones concentriques :
┌──────────────────────────────────────────┐
│ WALL ZONE │ cursor beyond outer shell
│ ┌────────────────────────────────────┐ │
│ │ VELOCITY ZONE │ │ soft shell → scene moves
│ │ ┌──────────────────────────────┐ │ │
│ │ │ DEAD ZONE (inside) │ │ │ no scene movement
│ │ │ │ │ │
│ │ │ ● centre │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ bubble surface │ │
│ └────────────────────────────────────┘ │
│ outer boundary │
└──────────────────────────────────────────┘
| Zone | Position du curseur | Comportement |
|---|---|---|
| Zone morte | À l'intérieur de la bulle | Aucune navigation. Léger amortissement. Vous pouvez manipuler la scène librement. |
| Zone de vitesse | Entre la surface et la limite extérieure | La courbe distance → vitesse déplace l'espace de travail dans la direction du curseur. |
| Zone de paroi | Au-delà de la limite extérieure | Un ressort rigide repousse le curseur vers l'intérieur, et la vitesse de navigation est plafonnée. |
Une force haptique de type ressort-amortisseur s'applique dans les trois zones — vous ressentez la surface, la coque de dérive et la paroi extérieure.
Démarrage rapide — activer la navigation par bulle
La navigation est une configuration persistante, envoyée en une seule fois : elle reste active jusqu'à ce que vous l'arrêtiez explicitement, que vous redémarriez le service ou que vous fermiez la session.
Démarrer (minimal — bulle par défaut en boîte arrondie)
- WebSocket
- HTTP
{
"inverse3": [
{
"device_id": "04C3",
"configure": {
"navigation": { "mode": "bubble" }
}
}
]
}
curl -X POST "http://localhost:10001/inverse3/04C3/config/navigation?session=:0" \
-H "Content-Type: application/json" \
-d '{"mode": "bubble"}'
Arrêter
- WebSocket
- HTTP
{
"inverse3": [
{
"device_id": "04C3",
"configure": {
"navigation": { "mode": "disabled" }
}
}
]
}
curl -X DELETE "http://localhost:10001/inverse3/04C3/config/navigation?session=:0"
Autres routes HTTP
| Méthode | Chemin | Rôle |
|---|---|---|
GET | /{type}/{id}/config/navigation?session=<expr> | Configuration de navigation actuelle et état |
POST | /{type}/{id}/config/navigation?session=<expr> | Démarrer ou mettre à jour la navigation |
DELETE | /{type}/{id}/config/navigation?session=<expr> | Arrêter la navigation |
Catalogue des formes de bulle
La zone morte de la bulle est décrite par une forme à fonction de distance signée (SDF). Différentes formes donnent différentes sensations de navigation — une boîte arrondie (la valeur par défaut) pour une zone de repos rectangulaire confortable, une sphère pour une dérive isotrope, un ellipsoïde pour favoriser certains axes, une capsule pour les couloirs.
Boîte arrondie (par défaut)
La zone morte par défaut est un volume plat, légèrement plus haut que profond, réglé pour être agréable à utiliser sur Inverse3 comme sur MInverse :
{
"shape": {
"primitive": "rounded_box",
"parameters": { "b": { "x": 0.05, "y": 0.02, "z": 0.03 }, "r": 0.01 }
}
}
Sphère — dérive isotrope
{
"shape": {
"primitive": "sphere",
"parameters": { "r": 0.05 }
}
}
Ellipsoïde — plus large en X/Z qu'en Y
À utiliser lorsque la dérive horizontale doit être plus souple que la dérive verticale :
{
"shape": {
"primitive": "ellipsoid",
"parameters": { "a": { "x": 0.06, "y": 0.03, "z": 0.06 } }
}
}
Capsule — couloir allongé
Deux points d'extrémité a/b ainsi qu'un rayon r :
{
"shape": {
"primitive": "capsule",
"parameters": {
"a": { "x": 0.0, "y": -0.03, "z": 0.0 },
"b": { "x": 0.0, "y": 0.03, "z": 0.0 },
"r": 0.04
}
}
}
Exemple complet avec dimensionnement personnalisé et sensation de mouvement :
{
"inverse3": [
{
"device_id": "04C3",
"configure": {
"navigation": {
"mode": "bubble",
"bubble": {
"shape": {
"primitive": "ellipsoid",
"parameters": { "a": { "x": 0.06, "y": 0.03, "z": 0.06 } }
},
"velocity_zone_width": 0.025,
"max_velocity": 1.0,
"velocity_ease": "quadratic_in"
}
}
}
}
]
}
Courbe de vitesse
La distance de la zone de vitesse est transformée par une courbe d'accélération (easing) avant d'être convertie en vitesse de l'espace de travail. Choisissez l'accélération qui correspond à la façon dont vous souhaitez que la scène accélère lorsque l'utilisateur pousse davantage dans la coque.
velocity_ease | Sensation | Quand l'utiliser |
|---|---|---|
linear | Rampe constante | Prévisible, bon choix par défaut |
quadratic_in | Départ lent, poussée plus rapide | Précis près de la surface, long trajet rapide |
cubic_in | Départ très lent | Très précis, les longs trajets montent en vitesse |
sine_out | Départ rapide et fluide, plateau près de l'extérieur | Réactif, plafonne en douceur |
quadratic_out | Rapide à l'entrée, confortable sur longue distance | Petite impulsion rapide, croisière confortable |
{ "velocity_ease": "quadratic_in", "max_velocity": 1.5 }
Comportement du centre
Le centre de la bulle se configure au moyen d'un objet imbriqué center comportant quatre
champs — position, relative, follow, speed — qui remplace l'ancienne
disposition à plat composée de center, center_enabled, center_mode et center_drift_speed.
{
"center": {
"position": { "x": 0.0, "y": 0.0, "z": 0.0 },
"relative": true,
"follow": false,
"speed": 0.02
}
}
Ancrage du centre
position et relative contrôlent l'emplacement initial du centre lors de l'activation :
relative | Sémantique de position |
|---|---|
true (par défaut) | position est un décalage par rapport au curseur — le centre initial est cursor + position. Avec la valeur par défaut (0, 0, 0), la bulle apparaît exactement sur le curseur. |
false | position est un point absolu dans l'espace de l'application — il est converti en espace de montage à l'entrée et reconverti en espace de l'application à la sortie, de sorte que les valeurs après un aller-retour correspondent. |
Déplacement ultérieur du centre
follow et speed contrôlent le comportement à chaque cycle une fois la bulle active :
follow | speed | Comportement | Équivalent précédent |
|---|---|---|---|
false (par défaut) | ignoré | Le centre reste à sa position initiale — centre fixe. | center_mode: "fixed" |
true | > 0 | Le centre dérive vers le curseur à la vitesse speed (m/s) tant que le curseur se trouve à l'intérieur. | center_mode: "auto_follow" (avec center_drift_speed) |
true | 0.0 | Le centre se cale sur le curseur à chaque cycle — effet de téléportation. | center_mode: "track_cursor" |
Le comportement par défaut du centre est désormais fixe (follow = false). Les versions antérieures
dérivaient vers le curseur par défaut (auto_follow). Définissez center.follow = true
pour rétablir l'ancien comportement de dérive, et ajustez center.speed pour correspondre à l'ancienne valeur
de center_drift_speed.
Lorsque follow = true et que le curseur dépasse la frontière extérieure, le centre
suit le curseur quelle que soit la valeur de speed, afin de garder la bulle autour de lui et
d'empêcher le curseur de s'échapper.
Réponse aux collisions
La bulle réagit aux forces externes exercées sur le curseur qui dépassent collision_detection.force_threshold :
- La zone de vitesse s'élargit temporairement (jusqu'à
collision_detection.inflate_ratiofoisvelocity_zone_width) afin de donner à l'utilisateur plus de marge de manœuvre pour contourner l'obstacle. - Lorsque
collision_detection.enabledvauttrue(par défaut), le centre de la bulle refuse de dériver dans la direction de la force de collision, ce qui empêche l'utilisateur d'entraîner l'espace de travail contre un mur rigide.
La détection de collision utilise l'hystérésis pour éviter le scintillement autour du seuil :
une fois une collision active, elle reste active jusqu'à ce que |ext_force| descende sous le
produit collision_detection.force_threshold × collision_detection.exit_ratio.
Diminuez exit_ratio pour élargir la plage d'hystérésis (collisions plus persistantes) ; réglez
la valeur à 1.0 pour désactiver l'hystérésis et n'utiliser qu'un seul seuil.
{
"collision_detection": {
"enabled": true,
"force_threshold": 0.5,
"inflate_ratio": 2.0,
"exit_ratio": 0.7
}
}
Référence des paramètres
Les plus souvent ajustés
| Paramètre | Valeur par défaut | Description |
|---|---|---|
shape | {rounded_box, r=0.01, b=(0.05,0.02,0.03)} | Forme SDF définissant la zone morte |
velocity_zone_width | 0.03 m | Épaisseur de la coque de contrôle en vitesse |
max_velocity | 0.5 m/s | Vitesse maximale de navigation |
velocity_ease | "quadratic_in" | Courbe de transition de la distance vers la vitesse |
center.follow | false | Indique si le centre suit le curseur |
center.speed | 0.02 m/s | Vitesse de dérive lorsque follow = true |
Liste complète des paramètres
| Champ | Type | Valeur par défaut | Description |
|---|---|---|---|
center.position | vec3 | (0, 0, 0) | Centre initial — décalage par rapport au curseur lorsque relative = true, point absolu de l'espace de l'application lorsque false |
center.relative | bool | true | true : position est un décalage par rapport au curseur. false : point absolu de l'espace de l'application, converti en espace de montage à la frontière |
center.follow | bool | false | false : le centre reste fixé à sa position initiale. true : le centre suit le curseur |
center.speed | float | 0.02 | Vitesse de dérive (m/s) lorsque follow = true ; 0.0 cale le centre sur le curseur à chaque cycle |
shape | shape | {rounded_box, r=0.01, b=(0.05,0.02,0.03)} | Forme SDF de la zone morte |
velocity_zone_width | float | 0.03 | Épaisseur de la coque de contrôle en vitesse (m) |
max_velocity | float | 0.5 | Vitesse maximale de navigation (m/s) |
velocity_ease | string | "quadratic_in" | Transition de la distance vers la vitesse |
reset_velocity_on_entry | bool | true | Remet à zéro la vitesse accumulée lorsque le curseur rentre à nouveau dans la zone de vitesse |
bump_width | float | 0.005 | Bosse tactile à la surface (m) |
bump_stiffness | float | 200.0 | Constante de raideur du ressort de la bosse à la surface |
spring_inner | float | 0.0 | Ressort au centre de la bulle |
spring_surface | float | 10.0 | Ressort à la surface |
spring_outer | float | 15.0 | Ressort à la frontière extérieure |
wall_stiffness | float | 700.0 | Raideur du ressort de mur rigide au-delà de la frontière extérieure |
damping_inner | float | 0.1 | Amortissement au centre |
damping_surface | float | 4.0 | Amortissement à la surface |
damping_outer | float | 7.0 | Amortissement à la frontière extérieure |
rotation_enabled | bool | false | Applique la rotation de l'espace de travail à la direction de navigation |
scale_enabled | bool | false | Applique l'échelle de l'espace de travail à la vitesse de navigation |
collision_detection.enabled | bool | true | Bloque la navigation dans la direction de la collision |
collision_detection.force_threshold | float | 1.0 | Intensité de la force externe nécessaire pour entrer en collision (N) |
collision_detection.inflate_ratio | float | 2.0 | Multiplicateur de la largeur de la zone de vitesse pendant une collision (doit être ≥ 1.0) |
collision_detection.exit_ratio | float | 0.7 | Ratio d'hystérésis — reste en collision tant que |ext_force| < force_threshold × exit_ratio. Plage (0, 1] ; 1.0 désactive l'hystérésis |
Deux groupes de fonctionnalités sont omis du schéma JSON 3.5 pendant que leurs implémentations sont remaniées pour la 3.6 :
- Bornage de la frontière de l'avatar : les champs
avatar_boundary_enabled,avatar_boundaryetavatar_boundary_hysteresis. - Délimitation de l'espace de travail : les champs
workspace_bounded,workspace_transition_speedetworkspace_transition_ease.
Le code du tick compile et s'exécute toujours avec les valeurs par défaut, mais les clients ne peuvent ni lire ni écrire ces champs via HTTP ou WebSocket. Dans la 3.6, la frontière de l'avatar reviendra sous la forme d'un bounds de premier niveau, au même niveau que l'objet bulle, avec une transformation de placement complète ; la délimitation de l'espace de travail reviendra une fois l'analyse de la gigue du centre terminée.
Règles de validation
velocity_zone_width > 00 ≤ bump_width < velocity_zone_widthmax_velocity > 0bump_stiffness ≥ 0spring_inner ≥ 0etspring_inner ≤ spring_surface ≤ spring_outerwall_stiffness ≥ 0damping_inner ≥ 0etdamping_inner ≤ damping_surface ≤ damping_outercollision_detection.force_threshold > 0collision_detection.inflate_ratio ≥ 1.0collision_detection.exit_ratiodans(0, 1]center.speed ≥ 0
Un POST ou configure.navigation avec des paramètres invalides est rejeté et un événement invalid-value est émis ; la configuration précédente reste active.
Événements
| Nom de l'événement | Déclenché lorsque |
|---|---|
navigation-started | La navigation est activée sur un appareil |
navigation-updated | La configuration de navigation est mise à jour alors qu'elle est déjà active |
navigation-stopped | La navigation est arrêtée (désactivation explicite, DELETE ou fermeture de session) |
invalid-value | Une configuration de navigation est rejetée par la validation |
Limites connues
- Délimitation de l'espace de travail et frontières de l'avatar masquées dans la 3.5 : les deux groupes de fonctionnalités sont absents du schéma JSON pendant qu'ils sont remaniés pour la 3.6. Voir la note ci-dessus, sous Référence des paramètres.
- Mise à l'échelle non uniforme + rotation : lorsque
rotation_enabledetscale_enabledsont tous deux activés, la direction de la vitesse est légèrement inexacte, car la rotation n'est pas appliquée aux axes de mise à l'échelle. - Mise à l'échelle de la taille par appareil : les tailles des bulles (rayon, largeur de zone) ne sont pas mises à l'échelle par le facteur d'échelle physique de l'appareil ; MInverse et Inverse3 utilisent les mêmes tailles absolues, qui peuvent donc sembler différentes sur chacun.