@fps-games/editor 0.1.2-beta.2 → 0.1.3-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 +39 -7
- package/dist/local-editor-harness.d.ts.map +1 -1
- package/dist/local-editor-harness.js +379 -119
- package/dist/local-editor-harness.js.map +1 -1
- package/node_modules/@fps-games/babylon-renderer/dist/index.d.ts +5 -0
- package/node_modules/@fps-games/babylon-renderer/dist/index.d.ts.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/dist/index.js +5 -0
- package/node_modules/@fps-games/babylon-renderer/dist/index.js.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/dist/lighting/directional-light-angles.d.ts +17 -0
- package/node_modules/@fps-games/babylon-renderer/dist/lighting/directional-light-angles.d.ts.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/dist/lighting/directional-light-angles.js +64 -0
- package/node_modules/@fps-games/babylon-renderer/dist/lighting/directional-light-angles.js.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-shaders.d.ts +7 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-shaders.d.ts.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-shaders.js +176 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-shaders.js.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-system.d.ts +5 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-system.d.ts.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-system.js +837 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/planar-shadow-system.js.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.d.ts +96 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.d.ts.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.js +45 -0
- package/node_modules/@fps-games/babylon-renderer/dist/shadows/types.js.map +1 -0
- package/node_modules/@fps-games/babylon-renderer/package.json +20 -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 +23 -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 +34 -4
- 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/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/types.d.ts +2 -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-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-types.d.ts +147 -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 +2 -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 +1060 -86
- 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/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,6 +1,6 @@
|
|
|
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
6
|
import { createLocalEditorSceneViewInteractionRuntime, } from './local-editor-scene-view-interaction-runtime';
|
|
@@ -13,21 +13,28 @@ export function createLocalEditorHarness(options) {
|
|
|
13
13
|
source: null,
|
|
14
14
|
assets: [],
|
|
15
15
|
assetFilter: '',
|
|
16
|
+
selectedAssetId: null,
|
|
16
17
|
babylon: null,
|
|
17
18
|
engine: null,
|
|
18
19
|
world: null,
|
|
19
20
|
grid: null,
|
|
20
21
|
gridVisible: true,
|
|
21
22
|
projection: null,
|
|
23
|
+
shadowPreview: null,
|
|
22
24
|
gizmo: null,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
sceneCameraPreview: null,
|
|
26
|
+
sceneCameraPreviewEnabled: false,
|
|
25
27
|
sceneViewInput: null,
|
|
26
28
|
sceneViewCamera: null,
|
|
27
29
|
sceneViewInteraction: null,
|
|
28
30
|
sceneViewMeasurement: null,
|
|
29
31
|
sceneViewSpatialOverlay: null,
|
|
30
32
|
selectionController: null,
|
|
33
|
+
sceneRenderScheduler: null,
|
|
34
|
+
viewportRenderCoordinator: null,
|
|
35
|
+
sceneFrameStats: null,
|
|
36
|
+
worldAppearanceKey: '',
|
|
37
|
+
worldRenderingKey: '',
|
|
31
38
|
boxSelection: null,
|
|
32
39
|
transformTool: 'select',
|
|
33
40
|
transformSpace: 'world',
|
|
@@ -38,9 +45,6 @@ export function createLocalEditorHarness(options) {
|
|
|
38
45
|
viewportSpatialOverlay: createEmptyEditorViewportSpatialOverlayState(),
|
|
39
46
|
duplicateDrag: null,
|
|
40
47
|
armedPlacement: null,
|
|
41
|
-
sceneRenderScheduler: null,
|
|
42
|
-
viewportRenderCoordinator: null,
|
|
43
|
-
sceneFrameStats: null,
|
|
44
48
|
resizeHandler: null,
|
|
45
49
|
status: 'Game running',
|
|
46
50
|
statusTone: 'default',
|
|
@@ -85,6 +89,14 @@ export function createLocalEditorHarness(options) {
|
|
|
85
89
|
if (createAssetFromBrowserIntent(state, options, assetId))
|
|
86
90
|
harness.render();
|
|
87
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
|
+
},
|
|
88
100
|
onSelectHierarchyItem: (input) => {
|
|
89
101
|
if (selectItem(state, options, input))
|
|
90
102
|
harness.render();
|
|
@@ -146,25 +158,43 @@ export function createLocalEditorHarness(options) {
|
|
|
146
158
|
if (patched || state.status !== previousStatus)
|
|
147
159
|
harness.render();
|
|
148
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,
|
|
149
179
|
onTransformToolChange: (tool) => {
|
|
150
180
|
state.transformTool = tool;
|
|
151
181
|
state.transformConstraint = normalizeTransformConstraint(tool, state.transformConstraint);
|
|
152
182
|
state.gizmo?.setTool(tool);
|
|
153
183
|
state.gizmo?.setConstraint(state.transformConstraint);
|
|
154
|
-
requestEditorSceneFrame(state, 'transform-tool-change');
|
|
155
184
|
harness.render();
|
|
185
|
+
requestEditorSceneFrame(state, 'transform-tool-change');
|
|
156
186
|
},
|
|
157
187
|
onTransformSpaceChange: (space) => {
|
|
158
188
|
state.transformSpace = space;
|
|
159
189
|
state.gizmo?.setSpace(space);
|
|
160
|
-
requestEditorSceneFrame(state, 'transform-space-change');
|
|
161
190
|
harness.render();
|
|
191
|
+
requestEditorSceneFrame(state, 'transform-space-change');
|
|
162
192
|
},
|
|
163
193
|
onTransformConstraintChange: (constraint) => {
|
|
164
194
|
state.transformConstraint = normalizeTransformConstraint(state.transformTool, constraint);
|
|
165
195
|
state.gizmo?.setConstraint(state.transformConstraint);
|
|
166
|
-
requestEditorSceneFrame(state, 'transform-constraint-change');
|
|
167
196
|
harness.render();
|
|
197
|
+
requestEditorSceneFrame(state, 'transform-constraint-change');
|
|
168
198
|
},
|
|
169
199
|
onTransformSnapEnabledChange: (enabled) => {
|
|
170
200
|
state.transformOperationSettings = updateTransformOperationSettings(state.transformOperationSettings, {
|
|
@@ -175,8 +205,8 @@ export function createLocalEditorHarness(options) {
|
|
|
175
205
|
});
|
|
176
206
|
state.gizmo?.setOperationSettings(state.transformOperationSettings);
|
|
177
207
|
state.status = enabled ? 'Transform snap enabled' : 'Transform snap disabled';
|
|
178
|
-
requestEditorSceneFrame(state, 'transform-operation-settings-change');
|
|
179
208
|
harness.render();
|
|
209
|
+
requestEditorSceneFrame(state, 'transform-operation-settings-change');
|
|
180
210
|
},
|
|
181
211
|
onTransformSnapStepChange: (input) => {
|
|
182
212
|
const value = normalizePositiveStep(input.value);
|
|
@@ -192,8 +222,8 @@ export function createLocalEditorHarness(options) {
|
|
|
192
222
|
state.transformOperationSettings = updateTransformOperationSettings(state.transformOperationSettings, { snap });
|
|
193
223
|
state.gizmo?.setOperationSettings(state.transformOperationSettings);
|
|
194
224
|
state.status = `Transform snap ${input.kind} step ${value}`;
|
|
195
|
-
requestEditorSceneFrame(state, 'transform-operation-settings-change');
|
|
196
225
|
harness.render();
|
|
226
|
+
requestEditorSceneFrame(state, 'transform-operation-settings-change');
|
|
197
227
|
},
|
|
198
228
|
onPlacementModeChange: (mode) => {
|
|
199
229
|
state.transformOperationSettings = updateTransformOperationSettings(state.transformOperationSettings, {
|
|
@@ -203,8 +233,8 @@ export function createLocalEditorHarness(options) {
|
|
|
203
233
|
if (state.transformOperationSettings.placementMode === 'off')
|
|
204
234
|
clearArmedPlacement(state);
|
|
205
235
|
state.status = `Placement mode: ${state.transformOperationSettings.placementMode}`;
|
|
206
|
-
requestEditorSceneFrame(state, 'placement-mode-change');
|
|
207
236
|
harness.render();
|
|
237
|
+
requestEditorSceneFrame(state, 'placement-mode-change');
|
|
208
238
|
},
|
|
209
239
|
onTransformAction: (action) => {
|
|
210
240
|
if (executeTransformAction(state, options, action))
|
|
@@ -230,8 +260,8 @@ export function createLocalEditorHarness(options) {
|
|
|
230
260
|
if (clearViewportMeasurement(state))
|
|
231
261
|
harness.render();
|
|
232
262
|
},
|
|
233
|
-
|
|
234
|
-
if (
|
|
263
|
+
onSceneCameraPreviewToggle: (enabled) => {
|
|
264
|
+
if (setSceneCameraPreviewEnabled(state, options, enabled))
|
|
235
265
|
harness.render();
|
|
236
266
|
},
|
|
237
267
|
onGridVisibleChange: (visible) => {
|
|
@@ -248,20 +278,17 @@ export function createLocalEditorHarness(options) {
|
|
|
248
278
|
},
|
|
249
279
|
onCancelActiveOperation: () => {
|
|
250
280
|
cancelActiveOperation(state);
|
|
251
|
-
requestEditorSceneFrame(state, 'cancel-active-operation');
|
|
252
281
|
harness.render();
|
|
253
282
|
},
|
|
254
283
|
},
|
|
255
284
|
});
|
|
256
|
-
const renderUi = () => {
|
|
257
|
-
syncViewportCameraState(state);
|
|
258
|
-
syncViewportMeasurementState(state);
|
|
259
|
-
syncViewportSpatialOverlay(state);
|
|
260
|
-
ui.update(createUiState(state, options));
|
|
261
|
-
};
|
|
262
285
|
harness = {
|
|
263
286
|
render() {
|
|
264
|
-
|
|
287
|
+
syncSceneCameraPreview(state, options);
|
|
288
|
+
syncViewportCameraState(state);
|
|
289
|
+
syncViewportMeasurementState(state);
|
|
290
|
+
syncViewportSpatialOverlay(state);
|
|
291
|
+
ui.update(createUiState(state, options));
|
|
265
292
|
},
|
|
266
293
|
notifyViewportRevealed(reason = 'viewport-revealed') {
|
|
267
294
|
state.viewportRenderCoordinator?.requestRevealFrame(reason);
|
|
@@ -512,11 +539,11 @@ function cloneViewportMeasurement(measurement) {
|
|
|
512
539
|
};
|
|
513
540
|
}
|
|
514
541
|
function setViewportViewPreset(state, preset) {
|
|
515
|
-
if (state.
|
|
516
|
-
state.status = 'Viewport view unavailable during
|
|
542
|
+
if (state.sceneCameraPreviewEnabled) {
|
|
543
|
+
state.status = 'Viewport view unavailable during Scene Camera preview';
|
|
517
544
|
state.statusTone = 'warning';
|
|
518
545
|
state.statusToneStatus = state.status;
|
|
519
|
-
state.statusDetails = 'Disable
|
|
546
|
+
state.statusDetails = 'Disable Scene Camera preview before switching editor viewport views.';
|
|
520
547
|
return true;
|
|
521
548
|
}
|
|
522
549
|
const nextPreset = normalizeViewportViewPreset(preset);
|
|
@@ -535,15 +562,15 @@ function setViewportViewPreset(state, preset) {
|
|
|
535
562
|
state.statusTone = 'default';
|
|
536
563
|
state.statusToneStatus = state.status;
|
|
537
564
|
state.statusDetails = '';
|
|
538
|
-
requestEditorSceneFrame(state, 'viewport-view-preset
|
|
565
|
+
requestEditorSceneFrame(state, 'viewport-view-preset');
|
|
539
566
|
return true;
|
|
540
567
|
}
|
|
541
568
|
function setViewportProjectionMode(state, mode) {
|
|
542
|
-
if (state.
|
|
543
|
-
state.status = 'Viewport projection unavailable during
|
|
569
|
+
if (state.sceneCameraPreviewEnabled) {
|
|
570
|
+
state.status = 'Viewport projection unavailable during Scene Camera preview';
|
|
544
571
|
state.statusTone = 'warning';
|
|
545
572
|
state.statusToneStatus = state.status;
|
|
546
|
-
state.statusDetails = 'Disable
|
|
573
|
+
state.statusDetails = 'Disable Scene Camera preview before switching editor viewport projection.';
|
|
547
574
|
return true;
|
|
548
575
|
}
|
|
549
576
|
const nextMode = normalizeViewportProjectionMode(mode);
|
|
@@ -559,7 +586,7 @@ function setViewportProjectionMode(state, mode) {
|
|
|
559
586
|
state.statusTone = 'default';
|
|
560
587
|
state.statusToneStatus = state.status;
|
|
561
588
|
state.statusDetails = '';
|
|
562
|
-
requestEditorSceneFrame(state, 'viewport-projection-mode
|
|
589
|
+
requestEditorSceneFrame(state, 'viewport-projection-mode');
|
|
563
590
|
return true;
|
|
564
591
|
}
|
|
565
592
|
function setViewportOverlaySettings(state, settings) {
|
|
@@ -608,6 +635,7 @@ function setViewportUtilityTool(state, tool) {
|
|
|
608
635
|
state.statusTone = 'default';
|
|
609
636
|
state.statusToneStatus = state.status;
|
|
610
637
|
state.statusDetails = '';
|
|
638
|
+
requestEditorSceneFrame(state, 'viewport-utility-tool');
|
|
611
639
|
return true;
|
|
612
640
|
}
|
|
613
641
|
function clearViewportMeasurement(state) {
|
|
@@ -620,6 +648,7 @@ function clearViewportMeasurement(state) {
|
|
|
620
648
|
state.statusTone = 'default';
|
|
621
649
|
state.statusToneStatus = state.status;
|
|
622
650
|
state.statusDetails = '';
|
|
651
|
+
requestEditorSceneFrame(state, 'viewport-measurement-clear');
|
|
623
652
|
return true;
|
|
624
653
|
}
|
|
625
654
|
export function applyLocalEditorHarnessViewportMeasurementPointerStart(state, event) {
|
|
@@ -713,19 +742,28 @@ function validateTransformActionSelection(state, action) {
|
|
|
713
742
|
}
|
|
714
743
|
async function createEditorWorld(state, options, renderUi, updateSceneFrameStats) {
|
|
715
744
|
disposeEditorWorld(state);
|
|
745
|
+
const render = (reason = 'editor-world-update') => {
|
|
746
|
+
renderUi();
|
|
747
|
+
requestEditorSceneFrame(state, reason);
|
|
748
|
+
};
|
|
716
749
|
const canvas = options.worldAdapter.getCanvas();
|
|
717
750
|
if (!canvas)
|
|
718
751
|
throw new Error('Editor canvas not found');
|
|
719
752
|
const babylon = await options.worldAdapter.loadBabylon();
|
|
720
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);
|
|
721
759
|
const world = createBabylonEditorWorld({
|
|
722
760
|
engine,
|
|
723
761
|
canvas,
|
|
724
762
|
babylon,
|
|
725
763
|
cameraTarget: options.world?.cameraTarget,
|
|
726
764
|
cameraRadius: options.world?.cameraRadius,
|
|
727
|
-
clearColor:
|
|
728
|
-
sky:
|
|
765
|
+
clearColor: initialAppearance.clearColor,
|
|
766
|
+
sky: initialAppearance.sky,
|
|
729
767
|
useRightHandedSystem: options.world?.useRightHandedSystem,
|
|
730
768
|
enableDefaultCameraControls: false,
|
|
731
769
|
});
|
|
@@ -752,6 +790,8 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
752
790
|
getCamera: () => world.scene?.activeCamera ?? world.camera ?? null,
|
|
753
791
|
getEditorCamera: () => world.camera ?? null,
|
|
754
792
|
}) ?? null;
|
|
793
|
+
if (initialAppearance.grid)
|
|
794
|
+
grid?.setColors(initialAppearance.grid);
|
|
755
795
|
grid?.setVisible(state.gridVisible);
|
|
756
796
|
const projection = createBabylonEditorProjection({
|
|
757
797
|
babylon,
|
|
@@ -775,10 +815,10 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
775
815
|
state.status = event.targetIds.length > 1
|
|
776
816
|
? `Dragging ${event.duplicate ? 'duplicate ' : ''}${event.tool} ${event.targetIds.length} objects`
|
|
777
817
|
: `Dragging ${event.duplicate ? 'duplicate ' : ''}${event.tool} ${event.nodeId ?? event.activeId ?? 'selection'}`;
|
|
778
|
-
|
|
818
|
+
render();
|
|
779
819
|
},
|
|
780
820
|
onDragUpdate() {
|
|
781
|
-
|
|
821
|
+
render();
|
|
782
822
|
},
|
|
783
823
|
onDragEnd(event) {
|
|
784
824
|
sceneViewInteraction.endGizmoDrag();
|
|
@@ -790,14 +830,13 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
790
830
|
onDragCancel(event) {
|
|
791
831
|
sceneViewInteraction.endGizmoDrag();
|
|
792
832
|
if (event.duplicate && cancelDuplicateDrag(state, options)) {
|
|
793
|
-
|
|
833
|
+
render();
|
|
794
834
|
return;
|
|
795
835
|
}
|
|
796
|
-
requestEditorSceneFrame(state, 'gizmo-drag-cancel');
|
|
797
836
|
state.status = event.targetIds.length > 1
|
|
798
837
|
? `Canceled ${event.tool} ${event.targetIds.length} objects`
|
|
799
838
|
: `Canceled ${event.tool} ${event.nodeId ?? event.activeId ?? 'selection'}`;
|
|
800
|
-
|
|
839
|
+
render();
|
|
801
840
|
},
|
|
802
841
|
onDragBlocked(event) {
|
|
803
842
|
sceneViewInteraction.endGizmoDrag();
|
|
@@ -826,15 +865,15 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
826
865
|
isOperationBlocked: () => state.gizmo?.getState().dragPhase === 'dragging',
|
|
827
866
|
onSelectionCommand(command) {
|
|
828
867
|
if (dispatchSelectionCommand(state, options, command))
|
|
829
|
-
|
|
868
|
+
render();
|
|
830
869
|
},
|
|
831
870
|
onFocusIntent(nodeId) {
|
|
832
871
|
if (focusProjectionNode(state, nodeId))
|
|
833
|
-
|
|
872
|
+
render();
|
|
834
873
|
},
|
|
835
874
|
onBoxSelectionChange(box) {
|
|
836
875
|
state.boxSelection = box;
|
|
837
|
-
|
|
876
|
+
render();
|
|
838
877
|
},
|
|
839
878
|
});
|
|
840
879
|
const sceneViewMeasurement = createBabylonSceneViewMeasurementController({
|
|
@@ -853,7 +892,7 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
853
892
|
sceneViewInteraction.beginPointerIntent(event.state.intent);
|
|
854
893
|
if (event.state.intent === 'measurement') {
|
|
855
894
|
if (handleViewportMeasurementStart(state, event.originalEvent))
|
|
856
|
-
|
|
895
|
+
render();
|
|
857
896
|
return;
|
|
858
897
|
}
|
|
859
898
|
if (event.state.intent === 'gizmo-drag') {
|
|
@@ -862,100 +901,93 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
862
901
|
}
|
|
863
902
|
if (event.state.intent === 'placement') {
|
|
864
903
|
if (previewArmedPlacement(state, event.originalEvent))
|
|
865
|
-
|
|
904
|
+
render();
|
|
866
905
|
return;
|
|
867
906
|
}
|
|
868
907
|
if (event.state.intent === 'view-plane-move') {
|
|
869
908
|
if (gizmo.beginViewPlaneMove(event.originalEvent))
|
|
870
|
-
|
|
909
|
+
render();
|
|
871
910
|
return;
|
|
872
911
|
}
|
|
873
912
|
if (event.state.intent === 'selection-click' || event.state.intent === 'box-select') {
|
|
874
913
|
selectionController.beginPointerSelection(event.originalEvent);
|
|
875
|
-
requestEditorSceneFrame(state,
|
|
914
|
+
requestEditorSceneFrame(state, 'viewport-pointer-selection-start');
|
|
876
915
|
}
|
|
877
916
|
},
|
|
878
917
|
onPointerIntentMove(event) {
|
|
879
918
|
if (event.state.intent === 'measurement') {
|
|
880
919
|
if (handleViewportMeasurementMove(state, event.originalEvent))
|
|
881
|
-
|
|
920
|
+
render();
|
|
882
921
|
return;
|
|
883
922
|
}
|
|
884
923
|
if (event.state.intent === 'placement') {
|
|
885
924
|
if (previewArmedPlacement(state, event.originalEvent))
|
|
886
|
-
|
|
925
|
+
render();
|
|
887
926
|
return;
|
|
888
927
|
}
|
|
889
928
|
if (event.state.intent === 'view-plane-move') {
|
|
890
929
|
if (gizmo.updateViewPlaneMove(event.originalEvent))
|
|
891
|
-
|
|
930
|
+
render();
|
|
892
931
|
return;
|
|
893
932
|
}
|
|
894
|
-
if (!state.
|
|
895
|
-
|
|
933
|
+
if (!state.sceneCameraPreviewEnabled && state.sceneViewCamera?.handlePointerIntentMove(event)) {
|
|
934
|
+
render();
|
|
896
935
|
return;
|
|
897
936
|
}
|
|
898
937
|
if (event.state.intent === 'selection-click' || event.state.intent === 'box-select') {
|
|
899
938
|
selectionController.updatePointerSelection(event.originalEvent, event.state.intent);
|
|
939
|
+
requestEditorSceneFrame(state, 'viewport-pointer-selection-update');
|
|
900
940
|
}
|
|
901
|
-
requestEditorSceneFrame(state, `pointer-${event.state.intent}`);
|
|
902
941
|
},
|
|
903
942
|
onPointerIntentEnd(event) {
|
|
904
943
|
sceneViewInteraction.endPointerIntent(event.state.intent);
|
|
905
944
|
if (event.state.intent === 'measurement') {
|
|
906
945
|
if (handleViewportMeasurementEnd(state))
|
|
907
|
-
|
|
946
|
+
render();
|
|
908
947
|
return;
|
|
909
948
|
}
|
|
910
949
|
if (event.state.intent === 'placement') {
|
|
911
950
|
if (commitArmedPlacement(state, options, event.originalEvent))
|
|
912
|
-
|
|
951
|
+
render();
|
|
913
952
|
return;
|
|
914
953
|
}
|
|
915
954
|
if (event.state.intent === 'view-plane-move') {
|
|
916
|
-
if (gizmo.endViewPlaneMove(event.originalEvent))
|
|
917
|
-
|
|
918
|
-
renderUi();
|
|
919
|
-
}
|
|
955
|
+
if (gizmo.endViewPlaneMove(event.originalEvent))
|
|
956
|
+
render();
|
|
920
957
|
return;
|
|
921
958
|
}
|
|
922
959
|
if (event.state.intent === 'selection-click' || event.state.intent === 'box-select') {
|
|
923
960
|
selectionController.endPointerSelection(event.originalEvent, event.state.intent);
|
|
961
|
+
requestEditorSceneFrame(state, 'viewport-pointer-selection-end');
|
|
924
962
|
}
|
|
925
|
-
requestEditorSceneFrame(state, `pointer-${event.state.intent}-end`);
|
|
926
963
|
},
|
|
927
964
|
onPointerIntentCancel(event) {
|
|
928
965
|
sceneViewInteraction.cancelPointerIntent(event.state.intent);
|
|
929
966
|
if (event.state.intent === 'measurement') {
|
|
930
|
-
|
|
967
|
+
render();
|
|
931
968
|
return;
|
|
932
969
|
}
|
|
933
970
|
if (event.state.intent === 'placement') {
|
|
934
971
|
state.gizmo?.setPlacementMarker(null);
|
|
935
|
-
|
|
936
|
-
renderUi();
|
|
972
|
+
render();
|
|
937
973
|
return;
|
|
938
974
|
}
|
|
939
975
|
if (event.state.intent === 'view-plane-move') {
|
|
940
976
|
gizmo.cancelDrag();
|
|
941
|
-
|
|
942
|
-
renderUi();
|
|
977
|
+
render();
|
|
943
978
|
return;
|
|
944
979
|
}
|
|
945
980
|
if (event.state.intent === 'selection-click' || event.state.intent === 'box-select') {
|
|
946
981
|
selectionController.cancelBoxSelection();
|
|
947
|
-
|
|
982
|
+
render();
|
|
948
983
|
}
|
|
949
|
-
requestEditorSceneFrame(state, `pointer-${event.state.intent}-cancel`);
|
|
950
984
|
},
|
|
951
985
|
onDoubleClick(event) {
|
|
952
986
|
selectionController.handleDoubleClick(event);
|
|
953
987
|
},
|
|
954
988
|
onWheel(event) {
|
|
955
|
-
if (!state.
|
|
956
|
-
|
|
957
|
-
renderUi();
|
|
958
|
-
}
|
|
989
|
+
if (!state.sceneCameraPreviewEnabled && state.sceneViewCamera?.handleWheel(event))
|
|
990
|
+
render();
|
|
959
991
|
},
|
|
960
992
|
onMovementKeysChange(event) {
|
|
961
993
|
sceneViewInteraction.updateMovementKeys(event.pressedMovementKeys);
|
|
@@ -968,7 +1000,7 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
968
1000
|
camera: world.camera,
|
|
969
1001
|
input: sceneViewInput,
|
|
970
1002
|
});
|
|
971
|
-
const
|
|
1003
|
+
const sceneCameraPreview = createBabylonSceneCameraPreviewController({
|
|
972
1004
|
babylon,
|
|
973
1005
|
scene: world.scene,
|
|
974
1006
|
editorCamera: world.camera,
|
|
@@ -977,13 +1009,17 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
977
1009
|
babylon,
|
|
978
1010
|
scene: world.scene,
|
|
979
1011
|
});
|
|
980
|
-
const document = state.session?.getState().workingDocument;
|
|
981
1012
|
if (document) {
|
|
982
1013
|
projection.projectNodes(options.documentAdapter.getProjectionNodes(document));
|
|
983
1014
|
const selection = state.session?.getState().selection ?? { selectedIds: [], activeId: null };
|
|
984
1015
|
projection.syncSelection(selection);
|
|
985
1016
|
gizmo.setSelection(selection);
|
|
986
1017
|
}
|
|
1018
|
+
const shadowPreview = createBabylonEditorShadowPreviewController({
|
|
1019
|
+
scene: world.scene,
|
|
1020
|
+
projection,
|
|
1021
|
+
options: initialRendering.shadowPreview,
|
|
1022
|
+
});
|
|
987
1023
|
const resize = () => {
|
|
988
1024
|
engine.resize?.();
|
|
989
1025
|
viewportRenderCoordinator.requestFrame('resize');
|
|
@@ -994,8 +1030,9 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
994
1030
|
state.world = world;
|
|
995
1031
|
state.grid = grid;
|
|
996
1032
|
state.projection = projection;
|
|
1033
|
+
state.shadowPreview = shadowPreview;
|
|
997
1034
|
state.gizmo = gizmo;
|
|
998
|
-
state.
|
|
1035
|
+
state.sceneCameraPreview = sceneCameraPreview;
|
|
999
1036
|
state.sceneViewInput = sceneViewInput;
|
|
1000
1037
|
state.sceneViewCamera = sceneViewCamera;
|
|
1001
1038
|
state.sceneViewMeasurement = sceneViewMeasurement;
|
|
@@ -1004,24 +1041,21 @@ async function createEditorWorld(state, options, renderUi, updateSceneFrameStats
|
|
|
1004
1041
|
state.sceneRenderScheduler = sceneRenderScheduler;
|
|
1005
1042
|
state.viewportRenderCoordinator = viewportRenderCoordinator;
|
|
1006
1043
|
state.sceneFrameStats = sceneRenderScheduler.getStats();
|
|
1044
|
+
state.worldAppearanceKey = initialAppearanceKey;
|
|
1045
|
+
state.worldRenderingKey = initialRenderingKey;
|
|
1007
1046
|
state.resizeHandler = resize;
|
|
1008
|
-
|
|
1047
|
+
viewportRenderCoordinator.requestFrame('editor-world-created');
|
|
1009
1048
|
}
|
|
1010
1049
|
function disposeEditorWorld(state) {
|
|
1011
1050
|
state.sceneViewInteraction?.dispose();
|
|
1012
1051
|
state.sceneViewInteraction = null;
|
|
1013
|
-
state.viewportRenderCoordinator?.dispose();
|
|
1014
|
-
state.viewportRenderCoordinator = null;
|
|
1015
|
-
state.sceneRenderScheduler?.dispose();
|
|
1016
|
-
state.sceneRenderScheduler = null;
|
|
1017
|
-
state.sceneFrameStats = null;
|
|
1018
1052
|
if (state.resizeHandler) {
|
|
1019
1053
|
window.removeEventListener('resize', state.resizeHandler);
|
|
1020
1054
|
state.resizeHandler = null;
|
|
1021
1055
|
}
|
|
1022
|
-
state.
|
|
1023
|
-
state.
|
|
1024
|
-
state.
|
|
1056
|
+
state.sceneCameraPreview?.dispose();
|
|
1057
|
+
state.sceneCameraPreview = null;
|
|
1058
|
+
state.sceneCameraPreviewEnabled = false;
|
|
1025
1059
|
state.sceneViewCamera?.dispose();
|
|
1026
1060
|
state.sceneViewCamera = null;
|
|
1027
1061
|
state.sceneViewMeasurement?.dispose();
|
|
@@ -1032,6 +1066,11 @@ function disposeEditorWorld(state) {
|
|
|
1032
1066
|
state.sceneViewInput = null;
|
|
1033
1067
|
state.selectionController?.dispose();
|
|
1034
1068
|
state.selectionController = null;
|
|
1069
|
+
state.viewportRenderCoordinator?.dispose();
|
|
1070
|
+
state.viewportRenderCoordinator = null;
|
|
1071
|
+
state.sceneRenderScheduler?.dispose();
|
|
1072
|
+
state.sceneRenderScheduler = null;
|
|
1073
|
+
state.sceneFrameStats = null;
|
|
1035
1074
|
state.boxSelection = null;
|
|
1036
1075
|
state.viewportSpatialOverlay = createEmptyEditorViewportSpatialOverlayState();
|
|
1037
1076
|
state.viewportMeasurement = createEmptyViewportMeasurement();
|
|
@@ -1041,10 +1080,13 @@ function disposeEditorWorld(state) {
|
|
|
1041
1080
|
};
|
|
1042
1081
|
state.gizmo?.dispose();
|
|
1043
1082
|
state.gizmo = null;
|
|
1083
|
+
state.shadowPreview?.dispose();
|
|
1084
|
+
state.shadowPreview = null;
|
|
1044
1085
|
state.projection?.dispose();
|
|
1045
1086
|
state.projection = null;
|
|
1046
1087
|
state.grid?.dispose();
|
|
1047
1088
|
state.grid = null;
|
|
1089
|
+
state.engine?.stopRenderLoop?.();
|
|
1048
1090
|
state.world?.dispose();
|
|
1049
1091
|
state.engine?.dispose?.();
|
|
1050
1092
|
state.babylon = null;
|
|
@@ -1054,11 +1096,11 @@ function disposeEditorWorld(state) {
|
|
|
1054
1096
|
function requestEditorSceneFrame(state, reason) {
|
|
1055
1097
|
state.viewportRenderCoordinator?.requestFrame(reason);
|
|
1056
1098
|
}
|
|
1057
|
-
async function runExclusive(state,
|
|
1099
|
+
async function runExclusive(state, render, action) {
|
|
1058
1100
|
if (state.busy)
|
|
1059
1101
|
return;
|
|
1060
1102
|
state.busy = true;
|
|
1061
|
-
|
|
1103
|
+
render();
|
|
1062
1104
|
try {
|
|
1063
1105
|
await action();
|
|
1064
1106
|
}
|
|
@@ -1071,7 +1113,7 @@ async function runExclusive(state, renderUi, action) {
|
|
|
1071
1113
|
}
|
|
1072
1114
|
finally {
|
|
1073
1115
|
state.busy = false;
|
|
1074
|
-
|
|
1116
|
+
render();
|
|
1075
1117
|
}
|
|
1076
1118
|
}
|
|
1077
1119
|
function selectItem(state, options, input) {
|
|
@@ -1591,11 +1633,69 @@ function syncCurrentSelectionToSceneArtifacts(state) {
|
|
|
1591
1633
|
syncSelectionToProjection(state, state.session?.getState().selection ?? { selectedIds: [], activeId: null });
|
|
1592
1634
|
}
|
|
1593
1635
|
function invalidateEditorScene(state, reason, options) {
|
|
1594
|
-
if (options && state.
|
|
1595
|
-
|
|
1636
|
+
if (options && state.sceneCameraPreviewEnabled) {
|
|
1637
|
+
syncSceneCameraPreview(state, options);
|
|
1596
1638
|
}
|
|
1597
1639
|
state.viewportRenderCoordinator?.invalidateScene(reason);
|
|
1598
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
|
+
}
|
|
1599
1699
|
function executeTransformAction(state, options, action) {
|
|
1600
1700
|
if (state.mode !== 'editor' || !state.session)
|
|
1601
1701
|
return false;
|
|
@@ -1693,6 +1793,66 @@ function createAssetFromBrowserIntent(state, options, assetId) {
|
|
|
1693
1793
|
}
|
|
1694
1794
|
return armAssetPlacement(state, options, assetId);
|
|
1695
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
|
+
}
|
|
1696
1856
|
function armAssetPlacement(state, options, assetId) {
|
|
1697
1857
|
if (state.mode !== 'editor')
|
|
1698
1858
|
return false;
|
|
@@ -1702,7 +1862,6 @@ function armAssetPlacement(state, options, assetId) {
|
|
|
1702
1862
|
return false;
|
|
1703
1863
|
state.armedPlacement = { assetId, asset };
|
|
1704
1864
|
state.gizmo?.setPlacementMarker(null);
|
|
1705
|
-
requestEditorSceneFrame(state, 'placement-arm-change');
|
|
1706
1865
|
state.status = `Placement armed: ${formatAssetLabel(asset, assetId)} (${state.transformOperationSettings.placementMode})`;
|
|
1707
1866
|
return true;
|
|
1708
1867
|
}
|
|
@@ -1732,7 +1891,6 @@ function commitArmedPlacement(state, options, event) {
|
|
|
1732
1891
|
if (!armed || !hit) {
|
|
1733
1892
|
state.gizmo?.setPlacementMarker(null);
|
|
1734
1893
|
state.status = 'Placement rejected: no hit';
|
|
1735
|
-
requestEditorSceneFrame(state, 'placement-marker-clear');
|
|
1736
1894
|
return true;
|
|
1737
1895
|
}
|
|
1738
1896
|
const patch = options.documentAdapter.createPlacedAssetPatch?.({
|
|
@@ -1782,11 +1940,8 @@ function pickArmedPlacementHit(state, event) {
|
|
|
1782
1940
|
: state.gizmo?.pickPlacementHit(event.clientX, event.clientY, mode) ?? null;
|
|
1783
1941
|
}
|
|
1784
1942
|
function clearArmedPlacement(state) {
|
|
1785
|
-
const hadArmedPlacement = !!state.armedPlacement;
|
|
1786
1943
|
state.armedPlacement = null;
|
|
1787
1944
|
state.gizmo?.setPlacementMarker(null);
|
|
1788
|
-
if (hadArmedPlacement)
|
|
1789
|
-
requestEditorSceneFrame(state, 'placement-clear');
|
|
1790
1945
|
}
|
|
1791
1946
|
function addAssetToDocument(state, options, assetId, createOptions = {}) {
|
|
1792
1947
|
if (state.mode !== 'editor') {
|
|
@@ -1837,7 +1992,7 @@ function addAssetToDocument(state, options, assetId, createOptions = {}) {
|
|
|
1837
1992
|
state.projection?.projectNode(projectedNode);
|
|
1838
1993
|
if (selectionResult)
|
|
1839
1994
|
syncSelectionToProjection(state, selectionResult.selection);
|
|
1840
|
-
invalidateEditorScene(state, 'projection-project-node'
|
|
1995
|
+
invalidateEditorScene(state, 'projection-project-node');
|
|
1841
1996
|
}
|
|
1842
1997
|
}
|
|
1843
1998
|
return {
|
|
@@ -1924,6 +2079,93 @@ function patchSerializedProperty(state, options, input) {
|
|
|
1924
2079
|
state.status = patch.label ?? `Patched ${payload.path}`;
|
|
1925
2080
|
return true;
|
|
1926
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
|
+
}
|
|
1927
2169
|
function createInspectorEditTransaction(state, options, document, input, targetIds) {
|
|
1928
2170
|
const property = findInspectorPropertyForEdit(state, options, document, input, targetIds);
|
|
1929
2171
|
if (!property) {
|
|
@@ -2241,7 +2483,9 @@ function rebuildProjectionFromDocument(state, options, document, selection) {
|
|
|
2241
2483
|
state.projection?.rebuild(options.documentAdapter.getProjectionNodes(document));
|
|
2242
2484
|
const sanitized = sanitizeSelection(state, options, document, selection);
|
|
2243
2485
|
syncSelectionToProjection(state, sanitized ?? selection);
|
|
2244
|
-
|
|
2486
|
+
syncEditorWorldAppearanceFromDocument(state, options, document, 'world-appearance-rebuild');
|
|
2487
|
+
syncEditorWorldRenderingFromDocument(state, options, document, 'world-rendering-rebuild', true);
|
|
2488
|
+
invalidateEditorScene(state, 'projection-rebuild');
|
|
2245
2489
|
}
|
|
2246
2490
|
function syncProjectionForDispatchResult(state, options, result, changedId) {
|
|
2247
2491
|
if (result.documentChanged && changedId && result.workingDocument) {
|
|
@@ -2257,8 +2501,12 @@ function syncProjectionForDispatchResult(state, options, result, changedId) {
|
|
|
2257
2501
|
const sanitized = result.workingDocument ? sanitizeSelection(state, options, result.workingDocument, selection) : null;
|
|
2258
2502
|
syncSelectionToProjection(state, sanitized ?? selection);
|
|
2259
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
|
+
}
|
|
2260
2508
|
if (result.documentChanged || result.selectionChanged) {
|
|
2261
|
-
invalidateEditorScene(state, 'projection-dispatch-result'
|
|
2509
|
+
invalidateEditorScene(state, 'projection-dispatch-result');
|
|
2262
2510
|
}
|
|
2263
2511
|
}
|
|
2264
2512
|
function syncProjectionForChangedIds(state, options, document, changedIds) {
|
|
@@ -2269,7 +2517,9 @@ function syncProjectionForChangedIds(state, options, document, changedIds) {
|
|
|
2269
2517
|
}
|
|
2270
2518
|
const selection = state.session?.getState().selection ?? { selectedIds: [], activeId: null };
|
|
2271
2519
|
syncSelectionToProjection(state, selection);
|
|
2272
|
-
|
|
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');
|
|
2273
2523
|
}
|
|
2274
2524
|
function reprojectProjectionForChangedIds(state, options, document, changedIds) {
|
|
2275
2525
|
for (const changedId of changedIds) {
|
|
@@ -2278,28 +2528,29 @@ function reprojectProjectionForChangedIds(state, options, document, changedIds)
|
|
|
2278
2528
|
state.projection?.projectNode(projectedNode);
|
|
2279
2529
|
}
|
|
2280
2530
|
syncCurrentSelectionToSceneArtifacts(state);
|
|
2531
|
+
syncEditorWorldAppearanceFromDocument(state, options, document, 'world-appearance-reproject-changed-ids');
|
|
2532
|
+
syncEditorWorldRenderingFromDocument(state, options, document, 'world-rendering-reproject-changed-ids', true);
|
|
2281
2533
|
invalidateEditorScene(state, 'projection-reproject-changed-ids', options);
|
|
2282
2534
|
}
|
|
2283
|
-
function
|
|
2535
|
+
function setSceneCameraPreviewEnabled(state, options, enabled) {
|
|
2284
2536
|
if (!enabled) {
|
|
2285
|
-
state.
|
|
2286
|
-
state.
|
|
2287
|
-
state.status = '
|
|
2288
|
-
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');
|
|
2289
2541
|
return true;
|
|
2290
2542
|
}
|
|
2291
|
-
state.
|
|
2292
|
-
if (!
|
|
2293
|
-
state.
|
|
2294
|
-
state.status = '
|
|
2543
|
+
state.sceneCameraPreviewEnabled = true;
|
|
2544
|
+
if (!syncSceneCameraPreview(state, options)) {
|
|
2545
|
+
state.sceneCameraPreviewEnabled = false;
|
|
2546
|
+
state.status = 'Scene Camera preview unavailable';
|
|
2295
2547
|
state.statusTone = 'warning';
|
|
2296
2548
|
state.statusToneStatus = state.status;
|
|
2297
2549
|
state.statusDetails = 'The current document did not provide a Main Camera preview rig.';
|
|
2298
|
-
requestEditorSceneFrame(state, 'main-camera-preview-toggle');
|
|
2299
2550
|
return true;
|
|
2300
2551
|
}
|
|
2301
|
-
state.status = '
|
|
2302
|
-
requestEditorSceneFrame(state, '
|
|
2552
|
+
state.status = 'Scene Camera preview enabled';
|
|
2553
|
+
requestEditorSceneFrame(state, 'scene-camera-preview-toggle');
|
|
2303
2554
|
return true;
|
|
2304
2555
|
}
|
|
2305
2556
|
function setGridVisible(state, visible) {
|
|
@@ -2315,16 +2566,16 @@ function setGridVisible(state, visible) {
|
|
|
2315
2566
|
requestEditorSceneFrame(state, 'grid-visible-change');
|
|
2316
2567
|
return true;
|
|
2317
2568
|
}
|
|
2318
|
-
function
|
|
2319
|
-
const controller = state.
|
|
2569
|
+
function syncSceneCameraPreview(state, options) {
|
|
2570
|
+
const controller = state.sceneCameraPreview;
|
|
2320
2571
|
if (!controller)
|
|
2321
2572
|
return false;
|
|
2322
|
-
if (!state.
|
|
2573
|
+
if (!state.sceneCameraPreviewEnabled) {
|
|
2323
2574
|
controller.setActive(false);
|
|
2324
2575
|
return false;
|
|
2325
2576
|
}
|
|
2326
2577
|
const document = state.session?.getState().workingDocument ?? null;
|
|
2327
|
-
const rig = document ? options.documentAdapter.
|
|
2578
|
+
const rig = document ? options.documentAdapter.getSceneCameraPreviewRig?.(document) ?? null : null;
|
|
2328
2579
|
if (!rig) {
|
|
2329
2580
|
controller.setActive(false);
|
|
2330
2581
|
return false;
|
|
@@ -2371,9 +2622,9 @@ function syncViewportMeasurementState(state) {
|
|
|
2371
2622
|
if (measurement)
|
|
2372
2623
|
state.viewportMeasurement = measurement;
|
|
2373
2624
|
}
|
|
2374
|
-
function
|
|
2625
|
+
function hasSceneCameraPreviewRig(state, options) {
|
|
2375
2626
|
const document = state.session?.getState().workingDocument ?? null;
|
|
2376
|
-
return !!document && !!options.documentAdapter.
|
|
2627
|
+
return !!document && !!options.documentAdapter.getSceneCameraPreviewRig?.(document);
|
|
2377
2628
|
}
|
|
2378
2629
|
function createUiState(state, options) {
|
|
2379
2630
|
const sessionState = state.session?.getState();
|
|
@@ -2398,8 +2649,13 @@ function createUiState(state, options) {
|
|
|
2398
2649
|
const inspectorMultiObject = document && inspectorMultiObjectBase
|
|
2399
2650
|
? withRuntimeInspectorSections(state, options, document, inspectorMultiObjectBase)
|
|
2400
2651
|
: inspectorMultiObjectBase;
|
|
2401
|
-
const
|
|
2402
|
-
.
|
|
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
|
+
]);
|
|
2403
2659
|
return {
|
|
2404
2660
|
mode: state.mode,
|
|
2405
2661
|
busy: state.busy,
|
|
@@ -2409,6 +2665,7 @@ function createUiState(state, options) {
|
|
|
2409
2665
|
summary: state.summary,
|
|
2410
2666
|
assetFilter: state.assetFilter,
|
|
2411
2667
|
assets,
|
|
2668
|
+
selectedAssetId: state.selectedAssetId,
|
|
2412
2669
|
assetCountLabel: `${assets.length} assets`,
|
|
2413
2670
|
hierarchy: document ? options.documentAdapter.getHierarchyItems(document) : [],
|
|
2414
2671
|
selectedIds,
|
|
@@ -2421,6 +2678,7 @@ function createUiState(state, options) {
|
|
|
2421
2678
|
serializedMultiObject,
|
|
2422
2679
|
inspectorObject,
|
|
2423
2680
|
inspectorMultiObject,
|
|
2681
|
+
renderingPanel: document ? options.documentAdapter.getRenderingPanelState?.(document) ?? null : null,
|
|
2424
2682
|
boxSelection: state.boxSelection,
|
|
2425
2683
|
coordinateAxes: options.world?.coordinateAxes === false
|
|
2426
2684
|
? null
|
|
@@ -2445,9 +2703,9 @@ function createUiState(state, options) {
|
|
|
2445
2703
|
sceneFrameStats: state.sceneFrameStats
|
|
2446
2704
|
? { ...state.sceneFrameStats, activeReasons: [...state.sceneFrameStats.activeReasons] }
|
|
2447
2705
|
: null,
|
|
2448
|
-
|
|
2449
|
-
enabled: state.
|
|
2450
|
-
available:
|
|
2706
|
+
sceneCameraPreview: {
|
|
2707
|
+
enabled: state.sceneCameraPreviewEnabled,
|
|
2708
|
+
available: hasSceneCameraPreviewRig(state, options),
|
|
2451
2709
|
},
|
|
2452
2710
|
grid: {
|
|
2453
2711
|
visible: state.gridVisible,
|
|
@@ -2479,7 +2737,7 @@ function createSceneViewCoordinateAxesState(state) {
|
|
|
2479
2737
|
return null;
|
|
2480
2738
|
return {
|
|
2481
2739
|
projectionMode: readSceneViewFreeCameraProjectionMode(state, camera),
|
|
2482
|
-
projectionToggleDisabled: state.
|
|
2740
|
+
projectionToggleDisabled: state.sceneCameraPreviewEnabled,
|
|
2483
2741
|
axes: [
|
|
2484
2742
|
createSceneViewCoordinateAxis('x', 'X', '#ff4b70', { x: 1, y: 0, z: 0 }, right, up, forward),
|
|
2485
2743
|
createSceneViewCoordinateAxis('y', 'Y', '#75ff42', { x: 0, y: 1, z: 0 }, right, up, forward),
|
|
@@ -2761,7 +3019,7 @@ function findAssetByResolvedId(state, options, assetId) {
|
|
|
2761
3019
|
selected = { asset, item };
|
|
2762
3020
|
}
|
|
2763
3021
|
}
|
|
2764
|
-
return selected?.asset;
|
|
3022
|
+
return selected?.asset ?? null;
|
|
2765
3023
|
}
|
|
2766
3024
|
function toBrowserAssetItem(options, asset) {
|
|
2767
3025
|
return options.worldAdapter.toBrowserAssetItem?.(asset)
|
|
@@ -2775,6 +3033,8 @@ function toBrowserAssetItem(options, asset) {
|
|
|
2775
3033
|
origin: asset.origin,
|
|
2776
3034
|
dedupeKey: asset.dedupeKey,
|
|
2777
3035
|
placeable: asset.placeable,
|
|
3036
|
+
preview: asset.preview,
|
|
3037
|
+
material: asset.material,
|
|
2778
3038
|
meta: asset.meta,
|
|
2779
3039
|
disabled: asset.disabled ?? asset.placeable === false,
|
|
2780
3040
|
};
|