Update viewer_ar_ios.js
Browse files- viewer_ar_ios.js +44 -33
viewer_ar_ios.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
| 1 |
-
<script>
|
| 2 |
/* viewer_ar_ios.js — AR PlayCanvas + GLB/USDZ via config.json
|
| 3 |
- Lit config.json (data-config) => { "glb_url": "...", "usdz_url": "..." }
|
| 4 |
- iOS : AR Quick Look (USDZ) avec #allowsContentScaling=0 (pas de zoom)
|
| 5 |
-
- Android/Desktop : WebXR AR (
|
| 6 |
- Éclairage PBR par défaut (sans WebXR light estimation)
|
| 7 |
- Monté dans un conteneur choisi (data-mount="#ar-mount") pour Squarespace
|
| 8 |
*/
|
|
@@ -10,7 +9,7 @@
|
|
| 10 |
(function () {
|
| 11 |
// ============ Utils: script tag / config / platform ============
|
| 12 |
function getCurrentScript() {
|
| 13 |
-
//
|
| 14 |
return document.currentScript || (function () {
|
| 15 |
var scripts = document.getElementsByTagName('script');
|
| 16 |
return scripts[scripts.length - 1] || null;
|
|
@@ -71,7 +70,7 @@
|
|
| 71 |
if (!window.pc) window.pc = ns;
|
| 72 |
return window.pc;
|
| 73 |
}
|
| 74 |
-
} catch (
|
| 75 |
}
|
| 76 |
throw new Error("ESM failed");
|
| 77 |
}
|
|
@@ -92,7 +91,7 @@
|
|
| 92 |
timeout(loadTimeoutMs)
|
| 93 |
]);
|
| 94 |
if (window.pc && window.pc.Application) return window.pc;
|
| 95 |
-
} catch (
|
| 96 |
}
|
| 97 |
throw new Error("UMD failed");
|
| 98 |
}
|
|
@@ -100,15 +99,15 @@
|
|
| 100 |
try {
|
| 101 |
if (esmFirst) return await tryESM();
|
| 102 |
return await tryUMD();
|
| 103 |
-
} catch (
|
| 104 |
if (esmFirst) return await tryUMD();
|
| 105 |
return await tryESM();
|
| 106 |
}
|
| 107 |
}
|
| 108 |
|
| 109 |
// ============ UI / Overlay commun ============
|
| 110 |
-
//
|
| 111 |
-
// tu peux déplacer ces règles dans ce fichier.)
|
| 112 |
var css = [
|
| 113 |
/* Toast (bas) */
|
| 114 |
".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}",
|
|
@@ -145,7 +144,7 @@
|
|
| 145 |
}
|
| 146 |
var overlayRoot = ensureOverlayRoot();
|
| 147 |
|
| 148 |
-
// ------ Système de message : toast (bas)
|
| 149 |
function getMessageEl() {
|
| 150 |
var el = overlayRoot.querySelector(".pc-ar-msg");
|
| 151 |
if (!el) {
|
|
@@ -175,7 +174,7 @@
|
|
| 175 |
params.set('allowsContentScaling', '0');
|
| 176 |
u.hash = params.toString();
|
| 177 |
return u.toString();
|
| 178 |
-
} catch (
|
| 179 |
return usdzUrl + (usdzUrl.indexOf('#') >= 0 ? '&' : '#') + 'allowsContentScaling=0';
|
| 180 |
}
|
| 181 |
}
|
|
@@ -244,7 +243,7 @@
|
|
| 244 |
// Optionnel : ramener le viewport sur le viewer
|
| 245 |
try {
|
| 246 |
mountEl.scrollIntoView({ behavior: 'instant', block: 'start' });
|
| 247 |
-
} catch (
|
| 248 |
|
| 249 |
return canvas;
|
| 250 |
}
|
|
@@ -362,28 +361,29 @@
|
|
| 362 |
modelRoot.enabled = false;
|
| 363 |
app.root.addChild(modelRoot);
|
| 364 |
|
| 365 |
-
var modelLoaded = false
|
|
|
|
| 366 |
|
| 367 |
// ===== Ombre de contact “blob” =====
|
| 368 |
var blobShadowEntity = null;
|
| 369 |
var BLOB_SIZE = 0.4;
|
| 370 |
var BLOB_OFFSET_Y = 0.005;
|
| 371 |
|
| 372 |
-
function makeBlobTexture(
|
| 373 |
-
|
| 374 |
var cvs = document.createElement('canvas');
|
| 375 |
-
cvs.width = cvs.height =
|
| 376 |
var ctx = cvs.getContext('2d');
|
| 377 |
-
var r =
|
| 378 |
-
var grd = ctx.createRadialGradient(
|
| 379 |
grd.addColorStop(0, 'rgba(0,0,0,0.5)');
|
| 380 |
grd.addColorStop(1, 'rgba(0,0,0,0.0)');
|
| 381 |
ctx.fillStyle = grd;
|
| 382 |
-
ctx.fillRect(0, 0,
|
| 383 |
|
| 384 |
-
var tex = new pc.Texture(
|
| 385 |
-
width:
|
| 386 |
-
height:
|
| 387 |
format: pc.PIXELFORMAT_R8_G8_B8_A8,
|
| 388 |
mipmaps: true,
|
| 389 |
magFilter: pc.FILTER_LINEAR,
|
|
@@ -420,7 +420,8 @@
|
|
| 420 |
}
|
| 421 |
|
| 422 |
// Euler de base (évite inversions)
|
| 423 |
-
var baseEulerX = 0
|
|
|
|
| 424 |
|
| 425 |
// Rotation via slider (0..360, 360 en haut / 0 en bas)
|
| 426 |
var rotationYDeg = 0;
|
|
@@ -473,7 +474,7 @@
|
|
| 473 |
|
| 474 |
modelLoaded = true;
|
| 475 |
|
| 476 |
-
// >>>
|
| 477 |
messageCenterBig("Modèle chargé. Touchez l’écran pour démarrer l’AR.");
|
| 478 |
});
|
| 479 |
|
|
@@ -490,7 +491,7 @@
|
|
| 490 |
var y = (e.clientY != null) ? e.clientY : ((e.touches && e.touches[0] && e.touches[0].clientY) || 0);
|
| 491 |
var ratio = (y - rect.top) / rect.height;
|
| 492 |
var t = Math.max(0, Math.min(1, ratio));
|
| 493 |
-
return (1 - t) * 360;
|
| 494 |
}
|
| 495 |
|
| 496 |
function onPointerDownCapture(e) {
|
|
@@ -499,7 +500,7 @@
|
|
| 499 |
isTrackDragging = true;
|
| 500 |
activePointerId = (e.pointerId != null) ? e.pointerId : 1;
|
| 501 |
if (rotTrack.setPointerCapture) {
|
| 502 |
-
try { rotTrack.setPointerCapture(activePointerId); } catch (
|
| 503 |
}
|
| 504 |
applyRotationY(degFromPointer(e));
|
| 505 |
e.preventDefault();
|
|
@@ -516,7 +517,7 @@
|
|
| 516 |
isTrackDragging = false;
|
| 517 |
isUIInteracting = false;
|
| 518 |
if (rotTrack.releasePointerCapture) {
|
| 519 |
-
try { rotTrack.releasePointerCapture(activePointerId); } catch (
|
| 520 |
}
|
| 521 |
activePointerId = null;
|
| 522 |
e.preventDefault();
|
|
@@ -556,11 +557,12 @@
|
|
| 556 |
app.keyboard.on("keydown", function (evt) { if (evt.key === pc.KEY_ESCAPE && app.xr.active) app.xr.end(); });
|
| 557 |
|
| 558 |
// Hit-test HORIZONTAL uniquement
|
| 559 |
-
var TMP_IN = new pc.Vec3(0, 1, 0)
|
|
|
|
| 560 |
function isHorizontalUpFacing(rot, minDot) {
|
| 561 |
-
|
| 562 |
rot.transformVector(TMP_IN, TMP_OUT);
|
| 563 |
-
return TMP_OUT.y >=
|
| 564 |
}
|
| 565 |
|
| 566 |
// Hit Test global
|
|
@@ -627,7 +629,8 @@
|
|
| 627 |
});
|
| 628 |
|
| 629 |
// Desktop : rotation souris (ignore si UI)
|
| 630 |
-
var isRotateMode = false
|
|
|
|
| 631 |
var ROTATE_SENSITIVITY = 0.25;
|
| 632 |
app.mouse.on("mousedown", function (e) {
|
| 633 |
if (!app.xr.active || !placedOnce || isUIInteracting) return;
|
|
@@ -664,8 +667,17 @@
|
|
| 664 |
}, { passive: true });
|
| 665 |
|
| 666 |
// AR events
|
| 667 |
-
app.xr.on("start", function () {
|
| 668 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 669 |
app.xr.on("available:" + pc.XRTYPE_AR, function (a) {
|
| 670 |
if (!a) messageToast("AR immersive indisponible.");
|
| 671 |
else if (!app.xr.hitTest.supported) messageToast("AR Hit Test non supporté.");
|
|
@@ -677,4 +689,3 @@
|
|
| 677 |
else messageToast("Chargement du modèle…");
|
| 678 |
}
|
| 679 |
})();
|
| 680 |
-
</script>
|
|
|
|
|
|
|
| 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)
|
| 4 |
+
- Android/Desktop : WebXR AR (plans HORIZONTAUX uniquement) + slider yaw + blob shadow
|
| 5 |
- Éclairage PBR par défaut (sans WebXR light estimation)
|
| 6 |
- Monté dans un conteneur choisi (data-mount="#ar-mount") pour Squarespace
|
| 7 |
*/
|
|
|
|
| 9 |
(function () {
|
| 10 |
// ============ Utils: script tag / config / platform ============
|
| 11 |
function getCurrentScript() {
|
| 12 |
+
// Robuste même si document.currentScript n'est pas dispo
|
| 13 |
return document.currentScript || (function () {
|
| 14 |
var scripts = document.getElementsByTagName('script');
|
| 15 |
return scripts[scripts.length - 1] || null;
|
|
|
|
| 70 |
if (!window.pc) window.pc = ns;
|
| 71 |
return window.pc;
|
| 72 |
}
|
| 73 |
+
} catch (_e) { /* continue */ }
|
| 74 |
}
|
| 75 |
throw new Error("ESM failed");
|
| 76 |
}
|
|
|
|
| 91 |
timeout(loadTimeoutMs)
|
| 92 |
]);
|
| 93 |
if (window.pc && window.pc.Application) return window.pc;
|
| 94 |
+
} catch (_e) { /* continue */ }
|
| 95 |
}
|
| 96 |
throw new Error("UMD failed");
|
| 97 |
}
|
|
|
|
| 99 |
try {
|
| 100 |
if (esmFirst) return await tryESM();
|
| 101 |
return await tryUMD();
|
| 102 |
+
} catch (_e) {
|
| 103 |
if (esmFirst) return await tryUMD();
|
| 104 |
return await tryESM();
|
| 105 |
}
|
| 106 |
}
|
| 107 |
|
| 108 |
// ============ UI / Overlay commun ============
|
| 109 |
+
// Style minimal intégré : toasts + variante centrée pour le message initial.
|
| 110 |
+
// (Si tu utilises un CSS externe, tu peux déplacer ces règles dans ce fichier.)
|
| 111 |
var css = [
|
| 112 |
/* Toast (bas) */
|
| 113 |
".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}",
|
|
|
|
| 144 |
}
|
| 145 |
var overlayRoot = ensureOverlayRoot();
|
| 146 |
|
| 147 |
+
// ------ Système de message : toast (bas) OU centré agrandi ------
|
| 148 |
function getMessageEl() {
|
| 149 |
var el = overlayRoot.querySelector(".pc-ar-msg");
|
| 150 |
if (!el) {
|
|
|
|
| 174 |
params.set('allowsContentScaling', '0');
|
| 175 |
u.hash = params.toString();
|
| 176 |
return u.toString();
|
| 177 |
+
} catch (_e) {
|
| 178 |
return usdzUrl + (usdzUrl.indexOf('#') >= 0 ? '&' : '#') + 'allowsContentScaling=0';
|
| 179 |
}
|
| 180 |
}
|
|
|
|
| 243 |
// Optionnel : ramener le viewport sur le viewer
|
| 244 |
try {
|
| 245 |
mountEl.scrollIntoView({ behavior: 'instant', block: 'start' });
|
| 246 |
+
} catch (_e) {}
|
| 247 |
|
| 248 |
return canvas;
|
| 249 |
}
|
|
|
|
| 361 |
modelRoot.enabled = false;
|
| 362 |
app.root.addChild(modelRoot);
|
| 363 |
|
| 364 |
+
var modelLoaded = false;
|
| 365 |
+
var placedOnce = false;
|
| 366 |
|
| 367 |
// ===== Ombre de contact “blob” =====
|
| 368 |
var blobShadowEntity = null;
|
| 369 |
var BLOB_SIZE = 0.4;
|
| 370 |
var BLOB_OFFSET_Y = 0.005;
|
| 371 |
|
| 372 |
+
function makeBlobTexture(appRef, size) {
|
| 373 |
+
var s = size || 256;
|
| 374 |
var cvs = document.createElement('canvas');
|
| 375 |
+
cvs.width = cvs.height = s;
|
| 376 |
var ctx = cvs.getContext('2d');
|
| 377 |
+
var r = s * 0.45;
|
| 378 |
+
var grd = ctx.createRadialGradient(s / 2, s / 2, r * 0.2, s / 2, s / 2, r);
|
| 379 |
grd.addColorStop(0, 'rgba(0,0,0,0.5)');
|
| 380 |
grd.addColorStop(1, 'rgba(0,0,0,0.0)');
|
| 381 |
ctx.fillStyle = grd;
|
| 382 |
+
ctx.fillRect(0, 0, s, s);
|
| 383 |
|
| 384 |
+
var tex = new pc.Texture(appRef.graphicsDevice, {
|
| 385 |
+
width: s,
|
| 386 |
+
height: s,
|
| 387 |
format: pc.PIXELFORMAT_R8_G8_B8_A8,
|
| 388 |
mipmaps: true,
|
| 389 |
magFilter: pc.FILTER_LINEAR,
|
|
|
|
| 420 |
}
|
| 421 |
|
| 422 |
// Euler de base (évite inversions)
|
| 423 |
+
var baseEulerX = 0;
|
| 424 |
+
var baseEulerZ = 0;
|
| 425 |
|
| 426 |
// Rotation via slider (0..360, 360 en haut / 0 en bas)
|
| 427 |
var rotationYDeg = 0;
|
|
|
|
| 474 |
|
| 475 |
modelLoaded = true;
|
| 476 |
|
| 477 |
+
// >>> Message centré & un peu plus grand pour l’invite initiale
|
| 478 |
messageCenterBig("Modèle chargé. Touchez l’écran pour démarrer l’AR.");
|
| 479 |
});
|
| 480 |
|
|
|
|
| 491 |
var y = (e.clientY != null) ? e.clientY : ((e.touches && e.touches[0] && e.touches[0].clientY) || 0);
|
| 492 |
var ratio = (y - rect.top) / rect.height;
|
| 493 |
var t = Math.max(0, Math.min(1, ratio));
|
| 494 |
+
return (1 - t) * 360; // 360 en haut, 0 en bas
|
| 495 |
}
|
| 496 |
|
| 497 |
function onPointerDownCapture(e) {
|
|
|
|
| 500 |
isTrackDragging = true;
|
| 501 |
activePointerId = (e.pointerId != null) ? e.pointerId : 1;
|
| 502 |
if (rotTrack.setPointerCapture) {
|
| 503 |
+
try { rotTrack.setPointerCapture(activePointerId); } catch (_er) {}
|
| 504 |
}
|
| 505 |
applyRotationY(degFromPointer(e));
|
| 506 |
e.preventDefault();
|
|
|
|
| 517 |
isTrackDragging = false;
|
| 518 |
isUIInteracting = false;
|
| 519 |
if (rotTrack.releasePointerCapture) {
|
| 520 |
+
try { rotTrack.releasePointerCapture(activePointerId); } catch (_er) {}
|
| 521 |
}
|
| 522 |
activePointerId = null;
|
| 523 |
e.preventDefault();
|
|
|
|
| 557 |
app.keyboard.on("keydown", function (evt) { if (evt.key === pc.KEY_ESCAPE && app.xr.active) app.xr.end(); });
|
| 558 |
|
| 559 |
// Hit-test HORIZONTAL uniquement
|
| 560 |
+
var TMP_IN = new pc.Vec3(0, 1, 0);
|
| 561 |
+
var TMP_OUT = new pc.Vec3();
|
| 562 |
function isHorizontalUpFacing(rot, minDot) {
|
| 563 |
+
var md = (typeof minDot === "number") ? minDot : 0.75;
|
| 564 |
rot.transformVector(TMP_IN, TMP_OUT);
|
| 565 |
+
return TMP_OUT.y >= md; // normale proche de +Y
|
| 566 |
}
|
| 567 |
|
| 568 |
// Hit Test global
|
|
|
|
| 629 |
});
|
| 630 |
|
| 631 |
// Desktop : rotation souris (ignore si UI)
|
| 632 |
+
var isRotateMode = false;
|
| 633 |
+
var lastMouseX = 0;
|
| 634 |
var ROTATE_SENSITIVITY = 0.25;
|
| 635 |
app.mouse.on("mousedown", function (e) {
|
| 636 |
if (!app.xr.active || !placedOnce || isUIInteracting) return;
|
|
|
|
| 667 |
}, { passive: true });
|
| 668 |
|
| 669 |
// AR events
|
| 670 |
+
app.xr.on("start", function () {
|
| 671 |
+
messageToast("Session AR démarrée. Visez le sol pour détecter un plan…");
|
| 672 |
+
reticle.enabled = true;
|
| 673 |
+
});
|
| 674 |
+
app.xr.on("end", function () {
|
| 675 |
+
messageToast("Session AR terminée.");
|
| 676 |
+
reticle.enabled = false;
|
| 677 |
+
isModelDragging = false;
|
| 678 |
+
isRotateMode = false;
|
| 679 |
+
rotRangeInput.disabled = true;
|
| 680 |
+
});
|
| 681 |
app.xr.on("available:" + pc.XRTYPE_AR, function (a) {
|
| 682 |
if (!a) messageToast("AR immersive indisponible.");
|
| 683 |
else if (!app.xr.hitTest.supported) messageToast("AR Hit Test non supporté.");
|
|
|
|
| 689 |
else messageToast("Chargement du modèle…");
|
| 690 |
}
|
| 691 |
})();
|
|
|