Compare commits

...

241 Commits

Author SHA1 Message Date
cb45b9bb04 added files 2026-02-25 05:58:50 +03:00
f3656f9d2c updated baseline 2026-02-25 05:58:50 +03:00
7ebbed6763 added funding
edit
2026-02-23 07:18:25 +03:00
e271bccaf5 added codeowners 2026-02-23 06:45:43 +03:00
50765f69c5 removed unused var 2026-02-23 04:36:48 +03:00
1169534133 fix 2026-02-23 04:32:13 +03:00
783501aab9 Enhance installation guide with prebuilt binaries section
Updated vcpkg section and added instructions for using prebuilt binaries from GitHub Releases.
2026-02-21 10:00:19 +03:00
1a79566731 Merge pull request #156 from orange-cpp/copilot/add-automatic-binary-attach
Add release workflow with binary uploads for all CI platforms
2026-02-21 09:31:12 +03:00
copilot-swe-agent[bot]
9183406e7a Add all CI pipeline platforms to release workflow (Linux x86, iOS, FreeBSD, Android, WebAssembly, MinGW)
Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>
2026-02-21 04:55:36 +00:00
copilot-swe-agent[bot]
b592af91a9 Add release workflow to automatically attach binaries to new releases
Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>
2026-02-21 04:45:20 +00:00
copilot-swe-agent[bot]
af9c043e95 Initial plan 2026-02-21 04:41:46 +00:00
2aaa8633b9 updated tag 2026-02-20 09:07:33 +03:00
0d1cc2088f Merge pull request #155 from orange-cpp/feaute/examples-folders
retructurized examples
2026-02-20 08:55:20 +03:00
260b3b97f9 fix 2026-02-20 08:53:03 +03:00
e0904690e6 fixed formating 2026-02-20 08:52:26 +03:00
7021d9d365 fixed formating 2026-02-20 08:46:43 +03:00
862d52593a retructurized examples 2026-02-20 07:48:19 +03:00
97a3111791 fixed code style 2026-02-20 04:02:26 +03:00
a9bec00973 Merge pull request #154 from luadebug/example1
Example with opengl triangle
2026-02-20 03:37:58 +03:00
Saikari
66debb46fa Refactor to use Color class and Triangle structure for better clarity in drawing functions 2026-02-20 03:01:34 +03:00
Saikari
b8323d3bc0 simplify complexity 2026-02-20 02:56:27 +03:00
Saikari
f363fa6f1a Fix text 2026-02-20 02:53:20 +03:00
Saikari
0546272493 Use dot not quad 2026-02-20 02:50:55 +03:00
Saikari
58801bfab3 Add one more example 2026-02-20 02:43:27 +03:00
4567cfa318 updated read me 2026-02-19 12:03:19 +03:00
1db9340dbf updated read me 2026-02-19 08:42:58 +03:00
7b9a181b0e Merge pull request #153 from orange-cpp/feature/cryengine-support
Feature/cryengine support
2026-02-19 08:34:33 +03:00
0876af3c14 added warning back 2026-02-19 08:34:21 +03:00
e46a369ddc added more tests 2026-02-19 08:06:34 +03:00
630ffa69f6 clang format 2026-02-19 08:00:23 +03:00
609970cdfe fix 2026-02-19 07:57:04 +03:00
e935155022 improved tests 2026-02-19 07:47:23 +03:00
800082e4b3 added mesh 2026-02-19 06:35:08 +03:00
02d909f77d added mesh trait 2026-02-19 01:06:08 +03:00
49319a1c7c added more files 2026-02-19 01:05:09 +03:00
d0f3e5059a added formulas 2026-02-19 01:05:09 +03:00
c9874f30d0 add 2026-02-19 01:05:09 +03:00
3080faeaa9 hotfix: projectile prediction 2026-02-19 01:01:38 +03:00
af36c909a4 improved simple game 2026-02-18 07:38:04 +03:00
1041256fac Revert "removed useless include"
This reverts commit 88012e2524.
2026-02-18 06:07:27 +03:00
88012e2524 removed useless include 2026-02-18 05:53:16 +03:00
4561dbe0e7 fix 2026-02-18 05:51:46 +03:00
b50f759675 more template stuff 2026-02-18 05:51:18 +03:00
85b672fdf3 Merge pull request #152 from orange-cpp/feature/camera_improvement
added more methods
2026-02-17 07:57:35 +03:00
360734f252 added more methods 2026-02-17 07:39:45 +03:00
1e215f3ff5 added more lfs file extensions 2026-02-15 16:43:08 +03:00
ca5f8cd328 moved another file to lfs 2026-02-15 15:49:51 +03:00
5e40a09b2e Merge pull request #151 from orange-cpp/feature/lfs
update
2026-02-15 15:38:19 +03:00
87e291d3b9 update 2026-02-15 15:35:58 +03:00
af44c918a2 removed psd file 2026-02-15 15:24:10 +03:00
0774a27d20 added site doc folder to gitignore 2026-02-14 20:55:48 +03:00
e212f66b85 added vscode config 2026-02-12 23:13:37 +03:00
6d0109edbc fix 2026-02-08 20:57:10 +03:00
58f1045e88 Merge pull request #148 from orange-cpp/feature/engine-units-to-metric
Feature/engine units to metric
2026-02-08 03:28:54 +03:00
fbd2543644 added tests 2026-02-08 03:15:21 +03:00
815b42159a added frostbite tests 2026-02-08 03:03:23 +03:00
dfc564b30d added for other engines 2026-02-08 02:58:59 +03:00
58a592a806 improved naming 2026-02-08 02:51:48 +03:00
4c92dbe1ed added for source 2026-02-08 02:43:10 +03:00
0145e0455c Merge pull request #146 from orange-cpp/feature/line_tracer_template
improvement
2026-02-06 00:14:15 +03:00
e91a2d7853 fixed warnings 2026-02-06 00:02:00 +03:00
9dc9b2cca2 added noexcept 2026-02-05 23:45:41 +03:00
268eff0f3f simplified shit 2026-02-05 23:43:17 +03:00
092b23993b added constexpr 2026-02-05 23:38:51 +03:00
1c844fe488 removed uselss c++ file 2026-02-05 23:31:14 +03:00
6490bf13ed improvement 2026-02-05 23:27:31 +03:00
e328f58a8e Merge pull request #145 from orange-cpp/feature/macho_improvement
Feature/macho improvement
2026-02-04 19:27:44 +03:00
41d09c5699 fixed bug 2026-02-04 19:10:06 +03:00
d14e77883a removed nesting 2026-02-04 18:35:04 +03:00
ca39dc20c1 removed nesting 2026-02-04 18:33:05 +03:00
02810fb0f3 fixed codestyle 2026-02-04 18:30:45 +03:00
3aa1e23025 added resharper ignore segment 2026-02-04 18:29:55 +03:00
Copilot
53fdff64e2 Add Mach-O pattern scanner (#144)
* Initial plan

* Add Mach-O pattern scanner implementation and unit tests

Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>

* Add Mach-O pattern scanner

Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>

* Remove CodeQL build artifacts from PR

Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>
2026-02-04 17:30:20 +03:00
9a1a622dad Merge pull request #143 from luadebug/opengl
fix `pixi run examples` for linux
2026-01-30 16:00:50 +03:00
Saikari
4630bd6dcf do similiar for aarch64 linux 2026-01-30 05:13:27 +03:00
Saikari
7d25101344 add pixi.lock to gitignore 2026-01-30 05:06:14 +03:00
Saikari
e48532bcb9 fix pixi run examples for linux 2026-01-30 05:04:50 +03:00
Saikari
32b3113556 Add initial project configuration and build scripts for omath library
- Created pixi.toml for project metadata and dependencies management.
- Added formatting script (fmt.cmake) to ensure consistent CMake file formatting.
- Implemented benchmark execution script (run.benchmark.cmake) to run benchmark tests.
- Developed example execution script (run.examples.cmake) to run example applications.
- Created unit test execution script (run.unit.tests.cmake) to run unit tests.

removed lock file
2026-01-30 02:36:34 +03:00
7bff17e46c updated tag 2026-01-29 20:09:36 +03:00
e12c6b1824 removed unity build completely 2026-01-29 20:06:18 +03:00
50ceb54eaa Merge pull request #139 from orange-cpp/feature/mesh_line_tracer
Feature/mesh line tracer
2026-01-27 01:42:41 +03:00
734e680c2f fixed naming 2026-01-27 01:25:49 +03:00
2c2d014f7b mesh improvement 2026-01-27 01:25:07 +03:00
a27ea2eedf WIP on feature/rotation_improved 2026-01-27 01:07:37 +03:00
86943cb9f0 Merge pull request #138 from orange-cpp/feature/primitives_improvement
Feature/primitives improvement
2026-01-27 01:07:15 +03:00
2f725bab05 dropped uselss cpp files 2026-01-27 00:51:17 +03:00
dc39f4fdc6 fixed naming 2026-01-27 00:39:34 +03:00
8f428a34bd added primitives to other engine triplets 2026-01-27 00:37:23 +03:00
9a61ce1984 added opengl primitives 2026-01-27 00:33:35 +03:00
3b8a5c09fa fix 2026-01-26 20:59:19 +03:00
108614a3a1 fix 2026-01-26 19:01:39 +03:00
a15e197d99 fix 2026-01-26 18:59:34 +03:00
f9261cb553 ppatch 2026-01-26 17:21:23 +03:00
6631819c75 added stuff 2026-01-26 17:07:38 +03:00
701687c38b added files 2026-01-26 15:44:46 +03:00
2753dfaa30 Merge pull request #137 from luadebug/xm
xmake Add tests
2026-01-22 12:57:19 +03:00
Saikari
ccc5fca804 fixup 2026-01-22 08:53:23 +03:00
Saikari
c77000897f Add tests 2026-01-22 08:52:18 +03:00
ca9faeacec Merge pull request #136 from luadebug/xmake
Add build configuration for omath and examples
2026-01-20 01:27:48 +03:00
Saikari
a9ef405c67 Add rules for CMake and pkg-config imports 2026-01-19 04:45:41 +03:00
Saikari
38dba9f3f7 Add build configuration for omath and examples 2026-01-19 04:43:21 +03:00
2fcd3c60e4 Update copyright year in LICENSE file 2026-01-17 21:35:34 +03:00
8ecda78e79 Merge pull request #135 from orange-cpp/feature/crypto_var
Feature/crypto var
2026-01-04 23:41:24 +03:00
ff4d20a4db added final 2026-01-04 23:24:07 +03:00
ce01671596 improvement 2026-01-04 23:22:25 +03:00
b11385c4e1 added cmake option 2026-01-04 23:02:58 +03:00
03d06d669b forgot to remove 2026-01-04 22:53:57 +03:00
cdf2c29013 removed 128 bit int 2026-01-04 22:46:09 +03:00
5209e85c67 improvement 2026-01-04 22:41:28 +03:00
1a9741fd6f fixed test 2026-01-04 22:23:57 +03:00
f172fe5837 silenced warn 2026-01-04 22:16:40 +03:00
cf71e8f4e5 improved stuff 2026-01-04 22:16:40 +03:00
90e6b4eaaa fix 2026-01-04 22:16:40 +03:00
056e715f04 added check 2026-01-04 22:16:40 +03:00
25690053d1 changed algorithm 2026-01-04 22:16:40 +03:00
84bdcc9dc7 added stuff 2026-01-04 22:16:40 +03:00
6c5cc816cc unlikely added 2026-01-04 03:27:46 +03:00
ea459a40da added unlikely 2026-01-03 08:57:56 +03:00
239f901c43 i dont like icons now 2026-01-01 03:59:12 +03:00
eed788f9f4 Feature/elf pattern scan (#133)
* added some code

* improvement

* visit

* added scanner code

* update

* fixed naming

* added const

* added type casting

* added file

* patch

* added unlikely

* added in module scanner

* fixing test

* fix

* remove

* improvement

* fix

* Update source/utility/elf_pattern_scan.cpp

Co-authored-by: Saikari <lin@sz.cn.eu.org>

* rev

* fix

* patch

* decomposed method

* fix

* fix

* improvement

* fix

* fix

* commented stuff

---------

Co-authored-by: Saikari <lin@sz.cn.eu.org>
2026-01-01 03:37:55 +03:00
03f069062a fixed tests 2025-12-30 08:40:42 +03:00
Saikari
241492c007 Use LLVM coverage and LCOV genhtml on Windows OS (#131) 2025-12-29 21:11:13 +03:00
Saikari
08d7a74e43 Use LLVM coverage and LCOV genhtml on Windows OS (#129) 2025-12-28 02:44:00 +03:00
67cee33236 improved code style 2025-12-28 02:32:35 +03:00
aaf1e026a7 improved code style 2025-12-28 02:29:19 +03:00
14bdfac01a Update license badge in README
Replaced the static badge with a dynamic GitHub license badge.
2025-12-27 20:07:45 +03:00
118427678a returned back to zlib 2025-12-27 20:06:17 +03:00
05da7b3dde umped version 2025-12-27 19:43:56 +03:00
f0f369a9ab warning fix 2025-12-25 02:35:50 +03:00
00aa706469 Implement coverage for Windows/Linux/MacOS (#126) (#128)
* Implement coverage for Windows/Linux/MacOS (#126)

* reverted

---------

Co-authored-by: Saikari <lin@sz.cn.eu.org>
2025-12-25 02:28:17 +03:00
c6225fec53 Add GitHub repo size badge to README 2025-12-24 02:38:26 +03:00
3952696199 Feature/more constexpr (#125)
* added constexpr

* fix

* improved stuff

* added const

* improvement

* fix

* fix

* patch
2025-12-24 02:32:14 +03:00
d620d729df Temp (#123)
* Coverage

* added fixes

* removed spacing

* removed junk

* removed print

* removed coverage

* removed useless stuff

* fix

---------

Co-authored-by: Saikari <lin@sz.cn.eu.org>
2025-12-23 02:47:12 +03:00
005f80a69c Merge pull request #121 from luadebug/try
Run unit tests for WASM
2025-12-22 00:34:28 +03:00
Saikari
103a4a62d7 try 2025-12-21 22:57:38 +03:00
0017655185 Merge pull request #120 from luadebug/android
Rework CI
2025-12-21 18:18:02 +03:00
Saikari
bb4e480602 Test build NDK 2025-12-21 18:03:25 +03:00
9ca0868a19 Merge pull request #118 from orange-cpp/develop
Develop
2025-12-20 00:46:42 +03:00
8cc9b9d774 specified specific vcpkg version 2025-12-20 00:37:14 +03:00
f523b35868 removed extra check 2025-12-19 23:59:25 +03:00
94f4ca0f7b replaced numeric limits 2025-12-19 23:49:54 +03:00
fa923102b8 changed epsilon 2025-12-19 23:43:03 +03:00
3bc69dfe26 changed epsilon to numeric limmits 2025-12-19 23:34:10 +03:00
7163167595 Merge pull request #117 from luadebug/mingw
Add MinGW support
2025-12-19 23:21:31 +03:00
a6d453f112 removed redundant copy 2025-12-19 23:19:47 +03:00
Saikari
a17e3af0ab Add MinGW support 2025-12-18 01:59:11 +03:00
0358e431f2 Merge pull request #116 from luadebug/wasm
Add WASM support
2025-12-17 14:02:58 +03:00
Saikari
8edd20dc03 Add WASM support 2025-12-17 13:38:14 +03:00
2b038c29b1 Merge pull request #115 from luadebug/iphone
Add iOS support
2025-12-16 19:16:52 +03:00
Saikari
5758b3bbe5 Add iOS support 2025-12-16 14:48:20 +03:00
1ee802dc68 Merge pull request #114 from luadebug/ndk
Add NDK support
2025-12-15 07:08:56 +03:00
Saikari
5fb280393b Add NDK support 2025-12-15 00:04:11 +03:00
dd4e59b04d Merge pull request #113 from luadebug/freebsd
Add FreeBSD support
2025-12-14 22:57:07 +03:00
Saikari
6fe8a2eec8 Add FreeBSD support 2025-12-14 22:48:29 +03:00
7e27ed1dd8 Merge pull request #112 from orange-cpp/feature/test
fix
2025-12-14 12:31:45 +03:00
f2f7c269bc fix 2025-12-14 12:20:05 +03:00
712a0a7045 Merge pull request #111 from orange-cpp/feature/frustum_culling_method
added check method
2025-12-14 11:15:17 +03:00
cac97eb23d removed nesting 2025-12-14 11:08:06 +03:00
ec6808c8f2 added const 2025-12-14 11:06:15 +03:00
37ce7f41e5 removed double size reserve 2025-12-14 11:05:24 +03:00
9c9546d3e0 improved readability 2025-12-14 10:59:38 +03:00
bc593ae3d0 extracted to methdod 2025-12-14 10:42:23 +03:00
d5217873a1 added nodiscard 2025-12-14 10:39:23 +03:00
5df50b6310 decomposed into method 2025-12-14 10:26:36 +03:00
c7eb9e0074 removed useless comment 2025-12-14 10:18:58 +03:00
f7cb3a9de2 style fix 2025-12-14 09:53:12 +03:00
35bea50e36 renamed stuff 2025-12-14 09:50:46 +03:00
c1de0b4a05 updated preset file 2025-12-13 23:34:15 +03:00
3b4a015783 improved pmr stuff 2025-12-13 00:06:45 +03:00
381edef9ea added missing include 2025-12-12 18:34:56 +03:00
7cd49fbca1 added check method 2025-12-09 10:21:54 +03:00
90dcf2fef8 added extra test 2025-12-09 10:16:57 +03:00
39ccf471b1 reverted is out of bounds check + added extra test 2025-12-09 10:06:25 +03:00
006911914c returned stuff back 2025-12-09 09:31:22 +03:00
21c1b2fbdb hot fix 2025-12-09 09:26:55 +03:00
731fdc6a55 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
c215a0a265 fixed bug due to on screen ndc check 2025-12-09 07:53:52 +03:00
0ff02e8317 Merge pull request #109 from orange-cpp/feature/collider_interface
Feature/collider interface
2025-12-06 14:59:34 +03:00
56ae33bc5f fixed naming 2025-12-06 14:49:30 +03:00
ccfa9461c9 removed file 2025-12-06 14:34:20 +03:00
c70eeab9c6 update 2025-12-06 14:30:44 +03:00
39ed4e50d2 fix 2025-12-06 13:56:25 +03:00
70c64f765b added collider interface 2025-12-06 13:56:25 +03:00
67ccaedb33 Merge pull request #108 from orange-cpp/fearure/epa_pmr
Fearure/epa pmr
2025-12-04 08:41:55 +03:00
59bbf31820 fix 2025-12-04 05:08:01 +03:00
6b54c790fc patch 2025-12-04 05:02:54 +03:00
5dff27ac8d fix 2025-12-04 05:01:52 +03:00
098d8af7b6 replaced with bool 2025-12-04 04:52:22 +03:00
e4e40c13d8 oops 2025-12-04 04:47:34 +03:00
40f57c5ff8 added nodiscard + static 2025-12-04 04:46:00 +03:00
eab040ab77 fixed typo 2025-12-04 04:45:15 +03:00
343f98490f tweak 2025-12-04 04:38:44 +03:00
46c65157fe replaced with emplace 2025-12-03 14:11:29 +03:00
e3f4de9197 back to static 2025-12-03 13:34:35 +03:00
d786eda33a switched to shared_ptr 2025-12-03 13:30:05 +03:00
57398efb31 added poly allocators 2025-12-03 09:52:53 +03:00
3471b44f9b fixed uv type 2025-12-01 05:03:43 +03:00
9a22cda382 added mesh culling 2025-11-30 06:19:38 +03:00
bc902a37f1 Auto stash before merge of "main" and "origin/main" 2025-11-30 01:32:25 +03:00
c22644d09f cleaned up code 2025-11-29 22:00:07 +03:00
c1d96509f1 updated read me 2025-11-29 21:45:09 +03:00
dd73157d1f Merge pull request #107 from orange-cpp/feature/example_improvement
fixed somebugs, improved tests
2025-11-29 21:41:43 +03:00
50c4822275 fixed rotation 2025-11-29 21:34:40 +03:00
89d2e5417f added rotation by pitch 2025-11-29 21:31:20 +03:00
8ff358df82 fixed somebugs, improved tests 2025-11-29 21:24:45 +03:00
24e51f1be3 Merge pull request #106 from orange-cpp/feature/mesh_upgrade
Feature/mesh upgrade
2025-11-29 16:55:31 +03:00
2e282d2732 changed ebo type 2025-11-29 16:49:19 +03:00
c5ec098ade fix 2025-11-29 16:37:30 +03:00
6edfb93d09 refactored using stuff 2025-11-29 16:35:43 +03:00
2d874f50a5 added conecpt to method 2025-11-29 16:33:31 +03:00
5a3f4cc330 added template arg to Vertex struct 2025-11-29 16:31:24 +03:00
09263b390f improved mesh class 2025-11-29 16:28:06 +03:00
5a8b033816 removed newline 2025-11-14 18:20:25 +03:00
485f2e1e39 Update version to 4.4.0 2025-11-14 14:35:58 +03:00
7b6aa474c1 Bump version from 4.2.0 to 4.4.0 2025-11-14 12:12:23 +03:00
87da07319e Revise Code of Conduct with comprehensive updates
Updated the Code of Conduct to include a universal declaration, preamble, and detailed articles outlining community rights, responsibilities, and enforcement strategies.
2025-11-14 00:30:55 +03:00
61eb879e99 Updates theme with custom link styles
Adds custom CSS to style links and the navbar,
improving the visual appearance of the documentation.

Disables unnecessary code inspection warnings to reduce clutter.
2025-11-13 23:47:17 +03:00
dc3a6f6749 Merge pull request #105 from orange-cpp/feature/epa_improvement
Feature/epa improvement
2025-11-13 22:13:26 +03:00
4c62e18042 added std::optional 2025-11-13 21:25:45 +03:00
f41a4596fd Calculates penetration vector for EPA algorithm
Adds `penetration_vector` to the `epa_result` struct to represent the direction and magnitude of penetration.

This allows for more accurate collision response calculations and simplifies access to penetration information.

Updates both the early-exit and iterative EPA calculations within `epa_algorithm.hpp` to compute and store the penetration vector, factoring in the relative origin of the colliding meshes.
2025-11-13 21:19:09 +03:00
076d5f95d1 Merge pull request #104 from orange-cpp/copilot/update-docs-new-classes
Add documentation for GJK/EPA collision detection and mesh primitives
2025-11-13 19:00:11 +03:00
copilot-swe-agent[bot]
0be9e57309 Update API overview with new collision detection and mesh classes
Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>
2025-11-13 15:21:37 +00:00
copilot-swe-agent[bot]
d9634d1580 Add documentation for collision detection and mesh classes
Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>
2025-11-13 15:19:36 +00:00
copilot-swe-agent[bot]
04a4217bc5 Initial plan 2025-11-13 15:10:10 +00:00
f5889ac20b Merge pull request #103 from orange-cpp/feature/epa_algorithm
Feature/epa algorithm
2025-11-13 18:08:18 +03:00
788fdff7ef made ref 2025-11-13 17:34:39 +03:00
9c383050cd add another test 2025-11-13 17:22:54 +03:00
c28cb1a748 fixed formating 2025-11-13 16:56:48 +03:00
bb174e1137 Refactors collision detection with GJK and EPA
This commit refactors the collision detection pipeline to utilize a more standard GJK algorithm and simplifies the EPA implementation.

Removes the custom `GjkAlgorithmWithSimplex` in favor of the standalone `GjkAlgorithm`. This streamlines the collision detection process and enhances code clarity.

Updates unit tests to align with the new GJK implementation, ensuring continued functionality and correctness.
2025-11-13 16:39:26 +03:00
d784602904 Improves EPA test coverage
Adds a comprehensive unit test for the EPA algorithm,
validating collision detection, depth calculation, and
separation axis determination with a cube overlap scenario.

Refactors the test for clarity and robustness.
2025-11-13 16:34:14 +03:00
8d4f76e84d Refactors vertex parameter name
Updates the `make_face` function to use `vertexes` instead of `verts` for clarity and consistency in naming conventions.
2025-11-13 16:16:23 +03:00
c274bd28c9 Refactor EPA algorithm for clarity
Improves code organization and readability within the EPA algorithm implementation.

Changes include renaming variables for better semantic meaning (e.g., `verts` to `vertexes`), adding `final` specifiers to structs for clarity, and enhancing function signatures with `[[nodiscard]]` where appropriate.

These refactorings aim to enhance maintainability and understanding of the code without altering its core functionality.
2025-11-13 16:15:45 +03:00
7efe1e54d4 Refactors face initialization
Replaces `push_back` with `emplace_back` when initializing faces in the EPA algorithm. This avoids unnecessary copying and improves performance during face creation.
2025-11-13 16:07:57 +03:00
3b80805a7e Refactors EPA algorithm loop
Replaces the `for(;;)` loop in the EPA algorithm with a `while(true)` loop for improved readability and clarity.

This change enhances the maintainability of the code without altering its functionality.
2025-11-13 16:06:18 +03:00
2eb609d83f fixed formating 2025-11-13 16:01:42 +03:00
14f007047a added epa 2025-11-13 15:57:38 +03:00
d8d585e3eb Refactors triangle calculations for generic vectors
Updates the `Triangle` class to use a generic `Vector` type instead of `Vector3`, enhancing flexibility and reusability.

Changes include updating relevant function return types to use `Vector::ContainedType` and adapting length and distance calculations accordingly.

This refactoring supports the ongoing work on the EPA algorithm (feature/epa_algorithm) by providing a more adaptable foundation for geometric calculations.
2025-11-13 15:29:10 +03:00
ce1142dac9 Adds GjkHitInfo and refactors collision detection
Introduces `GjkHitInfo` to encapsulate collision results, including the simplex.

Refactors `is_collide` to return the `GjkHitInfo` struct, providing more detailed collision data. This prepares the codebase for integrating the EPA algorithm (as per the feature branch) which requires simplex information.
2025-11-13 13:33:18 +03:00
12d11f3e8c Refactors GJK algorithm vertex type
Simplifies the GJK algorithm by using a type alias for the vertex type, improving code readability and reducing redundancy. Removes unnecessary includes.
2025-11-11 23:37:56 +03:00
f5b083a0da Adds nodiscard attribute to simplex functions
Applies the `[[nodiscard]]` attribute to several functions within the `omath::collision` namespace to improve code safety and signal potential misuse when return values are ignored. This encourages developers to handle the results of these functions appropriately.
2025-11-11 23:36:19 +03:00
58c80d7ec4 Refactors perpendicular vector selection
Simplifies logic for finding a non-zero perpendicular vector by iterating through candidate directions. This improves readability and maintainability.
2025-11-11 23:34:30 +03:00
176 changed files with 14400 additions and 924 deletions

25
.cmake-format Normal file
View File

@@ -0,0 +1,25 @@
format:
line_width: 100
tab_size: 4
use_tabchars: false
max_subgroups_hwrap: 3
max_pargs_hwrap: 5
separate_ctrl_name_with_space: false
separate_fn_name_with_space: false
dangle_parens: false
dangle_align: child
line_ending: unix
command_case: canonical
keyword_case: upper
enable_sort: true
autosort: true
markup:
bullet_char: "*"
enum_char: .
enable_markup: false
additional_commands:
target_link_libraries:
kwargs:
PUBLIC: "*"
SHARED: "*"
PRIVATE: "*"

95
.gitattributes vendored
View File

@@ -1,5 +1,98 @@
# SCM syntax highlighting & preventing 3-way merges
pixi.lock merge=binary linguist-language=YAML linguist-generated=true -diff
# Images
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
*.tif filter=lfs diff=lfs merge=lfs -text
*.tiff filter=lfs diff=lfs merge=lfs -text
*.bmp filter=lfs diff=lfs merge=lfs -text
*.heic filter=lfs diff=lfs merge=lfs -text
*.heif filter=lfs diff=lfs merge=lfs -text
*.avif filter=lfs diff=lfs merge=lfs -text
*.ico filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.ai filter=lfs diff=lfs merge=lfs -text
*.xcf filter=lfs diff=lfs merge=lfs -text
*.kra filter=lfs diff=lfs merge=lfs -text
*.sketch filter=lfs diff=lfs merge=lfs -text
# Video
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.mov filter=lfs diff=lfs merge=lfs -text
*.m4v filter=lfs diff=lfs merge=lfs -text
*.mkv filter=lfs diff=lfs merge=lfs -text
*.avi filter=lfs diff=lfs merge=lfs -text
*.wmv filter=lfs diff=lfs merge=lfs -text
*.flv filter=lfs diff=lfs merge=lfs -text
*.webm filter=lfs diff=lfs merge=lfs -text
*.mpeg filter=lfs diff=lfs merge=lfs -text
*.mpg filter=lfs diff=lfs merge=lfs -text
# Audio
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.flac filter=lfs diff=lfs merge=lfs -text
*.aac filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.m4a filter=lfs diff=lfs merge=lfs -text
# 3D / CAD / DCC
*.blend filter=lfs diff=lfs merge=lfs -text
*.fbx filter=lfs diff=lfs merge=lfs -text
*.obj filter=lfs diff=lfs merge=lfs -text
*.stl filter=lfs diff=lfs merge=lfs -text
*.glb filter=lfs diff=lfs merge=lfs -text
*.gltf filter=lfs diff=lfs merge=lfs -text
*.usd filter=lfs diff=lfs merge=lfs -text
*.usdz filter=lfs diff=lfs merge=lfs -text
*.max filter=lfs diff=lfs merge=lfs -text
*.c4d filter=lfs diff=lfs merge=lfs -text
*.dwg filter=lfs diff=lfs merge=lfs -text
*.dxf filter=lfs diff=lfs merge=lfs -text
*.step filter=lfs diff=lfs merge=lfs -text
*.stp filter=lfs diff=lfs merge=lfs -text
*.iges filter=lfs diff=lfs merge=lfs -text
*.igs filter=lfs diff=lfs merge=lfs -text
# Documents / publishing binaries
*.pdf filter=lfs diff=lfs merge=lfs -text
*.indd filter=lfs diff=lfs merge=lfs -text
*.idml filter=lfs diff=lfs merge=lfs -text
*.ppt filter=lfs diff=lfs merge=lfs -text
*.pptx filter=lfs diff=lfs merge=lfs -text
*.key filter=lfs diff=lfs merge=lfs -text
*.doc filter=lfs diff=lfs merge=lfs -text
*.docx filter=lfs diff=lfs merge=lfs -text
# Archives / packaged artifacts
*.zip filter=lfs diff=lfs merge=lfs -text
*.7z filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
# Fonts
*.ttf filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text
*.woff filter=lfs diff=lfs merge=lfs -text
*.woff2 filter=lfs diff=lfs merge=lfs -text
# ML / data artifacts
*.onnx filter=lfs diff=lfs merge=lfs -text
*.pt filter=lfs diff=lfs merge=lfs -text
*.pth filter=lfs diff=lfs merge=lfs -text
*.ckpt filter=lfs diff=lfs merge=lfs -text
*.pb filter=lfs diff=lfs merge=lfs -text
*.tflite filter=lfs diff=lfs merge=lfs -text
*.h5 filter=lfs diff=lfs merge=lfs -text
*.hdf5 filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.feather filter=lfs diff=lfs merge=lfs -text
*.arrow filter=lfs diff=lfs merge=lfs -text
*.npy filter=lfs diff=lfs merge=lfs -text
*.npz filter=lfs diff=lfs merge=lfs -text
*.pickle filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text

4
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
# These are supported funding model platforms
open_collective: libomathorg
github: orange-cpp

BIN
.github/psd/omath.psd LFS vendored

Binary file not shown.

View File

@@ -1,4 +1,4 @@
name: Omath CI (Arch Linux / Windows)
name: Omath CI
on:
push:
@@ -10,24 +10,82 @@ concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
##############################################################################
# 1) ARCH LINUX Clang / Ninja
# 1) Linux Clang / Ninja
##############################################################################
jobs:
arch-build-and-test:
name: Arch Linux (Clang)
runs-on: ubuntu-latest
container: archlinux:latest
linux-build-and-test:
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- name: Linux (Clang) (x64-linux)
triplet: x64-linux
runner: ubuntu-latest
preset: linux-release-vcpkg
coverage: true
install_cmd: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main"
sudo apt-get update
sudo apt-get install -y git build-essential cmake ninja-build \
zip unzip curl pkg-config ca-certificates \
clang-21 lld-21 libc++-21-dev libc++abi-21-dev \
llvm-21
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-21 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-21 100
sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-21 100
- name: Linux (Clang) (x86-linux)
triplet: x86-linux
runner: ubuntu-latest
preset: linux-release-vcpkg-x86
coverage: false
install_cmd: |
# Add LLVM 21 repository
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main"
# Add GCC Toolchain PPA
sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu plucky main universe"
# Enable i386 architecture
sudo dpkg --add-architecture i386
sudo apt-get update
# Install Clang 21
sudo apt-get install -y git build-essential cmake ninja-build \
zip unzip curl pkg-config ca-certificates \
clang-21 lld-21 libc++-21-dev libc++abi-21-dev
sudo apt-get install -y -t plucky binutils
# Install GCC 15 with multilib support
sudo apt-get install -y gcc-15-multilib g++-15-multilib
# Set up alternatives for Clang
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-21 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-21 100
sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-21 100
# Set up alternatives for GCC
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-15 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-15 100
- name: Linux (Clang) (arm64-linux)
triplet: arm64-linux
runner: ubuntu-24.04-arm
preset: linux-release-vcpkg-arm64
coverage: false
install_cmd: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main"
sudo apt-get update
sudo apt-get install -y git build-essential cmake ninja-build \
zip unzip curl pkg-config ca-certificates \
clang-21 lld-21 libc++-21-dev libc++abi-21-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-21 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-21 100
sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-21 100
fail-fast: false
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
- name: Install basic tool-chain with pacman
- name: Install basic tool-chain
shell: bash
run: |
pacman -Sy --noconfirm archlinux-keyring
pacman -Syu --noconfirm --needed \
git base-devel clang cmake ninja zip unzip fmt
run: ${{ matrix.install_cmd }}
- name: Checkout repository (with sub-modules)
uses: actions/checkout@v4
@@ -38,29 +96,82 @@ jobs:
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 linux-release-vcpkg -DOMATH_BUILD_TESTS=ON -DOMATH_BUILD_BENCHMARK=OFF -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
run: |
cmake --preset ${{ matrix.preset }} \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \
-DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
- name: Build
shell: bash
run: cmake --build cmake-build/build/linux-release-vcpkg --target unit_tests omath
run: cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
- name: Run unit_tests
shell: bash
run: ./out/Release/unit_tests
- name: Run Coverage
if: ${{ matrix.coverage == true }}
shell: bash
run: |
sudo apt-get install lcov
chmod +x scripts/coverage-llvm.sh
./scripts/coverage-llvm.sh \
"${{ github.workspace }}" \
"cmake-build/build/${{ matrix.preset }}" \
"./out/Release/unit_tests" \
"cmake-build/build/${{ matrix.preset }}/coverage"
- name: Upload Coverage Report
if: ${{ matrix.coverage == true }}
uses: actions/upload-artifact@v4
with:
name: coverage-report-linux
path: cmake-build/build/${{ matrix.preset }}/coverage/
- name: Upload logs on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: linux-build-logs-${{ matrix.triplet }}
path: |
cmake-build/build/${{ matrix.preset }}/**/*.log
${{ env.VCPKG_ROOT }}/buildtrees/**/*.log
##############################################################################
# 2) Windows MSVC / Ninja
##############################################################################
windows-build-and-test:
name: Windows (MSVC)
runs-on: windows-latest
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- name: Windows (MSVC) (x64-windows)
runner: windows-latest
arch: amd64
preset: windows-release-vcpkg
triplet: x64-windows
- name: Windows (MSVC) (x86-windows)
runner: windows-latest
arch: amd64_x86
preset: windows-release-vcpkg-x86
triplet: x86-windows
- name: Windows (MSVC) (arm64-windows)
runner: windows-11-arm
arch: arm64
preset: windows-release-vcpkg-arm64
triplet: arm64-windows
fail-fast: false
env:
OMATH_BUILD_VIA_VCPKG: ON
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
- name: Checkout repository (with sub-modules)
uses: actions/checkout@v4
@@ -72,25 +183,171 @@ jobs:
- name: Set up MSVC developer command-prompt
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.arch }}
- name: Configure (cmake --preset)
shell: bash
run: cmake --preset windows-release-vcpkg -DOMATH_BUILD_TESTS=ON -DOMATH_BUILD_BENCHMARK=OFF -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
run: |
cmake --preset ${{ matrix.preset }} \
-DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \
-DOMATH_ENABLE_COVERAGE=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
- name: Build
shell: bash
run: cmake --build cmake-build/build/windows-release-vcpkg --target unit_tests omath
run: cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
- name: Run unit_tests.exe
shell: bash
run: ./out/Release/unit_tests.exe
##########################################################################
# Coverage (x64-windows only)
##########################################################################
- name: Install LLVM
if: ${{ matrix.triplet == 'x64-windows' }}
run: |
choco install llvm -y
- name: Clean Build Directory for Coverage
if: ${{ matrix.triplet == 'x64-windows' }}
shell: pwsh
run: |
$buildDir = "cmake-build/build/${{ matrix.preset }}"
if (Test-Path $buildDir) {
Write-Host "Cleaning build directory to prevent compiler conflict: $buildDir"
Remove-Item -Path $buildDir -Recurse -Force
}
- name: Build Debug for Coverage
if: ${{ matrix.triplet == 'x64-windows' }}
shell: bash
run: |
cmake --preset ${{ matrix.preset }} \
-DCMAKE_C_COMPILER="C:/Program Files/LLVM/bin/clang-cl.exe" \
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang-cl.exe" \
-DCMAKE_LINKER="C:/Program Files/LLVM/bin/lld-link.exe" \
-DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \
-DOMATH_ENABLE_COVERAGE=ON \
-DOMATH_THREAT_WARNING_AS_ERROR=OFF \
-DCMAKE_BUILD_TYPE=Debug \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
cmake --build cmake-build/build/${{ matrix.preset }} --config Debug --target unit_tests omath
- name: Run Tests (Generates .profraw)
if: ${{ matrix.triplet == 'x64-windows' }}
shell: pwsh
env:
LLVM_PROFILE_FILE: "cmake-build/build/${{ matrix.preset }}/unit_tests.profraw"
run: |
./out/Debug/unit_tests.exe
- name: Process Coverage (llvm-profdata & llvm-cov)
if: ${{ matrix.triplet == 'x64-windows' }}
shell: pwsh
run: |
$BUILD_DIR = "cmake-build/build/${{ matrix.preset }}"
$EXE_PATH = "./out/Debug/unit_tests.exe"
# 1. Merge raw profile data (essential step)
& "C:/Program Files/LLVM/bin/llvm-profdata.exe" merge `
-sparse "$BUILD_DIR/unit_tests.profraw" `
-o "$BUILD_DIR/unit_tests.profdata"
# 2. Export to LCOV format
# NOTE: We explicitly ignore vcpkg_installed and system headers
& "C:/Program Files/LLVM/bin/llvm-cov.exe" export "$EXE_PATH" `
-instr-profile="$BUILD_DIR/unit_tests.profdata" `
-format=lcov `
-ignore-filename-regex="vcpkg_installed|external|tests" `
> "$BUILD_DIR/lcov.info"
if (Test-Path "$BUILD_DIR/lcov.info") {
Write-Host "✅ LCOV info created at $BUILD_DIR/lcov.info"
} else {
Write-Error "Failed to create LCOV info"
exit 1
}
- name: Install LCOV (for genhtml)
if: ${{ matrix.triplet == 'x64-windows' }}
run: choco install lcov -y
- name: Generate HTML Report
if: ${{ matrix.triplet == 'x64-windows' }}
shell: bash
run: |
BUILD_DIR="cmake-build/build/${{ matrix.preset }}"
LCOV_INFO="${BUILD_DIR}/lcov.info"
HTML_DIR="${BUILD_DIR}/coverage-html"
# Fix paths for genhtml (Perl hates backslashes)
sed -i 's|\\|/|g' "${LCOV_INFO}"
# Locate genhtml provided by 'choco install lcov'
# It is typically in ProgramData/chocolatey/lib/lcov/tools/bin
GENHTML=$(find /c/ProgramData/chocolatey -name genhtml -print -quit)
if [ -z "$GENHTML" ]; then
echo "Error: genhtml executable not found"
exit 1
fi
echo "Using genhtml: $GENHTML"
mkdir -p "$HTML_DIR"
# Run genhtml
# Added --demangle-cpp if your version supports it, otherwise remove it
perl "$GENHTML" \
"${LCOV_INFO}" \
--output-directory "$HTML_DIR" \
--title "OMath Coverage Report" \
--legend \
--show-details \
--branch-coverage \
--ignore-errors source
echo "✅ LCOV HTML report generated at $HTML_DIR"
- name: Upload Coverage (HTML Report)
if: ${{ matrix.triplet == 'x64-windows' }}
uses: actions/upload-artifact@v4
with:
name: coverage-html-windows
path: cmake-build/build/${{ matrix.preset }}/coverage-html/
- name: Upload logs on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: windows-build-logs-${{ matrix.triplet }}
path: |
cmake-build/build/${{ matrix.preset }}/**/*.log
${{ env.VCPKG_ROOT }}/buildtrees/**/*.log
##############################################################################
# 3) macOS AppleClang / Ninja
##############################################################################
macosx-build-and-test:
name: macOS (AppleClang)
runs-on: macOS-latest
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- name: macOS (AppleClang) (arm64-osx)
runner: macos-latest
preset: darwin-release-vcpkg
triplet: arm64-osx
coverage: true
- name: macOS (AppleClang) (x64-osx)
runner: macos-15-intel
preset: darwin-release-vcpkg-x64
triplet: x64-osx
coverage: false
fail-fast: false
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
@@ -111,12 +368,446 @@ jobs:
- name: Configure (cmake --preset)
shell: bash
run: cmake --preset darwin-release-vcpkg -DOMATH_BUILD_TESTS=ON -DOMATH_BUILD_BENCHMARK=OFF -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
run: |
cmake --preset ${{ matrix.preset }} \
-DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \
-DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
- name: Build
shell: bash
run: cmake --build cmake-build/build/darwin-release-vcpkg --target unit_tests omath
run: cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
- name: Run unit_tests
shell: bash
run: ./out/Release/unit_tests
- name: Run Coverage
if: ${{ matrix.coverage == true }}
shell: bash
run: |
brew install lcov
chmod +x scripts/coverage-llvm.sh
./scripts/coverage-llvm.sh \
"${{ github.workspace }}" \
"cmake-build/build/${{ matrix.preset }}" \
"./out/Release/unit_tests" \
"cmake-build/build/${{ matrix.preset }}/coverage"
- name: Upload Coverage Report
if: ${{ matrix.coverage == true }}
uses: actions/upload-artifact@v4
with:
name: coverage-report-macos
path: cmake-build/build/${{ matrix.preset }}/coverage/
- name: Upload logs on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: macos-build-logs-${{ matrix.triplet }}
path: |
cmake-build/build/${{ matrix.preset }}/**/*.log
${{ env.VCPKG_ROOT }}/buildtrees/**/*.log
##############################################################################
# 4) iOS AppleClang / Xcode / arm64-ios
##############################################################################
ios-build:
name: iOS (AppleClang) (${{ matrix.triplet }})
runs-on: macOS-latest
strategy:
matrix:
include:
- triplet: arm64-ios
preset: ios-release-vcpkg
fail-fast: false
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 ${{ matrix.preset }} \
-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/${{ matrix.preset }} --config Release --target unit_tests omath
- name: Upload logs on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: ios-build-logs-${{ matrix.triplet }}
path: |
cmake-build/build/${{ matrix.preset }}/**/*.log
${{ env.VCPKG_ROOT }}/buildtrees/**/*.log
##############################################################################
# 5) FreeBSD Clang / Ninja
##############################################################################
freebsd-build-and-test:
name: FreeBSD (Clang) (${{ matrix.triplet }})
runs-on: ubuntu-latest
strategy:
matrix:
include:
- triplet: x64-freebsd
preset: freebsd-release-vcpkg
arch: x86-64
fail-fast: false
env:
VCPKG_ROOT: ${{ github.workspace }}/tmp/vcpkg
steps:
- name: Checkout repository (with sub-modules)
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build and Test
uses: cross-platform-actions/action@v0.31.0
with:
operating_system: freebsd
architecture: ${{ matrix.arch }}
version: '15.0'
memory: '12G'
cpu_count: 4
run: |
sudo pkg install -y git curl zip unzip gmake llvm gsed bash perl5 openssl 7-zip coreutils cmake ninja pkgconf patchelf
git config --global --add safe.directory `pwd`
# Build vcpkg in /tmp to avoid sshfs timestamp sync issues
export VCPKG_ROOT=/tmp/vcpkg
rm -rf "$VCPKG_ROOT"
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
cd "$VCPKG_ROOT"
./bootstrap-vcpkg.sh
cd -
export VCPKG_FORCE_SYSTEM_BINARIES=0
cmake --preset ${{ matrix.preset }} \
-DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests" \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported"
cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
./out/Release/unit_tests
- name: Upload logs on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: freebsd-build-logs-${{ matrix.triplet }}
path: |
cmake-build/build/${{ matrix.preset }}/**/*.log
${{ env.VCPKG_ROOT }}/buildtrees/**/*.log
##############################################################################
# 6) Android NDK Clang / Ninja
##############################################################################
android-build:
name: Android NDK (${{ matrix.triplet }})
runs-on: ubuntu-latest
strategy:
matrix:
include:
- triplet: arm-neon-android
preset: android-arm-neon-release-vcpkg
- triplet: arm64-android
preset: android-arm64-release-vcpkg
- triplet: x64-android
preset: android-x64-release-vcpkg
- triplet: x86-android
preset: android-x86-release-vcpkg
fail-fast: false
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="r28b"
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
- 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 ${{ matrix.preset }} \
-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/${{ matrix.preset }} --target unit_tests omath
- name: Upload logs on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: android-build-logs-${{ matrix.triplet }}
path: |
cmake-build/build/${{ matrix.preset }}/**/*.log
${{ env.VCPKG_ROOT }}/buildtrees/**/*.log
##############################################################################
# 7) WebAssembly (Emscripten) Clang / Ninja / wasm32-emscripten
##############################################################################
wasm-build-and-test:
name: WebAssembly (Emscripten) (${{ matrix.triplet }})
runs-on: ubuntu-latest
strategy:
matrix:
include:
- triplet: wasm32-emscripten
preset: wasm-release-vcpkg
fail-fast: false
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 ${{ matrix.preset }} \
-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/${{ matrix.preset }} --target unit_tests omath
- name: Upload logs on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: wasm-build-logs-${{ matrix.triplet }}
path: |
cmake-build/build/${{ matrix.preset }}/**/*.log
${{ env.VCPKG_ROOT }}/buildtrees/**/*.log
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Run WASM Unit Tests
run: node out/Release/unit_tests.js
##############################################################################
# 8) Windows MSYS2 MinGW GCC / Ninja
##############################################################################
mingw-build-and-test:
name: ${{ matrix.name }}
runs-on: windows-latest
strategy:
matrix:
include:
- name: MINGW64 (MSYS2) (x64-mingw-dynamic)
msystem: MINGW64
pkg_prefix: mingw-w64-x86_64
preset: mingw-release-vcpkg
- name: UCRT64 (MSYS2) (x64-mingw-dynamic)
msystem: UCRT64
pkg_prefix: mingw-w64-ucrt-x86_64
preset: mingw-release-vcpkg
- name: MINGW32 (MSYS2) (x86-mingw-dynamic)
msystem: MINGW32
pkg_prefix: mingw-w64-i686
preset: mingw32-release-vcpkg
fail-fast: false
defaults:
run:
shell: msys2 {0}
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
update: true
install: >-
${{ matrix.pkg_prefix }}-toolchain
${{ matrix.pkg_prefix }}-cmake
${{ matrix.pkg_prefix }}-ninja
${{ matrix.pkg_prefix }}-pkg-config
git
base-devel
- name: Checkout repository (with sub-modules)
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up vcpkg
run: |
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
cd "$VCPKG_ROOT"
./bootstrap-vcpkg.sh
- name: Configure (cmake --preset)
run: |
cmake --preset ${{ matrix.preset }} \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;tests"
- name: Build
run: |
cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
- name: Run unit_tests.exe
run: |
./out/Release/unit_tests.exe
- name: Upload logs on failure
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: mingw-build-logs-${{ matrix.msystem }}
path: |
cmake-build/build/${{ matrix.preset }}/**/*.log
${{ env.VCPKG_ROOT }}/buildtrees/**/*.log
##############################################################################
# 9) Valgrind Memory Check
##############################################################################
valgrind-memory-check:
name: Valgrind Analysis (All Targets)
runs-on: ubuntu-latest
needs: [linux-build-and-test]
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
- name: Install toolchain
shell: bash
run: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main"
sudo apt-get update
sudo apt-get install -y git build-essential cmake ninja-build \
zip unzip curl pkg-config ca-certificates \
clang-21 lld-21 libc++-21-dev libc++abi-21-dev \
llvm-21 valgrind libxmu-dev libxi-dev libgl-dev libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-21 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-21 100
sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-21 100
- 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 linux-release-vcpkg \
-DCMAKE_BUILD_TYPE=Debug \
-DOMATH_BUILD_EXAMPLES=OFF \
-DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=ON \
-DOMATH_ENABLE_VALGRIND=ON \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests;benchmark"
- name: Build All Targets
shell: bash
run: cmake --build cmake-build/build/linux-release-vcpkg
- name: Run Valgrind (All Registered Targets)
shell: bash
working-directory: cmake-build/build/linux-release-vcpkg
run: |
cmake --build . --target valgrind_all

618
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,618 @@
name: Release
on:
release:
types: [published]
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
jobs:
##############################################################################
# 1) Linux Clang / Ninja
##############################################################################
linux-release:
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- name: Linux (Clang) (x64-linux)
triplet: x64-linux
runner: ubuntu-latest
preset: linux-release-vcpkg
archive: omath-linux-x64
install_cmd: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main"
sudo apt-get update
sudo apt-get install -y git build-essential cmake ninja-build \
zip unzip curl pkg-config ca-certificates \
clang-21 lld-21 libc++-21-dev libc++abi-21-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-21 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-21 100
sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-21 100
- name: Linux (Clang) (x86-linux)
triplet: x86-linux
runner: ubuntu-latest
preset: linux-release-vcpkg-x86
archive: omath-linux-x86
install_cmd: |
# Add LLVM 21 repository
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main"
# Add GCC Toolchain PPA
sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu plucky main universe"
# Enable i386 architecture
sudo dpkg --add-architecture i386
sudo apt-get update
# Install Clang 21
sudo apt-get install -y git build-essential cmake ninja-build \
zip unzip curl pkg-config ca-certificates \
clang-21 lld-21 libc++-21-dev libc++abi-21-dev
sudo apt-get install -y -t plucky binutils
# Install GCC 15 with multilib support
sudo apt-get install -y gcc-15-multilib g++-15-multilib
# Set up alternatives for Clang
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-21 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-21 100
sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-21 100
# Set up alternatives for GCC
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-15 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-15 100
- name: Linux (Clang) (arm64-linux)
triplet: arm64-linux
runner: ubuntu-24.04-arm
preset: linux-release-vcpkg-arm64
archive: omath-linux-arm64
install_cmd: |
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main"
sudo apt-get update
sudo apt-get install -y git build-essential cmake ninja-build \
zip unzip curl pkg-config ca-certificates \
clang-21 lld-21 libc++-21-dev libc++abi-21-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-21 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-21 100
sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-21 100
fail-fast: false
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
- name: Install basic tool-chain
shell: bash
run: ${{ matrix.install_cmd }}
- 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 ${{ matrix.preset }} \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=OFF \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2"
- name: Build
shell: bash
run: cmake --build cmake-build/build/${{ matrix.preset }} --target omath
- name: Install
shell: bash
run: cmake --install cmake-build/build/${{ matrix.preset }} --prefix staging
- name: Package
shell: bash
run: tar -czf ${{ matrix.archive }}.tar.gz -C staging .
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: gh release upload "${{ github.event.release.tag_name }}" "${{ matrix.archive }}.tar.gz" --clobber
##############################################################################
# 2) Windows MSVC / Ninja
##############################################################################
windows-release:
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- name: Windows (MSVC) (x64-windows)
runner: windows-latest
arch: amd64
preset: windows-release-vcpkg
triplet: x64-windows
archive: omath-windows-x64
- name: Windows (MSVC) (x86-windows)
runner: windows-latest
arch: amd64_x86
preset: windows-release-vcpkg-x86
triplet: x86-windows
archive: omath-windows-x86
- name: Windows (MSVC) (arm64-windows)
runner: windows-11-arm
arch: arm64
preset: windows-release-vcpkg-arm64
triplet: arm64-windows
archive: omath-windows-arm64
fail-fast: false
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
- name: Checkout repository (with sub-modules)
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
- name: Set up MSVC developer command-prompt
uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.arch }}
- name: Configure (cmake --preset)
shell: bash
run: |
cmake --preset ${{ matrix.preset }} \
-DOMATH_BUILD_TESTS=OFF \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2"
- name: Build
shell: bash
run: cmake --build cmake-build/build/${{ matrix.preset }} --target omath
- name: Install
shell: bash
run: cmake --install cmake-build/build/${{ matrix.preset }} --prefix staging
- name: Package
shell: pwsh
run: Compress-Archive -Path staging\* -DestinationPath "${{ matrix.archive }}.zip"
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: gh release upload "${{ github.event.release.tag_name }}" "${{ matrix.archive }}.zip" --clobber
##############################################################################
# 3) macOS AppleClang / Ninja
##############################################################################
macos-release:
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- name: macOS (AppleClang) (arm64-osx)
runner: macos-latest
preset: darwin-release-vcpkg
triplet: arm64-osx
archive: omath-macos-arm64
- name: macOS (AppleClang) (x64-osx)
runner: macos-15-intel
preset: darwin-release-vcpkg-x64
triplet: x64-osx
archive: omath-macos-x64
fail-fast: false
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
- name: Install basic tool-chain with Homebrew
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"
- name: Configure (cmake --preset)
shell: bash
run: |
cmake --preset ${{ matrix.preset }} \
-DOMATH_BUILD_TESTS=OFF \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2"
- name: Build
shell: bash
run: cmake --build cmake-build/build/${{ matrix.preset }} --target omath
- name: Install
shell: bash
run: cmake --install cmake-build/build/${{ matrix.preset }} --prefix staging
- name: Package
shell: bash
run: tar -czf ${{ matrix.archive }}.tar.gz -C staging .
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: gh release upload "${{ github.event.release.tag_name }}" "${{ matrix.archive }}.tar.gz" --clobber
##############################################################################
# 4) iOS AppleClang / Xcode / arm64-ios
##############################################################################
ios-release:
name: iOS (AppleClang) (${{ matrix.triplet }})
runs-on: macOS-latest
strategy:
matrix:
include:
- triplet: arm64-ios
preset: ios-release-vcpkg
archive: omath-ios-arm64
fail-fast: false
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 ${{ matrix.preset }} \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=OFF \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui"
- name: Build
shell: bash
run: |
cmake --build cmake-build/build/${{ matrix.preset }} --config Release --target omath
- name: Install
shell: bash
run: cmake --install cmake-build/build/${{ matrix.preset }} --prefix staging
- name: Package
shell: bash
run: tar -czf ${{ matrix.archive }}.tar.gz -C staging .
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: gh release upload "${{ github.event.release.tag_name }}" "${{ matrix.archive }}.tar.gz" --clobber
##############################################################################
# 5) FreeBSD Clang / Ninja
##############################################################################
freebsd-release:
name: FreeBSD (Clang) (${{ matrix.triplet }})
runs-on: ubuntu-latest
strategy:
matrix:
include:
- triplet: x64-freebsd
preset: freebsd-release-vcpkg
arch: x86-64
archive: omath-freebsd-x64
fail-fast: false
env:
VCPKG_ROOT: ${{ github.workspace }}/tmp/vcpkg
steps:
- name: Checkout repository (with sub-modules)
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build and Package
uses: cross-platform-actions/action@v0.31.0
with:
operating_system: freebsd
architecture: ${{ matrix.arch }}
version: '15.0'
memory: '12G'
cpu_count: 4
run: |
sudo pkg install -y git curl zip unzip gmake llvm gsed bash perl5 openssl 7-zip coreutils cmake ninja pkgconf patchelf
git config --global --add safe.directory `pwd`
# Build vcpkg in /tmp to avoid sshfs timestamp sync issues
export VCPKG_ROOT=/tmp/vcpkg
rm -rf "$VCPKG_ROOT"
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
cd "$VCPKG_ROOT"
./bootstrap-vcpkg.sh
cd -
export VCPKG_FORCE_SYSTEM_BINARIES=0
cmake --preset ${{ matrix.preset }} \
-DOMATH_BUILD_TESTS=OFF \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2" \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported"
cmake --build cmake-build/build/${{ matrix.preset }} --target omath
cmake --install cmake-build/build/${{ matrix.preset }} --prefix staging
tar -czf ${{ matrix.archive }}.tar.gz -C staging .
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: gh release upload "${{ github.event.release.tag_name }}" "${{ matrix.archive }}.tar.gz" --clobber
##############################################################################
# 6) Android NDK Clang / Ninja
##############################################################################
android-release:
name: Android NDK (${{ matrix.triplet }})
runs-on: ubuntu-latest
strategy:
matrix:
include:
- triplet: arm-neon-android
preset: android-arm-neon-release-vcpkg
archive: omath-android-arm-neon
- triplet: arm64-android
preset: android-arm64-release-vcpkg
archive: omath-android-arm64
- triplet: x64-android
preset: android-x64-release-vcpkg
archive: omath-android-x64
- triplet: x86-android
preset: android-x86-release-vcpkg
archive: omath-android-x86
fail-fast: false
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="r28b"
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
- 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 ${{ matrix.preset }} \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=OFF \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui"
- name: Build
shell: bash
run: |
cmake --build cmake-build/build/${{ matrix.preset }} --target omath
- name: Install
shell: bash
run: cmake --install cmake-build/build/${{ matrix.preset }} --prefix staging
- name: Package
shell: bash
run: tar -czf ${{ matrix.archive }}.tar.gz -C staging .
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: gh release upload "${{ github.event.release.tag_name }}" "${{ matrix.archive }}.tar.gz" --clobber
##############################################################################
# 7) WebAssembly (Emscripten) Clang / Ninja / wasm32-emscripten
##############################################################################
wasm-release:
name: WebAssembly (Emscripten) (${{ matrix.triplet }})
runs-on: ubuntu-latest
strategy:
matrix:
include:
- triplet: wasm32-emscripten
preset: wasm-release-vcpkg
archive: omath-wasm32
fail-fast: false
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
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 ${{ matrix.preset }} \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=OFF \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui"
- name: Build
shell: bash
run: |
cmake --build cmake-build/build/${{ matrix.preset }} --target omath
- name: Install
shell: bash
run: cmake --install cmake-build/build/${{ matrix.preset }} --prefix staging
- name: Package
shell: bash
run: tar -czf ${{ matrix.archive }}.tar.gz -C staging .
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: gh release upload "${{ github.event.release.tag_name }}" "${{ matrix.archive }}.tar.gz" --clobber
##############################################################################
# 8) Windows MSYS2 MinGW GCC / Ninja
##############################################################################
mingw-release:
name: ${{ matrix.name }}
runs-on: windows-latest
strategy:
matrix:
include:
- name: MINGW64 (MSYS2) (x64-mingw-dynamic)
msystem: MINGW64
pkg_prefix: mingw-w64-x86_64
preset: mingw-release-vcpkg
archive: omath-mingw64-x64
- name: UCRT64 (MSYS2) (x64-mingw-dynamic)
msystem: UCRT64
pkg_prefix: mingw-w64-ucrt-x86_64
preset: mingw-release-vcpkg
archive: omath-ucrt64-x64
- name: MINGW32 (MSYS2) (x86-mingw-dynamic)
msystem: MINGW32
pkg_prefix: mingw-w64-i686
preset: mingw32-release-vcpkg
archive: omath-mingw32-x86
fail-fast: false
defaults:
run:
shell: msys2 {0}
env:
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
steps:
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
update: true
install: >-
${{ matrix.pkg_prefix }}-toolchain
${{ matrix.pkg_prefix }}-cmake
${{ matrix.pkg_prefix }}-ninja
${{ matrix.pkg_prefix }}-pkg-config
git
base-devel
- name: Checkout repository (with sub-modules)
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up vcpkg
run: |
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
cd "$VCPKG_ROOT"
./bootstrap-vcpkg.sh
- name: Configure (cmake --preset)
run: |
cmake --preset ${{ matrix.preset }} \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=OFF \
-DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui"
- name: Build
run: |
cmake --build cmake-build/build/${{ matrix.preset }} --target omath
- name: Install
run: cmake --install cmake-build/build/${{ matrix.preset }} --prefix staging
- name: Package
shell: pwsh
run: Compress-Archive -Path staging\* -DestinationPath "${{ matrix.archive }}.zip"
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: gh release upload "${{ github.event.release.tag_name }}" "${{ matrix.archive }}.zip" --clobber

10
.gitignore vendored
View File

@@ -3,3 +3,13 @@
*.DS_Store
/extlibs/vcpkg
.idea/workspace.xml
/build/
/clang-coverage/
*.gcov
*.bin
# pixi lock
pixi.lock
# pixi environments
.pixi/*
!.pixi/config.toml
/site/

7
.idea/dictionaries/project.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>vmprotect</w>
</words>
</dictionary>
</component>

5
.idea/editor.xml generated
View File

@@ -17,7 +17,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooManyArgs/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCVQualifierCanNotBeAppliedToReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassIsIncomplete/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeedsConstructorBecauseOfUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
@@ -56,6 +56,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultInitializationWithNoUserConstructor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultIsUsedAsIdentifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultedSpecialMemberFunctionIsImplicitlyDeleted/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefinitionsOrder/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeletingVoidPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTemplateWithoutTemplateKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTypeWithoutTypenameKeyword/@EntryIndexedValue" value="WARNING" type="string" />
@@ -109,7 +110,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLambdaCaptureNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableWithNonTrivialDtorIsNeverUsed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableWithNonTrivialDtorIsNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLongFloat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeConst/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeStatic/@EntryIndexedValue" value="SUGGESTION" type="string" />

2
.idea/omath.iml generated
View File

@@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />
<module classpath="CIDR" type="CPP_MODULE" version="4" />

24
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": " Unit Tests (Debug)",
"type": "lldb",
"request": "launch",
"program": "${workspaceRoot}/out/Debug/unit_tests",
"args": [],
"cwd": "${workspaceRoot}"
},
{
"name": " Unit Tests (Release)",
"type": "lldb",
"request": "launch",
"program": "${workspaceRoot}/out/Release/unit_tests",
"args": [],
"cwd": "${workspaceRoot}"
}
]
}

View File

@@ -6,6 +6,9 @@ project(omath VERSION ${OMATH_VERSION} LANGUAGES CXX)
include(CMakePackageConfigHelpers)
include(CheckCXXCompilerFlag)
include(cmake/Coverage.cmake)
include(cmake/Valgrind.cmake)
if(MSVC)
check_cxx_compiler_flag("/arch:AVX2" COMPILER_SUPPORTS_AVX2)
else()
@@ -14,16 +17,23 @@ endif ()
option(OMATH_BUILD_TESTS "Build unit tests" OFF)
option(OMATH_BUILD_BENCHMARK "Build benchmarks" OFF)
option(OMATH_THREAT_WARNING_AS_ERROR "Set highest level of warnings and force compiler to treat them as errors" ON)
option(OMATH_THREAT_WARNING_AS_ERROR
"Set highest level of warnings and force compiler to treat them as errors" ON)
option(OMATH_BUILD_AS_SHARED_LIBRARY "Build Omath as .so or .dll" OFF)
option(OMATH_USE_AVX2 "Omath will use AVX2 to boost performance" ${COMPILER_SUPPORTS_AVX2})
option(OMATH_IMGUI_INTEGRATION "Omath will define method to convert omath types to imgui types" OFF)
option(OMATH_BUILD_EXAMPLES "Build example projects with you can learn & play" OFF)
option(OMATH_STATIC_MSVC_RUNTIME_LIBRARY "Force Omath to link static runtime" OFF)
option(OMATH_SUPRESS_SAFETY_CHECKS "Supress some safety checks in release build to improve general performance" ON)
option(OMATH_USE_UNITY_BUILD "Will enable unity build to speed up compilation" OFF)
option(OMATH_ENABLE_LEGACY "Will enable legacy classes that MUST be used ONLY for backward compatibility" ON)
option(OMATH_ENABLE_LEGACY
"Will enable legacy classes that MUST be used ONLY for backward compatibility" ON)
option(OMATH_SUPRESS_SAFETY_CHECKS
"Supress some safety checks in release build to improve general performance" ON)
option(OMATH_ENABLE_COVERAGE "Enable coverage" OFF)
option(OMATH_ENABLE_FORCE_INLINE
"Will for compiler to make some functions to be force inlined no matter what" ON)
option(OMATH_VMPROTECT_INTEGRATION
"omath will use vmprotect sdk to protect sensitive parts of code from reverse engineering"
OFF)
if(VCPKG_MANIFEST_FEATURES)
foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES)
@@ -35,18 +45,26 @@ 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)
elseif(omath_feature STREQUAL "vmprotect")
set(OMATH_VMPROTECT_INTEGRATION ON)
endif()
endforeach()
endif()
if(OMATH_USE_AVX2 AND NOT COMPILER_SUPPORTS_AVX2)
message(WARNING "OMATH_USE_AVX2 requested, but compiler/target does not support AVX2. Disabling.")
message(
WARNING "OMATH_USE_AVX2 requested, but compiler/target does not support AVX2. Disabling.")
set(OMATH_USE_AVX2 OFF CACHE BOOL "Omath will use AVX2 to boost performance" FORCE)
endif()
if(${PROJECT_IS_TOP_LEVEL})
message(STATUS "[${PROJECT_NAME}]: Building on ${CMAKE_HOST_SYSTEM_NAME}, compiler ${CMAKE_CXX_COMPILER_ID}")
message(
STATUS
"[${PROJECT_NAME}]: Building on ${CMAKE_HOST_SYSTEM_NAME}, compiler ${CMAKE_CXX_COMPILER_ID}"
)
message(STATUS "[${PROJECT_NAME}]: Warnings as errors ${OMATH_THREAT_WARNING_AS_ERROR}")
message(STATUS "[${PROJECT_NAME}]: Build unit tests ${OMATH_BUILD_TESTS}")
message(STATUS "[${PROJECT_NAME}]: Build benchmark ${OMATH_BUILD_BENCHMARK}")
@@ -58,6 +76,8 @@ if (${PROJECT_IS_TOP_LEVEL})
message(STATUS "[${PROJECT_NAME}]: ImGUI integration feature status ${OMATH_IMGUI_INTEGRATION}")
message(STATUS "[${PROJECT_NAME}]: Legacy features support ${OMATH_ENABLE_LEGACY}")
message(STATUS "[${PROJECT_NAME}]: Building using vcpkg ${OMATH_BUILD_VIA_VCPKG}")
message(STATUS "[${PROJECT_NAME}]: Coverage feature status ${OMATH_ENABLE_COVERAGE}")
message(STATUS "[${PROJECT_NAME}]: Valgrind feature status ${OMATH_ENABLE_VALGRIND}")
endif()
file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
@@ -69,7 +89,6 @@ else ()
add_library(${PROJECT_NAME} STATIC ${OMATH_SOURCES} ${OMATH_HEADERS})
endif()
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_VERSION="${PROJECT_VERSION}")
@@ -80,7 +99,8 @@ if (OMATH_IMGUI_INTEGRATION)
# IMGUI is being linked as submodule
if(TARGET imgui)
target_link_libraries(${PROJECT_NAME} PUBLIC imgui)
install(TARGETS imgui
install(
TARGETS imgui
EXPORT omathTargets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
@@ -93,6 +113,11 @@ if (OMATH_IMGUI_INTEGRATION)
endif()
if(OMATH_VMPROTECT_INTEGRATION)
find_package(vmprotect_sdk CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC vmprotect_sdk::vmprotect_sdk)
endif()
if(OMATH_USE_AVX2)
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_USE_AVX2)
endif()
@@ -105,22 +130,20 @@ if (OMATH_ENABLE_LEGACY)
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_ENABLE_LEGACY)
endif()
set_target_properties(${PROJECT_NAME} PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
if(OMATH_ENABLE_FORCE_INLINE)
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_ENABLE_FORCE_INLINE)
endif()
set_target_properties(
${PROJECT_NAME}
PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
CXX_STANDARD 23
CXX_STANDARD_REQUIRED ON)
if (OMATH_USE_UNITY_BUILD)
set_target_properties(${PROJECT_NAME} PROPERTIES
UNITY_BUILD ON
UNITY_BUILD_BATCH_SIZE 20)
endif ()
if(OMATH_STATIC_MSVC_RUNTIME_LIBRARY)
set_target_properties(${PROJECT_NAME} PROPERTIES
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
)
set_target_properties(${PROJECT_NAME} PROPERTIES MSVC_RUNTIME_LIBRARY
"MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
if(OMATH_USE_AVX2)
@@ -133,11 +156,19 @@ if (OMATH_USE_AVX2)
endif()
endif()
if(EMSCRIPTEN)
target_compile_options(${PROJECT_NAME} PUBLIC -fexceptions)
target_link_options(${PROJECT_NAME} PUBLIC -fexceptions)
endif()
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
if(OMATH_BUILD_TESTS)
add_subdirectory(tests)
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_BUILD_TESTS)
if(OMATH_ENABLE_COVERAGE)
omath_setup_coverage(${PROJECT_NAME})
endif()
endif()
if(OMATH_BUILD_BENCHMARK)
@@ -159,51 +190,52 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_definitions(${PROJECT_NAME} INTERFACE NOMINMAX)
endif()
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # Use this path when building the project
$<INSTALL_INTERFACE:include> # Use this path when the project is installed
target_include_directories(
${PROJECT_NAME}
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # Use this path
# when building
# the project
$<INSTALL_INTERFACE:include> # Use this path when the project is
# installed
)
# Installation rules
# Install the library
install(TARGETS ${PROJECT_NAME}
install(
TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets
ARCHIVE DESTINATION lib COMPONENT ${PROJECT_NAME} # For static libraries
LIBRARY DESTINATION lib COMPONENT ${PROJECT_NAME} # For shared libraries
RUNTIME DESTINATION bin COMPONENT ${PROJECT_NAME} # For executables (on Windows)
RUNTIME DESTINATION bin COMPONENT ${PROJECT_NAME} # For executables (on
# Windows)
)
# Install headers as part of omath_component
install(DIRECTORY include/ DESTINATION include COMPONENT ${PROJECT_NAME})
# Export omath target for CMake find_package support, also under omath_component
install(EXPORT ${PROJECT_NAME}Targets
install(
EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION lib/cmake/${PROJECT_NAME} COMPONENT ${PROJECT_NAME}
)
DESTINATION lib/cmake/${PROJECT_NAME}
COMPONENT ${PROJECT_NAME})
# Generate the omathConfigVersion.cmake file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/omathConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/omathConfigVersion.cmake"
VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion)
# Generate the omathConfig.cmake file from the template (which is in the cmake/ folder)
# Generate the omathConfig.cmake file from the template (which is in the cmake/
# folder)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/omathConfig.cmake.in" # Path to the .in file
"${CMAKE_CURRENT_BINARY_DIR}/omathConfig.cmake" # Output path for the generated file
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}
)
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/omathConfig.cmake.in" # Path to the .in
# file
"${CMAKE_CURRENT_BINARY_DIR}/omathConfig.cmake" # Output path for the
# generated file
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME})
# Install the generated config files
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/omathConfig.cmake"
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/omathConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/omathConfigVersion.cmake"
DESTINATION lib/cmake/${PROJECT_NAME}
)
DESTINATION lib/cmake/${PROJECT_NAME})

View File

@@ -1,15 +1,49 @@
{
"version": 3,
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 25,
"patch": 0
},
"configurePresets": [
{
"name": "windows-base",
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/cmake-build/build/${presetName}",
"installDir": "${sourceDir}/cmake-build/install/${presetName}",
"installDir": "${sourceDir}/cmake-build/install/${presetName}"
},
{
"name": "vcpkg-base",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_COMPILER": "cl.exe",
"CMAKE_MAKE_PROGRAM": "Ninja"
"OMATH_BUILD_VIA_VCPKG": "ON",
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"VCPKG_INSTALLED_DIR": "${sourceDir}/cmake-build/vcpkg_installed"
}
},
{
"name": "debug",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "release",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "windows-base",
"hidden": true,
"inherits": "base",
"cacheVariables": {
"CMAKE_CXX_COMPILER": "cl.exe"
},
"condition": {
"type": "equals",
@@ -18,59 +52,88 @@
}
},
{
"name": "windows-base-vcpkg",
"name": "windows-vcpkg-base",
"hidden": true,
"inherits": "windows-base",
"inherits": ["windows-base", "vcpkg-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"
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
}
},
{
"name": "windows-debug",
"displayName": "Debug",
"inherits": "windows-base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "windows-debug-vcpkg",
"displayName": "Debug",
"inherits": "windows-base-vcpkg",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "windows-release-vcpkg",
"displayName": "Release",
"inherits": "windows-base-vcpkg",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"OMATH_BUILD_VIA_VCPKG": "ON"
}
"displayName": "Windows Debug",
"inherits": ["windows-base", "debug"]
},
{
"name": "windows-release",
"displayName": "Release",
"inherits": "windows-base",
"displayName": "Windows Release",
"inherits": ["windows-base", "release"]
},
{
"name": "windows-debug-vcpkg",
"displayName": "Windows Debug (vcpkg)",
"inherits": ["windows-vcpkg-base", "debug"]
},
{
"name": "windows-release-vcpkg",
"displayName": "Windows Release (vcpkg)",
"inherits": ["windows-vcpkg-base", "release"]
},
{
"name": "windows-x86-vcpkg-base",
"hidden": true,
"inherits": ["windows-base", "vcpkg-base"],
"architecture": {
"value": "x86",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
"VCPKG_TARGET_TRIPLET": "x86-windows",
"VCPKG_HOST_TRIPLET": "x64-windows",
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
}
},
{
"name": "windows-debug-vcpkg-x86",
"displayName": "Windows x86 Debug (vcpkg)",
"inherits": ["windows-x86-vcpkg-base", "debug"]
},
{
"name": "windows-release-vcpkg-x86",
"displayName": "Windows x86 Release (vcpkg)",
"inherits": ["windows-x86-vcpkg-base", "release"]
},
{
"name": "windows-arm64-vcpkg-base",
"hidden": true,
"inherits": ["windows-base", "vcpkg-base"],
"architecture": {
"value": "arm64",
"strategy": "external"
},
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "arm64-windows",
"VCPKG_HOST_TRIPLET": "arm64-windows",
"VCPKG_MANIFEST_FEATURES": "tests;imgui;examples"
}
},
{
"name": "windows-debug-vcpkg-arm64",
"displayName": "Windows ARM64 Debug (vcpkg)",
"inherits": ["windows-arm64-vcpkg-base", "debug"]
},
{
"name": "windows-release-vcpkg-arm64",
"displayName": "Windows ARM64 Release (vcpkg)",
"inherits": ["windows-arm64-vcpkg-base", "release"]
},
{
"name": "linux-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"
},
"inherits": "base",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
@@ -78,57 +141,88 @@
}
},
{
"name": "linux-base-vcpkg",
"name": "linux-vcpkg-base",
"hidden": true,
"inherits": "linux-base",
"inherits": ["linux-base", "vcpkg-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"
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;vmprotect;examples"
}
},
{
"name": "linux-debug",
"displayName": "Linux Debug",
"inherits": "linux-base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "linux-debug-vcpkg",
"displayName": "Linux Debug",
"inherits": "linux-base-vcpkg",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
"inherits": ["linux-base", "debug"]
},
{
"name": "linux-release",
"displayName": "Linux Release",
"inherits": "linux-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
"inherits": ["linux-base", "release"]
},
{
"name": "linux-debug-vcpkg",
"displayName": "Linux Debug (vcpkg)",
"inherits": ["linux-vcpkg-base", "debug"]
},
{
"name": "linux-release-vcpkg",
"displayName": "Linux Release",
"inherits": "linux-base-vcpkg",
"displayName": "Linux Release (vcpkg)",
"inherits": ["linux-vcpkg-base", "release"]
},
{
"name": "linux-x86-vcpkg-base",
"hidden": true,
"inherits": ["linux-base", "vcpkg-base"],
"architecture": {
"value": "x86",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
"CMAKE_C_FLAGS": "-m32",
"CMAKE_CXX_FLAGS": "-m32",
"VCPKG_TARGET_TRIPLET": "x86-linux",
"VCPKG_HOST_TRIPLET": "x64-linux",
"VCPKG_MANIFEST_FEATURES": "tests;imgui"
}
},
{
"name": "linux-debug-vcpkg-x86",
"displayName": "Linux x86 Debug (vcpkg)",
"inherits": ["linux-x86-vcpkg-base", "debug"]
},
{
"name": "linux-release-vcpkg-x86",
"displayName": "Linux x86 Release (vcpkg)",
"inherits": ["linux-x86-vcpkg-base", "release"]
},
{
"name": "linux-arm64-vcpkg-base",
"hidden": true,
"inherits": ["linux-base", "vcpkg-base"],
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "arm64-linux",
"VCPKG_HOST_TRIPLET": "arm64-linux",
"VCPKG_MANIFEST_FEATURES": "tests;imgui"
}
},
{
"name": "linux-debug-vcpkg-arm64",
"displayName": "Linux ARM64 Debug (vcpkg)",
"inherits": ["linux-arm64-vcpkg-base", "debug"]
},
{
"name": "linux-release-vcpkg-arm64",
"displayName": "Linux ARM64 Release (vcpkg)",
"inherits": ["linux-arm64-vcpkg-base", "release"]
},
{
"name": "darwin-base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/cmake-build/build/${presetName}",
"installDir": "${sourceDir}/cmake-build/install/${presetName}",
"inherits": "base",
"cacheVariables": {
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_MAKE_PROGRAM": "ninja"
"CMAKE_CXX_COMPILER": "clang++"
},
"condition": {
"type": "equals",
@@ -137,47 +231,456 @@
}
},
{
"name": "darwin-base-vcpkg",
"name": "darwin-vcpkg-base",
"hidden": true,
"inherits": "darwin-base",
"inherits": ["darwin-base", "vcpkg-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"
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
}
},
{
"name": "darwin-debug",
"displayName": "Darwin Debug",
"inherits": "darwin-base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "darwin-debug-vcpkg",
"displayName": "Darwin Debug",
"inherits": "darwin-base-vcpkg",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
"displayName": "macOS Debug",
"inherits": ["darwin-base", "debug"]
},
{
"name": "darwin-release",
"displayName": "Darwin Release",
"inherits": "darwin-base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
"displayName": "macOS Release",
"inherits": ["darwin-base", "release"]
},
{
"name": "darwin-debug-vcpkg",
"displayName": "macOS Debug (vcpkg)",
"inherits": ["darwin-vcpkg-base", "debug"]
},
{
"name": "darwin-release-vcpkg",
"displayName": "Darwin Release",
"inherits": "darwin-base-vcpkg",
"displayName": "macOS Release (vcpkg)",
"inherits": ["darwin-vcpkg-base", "release"]
},
{
"name": "darwin-x64-vcpkg-base",
"hidden": true,
"inherits": ["darwin-base", "vcpkg-base"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
"CMAKE_OSX_ARCHITECTURES": "x86_64",
"VCPKG_TARGET_TRIPLET": "x64-osx",
"VCPKG_HOST_TRIPLET": "x64-osx",
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples"
}
},
{
"name": "darwin-debug-vcpkg-x64",
"displayName": "macOS x64 Debug (vcpkg)",
"inherits": ["darwin-x64-vcpkg-base", "debug"]
},
{
"name": "darwin-release-vcpkg-x64",
"displayName": "macOS x64 Release (vcpkg)",
"inherits": ["darwin-x64-vcpkg-base", "release"]
},
{
"name": "ios-base",
"hidden": true,
"inherits": "base",
"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-vcpkg-base",
"hidden": true,
"inherits": ["ios-base", "vcpkg-base"],
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "arm64-ios",
"VCPKG_HOST_TRIPLET": "arm64-osx",
"VCPKG_MANIFEST_FEATURES": "tests;imgui"
}
},
{
"name": "ios-debug-vcpkg",
"displayName": "iOS Debug (vcpkg)",
"inherits": ["ios-vcpkg-base", "debug"]
},
{
"name": "ios-release-vcpkg",
"displayName": "iOS Release (vcpkg)",
"inherits": ["ios-vcpkg-base", "release"]
},
{
"name": "freebsd-base",
"hidden": true,
"inherits": "base",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "FreeBSD"
}
},
{
"name": "freebsd-vcpkg-base",
"hidden": true,
"inherits": ["freebsd-base", "vcpkg-base"],
"cacheVariables": {
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2"
}
},
{
"name": "freebsd-debug",
"displayName": "FreeBSD Debug",
"inherits": ["freebsd-base", "debug"]
},
{
"name": "freebsd-release",
"displayName": "FreeBSD Release",
"inherits": ["freebsd-base", "release"]
},
{
"name": "freebsd-debug-vcpkg",
"displayName": "FreeBSD Debug (vcpkg)",
"inherits": ["freebsd-vcpkg-base", "debug"]
},
{
"name": "freebsd-release-vcpkg",
"displayName": "FreeBSD Release (vcpkg)",
"inherits": ["freebsd-vcpkg-base", "release"]
},
{
"name": "android-base",
"hidden": true,
"inherits": "base",
"cacheVariables": {
"CMAKE_SYSTEM_NAME": "Android",
"CMAKE_SYSTEM_VERSION": "24",
"CMAKE_ANDROID_NDK": "$env{ANDROID_NDK_HOME}",
"CMAKE_ANDROID_STL_TYPE": "c++_static"
}
},
{
"name": "android-vcpkg-base",
"hidden": true,
"inherits": ["android-base", "vcpkg-base"],
"cacheVariables": {
"VCPKG_MANIFEST_FEATURES": "tests;imgui"
}
},
{
"name": "android-arm64-base",
"hidden": true,
"inherits": "android-base",
"cacheVariables": {
"CMAKE_ANDROID_ARCH_ABI": "arm64-v8a"
}
},
{
"name": "android-arm64-vcpkg-base",
"hidden": true,
"inherits": "android-vcpkg-base",
"cacheVariables": {
"CMAKE_ANDROID_ARCH_ABI": "arm64-v8a",
"VCPKG_TARGET_TRIPLET": "arm64-android"
}
},
{
"name": "android-arm64-debug",
"displayName": "Android arm64-v8a Debug",
"inherits": ["android-arm64-base", "debug"]
},
{
"name": "android-arm64-release",
"displayName": "Android arm64-v8a Release",
"inherits": ["android-arm64-base", "release"]
},
{
"name": "android-arm64-debug-vcpkg",
"displayName": "Android arm64-v8a Debug (vcpkg)",
"inherits": ["android-arm64-vcpkg-base", "debug"]
},
{
"name": "android-arm64-release-vcpkg",
"displayName": "Android arm64-v8a Release (vcpkg)",
"inherits": ["android-arm64-vcpkg-base", "release"]
},
{
"name": "android-arm-neon-base",
"hidden": true,
"inherits": "android-base",
"cacheVariables": {
"CMAKE_ANDROID_ARCH_ABI": "armeabi-v7a",
"CMAKE_ANDROID_ARM_NEON": "ON"
}
},
{
"name": "android-arm-neon-vcpkg-base",
"hidden": true,
"inherits": "android-vcpkg-base",
"cacheVariables": {
"CMAKE_ANDROID_ARCH_ABI": "armeabi-v7a",
"CMAKE_ANDROID_ARM_NEON": "ON",
"VCPKG_TARGET_TRIPLET": "arm-neon-android"
}
},
{
"name": "android-arm-neon-debug",
"displayName": "Android armeabi-v7a NEON Debug",
"inherits": ["android-arm-neon-base", "debug"]
},
{
"name": "android-arm-neon-release",
"displayName": "Android armeabi-v7a NEON Release",
"inherits": ["android-arm-neon-base", "release"]
},
{
"name": "android-arm-neon-debug-vcpkg",
"displayName": "Android armeabi-v7a NEON Debug (vcpkg)",
"inherits": ["android-arm-neon-vcpkg-base", "debug"]
},
{
"name": "android-arm-neon-release-vcpkg",
"displayName": "Android armeabi-v7a NEON Release (vcpkg)",
"inherits": ["android-arm-neon-vcpkg-base", "release"]
},
{
"name": "android-x64-base",
"hidden": true,
"inherits": "android-base",
"cacheVariables": {
"CMAKE_ANDROID_ARCH_ABI": "x86_64"
}
},
{
"name": "android-x64-vcpkg-base",
"hidden": true,
"inherits": "android-vcpkg-base",
"cacheVariables": {
"CMAKE_ANDROID_ARCH_ABI": "x86_64",
"VCPKG_TARGET_TRIPLET": "x64-android"
}
},
{
"name": "android-x64-debug",
"displayName": "Android x86_64 Debug",
"inherits": ["android-x64-base", "debug"]
},
{
"name": "android-x64-release",
"displayName": "Android x86_64 Release",
"inherits": ["android-x64-base", "release"]
},
{
"name": "android-x64-debug-vcpkg",
"displayName": "Android x86_64 Debug (vcpkg)",
"inherits": ["android-x64-vcpkg-base", "debug"]
},
{
"name": "android-x64-release-vcpkg",
"displayName": "Android x86_64 Release (vcpkg)",
"inherits": ["android-x64-vcpkg-base", "release"]
},
{
"name": "android-x86-base",
"hidden": true,
"inherits": "android-base",
"cacheVariables": {
"CMAKE_ANDROID_ARCH_ABI": "x86"
}
},
{
"name": "android-x86-vcpkg-base",
"hidden": true,
"inherits": "android-vcpkg-base",
"cacheVariables": {
"CMAKE_ANDROID_ARCH_ABI": "x86",
"VCPKG_TARGET_TRIPLET": "x86-android"
}
},
{
"name": "android-x86-debug",
"displayName": "Android x86 Debug",
"inherits": ["android-x86-base", "debug"]
},
{
"name": "android-x86-release",
"displayName": "Android x86 Release",
"inherits": ["android-x86-base", "release"]
},
{
"name": "android-x86-debug-vcpkg",
"displayName": "Android x86 Debug (vcpkg)",
"inherits": ["android-x86-vcpkg-base", "debug"]
},
{
"name": "android-x86-release-vcpkg",
"displayName": "Android x86 Release (vcpkg)",
"inherits": ["android-x86-vcpkg-base", "release"]
},
{
"name": "android-debug",
"displayName": "Android Debug (default: arm64)",
"inherits": "android-arm64-debug"
},
{
"name": "android-release",
"displayName": "Android Release (default: arm64)",
"inherits": "android-arm64-release"
},
{
"name": "android-debug-vcpkg",
"displayName": "Android Debug (default: arm64, vcpkg)",
"inherits": "android-arm64-debug-vcpkg"
},
{
"name": "android-release-vcpkg",
"displayName": "Android Release (default: arm64, vcpkg)",
"inherits": "android-arm64-release-vcpkg"
},
{
"name": "wasm-base",
"hidden": true,
"inherits": "base"
},
{
"name": "wasm-vcpkg-base",
"hidden": true,
"inherits": ["wasm-base", "vcpkg-base"],
"cacheVariables": {
"VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "$env{EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
"VCPKG_TARGET_TRIPLET": "wasm32-emscripten",
"VCPKG_MANIFEST_FEATURES": "tests;imgui"
}
},
{
"name": "wasm-debug-vcpkg",
"displayName": "WebAssembly Debug (vcpkg)",
"inherits": ["wasm-vcpkg-base", "debug"]
},
{
"name": "wasm-release-vcpkg",
"displayName": "WebAssembly Release (vcpkg)",
"inherits": ["wasm-vcpkg-base", "release"]
},
{
"name": "mingw-base",
"hidden": true,
"inherits": "base",
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "mingw-vcpkg-base",
"hidden": true,
"inherits": ["mingw-base", "vcpkg-base"],
"environment": {
"VCPKG_DEFAULT_HOST_TRIPLET": "x64-mingw-dynamic"
},
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "x64-mingw-dynamic",
"VCPKG_HOST_TRIPLET": "x64-mingw-dynamic",
"VCPKG_MANIFEST_FEATURES": "tests;imgui"
}
},
{
"name": "mingw-debug",
"displayName": "MinGW x64 Debug",
"inherits": ["mingw-base", "debug"]
},
{
"name": "mingw-release",
"displayName": "MinGW x64 Release",
"inherits": ["mingw-base", "release"]
},
{
"name": "mingw-debug-vcpkg",
"displayName": "MinGW x64 Debug (vcpkg)",
"inherits": ["mingw-vcpkg-base", "debug"]
},
{
"name": "mingw-release-vcpkg",
"displayName": "MinGW x64 Release (vcpkg)",
"inherits": ["mingw-vcpkg-base", "release"]
},
{
"name": "mingw-ucrt-release-vcpkg",
"displayName": "MinGW UCRT64 Release (vcpkg)",
"inherits": ["mingw-vcpkg-base", "release"]
},
{
"name": "mingw32-base",
"hidden": true,
"inherits": "base",
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "mingw32-vcpkg-base",
"hidden": true,
"inherits": ["mingw32-base", "vcpkg-base"],
"environment": {
"VCPKG_DEFAULT_HOST_TRIPLET": "x86-mingw-dynamic"
},
"cacheVariables": {
"VCPKG_TARGET_TRIPLET": "x86-mingw-dynamic",
"VCPKG_HOST_TRIPLET": "x86-mingw-dynamic",
"VCPKG_MANIFEST_FEATURES": "tests;imgui"
}
},
{
"name": "mingw32-debug",
"displayName": "MinGW x86 Debug",
"inherits": ["mingw32-base", "debug"]
},
{
"name": "mingw32-release",
"displayName": "MinGW x86 Release",
"inherits": ["mingw32-base", "release"]
},
{
"name": "mingw32-debug-vcpkg",
"displayName": "MinGW x86 Debug (vcpkg)",
"inherits": ["mingw32-vcpkg-base", "debug"]
},
{
"name": "mingw32-release-vcpkg",
"displayName": "MinGW x86 Release (vcpkg)",
"inherits": ["mingw32-vcpkg-base", "release"]
}
]
}

37
CODEOWNERS Normal file
View File

@@ -0,0 +1,37 @@
## List of maintainers for the omath library
## This file purpose is to give newcomers to the project the responsible
## developers when submitting a pull request on GitHub, or opening a bug
## report in issues.
## This file will notably establish who is responsible for a specific
## area of omath. Being a maintainer means the following:
## - that person has good knownledge in the area
## - that person is able to enforce consistency in the area
## - that person may be available for giving help in the area
## - that person has push access on the repository
## Being a maintainer does not mean the following:
## - that person is dedicated to the area
## - that person is working full-time on the area/on omath
## - that person is paid
## - that person is always available
# omath core source code
/source @orange-cpp
/include @orange-cpp
# Tests and becnchmarks
/benchmark @orange-cpp
/tests @orange-cpp @luadebug
# Examples and documentation
/examples @luadebug @orange-cpp
/docs @orange-cpp
# Misc like formating
/scripts @luadebug
/pixi @luadebug
# CI/CD
/.github/workflows @luadbg @orange-cpp

View File

@@ -1,93 +1,201 @@
## 🎯 Goal
# UNIVERSAL DECLARATION OF CODE OF CONDUCT
_Declaration of Community Rights and Responsibilities_
My goal is to provide a space where it is safe for everyone to contribute to,
and get support for, open-source software in a respectful and cooperative
manner.
## Preamble
I value all contributions and want to make this project and its
surrounding community a place for everyone.
Whereas the Orange++ community is founded on cooperation, mutual respect and support for the development of open-source software;
As members, contributors, and everyone else who may participate in the
development, I strive to keep the entire experience civil.
Whereas it is essential that all participants can contribute and seek assistance in an environment that is safe, inclusive and free from discrimination and harassment;
## 📜 Standards
Whereas the dignity and equality of all participants, regardless of their traits or background, must be respected and protected;
Our community standards exist in order to make sure everyone feels comfortable
contributing to the project(s) together.
Now, therefore, this Community Code of Conduct is proclaimed as a common standard of behaviour for all members, contributors and participants in projects led by Orange++ and its official communities.
Our standards are:
- Do not harass, attack, or in any other way discriminate against anyone, including
for their protected traits, including, but not limited to, sex, religion, race,
appearance, gender, identity, nationality, sexuality, etc.
- Do not go off-topic, do not post spam.
- Treat everyone with respect.
---
Examples of breaking each rule respectively include:
- Harassment, bullying or inappropriate jokes about another person.
- Posting distasteful imagery, trolling, or posting things unrelated to the topic at hand.
- Treating someone as worse because of their lack of understanding of an issue.
## Article 1
## ⚡ Enforcement
This Code of Conduct establishes standards of behaviour intended to:
Enforcement of this CoC is done by Orange++ and/or other core contributors.
1. Provide a safe and welcoming environment for all participants.
2. Encourage respectful and constructive collaboration.
3. Prevent harassment, discrimination, and other harmful conduct.
I, as the core developer, will strive my best to keep this community civil and
following the standards outlined above.
All individuals who participate in Orange++ projects or official communities, whether online or offline, are expected to adhere to this Code of Conduct.
### 🚩 Reporting incidents
---
If you believe an incident of breaking these standards has occurred, but nobody has
taken appropriate action, you can privately contact the people responsible for dealing
with such incidents in multiple ways:
## Article 2
All participants are equal in dignity and rights within the community.
No person shall be harassed, attacked, or discriminated against on the basis of protected or personal traits, including but not limited to:
- sex;
- religion or belief;
- race or ethnicity;
- appearance;
- gender or gender identity;
- nationality;
- sexual orientation;
- or any other similar characteristic.
Treating someone as lesser or unworthy because of their knowledge, experience, or level of understanding of an issue is incompatible with this Code.
---
## Article 3
Participants shall treat one another with respect at all times.
Participants shall:
1. Engage in discussion in good faith and assume good intent where reasonable.
2. Provide feedback and criticism in a constructive and considerate manner.
3. Recognize that people have different backgrounds, perspectives, and levels of expertise.
Examples of conduct contrary to this Article include, but are not limited to:
- harassment, bullying, personal attacks or degrading comments;
- inappropriate or offensive jokes or remarks about another person;
- persistent disruption of discussions or activities.
---
## Article 4
Participants shall remain on topic and avoid posting spam or irrelevant material.
Content that is distasteful, deliberately inflammatory, or unrelated to the project or discussion at hand is prohibited.
Examples of prohibited conduct under this Article include:
- posting trolling or inflammatory messages;
- sharing disturbing or inappropriate imagery unrelated to the topic;
- repeatedly derailing conversations away from their intended purpose.
---
## Article 5
The following standards shall guide all participation in Orange++ projects and official communities:
1. Do not harass, attack, or discriminate against any person.
2. Do not go off-topic and do not post spam.
3. Treat all participants with respect.
These standards apply equally to maintainers, contributors, and all other participants, regardless of status or seniority.
---
## Article 6
Enforcement of this Code of Conduct is carried out by Orange++ and/or other core contributors (hereinafter “members”).
Members shall strive to:
1. Act fairly, consistently, and transparently.
2. Consider the context and severity of each incident.
3. Maintain a civil and welcoming environment for the community as a whole.
Where appropriate, members may consult individuals with relevant lived experience, particularly when an incident concerns a marginalized group, while preserving confidentiality as required by this Code.
---
## Article 7
Any participant who believes that a breach of this Code of Conduct has occurred and has not been appropriately addressed may report the incident privately.
Reports may be submitted through any of the following channels:
**E-mail**
***E-Mail***
- `orange-cpp@yandex.ru`
***Discord***
**Discord**
- `@orange_cpp`
***Telegram***
**Telegram**
- `@orange_cpp`
I guarantee your privacy and will not share those reports with anyone.
The reporting partys privacy shall be respected, and reports shall not be shared beyond those responsible for handling them, except where required by law or with the explicit consent of the reporting party.
## ⚖️ Enforcement Strategy
---
Depending on the severity of the infraction, any action from the list below may be applied.
Please keep in mind cases are reviewed on a per-case basis and members are the ultimate
deciding factor in the type of punishment.
## Article 8
If the matter benefited from an outside opinion, a member might reach for more opinions
from people unrelated, however, the final decision regarding the action
to be taken is still up to the member.
Depending on the nature and severity of the infraction, and taking into account past behaviour, members may apply one or more of the following measures.
For example, if the matter at hand regards a representative of a marginalized group or minority,
the member might ask for a first-hand opinion from another representative of such group.
**1. Correction / Edit**
### ✏️ Correction/Edit
Where a message is misleading, poorly worded, or likely to cause misunderstanding, members may:
If your message is found to be misleading or poorly worded, a member might
edit your message.
1. Request that the author clarify or correct the message; or
2. Edit the message where the platform permits and such action is appropriate and transparent.
### ⚠️ Warning/Deletion
**2. Warning / Deletion**
If your message is found inappropriate, a member might give you a public or private warning,
and/or delete your message.
Where a message is inappropriate or in breach of the standards, members may:
### 🔇 Mute
1. Issue a public or private warning; and/or
2. Delete the message.
If your message is disruptive, or you have been repeatedly violating the standards,
a member might mute (or temporarily ban) you.
**3. Mute / Temporary Ban**
### ⛔ Ban
Where a participant is repeatedly violating the standards, or where their behaviour is significantly disruptive, members may:
If your message is hateful, very disruptive, or other, less serious infractions are repeated
ignoring previous punishments, a member might ban you permanently.
1. Temporarily mute the participant; or
2. Temporarily suspend or ban the participant from the community.
## 🔎 Scope
**4. Permanent Ban**
This CoC shall apply to all projects ran under the Orange++ lead and all _official_ communities
outside of GitHub.
Where a message is hateful or severely disruptive, or where less serious infractions are repeated despite prior measures, members may permanently ban the participant.
However, it is worth noting that official communities outside of GitHub might have their own,
additional sets of rules.
Each case shall be considered individually. The final decision regarding the appropriate measure lies with the members responsible for enforcement.
---
## Article 9
Reports of misconduct and information regarding enforcement actions shall be handled with care and confidentiality.
The personal data of reporters, witnesses, and involved parties shall not be disclosed to third parties, except:
1. Where such disclosure is required by law; or
2. Where explicit consent has been given by the person concerned.
The maintainer guarantees that every report will be treated with discretion and respect.
---
## Article 10
This Code of Conduct applies to:
1. All projects led under the Orange++ name or leadership; and
2. All official communities associated with Orange++ outside of GitHub.
Official communities outside of GitHub may maintain additional rules specific to their platform. In case of overlap, participants are expected to follow:
1. The rules of the platform or community; and
2. This Code of Conduct, insofar as it is applicable.
---
## Article 11
This Code of Conduct shall be interpreted in a manner consistent with its purpose: to promote a safe, respectful and inclusive community.
The maintainer and core contributors may review and revise this Code of Conduct periodically in light of community needs and experience.
Significant changes should be communicated to the community in a timely and clear manner.
---
## Article 12
Nothing in this Community Code of Conduct may be interpreted as granting to any maintainer, member, contributor, or participant any right to engage in any activity or to perform any act that aims at undermining, limiting, or destroying the rights, protections, and standards set forth herein.
No rule, policy, custom, or decision within the Orange++ projects or their official communities may be invoked to justify harassment, discrimination, retaliation, or any other conduct contrary to this Code of Conduct.

View File

@@ -1,6 +1,6 @@
# 📥Installation Guide
## <img width="28px" src="https://vcpkg.io/assets/mark/mark.svg" /> Using vcpkg
## <img width="28px" src="https://vcpkg.io/assets/mark/mark.svg" /> Using vcpkg (recomended)
**Note**: Support vcpkg for package management
1. Install [vcpkg](https://github.com/microsoft/vcpkg)
2. Run the following command to install the orange-math package:
@@ -28,6 +28,46 @@ target("...")
add_packages("omath")
```
## <img width="28px" src="https://github.githubassets.com/favicons/favicon.svg" /> Using prebuilt binaries (GitHub Releases)
**Note**: This is the fastest option if you dont want to build from source.
1. **Go to the Releases page**
- Open the projects GitHub **Releases** page and choose the latest version.
2. **Download the correct asset for your platform**
- Pick the archive that matches your OS and architecture (for example: Windows x64 / Linux x64 / macOS arm64).
3. **Extract the archive**
- You should end up with something like:
- `include/` (headers)
- `lib/` or `bin/` (library files / DLLs)
- sometimes `cmake/` (CMake package config)
4. **Use it in your project**
### Option A: CMake package (recommended if the release includes CMake config files)
If the extracted folder contains something like `lib/cmake/omath` or `cmake/omath`, you can point CMake to it:
```cmake
# Example: set this to the extracted prebuilt folder
list(APPEND CMAKE_PREFIX_PATH "path/to/omath-prebuilt")
find_package(omath CONFIG REQUIRED)
target_link_libraries(main PRIVATE omath::omath)
```
### Option B: Manual include + link (works with any layout)
If theres no CMake package config, link it manually:
```cmake
target_include_directories(main PRIVATE "path/to/omath-prebuilt/include")
# Choose ONE depending on what you downloaded:
# - Static library: .lib / .a
# - Shared library: .dll + .lib import (Windows), .so (Linux), .dylib (macOS)
target_link_directories(main PRIVATE "path/to/omath-prebuilt/lib")
target_link_libraries(main PRIVATE omath) # or the actual library filename
```
## <img width="28px" src="https://upload.wikimedia.org/wikipedia/commons/e/ef/CMake_logo.svg?" /> Build from source using CMake
1. **Preparation**

44
LICENSE
View File

@@ -1,4 +1,4 @@
Copyright (C) 2024-2025 Orange++ <orange_github@proton.me>
Copyright (C) 2023-2026 Orange++ orange_github@proton.me
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -15,45 +15,3 @@ freely, subject to the following restrictions:
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
4. If you are an employee, contractor, volunteer, representative,
or have any other affiliation (past or present)
with any of the following entities:
* "Advertising Placement Services" LLC
* "NEW SOLUTIONS VERTICALS" LLC
* "Autoexpert" LLC
* "Creditit" LLC
* "Yandex.Taxi" LLC
* "Yandex.Eda" LLC
* "Yandex.Lavka" LLC
* "Yandex.Telecom" LLC
* "Yandex.Cloud" LLC
* "Micromobility" LLC
* "MM-Tech" LLC
* "Carsharing" LLC
* "Yandex.Drive" LLC
* "EDADIL PROMO" LLC
* "Kinopoisk" LLC
* "Yandex.Music" LLC
* "Refueling (Yandex.Zapravki)" LLC
* "Yandex.Pay" LLC
* "Financial and Payment Technologies" LLC
* "Yandex.Delivery" LLC
* "Delivery Club" LLC
* "Yandex.Check" LLC
* "SMB-Service" LLC
* "ADV-TECH" LLC
* "Yandex Fantech" LLC
* "Yandex Smena" LLC
* "Market.Operations" LLC
* "Yandex.Market" LLC
* "ID Tech" LLC
* "Yandex.Crowd" LLC
* "Yandex" LLC
* "Rutube" LLC
* "Kaspersky" LLC
Or if you represent or are associated with any legal, organizational, or
professional entity providing services to or on behalf of the aforementioned entities:
You are expressly forbidden from accessing, using, modifying, distributing, or
interacting with the Software and its source code in any form. You must immediately
delete or destroy any physical or digital copies of the Software and/or
its source code, including any derivative works, tools, or information obtained from the Software.

View File

@@ -2,9 +2,10 @@
![banner](docs/images/logos/omath_logo_macro.png)
![Static Badge](https://img.shields.io/badge/license-libomath-orange)
![GitHub License](https://img.shields.io/github/license/orange-cpp/omath)
![GitHub contributors](https://img.shields.io/github/contributors/orange-cpp/omath)
![GitHub top language](https://img.shields.io/github/languages/top/orange-cpp/omath)
![GitHub repo size](https://img.shields.io/github/repo-size/orange-cpp/omath)
[![CodeFactor](https://www.codefactor.io/repository/github/orange-cpp/omath/badge)](https://www.codefactor.io/repository/github/orange-cpp/omath)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/orange-cpp/omath/cmake-multi-platform.yml)
[![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/orange-math.svg)](https://repology.org/project/orange-math/versions)
@@ -43,7 +44,7 @@ It provides the latest features, is highly customizable, has all for cheat devel
</a>
</div>
## 🚀 Quick Example
## Quick Example
```cpp
#include <omath/omath.hpp>
@@ -68,20 +69,21 @@ if (auto screen = camera.world_to_screen(world_position)) {
}
```
**[➡️ See more examples and tutorials][TUTORIALS]**
**[See more examples and tutorials][TUTORIALS]**
# Features
- **🚀 Efficiency**: Optimized for performance, ensuring quick computations using AVX2.
- **🎯 Versatility**: Includes a wide array of mathematical functions and algorithms.
- **Ease of Use**: Simplified interface for convenient integration into various projects.
- **🎮 Projectile Prediction**: Projectile prediction engine with O(N) algo complexity, that can power you projectile aim-bot.
- **📐 3D Projection**: No need to find view-projection matrix anymore you can make your own projection pipeline.
- **💥 Collision Detection**: Production ready code to handle collision detection by using simple interfaces.
- **📦 No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution
- **🔧 Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types!
- **🎯 Engine support**: Supports coordinate systems of **Source, Unity, Unreal, Frostbite, IWEngine and canonical OpenGL**.
- **🌍 Cross platform**: Supports Windows, MacOS and Linux.
- **🔍 Algorithms**: Has ability to scan for byte pattern with wildcards in PE files/modules, binary slices, works even with Wine apps.
# Features
- **Efficiency**: Optimized for performance, ensuring quick computations using AVX2.
- **Versatility**: Includes a wide array of mathematical functions and algorithms.
- **Ease of Use**: Simplified interface for convenient integration into various projects.
- **Projectile Prediction**: Projectile prediction engine with O(N) algo complexity, that can power you projectile aim-bot.
- **3D Projection**: No need to find view-projection matrix anymore you can make your own projection pipeline.
- **Collision Detection**: Production ready code to handle collision detection by using simple interfaces.
- **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution
- **Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types!
- **Engine support**: Supports coordinate systems of **Source, Unity, Unreal, Frostbite, IWEngine, CryEngine and canonical OpenGL**.
- **Cross platform**: Supports Windows, MacOS and Linux.
- **Algorithms**: Has ability to scan for byte pattern with wildcards in ELF/Mach-O/PE files/modules, binary slices, works even with Wine apps.
- **Battle tested**: It's already used by some big players on the market like wraith.su and bluedream.ltd
<div align = center>
# Gallery
@@ -106,12 +108,16 @@ if (auto screen = camera.world_to_screen(world_position)) {
![TF2 Preview]
<br>
![OpenGL Preview]
<br>
<br>
</div>
## 📚 Documentation
## Documentation
- **[Getting Started Guide](http://libomath.org/getting_started/)** - Installation and first steps
- **[API Overview](http://libomath.org/api_overview/)** - Complete API reference
@@ -120,14 +126,14 @@ if (auto screen = camera.world_to_screen(world_position)) {
- **[Troubleshooting](http://libomath.org/troubleshooting/)** - Solutions to common issues
- **[Best Practices](http://libomath.org/best_practices/)** - Guidelines for effective usage
## 🤝 Community & Support
## Community & Support
- **Discord**: [Join our community](https://discord.gg/eDgdaWbqwZ)
- **Telegram**: [@orangennotes](https://t.me/orangennotes)
- **Issues**: [Report bugs or request features](https://github.com/orange-cpp/omath/issues)
- **Contributing**: See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines
# 💘 Acknowledgments
# Acknowledgments
- [All contributors](https://github.com/orange-cpp/omath/graphs/contributors)
<!----------------------------------{ Images }--------------------------------->
@@ -135,6 +141,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

View File

@@ -1 +1 @@
4.2.0
5.0.0

View File

@@ -1,11 +1,11 @@
project(omath_benchmark)
file(GLOB_RECURSE OMATH_BENCHMARK_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
add_executable(${PROJECT_NAME} ${OMATH_BENCHMARK_SOURCES})
set_target_properties(${PROJECT_NAME} PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
set_target_properties(
${PROJECT_NAME}
PROPERTIES 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}"
CXX_STANDARD 23
@@ -14,6 +14,11 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
if(TARGET benchmark::benchmark) # Benchmark is being linked as submodule
target_link_libraries(${PROJECT_NAME} PRIVATE benchmark::benchmark omath)
else()
find_package(benchmark CONFIG REQUIRED) # Benchmark is being linked as vcpkg package
find_package(benchmark CONFIG REQUIRED) # Benchmark is being linked as vcpkg
# package
target_link_libraries(${PROJECT_NAME} PRIVATE benchmark::benchmark omath)
endif()
if(OMATH_ENABLE_VALGRIND)
omath_setup_valgrind(${PROJECT_NAME})
endif()

67
cmake/Coverage.cmake Normal file
View File

@@ -0,0 +1,67 @@
# cmake/Coverage.cmake
include_guard(GLOBAL)
function(omath_setup_coverage TARGET_NAME)
if(ANDROID OR IOS OR EMSCRIPTEN)
return()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND MSVC)
target_compile_options(${TARGET_NAME} PRIVATE -fprofile-instr-generate -fcoverage-mapping
/Zi)
target_link_options(
${TARGET_NAME}
PRIVATE
-fprofile-instr-generate
-fcoverage-mapping
/DEBUG:FULL
/INCREMENTAL:NO
/PROFILE)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang")
target_compile_options(${TARGET_NAME} PRIVATE -fprofile-instr-generate -fcoverage-mapping
-g -O0)
target_link_options(${TARGET_NAME} PRIVATE -fprofile-instr-generate -fcoverage-mapping)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${TARGET_NAME} PRIVATE --coverage -g -O0)
target_link_options(${TARGET_NAME} PRIVATE --coverage)
endif()
if(TARGET coverage)
return()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND MSVC)
message(STATUS "MSVC detected: Use VS Code Coverage from CI workflow")
add_custom_target(
coverage
DEPENDS unit_tests
COMMAND $<TARGET_FILE:unit_tests>
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Running tests for coverage (use VS Code Coverage from CI)")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang")
add_custom_target(
coverage
DEPENDS unit_tests
COMMAND bash "${CMAKE_SOURCE_DIR}/scripts/coverage-llvm.sh" "${CMAKE_SOURCE_DIR}"
"${CMAKE_BINARY_DIR}" "$<TARGET_FILE:unit_tests>" "${CMAKE_BINARY_DIR}/coverage"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Running LLVM coverage")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_custom_target(
coverage
DEPENDS unit_tests
COMMAND $<TARGET_FILE:unit_tests> || true
COMMAND lcov --capture --directory "${CMAKE_BINARY_DIR}" --output-file
"${CMAKE_BINARY_DIR}/coverage.info" --ignore-errors mismatch,gcov
COMMAND
lcov --remove "${CMAKE_BINARY_DIR}/coverage.info" "*/tests/*" "*/gtest/*"
"*/googletest/*" "*/_deps/*" "/usr/*" --output-file
"${CMAKE_BINARY_DIR}/coverage.info" --ignore-errors unused
COMMAND genhtml "${CMAKE_BINARY_DIR}/coverage.info" --output-directory
"${CMAKE_BINARY_DIR}/coverage"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
COMMENT "Running lcov/genhtml")
endif()
endfunction()

41
cmake/Valgrind.cmake Normal file
View File

@@ -0,0 +1,41 @@
# cmake/Valgrind.cmake
if(DEFINED __OMATH_VALGRIND_INCLUDED)
return()
endif()
set(__OMATH_VALGRIND_INCLUDED TRUE)
find_program(VALGRIND_EXECUTABLE valgrind)
option(OMATH_ENABLE_VALGRIND "Enable Valgrind target for memory checking" ON)
if(OMATH_ENABLE_VALGRIND AND NOT TARGET valgrind_all)
add_custom_target(valgrind_all)
endif()
function(omath_setup_valgrind TARGET_NAME)
if(NOT OMATH_ENABLE_VALGRIND)
return()
endif()
if(NOT VALGRIND_EXECUTABLE)
message(WARNING "OMATH_ENABLE_VALGRIND is ON, but 'valgrind' executable was not found.")
return()
endif()
set(VALGRIND_FLAGS --leak-check=full --show-leak-kinds=all --track-origins=yes
--error-exitcode=99)
set(VALGRIND_TARGET "valgrind_${TARGET_NAME}")
if(NOT TARGET ${VALGRIND_TARGET})
add_custom_target(
${VALGRIND_TARGET}
DEPENDS ${TARGET_NAME}
COMMAND ${VALGRIND_EXECUTABLE} ${VALGRIND_FLAGS} $<TARGET_FILE:${TARGET_NAME}>
WORKING_DIRECTORY $<TARGET_FILE_DIR:${TARGET_NAME}>
COMMENT "Running Valgrind memory check on ${TARGET_NAME}..."
USES_TERMINAL)
add_dependencies(valgrind_all ${VALGRIND_TARGET})
endif()
endfunction()

465
docs/3d_primitives/mesh.md Normal file
View File

@@ -0,0 +1,465 @@
# `omath::primitives::Mesh` — 3D mesh with transformation support
> Header: `omath/3d_primitives/mesh.hpp`
> Namespace: `omath::primitives`
> Depends on: `omath::Vector3<T>`, `omath::Mat4X4`, `omath::Triangle<Vector3<T>>`
> Purpose: represent and transform 3D meshes in different engine coordinate systems
---
## Overview
`Mesh` represents a 3D polygonal mesh with vertex data and transformation capabilities. It stores:
* **Vertex buffer (VBO)** — array of 3D vertex positions
* **Index buffer (VAO)** — array of triangular faces (indices into VBO)
* **Transformation** — position, rotation, and scale with caching
The mesh supports transformation from local space to world space using engine-specific coordinate systems through the `MeshTrait` template parameter.
---
## Template Declaration
```cpp
template<class Mat4X4, class RotationAngles, class MeshTypeTrait, class Type = float>
class Mesh final;
```
### Template Parameters
* `Mat4X4` — Matrix type for transformations (typically `omath::Mat4X4`)
* `RotationAngles` — Rotation representation (e.g., `ViewAngles` with pitch/yaw/roll)
* `MeshTypeTrait` — Engine-specific transformation trait (see [Engine Traits](#engine-traits))
* `Type` — Scalar type for vertex coordinates (default `float`)
---
## Type Aliases
```cpp
using NumericType = Type;
```
Common engine-specific aliases:
```cpp
// Source Engine
using Mesh = omath::primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
// Unity Engine
using Mesh = omath::primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
// Unreal Engine
using Mesh = omath::primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
// Frostbite, IW Engine, OpenGL similar...
```
Use the pre-defined type aliases in engine namespaces:
```cpp
using namespace omath::source_engine;
Mesh my_mesh = /* ... */; // Uses SourceEngine::Mesh
```
---
## Data Members
### Vertex Data
```cpp
std::vector<Vector3<NumericType>> m_vertex_buffer; // VBO: vertex positions
std::vector<Vector3<std::size_t>> m_vertex_array_object; // VAO: face indices
```
* `m_vertex_buffer` — array of vertex positions in **local space**
* `m_vertex_array_object` — array of triangular faces, each containing 3 indices into `m_vertex_buffer`
**Public access**: These members are public for direct manipulation when needed.
---
## Constructor
```cpp
Mesh(std::vector<Vector3<NumericType>> vbo,
std::vector<Vector3<std::size_t>> vao,
Vector3<NumericType> scale = {1, 1, 1});
```
Creates a mesh from vertex and index data.
**Parameters**:
* `vbo` — vertex buffer (moved into mesh)
* `vao` — index buffer / vertex array object (moved into mesh)
* `scale` — initial scale (default `{1, 1, 1}`)
**Example**:
```cpp
std::vector<Vector3<float>> vertices = {
{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}
};
std::vector<Vector3<std::size_t>> faces = {
{0, 1, 2}, // Triangle 1
{0, 1, 3}, // Triangle 2
{0, 2, 3}, // Triangle 3
{1, 2, 3} // Triangle 4
};
using namespace omath::source_engine;
Mesh tetrahedron(std::move(vertices), std::move(faces));
```
---
## Transformation Methods
### Setting Transform Components
```cpp
void set_origin(const Vector3<NumericType>& new_origin);
void set_scale(const Vector3<NumericType>& new_scale);
void set_rotation(const RotationAngles& new_rotation_angles);
```
Update the mesh's transformation. **Side effect**: invalidates the cached transformation matrix, which will be recomputed on the next `get_to_world_matrix()` call.
**Example**:
```cpp
mesh.set_origin({10, 0, 5});
mesh.set_scale({2, 2, 2});
ViewAngles angles;
angles.pitch = PitchAngle::from_degrees(45.0f);
angles.yaw = YawAngle::from_degrees(30.0f);
mesh.set_rotation(angles);
```
### Getting Transform Components
```cpp
[[nodiscard]] const Vector3<NumericType>& get_origin() const;
[[nodiscard]] const Vector3<NumericType>& get_scale() const;
[[nodiscard]] const RotationAngles& get_rotation_angles() const;
```
Retrieve current transformation components.
### Transformation Matrix
```cpp
[[nodiscard]] const Mat4X4& get_to_world_matrix() const;
```
Returns the cached local-to-world transformation matrix. The matrix is computed lazily on first access after any transformation change:
```
M = Translation(origin) × Scale(scale) × Rotation(angles)
```
The rotation matrix is computed using the engine-specific `MeshTrait::rotation_matrix()` method.
**Caching**: The matrix is stored in a `mutable std::optional` and recomputed only when invalidated by `set_*` methods.
---
## Vertex Transformation
### `vertex_to_world_space`
```cpp
[[nodiscard]]
Vector3<float> vertex_to_world_space(const Vector3<float>& vertex) const;
```
Transforms a vertex from local space to world space by multiplying with the transformation matrix.
**Algorithm**:
1. Convert vertex to column matrix: `[x, y, z, 1]ᵀ`
2. Multiply by transformation matrix: `M × vertex`
3. Extract the resulting 3D position
**Usage**:
```cpp
Vector3<float> local_vertex{1, 0, 0};
Vector3<float> world_vertex = mesh.vertex_to_world_space(local_vertex);
```
**Note**: This is used internally by `MeshCollider` to provide world-space support functions for GJK/EPA.
---
## Face Transformation
### `make_face_in_world_space`
```cpp
[[nodiscard]]
Triangle<Vector3<float>> make_face_in_world_space(
const std::vector<Vector3<std::size_t>>::const_iterator vao_iterator
) const;
```
Creates a triangle in world space from a face index iterator.
**Parameters**:
* `vao_iterator` — iterator to an element in `m_vertex_array_object`
**Returns**: `Triangle` with all three vertices transformed to world space.
**Example**:
```cpp
for (auto it = mesh.m_vertex_array_object.begin();
it != mesh.m_vertex_array_object.end();
++it) {
Triangle<Vector3<float>> world_triangle = mesh.make_face_in_world_space(it);
// Render or process the triangle
}
```
---
## Usage Examples
### Creating a Box Mesh
```cpp
using namespace omath::source_engine;
std::vector<Vector3<float>> box_vbo = {
// Bottom face
{-0.5f, -0.5f, 0.0f}, { 0.5f, -0.5f, 0.0f},
{ 0.5f, 0.5f, 0.0f}, {-0.5f, 0.5f, 0.0f},
// Top face
{-0.5f, -0.5f, 1.0f}, { 0.5f, -0.5f, 1.0f},
{ 0.5f, 0.5f, 1.0f}, {-0.5f, 0.5f, 1.0f}
};
std::vector<Vector3<std::size_t>> box_vao = {
// Bottom
{0, 1, 2}, {0, 2, 3},
// Top
{4, 6, 5}, {4, 7, 6},
// Sides
{0, 4, 5}, {0, 5, 1},
{1, 5, 6}, {1, 6, 2},
{2, 6, 7}, {2, 7, 3},
{3, 7, 4}, {3, 4, 0}
};
Mesh box(std::move(box_vbo), std::move(box_vao));
box.set_origin({0, 0, 50});
box.set_scale({10, 10, 10});
```
### Transforming Mesh Over Time
```cpp
void update_mesh(Mesh& mesh, float delta_time) {
// Rotate mesh
auto rotation = mesh.get_rotation_angles();
rotation.yaw = YawAngle::from_degrees(
rotation.yaw.as_degrees() + 45.0f * delta_time
);
mesh.set_rotation(rotation);
// Oscillate position
auto origin = mesh.get_origin();
origin.z = 50.0f + 10.0f * std::sin(current_time * 2.0f);
mesh.set_origin(origin);
}
```
### Collision Detection
```cpp
using namespace omath::collision;
using namespace omath::source_engine;
Mesh mesh_a(vbo_a, vao_a);
mesh_a.set_origin({0, 0, 0});
Mesh mesh_b(vbo_b, vao_b);
mesh_b.set_origin({5, 0, 0});
MeshCollider collider_a(std::move(mesh_a));
MeshCollider collider_b(std::move(mesh_b));
auto result = GjkAlgorithm<MeshCollider<Mesh>>::check_collision(
collider_a, collider_b
);
```
### Rendering Transformed Triangles
```cpp
void render_mesh(const Mesh& mesh) {
for (auto it = mesh.m_vertex_array_object.begin();
it != mesh.m_vertex_array_object.end();
++it) {
Triangle<Vector3<float>> tri = mesh.make_face_in_world_space(it);
// Draw triangle with your renderer
draw_triangle(tri.m_vertex1, tri.m_vertex2, tri.m_vertex3);
}
}
```
---
## Engine Traits
Each game engine has a corresponding `MeshTrait` that provides the `rotation_matrix` function:
```cpp
class MeshTrait final {
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
};
```
### Available Engines
| Engine | Namespace | Header |
|--------|-----------|--------|
| Source Engine | `omath::source_engine` | `engines/source_engine/mesh.hpp` |
| Unity | `omath::unity_engine` | `engines/unity_engine/mesh.hpp` |
| Unreal | `omath::unreal_engine` | `engines/unreal_engine/mesh.hpp` |
| Frostbite | `omath::frostbite_engine` | `engines/frostbite_engine/mesh.hpp` |
| IW Engine | `omath::iw_engine` | `engines/iw_engine/mesh.hpp` |
| OpenGL | `omath::opengl_engine` | `engines/opengl_engine/mesh.hpp` |
**Example** (Source Engine):
```cpp
using namespace omath::source_engine;
// Uses source_engine::MeshTrait automatically
Mesh my_mesh(vertices, indices);
```
See [MeshTrait Documentation](#mesh-trait-documentation) for engine-specific details.
---
## Performance Considerations
### Matrix Caching
The transformation matrix is computed lazily and cached:
* **First access**: O(matrix multiply) ≈ 64 float operations
* **Subsequent access**: O(1) — returns cached matrix
* **Cache invalidation**: Any `set_*` call invalidates the cache
**Best practice**: Batch transformation updates before accessing the matrix:
```cpp
// Good: single matrix recomputation
mesh.set_origin(new_origin);
mesh.set_rotation(new_rotation);
mesh.set_scale(new_scale);
auto matrix = mesh.get_to_world_matrix(); // Computes once
// Bad: three matrix recomputations
mesh.set_origin(new_origin);
auto m1 = mesh.get_to_world_matrix(); // Compute
mesh.set_rotation(new_rotation);
auto m2 = mesh.get_to_world_matrix(); // Compute again
mesh.set_scale(new_scale);
auto m3 = mesh.get_to_world_matrix(); // Compute again
```
### Memory Layout
* **VBO**: Contiguous `std::vector` for cache-friendly access
* **VAO**: Contiguous indices for cache-friendly face iteration
* **Matrix**: Cached in `std::optional` (no allocation)
### Transformation Cost
* `vertex_to_world_space`: ~15-20 FLOPs per vertex (4×4 matrix multiply)
* `make_face_in_world_space`: ~60 FLOPs (3 vertices)
For high-frequency transformations, consider:
* Caching transformed vertices if the mesh doesn't change
* Using simpler proxy geometry for collision
* Batching transformations
---
## Coordinate System Details
Different engines use different coordinate systems:
| Engine | Up Axis | Forward Axis | Handedness |
|--------|---------|--------------|------------|
| Source | +Z | +Y | Right |
| Unity | +Y | +Z | Left |
| Unreal | +Z | +X | Left |
| Frostbite | +Y | +Z | Right |
| IW Engine | +Z | +Y | Right |
| OpenGL | +Y | +Z | Right |
The `MeshTrait::rotation_matrix` function accounts for these differences, ensuring correct transformations in each engine's space.
---
## Limitations & Edge Cases
### Empty Mesh
A mesh with no vertices or faces is valid but not useful:
```cpp
Mesh empty_mesh({}, {}); // Valid but meaningless
```
For collision detection, ensure `m_vertex_buffer` is non-empty.
### Index Validity
No bounds checking is performed on indices in `m_vertex_array_object`. Ensure all indices are valid:
```cpp
assert(face.x < mesh.m_vertex_buffer.size());
assert(face.y < mesh.m_vertex_buffer.size());
assert(face.z < mesh.m_vertex_buffer.size());
```
### Degenerate Triangles
Faces with duplicate indices or collinear vertices will produce degenerate triangles. The mesh doesn't validate this; users must ensure clean geometry.
### Thread Safety
* **Read-only**: Safe to read from multiple threads (including const methods)
* **Modification**: Not thread-safe; synchronize `set_*` calls externally
* **Matrix cache**: Uses `mutable` member; not thread-safe even for const methods
---
## See Also
- [MeshCollider Documentation](../collision/mesh_collider.md) - Collision wrapper for meshes
- [GJK Algorithm Documentation](../collision/gjk_algorithm.md) - Uses mesh for collision detection
- [EPA Algorithm Documentation](../collision/epa_algorithm.md) - Penetration depth with meshes
- [Triangle Documentation](../linear_algebra/triangle.md) - Triangle primitive
- [Mat4X4 Documentation](../linear_algebra/mat.md) - Transformation matrices
- [Box Documentation](box.md) - Box primitive
- [Plane Documentation](plane.md) - Plane primitive
---
## Mesh Trait Documentation
For engine-specific `MeshTrait` details, see:
- [Source Engine MeshTrait](../engines/source_engine/mesh_trait.md)
- [Unity Engine MeshTrait](../engines/unity_engine/mesh_trait.md)
- [Unreal Engine MeshTrait](../engines/unreal_engine/mesh_trait.md)
- [Frostbite Engine MeshTrait](../engines/frostbite/mesh_trait.md)
- [IW Engine MeshTrait](../engines/iw_engine/mesh_trait.md)
- [OpenGL Engine MeshTrait](../engines/opengl_engine/mesh_trait.md)
---
*Last updated: 13 Nov 2025*

View File

@@ -11,10 +11,10 @@ OMath is organized into several logical modules:
### Core Mathematics
- **Linear Algebra** - Vectors, matrices, triangles
- **Trigonometry** - Angles, view angles, trigonometric functions
- **3D Primitives** - Boxes, planes, geometric shapes
- **3D Primitives** - Boxes, planes, meshes, geometric shapes
### Game Development
- **Collision Detection** - Ray tracing, intersection tests
- **Collision Detection** - Ray tracing, GJK/EPA algorithms, mesh collision, intersection tests
- **Projectile Prediction** - Ballistics and aim-assist calculations
- **Projection** - Camera systems and world-to-screen transformations
- **Pathfinding** - A* algorithm, navigation meshes
@@ -131,6 +131,41 @@ omath::opengl_engine::Camera // OpenGL
## Collision Detection
### GJK/EPA Algorithms
Advanced convex shape collision detection using the Gilbert-Johnson-Keerthi and Expanding Polytope algorithms:
```cpp
namespace omath::collision {
template<class ColliderType>
class GjkAlgorithm;
template<class ColliderType>
class Epa;
}
```
**GJK (Gilbert-Johnson-Keerthi):**
* Detects collision between two convex shapes
* Returns a 4-point simplex when collision is detected
* O(k) complexity where k is typically < 20 iterations
* Works with any collider implementing `find_abs_furthest_vertex()`
**EPA (Expanding Polytope Algorithm):**
* Computes penetration depth and separation normal
* Takes GJK's output simplex as input
* Provides contact information for physics simulation
* Configurable iteration limit and convergence tolerance
**Supporting Types:**
| Type | Description | Key Features |
|------|-------------|--------------|
| `Simplex<VectorType>` | 1-4 point geometric simplex | Fixed capacity, GJK iteration support |
| `MeshCollider<MeshType>` | Convex mesh collider | Support function for GJK/EPA |
| `GjkHitInfo<VertexType>` | Collision result | Hit flag and simplex |
| `Epa::Result` | Penetration info | Depth, normal, iteration count |
### LineTracer
Ray-casting and line tracing utilities:
@@ -142,7 +177,7 @@ namespace omath::collision {
```
**Features:**
- Ray-triangle intersection
- Ray-triangle intersection (Möller-Trumbore algorithm)
- Ray-plane intersection
- Ray-box intersection
- Distance calculations
@@ -154,6 +189,14 @@ namespace omath::collision {
|------|-------------|-------------|
| `Plane` | Infinite plane | `intersects_ray()`, `distance_to_point()` |
| `Box` | Axis-aligned bounding box | `contains()`, `intersects()` |
| `Mesh` | Polygonal mesh with transforms | `vertex_to_world_space()`, `make_face_in_world_space()` |
**Mesh Features:**
* Vertex buffer (VBO) and index buffer (VAO/EBO) storage
* Position, rotation, and scale transformations
* Cached transformation matrix
* Engine-specific coordinate system support
* Compatible with `MeshCollider` for collision detection
---
@@ -241,6 +284,13 @@ Implements camera math for an engine:
- `calc_view_matrix()` - Build view matrix from angles and position
- `calc_projection_matrix()` - Build projection matrix from FOV and viewport
### MeshTrait
Provides mesh transformation for an engine:
- `rotation_matrix()` - Build rotation matrix from engine-specific angles
- Handles coordinate system differences (Y-up vs Z-up, left/right-handed)
- Used by `Mesh` class for local-to-world transformations
### PredEngineTrait
Provides physics/ballistics specific to an engine:
@@ -251,18 +301,18 @@ Provides physics/ballistics specific to an engine:
### Available Traits
| Engine | Camera Trait | Pred Engine Trait | Constants | Formulas |
|--------|--------------|-------------------|-----------|----------|
| Source Engine | ✓ | ✓ | ✓ | ✓ |
| Unity Engine | ✓ | ✓ | ✓ | ✓ |
| Unreal Engine | ✓ | ✓ | ✓ | ✓ |
| Frostbite | ✓ | ✓ | ✓ | ✓ |
| IW Engine | ✓ | ✓ | ✓ | ✓ |
| OpenGL | ✓ | ✓ | ✓ | ✓ |
| Engine | Camera Trait | Mesh Trait | Pred Engine Trait | Constants | Formulas |
|--------|--------------|------------|-------------------|-----------|----------|
| Source Engine | ✓ | ✓ | ✓ | ✓ | ✓ |
| Unity Engine | ✓ | ✓ | ✓ | ✓ | ✓ |
| Unreal Engine | ✓ | ✓ | ✓ | ✓ | ✓ |
| Frostbite | ✓ | ✓ | ✓ | ✓ | ✓ |
| IW Engine | ✓ | ✓ | ✓ | ✓ | ✓ |
| OpenGL | ✓ | ✓ | ✓ | ✓ | ✓ |
**Documentation:**
- See `docs/engines/<engine_name>/` for detailed per-engine docs
- Each engine has separate docs for camera_trait, pred_engine_trait, constants, and formulas
- Each engine has separate docs for camera_trait, mesh_trait, pred_engine_trait, constants, and formulas
---
@@ -524,4 +574,4 @@ UnityCamera camera{pos, SourceAngles{}}; // Wrong!
---
*Last updated: 1 Nov 2025*
*Last updated: 13 Nov 2025*

View File

@@ -0,0 +1,322 @@
# `omath::collision::Epa` — Expanding Polytope Algorithm for penetration depth
> Header: `omath/collision/epa_algorithm.hpp`
> Namespace: `omath::collision`
> Depends on: `Simplex<VertexType>`, collider types with `find_abs_furthest_vertex` method
> Algorithm: **EPA** (Expanding Polytope Algorithm) for penetration depth and contact normal
---
## Overview
The **EPA (Expanding Polytope Algorithm)** calculates the **penetration depth** and **separation normal** between two intersecting convex shapes. It is typically used as a follow-up to the GJK algorithm after a collision has been detected.
EPA takes a 4-point simplex containing the origin (from GJK) and iteratively expands it to find the point on the Minkowski difference closest to the origin. This point gives both:
* **Depth**: minimum translation distance to separate the shapes
* **Normal**: direction of separation (pointing from shape B to shape A)
`Epa` is a template class working with any collider type that implements the support function interface.
---
## `Epa::Result`
```cpp
struct Result final {
bool success{false}; // true if EPA converged
Vertex normal{}; // outward normal (from B to A)
float depth{0.0f}; // penetration depth
int iterations{0}; // number of iterations performed
int num_vertices{0}; // final polytope vertex count
int num_faces{0}; // final polytope face count
};
```
### Fields
* `success``true` if EPA successfully computed depth and normal; `false` if it failed to converge
* `normal` — unit vector pointing from shape B toward shape A (separation direction)
* `depth` — minimum distance to move shape A along `normal` to separate the shapes
* `iterations` — actual iteration count (useful for performance tuning)
* `num_vertices`, `num_faces` — final polytope size (for diagnostics)
---
## `Epa::Params`
```cpp
struct Params final {
int max_iterations{64}; // maximum iterations before giving up
float tolerance{1e-4f}; // absolute tolerance on distance growth
};
```
### Fields
* `max_iterations` — safety limit to prevent infinite loops (default 64)
* `tolerance` — convergence threshold: stop when distance grows less than this (default 1e-4)
---
## `Epa` Template Class
```cpp
template<class ColliderType>
class Epa final {
public:
using Vertex = typename ColliderType::VertexType;
static_assert(EpaVector<Vertex>, "VertexType must satisfy EpaVector concept");
// Solve for penetration depth and normal
[[nodiscard]]
static Result solve(
const ColliderType& a,
const ColliderType& b,
const Simplex<Vertex>& simplex,
const Params params = {}
);
};
```
### Precondition
The `simplex` parameter must:
* Have exactly 4 points (`simplex.size() == 4`)
* Contain the origin (i.e., be a valid GJK result with `hit == true`)
Violating this precondition leads to undefined behavior.
---
## Collider Requirements
Any type used as `ColliderType` must provide:
```cpp
// Type alias for vertex type (typically Vector3<float>)
using VertexType = /* ... */;
// Find the farthest point in world space along the given direction
[[nodiscard]]
VertexType find_abs_furthest_vertex(const VertexType& direction) const;
```
---
## Algorithm Details
### Expanding Polytope
EPA maintains a convex polytope (polyhedron) in Minkowski difference space `A - B`. Starting from the 4-point tetrahedron (simplex from GJK), it repeatedly:
1. **Find closest face** to the origin
2. **Support query** in the direction of the face normal
3. **Expand polytope** by adding the new support point
4. **Update faces** to maintain convexity
The algorithm terminates when:
* **Convergence**: the distance from origin to polytope stops growing (within tolerance)
* **Max iterations**: safety limit reached
* **Failure cases**: degenerate polytope or numerical issues
### Minkowski Difference
Like GJK, EPA operates in Minkowski difference space where `point = a - b` for points in shapes A and B. The closest point on this polytope to the origin gives the minimum separation.
### Face Winding
Faces are stored with outward-pointing normals. The algorithm uses a priority queue to efficiently find the face closest to the origin.
---
## Vertex Type Requirements
The `VertexType` must satisfy the `EpaVector` concept:
```cpp
template<class V>
concept EpaVector = requires(const V& a, const V& b, float s) {
{ a - b } -> std::same_as<V>;
{ 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>;
};
```
`omath::Vector3<float>` satisfies this concept.
---
## Usage Examples
### Basic EPA Usage
```cpp
using namespace omath::collision;
using namespace omath::source_engine;
// First, run GJK to detect collision
MeshCollider<Mesh> collider_a(mesh_a);
MeshCollider<Mesh> collider_b(mesh_b);
auto gjk_result = GjkAlgorithm<MeshCollider<Mesh>>::check_collision(
collider_a,
collider_b
);
if (gjk_result.hit) {
// Collision detected, use EPA to get penetration info
auto epa_result = Epa<MeshCollider<Mesh>>::solve(
collider_a,
collider_b,
gjk_result.simplex
);
if (epa_result.success) {
std::cout << "Penetration depth: " << epa_result.depth << "\n";
std::cout << "Separation normal: "
<< "(" << epa_result.normal.x << ", "
<< epa_result.normal.y << ", "
<< epa_result.normal.z << ")\n";
// Apply separation: move A away from B
Vector3<float> correction = epa_result.normal * epa_result.depth;
mesh_a.set_origin(mesh_a.get_origin() + correction);
}
}
```
### Custom Parameters
```cpp
// Use custom convergence settings
Epa<Collider>::Params params;
params.max_iterations = 128; // Allow more iterations for complex shapes
params.tolerance = 1e-5f; // Tighter tolerance for more accuracy
auto result = Epa<Collider>::solve(a, b, simplex, params);
```
### Physics Integration
```cpp
void resolve_collision(PhysicsBody& body_a, PhysicsBody& body_b) {
auto gjk_result = GjkAlgorithm<Collider>::check_collision(
body_a.collider, body_b.collider
);
if (!gjk_result.hit)
return; // No collision
auto epa_result = Epa<Collider>::solve(
body_a.collider,
body_b.collider,
gjk_result.simplex
);
if (epa_result.success) {
// Separate bodies
float mass_sum = body_a.mass + body_b.mass;
float ratio_a = body_b.mass / mass_sum;
float ratio_b = body_a.mass / mass_sum;
body_a.position += epa_result.normal * (epa_result.depth * ratio_a);
body_b.position -= epa_result.normal * (epa_result.depth * ratio_b);
// Apply collision response
apply_impulse(body_a, body_b, epa_result.normal);
}
}
```
---
## Performance Characteristics
* **Time complexity**: O(k × f) where k is iterations and f is faces per iteration (typically f grows slowly)
* **Space complexity**: O(n) where n is the number of polytope vertices (typically < 100)
* **Typical iterations**: 4-20 for most collisions
* **Worst case**: 64 iterations (configurable limit)
### Performance Tips
1. **Adjust max_iterations**: Balance accuracy vs. performance for your use case
2. **Tolerance tuning**: Larger tolerance = faster convergence but less accurate
3. **Shape complexity**: Simpler shapes (fewer faces) converge faster
4. **Deep penetrations**: Require more iterations; consider broad-phase separation
---
## Limitations & Edge Cases
* **Requires valid simplex**: Must be called with a 4-point simplex containing the origin (from successful GJK)
* **Convex shapes only**: Like GJK, EPA only works with convex colliders
* **Convergence failure**: Can fail to converge for degenerate or very thin shapes (check `result.success`)
* **Numerical precision**: Extreme scale differences or very small shapes may cause issues
* **Deep penetration**: Very deep intersections may require many iterations or fail to converge
### Error Handling
```cpp
auto result = Epa<Collider>::solve(a, b, simplex);
if (!result.success) {
// EPA failed to converge
// Fallback options:
// 1. Use a default separation (e.g., axis between centers)
// 2. Increase max_iterations and retry
// 3. Log a warning and skip this collision
std::cerr << "EPA failed after " << result.iterations << " iterations\n";
}
```
---
## Theory & Background
### Why EPA after GJK?
GJK determines **if** shapes intersect but doesn't compute penetration depth. EPA extends GJK's final simplex to find the exact depth and normal needed for:
* **Collision response** — separating objects realistically
* **Contact manifolds** — generating contact points for physics
* **Constraint solving** — iterative physics solvers
### Comparison with SAT
| Feature | EPA | SAT (Separating Axis Theorem) |
|---------|-----|-------------------------------|
| Works with | Any convex shape | Polytopes (faces/edges) |
| Penetration depth | Yes | Yes |
| Complexity | Iterative | Per-axis projection |
| Best for | General convex | Boxes, prisms |
| Typical speed | Moderate | Fast (few axes) |
EPA is more general; SAT is faster for axis-aligned shapes.
---
## Implementation Details
The EPA implementation in OMath:
* Uses a **priority queue** to efficiently find the closest face
* Maintains face winding for consistent normals
* Handles **edge cases**: degenerate faces, numerical instability
* Prevents infinite loops with iteration limits
* Returns detailed diagnostics (iteration count, polytope size)
---
## See Also
- [GJK Algorithm Documentation](gjk_algorithm.md) - Collision detection (required before EPA)
- [Simplex Documentation](simplex.md) - Input simplex structure
- [MeshCollider Documentation](mesh_collider.md) - Mesh-based collider
- [Mesh Documentation](../3d_primitives/mesh.md) - Mesh primitive
- [Tutorials - Collision Detection](../tutorials.md#tutorial-4-collision-detection) - Complete collision tutorial
- [API Overview](../api_overview.md) - High-level API reference
---
*Last updated: 13 Nov 2025*

View File

@@ -0,0 +1,216 @@
# `omath::collision::GjkAlgorithm` — Gilbert-Johnson-Keerthi collision detection
> Header: `omath/collision/gjk_algorithm.hpp`
> Namespace: `omath::collision`
> Depends on: `Simplex<VertexType>`, collider types with `find_abs_furthest_vertex` method
> Algorithm: **GJK** (Gilbert-Johnson-Keerthi) for convex shape collision detection
---
## Overview
The **GJK algorithm** determines whether two convex shapes intersect by iteratively constructing a simplex in Minkowski difference space. The algorithm is widely used in physics engines and collision detection systems due to its efficiency and robustness.
`GjkAlgorithm` is a template class that works with any collider type implementing the required support function interface:
* `find_abs_furthest_vertex(direction)` — returns the farthest point in the collider along the given direction.
The algorithm returns a `GjkHitInfo` containing:
* `hit` — boolean indicating whether the shapes intersect
* `simplex` — a 4-point simplex containing the origin (valid only when `hit == true`)
---
## `GjkHitInfo`
```cpp
template<class VertexType>
struct GjkHitInfo final {
bool hit{false}; // true if collision detected
Simplex<VertexType> simplex; // 4-point simplex (valid only if hit == true)
};
```
The `simplex` field is only meaningful when `hit == true` and contains 4 points. This simplex can be passed to the EPA algorithm for penetration depth calculation.
---
## `GjkAlgorithm`
```cpp
template<class ColliderType>
class GjkAlgorithm final {
using VertexType = typename ColliderType::VertexType;
public:
// Find support vertex in Minkowski difference
[[nodiscard]]
static VertexType find_support_vertex(
const ColliderType& collider_a,
const ColliderType& collider_b,
const VertexType& direction
);
// Check if two convex shapes intersect
[[nodiscard]]
static GjkHitInfo<VertexType> check_collision(
const ColliderType& collider_a,
const ColliderType& collider_b
);
};
```
---
## Collider Requirements
Any type used as `ColliderType` must provide:
```cpp
// Type alias for vertex type (typically Vector3<float>)
using VertexType = /* ... */;
// Find the farthest point in world space along the given direction
[[nodiscard]]
VertexType find_abs_furthest_vertex(const VertexType& direction) const;
```
Common collider types:
* `MeshCollider<MeshType>` — for arbitrary triangle meshes
* Custom colliders for spheres, boxes, capsules, etc.
---
## Algorithm Details
### Minkowski Difference
GJK operates in the **Minkowski difference** space `A - B`, where a point in this space represents the difference between points in shapes A and B. The shapes intersect if and only if the origin lies within this Minkowski difference.
### Support Function
The support function finds the point in the Minkowski difference farthest along a given direction:
```cpp
support(A, B, dir) = A.furthest(dir) - B.furthest(-dir)
```
This is computed by `find_support_vertex`.
### Simplex Iteration
The algorithm builds a simplex incrementally:
1. Start with an initial direction (typically vector between shape centers)
2. Add support vertices in directions that move the simplex toward the origin
3. Simplify the simplex to keep only points closest to the origin
4. Repeat until either:
* Origin is contained (collision detected, returns 4-point simplex)
* No progress can be made (no collision)
Maximum 64 iterations are performed to prevent infinite loops in edge cases.
---
## Usage Examples
### Basic Collision Check
```cpp
using namespace omath::collision;
using namespace omath::source_engine;
// Create mesh colliders
Mesh mesh_a = /* ... */;
Mesh mesh_b = /* ... */;
MeshCollider collider_a(mesh_a);
MeshCollider collider_b(mesh_b);
// Check for collision
auto result = GjkAlgorithm<MeshCollider<Mesh>>::check_collision(
collider_a,
collider_b
);
if (result.hit) {
std::cout << "Collision detected!\n";
// Can pass result.simplex to EPA for penetration depth
}
```
### Combined with EPA
```cpp
auto gjk_result = GjkAlgorithm<Collider>::check_collision(a, b);
if (gjk_result.hit) {
// Get penetration depth and normal using EPA
auto epa_result = Epa<Collider>::solve(
a, b, gjk_result.simplex
);
if (epa_result.success) {
std::cout << "Penetration depth: " << epa_result.depth << "\n";
std::cout << "Separation normal: " << epa_result.normal << "\n";
}
}
```
---
## Performance Characteristics
* **Time complexity**: O(k) where k is the number of iterations (typically < 20 for most cases)
* **Space complexity**: O(1) — only stores a 4-point simplex
* **Best case**: 4-8 iterations for well-separated objects
* **Worst case**: 64 iterations (hard limit)
* **Cache efficient**: operates on small fixed-size data structures
### Optimization Tips
1. **Initial direction**: Use vector between shape centers for faster convergence
2. **Early exit**: GJK quickly rejects non-intersecting shapes
3. **Warm starting**: Reuse previous simplex for continuous collision detection
4. **Broad phase**: Use spatial partitioning before GJK (AABB trees, grids)
---
## Limitations & Edge Cases
* **Convex shapes only**: GJK only works with convex colliders. For concave shapes, decompose into convex parts or use a mesh collider wrapper.
* **Degenerate simplices**: The algorithm handles degenerate cases, but numerical precision can cause issues with very thin or flat shapes.
* **Iteration limit**: Hard limit of 64 iterations prevents infinite loops but may miss collisions in extreme cases.
* **Zero-length directions**: The simplex update logic guards against zero-length vectors, returning safe fallbacks.
---
## Vertex Type Requirements
The `VertexType` must satisfy the `GjkVector` concept (defined in `simplex.hpp`):
```cpp
template<class V>
concept GjkVector = requires(const V& a, const V& b) {
{ -a } -> std::same_as<V>;
{ a - b } -> std::same_as<V>;
{ a.cross(b) } -> std::same_as<V>;
{ a.point_to_same_direction(b) } -> std::same_as<bool>;
};
```
`omath::Vector3<float>` satisfies this concept.
---
## See Also
- [EPA Algorithm Documentation](epa_algorithm.md) - Penetration depth calculation
- [Simplex Documentation](simplex.md) - Simplex data structure
- [MeshCollider Documentation](mesh_collider.md) - Mesh-based collider
- [Mesh Documentation](../3d_primitives/mesh.md) - Mesh primitive
- [LineTracer Documentation](line_tracer.md) - Ray-triangle intersection
- [Tutorials - Collision Detection](../tutorials.md#tutorial-4-collision-detection) - Complete collision tutorial
---
*Last updated: 13 Nov 2025*

View File

@@ -0,0 +1,371 @@
# `omath::collision::MeshCollider` — Convex hull collider for meshes
> Header: `omath/collision/mesh_collider.hpp`
> Namespace: `omath::collision`
> Depends on: `omath::primitives::Mesh`, `omath::Vector3<T>`
> Purpose: wrap a mesh to provide collision detection support for GJK/EPA
---
## Overview
`MeshCollider` wraps a `Mesh` object to provide the **support function** interface required by the GJK and EPA collision detection algorithms. The support function finds the vertex of the mesh farthest along a given direction, which is essential for constructing Minkowski difference simplices.
**Important**: `MeshCollider` assumes the mesh represents a **convex hull**. For non-convex shapes, you must either:
* Decompose into convex parts
* Use the convex hull of the mesh
* Use a different collision detection algorithm
---
## Template Declaration
```cpp
template<class MeshType>
class MeshCollider;
```
### MeshType Requirements
The `MeshType` must be an instantiation of `omath::primitives::Mesh` or provide:
```cpp
struct MeshType {
using NumericType = /* float, double, etc. */;
std::vector<Vector3<NumericType>> m_vertex_buffer;
// Transform vertex from local to world space
Vector3<NumericType> vertex_to_world_space(const Vector3<NumericType>&) const;
};
```
Common types:
* `omath::source_engine::Mesh`
* `omath::unity_engine::Mesh`
* `omath::unreal_engine::Mesh`
* `omath::frostbite_engine::Mesh`
* `omath::iw_engine::Mesh`
* `omath::opengl_engine::Mesh`
---
## Type Aliases
```cpp
using NumericType = typename MeshType::NumericType;
using VertexType = Vector3<NumericType>;
```
* `NumericType` — scalar type (typically `float`)
* `VertexType` — 3D vector type for vertices
---
## Constructor
```cpp
explicit MeshCollider(MeshType mesh);
```
Creates a collider from a mesh. The mesh is **moved** into the collider, so pass by value:
```cpp
omath::source_engine::Mesh my_mesh = /* ... */;
MeshCollider collider(std::move(my_mesh));
```
---
## Methods
### `find_furthest_vertex`
```cpp
[[nodiscard]]
const VertexType& find_furthest_vertex(const VertexType& direction) const;
```
Finds the vertex in the mesh's **local space** that has the maximum dot product with `direction`.
**Algorithm**: Linear search through all vertices (O(n) where n is vertex count).
**Returns**: Const reference to the vertex in `m_vertex_buffer`.
---
### `find_abs_furthest_vertex`
```cpp
[[nodiscard]]
VertexType find_abs_furthest_vertex(const VertexType& direction) const;
```
Finds the vertex farthest along `direction` and transforms it to **world space**. This is the primary method used by GJK/EPA.
**Steps**:
1. Find furthest vertex in local space using `find_furthest_vertex`
2. Transform to world space using `mesh.vertex_to_world_space()`
**Returns**: Vertex position in world coordinates.
**Usage in GJK**:
```cpp
// GJK support function for Minkowski difference
VertexType support = collider_a.find_abs_furthest_vertex(direction)
- collider_b.find_abs_furthest_vertex(-direction);
```
---
## Usage Examples
### Basic Collision Detection
```cpp
using namespace omath::collision;
using namespace omath::source_engine;
// Create meshes with vertex data
std::vector<Vector3<float>> vbo_a = {
{-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<Vector3<std::size_t>> vao_a = /* face indices */;
Mesh mesh_a(vbo_a, vao_a);
mesh_a.set_origin({0, 0, 0});
Mesh mesh_b(vbo_b, vao_b);
mesh_b.set_origin({5, 0, 0}); // Positioned away from mesh_a
// Wrap in colliders
MeshCollider<Mesh> collider_a(std::move(mesh_a));
MeshCollider<Mesh> collider_b(std::move(mesh_b));
// Run GJK
auto result = GjkAlgorithm<MeshCollider<Mesh>>::check_collision(
collider_a, collider_b
);
if (result.hit) {
std::cout << "Collision detected!\n";
}
```
### With EPA for Penetration Depth
```cpp
auto gjk_result = GjkAlgorithm<MeshCollider<Mesh>>::check_collision(
collider_a, collider_b
);
if (gjk_result.hit) {
auto epa_result = Epa<MeshCollider<Mesh>>::solve(
collider_a, collider_b, gjk_result.simplex
);
if (epa_result.success) {
std::cout << "Penetration: " << epa_result.depth << " units\n";
std::cout << "Normal: " << epa_result.normal << "\n";
}
}
```
### Custom Mesh Creation
```cpp
// Create a simple box mesh
std::vector<Vector3<float>> box_vertices = {
{-0.5f, -0.5f, -0.5f}, { 0.5f, -0.5f, -0.5f},
{ 0.5f, 0.5f, -0.5f}, {-0.5f, 0.5f, -0.5f},
{-0.5f, -0.5f, 0.5f}, { 0.5f, -0.5f, 0.5f},
{ 0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, 0.5f}
};
std::vector<Vector3<std::size_t>> box_indices = {
{0, 1, 2}, {0, 2, 3}, // Front face
{4, 6, 5}, {4, 7, 6}, // Back face
{0, 4, 5}, {0, 5, 1}, // Bottom face
{2, 6, 7}, {2, 7, 3}, // Top face
{0, 3, 7}, {0, 7, 4}, // Left face
{1, 5, 6}, {1, 6, 2} // Right face
};
using namespace omath::source_engine;
Mesh box_mesh(box_vertices, box_indices);
box_mesh.set_origin({10, 0, 0});
box_mesh.set_scale({2, 2, 2});
MeshCollider<Mesh> box_collider(std::move(box_mesh));
```
### Oriented Collision
```cpp
// Create rotated mesh
Mesh mesh(vertices, indices);
mesh.set_origin({5, 5, 5});
mesh.set_scale({1, 1, 1});
// Set rotation (engine-specific angles)
ViewAngles rotation;
rotation.pitch = PitchAngle::from_degrees(45.0f);
rotation.yaw = YawAngle::from_degrees(30.0f);
mesh.set_rotation(rotation);
// Collider automatically handles transformation
MeshCollider<Mesh> collider(std::move(mesh));
// Support function returns world-space vertices
auto support = collider.find_abs_furthest_vertex({0, 1, 0});
```
---
## Performance Considerations
### Linear Search
`find_furthest_vertex` performs a **linear search** through all vertices:
* **Time complexity**: O(n) per support query
* **GJK iterations**: ~10-20 support queries per collision test
* **Total cost**: O(k × n) where k is GJK iterations
For meshes with many vertices (>1000), consider:
* Using simpler proxy geometry (bounding box, convex hull with fewer vertices)
* Pre-computing hierarchical structures
* Using specialized collision shapes when possible
### Caching Opportunities
The implementation uses `std::ranges::max_element`, which is cache-friendly for contiguous vertex buffers. For optimal performance:
* Store vertices contiguously in memory
* Avoid pointer-based or scattered vertex storage
* Consider SoA (Structure of Arrays) layout for SIMD optimization
### World Space Transformation
The `vertex_to_world_space` call involves matrix multiplication:
* **Cost**: ~15-20 floating-point operations per vertex
* **Optimization**: The mesh caches its transformation matrix
* **Update cost**: Only recomputed when origin/rotation/scale changes
---
## Limitations & Edge Cases
### Convex Hull Requirement
**Critical**: GJK/EPA only work with **convex shapes**. If your mesh is concave:
#### Option 1: Convex Decomposition
```cpp
// Decompose concave mesh into convex parts
std::vector<Mesh> convex_parts = decompose_mesh(concave_mesh);
for (const auto& part : convex_parts) {
MeshCollider collider(part);
// Test each part separately
}
```
#### Option 2: Use Convex Hull
```cpp
// Compute convex hull of vertices
auto hull_vertices = compute_convex_hull(mesh.m_vertex_buffer);
Mesh hull_mesh(hull_vertices, hull_indices);
MeshCollider collider(std::move(hull_mesh));
```
#### Option 3: Different Algorithm
Use triangle-based collision (e.g., LineTracer) for true concave support.
### Empty Mesh
Behavior is undefined if `m_vertex_buffer` is empty. Always ensure:
```cpp
assert(!mesh.m_vertex_buffer.empty());
MeshCollider collider(std::move(mesh));
```
### Degenerate Meshes
* **Single vertex**: Treated as a point (degenerates to sphere collision)
* **Two vertices**: Line segment (may cause GJK issues)
* **Coplanar vertices**: Flat mesh; EPA may have convergence issues
**Recommendation**: Use at least 4 non-coplanar vertices for robustness.
---
## Coordinate Systems
`MeshCollider` supports different engine coordinate systems through the `MeshTrait`:
| Engine | Up Axis | Handedness | Rotation Order |
|--------|---------|------------|----------------|
| Source Engine | Z | Right-handed | Pitch/Yaw/Roll |
| Unity | Y | Left-handed | Pitch/Yaw/Roll |
| Unreal | Z | Left-handed | Roll/Pitch/Yaw |
| Frostbite | Y | Right-handed | Pitch/Yaw/Roll |
| IW Engine | Z | Right-handed | Pitch/Yaw/Roll |
| OpenGL | Y | Right-handed | Pitch/Yaw/Roll |
The `vertex_to_world_space` method handles these differences transparently.
---
## Advanced Usage
### Custom Support Function
For specialized collision shapes, implement a custom collider:
```cpp
class SphereCollider {
public:
using VertexType = Vector3<float>;
Vector3<float> center;
float radius;
VertexType find_abs_furthest_vertex(const VertexType& direction) const {
auto normalized = direction.normalized();
return center + normalized * radius;
}
};
// Use with GJK/EPA
auto result = GjkAlgorithm<SphereCollider>::check_collision(sphere_a, sphere_b);
```
### Debugging Support Queries
```cpp
class DebugMeshCollider : public MeshCollider<Mesh> {
public:
using MeshCollider::MeshCollider;
VertexType find_abs_furthest_vertex(const VertexType& direction) const {
auto result = MeshCollider::find_abs_furthest_vertex(direction);
std::cout << "Support query: direction=" << direction
<< " -> vertex=" << result << "\n";
return result;
}
};
```
---
## See Also
- [GJK Algorithm Documentation](gjk_algorithm.md) - Uses `MeshCollider` for collision detection
- [EPA Algorithm Documentation](epa_algorithm.md) - Uses `MeshCollider` for penetration depth
- [Simplex Documentation](simplex.md) - Data structure used by GJK
- [Mesh Documentation](../3d_primitives/mesh.md) - Underlying mesh primitive
- [Tutorials - Collision Detection](../tutorials.md#tutorial-4-collision-detection) - Complete collision tutorial
---
*Last updated: 13 Nov 2025*

327
docs/collision/simplex.md Normal file
View File

@@ -0,0 +1,327 @@
# `omath::collision::Simplex` — Fixed-capacity simplex for GJK/EPA
> Header: `omath/collision/simplex.hpp`
> Namespace: `omath::collision`
> Depends on: `Vector3<float>` (or any type satisfying `GjkVector` concept)
> Purpose: store and manipulate simplices in GJK and EPA algorithms
---
## Overview
`Simplex` is a lightweight container for up to 4 points, used internally by the GJK and EPA collision detection algorithms. A simplex in this context is a geometric shape defined by 1 to 4 vertices:
* **1 point** — a single vertex
* **2 points** — a line segment
* **3 points** — a triangle
* **4 points** — a tetrahedron
The GJK algorithm builds simplices incrementally to detect collisions, and EPA extends a 4-point simplex to compute penetration depth.
---
## Template & Concepts
```cpp
template<GjkVector VectorType = Vector3<float>>
class Simplex final;
```
### `GjkVector` Concept
The vertex type must satisfy:
```cpp
template<class V>
concept GjkVector = requires(const V& a, const V& b) {
{ -a } -> std::same_as<V>;
{ a - b } -> std::same_as<V>;
{ a.cross(b) } -> std::same_as<V>;
{ a.point_to_same_direction(b) } -> std::same_as<bool>;
};
```
`omath::Vector3<float>` satisfies this concept and is the default.
---
## Constructors & Assignment
```cpp
constexpr Simplex() = default;
constexpr Simplex& operator=(std::initializer_list<VectorType> list) noexcept;
```
### Initialization
```cpp
// Empty simplex
Simplex<Vector3<float>> s;
// Initialize with points
Simplex<Vector3<float>> s2;
s2 = {v1, v2, v3}; // 3-point simplex (triangle)
```
**Constraint**: Maximum 4 points. Passing more triggers an assertion in debug builds.
---
## Core Methods
### Adding Points
```cpp
constexpr void push_front(const VectorType& p) noexcept;
```
Inserts a point at the **front** (index 0), shifting existing points back. If the simplex is already at capacity (4 points), the last point is discarded.
**Usage pattern in GJK**:
```cpp
simplex.push_front(new_support_point);
// Now simplex[0] is the newest point
```
### Size & Capacity
```cpp
[[nodiscard]] constexpr std::size_t size() const noexcept;
[[nodiscard]] static constexpr std::size_t capacity = 4;
```
* `size()` — current number of points (0-4)
* `capacity` — maximum points (always 4)
### Element Access
```cpp
[[nodiscard]] constexpr VectorType& operator[](std::size_t index) noexcept;
[[nodiscard]] constexpr const VectorType& operator[](std::size_t index) const noexcept;
```
Access points by index. **No bounds checking** — index must be `< size()`.
```cpp
if (simplex.size() >= 2) {
auto edge = simplex[1] - simplex[0];
}
```
### Iterators
```cpp
[[nodiscard]] constexpr auto begin() noexcept;
[[nodiscard]] constexpr auto end() noexcept;
[[nodiscard]] constexpr auto begin() const noexcept;
[[nodiscard]] constexpr auto end() const noexcept;
```
Standard iterator support for range-based loops:
```cpp
for (const auto& vertex : simplex) {
std::cout << vertex << "\n";
}
```
---
## GJK-Specific Methods
These methods implement the core logic for simplifying simplices in the GJK algorithm.
### `contains_origin`
```cpp
[[nodiscard]] constexpr bool contains_origin() noexcept;
```
Determines if the origin lies within the current simplex. This is the **core GJK test**: if true, the shapes intersect.
* For a **1-point** simplex, always returns `false` (can't contain origin)
* For a **2-point** simplex (line), checks if origin projects onto the segment
* For a **3-point** simplex (triangle), checks if origin projects onto the triangle
* For a **4-point** simplex (tetrahedron), checks if origin is inside
**Side effect**: Simplifies the simplex by removing points not needed to maintain proximity to the origin. After calling, `size()` may have decreased.
**Return value**:
* `true` — origin is contained (collision detected)
* `false` — origin not contained; simplex has been simplified toward origin
### `next_direction`
```cpp
[[nodiscard]] constexpr VectorType next_direction() const noexcept;
```
Computes the next search direction for GJK. This is the direction from the simplex toward the origin, used to query the next support point.
* Must be called **after** `contains_origin()` returns `false`
* Behavior is **undefined** if called when `size() == 0` or when origin is already contained
---
## Usage Examples
### GJK Iteration (Simplified)
```cpp
Simplex<Vector3<float>> simplex;
Vector3<float> direction{1, 0, 0}; // Initial search direction
for (int i = 0; i < 64; ++i) {
// Get support point in current direction
auto support = find_support_vertex(collider_a, collider_b, direction);
// Check if we made progress
if (support.dot(direction) <= 0)
break; // No collision possible
simplex.push_front(support);
// Check if simplex contains origin
if (simplex.contains_origin()) {
// Collision detected!
assert(simplex.size() == 4);
return GjkHitInfo{true, simplex};
}
// Get next search direction
direction = simplex.next_direction();
}
// No collision
return GjkHitInfo{false, {}};
```
### Manual Simplex Construction
```cpp
using Vec3 = Vector3<float>;
Simplex<Vec3> simplex;
simplex = {
Vec3{0.0f, 0.0f, 0.0f},
Vec3{1.0f, 0.0f, 0.0f},
Vec3{0.0f, 1.0f, 0.0f},
Vec3{0.0f, 0.0f, 1.0f}
};
assert(simplex.size() == 4);
// Check if origin is inside this tetrahedron
bool has_collision = simplex.contains_origin();
```
### Iterating Over Points
```cpp
void print_simplex(const Simplex<Vector3<float>>& s) {
std::cout << "Simplex with " << s.size() << " points:\n";
for (std::size_t i = 0; i < s.size(); ++i) {
const auto& p = s[i];
std::cout << " [" << i << "] = ("
<< p.x << ", " << p.y << ", " << p.z << ")\n";
}
}
```
---
## Implementation Details
### Simplex Simplification
The `contains_origin()` method implements different tests based on simplex size:
#### Line Segment (2 points)
Checks if origin projects onto segment `[A, B]`:
* If yes, keeps both points
* If no, keeps only the closer point
#### Triangle (3 points)
Tests the origin against the triangle plane and edges using cross products. Simplifies to:
* The full triangle if origin projects onto its surface
* An edge if origin is closest to that edge
* A single vertex otherwise
#### Tetrahedron (4 points)
Tests origin against all four faces:
* If origin is inside, returns `true` (collision)
* If outside, reduces to the face/edge/vertex closest to origin
### Direction Calculation
The `next_direction()` method computes:
* For **line**: perpendicular from line toward origin
* For **triangle**: perpendicular from triangle toward origin
* Implementation uses cross products and projections to avoid sqrt when possible
---
## Performance Characteristics
* **Storage**: Fixed 4 × `sizeof(VectorType)` + size counter
* **Push front**: O(n) where n is current size (max 4, so effectively O(1))
* **Contains origin**: O(1) for each case (line, triangle, tetrahedron)
* **Next direction**: O(1) — simple cross products and subtractions
* **No heap allocations**: All storage is inline
**constexpr**: All methods are `constexpr`, enabling compile-time usage where feasible.
---
## Edge Cases & Constraints
### Degenerate Simplices
* **Zero-length edges**: Can occur if support points coincide. The algorithm handles this by checking `point_to_same_direction` before divisions.
* **Collinear points**: Triangle simplification detects and handles collinear cases by reducing to a line.
* **Flat tetrahedron**: If the 4th point is coplanar with the first 3, the origin containment test may have reduced precision.
### Assertions
* **Capacity**: `operator=` asserts `list.size() <= 4` in debug builds
* **Index bounds**: No bounds checking in release builds — ensure `index < size()`
### Thread Safety
* **Read-only**: Safe to read from multiple threads
* **Modification**: Not thread-safe; synchronize writes externally
---
## Relationship to GJK & EPA
### In GJK
* Starts empty or with an initial point
* Grows via `push_front` as support points are added
* Shrinks via `contains_origin` as it's simplified
* Once it reaches 4 points and contains origin, GJK succeeds
### In EPA
* Takes a 4-point simplex from GJK as input
* Uses the tetrahedron as the initial polytope
* Does not directly use the `Simplex` class for expansion (EPA maintains a more complex polytope structure)
---
## See Also
- [GJK Algorithm Documentation](gjk_algorithm.md) - Uses `Simplex` for collision detection
- [EPA Algorithm Documentation](epa_algorithm.md) - Takes 4-point `Simplex` as input
- [MeshCollider Documentation](mesh_collider.md) - Provides support function for GJK/EPA
- [Vector3 Documentation](../linear_algebra/vector3.md) - Default vertex type
- [Tutorials - Collision Detection](../tutorials.md#tutorial-4-collision-detection) - Collision tutorial
---
*Last updated: 13 Nov 2025*

View File

@@ -0,0 +1,119 @@
# `omath::frostbite_engine::MeshTrait` — mesh transformation trait for Frostbite Engine
> Header: `omath/engines/frostbite_engine/traits/mesh_trait.hpp`
> Namespace: `omath::frostbite_engine`
> Purpose: provide Frostbite Engine-specific rotation matrix computation for `omath::primitives::Mesh`
---
## Summary
`MeshTrait` is a trait class that provides the `rotation_matrix` function for transforming meshes in Frostbite's coordinate system. It serves as a template parameter to `omath::primitives::Mesh`, enabling engine-specific rotation behavior.
---
## Coordinate System
**Frostbite Engine** uses:
* **Up axis**: +Y
* **Forward axis**: +Z
* **Right axis**: +X
* **Handedness**: Right-handed
* **Rotation order**: Pitch (X) → Yaw (Y) → Roll (Z)
---
## API
```cpp
namespace omath::frostbite_engine {
class MeshTrait final {
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
};
} // namespace omath::frostbite_engine
```
---
## Method: `rotation_matrix`
```cpp
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
```
Computes a 4×4 rotation matrix from Frostbite-style Euler angles.
**Parameters**:
* `rotation``ViewAngles` containing pitch, yaw, and roll angles
**Returns**: 4×4 rotation matrix suitable for mesh transformation
**Implementation**: Delegates to `frostbite_engine::rotation_matrix(rotation)` defined in `formulas.hpp`.
---
## Usage
### With Mesh
```cpp
using namespace omath::frostbite_engine;
// Create mesh (MeshTrait is used automatically)
Mesh my_mesh(vertices, indices);
// Set rotation using ViewAngles
ViewAngles angles;
angles.pitch = PitchAngle::from_degrees(30.0f);
angles.yaw = YawAngle::from_degrees(45.0f);
angles.roll = RollAngle::from_degrees(0.0f);
my_mesh.set_rotation(angles);
// The rotation matrix is computed using MeshTrait::rotation_matrix
auto matrix = my_mesh.get_to_world_matrix();
```
---
## Rotation Conventions
Frostbite uses a right-handed Y-up coordinate system:
1. **Pitch** (rotation around X-axis / right axis)
* Positive pitch looks upward (+Y direction)
* Range: typically [-89°, 89°]
2. **Yaw** (rotation around Y-axis / up axis)
* Positive yaw rotates counterclockwise when viewed from above (right-handed)
* Range: [-180°, 180°]
3. **Roll** (rotation around Z-axis / forward axis)
* Positive roll tilts right
* Range: [-180°, 180°]
---
## Type Alias
```cpp
namespace omath::frostbite_engine {
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
}
```
---
## See Also
- [Mesh Documentation](../../3d_primitives/mesh.md) - Mesh primitive
- [Formulas Documentation](formulas.md) - Frostbite rotation formula
- [CameraTrait Documentation](camera_trait.md) - Camera trait
---
*Last updated: 13 Nov 2025*

View File

@@ -0,0 +1,119 @@
# `omath::iw_engine::MeshTrait` — mesh transformation trait for IW Engine
> Header: `omath/engines/iw_engine/traits/mesh_trait.hpp`
> Namespace: `omath::iw_engine`
> Purpose: provide IW Engine-specific rotation matrix computation for `omath::primitives::Mesh`
---
## Summary
`MeshTrait` is a trait class that provides the `rotation_matrix` function for transforming meshes in IW Engine's (Infinity Ward) coordinate system. It serves as a template parameter to `omath::primitives::Mesh`, enabling engine-specific rotation behavior.
---
## Coordinate System
**IW Engine** (Call of Duty) uses:
* **Up axis**: +Z
* **Forward axis**: +Y
* **Right axis**: +X
* **Handedness**: Right-handed
* **Rotation order**: Pitch (X) → Yaw (Z) → Roll (Y)
---
## API
```cpp
namespace omath::iw_engine {
class MeshTrait final {
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
};
} // namespace omath::iw_engine
```
---
## Method: `rotation_matrix`
```cpp
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
```
Computes a 4×4 rotation matrix from IW Engine-style Euler angles.
**Parameters**:
* `rotation``ViewAngles` containing pitch, yaw, and roll angles
**Returns**: 4×4 rotation matrix suitable for mesh transformation
**Implementation**: Delegates to `iw_engine::rotation_matrix(rotation)` defined in `formulas.hpp`.
---
## Usage
### With Mesh
```cpp
using namespace omath::iw_engine;
// Create mesh (MeshTrait is used automatically)
Mesh my_mesh(vertices, indices);
// Set rotation using ViewAngles
ViewAngles angles;
angles.pitch = PitchAngle::from_degrees(30.0f);
angles.yaw = YawAngle::from_degrees(45.0f);
angles.roll = RollAngle::from_degrees(0.0f);
my_mesh.set_rotation(angles);
// The rotation matrix is computed using MeshTrait::rotation_matrix
auto matrix = my_mesh.get_to_world_matrix();
```
---
## Rotation Conventions
IW Engine uses a right-handed Z-up coordinate system (similar to Source Engine):
1. **Pitch** (rotation around X-axis / right axis)
* Positive pitch looks upward (+Z direction)
* Range: typically [-89°, 89°]
2. **Yaw** (rotation around Z-axis / up axis)
* Positive yaw rotates counterclockwise when viewed from above
* Range: [-180°, 180°]
3. **Roll** (rotation around Y-axis / forward axis)
* Positive roll tilts right
* Range: [-180°, 180°]
---
## Type Alias
```cpp
namespace omath::iw_engine {
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
}
```
---
## See Also
- [Mesh Documentation](../../3d_primitives/mesh.md) - Mesh primitive
- [Formulas Documentation](formulas.md) - IW Engine rotation formula
- [CameraTrait Documentation](camera_trait.md) - Camera trait
---
*Last updated: 13 Nov 2025*

View File

@@ -0,0 +1,121 @@
# `omath::opengl_engine::MeshTrait` — mesh transformation trait for OpenGL
> Header: `omath/engines/opengl_engine/traits/mesh_trait.hpp`
> Namespace: `omath::opengl_engine`
> Purpose: provide OpenGL-specific rotation matrix computation for `omath::primitives::Mesh`
---
## Summary
`MeshTrait` is a trait class that provides the `rotation_matrix` function for transforming meshes in OpenGL's canonical coordinate system. It serves as a template parameter to `omath::primitives::Mesh`, enabling engine-specific rotation behavior.
---
## Coordinate System
**OpenGL** (canonical) uses:
* **Up axis**: +Y
* **Forward axis**: +Z (toward viewer)
* **Right axis**: +X
* **Handedness**: Right-handed
* **Rotation order**: Pitch (X) → Yaw (Y) → Roll (Z)
---
## API
```cpp
namespace omath::opengl_engine {
class MeshTrait final {
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
};
} // namespace omath::opengl_engine
```
---
## Method: `rotation_matrix`
```cpp
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
```
Computes a 4×4 rotation matrix from OpenGL-style Euler angles.
**Parameters**:
* `rotation``ViewAngles` containing pitch, yaw, and roll angles
**Returns**: 4×4 rotation matrix suitable for mesh transformation
**Implementation**: Delegates to `opengl_engine::rotation_matrix(rotation)` defined in `formulas.hpp`.
---
## Usage
### With Mesh
```cpp
using namespace omath::opengl_engine;
// Create mesh (MeshTrait is used automatically)
Mesh my_mesh(vertices, indices);
// Set rotation using ViewAngles
ViewAngles angles;
angles.pitch = PitchAngle::from_degrees(30.0f);
angles.yaw = YawAngle::from_degrees(45.0f);
angles.roll = RollAngle::from_degrees(0.0f);
my_mesh.set_rotation(angles);
// The rotation matrix is computed using MeshTrait::rotation_matrix
auto matrix = my_mesh.get_to_world_matrix();
```
---
## Rotation Conventions
OpenGL uses a right-handed Y-up coordinate system:
1. **Pitch** (rotation around X-axis / right axis)
* Positive pitch looks upward (+Y direction)
* Range: typically [-89°, 89°]
2. **Yaw** (rotation around Y-axis / up axis)
* Positive yaw rotates counterclockwise when viewed from above (right-handed)
* Range: [-180°, 180°]
3. **Roll** (rotation around Z-axis / depth axis)
* Positive roll tilts right
* Range: [-180°, 180°]
**Note**: In OpenGL, +Z points toward the viewer in view space, but away from the viewer in world space.
---
## Type Alias
```cpp
namespace omath::opengl_engine {
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
}
```
---
## See Also
- [Mesh Documentation](../../3d_primitives/mesh.md) - Mesh primitive
- [Formulas Documentation](formulas.md) - OpenGL rotation formula
- [CameraTrait Documentation](camera_trait.md) - Camera trait
---
*Last updated: 13 Nov 2025*

View File

@@ -0,0 +1,182 @@
# `omath::source_engine::MeshTrait` — mesh transformation trait for Source Engine
> Header: `omath/engines/source_engine/traits/mesh_trait.hpp`
> Namespace: `omath::source_engine`
> Purpose: provide Source Engine-specific rotation matrix computation for `omath::primitives::Mesh`
---
## Summary
`MeshTrait` is a trait class that provides the `rotation_matrix` function for transforming meshes in Source Engine's coordinate system. It serves as a template parameter to `omath::primitives::Mesh`, enabling engine-specific rotation behavior.
---
## Coordinate System
**Source Engine** uses:
* **Up axis**: +Z
* **Forward axis**: +Y
* **Right axis**: +X
* **Handedness**: Right-handed
* **Rotation order**: Pitch (X) → Yaw (Z) → Roll (Y)
---
## API
```cpp
namespace omath::source_engine {
class MeshTrait final {
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
};
} // namespace omath::source_engine
```
---
## Method: `rotation_matrix`
```cpp
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
```
Computes a 4×4 rotation matrix from Source Engine-style Euler angles.
**Parameters**:
* `rotation``ViewAngles` containing pitch, yaw, and roll angles
**Returns**: 4×4 rotation matrix suitable for mesh transformation
**Implementation**: Delegates to `source_engine::rotation_matrix(rotation)` defined in `formulas.hpp`.
---
## Usage
### With Mesh
```cpp
using namespace omath::source_engine;
// Create mesh (MeshTrait is used automatically)
Mesh my_mesh(vertices, indices);
// Set rotation using ViewAngles
ViewAngles angles;
angles.pitch = PitchAngle::from_degrees(30.0f);
angles.yaw = YawAngle::from_degrees(45.0f);
angles.roll = RollAngle::from_degrees(0.0f);
my_mesh.set_rotation(angles);
// The rotation matrix is computed using MeshTrait::rotation_matrix
auto matrix = my_mesh.get_to_world_matrix();
```
### Direct Usage
```cpp
using namespace omath::source_engine;
ViewAngles angles;
angles.pitch = PitchAngle::from_degrees(45.0f);
angles.yaw = YawAngle::from_degrees(90.0f);
angles.roll = RollAngle::from_degrees(0.0f);
Mat4X4 rot_matrix = MeshTrait::rotation_matrix(angles);
// Use the matrix directly
Vector3<float> local_point{1, 0, 0};
auto rotated = rot_matrix * mat_column_from_vector(local_point);
```
---
## Rotation Conventions
The rotation matrix is built following Source Engine's conventions:
1. **Pitch** (rotation around X-axis / right axis)
* Positive pitch looks upward (+Z direction)
* Range: typically [-89°, 89°]
2. **Yaw** (rotation around Z-axis / up axis)
* Positive yaw rotates counterclockwise when viewed from above
* Range: [-180°, 180°]
3. **Roll** (rotation around Y-axis / forward axis)
* Positive roll tilts right
* Range: [-180°, 180°]
**Composition**: The matrices are combined in the order Pitch × Yaw × Roll, producing a rotation that:
* First applies roll around the forward axis
* Then applies yaw around the up axis
* Finally applies pitch around the right axis
This matches Source Engine's internal rotation order.
---
## Related Functions
The trait delegates to the formula defined in `formulas.hpp`:
```cpp
[[nodiscard]]
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
```
See [Formulas Documentation](formulas.md) for details on the rotation matrix computation.
---
## Type Alias
The Source Engine mesh type is pre-defined:
```cpp
namespace omath::source_engine {
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
}
```
Use this alias to ensure correct trait usage:
```cpp
using namespace omath::source_engine;
// Correct: uses Source Engine trait
Mesh my_mesh(vbo, vao);
// Avoid: manually specifying template parameters
primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float> verbose_mesh(vbo, vao);
```
---
## Notes
* **Angle ranges**: Ensure angles are within valid ranges (pitch: [-89°, 89°], yaw/roll: [-180°, 180°])
* **Performance**: Matrix computation is O(1) with ~64 floating-point operations
* **Caching**: The mesh caches the transformation matrix; recomputed only when rotation changes
* **Compatibility**: Works with all Source Engine games (CS:GO, TF2, Portal, Half-Life 2, etc.)
---
## See Also
- [Mesh Documentation](../../3d_primitives/mesh.md) - Mesh primitive using this trait
- [MeshCollider Documentation](../../collision/mesh_collider.md) - Collision wrapper for meshes
- [Formulas Documentation](formulas.md) - Source Engine rotation formula
- [CameraTrait Documentation](camera_trait.md) - Camera transformation trait
- [Constants Documentation](constants.md) - Source Engine constants
- [API Overview](../../api_overview.md) - High-level API reference
---
*Last updated: 13 Nov 2025*

View File

@@ -0,0 +1,119 @@
# `omath::unity_engine::MeshTrait` — mesh transformation trait for Unity Engine
> Header: `omath/engines/unity_engine/traits/mesh_trait.hpp`
> Namespace: `omath::unity_engine`
> Purpose: provide Unity Engine-specific rotation matrix computation for `omath::primitives::Mesh`
---
## Summary
`MeshTrait` is a trait class that provides the `rotation_matrix` function for transforming meshes in Unity's coordinate system. It serves as a template parameter to `omath::primitives::Mesh`, enabling engine-specific rotation behavior.
---
## Coordinate System
**Unity Engine** uses:
* **Up axis**: +Y
* **Forward axis**: +Z
* **Right axis**: +X
* **Handedness**: Left-handed
* **Rotation order**: Pitch (X) → Yaw (Y) → Roll (Z)
---
## API
```cpp
namespace omath::unity_engine {
class MeshTrait final {
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
};
} // namespace omath::unity_engine
```
---
## Method: `rotation_matrix`
```cpp
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
```
Computes a 4×4 rotation matrix from Unity-style Euler angles.
**Parameters**:
* `rotation``ViewAngles` containing pitch, yaw, and roll angles
**Returns**: 4×4 rotation matrix suitable for mesh transformation
**Implementation**: Delegates to `unity_engine::rotation_matrix(rotation)` defined in `formulas.hpp`.
---
## Usage
### With Mesh
```cpp
using namespace omath::unity_engine;
// Create mesh (MeshTrait is used automatically)
Mesh my_mesh(vertices, indices);
// Set rotation using ViewAngles
ViewAngles angles;
angles.pitch = PitchAngle::from_degrees(30.0f);
angles.yaw = YawAngle::from_degrees(45.0f);
angles.roll = RollAngle::from_degrees(0.0f);
my_mesh.set_rotation(angles);
// The rotation matrix is computed using MeshTrait::rotation_matrix
auto matrix = my_mesh.get_to_world_matrix();
```
---
## Rotation Conventions
Unity uses a left-handed coordinate system with Y-up:
1. **Pitch** (rotation around X-axis / right axis)
* Positive pitch looks upward (+Y direction)
* Range: typically [-89°, 89°]
2. **Yaw** (rotation around Y-axis / up axis)
* Positive yaw rotates clockwise when viewed from above (left-handed)
* Range: [-180°, 180°]
3. **Roll** (rotation around Z-axis / forward axis)
* Positive roll tilts right
* Range: [-180°, 180°]
---
## Type Alias
```cpp
namespace omath::unity_engine {
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
}
```
---
## See Also
- [Mesh Documentation](../../3d_primitives/mesh.md) - Mesh primitive
- [Formulas Documentation](formulas.md) - Unity rotation formula
- [CameraTrait Documentation](camera_trait.md) - Camera trait
---
*Last updated: 13 Nov 2025*

View File

@@ -0,0 +1,121 @@
# `omath::unreal_engine::MeshTrait` — mesh transformation trait for Unreal Engine
> Header: `omath/engines/unreal_engine/traits/mesh_trait.hpp`
> Namespace: `omath::unreal_engine`
> Purpose: provide Unreal Engine-specific rotation matrix computation for `omath::primitives::Mesh`
---
## Summary
`MeshTrait` is a trait class that provides the `rotation_matrix` function for transforming meshes in Unreal Engine's coordinate system. It serves as a template parameter to `omath::primitives::Mesh`, enabling engine-specific rotation behavior.
---
## Coordinate System
**Unreal Engine** uses:
* **Up axis**: +Z
* **Forward axis**: +X
* **Right axis**: +Y
* **Handedness**: Left-handed
* **Rotation order**: Roll (Y) → Pitch (X) → Yaw (Z)
---
## API
```cpp
namespace omath::unreal_engine {
class MeshTrait final {
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
};
} // namespace omath::unreal_engine
```
---
## Method: `rotation_matrix`
```cpp
static Mat4X4 rotation_matrix(const ViewAngles& rotation);
```
Computes a 4×4 rotation matrix from Unreal-style Euler angles.
**Parameters**:
* `rotation``ViewAngles` containing pitch, yaw, and roll angles
**Returns**: 4×4 rotation matrix suitable for mesh transformation
**Implementation**: Delegates to `unreal_engine::rotation_matrix(rotation)` defined in `formulas.hpp`.
---
## Usage
### With Mesh
```cpp
using namespace omath::unreal_engine;
// Create mesh (MeshTrait is used automatically)
Mesh my_mesh(vertices, indices);
// Set rotation using ViewAngles
ViewAngles angles;
angles.pitch = PitchAngle::from_degrees(30.0f);
angles.yaw = YawAngle::from_degrees(45.0f);
angles.roll = RollAngle::from_degrees(0.0f);
my_mesh.set_rotation(angles);
// The rotation matrix is computed using MeshTrait::rotation_matrix
auto matrix = my_mesh.get_to_world_matrix();
```
---
## Rotation Conventions
Unreal uses a left-handed Z-up coordinate system:
1. **Roll** (rotation around Y-axis / right axis)
* Positive roll rotates forward axis upward
* Range: [-180°, 180°]
2. **Pitch** (rotation around X-axis / forward axis)
* Positive pitch looks upward
* Range: typically [-89°, 89°]
3. **Yaw** (rotation around Z-axis / up axis)
* Positive yaw rotates clockwise when viewed from above (left-handed)
* Range: [-180°, 180°]
**Note**: Unreal applies rotations in Roll-Pitch-Yaw order, different from most other engines.
---
## Type Alias
```cpp
namespace omath::unreal_engine {
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, float>;
}
```
---
## See Also
- [Mesh Documentation](../../3d_primitives/mesh.md) - Mesh primitive
- [Formulas Documentation](formulas.md) - Unreal rotation formula
- [CameraTrait Documentation](camera_trait.md) - Camera trait
---
*Last updated: 13 Nov 2025*

BIN
docs/images/showcase/opengl.png LFS Normal file

Binary file not shown.

65
docs/styles/links.css Normal file
View File

@@ -0,0 +1,65 @@
/* Normal links */
a {
color: orange;
}
/* On hover/focus */
a:hover,
a:focus {
color: #ff9900; /* a slightly different orange, optional */
}
/* Navbar background */
.navbar,
.navbar-default,
.navbar-inverse {
background-color: #a26228 !important; /* your orange */
border-color: #ff6600 !important;
}
/* Navbar brand + links */
.navbar .navbar-brand,
.navbar .navbar-nav > li > a {
color: #ffffff !important;
}
/* Active and hover states */
.navbar .navbar-nav > .active > a,
.navbar .navbar-nav > .active > a:focus,
.navbar .navbar-nav > .active > a:hover,
.navbar .navbar-nav > li > a:hover,
.navbar .navbar-nav > li > a:focus {
color: #ffffff !important;
}
/* === DROPDOWN MENU BACKGROUND === */
.navbar .dropdown-menu {
border-color: #ff6600 !important;
}
/* Caret icon (the little triangle) */
.navbar .dropdown-toggle .caret {
border-top-color: #ffffff !important;
border-bottom-color: #ffffff !important;
}
/* === BOOTSTRAP 3 STYLE ITEMS (mkdocs + bootswatch darkly often use this) === */
.navbar .dropdown-menu > li > a {
color: #ffffff !important;
}
.navbar .dropdown-menu > li > a:hover,
.navbar .dropdown-menu > li > a:focus {
background-color: #e65c00 !important; /* darker orange on hover */
color: #ffffff !important;
}
/* === BOOTSTRAP 4+ STYLE ITEMS (if your theme uses .dropdown-item) === */
.navbar .dropdown-item {
color: #ffffff !important;
}
.navbar .dropdown-item:hover,
.navbar .dropdown-item:focus {
background-color: #e65c00 !important;
color: #ffffff !important;
}

View File

@@ -1,9 +1,10 @@
project(examples)
add_executable(example_projection_matrix_builder example_proj_mat_builder.cpp)
set_target_properties(example_projection_matrix_builder PROPERTIES CXX_STANDARD 26)
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)
add_subdirectory(example_barycentric)
add_subdirectory(example_glfw3)
add_subdirectory(example_proj_mat_builder)
add_subdirectory(example_signature_scan)
add_subdirectory(exmple_var_encryption)
if(OMATH_ENABLE_VALGRIND)
omath_setup_valgrind(example_projection_matrix_builder)
omath_setup_valgrind(example_signature_scan)
omath_setup_valgrind(example_glfw3)
endif()

View File

@@ -0,0 +1,14 @@
project(example_barycentric)
add_executable(${PROJECT_NAME} example_barycentric.cpp)
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 23
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(OpenGL)
find_package(GLEW REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath GLEW::GLEW glfw OpenGL::GL)

View File

@@ -0,0 +1,427 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cmath>
#include <iostream>
#include <omath/omath.hpp>
#include <vector>
using omath::Color;
using omath::Triangle;
using omath::Vector3;
static const char* vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in float aPointSize;
layout (location = 3) in float aIsLine;
out vec3 vColor;
out float vIsLine;
void main() {
gl_Position = vec4(aPos, 1.0);
vColor = aColor;
gl_PointSize = aPointSize;
vIsLine = aIsLine;
}
)";
static const char* fragmentShaderSource = R"(
#version 330 core
in vec3 vColor;
in float vIsLine;
out vec4 FragColor;
void main() {
if (vIsLine < 0.5) {
// Calculate distance from center of the point
vec2 coord = gl_PointCoord - vec2(0.5);
if(length(coord) > 0.5)
discard;
}
FragColor = vec4(vColor, 1.0);
}
)";
GLuint compileShader(GLenum type, const char* src)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
GLint ok;
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
if (!ok)
{
char log[1024];
glGetShaderInfoLog(shader, sizeof(log), nullptr, log);
std::cerr << "Shader error: " << log << std::endl;
}
return shader;
}
void drawChar(char c, float x, float y, float scale, const Color& color, std::vector<float>& lines)
{
float w = 0.5f * scale;
float h = 1.0f * scale;
auto add = [&](float x1, float y1, float x2, float y2)
{
lines.push_back(x + x1 * w);
lines.push_back(y + y1 * h);
lines.push_back(0.0f);
lines.push_back(color.x);
lines.push_back(color.y);
lines.push_back(color.z);
lines.push_back(1.0f); // size
lines.push_back(1.0f); // isLine
lines.push_back(x + x2 * w);
lines.push_back(y + y2 * h);
lines.push_back(0.0f);
lines.push_back(color.x);
lines.push_back(color.y);
lines.push_back(color.z);
lines.push_back(1.0f); // size
lines.push_back(1.0f); // isLine
};
switch (c)
{
case '0':
add(0, 0, 1, 0);
add(1, 0, 1, 1);
add(1, 1, 0, 1);
add(0, 1, 0, 0);
break;
case '1':
add(0.5f, 0, 0.5f, 1);
add(0.25f, 0.75f, 0.5f, 1);
add(0.25f, 0, 0.75f, 0);
break;
case '2':
add(0, 1, 1, 1);
add(1, 1, 1, 0.5f);
add(1, 0.5f, 0, 0.5f);
add(0, 0.5f, 0, 0);
add(0, 0, 1, 0);
break;
case '3':
add(0, 1, 1, 1);
add(1, 1, 1, 0);
add(1, 0, 0, 0);
add(0, 0.5f, 1, 0.5f);
break;
case '4':
add(0, 1, 0, 0.5f);
add(0, 0.5f, 1, 0.5f);
add(1, 1, 1, 0);
break;
case '5':
add(1, 1, 0, 1);
add(0, 1, 0, 0.5f);
add(0, 0.5f, 1, 0.5f);
add(1, 0.5f, 1, 0);
add(1, 0, 0, 0);
break;
case '6':
add(1, 1, 0, 1);
add(0, 1, 0, 0);
add(0, 0, 1, 0);
add(1, 0, 1, 0.5f);
add(1, 0.5f, 0, 0.5f);
break;
case '7':
add(0, 1, 1, 1);
add(1, 1, 0.5f, 0);
break;
case '8':
add(0, 0, 1, 0);
add(1, 0, 1, 1);
add(1, 1, 0, 1);
add(0, 1, 0, 0);
add(0, 0.5f, 1, 0.5f);
break;
case '9':
add(1, 0.5f, 0, 0.5f);
add(0, 0.5f, 0, 1);
add(0, 1, 1, 1);
add(1, 1, 1, 0);
add(1, 0, 0, 0);
break;
case '.':
add(0.4f, 0, 0.6f, 0);
add(0.6f, 0, 0.6f, 0.2f);
add(0.6f, 0.2f, 0.4f, 0.2f);
add(0.4f, 0.2f, 0.4f, 0);
break;
}
}
void drawText(const std::string& text, float x, float y, float scale, const Color& color, std::vector<float>& lines)
{
float cursor = x;
for (char c : text)
{
drawChar(c, cursor, y, scale, color, lines);
cursor += (c == '.' ? 0.3f : 0.7f) * scale;
}
}
GLFWwindow* initWindow(int width, int height, const char* title)
{
if (!glfwInit())
{
std::cerr << "Failed to initialize GLFW\n";
return nullptr;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
if (!window)
{
std::cerr << "Failed to create GLFW window\n";
glfwTerminate();
return nullptr;
}
glfwMakeContextCurrent(window);
// Check if context is valid using standard GL
const GLubyte* renderer = glGetString(GL_RENDERER);
const GLubyte* version = glGetString(GL_VERSION);
if (renderer && version)
{
std::cout << "Renderer: " << renderer << "\n";
std::cout << "OpenGL version supported: " << version << "\n";
}
else
{
std::cerr << "Failed to get GL_RENDERER or GL_VERSION. Context might be invalid.\n";
}
glewExperimental = GL_TRUE;
GLenum glewErr = glewInit();
if (glewErr != GLEW_OK)
{
// Ignore GLEW_ERROR_NO_GLX_DISPLAY if we have a valid context (e.g. Wayland)
if (glewErr == GLEW_ERROR_NO_GLX_DISPLAY && renderer)
{
std::cerr << "GLEW warning: " << glewGetErrorString(glewErr) << " (Ignored because context seems valid)\n";
}
else
{
std::cerr << "Failed to initialize GLEW: " << glewGetErrorString(glewErr) << "\n";
glfwTerminate();
return nullptr;
}
}
return window;
}
GLuint createShaderProgram()
{
GLuint vs = compileShader(GL_VERTEX_SHADER, vertexShaderSource);
GLuint fs = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vs);
glAttachShader(shaderProgram, fs);
glLinkProgram(shaderProgram);
return shaderProgram;
}
void generatePointCloud(std::vector<float>& pointCloud, const Triangle<Vector3<float>>& triangle)
{
const auto& A = triangle.m_vertex1;
const auto& B = triangle.m_vertex2;
const auto& C = triangle.m_vertex3;
// Iterating over barycentric coordinates (u, v, w) from 0.0 to 1.0
for (float u = 0.0f; u <= 1.0f; u += 0.015f)
{
for (float v = 0.0f; v <= 1.0f - u; v += 0.015f)
{
float w = 1.0f - u - v;
if (w >= 0.0f && w <= 1.0f)
{
Vector3<float> P = A * u + B * v + C * w;
pointCloud.push_back(P.x);
pointCloud.push_back(P.y);
pointCloud.push_back(P.z);
pointCloud.push_back(u);
pointCloud.push_back(v);
pointCloud.push_back(w);
pointCloud.push_back(2.0f); // size
pointCloud.push_back(0.0f); // isLine
}
}
}
}
void setupBuffers(GLuint& VAO_cloud, GLuint& VBO_cloud, const std::vector<float>& pointCloud, GLuint& VAO_dyn,
GLuint& VBO_dyn)
{
glGenVertexArrays(1, &VAO_cloud);
glGenBuffers(1, &VBO_cloud);
glBindVertexArray(VAO_cloud);
glBindBuffer(GL_ARRAY_BUFFER, VBO_cloud);
glBufferData(GL_ARRAY_BUFFER, pointCloud.size() * sizeof(float), pointCloud.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(7 * sizeof(float)));
glEnableVertexAttribArray(3);
glGenVertexArrays(1, &VAO_dyn);
glGenBuffers(1, &VBO_dyn);
glBindVertexArray(VAO_dyn);
glBindBuffer(GL_ARRAY_BUFFER, VBO_dyn);
glBufferData(GL_ARRAY_BUFFER, 1000 * 8 * sizeof(float), NULL, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(7 * sizeof(float)));
glEnableVertexAttribArray(3);
}
void updateDynamicData(std::vector<float>& dynData, float u, float v, float w, const Vector3<float>& P,
const Triangle<Vector3<float>>& triangle)
{
const auto& A = triangle.m_vertex1;
const auto& B = triangle.m_vertex2;
const auto& C = triangle.m_vertex3;
float sizeA = 10.0f + u * 30.0f;
float sizeB = 10.0f + v * 30.0f;
float sizeC = 10.0f + w * 30.0f;
float sizeP = 12.0f;
dynData = {// Lines from P to A, B, C
P.x, P.y, P.z, u, v, w, 1.0f, 1.0f, A.x, A.y, A.z, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
P.x, P.y, P.z, u, v, w, 1.0f, 1.0f, B.x, B.y, B.z, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
P.x, P.y, P.z, u, v, w, 1.0f, 1.0f, C.x, C.y, C.z, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
// The animated dot itself (White)
P.x, P.y, P.z, 1.0f, 1.0f, 1.0f, sizeP, 0.0f,
// The 3 corner dots
A.x, A.y, A.z, 1.0f, 0.0f, 0.0f, sizeA, 0.0f, B.x, B.y, B.z, 0.0f, 1.0f, 0.0f, sizeB, 0.0f, C.x, C.y,
C.z, 0.0f, 0.0f, 1.0f, sizeC, 0.0f};
char bufA[16], bufB[16], bufC[16];
snprintf(bufA, sizeof(bufA), "%.2f", u);
snprintf(bufB, sizeof(bufB), "%.2f", v);
snprintf(bufC, sizeof(bufC), "%.2f", w);
// Keep text at a fixed distance from the dots
float distA = 0.13f;
float distB = 0.13f;
float distC = 0.13f;
drawText(bufA, A.x - 0.05f, A.y + distA, 0.1f, Color(1, 0, 0, 1), dynData);
drawText(bufB, B.x - 0.15f - distB, B.y - 0.05f - distB, 0.1f, Color(0, 1, 0, 1), dynData);
drawText(bufC, C.x + 0.05f + distC, C.y - 0.05f - distC, 0.1f, Color(0, 0, 1, 1), dynData);
}
int main()
{
GLFWwindow* window = initWindow(800, 800, "Barycentric Coordinates");
if (!window)
return -1;
GLuint shaderProgram = createShaderProgram();
// Triangle vertices as shown in the picture (Red, Green, Blue)
// Scaled down slightly to leave room for text
Triangle<Vector3<float>> triangle(Vector3<float>(0.0f, 0.6f, 0.0f), // Red dot (top)
Vector3<float>(-0.6f, -0.6f, 0.0f), // Green dot (bottom left)
Vector3<float>(0.6f, -0.6f, 0.0f) // Blue dot (bottom right)
);
std::vector<float> pointCloud;
generatePointCloud(pointCloud, triangle);
GLuint VAO_cloud, VBO_cloud, VAO_dyn, VBO_dyn;
setupBuffers(VAO_cloud, VBO_cloud, pointCloud, VAO_dyn, VBO_dyn);
glEnable(GL_PROGRAM_POINT_SIZE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.02f, 0.02f, 0.02f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
// Draw the point cloud (the iterated points)
glBindVertexArray(VAO_cloud);
glDrawArrays(GL_POINTS, 0, pointCloud.size() / 8);
// Animate the white dot to simulate dragging
float t = glfwGetTime();
float u = (std::sin(t * 1.5f) * 0.5f + 0.5f);
float v = (std::cos(t * 1.1f) * 0.5f + 0.5f);
if (u + v > 1.0f)
{
u = 1.0f - u;
v = 1.0f - v;
}
float w = 1.0f - u - v;
if (w > 1.0f)
{
float diff = w - 1.0f;
w = 1.0f;
u += diff / 2.0f;
v += diff / 2.0f;
}
else if (w < 0.0f)
{
float diff = -w;
w = 0.0f;
u -= diff / 2.0f;
v -= diff / 2.0f;
}
Vector3<float> P = triangle.m_vertex1 * u + triangle.m_vertex2 * v + triangle.m_vertex3 * w;
std::vector<float> dynData;
updateDynamicData(dynData, u, v, w, P, triangle);
glBindVertexArray(VAO_dyn);
glBindBuffer(GL_ARRAY_BUFFER, VBO_dyn);
glBufferSubData(GL_ARRAY_BUFFER, 0, dynData.size() * sizeof(float), dynData.data());
// Draw lines
glDrawArrays(GL_LINES, 0, 6);
// Draw text lines
int numTextVertices = (dynData.size() / 8) - 10;
if (numTextVertices > 0)
{
glDrawArrays(GL_LINES, 10, numTextVertices);
}
// Draw dots
glDrawArrays(GL_POINTS, 6, 4);
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}

View File

@@ -0,0 +1,14 @@
project(example_glfw3)
add_executable(${PROJECT_NAME} example_glfw3.cpp)
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 23
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(OpenGL)
find_package(GLEW REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath GLEW::GLEW glfw OpenGL::GL)

View File

@@ -0,0 +1,419 @@
// 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;
}
std::cout << "GLFW Version: " << glfwGetVersionString() << "\n";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Force GLX context creation API to ensure compatibility with GLEW
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
constexpr int SCR_WIDTH = 800;
constexpr 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);
// Check if context is valid using standard GL
const GLubyte* renderer = glGetString(GL_RENDERER);
const GLubyte* version = glGetString(GL_VERSION);
if (renderer && version)
{
std::cout << "Renderer: " << renderer << "\n";
std::cout << "OpenGL version supported: " << version << "\n";
}
else
{
std::cerr << "Failed to get GL_RENDERER or GL_VERSION. Context might be invalid.\n";
}
// ---------- GLEW init ----------
glewExperimental = GL_TRUE;
GLenum glewErr = glewInit();
if (glewErr != GLEW_OK)
{
// Ignore NO_GLX_DISPLAY if we have a valid context
if (glewErr == GLEW_ERROR_NO_GLX_DISPLAY && renderer)
{
std::cerr << "GLEW warning: " << glewGetErrorString(glewErr) << " (Ignored because context seems valid)\n";
}
else
{
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_element_buffer_object.size() * 3);
for (const auto& tri : cube.m_element_buffer_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();
float lastX = 640.0f / 2.0f;
float lastY = 480.0f / 2.0f;
bool firstMouse = true;
bool mouse_capture = false;
float mouseSensitivity = 0.1f;
// ---------- Main loop ----------
static double old_mouse_time = glfwGetTime();
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
omath::Vector3<float> move_dir;
if (glfwGetKey(window, GLFW_KEY_W))
move_dir += camera.get_forward();
if (glfwGetKey(window, GLFW_KEY_A))
move_dir -= camera.get_right();
if (glfwGetKey(window, GLFW_KEY_S))
move_dir -= camera.get_forward();
if (glfwGetKey(window, GLFW_KEY_D))
move_dir += camera.get_right();
if (glfwGetKey(window, GLFW_KEY_SPACE))
move_dir += camera.get_up();
if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL))
move_dir -= camera.get_up();
auto delta = glfwGetTime() - old_mouse_time;
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) && delta > 0.4)
{
old_mouse_time = glfwGetTime();
mouse_capture = !mouse_capture;
glfwSetInputMode(window, GLFW_CURSOR, mouse_capture ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL);
}
if (mouse_capture)
{
int x, y;
glfwGetWindowSize(window, &x, &y);
camera.set_origin(camera.get_origin() + (move_dir.normalized() * 0.4f));
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
float xoffset = (float)xpos - x / 2.f;
float yoffset = y / 2.f - ypos ; // reversed: y-coordinates go bottom->top for pitch
xoffset *= mouseSensitivity;
yoffset *= mouseSensitivity;
auto new_angles = camera.get_view_angles();
new_angles.pitch += decltype(new_angles.pitch)::from_degrees(yoffset);
new_angles.yaw -= decltype(new_angles.yaw)::from_degrees(xoffset);
camera.set_view_angles(new_angles);
glfwSetCursorPos(window, x / 2., y / 2);
}
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

@@ -0,0 +1,10 @@
project(example_projection_matrix_builder)
add_executable(${PROJECT_NAME} example_proj_mat_builder.cpp)
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 23
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(${PROJECT_NAME} PRIVATE omath::omath)

View File

@@ -0,0 +1,10 @@
project(example_signature_scan)
add_executable(${PROJECT_NAME} example_signature_scan.cpp)
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 23
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(${PROJECT_NAME} PRIVATE omath::omath)

View File

@@ -0,0 +1,10 @@
project(example_var_encryption)
add_executable(${PROJECT_NAME} main.cpp)
set_target_properties(
${PROJECT_NAME}
PROPERTIES CXX_STANDARD 23
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(${PROJECT_NAME} PRIVATE omath::omath)

View File

@@ -0,0 +1,15 @@
//
// Created by orange on 24.02.2026.
//
#include "omath/containers/encrypted_variable.hpp"
#include <omath/omath.hpp>
#include <print>
int main()
{
OMATH_DEF_CRYPT_VAR(int, 64) var{5};
var.encrypt();
std::println("{}", var.value());
var.decrypt();
std::println("{}", var.value());
return var.value();
}

View File

@@ -3,14 +3,60 @@
//
#pragma once
#include "mesh.hpp"
#include "omath/engines/opengl_engine/camera.hpp"
#include "omath/engines/opengl_engine/traits/mesh_trait.hpp"
#include "omath/linear_algebra/triangle.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <array>
namespace omath::primitives
{
template<class BoxMeshType>
[[nodiscard]]
std::array<Triangle<Vector3<float>>, 12> create_box(const Vector3<float>& top, const Vector3<float>& bottom,
const Vector3<float>& dir_forward, const Vector3<float>& dir_right,
float ratio = 4.f) noexcept;
BoxMeshType create_box(const Vector3<float>& top, const Vector3<float>& bottom, const Vector3<float>& dir_forward,
const Vector3<float>& dir_right, const float ratio = 4.f) noexcept
{
const auto height = top.distance_to(bottom);
const auto side_size = height / ratio;
// corner layout (03 bottom, 47 top)
std::array<Vector3<float>, 8> p;
p[0] = bottom + (dir_forward + dir_right) * side_size; // frontrightbottom
p[1] = bottom + (dir_forward - dir_right) * side_size; // frontleftbottom
p[2] = bottom + (-dir_forward + dir_right) * side_size; // backrightbottom
p[3] = bottom + (-dir_forward - dir_right) * side_size; // backleftbottom
p[4] = top + (dir_forward + dir_right) * side_size; // frontrighttop
p[5] = top + (dir_forward - dir_right) * side_size; // frontlefttop
p[6] = top + (-dir_forward + dir_right) * side_size; // backrighttop
p[7] = top + (-dir_forward - dir_right) * side_size; // backlefttop
std::array<Vector3<std::uint32_t>, 12> poly;
// bottom face (+Y up ⇒ wind CW when viewed from above)
poly[0] = {0, 2, 3};
poly[1] = {0, 3, 1};
// top face
poly[2] = {4, 7, 6};
poly[3] = {4, 5, 7};
// front face
poly[4] = {0, 5, 1};
poly[5] = {0, 4, 5};
// right face
poly[6] = {0, 6, 2};
poly[7] = {0, 4, 6};
// back face
poly[8] = {2, 7, 3};
poly[9] = {2, 6, 7};
// left face
poly[10] = {1, 7, 5};
poly[11] = {1, 3, 7};
return BoxMeshType{std::move(p), std::move(poly)};
}
} // namespace omath::primitives

View File

@@ -6,35 +6,58 @@
#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 VboType = std::vector<VertType>, class EboType = std::vector<Vector3<std::uint32_t>>>
class Mesh final
{
public:
using NumericType = Type;
using VectorType = VertType::VectorType;
using VertexType = VboType::value_type;
private:
using Vbo = std::vector<Vector3<NumericType>>;
using Vao = std::vector<Vector3<std::size_t>>;
using Vbo = VboType;
using Ebo = EboType;
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(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 +70,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 +92,40 @@ 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
{
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
{
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))};
if constexpr (HasPosition<VertexType>)
{
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)};
}
return {vertex_position_to_world_space(m_vertex_buffer.at(vao_iterator->x)),
vertex_position_to_world_space(m_vertex_buffer.at(vao_iterator->y)),
vertex_position_to_world_space(m_vertex_buffer.at(vao_iterator->z))};
}
private:
Vector3<NumericType> m_origin;
Vector3<NumericType> m_scale;
VectorType m_origin;
VectorType m_scale;
RotationAngles m_rotation_angles;

View File

@@ -3,14 +3,30 @@
//
#pragma once
#include "mesh.hpp"
#include "omath/engines/opengl_engine/camera.hpp"
#include "omath/engines/opengl_engine/mesh.hpp"
#include "omath/engines/opengl_engine/traits/mesh_trait.hpp"
#include "omath/linear_algebra/triangle.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <array>
namespace omath::primitives
{
template<class PlaneMeshType>
[[nodiscard]]
std::array<Triangle<Vector3<float>>, 2> create_plane(const Vector3<float>& vertex_a,
const Vector3<float>& vertex_b,
const Vector3<float>& direction, float size) noexcept;
PlaneMeshType create_plane(const Vector3<float>& vertex_a, const Vector3<float>& vertex_b,
const Vector3<float>& direction, const float size) noexcept
{
const auto second_vertex_a = vertex_a + direction * size;
const auto second_vertex_b = vertex_b + direction * size;
std::array<Vector3<float>, 4> grid = {vertex_a, vertex_b, second_vertex_a, second_vertex_b};
std::array<Vector3<std::uint32_t>, 2> poly;
poly[0] = {1, 1, 2};
poly[1] = {0, 1, 3};
return PlaneMeshType(std::move(grid), std::move(poly));
}
} // namespace omath::primitives

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

@@ -0,0 +1,309 @@
#pragma once
#include "simplex.hpp"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <limits>
#include <memory>
#include <memory_resource>
#include <queue>
#include <utility>
#include <vector>
namespace omath::collision
{
template<class V, class FloatingType>
concept EpaVector = requires(const V& a, const V& b, FloatingType s) {
{ a - b } -> std::same_as<V>;
{ a.cross(b) } -> std::same_as<V>;
{ a.dot(b) } -> std::same_as<FloatingType>;
{ -a } -> std::same_as<V>;
{ a * s } -> std::same_as<V>;
{ a / s } -> std::same_as<V>;
};
template<class ColliderInterfaceType>
class Epa final
{
public:
using VectorType = ColliderInterfaceType::VectorType;
static_assert(EpaVector<VectorType, typename VectorType::ContainedType>,
"VertexType must satisfy EpaVector concept");
private:
using FloatingType = VectorType::ContainedType;
public:
struct Result final
{
VectorType normal{}; // from A to B
VectorType penetration_vector;
FloatingType depth{0.0};
int iterations{0};
int num_vertices{0};
int num_faces{0};
};
struct Params final
{
int max_iterations{64};
FloatingType tolerance{1e-4}; // absolute tolerance on distance growth
};
// Precondition: simplex.size()==4 and contains the origin.
[[nodiscard]]
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::pmr::vector<VectorType> vertexes = build_initial_polytope_from_simplex(simplex, mem_resource);
// Initial tetra faces (windings corrected in make_face)
std::pmr::vector<Face> faces = create_initial_tetra_faces(mem_resource, vertexes);
auto heap = rebuild_heap(faces, mem_resource);
Result out{};
for (int it = 0; it < params.max_iterations; ++it)
{
// If heap might be stale after face edits, rebuild lazily.
if (heap.empty())
break;
// Rebuild when the "closest" face changed (simple cheap guard)
// (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, mem_resource);
if (heap.empty())
break;
// FIXME: STORE REF VALUE, DO NOT USE
// AFTER IF STATEMENT BLOCK
const Face& face = faces[heap.top().idx];
// Get the furthest point in face normal direction
const VectorType p = support_point(a, b, face.n);
const auto p_dist = face.n.dot(p);
// Converged if we cant push the face closer than tolerance
if (p_dist - face.d <= params.tolerance)
{
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());
out.penetration_vector = out.normal * out.depth;
return out;
}
// Add new vertex
const int new_idx = static_cast<int>(vertexes.size());
vertexes.emplace_back(p);
const auto [to_delete, boundary] = mark_visible_and_collect_horizon(faces, p);
erase_marked(faces, to_delete);
// Stitch new faces around the horizon
for (const auto& e : boundary)
faces.emplace_back(make_face(vertexes, e.a, e.b, new_idx));
// Rebuild heap after topology change
heap = rebuild_heap(faces, mem_resource);
if (!std::isfinite(vertexes.back().dot(vertexes.back())))
break; // safety
out.iterations = it + 1;
}
if (faces.empty())
return std::nullopt;
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;
return out;
}
private:
struct Face final
{
int i0, i1, i2;
VectorType n; // unit outward normal
FloatingType d; // n · v0 (>=0 ideally because origin is inside)
};
struct Edge final
{
int a, b;
};
struct HeapItem final
{
FloatingType d;
int idx;
};
struct HeapCmp final
{
[[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::pmr::vector<HeapItem>, HeapCmp>;
[[nodiscard]]
static Heap rebuild_heap(const std::pmr::vector<Face>& faces, auto& memory_resource)
{
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.emplace(faces[i].d, i);
return h; // allocator is preserved
}
[[nodiscard]]
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 > static_cast<FloatingType>(1e-7);
}
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::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.emplace_back(a, b); // horizon edge (directed)
}
[[nodiscard]]
static Face make_face(const std::pmr::vector<VectorType>& vertexes, int i0, int i1, int i2)
{
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) <= static_cast<FloatingType>(1e-30))
{
n = any_perp_vec(a1 - a0); // degenerate guard
}
// Ensure normal points outward (away from origin): require n·a0 >= 0
if (n.dot(a0) < static_cast<FloatingType>(0.0))
{
std::swap(i1, i2);
n = -n;
}
const auto inv_len =
static_cast<FloatingType>(1.0) / std::sqrt(std::max(n.dot(n), static_cast<FloatingType>(1e-30)));
n = n * inv_len;
const auto d = n.dot(a0);
return {i0, i1, i2, n, d};
}
[[nodiscard]]
static VectorType support_point(const ColliderInterfaceType& a, const ColliderInterfaceType& b,
const VectorType& dir)
{
return a.find_abs_furthest_vertex_position(dir) - b.find_abs_furthest_vertex_position(-dir);
}
template<class V>
[[nodiscard]]
static constexpr bool near_zero_vec(const V& v, const FloatingType eps = 1e-7f)
{
return v.dot(v) <= eps * eps;
}
template<class V>
[[nodiscard]]
static constexpr V any_perp_vec(const V& v)
{
for (const auto& dir : {V{1, 0, 0}, V{0, 1, 0}, V{0, 0, 1}})
if (const auto d = v.cross(dir); !near_zero_vec(d))
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

@@ -3,31 +3,44 @@
//
#pragma once
#include "mesh_collider.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include "simplex.hpp"
namespace omath::collision
{
template<class ColliderType>
template<class VertexType>
struct GjkHitInfo final
{
bool hit{false};
Simplex<VertexType> simplex; // valid only if hit == true and size==4
};
template<class ColliderInterfaceType>
class GjkAlgorithm final
{
using VectorType = ColliderInterfaceType::VectorType;
public:
[[nodiscard]]
static ColliderType::VertexType find_support_vertex(const ColliderType& collider_a,
const ColliderType& collider_b,
const ColliderType::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)
{
// Get initial support point in any direction
auto support = find_support_vertex(collider_a, collider_b, {1, 0, 0});
return is_collide_with_simplex_info(collider_a, collider_b).hit;
}
Simplex<typename ColliderType::VertexType> simplex;
[[nodiscard]]
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, VectorType{1, 0, 0});
Simplex<VectorType> simplex;
simplex.push_front(support);
auto direction = -support;
@@ -37,12 +50,12 @@ namespace omath::collision
support = find_support_vertex(collider_a, collider_b, direction);
if (support.dot(direction) <= 0.f)
return false;
return {false, simplex};
simplex.push_front(support);
if (simplex.handle(direction))
return true;
return {true, simplex};
}
}
};

