@littlecarlito/blorktools 0.50.4 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,628 @@
1
+ /**
2
+ * Asset Debugger - Rig Factory
3
+ *
4
+ * This module provides rig creation and initialization functionality.
5
+ * Code moved from rig-heading.js to create a separate factory module.
6
+ */
7
+ import * as THREE from 'three';
8
+ import { getState } from '../state/scene-state';
9
+ import { getIsDragging, setupMouseListeners } from './rig-mouse-handler';
10
+ import {
11
+ clearRigVisualization,
12
+ rigDetails,
13
+ updateRigDetails,
14
+ rigOptions,
15
+ updateLabelPosition
16
+ } from './rig-controller.js'
17
+ import {
18
+ bones,
19
+ boneJointMaterial,
20
+ boneMaterial,
21
+ boneSideMaterial,
22
+ boneVisualsGroup,
23
+ findFarthestBone,
24
+ setBoneMaterial,
25
+ setBoneSideMaterial,
26
+ setBoneJointMaterial,
27
+ resetBoneVisualGroup,
28
+ resetBones
29
+ } from './bone-kinematics.js';
30
+ import { createAxisIndicator } from '../../axis-indicator/axis-indicator';
31
+ import { createLabels } from './rig-label-factory';
32
+ import { applyJointConstraints } from './rig-constraint-manager';
33
+ import { parseJointConstraints } from '../data/glb-classifier';
34
+ import { addControlHandleToObject, primaryRigHandle } from './rig-handle-factory';
35
+
36
+ /**
37
+ * Create a rig system with visualization, controls and interactions
38
+ * @param {Object} model - The model to create a rig for
39
+ * @param {Object} scene - The Three.js scene
40
+ * @returns {Object} The created rig
41
+ */
42
+ export function createRig(model, scene) {
43
+ console.log('Creating rig...');
44
+
45
+ // Clear any existing rig visualization
46
+ clearRigVisualization(scene);
47
+ resetBones();
48
+
49
+ // Initialize rigDetails.joints if needed
50
+ if (!rigDetails) {
51
+ updateRigDetails({ bones: [], rigs: [], roots: [], controls: [], joints: [], constraints: [] });
52
+ } else if (!rigDetails.joints) {
53
+ rigDetails.joints = [];
54
+ }
55
+
56
+ // Add constraints array if not present
57
+ if (!rigDetails.constraints) {
58
+ rigDetails.constraints = [];
59
+ }
60
+
61
+ // Find all bones
62
+ let armature = null;
63
+
64
+ // Find armature first
65
+ model.traverse(node => {
66
+ if ((node.name.toLowerCase().includes('rig') ||
67
+ node.name.toLowerCase().includes('armature')) && !armature) {
68
+ armature = node;
69
+ console.log('Found armature:', node.name);
70
+ }
71
+ });
72
+
73
+ // First pass: collect all bones from armature if found
74
+ if (armature) {
75
+ armature.traverse(node => {
76
+ if (node.isBone || node.name.toLowerCase().includes('bone')) {
77
+ // Store initial rotation for reset functionality
78
+ node.userData.initialRotation = {
79
+ x: node.rotation.x,
80
+ y: node.rotation.y,
81
+ z: node.rotation.z,
82
+ order: node.rotation.order
83
+ };
84
+ // Log whether this is a root bone
85
+ if (node.name.toLowerCase().includes('root')) {
86
+ console.log('Found ROOT bone:', node.name);
87
+ } else {
88
+ console.log('Found bone:', node.name);
89
+ }
90
+
91
+ // Look for constraint data
92
+ const constraints = parseJointConstraints(node);
93
+ if (constraints) {
94
+ console.log(`Found constraints for bone ${node.name}:`, constraints);
95
+ // Apply constraints to the bone
96
+ applyJointConstraints(node, constraints);
97
+
98
+ // Store constraint information
99
+ if (rigDetails && rigDetails.constraints) {
100
+ rigDetails.constraints.push({
101
+ boneName: node.name,
102
+ type: constraints.type,
103
+ data: constraints
104
+ });
105
+ }
106
+ }
107
+
108
+ bones.push(node);
109
+ }
110
+ });
111
+ }
112
+
113
+ // If no bones were found in the armature, search the entire model
114
+ if (bones.length === 0) {
115
+ console.log('No bones found in armature, searching entire model');
116
+ model.traverse(node => {
117
+ if (node.isBone || node.name.toLowerCase().includes('bone')) {
118
+ // Store initial rotation for reset functionality
119
+ node.userData.initialRotation = {
120
+ x: node.rotation.x,
121
+ y: node.rotation.y,
122
+ z: node.rotation.z,
123
+ order: node.rotation.order
124
+ };
125
+
126
+ // Look for constraint data
127
+ const constraints = parseJointConstraints(node);
128
+ if (constraints) {
129
+ console.log(`Found constraints for bone ${node.name}:`, constraints);
130
+ // Apply constraints to the bone
131
+ applyJointConstraints(node, constraints);
132
+
133
+ // Store constraint information
134
+ if (rigDetails && rigDetails.constraints) {
135
+ rigDetails.constraints.push({
136
+ boneName: node.name,
137
+ type: constraints.type,
138
+ data: constraints
139
+ });
140
+ }
141
+ }
142
+
143
+ // Log whether this is a root bone
144
+ if (node.name.toLowerCase().includes('root')) {
145
+ console.log('Found ROOT bone in model:', node.name);
146
+ } else {
147
+ console.log('Found bone in model:', node.name);
148
+ }
149
+ bones.push(node);
150
+ }
151
+ });
152
+ }
153
+
154
+ // If still no bones, exit
155
+ if (bones.length === 0) {
156
+ console.log('No bones found in model');
157
+ return;
158
+ }
159
+
160
+ // Create materials for bone visualization
161
+ setBoneMaterial(new THREE.MeshBasicMaterial({
162
+ color: rigOptions.primaryColor,
163
+ side: THREE.DoubleSide,
164
+ wireframe: rigOptions.wireframe,
165
+ transparent: true,
166
+ opacity: 0.8 // Increased opacity for better visibility
167
+ }));
168
+
169
+ setBoneSideMaterial(new THREE.MeshBasicMaterial({
170
+ color: rigOptions.secondaryColor,
171
+ side: THREE.DoubleSide,
172
+ wireframe: rigOptions.wireframe,
173
+ transparent: true,
174
+ opacity: 0.8 // Increased opacity for better visibility
175
+ }));
176
+
177
+ // Joint material (now separate from primary/secondary colors)
178
+ setBoneJointMaterial(new THREE.MeshBasicMaterial({
179
+ color: rigOptions.jointColor,
180
+ side: THREE.DoubleSide,
181
+ wireframe: rigOptions.wireframe,
182
+ transparent: true,
183
+ opacity: 0.8 // Slightly more opaque for better visibility
184
+ }));
185
+
186
+ // Create a group to hold all bone visualizations
187
+ resetBoneVisualGroup(scene);
188
+
189
+ // Create axis indicator
190
+ const state = getState();
191
+ if (state.renderer && state.camera) {
192
+ createAxisIndicator(scene, state.camera, state.renderer);
193
+ }
194
+
195
+ // Group bones by parent for easier bone pair creation
196
+ const bonesByParent = new Map();
197
+
198
+ // Filter out control bones that we don't want to visualize
199
+ const visualizableBones = bones.filter(bone =>
200
+ !(bone.name.toLowerCase().includes('control') ||
201
+ bone.name.toLowerCase().includes('ctrl') ||
202
+ bone.name.toLowerCase().includes('handle'))
203
+ );
204
+
205
+ // Identify root bones
206
+ const rootBones = visualizableBones.filter(bone =>
207
+ bone.name.toLowerCase().includes('root')
208
+ );
209
+
210
+ // Group bones by parent for easier bone pair creation
211
+ visualizableBones.forEach(bone => {
212
+ if (bone.parent) {
213
+ const parentId = bone.parent.uuid;
214
+ if (!bonesByParent.has(parentId)) {
215
+ bonesByParent.set(parentId, []);
216
+ }
217
+ bonesByParent.get(parentId).push(bone);
218
+ }
219
+ });
220
+
221
+ // Calculate model scale for appropriate bone visualization size
222
+ const bbox = new THREE.Box3().setFromObject(model);
223
+ const size = new THREE.Vector3();
224
+ bbox.getSize(size);
225
+ const modelScale = size.length() * 0.02;
226
+ const boneRadius = Math.max(0.02, modelScale * 0.3);
227
+
228
+ // Create visual bones between parent-child bone pairs
229
+ visualizableBones.forEach(bone => {
230
+ // Skip if this bone is not in our scene
231
+ if (!scene.getObjectById(bone.id)) return;
232
+
233
+ // Get current bone position
234
+ const bonePos = new THREE.Vector3();
235
+ bone.getWorldPosition(bonePos);
236
+
237
+ // Check if this bone has children in our bone list
238
+ const childBones = bonesByParent.get(bone.uuid) || [];
239
+
240
+ // If this bone has child bones, create a visual bone for each connection
241
+ childBones.forEach(childBone => {
242
+ // Skip control bones
243
+ if (childBone.name.toLowerCase().includes('control') ||
244
+ childBone.name.toLowerCase().includes('ctrl') ||
245
+ childBone.name.toLowerCase().includes('handle')) {
246
+ return;
247
+ }
248
+
249
+ // Get child bone position
250
+ const childPos = new THREE.Vector3();
251
+ childBone.getWorldPosition(childPos);
252
+
253
+ // Calculate distance and direction
254
+ const distance = bonePos.distanceTo(childPos);
255
+
256
+ // Only create visual bone if distance is not zero
257
+ if (distance > 0.001) {
258
+ // Create a group for the bone mesh
259
+ const boneGroup = new THREE.Group();
260
+ boneVisualsGroup.add(boneGroup);
261
+
262
+ // Position bone group at parent bone position
263
+ boneGroup.position.copy(bonePos);
264
+
265
+ // Make the bone look at the child
266
+ const direction = new THREE.Vector3().subVectors(childPos, bonePos);
267
+ boneGroup.lookAt(childPos);
268
+
269
+ // Rotate to align with standard Three.js cylinder orientation
270
+ boneGroup.rotateX(Math.PI/2);
271
+
272
+ // Pass both materials (primary and secondary) for alternating sides
273
+ createBoneMesh(boneGroup, boneRadius, boneRadius, distance, boneJointMaterial, boneMaterial, boneSideMaterial);
274
+
275
+ // Store reference to the bone connection
276
+ boneGroup.userData.parentBone = bone;
277
+ boneGroup.userData.childBone = childBone;
278
+
279
+ // Add update function
280
+ boneGroup.userData.isVisualBone = true;
281
+ boneGroup.userData.updatePosition = createBoneUpdateFunction(boneGroup);
282
+
283
+ // Store the joint data for the rig details panel
284
+ if (rigDetails && rigDetails.joints) {
285
+ rigDetails.joints.push({
286
+ name: `Joint_${bone.name}_to_${childBone.name}`,
287
+ parentBone: bone.name,
288
+ childBone: childBone.name,
289
+ position: [bonePos.x, bonePos.y, bonePos.z],
290
+ count: 1
291
+ });
292
+ }
293
+ }
294
+ });
295
+ });
296
+
297
+ // Create root bone visualization (as a standalone puck)
298
+ rootBones.forEach(rootBone => {
299
+ // Get root bone position
300
+ const rootPos = new THREE.Vector3();
301
+ rootBone.getWorldPosition(rootPos);
302
+
303
+ console.log(`Creating standalone root visualization for: ${rootBone.name} at position:`, rootPos);
304
+
305
+ // Create a group for the root visualization
306
+ const rootGroup = new THREE.Group();
307
+ rootGroup.position.copy(rootPos);
308
+ boneVisualsGroup.add(rootGroup);
309
+
310
+ // Use the new createBoneJoint function
311
+ const rootPuckSize = boneRadius * 2.5;
312
+ const rootPuck = createBoneJoint('root',
313
+ { rootBone: rootBone },
314
+ rootPuckSize,
315
+ {
316
+ boneName: rootBone.name,
317
+ renderOrder: 25 // Higher than normal joints (10-15) but lower than handle (30)
318
+ }
319
+ );
320
+
321
+ // Add to root group
322
+ rootGroup.add(rootPuck);
323
+
324
+ // Store reference to the bone
325
+ rootGroup.userData.rootBone = rootBone;
326
+
327
+ // Add update function
328
+ rootGroup.userData.isVisualBone = true;
329
+ rootGroup.userData.updatePosition = () => {
330
+ if (rootGroup.userData.rootBone) {
331
+ const pos = new THREE.Vector3();
332
+ rootGroup.userData.rootBone.getWorldPosition(pos);
333
+ rootGroup.position.copy(pos);
334
+ }
335
+ };
336
+
337
+ // Store the root joint data for the rig details panel
338
+ if (rigDetails && rigDetails.joints) {
339
+ rigDetails.joints.push({
340
+ name: `Root_Joint_${rootBone.name}`,
341
+ parentBone: "Scene Root",
342
+ childBone: rootBone.name,
343
+ position: [rootPos.x, rootPos.y, rootPos.z],
344
+ count: 1,
345
+ isRoot: true
346
+ });
347
+ }
348
+
349
+ console.log(`Root visualization created for ${rootBone.name}`);
350
+ });
351
+
352
+ // Find the furthest bone from the root and add a control handle
353
+ const furthestBone = findFarthestBone();
354
+ if (furthestBone) {
355
+ addControlHandleToObject(furthestBone, scene, modelScale);
356
+ }
357
+
358
+ // Create labels for joints and bones
359
+ createLabels(scene);
360
+
361
+ // Set up mouse event listeners for hover effect
362
+ setupMouseListeners(scene);
363
+
364
+ console.log('Rig visualization created with', bones.length, 'bones');
365
+
366
+ // Explicitly check if ForceZ is enabled and apply it
367
+ // This ensures it gets applied even during initialization to avoid race conditions
368
+ if (rigOptions.forceZ && boneVisualsGroup) {
369
+ console.log('Force Z is enabled - applying immediately during rig creation');
370
+
371
+ // Apply ForceZ settings directly to the rig (similar to updateRigVisualization)
372
+ boneVisualsGroup.renderOrder = 1000;
373
+
374
+ if (boneMaterial) {
375
+ boneMaterial.depthTest = false;
376
+ boneMaterial.needsUpdate = true;
377
+ }
378
+
379
+ if (boneSideMaterial) {
380
+ boneSideMaterial.depthTest = false;
381
+ boneSideMaterial.needsUpdate = true;
382
+ }
383
+
384
+ // Set renderOrder and disable depth test for all meshes
385
+ boneVisualsGroup.traverse(object => {
386
+ if (object.isMesh) {
387
+ if (object.userData.bonePart === 'cap') {
388
+ object.renderOrder = 1020;
389
+ } else if (object.userData.bonePart === 'side') {
390
+ object.renderOrder = 1010;
391
+ } else {
392
+ object.renderOrder = 1000;
393
+ }
394
+
395
+ if (object.material) {
396
+ object.material.depthTest = false;
397
+ object.material.needsUpdate = true;
398
+ }
399
+ }
400
+ });
401
+
402
+ if (primaryRigHandle && primaryRigHandle.material) {
403
+ primaryRigHandle.renderOrder = 1030;
404
+ primaryRigHandle.material.depthTest = false;
405
+ primaryRigHandle.material.needsUpdate = true;
406
+ }
407
+ }
408
+
409
+ // At the end of createRig, log summary of constraints found
410
+ if (rigDetails && rigDetails.constraints && rigDetails.constraints.length > 0) {
411
+ console.log('=== JOINT CONSTRAINT SUMMARY ===');
412
+ console.log(`Found ${rigDetails.constraints.length} joint constraints:`);
413
+
414
+ // Group by constraint type
415
+ const constraintsByType = {};
416
+ rigDetails.constraints.forEach(constraint => {
417
+ if (!constraintsByType[constraint.type]) {
418
+ constraintsByType[constraint.type] = [];
419
+ }
420
+ constraintsByType[constraint.type].push(constraint.boneName);
421
+ });
422
+
423
+ // Log summary by type
424
+ Object.keys(constraintsByType).forEach(type => {
425
+ console.log(` - ${type}: ${constraintsByType[type].length} joints`);
426
+ constraintsByType[type].forEach(boneName => {
427
+ console.log(` - ${boneName}`);
428
+ });
429
+ });
430
+
431
+ console.log('================================');
432
+ } else {
433
+ console.log('No joint constraints found in model');
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Create a bone joint with consistent styling and properties
439
+ * @param {String} jointType - Type of joint ('regular', 'root')
440
+ * @param {Object} boneData - Object containing bone references
441
+ * @param {Number} radius - Radius size for the joint
442
+ * @param {Object} options - Additional options for joint customization
443
+ * @returns {Object} - The created joint mesh
444
+ */
445
+ function createBoneJoint(jointType, boneData, radius, options = {}) {
446
+ console.log(`Creating ${jointType} joint`);
447
+
448
+ // Create joint based on joint type
449
+ let geometry, material, joint, jointColor, opacity;
450
+
451
+ // Set default radius if not provided
452
+ radius = radius || 1.0;
453
+
454
+ // Prepare for a top-of-bone or bottom-of-bone joint
455
+ switch (jointType) {
456
+ case 'top':
457
+ // For top of bone (connects to child bone)
458
+ jointColor = options.jointColor || rigOptions.jointColor;
459
+ geometry = new THREE.SphereGeometry(radius, 16, 16);
460
+ opacity = 0.8;
461
+ break;
462
+ case 'bottom':
463
+ // For bottom of bone (connects to parent bone)
464
+ jointColor = options.jointColor || rigOptions.jointColor;
465
+ geometry = new THREE.SphereGeometry(radius, 16, 16);
466
+ opacity = 0.8;
467
+ break;
468
+ case 'root':
469
+ // Root bone puck
470
+ jointColor = 0xFF0000; // Red for root
471
+ geometry = new THREE.CylinderGeometry(radius * 1.2, radius * 1.2, radius * 0.5, 32);
472
+ opacity = 0.9;
473
+ break;
474
+ default:
475
+ // Default to a sphere
476
+ jointColor = options.jointColor || rigOptions.jointColor;
477
+ geometry = new THREE.SphereGeometry(radius, 16, 16);
478
+ opacity = 0.8;
479
+ }
480
+
481
+ // Create the material - make sure it matches our bone material settings
482
+ material = new THREE.MeshBasicMaterial({
483
+ color: jointColor,
484
+ side: THREE.DoubleSide,
485
+ wireframe: options.wireframe || rigOptions.wireframe,
486
+ transparent: true,
487
+ opacity: opacity
488
+ });
489
+
490
+ // Create the mesh
491
+ joint = new THREE.Mesh(geometry, material);
492
+ joint.userData.bonePart = 'cap';
493
+ joint.userData.jointType = jointType;
494
+
495
+ // For top/bottom joints, add bone reference
496
+ if (boneData) {
497
+ if (jointType === 'top') {
498
+ joint.userData.childBone = boneData.childBone;
499
+ } else if (jointType === 'bottom') {
500
+ joint.userData.parentBone = boneData.parentBone;
501
+ } else if (jointType === 'root') {
502
+ joint.userData.rootBone = boneData.parentBone;
503
+ }
504
+ }
505
+
506
+ return joint;
507
+ }
508
+
509
+ /**
510
+ * Create a bone mesh with joints
511
+ * @param {Object} parent - Parent THREE.Group to add the bone to
512
+ * @param {Number} radiusTop - Top radius of the bone
513
+ * @param {Number} radiusBottom - Bottom radius of the bone
514
+ * @param {Number} height - Height of the bone
515
+ * @param {Material} capMaterial - Material for bone caps
516
+ * @param {Material} sideMaterial - Material for bone sides
517
+ * @param {Material} alternateSideMaterial - Material for alternate sides
518
+ */
519
+ function createBoneMesh(parent, radiusTop, radiusBottom, height, capMaterial, sideMaterial, alternateSideMaterial) {
520
+ // First create a cylinder with 8 segments
521
+ const cylinderGeometry = new THREE.CylinderGeometry(radiusTop, radiusBottom, height, 8, 1, false);
522
+
523
+ // Create all sides in one group
524
+ const sidesGroup = new THREE.Group();
525
+ sidesGroup.position.y = height / 2;
526
+ parent.add(sidesGroup);
527
+
528
+ // Split the cylinder into 8 segments for alternating colors
529
+ for (let i = 0; i < 8; i++) {
530
+ // Create a segment
531
+ const segmentGeometry = new THREE.CylinderGeometry(
532
+ radiusTop, radiusBottom, height, 1, 1, false,
533
+ (Math.PI * 2 * i) / 8,
534
+ Math.PI * 2 / 8
535
+ );
536
+
537
+ // Use alternating materials based on segment index
538
+ const material = (i % 2 === 0) ? sideMaterial.clone() : alternateSideMaterial.clone();
539
+
540
+ // Create the segment mesh
541
+ const segment = new THREE.Mesh(segmentGeometry, material);
542
+ segment.userData.bonePart = 'side';
543
+ segment.userData.sideType = (i % 2 === 0) ? 'primary' : 'secondary';
544
+ segment.userData.segmentIndex = i;
545
+
546
+ sidesGroup.add(segment);
547
+ }
548
+
549
+ // Check if this is a root bone - either through parent/child data or name
550
+ let isRootBone = false;
551
+
552
+ // Check for root in parent data
553
+ if (parent.userData.childBone && parent.userData.childBone.name.toLowerCase().includes('root')) {
554
+ isRootBone = true;
555
+ }
556
+
557
+ // Check for root in parent data (parent bone)
558
+ if (parent.userData.parentBone && parent.userData.parentBone.name.toLowerCase().includes('root')) {
559
+ isRootBone = true;
560
+ }
561
+
562
+ // Create top joint
563
+ const jointType = isRootBone ? 'root' : 'regular';
564
+
565
+ // Create top joint using the new consolidated function
566
+ const topJoint = createBoneJoint(
567
+ jointType,
568
+ parent.userData,
569
+ radiusTop,
570
+ {
571
+ position: new THREE.Vector3(0, height, 0),
572
+ isTop: true
573
+ }
574
+ );
575
+ parent.add(topJoint);
576
+
577
+ // Create bottom joint using the new consolidated function
578
+ const bottomJoint = createBoneJoint(
579
+ jointType,
580
+ parent.userData,
581
+ radiusBottom,
582
+ {
583
+ position: new THREE.Vector3(0, 0, 0),
584
+ isTop: false
585
+ }
586
+ );
587
+ parent.add(bottomJoint);
588
+ }
589
+
590
+ /**
591
+ * Create a function to update bone visuals based on bone movement
592
+ * @param {Object} boneGroup - The visual bone group to update
593
+ * @returns {Function} Update function for the bone
594
+ */
595
+ function createBoneUpdateFunction(boneGroup) {
596
+ return () => {
597
+ if (boneGroup.userData.parentBone && boneGroup.userData.childBone) {
598
+ const parentPos = new THREE.Vector3();
599
+ const childPos = new THREE.Vector3();
600
+
601
+ boneGroup.userData.parentBone.getWorldPosition(parentPos);
602
+ boneGroup.userData.childBone.getWorldPosition(childPos);
603
+
604
+ // Update position and orientation
605
+ boneGroup.position.copy(parentPos);
606
+
607
+ // Make the bone look at the child
608
+ const direction = new THREE.Vector3().subVectors(childPos, parentPos);
609
+ if (direction.lengthSq() > 0.001) {
610
+ boneGroup.lookAt(childPos);
611
+ boneGroup.rotateX(Math.PI/2);
612
+
613
+ // Update scale to match new length
614
+ const distance = parentPos.distanceTo(childPos);
615
+
616
+ // Update the children
617
+ const children = boneGroup.children;
618
+ for (let i = 0; i < children.length; i++) {
619
+ if (children[i].userData.bonePart === 'side') {
620
+ children[i].scale.y = distance / children[i].geometry.parameters.height;
621
+ } else if (children[i].userData.bonePart === 'cap' && children[i].position.y > 0) {
622
+ children[i].position.y = distance;
623
+ }
624
+ }
625
+ }
626
+ }
627
+ };
628
+ }
@@ -0,0 +1,46 @@
1
+ import * as THREE from 'three';
2
+ import { rigOptions } from './rig-controller';
3
+ import { getIsDragging } from './rig-mouse-handler';
4
+
5
+ export let primaryRigHandle = null;
6
+
7
+ /**
8
+ * Add a control handle to the furthest bone
9
+ * @param {Object} targetObject - The bone to add the handle to
10
+ * @param {Object} scene - The Three.js scene
11
+ * @param {Number} modelScale - Scale factor for the handle size
12
+ */
13
+ export function addControlHandleToObject(targetObject, scene, modelScale) {
14
+ const handleSize = modelScale * 2.6;
15
+ const geometry = new THREE.SphereGeometry(handleSize, 16, 16);
16
+ const material = new THREE.MeshBasicMaterial({
17
+ color: rigOptions.normalColor,
18
+ transparent: true,
19
+ opacity: 0.7,
20
+ wireframe: false
21
+ });
22
+
23
+ primaryRigHandle = new THREE.Mesh(geometry, material);
24
+ primaryRigHandle.name = "PrimaryRigHandle";
25
+ scene.add(primaryRigHandle);
26
+
27
+ const bonePos = new THREE.Vector3();
28
+ targetObject.getWorldPosition(bonePos);
29
+ primaryRigHandle.position.copy(bonePos);
30
+ primaryRigHandle.userData.controlledBone = targetObject;
31
+ primaryRigHandle.userData.isControlHandle = true;
32
+ primaryRigHandle.userData.updatePosition = () => {
33
+ if (primaryRigHandle.userData.controlledBone && !getIsDragging()) {
34
+ const controlledBonePos = new THREE.Vector3();
35
+ primaryRigHandle.userData.controlledBone.getWorldPosition(controlledBonePos);
36
+ primaryRigHandle.position.copy(controlledBonePos);
37
+ }
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Clear the furthest bone handle reference
43
+ */
44
+ export function clearPrimaryRigHandle() {
45
+ primaryRigHandle = null;
46
+ }