MikaFil commited on
Commit
2cb73f7
·
verified ·
1 Parent(s): 461037d

Update viewer_ar_ios.js

Browse files
Files changed (1) hide show
  1. viewer_ar_ios.js +79 -43
viewer_ar_ios.js CHANGED
@@ -1,10 +1,9 @@
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 () {
@@ -116,7 +115,7 @@
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}",
@@ -169,7 +168,7 @@
169
  }
170
  }
171
 
172
- // iOS : bouton Quick Look (l’utilisateur 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;
@@ -284,16 +283,18 @@
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
  }
@@ -376,7 +377,12 @@
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;
@@ -434,6 +440,7 @@
434
  // Rotation via slider
435
  var baseEulerX = 0, baseEulerZ = 0;
436
  var rotationYDeg = 0;
 
437
  function clamp360(d) { return Math.max(0, Math.min(360, d)); }
438
 
439
  function updateKnobFromY(yDeg) {
@@ -442,10 +449,30 @@
442
  rotInput.value = String(Math.round(yDeg));
443
  rotVal.textContent = String(Math.round(yDeg)) + "°";
444
  }
 
 
 
 
 
445
  function applyRotationY(deg) {
446
  var y = clamp360(deg);
447
- rotationYDeg = y;
448
- modelRoot.setEulerAngles(baseEulerX, y, baseEulerZ);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
  updateKnobFromY(y);
450
  }
451
 
@@ -486,22 +513,25 @@
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)
@@ -562,12 +592,11 @@
562
  });
563
  }
564
 
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],
@@ -584,31 +613,37 @@
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 () {
@@ -625,7 +660,7 @@
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,12 +688,12 @@
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;
660
  lastMouseX = e.x;
661
- applyRotationY(rotationYDeg + dx * ROTATE_SENSITIVITY);
662
  }
663
  });
664
  app.mouse.on("mouseup", function () { isDragging = false; rotateMode = false; });
@@ -667,7 +702,6 @@
667
  // Slider (accessibilité clavier)
668
  rotInput.disabled = true;
669
  rotInput.addEventListener("input", function (e) {
670
- if (!modelRoot.enabled) return;
671
  var v = parseFloat(e.target.value || "0");
672
  applyRotationY(v);
673
  }, { passive: true });
@@ -675,9 +709,11 @@
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 () {
 
1
+ /* viewer_ar_ios.js — AR PlayCanvas + GLB/USDZ via config.json (+ "sol")
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) + rotation autour de l’axe local Y aligné à la normale du mur
5
+ - iOS : AR Quick Look (USDZ) — l’utilisateur place l’objet sur sol/mur
6
+ - Android/Desktop : WebXR AR + bouton "Lancer l’AR" + slider de rotation + ombre blob (sol uniquement)
 
7
  */
8
 
9
  (function () {
 
115
  "#ar-overlay-root{position:fixed;inset:0;z-index:10001;pointer-events:none}",
116
  ".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}",
117
  ".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)}",
118
+ "#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}",
119
  "#ar-start-button[disabled]{opacity:.5;cursor:not-allowed}",
120
  "#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}",
121
  "#ar-ios-quicklook-button img{display:block;height:60px;width:auto;box-shadow:0 10px 28px rgba(0,0,0,.28);border-radius:14px}",
 
168
  }
169
  }
170
 
171
+ // iOS : bouton Quick Look
172
  function ensureQuickLookButton(USDZ_URL) {
173
  var btn = document.getElementById("ar-ios-quicklook-button");
174
  if (btn) return btn;
 
283
  cfg.usdz_url :
284
  null;
285
 
286
+ // lecture de "sol" (par défaut true)
287
  var PLACE_ON_FLOOR = true;
288
  if (cfg && typeof cfg.sol === "boolean") PLACE_ON_FLOOR = !!cfg.sol;
289
 
290
+ // iOS → Quick Look
291
  if (isIOS()) {
292
  if (USDZ_URL) {
293
  ensureQuickLookButton(USDZ_URL);
294
+ message(
295
+ "Modèle chargé. Appuyez sur « Lancer l’AR » puis placez-le " +
296
+ (PLACE_ON_FLOOR ? "sur le sol." : "contre un mur.")
297
+ );
298
  } else {
299
  message("iOS détecté, mais aucun 'usdz_url' dans config.json.");
300
  }
 
377
  app.root.addChild(modelRoot);
378
  var modelLoaded = false, placedOnce = false;
379
 
380
+ // Mode mur / sol
381
+ var wallMode = !PLACE_ON_FLOOR;
382
+ var wallBaseRot = new pc.Quat(); // orientation de base alignée au mur
383
+ var wallAngleDeg = 0;
384
+
385
+ // Ombre blob (sol uniquement)
386
  var blob = null;
387
  var BLOB_SIZE = 0.4;
388
  var BLOB_OFFSET_Y = 0.005;
 
440
  // Rotation via slider
441
  var baseEulerX = 0, baseEulerZ = 0;
442
  var rotationYDeg = 0;
443
+
444
  function clamp360(d) { return Math.max(0, Math.min(360, d)); }
445
 
446
  function updateKnobFromY(yDeg) {
 
449
  rotInput.value = String(Math.round(yDeg));
450
  rotVal.textContent = String(Math.round(yDeg)) + "°";
451
  }
452
+
453
+ function getCurrentAngle() {
454
+ return wallMode ? wallAngleDeg : rotationYDeg;
455
+ }
456
+
457
  function applyRotationY(deg) {
458
  var y = clamp360(deg);
459
+ if (!modelRoot.enabled) {
460
+ updateKnobFromY(y);
461
+ return;
462
+ }
463
+
464
+ if (!wallMode) {
465
+ rotationYDeg = y;
466
+ modelRoot.setEulerAngles(baseEulerX, y, baseEulerZ);
467
+ } else {
468
+ wallAngleDeg = y;
469
+ var qLocal = new pc.Quat();
470
+ qLocal.setFromAxisAngle(new pc.Vec3(0, 1, 0), y); // rotation locale autour de Y
471
+ var qFinal = new pc.Quat();
472
+ qFinal.mul2(wallBaseRot, qLocal); // base (mur) puis rotation locale Y
473
+ modelRoot.setRotation(qFinal);
474
+ }
475
+
476
  updateKnobFromY(y);
477
  }
478
 
 
513
 
514
  if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
515
 
516
+ // ----- Détection orientation des plans -----
517
  var TMP_IN = new pc.Vec3(0, 1, 0), TMP_OUT = new pc.Vec3();
518
+
519
  // Sol : normal ≈ +Y
520
  function isHorizontalUpFacing(rot, minDot) {
521
  minDot = (typeof minDot === "number") ? minDot : 0.75;
522
  rot.transformVector(TMP_IN, TMP_OUT);
523
  return TMP_OUT.y >= minDot;
524
  }
525
+
526
+ // Mur : normal horizontale → axe Y du hit-test presque horizontal
527
  function isVerticalFacing(rot, maxAbsY) {
528
+ maxAbsY = (typeof maxAbsY === "number") ? maxAbsY : 0.35;
529
  rot.transformVector(TMP_IN, TMP_OUT);
530
  return Math.abs(TMP_OUT.y) <= maxAbsY;
531
  }
532
+
533
  function planeMatchesMode(rot) {
534
+ return wallMode ? isVerticalFacing(rot) : isHorizontalUpFacing(rot);
535
  }
536
 
537
  // Slider (pointer capture)
 
592
  });
593
  }
