@littlecarlito/blorktools 0.50.4 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/bin/cli.js +69 -0
  2. package/package.json +13 -7
  3. package/src/asset_debugger/axis-indicator/axis-indicator.css +6 -0
  4. package/src/asset_debugger/axis-indicator/axis-indicator.html +20 -0
  5. package/src/asset_debugger/axis-indicator/axis-indicator.js +822 -0
  6. package/src/asset_debugger/debugger-scene/debugger-scene.css +142 -0
  7. package/src/asset_debugger/debugger-scene/debugger-scene.html +80 -0
  8. package/src/asset_debugger/debugger-scene/debugger-scene.js +791 -0
  9. package/src/asset_debugger/header/header.css +73 -0
  10. package/src/asset_debugger/header/header.html +24 -0
  11. package/src/asset_debugger/header/header.js +224 -0
  12. package/src/asset_debugger/index.html +76 -0
  13. package/src/asset_debugger/landing-page/landing-page.css +396 -0
  14. package/src/asset_debugger/landing-page/landing-page.html +81 -0
  15. package/src/asset_debugger/landing-page/landing-page.js +611 -0
  16. package/src/asset_debugger/loading-splash/loading-splash.css +195 -0
  17. package/src/asset_debugger/loading-splash/loading-splash.html +22 -0
  18. package/src/asset_debugger/loading-splash/loading-splash.js +59 -0
  19. package/src/asset_debugger/loading-splash/preview-loading-splash.js +66 -0
  20. package/src/asset_debugger/main.css +14 -0
  21. package/src/asset_debugger/modals/examples-modal/examples-modal.css +41 -0
  22. package/src/asset_debugger/modals/examples-modal/examples-modal.html +18 -0
  23. package/src/asset_debugger/modals/examples-modal/examples-modal.js +111 -0
  24. package/src/asset_debugger/modals/examples-modal/examples.js +125 -0
  25. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.css +452 -0
  26. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.html +87 -0
  27. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.js +675 -0
  28. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.css +219 -0
  29. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.html +20 -0
  30. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.js +548 -0
  31. package/src/asset_debugger/modals/settings-modal/settings-modal.css +103 -0
  32. package/src/asset_debugger/modals/settings-modal/settings-modal.html +158 -0
  33. package/src/asset_debugger/modals/settings-modal/settings-modal.js +475 -0
  34. package/src/asset_debugger/panels/asset-panel/asset-panel.css +263 -0
  35. package/src/asset_debugger/panels/asset-panel/asset-panel.html +123 -0
  36. package/src/asset_debugger/panels/asset-panel/asset-panel.js +136 -0
  37. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.css +94 -0
  38. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.js +312 -0
  39. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.css +129 -0
  40. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.js +486 -0
  41. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.css +545 -0
  42. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.js +538 -0
  43. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.css +70 -0
  44. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.js +586 -0
  45. package/src/asset_debugger/panels/world-panel/world-panel.css +364 -0
  46. package/src/asset_debugger/panels/world-panel/world-panel.html +173 -0
  47. package/src/asset_debugger/panels/world-panel/world-panel.js +1891 -0
  48. package/src/asset_debugger/router.js +190 -0
  49. package/src/asset_debugger/util/animation/playback/animation-playback-controller.js +150 -0
  50. package/src/asset_debugger/util/animation/playback/animation-preview-controller.js +316 -0
  51. package/src/asset_debugger/util/animation/playback/css3d-bounce-controller.js +400 -0
  52. package/src/asset_debugger/util/animation/playback/css3d-reversal-controller.js +821 -0
  53. package/src/asset_debugger/util/animation/render/css3d-prerender-controller.js +696 -0
  54. package/src/asset_debugger/util/animation/render/debug-texture-factory.js +0 -0
  55. package/src/asset_debugger/util/animation/render/iframe2texture-render-controller.js +199 -0
  56. package/src/asset_debugger/util/animation/render/image2texture-prerender-controller.js +461 -0
  57. package/src/asset_debugger/util/animation/render/pbr-material-factory.js +82 -0
  58. package/src/asset_debugger/util/common.css +280 -0
  59. package/src/asset_debugger/util/data/animation-classifier.js +323 -0
  60. package/src/asset_debugger/util/data/duplicate-handler.js +20 -0
  61. package/src/asset_debugger/util/data/glb-buffer-manager.js +407 -0
  62. package/src/asset_debugger/util/data/glb-classifier.js +290 -0
  63. package/src/asset_debugger/util/data/html-formatter.js +76 -0
  64. package/src/asset_debugger/util/data/html-linter.js +276 -0
  65. package/src/asset_debugger/util/data/localstorage-manager.js +265 -0
  66. package/src/asset_debugger/util/data/mesh-html-manager.js +295 -0
  67. package/src/asset_debugger/util/data/string-serder.js +303 -0
  68. package/src/asset_debugger/util/data/texture-classifier.js +663 -0
  69. package/src/asset_debugger/util/data/upload/background-file-handler.js +292 -0
  70. package/src/asset_debugger/util/data/upload/dropzone-preview-controller.js +396 -0
  71. package/src/asset_debugger/util/data/upload/file-upload-manager.js +495 -0
  72. package/src/asset_debugger/util/data/upload/glb-file-handler.js +36 -0
  73. package/src/asset_debugger/util/data/upload/glb-preview-controller.js +317 -0
  74. package/src/asset_debugger/util/data/upload/lighting-file-handler.js +194 -0
  75. package/src/asset_debugger/util/data/upload/model-file-manager.js +104 -0
  76. package/src/asset_debugger/util/data/upload/texture-file-handler.js +166 -0
  77. package/src/asset_debugger/util/data/upload/zip-handler.js +686 -0
  78. package/src/asset_debugger/util/loaders/html2canvas-loader.js +107 -0
  79. package/src/asset_debugger/util/rig/bone-kinematics.js +403 -0
  80. package/src/asset_debugger/util/rig/rig-constraint-manager.js +618 -0
  81. package/src/asset_debugger/util/rig/rig-controller.js +612 -0
  82. package/src/asset_debugger/util/rig/rig-factory.js +628 -0
  83. package/src/asset_debugger/util/rig/rig-handle-factory.js +46 -0
  84. package/src/asset_debugger/util/rig/rig-label-factory.js +441 -0
  85. package/src/asset_debugger/util/rig/rig-mouse-handler.js +377 -0
  86. package/src/asset_debugger/util/rig/rig-state-manager.js +175 -0
  87. package/src/asset_debugger/util/rig/rig-tooltip-manager.js +267 -0
  88. package/src/asset_debugger/util/rig/rig-ui-factory.js +700 -0
  89. package/src/asset_debugger/util/scene/background-manager.js +284 -0
  90. package/src/asset_debugger/util/scene/camera-controller.js +243 -0
  91. package/src/asset_debugger/util/scene/css3d-debug-controller.js +406 -0
  92. package/src/asset_debugger/util/scene/css3d-frame-factory.js +113 -0
  93. package/src/asset_debugger/util/scene/css3d-scene-manager.js +529 -0
  94. package/src/asset_debugger/util/scene/glb-controller.js +208 -0
  95. package/src/asset_debugger/util/scene/lighting-manager.js +690 -0
  96. package/src/asset_debugger/util/scene/threejs-model-manager.js +437 -0
  97. package/src/asset_debugger/util/scene/threejs-preview-manager.js +207 -0
  98. package/src/asset_debugger/util/scene/threejs-preview-setup.js +478 -0
  99. package/src/asset_debugger/util/scene/threejs-scene-controller.js +286 -0
  100. package/src/asset_debugger/util/scene/ui-manager.js +107 -0
  101. package/src/asset_debugger/util/state/animation-state.js +128 -0
  102. package/src/asset_debugger/util/state/css3d-state.js +83 -0
  103. package/src/asset_debugger/util/state/glb-preview-state.js +31 -0
  104. package/src/asset_debugger/util/state/log-util.js +197 -0
  105. package/src/asset_debugger/util/state/scene-state.js +452 -0
  106. package/src/asset_debugger/util/state/threejs-state.js +54 -0
  107. package/src/asset_debugger/util/workers/lighting-worker.js +61 -0
  108. package/src/asset_debugger/util/workers/model-worker.js +109 -0
  109. package/src/asset_debugger/util/workers/texture-worker.js +54 -0
  110. package/src/asset_debugger/util/workers/worker-manager.js +212 -0
  111. package/src/asset_debugger/widgets/mesh-info-widget.js +280 -0
  112. package/src/index.html +261 -0
  113. package/src/index.js +8 -0
  114. package/vite.config.js +66 -0
@@ -0,0 +1,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
+ }