@littlecarlito/blorktools 0.50.3 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/bin/cli.js +69 -0
  2. package/package.json +13 -7
  3. package/src/asset_debugger/axis-indicator/axis-indicator.css +6 -0
  4. package/src/asset_debugger/axis-indicator/axis-indicator.html +20 -0
  5. package/src/asset_debugger/axis-indicator/axis-indicator.js +822 -0
  6. package/src/asset_debugger/debugger-scene/debugger-scene.css +142 -0
  7. package/src/asset_debugger/debugger-scene/debugger-scene.html +80 -0
  8. package/src/asset_debugger/debugger-scene/debugger-scene.js +791 -0
  9. package/src/asset_debugger/header/header.css +73 -0
  10. package/src/asset_debugger/header/header.html +24 -0
  11. package/src/asset_debugger/header/header.js +224 -0
  12. package/src/asset_debugger/index.html +76 -0
  13. package/src/asset_debugger/landing-page/landing-page.css +396 -0
  14. package/src/asset_debugger/landing-page/landing-page.html +81 -0
  15. package/src/asset_debugger/landing-page/landing-page.js +611 -0
  16. package/src/asset_debugger/loading-splash/loading-splash.css +195 -0
  17. package/src/asset_debugger/loading-splash/loading-splash.html +22 -0
  18. package/src/asset_debugger/loading-splash/loading-splash.js +59 -0
  19. package/src/asset_debugger/loading-splash/preview-loading-splash.js +66 -0
  20. package/src/asset_debugger/main.css +14 -0
  21. package/src/asset_debugger/modals/examples-modal/examples-modal.css +41 -0
  22. package/src/asset_debugger/modals/examples-modal/examples-modal.html +18 -0
  23. package/src/asset_debugger/modals/examples-modal/examples-modal.js +111 -0
  24. package/src/asset_debugger/modals/examples-modal/examples.js +125 -0
  25. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.css +452 -0
  26. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.html +87 -0
  27. package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.js +675 -0
  28. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.css +219 -0
  29. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.html +20 -0
  30. package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.js +548 -0
  31. package/src/asset_debugger/modals/settings-modal/settings-modal.css +103 -0
  32. package/src/asset_debugger/modals/settings-modal/settings-modal.html +158 -0
  33. package/src/asset_debugger/modals/settings-modal/settings-modal.js +475 -0
  34. package/src/asset_debugger/panels/asset-panel/asset-panel.css +263 -0
  35. package/src/asset_debugger/panels/asset-panel/asset-panel.html +123 -0
  36. package/src/asset_debugger/panels/asset-panel/asset-panel.js +136 -0
  37. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.css +94 -0
  38. package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.js +312 -0
  39. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.css +129 -0
  40. package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.js +486 -0
  41. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.css +545 -0
  42. package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.js +538 -0
  43. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.css +70 -0
  44. package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.js +586 -0
  45. package/src/asset_debugger/panels/world-panel/world-panel.css +364 -0
  46. package/src/asset_debugger/panels/world-panel/world-panel.html +173 -0
  47. package/src/asset_debugger/panels/world-panel/world-panel.js +1891 -0
  48. package/src/asset_debugger/router.js +190 -0
  49. package/src/asset_debugger/util/animation/playback/animation-playback-controller.js +150 -0
  50. package/src/asset_debugger/util/animation/playback/animation-preview-controller.js +316 -0
  51. package/src/asset_debugger/util/animation/playback/css3d-bounce-controller.js +400 -0
  52. package/src/asset_debugger/util/animation/playback/css3d-reversal-controller.js +821 -0
  53. package/src/asset_debugger/util/animation/render/css3d-prerender-controller.js +696 -0
  54. package/src/asset_debugger/util/animation/render/debug-texture-factory.js +0 -0
  55. package/src/asset_debugger/util/animation/render/iframe2texture-render-controller.js +199 -0
  56. package/src/asset_debugger/util/animation/render/image2texture-prerender-controller.js +461 -0
  57. package/src/asset_debugger/util/animation/render/pbr-material-factory.js +82 -0
  58. package/src/asset_debugger/util/common.css +280 -0
  59. package/src/asset_debugger/util/data/animation-classifier.js +323 -0
  60. package/src/asset_debugger/util/data/duplicate-handler.js +20 -0
  61. package/src/asset_debugger/util/data/glb-buffer-manager.js +407 -0
  62. package/src/asset_debugger/util/data/glb-classifier.js +290 -0
  63. package/src/asset_debugger/util/data/html-formatter.js +76 -0
  64. package/src/asset_debugger/util/data/html-linter.js +276 -0
  65. package/src/asset_debugger/util/data/localstorage-manager.js +265 -0
  66. package/src/asset_debugger/util/data/mesh-html-manager.js +295 -0
  67. package/src/asset_debugger/util/data/string-serder.js +303 -0
  68. package/src/asset_debugger/util/data/texture-classifier.js +663 -0
  69. package/src/asset_debugger/util/data/upload/background-file-handler.js +292 -0
  70. package/src/asset_debugger/util/data/upload/dropzone-preview-controller.js +396 -0
  71. package/src/asset_debugger/util/data/upload/file-upload-manager.js +495 -0
  72. package/src/asset_debugger/util/data/upload/glb-file-handler.js +36 -0
  73. package/src/asset_debugger/util/data/upload/glb-preview-controller.js +317 -0
  74. package/src/asset_debugger/util/data/upload/lighting-file-handler.js +194 -0
  75. package/src/asset_debugger/util/data/upload/model-file-manager.js +104 -0
  76. package/src/asset_debugger/util/data/upload/texture-file-handler.js +166 -0
  77. package/src/asset_debugger/util/data/upload/zip-handler.js +686 -0
  78. package/src/asset_debugger/util/loaders/html2canvas-loader.js +107 -0
  79. package/src/asset_debugger/util/rig/bone-kinematics.js +403 -0
  80. package/src/asset_debugger/util/rig/rig-constraint-manager.js +618 -0
  81. package/src/asset_debugger/util/rig/rig-controller.js +612 -0
  82. package/src/asset_debugger/util/rig/rig-factory.js +628 -0
  83. package/src/asset_debugger/util/rig/rig-handle-factory.js +46 -0
  84. package/src/asset_debugger/util/rig/rig-label-factory.js +441 -0
  85. package/src/asset_debugger/util/rig/rig-mouse-handler.js +377 -0
  86. package/src/asset_debugger/util/rig/rig-state-manager.js +175 -0
  87. package/src/asset_debugger/util/rig/rig-tooltip-manager.js +267 -0
  88. package/src/asset_debugger/util/rig/rig-ui-factory.js +700 -0
  89. package/src/asset_debugger/util/scene/background-manager.js +284 -0
  90. package/src/asset_debugger/util/scene/camera-controller.js +243 -0
  91. package/src/asset_debugger/util/scene/css3d-debug-controller.js +406 -0
  92. package/src/asset_debugger/util/scene/css3d-frame-factory.js +113 -0
  93. package/src/asset_debugger/util/scene/css3d-scene-manager.js +529 -0
  94. package/src/asset_debugger/util/scene/glb-controller.js +208 -0
  95. package/src/asset_debugger/util/scene/lighting-manager.js +690 -0
  96. package/src/asset_debugger/util/scene/threejs-model-manager.js +437 -0
  97. package/src/asset_debugger/util/scene/threejs-preview-manager.js +207 -0
  98. package/src/asset_debugger/util/scene/threejs-preview-setup.js +478 -0
  99. package/src/asset_debugger/util/scene/threejs-scene-controller.js +286 -0
  100. package/src/asset_debugger/util/scene/ui-manager.js +107 -0
  101. package/src/asset_debugger/util/state/animation-state.js +128 -0
  102. package/src/asset_debugger/util/state/css3d-state.js +83 -0
  103. package/src/asset_debugger/util/state/glb-preview-state.js +31 -0
  104. package/src/asset_debugger/util/state/log-util.js +197 -0
  105. package/src/asset_debugger/util/state/scene-state.js +452 -0
  106. package/src/asset_debugger/util/state/threejs-state.js +54 -0
  107. package/src/asset_debugger/util/workers/lighting-worker.js +61 -0
  108. package/src/asset_debugger/util/workers/model-worker.js +109 -0
  109. package/src/asset_debugger/util/workers/texture-worker.js +54 -0
  110. package/src/asset_debugger/util/workers/worker-manager.js +212 -0
  111. package/src/asset_debugger/widgets/mesh-info-widget.js +280 -0
  112. package/src/index.html +261 -0
  113. package/src/index.js +8 -0
  114. package/vite.config.js +66 -0
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Background Image Utilities
3
+ *
4
+ * Handles loading and setting up background images for the Asset Debugger
5
+ */
6
+
7
+ import * as THREE from 'three';
8
+ import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
9
+ import { EXRLoader } from 'three/examples/jsm/loaders/EXRLoader.js';
10
+ import { getState, updateState } from '../state/scene-state.js';
11
+
12
+ // Track loaded textures to avoid duplicates
13
+ const textureCache = new Map();
14
+
15
+ /**
16
+ * Sets up a background image in the scene
17
+ * @param {File} backgroundFile - The background image file (HDR, EXR, JPEG, PNG, WebP, or TIFF)
18
+ * @returns {Promise<THREE.Texture|null>} - Resolves with the texture when the background is set up
19
+ */
20
+ export function setupBackgroundImage(backgroundFile) {
21
+ return new Promise((resolve, reject) => {
22
+ if (!backgroundFile) {
23
+ console.warn('No background file provided');
24
+ resolve(null);
25
+ return;
26
+ }
27
+
28
+ const state = getState();
29
+ const scene = state.scene;
30
+
31
+ if (!scene) {
32
+ console.error('Scene not available for background setup');
33
+ reject(new Error('Scene not available'));
34
+ return;
35
+ }
36
+
37
+ // Check if we've already loaded this texture
38
+ const cachedTexture = textureCache.get(backgroundFile.name);
39
+ if (cachedTexture) {
40
+ applyBackgroundTexture(cachedTexture, backgroundFile);
41
+ resolve(cachedTexture);
42
+ return;
43
+ }
44
+
45
+ // Choose the appropriate loader based on file extension
46
+ const fileExtension = backgroundFile.name.split('.').pop().toLowerCase();
47
+
48
+ try {
49
+ if (fileExtension === 'hdr') {
50
+ loadHDRBackground(backgroundFile, resolve, reject);
51
+ } else if (fileExtension === 'exr') {
52
+ loadEXRBackground(backgroundFile, resolve, reject);
53
+ } else if (['jpg', 'jpeg', 'png', 'webp', 'tiff'].includes(fileExtension)) {
54
+ loadStandardBackground(backgroundFile, resolve, reject);
55
+ } else {
56
+ console.error('Unsupported background image format:', fileExtension);
57
+ reject(new Error(`Unsupported background image format: ${fileExtension}`));
58
+ }
59
+ } catch (error) {
60
+ console.error('Error setting up background:', error);
61
+ reject(error);
62
+ }
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Loads an HDR file as background
68
+ * @param {File} file - The HDR file
69
+ * @param {Function} resolve - Promise resolve function
70
+ * @param {Function} reject - Promise reject function
71
+ */
72
+ function loadHDRBackground(file, resolve, reject) {
73
+ const reader = new FileReader();
74
+ reader.onload = function(event) {
75
+ const loader = new RGBELoader();
76
+ loader.load(
77
+ event.target.result,
78
+ (texture) => {
79
+ texture.mapping = THREE.EquirectangularReflectionMapping;
80
+ applyBackgroundTexture(texture, file);
81
+ textureCache.set(file.name, texture);
82
+ resolve(texture);
83
+ },
84
+ undefined,
85
+ (error) => {
86
+ console.error('Error loading HDR background:', error);
87
+ reject(error);
88
+ }
89
+ );
90
+ };
91
+ reader.onerror = function(event) {
92
+ console.error('Error reading file:', event);
93
+ reject(new Error('Error reading file'));
94
+ };
95
+ reader.readAsDataURL(file);
96
+ }
97
+
98
+ /**
99
+ * Loads an EXR file as background
100
+ * @param {File} file - The EXR file
101
+ * @param {Function} resolve - Promise resolve function
102
+ * @param {Function} reject - Promise reject function
103
+ */
104
+ function loadEXRBackground(file, resolve, reject) {
105
+ const reader = new FileReader();
106
+ reader.onload = function(event) {
107
+ const loader = new EXRLoader();
108
+ loader.load(
109
+ event.target.result,
110
+ (texture) => {
111
+ texture.mapping = THREE.EquirectangularReflectionMapping;
112
+ applyBackgroundTexture(texture, file);
113
+ textureCache.set(file.name, texture);
114
+ resolve(texture);
115
+ },
116
+ undefined,
117
+ (error) => {
118
+ console.error('Error loading EXR background:', error);
119
+ reject(error);
120
+ }
121
+ );
122
+ };
123
+ reader.onerror = function(event) {
124
+ console.error('Error reading file:', event);
125
+ reject(new Error('Error reading file'));
126
+ };
127
+ reader.readAsDataURL(file);
128
+ }
129
+
130
+ /**
131
+ * Loads a standard image format (JPEG, PNG, WebP, TIFF) as background
132
+ * @param {File} file - The image file
133
+ * @param {Function} resolve - Promise resolve function
134
+ * @param {Function} reject - Promise reject function
135
+ */
136
+ function loadStandardBackground(file, resolve, reject) {
137
+ const reader = new FileReader();
138
+ reader.onload = function(event) {
139
+ const loader = new THREE.TextureLoader();
140
+ loader.load(
141
+ event.target.result,
142
+ (texture) => {
143
+ // For standard images, we'll now use equirectangular mapping
144
+ // to create a 360° background, just like HDR/EXR files
145
+ texture.mapping = THREE.EquirectangularReflectionMapping;
146
+
147
+ // Apply the texture to the background
148
+ applyBackgroundTexture(texture, file);
149
+ textureCache.set(file.name, texture);
150
+ resolve(texture);
151
+ },
152
+ undefined,
153
+ (error) => {
154
+ console.error('Error loading standard background:', error);
155
+ reject(error);
156
+ }
157
+ );
158
+ };
159
+ reader.onerror = function(event) {
160
+ console.error('Error reading file:', event);
161
+ reject(new Error('Error reading file'));
162
+ };
163
+ reader.readAsDataURL(file);
164
+ }
165
+
166
+ /**
167
+ * Applies the background texture to the scene
168
+ * @param {THREE.Texture} texture - The texture to apply as background
169
+ * @param {File} file - The original file
170
+ */
171
+ function applyBackgroundTexture(texture, file) {
172
+ const state = getState();
173
+
174
+ if (!state.scene) {
175
+ console.error('Scene not available for background texture application');
176
+ return;
177
+ }
178
+
179
+ console.debug('Adding background texture to state');
180
+
181
+ // Store the file and texture in state for reference
182
+ updateState({
183
+ backgroundFile: file,
184
+ backgroundTexture: texture
185
+ });
186
+
187
+ // DO NOT automatically set the scene background
188
+ // This allows the UI radio button selection to control visibility instead
189
+
190
+ // Dispatch an event to notify UI components
191
+ const event = new CustomEvent('background-updated', {
192
+ detail: { texture, file }
193
+ });
194
+ document.dispatchEvent(event);
195
+
196
+ // Call into world panel to update metadata, but don't change the radio selection
197
+ import('../../panels/world-panel/world-panel.js').then(worldPanelModule => {
198
+ if (worldPanelModule.updateBackgroundInfo) {
199
+ // Get metadata to display in the UI
200
+ const metadata = {
201
+ fileName: file.name,
202
+ type: file.type || file.name.split('.').pop().toUpperCase(),
203
+ dimensions: {
204
+ width: texture.image?.width || 0,
205
+ height: texture.image?.height || 0
206
+ },
207
+ fileSizeBytes: file.size
208
+ };
209
+
210
+ // Update the background info panel with this data
211
+ worldPanelModule.updateBackgroundInfo(metadata, false);
212
+
213
+ // Make sure the "None" radio is still selected
214
+ const noneRadio = document.querySelector('input[name="bg-option"][value="none"]');
215
+ if (noneRadio) {
216
+ noneRadio.checked = true;
217
+
218
+ // If there's a current option in world panel, also update it
219
+ if (typeof worldPanelModule.setCurrentBackgroundOption === 'function') {
220
+ worldPanelModule.setCurrentBackgroundOption('none');
221
+ }
222
+ }
223
+ }
224
+ }).catch(error => {
225
+ console.warn('Could not update world panel with background info:', error);
226
+ });
227
+
228
+ console.debug('Background texture added to state successfully');
229
+ }
230
+
231
+ /**
232
+ * Toggles the visibility of the background image
233
+ * @param {boolean} visible - Whether the background should be visible
234
+ */
235
+ export function toggleBackgroundVisibility(visible) {
236
+ const state = getState();
237
+
238
+ if (!state.scene) {
239
+ console.error('Scene not available for background visibility toggle');
240
+ return;
241
+ }
242
+
243
+ // Get the current background texture from state
244
+ const backgroundTexture = state.backgroundTexture;
245
+ const scene = state.scene;
246
+
247
+ if (visible) {
248
+ // Show the background - apply the texture if available
249
+ if (backgroundTexture) {
250
+ console.debug('Showing background texture');
251
+
252
+ // For all textures, set the scene background directly
253
+ scene.background = backgroundTexture;
254
+ console.debug('Restored background texture');
255
+
256
+ // Dispatch event to notify UI components
257
+ const event = new CustomEvent('background-visibility-changed', {
258
+ detail: { visible: true, texture: backgroundTexture }
259
+ });
260
+ document.dispatchEvent(event);
261
+ } else if (state.backgroundFile) {
262
+ // If we have a background file but no texture, try to load it
263
+ console.debug('No texture but have file, reloading background:',
264
+ state.backgroundFile.name);
265
+ setupBackgroundImage(state.backgroundFile);
266
+ }
267
+ } else {
268
+ // Hide the background
269
+
270
+ // Save and hide any background
271
+ if (scene.background) {
272
+ if (!scene.userData) scene.userData = {};
273
+ scene.userData.savedBackground = scene.background;
274
+ scene.background = null;
275
+ console.debug('Background hidden');
276
+ }
277
+
278
+ // Dispatch event to notify UI components
279
+ const event = new CustomEvent('background-visibility-changed', {
280
+ detail: { visible: false }
281
+ });
282
+ document.dispatchEvent(event);
283
+ }
284
+ }
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Asset Debugger - Camera Controls Module
3
+ *
4
+ * This module handles camera controls for the Asset Debugger.
5
+ * It provides functions to initialize, configure, and check the status of camera controls.
6
+ * This is the single source of truth for OrbitControls in the application.
7
+ */
8
+ import * as THREE from 'three';
9
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
10
+ import { getState, updateState } from '../state/scene-state';
11
+
12
+ // Store a reference to the controls instance
13
+ let controlsInstance = null;
14
+ let previousState = { target: new THREE.Vector3(), position: new THREE.Vector3() };
15
+ let isInitialized = false;
16
+
17
+ /**
18
+ * Initialize camera controls
19
+ * @param {THREE.Camera} camera - The camera to control
20
+ * @param {HTMLElement} domElement - The DOM element for control events
21
+ * @returns {OrbitControls} The initialized controls
22
+ */
23
+ export function initControls(camera, domElement) {
24
+ console.log('Controls: initControls called');
25
+
26
+ // Prevent multiple initializations - but only if we actually have a valid instance
27
+ if (isInitialized && controlsInstance && !controlsInstance._disposed) {
28
+ console.warn('Controls already initialized, returning existing instance');
29
+ return controlsInstance;
30
+ }
31
+
32
+ // Reset flags in case of stale state
33
+ isInitialized = false;
34
+ controlsInstance = null;
35
+
36
+ if (!camera) {
37
+ throw new Error('Camera is required to initialize controls');
38
+ }
39
+
40
+ if (!domElement) {
41
+ throw new Error('DOM element is required to initialize controls');
42
+ }
43
+
44
+ // TODO If works refactor to function
45
+ // Prevent standard right click behavior
46
+ let controlsActive = false;
47
+ let isDragging = false;
48
+ // Set state when orbit controls are engaged
49
+ document.addEventListener('mousedown', function(e) {
50
+ if (e.button === 2) { // Right mouse button
51
+ controlsActive = true;
52
+ isDragging = true;
53
+ }
54
+ });
55
+ document.addEventListener('mouseup', function(e) {
56
+ if (e.button === 2) {
57
+ isDragging = false;
58
+ // Keep controlsActive true briefly to prevent context menu
59
+ setTimeout(() => controlsActive = false, 50);
60
+ }
61
+ });
62
+ document.addEventListener('contextmenu', function(e) {
63
+ if (controlsActive || isDragging) {
64
+ e.preventDefault();
65
+ return false;
66
+ }
67
+ });
68
+
69
+
70
+ const controls = new OrbitControls(camera, domElement);
71
+
72
+ // Configure controls
73
+ controls.enableDamping = true;
74
+ controls.dampingFactor = 0.05;
75
+ controls.screenSpacePanning = true;
76
+ controls.minDistance = 0.5;
77
+ controls.maxDistance = 50;
78
+ controls.maxPolarAngle = Math.PI;
79
+
80
+ // Track mouse state to prevent inertia interruption
81
+ let isMouseDown = false;
82
+
83
+ // Store the previous camera state on mouse down
84
+ domElement.addEventListener('mousedown', () => {
85
+ isMouseDown = true;
86
+ previousState.target.copy(controls.target);
87
+ previousState.position.copy(camera.position);
88
+ });
89
+
90
+ // Handle movement continuation on mouse up
91
+ domElement.addEventListener('mouseup', () => {
92
+ isMouseDown = false;
93
+ });
94
+
95
+ // Store controls in local reference first
96
+ controlsInstance = controls;
97
+ isInitialized = true;
98
+
99
+ // Store in state
100
+ console.log('Controls: Storing controls in state');
101
+ updateState('controls', controls);
102
+
103
+ console.log('Controls initialized successfully');
104
+ return controls;
105
+ }
106
+
107
+ /**
108
+ * Get the OrbitControls instance
109
+ * This allows other modules to access the controls without importing OrbitControls directly
110
+ * @returns {OrbitControls|null} The controls instance or null if not initialized
111
+ */
112
+ export function getControls() {
113
+ return controlsInstance;
114
+ }
115
+
116
+ /**
117
+ * Create controls for the scene
118
+ * Function signature matches what's used in scene.js
119
+ * @param {THREE.Camera} camera - The camera to control
120
+ * @param {HTMLElement} domElement - The DOM element for control events
121
+ * @returns {OrbitControls} The initialized controls
122
+ */
123
+ export function createControls(camera, domElement) {
124
+ console.log('Controls: createControls called');
125
+ return initControls(camera, domElement);
126
+ }
127
+
128
+ /**
129
+ * Check if controls are initialized and ready
130
+ * @returns {boolean} True if controls are ready, false otherwise
131
+ */
132
+ export function areControlsReady() {
133
+ return isInitialized && !!controlsInstance;
134
+ }
135
+
136
+ /**
137
+ * Reset controls to default position
138
+ */
139
+ export function resetControls() {
140
+ const controls = getControls();
141
+ if (!controls) {
142
+ console.warn('Cannot reset controls: not initialized');
143
+ return;
144
+ }
145
+
146
+ controls.reset();
147
+ }
148
+
149
+ /**
150
+ * Force recreation of controls - use when controls are not working properly
151
+ * @param {THREE.Camera} camera - The camera to control
152
+ * @param {HTMLElement} domElement - The DOM element for control events
153
+ * @returns {OrbitControls} The new controls instance
154
+ */
155
+ export function recreateControls(camera, domElement) {
156
+ console.log('Controls: Forcibly recreating controls');
157
+
158
+ // Clean up existing controls
159
+ disposeControls();
160
+
161
+ // Reset flags
162
+ isInitialized = false;
163
+ controlsInstance = null;
164
+
165
+ // Create new controls
166
+ return initControls(camera, domElement);
167
+ }
168
+
169
+ /**
170
+ * Dispose controls properly to prevent memory leaks
171
+ */
172
+ export function disposeControls() {
173
+ const controls = getControls();
174
+ if (!controls) {
175
+ return;
176
+ }
177
+
178
+ console.log('Controls: Disposing controls');
179
+
180
+ // Remove from state
181
+ updateState('controls', null);
182
+
183
+ // Dispose and remove the controls
184
+ controls.dispose();
185
+ controlsInstance = null;
186
+ isInitialized = false;
187
+ }
188
+
189
+ /**
190
+ * Update control settings
191
+ * @param {Object} settings - Control settings to update
192
+ */
193
+ export function updateControlSettings(settings = {}) {
194
+ const controls = getControls();
195
+ if (!controls) {
196
+ console.warn('Cannot update control settings: not initialized');
197
+ return;
198
+ }
199
+
200
+ // Apply settings if provided
201
+ if (settings.enableDamping !== undefined) controls.enableDamping = settings.enableDamping;
202
+ if (settings.dampingFactor !== undefined) controls.dampingFactor = settings.dampingFactor;
203
+ if (settings.enableZoom !== undefined) controls.enableZoom = settings.enableZoom;
204
+ if (settings.enableRotate !== undefined) controls.enableRotate = settings.enableRotate;
205
+ if (settings.enablePan !== undefined) controls.enablePan = settings.enablePan;
206
+ if (settings.minDistance !== undefined) controls.minDistance = settings.minDistance;
207
+ if (settings.maxDistance !== undefined) controls.maxDistance = settings.maxDistance;
208
+ }
209
+
210
+ /**
211
+ * Update the controls in the animation loop
212
+ * Allows animation code to just call this instead of checking for controls
213
+ */
214
+ export function updateControls() {
215
+ const controls = getControls();
216
+ if (!controls) {
217
+ return;
218
+ }
219
+
220
+ // Always update controls if they exist, no need for extra checks
221
+ // This ensures smooth camera movement regardless of interaction state
222
+ controls.update();
223
+ }
224
+
225
+ /**
226
+ * Set the target for the controls
227
+ * @param {THREE.Vector3} position - The target position
228
+ */
229
+ export function setControlsTarget(position) {
230
+ const controls = getControls();
231
+ if (!controls) {
232
+ console.warn('Cannot set controls target: not initialized');
233
+ return;
234
+ }
235
+
236
+ if (!position) {
237
+ console.warn('Cannot set controls target: position is required');
238
+ return;
239
+ }
240
+
241
+ controls.target.copy(position);
242
+ controls.update();
243
+ }