@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.
- package/bin/cli.js +69 -0
- package/package.json +13 -7
- package/src/asset_debugger/axis-indicator/axis-indicator.css +6 -0
- package/src/asset_debugger/axis-indicator/axis-indicator.html +20 -0
- package/src/asset_debugger/axis-indicator/axis-indicator.js +822 -0
- package/src/asset_debugger/debugger-scene/debugger-scene.css +142 -0
- package/src/asset_debugger/debugger-scene/debugger-scene.html +80 -0
- package/src/asset_debugger/debugger-scene/debugger-scene.js +791 -0
- package/src/asset_debugger/header/header.css +73 -0
- package/src/asset_debugger/header/header.html +24 -0
- package/src/asset_debugger/header/header.js +224 -0
- package/src/asset_debugger/index.html +76 -0
- package/src/asset_debugger/landing-page/landing-page.css +396 -0
- package/src/asset_debugger/landing-page/landing-page.html +81 -0
- package/src/asset_debugger/landing-page/landing-page.js +611 -0
- package/src/asset_debugger/loading-splash/loading-splash.css +195 -0
- package/src/asset_debugger/loading-splash/loading-splash.html +22 -0
- package/src/asset_debugger/loading-splash/loading-splash.js +59 -0
- package/src/asset_debugger/loading-splash/preview-loading-splash.js +66 -0
- package/src/asset_debugger/main.css +14 -0
- package/src/asset_debugger/modals/examples-modal/examples-modal.css +41 -0
- package/src/asset_debugger/modals/examples-modal/examples-modal.html +18 -0
- package/src/asset_debugger/modals/examples-modal/examples-modal.js +111 -0
- package/src/asset_debugger/modals/examples-modal/examples.js +125 -0
- package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.css +452 -0
- package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.html +87 -0
- package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.js +675 -0
- package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.css +219 -0
- package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.html +20 -0
- package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.js +548 -0
- package/src/asset_debugger/modals/settings-modal/settings-modal.css +103 -0
- package/src/asset_debugger/modals/settings-modal/settings-modal.html +158 -0
- package/src/asset_debugger/modals/settings-modal/settings-modal.js +475 -0
- package/src/asset_debugger/panels/asset-panel/asset-panel.css +263 -0
- package/src/asset_debugger/panels/asset-panel/asset-panel.html +123 -0
- package/src/asset_debugger/panels/asset-panel/asset-panel.js +136 -0
- package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.css +94 -0
- package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.js +312 -0
- package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.css +129 -0
- package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.js +486 -0
- package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.css +545 -0
- package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.js +538 -0
- package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.css +70 -0
- package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.js +586 -0
- package/src/asset_debugger/panels/world-panel/world-panel.css +364 -0
- package/src/asset_debugger/panels/world-panel/world-panel.html +173 -0
- package/src/asset_debugger/panels/world-panel/world-panel.js +1891 -0
- package/src/asset_debugger/router.js +190 -0
- package/src/asset_debugger/util/animation/playback/animation-playback-controller.js +150 -0
- package/src/asset_debugger/util/animation/playback/animation-preview-controller.js +316 -0
- package/src/asset_debugger/util/animation/playback/css3d-bounce-controller.js +400 -0
- package/src/asset_debugger/util/animation/playback/css3d-reversal-controller.js +821 -0
- package/src/asset_debugger/util/animation/render/css3d-prerender-controller.js +696 -0
- package/src/asset_debugger/util/animation/render/debug-texture-factory.js +0 -0
- package/src/asset_debugger/util/animation/render/iframe2texture-render-controller.js +199 -0
- package/src/asset_debugger/util/animation/render/image2texture-prerender-controller.js +461 -0
- package/src/asset_debugger/util/animation/render/pbr-material-factory.js +82 -0
- package/src/asset_debugger/util/common.css +280 -0
- package/src/asset_debugger/util/data/animation-classifier.js +323 -0
- package/src/asset_debugger/util/data/duplicate-handler.js +20 -0
- package/src/asset_debugger/util/data/glb-buffer-manager.js +407 -0
- package/src/asset_debugger/util/data/glb-classifier.js +290 -0
- package/src/asset_debugger/util/data/html-formatter.js +76 -0
- package/src/asset_debugger/util/data/html-linter.js +276 -0
- package/src/asset_debugger/util/data/localstorage-manager.js +265 -0
- package/src/asset_debugger/util/data/mesh-html-manager.js +295 -0
- package/src/asset_debugger/util/data/string-serder.js +303 -0
- package/src/asset_debugger/util/data/texture-classifier.js +663 -0
- package/src/asset_debugger/util/data/upload/background-file-handler.js +292 -0
- package/src/asset_debugger/util/data/upload/dropzone-preview-controller.js +396 -0
- package/src/asset_debugger/util/data/upload/file-upload-manager.js +495 -0
- package/src/asset_debugger/util/data/upload/glb-file-handler.js +36 -0
- package/src/asset_debugger/util/data/upload/glb-preview-controller.js +317 -0
- package/src/asset_debugger/util/data/upload/lighting-file-handler.js +194 -0
- package/src/asset_debugger/util/data/upload/model-file-manager.js +104 -0
- package/src/asset_debugger/util/data/upload/texture-file-handler.js +166 -0
- package/src/asset_debugger/util/data/upload/zip-handler.js +686 -0
- package/src/asset_debugger/util/loaders/html2canvas-loader.js +107 -0
- package/src/asset_debugger/util/rig/bone-kinematics.js +403 -0
- package/src/asset_debugger/util/rig/rig-constraint-manager.js +618 -0
- package/src/asset_debugger/util/rig/rig-controller.js +612 -0
- package/src/asset_debugger/util/rig/rig-factory.js +628 -0
- package/src/asset_debugger/util/rig/rig-handle-factory.js +46 -0
- package/src/asset_debugger/util/rig/rig-label-factory.js +441 -0
- package/src/asset_debugger/util/rig/rig-mouse-handler.js +377 -0
- package/src/asset_debugger/util/rig/rig-state-manager.js +175 -0
- package/src/asset_debugger/util/rig/rig-tooltip-manager.js +267 -0
- package/src/asset_debugger/util/rig/rig-ui-factory.js +700 -0
- package/src/asset_debugger/util/scene/background-manager.js +284 -0
- package/src/asset_debugger/util/scene/camera-controller.js +243 -0
- package/src/asset_debugger/util/scene/css3d-debug-controller.js +406 -0
- package/src/asset_debugger/util/scene/css3d-frame-factory.js +113 -0
- package/src/asset_debugger/util/scene/css3d-scene-manager.js +529 -0
- package/src/asset_debugger/util/scene/glb-controller.js +208 -0
- package/src/asset_debugger/util/scene/lighting-manager.js +690 -0
- package/src/asset_debugger/util/scene/threejs-model-manager.js +437 -0
- package/src/asset_debugger/util/scene/threejs-preview-manager.js +207 -0
- package/src/asset_debugger/util/scene/threejs-preview-setup.js +478 -0
- package/src/asset_debugger/util/scene/threejs-scene-controller.js +286 -0
- package/src/asset_debugger/util/scene/ui-manager.js +107 -0
- package/src/asset_debugger/util/state/animation-state.js +128 -0
- package/src/asset_debugger/util/state/css3d-state.js +83 -0
- package/src/asset_debugger/util/state/glb-preview-state.js +31 -0
- package/src/asset_debugger/util/state/log-util.js +197 -0
- package/src/asset_debugger/util/state/scene-state.js +452 -0
- package/src/asset_debugger/util/state/threejs-state.js +54 -0
- package/src/asset_debugger/util/workers/lighting-worker.js +61 -0
- package/src/asset_debugger/util/workers/model-worker.js +109 -0
- package/src/asset_debugger/util/workers/texture-worker.js +54 -0
- package/src/asset_debugger/util/workers/worker-manager.js +212 -0
- package/src/asset_debugger/widgets/mesh-info-widget.js +280 -0
- package/src/index.html +261 -0
- package/src/index.js +8 -0
- 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
|
+
}
|