Compare commits

...

48 Commits

Author SHA1 Message Date
Vladislav Alpatov
c3202a3bc3 patch 2025-03-01 21:32:02 +03:00
Vladislav Alpatov
39ab9d065d added option to disable avx2 2025-03-01 21:30:53 +03:00
Vladislav Alpatov
ed372a1c78 fixed for clang support 2025-03-01 21:22:44 +03:00
70313f5ae0 Merge pull request #29 from orange-cpp/u/orange-cpp/template-vectors
Added template Vectors 2d 3d 4d
2025-03-01 21:17:42 +03:00
Vladislav Alpatov
978656b573 refactored 2025-03-01 21:17:03 +03:00
Vladislav Alpatov
a0f746b84c fixed style 2025-03-01 21:15:26 +03:00
Vladislav Alpatov
6d0d267743 now template 2025-03-01 21:11:46 +03:00
9ba3bc754a removed define 2025-02-23 21:56:28 +03:00
3d1844fa0e Merge pull request #28 from orange-cpp/u/orange-cpp/avx2-prediction
U/orange cpp/avx2 prediction
2025-02-23 16:32:27 +03:00
f21d29c6c2 added legacy impl 2025-02-23 10:10:35 +03:00
28a35d5bc9 added more classes 2025-02-23 09:57:29 +03:00
d9684ff73f read me update 2025-02-22 23:34:45 +03:00
900501f37e added language mention 2025-02-22 23:34:45 +03:00
5639cd0eb5 added AVX2 2025-02-22 23:34:45 +03:00
244d01c313 Merge pull request #27 from orange-cpp/u/orange-cpp/unity-build-support
added unity build support
2025-02-22 23:33:03 +03:00
e31ffac103 added unity build support 2025-02-22 23:32:29 +03:00
ae87257adf Merge pull request #26 from orange-cpp/u/orange-cpp/improved-astar
Improved A*
2025-02-22 23:06:33 +03:00
906f5099d1 improvement 2025-02-16 10:57:03 +03:00
96e4e1c9d6 added new method added concept for mat type 2025-02-16 10:06:04 +03:00
872dbe146f Merge pull request #25 from orange-cpp/u/orange-cpp/hotfix
improved cmake+hotfix of clang support
2025-01-17 16:59:30 +03:00
e0dcb65e3f improved cmake+hotfix of clang support 2025-01-17 16:56:47 +03:00
d0c532df39 hot fix 2025-01-06 05:36:27 +03:00
31907ceca3 Merge pull request #24 from orange-cpp/u/orange-cpp/improved-triangle-class
U/orange cpp/improved triangle class
2025-01-06 05:19:27 +03:00
afcfed4834 path 2025-01-06 05:17:42 +03:00
835fd110ba fix 2025-01-06 05:15:12 +03:00
29629a737d added some methods 2025-01-06 05:08:32 +03:00
6a324e8c0e improvement 2025-01-06 04:42:43 +03:00
42c84f2523 added midpoint for triangle class 2025-01-06 04:30:03 +03:00
9a4fb67289 hotfix 2025-01-02 20:32:25 +03:00
824b301d40 small code improvement 2025-01-02 18:40:19 +03:00
dcf466316c Merge pull request #23 from orange-cpp/u/orange-cpp/vec3-angle-calc
added AngleBetween method for Vector3 + tests
2025-01-02 12:54:27 +03:00
dc0bca14c5 added method + tests 2025-01-02 12:52:34 +03:00
b2db06a739 updated read me 2024-12-26 18:52:24 +03:00
cdce07f2a5 Merge pull request #22 from orange-cpp/u/orange-cpp/improved-camera
U/orange cpp/improved camera
2024-12-26 18:42:17 +03:00
931937d010 added open gl stuff 2024-12-23 17:53:47 +03:00
af880be056 added methods 2024-12-23 15:34:38 +03:00
c3bfd12971 Merge pull request #21 from orange-cpp/u/orange-cpp/dropped-shared-export
U/orange cpp/dropped shared export
2024-12-23 13:52:55 +03:00
9ad8ec755b removed header 2024-12-23 13:52:12 +03:00
409cd18de6 dropped .dll/.so support 2024-12-23 13:47:28 +03:00
001c6a8a47 fixed vcs.mxl 2024-12-23 13:43:14 +03:00
30bc3f2c6d changed readme 2024-12-21 19:41:31 +03:00
232f456a4d Merge pull request #20 from orange-cpp/u/orange-cpp/mat_sqr_brackets_op
Added brand new operator[] support for Mat/Matrix class
2024-12-21 19:34:51 +03:00
2b9575311e added operator for matrix 2024-12-21 19:32:09 +03:00
e497308349 added operator square brackets 2024-12-21 19:29:33 +03:00
390e584aee updated version 2024-12-21 16:03:54 +03:00
93082d6a8d added components check 2024-12-21 15:53:47 +03:00
c8c3c4e5fc Merge pull request #19 from luadebug/main
Update README.md Vcpkg integration
2024-12-21 14:31:30 +03:00
Saikari
24ad4ac698 Update README.md 2024-12-21 01:18:25 +03:00
64 changed files with 1276 additions and 599 deletions

1
.idea/vcs.xml generated
View File

@@ -2,7 +2,6 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/extlibs/glm" vcs="Git" />
<mapping directory="$PROJECT_DIR$/extlibs/googletest" vcs="Git" /> <mapping directory="$PROJECT_DIR$/extlibs/googletest" vcs="Git" />
</component> </component>
</project> </project>

View File

@@ -1,28 +1,38 @@
cmake_minimum_required(VERSION 3.26) cmake_minimum_required(VERSION 3.26)
project(omath VERSION 1.0.0) project(omath VERSION 1.0.1 LANGUAGES CXX)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
set(CMAKE_CXX_STANDARD 26)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
option(OMATH_BUILD_TESTS "Build unit tests" ON) option(OMATH_BUILD_TESTS "Build unit tests" ON)
option(OMATH_THREAT_WARNING_AS_ERROR "Set highest level of warnings and force compiler to treat them as errors" ON) option(OMATH_THREAT_WARNING_AS_ERROR "Set highest level of warnings and force compiler to treat them as errors" ON)
option(OMATH_BUILD_AS_SHARED_LIBRARY "Build Omath as .so or .dll" OFF) option(OMATH_BUILD_AS_SHARED_LIBRARY "Build Omath as .so or .dll" OFF)
option(OMATH_USE_AVX2 "Omath will use AVX2 to boost performance" ON)
if (OMATH_BUILD_AS_SHARED_LIBRARY) if (OMATH_BUILD_AS_SHARED_LIBRARY)
add_library(omath SHARED source/Vector3.cpp) add_library(omath SHARED source/Vector3.cpp)
else() else()
add_library(omath STATIC source/Vector3.cpp) add_library(omath STATIC source/Matrix.cpp)
endif() endif()
target_compile_definitions(omath PUBLIC OMATH_EXPORT) set_target_properties(omath PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
UNITY_BUILD ON
UNITY_BUILD_BATCH_SIZE 20
CXX_STANDARD 23
CXX_STANDARD_REQUIRED ON)
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(omath PRIVATE /Zc:static_assert-) target_compile_options(omath PRIVATE -mavx2 -mfma)
endif()
target_compile_features(omath PUBLIC cxx_std_23)
if (OMATH_USE_AVX2)
target_compile_definitions(omath PUBLIC OMATH_USE_AVX2)
endif() endif()
add_subdirectory(source) add_subdirectory(source)

View File

