MikaFil commited on
Commit
69541fa
·
verified ·
1 Parent(s): c259e16

Update deplacement_dans_env/viewer_pr_env.js

Browse files
Files changed (1) hide show
  1. deplacement_dans_env/viewer_pr_env.js +77 -57
deplacement_dans_env/viewer_pr_env.js CHANGED
@@ -1,12 +1,13 @@
1
- // viewer_pr_env.js — robuste (boot non-bloquant, logs, physics avec fallback)
2
  // ============================================================================
3
- // - PlayCanvas ESM
4
- // - Boot non-bloquant : l'app démarre et rend tout de suite (caméra au root)
5
- // - Ammo (WASM) en parallèle, avec timeout + double API (promise/callback)
6
- // - Colliders mesh posés après 1ère frame (postrender)
 
7
  // - Player capsule créé seulement si Ammo OK, puis caméra reparentée
8
- // - Chargement GSplat/GLB tolérant (logs + timeout) ; DPR dynamique ; logs jalons
9
- // - NE CHARGE PAS le script de contrôle caméra (à ajouter côté scène)
10
  // ============================================================================
11
 
12
  /* -------------------------------------------
@@ -90,8 +91,52 @@ function isMobileUA() {
90
  return isIOS || isAndroid;
91
  }
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  async function loadAmmoWithTimeout(baseUrl, timeoutMs = 4000) {
94
- // Config URLs
95
  try {
96
  pc.WasmModule.setConfig('Ammo', {
97
  glueUrl: `${baseUrl}ammo.wasm.js`,
@@ -102,7 +147,7 @@ async function loadAmmoWithTimeout(baseUrl, timeoutMs = 4000) {
102
  console.warn('[Ammo] setConfig failed (ignored):', e);
103
  }
104
 
105
- // 1) Tente API Promise
106
  try {
107
  const maybePromise = pc.WasmModule.getInstance('Ammo', `${baseUrl}ammo.wasm.js`);
108
  if (maybePromise && typeof maybePromise.then === 'function') {
@@ -118,7 +163,7 @@ async function loadAmmoWithTimeout(baseUrl, timeoutMs = 4000) {
118
  console.warn('[Ammo] promise API failed:', e);
119
  }
120
 
121
- // 2) Tente API callback
122
  let resolved = false;
123
  try {
124
  step('Ammo: waiting (callback API)…');
@@ -126,10 +171,7 @@ async function loadAmmoWithTimeout(baseUrl, timeoutMs = 4000) {
126
  new Promise((resolve) => pc.WasmModule.getInstance('Ammo', () => { resolved = true; resolve(); })),
127
  new Promise((_, rej) => setTimeout(() => rej(new Error('Ammo load timeout (callback)')), timeoutMs))
128
  ]);
129
- if (resolved) {
130
- step('Ammo: ready (callback API)');
131
- return true;
132
- }
133
  } catch (e) {
134
  console.warn('[Ammo] callback API failed:', e);
135
  }
@@ -237,18 +279,24 @@ export async function initializeViewer(config, instanceId) {
237
  canvas.addEventListener('blur', onCanvasBlur);
238
 
239
  // Empêche les touches scroll pendant survol
240
- const scrollKeys = new Set(['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','PageUp','PageDown','Home','End',' ','Space','Spacebar']);
241
  const onKeyDownCapture = (e) => { if (!isPointerOverCanvas) return; if (scrollKeys.has(e.key) || scrollKeys.has(e.code)) e.preventDefault(); };
242
  window.addEventListener('keydown', onKeyDownCapture, true);
243
 
244
  if (progressDialog) progressDialog.style.display = 'block';
245
 
246
- // ---- Import PlayCanvas ----
247
  if (!pc) {
248
- step('B: importing PlayCanvas…');
249
- pc = await import('https://esm.run/playcanvas');
250
- window.pc = pc; // debug convenience
251
- step('B+: PlayCanvas imported');
 
 
 
 
 
 
252
  }
253
 
254
  // ---- Crée l’Application (démarrage non-bloquant) ----
@@ -329,28 +377,18 @@ export async function initializeViewer(config, instanceId) {
329
  const assets = [];
330
  let sogAsset = null, glbAsset = null;
331
 
332
- if (sogUrl) {
333
- sogAsset = new pc.Asset('gsplat', 'gsplat', { url: sogUrl });
334
- app.assets.add(sogAsset); assets.push(sogAsset);
335
- }
336
- if (glbUrl) {
337
- glbAsset = new pc.Asset('glb', 'container', { url: glbUrl });
338
- app.assets.add(glbAsset); assets.push(glbAsset);
339
- }
340
 
341
  step('F: requesting assets', { sog: !!sogUrl, glb: !!glbUrl });
342
 
343
  await new Promise((resolve) => {
344
  if (!assets.length) return resolve();
345
-
346
  const loader = new pc.AssetListLoader(assets, app.assets);
347
  let done = false;
348
  const finish = (label) => { if (!done) { done = true; step('F+: assets loaded (' + label + ')'); resolve(); } };
349
-
350
  loader.on('error', (e) => { console.warn('[viewer] Asset load error:', e); finish('with errors'); });
351
  loader.load(() => finish('ok'));
352
-
353
- // garde-fou timeout
354
  setTimeout(() => { if (!done) { console.warn('[viewer] Asset load timeout — continuing'); finish('timeout'); } }, 10000);
355
  });
356
 
@@ -366,13 +404,8 @@ export async function initializeViewer(config, instanceId) {
366
 
367
  // ---- Instancier le GLB d’environnement ----
368
  envEntity = glbAsset && glbAsset.resource ? glbAsset.resource.instantiateRenderEntity() : null;
369
- if (envEntity) {
370
- envEntity.name = 'ENV_GLTF';
371
- app.root.addChild(envEntity);
372
- step('I: env GLB instanced');
373
- } else {
374
- console.warn('[viewer] No environment GLB — physics will be skipped.');
375
- }
376
 
377
  // ---- Matériau "fond uni" si pas d'espace expo ----
378
  if (!espace_expo_bool && envEntity) {
@@ -391,25 +424,18 @@ export async function initializeViewer(config, instanceId) {
391
  }
392
 
393
  // ---- Physique : chargement Ammo en parallèle, colliders après 1ère frame ----
394
- let physicsReady = false;
395
  if (wantPhysics) {
396
  (async () => {
397
  try {
398
- const ok = await loadAmmoWithTimeout(ammoBaseUrl, isMobileUA() ? 5000 : 3500).catch(() => false);
399
  if (!ok) { step('P: Ammo unavailable — rendering without physics'); return; }
400
 
401
- // Pose des colliders mesh après 1ère frame pour garantir meshInstances
402
  if (envEntity) {
403
  app.once('postrender', () => {
404
  const applyStaticMesh = (e) => {
405
- if (e.render && !e.collision) {
406
- try { e.addComponent('collision', { type: 'mesh' }); } catch (err) { console.warn('[phys] add collision failed:', err); }
407
- }
408
- if (!e.rigidbody) {
409
- try { e.addComponent('rigidbody', { type: 'static', friction: 0.6, restitution: 0.0 }); } catch (err) { console.warn('[phys] add rigidbody failed:', err); }
410
- } else if (e.rigidbody && e.rigidbody.type !== 'static') {
411
- e.rigidbody.type = 'static';
412
- }
413
  };
414
  const stack=[envEntity];
415
  while (stack.length){ const n=stack.pop(); applyStaticMesh(n); n.children?.forEach(c=>stack.push(c)); }
@@ -417,9 +443,7 @@ export async function initializeViewer(config, instanceId) {
417
  });
418
  }
419
 
420
- // Crée Player capsule et reparent caméra
421
  createPlayerCapsuleAndAttachCamera(app, config);
422
- physicsReady = true;
423
  step('Q: physics ON (player created)');
424
  } catch (e) {
425
  console.warn('[phys] init failed:', e);
@@ -458,7 +482,6 @@ export async function initializeViewer(config, instanceId) {
458
  Helpers: création Player capsule + cam
459
  -------------------------------------------- */
