Add coordinate system converter
This commit is contained in:
104
openVulkanoCpp/Math/CoordinateSystemConverter.hpp
Normal file
104
openVulkanoCpp/Math/CoordinateSystemConverter.hpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Math.hpp"
|
||||||
|
#include "CoordinateSystem.hpp"
|
||||||
|
|
||||||
|
namespace OpenVulkano::Math
|
||||||
|
{
|
||||||
|
class CoordinateSystemConverter
|
||||||
|
{
|
||||||
|
inline static Math::Matrix4i ROT_XP90 = { 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1 };
|
||||||
|
inline static Math::Matrix4i ROT_XN90 = { 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1 };
|
||||||
|
CoordinateSystem from, to;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr CoordinateSystemConverter(CoordinateSystem from, CoordinateSystem to) : from(from), to(to) {}
|
||||||
|
|
||||||
|
template<typename T, glm::qualifier Q>
|
||||||
|
[[nodiscard]] Vector<3, T, Q> Convert(const Vector<3, T, Q>& v) const
|
||||||
|
{
|
||||||
|
if (from == to) return v;
|
||||||
|
return ConvertHandedness(ConvertUp(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, glm::qualifier Q>
|
||||||
|
[[nodiscard]] Vector<4, T, Q> Convert(const Vector<4, T, Q>& v) const
|
||||||
|
{
|
||||||
|
if (from == to) return v;
|
||||||
|
return { ConvertHandedness(ConvertUp(reinterpret_cast<const glm::vec<3,T,Q>&>(v))), v.w };
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, glm::qualifier Q>
|
||||||
|
[[nodiscard]] Matrix<3, T, Q> Convert(const Matrix<3, T, Q>& mat) const
|
||||||
|
{
|
||||||
|
if (from == to) return mat;
|
||||||
|
return ConvertHandedness(ConvertUp(mat));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, glm::qualifier Q>
|
||||||
|
[[nodiscard]] Matrix<4, T, Q> Convert(const Matrix<4, T, Q>& mat) const
|
||||||
|
{
|
||||||
|
if (from == to) return mat;
|
||||||
|
return ConvertHandedness(ConvertUp(mat));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Conversion functions
|
||||||
|
template<typename T, glm::qualifier Q>
|
||||||
|
[[nodiscard]] Vector<3, T, Q> ConvertHandedness(const Vector<3, T, Q>& v) const
|
||||||
|
{
|
||||||
|
if (from.GetHandedness() == to.GetHandedness()) return v;
|
||||||
|
if (to == CoordinateSystem::UpAxis::Y)
|
||||||
|
return { v.x, v.y, -v.z };
|
||||||
|
return { v.x, -v.y, v.z };
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, glm::qualifier Q>
|
||||||
|
[[nodiscard]] Vector<3, T, Q> ConvertUp(const Vector<3, T, Q>& v) const
|
||||||
|
{
|
||||||
|
if (from.GetUpAxis() == to.GetUpAxis()) return v;
|
||||||
|
if (from.GetUpAxis() == CoordinateSystem::UpAxis::Y)
|
||||||
|
return { v.x, -v.z, v.y };
|
||||||
|
return { v.x, v.z, -v.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int S, typename T, glm::qualifier Q>
|
||||||
|
[[nodiscard]] Matrix<S, T, Q> ConvertHandedness(Matrix<S, T, Q> mat) const
|
||||||
|
{
|
||||||
|
constexpr int AXIS_Y = 1;
|
||||||
|
constexpr int AXIS_Z = 2;
|
||||||
|
if (from.GetHandedness() == to.GetHandedness()) return mat;
|
||||||
|
if (to == CoordinateSystem::UpAxis::Y)
|
||||||
|
for(int i = 0; i < S; i++) mat[i][AXIS_Z] = -mat[i][AXIS_Z];
|
||||||
|
else
|
||||||
|
for(int i = 0; i < S; i++) mat[i][AXIS_Y] = -mat[i][AXIS_Y];
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int S, typename T, glm::qualifier Q>
|
||||||
|
[[nodiscard]] Matrix<S, T, Q> ConvertUp(const Matrix<S, T, Q>& mat) const
|
||||||
|
{
|
||||||
|
if (from.GetUpAxis() == to.GetUpAxis()) return mat;
|
||||||
|
Matrix4i* conv;
|
||||||
|
Matrix4i* inverse;
|
||||||
|
if (from.GetUpAxis() == CoordinateSystem::UpAxis::Y && to.GetUpAxis() == CoordinateSystem::UpAxis::Z)
|
||||||
|
{
|
||||||
|
conv = &ROT_XP90;
|
||||||
|
inverse = &ROT_XN90;
|
||||||
|
}
|
||||||
|
else if (from.GetUpAxis() == CoordinateSystem::UpAxis::Z && to.GetUpAxis() == CoordinateSystem::UpAxis::Y)
|
||||||
|
{
|
||||||
|
conv = &ROT_XN90;
|
||||||
|
inverse = &ROT_XP90;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Matrix<S, T, Q>(*conv) * mat * Matrix<S, T, Q>(*inverse);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
885
tests/Math/CoordinateSystemConverterTest.cpp
Normal file
885
tests/Math/CoordinateSystemConverterTest.cpp
Normal file
@@ -0,0 +1,885 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Math/CoordinateSystemConverter.hpp"
|
||||||
|
#include <catch2/catch_all.hpp>
|
||||||
|
|
||||||
|
using namespace OpenVulkano::Math;
|
||||||
|
using Catch::Approx;
|
||||||
|
|
||||||
|
#define REQUIRE_VEC_EQ(vec1, vec2, eps) { \
|
||||||
|
REQUIRE(vec1.length() == vec2.length()); \
|
||||||
|
for (size_t i = 0; i < vec1.length(); i++) \
|
||||||
|
REQUIRE(Approx(vec1[i]).epsilon(eps) == vec2[i]); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define REQUIRE_VEC_CLOSE(v1, v2) REQUIRE_VEC_EQ(v1, v2, 1e-5f)
|
||||||
|
|
||||||
|
#define REQUIRE_MAT_EQ(mat1, mat2, eps) { \
|
||||||
|
REQUIRE(mat1.length() == mat2.length()); \
|
||||||
|
for (size_t c = 0; c < mat1.length(); c++) \
|
||||||
|
REQUIRE_VEC_EQ(mat1[c], mat2[c], eps) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define REQUIRE_MAT_CLOSE(mat1, mat2) REQUIRE_MAT_EQ(mat1, mat2, 1e-5f)
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Same system (no conversion)", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
CoordinateSystem sys(CoordinateSystem::RIGHT_HANDED_Y_UP);
|
||||||
|
CoordinateSystemConverter converter(sys, sys);
|
||||||
|
|
||||||
|
SECTION("Vec3 unchanged")
|
||||||
|
{
|
||||||
|
glm::vec3 v(1.0f, 2.0f, 3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(2.0f));
|
||||||
|
REQUIRE(result.z == Approx(3.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Vec4 unchanged")
|
||||||
|
{
|
||||||
|
glm::vec4 v(1.0f, 2.0f, 3.0f, 4.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(2.0f));
|
||||||
|
REQUIRE(result.z == Approx(3.0f));
|
||||||
|
REQUIRE(result.w == Approx(4.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Handedness conversion", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Right-handed Y-up to Left-handed Y-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::vec3 v(1.0f, 2.0f, 3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(2.0f));
|
||||||
|
REQUIRE(result.z == Approx(-3.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Right-handed Z-up to Left-handed Z-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::vec3 v(5.0f, 6.0f, 7.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
REQUIRE(result.x == Approx(5.0f));
|
||||||
|
REQUIRE(result.y == Approx(-6.0f));
|
||||||
|
REQUIRE(result.z == Approx(7.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Up axis conversion", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Y-up to Z-up (right-handed)")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::vec3 v(1.0f, 2.0f, 3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(-3.0f));
|
||||||
|
REQUIRE(result.z == Approx(2.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Z-up to Y-up (right-handed)")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::vec3 v(1.0f, 2.0f, 3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(3.0f));
|
||||||
|
REQUIRE(result.z == Approx(-2.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Combined conversion", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Right-handed Y-up to Left-handed Z-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::vec3 v(1.0f, 2.0f, 3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
// First convert up axis: Y->Z transforms (x,y,z) to (x,z,-y) = (1,3,-2)
|
||||||
|
// Then convert handedness: negate z = (1,3,2)
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(3.0f));
|
||||||
|
REQUIRE(result.z == Approx(2.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Left-handed Z-up to Right-handed Y-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::vec3 v(4.0f, 5.0f, 6.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
// First convert up axis: Z->Y transforms (x,y,z) to (x,-z,y) = (4,-6,5)
|
||||||
|
// Then convert handedness: negate z = (4,-6,-5)
|
||||||
|
REQUIRE(result.x == Approx(4.0f));
|
||||||
|
REQUIRE(result.y == Approx(6.0f));
|
||||||
|
REQUIRE(result.z == Approx(5.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Vec4 conversion", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Vec4 preserves w component")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::vec4 v(1.0f, 2.0f, 3.0f, 9.5f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(3.0f));
|
||||||
|
REQUIRE(result.z == Approx(2.0f));
|
||||||
|
REQUIRE(result.w == Approx(9.5f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Edge cases", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Zero vector")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::vec3 v(0.0f, 0.0f, 0.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
REQUIRE(result.x == Approx(0.0f));
|
||||||
|
REQUIRE(result.y == Approx(0.0f));
|
||||||
|
REQUIRE(result.z == Approx(0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Negative values")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::vec3 v(-1.0f, -2.0f, -3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
REQUIRE(result.x == Approx(-1.0f));
|
||||||
|
REQUIRE(result.y == Approx(-2.0f));
|
||||||
|
REQUIRE(result.z == Approx(3.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Identity Conversion", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Same coordinate system should return identity for Vec3")
|
||||||
|
{
|
||||||
|
auto systems = GENERATE(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
CoordinateSystemConverter converter(systems, systems);
|
||||||
|
Vector<3, float, glm::defaultp> v(1.0f, 2.0f, 3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
|
||||||
|
REQUIRE_VEC_CLOSE(result, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Same coordinate system should return identity for Vec4")
|
||||||
|
{
|
||||||
|
auto systems = GENERATE(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
CoordinateSystemConverter converter(systems, systems);
|
||||||
|
Vector<4, float, glm::defaultp> v(1.0f, 2.0f, 3.0f, 1.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
|
||||||
|
REQUIRE_VEC_CLOSE(result, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Vec3 Y-up to Z-up Conversions", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Right-handed Y-up to Right-handed Z-up - Unit vectors")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
// RH Y-up: X=right, Y=up, Z=backward (toward camera)
|
||||||
|
// RH Z-up: X=right, Y=forward (away from camera), Z=up
|
||||||
|
// Expected transformation: X->X, Y->Z, Z->-Y
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> xAxis(1.0f, 0.0f, 0.0f);
|
||||||
|
auto resultX = converter.Convert(xAxis);
|
||||||
|
REQUIRE(resultX.x == Approx(1.0f));
|
||||||
|
REQUIRE(resultX.y == Approx(0.0f));
|
||||||
|
REQUIRE(resultX.z == Approx(0.0f));
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> yAxis(0.0f, 1.0f, 0.0f);
|
||||||
|
auto resultY = converter.Convert(yAxis);
|
||||||
|
REQUIRE(resultY.x == Approx(0.0f));
|
||||||
|
REQUIRE(resultY.z == Approx(1.0f));
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> zAxis(0.0f, 0.0f, 1.0f);
|
||||||
|
auto resultZ = converter.Convert(zAxis);
|
||||||
|
REQUIRE(resultZ.y == Approx(-1.0f).margin(0.0001));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Right-handed Z-up to Right-handed Y-up - Unit vectors")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
// RH Z-up: X=right, Y=forward, Z=up
|
||||||
|
// RH Y-up: X=right, Y=up, Z=backward
|
||||||
|
// Expected transformation: X->X, Y->-Z, Z->Y
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> xAxis(1.0f, 0.0f, 0.0f);
|
||||||
|
auto resultX = converter.Convert(xAxis);
|
||||||
|
REQUIRE(resultX.x == Approx(1.0f));
|
||||||
|
REQUIRE(resultX.y == Approx(0.0f));
|
||||||
|
REQUIRE(resultX.z == Approx(0.0f));
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> yAxis(0.0f, 1.0f, 0.0f);
|
||||||
|
auto resultY = converter.Convert(yAxis);
|
||||||
|
REQUIRE(abs(resultY.z) == Approx(1.0f).margin(0.0001));
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> zAxis(0.0f, 0.0f, 1.0f);
|
||||||
|
auto resultZ = converter.Convert(zAxis);
|
||||||
|
REQUIRE(resultZ.y == Approx(1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Left-handed Y-up to Left-handed Z-up - Unit vectors")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
// LH Y-up: X=right, Y=up, Z=forward (away from camera)
|
||||||
|
// LH Z-up: X=right, Y=backward, Z=up (or similar)
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> xAxis(1.0f, 0.0f, 0.0f);
|
||||||
|
auto resultX = converter.Convert(xAxis);
|
||||||
|
REQUIRE(resultX.x == Approx(1.0f));
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> yAxis(0.0f, 1.0f, 0.0f);
|
||||||
|
auto resultY = converter.Convert(yAxis);
|
||||||
|
REQUIRE(abs(resultY.z) == Approx(1.0f).margin(0.0001));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Handedness Changes", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Right-handed Y-up to Left-handed Y-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handedness change typically negates one axis (usually Z)
|
||||||
|
// RH Y-up: Z points backward, LH Y-up: Z points forward
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> v(1.0f, 2.0f, 3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
|
||||||
|
// X and Y should remain the same or similar
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(2.0f));
|
||||||
|
// Z should be negated
|
||||||
|
REQUIRE(result.z == Approx(-3.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Right-handed Z-up to Left-handed Z-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> v(1.0f, 2.0f, 3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
|
||||||
|
// One axis should be negated for handedness change
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(-2.0f));
|
||||||
|
REQUIRE(result.z == Approx(3.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Left-handed Y-up to Right-handed Y-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> v(1.0f, 2.0f, 3.0f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(2.0f));
|
||||||
|
REQUIRE(result.z == Approx(-3.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Vec3 Length Preservation", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Vector length should be preserved across all conversions")
|
||||||
|
{
|
||||||
|
auto fromSys = GENERATE(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
auto toSys = GENERATE(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
CoordinateSystemConverter converter(fromSys, toSys);
|
||||||
|
Vector<3, float, glm::defaultp> v(3.0f, 4.0f, 5.0f);
|
||||||
|
|
||||||
|
float originalLength = glm::length(v);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
float resultLength = glm::length(result);
|
||||||
|
|
||||||
|
REQUIRE(resultLength == Approx(originalLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Vec4 Conversions", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Vec4 w-component should be preserved")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<4, float, glm::defaultp> point(1.0f, 2.0f, 3.0f, 1.0f);
|
||||||
|
auto result = converter.Convert(point);
|
||||||
|
|
||||||
|
REQUIRE(result.w == Approx(1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Vec4 direction vector (w=0)")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<4, float, glm::defaultp> direction(1.0f, 2.0f, 3.0f, 0.0f);
|
||||||
|
auto result = converter.Convert(direction);
|
||||||
|
|
||||||
|
REQUIRE(result.w == Approx(0.0f));
|
||||||
|
REQUIRE(result.x == Approx(1.0f));
|
||||||
|
REQUIRE(result.y == Approx(2.0f));
|
||||||
|
REQUIRE(result.z == Approx(-3.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Matrix3 Conversions", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Identity matrix through different coordinate systems")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Matrix<3, float, glm::defaultp> identity(1.0f);
|
||||||
|
auto result = converter.Convert(identity);
|
||||||
|
|
||||||
|
// Each column represents a basis vector transformation
|
||||||
|
// Column 0 is X axis, Column 1 is Y axis, Column 2 is Z axis
|
||||||
|
REQUIRE(result[0][0] == Approx(1.0f)); // X.x
|
||||||
|
REQUIRE(result[1][0] == Approx(0.0f)); // X.y
|
||||||
|
REQUIRE(result[2][0] == Approx(0.0f)); // X.z
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Matrix3 basis vector conversion")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Matrix<3, float, glm::defaultp> mat(1.0f);
|
||||||
|
auto result = converter.Convert(mat);
|
||||||
|
|
||||||
|
// When converting handedness, determinant sign should flip
|
||||||
|
float detOriginal = glm::determinant(mat);
|
||||||
|
float detResult = glm::determinant(result);
|
||||||
|
|
||||||
|
REQUIRE(detOriginal * detResult == Approx(-1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Matrix3 with rotation around Y axis")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
// Simple 90-degree rotation around Y axis in RH Y-up
|
||||||
|
Matrix<3, float, glm::defaultp> rotY(
|
||||||
|
0.0f, 0.0f, 1.0f,
|
||||||
|
0.0f, 1.0f, 0.0f,
|
||||||
|
-1.0f, 0.0f, 0.0f
|
||||||
|
);
|
||||||
|
|
||||||
|
auto result = converter.Convert(rotY);
|
||||||
|
|
||||||
|
// The transformation should maintain the rotation semantics
|
||||||
|
INFO("Rotation matrix converted between coordinate systems");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Matrix4 Conversions", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Matrix4 translation component conversion")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Matrix<4, float, glm::defaultp> transform(1.0f);
|
||||||
|
transform[3][0] = 10.0f;
|
||||||
|
transform[3][1] = 20.0f;
|
||||||
|
transform[3][2] = 30.0f;
|
||||||
|
|
||||||
|
auto result = converter.Convert(transform);
|
||||||
|
|
||||||
|
// Translation should convert like a position vector
|
||||||
|
Vector<3, float, glm::defaultp> translation(10.0f, 20.0f, 30.0f);
|
||||||
|
Vector<3, float, glm::defaultp> expectedTranslation = converter.Convert(translation);
|
||||||
|
|
||||||
|
REQUIRE(result[3][0] == Approx(expectedTranslation.x));
|
||||||
|
REQUIRE(result[3][1] == Approx(expectedTranslation.y));
|
||||||
|
REQUIRE(result[3][2] == Approx(expectedTranslation.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Matrix4 homogeneous row preservation")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Matrix<4, float, glm::defaultp> mat(1.0f);
|
||||||
|
auto result = converter.Convert(mat);
|
||||||
|
|
||||||
|
// Bottom row should remain [0,0,0,1]
|
||||||
|
REQUIRE(result[0][3] == Approx(0.0f));
|
||||||
|
REQUIRE(result[1][3] == Approx(0.0f));
|
||||||
|
REQUIRE(result[2][3] == Approx(0.0f));
|
||||||
|
REQUIRE(result[3][3] == Approx(1.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Round-trip Conversions", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Vec3 round-trip: RH Y-up -> RH Z-up -> RH Y-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter toZUp(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
CoordinateSystemConverter toYUp(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> original(1.5f, 2.5f, 3.5f);
|
||||||
|
auto intermediate = toZUp.Convert(original);
|
||||||
|
auto result = toYUp.Convert(intermediate);
|
||||||
|
|
||||||
|
REQUIRE_VEC_CLOSE(result, original);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Vec3 round-trip: RH Y-up -> LH Y-up -> RH Y-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter toLH(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
CoordinateSystemConverter toRH(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> original(1.5f, 2.5f, 3.5f);
|
||||||
|
auto intermediate = toLH.Convert(original);
|
||||||
|
auto result = toRH.Convert(intermediate);
|
||||||
|
|
||||||
|
REQUIRE_VEC_CLOSE(result, original);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Vec4 round-trip: LH Z-up -> RH Y-up -> LH Z-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter conv1(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
CoordinateSystemConverter conv2(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<4, float, glm::defaultp> original(1.5f, 2.5f, 3.5f, 0.75f);
|
||||||
|
auto intermediate = conv1.Convert(original);
|
||||||
|
auto result = conv2.Convert(intermediate);
|
||||||
|
|
||||||
|
REQUIRE_VEC_CLOSE(result, original);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Mat3 round-trip: RH Z-up -> LH Y-up -> RH Z-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter conv1(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
CoordinateSystemConverter conv2(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Matrix<3, float, glm::defaultp> original(
|
||||||
|
1.0f, 2.0f, 3.0f,
|
||||||
|
4.0f, 5.0f, 6.0f,
|
||||||
|
7.0f, 8.0f, 9.0f
|
||||||
|
);
|
||||||
|
|
||||||
|
auto intermediate = conv1.Convert(original);
|
||||||
|
auto result = conv2.Convert(intermediate);
|
||||||
|
|
||||||
|
REQUIRE_MAT_CLOSE(result, original);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Mat4 round-trip: LH Y-up -> RH Z-up -> LH Y-up")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter conv1(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
CoordinateSystemConverter conv2(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Matrix<4, float, glm::defaultp> original(1.0f);
|
||||||
|
original[3][0] = 5.0f;
|
||||||
|
original[3][1] = 10.0f;
|
||||||
|
original[3][2] = 15.0f;
|
||||||
|
|
||||||
|
auto intermediate = conv1.Convert(original);
|
||||||
|
auto result = conv2.Convert(intermediate);
|
||||||
|
|
||||||
|
REQUIRE_MAT_CLOSE(result, original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Chained Conversions", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Three-way conversion chain should be consistent")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter conv1(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
CoordinateSystemConverter conv2(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
CoordinateSystemConverter conv3(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> v(2.0f, 3.0f, 4.0f);
|
||||||
|
|
||||||
|
auto step1 = conv1.Convert(v);
|
||||||
|
auto step2 = conv2.Convert(step1);
|
||||||
|
auto step3 = conv3.Convert(step2);
|
||||||
|
|
||||||
|
// Direct conversion for comparison
|
||||||
|
CoordinateSystemConverter direct(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
auto directResult = direct.Convert(v);
|
||||||
|
|
||||||
|
// Multi-step and direct conversion should yield same result
|
||||||
|
REQUIRE_VEC_CLOSE(step3, directResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Edge Cases", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Zero vector conversion")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> zero(0.0f, 0.0f, 0.0f);
|
||||||
|
auto result = converter.Convert(zero);
|
||||||
|
|
||||||
|
REQUIRE_VEC_CLOSE(result, zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Negative components")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> negative(-1.0f, -2.0f, -3.0f);
|
||||||
|
auto result = converter.Convert(negative);
|
||||||
|
|
||||||
|
REQUIRE(result.x == Approx(-1.0f));
|
||||||
|
REQUIRE(result.y == Approx(-2.0f));
|
||||||
|
REQUIRE(result.z == Approx(3.0f)); // Z negated for handedness change
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Very small values")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> small(0.0001f, 0.0002f, 0.0003f);
|
||||||
|
auto result = converter.Convert(small);
|
||||||
|
|
||||||
|
float originalLength = glm::length(small);
|
||||||
|
float resultLength = glm::length(result);
|
||||||
|
REQUIRE(resultLength == Approx(originalLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Double precision support")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, double, glm::defaultp> v(1.0, 2.0, 3.0);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
|
||||||
|
double originalLength = glm::length(v);
|
||||||
|
double resultLength = glm::length(result);
|
||||||
|
REQUIRE(resultLength == Approx(originalLength));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Matrix Column Interpretation", "[CoordinateSystemConverter]")
|
||||||
|
{
|
||||||
|
SECTION("Matrix columns should convert as vectors")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Matrix<3, float, glm::defaultp> mat(
|
||||||
|
1.0f, 0.0f, 0.0f,
|
||||||
|
0.0f, 2.0f, 0.0f,
|
||||||
|
0.0f, 0.0f, 3.0f
|
||||||
|
);
|
||||||
|
|
||||||
|
auto result = converter.Convert(mat);
|
||||||
|
|
||||||
|
// Each column should convert like a vector
|
||||||
|
Vector<3, float, glm::defaultp> col0(mat[0][0], mat[0][1], mat[0][2]);
|
||||||
|
Vector<3, float, glm::defaultp> expectedCol0 = converter.Convert(col0);
|
||||||
|
|
||||||
|
REQUIRE(result[0][0] == Approx(expectedCol0.x));
|
||||||
|
REQUIRE(result[0][1] == Approx(expectedCol0.y));
|
||||||
|
REQUIRE(result[0][2] == Approx(expectedCol0.z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Full 8-system conversions", "[CoordinateSystemConverter][Exhaustive]")
|
||||||
|
{
|
||||||
|
auto fromSys = GENERATE(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
auto toSys = GENERATE(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
CoordinateSystemConverter converter(fromSys, toSys);
|
||||||
|
|
||||||
|
// Random non-zero vector
|
||||||
|
Vector<3, float, glm::defaultp> v(1.23f, -4.56f, 7.89f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
|
||||||
|
// Length should be preserved
|
||||||
|
REQUIRE(glm::length(result) == Approx(glm::length(v)));
|
||||||
|
|
||||||
|
// Check components are finite
|
||||||
|
REQUIRE(std::isfinite(result.x));
|
||||||
|
REQUIRE(std::isfinite(result.y));
|
||||||
|
REQUIRE(std::isfinite(result.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Vec4 arbitrary w-component", "[CoordinateSystemConverter][Vec4]")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<4, float, glm::defaultp> v(2.0f, -3.0f, 4.0f, 0.5f);
|
||||||
|
auto result = converter.Convert(v);
|
||||||
|
|
||||||
|
// w should remain unchanged
|
||||||
|
REQUIRE(result.w == Approx(0.5f));
|
||||||
|
|
||||||
|
// Check vector part length preservation
|
||||||
|
REQUIRE(glm::length(glm::vec3(result.x, result.y, result.z)) == Approx(glm::length(glm::vec3(2.0f, -3.0f, 4.0f))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Very large / small / NaN / Inf values", "[CoordinateSystemConverter][Edge]")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> largeVec(1e30f, -1e30f, 1e30f);
|
||||||
|
Vector<3, float, glm::defaultp> smallVec(1e-30f, -1e-30f, 1e-30f);
|
||||||
|
Vector<3, float, glm::defaultp> nanVec(NAN, 0.0f, 0.0f);
|
||||||
|
Vector<3, float, glm::defaultp> infVec(INFINITY, -INFINITY, 1.0f);
|
||||||
|
|
||||||
|
auto rLarge = converter.Convert(largeVec);
|
||||||
|
auto rSmall = converter.Convert(smallVec);
|
||||||
|
auto rNan = converter.Convert(nanVec);
|
||||||
|
auto rInf = converter.Convert(infVec);
|
||||||
|
|
||||||
|
// Length preservation for large/small
|
||||||
|
REQUIRE(glm::length(rLarge) == Approx(glm::length(largeVec)));
|
||||||
|
REQUIRE(glm::length(rSmall) == Approx(glm::length(smallVec)));
|
||||||
|
|
||||||
|
// NaN and Inf should propagate
|
||||||
|
REQUIRE(std::isnan(rNan.x));
|
||||||
|
REQUIRE(std::isinf(rInf.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Mat4 rotation and scale", "[CoordinateSystemConverter][Mat4]")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
// Non-uniform scale + rotation
|
||||||
|
glm::mat4 mat(1.0f);
|
||||||
|
mat[0][0] = 2.0f; mat[1][1] = 3.0f; mat[2][2] = 4.0f; // scaling
|
||||||
|
mat = glm::rotate(mat, glm::radians(90.0f), glm::vec3(0,1,0)); // rotation around Y axis
|
||||||
|
|
||||||
|
auto result = converter.Convert(mat);
|
||||||
|
|
||||||
|
// Check that determinant magnitude is preserved (handedness may flip sign)
|
||||||
|
float detOrig = glm::determinant(mat);
|
||||||
|
float detResult = glm::determinant(result);
|
||||||
|
REQUIRE(std::abs(detResult) == Approx(std::abs(detOrig)));
|
||||||
|
|
||||||
|
// Check translation row unchanged if identity
|
||||||
|
REQUIRE(result[3][3] == Approx(1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Matrix4 round-trip consistency", "[CoordinateSystemConverter][Mat4]")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter conv1(
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
CoordinateSystemConverter conv2(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Z_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Y_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
glm::mat4 original(1.0f);
|
||||||
|
original[3] = glm::vec4(5.0f, -10.0f, 15.0f, 1.0f);
|
||||||
|
original[0][0] = 2.0f; original[1][1] = 3.0f; original[2][2] = 4.0f; // scale
|
||||||
|
|
||||||
|
auto intermediate = conv1.Convert(original);
|
||||||
|
auto roundTrip = conv2.Convert(intermediate);
|
||||||
|
|
||||||
|
// All elements should match original
|
||||||
|
for (int c = 0; c < 4; ++c)
|
||||||
|
for (int r = 0; r < 4; ++r)
|
||||||
|
REQUIRE(roundTrip[c][r] == Approx(original[c][r]));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CoordinateSystemConverter - Vec3 all-zero / negative / mixed", "[CoordinateSystemConverter][Edge]")
|
||||||
|
{
|
||||||
|
CoordinateSystemConverter converter(
|
||||||
|
CoordinateSystem::LEFT_HANDED_Y_UP,
|
||||||
|
CoordinateSystem::RIGHT_HANDED_Z_UP
|
||||||
|
);
|
||||||
|
|
||||||
|
Vector<3, float, glm::defaultp> zero(0.0f, 0.0f, 0.0f);
|
||||||
|
Vector<3, float, glm::defaultp> neg(-1.0f, -2.0f, -3.0f);
|
||||||
|
Vector<3, float, glm::defaultp> mixed(-1.0f, 0.0f, 2.0f);
|
||||||
|
|
||||||
|
REQUIRE_VEC_CLOSE(converter.Convert(zero), zero);
|
||||||
|
REQUIRE(glm::length(converter.Convert(neg)) == Approx(glm::length(neg)));
|
||||||
|
REQUIRE(glm::length(converter.Convert(mixed)) == Approx(glm::length(mixed)));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user