Compare commits

...

66 Commits

Author SHA1 Message Date
94ee8751af Merge pull request #116 from luadebug/wasm
Add WASM support
2025-12-17 14:02:58 +03:00
Saikari
82b9b671f6 Add WASM support 2025-12-17 13:38:14 +03:00
082b5f69b8 Merge pull request #115 from luadebug/iphone
Add iOS support
2025-12-16 19:16:52 +03:00
Saikari
735a565446 Add iOS support 2025-12-16 14:48:20 +03:00
852bf5c56f Merge pull request #114 from luadebug/ndk
Add NDK support
2025-12-15 07:08:56 +03:00
Saikari
de5c8bc84d Add NDK support 2025-12-15 00:04:11 +03:00
35d9de1550 Merge pull request #113 from luadebug/freebsd
Add FreeBSD support
2025-12-14 22:57:07 +03:00
Saikari
201d8f5547 Add FreeBSD support 2025-12-14 22:48:29 +03:00
5a7b9d2338 Merge pull request #112 from orange-cpp/feature/test
fix
2025-12-14 12:31:45 +03:00
3d67827704 fix 2025-12-14 12:20:05 +03:00
45a37eb413 Merge pull request #111 from orange-cpp/feature/frustum_culling_method
added check method
2025-12-14 11:15:17 +03:00
90c4ea2036 removed nesting 2025-12-14 11:08:06 +03:00
e10cbf9356 added const 2025-12-14 11:06:15 +03:00
4ad44badb9 removed double size reserve 2025-12-14 11:05:24 +03:00
adce4a808a improved readability 2025-12-14 10:59:38 +03:00
257b06c552 extracted to methdod 2025-12-14 10:42:23 +03:00
a94c78f834 added nodiscard 2025-12-14 10:39:23 +03:00
b6ac0a1d61 decomposed into method 2025-12-14 10:26:36 +03:00
f3b74fe433 removed useless comment 2025-12-14 10:18:58 +03:00
2ddf29b158 style fix 2025-12-14 09:53:12 +03:00
bf30957acf renamed stuff 2025-12-14 09:50:46 +03:00
c9ac61935e updated preset file 2025-12-13 23:34:15 +03:00
60a3a42140 improved pmr stuff 2025-12-13 00:06:45 +03:00
17e21cde4b added missing include 2025-12-12 18:34:56 +03:00
7fb5ea47dd added check method 2025-12-09 10:21:54 +03:00
d7189eb7d4 added extra test 2025-12-09 10:16:57 +03:00
ff35571231 reverted is out of bounds check + added extra test 2025-12-09 10:06:25 +03:00
3744a6cdec returned stuff back 2025-12-09 09:31:22 +03:00
0fd9a5aed8 hot fix 2025-12-09 09:26:55 +03:00
3dd792c2d5 Merge pull request #110 from orange-cpp/feature/bug_fix
fixed bug due to on screen ndc check
2025-12-09 08:08:05 +03:00
584969da44 fixed bug due to on screen ndc check 2025-12-09 07:53:52 +03:00
acf36c3e04 Merge pull request #109 from orange-cpp/feature/collider_interface
Feature/collider interface
2025-12-06 14:59:34 +03:00
27c1d147c5 fixed naming 2025-12-06 14:49:30 +03:00
3831bc0999 removed file 2025-12-06 14:34:20 +03:00
d23bc3204d update 2025-12-06 14:30:44 +03:00
c158f08430 fix 2025-12-06 13:56:25 +03:00
e05eba42c3 added collider interface 2025-12-06 13:56:25 +03:00
e97be8c142 Merge pull request #108 from orange-cpp/fearure/epa_pmr
Fearure/epa pmr
2025-12-04 08:41:55 +03:00
e97d097b2b fix 2025-12-04 05:08:01 +03:00
58aa03c4a9 patch 2025-12-04 05:02:54 +03:00
e1399d1814 fix 2025-12-04 05:01:52 +03:00
1964d3d36f replaced with bool 2025-12-04 04:52:22 +03:00
d7a009eb67 oops 2025-12-04 04:47:34 +03:00
0e03805439 added nodiscard + static 2025-12-04 04:46:00 +03:00
eafefb40ec fixed typo 2025-12-04 04:45:15 +03:00
9e4c778e8f tweak 2025-12-04 04:38:44 +03:00
0788fd6122 replaced with emplace 2025-12-03 14:11:29 +03:00
3685f13344 back to static 2025-12-03 13:34:35 +03:00
d4d8f70fff switched to shared_ptr 2025-12-03 13:30:05 +03:00
918858e255 added poly allocators 2025-12-03 09:52:53 +03:00
1aff083ef3 fixed uv type 2025-12-01 05:03:43 +03:00
6414922884 added mesh culling 2025-11-30 06:19:38 +03:00
57ba809076 Auto stash before merge of "main" and "origin/main" 2025-11-30 01:32:25 +03:00
6fd3a695cf cleaned up code 2025-11-29 22:00:07 +03:00
f6857cac90 updated read me 2025-11-29 21:45:09 +03:00
b994e47357 Merge pull request #107 from orange-cpp/feature/example_improvement
fixed somebugs, improved tests
2025-11-29 21:41:43 +03:00
82b21d0458 fixed rotation 2025-11-29 21:34:40 +03:00
daa1abc047 added rotation by pitch 2025-11-29 21:31:20 +03:00
3a66b66c6a fixed somebugs, improved tests 2025-11-29 21:24:45 +03:00
6c89c72041 Merge pull request #106 from orange-cpp/feature/mesh_upgrade
Feature/mesh upgrade
2025-11-29 16:55:31 +03:00
e54d5e7388 changed ebo type 2025-11-29 16:49:19 +03:00
9a89e2467e fix 2025-11-29 16:37:30 +03:00
48bf06f69c refactored using stuff 2025-11-29 16:35:43 +03:00
8feddf872a added conecpt to method 2025-11-29 16:33:31 +03:00
99ebdeb188 added template arg to Vertex struct 2025-11-29 16:31:24 +03:00
ba267cbcb8 improved mesh class 2025-11-29 16:28:06 +03:00
29 changed files with 1151 additions and 204 deletions

