@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,441 @@
1
+ import * as THREE from 'three';
2
+ import {
3
+ clearLabels,
4
+ getLabelGroup,
5
+ hideLabels,
6
+ rigOptions,
7
+ setLabelGroup,
8
+ updateLabelPosition
9
+ } from "./rig-controller";
10
+ import { boneVisualsGroup } from './bone-kinematics';
11
+
12
+ /**
13
+ * Create joint labels for all joints in the scene
14
+ * @param {Object} scene - The Three.js scene
15
+ */
16
+ export function createJointLabels(scene) {
17
+ console.log('Creating joint labels...');
18
+
19
+ // Remove any existing labels first
20
+ clearLabels('joint', scene);
21
+
22
+ // Create a group to hold all labels
23
+ setLabelGroup('joint', "JointLabels", scene);
24
+
25
+ // Keep track of the labels created
26
+ const labelCount = {total: 0, added: 0};
27
+
28
+ // Track positions where we've already created labels to prevent duplicates
29
+ const labelPositions = [];
30
+ const positionTolerance = 0.001; // Tolerance for considering positions as identical
31
+
32
+ // Helper function to check if a position already has a label
33
+ const hasLabelAtPosition = (position) => {
34
+ return labelPositions.some(pos => position.distanceTo(pos) < positionTolerance);
35
+ };
36
+
37
+ // Find all bone meshes
38
+ boneVisualsGroup.traverse((object) => {
39
+ if (object.userData && object.userData.bonePart === 'cap') {
40
+ labelCount.total++;
41
+
42
+ // Get world position of this joint
43
+ const worldPos = new THREE.Vector3();
44
+ object.getWorldPosition(worldPos);
45
+
46
+ // Check if we already have a label at this position
47
+ if (hasLabelAtPosition(worldPos)) {
48
+ console.log('Skipping duplicate joint label at position:', worldPos);
49
+ return;
50
+ }
51
+
52
+ // Determine which bone name to use
53
+ let boneName = "";
54
+ if (object.parent && object.parent.userData) {
55
+ if (object.position.y > 0 && object.parent.userData.childBone) {
56
+ // Top sphere - use child bone name
57
+ boneName = object.parent.userData.childBone.name;
58
+ } else if (object.position.y === 0 && object.parent.userData.parentBone) {
59
+ // Bottom sphere - use parent bone name
60
+ boneName = object.parent.userData.parentBone.name;
61
+ }
62
+ }
63
+
64
+ if (boneName) {
65
+ // Create a label for this joint
66
+ const label = createSimpleLabel(boneName, object, scene);
67
+ if (label) {
68
+ const jointLabelGroup = getLabelGroup('joint');
69
+ if (jointLabelGroup) {
70
+ jointLabelGroup.add(label);
71
+ labelCount.added++;
72
+
73
+ // Record this position as having a label
74
+ labelPositions.push(worldPos);
75
+ }
76
+ }
77
+ }
78
+ }
79
+ });
80
+
81
+ console.log(`Created ${labelCount.added} labels out of ${labelCount.total} joint spheres found`);
82
+ return scene.getObjectByName("JointLabels");
83
+ }
84
+
85
+ /**
86
+ * Create bone labels for all bone segments in the scene
87
+ * @param {Object} scene - The Three.js scene
88
+ */
89
+ export function createBoneLabels(scene) {
90
+ console.log('Creating bone labels...');
91
+
92
+ // Remove any existing bone labels
93
+ clearLabels('bone', scene);
94
+
95
+ // Create a group to hold all bone labels
96
+ setLabelGroup('bone', "BoneLabels", scene);
97
+
98
+ // Keep track of the labels created
99
+ const labelCount = {total: 0, added: 0};
100
+
101
+ // Track bone connections that already have labels to prevent duplicates
102
+ const labeledBoneConnections = new Set();
103
+
104
+ // Find all bone groups
105
+ boneVisualsGroup.children.forEach((boneGroup) => {
106
+ // Skip if not a bone group
107
+ if (!boneGroup.userData || !boneGroup.userData.isVisualBone) {
108
+ return;
109
+ }
110
+
111
+ labelCount.total++;
112
+
113
+ // Get bone name information from the bone group
114
+ let boneName = "";
115
+ if (boneGroup.userData.parentBone && boneGroup.userData.childBone) {
116
+ // Create a connection identifier (both directions to handle differently ordered pairs)
117
+ const parentID = boneGroup.userData.parentBone.id || boneGroup.userData.parentBone.uuid;
118
+ const childID = boneGroup.userData.childBone.id || boneGroup.userData.childBone.uuid;
119
+ const connectionID = `${parentID}_${childID}`;
120
+ const reverseConnectionID = `${childID}_${parentID}`;
121
+
122
+ // Skip if we already created a label for this connection
123
+ if (labeledBoneConnections.has(connectionID) || labeledBoneConnections.has(reverseConnectionID)) {
124
+ console.log('Skipping duplicate bone label for connection:',
125
+ `${boneGroup.userData.parentBone.name} → ${boneGroup.userData.childBone.name}`);
126
+ return;
127
+ }
128
+
129
+ // Create a name that indicates the connection
130
+ boneName = `${boneGroup.userData.parentBone.name} → ${boneGroup.userData.childBone.name}`;
131
+
132
+ // Create a label for this bone
133
+ const label = createSimpleLabel(boneName, boneGroup, scene, true); // Pass true to indicate it's a bone label
134
+ if (label) {
135
+ const boneLabelGroup = getLabelGroup('bone');
136
+ if (boneLabelGroup) {
137
+ boneLabelGroup.add(label);
138
+ labelCount.added++;
139
+
140
+ // Mark this connection as labeled
141
+ labeledBoneConnections.add(connectionID);
142
+
143
+ // Calculate the midpoint between parent and child bones immediately
144
+ const parentPos = new THREE.Vector3();
145
+ const childPos = new THREE.Vector3();
146
+
147
+ boneGroup.userData.parentBone.getWorldPosition(parentPos);
148
+ boneGroup.userData.childBone.getWorldPosition(childPos);
149
+
150
+ // Calculate the middle point
151
+ const midPoint = new THREE.Vector3().addVectors(parentPos, childPos).multiplyScalar(0.5);
152
+
153
+ // Position at the middle point immediately
154
+ label.position.copy(midPoint);
155
+
156
+ // Position the label at the middle of the bone
157
+ if (label.userData.updatePosition) {
158
+ label.userData.updatePosition();
159
+ }
160
+ }
161
+ }
162
+ }
163
+ });
164
+
165
+ console.log(`Created ${labelCount.added} labels out of ${labelCount.total} bone segments found`);
166
+ return scene.getObjectByName("BoneLabels");
167
+ }
168
+
169
+ /**
170
+ * Create both joint and bone labels and set their visibility
171
+ * @param {Object} scene - The Three.js scene
172
+ */
173
+ export function createLabels(scene) {
174
+ // Create joint labels
175
+ console.log('Setting up joint labels');
176
+ createJointLabels(scene);
177
+
178
+ // Check if joint labels should be visible based on option
179
+ if (!rigOptions.showJointLabels) {
180
+ hideLabels('joint');
181
+ }
182
+
183
+ // Create bone labels
184
+ console.log('Setting up bone labels');
185
+ createBoneLabels(scene);
186
+
187
+ // Check if bone labels should be visible based on option
188
+ if (!rigOptions.showBoneLabels) {
189
+ hideLabels('bone');
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Create a simple text label as a sprite
195
+ * @param {string} text - Text to display
196
+ * @param {Object} joint - Joint object to attach to
197
+ * @param {Object} scene - Three.js scene
198
+ * @param {boolean} isBoneLabel - Whether this is a bone label (true) or joint label (false)
199
+ * @returns {Object} The created label sprite
200
+ */
201
+ function createSimpleLabel(text, joint, scene, isBoneLabel = false) {
202
+ console.log(`Creating label for ${isBoneLabel ? 'bone' : 'joint'}: ${text}`);
203
+
204
+ // Create a canvas for the label
205
+ const canvas = document.createElement('canvas');
206
+ const ctx = canvas.getContext('2d');
207
+
208
+ // Set canvas size
209
+ canvas.width = 256;
210
+ // Increase height for bone labels to accommodate multiple lines
211
+ canvas.height = isBoneLabel ? 96 : 64;
212
+
213
+ // Different styling for bone vs joint labels - ONLY VISUAL DIFFERENCES HERE
214
+ const labelConfig = {
215
+ bgColor: isBoneLabel ? 'rgba(30, 136, 229, 0.8)' : 'rgba(76, 175, 80, 0.8)',
216
+ gradientStops: isBoneLabel ?
217
+ [{ pos: 0, color: 'rgba(40, 40, 80, 0.4)' }, { pos: 1, color: 'rgba(20, 20, 40, 0.6)' }] :
218
+ [{ pos: 0, color: 'rgba(30, 60, 30, 0.4)' }, { pos: 1, color: 'rgba(10, 30, 10, 0.6)' }],
219
+ borderColor: isBoneLabel ? '#88AAFF' : '#AAFFAA',
220
+ scrollThumbColor: isBoneLabel ? '#88AAFF' : '#AAFFAA',
221
+ headerText: isBoneLabel ? 'BONE' : 'JOINT'
222
+ };
223
+
224
+ // Background fill
225
+ ctx.fillStyle = labelConfig.bgColor;
226
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
227
+
228
+ // Add gradient overlay
229
+ const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
230
+ labelConfig.gradientStops.forEach(stop => {
231
+ gradient.addColorStop(stop.pos, stop.color);
232
+ });
233
+ ctx.fillStyle = gradient;
234
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
235
+
236
+ // Border
237
+ ctx.strokeStyle = labelConfig.borderColor;
238
+ ctx.lineWidth = 2;
239
+ ctx.strokeRect(0, 0, canvas.width, canvas.height);
240
+
241
+ // Header line
242
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
243
+ ctx.fillRect(0, 0, canvas.width, 18);
244
+
245
+ // Header text
246
+ ctx.font = 'bold 12px monospace';
247
+ ctx.fillStyle = '#DDDDDD';
248
+ ctx.textAlign = 'left';
249
+ ctx.textBaseline = 'middle';
250
+ ctx.fillText(labelConfig.headerText, 6, 9);
251
+
252
+ // Main text
253
+ ctx.font = 'bold 16px monospace';
254
+ ctx.fillStyle = 'white';
255
+ ctx.textAlign = 'center';
256
+ ctx.textBaseline = 'middle';
257
+
258
+ // For bone labels, format as multiple lines if it contains an arrow
259
+ if (isBoneLabel && text.includes('→')) {
260
+ const parts = text.split('→');
261
+ const parent = parts[0].trim();
262
+ const child = parts[1].trim();
263
+
264
+ // Calculate max width to check if parts need scrolling
265
+ const parentWidth = ctx.measureText(parent).width;
266
+ const childWidth = ctx.measureText(child).width;
267
+ const maxWidth = canvas.width - 20; // 10px padding on each side
268
+
269
+ // First line: parent
270
+ let parentText = parent;
271
+ if (parentWidth > maxWidth) {
272
+ parentText = parent.length > 25 ? parent.substring(0, 22) + '...' : parent;
273
+ }
274
+ ctx.fillText(parentText, canvas.width / 2, 35);
275
+
276
+ // Arrow line
277
+ ctx.fillText('↓', canvas.width / 2, 55);
278
+
279
+ // Third line: child
280
+ let childText = child;
281
+ if (childWidth > maxWidth) {
282
+ childText = child.length > 25 ? child.substring(0, 22) + '...' : child;
283
+ }
284
+ ctx.fillText(childText, canvas.width / 2, 75);
285
+
286
+ // If either part was truncated, add a scroll indicator
287
+ if (parentWidth > maxWidth || childWidth > maxWidth) {
288
+ // Draw scroll indicator
289
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
290
+ ctx.fillRect(10, canvas.height - 8, canvas.width - 20, 4);
291
+
292
+ // Draw scrollbar thumb
293
+ const contentWidth = Math.max(parentWidth, childWidth);
294
+ const thumbWidth = Math.max(30, (maxWidth / contentWidth) * (canvas.width - 20));
295
+ ctx.fillStyle = labelConfig.scrollThumbColor;
296
+ ctx.fillRect(10, canvas.height - 8, thumbWidth, 4);
297
+ }
298
+ } else {
299
+ // Regular single-line text handling (for joint labels or non-arrow bone labels)
300
+ // Calculate text width to check if it needs scrolling
301
+ const textMetrics = ctx.measureText(text);
302
+ const textWidth = textMetrics.width;
303
+ const maxWidth = canvas.width - 20; // 10px padding on each side
304
+
305
+ // If text is too long, implement scrolling behavior
306
+ if (textWidth > maxWidth) {
307
+ // Indicate text is scrollable with ellipsis
308
+ const displayText = text.length > 25 ? text.substring(0, 22) + '...' : text;
309
+ ctx.fillText(displayText, canvas.width / 2, canvas.height / 2 + 5);
310
+
311
+ // Draw scroll indicator
312
+ ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
313
+ ctx.fillRect(10, canvas.height - 8, canvas.width - 20, 4);
314
+
315
+ // Draw scrollbar thumb
316
+ const thumbWidth = Math.max(30, (maxWidth / textWidth) * (canvas.width - 20));
317
+ ctx.fillStyle = labelConfig.scrollThumbColor;
318
+ ctx.fillRect(10, canvas.height - 8, thumbWidth, 4);
319
+ } else {
320
+ // Text fits, just display it
321
+ ctx.fillText(text, canvas.width / 2, canvas.height / 2 + 5);
322
+ }
323
+ }
324
+
325
+ // Create sprite material
326
+ const texture = new THREE.CanvasTexture(canvas);
327
+ const material = new THREE.SpriteMaterial({
328
+ map: texture,
329
+ transparent: true,
330
+ depthTest: false,
331
+ depthWrite: false
332
+ });
333
+
334
+ // Create sprite
335
+ const sprite = new THREE.Sprite(material);
336
+ sprite.userData.isBoneLabel = isBoneLabel;
337
+ sprite.userData.isJointLabel = !isBoneLabel;
338
+ sprite.userData.targetJoint = joint;
339
+ sprite.userData.labelText = text; // Store original text
340
+
341
+ // Add hover handling for label headers
342
+ sprite.userData.isInteractive = true;
343
+ sprite.userData.isLabelHeader = true;
344
+ sprite.userData.headerHeight = 18; // Height of the header section in pixels
345
+ sprite.userData.canvasHeight = canvas.height;
346
+ sprite.userData.canvasWidth = canvas.width;
347
+
348
+ // Add mouse event handling to prevent propagation when over the header
349
+ sprite.userData.onMouseMove = (event) => {
350
+ // Check if mouse is over the header area
351
+ if (sprite.userData.isMouseOverHeader) {
352
+ // Prevent event propagation to stop camera controls
353
+ event.stopPropagation();
354
+ }
355
+ };
356
+
357
+ // This function calculates if mouse is over the header or the content area
358
+ sprite.userData.checkHeaderHover = (mousePos) => {
359
+ if (!mousePos) return false;
360
+
361
+ // The sprite's coordinates go from -0.5 to 0.5 in both dimensions
362
+ // So we need to convert from local sprite coordinates to canvas coordinates
363
+
364
+ // Calculate the overall dimensions in world units
365
+ const totalHeight = sprite.scale.y;
366
+
367
+ // Convert mousePos.y from local coordinates (-0.5 to 0.5) to normalized height (0 to 1)
368
+ // In THREE.js, sprite local coords have origin at center, Y+ is up
369
+ // -0.5 is bottom, 0.5 is top, so we need to flip and shift
370
+ const normalizedY = 0.5 - mousePos.y; // Convert to 0 at top, 1 at bottom
371
+
372
+ // Now convert to canvas pixel coordinates
373
+ const canvasY = normalizedY * canvas.height;
374
+
375
+ // Check if we're in the header region (top 18px of the canvas)
376
+ const isOverHeader = canvasY >= 0 && canvasY <= sprite.userData.headerHeight;
377
+
378
+ return isOverHeader;
379
+ };
380
+
381
+ // Set initial position safely - updateLabelPosition now has defensive coding
382
+ updateLabelPosition(sprite, joint);
383
+
384
+ // FIXED SIZE FOR ALL LABELS - no dependency on underlying object geometry
385
+ const fixedScale = 0.3;
386
+ // Adjust height for bone labels to accommodate multiple lines
387
+ const heightFactor = isBoneLabel ? 0.4 : 0.25;
388
+ sprite.scale.set(fixedScale, fixedScale * heightFactor, 1);
389
+
390
+ // Set initial visibility based on appropriate option
391
+ sprite.visible = isBoneLabel ? rigOptions.showBoneLabels : rigOptions.showJointLabels;
392
+ if (!sprite.visible) {
393
+ console.log(`Label for ${text} is initially hidden`);
394
+ }
395
+
396
+ // Set up the update function - customize for bone labels
397
+ sprite.userData.updatePosition = () => {
398
+ // For bone labels, we want to position them in the middle of the bone
399
+ if (isBoneLabel) {
400
+ // Get the bone group which should have parent and child bone references
401
+ const boneGroup = joint.userData && joint.userData.isVisualBone ?
402
+ joint : (joint.parent && joint.parent.userData && joint.parent.userData.isVisualBone ?
403
+ joint.parent : null);
404
+
405
+ if (boneGroup && boneGroup.userData &&
406
+ boneGroup.userData.parentBone && boneGroup.userData.childBone) {
407
+
408
+ // Get world positions of parent and child bones
409
+ const parentPos = new THREE.Vector3();
410
+ const childPos = new THREE.Vector3();
411
+
412
+ boneGroup.userData.parentBone.getWorldPosition(parentPos);
413
+ boneGroup.userData.childBone.getWorldPosition(childPos);
414
+
415
+ // Calculate the middle point
416
+ const midPoint = new THREE.Vector3().addVectors(parentPos, childPos).multiplyScalar(0.5);
417
+
418
+ // Position exactly at the midpoint without any offset
419
+ sprite.position.copy(midPoint);
420
+ } else {
421
+ // Fallback for when we can't find the proper bone references
422
+ const worldPos = new THREE.Vector3();
423
+ joint.getWorldPosition(worldPos);
424
+ sprite.position.copy(worldPos);
425
+ }
426
+ } else {
427
+ // For joint labels, use the standard update function
428
+ updateLabelPosition(sprite, joint);
429
+ }
430
+ };
431
+
432
+ // Initialize the position
433
+ if (sprite.userData.updatePosition) {
434
+ sprite.userData.updatePosition();
435
+ }
436
+
437
+ // Make sure the sprite renders on top
438
+ sprite.renderOrder = isBoneLabel ? 990 : 1000; // Joint labels show on top of bone labels
439
+
440
+ return sprite;
441
+ }