@littlecarlito/blorktools 0.50.4 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/bin/cli.js +69 -0
  2. package/package.json +13 -7
  3. package/src/asset_debugger/axis-indicator/axis-indicator.css +6 -0
  4. package/src/asset_debugger/axis-indicator/axis-indicator.html +20 -0
  5. package/src/asset_debugger/axis-indicator/axis-indicator.js +822 -0
  6. package/src/asset_debugger/debugger-scene/debugger-scene.css +142 -0
  7. package/src/asset_debugger/debugger-scene/debugger-scene.html +80 -0
  8. package/src/asset_debugger/debugger-scene/debugger-scene.js +791 -0
  9. package/src/asset_debugger/header/header.css +73 -0
  10. package/src/asset_debugger/header/header.html +24 -0
  11. package/src/asset_debugger/header/header.js +224 -0
  12. package/src/asset_debugger/index.html +76 -0
  13. package/src/asset_debugger/landing-page/landing-page.css +396 -0
  14. package/src/asset_debugger/landing-page/landing-page.html +81 -0
  15. package/src/asset_debugger/landing-page/landing-page.js +611 -0
  16. package/src/asset_debugger/loading-splash/loading-splash.css +195 -0
  17. package/src/asset_debugger/loading-splash/loading-splash.html +22 -0
  18. package/src/asset_debugger/loading-splash/loading-splash.js +59 -0
  19. package/src/asset_debugger/loading-splash/preview-loading-splash.js +66 -0
  20. package/src/asset_debugger/main.css +14 -0
  21. package/src/asset_debugger/modals/examples-modal/examples-modal.css +41 -0
  22. package/src/asset_debugger/modals/examples-modal/examples-modal.html +18 -0
  23. package/src/asset_debugger/modals/examples-modal/examples-modal.js +111 -0
  24. package/src/asset_debugger/modals/examples-modal/examples.js +125 -0
  25. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.css +452 -0
  26. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.html +87 -0
  27. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.js +675 -0
  28. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.css +219 -0
  29. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.html +20 -0
  30. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.js +548 -0
  31. package/src/asset_debugger/modals/settings-modal/settings-modal.css +103 -0
  32. package/src/asset_debugger/modals/settings-modal/settings-modal.html +158 -0
  33. package/src/asset_debugger/modals/settings-modal/settings-modal.js +475 -0
  34. package/src/asset_debugger/panels/asset-panel/asset-panel.css +263 -0
  35. package/src/asset_debugger/panels/asset-panel/asset-panel.html +123 -0
  36. package/src/asset_debugger/panels/asset-panel/asset-panel.js +136 -0
  37. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.css +94 -0
  38. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.js +312 -0
  39. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.css +129 -0
  40. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.js +486 -0
  41. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.css +545 -0
  42. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.js +538 -0
  43. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.css +70 -0
  44. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.js +586 -0
  45. package/src/asset_debugger/panels/world-panel/world-panel.css +364 -0
  46. package/src/asset_debugger/panels/world-panel/world-panel.html +173 -0
  47. package/src/asset_debugger/panels/world-panel/world-panel.js +1891 -0
  48. package/src/asset_debugger/router.js +190 -0
  49. package/src/asset_debugger/util/animation/playback/animation-playback-controller.js +150 -0
  50. package/src/asset_debugger/util/animation/playback/animation-preview-controller.js +316 -0
  51. package/src/asset_debugger/util/animation/playback/css3d-bounce-controller.js +400 -0
  52. package/src/asset_debugger/util/animation/playback/css3d-reversal-controller.js +821 -0
  53. package/src/asset_debugger/util/animation/render/css3d-prerender-controller.js +696 -0
  54. package/src/asset_debugger/util/animation/render/debug-texture-factory.js +0 -0
  55. package/src/asset_debugger/util/animation/render/iframe2texture-render-controller.js +199 -0
  56. package/src/asset_debugger/util/animation/render/image2texture-prerender-controller.js +461 -0
  57. package/src/asset_debugger/util/animation/render/pbr-material-factory.js +82 -0
  58. package/src/asset_debugger/util/common.css +280 -0
  59. package/src/asset_debugger/util/data/animation-classifier.js +323 -0
  60. package/src/asset_debugger/util/data/duplicate-handler.js +20 -0
  61. package/src/asset_debugger/util/data/glb-buffer-manager.js +407 -0
  62. package/src/asset_debugger/util/data/glb-classifier.js +290 -0
  63. package/src/asset_debugger/util/data/html-formatter.js +76 -0
  64. package/src/asset_debugger/util/data/html-linter.js +276 -0
  65. package/src/asset_debugger/util/data/localstorage-manager.js +265 -0
  66. package/src/asset_debugger/util/data/mesh-html-manager.js +295 -0
  67. package/src/asset_debugger/util/data/string-serder.js +303 -0
  68. package/src/asset_debugger/util/data/texture-classifier.js +663 -0
  69. package/src/asset_debugger/util/data/upload/background-file-handler.js +292 -0
  70. package/src/asset_debugger/util/data/upload/dropzone-preview-controller.js +396 -0
  71. package/src/asset_debugger/util/data/upload/file-upload-manager.js +495 -0
  72. package/src/asset_debugger/util/data/upload/glb-file-handler.js +36 -0
  73. package/src/asset_debugger/util/data/upload/glb-preview-controller.js +317 -0
  74. package/src/asset_debugger/util/data/upload/lighting-file-handler.js +194 -0
  75. package/src/asset_debugger/util/data/upload/model-file-manager.js +104 -0
  76. package/src/asset_debugger/util/data/upload/texture-file-handler.js +166 -0
  77. package/src/asset_debugger/util/data/upload/zip-handler.js +686 -0
  78. package/src/asset_debugger/util/loaders/html2canvas-loader.js +107 -0
  79. package/src/asset_debugger/util/rig/bone-kinematics.js +403 -0
  80. package/src/asset_debugger/util/rig/rig-constraint-manager.js +618 -0
  81. package/src/asset_debugger/util/rig/rig-controller.js +612 -0
  82. package/src/asset_debugger/util/rig/rig-factory.js +628 -0
  83. package/src/asset_debugger/util/rig/rig-handle-factory.js +46 -0
  84. package/src/asset_debugger/util/rig/rig-label-factory.js +441 -0
  85. package/src/asset_debugger/util/rig/rig-mouse-handler.js +377 -0
  86. package/src/asset_debugger/util/rig/rig-state-manager.js +175 -0
  87. package/src/asset_debugger/util/rig/rig-tooltip-manager.js +267 -0
  88. package/src/asset_debugger/util/rig/rig-ui-factory.js +700 -0
  89. package/src/asset_debugger/util/scene/background-manager.js +284 -0
  90. package/src/asset_debugger/util/scene/camera-controller.js +243 -0
  91. package/src/asset_debugger/util/scene/css3d-debug-controller.js +406 -0
  92. package/src/asset_debugger/util/scene/css3d-frame-factory.js +113 -0
  93. package/src/asset_debugger/util/scene/css3d-scene-manager.js +529 -0
  94. package/src/asset_debugger/util/scene/glb-controller.js +208 -0
  95. package/src/asset_debugger/util/scene/lighting-manager.js +690 -0
  96. package/src/asset_debugger/util/scene/threejs-model-manager.js +437 -0
  97. package/src/asset_debugger/util/scene/threejs-preview-manager.js +207 -0
  98. package/src/asset_debugger/util/scene/threejs-preview-setup.js +478 -0
  99. package/src/asset_debugger/util/scene/threejs-scene-controller.js +286 -0
  100. package/src/asset_debugger/util/scene/ui-manager.js +107 -0
  101. package/src/asset_debugger/util/state/animation-state.js +128 -0
  102. package/src/asset_debugger/util/state/css3d-state.js +83 -0
  103. package/src/asset_debugger/util/state/glb-preview-state.js +31 -0
  104. package/src/asset_debugger/util/state/log-util.js +197 -0
  105. package/src/asset_debugger/util/state/scene-state.js +452 -0
  106. package/src/asset_debugger/util/state/threejs-state.js +54 -0
  107. package/src/asset_debugger/util/workers/lighting-worker.js +61 -0
  108. package/src/asset_debugger/util/workers/model-worker.js +109 -0
  109. package/src/asset_debugger/util/workers/texture-worker.js +54 -0
  110. package/src/asset_debugger/util/workers/worker-manager.js +212 -0
  111. package/src/asset_debugger/widgets/mesh-info-widget.js +280 -0
  112. package/src/index.html +261 -0
  113. package/src/index.js +8 -0
  114. package/vite.config.js +66 -0
