Formalize staged scene viewport gizmo frame API
This commit is contained in:
@@ -1,5 +1,22 @@
|
|||||||
# SceneViewport Overlay/Gizmo Rework Checkpoint
|
# SceneViewport Overlay/Gizmo Rework Checkpoint
|
||||||
|
|
||||||
|
## Update 2026-04-04 Phase 5D
|
||||||
|
|
||||||
|
### Stage-Oriented Gizmo Frame API Completed
|
||||||
|
|
||||||
|
- Extended `SceneViewportTransformGizmoCoordinator.{h,cpp}` with formal frame options, refresh requests, frame updates, and refresh-and-submit helpers.
|
||||||
|
- `SceneViewPanel` no longer directly assembles the large `RefreshSceneViewportTransformGizmos(...)` argument list for interaction and draw stages.
|
||||||
|
- The required dual-stage gizmo timing remains intact, but the stage orchestration now flows through `BuildSceneViewportTransformGizmoRefreshRequest(...)` and `RefreshAndSubmitSceneViewportTransformGizmoFrame(...)`.
|
||||||
|
- Added coordinator tests covering frame option/request construction and refresh-to-submit flow.
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
- `cmake --build build --config Debug --target editor_tests -- /p:BuildProjectReferences=false`
|
||||||
|
- `build/tests/Editor/Debug/editor_tests.exe --gtest_filter=SceneViewportInteractionActionsTest.*:SceneViewportInteractionResolverTest.*:SceneViewportTransformGizmoCoordinatorTest.*:SceneViewportOverlayRenderer_Test.*:SceneViewportOverlayProviderRegistryTest.*:ViewportRenderFlowUtilsTest.*`
|
||||||
|
- `cmake --build build --config Debug --target XCEditor`
|
||||||
|
|
||||||
|
All commands completed successfully in `Debug`.
|
||||||
|
|
||||||
## Update 2026-04-04 Phase 5C
|
## Update 2026-04-04 Phase 5C
|
||||||
|
|
||||||
### Transform Gizmo Coordinator Completed
|
### Transform Gizmo Coordinator Completed
|
||||||
|
|||||||
@@ -5,6 +5,96 @@
|
|||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
|
SceneViewportTransformGizmoFrameOptions BuildSceneViewportTransformGizmoFrameOptions(
|
||||||
|
bool useCenterPivot,
|
||||||
|
bool localSpace,
|
||||||
|
bool usingTransformTool,
|
||||||
|
bool showingMoveGizmo,
|
||||||
|
bool showingRotateGizmo,
|
||||||
|
bool showingScaleGizmo) {
|
||||||
|
SceneViewportTransformGizmoFrameOptions options = {};
|
||||||
|
options.useCenterPivot = useCenterPivot;
|
||||||
|
options.localSpace = localSpace;
|
||||||
|
options.usingTransformTool = usingTransformTool;
|
||||||
|
options.showingMoveGizmo = showingMoveGizmo;
|
||||||
|
options.showingRotateGizmo = showingRotateGizmo;
|
||||||
|
options.showingScaleGizmo = showingScaleGizmo;
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneViewportTransformGizmoRefreshRequest BuildSceneViewportTransformGizmoRefreshRequest(
|
||||||
|
IEditorContext& context,
|
||||||
|
const SceneViewportOverlayData& overlay,
|
||||||
|
const Math::Vector2& viewportSize,
|
||||||
|
const Math::Vector2& mousePosition,
|
||||||
|
const SceneViewportTransformGizmoFrameOptions& options) {
|
||||||
|
SceneViewportTransformGizmoRefreshRequest request = {};
|
||||||
|
request.context = &context;
|
||||||
|
request.overlay = overlay;
|
||||||
|
request.viewportSize = viewportSize;
|
||||||
|
request.mousePosition = mousePosition;
|
||||||
|
request.options = options;
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CancelSceneViewportTransformGizmoFrame(
|
||||||
|
IEditorContext& context,
|
||||||
|
SceneViewportMoveGizmo& moveGizmo,
|
||||||
|
SceneViewportRotateGizmo& rotateGizmo,
|
||||||
|
SceneViewportScaleGizmo& scaleGizmo) {
|
||||||
|
CancelSceneViewportTransformGizmoDrags(context, moveGizmo, rotateGizmo, scaleGizmo);
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneViewportTransformGizmoFrameUpdate RefreshSceneViewportTransformGizmoFrame(
|
||||||
|
const SceneViewportTransformGizmoRefreshRequest& request,
|
||||||
|
SceneViewportMoveGizmo& moveGizmo,
|
||||||
|
SceneViewportRotateGizmo& rotateGizmo,
|
||||||
|
SceneViewportScaleGizmo& scaleGizmo) {
|
||||||
|
SceneViewportTransformGizmoFrameUpdate update = {};
|
||||||
|
if (!request.IsValid()) {
|
||||||
|
return update;
|
||||||
|
}
|
||||||
|
|
||||||
|
update.frameState = RefreshSceneViewportTransformGizmos(
|
||||||
|
*request.context,
|
||||||
|
request.overlay,
|
||||||
|
request.viewportSize,
|
||||||
|
request.mousePosition,
|
||||||
|
request.options.useCenterPivot,
|
||||||
|
request.options.localSpace,
|
||||||
|
request.options.usingTransformTool,
|
||||||
|
request.options.showingMoveGizmo,
|
||||||
|
moveGizmo,
|
||||||
|
request.options.showingRotateGizmo,
|
||||||
|
rotateGizmo,
|
||||||
|
request.options.showingScaleGizmo,
|
||||||
|
scaleGizmo);
|
||||||
|
update.overlaySubmission = BuildSceneViewportTransformGizmoOverlaySubmission(
|
||||||
|
update.frameState,
|
||||||
|
request.options.showingMoveGizmo,
|
||||||
|
moveGizmo,
|
||||||
|
request.options.showingRotateGizmo,
|
||||||
|
rotateGizmo,
|
||||||
|
request.options.showingScaleGizmo,
|
||||||
|
scaleGizmo);
|
||||||
|
return update;
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneViewportTransformGizmoFrameUpdate RefreshAndSubmitSceneViewportTransformGizmoFrame(
|
||||||
|
IViewportHostService& viewportHostService,
|
||||||
|
const SceneViewportTransformGizmoRefreshRequest& request,
|
||||||
|
SceneViewportMoveGizmo& moveGizmo,
|
||||||
|
SceneViewportRotateGizmo& rotateGizmo,
|
||||||
|
SceneViewportScaleGizmo& scaleGizmo) {
|
||||||
|
SceneViewportTransformGizmoFrameUpdate update = RefreshSceneViewportTransformGizmoFrame(
|
||||||
|
request,
|
||||||
|
moveGizmo,
|
||||||
|
rotateGizmo,
|
||||||
|
scaleGizmo);
|
||||||
|
SubmitSceneViewportTransformGizmoOverlaySubmission(viewportHostService, update.overlaySubmission);
|
||||||
|
return update;
|
||||||
|
}
|
||||||
|
|
||||||
SceneViewportTransformGizmoOverlaySubmission BuildSceneViewportTransformGizmoOverlaySubmission(
|
SceneViewportTransformGizmoOverlaySubmission BuildSceneViewportTransformGizmoOverlaySubmission(
|
||||||
const SceneViewportTransformGizmoFrameState& frameState,
|
const SceneViewportTransformGizmoFrameState& frameState,
|
||||||
bool showingMoveGizmo,
|
bool showingMoveGizmo,
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Core/IEditorContext.h"
|
||||||
#include "SceneViewportInteractionActions.h"
|
#include "SceneViewportInteractionActions.h"
|
||||||
#include "SceneViewportOverlayHandleBuilder.h"
|
#include "SceneViewportOverlayHandleBuilder.h"
|
||||||
|
#include "SceneViewportTransformGizmoFrameBuilder.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -10,6 +12,27 @@ namespace Editor {
|
|||||||
|
|
||||||
class IUndoManager;
|
class IUndoManager;
|
||||||
|
|
||||||
|
struct SceneViewportTransformGizmoFrameOptions {
|
||||||
|
bool useCenterPivot = false;
|
||||||
|
bool localSpace = false;
|
||||||
|
bool usingTransformTool = false;
|
||||||
|
bool showingMoveGizmo = false;
|
||||||
|
bool showingRotateGizmo = false;
|
||||||
|
bool showingScaleGizmo = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SceneViewportTransformGizmoRefreshRequest {
|
||||||
|
IEditorContext* context = nullptr;
|
||||||
|
SceneViewportOverlayData overlay = {};
|
||||||
|
Math::Vector2 viewportSize = Math::Vector2::Zero();
|
||||||
|
Math::Vector2 mousePosition = Math::Vector2::Zero();
|
||||||
|
SceneViewportTransformGizmoFrameOptions options = {};
|
||||||
|
|
||||||
|
bool IsValid() const {
|
||||||
|
return context != nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct SceneViewportTransformGizmoOverlaySubmission {
|
struct SceneViewportTransformGizmoOverlaySubmission {
|
||||||
SceneViewportTransformGizmoOverlayState overlayState = {};
|
SceneViewportTransformGizmoOverlayState overlayState = {};
|
||||||
SceneViewportActiveGizmoKind activeGizmoKind = SceneViewportActiveGizmoKind::None;
|
SceneViewportActiveGizmoKind activeGizmoKind = SceneViewportActiveGizmoKind::None;
|
||||||
@@ -36,6 +59,45 @@ struct SceneViewportTransformGizmoLifecycleCommand {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SceneViewportTransformGizmoFrameUpdate {
|
||||||
|
SceneViewportTransformGizmoFrameState frameState = {};
|
||||||
|
SceneViewportTransformGizmoOverlaySubmission overlaySubmission = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
SceneViewportTransformGizmoFrameOptions BuildSceneViewportTransformGizmoFrameOptions(
|
||||||
|
bool useCenterPivot,
|
||||||
|
bool localSpace,
|
||||||
|
bool usingTransformTool,
|
||||||
|
bool showingMoveGizmo,
|
||||||
|
bool showingRotateGizmo,
|
||||||
|
bool showingScaleGizmo);
|
||||||
|
|
||||||
|
SceneViewportTransformGizmoRefreshRequest BuildSceneViewportTransformGizmoRefreshRequest(
|
||||||
|
IEditorContext& context,
|
||||||
|
const SceneViewportOverlayData& overlay,
|
||||||
|
const Math::Vector2& viewportSize,
|
||||||
|
const Math::Vector2& mousePosition,
|
||||||
|
const SceneViewportTransformGizmoFrameOptions& options);
|
||||||
|
|
||||||
|
void CancelSceneViewportTransformGizmoFrame(
|
||||||
|
IEditorContext& context,
|
||||||
|
SceneViewportMoveGizmo& moveGizmo,
|
||||||
|
SceneViewportRotateGizmo& rotateGizmo,
|
||||||
|
SceneViewportScaleGizmo& scaleGizmo);
|
||||||
|
|
||||||
|
SceneViewportTransformGizmoFrameUpdate RefreshSceneViewportTransformGizmoFrame(
|
||||||
|
const SceneViewportTransformGizmoRefreshRequest& request,
|
||||||
|
SceneViewportMoveGizmo& moveGizmo,
|
||||||
|
SceneViewportRotateGizmo& rotateGizmo,
|
||||||
|
SceneViewportScaleGizmo& scaleGizmo);
|
||||||
|
|
||||||
|
SceneViewportTransformGizmoFrameUpdate RefreshAndSubmitSceneViewportTransformGizmoFrame(
|
||||||
|
IViewportHostService& viewportHostService,
|
||||||
|
const SceneViewportTransformGizmoRefreshRequest& request,
|
||||||
|
SceneViewportMoveGizmo& moveGizmo,
|
||||||
|
SceneViewportRotateGizmo& rotateGizmo,
|
||||||
|
SceneViewportScaleGizmo& scaleGizmo);
|
||||||
|
|
||||||
SceneViewportTransformGizmoOverlaySubmission BuildSceneViewportTransformGizmoOverlaySubmission(
|
SceneViewportTransformGizmoOverlaySubmission BuildSceneViewportTransformGizmoOverlaySubmission(
|
||||||
const SceneViewportTransformGizmoFrameState& frameState,
|
const SceneViewportTransformGizmoFrameState& frameState,
|
||||||
bool showingMoveGizmo,
|
bool showingMoveGizmo,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include "Viewport/SceneViewportInteractionResolver.h"
|
#include "Viewport/SceneViewportInteractionResolver.h"
|
||||||
#include "Viewport/SceneViewportMath.h"
|
#include "Viewport/SceneViewportMath.h"
|
||||||
#include "Viewport/SceneViewportTransformGizmoCoordinator.h"
|
#include "Viewport/SceneViewportTransformGizmoCoordinator.h"
|
||||||
#include "Viewport/SceneViewportTransformGizmoFrameBuilder.h"
|
|
||||||
#include "ViewportPanelContent.h"
|
#include "ViewportPanelContent.h"
|
||||||
#include "Platform/Win32Utf8.h"
|
#include "Platform/Win32Utf8.h"
|
||||||
#include "UI/UI.h"
|
#include "UI/UI.h"
|
||||||
@@ -339,46 +338,49 @@ void SceneViewPanel::Render() {
|
|||||||
const bool showingScaleGizmo = m_toolMode == SceneViewportToolMode::Scale || usingTransformTool;
|
const bool showingScaleGizmo = m_toolMode == SceneViewportToolMode::Scale || usingTransformTool;
|
||||||
const bool useCenterPivot = m_pivotMode == SceneViewportPivotMode::Center;
|
const bool useCenterPivot = m_pivotMode == SceneViewportPivotMode::Center;
|
||||||
const bool localSpace = m_transformSpaceMode == SceneViewportTransformSpaceMode::Local;
|
const bool localSpace = m_transformSpaceMode == SceneViewportTransformSpaceMode::Local;
|
||||||
|
const SceneViewportTransformGizmoFrameOptions gizmoFrameOptions =
|
||||||
|
BuildSceneViewportTransformGizmoFrameOptions(
|
||||||
|
useCenterPivot,
|
||||||
|
localSpace,
|
||||||
|
usingTransformTool,
|
||||||
|
showingMoveGizmo,
|
||||||
|
showingRotateGizmo,
|
||||||
|
showingScaleGizmo);
|
||||||
const Math::Vector2 viewportSize(content.availableSize.x, content.availableSize.y);
|
const Math::Vector2 viewportSize(content.availableSize.x, content.availableSize.y);
|
||||||
const Math::Vector2 localMousePosition(
|
const Math::Vector2 localMousePosition(
|
||||||
io.MousePos.x - content.itemMin.x,
|
io.MousePos.x - content.itemMin.x,
|
||||||
io.MousePos.y - content.itemMin.y);
|
io.MousePos.y - content.itemMin.y);
|
||||||
SceneViewportOverlayData overlay = {};
|
SceneViewportOverlayData overlay = {};
|
||||||
|
SceneViewportTransformGizmoFrameUpdate interactionGizmoFrame = {};
|
||||||
SceneViewportTransformGizmoFrameState gizmoFrameState = {};
|
SceneViewportTransformGizmoFrameState gizmoFrameState = {};
|
||||||
SceneViewportOverlayFrameData emptySceneOverlayFrameData = {};
|
SceneViewportOverlayFrameData emptySceneOverlayFrameData = {};
|
||||||
|
|
||||||
if (hasInteractiveViewport) {
|
if (hasInteractiveViewport) {
|
||||||
overlay = viewportHostService->GetSceneViewOverlayData();
|
overlay = viewportHostService->GetSceneViewOverlayData();
|
||||||
gizmoFrameState = RefreshSceneViewportTransformGizmos(
|
interactionGizmoFrame = RefreshAndSubmitSceneViewportTransformGizmoFrame(
|
||||||
*m_context,
|
*viewportHostService,
|
||||||
overlay,
|
BuildSceneViewportTransformGizmoRefreshRequest(
|
||||||
viewportSize,
|
*m_context,
|
||||||
localMousePosition,
|
overlay,
|
||||||
useCenterPivot,
|
viewportSize,
|
||||||
localSpace,
|
localMousePosition,
|
||||||
usingTransformTool,
|
gizmoFrameOptions),
|
||||||
showingMoveGizmo,
|
|
||||||
m_moveGizmo,
|
m_moveGizmo,
|
||||||
showingRotateGizmo,
|
|
||||||
m_rotateGizmo,
|
m_rotateGizmo,
|
||||||
showingScaleGizmo,
|
|
||||||
m_scaleGizmo);
|
m_scaleGizmo);
|
||||||
|
gizmoFrameState = interactionGizmoFrame.frameState;
|
||||||
} else {
|
} else {
|
||||||
CancelSceneViewportTransformGizmoDrags(*m_context, m_moveGizmo, m_rotateGizmo, m_scaleGizmo);
|
CancelSceneViewportTransformGizmoFrame(
|
||||||
|
*m_context,
|
||||||
|
m_moveGizmo,
|
||||||
|
m_rotateGizmo,
|
||||||
|
m_scaleGizmo);
|
||||||
}
|
}
|
||||||
|
|
||||||
const SceneViewportTransformGizmoOverlaySubmission interactionGizmoSubmission =
|
const SceneViewportTransformGizmoOverlaySubmission interactionGizmoSubmission =
|
||||||
hasInteractiveViewport
|
hasInteractiveViewport
|
||||||
? BuildSceneViewportTransformGizmoOverlaySubmission(
|
? interactionGizmoFrame.overlaySubmission
|
||||||
gizmoFrameState,
|
|
||||||
showingMoveGizmo,
|
|
||||||
m_moveGizmo,
|
|
||||||
showingRotateGizmo,
|
|
||||||
m_rotateGizmo,
|
|
||||||
showingScaleGizmo,
|
|
||||||
m_scaleGizmo)
|
|
||||||
: SceneViewportTransformGizmoOverlaySubmission{};
|
: SceneViewportTransformGizmoOverlaySubmission{};
|
||||||
SubmitSceneViewportTransformGizmoOverlaySubmission(*viewportHostService, interactionGizmoSubmission);
|
|
||||||
const SceneViewportOverlayFrameData& interactionOverlayFrameData =
|
const SceneViewportOverlayFrameData& interactionOverlayFrameData =
|
||||||
hasInteractiveViewport
|
hasInteractiveViewport
|
||||||
? viewportHostService->GetSceneViewEditorOverlayFrameData(*m_context)
|
? viewportHostService->GetSceneViewEditorOverlayFrameData(*m_context)
|
||||||
@@ -559,32 +561,17 @@ void SceneViewPanel::Render() {
|
|||||||
|
|
||||||
if (content.hasViewportArea && content.frame.hasTexture) {
|
if (content.hasViewportArea && content.frame.hasTexture) {
|
||||||
overlay = viewportHostService->GetSceneViewOverlayData();
|
overlay = viewportHostService->GetSceneViewOverlayData();
|
||||||
const SceneViewportTransformGizmoFrameState drawGizmoFrameState =
|
RefreshAndSubmitSceneViewportTransformGizmoFrame(
|
||||||
RefreshSceneViewportTransformGizmos(
|
*viewportHostService,
|
||||||
|
BuildSceneViewportTransformGizmoRefreshRequest(
|
||||||
*m_context,
|
*m_context,
|
||||||
overlay,
|
overlay,
|
||||||
viewportSize,
|
viewportSize,
|
||||||
localMousePosition,
|
localMousePosition,
|
||||||
useCenterPivot,
|
gizmoFrameOptions),
|
||||||
localSpace,
|
m_moveGizmo,
|
||||||
usingTransformTool,
|
m_rotateGizmo,
|
||||||
showingMoveGizmo,
|
m_scaleGizmo);
|
||||||
m_moveGizmo,
|
|
||||||
showingRotateGizmo,
|
|
||||||
m_rotateGizmo,
|
|
||||||
showingScaleGizmo,
|
|
||||||
m_scaleGizmo);
|
|
||||||
|
|
||||||
SubmitSceneViewportTransformGizmoOverlaySubmission(
|
|
||||||
*viewportHostService,
|
|
||||||
BuildSceneViewportTransformGizmoOverlaySubmission(
|
|
||||||
drawGizmoFrameState,
|
|
||||||
showingMoveGizmo,
|
|
||||||
m_moveGizmo,
|
|
||||||
showingRotateGizmo,
|
|
||||||
m_rotateGizmo,
|
|
||||||
showingScaleGizmo,
|
|
||||||
m_scaleGizmo));
|
|
||||||
|
|
||||||
DrawSceneViewportHudOverlay(
|
DrawSceneViewportHudOverlay(
|
||||||
ImGui::GetWindowDrawList(),
|
ImGui::GetWindowDrawList(),
|
||||||
|
|||||||
@@ -1,21 +1,40 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "Core/EventBus.h"
|
||||||
|
#include "Core/IEditorContext.h"
|
||||||
|
#include "Core/IProjectManager.h"
|
||||||
|
#include "Core/ISceneManager.h"
|
||||||
|
#include "Core/ISelectionManager.h"
|
||||||
#include "Core/IUndoManager.h"
|
#include "Core/IUndoManager.h"
|
||||||
#include "Viewport/IViewportHostService.h"
|
#include "Viewport/IViewportHostService.h"
|
||||||
#include "Viewport/SceneViewportTransformGizmoCoordinator.h"
|
#include "Viewport/SceneViewportTransformGizmoCoordinator.h"
|
||||||
|
|
||||||
#include <XCEngine/Components/GameObject.h>
|
#include <XCEngine/Components/GameObject.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using XCEngine::Editor::BuildBeginSceneViewportTransformGizmoLifecycleCommand;
|
using XCEngine::Editor::BuildBeginSceneViewportTransformGizmoLifecycleCommand;
|
||||||
using XCEngine::Editor::BuildFrameSceneViewportTransformGizmoLifecycleCommand;
|
using XCEngine::Editor::BuildFrameSceneViewportTransformGizmoLifecycleCommand;
|
||||||
|
using XCEngine::Editor::BuildSceneViewportTransformGizmoFrameOptions;
|
||||||
|
using XCEngine::Editor::BuildSceneViewportTransformGizmoRefreshRequest;
|
||||||
using XCEngine::Editor::BuildSceneViewportTransformGizmoOverlaySubmission;
|
using XCEngine::Editor::BuildSceneViewportTransformGizmoOverlaySubmission;
|
||||||
|
using XCEngine::Editor::CancelSceneViewportTransformGizmoFrame;
|
||||||
using XCEngine::Editor::EditorViewportFrame;
|
using XCEngine::Editor::EditorViewportFrame;
|
||||||
using XCEngine::Editor::EditorViewportKind;
|
using XCEngine::Editor::EditorViewportKind;
|
||||||
|
using XCEngine::Editor::EditorActionRoute;
|
||||||
|
using XCEngine::Editor::EditorRuntimeMode;
|
||||||
|
using XCEngine::Editor::EventBus;
|
||||||
using XCEngine::Editor::IEditorContext;
|
using XCEngine::Editor::IEditorContext;
|
||||||
|
using XCEngine::Editor::IProjectManager;
|
||||||
|
using XCEngine::Editor::ISceneManager;
|
||||||
|
using XCEngine::Editor::ISelectionManager;
|
||||||
using XCEngine::Editor::IViewportHostService;
|
using XCEngine::Editor::IViewportHostService;
|
||||||
|
using XCEngine::Editor::AssetItemPtr;
|
||||||
using XCEngine::Editor::SceneViewportActiveGizmoKind;
|
using XCEngine::Editor::SceneViewportActiveGizmoKind;
|
||||||
|
using XCEngine::Editor::SceneViewportTransformGizmoFrameOptions;
|
||||||
|
using XCEngine::Editor::SceneViewportTransformGizmoFrameUpdate;
|
||||||
using XCEngine::Editor::SceneViewportInput;
|
using XCEngine::Editor::SceneViewportInput;
|
||||||
using XCEngine::Editor::SceneViewportInteractionActions;
|
using XCEngine::Editor::SceneViewportInteractionActions;
|
||||||
using XCEngine::Editor::SceneViewportOrientationAxis;
|
using XCEngine::Editor::SceneViewportOrientationAxis;
|
||||||
@@ -24,8 +43,185 @@ using XCEngine::Editor::SceneViewportOverlayFrameData;
|
|||||||
using XCEngine::Editor::SceneViewportTransformGizmoFrameState;
|
using XCEngine::Editor::SceneViewportTransformGizmoFrameState;
|
||||||
using XCEngine::Editor::SceneViewportTransformGizmoLifecycleStage;
|
using XCEngine::Editor::SceneViewportTransformGizmoLifecycleStage;
|
||||||
using XCEngine::Editor::SceneViewportTransformGizmoOverlayState;
|
using XCEngine::Editor::SceneViewportTransformGizmoOverlayState;
|
||||||
|
using XCEngine::Editor::RefreshAndSubmitSceneViewportTransformGizmoFrame;
|
||||||
using XCEngine::Editor::SubmitSceneViewportTransformGizmoOverlaySubmission;
|
using XCEngine::Editor::SubmitSceneViewportTransformGizmoOverlaySubmission;
|
||||||
|
using XCEngine::Editor::SceneSnapshot;
|
||||||
using XCEngine::Rendering::RenderContext;
|
using XCEngine::Rendering::RenderContext;
|
||||||
|
using XCEngine::Math::Vector2;
|
||||||
|
|
||||||
|
class StubSelectionManager : public ISelectionManager {
|
||||||
|
public:
|
||||||
|
void SetSelectedEntity(uint64_t entityId) override {
|
||||||
|
selectedEntity = entityId;
|
||||||
|
selectedEntities = entityId == 0 ? std::vector<uint64_t>{} : std::vector<uint64_t>{entityId};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSelectedEntities(const std::vector<uint64_t>& entityIds) override {
|
||||||
|
selectedEntities = entityIds;
|
||||||
|
selectedEntity = entityIds.empty() ? 0 : entityIds.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddToSelection(uint64_t entityId) override {
|
||||||
|
selectedEntities.push_back(entityId);
|
||||||
|
selectedEntity = entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveFromSelection(uint64_t entityId) override {
|
||||||
|
selectedEntities.erase(
|
||||||
|
std::remove(selectedEntities.begin(), selectedEntities.end(), entityId),
|
||||||
|
selectedEntities.end());
|
||||||
|
if (selectedEntity == entityId) {
|
||||||
|
selectedEntity = selectedEntities.empty() ? 0 : selectedEntities.front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearSelection() override {
|
||||||
|
selectedEntity = 0;
|
||||||
|
selectedEntities.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetSelectedEntity() const override {
|
||||||
|
return selectedEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<uint64_t>& GetSelectedEntities() const override {
|
||||||
|
return selectedEntities;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasSelection() const override {
|
||||||
|
return !selectedEntities.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetSelectionCount() const override {
|
||||||
|
return selectedEntities.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSelected(uint64_t entityId) const override {
|
||||||
|
return std::find(selectedEntities.begin(), selectedEntities.end(), entityId) != selectedEntities.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t selectedEntity = 0;
|
||||||
|
std::vector<uint64_t> selectedEntities = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
class StubSceneManager : public ISceneManager {
|
||||||
|
public:
|
||||||
|
XCEngine::Components::GameObject* CreateEntity(
|
||||||
|
const std::string&,
|
||||||
|
XCEngine::Components::GameObject* = nullptr) override { return nullptr; }
|
||||||
|
void DeleteEntity(XCEngine::Components::GameObject::ID) override {}
|
||||||
|
void RenameEntity(XCEngine::Components::GameObject::ID, const std::string&) override {}
|
||||||
|
XCEngine::Components::GameObject* GetEntity(XCEngine::Components::GameObject::ID entityId) override {
|
||||||
|
return entity != nullptr && entity->GetID() == entityId ? entity : nullptr;
|
||||||
|
}
|
||||||
|
const std::vector<XCEngine::Components::GameObject*>& GetRootEntities() const override { return rootEntities; }
|
||||||
|
void CopyEntity(XCEngine::Components::GameObject::ID) override {}
|
||||||
|
XCEngine::Components::GameObject::ID PasteEntity(XCEngine::Components::GameObject::ID = 0) override { return 0; }
|
||||||
|
XCEngine::Components::GameObject::ID DuplicateEntity(XCEngine::Components::GameObject::ID) override { return 0; }
|
||||||
|
void MoveEntity(XCEngine::Components::GameObject::ID, XCEngine::Components::GameObject::ID) override {}
|
||||||
|
bool HasClipboardData() const override { return false; }
|
||||||
|
void NewScene(const std::string& = "Untitled Scene") override {}
|
||||||
|
bool LoadScene(const std::string&) override { return false; }
|
||||||
|
bool SaveScene() override { return false; }
|
||||||
|
bool SaveSceneAs(const std::string&) override { return false; }
|
||||||
|
bool LoadStartupScene(const std::string&) override { return false; }
|
||||||
|
bool HasActiveScene() const override { return false; }
|
||||||
|
bool IsSceneDirty() const override { return false; }
|
||||||
|
void MarkSceneDirty() override {}
|
||||||
|
void SetSceneDocumentDirtyTrackingEnabled(bool) override {}
|
||||||
|
bool IsSceneDocumentDirtyTrackingEnabled() const override { return false; }
|
||||||
|
const std::string& GetCurrentScenePath() const override { return empty; }
|
||||||
|
const std::string& GetCurrentSceneName() const override { return empty; }
|
||||||
|
XCEngine::Components::Scene* GetScene() override { return nullptr; }
|
||||||
|
const XCEngine::Components::Scene* GetScene() const override { return nullptr; }
|
||||||
|
SceneSnapshot CaptureSceneSnapshot() const override { return {}; }
|
||||||
|
bool RestoreSceneSnapshot(const SceneSnapshot&) override { return false; }
|
||||||
|
void CreateDemoScene() override {}
|
||||||
|
|
||||||
|
XCEngine::Components::GameObject* entity = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<XCEngine::Components::GameObject*> rootEntities = {};
|
||||||
|
std::string empty;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StubProjectManager : public IProjectManager {
|
||||||
|
public:
|
||||||
|
const std::vector<AssetItemPtr>& GetCurrentItems() const override { return items; }
|
||||||
|
AssetItemPtr GetRootFolder() const override { return {}; }
|
||||||
|
AssetItemPtr GetCurrentFolder() const override { return {}; }
|
||||||
|
AssetItemPtr GetSelectedItem() const override { return {}; }
|
||||||
|
const std::string& GetSelectedItemPath() const override { return empty; }
|
||||||
|
int GetSelectedIndex() const override { return -1; }
|
||||||
|
void SetSelectedIndex(int) override {}
|
||||||
|
void SetSelectedItem(const AssetItemPtr&) override {}
|
||||||
|
void ClearSelection() override {}
|
||||||
|
int FindCurrentItemIndex(const std::string&) const override { return -1; }
|
||||||
|
void NavigateToFolder(const AssetItemPtr&) override {}
|
||||||
|
void NavigateBack() override {}
|
||||||
|
void NavigateToIndex(size_t) override {}
|
||||||
|
bool CanNavigateBack() const override { return false; }
|
||||||
|
std::string GetCurrentPath() const override { return empty; }
|
||||||
|
size_t GetPathDepth() const override { return 0; }
|
||||||
|
std::string GetPathName(size_t) const override { return {}; }
|
||||||
|
void Initialize(const std::string&) override {}
|
||||||
|
void RefreshCurrentFolder() override {}
|
||||||
|
AssetItemPtr CreateFolder(const std::string&) override { return {}; }
|
||||||
|
bool DeleteItem(const std::string&) override { return false; }
|
||||||
|
bool MoveItem(const std::string&, const std::string&) override { return false; }
|
||||||
|
bool RenameItem(const std::string&, const std::string&) override { return false; }
|
||||||
|
const std::string& GetProjectPath() const override { return empty; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<AssetItemPtr> items = {};
|
||||||
|
std::string empty;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StubUndoManager : public XCEngine::Editor::IUndoManager {
|
||||||
|
public:
|
||||||
|
void ClearHistory() override {}
|
||||||
|
bool CanUndo() const override { return false; }
|
||||||
|
bool CanRedo() const override { return false; }
|
||||||
|
const std::string& GetUndoLabel() const override { return empty; }
|
||||||
|
const std::string& GetRedoLabel() const override { return empty; }
|
||||||
|
void Undo() override {}
|
||||||
|
void Redo() override {}
|
||||||
|
XCEngine::Editor::UndoStateSnapshot CaptureCurrentState() const override { return {}; }
|
||||||
|
void PushCommand(const std::string&, XCEngine::Editor::UndoStateSnapshot, XCEngine::Editor::UndoStateSnapshot) override {}
|
||||||
|
void BeginInteractiveChange(const std::string&) override {}
|
||||||
|
bool HasPendingInteractiveChange() const override { return false; }
|
||||||
|
void FinalizeInteractiveChange() override {}
|
||||||
|
void CancelInteractiveChange() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string empty;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StubEditorContext : public IEditorContext {
|
||||||
|
public:
|
||||||
|
EventBus& GetEventBus() override { return eventBus; }
|
||||||
|
ISelectionManager& GetSelectionManager() override { return selectionManager; }
|
||||||
|
ISceneManager& GetSceneManager() override { return sceneManager; }
|
||||||
|
IProjectManager& GetProjectManager() override { return projectManager; }
|
||||||
|
XCEngine::Editor::IUndoManager& GetUndoManager() override { return undoManager; }
|
||||||
|
IViewportHostService* GetViewportHostService() override { return viewportHostService; }
|
||||||
|
void SetActiveActionRoute(EditorActionRoute route) override { activeRoute = route; }
|
||||||
|
EditorActionRoute GetActiveActionRoute() const override { return activeRoute; }
|
||||||
|
void SetRuntimeMode(EditorRuntimeMode mode) override { runtimeMode = mode; }
|
||||||
|
EditorRuntimeMode GetRuntimeMode() const override { return runtimeMode; }
|
||||||
|
void SetProjectPath(const std::string& path) override { projectPath = path; }
|
||||||
|
const std::string& GetProjectPath() const override { return projectPath; }
|
||||||
|
|
||||||
|
EventBus eventBus = {};
|
||||||
|
StubSelectionManager selectionManager = {};
|
||||||
|
StubSceneManager sceneManager = {};
|
||||||
|
StubProjectManager projectManager = {};
|
||||||
|
StubUndoManager undoManager = {};
|
||||||
|
IViewportHostService* viewportHostService = nullptr;
|
||||||
|
EditorActionRoute activeRoute = EditorActionRoute::None;
|
||||||
|
EditorRuntimeMode runtimeMode = EditorRuntimeMode::Edit;
|
||||||
|
std::string projectPath;
|
||||||
|
};
|
||||||
|
|
||||||
class StubViewportHostService : public IViewportHostService {
|
class StubViewportHostService : public IViewportHostService {
|
||||||
public:
|
public:
|
||||||
@@ -77,6 +273,55 @@ TEST(SceneViewportTransformGizmoCoordinatorTest, BuildFrameCommandMapsUpdateAndE
|
|||||||
EXPECT_EQ(endCommand.gizmoKind, SceneViewportActiveGizmoKind::Scale);
|
EXPECT_EQ(endCommand.gizmoKind, SceneViewportActiveGizmoKind::Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SceneViewportTransformGizmoCoordinatorTest, BuildFrameOptionsCopiesVisibilityAndModes) {
|
||||||
|
const SceneViewportTransformGizmoFrameOptions options = BuildSceneViewportTransformGizmoFrameOptions(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
true);
|
||||||
|
|
||||||
|
EXPECT_TRUE(options.useCenterPivot);
|
||||||
|
EXPECT_TRUE(options.localSpace);
|
||||||
|
EXPECT_FALSE(options.usingTransformTool);
|
||||||
|
EXPECT_TRUE(options.showingMoveGizmo);
|
||||||
|
EXPECT_FALSE(options.showingRotateGizmo);
|
||||||
|
EXPECT_TRUE(options.showingScaleGizmo);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SceneViewportTransformGizmoCoordinatorTest, BuildRefreshRequestCopiesInputs) {
|
||||||
|
StubEditorContext context = {};
|
||||||
|
SceneViewportOverlayData overlay = {};
|
||||||
|
overlay.valid = true;
|
||||||
|
overlay.verticalFovDegrees = 47.0f;
|
||||||
|
const SceneViewportTransformGizmoFrameOptions options = BuildSceneViewportTransformGizmoFrameOptions(
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false);
|
||||||
|
|
||||||
|
const auto request = BuildSceneViewportTransformGizmoRefreshRequest(
|
||||||
|
context,
|
||||||
|
overlay,
|
||||||
|
Vector2(320.0f, 180.0f),
|
||||||
|
Vector2(25.0f, 15.0f),
|
||||||
|
options);
|
||||||
|
|
||||||
|
EXPECT_TRUE(request.IsValid());
|
||||||
|
EXPECT_EQ(request.context, &context);
|
||||||
|
EXPECT_TRUE(request.overlay.valid);
|
||||||
|
EXPECT_FLOAT_EQ(request.overlay.verticalFovDegrees, 47.0f);
|
||||||
|
EXPECT_FLOAT_EQ(request.viewportSize.x, 320.0f);
|
||||||
|
EXPECT_FLOAT_EQ(request.viewportSize.y, 180.0f);
|
||||||
|
EXPECT_FLOAT_EQ(request.mousePosition.x, 25.0f);
|
||||||
|
EXPECT_FLOAT_EQ(request.mousePosition.y, 15.0f);
|
||||||
|
EXPECT_TRUE(request.options.localSpace);
|
||||||
|
EXPECT_TRUE(request.options.showingRotateGizmo);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(SceneViewportTransformGizmoCoordinatorTest, OverlaySubmissionBuildsAndSubmitsState) {
|
TEST(SceneViewportTransformGizmoCoordinatorTest, OverlaySubmissionBuildsAndSubmitsState) {
|
||||||
XCEngine::Components::GameObject gameObject("CoordinatorTestObject");
|
XCEngine::Components::GameObject gameObject("CoordinatorTestObject");
|
||||||
XCEngine::Editor::SceneViewportMoveGizmo moveGizmo = {};
|
XCEngine::Editor::SceneViewportMoveGizmo moveGizmo = {};
|
||||||
@@ -107,3 +352,44 @@ TEST(SceneViewportTransformGizmoCoordinatorTest, OverlaySubmissionBuildsAndSubmi
|
|||||||
EXPECT_TRUE(viewportHostService.lastSubmission.hasMoveGizmo);
|
EXPECT_TRUE(viewportHostService.lastSubmission.hasMoveGizmo);
|
||||||
EXPECT_EQ(viewportHostService.lastSubmission.moveEntityId, gameObject.GetID());
|
EXPECT_EQ(viewportHostService.lastSubmission.moveEntityId, gameObject.GetID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SceneViewportTransformGizmoCoordinatorTest, RefreshAndSubmitFrameUsesCoordinatorRequest) {
|
||||||
|
XCEngine::Components::GameObject gameObject("CoordinatorRefreshObject");
|
||||||
|
StubEditorContext context = {};
|
||||||
|
StubViewportHostService viewportHostService = {};
|
||||||
|
context.viewportHostService = &viewportHostService;
|
||||||
|
context.sceneManager.entity = &gameObject;
|
||||||
|
context.selectionManager.SetSelectedEntity(gameObject.GetID());
|
||||||
|
|
||||||
|
XCEngine::Editor::SceneViewportMoveGizmo moveGizmo = {};
|
||||||
|
XCEngine::Editor::SceneViewportRotateGizmo rotateGizmo = {};
|
||||||
|
XCEngine::Editor::SceneViewportScaleGizmo scaleGizmo = {};
|
||||||
|
const SceneViewportTransformGizmoFrameOptions options = BuildSceneViewportTransformGizmoFrameOptions(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false);
|
||||||
|
|
||||||
|
const SceneViewportTransformGizmoFrameUpdate update = RefreshAndSubmitSceneViewportTransformGizmoFrame(
|
||||||
|
viewportHostService,
|
||||||
|
BuildSceneViewportTransformGizmoRefreshRequest(
|
||||||
|
context,
|
||||||
|
{},
|
||||||
|
Vector2(640.0f, 360.0f),
|
||||||
|
Vector2(48.0f, 52.0f),
|
||||||
|
options),
|
||||||
|
moveGizmo,
|
||||||
|
rotateGizmo,
|
||||||
|
scaleGizmo);
|
||||||
|
|
||||||
|
EXPECT_EQ(update.frameState.moveContext.selectedObject, &gameObject);
|
||||||
|
EXPECT_EQ(update.frameState.selectionState.primaryObject, &gameObject);
|
||||||
|
EXPECT_TRUE(update.overlaySubmission.overlayState.hasMoveGizmo);
|
||||||
|
EXPECT_EQ(update.overlaySubmission.overlayState.moveEntityId, gameObject.GetID());
|
||||||
|
EXPECT_TRUE(viewportHostService.lastSubmission.hasMoveGizmo);
|
||||||
|
EXPECT_EQ(viewportHostService.lastSubmission.moveEntityId, gameObject.GetID());
|
||||||
|
|
||||||
|
CancelSceneViewportTransformGizmoFrame(context, moveGizmo, rotateGizmo, scaleGizmo);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user