MikaFil commited on
Commit
e8fe9df
·
verified ·
1 Parent(s): 01823c0

Update viewer_ar_ios.js

Browse files
Files changed (1) hide show
  1. viewer_ar_ios.js +51 -55
viewer_ar_ios.js CHANGED
@@ -1,12 +1,12 @@
1
- /* viewer_ar_ios.js — AR PlayCanvas + GLB/USDZ via config.json (+ "sol" + mode mur stable)
2
  - config.json : { "glb_url": "...", "usdz_url": "...", "sol": true|false }
3
  - sol = true → plans horizontaux (sol) + rotation autour de l’axe local Y (haut)
4
  - sol = false → plans verticaux (mur) :
5
- * Y local = normale du mur
6
- * Z local = direction plafond (Up monde) projetée dans le plan du mur
7
- * rotation utilisateur = autour de Y local (normale)
8
  * PENDANT une translation : orientation verrouillée (pas de twist parasite)
9
- * À la fin d’une translation : on recalcule la base par rapport AU NOUVEAU MUR
10
  - iOS : AR Quick Look (USDZ)
11
  - Android/Desktop : WebXR AR + bouton "Lancer l’AR" + slider de rotation
12
  */
@@ -305,8 +305,7 @@
305
  } else {
306
  message(
307
  "Modèle chargé. Appuyez sur « Lancer l’AR ». Sur iOS (Quick Look), l’objet ne peut pas être " +
308
- "automatiquement aligné au mur : placez-le et orientez-le manuellement " +
309
- "contre le mur (Z vers le plafond)."
310
  );
311
  }
312
  } else {
@@ -397,18 +396,17 @@
397
  var d = v.dot(n);
398
  return new pc.Vec3(v.x - d*n.x, v.y - d*n.y, v.z - d*n.z);
399
  }
400
- function normalizeOr(v, fallback) {
401
- var len = v.length();
402
- if (len > 1e-6) { v.scale(1/len); return v; }
403
- return fallback.clone ? fallback.clone() : new pc.Vec3(fallback.x, fallback.y, fallback.z);
404
- }
405
- function buildWallBasisFromNormal(N) {
406
- var Y = N.clone().normalize(); // Y = normale du mur
407
- var Z = projOnPlane(UP, Y); // Z = Up projeté (vers plafond)
408
- if (Z.lengthSq() < 1e-8) Z = projOnPlane(new pc.Vec3(0,0,1), Y);
409
- Z.normalize();
410
- var X = new pc.Vec3(); X.cross(Z, Y).normalize(); // X = Z × Y
411
- Z.cross(Y, X).normalize(); // Re-orthonormalise
412
  return { X: X, Y: Y, Z: Z };
413
  }
414
  function quatFromBasis(X, Y, Z) {
@@ -447,19 +445,19 @@
447
  q.normalize();
448
  return q;
449
  }
450
- function computeWallBaseFromRot(rot) {
451
- // Y monde = rot * (0,1,0) = normale du mur
452
- var N = new pc.Vec3();
453
- rot.transformVector(new pc.Vec3(0, 1, 0), N);
454
- N.normalize();
455
- var basis = buildWallBasisFromNormal(N);
456
- return quatFromBasis(basis.X, basis.Y, basis.Z); // Y=normale, Z=plafond
457
  }
458
 
459
  // Mode mur / sol
460
  var wallMode = !PLACE_ON_FLOOR;
461
- var wallBaseRot = new pc.Quat(); // base (mur) : Y=normale, Z=plafond
462
- var wallAngleDeg = 0; // yaw utilisateur autour de Y local
463
 
464
  // Ombre blob (sol uniquement)
465
  var blob = null;
@@ -516,7 +514,7 @@
516
  return e;
517
  }
518
 
519
- // Rotation via slider (sol: Euler Y, mur: twist autour de Y local)
520
  var baseEulerX = 0, baseEulerZ = 0;
521
  var rotationYDeg = 0;
522
  function clamp360(d) { return Math.max(0, Math.min(360, d)); }
@@ -536,11 +534,12 @@
536
  rotationYDeg = y;
537
  modelRoot.setEulerAngles(baseEulerX, y, baseEulerZ);
