From 2f7746caeb7754ad59be3ce157aca6ce9ba1a78b Mon Sep 17 00:00:00 2001 From: Saikari Date: Wed, 17 Dec 2025 14:36:03 +0300 Subject: [PATCH] Add MinGW support --- .github/workflows/cmake-multi-platform.yml | 206 ++++++++++++++++++++- CMakePresets.json | 188 +++++++++++++++++++ include/omath/linear_algebra/mat.hpp | 4 +- include/omath/projection/camera.hpp | 40 +++- tests/CMakeLists.txt | 4 +- 5 files changed, 427 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml index dde5e960..4a98353d 100644 --- a/.github/workflows/cmake-multi-platform.yml +++ b/.github/workflows/cmake-multi-platform.yml @@ -53,7 +53,7 @@ jobs: ############################################################################## - # 2) Windows – MSVC / Ninja + # 2) Windows x64 – MSVC (x64-windows) ############################################################################## windows-build-and-test: name: Windows (MSVC) (x64-windows) @@ -85,6 +85,43 @@ jobs: shell: bash run: ./out/Release/unit_tests.exe + + + ############################################################################## + # 3) Windows x86 – MSVC (x86-windows) + ############################################################################## + windows-x86-build-and-test: + name: Windows (MSVC) (x86-windows) + runs-on: windows-latest + env: + OMATH_BUILD_VIA_VCPKG: ON + + steps: + - name: Checkout repository (with sub-modules) + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Ninja + uses: seanmiddleditch/gha-setup-ninja@v4 + + - name: Set up MSVC developer command-prompt + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: amd64_x86 + + - name: Configure (cmake --preset) + shell: bash + run: cmake --preset windows-release-vcpkg-x86 -DOMATH_BUILD_TESTS=ON -DOMATH_BUILD_BENCHMARK=OFF -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests" + + - name: Build + shell: bash + run: cmake --build cmake-build/build/windows-release-vcpkg-x86 --target unit_tests omath + + - name: Run unit_tests.exe + shell: bash + run: ./out/Release/unit_tests.exe + ############################################################################## # 3) macOS – AppleClang / Ninja ############################################################################## @@ -306,4 +343,169 @@ jobs: - name: Build shell: bash run: | - cmake --build cmake-build/build/wasm-release-vcpkg --target unit_tests omath \ No newline at end of file + cmake --build cmake-build/build/wasm-release-vcpkg --target unit_tests omath + + ############################################################################## + # 7) Windows MSYS2 MinGW – GCC / Ninja / x64-mingw-dynamic + ############################################################################## + mingw-build-and-test: + name: MINGW64 (MSYS2) (x64-mingw-dynamic) + runs-on: windows-latest + + defaults: + run: + shell: msys2 {0} + + env: + VCPKG_ROOT: ${{ github.workspace }}/vcpkg + + steps: + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: >- + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-cmake + mingw-w64-x86_64-ninja + mingw-w64-x86_64-pkg-config + git + base-devel + + - name: Checkout repository (with sub-modules) + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up vcpkg + run: | + git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT" + cd "$VCPKG_ROOT" + ./bootstrap-vcpkg.sh + + - name: Configure (cmake --preset) + run: | + cmake --preset mingw-release-vcpkg \ + -DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \ + -DOMATH_BUILD_TESTS=ON \ + -DOMATH_BUILD_BENCHMARK=OFF \ + -DVCPKG_MANIFEST_FEATURES="imgui;tests" + + - name: Build + run: | + cmake --build cmake-build/build/mingw-release-vcpkg --target unit_tests omath + + - name: Run unit_tests.exe + run: | + ./out/Release/unit_tests.exe + + ############################################################################## + # 8) Windows UCRT64 MSYS2 MinGW – GCC / Ninja / x64-mingw-dynamic + ############################################################################## + mingw-ucrt-build-and-test: + name: UCRT64 (MSYS2) (x64-mingw-dynamic) + runs-on: windows-latest + + defaults: + run: + shell: msys2 {0} + + env: + VCPKG_ROOT: ${{ github.workspace }}/vcpkg + + steps: + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + mingw-w64-ucrt-x86_64-toolchain + mingw-w64-ucrt-x86_64-cmake + mingw-w64-ucrt-x86_64-ninja + mingw-w64-ucrt-x86_64-pkg-config + git + base-devel + + - name: Checkout repository (with sub-modules) + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up vcpkg + run: | + git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT" + cd "$VCPKG_ROOT" + ./bootstrap-vcpkg.sh + + - name: Configure (cmake --preset) + run: | + cmake --preset mingw-release-vcpkg \ + -DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \ + -DOMATH_BUILD_TESTS=ON \ + -DOMATH_BUILD_BENCHMARK=OFF \ + -DVCPKG_MANIFEST_FEATURES="imgui;tests" + + - name: Build + run: | + cmake --build cmake-build/build/mingw-release-vcpkg --target unit_tests omath + + - name: Run unit_tests.exe + run: | + ./out/Release/unit_tests.exe + + ############################################################################## + # 9) Windows MSYS2 MinGW32 – GCC / Ninja / x86-mingw-dynamic + ############################################################################## + mingw32-build-and-test: + name: MINGW32 (MSYS2) (x86-mingw-dynamic) + runs-on: windows-latest + + defaults: + run: + shell: msys2 {0} + + env: + VCPKG_ROOT: ${{ github.workspace }}/vcpkg + + steps: + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW32 + update: true + install: >- + mingw-w64-i686-toolchain + mingw-w64-i686-cmake + mingw-w64-i686-ninja + mingw-w64-i686-pkg-config + git + base-devel + + - name: Checkout repository (with sub-modules) + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up vcpkg + run: | + git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT" + cd "$VCPKG_ROOT" + ./bootstrap-vcpkg.sh + + - name: Configure (cmake --preset) + run: | + cmake --preset mingw32-release-vcpkg \ + -DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \ + -DOMATH_BUILD_TESTS=ON \ + -DOMATH_BUILD_BENCHMARK=OFF \ + -DVCPKG_MANIFEST_FEATURES="imgui;tests" + + - name: Build + run: | + cmake --build cmake-build/build/mingw32-release-vcpkg --target unit_tests omath + + - name: Run unit_tests.exe + run: | + ./out/Release/unit_tests.exe diff --git a/CMakePresets.json b/CMakePresets.json index 7f14cbc4..bc743475 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -53,6 +53,40 @@ "OMATH_BUILD_VIA_VCPKG": "ON" } }, + { + "name": "windows-base-vcpkg-x86", + "hidden": true, + "inherits": "windows-base", + "architecture": { + "value": "x86", + "strategy": "external" + }, + "cacheVariables": { + "OMATH_BUILD_VIA_VCPKG": "ON", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed", + "VCPKG_TARGET_TRIPLET": "x86-windows", + "VCPKG_HOST_TRIPLET": "x64-windows", + "VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples" + } + }, + { + "name": "windows-debug-vcpkg-x86", + "displayName": "Windows Debug Vcpkg (x86)", + "inherits": "windows-base-vcpkg-x86", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "windows-release-vcpkg-x86", + "displayName": "Windows Release Vcpkg (x86)", + "inherits": "windows-base-vcpkg-x86", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "OMATH_BUILD_VIA_VCPKG": "ON" + } + }, { "name": "windows-release", "displayName": "Release", @@ -365,6 +399,160 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } + }, + { + "name": "mingw-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/cmake-build/build/${presetName}", + "installDir": "${sourceDir}/cmake-build/install/${presetName}", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_C_COMPILER": "gcc", + "CMAKE_MAKE_PROGRAM": "ninja" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "mingw-ucrt-base-vcpkg", + "hidden": true, + "inherits": "mingw-base", + "environment": { + "VCPKG_DEFAULT_HOST_TRIPLET": "x64-mingw-dynamic" + }, + "cacheVariables": { + "OMATH_BUILD_VIA_VCPKG": "ON", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed", + "VCPKG_TARGET_TRIPLET": "x64-mingw-dynamic", + "VCPKG_HOST_TRIPLET": "x64-mingw-dynamic", + "VCPKG_MANIFEST_FEATURES": "tests;imgui" + } + }, + { + "name": "mingw-ucrt-release-vcpkg", + "displayName": "MinGW UCRT64 Release Vcpkg", + "inherits": "mingw-ucrt-base-vcpkg", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "mingw-base-vcpkg", + "hidden": true, + "inherits": "mingw-base", + "environment": { + "VCPKG_DEFAULT_HOST_TRIPLET": "x64-mingw-dynamic" + }, + "cacheVariables": { + "OMATH_BUILD_VIA_VCPKG": "ON", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed", + "VCPKG_TARGET_TRIPLET": "x64-mingw-dynamic", + "VCPKG_HOST_TRIPLET": "x64-mingw-dynamic", + "VCPKG_MANIFEST_FEATURES": "tests;imgui" + } + }, + { + "name": "mingw-debug", + "displayName": "MinGW Debug", + "inherits": "mingw-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "mingw-debug-vcpkg", + "displayName": "MinGW Debug Vcpkg", + "inherits": "mingw-base-vcpkg", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "mingw-release", + "displayName": "MinGW Release", + "inherits": "mingw-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "mingw-release-vcpkg", + "displayName": "MinGW Release Vcpkg", + "inherits": "mingw-base-vcpkg", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "mingw32-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/cmake-build/build/${presetName}", + "installDir": "${sourceDir}/cmake-build/install/${presetName}", + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_C_COMPILER": "gcc", + "CMAKE_MAKE_PROGRAM": "ninja" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "mingw32-base-vcpkg", + "hidden": true, + "inherits": "mingw32-base", + "environment": { + "VCPKG_DEFAULT_HOST_TRIPLET": "x86-mingw-dynamic" + }, + "cacheVariables": { + "OMATH_BUILD_VIA_VCPKG": "ON", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed", + "VCPKG_TARGET_TRIPLET": "x86-mingw-dynamic", + "VCPKG_HOST_TRIPLET": "x86-mingw-dynamic", + "VCPKG_MANIFEST_FEATURES": "tests;imgui" + } + }, + { + "name": "mingw32-debug", + "displayName": "MinGW32 Debug", + "inherits": "mingw32-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "mingw32-debug-vcpkg", + "displayName": "MinGW32 Debug Vcpkg", + "inherits": "mingw32-base-vcpkg", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "mingw32-release", + "displayName": "MinGW32 Release", + "inherits": "mingw32-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "mingw32-release-vcpkg", + "displayName": "MinGW32 Release Vcpkg", + "inherits": "mingw32-base-vcpkg", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } } ] } \ No newline at end of file diff --git a/include/omath/linear_algebra/mat.hpp b/include/omath/linear_algebra/mat.hpp index e86a57f6..6aeb147b 100644 --- a/include/omath/linear_algebra/mat.hpp +++ b/include/omath/linear_algebra/mat.hpp @@ -380,7 +380,9 @@ namespace omath { const auto det = determinant(); - if (det == 0) + constexpr Type det_epsilon = static_cast(1e-6); + + if (std::abs(det) < det_epsilon) return std::nullopt; const auto transposed_mat = transposed(); diff --git a/include/omath/projection/camera.hpp b/include/omath/projection/camera.hpp index 291f1898..e1e7955a 100644 --- a/include/omath/projection/camera.hpp +++ b/include/omath/projection/camera.hpp @@ -8,6 +8,7 @@ #include "omath/linear_algebra/triangle.hpp" #include "omath/linear_algebra/vector3.hpp" #include "omath/projection/error_codes.hpp" +#include #include #include #include @@ -229,13 +230,17 @@ namespace omath::projection auto projected = get_view_projection_matrix() * mat_column_from_vector(world_position); - if (projected.at(3, 0) == 0.0f) + constexpr float w_epsilon = 1e-6f; + const auto w = projected.at(3, 0); + if (w <= w_epsilon) { return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); + } - projected /= projected.at(3, 0); + projected /= w; - if (is_ndc_out_of_bounds(projected)) + if (is_ndc_out_of_bounds(projected)) { return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); + } return Vector3{projected.at(0, 0), projected.at(1, 0), projected.at(2, 0)}; } @@ -244,19 +249,33 @@ namespace omath::projection { const auto inv_view_proj = get_view_projection_matrix().inverted(); - if (!inv_view_proj) + if (!inv_view_proj) { return std::unexpected(Error::INV_VIEW_PROJ_MAT_DET_EQ_ZERO); + } auto inverted_projection = inv_view_proj.value() * mat_column_from_vector(ndc); - if (!inverted_projection.at(3, 0)) + constexpr float w_epsilon = 1e-6f; + const auto w = inverted_projection.at(3, 0); + if (std::abs(w) < w_epsilon) { return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); + } + + inverted_projection /= w; - inverted_projection /= inverted_projection.at(3, 0); + const Vector3 world_pos{inverted_projection.at(0, 0), inverted_projection.at(1, 0), + inverted_projection.at(2, 0)}; - return Vector3{inverted_projection.at(0, 0), inverted_projection.at(1, 0), - inverted_projection.at(2, 0)}; + // Validate that the computed world position is reasonable + constexpr float max_reasonable_component = 1e6f; + if (!std::isfinite(world_pos.x) || !std::isfinite(world_pos.y) + || !std::isfinite(world_pos.z) || std::abs(world_pos.x) > max_reasonable_component + || std::abs(world_pos.y) > max_reasonable_component || std::abs(world_pos.z) > max_reasonable_component) { + return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); + } + + return world_pos; } template @@ -290,7 +309,8 @@ namespace omath::projection template [[nodiscard]] constexpr static bool is_ndc_out_of_bounds(const Type& ndc) noexcept { - return std::ranges::any_of(ndc.raw_array(), [](const auto& val) { return val < -1 || val > 1; }); + constexpr float eps = 1e-5f; + return std::ranges::any_of(ndc.raw_array(), [](const auto& val) { return val < -1.0f - eps || val > 1.0f + eps; }); } // NDC REPRESENTATION: @@ -347,7 +367,7 @@ namespace omath::projection if constexpr (screen_start == ScreenStart::TOP_LEFT_CORNER) return {screen_pos.x / m_view_port.m_width * 2.f - 1.f, 1.f - screen_pos.y / m_view_port.m_height * 2.f, screen_pos.z}; - else if (screen_start == ScreenStart::BOTTOM_LEFT_CORNER) + else if constexpr (screen_start == ScreenStart::BOTTOM_LEFT_CORNER) return {screen_pos.x / m_view_port.m_width * 2.f - 1.f, (screen_pos.y / m_view_port.m_height - 0.5f) * 2.f, screen_pos.z}; else diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fd42c15e..1502fcfc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,7 +23,7 @@ else() # GTest is being linked as vcpkg package target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest GTest::gtest_main omath::omath) endif() -# Skip test discovery for Android builds - binaries cannot run on host -if (NOT (CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")) +# Skip test discovery for Android/iOS builds or when cross-compiling - binaries cannot run on host +if (NOT (ANDROID OR IOS)) gtest_discover_tests(${PROJECT_NAME}) endif()