MikaFil commited on
Commit
1877689
·
verified ·
1 Parent(s): 38a6e6a

Update viewer_ar_ios.js

Browse files
Files changed (1) hide show
  1. viewer_ar_ios.js +43 -32
viewer_ar_ios.js CHANGED
@@ -4,7 +4,7 @@
4
  - Android/Desktop : WebXR AR (plans HORIZONTAUX uniquement) + slider yaw + blob shadow
5
  - Éclairage PBR par défaut (sans WebXR light estimation)
6
  - Bouton "Lancer l’AR" (Android & iOS)
7
- - CSS EXTERNE via data-css (ou fallback), injecté AVANT création de l’UI
8
  - Montable dans un conteneur dédié via data-mount et data-height (Squarespace-friendly)
9
  */
10
 
@@ -17,20 +17,34 @@
17
  })();
18
  }
19
 
20
- // Injecte un <link rel="stylesheet"> dans CE document (évite doublons)
21
- function ensureCssLink(href) {
22
- if (!href) return;
23
- var links = document.querySelectorAll('link[rel="stylesheet"]');
24
- for (var i = 0; i < links.length; i++) {
25
- try {
26
- if (new URL(links[i].href).href === new URL(href, document.baseURI).href) return;
27
- } catch (_) {}
28
- if (links[i].href === href) return;
29
- }
30
- var linkEl = document.createElement('link');
31
- linkEl.rel = 'stylesheet';
32
- linkEl.href = href;
33
- document.head.appendChild(linkEl);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  }
35
 
36
  function findConfigUrl() {
@@ -204,7 +218,6 @@
204
  btn.id = 'ar-launch-btn';
205
  btn.type = 'button';
206
  btn.textContent = 'Lancer l’AR';
207
- // Évite que le clic déclenche d’autres gestuelles
208
  btn.addEventListener('pointerdown', function (e) { e.stopPropagation(); }, { passive: true });
209
  btn.addEventListener('click', function (e) { e.stopPropagation(); }, false);
210
  document.body.appendChild(btn);
@@ -223,15 +236,16 @@
223
  return { btn: btn, anchor: anchor || null };
224
  }
225
 
