@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.
- 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,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Models Module
|
|
3
|
+
*
|
|
4
|
+
* Handles creation and loading of 3D models for debugging.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as THREE from 'three';
|
|
8
|
+
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
9
|
+
import { getState, updateState } from '../state/scene-state.js';
|
|
10
|
+
import { createMaterial } from '../animation/render/pbr-material-factory.js';
|
|
11
|
+
import { fitCameraToObject } from '../../util/scene/threejs-scene-controller.js';
|
|
12
|
+
import { updateUvPanel } from '../../panels/asset-panel/uv-heading/uv-heading.js';
|
|
13
|
+
import { processModelFileForHtmlEditor, getCurrentGlbBuffer, setCurrentGlbBuffer } from './glb-controller.js';
|
|
14
|
+
import { createMeshVisibilityPanel } from '../../panels/asset-panel/mesh-heading/mesh-heading.js';
|
|
15
|
+
import { updateAtlasVisualization } from '../../panels/asset-panel/atlas-heading/atlas-heading.js';
|
|
16
|
+
import { updateRigPanel } from '../../panels/asset-panel/rig-heading/rig-heading.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Load and setup a custom model from file
|
|
20
|
+
* @param {HTMLElement} loadingIndicator - Loading indicator element to show/hide
|
|
21
|
+
* @returns {Promise} A promise that resolves when the model is loaded and set up
|
|
22
|
+
*/
|
|
23
|
+
export function loadAndSetupModel(loadingIndicator) {
|
|
24
|
+
const state = getState();
|
|
25
|
+
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const loader = new GLTFLoader();
|
|
28
|
+
const reader = new FileReader();
|
|
29
|
+
|
|
30
|
+
reader.onload = function(event) {
|
|
31
|
+
const modelData = event.target.result;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
let bufferPromises = [];
|
|
35
|
+
|
|
36
|
+
if (typeof setCurrentGlbBuffer === 'function') {
|
|
37
|
+
setCurrentGlbBuffer(modelData);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (processModelFileForHtmlEditor && state.modelFile) {
|
|
41
|
+
bufferPromises.push(processModelFileForHtmlEditor(state.modelFile));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!state.currentGlb) {
|
|
45
|
+
updateState('currentGlb', {
|
|
46
|
+
arrayBuffer: modelData,
|
|
47
|
+
fileName: state.modelFile?.name || 'model.glb',
|
|
48
|
+
fileSize: state.modelFile?.size || modelData.byteLength
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
state.currentGlb.arrayBuffer = modelData;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Promise.all(bufferPromises).then(() => {
|
|
55
|
+
const buffer = state.currentGlb?.arrayBuffer ||
|
|
56
|
+
(getCurrentGlbBuffer && getCurrentGlbBuffer());
|
|
57
|
+
|
|
58
|
+
if (!buffer) {
|
|
59
|
+
console.warn('GLB buffer was not set during processing.');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return buffer;
|
|
63
|
+
}).then((buffer) => {
|
|
64
|
+
loader.parse(modelData, '', (gltf) => {
|
|
65
|
+
try {
|
|
66
|
+
processLoadedModel(gltf);
|
|
67
|
+
resolve();
|
|
68
|
+
} catch (processError) {
|
|
69
|
+
console.error('Error processing model:', processError);
|
|
70
|
+
reject(processError);
|
|
71
|
+
}
|
|
72
|
+
}, undefined, function(error) {
|
|
73
|
+
console.error('Error loading model. Please make sure it is a valid glTF/GLB file:', error);
|
|
74
|
+
reject(error);
|
|
75
|
+
});
|
|
76
|
+
}).catch(error => {
|
|
77
|
+
console.warn('Buffer processing failed, attempting direct load:', error);
|
|
78
|
+
loader.parse(modelData, '', (gltf) => {
|
|
79
|
+
try {
|
|
80
|
+
processLoadedModel(gltf);
|
|
81
|
+
resolve();
|
|
82
|
+
} catch (processError) {
|
|
83
|
+
console.error('Error processing model:', processError);
|
|
84
|
+
reject(processError);
|
|
85
|
+
}
|
|
86
|
+
}, undefined, function(error) {
|
|
87
|
+
console.error('Error loading model. Please make sure it is a valid glTF/GLB file:', error);
|
|
88
|
+
reject(error);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
} catch (parseError) {
|
|
92
|
+
console.error('Error parsing model data:', parseError);
|
|
93
|
+
reject(parseError);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
reader.onerror = function(error) {
|
|
98
|
+
console.error('Error reading model file:', error);
|
|
99
|
+
reject(error);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
reader.readAsArrayBuffer(state.modelFile);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Process a loaded GLTF model
|
|
108
|
+
* @param {Object} gltf - The loaded GLTF data
|
|
109
|
+
*/
|
|
110
|
+
function processLoadedModel(gltf) {
|
|
111
|
+
const state = getState();
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
if (state.scene) {
|
|
115
|
+
if (state.model) {
|
|
116
|
+
state.scene.remove(state.model);
|
|
117
|
+
}
|
|
118
|
+
if (state.cube) {
|
|
119
|
+
state.scene.remove(state.cube);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
updateState('meshes', []);
|
|
124
|
+
updateState('meshGroups', {});
|
|
125
|
+
|
|
126
|
+
const model = gltf.scene;
|
|
127
|
+
updateState('model', model);
|
|
128
|
+
|
|
129
|
+
const baseMaterial = createMaterial();
|
|
130
|
+
|
|
131
|
+
const meshes = [];
|
|
132
|
+
model.traverse(node => {
|
|
133
|
+
if (node.isMesh) {
|
|
134
|
+
const originalMaterial = node.material;
|
|
135
|
+
const material = baseMaterial.clone();
|
|
136
|
+
|
|
137
|
+
if (state.textureObjects.baseColor && originalMaterial.map && material.map) {
|
|
138
|
+
material.map.offset.copy(originalMaterial.map.offset);
|
|
139
|
+
material.map.repeat.copy(originalMaterial.map.repeat);
|
|
140
|
+
material.map.rotation = originalMaterial.map.rotation;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
node.material = material;
|
|
144
|
+
|
|
145
|
+
if (state.textureObjects.orm && !node.geometry.attributes.uv2 && node.geometry.attributes.uv) {
|
|
146
|
+
node.geometry.attributes.uv2 = node.geometry.attributes.uv;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
meshes.push(node);
|
|
150
|
+
node.userData.meshId = meshes.length - 1;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
updateState('meshes', meshes);
|
|
155
|
+
state.scene.add(model);
|
|
156
|
+
|
|
157
|
+
createMeshVisibilityPanel();
|
|
158
|
+
fitCameraToObject(model);
|
|
159
|
+
|
|
160
|
+
const atlasTab = document.getElementById('atlas-tab');
|
|
161
|
+
const uvTab = document.getElementById('uv-tab');
|
|
162
|
+
|
|
163
|
+
if (atlasTab && atlasTab.classList.contains('active')) {
|
|
164
|
+
updateAtlasVisualization();
|
|
165
|
+
}
|
|
166
|
+
if (uvTab && uvTab.classList.contains('active')) {
|
|
167
|
+
updateUvPanel();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (updateRigPanel) {
|
|
171
|
+
try {
|
|
172
|
+
updateRigPanel();
|
|
173
|
+
} catch (rigError) {
|
|
174
|
+
console.error('Error updating rig panel:', rigError);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
} catch (processError) {
|
|
179
|
+
console.error('Error processing model:', processError);
|
|
180
|
+
throw processError;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Create a basic cube with loaded textures for material debugging
|
|
186
|
+
* @returns {THREE.Mesh} The created cube mesh
|
|
187
|
+
*/
|
|
188
|
+
export function createCube() {
|
|
189
|
+
const state = getState();
|
|
190
|
+
|
|
191
|
+
if (!state.scene) {
|
|
192
|
+
throw new Error("Scene not initialized. Try again after scene is ready.");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const geometry = new THREE.BoxGeometry(1, 1, 1);
|
|
196
|
+
|
|
197
|
+
if (state.textureObjects.orm && state.textureObjects.orm.image) {
|
|
198
|
+
geometry.attributes.uv2 = geometry.attributes.uv;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const material = createMaterial();
|
|
202
|
+
const cube = new THREE.Mesh(geometry, material);
|
|
203
|
+
cube.name = "Cube";
|
|
204
|
+
|
|
205
|
+
state.scene.add(cube);
|
|
206
|
+
updateState('cube', cube);
|
|
207
|
+
updateState('meshes', [cube]);
|
|
208
|
+
|
|
209
|
+
createMeshVisibilityPanel();
|
|
210
|
+
fitCameraToObject(cube);
|
|
211
|
+
|
|
212
|
+
const atlasTab = document.getElementById('atlas-tab');
|
|
213
|
+
const uvTab = document.getElementById('uv-tab');
|
|
214
|
+
|
|
215
|
+
if (atlasTab && atlasTab.classList.contains('active')) {
|
|
216
|
+
updateAtlasVisualization();
|
|
217
|
+
}
|
|
218
|
+
if (uvTab && uvTab.classList.contains('active')) {
|
|
219
|
+
updateUvPanel();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return cube;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Create a multi-material test cube for lighting showcase
|
|
227
|
+
* Each face has a different material to show different lighting properties
|
|
228
|
+
* @returns {THREE.Mesh} The created lighting test cube mesh
|
|
229
|
+
*/
|
|
230
|
+
export function createLightingTestCube() {
|
|
231
|
+
const state = getState();
|
|
232
|
+
|
|
233
|
+
if (!state.scene) {
|
|
234
|
+
throw new Error("Scene not initialized. Try again after scene is ready.");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const geometry = new THREE.BoxGeometry(1, 1, 1);
|
|
238
|
+
|
|
239
|
+
const materials = [
|
|
240
|
+
new THREE.MeshStandardMaterial({
|
|
241
|
+
color: 0x8888ff,
|
|
242
|
+
metalness: 1.0,
|
|
243
|
+
roughness: 0.1,
|
|
244
|
+
name: 'Metal'
|
|
245
|
+
}),
|
|
246
|
+
new THREE.MeshStandardMaterial({
|
|
247
|
+
color: 0xcc3333,
|
|
248
|
+
metalness: 0.0,
|
|
249
|
+
roughness: 0.9,
|
|
250
|
+
name: 'Matte'
|
|
251
|
+
}),
|
|
252
|
+
new THREE.MeshStandardMaterial({
|
|
253
|
+
color: 0x8B4513,
|
|
254
|
+
metalness: 0.0,
|
|
255
|
+
roughness: 0.7,
|
|
256
|
+
name: 'Wood'
|
|
257
|
+
}),
|
|
258
|
+
new THREE.MeshStandardMaterial({
|
|
259
|
+
color: 0x22cc22,
|
|
260
|
+
metalness: 0.1,
|
|
261
|
+
roughness: 0.5,
|
|
262
|
+
name: 'Plastic'
|
|
263
|
+
}),
|
|
264
|
+
new THREE.MeshStandardMaterial({
|
|
265
|
+
color: 0xffffff,
|
|
266
|
+
metalness: 0.0,
|
|
267
|
+
roughness: 0.3,
|
|
268
|
+
name: 'Ceramic'
|
|
269
|
+
}),
|
|
270
|
+
new THREE.MeshStandardMaterial({
|
|
271
|
+
color: 0xaaccff,
|
|
272
|
+
metalness: 0.2,
|
|
273
|
+
roughness: 0.1,
|
|
274
|
+
transparent: true,
|
|
275
|
+
opacity: 0.7,
|
|
276
|
+
name: 'Glass'
|
|
277
|
+
})
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
const cube = new THREE.Mesh(geometry, materials);
|
|
281
|
+
cube.name = "LightingTestCube";
|
|
282
|
+
|
|
283
|
+
state.scene.add(cube);
|
|
284
|
+
updateState('cube', cube);
|
|
285
|
+
updateState('meshes', [cube]);
|
|
286
|
+
|
|
287
|
+
createMeshVisibilityPanel();
|
|
288
|
+
fitCameraToObject(cube);
|
|
289
|
+
|
|
290
|
+
return cube;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Load the appropriate model for debugging
|
|
295
|
+
* @returns {Promise} A promise that resolves when the model is loaded
|
|
296
|
+
*/
|
|
297
|
+
export function loadDebugModel() {
|
|
298
|
+
const state = getState();
|
|
299
|
+
const loadingIndicator = document.getElementById('loading-indicator');
|
|
300
|
+
|
|
301
|
+
if (loadingIndicator) {
|
|
302
|
+
loadingIndicator.style.display = 'flex';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return new Promise((resolve, reject) => {
|
|
306
|
+
if (!state.scene) {
|
|
307
|
+
let attempts = 0;
|
|
308
|
+
const maxAttempts = 10;
|
|
309
|
+
const checkInterval = setInterval(() => {
|
|
310
|
+
attempts++;
|
|
311
|
+
if (state.scene) {
|
|
312
|
+
clearInterval(checkInterval);
|
|
313
|
+
handleModelLoading()
|
|
314
|
+
.then(() => {
|
|
315
|
+
if (loadingIndicator) loadingIndicator.style.display = 'none';
|
|
316
|
+
resolve();
|
|
317
|
+
})
|
|
318
|
+
.catch((error) => {
|
|
319
|
+
console.error('Model loading failed:', error);
|
|
320
|
+
if (loadingIndicator) loadingIndicator.style.display = 'none';
|
|
321
|
+
reject(error);
|
|
322
|
+
});
|
|
323
|
+
} else if (attempts >= maxAttempts) {
|
|
324
|
+
clearInterval(checkInterval);
|
|
325
|
+
const error = new Error('Scene initialization timeout');
|
|
326
|
+
console.error('Scene initialization timeout after', maxAttempts, 'attempts');
|
|
327
|
+
if (loadingIndicator) loadingIndicator.style.display = 'none';
|
|
328
|
+
reject(error);
|
|
329
|
+
}
|
|
330
|
+
}, 300);
|
|
331
|
+
} else {
|
|
332
|
+
handleModelLoading()
|
|
333
|
+
.then(() => {
|
|
334
|
+
if (loadingIndicator) loadingIndicator.style.display = 'none';
|
|
335
|
+
resolve();
|
|
336
|
+
})
|
|
337
|
+
.catch((error) => {
|
|
338
|
+
console.error('Model loading failed:', error);
|
|
339
|
+
if (loadingIndicator) loadingIndicator.style.display = 'none';
|
|
340
|
+
reject(error);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Handle model loading based on current state
|
|
348
|
+
* @returns {Promise} A promise that resolves when the model is loaded
|
|
349
|
+
*/
|
|
350
|
+
function handleModelLoading() {
|
|
351
|
+
return prepareGlbBuffer().then(loadModelBasedOnState);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Prepare the GLB buffer for HTML editor integration if a custom model file is available
|
|
356
|
+
* @returns {Promise} A promise that resolves when the GLB buffer is prepared
|
|
357
|
+
*/
|
|
358
|
+
function prepareGlbBuffer() {
|
|
359
|
+
const state = getState();
|
|
360
|
+
|
|
361
|
+
if (state.useCustomModel && state.modelFile) {
|
|
362
|
+
if (processModelFileForHtmlEditor) {
|
|
363
|
+
return processModelFileForHtmlEditor(state.modelFile)
|
|
364
|
+
.then(() => {
|
|
365
|
+
const updatedState = getState();
|
|
366
|
+
const buffer = updatedState.currentGlb?.arrayBuffer || getCurrentGlbBuffer();
|
|
367
|
+
|
|
368
|
+
if (!buffer) {
|
|
369
|
+
console.warn('GLB buffer was not set during pre-processing.');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return buffer;
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
return Promise.resolve(null);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return Promise.resolve(null);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Load the appropriate model based on current state
|
|
383
|
+
* @returns {Promise} A promise that resolves when the model is loaded
|
|
384
|
+
*/
|
|
385
|
+
function loadModelBasedOnState() {
|
|
386
|
+
const state = getState();
|
|
387
|
+
|
|
388
|
+
return new Promise((resolve, reject) => {
|
|
389
|
+
try {
|
|
390
|
+
if (state.selectedExample) {
|
|
391
|
+
if (state.selectedExample === 'rig') {
|
|
392
|
+
import('../../modals/examples-modal/examples.js').then(examplesModule => {
|
|
393
|
+
examplesModule.loadExample('wireframe-cube')
|
|
394
|
+
.then(resolve)
|
|
395
|
+
.catch(error => {
|
|
396
|
+
console.warn('Failed to load rig example:', error);
|
|
397
|
+
resolve();
|
|
398
|
+
});
|
|
399
|
+
}).catch(error => {
|
|
400
|
+
console.warn('Failed to import examples module:', error);
|
|
401
|
+
resolve();
|
|
402
|
+
});
|
|
403
|
+
} else {
|
|
404
|
+
console.warn(`Unknown example type: ${state.selectedExample}`);
|
|
405
|
+
resolve();
|
|
406
|
+
}
|
|
407
|
+
} else if (state.useCustomModel && state.modelFile) {
|
|
408
|
+
loadAndSetupModel(null)
|
|
409
|
+
.then(resolve)
|
|
410
|
+
.catch(reject);
|
|
411
|
+
} else if (state.useLightingTestCube) {
|
|
412
|
+
try {
|
|
413
|
+
createLightingTestCube();
|
|
414
|
+
resolve();
|
|
415
|
+
} catch (error) {
|
|
416
|
+
console.error('Failed to create lighting test cube:', error);
|
|
417
|
+
reject(error);
|
|
418
|
+
}
|
|
419
|
+
} else if (state.textureObjects.baseColor ||
|
|
420
|
+
state.textureObjects.orm ||
|
|
421
|
+
state.textureObjects.normal) {
|
|
422
|
+
try {
|
|
423
|
+
createCube();
|
|
424
|
+
resolve();
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.error('Failed to create cube:', error);
|
|
427
|
+
reject(error);
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
resolve();
|
|
431
|
+
}
|
|
432
|
+
} catch (error) {
|
|
433
|
+
console.error('Error in loadModelBasedOnState:', error);
|
|
434
|
+
reject(error);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { infoPanel, resetInfoPanel } from "../../widgets/mesh-info-widget";
|
|
2
|
+
import { logPreviewError } from "../state/log-util";
|
|
3
|
+
import { cleanupCSS3D } from "./css3d-scene-manager";
|
|
4
|
+
import { cleanupThreeJsScene, setupThreeJsScene } from "./threejs-preview-setup";
|
|
5
|
+
import { setIsPreviewActive, setIsPreviewAnimationPaused, setLastTextureUpdateTime } from "../state/animation-state";
|
|
6
|
+
import { reverseAnimationFrameId } from "../state/css3d-state";
|
|
7
|
+
import {
|
|
8
|
+
animationCss3dObject,
|
|
9
|
+
animationCss3dRenderer,
|
|
10
|
+
animationCss3dScene,
|
|
11
|
+
animationPreviewCamera,
|
|
12
|
+
animationPreviewRenderer,
|
|
13
|
+
animationPreviewScene,
|
|
14
|
+
frameBuffer,
|
|
15
|
+
pendingTextureUpdate,
|
|
16
|
+
previewPlane,
|
|
17
|
+
previewRenderTarget,
|
|
18
|
+
resetThreeJsState,
|
|
19
|
+
setPendingTextureUpdate
|
|
20
|
+
} from "../state/threejs-state";
|
|
21
|
+
import { previewAnimationId, resetLastAnimationFrameTime, resetPreviewAnimationId } from "../animation/playback/animation-preview-controller"
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Clean up Three.js preview resources using the generic cleanup utility
|
|
25
|
+
*/
|
|
26
|
+
export function cleanupThreeJsPreview() {
|
|
27
|
+
// Mark preview as inactive to stop animation loop first
|
|
28
|
+
setIsPreviewActive(false);
|
|
29
|
+
|
|
30
|
+
cleanupCSS3D();
|
|
31
|
+
cleanupInfoPanel();
|
|
32
|
+
|
|
33
|
+
// Set pending operations to false
|
|
34
|
+
if (pendingTextureUpdate) {
|
|
35
|
+
setPendingTextureUpdate(false);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Collect DOM elements to clean up
|
|
39
|
+
const domElements = [];
|
|
40
|
+
|
|
41
|
+
const textureCanvas = document.getElementById('html-texture-canvas');
|
|
42
|
+
if (textureCanvas) domElements.push(textureCanvas);
|
|
43
|
+
|
|
44
|
+
const hiddenContent = document.getElementById('hidden-html-content');
|
|
45
|
+
if (hiddenContent) domElements.push(hiddenContent);
|
|
46
|
+
|
|
47
|
+
const renderIframe = document.getElementById('html-render-iframe');
|
|
48
|
+
if (renderIframe) domElements.push(renderIframe);
|
|
49
|
+
|
|
50
|
+
// Collect event cleanup callbacks
|
|
51
|
+
const eventCleanupCallbacks = [
|
|
52
|
+
() => window.removeEventListener('resize', onPreviewResize),
|
|
53
|
+
() => {
|
|
54
|
+
if (animationPreviewCamera && animationPreviewCamera.userData && animationPreviewCamera.userData.keyHandler) {
|
|
55
|
+
document.removeEventListener('keydown', animationPreviewCamera.userData.keyHandler);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
// Collect objects to clean up
|
|
61
|
+
const objects = [];
|
|
62
|
+
if (previewPlane) objects.push(previewPlane);
|
|
63
|
+
if (animationCss3dObject) objects.push(animationCss3dObject);
|
|
64
|
+
|
|
65
|
+
// Additional cleanup configuration
|
|
66
|
+
const additionalCleanup = {
|
|
67
|
+
frameBuffer: frameBuffer,
|
|
68
|
+
animationFrames: [previewAnimationId, reverseAnimationFrameId].filter(id => id !== null),
|
|
69
|
+
textures: previewRenderTarget && previewRenderTarget.texture ? [previewRenderTarget.texture] : []
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Use the generic cleanup utility
|
|
73
|
+
cleanupThreeJsScene({
|
|
74
|
+
scene: animationPreviewScene,
|
|
75
|
+
camera: animationPreviewCamera,
|
|
76
|
+
renderer: animationPreviewRenderer,
|
|
77
|
+
objects,
|
|
78
|
+
domElements,
|
|
79
|
+
eventCleanupCallbacks,
|
|
80
|
+
additionalCleanup
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Handle CSS3D scene separately since it's not a standard Three.js scene
|
|
84
|
+
cleanupThreeJsScene({
|
|
85
|
+
scene: animationCss3dScene,
|
|
86
|
+
renderer: animationCss3dRenderer,
|
|
87
|
+
objects: [],
|
|
88
|
+
domElements: [],
|
|
89
|
+
eventCleanupCallbacks: [],
|
|
90
|
+
additionalCleanup: {}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Reset global variables
|
|
94
|
+
resetThreeJsState();
|
|
95
|
+
|
|
96
|
+
// Reset debug flag and global functions
|
|
97
|
+
window._css3dDebugLogged = false;
|
|
98
|
+
if (window.animateMessages) {
|
|
99
|
+
window.animateMessages = null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Reset animation state
|
|
103
|
+
setIsPreviewAnimationPaused(false);
|
|
104
|
+
setLastTextureUpdateTime(0);
|
|
105
|
+
// TODO Move this variable to animation state object
|
|
106
|
+
setPendingTextureUpdate(false);
|
|
107
|
+
resetLastAnimationFrameTime();
|
|
108
|
+
resetPreviewAnimationId();
|
|
109
|
+
|
|
110
|
+
console.log('Three.js and CSS3D resources cleaned up');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// TODO Rename to seomthing better and make it not coupled to specific panel
|
|
114
|
+
/**
|
|
115
|
+
* Clean up info panel resources
|
|
116
|
+
* Removes the info panel from the DOM and resets the infoPanel reference
|
|
117
|
+
* This is called when closing the preview or switching between preview modes
|
|
118
|
+
*/
|
|
119
|
+
function cleanupInfoPanel() {
|
|
120
|
+
if (infoPanel) {
|
|
121
|
+
try {
|
|
122
|
+
if (infoPanel.parentNode) {
|
|
123
|
+
infoPanel.parentNode.removeChild(infoPanel);
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.log('Error removing info panel:', e);
|
|
127
|
+
}
|
|
128
|
+
resetInfoPanel();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Handle window resize for the Three.js preview
|
|
134
|
+
*/
|
|
135
|
+
function onPreviewResize() {
|
|
136
|
+
const container = animationPreviewRenderer.domElement.parentElement;
|
|
137
|
+
if (!container) return;
|
|
138
|
+
|
|
139
|
+
const containerWidth = container.clientWidth;
|
|
140
|
+
const containerHeight = container.clientHeight;
|
|
141
|
+
|
|
142
|
+
// Update camera aspect ratio
|
|
143
|
+
if (animationPreviewCamera) {
|
|
144
|
+
animationPreviewCamera.aspect = containerWidth / containerHeight;
|
|
145
|
+
animationPreviewCamera.updateProjectionMatrix();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Update renderer
|
|
149
|
+
animationPreviewRenderer.setSize(containerWidth, containerHeight);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Initialize Three.js for HTML preview
|
|
154
|
+
* @param {HTMLElement} container - The container element for the Three.js canvas
|
|
155
|
+
* @param {HTMLIFrameElement} iframe - The iframe containing the HTML to render as texture
|
|
156
|
+
* @param {number} currentMeshId - The ID of the current mesh
|
|
157
|
+
* @param {boolean} createInfoPanel - Whether to create the info panel
|
|
158
|
+
*/
|
|
159
|
+
export function initThreeJsPreview(container, iframe, currentMeshId, createInfoPanel = true) {
|
|
160
|
+
// Handle window resize
|
|
161
|
+
window.addEventListener('resize', onPreviewResize);
|
|
162
|
+
try {
|
|
163
|
+
// We already have THREE imported at the top of the file
|
|
164
|
+
console.log('Using imported Three.js module');
|
|
165
|
+
|
|
166
|
+
// Only need to load html2canvas
|
|
167
|
+
loadHtml2Canvas(() => {
|
|
168
|
+
setupThreeJsScene(container, iframe, currentMeshId, createInfoPanel);
|
|
169
|
+
});
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('Error initializing Three.js preview:', error);
|
|
172
|
+
logPreviewError(`Three.js initialization error: ${error.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Load html2canvas library
|
|
178
|
+
* @param {Function} callback - Function to call when loading is complete
|
|
179
|
+
*/
|
|
180
|
+
function loadHtml2Canvas(callback) {
|
|
181
|
+
// Check if html2canvas is already loaded
|
|
182
|
+
if (typeof window.html2canvas !== 'undefined') {
|
|
183
|
+
callback();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Check if it's already being loaded
|
|
188
|
+
if (document.querySelector('script[src*="html2canvas"]')) {
|
|
189
|
+
const checkInterval = setInterval(() => {
|
|
190
|
+
if (typeof window.html2canvas !== 'undefined') {
|
|
191
|
+
clearInterval(checkInterval);
|
|
192
|
+
callback();
|
|
193
|
+
}
|
|
194
|
+
}, 100);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Load html2canvas
|
|
199
|
+
console.log('Loading html2canvas library');
|
|
200
|
+
const script = document.createElement('script');
|
|
201
|
+
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js';
|
|
202
|
+
script.onload = callback;
|
|
203
|
+
script.onerror = (error) => {
|
|
204
|
+
console.error('Failed to load html2canvas:', error);
|
|
205
|
+
};
|
|
206
|
+
document.head.appendChild(script);
|
|
207
|
+
}
|