diff --git a/include/omath/Matrix.h b/include/omath/Matrix.h index 9610377..1cb2633 100644 --- a/include/omath/Matrix.h +++ b/include/omath/Matrix.h @@ -2,7 +2,7 @@ #include #include #include - +#include namespace omath { @@ -13,11 +13,20 @@ namespace omath public: Matrix(size_t rows, size_t columns); - explicit Matrix(const std::vector> &rows); + Matrix(const std::initializer_list>& rows); [[nodiscard]] static Matrix ToScreenMatrix(float screenWidth, float screenHeight); + [[nodiscard]] + static Matrix TranslationMatrix(const Vector3& diff); + + [[nodiscard]] + static Matrix OrientationMatrix(const Vector3& forward, const Vector3& right, const Vector3& up); + + [[nodiscard]] + static Matrix ProjectionMatrix(float fielOfView, float aspectRatio,float near, float far); + Matrix(const Matrix &other); Matrix(size_t rows, size_t columns, const float *pRaw); @@ -42,7 +51,7 @@ namespace omath void SetDataFromRaw(const float* pRawMatrix); [[nodiscard]] - Matrix Transpose(); + Matrix Transpose() const; void Set(float val); diff --git a/include/omath/projection/Camera.h b/include/omath/projection/Camera.h index c96dd16..d389077 100644 --- a/include/omath/projection/Camera.h +++ b/include/omath/projection/Camera.h @@ -12,10 +12,19 @@ namespace omath::projection { + class ViewPort final + { + public: + float m_width; + float m_height; + + [[nodiscard]] float AspectRatio() const {return m_width / m_height;} + }; + class Camera { public: - Camera(const Vector3& position, const Vector3& viewAngles, const Vector3& viewPort, float fov, float near, float far); + Camera(const Vector3& position, const Vector3& viewAngles, const ViewPort& viewPort, float fov, float near, float far); void SetViewAngles(const Vector3& viewAngles); [[nodiscard]] const Vector3& GetViewAngles() const; static float& GetFloat1(); @@ -29,7 +38,7 @@ namespace omath::projection [[nodiscard]] std::expected WorldToScreen(const Vector3& worldPosition) const; - Vector3 m_viewPort; + ViewPort m_viewPort{}; float m_fieldOfView; float m_farPlaneDistance; diff --git a/source/matrix.cpp b/source/matrix.cpp index c6e0d69..cd98ee3 100644 --- a/source/matrix.cpp +++ b/source/matrix.cpp @@ -1,10 +1,13 @@ #include "omath/Matrix.h" +#include "omath/Vector3.h" +#include "omath/angles.h" + #include -#include "omath/Vector3.h" #include #include #include +#include namespace omath @@ -22,17 +25,26 @@ namespace omath Set(0.f); } - Matrix::Matrix(const std::vector> &rows) + Matrix::Matrix(const std::initializer_list>& rows) { m_rows = rows.size(); - m_columns = rows[0].size(); + m_columns = rows.begin()->size(); + for (const auto& row : rows) + if (row.size() != m_columns) + throw std::invalid_argument("All rows must have the same number of columns."); + m_data = std::make_unique(m_rows * m_columns); - for (size_t i = 0; i < m_rows; ++i) - for (size_t j = 0; j < m_columns; ++j) - At(i,j) = rows[i][j]; + size_t i = 0; + for (const auto& row : rows) + { + size_t j = 0; + for (const auto& value : row) + At(i, j++) = value; + ++i; + } } Matrix::Matrix(const Matrix &other) @@ -247,7 +259,7 @@ namespace omath return ((i + j + 2) % 2 == 0) ? tmp : -tmp; } - Matrix Matrix::Transpose() + Matrix Matrix::Transpose() const { Matrix transposed = {m_columns, m_rows}; @@ -298,14 +310,51 @@ namespace omath return Strip(i, j).Determinant(); } - Matrix Matrix::ToScreenMatrix(float screenWidth, float screenHeight) + Matrix Matrix::ToScreenMatrix(const float screenWidth, const float screenHeight) { - return Matrix({ - {screenWidth / 2.f, 0.f, 0.f, 0.f}, - {0.f, -screenHeight / 2.f, 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {screenWidth / 2.f, screenHeight / 2.f, 0.f, 1.f}, - }); + return + { + {screenWidth / 2.f, 0.f, 0.f, 0.f}, + {0.f, -screenHeight / 2.f, 0.f, 0.f}, + {0.f, 0.f, 1.f, 0.f}, + {screenWidth / 2.f, screenHeight / 2.f, 0.f, 1.f}, + }; + } + + Matrix Matrix::TranslationMatrix(const Vector3 &diff) + { + return + { + {1.f, 0.f, 0.f, 0.f}, + {0.f, 1.f, 0.f, 0.f}, + {0.f, 0.f, 1.f, 0.f}, + {diff.x, diff.y, diff.z, 1.f}, + }; + } + + Matrix Matrix::OrientationMatrix(const Vector3 &forward, const Vector3 &right, const Vector3 &up) + { + return + { + {right.x, up.x, forward.x, 0.f}, + {right.y, up.y, forward.y, 0.f}, + {right.z, up.z, forward.z, 0.f}, + {0.f, 0.f, 0.f, 1.f}, + }; + } + + Matrix Matrix::ProjectionMatrix(const float fielOfView, const float aspectRatio, const float near, + const float far) + { + const float fovHalfTan = std::tan(angles::DegreesToRadians(fielOfView) / 2.f); + + return + { + {1.f / (aspectRatio*fovHalfTan), 0.f, 0.f, 0.f}, + {0.f, 1.f / fovHalfTan, 0.f, 0.f}, + {0.f, 0.f, (far + near) / (far - near), 2.f * near * far / (far - near)}, + {0.f, 0.f, -1.f, 0.f} + }; } const float * Matrix::Raw() const diff --git a/source/projection/Camera.cpp b/source/projection/Camera.cpp index e7ae0f9..63e5578 100644 --- a/source/projection/Camera.cpp +++ b/source/projection/Camera.cpp @@ -10,8 +10,8 @@ namespace omath::projection { - Camera::Camera(const Vector3 &position, const Vector3 &viewAngles, const Vector3 &viewPort, const float fov, const float near, - const float far) + Camera::Camera(const Vector3 &position, const Vector3 &viewAngles, const ViewPort &viewPort, + const float fov, const float near, const float far) { m_origin = position; m_viewAngles = viewAngles; @@ -21,79 +21,41 @@ namespace omath::projection m_farPlaneDistance = far; } - float & Camera::GetFloat1() { - static float m_float1 = 1.52550f; + float& Camera::GetFloat1() { + static float m_float1 = 0.36689f; return m_float1; } - float & Camera::GetFloat2() { + float& Camera::GetFloat2() { static float m_float2 = 1.14500f; return m_float2; } Matrix Camera::GetViewMatrix() const - { - return GetTranslationMatrix() * GetOrientationMatrix(); - } - - Matrix Camera::GetProjectionMatrix() const - { - const float fovHalfTan = std::tan(angles::DegreesToRadians(m_fieldOfView) / 2.f); - const auto aspectRatio = m_viewPort.x / m_viewPort.y; - - const auto far = m_farPlaneDistance; - const auto near = m_nearPlaneDistance; - - return Matrix( - { - {1.f / (aspectRatio*fovHalfTan), 0.f, 0.f, 0.f}, - {0.f, 1.f / fovHalfTan, 0.f, 0.f}, - {0.f, 0.f, (far + near) / (far - near), 2.f * near * far / (far - near)}, - {0.f, 0.f, -1.f, 0.f}, - }); - } - - Matrix Camera::GetTranslationMatrix() const - { - return Matrix( - { - {1.f, 0.f, 0.f, 0.f}, - {0.f, 1.f, 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {-m_origin.x, -m_origin.y, -m_origin.z, 1.f}, - }); - } - - Matrix Camera::GetOrientationMatrix() const { const auto forward = Vector3::ForwardVector(m_viewAngles.x, m_viewAngles.y); const auto right = Vector3::RightVector(m_viewAngles.x, m_viewAngles.y, m_viewAngles.z); const auto up = Vector3::UpVector(m_viewAngles.x, m_viewAngles.y, m_viewAngles.z); - - return Matrix( - { - {right.x, up.x, forward.x, 0.f}, - {right.y, up.y, forward.y, 0.f}, - {right.z, up.z, forward.z, 0.f}, - {0.f, 0.f, 0.f, 1.f}, - }); + return Matrix::TranslationMatrix(-m_origin) * Matrix::OrientationMatrix(forward, right, up); } std::expected Camera::WorldToScreen(const Vector3 &worldPosition) const { const auto posVecAsMatrix = Matrix({{worldPosition.x, worldPosition.y, worldPosition.z, 1.f}}); - const auto viewProjectionMatrix = GetViewMatrix() * GetProjectionMatrix(); - auto projected = posVecAsMatrix * viewProjectionMatrix; + const auto projectionMatrix = Matrix::ProjectionMatrix(m_fieldOfView, m_viewPort.AspectRatio(), + m_nearPlaneDistance, m_farPlaneDistance); + + auto projected = posVecAsMatrix * (GetViewMatrix() * projectionMatrix); if (projected.At(0, 3) <= 0.f) return std::unexpected("Projection point is out of camera field of view"); projected /= projected.At(0, 3); - projected *= Matrix::ToScreenMatrix(m_viewPort.x, m_viewPort.y); + projected *= Matrix::ToScreenMatrix(m_viewPort.m_width, m_viewPort.m_height); return Vector3{projected.At(0, 0), projected.At(0, 1), projected.At(0, 2)}; } diff --git a/tests/UnitTestProjection.cpp b/tests/UnitTestProjection.cpp index dcd5d4c..2d92930 100644 --- a/tests/UnitTestProjection.cpp +++ b/tests/UnitTestProjection.cpp @@ -9,7 +9,7 @@ TEST(UnitTestProjection, IsPointOnScreen) { - const omath::projection::Camera camera({0, 0, 0}, {0, 0.f, 0.f} , {1920.f, 1080.f, 0.f}, 110, 0.1, 500); + const omath::projection::Camera camera({0.f, 0.f, 0.f}, {0, 0.f, 0.f} , {1920.f, 1080.f}, 110.f, 0.1f, 500.f); const auto proj = camera.WorldToScreen({100, 0, 15}); if (proj)