@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,822 @@
1
+ import * as THREE from 'three';
2
+ import { getState } from '../util/state/scene-state';
3
+
4
+ // Add variables to track axis indicator state
5
+ let axisIndicatorCollapsed = false;
6
+ let axisIndicatorPosition = { x: null, y: null }; // null means use default position
7
+
8
+ /**
9
+ * Create a coordinate axis indicator that blends into the scene
10
+ * @param {Object} scene - The Three.js scene
11
+ * @param {Object} camera - The Three.js camera
12
+ * @param {Object} renderer - The Three.js renderer
13
+ */
14
+ export function createAxisIndicator(scene, camera, renderer) {
15
+ console.log('Creating modern axis indicator');
16
+
17
+ // Create a new scene for the axis indicator
18
+ const axisScene = new THREE.Scene();
19
+ // Make background transparent to blend with main scene
20
+ axisScene.background = null;
21
+
22
+ // Create a camera for the axis indicator with wider field of view
23
+ const axisCamera = new THREE.PerspectiveCamera(60, 1, 0.1, 20);
24
+ axisCamera.position.set(0, 0, 5); // Position even further back to ensure all axes visible
25
+ axisCamera.lookAt(0, 0, 0);
26
+
27
+ // Create modern axes
28
+ const createAxis = (dir, color) => {
29
+ const group = new THREE.Group();
30
+
31
+ // Create line for positive axis direction
32
+ const lineGeometry = new THREE.BufferGeometry();
33
+ // Make line slightly shorter to leave space for arrow
34
+ const endPoint = new THREE.Vector3(dir.x, dir.y, dir.z).multiplyScalar(0.85);
35
+ lineGeometry.setAttribute('position',
36
+ new THREE.Float32BufferAttribute([0, 0, 0, endPoint.x, endPoint.y, endPoint.z], 3));
37
+
38
+ const lineMaterial = new THREE.LineBasicMaterial({
39
+ color: color,
40
+ linewidth: 8, // Increased from 5 to 8
41
+ depthTest: false,
42
+ transparent: true,
43
+ opacity: 1.0
44
+ });
45
+
46
+ const line = new THREE.Line(lineGeometry, lineMaterial);
47
+ group.add(line);
48
+
49
+ // Create negative axis direction (thicker, more visible dotted line)
50
+ const negLineGeometry = new THREE.BufferGeometry();
51
+ const negDir = new THREE.Vector3(-dir.x, -dir.y, -dir.z).multiplyScalar(0.85); // Increased from 0.7
52
+ negLineGeometry.setAttribute('position',
53
+ new THREE.Float32BufferAttribute([0, 0, 0, negDir.x, negDir.y, negDir.z], 3));
54
+
55
+ const dashedLineMaterial = new THREE.LineDashedMaterial({
56
+ color: color,
57
+ linewidth: 10, // Increased from 8
58
+ scale: 1,
59
+ dashSize: 0.18, // Increased from 0.15
60
+ gapSize: 0.07,
61
+ depthTest: false,
62
+ transparent: true,
63
+ opacity: 0.9 // Increased from 0.8
64
+ });
65
+
66
+ const dashedLine = new THREE.Line(negLineGeometry, dashedLineMaterial);
67
+ dashedLine.computeLineDistances(); // Required for dashed lines
68
+ group.add(dashedLine);
69
+
70
+ // Create modern arrow head (smaller)
71
+ const arrowGeometry = new THREE.CylinderGeometry(0, 0.1, 0.25, 8, 1); // Reduced from 0.15, 0.35
72
+ const arrowMaterial = new THREE.MeshBasicMaterial({
73
+ color: color,
74
+ transparent: true,
75
+ opacity: 1.0,
76
+ depthTest: false
77
+ });
78
+
79
+ const arrow = new THREE.Mesh(arrowGeometry, arrowMaterial);
80
+
81
+ // Position at the end of the line
82
+ arrow.position.copy(dir);
83
+
84
+ // Rotate arrow to point in the right direction
85
+ if (dir.x === 1) {
86
+ arrow.rotation.z = -Math.PI / 2;
87
+ } else if (dir.y === 1) {
88
+ // Default orientation works for Y
89
+ } else if (dir.z === 1) {
90
+ arrow.rotation.x = Math.PI / 2;
91
+ }
92
+
93
+ group.add(arrow);
94
+
95
+ // Create text label
96
+ const text = dir.x === 1 ? 'X' : dir.y === 1 ? 'Y' : 'Z';
97
+ const canvas = document.createElement('canvas');
98
+ canvas.width = 192; // Increased from 128 to 192
99
+ canvas.height = 192; // Increased from 128 to 192
100
+ const ctx = canvas.getContext('2d');
101
+
102
+ // Clear canvas
103
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
104
+
105
+ // Draw text with a subtle glow effect
106
+ ctx.font = 'bold 90px Arial'; // Increased from 68px to 90px
107
+ ctx.textAlign = 'center';
108
+ ctx.textBaseline = 'middle';
109
+
110
+ // Add a subtle glow effect
111
+ ctx.shadowColor = color;
112
+ ctx.shadowBlur = 10; // Increased from 8 to 10
113
+ ctx.fillStyle = color;
114
+ ctx.fillText(text, canvas.width/2, canvas.height/2);
115
+
116
+ const texture = new THREE.CanvasTexture(canvas);
117
+ const spriteMaterial = new THREE.SpriteMaterial({
118
+ map: texture,
119
+ transparent: true,
120
+ depthTest: false
121
+ });
122
+
123
+ const sprite = new THREE.Sprite(spriteMaterial);
124
+ // Position text beyond the arrow
125
+ sprite.position.copy(dir).multiplyScalar(1.5); // Increased from 1.4 to 1.5
126
+ sprite.scale.set(0.6, 0.6, 0.6); // Increased from 0.45 to 0.6
127
+
128
+ group.add(sprite);
129
+
130
+ return group;
131
+ };
132
+
133
+ // Create the three axes with modern colors
134
+ const xAxis = createAxis(new THREE.Vector3(1, 0, 0), '#ff4136'); // Vibrant red
135
+ const yAxis = createAxis(new THREE.Vector3(0, 1, 0), '#2ecc40'); // Vibrant green
136
+ const zAxis = createAxis(new THREE.Vector3(0, 0, 1), '#0074d9'); // Vibrant blue
137
+
138
+ axisScene.add(xAxis);
139
+ axisScene.add(yAxis);
140
+ axisScene.add(zAxis);
141
+
142
+ // Add a subtle center dot
143
+ const centerGeometry = new THREE.SphereGeometry(0.06, 16, 16); // Increased from 0.04, 12, 12
144
+ const centerMaterial = new THREE.MeshBasicMaterial({
145
+ color: 0xffffff,
146
+ transparent: true,
147
+ opacity: 0.9, // Increased from 0.8
148
+ depthTest: false
149
+ });
150
+ const centerSphere = new THREE.Mesh(centerGeometry, centerMaterial);
151
+ axisScene.add(centerSphere);
152
+
153
+ // Store references for cleanup later if needed
154
+ const state = getState();
155
+ state.axisScene = axisScene;
156
+ state.axisCamera = axisCamera;
157
+
158
+ // Find the correct viewport container (using the viewport ID)
159
+ let viewportContainer = document.getElementById('viewport');
160
+
161
+ // Fallback to direct parent if viewport not found
162
+ if (!viewportContainer) {
163
+ console.log('Viewport element not found, using renderer parent');
164
+ viewportContainer = renderer.domElement.closest('#viewport') ||
165
+ renderer.domElement.closest('#view-container') ||
166
+ renderer.domElement.closest('.view-panel') ||
167
+ renderer.domElement.parentElement;
168
+ }
169
+
170
+ console.log('Found viewport container:', viewportContainer);
171
+
172
+ // Size for the axis indicator (proportional to viewport size)
173
+ const size = Math.min(180, viewportContainer.offsetWidth / 4);
174
+
175
+ // Create container for the entire axis indicator (header + display)
176
+ const axisContainer = document.createElement('div');
177
+ axisContainer.id = 'axis-indicator-container';
178
+ axisContainer.style.position = 'absolute';
179
+ axisContainer.style.width = `${size}px`;
180
+ axisContainer.style.zIndex = '1000';
181
+ axisContainer.style.pointerEvents = 'auto';
182
+ axisContainer.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)';
183
+ axisContainer.style.border = '1px solid rgba(50, 50, 50, 0.7)';
184
+ axisContainer.style.borderRadius = '5px';
185
+ axisContainer.style.overflow = 'hidden';
186
+
187
+ // Initial position (top-right corner of the viewport)
188
+ const margin = 10;
189
+ if (axisIndicatorPosition.x === null || axisIndicatorPosition.y === null) {
190
+ axisIndicatorPosition.x = viewportContainer.offsetWidth - size - margin;
191
+ axisIndicatorPosition.y = margin;
192
+ }
193
+
194
+ axisContainer.style.left = `${axisIndicatorPosition.x}px`;
195
+ axisContainer.style.top = `${axisIndicatorPosition.y}px`;
196
+
197
+ // Create the header
198
+ const header = document.createElement('div');
199
+ header.id = 'axis-indicator-header';
200
+ header.style.backgroundColor = 'rgba(30, 30, 30, 0.7)';
201
+ header.style.color = 'white';
202
+ header.style.padding = '5px 10px';
203
+ header.style.cursor = 'grab';
204
+ header.style.userSelect = 'none';
205
+ header.style.display = 'flex';
206
+ header.style.alignItems = 'center';
207
+ header.style.justifyContent = 'space-between';
208
+ header.style.width = '100%'; // Full width
209
+ header.style.boxSizing = 'border-box'; // Include padding in width calculation
210
+
211
+ // Add title
212
+ const title = document.createElement('span');
213
+ title.textContent = 'Axis Indicator';
214
+ title.style.fontWeight = 'bold';
215
+ title.style.fontSize = '12px';
216
+
217
+ // Add collapse/expand button
218
+ const collapseBtn = document.createElement('span');
219
+ collapseBtn.textContent = axisIndicatorCollapsed ? '▼' : '▲';
220
+ collapseBtn.style.fontSize = '12px';
221
+ collapseBtn.style.cursor = 'pointer';
222
+ collapseBtn.style.marginLeft = '10px';
223
+ collapseBtn.style.width = '15px';
224
+ collapseBtn.style.textAlign = 'center';
225
+
226
+ // Add collapse functionality
227
+ collapseBtn.addEventListener('click', (e) => {
228
+ e.stopPropagation(); // Prevent triggering drag
229
+ axisIndicatorCollapsed = !axisIndicatorCollapsed;
230
+ collapseBtn.textContent = axisIndicatorCollapsed ? '▼' : '▲';
231
+
232
+ // Toggle display area visibility directly
233
+ const canvasContainer = document.getElementById('axis-indicator-canvas-container');
234
+ if (canvasContainer) {
235
+ canvasContainer.style.display = axisIndicatorCollapsed ? 'none' : 'block';
236
+ // Update container height when collapsed/expanded
237
+ updateContainerHeight();
238
+ }
239
+ });
240
+
241
+ // Add elements to header
242
+ header.appendChild(title);
243
+ header.appendChild(collapseBtn);
244
+
245
+ // Create canvas container for the indicator display
246
+ const canvasContainer = document.createElement('div');
247
+ canvasContainer.id = 'axis-indicator-canvas-container';
248
+ canvasContainer.style.width = `${size}px`;
249
+ canvasContainer.style.height = `${size}px`;
250
+ canvasContainer.style.backgroundColor = 'rgba(0, 0, 0, 0)';
251
+ canvasContainer.style.display = axisIndicatorCollapsed ? 'none' : 'block';
252
+
253
+ // Add both elements to the container
254
+ axisContainer.appendChild(header);
255
+ axisContainer.appendChild(canvasContainer);
256
+
257
+ // Add the container to the viewport
258
+ viewportContainer.appendChild(axisContainer);
259
+
260
+ // Function to update container height based on collapsed state
261
+ function updateContainerHeight() {
262
+ if (axisIndicatorCollapsed) {
263
+ axisContainer.style.height = `${header.offsetHeight}px`;
264
+ } else {
265
+ axisContainer.style.height = 'auto';
266
+ }
267
+ }
268
+
269
+ // Call once to set initial height
270
+ updateContainerHeight();
271
+
272
+ // Store scale factor for axis objects
273
+ let axisScale = 1.0;
274
+ const scaleMin = 0.5;
275
+ const scaleMax = 3.0;
276
+
277
+ // Add zoom functionality when hovering over the indicator
278
+ canvasContainer.addEventListener('wheel', (e) => {
279
+ e.preventDefault();
280
+
281
+ // Determine zoom direction
282
+ const delta = Math.sign(-e.deltaY);
283
+
284
+ // Adjust scale factor
285
+ axisScale += delta * 0.15;
286
+ axisScale = Math.max(scaleMin, Math.min(scaleMax, axisScale));
287
+
288
+ // Apply scale to all axis objects
289
+ xAxis.scale.set(axisScale, axisScale, axisScale);
290
+ yAxis.scale.set(axisScale, axisScale, axisScale);
291
+ zAxis.scale.set(axisScale, axisScale, axisScale);
292
+ centerSphere.scale.set(axisScale, axisScale, axisScale);
293
+
294
+ console.log(`Axis scale: ${axisScale.toFixed(2)}`);
295
+ });
296
+
297
+ // Make the header draggable (moves the entire container)
298
+ let isHeaderDragging = false;
299
+ let startX, startY;
300
+ let startLeft, startTop;
301
+
302
+ header.addEventListener('mousedown', (e) => {
303
+ isHeaderDragging = true;
304
+ startX = e.clientX;
305
+ startY = e.clientY;
306
+ startLeft = parseInt(axisContainer.style.left);
307
+ startTop = parseInt(axisContainer.style.top);
308
+ header.style.cursor = 'grabbing';
309
+
310
+ e.preventDefault();
311
+ });
312
+
313
+ document.addEventListener('mousemove', (e) => {
314
+ if (!isHeaderDragging) return;
315
+
316
+ const dx = e.clientX - startX;
317
+ const dy = e.clientY - startY;
318
+
319
+ const newLeft = startLeft + dx;
320
+ const newTop = startTop + dy;
321
+
322
+ // Get current viewport container dimensions
323
+ const containerRect = viewportContainer.getBoundingClientRect();
324
+ const maxLeft = containerRect.width - axisContainer.offsetWidth;
325
+ const maxTop = containerRect.height - axisContainer.offsetHeight;
326
+
327
+ const constrainedLeft = Math.max(0, Math.min(newLeft, maxLeft));
328
+ const constrainedTop = Math.max(0, Math.min(newTop, maxTop));
329
+
330
+ axisContainer.style.left = `${constrainedLeft}px`;
331
+ axisContainer.style.top = `${constrainedTop}px`;
332
+
333
+ // Update stored position
334
+ axisIndicatorPosition.x = constrainedLeft;
335
+ axisIndicatorPosition.y = constrainedTop;
336
+ });
337
+
338
+ document.addEventListener('mouseup', () => {
339
+ if (isHeaderDragging) {
340
+ isHeaderDragging = false;
341
+ header.style.cursor = 'grab';
342
+ }
343
+ });
344
+
345
+ // Create a separate renderer for the axis scene
346
+ const axisRenderer = new THREE.WebGLRenderer({
347
+ alpha: true,
348
+ antialias: true
349
+ });
350
+ axisRenderer.setSize(size, size);
351
+ axisRenderer.setClearColor(0x000000, 0);
352
+
353
+ // Add the renderer to the container
354
+ canvasContainer.appendChild(axisRenderer.domElement);
355
+
356
+ // Store renderer reference
357
+ axisScene.renderer = axisRenderer;
358
+
359
+ // Add a render callback to draw the axis indicator
360
+ const originalRender = renderer.render;
361
+ renderer.render = function(scene, camera) {
362
+ // Call original render with main scene and camera
363
+ originalRender.call(this, scene, camera);
364
+
365
+ // Skip rendering if collapsed or container was removed
366
+ const canvasContainer = document.getElementById('axis-indicator-canvas-container');
367
+ if (axisIndicatorCollapsed || !canvasContainer) {
368
+ return;
369
+ }
370
+
371
+ // Update rotation to match main camera
372
+ if (state.camera) {
373
+ const cameraDir = new THREE.Vector3(0, 0, -1).applyQuaternion(state.camera.quaternion);
374
+ const distance = axisCamera.position.length();
375
+ axisCamera.position.copy(cameraDir).negate().multiplyScalar(distance);
376
+ axisCamera.lookAt(0, 0, 0);
377
+ }
378
+
379
+ // Apply semi-transparency when overlaying content
380
+ const applyTransparency = (obj, factor) => {
381
+ if (obj.material) {
382
+ obj.material.opacity = obj.material.opacity * factor;
383
+ }
384
+ if (obj.children) {
385
+ obj.children.forEach(child => applyTransparency(child, factor));
386
+ }
387
+ };
388
+
389
+ // Apply transparency to all objects in axis scene
390
+ axisScene.children.forEach(obj => applyTransparency(obj, 0.7));
391
+
392
+ // Render axis scene with its own renderer
393
+ axisRenderer.render(axisScene, axisCamera);
394
+
395
+ // Reset transparency after rendering
396
+ axisScene.children.forEach(obj => {
397
+ const resetOpacity = (o) => {
398
+ if (o.material && o.material.opacity) {
399
+ o.material.opacity = o.material.opacity / 0.7;
400
+ }
401
+ if (o.children) {
402
+ o.children.forEach(child => resetOpacity(child));
403
+ }
404
+ };
405
+ resetOpacity(obj);
406
+ });
407
+ };
408
+
409
+ console.log('Modern axis indicator created with draggable header');
410
+
411
+ // Reset transparency after rendering
412
+ axisScene.children.forEach(obj => {
413
+ const resetOpacity = (o) => {
414
+ if (o.material && o.material.opacity) {
415
+ o.material.opacity = o.material.opacity / 0.7;
416
+ }
417
+ if (o.children) {
418
+ o.children.forEach(child => resetOpacity(child));
419
+ }
420
+ };
421
+ resetOpacity(obj);
422
+ });
423
+
424
+ // Create axis indicator mode event listener
425
+ document.addEventListener('axisIndicatorModeChange', function(e) {
426
+ const mode = e.detail.mode;
427
+ const intensity = e.detail.intensity || 0.7;
428
+ console.log('Axis indicator mode changed to:', mode);
429
+
430
+ // Update the state's embedded intensity if applicable
431
+ const state = getState();
432
+ if (state.embeddedAxisIndicator) {
433
+ state.embeddedAxisIndicator.intensity = intensity;
434
+ }
435
+
436
+ // Toggle between windowed, embedded, and disabled modes
437
+ if (mode === 'embedded') {
438
+ // Hide windowed version if it exists
439
+ const axisContainer = document.getElementById('axis-indicator-container');
440
+ if (axisContainer) {
441
+ axisContainer.style.display = 'none';
442
+ }
443
+
444
+ // Create embedded version
445
+ createEmbeddedAxisIndicator(scene, camera, renderer);
446
+ } else if (mode === 'disabled') {
447
+ // Hide windowed version if it exists
448
+ const axisContainer = document.getElementById('axis-indicator-container');
449
+ if (axisContainer) {
450
+ axisContainer.style.display = 'none';
451
+ }
452
+
453
+ // Remove embedded version if it exists
454
+ removeEmbeddedAxisIndicator();
455
+ } else {
456
+ // Show windowed version if it exists
457
+ const axisContainer = document.getElementById('axis-indicator-container');
458
+ if (axisContainer) {
459
+ axisContainer.style.display = 'block';
460
+ }
461
+
462
+ // Remove embedded version if it exists
463
+ removeEmbeddedAxisIndicator();
464
+ }
465
+ });
466
+
467
+ // Function to create embedded axis indicator
468
+ function createEmbeddedAxisIndicator(scene, camera, renderer) {
469
+ // Check if we already have an embedded axis indicator
470
+ const state = getState();
471
+ if (state.embeddedAxisIndicator) {
472
+ // If it exists, just set active to true
473
+ state.embeddedAxisIndicator.active = true;
474
+ return;
475
+ }
476
+
477
+ console.log('Creating embedded axis indicator');
478
+
479
+ // Create a new scene for the embedded axis indicator
480
+ const embeddedAxisScene = new THREE.Scene();
481
+ embeddedAxisScene.background = null; // Transparent background
482
+
483
+ // Create a camera for the embedded axis indicator with wide FOV
484
+ const embeddedAxisCamera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
485
+ embeddedAxisCamera.position.set(0, 0, 15); // Scaled back for better visibility
486
+ embeddedAxisCamera.lookAt(0, 0, 0);
487
+
488
+ // Create appropriately sized axes for background
489
+ const axisScale = 6.0; // Reduced scale by 25% (from 8.0 to 6.0)
490
+
491
+ // Create a modified axis creation function with thicker lines and smaller cones
492
+ const createEmbeddedAxis = (dir, color) => {
493
+ const group = new THREE.Group();
494
+
495
+ // Create line for positive axis direction - MUCH THICKER
496
+ const lineGeometry = new THREE.BufferGeometry();
497
+ const endPoint = new THREE.Vector3(dir.x, dir.y, dir.z).multiplyScalar(0.92); // Longer line
498
+ lineGeometry.setAttribute('position',
499
+ new THREE.Float32BufferAttribute([0, 0, 0, endPoint.x, endPoint.y, endPoint.z], 3));
500
+
501
+ const lineMaterial = new THREE.LineBasicMaterial({
502
+ color: color,
503
+ linewidth: 45,
504
+ depthTest: false,
505
+ transparent: true,
506
+ opacity: 1.0
507
+ });
508
+
509
+ const line = new THREE.Line(lineGeometry, lineMaterial);
510
+ group.add(line);
511
+
512
+ // Create negative axis direction with thicker dashed line
513
+ const negLineGeometry = new THREE.BufferGeometry();
514
+ const negDir = new THREE.Vector3(-dir.x, -dir.y, -dir.z).multiplyScalar(0.92); // Longer line
515
+ negLineGeometry.setAttribute('position',
516
+ new THREE.Float32BufferAttribute([0, 0, 0, negDir.x, negDir.y, negDir.z], 3));
517
+
518
+ const dashedLineMaterial = new THREE.LineDashedMaterial({
519
+ color: color,
520
+ linewidth: 45,
521
+ scale: 1,
522
+ dashSize: 0.2, // Larger dashes
523
+ gapSize: 0.05, // Smaller gaps
524
+ depthTest: false,
525
+ transparent: true,
526
+ opacity: 0.9
527
+ });
528
+
529
+ const dashedLine = new THREE.Line(negLineGeometry, dashedLineMaterial);
530
+ dashedLine.computeLineDistances(); // Required for dashed lines
531
+ group.add(dashedLine);
532
+
533
+ // Create very small arrow head (cone)
534
+ const arrowGeometry = new THREE.CylinderGeometry(0, 0.05, 0.15, 6, 1); // Tiny cone
535
+ const arrowMaterial = new THREE.MeshBasicMaterial({
536
+ color: color,
537
+ transparent: true,
538
+ opacity: 1.0,
539
+ depthTest: false
540
+ });
541
+
542
+ const arrow = new THREE.Mesh(arrowGeometry, arrowMaterial);
543
+
544
+ // Position at the end of the line
545
+ arrow.position.copy(dir);
546
+
547
+ // Rotate arrow to point in the right direction
548
+ if (dir.x === 1) {
549
+ arrow.rotation.z = -Math.PI / 2;
550
+ } else if (dir.y === 1) {
551
+ // Default orientation works for Y
552
+ } else if (dir.z === 1) {
553
+ arrow.rotation.x = Math.PI / 2;
554
+ }
555
+
556
+ group.add(arrow);
557
+
558
+ // Create larger and more visible text label
559
+ const text = dir.x === 1 ? 'X' : dir.y === 1 ? 'Y' : 'Z';
560
+ const canvas = document.createElement('canvas');
561
+ canvas.width = 256; // Larger canvas for clearer text
562
+ canvas.height = 256;
563
+ const ctx = canvas.getContext('2d');
564
+
565
+ // Clear canvas
566
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
567
+
568
+ // Draw text with a stronger glow effect
569
+ ctx.font = 'bold 96px Arial'; // Larger font
570
+ ctx.textAlign = 'center';
571
+ ctx.textBaseline = 'middle';
572
+
573
+ // Add a stronger glow effect
574
+ ctx.shadowColor = color;
575
+ ctx.shadowBlur = 15;
576
+ ctx.fillStyle = color;
577
+ ctx.fillText(text, canvas.width/2, canvas.height/2);
578
+
579
+ const texture = new THREE.CanvasTexture(canvas);
580
+ const spriteMaterial = new THREE.SpriteMaterial({
581
+ map: texture,
582
+ transparent: true,
583
+ depthTest: false
584
+ });
585
+
586
+ const sprite = new THREE.Sprite(spriteMaterial);
587
+ // Position text closer to the arrow tip
588
+ sprite.position.copy(dir).multiplyScalar(1.2); // Reduced from 1.6 to bring labels closer
589
+ sprite.scale.set(0.6, 0.6, 0.6); // Reduced label size by 25% (from 0.8 to 0.6)
590
+
591
+ group.add(sprite);
592
+
593
+ return group;
594
+ };
595
+
596
+ // Create the three axes using our embedded axis creation function
597
+ const embeddedXAxis = createEmbeddedAxis(new THREE.Vector3(1, 0, 0), '#ff4136'); // Red
598
+ const embeddedYAxis = createEmbeddedAxis(new THREE.Vector3(0, 1, 0), '#2ecc40'); // Green
599
+ const embeddedZAxis = createEmbeddedAxis(new THREE.Vector3(0, 0, 1), '#0074d9'); // Blue
600
+
601
+ // Scale up the axes for visibility while keeping proportions reasonable
602
+ embeddedXAxis.scale.set(axisScale, axisScale, axisScale);
603
+ embeddedYAxis.scale.set(axisScale, axisScale, axisScale);
604
+ embeddedZAxis.scale.set(axisScale, axisScale, axisScale);
605
+
606
+ // Add axes to the embedded scene
607
+ embeddedAxisScene.add(embeddedXAxis);
608
+ embeddedAxisScene.add(embeddedYAxis);
609
+ embeddedAxisScene.add(embeddedZAxis);
610
+
611
+ // Create a smaller center reference point (MAKE INVISIBLE)
612
+ const embeddedCenterGeometry = new THREE.SphereGeometry(0.05 * axisScale, 16, 16);
613
+ const embeddedCenterMaterial = new THREE.MeshBasicMaterial({
614
+ color: 0xffffff,
615
+ transparent: true,
616
+ opacity: 0, // Set opacity to 0 to make it invisible
617
+ depthTest: false
618
+ });
619
+ const embeddedCenterSphere = new THREE.Mesh(embeddedCenterGeometry, embeddedCenterMaterial);
620
+ embeddedAxisScene.add(embeddedCenterSphere);
621
+
622
+ // Get intensity value from settings if available
623
+ let intensity = 0.7; // Default intensity
624
+ const savedSettings = localStorage.getItem('assetDebuggerSettings');
625
+ if (savedSettings) {
626
+ try {
627
+ const settings = JSON.parse(savedSettings);
628
+ if (settings.axisIndicator && settings.axisIndicator.intensity !== undefined) {
629
+ intensity = settings.axisIndicator.intensity;
630
+ }
631
+ } catch (e) {
632
+ console.error('Error reading intensity from settings:', e);
633
+ }
634
+ }
635
+
636
+ // Store references
637
+ state.embeddedAxisIndicator = {
638
+ scene: embeddedAxisScene,
639
+ camera: embeddedAxisCamera,
640
+ xAxis: embeddedXAxis,
641
+ yAxis: embeddedYAxis,
642
+ zAxis: embeddedZAxis,
643
+ centerSphere: embeddedCenterSphere,
644
+ scale: axisScale,
645
+ active: true, // Mark as active
646
+ intensity: intensity // Use intensity from settings
647
+ };
648
+
649
+ // Create a renderer for the embedded axis indicator
650
+ const originalRender = renderer.render;
651
+
652
+ // Replace the renderer.render method
653
+ renderer.render = function(mainScene, mainCamera) {
654
+ // Check if embedded axis indicator is active
655
+ if (state.embeddedAxisIndicator && state.embeddedAxisIndicator.active) {
656
+ // First clear the renderer with black background
657
+ const oldClearColor = renderer.getClearColor(new THREE.Color());
658
+ const oldClearAlpha = renderer.getClearAlpha();
659
+
660
+ // Save auto clear settings
661
+ const oldAutoClear = renderer.autoClear;
662
+ const oldAutoClearColor = renderer.autoClearColor;
663
+ const oldAutoClearDepth = renderer.autoClearDepth;
664
+ const oldAutoClearStencil = renderer.autoClearStencil;
665
+
666
+ // Clear with black background
667
+ renderer.setClearColor(0x000000, 1);
668
+ renderer.clear(); // Clear everything
669
+
670
+ // Update embedded camera to match main camera rotation
671
+ if (mainCamera) {
672
+ const cameraDir = new THREE.Vector3(0, 0, -1).applyQuaternion(mainCamera.quaternion);
673
+ const distance = embeddedAxisCamera.position.length();
674
+ embeddedAxisCamera.position.copy(cameraDir).negate().multiplyScalar(distance);
675
+ embeddedAxisCamera.lookAt(0, 0, 0);
676
+
677
+ // Match aspect ratio
678
+ embeddedAxisCamera.aspect = mainCamera.aspect;
679
+ embeddedAxisCamera.updateProjectionMatrix();
680
+ }
681
+
682
+ // Adjust opacity for background effect
683
+ const applyBackgroundOpacity = (obj) => {
684
+ if (obj.material) {
685
+ // Use the stored intensity value
686
+ const intensity = state.embeddedAxisIndicator.intensity || 0.7;
687
+ obj.material.opacity = obj.material.originalOpacity * intensity;
688
+ }
689
+ if (obj.children) {
690
+ obj.children.forEach(child => applyBackgroundOpacity(child));
691
+ }
692
+ };
693
+
694
+ // Save original opacity values first time
695
+ if (!state.embeddedAxisIndicator.opacitySaved) {
696
+ state.embeddedAxisIndicator.scene.traverse(obj => {
697
+ if (obj.material && obj.material.opacity !== undefined) {
698
+ obj.material.originalOpacity = obj.material.opacity;
699
+ }
700
+ });
701
+ state.embeddedAxisIndicator.opacitySaved = true;
702
+ }
703
+
704
+ // Apply transparency to all objects in embedded axis scene
705
+ state.embeddedAxisIndicator.scene.traverse(applyBackgroundOpacity);
706
+
707
+ // Special settings for background rendering
708
+ renderer.autoClear = false;
709
+ renderer.autoClearDepth = true;
710
+ renderer.autoClearColor = false;
711
+ renderer.autoClearStencil = false;
712
+
713
+ // Render the axis scene first (as background)
714
+ originalRender.call(this, embeddedAxisScene, embeddedAxisCamera);
715
+
716
+ // Restore original material opacity
717
+ const restoreOpacity = (obj) => {
718
+ if (obj.material && obj.material.originalOpacity !== undefined) {
719
+ obj.material.opacity = obj.material.originalOpacity;
720
+ }
721
+ if (obj.children) {
722
+ obj.children.forEach(child => restoreOpacity(child));
723
+ }
724
+ };
725
+
726
+ state.embeddedAxisIndicator.scene.traverse(restoreOpacity);
727
+
728
+ // Reset settings for main scene render
729
+ renderer.autoClear = false; // Don't clear again
730
+ renderer.setClearColor(oldClearColor, oldClearAlpha);
731
+
732
+ // Now render the main scene on top
733
+ originalRender.call(this, mainScene, mainCamera);
734
+
735
+ // Restore original settings
736
+ renderer.autoClear = oldAutoClear;
737
+ renderer.autoClearColor = oldAutoClearColor;
738
+ renderer.autoClearDepth = oldAutoClearDepth;
739
+ renderer.autoClearStencil = oldAutoClearStencil;
740
+ } else {
741
+ // If embedded mode not active, just render normally
742
+ originalRender.call(this, mainScene, mainCamera);
743
+ }
744
+ };
745
+
746
+ console.log('Full-screen embedded axis indicator created successfully with intensity:', intensity);
747
+ }
748
+
749
+ // Function to remove embedded axis indicator
750
+ function removeEmbeddedAxisIndicator() {
751
+ const state = getState();
752
+
753
+ if (state.embeddedAxisIndicator) {
754
+ console.log('Removing embedded axis indicator');
755
+
756
+ // Mark as inactive first
757
+ state.embeddedAxisIndicator.active = false;
758
+
759
+ // If we have a scene, remove all objects to prevent memory leaks
760
+ if (state.embeddedAxisIndicator.scene) {
761
+ // Dispose of geometries and materials
762
+ state.embeddedAxisIndicator.scene.traverse((object) => {
763
+ if (object.geometry) {
764
+ object.geometry.dispose();
765
+ }
766
+
767
+ if (object.material) {
768
+ if (Array.isArray(object.material)) {
769
+ object.material.forEach(material => material.dispose());
770
+ } else {
771
+ object.material.dispose();
772
+ }
773
+ }
774
+ });
775
+
776
+ // Clear the scene
777
+ while (state.embeddedAxisIndicator.scene.children.length > 0) {
778
+ state.embeddedAxisIndicator.scene.remove(state.embeddedAxisIndicator.scene.children[0]);
779
+ }
780
+ }
781
+
782
+ // Free references to Three.js objects
783
+ state.embeddedAxisIndicator.xAxis = null;
784
+ state.embeddedAxisIndicator.yAxis = null;
785
+ state.embeddedAxisIndicator.zAxis = null;
786
+ state.embeddedAxisIndicator.centerSphere = null;
787
+ state.embeddedAxisIndicator.scene = null;
788
+ state.embeddedAxisIndicator.camera = null;
789
+
790
+ // Keep the embeddedAxisIndicator object but mark it as inactive
791
+ // This allows us to maintain settings like intensity
792
+ }
793
+ }
794
+
795
+ // Check for saved settings to initialize correct mode
796
+ const savedSettings = localStorage.getItem('assetDebuggerSettings');
797
+ if (savedSettings) {
798
+ try {
799
+ const settings = JSON.parse(savedSettings);
800
+ if (settings.axisIndicator && settings.axisIndicator.type) {
801
+ const mode = settings.axisIndicator.type;
802
+ const intensity = settings.axisIndicator.intensity || 0.7;
803
+
804
+ // Always forcefully trigger the mode change to ensure correct state
805
+ setTimeout(() => {
806
+ document.dispatchEvent(new CustomEvent('axisIndicatorModeChange', {
807
+ detail: {
808
+ mode: mode,
809
+ intensity: intensity
810
+ }
811
+ }));
812
+ console.log('Forced axis indicator mode:', mode);
813
+ }, 200);
814
+ }
815
+ } catch (e) {
816
+ console.error('Error loading saved axis indicator settings:', e);
817
+ }
818
+ }
819
+
820
+ // Draw a debug log to confirm axis indicator creation complete
821
+ console.log('Modern axis indicator setup complete');
822
+ }