Merge pull request #15 from orange-cpp/u/orange-cpp/misc

Global improvement
This commit is contained in:
2024-12-16 20:06:52 +03:00
committed by GitHub
49 changed files with 1050 additions and 372 deletions

62
.clang-format Normal file
View File

@@ -0,0 +1,62 @@
# Generated from CLion C/C++ Code Style settings
---
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: true
AlignTrailingComments: false
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AlwaysBreakTemplateDeclarations: Yes
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: true
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBraces: Custom
BreakConstructorInitializers: AfterColon
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 120
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ContinuationIndentWidth: 8
IncludeCategories:
- Regex: '^<.*'
Priority: 1
- Regex: '^".*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentWidth: 4
InsertNewlineAtEOF: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
PointerAlignment: Left
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
TabWidth: 4
...

147
.clang-tidy Normal file
View File

@@ -0,0 +1,147 @@
# Generated from CLion Inspection settings
---
Checks: '-*,
bugprone-argument-comment,
bugprone-assert-side-effect,
bugprone-bad-signal-to-kill-thread,
bugprone-branch-clone,
bugprone-copy-constructor-init,
bugprone-dangling-handle,
bugprone-dynamic-static-initializers,
bugprone-fold-init-type,
bugprone-forward-declaration-namespace,
bugprone-forwarding-reference-overload,
bugprone-inaccurate-erase,
bugprone-incorrect-roundings,
bugprone-integer-division,
bugprone-lambda-function-name,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-arithmetic-in-alloc,
bugprone-misplaced-widening-cast,
bugprone-move-forwarding-reference,
bugprone-multiple-statement-macro,
bugprone-no-escape,
bugprone-parent-virtual-call,
bugprone-posix-return,
bugprone-reserved-identifier,
bugprone-sizeof-container,
bugprone-sizeof-expression,
bugprone-spuriously-wake-up-functions,
bugprone-string-constructor,
bugprone-string-integer-assignment,
bugprone-string-literal-with-embedded-nul,
bugprone-suspicious-enum-usage,
bugprone-suspicious-include,
bugprone-suspicious-memset-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-semicolon,
bugprone-suspicious-string-compare,
bugprone-suspicious-memory-comparison,
bugprone-suspicious-realloc-usage,
bugprone-swapped-arguments,
bugprone-terminating-continue,
bugprone-throw-keyword-missing,
bugprone-too-small-loop-variable,
bugprone-undefined-memory-manipulation,
bugprone-undelegated-constructor,
bugprone-unhandled-self-assignment,
bugprone-unused-raii,
bugprone-unused-return-value,
bugprone-use-after-move,
bugprone-virtual-near-miss,
cert-dcl21-cpp,
cert-dcl58-cpp,
cert-err34-c,
cert-err52-cpp,
cert-err60-cpp,
cert-flp30-c,
cert-msc50-cpp,
cert-msc51-cpp,
cert-str34-c,
cppcoreguidelines-interfaces-global-init,
cppcoreguidelines-narrowing-conversions,
cppcoreguidelines-pro-type-member-init,
cppcoreguidelines-pro-type-static-cast-downcast,
cppcoreguidelines-slicing,
google-default-arguments,
google-explicit-constructor,
google-runtime-operator,
hicpp-exception-baseclass,
hicpp-multiway-paths-covered,
misc-misplaced-const,
misc-new-delete-overloads,
misc-no-recursion,
misc-non-copyable-objects,
misc-throw-by-value-catch-by-reference,
misc-unconventional-assign-operator,
misc-uniqueptr-reset-release,
modernize-avoid-bind,
modernize-concat-nested-namespaces,
modernize-deprecated-headers,
modernize-deprecated-ios-base-aliases,
modernize-loop-convert,
modernize-make-shared,
modernize-make-unique,
modernize-pass-by-value,
modernize-raw-string-literal,
modernize-redundant-void-arg,
modernize-replace-auto-ptr,
modernize-replace-disallow-copy-and-assign-macro,
modernize-replace-random-shuffle,
modernize-return-braced-init-list,
modernize-shrink-to-fit,
modernize-unary-static-assert,
modernize-use-auto,
modernize-use-bool-literals,
modernize-use-emplace,
modernize-use-equals-default,
modernize-use-equals-delete,
modernize-use-nodiscard,
modernize-use-noexcept,
modernize-use-nullptr,
modernize-use-override,
modernize-use-transparent-functors,
modernize-use-uncaught-exceptions,
mpi-buffer-deref,
mpi-type-mismatch,
openmp-use-default-none,
performance-faster-string-find,
performance-for-range-copy,
performance-implicit-conversion-in-loop,
performance-inefficient-algorithm,
performance-inefficient-string-concatenation,
performance-inefficient-vector-operation,
performance-move-const-arg,
performance-move-constructor-init,
performance-no-automatic-move,
performance-noexcept-move-constructor,
performance-trivially-destructible,
performance-type-promotion-in-math-fn,
performance-unnecessary-copy-initialization,
performance-unnecessary-value-param,
portability-simd-intrinsics,
readability-avoid-const-params-in-decls,
readability-const-return-type,
readability-container-size-empty,
readability-convert-member-functions-to-static,
readability-delete-null-pointer,
readability-deleted-default,
readability-inconsistent-declaration-parameter-name,
readability-make-member-function-const,
readability-misleading-indentation,
readability-misplaced-array-index,
readability-non-const-parameter,
readability-redundant-control-flow,
readability-redundant-declaration,
readability-redundant-function-ptr-dereference,
readability-redundant-smartptr-get,
readability-redundant-string-cstr,
readability-redundant-string-init,
readability-simplify-subscript-expr,
readability-static-accessed-through-instance,
readability-static-definition-in-anonymous-namespace,
readability-string-compare,
readability-uniqueptr-delete-release,
readability-use-anyofallof'

1
.idea/vcs.xml generated
View File

@@ -2,6 +2,7 @@
<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

