mirror of
https://github.com/orange-cpp/omath.git
synced 2026-02-13 15:03:27 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94ee8751af | |||
|
|
82b9b671f6 | ||
| 082b5f69b8 | |||
|
|
735a565446 | ||
| 852bf5c56f | |||
|
|
de5c8bc84d | ||
| 35d9de1550 | |||
|
|
201d8f5547 | ||
| 5a7b9d2338 | |||
| 3d67827704 | |||
| 45a37eb413 | |||
| 90c4ea2036 | |||
| e10cbf9356 | |||
| 4ad44badb9 | |||
| adce4a808a | |||
| 257b06c552 | |||
| a94c78f834 | |||
| b6ac0a1d61 | |||
| f3b74fe433 | |||
| 2ddf29b158 | |||
| bf30957acf | |||
| c9ac61935e | |||
| 60a3a42140 | |||
| 17e21cde4b | |||
| 7fb5ea47dd | |||
| d7189eb7d4 | |||
| ff35571231 | |||
| 3744a6cdec | |||
| 0fd9a5aed8 | |||
| 3dd792c2d5 | |||
| 584969da44 | |||
| acf36c3e04 | |||
| 27c1d147c5 | |||
| 3831bc0999 | |||
| d23bc3204d | |||
| c158f08430 | |||
| e05eba42c3 | |||
| e97be8c142 | |||
| e97d097b2b | |||
| 58aa03c4a9 | |||
| e1399d1814 | |||
| 1964d3d36f | |||
| d7a009eb67 | |||
| 0e03805439 | |||
| eafefb40ec | |||
| 9e4c778e8f | |||
| 0788fd6122 | |||
| 3685f13344 | |||
| d4d8f70fff | |||
| 918858e255 | |||
| 1aff083ef3 | |||
| 6414922884 | |||
| 57ba809076 | |||
| 6fd3a695cf | |||
| f6857cac90 | |||
| b994e47357 | |||
| 82b21d0458 | |||
| daa1abc047 | |||
| 3a66b66c6a | |||
| 6c89c72041 | |||
| e54d5e7388 | |||
| 9a89e2467e | |||
| 48bf06f69c | |||
| 8feddf872a | |||
| 99ebdeb188 | |||
| ba267cbcb8 |
197
.github/workflows/cmake-multi-platform.yml
vendored
197
.github/workflows/cmake-multi-platform.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Omath CI (Arch Linux / Windows)
|
name: Omath CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -12,11 +12,11 @@ concurrency:
|
|||||||
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# 1) ARCH LINUX – Clang / Ninja
|
# 1) Arch Linux – Clang / Ninja
|
||||||
##############################################################################
|
##############################################################################
|
||||||
jobs:
|
jobs:
|
||||||
arch-build-and-test:
|
arch-build-and-test:
|
||||||
name: Arch Linux (Clang)
|
name: Arch Linux (Clang) (x64-linux)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: archlinux:latest
|
container: archlinux:latest
|
||||||
env:
|
env:
|
||||||
@@ -56,7 +56,7 @@ jobs:
|
|||||||
# 2) Windows – MSVC / Ninja
|
# 2) Windows – MSVC / Ninja
|
||||||
##############################################################################
|
##############################################################################
|
||||||
windows-build-and-test:
|
windows-build-and-test:
|
||||||
name: Windows (MSVC)
|
name: Windows (MSVC) (x64-windows)
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
env:
|
env:
|
||||||
OMATH_BUILD_VIA_VCPKG: ON
|
OMATH_BUILD_VIA_VCPKG: ON
|
||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
# 3) macOS – AppleClang / Ninja
|
# 3) macOS – AppleClang / Ninja
|
||||||
##############################################################################
|
##############################################################################
|
||||||
macosx-build-and-test:
|
macosx-build-and-test:
|
||||||
name: macOS (AppleClang)
|
name: macOS (AppleClang) (arm64-osx)
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
env:
|
env:
|
||||||
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||||
@@ -120,3 +120,190 @@ jobs:
|
|||||||
- name: Run unit_tests
|
- name: Run unit_tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: ./out/Release/unit_tests
|
run: ./out/Release/unit_tests
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# 4) iOS – AppleClang / Xcode / arm64-ios
|
||||||
|
##############################################################################
|
||||||
|
ios-build:
|
||||||
|
name: iOS (AppleClang) (arm64-ios)
|
||||||
|
runs-on: macOS-latest
|
||||||
|
env:
|
||||||
|
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||||
|
steps:
|
||||||
|
- name: Install CMake tooling
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
brew install cmake ninja
|
||||||
|
|
||||||
|
- name: Checkout repository (with sub-modules)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Set up vcpkg
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||||
|
cd "$VCPKG_ROOT"
|
||||||
|
./bootstrap-vcpkg.sh
|
||||||
|
|
||||||
|
- name: Configure (cmake --preset)
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cmake --preset ios-release-vcpkg \
|
||||||
|
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
|
||||||
|
-DOMATH_BUILD_TESTS=ON \
|
||||||
|
-DOMATH_BUILD_BENCHMARK=OFF \
|
||||||
|
-DVCPKG_MANIFEST_FEATURES="imgui;tests"
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cmake --build cmake-build/build/ios-release-vcpkg --config Release --target unit_tests omath
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# 5) FreeBSD – Clang / Ninja
|
||||||
|
##############################################################################
|
||||||
|
freebsd-build-and-test:
|
||||||
|
name: FreeBSD (Clang) (x64-freebsd)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository (with sub-modules)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Build and Test
|
||||||
|
uses: vmactions/freebsd-vm@v1
|
||||||
|
with:
|
||||||
|
usesh: true
|
||||||
|
sync: sshfs
|
||||||
|
mem: 12288
|
||||||
|
copyback: false
|
||||||
|
prepare: pkg install -y git curl zip unzip gmake llvm gsed bash perl5 openssl 7-zip coreutils cmake ninja pkgconf patchelf
|
||||||
|
run: |
|
||||||
|
git config --global --add safe.directory `pwd`
|
||||||
|
# Build vcpkg in /tmp to avoid sshfs timestamp sync issues
|
||||||
|
# This prevents PCH timestamp mismatches during parallel builds
|
||||||
|
export VCPKG_ROOT=/tmp/vcpkg
|
||||||
|
rm -rf "$VCPKG_ROOT"
|
||||||
|
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||||
|
cd "$VCPKG_ROOT"
|
||||||
|
# Bootstrap vcpkg - it will build from source on FreeBSD
|
||||||
|
# Building in /tmp avoids sshfs timestamp issues that break PCH
|
||||||
|
./bootstrap-vcpkg.sh
|
||||||
|
cd -
|
||||||
|
export VCPKG_FORCE_SYSTEM_BINARIES=0
|
||||||
|
# VCPKG_ROOT is set to /tmp/vcpkg, which CMake will use via the preset
|
||||||
|
cmake --preset freebsd-release-vcpkg -DOMATH_BUILD_TESTS=ON -DOMATH_BUILD_BENCHMARK=OFF -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests" -DVCPKG_INSTALL_OPTIONS="--allow-unsupported"
|
||||||
|
cmake --build cmake-build/build/freebsd-release-vcpkg --target unit_tests omath
|
||||||
|
./out/Release/unit_tests
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# 5) Android NDK – Clang / Ninja / arm64-android
|
||||||
|
##############################################################################
|
||||||
|
android-build-and-test:
|
||||||
|
name: Android NDK (arm64-android)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||||
|
ANDROID_NDK_HOME: ${{ github.workspace }}/android-ndk
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository (with sub-modules)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Install Android NDK
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
NDK_VERSION="r28c"
|
||||||
|
NDK_ZIP="android-ndk-${NDK_VERSION}-linux.zip"
|
||||||
|
wget -q "https://dl.google.com/android/repository/${NDK_ZIP}"
|
||||||
|
unzip -q "${NDK_ZIP}" -d "${{ github.workspace }}"
|
||||||
|
mv "${{ github.workspace }}/android-ndk-${NDK_VERSION}" "$ANDROID_NDK_HOME"
|
||||||
|
rm "${NDK_ZIP}"
|
||||||
|
echo "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" >> $GITHUB_ENV
|
||||||
|
echo "Android NDK installed at: ${ANDROID_NDK_HOME}"
|
||||||
|
ls -la "${ANDROID_NDK_HOME}" | head -5
|
||||||
|
|
||||||
|
- name: Install basic tool-chain
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y ninja-build cmake
|
||||||
|
|
||||||
|
- name: Set up vcpkg
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||||
|
cd "$VCPKG_ROOT"
|
||||||
|
./bootstrap-vcpkg.sh
|
||||||
|
|
||||||
|
- name: Configure (cmake --preset)
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cmake --preset android-release-vcpkg \
|
||||||
|
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
|
||||||
|
-DOMATH_BUILD_TESTS=ON \
|
||||||
|
-DOMATH_BUILD_BENCHMARK=OFF \
|
||||||
|
-DVCPKG_MANIFEST_FEATURES="imgui;tests"
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cmake --build cmake-build/build/android-release-vcpkg --target unit_tests omath
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# 6) WebAssembly (Emscripten) – Clang / Ninja / wasm32-emscripten
|
||||||
|
##############################################################################
|
||||||
|
wasm-build-and-test:
|
||||||
|
name: WebAssembly (Emscripten) (wasm32-emscripten)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository (with sub-modules)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Install basic tool-chain
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y ninja-build
|
||||||
|
|
||||||
|
- name: Setup Emscripten
|
||||||
|
uses: mymindstorm/setup-emsdk@v14
|
||||||
|
with:
|
||||||
|
version: 'latest'
|
||||||
|
|
||||||
|
- name: Verify Emscripten
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "EMSDK=$EMSDK"
|
||||||
|
emcc --version
|
||||||
|
# Verify toolchain file exists
|
||||||
|
ls -la "$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
|
||||||
|
|
||||||
|
- name: Set up vcpkg
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||||
|
cd "$VCPKG_ROOT"
|
||||||
|
./bootstrap-vcpkg.sh
|
||||||
|
|
||||||
|
- name: Configure (cmake --preset)
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cmake --preset wasm-release-vcpkg \
|
||||||
|
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
|
||||||
|
-DOMATH_BUILD_TESTS=ON \
|
||||||
|
-DOMATH_BUILD_BENCHMARK=OFF \
|
||||||
|
-DVCPKG_MANIFEST_FEATURES="imgui;tests"
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cmake --build cmake-build/build/wasm-release-vcpkg --target unit_tests omath
|
||||||
4
.idea/editor.xml
generated
4
.idea/editor.xml
generated
@@ -201,7 +201,7 @@
|
|||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
|
||||||
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ if (VCPKG_MANIFEST_FEATURES)
|
|||||||
set(OMATH_BUILD_TESTS ON)
|
set(OMATH_BUILD_TESTS ON)
|
||||||
elseif (omath_feature STREQUAL "benchmark")
|
elseif (omath_feature STREQUAL "benchmark")
|
||||||
set(OMATH_BUILD_BENCHMARK ON)
|
set(OMATH_BUILD_BENCHMARK ON)
|
||||||
|
elseif (omath_feature STREQUAL "examples")
|
||||||
|
set(OMATH_BUILD_EXAMPLES ON)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
endforeach ()
|
endforeach ()
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
"OMATH_BUILD_VIA_VCPKG": "ON",
|
"OMATH_BUILD_VIA_VCPKG": "ON",
|
||||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||||
"VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed",
|
"VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed",
|
||||||
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2"
|
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "windows-debug-vcpkg",
|
"name": "windows-debug-vcpkg",
|
||||||
"displayName": "Debug",
|
"displayName": "Windows Debug Vcpkg",
|
||||||
"inherits": "windows-base-vcpkg",
|
"inherits": "windows-base-vcpkg",
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_BUILD_TYPE": "Debug"
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "windows-release-vcpkg",
|
"name": "windows-release-vcpkg",
|
||||||
"displayName": "Release",
|
"displayName": "Windows Release Vcpkg",
|
||||||
"inherits": "windows-base-vcpkg",
|
"inherits": "windows-base-vcpkg",
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_BUILD_TYPE": "Release",
|
"CMAKE_BUILD_TYPE": "Release",
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
"OMATH_BUILD_VIA_VCPKG": "ON",
|
"OMATH_BUILD_VIA_VCPKG": "ON",
|
||||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||||
"VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed",
|
"VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed",
|
||||||
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2"
|
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "darwin-debug-vcpkg",
|
"name": "darwin-debug-vcpkg",
|
||||||
"displayName": "Darwin Debug",
|
"displayName": "Darwin Debug Vcpkg",
|
||||||
"inherits": "darwin-base-vcpkg",
|
"inherits": "darwin-base-vcpkg",
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_BUILD_TYPE": "Debug"
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
@@ -173,11 +173,198 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "darwin-release-vcpkg",
|
"name": "darwin-release-vcpkg",
|
||||||
"displayName": "Darwin Release",
|
"displayName": "Darwin Release Vcpkg",
|
||||||
"inherits": "darwin-base-vcpkg",
|
"inherits": "darwin-base-vcpkg",
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_BUILD_TYPE": "Release"
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ios-base",
|
||||||
|
"hidden": true,
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/cmake-build/build/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_SYSTEM_NAME": "iOS",
|
||||||
|
"CMAKE_OSX_DEPLOYMENT_TARGET": "18.5",
|
||||||
|
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED": "NO",
|
||||||
|
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED": "NO"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Darwin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ios-base-vcpkg",
|
||||||
|
"hidden": true,
|
||||||
|
"inherits": "ios-base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||||
|
"VCPKG_TARGET_TRIPLET": "arm64-ios",
|
||||||
|
"VCPKG_HOST_TRIPLET": "arm64-osx"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ios-release-vcpkg",
|
||||||
|
"displayName": "iOS Release (vcpkg)",
|
||||||
|
"inherits": "ios-base-vcpkg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "freebsd-base",
|
||||||
|
"hidden": true,
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/cmake-build/build/${presetName}",
|
||||||
|
"installDir": "${sourceDir}/cmake-build/install/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_CXX_COMPILER": "clang++",
|
||||||
|
"CMAKE_MAKE_PROGRAM": "ninja"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "FreeBSD"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "freebsd-base-vcpkg",
|
||||||
|
"hidden": true,
|
||||||
|
"inherits": "freebsd-base",
|
||||||
|
"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_MANIFEST_FEATURES": "tests;imgui;avx2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "freebsd-debug",
|
||||||
|
"displayName": "FreeBSD Debug",
|
||||||
|
"inherits": "freebsd-base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "freebsd-debug-vcpkg",
|
||||||
|
"displayName": "FreeBSD Debug Vcpkg",
|
||||||
|
"inherits": "freebsd-base-vcpkg",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "freebsd-release",
|
||||||
|
"displayName": "FreeBSD Release",
|
||||||
|
"inherits": "freebsd-base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "freebsd-release-vcpkg",
|
||||||
|
"displayName": "FreeBSD Release Vcpkg",
|
||||||
|
"inherits": "freebsd-base-vcpkg",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "android-base",
|
||||||
|
"hidden": true,
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/cmake-build/build/${presetName}",
|
||||||
|
"installDir": "${sourceDir}/cmake-build/install/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_SYSTEM_NAME": "Android",
|
||||||
|
"CMAKE_SYSTEM_VERSION": "24",
|
||||||
|
"CMAKE_ANDROID_ARCH_ABI": "arm64-v8a",
|
||||||
|
"CMAKE_ANDROID_NDK": "$env{ANDROID_NDK_HOME}",
|
||||||
|
"CMAKE_MAKE_PROGRAM": "ninja"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "android-base-vcpkg",
|
||||||
|
"hidden": true,
|
||||||
|
"inherits": "android-base",
|
||||||
|
"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": "arm64-android",
|
||||||
|
"VCPKG_MANIFEST_FEATURES": "tests;imgui"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "android-debug",
|
||||||
|
"displayName": "Android Debug",
|
||||||
|
"inherits": "android-base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "android-debug-vcpkg",
|
||||||
|
"displayName": "Android Debug Vcpkg",
|
||||||
|
"inherits": "android-base-vcpkg",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "android-release",
|
||||||
|
"displayName": "Android Release",
|
||||||
|
"inherits": "android-base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "android-release-vcpkg",
|
||||||
|
"displayName": "Android Release Vcpkg",
|
||||||
|
"inherits": "android-base-vcpkg",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wasm-base",
|
||||||
|
"hidden": true,
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/cmake-build/build/${presetName}",
|
||||||
|
"installDir": "${sourceDir}/cmake-build/install/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_MAKE_PROGRAM": "ninja"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wasm-base-vcpkg",
|
||||||
|
"hidden": true,
|
||||||
|
"inherits": "wasm-base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"OMATH_BUILD_VIA_VCPKG": "ON",
|
||||||
|
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||||
|
"VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "$env{EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
|
||||||
|
"VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed",
|
||||||
|
"VCPKG_TARGET_TRIPLET": "wasm32-emscripten"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wasm-debug-vcpkg",
|
||||||
|
"displayName": "WASM Debug Vcpkg",
|
||||||
|
"inherits": "wasm-base-vcpkg",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "wasm-release-vcpkg",
|
||||||
|
"displayName": "WASM Release Vcpkg",
|
||||||
|
"inherits": "wasm-base-vcpkg",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -106,6 +106,10 @@ if (auto screen = camera.world_to_screen(world_position)) {
|
|||||||
|
|
||||||
![TF2 Preview]
|
![TF2 Preview]
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
![OpenGL Preview]
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@@ -135,6 +139,7 @@ if (auto screen = camera.world_to_screen(world_position)) {
|
|||||||
[BO2 Preview]: docs/images/showcase/cod_bo2.png
|
[BO2 Preview]: docs/images/showcase/cod_bo2.png
|
||||||
[CS2 Preview]: docs/images/showcase/cs2.jpeg
|
[CS2 Preview]: docs/images/showcase/cs2.jpeg
|
||||||
[TF2 Preview]: docs/images/showcase/tf2.jpg
|
[TF2 Preview]: docs/images/showcase/tf2.jpg
|
||||||
|
[OpenGL Preview]: docs/images/showcase/opengl.png
|
||||||
<!----------------------------------{ Buttons }--------------------------------->
|
<!----------------------------------{ Buttons }--------------------------------->
|
||||||
[QUICKSTART]: docs/getting_started.md
|
[QUICKSTART]: docs/getting_started.md
|
||||||
[INSTALL]: INSTALL.md
|
[INSTALL]: INSTALL.md
|
||||||
|
|||||||
BIN
docs/images/showcase/opengl.png
Normal file
BIN
docs/images/showcase/opengl.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 224 KiB |
@@ -1,9 +1,32 @@
|
|||||||
project(examples)
|
project(examples)
|
||||||
|
|
||||||
add_executable(example_projection_matrix_builder example_proj_mat_builder.cpp)
|
add_executable(example_projection_matrix_builder example_proj_mat_builder.cpp)
|
||||||
set_target_properties(example_projection_matrix_builder PROPERTIES CXX_STANDARD 26)
|
set_target_properties(example_projection_matrix_builder PROPERTIES
|
||||||
|
CXX_STANDARD 26
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
|
||||||
|
)
|
||||||
target_link_libraries(example_projection_matrix_builder PRIVATE omath::omath)
|
target_link_libraries(example_projection_matrix_builder PRIVATE omath::omath)
|
||||||
|
|
||||||
add_executable(example_signature_scan example_signature_scan.cpp)
|
add_executable(example_signature_scan example_signature_scan.cpp)
|
||||||
set_target_properties(example_signature_scan PROPERTIES CXX_STANDARD 26)
|
set_target_properties(example_signature_scan PROPERTIES
|
||||||
target_link_libraries(example_signature_scan PRIVATE omath::omath)
|
CXX_STANDARD 26
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
|
||||||
|
)
|
||||||
|
target_link_libraries(example_signature_scan PRIVATE omath::omath)
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(example_glfw3 example_glfw3.cpp)
|
||||||
|
set_target_properties(example_glfw3 PROPERTIES CXX_STANDARD 26
|
||||||
|
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
|
||||||
|
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
|
||||||
|
)
|
||||||
|
|
||||||
|
find_package(GLEW REQUIRED)
|
||||||
|
find_package(glfw3 CONFIG REQUIRED)
|
||||||
|
target_link_libraries(example_glfw3 PRIVATE omath::omath GLEW::GLEW glfw)
|
||||||
339
examples/example_glfw3.cpp
Normal file
339
examples/example_glfw3.cpp
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
// main.cpp
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// --- OpenGL / windowing ---
|
||||||
|
#include <GL/glew.h> // GLEW must come before GLFW
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
// --- your math / engine stuff ---
|
||||||
|
#include "omath/3d_primitives/mesh.hpp"
|
||||||
|
#include "omath/engines/opengl_engine/camera.hpp"
|
||||||
|
#include "omath/engines/opengl_engine/constants.hpp"
|
||||||
|
#include "omath/engines/opengl_engine/mesh.hpp"
|
||||||
|
#include "omath/linear_algebra/vector3.hpp"
|
||||||
|
|
||||||
|
using omath::Vector3;
|
||||||
|
|
||||||
|
// ---------------- TYPE ALIASES (ADAPT TO YOUR LIB) ----------------
|
||||||
|
|
||||||
|
// Your 4x4 matrix type
|
||||||
|
using Mat4x4 = omath::opengl_engine::Mat4X4;
|
||||||
|
|
||||||
|
// Rotation angles for the Mesh
|
||||||
|
using RotationAngles = omath::opengl_engine::ViewAngles;
|
||||||
|
|
||||||
|
// For brevity, alias the templates instantiated with your types
|
||||||
|
using VertexType = omath::primitives::Vertex<Vector3<float>>;
|
||||||
|
using CubeMesh = omath::opengl_engine::Mesh;
|
||||||
|
using MyCamera = omath::opengl_engine::Camera;
|
||||||
|
|
||||||
|
// ---------------- SHADERS ----------------
|
||||||
|
|
||||||
|
static const char* vertexShaderSource = R"(
|
||||||
|
#version 330 core
|
||||||
|
layout (location = 0) in vec3 aPos;
|
||||||
|
layout (location = 1) in vec3 aNormal;
|
||||||
|
layout (location = 2) in vec3 aUv;
|
||||||
|
|
||||||
|
uniform mat4 uMVP;
|
||||||
|
uniform mat4 uModel;
|
||||||
|
|
||||||
|
out vec3 vNormal;
|
||||||
|
out vec3 vUv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vNormal = aNormal;
|
||||||
|
vUv = aUv;
|
||||||
|
gl_Position = uMVP * uModel * vec4(aPos, 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static const char* fragmentShaderSource = R"(
|
||||||
|
#version 330 core
|
||||||
|
in vec3 vNormal;
|
||||||
|
in vec3 vUv;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 baseColor = normalize(abs(vNormal));
|
||||||
|
FragColor = vec4(baseColor, 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
GLuint compileShader(GLenum type, const char* src)
|
||||||
|
{
|
||||||
|
GLuint shader = glCreateShader(type);
|
||||||
|
glShaderSource(shader, 1, &src, nullptr);
|
||||||
|
glCompileShader(shader);
|
||||||
|
|
||||||
|
GLint ok = GL_FALSE;
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
char log[1024];
|
||||||
|
glGetShaderInfoLog(shader, sizeof(log), nullptr, log);
|
||||||
|
std::cerr << "Shader compile error: " << log << std::endl;
|
||||||
|
}
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint createShaderProgram()
|
||||||
|
{
|
||||||
|
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSource);
|
||||||
|
GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
|
||||||
|
|
||||||
|
GLuint prog = glCreateProgram();
|
||||||
|
glAttachShader(prog, vs);
|
||||||
|
glAttachShader(prog, fs);
|
||||||
|
glLinkProgram(prog);
|
||||||
|
|
||||||
|
GLint ok = GL_FALSE;
|
||||||
|
glGetProgramiv(prog, GL_LINK_STATUS, &ok);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
char log[1024];
|
||||||
|
glGetProgramInfoLog(prog, sizeof(log), nullptr, log);
|
||||||
|
std::cerr << "Program link error: " << log << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
return prog;
|
||||||
|
}
|
||||||
|
|
||||||
|
void framebuffer_size_callback(GLFWwindow* /*window*/, int w, int h)
|
||||||
|
{
|
||||||
|
glViewport(0, 0, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------- MAIN ----------------
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// ---------- GLFW init ----------
|
||||||
|
if (!glfwInit())
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to init GLFW\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const int SCR_WIDTH = 800;
|
||||||
|
const int SCR_HEIGHT = 600;
|
||||||
|
|
||||||
|
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "omath cube + camera (GLEW)", nullptr, nullptr);
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to create GLFW window\n";
|
||||||
|
glfwTerminate();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||||
|
|
||||||
|
// ---------- GLEW init ----------
|
||||||
|
glewExperimental = GL_TRUE;
|
||||||
|
GLenum glewErr = glewInit();
|
||||||
|
if (glewErr != GLEW_OK)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to initialize GLEW: " << reinterpret_cast<const char*>(glewGetErrorString(glewErr))
|
||||||
|
<< "\n";
|
||||||
|
glfwTerminate();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- GL state ----------
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
// Face culling
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glCullFace(GL_BACK); // cull back faces
|
||||||
|
glFrontFace(GL_CCW); // counter-clockwise is front
|
||||||
|
|
||||||
|
// ---------- Build Cube Mesh (CPU side) ----------
|
||||||
|
std::vector<VertexType> vbo;
|
||||||
|
vbo.reserve(8);
|
||||||
|
|
||||||
|
Vector3<float> p000{-0.5f, -0.5f, -0.5f};
|
||||||
|
Vector3<float> p001{-0.5f, -0.5f, 0.5f};
|
||||||
|
Vector3<float> p010{-0.5f, 0.5f, -0.5f};
|
||||||
|
Vector3<float> p011{-0.5f, 0.5f, 0.5f};
|
||||||
|
Vector3<float> p100{0.5f, -0.5f, -0.5f};
|
||||||
|
Vector3<float> p101{0.5f, -0.5f, 0.5f};
|
||||||
|
Vector3<float> p110{0.5f, 0.5f, -0.5f};
|
||||||
|
Vector3<float> p111{0.5f, 0.5f, 0.5f};
|
||||||
|
|
||||||
|
VertexType v0{p000, Vector3<float>{-1, -1, -1}, omath::Vector2<float>{0, 0}};
|
||||||
|
VertexType v1{p001, Vector3<float>{-1, -1, 1}, omath::Vector2<float>{0, 1}};
|
||||||
|
VertexType v2{p010, Vector3<float>{-1, 1, -1}, omath::Vector2<float>{1, 0}};
|
||||||
|
VertexType v3{p011, Vector3<float>{-1, 1, 1}, omath::Vector2<float>{1, 1}};
|
||||||
|
VertexType v4{p100, Vector3<float>{1, -1, -1}, omath::Vector2<float>{0, 0}};
|
||||||
|
VertexType v5{p101, Vector3<float>{1, -1, 1}, omath::Vector2<float>{0, 1}};
|
||||||
|
VertexType v6{p110, Vector3<float>{1, 1, -1}, omath::Vector2<float>{1, 0}};
|
||||||
|
VertexType v7{p111, Vector3<float>{1, 1, 1}, omath::Vector2<float>{1, 1}};
|
||||||
|
|
||||||
|
vbo.push_back(v0); // 0
|
||||||
|
vbo.push_back(v1); // 1
|
||||||
|
vbo.push_back(v2); // 2
|
||||||
|
vbo.push_back(v3); // 3
|
||||||
|
vbo.push_back(v4); // 4
|
||||||
|
vbo.push_back(v5); // 5
|
||||||
|
vbo.push_back(v6); // 6
|
||||||
|
vbo.push_back(v7); // 7
|
||||||
|
|
||||||
|
using Idx = Vector3<std::uint32_t>;
|
||||||
|
std::vector<Idx> ebo;
|
||||||
|
ebo.reserve(12);
|
||||||
|
|
||||||
|
// front (z+)
|
||||||
|
ebo.emplace_back(1, 5, 7);
|
||||||
|
ebo.emplace_back(1, 7, 3);
|
||||||
|
|
||||||
|
// back (z-)
|
||||||
|
ebo.emplace_back(0, 2, 6);
|
||||||
|
ebo.emplace_back(0, 6, 4);
|
||||||
|
|
||||||
|
// left (x-)
|
||||||
|
ebo.emplace_back(0, 1, 3);
|
||||||
|
ebo.emplace_back(0, 3, 2);
|
||||||
|
|
||||||
|
// right (x+)
|
||||||
|
ebo.emplace_back(4, 6, 7);
|
||||||
|
ebo.emplace_back(4, 7, 5);
|
||||||
|
|
||||||
|
// bottom (y-)
|
||||||
|
ebo.emplace_back(0, 4, 5);
|
||||||
|
ebo.emplace_back(0, 5, 1);
|
||||||
|
|
||||||
|
// top (y+)
|
||||||
|
ebo.emplace_back(2, 3, 7);
|
||||||
|
ebo.emplace_back(2, 7, 6);
|
||||||
|
|
||||||
|
CubeMesh cube{std::move(vbo), std::move(ebo)};
|
||||||
|
cube.set_origin({0.f, 0.f, 0.f});
|
||||||
|
cube.set_scale({2.f, 2.f, 2.f});
|
||||||
|
cube.set_rotation(RotationAngles{});
|
||||||
|
|
||||||
|
// ---------- OpenGL buffers ----------
|
||||||
|
GLuint VAO = 0, VBO = 0, EBO_GL = 0;
|
||||||
|
glGenVertexArrays(1, &VAO);
|
||||||
|
glGenBuffers(1, &VBO);
|
||||||
|
glGenBuffers(1, &EBO_GL);
|
||||||
|
|
||||||
|
glBindVertexArray(VAO);
|
||||||
|
|
||||||
|
// upload vertex buffer
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, cube.m_vertex_buffer.size() * sizeof(VertexType), cube.m_vertex_buffer.data(),
|
||||||
|
GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
// flatten EBO to GL indices
|
||||||
|
std::vector<GLuint> flatIndices;
|
||||||
|
flatIndices.reserve(cube.m_vertex_array_object.size() * 3);
|
||||||
|
for (const auto& tri : cube.m_vertex_array_object)
|
||||||
|
{
|
||||||
|
flatIndices.push_back(tri.x);
|
||||||
|
flatIndices.push_back(tri.y);
|
||||||
|
flatIndices.push_back(tri.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO_GL);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, flatIndices.size() * sizeof(GLuint), flatIndices.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
// vertex layout: position / normal / uv (each Vector3<float>)
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexType), (void*)offsetof(VertexType, position));
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexType), (void*)offsetof(VertexType, normal));
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(VertexType), (void*)offsetof(VertexType, uv));
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
// ---------- Camera setup ----------
|
||||||
|
omath::projection::ViewPort viewPort{static_cast<float>(SCR_WIDTH), static_cast<float>(SCR_HEIGHT)};
|
||||||
|
|
||||||
|
Vector3<float> camPos{0.f, 0.f, 3.f};
|
||||||
|
|
||||||
|
float nearPlane = 0.1f;
|
||||||
|
float farPlane = 100.f;
|
||||||
|
auto fov = omath::projection::FieldOfView::from_degrees(90.f);
|
||||||
|
|
||||||
|
MyCamera camera{camPos, {}, viewPort, fov, nearPlane, farPlane};
|
||||||
|
|
||||||
|
// ---------- Shader ----------
|
||||||
|
GLuint shaderProgram = createShaderProgram();
|
||||||
|
GLint uMvpLoc = glGetUniformLocation(shaderProgram, "uMVP");
|
||||||
|
GLint uModel = glGetUniformLocation(shaderProgram, "uModel");
|
||||||
|
|
||||||
|
static float old_frame_time = glfwGetTime();
|
||||||
|
|
||||||
|
// ---------- Main loop ----------
|
||||||
|
while (!glfwWindowShouldClose(window))
|
||||||
|
{
|
||||||
|
glfwPollEvents();
|
||||||
|
|
||||||
|
float currentTime = glfwGetTime();
|
||||||
|
float deltaTime = currentTime - old_frame_time;
|
||||||
|
old_frame_time = currentTime;
|
||||||
|
|
||||||
|
int fbW = 0, fbH = 0;
|
||||||
|
glfwGetFramebufferSize(window, &fbW, &fbH);
|
||||||
|
glViewport(0, 0, fbW, fbH);
|
||||||
|
|
||||||
|
viewPort.m_width = static_cast<float>(fbW);
|
||||||
|
viewPort.m_height = static_cast<float>(fbH);
|
||||||
|
camera.set_view_port(viewPort);
|
||||||
|
|
||||||
|
glClearColor(0.1f, 0.15f, 0.2f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
RotationAngles rot = cube.get_rotation_angles();
|
||||||
|
rot.yaw += omath::opengl_engine::YawAngle ::from_degrees(40.f * deltaTime);
|
||||||
|
rot.roll += omath::opengl_engine::RollAngle::from_degrees(40.f * deltaTime);
|
||||||
|
|
||||||
|
if (rot.pitch.as_degrees() == 90.f)
|
||||||
|
rot.pitch = omath::opengl_engine::PitchAngle::from_degrees(-90.f);
|
||||||
|
rot.pitch += omath::opengl_engine::PitchAngle::from_degrees(40.f * deltaTime);
|
||||||
|
cube.set_rotation(rot);
|
||||||
|
|
||||||
|
const Mat4x4& viewProj = camera.get_view_projection_matrix();
|
||||||
|
const auto& model = cube.get_to_world_matrix();
|
||||||
|
|
||||||
|
glUseProgram(shaderProgram);
|
||||||
|
|
||||||
|
// Send matrices to GPU
|
||||||
|
const float* mvpPtr = viewProj.raw_array().data();
|
||||||
|
const float* modelPtr = model.raw_array().data();
|
||||||
|
|
||||||
|
glUniformMatrix4fv(uMvpLoc, 1, GL_FALSE, mvpPtr);
|
||||||
|
glUniformMatrix4fv(uModel, 1, GL_FALSE, modelPtr);
|
||||||
|
|
||||||
|
glBindVertexArray(VAO);
|
||||||
|
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(flatIndices.size()), GL_UNSIGNED_INT, nullptr);
|
||||||
|
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- Cleanup ----------
|
||||||
|
glDeleteVertexArrays(1, &VAO);
|
||||||
|
glDeleteBuffers(1, &VBO);
|
||||||
|
glDeleteBuffers(1, &EBO_GL);
|
||||||
|
glDeleteProgram(shaderProgram);
|
||||||
|
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
glfwTerminate();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -6,35 +6,57 @@
|
|||||||
#include <omath/linear_algebra/mat.hpp>
|
#include <omath/linear_algebra/mat.hpp>
|
||||||
#include <omath/linear_algebra/vector3.hpp>
|
#include <omath/linear_algebra/vector3.hpp>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace omath::primitives
|
namespace omath::primitives
|
||||||
{
|
{
|
||||||
template<class Mat4X4, class RotationAngles, class MeshTypeTrait, class Type = float>
|
template<class VecType = Vector3<float>, class UvT = Vector2<float>>
|
||||||
|
struct Vertex final
|
||||||
|
{
|
||||||
|
using VectorType = VecType;
|
||||||
|
using UvType = UvT;
|
||||||
|
VectorType position;
|
||||||
|
VectorType normal;
|
||||||
|
UvType uv;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> concept HasPosition = requires(T vertex) { vertex.position; };
|
||||||
|
template<typename T> concept HasNormal = requires(T vertex) { vertex.normal; };
|
||||||
|
template<typename T> concept HasUv = requires(T vertex) { vertex.uv; };
|
||||||
|
|
||||||
|
template<class Mat4X4, class RotationAngles, class MeshTypeTrait, class VertType = Vertex<>>
|
||||||
class Mesh final
|
class Mesh final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using NumericType = Type;
|
using VectorType = VertType::VectorType;
|
||||||
|
using VertexType = VertType;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Vbo = std::vector<Vector3<NumericType>>;
|
using Vbo = std::vector<VertexType>;
|
||||||
using Vao = std::vector<Vector3<std::size_t>>;
|
using Ebo = std::vector<Vector3<std::uint32_t>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Vbo m_vertex_buffer;
|
Vbo m_vertex_buffer;
|
||||||
Vao m_vertex_array_object;
|
Ebo m_element_buffer_object;
|
||||||
|
|
||||||
Mesh(Vbo vbo, Vao vao, const Vector3<NumericType> scale = {1, 1, 1,})
|
Mesh(Vbo vbo, Ebo vao,
|
||||||
: m_vertex_buffer(std::move(vbo)), m_vertex_array_object(std::move(vao)), m_scale(std::move(scale))
|
const VectorType scale =
|
||||||
|
{
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
})
|
||||||
|
: m_vertex_buffer(std::move(vbo)), m_element_buffer_object(std::move(vao)), m_scale(std::move(scale))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void set_origin(const Vector3<NumericType>& new_origin)
|
void set_origin(const VectorType& new_origin)
|
||||||
{
|
{
|
||||||
m_origin = new_origin;
|
m_origin = new_origin;
|
||||||
m_to_world_matrix = std::nullopt;
|
m_to_world_matrix = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_scale(const Vector3<NumericType>& new_scale)
|
void set_scale(const VectorType& new_scale)
|
||||||
{
|
{
|
||||||
m_scale = new_scale;
|
m_scale = new_scale;
|
||||||
m_to_world_matrix = std::nullopt;
|
m_to_world_matrix = std::nullopt;
|
||||||
@@ -47,13 +69,13 @@ namespace omath::primitives
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
const Vector3<NumericType>& get_origin() const
|
const VectorType& get_origin() const
|
||||||
{
|
{
|
||||||
return m_origin;
|
return m_origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
const Vector3<NumericType>& get_scale() const
|
const VectorType& get_scale() const
|
||||||
{
|
{
|
||||||
return m_scale;
|
return m_scale;
|
||||||
}
|
}
|
||||||
@@ -69,31 +91,34 @@ namespace omath::primitives
|
|||||||
{
|
{
|
||||||
if (m_to_world_matrix)
|
if (m_to_world_matrix)
|
||||||
return m_to_world_matrix.value();
|
return m_to_world_matrix.value();
|
||||||
m_to_world_matrix =
|
m_to_world_matrix = mat_translation<float, Mat4X4::get_store_ordering()>(m_origin)
|
||||||
mat_translation(m_origin) * mat_scale(m_scale) * MeshTypeTrait::rotation_matrix(m_rotation_angles);
|
* MeshTypeTrait::rotation_matrix(m_rotation_angles)
|
||||||
|
* mat_scale<float, Mat4X4::get_store_ordering()>(m_scale);
|
||||||
|
|
||||||
return m_to_world_matrix.value();
|
return m_to_world_matrix.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Vector3<float> vertex_to_world_space(const Vector3<float>& vertex) const
|
VectorType vertex_position_to_world_space(const Vector3<float>& vertex_position) const
|
||||||
|
requires HasPosition<VertexType>
|
||||||
{
|
{
|
||||||
auto abs_vec = get_to_world_matrix() * mat_column_from_vector(vertex);
|
auto abs_vec = get_to_world_matrix() * mat_column_from_vector<typename Mat4X4::ContainedType, Mat4X4::get_store_ordering()>(vertex_position);
|
||||||
|
|
||||||
return {abs_vec.at(0, 0), abs_vec.at(1, 0), abs_vec.at(2, 0)};
|
return {abs_vec.at(0, 0), abs_vec.at(1, 0), abs_vec.at(2, 0)};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Triangle<Vector3<float>> make_face_in_world_space(const Vao::const_iterator vao_iterator) const
|
Triangle<VectorType> make_face_in_world_space(const Ebo::const_iterator vao_iterator) const
|
||||||
|
requires HasPosition<VertexType>
|
||||||
{
|
{
|
||||||
return {vertex_to_world_space(m_vertex_buffer.at(vao_iterator->x)),
|
return {vertex_position_to_world_space(m_vertex_buffer.at(vao_iterator->x).position),
|
||||||
vertex_to_world_space(m_vertex_buffer.at(vao_iterator->y)),
|
vertex_position_to_world_space(m_vertex_buffer.at(vao_iterator->y).position),
|
||||||
vertex_to_world_space(m_vertex_buffer.at(vao_iterator->z))};
|
vertex_position_to_world_space(m_vertex_buffer.at(vao_iterator->z).position)};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector3<NumericType> m_origin;
|
VectorType m_origin;
|
||||||
Vector3<NumericType> m_scale;
|
VectorType m_scale;
|
||||||
|
|
||||||
RotationAngles m_rotation_angles;
|
RotationAngles m_rotation_angles;
|
||||||
|
|
||||||
|
|||||||
23
include/omath/collision/collider_interface.hpp
Normal file
23
include/omath/collision/collider_interface.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// Created by Vladislav on 06.12.2025.
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
#include <omath/linear_algebra/vector3.hpp>
|
||||||
|
|
||||||
|
namespace omath::collision
|
||||||
|
{
|
||||||
|
template<class VecType = Vector3<float>>
|
||||||
|
class ColliderInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using VectorType = VecType;
|
||||||
|
virtual ~ColliderInterface() = default;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual VectorType find_abs_furthest_vertex_position(const VectorType& direction) const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual const VectorType& get_origin() const = 0;
|
||||||
|
virtual void set_origin(const VectorType& new_origin) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "simplex.hpp"
|
#include "simplex.hpp"
|
||||||
#include <algorithm> // find_if
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <memory_resource>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace omath::collision
|
namespace omath::collision
|
||||||
@@ -16,21 +19,21 @@ namespace omath::collision
|
|||||||
{ a.cross(b) } -> std::same_as<V>;
|
{ a.cross(b) } -> std::same_as<V>;
|
||||||
{ a.dot(b) } -> std::same_as<float>;
|
{ a.dot(b) } -> std::same_as<float>;
|
||||||
{ -a } -> std::same_as<V>;
|
{ -a } -> std::same_as<V>;
|
||||||
{ a* s } -> std::same_as<V>;
|
{ a * s } -> std::same_as<V>;
|
||||||
{ a / s } -> std::same_as<V>;
|
{ a / s } -> std::same_as<V>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class ColliderType>
|
template<class ColliderInterfaceType>
|
||||||
class Epa final
|
class Epa final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Vertex = typename ColliderType::VertexType;
|
using VectorType = ColliderInterfaceType::VectorType;
|
||||||
static_assert(EpaVector<Vertex>, "VertexType must satisfy EpaVector concept");
|
static_assert(EpaVector<VectorType>, "VertexType must satisfy EpaVector concept");
|
||||||
|
|
||||||
struct Result final
|
struct Result final
|
||||||
{
|
{
|
||||||
Vertex normal{}; // outward normal (from B to A)
|
VectorType normal{}; // from A to B
|
||||||
Vertex penetration_vector;
|
VectorType penetration_vector;
|
||||||
float depth{0.0f};
|
float depth{0.0f};
|
||||||
int iterations{0};
|
int iterations{0};
|
||||||
int num_vertices{0};
|
int num_vertices{0};
|
||||||
@@ -42,27 +45,19 @@ namespace omath::collision
|
|||||||
int max_iterations{64};
|
int max_iterations{64};
|
||||||
float tolerance{1e-4f}; // absolute tolerance on distance growth
|
float tolerance{1e-4f}; // absolute tolerance on distance growth
|
||||||
};
|
};
|
||||||
|
|
||||||
// Precondition: simplex.size()==4 and contains the origin.
|
// Precondition: simplex.size()==4 and contains the origin.
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<Result> solve(const ColliderType& a, const ColliderType& b, const Simplex<Vertex>& simplex,
|
static std::optional<Result> solve(const ColliderInterfaceType& a, const ColliderInterfaceType& b,
|
||||||
const Params params = {})
|
const Simplex<VectorType>& simplex, const Params params = {},
|
||||||
|
std::pmr::memory_resource& mem_resource = *std::pmr::get_default_resource())
|
||||||
{
|
{
|
||||||
// --- Build initial polytope from simplex (4 points) ---
|
// --- Build initial polytope from simplex (4 points) ---
|
||||||
std::vector<Vertex> vertexes;
|
std::pmr::vector<VectorType> vertexes = build_initial_polytope_from_simplex(simplex, mem_resource);
|
||||||
vertexes.reserve(64);
|
|
||||||
for (std::size_t i = 0; i < simplex.size(); ++i)
|
|
||||||
vertexes.push_back(simplex[i]);
|
|
||||||
|
|
||||||
// Initial tetra faces (windings corrected in make_face)
|
// Initial tetra faces (windings corrected in make_face)
|
||||||
std::vector<Face> faces;
|
std::pmr::vector<Face> faces = create_initial_tetra_faces(mem_resource, vertexes);
|
||||||
faces.reserve(128);
|
|
||||||
faces.emplace_back(make_face(vertexes, 0, 1, 2));
|
|
||||||
faces.emplace_back(make_face(vertexes, 0, 2, 3));
|
|
||||||
faces.emplace_back(make_face(vertexes, 0, 3, 1));
|
|
||||||
faces.emplace_back(make_face(vertexes, 1, 3, 2));
|
|
||||||
|
|
||||||
auto heap = rebuild_heap(faces);
|
auto heap = rebuild_heap(faces, mem_resource);
|
||||||
|
|
||||||
Result out{};
|
Result out{};
|
||||||
|
|
||||||
@@ -75,104 +70,71 @@ namespace omath::collision
|
|||||||
// (We could keep face handles; this is fine for small Ns.)
|
// (We could keep face handles; this is fine for small Ns.)
|
||||||
|
|
||||||
if (const auto top = heap.top(); faces[top.idx].d != top.d)
|
if (const auto top = heap.top(); faces[top.idx].d != top.d)
|
||||||
heap = rebuild_heap(faces);
|
heap = rebuild_heap(faces, mem_resource);
|
||||||
|
|
||||||
if (heap.empty())
|
if (heap.empty())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
const int fidx = heap.top().idx;
|
const int fidx = heap.top().idx;
|
||||||
const Face f = faces[fidx];
|
const Face face = faces[fidx];
|
||||||
|
|
||||||
// Get farthest point in face normal direction
|
// Get the furthest point in face normal direction
|
||||||
const Vertex p = support_point(a, b, f.n);
|
const VectorType p = support_point(a, b, face.n);
|
||||||
const float p_dist = f.n.dot(p);
|
const float p_dist = face.n.dot(p);
|
||||||
|
|
||||||
// Converged if we can’t push the face closer than tolerance
|
// Converged if we can’t push the face closer than tolerance
|
||||||
if (p_dist - f.d <= params.tolerance)
|
if (p_dist - face.d <= params.tolerance)
|
||||||
{
|
{
|
||||||
out.normal = f.n;
|
out.normal = face.n;
|
||||||
out.depth = f.d; // along unit normal
|
out.depth = face.d; // along unit normal
|
||||||
out.iterations = it + 1;
|
out.iterations = it + 1;
|
||||||
out.num_vertices = static_cast<int>(vertexes.size());
|
out.num_vertices = static_cast<int>(vertexes.size());
|
||||||
out.num_faces = static_cast<int>(faces.size());
|
out.num_faces = static_cast<int>(faces.size());
|
||||||
|
|
||||||
const auto centers = b.get_origin() - a.get_origin();
|
out.penetration_vector = out.normal * out.depth;
|
||||||
const auto sign = out.normal.dot(centers) >= 0 ? 1 : -1;
|
|
||||||
|
|
||||||
out.penetration_vector = out.normal * out.depth * sign;
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new vertex
|
// Add new vertex
|
||||||
const int new_idx = static_cast<int>(vertexes.size());
|
const int new_idx = static_cast<int>(vertexes.size());
|
||||||
vertexes.push_back(p);
|
vertexes.emplace_back(p);
|
||||||
|
|
||||||
// Mark faces visible from p and collect their horizon
|
const auto [to_delete, boundary] = mark_visible_and_collect_horizon(faces, p);
|
||||||
std::vector<char> to_delete(faces.size(), 0);
|
|
||||||
std::vector<Edge> boundary;
|
|
||||||
boundary.reserve(faces.size() * 2);
|
|
||||||
|
|
||||||
for (int i = 0; i < static_cast<int>(faces.size()); ++i)
|
erase_marked(faces, to_delete);
|
||||||
{
|
|
||||||
if (to_delete[i])
|
|
||||||
continue;
|
|
||||||
if (visible_from(faces[i], p))
|
|
||||||
{
|
|
||||||
const auto& rf = faces[i];
|
|
||||||
to_delete[i] = 1;
|
|
||||||
add_edge_boundary(boundary, rf.i0, rf.i1);
|
|
||||||
add_edge_boundary(boundary, rf.i1, rf.i2);
|
|
||||||
add_edge_boundary(boundary, rf.i2, rf.i0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove visible faces
|
|
||||||
std::vector<Face> new_faces;
|
|
||||||
new_faces.reserve(faces.size() + boundary.size());
|
|
||||||
for (int i = 0; i < static_cast<int>(faces.size()); ++i)
|
|
||||||
if (!to_delete[i])
|
|
||||||
new_faces.push_back(faces[i]);
|
|
||||||
faces.swap(new_faces);
|
|
||||||
|
|
||||||
// Stitch new faces around the horizon
|
// Stitch new faces around the horizon
|
||||||
for (const auto& e : boundary)
|
for (const auto& e : boundary)
|
||||||
faces.push_back(make_face(vertexes, e.a, e.b, new_idx));
|
faces.emplace_back(make_face(vertexes, e.a, e.b, new_idx));
|
||||||
|
|
||||||
// Rebuild heap after topology change
|
// Rebuild heap after topology change
|
||||||
heap = rebuild_heap(faces);
|
heap = rebuild_heap(faces, mem_resource);
|
||||||
|
|
||||||
if (!std::isfinite(vertexes.back().dot(vertexes.back())))
|
if (!std::isfinite(vertexes.back().dot(vertexes.back())))
|
||||||
break; // safety
|
break; // safety
|
||||||
out.iterations = it + 1;
|
out.iterations = it + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: pick closest face as best-effort answer
|
if (faces.empty())
|
||||||
if (!faces.empty())
|
return std::nullopt;
|
||||||
{
|
|
||||||
auto best = faces[0];
|
|
||||||
for (const auto& f : faces)
|
|
||||||
if (f.d < best.d)
|
|
||||||
best = f;
|
|
||||||
out.normal = best.n;
|
|
||||||
out.depth = best.d;
|
|
||||||
out.num_vertices = static_cast<int>(vertexes.size());
|
|
||||||
out.num_faces = static_cast<int>(faces.size());
|
|
||||||
|
|
||||||
const auto centers = b.get_origin() - a.get_origin();
|
const auto best = *std::ranges::min_element(faces, [](const auto& first, const auto& second)
|
||||||
const auto sign = out.normal.dot(centers) >= 0 ? 1 : -1;
|
{ return first.d < second.d; });
|
||||||
|
out.normal = best.n;
|
||||||
|
out.depth = best.d;
|
||||||
|
out.num_vertices = static_cast<int>(vertexes.size());
|
||||||
|
out.num_faces = static_cast<int>(faces.size());
|
||||||
|
|
||||||
out.penetration_vector = out.normal * out.depth * sign;
|
out.penetration_vector = out.normal * out.depth;
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Face final
|
struct Face final
|
||||||
{
|
{
|
||||||
int i0, i1, i2;
|
int i0, i1, i2;
|
||||||
Vertex n; // unit outward normal
|
VectorType n; // unit outward normal
|
||||||
float d; // n · v0 (>=0 ideally because origin is inside)
|
float d; // n · v0 (>=0 ideally because origin is inside)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -188,47 +150,53 @@ namespace omath::collision
|
|||||||
};
|
};
|
||||||
struct HeapCmp final
|
struct HeapCmp final
|
||||||
{
|
{
|
||||||
bool operator()(const HeapItem& lhs, const HeapItem& rhs) const noexcept
|
[[nodiscard]]
|
||||||
|
static bool operator()(const HeapItem& lhs, const HeapItem& rhs) noexcept
|
||||||
{
|
{
|
||||||
return lhs.d > rhs.d; // min-heap by distance
|
return lhs.d > rhs.d; // min-heap by distance
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
using Heap = std::priority_queue<HeapItem, std::vector<HeapItem>, HeapCmp>;
|
|
||||||
|
using Heap = std::priority_queue<HeapItem, std::pmr::vector<HeapItem>, HeapCmp>;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static Heap rebuild_heap(const std::vector<Face>& faces)
|
static Heap rebuild_heap(const std::pmr::vector<Face>& faces, auto& memory_resource)
|
||||||
{
|
{
|
||||||
Heap h;
|
std::pmr::vector<HeapItem> storage{&memory_resource};
|
||||||
|
storage.reserve(faces.size()); // optional but recommended
|
||||||
|
|
||||||
|
Heap h{HeapCmp{}, std::move(storage)};
|
||||||
|
|
||||||
for (int i = 0; i < static_cast<int>(faces.size()); ++i)
|
for (int i = 0; i < static_cast<int>(faces.size()); ++i)
|
||||||
h.push({faces[i].d, i});
|
h.emplace(faces[i].d, i);
|
||||||
return h;
|
|
||||||
|
return h; // allocator is preserved
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static bool visible_from(const Face& f, const Vertex& p)
|
static bool visible_from(const Face& f, const VectorType& p)
|
||||||
{
|
{
|
||||||
// positive if p is in front of the face
|
// positive if p is in front of the face
|
||||||
return (f.n.dot(p) - f.d) > 1e-7f;
|
return f.n.dot(p) - f.d > 1e-7f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_edge_boundary(std::vector<Edge>& boundary, int a, int b)
|
static void add_edge_boundary(std::pmr::vector<Edge>& boundary, int a, int b)
|
||||||
{
|
{
|
||||||
// Keep edges that appear only once; erase if opposite already present
|
// Keep edges that appear only once; erase if opposite already present
|
||||||
auto itb =
|
auto itb = std::ranges::find_if(boundary, [&](const Edge& e) { return e.a == b && e.b == a; });
|
||||||
std::find_if(boundary.begin(), boundary.end(), [&](const Edge& e) { return e.a == b && e.b == a; });
|
|
||||||
if (itb != boundary.end())
|
if (itb != boundary.end())
|
||||||
boundary.erase(itb); // internal edge cancels out
|
boundary.erase(itb); // internal edge cancels out
|
||||||
else
|
else
|
||||||
boundary.push_back({a, b}); // horizon edge (directed)
|
boundary.emplace_back(a, b); // horizon edge (directed)
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static Face make_face(const std::vector<Vertex>& vertexes, int i0, int i1, int i2)
|
static Face make_face(const std::pmr::vector<VectorType>& vertexes, int i0, int i1, int i2)
|
||||||
{
|
{
|
||||||
const Vertex& a0 = vertexes[i0];
|
const VectorType& a0 = vertexes[i0];
|
||||||
const Vertex& a1 = vertexes[i1];
|
const VectorType& a1 = vertexes[i1];
|
||||||
const Vertex& a2 = vertexes[i2];
|
const VectorType& a2 = vertexes[i2];
|
||||||
Vertex n = (a1 - a0).cross(a2 - a0);
|
VectorType n = (a1 - a0).cross(a2 - a0);
|
||||||
if (n.dot(n) <= 1e-30f)
|
if (n.dot(n) <= 1e-30f)
|
||||||
{
|
{
|
||||||
n = any_perp_vec(a1 - a0); // degenerate guard
|
n = any_perp_vec(a1 - a0); // degenerate guard
|
||||||
@@ -246,9 +214,10 @@ namespace omath::collision
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static Vertex support_point(const ColliderType& a, const ColliderType& b, const Vertex& dir)
|
static VectorType support_point(const ColliderInterfaceType& a, const ColliderInterfaceType& b,
|
||||||
|
const VectorType& dir)
|
||||||
{
|
{
|
||||||
return a.find_abs_furthest_vertex(dir) - b.find_abs_furthest_vertex(-dir);
|
return a.find_abs_furthest_vertex_position(dir) - b.find_abs_furthest_vertex_position(-dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class V>
|
template<class V>
|
||||||
@@ -267,5 +236,67 @@ namespace omath::collision
|
|||||||
return d;
|
return d;
|
||||||
return V{1, 0, 0};
|
return V{1, 0, 0};
|
||||||
}
|
}
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::pmr::vector<Face> create_initial_tetra_faces(std::pmr::memory_resource& mem_resource,
|
||||||
|
const std::pmr::vector<VectorType>& vertexes)
|
||||||
|
{
|
||||||
|
std::pmr::vector<Face> faces{&mem_resource};
|
||||||
|
faces.reserve(4);
|
||||||
|
faces.emplace_back(make_face(vertexes, 0, 1, 2));
|
||||||
|
faces.emplace_back(make_face(vertexes, 0, 2, 3));
|
||||||
|
faces.emplace_back(make_face(vertexes, 0, 3, 1));
|
||||||
|
faces.emplace_back(make_face(vertexes, 1, 3, 2));
|
||||||
|
return faces;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::pmr::vector<VectorType> build_initial_polytope_from_simplex(const Simplex<VectorType>& simplex,
|
||||||
|
std::pmr::memory_resource& mem_resource)
|
||||||
|
{
|
||||||
|
std::pmr::vector<VectorType> vertexes{&mem_resource};
|
||||||
|
vertexes.reserve(simplex.size());
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < simplex.size(); ++i)
|
||||||
|
vertexes.emplace_back(simplex[i]);
|
||||||
|
|
||||||
|
return vertexes;
|
||||||
|
}
|
||||||
|
static void erase_marked(std::pmr::vector<Face>& faces, const std::pmr::vector<bool>& to_delete)
|
||||||
|
{
|
||||||
|
auto* mr = faces.get_allocator().resource(); // keep same resource
|
||||||
|
std::pmr::vector<Face> kept{mr};
|
||||||
|
kept.reserve(faces.size());
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < faces.size(); ++i)
|
||||||
|
if (!to_delete[i])
|
||||||
|
kept.emplace_back(faces[i]);
|
||||||
|
|
||||||
|
faces.swap(kept);
|
||||||
|
}
|
||||||
|
struct Horizon
|
||||||
|
{
|
||||||
|
std::pmr::vector<bool> to_delete;
|
||||||
|
std::pmr::vector<Edge> boundary;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Horizon mark_visible_and_collect_horizon(const std::pmr::vector<Face>& faces, const VectorType& p)
|
||||||
|
{
|
||||||
|
auto* mr = faces.get_allocator().resource();
|
||||||
|
|
||||||
|
Horizon horizon{std::pmr::vector<bool>(faces.size(), false, mr), std::pmr::vector<Edge>(mr)};
|
||||||
|
horizon.boundary.reserve(faces.size());
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < faces.size(); ++i)
|
||||||
|
if (visible_from(faces[i], p))
|
||||||
|
{
|
||||||
|
const auto& rf = faces[i];
|
||||||
|
horizon.to_delete[i] = true;
|
||||||
|
add_edge_boundary(horizon.boundary, rf.i0, rf.i1);
|
||||||
|
add_edge_boundary(horizon.boundary, rf.i1, rf.i2);
|
||||||
|
add_edge_boundary(horizon.boundary, rf.i2, rf.i0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return horizon;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace omath::collision
|
} // namespace omath::collision
|
||||||
|
|||||||
@@ -14,32 +14,33 @@ namespace omath::collision
|
|||||||
Simplex<VertexType> simplex; // valid only if hit == true and size==4
|
Simplex<VertexType> simplex; // valid only if hit == true and size==4
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class ColliderType>
|
template<class ColliderInterfaceType>
|
||||||
class GjkAlgorithm final
|
class GjkAlgorithm final
|
||||||
{
|
{
|
||||||
using VertexType = typename ColliderType::VertexType;
|
using VectorType = ColliderInterfaceType::VectorType;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static VertexType find_support_vertex(const ColliderType& collider_a, const ColliderType& collider_b,
|
static VectorType find_support_vertex(const ColliderInterfaceType& collider_a,
|
||||||
const VertexType& direction)
|
const ColliderInterfaceType& collider_b, const VectorType& direction)
|
||||||
{
|
{
|
||||||
return collider_a.find_abs_furthest_vertex(direction) - collider_b.find_abs_furthest_vertex(-direction);
|
return collider_a.find_abs_furthest_vertex_position(direction)
|
||||||
|
- collider_b.find_abs_furthest_vertex_position(-direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static bool is_collide(const ColliderType& collider_a, const ColliderType& collider_b)
|
static bool is_collide(const ColliderInterfaceType& collider_a, const ColliderInterfaceType& collider_b)
|
||||||
{
|
{
|
||||||
return is_collide_with_simplex_info(collider_a, collider_b).hit;
|
return is_collide_with_simplex_info(collider_a, collider_b).hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static GjkHitInfo<VertexType> is_collide_with_simplex_info(const ColliderType& collider_a,
|
static GjkHitInfo<VectorType> is_collide_with_simplex_info(const ColliderInterfaceType& collider_a,
|
||||||
const ColliderType& collider_b)
|
const ColliderInterfaceType& collider_b)
|
||||||
{
|
{
|
||||||
auto support = find_support_vertex(collider_a, collider_b, {1, 0, 0});
|
auto support = find_support_vertex(collider_a, collider_b, VectorType{1, 0, 0});
|
||||||
|
|
||||||
Simplex<VertexType> simplex;
|
Simplex<VectorType> simplex;
|
||||||
simplex.push_front(support);
|
simplex.push_front(support);
|
||||||
|
|
||||||
auto direction = -support;
|
auto direction = -support;
|
||||||
|
|||||||
@@ -3,40 +3,53 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "collider_interface.hpp"
|
||||||
#include "omath/linear_algebra/vector3.hpp"
|
#include "omath/linear_algebra/vector3.hpp"
|
||||||
|
|
||||||
|
#ifdef OMATH_BUILD_TESTS
|
||||||
|
// ReSharper disable once CppInconsistentNaming
|
||||||
|
class UnitTestColider_FindFurthestVertex_Test;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace omath::collision
|
namespace omath::collision
|
||||||
{
|
{
|
||||||
template<class MeshType>
|
template<class MeshType>
|
||||||
class MeshCollider
|
class MeshCollider final : public ColliderInterface<typename MeshType::VertexType::VectorType>
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_BUILD_TESTS
|
||||||
|
friend UnitTestColider_FindFurthestVertex_Test;
|
||||||
|
#endif
|
||||||
public:
|
public:
|
||||||
using NumericType = typename MeshType::NumericType;
|
using VertexType = MeshType::VertexType;
|
||||||
|
using VectorType = MeshType::VertexType::VectorType;
|
||||||
using VertexType = Vector3<NumericType>;
|
|
||||||
explicit MeshCollider(MeshType mesh): m_mesh(std::move(mesh))
|
explicit MeshCollider(MeshType mesh): m_mesh(std::move(mesh))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
const VertexType& find_furthest_vertex(const VertexType& direction) const
|
VectorType find_abs_furthest_vertex_position(const VectorType& direction) const override
|
||||||
{
|
{
|
||||||
return *std::ranges::max_element(m_mesh.m_vertex_buffer, [&direction](const auto& first, const auto& second)
|
return m_mesh.vertex_position_to_world_space(find_furthest_vertex(direction).position);
|
||||||
{ return first.dot(direction) < second.dot(direction); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
VertexType find_abs_furthest_vertex(const VertexType& direction) const
|
const VectorType& get_origin() const override
|
||||||
{
|
|
||||||
return m_mesh.vertex_to_world_space(find_furthest_vertex(direction));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
const VertexType& get_origin() const
|
|
||||||
{
|
{
|
||||||
return m_mesh.get_origin();
|
return m_mesh.get_origin();
|
||||||
}
|
}
|
||||||
|
void set_origin(const VectorType& new_origin) override
|
||||||
|
{
|
||||||
|
m_mesh.set_origin(new_origin);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
[[nodiscard]]
|
||||||
|
const VertexType& find_furthest_vertex(const VectorType& direction) const
|
||||||
|
{
|
||||||
|
return *std::ranges::max_element(
|
||||||
|
m_mesh.m_vertex_buffer, [&direction](const auto& first, const auto& second)
|
||||||
|
{ return first.position.dot(direction) < second.position.dot(direction); });
|
||||||
|
}
|
||||||
MeshType m_mesh;
|
MeshType m_mesh;
|
||||||
};
|
};
|
||||||
} // namespace omath::collision
|
} // namespace omath::collision
|
||||||
@@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
namespace omath::frostbite_engine
|
namespace omath::frostbite_engine
|
||||||
{
|
{
|
||||||
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
|
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
|
||||||
}
|
}
|
||||||
@@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
namespace omath::iw_engine
|
namespace omath::iw_engine
|
||||||
{
|
{
|
||||||
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
|
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
|
||||||
}
|
}
|
||||||
@@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
namespace omath::opengl_engine
|
namespace omath::opengl_engine
|
||||||
{
|
{
|
||||||
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
|
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
|
||||||
}
|
}
|
||||||
@@ -8,5 +8,5 @@
|
|||||||
|
|
||||||
namespace omath::source_engine
|
namespace omath::source_engine
|
||||||
{
|
{
|
||||||
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
|
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,13 @@
|
|||||||
|
|
||||||
#undef near
|
#undef near
|
||||||
#undef far
|
#undef far
|
||||||
|
// Undefine FreeBSD/BSD system macros that conflict with method names
|
||||||
|
#ifdef minor
|
||||||
|
#undef minor
|
||||||
|
#endif
|
||||||
|
#ifdef major
|
||||||
|
#undef major
|
||||||
|
#endif
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
struct MatSize
|
struct MatSize
|
||||||
@@ -46,7 +53,7 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr static MatStoreType get_store_ordering() noexcept
|
consteval static MatStoreType get_store_ordering() noexcept
|
||||||
{
|
{
|
||||||
return StoreType;
|
return StoreType;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
|
|
||||||
// Collision detection
|
// Collision detection
|
||||||
#include "omath/collision/line_tracer.hpp"
|
#include "omath/collision/line_tracer.hpp"
|
||||||
|
#include "omath/collision/gjk_algorithm.hpp"
|
||||||
|
#include "omath/collision/epa_algorithm.hpp"
|
||||||
// Pathfinding algorithms
|
// Pathfinding algorithms
|
||||||
#include "omath/pathfinding/a_star.hpp"
|
#include "omath/pathfinding/a_star.hpp"
|
||||||
#include "omath/pathfinding/navigation_mesh.hpp"
|
#include "omath/pathfinding/navigation_mesh.hpp"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "omath/linear_algebra/mat.hpp"
|
#include "omath/linear_algebra/mat.hpp"
|
||||||
|
#include "omath/linear_algebra/triangle.hpp"
|
||||||
#include "omath/linear_algebra/vector3.hpp"
|
#include "omath/linear_algebra/vector3.hpp"
|
||||||
#include "omath/projection/error_codes.hpp"
|
#include "omath/projection/error_codes.hpp"
|
||||||
#include <expected>
|
#include <expected>
|
||||||
@@ -175,6 +176,53 @@ namespace omath::projection
|
|||||||
std::unreachable();
|
std::unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_culled_by_frustum(const Triangle<Vector3<float>>& triangle) const noexcept
|
||||||
|
{
|
||||||
|
// Transform to clip space (before perspective divide)
|
||||||
|
auto to_clip = [this](const Vector3<float>& point)
|
||||||
|
{
|
||||||
|
auto clip = get_view_projection_matrix()
|
||||||
|
* mat_column_from_vector<float, Mat4X4Type::get_store_ordering()>(point);
|
||||||
|
return std::array<float, 4>{
|
||||||
|
clip.at(0, 0), // x
|
||||||
|
clip.at(1, 0), // y
|
||||||
|
clip.at(2, 0), // z
|
||||||
|
clip.at(3, 0) // w
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto c0 = to_clip(triangle.m_vertex1);
|
||||||
|
const auto c1 = to_clip(triangle.m_vertex2);
|
||||||
|
const auto c2 = to_clip(triangle.m_vertex3);
|
||||||
|
|
||||||
|
// If all vertices are behind the camera (w <= 0), trivially reject
|
||||||
|
if (c0[3] <= 0.f && c1[3] <= 0.f && c2[3] <= 0.f)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Helper: all three vertices outside the same clip plane
|
||||||
|
auto all_outside_plane = [](const int axis, const std::array<float, 4>& a, const std::array<float, 4>& b,
|
||||||
|
const std::array<float, 4>& c, const bool positive_side)
|
||||||
|
{
|
||||||
|
if (positive_side)
|
||||||
|
return a[axis] > a[3] && b[axis] > b[3] && c[axis] > c[3];
|
||||||
|
return a[axis] < -a[3] && b[axis] < -b[3] && c[axis] < -c[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clip volume in clip space (OpenGL-style):
|
||||||
|
// -w <= x <= w
|
||||||
|
// -w <= y <= w
|
||||||
|
// -w <= z <= w
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
if (all_outside_plane(i, c0, c1, c2, false))
|
||||||
|
return true; // x < -w (left)
|
||||||
|
if (all_outside_plane(i, c0, c1, c2, true))
|
||||||
|
return true; // x > w (right)
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::expected<Vector3<float>, Error>
|
[[nodiscard]] std::expected<Vector3<float>, Error>
|
||||||
world_to_view_port(const Vector3<float>& world_position) const noexcept
|
world_to_view_port(const Vector3<float>& world_position) const noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,13 +37,6 @@ namespace omath::unreal_engine
|
|||||||
Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near,
|
Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near,
|
||||||
const float far) noexcept
|
const float far) noexcept
|
||||||
{
|
{
|
||||||
const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f);
|
return mat_perspective_left_handed(field_of_view, aspect_ratio, near, far);
|
||||||
|
|
||||||
return {
|
|
||||||
{1.f / (aspect_ratio * fov_half_tan), 0, 0, 0},
|
|
||||||
{0, 1.f / (fov_half_tan), 0, 0},
|
|
||||||
{0, 0, (far + near) / (far - near), -(2.f * far * near) / (far - near)},
|
|
||||||
{0, 0, -1.f, 0},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} // namespace omath::unreal_engine
|
} // namespace omath::unreal_engine
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ namespace omath
|
|||||||
return std::visit(
|
return std::visit(
|
||||||
[base_address, &pattern](const auto& nt_header) -> std::optional<std::uintptr_t>
|
[base_address, &pattern](const auto& nt_header) -> std::optional<std::uintptr_t>
|
||||||
{
|
{
|
||||||
// Define .code segment as scan area
|
// Define .text segment as scan area
|
||||||
const auto start = nt_header.optional_header.base_of_code;
|
const auto start = nt_header.optional_header.base_of_code;
|
||||||
const auto scan_size = nt_header.optional_header.size_code;
|
const auto scan_size = nt_header.optional_header.size_code;
|
||||||
|
|
||||||
|
|||||||
@@ -22,4 +22,8 @@ else() # GTest is being linked as vcpkg package
|
|||||||
find_package(GTest CONFIG REQUIRED)
|
find_package(GTest CONFIG REQUIRED)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest GTest::gtest_main omath::omath)
|
target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest GTest::gtest_main omath::omath)
|
||||||
endif()
|
endif()
|
||||||
gtest_discover_tests(${PROJECT_NAME})
|
|
||||||
|
# Skip test discovery for Android builds - binaries cannot run on host
|
||||||
|
if (NOT (CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME STREQUAL "iOS"))
|
||||||
|
gtest_discover_tests(${PROJECT_NAME})
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -82,6 +82,18 @@ TEST(unit_test_unreal_engine, ProjectTargetMovedFromCamera)
|
|||||||
EXPECT_NEAR(projected->y, 360, 0.00001f);
|
EXPECT_NEAR(projected->y, 360, 0.00001f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TEST(unit_test_unreal_engine, ProjectTargetMovedFromCameraBehind)
|
||||||
|
{
|
||||||
|
constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f);
|
||||||
|
const auto cam = omath::unreal_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.01f, 10000.f);
|
||||||
|
|
||||||
|
for (float distance = 0.02f; distance < 9000.f; distance += 100.f)
|
||||||
|
{
|
||||||
|
const auto projected = cam.world_to_screen({-distance, 0, 0});
|
||||||
|
|
||||||
|
EXPECT_FALSE(projected.has_value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_unreal_engine, CameraSetAndGetFov)
|
TEST(unit_test_unreal_engine, CameraSetAndGetFov)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,20 +7,32 @@
|
|||||||
|
|
||||||
TEST(UnitTestColider, CheckToWorld)
|
TEST(UnitTestColider, CheckToWorld)
|
||||||
{
|
{
|
||||||
omath::source_engine::Mesh mesh = {std::vector<omath::Vector3<float>>{{1.f, 1.f, 1.f}, {-1.f, -1.f, -1.f}}, {}};
|
omath::source_engine::Mesh mesh = {
|
||||||
|
std::vector<omath::primitives::Vertex<>>{
|
||||||
|
{ { 1.f, 1.f, 1.f }, {}, {} },
|
||||||
|
{ {-1.f, -1.f, -1.f }, {}, {} }
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
mesh.set_origin({0, 2, 0});
|
mesh.set_origin({0, 2, 0});
|
||||||
const omath::source_engine::MeshCollider collider(mesh);
|
const omath::source_engine::MeshCollider collider(mesh);
|
||||||
|
|
||||||
const auto vertex = collider.find_abs_furthest_vertex({1.f, 0.f, 0.f});
|
const auto vertex = collider.find_abs_furthest_vertex_position({1.f, 0.f, 0.f});
|
||||||
|
|
||||||
EXPECT_EQ(vertex, omath::Vector3<float>(1.f, 3.f, 1.f));
|
EXPECT_EQ(vertex, omath::Vector3<float>(1.f, 3.f, 1.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UnitTestColider, FindFurthestVertex)
|
TEST(UnitTestColider, FindFurthestVertex)
|
||||||
{
|
{
|
||||||
const omath::source_engine::Mesh mesh = {{{1.f, 1.f, 1.f}, {-1.f, -1.f, -1.f}}, {}};
|
const omath::source_engine::Mesh mesh = {
|
||||||
|
{
|
||||||
|
{ { 1.f, 1.f, 1.f }, {}, {} }, // position, normal, uv
|
||||||
|
{ {-1.f, -1.f, -1.f }, {}, {} }
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
const omath::source_engine::MeshCollider collider(mesh);
|
const omath::source_engine::MeshCollider collider(mesh);
|
||||||
const auto vertex = collider.find_furthest_vertex({1.f, 0.f, 0.f});
|
const auto vertex = collider.find_furthest_vertex({1.f, 0.f, 0.f}).position;
|
||||||
EXPECT_EQ(vertex, omath::Vector3<float>(1.f, 1.f, 1.f));
|
EXPECT_EQ(vertex, omath::Vector3<float>(1.f, 1.f, 1.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "omath/engines/source_engine/mesh.hpp"
|
#include "omath/engines/source_engine/mesh.hpp"
|
||||||
#include "omath/linear_algebra/vector3.hpp"
|
#include "omath/linear_algebra/vector3.hpp"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <memory_resource>
|
||||||
|
|
||||||
using Mesh = omath::source_engine::Mesh;
|
using Mesh = omath::source_engine::Mesh;
|
||||||
using Collider = omath::source_engine::MeshCollider;
|
using Collider = omath::source_engine::MeshCollider;
|
||||||
@@ -14,9 +15,17 @@ using EPA = omath::collision::Epa<Collider>;
|
|||||||
TEST(UnitTestEpa, TestCollisionTrue)
|
TEST(UnitTestEpa, TestCollisionTrue)
|
||||||
{
|
{
|
||||||
// Unit cube [-1,1]^3
|
// Unit cube [-1,1]^3
|
||||||
std::vector<omath::Vector3<float>> vbo = {{-1, -1, -1}, {-1, -1, 1}, {-1, 1, -1}, {-1, 1, 1},
|
std::vector<omath::primitives::Vertex<>> vbo = {
|
||||||
{1, 1, 1}, {1, 1, -1}, {1, -1, 1}, {1, -1, -1}};
|
{ {-1.f, -1.f, -1.f}, {}, {} },
|
||||||
std::vector<omath::Vector3<std::size_t>> vao; // not needed
|
{ {-1.f, -1.f, 1.f}, {}, {} },
|
||||||
|
{ {-1.f, 1.f, -1.f}, {}, {} },
|
||||||
|
{ {-1.f, 1.f, 1.f}, {}, {} },
|
||||||
|
{ { 1.f, 1.f, 1.f}, {}, {} },
|
||||||
|
{ { 1.f, 1.f, -1.f}, {}, {} },
|
||||||
|
{ { 1.f, -1.f, 1.f}, {}, {} },
|
||||||
|
{ { 1.f, -1.f, -1.f}, {}, {} }
|
||||||
|
};
|
||||||
|
std::vector<omath::Vector3<std::uint32_t>> vao; // not needed
|
||||||
|
|
||||||
Mesh a(vbo, vao, {1, 1, 1});
|
Mesh a(vbo, vao, {1, 1, 1});
|
||||||
Mesh b(vbo, vao, {1, 1, 1});
|
Mesh b(vbo, vao, {1, 1, 1});
|
||||||
@@ -33,9 +42,10 @@ TEST(UnitTestEpa, TestCollisionTrue)
|
|||||||
|
|
||||||
// EPA
|
// EPA
|
||||||
EPA::Params params;
|
EPA::Params params;
|
||||||
|
auto pool = std::make_shared<std::pmr::monotonic_buffer_resource>(1024);
|
||||||
params.max_iterations = 64;
|
params.max_iterations = 64;
|
||||||
params.tolerance = 1e-4f;
|
params.tolerance = 1e-4f;
|
||||||
auto epa = EPA::solve(A, B, gjk.simplex, params);
|
auto epa = EPA::solve(A, B, gjk.simplex, params, *pool);
|
||||||
ASSERT_TRUE(epa.has_value()) << "EPA should converge";
|
ASSERT_TRUE(epa.has_value()) << "EPA should converge";
|
||||||
|
|
||||||
// Normal is unit
|
// Normal is unit
|
||||||
@@ -51,7 +61,7 @@ TEST(UnitTestEpa, TestCollisionTrue)
|
|||||||
|
|
||||||
// Try both signs with a tiny margin (avoid grazing contacts)
|
// Try both signs with a tiny margin (avoid grazing contacts)
|
||||||
const float margin = 1.0f + 1e-3f;
|
const float margin = 1.0f + 1e-3f;
|
||||||
const auto pen = epa->normal * epa->depth;
|
const auto pen = epa->penetration_vector;
|
||||||
|
|
||||||
Mesh b_plus = b;
|
Mesh b_plus = b;
|
||||||
b_plus.set_origin(b_plus.get_origin() + pen * margin);
|
b_plus.set_origin(b_plus.get_origin() + pen * margin);
|
||||||
@@ -80,9 +90,17 @@ TEST(UnitTestEpa, TestCollisionTrue)
|
|||||||
}
|
}
|
||||||
TEST(UnitTestEpa, TestCollisionTrue2)
|
TEST(UnitTestEpa, TestCollisionTrue2)
|
||||||
{
|
{
|
||||||
std::vector<omath::Vector3<float>> vbo = {{-1, -1, -1}, {-1, -1, 1}, {-1, 1, -1}, {-1, 1, 1},
|
std::vector<omath::primitives::Vertex<>> vbo = {
|
||||||
{1, 1, 1}, {1, 1, -1}, {1, -1, 1}, {1, -1, -1}};
|
{ { -1.f, -1.f, -1.f }, {}, {} },
|
||||||
std::vector<omath::Vector3<std::size_t>> vao; // not needed
|
{ { -1.f, -1.f, 1.f }, {}, {} },
|
||||||
|
{ { -1.f, 1.f, -1.f }, {}, {} },
|
||||||
|
{ { -1.f, 1.f, 1.f }, {}, {} },
|
||||||
|
{ { 1.f, 1.f, 1.f }, {}, {} },
|
||||||
|
{ { 1.f, 1.f, -1.f }, {}, {} },
|
||||||
|
{ { 1.f, -1.f, 1.f }, {}, {} },
|
||||||
|
{ { 1.f, -1.f, -1.f }, {}, {} }
|
||||||
|
};
|
||||||
|
std::vector<omath::Vector3<std::uint32_t>> vao; // not needed
|
||||||
|
|
||||||
Mesh a(vbo, vao, {1, 1, 1});
|
Mesh a(vbo, vao, {1, 1, 1});
|
||||||
Mesh b(vbo, vao, {1, 1, 1});
|
Mesh b(vbo, vao, {1, 1, 1});
|
||||||
@@ -96,12 +114,12 @@ TEST(UnitTestEpa, TestCollisionTrue2)
|
|||||||
// --- GJK must detect collision and provide simplex ---
|
// --- GJK must detect collision and provide simplex ---
|
||||||
auto gjk = GJK::is_collide_with_simplex_info(A, B);
|
auto gjk = GJK::is_collide_with_simplex_info(A, B);
|
||||||
ASSERT_TRUE(gjk.hit) << "GJK should report collision for overlapping cubes";
|
ASSERT_TRUE(gjk.hit) << "GJK should report collision for overlapping cubes";
|
||||||
|
|
||||||
// --- EPA penetration ---
|
// --- EPA penetration ---
|
||||||
EPA::Params params;
|
EPA::Params params;
|
||||||
params.max_iterations = 64;
|
params.max_iterations = 64;
|
||||||
params.tolerance = 1e-4f;
|
params.tolerance = 1e-4f;
|
||||||
auto epa = EPA::solve(A, B, gjk.simplex, params);
|
auto pool = std::make_shared<std::pmr::monotonic_buffer_resource>(1024);
|
||||||
|
auto epa = EPA::solve(A, B, gjk.simplex, params, *pool);
|
||||||
ASSERT_TRUE(epa.has_value()) << "EPA should converge";
|
ASSERT_TRUE(epa.has_value()) << "EPA should converge";
|
||||||
|
|
||||||
// Normal is unit-length
|
// Normal is unit-length
|
||||||
@@ -115,12 +133,8 @@ TEST(UnitTestEpa, TestCollisionTrue2)
|
|||||||
EXPECT_NEAR(epa->normal.y, 0.0f, 1e-3f);
|
EXPECT_NEAR(epa->normal.y, 0.0f, 1e-3f);
|
||||||
EXPECT_NEAR(epa->normal.z, 0.0f, 1e-3f);
|
EXPECT_NEAR(epa->normal.z, 0.0f, 1e-3f);
|
||||||
|
|
||||||
// Choose a deterministic sign: orient penetration from A toward B
|
|
||||||
const auto centers = b.get_origin() - a.get_origin(); // (0.5, 0, 0)
|
|
||||||
float sign = (epa->normal.dot(centers) >= 0.0f) ? +1.0f : -1.0f;
|
|
||||||
|
|
||||||
constexpr float margin = 1.0f + 1e-3f; // tiny slack to avoid grazing
|
constexpr float margin = 1.0f + 1e-3f; // tiny slack to avoid grazing
|
||||||
const auto pen = epa->normal * epa->depth * sign;
|
const auto pen = epa->normal * epa->depth;
|
||||||
|
|
||||||
// Apply once: B + pen must separate; the opposite must still collide
|
// Apply once: B + pen must separate; the opposite must still collide
|
||||||
Mesh b_resolved = b;
|
Mesh b_resolved = b;
|
||||||
|
|||||||
@@ -8,15 +8,18 @@
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const omath::source_engine::Mesh mesh = {
|
const omath::source_engine::Mesh mesh = {
|
||||||
{{-1.f, -1.f, -1.f},
|
{
|
||||||
{-1.f, -1.f, 1.f},
|
{ {-1.f, -1.f, -1.f}, {}, {} },
|
||||||
{-1.f, 1.f, -1.f},
|
{ {-1.f, -1.f, 1.f}, {}, {} },
|
||||||
{-1.f, 1.f, 1.f},
|
{ {-1.f, 1.f, -1.f}, {}, {} },
|
||||||
{1.f, 1.f, 1.f},
|
{ {-1.f, 1.f, 1.f}, {}, {} },
|
||||||
{1.f, 1.f, -1.f},
|
{ { 1.f, 1.f, 1.f}, {}, {} },
|
||||||
{1.f, -1.f, 1.f},
|
{ { 1.f, 1.f, -1.f}, {}, {} },
|
||||||
{1.f, -1.f, -1.f}},
|
{ { 1.f, -1.f, 1.f}, {}, {} },
|
||||||
{}};
|
{ { 1.f, -1.f, -1.f}, {}, {} }
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
TEST(UnitTestGjk, TestCollisionTrue)
|
TEST(UnitTestGjk, TestCollisionTrue)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -231,3 +231,13 @@ TEST(UnitTestMatStandalone, Equanity)
|
|||||||
|
|
||||||
EXPECT_EQ(ndc_left_handed, ndc_right_handed);
|
EXPECT_EQ(ndc_left_handed, ndc_right_handed);
|
||||||
}
|
}
|
||||||
|
TEST(UnitTestMatStandalone, MatPerspectiveLeftHanded)
|
||||||
|
{
|
||||||
|
auto perspective_proj = mat_perspective_left_handed(90.f, 16.f/9.f, 0.1f, 1000.f);
|
||||||
|
auto projected = perspective_proj
|
||||||
|
* mat_column_from_vector<float>({0, 0, 0.1001});
|
||||||
|
|
||||||
|
projected /= projected.at(3, 0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(projected.at(2, 0) > -1.0f && projected.at(2, 0) < 0.f);
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"description": "General purpose math library",
|
"description": "General purpose math library",
|
||||||
"homepage": "https://github.com/orange-cpp/omath",
|
"homepage": "https://github.com/orange-cpp/omath",
|
||||||
"license": "Zlib",
|
"license": "Zlib",
|
||||||
"supports": "windows | linux",
|
"supports": "windows | linux | macos",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
{
|
{
|
||||||
"name": "vcpkg-cmake",
|
"name": "vcpkg-cmake",
|
||||||
@@ -26,6 +26,13 @@
|
|||||||
"benchmark"
|
"benchmark"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"examples": {
|
||||||
|
"description": "Build benchmarks",
|
||||||
|
"dependencies": [
|
||||||
|
"glfw3",
|
||||||
|
"glew"
|
||||||
|
]
|
||||||
|
},
|
||||||
"imgui": {
|
"imgui": {
|
||||||
"description": "Omath will define method to convert omath types to imgui types",
|
"description": "Omath will define method to convert omath types to imgui types",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
|||||||
Reference in New Issue
Block a user