From 212d4016e06fdfcc54fcd9f1711196ca836ac27e Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Wed, 29 Apr 2026 14:13:47 +0800 Subject: [PATCH] Refactor scene viewport state into local sessions --- editor/AGENTS.md | 14 +- editor/CMakeLists.txt | 1 + editor/app/Composition/EditorContext.cpp | 3 +- .../Scene/SceneViewportController.cpp | 59 ++-- .../Features/Scene/SceneViewportController.h | 3 + .../Features/Scene/SceneViewportFeature.cpp | 4 +- .../app/Features/Scene/SceneViewportFeature.h | 2 + .../Scene/SceneViewportSceneOverlay.cpp | 11 +- .../Scene/SceneViewportSceneOverlay.h | 4 +- .../Features/Scene/SceneViewportSession.cpp | 116 ++++++++ .../app/Features/Scene/SceneViewportSession.h | 46 +++ .../Scene/SceneViewportTransformGizmo.cpp | 21 +- .../Scene/SceneViewportTransformGizmo.h | 6 +- .../app/Services/Scene/EditorSceneRuntime.cpp | 266 +----------------- .../app/Services/Scene/EditorSceneRuntime.h | 35 +-- .../unit/test_scene_viewport_runtime.cpp | 142 +++++++--- 16 files changed, 353 insertions(+), 380 deletions(-) create mode 100644 editor/app/Features/Scene/SceneViewportSession.cpp create mode 100644 editor/app/Features/Scene/SceneViewportSession.h diff --git a/editor/AGENTS.md b/editor/AGENTS.md index 9d2fe536..1bbdc0af 100644 --- a/editor/AGENTS.md +++ b/editor/AGENTS.md @@ -62,9 +62,12 @@ Rules: - `EditorHostCommandBridge` delegates `file.*`, `run.*`, and `scripts.*` to a bound runtime owner. If no owner is bound, it must continue exposing honest disabled messages. -- `EditorSceneRuntime` owns raw scene editing behavior. `EditorRuntimeCoordinator` - owns scene document state: current path/name, dirty flag, new/open/save - routing, and runtime-mode transitions. +- `EditorSceneRuntime` owns raw scene editing behavior and document-adjacent + scene mutations. Scene viewport private state now lives in + `SceneViewportSession`, owned by the scene viewport feature/panel instance, + not by `EditorContext`, `EditorFrameServices`, or the shared scene runtime. + `EditorRuntimeCoordinator` owns scene document state: current path/name, + dirty flag, new/open/save routing, and runtime-mode transitions. - `ProjectPanel` may identify an openable scene asset, but scene document loading must go through `EditorFrameServices::RequestOpenSceneAsset` and the coordinator. Do not reintroduce `ProjectRuntime` pending-open queues. @@ -86,6 +89,9 @@ Rules: - keep panel dependencies explicit. If a panel needs more data or actions, add a narrow panel-local context or binding instead of routing everything through a generic shared services struct +- keep viewport-local state local. Camera/tool mode/pivot/space state for Scene + belongs in `SceneViewportSession` on the feature/controller side; do not push + it back into shared runtime/context/frame-service layers for convenience - do not reintroduce `void*` plus callback-function panel hooks for scene-open or utility-window requests; use typed callbacks or explicit requester interfaces instead @@ -106,6 +112,8 @@ Rules: - no new shared mutable panel-services bag or service-locator style panel dependency bundle - no `EditorFrameServices` tunneling into workspace-panel update/prepare paths +- no new path where Scene viewport render requests or camera/tool state are + recomputed from `EditorContext` instead of the owning `SceneViewportSession` - no scene-open side channel in `EditorProjectRuntime`; project UI must call the panel service request hook - no play-mode path that mutates the editable scene in place or skips diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 677fca60..ab4cf973 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -263,6 +263,7 @@ if(XCENGINE_BUILD_XCUI_EDITOR_CORE) app/Features/Scene/SceneViewportSceneOverlay.cpp app/Features/Scene/SceneEditCommandRoute.cpp app/Features/Scene/SceneViewportFeature.cpp + app/Features/Scene/SceneViewportSession.cpp app/Features/Scene/SceneViewportToolOverlay.cpp app/Features/Scene/SceneViewportController.cpp ) diff --git a/editor/app/Composition/EditorContext.cpp b/editor/app/Composition/EditorContext.cpp index db32edf4..196db030 100644 --- a/editor/app/Composition/EditorContext.cpp +++ b/editor/app/Composition/EditorContext.cpp @@ -326,8 +326,7 @@ std::vector EditorContext::SyncWorkspacePanelFrameEvents( void EditorContext::SyncSceneViewportRenderRequest( EditorSceneViewportRuntime& sceneViewportRuntime) { - sceneViewportRuntime.SetRenderRequest( - m_sceneRuntime.BuildSceneViewportRenderRequest()); + (void)sceneViewportRuntime; } } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/Scene/SceneViewportController.cpp b/editor/app/Features/Scene/SceneViewportController.cpp index 09d2ee53..068297e0 100644 --- a/editor/app/Features/Scene/SceneViewportController.cpp +++ b/editor/app/Features/Scene/SceneViewportController.cpp @@ -2,6 +2,7 @@ #include "Viewport/EditorViewportPicking.h" #include "Scene/EditorSceneRuntime.h" +#include "Scene/SceneViewportSession.h" #include "State/EditorCommandFocusService.h" #include "Panels/EditorPanelIds.h" @@ -105,30 +106,31 @@ bool TryResolveToolModeShortcut( void ApplySceneToolMode( SceneToolMode mode, + SceneViewportSession& session, EditorSceneRuntime& sceneRuntime, SceneViewportTransformGizmo& transformGizmo) { - if (sceneRuntime.GetToolMode() == mode) { + if (session.GetToolMode() == mode) { return; } transformGizmo.CancelDrag(sceneRuntime); - sceneRuntime.SetToolMode(mode); + session.SetToolMode(mode); } void ApplySceneViewportToggleButton( std::size_t toggleIndex, - EditorSceneRuntime& sceneRuntime) { + SceneViewportSession& session) { if (toggleIndex == 0u) { - sceneRuntime.SetToolPivotMode( - sceneRuntime.GetToolPivotMode() == SceneToolPivotMode::Pivot + session.SetToolPivotMode( + session.GetToolPivotMode() == SceneToolPivotMode::Pivot ? SceneToolPivotMode::Center : SceneToolPivotMode::Pivot); return; } if (toggleIndex == 1u) { - sceneRuntime.SetToolSpaceMode( - sceneRuntime.GetToolSpaceMode() == SceneToolSpaceMode::World + session.SetToolSpaceMode( + session.GetToolSpaceMode() == SceneToolSpaceMode::World ? SceneToolSpaceMode::Local : SceneToolSpaceMode::World); } @@ -162,6 +164,7 @@ void SceneViewportController::SetCommandFocusService( void SceneViewportController::Update( EditorSceneRuntime& sceneRuntime, + SceneViewportSession& session, const IViewportObjectPickerService& viewportObjectPicker, const UIEditorWorkspaceComposeState& composeState, const UIEditorWorkspaceComposeFrame& composeFrame) { @@ -173,7 +176,6 @@ void SceneViewportController::Update( if (m_transformGizmo.IsActive()) { m_transformGizmo.CancelDrag(sceneRuntime); } - sceneRuntime.SetHoveredToolHandle(SceneToolHandle::None); ResetInteractionState(); return; } @@ -221,9 +223,9 @@ void SceneViewportController::Update( slotLayout.inputRect, slotLayout.topBarRect, slotLayout.bounds, - sceneRuntime.GetToolMode(), - sceneRuntime.GetToolPivotMode(), - sceneRuntime.GetToolSpaceMode(), + session.GetToolMode(), + session.GetToolPivotMode(), + session.GetToolSpaceMode(), kSceneViewportToolOverlayInvalidIndex, kSceneViewportToolOverlayInvalidIndex, kSceneViewportToolOverlayInvalidIndex, @@ -254,7 +256,7 @@ void SceneViewportController::Update( m_activeToggleOverlayIndex = transitionHoveredToggleOverlayIndex; ApplySceneViewportToggleButton( m_activeToggleOverlayIndex, - sceneRuntime); + session); continue; } @@ -268,6 +270,7 @@ void SceneViewportController::Update( if (m_activeToolOverlayIndex < m_toolOverlay.GetFrame().buttons.size()) { ApplySceneToolMode( m_toolOverlay.GetFrame().buttons[m_activeToolOverlayIndex].mode, + session, sceneRuntime, m_transformGizmo); } @@ -288,9 +291,9 @@ void SceneViewportController::Update( slotLayout.inputRect, slotLayout.topBarRect, slotLayout.bounds, - sceneRuntime.GetToolMode(), - sceneRuntime.GetToolPivotMode(), - sceneRuntime.GetToolSpaceMode(), + session.GetToolMode(), + session.GetToolPivotMode(), + session.GetToolSpaceMode(), m_hoveredToolOverlayIndex, m_activeToolOverlayIndex, m_hoveredToggleOverlayIndex, @@ -300,7 +303,7 @@ void SceneViewportController::Update( m_activeToolOverlayIndex != kSceneViewportToolOverlayInvalidIndex || m_activeToggleOverlayIndex != kSceneViewportToolOverlayInvalidIndex; - const bool usingViewMoveTool = IsViewMoveTool(sceneRuntime.GetToolMode()); + const bool usingViewMoveTool = IsViewMoveTool(session.GetToolMode()); if (!m_transformGizmo.IsActive()) { for (const auto& transition : inputFrame.pointerButtonTransitions) { @@ -351,28 +354,29 @@ void SceneViewportController::Update( !m_navigationState.panDragging) { SceneToolMode shortcutMode = SceneToolMode::View; if (TryResolveToolModeShortcut(inputFrame, shortcutMode)) { - ApplySceneToolMode(shortcutMode, sceneRuntime, m_transformGizmo); + ApplySceneToolMode(shortcutMode, session, sceneRuntime, m_transformGizmo); } } if (inputFrame.focused && !m_transformGizmo.IsActive() && WasKeyPressed(inputFrame, KeyCode::F)) { - sceneRuntime.FocusSceneSelection(); + session.FocusSelection(sceneRuntime); } RefreshSceneOverlays( sceneRuntime, + session, slotLayout.inputRect, pointerScreen, viewportHoverEligible); - sceneRuntime.SetHoveredToolHandle(SceneToolHandle::None); if (m_transformGizmo.IsActive()) { if (WasKeyPressed(inputFrame, KeyCode::Escape)) { m_transformGizmo.CancelDrag(sceneRuntime); RefreshSceneOverlays( sceneRuntime, + session, slotLayout.inputRect, pointerScreen, viewportHoverEligible); @@ -383,6 +387,7 @@ void SceneViewportController::Update( m_transformGizmo.EndDrag(sceneRuntime); RefreshSceneOverlays( sceneRuntime, + session, slotLayout.inputRect, pointerScreen, viewportHoverEligible); @@ -392,6 +397,7 @@ void SceneViewportController::Update( m_transformGizmo.UpdateDrag(sceneRuntime); RefreshSceneOverlays( sceneRuntime, + session, slotLayout.inputRect, pointerScreen, viewportHoverEligible); @@ -409,14 +415,16 @@ void SceneViewportController::Update( RefreshSceneOverlays( sceneRuntime, + session, slotLayout.inputRect, transition.screenPosition, true); if (m_transformGizmo.IsHoveringHandle() && - m_transformGizmo.TryBeginDrag(sceneRuntime)) { + m_transformGizmo.TryBeginDrag(sceneRuntime, session)) { m_navigationState = {}; RefreshSceneOverlays( sceneRuntime, + session, slotLayout.inputRect, pointerScreen, viewportHoverEligible); @@ -429,6 +437,7 @@ void SceneViewportController::Update( sceneRuntime.SetSelection(sceneIconHit.entityId); RefreshSceneOverlays( sceneRuntime, + session, slotLayout.inputRect, pointerScreen, viewportHoverEligible); @@ -449,6 +458,7 @@ void SceneViewportController::Update( RefreshSceneOverlays( sceneRuntime, + session, slotLayout.inputRect, pointerScreen, viewportHoverEligible); @@ -486,14 +496,15 @@ void SceneViewportController::Update( } if (HasCameraInput(input)) { - sceneRuntime.ApplySceneViewportCameraInput(input); + session.ApplyCameraInput(input); RefreshSceneOverlays( sceneRuntime, + session, slotLayout.inputRect, pointerScreen, viewportHoverEligible); } else { - m_sceneOverlay.Refresh(sceneRuntime, slotLayout.inputRect); + m_sceneOverlay.Refresh(sceneRuntime, session, slotLayout.inputRect); } } @@ -529,12 +540,14 @@ float SceneViewportController::ConsumeDeltaTimeSeconds() { void SceneViewportController::RefreshSceneOverlays( EditorSceneRuntime& sceneRuntime, + const SceneViewportSession& session, const UIRect& viewportRect, const UIPoint& pointerScreen, bool hoverEnabled) { - m_sceneOverlay.Refresh(sceneRuntime, viewportRect); + m_sceneOverlay.Refresh(sceneRuntime, session, viewportRect); m_transformGizmo.Refresh( sceneRuntime, + session, viewportRect, pointerScreen, hoverEnabled); diff --git a/editor/app/Features/Scene/SceneViewportController.h b/editor/app/Features/Scene/SceneViewportController.h index bcf5ea1e..f4071e2c 100644 --- a/editor/app/Features/Scene/SceneViewportController.h +++ b/editor/app/Features/Scene/SceneViewportController.h @@ -17,6 +17,7 @@ class EditorCommandFocusService; class EditorIconService; class EditorSceneRuntime; class IViewportObjectPickerService; +class SceneViewportSession; class SceneViewportController { public: @@ -27,6 +28,7 @@ public: void Update( EditorSceneRuntime& sceneRuntime, + SceneViewportSession& session, const IViewportObjectPickerService& viewportObjectPicker, const UIEditorWorkspaceComposeState& composeState, const UIEditorWorkspaceComposeFrame& composeFrame); @@ -44,6 +46,7 @@ private: float ConsumeDeltaTimeSeconds(); void RefreshSceneOverlays( EditorSceneRuntime& sceneRuntime, + const SceneViewportSession& session, const ::XCEngine::UI::UIRect& viewportRect, const ::XCEngine::UI::UIPoint& pointerScreen, bool hoverEnabled); diff --git a/editor/app/Features/Scene/SceneViewportFeature.cpp b/editor/app/Features/Scene/SceneViewportFeature.cpp index 1ff80cc6..722af237 100644 --- a/editor/app/Features/Scene/SceneViewportFeature.cpp +++ b/editor/app/Features/Scene/SceneViewportFeature.cpp @@ -9,6 +9,7 @@ void SceneViewportFeature::Initialize( const EditorIconService* iconService, EditorSceneViewportRuntime& sceneViewportRuntime) { m_sceneViewportRuntime = &sceneViewportRuntime; + m_session.Reset(); m_controller.Initialize(iconService); } @@ -29,7 +30,7 @@ void SceneViewportFeature::SetCommandFocusService( void SceneViewportFeature::SyncRenderRequest(EditorSceneRuntime& sceneRuntime) { if (m_sceneViewportRuntime != nullptr) { m_sceneViewportRuntime->SetRenderRequest( - sceneRuntime.BuildSceneViewportRenderRequest()); + m_session.BuildRenderRequest(sceneRuntime)); } } @@ -43,6 +44,7 @@ void SceneViewportFeature::Update( m_controller.Update( sceneRuntime, + m_session, m_sceneViewportRuntime->GetObjectPicker(), composeState, composeFrame); diff --git a/editor/app/Features/Scene/SceneViewportFeature.h b/editor/app/Features/Scene/SceneViewportFeature.h index 42e47789..748b7d5b 100644 --- a/editor/app/Features/Scene/SceneViewportFeature.h +++ b/editor/app/Features/Scene/SceneViewportFeature.h @@ -1,5 +1,6 @@ #pragma once +#include "Scene/SceneViewportSession.h" #include "Assets/EditorIconService.h" #include "Scene/SceneViewportController.h" #include "Viewport/EditorViewportRuntimeServices.h" @@ -30,6 +31,7 @@ public: private: EditorSceneViewportRuntime* m_sceneViewportRuntime = nullptr; + SceneViewportSession m_session = {}; SceneViewportController m_controller = {}; }; diff --git a/editor/app/Features/Scene/SceneViewportSceneOverlay.cpp b/editor/app/Features/Scene/SceneViewportSceneOverlay.cpp index 4ee721e3..cc0d5970 100644 --- a/editor/app/Features/Scene/SceneViewportSceneOverlay.cpp +++ b/editor/app/Features/Scene/SceneViewportSceneOverlay.cpp @@ -3,6 +3,7 @@ #include "Scene/SceneViewportTransformGizmoSupport.h" #include "Assets/EditorIconService.h" #include "Scene/EditorSceneRuntime.h" +#include "Scene/SceneViewportSession.h" #include #include @@ -27,10 +28,9 @@ bool ContainsPoint(const UIRect& rect, const UIPoint& point) { } SceneViewportGizmoSupport::SceneViewportOverlayData BuildOverlayData( - const EditorSceneRuntime& sceneRuntime) { + const SceneViewportSession& session) { SceneViewportGizmoSupport::SceneViewportOverlayData overlay = {}; - const EditorSceneCameraSnapshot snapshot = - sceneRuntime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot snapshot = session.BuildCameraSnapshot(); if (!snapshot.valid) { return overlay; } @@ -88,7 +88,8 @@ void SceneViewportSceneOverlay::ResetFrame() { } void SceneViewportSceneOverlay::Refresh( - EditorSceneRuntime& sceneRuntime, + const EditorSceneRuntime& sceneRuntime, + const SceneViewportSession& session, const UIRect& viewportRect) { m_frame = {}; m_frame.clipRect = viewportRect; @@ -100,7 +101,7 @@ void SceneViewportSceneOverlay::Refresh( } const SceneViewportGizmoSupport::SceneViewportOverlayData overlay = - BuildOverlayData(sceneRuntime); + BuildOverlayData(session); if (!overlay.valid) { return; } diff --git a/editor/app/Features/Scene/SceneViewportSceneOverlay.h b/editor/app/Features/Scene/SceneViewportSceneOverlay.h index f7e8a4be..b7143aca 100644 --- a/editor/app/Features/Scene/SceneViewportSceneOverlay.h +++ b/editor/app/Features/Scene/SceneViewportSceneOverlay.h @@ -9,6 +9,7 @@ namespace XCEngine::UI::Editor::App { class EditorSceneRuntime; class EditorIconService; +class SceneViewportSession; class SceneViewportSceneOverlay { public: @@ -24,7 +25,8 @@ public: void SetIconService(const EditorIconService* icons); void ResetFrame(); void Refresh( - EditorSceneRuntime& sceneRuntime, + const EditorSceneRuntime& sceneRuntime, + const SceneViewportSession& session, const ::XCEngine::UI::UIRect& viewportRect); HitResult HitTest(const ::XCEngine::UI::UIPoint& point) const; void Append(::XCEngine::UI::UIDrawList& drawList) const; diff --git a/editor/app/Features/Scene/SceneViewportSession.cpp b/editor/app/Features/Scene/SceneViewportSession.cpp new file mode 100644 index 00000000..9e7d09db --- /dev/null +++ b/editor/app/Features/Scene/SceneViewportSession.cpp @@ -0,0 +1,116 @@ +#include "Scene/SceneViewportSession.h" + +#include "Scene/EditorSceneRuntime.h" + +namespace XCEngine::UI::Editor::App { + +namespace { + +std::uint64_t CombineSceneViewportRequestRevisions( + std::uint64_t sceneContentRevision, + std::uint64_t sceneViewCameraRevision) { + constexpr std::uint64_t kRevisionMixConstant = 0x9e3779b97f4a7c15ull; + return sceneContentRevision ^ + (sceneViewCameraRevision + kRevisionMixConstant + + (sceneContentRevision << 6u) + + (sceneContentRevision >> 2u)); +} + +} // namespace + +SceneViewportSession::SceneViewportSession() { + Reset(); +} + +void SceneViewportSession::Reset() { + m_cameraController.Reset(); + m_toolMode = SceneToolMode::Translate; + m_toolSpaceMode = SceneToolSpaceMode::World; + m_toolPivotMode = SceneToolPivotMode::Pivot; + m_cameraRevision = 0u; +} + +EditorSceneCameraSnapshot SceneViewportSession::BuildCameraSnapshot() const { + EditorSceneCameraSnapshot snapshot = {}; + snapshot.valid = true; + snapshot.position = m_cameraController.GetPosition(); + snapshot.forward = m_cameraController.GetForward(); + snapshot.right = m_cameraController.GetRight(); + snapshot.up = m_cameraController.GetUp(); + snapshot.verticalFovDegrees = 60.0f; + snapshot.nearClipPlane = 0.03f; + snapshot.farClipPlane = 2000.0f; + snapshot.orbitDistance = m_cameraController.GetDistance(); + return snapshot; +} + +SceneViewportRenderRequest SceneViewportSession::BuildRenderRequest( + const EditorSceneRuntime& sceneRuntime) const { + SceneViewportRenderRequest request = {}; + request.camera = BuildCameraSnapshot(); + request.requestRevision = + BuildRequestRevision(sceneRuntime.GetSceneContentRevision()); + if (const std::optional selectedId = + sceneRuntime.GetSelectedObjectId(); + selectedId.has_value()) { + request.selectedObjectIds.push_back(selectedId.value()); + request.selectedHelpers = + sceneRuntime.BuildSelectedViewportHelperSnapshots(); + } + return request; +} + +void SceneViewportSession::ApplyCameraInput( + const SceneViewportCameraInputState& input) { + m_cameraController.ApplyInput(input); + IncrementCameraRevision(); +} + +bool SceneViewportSession::FocusSelection( + const EditorSceneRuntime& sceneRuntime) { + SceneTransformSnapshot snapshot = {}; + if (!sceneRuntime.CaptureSelectedTransformSnapshot(snapshot)) { + return false; + } + + m_cameraController.Focus(snapshot.position); + IncrementCameraRevision(); + return true; +} + +SceneToolMode SceneViewportSession::GetToolMode() const { + return m_toolMode; +} + +SceneToolSpaceMode SceneViewportSession::GetToolSpaceMode() const { + return m_toolSpaceMode; +} + +SceneToolPivotMode SceneViewportSession::GetToolPivotMode() const { + return m_toolPivotMode; +} + +void SceneViewportSession::SetToolMode(SceneToolMode mode) { + m_toolMode = mode; +} + +void SceneViewportSession::SetToolSpaceMode(SceneToolSpaceMode mode) { + m_toolSpaceMode = mode; +} + +void SceneViewportSession::SetToolPivotMode(SceneToolPivotMode mode) { + m_toolPivotMode = mode; +} + +void SceneViewportSession::IncrementCameraRevision() { + ++m_cameraRevision; +} + +std::uint64_t SceneViewportSession::BuildRequestRevision( + std::uint64_t sceneContentRevision) const { + return CombineSceneViewportRequestRevisions( + sceneContentRevision, + m_cameraRevision); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/Scene/SceneViewportSession.h b/editor/app/Features/Scene/SceneViewportSession.h new file mode 100644 index 00000000..5481051f --- /dev/null +++ b/editor/app/Features/Scene/SceneViewportSession.h @@ -0,0 +1,46 @@ +#pragma once + +#include "Scene/EditorSceneBackend.h" +#include "Scene/SceneToolState.h" +#include "Scene/SceneViewportCameraController.h" +#include "Scene/SceneViewportRenderRequest.h" + +#include + +namespace XCEngine::UI::Editor::App { + +class EditorSceneRuntime; + +class SceneViewportSession { +public: + SceneViewportSession(); + + void Reset(); + + EditorSceneCameraSnapshot BuildCameraSnapshot() const; + SceneViewportRenderRequest BuildRenderRequest( + const EditorSceneRuntime& sceneRuntime) const; + + void ApplyCameraInput(const SceneViewportCameraInputState& input); + bool FocusSelection(const EditorSceneRuntime& sceneRuntime); + + SceneToolMode GetToolMode() const; + SceneToolSpaceMode GetToolSpaceMode() const; + SceneToolPivotMode GetToolPivotMode() const; + + void SetToolMode(SceneToolMode mode); + void SetToolSpaceMode(SceneToolSpaceMode mode); + void SetToolPivotMode(SceneToolPivotMode mode); + +private: + void IncrementCameraRevision(); + std::uint64_t BuildRequestRevision(std::uint64_t sceneContentRevision) const; + + SceneViewportCameraController m_cameraController = {}; + SceneToolMode m_toolMode = SceneToolMode::Translate; + SceneToolSpaceMode m_toolSpaceMode = SceneToolSpaceMode::World; + SceneToolPivotMode m_toolPivotMode = SceneToolPivotMode::Pivot; + std::uint64_t m_cameraRevision = 0u; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/Scene/SceneViewportTransformGizmo.cpp b/editor/app/Features/Scene/SceneViewportTransformGizmo.cpp index a4df5c08..db8807b3 100644 --- a/editor/app/Features/Scene/SceneViewportTransformGizmo.cpp +++ b/editor/app/Features/Scene/SceneViewportTransformGizmo.cpp @@ -2,6 +2,7 @@ #include "Scene/SceneViewportTransformGizmoSupport.h" #include "Scene/EditorSceneRuntime.h" +#include "Scene/SceneViewportSession.h" #include "Scene/SceneToolState.h" #include @@ -83,10 +84,9 @@ TransformGizmoSelectionState BuildSelectionState( return state; } -SceneViewportOverlayData BuildOverlayData(const EditorSceneRuntime& sceneRuntime) { +SceneViewportOverlayData BuildOverlayData(const SceneViewportSession& session) { SceneViewportOverlayData overlay = {}; - const EditorSceneCameraSnapshot snapshot = - sceneRuntime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot snapshot = session.BuildCameraSnapshot(); if (!snapshot.valid) { return overlay; } @@ -310,6 +310,7 @@ SceneViewportTransformGizmo& SceneViewportTransformGizmo::operator=( void SceneViewportTransformGizmo::Refresh( EditorSceneRuntime& sceneRuntime, + const SceneViewportSession& session, const UIRect& viewportRect, const UIPoint& pointerScreen, bool hoverEnabled) { @@ -322,16 +323,16 @@ void SceneViewportTransformGizmo::Refresh( return; } - const SceneViewportOverlayData overlay = BuildOverlayData(sceneRuntime); + const SceneViewportOverlayData overlay = BuildOverlayData(session); if (!overlay.valid) { CancelDrag(sceneRuntime); return; } const bool useCenterPivot = - sceneRuntime.GetToolPivotMode() == SceneToolPivotMode::Center; + session.GetToolPivotMode() == SceneToolPivotMode::Center; const bool localSpace = - sceneRuntime.GetToolSpaceMode() == SceneToolSpaceMode::Local; + session.GetToolSpaceMode() == SceneToolSpaceMode::Local; const TransformGizmoSelectionState selection = BuildSelectionState(sceneRuntime, useCenterPivot); if (!selection.primaryObject.IsValid()) { @@ -339,7 +340,7 @@ void SceneViewportTransformGizmo::Refresh( return; } - const SceneToolMode toolMode = sceneRuntime.GetToolMode(); + const SceneToolMode toolMode = session.GetToolMode(); if (toolMode == SceneToolMode::View) { CancelDrag(sceneRuntime); return; @@ -466,11 +467,13 @@ void SceneViewportTransformGizmo::Refresh( } } -bool SceneViewportTransformGizmo::TryBeginDrag(EditorSceneRuntime& sceneRuntime) { +bool SceneViewportTransformGizmo::TryBeginDrag( + EditorSceneRuntime& sceneRuntime, + const SceneViewportSession& session) { State& state = *m_state; state.undoBridge.Bind(sceneRuntime); - switch (sceneRuntime.GetToolMode()) { + switch (session.GetToolMode()) { case SceneToolMode::Translate: return state.moveGizmo.TryBeginDrag(state.moveContext, state.undoBridge); case SceneToolMode::Rotate: diff --git a/editor/app/Features/Scene/SceneViewportTransformGizmo.h b/editor/app/Features/Scene/SceneViewportTransformGizmo.h index a2cf85c5..97e852e9 100644 --- a/editor/app/Features/Scene/SceneViewportTransformGizmo.h +++ b/editor/app/Features/Scene/SceneViewportTransformGizmo.h @@ -9,6 +9,7 @@ namespace XCEngine::UI::Editor::App { class EditorSceneRuntime; +class SceneViewportSession; struct SceneViewportTransformGizmoTriangle { ::XCEngine::UI::UIPoint a = {}; @@ -35,10 +36,13 @@ public: void Refresh( EditorSceneRuntime& sceneRuntime, + const SceneViewportSession& session, const ::XCEngine::UI::UIRect& viewportRect, const ::XCEngine::UI::UIPoint& pointerScreen, bool hoverEnabled); - bool TryBeginDrag(EditorSceneRuntime& sceneRuntime); + bool TryBeginDrag( + EditorSceneRuntime& sceneRuntime, + const SceneViewportSession& session); bool UpdateDrag(EditorSceneRuntime& sceneRuntime); bool EndDrag(EditorSceneRuntime& sceneRuntime); void CancelDrag(EditorSceneRuntime& sceneRuntime); diff --git a/editor/app/Services/Scene/EditorSceneRuntime.cpp b/editor/app/Services/Scene/EditorSceneRuntime.cpp index c969d3c9..5ff85aa6 100644 --- a/editor/app/Services/Scene/EditorSceneRuntime.cpp +++ b/editor/app/Services/Scene/EditorSceneRuntime.cpp @@ -36,16 +36,6 @@ bool TransformSnapshotsMatch( NearlyEqual(lhs.scale, rhs.scale); } -std::uint64_t CombineSceneViewportRequestRevisions( - std::uint64_t sceneContentRevision, - std::uint64_t sceneViewCameraRevision) { - constexpr std::uint64_t kRevisionMixConstant = 0x9e3779b97f4a7c15ull; - return sceneContentRevision ^ - (sceneViewCameraRevision + kRevisionMixConstant + - (sceneContentRevision << 6u) + - (sceneContentRevision >> 2u)); -} - } // namespace std::string_view GetSceneToolModeName(SceneToolMode mode) { @@ -116,12 +106,9 @@ void EditorSceneRuntime::Reset() { m_startupSceneResult = {}; m_ownedSelectionService.ClearSelection(); m_selectionService = &m_ownedSelectionService; - m_sceneViewCameraController.Reset(); - m_toolState = {}; ResetTransformEditHistory(); m_inspectorRevision = 0u; m_sceneContentRevision = 0u; - m_sceneViewCameraRevision = 0u; } void EditorSceneRuntime::SetBackend(std::unique_ptr backend) { @@ -153,11 +140,7 @@ void EditorSceneRuntime::RefreshScene() { } else { RevalidateSelection(); } - } else if (m_toolState.dragState.active) { - ResetToolInteractionState(); } - - ClearInvalidToolInteractionState(); } void EditorSceneRuntime::EnsureSceneSelection() { @@ -179,39 +162,6 @@ EditorSceneHierarchySnapshot EditorSceneRuntime::BuildHierarchySnapshot() const : EditorSceneHierarchySnapshot{}; } -float EditorSceneRuntime::GetSceneViewOrbitDistance() const { - return m_sceneViewCameraController.GetDistance(); -} - -SceneViewportRenderRequest EditorSceneRuntime::BuildSceneViewportRenderRequest() { - SceneViewportRenderRequest request = {}; - request.camera = BuildSceneViewCameraSnapshot(); - request.requestRevision = BuildSceneViewportRequestRevision(); - if (const std::optional selectedId = GetSelectedObjectId(); - selectedId.has_value()) { - request.selectedObjectIds.push_back(selectedId.value()); - if (m_backend != nullptr) { - request.selectedHelpers = - m_backend->BuildViewportHelperSnapshots(selectedId.value()); - } - } - return request; -} - -EditorSceneCameraSnapshot EditorSceneRuntime::BuildSceneViewCameraSnapshot() const { - EditorSceneCameraSnapshot snapshot = {}; - snapshot.valid = true; - snapshot.position = m_sceneViewCameraController.GetPosition(); - snapshot.forward = m_sceneViewCameraController.GetForward(); - snapshot.right = m_sceneViewCameraController.GetRight(); - snapshot.up = m_sceneViewCameraController.GetUp(); - snapshot.verticalFovDegrees = 60.0f; - snapshot.nearClipPlane = 0.03f; - snapshot.farClipPlane = 2000.0f; - snapshot.orbitDistance = GetSceneViewOrbitDistance(); - return snapshot; -} - std::vector EditorSceneRuntime::BuildSceneViewportIconSnapshots() const { return m_backend != nullptr @@ -229,21 +179,14 @@ EditorSceneRuntime::BuildSceneViewportSelectionSnapshot() const { return m_backend->BuildViewportSelectionSnapshot(selectedId.value()); } -void EditorSceneRuntime::ApplySceneViewportCameraInput( - const SceneViewportCameraInputState& input) { - m_sceneViewCameraController.ApplyInput(input); - IncrementSceneViewCameraRevision(); -} - -bool EditorSceneRuntime::FocusSceneSelection() { - SceneTransformSnapshot snapshot = {}; - if (!CaptureSelectedTransformSnapshot(snapshot)) { - return false; +std::vector +EditorSceneRuntime::BuildSelectedViewportHelperSnapshots() const { + const std::optional selectedId = GetSelectedObjectId(); + if (!selectedId.has_value() || m_backend == nullptr) { + return {}; } - m_sceneViewCameraController.Focus(snapshot.position); - IncrementSceneViewCameraRevision(); - return true; + return m_backend->BuildViewportHelperSnapshots(selectedId.value()); } bool EditorSceneRuntime::HasSceneSelection() const { @@ -326,18 +269,13 @@ bool EditorSceneRuntime::SetSelection(EditorSceneObjectId id) { !previousId.has_value() || previousId.value() != id || !HasHierarchySelection(); - if (changed) { - ResetToolInteractionState(); - } SelectionService().SetHierarchySelection( MakeEditorGameObjectItemId(id), snapshot->displayName); - ClearInvalidToolInteractionState(); return changed; } void EditorSceneRuntime::ClearSelection() { - ResetToolInteractionState(); SelectionService().ClearSelection(); } @@ -359,7 +297,6 @@ bool EditorSceneRuntime::NewScene(std::string_view sceneName) { } ResetTransformEditHistory(); - ResetToolInteractionState(); SelectionService().ClearSelection(); IncrementInspectorRevision(); IncrementSceneContentRevision(); @@ -379,7 +316,6 @@ bool EditorSceneRuntime::OpenSceneAsset(const std::filesystem::path& scenePath) m_startupSceneResult.sceneName = scenePath.stem().string(); ResetTransformEditHistory(); - ResetToolInteractionState(); SelectionService().ClearSelection(); IncrementInspectorRevision(); IncrementSceneContentRevision(); @@ -661,70 +597,6 @@ bool EditorSceneRuntime::ApplySelectedComponentMutation( return true; } -const SceneToolState& EditorSceneRuntime::GetToolState() const { - return m_toolState; -} - -SceneToolMode EditorSceneRuntime::GetToolMode() const { - return m_toolState.mode; -} - -SceneToolSpaceMode EditorSceneRuntime::GetToolSpaceMode() const { - return m_toolState.spaceMode; -} - -SceneToolPivotMode EditorSceneRuntime::GetToolPivotMode() const { - return m_toolState.pivotMode; -} - -void EditorSceneRuntime::SetToolMode(SceneToolMode mode) { - if (m_toolState.mode == mode) { - return; - } - - ResetToolInteractionState(); - m_toolState.mode = mode; -} - -void EditorSceneRuntime::SetToolSpaceMode(SceneToolSpaceMode mode) { - if (m_toolState.spaceMode == mode) { - return; - } - - ResetToolInteractionState(); - m_toolState.spaceMode = mode; -} - -void EditorSceneRuntime::SetToolPivotMode(SceneToolPivotMode mode) { - if (m_toolState.pivotMode == mode) { - return; - } - - ResetToolInteractionState(); - m_toolState.pivotMode = mode; -} - -void EditorSceneRuntime::SetHoveredToolHandle(SceneToolHandle handle) { - if (m_toolState.dragState.active) { - return; - } - - m_toolState.hoveredHandle = handle; -} - -void EditorSceneRuntime::SetToolInteractionLock(SceneToolInteractionLock lock) { - m_toolState.interactionLock = lock; -} - -void EditorSceneRuntime::ClearToolInteractionLock() { - m_toolState.interactionLock = SceneToolInteractionLock::None; -} - -void EditorSceneRuntime::ResetToolInteractionState() { - CancelTransformToolDrag(); - ResetToolInteractionTransientState(); -} - bool EditorSceneRuntime::CaptureSelectedTransformSnapshot( SceneTransformSnapshot& outSnapshot) const { const std::optional selectedId = GetSelectedObjectId(); @@ -782,51 +654,6 @@ bool EditorSceneRuntime::RecordTransformEdit( return true; } -bool EditorSceneRuntime::BeginTransformToolDrag( - SceneToolHandle handle, - const ::XCEngine::UI::UIPoint& startPointerPosition) { - if (handle == SceneToolHandle::None || - m_toolState.mode == SceneToolMode::View) { - return false; - } - - SceneTransformSnapshot snapshot = {}; - if (!CaptureSelectedTransformSnapshot(snapshot)) { - return false; - } - - CancelTransformToolDrag(); - m_toolState.dragState = {}; - m_toolState.dragState.active = true; - m_toolState.dragState.mode = m_toolState.mode; - m_toolState.dragState.handle = handle; - m_toolState.dragState.startPointerPosition = startPointerPosition; - m_toolState.dragState.initialTransform = snapshot; - m_toolState.hoveredHandle = handle; - m_toolState.activeHandle = handle; - SetToolInteractionLock(SceneToolInteractionLock::TransformDrag); - return true; -} - -bool EditorSceneRuntime::HasActiveTransformToolDrag() const { - return m_toolState.dragState.active; -} - -const SceneToolDragState* EditorSceneRuntime::GetActiveTransformToolDrag() const { - return m_toolState.dragState.active ? &m_toolState.dragState : nullptr; -} - -bool EditorSceneRuntime::ApplyTransformToolPreview( - const SceneTransformSnapshot& snapshot) { - if (!m_toolState.dragState.active || - !snapshot.IsValid() || - snapshot.targetId != m_toolState.dragState.initialTransform.targetId) { - return false; - } - - return ApplyTransformSnapshot(snapshot); -} - bool EditorSceneRuntime::ApplyTransformToolWorldPreview( EditorSceneObjectId targetId, const Vector3& position, @@ -836,9 +663,7 @@ bool EditorSceneRuntime::ApplyTransformToolWorldPreview( } const EditorSceneObjectId activeTargetId = - m_toolState.dragState.active - ? m_toolState.dragState.initialTransform.targetId - : GetSelectedObjectId().value_or(kInvalidEditorSceneObjectId); + GetSelectedObjectId().value_or(kInvalidEditorSceneObjectId); if (activeTargetId == kInvalidEditorSceneObjectId || targetId != activeTargetId) { return false; @@ -862,9 +687,7 @@ bool EditorSceneRuntime::ApplyTransformToolLocalScalePreview( } const EditorSceneObjectId activeTargetId = - m_toolState.dragState.active - ? m_toolState.dragState.initialTransform.targetId - : GetSelectedObjectId().value_or(kInvalidEditorSceneObjectId); + GetSelectedObjectId().value_or(kInvalidEditorSceneObjectId); if (activeTargetId == kInvalidEditorSceneObjectId || targetId != activeTargetId) { return false; @@ -880,37 +703,6 @@ bool EditorSceneRuntime::ApplyTransformToolLocalScalePreview( return true; } -bool EditorSceneRuntime::CommitTransformToolDrag() { - if (!m_toolState.dragState.active) { - return false; - } - - SceneTransformSnapshot afterSnapshot = {}; - const bool captured = CaptureSelectedTransformSnapshot(afterSnapshot); - const SceneTransformSnapshot beforeSnapshot = - m_toolState.dragState.initialTransform; - ResetToolInteractionTransientState(); - - if (!captured || !afterSnapshot.IsValid() || !beforeSnapshot.IsValid()) { - return false; - } - - return RecordTransformEdit(beforeSnapshot, afterSnapshot); -} - -void EditorSceneRuntime::CancelTransformToolDrag() { - if (!m_toolState.dragState.active) { - return; - } - - const SceneTransformSnapshot snapshot = m_toolState.dragState.initialTransform; - if (snapshot.IsValid()) { - ApplyTransformSnapshot(snapshot); - } - - ResetToolInteractionTransientState(); -} - bool EditorSceneRuntime::CanUndoTransformEdit() const { return !m_transformUndoStack.empty(); } @@ -1027,46 +819,4 @@ void EditorSceneRuntime::IncrementSceneContentRevision() { ++m_sceneContentRevision; } -void EditorSceneRuntime::IncrementSceneViewCameraRevision() { - ++m_sceneViewCameraRevision; -} - -std::uint64_t EditorSceneRuntime::BuildSceneViewportRequestRevision() const { - return CombineSceneViewportRequestRevisions( - m_sceneContentRevision, - m_sceneViewCameraRevision); -} - -void EditorSceneRuntime::ResetToolInteractionTransientState() { - m_toolState.hoveredHandle = SceneToolHandle::None; - m_toolState.activeHandle = SceneToolHandle::None; - ClearToolInteractionLock(); - m_toolState.dragState = {}; -} - -void EditorSceneRuntime::ClearInvalidToolInteractionState() { - if (!HasSceneSelection()) { - ResetToolInteractionTransientState(); - return; - } - - if (!m_toolState.dragState.active) { - return; - } - - const SceneTransformSnapshot snapshot = m_toolState.dragState.initialTransform; - if (!snapshot.IsValid()) { - ResetToolInteractionTransientState(); - return; - } - - const std::optional selectionSnapshot = - m_backend != nullptr - ? m_backend->BuildViewportSelectionSnapshot(snapshot.targetId) - : std::nullopt; - if (!selectionSnapshot.has_value() || !selectionSnapshot->IsValid()) { - ResetToolInteractionTransientState(); - } -} - } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Services/Scene/EditorSceneRuntime.h b/editor/app/Services/Scene/EditorSceneRuntime.h index c511c7db..85dde1b1 100644 --- a/editor/app/Services/Scene/EditorSceneRuntime.h +++ b/editor/app/Services/Scene/EditorSceneRuntime.h @@ -1,10 +1,8 @@ #pragma once #include "Scene/EditorSceneBackend.h" -#include "Scene/SceneViewportCameraController.h" #include "Scene/SceneToolState.h" #include "State/EditorSelectionService.h" -#include "Scene/SceneViewportRenderRequest.h" #include @@ -48,13 +46,9 @@ public: const EditorStartupSceneResult& GetStartupResult() const; EditorSceneHierarchySnapshot BuildHierarchySnapshot() const; - float GetSceneViewOrbitDistance() const; - SceneViewportRenderRequest BuildSceneViewportRenderRequest(); - EditorSceneCameraSnapshot BuildSceneViewCameraSnapshot() const; std::vector BuildSceneViewportIconSnapshots() const; std::optional BuildSceneViewportSelectionSnapshot() const; - void ApplySceneViewportCameraInput(const SceneViewportCameraInputState& input); - bool FocusSceneSelection(); + std::vector BuildSelectedViewportHelperSnapshots() const; bool HasSceneSelection() const; std::optional GetSelectedObjectId() const; @@ -105,29 +99,11 @@ public: bool ApplySelectedComponentMutation( const EditorSceneComponentMutation& mutation); - const SceneToolState& GetToolState() const; - SceneToolMode GetToolMode() const; - SceneToolSpaceMode GetToolSpaceMode() const; - SceneToolPivotMode GetToolPivotMode() const; - void SetToolMode(SceneToolMode mode); - void SetToolSpaceMode(SceneToolSpaceMode mode); - void SetToolPivotMode(SceneToolPivotMode mode); - void SetHoveredToolHandle(SceneToolHandle handle); - void SetToolInteractionLock(SceneToolInteractionLock lock); - void ClearToolInteractionLock(); - void ResetToolInteractionState(); - bool CaptureSelectedTransformSnapshot(SceneTransformSnapshot& outSnapshot) const; bool ApplyTransformSnapshot(const SceneTransformSnapshot& snapshot); bool RecordTransformEdit( const SceneTransformSnapshot& before, const SceneTransformSnapshot& after); - bool BeginTransformToolDrag( - SceneToolHandle handle, - const ::XCEngine::UI::UIPoint& startPointerPosition); - bool HasActiveTransformToolDrag() const; - const SceneToolDragState* GetActiveTransformToolDrag() const; - bool ApplyTransformToolPreview(const SceneTransformSnapshot& snapshot); bool ApplyTransformToolWorldPreview( EditorSceneObjectId targetId, const ::XCEngine::Math::Vector3& position, @@ -135,8 +111,6 @@ public: bool ApplyTransformToolLocalScalePreview( EditorSceneObjectId targetId, const ::XCEngine::Math::Vector3& localScale); - bool CommitTransformToolDrag(); - void CancelTransformToolDrag(); bool CanUndoTransformEdit() const; bool CanRedoTransformEdit() const; @@ -160,25 +134,18 @@ private: std::string_view componentId) const; bool SelectFirstAvailableGameObject(); void ResetTransformEditHistory(); - void ResetToolInteractionTransientState(); - void ClearInvalidToolInteractionState(); void IncrementInspectorRevision(); void IncrementSceneContentRevision(); - void IncrementSceneViewCameraRevision(); - std::uint64_t BuildSceneViewportRequestRevision() const; std::filesystem::path m_projectRoot = {}; std::unique_ptr m_backend = {}; EditorStartupSceneResult m_startupSceneResult = {}; EditorSelectionService m_ownedSelectionService = {}; EditorSelectionService* m_selectionService = &m_ownedSelectionService; - SceneViewportCameraController m_sceneViewCameraController = {}; - SceneToolState m_toolState = {}; std::vector m_transformUndoStack = {}; std::vector m_transformRedoStack = {}; std::uint64_t m_inspectorRevision = 0u; std::uint64_t m_sceneContentRevision = 0u; - std::uint64_t m_sceneViewCameraRevision = 0u; }; } // namespace XCEngine::UI::Editor::App diff --git a/tests/UI/Editor/unit/test_scene_viewport_runtime.cpp b/tests/UI/Editor/unit/test_scene_viewport_runtime.cpp index fff653e5..fe45dda1 100644 --- a/tests/UI/Editor/unit/test_scene_viewport_runtime.cpp +++ b/tests/UI/Editor/unit/test_scene_viewport_runtime.cpp @@ -1,6 +1,7 @@ #include "Scene/EditorSceneRuntime.h" #include "Scene/EngineEditorSceneBackend.h" #include "Scene/SceneViewportController.h" +#include "Scene/SceneViewportSession.h" #include "Inspector/InspectorSubject.h" #include "Viewport/SceneViewportRenderService.h" #include "Viewport/ViewportHostService.h" @@ -218,6 +219,7 @@ const EditorSceneComponentDescriptor* FindComponentDescriptor( std::optional FindHoveredTransformGizmoPoint( SceneViewportTransformGizmo& gizmo, EditorSceneRuntime& runtime, + const SceneViewportSession& session, const UIRect& viewportRect) { for (float y = viewportRect.y + 8.0f; y < viewportRect.y + viewportRect.height - 8.0f; @@ -226,7 +228,7 @@ std::optional FindHoveredTransformGizmoPoint( x < viewportRect.x + viewportRect.width - 8.0f; x += 6.0f) { const UIPoint point(x, y); - gizmo.Refresh(runtime, viewportRect, point, true); + gizmo.Refresh(runtime, session, viewportRect, point, true); if (gizmo.IsHoveringHandle()) { return point; } @@ -244,18 +246,19 @@ TEST(SceneViewportRuntimeTests, ApplySceneViewportCameraInputUpdatesCameraTransf EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); + SceneViewportSession session = {}; const EditorSceneCameraSnapshot before = - runtime.BuildSceneViewCameraSnapshot(); + session.BuildCameraSnapshot(); ASSERT_TRUE(before.valid); SceneViewportCameraInputState input = {}; input.viewportHeight = 720.0f; input.zoomDelta = 1.0f; - runtime.ApplySceneViewportCameraInput(input); + session.ApplyCameraInput(input); const EditorSceneCameraSnapshot after = - runtime.BuildSceneViewCameraSnapshot(); + session.BuildCameraSnapshot(); ASSERT_TRUE(after.valid); EXPECT_NE(before.position.x, after.position.x); EXPECT_NE(before.position.y, after.position.y); @@ -270,6 +273,7 @@ TEST(SceneViewportRuntimeTests, FocusSceneSelectionRepositionsCameraAroundSelect EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); + SceneViewportSession session = {}; Scene* scene = GetLoadedActiveScene(); ASSERT_NE(scene, nullptr); @@ -278,13 +282,13 @@ TEST(SceneViewportRuntimeTests, FocusSceneSelectionRepositionsCameraAroundSelect ASSERT_TRUE(runtime.SetSelection(target->GetID())); const EditorSceneCameraSnapshot before = - runtime.BuildSceneViewCameraSnapshot(); + session.BuildCameraSnapshot(); ASSERT_TRUE(before.valid); - ASSERT_TRUE(runtime.FocusSceneSelection()); + ASSERT_TRUE(session.FocusSelection(runtime)); const EditorSceneCameraSnapshot after = - runtime.BuildSceneViewCameraSnapshot(); + session.BuildCameraSnapshot(); ASSERT_TRUE(after.valid); EXPECT_NE(before.position.x, after.position.x); EXPECT_NE(before.position.y, after.position.y); @@ -299,6 +303,7 @@ TEST(SceneViewportRuntimeTests, BuildSceneViewportRenderRequestIncludesSelectedO EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); + SceneViewportSession session = {}; Scene* scene = GetLoadedActiveScene(); ASSERT_NE(scene, nullptr); @@ -307,7 +312,7 @@ TEST(SceneViewportRuntimeTests, BuildSceneViewportRenderRequestIncludesSelectedO ASSERT_TRUE(runtime.SetSelection(target->GetID())); const SceneViewportRenderRequest request = - runtime.BuildSceneViewportRenderRequest(); + session.BuildRenderRequest(runtime); ASSERT_TRUE(request.IsValid()); ASSERT_EQ(request.selectedObjectIds.size(), 1u); EXPECT_EQ(request.selectedObjectIds.front(), target->GetID()); @@ -315,6 +320,39 @@ TEST(SceneViewportRuntimeTests, BuildSceneViewportRenderRequestIncludesSelectedO EXPECT_FALSE(request.debugSelectionMask); } +TEST(SceneViewportRuntimeTests, SceneViewportSessionsKeepCameraAndToolStateIndependent) { + ScopedSceneManagerReset reset = {}; + TemporaryProjectRoot projectRoot = {}; + SaveMainScene(projectRoot, Math::Vector3(4.0f, 5.0f, 6.0f)); + + EditorSceneRuntime runtime = {}; + BindEngineSceneBackend(runtime); + ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); + + SceneViewportSession primarySession = {}; + SceneViewportSession secondarySession = {}; + secondarySession.SetToolMode(SceneToolMode::View); + + SceneViewportCameraInputState input = {}; + input.viewportHeight = 720.0f; + input.zoomDelta = 1.0f; + primarySession.ApplyCameraInput(input); + + const EditorSceneCameraSnapshot primaryCamera = + primarySession.BuildCameraSnapshot(); + const EditorSceneCameraSnapshot secondaryCamera = + secondarySession.BuildCameraSnapshot(); + + EXPECT_EQ(primarySession.GetToolMode(), SceneToolMode::Translate); + EXPECT_EQ(secondarySession.GetToolMode(), SceneToolMode::View); + EXPECT_NE(primaryCamera.position.x, secondaryCamera.position.x); + EXPECT_NE(primaryCamera.position.y, secondaryCamera.position.y); + EXPECT_NE(primaryCamera.position.z, secondaryCamera.position.z); + EXPECT_NE( + primarySession.BuildRenderRequest(runtime).requestRevision, + secondarySession.BuildRenderRequest(runtime).requestRevision); +} + TEST(SceneViewportRuntimeTests, SelectedComponentsExposeTransformAndAttachedCameraDescriptors) { ScopedSceneManagerReset reset = {}; TemporaryProjectRoot projectRoot = {}; @@ -522,9 +560,9 @@ TEST(SceneViewportRuntimeTests, RightMouseDragRotatesSceneCameraThroughViewportC EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); + SceneViewportSession session = {}; - const EditorSceneCameraSnapshot before = - runtime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot before = session.BuildCameraSnapshot(); ASSERT_TRUE(before.valid); SceneViewportController controller = {}; @@ -547,6 +585,7 @@ TEST(SceneViewportRuntimeTests, RightMouseDragRotatesSceneCameraThroughViewportC }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(pressFrame, inputRect, viewportSize)); @@ -563,12 +602,12 @@ TEST(SceneViewportRuntimeTests, RightMouseDragRotatesSceneCameraThroughViewportC }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(dragFrame, inputRect, viewportSize)); - const EditorSceneCameraSnapshot after = - runtime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot after = session.BuildCameraSnapshot(); ASSERT_TRUE(after.valid); EXPECT_NE(before.forward.x, after.forward.x); EXPECT_NE(before.forward.y, after.forward.y); @@ -583,19 +622,18 @@ TEST(SceneViewportRuntimeTests, MoveRightInputMovesSceneCameraTowardPositiveCame EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); + SceneViewportSession session = {}; - const EditorSceneCameraSnapshot before = - runtime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot before = session.BuildCameraSnapshot(); ASSERT_TRUE(before.valid); SceneViewportCameraInputState input = {}; input.viewportHeight = 720.0f; input.deltaTime = 1.0f; input.moveRight = 1.0f; - runtime.ApplySceneViewportCameraInput(input); + session.ApplyCameraInput(input); - const EditorSceneCameraSnapshot after = - runtime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot after = session.BuildCameraSnapshot(); ASSERT_TRUE(after.valid); EXPECT_GT(after.position.x, before.position.x); } @@ -608,9 +646,9 @@ TEST(SceneViewportRuntimeTests, MiddleMouseDragPansSceneCameraWithGrabSemantics) EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); + SceneViewportSession session = {}; - const EditorSceneCameraSnapshot before = - runtime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot before = session.BuildCameraSnapshot(); ASSERT_TRUE(before.valid); SceneViewportController controller = {}; @@ -633,6 +671,7 @@ TEST(SceneViewportRuntimeTests, MiddleMouseDragPansSceneCameraWithGrabSemantics) }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(pressFrame, inputRect, viewportSize)); @@ -649,12 +688,12 @@ TEST(SceneViewportRuntimeTests, MiddleMouseDragPansSceneCameraWithGrabSemantics) }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(dragFrame, inputRect, viewportSize)); - const EditorSceneCameraSnapshot after = - runtime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot after = session.BuildCameraSnapshot(); ASSERT_TRUE(after.valid); EXPECT_LT(after.position.x, before.position.x); } @@ -667,10 +706,10 @@ TEST(SceneViewportRuntimeTests, ViewToolLeftMouseDragPansSceneCameraWithGrabSema EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); - runtime.SetToolMode(SceneToolMode::View); + SceneViewportSession session = {}; + session.SetToolMode(SceneToolMode::View); - const EditorSceneCameraSnapshot before = - runtime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot before = session.BuildCameraSnapshot(); ASSERT_TRUE(before.valid); SceneViewportController controller = {}; @@ -693,6 +732,7 @@ TEST(SceneViewportRuntimeTests, ViewToolLeftMouseDragPansSceneCameraWithGrabSema }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(pressFrame, inputRect, viewportSize)); @@ -709,12 +749,12 @@ TEST(SceneViewportRuntimeTests, ViewToolLeftMouseDragPansSceneCameraWithGrabSema }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(dragFrame, inputRect, viewportSize)); - const EditorSceneCameraSnapshot after = - runtime.BuildSceneViewCameraSnapshot(); + const EditorSceneCameraSnapshot after = session.BuildCameraSnapshot(); ASSERT_TRUE(after.valid); EXPECT_LT(after.position.x, before.position.x); } @@ -727,8 +767,9 @@ TEST(SceneViewportRuntimeTests, MouseWheelUsesSingleNotchNormalizationForSceneZo EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); + SceneViewportSession session = {}; const float beforeDistance = - runtime.BuildSceneViewportRenderRequest().camera.orbitDistance; + session.BuildRenderRequest(runtime).camera.orbitDistance; SceneViewportController controller = {}; SceneViewportRenderService sceneViewportRenderService = {}; @@ -744,6 +785,7 @@ TEST(SceneViewportRuntimeTests, MouseWheelUsesSingleNotchNormalizationForSceneZo }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(hoverFrame, inputRect, viewportSize)); @@ -756,12 +798,13 @@ TEST(SceneViewportRuntimeTests, MouseWheelUsesSingleNotchNormalizationForSceneZo }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(wheelFrame, inputRect, viewportSize)); const float afterDistance = - runtime.BuildSceneViewportRenderRequest().camera.orbitDistance; + session.BuildRenderRequest(runtime).camera.orbitDistance; EXPECT_LT(afterDistance, beforeDistance); EXPECT_GT(afterDistance, 4.0f); } @@ -774,8 +817,9 @@ TEST(SceneViewportRuntimeTests, ToolShortcutSwitchesFocusedSceneViewportIntoTran EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); - runtime.SetToolMode(SceneToolMode::View); - EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::View); + SceneViewportSession session = {}; + session.SetToolMode(SceneToolMode::View); + EXPECT_EQ(session.GetToolMode(), SceneToolMode::View); SceneViewportController controller = {}; SceneViewportRenderService sceneViewportRenderService = {}; @@ -802,6 +846,7 @@ TEST(SceneViewportRuntimeTests, ToolShortcutSwitchesFocusedSceneViewportIntoTran }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(focusFrame, inputRect, viewportSize)); @@ -818,11 +863,12 @@ TEST(SceneViewportRuntimeTests, ToolShortcutSwitchesFocusedSceneViewportIntoTran }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(shortcutFrame, inputRect, viewportSize)); - EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::Translate); + EXPECT_EQ(session.GetToolMode(), SceneToolMode::Translate); } TEST(SceneViewportRuntimeTests, SceneToolOverlayClickSwitchesModeOnPointerDown) { @@ -833,7 +879,8 @@ TEST(SceneViewportRuntimeTests, SceneToolOverlayClickSwitchesModeOnPointerDown) EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); - runtime.SetToolMode(SceneToolMode::Translate); + SceneViewportSession session = {}; + session.SetToolMode(SceneToolMode::Translate); SceneViewportController controller = {}; SceneViewportRenderService sceneViewportRenderService = {}; @@ -848,7 +895,10 @@ TEST(SceneViewportRuntimeTests, SceneToolOverlayClickSwitchesModeOnPointerDown) inputBridgeState, inputRect, { - MakePointerEvent(UIInputEventType::PointerMove, rotateButtonCenter.x, rotateButtonCenter.y), + MakePointerEvent( + UIInputEventType::PointerMove, + rotateButtonCenter.x, + rotateButtonCenter.y), MakePointerEvent( UIInputEventType::PointerButtonDown, rotateButtonCenter.x, @@ -857,11 +907,12 @@ TEST(SceneViewportRuntimeTests, SceneToolOverlayClickSwitchesModeOnPointerDown) }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(frame, inputRect, viewportSize)); - EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::Rotate); + EXPECT_EQ(session.GetToolMode(), SceneToolMode::Rotate); } TEST(SceneViewportRuntimeTests, SceneToolOverlayIncludesTransformButtonAndSwitchesModeOnPointerDown) { @@ -872,7 +923,8 @@ TEST(SceneViewportRuntimeTests, SceneToolOverlayIncludesTransformButtonAndSwitch EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); - runtime.SetToolMode(SceneToolMode::Translate); + SceneViewportSession session = {}; + session.SetToolMode(SceneToolMode::Translate); SceneViewportController controller = {}; SceneViewportRenderService sceneViewportRenderService = {}; @@ -899,11 +951,12 @@ TEST(SceneViewportRuntimeTests, SceneToolOverlayIncludesTransformButtonAndSwitch }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(frame, inputRect, viewportSize)); - EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::Transform); + EXPECT_EQ(session.GetToolMode(), SceneToolMode::Transform); } TEST(SceneViewportRuntimeTests, SceneToolOverlayHandlesCoalescedClickInSingleFrame) { @@ -914,7 +967,8 @@ TEST(SceneViewportRuntimeTests, SceneToolOverlayHandlesCoalescedClickInSingleFra EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); - runtime.SetToolMode(SceneToolMode::Translate); + SceneViewportSession session = {}; + session.SetToolMode(SceneToolMode::Translate); SceneViewportController controller = {}; SceneViewportRenderService sceneViewportRenderService = {}; @@ -946,11 +1000,12 @@ TEST(SceneViewportRuntimeTests, SceneToolOverlayHandlesCoalescedClickInSingleFra }); controller.Update( runtime, + session, sceneViewportRenderService, BuildSceneComposeState(inputBridgeState), BuildSceneComposeFrame(frame, inputRect, viewportSize)); - EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::Rotate); + EXPECT_EQ(session.GetToolMode(), SceneToolMode::Rotate); } TEST(SceneViewportRuntimeTests, TranslateGizmoDragAppliesPreviewToSelectedObject) { @@ -961,7 +1016,8 @@ TEST(SceneViewportRuntimeTests, TranslateGizmoDragAppliesPreviewToSelectedObject EditorSceneRuntime runtime = {}; BindEngineSceneBackend(runtime); ASSERT_TRUE(runtime.Initialize(projectRoot.Root())); - runtime.SetToolMode(SceneToolMode::Translate); + SceneViewportSession session = {}; + session.SetToolMode(SceneToolMode::Translate); runtime.EnsureSceneSelection(); Scene* scene = GetLoadedActiveScene(); @@ -974,7 +1030,7 @@ TEST(SceneViewportRuntimeTests, TranslateGizmoDragAppliesPreviewToSelectedObject SceneViewportTransformGizmo gizmo = {}; const UIRect viewportRect(0.0f, 0.0f, 640.0f, 360.0f); const std::optional hoverPoint = - FindHoveredTransformGizmoPoint(gizmo, runtime, viewportRect); + FindHoveredTransformGizmoPoint(gizmo, runtime, session, viewportRect); ASSERT_TRUE(hoverPoint.has_value()); const std::array dragTargets = { @@ -989,11 +1045,11 @@ TEST(SceneViewportRuntimeTests, TranslateGizmoDragAppliesPreviewToSelectedObject bool previewApplied = false; for (const UIPoint& dragPoint : dragTargets) { transform->SetPosition(Math::Vector3(0.0f, 0.0f, 0.0f)); - gizmo.Refresh(runtime, viewportRect, hoverPoint.value(), true); + gizmo.Refresh(runtime, session, viewportRect, hoverPoint.value(), true); ASSERT_TRUE(gizmo.IsHoveringHandle()); - ASSERT_TRUE(gizmo.TryBeginDrag(runtime)); + ASSERT_TRUE(gizmo.TryBeginDrag(runtime, session)); - gizmo.Refresh(runtime, viewportRect, dragPoint, true); + gizmo.Refresh(runtime, session, viewportRect, dragPoint, true); ASSERT_TRUE(gizmo.UpdateDrag(runtime)); const Math::Vector3 previewPosition = transform->GetPosition();