diff --git a/include/omath/matrix.h b/include/omath/Matrix.h similarity index 78% rename from include/omath/matrix.h rename to include/omath/Matrix.h index 79655cc..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); @@ -51,6 +60,8 @@ namespace omath Matrix operator*(const Matrix &other) const; + Matrix& operator*=(const Matrix &other); + Matrix operator*(float f) const; Matrix operator*(const Vector3 &vec3) const; diff --git a/include/omath/projection/Camera.h b/include/omath/projection/Camera.h new file mode 100644 index 0000000..4bbd069 --- /dev/null +++ b/include/omath/projection/Camera.h @@ -0,0 +1,48 @@ +// +// Created by Vlad on 27.08.2024. +// + +#pragma once + +#include +#include +#include +#include + + +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 ViewPort& viewPort, float fov, float near, float far); + void SetViewAngles(const Vector3& viewAngles); + + [[nodiscard]] const Vector3& GetViewAngles() const; + [[nodiscard]] Matrix GetViewMatrix() const; + [[nodiscard]] Matrix GetProjectionMatrix() const; + [[nodiscard]] Matrix GetTranslationMatrix() const; + [[nodiscard]] Matrix GetOrientationMatrix() const; + + [[nodiscard]] std::expected WorldToScreen(const Vector3& worldPosition) const; + + ViewPort m_viewPort{}; + float m_fieldOfView; + + float m_farPlaneDistance; + float m_nearPlaneDistance; + + private: + Vector3 m_viewAngles; + Vector3 m_origin; + }; +} diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 499567c..c35e3a5 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -5,4 +5,5 @@ target_sources(omath PRIVATE Vector4.cpp) add_subdirectory(prediction) -add_subdirectory(pathfinding) \ No newline at end of file +add_subdirectory(pathfinding) +add_subdirectory(projection) \ No newline at end of file diff --git a/source/Vector3.cpp b/source/Vector3.cpp index e0a68f1..40664cd 100644 --- a/source/Vector3.cpp +++ b/source/Vector3.cpp @@ -219,7 +219,12 @@ namespace omath const auto sinYaw = std::sin(angles::DegreesToRadians(yaw)); - return {cosPitch*cosYaw, cosPitch*sinYaw, sinPitch}; + return + { + cosPitch*cosYaw, + cosPitch*sinYaw, + sinPitch + }; } Vector3 Vector3::RightVector(const float pitch, const float yaw, const float roll) @@ -230,8 +235,8 @@ namespace omath const auto cosYaw = std::cos(angles::DegreesToRadians(yaw)); const auto sinYaw = std::sin(angles::DegreesToRadians(yaw)); - const auto cosRoll = std::cos(angles::DegreesToRadians(yaw)); - const auto sinRoll = std::sin(angles::DegreesToRadians(yaw)); + const auto cosRoll = std::cos(angles::DegreesToRadians(roll)); + const auto sinRoll = std::sin(angles::DegreesToRadians(roll)); return {-sinRoll*sinPitch*cosYaw + -cosRoll*-sinYaw, @@ -239,6 +244,11 @@ namespace omath sinRoll*cosPitch}; } + Vector3 Vector3::UpVector(float pitch, float yaw, float roll) + { + return RightVector(pitch, yaw, roll).Cross(ForwardVector(pitch, yaw)); + } + Vector3 Vector3::Cross(const Vector3 &v) const { return diff --git a/source/matrix.cpp b/source/matrix.cpp index 7ca9309..cd98ee3 100644 --- a/source/matrix.cpp +++ b/source/matrix.cpp @@ -1,10 +1,13 @@ -#include "omath/matrix.h" +#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) @@ -120,6 +132,12 @@ namespace omath return outMat; } + Matrix & Matrix::operator*=(const Matrix &other) + { + *this = *this * other; + return *this; + } + Matrix Matrix::operator*(const float f) const { auto out = *this; @@ -241,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}; @@ -292,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/CMakeLists.txt b/source/projection/CMakeLists.txt new file mode 100644 index 0000000..0abf868 --- /dev/null +++ b/source/projection/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(omath PRIVATE Camera.cpp) \ No newline at end of file diff --git a/source/projection/Camera.cpp b/source/projection/Camera.cpp new file mode 100644 index 0000000..49bb12c --- /dev/null +++ b/source/projection/Camera.cpp @@ -0,0 +1,52 @@ +// +// Created by Vlad on 27.08.2024. +// +#include "omath/projection/Camera.h" + +#include + +#include "omath/angles.h" + + +namespace omath::projection +{ + 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; + m_viewPort = viewPort; + m_fieldOfView = fov; + m_nearPlaneDistance = near; + m_farPlaneDistance = far; + } + + Matrix Camera::GetViewMatrix() 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::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 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.m_width, m_viewPort.m_height); + + return Vector3{projected.At(0, 0), projected.At(0, 1), projected.At(0, 2)}; + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9781a8b..f47be28 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}" file(GLOB TEST_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) include(GoogleTest) -add_executable(unit-tests UnitTestPrediction.cpp UnitTestMatrix.cpp UnitTestAstar.cpp) +add_executable(unit-tests UnitTestPrediction.cpp UnitTestMatrix.cpp UnitTestAstar.cpp UnitTestProjection.cpp) target_link_libraries(unit-tests PRIVATE gtest gtest_main omath) diff --git a/tests/UnitTestAstar.cpp b/tests/UnitTestAstar.cpp index 91780e8..f16cd8e 100644 --- a/tests/UnitTestAstar.cpp +++ b/tests/UnitTestAstar.cpp @@ -13,5 +13,5 @@ TEST(UnitTestAstar, FindingRightPath) mesh.m_verTextMap[{0.f, 1.f, 0.f}] = {{0.f, 2.f, 0.f}}; mesh.m_verTextMap[{0.f, 2.f, 0.f}] = {{0.f, 3.f, 0.f}}; mesh.m_verTextMap[{0.f, 3.f, 0.f}] = {}; - omath::pathfinding::Astar::FindPath({}, {0.f, 3.f, 0.f}, mesh); + std::ignore = omath::pathfinding::Astar::FindPath({}, {0.f, 3.f, 0.f}, mesh); } \ No newline at end of file diff --git a/tests/UnitTestMatrix.cpp b/tests/UnitTestMatrix.cpp index dceb264..a86692f 100644 --- a/tests/UnitTestMatrix.cpp +++ b/tests/UnitTestMatrix.cpp @@ -2,7 +2,7 @@ // Created by vlad on 5/18/2024. // #include -#include +#include #include diff --git a/tests/UnitTestProjection.cpp b/tests/UnitTestProjection.cpp new file mode 100644 index 0000000..2d92930 --- /dev/null +++ b/tests/UnitTestProjection.cpp @@ -0,0 +1,18 @@ +// +// Created by Vlad on 27.08.2024. +// +#include +#include +#include +#include +#include + +TEST(UnitTestProjection, IsPointOnScreen) +{ + 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) + std::print("{} {}", proj->x, proj->y); + EXPECT_TRUE(proj.has_value()); +} \ No newline at end of file