460
  function createPlayerCapsuleAndAttachCamera(app, config) {
461
- // Si déjà présent, ne pas recréer
462
  if (playerEntity && playerEntity.rigidbody) { step('Q+: player already exists'); return; }
463
 
464
  playerEntity = new pc.Entity('Player');
@@ -477,14 +500,12 @@ function createPlayerCapsuleAndAttachCamera(app, config) {
477
  linearDamping: 0.15,
478
  angularDamping: 0.999
479
  });
480
- playerEntity.rigidbody.angularFactor = new pc.Vec3(0, 0, 0); // éviter roulis
481
  } catch (e) { console.warn('[phys] add rigidbody failed:', e); }
482
 
483
- // Spawn
484
  playerEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
485
  app.root.addChild(playerEntity);
486
 
487
- // Caméra -> enfant Player (yeux)
488
  const eyes = config.eyesOffsetY !== undefined ? parseFloat(config.eyesOffsetY) : Math.max(0.1, capsuleHeight * 0.9);
489
  try {
490
  cameraEntity.reparent(playerEntity);
@@ -492,7 +513,6 @@ function createPlayerCapsuleAndAttachCamera(app, config) {
492
  cameraEntity.setLocalEulerAngles(0, 0, 0);
493
  } catch (e) { console.warn('[phys] camera reparent failed:', e); }
494
 
495
- // Gravité selon freeFly
496
  const freeFly = !!config.freeFly;
497
  app.scene.gravity = freeFly ? new pc.Vec3(0,0,0) : new pc.Vec3(0,-9.81,0);
498
  }
 
