MikaFil commited on
Commit
2e7c4cc
·
verified ·
1 Parent(s): 28e2b17

Update viewer.js

Browse files
Files changed (1) hide show
  1. viewer.js +138 -35
viewer.js CHANGED
@@ -1,6 +1,6 @@
1
  // viewer.js
2
  // ==============================
3
- // viewer.js
4
  // ==============================
5
 
6
  let pc; // will hold the PlayCanvas module once imported
@@ -15,14 +15,26 @@ let minZoom, maxZoom, minAngle, maxAngle, minAzimuth, maxAzimuth, minPivotY, min
15
  let modelX, modelY, modelZ, modelScale, modelRotationX, modelRotationY, modelRotationZ;
16
  let plyUrl, glbUrl;
17
 
18
- /**
19
- * initializeViewer(config, instanceId)
20
- */
 
 
 
 
21
  export async function initializeViewer(config, instanceId) {
22
- if (viewerInitialized) return;
 
 
 
23
 
24
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
25
- const isMobile = isIOS || /Android/i.test(navigator.userAgent);
 
 
 
 
 
26
 
27
  // 1. Read config
28
  plyUrl = config.ply_url;
@@ -89,21 +101,53 @@ export async function initializeViewer(config, instanceId) {
89
 
90
  // 5. Import PlayCanvas
91
  if (!pc) {
92
- pc = await import("https://esm.run/playcanvas");
93
- window.pc = pc;
 
 
 
 
 
 
94
  }
95
 
 
 
96
  try {
97
- // 6. Setup device & app
98
- const device = await pc.createGraphicsDevice(canvas, {
99
- deviceTypes: ["webgl2"],
100
- glslangUrl: "https://playcanvas.vercel.app/static/lib/glslang/glslang.js",
101
- twgslUrl: "https://playcanvas.vercel.app/static/lib/twgsl/twgsl.js",
102
- antialias: false
103
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
 
 
 
 
105
 
106
- const opts = new pc.AppOptions();
 
 
 
107
  opts.graphicsDevice = device;
108
  opts.mouse = new pc.Mouse(canvas);
109
  opts.touch = new pc.TouchDevice(canvas);
@@ -126,10 +170,14 @@ export async function initializeViewer(config, instanceId) {
126
  app = new pc.Application(canvas, opts);
127
  app.setCanvasFillMode(pc.FILLMODE_NONE);
128
  app.setCanvasResolution(pc.RESOLUTION_AUTO);
129
- //app.scene.exposure = 0.5;
130
- //app.scene.toneMapping = pc.TONEMAP_ACES;
 
 
 
131
 
132
- // Attach ResizeObserver to keep canvas in sync with container size
 
133
  resizeObserver = new ResizeObserver(entries => {
134
  for (const entry of entries) {
135
  const { width, height } = entry.contentRect;
@@ -146,22 +194,54 @@ export async function initializeViewer(config, instanceId) {
146
  app.on('destroy', () => {
147
  window.removeEventListener('resize', resizeCanvas);
148
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
- // 7. Assets
151
- const assets = {
152
- model: new pc.Asset('gsplat', 'gsplat', { url: plyUrl }),
 
 
153
  orbit: new pc.Asset('script', 'script', { url: "https://mikafil-viewer-gs.static.hf.space/orbit-camera.js" }),
154
- galerie: new pc.Asset('galerie', 'container', { url: glbUrl }),
155
  hdr: new pc.Asset('hdr', 'texture', {
156
  url: `https://huggingface.co/datasets/bilca/ply_files/resolve/main/galeries/blanc.png`
157
  }, { type: pc.TEXTURETYPE_RGBP, mipmaps: false })
158
  };
 
 
 
 
159
 
160
- const loader = new pc.AssetListLoader(Object.values(assets), app.assets);
161
- let lastProg = 0;
162
- assets.model.on('load', () => progressDialog.style.display = 'none');
 
 
 
 
 
163
  assets.model.on('error', err => {
164
- console.error("Error loading PLY file:", err);
165
  progressDialog.innerHTML = `<p style="color: red">Error loading model: ${err}</p>`;
166
  });
167
 
@@ -175,8 +255,15 @@ export async function initializeViewer(config, instanceId) {
175
  progressIndicator.value = lastProg;
176
  }
177
  }, 100);
 
 
 
 
178
 
 
 
179
  loader.load(async () => {
 
180
  app.start();
181
  app.scene.envAtlas = assets.hdr.resource;
182
 
@@ -219,7 +306,6 @@ export async function initializeViewer(config, instanceId) {
219
  clearColor: config.canvas_background
220
  ? parseInt(config.canvas_background.substr(1, 2), 16) / 255
221
  : 0,
222
- //toneMapping: pc.TONEMAP_ACES
223
  });
224
  cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
225
  cameraEntity.lookAt(modelEntity.getPosition());
@@ -256,8 +342,11 @@ export async function initializeViewer(config, instanceId) {
256
  });
257
  app.root.addChild(cameraEntity);
258
 
259
- // Reset & constrain updates
260
- app.once('update', () => resetViewerCamera());
 
 
 
261
  app.on('update', dt => {
262
  if (cameraEntity) {
263
  const pos = cameraEntity.getPosition();
@@ -281,16 +370,29 @@ export async function initializeViewer(config, instanceId) {
281
  defaultVisible: !!config.showTooltipsDefault,
282
  moveDuration: config.tooltipMoveDuration || 0.6
283
  });
 
284
  } catch (e) {
285
- console.error("Error loading tooltips.js:", e);
286
  }
287
 
288
  progressDialog.style.display = 'none';
289
  viewerInitialized = true;
 
 
 
 
 
 
 
 
 
 
 
 
290
  });
291
 
292
  } catch (error) {
293
- console.error("Error initializing PlayCanvas viewer:", error);
294
  progressDialog.innerHTML = `<p style="color: red">Error loading viewer: ${error.message}</p>`;
295
  }
296
  }
@@ -336,8 +438,9 @@ export function resetViewerCamera() {
336
  orbitCam._updatePosition && orbitCam._updatePosition();
337
 
338
  tempEnt.destroy();
 
339
  } catch (e) {
340
- console.error("Error resetting camera:", e);
341
  }
342
  }
343
 
@@ -357,4 +460,4 @@ export function cleanupViewer() {
357
  resizeObserver.disconnect();
358
  resizeObserver = null;
359
  }
360
- }
 
1
  // viewer.js
2
  // ==============================
3
+ // viewer.js (iPhone/Safari context detection & debug)
4
  // ==============================
5
 
6
  let pc; // will hold the PlayCanvas module once imported
 
15
  let modelX, modelY, modelZ, modelScale, modelRotationX, modelRotationY, modelRotationZ;
16
  let plyUrl, glbUrl;
17
 
18
+ function logAndAlert(msg) {
19
+ console.log(msg);
20
+ if (typeof window !== "undefined" && /iPhone|iPad|iPod|Safari/i.test(navigator.userAgent)) {
21
+ alert(msg);
22
+ }
23
+ }
24
+
25
  export async function initializeViewer(config, instanceId) {
26
+ if (viewerInitialized) {
27
+ logAndAlert("Viewer already initialized");
28
+ return;
29
+ }
30
 
31
+ const userAgent = navigator.userAgent;
32
+ const isIOS = /iPad|iPhone|iPod/.test(userAgent) && !window.MSStream;
33
+ const isSafari = /^((?!chrome|android).)*safari/i.test(userAgent);
34
+ const isMobile = isIOS || /Android/i.test(userAgent);
35
+
36
+ logAndAlert(`UserAgent: ${userAgent}`);
37
+ logAndAlert(`isIOS: ${isIOS} isSafari: ${isSafari}`);
38
 
39
  // 1. Read config
40
  plyUrl = config.ply_url;
 
101
 
102
  // 5. Import PlayCanvas
103
  if (!pc) {
104
+ try {
105
+ pc = await import("https://esm.run/playcanvas");
106
+ window.pc = pc;
107
+ logAndAlert("PlayCanvas module loaded");
108
+ } catch (e) {
109
+ logAndAlert("PlayCanvas module import FAILED: " + e);
110
+ return;
111
+ }
112
  }
113
 
114
+ // 6. Setup device & app (with robust fallback)
115
+ let device = null;
116
  try {
117
+ try {
118
+ logAndAlert("Attempting WebGL2 context creation…");
119
+ device = await pc.createGraphicsDevice(canvas, {
120
+ deviceTypes: ["webgl2"],
121
+ glslangUrl: "https://playcanvas.vercel.app/static/lib/glslang/glslang.js",
122
+ twgslUrl: "https://playcanvas.vercel.app/static/lib/twgsl/twgsl.js",
123
+ antialias: false
124
+ });
125
+ logAndAlert("WebGL2 context acquired.");
126
+ } catch (err) {
127
+ logAndAlert("WebGL2 context failed: " + err);
128
+ logAndAlert("Falling back to WebGL1 context…");
129
+ device = await pc.createGraphicsDevice(canvas, {
130
+ deviceTypes: ["webgl1"],
131
+ glslangUrl: "https://playcanvas.vercel.app/static/lib/glslang/glslang.js",
132
+ twgslUrl: "https://playcanvas.vercel.app/static/lib/twgsl/twgsl.js",
133
+ antialias: false
134
+ });
135
+ logAndAlert("WebGL1 context acquired.");
136
+ }
137
+ if (!device) {
138
+ logAndAlert("No WebGL context could be created!");
139
+ return;
140
+ }
141
  device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
142
+ } catch (e) {
143
+ logAndAlert("Graphics device creation failed: " + e);
144
+ return;
145
+ }
146
 
147
+ // 7. App setup
148
+ let opts;
149
+ try {
150
+ opts = new pc.AppOptions();
151
  opts.graphicsDevice = device;
152
  opts.mouse = new pc.Mouse(canvas);
153
  opts.touch = new pc.TouchDevice(canvas);
 
170
  app = new pc.Application(canvas, opts);
171
  app.setCanvasFillMode(pc.FILLMODE_NONE);
172
  app.setCanvasResolution(pc.RESOLUTION_AUTO);
173
+ logAndAlert("PlayCanvas Application created.");
174
+ } catch (e) {
175
+ logAndAlert("PlayCanvas Application creation FAILED: " + e);
176
+ return;
177
+ }
178
 
179
+ // Attach ResizeObserver to keep canvas in sync with container size
180
+ try {
181
  resizeObserver = new ResizeObserver(entries => {
182
  for (const entry of entries) {
183
  const { width, height } = entry.contentRect;
 
194
  app.on('destroy', () => {
195
  window.removeEventListener('resize', resizeCanvas);
196
  });
197
+ } catch (e) {
198
+ logAndAlert("ResizeObserver setup failed: " + e);
199
+ }
200
+
201
+ // 8. Asset loader, with CORS workaround if needed
202
+ let fetchAssetWithCors = async function(url) {
203
+ try {
204
+ const resp = await fetch(url, {mode: 'cors', credentials: 'omit', cache: 'force-cache'});
205
+ if (!resp.ok) throw new Error("Failed fetch: " + url);
206
+ return resp.url;
207
+ } catch (e) {
208
+ logAndAlert("Asset fetch CORS failed for " + url + ": " + e);
209
+ return url; // fallback to original
210
+ }
211
+ };
212
+ let plyAssetUrl = plyUrl;
213
+ let glbAssetUrl = glbUrl;
214
+ if (isIOS || isSafari) {
215
+ plyAssetUrl = await fetchAssetWithCors(plyUrl);
216
+ glbAssetUrl = await fetchAssetWithCors(glbUrl);
217
+ }
218
 
219
+ // 9. Assets
220
+ let assets;
221
+ try {
222
+ assets = {
223
+ model: new pc.Asset('gsplat', 'gsplat', { url: plyAssetUrl }),
224
  orbit: new pc.Asset('script', 'script', { url: "https://mikafil-viewer-gs.static.hf.space/orbit-camera.js" }),
225
+ galerie: new pc.Asset('galerie', 'container', { url: glbAssetUrl }),
226
  hdr: new pc.Asset('hdr', 'texture', {
227
  url: `https://huggingface.co/datasets/bilca/ply_files/resolve/main/galeries/blanc.png`
228
  }, { type: pc.TEXTURETYPE_RGBP, mipmaps: false })
229
  };
230
+ } catch (e) {
231
+ logAndAlert("Asset object creation failed: " + e);
232
+ return;
233
+ }
234
 
235
+ // 10. Loader and progress
236
+ let loader, lastProg = 0;
237
+ try {
238
+ loader = new pc.AssetListLoader(Object.values(assets), app.assets);
239
+ assets.model.on('load', () => {
240
+ progressDialog.style.display = 'none';
241
+ logAndAlert("PLY asset loaded!");
242
+ });
243
  assets.model.on('error', err => {
244
+ logAndAlert("Error loading PLY file: " + err);
245
  progressDialog.innerHTML = `<p style="color: red">Error loading model: ${err}</p>`;
246
  });
247
 
 
255
  progressIndicator.value = lastProg;
256
  }
257
  }, 100);
258
+ } catch (e) {
259
+ logAndAlert("AssetListLoader setup failed: " + e);
260
+ return;
261
+ }
262
 
263
+ // 11. Start loading and build scene
264
+ try {
265
  loader.load(async () => {
266
+ logAndAlert("Asset loader .load() finished, starting PlayCanvas app…");
267
  app.start();
268
  app.scene.envAtlas = assets.hdr.resource;
269
 
 
306
  clearColor: config.canvas_background
307
  ? parseInt(config.canvas_background.substr(1, 2), 16) / 255
308
  : 0,
 
309
  });
310
  cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
311
  cameraEntity.lookAt(modelEntity.getPosition());
 
342
  });
343
  app.root.addChild(cameraEntity);
344
 
345
+ // Initial camera reset
346
+ app.once('update', () => {
347
+ resetViewerCamera();
348
+ logAndAlert("First update: scene and camera should be visible now!");
349
+ });
350
  app.on('update', dt => {
351
  if (cameraEntity) {
352
  const pos = cameraEntity.getPosition();
 
370
  defaultVisible: !!config.showTooltipsDefault,
371
  moveDuration: config.tooltipMoveDuration || 0.6
372
  });
373
+ logAndAlert("Tooltips loaded");
374
  } catch (e) {
375
+ logAndAlert("Error loading tooltips.js: " + e);
376
  }
377
 
378
  progressDialog.style.display = 'none';
379
  viewerInitialized = true;
380
+ logAndAlert("Viewer is fully initialized!");
381
+
382
+ // DEBUG: Draw loop reach on iOS/Safari only
383
+ if (isIOS || isSafari) {
384
+ let firstDraw = true;
385
+ app.on('frameend', function() {
386
+ if (firstDraw) {
387
+ alert('First draw call reached (Safari/iOS)');
388
+ firstDraw = false;
389
+ }
390
+ });
391
+ }
392
  });
393
 
394
  } catch (error) {
395
+ logAndAlert("Error initializing PlayCanvas viewer: " + error);
396
  progressDialog.innerHTML = `<p style="color: red">Error loading viewer: ${error.message}</p>`;
397
  }
398
  }
 
438
  orbitCam._updatePosition && orbitCam._updatePosition();
439
 
440
  tempEnt.destroy();
441
+ logAndAlert("Camera reset to defaults.");
442
  } catch (e) {
443
+ logAndAlert("Error resetting camera: " + e);
444
  }
445
  }
446
 
 
460
  resizeObserver.disconnect();
461
  resizeObserver = null;
462
  }
463
+ }