Compare commits

..

355 Commits

Author SHA1 Message Date
orange d9815cd757 fix 2026-08-04 03:20:05 +03:00
orange ee00767147 fixed for kf3 2026-08-02 23:45:45 +03:00
orange 91090342bf Auto stash before checking out "origin/main" 2026-07-29 12:54:51 +03:00
orange 34a5bf0ce1 paralel build 2026-07-29 03:48:27 +03:00
orange f1fbd5ac3f fix 2026-07-29 03:05:26 +03:00
orange 4a79f260c5 added hashing/base64 functions 2026-07-29 02:58:25 +03:00
orange ac57e8da9e improvement 2026-07-20 01:00:50 +03:00
orange ecc9852cd8 improved angle class 2026-07-19 23:44:50 +03:00
orange 5591eb6f88 mingw fix 2026-07-19 17:13:59 +03:00
orange 4a6d7c458b patch 2026-07-19 16:55:17 +03:00
orange a791ac1a84 fix 2026-07-19 16:47:25 +03:00
orange 56b10d3d9c fix 2026-07-19 16:36:37 +03:00
orange c068e3e0d8 updated vcpkg base line 2026-07-19 16:16:06 +03:00
orange 078e00db0d added final 2026-07-19 14:49:56 +03:00
orange b3c8438bf1 improved matrix multiplication 2026-07-16 01:10:55 +03:00
orange 335096d0c8 fix 2026-07-15 20:38:04 +03:00
orange 9f4cc99790 5.5.1 2026-07-15 20:03:10 +03:00
orange f2ff761823 umped version 2026-07-15 19:50:16 +03:00
orange 4cd5e8f829 improved stuff 2026-07-15 19:42:44 +03:00
orange eac3adba2a Merge pull request #203 from BillyONeal/find-dependency
Add missing `find_dependency` calls.
2026-07-15 19:42:11 +03:00
Billy Robert O'Neal III 4e413d977a Add missing find_dependency calls.
This was detected in reviewing https://github.com/microsoft/vcpkg/pull/52901/ by GPT 5.6 Sol.

