@fps-games/editor 0.1.2-beta.1 → 0.1.3-beta.1
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 +44 -8
- package/dist/local-editor-harness.d.ts.map +1 -1
- package/dist/local-editor-harness.js +436 -170
- package/dist/local-editor-harness.js.map +1 -1
- package/dist/local-editor-scene-render-scheduler.d.ts +8 -1
- package/dist/local-editor-scene-render-scheduler.d.ts.map +1 -1
- package/dist/local-editor-scene-render-scheduler.js +23 -2
- package/dist/local-editor-scene-render-scheduler.js.map +1 -1
- package/dist/local-editor-scene-view-interaction-runtime.d.ts +22 -0
- package/dist/local-editor-scene-view-interaction-runtime.d.ts.map +1 -0
- package/dist/local-editor-scene-view-interaction-runtime.js +80 -0
- package/dist/local-editor-scene-view-interaction-runtime.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/editor-sky.d.ts +2 -2
- package/node_modules/@fps-games/editor-babylon/dist/editor-sky.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/editor-sky.js +31 -15
- package/node_modules/@fps-games/editor-babylon/dist/editor-sky.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/editor-world.d.ts +10 -0
- package/node_modules/@fps-games/editor-babylon/dist/editor-world.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/editor-world.js +31 -9
- package/node_modules/@fps-games/editor-babylon/dist/editor-world.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/index.d.ts +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/index.js +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/infinite-grid.d.ts +24 -0
- package/node_modules/@fps-games/editor-babylon/dist/infinite-grid.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/infinite-grid.js +241 -48
- package/node_modules/@fps-games/editor-babylon/dist/infinite-grid.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.d.ts +3 -1
- package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.js +166 -0
- package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts +12 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection.js +231 -32
- package/node_modules/@fps-games/editor-babylon/dist/projection.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.d.ts +4 -0
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.js +37 -23
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-camera-controller.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 +100 -8
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-input-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.d.ts +22 -0
- package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.js +71 -0
- package/node_modules/@fps-games/editor-babylon/dist/shadow-preview-controller.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.d.ts +6 -1
- 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 +107 -79
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/transform-solver.d.ts +30 -0
- package/node_modules/@fps-games/editor-babylon/dist/transform-solver.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/transform-solver.js +120 -0
- package/node_modules/@fps-games/editor-babylon/dist/transform-solver.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/types.d.ts +3 -1
- package/node_modules/@fps-games/editor-babylon/dist/types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/package.json +5 -4
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js +28 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js +103 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts +3 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js +49 -4
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.d.ts +4 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.js +12 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panel-registry.d.ts +3 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panel-registry.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panel-registry.js +7 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panel-registry.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts +8 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js +645 -20
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js +1 -1
- 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 +7 -6
- 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 +150 -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-workbench.d.ts +6 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +3 -4
- 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 +1067 -87
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/package.json +2 -2
- package/node_modules/@fps-games/editor-core/dist/scene-view-input.d.ts +7 -0
- 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 +22 -0
- 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.map +1 -1
- 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/dist/index.d.ts +61 -0
- package/node_modules/@fps-games/editor-protocol/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-protocol/dist/index.js +157 -0
- package/node_modules/@fps-games/editor-protocol/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-protocol/package.json +1 -1
- package/package.json +8 -6
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createEditorSession, createInspectorRegistry, DEFAULT_EDITOR_TRANSFORM_OPERATION_SETTINGS, DEFAULT_EDITOR_VIEWPORT_TOOL_STATE, createEmptyEditorViewportSpatialOverlayState, cloneEditorViewportToolState, cloneEditorViewportSpatialOverlayState, createInspectorEditPayload, computeEditorTransformActionTargets, mergeInspectorSections, normalizeEditorTransformConstraint, resolveEditorSelectionCommand, serializedMultiObjectToInspectorObject, serializedObjectToInspectorObject, validateSceneGraphDelete, validateSceneGraphDrop, validateSceneGraphGroupSelection, validateSceneGraphMove, validateSceneGraphRename, } from '@fps-games/editor-core';
|
|
2
2
|
import { createLocalEditorBrowserUi, } from '@fps-games/editor-browser';
|
|
3
|
-
import { createBabylonEditorProjection, createBabylonEditorWorld, createBabylonProjectionSelectionController,
|
|
3
|
+
import { createBabylonEditorProjection, createBabylonEditorWorld, createBabylonProjectionSelectionController, createBabylonSceneCameraPreviewController, createBabylonSceneViewCameraController, createBabylonSceneViewInputController, createBabylonSceneViewMeasurementController, createBabylonSceneViewSpatialOverlayController, createBabylonEditorShadowPreviewController, createBabylonTransformGizmoController, focusEditorViewportSelection, } from '@fps-games/editor-babylon';
|
|
4
4
|
import { createLocalEditorSceneRenderScheduler, } from './local-editor-scene-render-scheduler';
|
|
5
5
|
import { createLocalEditorViewportRenderCoordinator, } from './local-editor-viewport-render-coordinator';
|
|
6
|
+
import { createLocalEditorSceneViewInteractionRuntime, } from './local-editor-scene-view-interaction-runtime';
|
|
6
7
|
export function createLocalEditorHarness(options) {
|
|
7
8
|
const root = options.root ?? document.body;
|
|
8
9
|
const state = {
|
|
@@ -12,20 +13,28 @@ export function createLocalEditorHarness(options) {
|
|
|
12
13
|
source: null,
|
|
13
14
|
assets: [],
|
|
14
15
|
assetFilter: '',
|
|
16
|
+
selectedAssetId: null,
|
|
15
17
|
babylon: null,
|
|
16
18
|
engine: null,
|
|
17
19
|
world: null,
|
|
18
20
|
grid: null,
|
|
19
21
|
gridVisible: true,
|
|
20
22
|
projection: null,
|
|
23
|
+
shadowPreview: null,
|
|
21
24
|
gizmo: null,
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
sceneCameraPreview: null,
|
|
26
|
+
sceneCameraPreviewEnabled: false,
|
|
24
27
|
sceneViewInput: null,
|
|
25
28
|
sceneViewCamera: null,
|
|
29
|
+
sceneViewInteraction: null,
|
|
26
30
|
sceneViewMeasurement: null,
|
|
27
31
|
sceneViewSpatialOverlay: null,
|
|
28
32
|
selectionController: null,
|
|
33
|
+
sceneRenderScheduler: null,
|
|
34
|
+
viewportRenderCoordinator: null,
|
|
35
|
+
sceneFrameStats: null,
|
|
36
|
+
worldAppearanceKey: '',
|
|
37
|
+
worldRenderingKey: '',
|
|
29
38
|
boxSelection: null,
|
|
30
39
|
transformTool: 'select',
|
|
31
40
|
transformSpace: 'world',
|
|
@@ -36,9 +45,6 @@ export function createLocalEditorHarness(options) {
|
|
|
36
45
|
viewportSpatialOverlay: createEmptyEditorViewportSpatialOverlayState(),
|
|
37
46
|
duplicateDrag: null,
|
|
38
47
|
armedPlacement: null,
|
|
39
|
-
sceneRenderScheduler: null,
|
|
40
|
-
viewportRenderCoordinator: null,
|
|
41
|
-
sceneFrameStats: null,
|
|
42
48
|
resizeHandler: null,
|
|
43
49
|
status: 'Game running',
|
|
44
50
|
statusTone: 'default',
|
|
@@ -51,6 +57,9 @@ export function createLocalEditorHarness(options) {
|
|
|
51
57
|
root,
|
|
52
58
|
theme: options.theme,
|
|
53
59
|
localTestActions: options.localTestActions,
|
|
60
|
+
input: {
|
|
61
|
+
isShortcutReserved: (event) => state.sceneViewInput?.ownsKeyboardEvent(event) ?? false,
|
|
62
|
+
},
|
|
54
63
|
inspector: options.inspector,
|
|
55
64
|
hierarchy: {
|
|
56
65
|
contextActions: createBrowserHierarchyContextActions(state, options),
|
|
@@ -80,6 +89,14 @@ export function createLocalEditorHarness(options) {
|
|
|
80
89
|
if (createAssetFromBrowserIntent(state, options, assetId))
|
|
81
90
|
harness.render();
|
|
82
91
|
},
|
|
92
|
+
onSelectAsset: (assetId) => {
|
|
93
|
+
if (selectBrowserAsset(state, assetId))
|
|
94
|
+
harness.render();
|
|
95
|
+
},
|
|
96
|
+
onAssetAction: (input) => {
|
|
97
|
+
if (handleBrowserAssetAction(state, options, input))
|
|
98
|
+
harness.render();
|
|
99
|
+
},
|
|
83
100
|
onSelectHierarchyItem: (input) => {
|
|
84
101
|
if (selectItem(state, options, input))
|
|
85
102
|
harness.render();
|
|
@@ -141,25 +158,43 @@ export function createLocalEditorHarness(options) {
|
|
|
141
158
|
if (patched || state.status !== previousStatus)
|
|
142
159
|
harness.render();
|
|
143
160
|
},
|
|
161
|
+
onRenderingPropertyChange: options.documentAdapter.onRenderingPropertyChange
|
|
162
|
+
? (input) => {
|
|
163
|
+
void applyRenderingPropertyChange(state, options, input)
|
|
164
|
+
.then((changed) => {
|
|
165
|
+
if (changed)
|
|
166
|
+
harness.render();
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
: undefined,
|
|
170
|
+
onRenderingAction: options.documentAdapter.onRenderingAction
|
|
171
|
+
? (input) => {
|
|
172
|
+
void applyRenderingAction(state, options, input)
|
|
173
|
+
.then((changed) => {
|
|
174
|
+
if (changed)
|
|
175
|
+
harness.render();
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
: undefined,
|
|
144
179
|
onTransformToolChange: (tool) => {
|
|
145
180
|
state.transformTool = tool;
|
|
146
181
|
state.transformConstraint = normalizeTransformConstraint(tool, state.transformConstraint);
|
|
147
182
|
state.gizmo?.setTool(tool);
|
|
148
183
|
state.gizmo?.setConstraint(state.transformConstraint);
|
|
149
|
-
requestEditorSceneFrame(state, 'transform-tool-change');
|
|
150
184
|
harness.render();
|
|
185
|
+
requestEditorSceneFrame(state, 'transform-tool-change');
|
|
151
186
|
},
|
|
152
187
|
onTransformSpaceChange: (space) => {
|
|
153
188
|
state.transformSpace = space;
|
|
154
189
|
state.gizmo?.setSpace(space);
|
|
155
|
-
requestEditorSceneFrame(state, 'transform-space-change');
|
|
156
190
|
harness.render();
|
|
191
|
+
requestEditorSceneFrame(state, 'transform-space-change');
|
|
157
192
|
},
|
|
158
193
|
onTransformConstraintChange: (constraint) => {
|
|
159
194
|
state.transformConstraint = normalizeTransformConstraint(state.transformTool, constraint);
|
|
160
195
|
state.gizmo?.setConstraint(state.transformConstraint);
|
|
161
|
-
requestEditorSceneFrame(state, 'transform-constraint-change');
|
|
162
196
|
harness.render();
|
|
197
|
+
requestEditorSceneFrame(state, 'transform-constraint-change');
|
|
163
198
|
},
|
|
164
199
|
onTransformSnapEnabledChange: (enabled) => {
|
|
165
200
|
state.transformOperationSettings = updateTransformOperationSettings(state.transformOperationSettings, {
|
|
@@ -170,8 +205,8 @@ export function createLocalEditorHarness(options) {
|
|
|
170
205
|
});
|
|
171
206
|
state.gizmo?.setOperationSettings(state.transformOperationSettings);
|
|
172
207
|
state.status = enabled ? 'Transform snap enabled' : 'Transform snap disabled';
|
|
173
|
-
requestEditorSceneFrame(state, 'transform-operation-settings-change');
|
|
174
208
|
harness.render();
|
|
209
|
+
requestEditorSceneFrame(state, 'transform-operation-settings-change');
|
|
175
210
|
},
|
|
176
211
|
onTransformSnapStepChange: (input) => {
|
|
177
212
|
const value = normalizePositiveStep(input.value);
|
|
@@ -187,8 +222,8 @@ export function createLocalEditorHarness(options) {
|
|
|
187
222
|
state.transformOperationSettings = updateTransformOperationSettings(state.transformOperationSettings, { snap });
|
|
188
223
|
state.gizmo?.setOperationSettings(state.transformOperationSettings);
|
|
189
224
|
state.status = `Transform snap ${input.kind} step ${value}`;
|
|
190
|
-
requestEditorSceneFrame(state, 'transform-operation-settings-change');
|
|
191
225
|
harness.render();
|
|
226
|
+
requestEditorSceneFrame(state, 'transform-operation-settings-change');
|
|
192
227
|
},
|
|
193
228
|
onPlacementModeChange: (mode) => {
|
|
194
229
|
state.transformOperationSettings = updateTransformOperationSettings(state.transformOperationSettings, {
|
|
@@ -198,8 +233,8 @@ export function createLocalEditorHarness(options) {
|
|
|
198
233
|
if (state.transformOperationSettings.placementMode === 'off')
|
|
199
234
|
clearArmedPlacement(state);
|
|
200
235
|
state.status = `Placement mode: ${state.transformOperationSettings.placementMode}`;
|
|
201
|
-
requestEditorSceneFrame(state, 'placement-mode-change');
|
|
202
236
|
harness.render();
|
|
237
|
+
requestEditorSceneFrame(state, 'placement-mode-change');
|
|
203
238
|
},
|
|
204
239
|
onTransformAction: (action) => {
|
|
205
240
|
if (executeTransformAction(state, options, action))
|
|
@@ -225,8 +260,8 @@ export function createLocalEditorHarness(options) {
|
|
|
225
260
|
if (clearViewportMeasurement(state))
|
|
226
261
|
harness.render();
|
|
227
262
|
},
|
|
228
|
-
|
|
229
|
-
if (
|
|
263
|
+
onSceneCameraPreviewToggle: (enabled) => {
|
|
264
|
+
if (setSceneCameraPreviewEnabled(state, options, enabled))
|
|
230
265
|
harness.render();
|
|
231
266
|
},
|
|
232
267
|
onGridVisibleChange: (visible) => {
|
|
@@ -243,20 +278,17 @@ export function createLocalEditorHarness(options) {
|
|
|
243
278
|
},
|
|
244
279
|
onCancelActiveOperation: () => {
|
|
245
280
|
cancelActiveOperation(state);
|
|
246
|
-
requestEditorSceneFrame(state, 'cancel-active-operation');
|
|
247
281
|
harness.render();
|
|
248
282
|
},
|
|
249
283
|
},
|
|
250
284
|
});
|
|
251
|
-
const renderUi = () => {
|
|
252
|
-
syncViewportCameraState(state);
|
|
253
|
-
syncViewportMeasurementState(state);
|
|
254
|
-
syncViewportSpatialOverlay(state);
|
|
255
|
-
ui.update(createUiState(state, options));
|
|
256
|
-
};
|
|
257
285
|
harness = {
|
|
258
286
|
render() {
|
|
259
|
-
|
|
287
|
+
syncSceneCameraPreview(state, options);
|
|
288
|
+
syncViewportCameraState(state);
|
|
289
|
+
syncViewportMeasurementState(state);
|
|
290
|
+
syncViewportSpatialOverlay(state);
|
|
291
|
+
ui.update(createUiState(state, options));
|
|
260
292
|
},
|
|
261
293
|
notifyViewportRevealed(reason = 'viewport-revealed') {
|
|
262
294
|
state.viewportRenderCoordinator?.requestRevealFrame(reason);
|
|
@@ -507,11 +539,11 @@ function cloneViewportMeasurement(measurement) {
|
|
|
507
539
|
};
|
|
508
540
|
}
|
|
509
541
|
function setViewportViewPreset(state, preset) {
|
|
510
|
-
if (state.
|
|
511
|
-
state.status = 'Viewport view unavailable during
|
|
542
|
+
if (state.sceneCameraPreviewEnabled) {
|
|
543
|
+
state.status = 'Viewport view unavailable during Scene Camera preview';
|
|
512
544
|
state.statusTone = 'warning';
|
|
513
545
|
state.statusToneStatus = state.status;
|
|
514
|
-
state.statusDetails = 'Disable
|
|
546
|
+
state.statusDetails = 'Disable Scene Camera preview before switching editor viewport views.';
|
|
515
547
|
return true;
|
|
516
548
|
}
|
|
517
549
|
const nextPreset = normalizeViewportViewPreset(preset);
|
|
@@ -530,15 +562,15 @@ function setViewportViewPreset(state, preset) {
|
|
|
530
562
|
state.statusTone = 'default';
|
|
531
563
|
state.statusToneStatus = state.status;
|
|
532
564
|
state.statusDetails = '';
|
|
533
|
-
requestEditorSceneFrame(state, 'viewport-view-preset
|
|
565
|
+
requestEditorSceneFrame(state, 'viewport-view-preset');
|
|
534
566
|
return true;
|
|
535
567
|
}
|
|
536
568
|
function setViewportProjectionMode(state, mode) {
|
|
537
|
-
if (state.
|
|
538
|
-
state.status = 'Viewport projection unavailable during
|
|
569
|
+
if (state.sceneCameraPreviewEnabled) {
|
|
570
|
+
state.status = 'Viewport projection unavailable during Scene Camera preview';
|
|
539
571
|
state.statusTone = 'warning';
|
|
540
572
|
state.statusToneStatus = state.status;
|
|
541
|
-
state.statusDetails = 'Disable
|
|
573
|
+
state.statusDetails = 'Disable Scene Camera preview before switching editor viewport projection.';
|
|
542
574
|
return true;
|
|
543
575
|
}
|
|
544
576
|
const nextMode = normalizeViewportProjectionMode(mode);
|
|
@@ -554,7 +586,7 @@ function setViewportProjectionMode(state, mode) {
|
|
|
554
586
|
state.statusTone = 'default';
|
|
555
587
|
state.statusToneStatus = state.status;
|
|
556
588
|
state.statusDetails = '';
|
|
557
|
-
requestEditorSceneFrame(state, 'viewport-projection-mode
|
|
589
|
+
requestEditorSceneFrame(state, 'viewport-projection-mode');
|
|
558
590
|
return true;
|
|
559
591
|
}
|
|
560
592
|
function setViewportOverlaySettings(state, settings) {
|
|
@@ -603,6 +635,7 @@ function setViewportUtilityTool(state, tool) {
|
|
|
603
635
|
state.statusTone = 'default';
|
|
604
636
|
state.statusToneStatus = state.status;
|
|
605
637
|
state.statusDetails = '';
|
|
638
|
+
requestEditorSceneFrame(state, 'viewport-utility-tool');
|
|
606
639
|
return true;
|
|
607
640
|
}
|
|
608
641
|
function clearViewportMeasurement(state) {
|
|
@@ -615,6 +648,7 @@ function clearViewportMeasurement(state) {
|
|
|
615
648
|
state.statusTone = 'default';
|
|
616
649
|
state.statusToneStatus = state.status;
|
|
617
650
|
state.statusDetails = '';
|
|
651
|
+
requestEditorSceneFrame(state, 'viewport-measurement-clear');
|
|
618
652
|
return true;
|
|
619
653
|
}
|
|
620
654
|
export function applyLocalEditorHarnessViewportMeasurementPointerStart(state, event) {
|
|
@@ -708,23 +742,35 @@ function validateTransformActionSelection(state, action) {
|
|
|
708
742
|
}
|
|
709
743
|
async function createEditorWorld(state, options, renderUi, updateSceneFrameStats) {
|
|
710
744
|
disposeEditorWorld(state);
|
|
745
|
+
const render = (reason = 'editor-world-update') => {
|
|
746
|
+
renderUi();
|
|
747
|
+
requestEditorSceneFrame(state, reason);
|
|
748
|
+
};
|
|
711
749
|
const canvas = options.worldAdapter.getCanvas();
|
|
712
750
|
if (!canvas)
|
|
713
751
|
throw new Error('Editor canvas not found');
|
|
714
752
|
const babylon = await options.worldAdapter.loadBabylon();
|
|
715
753
|
const engine = options.worldAdapter.createEngine(babylon, canvas);
|
|
754
|
+
const document = state.session?.getState().workingDocument;
|
|
755
|
+
const initialAppearance = resolveLocalEditorWorldAppearance(options, document);
|
|
756
|
+
const initialAppearanceKey = serializeLocalEditorWorldAppearance(initialAppearance);
|
|
757
|
+
const initialRendering = resolveLocalEditorWorldRendering(options, document);
|
|
758
|
+
const initialRenderingKey = serializeLocalEditorWorldRendering(initialRendering);
|
|
716
759
|
const world = createBabylonEditorWorld({
|
|
717
760
|
engine,
|
|
718
761
|
canvas,
|
|
719
762
|
babylon,
|
|
720
763
|
cameraTarget: options.world?.cameraTarget,
|
|
721
764
|
cameraRadius: options.world?.cameraRadius,
|
|
722
|
-
clearColor:
|
|
723
|
-
sky:
|
|
765
|
+
clearColor: initialAppearance.clearColor,
|
|
766
|
+
sky: initialAppearance.sky,
|
|
724
767
|
useRightHandedSystem: options.world?.useRightHandedSystem,
|
|
725
768
|
enableDefaultCameraControls: false,
|
|
726
769
|
});
|
|
727
|
-
const sceneRenderScheduler = createLocalEditorSceneRenderScheduler(() =>
|
|
770
|
+
const sceneRenderScheduler = createLocalEditorSceneRenderScheduler((frame) => {
|
|
771
|
+
state.sceneViewInteraction?.updateFrame(frame);
|
|
772
|
+
world.render();
|
|
773
|
+
}, {
|
|
728
774
|
onStatsChange(stats) {
|
|
729
775
|
state.sceneFrameStats = stats;
|
|
730
776
|
updateSceneFrameStats(stats);
|
|
@@ -735,7 +781,17 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
735
781
|
getEngine: () => engine,
|
|
736
782
|
getScene: () => world.scene,
|
|
737
783
|
});
|
|
738
|
-
const
|
|
784
|
+
const sceneViewInteraction = createLocalEditorSceneViewInteractionRuntime({
|
|
785
|
+
coordinator: viewportRenderCoordinator,
|
|
786
|
+
getCamera: () => state.sceneViewCamera,
|
|
787
|
+
});
|
|
788
|
+
state.sceneViewInteraction = sceneViewInteraction;
|
|
789
|
+
const grid = options.createGrid?.(babylon, world.scene, world.camera, {
|
|
790
|
+
getCamera: () => world.scene?.activeCamera ?? world.camera ?? null,
|
|
791
|
+
getEditorCamera: () => world.camera ?? null,
|
|
792
|
+
}) ?? null;
|
|
793
|
+
if (initialAppearance.grid)
|
|
794
|
+
grid?.setColors(initialAppearance.grid);
|
|
739
795
|
grid?.setVisible(state.gridVisible);
|
|
740
796
|
const projection = createBabylonEditorProjection({
|
|
741
797
|
babylon,
|
|
@@ -743,6 +799,7 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
743
799
|
importModel: options.worldAdapter.importProjectionModel,
|
|
744
800
|
logger: console,
|
|
745
801
|
onProjectionReady(event) {
|
|
802
|
+
syncCurrentSelectionToSceneArtifacts(state);
|
|
746
803
|
viewportRenderCoordinator.invalidateScene(`projection-${event.nodeId}-ready`);
|
|
747
804
|
},
|
|
748
805
|
});
|
|
@@ -754,32 +811,41 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
754
811
|
initialSpace: state.transformSpace,
|
|
755
812
|
logger: console,
|
|
756
813
|
onDragStart(event) {
|
|
757
|
-
|
|
814
|
+
sceneViewInteraction.beginGizmoDrag();
|
|
758
815
|
state.status = event.targetIds.length > 1
|
|
759
816
|
? `Dragging ${event.duplicate ? 'duplicate ' : ''}${event.tool} ${event.targetIds.length} objects`
|
|
760
817
|
: `Dragging ${event.duplicate ? 'duplicate ' : ''}${event.tool} ${event.nodeId ?? event.activeId ?? 'selection'}`;
|
|
761
|
-
|
|
818
|
+
render();
|
|
762
819
|
},
|
|
763
820
|
onDragUpdate() {
|
|
764
|
-
|
|
821
|
+
render();
|
|
765
822
|
},
|
|
766
823
|
onDragEnd(event) {
|
|
767
|
-
|
|
824
|
+
sceneViewInteraction.endGizmoDrag();
|
|
768
825
|
const changed = commitGizmoTransform(state, options, event);
|
|
769
826
|
if (!changed)
|
|
770
827
|
requestEditorSceneFrame(state, 'gizmo-drag-end');
|
|
771
828
|
renderUi();
|
|
772
829
|
},
|
|
773
830
|
onDragCancel(event) {
|
|
774
|
-
|
|
831
|
+
sceneViewInteraction.endGizmoDrag();
|
|
775
832
|
if (event.duplicate && cancelDuplicateDrag(state, options)) {
|
|
776
|
-
|
|
833
|
+
render();
|
|
777
834
|
return;
|
|
778
835
|
}
|
|
779
|
-
requestEditorSceneFrame(state, 'gizmo-drag-cancel');
|
|
780
836
|
state.status = event.targetIds.length > 1
|
|
781
837
|
? `Canceled ${event.tool} ${event.targetIds.length} objects`
|
|
782
838
|
: `Canceled ${event.tool} ${event.nodeId ?? event.activeId ?? 'selection'}`;
|
|
839
|
+
render();
|
|
840
|
+
},
|
|
841
|
+
onDragBlocked(event) {
|
|
842
|
+
sceneViewInteraction.endGizmoDrag();
|
|
843
|
+
if (event.duplicate)
|
|
844
|
+
cancelDuplicateDrag(state, options);
|
|
845
|
+
requestEditorSceneFrame(state, 'gizmo-drag-blocked');
|
|
846
|
+
state.status = formatBlockedGizmoTransformStatus(event);
|
|
847
|
+
state.statusTone = 'warning';
|
|
848
|
+
state.statusToneStatus = state.status;
|
|
783
849
|
renderUi();
|
|
784
850
|
},
|
|
785
851
|
onDuplicateDragStart(input) {
|
|
@@ -799,15 +865,15 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
799
865
|
isOperationBlocked: () => state.gizmo?.getState().dragPhase === 'dragging',
|
|
800
866
|
onSelectionCommand(command) {
|
|
801
867
|
if (dispatchSelectionCommand(state, options, command))
|
|
802
|
-
|
|
868
|
+
render();
|
|
803
869
|
},
|
|
804
870
|
onFocusIntent(nodeId) {
|
|
805
871
|
if (focusProjectionNode(state, nodeId))
|
|
806
|
-
|
|
872
|
+
render();
|
|
807
873
|
},
|
|
808
874
|
onBoxSelectionChange(box) {
|
|
809
875
|
state.boxSelection = box;
|
|
810
|
-
|
|
876
|
+
render();
|
|
811
877
|
},
|
|
812
878
|
});
|
|
813
879
|
const sceneViewMeasurement = createBabylonSceneViewMeasurementController({
|
|
@@ -823,10 +889,10 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
823
889
|
isPlacementCandidate: () => isPlacementArmed(state),
|
|
824
890
|
isMeasurementCandidate: () => state.viewportTools.activeUtilityTool === 'measure-distance',
|
|
825
891
|
onPointerIntentStart(event) {
|
|
826
|
-
|
|
892
|
+
sceneViewInteraction.beginPointerIntent(event.state.intent);
|
|
827
893
|
if (event.state.intent === 'measurement') {
|
|
828
894
|
if (handleViewportMeasurementStart(state, event.originalEvent))
|
|
829
|
-
|
|
895
|
+
render();
|
|
830
896
|
return;
|
|
831
897
|
}
|
|
832
898
|
if (event.state.intent === 'gizmo-drag') {
|
|
@@ -835,108 +901,96 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
835
901
|
}
|
|
836
902
|
if (event.state.intent === 'placement') {
|
|
837
903
|
if (previewArmedPlacement(state, event.originalEvent))
|
|
838
|
-
|
|
904
|
+
render();
|
|
839
905
|
return;
|
|
840
906
|
}
|
|
841
907
|
if (event.state.intent === 'view-plane-move') {
|
|
842
908
|
if (gizmo.beginViewPlaneMove(event.originalEvent))
|
|
843
|
-
|
|
909
|
+
render();
|
|
844
910
|
return;
|
|
845
911
|
}
|
|
846
912
|
if (event.state.intent === 'selection-click' || event.state.intent === 'box-select') {
|
|
847
913
|
selectionController.beginPointerSelection(event.originalEvent);
|
|
848
|
-
requestEditorSceneFrame(state,
|
|
914
|
+
requestEditorSceneFrame(state, 'viewport-pointer-selection-start');
|
|
849
915
|
}
|
|
850
916
|
},
|
|
851
917
|
onPointerIntentMove(event) {
|
|
852
918
|
if (event.state.intent === 'measurement') {
|
|
853
919
|
if (handleViewportMeasurementMove(state, event.originalEvent))
|
|
854
|
-
|
|
920
|
+
render();
|
|
855
921
|
return;
|
|
856
922
|
}
|
|
857
923
|
if (event.state.intent === 'placement') {
|
|
858
924
|
if (previewArmedPlacement(state, event.originalEvent))
|
|
859
|
-
|
|
925
|
+
render();
|
|
860
926
|
return;
|
|
861
927
|
}
|
|
862
928
|
if (event.state.intent === 'view-plane-move') {
|
|
863
929
|
if (gizmo.updateViewPlaneMove(event.originalEvent))
|
|
864
|
-
|
|
930
|
+
render();
|
|
865
931
|
return;
|
|
866
932
|
}
|
|
867
|
-
if (!state.
|
|
868
|
-
|
|
933
|
+
if (!state.sceneCameraPreviewEnabled && state.sceneViewCamera?.handlePointerIntentMove(event)) {
|
|
934
|
+
render();
|
|
869
935
|
return;
|
|
870
936
|
}
|
|
871
937
|
if (event.state.intent === 'selection-click' || event.state.intent === 'box-select') {
|
|
872
938
|
selectionController.updatePointerSelection(event.originalEvent, event.state.intent);
|
|
939
|
+
requestEditorSceneFrame(state, 'viewport-pointer-selection-update');
|
|
873
940
|
}
|
|
874
|
-
requestEditorSceneFrame(state, `pointer-${event.state.intent}`);
|
|
875
941
|
},
|
|
876
942
|
onPointerIntentEnd(event) {
|
|
877
|
-
|
|
943
|
+
sceneViewInteraction.endPointerIntent(event.state.intent);
|
|
878
944
|
if (event.state.intent === 'measurement') {
|
|
879
945
|
if (handleViewportMeasurementEnd(state))
|
|
880
|
-
|
|
946
|
+
render();
|
|
881
947
|
return;
|
|
882
948
|
}
|
|
883
949
|
if (event.state.intent === 'placement') {
|
|
884
950
|
if (commitArmedPlacement(state, options, event.originalEvent))
|
|
885
|
-
|
|
951
|
+
render();
|
|
886
952
|
return;
|
|
887
953
|
}
|
|
888
954
|
if (event.state.intent === 'view-plane-move') {
|
|
889
|
-
if (gizmo.endViewPlaneMove(event.originalEvent))
|
|
890
|
-
|
|
891
|
-
renderUi();
|
|
892
|
-
}
|
|
955
|
+
if (gizmo.endViewPlaneMove(event.originalEvent))
|
|
956
|
+
render();
|
|
893
957
|
return;
|
|
894
958
|
}
|
|
895
959
|
if (event.state.intent === 'selection-click' || event.state.intent === 'box-select') {
|
|
896
960
|
selectionController.endPointerSelection(event.originalEvent, event.state.intent);
|
|
961
|
+
requestEditorSceneFrame(state, 'viewport-pointer-selection-end');
|
|
897
962
|
}
|
|
898
|
-
requestEditorSceneFrame(state, `pointer-${event.state.intent}-end`);
|
|
899
963
|
},
|
|
900
964
|
onPointerIntentCancel(event) {
|
|
901
|
-
|
|
965
|
+
sceneViewInteraction.cancelPointerIntent(event.state.intent);
|
|
902
966
|
if (event.state.intent === 'measurement') {
|
|
903
|
-
|
|
967
|
+
render();
|
|
904
968
|
return;
|
|
905
969
|
}
|
|
906
970
|
if (event.state.intent === 'placement') {
|
|
907
971
|
state.gizmo?.setPlacementMarker(null);
|
|
908
|
-
|
|
909
|
-
renderUi();
|
|
972
|
+
render();
|
|
910
973
|
return;
|
|
911
974
|
}
|
|
912
975
|
if (event.state.intent === 'view-plane-move') {
|
|
913
976
|
gizmo.cancelDrag();
|
|
914
|
-
|
|
915
|
-
renderUi();
|
|
977
|
+
render();
|
|
916
978
|
return;
|
|
917
979
|
}
|
|
918
980
|
if (event.state.intent === 'selection-click' || event.state.intent === 'box-select') {
|
|
919
981
|
selectionController.cancelBoxSelection();
|
|
920
|
-
|
|
982
|
+
render();
|
|
921
983
|
}
|
|
922
|
-
requestEditorSceneFrame(state, `pointer-${event.state.intent}-cancel`);
|
|
923
984
|
},
|
|
924
985
|
onDoubleClick(event) {
|
|
925
986
|
selectionController.handleDoubleClick(event);
|
|
926
987
|
},
|
|
927
988
|
onWheel(event) {
|
|
928
|
-
if (!state.
|
|
929
|
-
|
|
930
|
-
renderUi();
|
|
931
|
-
}
|
|
989
|
+
if (!state.sceneCameraPreviewEnabled && state.sceneViewCamera?.handleWheel(event))
|
|
990
|
+
render();
|
|
932
991
|
},
|
|
933
992
|
onMovementKeysChange(event) {
|
|
934
|
-
|
|
935
|
-
beginEditorSceneContinuousRender(state, 'camera-flythrough-keys');
|
|
936
|
-
}
|
|
937
|
-
else {
|
|
938
|
-
endEditorSceneContinuousRender(state, 'camera-flythrough-keys');
|
|
939
|
-
}
|
|
993
|
+
sceneViewInteraction.updateMovementKeys(event.pressedMovementKeys);
|
|
940
994
|
renderUi();
|
|
941
995
|
},
|
|
942
996
|
});
|
|
@@ -946,7 +1000,7 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
946
1000
|
camera: world.camera,
|
|
947
1001
|
input: sceneViewInput,
|
|
948
1002
|
});
|
|
949
|
-
const
|
|
1003
|
+
const sceneCameraPreview = createBabylonSceneCameraPreviewController({
|
|
950
1004
|
babylon,
|
|
951
1005
|
scene: world.scene,
|
|
952
1006
|
editorCamera: world.camera,
|
|
@@ -955,13 +1009,17 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
955
1009
|
babylon,
|
|
956
1010
|
scene: world.scene,
|
|
957
1011
|
});
|
|
958
|
-
const document = state.session?.getState().workingDocument;
|
|
959
1012
|
if (document) {
|
|
960
1013
|
projection.projectNodes(options.documentAdapter.getProjectionNodes(document));
|
|
961
1014
|
const selection = state.session?.getState().selection ?? { selectedIds: [], activeId: null };
|
|
962
1015
|
projection.syncSelection(selection);
|
|
963
1016
|
gizmo.setSelection(selection);
|
|
964
1017
|
}
|
|
1018
|
+
const shadowPreview = createBabylonEditorShadowPreviewController({
|
|
1019
|
+
scene: world.scene,
|
|
1020
|
+
projection,
|
|
1021
|
+
options: initialRendering.shadowPreview,
|
|
1022
|
+
});
|
|
965
1023
|
const resize = () => {
|
|
966
1024
|
engine.resize?.();
|
|
967
1025
|
viewportRenderCoordinator.requestFrame('resize');
|
|
@@ -972,8 +1030,9 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
972
1030
|
state.world = world;
|
|
973
1031
|
state.grid = grid;
|
|
974
1032
|
state.projection = projection;
|
|
1033
|
+
state.shadowPreview = shadowPreview;
|
|
975
1034
|
state.gizmo = gizmo;
|
|
976
|
-
state.
|
|
1035
|
+
state.sceneCameraPreview = sceneCameraPreview;
|
|
977
1036
|
state.sceneViewInput = sceneViewInput;
|
|
978
1037
|
state.sceneViewCamera = sceneViewCamera;
|
|
979
1038
|
state.sceneViewMeasurement = sceneViewMeasurement;
|
|
@@ -982,22 +1041,21 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
982
1041
|
state.sceneRenderScheduler = sceneRenderScheduler;
|
|
983
1042
|
state.viewportRenderCoordinator = viewportRenderCoordinator;
|
|
984
1043
|
state.sceneFrameStats = sceneRenderScheduler.getStats();
|
|
1044
|
+
state.worldAppearanceKey = initialAppearanceKey;
|
|
1045
|
+
state.worldRenderingKey = initialRenderingKey;
|
|
985
1046
|
state.resizeHandler = resize;
|
|
986
|
-
|
|
1047
|
+
viewportRenderCoordinator.requestFrame('editor-world-created');
|
|
987
1048
|
}
|
|
988
1049
|
function disposeEditorWorld(state) {
|
|
989
|
-
state.
|
|
990
|
-
state.
|
|
991
|
-
state.sceneRenderScheduler?.dispose();
|
|
992
|
-
state.sceneRenderScheduler = null;
|
|
993
|
-
state.sceneFrameStats = null;
|
|
1050
|
+
state.sceneViewInteraction?.dispose();
|
|
1051
|
+
state.sceneViewInteraction = null;
|
|
994
1052
|
if (state.resizeHandler) {
|
|
995
1053
|
window.removeEventListener('resize', state.resizeHandler);
|
|
996
1054
|
state.resizeHandler = null;
|
|
997
1055
|
}
|
|
998
|
-
state.
|
|
999
|
-
state.
|
|
1000
|
-
state.
|
|
1056
|
+
state.sceneCameraPreview?.dispose();
|
|
1057
|
+
state.sceneCameraPreview = null;
|
|
1058
|
+
state.sceneCameraPreviewEnabled = false;
|
|
1001
1059
|
state.sceneViewCamera?.dispose();
|
|
1002
1060
|
state.sceneViewCamera = null;
|
|
1003
1061
|
state.sceneViewMeasurement?.dispose();
|
|
@@ -1008,6 +1066,11 @@ function disposeEditorWorld(state) {
|
|
|
1008
1066
|
state.sceneViewInput = null;
|
|
1009
1067
|
state.selectionController?.dispose();
|
|
1010
1068
|
state.selectionController = null;
|
|
1069
|
+
state.viewportRenderCoordinator?.dispose();
|
|
1070
|
+
state.viewportRenderCoordinator = null;
|
|
1071
|
+
state.sceneRenderScheduler?.dispose();
|
|
1072
|
+
state.sceneRenderScheduler = null;
|
|
1073
|
+
state.sceneFrameStats = null;
|
|
1011
1074
|
state.boxSelection = null;
|
|
1012
1075
|
state.viewportSpatialOverlay = createEmptyEditorViewportSpatialOverlayState();
|
|
1013
1076
|
state.viewportMeasurement = createEmptyViewportMeasurement();
|
|
@@ -1017,10 +1080,13 @@ function disposeEditorWorld(state) {
|
|
|
1017
1080
|
};
|
|
1018
1081
|
state.gizmo?.dispose();
|
|
1019
1082
|
state.gizmo = null;
|
|
1083
|
+
state.shadowPreview?.dispose();
|
|
1084
|
+
state.shadowPreview = null;
|
|
1020
1085
|
state.projection?.dispose();
|
|
1021
1086
|
state.projection = null;
|
|
1022
1087
|
state.grid?.dispose();
|
|
1023
1088
|
state.grid = null;
|
|
1089
|
+
state.engine?.stopRenderLoop?.();
|
|
1024
1090
|
state.world?.dispose();
|
|
1025
1091
|
state.engine?.dispose?.();
|
|
1026
1092
|
state.babylon = null;
|
|
@@ -1030,46 +1096,11 @@ function disposeEditorWorld(state) {
|
|
|
1030
1096
|
function requestEditorSceneFrame(state, reason) {
|
|
1031
1097
|
state.viewportRenderCoordinator?.requestFrame(reason);
|
|
1032
1098
|
}
|
|
1033
|
-
function
|
|
1034
|
-
state.viewportRenderCoordinator?.beginContinuous(reason);
|
|
1035
|
-
}
|
|
1036
|
-
function endEditorSceneContinuousRender(state, reason) {
|
|
1037
|
-
state.viewportRenderCoordinator?.endContinuous(reason);
|
|
1038
|
-
}
|
|
1039
|
-
function beginContinuousRenderForPointerIntent(state, intent) {
|
|
1040
|
-
const reason = continuousRenderReasonForPointerIntent(intent);
|
|
1041
|
-
if (reason)
|
|
1042
|
-
beginEditorSceneContinuousRender(state, reason);
|
|
1043
|
-
}
|
|
1044
|
-
function endContinuousRenderForPointerIntent(state, intent) {
|
|
1045
|
-
const reason = continuousRenderReasonForPointerIntent(intent);
|
|
1046
|
-
if (reason)
|
|
1047
|
-
endEditorSceneContinuousRender(state, reason);
|
|
1048
|
-
if (intent === 'gizmo-drag')
|
|
1049
|
-
endEditorSceneContinuousRender(state, 'gizmo-drag');
|
|
1050
|
-
if (intent === 'flythrough')
|
|
1051
|
-
endEditorSceneContinuousRender(state, 'camera-flythrough-keys');
|
|
1052
|
-
}
|
|
1053
|
-
function continuousRenderReasonForPointerIntent(intent) {
|
|
1054
|
-
switch (intent) {
|
|
1055
|
-
case 'gizmo-drag':
|
|
1056
|
-
case 'view-plane-move':
|
|
1057
|
-
case 'placement':
|
|
1058
|
-
case 'measurement':
|
|
1059
|
-
case 'orbit':
|
|
1060
|
-
case 'pan':
|
|
1061
|
-
case 'dolly':
|
|
1062
|
-
case 'flythrough':
|
|
1063
|
-
return `pointer-${intent}`;
|
|
1064
|
-
default:
|
|
1065
|
-
return null;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
async function runExclusive(state, renderUi, action) {
|
|
1099
|
+
async function runExclusive(state, render, action) {
|
|
1069
1100
|
if (state.busy)
|
|
1070
1101
|
return;
|
|
1071
1102
|
state.busy = true;
|
|
1072
|
-
|
|
1103
|
+
render();
|
|
1073
1104
|
try {
|
|
1074
1105
|
await action();
|
|
1075
1106
|
}
|
|
@@ -1082,7 +1113,7 @@ async function runExclusive(state, renderUi, action) {
|
|
|
1082
1113
|
}
|
|
1083
1114
|
finally {
|
|
1084
1115
|
state.busy = false;
|
|
1085
|
-
|
|
1116
|
+
render();
|
|
1086
1117
|
}
|
|
1087
1118
|
}
|
|
1088
1119
|
function selectItem(state, options, input) {
|
|
@@ -1598,12 +1629,73 @@ function syncSelectionToProjection(state, selection) {
|
|
|
1598
1629
|
state.gizmo?.refreshSelection();
|
|
1599
1630
|
requestEditorSceneFrame(state, 'projection-selection');
|
|
1600
1631
|
}
|
|
1632
|
+
function syncCurrentSelectionToSceneArtifacts(state) {
|
|
1633
|
+
syncSelectionToProjection(state, state.session?.getState().selection ?? { selectedIds: [], activeId: null });
|
|
1634
|
+
}
|
|
1601
1635
|
function invalidateEditorScene(state, reason, options) {
|
|
1602
|
-
if (options && state.
|
|
1603
|
-
|
|
1636
|
+
if (options && state.sceneCameraPreviewEnabled) {
|
|
1637
|
+
syncSceneCameraPreview(state, options);
|
|
1604
1638
|
}
|
|
1605
1639
|
state.viewportRenderCoordinator?.invalidateScene(reason);
|
|
1606
1640
|
}
|
|
1641
|
+
function syncEditorWorldAppearanceFromDocument(state, options, document, reason) {
|
|
1642
|
+
const appearance = resolveLocalEditorWorldAppearance(options, document);
|
|
1643
|
+
const key = serializeLocalEditorWorldAppearance(appearance);
|
|
1644
|
+
if (key === state.worldAppearanceKey)
|
|
1645
|
+
return;
|
|
1646
|
+
state.worldAppearanceKey = key;
|
|
1647
|
+
state.world?.setAppearance({
|
|
1648
|
+
clearColor: appearance.clearColor,
|
|
1649
|
+
sky: appearance.sky,
|
|
1650
|
+
});
|
|
1651
|
+
state.grid?.setColors(appearance.grid ?? {});
|
|
1652
|
+
requestEditorSceneFrame(state, reason);
|
|
1653
|
+
}
|
|
1654
|
+
function syncEditorWorldRenderingFromDocument(state, options, document, reason, refreshProjection = false) {
|
|
1655
|
+
const rendering = resolveLocalEditorWorldRendering(options, document);
|
|
1656
|
+
const key = serializeLocalEditorWorldRendering(rendering);
|
|
1657
|
+
if (key === state.worldRenderingKey) {
|
|
1658
|
+
if (refreshProjection)
|
|
1659
|
+
state.shadowPreview?.refresh();
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
state.worldRenderingKey = key;
|
|
1663
|
+
state.shadowPreview?.setOptions(rendering.shadowPreview);
|
|
1664
|
+
requestEditorSceneFrame(state, reason);
|
|
1665
|
+
}
|
|
1666
|
+
function resolveLocalEditorWorldAppearance(options, document) {
|
|
1667
|
+
const documentAppearance = document
|
|
1668
|
+
? options.documentAdapter.getWorldAppearance?.(document) ?? null
|
|
1669
|
+
: null;
|
|
1670
|
+
return {
|
|
1671
|
+
clearColor: documentAppearance?.clearColor ?? options.world?.clearColor,
|
|
1672
|
+
sky: hasEditorWorldAppearanceSky(documentAppearance) ? documentAppearance.sky : options.world?.sky,
|
|
1673
|
+
grid: documentAppearance?.grid ?? options.world?.grid,
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
function resolveLocalEditorWorldRendering(options, document) {
|
|
1677
|
+
const documentRendering = document
|
|
1678
|
+
? options.documentAdapter.getWorldRendering?.(document) ?? null
|
|
1679
|
+
: null;
|
|
1680
|
+
return {
|
|
1681
|
+
shadowPreview: documentRendering?.shadowPreview ?? null,
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
function hasEditorWorldAppearanceSky(appearance) {
|
|
1685
|
+
return !!appearance && Object.prototype.hasOwnProperty.call(appearance, 'sky');
|
|
1686
|
+
}
|
|
1687
|
+
function serializeLocalEditorWorldAppearance(appearance) {
|
|
1688
|
+
return JSON.stringify({
|
|
1689
|
+
clearColor: appearance.clearColor ?? null,
|
|
1690
|
+
sky: hasEditorWorldAppearanceSky(appearance) ? appearance.sky : null,
|
|
1691
|
+
grid: appearance.grid ?? null,
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
function serializeLocalEditorWorldRendering(rendering) {
|
|
1695
|
+
return JSON.stringify({
|
|
1696
|
+
shadowPreview: rendering.shadowPreview ?? null,
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1607
1699
|
function executeTransformAction(state, options, action) {
|
|
1608
1700
|
if (state.mode !== 'editor' || !state.session)
|
|
1609
1701
|
return false;
|
|
@@ -1701,6 +1793,66 @@ function createAssetFromBrowserIntent(state, options, assetId) {
|
|
|
1701
1793
|
}
|
|
1702
1794
|
return armAssetPlacement(state, options, assetId);
|
|
1703
1795
|
}
|
|
1796
|
+
function selectBrowserAsset(state, assetId) {
|
|
1797
|
+
if (state.selectedAssetId === assetId)
|
|
1798
|
+
return false;
|
|
1799
|
+
state.selectedAssetId = assetId;
|
|
1800
|
+
state.status = `Selected asset ${assetId}`;
|
|
1801
|
+
return true;
|
|
1802
|
+
}
|
|
1803
|
+
function handleBrowserAssetAction(state, options, input) {
|
|
1804
|
+
state.selectedAssetId = input.browserAssetId;
|
|
1805
|
+
if (input.actionId === 'asset.add-to-scene') {
|
|
1806
|
+
return addAssetToDocument(state, options, input.assetId).ok;
|
|
1807
|
+
}
|
|
1808
|
+
if (input.actionId === 'asset.place') {
|
|
1809
|
+
if (state.transformOperationSettings.placementMode === 'off') {
|
|
1810
|
+
state.transformOperationSettings = updateTransformOperationSettings(state.transformOperationSettings, {
|
|
1811
|
+
placementMode: 'ground',
|
|
1812
|
+
});
|
|
1813
|
+
state.gizmo?.setOperationSettings(state.transformOperationSettings);
|
|
1814
|
+
}
|
|
1815
|
+
return armAssetPlacement(state, options, input.assetId);
|
|
1816
|
+
}
|
|
1817
|
+
const patched = patchBrowserAssetAction(state, options, input);
|
|
1818
|
+
if (patched)
|
|
1819
|
+
return true;
|
|
1820
|
+
state.status = `Asset action unavailable: ${input.actionId}`;
|
|
1821
|
+
state.statusTone = 'warning';
|
|
1822
|
+
state.statusToneStatus = state.status;
|
|
1823
|
+
return true;
|
|
1824
|
+
}
|
|
1825
|
+
function patchBrowserAssetAction(state, options, input) {
|
|
1826
|
+
const session = state.session;
|
|
1827
|
+
const document = session?.getState().workingDocument ?? null;
|
|
1828
|
+
if (!session || !document || !options.documentAdapter.createAssetActionPatch)
|
|
1829
|
+
return false;
|
|
1830
|
+
const asset = findAssetByResolvedId(state, options, input.assetId);
|
|
1831
|
+
const patch = options.documentAdapter.createAssetActionPatch({
|
|
1832
|
+
...input,
|
|
1833
|
+
document,
|
|
1834
|
+
asset,
|
|
1835
|
+
activeId: session.getState().selection.activeId,
|
|
1836
|
+
selectedIds: [...session.getState().selection.selectedIds],
|
|
1837
|
+
});
|
|
1838
|
+
if (!patch)
|
|
1839
|
+
return false;
|
|
1840
|
+
const result = session.dispatch({
|
|
1841
|
+
type: 'document.patch',
|
|
1842
|
+
label: patch.label ?? `Asset action ${input.actionId}`,
|
|
1843
|
+
patch: patch.patch,
|
|
1844
|
+
});
|
|
1845
|
+
state.summary = summarizeDocument(options, result.workingDocument, session.getSource());
|
|
1846
|
+
state.status = patch.label ?? `Asset action ${input.actionId}`;
|
|
1847
|
+
state.statusTone = 'success';
|
|
1848
|
+
state.statusToneStatus = state.status;
|
|
1849
|
+
state.statusDetails = '';
|
|
1850
|
+
if (patch.reprojectIds?.length)
|
|
1851
|
+
reprojectProjectionForChangedIds(state, options, result.workingDocument, patch.reprojectIds);
|
|
1852
|
+
else if (patch.changedIds)
|
|
1853
|
+
syncProjectionForChangedIds(state, options, result.workingDocument, patch.changedIds);
|
|
1854
|
+
return true;
|
|
1855
|
+
}
|
|
1704
1856
|
function armAssetPlacement(state, options, assetId) {
|
|
1705
1857
|
if (state.mode !== 'editor')
|
|
1706
1858
|
return false;
|
|
@@ -1710,7 +1862,6 @@ function armAssetPlacement(state, options, assetId) {
|
|
|
1710
1862
|
return false;
|
|
1711
1863
|
state.armedPlacement = { assetId, asset };
|
|
1712
1864
|
state.gizmo?.setPlacementMarker(null);
|
|
1713
|
-
requestEditorSceneFrame(state, 'placement-arm-change');
|
|
1714
1865
|
state.status = `Placement armed: ${formatAssetLabel(asset, assetId)} (${state.transformOperationSettings.placementMode})`;
|
|
1715
1866
|
return true;
|
|
1716
1867
|
}
|
|
@@ -1740,7 +1891,6 @@ function commitArmedPlacement(state, options, event) {
|
|
|
1740
1891
|
if (!armed || !hit) {
|
|
1741
1892
|
state.gizmo?.setPlacementMarker(null);
|
|
1742
1893
|
state.status = 'Placement rejected: no hit';
|
|
1743
|
-
requestEditorSceneFrame(state, 'placement-marker-clear');
|
|
1744
1894
|
return true;
|
|
1745
1895
|
}
|
|
1746
1896
|
const patch = options.documentAdapter.createPlacedAssetPatch?.({
|
|
@@ -1790,11 +1940,8 @@ function pickArmedPlacementHit(state, event) {
|
|
|
1790
1940
|
: state.gizmo?.pickPlacementHit(event.clientX, event.clientY, mode) ?? null;
|
|
1791
1941
|
}
|
|
1792
1942
|
function clearArmedPlacement(state) {
|
|
1793
|
-
const hadArmedPlacement = !!state.armedPlacement;
|
|
1794
1943
|
state.armedPlacement = null;
|
|
1795
1944
|
state.gizmo?.setPlacementMarker(null);
|
|
1796
|
-
if (hadArmedPlacement)
|
|
1797
|
-
requestEditorSceneFrame(state, 'placement-clear');
|
|
1798
1945
|
}
|
|
1799
1946
|
function addAssetToDocument(state, options, assetId, createOptions = {}) {
|
|
1800
1947
|
if (state.mode !== 'editor') {
|
|
@@ -1845,7 +1992,7 @@ function addAssetToDocument(state, options, assetId, createOptions = {}) {
|
|
|
1845
1992
|
state.projection?.projectNode(projectedNode);
|
|
1846
1993
|
if (selectionResult)
|
|
1847
1994
|
syncSelectionToProjection(state, selectionResult.selection);
|
|
1848
|
-
invalidateEditorScene(state, 'projection-project-node'
|
|
1995
|
+
invalidateEditorScene(state, 'projection-project-node');
|
|
1849
1996
|
}
|
|
1850
1997
|
}
|
|
1851
1998
|
return {
|
|
@@ -1932,6 +2079,93 @@ function patchSerializedProperty(state, options, input) {
|
|
|
1932
2079
|
state.status = patch.label ?? `Patched ${payload.path}`;
|
|
1933
2080
|
return true;
|
|
1934
2081
|
}
|
|
2082
|
+
async function applyRenderingPropertyChange(state, options, input) {
|
|
2083
|
+
if (state.mode !== 'editor')
|
|
2084
|
+
return false;
|
|
2085
|
+
if (!state.session || !options.documentAdapter.onRenderingPropertyChange)
|
|
2086
|
+
return false;
|
|
2087
|
+
const document = state.session.getState().workingDocument;
|
|
2088
|
+
try {
|
|
2089
|
+
const result = await options.documentAdapter.onRenderingPropertyChange({
|
|
2090
|
+
document,
|
|
2091
|
+
sectionId: input.sectionId,
|
|
2092
|
+
systemId: input.systemId,
|
|
2093
|
+
path: input.path,
|
|
2094
|
+
value: input.value,
|
|
2095
|
+
control: input.control,
|
|
2096
|
+
valueType: input.valueType,
|
|
2097
|
+
commitMode: input.commitMode,
|
|
2098
|
+
source: input.source,
|
|
2099
|
+
});
|
|
2100
|
+
const resultObject = typeof result === 'object' && result != null ? result : null;
|
|
2101
|
+
const changed = result === true || resultObject?.changed === true || resultObject?.refreshWorldRendering === true;
|
|
2102
|
+
if (resultObject?.status) {
|
|
2103
|
+
state.status = resultObject.status;
|
|
2104
|
+
state.statusTone = resultObject.statusTone ?? 'default';
|
|
2105
|
+
state.statusToneStatus = state.status;
|
|
2106
|
+
state.statusDetails = resultObject.statusDetails ?? '';
|
|
2107
|
+
}
|
|
2108
|
+
else if (changed) {
|
|
2109
|
+
state.status = `Updated rendering: ${input.path}`;
|
|
2110
|
+
state.statusTone = 'success';
|
|
2111
|
+
state.statusToneStatus = state.status;
|
|
2112
|
+
state.statusDetails = '';
|
|
2113
|
+
}
|
|
2114
|
+
if (changed) {
|
|
2115
|
+
syncEditorWorldRenderingFromDocument(state, options, document, 'rendering-panel-change', true);
|
|
2116
|
+
requestEditorSceneFrame(state, 'rendering-panel-change');
|
|
2117
|
+
}
|
|
2118
|
+
return changed || !!resultObject?.status;
|
|
2119
|
+
}
|
|
2120
|
+
catch (error) {
|
|
2121
|
+
state.status = error instanceof Error ? error.message : String(error);
|
|
2122
|
+
state.statusTone = 'error';
|
|
2123
|
+
state.statusToneStatus = state.status;
|
|
2124
|
+
state.statusDetails = state.status;
|
|
2125
|
+
console.error('[LocalEditorHarness] rendering property change failed', error);
|
|
2126
|
+
return true;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
async function applyRenderingAction(state, options, input) {
|
|
2130
|
+
if (state.mode !== 'editor')
|
|
2131
|
+
return false;
|
|
2132
|
+
if (!state.session || !options.documentAdapter.onRenderingAction)
|
|
2133
|
+
return false;
|
|
2134
|
+
const document = state.session.getState().workingDocument;
|
|
2135
|
+
try {
|
|
2136
|
+
const result = await options.documentAdapter.onRenderingAction({
|
|
2137
|
+
document,
|
|
2138
|
+
actionId: input.actionId,
|
|
2139
|
+
});
|
|
2140
|
+
const resultObject = typeof result === 'object' && result != null ? result : null;
|
|
2141
|
+
const changed = result === true || resultObject?.changed === true || resultObject?.refreshWorldRendering === true;
|
|
2142
|
+
if (resultObject?.status) {
|
|
2143
|
+
state.status = resultObject.status;
|
|
2144
|
+
state.statusTone = resultObject.statusTone ?? 'default';
|
|
2145
|
+
state.statusToneStatus = state.status;
|
|
2146
|
+
state.statusDetails = resultObject.statusDetails ?? '';
|
|
2147
|
+
}
|
|
2148
|
+
else if (changed) {
|
|
2149
|
+
state.status = `Rendering action: ${input.actionId}`;
|
|
2150
|
+
state.statusTone = 'success';
|
|
2151
|
+
state.statusToneStatus = state.status;
|
|
2152
|
+
state.statusDetails = '';
|
|
2153
|
+
}
|
|
2154
|
+
if (changed) {
|
|
2155
|
+
syncEditorWorldRenderingFromDocument(state, options, document, 'rendering-panel-action', true);
|
|
2156
|
+
requestEditorSceneFrame(state, 'rendering-panel-action');
|
|
2157
|
+
}
|
|
2158
|
+
return changed || !!resultObject?.status;
|
|
2159
|
+
}
|
|
2160
|
+
catch (error) {
|
|
2161
|
+
state.status = error instanceof Error ? error.message : String(error);
|
|
2162
|
+
state.statusTone = 'error';
|
|
2163
|
+
state.statusToneStatus = state.status;
|
|
2164
|
+
state.statusDetails = state.status;
|
|
2165
|
+
console.error('[LocalEditorHarness] rendering action failed', error);
|
|
2166
|
+
return true;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
1935
2169
|
function createInspectorEditTransaction(state, options, document, input, targetIds) {
|
|
1936
2170
|
const property = findInspectorPropertyForEdit(state, options, document, input, targetIds);
|
|
1937
2171
|
if (!property) {
|
|
@@ -2138,6 +2372,22 @@ function restoreBatchTransformPreview(state, targets) {
|
|
|
2138
2372
|
transforms[target.id] = target.before;
|
|
2139
2373
|
state.projection?.setNodeTransformsPreview(transforms);
|
|
2140
2374
|
}
|
|
2375
|
+
function formatBlockedGizmoTransformStatus(event) {
|
|
2376
|
+
const target = event.targetIds.length > 1
|
|
2377
|
+
? `${event.targetIds.length} objects`
|
|
2378
|
+
: event.nodeId ?? event.activeId ?? 'selection';
|
|
2379
|
+
const failed = event.failedTargetId && event.targetIds.length > 1
|
|
2380
|
+
? ` at ${event.failedTargetId}`
|
|
2381
|
+
: '';
|
|
2382
|
+
return `Blocked ${event.tool} ${target}${failed}: ${formatTransformOperationBlockReason(event.reason)}`;
|
|
2383
|
+
}
|
|
2384
|
+
function formatTransformOperationBlockReason(reason) {
|
|
2385
|
+
if (reason === 'non-trs-representable')
|
|
2386
|
+
return 'result cannot be represented as position/rotation/scale';
|
|
2387
|
+
if (reason === 'non-invertible-parent')
|
|
2388
|
+
return 'parent transform cannot be inverted';
|
|
2389
|
+
return 'unsupported transform';
|
|
2390
|
+
}
|
|
2141
2391
|
function cancelActiveGizmoDrag(state) {
|
|
2142
2392
|
state.gizmo?.cancelDrag();
|
|
2143
2393
|
}
|
|
@@ -2233,7 +2483,9 @@ function rebuildProjectionFromDocument(state, options, document, selection) {
|
|
|
2233
2483
|
state.projection?.rebuild(options.documentAdapter.getProjectionNodes(document));
|
|
2234
2484
|
const sanitized = sanitizeSelection(state, options, document, selection);
|
|
2235
2485
|
syncSelectionToProjection(state, sanitized ?? selection);
|
|
2236
|
-
|
|
2486
|
+
syncEditorWorldAppearanceFromDocument(state, options, document, 'world-appearance-rebuild');
|
|
2487
|
+
syncEditorWorldRenderingFromDocument(state, options, document, 'world-rendering-rebuild', true);
|
|
2488
|
+
invalidateEditorScene(state, 'projection-rebuild');
|
|
2237
2489
|
}
|
|
2238
2490
|
function syncProjectionForDispatchResult(state, options, result, changedId) {
|
|
2239
2491
|
if (result.documentChanged && changedId && result.workingDocument) {
|
|
@@ -2249,8 +2501,12 @@ function syncProjectionForDispatchResult(state, options, result, changedId) {
|
|
|
2249
2501
|
const sanitized = result.workingDocument ? sanitizeSelection(state, options, result.workingDocument, selection) : null;
|
|
2250
2502
|
syncSelectionToProjection(state, sanitized ?? selection);
|
|
2251
2503
|
}
|
|
2504
|
+
if (result.documentChanged && result.workingDocument) {
|
|
2505
|
+
syncEditorWorldAppearanceFromDocument(state, options, result.workingDocument, 'world-appearance-dispatch-result');
|
|
2506
|
+
syncEditorWorldRenderingFromDocument(state, options, result.workingDocument, 'world-rendering-dispatch-result', true);
|
|
2507
|
+
}
|
|
2252
2508
|
if (result.documentChanged || result.selectionChanged) {
|
|
2253
|
-
invalidateEditorScene(state, 'projection-dispatch-result'
|
|
2509
|
+
invalidateEditorScene(state, 'projection-dispatch-result');
|
|
2254
2510
|
}
|
|
2255
2511
|
}
|
|
2256
2512
|
function syncProjectionForChangedIds(state, options, document, changedIds) {
|
|
@@ -2261,7 +2517,9 @@ function syncProjectionForChangedIds(state, options, document, changedIds) {
|
|
|
2261
2517
|
}
|
|
2262
2518
|
const selection = state.session?.getState().selection ?? { selectedIds: [], activeId: null };
|
|
2263
2519
|
syncSelectionToProjection(state, selection);
|
|
2264
|
-
|
|
2520
|
+
syncEditorWorldAppearanceFromDocument(state, options, document, 'world-appearance-sync-changed-ids');
|
|
2521
|
+
syncEditorWorldRenderingFromDocument(state, options, document, 'world-rendering-sync-changed-ids', true);
|
|
2522
|
+
invalidateEditorScene(state, 'projection-sync-changed-ids');
|
|
2265
2523
|
}
|
|
2266
2524
|
function reprojectProjectionForChangedIds(state, options, document, changedIds) {
|
|
2267
2525
|
for (const changedId of changedIds) {
|
|
@@ -2269,31 +2527,30 @@ function reprojectProjectionForChangedIds(state, options, document, changedIds)
|
|
|
2269
2527
|
if (projectedNode)
|
|
2270
2528
|
state.projection?.projectNode(projectedNode);
|
|
2271
2529
|
}
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
state
|
|
2530
|
+
syncCurrentSelectionToSceneArtifacts(state);
|
|
2531
|
+
syncEditorWorldAppearanceFromDocument(state, options, document, 'world-appearance-reproject-changed-ids');
|
|
2532
|
+
syncEditorWorldRenderingFromDocument(state, options, document, 'world-rendering-reproject-changed-ids', true);
|
|
2275
2533
|
invalidateEditorScene(state, 'projection-reproject-changed-ids', options);
|
|
2276
2534
|
}
|
|
2277
|
-
function
|
|
2535
|
+
function setSceneCameraPreviewEnabled(state, options, enabled) {
|
|
2278
2536
|
if (!enabled) {
|
|
2279
|
-
state.
|
|
2280
|
-
state.
|
|
2281
|
-
state.status = '
|
|
2282
|
-
requestEditorSceneFrame(state, '
|
|
2537
|
+
state.sceneCameraPreviewEnabled = false;
|
|
2538
|
+
state.sceneCameraPreview?.setActive(false);
|
|
2539
|
+
state.status = 'Scene Camera preview disabled';
|
|
2540
|
+
requestEditorSceneFrame(state, 'scene-camera-preview-toggle');
|
|
2283
2541
|
return true;
|
|
2284
2542
|
}
|
|
2285
|
-
state.
|
|
2286
|
-
if (!
|
|
2287
|
-
state.
|
|
2288
|
-
state.status = '
|
|
2543
|
+
state.sceneCameraPreviewEnabled = true;
|
|
2544
|
+
if (!syncSceneCameraPreview(state, options)) {
|
|
2545
|
+
state.sceneCameraPreviewEnabled = false;
|
|
2546
|
+
state.status = 'Scene Camera preview unavailable';
|
|
2289
2547
|
state.statusTone = 'warning';
|
|
2290
2548
|
state.statusToneStatus = state.status;
|
|
2291
2549
|
state.statusDetails = 'The current document did not provide a Main Camera preview rig.';
|
|
2292
|
-
requestEditorSceneFrame(state, 'main-camera-preview-toggle');
|
|
2293
2550
|
return true;
|
|
2294
2551
|
}
|
|
2295
|
-
state.status = '
|
|
2296
|
-
requestEditorSceneFrame(state, '
|
|
2552
|
+
state.status = 'Scene Camera preview enabled';
|
|
2553
|
+
requestEditorSceneFrame(state, 'scene-camera-preview-toggle');
|
|
2297
2554
|
return true;
|
|
2298
2555
|
}
|
|
2299
2556
|
function setGridVisible(state, visible) {
|
|
@@ -2309,16 +2566,16 @@ function setGridVisible(state, visible) {
|
|
|
2309
2566
|
requestEditorSceneFrame(state, 'grid-visible-change');
|
|
2310
2567
|
return true;
|
|
2311
2568
|
}
|
|
2312
|
-
function
|
|
2313
|
-
const controller = state.
|
|
2569
|
+
function syncSceneCameraPreview(state, options) {
|
|
2570
|
+
const controller = state.sceneCameraPreview;
|
|
2314
2571
|
if (!controller)
|
|
2315
2572
|
return false;
|
|
2316
|
-
if (!state.
|
|
2573
|
+
if (!state.sceneCameraPreviewEnabled) {
|
|
2317
2574
|
controller.setActive(false);
|
|
2318
2575
|
return false;
|
|
2319
2576
|
}
|
|
2320
2577
|
const document = state.session?.getState().workingDocument ?? null;
|
|
2321
|
-
const rig = document ? options.documentAdapter.
|
|
2578
|
+
const rig = document ? options.documentAdapter.getSceneCameraPreviewRig?.(document) ?? null : null;
|
|
2322
2579
|
if (!rig) {
|
|
2323
2580
|
controller.setActive(false);
|
|
2324
2581
|
return false;
|
|
@@ -2365,9 +2622,9 @@ function syncViewportMeasurementState(state) {
|
|
|
2365
2622
|
if (measurement)
|
|
2366
2623
|
state.viewportMeasurement = measurement;
|
|
2367
2624
|
}
|
|
2368
|
-
function
|
|
2625
|
+
function hasSceneCameraPreviewRig(state, options) {
|
|
2369
2626
|
const document = state.session?.getState().workingDocument ?? null;
|
|
2370
|
-
return !!document && !!options.documentAdapter.
|
|
2627
|
+
return !!document && !!options.documentAdapter.getSceneCameraPreviewRig?.(document);
|
|
2371
2628
|
}
|
|
2372
2629
|
function createUiState(state, options) {
|
|
2373
2630
|
const sessionState = state.session?.getState();
|
|
@@ -2392,8 +2649,13 @@ function createUiState(state, options) {
|
|
|
2392
2649
|
const inspectorMultiObject = document && inspectorMultiObjectBase
|
|
2393
2650
|
? withRuntimeInspectorSections(state, options, document, inspectorMultiObjectBase)
|
|
2394
2651
|
: inspectorMultiObjectBase;
|
|
2395
|
-
const
|
|
2396
|
-
.
|
|
2652
|
+
const documentAssetItems = document
|
|
2653
|
+
? options.documentAdapter.getBrowserAssetItems?.(document) ?? []
|
|
2654
|
+
: [];
|
|
2655
|
+
const assets = dedupeLocalEditorBrowserAssetItems([
|
|
2656
|
+
...state.assets.map(asset => toBrowserAssetItem(options, asset)),
|
|
2657
|
+
...documentAssetItems,
|
|
2658
|
+
]);
|
|
2397
2659
|
return {
|
|
2398
2660
|
mode: state.mode,
|
|
2399
2661
|
busy: state.busy,
|
|
@@ -2403,6 +2665,7 @@ function createUiState(state, options) {
|
|
|
2403
2665
|
summary: state.summary,
|
|
2404
2666
|
assetFilter: state.assetFilter,
|
|
2405
2667
|
assets,
|
|
2668
|
+
selectedAssetId: state.selectedAssetId,
|
|
2406
2669
|
assetCountLabel: `${assets.length} assets`,
|
|
2407
2670
|
hierarchy: document ? options.documentAdapter.getHierarchyItems(document) : [],
|
|
2408
2671
|
selectedIds,
|
|
@@ -2415,6 +2678,7 @@ function createUiState(state, options) {
|
|
|
2415
2678
|
serializedMultiObject,
|
|
2416
2679
|
inspectorObject,
|
|
2417
2680
|
inspectorMultiObject,
|
|
2681
|
+
renderingPanel: document ? options.documentAdapter.getRenderingPanelState?.(document) ?? null : null,
|
|
2418
2682
|
boxSelection: state.boxSelection,
|
|
2419
2683
|
coordinateAxes: options.world?.coordinateAxes === false
|
|
2420
2684
|
? null
|
|
@@ -2439,9 +2703,9 @@ function createUiState(state, options) {
|
|
|
2439
2703
|
sceneFrameStats: state.sceneFrameStats
|
|
2440
2704
|
? { ...state.sceneFrameStats, activeReasons: [...state.sceneFrameStats.activeReasons] }
|
|
2441
2705
|
: null,
|
|
2442
|
-
|
|
2443
|
-
enabled: state.
|
|
2444
|
-
available:
|
|
2706
|
+
sceneCameraPreview: {
|
|
2707
|
+
enabled: state.sceneCameraPreviewEnabled,
|
|
2708
|
+
available: hasSceneCameraPreviewRig(state, options),
|
|
2445
2709
|
},
|
|
2446
2710
|
grid: {
|
|
2447
2711
|
visible: state.gridVisible,
|
|
@@ -2473,7 +2737,7 @@ function createSceneViewCoordinateAxesState(state) {
|
|
|
2473
2737
|
return null;
|
|
2474
2738
|
return {
|
|
2475
2739
|
projectionMode: readSceneViewFreeCameraProjectionMode(state, camera),
|
|
2476
|
-
projectionToggleDisabled: state.
|
|
2740
|
+
projectionToggleDisabled: state.sceneCameraPreviewEnabled,
|
|
2477
2741
|
axes: [
|
|
2478
2742
|
createSceneViewCoordinateAxis('x', 'X', '#ff4b70', { x: 1, y: 0, z: 0 }, right, up, forward),
|
|
2479
2743
|
createSceneViewCoordinateAxis('y', 'Y', '#75ff42', { x: 0, y: 1, z: 0 }, right, up, forward),
|
|
@@ -2755,7 +3019,7 @@ function findAssetByResolvedId(state, options, assetId) {
|
|
|
2755
3019
|
selected = { asset, item };
|
|
2756
3020
|
}
|
|
2757
3021
|
}
|
|
2758
|
-
return selected?.asset;
|
|
3022
|
+
return selected?.asset ?? null;
|
|
2759
3023
|
}
|
|
2760
3024
|
function toBrowserAssetItem(options, asset) {
|
|
2761
3025
|
return options.worldAdapter.toBrowserAssetItem?.(asset)
|
|
@@ -2769,6 +3033,8 @@ function toBrowserAssetItem(options, asset) {
|
|
|
2769
3033
|
origin: asset.origin,
|
|
2770
3034
|
dedupeKey: asset.dedupeKey,
|
|
2771
3035
|
placeable: asset.placeable,
|
|
3036
|
+
preview: asset.preview,
|
|
3037
|
+
material: asset.material,
|
|
2772
3038
|
meta: asset.meta,
|
|
2773
3039
|
disabled: asset.disabled ?? asset.placeable === false,
|
|
2774
3040
|
};
|