MikaFil commited on
Commit
687bd6f
·
verified ·
1 Parent(s): 34559ac

Update viewer_ar_ios.js

Browse files
Files changed (1) hide show
  1. viewer_ar_ios.js +58 -48
viewer_ar_ios.js CHANGED
@@ -1,9 +1,10 @@
1
- /* viewer_ar_ios.js — AR PlayCanvas + GLB/USDZ via config.json
2
- - Lit config.json (data-config) => { "glb_url": "...", "usdz_url": "..." }
3
- - iOS : AR Quick Look (USDZ) avec #allowsContentScaling=0 (pas de zoom) + bouton "Lancer l’AR" (centré, déclenche Quick Look direct)
4
- - Android/Desktop : WebXR AR (plans horizontaux uniquement) + bouton "Lancer l’AR" (centré, pas de tap écran) + curseur de rotation (yaw) + ombre blob
5
- - Éclairage PBR par défaut (sans WebXR light estimation)
6
- - Monté dans un conteneur choisi (data-mount="#ar-mount") pour Squarespace
 
7
  */
8
 
9
  (function () {
@@ -111,29 +112,14 @@
111
  // ==========================
112
  // Overlay / UI (styles intégrés)
113
  // ==========================
114
- // - #ar-overlay-root : overlay global (DOM overlay XR + toasts)
115
- // - #ar-start-button : bouton Android/Desktop
116
- // - #ar-ios-quicklook-button : bouton iOS (rel="ar" + <img> requis pour Quick Look)
117
- // - .ar-rotation-panel : panneau rotation
118
- // - #ar-rotation-* : slider yaw
119
-
120
  var css = [
121
- /* Conteneur overlay global */
122
  "#ar-overlay-root{position:fixed;inset:0;z-index:10001;pointer-events:none}",
123
-
124
- /* Toasts */
125
  ".pc-ar-msg{position:fixed;left:50%;transform:translateX(-50%);bottom:16px;z-index:10002;padding:10px 14px;background:rgba(0,0,0,.65);color:#fff;border-radius:12px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.3;text-align:center;max-width:min(90vw,640px);box-shadow:0 6px 20px rgba(0,0,0,.25);backdrop-filter:blur(4px);pointer-events:none}",
126
  ".pc-ar-msg.pc-ar-msg--centerBig{top:50%;bottom:auto;transform:translate(-50%,-50%);font-size:20px;padding:16px 22px;max-width:min(90vw,760px)}",
127
-
128
- /* ----- Bouton Android/Desktop : centré + légèrement plus grand ----- */
129
- "#ar-start-button{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:10003;pointer-events:auto;border:none;border-radius:16px;padding:16px 24px;font-size:18px;line-height:1;background:#000;color:#fff;box-shadow:0 10px 28px rgba(0,0,0,.28);cursor:pointer;min-width:200px;text-align:center}",
130
  "#ar-start-button[disabled]{opacity:.5;cursor:not-allowed}",
131
-
132
- /* ----- Bouton iOS Quick Look : centré + <img> (exigé par Quick Look) ----- */
133
  "#ar-ios-quicklook-button{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:10003;pointer-events:auto;display:inline-block;text-decoration:none}",
134
  "#ar-ios-quicklook-button img{display:block;height:60px;width:auto;box-shadow:0 10px 28px rgba(0,0,0,.28);border-radius:14px}",
135
-
136
- /* ----- Panneau rotation ----- */
137
  ".ar-rotation-panel{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:rgba(0,0,0,0.55);color:#fff;padding:12px 10px;border-radius:16px;width:56px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;pointer-events:auto;display:flex;flex-direction:column;align-items:center;gap:8px;box-shadow:0 6px 18px rgba(0,0,0,.25);backdrop-filter:blur(6px);touch-action:none}",
138
  ".ar-rotation-panel .label{font-size:12px;text-align:center;opacity:.95}",
139
  "#ar-rotation-track{position:relative;width:48px;height:200px;display:flex;align-items:center;justify-content:center;touch-action:none;overflow:visible;pointer-events:auto}",
@@ -183,7 +169,7 @@
183
  }
184
  }
185
 
186
- // iOS : ancre rel=ar + <img> (cest ce format qui déclenche Quick Look direct)
187
  function ensureQuickLookButton(USDZ_URL) {
188
  var btn = document.getElementById("ar-ios-quicklook-button");
189
  if (btn) return btn;
@@ -193,7 +179,6 @@
193
  anchor.setAttribute("rel", "ar");
194
  anchor.setAttribute("href", buildQuickLookHref(USDZ_URL));
195
 
196
- // Bouton visuel en SVG embarqué, libellé "Lancer l’AR"
197
  var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="240" height="60">' +
198
  '<rect rx="14" ry="14" width="240" height="60" fill="black"/>' +
199
  '<text x="120" y="37" font-size="18" text-anchor="middle" fill="white" font-family="system-ui, -apple-system, Segoe UI, Roboto">Lancer l’AR</text>' +
@@ -227,7 +212,7 @@
227
  }
228
 
229
  // ==========================
230
- // Montage du canvas (Squarespace-friendly)
231
  // ==========================
232
  function ensureCanvas() {
233
  var scriptEl = getCurrentScript();
@@ -299,18 +284,23 @@
299
  cfg.usdz_url :
300
  null;
301
 
302
- // iOS Quick Look via ancre rel=ar + <img> (ouverture directe de l’USDZ)
 
 
 
 
303
  if (isIOS()) {
304
  if (USDZ_URL) {
305
  ensureQuickLookButton(USDZ_URL);
306
- message("Modèle chargé. Appuyez sur « Lancer l’AR ».");
 
307
  } else {
308
  message("iOS détecté, mais aucun 'usdz_url' dans config.json.");
309
  }
310
  return;
311
  }
312
 
313
- // Android/Desktop → PlayCanvas/WebXR (bouton centré)
314
  try {
315
  await loadPlayCanvasRobust({ esmFirst: true, loadTimeoutMs: 15000 });
316
  } catch (e) {
@@ -318,13 +308,13 @@
318
  message("Impossible de charger PlayCanvas (réseau/CDN). Réessaie plus tard.");
319
  return;
320
  }
321
- initARApp(GLB_URL);
322
  })();
