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