@@ -0,0 +1,611 @@
1
+ // landing-page.js - Landing page module with proper imports and event handling
2
+
3
+ // Import all dependencies at the top
4
+ import ExamplesModal from "../modals/examples-modal/examples-modal.js";
5
+ import {
6
+ getBackgroundFile,
7
+ getBaseColorFile,
8
+ getLightingFile,
9
+ getModelFile,
10
+ getNormalFile,
11
+ getOrmFile,
12
+ hasFiles,
13
+ initDraftState,
14
+ setState,
15
+ printStateReport
16
+ } from "../util/state/scene-state.js";
17
+ import { loadSettings } from "../util/data/localstorage-manager.js";
18
+ import { handleTextureUpload } from "../util/data/upload/texture-file-handler.js";
19
+ import { terminateAllWorkers } from "../util/workers/worker-manager.js";
20
+ import { setupDropzones } from "../util/data/upload/file-upload-manager.js";
21
+ import { handleAutoLoad, processZipContents } from "../util/data/upload/zip-handler.js";
22
+ import { handleLightingUpload } from "../util/data/upload/lighting-file-handler.js";
23
+ import { handleModelUpload } from "../util/data/upload/model-file-manager.js";
24
+
25
+ // Module state
26
+ let isInitialized = false;
27
+ let eventListeners = [];
28
+
29
+ // Add event listener to terminate all workers when the page is unloaded
30
+ window.addEventListener('beforeunload', () => {
31
+ terminateAllWorkers();
32
+ });
33
+
34
+ // Main initialization function
35
+ export async function initalizeLandingPage() {
36
+ console.log('🌟 Initializing landing page...');
37
+
38
+ // Prevent double initialization
39
+ if (isInitialized) {
40
+ console.warn('⚠️ Landing page already initialized, skipping...');
41
+ return Promise.resolve(cleanup);
42
+ }
43
+
44
+ // Check if required DOM elements exist
45
+ if (!validateRequiredElements()) {
46
+ console.warn('⏳ Required DOM elements not found, waiting for them...');
47
+ // Use MutationObserver to wait for elements to be added
48
+ await waitForElements();
49
+ return initializeLandingPageInternal();
50
+ }
51
+
52
+ return Promise.resolve(initializeLandingPageInternal());
53
+ }
54
+
55
+ function validateRequiredElements() {
56
+ const requiredElements = [
57
+ 'upload-section',
58
+ 'start-debug'
59
+ ];
60
+
61
+ const results = requiredElements.map(id => {
62
+ const element = document.getElementById(id);
63
+ const found = !!element;
64
+ console.log(`Element ${id}: ${found ? 'found' : 'NOT FOUND'}`);
65
+ return found;
66
+ });
67
+
68
+ const allFound = results.every(found => found);
69
+ console.log(`All required elements found: ${allFound}`);
70
+ return allFound;
71
+ }
72
+
73
+ function waitForElements() {
74
+ return new Promise((resolve) => {
75
+ const observer = new MutationObserver((mutations) => {
76
+ if (validateRequiredElements()) {
77
+ observer.disconnect();
78
+ resolve();
79
+ }
80
+ });
81
+
82
+ observer.observe(document.body, {
83
+ childList: true,
84
+ subtree: true
85
+ });
86
+
87
+ // Timeout after 5 seconds
88
+ setTimeout(() => {
89
+ observer.disconnect();
90
+ resolve();
91
+ }, 5000);
92
+ });
93
+ }
94
+
95
+ function initializeLandingPageInternal() {
96
+ console.log('🔧 Starting landing page internal initialization...');
97
+
98
+ try {
99
+ // Initialize core functionality
100
+ preventDefaultDragBehavior();
101
+ initDraftState();
102
+ setupDropzones();
103
+ setupMainContainerDropzone();
104
+ setupEventListeners();
105
+ loadExamplesModal();
106
+
107
+ isInitialized = true;
108
+ console.log('✅ Landing page initialization complete');
109
+
110
+ return cleanup;
111
+
112
+ } catch (error) {
113
+ console.error('💥 Error during landing page initialization:', error);
114
+ return cleanup;
115
+ }
116
+ }
117
+
118
+ function setupEventListeners() {
119
+ // Setup start debug button listener
120
+ const startDebugBtn = document.getElementById('start-debug');
121
+ if (startDebugBtn) {
122
+ const verifyFileDropHandler = () => verifyFileDrop();
123
+ startDebugBtn.addEventListener('click', verifyFileDropHandler);
124
+ eventListeners.push({ element: startDebugBtn, event: 'click', handler: verifyFileDropHandler });
125
+ console.log('Start debug button listener attached');
126
+ } else {
127
+ console.warn('start-debug button not found in DOM');
128
+ }
129
+ }
130
+
131
+ function loadExamplesModal() {
132
+ const examplesModalContainer = document.getElementById('examples-modal-container');
133
+ if (examplesModalContainer) {
134
+ fetch('./modals/examples-modal/examples-modal.html')
135
+ .then(response => {
136
+ if (!response.ok) {
137
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
138
+ }
139
+ return response.text();
140
+ })
141
+ .then(html => {
142
+ examplesModalContainer.innerHTML = html;
143
+ console.log('Examples modal HTML loaded');
144
+ })
145
+ .catch(error => {
146
+ console.error('Error loading examples modal:', error);
147
+ });
148
+ } else {
149
+ console.warn('examples-modal-container not found in DOM');
150
+ }
151
+ }
152
+
153
+ function verifyFileDrop() {
154
+ printStateReport('Landing Page');
155
+
156
+ if (hasFiles()) {
157
+ // DON'T start debugging here - let the asset debugger page handle it
158
+ console.log('Files detected, navigating to asset debugger...');
159
+
160
+ // Use router navigation instead of direct window.location.href
161
+ if (window.appRouter) {
162
+ window.appRouter.navigateToPage('debugger-scene', {
163
+ hasFiles: true,
164
+ source: 'landing_page_verify'
165
+ });
166
+ } else {
167
+ console.error('Router not available, falling back to direct navigation');
168
+ window.location.href = '../debugger-scene/debugger-scene.html';
169
+ }
170
+ } else {
171
+ showExamplesModal();
172
+ }
173
+ }
174
+
175
+ function showExamplesModal() {
176
+ // Load settings for use with examples
177
+ const savedSettings = loadSettings();
178
+
179
+ // Create and show the modal
180
+ const examplesModal = new ExamplesModal((exampleType) => {
181
+ // Set flag in state to track which example was selected
182
+ setState({ selectedExample: exampleType });
183
+
184
+ // Use router navigation instead of direct window.location.href
185
+ if (window.appRouter) {
186
+ window.appRouter.navigateToPage('debugger-scene', {
187
+ selectedExample: exampleType,
188
+ source: 'examples_modal'
189
+ });
190
+ } else {
191
+ console.error('Router not available, falling back to direct navigation');
192
+ window.location.href = '../debugger-scene/debugger-scene.js';
193
+ }
194
+ });
195
+
196
+ // Show the examples modal
197
+ examplesModal.openModal();
198
+ }
199
+
200
+ // Prevent default drag-and-drop behavior for the entire document
201
+ function preventDefaultDragBehavior() {
202
+ const dragEventHandler = (e) => {
203
+ // Only prevent if it's actually a file drag operation
204
+ if (e.dataTransfer && e.dataTransfer.types &&
205
+ (e.dataTransfer.types.includes('Files') || e.dataTransfer.types.includes('application/x-moz-file'))) {
206
+ e.preventDefault();
207
+ e.stopPropagation();
208
+ }
209
+ };
210
+
211
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
212
+ document.addEventListener(eventName, dragEventHandler, false);
213
+ eventListeners.push({ element: document, event: eventName, handler: dragEventHandler });
214
+ });
215
+ }
216
+
217
+ /**
218
+ * Set up the main container as a dropzone for zip files
219
+ */
220
+ function setupMainContainerDropzone() {
221
+ const mainContainer = document.getElementById('upload-section');
222
+ const zipInfoElement = document.getElementById('zip-info');
223
+
224
+ if (!mainContainer) {
225
+ console.warn('upload-section not found in DOM, skipping main container dropzone setup');
226
+ return;
227
+ }
228
+
229
+ console.log('Setting up main container dropzone');
230
+
231
+ // Function to check if an element is a child of any dropzone
232
+ const isChildOfDropzone = (element) => {
233
+ if (!element) return false;
234
+
235
+ // Check if element itself is a dropzone
236
+ if (element.classList && element.classList.contains('dropzone')) {
237
+ return true;
238
+ }
239
+
240
+ // Check if element is a child of a dropzone
241
+ let parent = element.parentElement;
242
+ while (parent) {
243
+ if (parent.classList && parent.classList.contains('dropzone')) {
244
+ return true;
245
+ }
246
+ parent = parent.parentElement;
247
+ }
248
+
249
+ return false;
250
+ };
251
+
252
+ // Event handlers
253
+ const dragEnterHandler = function(e) {
254
+ e.preventDefault();
255
+ e.stopPropagation();
256
+
257
+ // Don't apply styling if dragging over a child dropzone
258
+ if (isChildOfDropzone(e.target)) return;
259
+
260
+ // Add active class to show it's a valid drop target
261
+ mainContainer.classList.add('dropzone-container-active');
262
+ };
263
+
264
+ const dragOverHandler = function(e) {
265
+ e.preventDefault();
266
+ e.stopPropagation();
267
+
268
+ // Don't apply styling if dragging over a child dropzone
269
+ if (isChildOfDropzone(e.target)) return;
270
+
271
+ // Set the drop effect
272
+ e.dataTransfer.dropEffect = 'copy';
273
+ };
274
+
275
+ const dragLeaveHandler = function(e) {
276
+ e.preventDefault();
277
+ e.stopPropagation();
278
+
279
+ // Don't remove styling if entering a child element within the container
280
+ // that isn't a dropzone
281
+ if (mainContainer.contains(e.relatedTarget) && !isChildOfDropzone(e.relatedTarget)) return;
282
+
283
+ // Remove active class
284
+ mainContainer.classList.remove('dropzone-container-active');
285
+ };
286
+
287
+ const dropHandler = function(e) {
288
+ e.preventDefault();
289
+ e.stopPropagation();
290
+
291
+ // Remove active class
292
+ mainContainer.classList.remove('dropzone-container-active');
293
+
294
+ // Don't handle drop if dropping on a child dropzone
295
+ if (isChildOfDropzone(e.target)) return;
296
+
297
+ const files = e.dataTransfer.files;
298
+ if (!files || files.length === 0) return;
299
+
300
+ // Process the files based on type
301
+ processMainDroppedFiles(files, zipInfoElement);
302
+ };
303
+
304
+ // Add event listeners and track them for cleanup
305
+ mainContainer.addEventListener('dragenter', dragEnterHandler);
306
+ mainContainer.addEventListener('dragover', dragOverHandler);
307
+ mainContainer.addEventListener('dragleave', dragLeaveHandler);
308
+ mainContainer.addEventListener('drop', dropHandler);
309
+
310
+ eventListeners.push(
311
+ { element: mainContainer, event: 'dragenter', handler: dragEnterHandler },
312
+ { element: mainContainer, event: 'dragover', handler: dragOverHandler },
313
+ { element: mainContainer, event: 'dragleave', handler: dragLeaveHandler },
314
+ { element: mainContainer, event: 'drop', handler: dropHandler }
315
+ );
316
+ }
317
+
318
+ /**
319
+ * Process files dropped on the main container
320
+ * @param {FileList} files - The files dropped
321
+ * @param {HTMLElement} infoElement - The element to display information
322
+ */
323
+ function processMainDroppedFiles(files, infoElement) {
324
+ if (!files || files.length === 0) return;
325
+
326
+ const file = files[0]; // Process only the first file for simplicity
327
+
328
+ // Show starting message
329
+ if (infoElement) {
330
+ infoElement.textContent = `Processing ${file.name}...`;
331
+ infoElement.style.display = 'block';
332
+ infoElement.style.color = '#007bff';
333
+ }
334
+
335
+ // Check file type and extension
336
+ const extension = file.name.split('.').pop().toLowerCase();
337
+ console.debug('Processing dropped file:', {
338
+ name: file.name,
339
+ type: file.type,
340
+ extension: extension,
341
+ size: file.size
342
+ });
343
+
344
+ // ZIP file handling
345
+ if (file.type === 'application/zip' || extension === 'zip') {
346
+ console.debug('Processing ZIP file:', file.name);
347
+ processZipFile(file);
348
+ return;
349
+ }
350
+
351
+ // Determine which dropzone to use based on file type/extension
352
+ let targetDropzone = determineTargetDropzone(file);
353
+ console.debug('Determined target dropzone:', targetDropzone, 'for file:', file.name);
354
+
355
+ if (targetDropzone) {
356
+ // Upload to the appropriate dropzone
357
+ uploadToDropzone(file, targetDropzone);
358
+
359
+ // Show success message
360
+ if (infoElement) {
361
+ infoElement.textContent = `File "${file.name}" loaded into ${getDropzoneName(targetDropzone)}`;
362
+ infoElement.style.display = 'block';
363
+ infoElement.style.color = 'green';
364
+ }
365
+ } else {
366
+ // No appropriate dropzone found
367
+ console.error('No appropriate dropzone found for file:', file.name);
368
+ if (infoElement) {
369
+ infoElement.textContent = `Error: Could not determine target for "${file.name}".
370
+ Supported types: ZIP, GLB, GLTF, HDR, EXR, JPG, PNG, WebP, TIFF`;
371
+ infoElement.style.display = 'block';
372
+ infoElement.style.color = 'red';
373
+ }
374
+ }
375
+ }
376
+
377
+ /**
378
+ * Determine the appropriate dropzone for a file
379
+ * @param {File} file - The file to check
380
+ * @returns {string|null} - The ID of the target dropzone or null if not supported
381
+ */
382
+ function determineTargetDropzone(file) {
383
+ const extension = file.name.split('.').pop().toLowerCase();
384
+ const mimeType = file.type.toLowerCase();
385
+
386
+ // 3D Model files
387
+ if (extension === 'glb' || extension === 'gltf') {
388
+ return 'model-dropzone';
389
+ }
390
+
391
+ // Lighting files - HDR, EXR
392
+ if (extension === 'hdr' || extension === 'exr') {
393
+ return 'lighting-dropzone';
394
+ }
395
+
396
+ // Image files - could be background, basecolor, normal, or ORM
397
+ // Let's use mime type to determine if it's an image
398
+ if (mimeType.startsWith('image/')) {
399
+ // Background images - any image type
400
+ return 'background-dropzone';
401
+ }
402
+
403
+ // Not a supported file type
404
+ return null;
405
+ }
406
+
407
+ /**
408
+ * Upload a file to a specific dropzone
409
+ * @param {File} file - The file to upload
410
+ * @param {string} dropzoneId - The ID of the target dropzone
411
+ */
412
+ function uploadToDropzone(file, dropzoneId) {
413
+ const dropzone = document.getElementById(dropzoneId);
414
+ if (!dropzone) {
415
+ throw new Error(`Dropzone "${dropzoneId}" not found`);
416
+ }
417
+
418
+ console.debug(`Uploading ${file.name} to ${dropzoneId}`);
419
+
420
+ // Get the info element for this dropzone
421
+ const infoElement = document.getElementById(`${dropzoneId.split('-')[0]}-info`);
422
+
423
+ // Handle file based on dropzone type directly instead of creating a new drop event
424
+ switch(dropzoneId) {
425
+ case 'model-dropzone':
426
+ console.debug('Loading model into dropzone:', file.name);
427
+ handleModelUpload(file, infoElement, dropzone);
428
+ break;
429
+ case 'background-dropzone':
430
+ console.debug('Loading background into dropzone:', file.name);
431
+ handleBackgroundUpload(file, infoElement, null, dropzone);
432
+ break;
433
+ case 'lighting-dropzone':
434
+ console.debug('Loading lighting into dropzone:', file.name);
435
+ handleLightingUpload(file, infoElement, null, dropzone);
436
+ break;
437
+ case 'basecolor-dropzone':
438
+ case 'orm-dropzone':
439
+ case 'normal-dropzone':
440
+ console.debug('Loading texture into dropzone:', file.name, 'type:', dropzoneId);
441
+ const textureType = dropzoneId.split('-')[0];
442
+ handleTextureUpload(file, textureType, infoElement, null, dropzone);
443
+ break;
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Get a user-friendly name for a dropzone
449
+ * @param {string} dropzoneId - The dropzone ID
450
+ * @returns {string} - A user-friendly name
451
+ */
452
+ function getDropzoneName(dropzoneId) {
453
+ const names = {
454
+ 'basecolor-dropzone': 'Base Color Atlas',
455
+ 'orm-dropzone': 'ORM Atlas',
456
+ 'normal-dropzone': 'Normal Atlas',
457
+ 'model-dropzone': '3D Model',
458
+ 'lighting-dropzone': 'Lighting',
459
+ 'background-dropzone': 'Background'
460
+ };
461
+
462
+ return names[dropzoneId] || dropzoneId;
463
+ }
464
+
465
+ /**
466
+ * Process a ZIP file
467
+ * @param {File} file - The ZIP file to process
468
+ */
469
+ async function processZipFile(file) {
470
+ console.log(`ZIP file received: ${file.name} size: ${file.size}`);
471
+
472
+ // Get the zip info element to show status
473
+ const zipInfoElement = document.getElementById('zip-info');
474
+
475
+ try {
476
+ // Show processing message
477
+ if (zipInfoElement) {
478
+ zipInfoElement.textContent = `Processing ${file.name}...`;
479
+ zipInfoElement.style.display = 'block';
480
+ zipInfoElement.style.color = '#007bff';
481
+ }
482
+
483
+ // Process the ZIP file contents using the zip-util module
484
+ const results = await processZipContents(file);
485
+
486
+ // Log the results
487
+ console.log('ZIP processing successful:', results);
488
+
489
+ // If successful, load files into dropzones
490
+ if (results.success) {
491
+ handleAutoLoad(results);
492
+
493
+ // Show success message
494
+ if (zipInfoElement) {
495
+ zipInfoElement.textContent = `ZIP file "${file.name}" processed successfully`;
496
+ zipInfoElement.style.color = 'green';
497
+
498
+ // Hide after 3 seconds
499
+ setTimeout(() => {
500
+ zipInfoElement.style.display = 'none';
501
+ }, 1000);
502
+ }
503
+ } else {
504
+ // Show error message
505
+ if (zipInfoElement) {
506
+ zipInfoElement.textContent = `Error processing ZIP file: ${results.error}`;
507
+ zipInfoElement.style.color = 'red';
508
+ }
509
+ }
510
+ } catch (error) {
511
+ console.error('Error processing ZIP file:', error);
512
+
513
+ // Show error message
514
+ if (zipInfoElement) {
515
+ zipInfoElement.textContent = `Error processing ZIP file: ${error.message}`;
516
+ zipInfoElement.style.color = 'red';
517
+ }
518
+ }
519
+ }
520
+
521
+ // Cleanup function to remove event listeners and reset state
522
+ function cleanup() {
523
+ console.log('Cleaning up landing page...');
524
+
525
+ // Remove all event listeners
526
+ eventListeners.forEach(({ element, event, handler }) => {
527
+ element.removeEventListener(event, handler);
528
+ });
529
+ eventListeners = [];
530
+
531
+ // Reset state
532
+ isInitialized = false;
533
+
534
+ console.log('Landing page cleanup complete');
535
+ }
536
+
537
+ /**
538
+ * Creates a clear button for a dropzone
539
+ * @param {HTMLElement} dropzone - The dropzone element
540
+ * @param {string} type - The type of asset ('basecolor', 'normal', 'orm', 'model', 'lighting', 'background')
541
+ * @param {string} originalTitle - The original title of the dropzone
542
+ * @returns {HTMLElement} The created clear button
543
+ */
544
+ export function createClearButton(dropzone, type, originalTitle) {
545
+ const clearButton = document.createElement('button');
546
+ clearButton.className = 'clear-preview-button';
547
+ clearButton.innerHTML = '×';
548
+ clearButton.title = 'Clear file';
549
+
550
+ clearButton.addEventListener('click', (e) => {
551
+ e.stopPropagation(); // Prevent dropzone click event
552
+
553
+ // Clear all relevant state for this type
554
+ clearStateForType(type);
555
+
556
+ // Clear the dropzone
557
+ clearDropzone(dropzone, type, originalTitle);
558
+
559
+ // Reattach the dropzone event handlers
560
+ setupDropzone(dropzone, type, document.getElementById(`${type}-info`));
561
+ });
562
+
563
+ return clearButton;
564
+ }
565
+
566
+ /**
567
+ * Clears all relevant state for a given asset type
568
+ * @param {string} type - The type of asset ('basecolor', 'normal', 'orm', 'model', 'lighting', 'background')
569
+ */
570
+ function clearStateForType(type) {
571
+ const state = getState();
572
+
573
+ switch (type) {
574
+ case 'basecolor':
575
+ case 'normal':
576
+ case 'orm':
577
+ // Clear texture object and file
578
+ if (state.textureObjects && state.textureObjects[type]) {
579
+ const texture = state.textureObjects[type];
580
+ if (texture && typeof texture.dispose === 'function') {
581
+ texture.dispose();
582
+ }
583
+ }
584
+ updateState('textureFiles', { ...state.textureFiles, [type]: null });
585
+ break;
586
+
587
+ case 'model':
588
+ updateState({
589
+ modelFile: null,
590
+ useCustomModel: false
591
+ });
592
+ break;
593
+
594
+ case 'lighting':
595
+ updateState({
596
+ lightingFile: null,
597
+ environmentTexture: null
598
+ });
599
+ break;
600
+
601
+ case 'background':
602
+ updateState({
603
+ backgroundFile: null,
604
+ backgroundTexture: null
605
+ });
606
+ break;
607
+ }
608
+
609
+ // Log the state after clearing
610
+ console.debug(`State after clearing ${type}:`, getState());
611
+ }