538
  } else {
 
539
  wallAngleDeg = y;
540
  var qLocal = new pc.Quat();
541
- qLocal.setFromAxisAngle(pc.Vec3.UP, y); // rotation autour de Y local (avant base)
542
  var qFinal = new pc.Quat();
543
- qFinal.mul2(wallBaseRot, qLocal); // base mur (Y=normale, Z=up projeté) puis yaw utilisateur
544
  modelRoot.setRotation(qFinal);
545
  }
546
  updateKnobFromY(y);
@@ -583,17 +582,19 @@
583
 
584
  if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
585
 
586
- // ----- Détection orientation des plans -----
587
- var TMP_IN = new pc.Vec3(0, 1, 0), TMP_OUT = new pc.Vec3();
588
 
589
  function isHorizontalUpFacing(rot, minDot) {
 
590
  minDot = (typeof minDot === "number") ? minDot : 0.75;
591
- rot.transformVector(TMP_IN, TMP_OUT);
592
  return TMP_OUT.y >= minDot;
593
  }
594
  function isVerticalFacing(rot, maxAbsY) {
 
595
  maxAbsY = (typeof maxAbsY === "number") ? maxAbsY : 0.35;
596
- rot.transformVector(TMP_IN, TMP_OUT);
597
  return Math.abs(TMP_OUT.y) <= maxAbsY;
598
  }
599
  function planeMatchesMode(rot) {
@@ -692,16 +693,16 @@
692
  applyRotationY(y0);
693
  } else {
694
  // --- MODE MUR (première pose) ---
695
- wallBaseRot.copy(computeWallBaseFromRot(rot)); // Y=normale, Z=plafond
696
  modelRoot.setRotation(wallBaseRot);
697
- applyRotationY(0); // yaw initial = 0
698
  }
699
 
700
  placedOnce = true;
701
  rotInput.disabled = false;
702
  message(
703
  wallMode
704
- ? "Objet placé contre le mur (Y=normale, Z→plafond). Glissez pour déplacer, tournez avec le slider →"
705
  : "Objet placé. Glissez pour déplacer, tournez-le avec le slider →"
706
  );
707
  }
@@ -711,6 +712,10 @@
711
  });
712
 
713
  // Drag XR continu (déplacements suivants, sans rotation parasite)
 
 
 
 
714
  app.xr.input.on("add", function (inputSource) {
715
  inputSource.on("selectstart", function () {
716
  if (uiInteracting) return;
@@ -729,29 +734,23 @@
729
  if (!isDragging) return;
730
  if (!planeMatchesMode(rot)) return;
731
 
732
- // Mémorise la dernière orientation de plan rencontrée
733
- lastHitRot.copy(rot);
734
-
735
- // Déplace uniquement
736
- modelRoot.setPosition(pos);
737
 
738
  if (!wallMode) {
739
  updateBlobPositionUnder(pos, rot);
740
  } else {
741
- // En mode mur : PAS de rotation pendant le drag
742
- modelRoot.setRotation(dragLockedRot);
743
  }
744
  });
745
 
746
- // Fin du drag : snap propre au NOUVEAU mur (si on est en mode mur)
747
  transientSource.once("remove", function () {
748
  isDragging = false;
749
  if (wallMode) {
750
- // Recalcule la base avec le dernier mur touché
751
- wallBaseRot.copy(computeWallBaseFromRot(lastHitRot));
752
- // Ré-applique le yaw utilisateur autour de Y local (normale du NOUVEAU mur)
753
  var qLocal = new pc.Quat();
754
- qLocal.setFromAxisAngle(pc.Vec3.UP, wallAngleDeg);
755
  var qFinal = new pc.Quat();
756
  qFinal.mul2(wallBaseRot, qLocal);
757
  modelRoot.setRotation(qFinal);
@@ -795,10 +794,7 @@
795
  }
796
  });
797
  app.mouse.on("mouseup", function () {
798
- if (isDragging) {
799
- // En desktop on n'a pas lastHitRot : on garde l'orientation verrouillée
800
- // (optionnel : on pourrait lancer un hit-test unique ici pour "snap", selon l'intégration)
801
- }
802
  isDragging = false;
803
  rotateMode = false;
804
  });
@@ -815,7 +811,7 @@
815
  app.xr.on("start", function () {
816
  if (startBtn) startBtn.style.display = "none";
817
  message(wallMode
818
- ? "Session AR démarrée. Visez un mur (Y=normale, Z du modèle sera vers le plafond)…"
819
  : "Session AR démarrée. Visez le sol pour détecter un plan…");
820
  reticle.enabled = true;
821
  });
 
1
+ /* viewer_ar_ios.js — AR PlayCanvas + GLB/USDZ via config.json (+ "sol" + mode mur : Z = normale, Y = haut)
2
  - config.json : { "glb_url": "...", "usdz_url": "...", "sol": true|false }
3
  - sol = true → plans horizontaux (sol) + rotation autour de l’axe local Y (haut)
4
  - sol = false → plans verticaux (mur) :
5
+ * Z local = normale du mur (perpendiculaire au plan)
6
+ * Y local = direction plafond (Up monde) projetée dans le plan
7
+ * rotation utilisateur = autour de Z local (normale)
8
  * PENDANT une translation : orientation verrouillée (pas de twist parasite)
9
+ * À la fin d’une translation : réalignement base sur le NOUVEAU mur (Z=normale, Y→plafond)
10
  - iOS : AR Quick Look (USDZ)
11
  - Android/Desktop : WebXR AR + bouton "Lancer l’AR" + slider de rotation
12
  */
 
305
  } else {
306
  message(
307
  "Modèle chargé. Appuyez sur « Lancer l’AR ». Sur iOS (Quick Look), l’objet ne peut pas être " +
308
+ "aligné automatiquement au mur : placez-le et orientez-le manuellement (Y vers le plafond)."
 
309
  );
310
  }
311
  } else {
 
396
  var d = v.dot(n);
397
  return new pc.Vec3(v.x - d*n.x, v.y - d*n.y, v.z - d*n.z);
398
  }
