MikaFil commited on
Commit
690b277
·
verified ·
1 Parent(s): 3454818

Update viewer.js

Browse files
Files changed (1) hide show
  1. viewer.js +248 -179
viewer.js CHANGED
@@ -14,196 +14,263 @@ let minZoom, maxZoom, minAngle, maxAngle, minAzimuth, maxAzimuth, minPivotY, min
14
  let modelX, modelY, modelZ, modelScale, modelRotationX, modelRotationY, modelRotationZ;
15
 
16
  export async function initializeViewer(config, instanceId) {
17
- if (viewerInitialized) return;
18
-
19
- // Params
20
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
21
- const isMobile = isIOS || /Android/i.test(navigator.userAgent);
22
-
23
- // URLs and config
24
- const sogsUrl = config.sogs_json_url;
25
- if (!sogsUrl) {
26
- alert('Missing SOGS (meta.json) URL in config.sogs_json_url.');
27
- return;
28
- }
29
-
30
- minZoom = parseFloat(config.minZoom || "1");
31
- maxZoom = parseFloat(config.maxZoom || "20");
32
- minAngle = parseFloat(config.minAngle || "-45");
33
- maxAngle = parseFloat(config.maxAngle || "90");
34
- minAzimuth = (config.minAzimuth !== undefined) ? parseFloat(config.minAzimuth) : -360;
35
- maxAzimuth = (config.maxAzimuth !== undefined) ? parseFloat(config.maxAzimuth) : 360;
36
- minPivotY = parseFloat(config.minPivotY || "0");
37
- minY = (config.minY !== undefined) ? parseFloat(config.minY) : 0;
38
-
39
- modelX = (config.modelX !== undefined) ? parseFloat(config.modelX) : 0;
40
- modelY = (config.modelY !== undefined) ? parseFloat(config.modelY) : 0;
41
- modelZ = (config.modelZ !== undefined) ? parseFloat(config.modelZ) : 0;
42
- modelScale = (config.modelScale !== undefined) ? parseFloat(config.modelScale) : 1;
43
- modelRotationX = (config.modelRotationX !== undefined) ? parseFloat(config.modelRotationX) : 0;
44
- modelRotationY = (config.modelRotationY !== undefined) ? parseFloat(config.modelRotationY) : 0;
45
- modelRotationZ = (config.modelRotationZ !== undefined) ? parseFloat(config.modelRotationZ) : 0;
46
-
47
- const cameraX = (config.cameraX !== undefined) ? parseFloat(config.cameraX) : 0;
48
- const cameraY = (config.cameraY !== undefined) ? parseFloat(config.cameraY) : 2;
49
- const cameraZ = (config.cameraZ !== undefined) ? parseFloat(config.cameraZ) : 5;
50
- const cameraXPhone = (config.cameraXPhone !== undefined) ? parseFloat(config.cameraXPhone) : cameraX;
51
- const cameraYPhone = (config.cameraYPhone !== undefined) ? parseFloat(config.cameraYPhone) : cameraY;
52
- const cameraZPhone = (config.cameraZPhone !== undefined) ? parseFloat(config.cameraZPhone) : (cameraZ * 1.5);
53
-
54
- chosenCameraX = isMobile ? cameraXPhone : cameraX;
55
- chosenCameraY = isMobile ? cameraYPhone : cameraY;
56
- chosenCameraZ = isMobile ? cameraZPhone : cameraZ;
57
-
58
- // DOM Elements
59
- const canvasId = 'canvas-' + instanceId;
60
- const progressDialog = document.getElementById('progress-dialog-' + instanceId);
61
- const viewerContainer = document.getElementById('viewer-container-' + instanceId);
62
-
63
- // Remove old canvas if any
64
- let oldCanvas = document.getElementById(canvasId);
65
- if (oldCanvas) oldCanvas.remove();
66
-
67
- // Create and add canvas
68
- const canvas = document.createElement('canvas');
69
- canvas.id = canvasId;
70
- canvas.className = 'ply-canvas';
71
- canvas.style.width = "100%";
72
- canvas.style.height = "100%";
73
- canvas.setAttribute('tabindex', '0');
74
- viewerContainer.insertBefore(canvas, progressDialog);
75
- canvas.style.touchAction = "none";
76
- canvas.style.webkitTouchCallout = "none";
77
- canvas.addEventListener('gesturestart', e => e.preventDefault());
78
- canvas.addEventListener('gesturechange', e => e.preventDefault());
79
- canvas.addEventListener('gestureend', e => e.preventDefault());
80
- canvas.addEventListener('dblclick', e => e.preventDefault());
81
- canvas.addEventListener('touchstart', e => { if (e.touches.length > 1) e.preventDefault(); }, { passive: false });
82
- canvas.addEventListener('wheel', (e) => { e.preventDefault(); }, { passive: false });
83
-
84
- progressDialog.style.display = 'block';
85
-
86
- // --- PlayCanvas Graphics Device ---
87
- const device = await pc.createGraphicsDevice(canvas, {
88
- deviceTypes: ["webgl2"],
89
- antialias: false,
90
- glslangUrl: "https://playcanvas.vercel.app/static/lib/glslang/glslang.js",
91
- twgslUrl: "https://playcanvas.vercel.app/static/lib/twgsl/twgsl.js"
92
- });
93
- device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
94
-
95
- // --- PlayCanvas AppBase (modern, 2.8.1+) ---
96
- const appOpts = new pc.AppOptions();
97
- appOpts.graphicsDevice = device;
98
- appOpts.mouse = new pc.Mouse(canvas);
99
- appOpts.touch = new pc.TouchDevice(canvas);
100
- appOpts.componentSystems = [
101
- pc.RenderComponentSystem,
102
- pc.CameraComponentSystem,
103
- pc.LightComponentSystem,
104
- pc.ScriptComponentSystem,
105
- pc.GSplatComponentSystem
106
- ];
107
- appOpts.resourceHandlers = [
108
- pc.TextureHandler,
109
- pc.ContainerHandler,
110
- pc.ScriptHandler,
111
- pc.GSplatHandler
112
- ];
113
-
114
- app = new pc.AppBase(canvas);
115
- app.init(appOpts);
116
-
117
- // Fill/resize
118
- app.setCanvasFillMode(pc.FILLMODE_NONE);
119
- app.setCanvasResolution(pc.RESOLUTION_AUTO);
120
-
121
- resizeObserver = new ResizeObserver(entries => {
122
- entries.forEach(entry => {
123
- app.resizeCanvas(entry.contentRect.width, entry.contentRect.height);
124
- });
125
- });
126
- resizeObserver.observe(viewerContainer);
127
-
128
- window.addEventListener('resize', () => app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight));
129
- app.on('destroy', () => resizeObserver.disconnect());
130
-
131
- // --- ASSETS (GSplat + Orbit camera) ---
132
- const assets = {
133
- model: new pc.Asset('gsplat', 'gsplat', { url: sogsUrl }),
134
- orbit: new pc.Asset('orbit', 'script', { url: "https://mikafil-viewer-sgos.static.hf.space/orbit-camera.js" }),
135
- // Optionally add other assets here if you want
136
- };
137
- for (const key in assets) app.assets.add(assets[key]);
138
-
139
- const loader = new pc.AssetListLoader(Object.values(assets), app.assets);
140
- loader.load(() => {
141
- app.start();
142
- progressDialog.style.display = 'none';
143
-
144
- // --- Add GSplat Model ---
145
- modelEntity = new pc.Entity('model');
146
- modelEntity.addComponent('gsplat', { asset: assets.model });
147
- modelEntity.setLocalPosition(modelX, modelY, modelZ);
148
- modelEntity.setLocalEulerAngles(modelRotationX, modelRotationY, modelRotationZ);
149
- modelEntity.setLocalScale(modelScale, modelScale, modelScale);
150
- app.root.addChild(modelEntity);
151
-
152
- // --- Camera ---
153
- cameraEntity = new pc.Entity('camera');
154
- cameraEntity.addComponent('camera', {
155
- clearColor: new pc.Color(1, 1, 1, 1)
156
  });
157
- cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
158
- cameraEntity.lookAt(modelEntity.getPosition());
159
- cameraEntity.addComponent('script');
160
- cameraEntity.script.create('orbitCamera', {
161
- attributes: {
162
- focusEntity: modelEntity,
163
- inertiaFactor: 0.2,
164
- distanceMax: maxZoom,
165
- distanceMin: minZoom,
166
- pitchAngleMax: maxAngle,
167
- pitchAngleMin: minAngle,
168
- yawAngleMax: maxAzimuth,
169
- yawAngleMin: minAzimuth,
170
- minY: minY,
171
- frameOnStart: false
172
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  });
174
- cameraEntity.script.create('orbitCameraInputMouse');
175
- cameraEntity.script.create('orbitCameraInputTouch');
176
- app.root.addChild(cameraEntity);
177
-
178
- app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
179
- app.once('update', () => resetViewerCamera());
180
-
181
- // --- Tooltips ---
182
- try {
183
- if (config.tooltips_url) {
184
- import('./tooltips.js').then(tooltipsModule => {
185
- tooltipsModule.initializeTooltips({
186
- app,
187
- cameraEntity,
188
- modelEntity,
189
- tooltipsUrl: config.tooltips_url,
190
- defaultVisible: !!config.showTooltipsDefault,
191
- moveDuration: config.tooltipMoveDuration || 0.6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  });
193
- }).catch(e => {});
 
 
194
  }
195
- } catch (e) {}
196
 
197
- viewerInitialized = true;
198
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
200
 
201
  // Reset camera helper
202
  export function resetViewerCamera() {
203
  try {
204
- if (!cameraEntity || !modelEntity || !app) return;
 
 
 
 
205
  const orbitCam = cameraEntity.script.orbitCamera;
206
- if (!orbitCam) return;
 
 
 
207
 
208
  const modelPos = modelEntity.getPosition();
209
  const tempEnt = new pc.Entity();
@@ -240,7 +307,9 @@ export function resetViewerCamera() {
240
  if (orbitCam._updatePosition) orbitCam._updatePosition();
241
 
242
  tempEnt.destroy();
 
243
  } catch (e) {
 
244
  // Silent fail
245
  }
246
  }
 
14
  let modelX, modelY, modelZ, modelScale, modelRotationX, modelRotationY, modelRotationZ;
15
 
16
  export async function initializeViewer(config, instanceId) {
17
+ try {
18
+ alert('viewer.js: initializeViewer called');
19
+ if (viewerInitialized) {
20
+ alert('viewer.js: already initialized');
21
+ return;
22
+ }
23
+ if (!config) {
24
+ alert('viewer.js: config is missing!');
25
+ return;
26
+ }
27
+
28
+ // Params
29
+ const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
30
+ const isMobile = isIOS || /Android/i.test(navigator.userAgent);
31
+
32
+ // URLs and config
33
+ const sogsUrl = config.sogs_json_url;
34
+ if (!sogsUrl) {
35
+ alert('Missing SOGS (meta.json) URL in config.sogs_json_url.');
36
+ return;
37
+ }
38
+ alert('sogsUrl: ' + sogsUrl);
39
+
40
+ minZoom = parseFloat(config.minZoom || "1");
41
+ maxZoom = parseFloat(config.maxZoom || "20");
42
+ minAngle = parseFloat(config.minAngle || "-45");
43
+ maxAngle = parseFloat(config.maxAngle || "90");
44
+ minAzimuth = (config.minAzimuth !== undefined) ? parseFloat(config.minAzimuth) : -360;
45
+ maxAzimuth = (config.maxAzimuth !== undefined) ? parseFloat(config.maxAzimuth) : 360;
46
+ minPivotY = parseFloat(config.minPivotY || "0");
47
+ minY = (config.minY !== undefined) ? parseFloat(config.minY) : 0;
48
+
49
+ modelX = (config.modelX !== undefined) ? parseFloat(config.modelX) : 0;
50
+ modelY = (config.modelY !== undefined) ? parseFloat(config.modelY) : 0;
51
+ modelZ = (config.modelZ !== undefined) ? parseFloat(config.modelZ) : 0;
52
+ modelScale = (config.modelScale !== undefined) ? parseFloat(config.modelScale) : 1;
53
+ modelRotationX = (config.modelRotationX !== undefined) ? parseFloat(config.modelRotationX) : 0;
54
+ modelRotationY = (config.modelRotationY !== undefined) ? parseFloat(config.modelRotationY) : 0;
55
+ modelRotationZ = (config.modelRotationZ !== undefined) ? parseFloat(config.modelRotationZ) : 0;
56
+
57
+ const cameraX = (config.cameraX !== undefined) ? parseFloat(config.cameraX) : 0;
58
+ const cameraY = (config.cameraY !== undefined) ? parseFloat(config.cameraY) : 2;
59
+ const cameraZ = (config.cameraZ !== undefined) ? parseFloat(config.cameraZ) : 5;
60
+ const cameraXPhone = (config.cameraXPhone !== undefined) ? parseFloat(config.cameraXPhone) : cameraX;
61
+ const cameraYPhone = (config.cameraYPhone !== undefined) ? parseFloat(config.cameraYPhone) : cameraY;
62
+ const cameraZPhone = (config.cameraZPhone !== undefined) ? parseFloat(config.cameraZPhone) : (cameraZ * 1.5);
63
+
64
+ chosenCameraX = isMobile ? cameraXPhone : cameraX;
65
+ chosenCameraY = isMobile ? cameraYPhone : cameraY;
66
+ chosenCameraZ = isMobile ? cameraZPhone : cameraZ;
67
+
68
+ // DOM Elements
69
+ const canvasId = 'canvas-' + instanceId;
70
+ const progressDialog = document.getElementById('progress-dialog-' + instanceId);
71
+ const viewerContainer = document.getElementById('viewer-container-' + instanceId);
72
+
73
+ alert('viewer.js: DOM elements - canvasId: ' + canvasId);
74
+
75
+ if (!viewerContainer) {
76
+ alert('viewer.js: viewerContainer not found!');
77
+ return;
78
+ }
79
+
80
+ // Remove old canvas if any
81
+ let oldCanvas = document.getElementById(canvasId);
82
+ if (oldCanvas) oldCanvas.remove();
83
+
84
+ // Create and add canvas
85
+ const canvas = document.createElement('canvas');
86
+ canvas.id = canvasId;
87
+ canvas.className = 'ply-canvas';
88
+ canvas.style.width = "100%";
89
+ canvas.style.height = "100%";
90
+ canvas.setAttribute('tabindex', '0');
91
+ viewerContainer.insertBefore(canvas, progressDialog);
92
+ canvas.style.touchAction = "none";
93
+ canvas.style.webkitTouchCallout = "none";
94
+ canvas.addEventListener('gesturestart', e => e.preventDefault());
95
+ canvas.addEventListener('gesturechange', e => e.preventDefault());
96
+ canvas.addEventListener('gestureend', e => e.preventDefault());
97
+ canvas.addEventListener('dblclick', e => e.preventDefault());
98
+ canvas.addEventListener('touchstart', e => { if (e.touches.length > 1) e.preventDefault(); }, { passive: false });
99
+ canvas.addEventListener('wheel', (e) => { e.preventDefault(); }, { passive: false });
100
+
101
+ alert('viewer.js: Canvas created and inserted.');
102
+
103
+ if (progressDialog) {
104
+ progressDialog.style.display = 'block';
105
+ } else {
106
+ alert('viewer.js: progressDialog not found!');
107
+ }
108
+
109
+ // --- PlayCanvas Graphics Device ---
110
+ alert('viewer.js: creating graphics device...');
111
+ const device = await pc.createGraphicsDevice(canvas, {
112
+ deviceTypes: ["webgl2"],
113
+ antialias: false,
114
+ glslangUrl: "https://playcanvas.vercel.app/static/lib/glslang/glslang.js",
115
+ twgslUrl: "https://playcanvas.vercel.app/static/lib/twgsl/twgsl.js"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  });
117
+ device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
118
+ alert('viewer.js: Graphics device created!');
119
+
120
+ // --- PlayCanvas AppBase (modern, 2.8.1+) ---
121
+ alert('viewer.js: Creating AppBase...');
122
+ const appOpts = new pc.AppOptions();
123
+ appOpts.graphicsDevice = device;
124
+ appOpts.mouse = new pc.Mouse(canvas);
125
+ appOpts.touch = new pc.TouchDevice(canvas);
126
+ appOpts.componentSystems = [
127
+ pc.RenderComponentSystem,
128
+ pc.CameraComponentSystem,
129
+ pc.LightComponentSystem,
130
+ pc.ScriptComponentSystem,
131
+ pc.GSplatComponentSystem
132
+ ];
133
+ appOpts.resourceHandlers = [
134
+ pc.TextureHandler,
135
+ pc.ContainerHandler,
136
+ pc.ScriptHandler,
137
+ pc.GSplatHandler
138
+ ];
139
+
140
+ app = new pc.AppBase(canvas);
141
+ app.init(appOpts);
142
+
143
+ alert('viewer.js: AppBase created and initialized!');
144
+
145
+ // Fill/resize
146
+ app.setCanvasFillMode(pc.FILLMODE_NONE);
147
+ app.setCanvasResolution(pc.RESOLUTION_AUTO);
148
+
149
+ resizeObserver = new ResizeObserver(entries => {
150
+ entries.forEach(entry => {
151
+ app.resizeCanvas(entry.contentRect.width, entry.contentRect.height);
152
+ });
153
  });
154
+ resizeObserver.observe(viewerContainer);
155
+
156
+ window.addEventListener('resize', () => app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight));
157
+ app.on('destroy', () => resizeObserver.disconnect());
158
+
159
+ // --- ASSETS (GSplat + Orbit camera) ---
160
+ alert('viewer.js: Creating GSplat and orbit assets...');
161
+ const assets = {
162
+ model: new pc.Asset('gsplat', 'gsplat', { url: sogsUrl }),
163
+ orbit: new pc.Asset('orbit', 'script', { url: "https://mikafil-viewer-sgos.static.hf.space/orbit-camera.js" }),
164
+ };
165
+ for (const key in assets) app.assets.add(assets[key]);
166
+
167
+ alert('viewer.js: Assets created, starting AssetListLoader...');
168
+
169
+ const loader = new pc.AssetListLoader(Object.values(assets), app.assets);
170
+
171
+ loader.load(() => {
172
+ alert('viewer.js: AssetListLoader finished loading assets!');
173
+ app.start();
174
+ if (progressDialog) progressDialog.style.display = 'none';
175
+
176
+ // --- Add GSplat Model ---
177
+ alert('viewer.js: Creating modelEntity and adding gsplat...');
178
+ modelEntity = new pc.Entity('model');
179
+ modelEntity.addComponent('gsplat', { asset: assets.model });
180
+ modelEntity.setLocalPosition(modelX, modelY, modelZ);
181
+ modelEntity.setLocalEulerAngles(modelRotationX, modelRotationY, modelRotationZ);
182
+ modelEntity.setLocalScale(modelScale, modelScale, modelScale);
183
+ app.root.addChild(modelEntity);
184
+
185
+ // --- Camera ---
186
+ alert('viewer.js: Creating camera entity...');
187
+ cameraEntity = new pc.Entity('camera');
188
+ cameraEntity.addComponent('camera', {
189
+ clearColor: new pc.Color(1, 1, 1, 1)
190
+ });
191
+ cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
192
+ cameraEntity.lookAt(modelEntity.getPosition());
193
+ cameraEntity.addComponent('script');
194
+ cameraEntity.script.create('orbitCamera', {
195
+ attributes: {
196
+ focusEntity: modelEntity,
197
+ inertiaFactor: 0.2,
198
+ distanceMax: maxZoom,
199
+ distanceMin: minZoom,
200
+ pitchAngleMax: maxAngle,
201
+ pitchAngleMin: minAngle,
202
+ yawAngleMax: maxAzimuth,
203
+ yawAngleMin: minAzimuth,
204
+ minY: minY,
205
+ frameOnStart: false
206
+ }
207
+ });
208
+ cameraEntity.script.create('orbitCameraInputMouse');
209
+ cameraEntity.script.create('orbitCameraInputTouch');
210
+ app.root.addChild(cameraEntity);
211
+
212
+ alert('viewer.js: Camera entity created and added.');
213
+
214
+ app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
215
+ app.once('update', () => {
216
+ alert('viewer.js: calling resetViewerCamera');
217
+ resetViewerCamera();
218
+ });
219
+
220
+ // --- Tooltips ---
221
+ try {
222
+ if (config.tooltips_url) {
223
+ import('./tooltips.js').then(tooltipsModule => {
224
+ alert('viewer.js: tooltips.js loaded!');
225
+ tooltipsModule.initializeTooltips({
226
+ app,
227
+ cameraEntity,
228
+ modelEntity,
229
+ tooltipsUrl: config.tooltips_url,
230
+ defaultVisible: !!config.showTooltipsDefault,
231
+ moveDuration: config.tooltipMoveDuration || 0.6
232
+ });
233
+ }).catch(e => {
234
+ alert('viewer.js: Error loading tooltips.js: ' + e);
235
+ console.error(e);
236
  });
237
+ }
238
+ } catch (e) {
239
+ alert('viewer.js: Exception importing tooltips: ' + e);
240
  }
 
241
 
242
+ viewerInitialized = true;
243
+ alert('viewer.js: viewerInitialized = true!');
244
+ });
245
+
246
+ loader.on('error', function(err, asset) {
247
+ alert('viewer.js: Asset loader error: ' + err + (asset ? ' for asset: ' + asset.name : ''));
248
+ console.error('Asset loader error:', err, asset);
249
+ });
250
+
251
+ loader.on('progress', function(loaded, total) {
252
+ console.log(`viewer.js: Asset loader progress: ${loaded} / ${total}`);
253
+ });
254
+
255
+ } catch (e) {
256
+ alert('viewer.js: FATAL exception: ' + (e.message || e));
257
+ console.error(e);
258
+ }
259
  }
260
 
261
  // Reset camera helper
262
  export function resetViewerCamera() {
263
  try {
264
+ alert('viewer.js: resetViewerCamera called');
265
+ if (!cameraEntity || !modelEntity || !app) {
266
+ alert('resetViewerCamera: cameraEntity/modelEntity/app missing');
267
+ return;
268
+ }
269
  const orbitCam = cameraEntity.script.orbitCamera;
270
+ if (!orbitCam) {
271
+ alert('resetViewerCamera: orbitCamera script not found!');
272
+ return;
273
+ }
274
 
275
  const modelPos = modelEntity.getPosition();
276
  const tempEnt = new pc.Entity();
 
307
  if (orbitCam._updatePosition) orbitCam._updatePosition();
308
 
309
  tempEnt.destroy();
310
+ alert('resetViewerCamera: success');
311
  } catch (e) {
312
+ alert('resetViewerCamera: Exception: ' + (e.message || e));
313
  // Silent fail
314
  }
315
  }