#include #include #include #include #include #include #include #include #include #include #include using namespace XCEngine::Components; using namespace XCEngine::Resources; namespace { Mesh* CreateTestMesh(const char* name, const char* path) { auto* mesh = new Mesh(); IResource::ConstructParams params = {}; params.name = name; params.path = path; params.guid = ResourceGUID::Generate(path); mesh->Initialize(params); return mesh; } Material* CreateTestMaterial(const char* name, const char* path) { auto* material = new Material(); IResource::ConstructParams params = {}; params.name = name; params.path = path; params.guid = ResourceGUID::Generate(path); material->Initialize(params); return material; } TEST(MeshFilterComponent_Test, SetMeshCachesResourceAndPath) { GameObject gameObject("MeshHolder"); auto* component = gameObject.AddComponent(); Mesh* mesh = CreateTestMesh("Quad", "Meshes/quad.mesh"); component->SetMesh(mesh); EXPECT_EQ(component->GetMesh(), mesh); EXPECT_EQ(component->GetMeshPath(), "Meshes/quad.mesh"); component->ClearMesh(); delete mesh; } TEST(MeshFilterComponent_Test, SerializeAndDeserializePreservesPath) { MeshFilterComponent source; Mesh* mesh = CreateTestMesh("Quad", "Meshes/serialized.mesh"); source.SetMesh(mesh); std::stringstream stream; source.Serialize(stream); MeshFilterComponent target; target.Deserialize(stream); EXPECT_EQ(target.GetMeshPath(), "Meshes/serialized.mesh"); EXPECT_EQ(target.GetMesh(), nullptr); source.ClearMesh(); delete mesh; } TEST(MeshFilterComponent_Test, SetMeshPathPreservesPathWithoutLoadedResource) { MeshFilterComponent component; component.SetMeshPath("Meshes/runtime.mesh"); EXPECT_EQ(component.GetMeshPath(), "Meshes/runtime.mesh"); EXPECT_EQ(component.GetMesh(), nullptr); component.SetMeshPath(""); EXPECT_EQ(component.GetMeshPath(), ""); EXPECT_EQ(component.GetMesh(), nullptr); } TEST(MeshRendererComponent_Test, SetMaterialsKeepsSlotsAndFlags) { GameObject gameObject("RendererHolder"); auto* component = gameObject.AddComponent(); Material* material0 = CreateTestMaterial("M0", "Materials/m0.mat"); Material* material1 = CreateTestMaterial("M1", "Materials/m1.mat"); component->SetMaterial(0, material0); component->SetMaterial(1, material1); component->SetCastShadows(false); component->SetReceiveShadows(false); component->SetRenderLayer(7); ASSERT_EQ(component->GetMaterialCount(), 2u); EXPECT_EQ(component->GetMaterial(0), material0); EXPECT_EQ(component->GetMaterial(1), material1); EXPECT_EQ(component->GetMaterialPaths()[0], "Materials/m0.mat"); EXPECT_EQ(component->GetMaterialPaths()[1], "Materials/m1.mat"); EXPECT_FALSE(component->GetCastShadows()); EXPECT_FALSE(component->GetReceiveShadows()); EXPECT_EQ(component->GetRenderLayer(), 7u); component->ClearMaterials(); delete material0; delete material1; } TEST(MeshRendererComponent_Test, SerializeAndDeserializePreservesMaterialPathsAndSettings) { MeshRendererComponent source; Material* material0 = CreateTestMaterial("M0", "Materials/serialized0.mat"); Material* material1 = CreateTestMaterial("M1", "Materials/serialized1.mat"); source.SetMaterial(0, material0); source.SetMaterial(1, material1); source.SetCastShadows(false); source.SetReceiveShadows(true); source.SetRenderLayer(3); std::stringstream stream; source.Serialize(stream); MeshRendererComponent target; target.Deserialize(stream); ASSERT_EQ(target.GetMaterialCount(), 2u); EXPECT_EQ(target.GetMaterial(0), nullptr); EXPECT_EQ(target.GetMaterial(1), nullptr); EXPECT_EQ(target.GetMaterialPaths()[0], "Materials/serialized0.mat"); EXPECT_EQ(target.GetMaterialPaths()[1], "Materials/serialized1.mat"); EXPECT_FALSE(target.GetCastShadows()); EXPECT_TRUE(target.GetReceiveShadows()); EXPECT_EQ(target.GetRenderLayer(), 3u); source.ClearMaterials(); delete material0; delete material1; } TEST(MeshRendererComponent_Test, SerializeAndDeserializePreservesTrailingEmptyMaterialSlots) { MeshRendererComponent source; Material* material0 = CreateTestMaterial("M0", "Materials/serialized0.mat"); source.SetMaterial(0, material0); source.SetMaterialPath(1, ""); source.SetCastShadows(false); source.SetReceiveShadows(true); source.SetRenderLayer(9); std::stringstream stream; source.Serialize(stream); MeshRendererComponent target; target.Deserialize(stream); ASSERT_EQ(target.GetMaterialCount(), 2u); EXPECT_EQ(target.GetMaterial(0), nullptr); EXPECT_EQ(target.GetMaterial(1), nullptr); EXPECT_EQ(target.GetMaterialPath(0), "Materials/serialized0.mat"); EXPECT_EQ(target.GetMaterialPath(1), ""); EXPECT_FALSE(target.GetCastShadows()); EXPECT_TRUE(target.GetReceiveShadows()); EXPECT_EQ(target.GetRenderLayer(), 9u); source.ClearMaterials(); delete material0; } TEST(MeshRendererComponent_Test, SetMaterialPathPreservesPathWithoutLoadedResource) { MeshRendererComponent component; component.SetMaterialPath(1, "Materials/runtime.mat"); ASSERT_EQ(component.GetMaterialCount(), 2u); EXPECT_EQ(component.GetMaterial(0), nullptr); EXPECT_EQ(component.GetMaterial(1), nullptr); EXPECT_EQ(component.GetMaterialPath(0), ""); EXPECT_EQ(component.GetMaterialPath(1), "Materials/runtime.mat"); component.SetMaterialPath(1, ""); EXPECT_EQ(component.GetMaterialPath(1), ""); EXPECT_EQ(component.GetMaterial(1), nullptr); } TEST(MeshRendererComponent_Test, SerializeAndDeserializeLoadsProjectMaterialByAssetRef) { namespace fs = std::filesystem; ResourceManager& manager = ResourceManager::Get(); manager.Initialize(); const fs::path projectRoot = fs::temp_directory_path() / "xc_mesh_renderer_asset_ref_test"; const fs::path assetsDir = projectRoot / "Assets"; const fs::path materialPath = assetsDir / "runtime.material"; fs::remove_all(projectRoot); fs::create_directories(assetsDir); { std::ofstream materialFile(materialPath); ASSERT_TRUE(materialFile.is_open()); materialFile << "{\n"; materialFile << " \"renderQueue\": \"geometry\",\n"; materialFile << " \"renderState\": {\n"; materialFile << " \"cull\": \"back\"\n"; materialFile << " }\n"; materialFile << "}"; } manager.SetResourceRoot(projectRoot.string().c_str()); MeshRendererComponent source; source.SetMaterialPath(0, "Assets/runtime.material"); ASSERT_EQ(source.GetMaterialCount(), 1u); ASSERT_NE(source.GetMaterial(0), nullptr); ASSERT_EQ(source.GetMaterialPath(0), "Assets/runtime.material"); ASSERT_EQ(source.GetMaterialAssetRefs().size(), 1u); EXPECT_TRUE(source.GetMaterialAssetRefs()[0].IsValid()); std::stringstream stream; source.Serialize(stream); const std::string serialized = stream.str(); EXPECT_NE(serialized.find("materialRefs="), std::string::npos); EXPECT_EQ(serialized.find("materialRefs=;"), std::string::npos); std::stringstream deserializeStream(serialized); MeshRendererComponent target; target.Deserialize(deserializeStream); ASSERT_EQ(target.GetMaterialCount(), 1u); EXPECT_EQ(target.GetMaterialPath(0), "Assets/runtime.material"); ASSERT_NE(target.GetMaterial(0), nullptr); EXPECT_TRUE(target.GetMaterialAssetRefs()[0].IsValid()); manager.SetResourceRoot(""); manager.Shutdown(); fs::remove_all(projectRoot); } } // namespace