@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,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
|
+
}
|