108 lines
3.7 KiB
C++
108 lines
3.7 KiB
C++
|
|
#include <gtest/gtest.h>
|
||
|
|
|
||
|
|
#include <XCEngine/Core/Math/Vector3.h>
|
||
|
|
#include <XCEngine/Resources/BuiltinResources.h>
|
||
|
|
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||
|
|
|
||
|
|
#include <cmath>
|
||
|
|
|
||
|
|
using namespace XCEngine::Math;
|
||
|
|
using namespace XCEngine::Resources;
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
constexpr float kAlignmentEpsilon = 1e-6f;
|
||
|
|
|
||
|
|
XCEngine::Core::uint32 GetIndexValue(const Mesh& mesh, XCEngine::Core::uint32 index) {
|
||
|
|
if (mesh.IsUse32BitIndex()) {
|
||
|
|
return static_cast<const XCEngine::Core::uint32*>(mesh.GetIndexData())[index];
|
||
|
|
}
|
||
|
|
|
||
|
|
return static_cast<const XCEngine::Core::uint32>(
|
||
|
|
static_cast<const XCEngine::Core::uint16*>(mesh.GetIndexData())[index]);
|
||
|
|
}
|
||
|
|
|
||
|
|
float ComputeTriangleNormalAlignment(const Mesh& mesh, XCEngine::Core::uint32 triangleStartIndex) {
|
||
|
|
const auto* vertices = static_cast<const StaticMeshVertex*>(mesh.GetVertexData());
|
||
|
|
if (vertices == nullptr || triangleStartIndex + 2 >= mesh.GetIndexCount()) {
|
||
|
|
return 0.0f;
|
||
|
|
}
|
||
|
|
|
||
|
|
const XCEngine::Core::uint32 i0 = GetIndexValue(mesh, triangleStartIndex);
|
||
|
|
const XCEngine::Core::uint32 i1 = GetIndexValue(mesh, triangleStartIndex + 1);
|
||
|
|
const XCEngine::Core::uint32 i2 = GetIndexValue(mesh, triangleStartIndex + 2);
|
||
|
|
if (i0 >= mesh.GetVertexCount() || i1 >= mesh.GetVertexCount() || i2 >= mesh.GetVertexCount()) {
|
||
|
|
return 0.0f;
|
||
|
|
}
|
||
|
|
|
||
|
|
const Vector3& p0 = vertices[i0].position;
|
||
|
|
const Vector3& p1 = vertices[i1].position;
|
||
|
|
const Vector3& p2 = vertices[i2].position;
|
||
|
|
const Vector3 faceNormal = Vector3::Cross(p1 - p0, p2 - p0);
|
||
|
|
if (faceNormal.SqrMagnitude() <= kAlignmentEpsilon) {
|
||
|
|
return 0.0f;
|
||
|
|
}
|
||
|
|
|
||
|
|
const Vector3 averagedVertexNormal =
|
||
|
|
(vertices[i0].normal + vertices[i1].normal + vertices[i2].normal) / 3.0f;
|
||
|
|
if (averagedVertexNormal.SqrMagnitude() <= kAlignmentEpsilon) {
|
||
|
|
return 0.0f;
|
||
|
|
}
|
||
|
|
|
||
|
|
return Vector3::Dot(faceNormal, averagedVertexNormal);
|
||
|
|
}
|
||
|
|
|
||
|
|
float FindReferenceAlignment(const Mesh& mesh) {
|
||
|
|
for (XCEngine::Core::uint32 index = 0; index + 2 < mesh.GetIndexCount(); index += 3) {
|
||
|
|
const float alignment = ComputeTriangleNormalAlignment(mesh, index);
|
||
|
|
if (std::abs(alignment) > kAlignmentEpsilon) {
|
||
|
|
return alignment;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0.0f;
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t CountTrianglesWithUnexpectedAlignment(const Mesh& mesh, float expectedSign) {
|
||
|
|
size_t mismatchCount = 0u;
|
||
|
|
for (XCEngine::Core::uint32 index = 0; index + 2 < mesh.GetIndexCount(); index += 3) {
|
||
|
|
const float alignment = ComputeTriangleNormalAlignment(mesh, index);
|
||
|
|
if (std::abs(alignment) <= kAlignmentEpsilon) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (alignment * expectedSign <= 0.0f) {
|
||
|
|
++mismatchCount;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return mismatchCount;
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BuiltinPrimitiveMesh, SphereUsesSameTriangleWindingConventionAsCube) {
|
||
|
|
LoadResult cubeResult = CreateBuiltinMeshResource(GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Cube));
|
||
|
|
ASSERT_TRUE(cubeResult);
|
||
|
|
ASSERT_NE(cubeResult.resource, nullptr);
|
||
|
|
|
||
|
|
LoadResult sphereResult = CreateBuiltinMeshResource(GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Sphere));
|
||
|
|
ASSERT_TRUE(sphereResult);
|
||
|
|
ASSERT_NE(sphereResult.resource, nullptr);
|
||
|
|
|
||
|
|
auto* cubeMesh = static_cast<Mesh*>(cubeResult.resource);
|
||
|
|
auto* sphereMesh = static_cast<Mesh*>(sphereResult.resource);
|
||
|
|
ASSERT_NE(cubeMesh, nullptr);
|
||
|
|
ASSERT_NE(sphereMesh, nullptr);
|
||
|
|
|
||
|
|
const float cubeAlignment = FindReferenceAlignment(*cubeMesh);
|
||
|
|
ASSERT_GT(std::abs(cubeAlignment), kAlignmentEpsilon);
|
||
|
|
const float expectedSign = cubeAlignment > 0.0f ? 1.0f : -1.0f;
|
||
|
|
|
||
|
|
EXPECT_EQ(CountTrianglesWithUnexpectedAlignment(*cubeMesh, expectedSign), 0u);
|
||
|
|
EXPECT_EQ(CountTrianglesWithUnexpectedAlignment(*sphereMesh, expectedSign), 0u);
|
||
|
|
|
||
|
|
delete cubeMesh;
|
||
|
|
delete sphereMesh;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|