399
+ function buildWallBasisFromNormalZ(Nz) {
400
+ // Z local = normale du mur
401
+ var Z = Nz.clone().normalize();
402
+ // Y local = Up projété dans le plan (toujours "vers le plafond" le long du mur)
403
+ var Y = projOnPlane(UP, Z);
404
+ if (Y.lengthSq() < 1e-8) Y = projOnPlane(new pc.Vec3(0,1,0), Z);
405
+ Y.normalize();
406
+ // X = Y × Z (garantit base orthonormée et droite, avec X×Y = Z)
407
+ var X = new pc.Vec3(); X.cross(Y, Z).normalize();
408
+ // Re-orthonormalise Y = Z × X pour éliminer la dérive
409
+ Y.cross(Z, X).normalize();
 
410
  return { X: X, Y: Y, Z: Z };
411
  }
412
  function quatFromBasis(X, Y, Z) {
 
445
  q.normalize();
446
  return q;
447
  }
448
+ function computeWallBaseFromRot_ZNormal(rot) {
449
+ // WebXR/PlayCanvas : la normale du plan est l’axe Z de la pose
450
+ var Nz = new pc.Vec3();
451
+ rot.transformVector(new pc.Vec3(0, 0, 1), Nz); // Z monde de la pose
452
+ Nz.normalize();
453
+ var basis = buildWallBasisFromNormalZ(Nz); // Z=normale, Y=Up projeté
454
+ return quatFromBasis(basis.X, basis.Y, basis.Z);
455
  }
456
 
457
  // Mode mur / sol
458
  var wallMode = !PLACE_ON_FLOOR;
459
+ var wallBaseRot = new pc.Quat(); // base (mur) : Z=normale, Y=up-projeté
460
+ var wallAngleDeg = 0; // rotation utilisateur autour de Z local (normale)
461
 
462
  // Ombre blob (sol uniquement)
463
  var blob = null;
 
514
  return e;
515
  }
516
 
517
+ // Rotation via slider (sol: Euler Y, mur: twist autour de Z local)
518
  var baseEulerX = 0, baseEulerZ = 0;
519
  var rotationYDeg = 0;
520
  function clamp360(d) { return Math.max(0, Math.min(360, d)); }
 
534
  rotationYDeg = y;
535
  modelRoot.setEulerAngles(baseEulerX, y, baseEulerZ);
