Formalize renderer material contracts and harden backpack import

This commit is contained in:
2026-04-08 04:27:21 +08:00
parent 7be3b2cc45
commit 6113ed92b0
18 changed files with 534 additions and 326 deletions

View File

@@ -15,6 +15,13 @@
#include <fstream>
#include <thread>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
using namespace XCEngine::Resources;
using namespace XCEngine::Containers;
@@ -24,6 +31,10 @@ std::string GetMeshFixturePath(const char* fileName) {
return (std::filesystem::path(XCENGINE_TEST_FIXTURES_DIR) / "Resources" / "Mesh" / fileName).string();
}
std::filesystem::path GetRepositoryRoot() {
return std::filesystem::path(__FILE__).parent_path().parent_path().parent_path().parent_path();
}
XCEngine::Core::uint32 GetFirstSectionMaterialIndex(const Mesh& mesh) {
if (mesh.GetSections().Empty()) {
return 0;
@@ -202,6 +213,119 @@ TEST(MeshLoader, ImportsMaterialTexturesFromObj) {
delete mesh;
}
TEST(MeshLoader, ProjectBackpackSampleImportsMaterialTextures) {
namespace fs = std::filesystem;
const fs::path repositoryRoot = GetRepositoryRoot();
const fs::path projectRoot = repositoryRoot / "project";
const fs::path backpackMeshPath = projectRoot / "Assets" / "Models" / "backpack" / "backpack.obj";
const fs::path assimpDllPath = repositoryRoot / "engine" / "third_party" / "assimp" / "bin" / "assimp-vc143-mt.dll";
if (!fs::exists(backpackMeshPath)) {
GTEST_SKIP() << "Backpack sample mesh is not available in the local project fixture.";
}
ASSERT_TRUE(fs::exists(assimpDllPath));
#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
ResourceManager& manager = ResourceManager::Get();
manager.Initialize();
manager.SetResourceRoot(projectRoot.string().c_str());
MeshLoader loader;
const LoadResult result = loader.Load(backpackMeshPath.string().c_str());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
auto* mesh = static_cast<Mesh*>(result.resource);
ASSERT_NE(mesh, nullptr);
ASSERT_FALSE(mesh->GetMaterials().Empty());
Material* material = GetFirstSectionMaterial(*mesh);
ASSERT_NE(material, nullptr);
EXPECT_EQ(material->GetTextureBindingCount(), 1u);
EXPECT_EQ(material->GetTextureBindingName(0), "_MainTex");
EXPECT_EQ(
fs::path(material->GetTextureBindingPath(0).CStr()).lexically_normal().generic_string(),
(projectRoot / "Assets" / "Models" / "backpack" / "diffuse.jpg").lexically_normal().generic_string());
delete mesh;
manager.SetResourceRoot("");
manager.Shutdown();
}
TEST(MeshLoader, ProjectBackpackSampleArtifactRetainsSectionMaterialTextures) {
namespace fs = std::filesystem;
const fs::path repositoryRoot = GetRepositoryRoot();
const fs::path projectRoot = repositoryRoot / "project";
const fs::path backpackMeshPath = projectRoot / "Assets" / "Models" / "backpack" / "backpack.obj";
const fs::path assimpDllPath = repositoryRoot / "engine" / "third_party" / "assimp" / "bin" / "assimp-vc143-mt.dll";
if (!fs::exists(backpackMeshPath)) {
GTEST_SKIP() << "Backpack sample mesh is not available in the local project fixture.";
}
ASSERT_TRUE(fs::exists(assimpDllPath));
#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
ResourceManager& manager = ResourceManager::Get();
manager.Initialize();
manager.SetResourceRoot(projectRoot.string().c_str());
AssetDatabase database;
database.Initialize(projectRoot.string().c_str());
AssetDatabase::ResolvedAsset resolvedAsset;
ASSERT_TRUE(database.EnsureArtifact("Assets/Models/backpack/backpack.obj", ResourceType::Mesh, resolvedAsset));
ASSERT_TRUE(resolvedAsset.artifactReady);
MeshLoader loader;
const LoadResult result = loader.Load(resolvedAsset.artifactMainPath.CStr());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
auto* mesh = static_cast<Mesh*>(result.resource);
ASSERT_NE(mesh, nullptr);
ASSERT_FALSE(mesh->GetSections().Empty());
ASSERT_FALSE(mesh->GetMaterials().Empty());
Material* sectionMaterial = GetFirstSectionMaterial(*mesh);
ASSERT_NE(sectionMaterial, nullptr);
EXPECT_EQ(sectionMaterial->GetTextureBindingCount(), 1u);
EXPECT_EQ(sectionMaterial->GetTextureBindingName(0), "_MainTex");
EXPECT_FALSE(sectionMaterial->GetTextureBindingPath(0).Empty());
delete mesh;
database.Shutdown();
manager.SetResourceRoot("");
manager.Shutdown();
}
TEST(MeshLoader, AssetDatabaseCreatesModelArtifactAndReusesItWithoutReimport) {
namespace fs = std::filesystem;
using namespace std::chrono_literals;