@fps-games/editor 0.1.5-beta.4 → 0.1.6-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/build-info.json +16 -16
- package/dist/local-editor-agent-actions.d.ts +8 -1
- package/dist/local-editor-agent-actions.d.ts.map +1 -1
- package/dist/local-editor-agent-actions.js +171 -12
- package/dist/local-editor-agent-actions.js.map +1 -1
- package/dist/local-editor-agent-context.d.ts +29 -1
- package/dist/local-editor-agent-context.d.ts.map +1 -1
- package/dist/local-editor-agent-context.js +403 -0
- package/dist/local-editor-agent-context.js.map +1 -1
- package/dist/local-editor-harness.d.ts +103 -5
- package/dist/local-editor-harness.d.ts.map +1 -1
- package/dist/local-editor-harness.js +1982 -101
- package/dist/local-editor-harness.js.map +1 -1
- package/dist/local-editor-marker-graph.d.ts +92 -0
- package/dist/local-editor-marker-graph.d.ts.map +1 -0
- package/dist/local-editor-marker-graph.js +1432 -0
- package/dist/local-editor-marker-graph.js.map +1 -0
- package/dist/local-editor-performance-stats.js +1 -1
- package/dist/local-editor-performance-stats.js.map +1 -1
- package/dist/playable-babylon-facade.d.ts +2 -2
- package/dist/playable-babylon-facade.d.ts.map +1 -1
- package/dist/playable-babylon-facade.js +1 -1
- package/dist/playable-babylon-facade.js.map +1 -1
- package/dist/playable-local-editor-host.d.ts +52 -7
- package/dist/playable-local-editor-host.d.ts.map +1 -1
- package/dist/playable-local-editor-host.js +23 -1
- package/dist/playable-local-editor-host.js.map +1 -1
- package/dist/playable-sdk.d.ts +4 -2
- package/dist/playable-sdk.d.ts.map +1 -1
- package/dist/playable-sdk.js +2 -1
- package/dist/playable-sdk.js.map +1 -1
- package/node_modules/@fps-games/babylon-renderer/package.json +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/camera-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/camera-controller.js +11 -0
- package/node_modules/@fps-games/editor-babylon/dist/camera-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/editor-sky.d.ts +3 -1
- 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 +114 -18
- 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 +1 -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 +3 -0
- 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 +3 -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 +3 -0
- package/node_modules/@fps-games/editor-babylon/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.d.ts +52 -0
- package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.js +1395 -0
- package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.js.map +1 -0
- 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 +4 -65
- package/node_modules/@fps-games/editor-babylon/dist/material-property-adapter.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.d.ts +28 -0
- package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.js +235 -0
- package/node_modules/@fps-games/editor-babylon/dist/material-slot-source-resolver.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.d.ts +37 -0
- package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.js +451 -0
- package/node_modules/@fps-games/editor-babylon/dist/prefab-comparison-preview-controller.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.js +24 -10
- package/node_modules/@fps-games/editor-babylon/dist/projection-asset-container-importer.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.d.ts +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.js +20 -4
- package/node_modules/@fps-games/editor-babylon/dist/projection-pick-resolver.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.d.ts +9 -0
- package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.js +64 -0
- package/node_modules/@fps-games/editor-babylon/dist/projection-source-owner-path.js.map +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts +44 -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 +311 -70
- package/node_modules/@fps-games/editor-babylon/dist/projection.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/package.json +5 -5
- package/node_modules/@fps-games/editor-browser/dist/index.d.ts +5 -2
- package/node_modules/@fps-games/editor-browser/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/index.js +2 -0
- package/node_modules/@fps-games/editor-browser/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.d.ts +5 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.js +23 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.js.map +1 -0
- 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 +69 -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-attach-dialog.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-attach-dialog.js +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-attach-dialog.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts +2 -0
- 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 +38 -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-tree.d.ts +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.d.ts.map +1 -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 +8 -2
- 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-icons.d.ts +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js +7 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panel-registry.js +1 -1
- 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 +4 -0
- 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 +1471 -29
- 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-prefab-stage.d.ts +52 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.js +131 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shared.js +9 -2
- 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-types.d.ts +234 -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 +10 -1
- 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 +3 -1
- 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 +4 -3
- 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 +1274 -55
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/package.json +3 -3
- package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts +1 -1
- package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts +2 -0
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.js +1 -0
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.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-playable-sdk/dist/asset-library/catalog.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.js +17 -3
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/document.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/document.js +2 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/document.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/types.d.ts +3 -2
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.d.ts +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.js +68 -2
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/document.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.d.ts +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.js +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-placement/index.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.d.ts +56 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.js +531 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/document/helpers.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/document/types.d.ts +103 -2
- package/node_modules/@fps-games/editor-playable-sdk/dist/document/types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.d.ts +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.js +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/index.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.js +9 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/schema.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.d.ts +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.js +3 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/field-schema/validators.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.js +3 -7
- package/node_modules/@fps-games/editor-playable-sdk/dist/local-harness/lifecycle.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.js +6 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/binding.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/types.d.ts +2 -10
- package/node_modules/@fps-games/editor-playable-sdk/dist/material-binding/types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.js +8 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/mutation/field-patch.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.js +66 -31
- package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/nodes.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/types.d.ts +2 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/runtime-preview/types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/protocol.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/protocol.js +40 -2
- package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/protocol.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.d.ts +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.js +20 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/descriptors.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/inspector.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/inspector.js +68 -27
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/inspector.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/object.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/object.js +7 -4
- package/node_modules/@fps-games/editor-playable-sdk/dist/serialized-property/object.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/session/hierarchy.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/session/hierarchy.js +2 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/session/hierarchy.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/package.json +3 -3
- package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts +57 -3
- package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-protocol/dist/agent.js.map +1 -1
- package/node_modules/@fps-games/editor-protocol/dist/index.d.ts +19 -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 +1 -0
- package/node_modules/@fps-games/editor-protocol/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-protocol/dist/marker-graph.d.ts +128 -0
- package/node_modules/@fps-games/editor-protocol/dist/marker-graph.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-protocol/dist/marker-graph.js +2 -0
- package/node_modules/@fps-games/editor-protocol/dist/marker-graph.js.map +1 -0
- package/node_modules/@fps-games/editor-protocol/package.json +1 -1
- package/package.json +8 -8
|
@@ -1,18 +1,61 @@
|
|
|
1
1
|
import { createEditorSession, createInspectorRegistry, DEFAULT_EDITOR_TRANSFORM_OPERATION_SETTINGS, DEFAULT_EDITOR_VIEWPORT_TOOL_STATE, createEmptyEditorViewportSpatialOverlayState, cloneEditorTransformSnapshot as cloneCoreEditorTransformSnapshot, composeEditorTransformMatrix, editorTransformMatricesAlmostEqual, cloneEditorViewportToolState, cloneEditorViewportSpatialOverlayState, aggregateInspectorObjectsToMultiObject, createInspectorEditPayload, compareInspectorSections, computeEditorTransformActionTargets, mergeInspectorSections, isEditorTransformTrsSnapshot, normalizeEditorTransformConstraint, readEditorTransformPosition, resolveEditorSelectionCommand, serializedMultiObjectToInspectorObject, serializedObjectToInspectorObject, validateSceneGraphDelete, validateSceneGraphDrop, validateSceneGraphGroupSelection, validateSceneGraphMove, validateSceneGraphRename, } from '@fps-games/editor-core';
|
|
2
2
|
import { createLocalEditorBrowserUi, toTransformOperationPolicyStatusLabel, } from '@fps-games/editor-browser';
|
|
3
3
|
import { createBabylonEnvironmentTextureController, createBabylonDefaultPostProcessPipelineController, } from '@fps-games/babylon-renderer';
|
|
4
|
-
import { createBabylonEditorProjection, createBabylonEditorWorld, createBabylonProjectionSelectionController, createBabylonSceneCameraPreviewController, createBabylonSceneViewCameraController, createBabylonSceneViewInputController, createBabylonSceneViewMeasurementController, createBabylonSceneViewSpatialOverlayController, createBabylonEditorShadowPreviewController, createBabylonTransformGizmoController, createBabylonEditorPerformanceMonitor, focusEditorViewportSelection, } from '@fps-games/editor-babylon';
|
|
4
|
+
import { createBabylonEditorProjection, createBabylonEditorMarkerOverlayController, createBabylonMarkerOverlayProjectionAdapter, createBabylonEditorWorld, createBabylonPrefabComparisonPreviewController, createBabylonProjectionSelectionController, createBabylonSceneCameraPreviewController, createBabylonSceneViewCameraController, createBabylonSceneViewInputController, createBabylonSceneViewMeasurementController, createBabylonSceneViewSpatialOverlayController, createBabylonEditorShadowPreviewController, createBabylonTransformGizmoController, createBabylonEditorPerformanceMonitor, focusEditorViewportSelection, } from '@fps-games/editor-babylon';
|
|
5
5
|
import { createLocalEditorSceneRenderScheduler, } from './local-editor-scene-render-scheduler.js';
|
|
6
6
|
import { createPlayableBabylonOutlineInspectorSection, createPlayableBabylonOutlineInspectorSeedPatchValue, isPlayableBabylonOutlineInspectorPatchPath, } from './playable-babylon-outline-inspector.js';
|
|
7
7
|
import { createLocalEditorViewportRenderCoordinator, } from './local-editor-viewport-render-coordinator.js';
|
|
8
8
|
import { createLocalEditorSceneViewInteractionRuntime, } from './local-editor-scene-view-interaction-runtime.js';
|
|
9
9
|
import { cloneLocalEditorPerformanceStats, countLocalEditorAuthoredObjects, createLocalEditorPerformanceStats, LOCAL_EDITOR_PERFORMANCE_MONITOR_REASON, } from './local-editor-performance-stats.js';
|
|
10
|
+
import { createMarkerGraphHierarchyItems, createMarkerGraphInspectorObject, createMarkerGraphRootInspectorObject, createMarkerPanelState, createMarkerRelationCommand, createMarkerRelationCreateManyCommand, createMarkerRelationDeleteConnectedCommand, createMarkerRelationReverseCommand, createMarkerGraphPatchFromInspectorEdit, findSpatialMarkerById, getSpatialMarkerDirectRelations, isMarkerGraphHierarchyId, isMarkerGraphHierarchyRootRowId, isSpatialMarkerBackedByHierarchyRow, isSpatialMarkerHierarchyVisible, readMarkerGraphHierarchyMarkerIdForGraph, readSpatialMarkerIdFromHierarchySelection, } from './local-editor-marker-graph.js';
|
|
10
11
|
const BATCH_EDIT_REQUIRES_MULTI_PATCH_REASON = 'Batch edit requires multi-property patch support.';
|
|
11
12
|
const LARGE_SELECTION_THRESHOLD = 200;
|
|
12
13
|
const LARGE_SHADOW_MODE_BATCH_THRESHOLD = 200;
|
|
13
14
|
const EDITOR_PROJECTION_IDLE_BEFORE_RUN_GAME_TIMEOUT_MS = 1500;
|
|
14
15
|
const EDITOR_WORLD_DISPOSE_BOUNDARY_DELAY_MS = 500;
|
|
15
16
|
const RENDERING_ACTION_PROGRESS_MIN_VISIBLE_MS = 1000;
|
|
17
|
+
const EMPTY_EDITOR_SELECTION = { selectedIds: [], activeId: null };
|
|
18
|
+
const MARKER_RELATION_HIGHLIGHT_RENDER_REASON = 'marker-relation-highlight';
|
|
19
|
+
const PREFAB_STAGE_STUDIO_WORLD_APPEARANCE = {
|
|
20
|
+
sky: {
|
|
21
|
+
preset: 'atmospheric',
|
|
22
|
+
topColor: { r: 0.68, g: 0.82, b: 0.9 },
|
|
23
|
+
horizonColor: { r: 0.98, g: 0.98, b: 0.93 },
|
|
24
|
+
bottomColor: { r: 0.44, g: 0.54, b: 0.53 },
|
|
25
|
+
cloudColor: { r: 1, g: 0.97, b: 0.86 },
|
|
26
|
+
cloudStrength: 0.1,
|
|
27
|
+
sunColor: { r: 1, g: 0.93, b: 0.72 },
|
|
28
|
+
sunDirection: { x: -0.24, y: 0.28, z: -0.93 },
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
const PREFAB_STAGE_STUDIO_ENVIRONMENT = {
|
|
32
|
+
textureUrl: 'https://assets.babylonjs.com/environments/studio.env',
|
|
33
|
+
intensity: 1,
|
|
34
|
+
rotationY: 0,
|
|
35
|
+
};
|
|
36
|
+
const PREFAB_STAGE_COMPARISON_PREVIEW_DEFINITIONS = [{
|
|
37
|
+
id: 'mesh-clone',
|
|
38
|
+
label: 'Mesh Clone',
|
|
39
|
+
mode: 'meshClone',
|
|
40
|
+
nodeIdSuffix: 'compare-clone',
|
|
41
|
+
}, {
|
|
42
|
+
id: 'instanced-mesh',
|
|
43
|
+
label: 'InstanceMesh',
|
|
44
|
+
mode: 'instancedMesh',
|
|
45
|
+
nodeIdSuffix: 'compare-instance',
|
|
46
|
+
}];
|
|
47
|
+
function createDefaultMarkerGraphVisibilityState() {
|
|
48
|
+
return {
|
|
49
|
+
rootVisible: true,
|
|
50
|
+
hiddenMarkerIds: new Set(),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function createEmptyMarkerCreateReservations() {
|
|
54
|
+
return {
|
|
55
|
+
ids: new Set(),
|
|
56
|
+
labels: new Set(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
16
59
|
export function createLocalEditorHarness(options) {
|
|
17
60
|
const root = options.root ?? document.body;
|
|
18
61
|
const state = {
|
|
@@ -31,6 +74,12 @@ export function createLocalEditorHarness(options) {
|
|
|
31
74
|
grid: null,
|
|
32
75
|
gridVisible: true,
|
|
33
76
|
projection: null,
|
|
77
|
+
markerOverlay: null,
|
|
78
|
+
markerGraphVisibility: createDefaultMarkerGraphVisibilityState(),
|
|
79
|
+
activeMarkerRelationId: null,
|
|
80
|
+
markerRelationPick: null,
|
|
81
|
+
markerRelationHighlightContinuous: false,
|
|
82
|
+
markerCreateReservations: createEmptyMarkerCreateReservations(),
|
|
34
83
|
shadowPreview: null,
|
|
35
84
|
environmentTexture: null,
|
|
36
85
|
postProcess: null,
|
|
@@ -45,6 +94,7 @@ export function createLocalEditorHarness(options) {
|
|
|
45
94
|
sceneViewMeasurement: null,
|
|
46
95
|
sceneViewSpatialOverlay: null,
|
|
47
96
|
selectionController: null,
|
|
97
|
+
prefabComparisonPreview: null,
|
|
48
98
|
sceneRenderScheduler: null,
|
|
49
99
|
viewportRenderCoordinator: null,
|
|
50
100
|
sceneFrameStats: null,
|
|
@@ -62,6 +112,7 @@ export function createLocalEditorHarness(options) {
|
|
|
62
112
|
viewportTools: cloneEditorViewportToolState(DEFAULT_EDITOR_VIEWPORT_TOOL_STATE),
|
|
63
113
|
viewportMeasurement: createEmptyViewportMeasurement(),
|
|
64
114
|
viewportSpatialOverlay: createEmptyEditorViewportSpatialOverlayState(),
|
|
115
|
+
prefabStage: null,
|
|
65
116
|
duplicateDrag: null,
|
|
66
117
|
activeTransformPreview: null,
|
|
67
118
|
armedPlacement: null,
|
|
@@ -116,6 +167,14 @@ export function createLocalEditorHarness(options) {
|
|
|
116
167
|
if (handleBrowserAssetAction(state, options, input))
|
|
117
168
|
harness.render();
|
|
118
169
|
},
|
|
170
|
+
onExitPrefabStage: () => {
|
|
171
|
+
if (exitPrefabStage(state, options))
|
|
172
|
+
harness.render();
|
|
173
|
+
},
|
|
174
|
+
onSelectPrefabStageItem: (itemId) => {
|
|
175
|
+
if (selectPrefabStageItem(state, itemId))
|
|
176
|
+
harness.render();
|
|
177
|
+
},
|
|
119
178
|
onSelectHierarchyItem: (input) => {
|
|
120
179
|
selectItem(state, options, input, harness.render);
|
|
121
180
|
},
|
|
@@ -123,6 +182,14 @@ export function createLocalEditorHarness(options) {
|
|
|
123
182
|
if (patchHierarchyItemEnabled(state, options, input))
|
|
124
183
|
harness.render();
|
|
125
184
|
},
|
|
185
|
+
onMarkerTypeAction: (input) => {
|
|
186
|
+
if (patchMarkerTypeCatalog(state, options, input))
|
|
187
|
+
harness.render();
|
|
188
|
+
},
|
|
189
|
+
onMarkerRelationAction: (input) => {
|
|
190
|
+
if (patchMarkerRelation(state, options, input))
|
|
191
|
+
harness.render();
|
|
192
|
+
},
|
|
126
193
|
onSelectionCommand: (command) => {
|
|
127
194
|
dispatchSelectionCommand(state, options, command, harness.render);
|
|
128
195
|
},
|
|
@@ -140,6 +207,12 @@ export function createLocalEditorHarness(options) {
|
|
|
140
207
|
harness.render();
|
|
141
208
|
}
|
|
142
209
|
: undefined,
|
|
210
|
+
onSceneGraphCreateMarker: options.documentAdapter.createMarkerGraphPatch
|
|
211
|
+
? (intent) => {
|
|
212
|
+
if (createSceneGraphMarker(state, options, intent))
|
|
213
|
+
harness.render();
|
|
214
|
+
}
|
|
215
|
+
: undefined,
|
|
143
216
|
onSceneGraphDelete: (intent) => {
|
|
144
217
|
if (deleteSceneGraphNodes(state, options, intent))
|
|
145
218
|
harness.render();
|
|
@@ -181,6 +254,10 @@ export function createLocalEditorHarness(options) {
|
|
|
181
254
|
if (patched || state.status !== previousStatus)
|
|
182
255
|
harness.render();
|
|
183
256
|
},
|
|
257
|
+
onInspectorAction: (input) => {
|
|
258
|
+
if (handleInspectorAction(state, options, input))
|
|
259
|
+
harness.render();
|
|
260
|
+
},
|
|
184
261
|
onRenderingPropertyChange: options.documentAdapter.onRenderingPropertyChange
|
|
185
262
|
? (input) => {
|
|
186
263
|
void applyRenderingPropertyChange(state, options, input)
|
|
@@ -201,7 +278,7 @@ export function createLocalEditorHarness(options) {
|
|
|
201
278
|
}
|
|
202
279
|
: undefined,
|
|
203
280
|
onTransformToolChange: (tool) => {
|
|
204
|
-
commitEditorStateChange(state, harness.render, 'transform-tool-change', () => {
|
|
281
|
+
commitEditorStateChange(state, options, harness.render, 'transform-tool-change', () => {
|
|
205
282
|
state.transformTool = tool;
|
|
206
283
|
state.transformConstraint = normalizeTransformConstraint(tool, state.transformConstraint);
|
|
207
284
|
}, {
|
|
@@ -209,21 +286,21 @@ export function createLocalEditorHarness(options) {
|
|
|
209
286
|
});
|
|
210
287
|
},
|
|
211
288
|
onTransformSpaceChange: (space) => {
|
|
212
|
-
commitEditorStateChange(state, harness.render, 'transform-space-change', () => {
|
|
289
|
+
commitEditorStateChange(state, options, harness.render, 'transform-space-change', () => {
|
|
213
290
|
state.transformSpace = space;
|
|
214
291
|
}, {
|
|
215
292
|
syncTransformArtifacts: true,
|
|
216
293
|
});
|
|
217
294
|
},
|
|
218
295
|
onTransformConstraintChange: (constraint) => {
|
|
219
|
-
commitEditorStateChange(state, harness.render, 'transform-constraint-change', () => {
|
|
296
|
+
commitEditorStateChange(state, options, harness.render, 'transform-constraint-change', () => {
|
|
220
297
|
state.transformConstraint = normalizeTransformConstraint(state.transformTool, constraint);
|
|
221
298
|
}, {
|
|
222
299
|
syncTransformArtifacts: true,
|
|
223
300
|
});
|
|
224
301
|
},
|
|
225
302
|
onTransformSnapEnabledChange: (enabled) => {
|
|
226
|
-
commitEditorStateChange(state, harness.render, 'transform-operation-settings-change', () => {
|
|
303
|
+
commitEditorStateChange(state, options, harness.render, 'transform-operation-settings-change', () => {
|
|
227
304
|
state.transformOperationSettings = updateTransformOperationSettings(state.transformOperationSettings, {
|
|
228
305
|
snap: {
|
|
229
306
|
...state.transformOperationSettings.snap,
|
|
@@ -239,7 +316,7 @@ export function createLocalEditorHarness(options) {
|
|
|
239
316
|
const value = normalizePositiveStep(input.value);
|
|
240
317
|
if (value == null)
|
|
241
318
|
return;
|
|
242
|
-
commitEditorStateChange(state, harness.render, 'transform-operation-settings-change', () => {
|
|
319
|
+
commitEditorStateChange(state, options, harness.render, 'transform-operation-settings-change', () => {
|
|
243
320
|
const snap = { ...state.transformOperationSettings.snap };
|
|
244
321
|
if (input.kind === 'move')
|
|
245
322
|
snap.moveStep = value;
|
|
@@ -254,7 +331,7 @@ export function createLocalEditorHarness(options) {
|
|
|
254
331
|
});
|
|
255
332
|
},
|
|
256
333
|
onPlacementModeChange: (mode) => {
|
|
257
|
-
commitEditorStateChange(state, harness.render, 'placement-mode-change', () => {
|
|
334
|
+
commitEditorStateChange(state, options, harness.render, 'placement-mode-change', () => {
|
|
258
335
|
state.transformOperationSettings = updateTransformOperationSettings(state.transformOperationSettings, {
|
|
259
336
|
placementMode: normalizePlacementMode(mode),
|
|
260
337
|
});
|
|
@@ -281,6 +358,10 @@ export function createLocalEditorHarness(options) {
|
|
|
281
358
|
if (setViewportOverlaySettings(state, settings))
|
|
282
359
|
harness.render();
|
|
283
360
|
},
|
|
361
|
+
onMarkerViewportVisibilityChange: (visible) => {
|
|
362
|
+
if (setMarkerViewportVisibility(state, options, visible))
|
|
363
|
+
harness.render();
|
|
364
|
+
},
|
|
284
365
|
onViewportUtilityToolChange: (tool) => {
|
|
285
366
|
if (setViewportUtilityTool(state, tool))
|
|
286
367
|
harness.render();
|
|
@@ -301,6 +382,18 @@ export function createLocalEditorHarness(options) {
|
|
|
301
382
|
setLocalEditorPerformanceMonitorOpen(state, open);
|
|
302
383
|
updateLocalEditorPerformanceStats(state, stats => ui.updatePerformanceStats?.(stats));
|
|
303
384
|
},
|
|
385
|
+
...{
|
|
386
|
+
onPrefabComparisonPreviewLayoutChange: () => {
|
|
387
|
+
harness.render();
|
|
388
|
+
},
|
|
389
|
+
onPrefabComparisonPreviewInteraction: (input) => {
|
|
390
|
+
if (!state.prefabStage)
|
|
391
|
+
return;
|
|
392
|
+
if (state.prefabComparisonPreview?.adjustView(input)) {
|
|
393
|
+
requestEditorSceneFrame(state, 'prefab-comparison-preview-interaction');
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
},
|
|
304
397
|
onFocusSelection: () => {
|
|
305
398
|
if (focusSelectedProjection(state))
|
|
306
399
|
harness.render();
|
|
@@ -333,11 +426,12 @@ export function createLocalEditorHarness(options) {
|
|
|
333
426
|
};
|
|
334
427
|
harness = {
|
|
335
428
|
render() {
|
|
336
|
-
syncSceneCameraPreview(state, options);
|
|
337
429
|
syncViewportCameraState(state);
|
|
338
430
|
syncViewportMeasurementState(state);
|
|
339
431
|
syncViewportSpatialOverlay(state);
|
|
340
432
|
ui.update(createUiState(state, options));
|
|
433
|
+
syncPrefabComparisonPreviewSurfaces(state, ui);
|
|
434
|
+
syncSceneCameraPreview(state, options);
|
|
341
435
|
},
|
|
342
436
|
notifyViewportRevealed(reason = 'viewport-revealed') {
|
|
343
437
|
state.viewportRenderCoordinator?.requestRevealFrame(reason);
|
|
@@ -412,6 +506,10 @@ export function createLocalEditorHarness(options) {
|
|
|
412
506
|
const preparedDocument = options.documentAdapter.prepareDocument?.(document, assets) ?? document;
|
|
413
507
|
state.assets = assets;
|
|
414
508
|
state.source = source;
|
|
509
|
+
state.markerGraphVisibility = createDefaultMarkerGraphVisibilityState();
|
|
510
|
+
state.activeMarkerRelationId = null;
|
|
511
|
+
state.markerRelationHighlightContinuous = false;
|
|
512
|
+
state.markerCreateReservations = createEmptyMarkerCreateReservations();
|
|
415
513
|
state.session = createEditorSession({
|
|
416
514
|
source: source ?? undefined,
|
|
417
515
|
persistedDocument: preparedDocument,
|
|
@@ -425,7 +523,8 @@ export function createLocalEditorHarness(options) {
|
|
|
425
523
|
ui.updatePreview(previewState);
|
|
426
524
|
else
|
|
427
525
|
ui.update(createUiState(state, options));
|
|
428
|
-
|
|
526
|
+
syncPrefabComparisonPreviewSurfaces(state, ui);
|
|
527
|
+
}, stats => ui.updateSceneFrameStats?.(stats), stats => ui.updatePerformanceStats?.(stats), () => ui.getPrefabComparisonPreviewSurfaces?.() ?? []);
|
|
429
528
|
state.mode = 'editor';
|
|
430
529
|
state.summary = loadedSource?.summary ?? summarizeDocument(options, preparedDocument, source);
|
|
431
530
|
state.status = `GameWorld disposed; EditorWorld active; assets=${assets.length}`;
|
|
@@ -667,6 +766,25 @@ function setViewportOverlaySettings(state, settings) {
|
|
|
667
766
|
state.statusDetails = '';
|
|
668
767
|
return true;
|
|
669
768
|
}
|
|
769
|
+
function setMarkerViewportVisibility(state, options, visible) {
|
|
770
|
+
const nextVisible = visible !== false;
|
|
771
|
+
if ((state.viewportTools.markersVisible ?? true) === nextVisible)
|
|
772
|
+
return false;
|
|
773
|
+
state.viewportTools = {
|
|
774
|
+
...state.viewportTools,
|
|
775
|
+
markersVisible: nextVisible,
|
|
776
|
+
};
|
|
777
|
+
state.status = nextVisible ? 'Marker view shown' : 'Marker view hidden';
|
|
778
|
+
state.statusTone = 'default';
|
|
779
|
+
state.statusToneStatus = state.status;
|
|
780
|
+
state.statusDetails = nextVisible
|
|
781
|
+
? 'Marker overlays and relation lines are visible in the editor viewport.'
|
|
782
|
+
: 'Marker overlays and relation lines are temporarily hidden in the editor viewport.';
|
|
783
|
+
state.editorStateRevision += 1;
|
|
784
|
+
syncCurrentSelectionToSceneArtifacts(state, options, 'marker-viewport-visibility', state.editorStateRevision);
|
|
785
|
+
requestEditorSceneFrame(state, 'marker-viewport-visibility');
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
670
788
|
function setViewportUtilityTool(state, tool) {
|
|
671
789
|
const nextTool = tool === 'measure-distance' ? 'measure-distance' : 'none';
|
|
672
790
|
state.viewportTools = {
|
|
@@ -801,7 +919,7 @@ function validateTransformActionSelection(state, action) {
|
|
|
801
919
|
? { ok: true }
|
|
802
920
|
: { ok: false, message: 'Distribute needs at least 3 selected objects' };
|
|
803
921
|
}
|
|
804
|
-
async function createEditorWorld(state, options, renderUi, renderPreviewUi, updateSceneFrameStats, updatePerformanceStats) {
|
|
922
|
+
async function createEditorWorld(state, options, renderUi, renderPreviewUi, updateSceneFrameStats, updatePerformanceStats, getPrefabComparisonPreviewSurfaces) {
|
|
805
923
|
disposeEditorWorld(state);
|
|
806
924
|
const render = (reason = 'editor-world-update') => {
|
|
807
925
|
renderUi();
|
|
@@ -816,9 +934,9 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
|
|
|
816
934
|
const babylon = await options.worldAdapter.loadBabylon();
|
|
817
935
|
const engine = options.worldAdapter.createEngine(babylon, canvas);
|
|
818
936
|
const document = state.session?.getState().workingDocument;
|
|
819
|
-
const initialAppearance =
|
|
937
|
+
const initialAppearance = resolveEffectiveLocalEditorWorldAppearance(state, options, document);
|
|
820
938
|
const initialAppearanceKey = serializeLocalEditorWorldAppearance(initialAppearance);
|
|
821
|
-
const initialRendering =
|
|
939
|
+
const initialRendering = resolveEffectiveLocalEditorWorldRendering(state, options, document);
|
|
822
940
|
const initialRenderingKey = serializeLocalEditorWorldRendering(initialRendering);
|
|
823
941
|
const world = createBabylonEditorWorld({
|
|
824
942
|
engine,
|
|
@@ -834,6 +952,8 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
|
|
|
834
952
|
const performanceMonitor = createBabylonEditorPerformanceMonitor(world.scene);
|
|
835
953
|
const sceneRenderScheduler = createLocalEditorSceneRenderScheduler((frame) => {
|
|
836
954
|
state.sceneViewInteraction?.updateFrame(frame);
|
|
955
|
+
syncPrefabComparisonPreviewSurfaces(state, { getPrefabComparisonPreviewSurfaces });
|
|
956
|
+
state.prefabComparisonPreview?.prepareFrame();
|
|
837
957
|
world.render();
|
|
838
958
|
}, {
|
|
839
959
|
onStatsChange(stats) {
|
|
@@ -867,19 +987,46 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
|
|
|
867
987
|
selectionEdgesPrewarm: options.world?.selectionEdgesPrewarm,
|
|
868
988
|
logger: console,
|
|
869
989
|
onProjectionReady(event) {
|
|
870
|
-
|
|
990
|
+
if (state.prefabStage) {
|
|
991
|
+
syncCurrentSelectionToSceneArtifacts(state, options, `prefab-stage-projection-${event.nodeId}-ready-selection`);
|
|
992
|
+
focusPrefabStagePreviewProjection(state, `prefab-stage-projection-${event.nodeId}-ready-focus`);
|
|
993
|
+
scheduleProjectionShadowPreviewRebuild(state, `prefab-stage-projection-${event.nodeId}-ready-shadow`);
|
|
994
|
+
viewportRenderCoordinator.invalidateScene(`prefab-stage-projection-${event.nodeId}-ready`);
|
|
995
|
+
renderUi();
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
syncCurrentSelectionToSceneArtifacts(state, options, `projection-${event.nodeId}-ready-selection`);
|
|
871
999
|
scheduleProjectionShadowPreviewRebuild(state, `projection-${event.nodeId}-ready-shadow`);
|
|
872
1000
|
viewportRenderCoordinator.invalidateScene(`projection-${event.nodeId}-ready`);
|
|
873
1001
|
},
|
|
874
1002
|
onProjectionBatchSettled(event) {
|
|
875
|
-
|
|
1003
|
+
if (state.prefabStage) {
|
|
1004
|
+
syncCurrentSelectionToSceneArtifacts(state, options, `prefab-stage-projection-batch-${event.batchId}-settled-selection`);
|
|
1005
|
+
focusPrefabStagePreviewProjection(state, `prefab-stage-projection-batch-${event.batchId}-settled-focus`);
|
|
1006
|
+
scheduleProjectionShadowPreviewRebuild(state, `prefab-stage-projection-batch-${event.batchId}-settled`);
|
|
1007
|
+
renderUi();
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
syncCurrentSelectionToSceneArtifacts(state, options, `projection-batch-${event.batchId}-settled-selection`);
|
|
876
1011
|
scheduleProjectionShadowPreviewRebuild(state, `projection-batch-${event.batchId}-settled`);
|
|
877
1012
|
},
|
|
878
1013
|
});
|
|
879
|
-
const
|
|
1014
|
+
const prefabComparisonPreview = createBabylonPrefabComparisonPreviewController({
|
|
880
1015
|
babylon,
|
|
881
1016
|
scene: world.scene,
|
|
1017
|
+
editorCamera: world.camera,
|
|
882
1018
|
projection,
|
|
1019
|
+
getSkyBackdrop: () => world.skyBackdrop,
|
|
1020
|
+
});
|
|
1021
|
+
const markerOverlay = createBabylonEditorMarkerOverlayController({
|
|
1022
|
+
babylon,
|
|
1023
|
+
scene: world.scene,
|
|
1024
|
+
});
|
|
1025
|
+
const markerAwareProjection = createBabylonMarkerOverlayProjectionAdapter(projection, markerOverlay);
|
|
1026
|
+
const gizmo = createBabylonTransformGizmoController({
|
|
1027
|
+
babylon,
|
|
1028
|
+
scene: world.scene,
|
|
1029
|
+
projection: markerAwareProjection,
|
|
883
1030
|
initialTool: state.transformTool,
|
|
884
1031
|
initialSpace: state.transformSpace,
|
|
885
1032
|
documentCapabilities: options.transformDocumentCapabilities,
|
|
@@ -897,6 +1044,8 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
|
|
|
897
1044
|
},
|
|
898
1045
|
onDragUpdate(event) {
|
|
899
1046
|
updateActiveTransformPreview(state, event);
|
|
1047
|
+
syncMarkerOverlayToSceneArtifacts(state, options, state.session?.getState().selection ?? { selectedIds: [], activeId: null });
|
|
1048
|
+
state.gizmo?.refreshSelection();
|
|
900
1049
|
preview('gizmo-drag-update');
|
|
901
1050
|
},
|
|
902
1051
|
onDragEnd(event) {
|
|
@@ -939,13 +1088,15 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
|
|
|
939
1088
|
const selectionController = createBabylonProjectionSelectionController({
|
|
940
1089
|
scene: world.scene,
|
|
941
1090
|
canvas,
|
|
942
|
-
projection,
|
|
1091
|
+
projection: markerAwareProjection,
|
|
943
1092
|
getTool: () => state.transformTool,
|
|
944
|
-
getSelection: () => state.session?.getSelection() ??
|
|
945
|
-
isSelectable: (nodeId) => isDocumentNodeSelectable(state, options, nodeId),
|
|
946
|
-
isLocked: (nodeId) => isDocumentNodeLocked(state, options, nodeId),
|
|
1093
|
+
getSelection: () => state.prefabStage ? EMPTY_EDITOR_SELECTION : state.session?.getSelection() ?? EMPTY_EDITOR_SELECTION,
|
|
1094
|
+
isSelectable: (nodeId) => !state.prefabStage && isDocumentNodeSelectable(state, options, nodeId),
|
|
1095
|
+
isLocked: (nodeId) => state.prefabStage ? true : isDocumentNodeLocked(state, options, nodeId),
|
|
947
1096
|
isOperationBlocked: () => state.gizmo?.getState().dragPhase === 'dragging',
|
|
948
1097
|
onSelectionCommand(command) {
|
|
1098
|
+
if (state.prefabStage)
|
|
1099
|
+
return;
|
|
949
1100
|
dispatchSelectionCommand(state, options, command, render);
|
|
950
1101
|
},
|
|
951
1102
|
onFocusIntent(nodeId) {
|
|
@@ -967,7 +1118,7 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
|
|
|
967
1118
|
isGizmoDragCandidate: (event) => gizmo.isGizmoDragCandidate(event),
|
|
968
1119
|
isBoxSelectCandidate: (event) => selectionController.isBoxSelectionCandidate(event),
|
|
969
1120
|
isViewPlaneMoveCandidate: (event) => gizmo.isViewPlaneMoveCandidate(event),
|
|
970
|
-
isPlacementCandidate: () => isPlacementArmed(state),
|
|
1121
|
+
isPlacementCandidate: () => !state.prefabStage && isPlacementArmed(state),
|
|
971
1122
|
isMeasurementCandidate: () => state.viewportTools.activeUtilityTool === 'measure-distance',
|
|
972
1123
|
onPointerIntentStart(event) {
|
|
973
1124
|
sceneViewInteraction.beginPointerIntent(event.state.intent);
|
|
@@ -1100,6 +1251,7 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
|
|
|
1100
1251
|
},
|
|
1101
1252
|
});
|
|
1102
1253
|
const environmentTexture = createBabylonEnvironmentTextureController(world.scene, initialRendering.environment ?? null);
|
|
1254
|
+
world.refreshSkyBackdrop();
|
|
1103
1255
|
const postProcess = createBabylonDefaultPostProcessPipelineController(world.scene, [world.camera], initialRendering.postProcess ?? null, { name: 'editorDefaultPostProcessPipeline' });
|
|
1104
1256
|
const resize = () => {
|
|
1105
1257
|
engine.resize?.();
|
|
@@ -1110,7 +1262,9 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
|
|
|
1110
1262
|
state.engine = engine;
|
|
1111
1263
|
state.world = world;
|
|
1112
1264
|
state.grid = grid;
|
|
1113
|
-
state.projection =
|
|
1265
|
+
state.projection = markerAwareProjection;
|
|
1266
|
+
state.prefabComparisonPreview = prefabComparisonPreview;
|
|
1267
|
+
state.markerOverlay = markerOverlay;
|
|
1114
1268
|
state.shadowPreview = shadowPreview;
|
|
1115
1269
|
state.environmentTexture = environmentTexture;
|
|
1116
1270
|
state.postProcess = postProcess;
|
|
@@ -1130,13 +1284,14 @@ async function createEditorWorld(state, options, renderUi, renderPreviewUi, upda
|
|
|
1130
1284
|
state.worldRenderingKey = initialRenderingKey;
|
|
1131
1285
|
state.resizeHandler = resize;
|
|
1132
1286
|
if (document) {
|
|
1133
|
-
projection.projectNodes(options
|
|
1287
|
+
projection.projectNodes(resolveCurrentEditorProjectionNodes(state, options, document));
|
|
1134
1288
|
scheduleProjectionShadowPreviewRebuild(state, 'editor-world-initial-projection-shadow');
|
|
1135
1289
|
}
|
|
1136
|
-
syncCurrentSelectionToSceneArtifacts(state, 'editor-world-initial-selection');
|
|
1290
|
+
syncCurrentSelectionToSceneArtifacts(state, options, 'editor-world-initial-selection');
|
|
1137
1291
|
viewportRenderCoordinator.requestFrame('editor-world-created');
|
|
1138
1292
|
}
|
|
1139
1293
|
function disposeEditorWorld(state) {
|
|
1294
|
+
clearPrefabStageForEditorTeardown(state);
|
|
1140
1295
|
state.activeTransformPreview = null;
|
|
1141
1296
|
state.sceneViewInteraction?.dispose();
|
|
1142
1297
|
state.sceneViewInteraction = null;
|
|
@@ -1157,6 +1312,12 @@ function disposeEditorWorld(state) {
|
|
|
1157
1312
|
state.sceneViewInput = null;
|
|
1158
1313
|
state.selectionController?.dispose();
|
|
1159
1314
|
state.selectionController = null;
|
|
1315
|
+
state.prefabComparisonPreview?.dispose();
|
|
1316
|
+
state.prefabComparisonPreview = null;
|
|
1317
|
+
if (state.markerRelationHighlightContinuous) {
|
|
1318
|
+
state.viewportRenderCoordinator?.endContinuous(MARKER_RELATION_HIGHLIGHT_RENDER_REASON);
|
|
1319
|
+
state.markerRelationHighlightContinuous = false;
|
|
1320
|
+
}
|
|
1160
1321
|
if (state.performanceMonitorOpen) {
|
|
1161
1322
|
state.viewportRenderCoordinator?.endContinuous(LOCAL_EDITOR_PERFORMANCE_MONITOR_REASON);
|
|
1162
1323
|
}
|
|
@@ -1179,6 +1340,8 @@ function disposeEditorWorld(state) {
|
|
|
1179
1340
|
};
|
|
1180
1341
|
state.gizmo?.dispose();
|
|
1181
1342
|
state.gizmo = null;
|
|
1343
|
+
state.markerOverlay?.dispose();
|
|
1344
|
+
state.markerOverlay = null;
|
|
1182
1345
|
if (state.projectionShadowPreviewRebuildFrame !== null) {
|
|
1183
1346
|
window.cancelAnimationFrame(state.projectionShadowPreviewRebuildFrame);
|
|
1184
1347
|
state.projectionShadowPreviewRebuildFrame = null;
|
|
@@ -1204,6 +1367,16 @@ function disposeEditorWorld(state) {
|
|
|
1204
1367
|
state.world = null;
|
|
1205
1368
|
state.engine = null;
|
|
1206
1369
|
}
|
|
1370
|
+
function clearPrefabStageForEditorTeardown(state) {
|
|
1371
|
+
const stage = state.prefabStage;
|
|
1372
|
+
if (!stage)
|
|
1373
|
+
return;
|
|
1374
|
+
state.prefabStage = null;
|
|
1375
|
+
setGridVisibleForEditorContext(state, stage.restoreGridVisible, 'prefab-stage-editor-teardown-grid');
|
|
1376
|
+
state.sceneCameraPreviewEnabled = stage.restoreSceneCameraPreviewEnabled;
|
|
1377
|
+
if (!stage.restoreSceneCameraPreviewEnabled)
|
|
1378
|
+
state.sceneCameraPreview?.setActive(false);
|
|
1379
|
+
}
|
|
1207
1380
|
async function waitForEditorProjectionIdleBeforeRunGame(state) {
|
|
1208
1381
|
const projection = state.projection;
|
|
1209
1382
|
if (!projection)
|
|
@@ -1271,6 +1444,27 @@ function requestEditorPreviewFrame(state, options, renderPreviewUi, reason) {
|
|
|
1271
1444
|
renderPreviewUi(createUiPreviewState(state, options));
|
|
1272
1445
|
requestEditorSceneFrame(state, reason);
|
|
1273
1446
|
}
|
|
1447
|
+
function syncPrefabComparisonPreviewSurfaces(state, ui) {
|
|
1448
|
+
const controller = state.prefabComparisonPreview;
|
|
1449
|
+
if (!controller)
|
|
1450
|
+
return;
|
|
1451
|
+
if (!state.prefabStage) {
|
|
1452
|
+
if (controller.clear())
|
|
1453
|
+
requestEditorSceneFrame(state, 'prefab-comparison-preview-clear');
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
const surfaces = ui.getPrefabComparisonPreviewSurfaces?.() ?? [];
|
|
1457
|
+
const changed = controller.sync(surfaces.map(toBabylonPrefabComparisonPreviewSurface));
|
|
1458
|
+
if (changed)
|
|
1459
|
+
requestEditorSceneFrame(state, 'prefab-comparison-preview-sync');
|
|
1460
|
+
}
|
|
1461
|
+
function toBabylonPrefabComparisonPreviewSurface(surface) {
|
|
1462
|
+
return {
|
|
1463
|
+
previewId: surface.previewId,
|
|
1464
|
+
projectionNodeId: surface.projectionNodeId,
|
|
1465
|
+
rect: surface.rect,
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1274
1468
|
function updateActiveTransformPreview(state, event) {
|
|
1275
1469
|
const activeId = event.activeId ?? event.nodeId ?? event.targetIds[0] ?? null;
|
|
1276
1470
|
if (!activeId) {
|
|
@@ -1351,13 +1545,29 @@ function selectItem(state, options, input, renderUi) {
|
|
|
1351
1545
|
return dispatchSelectionCommand(state, options, command, renderUi);
|
|
1352
1546
|
}
|
|
1353
1547
|
function createBrowserHierarchyContextActions(state, options) {
|
|
1354
|
-
return (options
|
|
1548
|
+
return createHierarchyContextActionRegistrations(state, options).map((registration) => ({
|
|
1355
1549
|
id: registration.id,
|
|
1356
1550
|
label: registration.label,
|
|
1357
1551
|
shortcut: registration.shortcut,
|
|
1358
1552
|
danger: registration.danger,
|
|
1359
1553
|
placement: registration.placement,
|
|
1360
1554
|
separatorBefore: registration.separatorBefore,
|
|
1555
|
+
children: (browserContext) => {
|
|
1556
|
+
const context = createHarnessHierarchyContextActionContext(state, options, browserContext);
|
|
1557
|
+
if (!context)
|
|
1558
|
+
return [];
|
|
1559
|
+
const children = typeof registration.children === 'function'
|
|
1560
|
+
? registration.children(context)
|
|
1561
|
+
: registration.children ?? [];
|
|
1562
|
+
return children.map(child => ({
|
|
1563
|
+
id: child.id,
|
|
1564
|
+
label: child.label,
|
|
1565
|
+
shortcut: child.shortcut,
|
|
1566
|
+
danger: child.danger,
|
|
1567
|
+
disabled: () => child.disabled?.(context) ?? false,
|
|
1568
|
+
payload: () => child.payload?.(context),
|
|
1569
|
+
}));
|
|
1570
|
+
},
|
|
1361
1571
|
visible: (browserContext) => {
|
|
1362
1572
|
const context = createHarnessHierarchyContextActionContext(state, options, browserContext);
|
|
1363
1573
|
if (!context)
|
|
@@ -1372,6 +1582,40 @@ function createBrowserHierarchyContextActions(state, options) {
|
|
|
1372
1582
|
},
|
|
1373
1583
|
}));
|
|
1374
1584
|
}
|
|
1585
|
+
function createHierarchyContextActionRegistrations(state, options) {
|
|
1586
|
+
return [
|
|
1587
|
+
createPrefabFromGameObjectHierarchyContextAction(state, options),
|
|
1588
|
+
...(options.hierarchy?.contextActions ?? []),
|
|
1589
|
+
];
|
|
1590
|
+
}
|
|
1591
|
+
function createPrefabFromGameObjectHierarchyContextAction(state, options) {
|
|
1592
|
+
return {
|
|
1593
|
+
id: 'asset.create-prefab-from-game-object',
|
|
1594
|
+
label: 'Create Prefab',
|
|
1595
|
+
placement: 'after-create',
|
|
1596
|
+
visible: (context) => (!state.prefabStage
|
|
1597
|
+
&& !!options.documentAdapter.createAssetActionPatch
|
|
1598
|
+
&& context.targetIds.length === 1
|
|
1599
|
+
&& context.activeId === context.targetIds[0]
|
|
1600
|
+
&& !!context.projectionNode?.asset),
|
|
1601
|
+
disabled: (context) => {
|
|
1602
|
+
if (context.targetIds.length !== 1 || !context.activeId)
|
|
1603
|
+
return 'Select exactly one model-backed GameObject.';
|
|
1604
|
+
if (!context.projectionNode?.asset)
|
|
1605
|
+
return 'Only model-backed GameObjects can be converted to Prefab assets.';
|
|
1606
|
+
return false;
|
|
1607
|
+
},
|
|
1608
|
+
run: (context) => patchBrowserAssetAction(state, options, {
|
|
1609
|
+
actionId: 'asset.create-prefab-from-game-object',
|
|
1610
|
+
assetId: undefined,
|
|
1611
|
+
browserAssetId: undefined,
|
|
1612
|
+
assetKind: 'prefab',
|
|
1613
|
+
}, {
|
|
1614
|
+
activeId: context.activeId,
|
|
1615
|
+
selectedIds: context.targetIds,
|
|
1616
|
+
}),
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1375
1619
|
function createHarnessHierarchyContextActionContext(state, options, browserContext, payload) {
|
|
1376
1620
|
const document = state.session?.getState().workingDocument ?? null;
|
|
1377
1621
|
if (!document)
|
|
@@ -1409,17 +1653,22 @@ function createBrowserContextForCustomAction(state, options, action) {
|
|
|
1409
1653
|
};
|
|
1410
1654
|
}
|
|
1411
1655
|
function runHierarchyCustomContextAction(state, options, action) {
|
|
1412
|
-
const registration = options.hierarchy?.contextActions?.find(candidate => candidate.id === action.id);
|
|
1413
|
-
if (!registration)
|
|
1414
|
-
return false;
|
|
1415
1656
|
const browserContext = createBrowserContextForCustomAction(state, options, action);
|
|
1416
1657
|
const context = browserContext
|
|
1417
1658
|
? createHarnessHierarchyContextActionContext(state, options, browserContext, action.payload)
|
|
1418
1659
|
: null;
|
|
1419
1660
|
if (!context)
|
|
1420
1661
|
return false;
|
|
1662
|
+
const registration = createHierarchyContextActionRegistrations(state, options)
|
|
1663
|
+
.find(candidate => candidate.id === action.id);
|
|
1664
|
+
if (!registration)
|
|
1665
|
+
return false;
|
|
1421
1666
|
try {
|
|
1422
|
-
|
|
1667
|
+
const result = registration.run(context);
|
|
1668
|
+
if (isLocalEditorHarnessPatchResult(result)) {
|
|
1669
|
+
return applyHierarchyContextActionPatch(state, options, result, registration.label);
|
|
1670
|
+
}
|
|
1671
|
+
return result === true;
|
|
1423
1672
|
}
|
|
1424
1673
|
catch (error) {
|
|
1425
1674
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1431,6 +1680,55 @@ function runHierarchyCustomContextAction(state, options, action) {
|
|
|
1431
1680
|
return true;
|
|
1432
1681
|
}
|
|
1433
1682
|
}
|
|
1683
|
+
function isLocalEditorHarnessPatchResult(value) {
|
|
1684
|
+
return !!value
|
|
1685
|
+
&& typeof value === 'object'
|
|
1686
|
+
&& 'patch' in value;
|
|
1687
|
+
}
|
|
1688
|
+
function applyHierarchyContextActionPatch(state, options, patch, fallbackLabel) {
|
|
1689
|
+
const document = state.session?.getState().workingDocument;
|
|
1690
|
+
if (state.mode !== 'editor' || !state.session || !document)
|
|
1691
|
+
return false;
|
|
1692
|
+
cancelActiveOperation(state);
|
|
1693
|
+
const label = patch.label ?? fallbackLabel;
|
|
1694
|
+
const projectionImpact = resolveProjectionImpact(patch, patch.createdId
|
|
1695
|
+
? createKnownCreatedReprojectProjectionImpact({
|
|
1696
|
+
createdNodeIds: [patch.createdId],
|
|
1697
|
+
reprojectIds: patch.reprojectIds,
|
|
1698
|
+
})
|
|
1699
|
+
: patch.reprojectIds?.length
|
|
1700
|
+
? createReprojectProjectionImpact(patch.reprojectIds)
|
|
1701
|
+
: patch.changedIds?.length
|
|
1702
|
+
? createReprojectProjectionImpact(patch.changedIds)
|
|
1703
|
+
: patch.changedId
|
|
1704
|
+
? createReprojectProjectionImpact([patch.changedId])
|
|
1705
|
+
: undefined);
|
|
1706
|
+
const result = state.session.dispatch({
|
|
1707
|
+
type: 'document.patch',
|
|
1708
|
+
label,
|
|
1709
|
+
patch: patch.patch,
|
|
1710
|
+
targetId: patch.createdId ?? patch.changedId ?? undefined,
|
|
1711
|
+
...withProjectionImpact(projectionImpact),
|
|
1712
|
+
});
|
|
1713
|
+
if (!result.documentChanged) {
|
|
1714
|
+
state.status = `${label} unchanged`;
|
|
1715
|
+
return true;
|
|
1716
|
+
}
|
|
1717
|
+
const selectionId = patch.createdId ?? patch.changedId ?? null;
|
|
1718
|
+
let selection = result.selection;
|
|
1719
|
+
if (selectionId && isNodeSelectableInDocument(options, result.workingDocument, selectionId)) {
|
|
1720
|
+
selection = state.session.dispatch({
|
|
1721
|
+
type: 'selection.replace',
|
|
1722
|
+
selectedIds: [selectionId],
|
|
1723
|
+
activeId: selectionId,
|
|
1724
|
+
label: `Select ${selectionId}`,
|
|
1725
|
+
}).selection;
|
|
1726
|
+
}
|
|
1727
|
+
rebuildProjectionFromDocument(state, options, result.workingDocument, selection);
|
|
1728
|
+
state.summary = summarizeDocument(options, result.workingDocument, state.session.getSource());
|
|
1729
|
+
state.status = label;
|
|
1730
|
+
return true;
|
|
1731
|
+
}
|
|
1434
1732
|
function handleContextAction(state, options, action) {
|
|
1435
1733
|
if (action.region !== 'hierarchy')
|
|
1436
1734
|
return false;
|
|
@@ -1465,6 +1763,14 @@ function handleContextAction(state, options, action) {
|
|
|
1465
1763
|
name: action.name,
|
|
1466
1764
|
});
|
|
1467
1765
|
}
|
|
1766
|
+
if (action.action === 'create-marker') {
|
|
1767
|
+
return createSceneGraphMarker(state, options, {
|
|
1768
|
+
parentId: action.parentId ?? null,
|
|
1769
|
+
activeId: action.activeId ?? null,
|
|
1770
|
+
markerType: action.markerType,
|
|
1771
|
+
name: action.name,
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1468
1774
|
if (action.action === 'delete') {
|
|
1469
1775
|
return deleteSceneGraphNodes(state, options, {
|
|
1470
1776
|
ids: action.targetIds,
|
|
@@ -1488,6 +1794,9 @@ function handleContextAction(state, options, action) {
|
|
|
1488
1794
|
function dispatchSelectionCommand(state, options, command, renderUi) {
|
|
1489
1795
|
if (state.mode !== 'editor')
|
|
1490
1796
|
return false;
|
|
1797
|
+
if (state.markerRelationPick) {
|
|
1798
|
+
return consumeMarkerRelationPickSelectionCommand(state, options, command, renderUi);
|
|
1799
|
+
}
|
|
1491
1800
|
cancelActiveOperation(state);
|
|
1492
1801
|
const session = state.session;
|
|
1493
1802
|
if (!session)
|
|
@@ -1499,16 +1808,109 @@ function dispatchSelectionCommand(state, options, command, renderUi) {
|
|
|
1499
1808
|
if (!changed)
|
|
1500
1809
|
return false;
|
|
1501
1810
|
if (renderUi) {
|
|
1502
|
-
commitEditorStateChange(state, renderUi, 'selection-command', () => { }, {
|
|
1811
|
+
commitEditorStateChange(state, options, renderUi, 'selection-command', () => { }, {
|
|
1503
1812
|
syncSelectionArtifacts: selection,
|
|
1504
1813
|
});
|
|
1505
1814
|
}
|
|
1506
1815
|
else {
|
|
1507
1816
|
state.editorStateRevision += 1;
|
|
1508
|
-
syncSelectionToProjection(state, selection, 'selection-command', state.editorStateRevision);
|
|
1817
|
+
syncSelectionToProjection(state, options, selection, 'selection-command', state.editorStateRevision);
|
|
1818
|
+
}
|
|
1819
|
+
return true;
|
|
1820
|
+
}
|
|
1821
|
+
function consumeMarkerRelationPickSelectionCommand(state, options, command, renderUi) {
|
|
1822
|
+
const pick = state.markerRelationPick;
|
|
1823
|
+
if (!pick?.active)
|
|
1824
|
+
return false;
|
|
1825
|
+
const document = state.session?.getState().workingDocument ?? null;
|
|
1826
|
+
const graph = document ? options.documentAdapter.getMarkerGraph?.(document) ?? null : null;
|
|
1827
|
+
if (!document || !graph) {
|
|
1828
|
+
updateMarkerRelationPickWarning(state, '当前文档没有 marker graph。');
|
|
1829
|
+
if (renderUi)
|
|
1830
|
+
renderUi();
|
|
1831
|
+
return true;
|
|
1832
|
+
}
|
|
1833
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
1834
|
+
const targetId = readMarkerRelationPickTargetId(command);
|
|
1835
|
+
if (!targetId) {
|
|
1836
|
+
updateMarkerRelationPickWarning(state, '没有拾取到有效端点,请点击标记或场景对象。');
|
|
1837
|
+
if (renderUi)
|
|
1838
|
+
renderUi();
|
|
1839
|
+
return true;
|
|
1840
|
+
}
|
|
1841
|
+
const endpoint = resolveMarkerRelationPickEndpoint(graph, hierarchy, targetId);
|
|
1842
|
+
if (!endpoint) {
|
|
1843
|
+
updateMarkerRelationPickWarning(state, `暂不支持的关系端点:${targetId}`);
|
|
1844
|
+
if (renderUi)
|
|
1845
|
+
renderUi();
|
|
1846
|
+
return true;
|
|
1847
|
+
}
|
|
1848
|
+
if (!pick.from) {
|
|
1849
|
+
const status = `关系拾取中:${formatMarkerRelationPickEndpointLabel(endpoint, graph, hierarchy)} 已作为起点,请点击终点。`;
|
|
1850
|
+
state.markerRelationPick = {
|
|
1851
|
+
...pick,
|
|
1852
|
+
from: endpoint,
|
|
1853
|
+
status,
|
|
1854
|
+
statusTone: 'success',
|
|
1855
|
+
};
|
|
1856
|
+
state.status = status;
|
|
1857
|
+
state.statusTone = 'success';
|
|
1858
|
+
state.statusToneStatus = state.status;
|
|
1859
|
+
state.statusDetails = '关系拾取会消费下一次有效视口点击,不改变当前选择。';
|
|
1860
|
+
if (renderUi)
|
|
1861
|
+
renderUi();
|
|
1862
|
+
return true;
|
|
1863
|
+
}
|
|
1864
|
+
const handled = patchMarkerRelation(state, options, {
|
|
1865
|
+
action: 'create',
|
|
1866
|
+
type: pick.type ?? '',
|
|
1867
|
+
fromKind: pick.from.kind,
|
|
1868
|
+
fromId: pick.from.id,
|
|
1869
|
+
toKind: endpoint.kind,
|
|
1870
|
+
toId: endpoint.id,
|
|
1871
|
+
directed: pick.directed,
|
|
1872
|
+
});
|
|
1873
|
+
if (handled && state.markerRelationPick) {
|
|
1874
|
+
state.markerRelationPick = {
|
|
1875
|
+
...state.markerRelationPick,
|
|
1876
|
+
status: state.status,
|
|
1877
|
+
statusTone: state.statusTone,
|
|
1878
|
+
};
|
|
1509
1879
|
}
|
|
1880
|
+
if (renderUi)
|
|
1881
|
+
renderUi();
|
|
1510
1882
|
return true;
|
|
1511
1883
|
}
|
|
1884
|
+
function readMarkerRelationPickTargetId(command) {
|
|
1885
|
+
if (command.type === 'selection.clear' || command.type === 'selection.remove')
|
|
1886
|
+
return null;
|
|
1887
|
+
return command.activeId?.trim() || command.selectedIds[command.selectedIds.length - 1]?.trim() || null;
|
|
1888
|
+
}
|
|
1889
|
+
function resolveMarkerRelationPickEndpoint(graph, hierarchy, targetId) {
|
|
1890
|
+
const markerId = readSpatialMarkerIdFromHierarchySelection(graph, targetId, hierarchy);
|
|
1891
|
+
if (markerId && findSpatialMarkerById(graph, markerId)) {
|
|
1892
|
+
return { kind: 'marker', id: markerId };
|
|
1893
|
+
}
|
|
1894
|
+
const hierarchyItem = hierarchy.find(item => item.id === targetId) ?? null;
|
|
1895
|
+
if (!hierarchyItem)
|
|
1896
|
+
return null;
|
|
1897
|
+
if (hierarchyItem.role === 'marker' || hierarchyItem.role === 'group' || hierarchyItem.role === 'root')
|
|
1898
|
+
return null;
|
|
1899
|
+
return { kind: 'scene-object', id: targetId };
|
|
1900
|
+
}
|
|
1901
|
+
function updateMarkerRelationPickWarning(state, status) {
|
|
1902
|
+
if (state.markerRelationPick) {
|
|
1903
|
+
state.markerRelationPick = {
|
|
1904
|
+
...state.markerRelationPick,
|
|
1905
|
+
status,
|
|
1906
|
+
statusTone: 'warning',
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
state.status = status;
|
|
1910
|
+
state.statusTone = 'warning';
|
|
1911
|
+
state.statusToneStatus = state.status;
|
|
1912
|
+
state.statusDetails = status;
|
|
1913
|
+
}
|
|
1512
1914
|
function renameSceneGraphNode(state, options, intent) {
|
|
1513
1915
|
const document = state.session?.getState().workingDocument;
|
|
1514
1916
|
if (state.mode !== 'editor' || !state.session || !document)
|
|
@@ -1524,7 +1926,10 @@ function renameSceneGraphNode(state, options, intent) {
|
|
|
1524
1926
|
state.status = `Rename rejected: ${intent.id}`;
|
|
1525
1927
|
return true;
|
|
1526
1928
|
}
|
|
1527
|
-
const
|
|
1929
|
+
const session = state.session;
|
|
1930
|
+
if (!session)
|
|
1931
|
+
return false;
|
|
1932
|
+
const result = session.dispatch({
|
|
1528
1933
|
type: 'document.patch',
|
|
1529
1934
|
label: patch.label ?? `Rename ${intent.id}`,
|
|
1530
1935
|
patch: patch.patch,
|
|
@@ -1536,7 +1941,7 @@ function renameSceneGraphNode(state, options, intent) {
|
|
|
1536
1941
|
return true;
|
|
1537
1942
|
}
|
|
1538
1943
|
rebuildProjectionFromDocument(state, options, result.workingDocument, result.selection);
|
|
1539
|
-
state.summary = summarizeDocument(options, result.workingDocument,
|
|
1944
|
+
state.summary = summarizeDocument(options, result.workingDocument, session.getSource());
|
|
1540
1945
|
state.status = patch.label ?? `Renamed ${intent.id}`;
|
|
1541
1946
|
return true;
|
|
1542
1947
|
}
|
|
@@ -1550,7 +1955,10 @@ function createSceneGraphGroup(state, options, intent) {
|
|
|
1550
1955
|
state.status = 'Create empty rejected';
|
|
1551
1956
|
return true;
|
|
1552
1957
|
}
|
|
1553
|
-
const
|
|
1958
|
+
const session = state.session;
|
|
1959
|
+
if (!session)
|
|
1960
|
+
return false;
|
|
1961
|
+
const result = session.dispatch({
|
|
1554
1962
|
type: 'document.patch',
|
|
1555
1963
|
label: patch.label ?? 'Create Empty',
|
|
1556
1964
|
patch: patch.patch,
|
|
@@ -1572,7 +1980,7 @@ function createSceneGraphGroup(state, options, intent) {
|
|
|
1572
1980
|
}).selection;
|
|
1573
1981
|
}
|
|
1574
1982
|
rebuildProjectionFromDocument(state, options, result.workingDocument, selection);
|
|
1575
|
-
state.summary = summarizeDocument(options, result.workingDocument,
|
|
1983
|
+
state.summary = summarizeDocument(options, result.workingDocument, session.getSource());
|
|
1576
1984
|
state.status = patch.label ?? (createdId ? `Created empty ${createdId}` : 'Created empty');
|
|
1577
1985
|
return true;
|
|
1578
1986
|
}
|
|
@@ -1615,6 +2023,182 @@ function createSceneGraphPrimitive(state, options, intent) {
|
|
|
1615
2023
|
state.status = patch.label ?? (createdId ? `Created ${intent.shape} ${createdId}` : `Created ${intent.shape}`);
|
|
1616
2024
|
return true;
|
|
1617
2025
|
}
|
|
2026
|
+
function createSceneGraphMarker(state, options, intent) {
|
|
2027
|
+
const document = state.session?.getState().workingDocument;
|
|
2028
|
+
if (state.mode !== 'editor' || !state.session || !document)
|
|
2029
|
+
return false;
|
|
2030
|
+
cancelActiveOperation(state);
|
|
2031
|
+
const markerType = intent.markerType?.trim() ?? '';
|
|
2032
|
+
const markerTypes = options.documentAdapter.getMarkerTypeCatalog?.(document) ?? [];
|
|
2033
|
+
const markerTypeDefinition = markerType
|
|
2034
|
+
? markerTypes.find(definition => definition.type === markerType) ?? null
|
|
2035
|
+
: null;
|
|
2036
|
+
if (markerType && !markerTypeDefinition) {
|
|
2037
|
+
state.status = `Create marker rejected: unknown marker type ${markerType}`;
|
|
2038
|
+
state.statusTone = 'warning';
|
|
2039
|
+
state.statusToneStatus = state.status;
|
|
2040
|
+
return true;
|
|
2041
|
+
}
|
|
2042
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
2043
|
+
const parentId = resolveCreateMarkerParentId(hierarchy, intent.parentId ?? null);
|
|
2044
|
+
const marker = createSceneGraphMarkerNode(state, options, document, intent, markerTypeDefinition);
|
|
2045
|
+
state.markerCreateReservations.ids.add(marker.id);
|
|
2046
|
+
state.markerCreateReservations.labels.add(marker.label);
|
|
2047
|
+
const patch = options.documentAdapter.createMarkerGraphPatch?.(document, {
|
|
2048
|
+
type: 'marker.create-box',
|
|
2049
|
+
marker,
|
|
2050
|
+
parentId,
|
|
2051
|
+
});
|
|
2052
|
+
if (!patch) {
|
|
2053
|
+
state.markerCreateReservations.ids.delete(marker.id);
|
|
2054
|
+
state.markerCreateReservations.labels.delete(marker.label);
|
|
2055
|
+
state.status = `Create marker rejected: ${marker.label}`;
|
|
2056
|
+
state.statusTone = 'warning';
|
|
2057
|
+
state.statusToneStatus = state.status;
|
|
2058
|
+
return true;
|
|
2059
|
+
}
|
|
2060
|
+
const result = state.session.dispatch({
|
|
2061
|
+
type: 'document.patch',
|
|
2062
|
+
label: patch.label ?? `Create marker ${marker.label}`,
|
|
2063
|
+
patch: patch.patch,
|
|
2064
|
+
targetId: patch.createdId ?? patch.changedId ?? marker.id,
|
|
2065
|
+
...withProjectionImpact(resolveProjectionImpact(patch, createKnownCreatedReprojectProjectionImpact({
|
|
2066
|
+
createdNodeIds: [patch.createdId ?? patch.changedId ?? marker.id],
|
|
2067
|
+
reprojectIds: patch.changedIds,
|
|
2068
|
+
}))),
|
|
2069
|
+
});
|
|
2070
|
+
if (!result.documentChanged) {
|
|
2071
|
+
state.markerCreateReservations.ids.delete(marker.id);
|
|
2072
|
+
state.markerCreateReservations.labels.delete(marker.label);
|
|
2073
|
+
state.status = `Create marker unchanged: ${marker.label}`;
|
|
2074
|
+
state.statusTone = 'warning';
|
|
2075
|
+
state.statusToneStatus = state.status;
|
|
2076
|
+
return true;
|
|
2077
|
+
}
|
|
2078
|
+
const createdId = patch.createdId ?? patch.changedId ?? marker.id;
|
|
2079
|
+
releaseReflectedMarkerCreateReservation(state, options, result.workingDocument, marker, createdId);
|
|
2080
|
+
let selection = result.selection;
|
|
2081
|
+
if (createdId && isNodeSelectableInDocument(options, result.workingDocument, createdId)) {
|
|
2082
|
+
selection = state.session.dispatch({
|
|
2083
|
+
type: 'selection.replace',
|
|
2084
|
+
selectedIds: [createdId],
|
|
2085
|
+
activeId: createdId,
|
|
2086
|
+
label: 'Select Created Marker',
|
|
2087
|
+
}).selection;
|
|
2088
|
+
}
|
|
2089
|
+
rebuildProjectionFromDocument(state, options, result.workingDocument, selection);
|
|
2090
|
+
state.summary = summarizeDocument(options, result.workingDocument, state.session.getSource());
|
|
2091
|
+
state.status = patch.label ?? `Created marker ${marker.label}`;
|
|
2092
|
+
state.statusTone = 'success';
|
|
2093
|
+
state.statusToneStatus = state.status;
|
|
2094
|
+
state.statusDetails = '';
|
|
2095
|
+
return true;
|
|
2096
|
+
}
|
|
2097
|
+
function releaseReflectedMarkerCreateReservation(state, options, document, marker, createdId) {
|
|
2098
|
+
const reflectedIds = new Set([
|
|
2099
|
+
marker.id,
|
|
2100
|
+
...(createdId ? [createdId] : []),
|
|
2101
|
+
]);
|
|
2102
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
2103
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
2104
|
+
const markerReflected = hierarchy.some(item => reflectedIds.has(item.id))
|
|
2105
|
+
|| (graph?.markers ?? []).some(candidate => reflectedIds.has(candidate.id));
|
|
2106
|
+
if (!markerReflected)
|
|
2107
|
+
return;
|
|
2108
|
+
state.markerCreateReservations.ids.delete(marker.id);
|
|
2109
|
+
state.markerCreateReservations.labels.delete(marker.label);
|
|
2110
|
+
}
|
|
2111
|
+
function createSceneGraphMarkerNode(state, options, document, intent, markerTypeDefinition) {
|
|
2112
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
2113
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
2114
|
+
const existingIds = new Set([
|
|
2115
|
+
...hierarchy.map(item => item.id),
|
|
2116
|
+
...(graph?.markers ?? []).map(marker => marker.id),
|
|
2117
|
+
...state.markerCreateReservations.ids,
|
|
2118
|
+
]);
|
|
2119
|
+
const markerType = markerTypeDefinition?.type ?? intent.markerType?.trim() ?? '';
|
|
2120
|
+
const baseLabel = intent.name?.trim() || markerTypeDefinition?.label || 'Marker';
|
|
2121
|
+
const id = createUniqueMarkerId(existingIds, markerType || baseLabel);
|
|
2122
|
+
const label = createUniqueMarkerLabel(new Set([
|
|
2123
|
+
...hierarchy.map(item => item.label),
|
|
2124
|
+
...state.markerCreateReservations.labels,
|
|
2125
|
+
]), baseLabel, id, markerType);
|
|
2126
|
+
const geometry = createInitialMarkerGeometry(state, markerTypeDefinition?.kind);
|
|
2127
|
+
return {
|
|
2128
|
+
id,
|
|
2129
|
+
kind: normalizeMarkerNodeKind(markerTypeDefinition?.kind),
|
|
2130
|
+
markerType,
|
|
2131
|
+
label,
|
|
2132
|
+
geometry,
|
|
2133
|
+
...(markerTypeDefinition?.defaultTags?.length ? { tags: [...markerTypeDefinition.defaultTags] } : {}),
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
function createInitialMarkerGeometry(state, markerKind) {
|
|
2137
|
+
const defaultScale = createDefaultMarkerScale(markerKind);
|
|
2138
|
+
const selection = state.session?.getState().selection ?? { selectedIds: [], activeId: null };
|
|
2139
|
+
const bounds = selection.selectedIds.length > 0
|
|
2140
|
+
? state.projection?.getSelectionBounds(selection.selectedIds) ?? null
|
|
2141
|
+
: null;
|
|
2142
|
+
const center = bounds?.center ?? { x: 0, y: defaultScale.y / 2, z: 0 };
|
|
2143
|
+
if (!isRegionMarkerKind(markerKind)) {
|
|
2144
|
+
return {
|
|
2145
|
+
kind: 'point',
|
|
2146
|
+
coordinateSpace: 'world',
|
|
2147
|
+
position: { ...center },
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
const size = bounds && isRegionMarkerKind(markerKind)
|
|
2151
|
+
? normalizeMarkerBoxSize(bounds.size)
|
|
2152
|
+
: defaultScale;
|
|
2153
|
+
return {
|
|
2154
|
+
kind: 'box',
|
|
2155
|
+
coordinateSpace: 'world',
|
|
2156
|
+
center: { ...center },
|
|
2157
|
+
size: { ...size },
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
function createDefaultMarkerScale(markerKind) {
|
|
2161
|
+
return isRegionMarkerKind(markerKind)
|
|
2162
|
+
? { x: 4, y: 1, z: 4 }
|
|
2163
|
+
: { x: 0.5, y: 0.5, z: 0.5 };
|
|
2164
|
+
}
|
|
2165
|
+
function normalizeMarkerNodeKind(kind) {
|
|
2166
|
+
return kind?.trim() || 'point';
|
|
2167
|
+
}
|
|
2168
|
+
function isRegionMarkerKind(kind) {
|
|
2169
|
+
return (kind?.trim() || 'point') === 'region';
|
|
2170
|
+
}
|
|
2171
|
+
function createUniqueMarkerId(existingIds, markerType) {
|
|
2172
|
+
const base = sanitizeMarkerIdPart(markerType) || 'marker';
|
|
2173
|
+
if (!existingIds.has(base))
|
|
2174
|
+
return base;
|
|
2175
|
+
let index = 2;
|
|
2176
|
+
while (existingIds.has(`${base}-${index}`))
|
|
2177
|
+
index += 1;
|
|
2178
|
+
return `${base}-${index}`;
|
|
2179
|
+
}
|
|
2180
|
+
function createUniqueMarkerLabel(existingLabels, baseLabel, id, markerType) {
|
|
2181
|
+
const trimmed = baseLabel.trim() || markerType || id;
|
|
2182
|
+
if (!existingLabels.has(trimmed))
|
|
2183
|
+
return trimmed;
|
|
2184
|
+
let index = 2;
|
|
2185
|
+
while (existingLabels.has(`${trimmed} ${index}`))
|
|
2186
|
+
index += 1;
|
|
2187
|
+
return `${trimmed} ${index}`;
|
|
2188
|
+
}
|
|
2189
|
+
function sanitizeMarkerIdPart(value) {
|
|
2190
|
+
return value
|
|
2191
|
+
.trim()
|
|
2192
|
+
.toLowerCase()
|
|
2193
|
+
.replace(/[^a-z0-9_-]+/g, '-')
|
|
2194
|
+
.replace(/^-+|-+$/g, '');
|
|
2195
|
+
}
|
|
2196
|
+
function resolveCreateMarkerParentId(hierarchy, parentId) {
|
|
2197
|
+
if (!parentId)
|
|
2198
|
+
return null;
|
|
2199
|
+
const parent = hierarchy.find(item => item.id === parentId) ?? null;
|
|
2200
|
+
return parent?.canHaveChildren === true && parent.role !== 'marker' ? parent.id : null;
|
|
2201
|
+
}
|
|
1618
2202
|
function deleteSceneGraphNodes(state, options, intent) {
|
|
1619
2203
|
const document = state.session?.getState().workingDocument;
|
|
1620
2204
|
if (state.mode !== 'editor' || !state.session || !document)
|
|
@@ -1880,7 +2464,7 @@ function sanitizeSelection(state, options, document, selection) {
|
|
|
1880
2464
|
});
|
|
1881
2465
|
return result?.selection ?? { selectedIds, activeId };
|
|
1882
2466
|
}
|
|
1883
|
-
function commitEditorStateChange(state, renderUi, reason, mutate, options = {}) {
|
|
2467
|
+
function commitEditorStateChange(state, harnessOptions, renderUi, reason, mutate, options = {}) {
|
|
1884
2468
|
const beforeRevision = state.editorStateRevision;
|
|
1885
2469
|
mutate();
|
|
1886
2470
|
const revision = beforeRevision + 1;
|
|
@@ -1890,7 +2474,7 @@ function commitEditorStateChange(state, renderUi, reason, mutate, options = {})
|
|
|
1890
2474
|
const selection = typeof options.syncSelectionArtifacts === 'boolean'
|
|
1891
2475
|
? state.session?.getState({ cloneDocuments: false }).selection ?? { selectedIds: [], activeId: null }
|
|
1892
2476
|
: options.syncSelectionArtifacts;
|
|
1893
|
-
syncSelectionToProjection(state, selection, reason, revision);
|
|
2477
|
+
syncSelectionToProjection(state, harnessOptions, selection, reason, revision);
|
|
1894
2478
|
}
|
|
1895
2479
|
else if (options.syncTransformOperationArtifacts) {
|
|
1896
2480
|
syncTransformOperationSettingsToSceneArtifacts(state, reason, revision);
|
|
@@ -1900,44 +2484,317 @@ function commitEditorStateChange(state, renderUi, reason, mutate, options = {})
|
|
|
1900
2484
|
}
|
|
1901
2485
|
return revision;
|
|
1902
2486
|
}
|
|
1903
|
-
function syncSelectionToProjection(state, selection, reason = 'projection-selection', revision = state.editorStateRevision) {
|
|
2487
|
+
function syncSelectionToProjection(state, options, selection, reason = 'projection-selection', revision = state.editorStateRevision) {
|
|
1904
2488
|
if (!beginSceneArtifactSync(state, reason, revision))
|
|
1905
2489
|
return;
|
|
1906
|
-
state.
|
|
1907
|
-
state.
|
|
2490
|
+
const projectedSelection = state.prefabStage ? EMPTY_EDITOR_SELECTION : selection;
|
|
2491
|
+
state.projection?.syncSelection(projectedSelection);
|
|
2492
|
+
syncMarkerOverlayToSceneArtifacts(state, options, projectedSelection);
|
|
2493
|
+
state.gizmo?.setSelection(projectedSelection);
|
|
1908
2494
|
syncTransformGizmoToSceneArtifacts(state, reason, revision, false);
|
|
1909
2495
|
}
|
|
1910
|
-
function syncCurrentSelectionToSceneArtifacts(state, reason = 'projection-selection-current', revision = state.editorStateRevision) {
|
|
1911
|
-
syncSelectionToProjection(state, state.session?.getState({ cloneDocuments: false }).selection ?? { selectedIds: [], activeId: null }, reason, revision);
|
|
2496
|
+
function syncCurrentSelectionToSceneArtifacts(state, options, reason = 'projection-selection-current', revision = state.editorStateRevision) {
|
|
2497
|
+
syncSelectionToProjection(state, options, state.session?.getState({ cloneDocuments: false }).selection ?? { selectedIds: [], activeId: null }, reason, revision);
|
|
1912
2498
|
}
|
|
1913
|
-
function
|
|
1914
|
-
|
|
2499
|
+
function syncMarkerOverlayToSceneArtifacts(state, options, selection) {
|
|
2500
|
+
const overlay = state.markerOverlay;
|
|
2501
|
+
if (!overlay)
|
|
1915
2502
|
return;
|
|
1916
|
-
const
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
}
|
|
1925
|
-
function
|
|
1926
|
-
|
|
2503
|
+
const document = state.session?.getState().workingDocument ?? null;
|
|
2504
|
+
const input = document
|
|
2505
|
+
? createMarkerOverlayInput(state, options, document, selection, createMarkerGraphHierarchyVisibility(state))
|
|
2506
|
+
: { markers: [] };
|
|
2507
|
+
const changed = overlay.sync(input);
|
|
2508
|
+
syncMarkerRelationHighlightRenderLoop(state, input);
|
|
2509
|
+
if (changed)
|
|
2510
|
+
requestEditorSceneFrame(state, 'marker-overlay-sync');
|
|
2511
|
+
}
|
|
2512
|
+
function syncMarkerRelationHighlightRenderLoop(state, input) {
|
|
2513
|
+
const shouldRun = (input.relations ?? []).some(relation => relation.active === true);
|
|
2514
|
+
if (shouldRun === state.markerRelationHighlightContinuous)
|
|
1927
2515
|
return;
|
|
1928
|
-
state.
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
2516
|
+
state.markerRelationHighlightContinuous = shouldRun;
|
|
2517
|
+
if (shouldRun) {
|
|
2518
|
+
if (canUseContinuousViewportRenderLoop()) {
|
|
2519
|
+
state.viewportRenderCoordinator?.beginContinuous(MARKER_RELATION_HIGHLIGHT_RENDER_REASON);
|
|
2520
|
+
}
|
|
2521
|
+
else {
|
|
2522
|
+
state.viewportRenderCoordinator?.requestFrame(MARKER_RELATION_HIGHLIGHT_RENDER_REASON);
|
|
2523
|
+
}
|
|
1934
2524
|
}
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
2525
|
+
else {
|
|
2526
|
+
state.viewportRenderCoordinator?.endContinuous(MARKER_RELATION_HIGHLIGHT_RENDER_REASON);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
function canUseContinuousViewportRenderLoop() {
|
|
2530
|
+
if (typeof window === 'undefined' || typeof window.requestAnimationFrame !== 'function')
|
|
2531
|
+
return true;
|
|
2532
|
+
if (typeof window.cancelAnimationFrame !== 'function')
|
|
2533
|
+
return true;
|
|
2534
|
+
let completedSynchronously = false;
|
|
2535
|
+
const frameId = window.requestAnimationFrame(() => {
|
|
2536
|
+
completedSynchronously = true;
|
|
2537
|
+
});
|
|
2538
|
+
window.cancelAnimationFrame(frameId);
|
|
2539
|
+
return !completedSynchronously;
|
|
2540
|
+
}
|
|
2541
|
+
function createMarkerOverlayInput(state, options, document, selection, visibility) {
|
|
2542
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
2543
|
+
if (!graph)
|
|
2544
|
+
return { markers: [] };
|
|
2545
|
+
if (state.viewportTools.markersVisible === false)
|
|
2546
|
+
return { markers: [], relations: [] };
|
|
2547
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
2548
|
+
const fullHierarchy = createMarkerGraphHierarchyItems(hierarchy, graph, visibility);
|
|
2549
|
+
const selectedIds = new Set(selection.selectedIds);
|
|
2550
|
+
const markerTypeLabels = new Map((options.documentAdapter.getMarkerTypeCatalog?.(document) ?? []).map(definition => [definition.type, definition.label]));
|
|
2551
|
+
const activeMarkerId = selection.activeId
|
|
2552
|
+
? readSpatialMarkerIdFromHierarchySelection(graph, selection.activeId, hierarchy)
|
|
2553
|
+
: null;
|
|
2554
|
+
return {
|
|
2555
|
+
activeMarkerRowId: selection.activeId,
|
|
2556
|
+
selectedMarkerRowIds: selection.selectedIds,
|
|
2557
|
+
markers: fullHierarchy
|
|
2558
|
+
.filter(item => item.role === 'marker')
|
|
2559
|
+
.map((item) => {
|
|
2560
|
+
const markerId = readSpatialMarkerIdFromHierarchySelection(graph, item.id, hierarchy);
|
|
2561
|
+
const marker = markerId ? findSpatialMarkerById(graph, markerId) : null;
|
|
2562
|
+
if (!marker || !isMarkerOverlayRowVisible(graph, item, marker.id, hierarchy, visibility))
|
|
2563
|
+
return null;
|
|
2564
|
+
const overlayMarker = resolveMarkerForOverlay(state, applyMarkerOverlayTransformPreview(state, item.id, marker));
|
|
2565
|
+
return {
|
|
2566
|
+
markerRowId: item.id,
|
|
2567
|
+
marker: overlayMarker,
|
|
2568
|
+
markerTypeLabel: markerTypeLabels.get(marker.markerType),
|
|
2569
|
+
selected: selectedIds.has(item.id),
|
|
2570
|
+
active: selection.activeId === item.id,
|
|
2571
|
+
};
|
|
2572
|
+
})
|
|
2573
|
+
.filter((entry) => !!entry),
|
|
2574
|
+
relations: createSelectedMarkerRelationOverlayEntries(state, resolveMarkerGraphForOverlay(state, graph), activeMarkerId, hierarchy, visibility),
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
function resolveMarkerGraphForOverlay(state, graph) {
|
|
2578
|
+
return {
|
|
2579
|
+
...graph,
|
|
2580
|
+
markers: graph.markers.map(marker => resolveMarkerForOverlay(state, marker)),
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
function resolveMarkerForOverlay(state, marker) {
|
|
2584
|
+
if (!isObjectBoundMarkerForOverlay(marker))
|
|
2585
|
+
return marker;
|
|
2586
|
+
const target = readSceneObjectMarkerTargetForOverlay(marker);
|
|
2587
|
+
if (!target)
|
|
2588
|
+
return marker;
|
|
2589
|
+
const bounds = state.projection?.getSelectionBounds([target.id]) ?? null;
|
|
2590
|
+
if (!bounds)
|
|
2591
|
+
return marker;
|
|
2592
|
+
return {
|
|
2593
|
+
...marker,
|
|
2594
|
+
spatial: {
|
|
2595
|
+
status: 'resolved',
|
|
2596
|
+
coordinateSpace: 'world',
|
|
2597
|
+
origin: { ...bounds.center },
|
|
2598
|
+
bounds: {
|
|
2599
|
+
min: { ...bounds.min },
|
|
2600
|
+
max: { ...bounds.max },
|
|
2601
|
+
center: { ...bounds.center },
|
|
2602
|
+
size: { ...bounds.size },
|
|
2603
|
+
},
|
|
2604
|
+
dimensions: { ...bounds.size },
|
|
2605
|
+
localFrame: {
|
|
2606
|
+
origin: { ...bounds.center },
|
|
2607
|
+
forward: marker.semanticFrame?.forward ? { ...marker.semanticFrame.forward } : { x: 0, y: 0, z: 1 },
|
|
2608
|
+
right: marker.semanticFrame?.right ? { ...marker.semanticFrame.right } : { x: 1, y: 0, z: 0 },
|
|
2609
|
+
up: marker.semanticFrame?.up ? { ...marker.semanticFrame.up } : { x: 0, y: 1, z: 0 },
|
|
2610
|
+
},
|
|
2611
|
+
target,
|
|
2612
|
+
},
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
function isObjectBoundMarkerForOverlay(marker) {
|
|
2616
|
+
const markerKind = marker.kind?.trim().toLowerCase();
|
|
2617
|
+
return markerKind === 'object'
|
|
2618
|
+
|| markerKind === 'object-bound'
|
|
2619
|
+
|| markerKind === 'object-bounds'
|
|
2620
|
+
|| marker.geometry.kind === 'object-bounds';
|
|
2621
|
+
}
|
|
2622
|
+
function readSceneObjectMarkerTargetForOverlay(marker) {
|
|
2623
|
+
if (marker.target?.kind === 'scene-object')
|
|
2624
|
+
return marker.target;
|
|
2625
|
+
if (marker.geometry.kind === 'object-bounds' && marker.geometry.target.kind === 'scene-object') {
|
|
2626
|
+
return marker.geometry.target;
|
|
2627
|
+
}
|
|
2628
|
+
if (marker.geometry.kind === 'point' && marker.geometry.target?.kind === 'scene-object') {
|
|
2629
|
+
return marker.geometry.target;
|
|
2630
|
+
}
|
|
2631
|
+
return null;
|
|
2632
|
+
}
|
|
2633
|
+
function createSelectedMarkerRelationOverlayEntries(state, graph, activeMarkerId, hierarchy, visibility) {
|
|
2634
|
+
if (!activeMarkerId)
|
|
2635
|
+
return [];
|
|
2636
|
+
if (!findSpatialMarkerById(graph, activeMarkerId))
|
|
2637
|
+
return [];
|
|
2638
|
+
const entries = [];
|
|
2639
|
+
for (const relation of getSpatialMarkerDirectRelations(graph, activeMarkerId)) {
|
|
2640
|
+
const from = resolveRelationOverlayEndpoint(state, graph, activeMarkerId, relation.from, hierarchy, visibility);
|
|
2641
|
+
const to = resolveRelationOverlayEndpoint(state, graph, activeMarkerId, relation.to, hierarchy, visibility);
|
|
2642
|
+
if (!from || !to)
|
|
2643
|
+
continue;
|
|
2644
|
+
entries.push({
|
|
2645
|
+
id: relation.id,
|
|
2646
|
+
type: relation.type,
|
|
2647
|
+
...(relation.label ? { label: relation.label } : {}),
|
|
2648
|
+
from,
|
|
2649
|
+
to,
|
|
2650
|
+
directed: relation.directed ?? true,
|
|
2651
|
+
active: state.activeMarkerRelationId === relation.id,
|
|
2652
|
+
});
|
|
2653
|
+
}
|
|
2654
|
+
return entries;
|
|
2655
|
+
}
|
|
2656
|
+
function applyMarkerOverlayTransformPreview(state, markerRowId, marker) {
|
|
2657
|
+
const transform = readActiveTransformPreviewTransform(state, markerRowId)
|
|
2658
|
+
?? (markerRowId === marker.id ? null : readActiveTransformPreviewTransform(state, marker.id));
|
|
2659
|
+
if (!transform)
|
|
2660
|
+
return marker;
|
|
2661
|
+
const semanticFrame = createMarkerSemanticFrameFromTransform(marker, transform);
|
|
2662
|
+
if (marker.geometry.kind === 'point') {
|
|
2663
|
+
return {
|
|
2664
|
+
...marker,
|
|
2665
|
+
geometry: {
|
|
2666
|
+
...marker.geometry,
|
|
2667
|
+
coordinateSpace: 'world',
|
|
2668
|
+
position: { ...transform.position },
|
|
2669
|
+
},
|
|
2670
|
+
semanticFrame,
|
|
2671
|
+
};
|
|
2672
|
+
}
|
|
2673
|
+
if (marker.geometry.kind !== 'box') {
|
|
2674
|
+
return {
|
|
2675
|
+
...marker,
|
|
2676
|
+
semanticFrame,
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
return {
|
|
2680
|
+
...marker,
|
|
2681
|
+
semanticFrame,
|
|
2682
|
+
geometry: {
|
|
2683
|
+
...marker.geometry,
|
|
2684
|
+
center: { ...transform.position },
|
|
2685
|
+
rotation: { ...transform.rotation },
|
|
2686
|
+
size: { ...transform.scale },
|
|
2687
|
+
},
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2690
|
+
function resolveRelationOverlayEndpoint(state, graph, activeMarkerId, endpoint, hierarchy, visibility) {
|
|
2691
|
+
if (endpoint.kind === 'marker') {
|
|
2692
|
+
if (!isMarkerVisibleForOverlayRelation(graph, endpoint.id, hierarchy, visibility))
|
|
2693
|
+
return null;
|
|
2694
|
+
const preview = endpoint.id === activeMarkerId ? readActiveTransformPreviewPosition(state) : null;
|
|
2695
|
+
if (preview)
|
|
2696
|
+
return preview;
|
|
2697
|
+
const marker = findSpatialMarkerById(graph, endpoint.id);
|
|
2698
|
+
return marker ? resolveMarkerOverlayEndpoint(marker) : null;
|
|
2699
|
+
}
|
|
2700
|
+
if (endpoint.kind === 'scene-object') {
|
|
2701
|
+
const preview = endpoint.id === state.activeTransformPreview?.activeId ? readActiveTransformPreviewPosition(state) : null;
|
|
2702
|
+
if (preview)
|
|
2703
|
+
return preview;
|
|
2704
|
+
const bounds = state.projection?.getSelectionBounds([endpoint.id]) ?? null;
|
|
2705
|
+
return bounds ? { ...bounds.center } : null;
|
|
2706
|
+
}
|
|
2707
|
+
return null;
|
|
2708
|
+
}
|
|
2709
|
+
function isMarkerVisibleForOverlayRelation(graph, markerId, hierarchy, visibility) {
|
|
2710
|
+
if (isSpatialMarkerBackedByHierarchyRow(graph, markerId, hierarchy)) {
|
|
2711
|
+
const markerRow = hierarchy.find(item => item.id === markerId);
|
|
2712
|
+
return markerRow?.enabled !== false && !isMarkerHiddenInHierarchyVisibility(markerId, visibility);
|
|
2713
|
+
}
|
|
2714
|
+
const markerRow = hierarchy.find(item => readMarkerGraphHierarchyMarkerIdForGraph(graph, item.id, hierarchy) === markerId);
|
|
2715
|
+
if (markerRow?.enabled === false)
|
|
2716
|
+
return false;
|
|
2717
|
+
return isSpatialMarkerHierarchyVisible(markerId, visibility);
|
|
2718
|
+
}
|
|
2719
|
+
function isMarkerHiddenInHierarchyVisibility(markerId, visibility) {
|
|
2720
|
+
const hiddenMarkerIds = visibility.hiddenMarkerIds;
|
|
2721
|
+
if (!hiddenMarkerIds)
|
|
2722
|
+
return false;
|
|
2723
|
+
return Array.isArray(hiddenMarkerIds)
|
|
2724
|
+
? hiddenMarkerIds.includes(markerId)
|
|
2725
|
+
: hiddenMarkerIds.has(markerId);
|
|
2726
|
+
}
|
|
2727
|
+
function readActiveTransformPreviewPosition(state) {
|
|
2728
|
+
const transform = readActiveTransformPreviewTransform(state);
|
|
2729
|
+
return transform ? { ...transform.position } : null;
|
|
2730
|
+
}
|
|
2731
|
+
function readActiveTransformPreviewTransform(state, targetId) {
|
|
2732
|
+
const preview = state.activeTransformPreview;
|
|
2733
|
+
const activeId = targetId ?? preview?.activeId ?? null;
|
|
2734
|
+
if (!preview || !activeId)
|
|
2735
|
+
return null;
|
|
2736
|
+
const transform = preview.transforms[activeId] ?? null;
|
|
2737
|
+
return transform && isEditorTransformTrsSnapshot(transform) ? transform : null;
|
|
2738
|
+
}
|
|
2739
|
+
function resolveMarkerOverlayEndpoint(marker) {
|
|
2740
|
+
if (isObjectBoundMarkerForOverlay(marker) && marker.spatial?.bounds?.center) {
|
|
2741
|
+
return { ...marker.spatial.bounds.center };
|
|
2742
|
+
}
|
|
2743
|
+
if (marker.geometry.kind === 'box')
|
|
2744
|
+
return { ...marker.geometry.center };
|
|
2745
|
+
if (marker.geometry.kind === 'point')
|
|
2746
|
+
return { ...marker.geometry.position };
|
|
2747
|
+
return null;
|
|
2748
|
+
}
|
|
2749
|
+
function isMarkerOverlayRowVisible(graph, item, markerId, hierarchy, visibility) {
|
|
2750
|
+
if (item.enabled === false)
|
|
2751
|
+
return false;
|
|
2752
|
+
if (readMarkerGraphHierarchyMarkerIdForGraph(graph, item.id, hierarchy)) {
|
|
2753
|
+
return isSpatialMarkerHierarchyVisible(markerId, visibility);
|
|
2754
|
+
}
|
|
2755
|
+
return true;
|
|
2756
|
+
}
|
|
2757
|
+
function createMarkerGraphHierarchyVisibility(state) {
|
|
2758
|
+
return {
|
|
2759
|
+
rootVisible: state.markerGraphVisibility.rootVisible,
|
|
2760
|
+
hiddenMarkerIds: state.markerGraphVisibility.hiddenMarkerIds,
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
function createMarkerRelationVisibilityOptions(state) {
|
|
2764
|
+
return {
|
|
2765
|
+
markersVisible: state.viewportTools.markersVisible !== false,
|
|
2766
|
+
rootVisible: state.markerGraphVisibility.rootVisible,
|
|
2767
|
+
hiddenMarkerIds: state.markerGraphVisibility.hiddenMarkerIds,
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
function syncTransformGizmoToSceneArtifacts(state, reason, revision = state.editorStateRevision, beginSync = true) {
|
|
2771
|
+
if (beginSync && !beginSceneArtifactSync(state, reason, revision))
|
|
2772
|
+
return;
|
|
2773
|
+
const gizmo = state.gizmo;
|
|
2774
|
+
if (gizmo) {
|
|
2775
|
+
gizmo.setTool(state.transformTool);
|
|
2776
|
+
gizmo.setSpace(state.transformSpace);
|
|
2777
|
+
gizmo.setConstraint(state.transformConstraint);
|
|
2778
|
+
gizmo.refreshSelection();
|
|
2779
|
+
}
|
|
2780
|
+
requestEditorArtifactRevealFrame(state, reason);
|
|
2781
|
+
}
|
|
2782
|
+
function syncTransformOperationSettingsToSceneArtifacts(state, reason, revision = state.editorStateRevision) {
|
|
2783
|
+
if (!beginSceneArtifactSync(state, reason, revision))
|
|
2784
|
+
return;
|
|
2785
|
+
state.gizmo?.setOperationSettings(state.transformOperationSettings);
|
|
2786
|
+
requestEditorSceneFrame(state, reason);
|
|
2787
|
+
}
|
|
2788
|
+
function beginSceneArtifactSync(state, reason, revision) {
|
|
2789
|
+
if (revision < state.sceneArtifactRevision) {
|
|
2790
|
+
return false;
|
|
2791
|
+
}
|
|
2792
|
+
state.sceneArtifactRevision = revision;
|
|
2793
|
+
return true;
|
|
2794
|
+
}
|
|
2795
|
+
function invalidateEditorScene(state, reason, options) {
|
|
2796
|
+
if (options && state.sceneCameraPreviewEnabled) {
|
|
2797
|
+
syncSceneCameraPreview(state, options);
|
|
1941
2798
|
}
|
|
1942
2799
|
state.viewportRenderCoordinator?.invalidateScene(reason);
|
|
1943
2800
|
}
|
|
@@ -1949,7 +2806,7 @@ function requestEditorArtifactRevealFrame(state, reason) {
|
|
|
1949
2806
|
coordinator.requestRevealFrame(`${reason}-artifact-reveal`);
|
|
1950
2807
|
}
|
|
1951
2808
|
function syncEditorWorldAppearanceFromDocument(state, options, document, reason) {
|
|
1952
|
-
const appearance =
|
|
2809
|
+
const appearance = resolveEffectiveLocalEditorWorldAppearance(state, options, document);
|
|
1953
2810
|
const key = serializeLocalEditorWorldAppearance(appearance);
|
|
1954
2811
|
if (key === state.worldAppearanceKey)
|
|
1955
2812
|
return;
|
|
@@ -1962,16 +2819,18 @@ function syncEditorWorldAppearanceFromDocument(state, options, document, reason)
|
|
|
1962
2819
|
requestEditorSceneFrame(state, reason);
|
|
1963
2820
|
}
|
|
1964
2821
|
function syncEditorWorldRenderingFromDocument(state, options, document, reason, refreshProjection = false) {
|
|
1965
|
-
const rendering =
|
|
2822
|
+
const rendering = resolveEffectiveLocalEditorWorldRendering(state, options, document);
|
|
1966
2823
|
const key = serializeLocalEditorWorldRendering(rendering);
|
|
1967
2824
|
if (key === state.worldRenderingKey) {
|
|
1968
2825
|
if (refreshProjection)
|
|
1969
2826
|
state.shadowPreview?.refresh();
|
|
2827
|
+
state.world?.refreshSkyBackdrop();
|
|
1970
2828
|
return;
|
|
1971
2829
|
}
|
|
1972
2830
|
state.worldRenderingKey = key;
|
|
1973
2831
|
state.shadowPreview?.setOptions(rendering.shadowPreview);
|
|
1974
2832
|
state.environmentTexture?.setProfile(rendering.environment);
|
|
2833
|
+
state.world?.refreshSkyBackdrop();
|
|
1975
2834
|
state.postProcess?.setProfile(rendering.postProcess);
|
|
1976
2835
|
requestEditorSceneFrame(state, reason);
|
|
1977
2836
|
}
|
|
@@ -1985,6 +2844,14 @@ function resolveLocalEditorWorldAppearance(options, document) {
|
|
|
1985
2844
|
grid: documentAppearance?.grid ?? options.world?.grid,
|
|
1986
2845
|
};
|
|
1987
2846
|
}
|
|
2847
|
+
function resolveEffectiveLocalEditorWorldAppearance(state, options, document) {
|
|
2848
|
+
if (!state.prefabStage)
|
|
2849
|
+
return resolveLocalEditorWorldAppearance(options, document);
|
|
2850
|
+
return {
|
|
2851
|
+
...resolveLocalEditorWorldAppearance(options, document),
|
|
2852
|
+
...PREFAB_STAGE_STUDIO_WORLD_APPEARANCE,
|
|
2853
|
+
};
|
|
2854
|
+
}
|
|
1988
2855
|
function resolveLocalEditorWorldRendering(options, document) {
|
|
1989
2856
|
const documentRendering = document
|
|
1990
2857
|
? options.documentAdapter.getWorldRendering?.(document) ?? null
|
|
@@ -1995,6 +2862,15 @@ function resolveLocalEditorWorldRendering(options, document) {
|
|
|
1995
2862
|
postProcess: documentRendering?.postProcess ?? null,
|
|
1996
2863
|
};
|
|
1997
2864
|
}
|
|
2865
|
+
function resolveEffectiveLocalEditorWorldRendering(state, options, document) {
|
|
2866
|
+
const rendering = resolveLocalEditorWorldRendering(options, document);
|
|
2867
|
+
if (!state.prefabStage)
|
|
2868
|
+
return rendering;
|
|
2869
|
+
return {
|
|
2870
|
+
...rendering,
|
|
2871
|
+
environment: PREFAB_STAGE_STUDIO_ENVIRONMENT,
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
1998
2874
|
function hasEditorWorldAppearanceSky(appearance) {
|
|
1999
2875
|
return !!appearance && Object.prototype.hasOwnProperty.call(appearance, 'sky');
|
|
2000
2876
|
}
|
|
@@ -2119,6 +2995,9 @@ function selectBrowserAsset(state, assetId) {
|
|
|
2119
2995
|
function handleBrowserAssetAction(state, options, input) {
|
|
2120
2996
|
if (input.browserAssetId)
|
|
2121
2997
|
state.selectedAssetId = input.browserAssetId;
|
|
2998
|
+
if (input.actionId === 'asset.edit-prefab') {
|
|
2999
|
+
return enterPrefabStage(state, options, input);
|
|
3000
|
+
}
|
|
2122
3001
|
if (input.actionId === 'asset.add-to-scene') {
|
|
2123
3002
|
return input.assetId ? addAssetToDocument(state, options, input.assetId).ok : false;
|
|
2124
3003
|
}
|
|
@@ -2141,18 +3020,182 @@ function handleBrowserAssetAction(state, options, input) {
|
|
|
2141
3020
|
state.statusToneStatus = state.status;
|
|
2142
3021
|
return true;
|
|
2143
3022
|
}
|
|
2144
|
-
function
|
|
3023
|
+
function handleInspectorAction(state, options, input) {
|
|
3024
|
+
if (!input.actionId.startsWith('prefab.'))
|
|
3025
|
+
return false;
|
|
3026
|
+
const assetId = readInspectorActionParamString(input, 'assetId');
|
|
3027
|
+
const browserAssetId = readInspectorActionParamString(input, 'browserAssetId')
|
|
3028
|
+
?? (assetId ? resolveBrowserAssetItemId(state, options, assetId) : null)
|
|
3029
|
+
?? undefined;
|
|
3030
|
+
if (input.actionId === 'prefab.edit-definition') {
|
|
3031
|
+
if (!assetId) {
|
|
3032
|
+
state.status = 'Prefab action unavailable: missing prefab asset id';
|
|
3033
|
+
state.statusTone = 'warning';
|
|
3034
|
+
state.statusToneStatus = state.status;
|
|
3035
|
+
return true;
|
|
3036
|
+
}
|
|
3037
|
+
return enterPrefabStage(state, options, {
|
|
3038
|
+
actionId: 'asset.edit-prefab',
|
|
3039
|
+
assetId,
|
|
3040
|
+
browserAssetId,
|
|
3041
|
+
assetKind: 'prefab',
|
|
3042
|
+
});
|
|
3043
|
+
}
|
|
3044
|
+
if (input.actionId === 'prefab.select-definition' || input.actionId === 'prefab.ping-source') {
|
|
3045
|
+
if (!assetId) {
|
|
3046
|
+
state.status = `Prefab action unavailable: ${input.actionId}`;
|
|
3047
|
+
state.statusTone = 'warning';
|
|
3048
|
+
state.statusToneStatus = state.status;
|
|
3049
|
+
return true;
|
|
3050
|
+
}
|
|
3051
|
+
const selected = browserAssetId ? selectBrowserAsset(state, browserAssetId) : false;
|
|
3052
|
+
if (!selected && browserAssetId) {
|
|
3053
|
+
state.status = `Selected asset ${browserAssetId}`;
|
|
3054
|
+
state.statusTone = 'success';
|
|
3055
|
+
state.statusToneStatus = state.status;
|
|
3056
|
+
return true;
|
|
3057
|
+
}
|
|
3058
|
+
if (!browserAssetId) {
|
|
3059
|
+
state.status = `Asset not visible in browser: ${assetId}`;
|
|
3060
|
+
state.statusTone = 'warning';
|
|
3061
|
+
state.statusToneStatus = state.status;
|
|
3062
|
+
return true;
|
|
3063
|
+
}
|
|
3064
|
+
return selected;
|
|
3065
|
+
}
|
|
3066
|
+
state.status = `Prefab inspector action unavailable: ${input.actionId}`;
|
|
3067
|
+
state.statusTone = 'warning';
|
|
3068
|
+
state.statusToneStatus = state.status;
|
|
3069
|
+
return true;
|
|
3070
|
+
}
|
|
3071
|
+
function readInspectorActionParamString(input, key) {
|
|
3072
|
+
const value = input.params?.[key];
|
|
3073
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
|
|
3074
|
+
}
|
|
3075
|
+
function enterPrefabStage(state, options, input) {
|
|
3076
|
+
if (state.mode !== 'editor')
|
|
3077
|
+
return false;
|
|
3078
|
+
const sessionState = state.session?.getState({ cloneDocuments: false });
|
|
3079
|
+
const document = sessionState?.workingDocument ?? null;
|
|
3080
|
+
const assetId = input.assetId?.trim();
|
|
3081
|
+
if (!document || !assetId)
|
|
3082
|
+
return false;
|
|
3083
|
+
cancelActiveOperation(state);
|
|
3084
|
+
const previousPrefabStage = state.prefabStage;
|
|
3085
|
+
const asset = findAssetByResolvedId(state, options, assetId);
|
|
3086
|
+
const browserItem = findBrowserAssetItemByResolvedId(state, options, document, input.browserAssetId ?? assetId);
|
|
3087
|
+
const descriptor = options.documentAdapter.getPrefabStageDescriptor?.(document, {
|
|
3088
|
+
assetId,
|
|
3089
|
+
browserAssetId: input.browserAssetId,
|
|
3090
|
+
asset,
|
|
3091
|
+
}) ?? createFallbackPrefabStageDescriptor(assetId, input.browserAssetId, browserItem);
|
|
3092
|
+
if (!descriptor) {
|
|
3093
|
+
state.status = `Prefab stage unavailable: ${assetId}`;
|
|
3094
|
+
state.statusTone = 'warning';
|
|
3095
|
+
state.statusToneStatus = state.status;
|
|
3096
|
+
return true;
|
|
3097
|
+
}
|
|
3098
|
+
if (previousPrefabStage)
|
|
3099
|
+
state.prefabComparisonPreview?.clear();
|
|
3100
|
+
state.prefabStage = {
|
|
3101
|
+
descriptor,
|
|
3102
|
+
asset,
|
|
3103
|
+
selectedItemId: 'prefab-root',
|
|
3104
|
+
restoreGridVisible: previousPrefabStage?.restoreGridVisible ?? state.gridVisible,
|
|
3105
|
+
restoreSceneCameraPreviewEnabled: previousPrefabStage?.restoreSceneCameraPreviewEnabled ?? state.sceneCameraPreviewEnabled,
|
|
3106
|
+
};
|
|
3107
|
+
setGridVisibleForEditorContext(state, false, 'prefab-stage-enter-grid');
|
|
3108
|
+
state.sceneCameraPreviewEnabled = false;
|
|
3109
|
+
state.sceneCameraPreview?.setActive(false);
|
|
3110
|
+
state.status = `Editing Prefab ${descriptor.label}`;
|
|
3111
|
+
state.statusTone = 'success';
|
|
3112
|
+
state.statusToneStatus = state.status;
|
|
3113
|
+
state.statusDetails = '';
|
|
3114
|
+
rebuildProjectionFromDocument(state, options, document, EMPTY_EDITOR_SELECTION);
|
|
3115
|
+
return true;
|
|
3116
|
+
}
|
|
3117
|
+
function exitPrefabStage(state, options) {
|
|
3118
|
+
if (!state.prefabStage)
|
|
3119
|
+
return false;
|
|
3120
|
+
const sessionState = state.session?.getState({ cloneDocuments: false });
|
|
3121
|
+
const label = sessionState?.workingDocument
|
|
3122
|
+
? resolvePrefabStageDescriptorForDocument(state, options, sessionState.workingDocument)?.label ?? state.prefabStage.descriptor.label
|
|
3123
|
+
: state.prefabStage.descriptor.label;
|
|
3124
|
+
const restoreGridVisible = state.prefabStage.restoreGridVisible;
|
|
3125
|
+
const restoreSceneCameraPreviewEnabled = state.prefabStage.restoreSceneCameraPreviewEnabled;
|
|
3126
|
+
state.prefabStage = null;
|
|
3127
|
+
setGridVisibleForEditorContext(state, restoreGridVisible, 'prefab-stage-exit-grid');
|
|
3128
|
+
if (sessionState?.workingDocument) {
|
|
3129
|
+
rebuildProjectionFromDocument(state, options, sessionState.workingDocument, sessionState.selection);
|
|
3130
|
+
}
|
|
3131
|
+
state.sceneCameraPreviewEnabled = restoreSceneCameraPreviewEnabled;
|
|
3132
|
+
if (restoreSceneCameraPreviewEnabled)
|
|
3133
|
+
syncSceneCameraPreview(state, options);
|
|
3134
|
+
else
|
|
3135
|
+
state.sceneCameraPreview?.setActive(false);
|
|
3136
|
+
state.status = `Exited Prefab ${label}`;
|
|
3137
|
+
state.statusTone = 'default';
|
|
3138
|
+
state.statusToneStatus = state.status;
|
|
3139
|
+
state.statusDetails = '';
|
|
3140
|
+
return true;
|
|
3141
|
+
}
|
|
3142
|
+
function selectPrefabStageItem(state, itemId) {
|
|
3143
|
+
if (!state.prefabStage || state.prefabStage.selectedItemId === itemId)
|
|
3144
|
+
return false;
|
|
3145
|
+
state.prefabStage = {
|
|
3146
|
+
...state.prefabStage,
|
|
3147
|
+
selectedItemId: itemId,
|
|
3148
|
+
};
|
|
3149
|
+
return true;
|
|
3150
|
+
}
|
|
3151
|
+
function createFallbackPrefabStageDescriptor(assetId, browserAssetId, item) {
|
|
3152
|
+
if (!item?.prefab)
|
|
3153
|
+
return null;
|
|
3154
|
+
return {
|
|
3155
|
+
assetId: item.prefab.id || item.assetId || assetId,
|
|
3156
|
+
browserAssetId: browserAssetId ?? item.id,
|
|
3157
|
+
label: item.prefab.name || item.label || item.prefab.id || assetId,
|
|
3158
|
+
sourceAssetId: item.prefab.sourceAssetId,
|
|
3159
|
+
sourceAssetGuid: item.prefab.sourceAssetGuid,
|
|
3160
|
+
readonly: item.prefab.readonly === true,
|
|
3161
|
+
previewNodeId: `prefab-stage:${item.prefab.id || item.assetId || assetId}`,
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
3164
|
+
function resolvePrefabStageDescriptorForDocument(state, options, document) {
|
|
3165
|
+
const stage = state.prefabStage;
|
|
3166
|
+
if (!stage)
|
|
3167
|
+
return null;
|
|
3168
|
+
const assetId = stage.descriptor.assetId;
|
|
3169
|
+
const browserAssetId = stage.descriptor.browserAssetId;
|
|
3170
|
+
const descriptor = options.documentAdapter.getPrefabStageDescriptor?.(document, {
|
|
3171
|
+
assetId,
|
|
3172
|
+
browserAssetId,
|
|
3173
|
+
asset: stage.asset,
|
|
3174
|
+
});
|
|
3175
|
+
if (descriptor) {
|
|
3176
|
+
return {
|
|
3177
|
+
...stage.descriptor,
|
|
3178
|
+
...descriptor,
|
|
3179
|
+
browserAssetId: descriptor.browserAssetId ?? browserAssetId,
|
|
3180
|
+
previewNodeId: descriptor.previewNodeId ?? stage.descriptor.previewNodeId,
|
|
3181
|
+
};
|
|
3182
|
+
}
|
|
3183
|
+
const browserItem = findBrowserAssetItemByResolvedId(state, options, document, browserAssetId ?? assetId);
|
|
3184
|
+
return createFallbackPrefabStageDescriptor(assetId, browserAssetId, browserItem) ?? stage.descriptor;
|
|
3185
|
+
}
|
|
3186
|
+
function patchBrowserAssetAction(state, options, input, selectionOverride) {
|
|
2145
3187
|
const session = state.session;
|
|
2146
3188
|
const document = session?.getState().workingDocument ?? null;
|
|
2147
3189
|
if (!session || !document || !options.documentAdapter.createAssetActionPatch)
|
|
2148
3190
|
return false;
|
|
2149
3191
|
const asset = input.assetId ? findAssetByResolvedId(state, options, input.assetId) : null;
|
|
3192
|
+
const sessionSelection = session.getState().selection;
|
|
2150
3193
|
const patch = options.documentAdapter.createAssetActionPatch({
|
|
2151
3194
|
...input,
|
|
2152
3195
|
document,
|
|
2153
3196
|
asset,
|
|
2154
|
-
activeId:
|
|
2155
|
-
selectedIds: [...
|
|
3197
|
+
activeId: selectionOverride?.activeId ?? sessionSelection.activeId,
|
|
3198
|
+
selectedIds: selectionOverride?.selectedIds ?? [...sessionSelection.selectedIds],
|
|
2156
3199
|
});
|
|
2157
3200
|
if (!patch)
|
|
2158
3201
|
return false;
|
|
@@ -2175,6 +3218,9 @@ function patchBrowserAssetAction(state, options, input) {
|
|
|
2175
3218
|
reprojectProjectionForChangedIds(state, options, result.workingDocument, patch.reprojectIds);
|
|
2176
3219
|
else if (patch.changedIds)
|
|
2177
3220
|
syncProjectionForChangedIds(state, options, result.workingDocument, patch.changedIds);
|
|
3221
|
+
else if (state.prefabStage && result.documentChanged) {
|
|
3222
|
+
rebuildProjectionFromDocument(state, options, result.workingDocument, EMPTY_EDITOR_SELECTION);
|
|
3223
|
+
}
|
|
2178
3224
|
if (input.actionId === 'asset.delete-material' && input.browserAssetId && state.selectedAssetId === input.browserAssetId) {
|
|
2179
3225
|
state.selectedAssetId = null;
|
|
2180
3226
|
}
|
|
@@ -2191,12 +3237,18 @@ function selectCreatedBrowserAssetFromPatch(state, options, createdId, input) {
|
|
|
2191
3237
|
else if (input.actionId === 'asset.create-material' || input.actionId === 'asset.duplicate-material') {
|
|
2192
3238
|
state.selectedAssetId = `material:${createdId}`;
|
|
2193
3239
|
}
|
|
3240
|
+
else if (input.actionId === 'asset.create-prefab'
|
|
3241
|
+
|| input.actionId === 'asset.create-prefab-from-game-object'
|
|
3242
|
+
|| input.actionId === 'asset.duplicate-prefab') {
|
|
3243
|
+
state.selectedAssetId = resolveBrowserAssetItemId(state, options, `prefab:${createdId}`) ?? `prefab:${createdId}`;
|
|
3244
|
+
}
|
|
2194
3245
|
}
|
|
2195
3246
|
function armAssetPlacement(state, options, assetId) {
|
|
2196
3247
|
if (state.mode !== 'editor')
|
|
2197
3248
|
return false;
|
|
2198
3249
|
cancelActiveOperation(state);
|
|
2199
|
-
const
|
|
3250
|
+
const document = state.session?.getState({ cloneDocuments: false }).workingDocument ?? null;
|
|
3251
|
+
const asset = findAssetOrDocumentBrowserItemByResolvedId(state, options, document, assetId);
|
|
2200
3252
|
if (!asset)
|
|
2201
3253
|
return false;
|
|
2202
3254
|
state.armedPlacement = { assetId, asset };
|
|
@@ -2298,7 +3350,7 @@ function addAssetToDocument(state, options, assetId, createOptions = {}) {
|
|
|
2298
3350
|
state.status = 'Asset creation rejected: document is not loaded';
|
|
2299
3351
|
return { ok: false, assetId, changed: false, status: state.status, error: 'document_not_loaded' };
|
|
2300
3352
|
}
|
|
2301
|
-
const asset =
|
|
3353
|
+
const asset = findAssetOrDocumentBrowserItemByResolvedId(state, options, beforeDocument, assetId);
|
|
2302
3354
|
if (!asset) {
|
|
2303
3355
|
state.status = `Asset creation rejected: ${assetId} not found`;
|
|
2304
3356
|
return { ok: false, assetId, changed: false, status: state.status, error: 'asset_not_found' };
|
|
@@ -2340,7 +3392,7 @@ function addAssetToDocument(state, options, assetId, createOptions = {}) {
|
|
|
2340
3392
|
if (projectedNode) {
|
|
2341
3393
|
state.projection?.projectNode(projectedNode);
|
|
2342
3394
|
if (selectionResult)
|
|
2343
|
-
syncSelectionToProjection(state, selectionResult.selection);
|
|
3395
|
+
syncSelectionToProjection(state, options, selectionResult.selection);
|
|
2344
3396
|
invalidateEditorScene(state, 'projection-project-node');
|
|
2345
3397
|
}
|
|
2346
3398
|
}
|
|
@@ -2367,6 +3419,12 @@ function patchSerializedProperty(state, options, input) {
|
|
|
2367
3419
|
return false;
|
|
2368
3420
|
}
|
|
2369
3421
|
const payload = transaction.payload;
|
|
3422
|
+
if (state.prefabStage) {
|
|
3423
|
+
return patchPrefabStageInspectorProperty(state, options, payload);
|
|
3424
|
+
}
|
|
3425
|
+
const markerGraphPatched = patchMarkerGraphInspectorProperty(state, options, document, payload);
|
|
3426
|
+
if (markerGraphPatched !== null)
|
|
3427
|
+
return markerGraphPatched;
|
|
2370
3428
|
const shouldReprojectInspectorEdit = shouldReprojectEditorInspectorEditPath(payload.path);
|
|
2371
3429
|
if (targetIds.length > 1) {
|
|
2372
3430
|
const patch = options.documentAdapter.createSerializedMultiPropertyPatch?.({
|
|
@@ -2424,6 +3482,10 @@ function patchSerializedProperty(state, options, input) {
|
|
|
2424
3482
|
}
|
|
2425
3483
|
else
|
|
2426
3484
|
syncProjectionForChangedIds(state, options, workingDocument, projectionSyncIds);
|
|
3485
|
+
if (isMarkerInspectorEditPath(payload.path)) {
|
|
3486
|
+
syncMarkerOverlayToSceneArtifacts(state, options, result.selection);
|
|
3487
|
+
state.gizmo?.refreshSelection();
|
|
3488
|
+
}
|
|
2427
3489
|
state.summary = summarizeDocument(options, workingDocument, state.session.getSource());
|
|
2428
3490
|
state.status = patch.label ?? `Patch ${payload.path} on ${targetIds.length} objects`;
|
|
2429
3491
|
return true;
|
|
@@ -2460,7 +3522,7 @@ function patchSerializedProperty(state, options, input) {
|
|
|
2460
3522
|
reprojectProjectionForChangedIds(state, options, result.workingDocument, patch.changedIds ?? [patch.changedId ?? payload.targetId]);
|
|
2461
3523
|
}
|
|
2462
3524
|
else if (isSerializedProjectionMetadataPath(payload.path)) {
|
|
2463
|
-
syncProjectionMetadataForChangedIds(state, options, result.workingDocument, [patch.changedId ?? payload.targetId], {
|
|
3525
|
+
syncProjectionMetadataForChangedIds(state, options, result.workingDocument, patch.changedIds ?? [patch.changedId ?? payload.targetId], {
|
|
2464
3526
|
invalidateStaticShadowArtifact: payload.path === 'shadowMode',
|
|
2465
3527
|
});
|
|
2466
3528
|
}
|
|
@@ -2470,11 +3532,313 @@ function patchSerializedProperty(state, options, input) {
|
|
|
2470
3532
|
syncProjectionForChangedIds(state, options, result.workingDocument, patch.changedIds);
|
|
2471
3533
|
else
|
|
2472
3534
|
syncProjectionForDispatchResult(state, options, result, patch.changedId ?? payload.targetId);
|
|
3535
|
+
if (isMarkerInspectorEditPath(payload.path)) {
|
|
3536
|
+
syncMarkerOverlayToSceneArtifacts(state, options, result.selection);
|
|
3537
|
+
state.gizmo?.refreshSelection();
|
|
3538
|
+
}
|
|
2473
3539
|
state.summary = summarizeDocument(options, result.workingDocument, state.session.getSource());
|
|
2474
3540
|
state.status = patch.label ?? `Patched ${payload.path}`;
|
|
2475
3541
|
return true;
|
|
2476
3542
|
}
|
|
3543
|
+
function patchPrefabStageInspectorProperty(state, options, payload) {
|
|
3544
|
+
if (payload.persistence !== 'document') {
|
|
3545
|
+
state.status = `Prefab field is not document-editable: ${payload.path}.`;
|
|
3546
|
+
return false;
|
|
3547
|
+
}
|
|
3548
|
+
const session = state.session;
|
|
3549
|
+
const document = session?.getState({ cloneDocuments: false }).workingDocument ?? null;
|
|
3550
|
+
if (!document)
|
|
3551
|
+
return false;
|
|
3552
|
+
const descriptor = resolvePrefabStageDescriptorForDocument(state, options, document) ?? state.prefabStage?.descriptor ?? null;
|
|
3553
|
+
if (!descriptor) {
|
|
3554
|
+
state.status = 'Prefab stage is not ready.';
|
|
3555
|
+
return false;
|
|
3556
|
+
}
|
|
3557
|
+
return patchBrowserAssetAction(state, options, {
|
|
3558
|
+
actionId: 'asset.edit-prefab-field',
|
|
3559
|
+
assetId: descriptor.assetId,
|
|
3560
|
+
browserAssetId: descriptor.browserAssetId,
|
|
3561
|
+
assetKind: 'prefab',
|
|
3562
|
+
fieldPath: payload.path,
|
|
3563
|
+
value: payload.value,
|
|
3564
|
+
});
|
|
3565
|
+
}
|
|
3566
|
+
function isMarkerInspectorEditPath(path) {
|
|
3567
|
+
return path === 'name' || path.startsWith('marker.');
|
|
3568
|
+
}
|
|
3569
|
+
function patchMarkerGraphInspectorProperty(state, options, document, payload) {
|
|
3570
|
+
if (!isMarkerInspectorEditPath(payload.path))
|
|
3571
|
+
return null;
|
|
3572
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
3573
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
3574
|
+
if (!readMarkerGraphHierarchyMarkerIdForGraph(graph, payload.targetId, hierarchy))
|
|
3575
|
+
return null;
|
|
3576
|
+
const markerTypes = options.documentAdapter.getMarkerTypeCatalog?.(document) ?? [];
|
|
3577
|
+
const command = createMarkerGraphPatchFromInspectorEdit(graph, payload, hierarchy, markerTypes);
|
|
3578
|
+
if (!command) {
|
|
3579
|
+
state.status = `Marker edit rejected: ${payload.path}`;
|
|
3580
|
+
return false;
|
|
3581
|
+
}
|
|
3582
|
+
const patch = options.documentAdapter.createMarkerGraphPatch?.(document, command);
|
|
3583
|
+
if (!patch) {
|
|
3584
|
+
state.status = `Marker edit rejected: ${payload.targetId}`;
|
|
3585
|
+
return false;
|
|
3586
|
+
}
|
|
3587
|
+
const result = state.session?.dispatch({
|
|
3588
|
+
type: 'document.patch',
|
|
3589
|
+
label: patch.label ?? `Patch marker ${payload.path}`,
|
|
3590
|
+
patch: patch.patch,
|
|
3591
|
+
targetId: payload.targetId,
|
|
3592
|
+
});
|
|
3593
|
+
if (!result?.documentChanged)
|
|
3594
|
+
return false;
|
|
3595
|
+
syncProjectionForDispatchResult(state, options, result, patch.changedId ?? payload.targetId);
|
|
3596
|
+
syncMarkerOverlayToSceneArtifacts(state, options, result.selection);
|
|
3597
|
+
state.gizmo?.refreshSelection();
|
|
3598
|
+
state.summary = summarizeDocument(options, result.workingDocument, state.session?.getSource());
|
|
3599
|
+
state.status = patch.label ?? `Patched marker ${payload.path}`;
|
|
3600
|
+
return true;
|
|
3601
|
+
}
|
|
3602
|
+
function patchMarkerTypeCatalog(state, options, input) {
|
|
3603
|
+
if (state.mode !== 'editor')
|
|
3604
|
+
return false;
|
|
3605
|
+
cancelActiveOperation(state);
|
|
3606
|
+
const document = state.session?.getState().workingDocument ?? null;
|
|
3607
|
+
if (!document)
|
|
3608
|
+
return false;
|
|
3609
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
3610
|
+
if (!graph || !options.documentAdapter.createMarkerGraphPatch)
|
|
3611
|
+
return false;
|
|
3612
|
+
const command = createMarkerTypeCatalogCommand(input);
|
|
3613
|
+
if (!command) {
|
|
3614
|
+
state.status = 'Marker type edit rejected';
|
|
3615
|
+
state.statusTone = 'warning';
|
|
3616
|
+
state.statusToneStatus = state.status;
|
|
3617
|
+
return false;
|
|
3618
|
+
}
|
|
3619
|
+
const patch = options.documentAdapter.createMarkerGraphPatch(document, command);
|
|
3620
|
+
if (!patch) {
|
|
3621
|
+
state.status = `Marker type edit rejected: ${input.type}`;
|
|
3622
|
+
state.statusTone = 'warning';
|
|
3623
|
+
state.statusToneStatus = state.status;
|
|
3624
|
+
return false;
|
|
3625
|
+
}
|
|
3626
|
+
const session = state.session;
|
|
3627
|
+
if (!session)
|
|
3628
|
+
return false;
|
|
3629
|
+
const result = session.dispatch({
|
|
3630
|
+
type: 'document.patch',
|
|
3631
|
+
label: patch.label ?? 'Patch marker type catalog',
|
|
3632
|
+
patch: patch.patch,
|
|
3633
|
+
targetId: session.getState().selection.activeId ?? undefined,
|
|
3634
|
+
});
|
|
3635
|
+
if (!result.documentChanged)
|
|
3636
|
+
return false;
|
|
3637
|
+
syncMarkerOverlayToSceneArtifacts(state, options, result.selection);
|
|
3638
|
+
state.summary = summarizeDocument(options, result.workingDocument, session.getSource());
|
|
3639
|
+
state.status = patch.label ?? 'Patched marker type catalog';
|
|
3640
|
+
state.statusTone = 'success';
|
|
3641
|
+
state.statusToneStatus = state.status;
|
|
3642
|
+
state.statusDetails = '';
|
|
3643
|
+
return true;
|
|
3644
|
+
}
|
|
3645
|
+
function patchMarkerRelation(state, options, input) {
|
|
3646
|
+
if (state.mode !== 'editor')
|
|
3647
|
+
return false;
|
|
3648
|
+
cancelActiveOperation(state);
|
|
3649
|
+
const document = state.session?.getState().workingDocument ?? null;
|
|
3650
|
+
if (!document)
|
|
3651
|
+
return false;
|
|
3652
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
3653
|
+
if (!graph)
|
|
3654
|
+
return false;
|
|
3655
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
3656
|
+
const activeId = state.session?.getState().selection.activeId ?? null;
|
|
3657
|
+
const activeMarkerId = activeId ? readSpatialMarkerIdFromHierarchySelection(graph, activeId, hierarchy) : null;
|
|
3658
|
+
if (input.action === 'pick-cancel') {
|
|
3659
|
+
state.markerRelationPick = null;
|
|
3660
|
+
state.status = '已取消关系端点拾取。';
|
|
3661
|
+
state.statusTone = 'success';
|
|
3662
|
+
state.statusToneStatus = state.status;
|
|
3663
|
+
state.statusDetails = '';
|
|
3664
|
+
return true;
|
|
3665
|
+
}
|
|
3666
|
+
if (input.action === 'pick-start') {
|
|
3667
|
+
const relationTypes = options.documentAdapter.getRelationTypeCatalog?.(document) ?? [];
|
|
3668
|
+
const type = input.type.trim();
|
|
3669
|
+
if (!type) {
|
|
3670
|
+
state.status = '请选择关系类型。';
|
|
3671
|
+
state.statusTone = 'warning';
|
|
3672
|
+
state.statusToneStatus = state.status;
|
|
3673
|
+
state.statusDetails = state.status;
|
|
3674
|
+
return true;
|
|
3675
|
+
}
|
|
3676
|
+
const typeDefinition = relationTypes.find(definition => definition.type === type);
|
|
3677
|
+
const from = createMarkerRelationPickEndpoint(input.fromKind, input.fromId);
|
|
3678
|
+
const directed = input.directed ?? typeDefinition?.directed ?? true;
|
|
3679
|
+
const typeLabel = typeDefinition?.label ?? type;
|
|
3680
|
+
const status = from
|
|
3681
|
+
? `关系拾取中:${formatMarkerRelationPickEndpointLabel(from, graph, hierarchy)} ${typeLabel} ... 请在视口点击终点。`
|
|
3682
|
+
: `关系拾取中:请选择 ${typeLabel} 的起点。`;
|
|
3683
|
+
state.markerRelationPick = {
|
|
3684
|
+
active: true,
|
|
3685
|
+
type,
|
|
3686
|
+
typeLabel,
|
|
3687
|
+
directed,
|
|
3688
|
+
from,
|
|
3689
|
+
status,
|
|
3690
|
+
statusTone: 'success',
|
|
3691
|
+
};
|
|
3692
|
+
state.status = status;
|
|
3693
|
+
state.statusTone = 'success';
|
|
3694
|
+
state.statusToneStatus = state.status;
|
|
3695
|
+
state.statusDetails = '关系拾取会消费下一次有效视口点击,不改变当前选择。';
|
|
3696
|
+
return true;
|
|
3697
|
+
}
|
|
3698
|
+
if (input.action === 'select') {
|
|
3699
|
+
const relationId = input.relationId.trim();
|
|
3700
|
+
if (!relationId)
|
|
3701
|
+
return false;
|
|
3702
|
+
const relation = graph.relations.find(candidate => candidate.id === relationId) ?? null;
|
|
3703
|
+
state.activeMarkerRelationId = relationId;
|
|
3704
|
+
state.status = relation ? `高亮关系:${relation.label ?? relation.type}` : `关系不存在:${relationId}`;
|
|
3705
|
+
state.statusTone = relation ? 'success' : 'warning';
|
|
3706
|
+
state.statusToneStatus = state.status;
|
|
3707
|
+
state.statusDetails = relation ? relationId : state.status;
|
|
3708
|
+
syncMarkerOverlayToSceneArtifacts(state, options, state.session?.getState().selection ?? { selectedIds: [], activeId: null });
|
|
3709
|
+
requestEditorSceneFrame(state, 'marker-relation-select');
|
|
3710
|
+
return true;
|
|
3711
|
+
}
|
|
3712
|
+
if (!options.documentAdapter.createMarkerGraphPatch)
|
|
3713
|
+
return false;
|
|
3714
|
+
const relationTypes = options.documentAdapter.getRelationTypeCatalog?.(document) ?? [];
|
|
3715
|
+
const result = input.action === 'reverse'
|
|
3716
|
+
? createMarkerRelationReverseCommand(input.relationId, graph)
|
|
3717
|
+
: input.action === 'delete-connected'
|
|
3718
|
+
? createMarkerRelationDeleteConnectedCommand(input.markerId ?? activeMarkerId ?? '', graph)
|
|
3719
|
+
: input.action === 'create' && input.toEndpoints && input.toEndpoints.length > 0
|
|
3720
|
+
? createMarkerRelationCreateManyCommand({
|
|
3721
|
+
type: input.type,
|
|
3722
|
+
fromKind: input.fromKind,
|
|
3723
|
+
fromId: input.fromId,
|
|
3724
|
+
toEndpoints: input.toEndpoints,
|
|
3725
|
+
directed: input.directed,
|
|
3726
|
+
}, graph, relationTypes, hierarchy, activeMarkerId)
|
|
3727
|
+
: createMarkerRelationCommand(input, graph, relationTypes, hierarchy, activeMarkerId);
|
|
3728
|
+
if (!result.command) {
|
|
3729
|
+
state.status = result.status;
|
|
3730
|
+
state.statusTone = result.statusTone;
|
|
3731
|
+
state.statusToneStatus = state.status;
|
|
3732
|
+
state.statusDetails = result.status;
|
|
3733
|
+
return true;
|
|
3734
|
+
}
|
|
3735
|
+
const patch = options.documentAdapter.createMarkerGraphPatch(document, result.command);
|
|
3736
|
+
if (!patch) {
|
|
3737
|
+
state.status = result.status || 'Marker relation edit rejected';
|
|
3738
|
+
state.statusTone = 'warning';
|
|
3739
|
+
state.statusToneStatus = state.status;
|
|
3740
|
+
state.statusDetails = state.status;
|
|
3741
|
+
return true;
|
|
3742
|
+
}
|
|
3743
|
+
const session = state.session;
|
|
3744
|
+
if (!session)
|
|
3745
|
+
return false;
|
|
3746
|
+
const patchResult = session.dispatch({
|
|
3747
|
+
type: 'document.patch',
|
|
3748
|
+
label: patch.label ?? result.status,
|
|
3749
|
+
patch: patch.patch,
|
|
3750
|
+
targetId: session.getState().selection.activeId ?? undefined,
|
|
3751
|
+
});
|
|
3752
|
+
if (!patchResult.documentChanged)
|
|
3753
|
+
return false;
|
|
3754
|
+
if (doesMarkerGraphCommandDeleteRelation(result.command, state.activeMarkerRelationId)) {
|
|
3755
|
+
state.activeMarkerRelationId = null;
|
|
3756
|
+
}
|
|
3757
|
+
if (!isMarkerRelationStillPresent(options, patchResult.workingDocument, state.activeMarkerRelationId)) {
|
|
3758
|
+
state.activeMarkerRelationId = null;
|
|
3759
|
+
}
|
|
3760
|
+
state.markerRelationPick = null;
|
|
3761
|
+
syncMarkerOverlayToSceneArtifacts(state, options, patchResult.selection);
|
|
3762
|
+
state.gizmo?.refreshSelection();
|
|
3763
|
+
state.summary = summarizeDocument(options, patchResult.workingDocument, session.getSource());
|
|
3764
|
+
state.status = patch.label ?? result.status;
|
|
3765
|
+
state.statusTone = result.statusTone;
|
|
3766
|
+
state.statusToneStatus = state.status;
|
|
3767
|
+
state.statusDetails = '';
|
|
3768
|
+
return true;
|
|
3769
|
+
}
|
|
3770
|
+
function createMarkerRelationPickEndpoint(kind, id) {
|
|
3771
|
+
const endpointKind = kind?.trim();
|
|
3772
|
+
const endpointId = id?.trim();
|
|
3773
|
+
if (!endpointKind || !endpointId)
|
|
3774
|
+
return null;
|
|
3775
|
+
if (endpointKind !== 'marker' && endpointKind !== 'scene-object')
|
|
3776
|
+
return null;
|
|
3777
|
+
return { kind: endpointKind, id: endpointId };
|
|
3778
|
+
}
|
|
3779
|
+
function formatMarkerRelationPickEndpointLabel(endpoint, graph, hierarchy) {
|
|
3780
|
+
const label = endpoint.kind === 'marker'
|
|
3781
|
+
? findSpatialMarkerById(graph, endpoint.id)?.label ?? endpoint.id
|
|
3782
|
+
: hierarchy.find(item => item.id === endpoint.id)?.label ?? endpoint.id;
|
|
3783
|
+
const kindLabel = endpoint.kind === 'marker' ? '标记' : '对象';
|
|
3784
|
+
return `${kindLabel} ${label}`;
|
|
3785
|
+
}
|
|
3786
|
+
function doesMarkerGraphCommandDeleteRelation(command, relationId) {
|
|
3787
|
+
if (!relationId)
|
|
3788
|
+
return false;
|
|
3789
|
+
switch (command.type) {
|
|
3790
|
+
case 'relation.delete':
|
|
3791
|
+
return command.relationId === relationId;
|
|
3792
|
+
case 'relation.delete-many':
|
|
3793
|
+
return command.relationIds.includes(relationId);
|
|
3794
|
+
default:
|
|
3795
|
+
return false;
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
function isMarkerRelationStillPresent(options, document, relationId) {
|
|
3799
|
+
if (!relationId)
|
|
3800
|
+
return true;
|
|
3801
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
3802
|
+
return !!graph?.relations.some(relation => relation.id === relationId);
|
|
3803
|
+
}
|
|
3804
|
+
function createMarkerTypeCatalogCommand(input) {
|
|
3805
|
+
const type = input.type.trim();
|
|
3806
|
+
if (!type)
|
|
3807
|
+
return null;
|
|
3808
|
+
if (input.action === 'delete') {
|
|
3809
|
+
return {
|
|
3810
|
+
type: 'marker-type.delete',
|
|
3811
|
+
typeId: type,
|
|
3812
|
+
};
|
|
3813
|
+
}
|
|
3814
|
+
const label = input.label?.trim() || type;
|
|
3815
|
+
const kind = input.kind?.trim() || undefined;
|
|
3816
|
+
const description = 'description' in input ? input.description?.trim() ?? '' : undefined;
|
|
3817
|
+
if (input.action === 'create') {
|
|
3818
|
+
return {
|
|
3819
|
+
type: 'marker-type.create',
|
|
3820
|
+
definition: {
|
|
3821
|
+
type,
|
|
3822
|
+
label,
|
|
3823
|
+
...(kind ? { kind } : {}),
|
|
3824
|
+
...(description ? { description } : {}),
|
|
3825
|
+
},
|
|
3826
|
+
};
|
|
3827
|
+
}
|
|
3828
|
+
return {
|
|
3829
|
+
type: 'marker-type.update',
|
|
3830
|
+
typeId: type,
|
|
3831
|
+
patch: {
|
|
3832
|
+
label,
|
|
3833
|
+
...(kind !== undefined ? { kind } : {}),
|
|
3834
|
+
...(description !== undefined ? { description } : {}),
|
|
3835
|
+
},
|
|
3836
|
+
};
|
|
3837
|
+
}
|
|
2477
3838
|
function patchHierarchyItemEnabled(state, options, input) {
|
|
3839
|
+
const markerVisibilityResult = patchMarkerGraphHierarchyVisibility(state, options, input);
|
|
3840
|
+
if (markerVisibilityResult !== 'unhandled')
|
|
3841
|
+
return markerVisibilityResult === 'changed';
|
|
2478
3842
|
return patchSerializedProperty(state, options, {
|
|
2479
3843
|
targetId: input.id,
|
|
2480
3844
|
path: 'enabled',
|
|
@@ -2486,6 +3850,47 @@ function patchHierarchyItemEnabled(state, options, input) {
|
|
|
2486
3850
|
source: 'toggle',
|
|
2487
3851
|
});
|
|
2488
3852
|
}
|
|
3853
|
+
function patchMarkerGraphHierarchyVisibility(state, options, input) {
|
|
3854
|
+
const document = state.session?.getState().workingDocument ?? null;
|
|
3855
|
+
if (!document)
|
|
3856
|
+
return 'unhandled';
|
|
3857
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
3858
|
+
if (!graph)
|
|
3859
|
+
return 'unhandled';
|
|
3860
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
3861
|
+
const visibility = state.markerGraphVisibility;
|
|
3862
|
+
if (isMarkerGraphHierarchyRootRowId(graph, input.id, hierarchy)) {
|
|
3863
|
+
if (visibility.rootVisible === input.enabled)
|
|
3864
|
+
return 'handled';
|
|
3865
|
+
visibility.rootVisible = input.enabled;
|
|
3866
|
+
state.editorStateRevision += 1;
|
|
3867
|
+
syncCurrentSelectionToSceneArtifacts(state, options, 'marker-graph-visibility-toggle', state.editorStateRevision);
|
|
3868
|
+
state.status = input.enabled ? 'Markers shown' : 'Markers hidden';
|
|
3869
|
+
state.statusTone = 'success';
|
|
3870
|
+
state.statusToneStatus = state.status;
|
|
3871
|
+
state.statusDetails = '';
|
|
3872
|
+
return 'changed';
|
|
3873
|
+
}
|
|
3874
|
+
const markerId = readMarkerGraphHierarchyMarkerIdForGraph(graph, input.id, hierarchy)
|
|
3875
|
+
?? (isSpatialMarkerBackedByHierarchyRow(graph, input.id, hierarchy) ? input.id : null);
|
|
3876
|
+
if (!markerId)
|
|
3877
|
+
return 'unhandled';
|
|
3878
|
+
const changed = input.enabled
|
|
3879
|
+
? visibility.hiddenMarkerIds.delete(markerId)
|
|
3880
|
+
: !visibility.hiddenMarkerIds.has(markerId);
|
|
3881
|
+
if (!input.enabled)
|
|
3882
|
+
visibility.hiddenMarkerIds.add(markerId);
|
|
3883
|
+
if (!changed)
|
|
3884
|
+
return 'handled';
|
|
3885
|
+
state.editorStateRevision += 1;
|
|
3886
|
+
syncCurrentSelectionToSceneArtifacts(state, options, 'marker-visibility-toggle', state.editorStateRevision);
|
|
3887
|
+
const marker = findSpatialMarkerById(graph, markerId);
|
|
3888
|
+
state.status = `${marker?.label ?? markerId} ${input.enabled ? 'shown' : 'hidden'}`;
|
|
3889
|
+
state.statusTone = 'success';
|
|
3890
|
+
state.statusToneStatus = state.status;
|
|
3891
|
+
state.statusDetails = '';
|
|
3892
|
+
return 'changed';
|
|
3893
|
+
}
|
|
2489
3894
|
async function applyRenderingPropertyChange(state, options, input) {
|
|
2490
3895
|
if (state.mode !== 'editor')
|
|
2491
3896
|
return false;
|
|
@@ -2735,6 +4140,11 @@ function findInspectorPropertyForEdit(state, options, document, input, targetIds
|
|
|
2735
4140
|
return findInspectorPropertyByPath(inspector, input.path);
|
|
2736
4141
|
}
|
|
2737
4142
|
function createInspectorObjectForEdit(state, options, document, input, targetIds) {
|
|
4143
|
+
if (targetIds.length === 1 && state.prefabStage) {
|
|
4144
|
+
const inspector = createPrefabStageInspectorObjectForEdit(state, options, document);
|
|
4145
|
+
if (inspector && inspector.targetIds.includes(input.targetId))
|
|
4146
|
+
return inspector;
|
|
4147
|
+
}
|
|
2738
4148
|
if (targetIds.length > 1) {
|
|
2739
4149
|
const activeId = state.session?.getState().selection.activeId ?? input.targetId ?? null;
|
|
2740
4150
|
const inspector = createInspectorMultiObjectBase(state, options, document, targetIds, activeId);
|
|
@@ -2742,6 +4152,9 @@ function createInspectorObjectForEdit(state, options, document, input, targetIds
|
|
|
2742
4152
|
return withDefaultEditorInspectorSections(state, options, document, inspector);
|
|
2743
4153
|
return null;
|
|
2744
4154
|
}
|
|
4155
|
+
const markerInspector = createMarkerInspectorObjectBase(options, document, input.targetId);
|
|
4156
|
+
if (markerInspector)
|
|
4157
|
+
return markerInspector;
|
|
2745
4158
|
const inspector = options.documentAdapter.getInspectorObject?.(document, input.targetId) ?? null;
|
|
2746
4159
|
if (inspector)
|
|
2747
4160
|
return withDefaultEditorInspectorSections(state, options, document, inspector);
|
|
@@ -2750,6 +4163,13 @@ function createInspectorObjectForEdit(state, options, document, input, targetIds
|
|
|
2750
4163
|
? withDefaultEditorInspectorSections(state, options, document, serializedObjectToInspectorObject(serializedObject, document))
|
|
2751
4164
|
: null;
|
|
2752
4165
|
}
|
|
4166
|
+
function createPrefabStageInspectorObjectForEdit(state, options, document) {
|
|
4167
|
+
const stage = state.prefabStage;
|
|
4168
|
+
if (!stage || !options.documentAdapter.getPrefabStageInspectorObject)
|
|
4169
|
+
return null;
|
|
4170
|
+
const descriptor = resolvePrefabStageDescriptorForDocument(state, options, document) ?? stage.descriptor;
|
|
4171
|
+
return options.documentAdapter.getPrefabStageInspectorObject(document, descriptor, stage.selectedItemId, createPrefabStageContext(state, descriptor)) ?? null;
|
|
4172
|
+
}
|
|
2753
4173
|
function findInspectorPropertyByPath(inspector, path) {
|
|
2754
4174
|
for (const section of inspector.sections) {
|
|
2755
4175
|
const property = section.properties.find(candidate => candidate.path === path);
|
|
@@ -2800,7 +4220,8 @@ function beginDuplicateDrag(state, options, input) {
|
|
|
2800
4220
|
return null;
|
|
2801
4221
|
if (state.duplicateDrag)
|
|
2802
4222
|
return null;
|
|
2803
|
-
const
|
|
4223
|
+
const session = state.session;
|
|
4224
|
+
const document = session.getState().workingDocument;
|
|
2804
4225
|
const patch = options.documentAdapter.createDuplicateSelectionPatch?.({
|
|
2805
4226
|
document,
|
|
2806
4227
|
targetIds: input.targetIds,
|
|
@@ -2811,9 +4232,9 @@ function beginDuplicateDrag(state, options, input) {
|
|
|
2811
4232
|
state.status = 'Duplicate drag rejected';
|
|
2812
4233
|
return null;
|
|
2813
4234
|
}
|
|
2814
|
-
const originalSelection =
|
|
4235
|
+
const originalSelection = session.getSelection();
|
|
2815
4236
|
const projectionImpact = resolveProjectionImpact(patch, createDuplicateProjectionImpact(patch));
|
|
2816
|
-
const result =
|
|
4237
|
+
const result = session.dispatch({
|
|
2817
4238
|
type: 'document.patch',
|
|
2818
4239
|
label: patch.label ?? `Duplicate ${input.targetIds.length} object(s)`,
|
|
2819
4240
|
patch: patch.patch,
|
|
@@ -2854,7 +4275,7 @@ function beginDuplicateDrag(state, options, input) {
|
|
|
2854
4275
|
})) {
|
|
2855
4276
|
rebuildProjectionFromDocument(state, options, result.workingDocument, selectionResult.selection);
|
|
2856
4277
|
}
|
|
2857
|
-
state.summary = summarizeDocument(options, result.workingDocument,
|
|
4278
|
+
state.summary = summarizeDocument(options, result.workingDocument, session.getSource());
|
|
2858
4279
|
state.status = patch.label ?? `Duplicated ${createdIds.length} object(s)`;
|
|
2859
4280
|
return {
|
|
2860
4281
|
targetIds: createdIds,
|
|
@@ -2884,6 +4305,9 @@ function commitGizmoTransform(state, options, event) {
|
|
|
2884
4305
|
if (state.mode !== 'editor' || !state.session)
|
|
2885
4306
|
return false;
|
|
2886
4307
|
const document = state.session.getState().workingDocument;
|
|
4308
|
+
const markerChanged = commitMarkerGizmoTransform(state, options, document, event);
|
|
4309
|
+
if (markerChanged !== null)
|
|
4310
|
+
return markerChanged;
|
|
2887
4311
|
if (isTransformBatchCommit(event)) {
|
|
2888
4312
|
const patch = options.documentAdapter.createTransformBatchPatch?.({
|
|
2889
4313
|
...event,
|
|
@@ -2934,7 +4358,10 @@ function commitGizmoTransform(state, options, event) {
|
|
|
2934
4358
|
}
|
|
2935
4359
|
return false;
|
|
2936
4360
|
}
|
|
2937
|
-
const
|
|
4361
|
+
const session = state.session;
|
|
4362
|
+
if (!session)
|
|
4363
|
+
return false;
|
|
4364
|
+
const result = session.dispatch({
|
|
2938
4365
|
type: 'document.patch',
|
|
2939
4366
|
label: patch.label ?? `${event.tool} ${targetId}`,
|
|
2940
4367
|
patch: patch.patch,
|
|
@@ -2949,14 +4376,135 @@ function commitGizmoTransform(state, options, event) {
|
|
|
2949
4376
|
reprojectProjectionForChangedIds(state, options, result.workingDocument, patch.reprojectIds);
|
|
2950
4377
|
else
|
|
2951
4378
|
syncProjectionForChangedIds(state, options, result.workingDocument, [patch.changedId ?? targetId]);
|
|
2952
|
-
state.summary = summarizeDocument(options, result.workingDocument,
|
|
4379
|
+
state.summary = summarizeDocument(options, result.workingDocument, session.getSource());
|
|
2953
4380
|
state.status = patch.label ?? `${event.tool} ${targetId}`;
|
|
2954
4381
|
state.duplicateDrag = null;
|
|
2955
4382
|
return result.documentChanged;
|
|
2956
4383
|
}
|
|
4384
|
+
function commitMarkerGizmoTransform(state, options, document, event) {
|
|
4385
|
+
if (isTransformBatchCommit(event))
|
|
4386
|
+
return null;
|
|
4387
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
4388
|
+
const hierarchy = graph ? options.documentAdapter.getHierarchyItems(document) : [];
|
|
4389
|
+
const markerId = readSpatialMarkerIdFromHierarchySelection(graph, event.nodeId, hierarchy);
|
|
4390
|
+
if (!markerId)
|
|
4391
|
+
return null;
|
|
4392
|
+
if (markerId === event.nodeId)
|
|
4393
|
+
return null;
|
|
4394
|
+
const marker = findSpatialMarkerById(graph, markerId);
|
|
4395
|
+
if (!marker)
|
|
4396
|
+
return false;
|
|
4397
|
+
if (!isEditorTransformTrsSnapshot(event.after)) {
|
|
4398
|
+
state.status = `Ignored ${event.tool} marker ${marker.label}: transform is not editable as TRS`;
|
|
4399
|
+
return false;
|
|
4400
|
+
}
|
|
4401
|
+
if (editorTransformSnapshotsEqual(event.before, event.after)) {
|
|
4402
|
+
state.status = `Unchanged ${event.tool} ${marker.label}`;
|
|
4403
|
+
return false;
|
|
4404
|
+
}
|
|
4405
|
+
const nextPatch = createMarkerTransformPatchFromGizmo(marker, event.after);
|
|
4406
|
+
const patch = options.documentAdapter.createMarkerGraphPatch?.(document, {
|
|
4407
|
+
type: 'marker.update',
|
|
4408
|
+
markerId,
|
|
4409
|
+
patch: nextPatch,
|
|
4410
|
+
});
|
|
4411
|
+
if (!patch) {
|
|
4412
|
+
state.markerOverlay?.setMarkerTransformPreview(event.nodeId, event.before);
|
|
4413
|
+
state.status = `Ignored ${event.tool} marker ${marker.label}`;
|
|
4414
|
+
return false;
|
|
4415
|
+
}
|
|
4416
|
+
const session = state.session;
|
|
4417
|
+
if (!session)
|
|
4418
|
+
return false;
|
|
4419
|
+
const result = session.dispatch({
|
|
4420
|
+
type: 'document.patch',
|
|
4421
|
+
label: patch.label ?? `${event.tool} marker ${marker.label}`,
|
|
4422
|
+
patch: patch.patch,
|
|
4423
|
+
targetId: event.nodeId,
|
|
4424
|
+
});
|
|
4425
|
+
syncProjectionForDispatchResult(state, options, result, patch.changedId ?? markerId);
|
|
4426
|
+
state.gizmo?.refreshSelection();
|
|
4427
|
+
requestEditorSceneFrame(state, 'marker-gizmo-transform');
|
|
4428
|
+
state.summary = summarizeDocument(options, result.workingDocument, session.getSource());
|
|
4429
|
+
state.status = patch.label ?? `${event.tool} marker ${marker.label}`;
|
|
4430
|
+
return result.documentChanged;
|
|
4431
|
+
}
|
|
4432
|
+
function createMarkerTransformPatchFromGizmo(marker, transform) {
|
|
4433
|
+
const semanticFrame = createMarkerSemanticFrameFromTransform(marker, transform);
|
|
4434
|
+
if (marker.geometry.kind === 'box') {
|
|
4435
|
+
const nextGeometry = {
|
|
4436
|
+
...marker.geometry,
|
|
4437
|
+
center: { ...transform.position },
|
|
4438
|
+
size: normalizeMarkerBoxSize(transform.scale),
|
|
4439
|
+
};
|
|
4440
|
+
if (marker.geometry.rotation || !editorVec3Equal(transform.rotation, { x: 0, y: 0, z: 0 })) {
|
|
4441
|
+
nextGeometry.rotation = { ...transform.rotation };
|
|
4442
|
+
}
|
|
4443
|
+
else {
|
|
4444
|
+
delete nextGeometry.rotation;
|
|
4445
|
+
}
|
|
4446
|
+
return {
|
|
4447
|
+
geometry: nextGeometry,
|
|
4448
|
+
semanticFrame,
|
|
4449
|
+
};
|
|
4450
|
+
}
|
|
4451
|
+
if (marker.geometry.kind === 'point') {
|
|
4452
|
+
return {
|
|
4453
|
+
geometry: {
|
|
4454
|
+
...marker.geometry,
|
|
4455
|
+
coordinateSpace: 'world',
|
|
4456
|
+
position: { ...transform.position },
|
|
4457
|
+
},
|
|
4458
|
+
semanticFrame,
|
|
4459
|
+
};
|
|
4460
|
+
}
|
|
4461
|
+
return {
|
|
4462
|
+
semanticFrame,
|
|
4463
|
+
};
|
|
4464
|
+
}
|
|
4465
|
+
function createMarkerSemanticFrameFromTransform(marker, transform) {
|
|
4466
|
+
return {
|
|
4467
|
+
...(marker.semanticFrame ?? {}),
|
|
4468
|
+
origin: { ...transform.position },
|
|
4469
|
+
right: rotateEditorVec3ByEuler({ x: 1, y: 0, z: 0 }, transform.rotation),
|
|
4470
|
+
up: rotateEditorVec3ByEuler({ x: 0, y: 1, z: 0 }, transform.rotation),
|
|
4471
|
+
forward: rotateEditorVec3ByEuler({ x: 0, y: 0, z: 1 }, transform.rotation),
|
|
4472
|
+
label: marker.semanticFrame?.label ?? marker.label,
|
|
4473
|
+
};
|
|
4474
|
+
}
|
|
4475
|
+
function rotateEditorVec3ByEuler(value, rotation) {
|
|
4476
|
+
const cx = Math.cos(rotation.x);
|
|
4477
|
+
const sx = Math.sin(rotation.x);
|
|
4478
|
+
const cy = Math.cos(rotation.y);
|
|
4479
|
+
const sy = Math.sin(rotation.y);
|
|
4480
|
+
const cz = Math.cos(rotation.z);
|
|
4481
|
+
const sz = Math.sin(rotation.z);
|
|
4482
|
+
const afterX = {
|
|
4483
|
+
x: value.x,
|
|
4484
|
+
y: value.y * cx - value.z * sx,
|
|
4485
|
+
z: value.y * sx + value.z * cx,
|
|
4486
|
+
};
|
|
4487
|
+
const afterY = {
|
|
4488
|
+
x: afterX.x * cy + afterX.z * sy,
|
|
4489
|
+
y: afterX.y,
|
|
4490
|
+
z: -afterX.x * sy + afterX.z * cy,
|
|
4491
|
+
};
|
|
4492
|
+
return {
|
|
4493
|
+
x: afterY.x * cz - afterY.y * sz,
|
|
4494
|
+
y: afterY.x * sz + afterY.y * cz,
|
|
4495
|
+
z: afterY.z,
|
|
4496
|
+
};
|
|
4497
|
+
}
|
|
2957
4498
|
function isTransformBatchCommit(event) {
|
|
2958
4499
|
return 'targets' in event;
|
|
2959
4500
|
}
|
|
4501
|
+
function normalizeMarkerBoxSize(size) {
|
|
4502
|
+
return {
|
|
4503
|
+
x: Math.max(0.000001, Math.abs(size.x)),
|
|
4504
|
+
y: Math.max(0.000001, Math.abs(size.y)),
|
|
4505
|
+
z: Math.max(0.000001, Math.abs(size.z)),
|
|
4506
|
+
};
|
|
4507
|
+
}
|
|
2960
4508
|
function restoreBatchTransformPreview(state, targets) {
|
|
2961
4509
|
const transforms = {};
|
|
2962
4510
|
for (const target of targets)
|
|
@@ -2991,6 +4539,14 @@ function cancelActiveOperation(state) {
|
|
|
2991
4539
|
function cancelEditorIntent(state, options) {
|
|
2992
4540
|
if (state.mode !== 'editor')
|
|
2993
4541
|
return false;
|
|
4542
|
+
if (state.markerRelationPick) {
|
|
4543
|
+
state.markerRelationPick = null;
|
|
4544
|
+
state.status = '已取消关系端点拾取。';
|
|
4545
|
+
state.statusTone = 'success';
|
|
4546
|
+
state.statusToneStatus = state.status;
|
|
4547
|
+
state.statusDetails = '';
|
|
4548
|
+
return true;
|
|
4549
|
+
}
|
|
2994
4550
|
if (state.viewportTools.activeUtilityTool === 'measure-distance'
|
|
2995
4551
|
|| state.viewportMeasurement.start
|
|
2996
4552
|
|| state.viewportMeasurement.end) {
|
|
@@ -3031,6 +4587,19 @@ function focusProjectionNode(state, nodeId) {
|
|
|
3031
4587
|
requestEditorSceneFrame(state, 'viewport-focus-selection');
|
|
3032
4588
|
return true;
|
|
3033
4589
|
}
|
|
4590
|
+
function focusPrefabStagePreviewProjection(state, reason) {
|
|
4591
|
+
const previewNodeId = state.prefabStage?.descriptor.previewNodeId;
|
|
4592
|
+
if (!previewNodeId)
|
|
4593
|
+
return;
|
|
4594
|
+
const root = state.projection?.getAttachableRoot(previewNodeId) ?? null;
|
|
4595
|
+
if (!root)
|
|
4596
|
+
return;
|
|
4597
|
+
const focused = focusEditorViewportSelection(state.world?.camera ?? null, root, {
|
|
4598
|
+
babylon: state.babylon ?? undefined,
|
|
4599
|
+
});
|
|
4600
|
+
if (focused)
|
|
4601
|
+
requestEditorSceneFrame(state, reason);
|
|
4602
|
+
}
|
|
3034
4603
|
function formatEditorStatusTime(timestamp) {
|
|
3035
4604
|
const date = new Date(timestamp);
|
|
3036
4605
|
const pad = (value) => String(value).padStart(2, '0');
|
|
@@ -3232,6 +4801,10 @@ function applyIncrementalHistoryProjectionImpact(state, options, result, operati
|
|
|
3232
4801
|
function applyIncrementalProjectionImpactToDocument(state, options, input) {
|
|
3233
4802
|
if (!state.projection)
|
|
3234
4803
|
return false;
|
|
4804
|
+
if (state.prefabStage) {
|
|
4805
|
+
rebuildProjectionFromDocument(state, options, input.document, EMPTY_EDITOR_SELECTION);
|
|
4806
|
+
return true;
|
|
4807
|
+
}
|
|
3235
4808
|
const syncTransformIds = uniqueProjectionIds(input.syncTransformIds);
|
|
3236
4809
|
const reprojectIds = uniqueProjectionIds(input.reprojectIds);
|
|
3237
4810
|
const projectNodeIds = uniqueProjectionIds(input.projectNodeIds);
|
|
@@ -3260,7 +4833,7 @@ function applyIncrementalProjectionImpactToDocument(state, options, input) {
|
|
|
3260
4833
|
state.projection.syncNodeTransform(projectedNode);
|
|
3261
4834
|
}
|
|
3262
4835
|
const sanitized = sanitizeSelection(state, options, input.document, input.selection);
|
|
3263
|
-
syncSelectionToProjection(state, sanitized ?? input.selection);
|
|
4836
|
+
syncSelectionToProjection(state, options, sanitized ?? input.selection);
|
|
3264
4837
|
syncEditorWorldAppearanceFromDocument(state, options, input.document, `world-appearance-${input.reason}`);
|
|
3265
4838
|
syncEditorWorldRenderingFromDocument(state, options, input.document, `world-rendering-${input.reason}`, true);
|
|
3266
4839
|
invalidateEditorScene(state, `projection-${input.reason}`, options);
|
|
@@ -3276,27 +4849,47 @@ function resolveProjectionNodes(options, document, ids) {
|
|
|
3276
4849
|
}
|
|
3277
4850
|
return projectedNodes;
|
|
3278
4851
|
}
|
|
4852
|
+
function resolveCurrentEditorProjectionNodes(state, options, document) {
|
|
4853
|
+
if (!state.prefabStage)
|
|
4854
|
+
return options.documentAdapter.getProjectionNodes(document);
|
|
4855
|
+
const descriptor = resolvePrefabStageDescriptorForDocument(state, options, document);
|
|
4856
|
+
if (!descriptor)
|
|
4857
|
+
return [];
|
|
4858
|
+
return options.documentAdapter.getPrefabStageProjectionNodes?.(document, descriptor) ?? [];
|
|
4859
|
+
}
|
|
3279
4860
|
function rebuildProjectionFromDocument(state, options, document, selection) {
|
|
3280
|
-
state
|
|
3281
|
-
|
|
3282
|
-
|
|
4861
|
+
const nodes = resolveCurrentEditorProjectionNodes(state, options, document);
|
|
4862
|
+
state.projection?.rebuild(nodes);
|
|
4863
|
+
const sanitized = state.prefabStage ? EMPTY_EDITOR_SELECTION : sanitizeSelection(state, options, document, selection);
|
|
4864
|
+
syncSelectionToProjection(state, options, sanitized ?? selection);
|
|
3283
4865
|
syncEditorWorldAppearanceFromDocument(state, options, document, 'world-appearance-rebuild');
|
|
3284
4866
|
syncEditorWorldRenderingFromDocument(state, options, document, 'world-rendering-rebuild', true);
|
|
4867
|
+
if (state.prefabStage && nodes.length === 0) {
|
|
4868
|
+
const descriptor = resolvePrefabStageDescriptorForDocument(state, options, document);
|
|
4869
|
+
state.status = `Prefab preview unavailable: ${descriptor?.label ?? state.prefabStage.descriptor.label}`;
|
|
4870
|
+
state.statusTone = 'warning';
|
|
4871
|
+
state.statusToneStatus = state.status;
|
|
4872
|
+
state.statusDetails = 'The prefab source model asset could not be resolved for preview.';
|
|
4873
|
+
}
|
|
3285
4874
|
invalidateEditorScene(state, 'projection-rebuild');
|
|
3286
4875
|
}
|
|
3287
4876
|
function syncProjectionForDispatchResult(state, options, result, changedId) {
|
|
4877
|
+
if (state.prefabStage && result.documentChanged && result.workingDocument) {
|
|
4878
|
+
rebuildProjectionFromDocument(state, options, result.workingDocument, EMPTY_EDITOR_SELECTION);
|
|
4879
|
+
return;
|
|
4880
|
+
}
|
|
3288
4881
|
if (result.documentChanged && changedId && result.workingDocument) {
|
|
3289
4882
|
const projectedNode = options.documentAdapter.getProjectionNode(result.workingDocument, changedId);
|
|
3290
4883
|
if (projectedNode)
|
|
3291
4884
|
state.projection?.syncNodeTransform(projectedNode);
|
|
3292
4885
|
}
|
|
3293
4886
|
if (result.selectionChanged) {
|
|
3294
|
-
syncSelectionToProjection(state, result.selection ?? { selectedIds: [], activeId: null });
|
|
4887
|
+
syncSelectionToProjection(state, options, result.selection ?? { selectedIds: [], activeId: null });
|
|
3295
4888
|
}
|
|
3296
4889
|
else if (result.documentChanged) {
|
|
3297
4890
|
const selection = state.session?.getState({ cloneDocuments: false }).selection ?? { selectedIds: [], activeId: null };
|
|
3298
4891
|
const sanitized = result.workingDocument ? sanitizeSelection(state, options, result.workingDocument, selection) : null;
|
|
3299
|
-
syncSelectionToProjection(state, sanitized ?? selection);
|
|
4892
|
+
syncSelectionToProjection(state, options, sanitized ?? selection);
|
|
3300
4893
|
}
|
|
3301
4894
|
if (result.documentChanged && result.workingDocument) {
|
|
3302
4895
|
syncEditorWorldAppearanceFromDocument(state, options, result.workingDocument, 'world-appearance-dispatch-result');
|
|
@@ -3307,18 +4900,26 @@ function syncProjectionForDispatchResult(state, options, result, changedId) {
|
|
|
3307
4900
|
}
|
|
3308
4901
|
}
|
|
3309
4902
|
function syncProjectionForChangedIds(state, options, document, changedIds) {
|
|
4903
|
+
if (state.prefabStage) {
|
|
4904
|
+
rebuildProjectionFromDocument(state, options, document, EMPTY_EDITOR_SELECTION);
|
|
4905
|
+
return;
|
|
4906
|
+
}
|
|
3310
4907
|
for (const changedId of changedIds) {
|
|
3311
4908
|
const projectedNode = options.documentAdapter.getProjectionNode(document, changedId);
|
|
3312
4909
|
if (projectedNode)
|
|
3313
4910
|
state.projection?.syncNodeTransform(projectedNode);
|
|
3314
4911
|
}
|
|
3315
4912
|
const selection = state.session?.getState({ cloneDocuments: false }).selection ?? { selectedIds: [], activeId: null };
|
|
3316
|
-
syncSelectionToProjection(state, selection);
|
|
4913
|
+
syncSelectionToProjection(state, options, selection);
|
|
3317
4914
|
syncEditorWorldAppearanceFromDocument(state, options, document, 'world-appearance-sync-changed-ids');
|
|
3318
4915
|
syncEditorWorldRenderingFromDocument(state, options, document, 'world-rendering-sync-changed-ids', true);
|
|
3319
4916
|
invalidateEditorScene(state, 'projection-sync-changed-ids');
|
|
3320
4917
|
}
|
|
3321
4918
|
function syncProjectionMetadataForChangedIds(state, options, document, changedIds, syncOptions = {}) {
|
|
4919
|
+
if (state.prefabStage) {
|
|
4920
|
+
rebuildProjectionFromDocument(state, options, document, EMPTY_EDITOR_SELECTION);
|
|
4921
|
+
return;
|
|
4922
|
+
}
|
|
3322
4923
|
for (const changedId of changedIds) {
|
|
3323
4924
|
const projectedNode = options.documentAdapter.getProjectionNode(document, changedId);
|
|
3324
4925
|
if (projectedNode) {
|
|
@@ -3326,7 +4927,7 @@ function syncProjectionMetadataForChangedIds(state, options, document, changedId
|
|
|
3326
4927
|
}
|
|
3327
4928
|
}
|
|
3328
4929
|
const selection = state.session?.getState({ cloneDocuments: false }).selection ?? { selectedIds: [], activeId: null };
|
|
3329
|
-
syncSelectionToProjection(state, selection);
|
|
4930
|
+
syncSelectionToProjection(state, options, selection);
|
|
3330
4931
|
syncEditorWorldAppearanceFromDocument(state, options, document, 'world-appearance-sync-metadata-ids');
|
|
3331
4932
|
if (syncOptions.invalidateStaticShadowArtifact) {
|
|
3332
4933
|
state.shadowPreview?.invalidateStaticProjectedArtifact?.();
|
|
@@ -3343,12 +4944,16 @@ function syncProjectionMetadataForChangedIds(state, options, document, changedId
|
|
|
3343
4944
|
invalidateEditorScene(state, 'projection-sync-metadata-ids');
|
|
3344
4945
|
}
|
|
3345
4946
|
function reprojectProjectionForChangedIds(state, options, document, changedIds) {
|
|
4947
|
+
if (state.prefabStage) {
|
|
4948
|
+
rebuildProjectionFromDocument(state, options, document, EMPTY_EDITOR_SELECTION);
|
|
4949
|
+
return;
|
|
4950
|
+
}
|
|
3346
4951
|
for (const changedId of changedIds) {
|
|
3347
4952
|
const projectedNode = options.documentAdapter.getProjectionNode(document, changedId);
|
|
3348
4953
|
if (projectedNode)
|
|
3349
4954
|
state.projection?.projectNode(projectedNode);
|
|
3350
4955
|
}
|
|
3351
|
-
syncCurrentSelectionToSceneArtifacts(state);
|
|
4956
|
+
syncCurrentSelectionToSceneArtifacts(state, options);
|
|
3352
4957
|
syncEditorWorldAppearanceFromDocument(state, options, document, 'world-appearance-reproject-changed-ids');
|
|
3353
4958
|
syncEditorWorldRenderingFromDocument(state, options, document, 'world-rendering-reproject-changed-ids', true);
|
|
3354
4959
|
invalidateEditorScene(state, 'projection-reproject-changed-ids', options);
|
|
@@ -3376,15 +4981,21 @@ function setSceneCameraPreviewEnabled(state, options, enabled) {
|
|
|
3376
4981
|
}
|
|
3377
4982
|
function setGridVisible(state, visible) {
|
|
3378
4983
|
const nextVisible = visible === true;
|
|
3379
|
-
if (state
|
|
4984
|
+
if (!setGridVisibleForEditorContext(state, nextVisible, 'grid-visible-change'))
|
|
3380
4985
|
return false;
|
|
3381
|
-
state.gridVisible = nextVisible;
|
|
3382
|
-
state.grid?.setVisible(nextVisible);
|
|
3383
4986
|
state.status = nextVisible ? 'Grid visible' : 'Grid hidden';
|
|
3384
4987
|
state.statusTone = 'default';
|
|
3385
4988
|
state.statusToneStatus = state.status;
|
|
3386
4989
|
state.statusDetails = '';
|
|
3387
|
-
|
|
4990
|
+
return true;
|
|
4991
|
+
}
|
|
4992
|
+
function setGridVisibleForEditorContext(state, visible, reason) {
|
|
4993
|
+
const nextVisible = visible === true;
|
|
4994
|
+
if (state.gridVisible === nextVisible)
|
|
4995
|
+
return false;
|
|
4996
|
+
state.gridVisible = nextVisible;
|
|
4997
|
+
state.grid?.setVisible(nextVisible);
|
|
4998
|
+
requestEditorSceneFrame(state, reason);
|
|
3388
4999
|
return true;
|
|
3389
5000
|
}
|
|
3390
5001
|
function syncSceneCameraPreview(state, options) {
|
|
@@ -3526,22 +5137,36 @@ function createUiState(state, options) {
|
|
|
3526
5137
|
const selectedIds = sessionState?.selection.selectedIds ?? [];
|
|
3527
5138
|
const activeId = sessionState?.selection.activeId ?? null;
|
|
3528
5139
|
const largeSelection = isLargeSelection(selectedIds);
|
|
5140
|
+
const markerGraph = document ? options.documentAdapter.getMarkerGraph?.(document) ?? null : null;
|
|
5141
|
+
const markerTypes = document ? options.documentAdapter.getMarkerTypeCatalog?.(document) ?? [] : [];
|
|
5142
|
+
const relationTypes = document ? options.documentAdapter.getRelationTypeCatalog?.(document) ?? [] : [];
|
|
5143
|
+
const documentHierarchy = document ? options.documentAdapter.getHierarchyItems(document) : [];
|
|
5144
|
+
const activeMarkerId = activeId ? readSpatialMarkerIdFromHierarchySelection(markerGraph, activeId, documentHierarchy) : null;
|
|
5145
|
+
const markerRelationVisibility = createMarkerRelationVisibilityOptions(state);
|
|
5146
|
+
const markerInspectorObject = document && activeId && selectedIds.length === 1
|
|
5147
|
+
? createMarkerInspectorObjectBase(options, document, activeId, markerGraph, documentHierarchy, markerTypes, state.activeMarkerRelationId, state.markerRelationPick, markerRelationVisibility)
|
|
5148
|
+
: null;
|
|
3529
5149
|
const serializedObject = document && activeId && selectedIds.length === 1
|
|
3530
|
-
? options.documentAdapter.getSerializedObject(document, activeId)
|
|
5150
|
+
? markerInspectorObject ? null : options.documentAdapter.getSerializedObject(document, activeId)
|
|
3531
5151
|
: null;
|
|
3532
5152
|
const serializedMultiObject = document && selectedIds.length > 1 && !largeSelection
|
|
3533
5153
|
? options.documentAdapter.getSerializedMultiObject?.(document, selectedIds, activeId) ?? null
|
|
3534
5154
|
: null;
|
|
3535
5155
|
const inspectorObjectBase = document && activeId && selectedIds.length === 1
|
|
3536
|
-
?
|
|
5156
|
+
? markerInspectorObject
|
|
5157
|
+
?? options.documentAdapter.getInspectorObject?.(document, activeId)
|
|
5158
|
+
?? (serializedObject ? serializedObjectToInspectorObject(serializedObject, document) : null)
|
|
3537
5159
|
: null;
|
|
3538
5160
|
const inspectorMultiObjectBase = document && selectedIds.length > 1
|
|
3539
5161
|
? createInspectorMultiObjectBase(state, options, document, selectedIds, activeId, serializedMultiObject, {
|
|
3540
5162
|
largeSelection,
|
|
3541
5163
|
})
|
|
3542
5164
|
: null;
|
|
3543
|
-
const
|
|
3544
|
-
?
|
|
5165
|
+
const markerRelationInspectorObjectBase = document && inspectorObjectBase
|
|
5166
|
+
? withMarkerGraphRelationInspectorSection(options, document, inspectorObjectBase, markerGraph, documentHierarchy, markerTypes, relationTypes, state.activeMarkerRelationId, state.markerRelationPick, markerRelationVisibility)
|
|
5167
|
+
: inspectorObjectBase;
|
|
5168
|
+
const inspectorObject = document && markerRelationInspectorObjectBase
|
|
5169
|
+
? withRuntimeInspectorSections(state, options, document, markerRelationInspectorObjectBase)
|
|
3545
5170
|
: inspectorObjectBase;
|
|
3546
5171
|
const inspectorMultiObject = document && inspectorMultiObjectBase
|
|
3547
5172
|
? withRuntimeInspectorSections(state, options, document, inspectorMultiObjectBase)
|
|
@@ -3551,8 +5176,10 @@ function createUiState(state, options) {
|
|
|
3551
5176
|
const documentAssetItems = document
|
|
3552
5177
|
? options.documentAdapter.getBrowserAssetItems?.(document) ?? []
|
|
3553
5178
|
: [];
|
|
3554
|
-
const hierarchy = document
|
|
3555
|
-
|
|
5179
|
+
const hierarchy = document
|
|
5180
|
+
? createMarkerGraphHierarchyItems(documentHierarchy, markerGraph, createMarkerGraphHierarchyVisibility(state))
|
|
5181
|
+
: [];
|
|
5182
|
+
state.performanceObjectCount = document ? countLocalEditorAuthoredObjects(documentHierarchy) : null;
|
|
3556
5183
|
state.performanceStats = createLocalEditorPerformanceStats({
|
|
3557
5184
|
frameStats: state.sceneFrameStats,
|
|
3558
5185
|
runtimeStats: state.performanceMonitor?.readSnapshot() ?? null,
|
|
@@ -3591,6 +5218,9 @@ function createUiState(state, options) {
|
|
|
3591
5218
|
renderingAction: state.renderingAction,
|
|
3592
5219
|
}) ?? null
|
|
3593
5220
|
: null,
|
|
5221
|
+
markerPanel: document
|
|
5222
|
+
? createMarkerPanelState(markerGraph, markerTypes, relationTypes, documentHierarchy, activeMarkerId, state.activeMarkerRelationId, state.markerRelationPick, markerRelationVisibility)
|
|
5223
|
+
: null,
|
|
3594
5224
|
boxSelection: state.boxSelection,
|
|
3595
5225
|
coordinateAxes: options.world?.coordinateAxes === false
|
|
3596
5226
|
? null
|
|
@@ -3620,6 +5250,7 @@ function createUiState(state, options) {
|
|
|
3620
5250
|
enabled: state.sceneCameraPreviewEnabled,
|
|
3621
5251
|
available: hasSceneCameraPreviewRig(state, options),
|
|
3622
5252
|
},
|
|
5253
|
+
prefabStage: createBrowserPrefabStageState(state, options, document),
|
|
3623
5254
|
grid: {
|
|
3624
5255
|
visible: state.gridVisible,
|
|
3625
5256
|
available: !!state.grid,
|
|
@@ -3635,6 +5266,123 @@ function createUiState(state, options) {
|
|
|
3635
5266
|
: null,
|
|
3636
5267
|
};
|
|
3637
5268
|
}
|
|
5269
|
+
function createBrowserPrefabStageState(state, options, document) {
|
|
5270
|
+
const stage = state.prefabStage;
|
|
5271
|
+
if (!stage || !document)
|
|
5272
|
+
return null;
|
|
5273
|
+
const descriptor = resolvePrefabStageDescriptorForDocument(state, options, document) ?? stage.descriptor;
|
|
5274
|
+
const context = createPrefabStageContext(state, descriptor);
|
|
5275
|
+
const structureItems = options.documentAdapter.getPrefabStageStructure?.(document, descriptor, context)
|
|
5276
|
+
?? createFallbackPrefabStageStructure(descriptor);
|
|
5277
|
+
const inspectorObject = options.documentAdapter.getPrefabStageInspectorObject?.(document, descriptor, stage.selectedItemId, context) ?? null;
|
|
5278
|
+
return {
|
|
5279
|
+
assetId: descriptor.assetId,
|
|
5280
|
+
browserAssetId: descriptor.browserAssetId,
|
|
5281
|
+
label: descriptor.label,
|
|
5282
|
+
sourceAssetId: descriptor.sourceAssetId,
|
|
5283
|
+
sourceAssetGuid: descriptor.sourceAssetGuid,
|
|
5284
|
+
readonly: descriptor.readonly,
|
|
5285
|
+
selectedItemId: stage.selectedItemId,
|
|
5286
|
+
structureItems,
|
|
5287
|
+
inspectorObject,
|
|
5288
|
+
importStructureReady: context.importStructureReady,
|
|
5289
|
+
importStats: createPrefabStageImportStats(context),
|
|
5290
|
+
previewNodeId: descriptor.previewNodeId,
|
|
5291
|
+
comparisonPreviews: createPrefabStageComparisonPreviews(state, descriptor),
|
|
5292
|
+
};
|
|
5293
|
+
}
|
|
5294
|
+
function createPrefabStageContext(state, descriptor) {
|
|
5295
|
+
const previewNodeId = descriptor.previewNodeId;
|
|
5296
|
+
if (!previewNodeId) {
|
|
5297
|
+
return {
|
|
5298
|
+
importStructure: null,
|
|
5299
|
+
importStructureReady: false,
|
|
5300
|
+
};
|
|
5301
|
+
}
|
|
5302
|
+
const projectedNode = state.projection?.getProjectedNode(previewNodeId) ?? null;
|
|
5303
|
+
return {
|
|
5304
|
+
previewNodeId,
|
|
5305
|
+
importStructure: state.projection?.readNodeImportStructure(previewNodeId) ?? null,
|
|
5306
|
+
importStructureReady: !!projectedNode && !projectedNode.loadPromise,
|
|
5307
|
+
};
|
|
5308
|
+
}
|
|
5309
|
+
function createPrefabStageImportStats(context) {
|
|
5310
|
+
const snapshot = context.importStructure;
|
|
5311
|
+
if (!context.importStructureReady) {
|
|
5312
|
+
return createEmptyPrefabStageImportStats('loading', snapshot);
|
|
5313
|
+
}
|
|
5314
|
+
if (!snapshot) {
|
|
5315
|
+
return createEmptyPrefabStageImportStats('missing', null);
|
|
5316
|
+
}
|
|
5317
|
+
const nodes = snapshot.nodes ?? [];
|
|
5318
|
+
return {
|
|
5319
|
+
status: 'ready',
|
|
5320
|
+
projectionNodeId: snapshot.projectionNodeId,
|
|
5321
|
+
...(snapshot.assetId ? { assetId: snapshot.assetId } : {}),
|
|
5322
|
+
...(snapshot.sourceId ? { sourceId: snapshot.sourceId } : {}),
|
|
5323
|
+
nodeCount: nodes.length,
|
|
5324
|
+
transformCount: nodes.filter(node => node.kind === 'root' || node.kind === 'transform').length,
|
|
5325
|
+
meshCount: nodes.filter(node => node.kind === 'mesh').length,
|
|
5326
|
+
materialCount: snapshot.materials?.length ?? 0,
|
|
5327
|
+
textureCount: snapshot.textures?.length ?? 0,
|
|
5328
|
+
animationCount: snapshot.animations?.length ?? 0,
|
|
5329
|
+
};
|
|
5330
|
+
}
|
|
5331
|
+
function createEmptyPrefabStageImportStats(status, snapshot) {
|
|
5332
|
+
return {
|
|
5333
|
+
status,
|
|
5334
|
+
...(snapshot?.projectionNodeId ? { projectionNodeId: snapshot.projectionNodeId } : {}),
|
|
5335
|
+
...(snapshot?.assetId ? { assetId: snapshot.assetId } : {}),
|
|
5336
|
+
...(snapshot?.sourceId ? { sourceId: snapshot.sourceId } : {}),
|
|
5337
|
+
nodeCount: 0,
|
|
5338
|
+
transformCount: 0,
|
|
5339
|
+
meshCount: 0,
|
|
5340
|
+
materialCount: 0,
|
|
5341
|
+
textureCount: 0,
|
|
5342
|
+
animationCount: 0,
|
|
5343
|
+
};
|
|
5344
|
+
}
|
|
5345
|
+
function createPrefabStageComparisonPreviews(state, descriptor) {
|
|
5346
|
+
return PREFAB_STAGE_COMPARISON_PREVIEW_DEFINITIONS.map((definition) => {
|
|
5347
|
+
const projectionNodeId = createPrefabStageComparisonProjectionNodeId(descriptor, definition.nodeIdSuffix);
|
|
5348
|
+
const projectedNode = state.projection?.getProjectedNode(projectionNodeId) ?? null;
|
|
5349
|
+
const status = !descriptor.sourceAssetId || !projectedNode
|
|
5350
|
+
? 'unavailable'
|
|
5351
|
+
: projectedNode.loadPromise
|
|
5352
|
+
? 'loading'
|
|
5353
|
+
: 'ready';
|
|
5354
|
+
return {
|
|
5355
|
+
id: definition.id,
|
|
5356
|
+
label: definition.label,
|
|
5357
|
+
mode: definition.mode,
|
|
5358
|
+
projectionNodeId,
|
|
5359
|
+
status,
|
|
5360
|
+
};
|
|
5361
|
+
});
|
|
5362
|
+
}
|
|
5363
|
+
function createPrefabStageComparisonProjectionNodeId(descriptor, suffix) {
|
|
5364
|
+
const previewNodeId = descriptor.previewNodeId ?? `prefab-stage:${descriptor.assetId}`;
|
|
5365
|
+
return `${previewNodeId}:${suffix}`;
|
|
5366
|
+
}
|
|
5367
|
+
function createFallbackPrefabStageStructure(descriptor) {
|
|
5368
|
+
return [{
|
|
5369
|
+
id: 'prefab-root',
|
|
5370
|
+
label: descriptor.label,
|
|
5371
|
+
kind: 'root',
|
|
5372
|
+
icon: 'prefab',
|
|
5373
|
+
meta: descriptor.assetId,
|
|
5374
|
+
readonly: descriptor.readonly,
|
|
5375
|
+
children: [{
|
|
5376
|
+
id: 'prefab-source',
|
|
5377
|
+
label: 'Source Model',
|
|
5378
|
+
kind: 'source',
|
|
5379
|
+
icon: 'asset',
|
|
5380
|
+
sourceAssetId: descriptor.sourceAssetId,
|
|
5381
|
+
meta: descriptor.sourceAssetId ?? 'Missing source asset',
|
|
5382
|
+
readonly: true,
|
|
5383
|
+
}],
|
|
5384
|
+
}];
|
|
5385
|
+
}
|
|
3638
5386
|
function createUiPreviewState(state, options) {
|
|
3639
5387
|
const sessionState = state.session?.getState({ cloneDocuments: false });
|
|
3640
5388
|
const selection = sessionState?.selection;
|
|
@@ -3891,12 +5639,108 @@ function createAggregatedInspectorMultiObjectBase(_state, options, document, sel
|
|
|
3891
5639
|
return aggregatedInspector;
|
|
3892
5640
|
}
|
|
3893
5641
|
function createSingleInspectorObjectBase(options, document, targetId) {
|
|
5642
|
+
const markerInspector = createMarkerInspectorObjectBase(options, document, targetId);
|
|
5643
|
+
if (markerInspector)
|
|
5644
|
+
return markerInspector;
|
|
3894
5645
|
const inspectorObject = options.documentAdapter.getInspectorObject?.(document, targetId) ?? null;
|
|
3895
5646
|
if (inspectorObject)
|
|
3896
|
-
return inspectorObject;
|
|
5647
|
+
return withMarkerGraphRelationInspectorSection(options, document, inspectorObject);
|
|
3897
5648
|
const serializedObject = options.documentAdapter.getSerializedObject(document, targetId);
|
|
3898
5649
|
return serializedObject ? serializedObjectToInspectorObject(serializedObject, document) : null;
|
|
3899
5650
|
}
|
|
5651
|
+
function withMarkerGraphRelationInspectorSection(options, document, inspectorObject, graph = options.documentAdapter.getMarkerGraph?.(document) ?? null, hierarchy = options.documentAdapter.getHierarchyItems(document), markerTypes = options.documentAdapter.getMarkerTypeCatalog?.(document) ?? [], relationTypes = options.documentAdapter.getRelationTypeCatalog?.(document) ?? [], activeRelationId = null, relationPick = null, relationVisibility = {}) {
|
|
5652
|
+
if (inspectorObject.targetIds.length !== 1)
|
|
5653
|
+
return inspectorObject;
|
|
5654
|
+
if (inspectorObject.sections.some(section => section.id === 'markerRelations'))
|
|
5655
|
+
return inspectorObject;
|
|
5656
|
+
if (inspectorObject.sections.some(section => section.properties.some(property => property.path === 'marker.relations.editor'))) {
|
|
5657
|
+
return inspectorObject;
|
|
5658
|
+
}
|
|
5659
|
+
const targetId = inspectorObject.activeId ?? inspectorObject.targetIds[0] ?? null;
|
|
5660
|
+
const markerId = readSpatialMarkerIdFromHierarchySelection(graph, targetId, hierarchy);
|
|
5661
|
+
if (!targetId || !markerId)
|
|
5662
|
+
return inspectorObject;
|
|
5663
|
+
const markerInspector = createMarkerGraphInspectorObject({
|
|
5664
|
+
document,
|
|
5665
|
+
graph,
|
|
5666
|
+
markerId: targetId,
|
|
5667
|
+
hierarchy,
|
|
5668
|
+
markerTypes,
|
|
5669
|
+
relationTypes,
|
|
5670
|
+
activeRelationId,
|
|
5671
|
+
relationPick,
|
|
5672
|
+
relationVisibility,
|
|
5673
|
+
});
|
|
5674
|
+
const relationSection = markerInspector?.sections.find(section => section.id === 'markerRelations') ?? null;
|
|
5675
|
+
if (!relationSection)
|
|
5676
|
+
return inspectorObject;
|
|
5677
|
+
const capabilities = new Set(inspectorObject.selection.capabilities ?? []);
|
|
5678
|
+
capabilities.add('markerGraph');
|
|
5679
|
+
const markerSection = inspectorObject.sections.find(section => section.id === 'marker') ?? null;
|
|
5680
|
+
const sections = markerSection
|
|
5681
|
+
? inspectorObject.sections.map(section => section.id === 'marker'
|
|
5682
|
+
? {
|
|
5683
|
+
...section,
|
|
5684
|
+
properties: [
|
|
5685
|
+
...section.properties,
|
|
5686
|
+
...createIntegratedMarkerRelationProperties(section.properties, relationSection.properties),
|
|
5687
|
+
],
|
|
5688
|
+
}
|
|
5689
|
+
: section)
|
|
5690
|
+
: mergeInspectorSections(inspectorObject.sections, [relationSection], { propertyConflict: 'ignore' });
|
|
5691
|
+
return {
|
|
5692
|
+
...inspectorObject,
|
|
5693
|
+
selection: {
|
|
5694
|
+
...inspectorObject.selection,
|
|
5695
|
+
targetKind: inspectorObject.selection.targetKind ?? 'marker',
|
|
5696
|
+
capabilities: [...capabilities],
|
|
5697
|
+
},
|
|
5698
|
+
sections,
|
|
5699
|
+
};
|
|
5700
|
+
}
|
|
5701
|
+
function createIntegratedMarkerRelationProperties(markerProperties, relationProperties) {
|
|
5702
|
+
const existingPaths = new Set(markerProperties.map(property => property.path));
|
|
5703
|
+
const baseOrder = markerProperties.reduce((maxOrder, property, index) => (Math.max(maxOrder, property.order ?? index)), -1);
|
|
5704
|
+
return relationProperties
|
|
5705
|
+
.filter(property => !existingPaths.has(property.path))
|
|
5706
|
+
.map((property, index) => ({
|
|
5707
|
+
...property,
|
|
5708
|
+
label: formatIntegratedMarkerRelationPropertyLabel(property),
|
|
5709
|
+
order: baseOrder + index + 1,
|
|
5710
|
+
}));
|
|
5711
|
+
}
|
|
5712
|
+
function formatIntegratedMarkerRelationPropertyLabel(property) {
|
|
5713
|
+
switch (property.path) {
|
|
5714
|
+
case 'marker.relations.count': return '关系数量';
|
|
5715
|
+
case 'marker.relations.status': return '关系状态';
|
|
5716
|
+
case 'marker.relations.editor': return '关系';
|
|
5717
|
+
default: return property.label;
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
function createMarkerInspectorObjectBase(options, document, targetId, graph = options.documentAdapter.getMarkerGraph?.(document) ?? null, hierarchy = options.documentAdapter.getHierarchyItems(document), markerTypes = options.documentAdapter.getMarkerTypeCatalog?.(document) ?? [], activeRelationId = null, relationPick = null, relationVisibility = {}) {
|
|
5721
|
+
const virtualMarkerId = readMarkerGraphHierarchyMarkerIdForGraph(graph, targetId, hierarchy);
|
|
5722
|
+
const isVirtualRoot = isMarkerGraphHierarchyRootRowId(graph, targetId, hierarchy);
|
|
5723
|
+
if (!virtualMarkerId && !isVirtualRoot)
|
|
5724
|
+
return null;
|
|
5725
|
+
return createMarkerGraphInspectorObject({
|
|
5726
|
+
document,
|
|
5727
|
+
graph,
|
|
5728
|
+
markerId: targetId,
|
|
5729
|
+
hierarchy,
|
|
5730
|
+
markerTypes,
|
|
5731
|
+
relationTypes: options.documentAdapter.getRelationTypeCatalog?.(document) ?? [],
|
|
5732
|
+
activeRelationId,
|
|
5733
|
+
relationPick,
|
|
5734
|
+
relationVisibility,
|
|
5735
|
+
}) ?? createMarkerGraphRootInspectorObject({
|
|
5736
|
+
document,
|
|
5737
|
+
graph,
|
|
5738
|
+
rootId: targetId,
|
|
5739
|
+
hierarchy,
|
|
5740
|
+
markerTypes,
|
|
5741
|
+
relationTypes: options.documentAdapter.getRelationTypeCatalog?.(document) ?? [],
|
|
5742
|
+
});
|
|
5743
|
+
}
|
|
3900
5744
|
function applyMultiInspectorBatchCapabilityGate(options, document, inspectorObject) {
|
|
3901
5745
|
return options.documentAdapter.createSerializedMultiPropertyPatch
|
|
3902
5746
|
? applyAggregatedBatchInspectorCapabilityGate(options, document, inspectorObject)
|
|
@@ -4225,6 +6069,12 @@ function isDocumentNodeLocked(state, options, id) {
|
|
|
4225
6069
|
return options.documentAdapter.isLocked?.(document, id) ?? false;
|
|
4226
6070
|
}
|
|
4227
6071
|
function isNodeSelectableInDocument(options, document, id) {
|
|
6072
|
+
const graph = options.documentAdapter.getMarkerGraph?.(document) ?? null;
|
|
6073
|
+
const hierarchy = graph ? options.documentAdapter.getHierarchyItems(document) : [];
|
|
6074
|
+
if (isMarkerGraphHierarchyRootRowId(graph, id, hierarchy))
|
|
6075
|
+
return true;
|
|
6076
|
+
if (isMarkerGraphHierarchyId(graph, id, hierarchy))
|
|
6077
|
+
return true;
|
|
4228
6078
|
if (options.documentAdapter.isLocked?.(document, id))
|
|
4229
6079
|
return false;
|
|
4230
6080
|
return options.documentAdapter.isSelectable?.(document, id) ?? true;
|
|
@@ -4246,6 +6096,36 @@ function findAssetByResolvedId(state, options, assetId) {
|
|
|
4246
6096
|
}
|
|
4247
6097
|
return selected?.asset ?? null;
|
|
4248
6098
|
}
|
|
6099
|
+
function findBrowserAssetItemByResolvedId(state, options, document, assetId) {
|
|
6100
|
+
let selected = null;
|
|
6101
|
+
for (const asset of state.assets) {
|
|
6102
|
+
const item = toBrowserAssetItem(options, asset);
|
|
6103
|
+
if (item.id !== assetId && item.assetId !== assetId && resolveAssetId(options, asset) !== assetId)
|
|
6104
|
+
continue;
|
|
6105
|
+
if (!selected || shouldReplaceBrowserAssetItem(selected, item))
|
|
6106
|
+
selected = item;
|
|
6107
|
+
}
|
|
6108
|
+
const documentItem = document ? findDocumentBrowserAssetItemByResolvedId(options, document, assetId) : null;
|
|
6109
|
+
if (documentItem && (!selected || shouldReplaceBrowserAssetItem(selected, documentItem)))
|
|
6110
|
+
selected = documentItem;
|
|
6111
|
+
return selected;
|
|
6112
|
+
}
|
|
6113
|
+
function findAssetOrDocumentBrowserItemByResolvedId(state, options, document, assetId) {
|
|
6114
|
+
return findAssetByResolvedId(state, options, assetId)
|
|
6115
|
+
?? (document ? findDocumentBrowserAssetItemByResolvedId(options, document, assetId) : null);
|
|
6116
|
+
}
|
|
6117
|
+
function findDocumentBrowserAssetItemByResolvedId(options, document, assetId) {
|
|
6118
|
+
let selected = null;
|
|
6119
|
+
for (const item of options.documentAdapter.getBrowserAssetItems?.(document) ?? []) {
|
|
6120
|
+
if (item.id !== assetId && item.assetId !== assetId)
|
|
6121
|
+
continue;
|
|
6122
|
+
if (item.placeable === false || item.disabled === true)
|
|
6123
|
+
continue;
|
|
6124
|
+
if (!selected || shouldReplaceBrowserAssetItem(selected, item))
|
|
6125
|
+
selected = item;
|
|
6126
|
+
}
|
|
6127
|
+
return selected;
|
|
6128
|
+
}
|
|
4249
6129
|
function resolveBrowserAssetItemId(state, options, assetId) {
|
|
4250
6130
|
let selected = null;
|
|
4251
6131
|
for (const asset of state.assets) {
|
|
@@ -4271,6 +6151,7 @@ function toBrowserAssetItem(options, asset) {
|
|
|
4271
6151
|
placeable: asset.placeable,
|
|
4272
6152
|
preview: asset.preview,
|
|
4273
6153
|
material: asset.material,
|
|
6154
|
+
prefab: asset.prefab,
|
|
4274
6155
|
meta: asset.meta,
|
|
4275
6156
|
disabled: asset.disabled ?? asset.placeable === false,
|
|
4276
6157
|
};
|