aaurelions commited on
Commit
22ce57e
·
verified ·
1 Parent(s): 8026f5a

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +26 -20
index.html CHANGED
@@ -69,7 +69,7 @@
69
 
70
  <canvas id="bg-canvas" class="absolute top-0 left-0 w-full h-full outline-none z-10"></canvas>
71
 
72
- <div id="loading-overlay" class="glass-ui absolute inset-0 z-[60] flex items-center justify-center opacity-0 pointer-events-none transition-opacity duration-300">
73
  <div class="spinner w-12 h-12 border-4 border-gray-600 rounded-full animate-spin"></div>
74
  </div>
75
  <div id="main-loader" class="absolute inset-0 bg-gray-900 flex flex-col items-center justify-center z-50 transition-opacity duration-500">
@@ -129,7 +129,7 @@
129
  <input type="text" id="model-url-input" class="w-full bg-gray-900/50 p-2 rounded-md border-2 border-gray-600 focus:border-blue-500 outline-none" placeholder="Enter model URL (.glb, .gltf)">
130
  <div class="flex justify-end gap-3 mt-2">
131
  <button onclick="document.getElementById('upload-model-modal').style.display='none'" class="bg-gray-600 px-4 py-2 rounded-md font-semibold hover:bg-gray-700">Cancel</button>
132
- <button id="load-model-url-btn" class="bg-blue-600 px-4 py-2 rounded-md font-semibold hover:bg-blue-700 min-w-[80px]">Load</button>
133
  </div>
134
  </div>
135
  </div>
@@ -146,7 +146,7 @@
146
  <input type="text" id="panorama-url-input" class="w-full bg-gray-900/50 p-2 rounded-md border-2 border-gray-600 focus:border-blue-500 outline-none" placeholder="Enter panorama image URL">
147
  <div class="flex justify-end gap-3 mt-2">
148
  <button onclick="document.getElementById('upload-panorama-modal').style.display='none'" class="bg-gray-600 px-4 py-2 rounded-md font-semibold hover:bg-gray-700">Cancel</button>
149
- <button id="load-panorama-url-btn" class="bg-blue-600 px-4 py-2 rounded-md font-semibold hover:bg-blue-700 min-w-[80px]">Load</button>
150
  </div>
151
  </div>
152
  </div>
@@ -162,6 +162,7 @@
162
  const defaultModelFiles = [ 'gerbera.glb', 'shahed1.glb', 'shahed2.glb', 'shahed3.glb', 'supercam.glb', 'zala.glb', 'beaver.glb' ].map(name => ({ name, url: `/glb/${name}`, thumb: `/glb/thumbnails/${name.replace('.glb', '.png')}` }));
163
 
164
  let scene, camera, renderer, controls;
 
165
  const loadedModels = new Map();
166
 
167
  const mainLoader = document.getElementById('main-loader');
@@ -196,7 +197,10 @@
196
  await loadAndSwitchModel(defaultModelFiles[0]);
197
 
198
  mainLoader.style.opacity = '0';
199
- setTimeout(() => mainLoader.style.display = 'none', 500);
 
 
 
200
 
201
  window.addEventListener('resize', onWindowResize);
202
  animate();
@@ -277,7 +281,7 @@
277
  const thumb = document.createElement('img');
278
  thumb.src = modelData.thumb;
279
  thumb.alt = `Thumbnail for ${modelData.name}`;