View File

@@ -1,4 +1,4 @@
name: Omath CI (Arch Linux / Windows)
name: Omath CI
on:
push:
@@ -12,11 +12,11 @@ concurrency:
##############################################################################
# 1) ARCH LINUX Clang / Ninja
# 1) Arch Linux Clang / Ninja
##############################################################################
jobs:
arch-build-and-test:
name: Arch Linux (Clang)
name: Arch Linux (Clang) (x64-linux)
runs-on: ubuntu-latest
container: archlinux:latest
env:
@@ -56,7 +56,7 @@ jobs:
# 2) Windows MSVC / Ninja
##############################################################################
windows-build-and-test:
name: Windows (MSVC)
name: Windows (MSVC) (x64-windows)
runs-on: windows-latest
env:
OMATH_BUILD_VIA_VCPKG: ON
@@ -89,7 +89,7 @@ jobs:
# 3) macOS AppleClang / Ninja
##############################################################################
macosx-build-and-test:
name: macOS (AppleClang)
name: macOS (AppleClang) (arm64-osx)
runs-on: macOS-latest
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
@@ -120,3 +120,190 @@ jobs:
- name: Run unit_tests
shell: bash
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
View File

@@ -201,7 +201,7 @@
<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/=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/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" 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/=CppUnmatchedPragmaRegionDirective/@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/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />

View File

@@ -35,6 +35,8 @@ if (VCPKG_MANIFEST_FEATURES)
set(OMATH_BUILD_TESTS ON)
elseif (omath_feature STREQUAL "benchmark")
set(OMATH_BUILD_BENCHMARK ON)
elseif (omath_feature STREQUAL "examples")
set(OMATH_BUILD_EXAMPLES ON)
endif ()
endforeach ()

View File

@@ -25,7 +25,7 @@
"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"
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
}
},
{
@@ -38,7 +38,7 @@
},
{
"name": "windows-debug-vcpkg",
"displayName": "Debug",
"displayName": "Windows Debug Vcpkg",
"inherits": "windows-base-vcpkg",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
@@ -46,7 +46,7 @@
},
{
"name": "windows-release-vcpkg",
"displayName": "Release",
"displayName": "Windows Release Vcpkg",
"inherits": "windows-base-vcpkg",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
@@ -144,7 +144,7 @@
"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"
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
}
},
{
@@ -157,7 +157,7 @@
},
{
"name": "darwin-debug-vcpkg",
"displayName": "Darwin Debug",
"displayName": "Darwin Debug Vcpkg",
"inherits": "darwin-base-vcpkg",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
@@ -173,11 +173,198 @@
},
{
"name": "darwin-release-vcpkg",
"displayName": "Darwin Release",
"displayName": "Darwin Release Vcpkg",
"inherits": "darwin-base-vcpkg",
"cacheVariables": {
"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"
}
}
]
}

