Add assimp-based mesh import

This commit is contained in:
2026-03-26 02:53:34 +08:00
parent b414bc5326
commit cb05472205
103 changed files with 24502 additions and 25 deletions

View File

@@ -28,5 +28,15 @@ target_include_directories(mesh_tests PRIVATE
${CMAKE_SOURCE_DIR}/tests/fixtures
)
target_compile_definitions(mesh_tests PRIVATE
XCENGINE_TEST_FIXTURES_DIR="${CMAKE_SOURCE_DIR}/tests/fixtures"
)
add_custom_command(TARGET mesh_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:mesh_tests>/assimp-vc143-mt.dll
)
include(GoogleTest)
gtest_discover_tests(mesh_tests)

View File

@@ -4,6 +4,7 @@
#include <XCEngine/Core/Asset/ResourceManager.h>
using namespace XCEngine::Resources;
using namespace XCEngine::Math;
namespace {
@@ -30,4 +31,44 @@ TEST(Mesh, GetSections) {
EXPECT_EQ(mesh.GetSections().Size(), 0u);
}
TEST(Mesh, SetVertexAndIndexData) {
Mesh mesh;
StaticMeshVertex vertices[3];
vertices[0].position = Vector3(0.0f, 0.0f, 0.0f);
vertices[1].position = Vector3(1.0f, 0.0f, 0.0f);
vertices[2].position = Vector3(0.0f, 1.0f, 0.0f);
const uint16_t indices[3] = {0, 1, 2};
mesh.SetVertexData(vertices, sizeof(vertices), 3, sizeof(StaticMeshVertex),
VertexAttribute::Position | VertexAttribute::Normal | VertexAttribute::UV0);
mesh.SetIndexData(indices, sizeof(indices), 3, false);
EXPECT_EQ(mesh.GetVertexCount(), 3u);
EXPECT_EQ(mesh.GetIndexCount(), 3u);
EXPECT_EQ(mesh.GetVertexStride(), sizeof(StaticMeshVertex));
EXPECT_TRUE(HasVertexAttribute(mesh.GetVertexAttributes(), VertexAttribute::Position));
EXPECT_TRUE(HasVertexAttribute(mesh.GetVertexAttributes(), VertexAttribute::Normal));
EXPECT_TRUE(HasVertexAttribute(mesh.GetVertexAttributes(), VertexAttribute::UV0));
EXPECT_GT(mesh.GetMemorySize(), 0u);
}
TEST(Mesh, AddSection) {
Mesh mesh;
MeshSection section{};
section.baseVertex = 1;
section.vertexCount = 3;
section.startIndex = 6;
section.indexCount = 9;
section.materialID = 2;
mesh.AddSection(section);
ASSERT_EQ(mesh.GetSections().Size(), 1u);
EXPECT_EQ(mesh.GetSections()[0].baseVertex, 1u);
EXPECT_EQ(mesh.GetSections()[0].materialID, 2u);
}
} // namespace

View File

@@ -101,4 +101,30 @@ TEST(MeshImportSettings, Clone) {
EXPECT_TRUE(clonedSettings->GetMergeMeshes());
}
TEST(MeshImportSettings, SaveAndLoadJSON) {
MeshImportSettings settings;
settings.SetImportFlags(MeshImportFlags::FlipUVs | MeshImportFlags::GenerateTangents);
settings.SetScale(2.5f);
settings.SetOffset(Vector3(1.0f, 2.0f, 3.0f));
settings.SetAxisConversion(false);
settings.SetMergeMeshes(true);
settings.SetOptimizeThreshold(0.6f);
settings.SetImportScale(0.75f);
settings.SetThreshold(0.05f);
const auto json = settings.SaveToJSON();
MeshImportSettings loaded;
EXPECT_TRUE(loaded.LoadFromJSON(json));
EXPECT_TRUE(loaded.HasImportFlag(MeshImportFlags::FlipUVs));
EXPECT_TRUE(loaded.HasImportFlag(MeshImportFlags::GenerateTangents));
EXPECT_FLOAT_EQ(loaded.GetScale(), 2.5f);
EXPECT_EQ(loaded.GetOffset(), Vector3(1.0f, 2.0f, 3.0f));
EXPECT_FALSE(loaded.GetAxisConversion());
EXPECT_TRUE(loaded.GetMergeMeshes());
EXPECT_FLOAT_EQ(loaded.GetOptimizeThreshold(), 0.6f);
EXPECT_FLOAT_EQ(loaded.GetImportScale(), 0.75f);
EXPECT_FLOAT_EQ(loaded.GetThreshold(), 0.05f);
}
} // namespace