323
 
324
  // ==========================
325
  // Application WebXR (Android/Desktop)
326
  // ==========================
327
- function initARApp(GLB_URL) {
328
  var pc = window.pc;
329
 
330
  var canvas = ensureCanvas();
@@ -386,7 +376,7 @@
386
  app.root.addChild(modelRoot);
387
  var modelLoaded = false, placedOnce = false;
388
 
389
- // Ombre blob
390
  var blob = null;
391
  var BLOB_SIZE = 0.4;
392
  var BLOB_OFFSET_Y = 0.005;
@@ -496,6 +486,24 @@
496
 
497
  if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
498
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  // Slider (pointer capture)
500
  var uiInteracting = false;
501
  var draggingTrack = false;
@@ -557,24 +565,16 @@
557
  // Bouton start Android/Desktop
558
  var startBtn = ensureARStartButton(activateAR);
559
 
560
- // Pas de démarrage par tap/clic sur le canvas
561
  app.keyboard.on("keydown", function (evt) { if (evt.key === pc.KEY_ESCAPE && app.xr.active) app.xr.end(); });
562
 
563
- // Hit-test (plans quasi-horizontaux)
564
- var TMP_IN = new pc.Vec3(0, 1, 0), TMP_OUT = new pc.Vec3();
565
- function isHorizontalUpFacing(rot, minDot) {
566
- minDot = (typeof minDot === "number") ? minDot : 0.75;
567
- rot.transformVector(TMP_IN, TMP_OUT);
568
- return TMP_OUT.y >= minDot;
569
- }
570
-
571
  app.xr.hitTest.on("available", function () {
572
  app.xr.hitTest.start({
573
  entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
574
  callback: function (err, hitSource) {
575
  if (err) { message("Le AR hit test n’a pas pu démarrer."); return; }
576
  hitSource.on("result", function (pos, rot) {
577
- if (!isHorizontalUpFacing(rot)) return;
578
 
579
  reticle.enabled = true;
580
  reticle.setPosition(pos);
@@ -584,23 +584,31 @@
584
  modelRoot.enabled = true;
585
  modelRoot.setPosition(pos);
586
 
587
- blob = createBlobShadowAt(pos, rot);
 
 
 
588
 
 
589
  var e = new pc.Vec3();
590
  rot.getEulerAngles(e);
591
  var y0 = ((e.y % 360) + 360) % 360;
 
 
592
  applyRotationY(y0);
593
 
594
  placedOnce = true;
595
  rotInput.disabled = false;
596
- message("Objet placé. Glissez pour déplacer, tournez-le avec le slider →");
 
 
597
  }
598
  });
599
  }
600
  });
601
  });