@@ -6,6 +6,9 @@ project(omath VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 26) 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)
@@ -23,10 +26,10 @@ if(OMATH_BUILD_TESTS)
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
if (WIN32 AND OMATH_THREAT_WARNING_AS_ERROR) if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND OMATH_THREAT_WARNING_AS_ERROR)
target_compile_options(omath PRIVATE /W4 /WX) target_compile_options(omath PRIVATE /W4 /WX)
elseif(UNIX AND OMATH_THREAT_WARNING_AS_ERROR) elseif(OMATH_THREAT_WARNING_AS_ERROR)
target_compile_options(omath PRIVATE -Wall -Wextra -Wpedantic) target_compile_options(omath PRIVATE -Wall -Wextra -Wpedantic -Werror)
endif() endif()
target_include_directories(omath target_include_directories(omath

View File

@@ -1,4 +1,4 @@
## Goal ## 🎯 Goal
My goal is to provide a space where it is safe for everyone to contribute to, My goal is to provide a space where it is safe for everyone to contribute to,
and get support for, open-source software in a respectful and cooperative and get support for, open-source software in a respectful and cooperative
@@ -10,7 +10,7 @@ surrounding community a place for everyone.
As members, contributors, and everyone else who may participate in the As members, contributors, and everyone else who may participate in the
development, I strive to keep the entire experience civil. development, I strive to keep the entire experience civil.
## Standards ## 📜 Standards
Our community standards exist in order to make sure everyone feels comfortable Our community standards exist in order to make sure everyone feels comfortable
contributing to the project(s) together. contributing to the project(s) together.
@@ -27,14 +27,14 @@ Examples of breaking each rule respectively include:
- Posting distasteful imagery, trolling, or posting things unrelated to the topic at hand. - Posting distasteful imagery, trolling, or posting things unrelated to the topic at hand.
- Treating someone as worse because of their lack of understanding of an issue. - Treating someone as worse because of their lack of understanding of an issue.
## Enforcement ## Enforcement
Enforcement of this CoC is done by Orange++ and/or other core contributors. Enforcement of this CoC is done by Orange++ and/or other core contributors.
I, as the core developer, will strive my best to keep this community civil and I, as the core developer, will strive my best to keep this community civil and
following the standards outlined above. following the standards outlined above.
### Reporting incidents ### 🚩 Reporting incidents
If you believe an incident of breaking these standards has occurred, but nobody has If you believe an incident of breaking these standards has occurred, but nobody has
taken appropriate action, you can privately contact the people responsible for dealing taken appropriate action, you can privately contact the people responsible for dealing
@@ -47,10 +47,11 @@ with such incidents in multiple ways:
- `@orange_cpp` - `@orange_cpp`
***Telegram*** ***Telegram***
- `@orange-cpp` - `@orange_cpp`
I guarantee your privacy and will not share those reports with anyone. I guarantee your privacy and will not share those reports with anyone.
## Enforcement Strategy ## ⚖️ Enforcement Strategy
Depending on the severity of the infraction, any action from the list below may be applied. Depending on the severity of the infraction, any action from the list below may be applied.
Please keep in mind cases are reviewed on a per-case basis and members are the ultimate Please keep in mind cases are reviewed on a per-case basis and members are the ultimate
@@ -63,27 +64,27 @@ to be taken is still up to the member.
For example, if the matter at hand regards a representative of a marginalized group or minority, For example, if the matter at hand regards a representative of a marginalized group or minority,
the member might ask for a first-hand opinion from another representative of such group. the member might ask for a first-hand opinion from another representative of such group.
### Correction/Edit ### ✏️ Correction/Edit
If your message is found to be misleading or poorly worded, a member might If your message is found to be misleading or poorly worded, a member might
edit your message. edit your message.
### Warning/Deletion ### ⚠️ Warning/Deletion
If your message is found inappropriate, a member might give you a public or private warning, If your message is found inappropriate, a member might give you a public or private warning,
and/or delete your message. and/or delete your message.
### Mute ### 🔇 Mute
If your message is disruptive, or you have been repeatedly violating the standards, If your message is disruptive, or you have been repeatedly violating the standards,
a member might mute (or temporarily ban) you. a member might mute (or temporarily ban) you.
### Ban ### Ban
If your message is hateful, very disruptive, or other, less serious infractions are repeated If your message is hateful, very disruptive, or other, less serious infractions are repeated
ignoring previous punishments, a member might ban you permanently. ignoring previous punishments, a member might ban you permanently.
## Scope ## 🔎 Scope
This CoC shall apply to all projects ran under the Orange++ lead and all _official_ communities This CoC shall apply to all projects ran under the Orange++ lead and all _official_ communities
outside of GitHub. outside of GitHub.

View File

@@ -2,4 +2,4 @@
## Reporting a Vulnerability ## Reporting a Vulnerability
Please report security issues to `orange-cpp@yandex.com` Please report security issues to `orange-cpp@yandex.ru`

154
include/omath/Angle.hpp Normal file
View File

@@ -0,0 +1,154 @@
//
// Created by Orange on 11/30/2024.
//
#pragma once
#include "omath/Angles.hpp"
#include <algorithm>
namespace omath
{
enum class AngleFlags
{
Normalized = 0,
Clamped = 1,
};
template<class Type = float, Type min = Type(0), Type max = Type(360), AngleFlags flags = AngleFlags::Normalized>
requires std::is_arithmetic_v<Type>
class Angle
{
Type m_angle;
constexpr Angle(const Type& degrees)
{
if constexpr (flags == AngleFlags::Normalized)
m_angle = angles::WrapAngle(degrees, min, max);
else if constexpr (flags == AngleFlags::Clamped)
m_angle = std::clamp(degrees, min, max);
else
{
static_assert(false);
std::unreachable();
}
}
public:
[[nodiscard]]
constexpr static Angle FromDegrees(const Type& degrees)
{
return {degrees};
}
constexpr Angle() : m_angle(0)
{
}
[[nodiscard]]
constexpr static Angle FromRadians(const Type& degrees)
{
return {angles::RadiansToDegrees<Type>(degrees)};
}
[[nodiscard]]
constexpr const Type& operator*() const
{
return m_angle;
}
[[nodiscard]]
constexpr Type AsDegrees() const
{
return m_angle;
}
[[nodiscard]]
constexpr Type AsRadians() const
{
return angles::DegreesToRadians(m_angle);
}
[[nodiscard]]
Type Sin() const
{
return std::sin(AsRadians());
}
[[nodiscard]]
Type Cos() const
{
return std::cos(AsRadians());
}
[[nodiscard]]
Type Tan() const
{
return std::tan(AsRadians());
}
[[nodiscard]]
Type Atan() const
{
return std::atan(AsRadians());
}
[[nodiscard]]
Type Cot() const
{
return Cos() / Sin();
}
[[nodiscard]]
constexpr Angle& operator+=(const Angle& other)
{
if constexpr (flags == AngleFlags::Normalized)
m_angle = angles::WrapAngle(m_angle + other.m_angle, min, max);
else if constexpr (flags == AngleFlags::Clamped)
m_angle = std::clamp(m_angle + other.m_angle, min, max);
else
{
static_assert(false);
std::unreachable();
}
return *this;
}
[[nodiscard]]
constexpr std::partial_ordering operator<=>(const Angle& other) const = default;
[[nodiscard]]
constexpr Angle& operator-=(const Angle& other)
{
return operator+=(-other);
}
[[nodiscard]]
constexpr Angle& operator+(const Angle& other)
{
if constexpr (flags == AngleFlags::Normalized)
return {angles::WrapAngle(m_angle + other.m_angle, min, max)};
else if constexpr (flags == AngleFlags::Clamped)
return {std::clamp(m_angle + other.m_angle, min, max)};
else
static_assert(false);
std::unreachable();
}
[[nodiscard]]
constexpr Angle& operator-(const Angle& other)
{
return operator+(-other);
}
[[nodiscard]]
constexpr Angle operator-() const
{
return {-m_angle};
}
};
}

View File

@@ -4,15 +4,61 @@
#pragma once #pragma once
#include <numbers> #include <numbers>
#include <cmath>
namespace omath::angles namespace omath::angles
{ {
[[nodiscard]] constexpr float RadiansToDegrees(const float radiands) template<class Type>
requires std::is_floating_point_v<Type>
[[nodiscard]] constexpr Type RadiansToDegrees(const Type& radians)
{ {
return radiands * (180.f / std::numbers::pi_v<float>); return radians * (Type(180) / std::numbers::pi_v<Type>);
} }
[[nodiscard]] constexpr float DegreesToRadians(const float degrees)
template<class Type>
requires std::is_floating_point_v<Type>
[[nodiscard]] constexpr Type DegreesToRadians(const Type& degrees)
{ {
return degrees * (std::numbers::pi_v<float> / 180.f); return degrees * (std::numbers::pi_v<Type> / Type(180));
}
template<class type>
requires std::is_floating_point_v<type>
[[nodiscard]] type HorizontalFovToVertical(const type& horFov, const type& aspect)
{
const auto fovRad = DegreesToRadians(horFov);
const auto vertFov = type(2) * std::atan(std::tan(fovRad / type(2)) / aspect);
return RadiansToDegrees(vertFov);
}
template<class Type>
requires std::is_floating_point_v<Type>
[[nodiscard]] Type VerticalFovToHorizontal(const Type& vertFov, const Type& aspect)
{
const auto fovRad = DegreesToRadians(vertFov);
const auto horFov = Type(2) * std::atan(std::tan(fovRad / Type(2)) * aspect);
return RadiansToDegrees(horFov);
}
template<class Type>
requires std::is_arithmetic_v<Type>
[[nodiscard]] Type WrapAngle(const Type& angle, const Type& min, const Type& max)
{
if (angle <= max && angle >= min)
return angle;
const Type range = max - min;
Type wrappedAngle = std::fmod(angle - min, range);
if (wrappedAngle < 0)
wrappedAngle += range;
return wrappedAngle + min;
} }
} }

View File

@@ -5,10 +5,9 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <utility> #include <utility>
#include "Vector3.hpp" #include "Vector3.hpp"
#include <stdexcept>
#include "Angles.hpp"
namespace omath namespace omath
@@ -25,7 +24,7 @@ namespace omath
}; };
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_floating_point_v<Type> || std::is_integral_v<Type>) requires std::is_arithmetic_v<Type>
class Mat final class Mat final
{ {
public: public:
@@ -33,7 +32,10 @@ namespace omath
{ {
Clear(); Clear();
} }
constexpr static MatStoreType GetStoreOrdering()
{
return StoreType;
}
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)
@@ -59,24 +61,24 @@ namespace omath
std::copy_n(rawData, Rows * Columns, m_data.begin()); std::copy_n(rawData, Rows * Columns, m_data.begin());
} }
constexpr Mat(const Mat &other) noexcept constexpr Mat(const Mat& other) noexcept
{ {
m_data = other.m_data; m_data = other.m_data;
} }
constexpr Mat(Mat &&other) noexcept constexpr Mat(Mat&& other) noexcept
{ {
m_data = std::move(other.m_data); m_data = std::move(other.m_data);
} }
[[nodiscard]] [[nodiscard]]
static consteval size_t RowCount() noexcept static constexpr size_t RowCount() noexcept
{ {
return Rows; return Rows;
} }
[[nodiscard]] [[nodiscard]]
static consteval size_t ColumnsCount() noexcept static constexpr size_t ColumnsCount() noexcept
{ {
return Columns; return Columns;
} }
@@ -87,7 +89,7 @@ namespace omath
return {Rows, Columns}; return {Rows, Columns};
} }
[[nodiscard]] 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");
@@ -105,9 +107,9 @@ namespace omath
} }
} }
[[nodiscard]] 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]]
@@ -126,16 +128,17 @@ namespace omath
Set(0); Set(0);
} }
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);
} }
// 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> operator*(const Mat<Columns, OtherColumns> &other) const constexpr Mat<Rows, OtherColumns, Type, StoreType>
operator*(const Mat<Columns, OtherColumns, Type, StoreType>& other) const
{ {
Mat<Rows, OtherColumns> result; Mat<Rows, OtherColumns, Type, StoreType> result;
for (size_t i = 0; i < Rows; ++i) for (size_t i = 0; i < Rows; ++i)
for (size_t j = 0; j < OtherColumns; ++j) for (size_t j = 0; j < OtherColumns; ++j)
@@ -148,7 +151,7 @@ namespace omath
return result; return result;
} }
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)
@@ -157,19 +160,20 @@ namespace omath
} }
template<size_t OtherColumns> template<size_t OtherColumns>
constexpr Mat<Rows, OtherColumns> operator*=(const Mat<Columns, OtherColumns> &other) constexpr Mat<Rows, OtherColumns, Type, StoreType>
operator*=(const Mat<Columns, OtherColumns, Type, StoreType>& other)
{ {
return *this = *this * other; return *this = *this * other;
} }
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;
} }
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)
@@ -177,14 +181,14 @@ namespace omath
return *this; return *this;
} }
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;
} }
constexpr Mat &operator=(const Mat &other) noexcept constexpr Mat& operator=(const Mat& other) noexcept
{ {
if (this == &other) if (this == &other)
return *this; return *this;
@@ -194,7 +198,7 @@ namespace omath
return *this; return *this;
} }
constexpr Mat &operator=(Mat &&other) noexcept constexpr Mat& operator=(Mat&& other) noexcept
{ {
if (this == &other) if (this == &other)
return *this; return *this;
@@ -207,9 +211,9 @@ namespace omath
} }
[[nodiscard]] [[nodiscard]]
constexpr Mat<Columns, Rows> Transposed() const noexcept constexpr Mat<Columns, Rows, Type, StoreType> Transposed() const noexcept
{ {
Mat<Columns, Rows> transposed; Mat<Columns, Rows, Type, StoreType> transposed;
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)
transposed.At(j, i) = At(i, j); transposed.At(j, i) = At(i, j);
@@ -240,9 +244,9 @@ namespace omath
} }
[[nodiscard]] [[nodiscard]]
constexpr Mat<Rows - 1, Columns - 1> 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> 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)
{ {
if (i == row) if (i == row)
@@ -260,15 +264,15 @@ namespace omath
} }
[[nodiscard]] [[nodiscard]]
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]]
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]]
@@ -288,12 +292,23 @@ namespace omath
return oss.str(); return oss.str();
} }
[[nodiscard]]
bool operator==(const Mat& mat) const
{
return m_data == mat.m_data;
}
[[nodiscard]]
bool operator!=(const Mat& mat) const
{
return !operator==(mat);
}
// Static methods that return fixed-size matrices // Static methods that return fixed-size matrices
[[nodiscard]] [[nodiscard]]
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},
{0, -screenHeight / 2, 0, 0}, {0, -screenHeight / 2, 0, 0},
{0, 0, 1, 0}, {0, 0, 1, 0},
@@ -301,68 +316,95 @@ namespace omath
}; };
} }
[[nodiscard]] private:
constexpr static Mat<4, 4> TranslationMat(const Vector3 &diff) noexcept std::array<Type, Rows * Columns> m_data;
{
return
{
{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{diff.x, diff.y, diff.z, 1},
}; };
}
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]] [[nodiscard]]
constexpr static Mat<4, 4> OrientationMat(const Vector3 &forward, const Vector3 &right, constexpr static Mat<1, 4, Type, St> MatRowFromVector(const Vector3& vector) noexcept
const Vector3 &up) noexcept
{
return
{
{right.x, up.x, forward.x, 0},
{right.y, up.y, forward.y, 0},
{right.z, up.z, forward.z, 0},
{0, 0, 0, 1},
};
}
[[nodiscard]]
constexpr static Mat<4, 4> ProjectionMat(const Type &fieldOfView, const Type &aspectRatio,
const Type &near, const Type &far, const Type &lensZoom) noexcept
{
const Type &fovHalfTan = std::tan(angles::DegreesToRadians(fieldOfView) / 2);
const Type &frustumHeight = far - near;
return
{
{-1 / (aspectRatio * fovHalfTan) * lensZoom, 0, 0, 0},
{0, -1 / fovHalfTan * lensZoom, 0, 0},
{0, 0, -far / frustumHeight, -1},
{0, 0, near * far / frustumHeight, 0}
};
}
[[nodiscard]]
constexpr static Mat<4, 1> MatRowFromVector(const Vector3 &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>
[[nodiscard]] [[nodiscard]]
constexpr static Mat<1, 4> MatColumnFromVector(const Vector3 &vector) noexcept constexpr static Mat<4, 1, Type, St> MatColumnFromVector(const Vector3& vector) noexcept
{
return {{vector.x}, {vector.y}, {vector.z}, {1}};
}
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]]
constexpr Mat<4, 4, Type, St> MatTranslation(const Vector3& diff) noexcept
{ {
return return
{ {
{ {1, 0, 0, diff.x},
{vector.x}, {0, 1, 0, diff.y},
{vector.y}, {0, 0, 1, diff.z},
{vector.z}, {0, 0, 0, 1},
{1}
}
}; };
} }
private: template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
std::array<Type, Rows * Columns> m_data; [[nodiscard]]
Mat<4, 4, Type, St> MatRotationAxisX(const Angle& angle) noexcept
{
return
{
{1, 0, 0, 0},
{0, angle.Cos(), -angle.Sin(), 0},
{0, angle.Sin(), angle.Cos(), 0},
{0, 0, 0, 1}
}; };
} }
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
[[nodiscard]]
Mat<4, 4, Type, St> MatRotationAxisY(const Angle& angle) noexcept
{
return
{
{angle.Cos(), 0, angle.Sin(), 0},
{0 , 1, 0, 0},
{-angle.Sin(), 0, angle.Cos(), 0},
{0 , 0, 0, 1}
};
}
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
[[nodiscard]]
Mat<4, 4, Type, St> MatRotationAxisZ(const Angle& angle) noexcept
{
return
{
{angle.Cos(), -angle.Sin(), 0, 0},
{angle.Sin(), angle.Cos(), 0, 0},
{ 0, 0, 1, 0},
{ 0, 0, 0, 1},
};
}
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]]
static Mat<4, 4, Type, St> MatCameraView(const Vector3& forward, const Vector3& right, const Vector3& up,
const Vector3& cameraOrigin) noexcept
{
return Mat<4, 4, Type, St>
{
{right.x, right.y, right.z, 0},
{up.x, up.y, up.z, 0},
{forward.x, forward.y, forward.z, 0},
{0, 0, 0, 1},
} * MatTranslation<Type, St>(-cameraOrigin);
}
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class ViewAngles>
[[nodiscard]]
Mat<4, 4, Type, St> MatRotation(const ViewAngles& angles) noexcept
{
return MatRotationAxisZ(angles.yaw) * MatRotationAxisY(angles.pitch) * MatRotationAxisX(angles.roll);
}
} // namespace omath

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <initializer_list>
#include <memory> #include <memory>
#include <string> #include <string>
#include <initializer_list>
namespace omath namespace omath
{ {
@@ -27,11 +27,11 @@ namespace omath
[[nodiscard]] [[nodiscard]]
static Matrix ProjectionMatrix(float fieldOfView, float aspectRatio, float near, float far); static Matrix ProjectionMatrix(float fieldOfView, float aspectRatio, float near, float far);
Matrix(const Matrix &other); Matrix(const Matrix& other);
Matrix(size_t rows, size_t columns, const float *pRaw); Matrix(size_t rows, size_t columns, const float* pRaw);
Matrix(Matrix &&other) noexcept; Matrix(Matrix&& other) noexcept;
[[nodiscard]] [[nodiscard]]
size_t RowCount() const noexcept; size_t RowCount() const noexcept;
@@ -43,7 +43,7 @@ namespace omath
std::pair<size_t, size_t> Size() const noexcept; std::pair<size_t, size_t> Size() const noexcept;
[[nodiscard]] [[nodiscard]]
float &At(size_t iRow, size_t iCol); float& At(size_t iRow, size_t iCol);
[[nodiscard]] [[nodiscard]]
float Sum(); float Sum();
@@ -56,17 +56,17 @@ namespace omath
void Set(float val); void Set(float val);
[[nodiscard]] [[nodiscard]]
const float &At(size_t iRow, size_t iCol) const; const float& At(size_t iRow, size_t iCol) const;
Matrix operator*(const Matrix &other) const; Matrix operator*(const Matrix& other) const;
Matrix& operator*=(const Matrix &other); Matrix& operator*=(const Matrix& other);
Matrix operator*(float f) const; Matrix operator*(float f) const;
Matrix &operator*=(float f); Matrix& operator*=(float f);
Matrix &operator/=(float f); Matrix& operator/=(float f);
void Clear(); void Clear();
@@ -85,9 +85,9 @@ namespace omath
[[nodiscard]] [[nodiscard]]
const float* Raw() const; const float* Raw() const;
Matrix &operator=(const Matrix &other); Matrix& operator=(const Matrix& other);
Matrix &operator=(Matrix &&other) noexcept; Matrix& operator=(Matrix&& other) noexcept;
Matrix operator/(float f) const; Matrix operator/(float f) const;
@@ -101,4 +101,4 @@ namespace omath
size_t m_columns; size_t m_columns;
std::unique_ptr<float[]> m_data; std::unique_ptr<float[]> m_data;
}; };
} } // namespace omath