(Also the file VERSION in this repo still claims 5.4.0 rather than 5.5.0 but I have not tried to fix that in this PR as I'm not sure what you want your next version to be)
2026-07-14 16:29:45 -07:00
orange d4c86dacc7 added gradient dir 2026-07-12 22:51:36 +03:00
orange 945f80241c Merge pull request #202 from orange-cpp/feature/gradient
Feature/gradient
2026-07-12 18:09:44 +03:00
orange 238d6aef4e improve skeleton esp 2026-07-12 11:27:34 +03:00
orange feb26e01c6 improved bars 2026-07-12 11:17:50 +03:00
orange 2c6d7c2dfa bar fixes 2026-07-12 11:10:13 +03:00
orange fb1ffac240 improvement 2026-07-11 23:17:14 +03:00
orange cbe220664f added outside glow 2026-07-11 22:39:18 +03:00
orange 1618a41318 added blur 2026-07-11 22:20:26 +03:00
orange 26823609e7 added glow 2026-07-11 18:50:59 +03:00
orange 1c89b12d32 improved stuff 2026-07-11 18:17:59 +03:00
orange 176f5b2aa1 gradient text + box fill 2026-07-11 18:05:20 +03:00
orange 78d92a3c0f improvement 2026-07-08 19:11:56 +03:00
orange d2ef40e0bd replaced with thread 2026-07-08 19:07:31 +03:00
orange d67d956aca showing mouse 2026-07-08 18:41:54 +03:00
orange 5e3bb0e961 fix stuff 2026-07-08 18:34:38 +03:00
orange 11b429d975 improved dx11 hook 2026-07-08 15:40:56 +03:00
orange b4f4917aec added constexpr support for radar 2026-07-05 05:53:28 +03:00
orange 9299ea8960 Merge pull request #201 from orange-cpp/feature/added_world_to_radar
Feature/added world to radar
2026-07-02 11:49:50 +03:00
orange 1982185b71 code style fix 2026-07-02 10:50:28 +03:00
orange 0a3b10022f improvement 2026-07-02 04:29:19 +03:00
orange 935f69426c improvement 2026-07-02 04:12:30 +03:00
orange 9eb4dd1155 fix 2026-07-02 04:02:28 +03:00
orange fdcb7845cf patch 2026-07-01 14:16:20 +03:00
orange 3e3d2e5a23 fix 2026-07-01 14:08:58 +03:00
orange 58d3e1904c fix 2026-07-01 13:53:51 +03:00
orange d9a6f6ec91 added stuff 2026-07-01 13:39:43 +03:00
orange 468a1f4703 Merge pull request #200 from orange-cpp/feature/aabb_improvemnt
Feature/aabb improvemnt
2026-06-30 01:51:30 +03:00
orange db99a1ecfc improved stuff 2026-06-30 01:32:45 +03:00
orange 8b4614eb89 simplified stuff 2026-06-29 15:08:11 +03:00
orange 0a072b0d56 added comments 2026-06-29 15:04:33 +03:00
orange 2c60959e2b improved dx12 hook 2026-06-29 14:55:29 +03:00
orange d7ec571a7a patch 2026-06-29 10:14:03 +03:00
orange 235917008c removed stff 2026-06-29 09:54:34 +03:00
orange 71e256d033 added static method 2026-06-26 02:04:34 +03:00
orange 306096af14 fixed stuffed 2026-06-22 06:31:56 +03:00
orange 502cfb431c added final 2026-06-22 06:21:54 +03:00
orange 8ccc9459d4 added additional method 2026-06-22 06:07:56 +03:00
orange 2ee485297f added more nodiscard messages 2026-06-22 03:48:44 +03:00
orange 6a5771e57d removed second FixedString 2026-06-19 02:03:17 +03:00
orange 58c082d399 improved some stuff 2026-06-19 01:54:53 +03:00
orange 11e6f67e23 added more noexcept 2026-06-19 01:09:53 +03:00
orange d7f9ab6e85 added noexcept 2026-06-19 01:09:31 +03:00
orange ceecdde9dc added hash 2026-06-19 01:05:36 +03:00
va_alpatov a546c1a6d1 added template hash 2026-06-19 01:01:27 +03:00
orange 8a591ce769 added nodiscard message 2026-06-15 22:27:12 +03:00
orange b0561c193e fix 2026-06-15 22:22:19 +03:00
orange fee4e4b9e3 added even more missing nodiscard messages 2026-06-15 20:31:57 +03:00
orange 2779bae82f added nodiscard messages 2026-06-15 20:22:35 +03:00
orange e82ec9c8ac removed useless inline keywords 2026-06-15 10:39:23 +03:00
orange 63ea93dc1c Merge pull request #198 from orange-cpp/feature/gcem
Feature/gcem
2026-06-15 01:17:23 +03:00
orange 10f5d866bd removed redundant option 2026-06-15 00:57:03 +03:00
orange 92da8bf0a6 fix 2026-06-15 00:45:16 +03:00
orange 47dcee037e removed gcem as dep 2026-06-15 00:37:47 +03:00
orange 75c3f2409d added modules support 2026-06-14 23:57:58 +03:00
orange a2a09fa8a6 removed .cpp for engines since they are no longer used 2026-06-14 23:47:25 +03:00
orange 43823c8fdc fix 2026-06-14 23:26:24 +03:00
orange 098be538f7 fix 2026-06-14 23:17:48 +03:00
orange 91d9335f83 added constexpr engine traits 2026-06-14 22:46:24 +03:00
orange 136cd39496 fix 2026-06-14 22:30:26 +03:00
orange bb618f692f added wrapers 2026-06-14 22:16:39 +03:00
orange 792fb13851 patch 2026-06-12 01:12:13 +03:00
orange 2d38387da9 fix 2026-06-12 01:05:14 +03:00
orange 5f14510e5b made constexpr cryengine camera & fromulas 2026-06-12 00:52:22 +03:00
orange 4a9125549f patch 2026-06-12 00:09:14 +03:00
orange d9acf5eea8 fix 2026-06-12 00:04:12 +03:00
orange 589f440e70 added gcem to pipelines 2026-06-11 23:54:53 +03:00
orange 854b50f317 added mat tests and triangle tests 2026-06-11 23:49:03 +03:00
orange a741fc1485 now add constexpr gcem tests for triangle and mat classes 2026-06-11 23:41:07 +03:00
orange a2be99be50 added more gcem to angle vec2,3 2026-06-11 23:20:18 +03:00
orange 00e7c564fd added gcem 2026-06-11 16:18:52 +03:00
orange 5d9dbec5b8 reverted fix 2026-06-10 15:22:31 +03:00
orange a2c9084e5f incremented version + improved naming 2026-06-10 14:29:18 +03:00
orange ad2523dc3a Merge pull request #197 from orange-cpp/feature/compile_time_patten_parsing
Feature/compile time patten parsing
2026-06-10 05:41:03 +03:00
orange 3599a7d592 fix 2026-06-10 05:22:04 +03:00
orange 00287c7a58 improved file scanners 2026-06-10 04:29:52 +03:00
orange ce589b4f17 added compile time pattern parsing 2026-06-10 04:09:13 +03:00
orange 8f6341e840 fix for lua 2026-06-08 19:22:04 +03:00
va_alpatov b9da58e138 added enity overlay aspect 2026-06-08 03:43:15 +03:00
orange 87896117d4 Merge pull request #196 from orange-cpp/feature/outline
improved outline arguments
2026-06-07 19:39:20 +03:00
va_alpatov de8a540426 applied fix for lua 2026-06-07 19:22:41 +03:00
va_alpatov d1eaee6e6e improved outline arguments 2026-06-07 19:08:01 +03:00
orange b9522fe191 nope 2026-06-03 17:34:06 +03:00
orange 2e1ff91156 added more tests 2026-06-03 16:26:31 +03:00
orange 40b2d2b332 Merge pull request #195 from orange-cpp/feature/rage_engine
added rage engine support
2026-06-03 16:15:43 +03:00
orange 8810ec20a5 added rage into read me 2026-06-03 16:14:13 +03:00
orange b7210dec4c added rage engine support
fix

patch
2026-06-03 15:35:41 +03:00
orange 56ed7e2f6e added outlined option for box 2026-05-23 11:56:52 +03:00
orange 39d0d0683d improvement 2026-05-22 12:35:14 +03:00
orange a04bceaeb6 Merge pull request #194 from orange-cpp/feature/code-style-skill
added code style skill for codex/claude
2026-05-22 12:05:39 +03:00
orange f0fe5821ed added code style skill for codex/claude 2026-05-22 12:05:06 +03:00
orange d8c5ea16fe Merge pull request #193 from orange-cpp/feature/nodiscrad-messages
Feature/nodiscrad messages
2026-05-22 11:50:24 +03:00
orange 848202cbd8 added nodiscard messages 2026-05-22 09:00:32 +03:00
orange 37128d18e7 camera message 2026-05-22 08:50:21 +03:00
orange 8433ef05ca Merge pull request #192 from orange-cpp/feature/lua
added more lua stuff
2026-05-18 11:11:13 +03:00
orange d9f2428e0e fixed for mingw 2026-05-18 10:54:48 +03:00
orange 23d3b7c9f5 windows fix 2026-05-18 10:34:23 +03:00
orange 36a7865b29 fixed arm 2026-05-18 10:28:07 +03:00
orange 2130d02090 patch 2026-05-18 10:22:36 +03:00
orange f602ab6538 Merge pull request #191 from orange-cpp/feature/mat_improvement
added improvement
2026-05-17 10:11:20 +03:00
orange e4087165b9 added safety check 2026-05-17 09:38:19 +03:00
orange cebcfc411d added improvement 2026-05-17 09:08:36 +03:00
orange a4fac65b7c added more lua stuff 2026-05-16 10:33:06 +03:00
va_alpatov 7f88bf8b21 hotfix 2026-05-16 05:35:41 +03:00
orange 93e70667f0 Merge pull request #190 from orange-cpp/feature/more-obb-tests
Feature/more obb tests
2026-05-14 03:05:50 +03:00
orange bdef596f16 added more tests 2026-05-14 02:30:21 +03:00
orange 7a2ac25e8d fixed inconsistant types 2026-05-14 01:37:33 +03:00
orange b6f41ed653 improved naming 2026-05-13 07:51:17 +03:00
orange 5ebba4a630 added constexpr 2026-05-13 07:03:31 +03:00
orange c73afa95cc update 2026-05-13 05:16:10 +03:00
orange 3ca657a048 added codex instructions 2026-05-13 05:12:19 +03:00
orange 9d1de20128 removed gitmodules 2026-05-12 08:23:17 +03:00
orange 6413c5d59c added ray tracer check for obb in line tracer 2026-05-07 05:30:47 +03:00
orange 94f88056cb Merge pull request #189 from orange-cpp/feature/obb
added obb
2026-05-07 05:23:20 +03:00
orange fbc35391c4 added obb 2026-05-07 05:04:35 +03:00
orange 6b637f6267 Merge pull request #188 from orange-cpp/feature/opengl_hook
Feature/opengl hook
2026-05-06 23:41:00 +03:00
orange fa52c9e985 added opengl for linux 2026-05-06 22:00:23 +03:00
orange 6ced4acdb6 removed copying 2026-05-06 20:10:34 +03:00
orange d90164cab8 added opengl hook 2026-05-06 20:05:45 +03:00
orange 29255cbb0e added claud config + skills 2026-05-06 04:04:57 +03:00
orange 8ad936f9f1 added separated mutexes for each call back 2026-05-04 20:55:42 +03:00
orange 57c834ded4 code style fixes 2026-05-04 19:42:39 +03:00
orange e25b1b3fc8 updated version 2026-05-04 06:09:27 +03:00
orange f2794230c3 Merge pull request #187 from orange-cpp/feature/hooking
Feature/hooking
2026-05-04 04:38:22 +03:00
orange 0515236c6c fix 2026-05-04 04:21:29 +03:00
orange 0215b7e0b7 using static for windows 2026-05-04 04:02:19 +03:00
orange 77b0ed3c81 fixed code style 2026-05-04 00:47:20 +03:00
orange 51bf4461ff fixed dx12 overlay 2026-05-04 00:45:48 +03:00
orange 232b48c3dd fixed dx12 hook 2026-05-04 00:10:53 +03:00
orange 105df90d05 decomposed method 2026-05-03 22:16:16 +03:00
orange 3aba53c8f8 fix 2026-05-03 21:59:48 +03:00
orange 71171acf36 added hooking of dx9 2026-05-03 21:58:51 +03:00
orange 1789b1ef51 added dx11 hook 2026-05-03 21:54:03 +03:00
orange 064d0cebbc update 2026-05-03 21:38:31 +03:00
orange 06d2752059 added dx12 hooking 2026-05-03 21:35:08 +03:00
orange 7e55b1d00e code clean up 2026-04-30 02:15:02 +03:00
orange 580f39210e added typecasting for vectors 2026-04-26 02:14:14 +03:00
orange 3e6b0e7180 fixe IW engine 2026-04-26 01:55:16 +03:00
orange e1d6c38a8e Merge pull request #186 from orange-cpp/feauture/camera_numeric_template
Feauture/camera numeric template
2026-04-25 21:48:06 +03:00
orange 77a8770aee patch 2026-04-25 21:31:59 +03:00
orange 9234704010 fixed projectile prediction for double 2026-04-25 21:05:00 +03:00
orange 4c65781c6f improvement 2026-04-25 05:34:53 +03:00
orange 29b49685be fix 2026-04-25 05:29:59 +03:00
orange 92582079c5 added types impl 2026-04-25 05:29:21 +03:00
orange 13c7f7eb5a fixed lua 2026-04-25 05:16:18 +03:00
orange 65cb803cfb update 2026-04-25 05:09:07 +03:00
orange 607c034be7 fix 2026-04-25 04:51:10 +03:00
orange 0487e285ef updated for unreal 2026-04-25 04:42:52 +03:00
orange 180f2f2afa added template + concept 2026-04-24 18:51:06 +03:00
orange 35bb1bc3c0 Merge pull request #185 from orange-cpp/feature/unreal-engine-projection-fix
Feature/unreal engine projection fix
2026-04-23 22:46:10 +03:00
orange e62e8672b3 fixed tests 2026-04-23 22:32:44 +03:00
orange 11c053e28c fixed rotation ordering 2026-04-23 21:24:46 +03:00
orange 56ebc47553 remove axys invertion 2026-04-23 20:02:34 +03:00
orange b3ba9eaadf updated formulas 2026-04-23 19:48:55 +03:00
orange 42a8a5a763 also fixed for source 2026-04-23 19:23:13 +03:00
orange 2eccb4023f fix 2026-04-23 18:33:00 +03:00
orange 3eb9daf10b +90 up -90 down fix for camera view angles 2026-04-23 02:05:54 +03:00
orange 27cb511510 Merge pull request #184 from orange-cpp/feature/more-tests
Feature/more tests
2026-04-20 01:50:23 +03:00
orange 27b24b5fe7 added gource script 2026-04-20 01:36:08 +03:00
orange 4186ae8d76 added more tests 2026-04-20 01:17:11 +03:00
orange 8e6e3211c2 Merge pull request #183 from orange-cpp/featue/aabb-improvement
Featue/aabb improvement
2026-04-19 23:49:25 +03:00
orange 1c0619ff7b added new methods 2026-04-19 23:20:29 +03:00
orange dfd18e96fb added aabb improvemnt 2026-04-19 23:07:58 +03:00
orange 20930c629a added method to get camera matrix 2026-04-18 15:40:38 +03:00
orange 0845a2e863 clarified interfaces 2026-04-18 12:54:37 +03:00
orange f3f454b02e Merge pull request #182 from orange-cpp/feature/camera_upgrade
Feature/camera upgrade
2026-04-15 18:59:18 +03:00
orange 0419043720 Update include/omath/engines/frostbite_engine/camera.hpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-15 03:48:03 +03:00
orange 79f64d9679 fixed unreal bug, improved interface 2026-04-15 03:38:02 +03:00
orange dbe29926dc fixed unity bug 2026-04-15 03:25:53 +03:00
orange 9d30446c55 added ability to get view angles from view matrix 2026-04-15 03:08:06 +03:00
orange ba80aebfae Merge pull request #181 from orange-cpp/feature/walk-bot
Feature/walk bot
2026-04-14 15:23:49 +03:00
orange 9c1b6d0ba3 added tests improved API 2026-04-12 21:21:23 +03:00
orange ea07d17dbb improved walkbot 2026-04-12 12:05:40 +03:00
orange bb974da0e2 improvement 2026-04-12 11:16:39 +03:00
orange fde764c1fa added code 2026-04-12 11:12:17 +03:00
va_alpatov 28e86fc355 tests hotfix 2026-04-11 20:23:08 +03:00
va_alpatov 93e7a9457a fixed pathfinding bug 2026-04-11 20:06:39 +03:00
orange 8f65183882 fixed tests 2026-04-08 15:34:10 +03:00
orange 327db8d441 updated contributing 2026-04-03 20:59:34 +03:00
orange d8188de736 keeping 1 AABB type 2026-03-28 14:22:36 +03:00
orange 33cd3f64e4 Merge pull request #180 from orange-cpp/feature/aabb-linetrace
added aabb line trace
2026-03-25 03:37:35 +03:00
orange 67a07eed45 added aabb line trace 2026-03-25 03:14:22 +03:00
orange 0b52b2847b Merge pull request #179 from orange-cpp/feature/aabb_check
added AABB check
2026-03-24 10:45:00 +03:00
orange d38895e4d7 added AABB check 2026-03-24 10:20:50 +03:00
orange 04203d46ff patch 2026-03-24 06:44:10 +03:00
orange bcbb5c1a8d fixed index 2026-03-24 06:05:56 +03:00
orange ba46c86664 simplified method 2026-03-24 06:03:35 +03:00
orange 3b0470cc11 Merge pull request #178 from orange-cpp/feature/imrovements
Feature/imrovements
2026-03-24 05:55:47 +03:00
orange 8562c5d1f2 added more unreachable checks 2026-03-24 05:28:01 +03:00
orange 8daba25c29 added ureachable 2026-03-24 05:21:00 +03:00
orange 29b7ac6450 Merge pull request #177 from orange-cpp/feature/custom_ndc_z_range
Feature/custom ndc z range
2026-03-24 04:20:57 +03:00
orange 89df10b778 specifeid ndc for game engines 2026-03-24 00:08:06 +03:00
orange 8fb96b83db removed dead code 2026-03-23 23:52:41 +03:00
orange 4b6db0c402 updated z range 2026-03-23 23:36:19 +03:00
orange a9ff7868cf simplified code 2026-03-23 05:52:35 +03:00
orange be80a5d243 added as_vector3 to view angles 2026-03-23 05:23:53 +03:00
orange 881d3b9a2a added fields 2026-03-22 19:07:38 +03:00
orange f60e18b6ba replaced with table offset 2026-03-22 18:58:07 +03:00
orange 0769d3d079 replaced with auto 2026-03-22 17:30:25 +03:00
orange b6755e21f9 fix 2026-03-22 16:32:00 +03:00
orange 2287602fa2 Merge pull request #176 from orange-cpp/feature/vtable_index
added stuff
2026-03-22 16:21:39 +03:00
orange 663890706e test fix 2026-03-22 16:06:57 +03:00
orange ab103f626b swaped to std::uintptr_t 2026-03-22 16:05:09 +03:00
orange cc4e01b100 added stuff 2026-03-22 16:00:35 +03:00
orange 308f7ed481 forgot return 2026-03-21 16:43:18 +03:00
orange 8802ad9af1 fix 2026-03-21 16:41:03 +03:00
orange 2ac508d6e8 fixed tests 2026-03-21 16:28:48 +03:00
orange eb1ca6055b added additional error code 2026-03-21 16:15:48 +03:00
orange b528e41de3 fixed test names 2026-03-21 15:45:22 +03:00
orange 8615ab2b7c changed name, fixed bug 2026-03-21 15:22:02 +03:00
orange 5a4c042fec replaced enum 2026-03-21 14:53:04 +03:00
orange 8063c1697a improved interface 2026-03-21 14:41:07 +03:00
orange 7567501f00 Merge pull request #175 from orange-cpp/feature/w2s_no_clip
added clip option
2026-03-21 14:12:07 +03:00
orange 46d999f846 added clip option 2026-03-21 13:58:06 +03:00
orange b54601132b added doc build to release 2026-03-21 06:32:05 +03:00
orange 5c8ce2d163 Merge pull request #174 from orange-cpp/feature/docs-pipelines
added docs pipeline
2026-03-21 06:26:21 +03:00
orange 04a86739b4 added docs pipeline 2026-03-21 06:11:20 +03:00
orange 575b411863 updated install md 2026-03-21 06:05:29 +03:00
orange 5a91151bc0 fix 2026-03-19 20:27:25 +03:00
orange 66d4df0524 fix 2026-03-19 20:17:10 +03:00
orange 54e14760ca fix 2026-03-19 20:09:07 +03:00
orange ee61c47d7d Merge pull request #173 from orange-cpp/feature/targeting_algorithms
Feature/targeting algorithms
2026-03-19 19:52:22 +03:00
orange d737aee1c5 added by distance targeting 2026-03-19 19:29:01 +03:00
orange ef422f0a86 added overload 2026-03-19 19:23:39 +03:00
orange e99ca0bc2b update 2026-03-19 19:19:42 +03:00
orange 5f94e36965 fix for windows specific suff related to far near macroses 2026-03-19 15:32:05 +03:00
orange 29510cf9e7 Removed from credit by own request 2026-03-19 15:24:35 +03:00
orange 927508a76b Merge pull request #172 from orange-cpp/feaute/methods_calling_improvement
Feaute/methods calling improvement
2026-03-19 01:33:42 +03:00
orange f390b386d7 fix 2026-03-19 01:06:16 +03:00
orange 012d837e8b fix windows x32 bit 2026-03-19 00:57:54 +03:00
orange 6236c8fd68 added nodiscard 2026-03-18 21:24:35 +03:00
orange 06dc36089f added overload 2026-03-18 21:19:09 +03:00
orange 91136a61c4 improvement 2026-03-18 21:12:18 +03:00
orange 9cdffcbdb1 added tests 2026-03-18 20:12:46 +03:00
orange a3e93ac259 added nttp 2026-03-18 20:05:32 +03:00
orange 59f6d7a361 added call_method 2026-03-18 19:58:52 +03:00
orange dcf1ef1ea9 Merge pull request #171 from orange-cpp/feaute/projectile_pred_improvement
Feaute/projectile pred improvement
2026-03-17 21:58:59 +03:00
orange 89bd879187 added tolerance depending on arch 2026-03-17 21:15:39 +03:00
orange aa08c7cb65 improved projectile prediction 2026-03-17 20:43:26 +03:00
orange a5c0ca0cbd added stuff 2026-03-17 20:31:46 +03:00
orange 624683aed6 added unreachanble 2026-03-17 19:53:15 +03:00
orange f46672b2c6 Merge pull request #170 from orange-cpp/feature/projectile_aim_widget
add projectile
2026-03-17 19:51:12 +03:00
orange b8e61f49fa add projectile 2026-03-17 19:36:35 +03:00
orange 37ea091282 Merge pull request #169 from orange-cpp/feaute/hud_features
Feaute/hud features
2026-03-16 14:39:58 +03:00
orange 29a2743728 renamed args 2026-03-16 13:17:16 +03:00
orange 1117eb37f1 added icon 2026-03-16 13:13:41 +03:00
orange b6b0d4db13 added aim dot 2026-03-16 03:24:53 +03:00
orange 2e8a74aaaf imroved spacer 2026-03-16 03:06:14 +03:00
orange d8632dc74c added progress ring 2026-03-16 03:03:23 +03:00
orange fd531c930c added spacer 2026-03-16 02:21:24 +03:00
orange a91673216d added const 2026-03-16 02:10:05 +03:00
orange 6487554844 corrected code style 2026-03-16 01:54:45 +03:00
orange 1744172694 updated credits 2026-03-15 20:42:13 +03:00
orange 114b2a6e58 Update README to enhance library description and features 2026-03-15 20:21:08 +03:00
orange d90a85d8b6 Merge pull request #168 from orange-cpp/feature/hud_declarative
Feature/hud declarative
2026-03-15 20:02:32 +03:00
orange e0a7179812 fix 2026-03-15 19:43:55 +03:00
orange a99dd24d6b improvement 2026-03-15 19:39:02 +03:00
orange d62dec9a8f changed api 2026-03-15 19:10:15 +03:00
orange 1a176d8f09 fix 2026-03-15 18:48:22 +03:00
orange 8e6ed19abf added dashed bar 2026-03-15 18:39:40 +03:00
orange 311ab45722 Merge pull request #167 from orange-cpp/feaute/sig_scan_file_in_mem
added stuff
2026-03-15 17:37:42 +03:00
orange 130277c1ae refactored test 2026-03-15 17:20:28 +03:00
orange 4f1c42d6f6 tests fix 2026-03-15 17:04:21 +03:00
orange ccea4a0f0d added stuff 2026-03-15 16:54:47 +03:00
orange 3fb98397e4 Merge pull request #166 from orange-cpp/feature/hud_improvement
Feature/hud improvement
2026-03-15 14:01:33 +03:00
orange 56256c40fb cleaned code 2026-03-15 13:47:41 +03:00
orange 46c94ae541 decomposed Run 2026-03-15 13:44:25 +03:00
orange a45f095b9c added skeleton 2026-03-15 04:59:47 +03:00
orange e849d23c47 improved dashed box 2026-03-15 04:56:10 +03:00
orange adad66599a adde dash box 2026-03-15 04:49:01 +03:00
orange 69bdfc3307 improved example 2026-03-15 04:43:19 +03:00
orange 55304c5df1 fixed bug 2026-03-15 04:28:56 +03:00
orange 19d796cd4e improvement 2026-03-15 04:23:07 +03:00
orange d31ea6ed4d added more stuff 2026-03-15 04:17:30 +03:00
orange 977d772687 fix 2026-03-13 22:20:57 +03:00
orange 746f1b84a8 hot fix 2026-03-13 22:16:42 +03:00
orange af399a14ed Merge pull request #165 from orange-cpp/feature/hud
Feature/hud
2026-03-13 22:11:26 +03:00
orange 6fb420642b updated props 2026-03-13 21:58:14 +03:00
orange 6a2b4b90b4 fix 2026-03-13 21:49:56 +03:00
orange 371d8154ee fix 2026-03-13 21:40:30 +03:00
orange d6a2165f83 fix 2026-03-13 21:37:03 +03:00
orange bb1b5ad14a removed shit 2026-03-13 21:32:44 +03:00
orange f188257e0f added stuff 2026-03-13 21:28:16 +03:00
orange 87966c82b9 added realization 2026-03-13 21:09:12 +03:00
orange 9da19582b5 added files 2026-03-13 20:51:59 +03:00
orange 29f3e2565d Merge pull request #164 from orange-cpp/feaute/disk_optimization
avoid saving files on disk
2026-03-13 03:55:56 +03:00
orange e083b15e0b update 2026-03-13 03:42:12 +03:00
orange ed9da79d08 avoid saving files on disk 2026-03-13 03:33:57 +03:00
orange 2002bcca83 Merge pull request #163 from orange-cpp/feature/serailization
Feature/serailization
2026-03-11 14:47:23 +03:00
orange ffacba71e2 changed to string view 2026-03-11 14:31:45 +03:00
orange 6081a9c426 added throw test 2026-03-11 14:30:01 +03:00
orange 8bbd504356 added check 2026-03-11 14:23:12 +03:00
orange 1d54039f57 added events 2026-03-11 14:19:58 +03:00
orange 93fc93d4f6 added more tests 2026-03-11 14:16:26 +03:00
orange b8a578774c improved serialization 2026-03-11 14:12:52 +03:00
orange bfa6c77776 Merge pull request #162 from orange-cpp/feature/scanner_example
Auto stash before checking out "origin/main"
2026-03-10 21:39:20 +03:00
orange 1341ef9925 Auto stash before checking out "origin/main" 2026-03-10 20:06:00 +03:00
orange 3ccbb0b25b Merge pull request #161 from orange-cpp/feature/lua-bindings
Feature/lua bindings
2026-03-10 19:41:34 +03:00
orange 68993db48a updated read me 2026-03-10 19:22:39 +03:00
orange 337204b3bf fixed missing source 2026-03-10 19:09:29 +03:00
orange 1e601d2d8f android fix 2026-03-10 19:01:54 +03:00
orange 2b4a75d011 added lua status to build logs 2026-03-10 19:00:36 +03:00
orange 99a30e8fdf added pattern scan to lua 2026-03-10 18:55:55 +03:00
orange 0cdd1d021f added triangle to lua 2026-03-10 18:39:20 +03:00
orange a79ac9743a added badge, added triangle 2026-03-10 18:39:10 +03:00
orange f26afb703b added bigobj for msvc and mingw 2026-03-10 18:29:13 +03:00
orange f237ee5f6a removed useless headers 2026-03-10 18:15:54 +03:00
orange 9058ea9b39 refactored to class 2026-03-10 18:14:29 +03:00
orange f707ac1adb fixed forward decl 2026-03-10 18:04:31 +03:00
orange cbdabd3fc2 removed useless header 2026-03-10 17:57:11 +03:00
orange 30e3feb4f8 fix 2026-03-10 16:38:00 +03:00
orange 0726fdef32 webasm fix 2026-03-10 16:31:49 +03:00
orange 0ffe0c2bdc potential fix 2026-03-10 16:24:21 +03:00
orange e9600ad42b splited lua into multiple sources 2026-03-10 16:15:02 +03:00
orange 673835618c restructurized stuff 2026-03-10 15:57:22 +03:00
orange afb2a13dd6 added color 2026-03-08 23:08:23 +03:00
orange 943472cf64 migrated to sol2
decomposed method

added vector2, vector4

refactored tests

added opengl engine to lua

added other engines

added source tests

removed tons of lua files
2026-03-08 12:55:35 +03:00
orange 9752accb14 Merge pull request #160 from orange-cpp/feaure/gjk-epa-improvement
Feaure/gjk epa improvement
2026-03-03 18:25:37 +03:00
orange 7373e6d3df added std namspace to int64_t type 2026-03-03 10:00:46 +03:00
orange 68f4c8cc72 added nodiscard 2026-03-03 09:38:05 +03:00
orange 2dafc8a49d added additional method 2026-03-03 09:22:11 +03:00
orange 11fe49e801 added const 2026-03-03 08:51:13 +03:00
orange dee705a391 improvement 2026-03-03 08:43:30 +03:00
orange bfe147ef80 Merge remote-tracking branch 'orange-cpp/feaure/gjk-epa-improvement' into feaure/gjk-epa-improvement 2026-03-03 08:27:50 +03:00
orange 2c70288a8f added epa tests 2026-03-03 08:27:26 +03:00
orange 529322fe34 decomposed method 2026-03-03 08:14:12 +03:00
orange 414b2af289 added gjk tests 2026-03-02 19:58:31 +03:00
orange a79ad6948c optimized 2026-03-02 19:40:45 +03:00
orange ea2c7c3d7f added benchmark 2026-03-02 19:40:37 +03:00
orange 91c2e0d74b Merge pull request #159 from orange-cpp/feature/color_update
Feature/color update
2026-03-01 13:53:18 +03:00
orange 52e9b906ff added const 2026-03-01 13:32:13 +03:00
orange cc6d625c2d added more formaters 2026-03-01 13:30:32 +03:00
orange 5eaec70846 fixed tests 2026-03-01 13:22:15 +03:00
orange 2063c4d33a updated color 2026-03-01 13:15:09 +03:00
orange 60bf8ca30f moved file 2026-03-01 13:00:24 +03:00
254 changed files with 24255 additions and 2489 deletions
+92
View File
@@ -0,0 +1,92 @@
---
name: code-style
description: omath project C++ code style derived from .idea/codeStyles/Project.xml and .clang-format. Use when writing, editing, or reviewing C++ code in this repo so formatting and naming match the rest of the codebase.
---
# omath Code Style
Authoritative sources: `.clang-format` (formatting) and `.idea/codeStyles/Project.xml` (Rider/CLion naming + formatting). When in doubt, run clang-format — it is the enforced formatter (`clangFormatSettings.ENABLED = true`).
## Formatting
Base style: LLVM with Stroustrup-style braces.
- **Indent**: 4 spaces, no tabs. Tab width 4. Continuation indent 8.
- **Column limit**: 120.
- **Namespace indentation**: `All` — indent contents of every namespace.
- **Access modifier offset**: -4 (access specifiers sit at the class column; members indent one level deeper).
- **Pointer/reference alignment**: Left, with a space *before* `*` / `&` in declarations: `const Vector3& other`, `Type* ptr`.
- **Include blocks**: Merge. Sort using-declarations.
- **Keep blank lines**: max 2 in code and declarations. No blank line at the start of a block.
- **Align trailing comments**: false.
- **Break before binary operators**: non-assignment.
### Braces (Allman / next-line for everything)
Opening brace on its own line after:
class, struct, union, enum, namespace, function, control statement (`if`/`for`/`while`/`switch`), `case` label, lambda body, `catch`, `else`, `while` (of do-while), extern block.
Empty functions, records, and namespaces still split (`SplitEmptyFunction/Record/Namespace: true`).
### Short-form rules (all disabled)
Never collapse onto one line: blocks, functions, lambdas, `if` statements, loops.
### Templates
`template<class T>` goes on its own line, declaration follows on the next line:
```cpp
template<class Type>
requires std::is_arithmetic_v<Type>
class Vector3 : public Vector2<Type>
{
...
};
```
No space after `template` keyword. `requires` clause is not extra-indented.
### Spaces
- After control-statement keywords (`if (`, `for (`, `while (`).
- **Not** before `(` in function declarations/definitions/calls.
- After C-style cast: `(int) x`.
- Around range-based-for colon: `for (auto& x : xs)`.
- After commas in template args/params.
- Inside empty parens/braces/templates: no.
## Naming
| Kind | Style | Example |
|---|---|---|
| Namespaces | `snake_case` | `omath::pathfinding`, `omath::primitives` |
| Types (class, struct, enum, union, concept, type alias, typedef, template parameter) | `PascalCase` | `Vector3`, `NavigationMesh`, `Astar`, `ContainedType` |
| Functions (free + member) | `snake_case` | `find_path`, `distance_to_sqr`, `create_box` |
| Fields (class/struct/union members) | `snake_case` | `dir_forward`, `nav_mesh` |
| Variables (global, local, lambda) | `snake_case` | `length_value`, `side_size` |
| Parameters | `snake_case` | `dir_right`, `v_other` |
| Macros | `UPPER_SNAKE_CASE` | `OMATH_FOO_BAR` |
| Enumerators | `UPPER_SNAKE_CASE` | `IMPOSSIBLE_BETWEEN_ANGLE`, `WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS` |
Enum types themselves are PascalCase (`enum class Vector3Error`, `enum class Error`); their members are UPPER_SNAKE_CASE.
## Files
- Headers: `.hpp`, sources: `.cpp`. Both use `snake_case` filenames (e.g. `vector3.hpp`, `proj_pred_engine_avx2.hpp`).
- C headers: `.h`, sources: `.c` (no enforced filename style).
- CUDA: `.cu` / `.cuh`.
- C++ modules: `.ixx`, `.mxx`, `.cppm`, `.ccm`, `.cxxm`, `.c++m`.
- Header guard: `#pragma once` only — no `#ifndef` guards.
- File header comment is optional and follows the form `// Created by <name> on <date>`.
## Idioms used throughout the codebase
- Prefer `[[nodiscard]]`, `noexcept`, and `constexpr` on math / value-type methods.
- `namespace omath` is the root; sub-features live in nested namespaces (`omath::collision`, `omath::engines::source_engine`, etc.).
- Closing namespace brace gets a trailing comment: `} // namespace omath::primitives`.
- Use `std::expected<T, E>` with an `enum class …Error` for fallible operations (see `Vector3Error`, `projection::Error`).
## When editing
Match the surrounding style exactly. If a region disagrees with this guide, prefer the existing local style — don't reformat unrelated code (per the project's CLAUDE.md "Surgical Changes" rule). Run clang-format on touched files before committing.
@@ -0,0 +1,67 @@
---
name: karpathy-guidelines
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
license: MIT
---
# Karpathy Guidelines
Behavioral guidelines to reduce common LLM coding mistakes, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
+1
View File
@@ -12,6 +12,7 @@ AlignConsecutiveMacros: AcrossEmptyLinesAndComments
AlignTrailingComments: false AlignTrailingComments: false
AllowShortBlocksOnASingleLine: Never AllowShortBlocksOnASingleLine: Never
AllowShortFunctionsOnASingleLine: None AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false AllowShortLoopsOnASingleLine: false
BreakTemplateDeclarations: Leave BreakTemplateDeclarations: Leave
+92
View File
@@ -0,0 +1,92 @@
---
name: code-style
description: omath project C++ code style derived from .idea/codeStyles/Project.xml and .clang-format. Use when writing, editing, or reviewing C++ code in this repo so formatting and naming match the rest of the codebase.
---
# omath Code Style
Authoritative sources: `.clang-format` (formatting) and `.idea/codeStyles/Project.xml` (Rider/CLion naming + formatting). When in doubt, run clang-format — it is the enforced formatter (`clangFormatSettings.ENABLED = true`).
## Formatting
Base style: LLVM with Stroustrup-style braces.
- **Indent**: 4 spaces, no tabs. Tab width 4. Continuation indent 8.
- **Column limit**: 120.
- **Namespace indentation**: `All` — indent contents of every namespace.
- **Access modifier offset**: -4 (access specifiers sit at the class column; members indent one level deeper).
- **Pointer/reference alignment**: Left, with a space *before* `*` / `&` in declarations: `const Vector3& other`, `Type* ptr`.
- **Include blocks**: Merge. Sort using-declarations.
- **Keep blank lines**: max 2 in code and declarations. No blank line at the start of a block.
- **Align trailing comments**: false.
- **Break before binary operators**: non-assignment.
### Braces (Allman / next-line for everything)
Opening brace on its own line after:
class, struct, union, enum, namespace, function, control statement (`if`/`for`/`while`/`switch`), `case` label, lambda body, `catch`, `else`, `while` (of do-while), extern block.
Empty functions, records, and namespaces still split (`SplitEmptyFunction/Record/Namespace: true`).
### Short-form rules (all disabled)
Never collapse onto one line: blocks, functions, lambdas, `if` statements, loops.
### Templates
`template<class T>` goes on its own line, declaration follows on the next line:
```cpp
template<class Type>
requires std::is_arithmetic_v<Type>
class Vector3 : public Vector2<Type>
{
...
};
```
No space after `template` keyword. `requires` clause is not extra-indented.
### Spaces
- After control-statement keywords (`if (`, `for (`, `while (`).
- **Not** before `(` in function declarations/definitions/calls.
- After C-style cast: `(int) x`.
- Around range-based-for colon: `for (auto& x : xs)`.
- After commas in template args/params.
- Inside empty parens/braces/templates: no.
## Naming
| Kind | Style | Example |
|---|---|---|
| Namespaces | `snake_case` | `omath::pathfinding`, `omath::primitives` |
| Types (class, struct, enum, union, concept, type alias, typedef, template parameter) | `PascalCase` | `Vector3`, `NavigationMesh`, `Astar`, `ContainedType` |
| Functions (free + member) | `snake_case` | `find_path`, `distance_to_sqr`, `create_box` |
| Fields (class/struct/union members) | `snake_case` | `dir_forward`, `nav_mesh` |
| Variables (global, local, lambda) | `snake_case` | `length_value`, `side_size` |
| Parameters | `snake_case` | `dir_right`, `v_other` |
| Macros | `UPPER_SNAKE_CASE` | `OMATH_FOO_BAR` |
| Enumerators | `UPPER_SNAKE_CASE` | `IMPOSSIBLE_BETWEEN_ANGLE`, `WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS` |
Enum types themselves are PascalCase (`enum class Vector3Error`, `enum class Error`); their members are UPPER_SNAKE_CASE.
## Files
- Headers: `.hpp`, sources: `.cpp`. Both use `snake_case` filenames (e.g. `vector3.hpp`, `proj_pred_engine_avx2.hpp`).
- C headers: `.h`, sources: `.c` (no enforced filename style).
- CUDA: `.cu` / `.cuh`.
- C++ modules: `.ixx`, `.mxx`, `.cppm`, `.ccm`, `.cxxm`, `.c++m`.
- Header guard: `#pragma once` only — no `#ifndef` guards.
- File header comment is optional and follows the form `// Created by <name> on <date>`.
## Idioms used throughout the codebase
- Prefer `[[nodiscard]]`, `noexcept`, and `constexpr` on math / value-type methods.
- `namespace omath` is the root; sub-features live in nested namespaces (`omath::collision`, `omath::engines::source_engine`, etc.).
- Closing namespace brace gets a trailing comment: `} // namespace omath::primitives`.
- Use `std::expected<T, E>` with an `enum class …Error` for fallible operations (see `Vector3Error`, `projection::Error`).
## When editing
Match the surrounding style exactly. If a region disagrees with this guide, prefer the existing local style — don't reformat unrelated code (per the project's CLAUDE.md "Surgical Changes" rule). Run clang-format on touched files before committing.
@@ -0,0 +1,67 @@
---
name: karpathy-guidelines
description: Behavioral guidelines to reduce common LLM coding mistakes. Use when writing, reviewing, or refactoring code to avoid overcomplication, make surgical changes, surface assumptions, and define verifiable success criteria.
license: MIT
---
# Karpathy Guidelines
Behavioral guidelines to reduce common LLM coding mistakes, derived from [Andrej Karpathy's observations](https://x.com/karpathy/status/2015883857489522876) on LLM coding pitfalls.
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
+38 -68
View File
@@ -87,6 +87,9 @@ jobs:
shell: bash shell: bash
run: ${{ matrix.install_cmd }} run: ${{ matrix.install_cmd }}
- name: Set up CMake and Ninja
uses: lukka/get-cmake@v4.3.4
- name: Checkout repository (with sub-modules) - name: Checkout repository (with sub-modules)
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
@@ -107,7 +110,7 @@ jobs:
-DOMATH_BUILD_TESTS=ON \ -DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \ -DOMATH_BUILD_BENCHMARK=OFF \
-DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \ -DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests" -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests;lua"
- name: Build - name: Build
shell: bash shell: bash
@@ -193,7 +196,7 @@ jobs:
-DOMATH_BUILD_TESTS=ON \ -DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \ -DOMATH_BUILD_BENCHMARK=OFF \
-DOMATH_ENABLE_COVERAGE=OFF \ -DOMATH_ENABLE_COVERAGE=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests" -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests;lua"
- name: Build - name: Build
shell: bash shell: bash
@@ -234,7 +237,7 @@ jobs:
-DOMATH_ENABLE_COVERAGE=ON \ -DOMATH_ENABLE_COVERAGE=ON \
-DOMATH_THREAT_WARNING_AS_ERROR=OFF \ -DOMATH_THREAT_WARNING_AS_ERROR=OFF \
-DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_BUILD_TYPE=Debug \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests" -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests;lua"
cmake --build cmake-build/build/${{ matrix.preset }} --config Debug --target unit_tests omath cmake --build cmake-build/build/${{ matrix.preset }} --config Debug --target unit_tests omath
- name: Run Tests (Generates .profraw) - name: Run Tests (Generates .profraw)
@@ -245,72 +248,37 @@ jobs:
run: | run: |
./out/Debug/unit_tests.exe ./out/Debug/unit_tests.exe
- name: Process Coverage (llvm-profdata & llvm-cov) - name: Process and render coverage
if: ${{ matrix.triplet == 'x64-windows' }} if: ${{ matrix.triplet == 'x64-windows' }}
shell: pwsh shell: pwsh
run: | run: |
$BUILD_DIR = "cmake-build/build/${{ matrix.preset }}" $buildDir = "cmake-build/build/${{ matrix.preset }}"
$EXE_PATH = "./out/Debug/unit_tests.exe" $exePath = "./out/Debug/unit_tests.exe"
$profile = "$buildDir/unit_tests.profdata"
$htmlDir = "$buildDir/coverage-html"
# 1. Merge raw profile data (essential step)
& "C:/Program Files/LLVM/bin/llvm-profdata.exe" merge ` & "C:/Program Files/LLVM/bin/llvm-profdata.exe" merge `
-sparse "$BUILD_DIR/unit_tests.profraw" ` -sparse "$buildDir/unit_tests.profraw" `
-o "$BUILD_DIR/unit_tests.profdata" -o $profile
# 2. Export to LCOV format if ($LASTEXITCODE -ne 0) {
# NOTE: We explicitly ignore vcpkg_installed and system headers exit $LASTEXITCODE
& "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) & "C:/Program Files/LLVM/bin/llvm-cov.exe" show $exePath `
if: ${{ matrix.triplet == 'x64-windows' }} "-instr-profile=$profile" `
run: choco install lcov -y "-format=html" `
"-output-dir=$htmlDir" `
"-ignore-filename-regex=vcpkg_installed|external|tests" `
"-show-branches=count"
- name: Generate HTML Report if ($LASTEXITCODE -ne 0) {
if: ${{ matrix.triplet == 'x64-windows' }} exit $LASTEXITCODE
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) if (-not (Test-Path "$htmlDir/index.html")) {
sed -i 's|\\|/|g' "${LCOV_INFO}" throw "LLVM coverage report was not generated"
}
# 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) - name: Upload Coverage (HTML Report)
if: ${{ matrix.triplet == 'x64-windows' }} if: ${{ matrix.triplet == 'x64-windows' }}
@@ -370,16 +338,19 @@ jobs:
shell: bash shell: bash
run: | run: |
cmake --preset ${{ matrix.preset }} \ cmake --preset ${{ matrix.preset }} \
-DCMAKE_C_COMPILER=$(xcrun --find clang) \
-DCMAKE_CXX_COMPILER=$(xcrun --find clang++) \
-DOMATH_BUILD_TESTS=ON \ -DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \ -DOMATH_BUILD_BENCHMARK=OFF \
-DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \ -DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests" -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests;lua"
- name: Build - name: Build
shell: bash shell: bash
run: cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath run: cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
- name: Run unit_tests - name: Run unit_tests
if: ${{ matrix.coverage != true }}
shell: bash shell: bash
run: ./out/Release/unit_tests run: ./out/Release/unit_tests
@@ -450,7 +421,7 @@ jobs:
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \ -DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=ON \ -DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \ -DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;tests" -DVCPKG_MANIFEST_FEATURES="imgui;tests;lua"
- name: Build - name: Build
shell: bash shell: bash
@@ -509,7 +480,7 @@ jobs:
cmake --preset ${{ matrix.preset }} \ cmake --preset ${{ matrix.preset }} \
-DOMATH_BUILD_TESTS=ON \ -DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \ -DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests" \ -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests;lua" \
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" -DVCPKG_INSTALL_OPTIONS="--allow-unsupported"
cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
./out/Release/unit_tests ./out/Release/unit_tests
@@ -581,7 +552,7 @@ jobs:
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \ -DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=ON \ -DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \ -DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;tests" -DVCPKG_MANIFEST_FEATURES="imgui;tests;lua"
- name: Build - name: Build
shell: bash shell: bash
@@ -650,7 +621,7 @@ jobs:
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \ -DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=ON \ -DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \ -DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;tests" -DVCPKG_MANIFEST_FEATURES="imgui;tests;lua"
- name: Build - name: Build
shell: bash shell: bash
@@ -735,11 +706,10 @@ jobs:
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \ -DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
-DOMATH_BUILD_TESTS=ON \ -DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=OFF \ -DOMATH_BUILD_BENCHMARK=OFF \
-DVCPKG_MANIFEST_FEATURES="imgui;tests" -DVCPKG_MANIFEST_FEATURES="imgui;tests;lua"
- name: Build - name: Build
run: | run: cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
- name: Run unit_tests.exe - name: Run unit_tests.exe
run: | run: |
@@ -800,7 +770,7 @@ jobs:
-DOMATH_BUILD_TESTS=ON \ -DOMATH_BUILD_TESTS=ON \
-DOMATH_BUILD_BENCHMARK=ON \ -DOMATH_BUILD_BENCHMARK=ON \
-DOMATH_ENABLE_VALGRIND=ON \ -DOMATH_ENABLE_VALGRIND=ON \
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests;benchmark" -DVCPKG_MANIFEST_FEATURES="imgui;avx2;lua;tests;benchmark"
- name: Build All Targets - name: Build All Targets
shell: bash shell: bash
+62
View File
@@ -0,0 +1,62 @@
name: Documentation
on:
push:
branches: [ main ]
paths:
- 'docs/**'
- 'mkdocs.yml'
- '.github/workflows/docs.yml'
pull_request:
branches: [ main ]
paths:
- 'docs/**'
- 'mkdocs.yml'
- '.github/workflows/docs.yml'
concurrency:
group: docs-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
name: Build Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install mkdocs and dependencies
run: pip install mkdocs mkdocs-bootswatch
- name: Build documentation
run: mkdocs build --strict
- name: Upload artifact
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v3
with:
path: site/
deploy:
name: Deploy to GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+32
View File
@@ -12,6 +12,35 @@ permissions:
contents: write contents: write
jobs: jobs:
##############################################################################
# 0) Documentation MkDocs
##############################################################################
docs-release:
name: Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install mkdocs and dependencies
run: pip install mkdocs mkdocs-bootswatch
- name: Build documentation
run: mkdocs build --strict
- name: Package
run: tar -czf omath-docs.tar.gz -C site .
- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ github.event.release.tag_name }}" omath-docs.tar.gz --clobber
############################################################################## ##############################################################################
# 1) Linux Clang / Ninja # 1) Linux Clang / Ninja
############################################################################## ##############################################################################
@@ -87,6 +116,9 @@ jobs:
shell: bash shell: bash
run: ${{ matrix.install_cmd }} run: ${{ matrix.install_cmd }}
- name: Set up CMake and Ninja
uses: lukka/get-cmake@v4.3.4
- name: Checkout repository (with sub-modules) - name: Checkout repository (with sub-modules)
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
View File
+6 -5
View File
@@ -17,7 +17,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooManyArgs/@EntryIndexedValue" value="WARNING" type="string" /> <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/=CppCStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCVQualifierCanNotBeAppliedToReference/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCVQualifierCanNotBeAppliedToReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassIsIncomplete/@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/=CppClassNeedsConstructorBecauseOfUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeverUsed/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
@@ -103,14 +103,14 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppImplicitDefaultConstructorNotAvailable/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppImplicitDefaultConstructorNotAvailable/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompatiblePointerConversion/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompatiblePointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompleteSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompleteSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInconsistentNaming/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInconsistentNaming/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIntegralToPointerConversion/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIntegralToPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInvalidLineContinuation/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInvalidLineContinuation/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppJoinDeclarationAndAssignment/@EntryIndexedValue" value="SUGGESTION" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppJoinDeclarationAndAssignment/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLambdaCaptureNeverUsed/@EntryIndexedValue" value="WARNING" type="string" /> <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/=CppLocalVariableMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableWithNonTrivialDtorIsNeverUsed/@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/=CppLongFloat/@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/=CppMemberFunctionMayBeConst/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeStatic/@EntryIndexedValue" value="SUGGESTION" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeStatic/@EntryIndexedValue" value="SUGGESTION" type="string" />
@@ -139,6 +139,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOverrideWithDifferentVisibility/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" />
@@ -202,7 +203,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
@@ -216,7 +217,7 @@
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" /> <option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />
+5
View File
@@ -0,0 +1,5 @@
{
"diagnostics.globals": [
"omath"
]
}
+65
View File
@@ -0,0 +1,65 @@
# AGENTS.md
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
---
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
+65
View File
@@ -0,0 +1,65 @@
# CLAUDE.md
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
## 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
## 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
## 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
## 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
---
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
+66 -5
View File
@@ -1,5 +1,4 @@
cmake_minimum_required(VERSION 3.26) cmake_minimum_required(VERSION 3.26)
file(READ VERSION OMATH_VERSION) file(READ VERSION OMATH_VERSION)
project(omath VERSION ${OMATH_VERSION} LANGUAGES CXX) project(omath VERSION ${OMATH_VERSION} LANGUAGES CXX)
@@ -31,6 +30,11 @@ option(OMATH_SUPRESS_SAFETY_CHECKS
option(OMATH_ENABLE_COVERAGE "Enable coverage" OFF) option(OMATH_ENABLE_COVERAGE "Enable coverage" OFF)
option(OMATH_ENABLE_FORCE_INLINE option(OMATH_ENABLE_FORCE_INLINE
"Will for compiler to make some functions to be force inlined no matter what" ON) "Will for compiler to make some functions to be force inlined no matter what" ON)
option(OMATH_ENABLE_LUA
"omath bindings for lua" OFF)
option(OMATH_ENABLE_HOOKING "omath will HooksManager that can hook DirectX/OpenGL automatically" OFF)
option(OMATH_ENABLE_MODULES "Build omath C++ module interface" OFF)
if(VCPKG_MANIFEST_FEATURES) if(VCPKG_MANIFEST_FEATURES)
foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES) foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES)
if(omath_feature STREQUAL "imgui") if(omath_feature STREQUAL "imgui")
@@ -43,6 +47,10 @@ if(VCPKG_MANIFEST_FEATURES)
set(OMATH_BUILD_BENCHMARK ON) set(OMATH_BUILD_BENCHMARK ON)
elseif(omath_feature STREQUAL "examples") elseif(omath_feature STREQUAL "examples")
set(OMATH_BUILD_EXAMPLES ON) set(OMATH_BUILD_EXAMPLES ON)
elseif(omath_feature STREQUAL "lua")
set(OMATH_ENABLE_LUA ON)
elseif(omath_feature STREQUAL "hooking")
set(OMATH_ENABLE_HOOKING ON)
endif() endif()
endforeach() endforeach()
@@ -72,6 +80,12 @@ if(${PROJECT_IS_TOP_LEVEL})
message(STATUS "[${PROJECT_NAME}]: Building using vcpkg ${OMATH_BUILD_VIA_VCPKG}") 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}]: Coverage feature status ${OMATH_ENABLE_COVERAGE}")
message(STATUS "[${PROJECT_NAME}]: Valgrind feature status ${OMATH_ENABLE_VALGRIND}") message(STATUS "[${PROJECT_NAME}]: Valgrind feature status ${OMATH_ENABLE_VALGRIND}")
message(STATUS "[${PROJECT_NAME}]: Lua feature status ${OMATH_ENABLE_LUA}")
message(STATUS "[${PROJECT_NAME}]: Modules feature status ${OMATH_ENABLE_MODULES}")
endif()
if(OMATH_STATIC_MSVC_RUNTIME_LIBRARY)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" CACHE STRING "" FORCE)
endif() endif()
file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
@@ -83,6 +97,46 @@ else()
add_library(${PROJECT_NAME} STATIC ${OMATH_SOURCES} ${OMATH_HEADERS}) add_library(${PROJECT_NAME} STATIC ${OMATH_SOURCES} ${OMATH_HEADERS})
endif() endif()
if(OMATH_ENABLE_MODULES)
if(CMAKE_VERSION VERSION_LESS 3.28)
message(FATAL_ERROR "OMATH_ENABLE_MODULES requires CMake 3.28 or newer")
endif()
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_SCAN_FOR_MODULES ON)
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${PROJECT_NAME} PRIVATE /wd5244)
endif()
target_sources(
${PROJECT_NAME}
PUBLIC FILE_SET omath_modules TYPE CXX_MODULES BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/modules"
FILES "${CMAKE_CURRENT_SOURCE_DIR}/modules/omath.cppm")
endif()
if (OMATH_ENABLE_LUA)
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_ENABLE_LUA)
find_package(Lua REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE ${LUA_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE ${LUA_LIBRARIES})
find_path(SOL2_INCLUDE_DIRS "sol/abort.hpp")
target_include_directories(${PROJECT_NAME} PRIVATE ${SOL2_INCLUDE_DIRS})
endif ()
if (OMATH_ENABLE_HOOKING)
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_ENABLE_HOOKING)
find_package(safetyhook CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE safetyhook::safetyhook)
if (WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE d3d9 d3d11 d3d12 dxgi opengl32 gdi32)
elseif (UNIX AND NOT APPLE)
find_package(OpenGL REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL ${CMAKE_DL_LIBS})
endif ()
endif ()
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_VERSION="${PROJECT_VERSION}") target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_VERSION="${PROJECT_VERSION}")
@@ -130,10 +184,6 @@ set_target_properties(
CXX_STANDARD 23 CXX_STANDARD 23
CXX_STANDARD_REQUIRED ON) CXX_STANDARD_REQUIRED ON)
if(OMATH_STATIC_MSVC_RUNTIME_LIBRARY)
set_target_properties(${PROJECT_NAME} PROPERTIES MSVC_RUNTIME_LIBRARY
"MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
if(OMATH_USE_AVX2) if(OMATH_USE_AVX2)
if(MSVC) if(MSVC)
@@ -174,6 +224,12 @@ elseif(OMATH_THREAT_WARNING_AS_ERROR)
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror) target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror)
endif() endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${PROJECT_NAME} PRIVATE /bigobj)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_HOST_SYSTEM_NAME EQUAL "Windows")
target_compile_options(${PROJECT_NAME} PRIVATE -mbig-obj)
endif()
# Windows SDK redefine min/max via preprocessor and break std::min and std::max # Windows SDK redefine min/max via preprocessor and break std::min and std::max
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_definitions(${PROJECT_NAME} INTERFACE NOMINMAX) target_compile_definitions(${PROJECT_NAME} INTERFACE NOMINMAX)
@@ -190,6 +246,10 @@ target_include_directories(
# Installation rules # Installation rules
if(OMATH_ENABLE_MODULES)
set(OMATH_MODULE_FILE_SET FILE_SET omath_modules DESTINATION modules)
endif()
# Install the library # Install the library
install( install(
TARGETS ${PROJECT_NAME} TARGETS ${PROJECT_NAME}
@@ -198,6 +258,7 @@ install(
LIBRARY DESTINATION lib COMPONENT ${PROJECT_NAME} # For shared libraries LIBRARY DESTINATION lib COMPONENT ${PROJECT_NAME} # For shared libraries
RUNTIME DESTINATION bin COMPONENT ${PROJECT_NAME} # For executables (on RUNTIME DESTINATION bin COMPONENT ${PROJECT_NAME} # For executables (on
# Windows) # Windows)
${OMATH_MODULE_FILE_SET}
) )
# Install headers as part of omath_component # Install headers as part of omath_component
+12 -8
View File
@@ -56,7 +56,9 @@
"hidden": true, "hidden": true,
"inherits": ["windows-base", "vcpkg-base"], "inherits": ["windows-base", "vcpkg-base"],
"cacheVariables": { "cacheVariables": {
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples" "VCPKG_TARGET_TRIPLET": "x64-windows-static",
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples;hooking",
"OMATH_STATIC_MSVC_RUNTIME_LIBRARY": "ON"
} }
}, },
{ {
@@ -89,9 +91,10 @@
"strategy": "external" "strategy": "external"
}, },
"cacheVariables": { "cacheVariables": {
"VCPKG_TARGET_TRIPLET": "x86-windows", "VCPKG_TARGET_TRIPLET": "x86-windows-static",
"VCPKG_HOST_TRIPLET": "x64-windows", "VCPKG_HOST_TRIPLET": "x64-windows",
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples" "VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples",
"OMATH_STATIC_MSVC_RUNTIME_LIBRARY": "ON"
} }
}, },
{ {
@@ -114,9 +117,10 @@
"strategy": "external" "strategy": "external"
}, },
"cacheVariables": { "cacheVariables": {
"VCPKG_TARGET_TRIPLET": "arm64-windows", "VCPKG_TARGET_TRIPLET": "arm64-windows-static",
"VCPKG_HOST_TRIPLET": "arm64-windows", "VCPKG_HOST_TRIPLET": "arm64-windows-static",
"VCPKG_MANIFEST_FEATURES": "tests;imgui;examples" "VCPKG_MANIFEST_FEATURES": "tests;imgui;examples",
"OMATH_STATIC_MSVC_RUNTIME_LIBRARY": "ON"
} }
}, },
{ {
@@ -145,7 +149,7 @@
"hidden": true, "hidden": true,
"inherits": ["linux-base", "vcpkg-base"], "inherits": ["linux-base", "vcpkg-base"],
"cacheVariables": { "cacheVariables": {
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2" "VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;lua"
} }
}, },
{ {
@@ -235,7 +239,7 @@
"hidden": true, "hidden": true,
"inherits": ["darwin-base", "vcpkg-base"], "inherits": ["darwin-base", "vcpkg-base"],
"cacheVariables": { "cacheVariables": {
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples" "VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples;lua"
} }
}, },
{ {
+25 -21
View File
@@ -1,32 +1,36 @@
## 🤝 Contributing to OMath or other Orange's Projects # Contributing
### ❕ Prerequisites ## Prerequisites
- A working up-to-date OMath installation - C++ compiler with C++23 support (Clang 18+, GCC 14+, MSVC 19.38+)
- C++ knowledge - CMake 3.25+
- Git knowledge - Git
- Ability to ask for help (Feel free to create empty pull-request or PM a maintainer - Familiarity with the codebase (see `INSTALL.md` for setup)
in [Telegram](https://t.me/orange_cpp))
### ⏬ Setting up OMath For questions, create a draft PR or reach out via [Telegram](https://t.me/orange_cpp).
Please read INSTALL.md file in repository ## Workflow
### 🔀 Pull requests and Branches 1. [Fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) the repository.
2. Create a feature branch from `main`.
3. Make your changes, ensuring tests pass.
4. Open a [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) against `main`.
In order to send code back to the official OMath repository, you must first create a copy of OMath on your github ## Code Style
account ([fork](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)) and
then [create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) back to OMath.
OMath development is performed on multiple branches. Changes are then pull requested into master. By default, changes Follow the project `.clang-format`. Run `clang-format` before committing.
merged into master will not roll out to stable build users unless the `stable` tag is updated.
### 📜 Code-Style ## Building
The orange code-style can be found in `.clang-format`. Use one of the CMake presets defined in `CMakePresets.json`:
### 📦 Building ```bash
cmake --preset <preset-name> -DOMATH_BUILD_TESTS=ON
cmake --build --preset <preset-name>
```
OMath has already created the `cmake-build` and `out` directories where cmake/bin files are located. By default, you Run `cmake --list-presets` to see available configurations.
can build OMath by running `cmake --build cmake-build/build/windows-release --target omath -j 6` in the source
directory. ## Tests
All new functionality must include unit tests. Run the test binary after building to verify nothing is broken.
+2 -1
View File
@@ -3,8 +3,9 @@
Thanks to everyone who made this possible, including: Thanks to everyone who made this possible, including:
- Saikari aka luadebug for VCPKG port and awesome new initial logo design. - Saikari aka luadebug for VCPKG port and awesome new initial logo design.
- AmbushedRaccoon for telegram post about omath to boost repository activity.
- Billy O'Neal aka BillyONeal for fixing compilation issues due to C math library compatibility. - Billy O'Neal aka BillyONeal for fixing compilation issues due to C math library compatibility.
- Alex2772 for reference of AUI declarative interface design for omath::hud.
- Keith O'Hara aka kthohr for a C++ generalized constant expression-based math library that was used as reference.
And a big hand to everyone else who has contributed over the past! And a big hand to everyone else who has contributed over the past!
+23
View File
@@ -28,6 +28,29 @@ target("...")
add_packages("omath") add_packages("omath")
``` ```
## <img width="28px" src="https://conan.io/favicon.png" /> Using Conan
**Note**: Support Conan for package management
1. Install [Conan](https://conan.io/downloads)
2. Run the following command to install the omath package:
```
conan install --requires="omath/[*]" --build=missing
```
conanfile.txt
```ini
[requires]
omath/[*]
[generators]
CMakeDeps
CMakeToolchain
```
CMakeLists.txt
```cmake
find_package(omath CONFIG REQUIRED)
target_link_libraries(main PRIVATE omath::omath)
```
For more details, see the [Conan documentation](https://docs.conan.io/2/).
## <img width="28px" src="https://github.githubassets.com/favicons/favicon.svg" /> Using prebuilt binaries (GitHub Releases) ## <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. **Note**: This is the fastest option if you dont want to build from source.
+10 -2
View File
@@ -9,11 +9,12 @@
[![CodeFactor](https://www.codefactor.io/repository/github/orange-cpp/omath/badge)](https://www.codefactor.io/repository/github/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) ![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) [![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/orange-math.svg)](https://repology.org/project/orange-math/versions)
![Conan Center](https://img.shields.io/conan/v/omath)
![GitHub forks](https://img.shields.io/github/forks/orange-cpp/omath) ![GitHub forks](https://img.shields.io/github/forks/orange-cpp/omath)
[![discord badge](https://dcbadge.limes.pink/api/server/https://discord.gg/eDgdaWbqwZ?style=flat)](https://discord.gg/eDgdaWbqwZ) [![discord badge](https://dcbadge.limes.pink/api/server/https://discord.gg/eDgdaWbqwZ?style=flat)](https://discord.gg/eDgdaWbqwZ)
[![telegram badge](https://img.shields.io/badge/Telegram-2CA5E0?style=flat-squeare&logo=telegram&logoColor=white)](https://t.me/orangennotes) [![telegram badge](https://img.shields.io/badge/Telegram-2CA5E0?style=flat-squeare&logo=telegram&logoColor=white)](https://t.me/orangennotes)
OMath is a 100% independent, constexpr template blazingly fast math library that doesn't have legacy C++ code. OMath is a 100% independent, constexpr template blazingly fast math/physics/games/mods/cheats development framework that doesn't have legacy C++ code.
It provides the latest features, is highly customizable, has all for cheat development, DirectX/OpenGL/Vulkan support, premade support for different game engines, much more constexpr stuff than in other libraries and more... It provides the latest features, is highly customizable, has all for cheat development, DirectX/OpenGL/Vulkan support, premade support for different game engines, much more constexpr stuff than in other libraries and more...
<br> <br>
@@ -80,9 +81,11 @@ if (auto screen = camera.world_to_screen(world_position)) {
- **Collision Detection**: Production ready code to handle collision detection by using simple interfaces. - **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 - **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! - **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**. - **Engine support**: Supports coordinate systems of **Source, Rage, Unity, Unreal, Frostbite, IWEngine, CryEngine and canonical OpenGL**.
- **Cross platform**: Supports Windows, MacOS and Linux. - **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. - **Algorithms**: Has ability to scan for byte pattern with wildcards in ELF/Mach-O/PE files/modules, binary slices, works even with Wine apps.
- **Scripting**: Supports to make scripts in Lua out of box.
- **Handy**: Allow to design wall hacks in modern jetpack compose like way.
- **Battle tested**: It's already used by some big players on the market like wraith.su and bluedream.ltd - **Battle tested**: It's already used by some big players on the market like wraith.su and bluedream.ltd
<div align = center> <div align = center>
@@ -110,6 +113,10 @@ if (auto screen = camera.world_to_screen(world_position)) {
<br> <br>
![GTA5 Preview]
<br>
![OpenGL Preview] ![OpenGL Preview]
<br> <br>
@@ -141,6 +148,7 @@ if (auto screen = camera.world_to_screen(world_position)) {
[BO2 Preview]: docs/images/showcase/cod_bo2.png [BO2 Preview]: docs/images/showcase/cod_bo2.png
[CS2 Preview]: docs/images/showcase/cs2.jpeg [CS2 Preview]: docs/images/showcase/cs2.jpeg
[TF2 Preview]: docs/images/showcase/tf2.jpg [TF2 Preview]: docs/images/showcase/tf2.jpg
[GTA5 Preview]: https://i.imgur.com/W7T8RhZ.png
[OpenGL Preview]: docs/images/showcase/opengl.png [OpenGL Preview]: docs/images/showcase/opengl.png
<!----------------------------------{ Buttons }---------------------------------> <!----------------------------------{ Buttons }--------------------------------->
[QUICKSTART]: docs/getting_started.md [QUICKSTART]: docs/getting_started.md
+1 -1
View File
@@ -1 +1 @@
5.0.0 5.5.1
+161
View File
@@ -0,0 +1,161 @@
//
// Created by Vlad on 3/2/2026.
//
#include <benchmark/benchmark.h>
#include <memory_resource>
#include <omath/collision/epa_algorithm.hpp>
#include <omath/collision/gjk_algorithm.hpp>
#include <omath/engines/source_engine/collider.hpp>
#include <omath/engines/source_engine/mesh.hpp>
using Mesh = omath::source_engine::Mesh;
using Collider = omath::source_engine::MeshCollider;
using Gjk = omath::collision::GjkAlgorithm<Collider>;
using Epa = omath::collision::Epa<Collider>;
namespace
{
// Unit cube with half-extent 1 — 8 vertices in [-1,1]^3.
const std::vector<omath::primitives::Vertex<>> k_cube_vbo = {
{ { -1.f, -1.f, -1.f }, {}, {} },
{ { -1.f, -1.f, 1.f }, {}, {} },
{ { -1.f, 1.f, -1.f }, {}, {} },
{ { -1.f, 1.f, 1.f }, {}, {} },
{ { 1.f, 1.f, 1.f }, {}, {} },
{ { 1.f, 1.f, -1.f }, {}, {} },
{ { 1.f, -1.f, 1.f }, {}, {} },
{ { 1.f, -1.f, -1.f }, {}, {} },
};
const std::vector<omath::Vector3<std::uint32_t>> k_empty_vao{};
} // namespace
// ---------------------------------------------------------------------------
// GJK benchmarks
// ---------------------------------------------------------------------------
// Separated cubes — origin distance 2.1, no overlap.
// Exercises the early-exit path and the centroid-based initial direction.
static void BM_Gjk_Separated(benchmark::State& state)
{
const Collider a{Mesh{k_cube_vbo, k_empty_vao}};
Mesh mesh_b{k_cube_vbo, k_empty_vao};
mesh_b.set_origin({0.f, 2.1f, 0.f});
const Collider b{mesh_b};
for ([[maybe_unused]] auto _ : state)
benchmark::DoNotOptimize(Gjk::is_collide(a, b));
}
// Overlapping cubes — B offset by 0.5 along X, ~1.5 units penetration depth.
static void BM_Gjk_Overlapping(benchmark::State& state)
{
const Collider a{Mesh{k_cube_vbo, k_empty_vao}};
Mesh mesh_b{k_cube_vbo, k_empty_vao};
mesh_b.set_origin({0.5f, 0.f, 0.f});
const Collider b{mesh_b};
for ([[maybe_unused]] auto _ : state)
benchmark::DoNotOptimize(Gjk::is_collide(a, b));
}
// Identical cubes at the same origin — deep overlap / worst case for GJK.
static void BM_Gjk_SameOrigin(benchmark::State& state)
{
const Collider a{Mesh{k_cube_vbo, k_empty_vao}};
const Collider b{Mesh{k_cube_vbo, k_empty_vao}};
for ([[maybe_unused]] auto _ : state)
benchmark::DoNotOptimize(Gjk::is_collide(a, b));
}
// ---------------------------------------------------------------------------
// EPA benchmarks
// ---------------------------------------------------------------------------
// EPA with a pre-allocated monotonic buffer (reset each iteration).
// Isolates algorithmic cost from allocator overhead.
static void BM_Epa_MonotonicBuffer(benchmark::State& state)
{
const Collider a{Mesh{k_cube_vbo, k_empty_vao}};
Mesh mesh_b{k_cube_vbo, k_empty_vao};
mesh_b.set_origin({0.5f, 0.f, 0.f});
const Collider b{mesh_b};
const auto [hit, simplex] = Gjk::is_collide_with_simplex_info(a, b);
if (!hit)
return; // shouldn't happen, but guard for safety
constexpr Epa::Params params{.max_iterations = 64, .tolerance = 1e-4f};
// Pre-allocate a 32 KiB stack buffer — enough for typical polytope growth.
constexpr std::size_t k_buf_size = 32768;
alignas(std::max_align_t) char buf[k_buf_size];
std::pmr::monotonic_buffer_resource mr{buf, k_buf_size, std::pmr::null_memory_resource()};
for ([[maybe_unused]] auto _ : state)
{
mr.release(); // reset the buffer without touching the upstream resource
benchmark::DoNotOptimize(Epa::solve(a, b, simplex, params, mr));
}
}
// EPA with the default (malloc-backed) memory resource.
// Shows total cost including allocator pressure.
static void BM_Epa_DefaultResource(benchmark::State& state)
{
const Collider a{Mesh{k_cube_vbo, k_empty_vao}};
Mesh mesh_b{k_cube_vbo, k_empty_vao};
mesh_b.set_origin({0.5f, 0.f, 0.f});
const Collider b{mesh_b};
const auto [hit, simplex] = Gjk::is_collide_with_simplex_info(a, b);
if (!hit)
return;
constexpr Epa::Params params{.max_iterations = 64, .tolerance = 1e-4f};
for ([[maybe_unused]] auto _ : state)
benchmark::DoNotOptimize(Epa::solve(a, b, simplex, params));
}
// ---------------------------------------------------------------------------
// Combined GJK + EPA pipeline
// ---------------------------------------------------------------------------
// Full collision pipeline: GJK detects contact, EPA resolves penetration.
// This is the hot path in a physics engine tick.
static void BM_GjkEpa_Pipeline(benchmark::State& state)
{
const Collider a{Mesh{k_cube_vbo, k_empty_vao}};
Mesh mesh_b{k_cube_vbo, k_empty_vao};
mesh_b.set_origin({0.5f, 0.f, 0.f});
const Collider b{mesh_b};
constexpr Epa::Params params{.max_iterations = 64, .tolerance = 1e-4f};
constexpr std::size_t k_buf_size = 32768;
alignas(std::max_align_t) char buf[k_buf_size];
std::pmr::monotonic_buffer_resource mr{buf, k_buf_size, std::pmr::null_memory_resource()};
for ([[maybe_unused]] auto _ : state)
{
mr.release();
const auto [hit, simplex] = Gjk::is_collide_with_simplex_info(a, b);
if (hit)
benchmark::DoNotOptimize(Epa::solve(a, b, simplex, params, mr));
}
}
BENCHMARK(BM_Gjk_Separated)->Iterations(100'000);
BENCHMARK(BM_Gjk_Overlapping)->Iterations(100'000);
BENCHMARK(BM_Gjk_SameOrigin)->Iterations(100'000);
BENCHMARK(BM_Epa_MonotonicBuffer)->Iterations(100'000);
BENCHMARK(BM_Epa_DefaultResource)->Iterations(100'000);
BENCHMARK(BM_GjkEpa_Pipeline)->Iterations(100'000);
+20 -10
View File
@@ -2,11 +2,9 @@
// Created by Vlad on 9/17/2025. // Created by Vlad on 9/17/2025.
// //
#include <benchmark/benchmark.h> #include <benchmark/benchmark.h>
#include <omath/omath.hpp> #include <omath/omath.hpp>
using namespace omath; using namespace omath;
void mat_float_multiplication_col_major(benchmark::State& state) void mat_float_multiplication_col_major(benchmark::State& state)
{ {
using MatType = Mat<128, 128, float, MatStoreType::COLUMN_MAJOR>; using MatType = Mat<128, 128, float, MatStoreType::COLUMN_MAJOR>;
@@ -15,9 +13,12 @@ void mat_float_multiplication_col_major(benchmark::State& state)
a.set(3.f); a.set(3.f);
b.set(7.f); b.set(7.f);
for ([[maybe_unused]] const auto _ : state) for ([[maybe_unused]] const auto _ : state)
std::ignore = a * b; {
benchmark::DoNotOptimize(a);
benchmark::DoNotOptimize(b);
benchmark::DoNotOptimize(a * b);
}
} }
void mat_float_multiplication_row_major(benchmark::State& state) void mat_float_multiplication_row_major(benchmark::State& state)
{ {
@@ -27,9 +28,12 @@ void mat_float_multiplication_row_major(benchmark::State& state)
a.set(3.f); a.set(3.f);
b.set(7.f); b.set(7.f);
for ([[maybe_unused]] const auto _ : state) for ([[maybe_unused]] const auto _ : state)
std::ignore = a * b; {
benchmark::DoNotOptimize(a);
benchmark::DoNotOptimize(b);
benchmark::DoNotOptimize(a * b);
}
} }
void mat_double_multiplication_row_major(benchmark::State& state) void mat_double_multiplication_row_major(benchmark::State& state)
@@ -40,9 +44,12 @@ void mat_double_multiplication_row_major(benchmark::State& state)
a.set(3.f); a.set(3.f);
b.set(7.f); b.set(7.f);
for ([[maybe_unused]] const auto _ : state) for ([[maybe_unused]] const auto _ : state)
std::ignore = a * b; {
benchmark::DoNotOptimize(a);
benchmark::DoNotOptimize(b);
benchmark::DoNotOptimize(a * b);
}
} }
void mat_double_multiplication_col_major(benchmark::State& state) void mat_double_multiplication_col_major(benchmark::State& state)
@@ -53,9 +60,12 @@ void mat_double_multiplication_col_major(benchmark::State& state)
a.set(3.f); a.set(3.f);
b.set(7.f); b.set(7.f);
for ([[maybe_unused]] const auto _ : state) for ([[maybe_unused]] const auto _ : state)
std::ignore = a * b; {
benchmark::DoNotOptimize(a);
benchmark::DoNotOptimize(b);
benchmark::DoNotOptimize(a * b);
}
} }
BENCHMARK(mat_float_multiplication_col_major)->Iterations(5000); BENCHMARK(mat_float_multiplication_col_major)->Iterations(5000);
+3 -3
View File
@@ -12,11 +12,11 @@ constexpr float hit_distance_tolerance = 5.f;
void source_engine_projectile_prediction(benchmark::State& state) void source_engine_projectile_prediction(benchmark::State& state)
{ {
constexpr Target target{.m_origin = {100, 0, 90}, .m_velocity = {0, 0, 0}, .m_is_airborne = false}; constexpr Target<float> target{.m_origin = {100, 0, 90}, .m_velocity = {0, 0, 0}, .m_is_airborne = false};
constexpr Projectile projectile = {.m_origin = {3, 2, 1}, .m_launch_speed = 5000, .m_gravity_scale = 0.4}; constexpr Projectile<float> projectile = {.m_origin = {3, 2, 1}, .m_launch_speed = 5000.f, .m_gravity_scale = 0.4f};
for ([[maybe_unused]] const auto _: state) for ([[maybe_unused]] const auto _: state)
std::ignore = ProjPredEngineLegacy(400, simulation_time_step, 50, hit_distance_tolerance) std::ignore = ProjPredEngineLegacy<>(400.f, simulation_time_step, 50.f, hit_distance_tolerance)
.maybe_calculate_aim_point(projectile, target); .maybe_calculate_aim_point(projectile, target);
} }
+11
View File
@@ -6,6 +6,17 @@ if (@OMATH_IMGUI_INTEGRATION@)
find_dependency(imgui CONFIG) find_dependency(imgui CONFIG)
endif() endif()
if (@OMATH_ENABLE_LUA@)
find_dependency(Lua)
endif()
if (@OMATH_ENABLE_HOOKING@)
find_dependency(safetyhook CONFIG)
if (NOT WIN32 AND (UNIX AND NOT APPLE))
find_dependency(OpenGL)
endif()
endif()
# Load the targets for the omath library # Load the targets for the omath library
include("${CMAKE_CURRENT_LIST_DIR}/omathTargets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/omathTargets.cmake")
check_required_components(omath) check_required_components(omath)
+65 -2
View File
@@ -1,6 +1,6 @@
# Installation # 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 **Note**: Support vcpkg for package management
1. Install [vcpkg](https://github.com/microsoft/vcpkg) 1. Install [vcpkg](https://github.com/microsoft/vcpkg)
2. Run the following command to install the orange-math package: 2. Run the following command to install the orange-math package:
@@ -28,6 +28,69 @@ target("...")
add_packages("omath") add_packages("omath")
``` ```
## <img width="28px" src="https://conan.io/favicon.png" /> Using Conan
**Note**: Support Conan for package management
1. Install [Conan](https://conan.io/downloads)
2. Run the following command to install the omath package:
```
conan install --requires="omath/[*]" --build=missing
```
conanfile.txt
```ini
[requires]
omath/[*]
[generators]
CMakeDeps
CMakeToolchain
```
CMakeLists.txt
```cmake
find_package(omath CONFIG REQUIRED)
target_link_libraries(main PRIVATE omath::omath)
```
For more details, see the [Conan documentation](https://docs.conan.io/2/).
## <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 ## <img width="28px" src="https://upload.wikimedia.org/wikipedia/commons/e/ef/CMake_logo.svg?" /> Build from source using CMake
1. **Preparation** 1. **Preparation**
+12 -10
View File
@@ -3,8 +3,8 @@
> Header: `omath/trigonometry/angle.hpp` > Header: `omath/trigonometry/angle.hpp`
> Namespace: `omath` > Namespace: `omath`
> Template: `Angle<Type = float, min = 0, max = 360, flags = AngleFlags::Normalized>` > Template: `Angle<Type = float, min = 0, max = 360, flags = AngleFlags::Normalized>`
> Requires: `std::is_arithmetic_v<Type>` > Requires: `std::is_floating_point_v<Type>`
> Formatters: `std::formatter` for `char`, `wchar_t`, `char8_t` → `"{}deg"` > Formatters: `std::formatter` for `char` and `wchar_t` → `"{}deg"`
--- ---
@@ -14,7 +14,7 @@
Two behaviors via `AngleFlags`: Two behaviors via `AngleFlags`:
* `AngleFlags::Normalized` (default): values are wrapped into `[min, max]` using `angles::wrap_angle`. * `AngleFlags::Normalized` (default): values are wrapped into `[min, max)` using `angles::wrap_angle`.
* `AngleFlags::Clamped`: values are clamped to `[min, max]` using `std::clamp`. * `AngleFlags::Clamped`: values are clamped to `[min, max]` using `std::clamp`.
--- ---
@@ -28,12 +28,16 @@ enum class AngleFlags { Normalized = 0, Clamped = 1 };
template<class Type = float, Type min = Type(0), Type max = Type(360), template<class Type = float, Type min = Type(0), Type max = Type(360),
AngleFlags flags = AngleFlags::Normalized> AngleFlags flags = AngleFlags::Normalized>
requires std::is_arithmetic_v<Type> requires std::is_floating_point_v<Type>
class Angle { class Angle {
public: public:
// Construction // Construction
static constexpr Angle from_degrees(const Type& deg) noexcept; static constexpr Angle from_degrees(const Type& deg) noexcept;
static constexpr Angle from_radians(const Type& rad) noexcept; static constexpr Angle from_radians(const Type& rad) noexcept;
static constexpr Angle from_asin(const Type& value) noexcept;
static constexpr Angle from_acos(const Type& value) noexcept;
static constexpr Angle from_atan(const Type& value) noexcept;
static constexpr Angle from_atan2(const Type& y, const Type& x) noexcept;
constexpr Angle() noexcept; // 0 deg, adjusted by flags/range constexpr Angle() noexcept; // 0 deg, adjusted by flags/range
// Accessors / conversions (degrees stored internally) // Accessors / conversions (degrees stored internally)
@@ -45,10 +49,9 @@ public:
Type sin() const noexcept; Type sin() const noexcept;
Type cos() const noexcept; Type cos() const noexcept;
Type tan() const noexcept; Type tan() const noexcept;
Type atan() const noexcept; // atan(as_radians()) (rarely used)
Type cot() const noexcept; // cos()/sin() (watch sin≈0) Type cot() const noexcept; // cos()/sin() (watch sin≈0)
// Arithmetic (wraps or clamps per flags and [min,max]) // Arithmetic (wraps or clamps per flags and configured range)
constexpr Angle& operator+=(const Angle&) noexcept; constexpr Angle& operator+=(const Angle&) noexcept;
constexpr Angle& operator-=(const Angle&) noexcept; constexpr Angle& operator-=(const Angle&) noexcept;
constexpr Angle operator+(const Angle&) noexcept; constexpr Angle operator+(const Angle&) noexcept;
@@ -68,7 +71,7 @@ public:
std::format("{}", Angle<float>::from_degrees(45)); // "45deg" std::format("{}", Angle<float>::from_degrees(45)); // "45deg"
``` ```
Formatters exist for `char`, `wchar_t`, and `char8_t`. Formatters exist for `char` and `wchar_t`.
--- ---
@@ -116,10 +119,9 @@ float deg = *yaw; // same as yaw.as_degrees()
## Semantics & notes ## Semantics & notes
* **Storage & units:** Internally stores **degrees** (`Type m_angle`). `as_radians()`/`from_radians()` use the project helpers in `omath::angles`. * **Storage & units:** Internally stores **degrees** (`Type m_angle`). `as_radians()`/`from_radians()` use the project helpers in `omath::angles`.
* **Arithmetic honors policy:** `operator+=`/`-=` and the binary `+`/`-` apply **wrap** or **clamp** in `[min,max]`, mirroring construction behavior. * **Arithmetic honors policy:** `operator+=`/`-=` and the binary `+`/`-` apply **wrap** or **clamp**, mirroring construction behavior.
* **`atan()`**: returns `std::atan(as_radians())` (the arctangent of the *radian value*). This is mathematically unusual for an angle type and is rarely useful; prefer `tan()`/`atan2` in client code when solving geometry problems.
* **`cot()` / `tan()` singularities:** Near multiples where `sin() ≈ 0` or `cos() ≈ 0`, results blow up. Guard in your usage if inputs can approach these points. * **`cot()` / `tan()` singularities:** Near multiples where `sin() ≈ 0` or `cos() ≈ 0`, results blow up. Guard in your usage if inputs can approach these points.
* **Comparison:** `operator<=>` is defaulted. With normalization, distinct representatives can compare as expected (e.g., `-180` vs `180` in signed ranges are distinct endpoints). * **Comparison:** `operator<=>` is defaulted. Normalization canonicalizes the maximum endpoint to the minimum endpoint.
* **No implicit numeric conversion:** Theres **no `operator Type()`**. Use `as_degrees()`/`as_radians()` (or `*angle`) explicitly—this intentional friction avoids unit mistakes. * **No implicit numeric conversion:** Theres **no `operator Type()`**. Use `as_degrees()`/`as_radians()` (or `*angle`) explicitly—this intentional friction avoids unit mistakes.
--- ---
+8 -8
View File
@@ -4,7 +4,7 @@
> Namespace: `omath::angles` > Namespace: `omath::angles`
> All functions are `[[nodiscard]]` and `noexcept` where applicable. > All functions are `[[nodiscard]]` and `noexcept` where applicable.
A small set of constexpr-friendly utilities for converting between degrees/radians, converting horizontal/vertical field of view, and wrapping angles into a closed interval. A small set of constexpr-friendly utilities for converting between degrees/radians, converting horizontal/vertical field of view, and wrapping angles into a half-open interval.
--- ---
@@ -29,9 +29,9 @@ template<class Type>
requires std::is_floating_point_v<Type> requires std::is_floating_point_v<Type>
Type vertical_fov_to_horizontal(const Type& vertical_fov, const Type& aspect) noexcept; Type vertical_fov_to_horizontal(const Type& vertical_fov, const Type& aspect) noexcept;
// Wrap angle into [min, max] (any arithmetic type) // Wrap angle into [min, max) (floating-point types)
template<class Type> template<class Type>
requires std::is_arithmetic_v<Type> requires std::is_floating_point_v<Type>
Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept; Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept;
``` ```
@@ -66,10 +66,10 @@ Formulas (in radians):
### Wrapping angles (or any periodic value) ### Wrapping angles (or any periodic value)
Wrap any numeric `angle` into `[min, max]`: Wrap any floating-point `angle` into `[min, max)`:
```cpp ```cpp
// Wrap degrees into [0, 360] // Wrap degrees into [0, 360)
float a = omath::angles::wrap_angle( 370.0f, 0.0f, 360.0f); // 10 float a = omath::angles::wrap_angle( 370.0f, 0.0f, 360.0f); // 10
float b = omath::angles::wrap_angle( -15.0f, 0.0f, 360.0f); // 345 float b = omath::angles::wrap_angle( -15.0f, 0.0f, 360.0f); // 345
// Signed range [-180,180] // Signed range [-180,180]
@@ -83,10 +83,10 @@ float c = omath::angles::wrap_angle( 200.0f, -180.0f, 180.0f); // -160
* **Type requirements** * **Type requirements**
* Converters & FOV helpers require **floating-point** `Type`. * Converters & FOV helpers require **floating-point** `Type`.
* `wrap_angle` accepts any arithmetic `Type` (floats or integers). * `wrap_angle` accepts floating-point types.
* **Aspect ratio** must be **positive** and finite. For `aspect == 0` the FOV helpers are undefined. * **Aspect ratio** must be **positive** and finite. For `aspect == 0` the FOV helpers are undefined.
* **Units**: FOV functions accept/return **degrees** but compute internally in radians. * **Units**: FOV functions accept/return **degrees** but compute internally in radians.
* **Wrapping interval**: Behavior assumes `max > min`. The result lies in the **closed interval** `[min, max]` with modulo arithmetic; if you need half-open behavior (e.g., `[min,max)`), adjust your range or post-process endpoint cases. * **Wrapping interval**: Behavior assumes `max > min`. The result lies in the half-open interval `[min, max)`.
* **constexpr**: Converters are `constexpr`; FOV helpers are runtime constexpr-compatible except for `std::atan/std::tan` constraints on some standard libraries. * **constexpr**: Converters are `constexpr`; FOV helpers are runtime constexpr-compatible except for `std::atan/std::tan` constraints on some standard libraries.
--- ---
@@ -103,5 +103,5 @@ float v = horizontal_fov_to_vertical(90.0f, 16.0f/9.0f);
float h = vertical_fov_to_horizontal(v, 16.0f/9.0f); float h = vertical_fov_to_horizontal(v, 16.0f/9.0f);
assert(std::abs(h - 90.0f) < 1e-5f); assert(std::abs(h - 90.0f) < 1e-5f);
assert(wrap_angle(360.0f, 0.0f, 360.0f) == 0.0f || wrap_angle(360.0f, 0.0f, 360.0f) == 360.0f); assert(wrap_angle(360.0f, 0.0f, 360.0f) == 0.0f);
``` ```
+15
View File
@@ -2,6 +2,21 @@ add_subdirectory(example_barycentric)
add_subdirectory(example_glfw3) add_subdirectory(example_glfw3)
add_subdirectory(example_proj_mat_builder) add_subdirectory(example_proj_mat_builder)
add_subdirectory(example_signature_scan) add_subdirectory(example_signature_scan)
add_subdirectory(example_hud)
if(OMATH_ENABLE_HOOKING AND WIN32)
# Requires imgui with dx9-binding, dx11-binding, dx12-binding, opengl3-binding, win32-binding.
# Install via: vcpkg install imgui[dx9-binding,dx11-binding,dx12-binding,opengl3-binding,win32-binding]
find_package(imgui CONFIG QUIET)
if(imgui_FOUND)
add_subdirectory(example_dx9_hook)
add_subdirectory(example_dx11_hook)
add_subdirectory(example_dx12_hook)
add_subdirectory(example_opengl_hook)
else()
message(STATUS "[omath] imgui not found - hook examples skipped")
endif()
endif()
if(OMATH_ENABLE_VALGRIND) if(OMATH_ENABLE_VALGRIND)
omath_setup_valgrind(example_projection_matrix_builder) omath_setup_valgrind(example_projection_matrix_builder)
@@ -71,18 +71,18 @@ void drawChar(char c, float x, float y, float scale, const Color& color, std::ve
lines.push_back(x + x1 * w); lines.push_back(x + x1 * w);
lines.push_back(y + y1 * h); lines.push_back(y + y1 * h);
lines.push_back(0.0f); lines.push_back(0.0f);
lines.push_back(color.x); lines.push_back(color.value().x);
lines.push_back(color.y); lines.push_back(color.value().y);
lines.push_back(color.z); lines.push_back(color.value().z);
lines.push_back(1.0f); // size lines.push_back(1.0f); // size
lines.push_back(1.0f); // isLine lines.push_back(1.0f); // isLine
lines.push_back(x + x2 * w); lines.push_back(x + x2 * w);
lines.push_back(y + y2 * h); lines.push_back(y + y2 * h);
lines.push_back(0.0f); lines.push_back(0.0f);
lines.push_back(color.x); lines.push_back(color.value().x);
lines.push_back(color.y); lines.push_back(color.value().y);
lines.push_back(color.z); lines.push_back(color.value().z);
lines.push_back(1.0f); // size lines.push_back(1.0f); // size
lines.push_back(1.0f); // isLine lines.push_back(1.0f); // isLine
}; };
+13
View File
@@ -0,0 +1,13 @@
project(example_dx11_hook)
add_library(${PROJECT_NAME} SHARED dllmain.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 23
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
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(imgui CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath imgui::imgui d3d11 dxgi)
+179
View File
@@ -0,0 +1,179 @@
#include "omath/hooks/hooks_manager.hpp"
#include <Windows.h>
#include <d3d11.h>
#include <dxgi.h>
#include <imgui.h>
#include <imgui_impl_dx11.h>
#include <imgui_impl_win32.h>
#include <thread>
#include <tuple>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);
namespace
{
bool g_initialized = false;
bool g_init_attempted = false;
ID3D11Device* g_device = nullptr;
ID3D11DeviceContext* g_context = nullptr;
ID3D11RenderTargetView* g_render_target_view = nullptr;
[[nodiscard]]
bool create_render_target(IDXGISwapChain* swap_chain)
{
ID3D11Texture2D* back_buffer = nullptr;
if (FAILED(swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer))))
return false;
D3D11_RENDER_TARGET_VIEW_DESC desc{};
desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
const HRESULT result = g_device->CreateRenderTargetView(back_buffer, &desc, &g_render_target_view);
back_buffer->Release();
return SUCCEEDED(result);
}
void release_render_target()
{
if (g_context)
g_context->OMSetRenderTargets(0, nullptr, nullptr);
if (g_render_target_view)
{
g_render_target_view->Release();
g_render_target_view = nullptr;
}
if (g_context)
g_context->Flush();
}
void init(IDXGISwapChain* swap_chain)
{
g_init_attempted = true;
if (FAILED(swap_chain->GetDevice(IID_PPV_ARGS(&g_device))))
return;
g_device->GetImmediateContext(&g_context);
DXGI_SWAP_CHAIN_DESC desc{};
std::ignore = swap_chain->GetDesc(&desc);
std::ignore = create_render_target(swap_chain);
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().LogFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui_ImplWin32_Init(desc.OutputWindow);
ImGui_ImplDX11_Init(g_device, g_context);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_wnd_proc(
[](const HWND h, const UINT msg, const WPARAM wp, const LPARAM lp) -> std::optional<LRESULT>
{
if (ImGui_ImplWin32_WndProcHandler(h, msg, wp, lp))
return 0;
return std::nullopt;
});
std::ignore = mgr.hook_wnd_proc(desc.OutputWindow);
g_initialized = true;
}
void on_present(IDXGISwapChain* swap_chain, UINT, UINT)
{
if (!g_initialized)
{
if (!g_init_attempted)
init(swap_chain);
return;
}
if (!g_render_target_view && !create_render_target(swap_chain))
return;
ID3D11RenderTargetView* previous_render_target_views[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT]{};
ID3D11DepthStencilView* previous_depth_stencil_view = nullptr;
g_context->OMGetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, previous_render_target_views,
&previous_depth_stencil_view);
g_context->OMSetRenderTargets(1, &g_render_target_view, nullptr);
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::GetIO().MouseDrawCursor = true;
ImGui::ShowDemoWindow();
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
g_context->OMSetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, previous_render_target_views,
previous_depth_stencil_view);
for (ID3D11RenderTargetView* render_target_view : previous_render_target_views)
{
if (render_target_view)
render_target_view->Release();
}
if (previous_depth_stencil_view)
previous_depth_stencil_view->Release();
}
void on_resize_buffers(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT)
{
release_render_target();
}
} // namespace
BOOL WINAPI DllMain(const HINSTANCE h_instance, const DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(h_instance);
std::thread(
[]()
{
while (!GetModuleHandle("d3d11.dll"))
Sleep(100);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_present(on_present);
mgr.set_on_resize_buffers(on_resize_buffers);
std::ignore = mgr.hook_dx11();
return 0;
})
.detach();
}
else if (reason == DLL_PROCESS_DETACH)
{
auto& mgr = omath::hooks::HooksManager::get();
mgr.unhook_wnd_proc();
mgr.unhook_dx11();
if (g_initialized)
{
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
release_render_target();
if (g_context)
{
g_context->Release();
g_context = nullptr;
}
if (g_device)
{
g_device->Release();
g_device = nullptr;
}
}
return TRUE;
}
+13
View File
@@ -0,0 +1,13 @@
project(example_dx12_hook)
add_library(${PROJECT_NAME} MODULE dllmain.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 23
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
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(imgui CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath imgui::imgui d3d12 dxgi)
+633
View File
@@ -0,0 +1,633 @@
#include "omath/hooks/hooks_manager.hpp"
#include <Windows.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <imgui.h>
#include <imgui_impl_dx12.h>
#include <imgui_impl_win32.h>
#include <mutex>
#include <tuple>
#include <vector>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);
bool show_menu = true;
namespace
{
constexpr UINT srv_heap_size = 64;
struct frame_context
{
ID3D12Resource* render_target = nullptr;
// Each back buffer gets its own allocator because allocators cannot be reset while GPU work uses them.
ID3D12CommandAllocator* command_allocator = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = {};
UINT64 fence_value = 0;
};
bool g_initialized = false;
bool g_init_attempted = false;
ID3D12Device* g_device = nullptr;
ID3D12CommandQueue* g_command_queue = nullptr;
ID3D12CommandQueue* g_pending_command_queue = nullptr;
IDXGISwapChain3* g_swap_chain = nullptr;
IUnknown* g_swap_chain_identity = nullptr;
ID3D12DescriptorHeap* g_rtv_heap = nullptr;
ID3D12DescriptorHeap* g_srv_heap = nullptr;
ID3D12GraphicsCommandList* g_command_list = nullptr;
ID3D12Fence* g_fence = nullptr;
HANDLE g_fence_event = nullptr;
UINT64 g_fence_value = 0;
UINT g_srv_descriptor_size = 0;
bool g_command_queue_selected = false;
std::mutex g_command_queue_mutex;
std::vector<UINT> g_free_srv_indices;
std::vector<frame_context> g_frames;
void allocate_srv_descriptor(ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* cpu_handle,
D3D12_GPU_DESCRIPTOR_HANDLE* gpu_handle)
{
IM_ASSERT(!g_free_srv_indices.empty());
const UINT index = g_free_srv_indices.back();
g_free_srv_indices.pop_back();
*cpu_handle = g_srv_heap->GetCPUDescriptorHandleForHeapStart();
*gpu_handle = g_srv_heap->GetGPUDescriptorHandleForHeapStart();
cpu_handle->ptr += static_cast<SIZE_T>(index) * g_srv_descriptor_size;
gpu_handle->ptr += static_cast<UINT64>(index) * g_srv_descriptor_size;
}
void free_srv_descriptor(ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle,
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle)
{
const D3D12_CPU_DESCRIPTOR_HANDLE cpu_start = g_srv_heap->GetCPUDescriptorHandleForHeapStart();
const D3D12_GPU_DESCRIPTOR_HANDLE gpu_start = g_srv_heap->GetGPUDescriptorHandleForHeapStart();
const UINT cpu_index = static_cast<UINT>((cpu_handle.ptr - cpu_start.ptr) / g_srv_descriptor_size);
const UINT gpu_index = static_cast<UINT>((gpu_handle.ptr - gpu_start.ptr) / g_srv_descriptor_size);
IM_ASSERT(cpu_index == gpu_index && cpu_index < srv_heap_size);
g_free_srv_indices.push_back(cpu_index);
}
void remember_direct_command_queue(ID3D12CommandQueue* queue)
{
if (queue->GetDesc().Type != D3D12_COMMAND_LIST_TYPE_DIRECT)
return;
std::scoped_lock lock(g_command_queue_mutex);
if (g_command_queue_selected || g_pending_command_queue == queue)
return;
queue->AddRef();
if (g_pending_command_queue)
g_pending_command_queue->Release();
g_pending_command_queue = queue;
}
ID3D12CommandQueue* take_command_queue_for_device(ID3D12Device* device)
{
std::scoped_lock lock(g_command_queue_mutex);
if (!g_pending_command_queue)
return nullptr;
ID3D12Device* queue_device = nullptr;
if (FAILED(g_pending_command_queue->GetDevice(IID_PPV_ARGS(&queue_device))))
return nullptr;
const bool matches = queue_device == device;
queue_device->Release();
if (!matches)
return nullptr;
ID3D12CommandQueue* queue = g_pending_command_queue;
g_pending_command_queue = nullptr;
g_command_queue_selected = true;
return queue;
}
bool is_target_swap_chain(IDXGISwapChain* swap_chain)
{
if (!g_swap_chain_identity)
return false;
IUnknown* identity = nullptr;
if (FAILED(swap_chain->QueryInterface(IID_PPV_ARGS(&identity))))
return false;
const bool matches = identity == g_swap_chain_identity;
identity->Release();
return matches;
}
// This fence tracks only the overlay work submitted by this DLL, not the game's whole frame.
bool create_sync_objects()
{
if (g_fence)
return true;
if (FAILED(g_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence))))
return false;
g_fence_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (!g_fence_event)
{
g_fence->Release();
g_fence = nullptr;
return false;
}
g_fence_value = 0;
return true;
}
bool wait_for_fence_value(UINT64 fence_value)
{
if (!g_fence || !g_fence_event || fence_value == 0 || g_fence->GetCompletedValue() >= fence_value)
return true;
if (FAILED(g_fence->SetEventOnCompletion(fence_value, g_fence_event)))
return false;
WaitForSingleObject(g_fence_event, INFINITE);
return true;
}
void wait_for_frame(frame_context& fc)
{
// The current back buffer's allocator is safe to reset only after its previous overlay pass completes.
if (wait_for_fence_value(fc.fence_value))
fc.fence_value = 0;
}
void wait_for_gpu()
{
// ResizeBuffers and shutdown must not release back buffers still referenced by queued overlay commands.
if (!g_command_queue || !g_fence || !g_fence_event)
return;
const UINT64 fence_value = ++g_fence_value;
if (FAILED(g_command_queue->Signal(g_fence, fence_value)))
return;
if (wait_for_fence_value(fence_value))
{
for (auto& fc : g_frames)
fc.fence_value = 0;
}
}
bool signal_frame(frame_context& fc)
{
if (!g_command_queue || !g_fence)
return false;
const UINT64 fence_value = ++g_fence_value;
if (FAILED(g_command_queue->Signal(g_fence, fence_value)))
return false;
fc.fence_value = fence_value;
return true;
}
void release_sync_objects()
{
if (g_fence_event)
{
CloseHandle(g_fence_event);
g_fence_event = nullptr;
}
if (g_fence)
{
g_fence->Release();
g_fence = nullptr;
}
g_fence_value = 0;
}
void release_frame_contexts()
{
for (auto& fc : g_frames)
{
if (fc.render_target)
{
fc.render_target->Release();
fc.render_target = nullptr;
}
if (fc.command_allocator)
{
fc.command_allocator->Release();
fc.command_allocator = nullptr;
}
fc.fence_value = 0;
}
g_frames.clear();
if (g_rtv_heap)
{
g_rtv_heap->Release();
g_rtv_heap = nullptr;
}
}
void release_command_objects()
{
if (g_command_list)
{
g_command_list->Release();
g_command_list = nullptr;
}
}
bool create_command_objects()
{
if (g_frames.empty() || !g_frames[0].command_allocator)
return false;
if (FAILED(g_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_frames[0].command_allocator,
nullptr, IID_PPV_ARGS(&g_command_list))))
{
release_command_objects();
return false;
}
g_command_list->Close();
return true;
}
bool create_render_targets(IDXGISwapChain* swap_chain)
{
// These references must be released before IDXGISwapChain::ResizeBuffers reaches the original function.
DXGI_SWAP_CHAIN_DESC desc{};
if (FAILED(swap_chain->GetDesc(&desc)))
return false;
const UINT buffer_count = desc.BufferCount;
D3D12_DESCRIPTOR_HEAP_DESC heap_desc{};
heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
heap_desc.NumDescriptors = buffer_count;
heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
heap_desc.NodeMask = 1;
if (FAILED(g_device->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&g_rtv_heap))))
return false;
g_frames.resize(buffer_count);
const UINT rtv_size = g_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = g_rtv_heap->GetCPUDescriptorHandleForHeapStart();
for (UINT i = 0; i < buffer_count; ++i)
{
g_frames[i].rtv_handle = rtv_handle;
if (FAILED(g_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&g_frames[i].command_allocator))))
{
release_frame_contexts();
return false;
}
if (FAILED(swap_chain->GetBuffer(i, IID_PPV_ARGS(&g_frames[i].render_target))))
{
release_frame_contexts();
return false;
}
g_device->CreateRenderTargetView(g_frames[i].render_target, nullptr, rtv_handle);
rtv_handle.ptr += rtv_size;
}
return true;
}
void init(IDXGISwapChain* swap_chain)
{
IDXGISwapChain3* swap_chain3 = nullptr;
if (FAILED(swap_chain->QueryInterface(IID_PPV_ARGS(&swap_chain3))))
return;
ID3D12Device* device = nullptr;
if (FAILED(swap_chain->GetDevice(IID_PPV_ARGS(&device))))
{
swap_chain3->Release();
return;
}
IUnknown* swap_chain_identity = nullptr;
if (FAILED(swap_chain->QueryInterface(IID_PPV_ARGS(&swap_chain_identity))))
{
device->Release();
swap_chain3->Release();
return;
}
DXGI_SWAP_CHAIN_DESC desc{};
if (FAILED(swap_chain->GetDesc(&desc)))
{
swap_chain_identity->Release();
device->Release();
swap_chain3->Release();
return;
}
ID3D12CommandQueue* command_queue = take_command_queue_for_device(device);
if (!command_queue)
{
swap_chain_identity->Release();
device->Release();
swap_chain3->Release();
return;
}
g_init_attempted = true;
g_swap_chain = swap_chain3;
g_swap_chain_identity = swap_chain_identity;
g_device = device;
g_command_queue = command_queue;
const UINT buffer_count = desc.BufferCount;
{
D3D12_DESCRIPTOR_HEAP_DESC heap_desc{};
heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
heap_desc.NumDescriptors = srv_heap_size;
heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
if (FAILED(g_device->CreateDescriptorHeap(&heap_desc, IID_PPV_ARGS(&g_srv_heap))))
return;
g_srv_descriptor_size = g_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
g_free_srv_indices.reserve(srv_heap_size);
for (UINT i = srv_heap_size; i > 0; --i)
g_free_srv_indices.push_back(i - 1);
}
if (!create_render_targets(swap_chain))
return;
if (!create_command_objects())
return;
if (!create_sync_objects())
return;
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().LogFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGui_ImplWin32_Init(desc.OutputWindow);
ImGui_ImplDX12_InitInfo init_info{};
init_info.Device = g_device;
init_info.CommandQueue = g_command_queue;
init_info.NumFramesInFlight = static_cast<int>(buffer_count);
init_info.RTVFormat = desc.BufferDesc.Format;
init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;
init_info.SrvDescriptorHeap = g_srv_heap;
init_info.SrvDescriptorAllocFn = allocate_srv_descriptor;
init_info.SrvDescriptorFreeFn = free_srv_descriptor;
ImGui_ImplDX12_Init(&init_info);
ImGui_ImplDX12_CreateDeviceObjects();
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_wnd_proc(
[](HWND h, UINT msg, WPARAM wp, LPARAM lp) -> std::optional<LRESULT>
{
if (!show_menu)
return std::nullopt;
if (ImGui_ImplWin32_WndProcHandler(h, msg, wp, lp))
return true;
return std::nullopt;
});
std::ignore = mgr.hook_wnd_proc(desc.OutputWindow);
g_initialized = true;
}
void on_execute_command_lists(ID3D12CommandQueue* queue, UINT, ID3D12CommandList* const*)
{
// The most recently used DIRECT queue is the best available proxy for the swap chain's presentation queue.
remember_direct_command_queue(queue);
}
bool ensure_initialized(IDXGISwapChain* swap_chain)
{
if (g_initialized)
return true;
if (!g_init_attempted)
init(swap_chain);
return false;
}
bool ensure_render_targets(IDXGISwapChain* swap_chain)
{
if (!g_frames.empty() && g_rtv_heap)
return true;
return create_render_targets(swap_chain);
}
bool ensure_command_list()
{
if (g_command_list)
return true;
return create_command_objects();
}
bool ensure_sync_ready()
{
if (g_fence)
return true;
return create_sync_objects();
}
bool ensure_present_resources(IDXGISwapChain* swap_chain)
{
if (!ensure_initialized(swap_chain) || !g_command_queue)
return false;
return ensure_render_targets(swap_chain) && ensure_command_list() && ensure_sync_ready();
}
bool begin_imgui_frame()
{
if (GetAsyncKeyState(VK_INSERT) & 1)
show_menu = !show_menu;
if (!show_menu)
return false;
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::GetIO().MouseDrawCursor = true;
ImGui::ShowDemoWindow();
ImGui::EndFrame();
return true;
}
frame_context* current_frame_context()
{
const UINT buf_idx = g_swap_chain->GetCurrentBackBufferIndex();
if (buf_idx >= g_frames.size())
return nullptr;
return &g_frames[buf_idx];
}
bool reset_overlay_command_list(frame_context& fc)
{
wait_for_frame(fc);
// Both resets depend on wait_for_frame(); otherwise DLSSG/Streamline can observe invalid GPU work.
if (FAILED(fc.command_allocator->Reset()))
return false;
if (FAILED(g_command_list->Reset(fc.command_allocator, nullptr)))
return false;
return true;
}
void transition_render_target(frame_context& fc, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after)
{
D3D12_RESOURCE_BARRIER barrier{};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = fc.render_target;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = before;
barrier.Transition.StateAfter = after;
g_command_list->ResourceBarrier(1, &barrier);
}
void record_overlay_commands(frame_context& fc)
{
transition_render_target(fc, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
g_command_list->OMSetRenderTargets(1, &fc.rtv_handle, FALSE, nullptr);
g_command_list->SetDescriptorHeaps(1, &g_srv_heap);
ImGui::Render();
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), g_command_list);
transition_render_target(fc, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
}
bool submit_overlay_commands(frame_context& fc)
{
if (FAILED(g_command_list->Close()))
return false;
ID3D12CommandList* cmd_lists[] = {g_command_list};
g_command_queue->ExecuteCommandLists(1, cmd_lists);
return signal_frame(fc);
}
void on_present(IDXGISwapChain* swap_chain, UINT, UINT)
{
if (g_initialized && !is_target_swap_chain(swap_chain))
return;
if (!ensure_present_resources(swap_chain) || !begin_imgui_frame())
return;
frame_context* fc = current_frame_context();
if (!fc || !reset_overlay_command_list(*fc))
return;
record_overlay_commands(*fc);
std::ignore = submit_overlay_commands(*fc);
}
void on_resize_buffers(IDXGISwapChain* swap_chain, UINT, UINT, UINT, DXGI_FORMAT, UINT)
{
if (!is_target_swap_chain(swap_chain))
return;
wait_for_gpu();
release_command_objects();
release_frame_contexts();
}
void release_dx12_resources()
{
wait_for_gpu();
release_command_objects();
release_frame_contexts();
release_sync_objects();
if (g_srv_heap)
{
g_srv_heap->Release();
g_srv_heap = nullptr;
}
g_srv_descriptor_size = 0;
g_free_srv_indices.clear();
if (g_swap_chain)
{
g_swap_chain->Release();
g_swap_chain = nullptr;
}
if (g_swap_chain_identity)
{
g_swap_chain_identity->Release();
g_swap_chain_identity = nullptr;
}
if (g_device)
{
g_device->Release();
g_device = nullptr;
}
if (g_command_queue)
{
g_command_queue->Release();
g_command_queue = nullptr;
}
std::scoped_lock lock(g_command_queue_mutex);
if (g_pending_command_queue)
{
g_pending_command_queue->Release();
g_pending_command_queue = nullptr;
}
g_command_queue_selected = false;
}
} // namespace
BOOL WINAPI DllMain(HINSTANCE h_instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(h_instance);
std::thread(
[]()
{
while (!GetModuleHandle("d3d12.dll"))
Sleep(100);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_present(on_present);
mgr.set_on_resize_buffers(on_resize_buffers);
mgr.set_on_execute_command_lists(on_execute_command_lists);
std::ignore = mgr.hook_dx12();
return 0;
})
.detach();
}
else if (reason == DLL_PROCESS_DETACH)
{
auto& mgr = omath::hooks::HooksManager::get();
mgr.unhook_wnd_proc();
mgr.unhook_dx12();
if (g_initialized)
{
ImGui_ImplDX12_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
release_dx12_resources();
}
return TRUE;
}
+13
View File
@@ -0,0 +1,13 @@
project(example_dx9_hook)
add_library(${PROJECT_NAME} MODULE dllmain.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 23
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
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(imgui CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath imgui::imgui d3d9)
+107
View File
@@ -0,0 +1,107 @@
#include "omath/hooks/hooks_manager.hpp"
#include <Windows.h>
#include <d3d9.h>
#include <imgui.h>
#include <imgui_impl_dx9.h>
#include <imgui_impl_win32.h>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);
namespace
{
bool g_initialized = false;
bool g_init_attempted = false;
void init(IDirect3DDevice9* device)
{
g_init_attempted = true;
D3DDEVICE_CREATION_PARAMETERS params{};
if (FAILED(device->GetCreationParameters(&params)))
return;
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().LogFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui_ImplWin32_Init(params.hFocusWindow);
ImGui_ImplDX9_Init(device);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_wnd_proc(
[](HWND h, UINT msg, WPARAM wp, LPARAM lp) -> std::optional<LRESULT>
{
if (ImGui_ImplWin32_WndProcHandler(h, msg, wp, lp))
return 0;
return std::nullopt;
});
std::ignore = mgr.hook_wnd_proc(params.hFocusWindow);
g_initialized = true;
}
void on_present(IDirect3DDevice9* device, const RECT*, const RECT*, HWND, const RGNDATA*)
{
if (!g_initialized)
{
if (!g_init_attempted)
init(device);
return;
}
ImGui_ImplDX9_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::GetIO().MouseDrawCursor = true;
ImGui::ShowDemoWindow();
ImGui::End();
ImGui::EndFrame();
ImGui::Render();
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
}
void on_reset(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*)
{
if (g_initialized)
ImGui_ImplDX9_InvalidateDeviceObjects();
}
} // namespace
BOOL WINAPI DllMain(HINSTANCE h_instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(h_instance);
std::thread(
[]()
{
while (!GetModuleHandle("d3d9.dll"))
Sleep(100);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_dx9_present(on_present);
mgr.set_on_dx9_reset(on_reset);
std::ignore = mgr.hook_dx9();
return 0;
})
.detach();
}
else if (reason == DLL_PROCESS_DETACH)
{
auto& mgr = omath::hooks::HooksManager::get();
mgr.unhook_wnd_proc();
mgr.unhook_dx9();
if (g_initialized)
{
ImGui_ImplDX9_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
}
return TRUE;
}
+6 -6
View File
@@ -318,22 +318,22 @@ int main()
glfwPollEvents(); glfwPollEvents();
omath::Vector3<float> move_dir; omath::Vector3<float> move_dir;
if (glfwGetKey(window, GLFW_KEY_W)) if (glfwGetKey(window, GLFW_KEY_W))
move_dir += camera.get_forward(); move_dir += camera.get_abs_forward();
if (glfwGetKey(window, GLFW_KEY_A)) if (glfwGetKey(window, GLFW_KEY_A))
move_dir -= camera.get_right(); move_dir -= camera.get_abs_right();
if (glfwGetKey(window, GLFW_KEY_S)) if (glfwGetKey(window, GLFW_KEY_S))
move_dir -= camera.get_forward(); move_dir -= camera.get_abs_forward();
if (glfwGetKey(window, GLFW_KEY_D)) if (glfwGetKey(window, GLFW_KEY_D))
move_dir += camera.get_right(); move_dir += camera.get_abs_right();
if (glfwGetKey(window, GLFW_KEY_SPACE)) if (glfwGetKey(window, GLFW_KEY_SPACE))
move_dir += camera.get_up(); move_dir += camera.get_abs_up();
if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL)) if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL))
move_dir -= camera.get_up(); move_dir -= camera.get_abs_up();
auto delta = glfwGetTime() - old_mouse_time; auto delta = glfwGetTime() - old_mouse_time;
+16
View File
@@ -0,0 +1,16 @@
project(example_hud)
add_executable(${PROJECT_NAME} main.cpp gui/main_window.cpp gui/main_window.hpp)
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 glfw imgui::imgui omath::omath OpenGL::GL)
+408
View File
@@ -0,0 +1,408 @@
//
// Created by Orange on 11/11/2024.
//
#include "main_window.hpp"
#include "omath/hud/renderer_realizations/imgui_renderer.hpp"
#include <GLFW/glfw3.h>
#include <cmath>
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <omath/hud/entity_overlay.hpp>
namespace imgui_desktop::gui
{
bool MainWindow::m_canMoveWindow = false;
MainWindow::MainWindow(const std::string_view& caption, int width, int height)
{
if (!glfwInit())
std::exit(EXIT_FAILURE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, true);
m_window = glfwCreateWindow(width, height, caption.data(), nullptr, nullptr);
glfwMakeContextCurrent(m_window);
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = {0.05f, 0.05f, 0.05f, 0.92f};
ImGui::GetStyle().AntiAliasedLines = false;
ImGui::GetStyle().AntiAliasedFill = false;
ImGui_ImplGlfw_InitForOpenGL(m_window, true);
ImGui_ImplOpenGL3_Init("#version 150");
}
void MainWindow::Run()
{
while (!glfwWindowShouldClose(m_window) && m_opened)
{
glfwPollEvents();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
const auto* vp = ImGui::GetMainViewport();
ImGui::GetBackgroundDrawList()->AddRectFilled({}, vp->Size, ImColor(30, 30, 30, 220));
draw_controls();
draw_overlay();
ImGui::Render();
present();
}
glfwDestroyWindow(m_window);
}
void MainWindow::draw_controls()
{
const auto* vp = ImGui::GetMainViewport();
ImGui::SetNextWindowPos({0.f, 0.f});
ImGui::SetNextWindowSize({280.f, vp->Size.y});
ImGui::Begin("Controls", &m_opened,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
ImGui::PushItemWidth(160.f);
const auto draw_glow_controls = [](const char* label, GlowSettings& settings)
{
ImGui::PushID(label);
ImGui::TextUnformatted(label);
ImGui::Checkbox("Glow", &settings.enabled);
if (settings.enabled)
{
ImGui::ColorEdit4("Color", reinterpret_cast<float*>(&settings.color), ImGuiColorEditFlags_NoInputs);
ImGui::InputFloat("Radius", &settings.radius);
ImGui::InputFloat("Intensity", &settings.intensity);
}
ImGui::PopID();
};
if (ImGui::CollapsingHeader("Entity", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::SliderFloat("X##ent", &m_entity_x, 100.f, vp->Size.x - 100.f);
ImGui::SliderFloat("Top Y", &m_entity_top_y, 20.f, m_entity_bottom_y - 20.f);
ImGui::SliderFloat("Bottom Y", &m_entity_bottom_y, m_entity_top_y + 20.f, vp->Size.y - 20.f);
ImGui::SliderFloat("Aspect", &m_entity_aspect, 1.f, 10.f);
draw_glow_controls("Canvas light", m_canvas_glow);
if (m_canvas_glow.enabled)
{
ImGui::InputInt("Blur layers##canvas", &m_canvas_glow_layers);
ImGui::InputFloat("Rounding##canvas", &m_canvas_glow_rounding);
}
}
if (ImGui::CollapsingHeader("Box", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Checkbox("Box##chk", &m_show_box);
ImGui::SameLine();
ImGui::Checkbox("Cornered", &m_show_cornered_box);
ImGui::SameLine();
ImGui::Checkbox("Dashed", &m_show_dashed_box);
ImGui::ColorEdit4("Color##box", reinterpret_cast<float*>(&m_box_color), ImGuiColorEditFlags_NoInputs);
ImGui::ColorEdit4("Fill##box", reinterpret_cast<float*>(&m_box_fill), ImGuiColorEditFlags_NoInputs);
ImGui::Checkbox("Gradient fill", &m_gradient_box_fill);
ImGui::Checkbox("Animate gradients", &m_animate_gradients);
if (m_gradient_box_fill)
{
ImGui::ColorEdit4("Gradient top##box", reinterpret_cast<float*>(&m_box_gradient_top),
ImGuiColorEditFlags_NoInputs);
ImGui::ColorEdit4("Gradient bottom##box", reinterpret_cast<float*>(&m_box_gradient_bottom),
ImGuiColorEditFlags_NoInputs);
}
ImGui::ColorEdit4("Outline##box", reinterpret_cast<float*>(&m_box_outline), ImGuiColorEditFlags_NoInputs);
ImGui::SliderFloat("Thickness", &m_box_thickness, 0.5f, 5.f);
ImGui::SliderFloat("Corner ratio", &m_corner_ratio, 0.05f, 0.5f);
draw_glow_controls("Box glow", m_box_glow);
draw_glow_controls("Cornered box glow", m_cornered_box_glow);
ImGui::Separator();
ImGui::ColorEdit4("Dash color", reinterpret_cast<float*>(&m_dash_color), ImGuiColorEditFlags_NoInputs);
ImGui::SliderFloat("Dash length", &m_dash_len, 2.f, 30.f);
ImGui::SliderFloat("Dash gap", &m_dash_gap, 1.f, 20.f);
ImGui::SliderFloat("Dash thick", &m_dash_thickness, 0.5f, 5.f);
}
if (ImGui::CollapsingHeader("Bars", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::ColorEdit4("Color##bar", reinterpret_cast<float*>(&m_bar_color), ImGuiColorEditFlags_NoInputs);
ImGui::ColorEdit4("BG##bar", reinterpret_cast<float*>(&m_bar_bg_color), ImGuiColorEditFlags_NoInputs);
ImGui::ColorEdit4("Outline##bar", reinterpret_cast<float*>(&m_bar_outline_color),
ImGuiColorEditFlags_NoInputs);
ImGui::Checkbox("Gradient bars", &m_gradient_bars);
ImGui::SliderFloat("Width##bar", &m_bar_width, 1.f, 20.f);
ImGui::SliderFloat("Value##bar", &m_bar_value, 0.f, 1.f);
ImGui::SliderFloat("Offset##bar", &m_bar_offset, 1.f, 20.f);
ImGui::Checkbox("Right##bar", &m_show_right_bar);
ImGui::SameLine();
ImGui::Checkbox("Left##bar", &m_show_left_bar);
ImGui::Checkbox("Top##bar", &m_show_top_bar);
ImGui::SameLine();
ImGui::Checkbox("Bottom##bar", &m_show_bottom_bar);
ImGui::Checkbox("Right dashed##bar", &m_show_right_dashed_bar);
ImGui::SameLine();
ImGui::Checkbox("Left dashed##bar", &m_show_left_dashed_bar);
ImGui::Checkbox("Top dashed##bar", &m_show_top_dashed_bar);
ImGui::SameLine();
ImGui::Checkbox("Bot dashed##bar", &m_show_bottom_dashed_bar);
ImGui::SliderFloat("Dash len##bar", &m_bar_dash_len, 2.f, 20.f);
ImGui::SliderFloat("Dash gap##bar", &m_bar_dash_gap, 1.f, 15.f);
draw_glow_controls("Bar glow", m_bar_glow);
}
if (ImGui::CollapsingHeader("Labels", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Checkbox("Outlined", &m_outlined);
ImGui::Checkbox("Gradient centered label", &m_gradient_label);
if (m_gradient_label)
{
ImGui::ColorEdit4("Gradient left##lbl", reinterpret_cast<float*>(&m_label_gradient_left),
ImGuiColorEditFlags_NoInputs);
ImGui::ColorEdit4("Gradient right##lbl", reinterpret_cast<float*>(&m_label_gradient_right),
ImGuiColorEditFlags_NoInputs);
if (m_animate_gradients)
{
int direction = static_cast<int>(m_label_gradient_direction);
if (ImGui::Combo("Direction##lbl", &direction, "Right to left\0Left to right\0"))
m_label_gradient_direction = static_cast<omath::hud::GradientDirection>(direction);
}
}
ImGui::SliderFloat("Offset##lbl", &m_label_offset, 0.f, 15.f);
ImGui::Checkbox("Right##lbl", &m_show_right_labels);
ImGui::SameLine();
ImGui::Checkbox("Left##lbl", &m_show_left_labels);
ImGui::Checkbox("Top##lbl", &m_show_top_labels);
ImGui::SameLine();
ImGui::Checkbox("Bottom##lbl", &m_show_bottom_labels);
ImGui::Checkbox("Ctr top##lbl", &m_show_centered_top);
ImGui::SameLine();
ImGui::Checkbox("Ctr bot##lbl", &m_show_centered_bottom);
draw_glow_controls("Label glow", m_label_glow);
}
if (ImGui::CollapsingHeader("Skeleton"))
{
ImGui::Checkbox("Show##skel", &m_show_skeleton);
ImGui::ColorEdit4("Color##skel", reinterpret_cast<float*>(&m_skel_color), ImGuiColorEditFlags_NoInputs);
ImGui::SliderFloat("Thick##skel", &m_skel_thickness, 0.5f, 5.f);
}
if (ImGui::CollapsingHeader("Progress Ring"))
{
ImGui::Checkbox("Show##ring", &m_show_ring);
ImGui::ColorEdit4("Color##ring", reinterpret_cast<float*>(&m_ring_color), ImGuiColorEditFlags_NoInputs);
ImGui::ColorEdit4("BG##ring", reinterpret_cast<float*>(&m_ring_bg), ImGuiColorEditFlags_NoInputs);
ImGui::SliderFloat("Radius##ring", &m_ring_radius, 4.f, 30.f);
ImGui::SliderFloat("Value##ring", &m_ring_ratio, 0.f, 1.f);
ImGui::SliderFloat("Thick##ring", &m_ring_thickness, 0.5f, 6.f);
ImGui::SliderFloat("Offset##ring", &m_ring_offset, 0.f, 15.f);
}
if (ImGui::CollapsingHeader("Scan Marker"))
{
ImGui::Checkbox("Show##scan", &m_show_scan);
ImGui::ColorEdit4("Fill##scan", reinterpret_cast<float*>(&m_scan_color), ImGuiColorEditFlags_NoInputs);
ImGui::ColorEdit4("Outline##scan", reinterpret_cast<float*>(&m_scan_outline), ImGuiColorEditFlags_NoInputs);
ImGui::SliderFloat("Thick##scan", &m_scan_outline_thickness, 0.5f, 5.f);
}
if (ImGui::CollapsingHeader("Aim Dot"))
{
ImGui::Checkbox("Show##aim", &m_show_aim);
ImGui::ColorEdit4("Color##aim", reinterpret_cast<float*>(&m_aim_color), ImGuiColorEditFlags_NoInputs);
ImGui::SliderFloat("Radius##aim", &m_aim_radius, 1.f, 10.f);
}
if (ImGui::CollapsingHeader("Projectile Aim"))
{
ImGui::Checkbox("Show##proj", &m_show_proj);
ImGui::ColorEdit4("Color##proj", reinterpret_cast<float*>(&m_proj_color), ImGuiColorEditFlags_NoInputs);
ImGui::SliderFloat("Size##proj", &m_proj_size, 1.f, 30.f);
ImGui::SliderFloat("Line width##proj", &m_proj_line_width, 0.5f, 5.f);
ImGui::SliderFloat("Pos X##proj", &m_proj_pos_x, 0.f, vp->Size.x);
ImGui::SliderFloat("Pos Y##proj", &m_proj_pos_y, 0.f, vp->Size.y);
ImGui::Combo("Figure##proj", &m_proj_figure, "Circle\0Square\0");
}
if (ImGui::CollapsingHeader("Snap Line"))
{
ImGui::Checkbox("Show##snap", &m_show_snap);
ImGui::ColorEdit4("Color##snap", reinterpret_cast<float*>(&m_snap_color), ImGuiColorEditFlags_NoInputs);
ImGui::SliderFloat("Width##snap", &m_snap_width, 0.5f, 5.f);
}
ImGui::PopItemWidth();
ImGui::End();
}
void MainWindow::draw_overlay()
{
using namespace omath::hud::widget;
using omath::hud::when;
const auto* vp = ImGui::GetMainViewport();
const DashedBar dbar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width,
m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset};
const float entity_height = m_entity_bottom_y - m_entity_top_y;
const float entity_half_width = entity_height / m_entity_aspect;
const auto joint = [&](const float x, const float y)
{
return omath::Vector2<float>{m_entity_x + (x * 2.f - 1.f) * entity_half_width,
m_entity_top_y + y * entity_height};
};
const auto head = joint(0.5f, 0.1f);
const auto neck = joint(0.5f, 0.22f);
const auto torso = joint(0.5f, 0.5f);
const auto left_shoulder = joint(0.3f, 0.25f);
const auto right_shoulder = joint(0.7f, 0.25f);
const auto left_elbow = joint(0.18f, 0.42f);
const auto right_elbow = joint(0.82f, 0.42f);
const auto left_hand = joint(0.1f, 0.58f);
const auto right_hand = joint(0.9f, 0.58f);
const auto left_hip = joint(0.4f, 0.55f);
const auto right_hip = joint(0.6f, 0.55f);
const auto left_knee = joint(0.36f, 0.75f);
const auto right_knee = joint(0.64f, 0.75f);
const auto left_foot = joint(0.3f, 0.95f);
const auto right_foot = joint(0.7f, 0.95f);
const std::array skeleton_bones = {
Bone{head, neck},
Bone{neck, torso},
Bone{neck, left_shoulder},
Bone{left_shoulder, left_elbow},
Bone{left_elbow, left_hand},
Bone{neck, right_shoulder},
Bone{right_shoulder, right_elbow},
Bone{right_elbow, right_hand},
Bone{torso, left_hip},
Bone{left_hip, left_knee},
Bone{left_knee, left_foot},
Bone{torso, right_hip},
Bone{right_hip, right_knee},
Bone{right_knee, right_foot},
};
const auto animated_color = [&](const omath::Color& color, const float hue_offset)
{
if (!m_animate_gradients)
return color;
auto hsv = color.to_hsv();
hsv.hue = std::fmod(hsv.hue + static_cast<float>(ImGui::GetTime()) * 0.15f + hue_offset, 1.f);
const auto animated = omath::Color::from_hsv(hsv).value();
return omath::Color{animated.x, animated.y, animated.z, color.value().w};
};
const auto box_gradient_top = animated_color(m_box_gradient_top, 0.f);
const auto box_gradient_bottom = animated_color(m_box_gradient_bottom, 0.5f);
const auto red = omath::Color{1.f, 0.f, 0.f, 1.f};
const auto green = omath::Color{0.f, 1.f, 0.f, 1.f};
const auto make_glow = [](const GlowSettings& settings) -> std::optional<Glow>
{
if (!settings.enabled)
return std::nullopt;
return Glow{settings.color, settings.radius, settings.intensity};
};
const auto box_glow = make_glow(m_box_glow);
const auto cornered_box_glow = make_glow(m_cornered_box_glow);
const auto bar_glow = make_glow(m_bar_glow);
const auto label_glow = make_glow(m_label_glow);
const auto canvas_glow = make_glow(m_canvas_glow);
const BarPaint bar_color = m_gradient_bars ? BarPaint{BarGradient{red, green}} : BarPaint{m_bar_color};
const Bar bar{bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_offset, bar_glow};
const Paint box_fill = m_gradient_box_fill
? Paint{omath::hud::Gradient{box_gradient_top, box_gradient_top,
box_gradient_bottom, box_gradient_bottom}}
: Paint{m_box_fill};
const Paint centered_label_color =
m_gradient_label ? Paint{omath::hud::Gradient{m_label_gradient_left, m_label_gradient_right,
m_label_gradient_right, m_label_gradient_left,
m_animate_gradients, 4.f, 0.7f,
m_label_gradient_direction}}
: Paint{omath::Color::from_rgba(255, 255, 255, 255)};
auto outline_helper = [](const bool is_outline) -> Outlined
{
return is_outline ? Outlined::On : Outlined::Off;
};
omath::hud::EntityOverlay({m_entity_x, m_entity_top_y}, {m_entity_x, m_entity_bottom_y}, m_entity_aspect,
std::make_shared<omath::hud::ImguiHudRenderer>())
.contents(
when(canvas_glow.has_value(), CanvasGlow{canvas_glow.value_or(Glow{m_canvas_glow.color}),
m_canvas_glow_layers, m_canvas_glow_rounding}),
// ── Boxes ────────────────────────────────────────────────────
when(m_show_box, Box{m_box_color, box_fill, m_box_outline, m_box_thickness, box_glow}),
when(m_show_cornered_box,
CorneredBox{omath::Color::from_rgba(255, 0, 255, 255), m_box_fill, m_box_outline,
m_corner_ratio, m_box_thickness, cornered_box_glow}),
when(m_show_dashed_box, DashedBox{m_dash_color, m_dash_len, m_dash_gap, m_dash_thickness}),
RightSide{
when(m_show_right_bar, bar),
when(m_show_right_dashed_bar, dbar),
when(m_show_right_labels, Label{{0.f, 1.f, 0.f, 1.f},
m_label_offset,
outline_helper(m_outlined),
"Health: 100/100"}),
when(m_show_right_labels, Label{{1.f, 0.f, 0.f, 1.f},
m_label_offset,
outline_helper(m_outlined),
"Shield: 125/125"}),
when(m_show_right_labels, Label{{1.f, 0.f, 1.f, 1.f},
m_label_offset,
outline_helper(m_outlined),
"*LOCKED*"}),
SpaceVertical{10},
when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio,
m_ring_thickness, m_ring_offset}),
},
LeftSide{
when(m_show_left_bar, bar),
when(m_show_left_dashed_bar, dbar),
when(m_show_left_labels,
Label{omath::Color::from_rgba(255, 128, 0, 255), m_label_offset,
outline_helper(m_outlined), "Armor: 75"}),
when(m_show_left_labels,
Label{omath::Color::from_rgba(0, 200, 255, 255), m_label_offset,
outline_helper(m_outlined), "Level: 42"}),
},
TopSide{
when(m_show_top_bar, bar),
when(m_show_top_dashed_bar, dbar),
when(m_show_centered_top,
Centered{Label{omath::Color::from_rgba(0, 255, 255, 255), m_label_offset,
outline_helper(m_outlined), "*VISIBLE*"}}),
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 255, 0, 255), m_label_offset,
outline_helper(m_outlined), "*SCOPED*"}),
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 0, 0, 255), m_label_offset,
outline_helper(m_outlined), "*BLEEDING*"}),
},
BottomSide{
when(m_show_bottom_bar, bar),
when(m_show_bottom_dashed_bar, dbar),
when(m_show_centered_bottom,
Centered{Label{centered_label_color, m_label_offset, outline_helper(m_outlined),
"Gradient PlayerName", label_glow}}),
when(m_show_bottom_labels, Label{omath::Color::from_rgba(200, 200, 0, 255),
m_label_offset, outline_helper(m_outlined), "42m"}),
},
when(m_show_aim, AimDot{{m_entity_x, m_entity_top_y + 40.f}, m_aim_color, m_aim_radius}),
when(m_show_scan, ScanMarker{m_scan_color, m_scan_outline, m_scan_outline_thickness}),
when(m_show_skeleton, Skeleton{skeleton_bones, m_skel_color, m_skel_thickness}),
when(m_show_proj, ProjectileAim{{m_proj_pos_x, m_proj_pos_y},
m_proj_color,
m_proj_size,
m_proj_line_width,
static_cast<ProjectileAim::Figure>(m_proj_figure)}),
when(m_show_snap, SnapLine{{vp->Size.x / 2.f, vp->Size.y}, m_snap_color, m_snap_width}));
}
void MainWindow::present()
{
int w, h;
glfwGetFramebufferSize(m_window, &w, &h);
glViewport(0, 0, w, h);
glClearColor(0.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(m_window);
}
} // namespace imgui_desktop::gui
+119
View File
@@ -0,0 +1,119 @@
//
// Created by Orange on 11/11/2024.
//
#pragma once
#include <omath/hud/entity_overlay.hpp>
#include <omath/utility/color.hpp>
#include <string_view>
struct GLFWwindow;
namespace imgui_desktop::gui
{
class MainWindow
{
public:
MainWindow(const std::string_view& caption, int width, int height);
void Run();
private:
struct GlowSettings
{
omath::Color color{0.f, 0.7f, 1.f, 0.8f};
float radius = 4.f;
float intensity = 0.8f;
bool enabled = false;
};
void draw_controls();
void draw_overlay();
void present();
GLFWwindow* m_window = nullptr;
static bool m_canMoveWindow;
bool m_opened = true;
// Entity
float m_entity_x = 550.f, m_entity_top_y = 150.f, m_entity_bottom_y = 450.f, m_entity_aspect = 4.f;
// Box
omath::Color m_box_color{1.f, 1.f, 1.f, 1.f};
omath::Color m_box_fill{0.f, 0.f, 0.f, 0.f};
omath::Color m_box_gradient_top{0.1f, 0.6f, 1.f, 0.55f};
omath::Color m_box_gradient_bottom{0.7f, 0.1f, 1.f, 0.15f};
omath::Color m_box_outline{0.f, 0.f, 0.f, 0.f};
float m_box_thickness = 1.f, m_corner_ratio = 0.2f;
bool m_gradient_box_fill = true;
bool m_animate_gradients = false;
bool m_show_box = true, m_show_cornered_box = true, m_show_dashed_box = false;
// Dashed box
omath::Color m_dash_color = omath::Color::from_rgba(255, 200, 0, 255);
float m_dash_len = 8.f, m_dash_gap = 5.f, m_dash_thickness = 1.f;
// Bars
omath::Color m_bar_color{0.f, 1.f, 0.f, 1.f};
omath::Color m_bar_bg_color{0.f, 0.f, 0.f, 0.5f};
omath::Color m_bar_outline_color{0.f, 0.f, 0.f, 1.f};
float m_bar_width = 4.f, m_bar_value = 0.75f, m_bar_offset = 5.f;
bool m_gradient_bars = true;
bool m_show_right_bar = true, m_show_left_bar = true;
bool m_show_top_bar = true, m_show_bottom_bar = true;
bool m_show_right_dashed_bar = false, m_show_left_dashed_bar = false;
bool m_show_top_dashed_bar = false, m_show_bottom_dashed_bar = false;
float m_bar_dash_len = 6.f, m_bar_dash_gap = 4.f;
// Labels
float m_label_offset = 3.f;
omath::Color m_label_gradient_left{0.f, 0.8f, 1.f, 1.f};
omath::Color m_label_gradient_right{1.f, 0.2f, 0.7f, 1.f};
bool m_outlined = true;
bool m_gradient_label = true;
omath::hud::GradientDirection m_label_gradient_direction = omath::hud::GradientDirection::RightToLeft;
bool m_show_right_labels = true, m_show_left_labels = true;
bool m_show_top_labels = true, m_show_bottom_labels = true;
bool m_show_centered_top = true, m_show_centered_bottom = true;
GlowSettings m_box_glow;
GlowSettings m_cornered_box_glow{{1.f, 0.f, 1.f, 0.8f}};
GlowSettings m_bar_glow{{1.f, 0.f, 0.f, 0.8f}};
GlowSettings m_label_glow;
GlowSettings m_canvas_glow{{1.f, 0.5f, 0.f, 0.8f}, 100.f};
int m_canvas_glow_layers = 64;
float m_canvas_glow_rounding = 40.f;
// Skeleton
omath::Color m_skel_color = omath::Color::from_rgba(255, 255, 255, 200);
float m_skel_thickness = 1.f;
bool m_show_skeleton = false;
// Progress ring
omath::Color m_ring_color = omath::Color::from_rgba(0, 200, 255, 255);
omath::Color m_ring_bg{0.3f, 0.3f, 0.3f, 0.5f};
float m_ring_radius = 10.f, m_ring_ratio = 0.65f, m_ring_thickness = 2.5f, m_ring_offset = 5.f;
bool m_show_ring = false;
// Scan marker
omath::Color m_scan_color = omath::Color::from_rgba(255, 200, 0, 150);
omath::Color m_scan_outline = omath::Color::from_rgba(255, 200, 0, 255);
float m_scan_outline_thickness = 2.f;
bool m_show_scan = false;
// Aim dot
omath::Color m_aim_color = omath::Color::from_rgba(255, 0, 0, 255);
float m_aim_radius = 3.f;
bool m_show_aim = false;
// Snap line
omath::Color m_snap_color = omath::Color::from_rgba(255, 50, 50, 255);
float m_snap_width = 1.5f;
bool m_show_snap = true;
// Projectile aim
omath::Color m_proj_color = omath::Color::from_rgba(255, 50, 50, 255);
float m_proj_size = 10.f;
float m_proj_line_width = 1.5f;
float m_proj_pos_x = 300.f, m_proj_pos_y = 30.f;
int m_proj_figure = 1; // 0=circle, 1=square
bool m_show_proj = true;
};
} // namespace imgui_desktop::gui
+8
View File
@@ -0,0 +1,8 @@
//
// Created by orange on 13.03.2026.
//
#include "gui/main_window.hpp"
int main()
{
imgui_desktop::gui::MainWindow("omath::hud", 800, 600).Run();
}
@@ -0,0 +1,13 @@
project(example_opengl_hook)
add_library(${PROJECT_NAME} MODULE dllmain.cpp)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 23
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>"
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(imgui CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE omath::omath imgui::imgui opengl32 gdi32)
+116
View File
@@ -0,0 +1,116 @@
#include "omath/hooks/hooks_manager.hpp"
#include <Windows.h>
#include <chrono>
#include <imgui.h>
#include <imgui_impl_opengl3.h>
#include <imgui_impl_win32.h>
#include <optional>
#include <thread>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND, UINT, WPARAM, LPARAM);
namespace
{
bool g_initialized = false;
bool g_init_attempted = false;
bool g_show_menu = true;
constexpr auto g_module_wait_delay = std::chrono::milliseconds{100};
void init(HDC hdc)
{
g_init_attempted = true;
const HWND hwnd = WindowFromDC(hdc);
if (!hwnd)
return;
ImGui::CreateContext();
ImGui::StyleColorsDark();
ImGui::GetIO().IniFilename = nullptr;
ImGui::GetIO().LogFilename = nullptr;
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplOpenGL3_Init();
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_wnd_proc(
[](HWND h, UINT msg, WPARAM wp, LPARAM lp) -> std::optional<LRESULT>
{
if (!g_show_menu)
return std::nullopt;
if (ImGui_ImplWin32_WndProcHandler(h, msg, wp, lp))
return 0;
return std::nullopt;
});
(void)mgr.hook_wnd_proc(hwnd);
g_initialized = true;
}
void on_swap_buffers(HDC hdc)
{
if (!g_initialized)
{
if (!g_init_attempted)
init(hdc);
return;
}
if (GetAsyncKeyState(VK_INSERT) & 1)
g_show_menu = !g_show_menu;
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
if (g_show_menu)
{
ImGui::SetNextWindowSize({300.f, 100.f}, ImGuiCond_Once);
ImGui::SetNextWindowPos({10.f, 10.f}, ImGuiCond_Once);
ImGui::Begin("omath | OpenGL hook");
ImGui::Text("Hook active");
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::Text("INSERT toggles this window");
ImGui::End();
}
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
void hook_when_opengl_is_loaded()
{
while (!GetModuleHandle("opengl32.dll"))
std::this_thread::sleep_for(g_module_wait_delay);
auto& mgr = omath::hooks::HooksManager::get();
mgr.set_on_opengl_swap_buffers(on_swap_buffers);
(void)mgr.hook_opengl();
}
} // namespace
BOOL WINAPI DllMain(HINSTANCE h_instance, DWORD reason, LPVOID)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(h_instance);
std::thread{hook_when_opengl_is_loaded}.detach();
}
else if (reason == DLL_PROCESS_DETACH)
{
auto& mgr = omath::hooks::HooksManager::get();
mgr.unhook_wnd_proc();
mgr.unhook_opengl();
if (g_initialized)
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
}
return true;
}
+87
View File
@@ -0,0 +1,87 @@
//
// Created by Vladislav on 24.03.2026.
//
#pragma once
#include "omath/linear_algebra/vector3.hpp"
#include <array>
namespace omath::primitives
{
enum class UpAxis
{
X,
Y,
Z
};
template<class Type, UpAxis Up = UpAxis::Y>
struct Aabb final
{
Vector3<Type> min;
Vector3<Type> max;
[[nodiscard]]
static consteval UpAxis get_up_axis()
{
return Up;
}
[[nodiscard]]
constexpr Vector3<Type> center() const noexcept
{
return (min + max) / static_cast<Type>(2);
}
[[nodiscard]]
constexpr Vector3<Type> extents() const noexcept
{
return (max - min) / static_cast<Type>(2);
}
[[nodiscard]]
constexpr Vector3<Type> top() const noexcept
{
const auto aabb_center = center();
if constexpr (Up == UpAxis::Z)
return {aabb_center.x, aabb_center.y, max.z};
else if constexpr (Up == UpAxis::X)
return {max.x, aabb_center.y, aabb_center.z};
else if constexpr (Up == UpAxis::Y)
return {aabb_center.x, max.y, aabb_center.z};
else
std::unreachable();
}
[[nodiscard]]
constexpr Vector3<Type> bottom() const noexcept
{
const auto aabb_center = center();
if constexpr (Up == UpAxis::Z)
return {aabb_center.x, aabb_center.y, min.z};
else if constexpr (Up == UpAxis::X)
return {min.x, aabb_center.y, aabb_center.z};
else if constexpr (Up == UpAxis::Y)
return {aabb_center.x, min.y, aabb_center.z};
else
std::unreachable();
}
[[nodiscard]]
constexpr std::array<Vector3<Type>, 8> vertices() const noexcept
{
return {
Vector3<Type>{min.x, min.y, min.z}, Vector3<Type>{max.x, min.y, min.z},
Vector3<Type>{min.x, max.y, min.z}, Vector3<Type>{max.x, max.y, min.z},
Vector3<Type>{min.x, min.y, max.z}, Vector3<Type>{max.x, min.y, max.z},
Vector3<Type>{min.x, max.y, max.z}, Vector3<Type>{max.x, max.y, max.z},
};
}
[[nodiscard]]
constexpr bool is_collide(const Aabb& other) const noexcept
{
return min.x <= other.max.x && max.x >= other.min.x && min.y <= other.max.y && max.y >= other.min.y
&& min.z <= other.max.z && max.z >= other.min.z;
}
};
} // namespace omath::primitives
+37
View File
@@ -0,0 +1,37 @@
//
// Created by Vladislav on 07.05.2026.
//
#pragma once
#include "omath/linear_algebra/vector3.hpp"
#include <array>
#include <type_traits>
namespace omath::primitives
{
// Oriented bounding box: a rectangular cuboid defined by a center, three
// orthonormal local axes, and the half-size along each of those axes.
template<class Type>
requires std::is_floating_point_v<Type>
struct Obb final
{
Vector3<Type> center;
Vector3<Type> axis_x;
Vector3<Type> axis_y;
Vector3<Type> axis_z;
Vector3<Type> half_extents;
[[nodiscard]]
constexpr std::array<Vector3<Type>, 8> vertices() const noexcept
{
const auto ex = axis_x * half_extents.x;
const auto ey = axis_y * half_extents.y;
const auto ez = axis_z * half_extents.z;
return {
center - ex - ey - ez, center + ex - ey - ez, center - ex + ey - ez, center + ex + ey - ez,
center - ex - ey + ez, center + ex - ey + ez, center - ex + ey + ez, center + ex + ey + ez,
};
}
};
} // namespace omath::primitives
+42
View File
@@ -0,0 +1,42 @@
//
// Created by orange on 7/1/2026.
//
#pragma once
#include "omath/linear_algebra/vector3.hpp"
#include <type_traits>
namespace omath::algorithm
{
template<class Camera, class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr Vector2<float> world_to_radar(const Camera& camera, const Vector3<FloatingType>& position, const FloatingType scale)
{
const auto look_at_angles = camera.calc_look_at_angles(position);
const auto current_angles = camera.get_view_angles();
if consteval
{
const auto right_yaw = current_angles.yaw
- camera.calc_look_at_angles(camera.get_origin() + camera.get_abs_right()).yaw
- decltype(current_angles.yaw)::from_degrees(90);
const auto sign = right_yaw.cos() < 0 ? -1.f : 1.f;
const auto yaw = current_angles.yaw - look_at_angles.yaw - decltype(current_angles.yaw)::from_degrees(90);
return omath::Vector2<float>(static_cast<float>(yaw.cos()) * sign, static_cast<float>(yaw.sin()))
* (camera.get_origin().distance_to(position) * scale);
}
static const auto sign = [&camera, &current_angles]
{
const auto right_yaw = current_angles.yaw
- camera.calc_look_at_angles(camera.get_origin() + camera.get_abs_right()).yaw
- decltype(current_angles.yaw)::from_degrees(90);
return right_yaw.cos() < 0 ? -1.f : 1.f;
}();
const auto yaw = current_angles.yaw - look_at_angles.yaw - decltype(current_angles.yaw)::from_degrees(90);
return omath::Vector2<float>(static_cast<float>(yaw.cos()) * sign, static_cast<float>(yaw.sin()))
* (camera.get_origin().distance_to(position) * scale);
}
} // namespace omath::algorithm
+98
View File
@@ -0,0 +1,98 @@
//
// Created by Vladislav on 19.03.2026.
//
#pragma once
#include "omath/linear_algebra/vector3.hpp"
#include <functional>
#include <iterator>
#include <optional>
#include <ranges>
namespace omath::algorithm
{
template<class CameraType, std::input_or_output_iterator IteratorType, class FilterT>
requires std::is_invocable_r_v<bool, std::function<FilterT>, std::iter_reference_t<IteratorType>>
[[nodiscard]]
IteratorType get_closest_target_by_fov(const IteratorType& begin, const IteratorType& end, const CameraType& camera,
auto get_position,
const std::optional<std::function<FilterT>>& filter_func = std::nullopt)
{
auto best_target = end;
const auto& camera_angles = camera.get_view_angles();
const Vector2<float> camera_angles_vec = {camera_angles.pitch.as_degrees(), camera_angles.yaw.as_degrees()};
for (auto current = begin; current != end; current = std::next(current))
{
if (filter_func && !filter_func.value()(*current))
continue;
if (best_target == end)
{
best_target = current;
continue;
}
const auto current_target_angles = camera.calc_look_at_angles(get_position(*current));
const auto best_target_angles = camera.calc_look_at_angles(get_position(*best_target));
const auto current_target_distance = camera_angles_vec.distance_to(current_target_angles.as_vector3());
const auto best_target_distance = camera_angles.as_vector3().distance_to(best_target_angles.as_vector3());
if (current_target_distance < best_target_distance)
best_target = current;
}
return best_target;
}
template<class CameraType, std::ranges::range RangeType, class FilterT>
requires std::is_invocable_r_v<bool, std::function<FilterT>,
std::ranges::range_reference_t<const RangeType>>
[[nodiscard]]
auto get_closest_target_by_fov(const RangeType& range, const CameraType& camera,
auto get_position,
const std::optional<std::function<FilterT>>& filter_func = std::nullopt)
{
return get_closest_target_by_fov<CameraType, decltype(std::ranges::begin(range)), FilterT>(
std::ranges::begin(range), std::ranges::end(range), camera, get_position, filter_func);
}
// ── By world-space distance ───────────────────────────────────────────────
template<std::input_or_output_iterator IteratorType, class FilterT>
requires std::is_invocable_r_v<bool, std::function<FilterT>, std::iter_reference_t<IteratorType>>
[[nodiscard]]
IteratorType get_closest_target_by_distance(const IteratorType& begin, const IteratorType& end,
const Vector3<float>& origin, auto get_position,
const std::optional<std::function<FilterT>>& filter_func = std::nullopt)
{
auto best_target = end;
for (auto current = begin; current != end; current = std::next(current))
{
if (filter_func && !filter_func.value()(*current))
continue;
if (best_target == end)
{
best_target = current;
continue;
}
if (origin.distance_to(get_position(*current)) < origin.distance_to(get_position(*best_target)))
best_target = current;
}
return best_target;
}
template<std::ranges::range RangeType, class FilterT>
requires std::is_invocable_r_v<bool, std::function<FilterT>,
std::ranges::range_reference_t<const RangeType>>
[[nodiscard]]
auto get_closest_target_by_distance(const RangeType& range, const Vector3<float>& origin,
auto get_position,
const std::optional<std::function<FilterT>>& filter_func = std::nullopt)
{
return get_closest_target_by_distance<decltype(std::ranges::begin(range)), FilterT>(
std::ranges::begin(range), std::ranges::end(range), origin, get_position, filter_func);
}
} // namespace omath::algorithm
+82 -81
View File
@@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <memory_resource> #include <memory_resource>
#include <queue> #include <queue>
#include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -48,7 +49,7 @@ namespace omath::collision
struct Params final struct Params final
{ {
int max_iterations{64}; int max_iterations{64};
FloatingType tolerance{1e-4}; // absolute tolerance on distance growth FloatingType tolerance{1e-4f}; // absolute tolerance on distance growth
}; };
// Precondition: simplex.size()==4 and contains the origin. // Precondition: simplex.size()==4 and contains the origin.
[[nodiscard]] [[nodiscard]]
@@ -56,83 +57,76 @@ namespace omath::collision
const Simplex<VectorType>& simplex, const Params params = {}, const Simplex<VectorType>& simplex, const Params params = {},
std::pmr::memory_resource& mem_resource = *std::pmr::get_default_resource()) 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); 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); std::pmr::vector<Face> faces = create_initial_tetra_faces(mem_resource, vertexes);
auto heap = rebuild_heap(faces, mem_resource); // Build initial min-heap by distance.
Heap heap = rebuild_heap(faces, mem_resource);
Result out{}; Result out{};
// Hoisted outside the loop to reuse bucket allocation across iterations.
// Initial bucket count 16 covers a typical horizon without rehashing.
BoundaryMap boundary{16, &mem_resource};
for (int it = 0; it < params.max_iterations; ++it) for (int it = 0; it < params.max_iterations; ++it)
{ {
// If heap might be stale after face edits, rebuild lazily. // Lazily discard stale (deleted or index-mismatched) heap entries.
if (heap.empty()) discard_stale_heap_entries(faces, heap);
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()) if (heap.empty())
break; break;
// FIXME: STORE REF VALUE, DO NOT USE
// AFTER IF STATEMENT BLOCK
const Face& face = faces[heap.top().idx]; 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 VectorType p = support_point(a, b, face.n);
const auto p_dist = face.n.dot(p); const auto p_dist = face.n.dot(p);
// Converged if we cant push the face closer than tolerance // Converged: new support can't push the face closer than tolerance.
if (p_dist - face.d <= params.tolerance) if (p_dist - face.d <= params.tolerance)
{ {
out.normal = face.n; out.normal = face.n;
out.depth = face.d; // along unit normal out.depth = face.d;
out.iterations = it + 1; out.iterations = it + 1;
out.num_vertices = static_cast<int>(vertexes.size()); out.num_vertices = static_cast<int>(vertexes.size());
out.num_faces = static_cast<int>(faces.size()); out.num_faces = static_cast<int>(faces.size());
out.penetration_vector = out.normal * out.depth; out.penetration_vector = out.normal * out.depth;
return out; return out;
} }
// Add new vertex
const int new_idx = static_cast<int>(vertexes.size()); const int new_idx = static_cast<int>(vertexes.size());
vertexes.emplace_back(p); vertexes.emplace_back(p);
const auto [to_delete, boundary] = mark_visible_and_collect_horizon(faces, p); // Tombstone visible faces and collect the horizon boundary.
// This avoids copying the faces array (O(n)) each iteration.
tombstone_visible_faces(faces, boundary, p);
erase_marked(faces, to_delete); // Stitch new faces around the horizon and push them directly onto the
// heap — no full O(n log n) rebuild needed.
// Stitch new faces around the horizon for (const auto& [key, e] : boundary)
for (const auto& e : boundary) {
const int fi = static_cast<int>(faces.size());
faces.emplace_back(make_face(vertexes, e.a, e.b, new_idx)); faces.emplace_back(make_face(vertexes, e.a, e.b, new_idx));
heap.emplace(faces.back().d, fi);
// Rebuild heap after topology change }
heap = rebuild_heap(faces, mem_resource);
if (!std::isfinite(vertexes.back().dot(vertexes.back()))) if (!std::isfinite(vertexes.back().dot(vertexes.back())))
break; // safety break; // safety
out.iterations = it + 1; out.iterations = it + 1;
} }
if (faces.empty()) // Find the best surviving (non-deleted) face.
const Face* best = find_best_surviving_face(faces);
if (!best)
return std::nullopt; return std::nullopt;
const auto best = *std::ranges::min_element(faces, [](const auto& first, const auto& second) out.normal = best->n;
{ return first.d < second.d; }); out.depth = best->d;
out.normal = best.n;
out.depth = best.d;
out.num_vertices = static_cast<int>(vertexes.size()); out.num_vertices = static_cast<int>(vertexes.size());
out.num_faces = static_cast<int>(faces.size()); out.num_faces = static_cast<int>(faces.size());
out.penetration_vector = out.normal * out.depth; out.penetration_vector = out.normal * out.depth;
return out; return out;
} }
@@ -141,7 +135,8 @@ namespace omath::collision
{ {
int i0, i1, i2; int i0, i1, i2;
VectorType n; // unit outward normal VectorType n; // unit outward normal
FloatingType d; // n · v0 (>=0 ideally because origin is inside) FloatingType d; // n · v0 (>= 0 ideally because origin is inside)
bool deleted{false}; // tombstone flag — avoids O(n) compaction per iteration
}; };
struct Edge final struct Edge final
@@ -154,6 +149,7 @@ namespace omath::collision
FloatingType d; FloatingType d;
int idx; int idx;
}; };
struct HeapCmp final struct HeapCmp final
{ {
[[nodiscard]] [[nodiscard]]
@@ -165,35 +161,44 @@ namespace omath::collision
using Heap = std::priority_queue<HeapItem, std::pmr::vector<HeapItem>, HeapCmp>; using Heap = std::priority_queue<HeapItem, std::pmr::vector<HeapItem>, HeapCmp>;
// Horizon boundary: maps packed(a,b) → Edge.
// Opposite edges cancel in O(1) via hash lookup instead of O(h) linear scan.
using BoundaryMap = std::pmr::unordered_map<std::int64_t, Edge>;
[[nodiscard]]
static constexpr std::int64_t pack_edge(const int a, const int b) noexcept
{
return (static_cast<std::int64_t>(a) << 32) | static_cast<std::uint32_t>(b);
}
[[nodiscard]] [[nodiscard]]
static Heap rebuild_heap(const std::pmr::vector<Face>& faces, auto& memory_resource) static Heap rebuild_heap(const std::pmr::vector<Face>& faces, auto& memory_resource)
{ {
std::pmr::vector<HeapItem> storage{&memory_resource}; std::pmr::vector<HeapItem> storage{&memory_resource};
storage.reserve(faces.size()); // optional but recommended storage.reserve(faces.size());
Heap h{HeapCmp{}, std::move(storage)}; Heap h{HeapCmp{}, std::move(storage)};
for (int i = 0; i < static_cast<int>(faces.size()); ++i) for (int i = 0; i < static_cast<int>(faces.size()); ++i)
if (!faces[i].deleted)
h.emplace(faces[i].d, i); h.emplace(faces[i].d, i);
return h;
return h; // allocator is preserved
} }
[[nodiscard]] [[nodiscard]]
static bool visible_from(const Face& f, const VectorType& p) static bool visible_from(const Face& f, const VectorType& p)
{ {
// positive if p is in front of the face
return f.n.dot(p) - f.d > static_cast<FloatingType>(1e-7); 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) static void add_edge_boundary(BoundaryMap& boundary, int a, int b)
{ {
// Keep edges that appear only once; erase if opposite already present // O(1) cancel: if the opposite edge (b→a) is already in the map it is an
auto itb = std::ranges::find_if(boundary, [&](const Edge& e) { return e.a == b && e.b == a; }); // internal edge shared by two visible faces and must be removed.
if (itb != boundary.end()) // Otherwise this is a horizon edge and we insert it.
boundary.erase(itb); // internal edge cancels out const std::int64_t rev = pack_edge(b, a);
if (const auto it = boundary.find(rev); it != boundary.end())
boundary.erase(it);
else else
boundary.emplace_back(a, b); // horizon edge (directed) boundary.emplace(pack_edge(a, b), Edge{a, b});
} }
[[nodiscard]] [[nodiscard]]
@@ -204,9 +209,7 @@ namespace omath::collision
const VectorType& a2 = vertexes[i2]; const VectorType& a2 = vertexes[i2];
VectorType n = (a1 - a0).cross(a2 - a0); VectorType n = (a1 - a0).cross(a2 - a0);
if (n.dot(n) <= static_cast<FloatingType>(1e-30)) if (n.dot(n) <= static_cast<FloatingType>(1e-30))
{
n = any_perp_vec(a1 - a0); // degenerate guard n = any_perp_vec(a1 - a0); // degenerate guard
}
// Ensure normal points outward (away from origin): require n·a0 >= 0 // Ensure normal points outward (away from origin): require n·a0 >= 0
if (n.dot(a0) < static_cast<FloatingType>(0.0)) if (n.dot(a0) < static_cast<FloatingType>(0.0))
{ {
@@ -243,6 +246,7 @@ namespace omath::collision
return d; return d;
return V{1, 0, 0}; return V{1, 0, 0};
} }
[[nodiscard]] [[nodiscard]]
static std::pmr::vector<Face> create_initial_tetra_faces(std::pmr::memory_resource& mem_resource, static std::pmr::vector<Face> create_initial_tetra_faces(std::pmr::memory_resource& mem_resource,
const std::pmr::vector<VectorType>& vertexes) const std::pmr::vector<VectorType>& vertexes)
@@ -262,48 +266,45 @@ namespace omath::collision
{ {
std::pmr::vector<VectorType> vertexes{&mem_resource}; std::pmr::vector<VectorType> vertexes{&mem_resource};
vertexes.reserve(simplex.size()); vertexes.reserve(simplex.size());
for (std::size_t i = 0; i < simplex.size(); ++i) for (std::size_t i = 0; i < simplex.size(); ++i)
vertexes.emplace_back(simplex[i]); vertexes.emplace_back(simplex[i]);
return vertexes; return vertexes;
} }
static void erase_marked(std::pmr::vector<Face>& faces, const std::pmr::vector<bool>& to_delete)
static const Face* find_best_surviving_face(const std::pmr::vector<Face>& faces)
{ {
auto* mr = faces.get_allocator().resource(); // keep same resource const Face* best = nullptr;
std::pmr::vector<Face> kept{mr}; for (const auto& f : faces)
kept.reserve(faces.size()); if (!f.deleted && (best == nullptr || f.d < best->d))
best = &f;
for (std::size_t i = 0; i < faces.size(); ++i) return best;
if (!to_delete[i])
kept.emplace_back(faces[i]);
faces.swap(kept);
} }
struct Horizon static void tombstone_visible_faces(std::pmr::vector<Face>& faces, BoundaryMap& boundary,
const VectorType& p)
{ {
std::pmr::vector<bool> to_delete; boundary.clear();
std::pmr::vector<Edge> boundary; for (auto& f : faces)
};
static Horizon mark_visible_and_collect_horizon(const std::pmr::vector<Face>& faces, const VectorType& p)
{ {
auto* mr = faces.get_allocator().resource(); if (!f.deleted && visible_from(f, p))
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]; f.deleted = true;
horizon.to_delete[i] = true; add_edge_boundary(boundary, f.i0, f.i1);
add_edge_boundary(horizon.boundary, rf.i0, rf.i1); add_edge_boundary(boundary, f.i1, f.i2);
add_edge_boundary(horizon.boundary, rf.i1, rf.i2); add_edge_boundary(boundary, f.i2, f.i0);
add_edge_boundary(horizon.boundary, rf.i2, rf.i0); }
}
} }
return horizon; static void discard_stale_heap_entries(const std::pmr::vector<Face>& faces,
std::priority_queue<HeapItem, std::pmr::vector<HeapItem>, HeapCmp>& heap)
{
while (!heap.empty())
{
const auto& top = heap.top();
if (!faces[top.idx].deleted && faces[top.idx].d == top.d)
break;
heap.pop();
}
} }
}; };
} // namespace omath::collision } // namespace omath::collision
+14 -1
View File
@@ -43,7 +43,20 @@ namespace omath::collision
const ColliderInterfaceType& collider_b, const ColliderInterfaceType& collider_b,
const GjkSettings& settings = {}) const GjkSettings& settings = {})
{ {
auto support = find_support_vertex(collider_a, collider_b, VectorType{1, 0, 0}); // Use centroid difference as initial direction — greatly reduces iterations for separated shapes.
VectorType initial_dir;
if constexpr (requires { collider_b.get_origin() - collider_a.get_origin(); })
{
initial_dir = collider_b.get_origin() - collider_a.get_origin();
if (initial_dir.dot(initial_dir) < settings.epsilon * settings.epsilon)
initial_dir = VectorType{1, 0, 0};
}
else
{
initial_dir = VectorType{1, 0, 0};
}
auto support = find_support_vertex(collider_a, collider_b, initial_dir);
Simplex<VectorType> simplex; Simplex<VectorType> simplex;
simplex.push_front(support); simplex.push_front(support);
+107
View File
@@ -3,6 +3,8 @@
// //
#pragma once #pragma once
#include "omath/3d_primitives/aabb.hpp"
#include "omath/3d_primitives/obb.hpp"
#include "omath/linear_algebra/triangle.hpp" #include "omath/linear_algebra/triangle.hpp"
#include "omath/linear_algebra/vector3.hpp" #include "omath/linear_algebra/vector3.hpp"
@@ -34,6 +36,8 @@ namespace omath::collision
class LineTracer final class LineTracer final
{ {
using TriangleType = Triangle<typename RayType::VectorType>; using TriangleType = Triangle<typename RayType::VectorType>;
using AABBType = primitives::Aabb<typename RayType::VectorType::ContainedType>;
using OBBType = primitives::Obb<typename RayType::VectorType::ContainedType>;
public: public:
LineTracer() = delete; LineTracer() = delete;
@@ -87,6 +91,109 @@ namespace omath::collision
return ray.start + ray_dir * t_hit; return ray.start + ray_dir * t_hit;
} }
// Slab method ray-AABB intersection
// Returns the hit point on the AABB surface, or ray.end if no intersection
[[nodiscard]]
constexpr static auto get_ray_hit_point(const RayType& ray, const AABBType& aabb) noexcept
{
using T = typename RayType::VectorType::ContainedType;
const auto dir = ray.direction_vector();
auto t_min = -std::numeric_limits<T>::infinity();
auto t_max = std::numeric_limits<T>::infinity();
const auto process_axis = [&](const T& d, const T& origin, const T& box_min,
const T& box_max) -> bool
{
constexpr T k_epsilon = std::numeric_limits<T>::epsilon();
if (std::abs(d) < k_epsilon)
return origin >= box_min && origin <= box_max;
const T inv = T(1) / d;
T t0 = (box_min - origin) * inv;
T t1 = (box_max - origin) * inv;
if (t0 > t1)
std::swap(t0, t1);
t_min = std::max(t_min, t0);
t_max = std::min(t_max, t1);
return t_min <= t_max;
};
if (!process_axis(dir.x, ray.start.x, aabb.min.x, aabb.max.x))
return ray.end;
if (!process_axis(dir.y, ray.start.y, aabb.min.y, aabb.max.y))
return ray.end;
if (!process_axis(dir.z, ray.start.z, aabb.min.z, aabb.max.z))
return ray.end;
// t_hit: use entry point if in front of origin, otherwise 0 (started inside)
const T t_hit = std::max(T(0), t_min);
if (t_max < T(0))
return ray.end; // box entirely behind origin
if (!ray.infinite_length && t_hit > T(1))
return ray.end; // box beyond ray endpoint
return ray.start + dir * t_hit;
}
// Slab method ray-OBB intersection. Project the ray into the OBB's local frame
// (axes are orthonormal, so the inverse rotation is just a transpose / dot products),
// then run the standard slab test against the local box [-half_extents, +half_extents].
// The ray parameter t is invariant under rigid transform, so the hit point is recovered
// in world space as ray.start + dir * t_hit.
[[nodiscard]]
constexpr static auto get_ray_hit_point(const RayType& ray, const OBBType& obb) noexcept
{
using T = typename RayType::VectorType::ContainedType;
const auto offset = ray.start - obb.center;
const auto dir = ray.direction_vector();
const T local_start[3] = {offset.dot(obb.axis_x), offset.dot(obb.axis_y), offset.dot(obb.axis_z)};
const T local_dir[3] = {dir.dot(obb.axis_x), dir.dot(obb.axis_y), dir.dot(obb.axis_z)};
const T half[3] = {obb.half_extents.x, obb.half_extents.y, obb.half_extents.z};
auto t_min = -std::numeric_limits<T>::infinity();
auto t_max = std::numeric_limits<T>::infinity();
const auto process_axis = [&](const T& d, const T& origin, const T& h) -> bool
{
constexpr T k_epsilon = std::numeric_limits<T>::epsilon();
if (std::abs(d) < k_epsilon)
return origin >= -h && origin <= h;
const T inv = T(1) / d;
T t0 = (-h - origin) * inv;
T t1 = (h - origin) * inv;
if (t0 > t1)
std::swap(t0, t1);
t_min = std::max(t_min, t0);
t_max = std::min(t_max, t1);
return t_min <= t_max;
};
if (!process_axis(local_dir[0], local_start[0], half[0]))
return ray.end;
if (!process_axis(local_dir[1], local_start[1], half[1]))
return ray.end;
if (!process_axis(local_dir[2], local_start[2], half[2]))
return ray.end;
const T t_hit = std::max(T(0), t_min);
if (t_max < T(0))
return ray.end; // box entirely behind origin
if (!ray.infinite_length && t_hit > T(1))
return ray.end; // box beyond ray endpoint
return ray.start + dir * t_hit;
}
template<class MeshType> template<class MeshType>
[[nodiscard]] [[nodiscard]]
constexpr static auto get_ray_hit_point(const RayType& ray, const MeshType& mesh) noexcept constexpr static auto get_ray_hit_point(const RayType& ray, const MeshType& mesh) noexcept
+10
View File
@@ -42,6 +42,16 @@ namespace omath::collision
m_mesh.set_origin(new_origin); m_mesh.set_origin(new_origin);
} }
[[nodiscard]]
const MeshType& get_mesh() const
{
return m_mesh;
}
[[nodiscard]]
MeshType& get_mesh()
{
return m_mesh;
}
private: private:
[[nodiscard]] [[nodiscard]]
const VertexType& find_furthest_vertex(const VectorType& direction) const const VertexType& find_furthest_vertex(const VectorType& direction) const
+1 -1
View File
@@ -9,5 +9,5 @@
namespace omath::cry_engine namespace omath::cry_engine
{ {
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>; using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::cry_engine } // namespace omath::cry_engine
@@ -15,7 +15,7 @@ namespace omath::cry_engine
constexpr Vector3<float> k_abs_forward = {0, 1, 0}; constexpr Vector3<float> k_abs_forward = {0, 1, 0};
using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; using Mat3X3 = Mat<3, 3, float, MatStoreType::ROW_MAJOR>;
using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>; using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
using PitchAngle = Angle<float, -90.f, 90.f, AngleFlags::Clamped>; using PitchAngle = Angle<float, -90.f, 90.f, AngleFlags::Clamped>;
using YawAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>; using YawAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
+78 -17
View File
@@ -7,26 +7,87 @@
namespace omath::cry_engine namespace omath::cry_engine
{ {
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept; constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z<float, MatStoreType::ROW_MAJOR>(angles.yaw)
* mat_rotation_axis_y<float, MatStoreType::ROW_MAJOR>(angles.roll)
* mat_rotation_axis_x<float, MatStoreType::ROW_MAJOR>(angles.pitch);
}
[[nodiscard]] [[nodiscard("forward vector result should not be discarded")]]
Vector3<float> right_vector(const ViewAngles& angles) noexcept; constexpr Vector3<float> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Vector3<float> up_vector(const ViewAngles& angles) noexcept; }
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; [[nodiscard("right vector result should not be discarded")]]
constexpr Vector3<float> right_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; }
[[nodiscard]] [[nodiscard("up vector result should not be discarded")]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; constexpr Vector3<float> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("view matrix result should not be discarded")]]
constexpr Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return mat_camera_view<float, MatStoreType::ROW_MAJOR>(forward_vector(angles), right_vector(angles),
up_vector(angles), cam_origin);
}
[[nodiscard("origin result should not be discarded")]]
constexpr Vector3<float> extract_origin(const Mat4X4& mat) noexcept
{
return mat_extract_origin(mat);
}
[[nodiscard("scale result should not be discarded")]]
constexpr Vector3<float> extract_scale(const Mat4X4& mat) noexcept
{
return mat_extract_scale(mat);
}
[[nodiscard("rotation angles result should not be discarded")]]
constexpr ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
{
const auto angles = mat_extract_rotation_zyx(mat);
return {
PitchAngle::from_degrees(angles.x),
YawAngle::from_degrees(angles.z),
RollAngle::from_degrees(angles.y),
};
}
[[nodiscard("perspective projection matrix result should not be discarded")]]
constexpr Mat4X4
calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near_plane,
const float far_plane,
const NDCDepthRange ndc_depth_range = NDCDepthRange::ZERO_TO_ONE) noexcept
{
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
std::unreachable();
}
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("centimeters value should not be discarded")]]
constexpr FloatingType units_to_centimeters(const FloatingType& units) constexpr FloatingType units_to_centimeters(const FloatingType& units)
{ {
return units / static_cast<FloatingType>(100); return units / static_cast<FloatingType>(100);
@@ -34,7 +95,7 @@ namespace omath::cry_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("meters value should not be discarded")]]
constexpr FloatingType units_to_meters(const FloatingType& units) constexpr FloatingType units_to_meters(const FloatingType& units)
{ {
return units; return units;
@@ -42,7 +103,7 @@ namespace omath::cry_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("kilometers value should not be discarded")]]
constexpr FloatingType units_to_kilometers(const FloatingType& units) constexpr FloatingType units_to_kilometers(const FloatingType& units)
{ {
return units_to_meters(units) / static_cast<FloatingType>(1000); return units_to_meters(units) / static_cast<FloatingType>(1000);
@@ -50,7 +111,7 @@ namespace omath::cry_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters) constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{ {
return centimeters * static_cast<FloatingType>(100); return centimeters * static_cast<FloatingType>(100);
@@ -58,7 +119,7 @@ namespace omath::cry_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType meters_to_units(const FloatingType& meters) constexpr FloatingType meters_to_units(const FloatingType& meters)
{ {
return meters; return meters;
@@ -66,7 +127,7 @@ namespace omath::cry_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers) constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{ {
return meters_to_units(kilometers * static_cast<FloatingType>(1000)); return meters_to_units(kilometers * static_cast<FloatingType>(1000));
@@ -5,20 +5,34 @@
#pragma once #pragma once
#include "omath/engines/cry_engine/formulas.hpp" #include "omath/engines/cry_engine/formulas.hpp"
#include "omath/projection/camera.hpp" #include "omath/projection/camera.hpp"
namespace omath::cry_engine namespace omath::cry_engine
{ {
class CameraTrait final class CameraTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("look-at angle result should not be discarded")]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept; constexpr static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin,
const Vector3<float>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
return {PitchAngle::from_asin(direction.z), -YawAngle::from_atan2(direction.x, direction.y),
RollAngle::from_radians(0.f)};
}
[[nodiscard]] [[nodiscard("view matrix result should not be discarded")]]
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; constexpr 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, return cry_engine::calc_view_matrix(angles, cam_origin);
float near, float far) noexcept; }
[[nodiscard("projection matrix result should not be discarded")]]
constexpr static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near_plane,
const float far_plane,
const NDCDepthRange ndc_depth_range) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near_plane, far_plane,
ndc_depth_range);
}
}; };
} // namespace omath::cry_engine } // namespace omath::cry_engine
@@ -10,7 +10,7 @@ namespace omath::cry_engine
class MeshTrait final class MeshTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation) static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{ {
return cry_engine::rotation_matrix(rotation); return cry_engine::rotation_matrix(rotation);
@@ -12,11 +12,13 @@ namespace omath::cry_engine
class PredEngineTrait final class PredEngineTrait final
{ {
public: public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile, [[nodiscard("projectile position result should not be discarded")]]
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw, const float pitch, const float yaw,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto current_pos = projectile.m_origin const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
auto current_pos = launch_pos
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), + forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)}) RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time; * projectile.m_launch_speed * time;
@@ -24,8 +26,8 @@ namespace omath::cry_engine
return current_pos; return current_pos;
} }
[[nodiscard]] [[nodiscard("target position result should not be discarded")]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target, static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto predicted = target.m_origin + target.m_velocity * time; auto predicted = target.m_origin + target.m_velocity * time;
@@ -35,20 +37,20 @@ namespace omath::cry_engine
return predicted; return predicted;
} }
[[nodiscard]] [[nodiscard("2d distance result should not be discarded")]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{ {
return std::sqrt(delta.x * delta.x + delta.y * delta.y); return std::sqrt(delta.x * delta.x + delta.y * delta.y);
} }
[[nodiscard]] [[nodiscard("height coordinate result should not be discarded")]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{ {
return vec.z; return vec.z;
} }
[[nodiscard]] [[nodiscard("viewpoint result should not be discarded")]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position, Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept const std::optional<float> projectile_pitch) noexcept
{ {
@@ -59,13 +61,13 @@ namespace omath::cry_engine
} }
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be: // Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down // 89 look up, -89 look down
[[nodiscard]] [[nodiscard("pitch angle result should not be discarded")]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::asin(direction.z)); return angles::radians_to_degrees(std::asin(direction.z));
} }
[[nodiscard]] [[nodiscard("yaw angle result should not be discarded")]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
@@ -9,5 +9,5 @@
namespace omath::frostbite_engine namespace omath::frostbite_engine
{ {
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>; using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::unity_engine } // namespace omath::frostbite_engine
@@ -7,26 +7,91 @@
namespace omath::frostbite_engine namespace omath::frostbite_engine
{ {
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept; constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard]] [[nodiscard("forward vector result should not be discarded")]]
Vector3<float> right_vector(const ViewAngles& angles) noexcept; constexpr Vector3<float> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Vector3<float> up_vector(const ViewAngles& angles) noexcept; }
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; [[nodiscard("right vector result should not be discarded")]]
constexpr Vector3<float> right_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; }
[[nodiscard]] [[nodiscard("up vector result should not be discarded")]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; constexpr Vector3<float> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("view matrix result should not be discarded")]]
constexpr Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return mat_camera_view<float, MatStoreType::ROW_MAJOR>(forward_vector(angles), right_vector(angles),
up_vector(angles), cam_origin);
}
[[nodiscard("rotation matrix result should not be discarded")]]
constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z<float, MatStoreType::ROW_MAJOR>(angles.roll)
* mat_rotation_axis_y<float, MatStoreType::ROW_MAJOR>(angles.yaw)
* mat_rotation_axis_x<float, MatStoreType::ROW_MAJOR>(angles.pitch);
}
[[nodiscard("origin result should not be discarded")]]
constexpr Vector3<float> extract_origin(const Mat4X4& mat) noexcept
{
return mat_extract_origin(mat);
}
[[nodiscard("scale result should not be discarded")]]
constexpr Vector3<float> extract_scale(const Mat4X4& mat) noexcept
{
return mat_extract_scale(mat);
}
[[nodiscard("rotation angles result should not be discarded")]]
constexpr ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
{
const auto angles = mat_extract_rotation_zyx(mat);
return {
PitchAngle::from_degrees(angles.x),
YawAngle::from_degrees(angles.y),
RollAngle::from_degrees(angles.z),
};
}
[[nodiscard("perspective projection matrix result should not be discarded")]]
constexpr Mat4X4 calc_perspective_projection_matrix(
const float field_of_view, const float aspect_ratio, const float near_plane, const float far_plane,
const NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept
{
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
std::unreachable();
}
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("centimeters value should not be discarded")]]
constexpr FloatingType units_to_centimeters(const FloatingType& units) constexpr FloatingType units_to_centimeters(const FloatingType& units)
{ {
return units / static_cast<FloatingType>(100); return units / static_cast<FloatingType>(100);
@@ -34,7 +99,7 @@ namespace omath::frostbite_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("meters value should not be discarded")]]
constexpr FloatingType units_to_meters(const FloatingType& units) constexpr FloatingType units_to_meters(const FloatingType& units)
{ {
return units; return units;
@@ -42,7 +107,7 @@ namespace omath::frostbite_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("kilometers value should not be discarded")]]
constexpr FloatingType units_to_kilometers(const FloatingType& units) constexpr FloatingType units_to_kilometers(const FloatingType& units)
{ {
return units_to_meters(units) / static_cast<FloatingType>(1000); return units_to_meters(units) / static_cast<FloatingType>(1000);
@@ -50,7 +115,7 @@ namespace omath::frostbite_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters) constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{ {
return centimeters * static_cast<FloatingType>(100); return centimeters * static_cast<FloatingType>(100);
@@ -58,7 +123,7 @@ namespace omath::frostbite_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType meters_to_units(const FloatingType& meters) constexpr FloatingType meters_to_units(const FloatingType& meters)
{ {
return meters; return meters;
@@ -66,7 +131,7 @@ namespace omath::frostbite_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers) constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{ {
return meters_to_units(kilometers * static_cast<FloatingType>(1000)); return meters_to_units(kilometers * static_cast<FloatingType>(1000));
@@ -11,14 +11,30 @@ namespace omath::frostbite_engine
class CameraTrait final class CameraTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("look-at angle result should not be discarded")]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept; constexpr static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin,
const Vector3<float>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
[[nodiscard]] return {-PitchAngle::from_asin(direction.y), YawAngle::from_atan2(direction.x, direction.z),
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; RollAngle::from_radians(0.f)};
[[nodiscard]] }
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept; [[nodiscard("view matrix result should not be discarded")]]
constexpr static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return frostbite_engine::calc_view_matrix(angles, cam_origin);
}
[[nodiscard("projection matrix result should not be discarded")]]
constexpr static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near_plane,
const float far_plane,
const NDCDepthRange ndc_depth_range) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near_plane, far_plane,
ndc_depth_range);
}
}; };
} // namespace omath::unreal_engine } // namespace omath::frostbite_engine
@@ -10,7 +10,7 @@ namespace omath::frostbite_engine
class MeshTrait final class MeshTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation) static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{ {
return frostbite_engine::rotation_matrix(rotation); return frostbite_engine::rotation_matrix(rotation);
@@ -12,11 +12,13 @@ namespace omath::frostbite_engine
class PredEngineTrait final class PredEngineTrait final
{ {
public: public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile, [[nodiscard("projectile position result should not be discarded")]]
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw, const float pitch, const float yaw,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto current_pos = projectile.m_origin const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
auto current_pos = launch_pos
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), + forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)}) RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time; * projectile.m_launch_speed * time;
@@ -24,8 +26,8 @@ namespace omath::frostbite_engine
return current_pos; return current_pos;
} }
[[nodiscard]] [[nodiscard("target position result should not be discarded")]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target, static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto predicted = target.m_origin + target.m_velocity * time; auto predicted = target.m_origin + target.m_velocity * time;
@@ -35,20 +37,20 @@ namespace omath::frostbite_engine
return predicted; return predicted;
} }
[[nodiscard]] [[nodiscard("2d distance result should not be discarded")]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{ {
return std::sqrt(delta.x * delta.x + delta.z * delta.z); return std::sqrt(delta.x * delta.x + delta.z * delta.z);
} }
[[nodiscard]] [[nodiscard("height coordinate result should not be discarded")]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{ {
return vec.y; return vec.y;
} }
[[nodiscard]] [[nodiscard("viewpoint result should not be discarded")]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position, Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept const std::optional<float> projectile_pitch) noexcept
{ {
@@ -59,13 +61,13 @@ namespace omath::frostbite_engine
} }
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be: // Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down // 89 look up, -89 look down
[[nodiscard]] [[nodiscard("pitch angle result should not be discarded")]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::asin(direction.y)); return angles::radians_to_degrees(std::asin(direction.y));
} }
[[nodiscard]] [[nodiscard("yaw angle result should not be discarded")]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
+1 -1
View File
@@ -9,5 +9,5 @@
namespace omath::iw_engine namespace omath::iw_engine
{ {
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>; using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::iw_engine } // namespace omath::iw_engine
+80 -17
View File
@@ -7,26 +7,89 @@
namespace omath::iw_engine namespace omath::iw_engine
{ {
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept; constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard]] [[nodiscard("forward vector result should not be discarded")]]
Vector3<float> right_vector(const ViewAngles& angles) noexcept; constexpr Vector3<float> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Vector3<float> up_vector(const ViewAngles& angles) noexcept; }
[[nodiscard]] [[nodiscard("right vector result should not be discarded")]]
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; constexpr Vector3<float> right_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard]] [[nodiscard("up vector result should not be discarded")]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; constexpr Vector3<float> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("rotation matrix result should not be discarded")]]
constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z(angles.yaw) * mat_rotation_axis_y(angles.pitch) * mat_rotation_axis_x(angles.roll);
}
[[nodiscard("origin result should not be discarded")]]
constexpr Vector3<float> extract_origin(const Mat4X4& mat) noexcept
{
return mat_extract_origin(mat);
}
[[nodiscard("scale result should not be discarded")]]
constexpr Vector3<float> extract_scale(const Mat4X4& mat) noexcept
{
return mat_extract_scale(mat);
}
[[nodiscard("rotation angles result should not be discarded")]]
constexpr ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
{
const auto angles = mat_extract_rotation_zyx(mat);
return {
PitchAngle::from_degrees(angles.y),
YawAngle::from_degrees(angles.z),
RollAngle::from_degrees(angles.x),
};
}
[[nodiscard("view matrix result should not be discarded")]]
constexpr Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return mat_camera_view(forward_vector(angles), right_vector(angles), up_vector(angles), cam_origin);
}
[[nodiscard("perspective projection matrix result should not be discarded")]]
constexpr Mat4X4 calc_perspective_projection_matrix(
const float field_of_view, const float aspect_ratio, const float near_plane, const float far_plane,
const NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept
{
constexpr float k_source_reference_aspect = 4.f / 3.f;
const auto vertical_fov = angles::horizontal_fov_to_vertical(field_of_view, k_source_reference_aspect);
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>(
vertical_fov, aspect_ratio, near_plane, far_plane);
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
vertical_fov, aspect_ratio, near_plane, far_plane);
std::unreachable();
}
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("centimeters value should not be discarded")]]
constexpr FloatingType units_to_centimeters(const FloatingType& units) constexpr FloatingType units_to_centimeters(const FloatingType& units)
{ {
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54); constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
@@ -35,7 +98,7 @@ namespace omath::iw_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("meters value should not be discarded")]]
constexpr FloatingType units_to_meters(const FloatingType& units) constexpr FloatingType units_to_meters(const FloatingType& units)
{ {
return units_to_centimeters(units) / static_cast<FloatingType>(100); return units_to_centimeters(units) / static_cast<FloatingType>(100);
@@ -43,7 +106,7 @@ namespace omath::iw_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("kilometers value should not be discarded")]]
constexpr FloatingType units_to_kilometers(const FloatingType& units) constexpr FloatingType units_to_kilometers(const FloatingType& units)
{ {
return units_to_meters(units) / static_cast<FloatingType>(1000); return units_to_meters(units) / static_cast<FloatingType>(1000);
@@ -51,7 +114,7 @@ namespace omath::iw_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters) constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{ {
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54); constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
@@ -60,7 +123,7 @@ namespace omath::iw_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType meters_to_units(const FloatingType& meters) constexpr FloatingType meters_to_units(const FloatingType& meters)
{ {
return centimeters_to_units(meters * static_cast<FloatingType>(100)); return centimeters_to_units(meters * static_cast<FloatingType>(100));
@@ -68,7 +131,7 @@ namespace omath::iw_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers) constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{ {
return meters_to_units(kilometers * static_cast<FloatingType>(1000)); return meters_to_units(kilometers * static_cast<FloatingType>(1000));
@@ -3,7 +3,7 @@
// //
#pragma once #pragma once
#include "omath/engines/iw_engine/constants.hpp" #include "omath/engines/iw_engine/formulas.hpp"
#include "omath/projection/camera.hpp" #include "omath/projection/camera.hpp"
namespace omath::iw_engine namespace omath::iw_engine
@@ -11,14 +11,30 @@ namespace omath::iw_engine
class CameraTrait final class CameraTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("look-at angle result should not be discarded")]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept; constexpr static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin,
const Vector3<float>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
[[nodiscard]] return {-PitchAngle::from_asin(direction.z), YawAngle::from_atan2(direction.y, direction.x),
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; RollAngle::from_radians(0.f)};
[[nodiscard]] }
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept; [[nodiscard("view matrix result should not be discarded")]]
constexpr static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return iw_engine::calc_view_matrix(angles, cam_origin);
}
[[nodiscard("projection matrix result should not be discarded")]]
constexpr static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near_plane,
const float far_plane,
const NDCDepthRange ndc_depth_range) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near_plane, far_plane,
ndc_depth_range);
}
}; };
} // namespace omath::iw_engine } // namespace omath::iw_engine
@@ -10,7 +10,7 @@ namespace omath::iw_engine
class MeshTrait final class MeshTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation) static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{ {
return iw_engine::rotation_matrix(rotation); return iw_engine::rotation_matrix(rotation);
@@ -13,11 +13,13 @@ namespace omath::iw_engine
class PredEngineTrait final class PredEngineTrait final
{ {
public: public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile, [[nodiscard("projectile position result should not be discarded")]]
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw, const float pitch, const float yaw,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto current_pos = projectile.m_origin const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
auto current_pos = launch_pos
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), + forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)}) RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time; * projectile.m_launch_speed * time;
@@ -25,8 +27,8 @@ namespace omath::iw_engine
return current_pos; return current_pos;
} }
[[nodiscard]] [[nodiscard("target position result should not be discarded")]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target, static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto predicted = target.m_origin + target.m_velocity * time; auto predicted = target.m_origin + target.m_velocity * time;
@@ -36,20 +38,20 @@ namespace omath::iw_engine
return predicted; return predicted;
} }
[[nodiscard]] [[nodiscard("2d distance result should not be discarded")]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{ {
return std::sqrt(delta.x * delta.x + delta.y * delta.y); return std::sqrt(delta.x * delta.x + delta.y * delta.y);
} }
[[nodiscard]] [[nodiscard("height coordinate result should not be discarded")]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{ {
return vec.z; return vec.z;
} }
[[nodiscard]] [[nodiscard("viewpoint result should not be discarded")]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position, Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept const std::optional<float> projectile_pitch) noexcept
{ {
@@ -60,7 +62,7 @@ namespace omath::iw_engine
} }
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be: // Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down // 89 look up, -89 look down
[[nodiscard]] [[nodiscard("pitch angle result should not be discarded")]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto distance = origin.distance_to(view_to); const auto distance = origin.distance_to(view_to);
@@ -68,7 +70,7 @@ namespace omath::iw_engine
return angles::radians_to_degrees(std::asin(delta.z / distance)); return angles::radians_to_degrees(std::asin(delta.z / distance));
} }
[[nodiscard]] [[nodiscard("yaw angle result should not be discarded")]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto delta = view_to - origin; const auto delta = view_to - origin;
@@ -8,5 +8,5 @@
namespace omath::opengl_engine namespace omath::opengl_engine
{ {
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, true>; using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::NEGATIVE_ONE_TO_ONE, {.inverted_forward = true}>;
} // namespace omath::opengl_engine } // namespace omath::opengl_engine
@@ -6,26 +6,93 @@
namespace omath::opengl_engine namespace omath::opengl_engine
{ {
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept; constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard]] [[nodiscard("forward vector result should not be discarded")]]
Vector3<float> right_vector(const ViewAngles& angles) noexcept; constexpr Vector3<float> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec =
rotation_matrix(angles) * mat_column_from_vector<float, MatStoreType::COLUMN_MAJOR>(k_abs_forward);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Vector3<float> up_vector(const ViewAngles& angles) noexcept; }
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; [[nodiscard("right vector result should not be discarded")]]
constexpr Vector3<float> right_vector(const ViewAngles& angles) noexcept
{
const auto vec =
rotation_matrix(angles) * mat_column_from_vector<float, MatStoreType::COLUMN_MAJOR>(k_abs_right);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; }
[[nodiscard]] [[nodiscard("up vector result should not be discarded")]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; constexpr Vector3<float> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector<float, MatStoreType::COLUMN_MAJOR>(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("view matrix result should not be discarded")]]
constexpr Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return mat_look_at_right_handed(cam_origin, cam_origin + forward_vector(angles), up_vector(angles));
}
[[nodiscard("rotation matrix result should not be discarded")]]
constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z<float, MatStoreType::COLUMN_MAJOR>(angles.roll)
* mat_rotation_axis_y<float, MatStoreType::COLUMN_MAJOR>(angles.yaw)
* mat_rotation_axis_x<float, MatStoreType::COLUMN_MAJOR>(angles.pitch);
}
[[nodiscard("origin result should not be discarded")]]
constexpr Vector3<float> extract_origin(const Mat4X4& mat) noexcept
{
return mat_extract_origin(mat);
}
[[nodiscard("scale result should not be discarded")]]
constexpr Vector3<float> extract_scale(const Mat4X4& mat) noexcept
{
return mat_extract_scale(mat);
}
[[nodiscard("rotation angles result should not be discarded")]]
constexpr ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
{
const auto angles = mat_extract_rotation_zyx(mat);
return {
PitchAngle::from_degrees(angles.x),
YawAngle::from_degrees(angles.y),
RollAngle::from_degrees(angles.z),
};
}
[[nodiscard("perspective projection matrix result should not be discarded")]]
constexpr Mat4X4 calc_perspective_projection_matrix(
const float field_of_view, const float aspect_ratio, const float near_plane, const float far_plane,
const NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept
{
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
return mat_perspective_right_handed_vertical_fov<float, MatStoreType::COLUMN_MAJOR,
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
return mat_perspective_right_handed_vertical_fov<float, MatStoreType::COLUMN_MAJOR,
NDCDepthRange::ZERO_TO_ONE>(field_of_view, aspect_ratio,
near_plane, far_plane);
std::unreachable();
}
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("centimeters value should not be discarded")]]
constexpr FloatingType units_to_centimeters(const FloatingType& units) constexpr FloatingType units_to_centimeters(const FloatingType& units)
{ {
return units / static_cast<FloatingType>(100); return units / static_cast<FloatingType>(100);
@@ -33,7 +100,7 @@ namespace omath::opengl_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("meters value should not be discarded")]]
constexpr FloatingType units_to_meters(const FloatingType& units) constexpr FloatingType units_to_meters(const FloatingType& units)
{ {
return units; return units;
@@ -41,7 +108,7 @@ namespace omath::opengl_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("kilometers value should not be discarded")]]
constexpr FloatingType units_to_kilometers(const FloatingType& units) constexpr FloatingType units_to_kilometers(const FloatingType& units)
{ {
return units_to_meters(units) / static_cast<FloatingType>(1000); return units_to_meters(units) / static_cast<FloatingType>(1000);
@@ -49,7 +116,7 @@ namespace omath::opengl_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters) constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{ {
return centimeters * static_cast<FloatingType>(100); return centimeters * static_cast<FloatingType>(100);
@@ -57,7 +124,7 @@ namespace omath::opengl_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType meters_to_units(const FloatingType& meters) constexpr FloatingType meters_to_units(const FloatingType& meters)
{ {
return meters; return meters;
@@ -65,7 +132,7 @@ namespace omath::opengl_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers) constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{ {
return meters_to_units(kilometers * static_cast<FloatingType>(1000)); return meters_to_units(kilometers * static_cast<FloatingType>(1000));
@@ -3,7 +3,7 @@
// //
#pragma once #pragma once
#include "omath/engines/opengl_engine/constants.hpp" #include "omath/engines/opengl_engine/formulas.hpp"
#include "omath/projection/camera.hpp" #include "omath/projection/camera.hpp"
namespace omath::opengl_engine namespace omath::opengl_engine
@@ -11,14 +11,30 @@ namespace omath::opengl_engine
class CameraTrait final class CameraTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("look-at angle result should not be discarded")]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept; constexpr static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin,
const Vector3<float>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
[[nodiscard]] return {PitchAngle::from_asin(direction.y), -YawAngle::from_atan2(direction.x, -direction.z),
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; RollAngle::from_radians(0.f)};
[[nodiscard]] }
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept; [[nodiscard("view matrix result should not be discarded")]]
constexpr static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return opengl_engine::calc_view_matrix(angles, cam_origin);
}
[[nodiscard("projection matrix result should not be discarded")]]
constexpr static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near_plane,
const float far_plane,
const NDCDepthRange ndc_depth_range) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near_plane, far_plane,
ndc_depth_range);
}
}; };
} // namespace omath::opengl_engine } // namespace omath::opengl_engine
@@ -10,7 +10,7 @@ namespace omath::opengl_engine
class MeshTrait final class MeshTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation) static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{ {
return opengl_engine::rotation_matrix(rotation); return opengl_engine::rotation_matrix(rotation);
@@ -12,11 +12,13 @@ namespace omath::opengl_engine
class PredEngineTrait final class PredEngineTrait final
{ {
public: public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile, [[nodiscard("projectile position result should not be discarded")]]
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw, const float pitch, const float yaw,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto current_pos = projectile.m_origin const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
auto current_pos = launch_pos
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), + forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)}) RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time; * projectile.m_launch_speed * time;
@@ -24,8 +26,8 @@ namespace omath::opengl_engine
return current_pos; return current_pos;
} }
[[nodiscard]] [[nodiscard("target position result should not be discarded")]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target, static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto predicted = target.m_origin + target.m_velocity * time; auto predicted = target.m_origin + target.m_velocity * time;
@@ -35,20 +37,20 @@ namespace omath::opengl_engine
return predicted; return predicted;
} }
[[nodiscard]] [[nodiscard("2d distance result should not be discarded")]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{ {
return std::sqrt(delta.x * delta.x + delta.z * delta.z); return std::sqrt(delta.x * delta.x + delta.z * delta.z);
} }
[[nodiscard]] [[nodiscard("height coordinate result should not be discarded")]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{ {
return vec.y; return vec.y;
} }
[[nodiscard]] [[nodiscard("viewpoint result should not be discarded")]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position, Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept const std::optional<float> projectile_pitch) noexcept
{ {
@@ -59,13 +61,13 @@ namespace omath::opengl_engine
} }
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be: // Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down // 89 look up, -89 look down
[[nodiscard]] [[nodiscard("pitch angle result should not be discarded")]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::asin(direction.y)); return angles::radians_to_degrees(std::asin(direction.y));
} }
[[nodiscard]] [[nodiscard("yaw angle result should not be discarded")]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
@@ -0,0 +1,13 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "omath/engines/rage_engine/constants.hpp"
#include "omath/projection/camera.hpp"
#include "traits/camera_trait.hpp"
namespace omath::rage_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::rage_engine
@@ -0,0 +1,25 @@
//
// Created by Orange on 6/3/2026.
//
#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::rage_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<3, 3, 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::rage_engine
@@ -0,0 +1,140 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "omath/engines/rage_engine/constants.hpp"
#include <type_traits>
namespace omath::rage_engine
{
[[nodiscard("rotation matrix result should not be discarded")]]
constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard("forward vector result should not be discarded")]]
constexpr Vector3<float> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("right vector result should not be discarded")]]
constexpr Vector3<float> right_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("up vector result should not be discarded")]]
constexpr Vector3<float> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("view matrix result should not be discarded")]]
constexpr Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return mat_camera_view<float, MatStoreType::ROW_MAJOR>(forward_vector(angles), right_vector(angles),
up_vector(angles), cam_origin);
}
[[nodiscard("rotation matrix result should not be discarded")]]
constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z<float, MatStoreType::ROW_MAJOR>(angles.yaw)
* mat_rotation_axis_y<float, MatStoreType::ROW_MAJOR>(angles.roll)
* mat_rotation_axis_x<float, MatStoreType::ROW_MAJOR>(angles.pitch);
}
[[nodiscard("origin result should not be discarded")]]
constexpr Vector3<float> extract_origin(const Mat4X4& mat) noexcept
{
return mat_extract_origin(mat);
}
[[nodiscard("scale result should not be discarded")]]
constexpr Vector3<float> extract_scale(const Mat4X4& mat) noexcept
{
return mat_extract_scale(mat);
}
[[nodiscard("rotation angles result should not be discarded")]]
constexpr ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
{
const auto angles = mat_extract_rotation_zyx(mat);
return {
PitchAngle::from_degrees(angles.x),
YawAngle::from_degrees(angles.z),
RollAngle::from_degrees(angles.y),
};
}
[[nodiscard("perspective projection matrix result should not be discarded")]]
constexpr Mat4X4
calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near_plane,
const float far_plane,
const NDCDepthRange ndc_depth_range = NDCDepthRange::ZERO_TO_ONE) noexcept
{
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
std::unreachable();
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard("centimeters value should not be discarded")]]
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("meters value should not be discarded")]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard("kilometers value should not be discarded")]]
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("units value should not be discarded")]]
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("units value should not be discarded")]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard("units value should not be discarded")]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::rage_engine
@@ -0,0 +1,13 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "constants.hpp"
#include "omath/3d_primitives/mesh.hpp"
#include "traits/mesh_trait.hpp"
namespace omath::rage_engine
{
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
} // namespace omath::rage_engine
@@ -0,0 +1,40 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "omath/engines/rage_engine/formulas.hpp"
#include "omath/projection/camera.hpp"
namespace omath::rage_engine
{
class CameraTrait final
{
public:
[[nodiscard("look-at angle result should not be discarded")]]
constexpr static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin,
const Vector3<float>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
return {PitchAngle::from_asin(direction.z), -YawAngle::from_atan2(direction.x, direction.y),
RollAngle::from_radians(0.f)};
}
[[nodiscard("view matrix result should not be discarded")]]
constexpr static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return rage_engine::calc_view_matrix(angles, cam_origin);
}
[[nodiscard("projection matrix result should not be discarded")]]
constexpr static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near_plane,
const float far_plane,
const NDCDepthRange ndc_depth_range) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near_plane, far_plane,
ndc_depth_range);
}
};
} // namespace omath::rage_engine
@@ -0,0 +1,20 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include <omath/engines/rage_engine/constants.hpp>
#include <omath/engines/rage_engine/formulas.hpp>
namespace omath::rage_engine
{
class MeshTrait final
{
public:
[[nodiscard("rotation matrix result should not be discarded")]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{
return rage_engine::rotation_matrix(rotation);
}
};
} // namespace omath::rage_engine
@@ -0,0 +1,77 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "omath/engines/rage_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::rage_engine
{
class PredEngineTrait final
{
public:
[[nodiscard("projectile position result should not be discarded")]]
constexpr static Vector3<float>
predict_projectile_position(const projectile_prediction::Projectile<float>& projectile, const float pitch,
const float yaw, const float time, const float gravity) noexcept
{
const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
auto current_pos = launch_pos
+ 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("target position result should not be discarded")]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& 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("2d distance result should not be discarded")]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.y * delta.y);
}
[[nodiscard("height coordinate result should not be discarded")]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.z;
}
[[nodiscard("viewpoint result should not be discarded")]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& 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};
}
[[nodiscard("pitch angle result should not be discarded")]]
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("yaw angle result should not be discarded")]]
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::rage_engine
@@ -7,5 +7,5 @@
#include "traits/camera_trait.hpp" #include "traits/camera_trait.hpp"
namespace omath::source_engine namespace omath::source_engine
{ {
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>; using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::source_engine } // namespace omath::source_engine
@@ -6,26 +6,89 @@
namespace omath::source_engine namespace omath::source_engine
{ {
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept; constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard]] [[nodiscard("forward vector result should not be discarded")]]
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; constexpr Vector3<float> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Vector3<float> right_vector(const ViewAngles& angles) noexcept; }
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
Vector3<float> up_vector(const ViewAngles& angles) noexcept; constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z(angles.yaw) * mat_rotation_axis_y(angles.pitch) * mat_rotation_axis_x(angles.roll);
}
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; [[nodiscard("origin result should not be discarded")]]
constexpr Vector3<float> extract_origin(const Mat4X4& mat) noexcept
{
return mat_extract_origin(mat);
}
[[nodiscard]] [[nodiscard("scale result should not be discarded")]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; constexpr Vector3<float> extract_scale(const Mat4X4& mat) noexcept
{
return mat_extract_scale(mat);
}
[[nodiscard("rotation angles result should not be discarded")]]
constexpr ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
{
const auto angles = mat_extract_rotation_zyx(mat);
return {
PitchAngle::from_degrees(angles.y),
YawAngle::from_degrees(angles.z),
RollAngle::from_degrees(angles.x),
};
}
[[nodiscard("right vector result should not be discarded")]]
constexpr Vector3<float> right_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("up vector result should not be discarded")]]
constexpr Vector3<float> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("view matrix result should not be discarded")]]
constexpr Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return mat_camera_view(forward_vector(angles), right_vector(angles), up_vector(angles), cam_origin);
}
[[nodiscard("perspective projection matrix result should not be discarded")]]
constexpr Mat4X4 calc_perspective_projection_matrix(
const float field_of_view, const float aspect_ratio, const float near_plane, const float far_plane,
const NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept
{
constexpr float k_source_reference_aspect = 4.f / 3.f;
const auto vertical_fov = angles::horizontal_fov_to_vertical(field_of_view, k_source_reference_aspect);
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>(
vertical_fov, aspect_ratio, near_plane, far_plane);
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
vertical_fov, aspect_ratio, near_plane, far_plane);
std::unreachable();
}
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("centimeters value should not be discarded")]]
constexpr FloatingType units_to_centimeters(const FloatingType& units) constexpr FloatingType units_to_centimeters(const FloatingType& units)
{ {
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54); constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
@@ -34,7 +97,7 @@ namespace omath::source_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("meters value should not be discarded")]]
constexpr FloatingType units_to_meters(const FloatingType& units) constexpr FloatingType units_to_meters(const FloatingType& units)
{ {
return units_to_centimeters(units) / static_cast<FloatingType>(100); return units_to_centimeters(units) / static_cast<FloatingType>(100);
@@ -42,7 +105,7 @@ namespace omath::source_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("kilometers value should not be discarded")]]
constexpr FloatingType units_to_kilometers(const FloatingType& units) constexpr FloatingType units_to_kilometers(const FloatingType& units)
{ {
return units_to_meters(units) / static_cast<FloatingType>(1000); return units_to_meters(units) / static_cast<FloatingType>(1000);
@@ -50,7 +113,7 @@ namespace omath::source_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters) constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{ {
constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54); constexpr auto centimeter_in_unit = static_cast<FloatingType>(2.54);
@@ -59,7 +122,7 @@ namespace omath::source_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType meters_to_units(const FloatingType& meters) constexpr FloatingType meters_to_units(const FloatingType& meters)
{ {
return centimeters_to_units(meters * static_cast<FloatingType>(100)); return centimeters_to_units(meters * static_cast<FloatingType>(100));
@@ -67,7 +130,7 @@ namespace omath::source_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers) constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{ {
return meters_to_units(kilometers * static_cast<FloatingType>(1000)); return meters_to_units(kilometers * static_cast<FloatingType>(1000));
@@ -3,7 +3,7 @@
// //
#pragma once #pragma once
#include "omath/engines/source_engine/constants.hpp" #include "omath/engines/source_engine/formulas.hpp"
#include "omath/projection/camera.hpp" #include "omath/projection/camera.hpp"
namespace omath::source_engine namespace omath::source_engine
@@ -11,14 +11,30 @@ namespace omath::source_engine
class CameraTrait final class CameraTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("look-at angle result should not be discarded")]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept; constexpr static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin,
const Vector3<float>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
[[nodiscard]] return {-PitchAngle::from_asin(direction.z),
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; YawAngle::from_atan2(direction.y, direction.x), RollAngle::from_radians(0.f)};
[[nodiscard]] }
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept; [[nodiscard("view matrix result should not be discarded")]]
constexpr static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return source_engine::calc_view_matrix(angles, cam_origin);
}
[[nodiscard("projection matrix result should not be discarded")]]
constexpr static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near_plane,
const float far_plane,
const NDCDepthRange ndc_depth_range) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near_plane, far_plane,
ndc_depth_range);
}
}; };
} // namespace omath::source_engine } // namespace omath::source_engine
@@ -10,7 +10,7 @@ namespace omath::source_engine
class MeshTrait final class MeshTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation) static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{ {
return source_engine::rotation_matrix(rotation); return source_engine::rotation_matrix(rotation);
@@ -13,11 +13,13 @@ namespace omath::source_engine
class PredEngineTrait final class PredEngineTrait final
{ {
public: public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile, [[nodiscard("projectile position result should not be discarded")]]
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw, const float pitch, const float yaw,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto current_pos = projectile.m_origin const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
auto current_pos = launch_pos
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), + forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)}) RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time; * projectile.m_launch_speed * time;
@@ -25,8 +27,8 @@ namespace omath::source_engine
return current_pos; return current_pos;
} }
[[nodiscard]] [[nodiscard("target position result should not be discarded")]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target, static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto predicted = target.m_origin + target.m_velocity * time; auto predicted = target.m_origin + target.m_velocity * time;
@@ -36,20 +38,20 @@ namespace omath::source_engine
return predicted; return predicted;
} }
[[nodiscard]] [[nodiscard("2d distance result should not be discarded")]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{ {
return std::sqrt(delta.x * delta.x + delta.y * delta.y); return std::sqrt(delta.x * delta.x + delta.y * delta.y);
} }
[[nodiscard]] [[nodiscard("height coordinate result should not be discarded")]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{ {
return vec.z; return vec.z;
} }
[[nodiscard]] [[nodiscard("viewpoint result should not be discarded")]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position, Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept const std::optional<float> projectile_pitch) noexcept
{ {
@@ -60,7 +62,7 @@ namespace omath::source_engine
} }
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be: // Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down // 89 look up, -89 look down
[[nodiscard]] [[nodiscard("pitch angle result should not be discarded")]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto distance = origin.distance_to(view_to); const auto distance = origin.distance_to(view_to);
@@ -68,7 +70,7 @@ namespace omath::source_engine
return angles::radians_to_degrees(std::asin(delta.z / distance)); return angles::radians_to_degrees(std::asin(delta.z / distance));
} }
[[nodiscard]] [[nodiscard("yaw angle result should not be discarded")]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto delta = view_to - origin; const auto delta = view_to - origin;
@@ -9,5 +9,5 @@
namespace omath::unity_engine namespace omath::unity_engine
{ {
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>; using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE, {.inverted_forward = true}>;
} // namespace omath::unity_engine } // namespace omath::unity_engine
+81 -17
View File
@@ -7,26 +7,90 @@
namespace omath::unity_engine namespace omath::unity_engine
{ {
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept; constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard]] [[nodiscard("forward vector result should not be discarded")]]
Vector3<float> right_vector(const ViewAngles& angles) noexcept; constexpr Vector3<float> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Vector3<float> up_vector(const ViewAngles& angles) noexcept; }
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; [[nodiscard("right vector result should not be discarded")]]
constexpr Vector3<float> right_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; }
[[nodiscard]] [[nodiscard("up vector result should not be discarded")]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; constexpr Vector3<float> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("view matrix result should not be discarded")]]
constexpr Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return mat_camera_view<float, MatStoreType::ROW_MAJOR>(-forward_vector(angles), right_vector(angles),
up_vector(angles), cam_origin);
}
[[nodiscard("rotation matrix result should not be discarded")]]
constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z<float, MatStoreType::ROW_MAJOR>(angles.roll)
* mat_rotation_axis_y<float, MatStoreType::ROW_MAJOR>(angles.yaw)
* mat_rotation_axis_x<float, MatStoreType::ROW_MAJOR>(angles.pitch);
}
[[nodiscard("origin result should not be discarded")]]
constexpr Vector3<float> extract_origin(const Mat4X4& mat) noexcept
{
return mat_extract_origin(mat);
}
[[nodiscard("scale result should not be discarded")]]
constexpr Vector3<float> extract_scale(const Mat4X4& mat) noexcept
{
return mat_extract_scale(mat);
}
[[nodiscard("rotation angles result should not be discarded")]]
constexpr ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
{
const auto angles = mat_extract_rotation_zyx(mat);
return {
PitchAngle::from_degrees(angles.x),
YawAngle::from_degrees(angles.y),
RollAngle::from_degrees(angles.z),
};
}
[[nodiscard("perspective projection matrix result should not be discarded")]]
constexpr Mat4X4 calc_perspective_projection_matrix(
const float field_of_view, const float aspect_ratio, const float near_plane, const float far_plane,
const NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept
{
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
return omath::mat_perspective_right_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
NDCDepthRange::ZERO_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
return omath::mat_perspective_right_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
std::unreachable();
}
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("centimeters value should not be discarded")]]
constexpr FloatingType units_to_centimeters(const FloatingType& units) constexpr FloatingType units_to_centimeters(const FloatingType& units)
{ {
return units / static_cast<FloatingType>(100); return units / static_cast<FloatingType>(100);
@@ -34,7 +98,7 @@ namespace omath::unity_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("meters value should not be discarded")]]
constexpr FloatingType units_to_meters(const FloatingType& units) constexpr FloatingType units_to_meters(const FloatingType& units)
{ {
return units; return units;
@@ -42,7 +106,7 @@ namespace omath::unity_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("kilometers value should not be discarded")]]
constexpr FloatingType units_to_kilometers(const FloatingType& units) constexpr FloatingType units_to_kilometers(const FloatingType& units)
{ {
return units_to_meters(units) / static_cast<FloatingType>(1000); return units_to_meters(units) / static_cast<FloatingType>(1000);
@@ -50,7 +114,7 @@ namespace omath::unity_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters) constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{ {
return centimeters * static_cast<FloatingType>(100); return centimeters * static_cast<FloatingType>(100);
@@ -58,7 +122,7 @@ namespace omath::unity_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType meters_to_units(const FloatingType& meters) constexpr FloatingType meters_to_units(const FloatingType& meters)
{ {
return meters; return meters;
@@ -66,7 +130,7 @@ namespace omath::unity_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers) constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{ {
return meters_to_units(kilometers * static_cast<FloatingType>(1000)); return meters_to_units(kilometers * static_cast<FloatingType>(1000));
@@ -11,14 +11,30 @@ namespace omath::unity_engine
class CameraTrait final class CameraTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("look-at angle result should not be discarded")]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept; constexpr static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin,
const Vector3<float>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
[[nodiscard]] return {-PitchAngle::from_asin(direction.y), YawAngle::from_atan2(direction.x, direction.z),
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; RollAngle::from_radians(0.f)};
[[nodiscard]] }
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept; [[nodiscard("view matrix result should not be discarded")]]
constexpr static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return unity_engine::calc_view_matrix(angles, cam_origin);
}
[[nodiscard("projection matrix result should not be discarded")]]
constexpr static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near_plane,
const float far_plane,
const NDCDepthRange ndc_depth_range) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near_plane, far_plane,
ndc_depth_range);
}
}; };
} // namespace omath::unity_engine } // namespace omath::unity_engine
@@ -10,7 +10,7 @@ namespace omath::unity_engine
class MeshTrait final class MeshTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation) static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{ {
return unity_engine::rotation_matrix(rotation); return unity_engine::rotation_matrix(rotation);
@@ -12,11 +12,13 @@ namespace omath::unity_engine
class PredEngineTrait final class PredEngineTrait final
{ {
public: public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile, [[nodiscard("projectile position result should not be discarded")]]
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw, const float pitch, const float yaw,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto current_pos = projectile.m_origin const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
auto current_pos = launch_pos
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), + forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)}) RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time; * projectile.m_launch_speed * time;
@@ -24,8 +26,8 @@ namespace omath::unity_engine
return current_pos; return current_pos;
} }
[[nodiscard]] [[nodiscard("target position result should not be discarded")]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target, static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept const float time, const float gravity) noexcept
{ {
auto predicted = target.m_origin + target.m_velocity * time; auto predicted = target.m_origin + target.m_velocity * time;
@@ -35,20 +37,20 @@ namespace omath::unity_engine
return predicted; return predicted;
} }
[[nodiscard]] [[nodiscard("2d distance result should not be discarded")]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{ {
return std::sqrt(delta.x * delta.x + delta.z * delta.z); return std::sqrt(delta.x * delta.x + delta.z * delta.z);
} }
[[nodiscard]] [[nodiscard("height coordinate result should not be discarded")]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{ {
return vec.y; return vec.y;
} }
[[nodiscard]] [[nodiscard("viewpoint result should not be discarded")]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position, Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept const std::optional<float> projectile_pitch) noexcept
{ {
@@ -59,13 +61,13 @@ namespace omath::unity_engine
} }
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be: // Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down // 89 look up, -89 look down
[[nodiscard]] [[nodiscard("pitch angle result should not be discarded")]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::asin(direction.y)); return angles::radians_to_degrees(std::asin(direction.y));
} }
[[nodiscard]] [[nodiscard("yaw angle result should not be discarded")]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
@@ -9,5 +9,5 @@
namespace omath::unreal_engine namespace omath::unreal_engine
{ {
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>; using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE, {}, double>;
} // namespace omath::unreal_engine } // namespace omath::unreal_engine
@@ -11,16 +11,16 @@
namespace omath::unreal_engine namespace omath::unreal_engine
{ {
constexpr Vector3<float> k_abs_up = {0, 0, 1}; constexpr Vector3<double> k_abs_up = {0, 0, 1};
constexpr Vector3<float> k_abs_right = {0, 1, 0}; constexpr Vector3<double> k_abs_right = {0, 1, 0};
constexpr Vector3<float> k_abs_forward = {1, 0, 0}; constexpr Vector3<double> k_abs_forward = {1, 0, 0};
using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; using Mat4X4 = Mat<4, 4, double, MatStoreType::ROW_MAJOR>;
using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; using Mat3X3 = Mat<4, 4, double, MatStoreType::ROW_MAJOR>;
using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>; using Mat1X3 = Mat<1, 3, double, MatStoreType::ROW_MAJOR>;
using PitchAngle = Angle<float, -90.f, 90.f, AngleFlags::Clamped>; using PitchAngle = Angle<double, -90., 90., AngleFlags::Clamped>;
using YawAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>; using YawAngle = Angle<double, -180., 180., AngleFlags::Normalized>;
using RollAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>; using RollAngle = Angle<double, -180., 180., AngleFlags::Normalized>;
using ViewAngles = omath::ViewAngles<PitchAngle, YawAngle, RollAngle>; using ViewAngles = omath::ViewAngles<PitchAngle, YawAngle, RollAngle>;
} // namespace omath::unreal_engine } // namespace omath::unreal_engine
@@ -7,26 +7,90 @@
namespace omath::unreal_engine namespace omath::unreal_engine
{ {
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept; constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard]] [[nodiscard("forward vector result should not be discarded")]]
Vector3<float> right_vector(const ViewAngles& angles) noexcept; constexpr Vector3<double> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Vector3<float> up_vector(const ViewAngles& angles) noexcept; }
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; [[nodiscard("right vector result should not be discarded")]]
constexpr Vector3<double> right_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
[[nodiscard]] return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; }
[[nodiscard]] [[nodiscard("up vector result should not be discarded")]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; constexpr Vector3<double> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
[[nodiscard("view matrix result should not be discarded")]]
constexpr Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<double>& cam_origin) noexcept
{
return mat_camera_view<double, MatStoreType::ROW_MAJOR>(forward_vector(angles), right_vector(angles),
up_vector(angles), cam_origin);
}
[[nodiscard("rotation matrix result should not be discarded")]]
constexpr Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z<double, MatStoreType::ROW_MAJOR>(angles.yaw)
* mat_rotation_axis_y<double, MatStoreType::ROW_MAJOR>(-angles.pitch)
* mat_rotation_axis_x<double, MatStoreType::ROW_MAJOR>(-angles.roll);
}
[[nodiscard("origin result should not be discarded")]]
constexpr Vector3<double> extract_origin(const Mat4X4& mat) noexcept
{
return mat_extract_origin(mat);
}
[[nodiscard("scale result should not be discarded")]]
constexpr Vector3<double> extract_scale(const Mat4X4& mat) noexcept
{
return mat_extract_scale(mat);
}
[[nodiscard("rotation angles result should not be discarded")]]
constexpr ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
{
const auto angles = mat_extract_rotation_zyx(mat);
return {
PitchAngle::from_degrees(-angles.y),
YawAngle::from_degrees(angles.z),
RollAngle::from_degrees(-angles.x),
};
}
[[nodiscard("perspective projection matrix result should not be discarded")]]
constexpr Mat4X4 calc_perspective_projection_matrix(
const double field_of_view, const double aspect_ratio, const double near_plane, const double far_plane,
const NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept
{
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
return mat_perspective_left_handed_horizontal_fov<double, MatStoreType::ROW_MAJOR,
NDCDepthRange::ZERO_TO_ONE>(field_of_view, aspect_ratio,
near_plane, far_plane);
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
return mat_perspective_left_handed_horizontal_fov<double, MatStoreType::ROW_MAJOR,
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
field_of_view, aspect_ratio, near_plane, far_plane);
std::unreachable();
}
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("centimeters value should not be discarded")]]
constexpr FloatingType units_to_centimeters(const FloatingType& units) constexpr FloatingType units_to_centimeters(const FloatingType& units)
{ {
return units; return units;
@@ -34,7 +98,7 @@ namespace omath::unreal_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("meters value should not be discarded")]]
constexpr FloatingType units_to_meters(const FloatingType& units) constexpr FloatingType units_to_meters(const FloatingType& units)
{ {
return units / static_cast<FloatingType>(100); return units / static_cast<FloatingType>(100);
@@ -42,7 +106,7 @@ namespace omath::unreal_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("kilometers value should not be discarded")]]
constexpr FloatingType units_to_kilometers(const FloatingType& units) constexpr FloatingType units_to_kilometers(const FloatingType& units)
{ {
return units_to_meters(units) / static_cast<FloatingType>(1000); return units_to_meters(units) / static_cast<FloatingType>(1000);
@@ -50,7 +114,7 @@ namespace omath::unreal_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters) constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{ {
return centimeters; return centimeters;
@@ -58,7 +122,7 @@ namespace omath::unreal_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType meters_to_units(const FloatingType& meters) constexpr FloatingType meters_to_units(const FloatingType& meters)
{ {
return meters * static_cast<FloatingType>(100); return meters * static_cast<FloatingType>(100);
@@ -66,7 +130,7 @@ namespace omath::unreal_engine
template<class FloatingType> template<class FloatingType>
requires std::is_floating_point_v<FloatingType> requires std::is_floating_point_v<FloatingType>
[[nodiscard]] [[nodiscard("units value should not be discarded")]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers) constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{ {
return meters_to_units(kilometers * static_cast<FloatingType>(1000)); return meters_to_units(kilometers * static_cast<FloatingType>(1000));
@@ -11,14 +11,30 @@ namespace omath::unreal_engine
class CameraTrait final class CameraTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("look-at angle result should not be discarded")]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept; constexpr static ViewAngles calc_look_at_angle(const Vector3<double>& cam_origin,
const Vector3<double>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
[[nodiscard]] return {PitchAngle::from_asin(direction.z), YawAngle::from_atan2(direction.y, direction.x),
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept; RollAngle::from_radians(0.f)};
[[nodiscard]] }
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept; [[nodiscard("view matrix result should not be discarded")]]
constexpr static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<double>& cam_origin) noexcept
{
return unreal_engine::calc_view_matrix(angles, cam_origin);
}
[[nodiscard("projection matrix result should not be discarded")]]
constexpr static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const double near_plane,
const double far_plane,
const NDCDepthRange ndc_depth_range) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near_plane, far_plane,
ndc_depth_range);
}
}; };
} // namespace omath::unreal_engine } // namespace omath::unreal_engine
@@ -10,7 +10,7 @@ namespace omath::unreal_engine
class MeshTrait final class MeshTrait final
{ {
public: public:
[[nodiscard]] [[nodiscard("rotation matrix result should not be discarded")]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation) static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{ {
return unreal_engine::rotation_matrix(rotation); return unreal_engine::rotation_matrix(rotation);
@@ -12,66 +12,73 @@ namespace omath::unreal_engine
class PredEngineTrait final class PredEngineTrait final
{ {
public: public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile, [[nodiscard("projectile position result should not be discarded")]]
const float pitch, const float yaw, static Vector3<double> predict_projectile_position(const projectile_prediction::Projectile<double>& projectile,
const float time, const float gravity) noexcept const double pitch, const double yaw,
const double time, const double gravity) noexcept
{ {
auto current_pos = projectile.m_origin const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), const auto fwd_d = forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)}) RollAngle::from_degrees(0)});
auto current_pos = launch_pos
+ Vector3<double>{fwd_d.x, fwd_d.y, fwd_d.z}
* projectile.m_launch_speed * time; * projectile.m_launch_speed * time;
current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f; current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5;
return current_pos; return current_pos;
} }
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target, [[nodiscard("target position result should not be discarded")]]
const float time, const float gravity) noexcept static Vector3<double> predict_target_position(const projectile_prediction::Target<double>& target,
const double time, const double gravity) noexcept
{ {
auto predicted = target.m_origin + target.m_velocity * time; auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne) if (target.m_is_airborne)
predicted.y -= gravity * (time * time) * 0.5f; predicted.y -= gravity * (time * time) * 0.5;
return predicted; return predicted;
} }
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept [[nodiscard("2d distance result should not be discarded")]]
static double calc_vector_2d_distance(const Vector3<double>& delta) noexcept
{ {
return std::sqrt(delta.x * delta.x + delta.z * delta.z); return std::sqrt(delta.x * delta.x + delta.z * delta.z);
} }
[[nodiscard]] [[nodiscard("height coordinate result should not be discarded")]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept static double get_vector_height_coordinate(const Vector3<double>& vec) noexcept
{ {
return vec.y; return vec.y;
} }
[[nodiscard]] [[nodiscard("viewpoint result should not be discarded")]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, static Vector3<double> calc_viewpoint_from_angles(const projectile_prediction::Projectile<double>& projectile,
Vector3<float> predicted_target_position, Vector3<double> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept const std::optional<double> projectile_pitch) noexcept
{ {
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin); 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())); 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}; 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: // Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down // 89 look up, -89 look down
[[nodiscard]] [[nodiscard("pitch angle result should not be discarded")]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept static double calc_direct_pitch_angle(const Vector3<double>& origin, const Vector3<double>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::asin(direction.z)); 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 [[nodiscard("yaw angle result should not be discarded")]]
static double calc_direct_yaw_angle(const Vector3<double>& origin, const Vector3<double>& view_to) noexcept
{ {
const auto direction = (view_to - origin).normalized(); const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::atan2(direction.y, direction.x)); return angles::radians_to_degrees(std::atan2(direction.y, direction.x));
}; }
}; };
} // namespace omath::unreal_engine } // namespace omath::unreal_engine
+130
View File
@@ -0,0 +1,130 @@
#pragma once
#include <cstdint>
#include <expected>
#include <string>
#include <string_view>
namespace omath::hashing
{
enum class Base64Error
{
INVALID_LENGTH,
INVALID_CHARACTER,
INVALID_PADDING
};
[[nodiscard("FNV-1a hash result should not be discarded")]]
constexpr std::uint64_t fnv1a(const std::string_view value) noexcept
{
std::uint64_t hash = 14695981039346656037ULL;
for (const char character : value)
{
hash ^= static_cast<std::uint8_t>(character);
hash *= 1099511628211ULL;
}
return hash;
}
[[nodiscard("CRC-32 checksum result should not be discarded")]]
constexpr std::uint32_t crc32(const std::string_view value) noexcept
{
std::uint32_t crc = 0xFFFFFFFFU;
for (const char character : value)
{
crc ^= static_cast<std::uint8_t>(character);
for (int bit = 0; bit < 8; bit++)
{
crc = crc & 1U ? (crc >> 1U) ^ 0xEDB88320U : crc >> 1U;
}
}
return crc ^ 0xFFFFFFFFU;
}
[[nodiscard("Base64 encoding result should not be discarded")]]
constexpr std::string base64_encode(const std::string_view value)
{
constexpr std::string_view alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string result;
result.reserve((value.size() + 2) / 3 * 4);
for (std::size_t index = 0; index < value.size(); index += 3)
{
const std::uint32_t first = static_cast<std::uint8_t>(value[index]);
const std::uint32_t second = index + 1 < value.size() ? static_cast<std::uint8_t>(value[index + 1]) : 0;
const std::uint32_t third = index + 2 < value.size() ? static_cast<std::uint8_t>(value[index + 2]) : 0;
const std::uint32_t chunk = first << 16U | second << 8U | third;
result += alphabet[(chunk >> 18U) & 0x3FU];
result += alphabet[(chunk >> 12U) & 0x3FU];
result += index + 1 < value.size() ? alphabet[(chunk >> 6U) & 0x3FU] : '=';
result += index + 2 < value.size() ? alphabet[chunk & 0x3FU] : '=';
}
return result;
}
[[nodiscard("Base64 decoding result should not be discarded")]]
constexpr std::expected<std::string, Base64Error> base64_decode(const std::string_view value)
{
if (value.size() % 4 != 0)
return std::unexpected(Base64Error::INVALID_LENGTH);
std::string result;
result.reserve(value.size() / 4 * 3);
for (std::size_t index = 0; index < value.size(); index += 4)
{
const auto decode_character = [](const char character) constexpr -> int
{
if (character >= 'A' && character <= 'Z')
return character - 'A';
if (character >= 'a' && character <= 'z')
return character - 'a' + 26;
if (character >= '0' && character <= '9')
return character - '0' + 52;
if (character == '+')
return 62;
if (character == '/')
return 63;
return -1;
};
const bool is_last_chunk = index + 4 == value.size();
const int first = decode_character(value[index]);
const int second = decode_character(value[index + 1]);
const int third = value[index + 2] == '=' ? -2 : decode_character(value[index + 2]);
const int fourth = value[index + 3] == '=' ? -2 : decode_character(value[index + 3]);
if (first < 0 || second < 0 || third == -1 || fourth == -1)
return std::unexpected(Base64Error::INVALID_CHARACTER);
if (third == -2)
{
if (fourth != -2 || !is_last_chunk || (second & 0x0F) != 0)
return std::unexpected(Base64Error::INVALID_PADDING);
}
else if (fourth == -2 && (!is_last_chunk || (third & 0x03) != 0))
return std::unexpected(Base64Error::INVALID_PADDING);
const std::uint32_t chunk = static_cast<std::uint32_t>(first) << 18U
| static_cast<std::uint32_t>(second) << 12U
| static_cast<std::uint32_t>(third < 0 ? 0 : third) << 6U
| static_cast<std::uint32_t>(fourth < 0 ? 0 : fourth);
result += static_cast<char>(chunk >> 16U);
if (third >= 0)
result += static_cast<char>(chunk >> 8U);
if (fourth >= 0)
result += static_cast<char>(chunk);
}
return result;
}
} // namespace omath::hashing
+214
View File
@@ -0,0 +1,214 @@
#pragma once
#ifdef OMATH_ENABLE_HOOKING
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <shared_mutex>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>
#include <d3d12.h>
#include <d3d9.h>
#include <dxgi.h>
#endif // _WIN32
#ifdef __linux__
#include <GL/glx.h>
#endif // __linux__
#include <safetyhook.hpp>
namespace omath::hooks
{
class HooksManager final
{
HooksManager() = default;
public:
#ifdef _WIN32
// IDXGISwapChain callbacks — shared between DX11 and DX12 (same interface, same signature).
using present_callback = std::function<void(IDXGISwapChain*, UINT, UINT)>;
using resize_buffers_callback = std::function<void(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT)>;
using execute_command_lists_callback =
std::function<void(ID3D12CommandQueue*, UINT, ID3D12CommandList* const*)>;
// IDirect3DDevice9 callbacks — DX9 only.
using dx9_present_callback =
std::function<void(IDirect3DDevice9*, const RECT*, const RECT*, HWND, const RGNDATA*)>;
using dx9_reset_callback = std::function<void(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*)>;
using dx9_end_scene_callback = std::function<void(IDirect3DDevice9*)>;
// OpenGL callback — Windows. Fires before the hooked buffer swap function calls the original.
using opengl_swap_buffers_callback = std::function<void(HDC)>;
// Return nullopt to pass the message to the original WndProc; return a value to intercept it.
using wnd_proc_callback = std::function<std::optional<LRESULT>(HWND, UINT, WPARAM, LPARAM)>;
#endif // _WIN32
#ifdef __linux__
// OpenGL/GLX callback — Linux. Fires before glXSwapBuffers calls the original.
using opengl_swap_buffers_callback = std::function<void(Display*, GLXDrawable)>;
#endif // __linux__
template<typename Callback>
using callback_ptr = std::shared_ptr<const Callback>;
[[nodiscard]] static HooksManager& get();
HooksManager(const HooksManager&) = delete;
HooksManager& operator=(const HooksManager&) = delete;
~HooksManager();
#ifdef _WIN32
[[nodiscard]] bool hook_dx9();
void unhook_dx9();
void set_on_dx9_present(dx9_present_callback callback);
void set_on_dx9_reset(dx9_reset_callback callback);
void set_on_dx9_end_scene(dx9_end_scene_callback callback);
[[nodiscard]] bool hook_dx11();
void unhook_dx11();
[[nodiscard]] bool hook_dx12();
void unhook_dx12();
#endif // _WIN32
[[nodiscard]] bool hook_opengl();
void unhook_opengl();
void set_on_opengl_swap_buffers(opengl_swap_buffers_callback callback);
#ifdef _WIN32
// Present and ResizeBuffers callbacks fire for whichever of DX11/DX12 is hooked.
void set_on_present(present_callback callback);
void set_on_resize_buffers(resize_buffers_callback callback);
void set_on_execute_command_lists(execute_command_lists_callback callback);
[[nodiscard]] bool hook_wnd_proc(HWND hwnd);
void unhook_wnd_proc();
void set_on_wnd_proc(wnd_proc_callback callback);
#endif // _WIN32
private:
#ifdef _WIN32
[[nodiscard]]
static HRESULT __stdcall dx9_present_detour(IDirect3DDevice9* p_device, const RECT* p_source_rect,
const RECT* p_dest_rect, HWND h_dest_window_override,
const RGNDATA* p_dirty_region);
[[nodiscard]]
static HRESULT __stdcall dx9_reset_detour(IDirect3DDevice9* p_device,
D3DPRESENT_PARAMETERS* p_presentation_parameters);
[[nodiscard]]
static HRESULT __stdcall dx9_end_scene_detour(IDirect3DDevice9* p_device);
[[nodiscard]]
static HRESULT __stdcall dx11_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags);
[[nodiscard]]
static HRESULT __stdcall dx11_resize_buffers_detour(IDXGISwapChain* p_swap_chain, UINT buffer_count, UINT width,
UINT height, DXGI_FORMAT new_format, UINT swap_chain_flags);
[[nodiscard]]
static HRESULT __stdcall dx12_present_detour(IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags);
[[nodiscard]]
static HRESULT __stdcall dx12_resize_buffers_detour(IDXGISwapChain* p_swap_chain, UINT buffer_count, UINT width,
UINT height, DXGI_FORMAT new_format, UINT swap_chain_flags);
static void __stdcall dx12_execute_command_lists_detour(ID3D12CommandQueue* p_command_queue,
UINT num_command_lists,
ID3D12CommandList* const* pp_command_lists);
[[nodiscard]]
static BOOL __stdcall opengl_wgl_swap_buffers_detour(HDC hdc);
[[nodiscard]]
static BOOL __stdcall opengl_swap_buffers_detour(HDC hdc);
[[nodiscard]]
static LRESULT __stdcall wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param);
#endif // _WIN32
#ifdef __linux__
static void opengl_glx_swap_buffers_detour(Display* display, GLXDrawable drawable);
#endif // __linux__
mutable std::shared_mutex m_hook_state_mutex;
#ifdef _WIN32
mutable std::shared_mutex m_dx9_present_mutex;
mutable std::shared_mutex m_dx9_reset_mutex;
mutable std::shared_mutex m_dx9_end_scene_mutex;
mutable std::shared_mutex m_present_mutex;
mutable std::shared_mutex m_resize_buffers_mutex;
mutable std::shared_mutex m_execute_command_lists_mutex;
mutable std::shared_mutex m_wnd_proc_mutex;
#endif // _WIN32
mutable std::shared_mutex m_opengl_swap_buffers_mutex;
#ifdef _WIN32
bool m_is_dx9_hooked = false;
bool m_is_dx11_hooked = false;
bool m_is_dx12_hooked = false;
bool m_is_wnd_proc_hooked = false;
HWND m_hooked_hwnd = nullptr;
WNDPROC m_original_wndproc = nullptr;
safetyhook::InlineHook m_dx9_present_hook;
safetyhook::InlineHook m_dx9_reset_hook;
safetyhook::InlineHook m_dx9_end_scene_hook;
safetyhook::InlineHook m_dx11_present_hook;
safetyhook::InlineHook m_dx11_resize_buffers_hook;
safetyhook::InlineHook m_dx12_present_hook;
safetyhook::InlineHook m_dx12_resize_buffers_hook;
safetyhook::InlineHook m_dx12_execute_command_lists_hook;
safetyhook::InlineHook m_opengl_wgl_swap_buffers_hook;
safetyhook::InlineHook m_opengl_swap_buffers_hook;
#endif // _WIN32
#ifdef __linux__
safetyhook::InlineHook m_opengl_glx_swap_buffers_hook;
#endif // __linux__
bool m_is_opengl_hooked = false;
#ifdef _WIN32
callback_ptr<dx9_present_callback> m_dx9_present_cb;
callback_ptr<dx9_reset_callback> m_dx9_reset_cb;
callback_ptr<dx9_end_scene_callback> m_dx9_end_scene_cb;
callback_ptr<present_callback> m_present_cb;
callback_ptr<resize_buffers_callback> m_resize_buffers_cb;
callback_ptr<execute_command_lists_callback> m_execute_command_lists_cb;
callback_ptr<wnd_proc_callback> m_wnd_proc_cb;
#endif // _WIN32
callback_ptr<opengl_swap_buffers_callback> m_opengl_swap_buffers_cb;
};
} // namespace omath::hooks
#else // !OMATH_ENABLE_HOOKING
namespace omath::hooks
{
class HooksManager final
{
HooksManager() = default;
public:
[[nodiscard]] static HooksManager& get();
HooksManager(const HooksManager&) = delete;
~HooksManager();
};
} // namespace omath::hooks
#endif
+24
View File
@@ -0,0 +1,24 @@
//
// Created by orange on 13.03.2026.
//
#pragma once
#include "omath/linear_algebra/vector2.hpp"
#include <array>
namespace omath::hud
{
class CanvasBox final
{
public:
CanvasBox() = default;
CanvasBox(Vector2<float> top, Vector2<float> bottom, float ratio = 4.f);
[[nodiscard("You have to use array")]]
std::array<Vector2<float>, 4> as_array() const;
Vector2<float> top_left_corner;
Vector2<float> top_right_corner;
Vector2<float> bottom_left_corner;
Vector2<float> bottom_right_corner;
};
} // namespace omath::hud
+291
View File
@@ -0,0 +1,291 @@
//
// Created by orange on 13.03.2026.
//
#pragma once
#include "canvas_box.hpp"
#include "entity_overlay_widgets.hpp"
#include "hud_renderer_interface.hpp"
#include "omath/3d_primitives/aabb.hpp"
#include "omath/linear_algebra/vector2.hpp"
#include "omath/utility/color.hpp"
#include <expected>
#include <memory>
#include <string_view>
#include <type_traits>
namespace omath::hud
{
enum class EntityOverlayError
{
NO_PROJECTED_VERTEX,
CENTER_PROJECTION_FAILED,
};
class EntityOverlay final
{
public:
EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom, float aspect,
const std::shared_ptr<HudRendererInterface>& renderer);
// ── Boxes ────────────────────────────────────────────────────────
EntityOverlay& add_2d_box(const Color& box_color, const Color& fill_color = Color{0.f, 0.f, 0.f, 0.f},
const Color& outline_color = Color{0.f, 0.f, 0.f, 0.f}, float thickness = 1.f);
EntityOverlay& add_2d_box(const Color& box_color, const Gradient& fill_gradient,
const Color& outline_color = Color{0.f, 0.f, 0.f, 0.f}, float thickness = 1.f);
EntityOverlay& add_cornered_2d_box(const Color& box_color, const Color& fill_color = Color{0.f, 0.f, 0.f, 0.f},
const Color& outline_color = Color{0.f, 0.f, 0.f, 0.f},
float corner_ratio_len = 0.2f, float thickness = 1.f);
EntityOverlay& add_dashed_box(const Color& color, float dash_len = 8.f, float gap_len = 5.f,
float thickness = 1.f);
// ── Bars ─────────────────────────────────────────────────────────
EntityOverlay& add_right_bar(const widget::BarPaint& color, const Color& outline_color, const Color& bg_color,
float width, float ratio, float offset = 5.f,
const std::optional<widget::Glow>& glow = std::nullopt);
EntityOverlay& add_left_bar(const widget::BarPaint& color, const Color& outline_color, const Color& bg_color,
float width, float ratio, float offset = 5.f,
const std::optional<widget::Glow>& glow = std::nullopt);
EntityOverlay& add_top_bar(const widget::BarPaint& color, const Color& outline_color, const Color& bg_color,
float height, float ratio, float offset = 5.f,
const std::optional<widget::Glow>& glow = std::nullopt);
EntityOverlay& add_bottom_bar(const widget::BarPaint& color, const Color& outline_color, const Color& bg_color,
float height, float ratio, float offset = 5.f,
const std::optional<widget::Glow>& glow = std::nullopt);
EntityOverlay& add_right_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
float width, float ratio, float dash_len, float gap_len,
float offset = 5.f);
EntityOverlay& add_left_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
float width, float ratio, float dash_len, float gap_len, float offset = 5.f);
EntityOverlay& add_top_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
float height, float ratio, float dash_len, float gap_len, float offset = 5.f);
EntityOverlay& add_bottom_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color,
float height, float ratio, float dash_len, float gap_len,
float offset = 5.f);
// ── Labels ───────────────────────────────────────────────────────
EntityOverlay& add_right_label(const widget::Paint& color, float offset, widget::Outlined outlined,
const std::string_view& text,
const std::optional<widget::Glow>& glow = std::nullopt);
EntityOverlay& add_left_label(const widget::Paint& color, float offset, widget::Outlined outlined,
const std::string_view& text,
const std::optional<widget::Glow>& glow = std::nullopt);
EntityOverlay& add_top_label(const widget::Paint& color, float offset, widget::Outlined outlined,
std::string_view text, const std::optional<widget::Glow>& glow = std::nullopt);
EntityOverlay& add_bottom_label(const widget::Paint& color, float offset, widget::Outlined outlined,
std::string_view text, const std::optional<widget::Glow>& glow = std::nullopt);
EntityOverlay& add_centered_top_label(const widget::Paint& color, float offset, widget::Outlined outlined,
const std::string_view& text,
const std::optional<widget::Glow>& glow = std::nullopt);
EntityOverlay& add_centered_bottom_label(const widget::Paint& color, float offset, widget::Outlined outlined,
const std::string_view& text,
const std::optional<widget::Glow>& glow = std::nullopt);
template<typename... Args>
requires(sizeof...(Args) > 0)
EntityOverlay& add_right_label(const widget::Paint& color, const float offset, const widget::Outlined outlined,
std::format_string<Args...> fmt, Args&&... args)
{
return add_right_label(color, offset, outlined,
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
}
template<typename... Args>
requires(sizeof...(Args) > 0)
EntityOverlay& add_left_label(const widget::Paint& color, const float offset, const widget::Outlined outlined,
std::format_string<Args...> fmt, Args&&... args)
{
return add_left_label(color, offset, outlined,
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
}
template<typename... Args>
requires(sizeof...(Args) > 0)
EntityOverlay& add_top_label(const widget::Paint& color, const float offset, const widget::Outlined outlined,
std::format_string<Args...> fmt, Args&&... args)
{
return add_top_label(color, offset, outlined,
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
}
template<typename... Args>
requires(sizeof...(Args) > 0)
EntityOverlay& add_bottom_label(const widget::Paint& color, const float offset, const widget::Outlined outlined,
std::format_string<Args...> fmt, Args&&... args)
{
return add_bottom_label(color, offset, outlined,
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
}
template<typename... Args>
requires(sizeof...(Args) > 0)
EntityOverlay& add_centered_top_label(const widget::Paint& color, const float offset,
const widget::Outlined outlined, std::format_string<Args...> fmt,
Args&&... args)
{
return add_centered_top_label(color, offset, outlined,
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
}
template<typename... Args>
requires(sizeof...(Args) > 0)
EntityOverlay& add_centered_bottom_label(const widget::Paint& color, const float offset,
const widget::Outlined outlined, std::format_string<Args...> fmt,
Args&&... args)
{
return add_centered_bottom_label(color, offset, outlined,
std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))});
}
// ── Spacers ─────────────────────────────────────────────────────
EntityOverlay& add_right_space_vertical(float size);
EntityOverlay& add_right_space_horizontal(float size);
EntityOverlay& add_left_space_vertical(float size);
EntityOverlay& add_left_space_horizontal(float size);
EntityOverlay& add_top_space_vertical(float size);
EntityOverlay& add_top_space_horizontal(float size);
EntityOverlay& add_bottom_space_vertical(float size);
EntityOverlay& add_bottom_space_horizontal(float size);
// ── Progress rings ──────────────────────────────────────────────
EntityOverlay& add_right_progress_ring(const Color& color, const Color& bg, float radius, float ratio,
float thickness = 2.f, float offset = 5.f, int segments = 0);
EntityOverlay& add_left_progress_ring(const Color& color, const Color& bg, float radius, float ratio,
float thickness = 2.f, float offset = 5.f, int segments = 0);
EntityOverlay& add_top_progress_ring(const Color& color, const Color& bg, float radius, float ratio,
float thickness = 2.f, float offset = 5.f, int segments = 0);
EntityOverlay& add_bottom_progress_ring(const Color& color, const Color& bg, float radius, float ratio,
float thickness = 2.f, float offset = 5.f, int segments = 0);
// ── Icons ────────────────────────────────────────────────────────
EntityOverlay& add_right_icon(const std::any& texture_id, float width, float height,
const Color& tint = Color{1.f, 1.f, 1.f, 1.f}, float offset = 5.f);
EntityOverlay& add_left_icon(const std::any& texture_id, float width, float height,
const Color& tint = Color{1.f, 1.f, 1.f, 1.f}, float offset = 5.f);
EntityOverlay& add_top_icon(const std::any& texture_id, float width, float height,
const Color& tint = Color{1.f, 1.f, 1.f, 1.f}, float offset = 5.f);
EntityOverlay& add_bottom_icon(const std::any& texture_id, float width, float height,
const Color& tint = Color{1.f, 1.f, 1.f, 1.f}, float offset = 5.f);
// ── Misc ─────────────────────────────────────────────────────────
EntityOverlay& add_snap_line(const Vector2<float>& start_pos, const Color& color, float width);
EntityOverlay& add_skeleton(const Color& color, float thickness = 1.f);
EntityOverlay& add_skeleton(std::span<const widget::Bone> bones, const Color& color, float thickness = 1.f);
// ── Declarative interface ─────────────────────────────────────────
/// Pass any combination of widget:: descriptor structs (and std::optional<W>
/// from when()) to render them all in declaration order.
template<typename... Widgets>
EntityOverlay& contents(Widgets&&... widgets)
{
(dispatch(std::forward<Widgets>(widgets)), ...);
return *this;
}
template<class Camera, class Aabb>
[[nodiscard]]
static std::expected<EntityOverlay, EntityOverlayError>
from_aabb(const Camera& camera, const Aabb& aabb, const float aspect,
const std::shared_ptr<HudRendererInterface>& renderer)
{
using ProjectedVector = std::invoke_result_t<decltype(&Aabb::center), const Aabb&>;
ProjectedVector top;
ProjectedVector bottom;
bool has_projected_vertex = false;
for (const auto& vertex : aabb.vertices())
{
if (auto projected = camera.world_to_screen_unclipped(vertex))
{
if (!has_projected_vertex)
{
top = *projected;
bottom = *projected;
has_projected_vertex = true;
continue;
}
if (projected->y < top.y)
top = *projected;
if (projected->y > bottom.y)
bottom = *projected;
}
}
if (!has_projected_vertex)
return std::unexpected(EntityOverlayError::NO_PROJECTED_VERTEX);
auto center = camera.world_to_screen_unclipped(aabb.center());
if (!center)
return std::unexpected(EntityOverlayError::CENTER_PROJECTION_FAILED);
const auto center_x = static_cast<float>(center->x);
return EntityOverlay{
{center_x, static_cast<float>(top.y)}, {center_x, static_cast<float>(bottom.y)}, aspect, renderer};
}
private:
// optional<W> dispatch — enables when() conditional widgets
template<typename W>
void dispatch(const std::optional<W>& w)
{
if (w)
dispatch(*w);
}
void dispatch(const widget::Box& box);
void dispatch(const widget::CanvasGlow& canvas_glow);
void dispatch(const widget::CorneredBox& cornered_box);
void dispatch(const widget::DashedBox& dashed_box);
void dispatch(const widget::RightSide& right_side);
void dispatch(const widget::LeftSide& left_side);
void dispatch(const widget::TopSide& top_side);
void dispatch(const widget::BottomSide& bottom_side);
void dispatch(const widget::Skeleton& skeleton);
void dispatch(const widget::SnapLine& snap_line);
void dispatch(const widget::ScanMarker& scan_marker);
void dispatch(const widget::AimDot& aim_dot);
void dispatch(const widget::ProjectileAim& proj_widget);
void draw_progress_ring(const Vector2<float>& center, const widget::ProgressRing& ring);
void draw_label(const Vector2<float>& position, const widget::Paint& paint, widget::Outlined outlined,
const std::string_view& text, const std::optional<widget::Glow>& glow);
void draw_glow_polyline(const std::span<const Vector2<float>>& points, const widget::Glow& glow,
float thickness) const;
void draw_glow_line(const Vector2<float>& from, const Vector2<float>& to, const widget::Glow& glow,
float thickness) const;
void draw_glow_rectangle(const Vector2<float>& min, const Vector2<float>& max,
const std::optional<widget::Glow>& glow) const;
void draw_canvas_glow(const widget::CanvasGlow& canvas_glow) const;
void draw_filled_rectangle(const Vector2<float>& min, const Vector2<float>& max,
const widget::Paint& paint) const;
[[nodiscard]]
static widget::Paint resolve_bar_paint(const widget::BarPaint& paint, float ratio, bool vertical);
void draw_dashed_line(const Vector2<float>& from, const Vector2<float>& to, const Color& color, float dash_len,
float gap_len, float thickness) const;
void draw_dashed_fill(const Vector2<float>& origin, const Vector2<float>& step_dir,
const Vector2<float>& perp_dir, float full_len, float filled_len, const Color& fill_color,
const Color& split_color, float dash_len, float gap_len) const;
CanvasBox m_canvas;
Vector2<float> m_text_cursor_right;
Vector2<float> m_text_cursor_top;
Vector2<float> m_text_cursor_bottom;
Vector2<float> m_text_cursor_left;
std::shared_ptr<HudRendererInterface> m_renderer;
};
} // namespace omath::hud
@@ -0,0 +1,325 @@
//
// Created by orange on 15.03.2026.
//
#pragma once
#include "gradient.hpp"
#include "omath/linear_algebra/vector2.hpp"
#include "omath/utility/color.hpp"
#include <any>
#include <array>
#include <initializer_list>
#include <optional>
#include <span>
#include <string_view>
#include <type_traits>
#include <variant>
#include <vector>
namespace omath::hud::widget
{
struct Paint : std::variant<Color, Gradient>
{
using std::variant<Color, Gradient>::variant;
constexpr Paint(const float red, const float green, const float blue, const float alpha)
: std::variant<Color, Gradient>(Color{red, green, blue, alpha})
{
}
};
struct BarGradient
{
Color empty_color;
Color full_color;
};
struct BarPaint : std::variant<Color, Gradient, BarGradient>
{
using std::variant<Color, Gradient, BarGradient>::variant;
constexpr BarPaint(const float red, const float green, const float blue, const float alpha)
: std::variant<Color, Gradient, BarGradient>(Color{red, green, blue, alpha})
{
}
BarPaint(const Paint& paint)
{
std::visit(
[this](const auto& value)
{
this->template emplace<std::decay_t<decltype(value)>>(value);
},
paint);
}
};
// ── Overloaded helper for std::visit ──────────────────────────────────────
template<typename... Ts>
struct Overloaded : Ts...
{
using Ts::operator()...;
};
template<typename... Ts>
Overloaded(Ts...) -> Overloaded<Ts...>;
struct Glow
{
Color color;
float radius = 4.f;
float intensity = 1.f;
};
struct CanvasGlow
{
Glow glow;
int layers = 64;
float rounding = 20.f;
};
// ── Standalone widgets ────────────────────────────────────────────────────
struct Box
{
Color color;
Paint fill{Color{0.f, 0.f, 0.f, 0.f}};
Color outline{0.f, 0.f, 0.f, 0.f};
float thickness = 1.f;
std::optional<Glow> glow = std::nullopt;
};
enum class Outlined
{
Off,
On,
};
struct CorneredBox
{
Color color;
Color fill{0.f, 0.f, 0.f, 0.f};
Color outline{0.f, 0.f, 0.f, 0.f};
float corner_ratio = 0.2f;
float thickness = 1.f;
std::optional<Glow> glow = std::nullopt;
};
struct DashedBox
{
Color color;
float dash_len = 8.f;
float gap_len = 5.f;
float thickness = 1.f;
};
struct Bone
{
Vector2<float> start;
Vector2<float> end;
};
struct Skeleton
{
std::vector<Bone> bones;
Color color;
float thickness = 1.f;
Skeleton(const Color& color, const float thickness = 1.f): color(color), thickness(thickness)
{
}
Skeleton(const std::span<const Bone> bones, const Color& color, const float thickness = 1.f)
: bones(bones.begin(), bones.end()), color(color), thickness(thickness)
{
}
Skeleton(const std::initializer_list<Bone> bones, const Color& color, const float thickness = 1.f)
: bones(bones), color(color), thickness(thickness)
{
}
template<std::size_t Size>
Skeleton(const std::array<Bone, Size>& bones, const Color& color, const float thickness = 1.f)
: Skeleton(std::span<const Bone>{bones}, color, thickness)
{
}
};
struct SnapLine
{
Vector2<float> start;
Color color;
float width;
};
struct ScanMarker
{
Color color;
Color outline{0.f, 0.f, 0.f, 0.f};
float outline_thickness = 1.f;
};
/// Dot at an absolute screen position.
struct AimDot
{
Vector2<float> position;
Color color;
float radius = 3.f;
};
struct ProjectileAim
{
enum class Figure
{
CIRCLE,
SQUARE,
};
Vector2<float> position;
Color color;
float size = 3.f;
float line_size = 1.f;
Figure figure = Figure::SQUARE;
};
// ── Side-agnostic widgets (used inside XxxSide containers) ────────────────
/// A filled bar. `size` is width for left/right sides, height for top/bottom.
struct Bar
{
BarPaint color;
Color outline;
Color bg;
float size;
float ratio;
float offset = 5.f;
std::optional<Glow> glow = std::nullopt;
};
/// A dashed bar. Same field semantics as Bar plus dash parameters.
struct DashedBar
{
Color color;
Color outline;
Color bg;
float size;
float ratio;
float dash_len;
float gap_len;
float offset = 5.f;
};
struct Label
{
Paint color;
float offset;
Outlined outlined;
std::string_view text;
std::optional<Glow> glow = std::nullopt;
};
/// Wraps a Label to request horizontal centering (only applied in TopSide / BottomSide).
template<typename W>
struct Centered
{
W child;
};
template<typename W>
Centered(W) -> Centered<W>;
/// Empty vertical gap that advances the Y cursor without drawing.
struct SpaceVertical
{
float size;
};
/// Empty horizontal gap that advances the X cursor without drawing.
struct SpaceHorizontal
{
float size;
};
struct ProgressRing
{
Color color;
Color bg{0.3f, 0.3f, 0.3f, 0.5f};
float radius = 12.f;
float ratio;
float thickness = 2.f;
float offset = 5.f;
int segments = 32;
};
struct Icon
{
std::any texture_id;
float width;
float height;
Color tint{1.f, 1.f, 1.f, 1.f};
float offset = 5.f;
};
// ── Side widget variant ───────────────────────────────────────────────────
struct None
{
}; ///< No-op placeholder — used by widget::when for disabled elements.
using SideWidget = std::variant<None, Bar, DashedBar, Label, Centered<Label>, SpaceVertical, SpaceHorizontal,
ProgressRing, Icon>;
// ── Side containers ───────────────────────────────────────────────────────
// Storing std::initializer_list<SideWidget> is safe here: the backing array
// is a const SideWidget[] on the caller's stack whose lifetime matches the
// temporary side-container object, which is consumed within the same
// full-expression by EntityOverlay::dispatch. No heap allocation occurs.
struct RightSide
{
std::initializer_list<SideWidget> children;
RightSide(const std::initializer_list<SideWidget> c): children(c)
{
}
};
struct LeftSide
{
std::initializer_list<SideWidget> children;
LeftSide(const std::initializer_list<SideWidget> c): children(c)
{
}
};
struct TopSide
{
std::initializer_list<SideWidget> children;
TopSide(const std::initializer_list<SideWidget> c): children(c)
{
}
};
struct BottomSide
{
std::initializer_list<SideWidget> children;
BottomSide(const std::initializer_list<SideWidget> c): children(c)
{
}
};
} // namespace omath::hud::widget
namespace omath::hud::widget
{
/// Inside XxxSide containers: returns the widget as a SideWidget when condition is true,
/// or None{} otherwise. Preferred over hud::when for types inside the SideWidget variant.
template<typename W>
requires std::constructible_from<SideWidget, W>
SideWidget when(const bool condition, W widget)
{
if (condition)
return SideWidget{std::move(widget)};
return None{};
}
} // namespace omath::hud::widget
namespace omath::hud
{
/// Top-level: returns an engaged optional<W> when condition is true, std::nullopt otherwise.
/// Designed for use with EntityOverlay::contents() for top-level widget types.
template<typename W>
std::optional<W> when(const bool condition, W widget)
{
if (condition)
return std::move(widget);
return std::nullopt;
}
} // namespace omath::hud
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "omath/utility/color.hpp"
namespace omath::hud
{
enum class GradientDirection
{
RightToLeft,
LeftToRight,
};
struct Gradient final
{
Color top_left;
Color top_right;
Color bottom_right;
Color bottom_left;
bool animated = false;
float animation_speed = 4.f;
float animation_spread = 0.7f;
GradientDirection direction = GradientDirection::RightToLeft;
};
} // namespace omath::hud

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