├── .clang-format ├── .clang-tidy ├── .github └── workflows │ └── cmake-multi-platform.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CMakePresets.json ├── CODE_OF_CONDUCT.md ├── CREDITS.md ├── INSTALL.md ├── LICENSE ├── README.md ├── SECURITY.md ├── cmake └── omathConfig.cmake.in ├── examples ├── CMakeLists.txt └── example_proj_mat_builder.cpp ├── extlibs └── CMakeLists.txt ├── include └── omath │ ├── 3d_primitives │ └── box.hpp │ ├── angle.hpp │ ├── angles.hpp │ ├── collision │ └── line_tracer.hpp │ ├── color.hpp │ ├── engines │ ├── iw_engine │ │ ├── camera.hpp │ │ ├── constants.hpp │ │ └── formulas.hpp │ ├── opengl_engine │ │ ├── camera.hpp │ │ ├── constants.hpp │ │ └── formulas.hpp │ ├── source_engine │ │ ├── camera.hpp │ │ ├── constants.hpp │ │ └── formulas.hpp │ └── unity_engine │ │ ├── camera.hpp │ │ ├── constants.hpp │ │ └── formulas.hpp │ ├── mat.hpp │ ├── matrix.hpp │ ├── pathfinding │ ├── a_star.hpp │ └── navigation_mesh.hpp │ ├── projectile_prediction │ ├── proj_pred_engine.hpp │ ├── proj_pred_engine_avx2.hpp │ ├── proj_pred_engine_legacy.hpp │ ├── projectile.hpp │ └── target.hpp │ ├── projection │ ├── camera.hpp │ └── error_codes.hpp │ ├── triangle.hpp │ ├── vector2.hpp │ ├── vector3.hpp │ ├── vector4.hpp │ └── view_angles.hpp ├── source ├── 3d_primitives │ └── box.cpp ├── collision │ └── line_tracer.cpp ├── engines │ ├── iw_engine │ │ ├── camera.cpp │ │ └── formulas.cpp │ ├── opengl_engine │ │ ├── camera.cpp │ │ └── formulas.cpp │ ├── source_engine │ │ ├── camera.cpp │ │ └── formulas.cpp │ └── unity_engine │ │ ├── camera.cpp │ │ └── formulas.cpp ├── matrix.cpp ├── pathfinding │ ├── a_star.cpp │ └── navigation_mesh.cpp └── projectile_prediction │ ├── proj_pred_engine_avx2.cpp │ ├── proj_pred_engine_legacy.cpp │ └── projectile.cpp ├── tests ├── CMakeLists.txt ├── engines │ ├── unit_test_iw_engine.cpp │ ├── unit_test_open_gl.cpp │ ├── unit_test_source_engine.cpp │ └── unit_test_unity_engine.cpp └── general │ ├── unit_test_a_star.cpp │ ├── unit_test_angle.cpp │ ├── unit_test_angles.cpp │ ├── unit_test_box_primitive.cpp │ ├── unit_test_color.cpp │ ├── unit_test_line_trace.cpp │ ├── unit_test_mat.cpp │ ├── unit_test_matrix.cpp │ ├── unit_test_prediction.cpp │ ├── unit_test_projection.cpp │ ├── unit_test_triangle.cpp │ ├── unit_test_vector2.cpp │ ├── unit_test_vector3.cpp │ ├── unit_test_vector4.cpp │ └── unit_test_view_angles.cpp └── writerside ├── c.list ├── cfg └── buildprofiles.xml ├── images ├── completion_procedure.png ├── completion_procedure_dark.png ├── convert_table_to_xml.png ├── convert_table_to_xml_dark.png ├── new_topic_options.png └── new_topic_options_dark.png ├── o.tree ├── redirection-rules.xml ├── topics ├── Code-Of-Conduct.md ├── Community.md ├── Documentation.md ├── License.md └── starter-topic.md ├── v.list └── writerside.cfg /.clang-format: -------------------------------------------------------------------------------- 1 | # Generated by CLion for Stroustrup 2 | # The Stroustrup style, named after Bjarne Stroustrup, the creator of C++, is similar to the K&R style but differs 3 | # in its treatment of the class definitions and the placement of braces in certain contexts. The opening brace is 4 | # placed on the same line as the control statement, and the closing brace is on its own line. 5 | BasedOnStyle: LLVM 6 | 7 | AccessModifierOffset: -4 8 | AlignConsecutiveAssignments: None 9 | AlignConsecutiveBitFields: None 10 | AlignConsecutiveDeclarations: None 11 | AlignConsecutiveMacros: AcrossEmptyLinesAndComments 12 | AlignTrailingComments: false 13 | AllowShortBlocksOnASingleLine: Never 14 | AllowShortFunctionsOnASingleLine: None 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | BreakTemplateDeclarations: Leave 18 | BreakBeforeBraces: Custom 19 | BraceWrapping: 20 | AfterCaseLabel: true 21 | AfterClass: true 22 | AfterFunction: true 23 | AfterControlStatement: true 24 | SplitEmptyFunction: true 25 | AfterEnum: true 26 | AfterNamespace: true 27 | AfterStruct: true 28 | AfterUnion: true 29 | AfterExternBlock: true 30 | BeforeCatch: true 31 | BeforeElse: true 32 | BeforeLambdaBody: true 33 | BeforeWhile: true 34 | SplitEmptyRecord: true 35 | SplitEmptyNamespace: true 36 | BreakBeforeBinaryOperators: NonAssignment 37 | BreakBeforeConceptDeclarations: false 38 | ColumnLimit: 120 39 | IncludeBlocks: Merge 40 | IndentExternBlock: Indent 41 | IndentRequiresClause: false 42 | IndentWidth: 4 43 | ContinuationIndentWidth: 8 44 | KeepEmptyLinesAtTheStartOfBlocks: false 45 | NamespaceIndentation: All 46 | PointerAlignment: Left 47 | SortUsingDeclarations: true 48 | SpaceAfterTemplateKeyword: false 49 | SpaceBeforeCtorInitializerColon: false 50 | SpaceBeforeParens: Custom 51 | SpaceBeforeParensOptions: 52 | AfterControlStatements: true 53 | AfterFunctionDeclarationName: false 54 | AfterFunctionDefinitionName: false 55 | AfterForeachMacros: true 56 | AfterIfMacros: true 57 | AfterOverloadedOperator: false 58 | BeforeNonEmptyParentheses: false 59 | SpaceBeforeRangeBasedForLoopColon: false 60 | SpaceInEmptyParentheses: false 61 | SpacesInCStyleCastParentheses: false 62 | SpacesInConditionalStatement: false 63 | SpacesInContainerLiterals: false 64 | SpacesInParentheses: false -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | # Generated from CLion Inspection settings 2 | --- 3 | Checks: '-*, 4 | bugprone-argument-comment, 5 | bugprone-assert-side-effect, 6 | bugprone-bad-signal-to-kill-thread, 7 | bugprone-branch-clone, 8 | bugprone-copy-constructor-init, 9 | bugprone-dangling-handle, 10 | bugprone-dynamic-static-initializers, 11 | bugprone-fold-init-type, 12 | bugprone-forward-declaration-namespace, 13 | bugprone-forwarding-reference-overload, 14 | bugprone-inaccurate-erase, 15 | bugprone-incorrect-roundings, 16 | bugprone-integer-division, 17 | bugprone-lambda-function-name, 18 | bugprone-macro-parentheses, 19 | bugprone-macro-repeated-side-effects, 20 | bugprone-misplaced-operator-in-strlen-in-alloc, 21 | bugprone-misplaced-pointer-arithmetic-in-alloc, 22 | bugprone-misplaced-widening-cast, 23 | bugprone-move-forwarding-reference, 24 | bugprone-multiple-statement-macro, 25 | bugprone-no-escape, 26 | bugprone-parent-virtual-call, 27 | bugprone-posix-return, 28 | bugprone-reserved-identifier, 29 | bugprone-sizeof-container, 30 | bugprone-sizeof-expression, 31 | bugprone-spuriously-wake-up-functions, 32 | bugprone-string-constructor, 33 | bugprone-string-integer-assignment, 34 | bugprone-string-literal-with-embedded-nul, 35 | bugprone-suspicious-enum-usage, 36 | bugprone-suspicious-include, 37 | bugprone-suspicious-memset-usage, 38 | bugprone-suspicious-missing-comma, 39 | bugprone-suspicious-semicolon, 40 | bugprone-suspicious-string-compare, 41 | bugprone-suspicious-memory-comparison, 42 | bugprone-suspicious-realloc-usage, 43 | bugprone-swapped-arguments, 44 | bugprone-terminating-continue, 45 | bugprone-throw-keyword-missing, 46 | bugprone-too-small-loop-variable, 47 | bugprone-undefined-memory-manipulation, 48 | bugprone-undelegated-constructor, 49 | bugprone-unhandled-self-assignment, 50 | bugprone-unused-raii, 51 | bugprone-unused-return-value, 52 | bugprone-use-after-move, 53 | bugprone-virtual-near-miss, 54 | cert-dcl21-cpp, 55 | cert-dcl58-cpp, 56 | cert-err34-c, 57 | cert-err52-cpp, 58 | cert-err60-cpp, 59 | cert-flp30-c, 60 | cert-msc50-cpp, 61 | cert-msc51-cpp, 62 | cert-str34-c, 63 | cppcoreguidelines-interfaces-global-init, 64 | cppcoreguidelines-narrowing-conversions, 65 | cppcoreguidelines-pro-type-member-init, 66 | cppcoreguidelines-pro-type-static-cast-downcast, 67 | cppcoreguidelines-slicing, 68 | google-default-arguments, 69 | google-explicit-constructor, 70 | google-runtime-operator, 71 | hicpp-exception-baseclass, 72 | hicpp-multiway-paths-covered, 73 | misc-misplaced-const, 74 | misc-new-delete-overloads, 75 | misc-no-recursion, 76 | misc-non-copyable-objects, 77 | misc-throw-by-value-catch-by-reference, 78 | misc-unconventional-assign-operator, 79 | misc-uniqueptr-reset-release, 80 | modernize-avoid-bind, 81 | modernize-concat-nested-namespaces, 82 | modernize-deprecated-headers, 83 | modernize-deprecated-ios-base-aliases, 84 | modernize-loop-convert, 85 | modernize-make-shared, 86 | modernize-make-unique, 87 | modernize-pass-by-value, 88 | modernize-raw-string-literal, 89 | modernize-redundant-void-arg, 90 | modernize-replace-auto-ptr, 91 | modernize-replace-disallow-copy-and-assign-macro, 92 | modernize-replace-random-shuffle, 93 | modernize-return-braced-init-list, 94 | modernize-shrink-to-fit, 95 | modernize-unary-static-assert, 96 | modernize-use-auto, 97 | modernize-use-bool-literals, 98 | modernize-use-emplace, 99 | modernize-use-equals-default, 100 | modernize-use-equals-delete, 101 | modernize-use-nodiscard, 102 | modernize-use-noexcept, 103 | modernize-use-nullptr, 104 | modernize-use-override, 105 | modernize-use-transparent-functors, 106 | modernize-use-uncaught-exceptions, 107 | mpi-buffer-deref, 108 | mpi-type-mismatch, 109 | openmp-use-default-none, 110 | performance-faster-string-find, 111 | performance-for-range-copy, 112 | performance-implicit-conversion-in-loop, 113 | performance-inefficient-algorithm, 114 | performance-inefficient-string-concatenation, 115 | performance-inefficient-vector-operation, 116 | performance-move-const-arg, 117 | performance-move-constructor-init, 118 | performance-no-automatic-move, 119 | performance-noexcept-move-constructor, 120 | performance-trivially-destructible, 121 | performance-type-promotion-in-math-fn, 122 | performance-unnecessary-copy-initialization, 123 | performance-unnecessary-value-param, 124 | portability-simd-intrinsics, 125 | readability-avoid-const-params-in-decls, 126 | readability-const-return-type, 127 | readability-container-size-empty, 128 | readability-convert-member-functions-to-static, 129 | readability-delete-null-pointer, 130 | readability-deleted-default, 131 | readability-inconsistent-declaration-parameter-name, 132 | readability-make-member-function-const, 133 | readability-misleading-indentation, 134 | readability-misplaced-array-index, 135 | readability-non-const-parameter, 136 | readability-redundant-control-flow, 137 | readability-redundant-declaration, 138 | readability-redundant-function-ptr-dereference, 139 | readability-redundant-smartptr-get, 140 | readability-redundant-string-cstr, 141 | readability-redundant-string-init, 142 | readability-simplify-subscript-expr, 143 | readability-static-accessed-through-instance, 144 | readability-static-definition-in-anonymous-namespace, 145 | readability-string-compare, 146 | readability-uniqueptr-delete-release, 147 | readability-use-anyofallof' -------------------------------------------------------------------------------- /.github/workflows/cmake-multi-platform.yml: -------------------------------------------------------------------------------- 1 | name: Omath CI (Arch Linux / Windows) 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | concurrency: 10 | group: ci-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | 14 | ############################################################################## 15 | # 1) ARCH LINUX – Clang / Ninja 16 | ############################################################################## 17 | jobs: 18 | arch-build-and-test: 19 | name: Arch Linux (Clang) 20 | runs-on: ubuntu-latest 21 | container: archlinux:latest 22 | 23 | steps: 24 | - name: Install basic tool-chain with pacman 25 | shell: bash 26 | run: | 27 | pacman -Sy --noconfirm archlinux-keyring 28 | pacman -Syu --noconfirm --needed \ 29 | git base-devel clang cmake ninja 30 | 31 | - name: Checkout repository (with sub-modules) 32 | uses: actions/checkout@v4 33 | with: 34 | submodules: recursive 35 | 36 | - name: Configure (cmake --preset) 37 | shell: bash 38 | run: cmake --preset linux-release -DOMATH_BUILD_TESTS=ON 39 | 40 | - name: Build 41 | shell: bash 42 | run: cmake --build cmake-build/build/linux-release --target all 43 | 44 | - name: Run unit_tests 45 | shell: bash 46 | run: ./out/Release/unit_tests 47 | 48 | 49 | 50 | ############################################################################## 51 | # 2) Windows – MSVC / Ninja 52 | ############################################################################## 53 | windows-build-and-test: 54 | name: Windows (MSVC) 55 | runs-on: windows-latest 56 | 57 | steps: 58 | - name: Checkout repository (with sub-modules) 59 | uses: actions/checkout@v4 60 | with: 61 | submodules: recursive 62 | 63 | - name: Install Ninja 64 | uses: seanmiddleditch/gha-setup-ninja@v4 65 | 66 | - name: Set up MSVC developer command-prompt 67 | uses: ilammy/msvc-dev-cmd@v1 68 | 69 | - name: Configure (cmake --preset) 70 | shell: bash 71 | run: cmake --preset windows-release -DOMATH_BUILD_TESTS=ON 72 | 73 | - name: Build 74 | shell: bash 75 | run: cmake --build cmake-build/build/windows-release --target all 76 | 77 | - name: Run unit_tests.exe 78 | shell: bash 79 | run: ./out/Release/unit_tests.exe 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /cmake-build/ 2 | /.idea 3 | /out 4 | *.DS_Store -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extlibs/googletest"] 2 | path = extlibs/googletest 3 | url = https://github.com/google/googletest.git -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.26) 2 | 3 | project(omath VERSION 3.0.2 LANGUAGES CXX) 4 | 5 | include(CMakePackageConfigHelpers) 6 | 7 | 8 | option(OMATH_BUILD_TESTS "Build unit tests" OFF) 9 | option(OMATH_THREAT_WARNING_AS_ERROR "Set highest level of warnings and force compiler to treat them as errors" ON) 10 | option(OMATH_BUILD_AS_SHARED_LIBRARY "Build Omath as .so or .dll" OFF) 11 | option(OMATH_USE_AVX2 "Omath will use AVX2 to boost performance" ON) 12 | option(OMATH_IMGUI_INTEGRATION "Omath will define method to convert omath types to imgui types" OFF) 13 | option(OMATH_BUILD_EXAMPLES "Build example projects with you can learn & play" OFF) 14 | option(OMATH_STATIC_MSVC_RUNTIME_LIBRARY "Force Omath to link static runtime" OFF) 15 | option(OMATH_SUPRESS_SAFETY_CHECKS "Supress some safety checks in release build to improve general performance" ON) 16 | option(OMATH_USE_UNITY_BUILD "Will enable unity build to speed up compilation" ON) 17 | 18 | file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") 19 | file(GLOB_RECURSE OMATH_HEADERS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp") 20 | 21 | 22 | if (OMATH_BUILD_AS_SHARED_LIBRARY) 23 | add_library(omath SHARED ${OMATH_SOURCES} ${OMATH_HEADERS}) 24 | else () 25 | add_library(omath STATIC ${OMATH_SOURCES} ${OMATH_HEADERS}) 26 | endif () 27 | 28 | message(STATUS "Building on ${CMAKE_HOST_SYSTEM_NAME}") 29 | add_library(omath::omath ALIAS omath) 30 | 31 | if (OMATH_IMGUI_INTEGRATION) 32 | target_compile_definitions(omath PUBLIC OMATH_IMGUI_INTEGRATION) 33 | 34 | # IMGUI is being linked as submodule 35 | if (TARGET imgui) 36 | target_link_libraries(omath PUBLIC imgui) 37 | install(TARGETS imgui 38 | EXPORT omathTargets 39 | ARCHIVE DESTINATION lib 40 | LIBRARY DESTINATION lib 41 | RUNTIME DESTINATION bin) 42 | else () 43 | # Assume that IMGUI linked via VCPKG. 44 | find_package(imgui CONFIG REQUIRED) 45 | target_link_libraries(omath PUBLIC imgui::imgui) 46 | endif () 47 | 48 | endif () 49 | 50 | if (OMATH_USE_AVX2) 51 | target_compile_definitions(omath PUBLIC OMATH_USE_AVX2) 52 | endif () 53 | 54 | if (OMATH_SUPRESS_SAFETY_CHECKS) 55 | target_compile_definitions(omath PUBLIC OMATH_SUPRESS_SAFETY_CHECKS) 56 | endif () 57 | 58 | set_target_properties(omath PROPERTIES 59 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}" 60 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}" 61 | CXX_STANDARD 23 62 | CXX_STANDARD_REQUIRED ON) 63 | 64 | if (OMATH_USE_UNITY_BUILD) 65 | set_target_properties(omath PROPERTIES 66 | UNITY_BUILD ON 67 | UNITY_BUILD_BATCH_SIZE 20) 68 | endif () 69 | 70 | if (OMATH_STATIC_MSVC_RUNTIME_LIBRARY) 71 | set_target_properties(omath PROPERTIES 72 | MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" 73 | ) 74 | endif () 75 | 76 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 77 | target_compile_options(omath PRIVATE -mavx2 -mfma) 78 | endif () 79 | 80 | target_compile_features(omath PUBLIC cxx_std_23) 81 | 82 | 83 | if (OMATH_BUILD_TESTS) 84 | add_subdirectory(extlibs) 85 | add_subdirectory(tests) 86 | endif () 87 | 88 | if (OMATH_BUILD_EXAMPLES) 89 | add_subdirectory(examples) 90 | endif () 91 | 92 | if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND OMATH_THREAT_WARNING_AS_ERROR) 93 | target_compile_options(omath PRIVATE /W4 /WX) 94 | elseif (OMATH_THREAT_WARNING_AS_ERROR) 95 | target_compile_options(omath PRIVATE -Wall -Wextra -Wpedantic -Werror) 96 | endif () 97 | 98 | target_include_directories(omath 99 | PUBLIC 100 | $ # Use this path when building the project 101 | $ # Use this path when the project is installed 102 | ) 103 | 104 | 105 | # Installation rules 106 | 107 | # Install the library 108 | install(TARGETS omath 109 | EXPORT omathTargets 110 | ARCHIVE DESTINATION lib COMPONENT omath # For static libraries 111 | LIBRARY DESTINATION lib COMPONENT omath # For shared libraries 112 | RUNTIME DESTINATION bin COMPONENT omath # For executables (on Windows) 113 | ) 114 | 115 | # Install headers as part of omath_component 116 | install(DIRECTORY include/ DESTINATION include COMPONENT omath) 117 | 118 | # Export omath target for CMake find_package support, also under omath_component 119 | install(EXPORT omathTargets 120 | FILE omathTargets.cmake 121 | NAMESPACE omath:: 122 | DESTINATION lib/cmake/omath COMPONENT omath 123 | ) 124 | 125 | 126 | # Generate the omathConfigVersion.cmake file 127 | write_basic_package_version_file( 128 | "${CMAKE_CURRENT_BINARY_DIR}/omathConfigVersion.cmake" 129 | VERSION ${PROJECT_VERSION} 130 | COMPATIBILITY AnyNewerVersion 131 | ) 132 | 133 | # Generate the omathConfig.cmake file from the template (which is in the cmake/ folder) 134 | configure_package_config_file( 135 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/omathConfig.cmake.in" # Path to the .in file 136 | "${CMAKE_CURRENT_BINARY_DIR}/omathConfig.cmake" # Output path for the generated file 137 | INSTALL_DESTINATION lib/cmake/omath 138 | ) 139 | 140 | # Install the generated config files 141 | install(FILES 142 | "${CMAKE_CURRENT_BINARY_DIR}/omathConfig.cmake" 143 | "${CMAKE_CURRENT_BINARY_DIR}/omathConfigVersion.cmake" 144 | DESTINATION lib/cmake/omath 145 | ) 146 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "windows-base", 6 | "hidden": true, 7 | "generator": "Ninja", 8 | "binaryDir": "${sourceDir}/cmake-build/build/${presetName}", 9 | "installDir": "${sourceDir}/cmake-build/install/${presetName}", 10 | "cacheVariables": { 11 | "CMAKE_C_COMPILER": "cl.exe", 12 | "CMAKE_CXX_COMPILER": "cl.exe" 13 | }, 14 | "condition": { 15 | "type": "equals", 16 | "lhs": "${hostSystemName}", 17 | "rhs": "Windows" 18 | } 19 | }, 20 | { 21 | "name": "windows-debug", 22 | "displayName": "Debug", 23 | "inherits": "windows-base", 24 | "cacheVariables": { 25 | "CMAKE_BUILD_TYPE": "Debug" 26 | } 27 | }, 28 | { 29 | "name": "windows-release", 30 | "displayName": "Release", 31 | "inherits": "windows-base", 32 | "cacheVariables": { 33 | "CMAKE_BUILD_TYPE": "Release" 34 | } 35 | }, 36 | { 37 | "name": "linux-base", 38 | "hidden": true, 39 | "generator": "Ninja", 40 | "binaryDir": "${sourceDir}/cmake-build/build/${presetName}", 41 | "installDir": "${sourceDir}/cmake-build/install/${presetName}", 42 | "cacheVariables": { 43 | "CMAKE_C_COMPILER": "clang", 44 | "CMAKE_CXX_COMPILER": "clang++" 45 | }, 46 | "condition": { 47 | "type": "equals", 48 | "lhs": "${hostSystemName}", 49 | "rhs": "Linux" 50 | } 51 | }, 52 | { 53 | "name": "linux-debug", 54 | "displayName": "Linux Debug", 55 | "inherits": "linux-base", 56 | "cacheVariables": { 57 | "CMAKE_BUILD_TYPE": "Debug" 58 | } 59 | }, 60 | { 61 | "name": "linux-release", 62 | "displayName": "Linux Release", 63 | "inherits": "linux-debug", 64 | "cacheVariables": { 65 | "CMAKE_BUILD_TYPE": "Release" 66 | } 67 | }, 68 | { 69 | "name": "darwin-base", 70 | "hidden": true, 71 | "generator": "Ninja", 72 | "binaryDir": "${sourceDir}/cmake-build/build/${presetName}", 73 | "installDir": "${sourceDir}/cmake-build/install/${presetName}", 74 | "cacheVariables": { 75 | "CMAKE_C_COMPILER": "clang", 76 | "CMAKE_CXX_COMPILER": "clang++" 77 | }, 78 | "condition": { 79 | "type": "equals", 80 | "lhs": "${hostSystemName}", 81 | "rhs": "Darwin" 82 | } 83 | }, 84 | { 85 | "name": "darwin-debug", 86 | "displayName": "Darwin Debug", 87 | "inherits": "darwin-base", 88 | "cacheVariables": { 89 | "CMAKE_BUILD_TYPE": "Debug" 90 | } 91 | }, 92 | { 93 | "name": "darwin-release", 94 | "displayName": "Darwin Release", 95 | "inherits": "darwin-debug", 96 | "cacheVariables": { 97 | "CMAKE_BUILD_TYPE": "Release" 98 | } 99 | } 100 | ] 101 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## 🎯 Goal 2 | 3 | My goal is to provide a space where it is safe for everyone to contribute to, 4 | and get support for, open-source software in a respectful and cooperative 5 | manner. 6 | 7 | I value all contributions and want to make this project and its 8 | surrounding community a place for everyone. 9 | 10 | As members, contributors, and everyone else who may participate in the 11 | development, I strive to keep the entire experience civil. 12 | 13 | ## 📜 Standards 14 | 15 | Our community standards exist in order to make sure everyone feels comfortable 16 | contributing to the project(s) together. 17 | 18 | Our standards are: 19 | - Do not harass, attack, or in any other way discriminate against anyone, including 20 | for their protected traits, including, but not limited to, sex, religion, race, 21 | appearance, gender, identity, nationality, sexuality, etc. 22 | - Do not go off-topic, do not post spam. 23 | - Treat everyone with respect. 24 | 25 | Examples of breaking each rule respectively include: 26 | - Harassment, bullying or inappropriate jokes about another person. 27 | - Posting distasteful imagery, trolling, or posting things unrelated to the topic at hand. 28 | - Treating someone as worse because of their lack of understanding of an issue. 29 | 30 | ## ⚡ Enforcement 31 | 32 | Enforcement of this CoC is done by Orange++ and/or other core contributors. 33 | 34 | I, as the core developer, will strive my best to keep this community civil and 35 | following the standards outlined above. 36 | 37 | ### 🚩 Reporting incidents 38 | 39 | If you believe an incident of breaking these standards has occurred, but nobody has 40 | taken appropriate action, you can privately contact the people responsible for dealing 41 | with such incidents in multiple ways: 42 | 43 | ***E-Mail*** 44 | - `orange-cpp@yandex.ru` 45 | 46 | ***Discord*** 47 | - `@orange_cpp` 48 | 49 | ***Telegram*** 50 | - `@orange_cpp` 51 | 52 | I guarantee your privacy and will not share those reports with anyone. 53 | 54 | ## ⚖️ Enforcement Strategy 55 | 56 | Depending on the severity of the infraction, any action from the list below may be applied. 57 | Please keep in mind cases are reviewed on a per-case basis and members are the ultimate 58 | deciding factor in the type of punishment. 59 | 60 | If the matter benefited from an outside opinion, a member might reach for more opinions 61 | from people unrelated, however, the final decision regarding the action 62 | to be taken is still up to the member. 63 | 64 | For example, if the matter at hand regards a representative of a marginalized group or minority, 65 | the member might ask for a first-hand opinion from another representative of such group. 66 | 67 | ### ✏️ Correction/Edit 68 | 69 | If your message is found to be misleading or poorly worded, a member might 70 | edit your message. 71 | 72 | ### ⚠️ Warning/Deletion 73 | 74 | If your message is found inappropriate, a member might give you a public or private warning, 75 | and/or delete your message. 76 | 77 | ### 🔇 Mute 78 | 79 | If your message is disruptive, or you have been repeatedly violating the standards, 80 | a member might mute (or temporarily ban) you. 81 | 82 | ### ⛔ Ban 83 | 84 | If your message is hateful, very disruptive, or other, less serious infractions are repeated 85 | ignoring previous punishments, a member might ban you permanently. 86 | 87 | ## 🔎 Scope 88 | 89 | This CoC shall apply to all projects ran under the Orange++ lead and all _official_ communities 90 | outside of GitHub. 91 | 92 | However, it is worth noting that official communities outside of GitHub might have their own, 93 | additional sets of rules. -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | # OMATH CREDITS 2 | 3 | Thanks to everyone who made this possible, including: 4 | 5 | - Saikari aka luadebug for VCPKG port. 6 | 7 | And a big hand to everyone else who has contributed over the past! 8 | 9 | THANKS! <3 10 | 11 | -- Orange++ -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # 📥Installation Guide 2 | 3 | ## Using vcpkg 4 | **Note**: Support vcpkg for package management 5 | 1. Install [vcpkg](https://github.com/microsoft/vcpkg) 6 | 2. Run the following command to install the orange-math package: 7 | ``` 8 | vcpkg install orange-math 9 | ``` 10 | CMakeLists.txt 11 | ```cmake 12 | find_package(omath CONFIG REQUIRED) 13 | target_link_libraries(main PRIVATE omath::omath) 14 | ``` 15 | 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). 16 | 17 | ## Build from source using CMake 18 | 1. **Preparation** 19 | 20 | Install needed tools: cmake, clang, git, msvc (windows only). 21 | 22 | 1. **Linux:** 23 | ```bash 24 | sudo pacman -Sy cmake ninja clang git 25 | ``` 26 | 2. **MacOS:** 27 | ```bash 28 | brew install llvm git cmake ninja 29 | ``` 30 | 3. **Windows:** 31 | 32 | Install Visual Studio from [here](https://visualstudio.microsoft.com/downloads/) and Git from [here](https://git-scm.com/downloads). 33 | 34 | Use x64 Native Tools shell to execute needed commands down below. 35 | 2. **Clone the repository:** 36 | ```bash 37 | git clone https://github.com/orange-cpp/omath.git 38 | ``` 39 | 3. **Navigate to the project directory:** 40 | ```bash 41 | cd omath 42 | ``` 43 | 4. **Build the project using CMake:** 44 | ```bash 45 | cmake --preset windows-release -S . 46 | cmake --build cmake-build/build/windows-release --target omath -j 6 47 | ``` 48 | Use **\-\** preset to build siutable version for yourself. Like **windows-release** or **linux-release**. 49 | 50 | | Platform Name | Build Config | 51 | |---------------|---------------| 52 | | windows | release/debug | 53 | | linux | release/debug | 54 | | darwin | release/debug | 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2024-2025 Orange++ 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![banner](https://i.imgur.com/SM9ccP6.png) 4 | 5 | ![GitHub License](https://img.shields.io/github/license/orange-cpp/omath) 6 | ![GitHub contributors](https://img.shields.io/github/contributors/orange-cpp/omath) 7 | ![GitHub top language](https://img.shields.io/github/languages/top/orange-cpp/omath) 8 | [![CodeFactor](https://www.codefactor.io/repository/github/orange-cpp/omath/badge)](https://www.codefactor.io/repository/github/orange-cpp/omath) 9 | ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/orange-cpp/omath/cmake-multi-platform.yml) 10 | ![GitHub forks](https://img.shields.io/github/forks/orange-cpp/omath) 11 |
12 | 13 | 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. 14 | 15 | ## 👁‍🗨 Features 16 | - **Efficiency**: Optimized for performance, ensuring quick computations using AVX2. 17 | - **Versatility**: Includes a wide array of mathematical functions and algorithms. 18 | - **Ease of Use**: Simplified interface for convenient integration into various projects. 19 | - **Projectile Prediction**: Projectile prediction engine with O(N) algo complexity, that can power you projectile aim-bot. 20 | - **3D Projection**: No need to find view-projection matrix anymore you can make your own projection pipeline. 21 | - **Collision Detection**: Production ready code to handle collision detection by using simple interfaces. 22 | - **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution 23 | - **Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types! 24 | 25 | ## Supported Render Pipelines 26 | | ENGINE | SUPPORT | 27 | |----------|---------| 28 | | Source | ✅YES | 29 | | Unity | ✅YES | 30 | | IWEngine | ✅YES | 31 | | Unreal | ❌NO | 32 | 33 | ## Supported Operating Systems 34 | 35 | | OS | SUPPORT | 36 | |----------------|---------| 37 | | Windows 10/11 | ✅YES | 38 | | Linux | ✅YES | 39 | | Darwin (MacOS) | ✅YES | 40 | 41 | ## ⏬ Installation 42 | Please read our [installation guide](https://github.com/orange-cpp/omath/blob/main/INSTALL.md). If this link doesn't work check out INSTALL.md file. 43 | 44 | ## ❔ Usage 45 | Simple world to screen function 46 | ```c++ 47 | TEST(UnitTestProjection, IsPointOnScreen) 48 | { 49 | const omath::projection::Camera camera({0.f, 0.f, 0.f}, {0, 0.f, 0.f} , {1920.f, 1080.f}, 110.f, 0.1f, 500.f); 50 | 51 | const auto proj = camera.WorldToScreen({100, 0, 15}); 52 | EXPECT_TRUE(proj.has_value()); 53 | } 54 | ``` 55 | ## Showcase 56 |
57 | OMATH for making cheats (click to open) 58 | 59 | With `omath/projection` module you can achieve simple ESP hack for powered by Source/Unreal/Unity engine games, like [Apex Legends](https://store.steampowered.com/app/1172470/Apex_Legends/). 60 | 61 | ![banner](https://i.imgur.com/lcJrfcZ.png) 62 | Or for InfinityWard Engine based games. Like Call of Duty Black Ops 2! 63 | ![banner](https://i.imgur.com/F8dmdoo.png) 64 | Or create simple trigger bot with embeded traceline from omath::collision::LineTrace 65 | ![banner](https://i.imgur.com/fxMjRKo.jpeg) 66 | Or even advanced projectile aimbot 67 | [Watch Video](https://youtu.be/lM_NJ1yCunw?si=5E87OrQMeypxSJ3E) 68 |
69 | 70 | ## 🫵🏻 Contributing 71 | Contributions to `omath` are welcome! Please read `CONTRIBUTING.md` for details on our code of conduct and the process for submitting pull requests. 72 | 73 | ## 📜 License 74 | This project is licensed under the MIT - see the `LICENSE` file for details. 75 | 76 | ## 💘 Acknowledgments 77 | - [All contributors](https://github.com/orange-cpp/omath/graphs/contributors) 78 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please report security issues to `orange-cpp@yandex.ru` -------------------------------------------------------------------------------- /cmake/omathConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | 5 | # Load the targets for the omath library 6 | include("${CMAKE_CURRENT_LIST_DIR}/omathTargets.cmake") 7 | check_required_components(omath) 8 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(examples) 2 | 3 | add_executable(ExampleProjectionMatrixBuilder example_proj_mat_builder.cpp) 4 | target_link_libraries(ExampleProjectionMatrixBuilder PRIVATE omath::omath) -------------------------------------------------------------------------------- /examples/example_proj_mat_builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/19/2025. 3 | // 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | int main() 12 | { 13 | std::println("OMATH Projection Matrix Builder"); 14 | 15 | float fov = 0; 16 | float near = 0; 17 | float far = 0; 18 | float viewPortWidth = 0; 19 | float viewPortHeight = 0; 20 | 21 | std::print("Enter camera fov: "); 22 | std::cin >> fov; 23 | 24 | std::print("Enter camera z near: "); 25 | std::cin >> near; 26 | 27 | std::print("Enter camera z far: "); 28 | std::cin >> far; 29 | 30 | std::print("Enter camera screen width: "); 31 | std::cin >> viewPortWidth; 32 | 33 | std::print("Enter camera screen height: "); 34 | std::cin >> viewPortHeight; 35 | 36 | const auto mat = 37 | omath::opengl_engine::CalcPerspectiveProjectionMatrix(fov, viewPortWidth / viewPortHeight, near, far); 38 | 39 | std::print("{}", mat.ToString()); 40 | }; 41 | -------------------------------------------------------------------------------- /extlibs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(googletest) -------------------------------------------------------------------------------- /include/omath/3d_primitives/box.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 4/18/2025. 3 | // 4 | 5 | #pragma once 6 | #include 7 | #include "omath/triangle.hpp" 8 | #include "omath/vector3.hpp" 9 | 10 | 11 | namespace omath::primitives 12 | { 13 | [[nodiscard]] 14 | std::array>, 12> create_box(const Vector3& top, const Vector3& bottom, 15 | const Vector3& dir_forward, const Vector3& dir_right, 16 | float ratio = 4.f) noexcept; 17 | } 18 | -------------------------------------------------------------------------------- /include/omath/angle.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/30/2024. 3 | // 4 | 5 | #pragma once 6 | #include "omath/angles.hpp" 7 | #include 8 | #include 9 | 10 | namespace omath 11 | { 12 | enum class AngleFlags 13 | { 14 | Normalized = 0, 15 | Clamped = 1, 16 | }; 17 | 18 | template 19 | requires std::is_arithmetic_v 20 | class Angle 21 | { 22 | Type m_angle; 23 | constexpr explicit Angle(const Type& degrees) noexcept 24 | { 25 | if constexpr (flags == AngleFlags::Normalized) 26 | m_angle = angles::wrap_angle(degrees, min, max); 27 | 28 | else if constexpr (flags == AngleFlags::Clamped) 29 | m_angle = std::clamp(degrees, min, max); 30 | else 31 | { 32 | static_assert(false); 33 | std::unreachable(); 34 | } 35 | } 36 | 37 | public: 38 | [[nodiscard]] 39 | constexpr static Angle from_degrees(const Type& degrees) noexcept 40 | { 41 | return Angle{degrees}; 42 | } 43 | constexpr Angle() noexcept: m_angle(0) 44 | { 45 | } 46 | [[nodiscard]] 47 | constexpr static Angle from_radians(const Type& degrees) noexcept 48 | { 49 | return Angle{angles::radians_to_degrees(degrees)}; 50 | } 51 | 52 | [[nodiscard]] 53 | constexpr const Type& operator*() const noexcept 54 | { 55 | return m_angle; 56 | } 57 | 58 | [[nodiscard]] 59 | constexpr Type as_degrees() const noexcept 60 | { 61 | return m_angle; 62 | } 63 | 64 | [[nodiscard]] 65 | constexpr Type as_radians() const noexcept 66 | { 67 | return angles::degrees_to_radians(m_angle); 68 | } 69 | 70 | [[nodiscard]] 71 | Type sin() const noexcept 72 | { 73 | return std::sin(as_radians()); 74 | } 75 | 76 | [[nodiscard]] 77 | Type cos() const noexcept 78 | { 79 | return std::cos(as_radians()); 80 | } 81 | 82 | [[nodiscard]] 83 | Type tan() const noexcept 84 | { 85 | return std::tan(as_radians()); 86 | } 87 | 88 | [[nodiscard]] 89 | Type atan() const noexcept 90 | { 91 | return std::atan(as_radians()); 92 | } 93 | 94 | [[nodiscard]] 95 | Type cot() const noexcept 96 | { 97 | return cos() / sin(); 98 | } 99 | 100 | constexpr Angle& operator+=(const Angle& other) noexcept 101 | { 102 | if constexpr (flags == AngleFlags::Normalized) 103 | m_angle = angles::wrap_angle(m_angle + other.m_angle, min, max); 104 | 105 | else if constexpr (flags == AngleFlags::Clamped) 106 | m_angle = std::clamp(m_angle + other.m_angle, min, max); 107 | else 108 | { 109 | static_assert(false); 110 | std::unreachable(); 111 | } 112 | 113 | return *this; 114 | } 115 | 116 | [[nodiscard]] 117 | constexpr std::partial_ordering operator<=>(const Angle& other) const noexcept = default; 118 | 119 | constexpr Angle& operator-=(const Angle& other) noexcept 120 | { 121 | return operator+=(-other); 122 | } 123 | 124 | [[nodiscard]] 125 | constexpr Angle& operator+(const Angle& other) noexcept 126 | { 127 | if constexpr (flags == AngleFlags::Normalized) 128 | return {angles::wrap_angle(m_angle + other.m_angle, min, max)}; 129 | 130 | else if constexpr (flags == AngleFlags::Clamped) 131 | return {std::clamp(m_angle + other.m_angle, min, max)}; 132 | 133 | else 134 | static_assert(false); 135 | 136 | std::unreachable(); 137 | } 138 | 139 | [[nodiscard]] 140 | constexpr Angle& operator-(const Angle& other) noexcept 141 | { 142 | return operator+(-other); 143 | } 144 | 145 | [[nodiscard]] 146 | constexpr Angle operator-() const noexcept 147 | { 148 | return Angle{-m_angle}; 149 | } 150 | }; 151 | } // namespace omath 152 | -------------------------------------------------------------------------------- /include/omath/angles.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by vlad on 11/6/23. 3 | // 4 | 5 | #pragma once 6 | #include 7 | #include 8 | 9 | namespace omath::angles 10 | { 11 | template 12 | requires std::is_floating_point_v 13 | [[nodiscard]] constexpr Type radians_to_degrees(const Type& radians) noexcept 14 | { 15 | return radians * (static_cast(180) / std::numbers::pi_v); 16 | } 17 | 18 | template 19 | requires std::is_floating_point_v 20 | [[nodiscard]] constexpr Type degrees_to_radians(const Type& degrees) noexcept 21 | { 22 | return degrees * (std::numbers::pi_v / static_cast(180)); 23 | } 24 | 25 | template 26 | requires std::is_floating_point_v 27 | [[nodiscard]] Type horizontal_fov_to_vertical(const Type& horizontal_fov, const Type& aspect) noexcept 28 | { 29 | const auto fov_rad = degrees_to_radians(horizontal_fov); 30 | 31 | const auto vert_fov = static_cast(2) * std::atan(std::tan(fov_rad / static_cast(2)) / aspect); 32 | 33 | return radians_to_degrees(vert_fov); 34 | } 35 | 36 | template 37 | requires std::is_floating_point_v 38 | [[nodiscard]] Type vertical_fov_to_horizontal(const Type& vertical_fov, const Type& aspect) noexcept 39 | { 40 | const auto fov_as_radians = degrees_to_radians(vertical_fov); 41 | 42 | const auto horizontal_fov = 43 | static_cast(2) * std::atan(std::tan(fov_as_radians / static_cast(2)) * aspect); 44 | 45 | return radians_to_degrees(horizontal_fov); 46 | } 47 | 48 | template 49 | requires std::is_arithmetic_v 50 | [[nodiscard]] Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept 51 | { 52 | if (angle <= max && angle >= min) 53 | return angle; 54 | 55 | const Type range = max - min; 56 | 57 | Type wrapped_angle = std::fmod(angle - min, range); 58 | 59 | if (wrapped_angle < 0) 60 | wrapped_angle += range; 61 | 62 | return wrapped_angle + min; 63 | } 64 | } // namespace omath::angles 65 | -------------------------------------------------------------------------------- /include/omath/collision/line_tracer.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/13/2024. 3 | // 4 | #pragma once 5 | 6 | #include "omath/triangle.hpp" 7 | #include "omath/vector3.hpp" 8 | 9 | namespace omath::collision 10 | { 11 | class Ray 12 | { 13 | public: 14 | Vector3 start; 15 | Vector3 end; 16 | bool infinite_length = false; 17 | 18 | [[nodiscard]] 19 | Vector3 direction_vector() const noexcept; 20 | 21 | [[nodiscard]] 22 | Vector3 direction_vector_normalized() const noexcept; 23 | }; 24 | class LineTracer 25 | { 26 | public: 27 | LineTracer() = delete; 28 | 29 | [[nodiscard]] 30 | static bool can_trace_line(const Ray& ray, const Triangle>& triangle) noexcept; 31 | 32 | // Realization of Möller–Trumbore intersection algorithm 33 | // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm 34 | [[nodiscard]] 35 | static Vector3 get_ray_hit_point(const Ray& ray, const Triangle>& triangle) noexcept; 36 | }; 37 | } // namespace omath::collision 38 | -------------------------------------------------------------------------------- /include/omath/color.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by vlad on 2/4/24. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "omath/vector3.hpp" 8 | #include "omath/vector4.hpp" 9 | #include 10 | 11 | #ifdef max 12 | #undef max 13 | #endif 14 | 15 | #ifdef min 16 | #undef min 17 | #endif 18 | 19 | namespace omath 20 | { 21 | struct Hsv 22 | { 23 | float hue{}; 24 | float saturation{}; 25 | float value{}; 26 | }; 27 | 28 | class Color final : public Vector4 29 | { 30 | public: 31 | constexpr Color(const float r, const float g, const float b, const float a) noexcept: Vector4(r, g, b, a) 32 | { 33 | clamp(0.f, 1.f); 34 | } 35 | 36 | constexpr explicit Color() noexcept = default; 37 | [[nodiscard]] 38 | constexpr static Color from_rgba(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) noexcept 39 | { 40 | return Color{Vector4(r, g, b, a) / 255.f}; 41 | } 42 | 43 | [[nodiscard]] 44 | constexpr static Color from_hsv(float hue, const float saturation, const float value) noexcept 45 | { 46 | float r{}, g{}, b{}; 47 | 48 | hue = std::clamp(hue, 0.f, 1.f); 49 | 50 | const int i = static_cast(hue * 6.f); 51 | const float f = hue * 6.f - static_cast(i); 52 | const float p = value * (1 - saturation); 53 | const float q = value * (1 - f * saturation); 54 | const float t = value * (1 - (1 - f) * saturation); 55 | 56 | switch (i % 6) 57 | { 58 | case 0: 59 | r = value, g = t, b = p; 60 | break; 61 | case 1: 62 | r = q, g = value, b = p; 63 | break; 64 | case 2: 65 | r = p, g = value, b = t; 66 | break; 67 | case 3: 68 | r = p, g = q, b = value; 69 | break; 70 | case 4: 71 | r = t, g = p, b = value; 72 | break; 73 | case 5: 74 | r = value, g = p, b = q; 75 | break; 76 | 77 | default: 78 | return {0.f, 0.f, 0.f, 0.f}; 79 | } 80 | 81 | return {r, g, b, 1.f}; 82 | } 83 | 84 | [[nodiscard]] 85 | constexpr static Color from_hsv(const Hsv& hsv) noexcept 86 | { 87 | return from_hsv(hsv.hue, hsv.saturation, hsv.value); 88 | } 89 | 90 | [[nodiscard]] 91 | constexpr Hsv to_hsv() const noexcept 92 | { 93 | Hsv hsv_data; 94 | 95 | const float& red = x; 96 | const float& green = y; 97 | const float& blue = z; 98 | 99 | const float max = std::max({red, green, blue}); 100 | const float min = std::min({red, green, blue}); 101 | const float delta = max - min; 102 | 103 | if (delta == 0.f) 104 | hsv_data.hue = 0.f; 105 | 106 | else if (max == red) 107 | hsv_data.hue = 60.f * (std::fmodf(((green - blue) / delta), 6.f)); 108 | else if (max == green) 109 | hsv_data.hue = 60.f * (((blue - red) / delta) + 2.f); 110 | else if (max == blue) 111 | hsv_data.hue = 60.f * (((red - green) / delta) + 4.f); 112 | 113 | if (hsv_data.hue < 0.f) 114 | hsv_data.hue += 360.f; 115 | 116 | hsv_data.hue /= 360.f; 117 | hsv_data.saturation = max == 0.f ? 0.f : delta / max; 118 | hsv_data.value = max; 119 | 120 | return hsv_data; 121 | } 122 | 123 | constexpr explicit Color(const Vector4& vec) noexcept: Vector4(vec) 124 | { 125 | clamp(0.f, 1.f); 126 | } 127 | constexpr void set_hue(const float hue) noexcept 128 | { 129 | auto hsv = to_hsv(); 130 | hsv.hue = hue; 131 | 132 | *this = from_hsv(hsv); 133 | } 134 | 135 | constexpr void set_saturation(const float saturation) noexcept 136 | { 137 | auto hsv = to_hsv(); 138 | hsv.saturation = saturation; 139 | 140 | *this = from_hsv(hsv); 141 | } 142 | 143 | constexpr void set_value(const float value) noexcept 144 | { 145 | auto hsv = to_hsv(); 146 | hsv.value = value; 147 | 148 | *this = from_hsv(hsv); 149 | } 150 | [[nodiscard]] 151 | constexpr Color blend(const Color& other, float ratio) const noexcept 152 | { 153 | ratio = std::clamp(ratio, 0.f, 1.f); 154 | return Color(*this * (1.f - ratio) + other * ratio); 155 | } 156 | 157 | [[nodiscard]] static constexpr Color red() 158 | { 159 | return {1.f, 0.f, 0.f, 1.f}; 160 | } 161 | [[nodiscard]] static constexpr Color green() 162 | { 163 | return {0.f, 1.f, 0.f, 1.f}; 164 | } 165 | [[nodiscard]] static constexpr Color blue() 166 | { 167 | return {0.f, 0.f, 1.f, 1.f}; 168 | } 169 | }; 170 | } // namespace omath 171 | -------------------------------------------------------------------------------- /include/omath/engines/iw_engine/camera.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/17/2025. 3 | // 4 | 5 | #pragma once 6 | #include "omath/engines/iw_engine/constants.hpp" 7 | #include "omath/projection/camera.hpp" 8 | 9 | namespace omath::iw_engine 10 | { 11 | class Camera final : public projection::Camera 12 | { 13 | public: 14 | Camera(const Vector3& position, const ViewAngles& view_angles, const projection::ViewPort& view_port, 15 | const Angle& fov, float near, float far); 16 | void look_at(const Vector3& target) override; 17 | 18 | protected: 19 | [[nodiscard]] Mat4X4 calc_view_matrix() const noexcept override; 20 | [[nodiscard]] Mat4X4 calc_projection_matrix() const noexcept override; 21 | }; 22 | } // namespace omath::iw_engine -------------------------------------------------------------------------------- /include/omath/engines/iw_engine/constants.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/17/2025. 3 | // 4 | 5 | #pragma once 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace omath::iw_engine 12 | { 13 | constexpr Vector3 k_abs_up = {0, 0, 1}; 14 | constexpr Vector3 k_abs_right = {0, -1, 0}; 15 | constexpr Vector3 k_abs_forward = {1, 0, 0}; 16 | 17 | using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; 18 | using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; 19 | using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>; 20 | using PitchAngle = Angle; 21 | using YawAngle = Angle; 22 | using RollAngle = Angle; 23 | 24 | using ViewAngles = omath::ViewAngles; 25 | } // namespace omath::iw_engine -------------------------------------------------------------------------------- /include/omath/engines/iw_engine/formulas.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/17/2025. 3 | // 4 | 5 | #pragma once 6 | #include "omath/engines/iw_engine/constants.hpp" 7 | 8 | namespace omath::iw_engine 9 | { 10 | [[nodiscard]] 11 | Vector3 forward_vector(const ViewAngles& angles) noexcept; 12 | 13 | [[nodiscard]] 14 | Vector3 right_vector(const ViewAngles& angles) noexcept; 15 | 16 | [[nodiscard]] 17 | Vector3 up_vector(const ViewAngles& angles) noexcept; 18 | 19 | [[nodiscard]] 20 | Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; 21 | 22 | [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; 23 | 24 | [[nodiscard]] 25 | Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; 26 | } // namespace omath::iw_engine 27 | -------------------------------------------------------------------------------- /include/omath/engines/opengl_engine/camera.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 12/23/2024. 3 | // 4 | #pragma once 5 | #include "omath/engines/opengl_engine/constants.hpp" 6 | #include "omath/projection/camera.hpp" 7 | 8 | namespace omath::opengl_engine 9 | { 10 | class Camera final : public projection::Camera 11 | { 12 | public: 13 | Camera(const Vector3& position, const ViewAngles& view_angles, const projection::ViewPort& view_port, 14 | const Angle& fov, float near, float far); 15 | void look_at(const Vector3& target) override; 16 | [[nodiscard]] Mat4X4 calc_view_matrix() const noexcept override; 17 | [[nodiscard]] Mat4X4 calc_projection_matrix() const noexcept override; 18 | }; 19 | } // namespace omath::opengl_engine -------------------------------------------------------------------------------- /include/omath/engines/opengl_engine/constants.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 12/23/2024. 3 | // 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace omath::opengl_engine 12 | { 13 | constexpr Vector3 k_abs_up = {0, 1, 0}; 14 | constexpr Vector3 k_abs_right = {1, 0, 0}; 15 | constexpr Vector3 k_abs_forward = {0, 0, -1}; 16 | 17 | using Mat4X4 = Mat<4, 4, float, MatStoreType::COLUMN_MAJOR>; 18 | using Mat3X3 = Mat<4, 4, float, MatStoreType::COLUMN_MAJOR>; 19 | using Mat1X3 = Mat<1, 3, float, MatStoreType::COLUMN_MAJOR>; 20 | using PitchAngle = Angle; 21 | using YawAngle = Angle; 22 | using RollAngle = Angle; 23 | 24 | using ViewAngles = omath::ViewAngles; 25 | } // namespace omath::opengl_engine -------------------------------------------------------------------------------- /include/omath/engines/opengl_engine/formulas.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 12/23/2024. 3 | // 4 | #pragma once 5 | #include "omath/engines/opengl_engine/constants.hpp" 6 | 7 | 8 | namespace omath::opengl_engine 9 | { 10 | [[nodiscard]] 11 | Vector3 forward_vector(const ViewAngles& angles) noexcept; 12 | 13 | [[nodiscard]] 14 | Vector3 right_vector(const ViewAngles& angles) noexcept; 15 | 16 | [[nodiscard]] 17 | Vector3 up_vector(const ViewAngles& angles) noexcept; 18 | 19 | [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; 20 | 21 | [[nodiscard]] 22 | Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; 23 | 24 | [[nodiscard]] 25 | Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; 26 | } // namespace omath::opengl_engine 27 | -------------------------------------------------------------------------------- /include/omath/engines/source_engine/camera.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 12/4/2024. 3 | // 4 | #pragma once 5 | #include "omath/engines/source_engine/constants.hpp" 6 | #include "omath/projection/camera.hpp" 7 | 8 | namespace omath::source_engine 9 | { 10 | class Camera final : public projection::Camera 11 | { 12 | public: 13 | Camera(const Vector3& position, const ViewAngles& view_angles, const projection::ViewPort& view_port, 14 | const Angle& fov, float near, float far); 15 | void look_at(const Vector3& target) override; 16 | 17 | protected: 18 | [[nodiscard]] Mat4X4 calc_view_matrix() const noexcept override; 19 | [[nodiscard]] Mat4X4 calc_projection_matrix() const noexcept override; 20 | }; 21 | } // namespace omath::source_engine -------------------------------------------------------------------------------- /include/omath/engines/source_engine/constants.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 12/4/2024. 3 | // 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace omath::source_engine 12 | { 13 | constexpr Vector3 k_abs_up = {0, 0, 1}; 14 | constexpr Vector3 k_abs_right = {0, -1, 0}; 15 | constexpr Vector3 k_abs_forward = {1, 0, 0}; 16 | 17 | using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; 18 | using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; 19 | using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>; 20 | using PitchAngle = Angle; 21 | using YawAngle = Angle; 22 | using RollAngle = Angle; 23 | 24 | using ViewAngles = omath::ViewAngles; 25 | } // namespace omath::source_engine 26 | -------------------------------------------------------------------------------- /include/omath/engines/source_engine/formulas.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 12/4/2024. 3 | // 4 | #pragma once 5 | #include "omath/engines/source_engine/constants.hpp" 6 | 7 | namespace omath::source_engine 8 | { 9 | [[nodiscard]] 10 | Vector3 forward_vector(const ViewAngles& angles) noexcept; 11 | 12 | [[nodiscard]] 13 | Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; 14 | 15 | [[nodiscard]] 16 | Vector3 right_vector(const ViewAngles& angles) noexcept; 17 | 18 | [[nodiscard]] 19 | Vector3 up_vector(const ViewAngles& angles) noexcept; 20 | 21 | [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; 22 | 23 | [[nodiscard]] 24 | Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; 25 | } // namespace omath::source_engine 26 | -------------------------------------------------------------------------------- /include/omath/engines/unity_engine/camera.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/22/2025. 3 | // 4 | 5 | #pragma once 6 | #include "omath/engines/unity_engine/constants.hpp" 7 | #include "omath/projection/camera.hpp" 8 | 9 | namespace omath::unity_engine 10 | { 11 | class Camera final : public projection::Camera 12 | { 13 | public: 14 | Camera(const Vector3& position, const ViewAngles& view_angles, const projection::ViewPort& view_port, 15 | const Angle& fov, float near, float far); 16 | void look_at(const Vector3& target) override; 17 | 18 | protected: 19 | [[nodiscard]] Mat4X4 calc_view_matrix() const noexcept override; 20 | [[nodiscard]] Mat4X4 calc_projection_matrix() const noexcept override; 21 | }; 22 | } // namespace omath::unity_engine -------------------------------------------------------------------------------- /include/omath/engines/unity_engine/constants.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/22/2025. 3 | // 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace omath::unity_engine 13 | { 14 | constexpr Vector3 k_abs_up = {0, 1, 0}; 15 | constexpr Vector3 k_abs_right = {1, 0, 0}; 16 | constexpr Vector3 k_abs_forward = {0, 0, 1}; 17 | 18 | using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; 19 | using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; 20 | using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>; 21 | using PitchAngle = Angle; 22 | using YawAngle = Angle; 23 | using RollAngle = Angle; 24 | 25 | using ViewAngles = omath::ViewAngles; 26 | } // namespace omath::unity_engine 27 | -------------------------------------------------------------------------------- /include/omath/engines/unity_engine/formulas.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/22/2025. 3 | // 4 | 5 | #pragma once 6 | #include "omath/engines/unity_engine/constants.hpp" 7 | 8 | namespace omath::unity_engine 9 | { 10 | [[nodiscard]] 11 | Vector3 forward_vector(const ViewAngles& angles) noexcept; 12 | 13 | [[nodiscard]] 14 | Vector3 right_vector(const ViewAngles& angles) noexcept; 15 | 16 | [[nodiscard]] 17 | Vector3 up_vector(const ViewAngles& angles) noexcept; 18 | 19 | [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; 20 | 21 | [[nodiscard]] 22 | Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; 23 | 24 | [[nodiscard]] 25 | Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; 26 | } // namespace omath::unity_engine 27 | -------------------------------------------------------------------------------- /include/omath/matrix.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "omath/vector3.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | namespace omath 8 | { 9 | 10 | class Matrix final 11 | { 12 | public: 13 | Matrix(); 14 | Matrix(size_t rows, size_t columns); 15 | 16 | Matrix(const std::initializer_list>& rows); 17 | 18 | [[nodiscard]] 19 | static Matrix to_screen_matrix(float screen_width, float screen_height); 20 | 21 | [[nodiscard]] 22 | static Matrix translation_matrix(const Vector3& diff); 23 | 24 | [[nodiscard]] 25 | static Matrix orientation_matrix(const Vector3& forward, const Vector3& right, 26 | const Vector3& up); 27 | 28 | [[nodiscard]] 29 | static Matrix projection_matrix(float field_of_view, float aspect_ratio, float near, float far); 30 | 31 | Matrix(const Matrix& other); 32 | 33 | Matrix(size_t rows, size_t columns, const float* raw_data); 34 | 35 | Matrix(Matrix&& other) noexcept; 36 | 37 | [[nodiscard]] 38 | size_t row_count() const noexcept; 39 | 40 | [[nodiscard]] 41 | float& operator[](size_t row, size_t column); 42 | 43 | [[nodiscard]] 44 | size_t columns_count() const noexcept; 45 | 46 | [[nodiscard]] 47 | std::pair size() const noexcept; 48 | 49 | [[nodiscard]] 50 | float& at(size_t row, size_t col); 51 | 52 | [[nodiscard]] 53 | float sum(); 54 | 55 | void set_data_from_raw(const float* raw_matrix); 56 | 57 | [[nodiscard]] 58 | Matrix transpose() const; 59 | 60 | void set(float val); 61 | 62 | [[nodiscard]] 63 | const float& at(size_t row, size_t col) const; 64 | 65 | Matrix operator*(const Matrix& other) const; 66 | 67 | Matrix& operator*=(const Matrix& other); 68 | 69 | Matrix operator*(float f) const; 70 | 71 | Matrix& operator*=(float f); 72 | 73 | Matrix& operator/=(float f); 74 | 75 | void clear(); 76 | 77 | [[nodiscard]] 78 | Matrix strip(size_t row, size_t column) const; 79 | 80 | [[nodiscard]] 81 | float minor(size_t i, size_t j) const; 82 | 83 | [[nodiscard]] 84 | float alg_complement(size_t i, size_t j) const; 85 | 86 | [[nodiscard]] 87 | float determinant() const; 88 | 89 | [[nodiscard]] 90 | const float* raw() const; 91 | 92 | Matrix& operator=(const Matrix& other); 93 | 94 | Matrix& operator=(Matrix&& other) noexcept; 95 | 96 | Matrix operator/(float f) const; 97 | 98 | [[nodiscard]] 99 | std::string to_string() const; 100 | 101 | ~Matrix(); 102 | 103 | private: 104 | size_t m_rows; 105 | size_t m_columns; 106 | std::unique_ptr m_data; 107 | }; 108 | } // namespace omath 109 | -------------------------------------------------------------------------------- /include/omath/pathfinding/a_star.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 28.07.2024. 3 | // 4 | 5 | #pragma once 6 | #include "omath/pathfinding/navigation_mesh.hpp" 7 | #include "omath/vector3.hpp" 8 | #include 9 | 10 | namespace omath::pathfinding 11 | { 12 | struct PathNode; 13 | class Astar final 14 | { 15 | public: 16 | [[nodiscard]] 17 | static std::vector> find_path(const Vector3& start, const Vector3& end, 18 | const NavigationMesh& nav_mesh) noexcept; 19 | 20 | private: 21 | [[nodiscard]] 22 | static std::vector> 23 | reconstruct_final_path(const std::unordered_map, PathNode>& closed_list, 24 | const Vector3& current) noexcept; 25 | 26 | [[nodiscard]] 27 | static auto get_perfect_node(const std::unordered_map, PathNode>& open_list, 28 | const Vector3& end_vertex) noexcept; 29 | }; 30 | } // namespace omath::pathfinding 31 | -------------------------------------------------------------------------------- /include/omath/pathfinding/navigation_mesh.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 28.07.2024. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "omath/vector3.hpp" 8 | #include 9 | #include 10 | #include 11 | 12 | namespace omath::pathfinding 13 | { 14 | 15 | enum Error 16 | { 17 | }; 18 | 19 | class NavigationMesh final 20 | { 21 | public: 22 | [[nodiscard]] 23 | std::expected, std::string> get_closest_vertex(const Vector3& point) const noexcept; 24 | 25 | [[nodiscard]] 26 | const std::vector>& get_neighbors(const Vector3& vertex) const noexcept; 27 | 28 | [[nodiscard]] 29 | bool empty() const; 30 | 31 | [[nodiscard]] std::vector serialize() const noexcept; 32 | 33 | void deserialize(const std::vector& raw) noexcept; 34 | 35 | std::unordered_map, std::vector>> m_vertex_map; 36 | }; 37 | } // namespace omath::pathfinding 38 | -------------------------------------------------------------------------------- /include/omath/projectile_prediction/proj_pred_engine.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 2/23/2025. 3 | // 4 | #pragma once 5 | #include "omath/projectile_prediction/projectile.hpp" 6 | #include "omath/projectile_prediction/target.hpp" 7 | #include "omath/vector3.hpp" 8 | 9 | namespace omath::projectile_prediction 10 | { 11 | class ProjPredEngine 12 | { 13 | public: 14 | [[nodiscard]] 15 | virtual std::optional> maybe_calculate_aim_point(const Projectile& projectile, 16 | const Target& target) const = 0; 17 | virtual ~ProjPredEngine() = default; 18 | }; 19 | } // namespace omath::projectile_prediction 20 | -------------------------------------------------------------------------------- /include/omath/projectile_prediction/proj_pred_engine_avx2.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 2/23/2025. 3 | // 4 | #pragma once 5 | #include "omath/projectile_prediction/proj_pred_engine.hpp" 6 | 7 | namespace omath::projectile_prediction 8 | { 9 | class ProjPredEngineAvx2 final : public ProjPredEngine 10 | { 11 | public: 12 | [[nodiscard]] std::optional> 13 | maybe_calculate_aim_point(const Projectile& projectile, const Target& target) const override; 14 | 15 | ProjPredEngineAvx2(float gravity_constant, float simulation_time_step, float maximum_simulation_time); 16 | ~ProjPredEngineAvx2() override = default; 17 | 18 | private: 19 | [[nodiscard]] static std::optional calculate_pitch(const Vector3& proj_origin, 20 | const Vector3& target_pos, 21 | float bullet_gravity, float v0, float time) ; 22 | 23 | // We use [[maybe_unused]] here since AVX2 is not available for ARM and ARM64 CPU 24 | [[maybe_unused]] const float m_gravity_constant; 25 | [[maybe_unused]] const float m_simulation_time_step; 26 | [[maybe_unused]] const float m_maximum_simulation_time; 27 | }; 28 | } // namespace omath::projectile_prediction 29 | -------------------------------------------------------------------------------- /include/omath/projectile_prediction/proj_pred_engine_legacy.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 6/9/2024. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "omath/projectile_prediction/proj_pred_engine.hpp" 8 | #include "omath/projectile_prediction/projectile.hpp" 9 | #include "omath/projectile_prediction/target.hpp" 10 | #include "omath/vector3.hpp" 11 | #include 12 | 13 | namespace omath::projectile_prediction 14 | { 15 | class ProjPredEngineLegacy final : public ProjPredEngine 16 | { 17 | public: 18 | explicit ProjPredEngineLegacy(float gravity_constant, float simulation_time_step, float maximum_simulation_time, 19 | float distance_tolerance); 20 | 21 | [[nodiscard]] 22 | std::optional> maybe_calculate_aim_point(const Projectile& projectile, 23 | const Target& target) const override; 24 | 25 | private: 26 | const float m_gravity_constant; 27 | const float m_simulation_time_step; 28 | const float m_maximum_simulation_time; 29 | const float m_distance_tolerance; 30 | 31 | [[nodiscard]] 32 | std::optional 33 | maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile, 34 | const Vector3& target_position) const noexcept; 35 | 36 | [[nodiscard]] 37 | bool is_projectile_reached_target(const Vector3& target_position, const Projectile& projectile, 38 | float pitch, float time) const noexcept; 39 | }; 40 | } // namespace omath::projectile_prediction 41 | -------------------------------------------------------------------------------- /include/omath/projectile_prediction/projectile.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 6/9/2024. 3 | // 4 | 5 | #pragma once 6 | #include "omath/vector3.hpp" 7 | 8 | namespace omath::projectile_prediction 9 | { 10 | class Projectile final 11 | { 12 | public: 13 | [[nodiscard]] 14 | Vector3 predict_position(float pitch, float yaw, float time, float gravity) const noexcept; 15 | 16 | Vector3 m_origin; 17 | float m_launch_speed{}; 18 | float m_gravity_scale{}; 19 | }; 20 | } // namespace omath::projectile_prediction -------------------------------------------------------------------------------- /include/omath/projectile_prediction/target.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 6/9/2024. 3 | // 4 | 5 | #pragma once 6 | #include "omath/vector3.hpp" 7 | 8 | namespace omath::projectile_prediction 9 | { 10 | class Target final 11 | { 12 | public: 13 | [[nodiscard]] 14 | constexpr Vector3 predict_position(const float time, const float gravity) const noexcept 15 | { 16 | auto predicted = m_origin + m_velocity * time; 17 | 18 | if (m_is_airborne) 19 | predicted.z -= gravity * (time*time) * 0.5f; 20 | 21 | return predicted; 22 | } 23 | 24 | Vector3 m_origin; 25 | Vector3 m_velocity; 26 | bool m_is_airborne{}; 27 | }; 28 | } // namespace omath::projectile_prediction -------------------------------------------------------------------------------- /include/omath/projection/camera.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 27.08.2024. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "omath/projection/error_codes.hpp" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace omath::projection 15 | { 16 | class ViewPort final 17 | { 18 | public: 19 | float m_width; 20 | float m_height; 21 | 22 | [[nodiscard]] constexpr float aspect_ratio() const 23 | { 24 | return m_width / m_height; 25 | } 26 | }; 27 | using FieldOfView = Angle; 28 | 29 | template 30 | class Camera 31 | { 32 | public: 33 | virtual ~Camera() = default; 34 | Camera(const Vector3& position, const ViewAnglesType& view_angles, const ViewPort& view_port, 35 | const FieldOfView& fov, const float near, const float far) noexcept 36 | : m_view_port(view_port), m_field_of_view(fov), m_far_plane_distance(far), m_near_plane_distance(near), 37 | m_view_angles(view_angles), m_origin(position) 38 | { 39 | } 40 | 41 | protected: 42 | virtual void look_at(const Vector3& target) = 0; 43 | 44 | [[nodiscard]] virtual Mat4X4Type calc_view_matrix() const noexcept = 0; 45 | 46 | [[nodiscard]] virtual Mat4X4Type calc_projection_matrix() const noexcept = 0; 47 | 48 | [[nodiscard]] Mat4X4Type calc_view_projection_matrix() const noexcept 49 | { 50 | return calc_projection_matrix() * calc_view_matrix(); 51 | } 52 | 53 | public: 54 | [[nodiscard]] const Mat4X4Type& get_view_projection_matrix() const noexcept 55 | { 56 | if (!m_view_projection_matrix.has_value()) 57 | m_view_projection_matrix = calc_view_projection_matrix(); 58 | 59 | return m_view_projection_matrix.value(); 60 | } 61 | 62 | void set_field_of_view(const FieldOfView& fov) noexcept 63 | { 64 | m_field_of_view = fov; 65 | m_view_projection_matrix = std::nullopt; 66 | } 67 | 68 | void set_near_plane(const float near) noexcept 69 | { 70 | m_near_plane_distance = near; 71 | m_view_projection_matrix = std::nullopt; 72 | } 73 | 74 | void set_far_plane(const float far) noexcept 75 | { 76 | m_far_plane_distance = far; 77 | m_view_projection_matrix = std::nullopt; 78 | } 79 | 80 | void set_view_angles(const ViewAnglesType& view_angles) noexcept 81 | { 82 | m_view_angles = view_angles; 83 | m_view_projection_matrix = std::nullopt; 84 | } 85 | 86 | void set_origin(const Vector3& origin) noexcept 87 | { 88 | m_origin = origin; 89 | m_view_projection_matrix = std::nullopt; 90 | } 91 | 92 | void set_view_port(const ViewPort& view_port) noexcept 93 | { 94 | m_view_port = view_port; 95 | m_view_projection_matrix = std::nullopt; 96 | } 97 | 98 | [[nodiscard]] const FieldOfView& get_field_of_view() const noexcept 99 | { 100 | return m_field_of_view; 101 | } 102 | 103 | [[nodiscard]] const float& get_near_plane() const noexcept 104 | { 105 | return m_near_plane_distance; 106 | } 107 | 108 | [[nodiscard]] const float& get_far_plane() const noexcept 109 | { 110 | return m_far_plane_distance; 111 | } 112 | 113 | [[nodiscard]] const ViewAnglesType& get_view_angles() const noexcept 114 | { 115 | return m_view_angles; 116 | } 117 | 118 | [[nodiscard]] const Vector3& get_origin() const noexcept 119 | { 120 | return m_origin; 121 | } 122 | 123 | [[nodiscard]] std::expected, Error> 124 | world_to_screen(const Vector3& world_position) const noexcept 125 | { 126 | auto normalized_cords = world_to_view_port(world_position); 127 | 128 | if (!normalized_cords.has_value()) 129 | return std::unexpected{normalized_cords.error()}; 130 | 131 | return ndc_to_screen_position(*normalized_cords); 132 | } 133 | 134 | [[nodiscard]] std::expected, Error> 135 | world_to_view_port(const Vector3& world_position) const noexcept 136 | { 137 | auto projected = get_view_projection_matrix() 138 | * mat_column_from_vector(world_position); 139 | 140 | if (projected.at(3, 0) == 0.0f) 141 | return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); 142 | 143 | projected /= projected.at(3, 0); 144 | 145 | if (is_ndc_out_of_bounds(projected)) 146 | return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); 147 | 148 | return Vector3{projected.at(0, 0), projected.at(1, 0), projected.at(2, 0)}; 149 | } 150 | 151 | protected: 152 | ViewPort m_view_port{}; 153 | Angle m_field_of_view; 154 | 155 | mutable std::optional m_view_projection_matrix; 156 | 157 | float m_far_plane_distance; 158 | float m_near_plane_distance; 159 | 160 | ViewAnglesType m_view_angles; 161 | Vector3 m_origin; 162 | 163 | private: 164 | template 165 | [[nodiscard]] constexpr static bool is_ndc_out_of_bounds(const Type& ndc) noexcept 166 | { 167 | return std::ranges::any_of(ndc.raw_array(), [](const auto& val) { return val < -1 || val > 1; }); 168 | } 169 | 170 | [[nodiscard]] Vector3 ndc_to_screen_position(const Vector3& ndc) const noexcept 171 | { 172 | return {(ndc.x + 1.f) / 2.f * m_view_port.m_width, (1.f - ndc.y) / 2.f * m_view_port.m_height, ndc.z}; 173 | } 174 | }; 175 | } // namespace omath::projection 176 | -------------------------------------------------------------------------------- /include/omath/projection/error_codes.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 03.09.2024. 3 | // 4 | 5 | #pragma once 6 | #include 7 | 8 | namespace omath::projection 9 | { 10 | enum class Error : uint16_t 11 | { 12 | WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS, 13 | }; 14 | } -------------------------------------------------------------------------------- /include/omath/triangle.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/13/2024. 3 | // 4 | #pragma once 5 | #include "omath/vector3.hpp" 6 | 7 | namespace omath 8 | { 9 | /* 10 | |\ 11 | | \ 12 | a | \ hypot 13 | | \ 14 | ----- 15 | b 16 | */ 17 | 18 | template 19 | class Triangle final 20 | { 21 | public: 22 | constexpr Triangle() = default; 23 | constexpr Triangle(const Vector& vertex1, const Vector& vertex2, const Vector& vertex3) 24 | : m_vertex1(vertex1), m_vertex2(vertex2), m_vertex3(vertex3) 25 | { 26 | } 27 | 28 | Vector3 m_vertex1; 29 | Vector3 m_vertex2; 30 | Vector3 m_vertex3; 31 | 32 | [[nodiscard]] 33 | constexpr Vector3 calculate_normal() const 34 | { 35 | const auto b = side_b_vector(); 36 | const auto a = side_a_vector(); 37 | 38 | return b.cross(a).normalized(); 39 | } 40 | 41 | [[nodiscard]] 42 | float side_a_length() const 43 | { 44 | return m_vertex1.distance_to(m_vertex2); 45 | } 46 | 47 | [[nodiscard]] 48 | float side_b_length() const 49 | { 50 | return m_vertex3.distance_to(m_vertex2); 51 | } 52 | 53 | [[nodiscard]] 54 | constexpr Vector3 side_a_vector() const 55 | { 56 | return m_vertex1 - m_vertex2; 57 | } 58 | 59 | [[nodiscard]] 60 | constexpr float hypot() const 61 | { 62 | return m_vertex1.distance_to(m_vertex3); 63 | } 64 | [[nodiscard]] 65 | constexpr bool is_rectangular() const 66 | { 67 | const auto side_a = side_a_length(); 68 | const auto side_b = side_b_length(); 69 | const auto hypot_value = hypot(); 70 | 71 | return std::abs(side_a * side_a + side_b * side_b - hypot_value * hypot_value) <= 0.0001f; 72 | } 73 | [[nodiscard]] 74 | constexpr Vector3 side_b_vector() const 75 | { 76 | return m_vertex3 - m_vertex2; 77 | } 78 | [[nodiscard]] 79 | constexpr Vector3 mid_point() const 80 | { 81 | return (m_vertex1 + m_vertex2 + m_vertex3) / 3; 82 | } 83 | }; 84 | } // namespace omath 85 | -------------------------------------------------------------------------------- /include/omath/vector2.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 02.09.2024. 3 | // 4 | 5 | #pragma once 6 | #include 7 | #include 8 | 9 | #ifdef OMATH_IMGUI_INTEGRATION 10 | #include 11 | #endif 12 | 13 | namespace omath 14 | { 15 | 16 | template 17 | requires std::is_arithmetic_v 18 | class Vector2 19 | { 20 | public: 21 | Type x = static_cast(0); 22 | Type y = static_cast(0); 23 | 24 | // Constructors 25 | constexpr Vector2() = default; 26 | 27 | constexpr Vector2(const Type& x, const Type& y) noexcept: x(x), y(y) 28 | { 29 | } 30 | 31 | // Equality operators 32 | [[nodiscard]] 33 | constexpr bool operator==(const Vector2& other) const noexcept 34 | { 35 | return x == other.x && y == other.y; 36 | } 37 | 38 | [[nodiscard]] 39 | constexpr bool operator!=(const Vector2& other) const noexcept 40 | { 41 | return !(*this == other); 42 | } 43 | 44 | // Compound assignment operators 45 | constexpr Vector2& operator+=(const Vector2& other) noexcept 46 | { 47 | x += other.x; 48 | y += other.y; 49 | 50 | return *this; 51 | } 52 | 53 | constexpr Vector2& operator-=(const Vector2& other) noexcept 54 | { 55 | x -= other.x; 56 | y -= other.y; 57 | 58 | return *this; 59 | } 60 | 61 | constexpr Vector2& operator*=(const Vector2& other) noexcept 62 | { 63 | x *= other.x; 64 | y *= other.y; 65 | 66 | return *this; 67 | } 68 | 69 | constexpr Vector2& operator/=(const Vector2& other) noexcept 70 | { 71 | x /= other.x; 72 | y /= other.y; 73 | 74 | return *this; 75 | } 76 | 77 | constexpr Vector2& operator*=(const Type& value) noexcept 78 | { 79 | x *= value; 80 | y *= value; 81 | 82 | return *this; 83 | } 84 | 85 | constexpr Vector2& operator/=(const Type& value) noexcept 86 | { 87 | x /= value; 88 | y /= value; 89 | 90 | return *this; 91 | } 92 | 93 | constexpr Vector2& operator+=(const Type& value) noexcept 94 | { 95 | x += value; 96 | y += value; 97 | 98 | return *this; 99 | } 100 | 101 | constexpr Vector2& operator-=(const Type& value) noexcept 102 | { 103 | x -= value; 104 | y -= value; 105 | 106 | return *this; 107 | } 108 | 109 | // Basic vector operations 110 | [[nodiscard]] Type distance_to(const Vector2& other) const noexcept 111 | { 112 | return std::sqrt(distance_to_sqr(other)); 113 | } 114 | 115 | [[nodiscard]] constexpr Type distance_to_sqr(const Vector2& other) const noexcept 116 | { 117 | return (x - other.x) * (x - other.x) + (y - other.y) * (y - other.y); 118 | } 119 | 120 | [[nodiscard]] constexpr Type dot(const Vector2& other) const noexcept 121 | { 122 | return x * other.x + y * other.y; 123 | } 124 | 125 | #ifndef _MSC_VER 126 | [[nodiscard]] constexpr Type length() const noexcept 127 | { 128 | return std::hypot(this->x, this->y); 129 | } 130 | 131 | [[nodiscard]] constexpr Vector2 normalized() const noexcept 132 | { 133 | const Type len = length(); 134 | return len > 0.f ? *this / len : *this; 135 | } 136 | #else 137 | [[nodiscard]] Type length() const noexcept 138 | { 139 | return std::hypot(x, y); 140 | } 141 | 142 | [[nodiscard]] Vector2 normalized() const noexcept 143 | { 144 | const Type len = length(); 145 | return len > 0.f ? *this / len : *this; 146 | } 147 | #endif 148 | [[nodiscard]] constexpr Type length_sqr() const noexcept 149 | { 150 | return x * x + y * y; 151 | } 152 | 153 | constexpr Vector2& abs() noexcept 154 | { 155 | // FIXME: Replace with std::abs, if it will become constexprable 156 | x = x < 0 ? -x : x; 157 | y = y < 0 ? -y : y; 158 | return *this; 159 | } 160 | 161 | [[nodiscard]] constexpr Vector2 operator-() const noexcept 162 | { 163 | return {-x, -y}; 164 | } 165 | 166 | // Binary arithmetic operators 167 | [[nodiscard]] constexpr Vector2 operator+(const Vector2& other) const noexcept 168 | { 169 | return {x + other.x, y + other.y}; 170 | } 171 | 172 | [[nodiscard]] constexpr Vector2 operator-(const Vector2& other) const noexcept 173 | { 174 | return {x - other.x, y - other.y}; 175 | } 176 | 177 | [[nodiscard]] constexpr Vector2 operator*(const Type& value) const noexcept 178 | { 179 | return {x * value, y * value}; 180 | } 181 | 182 | [[nodiscard]] constexpr Vector2 operator/(const Type& value) const noexcept 183 | { 184 | return {x / value, y / value}; 185 | } 186 | 187 | // Sum of elements 188 | [[nodiscard]] constexpr Type sum() const noexcept 189 | { 190 | return x + y; 191 | } 192 | 193 | [[nodiscard]] 194 | constexpr std::tuple as_tuple() const noexcept 195 | { 196 | return std::make_tuple(x, y); 197 | } 198 | 199 | #ifdef OMATH_IMGUI_INTEGRATION 200 | [[nodiscard]] 201 | ImVec2 to_im_vec2() const noexcept 202 | { 203 | return {static_cast(this->x), static_cast(this->y)}; 204 | } 205 | #endif 206 | }; 207 | } // namespace omath 208 | -------------------------------------------------------------------------------- /include/omath/vector3.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by vlad on 10/28/23. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "omath/angle.hpp" 8 | #include "omath/vector2.hpp" 9 | #include 10 | #include 11 | #include 12 | 13 | namespace omath 14 | { 15 | 16 | enum class Vector3Error 17 | { 18 | IMPOSSIBLE_BETWEEN_ANGLE, 19 | }; 20 | 21 | template 22 | requires std::is_arithmetic_v 23 | class Vector3 : public Vector2 24 | { 25 | public: 26 | Type z = static_cast(0); 27 | constexpr Vector3(const Type& x, const Type& y, const Type& z) noexcept: Vector2(x, y), z(z) 28 | { 29 | } 30 | constexpr Vector3() noexcept: Vector2() {}; 31 | 32 | [[nodiscard]] constexpr bool operator==(const Vector3& other) const noexcept 33 | { 34 | return Vector2::operator==(other) && (other.z == z); 35 | } 36 | 37 | [[nodiscard]] constexpr bool operator!=(const Vector3& other) const noexcept 38 | { 39 | return !(*this == other); 40 | } 41 | 42 | constexpr Vector3& operator+=(const Vector3& other) noexcept 43 | { 44 | Vector2::operator+=(other); 45 | z += other.z; 46 | 47 | return *this; 48 | } 49 | 50 | constexpr Vector3& operator-=(const Vector3& other) noexcept 51 | { 52 | Vector2::operator-=(other); 53 | z -= other.z; 54 | 55 | return *this; 56 | } 57 | 58 | constexpr Vector3& operator*=(const Type& value) noexcept 59 | { 60 | Vector2::operator*=(value); 61 | z *= value; 62 | 63 | return *this; 64 | } 65 | 66 | constexpr Vector3& operator*=(const Vector3& other) noexcept 67 | { 68 | Vector2::operator*=(other); 69 | z *= other.z; 70 | 71 | return *this; 72 | } 73 | 74 | constexpr Vector3& operator/=(const Vector3& other) noexcept 75 | { 76 | Vector2::operator/=(other); 77 | z /= other.z; 78 | 79 | return *this; 80 | } 81 | 82 | constexpr Vector3& operator+=(const Type& value) noexcept 83 | { 84 | Vector2::operator+=(value); 85 | z += value; 86 | 87 | return *this; 88 | } 89 | 90 | constexpr Vector3& operator/=(const Type& value) noexcept 91 | { 92 | Vector2::operator/=(value); 93 | z /= value; 94 | 95 | return *this; 96 | } 97 | 98 | constexpr Vector3& operator-=(const Type& value) noexcept 99 | { 100 | Vector2::operator-=(value); 101 | z -= value; 102 | 103 | return *this; 104 | } 105 | 106 | constexpr Vector3& abs() noexcept 107 | { 108 | Vector2::abs(); 109 | z = z < 0.f ? -z : z; 110 | 111 | return *this; 112 | } 113 | 114 | [[nodiscard]] constexpr Type distance_to_sqr(const Vector3& other) const noexcept 115 | { 116 | return (*this - other).length_sqr(); 117 | } 118 | 119 | [[nodiscard]] constexpr Type dot(const Vector3& other) const noexcept 120 | { 121 | return Vector2::dot(other) + z * other.z; 122 | } 123 | 124 | #ifndef _MSC_VER 125 | [[nodiscard]] constexpr Type length() const 126 | { 127 | return std::hypot(this->x, this->y, z); 128 | } 129 | 130 | [[nodiscard]] constexpr Type length_2d() const 131 | { 132 | return Vector2::length(); 133 | } 134 | [[nodiscard]] Type distance_to(const Vector3& other) const 135 | { 136 | return (*this - other).length(); 137 | } 138 | [[nodiscard]] constexpr Vector3 normalized() const 139 | { 140 | const Type length_value = this->length(); 141 | 142 | return length_value != 0 ? *this / length_value : *this; 143 | } 144 | #else 145 | [[nodiscard]] Type length() const noexcept 146 | { 147 | return std::hypot(this->x, this->y, z); 148 | } 149 | 150 | [[nodiscard]] Vector3 normalized() const noexcept 151 | { 152 | const Type len = this->length(); 153 | 154 | return len != 0 ? *this / len : *this; 155 | } 156 | 157 | [[nodiscard]] Type length_2d() const noexcept 158 | { 159 | return Vector2::length(); 160 | } 161 | 162 | [[nodiscard]] Type distance_to(const Vector3& vOther) const noexcept 163 | { 164 | return (*this - vOther).length(); 165 | } 166 | #endif 167 | 168 | [[nodiscard]] constexpr Type length_sqr() const noexcept 169 | { 170 | return Vector2::length_sqr() + z * z; 171 | } 172 | 173 | [[nodiscard]] constexpr Vector3 operator-() const noexcept 174 | { 175 | return {-this->x, -this->y, -z}; 176 | } 177 | 178 | [[nodiscard]] constexpr Vector3 operator+(const Vector3& other) const noexcept 179 | { 180 | return {this->x + other.x, this->y + other.y, z + other.z}; 181 | } 182 | 183 | [[nodiscard]] constexpr Vector3 operator-(const Vector3& other) const noexcept 184 | { 185 | return {this->x - other.x, this->y - other.y, z - other.z}; 186 | } 187 | 188 | [[nodiscard]] constexpr Vector3 operator*(const Type& value) const noexcept 189 | { 190 | return {this->x * value, this->y * value, z * value}; 191 | } 192 | 193 | [[nodiscard]] constexpr Vector3 operator*(const Vector3& other) const noexcept 194 | { 195 | return {this->x * other.x, this->y * other.y, z * other.z}; 196 | } 197 | 198 | [[nodiscard]] constexpr Vector3 operator/(const Type& value) const noexcept 199 | { 200 | return {this->x / value, this->y / value, z / value}; 201 | } 202 | 203 | [[nodiscard]] constexpr Vector3 operator/(const Vector3& other) const noexcept 204 | { 205 | return {this->x / other.x, this->y / other.y, z / other.z}; 206 | } 207 | 208 | [[nodiscard]] constexpr Vector3 cross(const Vector3& other) const noexcept 209 | { 210 | return {this->y * other.z - z * other.y, z * other.x - this->x * other.z, 211 | this->x * other.y - this->y * other.x}; 212 | } 213 | 214 | [[nodiscard]] constexpr Type sum() const noexcept 215 | { 216 | return sum_2d() + z; 217 | } 218 | 219 | [[nodiscard]] std::expected, Vector3Error> 220 | angle_between(const Vector3& other) const noexcept 221 | { 222 | const auto bottom = length() * other.length(); 223 | 224 | if (bottom == 0.f) 225 | return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE); 226 | 227 | return Angle::from_radians(std::acos(dot(other) / bottom)); 228 | } 229 | 230 | [[nodiscard]] bool is_perpendicular(const Vector3& other) const noexcept 231 | { 232 | if (const auto angle = angle_between(other)) 233 | return angle->as_degrees() == 90.f; 234 | 235 | return false; 236 | } 237 | 238 | [[nodiscard]] constexpr Type sum_2d() const noexcept 239 | { 240 | return Vector2::sum(); 241 | } 242 | 243 | [[nodiscard]] constexpr std::tuple as_tuple() const noexcept 244 | { 245 | return std::make_tuple(this->x, this->y, z); 246 | } 247 | 248 | [[nodiscard]] Vector3 view_angle_to(const Vector3& other) const noexcept 249 | { 250 | const auto distance = distance_to(other); 251 | const auto delta = other - *this; 252 | 253 | return {angles::radians_to_degrees(std::asin(delta.z / distance)), 254 | angles::radians_to_degrees(std::atan2(delta.y, delta.x)), 0}; 255 | } 256 | }; 257 | } // namespace omath 258 | // ReSharper disable once CppRedundantNamespaceDefinition 259 | namespace std 260 | { 261 | template<> struct hash> 262 | { 263 | std::size_t operator()(const omath::Vector3& vec) const noexcept 264 | { 265 | std::size_t hash = 0; 266 | constexpr std::hash hasher; 267 | 268 | hash ^= hasher(vec.x) + 0x9e3779b9 + (hash << 6) + (hash >> 2); 269 | hash ^= hasher(vec.y) + 0x9e3779b9 + (hash << 6) + (hash >> 2); 270 | hash ^= hasher(vec.z) + 0x9e3779b9 + (hash << 6) + (hash >> 2); 271 | 272 | return hash; 273 | } 274 | }; 275 | } // namespace std 276 | -------------------------------------------------------------------------------- /include/omath/vector4.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Vector4.h 3 | // 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace omath 10 | { 11 | template 12 | requires std::is_arithmetic_v 13 | class Vector4 : public Vector3 14 | { 15 | public: 16 | Type w; 17 | 18 | constexpr Vector4(const Type& x, const Type& y, const Type& z, const Type& w): Vector3(x, y, z), w(w) 19 | { 20 | } 21 | constexpr Vector4() noexcept : Vector3(), w(0) {}; 22 | 23 | [[nodiscard]] 24 | constexpr bool operator==(const Vector4& other) const noexcept 25 | { 26 | return Vector3::operator==(other) && w == other.w; 27 | } 28 | 29 | [[nodiscard]] 30 | constexpr bool operator!=(const Vector4& other) const noexcept 31 | { 32 | return !(*this == other); 33 | } 34 | 35 | constexpr Vector4& operator+=(const Vector4& other) noexcept 36 | { 37 | Vector3::operator+=(other); 38 | w += other.w; 39 | 40 | return *this; 41 | } 42 | 43 | constexpr Vector4& operator-=(const Vector4& other) noexcept 44 | { 45 | Vector3::operator-=(other); 46 | w -= other.w; 47 | 48 | return *this; 49 | } 50 | 51 | constexpr Vector4& operator*=(const Type& value) noexcept 52 | { 53 | Vector3::operator*=(value); 54 | w *= value; 55 | 56 | return *this; 57 | } 58 | 59 | constexpr Vector4& operator*=(const Vector4& other) noexcept 60 | { 61 | Vector3::operator*=(other); 62 | w *= other.w; 63 | 64 | return *this; 65 | } 66 | 67 | constexpr Vector4& operator/=(const Type& value) noexcept 68 | { 69 | Vector3::operator/=(value); 70 | w /= value; 71 | 72 | return *this; 73 | } 74 | 75 | constexpr Vector4& operator/=(const Vector4& other) noexcept 76 | { 77 | Vector3::operator/=(other); 78 | w /= other.w; 79 | return *this; 80 | } 81 | 82 | [[nodiscard]] constexpr Type length_sqr() const noexcept 83 | { 84 | return Vector3::length_sqr() + w * w; 85 | } 86 | 87 | [[nodiscard]] constexpr Type dot(const Vector4& other) const noexcept 88 | { 89 | return Vector3::dot(other) + w * other.w; 90 | } 91 | 92 | [[nodiscard]] Vector3 length() const noexcept 93 | { 94 | return std::sqrt(length_sqr()); 95 | } 96 | 97 | constexpr Vector4& abs() noexcept 98 | { 99 | Vector3::abs(); 100 | w = w < 0.f ? -w : w; 101 | 102 | return *this; 103 | } 104 | constexpr Vector4& clamp(const Type& min, const Type& max) noexcept 105 | { 106 | this->x = std::clamp(this->x, min, max); 107 | this->y = std::clamp(this->y, min, max); 108 | this->z = std::clamp(this->z, min, max); 109 | 110 | return *this; 111 | } 112 | 113 | [[nodiscard]] 114 | constexpr Vector4 operator-() const noexcept 115 | { 116 | return {-this->x, -this->y, -this->z, -w}; 117 | } 118 | 119 | [[nodiscard]] 120 | constexpr Vector4 operator+(const Vector4& other) const noexcept 121 | { 122 | return {this->x + other.x, this->y + other.y, this->z + other.z, w + other.w}; 123 | } 124 | 125 | [[nodiscard]] 126 | constexpr Vector4 operator-(const Vector4& other) const noexcept 127 | { 128 | return {this->x - other.x, this->y - other.y, this->z - other.z, w - other.w}; 129 | } 130 | 131 | [[nodiscard]] 132 | constexpr Vector4 operator*(const Type& value) const noexcept 133 | { 134 | return {this->x * value, this->y * value, this->z * value, w * value}; 135 | } 136 | 137 | [[nodiscard]] 138 | constexpr Vector4 operator*(const Vector4& other) const noexcept 139 | { 140 | return {this->x * other.x, this->y * other.y, this->z * other.z, w * other.w}; 141 | } 142 | 143 | [[nodiscard]] 144 | constexpr Vector4 operator/(const Type& value) const noexcept 145 | { 146 | return {this->x / value, this->y / value, this->z / value, w / value}; 147 | } 148 | 149 | [[nodiscard]] 150 | constexpr Vector4 operator/(const Vector4& other) const noexcept 151 | { 152 | return {this->x / other.x, this->y / other.y, this->z / other.z, w / other.w}; 153 | } 154 | 155 | [[nodiscard]] 156 | constexpr Type sum() const noexcept 157 | { 158 | return Vector3::sum() + w; 159 | } 160 | 161 | #ifdef OMATH_IMGUI_INTEGRATION 162 | [[nodiscard]] 163 | ImVec4 to_im_vec4() const noexcept 164 | { 165 | return { 166 | static_cast(this->x), 167 | static_cast(this->y), 168 | static_cast(this->z), 169 | static_cast(w), 170 | }; 171 | } 172 | #endif 173 | }; 174 | } // namespace omath 175 | -------------------------------------------------------------------------------- /include/omath/view_angles.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/30/2024. 3 | // 4 | #pragma once 5 | 6 | namespace omath 7 | { 8 | template 9 | struct ViewAngles 10 | { 11 | PitchType pitch; 12 | YawType yaw; 13 | RollType roll; 14 | }; 15 | } // namespace omath 16 | -------------------------------------------------------------------------------- /source/3d_primitives/box.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 4/18/2025. 3 | // 4 | #include "omath/3d_primitives/box.hpp" 5 | 6 | namespace omath::primitives 7 | { 8 | std::array>, 12> create_box(const Vector3& top, const Vector3& bottom, 9 | const Vector3& dir_forward, 10 | const Vector3& dir_right, const float ratio) noexcept 11 | { 12 | const auto height = top.distance_to(bottom); 13 | const auto side_size = height / ratio; 14 | 15 | // corner layout (0‑3 bottom, 4‑7 top) 16 | std::array, 8> p; 17 | p[0] = bottom + (dir_forward + dir_right) * side_size; // front‑right‑bottom 18 | p[1] = bottom + (dir_forward - dir_right) * side_size; // front‑left‑bottom 19 | p[2] = bottom + (-dir_forward + dir_right) * side_size; // back‑right‑bottom 20 | p[3] = bottom + (-dir_forward - dir_right) * side_size; // back‑left‑bottom 21 | p[4] = top + (dir_forward + dir_right) * side_size; // front‑right‑top 22 | p[5] = top + (dir_forward - dir_right) * side_size; // front‑left‑top 23 | p[6] = top + (-dir_forward + dir_right) * side_size; // back‑right‑top 24 | p[7] = top + (-dir_forward - dir_right) * side_size; // back‑left‑top 25 | 26 | std::array>, 12> poly; 27 | 28 | // bottom face (+Y up ⇒ wind CW when viewed from above) 29 | poly[0] = {p[0], p[2], p[3]}; 30 | poly[1] = {p[0], p[3], p[1]}; 31 | 32 | // top face 33 | poly[2] = {p[4], p[7], p[6]}; 34 | poly[3] = {p[4], p[5], p[7]}; 35 | 36 | // front face 37 | poly[4] = {p[0], p[5], p[1]}; 38 | poly[5] = {p[0], p[4], p[5]}; 39 | 40 | // right face 41 | poly[6] = {p[0], p[6], p[2]}; 42 | poly[7] = {p[0], p[4], p[6]}; 43 | 44 | // back face 45 | poly[8] = {p[2], p[7], p[3]}; 46 | poly[9] = {p[2], p[6], p[7]}; 47 | 48 | // left face 49 | poly[10] = {p[1], p[7], p[5]}; 50 | poly[11] = {p[1], p[3], p[7]}; 51 | 52 | return poly; 53 | } 54 | } // namespace omath::primitives 55 | -------------------------------------------------------------------------------- /source/collision/line_tracer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/13/2024. 3 | // 4 | #include "omath/collision/line_tracer.hpp" 5 | 6 | namespace omath::collision 7 | { 8 | bool LineTracer::can_trace_line(const Ray& ray, const Triangle>& triangle) noexcept 9 | { 10 | return get_ray_hit_point(ray, triangle) == ray.end; 11 | } 12 | Vector3 Ray::direction_vector() const noexcept 13 | { 14 | return end - start; 15 | } 16 | 17 | Vector3 Ray::direction_vector_normalized() const noexcept 18 | { 19 | return direction_vector().normalized(); 20 | } 21 | 22 | Vector3 LineTracer::get_ray_hit_point(const Ray& ray, const Triangle>& triangle) noexcept 23 | { 24 | constexpr float k_epsilon = std::numeric_limits::epsilon(); 25 | 26 | const auto side_a = triangle.side_a_vector(); 27 | const auto side_b = triangle.side_b_vector(); 28 | 29 | const auto ray_dir = ray.direction_vector(); 30 | 31 | const auto p = ray_dir.cross(side_b); 32 | const auto det = side_a.dot(p); 33 | 34 | if (std::abs(det) < k_epsilon) 35 | return ray.end; 36 | 37 | const auto inv_det = 1.0f / det; 38 | const auto t = ray.start - triangle.m_vertex2; 39 | const auto u = t.dot(p) * inv_det; 40 | 41 | if ((u < 0 && std::abs(u) > k_epsilon) || (u > 1 && std::abs(u - 1) > k_epsilon)) 42 | return ray.end; 43 | 44 | const auto q = t.cross(side_a); 45 | // ReSharper disable once CppTooWideScopeInitStatement 46 | const auto v = ray_dir.dot(q) * inv_det; 47 | 48 | if ((v < 0 && std::abs(v) > k_epsilon) || (u + v > 1 && std::abs(u + v - 1) > k_epsilon)) 49 | return ray.end; 50 | 51 | const auto t_hit = side_b.dot(q) * inv_det; 52 | 53 | if (ray.infinite_length) 54 | { 55 | if (t_hit <= k_epsilon) 56 | return ray.end; 57 | } 58 | else if (t_hit <= k_epsilon || t_hit > 1.0f - k_epsilon) 59 | return ray.end; 60 | 61 | return ray.start + ray_dir * t_hit; 62 | } 63 | } // namespace omath::collision 64 | -------------------------------------------------------------------------------- /source/engines/iw_engine/camera.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/17/2025. 3 | // 4 | #include "omath/engines/iw_engine/camera.hpp" 5 | #include "omath/engines/iw_engine/formulas.hpp" 6 | 7 | namespace omath::iw_engine 8 | { 9 | 10 | Camera::Camera(const Vector3& position, const ViewAngles& view_angles, const projection::ViewPort& view_port, 11 | const Angle& fov, const float near, const float far) 12 | : projection::Camera(position, view_angles, view_port, fov, near, far) 13 | { 14 | } 15 | void Camera::look_at([[maybe_unused]] const Vector3& target) 16 | { 17 | const float distance = m_origin.distance_to(target); 18 | const auto delta = target - m_origin; 19 | 20 | m_view_angles.pitch = PitchAngle::from_radians(std::asin(delta.z / distance)); 21 | m_view_angles.yaw = -YawAngle::from_radians(std::atan2(delta.y, delta.x)); 22 | m_view_angles.roll = RollAngle::from_radians(0.f); 23 | } 24 | Mat4X4 Camera::calc_view_matrix() const noexcept 25 | { 26 | return iw_engine::calc_view_matrix(m_view_angles, m_origin); 27 | } 28 | Mat4X4 Camera::calc_projection_matrix() const noexcept 29 | { 30 | return calc_perspective_projection_matrix(m_field_of_view.as_degrees(), m_view_port.aspect_ratio(), 31 | m_near_plane_distance, m_far_plane_distance); 32 | } 33 | } // namespace omath::iw_engine -------------------------------------------------------------------------------- /source/engines/iw_engine/formulas.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/19/2025. 3 | // 4 | #include "omath/engines/iw_engine/formulas.hpp" 5 | 6 | namespace omath::iw_engine 7 | { 8 | 9 | Vector3 forward_vector(const ViewAngles& angles) noexcept 10 | { 11 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); 12 | 13 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 14 | } 15 | 16 | Vector3 right_vector(const ViewAngles& angles) noexcept 17 | { 18 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); 19 | 20 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 21 | } 22 | Vector3 up_vector(const ViewAngles& angles) noexcept 23 | { 24 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); 25 | 26 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 27 | } 28 | Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept 29 | { 30 | return mat_rotation_axis_z(angles.yaw) * mat_rotation_axis_y(angles.pitch) * mat_rotation_axis_x(angles.roll); 31 | } 32 | 33 | Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept 34 | { 35 | return mat_camera_view(forward_vector(angles), right_vector(angles), up_vector(angles), cam_origin); 36 | } 37 | 38 | Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, 39 | const float far) noexcept 40 | { 41 | // NOTE: Need magic number to fix fov calculation, since IW engine inherit Quake proj matrix calculation 42 | constexpr auto k_multiply_factor = 0.75f; 43 | 44 | const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f) * k_multiply_factor; 45 | 46 | return { 47 | {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, 48 | {0, 1.f / (fov_half_tan), 0, 0}, 49 | {0, 0, (far + near) / (far - near), -(2.f * far * near) / (far - near)}, 50 | {0, 0, 1, 0}, 51 | }; 52 | }; 53 | } // namespace omath::iw_engine 54 | -------------------------------------------------------------------------------- /source/engines/opengl_engine/camera.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 12/23/2024. 3 | // 4 | #include "omath/engines/opengl_engine/camera.hpp" 5 | #include "omath/engines/opengl_engine/formulas.hpp" 6 | 7 | namespace omath::opengl_engine 8 | { 9 | 10 | Camera::Camera(const Vector3& position, const ViewAngles& view_angles, const projection::ViewPort& view_port, 11 | const Angle& fov, const float near, const float far) 12 | : projection::Camera(position, view_angles, view_port, fov, near, far) 13 | { 14 | } 15 | void Camera::look_at([[maybe_unused]] const Vector3& target) 16 | { 17 | const float distance = m_origin.distance_to(target); 18 | const auto delta = target - m_origin; 19 | 20 | m_view_angles.pitch = PitchAngle::from_radians(std::asin(delta.z / distance)); 21 | m_view_angles.yaw = -YawAngle::from_radians(std::atan2(delta.y, delta.x)); 22 | m_view_angles.roll = RollAngle::from_radians(0.f); 23 | } 24 | Mat4X4 Camera::calc_view_matrix() const noexcept 25 | { 26 | return opengl_engine::calc_view_matrix(m_view_angles, m_origin); 27 | } 28 | Mat4X4 Camera::calc_projection_matrix() const noexcept 29 | { 30 | return calc_perspective_projection_matrix(m_field_of_view.as_degrees(), m_view_port.aspect_ratio(), 31 | m_near_plane_distance, m_far_plane_distance); 32 | } 33 | } // namespace omath::opengl_engine 34 | -------------------------------------------------------------------------------- /source/engines/opengl_engine/formulas.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/19/2025. 3 | // 4 | #include "omath/engines/opengl_engine/formulas.hpp" 5 | 6 | namespace omath::opengl_engine 7 | { 8 | 9 | Vector3 forward_vector(const ViewAngles& angles) noexcept 10 | { 11 | const auto vec 12 | = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); 13 | 14 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 15 | } 16 | Vector3 right_vector(const ViewAngles& angles) noexcept 17 | { 18 | const auto vec 19 | = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); 20 | 21 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 22 | } 23 | Vector3 up_vector(const ViewAngles& angles) noexcept 24 | { 25 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); 26 | 27 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 28 | } 29 | Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept 30 | { 31 | return mat_camera_view(-forward_vector(angles), right_vector(angles), 32 | up_vector(angles), cam_origin); 33 | } 34 | Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept 35 | { 36 | return mat_rotation_axis_x(-angles.pitch) 37 | * mat_rotation_axis_y(-angles.yaw) 38 | * mat_rotation_axis_z(angles.roll); 39 | } 40 | Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, 41 | const float far) noexcept 42 | { 43 | const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f); 44 | 45 | return { 46 | {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, 47 | {0, 1.f / (fov_half_tan), 0, 0}, 48 | {0, 0, -(far + near) / (far - near), -(2.f * far * near) / (far - near)}, 49 | {0, 0, -1, 0}, 50 | }; 51 | } 52 | } // namespace omath::opengl_engine 53 | -------------------------------------------------------------------------------- /source/engines/source_engine/camera.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 12/4/2024. 3 | // 4 | #include "omath/engines/source_engine/camera.hpp" 5 | #include "omath/engines/source_engine/formulas.hpp" 6 | 7 | namespace omath::source_engine 8 | { 9 | 10 | Camera::Camera(const Vector3& position, const ViewAngles& view_angles, const projection::ViewPort& view_port, 11 | const projection::FieldOfView& fov, const float near, const float far) 12 | : projection::Camera(position, view_angles, view_port, fov, near, far) 13 | { 14 | } 15 | void Camera::look_at(const Vector3& target) 16 | { 17 | const float distance = m_origin.distance_to(target); 18 | const auto delta = target - m_origin; 19 | 20 | m_view_angles.pitch = PitchAngle::from_radians(std::asin(delta.z / distance)); 21 | m_view_angles.yaw = -YawAngle::from_radians(std::atan2(delta.y, delta.x)); 22 | m_view_angles.roll = RollAngle::from_radians(0.f); 23 | } 24 | 25 | Mat4X4 Camera::calc_view_matrix() const noexcept 26 | { 27 | return source_engine::calc_view_matrix(m_view_angles, m_origin); 28 | } 29 | 30 | Mat4X4 Camera::calc_projection_matrix() const noexcept 31 | { 32 | return calc_perspective_projection_matrix(m_field_of_view.as_degrees(), m_view_port.aspect_ratio(), 33 | m_near_plane_distance, m_far_plane_distance); 34 | } 35 | } // namespace omath::source_engine 36 | -------------------------------------------------------------------------------- /source/engines/source_engine/formulas.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/19/2025. 3 | // 4 | #include 5 | 6 | namespace omath::source_engine 7 | { 8 | Vector3 forward_vector(const ViewAngles& angles) noexcept 9 | { 10 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); 11 | 12 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 13 | } 14 | 15 | Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept 16 | { 17 | return mat_rotation_axis_z(angles.yaw) * mat_rotation_axis_y(angles.pitch) * mat_rotation_axis_x(angles.roll); 18 | } 19 | 20 | Vector3 right_vector(const ViewAngles& angles) noexcept 21 | { 22 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); 23 | 24 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 25 | } 26 | Vector3 up_vector(const ViewAngles& angles) noexcept 27 | { 28 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); 29 | 30 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 31 | } 32 | 33 | Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept 34 | { 35 | return mat_camera_view(forward_vector(angles), right_vector(angles), up_vector(angles), cam_origin); 36 | } 37 | 38 | Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, 39 | const float far) noexcept 40 | { 41 | // NOTE: Need magic number to fix fov calculation, since source inherit Quake proj matrix calculation 42 | constexpr auto k_multiply_factor = 0.75f; 43 | 44 | const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f) * k_multiply_factor; 45 | 46 | return { 47 | {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, 48 | {0, 1.f / (fov_half_tan), 0, 0}, 49 | {0, 0, (far + near) / (far - near), -(2.f * far * near) / (far - near)}, 50 | {0, 0, 1, 0}, 51 | }; 52 | } 53 | } // namespace omath::source_engine 54 | -------------------------------------------------------------------------------- /source/engines/unity_engine/camera.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/22/2025. 3 | // 4 | #include 5 | #include 6 | 7 | namespace omath::unity_engine 8 | { 9 | Camera::Camera(const Vector3& position, const ViewAngles& view_angles, const projection::ViewPort& view_port, 10 | const projection::FieldOfView& fov, const float near, const float far) 11 | : projection::Camera(position, view_angles, view_port, fov, near, far) 12 | { 13 | } 14 | void Camera::look_at([[maybe_unused]] const Vector3& target) 15 | { 16 | throw std::runtime_error("Not implemented"); 17 | } 18 | Mat4X4 Camera::calc_view_matrix() const noexcept 19 | { 20 | return unity_engine::calc_view_matrix(m_view_angles, m_origin); 21 | } 22 | Mat4X4 Camera::calc_projection_matrix() const noexcept 23 | { 24 | return calc_perspective_projection_matrix(m_field_of_view.as_degrees(), m_view_port.aspect_ratio(), 25 | m_near_plane_distance, m_far_plane_distance); 26 | } 27 | } // namespace omath::unity_engine 28 | -------------------------------------------------------------------------------- /source/engines/unity_engine/formulas.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/22/2025. 3 | // 4 | #include "omath/engines/unity_engine/formulas.hpp" 5 | 6 | namespace omath::unity_engine 7 | { 8 | Vector3 forward_vector(const ViewAngles& angles) noexcept 9 | { 10 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); 11 | 12 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 13 | } 14 | Vector3 right_vector(const ViewAngles& angles) noexcept 15 | { 16 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); 17 | 18 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 19 | } 20 | Vector3 up_vector(const ViewAngles& angles) noexcept 21 | { 22 | const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); 23 | 24 | return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; 25 | } 26 | Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept 27 | { 28 | return mat_camera_view(forward_vector(angles), -right_vector(angles), 29 | up_vector(angles), cam_origin); 30 | } 31 | Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept 32 | { 33 | return mat_rotation_axis_x(angles.pitch) 34 | * mat_rotation_axis_y(angles.yaw) 35 | * mat_rotation_axis_z(angles.roll); 36 | } 37 | Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, 38 | const float far) noexcept 39 | { 40 | const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f); 41 | 42 | return { 43 | {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, 44 | {0, 1.f / (fov_half_tan), 0, 0}, 45 | {0, 0, (far + near) / (far - near), -(2.f * far * near) / (far - near)}, 46 | {0, 0, -1.f, 0}, 47 | }; 48 | } 49 | } // namespace omath::unity_engine 50 | -------------------------------------------------------------------------------- /source/pathfinding/a_star.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 28.07.2024. 3 | // 4 | #include "omath/pathfinding/a_star.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace omath::pathfinding 11 | { 12 | struct PathNode final 13 | { 14 | std::optional> came_from; 15 | float g_cost = 0.f; 16 | }; 17 | 18 | std::vector> 19 | Astar::reconstruct_final_path(const std::unordered_map, PathNode>& closed_list, 20 | const Vector3& current) noexcept 21 | { 22 | std::vector> path; 23 | std::optional current_opt = current; 24 | 25 | while (current_opt) 26 | { 27 | path.push_back(*current_opt); 28 | 29 | auto it = closed_list.find(*current_opt); 30 | 31 | if (it == closed_list.end()) 32 | break; 33 | 34 | current_opt = it->second.came_from; 35 | } 36 | 37 | std::ranges::reverse(path); 38 | return path; 39 | } 40 | auto Astar::get_perfect_node(const std::unordered_map, PathNode>& open_list, 41 | const Vector3& end_vertex) noexcept 42 | { 43 | return std::ranges::min_element(open_list, 44 | [&end_vertex](const auto& a, const auto& b) 45 | { 46 | const float fa = a.second.g_cost + a.first.distance_to(end_vertex); 47 | const float fb = b.second.g_cost + b.first.distance_to(end_vertex); 48 | return fa < fb; 49 | }); 50 | } 51 | 52 | std::vector> Astar::find_path(const Vector3& start, const Vector3& end, 53 | const NavigationMesh& nav_mesh) noexcept 54 | { 55 | std::unordered_map, PathNode> closed_list; 56 | std::unordered_map, PathNode> open_list; 57 | 58 | auto maybe_start_vertex = nav_mesh.get_closest_vertex(start); 59 | auto maybe_end_vertex = nav_mesh.get_closest_vertex(end); 60 | 61 | if (!maybe_start_vertex || !maybe_end_vertex) 62 | return {}; 63 | 64 | const auto start_vertex = maybe_start_vertex.value(); 65 | const auto end_vertex = maybe_end_vertex.value(); 66 | 67 | open_list.emplace(start_vertex, PathNode{std::nullopt, 0.f}); 68 | 69 | while (!open_list.empty()) 70 | { 71 | auto current_it = get_perfect_node(open_list, end_vertex); 72 | 73 | const auto current = current_it->first; 74 | const auto current_node = current_it->second; 75 | 76 | if (current == end_vertex) 77 | return reconstruct_final_path(closed_list, current); 78 | 79 | closed_list.emplace(current, current_node); 80 | open_list.erase(current_it); 81 | 82 | for (const auto& neighbor: nav_mesh.get_neighbors(current)) 83 | { 84 | if (closed_list.contains(neighbor)) 85 | continue; 86 | 87 | const float tentative_g_cost = current_node.g_cost + neighbor.distance_to(current); 88 | 89 | // ReSharper disable once CppTooWideScopeInitStatement 90 | const auto open_it = open_list.find(neighbor); 91 | 92 | if (open_it == open_list.end() || tentative_g_cost < open_it->second.g_cost) 93 | open_list[neighbor] = PathNode{current, tentative_g_cost}; 94 | } 95 | } 96 | 97 | return {}; 98 | } 99 | } // namespace omath::pathfinding 100 | -------------------------------------------------------------------------------- /source/pathfinding/navigation_mesh.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 28.07.2024. 3 | // 4 | #include "omath/pathfinding/navigation_mesh.hpp" 5 | #include 6 | #include 7 | namespace omath::pathfinding 8 | { 9 | std::expected, std::string> 10 | NavigationMesh::get_closest_vertex(const Vector3& point) const noexcept 11 | { 12 | const auto res = std::ranges::min_element(m_vertex_map, [&point](const auto& a, const auto& b) 13 | { return a.first.distance_to(point) < b.first.distance_to(point); }); 14 | 15 | if (res == m_vertex_map.cend()) 16 | return std::unexpected("Failed to get clossest point"); 17 | 18 | return res->first; 19 | } 20 | 21 | const std::vector>& NavigationMesh::get_neighbors(const Vector3& vertex) const noexcept 22 | { 23 | return m_vertex_map.at(vertex); 24 | } 25 | 26 | bool NavigationMesh::empty() const 27 | { 28 | return m_vertex_map.empty(); 29 | } 30 | 31 | std::vector NavigationMesh::serialize() const noexcept 32 | { 33 | auto dump_to_vector = [](const T& t, std::vector& vec) 34 | { 35 | for (size_t i = 0; i < sizeof(t); i++) 36 | vec.push_back(*(reinterpret_cast(&t) + i)); 37 | }; 38 | 39 | std::vector raw; 40 | 41 | for (const auto& [vertex, neighbors]: m_vertex_map) 42 | { 43 | const auto neighbors_count = neighbors.size(); 44 | 45 | dump_to_vector(vertex, raw); 46 | dump_to_vector(neighbors_count, raw); 47 | 48 | for (const auto& neighbor: neighbors) 49 | dump_to_vector(neighbor, raw); 50 | } 51 | return raw; 52 | } 53 | 54 | void NavigationMesh::deserialize(const std::vector& raw) noexcept 55 | { 56 | auto load_from_vector = [](const std::vector& vec, size_t& offset, auto& value) 57 | { 58 | if (offset + sizeof(value) > vec.size()) 59 | { 60 | throw std::runtime_error("Deserialize: Invalid input data size."); 61 | } 62 | std::copy_n(vec.data() + offset, sizeof(value), reinterpret_cast(&value)); 63 | offset += sizeof(value); 64 | }; 65 | 66 | m_vertex_map.clear(); 67 | 68 | size_t offset = 0; 69 | 70 | while (offset < raw.size()) 71 | { 72 | Vector3 vertex; 73 | load_from_vector(raw, offset, vertex); 74 | 75 | uint16_t neighbors_count; 76 | load_from_vector(raw, offset, neighbors_count); 77 | 78 | std::vector> neighbors; 79 | neighbors.reserve(neighbors_count); 80 | 81 | for (size_t i = 0; i < neighbors_count; ++i) 82 | { 83 | Vector3 neighbor; 84 | load_from_vector(raw, offset, neighbor); 85 | neighbors.push_back(neighbor); 86 | } 87 | 88 | m_vertex_map.emplace(vertex, std::move(neighbors)); 89 | } 90 | } 91 | } // namespace omath::pathfinding 92 | -------------------------------------------------------------------------------- /source/projectile_prediction/proj_pred_engine_avx2.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 2/23/2025. 3 | // 4 | #include "omath/projectile_prediction/proj_pred_engine_avx2.hpp" 5 | #include 6 | #include 7 | 8 | #if defined(OMATH_USE_AVX2) && defined(__i386__) && defined(__x86_64__) 9 | #include 10 | #else 11 | #include 12 | #endif 13 | 14 | namespace omath::projectile_prediction 15 | { 16 | std::optional> 17 | ProjPredEngineAvx2::maybe_calculate_aim_point([[maybe_unused]] const Projectile& projectile, 18 | [[maybe_unused]] const Target& target) const 19 | { 20 | #if defined(OMATH_USE_AVX2) && defined(__i386__) && defined(__x86_64__) 21 | const float bulletGravity = m_gravityConstant * projectile.m_gravityScale; 22 | const float v0 = projectile.m_launchSpeed; 23 | const float v0Sqr = v0 * v0; 24 | const Vector3 projOrigin = projectile.m_origin; 25 | 26 | constexpr int SIMD_FACTOR = 8; 27 | float currentTime = m_simulationTimeStep; 28 | 29 | for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep * SIMD_FACTOR) 30 | { 31 | const __m256 times 32 | = _mm256_setr_ps(currentTime, currentTime + m_simulationTimeStep, 33 | currentTime + m_simulationTimeStep * 2, currentTime + m_simulationTimeStep * 3, 34 | currentTime + m_simulationTimeStep * 4, currentTime + m_simulationTimeStep * 5, 35 | currentTime + m_simulationTimeStep * 6, currentTime + m_simulationTimeStep * 7); 36 | 37 | const __m256 targetX 38 | = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.x), times, _mm256_set1_ps(target.m_origin.x)); 39 | const __m256 targetY 40 | = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.y), times, _mm256_set1_ps(target.m_origin.y)); 41 | const __m256 timesSq = _mm256_mul_ps(times, times); 42 | const __m256 targetZ = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.z), times, 43 | _mm256_fnmadd_ps(_mm256_set1_ps(0.5f * m_gravityConstant), timesSq, 44 | _mm256_set1_ps(target.m_origin.z))); 45 | 46 | const __m256 deltaX = _mm256_sub_ps(targetX, _mm256_set1_ps(projOrigin.x)); 47 | const __m256 deltaY = _mm256_sub_ps(targetY, _mm256_set1_ps(projOrigin.y)); 48 | const __m256 deltaZ = _mm256_sub_ps(targetZ, _mm256_set1_ps(projOrigin.z)); 49 | 50 | const __m256 dSqr = _mm256_add_ps(_mm256_mul_ps(deltaX, deltaX), _mm256_mul_ps(deltaY, deltaY)); 51 | 52 | const __m256 bgTimesSq = _mm256_mul_ps(_mm256_set1_ps(bulletGravity), timesSq); 53 | const __m256 term = _mm256_add_ps(deltaZ, _mm256_mul_ps(_mm256_set1_ps(0.5f), bgTimesSq)); 54 | const __m256 termSq = _mm256_mul_ps(term, term); 55 | const __m256 numerator = _mm256_add_ps(dSqr, termSq); 56 | const __m256 denominator = _mm256_add_ps(timesSq, _mm256_set1_ps(1e-8f)); // Avoid division by zero 57 | const __m256 requiredV0Sqr = _mm256_div_ps(numerator, denominator); 58 | 59 | const __m256 v0SqrVec = _mm256_set1_ps(v0Sqr + 1e-3f); 60 | const __m256 mask = _mm256_cmp_ps(requiredV0Sqr, v0SqrVec, _CMP_LE_OQ); 61 | 62 | const unsigned validMask = _mm256_movemask_ps(mask); 63 | 64 | if (!validMask) 65 | continue; 66 | 67 | alignas(32) float validTimes[SIMD_FACTOR]; 68 | _mm256_store_ps(validTimes, times); 69 | 70 | for (int i = 0; i < SIMD_FACTOR; ++i) 71 | { 72 | if (!(validMask & (1 << i))) 73 | continue; 74 | 75 | const float candidateTime = validTimes[i]; 76 | 77 | if (candidateTime > m_maximumSimulationTime) 78 | continue; 79 | 80 | // Fine search around candidate time 81 | for (float fineTime = candidateTime - m_simulationTimeStep * 2; 82 | fineTime <= candidateTime + m_simulationTimeStep * 2; fineTime += m_simulationTimeStep) 83 | { 84 | if (fineTime < 0) 85 | continue; 86 | 87 | const Vector3 targetPos = target.PredictPosition(fineTime, m_gravityConstant); 88 | const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, fineTime); 89 | if (!pitch) 90 | continue; 91 | 92 | const Vector3 delta = targetPos - projOrigin; 93 | const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); 94 | const float height = d * std::tan(angles::DegreesToRadians(*pitch)); 95 | return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); 96 | } 97 | } 98 | } 99 | 100 | // Fallback scalar processing for remaining times 101 | for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep) 102 | { 103 | const Vector3 targetPos = target.PredictPosition(currentTime, m_gravityConstant); 104 | const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, currentTime); 105 | if (!pitch) 106 | continue; 107 | 108 | const Vector3 delta = targetPos - projOrigin; 109 | const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); 110 | const float height = d * std::tan(angles::DegreesToRadians(*pitch)); 111 | return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); 112 | } 113 | 114 | return std::nullopt; 115 | #else 116 | throw std::runtime_error( 117 | std::format("{} AVX2 feature is not enabled!", std::source_location::current().function_name())); 118 | #endif 119 | } 120 | ProjPredEngineAvx2::ProjPredEngineAvx2(const float gravity_constant, const float simulation_time_step, 121 | const float maximum_simulation_time) 122 | : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), 123 | m_maximum_simulation_time(maximum_simulation_time) 124 | { 125 | } 126 | std::optional ProjPredEngineAvx2::calculate_pitch([[maybe_unused]] const Vector3& proj_origin, 127 | [[maybe_unused]] const Vector3& target_pos, 128 | [[maybe_unused]] const float bullet_gravity, 129 | [[maybe_unused]] const float v0, 130 | [[maybe_unused]] const float time) 131 | { 132 | #if defined(OMATH_USE_AVX2) && defined(__i386__) && defined(__x86_64__) 133 | if (time <= 0.0f) 134 | return std::nullopt; 135 | 136 | const Vector3 delta = target_pos - proj_origin; 137 | const float dSqr = delta.x * delta.x + delta.y * delta.y; 138 | const float h = delta.z; 139 | 140 | const float term = h + 0.5f * bullet_gravity * time * time; 141 | const float requiredV0Sqr = (dSqr + term * term) / (time * time); 142 | const float v0Sqr = v0 * v0; 143 | 144 | if (requiredV0Sqr > v0Sqr + 1e-3f) 145 | return std::nullopt; 146 | 147 | if (dSqr == 0.0f) 148 | return term >= 0.0f ? 90.0f : -90.0f; 149 | 150 | const float d = std::sqrt(dSqr); 151 | const float tanTheta = term / d; 152 | return angles::RadiansToDegrees(std::atan(tanTheta)); 153 | #else 154 | throw std::runtime_error( 155 | std::format("{} AVX2 feature is not enabled!", std::source_location::current().function_name())); 156 | #endif 157 | } 158 | } // namespace omath::projectile_prediction 159 | -------------------------------------------------------------------------------- /source/projectile_prediction/proj_pred_engine_legacy.cpp: -------------------------------------------------------------------------------- 1 | #include "omath/projectile_prediction/proj_pred_engine_legacy.hpp" 2 | #include 3 | #include 4 | 5 | namespace omath::projectile_prediction 6 | { 7 | ProjPredEngineLegacy::ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step, 8 | const float maximum_simulation_time, const float distance_tolerance) 9 | : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), 10 | m_maximum_simulation_time(maximum_simulation_time), m_distance_tolerance(distance_tolerance) 11 | { 12 | } 13 | 14 | std::optional> ProjPredEngineLegacy::maybe_calculate_aim_point(const Projectile& projectile, 15 | const Target& target) const 16 | { 17 | for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step) 18 | { 19 | const auto predicted_target_position = target.predict_position(time, m_gravity_constant); 20 | 21 | const auto projectile_pitch = 22 | maybe_calculate_projectile_launch_pitch_angle(projectile, predicted_target_position); 23 | 24 | if (!projectile_pitch.has_value()) [[unlikely]] 25 | continue; 26 | 27 | if (!is_projectile_reached_target(predicted_target_position, projectile, projectile_pitch.value(), time)) 28 | continue; 29 | 30 | const auto delta2d = (predicted_target_position - projectile.m_origin).length_2d(); 31 | const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value())); 32 | 33 | return Vector3(predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height); 34 | } 35 | return std::nullopt; 36 | } 37 | 38 | std::optional ProjPredEngineLegacy::maybe_calculate_projectile_launch_pitch_angle( 39 | const Projectile& projectile, const Vector3& target_position) const noexcept 40 | { 41 | const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale; 42 | const auto delta = target_position - projectile.m_origin; 43 | 44 | const auto distance2d = delta.length_2d(); 45 | const auto distance2d_sqr = distance2d * distance2d; 46 | const auto launch_speed_sqr = projectile.m_launch_speed * projectile.m_launch_speed; 47 | 48 | float root = launch_speed_sqr * launch_speed_sqr 49 | - bullet_gravity * (bullet_gravity * distance2d_sqr + 2.0f * delta.z * launch_speed_sqr); 50 | 51 | if (root < 0.0f) [[unlikely]] 52 | return std::nullopt; 53 | 54 | root = std::sqrt(root); 55 | const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d)); 56 | 57 | return angles::radians_to_degrees(angle); 58 | } 59 | 60 | bool ProjPredEngineLegacy::is_projectile_reached_target(const Vector3& target_position, 61 | const Projectile& projectile, const float pitch, 62 | const float time) const noexcept 63 | { 64 | const auto yaw = projectile.m_origin.view_angle_to(target_position).y; 65 | const auto projectile_position = projectile.predict_position(pitch, yaw, time, m_gravity_constant); 66 | 67 | return projectile_position.distance_to(target_position) <= m_distance_tolerance; 68 | } 69 | } // namespace omath::projectile_prediction 70 | -------------------------------------------------------------------------------- /source/projectile_prediction/projectile.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 6/9/2024. 3 | // 4 | 5 | #include "omath/projectile_prediction/projectile.hpp" 6 | #include 7 | 8 | namespace omath::projectile_prediction 9 | { 10 | Vector3 Projectile::predict_position(const float pitch, const float yaw, const float time, 11 | const float gravity) const noexcept 12 | { 13 | auto current_pos = m_origin 14 | + source_engine::forward_vector({source_engine::PitchAngle::from_degrees(-pitch), 15 | source_engine::YawAngle::from_degrees(yaw), 16 | source_engine::RollAngle::from_degrees(0)}) 17 | * m_launch_speed * time; 18 | current_pos.z -= (gravity * m_gravity_scale) * (time * time) * 0.5f; 19 | 20 | return current_pos; 21 | } 22 | } // namespace omath::projectile_prediction 23 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | 3 | project(unit_tests) 4 | 5 | include(GoogleTest) 6 | 7 | file(GLOB_RECURSE UNIT_TESTS_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") 8 | add_executable(unit_tests ${UNIT_TESTS_SOURCES}) 9 | 10 | set_target_properties(unit_tests PROPERTIES 11 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}" 12 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}" 13 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}" 14 | UNITY_BUILD ON 15 | UNITY_BUILD_BATCH_SIZE 20 16 | CXX_STANDARD 23 17 | CXX_STANDARD_REQUIRED ON) 18 | 19 | 20 | target_link_libraries(unit_tests PRIVATE gtest gtest_main omath::omath) 21 | 22 | gtest_discover_tests(unit_tests) -------------------------------------------------------------------------------- /tests/engines/unit_test_iw_engine.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 3/17/2025. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | TEST(UnitTestIwEngine, ForwardVector) 11 | { 12 | const auto forward = omath::iw_engine::forward_vector({}); 13 | 14 | EXPECT_EQ(forward, omath::iw_engine::k_abs_forward); 15 | } 16 | 17 | TEST(UnitTestIwEngine, RightVector) 18 | { 19 | const auto right = omath::iw_engine::right_vector({}); 20 | 21 | EXPECT_EQ(right, omath::iw_engine::k_abs_right); 22 | } 23 | 24 | TEST(UnitTestIwEngine, UpVector) 25 | { 26 | const auto up = omath::iw_engine::up_vector({}); 27 | EXPECT_EQ(up, omath::iw_engine::k_abs_up); 28 | } 29 | 30 | TEST(UnitTestIwEngine, ForwardVectorRotationYaw) 31 | { 32 | omath::iw_engine::ViewAngles angles; 33 | 34 | angles.yaw = omath::iw_engine::YawAngle::from_degrees(-90.f); 35 | 36 | const auto forward = omath::iw_engine::forward_vector(angles); 37 | EXPECT_NEAR(forward.x, omath::iw_engine::k_abs_right.x, 0.00001f); 38 | EXPECT_NEAR(forward.y, omath::iw_engine::k_abs_right.y, 0.00001f); 39 | EXPECT_NEAR(forward.z, omath::iw_engine::k_abs_right.z, 0.00001f); 40 | } 41 | 42 | TEST(UnitTestIwEngine, ForwardVectorRotationPitch) 43 | { 44 | omath::iw_engine::ViewAngles angles; 45 | 46 | angles.pitch = omath::iw_engine::PitchAngle::from_degrees(-89.f); 47 | 48 | const auto forward = omath::iw_engine::forward_vector(angles); 49 | EXPECT_NEAR(forward.x, omath::iw_engine::k_abs_up.x, 0.02f); 50 | EXPECT_NEAR(forward.y, omath::iw_engine::k_abs_up.y, 0.01f); 51 | EXPECT_NEAR(forward.z, omath::iw_engine::k_abs_up.z, 0.01f); 52 | } 53 | 54 | TEST(UnitTestIwEngine, ForwardVectorRotationRoll) 55 | { 56 | omath::iw_engine::ViewAngles angles; 57 | 58 | angles.roll = omath::iw_engine::RollAngle::from_degrees(90.f); 59 | 60 | const auto forward = omath::iw_engine::up_vector(angles); 61 | EXPECT_NEAR(forward.x, omath::iw_engine::k_abs_right.x, 0.00001f); 62 | EXPECT_NEAR(forward.y, omath::iw_engine::k_abs_right.y, 0.00001f); 63 | EXPECT_NEAR(forward.z, omath::iw_engine::k_abs_right.z, 0.00001f); 64 | } 65 | 66 | TEST(UnitTestIwEngine, ProjectTargetMovedFromCamera) 67 | { 68 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); 69 | const auto cam = omath::iw_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); 70 | 71 | 72 | for (float distance = 0.02f; distance < 1000.f; distance += 0.01f) 73 | { 74 | const auto projected = cam.world_to_screen({distance, 0, 0}); 75 | 76 | EXPECT_TRUE(projected.has_value()); 77 | 78 | if (!projected.has_value()) 79 | continue; 80 | 81 | EXPECT_NEAR(projected->x, 960, 0.00001f); 82 | EXPECT_NEAR(projected->y, 540, 0.00001f); 83 | } 84 | } 85 | 86 | TEST(UnitTestIwEngine, CameraSetAndGetFov) 87 | { 88 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); 89 | auto cam = omath::iw_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); 90 | 91 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 90.f); 92 | cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); 93 | 94 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); 95 | } 96 | 97 | TEST(UnitTestIwEngine, CameraSetAndGetOrigin) 98 | { 99 | auto cam = omath::iw_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, {}, 0.01f, 1000.f); 100 | 101 | EXPECT_EQ(cam.get_origin(), omath::Vector3{}); 102 | cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); 103 | 104 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); 105 | } -------------------------------------------------------------------------------- /tests/engines/unit_test_open_gl.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/23/2024. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | TEST(UnitTestOpenGL, ForwardVector) 11 | { 12 | const auto forward = omath::opengl_engine::forward_vector({}); 13 | EXPECT_EQ(forward, omath::opengl_engine::k_abs_forward); 14 | } 15 | 16 | TEST(UnitTestOpenGL, RightVector) 17 | { 18 | const auto right = omath::opengl_engine::right_vector({}); 19 | EXPECT_EQ(right, omath::opengl_engine::k_abs_right); 20 | } 21 | 22 | TEST(UnitTestOpenGL, UpVector) 23 | { 24 | const auto up = omath::opengl_engine::up_vector({}); 25 | EXPECT_EQ(up, omath::opengl_engine::k_abs_up); 26 | } 27 | 28 | TEST(UnitTestOpenGL, ForwardVectorRotationYaw) 29 | { 30 | omath::opengl_engine::ViewAngles angles; 31 | 32 | angles.yaw = omath::opengl_engine::YawAngle::from_degrees(90.f); 33 | 34 | const auto forward = omath::opengl_engine::forward_vector(angles); 35 | EXPECT_NEAR(forward.x, omath::opengl_engine::k_abs_right.x, 0.00001f); 36 | EXPECT_NEAR(forward.y, omath::opengl_engine::k_abs_right.y, 0.00001f); 37 | EXPECT_NEAR(forward.z, omath::opengl_engine::k_abs_right.z, 0.00001f); 38 | } 39 | 40 | 41 | 42 | TEST(UnitTestOpenGL, ForwardVectorRotationPitch) 43 | { 44 | omath::opengl_engine::ViewAngles angles; 45 | 46 | angles.pitch = omath::opengl_engine::PitchAngle::from_degrees(-90.f); 47 | 48 | const auto forward = omath::opengl_engine::forward_vector(angles); 49 | EXPECT_NEAR(forward.x, omath::opengl_engine::k_abs_up.x, 0.00001f); 50 | EXPECT_NEAR(forward.y, omath::opengl_engine::k_abs_up.y, 0.00001f); 51 | EXPECT_NEAR(forward.z, omath::opengl_engine::k_abs_up.z, 0.00001f); 52 | } 53 | 54 | TEST(UnitTestOpenGL, ForwardVectorRotationRoll) 55 | { 56 | omath::opengl_engine::ViewAngles angles; 57 | 58 | angles.roll = omath::opengl_engine::RollAngle::from_degrees(-90.f); 59 | 60 | const auto forward = omath::opengl_engine::up_vector(angles); 61 | EXPECT_NEAR(forward.x, omath::opengl_engine::k_abs_right.x, 0.00001f); 62 | EXPECT_NEAR(forward.y, omath::opengl_engine::k_abs_right.y, 0.00001f); 63 | EXPECT_NEAR(forward.z, omath::opengl_engine::k_abs_right.z, 0.00001f); 64 | } 65 | 66 | TEST(UnitTestOpenGL, ProjectTargetMovedFromCamera) 67 | { 68 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); 69 | const auto cam = omath::opengl_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); 70 | 71 | 72 | for (float distance = -10.f; distance > -1000.f; distance -= 0.01f) 73 | { 74 | const auto projected = cam.world_to_screen({0, 0, distance}); 75 | 76 | EXPECT_TRUE(projected.has_value()); 77 | 78 | if (!projected.has_value()) 79 | continue; 80 | 81 | EXPECT_NEAR(projected->x, 960, 0.00001f); 82 | EXPECT_NEAR(projected->y, 540, 0.00001f); 83 | } 84 | } 85 | 86 | TEST(UnitTestOpenGL, CameraSetAndGetFov) 87 | { 88 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); 89 | auto cam = omath::opengl_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); 90 | 91 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 90.f); 92 | cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); 93 | 94 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); 95 | } 96 | 97 | TEST(UnitTestOpenGL, CameraSetAndGetOrigin) 98 | { 99 | auto cam = omath::opengl_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, {}, 0.01f, 1000.f); 100 | 101 | EXPECT_EQ(cam.get_origin(), omath::Vector3{}); 102 | cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); 103 | 104 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); 105 | } -------------------------------------------------------------------------------- /tests/engines/unit_test_source_engine.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/23/2024. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | TEST(UnitTestSourceEngine, ForwardVector) 11 | { 12 | const auto forward = omath::source_engine::forward_vector({}); 13 | 14 | EXPECT_EQ(forward, omath::source_engine::k_abs_forward); 15 | } 16 | 17 | TEST(UnitTestSourceEngine, RightVector) 18 | { 19 | const auto right = omath::source_engine::right_vector({}); 20 | 21 | EXPECT_EQ(right, omath::source_engine::k_abs_right); 22 | } 23 | 24 | TEST(UnitTestSourceEngine, UpVector) 25 | { 26 | const auto up = omath::source_engine::up_vector({}); 27 | EXPECT_EQ(up, omath::source_engine::k_abs_up); 28 | } 29 | 30 | TEST(UnitTestSourceEngine, ForwardVectorRotationYaw) 31 | { 32 | omath::source_engine::ViewAngles angles; 33 | 34 | angles.yaw = omath::source_engine::YawAngle::from_degrees(-90.f); 35 | 36 | const auto forward = omath::source_engine::forward_vector(angles); 37 | EXPECT_NEAR(forward.x, omath::source_engine::k_abs_right.x, 0.00001f); 38 | EXPECT_NEAR(forward.y, omath::source_engine::k_abs_right.y, 0.00001f); 39 | EXPECT_NEAR(forward.z, omath::source_engine::k_abs_right.z, 0.00001f); 40 | } 41 | 42 | TEST(UnitTestSourceEngine, ForwardVectorRotationPitch) 43 | { 44 | omath::source_engine::ViewAngles angles; 45 | 46 | angles.pitch = omath::source_engine::PitchAngle::from_degrees(-89.f); 47 | 48 | const auto forward = omath::source_engine::forward_vector(angles); 49 | EXPECT_NEAR(forward.x, omath::source_engine::k_abs_up.x, 0.02f); 50 | EXPECT_NEAR(forward.y, omath::source_engine::k_abs_up.y, 0.01f); 51 | EXPECT_NEAR(forward.z, omath::source_engine::k_abs_up.z, 0.01f); 52 | } 53 | 54 | TEST(UnitTestSourceEngine, ForwardVectorRotationRoll) 55 | { 56 | omath::source_engine::ViewAngles angles; 57 | 58 | angles.roll = omath::source_engine::RollAngle::from_degrees(90.f); 59 | 60 | const auto forward = omath::source_engine::up_vector(angles); 61 | EXPECT_NEAR(forward.x, omath::source_engine::k_abs_right.x, 0.00001f); 62 | EXPECT_NEAR(forward.y, omath::source_engine::k_abs_right.y, 0.00001f); 63 | EXPECT_NEAR(forward.z, omath::source_engine::k_abs_right.z, 0.00001f); 64 | } 65 | 66 | TEST(UnitTestSourceEngine, ProjectTargetMovedFromCamera) 67 | { 68 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); 69 | const auto cam = omath::source_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); 70 | 71 | 72 | for (float distance = 0.02f; distance < 1000.f; distance += 0.01f) 73 | { 74 | const auto projected = cam.world_to_screen({distance, 0, 0}); 75 | 76 | EXPECT_TRUE(projected.has_value()); 77 | 78 | if (!projected.has_value()) 79 | continue; 80 | 81 | EXPECT_NEAR(projected->x, 960, 0.00001f); 82 | EXPECT_NEAR(projected->y, 540, 0.00001f); 83 | } 84 | } 85 | 86 | TEST(UnitTestSourceEngine, ProjectTargetMovedUp) 87 | { 88 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); 89 | const auto cam = omath::source_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); 90 | 91 | auto prev = 1080.f; 92 | for (float distance = 0.0f; distance < 10.f; distance += 1.f) 93 | { 94 | const auto projected = cam.world_to_screen({100.f, 0, distance}); 95 | EXPECT_TRUE(projected.has_value()); 96 | 97 | if (!projected.has_value()) 98 | continue; 99 | 100 | EXPECT_TRUE(projected->y < prev); 101 | 102 | prev = projected->y; 103 | } 104 | } 105 | 106 | TEST(UnitTestSourceEngine, CameraSetAndGetFov) 107 | { 108 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); 109 | auto cam = omath::source_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); 110 | 111 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 90.f); 112 | cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); 113 | 114 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); 115 | } 116 | 117 | TEST(UnitTestSourceEngine, CameraSetAndGetOrigin) 118 | { 119 | auto cam = omath::source_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, {}, 0.01f, 1000.f); 120 | 121 | EXPECT_EQ(cam.get_origin(), omath::Vector3{}); 122 | cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); 123 | 124 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); 125 | } -------------------------------------------------------------------------------- /tests/engines/unit_test_unity_engine.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/27/2024. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | TEST(UnitTestUnityEngine, ForwardVector) 11 | { 12 | const auto forward = omath::unity_engine::forward_vector({}); 13 | 14 | EXPECT_EQ(forward, omath::unity_engine::k_abs_forward); 15 | } 16 | 17 | TEST(UnitTestUnityEngine, ForwardVectorRotationYaw) 18 | { 19 | omath::unity_engine::ViewAngles angles; 20 | 21 | angles.yaw = omath::unity_engine::YawAngle::from_degrees(90.f); 22 | 23 | const auto forward = omath::unity_engine::forward_vector(angles); 24 | EXPECT_NEAR(forward.x, omath::unity_engine::k_abs_right.x, 0.00001f); 25 | EXPECT_NEAR(forward.y, omath::unity_engine::k_abs_right.y, 0.00001f); 26 | EXPECT_NEAR(forward.z, omath::unity_engine::k_abs_right.z, 0.00001f); 27 | } 28 | 29 | TEST(UnitTestUnityEngine, ForwardVectorRotationPitch) 30 | { 31 | omath::unity_engine::ViewAngles angles; 32 | 33 | angles.pitch = omath::unity_engine::PitchAngle::from_degrees(-90.f); 34 | 35 | const auto forward = omath::unity_engine::forward_vector(angles); 36 | EXPECT_NEAR(forward.x, omath::unity_engine::k_abs_up.x, 0.00001f); 37 | EXPECT_NEAR(forward.y, omath::unity_engine::k_abs_up.y, 0.00001f); 38 | EXPECT_NEAR(forward.z, omath::unity_engine::k_abs_up.z, 0.00001f); 39 | } 40 | 41 | TEST(UnitTestUnityEngine, ForwardVectorRotationRoll) 42 | { 43 | omath::unity_engine::ViewAngles angles; 44 | 45 | angles.roll = omath::unity_engine::RollAngle::from_degrees(-90.f); 46 | 47 | const auto forward = omath::unity_engine::up_vector(angles); 48 | EXPECT_NEAR(forward.x, omath::unity_engine::k_abs_right.x, 0.00001f); 49 | EXPECT_NEAR(forward.y, omath::unity_engine::k_abs_right.y, 0.00001f); 50 | EXPECT_NEAR(forward.z, omath::unity_engine::k_abs_right.z, 0.00001f); 51 | } 52 | 53 | TEST(UnitTestUnityEngine, RightVector) 54 | { 55 | const auto right = omath::unity_engine::right_vector({}); 56 | 57 | EXPECT_EQ(right, omath::unity_engine::k_abs_right); 58 | } 59 | 60 | TEST(UnitTestUnityEngine, UpVector) 61 | { 62 | const auto up = omath::unity_engine::up_vector({}); 63 | EXPECT_EQ(up, omath::unity_engine::k_abs_up); 64 | } 65 | 66 | TEST(UnitTestUnityEngine, ProjectTargetMovedFromCamera) 67 | { 68 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f); 69 | const auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.01f, 1000.f); 70 | 71 | 72 | for (float distance = 0.02f; distance < 100.f; distance += 0.01f) 73 | { 74 | const auto projected = cam.world_to_screen({0, 0, distance}); 75 | 76 | EXPECT_TRUE(projected.has_value()); 77 | 78 | if (!projected.has_value()) 79 | continue; 80 | 81 | EXPECT_NEAR(projected->x, 640, 0.00001f); 82 | EXPECT_NEAR(projected->y, 360, 0.00001f); 83 | } 84 | } 85 | TEST(UnitTestUnityEngine, Project) 86 | { 87 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f); 88 | 89 | const auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.03f, 1000.f); 90 | const auto proj = cam.world_to_screen({5.f, 3, 10.f}); 91 | std::println("{} {}", proj->x, proj->y); 92 | } 93 | 94 | TEST(UnitTestUnityEngine, CameraSetAndGetFov) 95 | { 96 | constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); 97 | auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); 98 | 99 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 90.f); 100 | cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); 101 | 102 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); 103 | } 104 | 105 | TEST(UnitTestUnityEngine, CameraSetAndGetOrigin) 106 | { 107 | auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, {}, 0.01f, 1000.f); 108 | 109 | EXPECT_EQ(cam.get_origin(), omath::Vector3{}); 110 | cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); 111 | 112 | EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); 113 | } -------------------------------------------------------------------------------- /tests/general/unit_test_a_star.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 18.08.2024. 3 | // 4 | #include 5 | #include 6 | 7 | TEST(unit_test_a_star, finding_right_path) 8 | { 9 | omath::pathfinding::NavigationMesh mesh; 10 | 11 | mesh.m_vertex_map[{0.f, 0.f, 0.f}] = {{0.f, 1.f, 0.f}}; 12 | mesh.m_vertex_map[{0.f, 1.f, 0.f}] = {{0.f, 2.f, 0.f}}; 13 | mesh.m_vertex_map[{0.f, 2.f, 0.f}] = {{0.f, 3.f, 0.f}}; 14 | mesh.m_vertex_map[{0.f, 3.f, 0.f}] = {}; 15 | std::ignore = omath::pathfinding::Astar::find_path({}, {0.f, 3.f, 0.f}, mesh); 16 | } -------------------------------------------------------------------------------- /tests/general/unit_test_angle.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/30/2024. 3 | // 4 | -------------------------------------------------------------------------------- /tests/general/unit_test_angles.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/30/2024. 3 | // 4 | #include 5 | #include 6 | 7 | TEST(unit_test_angles, radians_to_deg) 8 | { 9 | constexpr float rad = 67; 10 | 11 | EXPECT_NEAR(omath::angles::radians_to_degrees(rad), 3838.82f, 0.01f); 12 | } 13 | 14 | TEST(unit_test_angles, degrees_to_radians) 15 | { 16 | constexpr float degree = 90; 17 | 18 | EXPECT_NEAR(omath::angles::degrees_to_radians(degree), 1.5708f, 0.01f); 19 | } 20 | 21 | TEST(unit_test_angles, horizontal_fov_to_verical) 22 | { 23 | constexpr float hFov = 90; 24 | constexpr float aspectRation = 16.0f / 9.0f; 25 | const auto verticalFov = omath::angles::horizontal_fov_to_vertical(hFov, aspectRation); 26 | 27 | EXPECT_NEAR(verticalFov, 58.71f, 0.01f); 28 | } 29 | 30 | TEST(unit_test_angles, vertical_to_horizontal) 31 | { 32 | constexpr float vFov = 58.71; 33 | constexpr float aspectRation = 16.0f / 9.0f; 34 | const auto horizontalFov = omath::angles::vertical_fov_to_horizontal(vFov, aspectRation); 35 | 36 | EXPECT_NEAR(horizontalFov, 89.99f, 0.01f); 37 | } 38 | TEST(unit_test_angles, wrap_angle) 39 | { 40 | const float wrapped = omath::angles::wrap_angle(361.f, 0.f, 360.f); 41 | 42 | EXPECT_NEAR(wrapped, 1.f, 0.01f); 43 | } 44 | TEST(unit_test_angles, wrap_angle_negative_range) 45 | { 46 | const float wrapped = omath::angles::wrap_angle(-90.f, 0.f, 360.f); 47 | 48 | EXPECT_NEAR(wrapped, 270.f, 0.01f); 49 | } -------------------------------------------------------------------------------- /tests/general/unit_test_box_primitive.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 4/18/2025. 3 | // 4 | #include 5 | #include 6 | 7 | -------------------------------------------------------------------------------- /tests/general/unit_test_color.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 01.09.2024. 3 | // 4 | #include 5 | #include 6 | 7 | 8 | using namespace omath; 9 | 10 | class UnitTestColor : public ::testing::Test 11 | { 12 | protected: 13 | Color color1; 14 | Color color2; 15 | 16 | void SetUp() override 17 | { 18 | color1 = Color::red(); 19 | color2 = Color::green(); 20 | } 21 | }; 22 | 23 | // Test constructors 24 | TEST_F(UnitTestColor, Constructor_Float) 25 | { 26 | constexpr Color color(0.5f, 0.5f, 0.5f, 1.0f); 27 | EXPECT_FLOAT_EQ(color.x, 0.5f); 28 | EXPECT_FLOAT_EQ(color.y, 0.5f); 29 | EXPECT_FLOAT_EQ(color.z, 0.5f); 30 | EXPECT_FLOAT_EQ(color.w, 1.0f); 31 | } 32 | 33 | TEST_F(UnitTestColor, Constructor_Vector4) 34 | { 35 | constexpr omath::Vector4 vec(0.2f, 0.4f, 0.6f, 0.8f); 36 | Color color(vec); 37 | EXPECT_FLOAT_EQ(color.x, 0.2f); 38 | EXPECT_FLOAT_EQ(color.y, 0.4f); 39 | EXPECT_FLOAT_EQ(color.z, 0.6f); 40 | EXPECT_FLOAT_EQ(color.w, 0.8f); 41 | } 42 | 43 | // Test static methods for color creation 44 | TEST_F(UnitTestColor, FromRGBA) 45 | { 46 | constexpr Color color = Color::from_rgba(128, 64, 32, 255); 47 | EXPECT_FLOAT_EQ(color.x, 128.0f / 255.0f); 48 | EXPECT_FLOAT_EQ(color.y, 64.0f / 255.0f); 49 | EXPECT_FLOAT_EQ(color.z, 32.0f / 255.0f); 50 | EXPECT_FLOAT_EQ(color.w, 1.0f); 51 | } 52 | 53 | TEST_F(UnitTestColor, FromHSV) 54 | { 55 | constexpr Color color = Color::from_hsv(0.0f, 1.0f, 1.0f); // Red in HSV 56 | EXPECT_FLOAT_EQ(color.x, 1.0f); 57 | EXPECT_FLOAT_EQ(color.y, 0.0f); 58 | EXPECT_FLOAT_EQ(color.z, 0.0f); 59 | EXPECT_FLOAT_EQ(color.w, 1.0f); 60 | } 61 | 62 | // Test HSV conversion 63 | TEST_F(UnitTestColor, ToHSV) 64 | { 65 | Hsv hsv = color1.to_hsv(); // Red color 66 | EXPECT_FLOAT_EQ(hsv.hue, 0.0f); 67 | EXPECT_FLOAT_EQ(hsv.saturation, 1.0f); 68 | EXPECT_FLOAT_EQ(hsv.value, 1.0f); 69 | } 70 | 71 | // Test color blending 72 | TEST_F(UnitTestColor, Blend) 73 | { 74 | Color blended = color1.blend(color2, 0.5f); 75 | EXPECT_FLOAT_EQ(blended.x, 0.5f); 76 | EXPECT_FLOAT_EQ(blended.y, 0.5f); 77 | EXPECT_FLOAT_EQ(blended.z, 0.0f); 78 | EXPECT_FLOAT_EQ(blended.w, 1.0f); 79 | } 80 | 81 | // Test predefined colors 82 | TEST_F(UnitTestColor, PredefinedColors) 83 | { 84 | constexpr Color red = Color::red(); 85 | constexpr Color green = Color::green(); 86 | constexpr Color blue = Color::blue(); 87 | 88 | EXPECT_FLOAT_EQ(red.x, 1.0f); 89 | EXPECT_FLOAT_EQ(red.y, 0.0f); 90 | EXPECT_FLOAT_EQ(red.z, 0.0f); 91 | EXPECT_FLOAT_EQ(red.w, 1.0f); 92 | 93 | EXPECT_FLOAT_EQ(green.x, 0.0f); 94 | EXPECT_FLOAT_EQ(green.y, 1.0f); 95 | EXPECT_FLOAT_EQ(green.z, 0.0f); 96 | EXPECT_FLOAT_EQ(green.w, 1.0f); 97 | 98 | EXPECT_FLOAT_EQ(blue.x, 0.0f); 99 | EXPECT_FLOAT_EQ(blue.y, 0.0f); 100 | EXPECT_FLOAT_EQ(blue.z, 1.0f); 101 | EXPECT_FLOAT_EQ(blue.w, 1.0f); 102 | } 103 | 104 | // Test non-member function: Blend for Vector3 105 | TEST_F(UnitTestColor, BlendVector3) 106 | { 107 | constexpr Color v1(1.0f, 0.0f, 0.0f, 1.f); // Red 108 | constexpr Color v2(0.0f, 1.0f, 0.0f, 1.f); // Green 109 | constexpr Color blended = v1.blend(v2, 0.5f); 110 | EXPECT_FLOAT_EQ(blended.x, 0.5f); 111 | EXPECT_FLOAT_EQ(blended.y, 0.5f); 112 | EXPECT_FLOAT_EQ(blended.z, 0.0f); 113 | } -------------------------------------------------------------------------------- /tests/general/unit_test_line_trace.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Revised unit‑test suite for LineTracer (segment‑based Möller–Trumbore) 3 | // Pure ASCII: avoids non‑standard characters that MSVC rejects. 4 | // 5 | #include "gtest/gtest.h" 6 | #include "omath/collision/line_tracer.hpp" 7 | #include "omath/triangle.hpp" 8 | #include "omath/vector3.hpp" 9 | #include 10 | 11 | using namespace omath; 12 | using namespace omath::collision; 13 | 14 | using Vec3 = Vector3; 15 | 16 | namespace 17 | { 18 | 19 | // ----------------------------------------------------------------------------- 20 | // Constants & helpers 21 | // ----------------------------------------------------------------------------- 22 | constexpr float kTol = 1e-5f; 23 | 24 | bool VecEqual(const Vec3& a, const Vec3& b, float tol = kTol) 25 | { 26 | return std::fabs(a.x - b.x) < tol && 27 | std::fabs(a.y - b.y) < tol && 28 | std::fabs(a.z - b.z) < tol; 29 | } 30 | 31 | // ----------------------------------------------------------------------------- 32 | // Fixture with one canonical right‑angled triangle in the XY plane. 33 | // ----------------------------------------------------------------------------- 34 | class LineTracerFixture : public ::testing::Test 35 | { 36 | protected: 37 | LineTracerFixture() : 38 | triangle({0.f, 0.f, 0.f}, {1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}) 39 | { 40 | } 41 | 42 | Triangle triangle; 43 | }; 44 | 45 | // ----------------------------------------------------------------------------- 46 | // Data‑driven tests for CanTraceLine 47 | // ----------------------------------------------------------------------------- 48 | struct TraceCase 49 | { 50 | Ray ray; 51 | bool expected_clear; // true => segment does NOT hit the triangle 52 | }; 53 | 54 | class CanTraceLineParam : public LineTracerFixture, 55 | public ::testing::WithParamInterface 56 | { 57 | }; 58 | 59 | TEST_P(CanTraceLineParam, VariousRays) 60 | { 61 | const auto& p = GetParam(); 62 | EXPECT_EQ(LineTracer::can_trace_line(p.ray, triangle), p.expected_clear); 63 | } 64 | 65 | INSTANTIATE_TEST_SUITE_P( 66 | BasicScenarios, 67 | CanTraceLineParam, 68 | ::testing::Values( 69 | TraceCase{Ray{{ 0.3f, 0.3f, -1.f},{ 0.3f, 0.3f, 1.f}}, false}, // hit through centre 70 | TraceCase{Ray{{ 0.3f, 0.3f, 1.f},{ 0.3f, 0.3f, 2.f}}, true}, // parallel above 71 | TraceCase{Ray{{ 0.3f, 0.3f, 0.f},{ 0.3f, 0.3f,-1.f}}, true}, // starts inside, goes away 72 | TraceCase{Ray{{ 2.0f, 2.0f, -1.f},{ 2.0f, 2.0f, 1.f}}, true}, // misses entirely 73 | TraceCase{Ray{{-1.0f,-1.0f, 0.f},{ 1.5f, 1.5f, 0.f}},true}, // lies in plane, outside tri 74 | TraceCase{Ray{{-1.0f,-1.0f, -1.f},{ 0.0f, 0.0f, 0.f}}, true}, // endpoint on vertex 75 | TraceCase{Ray{{-1.0f, 0.0f, -1.f},{ 0.5f, 0.0f, 0.f}}, true} // endpoint on edge 76 | ) 77 | ); 78 | 79 | // ----------------------------------------------------------------------------- 80 | // Validate that the reported hit point is correct for a genuine intersection. 81 | // ----------------------------------------------------------------------------- 82 | TEST_F(LineTracerFixture, HitPointCorrect) 83 | { 84 | constexpr Ray ray{{0.3f, 0.3f, -1.f}, {0.3f, 0.3f, 1.f}}; 85 | constexpr Vec3 expected{0.3f, 0.3f, 0.f}; 86 | 87 | const Vec3 hit = LineTracer::get_ray_hit_point(ray, triangle); 88 | ASSERT_FALSE(VecEqual(hit, ray.end)); 89 | EXPECT_TRUE(VecEqual(hit, expected)); 90 | } 91 | 92 | // ----------------------------------------------------------------------------- 93 | // Triangle far beyond the ray should not block. 94 | // ----------------------------------------------------------------------------- 95 | TEST_F(LineTracerFixture, DistantTriangleClear) 96 | { 97 | constexpr Ray short_ray{{0.f, 0.f, 0.f}, {0.f, 0.f, 1.f}}; 98 | constexpr Triangle distant{{1000.f, 1000.f, 1000.f}, 99 | {1001.f, 1000.f, 1000.f}, 100 | {1000.f, 1001.f, 1000.f}}; 101 | 102 | EXPECT_TRUE(LineTracer::can_trace_line(short_ray, distant)); 103 | } 104 | 105 | TEST(LineTracerTraceRayEdge, CantHit) 106 | { 107 | constexpr omath::Triangle> triangle{{2, 0, 0}, {2, 2, 0}, {2, 2, 2}}; 108 | 109 | constexpr Ray ray{{}, {1.0, 0, 0}, false}; 110 | 111 | EXPECT_TRUE(omath::collision::LineTracer::can_trace_line(ray, triangle)); 112 | } 113 | TEST(LineTracerTraceRayEdge, CanHit) 114 | { 115 | constexpr omath::Triangle> triangle{{2, 0, 0}, {2, 2, 0}, {2, 2, 2}}; 116 | 117 | constexpr Ray ray{{}, {2.1, 0, 0}, false}; 118 | EXPECT_FALSE(omath::collision::LineTracer::can_trace_line(ray, triangle)); 119 | } 120 | } // namespace 121 | -------------------------------------------------------------------------------- /tests/general/unit_test_mat.cpp: -------------------------------------------------------------------------------- 1 | // UnitTestMat.cpp 2 | #include 3 | #include "omath/mat.hpp" 4 | #include "omath/vector3.hpp" 5 | 6 | using namespace omath; 7 | 8 | class UnitTestMat : public ::testing::Test 9 | { 10 | protected: 11 | Mat<2, 2> m1; 12 | Mat<2, 2> m2; 13 | 14 | void SetUp() override 15 | { 16 | m1 = Mat<2, 2>(); 17 | m2 = Mat<2, 2>{{1.0f, 2.0f}, {3.0f, 4.0f}}; 18 | } 19 | }; 20 | 21 | // Test constructors 22 | TEST_F(UnitTestMat, Constructor_Default) 23 | { 24 | Mat<3, 3> m; 25 | EXPECT_EQ(m.row_count(), 3); 26 | EXPECT_EQ(m.columns_count(), 3); 27 | for (size_t i = 0; i < 3; ++i) 28 | for (size_t j = 0; j < 3; ++j) 29 | EXPECT_FLOAT_EQ(m.at(i, j), 0.0f); 30 | } 31 | 32 | TEST_F(UnitTestMat, Constructor_InitializerList) 33 | { 34 | constexpr Mat<2, 2> m{{1.0f, 2.0f}, {3.0f, 4.0f}}; 35 | EXPECT_EQ(m.row_count(), 2); 36 | EXPECT_EQ(m.columns_count(), 2); 37 | EXPECT_FLOAT_EQ(m.at(0, 0), 1.0f); 38 | EXPECT_FLOAT_EQ(m.at(0, 1), 2.0f); 39 | EXPECT_FLOAT_EQ(m.at(1, 0), 3.0f); 40 | EXPECT_FLOAT_EQ(m.at(1, 1), 4.0f); 41 | } 42 | 43 | TEST_F(UnitTestMat, Operator_SquareBrackets) 44 | { 45 | EXPECT_EQ((m2[0, 0]), 1.0f); 46 | EXPECT_EQ((m2[0, 1]), 2.0f); 47 | EXPECT_EQ((m2[1, 0]), 3.0f); 48 | EXPECT_EQ((m2[1, 1]), 4.0f); 49 | } 50 | 51 | TEST_F(UnitTestMat, Constructor_Copy) 52 | { 53 | Mat<2, 2> m3 = m2; 54 | EXPECT_EQ(m3.row_count(), m2.row_count()); 55 | EXPECT_EQ(m3.columns_count(), m2.columns_count()); 56 | EXPECT_FLOAT_EQ(m3.at(0, 0), m2.at(0, 0)); 57 | EXPECT_FLOAT_EQ(m3.at(1, 1), m2.at(1, 1)); 58 | } 59 | 60 | TEST_F(UnitTestMat, Constructor_Move) 61 | { 62 | Mat<2, 2> m3 = std::move(m2); 63 | EXPECT_EQ(m3.row_count(), 2); 64 | EXPECT_EQ(m3.columns_count(), 2); 65 | EXPECT_FLOAT_EQ(m3.at(0, 0), 1.0f); 66 | EXPECT_FLOAT_EQ(m3.at(1, 1), 4.0f); 67 | // m2 is in a valid but unspecified state after move 68 | } 69 | 70 | // Test matrix operations 71 | TEST_F(UnitTestMat, Operator_Multiplication_Matrix) 72 | { 73 | Mat<2, 2> m3 = m2 * m2; 74 | EXPECT_EQ(m3.row_count(), 2); 75 | EXPECT_EQ(m3.columns_count(), 2); 76 | EXPECT_FLOAT_EQ(m3.at(0, 0), 7.0f); 77 | EXPECT_FLOAT_EQ(m3.at(0, 1), 10.0f); 78 | EXPECT_FLOAT_EQ(m3.at(1, 0), 15.0f); 79 | EXPECT_FLOAT_EQ(m3.at(1, 1), 22.0f); 80 | } 81 | 82 | TEST_F(UnitTestMat, Operator_Multiplication_Scalar) 83 | { 84 | Mat<2, 2> m3 = m2 * 2.0f; 85 | EXPECT_FLOAT_EQ(m3.at(0, 0), 2.0f); 86 | EXPECT_FLOAT_EQ(m3.at(1, 1), 8.0f); 87 | } 88 | 89 | TEST_F(UnitTestMat, Operator_Division_Scalar) 90 | { 91 | Mat<2, 2> m3 = m2 / 2.0f; 92 | EXPECT_FLOAT_EQ(m3.at(0, 0), 0.5f); 93 | EXPECT_FLOAT_EQ(m3.at(1, 1), 2.0f); 94 | } 95 | 96 | // Test matrix functions 97 | TEST_F(UnitTestMat, Transpose) 98 | { 99 | Mat<2, 2> m3 = m2.transposed(); 100 | EXPECT_FLOAT_EQ(m3.at(0, 0), m2.at(0, 0)); 101 | EXPECT_FLOAT_EQ(m3.at(0, 1), m2.at(1, 0)); 102 | EXPECT_FLOAT_EQ(m3.at(1, 0), m2.at(0, 1)); 103 | EXPECT_FLOAT_EQ(m3.at(1, 1), m2.at(1, 1)); 104 | } 105 | 106 | TEST_F(UnitTestMat, Determinant) 107 | { 108 | const float det = m2.determinant(); 109 | EXPECT_FLOAT_EQ(det, -2.0f); 110 | } 111 | 112 | TEST_F(UnitTestMat, Sum) 113 | { 114 | const float sum = m2.sum(); 115 | EXPECT_FLOAT_EQ(sum, 10.0f); 116 | } 117 | 118 | TEST_F(UnitTestMat, Clear) 119 | { 120 | m2.clear(); 121 | for (size_t i = 0; i < m2.row_count(); ++i) 122 | for (size_t j = 0; j < m2.columns_count(); ++j) 123 | EXPECT_FLOAT_EQ(m2.at(i, j), 0.0f); 124 | } 125 | 126 | TEST_F(UnitTestMat, ToString) 127 | { 128 | const std::string str = m2.to_string(); 129 | EXPECT_FALSE(str.empty()); 130 | EXPECT_EQ(str, "[[ 1.000, 2.000]\n [ 3.000, 4.000]]"); 131 | } 132 | 133 | // Test assignment operators 134 | TEST_F(UnitTestMat, AssignmentOperator_Copy) 135 | { 136 | Mat<2, 2> m3; 137 | m3 = m2; 138 | EXPECT_EQ(m3.row_count(), m2.row_count()); 139 | EXPECT_EQ(m3.columns_count(), m2.columns_count()); 140 | EXPECT_FLOAT_EQ(m3.at(0, 0), m2.at(0, 0)); 141 | } 142 | 143 | TEST_F(UnitTestMat, AssignmentOperator_Move) 144 | { 145 | Mat<2, 2> m3; 146 | m3 = std::move(m2); 147 | EXPECT_EQ(m3.row_count(), 2); 148 | EXPECT_EQ(m3.columns_count(), 2); 149 | EXPECT_FLOAT_EQ(m3.at(0, 0), 1.0f); 150 | EXPECT_FLOAT_EQ(m3.at(1, 1), 4.0f); 151 | // m2 is in a valid but unspecified state after move 152 | } 153 | 154 | // Test static methods 155 | TEST_F(UnitTestMat, StaticMethod_ToScreenMat) 156 | { 157 | Mat<4, 4> screenMat = Mat<4, 4>::to_screen_mat(800.0f, 600.0f); 158 | EXPECT_FLOAT_EQ(screenMat.at(0, 0), 400.0f); 159 | EXPECT_FLOAT_EQ(screenMat.at(1, 1), -300.0f); 160 | EXPECT_FLOAT_EQ(screenMat.at(3, 0), 400.0f); 161 | EXPECT_FLOAT_EQ(screenMat.at(3, 1), 300.0f); 162 | EXPECT_FLOAT_EQ(screenMat.at(3, 3), 1.0f); 163 | } 164 | 165 | 166 | // Test exception handling in At() method 167 | TEST_F(UnitTestMat, Method_At_OutOfRange) 168 | { 169 | #if !defined(NDEBUG) && defined(OMATH_SUPRESS_SAFETY_CHECKS) 170 | EXPECT_THROW(std::ignore = m2.At(2, 0), std::out_of_range); 171 | EXPECT_THROW(std::ignore = m2.At(0, 2), std::out_of_range); 172 | #endif 173 | } 174 | 175 | // Test Determinant for 3x3 matrix 176 | TEST(UnitTestMatStandalone, Determinant_3x3) 177 | { 178 | constexpr auto det = Mat<3, 3>{{6, 1, 1}, {4, -2, 5}, {2, 8, 7}}.determinant(); 179 | EXPECT_FLOAT_EQ(det, -306.0f); 180 | } 181 | 182 | // Test Minor for 3x3 matrix 183 | TEST(UnitTestMatStandalone, Strip_3x3) 184 | { 185 | constexpr Mat<3, 3> m{{3, 0, 2}, {2, 0, -2}, {0, 1, 1}}; 186 | auto minor = m.strip(0, 0); 187 | EXPECT_EQ(minor.row_count(), 2); 188 | EXPECT_EQ(minor.columns_count(), 2); 189 | EXPECT_FLOAT_EQ(minor.at(0, 0), 0.0f); 190 | EXPECT_FLOAT_EQ(minor.at(0, 1), -2.0f); 191 | EXPECT_FLOAT_EQ(minor.at(1, 0), 1.0f); 192 | EXPECT_FLOAT_EQ(minor.at(1, 1), 1.0f); 193 | } 194 | 195 | // Test Transpose for non-square matrix 196 | TEST(UnitTestMatStandalone, Transpose_NonSquare) 197 | { 198 | constexpr Mat<2, 3> m{{1.0f, 2.0f, 3.0f}, {4.0f, 5.0f, 6.0f}}; 199 | auto transposed = m.transposed(); 200 | EXPECT_EQ(transposed.row_count(), 3); 201 | EXPECT_EQ(transposed.columns_count(), 2); 202 | EXPECT_FLOAT_EQ(transposed.at(0, 0), 1.0f); 203 | EXPECT_FLOAT_EQ(transposed.at(1, 0), 2.0f); 204 | EXPECT_FLOAT_EQ(transposed.at(2, 0), 3.0f); 205 | EXPECT_FLOAT_EQ(transposed.at(0, 1), 4.0f); 206 | EXPECT_FLOAT_EQ(transposed.at(1, 1), 5.0f); 207 | EXPECT_FLOAT_EQ(transposed.at(2, 1), 6.0f); 208 | } 209 | 210 | TEST(UnitTestMatStandalone, Enverse) 211 | { 212 | constexpr Mat<2, 2> m{{1.0f, 3.0f}, {2.0f, 5.0f}}; 213 | constexpr Mat<2,2> mv{{-5.0f, 3.0f}, {2.0f, -1.0f}}; 214 | 215 | EXPECT_EQ(mv, m.inverted()); 216 | } 217 | -------------------------------------------------------------------------------- /tests/general/unit_test_matrix.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by vlad on 5/18/2024. 3 | // 4 | #include 5 | #include 6 | #include "omath/vector3.hpp" 7 | 8 | using namespace omath; 9 | 10 | 11 | class UnitTestMatrix : public ::testing::Test 12 | { 13 | protected: 14 | Matrix m1; 15 | Matrix m2; 16 | 17 | void SetUp() override 18 | { 19 | m1 = Matrix(2, 2); 20 | m2 = Matrix{{1.0f, 2.0f}, {3.0f, 4.0f}}; 21 | } 22 | }; 23 | 24 | // Test constructors 25 | TEST_F(UnitTestMatrix, Constructor_Size) 26 | { 27 | const Matrix m(3, 3); 28 | EXPECT_EQ(m.row_count(), 3); 29 | EXPECT_EQ(m.columns_count(), 3); 30 | } 31 | 32 | TEST_F(UnitTestMatrix, Operator_SquareBrackets) 33 | { 34 | EXPECT_EQ((m2[0, 0]), 1.0f); 35 | EXPECT_EQ((m2[0, 1]), 2.0f); 36 | EXPECT_EQ((m2[1, 0]), 3.0f); 37 | EXPECT_EQ((m2[1, 1]), 4.0f); 38 | } 39 | 40 | 41 | TEST_F(UnitTestMatrix, Constructor_InitializerList) 42 | { 43 | Matrix m{{1.0f, 2.0f}, {3.0f, 4.0f}}; 44 | EXPECT_EQ(m.row_count(), 2); 45 | EXPECT_EQ(m.columns_count(), 2); 46 | EXPECT_FLOAT_EQ(m.at(0, 0), 1.0f); 47 | EXPECT_FLOAT_EQ(m.at(1, 1), 4.0f); 48 | } 49 | 50 | TEST_F(UnitTestMatrix, Constructor_Copy) 51 | { 52 | Matrix m3 = m2; 53 | EXPECT_EQ(m3.row_count(), m2.row_count()); 54 | EXPECT_EQ(m3.columns_count(), m2.columns_count()); 55 | EXPECT_FLOAT_EQ(m3.at(0, 0), m2.at(0, 0)); 56 | } 57 | 58 | TEST_F(UnitTestMatrix, Constructor_Move) 59 | { 60 | Matrix m3 = std::move(m2); 61 | EXPECT_EQ(m3.row_count(), 2); 62 | EXPECT_EQ(m3.columns_count(), 2); 63 | EXPECT_FLOAT_EQ(m3.at(0, 0), 1.0f); 64 | EXPECT_EQ(m2.row_count(), 0); // m2 should be empty after the move 65 | EXPECT_EQ(m2.columns_count(), 0); 66 | } 67 | 68 | // Test matrix operations 69 | TEST_F(UnitTestMatrix, Operator_Multiplication_Matrix) 70 | { 71 | Matrix m3 = m2 * m2; 72 | EXPECT_EQ(m3.row_count(), 2); 73 | EXPECT_EQ(m3.columns_count(), 2); 74 | EXPECT_FLOAT_EQ(m3.at(0, 0), 7.0f); 75 | EXPECT_FLOAT_EQ(m3.at(1, 1), 22.0f); 76 | } 77 | 78 | TEST_F(UnitTestMatrix, Operator_Multiplication_Scalar) 79 | { 80 | Matrix m3 = m2 * 2.0f; 81 | EXPECT_FLOAT_EQ(m3.at(0, 0), 2.0f); 82 | EXPECT_FLOAT_EQ(m3.at(1, 1), 8.0f); 83 | } 84 | 85 | TEST_F(UnitTestMatrix, Operator_Division_Scalar) 86 | { 87 | Matrix m3 = m2 / 2.0f; 88 | EXPECT_FLOAT_EQ(m3.at(0, 0), 0.5f); 89 | EXPECT_FLOAT_EQ(m3.at(1, 1), 2.0f); 90 | } 91 | 92 | // Test matrix functions 93 | TEST_F(UnitTestMatrix, Transpose) 94 | { 95 | Matrix m3 = m2.transpose(); 96 | EXPECT_FLOAT_EQ(m3.at(0, 1), 3.0f); 97 | EXPECT_FLOAT_EQ(m3.at(1, 0), 2.0f); 98 | } 99 | 100 | TEST_F(UnitTestMatrix, Determinant) 101 | { 102 | const float det = m2.determinant(); 103 | EXPECT_FLOAT_EQ(det, -2.0f); 104 | } 105 | 106 | TEST_F(UnitTestMatrix, Minor) 107 | { 108 | const float minor = m2.minor(0, 0); 109 | EXPECT_FLOAT_EQ(minor, 4.0f); 110 | } 111 | 112 | TEST_F(UnitTestMatrix, AlgComplement) 113 | { 114 | const float algComp = m2.alg_complement(0, 0); 115 | EXPECT_FLOAT_EQ(algComp, 4.0f); 116 | } 117 | 118 | TEST_F(UnitTestMatrix, Strip) 119 | { 120 | Matrix m3 = m2.strip(0, 0); 121 | EXPECT_EQ(m3.row_count(), 1); 122 | EXPECT_EQ(m3.columns_count(), 1); 123 | EXPECT_FLOAT_EQ(m3.at(0, 0), 4.0f); 124 | } 125 | 126 | TEST_F(UnitTestMatrix, ProjectionMatrix) 127 | { 128 | const Matrix proj = Matrix::projection_matrix(45.0f, 1.33f, 0.1f, 100.0f); 129 | EXPECT_EQ(proj.row_count(), 4); 130 | EXPECT_EQ(proj.columns_count(), 4); 131 | // Further checks on projection matrix elements could be added 132 | } 133 | 134 | // Test other member functions 135 | TEST_F(UnitTestMatrix, Set) 136 | { 137 | m1.set(3.0f); 138 | EXPECT_FLOAT_EQ(m1.at(0, 0), 3.0f); 139 | EXPECT_FLOAT_EQ(m1.at(1, 1), 3.0f); 140 | } 141 | 142 | TEST_F(UnitTestMatrix, Sum) 143 | { 144 | const float sum = m2.sum(); 145 | EXPECT_FLOAT_EQ(sum, 10.0f); 146 | } 147 | 148 | TEST_F(UnitTestMatrix, Clear) 149 | { 150 | m2.clear(); 151 | EXPECT_FLOAT_EQ(m2.at(0, 0), 0.0f); 152 | EXPECT_FLOAT_EQ(m2.at(1, 1), 0.0f); 153 | } 154 | 155 | TEST_F(UnitTestMatrix, ToString) 156 | { 157 | const std::string str = m2.to_string(); 158 | EXPECT_FALSE(str.empty()); 159 | } 160 | 161 | // Test assignment operators 162 | TEST_F(UnitTestMatrix, AssignmentOperator_Copy) 163 | { 164 | Matrix m3(2, 2); 165 | m3 = m2; 166 | EXPECT_EQ(m3.row_count(), m2.row_count()); 167 | EXPECT_EQ(m3.columns_count(), m2.columns_count()); 168 | EXPECT_FLOAT_EQ(m3.at(0, 0), m2.at(0, 0)); 169 | } 170 | 171 | TEST_F(UnitTestMatrix, AssignmentOperator_Move) 172 | { 173 | Matrix m3(2, 2); 174 | m3 = std::move(m2); 175 | EXPECT_EQ(m3.row_count(), 2); 176 | EXPECT_EQ(m3.columns_count(), 2); 177 | EXPECT_FLOAT_EQ(m3.at(0, 0), 1.0f); 178 | EXPECT_EQ(m2.row_count(), 0); // m2 should be empty after the move 179 | EXPECT_EQ(m2.columns_count(), 0); 180 | } -------------------------------------------------------------------------------- /tests/general/unit_test_prediction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | TEST(UnitTestPrediction, PredictionTest) 5 | { 6 | constexpr omath::projectile_prediction::Target target{ 7 | .m_origin = {100, 0, 90}, .m_velocity = {0, 0, 0}, .m_is_airborne = false}; 8 | constexpr omath::projectile_prediction::Projectile proj = { 9 | .m_origin = {3, 2, 1}, .m_launch_speed = 5000, .m_gravity_scale = 0.4}; 10 | const auto viewPoint = 11 | omath::projectile_prediction::ProjPredEngineLegacy(400, 1.f / 1000.f, 50, 5.f).maybe_calculate_aim_point(proj, target); 12 | 13 | const auto [pitch, yaw, _] = proj.m_origin.view_angle_to(viewPoint.value()).as_tuple(); 14 | 15 | EXPECT_NEAR(42.547142, pitch, 0.01f); 16 | EXPECT_NEAR(-1.181189, yaw, 0.01f); 17 | } 18 | -------------------------------------------------------------------------------- /tests/general/unit_test_projection.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 27.08.2024. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | TEST(UnitTestProjection, Projection) 11 | { 12 | const auto x = omath::Angle::from_degrees(90.f); 13 | auto cam = omath::source_engine::Camera({0, 0, 0}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, x, 0.01f, 1000.f); 14 | 15 | const auto projected = cam.world_to_screen({1000, 0, 50}); 16 | std::print("{} {} {}", projected->x, projected->y, projected->z); 17 | } -------------------------------------------------------------------------------- /tests/general/unit_test_triangle.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 1/6/2025. 3 | // 4 | #include // For std::sqrt, std::isinf, std::isnan 5 | #include 6 | #include 7 | #include "omath/triangle.hpp" 8 | 9 | 10 | using namespace omath; 11 | 12 | class UnitTestTriangle : public ::testing::Test 13 | { 14 | protected: 15 | // Define some Triangles to use in tests 16 | Triangle> t1; 17 | Triangle> t2; 18 | Triangle> t3; 19 | 20 | constexpr void SetUp() override 21 | { 22 | // Triangle with vertices (0, 0, 0), (1, 0, 0), (0, 1, 0) 23 | t1 = Triangle>( 24 | Vector3(0.0f, 0.0f, 0.0f), 25 | Vector3(1.0f, 0.0f, 0.0f), 26 | Vector3(0.0f, 1.0f, 0.0f) 27 | ); 28 | 29 | // Triangle with vertices (1, 2, 3), (4, 5, 6), (7, 8, 9) 30 | t2 = Triangle>( 31 | Vector3(1.0f, 2.0f, 3.0f), 32 | Vector3(4.0f, 5.0f, 6.0f), 33 | Vector3(7.0f, 8.0f, 9.0f) 34 | ); 35 | 36 | // An isosceles right triangle 37 | t3 = Triangle>( 38 | Vector3(0.0f, 0.0f, 0.0f), 39 | Vector3(2.0f, 0.0f, 0.0f), 40 | Vector3(0.0f, 2.0f, 0.0f) 41 | ); 42 | } 43 | }; 44 | 45 | // Test constructor and vertices 46 | TEST_F(UnitTestTriangle, Constructor) 47 | { 48 | constexpr Triangle> t( 49 | Vector3(1.0f, 2.0f, 3.0f), 50 | Vector3(4.0f, 5.0f, 6.0f), 51 | Vector3(7.0f, 8.0f, 9.0f) 52 | ); 53 | 54 | EXPECT_FLOAT_EQ(t.m_vertex1.x, 1.0f); 55 | EXPECT_FLOAT_EQ(t.m_vertex1.y, 2.0f); 56 | EXPECT_FLOAT_EQ(t.m_vertex1.z, 3.0f); 57 | 58 | EXPECT_FLOAT_EQ(t.m_vertex2.x, 4.0f); 59 | EXPECT_FLOAT_EQ(t.m_vertex2.y, 5.0f); 60 | EXPECT_FLOAT_EQ(t.m_vertex2.z, 6.0f); 61 | 62 | EXPECT_FLOAT_EQ(t.m_vertex3.x, 7.0f); 63 | EXPECT_FLOAT_EQ(t.m_vertex3.y, 8.0f); 64 | EXPECT_FLOAT_EQ(t.m_vertex3.z, 9.0f); 65 | } 66 | 67 | // Test CalculateNormal 68 | TEST_F(UnitTestTriangle, CalculateNormal) 69 | { 70 | // For t1, the normal should point in the +Z direction (0, 0, 1) or (0, 0, -1) 71 | const Vector3 normal_t1 = t1.calculate_normal(); 72 | // Check if it's normalized and pointed along Z (sign can differ, so use absolute check) 73 | EXPECT_NEAR(std::fabs(normal_t1.z), 1.0f, 1e-5f); 74 | EXPECT_NEAR(normal_t1.length(), 1.0f, 1e-5f); 75 | 76 | 77 | // For t3, we expect the normal to be along +Z as well 78 | const Vector3 normal_t3 = t3.calculate_normal(); 79 | EXPECT_NEAR(std::fabs(normal_t3.z), 1.0f, 1e-5f); 80 | } 81 | 82 | // Test side lengths 83 | TEST_F(UnitTestTriangle, SideLengths) 84 | { 85 | // For t1 side lengths 86 | EXPECT_FLOAT_EQ(t1.side_a_length(), std::sqrt(1.0f)); // distance between (0,0,0) and (1,0,0) 87 | EXPECT_FLOAT_EQ(t1.side_b_length(), std::sqrt(1.0f + 1.0f)); // distance between (4,5,6) & (7,8,9)... but we are testing t1, so let's be accurate: 88 | // Actually, for t1: vertex2=(1,0,0), vertex3=(0,1,0) 89 | // Dist between (0,1,0) and (1,0,0) = sqrt((1-0)^2 + (0-1)^2) = sqrt(1 + 1) = sqrt(2) 90 | EXPECT_FLOAT_EQ(t1.side_b_length(), std::sqrt(2.0f)); 91 | 92 | // For t3, side a = distance between vertex1=(0,0,0) and vertex2=(2,0,0), which is 2 93 | // side b = distance between vertex3=(0,2,0) and vertex2=(2,0,0), which is sqrt(2^2 + (-2)^2)= sqrt(8)= 2.828... 94 | // We'll just check side a first: 95 | EXPECT_FLOAT_EQ(t3.side_a_length(), 2.0f); 96 | // Then side b: 97 | EXPECT_FLOAT_EQ(t3.side_b_length(), std::sqrt(8.0f)); 98 | } 99 | 100 | // Test side vectors 101 | TEST_F(UnitTestTriangle, SideVectors) 102 | { 103 | const Vector3 sideA_t1 = t1.side_a_vector(); // m_vertex1 - m_vertex2 104 | EXPECT_FLOAT_EQ(sideA_t1.x, 0.0f - 1.0f); 105 | EXPECT_FLOAT_EQ(sideA_t1.y, 0.0f - 0.0f); 106 | EXPECT_FLOAT_EQ(sideA_t1.z, 0.0f - 0.0f); 107 | 108 | const Vector3 sideB_t1 = t1.side_b_vector(); // m_vertex3 - m_vertex2 109 | EXPECT_FLOAT_EQ(sideB_t1.x, 0.0f - 1.0f); 110 | EXPECT_FLOAT_EQ(sideB_t1.y, 1.0f - 0.0f); 111 | EXPECT_FLOAT_EQ(sideB_t1.z, 0.0f - 0.0f); 112 | } 113 | 114 | TEST_F(UnitTestTriangle, IsRectangular) 115 | { 116 | EXPECT_TRUE(Triangle>({2,0,0}, {}, {0,2,0}).is_rectangular()); 117 | } 118 | // Test midpoint 119 | TEST_F(UnitTestTriangle, MidPoint) 120 | { 121 | // For t1, midpoint of (0,0,0), (1,0,0), (0,1,0) 122 | const Vector3 mid1 = t1.mid_point(); 123 | EXPECT_FLOAT_EQ(mid1.x, (0.0f + 1.0f + 0.0f) / 3.0f); 124 | EXPECT_FLOAT_EQ(mid1.y, (0.0f + 0.0f + 1.0f) / 3.0f); 125 | EXPECT_FLOAT_EQ(mid1.z, 0.0f); 126 | 127 | // For t2, midpoint of (1,2,3), (4,5,6), (7,8,9) 128 | const Vector3 mid2 = t2.mid_point(); 129 | EXPECT_FLOAT_EQ(mid2.x, (1.0f + 4.0f + 7.0f) / 3.0f); 130 | EXPECT_FLOAT_EQ(mid2.y, (2.0f + 5.0f + 8.0f) / 3.0f); 131 | EXPECT_FLOAT_EQ(mid2.z, (3.0f + 6.0f + 9.0f) / 3.0f); 132 | } 133 | -------------------------------------------------------------------------------- /tests/general/unit_test_vector2.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vlad on 02.09.2024. 3 | // 4 | 5 | #include // For FLT_MAX and FLT_MIN 6 | #include // For std::isinf and std::isnan 7 | #include 8 | #include 9 | 10 | using namespace omath; 11 | 12 | class UnitTestVector2 : public ::testing::Test 13 | { 14 | protected: 15 | Vector2 v1; 16 | Vector2 v2; 17 | 18 | constexpr void SetUp() override 19 | { 20 | v1 = Vector2(1.0f, 2.0f); 21 | v2 = Vector2(4.0f, 5.0f); 22 | } 23 | }; 24 | 25 | // Test constructor and default values 26 | TEST_F(UnitTestVector2, Constructor_Default) 27 | { 28 | constexpr Vector2 v; 29 | EXPECT_FLOAT_EQ(v.x, 0.0f); 30 | EXPECT_FLOAT_EQ(v.y, 0.0f); 31 | } 32 | 33 | TEST_F(UnitTestVector2, Constructor_Values) 34 | { 35 | constexpr Vector2 v(1.0f, 2.0f); 36 | EXPECT_FLOAT_EQ(v.x, 1.0f); 37 | EXPECT_FLOAT_EQ(v.y, 2.0f); 38 | } 39 | 40 | // Test equality operators 41 | TEST_F(UnitTestVector2, EqualityOperator) 42 | { 43 | constexpr Vector2 v3(1.0f, 2.0f); 44 | EXPECT_TRUE(v1 == v3); 45 | EXPECT_FALSE(v1 == v2); 46 | } 47 | 48 | TEST_F(UnitTestVector2, InequalityOperator) 49 | { 50 | constexpr Vector2 v3(1.0f, 2.0f); 51 | EXPECT_FALSE(v1 != v3); 52 | EXPECT_TRUE(v1 != v2); 53 | } 54 | 55 | // Test arithmetic operators 56 | TEST_F(UnitTestVector2, AdditionOperator) 57 | { 58 | constexpr Vector2 v3 = Vector2(1.0f, 2.0f) + Vector2(4.0f, 5.0f); 59 | EXPECT_FLOAT_EQ(v3.x, 5.0f); 60 | EXPECT_FLOAT_EQ(v3.y, 7.0f); 61 | } 62 | 63 | TEST_F(UnitTestVector2, SubtractionOperator) 64 | { 65 | constexpr Vector2 v3 = Vector2(4.0f, 5.0f) - Vector2(1.0f, 2.0f); 66 | EXPECT_FLOAT_EQ(v3.x, 3.0f); 67 | EXPECT_FLOAT_EQ(v3.y, 3.0f); 68 | } 69 | 70 | TEST_F(UnitTestVector2, MultiplicationOperator) 71 | { 72 | constexpr Vector2 v3 = Vector2(1.0f, 2.0f) * 2.0f; 73 | EXPECT_FLOAT_EQ(v3.x, 2.0f); 74 | EXPECT_FLOAT_EQ(v3.y, 4.0f); 75 | } 76 | 77 | TEST_F(UnitTestVector2, DivisionOperator) 78 | { 79 | constexpr Vector2 v3 = Vector2(4.0f, 5.0f) / 2.0f; 80 | EXPECT_FLOAT_EQ(v3.x, 2.0f); 81 | EXPECT_FLOAT_EQ(v3.y, 2.5f); 82 | } 83 | 84 | TEST_F(UnitTestVector2, NegationOperator) 85 | { 86 | constexpr Vector2 v3 = -Vector2(1.0f, 2.0f); 87 | EXPECT_FLOAT_EQ(v3.x, -1.0f); 88 | EXPECT_FLOAT_EQ(v3.y, -2.0f); 89 | } 90 | 91 | // Test compound assignment operators 92 | TEST_F(UnitTestVector2, AdditionAssignmentOperator) 93 | { 94 | v1 += v2; 95 | EXPECT_FLOAT_EQ(v1.x, 5.0f); 96 | EXPECT_FLOAT_EQ(v1.y, 7.0f); 97 | } 98 | 99 | TEST_F(UnitTestVector2, SubtractionAssignmentOperator) 100 | { 101 | v1 -= v2; 102 | EXPECT_FLOAT_EQ(v1.x, -3.0f); 103 | EXPECT_FLOAT_EQ(v1.y, -3.0f); 104 | } 105 | 106 | TEST_F(UnitTestVector2, MultiplicationAssignmentOperator) 107 | { 108 | v1 *= 2.0f; 109 | EXPECT_FLOAT_EQ(v1.x, 2.0f); 110 | EXPECT_FLOAT_EQ(v1.y, 4.0f); 111 | } 112 | 113 | TEST_F(UnitTestVector2, DivisionAssignmentOperator) 114 | { 115 | v1 /= 2.0f; 116 | EXPECT_FLOAT_EQ(v1.x, 0.5f); 117 | EXPECT_FLOAT_EQ(v1.y, 1.0f); 118 | } 119 | 120 | // New tests for compound assignment with vectors 121 | TEST_F(UnitTestVector2, MultiplicationAssignmentOperator_Vector) 122 | { 123 | v1 *= v2; 124 | EXPECT_FLOAT_EQ(v1.x, 1.0f * 4.0f); 125 | EXPECT_FLOAT_EQ(v1.y, 2.0f * 5.0f); 126 | } 127 | 128 | TEST_F(UnitTestVector2, DivisionAssignmentOperator_Vector) 129 | { 130 | v1 /= v2; 131 | EXPECT_FLOAT_EQ(v1.x, 1.0f / 4.0f); 132 | EXPECT_FLOAT_EQ(v1.y, 2.0f / 5.0f); 133 | } 134 | 135 | // New tests for compound assignment with floats 136 | TEST_F(UnitTestVector2, AdditionAssignmentOperator_Float) 137 | { 138 | v1 += 3.0f; 139 | EXPECT_FLOAT_EQ(v1.x, 4.0f); 140 | EXPECT_FLOAT_EQ(v1.y, 5.0f); 141 | } 142 | 143 | TEST_F(UnitTestVector2, SubtractionAssignmentOperator_Float) 144 | { 145 | v1 -= 1.0f; 146 | EXPECT_FLOAT_EQ(v1.x, 0.0f); 147 | EXPECT_FLOAT_EQ(v1.y, 1.0f); 148 | } 149 | 150 | // Test other member functions 151 | TEST_F(UnitTestVector2, DistTo) 152 | { 153 | const float dist = v1.distance_to(v2); 154 | EXPECT_FLOAT_EQ(dist, std::sqrt(18.0f)); 155 | } 156 | 157 | TEST_F(UnitTestVector2, DistTo_SamePoint) 158 | { 159 | const float dist = v1.distance_to(v1); 160 | EXPECT_FLOAT_EQ(dist, 0.0f); 161 | } 162 | 163 | TEST_F(UnitTestVector2, DistToSqr) 164 | { 165 | constexpr float distSqr = Vector2(1.0f, 2.0f).distance_to_sqr(Vector2(4.0f, 5.0f)); 166 | EXPECT_FLOAT_EQ(distSqr, 18.0f); 167 | } 168 | 169 | TEST_F(UnitTestVector2, DistToSqr_SamePoint) 170 | { 171 | constexpr float distSqr = Vector2(1.0f, 2.0f).distance_to_sqr(Vector2(1.0f, 2.0f)); 172 | EXPECT_FLOAT_EQ(distSqr, 0.0f); 173 | } 174 | 175 | TEST_F(UnitTestVector2, DotProduct) 176 | { 177 | constexpr float dot = Vector2(1.0f, 2.0f).dot(Vector2(4.0f, 5.0f)); 178 | EXPECT_FLOAT_EQ(dot, 14.0f); 179 | } 180 | 181 | TEST_F(UnitTestVector2, DotProduct_PerpendicularVectors) 182 | { 183 | constexpr float dot = Vector2(1.0f, 0.0f).dot(Vector2(0.0f, 1.0f)); 184 | EXPECT_FLOAT_EQ(dot, 0.0f); 185 | } 186 | 187 | TEST_F(UnitTestVector2, DotProduct_ParallelVectors) 188 | { 189 | constexpr float dot = Vector2(1.0f, 1.0f).dot(Vector2(2.0f, 2.0f)); 190 | EXPECT_FLOAT_EQ(dot, 4.0f); 191 | } 192 | 193 | TEST_F(UnitTestVector2, Length) 194 | { 195 | const float length = v1.length(); 196 | EXPECT_FLOAT_EQ(length, std::sqrt(5.0f)); 197 | } 198 | 199 | TEST_F(UnitTestVector2, Length_ZeroVector) 200 | { 201 | constexpr Vector2 v_zero(0.0f, 0.0f); 202 | const float length = v_zero.length(); 203 | EXPECT_FLOAT_EQ(length, 0.0f); 204 | } 205 | 206 | TEST_F(UnitTestVector2, Length_LargeValues) 207 | { 208 | constexpr Vector2 v_large(FLT_MAX, FLT_MAX); 209 | const float length = v_large.length(); 210 | EXPECT_TRUE(std::isinf(length)); 211 | } 212 | 213 | TEST_F(UnitTestVector2, LengthSqr) 214 | { 215 | constexpr float lengthSqr = Vector2(1.0f, 2.0f).length_sqr(); 216 | EXPECT_FLOAT_EQ(lengthSqr, 5.0f); 217 | } 218 | 219 | TEST_F(UnitTestVector2, Abs) 220 | { 221 | Vector2 v3(-1.0f, -2.0f); 222 | v3.abs(); 223 | EXPECT_FLOAT_EQ(v3.x, 1.0f); 224 | EXPECT_FLOAT_EQ(v3.y, 2.0f); 225 | } 226 | 227 | TEST_F(UnitTestVector2, Abs_PositiveValues) 228 | { 229 | Vector2 v3(1.0f, 2.0f); 230 | v3.abs(); 231 | EXPECT_FLOAT_EQ(v3.x, 1.0f); 232 | EXPECT_FLOAT_EQ(v3.y, 2.0f); 233 | } 234 | 235 | TEST_F(UnitTestVector2, Abs_ZeroValues) 236 | { 237 | Vector2 v3(0.0f, 0.0f); 238 | v3.abs(); 239 | EXPECT_FLOAT_EQ(v3.x, 0.0f); 240 | EXPECT_FLOAT_EQ(v3.y, 0.0f); 241 | } 242 | 243 | TEST_F(UnitTestVector2, Sum) 244 | { 245 | constexpr float sum = Vector2(1.0f, 2.0f).sum(); 246 | EXPECT_FLOAT_EQ(sum, 3.0f); 247 | } 248 | 249 | TEST_F(UnitTestVector2, Sum_NegativeValues) 250 | { 251 | constexpr float sum = Vector2(-1.0f, -2.0f).sum(); 252 | EXPECT_FLOAT_EQ(sum, -3.0f); 253 | } 254 | 255 | TEST_F(UnitTestVector2, Normalized) 256 | { 257 | const Vector2 v3 = v1.normalized(); 258 | EXPECT_NEAR(v3.x, 0.44721f, 0.0001f); 259 | EXPECT_NEAR(v3.y, 0.89443f, 0.0001f); 260 | } 261 | 262 | TEST_F(UnitTestVector2, Normalized_ZeroVector) 263 | { 264 | constexpr Vector2 v_zero(0.0f, 0.0f); 265 | const Vector2 v_norm = v_zero.normalized(); 266 | EXPECT_FLOAT_EQ(v_norm.x, 0.0f); 267 | EXPECT_FLOAT_EQ(v_norm.y, 0.0f); 268 | } 269 | 270 | // Test AsTuple method 271 | TEST_F(UnitTestVector2, AsTuple) 272 | { 273 | const auto tuple = v1.as_tuple(); 274 | EXPECT_FLOAT_EQ(std::get<0>(tuple), v1.x); 275 | EXPECT_FLOAT_EQ(std::get<1>(tuple), v1.y); 276 | } 277 | 278 | // Test division by zero 279 | TEST_F(UnitTestVector2, DivisionOperator_DivideByZero) 280 | { 281 | constexpr Vector2 v(1.0f, 2.0f); 282 | constexpr float zero = 0.0f; 283 | const Vector2 result = v / zero; 284 | EXPECT_TRUE(std::isinf(result.x) || std::isnan(result.x)); 285 | EXPECT_TRUE(std::isinf(result.y) || std::isnan(result.y)); 286 | } 287 | 288 | TEST_F(UnitTestVector2, DivisionAssignmentOperator_DivideByZero) 289 | { 290 | Vector2 v(1.0f, 2.0f); 291 | constexpr float zero = 0.0f; 292 | v /= zero; 293 | EXPECT_TRUE(std::isinf(v.x) || std::isnan(v.x)); 294 | EXPECT_TRUE(std::isinf(v.y) || std::isnan(v.y)); 295 | } 296 | 297 | TEST_F(UnitTestVector2, DivisionAssignmentOperator_VectorWithZero) 298 | { 299 | Vector2 v(1.0f, 2.0f); 300 | constexpr Vector2 v_zero(0.0f, 1.0f); 301 | v /= v_zero; 302 | EXPECT_TRUE(std::isinf(v.x) || std::isnan(v.x)); 303 | EXPECT_FLOAT_EQ(v.y, 2.0f / 1.0f); 304 | } 305 | 306 | // Test operations with infinity and NaN 307 | TEST_F(UnitTestVector2, Operator_WithInfinity) 308 | { 309 | constexpr Vector2 v_inf(INFINITY, INFINITY); 310 | const Vector2 result = v1 + v_inf; 311 | EXPECT_TRUE(std::isinf(result.x)); 312 | EXPECT_TRUE(std::isinf(result.y)); 313 | } 314 | 315 | TEST_F(UnitTestVector2, Operator_WithNaN) 316 | { 317 | constexpr Vector2 v_nan(NAN, NAN); 318 | const Vector2 result = v1 + v_nan; 319 | EXPECT_TRUE(std::isnan(result.x)); 320 | EXPECT_TRUE(std::isnan(result.y)); 321 | } 322 | 323 | // Test negative values in arithmetic operations 324 | TEST_F(UnitTestVector2, AdditionOperator_NegativeValues) 325 | { 326 | constexpr Vector2 v_neg(-1.0f, -2.0f); 327 | const Vector2 result = v1 + v_neg; 328 | EXPECT_FLOAT_EQ(result.x, 0.0f); 329 | EXPECT_FLOAT_EQ(result.y, 0.0f); 330 | } 331 | 332 | TEST_F(UnitTestVector2, SubtractionOperator_NegativeValues) 333 | { 334 | constexpr Vector2 v_neg(-1.0f, -2.0f); 335 | const Vector2 result = v1 - v_neg; 336 | EXPECT_FLOAT_EQ(result.x, 2.0f); 337 | EXPECT_FLOAT_EQ(result.y, 4.0f); 338 | } 339 | 340 | // Test negation of zero vector 341 | TEST_F(UnitTestVector2, NegationOperator_ZeroVector) 342 | { 343 | constexpr Vector2 v_zero(0.0f, 0.0f); 344 | constexpr Vector2 result = -v_zero; 345 | EXPECT_FLOAT_EQ(result.x, -0.0f); 346 | EXPECT_FLOAT_EQ(result.y, -0.0f); 347 | } 348 | 349 | // Static assertions (compile-time checks) 350 | static_assert(Vector2(1.0f, 2.0f).length_sqr() == 5.0f, "LengthSqr should be 5"); 351 | static_assert(Vector2(1.0f, 2.0f).dot(Vector2(4.0f, 5.0f)) == 14.0f, "Dot product should be 14"); 352 | static_assert(Vector2(4.0f, 5.0f).distance_to_sqr(Vector2(1.0f, 2.0f)) == 18.0f, "DistToSqr should be 18"); 353 | static_assert(Vector2(-1.0f, -2.0f).abs() == Vector2(1.0f, 2.0f), "Abs should convert negative values to positive"); 354 | -------------------------------------------------------------------------------- /tests/general/unit_test_vector4.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by vlad on 9/24/2024. 3 | // 4 | // 5 | // Vector4Test.cpp 6 | // 7 | 8 | #include 9 | #include // For std::numeric_limits 10 | #include 11 | 12 | using namespace omath; 13 | 14 | class UnitTestVector4 : public ::testing::Test 15 | { 16 | protected: 17 | Vector4 v1; 18 | Vector4 v2; 19 | 20 | void SetUp() override 21 | { 22 | v1 = Vector4(1.0f, 2.0f, 3.0f, 4.0f); 23 | v2 = Vector4(4.0f, 5.0f, 6.0f, 7.0f); 24 | } 25 | }; 26 | 27 | // Test constructor and default values 28 | TEST_F(UnitTestVector4, Constructor_Default) 29 | { 30 | constexpr Vector4 v; 31 | EXPECT_FLOAT_EQ(v.x, 0.0f); 32 | EXPECT_FLOAT_EQ(v.y, 0.0f); 33 | EXPECT_FLOAT_EQ(v.z, 0.0f); 34 | EXPECT_FLOAT_EQ(v.w, 0.0f); 35 | } 36 | 37 | TEST_F(UnitTestVector4, Constructor_Values) 38 | { 39 | constexpr Vector4 v(1.0f, 2.0f, 3.0f, 4.0f); 40 | EXPECT_FLOAT_EQ(v.x, 1.0f); 41 | EXPECT_FLOAT_EQ(v.y, 2.0f); 42 | EXPECT_FLOAT_EQ(v.z, 3.0f); 43 | EXPECT_FLOAT_EQ(v.w, 4.0f); 44 | } 45 | 46 | // Test equality operators 47 | TEST_F(UnitTestVector4, EqualityOperator) 48 | { 49 | constexpr Vector4 v3(1.0f, 2.0f, 3.0f, 4.0f); 50 | EXPECT_TRUE(v1 == v3); 51 | EXPECT_FALSE(v1 == v2); 52 | } 53 | 54 | TEST_F(UnitTestVector4, InequalityOperator) 55 | { 56 | constexpr Vector4 v3(1.0f, 2.0f, 3.0f, 4.0f); 57 | EXPECT_FALSE(v1 != v3); 58 | EXPECT_TRUE(v1 != v2); 59 | } 60 | 61 | // Test arithmetic operators 62 | TEST_F(UnitTestVector4, AdditionOperator) 63 | { 64 | constexpr Vector4 v3 = Vector4(1.0f, 2.0f, 3.0f, 4.0f) + Vector4(4.0f, 5.0f, 6.0f, 7.0f); 65 | EXPECT_FLOAT_EQ(v3.x, 5.0f); 66 | EXPECT_FLOAT_EQ(v3.y, 7.0f); 67 | EXPECT_FLOAT_EQ(v3.z, 9.0f); 68 | EXPECT_FLOAT_EQ(v3.w, 11.0f); 69 | } 70 | 71 | TEST_F(UnitTestVector4, SubtractionOperator) 72 | { 73 | constexpr Vector4 v3 = Vector4(4.0f, 5.0f, 6.0f, 7.0f) - Vector4(1.0f, 2.0f, 3.0f, 4.0f); 74 | EXPECT_FLOAT_EQ(v3.x, 3.0f); 75 | EXPECT_FLOAT_EQ(v3.y, 3.0f); 76 | EXPECT_FLOAT_EQ(v3.z, 3.0f); 77 | EXPECT_FLOAT_EQ(v3.w, 3.0f); 78 | } 79 | 80 | TEST_F(UnitTestVector4, MultiplicationOperator_Scalar) 81 | { 82 | constexpr Vector4 v3 = Vector4(1.0f, 2.0f, 3.0f, 4.0f) * 2.0f; 83 | EXPECT_FLOAT_EQ(v3.x, 2.0f); 84 | EXPECT_FLOAT_EQ(v3.y, 4.0f); 85 | EXPECT_FLOAT_EQ(v3.z, 6.0f); 86 | EXPECT_FLOAT_EQ(v3.w, 8.0f); 87 | } 88 | 89 | TEST_F(UnitTestVector4, MultiplicationOperator_Vector) 90 | { 91 | constexpr Vector4 v3 = Vector4(1.0f, 2.0f, 3.0f, 4.0f) * Vector4(4.0f, 5.0f, 6.0f, 7.0f); 92 | EXPECT_FLOAT_EQ(v3.x, 4.0f); 93 | EXPECT_FLOAT_EQ(v3.y, 10.0f); 94 | EXPECT_FLOAT_EQ(v3.z, 18.0f); 95 | EXPECT_FLOAT_EQ(v3.w, 28.0f); 96 | } 97 | 98 | TEST_F(UnitTestVector4, DivisionOperator_Scalar) 99 | { 100 | constexpr Vector4 v3 = Vector4(4.0f, 5.0f, 6.0f, 7.0f) / 2.0f; 101 | EXPECT_FLOAT_EQ(v3.x, 2.0f); 102 | EXPECT_FLOAT_EQ(v3.y, 2.5f); 103 | EXPECT_FLOAT_EQ(v3.z, 3.0f); 104 | EXPECT_FLOAT_EQ(v3.w, 3.5f); 105 | } 106 | 107 | TEST_F(UnitTestVector4, DivisionOperator_Vector) 108 | { 109 | constexpr Vector4 v3 = Vector4(4.0f, 5.0f, 6.0f, 7.0f) / Vector4(1.0f, 2.0f, 3.0f, 4.0f); 110 | EXPECT_FLOAT_EQ(v3.x, 4.0f); 111 | EXPECT_FLOAT_EQ(v3.y, 2.5f); 112 | EXPECT_FLOAT_EQ(v3.z, 2.0f); 113 | EXPECT_FLOAT_EQ(v3.w, 1.75f); 114 | } 115 | 116 | // Test compound assignment operators 117 | TEST_F(UnitTestVector4, AdditionAssignmentOperator) 118 | { 119 | v1 += v2; 120 | EXPECT_FLOAT_EQ(v1.x, 5.0f); 121 | EXPECT_FLOAT_EQ(v1.y, 7.0f); 122 | EXPECT_FLOAT_EQ(v1.z, 9.0f); 123 | EXPECT_FLOAT_EQ(v1.w, 11.0f); 124 | } 125 | 126 | TEST_F(UnitTestVector4, SubtractionAssignmentOperator) 127 | { 128 | v1 -= v2; 129 | EXPECT_FLOAT_EQ(v1.x, -3.0f); 130 | EXPECT_FLOAT_EQ(v1.y, -3.0f); 131 | EXPECT_FLOAT_EQ(v1.z, -3.0f); 132 | EXPECT_FLOAT_EQ(v1.w, -3.0f); 133 | } 134 | 135 | TEST_F(UnitTestVector4, MultiplicationAssignmentOperator_Scalar) 136 | { 137 | v1 *= 2.0f; 138 | EXPECT_FLOAT_EQ(v1.x, 2.0f); 139 | EXPECT_FLOAT_EQ(v1.y, 4.0f); 140 | EXPECT_FLOAT_EQ(v1.z, 6.0f); 141 | EXPECT_FLOAT_EQ(v1.w, 8.0f); 142 | } 143 | 144 | TEST_F(UnitTestVector4, MultiplicationAssignmentOperator_Vector) 145 | { 146 | v1 *= v2; 147 | EXPECT_FLOAT_EQ(v1.x, 4.0f); 148 | EXPECT_FLOAT_EQ(v1.y, 10.0f); 149 | EXPECT_FLOAT_EQ(v1.z, 18.0f); 150 | EXPECT_FLOAT_EQ(v1.w, 28.0f); 151 | } 152 | 153 | TEST_F(UnitTestVector4, DivisionAssignmentOperator_Scalar) 154 | { 155 | v1 /= 2.0f; 156 | EXPECT_FLOAT_EQ(v1.x, 0.5f); 157 | EXPECT_FLOAT_EQ(v1.y, 1.0f); 158 | EXPECT_FLOAT_EQ(v1.z, 1.5f); 159 | EXPECT_FLOAT_EQ(v1.w, 2.0f); 160 | } 161 | 162 | TEST_F(UnitTestVector4, DivisionAssignmentOperator_Vector) 163 | { 164 | v1 /= v2; 165 | EXPECT_FLOAT_EQ(v1.x, 0.25f); 166 | EXPECT_FLOAT_EQ(v1.y, 0.4f); 167 | EXPECT_FLOAT_EQ(v1.z, 0.5f); 168 | EXPECT_FLOAT_EQ(v1.w, 4.0f / 7.0f); 169 | } 170 | 171 | TEST_F(UnitTestVector4, NegationOperator) 172 | { 173 | constexpr Vector4 v3 = -Vector4(1.0f, 2.0f, 3.0f, 4.0f); 174 | EXPECT_FLOAT_EQ(v3.x, -1.0f); 175 | EXPECT_FLOAT_EQ(v3.y, -2.0f); 176 | EXPECT_FLOAT_EQ(v3.z, -3.0f); 177 | EXPECT_FLOAT_EQ(v3.w, -4.0f); 178 | } 179 | 180 | // Test other member functions 181 | TEST_F(UnitTestVector4, LengthSqr) 182 | { 183 | constexpr float lengthSqr = Vector4(1.0f, 2.0f, 3.0f, 4.0f).length_sqr(); 184 | EXPECT_FLOAT_EQ(lengthSqr, 30.0f); 185 | } 186 | 187 | TEST_F(UnitTestVector4, DotProduct) 188 | { 189 | constexpr float dot = Vector4(1.0f, 2.0f, 3.0f, 4.0f).dot(Vector4(4.0f, 5.0f, 6.0f, 7.0f)); 190 | EXPECT_FLOAT_EQ(dot, 60.0f); 191 | } 192 | 193 | TEST_F(UnitTestVector4, Abs) 194 | { 195 | Vector4 v3 = Vector4(-1.0f, -2.0f, -3.0f, -4.0f); 196 | v3.abs(); 197 | EXPECT_FLOAT_EQ(v3.x, 1.0f); 198 | EXPECT_FLOAT_EQ(v3.y, 2.0f); 199 | EXPECT_FLOAT_EQ(v3.z, 3.0f); 200 | EXPECT_FLOAT_EQ(v3.w, 4.0f); 201 | } 202 | 203 | TEST_F(UnitTestVector4, Sum) 204 | { 205 | constexpr float sum = Vector4(1.0f, 2.0f, 3.0f, 4.0f).sum(); 206 | EXPECT_FLOAT_EQ(sum, 10.0f); 207 | } 208 | 209 | TEST_F(UnitTestVector4, Clamp) 210 | { 211 | Vector4 v3 = Vector4(1.0f, 2.0f, 3.0f, 4.0f); 212 | v3.clamp(1.5f, 2.5f); 213 | EXPECT_FLOAT_EQ(v3.x, 1.5f); 214 | EXPECT_FLOAT_EQ(v3.y, 2.0f); 215 | EXPECT_FLOAT_EQ(v3.z, 2.5f); 216 | EXPECT_FLOAT_EQ(v3.w, 4.0f); // w is not clamped in this method 217 | } 218 | -------------------------------------------------------------------------------- /tests/general/unit_test_view_angles.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Orange on 11/30/2024. 3 | // 4 | #include -------------------------------------------------------------------------------- /writerside/c.list: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /writerside/cfg/buildprofiles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | true 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /writerside/images/completion_procedure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orange-cpp/omath/9c2be6306c204c79e430498f4d3398bb20579c8c/writerside/images/completion_procedure.png -------------------------------------------------------------------------------- /writerside/images/completion_procedure_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orange-cpp/omath/9c2be6306c204c79e430498f4d3398bb20579c8c/writerside/images/completion_procedure_dark.png -------------------------------------------------------------------------------- /writerside/images/convert_table_to_xml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orange-cpp/omath/9c2be6306c204c79e430498f4d3398bb20579c8c/writerside/images/convert_table_to_xml.png -------------------------------------------------------------------------------- /writerside/images/convert_table_to_xml_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orange-cpp/omath/9c2be6306c204c79e430498f4d3398bb20579c8c/writerside/images/convert_table_to_xml_dark.png -------------------------------------------------------------------------------- /writerside/images/new_topic_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orange-cpp/omath/9c2be6306c204c79e430498f4d3398bb20579c8c/writerside/images/new_topic_options.png -------------------------------------------------------------------------------- /writerside/images/new_topic_options_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orange-cpp/omath/9c2be6306c204c79e430498f4d3398bb20579c8c/writerside/images/new_topic_options_dark.png -------------------------------------------------------------------------------- /writerside/o.tree: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /writerside/redirection-rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | " from OMATH]]> 11 | Empty-MD-Topic.html 12 | 13 | -------------------------------------------------------------------------------- /writerside/topics/Code-Of-Conduct.md: -------------------------------------------------------------------------------- 1 | # Code Of Conduct 2 | 3 | ## 🎯 Goal 4 | 5 | My goal is to provide a space where it is safe for everyone to contribute to, 6 | and get support for, open-source software in a respectful and cooperative 7 | manner. 8 | 9 | I value all contributions and want to make this project and its 10 | surrounding community a place for everyone. 11 | 12 | As members, contributors, and everyone else who may participate in the 13 | development, I strive to keep the entire experience civil. 14 | 15 | ## 📜 Standards 16 | 17 | Our community standards exist in order to make sure everyone feels comfortable 18 | contributing to the project(s) together. 19 | 20 | Our standards are: 21 | - Do not harass, attack, or in any other way discriminate against anyone, including 22 | for their protected traits, including, but not limited to, sex, religion, race, 23 | appearance, gender, identity, nationality, sexuality, etc. 24 | - Do not go off-topic, do not post spam. 25 | - Treat everyone with respect. 26 | 27 | Examples of breaking each rule respectively include: 28 | - Harassment, bullying or inappropriate jokes about another person. 29 | - Posting distasteful imagery, trolling, or posting things unrelated to the topic at hand. 30 | - Treating someone as worse because of their lack of understanding of an issue. 31 | 32 | ## ⚡ Enforcement 33 | 34 | Enforcement of this CoC is done by Orange++ and/or other core contributors. 35 | 36 | I, as the core developer, will strive my best to keep this community civil and 37 | following the standards outlined above. 38 | 39 | ### 🚩 Reporting incidents 40 | 41 | If you believe an incident of breaking these standards has occurred, but nobody has 42 | taken appropriate action, you can privately contact the people responsible for dealing 43 | with such incidents in multiple ways: 44 | 45 | ***E-Mail*** 46 | - `orange-cpp@yandex.ru` 47 | 48 | ***Discord*** 49 | - `@orange_cpp` 50 | 51 | ***Telegram*** 52 | - `@orange_cpp` 53 | 54 | I guarantee your privacy and will not share those reports with anyone. 55 | 56 | ## ⚖️ Enforcement Strategy 57 | 58 | Depending on the severity of the infraction, any action from the list below may be applied. 59 | Please keep in mind cases are reviewed on a per-case basis and members are the ultimate 60 | deciding factor in the type of punishment. 61 | 62 | If the matter benefited from an outside opinion, a member might reach for more opinions 63 | from people unrelated, however, the final decision regarding the action 64 | to be taken is still up to the member. 65 | 66 | For example, if the matter at hand regards a representative of a marginalized group or minority, 67 | the member might ask for a first-hand opinion from another representative of such group. 68 | 69 | ### ✏️ Correction/Edit 70 | 71 | If your message is found to be misleading or poorly worded, a member might 72 | edit your message. 73 | 74 | ### ⚠️ Warning/Deletion 75 | 76 | If your message is found inappropriate, a member might give you a public or private warning, 77 | and/or delete your message. 78 | 79 | ### 🔇 Mute 80 | 81 | If your message is disruptive, or you have been repeatedly violating the standards, 82 | a member might mute (or temporarily ban) you. 83 | 84 | ### ⛔ Ban 85 | 86 | If your message is hateful, very disruptive, or other, less serious infractions are repeated 87 | ignoring previous punishments, a member might ban you permanently. 88 | 89 | ## 🔎 Scope 90 | 91 | This CoC shall apply to all projects ran under the Orange++ lead and all _official_ communities 92 | outside of GitHub. 93 | 94 | However, it is worth noting that official communities outside of GitHub might have their own, 95 | additional sets of rules. -------------------------------------------------------------------------------- /writerside/topics/Community.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | Thanks to everyone who made this possible, including: 4 | 5 | - Saikari aka luadebug for VCPKG port. 6 | 7 | And a big hand to everyone else who has contributed over the past! 8 | 9 | THANKS! <3 10 | 11 | -- Orange++ -------------------------------------------------------------------------------- /writerside/topics/Documentation.md: -------------------------------------------------------------------------------- 1 | # 📥Installation Guide 2 | 3 | ## Using vcpkg 4 | **Note**: Support vcpkg for package management 5 | 1. Install [vcpkg](https://github.com/microsoft/vcpkg) 6 | 2. Run the following command to install the orange-math package: 7 | ``` 8 | vcpkg install orange-math 9 | ``` 10 | CMakeLists.txt 11 | ```cmake 12 | find_package(omath CONFIG REQUIRED) 13 | target_link_libraries(main PRIVATE omath::omath) 14 | ``` 15 | 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). 16 | 17 | ## Build from source using CMake 18 | 1. **Preparation** 19 | 20 | Install needed tools: cmake, clang, git, msvc (windows only). 21 | 22 | 1. **Linux:** 23 | ```bash 24 | sudo pacman -Sy cmake ninja clang git 25 | ``` 26 | 2. **MacOS:** 27 | ```bash 28 | brew install llvm git cmake ninja 29 | ``` 30 | 3. **Windows:** 31 | 32 | Install Visual Studio from [here](https://visualstudio.microsoft.com/downloads/) and Git from [here](https://git-scm.com/downloads). 33 | 34 | Use x64 Native Tools shell to execute needed commands down below. 35 | 2. **Clone the repository:** 36 | ```bash 37 | git clone https://github.com/orange-cpp/omath.git 38 | ``` 39 | 3. **Navigate to the project directory:** 40 | ```bash 41 | cd omath 42 | ``` 43 | 4. **Build the project using CMake:** 44 | ```bash 45 | cmake --preset windows-release -S . 46 | cmake --build cmake-build/build/windows-release --target omath -j 6 47 | ``` 48 | Use **\-\** preset to build siutable version for yourself. Like **windows-release** or **linux-release**. 49 | 50 | | Platform Name | Build Config | 51 | |---------------|---------------| 52 | | windows | release/debug | 53 | | linux | release/debug | 54 | | darwin | release/debug | -------------------------------------------------------------------------------- /writerside/topics/License.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Copyright (c) 2025 Orange++ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /writerside/topics/starter-topic.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | ![banner](https://i.imgur.com/SM9ccP6.png) 4 | 5 | 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. 6 | 7 | ## 👁‍🗨 Features 8 | - **Efficiency**: Optimized for performance, ensuring quick computations using AVX2. 9 | - **Versatility**: Includes a wide array of mathematical functions and algorithms. 10 | - **Ease of Use**: Simplified interface for convenient integration into various projects. 11 | - **Projectile Prediction**: Projectile prediction engine with O(N) algo complexity, that can power you projectile aim-bot. 12 | - **3D Projection**: No need to find view-projection matrix anymore you can make your own projection pipeline. 13 | - **Collision Detection**: Production ready code to handle collision detection by using simple interfaces. 14 | - **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution 15 | - **Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types! 16 | 17 | ## Supported Render Pipelines 18 | | ENGINE | SUPPORT | 19 | |----------|---------| 20 | | Source | ✅YES | 21 | | Unity | ✅YES | 22 | | IWEngine | ✅YES | 23 | | Unreal | ❌NO | 24 | 25 | ## Supported Operating Systems 26 | 27 | | OS | SUPPORT | 28 | |----------------|---------| 29 | | Windows 10/11 | ✅YES | 30 | | Linux | ✅YES | 31 | | Darwin (MacOS) | ✅YES | 32 | 33 | ## ⏬ Installation 34 | Please read our [installation guide](https://github.com/orange-cpp/omath/blob/main/INSTALL.md). If this link doesn't work check out INSTALL.md file. 35 | 36 | ## ❔ Usage 37 | Simple world to screen function 38 | ```c++ 39 | TEST(UnitTestProjection, IsPointOnScreen) 40 | { 41 | const omath::projection::Camera camera({0.f, 0.f, 0.f}, {0, 0.f, 0.f} , {1920.f, 1080.f}, 110.f, 0.1f, 500.f); 42 | 43 | const auto proj = camera.WorldToScreen({100, 0, 15}); 44 | EXPECT_TRUE(proj.has_value()); 45 | } 46 | ``` 47 | ## Showcase 48 | 49 | With `omath/projection` module you can achieve simple ESP hack for powered by Source/Unreal/Unity engine games, like [Apex Legends](https://store.steampowered.com/app/1172470/Apex_Legends/). 50 | 51 | ![banner](https://i.imgur.com/lcJrfcZ.png) 52 | Or for InfinityWard Engine based games. Like Call of Duty Black Ops 2! 53 | ![banner](https://i.imgur.com/F8dmdoo.png) 54 | Or create simple trigger bot with embeded traceline from omath::collision::LineTrace 55 | ![banner](https://i.imgur.com/fxMjRKo.jpeg) 56 | Or even advanced projectile aimbot 57 | [Watch Video](https://youtu.be/lM_NJ1yCunw?si=5E87OrQMeypxSJ3E) 58 | 59 | 60 | ## 🫵🏻 Contributing 61 | Contributions to `omath` are welcome! Please read `CONTRIBUTING.md` for details on our code of conduct and the process for submitting pull requests. 62 | 63 | ## 📜 License 64 | This project is licensed under the MIT - see the `LICENSE` file for details. 65 | 66 | ## 💘 Acknowledgments 67 | - [All contributors](https://github.com/orange-cpp/omath/graphs/contributors) 68 | -------------------------------------------------------------------------------- /writerside/v.list: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /writerside/writerside.cfg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | --------------------------------------------------------------------------------