MikaFil commited on
Commit
62ebf50
·
verified ·
1 Parent(s): 5a24171

Update viewer.js

Browse files
Files changed (1) hide show
  1. viewer.js +57 -39
viewer.js CHANGED
@@ -61,7 +61,7 @@ function traverse(entity, callback) {
61
  }
62
 
63
  /* -------------------------------------------
64
- Chargement unique de orbit-camera.js
65
  -------------------------------------------- */
66
 
67
  async function ensureOrbitScriptsLoaded() {
@@ -73,14 +73,15 @@ async function ensureOrbitScriptsLoaded() {
73
 
74
  window.__PLY_ORBIT_LOADING__ = new Promise((resolve, reject) => {
75
  const s = document.createElement("script");
76
- s.src = "https://mikafil-viewer-sgos.static.hf.space/orbit-camera.js";
 
77
  s.async = true;
78
  s.onload = () => {
79
  window.__PLY_ORBIT_LOADED__ = true;
80
  resolve();
81
  };
82
  s.onerror = (e) => {
83
- console.error("[viewer.js] Failed to load orbit-camera.js", e);
84
  reject(e);
85
  };
86
  document.head.appendChild(s);
@@ -97,6 +98,7 @@ let pc;
97
  export let app = null;
98
  let cameraEntity = null;
99
  let modelEntity = null;
 
100
  let viewerInitialized = false;
101
  let resizeObserver = null;
102
  let resizeTimeout = null;
@@ -349,28 +351,23 @@ export async function initializeViewer(config, instanceId) {
349
  canvas.removeEventListener("blur", onCanvasBlur);
350
  });
351
 
352
- // --- Enregistre les assets en 2 phases ---
353
- // Phase 1 : .sog (priorité) ⇒ time-to-first-pixel
354
  const sogAsset = new pc.Asset("gsplat", "gsplat", { url: sogUrl });
355
- app.assets.add(sogAsset);
356
-
357
- // Phase 2 (déférée) : glb + présentoir
358
  const glbAsset = new pc.Asset("glb", "container", { url: glbUrl });
359
- const presentoirAsset = new pc.Asset("presentoir", "container", { url: presentoirUrl });
360
  app.assets.add(glbAsset);
361
- app.assets.add(presentoirAsset);
362
 
363
- // Assure orbit-camera.js (nécessaire pour les scripts d'input)
364
  await ensureOrbitScriptsLoaded();
365
 
366
- // ---------- PHASE 1 : charger le .sog, démarrer tout de suite ----------
367
  await new Promise((resolve, reject) => {
368
- const loader1 = new pc.AssetListLoader([sogAsset], app.assets);
369
- loader1.load(() => resolve());
370
- loader1.on('error', reject);
371
  });
372
 
373
- app.start(); // démarre l'update loop le plus tôt possible
374
  progressDialog.style.display = "none";
375
 
376
  // --- Modèle principal (GSplat via .sog) ---
@@ -381,7 +378,18 @@ export async function initializeViewer(config, instanceId) {
381
  modelEntity.setLocalScale(modelScale, modelScale, modelScale);
382
  app.root.addChild(modelEntity);
383
 
384
- // --- Caméra + scripts d’input ---
 
 
 
 
 
 
 
 
 
 
 
385
  cameraEntity = new pc.Entity("camera");
386
  cameraEntity.addComponent("camera", {
387
  clearColor: new pc.Color(color_bg),
@@ -392,9 +400,11 @@ export async function initializeViewer(config, instanceId) {
392
  cameraEntity.lookAt(modelEntity.getPosition());
393
  cameraEntity.addComponent("script");
394
 
 
395
  cameraEntity.script.create("orbitCamera", {
396
  attributes: {
397
- focusEntity: glbAsset,
 
398
  inertiaFactor: 0.2,
399
  distanceMax: maxZoom,
400
  distanceMin: minZoom,
@@ -418,6 +428,8 @@ export async function initializeViewer(config, instanceId) {
418
 
419
  // Taille initiale
420
  app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
 
 
421
  app.once("update", () => resetViewerCamera());
422
 
423
  // ---------- Perf dynamique : DPR temporairement réduit pendant interaction ----------
@@ -425,42 +437,33 @@ export async function initializeViewer(config, instanceId) {
425
  const clamped = Math.max(0.5, Math.min(val, maxDevicePixelRatio));
426
  if (app.graphicsDevice.maxPixelRatio !== clamped) {
427
  app.graphicsDevice.maxPixelRatio = clamped;
428
- // Force un resize pour appliquer immédiatement
429
  app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
430
  }
431
  };
432
 
433
  const bumpInteraction = () => {
434
- // baisse DPR pendant l'interaction
435
  setDpr(interactDpr);
436
  if (idleTimer) clearTimeout(idleTimer);
437
  idleTimer = setTimeout(() => {
438
- // restaure DPR max quand l'utilisateur s'arrête
439
  setDpr(Math.min(window.devicePixelRatio || 1, maxDevicePixelRatio));
440
  }, idleRestoreDelay);
441
  };
442
 
443
- // Hooks d'interaction
444
  const interactionEvents = ["mousedown", "mousemove", "mouseup", "wheel", "touchstart", "touchmove", "keydown"];
445
  interactionEvents.forEach((ev) => {
446
  canvas.addEventListener(ev, bumpInteraction, { passive: true });
447
  });
448
 
449
- // ---------- PHASE 2 (déférée) : charger sol/environnement et tooltips ----------
450
- // On charge le reste sans bloquer l’affichage initial
451
  setTimeout(async () => {
452
  try {
 
 
453
  await new Promise((resolve) => {
454
- const loader2 = new pc.AssetListLoader([glbAsset, presentoirAsset], app.assets);
455
  loader2.load(() => resolve());
456
  });
457
 
458
- // Sol / environnement
459
- const glbEntity = glbAsset.resource ? glbAsset.resource.instantiateRenderEntity() : null;
460
- if (glbEntity) {
461
- app.root.addChild(glbEntity);
462
- }
463
-
464
  const presentoirEntity =
465
  presentoirAsset.resource ? presentoirAsset.resource.instantiateRenderEntity() : null;
466
  if (presentoirEntity) {
@@ -468,6 +471,7 @@ export async function initializeViewer(config, instanceId) {
468
  app.root.addChild(presentoirEntity);
469
  }
470
 
 
471
  if (!espace_expo_bool) {
472
  const matSol = new pc.StandardMaterial();
473
  matSol.blendType = pc.BLEND_NONE;
@@ -484,19 +488,16 @@ export async function initializeViewer(config, instanceId) {
484
  });
485
  }
486
 
487
- if (glbEntity) {
488
- traverse(glbEntity, (node) => {
489
  if (node.render && node.render.meshInstances) {
490
  for (const mi of node.render.meshInstances) mi.material = matSol;
491
  }
492
  });
493
-
494
- // ////// MODIFIE A LA MANO FAIRE GAFFE //////
495
- glbEntity.setLocalScale(10, 10, 10);
496
  }
497
  }
498
 
499
- // Tooltips (optionnels) — chargés après premier rendu
500
  try {
501
  if (config.tooltips_url) {
502
  import("./tooltips.js")
@@ -528,10 +529,27 @@ export async function initializeViewer(config, instanceId) {
528
  export function resetViewerCamera() {
529
  try {
530
  if (!cameraEntity || !modelEntity || !app) return;
531
- const orbitCam = cameraEntity.script.orbitCamera;
532
- if (!orbitCam) return;
533
 
534
  const modelPos = modelEntity.getPosition();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  const tempEnt = new pc.Entity();
536
  tempEnt.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
537
  tempEnt.lookAt(modelPos);
 
61
  }
62
 
63
  /* -------------------------------------------
64
+ Chargement unique de ctrl_camera_pr_env.js
65
  -------------------------------------------- */
66
 
67
  async function ensureOrbitScriptsLoaded() {
 
73
 
74
  window.__PLY_ORBIT_LOADING__ = new Promise((resolve, reject) => {
75
  const s = document.createElement("script");
76
+ // IMPORTANT : charger le script free-camera + collisions (pas l'ancienne orbit-camera)
77
+ s.src = "https://mikafil-viewer-sgos.static.hf.space/deplacement_dans_env/ctrl_camera_pr_env.js";
78
  s.async = true;
79
  s.onload = () => {
80
  window.__PLY_ORBIT_LOADED__ = true;
81
  resolve();
82
  };
83
  s.onerror = (e) => {
84
+ console.error("[viewer.js] Failed to load ctrl_camera_pr_env.js", e);
85
  reject(e);
86
  };
87
  document.head.appendChild(s);
 
98
  export let app = null;
99
  let cameraEntity = null;
100
  let modelEntity = null;
101
+ let envEntity = null; // <<< GLB instancié (focusEntity collisions)
102
  let viewerInitialized = false;
103
  let resizeObserver = null;
104
  let resizeTimeout = null;
 
351
  canvas.removeEventListener("blur", onCanvasBlur);
352
  });
353
 
354
+ // --- Enregistre et charge les assets en 1 phase pour SOG + GLB (focusEntity prêt avant la caméra) ---
 
355
  const sogAsset = new pc.Asset("gsplat", "gsplat", { url: sogUrl });
 
 
 
356
  const glbAsset = new pc.Asset("glb", "container", { url: glbUrl });
357
+ app.assets.add(sogAsset);
358
  app.assets.add(glbAsset);
 
359
 
360
+ // Assure la free-cam + collisions
361
  await ensureOrbitScriptsLoaded();
362
 
363
+ // ---------- CHARGEMENT SOG + GLB AVANT CREATION CAMERA ----------
364
  await new Promise((resolve, reject) => {
365
+ const loader = new pc.AssetListLoader([sogAsset, glbAsset], app.assets);
366
+ loader.load(() => resolve());
367
+ loader.on('error', reject);
368
  });
369
 
370
+ app.start(); // démarre l'update loop dès que possible
371
  progressDialog.style.display = "none";
372
 
373
  // --- Modèle principal (GSplat via .sog) ---
 
378
  modelEntity.setLocalScale(modelScale, modelScale, modelScale);
379
  app.root.addChild(modelEntity);
380
 
381
+ // --- Instancier le GLB d’environnement (collision) ---
382
+ envEntity = glbAsset.resource ? glbAsset.resource.instantiateRenderEntity() : null;
383
+ if (envEntity) {
384
+ envEntity.name = "ENV_GLTF";
385
+ app.root.addChild(envEntity);
386
+ // NOTE : évite de changer l'échelle ici, sauf si nécessaire. Si tu DOIS :
387
+ // envEntity.setLocalScale(1,1,1); // garde l'échelle d'import cohérente
388
+ } else {
389
+ console.warn("[viewer.js] GLB resource missing: collisions will fallback on GSplat (aucun mesh).");
390
+ }
391
+
392
+ // --- Caméra + scripts d’input (free-cam nommée 'orbitCamera' pour compat) ---
393
  cameraEntity = new pc.Entity("camera");
394
  cameraEntity.addComponent("camera", {
395
  clearColor: new pc.Color(color_bg),
 
400
  cameraEntity.lookAt(modelEntity.getPosition());
401
  cameraEntity.addComponent("script");
402
 
403
+ // >>> focusEntity = GLB en priorité (sinon fallback gsplat) <<<
404
  cameraEntity.script.create("orbitCamera", {
405
  attributes: {
406
+ focusEntity: envEntity || modelEntity,
407
+ // Attributs hérités pour compat, mais seront interprétés par la free-cam
408
  inertiaFactor: 0.2,
409
  distanceMax: maxZoom,
410
  distanceMin: minZoom,
 
428
 
429
  // Taille initiale
430
  app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
431
+
432
+ // IMPORTANT : si la free-cam est active, ne pas "forcer" un reset d'orbite.
433
  app.once("update", () => resetViewerCamera());
434
 
435
  // ---------- Perf dynamique : DPR temporairement réduit pendant interaction ----------
 
437
  const clamped = Math.max(0.5, Math.min(val, maxDevicePixelRatio));
438
  if (app.graphicsDevice.maxPixelRatio !== clamped) {
439
  app.graphicsDevice.maxPixelRatio = clamped;
 
440
  app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
441
  }
442
  };
443
 
444
  const bumpInteraction = () => {
 
445
  setDpr(interactDpr);
446
  if (idleTimer) clearTimeout(idleTimer);
447
  idleTimer = setTimeout(() => {
 
448
  setDpr(Math.min(window.devicePixelRatio || 1, maxDevicePixelRatio));
449
  }, idleRestoreDelay);
450
  };
451
 
 
452
  const interactionEvents = ["mousedown", "mousemove", "mouseup", "wheel", "touchstart", "touchmove", "keydown"];
453
  interactionEvents.forEach((ev) => {
454
  canvas.addEventListener(ev, bumpInteraction, { passive: true });
455
  });
456
 
457
+ // ---------- CHARGEMENT DIFFÉRÉ : présentoir et tooltips ----------
 
458
  setTimeout(async () => {
459
  try {
460
+ const presentoirAsset = new pc.Asset("presentoir", "container", { url: presentoirUrl });
461
+ app.assets.add(presentoirAsset);
462
  await new Promise((resolve) => {
463
+ const loader2 = new pc.AssetListLoader([presentoirAsset], app.assets);
464
  loader2.load(() => resolve());
465
  });
466
 
 
 
 
 
 
 
467
  const presentoirEntity =
468
  presentoirAsset.resource ? presentoirAsset.resource.instantiateRenderEntity() : null;
469
  if (presentoirEntity) {
 
471
  app.root.addChild(presentoirEntity);
472
  }
473
 
474
+ // Si pas d'espace expo : recolore GLB & présentoir pour fond uni
475
  if (!espace_expo_bool) {
476
  const matSol = new pc.StandardMaterial();
477
  matSol.blendType = pc.BLEND_NONE;
 
488
  });
489
  }
490
 
491
+ if (envEntity) {
492
+ traverse(envEntity, (node) => {
493
  if (node.render && node.render.meshInstances) {
494
  for (const mi of node.render.meshInstances) mi.material = matSol;
495
  }
496
  });
 
 
 
497
  }
498
  }
499
 
500
+ // Tooltips (optionnels)
501
  try {
502
  if (config.tooltips_url) {
503
  import("./tooltips.js")
 
529
  export function resetViewerCamera() {
530
  try {
531
  if (!cameraEntity || !modelEntity || !app) return;
532
+ const camScript = cameraEntity.script && cameraEntity.script.orbitCamera;
533
+ if (!camScript) return;
534
 
535
  const modelPos = modelEntity.getPosition();
536
+
537
+ // Si c'est une FREE-CAM (notre script), ne pas toucher à des champs d'orbite.
538
+ // On se contente éventuellement de réaligner le regard.
539
+ const looksLikeOrbit =
540
+ ("pivotPoint" in camScript) ||
541
+ ("_distance" in camScript) ||
542
+ ("_updatePosition" in camScript);
543
+
544
+ if (!looksLikeOrbit) {
545
+ // Free camera : juste orienter vers le modèle si souhaité
546
+ cameraEntity.lookAt(modelPos);
547
+ return;
548
+ }
549
+
550
+ // --- Cas d'une vraie orbit-camera (compat héritée) ---
551
+ const orbitCam = camScript;
552
+
553
  const tempEnt = new pc.Entity();
554
  tempEnt.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
555
  tempEnt.lookAt(modelPos);