536
  } else {
537
+ // mur : rotation autour de Z local
538
  wallAngleDeg = y;
539
  var qLocal = new pc.Quat();
540
+ qLocal.setFromAxisAngle(new pc.Vec3(0, 0, 1), y); // rotation locale autour de Z
541
  var qFinal = new pc.Quat();
542
+ qFinal.mul2(wallBaseRot, qLocal); // base (Z=normale, Y=up projeté) puis twist utilisateur
543
  modelRoot.setRotation(qFinal);
544
  }
545
  updateKnobFromY(y);
 
582
 
583
  if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
584
 
585
+ // ----- Détection orientation des plans (utilise Z comme normale) -----
586
+ var TMP_Z = new pc.Vec3(0, 0, 1), TMP_OUT = new pc.Vec3();
587
 
588
  function isHorizontalUpFacing(rot, minDot) {
589
+ // sol : normale (Z) ≈ +Y
590
  minDot = (typeof minDot === "number") ? minDot : 0.75;
591
+ rot.transformVector(TMP_Z, TMP_OUT);
592
  return TMP_OUT.y >= minDot;
593
  }
594
  function isVerticalFacing(rot, maxAbsY) {
595
+ // mur : normale (Z) quasi horizontale → |Z.y| petit
596
  maxAbsY = (typeof maxAbsY === "number") ? maxAbsY : 0.35;
597
+ rot.transformVector(TMP_Z, TMP_OUT);
598
  return Math.abs(TMP_OUT.y) <= maxAbsY;
599
  }
600
  function planeMatchesMode(rot) {
 
693
  applyRotationY(y0);
694
  } else {
695
  // --- MODE MUR (première pose) ---
696
+ wallBaseRot.copy(computeWallBaseFromRot_ZNormal(rot)); // Z=normale, Y=up-projeté
697
  modelRoot.setRotation(wallBaseRot);
698
+ applyRotationY(0); // angle initial = 0 (autour de Z)
699
  }
700
 
701
  placedOnce = true;
702
  rotInput.disabled = false;
703
  message(
704
  wallMode
705
+ ? "Objet placé contre le mur (Z=normale, Y→plafond). Glissez pour déplacer, tournez avec le slider →"
706
  : "Objet placé. Glissez pour déplacer, tournez-le avec le slider →"
707
  );
708
  }
 
712
  });
713
 
714
  // Drag XR continu (déplacements suivants, sans rotation parasite)
715
+ var isDragging = false;
716
+ var dragLockedRot = new pc.Quat();
717
+ var lastHitRot = new pc.Quat();
718
+
719
  app.xr.input.on("add", function (inputSource) {
720
  inputSource.on("selectstart", function () {
721
  if (uiInteracting) return;
 
734
  if (!isDragging) return;
735
  if (!planeMatchesMode(rot)) return;
736
 
737
+ lastHitRot.copy(rot); // garde trace du dernier mur rencontré
738
+ modelRoot.setPosition(pos); // translate uniquement
 
 
 
739
 
740
  if (!wallMode) {
741
  updateBlobPositionUnder(pos, rot);
742
  } else {
743
+ modelRoot.setRotation(dragLockedRot); // aucune rotation pendant le drag
 
744
  }
745
  });
746
 
747
+ // Fin du drag : snap orientation sur le NOUVEAU mur, en conservant l'angle utilisateur
748
  transientSource.once("remove", function () {
749
  isDragging = false;
750
  if (wallMode) {
751
+ wallBaseRot.copy(computeWallBaseFromRot_ZNormal(lastHitRot));
 
 
752
  var qLocal = new pc.Quat();
753
+ qLocal.setFromAxisAngle(new pc.Vec3(0, 0, 1), wallAngleDeg); // rotation autour de Z local
754
  var qFinal = new pc.Quat();
755
  qFinal.mul2(wallBaseRot, qLocal);
756
  modelRoot.setRotation(qFinal);
 
794
  }
795
  });
796
  app.mouse.on("mouseup", function () {
797
+ // (optionnel) faire un snap sur desktop si tu déclenches un hit-test ici.
 
 
 
798
  isDragging = false;
799
  rotateMode = false;
800
  });
 
811
  app.xr.on("start", function () {
812
  if (startBtn) startBtn.style.display = "none";
813
  message(wallMode
814
+ ? "Session AR démarrée. Visez un mur (Z=normale, Y du modèle vers le plafond)…"
815
  : "Session AR démarrée. Visez le sol pour détecter un plan…");
816
  reticle.enabled = true;
817
  });