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