602
 
603
- // Drag XR continu
604
  var isDragging = false;
605
  app.xr.input.on("add", function (inputSource) {
606
  inputSource.on("selectstart", function () {
@@ -615,9 +623,9 @@
615
 
616
  transientSource.on("result", function (pos, rot) {
617
  if (!isDragging) return;
618
- if (!isHorizontalUpFacing(rot)) return;
619
  modelRoot.setPosition(pos);
620
- updateBlobPositionUnder(pos, rot);
621
  });
622
 
623
  transientSource.once("remove", function () { isDragging = false; });
@@ -645,7 +653,7 @@
645
  if (reticle.enabled) {
646
  var p = reticle.getPosition();
647
  modelRoot.setPosition(p);
648
- updateBlobPositionUnder(p, null);
649
  }
650
  } else if (rotateMode && modelRoot.enabled) {
651
  var dx = e.x - lastMouseX;
@@ -667,7 +675,9 @@
667
  // Événements AR (feedback + visibilité bouton)
668
  app.xr.on("start", function () {
669
  if (startBtn) startBtn.style.display = "none";
670
- message("Session AR démarrée. Visez le sol pour détecter un plan…");
 
 
671
  reticle.enabled = true;
672
  });
673
  app.xr.on("end", function () {
 
1
+ /* viewer_ar_ios.js — AR PlayCanvas + GLB/USDZ via config.json (+ support murs via "sol": false)
2
+ - Lit config.json (data-config) => { "glb_url": "...", "usdz_url": "...", "sol": true|false }
3
+ - iOS : AR Quick Look (USDZ) on ne peut PAS forcer mur/sol, mais l’utilisateur peut glisser l’objet sur un mur
4
+ - Android/Desktop : WebXR AR
5
+ * sol=true => plans horizontaux (sol)
6
+ * sol=false => plans verticaux (murs)
7
+ + bouton "Lancer l’AR" (centré) + curseur rotation (yaw)
8
  */
9
 
10
  (function () {
 
112
  // ==========================
113
  // Overlay / UI (styles intégrés)
114
  // ==========================
 
 
 
 
 
 
115
  var css = [
 
116
  "#ar-overlay-root{position:fixed;inset:0;z-index:10001;pointer-events:none}",
 
 
117
  ".pc-ar-msg{position:fixed;left:50%;transform:translateX(-50%);bottom:16px;z-index:10002;padding:10px 14px;background:rgba(0,0,0,.65);color:#fff;border-radius:12px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.3;text-align:center;max-width:min(90vw,640px);box-shadow:0 6px 20px rgba(0,0,0,.25);backdrop-filter:blur(4px);pointer-events:none}",
118
  ".pc-ar-msg.pc-ar-msg--centerBig{top:50%;bottom:auto;transform:translate(-50%,-50%);font-size:20px;padding:16px 22px;max-width:min(90vw,760px)}",
119
+ "#ar-start-button{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:10003;pointer-events:auto;border:none;border-radius:16px;padding:16px 24px;font-size:18px;background:#000;color:#fff;box-shadow:0 10px 28px rgba(0,0,0,.28);cursor:pointer;min-width:200px;text-align:center}",
 
 
120
  "#ar-start-button[disabled]{opacity:.5;cursor:not-allowed}",
 
 
121
  "#ar-ios-quicklook-button{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:10003;pointer-events:auto;display:inline-block;text-decoration:none}",
122
  "#ar-ios-quicklook-button img{display:block;height:60px;width:auto;box-shadow:0 10px 28px rgba(0,0,0,.28);border-radius:14px}",
 
 
123
  ".ar-rotation-panel{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:rgba(0,0,0,0.55);color:#fff;padding:12px 10px;border-radius:16px;width:56px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;pointer-events:auto;display:flex;flex-direction:column;align-items:center;gap:8px;box-shadow:0 6px 18px rgba(0,0,0,.25);backdrop-filter:blur(6px);touch-action:none}",
124
  ".ar-rotation-panel .label{font-size:12px;text-align:center;opacity:.95}",
125
  "#ar-rotation-track{position:relative;width:48px;height:200px;display:flex;align-items:center;justify-content:center;touch-action:none;overflow:visible;pointer-events:auto}",
 
169
  }
170
  }
171
 
172
+ // iOS : bouton Quick Look (lutilisateur peut glisser l’objet sur un mur si souhaité)
173
  function ensureQuickLookButton(USDZ_URL) {
174
  var btn = document.getElementById("ar-ios-quicklook-button");
175
  if (btn) return btn;
 
179
  anchor.setAttribute("rel", "ar");
180
  anchor.setAttribute("href", buildQuickLookHref(USDZ_URL));
181
 
 
182
  var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="240" height="60">' +
183
  '<rect rx="14" ry="14" width="240" height="60" fill="black"/>' +
184
  '<text x="120" y="37" font-size="18" text-anchor="middle" fill="white" font-family="system-ui, -apple-system, Segoe UI, Roboto">Lancer l’AR</text>' +
 
212
  }
213
 
214
  // ==========================
215
+ // Montage du canvas
216
  // ==========================
217
  function ensureCanvas() {
218
  var scriptEl = getCurrentScript();
 
284
  cfg.usdz_url :
285
  null;
286
 
287
+ // NEW: lecture de "sol" (par défaut true)
288
+ var PLACE_ON_FLOOR = true;
289
+ if (cfg && typeof cfg.sol === "boolean") PLACE_ON_FLOOR = !!cfg.sol;
290
+
291
+ // iOS → Quick Look (on ne peut pas imposer mur/sol)
292
  if (isIOS()) {
293
  if (USDZ_URL) {
294
  ensureQuickLookButton(USDZ_URL);
295
+ message("Modèle chargé. Appuyez sur « Lancer l’AR » puis placez-le " +
296
+ (PLACE_ON_FLOOR ? "sur le sol." : "sur un mur (faites-le glisser)."));
297
  } else {
298
  message("iOS détecté, mais aucun 'usdz_url' dans config.json.");
299
  }
300
  return;
301
  }
302
 
303
+ // Android/Desktop → WebXR
304
  try {
305
  await loadPlayCanvasRobust({ esmFirst: true, loadTimeoutMs: 15000 });
306
  } catch (e) {
 
308
  message("Impossible de charger PlayCanvas (réseau/CDN). Réessaie plus tard.");
309
  return;
310
  }
311
+ initARApp(GLB_URL, PLACE_ON_FLOOR);
312
  })();
313
 
314
  // ==========================
315
  // Application WebXR (Android/Desktop)
316
  // ==========================
317
+ function initARApp(GLB_URL, PLACE_ON_FLOOR) {
318
  var pc = window.pc;
319
 
320
  var canvas = ensureCanvas();
 
376
  app.root.addChild(modelRoot);
377
  var modelLoaded = false, placedOnce = false;
378
 
379
+ // Ombre blob (uniquement pour le sol)
380
  var blob = null;
381
  var BLOB_SIZE = 0.4;
382
  var BLOB_OFFSET_Y = 0.005;
 
486
 
487
  if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
488
 
489
+ // ----- Détection d’orientation de plan -----
490
+ var TMP_IN = new pc.Vec3(0, 1, 0), TMP_OUT = new pc.Vec3();
491
+ // Sol : normal ≈ +Y
492
+ function isHorizontalUpFacing(rot, minDot) {
493
+ minDot = (typeof minDot === "number") ? minDot : 0.75;
494
+ rot.transformVector(TMP_IN, TMP_OUT);
495
+ return TMP_OUT.y >= minDot;
496
+ }
497
+ // Mur : normal ≈ horizontale (|y| petit)
498
+ function isVerticalFacing(rot, maxAbsY) {
499
+ maxAbsY = (typeof maxAbsY === "number") ? maxAbsY : 0.35; // tolérance
500
+ rot.transformVector(TMP_IN, TMP_OUT);
501
+ return Math.abs(TMP_OUT.y) <= maxAbsY;
502
+ }
503
+ function planeMatchesMode(rot) {
504
+ return PLACE_ON_FLOOR ? isHorizontalUpFacing(rot) : isVerticalFacing(rot);
505
+ }
506
+
507
  // Slider (pointer capture)
508
  var uiInteracting = false;
509
  var draggingTrack = false;
 
565
  // Bouton start Android/Desktop
566
  var startBtn = ensureARStartButton(activateAR);
567
 
 
568
  app.keyboard.on("keydown", function (evt) { if (evt.key === pc.KEY_ESCAPE && app.xr.active) app.xr.end(); });
569
 
570
+ // Hit-test (sol ⇧ vs mur ⇨)
 
 
 
 
 
 
 
571
  app.xr.hitTest.on("available", function () {
572
  app.xr.hitTest.start({
573
  entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
574
  callback: function (err, hitSource) {
575
  if (err) { message("Le AR hit test n’a pas pu démarrer."); return; }
576
  hitSource.on("result", function (pos, rot) {
577
+ if (!planeMatchesMode(rot)) return;
578
 
579
  reticle.enabled = true;
580
  reticle.setPosition(pos);
 
584
  modelRoot.enabled = true;
585
  modelRoot.setPosition(pos);
586
 
587
+ // Ombre : seulement pour le sol
588
+ if (PLACE_ON_FLOOR) {
589
+ blob = createBlobShadowAt(pos, rot);
590
+ }
591
 
592
+ // Aligner l’orientation “yaw” avec le plan détecté (sol ou mur)
593
  var e = new pc.Vec3();
594
  rot.getEulerAngles(e);
595
  var y0 = ((e.y % 360) + 360) % 360;
596
+ // sur mur : garder l’objet droit (pas d’inclinaison X/Z)
597
+ if (!PLACE_ON_FLOOR) { baseEulerX = 0; baseEulerZ = 0; }
598
  applyRotationY(y0);
599
 
600
  placedOnce = true;
601
  rotInput.disabled = false;
602
+ message(PLACE_ON_FLOOR ?
603
+ "Objet placé. Glissez pour déplacer, tournez-le avec le slider →" :
604
+ "Objet placé contre le mur. Glissez pour déplacer sur le mur, tournez-le avec le slider →");
605
  }
606
  });
607
  }
608
  });
609
  });
610
 
611
+ // Drag XR continu (suivant le mode)
612
  var isDragging = false;
613
  app.xr.input.on("add", function (inputSource) {
614
  inputSource.on("selectstart", function () {
 
623
 
624
  transientSource.on("result", function (pos, rot) {
625
  if (!isDragging) return;
626
+ if (!planeMatchesMode(rot)) return;
627
  modelRoot.setPosition(pos);
628
+ if (PLACE_ON_FLOOR) updateBlobPositionUnder(pos, rot);
629
  });
630
 
631
  transientSource.once("remove", function () { isDragging = false; });
 
653
  if (reticle.enabled) {
654
  var p = reticle.getPosition();
655
  modelRoot.setPosition(p);
656
+ if (PLACE_ON_FLOOR) updateBlobPositionUnder(p, null);
657
  }
658
  } else if (rotateMode && modelRoot.enabled) {
659
  var dx = e.x - lastMouseX;
 
675
  // Événements AR (feedback + visibilité bouton)
676
  app.xr.on("start", function () {
677
  if (startBtn) startBtn.style.display = "none";
678
+ message(PLACE_ON_FLOOR ?
679
+ "Session AR démarrée. Visez le sol pour détecter un plan…" :
680
+ "Session AR démarrée. Visez un mur pour détecter un plan vertical…");
681
  reticle.enabled = true;
682
  });
683
  app.xr.on("end", function () {