MikaFil commited on
Commit
e2562fe
·
verified ·
1 Parent(s): 61cac23

Update viewer.js

Browse files
Files changed (1) hide show
  1. viewer.js +66 -41
viewer.js CHANGED
@@ -11,7 +11,7 @@ let resizeObserver = null;
11
  let chosenCameraX, chosenCameraY, chosenCameraZ;
12
  let minZoom, maxZoom, minAngle, maxAngle, minAzimuth, maxAzimuth, minPivotY, minY;
13
  let modelX, modelY, modelZ, modelScale, modelRotationX, modelRotationY, modelRotationZ;
14
- let plyUrl, glbUrl;
15
 
16
  export async function initializeViewer(config, instanceId) {
17
  if (viewerInitialized) return;
@@ -19,8 +19,12 @@ export async function initializeViewer(config, instanceId) {
19
  const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
20
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
21
 
22
- plyUrl = config.ply_url;
23
- glbUrl = config.glb_url;
 
 
 
 
24
  minZoom = parseFloat(config.minZoom || "1");
25
  maxZoom = parseFloat(config.maxZoom || "20");
26
  minAngle = parseFloat(config.minAngle || "-45");
@@ -73,9 +77,8 @@ export async function initializeViewer(config, instanceId) {
73
  canvas.addEventListener('dblclick', e => e.preventDefault());
74
  canvas.addEventListener('touchstart', e => { if (e.touches.length > 1) e.preventDefault(); }, { passive: false });
75
 
76
- // --- The following line attaches mouse wheel suppression to canvas only ---
77
  canvas.addEventListener('wheel', (e) => {
78
- e.preventDefault(); // Only block page scroll if mouse is over viewer
79
  }, { passive: false });
80
 
81
  progressDialog.style.display = 'block';
@@ -85,7 +88,7 @@ export async function initializeViewer(config, instanceId) {
85
  window.pc = pc;
86
  }
87
 
88
- // Create app first
89
  const device = await pc.createGraphicsDevice(canvas, {
90
  deviceTypes: ["webgl2"],
91
  glslangUrl: "https://playcanvas.vercel.app/static/lib/glslang/glslang.js",
@@ -96,7 +99,6 @@ export async function initializeViewer(config, instanceId) {
96
 
97
  const opts = new pc.AppOptions();
98
  opts.graphicsDevice = device;
99
- // Attach input only to canvas
100
  opts.mouse = new pc.Mouse(canvas);
101
  opts.touch = new pc.TouchDevice(canvas);
102
  opts.componentSystems = [
@@ -104,15 +106,13 @@ export async function initializeViewer(config, instanceId) {
104
  pc.CameraComponentSystem,
105
  pc.LightComponentSystem,
106
  pc.ScriptComponentSystem,
107
- pc.GSplatComponentSystem,
108
  pc.CollisionComponentSystem,
109
  pc.RigidbodyComponentSystem
110
  ];
111
  opts.resourceHandlers = [
112
  pc.TextureHandler,
113
  pc.ContainerHandler,
114
- pc.ScriptHandler,
115
- pc.GSplatHandler
116
  ];
117
 
118
  app = new pc.Application(canvas, opts);
@@ -129,41 +129,71 @@ export async function initializeViewer(config, instanceId) {
129
  window.addEventListener('resize', () => app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight));
130
  app.on('destroy', () => resizeObserver.disconnect());
131
 
132
- // Assets after app exists
133
- const assets = {
134
- model: new pc.Asset('gsplat', 'gsplat', { url: plyUrl }),
135
- orbit: new pc.Asset('script', 'script', { url: "https://mikafil-viewer-gs.static.hf.space/orbit-camera.js" }),
136
- glb: new pc.Asset('glb', 'container', { url: glbUrl }),
137
- };
138
- for (const key in assets) app.assets.add(assets[key]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
- const loader = new pc.AssetListLoader(Object.values(assets), app.assets);
141
- loader.load(() => {
142
  app.start();
143
  progressDialog.style.display = 'none';
144
 
145
- // Add PLY/GSplat model
146
  modelEntity = new pc.Entity('model');
147
- modelEntity.addComponent('gsplat', { asset: assets.model });
 
 
 
 
 
 
 
 
 
 
 
148
  modelEntity.setLocalPosition(modelX, modelY, modelZ);
149
  modelEntity.setLocalEulerAngles(modelRotationX, modelRotationY, modelRotationZ);
150
  modelEntity.setLocalScale(modelScale, modelScale, modelScale);
151
  app.root.addChild(modelEntity);
152
 
153
- // --- Add the GLB entity ---
154
- if (assets.glb && assets.glb.resource && assets.glb.resource.instantiateRenderEntity) {
155
- const glbEntity = assets.glb.resource.instantiateRenderEntity();
156
- app.root.addChild(glbEntity);
157
- }
158
-
159
  cameraEntity = new pc.Entity('camera');
160
- // PURE WHITE BACKGROUND
161
- cameraEntity.addComponent('camera', { clearColor: new pc.Color(1, 1, 1, 1) }); // White RGBA
162
  cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
163
  cameraEntity.lookAt(modelEntity.getPosition());
164
  cameraEntity.addComponent('script');
165
 
166
- // === MAIN FIX: Pass ALL relevant attributes including minY ===
167
  cameraEntity.script.create('orbitCamera', {
168
  attributes: {
169
  focusEntity: modelEntity,
@@ -183,10 +213,9 @@ export async function initializeViewer(config, instanceId) {
183
  app.root.addChild(cameraEntity);
184
 
185
  app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
186
-
187
  app.once('update', () => resetViewerCamera());
188
 
189
- // Tooltips supported if tooltips_url set
190
  try {
191
  if (config.tooltips_url) {
192
  import('./tooltips.js').then(tooltipsModule => {
@@ -198,18 +227,16 @@ export async function initializeViewer(config, instanceId) {
198
  defaultVisible: !!config.showTooltipsDefault,
199
  moveDuration: config.tooltipMoveDuration || 0.6
200
  });
201
- }).catch(e => {
202
- // Tooltips optional: fail silently if missing
203
- });
204
  }
205
- } catch (e) {
206
- // Tooltips optional, fail silently
207
- }
208
 
209
  viewerInitialized = true;
210
  });
 
211
  }
212
 
 
213
  export function resetViewerCamera() {
214
  try {
215
  if (!cameraEntity || !modelEntity || !app) return;
@@ -251,7 +278,5 @@ export function resetViewerCamera() {
251
  if (orbitCam._updatePosition) orbitCam._updatePosition();
252
 
253
  tempEnt.destroy();
254
- } catch (e) {
255
- // Silent fail
256
- }
257
  }
 
11
  let chosenCameraX, chosenCameraY, chosenCameraZ;
12
  let minZoom, maxZoom, minAngle, maxAngle, minAzimuth, maxAzimuth, minPivotY, minY;
13
  let modelX, modelY, modelZ, modelScale, modelRotationX, modelRotationY, modelRotationZ;
14
+ let sogsJsonUrl;
15
 
16
  export async function initializeViewer(config, instanceId) {
17
  if (viewerInitialized) return;
 
19
  const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
20
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
21
 
22
+ // SOGS usage: we expect config.sogs_json_url
23
+ sogsJsonUrl = config.sogs_json_url;
24
+ if (!sogsJsonUrl) {
25
+ throw new Error('Missing config.sogs_json_url for optimized SOGS/webp loading.');
26
+ }
27
+
28
  minZoom = parseFloat(config.minZoom || "1");
29
  maxZoom = parseFloat(config.maxZoom || "20");
30
  minAngle = parseFloat(config.minAngle || "-45");
 
77
  canvas.addEventListener('dblclick', e => e.preventDefault());
78
  canvas.addEventListener('touchstart', e => { if (e.touches.length > 1) e.preventDefault(); }, { passive: false });
79
 
 
80
  canvas.addEventListener('wheel', (e) => {
81
+ e.preventDefault();
82
  }, { passive: false });
83
 
84
  progressDialog.style.display = 'block';
 
88
  window.pc = pc;
89
  }
90
 
91
+ // Create PlayCanvas app
92
  const device = await pc.createGraphicsDevice(canvas, {
93
  deviceTypes: ["webgl2"],
94
  glslangUrl: "https://playcanvas.vercel.app/static/lib/glslang/glslang.js",
 
99
 
100
  const opts = new pc.AppOptions();
101
  opts.graphicsDevice = device;
 
102
  opts.mouse = new pc.Mouse(canvas);
103
  opts.touch = new pc.TouchDevice(canvas);
104
  opts.componentSystems = [
 
106
  pc.CameraComponentSystem,
107
  pc.LightComponentSystem,
108
  pc.ScriptComponentSystem,
 
109
  pc.CollisionComponentSystem,
110
  pc.RigidbodyComponentSystem
111
  ];
112
  opts.resourceHandlers = [
113
  pc.TextureHandler,
114
  pc.ContainerHandler,
115
+ pc.ScriptHandler
 
116
  ];
117
 
118
  app = new pc.Application(canvas, opts);
 
129
  window.addEventListener('resize', () => app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight));
130
  app.on('destroy', () => resizeObserver.disconnect());
131
 
132
+ // Load orbit camera scripts
133
+ const orbitAsset = new pc.Asset('orbit', 'script', { url: "https://mikafil-viewer-gs.static.hf.space/orbit-camera.js" });
134
+ app.assets.add(orbitAsset);
135
+
136
+ // Load SOGS json (contains metadata and webp references)
137
+ let sogsData, sogsTextures = {};
138
+ try {
139
+ const jsonResp = await fetch(sogsJsonUrl);
140
+ sogsData = await jsonResp.json();
141
+ if (!Array.isArray(sogsData.images)) throw new Error('sogs_json missing .images array');
142
+ // Preload all webp textures as PlayCanvas textures
143
+ for (let i = 0; i < sogsData.images.length; ++i) {
144
+ const imageInfo = sogsData.images[i];
145
+ let url = imageInfo.path || imageInfo.url;
146
+ if (!/^https?:\/\//.test(url) && sogsJsonUrl) {
147
+ // resolve relative to sogsJsonUrl
148
+ const baseUrl = sogsJsonUrl.split('/').slice(0, -1).join('/');
149
+ url = `${baseUrl}/${url}`;
150
+ }
151
+ // Load texture asset synchronously (block start until all images ready)
152
+ sogsTextures[imageInfo.name || `img${i}`] = await new Promise((resolve, reject) => {
153
+ const asset = new pc.Asset(imageInfo.name || `img${i}`, 'texture', { url });
154
+ app.assets.add(asset);
155
+ asset.ready(resolve.bind(null, asset.resource));
156
+ asset.on('error', reject);
157
+ app.assets.load(asset);
158
+ });
159
+ }
160
+ } catch (e) {
161
+ progressDialog.style.display = 'none';
162
+ alert("Failed to load SOGS JSON/images: " + e.message);
163
+ throw e;
164
+ }
165
 
166
+ // Continue once orbit script is loaded
167
+ orbitAsset.ready(async () => {
168
  app.start();
169
  progressDialog.style.display = 'none';
170
 
171
+ // Add the SOGS/webp point cloud as a "gsplat" entity/component
172
  modelEntity = new pc.Entity('model');
173
+
174
+ // Here you would use your SOGS-compatible GSplat renderer component.
175
+ // Let's define a placeholder `gsplat` script which you must replace
176
+ // with your PlayCanvas SOGS/gsplat renderer.
177
+ modelEntity.addComponent('script');
178
+ modelEntity.script.create('gsplat', {
179
+ attributes: {
180
+ sogsData: sogsData,
181
+ textures: sogsTextures
182
+ }
183
+ });
184
+
185
  modelEntity.setLocalPosition(modelX, modelY, modelZ);
186
  modelEntity.setLocalEulerAngles(modelRotationX, modelRotationY, modelRotationZ);
187
  modelEntity.setLocalScale(modelScale, modelScale, modelScale);
188
  app.root.addChild(modelEntity);
189
 
190
+ // Camera entity
 
 
 
 
 
191
  cameraEntity = new pc.Entity('camera');
192
+ cameraEntity.addComponent('camera', { clearColor: new pc.Color(1, 1, 1, 1) }); // White BG
 
193
  cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
194
  cameraEntity.lookAt(modelEntity.getPosition());
195
  cameraEntity.addComponent('script');
196
 
 
197
  cameraEntity.script.create('orbitCamera', {
198
  attributes: {
199
  focusEntity: modelEntity,
 
213
  app.root.addChild(cameraEntity);
214
 
215
  app.resizeCanvas(viewerContainer.clientWidth, viewerContainer.clientHeight);
 
216
  app.once('update', () => resetViewerCamera());
217
 
218
+ // Optional: tooltips
219
  try {
220
  if (config.tooltips_url) {
221
  import('./tooltips.js').then(tooltipsModule => {
 
227
  defaultVisible: !!config.showTooltipsDefault,
228
  moveDuration: config.tooltipMoveDuration || 0.6
229
  });
230
+ }).catch(e => {});
 
 
231
  }
232
+ } catch (e) {}
 
 
233
 
234
  viewerInitialized = true;
235
  });
236
+ app.assets.load(orbitAsset);
237
  }
238
 
239
+ // Reset camera helper, unchanged from PLY version
240
  export function resetViewerCamera() {
241
  try {
242
  if (!cameraEntity || !modelEntity || !app) return;
 
278
  if (orbitCam._updatePosition) orbitCam._updatePosition();
279
 
280
  tempEnt.destroy();
281
+ } catch (e) {}
 
 
282
  }