View File

@@ -1,13 +1,19 @@
#include <gtest/gtest.h>
#include <XCEngine/Resources/Mesh/MeshLoader.h>
#include <XCEngine/Resources/Mesh/MeshImportSettings.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include <XCEngine/Core/Containers/Array.h>
#include <filesystem>
using namespace XCEngine::Resources;
using namespace XCEngine::Containers;
namespace {
std::string GetMeshFixturePath(const char* fileName) {
return (std::filesystem::path(XCENGINE_TEST_FIXTURES_DIR) / "Resources" / "Mesh" / fileName).string();
}
TEST(MeshLoader, GetResourceType) {
MeshLoader loader;
EXPECT_EQ(loader.GetResourceType(), ResourceType::Mesh);
@@ -24,6 +30,7 @@ TEST(MeshLoader, CanLoad) {
EXPECT_TRUE(loader.CanLoad("test.obj"));
EXPECT_TRUE(loader.CanLoad("test.fbx"));
EXPECT_TRUE(loader.CanLoad("test.gltf"));
EXPECT_TRUE(loader.CanLoad("test.OBJ"));
EXPECT_FALSE(loader.CanLoad("test.txt"));
EXPECT_FALSE(loader.CanLoad("test.png"));
}
@@ -34,4 +41,61 @@ TEST(MeshLoader, LoadInvalidPath) {
EXPECT_FALSE(result);
}
TEST(MeshLoader, LoadValidObjMesh) {
MeshLoader loader;
const std::string path = GetMeshFixturePath("triangle.obj");
LoadResult result = loader.Load(path.c_str());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
auto* mesh = static_cast<Mesh*>(result.resource);
EXPECT_EQ(mesh->GetVertexCount(), 3u);
EXPECT_EQ(mesh->GetIndexCount(), 3u);
EXPECT_EQ(mesh->GetVertexStride(), sizeof(StaticMeshVertex));
EXPECT_FALSE(mesh->IsUse32BitIndex());
ASSERT_EQ(mesh->GetSections().Size(), 1u);
EXPECT_EQ(mesh->GetSections()[0].vertexCount, 3u);
EXPECT_EQ(mesh->GetSections()[0].indexCount, 3u);
EXPECT_TRUE(HasVertexAttribute(mesh->GetVertexAttributes(), VertexAttribute::Position));
EXPECT_TRUE(HasVertexAttribute(mesh->GetVertexAttributes(), VertexAttribute::Normal));
EXPECT_TRUE(HasVertexAttribute(mesh->GetVertexAttributes(), VertexAttribute::UV0));
const auto* vertices = static_cast<const StaticMeshVertex*>(mesh->GetVertexData());
ASSERT_NE(vertices, nullptr);
EXPECT_FLOAT_EQ(vertices[0].position.x, 0.0f);
EXPECT_FLOAT_EQ(vertices[0].position.y, 0.0f);
EXPECT_FLOAT_EQ(vertices[0].position.z, -1.0f);
EXPECT_FLOAT_EQ(vertices[0].normal.z, -1.0f);
EXPECT_FLOAT_EQ(vertices[1].uv0.x, 1.0f);
EXPECT_FLOAT_EQ(vertices[2].uv0.y, 1.0f);
delete mesh;
}
TEST(MeshLoader, GeneratesNormalsAndTangentsWhenRequested) {
MeshLoader loader;
MeshImportSettings settings;
settings.AddImportFlag(MeshImportFlags::GenerateNormals);
settings.AddImportFlag(MeshImportFlags::GenerateTangents);
const std::string path = GetMeshFixturePath("triangle_no_normals.obj");
LoadResult result = loader.Load(path.c_str(), &settings);
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
auto* mesh = static_cast<Mesh*>(result.resource);
EXPECT_TRUE(HasVertexAttribute(mesh->GetVertexAttributes(), VertexAttribute::Normal));
EXPECT_TRUE(HasVertexAttribute(mesh->GetVertexAttributes(), VertexAttribute::Tangent));
EXPECT_TRUE(HasVertexAttribute(mesh->GetVertexAttributes(), VertexAttribute::Bitangent));
const auto* vertices = static_cast<const StaticMeshVertex*>(mesh->GetVertexData());
ASSERT_NE(vertices, nullptr);
EXPECT_GT(vertices[0].normal.Magnitude(), 0.0f);
EXPECT_GT(vertices[0].tangent.Magnitude(), 0.0f);
EXPECT_GT(vertices[0].bitangent.Magnitude(), 0.0f);
delete mesh;
}
} // namespace