638 lines
22 KiB
C++
638 lines
22 KiB
C++
#include <gtest/gtest.h>
|
|
#include <XCEngine/Components/AudioListenerComponent.h>
|
|
#include <XCEngine/Components/AudioSourceComponent.h>
|
|
#include <XCEngine/Scene/Scene.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/Components/TransformComponent.h>
|
|
#include <XCEngine/Core/Asset/ResourceManager.h>
|
|
#include <XCEngine/Core/Math/Vector3.h>
|
|
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
#ifdef _WIN32
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
using namespace XCEngine::Components;
|
|
using namespace XCEngine::Math;
|
|
|
|
namespace {
|
|
|
|
std::filesystem::path GetRepositoryRoot() {
|
|
return std::filesystem::path(__FILE__).parent_path().parent_path().parent_path();
|
|
}
|
|
|
|
class TestComponent : public Component {
|
|
public:
|
|
TestComponent() = default;
|
|
explicit TestComponent(const std::string& name) : m_customName(name) {}
|
|
|
|
std::string GetName() const override {
|
|
return m_customName.empty() ? "TestComponent" : m_customName;
|
|
}
|
|
|
|
bool m_awakeCalled = false;
|
|
bool m_updateCalled = false;
|
|
bool m_onDestroyCalled = false;
|
|
int m_startCount = 0;
|
|
int m_updateCount = 0;
|
|
int m_onDestroyCount = 0;
|
|
int* m_externalOnDestroyCount = nullptr;
|
|
|
|
void Awake() override { m_awakeCalled = true; }
|
|
void Start() override { ++m_startCount; }
|
|
void Update(float deltaTime) override { m_updateCalled = true; ++m_updateCount; }
|
|
void OnDestroy() override {
|
|
m_onDestroyCalled = true;
|
|
++m_onDestroyCount;
|
|
if (m_externalOnDestroyCount) {
|
|
++(*m_externalOnDestroyCount);
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::string m_customName;
|
|
};
|
|
|
|
class SceneTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
testScene = std::make_unique<Scene>("TestScene");
|
|
}
|
|
|
|
std::filesystem::path GetTempScenePath(const char* fileName) const {
|
|
return std::filesystem::temp_directory_path() / fileName;
|
|
}
|
|
|
|
std::unique_ptr<Scene> testScene;
|
|
};
|
|
|
|
TEST(Scene_Test, DefaultConstructor_DefaultName) {
|
|
Scene s;
|
|
|
|
EXPECT_EQ(s.GetName(), "Untitled");
|
|
}
|
|
|
|
TEST(Scene_Test, NamedConstructor) {
|
|
Scene s("MyScene");
|
|
|
|
EXPECT_EQ(s.GetName(), "MyScene");
|
|
}
|
|
|
|
TEST_F(SceneTest, CreateGameObject_Simple) {
|
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
|
|
|
EXPECT_NE(go, nullptr);
|
|
EXPECT_EQ(go->GetName(), "TestObject");
|
|
}
|
|
|
|
TEST_F(SceneTest, CreateGameObject_WithParent) {
|
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
|
GameObject* child = testScene->CreateGameObject("Child", parent);
|
|
|
|
EXPECT_NE(child, nullptr);
|
|
EXPECT_EQ(child->GetParent(), parent);
|
|
}
|
|
|
|
TEST_F(SceneTest, CreateGameObject_AssignsID) {
|
|
GameObject* go1 = testScene->CreateGameObject("Object1");
|
|
GameObject* go2 = testScene->CreateGameObject("Object2");
|
|
|
|
EXPECT_NE(go1->GetID(), GameObject::INVALID_ID);
|
|
EXPECT_NE(go2->GetID(), GameObject::INVALID_ID);
|
|
EXPECT_NE(go1->GetID(), go2->GetID());
|
|
}
|
|
|
|
TEST_F(SceneTest, CreateGameObject_AddsToRoot) {
|
|
GameObject* go = testScene->CreateGameObject("RootObject");
|
|
|
|
auto roots = testScene->GetRootGameObjects();
|
|
|
|
EXPECT_EQ(roots.size(), 1u);
|
|
EXPECT_EQ(roots[0], go);
|
|
}
|
|
|
|
TEST_F(SceneTest, DestroyGameObject_Simple) {
|
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
|
|
|
testScene->DestroyGameObject(go);
|
|
|
|
EXPECT_EQ(testScene->Find("TestObject"), nullptr);
|
|
}
|
|
|
|
TEST_F(SceneTest, DestroyGameObject_WithChildren) {
|
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
|
testScene->CreateGameObject("Child", parent);
|
|
|
|
testScene->DestroyGameObject(parent);
|
|
|
|
EXPECT_EQ(testScene->Find("Parent"), nullptr);
|
|
EXPECT_EQ(testScene->Find("Child"), nullptr);
|
|
}
|
|
|
|
TEST_F(SceneTest, DestroyGameObject_CallsOnDestroyOnce) {
|
|
GameObject* go = testScene->CreateGameObject("DestroyMe");
|
|
TestComponent* comp = go->AddComponent<TestComponent>();
|
|
int destroyCount = 0;
|
|
comp->m_externalOnDestroyCount = &destroyCount;
|
|
|
|
testScene->DestroyGameObject(go);
|
|
|
|
EXPECT_EQ(destroyCount, 1);
|
|
}
|
|
|
|
TEST_F(SceneTest, Find_Exists) {
|
|
testScene->CreateGameObject("FindMe");
|
|
|
|
GameObject* found = testScene->Find("FindMe");
|
|
|
|
EXPECT_NE(found, nullptr);
|
|
EXPECT_EQ(found->GetName(), "FindMe");
|
|
}
|
|
|
|
TEST_F(SceneTest, Find_NotExists) {
|
|
GameObject* found = testScene->Find("NonExistent");
|
|
|
|
EXPECT_EQ(found, nullptr);
|
|
}
|
|
|
|
TEST_F(SceneTest, GetRootGameObjects_ReturnsTopLevel) {
|
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
|
testScene->CreateGameObject("Child", parent);
|
|
|
|
auto roots = testScene->GetRootGameObjects();
|
|
|
|
EXPECT_EQ(roots.size(), 1u);
|
|
EXPECT_EQ(roots[0], parent);
|
|
}
|
|
|
|
TEST(Scene_Test, GetRootGameObjects_EmptyScene) {
|
|
Scene emptyScene;
|
|
|
|
auto roots = emptyScene.GetRootGameObjects();
|
|
|
|
EXPECT_EQ(roots.size(), 0u);
|
|
}
|
|
|
|
TEST_F(SceneTest, Update_UpdatesActiveObjects) {
|
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
|
TestComponent* comp = go->AddComponent<TestComponent>();
|
|
|
|
testScene->Update(0.016f);
|
|
|
|
EXPECT_TRUE(comp->m_updateCalled);
|
|
}
|
|
|
|
TEST_F(SceneTest, Update_SkipsInactiveObjects) {
|
|
GameObject* go = testScene->CreateGameObject("InactiveObject");
|
|
TestComponent* comp = go->AddComponent<TestComponent>();
|
|
go->SetActive(false);
|
|
|
|
testScene->Update(0.016f);
|
|
|
|
EXPECT_FALSE(comp->m_updateCalled);
|
|
}
|
|
|
|
TEST_F(SceneTest, Update_RecursivelyUpdatesChildObjects) {
|
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
|
GameObject* child = testScene->CreateGameObject("Child", parent);
|
|
TestComponent* comp = child->AddComponent<TestComponent>();
|
|
|
|
testScene->Update(0.016f);
|
|
|
|
EXPECT_TRUE(comp->m_updateCalled);
|
|
EXPECT_EQ(comp->m_updateCount, 1);
|
|
}
|
|
|
|
TEST_F(SceneTest, Update_CallsStartOnlyOnceBeforeUpdate) {
|
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
|
TestComponent* comp = go->AddComponent<TestComponent>();
|
|
|
|
testScene->Update(0.016f);
|
|
testScene->Update(0.016f);
|
|
|
|
EXPECT_EQ(comp->m_startCount, 1);
|
|
EXPECT_EQ(comp->m_updateCount, 2);
|
|
}
|
|
|
|
TEST(Scene_Test, IsActive_DefaultTrue) {
|
|
Scene s;
|
|
|
|
EXPECT_TRUE(s.IsActive());
|
|
}
|
|
|
|
TEST(Scene_Test, SetActive) {
|
|
Scene s;
|
|
s.SetActive(false);
|
|
|
|
EXPECT_FALSE(s.IsActive());
|
|
}
|
|
|
|
TEST_F(SceneTest, FindObjectOfType) {
|
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
|
go->AddComponent<TestComponent>();
|
|
|
|
TestComponent* found = testScene->FindObjectOfType<TestComponent>();
|
|
|
|
EXPECT_NE(found, nullptr);
|
|
}
|
|
|
|
TEST_F(SceneTest, FindObjectsOfType) {
|
|
GameObject* go1 = testScene->CreateGameObject("Object1");
|
|
GameObject* go2 = testScene->CreateGameObject("Object2");
|
|
go1->AddComponent<TestComponent>();
|
|
go2->AddComponent<TestComponent>();
|
|
|
|
auto found = testScene->FindObjectsOfType<TestComponent>();
|
|
|
|
EXPECT_EQ(found.size(), 2u);
|
|
}
|
|
|
|
TEST_F(SceneTest, FindGameObjectWithTag) {
|
|
GameObject* go = testScene->CreateGameObject("MyTag");
|
|
|
|
GameObject* found = testScene->FindGameObjectWithTag("MyTag");
|
|
|
|
EXPECT_NE(found, nullptr);
|
|
}
|
|
|
|
TEST_F(SceneTest, OnGameObjectCreated_Event) {
|
|
bool eventFired = false;
|
|
testScene->OnGameObjectCreated().Subscribe([&eventFired](GameObject*) {
|
|
eventFired = true;
|
|
});
|
|
|
|
testScene->CreateGameObject("TestObject");
|
|
|
|
EXPECT_TRUE(eventFired);
|
|
}
|
|
|
|
TEST_F(SceneTest, OnGameObjectDestroyed_Event) {
|
|
bool eventFired = false;
|
|
testScene->OnGameObjectDestroyed().Subscribe([&eventFired](GameObject*) {
|
|
eventFired = true;
|
|
});
|
|
|
|
GameObject* go = testScene->CreateGameObject("TestObject");
|
|
testScene->DestroyGameObject(go);
|
|
|
|
EXPECT_TRUE(eventFired);
|
|
}
|
|
|
|
TEST_F(SceneTest, Save_And_Load) {
|
|
GameObject* go = testScene->CreateGameObject("SavedObject");
|
|
go->GetTransform()->SetLocalPosition(XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
|
go->SetActive(true);
|
|
|
|
const std::filesystem::path scenePath = GetTempScenePath("test_scene.xc");
|
|
testScene->Save(scenePath.string());
|
|
|
|
Scene loadedScene;
|
|
loadedScene.Load(scenePath.string());
|
|
|
|
GameObject* loadedGo = loadedScene.Find("SavedObject");
|
|
EXPECT_NE(loadedGo, nullptr);
|
|
EXPECT_EQ(loadedGo->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
|
|
|
std::filesystem::remove(scenePath);
|
|
}
|
|
|
|
TEST_F(SceneTest, Save_ContainsGameObjectData) {
|
|
testScene->CreateGameObject("Player");
|
|
testScene->CreateGameObject("Enemy");
|
|
|
|
const std::filesystem::path scenePath = GetTempScenePath("test_scene_multi.xc");
|
|
testScene->Save(scenePath.string());
|
|
|
|
std::ifstream file(scenePath);
|
|
std::stringstream buffer;
|
|
buffer << file.rdbuf();
|
|
std::string content = buffer.str();
|
|
file.close();
|
|
|
|
EXPECT_TRUE(content.find("Player") != std::string::npos);
|
|
EXPECT_TRUE(content.find("Enemy") != std::string::npos);
|
|
|
|
std::filesystem::remove(scenePath);
|
|
}
|
|
|
|
TEST_F(SceneTest, Save_And_Load_PreservesAudioComponents) {
|
|
GameObject* listenerObject = testScene->CreateGameObject("Listener");
|
|
GameObject* sourceObject = testScene->CreateGameObject("Source");
|
|
|
|
listenerObject->AddComponent<AudioListenerComponent>();
|
|
sourceObject->AddComponent<AudioSourceComponent>();
|
|
|
|
const std::filesystem::path scenePath = GetTempScenePath("test_scene_audio_components.xc");
|
|
testScene->Save(scenePath.string());
|
|
|
|
Scene loadedScene;
|
|
loadedScene.Load(scenePath.string());
|
|
|
|
GameObject* loadedListenerObject = loadedScene.Find("Listener");
|
|
GameObject* loadedSourceObject = loadedScene.Find("Source");
|
|
ASSERT_NE(loadedListenerObject, nullptr);
|
|
ASSERT_NE(loadedSourceObject, nullptr);
|
|
|
|
EXPECT_NE(loadedListenerObject->GetComponent<AudioListenerComponent>(), nullptr);
|
|
EXPECT_NE(loadedSourceObject->GetComponent<AudioSourceComponent>(), nullptr);
|
|
|
|
std::filesystem::remove(scenePath);
|
|
}
|
|
|
|
TEST_F(SceneTest, Save_And_Load_PreservesHierarchyAndComponents) {
|
|
testScene->SetName("Serialized Scene");
|
|
testScene->SetActive(false);
|
|
|
|
GameObject* parent = testScene->CreateGameObject("Rig Root");
|
|
parent->GetTransform()->SetLocalPosition(Vector3(5.0f, 0.0f, 0.0f));
|
|
parent->SetActive(false);
|
|
|
|
auto* light = parent->AddComponent<LightComponent>();
|
|
light->SetLightType(LightType::Spot);
|
|
light->SetIntensity(3.5f);
|
|
light->SetRange(12.0f);
|
|
light->SetSpotAngle(45.0f);
|
|
light->SetCastsShadows(true);
|
|
|
|
GameObject* child = testScene->CreateGameObject("Main Camera", parent);
|
|
child->GetTransform()->SetLocalPosition(Vector3(1.0f, 2.0f, 3.0f));
|
|
child->GetTransform()->SetLocalScale(Vector3(2.0f, 2.0f, 2.0f));
|
|
|
|
auto* camera = child->AddComponent<CameraComponent>();
|
|
camera->SetProjectionType(CameraProjectionType::Orthographic);
|
|
camera->SetOrthographicSize(7.5f);
|
|
camera->SetNearClipPlane(0.5f);
|
|
camera->SetFarClipPlane(250.0f);
|
|
camera->SetDepth(2.0f);
|
|
camera->SetPrimary(false);
|
|
|
|
const std::filesystem::path scenePath = GetTempScenePath("test_scene_hierarchy_components.xc");
|
|
testScene->Save(scenePath.string());
|
|
|
|
Scene loadedScene;
|
|
loadedScene.Load(scenePath.string());
|
|
|
|
EXPECT_EQ(loadedScene.GetName(), "Serialized Scene");
|
|
EXPECT_FALSE(loadedScene.IsActive());
|
|
|
|
GameObject* loadedParent = loadedScene.Find("Rig Root");
|
|
GameObject* loadedChild = loadedScene.Find("Main Camera");
|
|
ASSERT_NE(loadedParent, nullptr);
|
|
ASSERT_NE(loadedChild, nullptr);
|
|
|
|
EXPECT_EQ(loadedChild->GetParent(), loadedParent);
|
|
EXPECT_EQ(loadedScene.GetRootGameObjects().size(), 1u);
|
|
EXPECT_FALSE(loadedParent->IsActive());
|
|
EXPECT_EQ(loadedParent->GetTransform()->GetLocalPosition(), Vector3(5.0f, 0.0f, 0.0f));
|
|
EXPECT_EQ(loadedChild->GetTransform()->GetLocalPosition(), Vector3(1.0f, 2.0f, 3.0f));
|
|
EXPECT_EQ(loadedChild->GetTransform()->GetPosition(), Vector3(6.0f, 2.0f, 3.0f));
|
|
|
|
auto* loadedLight = loadedParent->GetComponent<LightComponent>();
|
|
ASSERT_NE(loadedLight, nullptr);
|
|
EXPECT_EQ(loadedLight->GetLightType(), LightType::Spot);
|
|
EXPECT_FLOAT_EQ(loadedLight->GetIntensity(), 3.5f);
|
|
EXPECT_FLOAT_EQ(loadedLight->GetRange(), 12.0f);
|
|
EXPECT_FLOAT_EQ(loadedLight->GetSpotAngle(), 45.0f);
|
|
EXPECT_TRUE(loadedLight->GetCastsShadows());
|
|
|
|
auto* loadedCamera = loadedChild->GetComponent<CameraComponent>();
|
|
ASSERT_NE(loadedCamera, nullptr);
|
|
EXPECT_EQ(loadedCamera->GetProjectionType(), CameraProjectionType::Orthographic);
|
|
EXPECT_FLOAT_EQ(loadedCamera->GetOrthographicSize(), 7.5f);
|
|
EXPECT_FLOAT_EQ(loadedCamera->GetNearClipPlane(), 0.5f);
|
|
EXPECT_FLOAT_EQ(loadedCamera->GetFarClipPlane(), 250.0f);
|
|
EXPECT_FLOAT_EQ(loadedCamera->GetDepth(), 2.0f);
|
|
EXPECT_FALSE(loadedCamera->IsPrimary());
|
|
|
|
std::filesystem::remove(scenePath);
|
|
}
|
|
|
|
TEST_F(SceneTest, SerializeToString_And_DeserializeFromString_PreservesHierarchyAndComponents) {
|
|
testScene->SetName("Round Trip Scene");
|
|
testScene->SetActive(false);
|
|
|
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
|
parent->GetTransform()->SetLocalPosition(Vector3(2.0f, 4.0f, 6.0f));
|
|
parent->GetTransform()->SetLocalScale(Vector3(1.5f, 1.5f, 1.5f));
|
|
|
|
auto* light = parent->AddComponent<LightComponent>();
|
|
light->SetLightType(LightType::Point);
|
|
light->SetIntensity(4.0f);
|
|
light->SetRange(20.0f);
|
|
light->SetCastsShadows(true);
|
|
|
|
GameObject* child = testScene->CreateGameObject("Child Camera", parent);
|
|
child->GetTransform()->SetLocalPosition(Vector3(1.0f, 0.0f, -3.0f));
|
|
|
|
auto* camera = child->AddComponent<CameraComponent>();
|
|
camera->SetProjectionType(CameraProjectionType::Perspective);
|
|
camera->SetFieldOfView(72.0f);
|
|
camera->SetNearClipPlane(0.2f);
|
|
camera->SetFarClipPlane(512.0f);
|
|
camera->SetPrimary(true);
|
|
|
|
const std::string serialized = testScene->SerializeToString();
|
|
|
|
Scene loadedScene;
|
|
loadedScene.DeserializeFromString(serialized);
|
|
|
|
EXPECT_EQ(loadedScene.GetName(), "Round Trip Scene");
|
|
EXPECT_FALSE(loadedScene.IsActive());
|
|
EXPECT_EQ(loadedScene.GetRootGameObjects().size(), 1u);
|
|
|
|
GameObject* loadedParent = loadedScene.Find("Parent");
|
|
GameObject* loadedChild = loadedScene.Find("Child Camera");
|
|
ASSERT_NE(loadedParent, nullptr);
|
|
ASSERT_NE(loadedChild, nullptr);
|
|
|
|
EXPECT_EQ(loadedChild->GetParent(), loadedParent);
|
|
EXPECT_EQ(loadedParent->GetTransform()->GetLocalPosition(), Vector3(2.0f, 4.0f, 6.0f));
|
|
EXPECT_EQ(loadedParent->GetTransform()->GetLocalScale(), Vector3(1.5f, 1.5f, 1.5f));
|
|
EXPECT_EQ(loadedChild->GetTransform()->GetPosition(), Vector3(3.5f, 4.0f, 1.5f));
|
|
|
|
auto* loadedLight = loadedParent->GetComponent<LightComponent>();
|
|
ASSERT_NE(loadedLight, nullptr);
|
|
EXPECT_EQ(loadedLight->GetLightType(), LightType::Point);
|
|
EXPECT_FLOAT_EQ(loadedLight->GetIntensity(), 4.0f);
|
|
EXPECT_FLOAT_EQ(loadedLight->GetRange(), 20.0f);
|
|
EXPECT_TRUE(loadedLight->GetCastsShadows());
|
|
|
|
auto* loadedCamera = loadedChild->GetComponent<CameraComponent>();
|
|
ASSERT_NE(loadedCamera, nullptr);
|
|
EXPECT_EQ(loadedCamera->GetProjectionType(), CameraProjectionType::Perspective);
|
|
EXPECT_FLOAT_EQ(loadedCamera->GetFieldOfView(), 72.0f);
|
|
EXPECT_FLOAT_EQ(loadedCamera->GetNearClipPlane(), 0.2f);
|
|
EXPECT_FLOAT_EQ(loadedCamera->GetFarClipPlane(), 512.0f);
|
|
EXPECT_TRUE(loadedCamera->IsPrimary());
|
|
}
|
|
|
|
TEST_F(SceneTest, SerializeToString_And_DeserializeFromString_PreservesUUIDs) {
|
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
|
GameObject* child = testScene->CreateGameObject("Child", parent);
|
|
|
|
const uint64_t parentUUID = parent->GetUUID();
|
|
const uint64_t childUUID = child->GetUUID();
|
|
|
|
const std::string serialized = testScene->SerializeToString();
|
|
|
|
Scene loadedScene;
|
|
loadedScene.DeserializeFromString(serialized);
|
|
|
|
GameObject* loadedParent = loadedScene.Find("Parent");
|
|
GameObject* loadedChild = loadedScene.Find("Child");
|
|
ASSERT_NE(loadedParent, nullptr);
|
|
ASSERT_NE(loadedChild, nullptr);
|
|
|
|
EXPECT_EQ(loadedParent->GetUUID(), parentUUID);
|
|
EXPECT_EQ(loadedChild->GetUUID(), childUUID);
|
|
}
|
|
|
|
TEST_F(SceneTest, Save_ContainsHierarchyAndComponentEntries) {
|
|
GameObject* parent = testScene->CreateGameObject("Parent");
|
|
GameObject* child = testScene->CreateGameObject("Child", parent);
|
|
child->AddComponent<CameraComponent>();
|
|
parent->AddComponent<LightComponent>();
|
|
|
|
const std::filesystem::path scenePath = GetTempScenePath("test_scene_format.xc");
|
|
testScene->Save(scenePath.string());
|
|
|
|
std::ifstream file(scenePath);
|
|
std::stringstream buffer;
|
|
buffer << file.rdbuf();
|
|
const std::string content = buffer.str();
|
|
file.close();
|
|
|
|
EXPECT_TRUE(content.find("gameobject_begin") != std::string::npos);
|
|
EXPECT_TRUE(content.find("parent=" + std::to_string(parent->GetID())) != std::string::npos);
|
|
EXPECT_TRUE(content.find("component=Camera;") != std::string::npos);
|
|
EXPECT_TRUE(content.find("component=Light;") != std::string::npos);
|
|
|
|
std::filesystem::remove(scenePath);
|
|
}
|
|
|
|
TEST_F(SceneTest, SaveAndLoad_PreservesMeshComponentPaths) {
|
|
GameObject* meshObject = testScene->CreateGameObject("Backpack");
|
|
auto* meshFilter = meshObject->AddComponent<MeshFilterComponent>();
|
|
auto* meshRenderer = meshObject->AddComponent<MeshRendererComponent>();
|
|
meshFilter->SetMeshPath("Assets/Models/backpack/backpack.obj");
|
|
meshRenderer->SetMaterialPath(0, "Assets/Materials/backpack.mat");
|
|
meshRenderer->SetCastShadows(false);
|
|
meshRenderer->SetReceiveShadows(true);
|
|
meshRenderer->SetRenderLayer(4);
|
|
|
|
const std::filesystem::path scenePath = GetTempScenePath("test_scene_mesh_components.xc");
|
|
testScene->Save(scenePath.string());
|
|
|
|
Scene loadedScene;
|
|
loadedScene.Load(scenePath.string());
|
|
|
|
GameObject* loadedObject = loadedScene.Find("Backpack");
|
|
ASSERT_NE(loadedObject, nullptr);
|
|
|
|
auto* loadedMeshFilter = loadedObject->GetComponent<MeshFilterComponent>();
|
|
auto* loadedMeshRenderer = loadedObject->GetComponent<MeshRendererComponent>();
|
|
ASSERT_NE(loadedMeshFilter, nullptr);
|
|
ASSERT_NE(loadedMeshRenderer, nullptr);
|
|
|
|
EXPECT_EQ(loadedMeshFilter->GetMeshPath(), "Assets/Models/backpack/backpack.obj");
|
|
ASSERT_EQ(loadedMeshRenderer->GetMaterialCount(), 1u);
|
|
EXPECT_EQ(loadedMeshRenderer->GetMaterialPath(0), "Assets/Materials/backpack.mat");
|
|
EXPECT_FALSE(loadedMeshRenderer->GetCastShadows());
|
|
EXPECT_TRUE(loadedMeshRenderer->GetReceiveShadows());
|
|
EXPECT_EQ(loadedMeshRenderer->GetRenderLayer(), 4u);
|
|
|
|
std::filesystem::remove(scenePath);
|
|
}
|
|
|
|
TEST(Scene_ProjectSample, MainSceneLoadsBackpackMeshAsset) {
|
|
namespace fs = std::filesystem;
|
|
|
|
XCEngine::Resources::ResourceManager& resourceManager = XCEngine::Resources::ResourceManager::Get();
|
|
resourceManager.Initialize();
|
|
|
|
struct ResourceManagerGuard {
|
|
XCEngine::Resources::ResourceManager* manager = nullptr;
|
|
~ResourceManagerGuard() {
|
|
if (manager != nullptr) {
|
|
manager->Shutdown();
|
|
}
|
|
}
|
|
} resourceManagerGuard{ &resourceManager };
|
|
|
|
struct CurrentPathGuard {
|
|
fs::path previousPath;
|
|
~CurrentPathGuard() {
|
|
if (!previousPath.empty()) {
|
|
fs::current_path(previousPath);
|
|
}
|
|
}
|
|
} currentPathGuard{ fs::current_path() };
|
|
|
|
const fs::path repositoryRoot = GetRepositoryRoot();
|
|
const fs::path projectRoot = repositoryRoot / "project";
|
|
const fs::path mainScenePath = projectRoot / "Assets" / "Scenes" / "Main.xc";
|
|
const fs::path assimpDllPath = repositoryRoot / "engine" / "third_party" / "assimp" / "bin" / "assimp-vc143-mt.dll";
|
|
const fs::path backpackMeshPath = projectRoot / "Assets" / "Models" / "backpack" / "backpack.obj";
|
|
|
|
ASSERT_TRUE(fs::exists(mainScenePath));
|
|
ASSERT_TRUE(fs::exists(assimpDllPath));
|
|
ASSERT_TRUE(fs::exists(backpackMeshPath));
|
|
|
|
#ifdef _WIN32
|
|
struct DllGuard {
|
|
HMODULE module = nullptr;
|
|
~DllGuard() {
|
|
if (module != nullptr) {
|
|
FreeLibrary(module);
|
|
}
|
|
}
|
|
} dllGuard;
|
|
dllGuard.module = LoadLibraryW(assimpDllPath.wstring().c_str());
|
|
ASSERT_NE(dllGuard.module, nullptr);
|
|
#endif
|
|
|
|
fs::current_path(projectRoot);
|
|
|
|
ASSERT_NE(resourceManager.GetLoader(XCEngine::Resources::ResourceType::Mesh), nullptr);
|
|
XCEngine::Resources::MeshLoader meshLoader;
|
|
const XCEngine::Resources::LoadResult directMeshLoadResult =
|
|
meshLoader.Load("Assets/Models/backpack/backpack.obj");
|
|
ASSERT_TRUE(directMeshLoadResult)
|
|
<< "MeshLoader failed: " << directMeshLoadResult.errorMessage.CStr();
|
|
|
|
const auto directMeshHandle =
|
|
resourceManager.Load<XCEngine::Resources::Mesh>("Assets/Models/backpack/backpack.obj");
|
|
ASSERT_NE(directMeshHandle.Get(), nullptr);
|
|
ASSERT_TRUE(directMeshHandle->IsValid());
|
|
ASSERT_GT(directMeshHandle->GetVertexCount(), 0u);
|
|
|
|
Scene loadedScene;
|
|
loadedScene.Load(mainScenePath.string());
|
|
|
|
GameObject* backpackObject = loadedScene.Find("BackpackMesh");
|
|
ASSERT_NE(backpackObject, nullptr);
|
|
|
|
auto* meshFilter = backpackObject->GetComponent<MeshFilterComponent>();
|
|
auto* meshRenderer = backpackObject->GetComponent<MeshRendererComponent>();
|
|
ASSERT_NE(meshFilter, nullptr);
|
|
ASSERT_NE(meshRenderer, nullptr);
|
|
ASSERT_NE(meshFilter->GetMesh(), nullptr);
|
|
EXPECT_TRUE(meshFilter->GetMesh()->IsValid());
|
|
EXPECT_GT(meshFilter->GetMesh()->GetVertexCount(), 0u);
|
|
EXPECT_GT(meshFilter->GetMesh()->GetSections().Size(), 0u);
|
|
EXPECT_GT(meshFilter->GetMesh()->GetMaterials().Size(), 0u);
|
|
EXPECT_EQ(meshFilter->GetMeshPath(), "Assets/Models/backpack/backpack.obj");
|
|
}
|
|
|
|
} // namespace
|