bilca commited on
Commit
2bd5a64
·
verified ·
1 Parent(s): 1a9f805

Update js_scripts/index.js

Browse files
Files changed (1) hide show
  1. js_scripts/index.js +53 -494
js_scripts/index.js CHANGED
@@ -1,507 +1,66 @@
1
- (async function() {
2
- // Retrieve the current script tag and load the JSON configuration file from the data-config attribute.
3
- const scriptTag = document.currentScript;
4
- const configUrl = scriptTag.getAttribute("data-config");
5
- let config = {};
6
- if (configUrl) {
7
- try {
8
- const response = await fetch(configUrl);
9
- config = await response.json();
10
- } catch (error) {
11
- console.error("Error loading config file:", error);
12
- return;
13
- }
14
- } else {
15
- console.error("No config file provided. Please set a data-config attribute on the script tag.");
16
- return;
17
- }
18
 
19
- // Load the external CSS file if provided in the config.
20
- if (config.css_url) {
21
- const linkEl = document.createElement("link");
22
- linkEl.rel = "stylesheet";
23
- linkEl.href = config.css_url;
24
- document.head.appendChild(linkEl);
25
- }
26
 
27
- // --- Outer scope variables for camera state ---
28
- let cameraInstance = null;
29
- let controlsInstance = null;
30
- let initialCameraPosition = null;
31
- let initialCameraRotation = null;
32
- let rendererInstance = null;
33
- let sceneInstance = null;
34
- let SPLAT = null; // We'll save the imported SPLAT module here.
35
- let animFrameId = null;
36
- let resizeHandler = null;
37
- let hasInitializedViewer = false;
38
- let loadedPlyModel = null;
39
-
40
- // Generate a unique identifier for this widget instance.
41
- const instanceId = Math.random().toString(36).substr(2, 8);
42
-
43
- // Read configuration values from the JSON file.
44
- const gifUrl = config.gif_url;
45
- const plyUrl = config.ply_url;
46
- const minZoom = parseFloat(config.minZoom || "0");
47
- const maxZoom = parseFloat(config.maxZoom || "20");
48
- const minAngle = parseFloat(config.minAngle || "0");
49
- const maxAngle = parseFloat(config.maxAngle || "360");
50
- const minAzimuth = config.minAzimuth !== undefined ? parseFloat(config.minAzimuth) : -Infinity;
51
- const maxAzimuth = config.maxAzimuth !== undefined ? parseFloat(config.maxAzimuth) : Infinity;
52
-
53
- // Read initial orbit parameters for desktop.
54
- const initAlphaDesktop = config.initAlpha !== undefined ? parseFloat(config.initAlpha) : 0.5;
55
- const initBetaDesktop = config.initBeta !== undefined ? parseFloat(config.initBeta) : 0.5;
56
- const initRadiusDesktop = config.initRadius !== undefined ? parseFloat(config.initRadius) : 5;
57
- // Read initial orbit parameters for phone.
58
- const initAlphaPhone = config.initAlphaPhone !== undefined ? parseFloat(config.initAlphaPhone) : initAlphaDesktop;
59
- const initBetaPhone = config.initBetaPhone !== undefined ? parseFloat(config.initBetaPhone) : initBetaDesktop;
60
- const initRadiusPhone = config.initRadiusPhone !== undefined ? parseFloat(config.initRadiusPhone) : initRadiusDesktop;
61
 
62
- // Detect if the device is iOS.
63
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
64
- // Also detect Android devices.
65
- const isMobile = isIOS || /Android/i.test(navigator.userAgent);
66
-
67
- // Choose the appropriate initial orbit values based on device type.
68
- const chosenInitAlpha = isMobile ? initAlphaPhone : initAlphaDesktop;
69
- const chosenInitBeta = isMobile ? initBetaPhone : initBetaDesktop;
70
- const chosenInitRadius = isMobile ? initRadiusPhone : initRadiusDesktop;
71
-
72
- // Determine the aspect ratio.
73
- let aspectPercent = "100%";
74
- if (config.aspect) {
75
- if (config.aspect.indexOf(":") !== -1) {
76
- const parts = config.aspect.split(":");
77
- const w = parseFloat(parts[0]);
78
- const h = parseFloat(parts[1]);
79
- if (!isNaN(w) && !isNaN(h) && w > 0) {
80
- aspectPercent = (h / w * 100) + "%";
81
- }
82
- } else {
83
- const aspectValue = parseFloat(config.aspect);
84
- if (!isNaN(aspectValue) && aspectValue > 0) {
85
- aspectPercent = (100 / aspectValue) + "%";
86
- }
87
- }
88
- } else {
89
- const parentContainer = scriptTag.parentNode;
90
- const containerWidth = parentContainer.offsetWidth;
91
- const containerHeight = parentContainer.offsetHeight;
92
- if (containerWidth > 0 && containerHeight > 0) {
93
- aspectPercent = (containerHeight / containerWidth * 100) + "%";
94
- }
95
- }
96
-
97
- // Create the widget container.
98
- const widgetContainer = document.createElement('div');
99
- widgetContainer.id = 'ply-widget-container-' + instanceId;
100
- widgetContainer.classList.add('ply-widget-container');
101
- // Add a mobile class if on a phone.
102
- if (isMobile) {
103
- widgetContainer.classList.add('mobile');
104
- }
105
- // Set inline style for aspect ratio.
106
- widgetContainer.style.height = "0";
107
- widgetContainer.style.paddingBottom = aspectPercent;
108
 
109
- widgetContainer.innerHTML = `
110
- <!-- GIF Preview Container -->
111
- <div id="gif-preview-container-${instanceId}" class="gif-preview-container">
112
- <img id="preview-image-${instanceId}" alt="Preview" crossorigin="anonymous">
113
- </div>
114
- <!-- Viewer Container -->
115
- <div id="viewer-container-${instanceId}" class="viewer-container" style="display: none;">
116
- <canvas id="canvas-${instanceId}" class="ply-canvas"></canvas>
117
- <div id="progress-dialog-${instanceId}" class="progress-dialog">
118
- <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
119
- </div>
120
- <button id="close-btn-${instanceId}" class="widget-button close-btn">X</button>
121
- <button id="fullscreen-toggle-${instanceId}" class="widget-button fullscreen-toggle">⇱</button>
122
- <button id="help-toggle-${instanceId}" class="widget-button help-toggle">?</button>
123
- <button id="reset-camera-btn-${instanceId}" class="widget-button reset-camera-btn">
124
- <span class="reset-icon">⟲</span>
125
- </button>
126
- <div id="menu-content-${instanceId}" class="menu-content"></div>
127
- </div>
128
- `;
129
- scriptTag.parentNode.appendChild(widgetContainer);
130
-
131
- // Grab element references.
132
- const gifPreview = document.getElementById('gif-preview-container-' + instanceId);
133
- const viewerContainer = document.getElementById('viewer-container-' + instanceId);
134
- const previewImage = document.getElementById('preview-image-' + instanceId);
135
- const closeBtn = document.getElementById('close-btn-' + instanceId);
136
- const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
137
- const helpToggle = document.getElementById('help-toggle-' + instanceId);
138
- const resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
139
- const menuContent = document.getElementById('menu-content-' + instanceId);
140
- const canvas = document.getElementById('canvas-' + instanceId);
141
- const progressDialog = document.getElementById('progress-dialog-' + instanceId);
142
- const progressIndicator = document.getElementById('progress-indicator-' + instanceId);
143
-
144
- // Set help instructions based on device type.
145
- if (isMobile) {
146
- menuContent.innerHTML = `
147
- - Pour vous déplacer, glissez deux doigts sur l'écran.<br>
148
- - Pour orbiter, utilisez un doigt.<br>
149
- - Pour zoomer, pincez avec deux doigts.
150
- `;
151
- } else {
152
- menuContent.innerHTML = `
153
- - orbitez avec le clic droit<br>
154
- - zoomez avec la molette<br>
155
- - déplacez vous avec le clic gauche
156
- `;
157
- }
158
-
159
- // If a gif_url is provided, set the preview image.
160
- // Otherwise, hide the preview container, show the viewer immediately,
161
- // and hide the "close" button since there's no preview to return to.
162
- if (gifUrl) {
163
- previewImage.src = gifUrl;
164
- gifPreview.style.display = 'block';
165
- viewerContainer.style.display = 'none';
166
- } else {
167
- gifPreview.style.display = 'none';
168
- viewerContainer.style.display = 'block';
169
- closeBtn.style.display = 'none';
170
- setTimeout(() => {
171
- initializeViewer();
172
- }, 100);
173
- }
174
-
175
- // --- Button Event Handlers ---
176
- if (gifUrl) {
177
- gifPreview.addEventListener('click', function() {
178
- console.log("GIF preview clicked, showing 3D viewer");
179
- gifPreview.style.display = 'none';
180
- viewerContainer.style.display = 'block';
181
- initializeViewer();
182
- });
183
- }
184
-
185
- // Function to clean up the viewer
186
- function cleanupViewer() {
187
- console.log("Cleaning up viewer resources...");
188
-
189
- // Stop animation frame
190
- if (animFrameId) {
191
- cancelAnimationFrame(animFrameId);
192
- animFrameId = null;
193
- }
194
-
195
- // Remove resize event handler
196
- if (resizeHandler) {
197
- window.removeEventListener("resize", resizeHandler);
198
- resizeHandler = null;
199
- }
200
-
201
- // Dispose controls
202
- if (controlsInstance && typeof controlsInstance.dispose === 'function') {
203
- try {
204
- controlsInstance.dispose();
205
- controlsInstance = null;
206
- } catch (e) {
207
- console.error("Error disposing controls:", e);
208
- }
209
- }
210
-
211
- // Clear references but preserve loaded objects
212
- // We don't fully dispose the renderer to preserve WebGL context
213
- rendererInstance = null;
214
- sceneInstance = null;
215
- cameraInstance = null;
216
- initialCameraPosition = null;
217
- initialCameraRotation = null;
218
 
219
- // Mark viewer as not initialized
220
- hasInitializedViewer = false;
221
 
222
- console.log("Viewer cleanup complete - renderer and PLY model data preserved for fast reload");
223
- }
224
-
225
- closeBtn.addEventListener('click', function() {
226
- console.log("Close button clicked");
227
 
228
- // Handle fullscreen exit
229
- if (document.fullscreenElement === widgetContainer) {
230
- if (document.exitFullscreen) {
231
- document.exitFullscreen();
232
- }
233
- }
234
- if (widgetContainer.classList.contains('fake-fullscreen')) {
235
- widgetContainer.classList.remove('fake-fullscreen');
236
- fullscreenToggle.textContent = '⇱';
237
- }
238
 
239
- // Clean up the viewer
240
- cleanupViewer();
 
 
 
241
 
242
- // Hide viewer and show GIF
243
- viewerContainer.style.display = 'none';
244
- gifPreview.style.display = 'block';
245
- });
246
-
247
- fullscreenToggle.addEventListener('click', function() {
248
- if (isIOS) {
249
- if (!widgetContainer.classList.contains('fake-fullscreen')) {
250
- widgetContainer.classList.add('fake-fullscreen');
251
- } else {
252
- widgetContainer.classList.remove('fake-fullscreen');
253
- resetCamera();
254
- }
255
- fullscreenToggle.textContent = widgetContainer.classList.contains('fake-fullscreen') ? '⇲' : '⇱';
256
- } else {
257
- if (!document.fullscreenElement) {
258
- if (widgetContainer.requestFullscreen) {
259
- widgetContainer.requestFullscreen();
260
- } else if (widgetContainer.webkitRequestFullscreen) {
261
- widgetContainer.webkitRequestFullscreen();
262
- } else if (widgetContainer.mozRequestFullScreen) {
263
- widgetContainer.mozRequestFullScreen();
264
- } else if (widgetContainer.msRequestFullscreen) {
265
- widgetContainer.msRequestFullscreen();
266
- }
267
- } else {
268
- if (document.exitFullscreen) {
269
- document.exitFullscreen();
270
- }
271
- }
272
- }
273
- });
274
-
275
- // Listen for native fullscreen changes.
276
- document.addEventListener('fullscreenchange', function() {
277
- if (document.fullscreenElement === widgetContainer) {
278
- fullscreenToggle.textContent = '⇲';
279
- widgetContainer.style.height = '100%';
280
- widgetContainer.style.paddingBottom = '0';
281
- resetCamera();
282
- } else {
283
- fullscreenToggle.textContent = '⇱';
284
- widgetContainer.style.height = '0';
285
- widgetContainer.style.paddingBottom = aspectPercent;
286
- resetCamera();
287
- }
288
- });
289
-
290
- helpToggle.addEventListener('click', function(e) {
291
- e.stopPropagation();
292
- menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
293
- });
294
-
295
- // --- Camera Reset Function ---
296
- function resetCamera() {
297
- console.log("Resetting camera to initial position");
298
- if (!cameraInstance || !SPLAT) {
299
- console.log("Cannot reset camera - missing camera instance");
300
- return;
301
- }
302
 
303
- try {
304
- // Create a new camera to reset its position
305
- cameraInstance = new SPLAT.Camera();
306
-
307
- // Dispose previous controls if they exist
308
- if (controlsInstance && typeof controlsInstance.dispose === 'function') {
309
- controlsInstance.dispose();
310
- }
311
-
312
- // Create new controls
313
- controlsInstance = new SPLAT.OrbitControls(
314
- cameraInstance,
315
- canvas,
316
- 0.5,
317
- 0.5,
318
- 5,
319
- true,
320
- new SPLAT.Vector3(),
321
- chosenInitAlpha,
322
- chosenInitBeta,
323
- chosenInitRadius
324
- );
325
-
326
- // Set control constraints
327
- controlsInstance.maxZoom = maxZoom;
328
- controlsInstance.minZoom = minZoom;
329
- controlsInstance.minAngle = minAngle;
330
- controlsInstance.maxAngle = maxAngle;
331
- controlsInstance.minAzimuth = minAzimuth;
332
- controlsInstance.maxAzimuth = maxAzimuth;
333
- controlsInstance.panSpeed = isMobile ? 0.5 : 1.2;
334
-
335
- // Update controls
336
- controlsInstance.update();
337
-
338
- console.log("Camera reset successful");
339
- } catch (error) {
340
- console.error("Error resetting camera:", error);
341
- }
342
- }
343
-
344
- resetCameraBtn.addEventListener('click', async function() {
345
- console.log("Reset camera button clicked");
346
- resetCamera();
347
- });
348
-
349
- document.addEventListener('keydown', function(e) {
350
- if (e.key === 'Escape' || e.key === 'Esc') {
351
- let wasFullscreen = false;
352
- if (document.fullscreenElement === widgetContainer) {
353
- wasFullscreen = true;
354
- if (document.exitFullscreen) {
355
- document.exitFullscreen();
356
- }
357
- }
358
- if (widgetContainer.classList.contains('fake-fullscreen')) {
359
- wasFullscreen = true;
360
- widgetContainer.classList.remove('fake-fullscreen');
361
- fullscreenToggle.textContent = '⇱';
362
- }
363
- if (wasFullscreen) {
364
- resetCamera();
365
- }
366
- }
367
- });
368
-
369
- // --- Initialize the 3D PLY Viewer ---
370
- async function initializeViewer() {
371
- // Skip initialization if already initialized
372
- if (hasInitializedViewer) {
373
- console.log("Viewer already initialized, skipping initialization");
374
- return;
375
- }
376
 
377
- console.log("Initializing PLY viewer...");
378
- progressDialog.style.display = 'block';
379
- progressIndicator.value = 0;
380
 
381
- try {
382
- // Load the SPLAT library if not already loaded
383
- if (!SPLAT) {
384
- console.log("Loading SPLAT library...");
385
- SPLAT = await import("https://bilca-gsplat-library.static.hf.space/dist/index.js");
386
- console.log("SPLAT library loaded successfully");
387
- }
388
-
389
- // Create new renderer or clear existing one
390
- console.log("Creating WebGL renderer...");
391
- rendererInstance = new SPLAT.WebGLRenderer(canvas);
392
-
393
- // Create new scene
394
- console.log("Creating scene...");
395
- sceneInstance = new SPLAT.Scene();
396
-
397
- // Create camera
398
- console.log("Creating camera...");
399
- cameraInstance = new SPLAT.Camera();
400
-
401
- // Store initial camera state
402
- initialCameraPosition = cameraInstance.position.clone();
403
- initialCameraRotation = cameraInstance.rotation.clone();
404
-
405
- // Set canvas background
406
- canvas.style.background = config.canvas_background || "#FEFEFD";
407
-
408
- // Create controls
409
- console.log("Creating orbit controls...");
410
- controlsInstance = new SPLAT.OrbitControls(
411
- cameraInstance,
412
- canvas,
413
- 0.5,
414
- 0.5,
415
- 5,
416
- true,
417
- new SPLAT.Vector3(),
418
- chosenInitAlpha,
419
- chosenInitBeta,
420
- chosenInitRadius
421
- );
422
-
423
- // Set control constraints
424
- controlsInstance.maxZoom = maxZoom;
425
- controlsInstance.minZoom = minZoom;
426
- controlsInstance.minAngle = minAngle;
427
- controlsInstance.maxAngle = maxAngle;
428
- controlsInstance.minAzimuth = minAzimuth;
429
- controlsInstance.maxAzimuth = maxAzimuth;
430
- controlsInstance.panSpeed = isMobile ? 0.5 : 1.2;
431
-
432
- controlsInstance.update();
433
-
434
- // Handle resize
435
- const handleResize = () => {
436
- if (rendererInstance) {
437
- rendererInstance.setSize(canvas.clientWidth, canvas.clientHeight);
438
- }
439
- };
440
-
441
- // Set up resize handler
442
- handleResize();
443
- resizeHandler = handleResize;
444
- window.addEventListener("resize", resizeHandler);
445
-
446
- // Load PLY file if not already loaded or reload it
447
- console.log("Loading or reusing PLY model...");
448
-
449
- try {
450
- // Always load fresh PLY data
451
- console.log(`Loading PLY file from ${plyUrl}`);
452
- loadedPlyModel = await SPLAT.PLYLoader.LoadAsync(
453
- plyUrl,
454
- sceneInstance,
455
- (progress) => {
456
- progressIndicator.value = progress * 100;
457
- }
458
- );
459
- console.log("PLY file loaded successfully, model:", loadedPlyModel);
460
- } catch (error) {
461
- console.error("Error loading PLY file:", error);
462
- progressDialog.innerHTML = `<p style="color: red">Error loading model: ${error.message}</p>`;
463
- return;
464
- }
465
-
466
- // Hide progress dialog
467
- progressDialog.style.display = 'none';
468
-
469
- // Debug: Check scene content
470
- console.log("Scene contents:", sceneInstance);
471
- if (sceneInstance && typeof sceneInstance.getChildren === 'function') {
472
- const children = sceneInstance.getChildren();
473
- console.log("Scene children:", children);
474
- if (children && children.length === 0) {
475
- console.warn("Warning: Scene has no children!");
476
- }
477
- } else {
478
- console.log("Cannot check scene children - getChildren method not available");
479
- }
480
-
481
- // Start animation loop
482
- console.log("Starting animation loop...");
483
- function animate() {
484
- if (!controlsInstance || !sceneInstance || !cameraInstance || !rendererInstance) {
485
- console.log("Animation stopped - resources cleaned up");
486
- return;
487
- }
488
-
489
- controlsInstance.update();
490
- rendererInstance.render(sceneInstance, cameraInstance);
491
- animFrameId = requestAnimationFrame(animate);
492
- }
493
-
494
- // Start the animation
495
- animFrameId = requestAnimationFrame(animate);
496
-
497
- // Mark viewer as initialized
498
- hasInitializedViewer = true;
499
- console.log("PLY viewer initialization complete");
500
-
501
- } catch (error) {
502
- console.error("Error initializing PLY viewer:", error);
503
- progressDialog.innerHTML = `<p style="color: red">Error initializing viewer: ${error.message}</p>`;
504
- hasInitializedViewer = false;
505
- }
506
  }
507
- })();
 