View File

@@ -10,6 +10,7 @@ namespace omath
{ {
public: public:
Triangle3d(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3); Triangle3d(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3);
Vector3 m_vertex1; Vector3 m_vertex1;
Vector3 m_vertex2; Vector3 m_vertex2;
Vector3 m_vertex3; Vector3 m_vertex3;

View File

@@ -216,10 +216,6 @@ namespace omath
[[nodiscard]] Vector3 ViewAngleTo(const Vector3& other) const; [[nodiscard]] Vector3 ViewAngleTo(const Vector3& other) const;
[[nodiscard]] static Vector3 ForwardVector(float pitch, float yaw);
[[nodiscard]] static Vector3 RightVector(float pitch, float yaw, float roll);
[[nodiscard]] static Vector3 UpVector(float pitch, float yaw, float roll);
[[nodiscard]] std::tuple<float, float, float> AsTuple() const [[nodiscard]] std::tuple<float, float, float> AsTuple() const
{ {
return std::make_tuple(x, y, z); return std::make_tuple(x, y, z);

View File

@@ -0,0 +1,15 @@
//
// Created by Orange on 11/30/2024.
//
#pragma once
namespace omath
{
template<class PitchType, class YawType, class RollType>
struct ViewAngles
{
PitchType pitch;
YawType yaw;
RollType roll;
};
}

View File

@@ -26,6 +26,7 @@ namespace omath::collision
public: public:
LineTracer() = delete; LineTracer() = delete;
[[nodiscard]] [[nodiscard]]
static bool CanTraceLine(const Ray& ray, const Triangle3d& triangle); static bool CanTraceLine(const Ray& ray, const Triangle3d& triangle);

View File

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

View File

@@ -0,0 +1,24 @@
//
// Created by Orange on 12/4/2024.
//
#pragma once
#include <omath/Vector3.hpp>
#include <omath/Mat.hpp>
#include <omath/Angle.hpp>
#include <omath/ViewAngles.hpp>
namespace omath::source
{
constexpr Vector3 kAbsUp = {0, 0, 1};
constexpr Vector3 kAbsRight = {0, -1, 0};
constexpr Vector3 kAbsForward = {1, 0, 0};
using Mat4x4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat3x3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat1x3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
using PitchAngle = Angle<float, -89.f, 89.f, AngleFlags::Clamped>;
using YawAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
using RollAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
using ViewAngles = omath::ViewAngles<PitchAngle, YawAngle, RollAngle>;
} // namespace omath::source

View File

@@ -0,0 +1,57 @@
//
// Created by Orange on 12/4/2024.
//
#pragma once
#include "Constants.h"
namespace omath::source
{
[[nodiscard]]
inline Vector3 ForwardVector(const ViewAngles& angles)
{
const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsForward);
return {vec.At(0, 0), vec.At(1, 0), vec.At(2, 0)};
}
[[nodiscard]]
inline Vector3 RightVector(const ViewAngles& angles)
{
const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsRight);
return {vec.At(0, 0), vec.At(1, 0), vec.At(2, 0)};
}
[[nodiscard]]
inline Vector3 UpVector(const ViewAngles& angles)
{
const auto vec = MatRotation(angles) * MatColumnFromVector(kAbsUp);
return {vec.At(0, 0), vec.At(1, 0), vec.At(2, 0)};
}
[[nodiscard]] inline Mat4x4 CalcViewMatrix(const ViewAngles& angles, const Vector3& cam_origin)
{
return MatCameraView(ForwardVector(angles), RightVector(angles), UpVector(angles), cam_origin);
}
[[nodiscard]]
inline Mat4x4 CalcPerspectiveProjectionMatrix(const float fieldOfView, const float aspectRatio, const float near,
const float far)
{
// NOTE: Needed tp make thing draw normal, since source is wierd
// and use tricky projection matrix formula.
constexpr auto kMultiplyFactor = 0.75f;
const float fovHalfTan = std::tan(angles::DegreesToRadians(fieldOfView) / 2.f) * kMultiplyFactor;
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},
};
}
} // namespace omath::source