@@ -11,7 +11,7 @@
Oranges's Math Library (omath) is a comprehensive, open-source library aimed at providing efficient, reliable, and versatile mathematical functions and algorithms. Developed primarily in C++, this library is designed to cater to a wide range of mathematical operations essential in scientific computing, engineering, and academic research. Oranges's Math Library (omath) is a comprehensive, open-source library aimed at providing efficient, reliable, and versatile mathematical functions and algorithms. Developed primarily in C++, this library is designed to cater to a wide range of mathematical operations essential in scientific computing, engineering, and academic research.
## Features ## 👁‍🗨 Features
- **Efficiency**: Optimized for performance, ensuring quick computations. - **Efficiency**: Optimized for performance, ensuring quick computations.
- **Versatility**: Includes a wide array of mathematical functions and algorithms. - **Versatility**: Includes a wide array of mathematical functions and algorithms.
- **Ease of Use**: Simplified interface for convenient integration into various projects. - **Ease of Use**: Simplified interface for convenient integration into various projects.
@@ -20,12 +20,26 @@ Oranges's Math Library (omath) is a comprehensive, open-source library aimed at
- **Collision Detection**: Production ready code to handle collision detection by using simple interfaces. - **Collision Detection**: Production ready code to handle collision detection by using simple interfaces.
- **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution - **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution
## Getting Started ## Getting Started
### Prerequisites ### Prerequisites
- C++ Compiler - C++ Compiler
- CMake (for building the project) - CMake (for building the project)
### Installation ### Installation
### vcpkg
**Note**: Support vcpkg for package management
1. Install vcpkg (https://github.com/microsoft/vcpkg)
2. Run the following command to install the orange-math package:
```
vcpkg install orange-math
```
CMakeLists.txt
```cmake
find_package(omath CONFIG REQUIRED)
target_link_libraries(main PRIVATE omath::omath)
```
For detailed commands on installing different versions and more information, please refer to Microsoft's official instructions (https://learn.microsoft.com/en-us/vcpkg/get_started/overview)
### Build from source
1. Clone the repository: 1. Clone the repository:
``` ```
git clone https://github.com/orange-cpp/omath.git git clone https://github.com/orange-cpp/omath.git
@@ -37,10 +51,10 @@ Oranges's Math Library (omath) is a comprehensive, open-source library aimed at
3. Build the project using CMake: 3. Build the project using CMake:
``` ```
cmake --preset windows-release -S . cmake --preset windows-release -S .
cmake --build cmake-build/build/windows-release --target server -j 6 cmake --build cmake-build/build/windows-release --target omath -j 6
``` ```
Use **\<platform\>-\<build configuration\>** preset to build siutable version for yourself. Like **windows-release** or **linux-release**. Use **\<platform\>-\<build configuration\>** preset to build siutable version for yourself. Like **windows-release** or **linux-release**.
## Usage ## Usage
Simple world to screen function Simple world to screen function
```c++ ```c++
TEST(UnitTestProjection, IsPointOnScreen) TEST(UnitTestProjection, IsPointOnScreen)
@@ -62,11 +76,11 @@ With `omath/projection` module you can achieve simple ESP hack for powered by So
</details> </details>
## Contributing ## 🫵🏻 Contributing
Contributions to `omath` are welcome! Please read `CONTRIBUTING.md` for details on our code of conduct and the process for submitting pull requests. Contributions to `omath` are welcome! Please read `CONTRIBUTING.md` for details on our code of conduct and the process for submitting pull requests.
## License ## 📜 License
This project is licensed under the MIT - see the `LICENSE` file for details. This project is licensed under the MIT - see the `LICENSE` file for details.
## Acknowledgments ## 💘 Acknowledgments
- Orange | [Telegram](https://t.me/orange_cpp) - [All contributors](https://github.com/orange-cpp/omath/graphs/contributors)

View File

@@ -4,3 +4,4 @@ include(CMakeFindDependencyMacro)
# Load the targets for the omath library # Load the targets for the omath library
include("${CMAKE_CURRENT_LIST_DIR}/omathTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/omathTargets.cmake")
check_required_components(omath)

View File

@@ -4,9 +4,7 @@
#pragma once #pragma once
#include "omath/Angles.hpp" #include "omath/Angles.hpp"
#include <algorithm> #include <algorithm>
#include "omath/omath_export.hpp"
namespace omath namespace omath
{ {
@@ -18,7 +16,7 @@ namespace omath
template<class Type = float, Type min = Type(0), Type max = Type(360), AngleFlags flags = AngleFlags::Normalized> template<class Type = float, Type min = Type(0), Type max = Type(360), AngleFlags flags = AngleFlags::Normalized>
requires std::is_arithmetic_v<Type> requires std::is_arithmetic_v<Type>
class OMATH_API Angle class Angle
{ {
Type m_angle; Type m_angle;
constexpr Angle(const Type& degrees) constexpr Angle(const Type& degrees)

View File

@@ -7,11 +7,10 @@
#include "omath/Vector3.hpp" #include "omath/Vector3.hpp"
#include <cstdint> #include <cstdint>
#include "omath/Vector4.hpp" #include "omath/Vector4.hpp"
#include "omath/omath_export.hpp"
namespace omath namespace omath
{ {
struct OMATH_API HSV struct HSV
{ {
float m_hue{}; float m_hue{};
float m_saturation{}; float m_saturation{};
@@ -19,10 +18,10 @@ namespace omath
}; };
class OMATH_API Color final : public Vector4 class Color final : public Vector4<float>
{ {
public: public:
constexpr Color(float r, float g, float b, float a) : Vector4(r,g,b,a) constexpr Color(const float r, const float g, const float b, const float a) : Vector4(r,g,b,a)
{ {
Clamp(0.f, 1.f); Clamp(0.f, 1.f);
} }

View File

@@ -8,11 +8,10 @@
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
#include "Vector3.hpp" #include "Vector3.hpp"
#include "omath/omath_export.hpp"
namespace omath namespace omath
{ {
struct OMATH_API MatSize struct MatSize
{ {
size_t rows, columns; size_t rows, columns;
}; };
@@ -23,20 +22,27 @@ namespace omath
COLUMN_MAJOR COLUMN_MAJOR
}; };
template<typename M1, typename M2>
concept MatTemplateEqual =
(M1::rows == M2::rows) && (M1::columns == M2::columns) &&
std::is_same_v<typename M1::value_type, typename M2::value_type> &&
(M1::store_type == M2::store_type);
template<size_t Rows = 0, size_t Columns = 0, class Type = float, MatStoreType StoreType = MatStoreType::ROW_MAJOR> template<size_t Rows = 0, size_t Columns = 0, class Type = float, MatStoreType StoreType = MatStoreType::ROW_MAJOR>
requires std::is_arithmetic_v<Type> requires std::is_arithmetic_v<Type>
class Mat final class Mat final
{ {
public: public:
OMATH_API constexpr Mat() constexpr Mat()
{ {
Clear(); Clear();
} }
OMATH_API constexpr static MatStoreType GetStoreOrdering() constexpr static MatStoreType GetStoreOrdering()
{ {
return StoreType; return StoreType;
} }
OMATH_API constexpr Mat(const std::initializer_list<std::initializer_list<Type>>& rows) constexpr Mat(const std::initializer_list<std::initializer_list<Type>>& rows)
{ {
if (rows.size() != Rows) if (rows.size() != Rows)
throw std::invalid_argument("Initializer list rows size does not match template parameter Rows"); throw std::invalid_argument("Initializer list rows size does not match template parameter Rows");
@@ -56,40 +62,45 @@ namespace omath
} }
} }
OMATH_API constexpr explicit Mat(const Type* rawData) constexpr explicit Mat(const Type* rawData)
{ {
std::copy_n(rawData, Rows * Columns, m_data.begin()); std::copy_n(rawData, Rows * Columns, m_data.begin());
} }
OMATH_API constexpr Mat(const Mat& other) noexcept constexpr Mat(const Mat& other) noexcept
{ {
m_data = other.m_data; m_data = other.m_data;
} }
OMATH_API constexpr Mat(Mat&& other) noexcept constexpr Type& operator[](const size_t row, const size_t col)
{
return At(row, col);
}
constexpr Mat(Mat&& other) noexcept
{ {
m_data = std::move(other.m_data); m_data = std::move(other.m_data);
} }
[[nodiscard]] [[nodiscard]]
OMATH_API static constexpr size_t RowCount() noexcept static constexpr size_t RowCount() noexcept
{ {
return Rows; return Rows;
} }
[[nodiscard]] [[nodiscard]]
OMATH_API static constexpr size_t ColumnsCount() noexcept static constexpr size_t ColumnsCount() noexcept
{ {
return Columns; return Columns;
} }
[[nodiscard]] [[nodiscard]]
OMATH_API static consteval MatSize Size() noexcept static consteval MatSize Size() noexcept
{ {
return {Rows, Columns}; return {Rows, Columns};
} }
[[nodiscard]] OMATH_API constexpr const Type& At(const size_t rowIndex, const size_t columnIndex) const [[nodiscard]] constexpr const Type& At(const size_t rowIndex, const size_t columnIndex) const
{ {
if (rowIndex >= Rows || columnIndex >= Columns) if (rowIndex >= Rows || columnIndex >= Columns)
throw std::out_of_range("Index out of range"); throw std::out_of_range("Index out of range");
@@ -107,13 +118,13 @@ namespace omath
} }
} }
[[nodiscard]] OMATH_API constexpr Type& At(const size_t rowIndex, const size_t columnIndex) [[nodiscard]] constexpr Type& At(const size_t rowIndex, const size_t columnIndex)
{ {
return const_cast<Type&>(std::as_const(*this).At(rowIndex, columnIndex)); return const_cast<Type&>(std::as_const(*this).At(rowIndex, columnIndex));
} }
[[nodiscard]] [[nodiscard]]
OMATH_API constexpr Type Sum() const noexcept constexpr Type Sum() const noexcept
{ {
Type sum = 0; Type sum = 0;
for (size_t i = 0; i < Rows; ++i) for (size_t i = 0; i < Rows; ++i)
@@ -123,12 +134,12 @@ namespace omath
return sum; return sum;
} }
OMATH_API constexpr void Clear() noexcept constexpr void Clear() noexcept
{ {
Set(0); Set(0);
} }
OMATH_API constexpr void Set(const Type& value) noexcept constexpr void Set(const Type& value) noexcept
{ {
std::ranges::fill(m_data, value); std::ranges::fill(m_data, value);
} }
@@ -136,7 +147,7 @@ namespace omath
// Operator overloading for multiplication with another Mat // Operator overloading for multiplication with another Mat
template<size_t OtherColumns> template<size_t OtherColumns>
constexpr Mat<Rows, OtherColumns, Type, StoreType> constexpr Mat<Rows, OtherColumns, Type, StoreType>
OMATH_API operator*(const Mat<Columns, OtherColumns, Type, StoreType>& other) const operator*(const Mat<Columns, OtherColumns, Type, StoreType>& other) const
{ {
Mat<Rows, OtherColumns, Type, StoreType> result; Mat<Rows, OtherColumns, Type, StoreType> result;
@@ -151,7 +162,7 @@ namespace omath
return result; return result;
} }
OMATH_API constexpr Mat& operator*=(const Type& f) noexcept constexpr Mat& operator*=(const Type& f) noexcept
{ {
for (size_t i = 0; i < Rows; ++i) for (size_t i = 0; i < Rows; ++i)
for (size_t j = 0; j < Columns; ++j) for (size_t j = 0; j < Columns; ++j)
@@ -161,19 +172,19 @@ namespace omath
template<size_t OtherColumns> template<size_t OtherColumns>
constexpr Mat<Rows, OtherColumns, Type, StoreType> constexpr Mat<Rows, OtherColumns, Type, StoreType>
OMATH_API operator*=(const Mat<Columns, OtherColumns, Type, StoreType>& other) operator*=(const Mat<Columns, OtherColumns, Type, StoreType>& other)
{ {
return *this = *this * other; return *this = *this * other;
} }
OMATH_API constexpr Mat operator*(const Type& f) const noexcept constexpr Mat operator*(const Type& f) const noexcept
{ {
Mat result(*this); Mat result(*this);
result *= f; result *= f;
return result; return result;
} }
OMATH_API constexpr Mat& operator/=(const Type& f) noexcept constexpr Mat& operator/=(const Type& f) noexcept
{ {
for (size_t i = 0; i < Rows; ++i) for (size_t i = 0; i < Rows; ++i)
for (size_t j = 0; j < Columns; ++j) for (size_t j = 0; j < Columns; ++j)
@@ -181,14 +192,14 @@ namespace omath
return *this; return *this;
} }
OMATH_API constexpr Mat operator/(const Type& f) const noexcept constexpr Mat operator/(const Type& f) const noexcept
{ {
Mat result(*this); Mat result(*this);
result /= f; result /= f;
return result; return result;
} }
OMATH_API constexpr Mat& operator=(const Mat& other) noexcept constexpr Mat& operator=(const Mat& other) noexcept
{ {
if (this == &other) if (this == &other)
return *this; return *this;
@@ -198,7 +209,7 @@ namespace omath
return *this; return *this;
} }
OMATH_API constexpr Mat& operator=(Mat&& other) noexcept constexpr Mat& operator=(Mat&& other) noexcept
{ {
if (this == &other) if (this == &other)
return *this; return *this;
@@ -211,7 +222,7 @@ namespace omath
} }
[[nodiscard]] [[nodiscard]]
OMATH_API constexpr Mat<Columns, Rows, Type, StoreType> Transposed() const noexcept constexpr Mat<Columns, Rows, Type, StoreType> Transposed() const noexcept
{ {
Mat<Columns, Rows, Type, StoreType> transposed; Mat<Columns, Rows, Type, StoreType> transposed;
for (size_t i = 0; i < Rows; ++i) for (size_t i = 0; i < Rows; ++i)
@@ -222,7 +233,7 @@ namespace omath
} }
[[nodiscard]] [[nodiscard]]
OMATH_API constexpr Type Determinant() const constexpr Type Determinant() const
{ {
static_assert(Rows == Columns, "Determinant is only defined for square matrices."); static_assert(Rows == Columns, "Determinant is only defined for square matrices.");
@@ -244,7 +255,7 @@ namespace omath
} }
[[nodiscard]] [[nodiscard]]
OMATH_API constexpr Mat<Rows - 1, Columns - 1, Type, StoreType> Minor(const size_t row, const size_t column) const constexpr Mat<Rows - 1, Columns - 1, Type, StoreType> Minor(const size_t row, const size_t column) const
{ {
Mat<Rows - 1, Columns - 1, Type, StoreType> result; Mat<Rows - 1, Columns - 1, Type, StoreType> result;
for (size_t i = 0, m = 0; i < Rows; ++i) for (size_t i = 0, m = 0; i < Rows; ++i)
@@ -264,19 +275,19 @@ namespace omath
} }
[[nodiscard]] [[nodiscard]]
OMATH_API constexpr const std::array<Type, Rows * Columns>& RawArray() const constexpr const std::array<Type, Rows * Columns>& RawArray() const
{ {
return m_data; return m_data;
} }
[[nodiscard]] [[nodiscard]]
OMATH_API constexpr std::array<Type, Rows * Columns>& RawArray() constexpr std::array<Type, Rows * Columns>& RawArray()
{ {
return const_cast<std::array<Type, Rows * Columns>>(std::as_const(*this).RawArray()); return const_cast<std::array<Type, Rows * Columns>>(std::as_const(*this).RawArray());
} }
[[nodiscard]] [[nodiscard]]
OMATH_API std::string ToString() const noexcept std::string ToString() const noexcept
{ {
std::ostringstream oss; std::ostringstream oss;
for (size_t i = 0; i < Rows; ++i) for (size_t i = 0; i < Rows; ++i)
@@ -293,20 +304,20 @@ namespace omath
} }
[[nodiscard]] [[nodiscard]]
OMATH_API bool operator==(const Mat& mat) const bool operator==(const Mat& mat) const
{ {
return m_data == mat.m_data; return m_data == mat.m_data;
} }
[[nodiscard]] [[nodiscard]]
OMATH_API bool operator!=(const Mat& mat) const bool operator!=(const Mat& mat) const
{ {
return !operator==(mat); return !operator==(mat);
} }
// Static methods that return fixed-size matrices // Static methods that return fixed-size matrices
[[nodiscard]] [[nodiscard]]
OMATH_API constexpr static Mat<4, 4> ToScreenMat(const Type& screenWidth, const Type& screenHeight) noexcept constexpr static Mat<4, 4> ToScreenMat(const Type& screenWidth, const Type& screenHeight) noexcept
{ {
return { return {
{screenWidth / 2, 0, 0, 0}, {screenWidth / 2, 0, 0, 0},
@@ -322,21 +333,21 @@ namespace omath
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]] [[nodiscard]]
constexpr static Mat<1, 4, Type, St> MatRowFromVector(const Vector3& vector) noexcept constexpr static Mat<1, 4, Type, St> MatRowFromVector(const Vector3<Type>& vector) noexcept
{ {
return {{vector.x, vector.y, vector.z, 1}}; return {{vector.x, vector.y, vector.z, 1}};
} }
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]] [[nodiscard]]
constexpr static Mat<4, 1, Type, St> MatColumnFromVector(const Vector3& vector) noexcept constexpr static Mat<4, 1, Type, St> MatColumnFromVector(const Vector3<Type>& vector) noexcept
{ {
return {{vector.x}, {vector.y}, {vector.z}, {1}}; return {{vector.x}, {vector.y}, {vector.z}, {1}};
} }
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]] [[nodiscard]]
OMATH_API constexpr Mat<4, 4, Type, St> MatTranslation(const Vector3& diff) noexcept constexpr Mat<4, 4, Type, St> MatTranslation(const Vector3<Type>& diff) noexcept
{ {
return return
{ {
@@ -349,7 +360,7 @@ namespace omath
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
[[nodiscard]] [[nodiscard]]
OMATH_API Mat<4, 4, Type, St> MatRotationAxisX(const Angle& angle) noexcept Mat<4, 4, Type, St> MatRotationAxisX(const Angle& angle) noexcept
{ {
return return
{ {
@@ -362,7 +373,7 @@ namespace omath
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
[[nodiscard]] [[nodiscard]]
OMATH_API Mat<4, 4, Type, St> MatRotationAxisY(const Angle& angle) noexcept Mat<4, 4, Type, St> MatRotationAxisY(const Angle& angle) noexcept
{ {
return return
{ {
@@ -375,7 +386,7 @@ namespace omath
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
[[nodiscard]] [[nodiscard]]
OMATH_API Mat<4, 4, Type, St> MatRotationAxisZ(const Angle& angle) noexcept Mat<4, 4, Type, St> MatRotationAxisZ(const Angle& angle) noexcept
{ {
return return
{ {
@@ -388,8 +399,8 @@ namespace omath
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]] [[nodiscard]]
static Mat<4, 4, Type, St> MatCameraView(const Vector3& forward, const Vector3& right, const Vector3& up, static Mat<4, 4, Type, St> MatCameraView(const Vector3<Type>& forward, const Vector3<Type>& right,
const Vector3& cameraOrigin) noexcept const Vector3<Type>& up, const Vector3<Type>& cameraOrigin) noexcept
{ {
return Mat<4, 4, Type, St> return Mat<4, 4, Type, St>
{ {
@@ -403,7 +414,7 @@ namespace omath
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class ViewAngles> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class ViewAngles>
[[nodiscard]] [[nodiscard]]
OMATH_API Mat<4, 4, Type, St> MatRotation(const ViewAngles& angles) noexcept Mat<4, 4, Type, St> MatRotation(const ViewAngles& angles) noexcept
{ {
return MatRotationAxisZ(angles.yaw) * MatRotationAxisY(angles.pitch) * MatRotationAxisX(angles.roll); return MatRotationAxisZ(angles.yaw) * MatRotationAxisY(angles.pitch) * MatRotationAxisX(angles.roll);
} }

View File

@@ -2,100 +2,103 @@
#include <initializer_list> #include <initializer_list>
#include <memory> #include <memory>
#include <string> #include <string>
#include "omath/omath_export.hpp" #include "Vector3.hpp"
namespace omath namespace omath
{ {
class Vector3;
class Matrix final class Matrix final
{ {
public: public:
OMATH_API Matrix(); Matrix();
OMATH_API Matrix(size_t rows, size_t columns); Matrix(size_t rows, size_t columns);
OMATH_API Matrix(const std::initializer_list<std::initializer_list<float>>& rows); Matrix(const std::initializer_list<std::initializer_list<float>>& rows);
[[nodiscard]] [[nodiscard]]
OMATH_API static Matrix ToScreenMatrix(float screenWidth, float screenHeight); static Matrix ToScreenMatrix(float screenWidth, float screenHeight);
[[nodiscard]] [[nodiscard]]
OMATH_API static Matrix TranslationMatrix(const Vector3& diff); static Matrix TranslationMatrix(const Vector3<float>& diff);
[[nodiscard]] [[nodiscard]]
OMATH_API static Matrix OrientationMatrix(const Vector3& forward, const Vector3& right, const Vector3& up); static Matrix OrientationMatrix(const Vector3<float>& forward, const Vector3<float>& right, const Vector3<float>& up);
[[nodiscard]] [[nodiscard]]
OMATH_API static Matrix ProjectionMatrix(float fieldOfView, float aspectRatio, float near, float far); static Matrix ProjectionMatrix(float fieldOfView, float aspectRatio, float near, float far);
OMATH_API Matrix(const Matrix& other); Matrix(const Matrix& other);
OMATH_API Matrix(size_t rows, size_t columns, const float* pRaw); Matrix(size_t rows, size_t columns, const float* pRaw);
OMATH_API Matrix(Matrix&& other) noexcept; Matrix(Matrix&& other) noexcept;
[[nodiscard]] [[nodiscard]]
OMATH_API size_t RowCount() const noexcept; size_t RowCount() const noexcept;
[[nodiscard]] [[nodiscard]]
OMATH_API size_t ColumnsCount() const noexcept; float& operator[](size_t row, size_t column);
[[nodiscard]] [[nodiscard]]
OMATH_API std::pair<size_t, size_t> Size() const noexcept; size_t ColumnsCount() const noexcept;
[[nodiscard]] [[nodiscard]]
OMATH_API float& At(size_t iRow, size_t iCol); std::pair<size_t, size_t> Size() const noexcept;
[[nodiscard]] [[nodiscard]]
OMATH_API float Sum(); float& At(size_t iRow, size_t iCol);
OMATH_API void SetDataFromRaw(const float* pRawMatrix);
[[nodiscard]] [[nodiscard]]
OMATH_API Matrix Transpose() const; float Sum();
OMATH_API void Set(float val); void SetDataFromRaw(const float* pRawMatrix);
[[nodiscard]] [[nodiscard]]
OMATH_API const float& At(size_t iRow, size_t iCol) const; Matrix Transpose() const;
OMATH_API Matrix operator*(const Matrix& other) const; void Set(float val);
OMATH_API Matrix& operator*=(const Matrix& other);
OMATH_API Matrix operator*(float f) const;
OMATH_API Matrix& operator*=(float f);
OMATH_API Matrix& operator/=(float f);
OMATH_API void Clear();
[[nodiscard]] [[nodiscard]]
OMATH_API Matrix Strip(size_t row, size_t column) const; const float& At(size_t iRow, size_t iCol) const;
Matrix operator*(const Matrix& other) const;
Matrix& operator*=(const Matrix& other);
Matrix operator*(float f) const;
Matrix& operator*=(float f);
Matrix& operator/=(float f);
void Clear();
[[nodiscard]] [[nodiscard]]
OMATH_API float Minor(size_t i, size_t j) const; Matrix Strip(size_t row, size_t column) const;
[[nodiscard]] [[nodiscard]]
OMATH_API float AlgComplement(size_t i, size_t j) const; float Minor(size_t i, size_t j) const;
[[nodiscard]] [[nodiscard]]
OMATH_API float Determinant() const; float AlgComplement(size_t i, size_t j) const;
[[nodiscard]] [[nodiscard]]
OMATH_API const float* Raw() const; float Determinant() const;
OMATH_API Matrix& operator=(const Matrix& other);
OMATH_API Matrix& operator=(Matrix&& other) noexcept;
OMATH_API Matrix operator/(float f) const;
[[nodiscard]] [[nodiscard]]
OMATH_API std::string ToString() const; const float* Raw() const;
OMATH_API ~Matrix(); Matrix& operator=(const Matrix& other);
Matrix& operator=(Matrix&& other) noexcept;
Matrix operator/(float f) const;
[[nodiscard]]
std::string ToString() const;
~Matrix();
private: private:
size_t m_rows; size_t m_rows;

View File

@@ -0,0 +1,74 @@
//
// Created by Orange on 11/13/2024.
//
#pragma once
#include "omath/Vector3.hpp"
namespace omath
{
template<class Vector>
class Triangle final
{
public:
constexpr Triangle() = default;
constexpr Triangle(const Vector& vertex1, const Vector& vertex2, const Vector& vertex3)
: m_vertex1(vertex1), m_vertex2(vertex2), m_vertex3(vertex3)
{
}
Vector3<float> m_vertex1;
Vector3<float> m_vertex2;
Vector3<float> m_vertex3;
[[nodiscard]]
constexpr Vector3<float> CalculateNormal() const
{
const auto b = SideBVector();
const auto a = SideAVector();
return b.Cross(a).Normalized();
}
[[nodiscard]]
float SideALength() const
{
return m_vertex1.DistTo(m_vertex2);
}
[[nodiscard]]
float SideBLength() const
{
return m_vertex3.DistTo(m_vertex2);
}
[[nodiscard]]
constexpr Vector3<float> SideAVector() const
{
return m_vertex1 - m_vertex2;
}
[[nodiscard]]
constexpr float Hypot() const
{
return m_vertex1.DistTo(m_vertex3);
}
[[nodiscard]]
constexpr bool IsRectangular() const
{
const auto sideA = SideALength();
const auto sideB = SideBLength();
const auto hypot = Hypot();
return std::abs(sideA*sideA + sideB*sideB - hypot*hypot) <= 0.0001f;
}
[[nodiscard]]
constexpr Vector3<float> SideBVector() const
{
return m_vertex3 - m_vertex2;
}
[[nodiscard]]
constexpr Vector3<float> MidPoint() const
{
return (m_vertex1 + m_vertex2 + m_vertex3) / 3;
}
};
} // namespace omath

View File

@@ -1,34 +0,0 @@
//
// Created by Orange on 11/13/2024.
//
#pragma once
#include "omath/Vector3.hpp"
#include "omath/omath_export.hpp"
namespace omath
{
class OMATH_API Triangle3d final
{
public:
Triangle3d(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3);
Vector3 m_vertex1;
Vector3 m_vertex2;
Vector3 m_vertex3;
[[nodiscard]]
Vector3 CalculateNormal() const;
[[nodiscard]]
float SideALength() const;
[[nodiscard]]
float SideBLength() const;
[[nodiscard]]
Vector3 SideAVector() const;
[[nodiscard]]
Vector3 SideBVector() const;
};
}

View File

@@ -5,20 +5,21 @@
#pragma once #pragma once
#include <tuple> #include <tuple>
#include <cmath> #include <cmath>
#include "omath/omath_export.hpp"
namespace omath namespace omath
{ {
class OMATH_API Vector2
template<class Type> requires std::is_arithmetic_v<Type>
class Vector2
{ {
public: public:
float x = 0.f; Type x = static_cast<Type>(0);
float y = 0.f; Type y = static_cast<Type>(0);
// Constructors // Constructors
constexpr Vector2() = default; constexpr Vector2() = default;
constexpr Vector2(const float x, const float y) : x(x), y(y) {} constexpr Vector2(const Type& x, const Type& y) : x(x), y(y) {}
// Equality operators // Equality operators
[[nodiscard]] [[nodiscard]]
@@ -66,7 +67,7 @@ namespace omath
return *this; return *this;
} }
constexpr Vector2& operator*=(const float fl) constexpr Vector2& operator*=(const Type& fl)
{ {
x *= fl; x *= fl;
y *= fl; y *= fl;
@@ -74,7 +75,7 @@ namespace omath
return *this; return *this;
} }
constexpr Vector2& operator/=(const float fl) constexpr Vector2& operator/=(const Type& fl)
{ {
x /= fl; x /= fl;
y /= fl; y /= fl;
@@ -82,7 +83,7 @@ namespace omath
return *this; return *this;
} }
constexpr Vector2& operator+=(const float fl) constexpr Vector2& operator+=(const Type& fl)
{ {
x += fl; x += fl;
y += fl; y += fl;
@@ -90,7 +91,7 @@ namespace omath
return *this; return *this;
} }
constexpr Vector2& operator-=(const float fl) constexpr Vector2& operator-=(const Type& fl)
{ {
x -= fl; x -= fl;
y -= fl; y -= fl;
@@ -99,45 +100,45 @@ namespace omath
} }
// Basic vector operations // Basic vector operations
[[nodiscard]] float DistTo(const Vector2& vOther) const [[nodiscard]] Type DistTo(const Vector2& vOther) const
{ {
return std::sqrt(DistToSqr(vOther)); return std::sqrt(DistToSqr(vOther));
} }
[[nodiscard]] constexpr float DistToSqr(const Vector2& vOther) const [[nodiscard]] constexpr Type DistToSqr(const Vector2& vOther) const
{ {
return (x - vOther.x) * (x - vOther.x) + (y - vOther.y) * (y - vOther.y); return (x - vOther.x) * (x - vOther.x) + (y - vOther.y) * (y - vOther.y);
} }
[[nodiscard]] constexpr float Dot(const Vector2& vOther) const [[nodiscard]] constexpr Type Dot(const Vector2& vOther) const
{ {
return x * vOther.x + y * vOther.y; return x * vOther.x + y * vOther.y;
} }
#ifndef _MSC_VER #ifndef _MSC_VER
[[nodiscard]] constexpr float Length() const [[nodiscard]] constexpr Type& Length() const
{ {
return std::hypot(x, y); return std::hypot(x, y);
} }
[[nodiscard]] constexpr Vector2 Normalized() const [[nodiscard]] constexpr Vector2 Normalized() const
{ {
const float len = Length(); const Type len = Length();
return len > 0.f ? *this / len : *this; return len > 0.f ? *this / len : *this;
} }
#else #else
[[nodiscard]] float Length() const [[nodiscard]] Type Length() const
{ {
return std::hypot(x, y); return std::hypot(x, y);
} }
[[nodiscard]] Vector2 Normalized() const [[nodiscard]] Vector2 Normalized() const
{ {
const float len = Length(); const Type len = Length();
return len > 0.f ? *this / len : *this; return len > 0.f ? *this / len : *this;
} }
#endif #endif
[[nodiscard]] constexpr float LengthSqr() const [[nodiscard]] constexpr Type LengthSqr() const
{ {
return x * x + y * y; return x * x + y * y;
} }
@@ -188,13 +189,13 @@ namespace omath
} }
// Sum of elements // Sum of elements
[[nodiscard]] constexpr float Sum() const [[nodiscard]] constexpr Type Sum() const
{ {
return x + y; return x + y;
} }
[[nodiscard]] [[nodiscard]]
constexpr std::tuple<float, float> AsTuple() const constexpr std::tuple<Type, Type> AsTuple() const
{ {
return std::make_tuple(x, y); return std::make_tuple(x, y);
} }

View File

@@ -7,20 +7,30 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include "omath/Vector2.hpp" #include "omath/Vector2.hpp"
#include "omath/omath_export.hpp" #include "omath/Angle.hpp"
#include <expected>
#include <immintrin.h>
namespace omath namespace omath
{ {
class OMATH_API Vector3 : public Vector2
enum class Vector3Error
{
IMPOSSIBLE_BETWEEN_ANGLE,
};
template<class Type> requires std::is_arithmetic_v<Type>
class Vector3 : public Vector2<Type>
{ {
public: public:
float z = 0.f; Type z = static_cast<Type>(0);
constexpr Vector3(const float x, const float y, const float z) : Vector2(x, y), z(z) { } constexpr Vector3(const Type& x, const Type& y, const Type& z) : Vector2<Type>(x, y), z(z) { }
constexpr Vector3() : Vector2() {}; constexpr Vector3() : Vector2<Type>() {};
[[nodiscard]] constexpr bool operator==(const Vector3& src) const [[nodiscard]] constexpr bool operator==(const Vector3& src) const
{ {
return Vector2::operator==(src) && (src.z == z); return Vector2<Type>::operator==(src) && (src.z == z);
} }
[[nodiscard]] constexpr bool operator!=(const Vector3& src) const [[nodiscard]] constexpr bool operator!=(const Vector3& src) const
@@ -30,7 +40,7 @@ namespace omath
constexpr Vector3& operator+=(const Vector3& v) constexpr Vector3& operator+=(const Vector3& v)
{ {
Vector2::operator+=(v); Vector2<Type>::operator+=(v);
z += v.z; z += v.z;
return *this; return *this;
@@ -38,7 +48,7 @@ namespace omath
constexpr Vector3& operator-=(const Vector3& v) constexpr Vector3& operator-=(const Vector3& v)
{ {
Vector2::operator-=(v); Vector2<Type>::operator-=(v);
z -= v.z; z -= v.z;
return *this; return *this;
@@ -46,7 +56,7 @@ namespace omath
constexpr Vector3& operator*=(const float fl) constexpr Vector3& operator*=(const float fl)
{ {
Vector2::operator*=(fl); Vector2<Type>::operator*=(fl);
z *= fl; z *= fl;
return *this; return *this;
@@ -54,7 +64,7 @@ namespace omath
constexpr Vector3& operator*=(const Vector3& v) constexpr Vector3& operator*=(const Vector3& v)
{ {
Vector2::operator*=(v); Vector2<Type>::operator*=(v);
z *= v.z; z *= v.z;
return *this; return *this;
@@ -62,7 +72,7 @@ namespace omath
constexpr Vector3& operator/=(const Vector3& v) constexpr Vector3& operator/=(const Vector3& v)
{ {
Vector2::operator/=(v); Vector2<Type>::operator/=(v);
z /= v.z; z /= v.z;
return *this; return *this;
@@ -70,7 +80,7 @@ namespace omath
constexpr Vector3& operator+=(const float fl) constexpr Vector3& operator+=(const float fl)
{ {
Vector2::operator+=(fl); Vector2<Type>::operator+=(fl);
z += fl; z += fl;
return *this; return *this;
@@ -78,7 +88,7 @@ namespace omath
constexpr Vector3& operator/=(const float fl) constexpr Vector3& operator/=(const float fl)
{ {
Vector2::operator/=(fl); Vector2<Type>::operator/=(fl);
z /= fl; z /= fl;
return *this; return *this;
@@ -86,7 +96,7 @@ namespace omath
constexpr Vector3& operator-=(const float fl) constexpr Vector3& operator-=(const float fl)
{ {
Vector2::operator-=(fl); Vector2<Type>::operator-=(fl);
z -= fl; z -= fl;
return *this; return *this;
@@ -94,7 +104,7 @@ namespace omath
constexpr Vector3& Abs() constexpr Vector3& Abs()
{ {
Vector2::Abs(); Vector2<Type>::Abs();
z = z < 0.f ? -z : z; z = z < 0.f ? -z : z;
return *this; return *this;
@@ -107,7 +117,7 @@ namespace omath
[[nodiscard]] constexpr float Dot(const Vector3& vOther) const [[nodiscard]] constexpr float Dot(const Vector3& vOther) const
{ {
return Vector2::Dot(vOther) + z * vOther.z; return Vector2<Type>::Dot(vOther) + z * vOther.z;
} }
#ifndef _MSC_VER #ifndef _MSC_VER
@@ -133,7 +143,7 @@ namespace omath
#else #else
[[nodiscard]] float Length() const [[nodiscard]] float Length() const
{ {
return std::hypot(x, y, z); return std::hypot(this->x, this->y, z);
} }
[[nodiscard]] Vector3 Normalized() const [[nodiscard]] Vector3 Normalized() const
@@ -143,9 +153,9 @@ namespace omath
return length != 0 ? *this / length : *this; return length != 0 ? *this / length : *this;
} }
[[nodiscard]] float Length2D() const [[nodiscard]] Type Length2D() const
{ {
return Vector2::Length(); return Vector2<Type>::Length();
} }
[[nodiscard]] float DistTo(const Vector3& vOther) const [[nodiscard]] float DistTo(const Vector3& vOther) const
@@ -157,51 +167,51 @@ namespace omath
[[nodiscard]] constexpr float LengthSqr() const [[nodiscard]] constexpr float LengthSqr() const
{ {
return Vector2::LengthSqr() + z * z; return Vector2<Type>::LengthSqr() + z * z;
} }
[[nodiscard]] constexpr Vector3 operator-() const [[nodiscard]] constexpr Vector3 operator-() const
{ {
return {-x, -y, -z}; return {-this->x, -this->y, -z};
} }
[[nodiscard]] constexpr Vector3 operator+(const Vector3& v) const [[nodiscard]] constexpr Vector3 operator+(const Vector3& v) const
{ {
return {x + v.x, y + v.y, z + v.z}; return {this->x + v.x, this->y + v.y, z + v.z};
} }
[[nodiscard]] constexpr Vector3 operator-(const Vector3& v) const [[nodiscard]] constexpr Vector3 operator-(const Vector3& v) const
{ {
return {x - v.x, y - v.y, z - v.z}; return {this->x - v.x, this->y - v.y, z - v.z};
} }
[[nodiscard]] constexpr Vector3 operator*(const float fl) const [[nodiscard]] constexpr Vector3 operator*(const float fl) const
{ {
return {x * fl, y * fl, z * fl}; return {this->x * fl, this->y * fl, z * fl};
} }
[[nodiscard]] constexpr Vector3 operator*(const Vector3& v) const [[nodiscard]] constexpr Vector3 operator*(const Vector3& v) const
{ {
return {x * v.x, y * v.y, z * v.z}; return {this->x * v.x, this->y * v.y, z * v.z};
} }
[[nodiscard]] constexpr Vector3 operator/(const float fl) const [[nodiscard]] constexpr Vector3 operator/(const float fl) const
{ {
return {x / fl, y / fl, z / fl}; return {this->x / fl, this->y / fl, z / fl};
} }
[[nodiscard]] constexpr Vector3 operator/(const Vector3& v) const [[nodiscard]] constexpr Vector3 operator/(const Vector3& v) const
{ {
return {x / v.x, y / v.y, z / v.z}; return {this->x / v.x, this->y / v.y, z / v.z};
} }
[[nodiscard]] constexpr Vector3 Cross(const Vector3 &v) const [[nodiscard]] constexpr Vector3 Cross(const Vector3 &v) const
{ {
return return
{ {
y * v.z - z * v.y, this->y * v.z - z * v.y,
z * v.x - x * v.z, z * v.x - this->x * v.z,
x * v.y - y * v.x this->x * v.y - this->y * v.x
}; };
} }
[[nodiscard]] constexpr float Sum() const [[nodiscard]] constexpr float Sum() const
@@ -209,16 +219,46 @@ namespace omath
return Sum2D() + z; return Sum2D() + z;
} }
[[nodiscard]] constexpr float Sum2D() const [[nodiscard]] std::expected<Angle<float, 0.f, 180.f, AngleFlags::Clamped>, Vector3Error>
AngleBetween(const Vector3& other) const
{ {
return Vector2::Sum(); const auto bottom = Length() * other.Length();
if (bottom == 0.f)
return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE);
return Angle<float, 0.f, 180.f, AngleFlags::Clamped>::FromRadians(std::acos(Dot(other) / bottom));
} }
[[nodiscard]] Vector3 ViewAngleTo(const Vector3& other) const; [[nodiscard]] bool IsPerpendicular(const Vector3& other) const
[[nodiscard]] std::tuple<float, float, float> AsTuple() const
{ {
return std::make_tuple(x, y, z); if (const auto angle = AngleBetween(other))
return angle->AsDegrees() == 90.f;
return false;
}
[[nodiscard]] constexpr float Sum2D() const
{
return Vector2<Type>::Sum();
}
[[nodiscard]] constexpr std::tuple<float, float, float> AsTuple() const
{
return std::make_tuple(this->x, this->y, z);
}
[[nodiscard]] Vector3 ViewAngleTo(const Vector3 &other) const
{
const float distance = DistTo(other);
const auto delta = other - *this;
return
{
angles::RadiansToDegrees(std::asin(delta.z / distance)),
angles::RadiansToDegrees(std::atan2(delta.y, delta.x)),
0
};
} }
}; };
} }
@@ -226,9 +266,9 @@ namespace omath
namespace std namespace std
{ {
template<> template<>
struct hash<omath::Vector3> struct hash<omath::Vector3<float>>
{ {
std::size_t operator()(const omath::Vector3& vec) const noexcept std::size_t operator()(const omath::Vector3<float>& vec) const noexcept
{ {
std::size_t hash = 0; std::size_t hash = 0;
constexpr std::hash<float> hasher; constexpr std::hash<float> hasher;

View File

@@ -5,22 +5,22 @@
#include <omath/Vector3.hpp> #include <omath/Vector3.hpp>
#include <algorithm> #include <algorithm>
#include "omath/omath_export.hpp"
namespace omath namespace omath
{ {
class OMATH_API Vector4 : public Vector3 template <class Type>
class Vector4 : public Vector3<Type>
{ {
public: public:
float w; Type w;
constexpr Vector4(const float x, const float y, const float z, const float w) : Vector3(x, y, z), w(w) {} constexpr Vector4(const Type& x, const Type& y, const Type& z, const Type& w) : Vector3<Type>(x, y, z), w(w) {}
constexpr Vector4() : Vector3(), w(0.f) {}; constexpr Vector4() : Vector3<Type>(), w(0) {};
[[nodiscard]] [[nodiscard]]
constexpr bool operator==(const Vector4& src) const constexpr bool operator==(const Vector4& src) const
{ {
return Vector3::operator==(src) && w == src.w; return Vector3<Type>::operator==(src) && w == src.w;
} }
[[nodiscard]] [[nodiscard]]
@@ -31,7 +31,7 @@ namespace omath
constexpr Vector4& operator+=(const Vector4& v) constexpr Vector4& operator+=(const Vector4& v)
{ {
Vector3::operator+=(v); Vector3<Type>::operator+=(v);
w += v.w; w += v.w;
return *this; return *this;
@@ -39,7 +39,7 @@ namespace omath
constexpr Vector4& operator-=(const Vector4& v) constexpr Vector4& operator-=(const Vector4& v)
{ {
Vector3::operator-=(v); Vector3<Type>::operator-=(v);
w -= v.w; w -= v.w;
return *this; return *this;
@@ -47,7 +47,7 @@ namespace omath
constexpr Vector4& operator*=(const float scalar) constexpr Vector4& operator*=(const float scalar)
{ {
Vector3::operator*=(scalar); Vector3<Type>::operator*=(scalar);
w *= scalar; w *= scalar;
return *this; return *this;
@@ -55,7 +55,7 @@ namespace omath
constexpr Vector4& operator*=(const Vector4& v) constexpr Vector4& operator*=(const Vector4& v)
{ {
Vector3::operator*=(v); Vector3<Type>::operator*=(v);
w *= v.w; w *= v.w;
return *this; return *this;
@@ -63,7 +63,7 @@ namespace omath
constexpr Vector4& operator/=(const float scalar) constexpr Vector4& operator/=(const float scalar)
{ {
Vector3::operator/=(scalar); Vector3<Type>::operator/=(scalar);
w /= scalar; w /= scalar;
return *this; return *this;
@@ -71,35 +71,38 @@ namespace omath
constexpr Vector4& operator/=(const Vector4& v) constexpr Vector4& operator/=(const Vector4& v)
{ {
Vector3::operator/=(v); Vector3<Type>::operator/=(v);
w /= v.w; w /= v.w;
return *this; return *this;
} }
[[nodiscard]] constexpr float LengthSqr() const [[nodiscard]] constexpr Type LengthSqr() const
{ {
return Vector3::LengthSqr() + w * w; return Vector3<Type>::LengthSqr() + w * w;
} }
[[nodiscard]] constexpr float Dot(const Vector4& vOther) const [[nodiscard]] constexpr float Dot(const Vector4& vOther) const
{ {
return Vector3::Dot(vOther) + w * vOther.w; return Vector3<Type>::Dot(vOther) + w * vOther.w;
} }
[[nodiscard]] float Length() const; [[nodiscard]] Vector3<Type> Length() const
{
return std::sqrt(LengthSqr());
}
constexpr Vector4& Abs() constexpr Vector4& Abs()
{ {
Vector3::Abs(); Vector3<Type>::Abs();
w = w < 0.f ? -w : w; w = w < 0.f ? -w : w;
return *this; return *this;
} }
constexpr Vector4& Clamp(const float min, const float max) constexpr Vector4& Clamp(const float min, const float max)
{ {
x = std::clamp(x, min, max); this->x = std::clamp(this->x, min, max);
y = std::clamp(y, min, max); this->y = std::clamp(this->y, min, max);
z = std::clamp(z, min, max); this->z = std::clamp(this->z, min, max);
return *this; return *this;
} }
@@ -107,49 +110,49 @@ namespace omath
[[nodiscard]] [[nodiscard]]
constexpr Vector4 operator-() const constexpr Vector4 operator-() const
{ {
return {-x, -y, -z, -w}; return {-this->x, -this->y, -this->z, -w};
} }
[[nodiscard]] [[nodiscard]]
constexpr Vector4 operator+(const Vector4& v) const constexpr Vector4 operator+(const Vector4& v) const
{ {
return {x + v.x, y + v.y, z + v.z, w + v.w}; return {this->x + v.x, this->y + v.y, this->z + v.z, w + v.w};
} }
[[nodiscard]] [[nodiscard]]
constexpr Vector4 operator-(const Vector4& v) const constexpr Vector4 operator-(const Vector4& v) const
{ {
return {x - v.x, y - v.y, z - v.z, w - v.w}; return {this->x - v.x, this->y - v.y, this->z - v.z, w - v.w};
} }
[[nodiscard]] [[nodiscard]]
constexpr Vector4 operator*(const float scalar) const constexpr Vector4 operator*(const float scalar) const
{ {
return {x * scalar, y * scalar, z * scalar, w * scalar}; return {this->x * scalar, this->y * scalar, this->z * scalar, w * scalar};
} }
[[nodiscard]] [[nodiscard]]
constexpr Vector4 operator*(const Vector4& v) const constexpr Vector4 operator*(const Vector4& v) const
{ {
return {x * v.x, y * v.y, z * v.z, w * v.w}; return {this->x * v.x, this->y * v.y, this->z * v.z, w * v.w};
} }
[[nodiscard]] [[nodiscard]]
constexpr Vector4 operator/(const float scalar) const constexpr Vector4 operator/(const float scalar) const
{ {
return {x / scalar, y / scalar, z / scalar, w / scalar}; return {this->x / scalar, this->y / scalar, this->z / scalar, w / scalar};
} }
[[nodiscard]] [[nodiscard]]
constexpr Vector4 operator/(const Vector4& v) const constexpr Vector4 operator/(const Vector4& v) const
{ {
return {x / v.x, y / v.y, z / v.z, w / v.w}; return {this->x / v.x, this->y / v.y, this->z / v.z, w / v.w};
} }
[[nodiscard]] [[nodiscard]]
constexpr float Sum() const constexpr Type Sum() const
{ {
return Vector3::Sum() + w; return Vector3<Type>::Sum() + w;
} }
}; };
} }

View File

@@ -3,12 +3,10 @@
// //
#pragma once #pragma once
#include "omath/omath_export.hpp"
namespace omath namespace omath
{ {
template<class PitchType, class YawType, class RollType> template<class PitchType, class YawType, class RollType>
struct OMATH_API ViewAngles struct ViewAngles
{ {
PitchType pitch; PitchType pitch;
YawType yaw; YawType yaw;

View File

@@ -4,36 +4,35 @@
#pragma once #pragma once
#include "omath/Vector3.hpp" #include "omath/Vector3.hpp"
#include "omath/Triangle3d.hpp" #include "omath/Triangle.hpp"
#include "omath/omath_export.hpp"
namespace omath::collision namespace omath::collision
{ {
class OMATH_API Ray class Ray
{ {
public: public:
Vector3 start; Vector3<float> start;
Vector3 end; Vector3<float> end;
[[nodiscard]] [[nodiscard]]
Vector3 DirectionVector() const; Vector3<float> DirectionVector() const;
[[nodiscard]] [[nodiscard]]
Vector3 DirectionVectorNormalized() const; Vector3<float> DirectionVectorNormalized() const;
}; };
class OMATH_API LineTracer class LineTracer
{ {
public: public:
LineTracer() = delete; LineTracer() = delete;
[[nodiscard]] [[nodiscard]]
static bool CanTraceLine(const Ray& ray, const Triangle3d& triangle); static bool CanTraceLine(const Ray& ray, const Triangle<Vector3<float>>& triangle);
// Realization of MöllerTrumbore intersection algorithm // Realization of MöllerTrumbore intersection algorithm
// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
[[nodiscard]] [[nodiscard]]
static Vector3 GetRayHitPoint(const Ray& ray, const Triangle3d& triangle); static Vector3<float> GetRayHitPoint(const Ray& ray, const Triangle<Vector3<float>>& triangle);
}; };
} }

View File

@@ -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<Mat4x4, ViewAngles>
{
public:
Camera(const Vector3<float>& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort,
const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, float near, float far);
void LookAt(const Vector3<float>& target) override;
[[nodiscard]] Mat4x4 CalcViewMatrix() const override;
[[nodiscard]] Mat4x4 CalcProjectionMatrix() const override;
};
}

View File

@@ -0,0 +1,25 @@
//
// Created by Orange on 12/23/2024.
//
#pragma once
#include <omath/Vector3.hpp>
#include <omath/Mat.hpp>
#include <omath/Angle.hpp>
#include <omath/ViewAngles.hpp>
namespace omath::opengl
{
constexpr Vector3<float> kAbsUp = {0, 1, 0};
constexpr Vector3<float> kAbsRight = {1, 0, 0};
constexpr Vector3<float> 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<float, 0.f, 180.f, AngleFlags::Clamped>;
using YawAngle = Angle<float, 0.f, 360.f, AngleFlags::Normalized>;
using RollAngle = Angle<float, 0.f, 360.f, AngleFlags::Normalized>;
using ViewAngles = omath::ViewAngles<PitchAngle, YawAngle, RollAngle>;
}

View File

@@ -0,0 +1,54 @@
//
// Created by Orange on 12/23/2024.
//
#pragma once
#include "Constants.hpp"
namespace omath::opengl
{
[[nodiscard]]
inline Vector3<float> 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<float> 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<float> 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<float>& cam_origin)
{
return MatCameraView<float, MatStoreType::COLUMN_MAJOR>(-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},
};
}
}

View File

@@ -2,19 +2,18 @@
// Created by Orange on 12/4/2024. // Created by Orange on 12/4/2024.
// //
#pragma once #pragma once
#include "Constants.h" #include "Constants.hpp"
#include "omath/projection/Camera.hpp" #include "omath/projection/Camera.hpp"
#include "omath/omath_export.hpp"
namespace omath::source namespace omath::source
{ {
class OMATH_API Camera final : public projection::Camera<Mat4x4, ViewAngles> class Camera final : public projection::Camera<Mat4x4, ViewAngles>
{ {
public: public:
Camera(const Vector3& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort, Camera(const Vector3<float>& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort,
const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, float near, float far); const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, float near, float far);
void LookAt(const Vector3& target) override; void LookAt(const Vector3<float>& target) override;
[[nodiscard]] Mat4x4 GetViewMatrix() const override; [[nodiscard]] Mat4x4 CalcViewMatrix() const override;
[[nodiscard]] Mat4x4 GetProjectionMatrix() const override; [[nodiscard]] Mat4x4 CalcProjectionMatrix() const override;
}; };
} }

View File

@@ -9,9 +9,9 @@
#include <omath/ViewAngles.hpp> #include <omath/ViewAngles.hpp>
namespace omath::source namespace omath::source
{ {
constexpr Vector3 kAbsUp = {0, 0, 1}; constexpr Vector3<float> kAbsUp = {0, 0, 1};
constexpr Vector3 kAbsRight = {0, -1, 0}; constexpr Vector3<float> kAbsRight = {0, -1, 0};
constexpr Vector3 kAbsForward = {1, 0, 0}; constexpr Vector3<float> kAbsForward = {1, 0, 0};
using Mat4x4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; using Mat4x4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat3x3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; using Mat3x3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;

View File

@@ -2,12 +2,12 @@
// Created by Orange on 12/4/2024. // Created by Orange on 12/4/2024.
// //
#pragma once #pragma once
#include "Constants.h" #include "Constants.hpp"
namespace omath::source namespace omath::source
{ {
[[nodiscard]] [[nodiscard]]
inline Vector3 ForwardVector(const ViewAngles& angles) inline Vector3<float> ForwardVector(const ViewAngles& angles)
{ {
const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsForward); const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsForward);
@@ -15,7 +15,7 @@ namespace omath::source
} }
[[nodiscard]] [[nodiscard]]
inline Vector3 RightVector(const ViewAngles& angles) inline Vector3<float> RightVector(const ViewAngles& angles)
{ {
const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsRight); const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsRight);
@@ -23,14 +23,14 @@ namespace omath::source
} }
[[nodiscard]] [[nodiscard]]
inline Vector3 UpVector(const ViewAngles& angles) inline Vector3<float> UpVector(const ViewAngles& angles)
{ {
const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsUp); const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsUp);
return {vec.At(0, 0), vec.At(1, 0), vec.At(2, 0)}; return {vec.At(0, 0), vec.At(1, 0), vec.At(2, 0)};
} }
[[nodiscard]] inline Mat4x4 CalcViewMatrix(const ViewAngles& angles, const Vector3& cam_origin) [[nodiscard]] inline Mat4x4 CalcViewMatrix(const ViewAngles& angles, const Vector3<float>& cam_origin)
{ {
return MatCameraView(ForwardVector(angles), RightVector(angles), UpVector(angles), cam_origin); return MatCameraView(ForwardVector(angles), RightVector(angles), UpVector(angles), cam_origin);
} }

View File

@@ -1,24 +0,0 @@
#pragma once
/* Export prefix for functions */
#ifdef _MSC_VER
/* MSVC */
# define OMATH_API_EXPORT __declspec(dllexport)
#else
/* GCC/Clang */
# define OMATH_API_EXPORT __attribute__((visibility("default")))
#endif
/* Import prefix for functions */
#ifdef _MSC_VER
# define OMATH_API_IMPORT __declspec(dllimport)
#else
# define OMATH_API_IMPORT extern
#endif
/* Resolve import/export */
#ifdef OMATH_EXPORT
# define OMATH_API OMATH_API_EXPORT
#else
# define OMATH_API OMATH_API_IMPORT
#endif

View File

@@ -6,15 +6,25 @@
#include <vector> #include <vector>
#include "NavigationMesh.hpp" #include "NavigationMesh.hpp"
#include "omath/Vector3.hpp" #include "omath/Vector3.hpp"
#include "omath/omath_export.hpp"
namespace omath::pathfinding namespace omath::pathfinding
{ {
class OMATH_API Astar final struct PathNode;
class Astar final
{ {
public: public:
[[nodiscard]] [[nodiscard]]
static std::vector<Vector3> FindPath(const Vector3& start, const Vector3& end, const NavigationMesh& navMesh); static std::vector<Vector3<float>> FindPath(const Vector3<float>& start, const Vector3<float>& end,
const NavigationMesh& navMesh);
private:
[[nodiscard]]
static std::vector<Vector3<float>>
ReconstructFinalPath(const std::unordered_map<Vector3<float>, PathNode>& closedList,
const Vector3<float>& current);
[[nodiscard]]
static auto GetPerfectNode(const std::unordered_map<Vector3<float>, PathNode>& openList,
const Vector3<float>& endVertex);
}; };
} } // namespace omath::pathfinding

View File

@@ -8,7 +8,6 @@
#include <expected> #include <expected>
#include <vector> #include <vector>
#include <string> #include <string>
#include "omath/omath_export.hpp"
namespace omath::pathfinding namespace omath::pathfinding
{ {
@@ -23,17 +22,17 @@ namespace omath::pathfinding
public: public:
[[nodiscard]] [[nodiscard]]
OMATH_API std::expected<Vector3, std::string> GetClosestVertex(const Vector3& point) const; std::expected<Vector3<float>, std::string> GetClosestVertex(const Vector3<float>& point) const;
[[nodiscard]] [[nodiscard]]
OMATH_API const std::vector<Vector3>& GetNeighbors(const Vector3& vertex) const; const std::vector<Vector3<float>>& GetNeighbors(const Vector3<float>& vertex) const;
[[nodiscard]] [[nodiscard]]
OMATH_API bool Empty() const; bool Empty() const;
[[nodiscard]] std::vector<uint8_t> Serialize() const; [[nodiscard]] std::vector<uint8_t> Serialize() const;
OMATH_API void Deserialize(const std::vector<uint8_t>& raw); void Deserialize(const std::vector<uint8_t>& raw);
std::unordered_map<Vector3, std::vector<Vector3>> m_verTextMap; std::unordered_map<Vector3<float>, std::vector<Vector3<float>>> m_verTextMap;
}; };
} }

View File

@@ -1,39 +0,0 @@
//
// Created by Vlad on 6/9/2024.
//
#pragma once
#include <optional>
#include "omath/Vector3.hpp"
#include "omath/prediction/Projectile.hpp"
#include "omath/prediction/Target.hpp"
#include "omath/omath_export.hpp"
namespace omath::prediction
{
class OMATH_API Engine final
{
public:
explicit Engine(float gravityConstant, float simulationTimeStep,
float maximumSimulationTime, float distanceTolerance);
[[nodiscard]]
std::optional<Vector3> MaybeCalculateAimPoint(const Projectile& projectile, const Target& target) const;
private:
const float m_gravityConstant;
const float m_simulationTimeStep;
const float m_maximumSimulationTime;
const float m_distanceTolerance;
[[nodiscard]]
std::optional<float> MaybeCalculateProjectileLaunchPitchAngle(const Projectile& projectile,
const Vector3& targetPosition) const;
[[nodiscard]]
bool IsProjectileReachedTarget(const Vector3& targetPosition, const Projectile& projectile, float pitch, float time) const;
};
}

View File

@@ -1,22 +0,0 @@
//
// Created by Vlad on 6/9/2024.
//
#pragma once
#include "omath/Vector3.hpp"
#include "omath/omath_export.hpp"
namespace omath::prediction
{
class OMATH_API Projectile final
{
public:
[[nodiscard]]
Vector3 PredictPosition(float pitch, float yaw, float time, float gravity) const;
Vector3 m_origin;
float m_launchSpeed{};
float m_gravityScale{};
};
}

View File

@@ -0,0 +1,20 @@
//
// Created by Vlad on 2/23/2025.
//
#pragma once
#include "Projectile.hpp"
#include "Target.hpp"
#include "omath/Vector3.hpp"
namespace omath::projectile_prediction
{
class ProjPredEngine
{
public:
[[nodiscard]]
virtual std::optional<Vector3<float>> MaybeCalculateAimPoint(const Projectile& projectile,
const Target& target) const = 0;
virtual ~ProjPredEngine() = default;
};
} // namespace omath::projectile_prediction

View File

@@ -0,0 +1,26 @@
//
// Created by Vlad on 2/23/2025.
//
#pragma once
#include "ProjPredEngine.hpp"
namespace omath::projectile_prediction
{
class ProjPredEngineAVX2 final : public ProjPredEngine
{
public:
[[nodiscard]] std::optional<Vector3<float>> MaybeCalculateAimPoint(const Projectile& projectile,
const Target& target) const override;
ProjPredEngineAVX2(float gravityConstant, float simulationTimeStep, float maximumSimulationTime);
~ProjPredEngineAVX2() override = default;
private:
[[nodiscard]] static std::optional<float> CalculatePitch(const Vector3<float>& projOrigin, const Vector3<float>& targetPos,
float bulletGravity, float v0, float time);
const float m_gravityConstant;
const float m_simulationTimeStep;
const float m_maximumSimulationTime;
};
} // namespace omath::projectile_prediction

View File

@@ -0,0 +1,41 @@
//
// Created by Vlad on 6/9/2024.
//
#pragma once
#include <optional>
#include "omath/Vector3.hpp"
#include "omath/projectile_prediction/ProjPredEngine.hpp"
#include "omath/projectile_prediction/Projectile.hpp"
#include "omath/projectile_prediction/Target.hpp"
namespace omath::projectile_prediction
{
class ProjPredEngineLegacy final : public ProjPredEngine
{
public:
explicit ProjPredEngineLegacy(float gravityConstant, float simulationTimeStep, float maximumSimulationTime,
float distanceTolerance);
[[nodiscard]]
std::optional<Vector3<float>> MaybeCalculateAimPoint(const Projectile& projectile,
const Target& target) const override;
private:
const float m_gravityConstant;
const float m_simulationTimeStep;
const float m_maximumSimulationTime;
const float m_distanceTolerance;
[[nodiscard]]
std::optional<float> MaybeCalculateProjectileLaunchPitchAngle(const Projectile& projectile,
const Vector3<float>& targetPosition) const;
[[nodiscard]]
bool IsProjectileReachedTarget(const Vector3<float>& targetPosition, const Projectile& projectile, float pitch,
float time) const;
};
} // namespace omath::projectile_prediction

View File

@@ -0,0 +1,21 @@
//
// Created by Vlad on 6/9/2024.
//
#pragma once
#include "omath/Vector3.hpp"
namespace omath::projectile_prediction
{
class Projectile final
{
public:
[[nodiscard]]
Vector3<float> PredictPosition(float pitch, float yaw, float time, float gravity) const;
Vector3<float> m_origin;
float m_launchSpeed{};
float m_gravityScale{};
};
}

View File

@@ -4,16 +4,15 @@
#pragma once #pragma once
#include "omath/Vector3.hpp" #include "omath/Vector3.hpp"
#include "omath/omath_export.hpp"
namespace omath::prediction namespace omath::projectile_prediction
{ {
class OMATH_API Target final class Target final
{ {
public: public:
[[nodiscard]] [[nodiscard]]
constexpr Vector3 PredictPosition(const float time, const float gravity) const constexpr Vector3<float> PredictPosition(const float time, const float gravity) const
{ {
auto predicted = m_origin + m_velocity * time; auto predicted = m_origin + m_velocity * time;
@@ -23,8 +22,8 @@ namespace omath::prediction
return predicted; return predicted;
} }
Vector3 m_origin; Vector3<float> m_origin;
Vector3 m_velocity; Vector3<float> m_velocity;
bool m_isAirborne{}; bool m_isAirborne{};
}; };
} }

View File

@@ -10,11 +10,10 @@
#include "ErrorCodes.hpp" #include "ErrorCodes.hpp"
#include <omath/Angle.hpp> #include <omath/Angle.hpp>
#include <type_traits> #include <type_traits>
#include "omath/omath_export.hpp"
namespace omath::projection namespace omath::projection
{ {
class OMATH_API ViewPort final class ViewPort final
{ {
public: public:
float m_width; float m_width;
@@ -25,15 +24,14 @@ namespace omath::projection
return m_width / m_height; return m_width / m_height;
} }
}; };
using FieldOfView = const Angle<float, 0.f, 180.f, AngleFlags::Clamped>; using FieldOfView = Angle<float, 0.f, 180.f, AngleFlags::Clamped>;
template<class Mat4x4Type, class ViewAnglesType> template<class Mat4x4Type, class ViewAnglesType>
class OMATH_API Camera class Camera
{ {
public: public:
virtual ~Camera() = default; virtual ~Camera() = default;
Camera(const Vector3& position, const ViewAnglesType& viewAngles, const ViewPort& viewPort, Camera(const Vector3<float>& position, const ViewAnglesType& viewAngles, const ViewPort& viewPort,
const FieldOfView& fov, const float near, const float far) : const FieldOfView& fov, const float near, const float far) :
m_viewPort(viewPort), m_fieldOfView(fov), m_farPlaneDistance(far), m_nearPlaneDistance(near), m_viewPort(viewPort), m_fieldOfView(fov), m_farPlaneDistance(far), m_nearPlaneDistance(near),
m_viewAngles(viewAngles), m_origin(position) m_viewAngles(viewAngles), m_origin(position)
@@ -41,20 +39,84 @@ namespace omath::projection
} }
virtual void LookAt(const Vector3& target) = 0; virtual void LookAt(const Vector3<float>& 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<Vector3, Error> WorldToScreen(const Mat4x4Type& viewProj, const Vector3& worldPosition) const void SetFieldOfView(const FieldOfView& fov)
{ {
auto projected = viewProj * MatColumnFromVector<float, Mat4x4Type::GetStoreOrdering()>(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<float>& 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<float>& GetOrigin() const
{
return m_origin;
}
[[nodiscard]] std::expected<Vector3<float>, Error> WorldToScreen(const Vector3<float>& worldPosition) const
{
if (!m_viewProjectionMatrix.has_value())
m_viewProjectionMatrix = CalcViewProjectionMatrix();
auto projected = m_viewProjectionMatrix.value() * MatColumnFromVector<float, Mat4x4Type::GetStoreOrdering()>(worldPosition);
if (projected.At(3, 0) == 0.0f) if (projected.At(3, 0) == 0.0f)
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS);
@@ -64,19 +126,24 @@ namespace omath::projection
if (IsNdcOutOfBounds(projected)) if (IsNdcOutOfBounds(projected))
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); 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: protected:
ViewPort m_viewPort{}; ViewPort m_viewPort{};
Angle<float, 0.f, 180.f, AngleFlags::Clamped> m_fieldOfView; Angle<float, 0.f, 180.f, AngleFlags::Clamped> m_fieldOfView;
mutable std::optional<Mat4x4Type> m_viewProjectionMatrix;
float m_farPlaneDistance; float m_farPlaneDistance;
float m_nearPlaneDistance; float m_nearPlaneDistance;
ViewAnglesType m_viewAngles; ViewAnglesType m_viewAngles;
Vector3 m_origin; Vector3<float> m_origin;
private: private:
template<class Type> template<class Type>

View File

@@ -1,13 +1,9 @@
target_sources(omath PRIVATE target_sources(omath PRIVATE
Vector3.cpp
Matrix.cpp Matrix.cpp
color.cpp color.cpp
Vector4.cpp
Vector2.cpp
Triangle3d.cpp
) )
add_subdirectory(prediction) add_subdirectory(projectile_prediction)
add_subdirectory(pathfinding) add_subdirectory(pathfinding)
add_subdirectory(projection) add_subdirectory(projection)
add_subdirectory(collision) add_subdirectory(collision)

View File

@@ -75,6 +75,11 @@ namespace omath
return m_rows; return m_rows;
} }
float& Matrix::operator[](const size_t row, const size_t column)
{
return At(row, column);
}
Matrix::Matrix(Matrix&& other) noexcept Matrix::Matrix(Matrix&& other) noexcept
{ {
m_rows = other.m_rows; m_rows = other.m_rows;
@@ -310,7 +315,7 @@ namespace omath
}; };
} }
Matrix Matrix::TranslationMatrix(const Vector3& diff) Matrix Matrix::TranslationMatrix(const Vector3<float>& diff)
{ {
return { return {
{1.f, 0.f, 0.f, 0.f}, {1.f, 0.f, 0.f, 0.f},
@@ -320,7 +325,7 @@ namespace omath
}; };
} }
Matrix Matrix::OrientationMatrix(const Vector3& forward, const Vector3& right, const Vector3& up) Matrix Matrix::OrientationMatrix(const Vector3<float>& forward, const Vector3<float>& right, const Vector3<float>& up)
{ {
return { return {
{right.x, up.x, forward.x, 0.f}, {right.x, up.x, forward.x, 0.f},

View File

@@ -1,36 +0,0 @@
#include "omath/Triangle3d.hpp"
namespace omath
{
Triangle3d::Triangle3d(const Vector3 &vertex1, const Vector3 &vertex2, const Vector3 &vertex3)
: m_vertex1(vertex1), m_vertex2(vertex2), m_vertex3(vertex3)
{
}
Vector3 Triangle3d::CalculateNormal() const
{
return (m_vertex1 - m_vertex2).Cross(m_vertex3 - m_vertex1).Normalized();
}
float Triangle3d::SideALength() const
{
return m_vertex1.DistTo(m_vertex2);
}
float Triangle3d::SideBLength() const
{
return m_vertex3.DistTo(m_vertex2);
}
Vector3 Triangle3d::SideAVector() const
{
return m_vertex1 - m_vertex2;
}
Vector3 Triangle3d::SideBVector() const
{
return m_vertex3 - m_vertex2;
}
}

View File

@@ -1,11 +0,0 @@
//
// Created by Vlad on 02.09.2024.
//
#include "omath/Vector2.hpp"
#include <cmath>
namespace omath
{
}

View File

@@ -1,23 +0,0 @@
//
// Created by vlad on 10/28/23.
//
#include <omath/Vector3.hpp>
#include <cmath>
#include <omath/Angles.hpp>
namespace omath
{
Vector3 Vector3::ViewAngleTo(const Vector3 &other) const
{
const float distance = DistTo(other);
const auto delta = other - *this;
return
{
angles::RadiansToDegrees(std::asin(delta.z / distance)),
angles::RadiansToDegrees(std::atan2(delta.y, delta.x)),
0.f
};
}
}

View File

@@ -1,16 +0,0 @@
//
// Vector4.cpp
//
#include "omath/Vector4.hpp"
#include <cmath>
namespace omath
{
float Vector4::Length() const
{
return std::sqrt(LengthSqr());
}
}

View File

@@ -5,21 +5,21 @@
namespace omath::collision namespace omath::collision
{ {
bool LineTracer::CanTraceLine(const Ray &ray, const Triangle3d &triangle) bool LineTracer::CanTraceLine(const Ray& ray, const Triangle<Vector3<float>>& triangle)
{ {
return GetRayHitPoint(ray, triangle) == ray.end; return GetRayHitPoint(ray, triangle) == ray.end;
} }
Vector3 Ray::DirectionVector() const Vector3<float> Ray::DirectionVector() const
{ {
return end - start; return end - start;
} }
Vector3 Ray::DirectionVectorNormalized() const Vector3<float> Ray::DirectionVectorNormalized() const
{ {
return DirectionVector().Normalized(); return DirectionVector().Normalized();
} }
Vector3 LineTracer::GetRayHitPoint(const Ray &ray, const Triangle3d &triangle) Vector3<float> LineTracer::GetRayHitPoint(const Ray& ray, const Triangle<Vector3<float>>& triangle)
{ {
constexpr float kEpsilon = std::numeric_limits<float>::epsilon(); constexpr float kEpsilon = std::numeric_limits<float>::epsilon();
@@ -41,7 +41,7 @@ namespace omath::collision
const auto u = t.Dot(p) * invDet; const auto u = t.Dot(p) * invDet;
if ((u < 0 && std::abs(u) > kEpsilon) || (u > 1 && std::abs(u-1) > kEpsilon)) if ((u < 0 && std::abs(u) > kEpsilon) || (u > 1 && std::abs(u - 1) > kEpsilon))
return ray.end; return ray.end;
const auto q = t.Cross(sideA); const auto q = t.Cross(sideA);
@@ -59,4 +59,4 @@ namespace omath::collision
return ray.start + rayDir * tHit; return ray.start + rayDir * tHit;
} }
} } // namespace omath::collision

View File

@@ -1 +1,2 @@
add_subdirectory(Source) add_subdirectory(Source)
add_subdirectory(OpenGL)

View File

@@ -0,0 +1 @@
target_sources(omath PRIVATE Camera.cpp)

View File

@@ -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<float>& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort,
const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, const float near, const float far) :
projection::Camera<Mat4x4, ViewAngles>(position, viewAngles, viewPort, fov, near, far)
{
}
void Camera::LookAt([[maybe_unused]] const Vector3<float>& 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

View File

@@ -8,12 +8,12 @@
namespace omath::source namespace omath::source
{ {
Camera::Camera(const Vector3& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort, Camera::Camera(const Vector3<float>& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort,
const projection::FieldOfView& fov, const float near, const float far) : const projection::FieldOfView& fov, const float near, const float far) :
projection::Camera<Mat4x4, ViewAngles>(position, viewAngles, viewPort, fov, near, far) projection::Camera<Mat4x4, ViewAngles>(position, viewAngles, viewPort, fov, near, far)
{ {
} }
void Camera::LookAt(const Vector3& target) void Camera::LookAt(const Vector3<float>& target)
{ {
const float distance = m_origin.DistTo(target); const float distance = m_origin.DistTo(target);
const auto delta = target - m_origin; const auto delta = target - m_origin;
@@ -24,13 +24,14 @@ namespace omath::source
m_viewAngles.roll = RollAngle::FromRadians(0.f); 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 } // namespace omath::source

View File

@@ -3,60 +3,98 @@
// //
#include "omath/pathfinding/Astar.hpp" #include "omath/pathfinding/Astar.hpp"
#include <algorithm>
#include <optional> #include <optional>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <algorithm>
namespace omath::pathfinding namespace omath::pathfinding
{ {
struct PathNode final struct PathNode final
{ {
std::optional<Vector3> cameFrom; std::optional<Vector3<float>> cameFrom;
float gCost = 0.f; float gCost = 0.f;
}; };
std::vector<Vector3> Astar::FindPath(const Vector3 &start, const Vector3 &end, const NavigationMesh &navMesh) std::vector<Vector3<float>> Astar::ReconstructFinalPath(const std::unordered_map<Vector3<float>, PathNode>& closedList,
const Vector3<float>& current)
{ {
std::unordered_map<Vector3, PathNode> closedList; std::vector<Vector3<float>> path;
std::unordered_map<Vector3, PathNode> openList; std::optional currentOpt = current;
while (currentOpt)
{
path.push_back(*currentOpt);
auto it = closedList.find(*currentOpt);
if (it == closedList.end())
break;
currentOpt = it->second.cameFrom;
}
std::ranges::reverse(path);
return path;
}
auto Astar::GetPerfectNode(const std::unordered_map<Vector3<float>, PathNode>& openList, const Vector3<float>& endVertex)
{
return std::ranges::min_element(openList,
[&endVertex](const auto& a, const auto& b)
{
const float fA = a.second.gCost + a.first.DistTo(endVertex);
const float fB = b.second.gCost + b.first.DistTo(endVertex);
return fA < fB;
});
}
std::vector<Vector3<float>> Astar::FindPath(const Vector3<float>& start, const Vector3<float>& end, const NavigationMesh& navMesh)
{
std::unordered_map<Vector3<float>, PathNode> closedList;
std::unordered_map<Vector3<float>, PathNode> openList;
auto maybeStartVertex = navMesh.GetClosestVertex(start);
auto maybeEndVertex = navMesh.GetClosestVertex(end);
if (!maybeStartVertex || !maybeEndVertex)
return {};
const auto startVertex = maybeStartVertex.value();
const auto endVertex = maybeEndVertex.value();
const auto startVertex = navMesh.GetClosestVertex(start).value();
const auto endVertex = navMesh.GetClosestVertex(end).value();
openList.emplace(startVertex, PathNode{std::nullopt, 0.f}); openList.emplace(startVertex, PathNode{std::nullopt, 0.f});
while (!openList.empty()) while (!openList.empty())
{ {
const auto perfectVertex = *std::ranges::min_element(openList, auto currentIt = GetPerfectNode(openList, endVertex);
[&endVertex](const auto& a, const auto& b) -> bool
{
const auto aCost = a.second.gCost + a.first.DistTo(endVertex);
const auto bCost = b.second.gCost + b.first.DistTo(endVertex);
return aCost < bCost;
});
closedList.emplace(perfectVertex); const auto current = currentIt->first;
openList.erase(perfectVertex.first); const auto currentNode = currentIt->second;
for (const auto& neighbor : navMesh.GetNeighbors(perfectVertex.first)) if (current == endVertex)
if (!closedList.contains(neighbor)) return ReconstructFinalPath(closedList, current);
openList.emplace(neighbor, PathNode{perfectVertex.first, neighbor.DistTo(perfectVertex.first) + perfectVertex.second.gCost});
if (perfectVertex.first != endVertex) closedList.emplace(current, currentNode);
continue; openList.erase(currentIt);
std::vector<Vector3> path = {}; for (const auto& neighbor: navMesh.GetNeighbors(current))
{
if (closedList.contains(neighbor))
continue;
for (std::optional current = perfectVertex.first; current; current = closedList.at(*current).cameFrom ) const float tentativeGCost = currentNode.gCost + neighbor.DistTo(current);
path.push_back(current.value());
return path; const auto openIt = openList.find(neighbor);
if (openIt == openList.end() || tentativeGCost < openIt->second.gCost)
openList[neighbor] = PathNode{current, tentativeGCost};
}
} }
return {}; return {};
} }
} } // namespace omath::pathfinding

View File

@@ -7,7 +7,7 @@
#include <algorithm> #include <algorithm>
namespace omath::pathfinding namespace omath::pathfinding
{ {
std::expected<Vector3, std::string> NavigationMesh::GetClosestVertex(const Vector3 &point) const std::expected<Vector3<float>, std::string> NavigationMesh::GetClosestVertex(const Vector3<float> &point) const
{ {
const auto res = std::ranges::min_element(m_verTextMap, const auto res = std::ranges::min_element(m_verTextMap,
[&point](const auto& a, const auto& b) [&point](const auto& a, const auto& b)
@@ -21,7 +21,7 @@ namespace omath::pathfinding
return res->first; return res->first;
} }
const std::vector<Vector3>& NavigationMesh::GetNeighbors(const Vector3 &vertex) const const std::vector<Vector3<float>>& NavigationMesh::GetNeighbors(const Vector3<float> &vertex) const
{ {
return m_verTextMap.at(vertex); return m_verTextMap.at(vertex);
} }
@@ -73,18 +73,18 @@ namespace omath::pathfinding
while (offset < raw.size()) while (offset < raw.size())
{ {
Vector3 vertex; Vector3<float> vertex;
loadFromVector(raw, offset, vertex); loadFromVector(raw, offset, vertex);
uint16_t neighborsCount; uint16_t neighborsCount;
loadFromVector(raw, offset, neighborsCount); loadFromVector(raw, offset, neighborsCount);
std::vector<Vector3> neighbors; std::vector<Vector3<float>> neighbors;
neighbors.reserve(neighborsCount); neighbors.reserve(neighborsCount);
for (size_t i = 0; i < neighborsCount; ++i) for (size_t i = 0; i < neighborsCount; ++i)
{ {
Vector3 neighbor; Vector3<float> neighbor;
loadFromVector(raw, offset, neighbor); loadFromVector(raw, offset, neighbor);
neighbors.push_back(neighbor); neighbors.push_back(neighbor);
} }

View File

@@ -1 +0,0 @@
target_sources(omath PRIVATE Engine.cpp Projectile.cpp Target.cpp)

View File

@@ -0,0 +1 @@
target_sources(omath PRIVATE ProjPredEngineLegacy.cpp Projectile.cpp Target.cpp ProjPredEngineAVX2.cpp ProjPredEngine.cpp)

View File

@@ -0,0 +1,10 @@
//
// Created by Vlad on 2/23/2025.
//
#include "omath/projectile_prediction/ProjPredEngine.hpp"
namespace omath::projectile_prediction
{
} // namespace omath::projectile_prediction

View File

@@ -0,0 +1,145 @@
//
// Created by Vlad on 2/23/2025.
//
#include "omath/projectile_prediction/ProjPredEngineAVX2.hpp"
#include "source_location"
namespace omath::projectile_prediction
{
std::optional<Vector3<float>>
ProjPredEngineAVX2::MaybeCalculateAimPoint([[maybe_unused]] const Projectile& projectile,
[[maybe_unused]] const Target& target) const
{
#ifdef OMATH_USE_AVX2
const float bulletGravity = m_gravityConstant * projectile.m_gravityScale;
const float v0 = projectile.m_launchSpeed;
const float v0Sqr = v0 * v0;
const Vector3 projOrigin = projectile.m_origin;
constexpr int SIMD_FACTOR = 8;
float currentTime = m_simulationTimeStep;
for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep * SIMD_FACTOR)
{
const __m256 times =
_mm256_setr_ps(currentTime, currentTime + m_simulationTimeStep,
currentTime + m_simulationTimeStep * 2, currentTime + m_simulationTimeStep * 3,
currentTime + m_simulationTimeStep * 4, currentTime + m_simulationTimeStep * 5,
currentTime + m_simulationTimeStep * 6, currentTime + m_simulationTimeStep * 7);
const __m256 targetX =
_mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.x), times, _mm256_set1_ps(target.m_origin.x));
const __m256 targetY =
_mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.y), times, _mm256_set1_ps(target.m_origin.y));
const __m256 timesSq = _mm256_mul_ps(times, times);
const __m256 targetZ = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.z), times,
_mm256_fnmadd_ps(_mm256_set1_ps(0.5f * m_gravityConstant), timesSq,
_mm256_set1_ps(target.m_origin.z)));
const __m256 deltaX = _mm256_sub_ps(targetX, _mm256_set1_ps(projOrigin.x));
const __m256 deltaY = _mm256_sub_ps(targetY, _mm256_set1_ps(projOrigin.y));
const __m256 deltaZ = _mm256_sub_ps(targetZ, _mm256_set1_ps(projOrigin.z));
const __m256 dSqr = _mm256_add_ps(_mm256_mul_ps(deltaX, deltaX), _mm256_mul_ps(deltaY, deltaY));
const __m256 bgTimesSq = _mm256_mul_ps(_mm256_set1_ps(bulletGravity), timesSq);
const __m256 term = _mm256_add_ps(deltaZ, _mm256_mul_ps(_mm256_set1_ps(0.5f), bgTimesSq));
const __m256 termSq = _mm256_mul_ps(term, term);
const __m256 numerator = _mm256_add_ps(dSqr, termSq);
const __m256 denominator = _mm256_add_ps(timesSq, _mm256_set1_ps(1e-8f)); // Avoid division by zero
const __m256 requiredV0Sqr = _mm256_div_ps(numerator, denominator);
const __m256 v0SqrVec = _mm256_set1_ps(v0Sqr + 1e-3f);
const __m256 mask = _mm256_cmp_ps(requiredV0Sqr, v0SqrVec, _CMP_LE_OQ);
const unsigned validMask = _mm256_movemask_ps(mask);
if (!validMask)
continue;
alignas(32) float validTimes[SIMD_FACTOR];
_mm256_store_ps(validTimes, times);
for (int i = 0; i < SIMD_FACTOR; ++i)
{
if (!(validMask & (1 << i)))
continue;
const float candidateTime = validTimes[i];
if (candidateTime > m_maximumSimulationTime)
continue;
// Fine search around candidate time
for (float fineTime = candidateTime - m_simulationTimeStep * 2;
fineTime <= candidateTime + m_simulationTimeStep * 2; fineTime += m_simulationTimeStep)
{
if (fineTime < 0)
continue;
const Vector3 targetPos = target.PredictPosition(fineTime, m_gravityConstant);
const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, fineTime);
if (!pitch)
continue;
const Vector3 delta = targetPos - projOrigin;
const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y);
const float height = d * std::tan(angles::DegreesToRadians(*pitch));
return Vector3(targetPos.x, targetPos.y, projOrigin.z + height);
}
}
}
// Fallback scalar processing for remaining times
for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep)
{
const Vector3 targetPos = target.PredictPosition(currentTime, m_gravityConstant);
const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, currentTime);
if (!pitch)
continue;
const Vector3 delta = targetPos - projOrigin;
const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y);
const float height = d * std::tan(angles::DegreesToRadians(*pitch));
return Vector3(targetPos.x, targetPos.y, projOrigin.z + height);
}
return std::nullopt;
}
ProjPredEngineAVX2::ProjPredEngineAVX2(const float gravityConstant, const float simulationTimeStep,
const float maximumSimulationTime) :
m_gravityConstant(gravityConstant), m_simulationTimeStep(maximumSimulationTime),
m_maximumSimulationTime(simulationTimeStep)
{
}
std::optional<float> ProjPredEngineAVX2::CalculatePitch(const Vector3<float>& projOrigin,
const Vector3<float>& targetPos, const float bulletGravity,
const float v0, const float time)
{
if (time <= 0.0f)
return std::nullopt;
const Vector3 delta = targetPos - projOrigin;
const float dSqr = delta.x * delta.x + delta.y * delta.y;
const float h = delta.z;
const float term = h + 0.5f * bulletGravity * time * time;
const float requiredV0Sqr = (dSqr + term * term) / (time * time);
const float v0Sqr = v0 * v0;
if (requiredV0Sqr > v0Sqr + 1e-3f)
return std::nullopt;
if (dSqr == 0.0f)
return term >= 0.0f ? 90.0f : -90.0f;
const float d = std::sqrt(dSqr);
const float tanTheta = term / d;
return angles::RadiansToDegrees(std::atan(tanTheta));
#else
throw std::runtime_error(
std::format("{} AVX2 feature is not enabled!", std::source_location::current().function_name()));
#endif
}
} // namespace omath::projectile_prediction

View File

@@ -1,25 +1,18 @@
// #include "omath/projectile_prediction/ProjPredEngineLegacy.hpp"
// Created by Vlad on 6/9/2024.
//
#include "omath/prediction/Engine.hpp"
#include <cmath> #include <cmath>
#include <omath/Angles.hpp> #include <omath/Angles.hpp>
namespace omath::projectile_prediction
namespace omath::prediction
{ {
Engine::Engine(const float gravityConstant, const float simulationTimeStep, ProjPredEngineLegacy::ProjPredEngineLegacy(const float gravityConstant, const float simulationTimeStep,
const float maximumSimulationTime, const float distanceTolerance) const float maximumSimulationTime, const float distanceTolerance) :
: m_gravityConstant(gravityConstant), m_gravityConstant(gravityConstant), m_simulationTimeStep(simulationTimeStep),
m_simulationTimeStep(simulationTimeStep), m_maximumSimulationTime(maximumSimulationTime), m_distanceTolerance(distanceTolerance)
m_maximumSimulationTime(maximumSimulationTime),
m_distanceTolerance(distanceTolerance)
{ {
} }
std::optional<Vector3> Engine::MaybeCalculateAimPoint(const Projectile &projectile, const Target &target) const std::optional<Vector3<float>> ProjPredEngineLegacy::MaybeCalculateAimPoint(const Projectile& projectile,
const Target& target) const
{ {
for (float time = 0.f; time < m_maximumSimulationTime; time += m_simulationTimeStep) for (float time = 0.f; time < m_maximumSimulationTime; time += m_simulationTimeStep)
{ {
@@ -28,7 +21,7 @@ namespace omath::prediction
const auto projectilePitch = MaybeCalculateProjectileLaunchPitchAngle(projectile, predictedTargetPosition); const auto projectilePitch = MaybeCalculateProjectileLaunchPitchAngle(projectile, predictedTargetPosition);
if (!projectilePitch.has_value()) [[unlikely]] if (!projectilePitch.has_value()) [[unlikely]]
continue; continue;
if (!IsProjectileReachedTarget(predictedTargetPosition, projectile, projectilePitch.value(), time)) if (!IsProjectileReachedTarget(predictedTargetPosition, projectile, projectilePitch.value(), time))
continue; continue;
@@ -41,8 +34,9 @@ namespace omath::prediction
return std::nullopt; return std::nullopt;
} }
std::optional<float> Engine::MaybeCalculateProjectileLaunchPitchAngle(const Projectile &projectile, std::optional<float>
const Vector3 &targetPosition) const ProjPredEngineLegacy::MaybeCalculateProjectileLaunchPitchAngle(const Projectile& projectile,
const Vector3<float>& targetPosition) const
{ {
const auto bulletGravity = m_gravityConstant * projectile.m_gravityScale; const auto bulletGravity = m_gravityConstant * projectile.m_gravityScale;
const auto delta = targetPosition - projectile.m_origin; const auto delta = targetPosition - projectile.m_origin;
@@ -51,11 +45,11 @@ namespace omath::prediction
const auto distance2dSqr = distance2d * distance2d; const auto distance2dSqr = distance2d * distance2d;
const auto launchSpeedSqr = projectile.m_launchSpeed * projectile.m_launchSpeed; const auto launchSpeedSqr = projectile.m_launchSpeed * projectile.m_launchSpeed;
float root = launchSpeedSqr * launchSpeedSqr - bulletGravity * (bulletGravity * float root = launchSpeedSqr * launchSpeedSqr -
distance2dSqr + 2.0f * delta.z * launchSpeedSqr); bulletGravity * (bulletGravity * distance2dSqr + 2.0f * delta.z * launchSpeedSqr);
if (root < 0.0f) [[unlikely]] if (root < 0.0f) [[unlikely]]
return std::nullopt; return std::nullopt;
root = std::sqrt(root); root = std::sqrt(root);
const float angle = std::atan((launchSpeedSqr - root) / (bulletGravity * distance2d)); const float angle = std::atan((launchSpeedSqr - root) / (bulletGravity * distance2d));
@@ -63,12 +57,12 @@ namespace omath::prediction
return angles::RadiansToDegrees(angle); return angles::RadiansToDegrees(angle);
} }
bool Engine::IsProjectileReachedTarget(const Vector3 &targetPosition, const Projectile &projectile, bool ProjPredEngineLegacy::IsProjectileReachedTarget(const Vector3<float>& targetPosition, const Projectile& projectile,
const float pitch, const float time) const const float pitch, const float time) const
{ {
const auto yaw = projectile.m_origin.ViewAngleTo(targetPosition).y; const auto yaw = projectile.m_origin.ViewAngleTo(targetPosition).y;
const auto projectilePosition = projectile.PredictPosition(pitch, yaw, time, m_gravityConstant); const auto projectilePosition = projectile.PredictPosition(pitch, yaw, time, m_gravityConstant);
return projectilePosition.DistTo(targetPosition) <= m_distanceTolerance; return projectilePosition.DistTo(targetPosition) <= m_distanceTolerance;
} }
} } // namespace omath::projectile_prediction

View File

@@ -2,13 +2,13 @@
// Created by Vlad on 6/9/2024. // Created by Vlad on 6/9/2024.
// //
#include "omath/prediction/Projectile.hpp" #include "omath/projectile_prediction/Projectile.hpp"
#include <cmath>
#include <omath/engines/Source/Formulas.hpp> #include <omath/engines/Source/Formulas.hpp>
namespace omath::prediction namespace omath::projectile_prediction
{ {
Vector3 Projectile::PredictPosition(const float pitch, const float yaw, const float time, const float gravity) const Vector3<float> Projectile::PredictPosition(const float pitch, const float yaw, const float time, const float gravity) const
{ {
auto currentPos = m_origin + source::ForwardVector({source::PitchAngle::FromDegrees(-pitch), auto currentPos = m_origin + source::ForwardVector({source::PitchAngle::FromDegrees(-pitch),
source::YawAngle::FromDegrees(yaw), source::YawAngle::FromDegrees(yaw),

View File

@@ -2,7 +2,7 @@
// Created by Vlad on 6/9/2024. // Created by Vlad on 6/9/2024.
// //
#include "omath/prediction/Target.hpp" #include "omath/projectile_prediction/Projectile.hpp"
namespace omath::prediction namespace omath::prediction

View File

@@ -1,7 +1,6 @@
enable_testing() enable_testing()
project(unit-tests) project(unit-tests)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}")
include(GoogleTest) include(GoogleTest)
add_executable(unit-tests add_executable(unit-tests
@@ -18,6 +17,7 @@ add_executable(unit-tests
general/UnitTestAngles.cpp general/UnitTestAngles.cpp
general/UnitTestViewAngles.cpp general/UnitTestViewAngles.cpp
general/UnitTestAngle.cpp general/UnitTestAngle.cpp
general/UnitTestTriangle.cpp
engines/UnitTestOpenGL.cpp engines/UnitTestOpenGL.cpp
engines/UnitTestUnityEngine.cpp engines/UnitTestUnityEngine.cpp
@@ -25,6 +25,15 @@ add_executable(unit-tests
) )
set_target_properties(unit-tests PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
UNITY_BUILD ON
UNITY_BUILD_BATCH_SIZE 20
CXX_STANDARD 23
CXX_STANDARD_REQUIRED ON)
target_link_libraries(unit-tests PRIVATE gtest gtest_main omath) target_link_libraries(unit-tests PRIVATE gtest gtest_main omath)
gtest_discover_tests(unit-tests) gtest_discover_tests(unit-tests)

View File

@@ -1,37 +1,69 @@
// //
// Created by Orange on 11/23/2024. // Created by Orange on 11/23/2024.
// //
#include <complex>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <omath/Matrix.hpp> #include <omath/engines/OpenGL/Camera.hpp>
#include <print> #include <omath/engines/OpenGL/Constants.hpp>
#include <omath/engines/OpenGL/Formulas.hpp>
// #include <glm/glm.hpp>
// #include "glm/ext/matrix_clip_space.hpp"
// #include "glm/ext/matrix_transform.hpp"
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); EXPECT_EQ(forward, omath::opengl::kAbsForward);
// 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); TEST(UnitTestOpenGL, RightVector)
{
const auto right = omath::opengl::RightVector({});
glm::vec4 ndc_glm2 = proj_glm * glm::vec4(300.f, 0.f, -1000.f, 1.f);
ndc_glm2 /= ndc_glm2.w; EXPECT_EQ(right, omath::opengl::kAbsRight);
const omath::Mat<4, 1, float, omath::MatStoreType::COLUMN_MAJOR> cords_omath = }
{
{0}, TEST(UnitTestOpenGL, UpVector)
{0}, {
{-0.2f}, const auto up = omath::opengl::UpVector({});
{1} EXPECT_EQ(up, omath::opengl::kAbsUp);
}; }
//auto ndc_omath = proj_omath * cords_omath; TEST(UnitTestOpenGL, ProjectTargetMovedFromCamera)
// ndc_omath /= ndc_omath.At(3, 0); {
*/ 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);
for (float distance = -10.f; distance > -1000.f; distance -= 0.01f)
{
const auto projected = cam.WorldToScreen({0, 0, distance});
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<float>{});
cam.SetFieldOfView(omath::projection::FieldOfView::FromDegrees(50.f));
EXPECT_EQ(cam.GetFieldOfView().AsDegrees(), 50.f);
} }

View File

@@ -3,7 +3,7 @@
// //
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <omath/engines/Source/Camera.hpp> #include <omath/engines/Source/Camera.hpp>
#include <omath/engines/Source/Constants.h> #include <omath/engines/Source/Constants.hpp>
#include <omath/engines/Source/Formulas.hpp> #include <omath/engines/Source/Formulas.hpp>
@@ -27,17 +27,15 @@ TEST(UnitTestSourceEngine, UpVector)
EXPECT_EQ(up, omath::source::kAbsUp); EXPECT_EQ(up, omath::source::kAbsUp);
} }
TEST(UnitTestSourceEngine, PerpectiveProjectionAtCenter) TEST(UnitTestSourceEngine, ProjectTargetMovedFromCamera)
{ {
constexpr auto fov = omath::projection::FieldOfView::FromDegrees(90.f); 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); 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) 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()); EXPECT_TRUE(projected.has_value());
@@ -48,3 +46,24 @@ TEST(UnitTestSourceEngine, PerpectiveProjectionAtCenter)
EXPECT_NEAR(projected->y, 540, 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<float>{});
cam.SetFieldOfView(omath::projection::FieldOfView::FromDegrees(50.f));
EXPECT_EQ(cam.GetFieldOfView().AsDegrees(), 50.f);
}

View File

@@ -1,6 +1,6 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "omath/collision/LineTracer.hpp" #include "omath/collision/LineTracer.hpp"
#include "omath/Triangle3d.hpp" #include "omath/Triangle.hpp"
#include "omath/Vector3.hpp" #include "omath/Vector3.hpp"
using namespace omath; using namespace omath;
@@ -10,10 +10,10 @@ class LineTracerTest : public ::testing::Test
{ {
protected: protected:
// Set up common variables for use in each test // Set up common variables for use in each test
Vector3 vertex1{0.0f, 0.0f, 0.0f}; Vector3<float> vertex1{0.0f, 0.0f, 0.0f};
Vector3 vertex2{1.0f, 0.0f, 0.0f}; Vector3<float> vertex2{1.0f, 0.0f, 0.0f};
Vector3 vertex3{0.0f, 1.0f, 0.0f}; Vector3<float> vertex3{0.0f, 1.0f, 0.0f};
Triangle3d triangle{vertex1, vertex2, vertex3}; Triangle<Vector3<float>> triangle{vertex1, vertex2, vertex3};
}; };
// Test that a ray intersecting the triangle returns false for CanTraceLine // Test that a ray intersecting the triangle returns false for CanTraceLine
@@ -71,7 +71,7 @@ TEST_F(LineTracerTest, TriangleFarBeyondRayEndPoint)
constexpr Ray ray{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}; constexpr Ray ray{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}};
// Define a triangle far beyond the ray's endpoint // Define a triangle far beyond the ray's endpoint
const Triangle3d distantTriangle{ constexpr Triangle<Vector3<float>> distantTriangle{
{1000.0f, 1000.0f, 1000.0f}, {1001.0f, 1000.0f, 1000.0f}, {1000.0f, 1001.0f, 1000.0f} {1000.0f, 1000.0f, 1000.0f}, {1001.0f, 1000.0f, 1000.0f}, {1000.0f, 1001.0f, 1000.0f}
}; };

View File

@@ -40,6 +40,14 @@ TEST_F(UnitTestMat, Constructor_InitializerList)
EXPECT_FLOAT_EQ(m.At(1, 1), 4.0f); EXPECT_FLOAT_EQ(m.At(1, 1), 4.0f);
} }
TEST_F(UnitTestMat, Operator_SquareBrackets)
{
EXPECT_EQ((m2[0, 0]), 1.0f);
EXPECT_EQ((m2[0, 1]), 2.0f);
EXPECT_EQ((m2[1, 0]), 3.0f);
EXPECT_EQ((m2[1, 1]), 4.0f);
}
TEST_F(UnitTestMat, Constructor_Copy) TEST_F(UnitTestMat, Constructor_Copy)
{ {
Mat<2, 2> m3 = m2; Mat<2, 2> m3 = m2;

View File

@@ -29,6 +29,15 @@ TEST_F(UnitTestMatrix, Constructor_Size)
EXPECT_EQ(m.ColumnsCount(), 3); EXPECT_EQ(m.ColumnsCount(), 3);
} }
TEST_F(UnitTestMatrix, Operator_SquareBrackets)
{
EXPECT_EQ((m2[0, 0]), 1.0f);
EXPECT_EQ((m2[0, 1]), 2.0f);
EXPECT_EQ((m2[1, 0]), 3.0f);
EXPECT_EQ((m2[1, 1]), 4.0f);
}
TEST_F(UnitTestMatrix, Constructor_InitializerList) TEST_F(UnitTestMatrix, Constructor_InitializerList)
{ {
Matrix m{{1.0f, 2.0f}, {3.0f, 4.0f}}; Matrix m{{1.0f, 2.0f}, {3.0f, 4.0f}};

View File

@@ -1,15 +1,17 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <omath/prediction/Engine.hpp> #include <omath/projectile_prediction/ProjPredEngineLegacy.hpp>
TEST(UnitTestPrediction, PredictionTest) TEST(UnitTestPrediction, PredictionTest)
{ {
constexpr omath::prediction::Target target{ constexpr omath::projectile_prediction::Target target{
.m_origin = {100, 0, 90}, .m_velocity = {0, 0, 0}, .m_isAirborne = false}; .m_origin = {100, 0, 90}, .m_velocity = {0, 0, 0}, .m_isAirborne = false};
constexpr omath::prediction::Projectile proj = {.m_origin = {3,2,1}, .m_launchSpeed = 5000, .m_gravityScale= 0.4}; constexpr omath::projectile_prediction::Projectile proj = {
const auto viewPoint = omath::prediction::Engine(400, 1.f / 1000.f, 50, 5.f).MaybeCalculateAimPoint(proj, target); .m_origin = {3, 2, 1}, .m_launchSpeed = 5000, .m_gravityScale = 0.4};
const auto viewPoint =
omath::projectile_prediction::ProjPredEngineLegacy(400, 1.f / 1000.f, 50, 5.f).MaybeCalculateAimPoint(proj, target);
const auto [pitch, yaw, _] = proj.m_origin.ViewAngleTo(viewPoint.value()).AsTuple(); const auto [pitch, yaw, _] = proj.m_origin.ViewAngleTo(viewPoint.value()).AsTuple();
EXPECT_NEAR(42.547142, pitch, 0.0001f); EXPECT_NEAR(42.547142, pitch, 0.01f);
EXPECT_NEAR(-1.181189, yaw, 0.0001f); EXPECT_NEAR(-1.181189, yaw, 0.01f);
} }

View File

@@ -10,9 +10,9 @@
TEST(UnitTestProjection, Projection) TEST(UnitTestProjection, Projection)
{ {
auto x = omath::Angle<float, 0.f, 180.f, omath::AngleFlags::Clamped>::FromDegrees(90.f); const auto x = omath::Angle<float, 0.f, 180.f, omath::AngleFlags::Clamped>::FromDegrees(90.f);
auto cam = omath::source::Camera({0, 0, 0}, omath::source::ViewAngles{}, {1920.f, 1080.f}, x, 0.01f, 1000.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); std::print("{} {} {}", projected->x, projected->y, projected->z);
} }

View File

@@ -0,0 +1,132 @@
//
// Created by Orange on 1/6/2025.
//
#include "omath/Triangle.hpp"
#include <gtest/gtest.h>
#include <omath/Vector3.hpp>
#include <cmath> // For std::sqrt, std::isinf, std::isnan
using namespace omath;
class UnitTestTriangle : public ::testing::Test
{
protected:
// Define some Triangles to use in tests
Triangle<Vector3<float>> t1;
Triangle<Vector3<float>> t2;
Triangle<Vector3<float>> t3;
constexpr void SetUp() override
{
// Triangle with vertices (0, 0, 0), (1, 0, 0), (0, 1, 0)
t1 = Triangle<Vector3<float>>(
Vector3(0.0f, 0.0f, 0.0f),
Vector3(1.0f, 0.0f, 0.0f),
Vector3(0.0f, 1.0f, 0.0f)
);
// Triangle with vertices (1, 2, 3), (4, 5, 6), (7, 8, 9)
t2 = Triangle<Vector3<float>>(
Vector3(1.0f, 2.0f, 3.0f),
Vector3(4.0f, 5.0f, 6.0f),
Vector3(7.0f, 8.0f, 9.0f)
);
// An isosceles right triangle
t3 = Triangle<Vector3<float>>(
Vector3(0.0f, 0.0f, 0.0f),
Vector3(2.0f, 0.0f, 0.0f),
Vector3(0.0f, 2.0f, 0.0f)
);
}
};
// Test constructor and vertices
TEST_F(UnitTestTriangle, Constructor)
{
constexpr Triangle<Vector3<float>> t(
Vector3(1.0f, 2.0f, 3.0f),
Vector3(4.0f, 5.0f, 6.0f),
Vector3(7.0f, 8.0f, 9.0f)
);
EXPECT_FLOAT_EQ(t.m_vertex1.x, 1.0f);
EXPECT_FLOAT_EQ(t.m_vertex1.y, 2.0f);
EXPECT_FLOAT_EQ(t.m_vertex1.z, 3.0f);
EXPECT_FLOAT_EQ(t.m_vertex2.x, 4.0f);
EXPECT_FLOAT_EQ(t.m_vertex2.y, 5.0f);
EXPECT_FLOAT_EQ(t.m_vertex2.z, 6.0f);
EXPECT_FLOAT_EQ(t.m_vertex3.x, 7.0f);
EXPECT_FLOAT_EQ(t.m_vertex3.y, 8.0f);
EXPECT_FLOAT_EQ(t.m_vertex3.z, 9.0f);
}
// Test CalculateNormal
TEST_F(UnitTestTriangle, CalculateNormal)
{
// For t1, the normal should point in the +Z direction (0, 0, 1) or (0, 0, -1)
const Vector3 normal_t1 = t1.CalculateNormal();
// Check if it's normalized and pointed along Z (sign can differ, so use absolute check)
EXPECT_NEAR(std::fabs(normal_t1.z), 1.0f, 1e-5f);
EXPECT_NEAR(normal_t1.Length(), 1.0f, 1e-5f);
// For t3, we expect the normal to be along +Z as well
const Vector3 normal_t3 = t3.CalculateNormal();
EXPECT_NEAR(std::fabs(normal_t3.z), 1.0f, 1e-5f);
}
// Test side lengths
TEST_F(UnitTestTriangle, SideLengths)
{
// For t1 side lengths
EXPECT_FLOAT_EQ(t1.SideALength(), std::sqrt(1.0f)); // distance between (0,0,0) and (1,0,0)
EXPECT_FLOAT_EQ(t1.SideBLength(), std::sqrt(1.0f + 1.0f)); // distance between (4,5,6) & (7,8,9)... but we are testing t1, so let's be accurate:
// Actually, for t1: vertex2=(1,0,0), vertex3=(0,1,0)
// Dist between (0,1,0) and (1,0,0) = sqrt((1-0)^2 + (0-1)^2) = sqrt(1 + 1) = sqrt(2)
EXPECT_FLOAT_EQ(t1.SideBLength(), std::sqrt(2.0f));
// For t3, side a = distance between vertex1=(0,0,0) and vertex2=(2,0,0), which is 2
// side b = distance between vertex3=(0,2,0) and vertex2=(2,0,0), which is sqrt(2^2 + (-2)^2)= sqrt(8)= 2.828...
// We'll just check side a first:
EXPECT_FLOAT_EQ(t3.SideALength(), 2.0f);
// Then side b:
EXPECT_FLOAT_EQ(t3.SideBLength(), std::sqrt(8.0f));
}
// Test side vectors
TEST_F(UnitTestTriangle, SideVectors)
{
const Vector3 sideA_t1 = t1.SideAVector(); // m_vertex1 - m_vertex2
EXPECT_FLOAT_EQ(sideA_t1.x, 0.0f - 1.0f);
EXPECT_FLOAT_EQ(sideA_t1.y, 0.0f - 0.0f);
EXPECT_FLOAT_EQ(sideA_t1.z, 0.0f - 0.0f);
const Vector3 sideB_t1 = t1.SideBVector(); // m_vertex3 - m_vertex2
EXPECT_FLOAT_EQ(sideB_t1.x, 0.0f - 1.0f);
EXPECT_FLOAT_EQ(sideB_t1.y, 1.0f - 0.0f);
EXPECT_FLOAT_EQ(sideB_t1.z, 0.0f - 0.0f);
}
TEST_F(UnitTestTriangle, IsRectangular)
{
EXPECT_TRUE(Triangle<Vector3<float>>({2,0,0}, {}, {0,2,0}).IsRectangular());
}
// Test midpoint
TEST_F(UnitTestTriangle, MidPoint)
{
// For t1, midpoint of (0,0,0), (1,0,0), (0,1,0)
const Vector3 mid1 = t1.MidPoint();
EXPECT_FLOAT_EQ(mid1.x, (0.0f + 1.0f + 0.0f) / 3.0f);
EXPECT_FLOAT_EQ(mid1.y, (0.0f + 0.0f + 1.0f) / 3.0f);
EXPECT_FLOAT_EQ(mid1.z, 0.0f);
// For t2, midpoint of (1,2,3), (4,5,6), (7,8,9)
const Vector3 mid2 = t2.MidPoint();
EXPECT_FLOAT_EQ(mid2.x, (1.0f + 4.0f + 7.0f) / 3.0f);
EXPECT_FLOAT_EQ(mid2.y, (2.0f + 5.0f + 8.0f) / 3.0f);
EXPECT_FLOAT_EQ(mid2.z, (3.0f + 6.0f + 9.0f) / 3.0f);
}

View File

@@ -12,8 +12,8 @@ using namespace omath;
class UnitTestVector2 : public ::testing::Test class UnitTestVector2 : public ::testing::Test
{ {
protected: protected:
Vector2 v1; Vector2<float> v1;
Vector2 v2; Vector2<float> v2;
constexpr void SetUp() override constexpr void SetUp() override
{ {
@@ -25,7 +25,7 @@ protected:
// Test constructor and default values // Test constructor and default values
TEST_F(UnitTestVector2, Constructor_Default) TEST_F(UnitTestVector2, Constructor_Default)
{ {
constexpr Vector2 v; constexpr Vector2<float> v;
EXPECT_FLOAT_EQ(v.x, 0.0f); EXPECT_FLOAT_EQ(v.x, 0.0f);
EXPECT_FLOAT_EQ(v.y, 0.0f); EXPECT_FLOAT_EQ(v.y, 0.0f);
} }

View File

@@ -13,8 +13,8 @@ using namespace omath;
class UnitTestVector3 : public ::testing::Test class UnitTestVector3 : public ::testing::Test
{ {
protected: protected:
Vector3 v1; Vector3<float> v1;
Vector3 v2; Vector3<float> v2;
void SetUp() override void SetUp() override
{ {
@@ -26,7 +26,7 @@ protected:
// Test constructor and default values // Test constructor and default values
TEST_F(UnitTestVector3, Constructor_Default) TEST_F(UnitTestVector3, Constructor_Default)
{ {
constexpr Vector3 v; constexpr Vector3<float> v;
EXPECT_FLOAT_EQ(v.x, 0.0f); EXPECT_FLOAT_EQ(v.x, 0.0f);
EXPECT_FLOAT_EQ(v.y, 0.0f); EXPECT_FLOAT_EQ(v.y, 0.0f);
EXPECT_FLOAT_EQ(v.z, 0.0f); EXPECT_FLOAT_EQ(v.z, 0.0f);
@@ -34,7 +34,7 @@ TEST_F(UnitTestVector3, Constructor_Default)
TEST_F(UnitTestVector3, Constructor_Values) TEST_F(UnitTestVector3, Constructor_Values)
{ {
constexpr Vector3 v(1.0f, 2.0f, 3.0f); constexpr Vector3<float> v(1.0f, 2.0f, 3.0f);
EXPECT_FLOAT_EQ(v.x, 1.0f); EXPECT_FLOAT_EQ(v.x, 1.0f);
EXPECT_FLOAT_EQ(v.y, 2.0f); EXPECT_FLOAT_EQ(v.y, 2.0f);
EXPECT_FLOAT_EQ(v.z, 3.0f); EXPECT_FLOAT_EQ(v.z, 3.0f);
@@ -387,6 +387,21 @@ TEST_F(UnitTestVector3, AsTuple)
EXPECT_FLOAT_EQ(std::get<2>(tuple), v1.z); EXPECT_FLOAT_EQ(std::get<2>(tuple), v1.z);
} }
// Test AsTuple method
TEST_F(UnitTestVector3, AngleBeatween)
{
EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).AngleBetween({1, 0 ,0}).value().AsDegrees(), 90.0f);
EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).AngleBetween({0.0f, 0.0f, 1.0f}).value().AsDegrees(), 0.0f);
EXPECT_FALSE(Vector3(0.0f, 0.0f, 0.0f).AngleBetween({0.0f, 0.0f, 1.0f}).has_value());
}
TEST_F(UnitTestVector3, IsPerpendicular)
{
EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).IsPerpendicular({1, 0 ,0}), true);
EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).IsPerpendicular({0.0f, 0.0f, 1.0f}), false);
EXPECT_FALSE(Vector3(0.0f, 0.0f, 0.0f).IsPerpendicular({0.0f, 0.0f, 1.0f}));
}
// Static assertions (compile-time checks) // Static assertions (compile-time checks)
static_assert(Vector3(1.0f, 2.0f, 3.0f).LengthSqr() == 14.0f, "LengthSqr should be 14"); static_assert(Vector3(1.0f, 2.0f, 3.0f).LengthSqr() == 14.0f, "LengthSqr should be 14");
static_assert(Vector3(1.0f, 2.0f, 3.0f).Dot(Vector3(4.0f, 5.0f, 6.0f)) == 32.0f, "Dot product should be 32"); static_assert(Vector3(1.0f, 2.0f, 3.0f).Dot(Vector3(4.0f, 5.0f, 6.0f)) == 32.0f, "Dot product should be 32");

View File

@@ -14,8 +14,8 @@ using namespace omath;
class UnitTestVector4 : public ::testing::Test class UnitTestVector4 : public ::testing::Test
{ {
protected: protected:
Vector4 v1; Vector4<float> v1;
Vector4 v2; Vector4<float> v2;
void SetUp() override void SetUp() override
{ {
@@ -27,7 +27,7 @@ protected:
// Test constructor and default values // Test constructor and default values
TEST_F(UnitTestVector4, Constructor_Default) TEST_F(UnitTestVector4, Constructor_Default)
{ {
constexpr Vector4 v; constexpr Vector4<float> v;
EXPECT_FLOAT_EQ(v.x, 0.0f); EXPECT_FLOAT_EQ(v.x, 0.0f);
EXPECT_FLOAT_EQ(v.y, 0.0f); EXPECT_FLOAT_EQ(v.y, 0.0f);
EXPECT_FLOAT_EQ(v.z, 0.0f); EXPECT_FLOAT_EQ(v.z, 0.0f);