View File

@@ -106,6 +106,10 @@ if (auto screen = camera.world_to_screen(world_position)) {
![TF2 Preview]
<br>
![OpenGL Preview]
<br>
<br>
@@ -135,6 +139,7 @@ if (auto screen = camera.world_to_screen(world_position)) {
[BO2 Preview]: docs/images/showcase/cod_bo2.png
[CS2 Preview]: docs/images/showcase/cs2.jpeg
[TF2 Preview]: docs/images/showcase/tf2.jpg
[OpenGL Preview]: docs/images/showcase/opengl.png
<!----------------------------------{ Buttons }--------------------------------->
[QUICKSTART]: docs/getting_started.md
[INSTALL]: INSTALL.md

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

View File

@@ -1,9 +1,32 @@
project(examples)
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)
add_executable(example_signature_scan example_signature_scan.cpp)
set_target_properties(example_signature_scan PROPERTIES CXX_STANDARD 26)
target_link_libraries(example_signature_scan PRIVATE omath::omath)
set_target_properties(example_signature_scan 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_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
View 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;
}

View File

@@ -6,35 +6,57 @@
#include <omath/linear_algebra/mat.hpp>
#include <omath/linear_algebra/vector3.hpp>
#include <utility>
#include <variant>
#include <vector>
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
{
public:
using NumericType = Type;
using VectorType = VertType::VectorType;
using VertexType = VertType;
private:
using Vbo = std::vector<Vector3<NumericType>>;
using Vao = std::vector<Vector3<std::size_t>>;
using Vbo = std::vector<VertexType>;
using Ebo = std::vector<Vector3<std::uint32_t>>;
public:
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,})
: m_vertex_buffer(std::move(vbo)), m_vertex_array_object(std::move(vao)), m_scale(std::move(scale))
Mesh(Vbo vbo, Ebo vao,
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_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_to_world_matrix = std::nullopt;
@@ -47,13 +69,13 @@ namespace omath::primitives
}
[[nodiscard]]
const Vector3<NumericType>& get_origin() const
const VectorType& get_origin() const
{
return m_origin;
}
[[nodiscard]]
const Vector3<NumericType>& get_scale() const
const VectorType& get_scale() const
{
return m_scale;
}
@@ -69,31 +91,34 @@ namespace omath::primitives
{
if (m_to_world_matrix)
return m_to_world_matrix.value();
m_to_world_matrix =
mat_translation(m_origin) * mat_scale(m_scale) * MeshTypeTrait::rotation_matrix(m_rotation_angles);
m_to_world_matrix = mat_translation<float, Mat4X4::get_store_ordering()>(m_origin)
* MeshTypeTrait::rotation_matrix(m_rotation_angles)
* mat_scale<float, Mat4X4::get_store_ordering()>(m_scale);
return m_to_world_matrix.value();
}
[[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)};
}
[[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)),
vertex_to_world_space(m_vertex_buffer.at(vao_iterator->y)),
vertex_to_world_space(m_vertex_buffer.at(vao_iterator->z))};
return {vertex_position_to_world_space(m_vertex_buffer.at(vao_iterator->x).position),
vertex_position_to_world_space(m_vertex_buffer.at(vao_iterator->y).position),
vertex_position_to_world_space(m_vertex_buffer.at(vao_iterator->z).position)};
}
private:
Vector3<NumericType> m_origin;
Vector3<NumericType> m_scale;
VectorType m_origin;
VectorType m_scale;
RotationAngles m_rotation_angles;

View 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;
};
}

View File