View File

@@ -12,6 +12,12 @@
namespace omath::pathfinding namespace omath::pathfinding
{ {
enum Error
{
};
class NavigationMesh final class NavigationMesh final
{ {
public: public:

View File

@@ -13,7 +13,7 @@ namespace omath::prediction
public: public:
[[nodiscard]] [[nodiscard]]
constexpr Vector3 PredictPosition(float time, float gravity) const constexpr Vector3 PredictPosition(const float time, const float gravity) const
{ {
auto predicted = m_origin + m_velocity * time; auto predicted = m_origin + m_velocity * time;

View File

@@ -5,10 +5,11 @@
#pragma once #pragma once
#include <expected> #include <expected>
#include <omath/Vector3.hpp>
#include <omath/Mat.hpp> #include <omath/Mat.hpp>
#include <string_view> #include <omath/Vector3.hpp>
#include "ErrorCodes.hpp" #include "ErrorCodes.hpp"
#include <omath/Angle.hpp>
#include <type_traits>
namespace omath::projection namespace omath::projection
@@ -19,29 +20,70 @@ namespace omath::projection
float m_width; float m_width;
float m_height; float m_height;
[[nodiscard]] constexpr float AspectRatio() const {return m_width / m_height;} [[nodiscard]] constexpr float AspectRatio() const
{
return m_width / m_height;
}
}; };
using FieldOfView = const Angle<float, 0.f, 180.f, AngleFlags::Clamped>;
template<class Mat4x4Type, class ViewAnglesType>
class Camera class Camera
{ {
public: public:
Camera(const Vector3& position, const Vector3& viewAngles, const ViewPort& viewPort, virtual ~Camera() = default;
float fov, float near, float far, float lensZoom); Camera(const Vector3& position, const ViewAnglesType& viewAngles, const ViewPort& viewPort,
void SetViewAngles(const Vector3& viewAngles); const FieldOfView& fov, const float near, const float far) :
m_viewPort(viewPort), m_fieldOfView(fov), m_farPlaneDistance(far), m_nearPlaneDistance(near),
m_viewAngles(viewAngles), m_origin(position)
{
[[nodiscard]] Mat<4, 4> GetViewMatrix() const; }
[[nodiscard]] std::expected<Vector3, Error> WorldToScreen(const Vector3& worldPosition) const; virtual void LookAt(const Vector3& target) = 0;
[[nodiscard]] virtual Mat4x4Type GetViewMatrix() const = 0;
[[nodiscard]] virtual Mat4x4Type GetProjectionMatrix() const = 0;
[[nodiscard]] Mat4x4Type GetViewProjectionMatrix()
{
return GetProjectionMatrix() * GetViewMatrix();
}
[[nodiscard]] std::expected<Vector3, Error> WorldToScreen(const Mat4x4Type& viewProj, const Vector3& worldPosition) const
{
auto projected = viewProj * MatColumnFromVector<float, Mat4x4Type::GetStoreOrdering()>(worldPosition);
if (projected.At(3, 0) == 0.0f)
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS);
projected /= projected.At(3, 0);
if (IsNdcOutOfBounds(projected))
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS);
return Vector3{(projected.At(0,0)+1) / 2 * m_viewPort.m_width , (-projected.At(1,0)+1) / 2 * m_viewPort.m_height, projected.At(2,0)};
}
protected:
ViewPort m_viewPort{}; ViewPort m_viewPort{};
float m_fieldOfView; Angle<float, 0.f, 180.f, AngleFlags::Clamped> m_fieldOfView;
float m_farPlaneDistance; float m_farPlaneDistance;
float m_nearPlaneDistance; float m_nearPlaneDistance;
float m_lensZoom;
ViewAnglesType m_viewAngles;
Vector3 m_origin;
private: private:
Vector3 m_viewAngles; template<class Type>
Vector3 m_origin; [[nodiscard]]
constexpr static bool IsNdcOutOfBounds(const Type& ndc)
{
return std::ranges::any_of( ndc.RawArray(), [](const auto& val) { return val < -1 || val > 1; });
}
}; };
} } // namespace omath::projection

