chore: checkpoint current workspace changes
This commit is contained in:
@@ -77,11 +77,19 @@ endif()
|
||||
|
||||
add_executable(editor_tests ${EDITOR_TEST_SOURCES})
|
||||
|
||||
add_executable(nahida_preview_regenerator
|
||||
nahida_preview_regenerator.cpp
|
||||
${CMAKE_SOURCE_DIR}/editor/src/Core/UndoManager.cpp
|
||||
${CMAKE_SOURCE_DIR}/editor/src/Managers/SceneManager.cpp
|
||||
${CMAKE_SOURCE_DIR}/editor/src/Managers/ProjectManager.cpp
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
set_target_properties(editor_tests PROPERTIES
|
||||
LINK_FLAGS "/NODEFAULTLIB:libcpmt.lib /NODEFAULTLIB:libcmt.lib"
|
||||
)
|
||||
target_compile_options(editor_tests PRIVATE /FS /utf-8)
|
||||
target_compile_options(nahida_preview_regenerator PRIVATE /FS /utf-8)
|
||||
endif()
|
||||
|
||||
target_link_libraries(editor_tests PRIVATE
|
||||
@@ -92,6 +100,10 @@ target_link_libraries(editor_tests PRIVATE
|
||||
comdlg32
|
||||
)
|
||||
|
||||
target_link_libraries(nahida_preview_regenerator PRIVATE
|
||||
XCEngine
|
||||
)
|
||||
|
||||
target_include_directories(editor_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/editor/src
|
||||
@@ -100,6 +112,13 @@ target_include_directories(editor_tests PRIVATE
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
|
||||
)
|
||||
|
||||
target_include_directories(nahida_preview_regenerator PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/engine/include
|
||||
${CMAKE_SOURCE_DIR}/editor/src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src
|
||||
${CMAKE_BINARY_DIR}/_deps/imgui-src/backends
|
||||
)
|
||||
|
||||
file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}" XCENGINE_EDITOR_TEST_REPO_ROOT_CMAKE)
|
||||
file(TO_CMAKE_PATH "${XCENGINE_MONO_ROOT_DIR}" XCENGINE_EDITOR_TEST_MONO_ROOT_CMAKE)
|
||||
|
||||
@@ -108,6 +127,22 @@ target_compile_definitions(editor_tests PRIVATE
|
||||
XCENGINE_EDITOR_MONO_ROOT_DIR="${XCENGINE_EDITOR_TEST_MONO_ROOT_CMAKE}"
|
||||
)
|
||||
|
||||
target_compile_definitions(nahida_preview_regenerator PRIVATE
|
||||
XCENGINE_EDITOR_REPO_ROOT="${XCENGINE_EDITOR_TEST_REPO_ROOT_CMAKE}"
|
||||
)
|
||||
|
||||
add_custom_command(TARGET editor_tests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_SOURCE_DIR}/engine/third_party/assimp/bin/assimp-vc143-mt.dll
|
||||
$<TARGET_FILE_DIR:editor_tests>/assimp-vc143-mt.dll
|
||||
)
|
||||
|
||||
add_custom_command(TARGET nahida_preview_regenerator POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_SOURCE_DIR}/engine/third_party/assimp/bin/assimp-vc143-mt.dll
|
||||
$<TARGET_FILE_DIR:nahida_preview_regenerator>/assimp-vc143-mt.dll
|
||||
)
|
||||
|
||||
if(XCENGINE_ENABLE_MONO_SCRIPTING AND TARGET xcengine_managed_assemblies)
|
||||
add_dependencies(editor_tests xcengine_managed_assemblies)
|
||||
|
||||
|
||||
193
tests/editor/nahida_preview_regenerator.cpp
Normal file
193
tests/editor/nahida_preview_regenerator.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "Commands/ProjectCommands.h"
|
||||
#include "Core/EditorContext.h"
|
||||
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/Components/LightComponent.h>
|
||||
#include <XCEngine/Components/MeshFilterComponent.h>
|
||||
#include <XCEngine/Components/MeshRendererComponent.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Math/Color.h>
|
||||
#include <XCEngine/Core/Math/Quaternion.h>
|
||||
#include <XCEngine/Core/Math/Rect.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kDefaultModelAssetPath = "Assets/Models/nahida/Avatar_Loli_Catalyst_Nahida.fbx";
|
||||
constexpr const char* kDefaultSceneAssetPath = "Assets/Scenes/NahidaPreview.xc";
|
||||
constexpr const char* kDefaultSceneName = "Nahida Preview";
|
||||
|
||||
std::shared_ptr<XCEngine::Editor::AssetItem> MakeModelAssetItem(
|
||||
const fs::path& projectRoot,
|
||||
const std::string& modelAssetPath) {
|
||||
auto item = std::make_shared<XCEngine::Editor::AssetItem>();
|
||||
item->name = fs::path(modelAssetPath).filename().string();
|
||||
item->type = "Model";
|
||||
item->isFolder = false;
|
||||
item->fullPath = (projectRoot / fs::path(modelAssetPath)).string();
|
||||
return item;
|
||||
}
|
||||
|
||||
void ConfigurePreviewCamera(XCEngine::Components::GameObject& gameObject) {
|
||||
using namespace XCEngine;
|
||||
|
||||
gameObject.GetTransform()->SetLocalPosition(Math::Vector3(0.0f, 1.2f, -4.25f));
|
||||
gameObject.GetTransform()->SetLocalRotation(Math::Quaternion(0.104528f, 0.0f, 0.0f, 0.994522f));
|
||||
|
||||
auto* camera = gameObject.AddComponent<Components::CameraComponent>();
|
||||
camera->SetProjectionType(Components::CameraProjectionType::Perspective);
|
||||
camera->SetFieldOfView(35.0f);
|
||||
camera->SetNearClipPlane(0.01f);
|
||||
camera->SetFarClipPlane(100.0f);
|
||||
camera->SetDepth(0.0f);
|
||||
camera->SetPrimary(true);
|
||||
camera->SetClearMode(Components::CameraClearMode::Auto);
|
||||
camera->SetStackType(Components::CameraStackType::Base);
|
||||
camera->SetCullingMask(0xFFFFFFFFu);
|
||||
camera->SetViewportRect(Math::Rect(0.0f, 0.0f, 1.0f, 1.0f));
|
||||
camera->SetClearColor(Math::Color(0.04f, 0.05f, 0.07f, 1.0f));
|
||||
camera->SetSkyboxEnabled(false);
|
||||
camera->SetSkyboxTopColor(Math::Color(0.18f, 0.36f, 0.74f, 1.0f));
|
||||
camera->SetSkyboxHorizonColor(Math::Color(0.78f, 0.84f, 0.92f, 1.0f));
|
||||
camera->SetSkyboxBottomColor(Math::Color(0.92f, 0.93f, 0.95f, 1.0f));
|
||||
}
|
||||
|
||||
void ConfigureKeyLight(XCEngine::Components::GameObject& gameObject) {
|
||||
using namespace XCEngine;
|
||||
|
||||
gameObject.GetTransform()->SetLocalPosition(Math::Vector3(2.5f, 3.0f, -2.0f));
|
||||
gameObject.GetTransform()->SetLocalRotation(Math::Quaternion(0.21644f, -0.39404f, 0.09198f, 0.88755f));
|
||||
|
||||
auto* light = gameObject.AddComponent<Components::LightComponent>();
|
||||
light->SetLightType(Components::LightType::Directional);
|
||||
light->SetColor(Math::Color(1.0f, 0.976f, 0.94f, 1.0f));
|
||||
light->SetIntensity(1.4f);
|
||||
light->SetRange(10.0f);
|
||||
light->SetSpotAngle(30.0f);
|
||||
light->SetCastsShadows(true);
|
||||
}
|
||||
|
||||
void ConfigureFillLight(XCEngine::Components::GameObject& gameObject) {
|
||||
using namespace XCEngine;
|
||||
|
||||
gameObject.GetTransform()->SetLocalPosition(Math::Vector3(-1.75f, 1.5f, -1.25f));
|
||||
gameObject.GetTransform()->SetLocalRotation(Math::Quaternion::Identity());
|
||||
|
||||
auto* light = gameObject.AddComponent<Components::LightComponent>();
|
||||
light->SetLightType(Components::LightType::Point);
|
||||
light->SetColor(Math::Color(0.24f, 0.32f, 0.5f, 1.0f));
|
||||
light->SetIntensity(0.35f);
|
||||
light->SetRange(10.0f);
|
||||
light->SetSpotAngle(30.0f);
|
||||
light->SetCastsShadows(false);
|
||||
}
|
||||
|
||||
void ConfigureGround(XCEngine::Components::GameObject& gameObject) {
|
||||
using namespace XCEngine;
|
||||
|
||||
gameObject.GetTransform()->SetLocalPosition(Math::Vector3::Zero());
|
||||
gameObject.GetTransform()->SetLocalRotation(Math::Quaternion::Identity());
|
||||
gameObject.GetTransform()->SetLocalScale(Math::Vector3(8.0f, 1.0f, 8.0f));
|
||||
|
||||
auto* meshFilter = gameObject.AddComponent<Components::MeshFilterComponent>();
|
||||
meshFilter->SetMeshPath("builtin://meshes/plane");
|
||||
|
||||
auto* meshRenderer = gameObject.AddComponent<Components::MeshRendererComponent>();
|
||||
meshRenderer->SetMaterialPath(0, "builtin://materials/default-primitive");
|
||||
meshRenderer->SetCastShadows(true);
|
||||
meshRenderer->SetReceiveShadows(true);
|
||||
meshRenderer->SetRenderLayer(0);
|
||||
}
|
||||
|
||||
std::string ParseArgValue(int argc, char** argv, const char* name, const char* fallback) {
|
||||
const std::string optionPrefix = std::string(name) + "=";
|
||||
for (int index = 1; index < argc; ++index) {
|
||||
const std::string argument = argv[index];
|
||||
if (argument.rfind(optionPrefix, 0) == 0) {
|
||||
return argument.substr(optionPrefix.size());
|
||||
}
|
||||
}
|
||||
|
||||
return fallback != nullptr ? std::string(fallback) : std::string();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
using namespace XCEngine;
|
||||
|
||||
const fs::path repoRoot(XCENGINE_EDITOR_REPO_ROOT);
|
||||
const std::string projectArg = ParseArgValue(argc, argv, "--project-root", nullptr);
|
||||
const fs::path projectRoot = projectArg.empty() ? (repoRoot / "project") : fs::path(projectArg);
|
||||
const std::string modelAssetPath =
|
||||
ParseArgValue(argc, argv, "--model-asset", kDefaultModelAssetPath);
|
||||
const std::string sceneAssetPath =
|
||||
ParseArgValue(argc, argv, "--scene-asset", kDefaultSceneAssetPath);
|
||||
const fs::path sceneFilePath = projectRoot / fs::path(sceneAssetPath);
|
||||
|
||||
if (!fs::exists(projectRoot) || !fs::is_directory(projectRoot)) {
|
||||
std::cerr << "Project root does not exist: " << projectRoot << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Editor::EditorContext context;
|
||||
context.SetProjectPath(projectRoot.string());
|
||||
context.GetProjectManager().Initialize(projectRoot.string());
|
||||
context.GetSceneManager().NewScene(kDefaultSceneName);
|
||||
|
||||
auto& resourceManager = Resources::ResourceManager::Get();
|
||||
resourceManager.Initialize();
|
||||
resourceManager.SetResourceRoot(projectRoot.string().c_str());
|
||||
|
||||
auto* camera = context.GetSceneManager().CreateEntity("Preview Camera");
|
||||
auto* keyLight = context.GetSceneManager().CreateEntity("Key Light");
|
||||
auto* fillLight = context.GetSceneManager().CreateEntity("Fill Light");
|
||||
auto* ground = context.GetSceneManager().CreateEntity("Ground");
|
||||
auto* avatarRoot = context.GetSceneManager().CreateEntity("AvatarRoot");
|
||||
if (camera == nullptr || keyLight == nullptr || fillLight == nullptr || ground == nullptr || avatarRoot == nullptr) {
|
||||
std::cerr << "Failed to create preview scene entities." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ConfigurePreviewCamera(*camera);
|
||||
ConfigureKeyLight(*keyLight);
|
||||
ConfigureFillLight(*fillLight);
|
||||
ConfigureGround(*ground);
|
||||
avatarRoot->GetTransform()->SetLocalPosition(Math::Vector3::Zero());
|
||||
avatarRoot->GetTransform()->SetLocalRotation(Math::Quaternion::Identity());
|
||||
avatarRoot->GetTransform()->SetLocalScale(Math::Vector3::One());
|
||||
|
||||
const auto modelItem = MakeModelAssetItem(projectRoot, modelAssetPath);
|
||||
auto* createdRoot = Editor::Commands::InstantiateModelAsset(
|
||||
context,
|
||||
modelItem,
|
||||
avatarRoot,
|
||||
"Instantiate Nahida Preview Model");
|
||||
if (createdRoot == nullptr) {
|
||||
std::cerr << "Failed to instantiate model asset: " << modelAssetPath << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
createdRoot->SetName("NahidaUnityModel");
|
||||
|
||||
if (!context.GetSceneManager().SaveSceneAs(sceneFilePath.string())) {
|
||||
std::cerr << "Failed to save scene: " << sceneFilePath << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
resourceManager.UnloadAll();
|
||||
resourceManager.SetResourceRoot("");
|
||||
resourceManager.Shutdown();
|
||||
|
||||
std::cout << "Generated scene: " << sceneFilePath << std::endl;
|
||||
std::cout << "Model asset: " << modelAssetPath << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Actions/MainMenuActionRouter.h"
|
||||
#include "Actions/ProjectActionRouter.h"
|
||||
#include "Commands/EntityCommands.h"
|
||||
#include "Commands/ProjectCommands.h"
|
||||
#include "Commands/SceneCommands.h"
|
||||
#include "Core/EditorContext.h"
|
||||
#include "Core/PlaySessionController.h"
|
||||
@@ -15,6 +16,7 @@
|
||||
#include <XCEngine/Core/Math/Quaternion.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Model/Model.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
@@ -22,11 +24,41 @@
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace XCEngine::Editor {
|
||||
namespace {
|
||||
|
||||
fs::path GetRepositoryRoot() {
|
||||
return fs::path(XCENGINE_EDITOR_REPO_ROOT);
|
||||
}
|
||||
|
||||
std::string GetMeshFixturePath(const char* fileName) {
|
||||
return (GetRepositoryRoot() / "tests" / "Fixtures" / "Resources" / "Mesh" / fileName).string();
|
||||
}
|
||||
|
||||
void CopyTexturedTriangleFixture(const fs::path& assetsDir) {
|
||||
fs::copy_file(
|
||||
GetMeshFixturePath("textured_triangle.obj"),
|
||||
assetsDir / "textured_triangle.obj",
|
||||
fs::copy_options::overwrite_existing);
|
||||
fs::copy_file(
|
||||
GetMeshFixturePath("textured_triangle.mtl"),
|
||||
assetsDir / "textured_triangle.mtl",
|
||||
fs::copy_options::overwrite_existing);
|
||||
fs::copy_file(
|
||||
GetMeshFixturePath("checker.bmp"),
|
||||
assetsDir / "checker.bmp",
|
||||
fs::copy_options::overwrite_existing);
|
||||
}
|
||||
|
||||
bool DirectoryHasEntries(const fs::path& directoryPath) {
|
||||
std::error_code ec;
|
||||
if (!fs::exists(directoryPath, ec) || !fs::is_directory(directoryPath, ec)) {
|
||||
@@ -36,6 +68,18 @@ bool DirectoryHasEntries(const fs::path& directoryPath) {
|
||||
return fs::directory_iterator(directoryPath) != fs::directory_iterator();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
struct AssimpDllGuard {
|
||||
HMODULE module = nullptr;
|
||||
|
||||
~AssimpDllGuard() {
|
||||
if (module != nullptr) {
|
||||
FreeLibrary(module);
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class EditorActionRoutingTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
@@ -98,6 +142,26 @@ protected:
|
||||
return total;
|
||||
}
|
||||
|
||||
template <typename ComponentType>
|
||||
static ::XCEngine::Components::GameObject* FindFirstEntityWithComponent(
|
||||
::XCEngine::Components::GameObject* gameObject) {
|
||||
if (gameObject == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (gameObject->GetComponent<ComponentType>() != nullptr) {
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < gameObject->GetChildCount(); ++i) {
|
||||
if (auto* found = FindFirstEntityWithComponent<ComponentType>(gameObject->GetChild(i))) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EditorContext m_context;
|
||||
fs::path m_projectRoot;
|
||||
};
|
||||
@@ -223,6 +287,138 @@ TEST_F(EditorActionRoutingTest, ProjectRouteExecutesOpenBackAndDelete) {
|
||||
EXPECT_FALSE(fs::exists(filePath));
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, ProjectCommandsInstantiateModelAssetBuildsHierarchyAndSupportsUndoRedo) {
|
||||
using ::XCEngine::Resources::ResourceManager;
|
||||
|
||||
const fs::path assetsDir = m_projectRoot / "Assets";
|
||||
CopyTexturedTriangleFixture(assetsDir);
|
||||
m_context.GetProjectManager().RefreshCurrentFolder();
|
||||
|
||||
const AssetItemPtr modelItem = FindCurrentItemByName("textured_triangle.obj");
|
||||
ASSERT_NE(modelItem, nullptr);
|
||||
EXPECT_EQ(modelItem->type, "Model");
|
||||
EXPECT_TRUE(Commands::CanInstantiateModelAsset(m_context, modelItem));
|
||||
|
||||
#ifdef _WIN32
|
||||
AssimpDllGuard dllGuard;
|
||||
const fs::path assimpDllPath =
|
||||
GetRepositoryRoot() / "engine" / "third_party" / "assimp" / "bin" / "assimp-vc143-mt.dll";
|
||||
ASSERT_TRUE(fs::exists(assimpDllPath));
|
||||
dllGuard.module = LoadLibraryW(assimpDllPath.wstring().c_str());
|
||||
ASSERT_NE(dllGuard.module, nullptr);
|
||||
#endif
|
||||
|
||||
ResourceManager& resourceManager = ResourceManager::Get();
|
||||
resourceManager.Initialize();
|
||||
resourceManager.SetResourceRoot(m_projectRoot.string().c_str());
|
||||
|
||||
const size_t entityCountBeforeInstantiate = CountHierarchyEntities(m_context.GetSceneManager());
|
||||
auto* createdRoot = Commands::InstantiateModelAsset(m_context, modelItem);
|
||||
ASSERT_NE(createdRoot, nullptr);
|
||||
const uint64_t createdRootId = createdRoot->GetID();
|
||||
|
||||
const size_t entityCountAfterInstantiate = CountHierarchyEntities(m_context.GetSceneManager());
|
||||
EXPECT_GT(entityCountAfterInstantiate, entityCountBeforeInstantiate);
|
||||
EXPECT_EQ(m_context.GetSelectionManager().GetSelectedEntity(), createdRootId);
|
||||
EXPECT_TRUE(m_context.GetUndoManager().CanUndo());
|
||||
|
||||
auto* meshObject = FindFirstEntityWithComponent<::XCEngine::Components::MeshFilterComponent>(createdRoot);
|
||||
ASSERT_NE(meshObject, nullptr);
|
||||
auto* meshFilter = meshObject->GetComponent<::XCEngine::Components::MeshFilterComponent>();
|
||||
auto* meshRenderer = meshObject->GetComponent<::XCEngine::Components::MeshRendererComponent>();
|
||||
ASSERT_NE(meshFilter, nullptr);
|
||||
ASSERT_NE(meshRenderer, nullptr);
|
||||
EXPECT_TRUE(meshFilter->GetMeshAssetRef().IsValid());
|
||||
ASSERT_FALSE(meshRenderer->GetMaterialAssetRefs().empty());
|
||||
EXPECT_TRUE(meshRenderer->GetMaterialAssetRefs()[0].IsValid());
|
||||
|
||||
m_context.GetUndoManager().Undo();
|
||||
EXPECT_EQ(CountHierarchyEntities(m_context.GetSceneManager()), entityCountBeforeInstantiate);
|
||||
EXPECT_EQ(m_context.GetSceneManager().GetEntity(createdRootId), nullptr);
|
||||
EXPECT_FALSE(m_context.GetSelectionManager().HasSelection());
|
||||
|
||||
m_context.GetUndoManager().Redo();
|
||||
EXPECT_EQ(CountHierarchyEntities(m_context.GetSceneManager()), entityCountAfterInstantiate);
|
||||
EXPECT_EQ(m_context.GetSelectionManager().GetSelectedEntity(), createdRootId);
|
||||
|
||||
auto* restoredRoot = m_context.GetSceneManager().GetEntity(createdRootId);
|
||||
ASSERT_NE(restoredRoot, nullptr);
|
||||
auto* restoredMeshObject =
|
||||
FindFirstEntityWithComponent<::XCEngine::Components::MeshFilterComponent>(restoredRoot);
|
||||
ASSERT_NE(restoredMeshObject, nullptr);
|
||||
auto* restoredMeshFilter =
|
||||
restoredMeshObject->GetComponent<::XCEngine::Components::MeshFilterComponent>();
|
||||
auto* restoredMeshRenderer =
|
||||
restoredMeshObject->GetComponent<::XCEngine::Components::MeshRendererComponent>();
|
||||
ASSERT_NE(restoredMeshFilter, nullptr);
|
||||
ASSERT_NE(restoredMeshRenderer, nullptr);
|
||||
EXPECT_TRUE(restoredMeshFilter->GetMeshAssetRef().IsValid());
|
||||
ASSERT_FALSE(restoredMeshRenderer->GetMaterialAssetRefs().empty());
|
||||
EXPECT_TRUE(restoredMeshRenderer->GetMaterialAssetRefs()[0].IsValid());
|
||||
|
||||
resourceManager.UnloadAll();
|
||||
resourceManager.SetResourceRoot("");
|
||||
resourceManager.Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, ProjectCommandsInstantiateModelAssetAppliesSidecarMaterialOverrides) {
|
||||
using ::XCEngine::Resources::Model;
|
||||
using ::XCEngine::Resources::ResourceManager;
|
||||
using ::XCEngine::Resources::ResourceType;
|
||||
|
||||
const fs::path assetsDir = m_projectRoot / "Assets";
|
||||
CopyTexturedTriangleFixture(assetsDir);
|
||||
std::ofstream(assetsDir / "OverrideMaterial.mat")
|
||||
<< "{\n"
|
||||
" \"renderQueue\": \"geometry\"\n"
|
||||
"}\n";
|
||||
|
||||
#ifdef _WIN32
|
||||
AssimpDllGuard dllGuard;
|
||||
const fs::path assimpDllPath =
|
||||
GetRepositoryRoot() / "engine" / "third_party" / "assimp" / "bin" / "assimp-vc143-mt.dll";
|
||||
ASSERT_TRUE(fs::exists(assimpDllPath));
|
||||
dllGuard.module = LoadLibraryW(assimpDllPath.wstring().c_str());
|
||||
ASSERT_NE(dllGuard.module, nullptr);
|
||||
#endif
|
||||
|
||||
ResourceManager& resourceManager = ResourceManager::Get();
|
||||
resourceManager.Initialize();
|
||||
resourceManager.SetResourceRoot(m_projectRoot.string().c_str());
|
||||
|
||||
const auto modelHandle = resourceManager.Load<Model>("Assets/textured_triangle.obj");
|
||||
ASSERT_TRUE(modelHandle.IsValid());
|
||||
ASSERT_FALSE(modelHandle->GetMeshBindings().Empty());
|
||||
|
||||
std::ofstream(assetsDir / "textured_triangle.obj.materialmap")
|
||||
<< modelHandle->GetMeshBindings()[0].meshLocalID
|
||||
<< "=Assets/OverrideMaterial.mat\n";
|
||||
|
||||
m_context.GetProjectManager().RefreshCurrentFolder();
|
||||
const AssetItemPtr modelItem = FindCurrentItemByName("textured_triangle.obj");
|
||||
ASSERT_NE(modelItem, nullptr);
|
||||
|
||||
auto* createdRoot = Commands::InstantiateModelAsset(m_context, modelItem);
|
||||
ASSERT_NE(createdRoot, nullptr);
|
||||
|
||||
auto* meshObject = FindFirstEntityWithComponent<::XCEngine::Components::MeshFilterComponent>(createdRoot);
|
||||
ASSERT_NE(meshObject, nullptr);
|
||||
auto* meshRenderer = meshObject->GetComponent<::XCEngine::Components::MeshRendererComponent>();
|
||||
ASSERT_NE(meshRenderer, nullptr);
|
||||
|
||||
::XCEngine::Resources::AssetRef overrideMaterialRef;
|
||||
ASSERT_TRUE(resourceManager.TryGetAssetRef("Assets/OverrideMaterial.mat", ResourceType::Material, overrideMaterialRef));
|
||||
ASSERT_FALSE(meshRenderer->GetMaterialAssetRefs().empty());
|
||||
EXPECT_EQ(meshRenderer->GetMaterialAssetRefs()[0].assetGuid, overrideMaterialRef.assetGuid);
|
||||
EXPECT_EQ(meshRenderer->GetMaterialAssetRefs()[0].localID, overrideMaterialRef.localID);
|
||||
EXPECT_EQ(meshRenderer->GetMaterialAssetRefs()[0].resourceType, ResourceType::Material);
|
||||
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Assets/OverrideMaterial.mat");
|
||||
|
||||
resourceManager.UnloadAll();
|
||||
resourceManager.SetResourceRoot("");
|
||||
resourceManager.Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(EditorActionRoutingTest, LoadSceneResetsSelectionAndUndoAfterFallbackSave) {
|
||||
auto* savedEntity = Commands::CreateEmptyEntity(m_context, nullptr, "Create Saved", "SavedEntity");
|
||||
ASSERT_NE(savedEntity, nullptr);
|
||||
|
||||
Reference in New Issue
Block a user