1
+ // viewer_pr_env.js — robuste v2 (multi‑CDN PlayCanvas import)
2
  // ============================================================================
3
+ // - Boot non-bloquant : l'app démarre tout de suite (caméra au root)
4
+ // - Import PlayCanvas **robuste** : ESM multi‑CDN (esm.run jsDelivr unpkg)
5
+ // puis fallback **UMD** (playcanvas.js) si ESM bloqué par CSP/iframe
6
+ // - Ammo (WASM) en parallèle, timeout + double API (promise/callback)
7
+ // - Colliders mesh ajoutés après 1ʳᵉ frame (postrender)
8
  // - Player capsule créé seulement si Ammo OK, puis caméra reparentée
9
+ // - GSplat/GLB chargés avec logs + timeout ; DPR dynamique ; logs jalons
10
+ // - Ne charge pas le script de contrôle caméra (à ajouter côté scène)
11
  // ============================================================================
12
 
13
  /* -------------------------------------------
 
91
  return isIOS || isAndroid;
92
  }
93
 
94
+ async function importPlayCanvasRobust(timeoutMs = 6000) {
95
+ const sourcesESM = [
96
+ 'https://esm.run/playcanvas',
97
+ 'https://cdn.jsdelivr.net/npm/playcanvas@latest/build/playcanvas.mjs',
98
+ 'https://unpkg.com/playcanvas@latest/build/playcanvas.mjs'
99
+ ];
100
+
101
+ // Essais ESM
102
+ for (const url of sourcesESM) {
103
+ try {
104
+ console.log('[PC-IMPORT] Try ESM:', url);
105
+ const mod = await Promise.race([
106
+ import(/* @vite-ignore */ url),
107
+ new Promise((_, rej) => setTimeout(() => rej(new Error('ESM timeout')), timeoutMs))
108
+ ]);
109
+ if (mod) {
110
+ console.log('[PC-IMPORT] ESM OK:', url);
111
+ return mod; // module ESM
112
+ }
113
+ } catch (e) {
114
+ console.warn('[PC-IMPORT] ESM failed:', url, e);
115
+ }
116
+ }
117
+
118
+ // Fallback UMD
119
+ const sourcesUMD = [
120
+ 'https://cdn.jsdelivr.net/npm/playcanvas@latest/build/playcanvas.js',
121
+ 'https://unpkg.com/playcanvas@latest/build/playcanvas.js'
122
+ ];
123
+ for (const url of sourcesUMD) {
124
+ try {
125
+ console.log('[PC-IMPORT] Try UMD:', url);
126
+ await new Promise((resolve, reject) => {
127
+ const s = document.createElement('script');
128
+ s.src = url; s.async = true; s.onload = resolve; s.onerror = reject;
129
+ document.head.appendChild(s);
130
+ });
131
+ if (window.pc) { console.log('[PC-IMPORT] UMD OK:', url); return window.pc; }
132
+ } catch (e) {
133
+ console.warn('[PC-IMPORT] UMD failed:', url, e);
134
+ }
135
+ }
136
+ throw new Error('PlayCanvas import failed on all sources');
137
+ }
138
+
139
  async function loadAmmoWithTimeout(baseUrl, timeoutMs = 4000) {
 
140
  try {
141
  pc.WasmModule.setConfig('Ammo', {
142
  glueUrl: `${baseUrl}ammo.wasm.js`,
 
147
  console.warn('[Ammo] setConfig failed (ignored):', e);
148
  }
149
 
150
+ // 1) API Promise
151
  try {
152
  const maybePromise = pc.WasmModule.getInstance('Ammo', `${baseUrl}ammo.wasm.js`);
153
  if (maybePromise && typeof maybePromise.then === 'function') {
 
163
  console.warn('[Ammo] promise API failed:', e);
164
  }
165
 
166
+ // 2) API callback
167
  let resolved = false;
168
  try {
169
  step('Ammo: waiting (callback API)…');
 
171
  new Promise((resolve) => pc.WasmModule.getInstance('Ammo', () => { resolved = true; resolve(); })),
172
  new Promise((_, rej) => setTimeout(() => rej(new Error('Ammo load timeout (callback)')), timeoutMs))
173
  ]);
174
+ if (resolved) { step('Ammo: ready (callback API)'); return true; }
 
 
 
175
  } catch (e) {
176
  console.warn('[Ammo] callback API failed:', e);
177
  }
 
279
  canvas.addEventListener('blur', onCanvasBlur);
280
 
281
  // Empêche les touches scroll pendant survol
282
+ const scrollKeys = new Set(['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','PageUp','PageDown','Home','End',' ','Space','Space','Spacebar']);
283
  const onKeyDownCapture = (e) => { if (!isPointerOverCanvas) return; if (scrollKeys.has(e.key) || scrollKeys.has(e.code)) e.preventDefault(); };
284
  window.addEventListener('keydown', onKeyDownCapture, true);
285
 
286
  if (progressDialog) progressDialog.style.display = 'block';
287
 
288
+ // ---- Import PlayCanvas (robuste) ----
289
  if (!pc) {
290
+ step('B: importing PlayCanvas (robust)…');
291
+ try {
292
+ const modOrPc = await importPlayCanvasRobust( mobile ? 8000 : 6000 );
293
+ pc = modOrPc && modOrPc.Application ? modOrPc : (window.pc || modOrPc);
294
+ window.pc = pc; // debug convenience
295
+ step('B+: PlayCanvas imported OK');
296
+ } catch (e) {
297
+ console.error('[PC-IMPORT] FAILED:', e);
298
+ throw e; // surface d’erreurs l’affichera
299
+ }
300
  }
301
 
302
  // ---- Crée l’Application (démarrage non-bloquant) ----
 
377
  const assets = [];
378
  let sogAsset = null, glbAsset = null;
379
 
380
+ if (sogUrl) { sogAsset = new pc.Asset('gsplat', 'gsplat', { url: sogUrl }); app.assets.add(sogAsset); assets.push(sogAsset); }
381
+ if (glbUrl) { glbAsset = new pc.Asset('glb', 'container', { url: glbUrl }); app.assets.add(glbAsset); assets.push(glbAsset); }
 
 
 
 
 
 
382
 
383
  step('F: requesting assets', { sog: !!sogUrl, glb: !!glbUrl });
384
 
385
  await new Promise((resolve) => {
386
  if (!assets.length) return resolve();
 
387
  const loader = new pc.AssetListLoader(assets, app.assets);
388
  let done = false;
389
  const finish = (label) => { if (!done) { done = true; step('F+: assets loaded (' + label + ')'); resolve(); } };
 
390
  loader.on('error', (e) => { console.warn('[viewer] Asset load error:', e); finish('with errors'); });
391
  loader.load(() => finish('ok'));
 
 
392
  setTimeout(() => { if (!done) { console.warn('[viewer] Asset load timeout — continuing'); finish('timeout'); } }, 10000);
393
  });
394
 
 
404
 
405
  // ---- Instancier le GLB d’environnement ----
406
  envEntity = glbAsset && glbAsset.resource ? glbAsset.resource.instantiateRenderEntity() : null;
407
+ if (envEntity) { envEntity.name = 'ENV_GLTF'; app.root.addChild(envEntity); step('I: env GLB instanced'); }
408
+ else { console.warn('[viewer] No environment GLB — physics will be skipped.'); }
 
 
 
 
 
409
 
410
  // ---- Matériau "fond uni" si pas d'espace expo ----
411
  if (!espace_expo_bool && envEntity) {
 
424
  }
425
 
426
  // ---- Physique : chargement Ammo en parallèle, colliders après 1ère frame ----
 
427
  if (wantPhysics) {
428
  (async () => {
429
  try {
430
+ const ok = await loadAmmoWithTimeout(ammoBaseUrl, isMobileUA() ? 8000 : 5000).catch(() => false);
431
  if (!ok) { step('P: Ammo unavailable — rendering without physics'); return; }
432
 
 
433
  if (envEntity) {
434
  app.once('postrender', () => {
435
  const applyStaticMesh = (e) => {
436
+ if (e.render && !e.collision) { try { e.addComponent('collision', { type: 'mesh' }); } catch (err) { console.warn('[phys] add collision failed:', err); } }
437
+ if (!e.rigidbody) { try { e.addComponent('rigidbody', { type: 'static', friction: 0.6, restitution: 0.0 }); } catch (err) { console.warn('[phys] add rigidbody failed:', err); } }
438
+ else if (e.rigidbody && e.rigidbody.type !== 'static') { e.rigidbody.type = 'static'; }
 
 
 
 
 
439
  };
440
  const stack=[envEntity];
441
  while (stack.length){ const n=stack.pop(); applyStaticMesh(n); n.children?.forEach(c=>stack.push(c)); }
 
443
  });
444
  }
445
 
 
446
  createPlayerCapsuleAndAttachCamera(app, config);
 
447
  step('Q: physics ON (player created)');
448
  } catch (e) {
449
  console.warn('[phys] init failed:', e);
 
482
  Helpers: création Player capsule + cam
483
  -------------------------------------------- */
484
  function createPlayerCapsuleAndAttachCamera(app, config) {
 
485
  if (playerEntity && playerEntity.rigidbody) { step('Q+: player already exists'); return; }
486
 
487
  playerEntity = new pc.Entity('Player');
 
500
  linearDamping: 0.15,
501
  angularDamping: 0.999
502
  });
503
+ playerEntity.rigidbody.angularFactor = new pc.Vec3(0, 0, 0);
504
  } catch (e) { console.warn('[phys] add rigidbody failed:', e); }
505
 
 
506
  playerEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
507
  app.root.addChild(playerEntity);
508
 
 
509
  const eyes = config.eyesOffsetY !== undefined ? parseFloat(config.eyesOffsetY) : Math.max(0.1, capsuleHeight * 0.9);
510
  try {
511
  cameraEntity.reparent(playerEntity);
 
513
  cameraEntity.setLocalEulerAngles(0, 0, 0);
514
  } catch (e) { console.warn('[phys] camera reparent failed:', e); }
515
 
 
516
  const freeFly = !!config.freeFly;
517
  app.scene.gravity = freeFly ? new pc.Vec3(0,0,0) : new pc.Vec3(0,-9.81,0);
518
  }