refactor(editor): isolate engine service boundaries
This commit is contained in:
@@ -75,6 +75,7 @@ set(EDITOR_APP_CORE_TEST_SOURCES
|
||||
|
||||
set(EDITOR_APP_FEATURE_TEST_SOURCES
|
||||
test_editor_window_input_routing.cpp
|
||||
test_game_viewport_runtime.cpp
|
||||
test_project_panel.cpp
|
||||
test_scene_viewport_render_plan.cpp
|
||||
test_scene_viewport_runtime.cpp
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
#include "Scene/EditorSceneBackend.h"
|
||||
#include "Scene/EditorSceneRuntime.h"
|
||||
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
namespace {
|
||||
|
||||
using ::XCEngine::Components::GameObject;
|
||||
using ::XCEngine::Components::Scene;
|
||||
|
||||
class FakeEditorSceneBackend final : public EditorSceneBackend {
|
||||
public:
|
||||
EditorStartupSceneResult EnsureStartupScene(
|
||||
@@ -21,10 +15,6 @@ public:
|
||||
return startupSceneResult;
|
||||
}
|
||||
|
||||
Scene* GetActiveScene() const override {
|
||||
return activeScene;
|
||||
}
|
||||
|
||||
EditorSceneHierarchySnapshot BuildHierarchySnapshot() const override {
|
||||
return hierarchySnapshot;
|
||||
}
|
||||
@@ -34,9 +24,15 @@ public:
|
||||
return openSceneResult;
|
||||
}
|
||||
|
||||
GameObject* FindGameObject(std::string_view itemId) const override {
|
||||
lastFindItemId = std::string(itemId);
|
||||
return foundGameObject;
|
||||
std::optional<EditorSceneObjectSnapshot> GetObjectSnapshot(
|
||||
std::string_view itemId) const override {
|
||||
lastGetObjectSnapshotItemId = std::string(itemId);
|
||||
if (objectSnapshot.has_value() &&
|
||||
objectSnapshot->itemId == itemId) {
|
||||
return objectSnapshot;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool AddComponent(
|
||||
@@ -85,14 +81,13 @@ public:
|
||||
|
||||
EditorStartupSceneResult startupSceneResult = {};
|
||||
EditorSceneHierarchySnapshot hierarchySnapshot = {};
|
||||
Scene* activeScene = nullptr;
|
||||
GameObject* foundGameObject = nullptr;
|
||||
std::optional<EditorSceneObjectSnapshot> objectSnapshot = std::nullopt;
|
||||
bool openSceneResult = false;
|
||||
bool addComponentResult = false;
|
||||
int ensureStartupSceneCallCount = 0;
|
||||
std::filesystem::path lastProjectRoot = {};
|
||||
std::filesystem::path lastOpenedScenePath = {};
|
||||
mutable std::string lastFindItemId = {};
|
||||
mutable std::string lastGetObjectSnapshotItemId = {};
|
||||
std::string lastAddComponentItemId = {};
|
||||
std::string lastAddComponentTypeName = {};
|
||||
};
|
||||
@@ -119,21 +114,30 @@ TEST(EditorSceneRuntimeBackendTests, InitializeUsesBoundBackend) {
|
||||
EXPECT_EQ(runtime.GetStartupResult().sceneName, "Main");
|
||||
}
|
||||
|
||||
TEST(EditorSceneRuntimeBackendTests, FindGameObjectUsesBoundBackend) {
|
||||
TEST(EditorSceneRuntimeBackendTests, SetSelectionUsesBoundBackendObjectSnapshotLookup) {
|
||||
auto backend = std::make_unique<FakeEditorSceneBackend>();
|
||||
Scene scene("Main");
|
||||
GameObject probe("Probe");
|
||||
backend->startupSceneResult.ready = true;
|
||||
backend->activeScene = &scene;
|
||||
backend->foundGameObject = &probe;
|
||||
backend->objectSnapshot = EditorSceneObjectSnapshot{
|
||||
.itemId = "42",
|
||||
.objectId = 42u,
|
||||
.displayName = "Probe",
|
||||
.componentTypeNames = {},
|
||||
.visibleMaterialSlotCount = 1u
|
||||
};
|
||||
FakeEditorSceneBackend* const backendPtr = backend.get();
|
||||
|
||||
EditorSceneRuntime runtime = {};
|
||||
runtime.SetBackend(std::move(backend));
|
||||
ASSERT_TRUE(runtime.Initialize("D:/Xuanchi/Main/XCEngine/project"));
|
||||
|
||||
EXPECT_EQ(runtime.FindGameObject("42"), &probe);
|
||||
EXPECT_EQ(backendPtr->lastFindItemId, "42");
|
||||
ASSERT_TRUE(runtime.SetSelection("42"));
|
||||
const std::optional<EditorSceneObjectSnapshot> selectedObject =
|
||||
runtime.GetSelectedObjectSnapshot();
|
||||
ASSERT_TRUE(selectedObject.has_value());
|
||||
ASSERT_TRUE(runtime.GetSelectedObjectId().has_value());
|
||||
EXPECT_EQ(runtime.GetSelectedObjectId().value(), 42u);
|
||||
EXPECT_EQ(selectedObject->displayName, "Probe");
|
||||
EXPECT_EQ(backendPtr->lastGetObjectSnapshotItemId, "42");
|
||||
}
|
||||
|
||||
TEST(EditorSceneRuntimeBackendTests, BuildHierarchySnapshotUsesBoundBackend) {
|
||||
|
||||
@@ -95,11 +95,11 @@ TEST(EditorShellAssetValidationTest, ProductManifestDeclaresPanelRuntimeAndViewp
|
||||
|
||||
const auto* gamePanel = FindEditorProductPanel(kGamePanelId);
|
||||
ASSERT_NE(gamePanel, nullptr);
|
||||
EXPECT_EQ(gamePanel->runtimeKind, EditorProductPanelRuntimeKind::None);
|
||||
EXPECT_EQ(gamePanel->runtimeKind, EditorProductPanelRuntimeKind::Game);
|
||||
EXPECT_EQ(
|
||||
gamePanel->viewportRendererKind,
|
||||
EditorProductViewportRendererKind::Placeholder);
|
||||
EXPECT_FALSE(gamePanel->viewportPlaceholderStatus.empty());
|
||||
EditorProductViewportRendererKind::Game);
|
||||
EXPECT_TRUE(gamePanel->viewportPlaceholderStatus.empty());
|
||||
}
|
||||
|
||||
TEST(EditorShellAssetValidationTest, ValidationRejectsWorkspacePanelMissingFromRegistry) {
|
||||
|
||||
100
tests/UI/Editor/unit/test_game_viewport_runtime.cpp
Normal file
100
tests/UI/Editor/unit/test_game_viewport_runtime.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Game/GameViewportFeature.h"
|
||||
#include "Panels/EditorPanelIds.h"
|
||||
#include "State/EditorCommandFocusService.h"
|
||||
#include "Viewport/GameViewportRenderService.h"
|
||||
|
||||
#include <XCEditor/Viewport/UIEditorViewportInputBridge.h>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
namespace {
|
||||
|
||||
using ::XCEngine::UI::UIInputEvent;
|
||||
using ::XCEngine::UI::UIInputEventType;
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
using ::XCEngine::UI::UIPointerButton;
|
||||
using ::XCEngine::UI::UIRect;
|
||||
using ::XCEngine::UI::UISize;
|
||||
using ::XCEngine::UI::Editor::UIEditorViewportInputBridgeState;
|
||||
using ::XCEngine::UI::Editor::UIEditorWorkspaceComposeFrame;
|
||||
using ::XCEngine::UI::Editor::UIEditorWorkspaceComposeState;
|
||||
using ::XCEngine::UI::Editor::UIEditorWorkspacePanelPresentationState;
|
||||
using ::XCEngine::UI::Editor::UIEditorWorkspaceViewportComposeFrame;
|
||||
using ::XCEngine::UI::Editor::UpdateUIEditorViewportInputBridge;
|
||||
|
||||
UIInputEvent MakePointerEvent(
|
||||
UIInputEventType type,
|
||||
float x,
|
||||
float y,
|
||||
UIPointerButton button = UIPointerButton::None) {
|
||||
UIInputEvent event = {};
|
||||
event.type = type;
|
||||
event.position = UIPoint(x, y);
|
||||
event.pointerButton = button;
|
||||
return event;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceComposeState BuildGameComposeState(
|
||||
const UIEditorViewportInputBridgeState& inputBridgeState) {
|
||||
UIEditorWorkspaceComposeState composeState = {};
|
||||
UIEditorWorkspacePanelPresentationState panelState = {};
|
||||
panelState.panelId = std::string(kGamePanelId);
|
||||
panelState.viewportShellState.inputBridgeState = inputBridgeState;
|
||||
composeState.panelStates.push_back(std::move(panelState));
|
||||
return composeState;
|
||||
}
|
||||
|
||||
UIEditorWorkspaceComposeFrame BuildGameComposeFrame(
|
||||
const ::XCEngine::UI::Editor::UIEditorViewportInputBridgeFrame& inputFrame,
|
||||
const UIRect& inputRect,
|
||||
const UISize& requestedViewportSize) {
|
||||
UIEditorWorkspaceComposeFrame composeFrame = {};
|
||||
UIEditorWorkspaceViewportComposeFrame viewportFrame = {};
|
||||
viewportFrame.panelId = std::string(kGamePanelId);
|
||||
viewportFrame.viewportShellFrame.inputFrame = inputFrame;
|
||||
viewportFrame.viewportShellFrame.requestedViewportSize = requestedViewportSize;
|
||||
viewportFrame.viewportShellFrame.slotLayout.inputRect = inputRect;
|
||||
viewportFrame.viewportShellFrame.slotLayout.bounds = inputRect;
|
||||
composeFrame.viewportFrames.push_back(std::move(viewportFrame));
|
||||
return composeFrame;
|
||||
}
|
||||
|
||||
TEST(GameViewportRuntimeTests, PointerDownClaimsGameCommandFocus) {
|
||||
GameViewportFeature feature = {};
|
||||
EditorCommandFocusService commandFocusService = {};
|
||||
feature.SetCommandFocusService(&commandFocusService);
|
||||
|
||||
UIEditorViewportInputBridgeState inputBridgeState = {};
|
||||
const UIRect inputRect(100.0f, 80.0f, 640.0f, 360.0f);
|
||||
const UISize viewportSize(640.0f, 360.0f);
|
||||
const auto frame = UpdateUIEditorViewportInputBridge(
|
||||
inputBridgeState,
|
||||
inputRect,
|
||||
{
|
||||
MakePointerEvent(UIInputEventType::PointerMove, 220.0f, 180.0f),
|
||||
MakePointerEvent(
|
||||
UIInputEventType::PointerButtonDown,
|
||||
220.0f,
|
||||
180.0f,
|
||||
UIPointerButton::Left)
|
||||
});
|
||||
|
||||
feature.Update(
|
||||
BuildGameComposeState(inputBridgeState),
|
||||
BuildGameComposeFrame(frame, inputRect, viewportSize));
|
||||
|
||||
EXPECT_EQ(commandFocusService.GetExplicitRoute(), EditorActionRoute::Game);
|
||||
}
|
||||
|
||||
TEST(GameViewportRuntimeTests, GameViewportRendererUsesDefaultViewportResources) {
|
||||
const ViewportResourceRequirements requirements =
|
||||
GameViewportRenderService::GetViewportResourceRequirements();
|
||||
|
||||
EXPECT_FALSE(requirements.requiresDepthSampling);
|
||||
EXPECT_FALSE(requirements.requiresObjectIdSurface);
|
||||
EXPECT_FALSE(requirements.requiresSelectionMaskSurface);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
@@ -80,7 +80,9 @@ TEST(HierarchySceneBindingTests, BuildFromSceneUsesRealGameObjectIds) {
|
||||
GameObject* child = scene->CreateGameObject("Child", root);
|
||||
ASSERT_NE(child, nullptr);
|
||||
|
||||
const HierarchyModel model = HierarchyModel::BuildFromScene(scene);
|
||||
const std::unique_ptr<EditorSceneBackend> backend = CreateTestSceneBackend();
|
||||
const HierarchyModel model =
|
||||
HierarchyModel::BuildFromSnapshot(backend->BuildHierarchySnapshot());
|
||||
const HierarchyNode* rootNode =
|
||||
model.FindNode(MakeEditorGameObjectItemId(root->GetID()));
|
||||
ASSERT_NE(rootNode, nullptr);
|
||||
@@ -109,7 +111,8 @@ TEST(HierarchySceneBindingTests, DuplicateGameObjectClonesHierarchyIntoScene) {
|
||||
backend->DuplicateGameObject(MakeEditorGameObjectItemId(root->GetID()));
|
||||
ASSERT_FALSE(duplicateId.empty());
|
||||
|
||||
const HierarchyModel model = HierarchyModel::BuildFromScene(scene);
|
||||
const HierarchyModel model =
|
||||
HierarchyModel::BuildFromSnapshot(backend->BuildHierarchySnapshot());
|
||||
const HierarchyNode* duplicateNode = model.FindNode(duplicateId);
|
||||
ASSERT_NE(duplicateNode, nullptr);
|
||||
EXPECT_EQ(duplicateNode->label, "Root");
|
||||
@@ -136,7 +139,8 @@ TEST(HierarchySceneBindingTests, RenameGameObjectUpdatesRealSceneAndProjection)
|
||||
ASSERT_TRUE(backend->RenameGameObject(itemId, "PlayerCamera"));
|
||||
EXPECT_EQ(gameObject->GetName(), "PlayerCamera");
|
||||
|
||||
const HierarchyModel model = HierarchyModel::BuildFromScene(scene);
|
||||
const HierarchyModel model =
|
||||
HierarchyModel::BuildFromSnapshot(backend->BuildHierarchySnapshot());
|
||||
const HierarchyNode* node = model.FindNode(itemId);
|
||||
ASSERT_NE(node, nullptr);
|
||||
EXPECT_EQ(node->label, "PlayerCamera");
|
||||
@@ -162,7 +166,8 @@ TEST(HierarchySceneBindingTests, ReparentAndMoveToRootOperateOnRealScene) {
|
||||
MakeEditorGameObjectItemId(parentB->GetID())));
|
||||
EXPECT_EQ(child->GetParent(), parentB);
|
||||
|
||||
HierarchyModel model = HierarchyModel::BuildFromScene(scene);
|
||||
HierarchyModel model =
|
||||
HierarchyModel::BuildFromSnapshot(backend->BuildHierarchySnapshot());
|
||||
const HierarchyNode* parentBNode =
|
||||
model.FindNode(MakeEditorGameObjectItemId(parentB->GetID()));
|
||||
ASSERT_NE(parentBNode, nullptr);
|
||||
@@ -173,7 +178,7 @@ TEST(HierarchySceneBindingTests, ReparentAndMoveToRootOperateOnRealScene) {
|
||||
MakeEditorGameObjectItemId(child->GetID())));
|
||||
EXPECT_EQ(child->GetParent(), nullptr);
|
||||
|
||||
model = HierarchyModel::BuildFromScene(scene);
|
||||
model = HierarchyModel::BuildFromSnapshot(backend->BuildHierarchySnapshot());
|
||||
const auto roots = scene->GetRootGameObjects();
|
||||
EXPECT_EQ(roots.size(), 3u);
|
||||
}
|
||||
@@ -196,11 +201,12 @@ TEST(HierarchySceneBindingTests, EnsureStartupSceneLoadsMainSceneAndSetsActive)
|
||||
backend->EnsureStartupScene(projectRoot.Root());
|
||||
EXPECT_TRUE(result.ready);
|
||||
EXPECT_TRUE(result.loadedFromDisk);
|
||||
ASSERT_NE(backend->GetActiveScene(), nullptr);
|
||||
EXPECT_EQ(backend->GetActiveScene()->GetName(), "Main");
|
||||
Scene* const activeScene = SceneManager::Get().GetActiveScene();
|
||||
ASSERT_NE(activeScene, nullptr);
|
||||
EXPECT_EQ(activeScene->GetName(), "Main");
|
||||
|
||||
const HierarchyModel model =
|
||||
HierarchyModel::BuildFromScene(backend->GetActiveScene());
|
||||
HierarchyModel::BuildFromSnapshot(backend->BuildHierarchySnapshot());
|
||||
EXPECT_FALSE(model.Empty());
|
||||
|
||||
SceneManager& sceneManager = SceneManager::Get();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Inspector/Components/IInspectorComponentEditor.h"
|
||||
#include "Inspector/Components/InspectorComponentEditorRegistry.h"
|
||||
#include "Inspector/InspectorFieldValueApplier.h"
|
||||
#include "Inspector/InspectorPresentationModel.h"
|
||||
#include "Inspector/InspectorSubject.h"
|
||||
#include "Scene/EditorSceneRuntime.h"
|
||||
@@ -118,6 +119,20 @@ const UIEditorPropertyGridField* FindField(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const UIEditorPropertyGridField* FindFieldById(
|
||||
const InspectorPresentationModel& model,
|
||||
std::string_view fieldId) {
|
||||
for (const UIEditorPropertyGridSection& section : model.sections) {
|
||||
for (const UIEditorPropertyGridField& field : section.fields) {
|
||||
if (field.fieldId == fieldId) {
|
||||
return &field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const InspectorPresentationComponentBinding* FindBinding(
|
||||
const InspectorPresentationModel& model,
|
||||
std::string_view typeName) {
|
||||
@@ -201,7 +216,7 @@ TEST(InspectorPresentationModelTests, SceneObjectSubjectBuildsRegisteredComponen
|
||||
EditorSceneRuntime runtime = {};
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
Scene* scene = runtime.GetActiveScene();
|
||||
Scene* scene = SceneManager::Get().GetActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
GameObject* parent = scene->Find("Parent");
|
||||
ASSERT_NE(parent, nullptr);
|
||||
@@ -273,7 +288,7 @@ TEST(InspectorPresentationModelTests, CameraSkyboxMaterialBuildsAssetField) {
|
||||
EditorSceneRuntime runtime = {};
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
Scene* scene = runtime.GetActiveScene();
|
||||
Scene* scene = SceneManager::Get().GetActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
GameObject* parent = scene->Find("Parent");
|
||||
ASSERT_NE(parent, nullptr);
|
||||
@@ -302,5 +317,57 @@ TEST(InspectorPresentationModelTests, CameraSkyboxMaterialBuildsAssetField) {
|
||||
"Skybox.mat");
|
||||
}
|
||||
|
||||
TEST(InspectorPresentationModelTests, BoundFieldValueApplierKeepsComponentViewAliveDuringApply) {
|
||||
ScopedSceneManagerReset reset = {};
|
||||
TemporaryProjectRoot projectRoot = {};
|
||||
SaveMainScene(projectRoot);
|
||||
|
||||
EditorSceneRuntime runtime = {};
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
Scene* scene = SceneManager::Get().GetActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
GameObject* parent = scene->Find("Parent");
|
||||
ASSERT_NE(parent, nullptr);
|
||||
ASSERT_TRUE(runtime.SetSelection(parent->GetID()));
|
||||
|
||||
const InspectorSubject subject =
|
||||
BuildInspectorSubject(EditorSession{}, runtime);
|
||||
ASSERT_EQ(subject.kind, InspectorSubjectKind::SceneObject);
|
||||
|
||||
const InspectorPresentationModel model =
|
||||
BuildInspectorPresentationModel(
|
||||
subject,
|
||||
runtime,
|
||||
InspectorComponentEditorRegistry::Get());
|
||||
const auto* transformBinding = FindBinding(model, "Transform");
|
||||
ASSERT_NE(transformBinding, nullptr);
|
||||
ASSERT_FALSE(transformBinding->fieldIds.empty());
|
||||
|
||||
const UIEditorPropertyGridField* originalField =
|
||||
FindFieldById(model, transformBinding->fieldIds.front());
|
||||
ASSERT_NE(originalField, nullptr);
|
||||
ASSERT_EQ(originalField->kind, Widgets::UIEditorPropertyGridFieldKind::Vector3);
|
||||
|
||||
UIEditorPropertyGridField editedField = *originalField;
|
||||
editedField.vector3Value.values = { 8.0, 9.0, 10.0 };
|
||||
|
||||
bool applied = false;
|
||||
EXPECT_NO_THROW(
|
||||
applied = ApplyInspectorComponentBoundFieldValue(
|
||||
runtime,
|
||||
subject.sceneObject,
|
||||
*transformBinding,
|
||||
editedField));
|
||||
ASSERT_TRUE(applied);
|
||||
|
||||
const auto* transform = parent->GetTransform();
|
||||
ASSERT_NE(transform, nullptr);
|
||||
const auto position = transform->GetLocalPosition();
|
||||
EXPECT_FLOAT_EQ(position.x, 8.0f);
|
||||
EXPECT_FLOAT_EQ(position.y, 9.0f);
|
||||
EXPECT_FLOAT_EQ(position.z, 10.0f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#include "Viewport/SceneViewportRenderPlan.h"
|
||||
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace {
|
||||
@@ -72,17 +69,17 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
SceneViewportRenderRequest CreateValidRequest(
|
||||
XCEngine::Components::GameObject& cameraObject) {
|
||||
auto* camera =
|
||||
cameraObject.AddComponent<XCEngine::Components::CameraComponent>();
|
||||
EXPECT_NE(camera, nullptr);
|
||||
EXPECT_NE(cameraObject.GetTransform(), nullptr);
|
||||
cameraObject.GetTransform()->SetPosition(
|
||||
XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
||||
SceneViewportRenderRequest CreateValidRequest() {
|
||||
SceneViewportRenderRequest request = {};
|
||||
request.camera = camera;
|
||||
request.orbitDistance = 9.0f;
|
||||
request.camera.valid = true;
|
||||
request.camera.position = XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f);
|
||||
request.camera.forward = XCEngine::Math::Vector3::Forward();
|
||||
request.camera.right = XCEngine::Math::Vector3::Right();
|
||||
request.camera.up = XCEngine::Math::Vector3::Up();
|
||||
request.camera.verticalFovDegrees = 60.0f;
|
||||
request.camera.nearClipPlane = 0.03f;
|
||||
request.camera.farClipPlane = 2000.0f;
|
||||
request.camera.orbitDistance = 9.0f;
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -98,8 +95,7 @@ TEST(SceneViewportRenderPlanTests, BuildRenderPlanCreatesOutlinePassWhenSelectio
|
||||
targets.selectionMaskView = &selectionMaskView;
|
||||
targets.selectionMaskShaderView = &selectionMaskShaderView;
|
||||
|
||||
XCEngine::Components::GameObject cameraObject("SceneCamera");
|
||||
SceneViewportRenderRequest request = CreateValidRequest(cameraObject);
|
||||
SceneViewportRenderRequest request = CreateValidRequest();
|
||||
request.selectedObjectIds = { 7u, 11u };
|
||||
|
||||
std::size_t gridFactoryCallCount = 0u;
|
||||
@@ -136,8 +132,7 @@ TEST(SceneViewportRenderPlanTests, BuildRenderPlanCreatesOutlinePassWhenSelectio
|
||||
|
||||
TEST(SceneViewportRenderPlanTests, BuildRenderPlanWarnsWhenSelectionResourcesAreUnavailable) {
|
||||
ViewportRenderTargets targets = {};
|
||||
XCEngine::Components::GameObject cameraObject("SceneCamera");
|
||||
SceneViewportRenderRequest request = CreateValidRequest(cameraObject);
|
||||
SceneViewportRenderRequest request = CreateValidRequest();
|
||||
request.selectedObjectIds = { 42u };
|
||||
|
||||
std::size_t gridFactoryCallCount = 0u;
|
||||
@@ -167,8 +162,7 @@ TEST(SceneViewportRenderPlanTests, BuildRenderPlanWarnsWhenSelectionResourcesAre
|
||||
}
|
||||
|
||||
TEST(SceneViewportRenderPlanTests, BuildSceneViewportGridPassDataCopiesCameraTransformAndLens) {
|
||||
XCEngine::Components::GameObject cameraObject("SceneCamera");
|
||||
SceneViewportRenderRequest request = CreateValidRequest(cameraObject);
|
||||
SceneViewportRenderRequest request = CreateValidRequest();
|
||||
|
||||
const SceneViewportGridPassData gridData =
|
||||
BuildSceneViewportGridPassData(request);
|
||||
@@ -177,9 +171,9 @@ TEST(SceneViewportRenderPlanTests, BuildSceneViewportGridPassDataCopiesCameraTra
|
||||
EXPECT_FLOAT_EQ(gridData.cameraPosition.x, 1.0f);
|
||||
EXPECT_FLOAT_EQ(gridData.cameraPosition.y, 2.0f);
|
||||
EXPECT_FLOAT_EQ(gridData.cameraPosition.z, 3.0f);
|
||||
EXPECT_FLOAT_EQ(gridData.verticalFovDegrees, request.camera->GetFieldOfView());
|
||||
EXPECT_FLOAT_EQ(gridData.nearClipPlane, request.camera->GetNearClipPlane());
|
||||
EXPECT_FLOAT_EQ(gridData.farClipPlane, request.camera->GetFarClipPlane());
|
||||
EXPECT_FLOAT_EQ(gridData.verticalFovDegrees, request.camera.verticalFovDegrees);
|
||||
EXPECT_FLOAT_EQ(gridData.nearClipPlane, request.camera.nearClipPlane);
|
||||
EXPECT_FLOAT_EQ(gridData.farClipPlane, request.camera.farClipPlane);
|
||||
EXPECT_FLOAT_EQ(gridData.orbitDistance, 9.0f);
|
||||
}
|
||||
|
||||
@@ -204,8 +198,7 @@ TEST(SceneViewportRenderPlanTests, ApplyRenderPlanAttachesPassesAndMarksRenderSt
|
||||
targets.objectIdState = ResourceStates::Common;
|
||||
targets.selectionMaskState = ResourceStates::Common;
|
||||
|
||||
XCEngine::Components::GameObject cameraObject("SceneCamera");
|
||||
SceneViewportRenderRequest request = CreateValidRequest(cameraObject);
|
||||
SceneViewportRenderRequest request = CreateValidRequest();
|
||||
request.selectedObjectIds = { 24u };
|
||||
|
||||
auto result = BuildSceneViewportRenderPlan(
|
||||
|
||||
@@ -23,8 +23,11 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
namespace {
|
||||
@@ -113,6 +116,10 @@ void BindEngineSceneBackend(EditorSceneRuntime& runtime) {
|
||||
::XCEngine::Resources::ResourceManager::Get()));
|
||||
}
|
||||
|
||||
Scene* GetLoadedActiveScene() {
|
||||
return SceneManager::Get().GetActiveScene();
|
||||
}
|
||||
|
||||
UIInputEvent MakePointerEvent(
|
||||
UIInputEventType type,
|
||||
float x,
|
||||
@@ -208,6 +215,27 @@ const EditorSceneComponentDescriptor* FindComponentDescriptor(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<UIPoint> FindHoveredTransformGizmoPoint(
|
||||
SceneViewportTransformGizmo& gizmo,
|
||||
EditorSceneRuntime& runtime,
|
||||
const UIRect& viewportRect) {
|
||||
for (float y = viewportRect.y + 8.0f;
|
||||
y < viewportRect.y + viewportRect.height - 8.0f;
|
||||
y += 6.0f) {
|
||||
for (float x = viewportRect.x + 8.0f;
|
||||
x < viewportRect.x + viewportRect.width - 8.0f;
|
||||
x += 6.0f) {
|
||||
const UIPoint point(x, y);
|
||||
gizmo.Refresh(runtime, viewportRect, point, true);
|
||||
if (gizmo.IsHoveringHandle()) {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TEST(SceneViewportRuntimeTests, ApplySceneViewportCameraInputUpdatesCameraTransform) {
|
||||
ScopedSceneManagerReset reset = {};
|
||||
TemporaryProjectRoot projectRoot = {};
|
||||
@@ -217,22 +245,21 @@ TEST(SceneViewportRuntimeTests, ApplySceneViewportCameraInputUpdatesCameraTransf
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
|
||||
auto* camera = runtime.GetSceneViewCamera();
|
||||
ASSERT_NE(camera, nullptr);
|
||||
auto* transform = camera->GetGameObject()->GetTransform();
|
||||
ASSERT_NE(transform, nullptr);
|
||||
|
||||
const Math::Vector3 before = transform->GetPosition();
|
||||
const EditorSceneCameraSnapshot before =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(before.valid);
|
||||
|
||||
SceneViewportCameraInputState input = {};
|
||||
input.viewportHeight = 720.0f;
|
||||
input.zoomDelta = 1.0f;
|
||||
runtime.ApplySceneViewportCameraInput(input);
|
||||
|
||||
const Math::Vector3 after = transform->GetPosition();
|
||||
EXPECT_NE(before.x, after.x);
|
||||
EXPECT_NE(before.y, after.y);
|
||||
EXPECT_NE(before.z, after.z);
|
||||
const EditorSceneCameraSnapshot after =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(after.valid);
|
||||
EXPECT_NE(before.position.x, after.position.x);
|
||||
EXPECT_NE(before.position.y, after.position.y);
|
||||
EXPECT_NE(before.position.z, after.position.z);
|
||||
}
|
||||
|
||||
TEST(SceneViewportRuntimeTests, FocusSceneSelectionRepositionsCameraAroundSelectedObject) {
|
||||
@@ -243,25 +270,25 @@ TEST(SceneViewportRuntimeTests, FocusSceneSelectionRepositionsCameraAroundSelect
|
||||
EditorSceneRuntime runtime = {};
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
Scene* scene = runtime.GetActiveScene();
|
||||
Scene* scene = GetLoadedActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
|
||||
GameObject* target = scene->Find("Target");
|
||||
ASSERT_NE(target, nullptr);
|
||||
ASSERT_TRUE(runtime.SetSelection(target->GetID()));
|
||||
|
||||
auto* camera = runtime.GetSceneViewCamera();
|
||||
ASSERT_NE(camera, nullptr);
|
||||
auto* transform = camera->GetGameObject()->GetTransform();
|
||||
ASSERT_NE(transform, nullptr);
|
||||
const Math::Vector3 before = transform->GetPosition();
|
||||
const EditorSceneCameraSnapshot before =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(before.valid);
|
||||
|
||||
ASSERT_TRUE(runtime.FocusSceneSelection());
|
||||
|
||||
const Math::Vector3 after = transform->GetPosition();
|
||||
EXPECT_NE(before.x, after.x);
|
||||
EXPECT_NE(before.y, after.y);
|
||||
EXPECT_NE(before.z, after.z);
|
||||
const EditorSceneCameraSnapshot after =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(after.valid);
|
||||
EXPECT_NE(before.position.x, after.position.x);
|
||||
EXPECT_NE(before.position.y, after.position.y);
|
||||
EXPECT_NE(before.position.z, after.position.z);
|
||||
}
|
||||
|
||||
TEST(SceneViewportRuntimeTests, BuildSceneViewportRenderRequestIncludesSelectedObjectId) {
|
||||
@@ -272,7 +299,7 @@ TEST(SceneViewportRuntimeTests, BuildSceneViewportRenderRequestIncludesSelectedO
|
||||
EditorSceneRuntime runtime = {};
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
Scene* scene = runtime.GetActiveScene();
|
||||
Scene* scene = GetLoadedActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
|
||||
GameObject* target = scene->Find("Target");
|
||||
@@ -284,7 +311,7 @@ TEST(SceneViewportRuntimeTests, BuildSceneViewportRenderRequestIncludesSelectedO
|
||||
ASSERT_TRUE(request.IsValid());
|
||||
ASSERT_EQ(request.selectedObjectIds.size(), 1u);
|
||||
EXPECT_EQ(request.selectedObjectIds.front(), target->GetID());
|
||||
EXPECT_GT(request.orbitDistance, 0.0f);
|
||||
EXPECT_GT(request.camera.orbitDistance, 0.0f);
|
||||
EXPECT_FALSE(request.debugSelectionMask);
|
||||
}
|
||||
|
||||
@@ -296,7 +323,7 @@ TEST(SceneViewportRuntimeTests, SelectedComponentsExposeTransformAndAttachedCame
|
||||
EditorSceneRuntime runtime = {};
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
Scene* scene = runtime.GetActiveScene();
|
||||
Scene* scene = GetLoadedActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
GameObject* target = scene->Find("Target");
|
||||
ASSERT_NE(target, nullptr);
|
||||
@@ -327,7 +354,7 @@ TEST(SceneViewportRuntimeTests, RemoveSelectedComponentDropsRemovableDescriptorB
|
||||
EditorSceneRuntime runtime = {};
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
Scene* scene = runtime.GetActiveScene();
|
||||
Scene* scene = GetLoadedActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
GameObject* target = scene->Find("Target");
|
||||
ASSERT_NE(target, nullptr);
|
||||
@@ -356,7 +383,9 @@ TEST(SceneViewportRuntimeTests, TransformSetterApisWriteLocalValuesOnSelectedTra
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
runtime.EnsureSceneSelection();
|
||||
|
||||
auto* target = const_cast<GameObject*>(runtime.GetSelectedGameObject());
|
||||
Scene* scene = GetLoadedActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
auto* target = scene->Find("Target");
|
||||
ASSERT_NE(target, nullptr);
|
||||
auto* transform = target->GetTransform();
|
||||
ASSERT_NE(transform, nullptr);
|
||||
@@ -400,7 +429,7 @@ TEST(SceneViewportRuntimeTests, SelectionStampAdvancesOnSceneSelectionChanges) {
|
||||
const std::uint64_t initialStamp = runtime.GetSelectionStamp();
|
||||
EXPECT_GT(initialStamp, 0u);
|
||||
|
||||
Scene* scene = runtime.GetActiveScene();
|
||||
Scene* scene = GetLoadedActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
GameObject* secondary = scene->CreateGameObject("Secondary");
|
||||
ASSERT_NE(secondary, nullptr);
|
||||
@@ -438,7 +467,7 @@ TEST(SceneViewportRuntimeTests, InspectorSelectionResolverFollowsUnifiedSelectio
|
||||
BuildInspectorSubject(session, runtime);
|
||||
EXPECT_EQ(sceneSubject.kind, InspectorSubjectKind::SceneObject);
|
||||
EXPECT_EQ(sceneSubject.source, InspectorSelectionSource::Scene);
|
||||
EXPECT_EQ(sceneSubject.sceneObject.displayName, "Target");
|
||||
EXPECT_EQ(sceneSubject.sceneObject.object.displayName, "Target");
|
||||
|
||||
selectionService.SetProjectSelection(
|
||||
"asset:scene",
|
||||
@@ -494,11 +523,9 @@ TEST(SceneViewportRuntimeTests, RightMouseDragRotatesSceneCameraThroughViewportC
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
|
||||
auto* camera = runtime.GetSceneViewCamera();
|
||||
ASSERT_NE(camera, nullptr);
|
||||
auto* transform = camera->GetGameObject()->GetTransform();
|
||||
ASSERT_NE(transform, nullptr);
|
||||
const Math::Vector3 beforeForward = transform->GetForward();
|
||||
const EditorSceneCameraSnapshot before =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(before.valid);
|
||||
|
||||
SceneViewportController controller = {};
|
||||
SceneViewportRenderService sceneViewportRenderService = {};
|
||||
@@ -540,10 +567,12 @@ TEST(SceneViewportRuntimeTests, RightMouseDragRotatesSceneCameraThroughViewportC
|
||||
BuildSceneComposeState(inputBridgeState),
|
||||
BuildSceneComposeFrame(dragFrame, inputRect, viewportSize));
|
||||
|
||||
const Math::Vector3 afterForward = transform->GetForward();
|
||||
EXPECT_NE(beforeForward.x, afterForward.x);
|
||||
EXPECT_NE(beforeForward.y, afterForward.y);
|
||||
EXPECT_NE(beforeForward.z, afterForward.z);
|
||||
const EditorSceneCameraSnapshot after =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(after.valid);
|
||||
EXPECT_NE(before.forward.x, after.forward.x);
|
||||
EXPECT_NE(before.forward.y, after.forward.y);
|
||||
EXPECT_NE(before.forward.z, after.forward.z);
|
||||
}
|
||||
|
||||
TEST(SceneViewportRuntimeTests, MoveRightInputMovesSceneCameraTowardPositiveCameraRight) {
|
||||
@@ -555,11 +584,9 @@ TEST(SceneViewportRuntimeTests, MoveRightInputMovesSceneCameraTowardPositiveCame
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
|
||||
auto* camera = runtime.GetSceneViewCamera();
|
||||
ASSERT_NE(camera, nullptr);
|
||||
auto* transform = camera->GetGameObject()->GetTransform();
|
||||
ASSERT_NE(transform, nullptr);
|
||||
const Math::Vector3 before = transform->GetPosition();
|
||||
const EditorSceneCameraSnapshot before =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(before.valid);
|
||||
|
||||
SceneViewportCameraInputState input = {};
|
||||
input.viewportHeight = 720.0f;
|
||||
@@ -567,8 +594,10 @@ TEST(SceneViewportRuntimeTests, MoveRightInputMovesSceneCameraTowardPositiveCame
|
||||
input.moveRight = 1.0f;
|
||||
runtime.ApplySceneViewportCameraInput(input);
|
||||
|
||||
const Math::Vector3 after = transform->GetPosition();
|
||||
EXPECT_GT(after.x, before.x);
|
||||
const EditorSceneCameraSnapshot after =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(after.valid);
|
||||
EXPECT_GT(after.position.x, before.position.x);
|
||||
}
|
||||
|
||||
TEST(SceneViewportRuntimeTests, MiddleMouseDragPansSceneCameraWithGrabSemantics) {
|
||||
@@ -580,11 +609,9 @@ TEST(SceneViewportRuntimeTests, MiddleMouseDragPansSceneCameraWithGrabSemantics)
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
|
||||
auto* camera = runtime.GetSceneViewCamera();
|
||||
ASSERT_NE(camera, nullptr);
|
||||
auto* transform = camera->GetGameObject()->GetTransform();
|
||||
ASSERT_NE(transform, nullptr);
|
||||
const Math::Vector3 before = transform->GetPosition();
|
||||
const EditorSceneCameraSnapshot before =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(before.valid);
|
||||
|
||||
SceneViewportController controller = {};
|
||||
SceneViewportRenderService sceneViewportRenderService = {};
|
||||
@@ -626,8 +653,10 @@ TEST(SceneViewportRuntimeTests, MiddleMouseDragPansSceneCameraWithGrabSemantics)
|
||||
BuildSceneComposeState(inputBridgeState),
|
||||
BuildSceneComposeFrame(dragFrame, inputRect, viewportSize));
|
||||
|
||||
const Math::Vector3 after = transform->GetPosition();
|
||||
EXPECT_LT(after.x, before.x);
|
||||
const EditorSceneCameraSnapshot after =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(after.valid);
|
||||
EXPECT_LT(after.position.x, before.position.x);
|
||||
}
|
||||
|
||||
TEST(SceneViewportRuntimeTests, ViewToolLeftMouseDragPansSceneCameraWithGrabSemantics) {
|
||||
@@ -640,11 +669,9 @@ TEST(SceneViewportRuntimeTests, ViewToolLeftMouseDragPansSceneCameraWithGrabSema
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
runtime.SetToolMode(SceneToolMode::View);
|
||||
|
||||
auto* camera = runtime.GetSceneViewCamera();
|
||||
ASSERT_NE(camera, nullptr);
|
||||
auto* transform = camera->GetGameObject()->GetTransform();
|
||||
ASSERT_NE(transform, nullptr);
|
||||
const Math::Vector3 before = transform->GetPosition();
|
||||
const EditorSceneCameraSnapshot before =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(before.valid);
|
||||
|
||||
SceneViewportController controller = {};
|
||||
SceneViewportRenderService sceneViewportRenderService = {};
|
||||
@@ -686,8 +713,10 @@ TEST(SceneViewportRuntimeTests, ViewToolLeftMouseDragPansSceneCameraWithGrabSema
|
||||
BuildSceneComposeState(inputBridgeState),
|
||||
BuildSceneComposeFrame(dragFrame, inputRect, viewportSize));
|
||||
|
||||
const Math::Vector3 after = transform->GetPosition();
|
||||
EXPECT_LT(after.x, before.x);
|
||||
const EditorSceneCameraSnapshot after =
|
||||
runtime.BuildSceneViewCameraSnapshot();
|
||||
ASSERT_TRUE(after.valid);
|
||||
EXPECT_LT(after.position.x, before.position.x);
|
||||
}
|
||||
|
||||
TEST(SceneViewportRuntimeTests, MouseWheelUsesSingleNotchNormalizationForSceneZoom) {
|
||||
@@ -699,7 +728,7 @@ TEST(SceneViewportRuntimeTests, MouseWheelUsesSingleNotchNormalizationForSceneZo
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
const float beforeDistance =
|
||||
runtime.BuildSceneViewportRenderRequest().orbitDistance;
|
||||
runtime.BuildSceneViewportRenderRequest().camera.orbitDistance;
|
||||
|
||||
SceneViewportController controller = {};
|
||||
SceneViewportRenderService sceneViewportRenderService = {};
|
||||
@@ -732,7 +761,7 @@ TEST(SceneViewportRuntimeTests, MouseWheelUsesSingleNotchNormalizationForSceneZo
|
||||
BuildSceneComposeFrame(wheelFrame, inputRect, viewportSize));
|
||||
|
||||
const float afterDistance =
|
||||
runtime.BuildSceneViewportRenderRequest().orbitDistance;
|
||||
runtime.BuildSceneViewportRenderRequest().camera.orbitDistance;
|
||||
EXPECT_LT(afterDistance, beforeDistance);
|
||||
EXPECT_GT(afterDistance, 4.0f);
|
||||
}
|
||||
@@ -924,6 +953,64 @@ TEST(SceneViewportRuntimeTests, SceneToolOverlayHandlesCoalescedClickInSingleFra
|
||||
EXPECT_EQ(runtime.GetToolMode(), SceneToolMode::Rotate);
|
||||
}
|
||||
|
||||
TEST(SceneViewportRuntimeTests, TranslateGizmoDragAppliesPreviewToSelectedObject) {
|
||||
ScopedSceneManagerReset reset = {};
|
||||
TemporaryProjectRoot projectRoot = {};
|
||||
SaveMainScene(projectRoot, Math::Vector3(0.0f, 0.0f, 0.0f));
|
||||
|
||||
EditorSceneRuntime runtime = {};
|
||||
BindEngineSceneBackend(runtime);
|
||||
ASSERT_TRUE(runtime.Initialize(projectRoot.Root()));
|
||||
runtime.SetToolMode(SceneToolMode::Translate);
|
||||
runtime.EnsureSceneSelection();
|
||||
|
||||
Scene* scene = GetLoadedActiveScene();
|
||||
ASSERT_NE(scene, nullptr);
|
||||
GameObject* target = scene->Find("Target");
|
||||
ASSERT_NE(target, nullptr);
|
||||
auto* transform = target->GetTransform();
|
||||
ASSERT_NE(transform, nullptr);
|
||||
|
||||
SceneViewportTransformGizmo gizmo = {};
|
||||
const UIRect viewportRect(0.0f, 0.0f, 640.0f, 360.0f);
|
||||
const std::optional<UIPoint> hoverPoint =
|
||||
FindHoveredTransformGizmoPoint(gizmo, runtime, viewportRect);
|
||||
ASSERT_TRUE(hoverPoint.has_value());
|
||||
|
||||
const std::array<UIPoint, 6u> dragTargets = {
|
||||
UIPoint(hoverPoint->x + 40.0f, hoverPoint->y),
|
||||
UIPoint(hoverPoint->x - 40.0f, hoverPoint->y),
|
||||
UIPoint(hoverPoint->x, hoverPoint->y + 40.0f),
|
||||
UIPoint(hoverPoint->x, hoverPoint->y - 40.0f),
|
||||
UIPoint(hoverPoint->x + 32.0f, hoverPoint->y + 32.0f),
|
||||
UIPoint(hoverPoint->x - 32.0f, hoverPoint->y - 32.0f)
|
||||
};
|
||||
|
||||
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);
|
||||
ASSERT_TRUE(gizmo.IsHoveringHandle());
|
||||
ASSERT_TRUE(gizmo.TryBeginDrag(runtime));
|
||||
|
||||
gizmo.Refresh(runtime, viewportRect, dragPoint, true);
|
||||
ASSERT_TRUE(gizmo.UpdateDrag(runtime));
|
||||
|
||||
const Math::Vector3 previewPosition = transform->GetPosition();
|
||||
if (std::abs(previewPosition.x) > 0.0001f ||
|
||||
std::abs(previewPosition.y) > 0.0001f ||
|
||||
std::abs(previewPosition.z) > 0.0001f) {
|
||||
previewApplied = true;
|
||||
gizmo.CancelDrag(runtime);
|
||||
break;
|
||||
}
|
||||
|
||||
gizmo.CancelDrag(runtime);
|
||||
}
|
||||
|
||||
EXPECT_TRUE(previewApplied);
|
||||
}
|
||||
|
||||
TEST(SceneViewportRuntimeTests, SceneViewportRendererDeclaresExplicitAuxiliaryResourceRequirements) {
|
||||
const ViewportResourceRequirements requirements =
|
||||
SceneViewportRenderService::GetViewportResourceRequirements();
|
||||
|
||||
Reference in New Issue
Block a user