View File

@@ -11,3 +11,4 @@ add_subdirectory(prediction)
add_subdirectory(pathfinding) add_subdirectory(pathfinding)
add_subdirectory(projection) add_subdirectory(projection)
add_subdirectory(collision) add_subdirectory(collision)
add_subdirectory(engines)

View File

@@ -1,13 +1,12 @@
#include "omath/Matrix.hpp" #include "omath/Matrix.hpp"
#include "omath/Vector3.hpp"
#include "omath/Angles.hpp" #include "omath/Angles.hpp"
#include "omath/Vector3.hpp"
#include <complex>
#include <format> #include <format>
#include <utility>
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
#include <complex>
namespace omath namespace omath
@@ -31,23 +30,23 @@ namespace omath
m_columns = rows.begin()->size(); m_columns = rows.begin()->size();
for (const auto& row : rows) for (const auto& row: rows)
if (row.size() != m_columns) if (row.size() != m_columns)
throw std::invalid_argument("All rows must have the same number of columns."); throw std::invalid_argument("All rows must have the same number of columns.");
m_data = std::make_unique<float[]>(m_rows * m_columns); m_data = std::make_unique<float[]>(m_rows * m_columns);
size_t i = 0; size_t i = 0;
for (const auto& row : rows) for (const auto& row: rows)
{ {
size_t j = 0; size_t j = 0;
for (const auto& value : row) for (const auto& value: row)
At(i, j++) = value; At(i, j++) = value;
++i; ++i;
} }
} }
Matrix::Matrix(const Matrix &other) Matrix::Matrix(const Matrix& other)
{ {
m_rows = other.m_rows; m_rows = other.m_rows;
m_columns = other.m_columns; m_columns = other.m_columns;
@@ -59,7 +58,7 @@ namespace omath
At(i, j) = other.At(i, j); At(i, j) = other.At(i, j);
} }
Matrix::Matrix(const size_t rows, const size_t columns, const float *pRaw) Matrix::Matrix(const size_t rows, const size_t columns, const float* pRaw)
{ {
m_rows = rows; m_rows = rows;
m_columns = columns; m_columns = columns;
@@ -67,9 +66,8 @@ namespace omath
m_data = std::make_unique<float[]>(m_rows * m_columns); m_data = std::make_unique<float[]>(m_rows * m_columns);
for (size_t i = 0; i < rows*columns; ++i) for (size_t i = 0; i < rows * columns; ++i)
At(i / rows, i % columns) = pRaw[i]; At(i / rows, i % columns) = pRaw[i];
} }
size_t Matrix::RowCount() const noexcept size_t Matrix::RowCount() const noexcept
@@ -77,7 +75,7 @@ namespace omath
return m_rows; return m_rows;
} }
Matrix::Matrix(Matrix &&other) noexcept Matrix::Matrix(Matrix&& other) noexcept
{ {
m_rows = other.m_rows; m_rows = other.m_rows;
m_columns = other.m_columns; m_columns = other.m_columns;
@@ -99,7 +97,7 @@ namespace omath
return {RowCount(), ColumnsCount()}; return {RowCount(), ColumnsCount()};
} }
float &Matrix::At(const size_t iRow, const size_t iCol) float& Matrix::At(const size_t iRow, const size_t iCol)
{ {
return const_cast<float&>(std::as_const(*this).At(iRow, iCol)); return const_cast<float&>(std::as_const(*this).At(iRow, iCol));
} }
@@ -115,12 +113,12 @@ namespace omath
return sum; return sum;
} }
const float &Matrix::At(const size_t iRow, const size_t iCol) const const float& Matrix::At(const size_t iRow, const size_t iCol) const
{ {
return m_data[iRow * m_columns + iCol]; return m_data[iRow * m_columns + iCol];
} }
Matrix Matrix::operator*(const Matrix &other) const Matrix Matrix::operator*(const Matrix& other) const
{ {
if (m_columns != other.m_rows) if (m_columns != other.m_rows)
throw std::runtime_error("n != m"); throw std::runtime_error("n != m");
@@ -136,7 +134,7 @@ namespace omath
return outMat; return outMat;
} }
Matrix & Matrix::operator*=(const Matrix &other) Matrix& Matrix::operator*=(const Matrix& other)
{ {
*this = *this * other; *this = *this * other;
return *this; return *this;
@@ -152,7 +150,7 @@ namespace omath
return out; return out;
} }
Matrix &Matrix::operator*=(const float f) Matrix& Matrix::operator*=(const float f)
{ {
for (size_t i = 0; i < RowCount(); i++) for (size_t i = 0; i < RowCount(); i++)
for (size_t j = 0; j < ColumnsCount(); j++) for (size_t j = 0; j < ColumnsCount(); j++)
@@ -165,7 +163,7 @@ namespace omath
Set(0.f); Set(0.f);
} }
Matrix &Matrix::operator=(const Matrix &other) Matrix& Matrix::operator=(const Matrix& other)
{ {
if (this == &other) if (this == &other)
return *this; return *this;
@@ -175,10 +173,9 @@ namespace omath
At(i, j) = other.At(i, j); At(i, j) = other.At(i, j);
return *this; return *this;
} }
Matrix &Matrix::operator=(Matrix &&other) noexcept Matrix& Matrix::operator=(Matrix&& other) noexcept
{ {
if (this == &other) if (this == &other)
return *this; return *this;
@@ -191,10 +188,9 @@ namespace omath
other.m_columns = 0; other.m_columns = 0;
return *this; return *this;
} }
Matrix &Matrix::operator/=(const float f) Matrix& Matrix::operator/=(const float f)
{ {
for (size_t i = 0; i < m_rows; ++i) for (size_t i = 0; i < m_rows; ++i)
for (size_t j = 0; j < m_columns; ++j) for (size_t j = 0; j < m_columns; ++j)
@@ -221,9 +217,9 @@ namespace omath
{ {
for (size_t j = 0; j < m_columns; ++j) for (size_t j = 0; j < m_columns; ++j)
{ {
str += std::format("{:.1f}",At(i, j)); str += std::format("{:.1f}", At(i, j));
if (j == m_columns-1) if (j == m_columns - 1)
str += '\n'; str += '\n';
else else
str += ' '; str += ' ';
@@ -306,8 +302,7 @@ namespace omath
Matrix Matrix::ToScreenMatrix(const float screenWidth, const float screenHeight) Matrix Matrix::ToScreenMatrix(const float screenWidth, const float screenHeight)
{ {
return return {
{
{screenWidth / 2.f, 0.f, 0.f, 0.f}, {screenWidth / 2.f, 0.f, 0.f, 0.f},
{0.f, -screenHeight / 2.f, 0.f, 0.f}, {0.f, -screenHeight / 2.f, 0.f, 0.f},
{0.f, 0.f, 1.f, 0.f}, {0.f, 0.f, 1.f, 0.f},
@@ -315,10 +310,9 @@ namespace omath
}; };
} }
Matrix Matrix::TranslationMatrix(const Vector3 &diff) Matrix Matrix::TranslationMatrix(const Vector3& diff)
{
return
{ {
return {
{1.f, 0.f, 0.f, 0.f}, {1.f, 0.f, 0.f, 0.f},
{0.f, 1.f, 0.f, 0.f}, {0.f, 1.f, 0.f, 0.f},
{0.f, 0.f, 1.f, 0.f}, {0.f, 0.f, 1.f, 0.f},
@@ -326,10 +320,9 @@ namespace omath
}; };
} }
Matrix Matrix::OrientationMatrix(const Vector3 &forward, const Vector3 &right, const Vector3 &up) Matrix Matrix::OrientationMatrix(const Vector3& forward, const Vector3& right, const Vector3& up)
{
return
{ {
return {
{right.x, up.x, forward.x, 0.f}, {right.x, up.x, forward.x, 0.f},
{right.y, up.y, forward.y, 0.f}, {right.y, up.y, forward.y, 0.f},
{right.z, up.z, forward.z, 0.f}, {right.z, up.z, forward.z, 0.f},
@@ -337,18 +330,14 @@ namespace omath
}; };
} }
Matrix Matrix::ProjectionMatrix(const float fieldOfView, const float aspectRatio, const float near, Matrix Matrix::ProjectionMatrix(const float fieldOfView, const float aspectRatio, const float near, const float far)
const float far)
{ {
const float fovHalfTan = std::tan(angles::DegreesToRadians(fieldOfView) / 2.f); const float fovHalfTan = std::tan(angles::DegreesToRadians(fieldOfView) / 2.f);
return return {{1.f / (aspectRatio * fovHalfTan), 0.f, 0.f, 0.f},
{
{1.f / (aspectRatio*fovHalfTan), 0.f, 0.f, 0.f},
{0.f, 1.f / fovHalfTan, 0.f, 0.f}, {0.f, 1.f / fovHalfTan, 0.f, 0.f},
{0.f, 0.f, (far + near) / (far - near), 2.f * near * far / (far - near)}, {0.f, 0.f, (far + near) / (far - near), 2.f * near * far / (far - near)},
{0.f, 0.f, -1.f, 0.f} {0.f, 0.f, -1.f, 0.f}};
};
} }
const float* Matrix::Raw() const const float* Matrix::Raw() const
@@ -356,9 +345,9 @@ namespace omath
return m_data.get(); return m_data.get();
} }
void Matrix::SetDataFromRaw(const float *pRawMatrix) void Matrix::SetDataFromRaw(const float* pRawMatrix)
{ {
for (size_t i = 0; i < m_columns*m_rows; ++i) for (size_t i = 0; i < m_columns * m_rows; ++i)
At(i / m_rows, i % m_columns) = pRawMatrix[i]; At(i / m_rows, i % m_columns) = pRawMatrix[i];
} }
@@ -368,4 +357,4 @@ namespace omath
m_rows = 0; m_rows = 0;
m_data = nullptr; m_data = nullptr;
} }
} } // namespace omath