View File

@@ -8,30 +8,103 @@
namespace omath::collision
{
class Ray
template<class T = Vector3<float>>
class Ray final
{
public:
Vector3<float> start;
Vector3<float> end;
using VectorType = T;
VectorType start;
VectorType end;
bool infinite_length = false;
[[nodiscard]]
Vector3<float> direction_vector() const noexcept;
constexpr VectorType direction_vector() const noexcept
{
return end - start;
}
[[nodiscard]]
Vector3<float> direction_vector_normalized() const noexcept;
};
class LineTracer
constexpr VectorType direction_vector_normalized() const noexcept
{
return direction_vector().normalized();
}
};
template<class RayType = Ray<>>
class LineTracer final
{
using TriangleType = Triangle<typename RayType::VectorType>;
public:
LineTracer() = delete;
[[nodiscard]]
static bool can_trace_line(const Ray& ray, const Triangle<Vector3<float>>& triangle) noexcept;
constexpr static bool can_trace_line(const RayType& ray, const TriangleType& triangle) noexcept
{
return get_ray_hit_point(ray, triangle) == ray.end;
}
// Realization of MöllerTrumbore intersection algorithm
// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
[[nodiscard]]
static Vector3<float> get_ray_hit_point(const Ray& ray, const Triangle<Vector3<float>>& triangle) noexcept;
constexpr static auto get_ray_hit_point(const RayType& ray, const TriangleType& triangle) noexcept
{
constexpr float k_epsilon = std::numeric_limits<float>::epsilon();
const auto side_a = triangle.side_a_vector();
const auto side_b = triangle.side_b_vector();
const auto ray_dir = ray.direction_vector();
const auto p = ray_dir.cross(side_b);
const auto det = side_a.dot(p);
if (std::abs(det) < k_epsilon)
return ray.end;
const auto inv_det = 1 / det;
const auto t = ray.start - triangle.m_vertex2;
const auto u = t.dot(p) * inv_det;
if ((u < 0 && std::abs(u) > k_epsilon) || (u > 1 && std::abs(u - 1) > k_epsilon))
return ray.end;
const auto q = t.cross(side_a);
// ReSharper disable once CppTooWideScopeInitStatement
const auto v = ray_dir.dot(q) * inv_det;
if ((v < 0 && std::abs(v) > k_epsilon) || (u + v > 1 && std::abs(u + v - 1) > k_epsilon))
return ray.end;
const auto t_hit = side_b.dot(q) * inv_det;
if (ray.infinite_length && t_hit <= k_epsilon)
return ray.end;
if (t_hit <= k_epsilon || t_hit > 1 - k_epsilon)
return ray.end;
return ray.start + ray_dir * t_hit;
}
template<class MeshType>
[[nodiscard]]
constexpr static auto get_ray_hit_point(const RayType& ray, const MeshType& mesh) noexcept
{
auto mesh_hit = ray.end;
const auto begin = mesh.m_element_buffer_object.cbegin();
const auto end = mesh.m_element_buffer_object.cend();
for (auto current = begin; current < end; current = std::next(current))
{
const auto face = mesh.make_face_in_world_space(current);
auto ray_stop_point = get_ray_hit_point(ray, face);
if (ray_stop_point.distance_to(ray.start) < mesh_hit.distance_to(ray.start))
mesh_hit = ray_stop_point;
}
return mesh_hit;
}
};
} // namespace omath::collision