280
- thumb.className = 'w-full h-auto aspect-square object-contain'; // Use object-contain for PNGs
281
  thumb.onerror = () => {
282
  thumb.src = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2E1YjRjYyIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTUgMyBsIDE0IDAgYSBUbyAwIDAgMSAyIDIgTCAyMSAxOSBhIDIgMiAwIDAgMSAtMiAyIEwgNSAyMSBhIDIgMiAwIDAgMSAtMiAtMiBMIDMgNSBhIDIgMiAwIDAgMSAyIC0yIFoiPjwvcGF0aD48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSI0Ij48L2NpcmNsZT48L3N2Zz4=';
283
  thumb.classList.add('bg-gray-800', 'p-4');
@@ -320,7 +324,7 @@
320
  function toggleButtonLoading(button, isLoading) {
321
  if (isLoading) {
322
  button.disabled = true;
323
- button.innerHTML = `<svg class="animate-spin h-5 w-5 mx-auto" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>`;
324
  } else {
325
  button.disabled = false;
326
  button.innerHTML = 'Load';
@@ -328,9 +332,11 @@
328
  }
329
 
330
  function setupEventListeners() {
331
- // Panels
332
  const modelPanel = document.getElementById('model-panel');
333
  const panoramaPanel = document.getElementById('panorama-panel');
 
 
 
334
  const closeModelPanel = () => modelPanel.classList.remove('is-open');
335
  const closePanoramaPanel = () => panoramaPanel.classList.remove('is-open');
336
  document.getElementById('model-panel-trigger').addEventListener('click', e => { e.stopPropagation(); modelPanel.classList.toggle('is-open'); closePanoramaPanel(); });
@@ -339,37 +345,34 @@
339
  document.getElementById('close-panorama-panel').addEventListener('click', closePanoramaPanel);
340
  document.getElementById('bg-canvas').addEventListener('click', () => { closeModelPanel(); closePanoramaPanel(); });
341
 
342
- // Modals
343
- const uploadModelModal = document.getElementById('upload-model-modal');
344
- const uploadPanoramaModal = document.getElementById('upload-panorama-modal');
345
  document.getElementById('add-model-btn').addEventListener('click', () => uploadModelModal.style.display = 'flex');
346
  document.getElementById('add-panorama-btn').addEventListener('click', () => uploadPanoramaModal.style.display = 'flex');
347
 
348
- // File Handling
349
  const modelFileInput = document.getElementById('model-file-input');
350
  const panoramaFileInput = document.getElementById('panorama-file-input');
351
  modelFileInput.addEventListener('change', e => handleModelFile(e.target.files[0]));
352
  panoramaFileInput.addEventListener('change', e => handlePanoramaFile(e.target.files[0]));
353
 
354
- // URL Loading
355
  const loadModelUrlBtn = document.getElementById('load-model-url-btn');
 
356
  loadModelUrlBtn.addEventListener('click', () => {
357
- const url = document.getElementById('model-url-input').value.trim();
358
  if(url) {
359
  toggleButtonLoading(loadModelUrlBtn, true);
360
  handleModelFile(url).finally(() => toggleButtonLoading(loadModelUrlBtn, false));
361
  }
362
  });
 
363
  const loadPanoramaUrlBtn = document.getElementById('load-panorama-url-btn');
 
364
  loadPanoramaUrlBtn.addEventListener('click', () => {
365
- const url = document.getElementById('panorama-url-input').value.trim();
366
  if(url) {
367
  toggleButtonLoading(loadPanoramaUrlBtn, true);
368
  handlePanoramaFile(url).finally(() => toggleButtonLoading(loadPanoramaUrlBtn, false));
369
  }
370
  });
371
 
372
- // Drag and Drop
373
  setupDragDrop('model-drop-zone', handleModelFile, modelFileInput);
374
  setupDragDrop('panorama-drop-zone', handlePanoramaFile, panoramaFileInput);
375
  }
@@ -378,11 +381,12 @@
378
  if(!fileOrUrl) return;
379
  const isUrl = typeof fileOrUrl === 'string';
380
  const url = isUrl ? fileOrUrl : URL.createObjectURL(fileOrUrl);
381
- const name = isUrl ? fileOrUrl.split('/').pop() : fileOrUrl.name;
382
- const modelData = { name, url, thumb: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2E1YjRjYyIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTUgMyBsIDE0IDAgYSBUbyAwIDAgMSAyIDIgTCAyMSAxOSBhIDIgMiAwIDAgMSAtMiAyIEwgNSAyMSBhIDIgMiAwIDAgMSAtMiAtMiBMIDMgNSBhIDIgMiAwIDAgMSAyIC0yIFoiPjwvcGF0aD48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSI0Ij48L2NpcmNsZT48L3N2Zz4=' }; // Placeholder thumb
383
 
384
- createModelCard(modelData);
385
  document.getElementById('upload-model-modal').style.display = 'none';
 
 
386
  await loadAndSwitchModel(modelData);
387
  }
388
 
@@ -390,11 +394,12 @@
390
  if(!fileOrUrl) return;
391
  const isUrl = typeof fileOrUrl === 'string';
392
  const url = isUrl ? fileOrUrl : URL.createObjectURL(fileOrUrl);
393
- const name = isUrl ? fileOrUrl.split('/').pop() : fileOrUrl.name;
394
  const panoData = { name, url, thumb: url };
395
 
396
- createPanoramaCard(panoData);
397
  document.getElementById('upload-panorama-modal').style.display = 'none';
 
 
398
  await setPanorama(panoData);
399
  }
400
 
@@ -413,6 +418,7 @@
413
  }