1
+ // Fix for cameraXPhone not working on mobile devices in PlayCanvas implementation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ // The key issue is in the camera setup section of your PlayCanvas viewer
4
+ // Make sure these changes are applied to the camera creation code
5
+ // Look for the section where cameraEntity is created and positioned
 
 
 
 
6
 
7
+ // Create camera entity
8
+ cameraEntity = new pc.Entity('camera');
9
+ cameraEntity.addComponent('camera', {
10
+ clearColor: new pc.Color(
11
+ config.canvas_background ? parseInt(config.canvas_background.substr(1, 2), 16) / 255 : 0,
12
+ config.canvas_background ? parseInt(config.canvas_background.substr(3, 2), 16) / 255 : 0,
13
+ config.canvas_background ? parseInt(config.canvas_background.substr(5, 2), 16) / 255 : 0
14
+ ),
15
+ toneMapping: pc.TONEMAP_ACES
16
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ // Log the chosen camera position for debugging
19
+ console.log(`Setting camera position for ${isMobile ? 'mobile' : 'desktop'}: (${chosenCameraX}, ${chosenCameraY}, ${chosenCameraZ})`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ // Set camera position directly using X, Y, Z coordinates from config
22
+ cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
23
+ cameraEntity.lookAt(modelEntity.getPosition());
24
+
25
+ // The resetCamera function should also be updated to use the correct values:
26
+ function resetCamera() {
27
+ if (!cameraEntity || !modelEntity || !app) return;
28
+
29
+ try {
30
+ // Get the orbit camera script
31
+ const orbitCam = cameraEntity.script.orbitCamera;
32
+ if (!orbitCam) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ // Store model position
35
+ const modelPos = modelEntity.getPosition();
36
 
37
+ console.log(`Reset camera position for ${isMobile ? 'mobile' : 'desktop'}: (${chosenCameraX}, ${chosenCameraY}, ${chosenCameraZ})`);
 
 
 
 
38
 
39
+ // 1. Create a temporary entity to help calculate new values
40
+ const tempEntity = new pc.Entity();
41
+ tempEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
42
+ tempEntity.lookAt(modelPos);
 
 
 
 
 
 
43
 
44
+ // 2. Calculate the distance between camera and model
45
+ const distance = new pc.Vec3().sub2(
46
+ new pc.Vec3(chosenCameraX, chosenCameraY, chosenCameraZ),
47
+ modelPos
48
+ ).length();
49
 
50
+ // 3. Set camera position
51
+ cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
52
+ cameraEntity.lookAt(modelPos);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
+ // 4. Update the orbit camera's pivot point
55
+ orbitCam.pivotPoint = new pc.Vec3(modelPos.x, modelPos.y, modelPos.z);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
+ // 5. Set the distance
58
+ orbitCam._targetDistance = distance;
59
+ orbitCam._distance = distance;
60
 
61
+ // Rest of your resetCamera function...
62
+ // ...
63
+ } catch (error) {
64
+ console.error("Error resetting camera:", error);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  }
66
+ }