View File

@@ -3,35 +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 Vector3<float>& find_furthest_vertex(const Vector3<float>& 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]]
Vector3<float> find_abs_furthest_vertex(const Vector3<float>& direction) const
const VectorType& get_origin() const override
{
return m_mesh.vertex_to_world_space(find_furthest_vertex(direction));
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

@@ -47,10 +47,13 @@ namespace omath::collision
++m_size;
}
[[nodiscard]]
constexpr const VectorType& operator[](std::size_t i) const noexcept
{
return m_points[i];
}
[[nodiscard]]
constexpr VectorType& operator[](std::size_t i) noexcept
{
return m_points[i];
@@ -126,24 +129,24 @@ namespace omath::collision
}
template<class V>
static constexpr bool near_zero(const V& v, const float eps = 1e-7f)
[[nodiscard]]
static constexpr bool near_zero(const V& v, const float eps = 1e-7f) noexcept
{
return v.dot(v) <= eps * eps;
}
template<class V>
[[nodiscard]]
static constexpr V any_perp(const V& v)
{
// try cross with axes until non-zero
V d = v.cross(V{1, 0, 0});
if (near_zero(d))
d = v.cross(V{0, 1, 0});
if (near_zero(d))
d = v.cross(V{0, 0, 1});
for (const auto& dir : {V{1, 0, 0}, {0, 1, 0}, {0, 0, 1}})
if (const auto d = v.cross(dir); !near_zero(d))
return d;
std::unreachable();
}
constexpr bool handle_line(VectorType& direction)
[[nodiscard]]
constexpr bool handle_line(VectorType& direction) noexcept
{
const auto& a = m_points[0];
const auto& b = m_points[1];
@@ -155,21 +158,11 @@ namespace omath::collision
{
// ReSharper disable once CppTooWideScopeInitStatement
auto n = ab.cross(ao); // Needed to valid handle collision if colliders placed at same origin pos
if (near_zero(n))
{
// collinear: origin lies on ray AB (often on segment), pick any perp to escape
direction = any_perp(ab);
direction = near_zero(n) ? any_perp(ab) : n.cross(ab);
return false;
}
else
{
direction = n.cross(ab);
}
}
else
{
*this = {a};
direction = ao;
}
return false;
}