View File

@@ -20,50 +20,4 @@ namespace omath
0.f 0.f
}; };
} }
Vector3 Vector3::ForwardVector(const float pitch, const float yaw)
{
const auto cosPitch = std::cos(angles::DegreesToRadians(pitch));
const auto sinPitch = std::sin(angles::DegreesToRadians(pitch));
const auto cosYaw = std::cos(angles::DegreesToRadians(yaw));
const auto sinYaw = std::sin(angles::DegreesToRadians(yaw));
return
{
cosPitch*cosYaw,
cosPitch*sinYaw,
sinPitch
};
}
Vector3 Vector3::RightVector(const float pitch, const float yaw, const float roll)
{
const auto radPitch = angles::DegreesToRadians(pitch);
const auto radYaw = angles::DegreesToRadians(yaw);
const auto radRoll = angles::DegreesToRadians(roll);
const auto cosPitch = std::cos(radPitch);
const auto sinPitch = std::sin(radPitch);
const auto cosYaw = std::cos(radYaw);
const auto sinYaw = std::sin(radYaw);
const auto cosRoll = std::cos(radRoll);
const auto sinRoll = std::sin(radRoll);
return
{
sinRoll*sinPitch*cosYaw + cosRoll*sinYaw,
sinRoll*sinPitch*sinYaw - cosRoll*cosYaw,
-sinRoll*cosPitch
};
}
Vector3 Vector3::UpVector(float pitch, float yaw, float roll)
{
return RightVector(pitch, yaw, roll).Cross(ForwardVector(pitch, yaw));
}
} }

View File

@@ -1,7 +1,6 @@
// //
// Created by Orange on 11/13/2024. // Created by Orange on 11/13/2024.
// //
#pragma once
#include "omath/collision/LineTracer.hpp" #include "omath/collision/LineTracer.hpp"
namespace omath::collision namespace omath::collision

View File

