Formalize scene viewport interaction resolver

This commit is contained in:
2026-04-03 17:16:16 +08:00
parent 27014e613e
commit 1ac2afb0bb
7 changed files with 345 additions and 156 deletions

View File

@@ -11,6 +11,7 @@ set(EDITOR_TEST_SOURCES
test_scene_viewport_rotate_gizmo.cpp
test_scene_viewport_scale_gizmo.cpp
test_scene_viewport_picker.cpp
test_scene_viewport_interaction_resolver.cpp
test_scene_viewport_shader_paths.cpp
test_scene_viewport_overlay_renderer.cpp
test_scene_viewport_overlay_providers.cpp
@@ -38,6 +39,7 @@ set(EDITOR_TEST_SOURCES
${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportRotateGizmo.cpp
${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportScaleGizmo.cpp
${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportHudOverlay.cpp
${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportInteractionResolver.cpp
${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportOrientationGizmo.cpp
${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportOverlayBuilder.cpp
${CMAKE_SOURCE_DIR}/editor/src/Viewport/SceneViewportOverlayProviders.cpp

View File

@@ -0,0 +1,90 @@
#include <gtest/gtest.h>
#include "Viewport/SceneViewportInteractionResolver.h"
using XCEngine::Editor::ResolveSceneViewportInteraction;
using XCEngine::Editor::SceneViewportGizmoAxis;
using XCEngine::Editor::SceneViewportHudOverlayHitKind;
using XCEngine::Editor::SceneViewportHudOverlayHitResult;
using XCEngine::Editor::SceneViewportInteractionKind;
using XCEngine::Editor::SceneViewportInteractionResolveRequest;
using XCEngine::Editor::SceneViewportOrientationAxis;
using XCEngine::Editor::SceneViewportOverlayFrameData;
using XCEngine::Editor::SceneViewportOverlayHandleHitResult;
using XCEngine::Editor::SceneViewportOverlayHandleKind;
using XCEngine::Editor::SceneViewportOverlayHandleRecord;
using XCEngine::Editor::SceneViewportOverlayHandleShape;
using XCEngine::Math::Vector2;
TEST(SceneViewportInteractionResolverTest, ReturnsNoHitWhenBothSourcesAreEmpty) {
const auto interaction = ResolveSceneViewportInteraction(
SceneViewportOverlayHandleHitResult{},
SceneViewportHudOverlayHitResult{});
EXPECT_FALSE(interaction.HasHit());
EXPECT_EQ(interaction.kind, SceneViewportInteractionKind::None);
}
TEST(SceneViewportInteractionResolverTest, PrefersTransformHandleOverHudOrientationByPriority) {
SceneViewportOverlayHandleHitResult overlayHandleHit = {};
overlayHandleHit.kind = SceneViewportOverlayHandleKind::MoveAxis;
overlayHandleHit.handleId = static_cast<uint64_t>(SceneViewportGizmoAxis::X);
overlayHandleHit.priority = 322;
overlayHandleHit.distanceSq = 4.0f;
overlayHandleHit.depth = 3.0f;
SceneViewportHudOverlayHitResult hudHit = {};
hudHit.kind = SceneViewportHudOverlayHitKind::OrientationAxis;
hudHit.orientationAxis = SceneViewportOrientationAxis::PositiveX;
const auto interaction = ResolveSceneViewportInteraction(overlayHandleHit, hudHit);
ASSERT_TRUE(interaction.HasHit());
EXPECT_EQ(interaction.kind, SceneViewportInteractionKind::MoveGizmo);
EXPECT_EQ(interaction.moveAxis, SceneViewportGizmoAxis::X);
EXPECT_EQ(interaction.orientationAxis, SceneViewportOrientationAxis::None);
}
TEST(SceneViewportInteractionResolverTest, PrefersHudOrientationOverSceneIconByPriority) {
SceneViewportOverlayHandleHitResult overlayHandleHit = {};
overlayHandleHit.kind = SceneViewportOverlayHandleKind::SceneIcon;
overlayHandleHit.entityId = 99;
overlayHandleHit.priority = 100;
overlayHandleHit.distanceSq = 0.0f;
overlayHandleHit.depth = 0.0f;
SceneViewportHudOverlayHitResult hudHit = {};
hudHit.kind = SceneViewportHudOverlayHitKind::OrientationAxis;
hudHit.orientationAxis = SceneViewportOrientationAxis::PositiveY;
const auto interaction = ResolveSceneViewportInteraction(overlayHandleHit, hudHit);
ASSERT_TRUE(interaction.HasHit());
EXPECT_EQ(interaction.kind, SceneViewportInteractionKind::OrientationGizmo);
EXPECT_EQ(interaction.orientationAxis, SceneViewportOrientationAxis::PositiveY);
EXPECT_EQ(interaction.entityId, 0u);
}
TEST(SceneViewportInteractionResolverTest, RequestPathDelegatesToOverlayHandleHitTesting) {
SceneViewportOverlayFrameData frameData = {};
frameData.overlay.valid = true;
SceneViewportOverlayHandleRecord& record = frameData.handleRecords.emplace_back();
record.kind = SceneViewportOverlayHandleKind::SceneIcon;
record.entityId = 77;
record.shape = SceneViewportOverlayHandleShape::ScreenRect;
record.priority = 100;
record.screenCenter = Vector2(80.0f, 60.0f);
record.screenHalfSize = Vector2(12.0f, 10.0f);
SceneViewportInteractionResolveRequest request = {};
request.overlayFrameData = &frameData;
request.viewportSize = Vector2(200.0f, 150.0f);
request.localMousePosition = Vector2(80.0f, 60.0f);
const auto interaction = ResolveSceneViewportInteraction(request);
ASSERT_TRUE(interaction.HasHit());
EXPECT_EQ(interaction.kind, SceneViewportInteractionKind::SceneIcon);
EXPECT_EQ(interaction.entityId, 77u);
}