@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,690 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lighting Utilities Module
|
|
3
|
+
*
|
|
4
|
+
* This module handles lighting setup and management for the Asset Debugger.
|
|
5
|
+
*/
|
|
6
|
+
import * as THREE from 'three';
|
|
7
|
+
import { getState, updateState } from '../state/scene-state';
|
|
8
|
+
|
|
9
|
+
// Default exposure value for HDR/EXR environment maps
|
|
10
|
+
let environmentExposure = 1.0;
|
|
11
|
+
|
|
12
|
+
// Debug flag to control logging - set to false to disable logs
|
|
13
|
+
const DEBUG_LIGHTING = false;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Add standard lighting to the scene
|
|
17
|
+
* @param {THREE.Scene} scene - The scene to add lighting to
|
|
18
|
+
*/
|
|
19
|
+
export function addLighting(scene) {
|
|
20
|
+
if (DEBUG_LIGHTING) {
|
|
21
|
+
console.log('Adding standard lighting to scene');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
|
|
25
|
+
scene.add(ambientLight);
|
|
26
|
+
|
|
27
|
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
|
|
28
|
+
directionalLight.position.set(5, 5, 5);
|
|
29
|
+
scene.add(directionalLight);
|
|
30
|
+
|
|
31
|
+
// Store lighting references in the state
|
|
32
|
+
updateState('ambientLight', ambientLight);
|
|
33
|
+
updateState('directionalLight', directionalLight);
|
|
34
|
+
|
|
35
|
+
// Expose exposure update function globally
|
|
36
|
+
window.updateExposure = updateExposure;
|
|
37
|
+
|
|
38
|
+
if (DEBUG_LIGHTING) {
|
|
39
|
+
console.log('Standard lighting setup complete');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Update lighting parameters
|
|
45
|
+
* @param {Object} params - Lighting parameters
|
|
46
|
+
* @param {Object} [params.ambient] - Ambient light parameters
|
|
47
|
+
* @param {string} [params.ambient.color] - Ambient light color in hex
|
|
48
|
+
* @param {number} [params.ambient.intensity] - Ambient light intensity
|
|
49
|
+
* @param {Object} [params.directional] - Directional light parameters
|
|
50
|
+
* @param {string} [params.directional.color] - Directional light color in hex
|
|
51
|
+
* @param {number} [params.directional.intensity] - Directional light intensity
|
|
52
|
+
* @param {Object} [params.directional.position] - Directional light position
|
|
53
|
+
*/
|
|
54
|
+
export function updateLighting(params = {}) {
|
|
55
|
+
const state = getState();
|
|
56
|
+
|
|
57
|
+
if (params.ambient && state.ambientLight) {
|
|
58
|
+
if (params.ambient.color !== undefined) {
|
|
59
|
+
state.ambientLight.color.set(params.ambient.color);
|
|
60
|
+
}
|
|
61
|
+
if (params.ambient.intensity !== undefined) {
|
|
62
|
+
state.ambientLight.intensity = params.ambient.intensity;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (params.directional && state.directionalLight) {
|
|
67
|
+
if (params.directional.color !== undefined) {
|
|
68
|
+
state.directionalLight.color.set(params.directional.color);
|
|
69
|
+
}
|
|
70
|
+
if (params.directional.intensity !== undefined) {
|
|
71
|
+
state.directionalLight.intensity = params.directional.intensity;
|
|
72
|
+
}
|
|
73
|
+
if (params.directional.position) {
|
|
74
|
+
const pos = params.directional.position;
|
|
75
|
+
if (pos.x !== undefined) state.directionalLight.position.x = pos.x;
|
|
76
|
+
if (pos.y !== undefined) state.directionalLight.position.y = pos.y;
|
|
77
|
+
if (pos.z !== undefined) state.directionalLight.position.z = pos.z;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Update the exposure of the environment map
|
|
84
|
+
* @param {number} value - Exposure value (0-2)
|
|
85
|
+
*/
|
|
86
|
+
export function updateExposure(value) {
|
|
87
|
+
const state = getState();
|
|
88
|
+
if (!state.renderer) return;
|
|
89
|
+
|
|
90
|
+
environmentExposure = value;
|
|
91
|
+
state.renderer.toneMappingExposure = value;
|
|
92
|
+
|
|
93
|
+
if (DEBUG_LIGHTING) {
|
|
94
|
+
console.log(`Environment exposure updated to ${value}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Set up environment lighting from an HDR or EXR file
|
|
100
|
+
* @param {File} file - HDR or EXR file object
|
|
101
|
+
* @returns {Promise<THREE.Texture>} - Promise resolving to the loaded environment texture
|
|
102
|
+
*/
|
|
103
|
+
export function setupEnvironmentLighting(file) {
|
|
104
|
+
const state = getState();
|
|
105
|
+
if (!state.scene || !state.renderer) {
|
|
106
|
+
console.error('Cannot setup environment lighting: scene or renderer not available');
|
|
107
|
+
return Promise.reject(new Error('Scene or renderer not available'));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Ensure viewport is visible before setting up lighting
|
|
111
|
+
const viewport = document.getElementById('viewport');
|
|
112
|
+
if (viewport) {
|
|
113
|
+
console.log('DEBUG: Ensuring viewport is visible before environment lighting setup', {
|
|
114
|
+
currentDisplay: viewport.style.display
|
|
115
|
+
});
|
|
116
|
+
viewport.style.display = 'block';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Configure renderer for HDR/EXR
|
|
120
|
+
state.renderer.outputEncoding = THREE.sRGBEncoding;
|
|
121
|
+
state.renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
122
|
+
state.renderer.toneMappingExposure = environmentExposure;
|
|
123
|
+
|
|
124
|
+
if (DEBUG_LIGHTING) {
|
|
125
|
+
console.log(`Setting up environment lighting from ${file.name} (${file.size} bytes)`);
|
|
126
|
+
console.log('Renderer config:', {
|
|
127
|
+
outputEncoding: state.renderer.outputEncoding,
|
|
128
|
+
toneMapping: state.renderer.toneMapping,
|
|
129
|
+
exposure: state.renderer.toneMappingExposure
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Return a promise that resolves with the loaded texture
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
// First parse and log all lighting data
|
|
136
|
+
parseLightingData(file).catch(error => {
|
|
137
|
+
console.error('Error parsing lighting data:', error);
|
|
138
|
+
// Continue even if metadata parsing fails
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Determine if this is an EXR file
|
|
142
|
+
const isEXR = file.name.toLowerCase().endsWith('.exr');
|
|
143
|
+
|
|
144
|
+
// Create object URL from the file
|
|
145
|
+
const url = URL.createObjectURL(file);
|
|
146
|
+
|
|
147
|
+
// Function to handle successful texture loading
|
|
148
|
+
const handleTextureLoaded = (texture) => {
|
|
149
|
+
if (DEBUG_LIGHTING) {
|
|
150
|
+
console.log(`${isEXR ? 'EXR' : 'HDR'} texture loaded successfully:`, texture);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
texture.mapping = THREE.EquirectangularReflectionMapping;
|
|
154
|
+
|
|
155
|
+
// Set scene environment but NOT background
|
|
156
|
+
state.scene.environment = texture;
|
|
157
|
+
|
|
158
|
+
// Store the texture in state for later reference if needed
|
|
159
|
+
updateState('environmentTexture', texture);
|
|
160
|
+
|
|
161
|
+
// Clean up object URL
|
|
162
|
+
URL.revokeObjectURL(url);
|
|
163
|
+
|
|
164
|
+
// Update exposure control if it exists
|
|
165
|
+
const exposureControl = document.getElementById('exposure-value');
|
|
166
|
+
if (exposureControl) {
|
|
167
|
+
exposureControl.value = environmentExposure;
|
|
168
|
+
const valueDisplay = exposureControl.previousElementSibling.querySelector('.value-display');
|
|
169
|
+
if (valueDisplay) {
|
|
170
|
+
valueDisplay.textContent = environmentExposure.toFixed(1);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Hide no data message since we now have lighting data
|
|
175
|
+
const noDataMessage = document.querySelector('.no-data-message');
|
|
176
|
+
if (noDataMessage) {
|
|
177
|
+
noDataMessage.style.display = 'none';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check viewport again to ensure it's still visible
|
|
181
|
+
const viewportAfter = document.getElementById('viewport');
|
|
182
|
+
if (viewportAfter && viewportAfter.style.display !== 'block') {
|
|
183
|
+
console.log(`DEBUG: Restoring viewport visibility after ${isEXR ? 'EXR' : 'HDR'} loading`);
|
|
184
|
+
viewportAfter.style.display = 'block';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Ensure controls are properly set up for the current scene
|
|
188
|
+
import('./camera-controller.js').then(controlsModule => {
|
|
189
|
+
console.log('Setting up camera and controls after environment lighting setup');
|
|
190
|
+
|
|
191
|
+
if (state.camera && state.renderer) {
|
|
192
|
+
// Reset camera position
|
|
193
|
+
state.camera.position.set(3, 2, 5);
|
|
194
|
+
state.camera.lookAt(0, 0, 0);
|
|
195
|
+
state.camera.updateProjectionMatrix();
|
|
196
|
+
|
|
197
|
+
// Instead of resetting existing controls, recreate them entirely
|
|
198
|
+
if (controlsModule.recreateControls) {
|
|
199
|
+
controlsModule.recreateControls(state.camera, state.renderer.domElement);
|
|
200
|
+
console.log('Controls recreated successfully - scene should now be interactive');
|
|
201
|
+
} else {
|
|
202
|
+
console.warn('recreateControls function not found, controls may not work properly');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Trigger a resize event to ensure everything is rendered correctly
|
|
206
|
+
window.dispatchEvent(new Event('resize'));
|
|
207
|
+
}
|
|
208
|
+
}).catch(error => {
|
|
209
|
+
console.error('Error fixing controls:', error);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Resolve the promise with the texture
|
|
213
|
+
resolve(texture);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
if (isEXR) {
|
|
217
|
+
// Use EXRLoader for EXR files
|
|
218
|
+
if (DEBUG_LIGHTING) {
|
|
219
|
+
console.log('Loading EXR file with EXRLoader');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
import('three/addons/loaders/EXRLoader.js').then(({ EXRLoader }) => {
|
|
223
|
+
if (DEBUG_LIGHTING) {
|
|
224
|
+
console.log('EXRLoader imported successfully');
|
|
225
|
+
console.log('Created URL for EXR file:', url);
|
|
226
|
+
console.log('Starting EXR texture loading...');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const loader = new EXRLoader();
|
|
230
|
+
loader.setDataType(THREE.FloatType);
|
|
231
|
+
|
|
232
|
+
loader.load(url,
|
|
233
|
+
// Success callback
|
|
234
|
+
handleTextureLoaded,
|
|
235
|
+
// Progress callback
|
|
236
|
+
undefined,
|
|
237
|
+
// Error callback
|
|
238
|
+
(error) => {
|
|
239
|
+
console.error('Error loading EXR texture:', error);
|
|
240
|
+
URL.revokeObjectURL(url);
|
|
241
|
+
reject(error);
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
}).catch(error => {
|
|
245
|
+
console.error('Error importing EXRLoader:', error);
|
|
246
|
+
URL.revokeObjectURL(url);
|
|
247
|
+
reject(error);
|
|
248
|
+
});
|
|
249
|
+
} else {
|
|
250
|
+
// Use RGBELoader for HDR files
|
|
251
|
+
if (DEBUG_LIGHTING) {
|
|
252
|
+
console.log('Loading HDR file with RGBELoader');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
import('three/addons/loaders/RGBELoader.js').then(({ RGBELoader }) => {
|
|
256
|
+
if (DEBUG_LIGHTING) {
|
|
257
|
+
console.log('RGBELoader imported successfully');
|
|
258
|
+
console.log('Created URL for HDR file:', url);
|
|
259
|
+
console.log('Starting HDR texture loading...');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const loader = new RGBELoader();
|
|
263
|
+
|
|
264
|
+
loader.load(url,
|
|
265
|
+
// Success callback
|
|
266
|
+
handleTextureLoaded,
|
|
267
|
+
// Progress callback
|
|
268
|
+
undefined,
|
|
269
|
+
// Error callback
|
|
270
|
+
(error) => {
|
|
271
|
+
console.error('Error loading HDR texture:', error);
|
|
272
|
+
URL.revokeObjectURL(url);
|
|
273
|
+
reject(error);
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
}).catch(error => {
|
|
277
|
+
console.error('Error importing RGBELoader:', error);
|
|
278
|
+
URL.revokeObjectURL(url);
|
|
279
|
+
reject(error);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Reset lighting to default values
|
|
287
|
+
*/
|
|
288
|
+
export function resetLighting() {
|
|
289
|
+
const state = getState();
|
|
290
|
+
|
|
291
|
+
if (state.ambientLight) {
|
|
292
|
+
state.ambientLight.color.set(0xffffff);
|
|
293
|
+
state.ambientLight.intensity = 0.5;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (state.directionalLight) {
|
|
297
|
+
state.directionalLight.color.set(0xffffff);
|
|
298
|
+
state.directionalLight.intensity = 1.0;
|
|
299
|
+
state.directionalLight.position.set(5, 5, 5);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Reset environment exposure
|
|
303
|
+
environmentExposure = 1.0;
|
|
304
|
+
if (state.renderer) {
|
|
305
|
+
state.renderer.toneMappingExposure = environmentExposure;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Update exposure control if it exists
|
|
309
|
+
const exposureControl = document.getElementById('exposure-value');
|
|
310
|
+
if (exposureControl) {
|
|
311
|
+
exposureControl.value = environmentExposure;
|
|
312
|
+
const valueDisplay = exposureControl.previousElementSibling.querySelector('.value-display');
|
|
313
|
+
if (valueDisplay) {
|
|
314
|
+
valueDisplay.textContent = environmentExposure.toFixed(1);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Only reset environment lighting if we don't have HDR/EXR data loaded
|
|
319
|
+
// Check if we have an environment texture loaded
|
|
320
|
+
const hasEnvironmentTexture = state.scene && state.scene.environment !== null;
|
|
321
|
+
|
|
322
|
+
if (!hasEnvironmentTexture && state.scene) {
|
|
323
|
+
// Only clear environment if no HDR/EXR is loaded
|
|
324
|
+
state.scene.environment = null;
|
|
325
|
+
state.scene.background = new THREE.Color(0x111111);
|
|
326
|
+
|
|
327
|
+
// Show no data message only if we don't have HDR/EXR data
|
|
328
|
+
const noDataMessage = document.querySelector('.no-data-message');
|
|
329
|
+
if (noDataMessage) {
|
|
330
|
+
noDataMessage.style.display = 'block';
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Parse lighting data from an HDR or EXR file without applying it to the scene
|
|
337
|
+
* @param {File} file - HDR or EXR file object
|
|
338
|
+
* @returns {Promise<Object>} - Promise resolving to lighting data
|
|
339
|
+
*/
|
|
340
|
+
export function parseLightingData(file) {
|
|
341
|
+
return new Promise((resolve, reject) => {
|
|
342
|
+
// Determine file type
|
|
343
|
+
const isEXR = file.name.toLowerCase().endsWith('.exr');
|
|
344
|
+
const isHDR = file.name.toLowerCase().endsWith('.hdr');
|
|
345
|
+
|
|
346
|
+
if (!isEXR && !isHDR) {
|
|
347
|
+
reject(new Error('Unsupported file type. Only HDR and EXR files are supported.'));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (DEBUG_LIGHTING) {
|
|
352
|
+
console.log(`Parsing ${isEXR ? 'EXR' : 'HDR'} lighting data from ${file.name}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Load appropriate loaders based on file type
|
|
356
|
+
const loaderPromise = isEXR ?
|
|
357
|
+
Promise.all([
|
|
358
|
+
import('three/addons/loaders/EXRLoader.js'),
|
|
359
|
+
import('three/addons/loaders/RGBELoader.js')
|
|
360
|
+
]) :
|
|
361
|
+
import('three/addons/loaders/RGBELoader.js');
|
|
362
|
+
|
|
363
|
+
loaderPromise.then((modules) => {
|
|
364
|
+
const url = URL.createObjectURL(file);
|
|
365
|
+
|
|
366
|
+
if (isEXR) {
|
|
367
|
+
const { EXRLoader } = modules[0];
|
|
368
|
+
const loader = new EXRLoader();
|
|
369
|
+
loader.setDataType(THREE.FloatType);
|
|
370
|
+
|
|
371
|
+
loader.load(url, (texture) => {
|
|
372
|
+
// Basic texture metadata
|
|
373
|
+
const basicMetadata = {
|
|
374
|
+
type: 'EXR',
|
|
375
|
+
fileName: file.name,
|
|
376
|
+
fileSizeBytes: file.size,
|
|
377
|
+
dimensions: {
|
|
378
|
+
width: texture.image.width || null,
|
|
379
|
+
height: texture.image.height || null
|
|
380
|
+
},
|
|
381
|
+
aspectRatio: texture.image.width && texture.image.height ?
|
|
382
|
+
(texture.image.width / texture.image.height).toFixed(2) : null,
|
|
383
|
+
format: texture.format || null,
|
|
384
|
+
dataType: texture.type || null,
|
|
385
|
+
internalFormat: texture.internalFormat || null
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// Extract advanced metadata where possible
|
|
389
|
+
const advancedMetadata = extractEXRMetadata(texture);
|
|
390
|
+
|
|
391
|
+
// Combine metadata
|
|
392
|
+
const completeMetadata = {
|
|
393
|
+
...basicMetadata,
|
|
394
|
+
...advancedMetadata,
|
|
395
|
+
// Physical properties
|
|
396
|
+
maxLuminance: estimateMaxLuminance(texture),
|
|
397
|
+
averageLuminance: estimateAverageLuminance(texture),
|
|
398
|
+
dynamicRange: estimateDynamicRange(texture),
|
|
399
|
+
// Technical details
|
|
400
|
+
compression: texture.compressionType || null,
|
|
401
|
+
colorSpace: texture.colorSpace || null,
|
|
402
|
+
encoding: texture.encoding || null,
|
|
403
|
+
isHDR: true
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
if (DEBUG_LIGHTING) {
|
|
407
|
+
console.log('EXR Detailed Metadata:', completeMetadata);
|
|
408
|
+
}
|
|
409
|
+
URL.revokeObjectURL(url);
|
|
410
|
+
resolve(completeMetadata);
|
|
411
|
+
}, undefined, reject);
|
|
412
|
+
} else {
|
|
413
|
+
// HDR file
|
|
414
|
+
const RGBELoader = isEXR ? modules[1].RGBELoader : modules.RGBELoader;
|
|
415
|
+
const loader = new RGBELoader();
|
|
416
|
+
|
|
417
|
+
loader.load(url, (texture) => {
|
|
418
|
+
// Basic texture metadata
|
|
419
|
+
const basicMetadata = {
|
|
420
|
+
type: 'HDR',
|
|
421
|
+
fileName: file.name,
|
|
422
|
+
fileSizeBytes: file.size,
|
|
423
|
+
dimensions: {
|
|
424
|
+
width: texture.image.width || null,
|
|
425
|
+
height: texture.image.height || null
|
|
426
|
+
},
|
|
427
|
+
aspectRatio: texture.image.width && texture.image.height ?
|
|
428
|
+
(texture.image.width / texture.image.height).toFixed(2) : null,
|
|
429
|
+
format: texture.format || null,
|
|
430
|
+
mappingType: texture.mapping || null
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// Extract advanced metadata where possible
|
|
434
|
+
const advancedMetadata = extractHDRMetadata(texture);
|
|
435
|
+
|
|
436
|
+
// Combine metadata
|
|
437
|
+
const completeMetadata = {
|
|
438
|
+
...basicMetadata,
|
|
439
|
+
...advancedMetadata,
|
|
440
|
+
// Physical properties
|
|
441
|
+
maxLuminance: estimateMaxLuminance(texture),
|
|
442
|
+
averageLuminance: estimateAverageLuminance(texture),
|
|
443
|
+
dynamicRange: estimateDynamicRange(texture),
|
|
444
|
+
// Technical details
|
|
445
|
+
colorSpace: texture.colorSpace || null,
|
|
446
|
+
encoding: texture.encoding || null,
|
|
447
|
+
isHDR: true
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
if (DEBUG_LIGHTING) {
|
|
451
|
+
console.log('HDR Detailed Metadata:', completeMetadata);
|
|
452
|
+
}
|
|
453
|
+
URL.revokeObjectURL(url);
|
|
454
|
+
resolve(completeMetadata);
|
|
455
|
+
}, undefined, reject);
|
|
456
|
+
}
|
|
457
|
+
}).catch(error => {
|
|
458
|
+
console.error('Error loading lighting data parser:', error);
|
|
459
|
+
reject(error);
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Extract EXR-specific metadata from texture
|
|
466
|
+
* @private
|
|
467
|
+
* @param {THREE.Texture} texture - The EXR texture
|
|
468
|
+
* @returns {Object} EXR metadata
|
|
469
|
+
*/
|
|
470
|
+
function extractEXRMetadata(texture) {
|
|
471
|
+
const metadata = {
|
|
472
|
+
// Standard EXR metadata fields
|
|
473
|
+
version: null,
|
|
474
|
+
channels: null,
|
|
475
|
+
compression: null,
|
|
476
|
+
pixelAspectRatio: null,
|
|
477
|
+
displayWindow: null,
|
|
478
|
+
dataWindow: null,
|
|
479
|
+
lineOrder: null,
|
|
480
|
+
chromaticities: null,
|
|
481
|
+
owner: null,
|
|
482
|
+
comments: null,
|
|
483
|
+
creationTimestamp: null,
|
|
484
|
+
creationSoftware: null,
|
|
485
|
+
exposureValue: null,
|
|
486
|
+
gamma: null,
|
|
487
|
+
whitePoint: null
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
try {
|
|
491
|
+
// Attempt to extract header data if available
|
|
492
|
+
if (texture.exrData) {
|
|
493
|
+
const header = texture.exrData.header || {};
|
|
494
|
+
|
|
495
|
+
metadata.version = header.version || null;
|
|
496
|
+
metadata.channels = header.channels ? Object.keys(header.channels) : null;
|
|
497
|
+
metadata.compression = header.compression || null;
|
|
498
|
+
metadata.pixelAspectRatio = header.pixelAspectRatio || null;
|
|
499
|
+
metadata.displayWindow = header.displayWindow || null;
|
|
500
|
+
metadata.dataWindow = header.dataWindow || null;
|
|
501
|
+
metadata.lineOrder = header.lineOrder || null;
|
|
502
|
+
metadata.chromaticities = header.chromaticities || null;
|
|
503
|
+
|
|
504
|
+
// Extract any custom attributes
|
|
505
|
+
if (header.attributes) {
|
|
506
|
+
metadata.owner = header.attributes.owner || null;
|
|
507
|
+
metadata.comments = header.attributes.comments || null;
|
|
508
|
+
metadata.creationTimestamp = header.attributes.creationTime || null;
|
|
509
|
+
metadata.creationSoftware = header.attributes.software || null;
|
|
510
|
+
metadata.exposureValue = header.attributes.exposure || null;
|
|
511
|
+
metadata.gamma = header.attributes.gamma || null;
|
|
512
|
+
metadata.whitePoint = header.attributes.whitePoint || null;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
} catch (e) {
|
|
516
|
+
console.warn('Could not extract EXR header data:', e);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return metadata;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Extract HDR-specific metadata from texture
|
|
524
|
+
* @private
|
|
525
|
+
* @param {THREE.Texture} texture - The HDR texture
|
|
526
|
+
* @returns {Object} HDR metadata
|
|
527
|
+
*/
|
|
528
|
+
function extractHDRMetadata(texture) {
|
|
529
|
+
const metadata = {
|
|
530
|
+
// Standard HDR metadata fields
|
|
531
|
+
formatIdentifier: null,
|
|
532
|
+
exposureValue: null,
|
|
533
|
+
gamma: null,
|
|
534
|
+
pixelAspectRatio: null,
|
|
535
|
+
creationSoftware: null,
|
|
536
|
+
comments: null,
|
|
537
|
+
colorSpace: null,
|
|
538
|
+
whitePoint: null,
|
|
539
|
+
sceneBrightness: null,
|
|
540
|
+
creationTimestamp: null
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
try {
|
|
544
|
+
// Attempt to extract header data if available
|
|
545
|
+
if (texture.hdrData) {
|
|
546
|
+
const header = texture.hdrData || {};
|
|
547
|
+
|
|
548
|
+
metadata.formatIdentifier = header.format || null;
|
|
549
|
+
metadata.exposureValue = header.exposure || null;
|
|
550
|
+
metadata.gamma = header.gamma || null;
|
|
551
|
+
metadata.pixelAspectRatio = header.pixelAspectRatio || null;
|
|
552
|
+
metadata.creationSoftware = header.software || null;
|
|
553
|
+
metadata.comments = header.comments || null;
|
|
554
|
+
metadata.colorSpace = header.colorSpace || null;
|
|
555
|
+
metadata.whitePoint = header.whitePoint || null;
|
|
556
|
+
metadata.sceneBrightness = header.sceneBrightness || null;
|
|
557
|
+
metadata.creationTimestamp = header.creationTime || null;
|
|
558
|
+
}
|
|
559
|
+
} catch (e) {
|
|
560
|
+
console.warn('Could not extract HDR header data:', e);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
return metadata;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Estimate the average luminance from a texture
|
|
568
|
+
* @private
|
|
569
|
+
* @param {THREE.Texture} texture - The texture to analyze
|
|
570
|
+
* @returns {number|null} - Estimated average luminance
|
|
571
|
+
*/
|
|
572
|
+
function estimateAverageLuminance(texture) {
|
|
573
|
+
if (!texture || !texture.image) return null;
|
|
574
|
+
|
|
575
|
+
try {
|
|
576
|
+
// Check if we can access image data
|
|
577
|
+
if (texture.image.data) {
|
|
578
|
+
const data = texture.image.data;
|
|
579
|
+
let totalLuminance = 0;
|
|
580
|
+
let samples = 0;
|
|
581
|
+
|
|
582
|
+
// Sample a subset of pixels for performance
|
|
583
|
+
const step = Math.max(1, Math.floor(data.length / 5000));
|
|
584
|
+
for (let i = 0; i < data.length; i += step * 4) {
|
|
585
|
+
if (i + 2 >= data.length) break;
|
|
586
|
+
|
|
587
|
+
// Calculate luminance from RGB
|
|
588
|
+
const r = data[i];
|
|
589
|
+
const g = data[i + 1];
|
|
590
|
+
const b = data[i + 2];
|
|
591
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
592
|
+
|
|
593
|
+
totalLuminance += luminance;
|
|
594
|
+
samples++;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return samples > 0 ? totalLuminance / samples : null;
|
|
598
|
+
}
|
|
599
|
+
} catch (e) {
|
|
600
|
+
console.warn('Could not calculate average luminance:', e);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Estimate the dynamic range from a texture
|
|
608
|
+
* @private
|
|
609
|
+
* @param {THREE.Texture} texture - The texture to analyze
|
|
610
|
+
* @returns {number|null} - Estimated dynamic range in stops
|
|
611
|
+
*/
|
|
612
|
+
function estimateDynamicRange(texture) {
|
|
613
|
+
if (!texture || !texture.image) return null;
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
// Check if we can access image data
|
|
617
|
+
if (texture.image.data) {
|
|
618
|
+
const data = texture.image.data;
|
|
619
|
+
let minLuminance = Infinity;
|
|
620
|
+
let maxLuminance = 0;
|
|
621
|
+
|
|
622
|
+
// Sample a subset of pixels for performance
|
|
623
|
+
const step = Math.max(1, Math.floor(data.length / 5000));
|
|
624
|
+
for (let i = 0; i < data.length; i += step * 4) {
|
|
625
|
+
if (i + 2 >= data.length) break;
|
|
626
|
+
|
|
627
|
+
// Calculate luminance from RGB
|
|
628
|
+
const r = data[i];
|
|
629
|
+
const g = data[i + 1];
|
|
630
|
+
const b = data[i + 2];
|
|
631
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
632
|
+
|
|
633
|
+
if (luminance > 0) { // Avoid log(0)
|
|
634
|
+
minLuminance = Math.min(minLuminance, luminance);
|
|
635
|
+
maxLuminance = Math.max(maxLuminance, luminance);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
if (minLuminance < maxLuminance && minLuminance > 0) {
|
|
640
|
+
// Calculate dynamic range in stops (powers of 2)
|
|
641
|
+
return Math.log2(maxLuminance / minLuminance);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
} catch (e) {
|
|
645
|
+
console.warn('Could not calculate dynamic range:', e);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Estimate the maximum luminance from a texture
|
|
653
|
+
* @private
|
|
654
|
+
* @param {THREE.Texture} texture - The texture to analyze
|
|
655
|
+
* @returns {number} - Estimated maximum luminance
|
|
656
|
+
*/
|
|
657
|
+
function estimateMaxLuminance(texture) {
|
|
658
|
+
if (!texture || !texture.image) return 0;
|
|
659
|
+
|
|
660
|
+
// For a more accurate implementation, we would need to analyze the texture data
|
|
661
|
+
// This is a simplified estimation based on texture properties
|
|
662
|
+
try {
|
|
663
|
+
// Check if we can access image data
|
|
664
|
+
if (texture.image.data) {
|
|
665
|
+
const data = texture.image.data;
|
|
666
|
+
let maxLuminance = 0;
|
|
667
|
+
|
|
668
|
+
// Sample a subset of pixels for performance
|
|
669
|
+
const step = Math.max(1, Math.floor(data.length / 1000));
|
|
670
|
+
for (let i = 0; i < data.length; i += step * 4) {
|
|
671
|
+
if (i + 2 >= data.length) break;
|
|
672
|
+
|
|
673
|
+
// Calculate rough luminance from RGB
|
|
674
|
+
const r = data[i];
|
|
675
|
+
const g = data[i + 1];
|
|
676
|
+
const b = data[i + 2];
|
|
677
|
+
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
678
|
+
|
|
679
|
+
maxLuminance = Math.max(maxLuminance, luminance);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return maxLuminance;
|
|
683
|
+
}
|
|
684
|
+
} catch (e) {
|
|
685
|
+
console.warn('Could not analyze texture data:', e);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Fallback to a nominal value
|
|
689
|
+
return 1.0;
|
|
690
|
+
}
|