@fps-games/editor 0.1.1-beta.0 → 0.1.1-beta.2
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/dist/local-editor-harness.d.ts +30 -1
- package/dist/local-editor-harness.d.ts.map +1 -1
- package/dist/local-editor-harness.js +430 -11
- package/dist/local-editor-harness.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-input-controller.d.ts +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-input-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-input-controller.js +4 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-input-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.d.ts +22 -2
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.js +469 -90
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/types.d.ts +4 -0
- package/node_modules/@fps-games/editor-babylon/dist/types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/package.json +4 -4
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.d.ts +9 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js +77 -7
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.js +4 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +25 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js +227 -27
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/package.json +4 -1
- package/node_modules/@fps-games/editor-core/dist/editor-session.d.ts +4 -1
- package/node_modules/@fps-games/editor-core/dist/editor-session.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/editor-session.js +13 -3
- package/node_modules/@fps-games/editor-core/dist/editor-session.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/index.d.ts +1 -0
- package/node_modules/@fps-games/editor-core/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/index.js +1 -0
- package/node_modules/@fps-games/editor-core/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/scene-view-input.d.ts +1 -1
- package/node_modules/@fps-games/editor-core/dist/scene-view-input.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/transform-gizmo.d.ts +78 -2
- package/node_modules/@fps-games/editor-core/dist/transform-gizmo.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/transform-gizmo.js +81 -1
- package/node_modules/@fps-games/editor-core/dist/transform-gizmo.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/transform-operations.d.ts +32 -0
- package/node_modules/@fps-games/editor-core/dist/transform-operations.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-core/dist/transform-operations.js +125 -0
- package/node_modules/@fps-games/editor-core/dist/transform-operations.js.map +1 -0
- package/node_modules/@fps-games/editor-core/package.json +2 -2
- package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
- package/node_modules/@fps-games/editor-protocol/package.json +1 -1
- package/package.json +6 -6
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
import { DEFAULT_EDITOR_TRANSFORM_OPERATION_SETTINGS, normalizeEditorTransformConstraint, snapEditorTransformSnapshot, } from '@fps-games/editor-core';
|
|
2
|
+
export function resolveBabylonTransformHandleConstraint(tool, handleKey) {
|
|
3
|
+
if (tool === 'move') {
|
|
4
|
+
if (handleKey === 'xPlaneGizmo' || handleKey === 'yPlaneGizmo' || handleKey === 'zPlaneGizmo')
|
|
5
|
+
return 'plane';
|
|
6
|
+
if (handleKey === 'xGizmo' || handleKey === 'yGizmo' || handleKey === 'zGizmo')
|
|
7
|
+
return 'axis';
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
if (tool === 'rotate') {
|
|
11
|
+
return handleKey === 'xGizmo' || handleKey === 'yGizmo' || handleKey === 'zGizmo' ? 'axis' : null;
|
|
12
|
+
}
|
|
13
|
+
if (tool === 'scale') {
|
|
14
|
+
if (handleKey === 'uniformScaleGizmo')
|
|
15
|
+
return 'uniform';
|
|
16
|
+
return handleKey === 'xGizmo' || handleKey === 'yGizmo' || handleKey === 'zGizmo' ? 'axis' : null;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
1
20
|
export function createBabylonTransformGizmoController(options) {
|
|
2
21
|
const GizmoManager = options.babylon.GizmoManager;
|
|
3
22
|
if (!GizmoManager)
|
|
@@ -8,13 +27,19 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
8
27
|
manager.boundingBoxGizmoEnabled = false;
|
|
9
28
|
let tool = options.initialTool ?? 'select';
|
|
10
29
|
let space = options.initialSpace ?? 'world';
|
|
11
|
-
let constraint = 'axis';
|
|
30
|
+
let constraint = normalizeConstraintForTool(tool, 'axis') ?? 'axis';
|
|
12
31
|
let selectedNodeId = null;
|
|
13
32
|
let selectedNodeIds = [];
|
|
14
33
|
let activeDrag = null;
|
|
15
34
|
let disposed = false;
|
|
35
|
+
let operationSettings = cloneOperationSettings(DEFAULT_EDITOR_TRANSFORM_OPERATION_SETTINGS);
|
|
36
|
+
let pendingDuplicateDrag = false;
|
|
16
37
|
let observerDisposers = [];
|
|
17
38
|
let pivotProxy = null;
|
|
39
|
+
let freeMoveHandle = null;
|
|
40
|
+
let freeMoveHandleMaterial = null;
|
|
41
|
+
let placementMarker = null;
|
|
42
|
+
let placementMarkerMaterial = null;
|
|
18
43
|
const canvas = options.scene.getEngine?.().getRenderingCanvas?.();
|
|
19
44
|
function activeTransformTool() {
|
|
20
45
|
return tool === 'select' ? null : tool;
|
|
@@ -29,6 +54,14 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
29
54
|
return [];
|
|
30
55
|
return selectedNodeIds.filter(nodeId => !!options.projection.getAttachableRoot(nodeId));
|
|
31
56
|
}
|
|
57
|
+
function getCurrentTransformTargetIds() {
|
|
58
|
+
return selectedNodeIds.length > 1
|
|
59
|
+
? getBatchTransformTargetIds()
|
|
60
|
+
: [getActiveTargetId()].filter((nodeId) => !!nodeId);
|
|
61
|
+
}
|
|
62
|
+
function normalizeConstraintForTool(nextTool, nextConstraint) {
|
|
63
|
+
return normalizeEditorTransformConstraint(nextTool, nextConstraint);
|
|
64
|
+
}
|
|
32
65
|
function ensurePivotProxy() {
|
|
33
66
|
if (pivotProxy)
|
|
34
67
|
return pivotProxy;
|
|
@@ -42,13 +75,13 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
42
75
|
};
|
|
43
76
|
return pivotProxy;
|
|
44
77
|
}
|
|
45
|
-
function setPivotProxyTransform(pivot) {
|
|
78
|
+
function setPivotProxyTransform(pivot, rotation = { x: 0, y: 0, z: 0 }) {
|
|
46
79
|
const proxy = ensurePivotProxy();
|
|
47
80
|
const Vector3 = options.babylon.Vector3;
|
|
48
81
|
if (!proxy || !Vector3)
|
|
49
82
|
return null;
|
|
50
83
|
proxy.position = new Vector3(pivot.position.x, pivot.position.y, pivot.position.z);
|
|
51
|
-
proxy.rotation = new Vector3(
|
|
84
|
+
proxy.rotation = new Vector3(rotation.x, rotation.y, rotation.z);
|
|
52
85
|
proxy.scaling = new Vector3(1, 1, 1);
|
|
53
86
|
return proxy;
|
|
54
87
|
}
|
|
@@ -68,28 +101,31 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
68
101
|
catch { }
|
|
69
102
|
});
|
|
70
103
|
}
|
|
71
|
-
function registerDragObserversFor(gizmo) {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
104
|
+
function registerDragObserversFor(activeTool, gizmo) {
|
|
105
|
+
const handleKeys = [
|
|
106
|
+
'xGizmo',
|
|
107
|
+
'yGizmo',
|
|
108
|
+
'zGizmo',
|
|
109
|
+
'uniformScaleGizmo',
|
|
110
|
+
'xPlaneGizmo',
|
|
111
|
+
'yPlaneGizmo',
|
|
112
|
+
'zPlaneGizmo',
|
|
80
113
|
];
|
|
81
|
-
for (const
|
|
82
|
-
const
|
|
83
|
-
|
|
114
|
+
for (const handleKey of handleKeys) {
|
|
115
|
+
const activeConstraint = resolveBabylonTransformHandleConstraint(activeTool, handleKey);
|
|
116
|
+
if (!activeConstraint)
|
|
117
|
+
continue;
|
|
118
|
+
const behavior = gizmo?.[handleKey]?.dragBehavior;
|
|
119
|
+
addObserver(behavior?.onDragStartObservable, () => beginDrag(activeConstraint));
|
|
84
120
|
addObserver(behavior?.onDragObservable, updateDrag);
|
|
85
121
|
addObserver(behavior?.onDragEndObservable, endDrag);
|
|
86
122
|
}
|
|
87
123
|
}
|
|
88
124
|
function registerDragObservers() {
|
|
89
125
|
clearDragObservers();
|
|
90
|
-
registerDragObserversFor(manager.gizmos?.positionGizmo);
|
|
91
|
-
registerDragObserversFor(manager.gizmos?.rotationGizmo);
|
|
92
|
-
registerDragObserversFor(manager.gizmos?.scaleGizmo);
|
|
126
|
+
registerDragObserversFor('move', manager.gizmos?.positionGizmo);
|
|
127
|
+
registerDragObserversFor('rotate', manager.gizmos?.rotationGizmo);
|
|
128
|
+
registerDragObserversFor('scale', manager.gizmos?.scaleGizmo);
|
|
93
129
|
}
|
|
94
130
|
function applySpacePreference() {
|
|
95
131
|
const matchAttachedMesh = space === 'local';
|
|
@@ -105,7 +141,15 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
105
141
|
}
|
|
106
142
|
}
|
|
107
143
|
catch { }
|
|
108
|
-
for (const axis of [
|
|
144
|
+
for (const axis of [
|
|
145
|
+
gizmo?.xGizmo,
|
|
146
|
+
gizmo?.yGizmo,
|
|
147
|
+
gizmo?.zGizmo,
|
|
148
|
+
gizmo?.xPlaneGizmo,
|
|
149
|
+
gizmo?.yPlaneGizmo,
|
|
150
|
+
gizmo?.zPlaneGizmo,
|
|
151
|
+
gizmo?.uniformScaleGizmo,
|
|
152
|
+
]) {
|
|
109
153
|
try {
|
|
110
154
|
if ('updateGizmoRotationToMatchAttachedMesh' in (axis ?? {})) {
|
|
111
155
|
axis.updateGizmoRotationToMatchAttachedMesh = matchAttachedMesh;
|
|
@@ -115,25 +159,26 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
115
159
|
}
|
|
116
160
|
}
|
|
117
161
|
}
|
|
162
|
+
function applyHandlePreference() {
|
|
163
|
+
const positionGizmo = manager.gizmos?.positionGizmo;
|
|
164
|
+
if (positionGizmo) {
|
|
165
|
+
try {
|
|
166
|
+
positionGizmo.planarGizmoEnabled = tool === 'move';
|
|
167
|
+
}
|
|
168
|
+
catch { }
|
|
169
|
+
}
|
|
170
|
+
}
|
|
118
171
|
function attachCurrentSelection() {
|
|
119
172
|
const activeTool = activeTransformTool();
|
|
120
|
-
|
|
121
|
-
manager.positionGizmoEnabled = activeTool === 'move' && !viewPlaneMove;
|
|
173
|
+
manager.positionGizmoEnabled = activeTool === 'move';
|
|
122
174
|
manager.rotationGizmoEnabled = activeTool === 'rotate';
|
|
123
175
|
manager.scaleGizmoEnabled = activeTool === 'scale';
|
|
176
|
+
applyHandlePreference();
|
|
124
177
|
applySpacePreference();
|
|
125
178
|
registerDragObservers();
|
|
126
179
|
let target = null;
|
|
127
|
-
if (activeTool
|
|
128
|
-
|
|
129
|
-
if (batchTransformTargetIds.length > 1) {
|
|
130
|
-
const pivot = options.projection.getSelectionPivot(batchTransformTargetIds);
|
|
131
|
-
target = pivot ? setPivotProxyTransform(pivot) : null;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
const activeTargetId = getActiveTargetId();
|
|
135
|
-
target = activeTargetId ? options.projection.getAttachableRoot(activeTargetId) : null;
|
|
136
|
-
}
|
|
180
|
+
if (activeTool) {
|
|
181
|
+
target = attachNativeTransformProxy(getCurrentTransformTargetIds());
|
|
137
182
|
}
|
|
138
183
|
try {
|
|
139
184
|
manager.attachToNode?.(target ?? null);
|
|
@@ -141,23 +186,86 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
141
186
|
catch (error) {
|
|
142
187
|
options.logger?.warn?.('[BabylonTransformGizmoController] failed to attach gizmo', error);
|
|
143
188
|
}
|
|
189
|
+
updateFreeMoveHandle();
|
|
144
190
|
}
|
|
145
|
-
function beginDrag() {
|
|
191
|
+
function beginDrag(activeConstraint = constraint) {
|
|
146
192
|
if (activeDrag)
|
|
147
193
|
return;
|
|
148
194
|
const activeTool = activeTransformTool();
|
|
149
195
|
if (!activeTool)
|
|
150
196
|
return;
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const drag = createActiveDrag(activeTool, targetIds,
|
|
197
|
+
const duplicate = pendingDuplicateDrag;
|
|
198
|
+
pendingDuplicateDrag = false;
|
|
199
|
+
const targetIds = resolveDragTargetIds(activeTool, getCurrentTransformTargetIds(), activeConstraint, duplicate);
|
|
200
|
+
const drag = createActiveDrag(activeTool, targetIds, activeConstraint, duplicate);
|
|
155
201
|
if (!drag)
|
|
156
202
|
return;
|
|
157
203
|
activeDrag = drag;
|
|
158
204
|
options.onDragStart?.(activeDrag);
|
|
159
205
|
}
|
|
160
|
-
function
|
|
206
|
+
function resolveDragTargetIds(activeTool, targetIds, activeConstraint, duplicate) {
|
|
207
|
+
if (!duplicate || targetIds.length === 0)
|
|
208
|
+
return targetIds;
|
|
209
|
+
const beforeTransforms = options.projection.readNodeTransforms(targetIds);
|
|
210
|
+
const validTargetIds = targetIds.filter(nodeId => !!beforeTransforms[nodeId]);
|
|
211
|
+
if (validTargetIds.length === 0)
|
|
212
|
+
return [];
|
|
213
|
+
const duplicateResult = options.onDuplicateDragStart?.({
|
|
214
|
+
targetIds: validTargetIds,
|
|
215
|
+
activeId: selectedNodeId && validTargetIds.includes(selectedNodeId) ? selectedNodeId : validTargetIds[validTargetIds.length - 1] ?? null,
|
|
216
|
+
tool: activeTool,
|
|
217
|
+
space,
|
|
218
|
+
constraint: activeConstraint,
|
|
219
|
+
beforeTransforms,
|
|
220
|
+
});
|
|
221
|
+
if (!duplicateResult?.targetIds.length)
|
|
222
|
+
return [];
|
|
223
|
+
selectedNodeIds = [...duplicateResult.targetIds];
|
|
224
|
+
selectedNodeId = duplicateResult.activeId && selectedNodeIds.includes(duplicateResult.activeId)
|
|
225
|
+
? duplicateResult.activeId
|
|
226
|
+
: selectedNodeIds[selectedNodeIds.length - 1] ?? null;
|
|
227
|
+
if (!attachDragTargetIds(selectedNodeIds))
|
|
228
|
+
return [];
|
|
229
|
+
return selectedNodeIds;
|
|
230
|
+
}
|
|
231
|
+
function attachDragTargetIds(targetIds) {
|
|
232
|
+
const target = attachNativeTransformProxy(targetIds);
|
|
233
|
+
if (!target)
|
|
234
|
+
return false;
|
|
235
|
+
try {
|
|
236
|
+
manager.attachToNode?.(target);
|
|
237
|
+
updateFreeMoveHandle();
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
options.logger?.warn?.('[BabylonTransformGizmoController] failed to attach duplicate drag target', error);
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function attachNativeTransformProxy(targetIds) {
|
|
246
|
+
const validTargetIds = targetIds.filter(nodeId => !!options.projection.getAttachableRoot(nodeId));
|
|
247
|
+
if (validTargetIds.length === 0)
|
|
248
|
+
return null;
|
|
249
|
+
const pivot = validTargetIds.length > 1
|
|
250
|
+
? options.projection.getSelectionPivot(validTargetIds)
|
|
251
|
+
: createSingleTargetPivot(validTargetIds[0]);
|
|
252
|
+
if (!pivot)
|
|
253
|
+
return null;
|
|
254
|
+
const rotation = validTargetIds.length === 1 && space === 'local'
|
|
255
|
+
? options.projection.readNodeTransform(validTargetIds[0])?.rotation ?? { x: 0, y: 0, z: 0 }
|
|
256
|
+
: { x: 0, y: 0, z: 0 };
|
|
257
|
+
return setPivotProxyTransform(pivot, rotation);
|
|
258
|
+
}
|
|
259
|
+
function createSingleTargetPivot(nodeId) {
|
|
260
|
+
const transform = options.projection.readNodeTransform(nodeId);
|
|
261
|
+
return transform
|
|
262
|
+
? {
|
|
263
|
+
mode: 'selection-center',
|
|
264
|
+
position: transform.position,
|
|
265
|
+
}
|
|
266
|
+
: null;
|
|
267
|
+
}
|
|
268
|
+
function createActiveDrag(activeTool, targetIds, activeConstraint, duplicate = false) {
|
|
161
269
|
if (targetIds.length === 0)
|
|
162
270
|
return null;
|
|
163
271
|
const beforeTransforms = options.projection.readNodeTransforms(targetIds);
|
|
@@ -183,55 +291,34 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
183
291
|
pivot,
|
|
184
292
|
before,
|
|
185
293
|
beforeTransforms,
|
|
294
|
+
duplicate,
|
|
295
|
+
proxyStart: readNativeProxyTransform(pivot),
|
|
186
296
|
};
|
|
187
297
|
}
|
|
188
298
|
function updateDrag() {
|
|
189
299
|
if (!activeDrag)
|
|
190
300
|
return;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
: Object.values(currentTransforms)[0] ?? null;
|
|
196
|
-
if (!current)
|
|
197
|
-
return;
|
|
198
|
-
options.onDragUpdate?.({ ...activeDrag, current });
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
if (!activeDrag.nodeId)
|
|
202
|
-
return;
|
|
203
|
-
const current = options.projection.readNodeTransform(activeDrag.nodeId);
|
|
301
|
+
const currentTransforms = previewBatchTransform(activeDrag);
|
|
302
|
+
const current = activeDrag.activeId
|
|
303
|
+
? currentTransforms[activeDrag.activeId] ?? Object.values(currentTransforms)[0] ?? null
|
|
304
|
+
: Object.values(currentTransforms)[0] ?? null;
|
|
204
305
|
if (!current)
|
|
205
306
|
return;
|
|
307
|
+
if (activeDrag.tool === 'move') {
|
|
308
|
+
const pivotPosition = resolvePreviewPivotPosition(activeDrag, currentTransforms);
|
|
309
|
+
if (pivotPosition)
|
|
310
|
+
setFreeMoveHandlePosition(pivotPosition);
|
|
311
|
+
}
|
|
206
312
|
options.onDragUpdate?.({ ...activeDrag, current });
|
|
207
313
|
}
|
|
208
314
|
function endDrag() {
|
|
209
315
|
const drag = activeDrag;
|
|
210
316
|
if (!drag)
|
|
211
317
|
return;
|
|
318
|
+
const afterTransforms = previewBatchTransform(drag);
|
|
212
319
|
activeDrag = null;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
emitTransformCommit(drag, afterTransforms);
|
|
216
|
-
attachCurrentSelection();
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
if (!drag.nodeId || !drag.before) {
|
|
220
|
-
options.onDragCancel?.(drag);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
const after = options.projection.readNodeTransform(drag.nodeId);
|
|
224
|
-
if (!after) {
|
|
225
|
-
options.onDragCancel?.(drag);
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
options.onDragEnd?.({
|
|
229
|
-
nodeId: drag.nodeId,
|
|
230
|
-
tool: drag.tool,
|
|
231
|
-
space: drag.space,
|
|
232
|
-
before: drag.before,
|
|
233
|
-
after,
|
|
234
|
-
});
|
|
320
|
+
emitTransformCommit(drag, afterTransforms);
|
|
321
|
+
attachCurrentSelection();
|
|
235
322
|
}
|
|
236
323
|
function emitTransformCommit(drag, afterTransforms) {
|
|
237
324
|
if (drag.targetIds.length === 1) {
|
|
@@ -246,6 +333,7 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
246
333
|
nodeId,
|
|
247
334
|
tool: drag.tool,
|
|
248
335
|
space: drag.space,
|
|
336
|
+
constraint: drag.constraint,
|
|
249
337
|
before,
|
|
250
338
|
after,
|
|
251
339
|
});
|
|
@@ -273,14 +361,19 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
273
361
|
rotation: { x: 0, y: 0, z: 0 },
|
|
274
362
|
scale: { x: 1, y: 1, z: 1 },
|
|
275
363
|
});
|
|
364
|
+
const proxyStart = drag.proxyStart ?? {
|
|
365
|
+
position: drag.pivot.position,
|
|
366
|
+
rotation: { x: 0, y: 0, z: 0 },
|
|
367
|
+
scale: { x: 1, y: 1, z: 1 },
|
|
368
|
+
};
|
|
276
369
|
if (drag.tool === 'move') {
|
|
277
|
-
return previewMoveWithDelta(drag, subtractVec3(pivotTransform.position,
|
|
370
|
+
return previewMoveWithDelta(drag, subtractVec3(pivotTransform.position, proxyStart.position));
|
|
278
371
|
}
|
|
279
372
|
if (drag.tool === 'rotate') {
|
|
280
|
-
return previewRotateWithDelta(drag, pivotTransform.rotation);
|
|
373
|
+
return previewRotateWithDelta(drag, subtractVec3(pivotTransform.rotation, proxyStart.rotation));
|
|
281
374
|
}
|
|
282
375
|
if (drag.tool === 'scale') {
|
|
283
|
-
return previewScaleWithDelta(drag, pivotTransform.scale);
|
|
376
|
+
return previewScaleWithDelta(drag, divideVec3(pivotTransform.scale, proxyStart.scale));
|
|
284
377
|
}
|
|
285
378
|
return {};
|
|
286
379
|
}
|
|
@@ -290,7 +383,7 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
290
383
|
const before = drag.beforeTransforms[nodeId];
|
|
291
384
|
if (!before)
|
|
292
385
|
continue;
|
|
293
|
-
transforms[nodeId] = translateTransform(before, delta);
|
|
386
|
+
transforms[nodeId] = applySnapToTransform(drag, nodeId, translateTransform(before, delta));
|
|
294
387
|
}
|
|
295
388
|
options.projection.setNodeTransformsPreview(transforms);
|
|
296
389
|
return transforms;
|
|
@@ -314,6 +407,7 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
314
407
|
rotation: addVec3(before.rotation, rotationDelta),
|
|
315
408
|
scale: before.scale,
|
|
316
409
|
};
|
|
410
|
+
transforms[nodeId] = applySnapToTransform(drag, nodeId, transforms[nodeId]);
|
|
317
411
|
}
|
|
318
412
|
options.projection.setNodeTransformsPreview(transforms);
|
|
319
413
|
return transforms;
|
|
@@ -335,10 +429,26 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
335
429
|
rotation: before.rotation,
|
|
336
430
|
scale: multiplyVec3(before.scale, safeScale),
|
|
337
431
|
};
|
|
432
|
+
transforms[nodeId] = applySnapToTransform(drag, nodeId, transforms[nodeId]);
|
|
338
433
|
}
|
|
339
434
|
options.projection.setNodeTransformsPreview(transforms);
|
|
340
435
|
return transforms;
|
|
341
436
|
}
|
|
437
|
+
function applySnapToTransform(drag, nodeId, after) {
|
|
438
|
+
if (!operationSettings.snap.enabled)
|
|
439
|
+
return after;
|
|
440
|
+
const before = drag.beforeTransforms[nodeId];
|
|
441
|
+
if (!before)
|
|
442
|
+
return after;
|
|
443
|
+
return snapEditorTransformSnapshot(before, after, drag.tool, operationSettings.snap);
|
|
444
|
+
}
|
|
445
|
+
function readNativeProxyTransform(pivot) {
|
|
446
|
+
return readProjectionLikeTransform(pivotProxy, {
|
|
447
|
+
position: pivot.position,
|
|
448
|
+
rotation: { x: 0, y: 0, z: 0 },
|
|
449
|
+
scale: { x: 1, y: 1, z: 1 },
|
|
450
|
+
});
|
|
451
|
+
}
|
|
342
452
|
function readProjectionLikeTransform(node, fallback) {
|
|
343
453
|
const rotation = node?.rotationQuaternion?.toEulerAngles?.() ?? node?.rotation;
|
|
344
454
|
return {
|
|
@@ -387,6 +497,13 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
387
497
|
z: left.z * right.z,
|
|
388
498
|
};
|
|
389
499
|
}
|
|
500
|
+
function divideVec3(left, right) {
|
|
501
|
+
return {
|
|
502
|
+
x: Math.abs(right.x) > 0.000001 ? left.x / right.x : 1,
|
|
503
|
+
y: Math.abs(right.y) > 0.000001 ? left.y / right.y : 1,
|
|
504
|
+
z: Math.abs(right.z) > 0.000001 ? left.z / right.z : 1,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
390
507
|
function rotateVec3Euler(value, rotation) {
|
|
391
508
|
return rotateZ(rotateY(rotateX(value, rotation.x), rotation.y), rotation.z);
|
|
392
509
|
}
|
|
@@ -418,13 +535,15 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
418
535
|
};
|
|
419
536
|
}
|
|
420
537
|
function isViewPlaneMoveCandidate(event) {
|
|
421
|
-
if (disposed || activeDrag || tool !== 'move' ||
|
|
538
|
+
if (disposed || activeDrag || tool !== 'move' || event.button !== 0)
|
|
422
539
|
return false;
|
|
423
|
-
const targetIds =
|
|
424
|
-
? getBatchTransformTargetIds()
|
|
425
|
-
: [getActiveTargetId()].filter((nodeId) => !!nodeId);
|
|
540
|
+
const targetIds = getCurrentTransformTargetIds();
|
|
426
541
|
if (targetIds.length === 0)
|
|
427
542
|
return false;
|
|
543
|
+
if (pickFreeMoveHandleAt(event.clientX, event.clientY))
|
|
544
|
+
return true;
|
|
545
|
+
if (constraint !== 'free')
|
|
546
|
+
return false;
|
|
428
547
|
const pickedId = options.projection.pickNodeIdAt(event.clientX, event.clientY);
|
|
429
548
|
return !!pickedId && targetIds.includes(pickedId);
|
|
430
549
|
}
|
|
@@ -432,18 +551,22 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
432
551
|
if (disposed || activeDrag || event.button !== 0)
|
|
433
552
|
return false;
|
|
434
553
|
const activeTool = activeTransformTool();
|
|
435
|
-
if (!activeTool
|
|
554
|
+
if (!activeTool)
|
|
436
555
|
return false;
|
|
437
556
|
const picked = pickGizmoMeshAt(event.clientX, event.clientY);
|
|
438
|
-
|
|
557
|
+
if (isFreeMoveHandleNode(picked))
|
|
558
|
+
return false;
|
|
559
|
+
const candidate = !!picked && isGizmoNode(picked);
|
|
560
|
+
if (candidate)
|
|
561
|
+
pendingDuplicateDrag = isDuplicateDragModifier(event);
|
|
562
|
+
return candidate;
|
|
439
563
|
}
|
|
440
564
|
function beginViewPlaneMove(event) {
|
|
441
565
|
if (!isViewPlaneMoveCandidate(event))
|
|
442
566
|
return false;
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const drag = createActiveDrag('move', targetIds, 'view-plane');
|
|
567
|
+
const duplicate = isDuplicateDragModifier(event);
|
|
568
|
+
const targetIds = resolveDragTargetIds('move', getCurrentTransformTargetIds(), 'free', duplicate);
|
|
569
|
+
const drag = createActiveDrag('move', targetIds, 'free', duplicate);
|
|
447
570
|
if (!drag)
|
|
448
571
|
return false;
|
|
449
572
|
const startPoint = projectPointerToViewPlane(event.clientX, event.clientY, drag.pivot.position);
|
|
@@ -457,6 +580,9 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
457
580
|
options.onDragStart?.(drag);
|
|
458
581
|
return true;
|
|
459
582
|
}
|
|
583
|
+
function isDuplicateDragModifier(event) {
|
|
584
|
+
return event.altKey === true;
|
|
585
|
+
}
|
|
460
586
|
function updateViewPlaneMove(event) {
|
|
461
587
|
const drag = activeDrag;
|
|
462
588
|
if (!drag?.viewPlane || drag.viewPlane.pointerId !== event.pointerId || disposed)
|
|
@@ -464,7 +590,9 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
464
590
|
const currentPoint = projectPointerToViewPlane(event.clientX, event.clientY, drag.pivot.position);
|
|
465
591
|
if (!currentPoint)
|
|
466
592
|
return false;
|
|
467
|
-
const
|
|
593
|
+
const delta = subtractVec3(currentPoint, drag.viewPlane.startPoint);
|
|
594
|
+
const transforms = previewMoveWithDelta(drag, delta);
|
|
595
|
+
setFreeMoveHandlePosition(resolvePreviewPivotPosition(drag, transforms) ?? addVec3(drag.pivot.position, delta));
|
|
468
596
|
const current = drag.activeId
|
|
469
597
|
? transforms[drag.activeId] ?? Object.values(transforms)[0] ?? null
|
|
470
598
|
: Object.values(transforms)[0] ?? null;
|
|
@@ -485,6 +613,16 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
485
613
|
attachCurrentSelection();
|
|
486
614
|
return true;
|
|
487
615
|
}
|
|
616
|
+
function resolvePreviewPivotPosition(drag, transforms) {
|
|
617
|
+
for (const nodeId of drag.targetIds) {
|
|
618
|
+
const before = drag.beforeTransforms[nodeId];
|
|
619
|
+
const after = transforms[nodeId];
|
|
620
|
+
if (!before || !after)
|
|
621
|
+
continue;
|
|
622
|
+
return addVec3(drag.pivot.position, subtractVec3(after.position, before.position));
|
|
623
|
+
}
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
488
626
|
function projectPointerToViewPlane(clientX, clientY, planePoint) {
|
|
489
627
|
const camera = options.scene.activeCamera ?? options.scene.cameraToUseForPointers ?? null;
|
|
490
628
|
const Vector3 = options.babylon.Vector3;
|
|
@@ -516,6 +654,113 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
516
654
|
const forwardZ = options.scene.useRightHandedSystem ? -1 : 1;
|
|
517
655
|
return readVec3Like(camera.getDirection(new Vector3(0, 0, forwardZ)));
|
|
518
656
|
}
|
|
657
|
+
function pickPlacementHit(clientX, clientY, mode) {
|
|
658
|
+
if (mode === 'off')
|
|
659
|
+
return null;
|
|
660
|
+
if (mode === 'ground')
|
|
661
|
+
return pickGroundPlacementHit(clientX, clientY);
|
|
662
|
+
return pickSurfacePlacementHit(clientX, clientY);
|
|
663
|
+
}
|
|
664
|
+
function pickGroundPlacementHit(clientX, clientY) {
|
|
665
|
+
const ray = createScenePointerRay(clientX, clientY);
|
|
666
|
+
const origin = readVec3Like(ray?.origin);
|
|
667
|
+
const direction = readVec3Like(ray?.direction);
|
|
668
|
+
if (!origin || !direction || Math.abs(direction.y) < 0.000001)
|
|
669
|
+
return null;
|
|
670
|
+
const t = -origin.y / direction.y;
|
|
671
|
+
if (!Number.isFinite(t) || t < 0)
|
|
672
|
+
return null;
|
|
673
|
+
return {
|
|
674
|
+
mode: 'ground',
|
|
675
|
+
position: addVec3(origin, scaleVec3(direction, t)),
|
|
676
|
+
normal: { x: 0, y: 1, z: 0 },
|
|
677
|
+
nodeId: null,
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function pickSurfacePlacementHit(clientX, clientY) {
|
|
681
|
+
if (!canvas || typeof options.scene.pick !== 'function')
|
|
682
|
+
return null;
|
|
683
|
+
const rect = canvas.getBoundingClientRect();
|
|
684
|
+
const pick = options.scene.pick(clientX - rect.left, clientY - rect.top, (mesh) => !isPlacementPickIgnored(mesh));
|
|
685
|
+
const point = readVec3Like(pick?.pickedPoint);
|
|
686
|
+
if (!pick?.hit || !point)
|
|
687
|
+
return null;
|
|
688
|
+
const normal = readVec3Like(pick?.getNormal?.(true)) ?? readVec3Like(pick?.normal);
|
|
689
|
+
return {
|
|
690
|
+
mode: 'surface',
|
|
691
|
+
position: point,
|
|
692
|
+
normal: normal ?? undefined,
|
|
693
|
+
nodeId: options.projection.resolveProjectionNodeId(pick.pickedMesh ?? null),
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
function isPlacementPickIgnored(node) {
|
|
697
|
+
let current = node;
|
|
698
|
+
while (current) {
|
|
699
|
+
if (current.metadata?.editorProjectionHelper)
|
|
700
|
+
return true;
|
|
701
|
+
if (current.metadata?.editorTransformFreeMoveHandle)
|
|
702
|
+
return true;
|
|
703
|
+
if (current.metadata?.editorPlacementMarker)
|
|
704
|
+
return true;
|
|
705
|
+
current = current.parent ?? null;
|
|
706
|
+
}
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
function createScenePointerRay(clientX, clientY) {
|
|
710
|
+
const camera = options.scene.activeCamera ?? options.scene.cameraToUseForPointers ?? null;
|
|
711
|
+
const Matrix = options.babylon.Matrix;
|
|
712
|
+
if (!canvas || !camera || !Matrix?.Identity || !options.scene.createPickingRay)
|
|
713
|
+
return null;
|
|
714
|
+
const rect = canvas.getBoundingClientRect();
|
|
715
|
+
return options.scene.createPickingRay(clientX - rect.left, clientY - rect.top, Matrix.Identity(), camera);
|
|
716
|
+
}
|
|
717
|
+
function ensurePlacementMarker() {
|
|
718
|
+
if (placementMarker)
|
|
719
|
+
return placementMarker;
|
|
720
|
+
const MeshBuilder = options.babylon.MeshBuilder;
|
|
721
|
+
const StandardMaterial = options.babylon.StandardMaterial;
|
|
722
|
+
const Color3 = options.babylon.Color3;
|
|
723
|
+
const utilityScene = manager.utilityLayer?.utilityLayerScene;
|
|
724
|
+
if (!MeshBuilder?.CreateSphere || !utilityScene)
|
|
725
|
+
return null;
|
|
726
|
+
placementMarker = MeshBuilder.CreateSphere('editor.placement.marker', { diameter: 0.32, segments: 16 }, utilityScene);
|
|
727
|
+
placementMarker.metadata = {
|
|
728
|
+
...(placementMarker.metadata ?? {}),
|
|
729
|
+
editorProjectionHelper: true,
|
|
730
|
+
editorPlacementMarker: true,
|
|
731
|
+
};
|
|
732
|
+
placementMarker.isPickable = false;
|
|
733
|
+
if (StandardMaterial && Color3) {
|
|
734
|
+
placementMarkerMaterial = new StandardMaterial('editor.placement.marker.material', utilityScene);
|
|
735
|
+
placementMarkerMaterial.diffuseColor = new Color3(0.18, 0.86, 0.78);
|
|
736
|
+
placementMarkerMaterial.emissiveColor = new Color3(0.1, 0.62, 0.56);
|
|
737
|
+
placementMarkerMaterial.specularColor = new Color3(0.04, 0.18, 0.16);
|
|
738
|
+
placementMarker.material = placementMarkerMaterial;
|
|
739
|
+
}
|
|
740
|
+
setPlacementMarkerVisible(false);
|
|
741
|
+
return placementMarker;
|
|
742
|
+
}
|
|
743
|
+
function setPlacementMarkerVisible(visible) {
|
|
744
|
+
if (!placementMarker)
|
|
745
|
+
return;
|
|
746
|
+
placementMarker.isVisible = visible;
|
|
747
|
+
try {
|
|
748
|
+
placementMarker.setEnabled?.(visible);
|
|
749
|
+
}
|
|
750
|
+
catch { }
|
|
751
|
+
}
|
|
752
|
+
function setPlacementMarker(hit) {
|
|
753
|
+
if (!hit) {
|
|
754
|
+
setPlacementMarkerVisible(false);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
const marker = ensurePlacementMarker();
|
|
758
|
+
const Vector3 = options.babylon.Vector3;
|
|
759
|
+
if (!marker || !Vector3)
|
|
760
|
+
return;
|
|
761
|
+
marker.position = new Vector3(hit.position.x, hit.position.y, hit.position.z);
|
|
762
|
+
setPlacementMarkerVisible(true);
|
|
763
|
+
}
|
|
519
764
|
function pickGizmoMeshAt(clientX, clientY) {
|
|
520
765
|
if (!canvas)
|
|
521
766
|
return null;
|
|
@@ -529,10 +774,88 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
529
774
|
const scenePick = typeof options.scene.pick === 'function' ? options.scene.pick(x, y) : null;
|
|
530
775
|
return scenePick?.hit ? scenePick.pickedMesh ?? null : null;
|
|
531
776
|
}
|
|
777
|
+
function pickFreeMoveHandleAt(clientX, clientY) {
|
|
778
|
+
if (!canvas || !freeMoveHandle)
|
|
779
|
+
return null;
|
|
780
|
+
const rect = canvas.getBoundingClientRect();
|
|
781
|
+
const x = clientX - rect.left;
|
|
782
|
+
const y = clientY - rect.top;
|
|
783
|
+
const utilityScene = manager.utilityLayer?.utilityLayerScene;
|
|
784
|
+
const pick = typeof utilityScene?.pick === 'function'
|
|
785
|
+
? utilityScene.pick(x, y, (mesh) => isFreeMoveHandleNode(mesh))
|
|
786
|
+
: null;
|
|
787
|
+
return pick?.hit && isFreeMoveHandleNode(pick.pickedMesh) ? pick.pickedMesh : null;
|
|
788
|
+
}
|
|
789
|
+
function ensureFreeMoveHandle() {
|
|
790
|
+
if (freeMoveHandle)
|
|
791
|
+
return freeMoveHandle;
|
|
792
|
+
const MeshBuilder = options.babylon.MeshBuilder;
|
|
793
|
+
const StandardMaterial = options.babylon.StandardMaterial;
|
|
794
|
+
const Color3 = options.babylon.Color3;
|
|
795
|
+
const utilityScene = manager.utilityLayer?.utilityLayerScene;
|
|
796
|
+
if (!MeshBuilder?.CreateSphere || !utilityScene)
|
|
797
|
+
return null;
|
|
798
|
+
freeMoveHandle = MeshBuilder.CreateSphere('editor.transform.freeMoveHandle', { diameter: 0.22, segments: 12 }, utilityScene);
|
|
799
|
+
freeMoveHandle.metadata = {
|
|
800
|
+
...(freeMoveHandle.metadata ?? {}),
|
|
801
|
+
editorProjectionHelper: true,
|
|
802
|
+
editorTransformFreeMoveHandle: true,
|
|
803
|
+
};
|
|
804
|
+
freeMoveHandle.isPickable = true;
|
|
805
|
+
if (StandardMaterial && Color3) {
|
|
806
|
+
freeMoveHandleMaterial = new StandardMaterial('editor.transform.freeMoveHandle.material', utilityScene);
|
|
807
|
+
freeMoveHandleMaterial.diffuseColor = new Color3(1, 0.82, 0.28);
|
|
808
|
+
freeMoveHandleMaterial.emissiveColor = new Color3(1, 0.68, 0.18);
|
|
809
|
+
freeMoveHandleMaterial.specularColor = new Color3(0.2, 0.18, 0.08);
|
|
810
|
+
freeMoveHandle.material = freeMoveHandleMaterial;
|
|
811
|
+
}
|
|
812
|
+
setFreeMoveHandleVisible(false);
|
|
813
|
+
return freeMoveHandle;
|
|
814
|
+
}
|
|
815
|
+
function setFreeMoveHandleVisible(visible) {
|
|
816
|
+
if (!freeMoveHandle)
|
|
817
|
+
return;
|
|
818
|
+
freeMoveHandle.isVisible = visible;
|
|
819
|
+
try {
|
|
820
|
+
freeMoveHandle.setEnabled?.(visible);
|
|
821
|
+
}
|
|
822
|
+
catch { }
|
|
823
|
+
}
|
|
824
|
+
function setFreeMoveHandlePosition(position) {
|
|
825
|
+
const handle = ensureFreeMoveHandle();
|
|
826
|
+
const Vector3 = options.babylon.Vector3;
|
|
827
|
+
if (!handle || !Vector3)
|
|
828
|
+
return;
|
|
829
|
+
handle.position = new Vector3(position.x, position.y, position.z);
|
|
830
|
+
setFreeMoveHandleVisible(true);
|
|
831
|
+
}
|
|
832
|
+
function updateFreeMoveHandle() {
|
|
833
|
+
if (tool !== 'move' || activeDrag) {
|
|
834
|
+
setFreeMoveHandleVisible(false);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
const targetIds = getCurrentTransformTargetIds();
|
|
838
|
+
if (targetIds.length === 0) {
|
|
839
|
+
setFreeMoveHandleVisible(false);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const pivot = targetIds.length > 1
|
|
843
|
+
? options.projection.getSelectionPivot(targetIds)
|
|
844
|
+
: null;
|
|
845
|
+
const position = pivot?.position
|
|
846
|
+
?? (targetIds[0] ? options.projection.readNodeTransform(targetIds[0])?.position : null);
|
|
847
|
+
if (!position) {
|
|
848
|
+
setFreeMoveHandleVisible(false);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
setFreeMoveHandlePosition(position);
|
|
852
|
+
}
|
|
532
853
|
function isGizmoNode(node) {
|
|
533
854
|
let current = node;
|
|
534
855
|
const roots = collectCurrentGizmoRoots();
|
|
535
856
|
while (current) {
|
|
857
|
+
if (isFreeMoveHandleNode(current))
|
|
858
|
+
return false;
|
|
536
859
|
if (roots.has(current))
|
|
537
860
|
return true;
|
|
538
861
|
if (current.metadata?.editorProjection?.nodeId)
|
|
@@ -546,6 +869,15 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
546
869
|
}
|
|
547
870
|
return false;
|
|
548
871
|
}
|
|
872
|
+
function isFreeMoveHandleNode(node) {
|
|
873
|
+
let current = node;
|
|
874
|
+
while (current) {
|
|
875
|
+
if (current.metadata?.editorTransformFreeMoveHandle)
|
|
876
|
+
return true;
|
|
877
|
+
current = current.parent ?? null;
|
|
878
|
+
}
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
549
881
|
function collectCurrentGizmoRoots() {
|
|
550
882
|
const roots = new Set();
|
|
551
883
|
const activeTool = activeTransformTool();
|
|
@@ -591,6 +923,7 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
591
923
|
return;
|
|
592
924
|
controller.cancelDrag();
|
|
593
925
|
tool = nextTool;
|
|
926
|
+
constraint = normalizeConstraintForTool(tool, constraint) ?? 'axis';
|
|
594
927
|
attachCurrentSelection();
|
|
595
928
|
},
|
|
596
929
|
setSpace(nextSpace) {
|
|
@@ -605,12 +938,25 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
605
938
|
setConstraint(nextConstraint) {
|
|
606
939
|
if (disposed)
|
|
607
940
|
return;
|
|
608
|
-
|
|
941
|
+
const normalized = normalizeConstraintForTool(tool, nextConstraint) ?? 'axis';
|
|
942
|
+
if (constraint === normalized)
|
|
609
943
|
return;
|
|
610
944
|
controller.cancelDrag();
|
|
611
|
-
constraint =
|
|
945
|
+
constraint = normalized;
|
|
612
946
|
attachCurrentSelection();
|
|
613
947
|
},
|
|
948
|
+
setOperationSettings(settings) {
|
|
949
|
+
if (disposed)
|
|
950
|
+
return;
|
|
951
|
+
operationSettings = cloneOperationSettings(settings);
|
|
952
|
+
if (activeDrag)
|
|
953
|
+
updateDrag();
|
|
954
|
+
},
|
|
955
|
+
preparePointerDrag(event) {
|
|
956
|
+
if (disposed || event.button !== 0)
|
|
957
|
+
return;
|
|
958
|
+
pendingDuplicateDrag = isDuplicateDragModifier(event);
|
|
959
|
+
},
|
|
614
960
|
setSelectedNode(nextNodeId) {
|
|
615
961
|
if (disposed)
|
|
616
962
|
return;
|
|
@@ -648,6 +994,8 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
648
994
|
beginViewPlaneMove,
|
|
649
995
|
updateViewPlaneMove,
|
|
650
996
|
endViewPlaneMove,
|
|
997
|
+
pickPlacementHit,
|
|
998
|
+
setPlacementMarker,
|
|
651
999
|
cancelDrag() {
|
|
652
1000
|
const drag = activeDrag;
|
|
653
1001
|
if (!drag)
|
|
@@ -655,11 +1003,11 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
655
1003
|
activeDrag = null;
|
|
656
1004
|
if (drag.targetIds.length > 1) {
|
|
657
1005
|
options.projection.setNodeTransformsPreview(drag.beforeTransforms);
|
|
658
|
-
attachCurrentSelection();
|
|
659
1006
|
}
|
|
660
1007
|
else if (drag.nodeId && drag.before) {
|
|
661
1008
|
options.projection.setNodeTransformPreview(drag.nodeId, drag.before);
|
|
662
1009
|
}
|
|
1010
|
+
attachCurrentSelection();
|
|
663
1011
|
options.onDragCancel?.(drag);
|
|
664
1012
|
},
|
|
665
1013
|
dispose() {
|
|
@@ -679,11 +1027,42 @@ export function createBabylonTransformGizmoController(options) {
|
|
|
679
1027
|
pivotProxy?.dispose?.();
|
|
680
1028
|
}
|
|
681
1029
|
catch { }
|
|
1030
|
+
try {
|
|
1031
|
+
freeMoveHandle?.dispose?.();
|
|
1032
|
+
}
|
|
1033
|
+
catch { }
|
|
1034
|
+
try {
|
|
1035
|
+
freeMoveHandleMaterial?.dispose?.();
|
|
1036
|
+
}
|
|
1037
|
+
catch { }
|
|
1038
|
+
try {
|
|
1039
|
+
placementMarker?.dispose?.();
|
|
1040
|
+
}
|
|
1041
|
+
catch { }
|
|
1042
|
+
try {
|
|
1043
|
+
placementMarkerMaterial?.dispose?.();
|
|
1044
|
+
}
|
|
1045
|
+
catch { }
|
|
682
1046
|
pivotProxy = null;
|
|
1047
|
+
freeMoveHandle = null;
|
|
1048
|
+
freeMoveHandleMaterial = null;
|
|
1049
|
+
placementMarker = null;
|
|
1050
|
+
placementMarkerMaterial = null;
|
|
683
1051
|
disposed = true;
|
|
684
1052
|
},
|
|
685
1053
|
};
|
|
686
1054
|
attachCurrentSelection();
|
|
687
1055
|
return controller;
|
|
688
1056
|
}
|
|
1057
|
+
function cloneOperationSettings(settings) {
|
|
1058
|
+
return {
|
|
1059
|
+
snap: {
|
|
1060
|
+
enabled: settings.snap.enabled,
|
|
1061
|
+
moveStep: settings.snap.moveStep,
|
|
1062
|
+
rotateStepDegrees: settings.snap.rotateStepDegrees,
|
|
1063
|
+
scaleStep: settings.snap.scaleStep,
|
|
1064
|
+
},
|
|
1065
|
+
placementMode: settings.placementMode,
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
689
1068
|
//# sourceMappingURL=transform-gizmo-controller.js.map
|