@@ -1,11 +1,14 @@
#pragma once
#include "simplex.hpp"
#include <algorithm> // find_if
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <limits>
#include <memory>
#include <memory_resource>
#include <queue>
#include <utility>
#include <vector>
namespace omath::collision
@@ -16,21 +19,21 @@ namespace omath::collision
{ a.cross(b) } -> std::same_as<V>;
{ a.dot(b) } -> std::same_as<float>;
{ -a } -> 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
{
public:
using Vertex = typename ColliderType::VertexType;
static_assert(EpaVector<Vertex>, "VertexType must satisfy EpaVector concept");
using VectorType = ColliderInterfaceType::VectorType;
static_assert(EpaVector<VectorType>, "VertexType must satisfy EpaVector concept");
struct Result final
{
Vertex normal{}; // outward normal (from B to A)
Vertex penetration_vector;
VectorType normal{}; // from A to B
VectorType penetration_vector;
float depth{0.0f};
int iterations{0};
int num_vertices{0};
@@ -42,27 +45,19 @@ namespace omath::collision
int max_iterations{64};
float tolerance{1e-4f}; // absolute tolerance on distance growth
};
// Precondition: simplex.size()==4 and contains the origin.
[[nodiscard]]
static std::optional<Result> solve(const ColliderType& a, const ColliderType& b, const Simplex<Vertex>& simplex,
const Params params = {})
static std::optional<Result> solve(const ColliderInterfaceType& a, const ColliderInterfaceType& b,
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) ---
std::vector<Vertex> vertexes;
vertexes.reserve(64);
for (std::size_t i = 0; i < simplex.size(); ++i)
vertexes.push_back(simplex[i]);
std::pmr::vector<VectorType> vertexes = build_initial_polytope_from_simplex(simplex, mem_resource);
// Initial tetra faces (windings corrected in make_face)
std::vector<Face> faces;
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));
std::pmr::vector<Face> faces = create_initial_tetra_faces(mem_resource, vertexes);
auto heap = rebuild_heap(faces);
auto heap = rebuild_heap(faces, mem_resource);
Result out{};
@@ -75,104 +70,71 @@ namespace omath::collision
// (We could keep face handles; this is fine for small Ns.)
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())
break;
const int fidx = heap.top().idx;
const Face f = faces[fidx];
const Face face = faces[fidx];
// Get farthest point in face normal direction
const Vertex p = support_point(a, b, f.n);
const float p_dist = f.n.dot(p);
// Get the furthest point in face normal direction
const VectorType p = support_point(a, b, face.n);
const float p_dist = face.n.dot(p);
// Converged if we cant 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.depth = f.d; // along unit normal
out.normal = face.n;
out.depth = face.d; // along unit normal
out.iterations = it + 1;
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 sign = out.normal.dot(centers) >= 0 ? 1 : -1;
out.penetration_vector = out.normal * out.depth * sign;
out.penetration_vector = out.normal * out.depth;
return out;
}
// Add new vertex
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
std::vector<char> to_delete(faces.size(), 0);
std::vector<Edge> boundary;
boundary.reserve(faces.size() * 2);
const auto [to_delete, boundary] = mark_visible_and_collect_horizon(faces, p);
for (int i = 0; i < static_cast<int>(faces.size()); ++i)
{
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);
erase_marked(faces, to_delete);
// Stitch new faces around the horizon
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
heap = rebuild_heap(faces);
heap = rebuild_heap(faces, mem_resource);
if (!std::isfinite(vertexes.back().dot(vertexes.back())))
break; // safety
out.iterations = it + 1;
}
// Fallback: pick closest face as best-effort answer
if (!faces.empty())
{
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());
if (faces.empty())
return std::nullopt;
const auto centers = b.get_origin() - a.get_origin();
const auto sign = out.normal.dot(centers) >= 0 ? 1 : -1;
const auto best = *std::ranges::min_element(faces, [](const auto& first, const auto& second)
{ 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 std::nullopt;
return out;
}
private:
struct Face final
{
int i0, i1, i2;
Vertex n; // unit outward normal
VectorType n; // unit outward normal
float d; // n · v0 (>=0 ideally because origin is inside)
};
@@ -188,47 +150,53 @@ namespace omath::collision
};
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
}
};
using Heap = std::priority_queue<HeapItem, std::vector<HeapItem>, HeapCmp>;
using Heap = std::priority_queue<HeapItem, std::pmr::vector<HeapItem>, HeapCmp>;
[[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)
h.push({faces[i].d, i});
return h;
h.emplace(faces[i].d, i);
return h; // allocator is preserved
}
[[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
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
auto itb =
std::find_if(boundary.begin(), boundary.end(), [&](const Edge& e) { return e.a == b && e.b == a; });
auto itb = std::ranges::find_if(boundary, [&](const Edge& e) { return e.a == b && e.b == a; });
if (itb != boundary.end())
boundary.erase(itb); // internal edge cancels out
else
boundary.push_back({a, b}); // horizon edge (directed)
boundary.emplace_back(a, b); // horizon edge (directed)
}
[[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 Vertex& a1 = vertexes[i1];
const Vertex& a2 = vertexes[i2];
Vertex n = (a1 - a0).cross(a2 - a0);
const VectorType& a0 = vertexes[i0];
const VectorType& a1 = vertexes[i1];
const VectorType& a2 = vertexes[i2];
VectorType n = (a1 - a0).cross(a2 - a0);
if (n.dot(n) <= 1e-30f)
{
n = any_perp_vec(a1 - a0); // degenerate guard
@@ -246,9 +214,10 @@ namespace omath::collision
}
[[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>
@@ -267,5 +236,67 @@ namespace omath::collision
return d;
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

View File

@@ -14,32 +14,33 @@ namespace omath::collision
Simplex<VertexType> simplex; // valid only if hit == true and size==4
};
template<class ColliderType>
template<class ColliderInterfaceType>
class GjkAlgorithm final
{
using VertexType = typename ColliderType::VertexType;
using VectorType = ColliderInterfaceType::VectorType;
public:
[[nodiscard]]
static VertexType find_support_vertex(const ColliderType& collider_a, const ColliderType& collider_b,
const VertexType& direction)
static VectorType find_support_vertex(const ColliderInterfaceType& collider_a,
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]]
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;
}
[[nodiscard]]
static GjkHitInfo<VertexType> is_collide_with_simplex_info(const ColliderType& collider_a,
const ColliderType& collider_b)
static GjkHitInfo<VectorType> is_collide_with_simplex_info(const ColliderInterfaceType& collider_a,
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);
auto direction = -support;

View File

@@ -3,40 +3,53 @@
//
#pragma once
#include "collider_interface.hpp"
#include "omath/linear_algebra/vector3.hpp"
#ifdef OMATH_BUILD_TESTS
// ReSharper disable once CppInconsistentNaming
class UnitTestColider_FindFurthestVertex_Test;
#endif
namespace omath::collision
{
template<class MeshType>
class MeshCollider
class MeshCollider final : public ColliderInterface<typename MeshType::VertexType::VectorType>
{
#ifdef OMATH_BUILD_TESTS
friend UnitTestColider_FindFurthestVertex_Test;
#endif
public:
using NumericType = typename MeshType::NumericType;
using VertexType = Vector3<NumericType>;
using VertexType = MeshType::VertexType;
using VectorType = MeshType::VertexType::VectorType;
explicit MeshCollider(MeshType mesh): m_mesh(std::move(mesh))
{
}
[[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 first.dot(direction) < second.dot(direction); });
return m_mesh.vertex_position_to_world_space(find_furthest_vertex(direction).position);
}
[[nodiscard]]
VertexType find_abs_furthest_vertex(const VertexType& direction) const
{
return m_mesh.vertex_to_world_space(find_furthest_vertex(direction));
}
[[nodiscard]]
const VertexType& get_origin() const
const VectorType& get_origin() const override
{
return m_mesh.get_origin();
}
void set_origin(const VectorType& new_origin) override
{
m_mesh.set_origin(new_origin);
}
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;
};
} // namespace omath::collision

View File

@@ -8,5 +8,5 @@
namespace omath::frostbite_engine
{
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
}

View File

@@ -8,5 +8,5 @@
namespace omath::iw_engine
{
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
}

View File

@@ -8,5 +8,5 @@
namespace omath::opengl_engine
{
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
}

View File

@@ -8,5 +8,5 @@
namespace omath::source_engine
{
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
}

View File

@@ -17,6 +17,13 @@
#undef near
#undef far
// Undefine FreeBSD/BSD system macros that conflict with method names
#ifdef minor
#undef minor
#endif
#ifdef major
#undef major
#endif
namespace omath
{
struct MatSize
@@ -46,7 +53,7 @@ namespace omath
}
[[nodiscard]]
constexpr static MatStoreType get_store_ordering() noexcept
consteval static MatStoreType get_store_ordering() noexcept
{
return StoreType;
}

View File

@@ -30,7 +30,8 @@
// Collision detection
#include "omath/collision/line_tracer.hpp"
#include "omath/collision/gjk_algorithm.hpp"
#include "omath/collision/epa_algorithm.hpp"
// Pathfinding algorithms
#include "omath/pathfinding/a_star.hpp"
#include "omath/pathfinding/navigation_mesh.hpp"

View File

@@ -5,6 +5,7 @@
#pragma once
#include "omath/linear_algebra/mat.hpp"
#include "omath/linear_algebra/triangle.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include "omath/projection/error_codes.hpp"
#include <expected>
@@ -175,6 +176,53 @@ namespace omath::projection
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>
world_to_view_port(const Vector3<float>& world_position) const noexcept
{

View File

@@ -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,
const float far) noexcept
{
const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f);
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},
};
return mat_perspective_left_handed(field_of_view, aspect_ratio, near, far);
}
} // namespace omath::unreal_engine

