refactor: move scene view selection utilities into renderer
This commit is contained in:
@@ -7,6 +7,7 @@ set(RENDERING_UNIT_TEST_SOURCES
|
||||
test_builtin_forward_pipeline.cpp
|
||||
test_builtin_scene_view_post_pass_plan.cpp
|
||||
test_camera_scene_renderer.cpp
|
||||
test_render_scene_utility.cpp
|
||||
test_render_scene_extractor.cpp
|
||||
)
|
||||
|
||||
|
||||
214
tests/Rendering/unit/test_render_scene_utility.cpp
Normal file
214
tests/Rendering/unit/test_render_scene_utility.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
#include <XCEngine/Components/MeshFilterComponent.h>
|
||||
#include <XCEngine/Components/MeshRendererComponent.h>
|
||||
#include <XCEngine/Core/Asset/IResource.h>
|
||||
#include <XCEngine/Core/Math/Quaternion.h>
|
||||
#include <XCEngine/Rendering/RenderSceneUtility.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
using XCEngine::Components::CameraComponent;
|
||||
using XCEngine::Components::CameraProjectionType;
|
||||
using XCEngine::Components::GameObject;
|
||||
using XCEngine::Components::MeshFilterComponent;
|
||||
using XCEngine::Components::MeshRendererComponent;
|
||||
using XCEngine::Components::Scene;
|
||||
using XCEngine::Math::Matrix4x4;
|
||||
using XCEngine::Math::Vector3;
|
||||
using XCEngine::Rendering::BuildRenderCameraData;
|
||||
using XCEngine::Rendering::CollectRenderItemsForEntityIds;
|
||||
using XCEngine::Resources::Mesh;
|
||||
using XCEngine::Resources::MeshSection;
|
||||
using XCEngine::Resources::StaticMeshVertex;
|
||||
|
||||
bool NearlyEqual(float lhs, float rhs, float epsilon = 1e-4f) {
|
||||
return std::abs(lhs - rhs) <= epsilon;
|
||||
}
|
||||
|
||||
bool NearlyEqual(const Matrix4x4& lhs, const Matrix4x4& rhs, float epsilon = 1e-4f) {
|
||||
for (int row = 0; row < 4; ++row) {
|
||||
for (int column = 0; column < 4; ++column) {
|
||||
if (!NearlyEqual(lhs.m[row][column], rhs.m[row][column], epsilon)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<Mesh> CreateSelectionMesh(uint64_t guid, bool splitIntoTwoSections) {
|
||||
auto mesh = std::make_unique<Mesh>();
|
||||
|
||||
XCEngine::Resources::IResource::ConstructParams params = {};
|
||||
params.name = "SelectionMesh";
|
||||
params.path = "memory://selection_mesh";
|
||||
params.guid = XCEngine::Resources::ResourceGUID(guid);
|
||||
mesh->Initialize(params);
|
||||
|
||||
const StaticMeshVertex vertices[] = {
|
||||
{ Vector3(-1.0f, -1.0f, 0.0f) },
|
||||
{ Vector3(1.0f, -1.0f, 0.0f) },
|
||||
{ Vector3(0.0f, 1.0f, 0.0f) },
|
||||
{ Vector3(-1.0f, -1.0f, 0.0f) },
|
||||
{ Vector3(0.0f, 1.0f, 0.0f) },
|
||||
{ Vector3(-2.0f, 1.0f, 0.0f) }
|
||||
};
|
||||
const uint16_t indices[] = { 0, 1, 2, 3, 4, 5 };
|
||||
|
||||
mesh->SetVertexData(
|
||||
vertices,
|
||||
sizeof(vertices),
|
||||
static_cast<uint32_t>(std::size(vertices)),
|
||||
sizeof(StaticMeshVertex),
|
||||
XCEngine::Resources::VertexAttribute::Position);
|
||||
mesh->SetIndexData(indices, sizeof(indices), static_cast<uint32_t>(std::size(indices)), false);
|
||||
mesh->SetBounds(XCEngine::Math::Bounds(Vector3(-0.5f, 0.0f, 0.0f), Vector3(3.0f, 2.0f, 0.0f)));
|
||||
|
||||
MeshSection firstSection = {};
|
||||
firstSection.baseVertex = 0;
|
||||
firstSection.vertexCount = 3;
|
||||
firstSection.startIndex = 0;
|
||||
firstSection.indexCount = 3;
|
||||
firstSection.materialID = 0;
|
||||
firstSection.bounds = mesh->GetBounds();
|
||||
mesh->AddSection(firstSection);
|
||||
|
||||
if (splitIntoTwoSections) {
|
||||
MeshSection secondSection = {};
|
||||
secondSection.baseVertex = 3;
|
||||
secondSection.vertexCount = 3;
|
||||
secondSection.startIndex = 3;
|
||||
secondSection.indexCount = 3;
|
||||
secondSection.materialID = 1;
|
||||
secondSection.bounds = mesh->GetBounds();
|
||||
mesh->AddSection(secondSection);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
class RenderSceneUtilityTest : public ::testing::Test {
|
||||
protected:
|
||||
GameObject* CreateMeshObject(
|
||||
Scene& scene,
|
||||
const std::string& name,
|
||||
const Vector3& position,
|
||||
bool enabled = true,
|
||||
bool splitIntoTwoSections = false) {
|
||||
std::unique_ptr<Mesh> mesh = CreateSelectionMesh(++m_nextMeshGuid, splitIntoTwoSections);
|
||||
Mesh* meshPtr = mesh.get();
|
||||
m_meshes.push_back(std::move(mesh));
|
||||
|
||||
GameObject* object = scene.CreateGameObject(name);
|
||||
object->GetTransform()->SetPosition(position);
|
||||
|
||||
auto* meshFilter = object->AddComponent<MeshFilterComponent>();
|
||||
auto* meshRenderer = object->AddComponent<MeshRendererComponent>();
|
||||
meshFilter->SetMesh(meshPtr);
|
||||
meshFilter->SetEnabled(enabled);
|
||||
meshRenderer->SetEnabled(enabled);
|
||||
return object;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t m_nextMeshGuid = 1;
|
||||
std::vector<std::unique_ptr<Mesh>> m_meshes;
|
||||
};
|
||||
|
||||
TEST_F(RenderSceneUtilityTest, BuildRenderCameraDataMatchesCameraTransformConvention) {
|
||||
Scene scene("RenderSceneUtilityScene");
|
||||
GameObject* cameraObject = scene.CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
ASSERT_NE(camera, nullptr);
|
||||
|
||||
cameraObject->GetTransform()->SetPosition(Vector3(2.0f, 3.0f, -8.0f));
|
||||
cameraObject->GetTransform()->SetRotation(XCEngine::Math::Quaternion::FromEulerAngles(
|
||||
12.0f * XCEngine::Math::DEG_TO_RAD,
|
||||
-37.0f * XCEngine::Math::DEG_TO_RAD,
|
||||
5.0f * XCEngine::Math::DEG_TO_RAD));
|
||||
camera->SetProjectionType(CameraProjectionType::Perspective);
|
||||
camera->SetFieldOfView(53.0f);
|
||||
camera->SetNearClipPlane(0.03f);
|
||||
camera->SetFarClipPlane(1500.0f);
|
||||
|
||||
const auto cameraData = BuildRenderCameraData(*camera, 1280, 720);
|
||||
|
||||
EXPECT_TRUE(NearlyEqual(
|
||||
cameraData.view.Transpose(),
|
||||
cameraObject->GetTransform()->GetWorldToLocalMatrix()));
|
||||
EXPECT_TRUE(NearlyEqual(
|
||||
cameraData.projection.Transpose(),
|
||||
Matrix4x4::Perspective(
|
||||
53.0f * XCEngine::Math::DEG_TO_RAD,
|
||||
1280.0f / 720.0f,
|
||||
0.03f,
|
||||
1500.0f)));
|
||||
EXPECT_EQ(cameraData.viewportWidth, 1280u);
|
||||
EXPECT_EQ(cameraData.viewportHeight, 720u);
|
||||
EXPECT_EQ(cameraData.worldPosition, cameraObject->GetTransform()->GetPosition());
|
||||
EXPECT_FLOAT_EQ(cameraData.clearColor.r, camera->GetClearColor().r);
|
||||
EXPECT_FLOAT_EQ(cameraData.clearColor.g, camera->GetClearColor().g);
|
||||
EXPECT_FLOAT_EQ(cameraData.clearColor.b, camera->GetClearColor().b);
|
||||
EXPECT_FLOAT_EQ(cameraData.clearColor.a, camera->GetClearColor().a);
|
||||
}
|
||||
|
||||
TEST_F(RenderSceneUtilityTest, CollectRenderItemsForEntityIdsFiltersInvalidTargets) {
|
||||
Scene scene("RenderSceneUtilityScene");
|
||||
|
||||
GameObject* validObject = CreateMeshObject(scene, "Valid", Vector3(0.0f, 0.0f, 4.0f));
|
||||
GameObject* disabledObject = CreateMeshObject(scene, "Disabled", Vector3(2.0f, 0.0f, 4.0f), false);
|
||||
GameObject* inactiveObject = CreateMeshObject(scene, "Inactive", Vector3(-2.0f, 0.0f, 4.0f));
|
||||
inactiveObject->SetActive(false);
|
||||
|
||||
const std::vector<uint64_t> entityIds = {
|
||||
0u,
|
||||
validObject->GetID(),
|
||||
disabledObject->GetID(),
|
||||
inactiveObject->GetID(),
|
||||
validObject->GetID(),
|
||||
987654321u
|
||||
};
|
||||
|
||||
const auto renderables = CollectRenderItemsForEntityIds(
|
||||
scene,
|
||||
entityIds,
|
||||
Vector3(2.0f, 3.0f, -8.0f));
|
||||
|
||||
ASSERT_EQ(renderables.size(), 1u);
|
||||
EXPECT_EQ(renderables[0].gameObject, validObject);
|
||||
EXPECT_EQ(renderables[0].meshRenderer, validObject->GetComponent<MeshRendererComponent>());
|
||||
EXPECT_TRUE(renderables[0].hasSection);
|
||||
}
|
||||
|
||||
TEST_F(RenderSceneUtilityTest, CollectRenderItemsForEntityIdsExpandsMeshSections) {
|
||||
Scene scene("RenderSceneUtilityScene");
|
||||
GameObject* object = CreateMeshObject(
|
||||
scene,
|
||||
"Sectioned",
|
||||
Vector3(1.0f, 0.0f, 6.0f),
|
||||
true,
|
||||
true);
|
||||
|
||||
const auto renderables = CollectRenderItemsForEntityIds(
|
||||
scene,
|
||||
{ object->GetID() },
|
||||
Vector3(2.0f, 3.0f, -8.0f));
|
||||
|
||||
ASSERT_EQ(renderables.size(), 2u);
|
||||
EXPECT_EQ(renderables[0].gameObject, object);
|
||||
EXPECT_EQ(renderables[0].sectionIndex, 0u);
|
||||
EXPECT_EQ(renderables[0].materialIndex, 0u);
|
||||
EXPECT_EQ(renderables[1].sectionIndex, 1u);
|
||||
EXPECT_EQ(renderables[1].materialIndex, 1u);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user