@@ -0,0 +1 @@
add_subdirectory(Source)

View File

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

View File

@@ -0,0 +1,36 @@
//
// Created by Orange on 12/4/2024.
//
#include "omath/engines/Source/Camera.hpp"
#include "omath/engines/Source/Formulas.hpp"
namespace omath::source
{
Camera::Camera(const Vector3& position, const ViewAngles& viewAngles, const projection::ViewPort& viewPort,
const projection::FieldOfView& fov, const float near, const float far) :
projection::Camera<Mat4x4, ViewAngles>(position, viewAngles, viewPort, fov, near, far)
{
}
void Camera::LookAt(const Vector3& target)
{
const float distance = m_origin.DistTo(target);
const auto delta = target - m_origin;
m_viewAngles.pitch = PitchAngle::FromRadians(std::asin(delta.z / distance));
m_viewAngles.yaw = -YawAngle::FromRadians(std::atan2(delta.y, delta.x));
m_viewAngles.roll = RollAngle::FromRadians(0.f);
}
Mat4x4 Camera::GetViewMatrix() const
{
return CalcViewMatrix(m_viewAngles, m_origin);
}
Mat4x4 Camera::GetProjectionMatrix() const
{
return CalcPerspectiveProjectionMatrix(m_fieldOfView.AsDegrees(), m_viewPort.AspectRatio(), m_nearPlaneDistance, m_farPlaneDistance);
}
} // namespace omath::source

View File

@@ -58,7 +58,7 @@ namespace omath::prediction
return std::nullopt; return std::nullopt;
root = std::sqrt(root); root = std::sqrt(root);
const float angle = std::atan((std::pow(projectile.m_launchSpeed, 2.f) - root) / (bulletGravity * distance2d)); const float angle = std::atan((launchSpeedSqr - root) / (bulletGravity * distance2d));
return angles::RadiansToDegrees(angle); return angles::RadiansToDegrees(angle);
} }

View File

@@ -4,15 +4,18 @@
#include "omath/prediction/Projectile.hpp" #include "omath/prediction/Projectile.hpp"
#include <cmath> #include <cmath>
#include <omath/engines/Source/Formulas.hpp>
namespace omath::prediction namespace omath::prediction
{ {
Vector3 Projectile::PredictPosition(const float pitch, const float yaw, const float time, const float gravity) const Vector3 Projectile::PredictPosition(const float pitch, const float yaw, const float time, const float gravity) const
{ {
auto currentPos = m_origin + Vector3::ForwardVector(pitch, yaw) * m_launchSpeed * time; auto currentPos = m_origin + source::ForwardVector({source::PitchAngle::FromDegrees(-pitch),
currentPos.z -= (gravity * m_gravityScale) * std::pow(time, 2.f) * 0.5f; source::YawAngle::FromDegrees(yaw),
source::RollAngle::FromDegrees(0)}) *
m_launchSpeed * time;
currentPos.z -= (gravity * m_gravityScale) * (time * time) * 0.5f;
return currentPos; return currentPos;
} }
} } // namespace omath::prediction

View File

@@ -3,56 +3,7 @@
// //
#include "omath/projection/Camera.hpp" #include "omath/projection/Camera.hpp"
#include <complex>
#include "omath/Angles.hpp"
namespace omath::projection namespace omath::projection
{ {
Camera::Camera(const Vector3 &position, const Vector3 &viewAngles, const ViewPort &viewPort,
const float fov, const float near, const float far, const float lensZoom)
{
m_origin = position;
m_viewAngles = viewAngles;
m_viewPort = viewPort;
m_fieldOfView = fov;
m_nearPlaneDistance = near;
m_farPlaneDistance = far;
m_lensZoom = lensZoom;
}
Mat<4, 4> Camera::GetViewMatrix() const
{
const auto forward = Vector3::ForwardVector(m_viewAngles.x, m_viewAngles.y);
const auto right = Vector3::RightVector(m_viewAngles.x, m_viewAngles.y, m_viewAngles.z);
const auto up = Vector3::UpVector(m_viewAngles.x, m_viewAngles.y, m_viewAngles.z);
return Mat<>::TranslationMat(-m_origin) * Mat<>::OrientationMat(forward, right, up);
}
std::expected<Vector3, Error> Camera::WorldToScreen(const Vector3& worldPosition) const
{
const auto posVecAsMatrix = Mat<>::MatColumnFromVector(worldPosition);
const auto projectionMatrix = Mat<>::ProjectionMat(m_fieldOfView, m_viewPort.AspectRatio(),
m_nearPlaneDistance, m_farPlaneDistance, m_lensZoom);
Mat<1, 4> projected = posVecAsMatrix * (GetViewMatrix() * projectionMatrix);
if (projected.At(0, 3) == 0.f)
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS);
projected /= projected.At(0, 3);
if (projected.At(0, 0) < -1.f || projected.At(0, 0) > 1.f ||
projected.At(0, 1) < -1.f || projected.At(0, 1) > 1.f ||
projected.At(0, 2) < -1.f || projected.At(0, 2) > 1.f)
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS);
projected *= Mat<4, 4>::ToScreenMat(m_viewPort.m_width, m_viewPort.m_height);
return Vector3{projected.At(0, 0), projected.At(0, 1), projected.At(0, 2)};
}
} }

View File

@@ -5,16 +5,24 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
include(GoogleTest) include(GoogleTest)
add_executable(unit-tests add_executable(unit-tests
UnitTestPrediction.cpp general/UnitTestPrediction.cpp
UnitTestMatrix.cpp general/UnitTestMatrix.cpp
UnitTestMat.cpp general/UnitTestMat.cpp
UnitTestAstar.cpp general/UnitTestAstar.cpp
UnitTestProjection.cpp general/UnitTestProjection.cpp
UnitTestVector3.cpp general/UnitTestVector3.cpp
UnitTestVector2.cpp general/UnitTestVector2.cpp
UnitTestColor.cpp general/UnitTestColor.cpp
UnitTestVector4.cpp general/UnitTestVector4.cpp
UnitTestLineTrace.cpp general/UnitTestLineTrace.cpp
general/UnitTestAngles.cpp
general/UnitTestViewAngles.cpp
general/UnitTestAngle.cpp
engines/UnitTestOpenGL.cpp
engines/UnitTestUnityEngine.cpp
engines/UnitTestSourceEngine.cpp
) )
target_link_libraries(unit-tests PRIVATE gtest gtest_main omath) target_link_libraries(unit-tests PRIVATE gtest gtest_main omath)

View File

@@ -1,19 +0,0 @@
//
// Created by Vlad on 27.08.2024.
//
#include <complex>
#include <gtest/gtest.h>
#include <omath/Matrix.hpp>
#include <print>
#include <omath/projection/Camera.hpp>
TEST(UnitTestProjection, Projection)
{
const omath::projection::Camera camera({0.f, 0.f, 0.f}, {0, 0.f, 0.f} , {1920.f, 1080.f}, 110.f, 0.375f, 5000.f, 1.335f);
const auto projected = camera.WorldToScreen({5000, 0, 0});
EXPECT_TRUE(projected.has_value());
EXPECT_EQ(projected->z, 1.f);
}

View File

@@ -0,0 +1,37 @@
//
// Created by Orange on 11/23/2024.
//
#include <complex>
#include <gtest/gtest.h>
#include <omath/Matrix.hpp>
#include <print>
// #include <glm/glm.hpp>
// #include "glm/ext/matrix_clip_space.hpp"
// #include "glm/ext/matrix_transform.hpp"
TEST(UnitTestOpenGL, Projection)
{
/*const auto proj_glm = glm::perspective(glm::radians(90.f), 16.f / 9.f, 0.1f, 1000.f);
// const auto proj_glm2 = glm::perspectiveLH_NO(glm::radians(90.f), 16.f / 9.f, 0.1f, 1000.f);
// const auto proj_omath = omath::Mat<4, 4, float, omath::MatStoreType::COLUMN_MAJOR>((const float*)&proj_glm);
// EXPECT_EQ(omath::opengl::PerspectiveProjectionMatrix(90, 16.f / 9.f, 0.1f, 1000.f), proj_omath);
glm::vec4 ndc_glm2 = proj_glm * glm::vec4(300.f, 0.f, -1000.f, 1.f);
ndc_glm2 /= ndc_glm2.w;
const omath::Mat<4, 1, float, omath::MatStoreType::COLUMN_MAJOR> cords_omath =
{
{0},
{0},
{-0.2f},
{1}
};
//auto ndc_omath = proj_omath * cords_omath;
// ndc_omath /= ndc_omath.At(3, 0);
*/
}