View File

@@ -0,0 +1,200 @@
//
// Created by Vladislav on 04.01.2026.
//
#pragma once
#include <VMProtectSDK.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <span>
#include <source_location>
#ifdef OMATH_ENABLE_FORCE_INLINE
#ifdef _MSC_VER
#define OMATH_FORCE_INLINE __forceinline
#else
#define OMATH_FORCE_INLINE __attribute__((always_inline)) inline
#endif
#else
#define OMATH_FORCE_INLINE
#endif
namespace omath::detail
{
[[nodiscard]]
consteval std::uint64_t fnv1a_64(const char* s)
{
std::uint64_t h = 14695981039346656037ull;
while (*s)
{
h ^= static_cast<unsigned char>(*s++);
h *= 1099511628211ull;
}
return h;
}
// SplitMix64 mixer (good quality for seeding / scrambling)
[[nodiscard]]
consteval std::uint64_t splitmix64(std::uint64_t x)
{
x += 0x9E3779B97F4A7C15ull;
x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ull;
x = (x ^ (x >> 27)) * 0x94D049BB133111EBull;
return x ^ (x >> 31);
}
// Choose your policy:
// - If you want reproducible builds, REMOVE __DATE__/__TIME__.
// - If you want "different each build", keep them.
[[nodiscard]]
consteval std::uint64_t base_seed()
{
std::uint64_t h = 0;
h ^= fnv1a_64(__FILE__);
h ^= splitmix64(fnv1a_64(__DATE__));
h ^= splitmix64(fnv1a_64(__TIME__));
return splitmix64(h);
}
// Produce a "random" 64-bit value for a given stream index (compile-time)
template<std::uint64_t Stream>
[[nodiscard]]
consteval std::uint64_t rand_u64()
{
// Stream is usually __COUNTER__ so each call site differs
return splitmix64(base_seed() + 0xD1B54A32D192ED03ull * (Stream + 1));
}
template<std::int64_t Lo, std::int64_t Hi, std::uint64_t Stream>
[[nodiscard]]
consteval std::int64_t rand_uint8_t()
{
static_assert(Lo <= Hi);
const std::uint64_t r = rand_u64<Stream>();
return static_cast<std::int64_t>(r) + Lo;
}
[[nodiscard]]
consteval std::uint64_t rand_u64(const std::uint64_t seed, const std::uint64_t i)
{
return splitmix64(seed + 0xD1B54A32D192ED03ull * (i + 1ull));
}
// Convert to int (uses low 32 bits; you can also use high bits if you prefer)
[[nodiscard]]
consteval std::uint8_t rand_uint8t(const std::uint64_t seed, const std::uint64_t i)
{
return static_cast<std::uint8_t>(rand_u64(seed, i)); // narrowing is fine/deterministic
}
template<std::size_t N, std::uint64_t Seed, std::size_t... I>
[[nodiscard]]
consteval std::array<std::uint8_t, N> make_array_impl(std::index_sequence<I...>)
{
return {rand_uint8t(Seed, static_cast<std::uint64_t>(I))...};
}
template<std::size_t N, std::uint64_t Seed>
[[nodiscard]]
consteval std::array<std::uint8_t, N> make_array()
{
return make_array_impl<N, Seed>(std::make_index_sequence<N>{});
}
} // namespace omath::detail
namespace omath
{
template<class T>
class VarAnchor;
template<class T, std::size_t key_size, std::array<std::uint8_t, key_size> key>
class EncryptedVariable final
{
using value_type = std::remove_cvref_t<T>;
bool m_is_encrypted{};
value_type m_data{};
OMATH_FORCE_INLINE void xor_contained_var_by_key()
{
VMProtectBeginVirtualization(nullptr);
// Safe, keeps const-correctness, and avoids reinterpret_cast issues
auto bytes = std::as_writable_bytes(std::span<value_type, 1>{&m_data, 1});
for (std::size_t i = 0; i < bytes.size(); ++i)
{
const auto k = static_cast<std::uint8_t>(key[i % key_size] + (i * key_size));
bytes[i] ^= static_cast<std::byte>(k);
}
VMProtectEnd();
}
public:
OMATH_FORCE_INLINE constexpr explicit EncryptedVariable(const value_type& data)
: m_is_encrypted(false), m_data(data)
{
encrypt();
}
[[nodiscard]] constexpr bool is_encrypted() const
{
return m_is_encrypted;
}
OMATH_FORCE_INLINE void decrypt()
{
if (!m_is_encrypted)
return;
xor_contained_var_by_key();
m_is_encrypted = false;
}
OMATH_FORCE_INLINE void encrypt()
{
if (m_is_encrypted)
return;
xor_contained_var_by_key();
m_is_encrypted = true;
}
[[nodiscard]] OMATH_FORCE_INLINE constexpr value_type& value()
{
return m_data;
}
[[nodiscard]] OMATH_FORCE_INLINE constexpr const value_type& value() const
{
return m_data;
}
constexpr OMATH_FORCE_INLINE ~EncryptedVariable()
{
decrypt();
}
[[nodiscard]] constexpr OMATH_FORCE_INLINE auto drop_anchor()
{
return VarAnchor{*this};
}
};
template<class EncryptedVarType>
class VarAnchor final
{
public:
// ReSharper disable once CppNonExplicitConvertingConstructor
OMATH_FORCE_INLINE constexpr VarAnchor(EncryptedVarType& var): m_var(var)
{
m_var.decrypt();
}
OMATH_FORCE_INLINE constexpr ~VarAnchor()
{
m_var.encrypt();
}
private:
EncryptedVarType& m_var;
};
} // namespace omath
#define OMATH_CT_RAND_ARRAY_BYTE(N) \
(::omath::detail::make_array<(N), (::omath::detail::base_seed() ^ static_cast<std::uint64_t>(__COUNTER__))>())
#define OMATH_DEF_CRYPT_VAR(TYPE, KEY_SIZE) omath::EncryptedVariable<TYPE, KEY_SIZE, OMATH_CT_RAND_ARRAY_BYTE(KEY_SIZE)>

