@littlecarlito/blorktools 0.50.4 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +69 -0
- package/package.json +13 -7
- package/src/asset_debugger/axis-indicator/axis-indicator.css +6 -0
- package/src/asset_debugger/axis-indicator/axis-indicator.html +20 -0
- package/src/asset_debugger/axis-indicator/axis-indicator.js +822 -0
- package/src/asset_debugger/debugger-scene/debugger-scene.css +142 -0
- package/src/asset_debugger/debugger-scene/debugger-scene.html +80 -0
- package/src/asset_debugger/debugger-scene/debugger-scene.js +791 -0
- package/src/asset_debugger/header/header.css +73 -0
- package/src/asset_debugger/header/header.html +24 -0
- package/src/asset_debugger/header/header.js +224 -0
- package/src/asset_debugger/index.html +76 -0
- package/src/asset_debugger/landing-page/landing-page.css +396 -0
- package/src/asset_debugger/landing-page/landing-page.html +81 -0
- package/src/asset_debugger/landing-page/landing-page.js +611 -0
- package/src/asset_debugger/loading-splash/loading-splash.css +195 -0
- package/src/asset_debugger/loading-splash/loading-splash.html +22 -0
- package/src/asset_debugger/loading-splash/loading-splash.js +59 -0
- package/src/asset_debugger/loading-splash/preview-loading-splash.js +66 -0
- package/src/asset_debugger/main.css +14 -0
- package/src/asset_debugger/modals/examples-modal/examples-modal.css +41 -0
- package/src/asset_debugger/modals/examples-modal/examples-modal.html +18 -0
- package/src/asset_debugger/modals/examples-modal/examples-modal.js +111 -0
- package/src/asset_debugger/modals/examples-modal/examples.js +125 -0
- package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.css +452 -0
- package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.html +87 -0
- package/src/asset_debugger/modals/html-editor-modal/html-editor-modal.js +675 -0
- package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.css +219 -0
- package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.html +20 -0
- package/src/asset_debugger/modals/mesh-info-modal/mesh-info-modal.js +548 -0
- package/src/asset_debugger/modals/settings-modal/settings-modal.css +103 -0
- package/src/asset_debugger/modals/settings-modal/settings-modal.html +158 -0
- package/src/asset_debugger/modals/settings-modal/settings-modal.js +475 -0
- package/src/asset_debugger/panels/asset-panel/asset-panel.css +263 -0
- package/src/asset_debugger/panels/asset-panel/asset-panel.html +123 -0
- package/src/asset_debugger/panels/asset-panel/asset-panel.js +136 -0
- package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.css +94 -0
- package/src/asset_debugger/panels/asset-panel/atlas-heading/atlas-heading.js +312 -0
- package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.css +129 -0
- package/src/asset_debugger/panels/asset-panel/mesh-heading/mesh-heading.js +486 -0
- package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.css +545 -0
- package/src/asset_debugger/panels/asset-panel/rig-heading/rig-heading.js +538 -0
- package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.css +70 -0
- package/src/asset_debugger/panels/asset-panel/uv-heading/uv-heading.js +586 -0
- package/src/asset_debugger/panels/world-panel/world-panel.css +364 -0
- package/src/asset_debugger/panels/world-panel/world-panel.html +173 -0
- package/src/asset_debugger/panels/world-panel/world-panel.js +1891 -0
- package/src/asset_debugger/router.js +190 -0
- package/src/asset_debugger/util/animation/playback/animation-playback-controller.js +150 -0
- package/src/asset_debugger/util/animation/playback/animation-preview-controller.js +316 -0
- package/src/asset_debugger/util/animation/playback/css3d-bounce-controller.js +400 -0
- package/src/asset_debugger/util/animation/playback/css3d-reversal-controller.js +821 -0
- package/src/asset_debugger/util/animation/render/css3d-prerender-controller.js +696 -0
- package/src/asset_debugger/util/animation/render/debug-texture-factory.js +0 -0
- package/src/asset_debugger/util/animation/render/iframe2texture-render-controller.js +199 -0
- package/src/asset_debugger/util/animation/render/image2texture-prerender-controller.js +461 -0
- package/src/asset_debugger/util/animation/render/pbr-material-factory.js +82 -0
- package/src/asset_debugger/util/common.css +280 -0
- package/src/asset_debugger/util/data/animation-classifier.js +323 -0
- package/src/asset_debugger/util/data/duplicate-handler.js +20 -0
- package/src/asset_debugger/util/data/glb-buffer-manager.js +407 -0
- package/src/asset_debugger/util/data/glb-classifier.js +290 -0
- package/src/asset_debugger/util/data/html-formatter.js +76 -0
- package/src/asset_debugger/util/data/html-linter.js +276 -0
- package/src/asset_debugger/util/data/localstorage-manager.js +265 -0
- package/src/asset_debugger/util/data/mesh-html-manager.js +295 -0
- package/src/asset_debugger/util/data/string-serder.js +303 -0
- package/src/asset_debugger/util/data/texture-classifier.js +663 -0
- package/src/asset_debugger/util/data/upload/background-file-handler.js +292 -0
- package/src/asset_debugger/util/data/upload/dropzone-preview-controller.js +396 -0
- package/src/asset_debugger/util/data/upload/file-upload-manager.js +495 -0
- package/src/asset_debugger/util/data/upload/glb-file-handler.js +36 -0
- package/src/asset_debugger/util/data/upload/glb-preview-controller.js +317 -0
- package/src/asset_debugger/util/data/upload/lighting-file-handler.js +194 -0
- package/src/asset_debugger/util/data/upload/model-file-manager.js +104 -0
- package/src/asset_debugger/util/data/upload/texture-file-handler.js +166 -0
- package/src/asset_debugger/util/data/upload/zip-handler.js +686 -0
- package/src/asset_debugger/util/loaders/html2canvas-loader.js +107 -0
- package/src/asset_debugger/util/rig/bone-kinematics.js +403 -0
- package/src/asset_debugger/util/rig/rig-constraint-manager.js +618 -0
- package/src/asset_debugger/util/rig/rig-controller.js +612 -0
- package/src/asset_debugger/util/rig/rig-factory.js +628 -0
- package/src/asset_debugger/util/rig/rig-handle-factory.js +46 -0
- package/src/asset_debugger/util/rig/rig-label-factory.js +441 -0
- package/src/asset_debugger/util/rig/rig-mouse-handler.js +377 -0
- package/src/asset_debugger/util/rig/rig-state-manager.js +175 -0
- package/src/asset_debugger/util/rig/rig-tooltip-manager.js +267 -0
- package/src/asset_debugger/util/rig/rig-ui-factory.js +700 -0
- package/src/asset_debugger/util/scene/background-manager.js +284 -0
- package/src/asset_debugger/util/scene/camera-controller.js +243 -0
- package/src/asset_debugger/util/scene/css3d-debug-controller.js +406 -0
- package/src/asset_debugger/util/scene/css3d-frame-factory.js +113 -0
- package/src/asset_debugger/util/scene/css3d-scene-manager.js +529 -0
- package/src/asset_debugger/util/scene/glb-controller.js +208 -0
- package/src/asset_debugger/util/scene/lighting-manager.js +690 -0
- package/src/asset_debugger/util/scene/threejs-model-manager.js +437 -0
- package/src/asset_debugger/util/scene/threejs-preview-manager.js +207 -0
- package/src/asset_debugger/util/scene/threejs-preview-setup.js +478 -0
- package/src/asset_debugger/util/scene/threejs-scene-controller.js +286 -0
- package/src/asset_debugger/util/scene/ui-manager.js +107 -0
- package/src/asset_debugger/util/state/animation-state.js +128 -0
- package/src/asset_debugger/util/state/css3d-state.js +83 -0
- package/src/asset_debugger/util/state/glb-preview-state.js +31 -0
- package/src/asset_debugger/util/state/log-util.js +197 -0
- package/src/asset_debugger/util/state/scene-state.js +452 -0
- package/src/asset_debugger/util/state/threejs-state.js +54 -0
- package/src/asset_debugger/util/workers/lighting-worker.js +61 -0
- package/src/asset_debugger/util/workers/model-worker.js +109 -0
- package/src/asset_debugger/util/workers/texture-worker.js +54 -0
- package/src/asset_debugger/util/workers/worker-manager.js +212 -0
- package/src/asset_debugger/widgets/mesh-info-widget.js +280 -0
- package/src/index.html +261 -0
- package/src/index.js +8 -0
- package/vite.config.js +66 -0
|
@@ -0,0 +1,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
|
+
}
|