View File

@@ -0,0 +1,50 @@
//
// Created by Orange on 11/23/2024.
//
#include <gtest/gtest.h>
#include <omath/engines/Source/Camera.hpp>
#include <omath/engines/Source/Constants.h>
#include <omath/engines/Source/Formulas.hpp>
TEST(UnitTestSourceEngine, ForwardVector)
{
const auto forward = omath::source::ForwardVector({});
EXPECT_EQ(forward, omath::source::kAbsForward);
}
TEST(UnitTestSourceEngine, RightVector)
{
const auto right = omath::source::RightVector({});
EXPECT_EQ(right, omath::source::kAbsRight);
}
TEST(UnitTestSourceEngine, UpVector)
{
const auto up = omath::source::UpVector({});
EXPECT_EQ(up, omath::source::kAbsUp);
}
TEST(UnitTestSourceEngine, PerpectiveProjectionAtCenter)
{
constexpr auto fov = omath::projection::FieldOfView::FromDegrees(90.f);
auto cam = omath::source::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f);
const auto viewProjMatrix = cam.GetViewProjectionMatrix();
for (float distance = 0.02f; distance < 1000.f; distance += 0.01f)
{
const auto projected = cam.WorldToScreen(viewProjMatrix, {distance, 0, 0});
EXPECT_TRUE(projected.has_value());
if (!projected.has_value())
continue;
EXPECT_NEAR(projected->x, 960, 0.00001f);
EXPECT_NEAR(projected->y, 540, 0.00001f);
}
}

View File

@@ -0,0 +1,3 @@
//
// Created by Orange on 11/27/2024.
//

View File

@@ -0,0 +1,3 @@
//
// Created by Orange on 11/30/2024.
//

View File

@@ -0,0 +1,50 @@
//
// Created by Orange on 11/30/2024.
//
#include <gtest/gtest.h>
#include <omath/Angles.hpp>
#include <omath/Angle.hpp>
TEST(UnitTestAngles, RadiansToDeg)
{
constexpr float rad = 67;
EXPECT_NEAR(omath::angles::RadiansToDegrees(rad), 3838.82f, 0.01f);
}
TEST(UnitTestAngles, DegreesToRadians)
{
constexpr float degree = 90;
EXPECT_NEAR(omath::angles::DegreesToRadians(degree), 1.5708f, 0.01f);
}
TEST(UnitTestAngles, HorizontalFovToVerical)
{
constexpr float hFov = 90;
constexpr float aspectRation = 16.0f / 9.0f;
const auto verticalFov = omath::angles::HorizontalFovToVertical(hFov, aspectRation);
EXPECT_NEAR(verticalFov, 58.71f, 0.01f);
}
TEST(UnitTestAngles, VerticalToHorizontal)
{
constexpr float vFov = 58.71;
constexpr float aspectRation = 16.0f / 9.0f;
const auto horizontalFov = omath::angles::VerticalFovToHorizontal(vFov, aspectRation);
EXPECT_NEAR(horizontalFov, 89.99f, 0.01f);
}
TEST(UnitTestAngles, WrapAngle)
{
const float wrapped = omath::angles::WrapAngle(361.f, 0.f, 360.f);
EXPECT_NEAR(wrapped, 1.f, 0.01f);
}
TEST(UnitTestAngles, WrapAngleNegativeRange)
{
const float wrapped = omath::angles::WrapAngle(-90.f, 0.f, 360.f);
EXPECT_NEAR(wrapped, 270.f, 0.01f);
}

View File

@@ -154,35 +154,6 @@ TEST_F(UnitTestMat, StaticMethod_ToScreenMat)
EXPECT_FLOAT_EQ(screenMat.At(3, 3), 1.0f); EXPECT_FLOAT_EQ(screenMat.At(3, 3), 1.0f);
} }
// Test static method: TranslationMat
TEST_F(UnitTestMat, StaticMethod_TranslationMat)
{
Vector3 diff{10.0f, 20.0f, 30.0f};
Mat<4, 4> transMat = Mat<4, 4>::TranslationMat(diff);
EXPECT_FLOAT_EQ(transMat.At(0, 0), 1.0f);
EXPECT_FLOAT_EQ(transMat.At(3, 0), diff.x);
EXPECT_FLOAT_EQ(transMat.At(3, 1), diff.y);
EXPECT_FLOAT_EQ(transMat.At(3, 2), diff.z);
EXPECT_FLOAT_EQ(transMat.At(3, 3), 1.0f);
}
// Test static method: OrientationMat
TEST_F(UnitTestMat, StaticMethod_OrientationMat)
{
constexpr Vector3 forward{0.0f, 0.0f, 1.0f};
constexpr Vector3 right{1.0f, 0.0f, 0.0f};
constexpr Vector3 up{0.0f, 1.0f, 0.0f};
constexpr Mat<4, 4> orientMat = Mat<4, 4>::OrientationMat(forward, right, up);
EXPECT_FLOAT_EQ(orientMat.At(0, 0), right.x);
EXPECT_FLOAT_EQ(orientMat.At(0, 1), up.x);
EXPECT_FLOAT_EQ(orientMat.At(0, 2), forward.x);
EXPECT_FLOAT_EQ(orientMat.At(1, 0), right.y);
EXPECT_FLOAT_EQ(orientMat.At(1, 1), up.y);
EXPECT_FLOAT_EQ(orientMat.At(1, 2), forward.y);
EXPECT_FLOAT_EQ(orientMat.At(2, 0), right.z);
EXPECT_FLOAT_EQ(orientMat.At(2, 1), up.z);
EXPECT_FLOAT_EQ(orientMat.At(2, 2), forward.z);
}
// Test exception handling in At() method // Test exception handling in At() method
TEST_F(UnitTestMat, Method_At_OutOfRange) TEST_F(UnitTestMat, Method_At_OutOfRange)

View File

@@ -3,8 +3,9 @@
TEST(UnitTestPrediction, PredictionTest) TEST(UnitTestPrediction, PredictionTest)
{ {
const omath::prediction::Target target{.m_origin = {100, 0, 90}, .m_velocity = {0, 0, 0}, .m_isAirborne = false}; constexpr omath::prediction::Target target{
const omath::prediction::Projectile proj = {.m_origin = {3,2,1}, .m_launchSpeed = 5000, .m_gravityScale= 0.4}; .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};
const auto viewPoint = omath::prediction::Engine(400, 1.f / 1000.f, 50, 5.f).MaybeCalculateAimPoint(proj, target); const auto viewPoint = omath::prediction::Engine(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();

View File

@@ -0,0 +1,18 @@
//
// Created by Vlad on 27.08.2024.
//
#include <complex>
#include <gtest/gtest.h>
#include <omath/Matrix.hpp>
#include <omath/engines/Source/Camera.hpp>
#include <omath/projection/Camera.hpp>
#include <print>
TEST(UnitTestProjection, Projection)
{
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);
const auto projected = cam.WorldToScreen(cam.GetViewProjectionMatrix(), {1000, 0, 50});
std::print("{} {} {}", projected->x, projected->y, projected->z);
}

View File

@@ -0,0 +1,4 @@
//
// Created by Orange on 11/30/2024.
//
#include <omath/ViewAngles.hpp>