@littlecarlito/blorktools 0.50.3 → 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,495 @@
1
+ import { getState, updateState } from "../../state/scene-state";
2
+ import { handleLightingUpload } from "./lighting-file-handler";
3
+ import { handleModelUpload } from "./model-file-manager";
4
+ import { handleBackgroundUpload } from "./background-file-handler";
5
+ import { handleTextureUpload } from "./texture-file-handler";
6
+ import { handleZipUpload } from "./zip-handler";
7
+
8
+ // File type configuration object - a centralized definition of properties for each file type
9
+ const FILE_TYPE_CONFIG = {
10
+ baseColor: {
11
+ title: 'Base Color Atlas',
12
+ instruction: 'Drag & drop your base color texture atlas here',
13
+ acceptedFileTypes: ['.png', '.jpg', '.jpeg', '.webp', '.tif', '.tiff', '.bmp'],
14
+ stateKey: 'textureFiles',
15
+ handler: handleTextureUpload,
16
+ resetState: resetBaseColorState
17
+ },
18
+ orm: {
19
+ title: 'ORM Atlas',
20
+ instruction: 'Drag & drop your ORM (Occlusion, Roughness, Metalness) texture atlas here',
21
+ acceptedFileTypes: ['.png', '.jpg', '.jpeg', '.webp', '.tif', '.tiff', '.bmp'],
22
+ stateKey: 'textureFiles',
23
+ handler: handleTextureUpload,
24
+ resetState: resetOrmState
25
+ },
26
+ normal: {
27
+ title: 'Normal Atlas',
28
+ instruction: 'Drag & drop your normal map texture atlas here',
29
+ acceptedFileTypes: ['.png', '.jpg', '.jpeg', '.webp', '.tif', '.tiff', '.bmp'],
30
+ stateKey: 'textureFiles',
31
+ handler: handleTextureUpload,
32
+ resetState: resetNormalState
33
+ },
34
+ model: {
35
+ title: '3D Model',
36
+ instruction: 'Drag & drop a GLB model file here',
37
+ optionalText: 'If not provided, a cube will be used',
38
+ acceptedFileTypes: ['.glb'],
39
+ stateKey: 'modelFile',
40
+ handler: handleModelUpload,
41
+ resetState: resetModelState
42
+ },
43
+ lighting: {
44
+ title: 'Lighting File',
45
+ instruction: 'Drag & drop your HDR or EXR lighting file here',
46
+ acceptedFileTypes: ['.hdr', '.exr'],
47
+ stateKey: 'lightingFile',
48
+ handler: handleLightingUpload,
49
+ resetState: resetLightingState
50
+ },
51
+ background: {
52
+ title: 'Background Image',
53
+ instruction: 'Drag & drop your HDR, EXR, JPEG, PNG, WebP, or TIFF background image here',
54
+ acceptedFileTypes: ['.hdr', '.exr', '.jpg', '.jpeg', '.png', '.webp', '.tiff', '.tif'],
55
+ stateKey: 'backgroundFile',
56
+ handler: handleBackgroundUpload,
57
+ resetState: resetBackgroundState
58
+ },
59
+ zip: {
60
+ title: 'ZIP Archive',
61
+ instruction: 'Drag & drop a ZIP file containing asset files here',
62
+ acceptedFileTypes: ['.zip'],
63
+ stateKey: 'zipFile',
64
+ handler: handleZipUpload,
65
+ resetState: resetZipState
66
+ }
67
+ };
68
+
69
+ /**
70
+ * Reset state for base color texture
71
+ */
72
+ function resetBaseColorState() {
73
+ console.debug('Resetting base color texture state');
74
+ const state = getState();
75
+ if (state.textureFiles) {
76
+ state.textureFiles.baseColor = null;
77
+ updateState('textureFiles', state.textureFiles);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Reset state for ORM texture
83
+ */
84
+ function resetOrmState() {
85
+ console.debug('Resetting ORM texture state');
86
+ const state = getState();
87
+ if (state.textureFiles) {
88
+ state.textureFiles.orm = null;
89
+ updateState('textureFiles', state.textureFiles);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Reset state for normal texture
95
+ */
96
+ function resetNormalState() {
97
+ console.debug('Resetting normal texture state');
98
+ const state = getState();
99
+ if (state.textureFiles) {
100
+ state.textureFiles.normal = null;
101
+ updateState('textureFiles', state.textureFiles);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Reset state for model file
107
+ */
108
+ function resetModelState() {
109
+ // Get current model and clear it from the scene if needed
110
+ const state = getState();
111
+ if (state.model && state.scene) {
112
+ // Remove model from scene
113
+ state.scene.remove(state.model);
114
+ // Dispose of any textures or geometries within the model
115
+ if (state.model.traverse) {
116
+ state.model.traverse((node) => {
117
+ if (node.geometry) node.geometry.dispose();
118
+ if (node.material) {
119
+ if (Array.isArray(node.material)) {
120
+ node.material.forEach(mat => {
121
+ Object.values(mat).forEach(value => {
122
+ if (value && typeof value.dispose === 'function') value.dispose();
123
+ });
124
+ mat.dispose();
125
+ });
126
+ } else {
127
+ Object.values(node.material).forEach(value => {
128
+ if (value && typeof value.dispose === 'function') value.dispose();
129
+ });
130
+ node.material.dispose();
131
+ }
132
+ }
133
+ });
134
+ }
135
+ }
136
+
137
+ // Clear model-related state
138
+ updateState('modelFile', null);
139
+ updateState('useCustomModel', false);
140
+ updateState('model', null);
141
+ }
142
+
143
+ /**
144
+ * Reset state for lighting file
145
+ */
146
+ function resetLightingState() {
147
+ // Get current state
148
+ const state = getState();
149
+
150
+ // Remove environment map from scene if it exists
151
+ if (state.scene && state.scene.environment) {
152
+ // Dispose of the environment texture
153
+ if (state.scene.environment.dispose) {
154
+ state.scene.environment.dispose();
155
+ }
156
+ state.scene.environment = null;
157
+
158
+ // Also reset the background if it was using the same environment map
159
+ if (state.scene.background === state.scene.environment) {
160
+ state.scene.background = null;
161
+ }
162
+ }
163
+
164
+ // Clear lighting-related state
165
+ updateState('lightingFile', null);
166
+ updateState('environmentLightingEnabled', false);
167
+ updateState('environmentTexture', null);
168
+ }
169
+
170
+ /**
171
+ * Reset state for background file
172
+ */
173
+ function resetBackgroundState() {
174
+ // Get current state
175
+ const state = getState();
176
+
177
+ // Dispose of background texture if it exists
178
+ if (state.backgroundTexture && state.backgroundTexture.dispose) {
179
+ state.backgroundTexture.dispose();
180
+ }
181
+
182
+ // Remove background from scene if it exists and is different from environment
183
+ if (state.scene && state.scene.background && state.scene.background !== state.scene.environment) {
184
+ if (state.scene.background.dispose) {
185
+ state.scene.background.dispose();
186
+ }
187
+ state.scene.background = null;
188
+ }
189
+
190
+ // Clear background-related state
191
+ updateState({
192
+ backgroundFile: null,
193
+ backgroundTexture: null,
194
+ backgroundEnabled: false
195
+ });
196
+ }
197
+
198
+ /**
199
+ * Reset state for ZIP file
200
+ */
201
+ function resetZipState() {
202
+ console.debug('Resetting ZIP file state');
203
+ updateState('zipFile', null);
204
+ }
205
+
206
+ /**
207
+ * Setup dropzones for file input
208
+ */
209
+ export function setupDropzones() {
210
+ console.log('Setting up dropzones for file input...');
211
+
212
+ // Get dropzone elements
213
+ const baseColorDropzone = document.getElementById('basecolor-dropzone');
214
+ const ormDropzone = document.getElementById('orm-dropzone');
215
+ const normalDropzone = document.getElementById('normal-dropzone');
216
+ const modelDropzone = document.getElementById('model-dropzone');
217
+ const lightingDropzone = document.getElementById('lighting-dropzone');
218
+ const backgroundDropzone = document.getElementById('background-dropzone');
219
+
220
+ console.log('Dropzone elements found:', {
221
+ baseColor: !!baseColorDropzone,
222
+ orm: !!ormDropzone,
223
+ normal: !!normalDropzone,
224
+ model: !!modelDropzone,
225
+ lighting: !!lightingDropzone,
226
+ background: !!backgroundDropzone
227
+ });
228
+
229
+ // Get info elements
230
+ const baseColorInfo = document.getElementById('basecolor-info');
231
+ const ormInfo = document.getElementById('orm-info');
232
+ const normalInfo = document.getElementById('normal-info');
233
+ const modelInfo = document.getElementById('model-info');
234
+ const lightingInfo = document.getElementById('lighting-info');
235
+ const backgroundInfo = document.getElementById('background-info');
236
+
237
+ // Set up each dropzone using the configuration
238
+ const dropzones = [
239
+ { element: baseColorDropzone, type: 'baseColor', info: baseColorInfo },
240
+ { element: ormDropzone, type: 'orm', info: ormInfo },
241
+ { element: normalDropzone, type: 'normal', info: normalInfo },
242
+ { element: modelDropzone, type: 'model', info: modelInfo },
243
+ { element: lightingDropzone, type: 'lighting', info: lightingInfo },
244
+ { element: backgroundDropzone, type: 'background', info: backgroundInfo }
245
+ ];
246
+
247
+ dropzones.forEach(dz => {
248
+ if (dz.element && dz.info) {
249
+ setupDropzone(dz.element, dz.type, dz.info);
250
+ } else if (dz.element) {
251
+ // If info element not found, still set up the dropzone
252
+ console.warn(`Info element for ${dz.type} not found, setting up with null infoElement`);
253
+ setupDropzone(dz.element, dz.type, null);
254
+ }
255
+ });
256
+
257
+ console.log('Dropzones setup complete');
258
+ }
259
+
260
+ /**
261
+ * Set up a single dropzone with event handlers
262
+ * @param {HTMLElement} dropzone - The dropzone element
263
+ * @param {string} fileType - The type of file this dropzone accepts
264
+ * @param {HTMLElement} infoElement - Element to display file info
265
+ */
266
+ export function setupDropzone(dropzone, fileType, infoElement) {
267
+ if (!dropzone) {
268
+ console.error(`Error: dropzone is null or undefined for type ${fileType}`);
269
+ return;
270
+ }
271
+
272
+ // First remove any existing event listeners to prevent duplicates
273
+ const clone = dropzone.cloneNode(true);
274
+ if(dropzone.parentNode != null) {
275
+ dropzone.parentNode.replaceChild(clone, dropzone);
276
+ }
277
+ dropzone = clone;
278
+
279
+ const config = FILE_TYPE_CONFIG[fileType];
280
+
281
+ if (!config) {
282
+ console.error(`No configuration found for file type: ${fileType}`);
283
+ return;
284
+ }
285
+
286
+ // Refresh infoElement reference if it's null (likely after clearing)
287
+ if (!infoElement) {
288
+ infoElement = document.getElementById(fileType.toLowerCase() + '-info');
289
+ }
290
+
291
+ // Set up the drop event for this dropzone
292
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
293
+ dropzone.addEventListener(eventName, preventDefaults, false);
294
+ });
295
+
296
+ // Highlight drop area when item is dragged over it
297
+ ['dragenter', 'dragover'].forEach(eventName => {
298
+ dropzone.addEventListener(eventName, highlight, false);
299
+ });
300
+
301
+ ['dragleave', 'drop'].forEach(eventName => {
302
+ dropzone.addEventListener(eventName, unhighlight, false);
303
+ });
304
+
305
+ function highlight(e) {
306
+ dropzone.classList.add('active');
307
+ }
308
+
309
+ function unhighlight(e) {
310
+ dropzone.classList.remove('active');
311
+ }
312
+
313
+ // Handle file drop
314
+ dropzone.addEventListener('drop', event => {
315
+ event.preventDefault();
316
+
317
+ const dt = event.dataTransfer;
318
+ const files = dt.files;
319
+
320
+ if (files.length === 0) {
321
+ return false;
322
+ }
323
+
324
+ const file = files[0]; // Use only the first file
325
+
326
+ // Check if file extension is valid for this dropzone
327
+ const validExtensions = config.acceptedFileTypes;
328
+ const isValidFile = file && validExtensions.some(ext => file.name.toLowerCase().endsWith(ext));
329
+
330
+ if (isValidFile) {
331
+ // Use the handler function from the configuration
332
+ // For texture uploads, we need to pass the actual texture type string
333
+ if (['baseColor', 'orm', 'normal'].includes(fileType)) {
334
+ config.handler(file, fileType, infoElement, null, dropzone);
335
+ } else {
336
+ // Ensure we pass the current dropzone element to the handler
337
+ config.handler(file, infoElement, null, dropzone);
338
+ }
339
+ } else if (file) {
340
+ alert(`Please upload a valid file format: ${validExtensions.join(', ')}`);
341
+ return false;
342
+ }
343
+
344
+ return false;
345
+ }, false);
346
+
347
+ // Handle click to select file using file input
348
+ dropzone.addEventListener('click', (event) => {
349
+ // If the click was on a clear button, don't do anything
350
+ if (event.target.classList.contains('clear-preview-button')) {
351
+ return;
352
+ }
353
+
354
+ // If the dropzone has a file (has-file class), only allow drag and drop to replace or clear button
355
+ if (dropzone.classList.contains('has-file')) {
356
+ // Check if the click was on a preview element (for example, the 3D model preview or image)
357
+ // Don't open file dialog if click is on any preview element or inside a preview container
358
+ const isOnPreview = event.target.closest('.preview') ||
359
+ event.target.classList.contains('texture-preview-img') ||
360
+ event.target.classList.contains('hdr-preview-canvas') ||
361
+ event.target.classList.contains('texture-preview-container') ||
362
+ event.target.classList.contains('hdr-preview-container');
363
+
364
+ if (isOnPreview) {
365
+ // If this is a click on a preview element, just return without opening the file picker
366
+ return;
367
+ }
368
+
369
+ // If we get here, this is a click on the dropzone but not on a preview element
370
+ // Since the dropzone already has a file, do nothing (don't open file dialog)
371
+ return;
372
+ }
373
+
374
+ // Create a file input element - only for empty dropzones
375
+ const input = document.createElement('input');
376
+ input.type = 'file';
377
+
378
+ // Set accept attribute based on file type
379
+ input.accept = config.acceptedFileTypes.join(',');
380
+
381
+ // Handle file selection
382
+ input.onchange = e => {
383
+ const file = e.target.files[0];
384
+ if (!file) return;
385
+
386
+ const isValidFile = config.acceptedFileTypes.some(ext => file.name.toLowerCase().endsWith(ext));
387
+
388
+ if (isValidFile) {
389
+ // Pass the dropzone element to the handler
390
+ // For texture uploads, we need to pass the actual texture type string
391
+ if (['baseColor', 'orm', 'normal'].includes(fileType)) {
392
+ config.handler(file, fileType, infoElement, null, dropzone);
393
+ } else {
394
+ // Make sure to pass the dropzone parameter for all handlers
395
+ config.handler(file, infoElement, null, dropzone);
396
+ }
397
+ } else {
398
+ alert(`Please upload a valid file format: ${config.acceptedFileTypes.join(', ')}`);
399
+ }
400
+ };
401
+
402
+ input.click();
403
+ });
404
+ }
405
+
406
+ /**
407
+ * Clear a dropzone and reset it to its original state
408
+ * @param {HTMLElement} dropzone - The dropzone element to clear
409
+ * @param {string} fileType - The type of file ('baseColor', 'orm', 'normal', 'model', 'lighting', 'background')
410
+ * @param {string} title - The original title of the dropzone
411
+ */
412
+ export function clearDropzone(dropzone, fileType, title) {
413
+ const config = FILE_TYPE_CONFIG[fileType];
414
+
415
+ if (!config) {
416
+ console.error(`No configuration found for file type: ${fileType}`);
417
+ return;
418
+ }
419
+
420
+ // Reset state based on file type configuration
421
+ if (config.resetState) {
422
+ // Use custom reset function if defined
423
+ config.resetState();
424
+ } else if (config.stateKey === 'textureFiles') {
425
+ // Handle texture file state
426
+ const state = getState();
427
+ state.textureFiles[fileType] = null;
428
+ updateState('textureFiles', state.textureFiles);
429
+
430
+ // BUGFIX: Also clear the corresponding texture object
431
+ // This ensures the texture doesn't continue to be used after clearing
432
+ if (state.textureObjects && state.textureObjects[fileType]) {
433
+ state.textureObjects[fileType] = null;
434
+ updateState('textureObjects', state.textureObjects);
435
+ }
436
+ } else if (config.stateKey) {
437
+ // Handle other state keys
438
+ updateState(config.stateKey, null);
439
+ }
440
+
441
+ // Clear the dropzone classes and content
442
+ dropzone.classList.remove('has-file');
443
+ dropzone.innerHTML = '';
444
+
445
+ // Recreate the original dropzone content
446
+ const titleElement = document.createElement('h3');
447
+ titleElement.textContent = config.title || title;
448
+ dropzone.appendChild(titleElement);
449
+
450
+ // Add instruction text
451
+ if (config.instruction) {
452
+ const instructionText = document.createElement('p');
453
+ instructionText.textContent = config.instruction;
454
+ dropzone.appendChild(instructionText);
455
+ }
456
+
457
+ // Add optional text if present
458
+ if (config.optionalText) {
459
+ const optionalText = document.createElement('p');
460
+ optionalText.textContent = config.optionalText;
461
+ dropzone.appendChild(optionalText);
462
+ }
463
+
464
+ // Add an empty file info element
465
+ const infoElement = document.createElement('p');
466
+ infoElement.className = 'file-info';
467
+ infoElement.id = fileType.toLowerCase() + '-info';
468
+ dropzone.appendChild(infoElement);
469
+
470
+ // Get the newly created info element to pass to setupDropzone
471
+ const newInfoElement = document.getElementById(fileType.toLowerCase() + '-info');
472
+
473
+ // Reattach the dropzone event handlers
474
+ setupDropzone(dropzone, fileType, newInfoElement || infoElement);
475
+ }
476
+
477
+ /**
478
+ * Prevent default drag behaviors
479
+ * @param {Event} e - The event object
480
+ */
481
+ function preventDefaults(e) {
482
+ e.preventDefault();
483
+ e.stopPropagation();
484
+ }
485
+
486
+ /**
487
+ * Format file size for display
488
+ * @param {number} bytes - The file size in bytes
489
+ * @returns {string} Formatted file size
490
+ */
491
+ export function formatFileSize(bytes) {
492
+ if (bytes < 1024) return bytes + ' bytes';
493
+ else if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
494
+ else return (bytes / 1048576).toFixed(1) + ' MB';
495
+ }
@@ -0,0 +1,36 @@
1
+ import { processModelFile } from "../../workers/worker-manager";
2
+
3
+ /**
4
+ * Process a GLB model file using web workers
5
+ * @param {File} file - The GLB file to process
6
+ * @returns {Promise} A promise that resolves when processing is complete
7
+ */
8
+ export async function processGLBFile(file) {
9
+ // Basic file validation client-side before sending to worker
10
+ if (!file || !file.name.toLowerCase().endsWith('.glb')) {
11
+ throw new Error('Invalid GLB file');
12
+ }
13
+
14
+ try {
15
+ // Process the file using the worker-manager
16
+ // This handles the file in a separate thread
17
+ const result = await processModelFile(file);
18
+
19
+ if (result.status !== 'success') {
20
+ throw new Error(result.error || 'Unknown error processing GLB file');
21
+ }
22
+
23
+ // Convert file to array buffer for further processing
24
+ const arrayBuffer = await file.arrayBuffer();
25
+
26
+ return {
27
+ arrayBuffer,
28
+ fileName: file.name,
29
+ fileSize: file.size,
30
+ ...result // Include any additional metadata from worker
31
+ };
32
+ } catch (error) {
33
+ console.error('Error in processGLBModel:', error);
34
+ throw error;
35
+ }
36
+ }