View File

@@ -0,0 +1,13 @@
//
// Created by Vlad on 3/22/2025.
//
#pragma once
#include "omath/engines/cry_engine/constants.hpp"
#include "omath/projection/camera.hpp"
#include "traits/camera_trait.hpp"
namespace omath::cry_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>;
} // namespace omath::cry_engine

View File

@@ -0,0 +1,25 @@
//
// Created by Vlad on 10/21/2025.
//
#pragma once
#include "omath/linear_algebra/mat.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <omath/trigonometry/angle.hpp>
#include <omath/trigonometry/view_angles.hpp>
namespace omath::cry_engine
{
constexpr Vector3<float> k_abs_up = {0, 0, 1};
constexpr Vector3<float> k_abs_right = {1, 0, 0};
constexpr Vector3<float> k_abs_forward = {0, 1, 0};
using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
using PitchAngle = Angle<float, -90.f, 90.f, AngleFlags::Clamped>;
using YawAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
using RollAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
using ViewAngles = omath::ViewAngles<PitchAngle, YawAngle, RollAngle>;
} // namespace omath::cry_engine

View File

@@ -0,0 +1,74 @@
//
// Created by Vlad on 3/22/2025.
//
#pragma once
#include "omath/engines/cry_engine/constants.hpp"
namespace omath::cry_engine
{
[[nodiscard]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept;
[[nodiscard]]
Vector3<float> right_vector(const ViewAngles& angles) noexcept;
[[nodiscard]]
Vector3<float> up_vector(const ViewAngles& angles) noexcept;
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
[[nodiscard]]
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::cry_engine

View File

@@ -0,0 +1,12 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include "constants.hpp"
#include "omath/3d_primitives/mesh.hpp"
#include "traits/mesh_trait.hpp"
namespace omath::cry_engine
{
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
}

View File

@@ -0,0 +1,24 @@
//
// Created by Vlad on 8/10/2025.
//
#pragma once
#include "omath/engines/cry_engine/formulas.hpp"
#include "omath/projection/camera.hpp"
namespace omath::cry_engine
{
class CameraTrait final
{
public:
[[nodiscard]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept;
[[nodiscard]]
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
[[nodiscard]]
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept;
};
} // namespace omath::cry_engine

View File

@@ -0,0 +1,19 @@
//
// Created by Vladislav on 09.11.2025.
//
#pragma once
#include <omath/engines/cry_engine/constants.hpp>
#include <omath/engines/cry_engine/formulas.hpp>
namespace omath::cry_engine
{
class MeshTrait final
{
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{
return cry_engine::rotation_matrix(rotation);
}
};
} // namespace omath::cry_engine

View File

@@ -0,0 +1,76 @@
//
// Created by Vlad on 8/6/2025.
//
#pragma once
#include "omath/engines/cry_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::cry_engine
{
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
auto current_pos = projectile.m_origin
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time;
current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne)
predicted.z -= gravity * (time * time) * 0.5f;
return predicted;
}
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.y * delta.y);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.z;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::asin(direction.z));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(-std::atan2(direction.x, direction.y));
};
};
} // namespace omath::cry_engine