414
 
415
  function showLoadingOverlay(show) {
 
416
  loadingOverlay.style.opacity = show ? '1' : '0';
417
  loadingOverlay.style.pointerEvents = show ? 'auto' : 'none';
418
  }
 
69
 
70
  <canvas id="bg-canvas" class="absolute top-0 left-0 w-full h-full outline-none z-10"></canvas>
71
 
72
+ <div id="loading-overlay" class="glass-ui absolute inset-0 z-[51] flex items-center justify-center opacity-0 pointer-events-none transition-opacity duration-300">
73
  <div class="spinner w-12 h-12 border-4 border-gray-600 rounded-full animate-spin"></div>
74
  </div>
75
  <div id="main-loader" class="absolute inset-0 bg-gray-900 flex flex-col items-center justify-center z-50 transition-opacity duration-500">
 
129
  <input type="text" id="model-url-input" class="w-full bg-gray-900/50 p-2 rounded-md border-2 border-gray-600 focus:border-blue-500 outline-none" placeholder="Enter model URL (.glb, .gltf)">
130
  <div class="flex justify-end gap-3 mt-2">
131
  <button onclick="document.getElementById('upload-model-modal').style.display='none'" class="bg-gray-600 px-4 py-2 rounded-md font-semibold hover:bg-gray-700">Cancel</button>
132
+ <button id="load-model-url-btn" class="bg-blue-600 px-4 py-2 rounded-md font-semibold hover:bg-blue-700 min-w-[80px] flex justify-center items-center">Load</button>
133
  </div>
134
  </div>
135
  </div>
 
146
  <input type="text" id="panorama-url-input" class="w-full bg-gray-900/50 p-2 rounded-md border-2 border-gray-600 focus:border-blue-500 outline-none" placeholder="Enter panorama image URL">
147
  <div class="flex justify-end gap-3 mt-2">
148
  <button onclick="document.getElementById('upload-panorama-modal').style.display='none'" class="bg-gray-600 px-4 py-2 rounded-md font-semibold hover:bg-gray-700">Cancel</button>
149
+ <button id="load-panorama-url-btn" class="bg-blue-600 px-4 py-2 rounded-md font-semibold hover:bg-blue-700 min-w-[80px] flex justify-center items-center">Load</button>
150
  </div>
151
  </div>
152
  </div>
 
162
  const defaultModelFiles = [ 'gerbera.glb', 'shahed1.glb', 'shahed2.glb', 'shahed3.glb', 'supercam.glb', 'zala.glb', 'beaver.glb' ].map(name => ({ name, url: `/glb/${name}`, thumb: `/glb/thumbnails/${name.replace('.glb', '.png')}` }));
163
 
164
  let scene, camera, renderer, controls;
165
+ let isInitialized = false;
166
  const loadedModels = new Map();
167
 
168
  const mainLoader = document.getElementById('main-loader');
 
197
  await loadAndSwitchModel(defaultModelFiles[0]);
198
 
199
  mainLoader.style.opacity = '0';
200
+ setTimeout(() => {
201
+ mainLoader.style.display = 'none';
202
+ isInitialized = true;
203
+ }, 500);
204
 
205
  window.addEventListener('resize', onWindowResize);
206
  animate();
 
281
  const thumb = document.createElement('img');
282
  thumb.src = modelData.thumb;
283
  thumb.alt = `Thumbnail for ${modelData.name}`;