594
 
 
595
  var startBtn = ensureARStartButton(activateAR);
596
 
597
  app.keyboard.on("keydown", function (evt) { if (evt.key === pc.KEY_ESCAPE && app.xr.active) app.xr.end(); });
598
 
599
+ // Hit-test (sol vs mur)
600
  app.xr.hitTest.on("available", function () {
601
  app.xr.hitTest.start({
602
  entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
 
613
  modelRoot.enabled = true;
614
  modelRoot.setPosition(pos);
615
 
616
+ if (!wallMode) {
617
+ // --- MODE SOL ---
618
  blob = createBlobShadowAt(pos, rot);
 
619
 
620
+ var e = new pc.Vec3();
621
+ rot.getEulerAngles(e);
622
+ var y0 = ((e.y % 360) + 360) % 360;
623
+ applyRotationY(y0);
624
+ } else {
625
+ // --- MODE MUR ---
626
+ // on aligne directement l’objet sur la rotation du hit-test
627
+ // (axe local Y du modèle = normale du mur)
628
+ modelRoot.setRotation(rot);
629
+ wallBaseRot.copy(rot);
630
+ applyRotationY(0); // angle initial = 0
631
+ }
632
 
633
  placedOnce = true;
634
  rotInput.disabled = false;
635
+ message(
636
+ wallMode
637
+ ? "Objet placé contre le mur. Glissez pour déplacer sur le mur, tournez-le avec le slider →"
638
+ : "Objet placé. Glissez pour déplacer, tournez-le avec le slider →"
639
+ );
640
  }
641
  });
642
  }
643
  });
644
  });
645
 
646
+ // Drag XR continu
647
  var isDragging = false;
648
  app.xr.input.on("add", function (inputSource) {
649
  inputSource.on("selectstart", function () {
 
660
  if (!isDragging) return;
661
  if (!planeMatchesMode(rot)) return;
662
  modelRoot.setPosition(pos);
663
+ if (!wallMode) updateBlobPositionUnder(pos, rot);
664
  });
665
 
666
  transientSource.once("remove", function () { isDragging = false; });
 
688
  if (reticle.enabled) {
689
  var p = reticle.getPosition();
690
  modelRoot.setPosition(p);
691
+ if (!wallMode) updateBlobPositionUnder(p, null);
692
  }
693
  } else if (rotateMode && modelRoot.enabled) {
694
  var dx = e.x - lastMouseX;
695
  lastMouseX = e.x;
696
+ applyRotationY(getCurrentAngle() + dx * ROTATE_SENSITIVITY);
697
  }
698
  });
699
  app.mouse.on("mouseup", function () { isDragging = false; rotateMode = false; });
 
702
  // Slider (accessibilité clavier)
703
  rotInput.disabled = true;
704
  rotInput.addEventListener("input", function (e) {
 
705
  var v = parseFloat(e.target.value || "0");
706
  applyRotationY(v);
707
  }, { passive: true });
 
709
  // Événements AR (feedback + visibilité bouton)
710
  app.xr.on("start", function () {
711
  if (startBtn) startBtn.style.display = "none";
712
+ message(
713
+ wallMode
714
+ ? "Session AR démarrée. Visez un mur pour détecter un plan vertical…"
715
+ : "Session AR démarrée. Visez le sol pour détecter un plan…"
716
+ );
717
  reticle.enabled = true;
718
  });
719
  app.xr.on("end", function () {