View File

@@ -23,4 +23,52 @@ namespace omath::frostbite_engine
[[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
} // namespace omath::unity_engine
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::frostbite_engine

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

@@ -0,0 +1,17 @@
//
// Created by Vladislav on 27.01.2026.
//
#pragma once
#include "mesh.hpp"
#include "omath/engines/frostbite_engine/traits/mesh_trait.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <array>
namespace omath::frostbite_engine
{
using BoxMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>, std::array<Vector3<float>, 8>,
std::array<Vector3<std::uint32_t>, 12>>;
using PlaneMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>,
std::array<Vector3<float>, 4>, std::array<Vector3<std::uint32_t>, 2>>;
} // namespace omath::frostbite_engine

View File

@@ -55,7 +55,7 @@ namespace omath::frostbite_engine
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
return {predicted_target_position.x, projectile.m_origin.y + height, predicted_target_position.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down

View File

@@ -23,4 +23,54 @@ namespace omath::iw_engine
[[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
return units * centimeter_in_unit;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units_to_centimeters(units) / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
return centimeters / centimeter_in_unit;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return centimeters_to_units(meters * static_cast<FloatingType>(100));
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::iw_engine

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

@@ -0,0 +1,17 @@
//
// Created by Vladislav on 27.01.2026.
//
#pragma once
#include "mesh.hpp"
#include "omath/engines/iw_engine/traits/mesh_trait.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <array>
namespace omath::iw_engine
{
using BoxMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>, std::array<Vector3<float>, 8>,
std::array<Vector3<std::uint32_t>, 12>>;
using PlaneMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>,
std::array<Vector3<float>, 4>, std::array<Vector3<std::uint32_t>, 2>>;
} // namespace omath::iw_engine

View File

@@ -8,5 +8,5 @@
namespace omath::opengl_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>;
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, true>;
} // namespace omath::opengl_engine

View File

@@ -4,7 +4,6 @@
#pragma once
#include "omath/engines/opengl_engine/constants.hpp"
namespace omath::opengl_engine
{
[[nodiscard]]
@@ -23,4 +22,52 @@ namespace omath::opengl_engine
[[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::opengl_engine

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

@@ -0,0 +1,17 @@
//
// Created by Vladislav on 27.01.2026.
//
#pragma once
#include "mesh.hpp"
#include "omath/engines/opengl_engine/traits/mesh_trait.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <array>
namespace omath::opengl_engine
{
using BoxMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>, std::array<Vector3<float>, 8>,
std::array<Vector3<std::uint32_t>, 12>>;
using PlaneMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>,
std::array<Vector3<float>, 4>, std::array<Vector3<std::uint32_t>, 2>>;
} // namespace omath::opengl_engine

View File

@@ -55,7 +55,7 @@ namespace omath::opengl_engine
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
return {predicted_target_position.x, projectile.m_origin.y + height, predicted_target_position.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down

View File

@@ -22,4 +22,54 @@ namespace omath::source_engine
[[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
return units * centimeter_in_unit;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units_to_centimeters(units) / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
return centimeters / centimeter_in_unit;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return centimeters_to_units(meters * static_cast<FloatingType>(100));
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::source_engine

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

@@ -0,0 +1,17 @@
//
// Created by Vladislav on 27.01.2026.
//
#pragma once
#include "mesh.hpp"
#include "omath/engines/source_engine/traits/mesh_trait.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <array>
namespace omath::source_engine
{
using BoxMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>, std::array<Vector3<float>, 8>,
std::array<Vector3<std::uint32_t>, 12>>;
using PlaneMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>,
std::array<Vector3<float>, 4>, std::array<Vector3<std::uint32_t>, 2>>;
} // namespace omath::source_engine

View File

@@ -23,4 +23,52 @@ namespace omath::unity_engine
[[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::unity_engine

View File

@@ -0,0 +1,17 @@
//
// Created by Vladislav on 27.01.2026.
//
#pragma once
#include "mesh.hpp"
#include "omath/engines/unity_engine/traits/mesh_trait.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <array>
namespace omath::unity_engine
{
using BoxMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>, std::array<Vector3<float>, 8>,
std::array<Vector3<std::uint32_t>, 12>>;
using PlaneMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>,
std::array<Vector3<float>, 4>, std::array<Vector3<std::uint32_t>, 2>>;
} // namespace omath::unity_engine

View File

@@ -55,7 +55,7 @@ namespace omath::unity_engine
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
return {predicted_target_position.x, projectile.m_origin.y + height, predicted_target_position.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down

View File

@@ -23,4 +23,52 @@ namespace omath::unreal_engine
[[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::unreal_engine

View File

@@ -0,0 +1,17 @@
//
// Created by Vladislav on 27.01.2026.
//
#pragma once
#include "mesh.hpp"
#include "omath/engines/unreal_engine/traits/mesh_trait.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <array>
namespace omath::unreal_engine
{
using BoxMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>, std::array<Vector3<float>, 8>,
std::array<Vector3<std::uint32_t>, 12>>;
using PlaneMesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait, primitives::Vertex<>,
std::array<Vector3<float>, 4>, std::array<Vector3<std::uint32_t>, 2>>;
} // namespace omath::unreal_engine

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;
}
@@ -373,7 +380,7 @@ namespace omath
{
const auto det = determinant();
if (det == 0)
if (std::abs(det) < std::numeric_limits<Type>::epsilon())
return std::nullopt;
const auto transposed_mat = transposed();

View File

@@ -26,12 +26,12 @@ namespace omath
{
}
Vector3<float> m_vertex1;
Vector3<float> m_vertex2;
Vector3<float> m_vertex3;
Vector m_vertex1;
Vector m_vertex2;
Vector m_vertex3;
[[nodiscard]]
constexpr Vector3<float> calculate_normal() const
constexpr Vector calculate_normal() const
{
const auto b = side_b_vector();
const auto a = side_a_vector();
@@ -40,25 +40,25 @@ namespace omath
}
[[nodiscard]]
float side_a_length() const
Vector::ContainedType side_a_length() const
{
return m_vertex1.distance_to(m_vertex2);
}
[[nodiscard]]
float side_b_length() const
Vector::ContainedType side_b_length() const
{
return m_vertex3.distance_to(m_vertex2);
}
[[nodiscard]]
constexpr Vector3<float> side_a_vector() const
constexpr Vector side_a_vector() const
{
return m_vertex1 - m_vertex2;
}
[[nodiscard]]
constexpr float hypot() const
constexpr Vector::ContainedType hypot() const
{
return m_vertex1.distance_to(m_vertex3);
}
@@ -72,12 +72,12 @@ namespace omath
return std::abs(side_a * side_a + side_b * side_b - hypot_value * hypot_value) <= 0.0001f;
}
[[nodiscard]]
constexpr Vector3<float> side_b_vector() const
constexpr Vector side_b_vector() const
{
return m_vertex3 - m_vertex2;
}
[[nodiscard]]
constexpr Vector3<float> mid_point() const
constexpr Vector mid_point() const
{
return (m_vertex1 + m_vertex2 + m_vertex3) / 3;
}

View File

@@ -19,6 +19,7 @@ namespace omath
class Vector2
{
public:
using ContainedType = Type;
Type x = static_cast<Type>(0);
Type y = static_cast<Type>(0);
@@ -219,6 +220,12 @@ namespace omath
{
return std::make_tuple(x, y);
}
[[nodiscard]]
constexpr std::array<Type, 2> as_array() const noexcept
{
return {x, y};
}
#ifdef OMATH_IMGUI_INTEGRATION
[[nodiscard]]
constexpr ImVec2 to_im_vec2() const noexcept

View File

@@ -4,8 +4,8 @@
#pragma once
#include "omath/trigonometry/angle.hpp"
#include "omath/linear_algebra/vector2.hpp"
#include "omath/trigonometry/angle.hpp"
#include <cstdint>
#include <expected>
#include <functional>
@@ -23,6 +23,7 @@ namespace omath
class Vector3 : public Vector2<Type>
{
public:
using ContainedType = Type;
Type z = static_cast<Type>(0);
constexpr Vector3(const Type& x, const Type& y, const Type& z) noexcept: Vector2<Type>(x, y), z(z)
{
@@ -232,10 +233,11 @@ namespace omath
return Angle<float, 0.f, 180.f, AngleFlags::Clamped>::from_radians(std::acos(dot(other) / bottom));
}
[[nodiscard]] bool is_perpendicular(const Vector3& other) const noexcept
[[nodiscard]] bool is_perpendicular(const Vector3& other,
Type epsilon = static_cast<Type>(0.0001)) const noexcept
{
if (const auto angle = angle_between(other))
return angle->as_degrees() == static_cast<Type>(90);
return std::abs(angle->as_degrees() - static_cast<Type>(90)) <= epsilon;
return false;
}
@@ -273,6 +275,12 @@ namespace omath
{
return length() >= other.length();
}
[[nodiscard]]
constexpr std::array<Type, 3> as_array() const noexcept
{
return {this->x, this->y, z};
}
};
} // namespace omath

View File

@@ -3,8 +3,8 @@
//
#pragma once
#include <algorithm>
#include "omath/linear_algebra/vector3.hpp"
#include <algorithm>
namespace omath
{
@@ -13,6 +13,7 @@ namespace omath
class Vector4 : public Vector3<Type>
{
public:
using ContainedType = Type;
Type w;
constexpr Vector4(const Type& x, const Type& y, const Type& z, const Type& w): Vector3<Type>(x, y, z), w(w)
@@ -182,6 +183,12 @@ namespace omath
return length() >= other.length();
}
[[nodiscard]]
constexpr std::array<Type, 4> as_array() const noexcept
{
return {this->x, this->y, this->z, w};
}
#ifdef OMATH_IMGUI_INTEGRATION
[[nodiscard]]
constexpr ImVec4 to_im_vec4() const noexcept

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,8 +5,10 @@
#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 <cmath>
#include <expected>
#include <omath/trigonometry/angle.hpp>
#include <type_traits>
@@ -50,7 +52,7 @@ namespace omath::projection
requires noexcept(T::calc_projection_matrix(fov, viewport, znear, zfar));
};
template<class Mat4X4Type, class ViewAnglesType, class TraitClass>
template<class Mat4X4Type, class ViewAnglesType, class TraitClass, bool inverted_z = false>
requires CameraEngineConcept<TraitClass, Mat4X4Type, ViewAnglesType>
class Camera final
{
@@ -78,59 +80,96 @@ namespace omath::projection
{
m_view_angles = TraitClass::calc_look_at_angle(m_origin, target);
m_view_projection_matrix = std::nullopt;
m_view_matrix = std::nullopt;
}
protected:
[[nodiscard]] Mat4X4Type calc_view_projection_matrix() const noexcept
[[nodiscard]]
Vector3<float> get_forward() const noexcept
{
return TraitClass::calc_projection_matrix(m_field_of_view, m_view_port, m_near_plane_distance,
m_far_plane_distance)
* TraitClass::calc_view_matrix(m_view_angles, m_origin);
const auto& view_matrix = get_view_matrix();
if constexpr (inverted_z)
return -Vector3<float>{view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]};
return {view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]};
}
[[nodiscard]]
Vector3<float> get_right() const noexcept
{
const auto& view_matrix = get_view_matrix();
return {view_matrix[0, 0], view_matrix[0, 1], view_matrix[0, 2]};
}
[[nodiscard]]
Vector3<float> get_up() const noexcept
{
const auto& view_matrix = get_view_matrix();
return {view_matrix[1, 0], view_matrix[1, 1], view_matrix[1, 2]};
}
public:
[[nodiscard]] const Mat4X4Type& get_view_projection_matrix() const noexcept
{
if (!m_view_projection_matrix.has_value())
m_view_projection_matrix = calc_view_projection_matrix();
m_view_projection_matrix = get_projection_matrix() * get_view_matrix();
return m_view_projection_matrix.value();
}
[[nodiscard]] const Mat4X4Type& get_view_matrix() const noexcept
{
if (!m_view_matrix.has_value())
m_view_matrix = TraitClass::calc_view_matrix(m_view_angles, m_origin);
return m_view_matrix.value();
}
[[nodiscard]] const Mat4X4Type& get_projection_matrix() const noexcept
{
if (!m_projection_matrix.has_value())
m_projection_matrix = TraitClass::calc_projection_matrix(m_field_of_view, m_view_port,
m_near_plane_distance, m_far_plane_distance);
return m_projection_matrix.value();
}
void set_field_of_view(const FieldOfView& fov) noexcept
{
m_field_of_view = fov;
m_view_projection_matrix = std::nullopt;
m_projection_matrix = std::nullopt;
}
void set_near_plane(const float near) noexcept
{
m_near_plane_distance = near;
m_view_projection_matrix = std::nullopt;
m_projection_matrix = std::nullopt;
}
void set_far_plane(const float far) noexcept
{
m_far_plane_distance = far;
m_view_projection_matrix = std::nullopt;
m_projection_matrix = std::nullopt;
}
void set_view_angles(const ViewAnglesType& view_angles) noexcept
{
m_view_angles = view_angles;
m_view_projection_matrix = std::nullopt;
m_view_matrix = std::nullopt;
}
void set_origin(const Vector3<float>& origin) noexcept
{
m_origin = origin;
m_view_projection_matrix = std::nullopt;
m_view_matrix = std::nullopt;
}
void set_view_port(const ViewPort& view_port) noexcept
{
m_view_port = view_port;
m_view_projection_matrix = std::nullopt;
m_projection_matrix = std::nullopt;
}
[[nodiscard]] const FieldOfView& get_field_of_view() const noexcept
@@ -175,16 +214,64 @@ 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
{
auto projected = get_view_projection_matrix()
* mat_column_from_vector<float, Mat4X4Type::get_store_ordering()>(world_position);
if (projected.at(3, 0) == 0.0f)
const auto& w = projected.at(3, 0);
if (w <= std::numeric_limits<float>::epsilon())
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS);
projected /= projected.at(3, 0);
projected /= w;
if (is_ndc_out_of_bounds(projected))
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS);
@@ -202,10 +289,12 @@ namespace omath::projection
auto inverted_projection =
inv_view_proj.value() * mat_column_from_vector<float, Mat4X4Type::get_store_ordering()>(ndc);
if (!inverted_projection.at(3, 0))
const auto& w = inverted_projection.at(3, 0);
if (std::abs(w) < std::numeric_limits<float>::epsilon())
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS);
inverted_projection /= inverted_projection.at(3, 0);
inverted_projection /= w;
return Vector3<float>{inverted_projection.at(0, 0), inverted_projection.at(1, 0),
inverted_projection.at(2, 0)};
@@ -231,7 +320,8 @@ namespace omath::projection
Angle<float, 0.f, 180.f, AngleFlags::Clamped> m_field_of_view;
mutable std::optional<Mat4X4Type> m_view_projection_matrix;
mutable std::optional<Mat4X4Type> m_projection_matrix;
mutable std::optional<Mat4X4Type> m_view_matrix;
float m_far_plane_distance;
float m_near_plane_distance;
@@ -242,7 +332,9 @@ namespace omath::projection
template<class Type>
[[nodiscard]] constexpr static bool is_ndc_out_of_bounds(const Type& ndc) noexcept
{
return std::ranges::any_of(ndc.raw_array(), [](const auto& val) { return val < -1 || val > 1; });
constexpr auto eps = std::numeric_limits<float>::epsilon();
return std::ranges::any_of(ndc.raw_array(),
[](const auto& val) { return val < -1.0f - eps || val > 1.0f + eps; });
}
// NDC REPRESENTATION:
@@ -299,7 +391,7 @@ namespace omath::projection
if constexpr (screen_start == ScreenStart::TOP_LEFT_CORNER)
return {screen_pos.x / m_view_port.m_width * 2.f - 1.f, 1.f - screen_pos.y / m_view_port.m_height * 2.f,
screen_pos.z};
else if (screen_start == ScreenStart::BOTTOM_LEFT_CORNER)
else if constexpr (screen_start == ScreenStart::BOTTOM_LEFT_CORNER)
return {screen_pos.x / m_view_port.m_width * 2.f - 1.f,
(screen_pos.y / m_view_port.m_height - 0.5f) * 2.f, screen_pos.z};
else

View File

@@ -64,9 +64,8 @@ namespace omath
case 5:
r = value, g = p, b = q;
break;
default:
return {0.f, 0.f, 0.f, 0.f};
std::unreachable();
}
return {r, g, b, 1.f};
@@ -190,7 +189,7 @@ template<>
struct std::formatter<omath::Color> // NOLINT(*-dcl58-cpp)
{
[[nodiscard]]
static constexpr auto parse(std::format_parse_context& ctx)
static constexpr auto parse(const std::format_parse_context& ctx)
{
return ctx.begin();
}
@@ -207,6 +206,6 @@ struct std::formatter<omath::Color> // NOLINT(*-dcl58-cpp)
if constexpr (std::is_same_v<typename FormatContext::char_type, char8_t>)
return std::format_to(ctx.out(), u8"{}", col.to_u8string());
return std::unreachable();
std::unreachable();
}
};

View File

@@ -0,0 +1,25 @@
//
// Created by Vladislav on 30.12.2025.
//
#pragma once
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string_view>
#include "section_scan_result.hpp"
namespace omath
{
class ElfPatternScanner final
{
public:
[[nodiscard]]
static std::optional<std::uintptr_t>
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
const std::string_view& target_section_name = ".text");
[[nodiscard]]
static std::optional<SectionScanResult>
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
const std::string_view& target_section_name = ".text");
};
} // namespace omath

View File

@@ -0,0 +1,25 @@
//
// Created by Copilot on 04.02.2026.
//
#pragma once
#include <cstdint>
#include <filesystem>
#include <optional>
#include <string_view>
#include "section_scan_result.hpp"
namespace omath
{
class MachOPatternScanner final
{
public:
[[nodiscard]]
static std::optional<std::uintptr_t>
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
const std::string_view& target_section_name = "__text");
[[nodiscard]]
static std::optional<SectionScanResult>
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
const std::string_view& target_section_name = "__text");
};
} // namespace omath

View File

@@ -51,9 +51,13 @@ namespace omath
const auto whole_range_size = static_cast<std::ptrdiff_t>(std::distance(begin, end));
const std::ptrdiff_t scan_size = whole_range_size - static_cast<std::ptrdiff_t>(pattern.size());
const auto pattern_size = static_cast<std::ptrdiff_t>(parsed_pattern->size());
const std::ptrdiff_t scan_size = whole_range_size - pattern_size;
for (std::ptrdiff_t i = 0; i < scan_size; i++)
if (scan_size < 0)
return end;
for (std::ptrdiff_t i = 0; i <= scan_size; i++)
{
bool found = true;

View File

@@ -7,23 +7,20 @@
#include <filesystem>
#include <optional>
#include <string_view>
#include "section_scan_result.hpp"
namespace omath
{
struct PeSectionScanResult
{
std::uint64_t virtual_base_addr;
std::uint64_t raw_base_addr;
std::ptrdiff_t target_offset;
};
class PePatternScanner final
{
public:
[[nodiscard]]
static std::optional<std::uintptr_t> scan_for_pattern_in_loaded_module(const void* module_base_address,
const std::string_view& pattern);
static std::optional<std::uintptr_t>
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
const std::string_view& target_section_name = ".text");
[[nodiscard]]
static std::optional<PeSectionScanResult>
static std::optional<SectionScanResult>
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
const std::string_view& target_section_name = ".text");
};

View File

@@ -0,0 +1,16 @@
//
// Created by Vladislav on 01.01.2026.
//
#pragma once
#include <cstddef>
#include <cstdint>
namespace omath
{
struct SectionScanResult final
{
std::uintptr_t virtual_base_addr;
std::uintptr_t raw_base_addr;
std::ptrdiff_t target_offset;
};
}

View File

@@ -4,3 +4,4 @@ theme:
extra_css:
- styles/center.css
- styles/custom-header.css
- styles/links.css

69
pixi.toml Normal file
View File

@@ -0,0 +1,69 @@
[workspace]
name = "omath"
version = "5.7.0"
description = "Cross-platform modern general purpose math library written in C++23 that suitable for cheat/game development."
authors = [
"orange-cpp <orange_github@proton.me>"
]
license = "Zlib"
license-file = "LICENSE"
readme = "README.md"
documentation = "http://libomath.org"
repository = "https://github.com/orange-cpp/omath"
channels = ["conda-forge"]
platforms = ["win-64", "linux-64", "linux-aarch64", "osx-64", "osx-arm64"]
[tasks]
format = { cwd = "pixi", cmd = "cmake -P fmt.cmake" }
configure = { cmd = "cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DOMATH_USE_AVX2=OFF -DOMATH_IMGUI_INTEGRATION=ON -DOMATH_BUILD_TESTS=ON -DOMATH_BUILD_BENCHMARK=ON -DOMATH_BUILD_EXAMPLES=ON .", depends-on = ["format"] }
build = { cmd = "cmake --build build --config Debug -j", depends-on = ["configure"] }
examples = { cwd = "pixi", cmd = "cmake -DCMAKE_BUILD_TYPE=Debug -P run.examples.cmake", depends-on = ["build"] }
tests = { cwd = "pixi", cmd = "cmake -DCMAKE_BUILD_TYPE=Debug -P run.unit.tests.cmake", depends-on = ["build"] }
benchmark = { cwd = "pixi", cmd = "cmake -DCMAKE_BUILD_TYPE=Debug -P run.benchmark.cmake", depends-on = ["build"] }
[dependencies]
benchmark = ">=1.9.5,<2"
ccache = ">=4.12.2,<5"
cmake = ">=4.2.3,<5"
cmake-format = ">=0.6.13,<0.7"
cxx-compiler = ">=1.11.0,<2"
imgui = ">=1.92.3,<2"
gtest = ">=1.17.0,<2"
glew = ">=2.3.0,<3"
glfw = ">=3.4,<4"
ninja = ">=1.13.2,<2"
[target.linux-64.dependencies]
mesa-libgl-devel-cos7-x86_64 = ">=18.3.4,<19"
xorg-x11-server-xvfb-cos7-x86_64 = ">=1.20.4,<2"
[target.linux-64.activation.env]
__GLX_VENDOR_LIBRARY_NAME = "mesa"
EGL_PLATFORM = "x11"
GLFW_PLATFORM = "x11"
[target.linux-64.tasks]
examples = { cwd = "pixi", cmd = "xvfb-run -a -s '-screen 0 1024x768x24 +extension GLX +render' cmake -DCMAKE_BUILD_TYPE=Debug -P run.examples.cmake", depends-on = ["build"] }
[target.win-64.dependencies]
mesa-libgl-devel-cos7-x86_64 = ">=18.3.4,<19"
[target.osx-64.dependencies]
mesa-libgl-devel-cos7-x86_64 = ">=18.3.4,<19"
[target.osx-arm64.dependencies]
mesa-libgl-devel-cos7-aarch64 = ">=18.3.4,<19"
[target.linux-aarch64.dependencies]
mesa-libgl-devel-cos7-aarch64 = ">=18.3.4,<19"
xorg-x11-server-xvfb-cos7-aarch64 = ">=1.20.4,<2"
[target.linux-aarch64.activation.env]
__GLX_VENDOR_LIBRARY_NAME = "mesa"
EGL_PLATFORM = "x11"
GLFW_PLATFORM = "x11"
[target.linux-aarch64.tasks]
examples = { cwd = "pixi", cmd = "xvfb-run -a -s '-screen 0 1024x768x24 +extension GLX +render' cmake -DCMAKE_BUILD_TYPE=Debug -P run.examples.cmake", depends-on = ["build"] }

36
pixi/fmt.cmake Normal file
View File

@@ -0,0 +1,36 @@
# cmake/Format.cmake
# Find cmake-format executable
find_program(CMAKE_FORMAT_EXECUTABLE NAMES cmake-format)
if(NOT CMAKE_FORMAT_EXECUTABLE)
message(FATAL_ERROR "cmake-format not found. Please install it first.")
endif()
# Get the project root directory (assuming this script is in cmake/
# subdirectory)
get_filename_component(PROJECT_ROOT "../${CMAKE_CURRENT_LIST_DIR}" ABSOLUTE)
# Iterate over all files in the project root
file(GLOB_RECURSE ALL_FILES "${PROJECT_ROOT}/*")
foreach(FILE ${ALL_FILES})
# Basic ignores for common directories to avoid formatting external/build
# files Note: We check for substrings in the full path
if("${FILE}" MATCHES "/\\.git/"
OR "${FILE}" MATCHES "/build/"
OR "${FILE}" MATCHES "/cmake-build/"
OR "${FILE}" MATCHES "/\\.pixi/"
OR "${FILE}" MATCHES "/vcpkg_installed/")
continue()
endif()
get_filename_component(FILENAME "${FILE}" NAME)
# Check if file ends with .cmake or matches exactly to CMakeLists.txt
if("${FILENAME}" STREQUAL "CMakeLists.txt" OR "${FILENAME}" MATCHES "\\.cmake$")
message(STATUS "Formatting ${FILE}")
execute_process(COMMAND ${CMAKE_FORMAT_EXECUTABLE} --config-files
"${PROJECT_ROOT}/.cmake-format" -i "${FILE}")
endif()
endforeach()

Some files were not shown because too many files have changed in this diff Show More