engine: sync editor rendering and ui changes
This commit is contained in:
@@ -9,9 +9,12 @@
|
||||
#include "Core/EditorContext.h"
|
||||
#include "Core/PlaySessionController.h"
|
||||
|
||||
#include <XCEngine/Components/MeshFilterComponent.h>
|
||||
#include <XCEngine/Components/MeshRendererComponent.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Math/Quaternion.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
@@ -24,6 +27,15 @@ namespace fs = std::filesystem;
|
||||
namespace XCEngine::Editor {
|
||||
namespace {
|
||||
|
||||
bool DirectoryHasEntries(const fs::path& directoryPath) {
|
||||
std::error_code ec;
|
||||
if (!fs::exists(directoryPath, ec) || !fs::is_directory(directoryPath, ec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return fs::directory_iterator(directoryPath) != fs::directory_iterator();
|
||||
}
|
||||
|
||||
class EditorActionRoutingTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
@@ -138,6 +150,41 @@ TEST_F(EditorActionRoutingTest, HierarchyRouteExecutesCopyPasteDuplicateDeleteAn
|
||||
EXPECT_FALSE(m_context.GetSelectionManager().IsSelected(duplicatedEntityId));
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, CreatePrimitiveEntityAddsBuiltinMeshComponentsAndSupportsUndoRedo) {
|
||||
using XCEngine::Resources::BuiltinPrimitiveType;
|
||||
using XCEngine::Resources::GetBuiltinDefaultPrimitiveMaterialPath;
|
||||
using XCEngine::Resources::GetBuiltinPrimitiveMeshPath;
|
||||
|
||||
auto* entity = Commands::CreatePrimitiveEntity(m_context, BuiltinPrimitiveType::Cube, nullptr);
|
||||
ASSERT_NE(entity, nullptr);
|
||||
EXPECT_EQ(CountHierarchyEntities(m_context.GetSceneManager()), 1u);
|
||||
|
||||
auto* meshFilter = entity->GetComponent<::XCEngine::Components::MeshFilterComponent>();
|
||||
auto* meshRenderer = entity->GetComponent<::XCEngine::Components::MeshRendererComponent>();
|
||||
ASSERT_NE(meshFilter, nullptr);
|
||||
ASSERT_NE(meshRenderer, nullptr);
|
||||
EXPECT_EQ(meshFilter->GetMeshPath(), GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Cube).CStr());
|
||||
ASSERT_EQ(meshRenderer->GetMaterialCount(), 1u);
|
||||
EXPECT_EQ(meshRenderer->GetMaterialPath(0), GetBuiltinDefaultPrimitiveMaterialPath().CStr());
|
||||
EXPECT_TRUE(m_context.GetUndoManager().CanUndo());
|
||||
|
||||
m_context.GetUndoManager().Undo();
|
||||
EXPECT_EQ(CountHierarchyEntities(m_context.GetSceneManager()), 0u);
|
||||
|
||||
m_context.GetUndoManager().Redo();
|
||||
EXPECT_EQ(CountHierarchyEntities(m_context.GetSceneManager()), 1u);
|
||||
|
||||
auto* restored = m_context.GetSceneManager().GetScene()->Find("Cube");
|
||||
ASSERT_NE(restored, nullptr);
|
||||
auto* restoredMeshFilter = restored->GetComponent<::XCEngine::Components::MeshFilterComponent>();
|
||||
auto* restoredMeshRenderer = restored->GetComponent<::XCEngine::Components::MeshRendererComponent>();
|
||||
ASSERT_NE(restoredMeshFilter, nullptr);
|
||||
ASSERT_NE(restoredMeshRenderer, nullptr);
|
||||
EXPECT_EQ(restoredMeshFilter->GetMeshPath(), GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Cube).CStr());
|
||||
ASSERT_EQ(restoredMeshRenderer->GetMaterialCount(), 1u);
|
||||
EXPECT_EQ(restoredMeshRenderer->GetMaterialPath(0), GetBuiltinDefaultPrimitiveMaterialPath().CStr());
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, ProjectRouteExecutesOpenBackAndDelete) {
|
||||
const fs::path assetsDir = m_projectRoot / "Assets";
|
||||
const fs::path folderPath = assetsDir / "RouteFolder";
|
||||
@@ -419,6 +466,32 @@ TEST_F(EditorActionRoutingTest, HierarchyRouterRenameHelpersPublishAndCommit) {
|
||||
m_context.GetEventBus().Unsubscribe<EntityRenameRequestedEvent>(renameSubscription);
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, CreateTypedLightCommandsAssignExpectedNamesAndTypes) {
|
||||
auto* directionalLight = Commands::CreateDirectionalLightEntity(m_context);
|
||||
auto* pointLight = Commands::CreatePointLightEntity(m_context);
|
||||
auto* spotLight = Commands::CreateSpotLightEntity(m_context);
|
||||
|
||||
ASSERT_NE(directionalLight, nullptr);
|
||||
ASSERT_NE(pointLight, nullptr);
|
||||
ASSERT_NE(spotLight, nullptr);
|
||||
|
||||
EXPECT_EQ(directionalLight->GetName(), "Directional Light");
|
||||
EXPECT_EQ(pointLight->GetName(), "Point Light");
|
||||
EXPECT_EQ(spotLight->GetName(), "Spot Light");
|
||||
|
||||
auto* directionalComponent = directionalLight->GetComponent<Components::LightComponent>();
|
||||
auto* pointComponent = pointLight->GetComponent<Components::LightComponent>();
|
||||
auto* spotComponent = spotLight->GetComponent<Components::LightComponent>();
|
||||
|
||||
ASSERT_NE(directionalComponent, nullptr);
|
||||
ASSERT_NE(pointComponent, nullptr);
|
||||
ASSERT_NE(spotComponent, nullptr);
|
||||
|
||||
EXPECT_EQ(directionalComponent->GetLightType(), Components::LightType::Directional);
|
||||
EXPECT_EQ(pointComponent->GetLightType(), Components::LightType::Point);
|
||||
EXPECT_EQ(spotComponent->GetLightType(), Components::LightType::Spot);
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, HierarchyItemContextRequestSelectsEntityAndStoresPopupTarget) {
|
||||
auto* entity = Commands::CreateEmptyEntity(m_context, nullptr, "Create Entity", "ContextTarget");
|
||||
ASSERT_NE(entity, nullptr);
|
||||
@@ -483,68 +556,6 @@ TEST_F(EditorActionRoutingTest, ProjectCommandsRenameAssetUpdatesSelectionAndPre
|
||||
EXPECT_EQ(m_context.GetProjectManager().GetSelectedItemPath(), renamedItem->fullPath);
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, ProjectCommandsMigrateSceneAssetReferencesRewritesLegacyScenePayloads) {
|
||||
using ::XCEngine::Resources::ResourceManager;
|
||||
|
||||
const fs::path assetsDir = m_projectRoot / "Assets";
|
||||
const fs::path scenesDir = assetsDir / "Scenes";
|
||||
const fs::path materialPath = assetsDir / "runtime.material";
|
||||
const fs::path scenePath = scenesDir / "LegacyScene.xc";
|
||||
|
||||
{
|
||||
std::ofstream materialFile(materialPath.string(), std::ios::out | std::ios::trunc);
|
||||
ASSERT_TRUE(materialFile.is_open());
|
||||
materialFile << "{\n";
|
||||
materialFile << " \"renderQueue\": \"geometry\",\n";
|
||||
materialFile << " \"renderState\": {\n";
|
||||
materialFile << " \"cull\": \"back\"\n";
|
||||
materialFile << " }\n";
|
||||
materialFile << "}\n";
|
||||
}
|
||||
|
||||
{
|
||||
std::ofstream sceneFile(scenePath.string(), std::ios::out | std::ios::trunc);
|
||||
ASSERT_TRUE(sceneFile.is_open());
|
||||
sceneFile << "# XCEngine Scene File\n";
|
||||
sceneFile << "scene=Legacy Scene\n";
|
||||
sceneFile << "active=1\n\n";
|
||||
sceneFile << "gameobject_begin\n";
|
||||
sceneFile << "id=1\n";
|
||||
sceneFile << "uuid=1\n";
|
||||
sceneFile << "name=Legacy Object\n";
|
||||
sceneFile << "active=1\n";
|
||||
sceneFile << "parent=0\n";
|
||||
sceneFile << "transform=position=0,0,0;rotation=0,0,0,1;scale=1,1,1;\n";
|
||||
sceneFile << "component=MeshFilter;mesh=builtin://meshes/cube;meshRef=;\n";
|
||||
sceneFile << "component=MeshRenderer;materials=Assets/runtime.material;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;\n";
|
||||
sceneFile << "gameobject_end\n";
|
||||
}
|
||||
|
||||
ASSERT_TRUE(Commands::CanMigrateSceneAssetReferences(m_context));
|
||||
const IProjectManager::SceneAssetReferenceMigrationReport report =
|
||||
Commands::MigrateSceneAssetReferences(m_context);
|
||||
|
||||
EXPECT_EQ(report.scannedSceneCount, 1u);
|
||||
EXPECT_EQ(report.migratedSceneCount, 1u);
|
||||
EXPECT_EQ(report.unchangedSceneCount, 0u);
|
||||
EXPECT_EQ(report.failedSceneCount, 0u);
|
||||
|
||||
std::ifstream migratedScene(scenePath.string(), std::ios::in | std::ios::binary);
|
||||
ASSERT_TRUE(migratedScene.is_open());
|
||||
std::string migratedText((std::istreambuf_iterator<char>(migratedScene)),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
||||
EXPECT_NE(migratedText.find("meshPath=builtin://meshes/cube;"), std::string::npos);
|
||||
EXPECT_EQ(migratedText.find("component=MeshFilter;mesh=builtin://meshes/cube;"), std::string::npos);
|
||||
EXPECT_NE(migratedText.find("materialPaths=;"), std::string::npos);
|
||||
EXPECT_NE(migratedText.find("materialRefs="), std::string::npos);
|
||||
EXPECT_EQ(migratedText.find("materialRefs=;"), std::string::npos);
|
||||
EXPECT_EQ(migratedText.find("component=MeshRenderer;materials="), std::string::npos);
|
||||
|
||||
ResourceManager::Get().SetResourceRoot("");
|
||||
ResourceManager::Get().Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, ProjectItemContextRequestSelectsAssetAndStoresPopupTarget) {
|
||||
const fs::path assetsDir = m_projectRoot / "Assets";
|
||||
const fs::path filePath = assetsDir / "ContextAsset.txt";
|
||||
@@ -618,6 +629,64 @@ TEST_F(EditorActionRoutingTest, ProjectSelectionSurvivesRefreshWhenItemOrderChan
|
||||
FindCurrentItemIndexByName("Selected.txt"));
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, ProjectCommandsExposeAssetCacheMaintenanceActions) {
|
||||
using ::XCEngine::Resources::ResourceManager;
|
||||
|
||||
const fs::path materialPath = m_projectRoot / "Assets" / "ToolMaterial.mat";
|
||||
std::ofstream(materialPath.string()) << "{\n \"renderQueue\": \"geometry\"\n}\n";
|
||||
m_context.GetProjectManager().RefreshCurrentFolder();
|
||||
|
||||
ResourceManager& resourceManager = ResourceManager::Get();
|
||||
resourceManager.Initialize();
|
||||
resourceManager.SetResourceRoot(m_projectRoot.string().c_str());
|
||||
|
||||
const AssetItemPtr materialItem = FindCurrentItemByName("ToolMaterial.mat");
|
||||
ASSERT_NE(materialItem, nullptr);
|
||||
m_context.GetProjectManager().SetSelectedItem(materialItem);
|
||||
|
||||
EXPECT_TRUE(Commands::CanReimportSelectedAsset(m_context));
|
||||
EXPECT_TRUE(Commands::CanReimportAllAssets(m_context));
|
||||
EXPECT_TRUE(Commands::CanClearLibrary(m_context));
|
||||
|
||||
m_context.SetRuntimeMode(EditorRuntimeMode::Play);
|
||||
EXPECT_FALSE(Commands::CanReimportSelectedAsset(m_context));
|
||||
EXPECT_FALSE(Commands::CanReimportAllAssets(m_context));
|
||||
EXPECT_FALSE(Commands::CanClearLibrary(m_context));
|
||||
|
||||
m_context.SetRuntimeMode(EditorRuntimeMode::Edit);
|
||||
resourceManager.SetResourceRoot("");
|
||||
resourceManager.Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, ProjectCommandsReimportSelectedAssetAndClearLibraryDriveAssetCache) {
|
||||
using ::XCEngine::Resources::ResourceManager;
|
||||
|
||||
const fs::path materialPath = m_projectRoot / "Assets" / "ToolMaterial.mat";
|
||||
std::ofstream(materialPath.string()) << "{\n \"renderQueue\": \"geometry\"\n}\n";
|
||||
m_context.GetProjectManager().RefreshCurrentFolder();
|
||||
|
||||
ResourceManager& resourceManager = ResourceManager::Get();
|
||||
resourceManager.Initialize();
|
||||
resourceManager.SetResourceRoot(m_projectRoot.string().c_str());
|
||||
|
||||
const AssetItemPtr materialItem = FindCurrentItemByName("ToolMaterial.mat");
|
||||
ASSERT_NE(materialItem, nullptr);
|
||||
m_context.GetProjectManager().SetSelectedItem(materialItem);
|
||||
|
||||
const fs::path libraryRoot(resourceManager.GetProjectLibraryRoot().CStr());
|
||||
EXPECT_TRUE(Commands::ReimportSelectedAsset(m_context));
|
||||
EXPECT_TRUE(DirectoryHasEntries(libraryRoot / "Artifacts"));
|
||||
|
||||
EXPECT_TRUE(Commands::ClearLibrary(m_context));
|
||||
EXPECT_FALSE(DirectoryHasEntries(libraryRoot / "Artifacts"));
|
||||
|
||||
EXPECT_TRUE(Commands::ReimportAllAssets(m_context));
|
||||
EXPECT_TRUE(DirectoryHasEntries(libraryRoot / "Artifacts"));
|
||||
|
||||
resourceManager.SetResourceRoot("");
|
||||
resourceManager.Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, ProjectCommandsRejectMovingFolderIntoItsDescendant) {
|
||||
const fs::path assetsDir = m_projectRoot / "Assets";
|
||||
const fs::path parentPath = assetsDir / "Parent";
|
||||
|
||||
@@ -34,6 +34,8 @@ using XCEngine::Editor::SceneViewportInteractionResult;
|
||||
using XCEngine::Editor::SceneViewportOrientationAxis;
|
||||
using XCEngine::Editor::IViewportHostService;
|
||||
using XCEngine::Math::Vector2;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UISize;
|
||||
|
||||
class StubSelectionManager : public ISelectionManager {
|
||||
public:
|
||||
@@ -121,6 +123,8 @@ public:
|
||||
const std::string& GetCurrentSceneName() const override { return empty; }
|
||||
XCEngine::Components::Scene* GetScene() override { return nullptr; }
|
||||
const XCEngine::Components::Scene* GetScene() const override { return nullptr; }
|
||||
XCEngine::Editor::SceneLoadProgressSnapshot GetSceneLoadProgress() const override { return {}; }
|
||||
void NotifySceneViewportFramePresented(std::uint32_t) override {}
|
||||
SceneSnapshot CaptureSceneSnapshot() const override { return {}; }
|
||||
bool RestoreSceneSnapshot(const SceneSnapshot&) override { return false; }
|
||||
void CreateDemoScene() override {}
|
||||
@@ -187,9 +191,9 @@ public:
|
||||
void BeginFrame() override {}
|
||||
XCEngine::Editor::EditorViewportFrame RequestViewport(
|
||||
XCEngine::Editor::EditorViewportKind,
|
||||
const ImVec2&) override { return {}; }
|
||||
const UISize&) override { return {}; }
|
||||
void UpdateSceneViewInput(IEditorContext&, const XCEngine::Editor::SceneViewportInput&) override {}
|
||||
uint64_t PickSceneViewEntity(IEditorContext&, const ImVec2&, const ImVec2&) override {
|
||||
uint64_t PickSceneViewEntity(IEditorContext&, const UISize&, const UIPoint&) override {
|
||||
++pickCallCount;
|
||||
return pickedEntity;
|
||||
}
|
||||
@@ -285,7 +289,7 @@ TEST(SceneViewportInteractionActionsTest, DispatchOrientationActionAlignsViewpor
|
||||
actions,
|
||||
context,
|
||||
viewportHostService,
|
||||
ImVec2(200.0f, 100.0f),
|
||||
UISize(200.0f, 100.0f),
|
||||
Vector2(40.0f, 30.0f));
|
||||
|
||||
EXPECT_EQ(viewportHostService.alignedAxis, SceneViewportOrientationAxis::PositiveY);
|
||||
@@ -305,7 +309,7 @@ TEST(SceneViewportInteractionActionsTest, DispatchSceneIconClickSelectsEntityWit
|
||||
actions,
|
||||
context,
|
||||
viewportHostService,
|
||||
ImVec2(200.0f, 100.0f),
|
||||
UISize(200.0f, 100.0f),
|
||||
Vector2(40.0f, 30.0f));
|
||||
|
||||
EXPECT_EQ(context.selectionManager.selectedEntity, 42u);
|
||||
@@ -325,7 +329,7 @@ TEST(SceneViewportInteractionActionsTest, DispatchScenePickSelectsPickedEntityOr
|
||||
actions,
|
||||
context,
|
||||
viewportHostService,
|
||||
ImVec2(200.0f, 100.0f),
|
||||
UISize(200.0f, 100.0f),
|
||||
Vector2(40.0f, 30.0f));
|
||||
|
||||
EXPECT_EQ(context.selectionManager.selectedEntity, 77u);
|
||||
@@ -336,7 +340,7 @@ TEST(SceneViewportInteractionActionsTest, DispatchScenePickSelectsPickedEntityOr
|
||||
actions,
|
||||
context,
|
||||
viewportHostService,
|
||||
ImVec2(200.0f, 100.0f),
|
||||
UISize(200.0f, 100.0f),
|
||||
Vector2(40.0f, 30.0f));
|
||||
|
||||
EXPECT_EQ(context.selectionManager.selectedEntity, 0u);
|
||||
|
||||
@@ -44,6 +44,8 @@ using XCEngine::Editor::SceneViewportTransformGizmoOverlayState;
|
||||
using XCEngine::Editor::SceneViewportTransformSpaceMode;
|
||||
using XCEngine::Editor::ShouldFocusSceneViewportAfterInteraction;
|
||||
using XCEngine::Rendering::RenderContext;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UISize;
|
||||
|
||||
class EmptySelectionManager : public ISelectionManager {
|
||||
public:
|
||||
@@ -90,6 +92,8 @@ public:
|
||||
const std::string& GetCurrentSceneName() const override { return empty; }
|
||||
XCEngine::Components::Scene* GetScene() override { return nullptr; }
|
||||
const XCEngine::Components::Scene* GetScene() const override { return nullptr; }
|
||||
XCEngine::Editor::SceneLoadProgressSnapshot GetSceneLoadProgress() const override { return {}; }
|
||||
void NotifySceneViewportFramePresented(std::uint32_t) override {}
|
||||
SceneSnapshot CaptureSceneSnapshot() const override { return {}; }
|
||||
bool RestoreSceneSnapshot(const SceneSnapshot&) override { return false; }
|
||||
void CreateDemoScene() override {}
|
||||
@@ -180,9 +184,9 @@ public:
|
||||
class StubViewportHostService : public IViewportHostService {
|
||||
public:
|
||||
void BeginFrame() override {}
|
||||
EditorViewportFrame RequestViewport(EditorViewportKind, const ImVec2&) override { return {}; }
|
||||
EditorViewportFrame RequestViewport(EditorViewportKind, const UISize&) override { return {}; }
|
||||
void UpdateSceneViewInput(IEditorContext&, const SceneViewportInput&) override {}
|
||||
uint64_t PickSceneViewEntity(IEditorContext&, const ImVec2&, const ImVec2&) override { return 0; }
|
||||
uint64_t PickSceneViewEntity(IEditorContext&, const UISize&, const UIPoint&) override { return 0; }
|
||||
void AlignSceneViewToOrientationAxis(SceneViewportOrientationAxis) override {}
|
||||
SceneViewportOverlayData GetSceneViewOverlayData() const override { return overlay; }
|
||||
const SceneViewportOverlayFrameData& GetSceneViewEditorOverlayFrameData(IEditorContext&) override {
|
||||
|
||||
@@ -13,8 +13,6 @@ using XCEngine::Editor::SceneViewportNavigationState;
|
||||
using XCEngine::Editor::SceneViewportToolMode;
|
||||
using XCEngine::Editor::SceneViewportToolShortcutRequest;
|
||||
using XCEngine::Editor::UpdateSceneViewportNavigationState;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UISize;
|
||||
|
||||
TEST(SceneViewportNavigationTest, ToolShortcutActionIgnoresShortcutsDuringTextInputOrDrag) {
|
||||
SceneViewportToolShortcutRequest request = {};
|
||||
@@ -90,6 +88,22 @@ TEST(SceneViewportNavigationTest, NavigationUpdateBeginsLookAndPanDrags) {
|
||||
EXPECT_EQ(middlePanUpdate.state.panDragButton, ImGuiMouseButton_Middle);
|
||||
}
|
||||
|
||||
TEST(SceneViewportNavigationTest, NavigationUpdateBeginsMiddlePanWhenHeldButtonMissedClickEdge) {
|
||||
SceneViewportNavigationRequest middlePanRequest = {};
|
||||
middlePanRequest.hasInteractiveViewport = true;
|
||||
middlePanRequest.viewportHovered = true;
|
||||
middlePanRequest.middleMouseDown = true;
|
||||
|
||||
const auto middlePanUpdate = UpdateSceneViewportNavigationState(middlePanRequest);
|
||||
|
||||
EXPECT_FALSE(middlePanUpdate.beginLookDrag);
|
||||
EXPECT_TRUE(middlePanUpdate.beginPanDrag);
|
||||
EXPECT_FALSE(middlePanUpdate.beginLeftPanDrag);
|
||||
EXPECT_TRUE(middlePanUpdate.beginMiddlePanDrag);
|
||||
EXPECT_TRUE(middlePanUpdate.state.panDragging);
|
||||
EXPECT_EQ(middlePanUpdate.state.panDragButton, ImGuiMouseButton_Middle);
|
||||
}
|
||||
|
||||
TEST(SceneViewportNavigationTest, NavigationUpdateEndsDragsWhenButtonsRelease) {
|
||||
SceneViewportNavigationRequest lookRequest = {};
|
||||
lookRequest.state.lookDragging = true;
|
||||
@@ -120,7 +134,7 @@ TEST(SceneViewportNavigationTest, CaptureFlagsTrackNavigationAndActiveGizmos) {
|
||||
|
||||
TEST(SceneViewportNavigationTest, BuildInputRoutesWheelFocusMovementAndMouseDelta) {
|
||||
SceneViewportInputBuildRequest request = {};
|
||||
request.viewportSize = UISize(640.0f, 360.0f);
|
||||
request.viewportSize = XCEngine::UI::UISize(640.0f, 360.0f);
|
||||
request.viewportHovered = true;
|
||||
request.viewportFocused = true;
|
||||
request.mouseWheel = 2.0f;
|
||||
@@ -132,10 +146,10 @@ TEST(SceneViewportNavigationTest, BuildInputRoutesWheelFocusMovementAndMouseDelt
|
||||
|
||||
request = {};
|
||||
request.state.lookDragging = true;
|
||||
request.viewportSize = UISize(640.0f, 360.0f);
|
||||
request.viewportSize = XCEngine::UI::UISize(640.0f, 360.0f);
|
||||
request.viewportHovered = true;
|
||||
request.mouseWheel = 1.5f;
|
||||
request.mouseDelta = UIPoint(5.0f, -3.0f);
|
||||
request.mouseDelta = XCEngine::UI::UIPoint(5.0f, -3.0f);
|
||||
request.fastMove = true;
|
||||
request.focusSelectionKeyPressed = true;
|
||||
request.moveForwardKeyDown = true;
|
||||
|
||||
@@ -103,6 +103,20 @@ bool ContainsSpriteKind(
|
||||
});
|
||||
}
|
||||
|
||||
const SceneViewportOverlayLinePrimitive* FindLineStartingAt(
|
||||
const SceneViewportOverlayFrameData& frameData,
|
||||
const Math::Vector3& position) {
|
||||
const auto matchesPosition = [&position](const SceneViewportOverlayLinePrimitive& line) {
|
||||
return (line.startWorld - position).SqrMagnitude() <= 1e-4f;
|
||||
};
|
||||
|
||||
const auto it = std::find_if(
|
||||
frameData.worldLines.begin(),
|
||||
frameData.worldLines.end(),
|
||||
matchesPosition);
|
||||
return it != frameData.worldLines.end() ? &(*it) : nullptr;
|
||||
}
|
||||
|
||||
TEST(SceneViewportOverlayProviderRegistryTest, AppendsProvidersInRegistrationOrder) {
|
||||
EditorContext context;
|
||||
context.GetSceneManager().NewScene("Overlay Provider Registry");
|
||||
@@ -186,10 +200,89 @@ TEST(SceneViewportOverlayProviderRegistryTest, LightProviderBuildsSceneIconAndSe
|
||||
provider->AppendOverlay(buildContext, frameData);
|
||||
|
||||
ASSERT_EQ(frameData.worldSprites.size(), 1u);
|
||||
EXPECT_EQ(frameData.worldSprites[0].textureKind, SceneViewportOverlaySpriteTextureKind::Light);
|
||||
EXPECT_EQ(frameData.worldSprites[0].textureKind, SceneViewportOverlaySpriteTextureKind::DirectionalLight);
|
||||
ASSERT_EQ(frameData.handleRecords.size(), 1u);
|
||||
EXPECT_EQ(frameData.handleRecords[0].entityId, lightEntity->GetID());
|
||||
EXPECT_GT(frameData.worldLines.size(), 0u);
|
||||
|
||||
const SceneViewportOverlayLinePrimitive* connectorLine =
|
||||
FindLineStartingAt(frameData, lightEntity->GetTransform()->GetPosition());
|
||||
ASSERT_NE(connectorLine, nullptr);
|
||||
EXPECT_GT(connectorLine->endWorld.z, connectorLine->startWorld.z);
|
||||
}
|
||||
|
||||
TEST(SceneViewportOverlayProviderRegistryTest, LightProviderBuildsSelectedPointLightHelper) {
|
||||
EditorContext context;
|
||||
context.GetSceneManager().NewScene("Point Light Overlay Provider");
|
||||
|
||||
auto* lightEntity = context.GetSceneManager().CreateEntity("PointLight");
|
||||
ASSERT_NE(lightEntity, nullptr);
|
||||
lightEntity->GetTransform()->SetPosition(Math::Vector3(0.0f, 0.0f, 12.0f));
|
||||
|
||||
auto* light = lightEntity->AddComponent<Components::LightComponent>();
|
||||
ASSERT_NE(light, nullptr);
|
||||
light->SetLightType(Components::LightType::Point);
|
||||
light->SetRange(3.5f);
|
||||
|
||||
const SceneViewportOverlayData overlay = CreateValidOverlay();
|
||||
const std::vector<uint64_t> selectedObjectIds = { lightEntity->GetID() };
|
||||
const SceneViewportOverlayBuildContext buildContext =
|
||||
CreateBuildContext(context, overlay, selectedObjectIds);
|
||||
|
||||
auto provider = CreateSceneViewportLightOverlayProvider();
|
||||
ASSERT_NE(provider, nullptr);
|
||||
|
||||
SceneViewportOverlayFrameData frameData = {};
|
||||
frameData.overlay = overlay;
|
||||
provider->AppendOverlay(buildContext, frameData);
|
||||
|
||||
ASSERT_EQ(frameData.worldSprites.size(), 1u);
|
||||
EXPECT_EQ(frameData.worldSprites[0].textureKind, SceneViewportOverlaySpriteTextureKind::PointLight);
|
||||
ASSERT_EQ(frameData.handleRecords.size(), 1u);
|
||||
EXPECT_EQ(frameData.handleRecords[0].entityId, lightEntity->GetID());
|
||||
EXPECT_EQ(frameData.worldLines.size(), 96u);
|
||||
EXPECT_NEAR(
|
||||
(frameData.worldLines[0].startWorld - lightEntity->GetTransform()->GetPosition()).Magnitude(),
|
||||
light->GetRange(),
|
||||
1e-3f);
|
||||
}
|
||||
|
||||
TEST(SceneViewportOverlayProviderRegistryTest, LightProviderBuildsSelectedSpotLightHelper) {
|
||||
EditorContext context;
|
||||
context.GetSceneManager().NewScene("Spot Light Overlay Provider");
|
||||
|
||||
auto* lightEntity = context.GetSceneManager().CreateEntity("SpotLight");
|
||||
ASSERT_NE(lightEntity, nullptr);
|
||||
lightEntity->GetTransform()->SetPosition(Math::Vector3(0.0f, 0.0f, 6.0f));
|
||||
|
||||
auto* light = lightEntity->AddComponent<Components::LightComponent>();
|
||||
ASSERT_NE(light, nullptr);
|
||||
light->SetLightType(Components::LightType::Spot);
|
||||
light->SetRange(4.0f);
|
||||
light->SetSpotAngle(30.0f);
|
||||
|
||||
const SceneViewportOverlayData overlay = CreateValidOverlay();
|
||||
const std::vector<uint64_t> selectedObjectIds = { lightEntity->GetID() };
|
||||
const SceneViewportOverlayBuildContext buildContext =
|
||||
CreateBuildContext(context, overlay, selectedObjectIds);
|
||||
|
||||
auto provider = CreateSceneViewportLightOverlayProvider();
|
||||
ASSERT_NE(provider, nullptr);
|
||||
|
||||
SceneViewportOverlayFrameData frameData = {};
|
||||
frameData.overlay = overlay;
|
||||
provider->AppendOverlay(buildContext, frameData);
|
||||
|
||||
ASSERT_EQ(frameData.worldSprites.size(), 1u);
|
||||
EXPECT_EQ(frameData.worldSprites[0].textureKind, SceneViewportOverlaySpriteTextureKind::SpotLight);
|
||||
ASSERT_EQ(frameData.handleRecords.size(), 1u);
|
||||
EXPECT_EQ(frameData.handleRecords[0].entityId, lightEntity->GetID());
|
||||
EXPECT_EQ(frameData.worldLines.size(), 37u);
|
||||
|
||||
const SceneViewportOverlayLinePrimitive* connectorLine =
|
||||
FindLineStartingAt(frameData, lightEntity->GetTransform()->GetPosition());
|
||||
ASSERT_NE(connectorLine, nullptr);
|
||||
EXPECT_GT(connectorLine->endWorld.z, connectorLine->startWorld.z);
|
||||
}
|
||||
|
||||
TEST(SceneViewportOverlayProviderRegistryTest, TransformGizmoProviderBuildsOverlayFromFormalState) {
|
||||
@@ -247,7 +340,7 @@ TEST(
|
||||
EXPECT_EQ(frameData.worldSprites.size(), 2u);
|
||||
EXPECT_EQ(frameData.handleRecords.size(), 2u);
|
||||
EXPECT_TRUE(ContainsSpriteKind(frameData, SceneViewportOverlaySpriteTextureKind::Camera));
|
||||
EXPECT_TRUE(ContainsSpriteKind(frameData, SceneViewportOverlaySpriteTextureKind::Light));
|
||||
EXPECT_TRUE(ContainsSpriteKind(frameData, SceneViewportOverlaySpriteTextureKind::DirectionalLight));
|
||||
EXPECT_GT(frameData.worldLines.size(), 12u);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,9 @@ using XCEngine::Editor::BuildSceneViewportHudOverlayData;
|
||||
using XCEngine::Editor::BuildSceneViewportViewMatrix;
|
||||
using XCEngine::Editor::HitTestSceneViewportHudOverlay;
|
||||
using XCEngine::Editor::ProjectSceneViewportWorldPoint;
|
||||
using XCEngine::Editor::SceneViewportOverlayFrameData;
|
||||
using XCEngine::Editor::SceneViewportOverlaySpritePrimitive;
|
||||
using XCEngine::Editor::SceneViewportOverlaySpriteTextureKind;
|
||||
using XCEngine::Editor::SceneViewportOverlayData;
|
||||
using XCEngine::Components::GameObject;
|
||||
using XCEngine::Math::Vector3;
|
||||
@@ -284,6 +287,26 @@ TEST(SceneViewportOverlayRenderer_Test, BuildSceneViewportHudOverlayDataTracksVi
|
||||
EXPECT_FALSE(hiddenHud.HasVisibleElements());
|
||||
}
|
||||
|
||||
TEST(SceneViewportOverlayRenderer_Test, BuildSceneViewportHudOverlayDataCanExposeSceneIconsWithoutOrientationGizmo) {
|
||||
SceneViewportOverlayData overlay = {};
|
||||
overlay.valid = true;
|
||||
|
||||
SceneViewportOverlayFrameData frameData = {};
|
||||
frameData.overlay = overlay;
|
||||
SceneViewportOverlaySpritePrimitive& sprite = frameData.worldSprites.emplace_back();
|
||||
sprite.worldPosition = Vector3::Zero();
|
||||
sprite.sizePixels = XCEngine::Math::Vector2(32.0f, 32.0f);
|
||||
sprite.textureKind = SceneViewportOverlaySpriteTextureKind::Camera;
|
||||
|
||||
const auto iconsOnlyHud = BuildSceneViewportHudOverlayData(
|
||||
overlay,
|
||||
false,
|
||||
&frameData,
|
||||
true);
|
||||
|
||||
EXPECT_TRUE(iconsOnlyHud.HasVisibleElements());
|
||||
}
|
||||
|
||||
TEST(SceneViewportOverlayRenderer_Test, HitTestSceneViewportHudOverlaySkipsInvalidOrHiddenOverlay) {
|
||||
const SceneViewportHudOverlayHitResult invalidHit =
|
||||
HitTestSceneViewportHudOverlay({}, ImVec2(0.0f, 0.0f), ImVec2(200.0f, 200.0f), ImVec2(100.0f, 100.0f));
|
||||
|
||||
@@ -17,11 +17,23 @@ using XCEngine::Editor::kSceneViewportOverlaySpriteResourceCount;
|
||||
using XCEngine::Editor::kSceneViewportOverlaySpriteTextureKinds;
|
||||
|
||||
TEST(SceneViewportOverlaySpriteResourcesTest, TextureKindIndexMappingIsStable) {
|
||||
EXPECT_EQ(kSceneViewportOverlaySpriteResourceCount, 2u);
|
||||
EXPECT_EQ(kSceneViewportOverlaySpriteResourceCount, 4u);
|
||||
EXPECT_EQ(GetSceneViewportOverlaySpriteResourceIndex(SceneViewportOverlaySpriteTextureKind::Camera), 0u);
|
||||
EXPECT_EQ(GetSceneViewportOverlaySpriteResourceIndex(SceneViewportOverlaySpriteTextureKind::Light), 1u);
|
||||
EXPECT_EQ(
|
||||
GetSceneViewportOverlaySpriteResourceIndex(SceneViewportOverlaySpriteTextureKind::DirectionalLight),
|
||||
1u);
|
||||
EXPECT_EQ(GetSceneViewportOverlaySpriteResourceIndex(SceneViewportOverlaySpriteTextureKind::PointLight), 2u);
|
||||
EXPECT_EQ(GetSceneViewportOverlaySpriteResourceIndex(SceneViewportOverlaySpriteTextureKind::SpotLight), 3u);
|
||||
EXPECT_EQ(GetSceneViewportOverlaySpriteTextureKindByIndex(0u), SceneViewportOverlaySpriteTextureKind::Camera);
|
||||
EXPECT_EQ(GetSceneViewportOverlaySpriteTextureKindByIndex(1u), SceneViewportOverlaySpriteTextureKind::Light);
|
||||
EXPECT_EQ(
|
||||
GetSceneViewportOverlaySpriteTextureKindByIndex(1u),
|
||||
SceneViewportOverlaySpriteTextureKind::DirectionalLight);
|
||||
EXPECT_EQ(
|
||||
GetSceneViewportOverlaySpriteTextureKindByIndex(2u),
|
||||
SceneViewportOverlaySpriteTextureKind::PointLight);
|
||||
EXPECT_EQ(
|
||||
GetSceneViewportOverlaySpriteTextureKindByIndex(3u),
|
||||
SceneViewportOverlaySpriteTextureKind::SpotLight);
|
||||
}
|
||||
|
||||
TEST(SceneViewportOverlaySpriteResourcesTest, AssetSpecsResolveKnownEditorIcons) {
|
||||
@@ -34,6 +46,24 @@ TEST(SceneViewportOverlaySpriteResourcesTest, AssetSpecsResolveKnownEditorIcons)
|
||||
EXPECT_TRUE(path.is_absolute());
|
||||
EXPECT_TRUE(std::filesystem::exists(path));
|
||||
EXPECT_NE(path.generic_string().find("editor/resources/Icons"), std::string::npos);
|
||||
|
||||
switch (textureKind) {
|
||||
case SceneViewportOverlaySpriteTextureKind::Camera:
|
||||
EXPECT_EQ(path.filename().generic_string(), "camera_gizmo.png");
|
||||
break;
|
||||
case SceneViewportOverlaySpriteTextureKind::DirectionalLight:
|
||||
EXPECT_EQ(path.filename().generic_string(), "directional_light_gizmo.png");
|
||||
break;
|
||||
case SceneViewportOverlaySpriteTextureKind::PointLight:
|
||||
EXPECT_EQ(path.filename().generic_string(), "point_light_gizmo.png");
|
||||
break;
|
||||
case SceneViewportOverlaySpriteTextureKind::SpotLight:
|
||||
EXPECT_EQ(path.filename().generic_string(), "spot_light_gizmo.png");
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Unexpected texture kind";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Viewport/SceneViewportShaderPaths.h"
|
||||
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
|
||||
#include <filesystem>
|
||||
@@ -12,9 +13,11 @@
|
||||
namespace {
|
||||
|
||||
using XCEngine::Editor::GetSceneViewportCameraGizmoIconPath;
|
||||
using XCEngine::Editor::GetSceneViewportDirectionalLightGizmoIconPath;
|
||||
using XCEngine::Editor::GetSceneViewportInfiniteGridShaderPath;
|
||||
using XCEngine::Editor::GetSceneViewportMainLightGizmoIconPath;
|
||||
using XCEngine::Editor::GetSceneViewportObjectIdOutlineShaderPath;
|
||||
using XCEngine::Editor::GetSceneViewportPointLightGizmoIconPath;
|
||||
using XCEngine::Editor::GetSceneViewportSpotLightGizmoIconPath;
|
||||
using XCEngine::Resources::GetBuiltinObjectIdOutlineShaderPath;
|
||||
using XCEngine::Resources::LoadResult;
|
||||
using XCEngine::Resources::ResourceHandle;
|
||||
using XCEngine::Resources::ResourceManager;
|
||||
@@ -27,22 +30,29 @@ using XCEngine::Resources::ShaderType;
|
||||
|
||||
TEST(SceneViewportShaderPathsTest, ResolvePathsUnderEditorResources) {
|
||||
const std::filesystem::path gridPath(GetSceneViewportInfiniteGridShaderPath().CStr());
|
||||
const std::filesystem::path outlinePath(GetSceneViewportObjectIdOutlineShaderPath().CStr());
|
||||
const std::filesystem::path cameraIconPath(GetSceneViewportCameraGizmoIconPath().CStr());
|
||||
const std::filesystem::path lightIconPath(GetSceneViewportMainLightGizmoIconPath().CStr());
|
||||
const std::filesystem::path directionalLightIconPath(GetSceneViewportDirectionalLightGizmoIconPath().CStr());
|
||||
const std::filesystem::path pointLightIconPath(GetSceneViewportPointLightGizmoIconPath().CStr());
|
||||
const std::filesystem::path spotLightIconPath(GetSceneViewportSpotLightGizmoIconPath().CStr());
|
||||
|
||||
EXPECT_TRUE(gridPath.is_absolute());
|
||||
EXPECT_TRUE(outlinePath.is_absolute());
|
||||
EXPECT_TRUE(cameraIconPath.is_absolute());
|
||||
EXPECT_TRUE(lightIconPath.is_absolute());
|
||||
EXPECT_TRUE(directionalLightIconPath.is_absolute());
|
||||
EXPECT_TRUE(pointLightIconPath.is_absolute());
|
||||
EXPECT_TRUE(spotLightIconPath.is_absolute());
|
||||
EXPECT_TRUE(std::filesystem::exists(gridPath));
|
||||
EXPECT_TRUE(std::filesystem::exists(outlinePath));
|
||||
EXPECT_TRUE(std::filesystem::exists(cameraIconPath));
|
||||
EXPECT_TRUE(std::filesystem::exists(lightIconPath));
|
||||
EXPECT_TRUE(std::filesystem::exists(directionalLightIconPath));
|
||||
EXPECT_TRUE(std::filesystem::exists(pointLightIconPath));
|
||||
EXPECT_TRUE(std::filesystem::exists(spotLightIconPath));
|
||||
EXPECT_NE(gridPath.generic_string().find("editor/resources/shaders/scene-viewport"), std::string::npos);
|
||||
EXPECT_NE(outlinePath.generic_string().find("editor/resources/shaders/scene-viewport"), std::string::npos);
|
||||
EXPECT_NE(cameraIconPath.generic_string().find("editor/resources/Icons"), std::string::npos);
|
||||
EXPECT_NE(lightIconPath.generic_string().find("editor/resources/Icons"), std::string::npos);
|
||||
EXPECT_NE(directionalLightIconPath.generic_string().find("editor/resources/Icons"), std::string::npos);
|
||||
EXPECT_NE(pointLightIconPath.generic_string().find("editor/resources/Icons"), std::string::npos);
|
||||
EXPECT_NE(spotLightIconPath.generic_string().find("editor/resources/Icons"), std::string::npos);
|
||||
EXPECT_EQ(directionalLightIconPath.filename().generic_string(), "directional_light_gizmo.png");
|
||||
EXPECT_EQ(pointLightIconPath.filename().generic_string(), "point_light_gizmo.png");
|
||||
EXPECT_EQ(spotLightIconPath.filename().generic_string(), "spot_light_gizmo.png");
|
||||
}
|
||||
|
||||
TEST(SceneViewportShaderPathsTest, ShaderLoaderLoadsSceneViewportInfiniteGridShader) {
|
||||
@@ -81,7 +91,7 @@ TEST(SceneViewportShaderPathsTest, ResourceManagerLoadsSceneViewportOutlineShade
|
||||
ResourceManager& manager = ResourceManager::Get();
|
||||
manager.Shutdown();
|
||||
|
||||
const ResourceHandle<Shader> shaderHandle = manager.Load<Shader>(GetSceneViewportObjectIdOutlineShaderPath());
|
||||
const ResourceHandle<Shader> shaderHandle = manager.Load<Shader>(GetBuiltinObjectIdOutlineShaderPath());
|
||||
ASSERT_TRUE(shaderHandle.IsValid());
|
||||
|
||||
const ShaderPass* pass = shaderHandle->FindPass("ObjectIdOutline");
|
||||
@@ -94,7 +104,7 @@ TEST(SceneViewportShaderPathsTest, ResourceManagerLoadsSceneViewportOutlineShade
|
||||
ShaderBackend::D3D12);
|
||||
ASSERT_NE(fragment, nullptr);
|
||||
EXPECT_NE(
|
||||
std::string(fragment->sourceCode.CStr()).find("XC_EDITOR_SCENE_VIEW_OBJECT_ID_OUTLINE_D3D12_PS"),
|
||||
std::string(fragment->sourceCode.CStr()).find("XC_BUILTIN_OBJECT_ID_OUTLINE_D3D12_PS"),
|
||||
std::string::npos);
|
||||
|
||||
manager.Shutdown();
|
||||
|
||||
@@ -48,6 +48,8 @@ using XCEngine::Editor::SubmitSceneViewportTransformGizmoOverlaySubmission;
|
||||
using XCEngine::Editor::SceneSnapshot;
|
||||
using XCEngine::Rendering::RenderContext;
|
||||
using XCEngine::Math::Vector2;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UISize;
|
||||
|
||||
class StubSelectionManager : public ISelectionManager {
|
||||
public:
|
||||
@@ -134,6 +136,8 @@ public:
|
||||
const std::string& GetCurrentSceneName() const override { return empty; }
|
||||
XCEngine::Components::Scene* GetScene() override { return nullptr; }
|
||||
const XCEngine::Components::Scene* GetScene() const override { return nullptr; }
|
||||
XCEngine::Editor::SceneLoadProgressSnapshot GetSceneLoadProgress() const override { return {}; }
|
||||
void NotifySceneViewportFramePresented(std::uint32_t) override {}
|
||||
SceneSnapshot CaptureSceneSnapshot() const override { return {}; }
|
||||
bool RestoreSceneSnapshot(const SceneSnapshot&) override { return false; }
|
||||
void CreateDemoScene() override {}
|
||||
@@ -226,9 +230,9 @@ public:
|
||||
class StubViewportHostService : public IViewportHostService {
|
||||
public:
|
||||
void BeginFrame() override {}
|
||||
EditorViewportFrame RequestViewport(EditorViewportKind, const ImVec2&) override { return {}; }
|
||||
EditorViewportFrame RequestViewport(EditorViewportKind, const UISize&) override { return {}; }
|
||||
void UpdateSceneViewInput(IEditorContext&, const SceneViewportInput&) override {}
|
||||
uint64_t PickSceneViewEntity(IEditorContext&, const ImVec2&, const ImVec2&) override { return 0; }
|
||||
uint64_t PickSceneViewEntity(IEditorContext&, const UISize&, const UIPoint&) override { return 0; }
|
||||
void AlignSceneViewToOrientationAxis(SceneViewportOrientationAxis) override {}
|
||||
SceneViewportOverlayData GetSceneViewOverlayData() const override { return {}; }
|
||||
const SceneViewportOverlayFrameData& GetSceneViewEditorOverlayFrameData(IEditorContext&) override {
|
||||
|
||||
@@ -17,6 +17,8 @@ using XCEngine::Editor::ViewportObjectIdReadbackRequest;
|
||||
using XCEngine::RHI::RHICommandQueue;
|
||||
using XCEngine::RHI::RHITexture;
|
||||
using XCEngine::RHI::ResourceStates;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UISize;
|
||||
|
||||
RHICommandQueue* MakeDummyQueue() {
|
||||
return reinterpret_cast<RHICommandQueue*>(static_cast<uintptr_t>(0x1));
|
||||
@@ -34,8 +36,8 @@ ViewportObjectIdPickContext CreateValidContext() {
|
||||
context.textureWidth = 1280;
|
||||
context.textureHeight = 720;
|
||||
context.hasValidFrame = true;
|
||||
context.viewportSize = ImVec2(1280.0f, 720.0f);
|
||||
context.viewportMousePosition = ImVec2(640.0f, 360.0f);
|
||||
context.viewportSize = UISize(1280.0f, 720.0f);
|
||||
context.viewportMousePosition = UIPoint(640.0f, 360.0f);
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -59,17 +61,17 @@ TEST(ViewportObjectIdPickerTest, CanPickRejectsMissingOrOutOfBoundsInputs) {
|
||||
EXPECT_FALSE(CanPickViewportObjectId(context));
|
||||
|
||||
context = CreateValidContext();
|
||||
context.viewportMousePosition = ImVec2(-1.0f, 10.0f);
|
||||
context.viewportMousePosition = UIPoint(-1.0f, 10.0f);
|
||||
EXPECT_FALSE(CanPickViewportObjectId(context));
|
||||
|
||||
context = CreateValidContext();
|
||||
context.viewportMousePosition = ImVec2(10.0f, 721.0f);
|
||||
context.viewportMousePosition = UIPoint(10.0f, 721.0f);
|
||||
EXPECT_FALSE(CanPickViewportObjectId(context));
|
||||
}
|
||||
|
||||
TEST(ViewportObjectIdPickerTest, BuildReadbackRequestMapsViewportCoordinatesToTexturePixels) {
|
||||
ViewportObjectIdPickContext context = CreateValidContext();
|
||||
context.viewportMousePosition = ImVec2(1280.0f, 720.0f);
|
||||
context.viewportMousePosition = UIPoint(1280.0f, 720.0f);
|
||||
|
||||
ViewportObjectIdReadbackRequest request = {};
|
||||
ASSERT_TRUE(BuildViewportObjectIdReadbackRequest(context, request));
|
||||
|
||||
@@ -299,6 +299,41 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsPostSceneA
|
||||
EXPECT_EQ(result.warningStatusText, nullptr);
|
||||
}
|
||||
|
||||
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanSkipsRhiOverlayPassWhenFrameContainsOnlySceneIcons) {
|
||||
const SceneViewportOverlayData overlay = CreateValidOverlay();
|
||||
|
||||
SceneViewportOverlayFrameData editorOverlayFrameData = {};
|
||||
editorOverlayFrameData.overlay = overlay;
|
||||
auto& sprite = editorOverlayFrameData.worldSprites.emplace_back();
|
||||
sprite.worldPosition = XCEngine::Math::Vector3::Zero();
|
||||
sprite.sizePixels = XCEngine::Math::Vector2(32.0f, 32.0f);
|
||||
|
||||
size_t overlayFactoryCallCount = 0u;
|
||||
const auto result = BuildSceneViewportRenderPlan(
|
||||
{},
|
||||
overlay,
|
||||
{},
|
||||
editorOverlayFrameData,
|
||||
[](const SceneViewportGridPassData&) {
|
||||
return std::make_unique<NoopRenderPass>();
|
||||
},
|
||||
[](
|
||||
RHIResourceView*,
|
||||
const std::vector<uint64_t>&,
|
||||
const SceneViewportSelectionOutlineStyle&) {
|
||||
return std::make_unique<NoopRenderPass>();
|
||||
},
|
||||
[&overlayFactoryCallCount](const SceneViewportOverlayFrameData&) {
|
||||
++overlayFactoryCallCount;
|
||||
return std::make_unique<NoopRenderPass>();
|
||||
},
|
||||
false);
|
||||
|
||||
EXPECT_EQ(result.plan.postScenePasses.GetPassCount(), 1u);
|
||||
EXPECT_EQ(result.plan.overlayPasses.GetPassCount(), 0u);
|
||||
EXPECT_EQ(overlayFactoryCallCount, 0u);
|
||||
}
|
||||
|
||||
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanWarnsWhenSelectionOutlineCannotAccessObjectIdTexture) {
|
||||
const SceneViewportOverlayData overlay = CreateValidOverlay();
|
||||
|
||||
|
||||
@@ -81,7 +81,9 @@ TEST(ViewportRenderTargetsTest, BuildReuseQueryReflectsCurrentResourcePresence)
|
||||
targets.objectIdTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0x5));
|
||||
targets.objectIdView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x6));
|
||||
targets.objectIdShaderView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x7));
|
||||
targets.textureId = static_cast<ImTextureID>(static_cast<uintptr_t>(0x8));
|
||||
targets.textureHandle.nativeHandle = 0x8;
|
||||
targets.textureHandle.width = 1280;
|
||||
targets.textureHandle.height = 720;
|
||||
|
||||
const auto query =
|
||||
BuildViewportRenderTargetsReuseQuery(EditorViewportKind::Scene, targets, 1280, 720);
|
||||
@@ -146,7 +148,9 @@ TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsSt
|
||||
targets.objectIdShaderView = objectIdShaderView;
|
||||
targets.imguiCpuHandle.ptr = 123;
|
||||
targets.imguiGpuHandle.ptr = 456;
|
||||
targets.textureId = static_cast<ImTextureID>(static_cast<uintptr_t>(789));
|
||||
targets.textureHandle.nativeHandle = 789;
|
||||
targets.textureHandle.width = 640;
|
||||
targets.textureHandle.height = 360;
|
||||
targets.colorState = ResourceStates::RenderTarget;
|
||||
targets.objectIdState = ResourceStates::PixelShaderResource;
|
||||
targets.hasValidObjectIdFrame = true;
|
||||
@@ -171,7 +175,9 @@ TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsSt
|
||||
EXPECT_EQ(targets.objectIdShaderView, nullptr);
|
||||
EXPECT_EQ(targets.imguiCpuHandle.ptr, 0u);
|
||||
EXPECT_EQ(targets.imguiGpuHandle.ptr, 0u);
|
||||
EXPECT_EQ(targets.textureId, ImTextureID{});
|
||||
EXPECT_EQ(targets.textureHandle.nativeHandle, 0u);
|
||||
EXPECT_EQ(targets.textureHandle.width, 0u);
|
||||
EXPECT_EQ(targets.textureHandle.height, 0u);
|
||||
EXPECT_EQ(targets.colorState, ResourceStates::Common);
|
||||
EXPECT_EQ(targets.objectIdState, ResourceStates::Common);
|
||||
EXPECT_FALSE(targets.hasValidObjectIdFrame);
|
||||
|
||||
Reference in New Issue
Block a user