284
+ thumb.className = 'w-full h-auto aspect-square object-contain';
285
  thumb.onerror = () => {
286
  thumb.src = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiB2aWV3Qm94PSIwIDAgMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2E1YjRjYyIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTUgMyBsIDE0IDAgYSBUbyAwIDAgMSAyIDIgTCAyMSAxOSBhIDIgMiAwIDAgMSAtMiAyIEwgNSAyMSBhIDIgMiAwIDAgMSAtMiAtMiBMIDMgNSBhIDIgMiAwIDAgMSAyIC0yIFoiPjwvcGF0aD48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSI0Ij48L2NpcmNsZT48L3N2Zz4=';
287
  thumb.classList.add('bg-gray-800', 'p-4');
 
324
  function toggleButtonLoading(button, isLoading) {
325
  if (isLoading) {
326
  button.disabled = true;
327
+ button.innerHTML = `<svg class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>`;
328
  } else {
329
  button.disabled = false;
330
  button.innerHTML = 'Load';
 
332
  }
333
 
334
  function setupEventListeners() {
 
335
  const modelPanel = document.getElementById('model-panel');
336
  const panoramaPanel = document.getElementById('panorama-panel');
337
+ const uploadModelModal = document.getElementById('upload-model-modal');
338
+ const uploadPanoramaModal = document.getElementById('upload-panorama-modal');
339
+
340
  const closeModelPanel = () => modelPanel.classList.remove('is-open');
341
  const closePanoramaPanel = () => panoramaPanel.classList.remove('is-open');
342
  document.getElementById('model-panel-trigger').addEventListener('click', e => { e.stopPropagation(); modelPanel.classList.toggle('is-open'); closePanoramaPanel(); });
 
345
  document.getElementById('close-panorama-panel').addEventListener('click', closePanoramaPanel);
346
  document.getElementById('bg-canvas').addEventListener('click', () => { closeModelPanel(); closePanoramaPanel(); });
347
 
 
 
 
348
  document.getElementById('add-model-btn').addEventListener('click', () => uploadModelModal.style.display = 'flex');
349
  document.getElementById('add-panorama-btn').addEventListener('click', () => uploadPanoramaModal.style.display = 'flex');
350
 
 
351
  const modelFileInput = document.getElementById('model-file-input');
352
  const panoramaFileInput = document.getElementById('panorama-file-input');
353
  modelFileInput.addEventListener('change', e => handleModelFile(e.target.files[0]));
354
  panoramaFileInput.addEventListener('change', e => handlePanoramaFile(e.target.files[0]));
355
 
 
356
  const loadModelUrlBtn = document.getElementById('load-model-url-btn');
357
+ const modelUrlInput = document.getElementById('model-url-input');
358
  loadModelUrlBtn.addEventListener('click', () => {
359
+ const url = modelUrlInput.value.trim();
360
  if(url) {
361
  toggleButtonLoading(loadModelUrlBtn, true);
362
  handleModelFile(url).finally(() => toggleButtonLoading(loadModelUrlBtn, false));
363
  }
364
  });
365
+
366
  const loadPanoramaUrlBtn = document.getElementById('load-panorama-url-btn');
367
+ const panoramaUrlInput = document.getElementById('panorama-url-input');
368
  loadPanoramaUrlBtn.addEventListener('click', () => {
369
+ const url = panoramaUrlInput.value.trim();
370
  if(url) {
371
  toggleButtonLoading(loadPanoramaUrlBtn, true);
372
  handlePanoramaFile(url).finally(() => toggleButtonLoading(loadPanoramaUrlBtn, false));
373
  }
374
  });
375
 
 
376
  setupDragDrop('model-drop-zone', handleModelFile, modelFileInput);
377
  setupDragDrop('panorama-drop-zone', handlePanoramaFile, panoramaFileInput);
378
  }
 
381
  if(!fileOrUrl) return;
382
  const isUrl = typeof fileOrUrl === 'string';
383
  const url = isUrl ? fileOrUrl : URL.createObjectURL(fileOrUrl);
384
+ const name = isUrl ? fileOrUrl.split('/').pop().split('?')[0] : fileOrUrl.name;
385
+ const modelData = { name, url, thumb: `glb/thumbnails/${name.replace('.glb', '.png')}` };
386
 
 
387
  document.getElementById('upload-model-modal').style.display = 'none';
388
+ document.getElementById('model-url-input').value = '';
389
+ createModelCard(modelData);
390
  await loadAndSwitchModel(modelData);
391
  }
392
 
 
394
  if(!fileOrUrl) return;
395
  const isUrl = typeof fileOrUrl === 'string';
396
  const url = isUrl ? fileOrUrl : URL.createObjectURL(fileOrUrl);
397
+ const name = isUrl ? fileOrUrl.split('/').pop().split('?')[0] : fileOrUrl.name;
398
  const panoData = { name, url, thumb: url };
399
 
 
400
  document.getElementById('upload-panorama-modal').style.display = 'none';
401
+ document.getElementById('panorama-url-input').value = '';
402
+ createPanoramaCard(panoData);
403
  await setPanorama(panoData);
404
  }
405
 
 
418
  }
419
 
420
  function showLoadingOverlay(show) {
421
+ if (show && !isInitialized) return;
422
  loadingOverlay.style.opacity = show ? '1' : '0';
423
  loadingOverlay.style.pointerEvents = show ? 'auto' : 'none';
424
  }