diff --git a/CMakeLists.txt b/CMakeLists.txt index 9af2110..08132d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,10 @@ option(OMATH_BUILD_AS_SHARED_LIBRARY "Build Omath as .so or .dll" OFF) if (OMATH_BUILD_AS_SHARED_LIBRARY) add_library(omath SHARED source/Vector3.cpp) else() - add_library(omath STATIC source/Vector3.cpp) + add_library(omath STATIC source/Vector3.cpp + include/omath/engines/OpenGL/Constants.hpp + include/omath/engines/OpenGL/Formulas.hpp + include/omath/engines/OpenGL/Camera.hpp) endif() target_compile_definitions(omath PUBLIC OMATH_EXPORT) diff --git a/include/omath/engines/OpenGL/Camera.hpp b/include/omath/engines/OpenGL/Camera.hpp new file mode 100644 index 0000000..0ad9729 --- /dev/null +++ b/include/omath/engines/OpenGL/Camera.hpp @@ -0,0 +1,19 @@ +// +// Created by Orange on 12/23/2024. +// +#pragma once +#include "Constants.hpp" +#include "omath/projection/Camera.hpp" + +namespace omath::opengl +{ + class Camera final : public projection::Camera + { + public: + Camera(const Vector3& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort, + const Angle& fov, float near, float far); + void LookAt(const Vector3& target) override; + [[nodiscard]] Mat4x4 CalcViewMatrix() const override; + [[nodiscard]] Mat4x4 CalcProjectionMatrix() const override; + }; +} \ No newline at end of file diff --git a/include/omath/engines/OpenGL/Constants.hpp b/include/omath/engines/OpenGL/Constants.hpp new file mode 100644 index 0000000..a8912a2 --- /dev/null +++ b/include/omath/engines/OpenGL/Constants.hpp @@ -0,0 +1,25 @@ +// +// Created by Orange on 12/23/2024. +// +#pragma once + +#include +#include +#include +#include + +namespace omath::opengl +{ + constexpr Vector3 kAbsUp = {0, 1, 0}; + constexpr Vector3 kAbsRight = {1, 0, 0}; + constexpr Vector3 kAbsForward = {0, 0, -1}; + + using Mat4x4 = Mat<4, 4, float, MatStoreType::COLUMN_MAJOR>; + using Mat3x3 = Mat<4, 4, float, MatStoreType::COLUMN_MAJOR>; + using Mat1x3 = Mat<1, 3, float, MatStoreType::COLUMN_MAJOR>; + using PitchAngle = Angle; + using YawAngle = Angle; + using RollAngle = Angle; + + using ViewAngles = omath::ViewAngles; +} \ No newline at end of file diff --git a/include/omath/engines/OpenGL/Formulas.hpp b/include/omath/engines/OpenGL/Formulas.hpp new file mode 100644 index 0000000..8e6b2ed --- /dev/null +++ b/include/omath/engines/OpenGL/Formulas.hpp @@ -0,0 +1,54 @@ +// +// Created by Orange on 12/23/2024. +// +#pragma once +#include "Constants.hpp" + + +namespace omath::opengl +{ + [[nodiscard]] + inline Vector3 ForwardVector(const ViewAngles& angles) + { + const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsForward); + + return {vec.At(0, 0), vec.At(1, 0), vec.At(2, 0)}; + } + + [[nodiscard]] + inline Vector3 RightVector(const ViewAngles& angles) + { + const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsRight); + + return {vec.At(0, 0), vec.At(1, 0), vec.At(2, 0)}; + } + + [[nodiscard]] + inline Vector3 UpVector(const ViewAngles& angles) + { + const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsUp); + + return {vec.At(0, 0), vec.At(1, 0), vec.At(2, 0)}; + } + + [[nodiscard]] inline Mat4x4 CalcViewMatrix(const ViewAngles& angles, const Vector3& cam_origin) + { + return MatCameraView(-ForwardVector(angles), RightVector(angles), UpVector(angles), cam_origin); + } + + + [[nodiscard]] + inline Mat4x4 CalcPerspectiveProjectionMatrix(const float fieldOfView, const float aspectRatio, const float near, + const float far) + { + const float fovHalfTan = std::tan(angles::DegreesToRadians(fieldOfView) / 2.f); + + return { + {1.f / (aspectRatio * fovHalfTan), 0, 0, 0}, + {0, 1.f / (fovHalfTan), 0, 0}, + {0, 0, -(far + near) / (far - near), -(2.f * far * near) / (far - near)}, + {0, 0, -1, 0}, + + }; + } +} \ No newline at end of file diff --git a/include/omath/engines/Source/Camera.hpp b/include/omath/engines/Source/Camera.hpp index 7298cfe..739aa86 100644 --- a/include/omath/engines/Source/Camera.hpp +++ b/include/omath/engines/Source/Camera.hpp @@ -2,7 +2,7 @@ // Created by Orange on 12/4/2024. // #pragma once -#include "Constants.h" +#include "Constants.hpp" #include "omath/projection/Camera.hpp" namespace omath::source @@ -13,7 +13,7 @@ namespace omath::source Camera(const Vector3& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort, const Angle& fov, float near, float far); void LookAt(const Vector3& target) override; - [[nodiscard]] Mat4x4 GetViewMatrix() const override; - [[nodiscard]] Mat4x4 GetProjectionMatrix() const override; + [[nodiscard]] Mat4x4 CalcViewMatrix() const override; + [[nodiscard]] Mat4x4 CalcProjectionMatrix() const override; }; } \ No newline at end of file diff --git a/include/omath/engines/Source/Constants.h b/include/omath/engines/Source/Constants.hpp similarity index 100% rename from include/omath/engines/Source/Constants.h rename to include/omath/engines/Source/Constants.hpp diff --git a/include/omath/engines/Source/Formulas.hpp b/include/omath/engines/Source/Formulas.hpp index 3c8a6d7..0b8ff03 100644 --- a/include/omath/engines/Source/Formulas.hpp +++ b/include/omath/engines/Source/Formulas.hpp @@ -2,7 +2,7 @@ // Created by Orange on 12/4/2024. // #pragma once -#include "Constants.h" +#include "Constants.hpp" namespace omath::source { diff --git a/include/omath/projection/Camera.hpp b/include/omath/projection/Camera.hpp index 1b93b4a..95f4eb0 100644 --- a/include/omath/projection/Camera.hpp +++ b/include/omath/projection/Camera.hpp @@ -29,7 +29,6 @@ namespace omath::projection template class Camera { - public: virtual ~Camera() = default; Camera(const Vector3& position, const ViewAnglesType& viewAngles, const ViewPort& viewPort, @@ -42,18 +41,82 @@ namespace omath::projection virtual void LookAt(const Vector3& target) = 0; - [[nodiscard]] virtual Mat4x4Type GetViewMatrix() const = 0; + [[nodiscard]] virtual Mat4x4Type CalcViewMatrix() const = 0; - [[nodiscard]] virtual Mat4x4Type GetProjectionMatrix() const = 0; + [[nodiscard]] virtual Mat4x4Type CalcProjectionMatrix() const = 0; - [[nodiscard]] Mat4x4Type GetViewProjectionMatrix() + [[nodiscard]] Mat4x4Type CalcViewProjectionMatrix() const { - return GetProjectionMatrix() * GetViewMatrix(); + return CalcProjectionMatrix() * CalcViewMatrix(); } - [[nodiscard]] std::expected WorldToScreen(const Mat4x4Type& viewProj, const Vector3& worldPosition) const + void SetFieldOfView(const FieldOfView& fov) { - auto projected = viewProj * MatColumnFromVector(worldPosition); + m_fieldOfView = fov; + m_viewProjectionMatrix = CalcViewProjectionMatrix(); + } + + void SetNearPlane(const float near) + { + m_nearPlaneDistance = near; + m_viewProjectionMatrix = CalcViewProjectionMatrix(); + } + + void SetFarPlane(const float far) + { + m_farPlaneDistance = far; + m_viewProjectionMatrix = CalcViewProjectionMatrix(); + } + + void SetViewAngles(const ViewAnglesType& viewAngles) + { + m_viewAngles = viewAngles; + m_viewProjectionMatrix = CalcViewProjectionMatrix(); + } + + void SetOrigin(const Vector3& origin) + { + m_origin = origin; + m_viewProjectionMatrix = CalcViewProjectionMatrix(); + } + + void SetViewPort(const ViewPort& viewPort) + { + m_viewPort = viewPort; + m_viewProjectionMatrix = CalcViewProjectionMatrix(); + } + + [[nodiscard]] const FieldOfView& GetFieldOfView() const + { + return m_fieldOfView; + } + + [[nodiscard]] const float& GetNearPlane() const + { + return m_nearPlaneDistance; + } + + [[nodiscard]] const float& GetFarPlane() const + { + return m_farPlaneDistance; + } + + [[nodiscard]] const ViewAnglesType& GetViewAngles() const + { + return m_viewAngles; + } + + [[nodiscard]] const Vector3& GetOrigin() const + { + return m_origin; + } + + [[nodiscard]] std::expected WorldToScreen(const Vector3& worldPosition) const + { + if (!m_viewProjectionMatrix.has_value()) + m_viewProjectionMatrix = CalcViewProjectionMatrix(); + + auto projected = m_viewProjectionMatrix.value() * MatColumnFromVector(worldPosition); if (projected.At(3, 0) == 0.0f) return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); @@ -63,13 +126,18 @@ namespace omath::projection if (IsNdcOutOfBounds(projected)) return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); - return Vector3{(projected.At(0,0)+1) / 2 * m_viewPort.m_width , (-projected.At(1,0)+1) / 2 * m_viewPort.m_height, projected.At(2,0)}; + const auto screenPositionX = (projected.At(0,0)+1.f) / 2.f * m_viewPort.m_width; + const auto screenPositionY = (-projected.At(1,0)+1) / 2.f * m_viewPort.m_height; + + return Vector3{screenPositionX, screenPositionY, projected.At(2,0)}; } protected: ViewPort m_viewPort{}; Angle m_fieldOfView; + mutable std::optional m_viewProjectionMatrix; + float m_farPlaneDistance; float m_nearPlaneDistance; diff --git a/source/engines/CMakeLists.txt b/source/engines/CMakeLists.txt index f01db76..2d2a3c9 100644 --- a/source/engines/CMakeLists.txt +++ b/source/engines/CMakeLists.txt @@ -1 +1,2 @@ -add_subdirectory(Source) \ No newline at end of file +add_subdirectory(Source) +add_subdirectory(OpenGL) \ No newline at end of file diff --git a/source/engines/OpenGL/CMakeLists.txt b/source/engines/OpenGL/CMakeLists.txt new file mode 100644 index 0000000..0abf868 --- /dev/null +++ b/source/engines/OpenGL/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(omath PRIVATE Camera.cpp) \ No newline at end of file diff --git a/source/engines/OpenGL/Camera.cpp b/source/engines/OpenGL/Camera.cpp new file mode 100644 index 0000000..9ad5a47 --- /dev/null +++ b/source/engines/OpenGL/Camera.cpp @@ -0,0 +1,35 @@ +// +// Created by Orange on 12/23/2024. +// +#include "omath/engines/OpenGL/Camera.hpp" +#include "omath/engines/OpenGL/Formulas.hpp" + + +namespace omath::opengl +{ + + Camera::Camera(const Vector3& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort, + const Angle& fov, const float near, const float far) : + projection::Camera(position, viewAngles, viewPort, fov, near, far) + { + } + void Camera::LookAt([[maybe_unused]] const Vector3& target) + { + const float distance = m_origin.DistTo(target); + const auto delta = target - m_origin; + + + m_viewAngles.pitch = PitchAngle::FromRadians(std::asin(delta.z / distance)); + m_viewAngles.yaw = -YawAngle::FromRadians(std::atan2(delta.y, delta.x)); + m_viewAngles.roll = RollAngle::FromRadians(0.f); + } + Mat4x4 Camera::CalcViewMatrix() const + { + return opengl::CalcViewMatrix(m_viewAngles, m_origin); + } + Mat4x4 Camera::CalcProjectionMatrix() const + { + return CalcPerspectiveProjectionMatrix(m_fieldOfView.AsDegrees(), m_viewPort.AspectRatio(), m_nearPlaneDistance, + m_farPlaneDistance); + } +} // namespace omath::opengl diff --git a/source/engines/Source/Camera.cpp b/source/engines/Source/Camera.cpp index 51eb507..444e208 100644 --- a/source/engines/Source/Camera.cpp +++ b/source/engines/Source/Camera.cpp @@ -24,13 +24,14 @@ namespace omath::source m_viewAngles.roll = RollAngle::FromRadians(0.f); } - Mat4x4 Camera::GetViewMatrix() const + Mat4x4 Camera::CalcViewMatrix() const { - return CalcViewMatrix(m_viewAngles, m_origin); + return source::CalcViewMatrix(m_viewAngles, m_origin); } - Mat4x4 Camera::GetProjectionMatrix() const + Mat4x4 Camera::CalcProjectionMatrix() const { - return CalcPerspectiveProjectionMatrix(m_fieldOfView.AsDegrees(), m_viewPort.AspectRatio(), m_nearPlaneDistance, m_farPlaneDistance); + return CalcPerspectiveProjectionMatrix(m_fieldOfView.AsDegrees(), m_viewPort.AspectRatio(), m_nearPlaneDistance, + m_farPlaneDistance); } } // namespace omath::source diff --git a/tests/engines/UnitTestOpenGL.cpp b/tests/engines/UnitTestOpenGL.cpp index 46f5afc..461d9d2 100644 --- a/tests/engines/UnitTestOpenGL.cpp +++ b/tests/engines/UnitTestOpenGL.cpp @@ -1,37 +1,69 @@ // // Created by Orange on 11/23/2024. // -#include #include -#include -#include - -// #include - -// #include "glm/ext/matrix_clip_space.hpp" -// #include "glm/ext/matrix_transform.hpp" +#include +#include +#include -TEST(UnitTestOpenGL, Projection) +TEST(UnitTestOpenGL, ForwardVector) { + const auto forward = omath::opengl::ForwardVector({}); - /*const auto proj_glm = glm::perspective(glm::radians(90.f), 16.f / 9.f, 0.1f, 1000.f); - // const auto proj_glm2 = glm::perspectiveLH_NO(glm::radians(90.f), 16.f / 9.f, 0.1f, 1000.f); - // const auto proj_omath = omath::Mat<4, 4, float, omath::MatStoreType::COLUMN_MAJOR>((const float*)&proj_glm); - // EXPECT_EQ(omath::opengl::PerspectiveProjectionMatrix(90, 16.f / 9.f, 0.1f, 1000.f), proj_omath); + EXPECT_EQ(forward, omath::opengl::kAbsForward); +} + +TEST(UnitTestOpenGL, RightVector) +{ + const auto right = omath::opengl::RightVector({}); + + EXPECT_EQ(right, omath::opengl::kAbsRight); +} + +TEST(UnitTestOpenGL, UpVector) +{ + const auto up = omath::opengl::UpVector({}); + EXPECT_EQ(up, omath::opengl::kAbsUp); +} + +TEST(UnitTestOpenGL, ProjectTargetMovedFromCamera) +{ + constexpr auto fov = omath::projection::FieldOfView::FromDegrees(90.f); + auto cam = omath::opengl::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); - glm::vec4 ndc_glm2 = proj_glm * glm::vec4(300.f, 0.f, -1000.f, 1.f); - ndc_glm2 /= ndc_glm2.w; - const omath::Mat<4, 1, float, omath::MatStoreType::COLUMN_MAJOR> cords_omath = + for (float distance = -10.f; distance > -1000.f; distance -= 0.01f) { - {0}, - {0}, - {-0.2f}, - {1} - }; + const auto projected = cam.WorldToScreen({0, 0, distance}); - //auto ndc_omath = proj_omath * cords_omath; - // ndc_omath /= ndc_omath.At(3, 0); - */ + EXPECT_TRUE(projected.has_value()); + + if (!projected.has_value()) + continue; + + EXPECT_NEAR(projected->x, 960, 0.00001f); + EXPECT_NEAR(projected->y, 540, 0.00001f); + } +} + +TEST(UnitTestOpenGL, CameraSetAndGetFov) +{ + constexpr auto fov = omath::projection::FieldOfView::FromDegrees(90.f); + auto cam = omath::opengl::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); + + EXPECT_EQ(cam.GetFieldOfView().AsDegrees(), 90.f); + cam.SetFieldOfView(omath::projection::FieldOfView::FromDegrees(50.f)); + + EXPECT_EQ(cam.GetFieldOfView().AsDegrees(), 50.f); +} + +TEST(UnitTestOpenGL, CameraSetAndGetOrigin) +{ + auto cam = omath::opengl::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, {}, 0.01f, 1000.f); + + EXPECT_EQ(cam.GetOrigin(), omath::Vector3{}); + cam.SetFieldOfView(omath::projection::FieldOfView::FromDegrees(50.f)); + + EXPECT_EQ(cam.GetFieldOfView().AsDegrees(), 50.f); } \ No newline at end of file diff --git a/tests/engines/UnitTestSourceEngine.cpp b/tests/engines/UnitTestSourceEngine.cpp index 4d641a1..cb73a44 100644 --- a/tests/engines/UnitTestSourceEngine.cpp +++ b/tests/engines/UnitTestSourceEngine.cpp @@ -3,7 +3,7 @@ // #include #include -#include +#include #include @@ -27,17 +27,15 @@ TEST(UnitTestSourceEngine, UpVector) EXPECT_EQ(up, omath::source::kAbsUp); } -TEST(UnitTestSourceEngine, PerpectiveProjectionAtCenter) +TEST(UnitTestSourceEngine, ProjectTargetMovedFromCamera) { constexpr auto fov = omath::projection::FieldOfView::FromDegrees(90.f); auto cam = omath::source::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); - const auto viewProjMatrix = cam.GetViewProjectionMatrix(); - for (float distance = 0.02f; distance < 1000.f; distance += 0.01f) { - const auto projected = cam.WorldToScreen(viewProjMatrix, {distance, 0, 0}); + const auto projected = cam.WorldToScreen({distance, 0, 0}); EXPECT_TRUE(projected.has_value()); @@ -47,4 +45,25 @@ TEST(UnitTestSourceEngine, PerpectiveProjectionAtCenter) EXPECT_NEAR(projected->x, 960, 0.00001f); EXPECT_NEAR(projected->y, 540, 0.00001f); } +} + +TEST(UnitTestSourceEngine, CameraSetAndGetFov) +{ + constexpr auto fov = omath::projection::FieldOfView::FromDegrees(90.f); + auto cam = omath::source::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); + + EXPECT_EQ(cam.GetFieldOfView().AsDegrees(), 90.f); + cam.SetFieldOfView(omath::projection::FieldOfView::FromDegrees(50.f)); + + EXPECT_EQ(cam.GetFieldOfView().AsDegrees(), 50.f); +} + +TEST(UnitTestSourceEngine, CameraSetAndGetOrigin) +{ + auto cam = omath::source::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, {}, 0.01f, 1000.f); + + EXPECT_EQ(cam.GetOrigin(), omath::Vector3{}); + cam.SetFieldOfView(omath::projection::FieldOfView::FromDegrees(50.f)); + + EXPECT_EQ(cam.GetFieldOfView().AsDegrees(), 50.f); } \ No newline at end of file diff --git a/tests/general/UnitTestProjection.cpp b/tests/general/UnitTestProjection.cpp index ab8b71c..3d9b507 100644 --- a/tests/general/UnitTestProjection.cpp +++ b/tests/general/UnitTestProjection.cpp @@ -10,9 +10,9 @@ TEST(UnitTestProjection, Projection) { - auto x = omath::Angle::FromDegrees(90.f); + const auto x = omath::Angle::FromDegrees(90.f); auto cam = omath::source::Camera({0, 0, 0}, omath::source::ViewAngles{}, {1920.f, 1080.f}, x, 0.01f, 1000.f); - const auto projected = cam.WorldToScreen(cam.GetViewProjectionMatrix(), {1000, 0, 50}); + const auto projected = cam.WorldToScreen({1000, 0, 50}); std::print("{} {} {}", projected->x, projected->y, projected->z); } \ No newline at end of file