@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.
- 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,377 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { rigOptions, getLabelGroup } from './rig-controller';
|
|
3
|
+
import { restoreLockedBoneRotations, updateBoneVisuals, moveBonesForTarget } from './bone-kinematics';
|
|
4
|
+
import { getState } from '../state/scene-state';
|
|
5
|
+
import { primaryRigHandle } from './rig-handle-factory';
|
|
6
|
+
|
|
7
|
+
// Raycaster for mouse interaction
|
|
8
|
+
let raycaster = new THREE.Raycaster();
|
|
9
|
+
let mouse = new THREE.Vector2();
|
|
10
|
+
let hoveredHandle = null;
|
|
11
|
+
let hoveredLabelHeader = null; // Track which label header is currently hovered
|
|
12
|
+
// Drag state tracking
|
|
13
|
+
let isDragging = false;
|
|
14
|
+
let dragStartPosition = new THREE.Vector3();
|
|
15
|
+
let dragPlane = new THREE.Plane();
|
|
16
|
+
let dragOffset = new THREE.Vector3();
|
|
17
|
+
let dragTarget = null;
|
|
18
|
+
let dragTargetPosition = new THREE.Vector3();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Set up mouse listeners for handle interaction
|
|
22
|
+
* @param {Object} scene - The Three.js scene
|
|
23
|
+
*/
|
|
24
|
+
export function setupMouseListeners(scene) {
|
|
25
|
+
const state = getState();
|
|
26
|
+
const renderer = state.renderer;
|
|
27
|
+
if (!renderer) return;
|
|
28
|
+
const domElement = renderer.domElement;
|
|
29
|
+
// Mouse move handler
|
|
30
|
+
domElement.addEventListener('mousemove', (event) => {
|
|
31
|
+
// Calculate mouse position in normalized device coordinates (-1 to +1)
|
|
32
|
+
const rect = domElement.getBoundingClientRect();
|
|
33
|
+
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
34
|
+
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
35
|
+
// Update raycaster with the new mouse position
|
|
36
|
+
if (state.camera) {
|
|
37
|
+
raycaster.setFromCamera(mouse, state.camera);
|
|
38
|
+
}
|
|
39
|
+
// Check for handle hover
|
|
40
|
+
checkHandleHover();
|
|
41
|
+
// Handle dragging
|
|
42
|
+
if (getIsDragging() && dragTarget) {
|
|
43
|
+
handleDrag();
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
// Mouse down handler
|
|
47
|
+
domElement.addEventListener('mousedown', (event) => {
|
|
48
|
+
if (event.button !== 0) return; // Only handle left mouse button
|
|
49
|
+
// Skip if Display Rig is not enabled
|
|
50
|
+
if (!rigOptions.displayRig) return;
|
|
51
|
+
const state = getState();
|
|
52
|
+
if (!state.camera) return;
|
|
53
|
+
|
|
54
|
+
// Since we're removing label header click handling, just check for control handle clicks
|
|
55
|
+
raycaster.setFromCamera(mouse, state.camera);
|
|
56
|
+
const intersects = raycaster.intersectObject(primaryRigHandle);
|
|
57
|
+
if (intersects.length > 0) {
|
|
58
|
+
console.log('Starting drag on handle:', primaryRigHandle.name);
|
|
59
|
+
startDrag(intersects[0], primaryRigHandle);
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
// Mouse up handler
|
|
64
|
+
domElement.addEventListener('mouseup', (event) => {
|
|
65
|
+
if (getIsDragging()) {
|
|
66
|
+
stopDrag();
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
// Mouse leave handler
|
|
71
|
+
domElement.addEventListener('mouseleave', (event) => {
|
|
72
|
+
if (getIsDragging()) {
|
|
73
|
+
stopDrag();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Reset any hover states when mouse leaves the canvas
|
|
77
|
+
resetHoveredStates();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Reset all hovered states when mouse leaves canvas or on other events
|
|
83
|
+
*/
|
|
84
|
+
function resetHoveredStates() {
|
|
85
|
+
// Reset control handle hover state
|
|
86
|
+
if (hoveredHandle && hoveredHandle.material) {
|
|
87
|
+
hoveredHandle.material.color.setHex(rigOptions.normalColor);
|
|
88
|
+
hoveredHandle.material.needsUpdate = true;
|
|
89
|
+
hoveredHandle = null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Reset label header hover state
|
|
93
|
+
if (hoveredLabelHeader && hoveredLabelHeader.material) {
|
|
94
|
+
hoveredLabelHeader.material.opacity = 0.8; // Default opacity
|
|
95
|
+
hoveredLabelHeader.material.needsUpdate = true;
|
|
96
|
+
hoveredLabelHeader = null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Reset mouse cursor and controls
|
|
100
|
+
document.body.style.cursor = 'auto';
|
|
101
|
+
const state = getState();
|
|
102
|
+
if (state.controls && !state.controls.enabled && !getIsDragging()) {
|
|
103
|
+
state.controls.enabled = true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get all label sprites from the scene
|
|
109
|
+
* @returns {Array} Array of label sprites
|
|
110
|
+
*/
|
|
111
|
+
function getAllLabels() {
|
|
112
|
+
const labels = [];
|
|
113
|
+
|
|
114
|
+
// Add joint labels if they exist
|
|
115
|
+
const jointLabelGroup = getLabelGroup('joint');
|
|
116
|
+
if (jointLabelGroup) {
|
|
117
|
+
jointLabelGroup.children.forEach(label => {
|
|
118
|
+
if (label.userData && (label.userData.isJointLabel || label.userData.isBoneLabel)) {
|
|
119
|
+
labels.push(label);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Add bone labels if they exist
|
|
125
|
+
const boneLabelGroup = getLabelGroup('bone');
|
|
126
|
+
if (boneLabelGroup) {
|
|
127
|
+
boneLabelGroup.children.forEach(label => {
|
|
128
|
+
if (label.userData && label.userData.isBoneLabel) {
|
|
129
|
+
labels.push(label);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return labels;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Check if mouse is hovering over the control handle or any label headers
|
|
139
|
+
*/
|
|
140
|
+
export function checkHandleHover() {
|
|
141
|
+
// Don't check for hover if rig display is disabled
|
|
142
|
+
if (!rigOptions.displayRig) return;
|
|
143
|
+
|
|
144
|
+
const state = getState();
|
|
145
|
+
const camera = state.camera;
|
|
146
|
+
const controls = state.controls; // Get orbit controls reference
|
|
147
|
+
if (!camera) return;
|
|
148
|
+
|
|
149
|
+
// Skip hover processing if we're dragging
|
|
150
|
+
if (getIsDragging()) return;
|
|
151
|
+
|
|
152
|
+
// Update the picking ray with the camera and mouse position
|
|
153
|
+
raycaster.setFromCamera(mouse, state.camera);
|
|
154
|
+
|
|
155
|
+
// Track if any hover was detected in this cycle
|
|
156
|
+
let hoverDetected = false;
|
|
157
|
+
|
|
158
|
+
// First check the furthest bone handle
|
|
159
|
+
if (primaryRigHandle) {
|
|
160
|
+
const handleIntersects = raycaster.intersectObject(primaryRigHandle);
|
|
161
|
+
|
|
162
|
+
if (handleIntersects.length > 0) {
|
|
163
|
+
// We hit the handle
|
|
164
|
+
hoverDetected = true;
|
|
165
|
+
|
|
166
|
+
// Set or update hover state
|
|
167
|
+
if (hoveredHandle !== primaryRigHandle) {
|
|
168
|
+
// Reset any previously hovered label header
|
|
169
|
+
if (hoveredLabelHeader) {
|
|
170
|
+
hoveredLabelHeader.material.opacity = 0.8; // Default opacity
|
|
171
|
+
hoveredLabelHeader.material.needsUpdate = true;
|
|
172
|
+
hoveredLabelHeader = null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Set the handle as hovered
|
|
176
|
+
hoveredHandle = primaryRigHandle;
|
|
177
|
+
hoveredHandle.material.color.setHex(rigOptions.hoverColor);
|
|
178
|
+
hoveredHandle.material.needsUpdate = true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Update cursor and controls
|
|
182
|
+
document.body.style.cursor = 'pointer';
|
|
183
|
+
if (controls && controls.enabled) {
|
|
184
|
+
controls.enabled = false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else if (hoveredHandle === primaryRigHandle) {
|
|
188
|
+
// We had the handle hovered but no longer
|
|
189
|
+
hoveredHandle.material.color.setHex(rigOptions.normalColor);
|
|
190
|
+
hoveredHandle.material.needsUpdate = true;
|
|
191
|
+
hoveredHandle = null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Now check label headers if nothing else is hovered
|
|
196
|
+
if (!hoverDetected) {
|
|
197
|
+
const allLabels = getAllLabels();
|
|
198
|
+
const labelIntersects = raycaster.intersectObjects(allLabels);
|
|
199
|
+
|
|
200
|
+
if (labelIntersects.length > 0) {
|
|
201
|
+
for (let i = 0; i < labelIntersects.length; i++) {
|
|
202
|
+
const label = labelIntersects[i].object;
|
|
203
|
+
|
|
204
|
+
// Skip if the label doesn't have header hover checking
|
|
205
|
+
if (!label.userData || !label.userData.checkHeaderHover) continue;
|
|
206
|
+
|
|
207
|
+
// Convert intersection point to local coordinates
|
|
208
|
+
const localPoint = label.worldToLocal(labelIntersects[i].point.clone());
|
|
209
|
+
|
|
210
|
+
// Check if over header area
|
|
211
|
+
if (label.userData.checkHeaderHover(localPoint)) {
|
|
212
|
+
// Hovering over a header
|
|
213
|
+
hoverDetected = true;
|
|
214
|
+
|
|
215
|
+
// Update hover state
|
|
216
|
+
if (hoveredLabelHeader !== label) {
|
|
217
|
+
// Reset previous hover states
|
|
218
|
+
if (hoveredHandle) {
|
|
219
|
+
hoveredHandle.material.color.setHex(rigOptions.normalColor);
|
|
220
|
+
hoveredHandle.material.needsUpdate = true;
|
|
221
|
+
hoveredHandle = null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (hoveredLabelHeader) {
|
|
225
|
+
hoveredLabelHeader.material.opacity = 0.8; // Default opacity
|
|
226
|
+
hoveredLabelHeader.material.needsUpdate = true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Set new hover state
|
|
230
|
+
hoveredLabelHeader = label;
|
|
231
|
+
hoveredLabelHeader.material.opacity = 1.0; // Full opacity on hover
|
|
232
|
+
hoveredLabelHeader.material.needsUpdate = true;
|
|
233
|
+
|
|
234
|
+
// Store the hover state
|
|
235
|
+
label.userData.isMouseOverHeader = true;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Update cursor and controls
|
|
239
|
+
document.body.style.cursor = 'pointer';
|
|
240
|
+
if (controls && controls.enabled) {
|
|
241
|
+
controls.enabled = false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
break; // Found a header hover, no need to check more
|
|
245
|
+
}
|
|
246
|
+
else if (hoveredLabelHeader === label) {
|
|
247
|
+
// We were hovering this label but now we're not over the header
|
|
248
|
+
hoveredLabelHeader.material.opacity = 0.8; // Reset to default opacity
|
|
249
|
+
hoveredLabelHeader.material.needsUpdate = true;
|
|
250
|
+
hoveredLabelHeader.userData.isMouseOverHeader = false;
|
|
251
|
+
hoveredLabelHeader = null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// If no hover detected anywhere, reset everything
|
|
258
|
+
if (!hoverDetected) {
|
|
259
|
+
// Reset control handle hover
|
|
260
|
+
if (hoveredHandle) {
|
|
261
|
+
hoveredHandle.material.color.setHex(rigOptions.normalColor);
|
|
262
|
+
hoveredHandle.material.needsUpdate = true;
|
|
263
|
+
hoveredHandle = null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Reset label header hover
|
|
267
|
+
if (hoveredLabelHeader) {
|
|
268
|
+
hoveredLabelHeader.material.opacity = 0.8; // Reset to default opacity
|
|
269
|
+
hoveredLabelHeader.material.needsUpdate = true;
|
|
270
|
+
hoveredLabelHeader.userData.isMouseOverHeader = false;
|
|
271
|
+
hoveredLabelHeader = null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Reset cursor and controls
|
|
275
|
+
document.body.style.cursor = 'auto';
|
|
276
|
+
if (controls && !controls.enabled) {
|
|
277
|
+
controls.enabled = true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Handle dragging logic
|
|
284
|
+
*/
|
|
285
|
+
function handleDrag() {
|
|
286
|
+
if (!isDragging || !dragTarget) return;
|
|
287
|
+
const state = getState();
|
|
288
|
+
// Get current intersection point with drag plane
|
|
289
|
+
const planeIntersection = new THREE.Vector3();
|
|
290
|
+
// Check if ray intersects plane
|
|
291
|
+
if (raycaster.ray.intersectPlane(dragPlane, planeIntersection)) {
|
|
292
|
+
// Apply the offset to maintain the grab point
|
|
293
|
+
planeIntersection.add(dragOffset);
|
|
294
|
+
// Move handle to new position
|
|
295
|
+
dragTarget.position.copy(planeIntersection);
|
|
296
|
+
// Apply IK if this is the furthest bone handle
|
|
297
|
+
if (dragTarget === primaryRigHandle && dragTarget.userData.controlledBone) {
|
|
298
|
+
const controlledBone = dragTarget.userData.controlledBone;
|
|
299
|
+
// Even if the controlled bone is locked, we still want to move other bones in the chain
|
|
300
|
+
// This is different from before - we don't check if the target bone is locked here
|
|
301
|
+
// Store current locked bone rotations
|
|
302
|
+
restoreLockedBoneRotations();
|
|
303
|
+
// Use the moveBonesForTarget function to handle IK chain properly
|
|
304
|
+
moveBonesForTarget(controlledBone, planeIntersection);
|
|
305
|
+
// Restore locked bone rotations again
|
|
306
|
+
restoreLockedBoneRotations();
|
|
307
|
+
// Force immediate update of visual bone meshes during drag
|
|
308
|
+
updateBoneVisuals();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Start dragging a control handle
|
|
315
|
+
* @param {Object} intersection - The intersection data from raycaster
|
|
316
|
+
* @param {Object} handle - The handle being dragged
|
|
317
|
+
*/
|
|
318
|
+
function startDrag(intersection, handle) {
|
|
319
|
+
// Ensure the handle exists
|
|
320
|
+
if (!handle) return;
|
|
321
|
+
// Set the dragging state to true
|
|
322
|
+
setIsDragging(true);
|
|
323
|
+
dragTarget = handle;
|
|
324
|
+
// Update material to active color
|
|
325
|
+
if (handle.material) {
|
|
326
|
+
handle.material.color.setHex(rigOptions.activeColor);
|
|
327
|
+
handle.material.needsUpdate = true;
|
|
328
|
+
}
|
|
329
|
+
// Store the initial position
|
|
330
|
+
dragTargetPosition.copy(handle.position);
|
|
331
|
+
// Get the state
|
|
332
|
+
const state = getState();
|
|
333
|
+
// Create a drag plane perpendicular to the camera
|
|
334
|
+
const planeNormal = new THREE.Vector3(0, 0, 1).applyQuaternion(state.camera.quaternion);
|
|
335
|
+
dragPlane.setFromNormalAndCoplanarPoint(planeNormal, dragTargetPosition);
|
|
336
|
+
// Calculate offset for precise dragging
|
|
337
|
+
const dragIntersectionPoint = new THREE.Vector3();
|
|
338
|
+
raycaster.ray.intersectPlane(dragPlane, dragIntersectionPoint);
|
|
339
|
+
dragOffset.subVectors(dragTargetPosition, dragIntersectionPoint);
|
|
340
|
+
console.log('Drag started at', dragTargetPosition);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Stop dragging operation
|
|
345
|
+
*/
|
|
346
|
+
function stopDrag() {
|
|
347
|
+
if (!getIsDragging() || !dragTarget) return;
|
|
348
|
+
setIsDragging(false);
|
|
349
|
+
// Reset material to normal or hover color based on current state
|
|
350
|
+
if (dragTarget.material) {
|
|
351
|
+
const isHovered = dragTarget === hoveredHandle;
|
|
352
|
+
dragTarget.material.color.setHex(isHovered ? rigOptions.hoverColor : rigOptions.normalColor);
|
|
353
|
+
dragTarget.material.needsUpdate = true;
|
|
354
|
+
}
|
|
355
|
+
// Re-enable orbit controls
|
|
356
|
+
const state = getState();
|
|
357
|
+
if (state.controls && !state.controls.enabled) {
|
|
358
|
+
state.controls.enabled = true;
|
|
359
|
+
document.body.style.cursor = 'auto';
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Update the isDragging state
|
|
365
|
+
* @param {Boolean} dragging - The new dragging state
|
|
366
|
+
*/
|
|
367
|
+
export function setIsDragging(dragging) {
|
|
368
|
+
isDragging = dragging;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Returns the current dragging state
|
|
373
|
+
* @returns {Boolean} The current dragging state
|
|
374
|
+
*/
|
|
375
|
+
export function getIsDragging() {
|
|
376
|
+
return isDragging;
|
|
377
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { bones } from './bone-kinematics';
|
|
3
|
+
import { jointPreviousValues } from '../../panels/asset-panel/rig-heading/rig-heading';
|
|
4
|
+
import { disableApplyButton, enableApplyButton } from './rig-ui-factory';
|
|
5
|
+
|
|
6
|
+
// Add debug flag
|
|
7
|
+
export let jointSettingsDebug = true;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Store current bone world positions and rotations
|
|
11
|
+
* @returns {Map} Map of bone states keyed by bone name
|
|
12
|
+
*/
|
|
13
|
+
export function storeBoneCurrentState() {
|
|
14
|
+
const boneCurrentState = new Map();
|
|
15
|
+
|
|
16
|
+
bones.forEach(bone => {
|
|
17
|
+
if (bone) {
|
|
18
|
+
bone.updateWorldMatrix(true, false);
|
|
19
|
+
const worldPosition = new THREE.Vector3();
|
|
20
|
+
const worldQuaternion = new THREE.Quaternion();
|
|
21
|
+
bone.getWorldPosition(worldPosition);
|
|
22
|
+
bone.getWorldQuaternion(worldQuaternion);
|
|
23
|
+
|
|
24
|
+
boneCurrentState.set(bone.name, {
|
|
25
|
+
position: worldPosition.clone(),
|
|
26
|
+
quaternion: worldQuaternion.clone()
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return boneCurrentState;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Update previous values for all constraint dropdowns
|
|
36
|
+
* @param {NodeList} constraintSelects - List of constraint select elements
|
|
37
|
+
*/
|
|
38
|
+
export function updatePreviousValues(constraintSelects) {
|
|
39
|
+
constraintSelects.forEach(select => {
|
|
40
|
+
const boneName = select.getAttribute('data-bone-name');
|
|
41
|
+
jointPreviousValues.set(boneName, select.value);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Update the state of all bone constraint settings
|
|
47
|
+
*/
|
|
48
|
+
export function updateConstraintSettingsState() {
|
|
49
|
+
const constraintSelects = document.querySelectorAll('select[data-bone-constraint]');
|
|
50
|
+
let allConstraintsInPreviousState = true;
|
|
51
|
+
|
|
52
|
+
constraintSelects.forEach(select => {
|
|
53
|
+
const boneName = select.getAttribute('data-bone-name');
|
|
54
|
+
const previousValue = jointPreviousValues.get(boneName);
|
|
55
|
+
const currentValue = select.value;
|
|
56
|
+
|
|
57
|
+
if (previousValue !== currentValue) {
|
|
58
|
+
allConstraintsInPreviousState = false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Also check constraint parameters (for advanced constraints)
|
|
62
|
+
if (currentValue === 'SINGLE_AXIS_ROTATION') {
|
|
63
|
+
// Check hinge parameters
|
|
64
|
+
const itemElem = select.closest('.rig-item');
|
|
65
|
+
if (itemElem) {
|
|
66
|
+
const minInput = itemElem.querySelector('.rig-min-input');
|
|
67
|
+
const maxInput = itemElem.querySelector('.rig-max-input');
|
|
68
|
+
const axisSelect = itemElem.querySelector('.rig-axis-select');
|
|
69
|
+
|
|
70
|
+
if (minInput && maxInput && axisSelect) {
|
|
71
|
+
// Get stored values (default if not stored)
|
|
72
|
+
const storedConfig = jointPreviousValues.get(`${boneName}:hinge-config`);
|
|
73
|
+
|
|
74
|
+
// If no stored config, this is the first time, so store current values
|
|
75
|
+
if (!storedConfig) {
|
|
76
|
+
jointPreviousValues.set(`${boneName}:hinge-config`, {
|
|
77
|
+
axis: axisSelect.value,
|
|
78
|
+
min: parseInt(minInput.value),
|
|
79
|
+
max: parseInt(maxInput.value)
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
// Check if current values match stored values
|
|
83
|
+
if (storedConfig.axis !== axisSelect.value ||
|
|
84
|
+
storedConfig.min !== parseInt(minInput.value) ||
|
|
85
|
+
storedConfig.max !== parseInt(maxInput.value)) {
|
|
86
|
+
allConstraintsInPreviousState = false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} else if (currentValue === 'LIMIT_ROTATION_XYZ') {
|
|
92
|
+
// Check rotation limits
|
|
93
|
+
const itemElem = select.closest('.rig-item');
|
|
94
|
+
if (itemElem) {
|
|
95
|
+
const rotLimitContainers = itemElem.querySelectorAll('.rig-axis-limits');
|
|
96
|
+
const storedConfig = jointPreviousValues.get(`${boneName}:rotation-limits`);
|
|
97
|
+
|
|
98
|
+
// Create a new config object from current values
|
|
99
|
+
const currentConfig = { x: {}, y: {}, z: {} };
|
|
100
|
+
let hasChanges = false;
|
|
101
|
+
|
|
102
|
+
rotLimitContainers.forEach((container, index) => {
|
|
103
|
+
const axis = ['x', 'y', 'z'][index];
|
|
104
|
+
const minInput = container.querySelector('.rig-min-limit input');
|
|
105
|
+
const maxInput = container.querySelector('.rig-max-limit input');
|
|
106
|
+
|
|
107
|
+
if (minInput && maxInput) {
|
|
108
|
+
currentConfig[axis].min = parseInt(minInput.value);
|
|
109
|
+
currentConfig[axis].max = parseInt(maxInput.value);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// If no stored config, store current values
|
|
114
|
+
if (!storedConfig) {
|
|
115
|
+
jointPreviousValues.set(`${boneName}:rotation-limits`, JSON.parse(JSON.stringify(currentConfig)));
|
|
116
|
+
} else {
|
|
117
|
+
// Check each axis for changes
|
|
118
|
+
['x', 'y', 'z'].forEach(axis => {
|
|
119
|
+
if (storedConfig[axis]?.min !== currentConfig[axis]?.min ||
|
|
120
|
+
storedConfig[axis]?.max !== currentConfig[axis]?.max) {
|
|
121
|
+
hasChanges = true;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (hasChanges) {
|
|
126
|
+
allConstraintsInPreviousState = false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} else if (currentValue === 'DYNAMIC_SPRING') {
|
|
131
|
+
// Check spring parameters
|
|
132
|
+
const itemElem = select.closest('.rig-item');
|
|
133
|
+
if (itemElem) {
|
|
134
|
+
const stiffnessInput = itemElem.querySelector('.rig-stiffness-input');
|
|
135
|
+
const dampingInput = itemElem.querySelector('.rig-damping-input');
|
|
136
|
+
const gravityInput = itemElem.querySelector('.rig-gravity-input');
|
|
137
|
+
|
|
138
|
+
// Get stored values
|
|
139
|
+
const storedConfig = jointPreviousValues.get(`${boneName}:spring-config`);
|
|
140
|
+
|
|
141
|
+
// If no stored config, store current values
|
|
142
|
+
if (!storedConfig && stiffnessInput && dampingInput && gravityInput) {
|
|
143
|
+
jointPreviousValues.set(`${boneName}:spring-config`, {
|
|
144
|
+
stiffness: parseInt(stiffnessInput.value),
|
|
145
|
+
damping: parseInt(dampingInput.value),
|
|
146
|
+
gravity: parseFloat(gravityInput.value)
|
|
147
|
+
});
|
|
148
|
+
} else if (stiffnessInput && dampingInput && gravityInput) {
|
|
149
|
+
// Check if current values match stored values
|
|
150
|
+
if (storedConfig.stiffness !== parseInt(stiffnessInput.value) ||
|
|
151
|
+
storedConfig.damping !== parseInt(dampingInput.value) ||
|
|
152
|
+
storedConfig.gravity !== parseFloat(gravityInput.value)) {
|
|
153
|
+
allConstraintsInPreviousState = false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (jointSettingsDebug) {
|
|
161
|
+
console.log(`All Bone Constraints in previous state: ${allConstraintsInPreviousState}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Control Apply Changes button state based on changes
|
|
165
|
+
const applyButton = document.getElementById('apply-bone-constraints-button');
|
|
166
|
+
if (applyButton) {
|
|
167
|
+
if (allConstraintsInPreviousState) {
|
|
168
|
+
disableApplyButton(applyButton);
|
|
169
|
+
} else {
|
|
170
|
+
enableApplyButton(applyButton);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return allConstraintsInPreviousState;
|
|
175
|
+
}
|