View File

@@ -320,7 +320,7 @@ namespace omath
return std::visit(
[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 scan_size = nt_header.optional_header.size_code;

View File

@@ -22,4 +22,8 @@ else() # GTest is being linked as vcpkg package
find_package(GTest CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest GTest::gtest_main omath::omath)
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()

View File

@@ -82,6 +82,18 @@ TEST(unit_test_unreal_engine, ProjectTargetMovedFromCamera)
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)
{

View File

@@ -7,20 +7,32 @@
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});
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));
}
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 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));
}

View File

@@ -5,6 +5,7 @@
#include "omath/engines/source_engine/mesh.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <gtest/gtest.h>
#include <memory_resource>
using Mesh = omath::source_engine::Mesh;
using Collider = omath::source_engine::MeshCollider;
@@ -14,9 +15,17 @@ using EPA = omath::collision::Epa<Collider>;
TEST(UnitTestEpa, TestCollisionTrue)
{
// Unit cube [-1,1]^3
std::vector<omath::Vector3<float>> vbo = {{-1, -1, -1}, {-1, -1, 1}, {-1, 1, -1}, {-1, 1, 1},
{1, 1, 1}, {1, 1, -1}, {1, -1, 1}, {1, -1, -1}};
std::vector<omath::Vector3<std::size_t>> vao; // not needed
std::vector<omath::primitives::Vertex<>> vbo = {
{ {-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}, {}, {} }
};
std::vector<omath::Vector3<std::uint32_t>> vao; // not needed
Mesh a(vbo, vao, {1, 1, 1});
Mesh b(vbo, vao, {1, 1, 1});
@@ -33,9 +42,10 @@ TEST(UnitTestEpa, TestCollisionTrue)
// EPA
EPA::Params params;
auto pool = std::make_shared<std::pmr::monotonic_buffer_resource>(1024);
params.max_iterations = 64;
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";
// Normal is unit
@@ -51,7 +61,7 @@ TEST(UnitTestEpa, TestCollisionTrue)
// Try both signs with a tiny margin (avoid grazing contacts)
const float margin = 1.0f + 1e-3f;
const auto pen = epa->normal * epa->depth;
const auto pen = epa->penetration_vector;
Mesh b_plus = b;
b_plus.set_origin(b_plus.get_origin() + pen * margin);
@@ -80,9 +90,17 @@ TEST(UnitTestEpa, TestCollisionTrue)
}
TEST(UnitTestEpa, TestCollisionTrue2)
{
std::vector<omath::Vector3<float>> vbo = {{-1, -1, -1}, {-1, -1, 1}, {-1, 1, -1}, {-1, 1, 1},
{1, 1, 1}, {1, 1, -1}, {1, -1, 1}, {1, -1, -1}};
std::vector<omath::Vector3<std::size_t>> vao; // not needed
std::vector<omath::primitives::Vertex<>> vbo = {
{ { -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 }, {}, {} }
};
std::vector<omath::Vector3<std::uint32_t>> vao; // not needed
Mesh a(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 ---
auto gjk = GJK::is_collide_with_simplex_info(A, B);
ASSERT_TRUE(gjk.hit) << "GJK should report collision for overlapping cubes";
// --- EPA penetration ---
EPA::Params params;
params.max_iterations = 64;
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";
// Normal is unit-length
@@ -115,12 +133,8 @@ TEST(UnitTestEpa, TestCollisionTrue2)
EXPECT_NEAR(epa->normal.y, 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
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
Mesh b_resolved = b;

View File

@@ -8,15 +8,18 @@
namespace
{
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)
{

View File

@@ -231,3 +231,13 @@ TEST(UnitTestMatStandalone, Equanity)
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);
}

View File

@@ -4,7 +4,7 @@
"description": "General purpose math library",
"homepage": "https://github.com/orange-cpp/omath",
"license": "Zlib",
"supports": "windows | linux",
"supports": "windows | linux | macos",
"dependencies": [
{
"name": "vcpkg-cmake",
@@ -26,6 +26,13 @@
"benchmark"
]
},
"examples": {
"description": "Build benchmarks",
"dependencies": [
"glfw3",
"glew"
]
},
"imgui": {
"description": "Omath will define method to convert omath types to imgui types",
"dependencies": [