226
- // ========== Boot : charge CSS, config, branche iOS/Android ==========
227
  (async function () {
228
- // A) CSS externe depuis data-css (ou valeur par défaut)
229
  var scriptEl = getCurrentScript();
230
  var cssHref = (scriptEl && scriptEl.getAttribute('data-css'))
231
  || "https://huggingface.co/spaces/MikaFil/VR/resolve/main/css/style.css";
232
- ensureCssLink(cssHref);
233
 
234
- // B) Config.json
 
 
 
235
  var cfgUrl = findConfigUrl();
236
  var cfg = await loadConfigJson(cfgUrl);
237
  var GLB_URL = (cfg && typeof cfg.glb_url === "string" && cfg.glb_url)
@@ -239,12 +253,12 @@
239
  : "https://huggingface.co/datasets/MikaFil/viewer_gs/resolve/main/AR/tests/danae_no_metallic.glb";
240
  var USDZ_URL = (cfg && typeof cfg.usdz_url === "string" && cfg.usdz_url) ? cfg.usdz_url : null;
241
 
242
- // C) Bouton + (iOS) ancre Quick Look
243
  var controls = ensureLaunchControls(USDZ_URL);
244
  var launchBtn = controls.btn;
245
  var iosAnchor = controls.anchor;
246
 
247
- // D) iOS → Quick Look (pas de WebXR)
248
  if (isIOS()) {
249
  if (USDZ_URL) {
250
  message("iOS détecté : appuyez sur « Lancer l’AR » pour ouvrir Quick Look.");
@@ -259,7 +273,7 @@
259
  return;
260
  }
261
 
262
- // E) Android/Desktop → PlayCanvas
263
  try {
264
  await loadPlayCanvasRobust({ esmFirst: true, loadTimeoutMs: 15000 });
265
  } catch (e) {
@@ -340,7 +354,7 @@
340
  app.root.addChild(modelRoot);
341
  var modelLoaded = false, placedOnce = false;
342
 
343
- // ===== Blob Shadow (ombre de contact peinte) =====
344
  var blob = null;
345
  var BLOB_SIZE = 0.4;
346
  var BLOB_OFFSET_Y = 0.005;
@@ -424,7 +438,6 @@
424
  modelRoot.addChild(instance);
425
  modelRoot.setLocalScale(1, 1, 1);
426
 
427
- // Petits ajustements matériaux (éviter rendu trop sombre sans IBL)
428
  var renders = instance.findComponents('render');
429
  for (var ri = 0; ri < renders.length; ri++) {
430
  var r = renders[ri];
@@ -449,7 +462,7 @@
449
 
450
  if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
451
 
452
- // Slider fiable : pointer events en capture (et blocage des interactions scène)
453
  var uiInteracting = false, draggingWrap = false, activePointerId = null;
454
  function insideWrap(target) { return rotWrap.contains(target); }
455
  function degFromPointer(e) {
@@ -483,7 +496,7 @@
483
  document.addEventListener("pointerup", endDrag, { capture: true, passive: false });
484
  document.addEventListener("pointercancel", endDrag, { capture: true, passive: false });
485
 
486
- // Démarrage AR (exposé pour le bouton "Lancer l’AR")
487
  function activateAR() {
488
  if (!app.xr.isAvailable(pc.XRTYPE_AR)) { message("AR immersive indisponible sur cet appareil."); return; }
489
  if (!app.xr.domOverlay) app.xr.domOverlay = {};
@@ -508,7 +521,7 @@
508
  }
509
  app.keyboard.on("keydown", function (evt) { if (evt.key === pc.KEY_ESCAPE && app.xr.active) app.xr.end(); });
510
 
511
- // Hit-test HORIZONTAL uniquement (normale proche +Y)
512
  var TMP_IN = new pc.Vec3(0, 1, 0), TMP_OUT = new pc.Vec3();
513
  function isHorizontalUpFacing(rot, minDot) {
514
  minDot = (typeof minDot === "number") ? minDot : 0.75;
@@ -516,7 +529,6 @@
516
  return TMP_OUT.y >= minDot;
517
  }
518
 
519
- // Hit Test global
520
  app.xr.hitTest.on("available", function () {
521
  app.xr.hitTest.start({
522
  entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
@@ -532,7 +544,6 @@
532
  modelRoot.enabled = true;
533
  modelRoot.setPosition(pos);
534
 
535
- // Ombre de contact
536
  blob = createBlobShadowAt(pos, rot);
537
 
538
  var e = new pc.Vec3(); rot.getEulerAngles(e);
@@ -548,7 +559,7 @@
548
  });
549
  });
550
 
551
- // Déplacement XR (drag) — ignoré si UI active
552
  var isDragging = false;
553
  app.xr.input.on("add", function (inputSource) {
554
  inputSource.on("selectstart", function () {
@@ -575,7 +586,7 @@
575
  inputSource.on("selectend", function () { isDragging = false; });
576
  });
577
 
578
- // Desktop : rotation à la souris (clic droit ou Shift+clic gauche)
579
  var rotateMode = false, lastMouseX = 0;
580
  var ROTATE_SENSITIVITY = 0.25;
581
  app.mouse.on("mousedown", function (e) {
 
4
  - Android/Desktop : WebXR AR (plans HORIZONTAUX uniquement) + slider yaw + blob shadow
5
  - Éclairage PBR par défaut (sans WebXR light estimation)
6
  - Bouton "Lancer l’AR" (Android & iOS)
7
+ - CSS EXTERNE via data-css, **attendue avant création de l’UI**
8
  - Montable dans un conteneur dédié via data-mount et data-height (Squarespace-friendly)
9
  */
10
 
 
17
  })();
18
  }
19
 
20
+ // Injecte <link rel="stylesheet"> et **attend** son chargement
21
+ function ensureCssLinkAsync(href) {
22
+ return new Promise(function (resolve) {
23
+ if (!href) return resolve();
24
+
25
+ // Déjà injectée ?
26
+ var links = document.querySelectorAll('link[rel="stylesheet"]');
27
+ for (var i = 0; i < links.length; i++) {
28
+ try {
29
+ if (new URL(links[i].href, document.baseURI).href === new URL(href, document.baseURI).href) {
30
+ // Déjà présente : on considère chargée
31
+ return resolve();
32
+ }
33
+ } catch (_) {
34
+ if (links[i].href === href) return resolve();
35
+ }
36
+ }
37
+
38
+ var linkEl = document.createElement('link');
39
+ linkEl.rel = 'stylesheet';
40
+ linkEl.href = href;
41
+ linkEl.onload = function () { resolve(); };
42
+ linkEl.onerror = function () {
43
+ console.warn('[viewer_ar_ios] CSS failed to load:', href);
44
+ resolve(); // on continue quand même (pas de blocage)
45
+ };
46
+ document.head.appendChild(linkEl);
47
+ });
48
  }
49
 
50
  function findConfigUrl() {
 
218
  btn.id = 'ar-launch-btn';
219
  btn.type = 'button';
220
  btn.textContent = 'Lancer l’AR';
 
221
  btn.addEventListener('pointerdown', function (e) { e.stopPropagation(); }, { passive: true });
222
  btn.addEventListener('click', function (e) { e.stopPropagation(); }, false);
223
  document.body.appendChild(btn);
 
236
  return { btn: btn, anchor: anchor || null };
237
  }
238
 
239
+ // ========== Boot : charger **CSS**, puis config, puis brancher iOS/Android ==========
240
  (async function () {
 
241
  var scriptEl = getCurrentScript();
242
  var cssHref = (scriptEl && scriptEl.getAttribute('data-css'))
243
  || "https://huggingface.co/spaces/MikaFil/VR/resolve/main/css/style.css";
 
244
 
245
+ // 1) Charger la CSS et ATTENDRE le onload avant toute création d'UI
246
+ await ensureCssLinkAsync(cssHref);
247
+
248
+ // 2) Lire config.json
249
  var cfgUrl = findConfigUrl();
250
  var cfg = await loadConfigJson(cfgUrl);
251
  var GLB_URL = (cfg && typeof cfg.glb_url === "string" && cfg.glb_url)
 
253
  : "https://huggingface.co/datasets/MikaFil/viewer_gs/resolve/main/AR/tests/danae_no_metallic.glb";
254
  var USDZ_URL = (cfg && typeof cfg.usdz_url === "string" && cfg.usdz_url) ? cfg.usdz_url : null;
255
 
256
+ // 3) Construire les contrôles **après** CSS
257
  var controls = ensureLaunchControls(USDZ_URL);
258
  var launchBtn = controls.btn;
259
  var iosAnchor = controls.anchor;
260
 
261
+ // 4) iOS → Quick Look (pas de WebXR)
262
  if (isIOS()) {
263
  if (USDZ_URL) {
264
  message("iOS détecté : appuyez sur « Lancer l’AR » pour ouvrir Quick Look.");
 
273
  return;
274
  }
275
 
276
+ // 5) Android/Desktop → PlayCanvas
277
  try {
278
  await loadPlayCanvasRobust({ esmFirst: true, loadTimeoutMs: 15000 });
279
  } catch (e) {
 
354
  app.root.addChild(modelRoot);
355
  var modelLoaded = false, placedOnce = false;
356
 
357
+ // ===== Blob Shadow =====
358
  var blob = null;
359
  var BLOB_SIZE = 0.4;
360
  var BLOB_OFFSET_Y = 0.005;
 
438
  modelRoot.addChild(instance);
439
  modelRoot.setLocalScale(1, 1, 1);
440
 
 
441
  var renders = instance.findComponents('render');
442
  for (var ri = 0; ri < renders.length; ri++) {
443
  var r = renders[ri];
 
462
 
463
  if (!app.xr.supported) { message("WebXR n’est pas supporté sur cet appareil."); return; }
464
 
465
+ // Slider fiable
466
  var uiInteracting = false, draggingWrap = false, activePointerId = null;
467
  function insideWrap(target) { return rotWrap.contains(target); }
468
  function degFromPointer(e) {
 
496
  document.addEventListener("pointerup", endDrag, { capture: true, passive: false });
497
  document.addEventListener("pointercancel", endDrag, { capture: true, passive: false });
498
 
499
+ // Démarrage AR (exposé pour le bouton)
500
  function activateAR() {
501
  if (!app.xr.isAvailable(pc.XRTYPE_AR)) { message("AR immersive indisponible sur cet appareil."); return; }
502
  if (!app.xr.domOverlay) app.xr.domOverlay = {};
 
521
  }
522
  app.keyboard.on("keydown", function (evt) { if (evt.key === pc.KEY_ESCAPE && app.xr.active) app.xr.end(); });
523
 
524
+ // Hit-test HORIZONTAL
525
  var TMP_IN = new pc.Vec3(0, 1, 0), TMP_OUT = new pc.Vec3();
526
  function isHorizontalUpFacing(rot, minDot) {
527
  minDot = (typeof minDot === "number") ? minDot : 0.75;
 
529
  return TMP_OUT.y >= minDot;
530
  }
531
 
 
532
  app.xr.hitTest.on("available", function () {
533
  app.xr.hitTest.start({
534
  entityTypes: [pc.XRTRACKABLE_POINT, pc.XRTRACKABLE_PLANE],
 
544
  modelRoot.enabled = true;
545
  modelRoot.setPosition(pos);
546
 
 
547
  blob = createBlobShadowAt(pos, rot);
548
 
549
  var e = new pc.Vec3(); rot.getEulerAngles(e);
 
559
  });
560
  });
561
 
562
+ // Déplacement XR
563
  var isDragging = false;
564
  app.xr.input.on("add", function (inputSource) {
565
  inputSource.on("selectstart", function () {
 
586
  inputSource.on("selectend", function () { isDragging = false; });
587
  });
588
 
589
+ // Desktop : rotation souris
590
  var rotateMode = false, lastMouseX = 0;
591
  var ROTATE_SENSITIVITY = 0.25;
592
  app.mouse.on("mousedown", function (e) {