Compare commits

...

100 Commits

Author SHA1 Message Date
13a68eea45 right-handed DirectionVector
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m49s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 30s
2024-11-27 19:46:58 -05:00
79e617b780 RoundTrip angle conversion.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m23s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 31s
2024-11-19 15:39:06 -05:00
aaea5ff53e AxisAngle FromQuaternion
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m52s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
2024-11-19 09:13:37 -05:00
2caa4c8412 Quaternion from EulerAngleXYZ
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m14s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2024-11-18 21:51:40 -05:00
bb1b1b5a13 Remove EulerAngle & Add EulerAngleXYZ.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m38s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2024-11-18 20:39:08 -05:00
80b752128c Update Vector3.cpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m24s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 30s
Added missing Vector3::One
2024-10-31 02:09:23 -04:00
3fc9ca3954 Update Vector3.hpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m36s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
fixed a documentation copy paste mistake
2024-10-31 02:02:03 -04:00
af75700c46 Implemented Bezier Curve in 3 Dimensions.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m13s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 22s
2024-10-25 13:06:26 -04:00
bf794ce092 Sending up work
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m37s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 29s
2024-10-23 14:16:41 -04:00
192b93ded4 Update Doxyfile to check if documentation-rollout toolchain is working properly. (I think no?)
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 4m44s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 22s
2024-10-22 09:39:48 -04:00
85aac1c192 Update CMakeLists.txt
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 6m44s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
Get rid of event cmake warning.
2024-10-10 12:14:21 -04:00
29db4f0792 Implement rest of LineSegment2D members.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m24s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
2024-10-07 15:36:48 -04:00
143b7e7279 Implementing LineSegment2D class
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m30s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 28s
2024-10-06 22:25:01 -04:00
3861741ff2 Remove inlining of Capsule::AnyPointFast() (seems to prevent compilation on -O3 optimization level)
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m16s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 28s
2024-10-04 12:36:43 -04:00
262603a496 Merge remote-tracking branch 'origin/main'
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m25s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 23s
2024-10-01 10:38:56 -04:00
5e0e4b8349 Implementing LineSegment2D class 2024-10-01 10:38:49 -04:00
Redacted
756316169e Update README.md
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 5m15s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 20s
2024-09-17 14:46:53 -04:00
237c318bf1 fix https://git.redacted.cc/Redacted/ReWindow.git using rebitch
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m24s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2024-08-26 19:49:12 -04:00
2b5978f11b Matrix2x2 ptr()
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m25s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 20s
2024-08-24 10:32:05 -04:00
155296ac88 Merge remote-tracking branch 'origin/main'
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m11s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 20s
2024-08-22 18:12:39 -04:00
71474081bc Changes by dawsh 2024-08-22 18:12:07 -04:00
Redacted
53badd110a Update CMakeLists.txt
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 6m2s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 20s
2024-08-22 12:03:49 -04:00
05d664ecba migrate to new JTest API
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 6m37s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
2024-08-21 20:21:14 -04:00
a4e0934f17 Update Triangle2D.cpp
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 5m55s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2024-08-20 21:44:42 -04:00
47993084ff Set up shapes for dynamic cast
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m29s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 22s
2024-08-20 21:37:14 -04:00
fe654611eb Ideally fixed!!!
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m8s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2024-08-11 22:13:03 -04:00
94a3164930 Fixed windows errors
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m33s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
2024-08-07 23:25:16 -04:00
7bb94f9897 Fixed windows errors
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 6m13s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
2024-08-07 23:17:59 -04:00
b58582d7c4 Fixed RNG Float results!!!
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 6m31s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2024-08-07 14:00:46 -04:00
11062e761a Actually builds now (Sorry bout that)
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m13s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 20s
2024-08-05 18:24:51 -04:00
6aa7dc9745 Fixed undefined reference in Bezier curve. Other additions and fixes included.
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 58s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2024-08-05 15:14:06 -04:00
c10c63a7e1 Merge remote-tracking branch 'origin/main'
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m0s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 25s
2024-07-31 14:22:22 -04:00
a3d4562dec Implement Circle header, rename all files to use .hpp extension. 2024-07-31 14:22:10 -04:00
8241adb88b reci test
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m26s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 22s
2024-07-29 19:20:47 -04:00
38c8afa0e6 Merge remote-tracking branch 'refs/remotes/origin/main'
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m28s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 19s
reci bullshit
2024-07-29 19:17:33 -04:00
25000760e1 reci test 2024-07-29 19:17:03 -04:00
1c2250dbc7 Update J3ML.cpp
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 58s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
Fix for GCC 13.3
2024-07-29 19:11:02 -04:00
292517ecc5 reci test
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m38s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
2024-07-29 18:55:07 -04:00
482cac72e7 reci test
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 20s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 28s
2024-07-29 18:51:16 -04:00
779b377548 Make sure to use lua5.3
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m36s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 33s
2024-07-29 18:47:01 -04:00
03bdd87398 Updated to use new ReCI
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 14s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 33s
2024-07-29 18:45:06 -04:00
3e607d310d Add PBVolume.hpp
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m1s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2024-07-10 14:22:08 -04:00
a03c2cbfb0 Add OBB::ClosestPoint Enclose Distance Contains 2024-07-10 14:21:56 -04:00
c24a352350 Add Plane::ProjectToNegativeHalf ProjectToPositiveHalf 2024-07-10 14:21:34 -04:00
c5e5958066 Re-add vertices to Triangle2D for compat with JGL 2024-07-10 14:20:48 -04:00
a3963a4f66 Add SparseMatrix.hpp - Currently just a stub header, research into sparse matrices forthcoming. 2024-07-10 14:20:34 -04:00
ed9fbc7ab8 Header Fixes 2024-07-10 14:20:11 -04:00
e8b907d86a Implement Matrix4x4::Determinant4 IsIdentity 2024-07-10 14:19:46 -04:00
68c6f6c9f8 Implement Polygon::ToPolyhedron 2024-07-10 14:19:16 -04:00
2f9cb5dd87 Implement Polyhedron::Contains Intersects ContainsConvex ClosestPoint IsNull 2024-07-10 14:19:04 -04:00
b07e926cd9 Add copyright signature to main file 2024-07-10 14:18:10 -04:00
98802f2b0d Implement J3ML Core Math & Unit Tests 2024-07-10 14:17:56 -04:00
cbfbc6acf0 Implement Frustum Unit Tests (Currently failing) 2024-07-10 14:17:34 -04:00
6684c64ab7 Frustum partial implementation. 2024-07-10 14:17:20 -04:00
62aeb36628 Header include fixes and implementation of more unit tests. 2024-07-10 14:16:51 -04:00
926defe7bb Implement AABB Unit Tests 2024-07-10 14:15:55 -04:00
52e1670b80 Several files renamed to match new consistency-style of preferring .hpp over .h to indicate C++ Headers 2024-07-10 14:15:25 -04:00
6ae876c435 Implement Sphere documentation 2024-07-10 14:14:21 -04:00
a8dd46efc3 Implement Triangle::NumFaces NumEdges NumVertices CenterPoint Intersects(LineSegment) 2024-07-10 14:13:51 -04:00
2195752e1e Implement Vector2::RandomBox 2024-07-10 14:13:14 -04:00
27efa7da92 Implement Vector3::ScaleToLength & ToString() 2024-07-10 14:12:47 -04:00
a6612fac4d Further implementation of core math functions.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m20s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 23s
2024-07-06 21:45:11 -04:00
5db85bf035 Remove Reinterpret from RNG file 2024-07-06 21:44:45 -04:00
57a74fd61c Moved Union Reinterpret implementation to Reinterpret.hpp 2024-07-06 21:44:28 -04:00
9253cfc8c7 Fixed several recursive header issues, refactored Math lib, began implementing core mathematical functions as wrappers around stdmath, will implement SSE and lookup tables later.
All checks were successful
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 1m13s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 19s
2024-07-05 16:13:13 -04:00
bc7adae8af reci test
All checks were successful
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
Run ReCI Build Test / Explore-Gitea-Actions (push) Successful in 41s
2024-07-04 14:58:42 -04:00
be6e71a7eb reci test
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 12s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 19s
2024-07-04 14:57:40 -04:00
66ca06a5b8 reci test
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 13s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2024-07-04 14:56:50 -04:00
adcf0e68d7 reci test
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 0s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2024-07-04 14:56:15 -04:00
e07f2c9601 reci test
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 0s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
2024-07-04 14:55:48 -04:00
bcbf97d2c7 ReCI test
Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 0s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 18s
2024-07-04 14:47:58 -04:00
4db75da577 test ReCI
All checks were successful
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2024-07-04 14:45:55 -04:00
89561e4f2f Use ReCI workflow
All checks were successful
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 22s
2024-07-04 14:44:57 -04:00
c71cabf523 action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 3s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Failing after 13m16s
2024-07-03 15:15:41 -04:00
5185f631ba action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 3s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Failing after 15m23s
2024-07-03 14:34:19 -04:00
962251d6a7 action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 3s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Has been cancelled
2024-07-03 12:50:58 -04:00
14d1c466af action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 0s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Has been cancelled
2024-07-03 12:41:02 -04:00
c722fdfc63 action test
Some checks failed
Build Docs With Doxygen / Explore-Gitea-Actions (push) Has been cancelled
Run tests / Explore-Gitea-Actions (push) Failing after 0s
2024-07-03 12:01:47 -04:00
f01392a64f action test
Some checks failed
Build Docs With Doxygen / Explore-Gitea-Actions (push) Waiting to run
Run tests / Explore-Gitea-Actions (push) Failing after 1s
2024-07-03 11:57:46 -04:00
985ac12509 action test
Some checks failed
Build Docs With Doxygen / Explore-Gitea-Actions (push) Waiting to run
Run tests / Explore-Gitea-Actions (push) Failing after 1s
2024-07-03 11:55:39 -04:00
ad09bcaeb1 action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 14m32s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Failing after 3m6s
2024-07-03 10:31:07 -04:00
a11b4b6d3c action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 5s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Failing after 3s
2024-07-03 10:21:16 -04:00
e2500da25d action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Waiting to run
Build Docs With Doxygen / Explore-Gitea-Actions (push) Failing after 6s
2024-07-03 10:21:06 -04:00
9aa3671e6e action test
Some checks are pending
Run tests / Explore-Gitea-Actions (push) Waiting to run
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 21s
2024-07-03 10:12:49 -04:00
be64b705ac action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Waiting to run
Build Docs With Doxygen / Explore-Gitea-Actions (push) Has been cancelled
2024-07-03 10:00:32 -04:00
afdb7fd428 action test
Some checks failed
Build Docs With Doxygen / Explore-Gitea-Actions (push) Has been cancelled
Run tests / Explore-Gitea-Actions (push) Has been cancelled
2024-07-03 09:59:21 -04:00
76cc842838 action test
Some checks failed
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 20s
Run tests / Explore-Gitea-Actions (push) Has been cancelled
2024-07-03 09:57:10 -04:00
6484e02dfb action test
Some checks are pending
Run tests / Explore-Gitea-Actions (push) Waiting to run
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
2024-07-03 09:56:07 -04:00
39613184b3 action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 1s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2024-07-03 09:54:39 -04:00
192e3fa709 action test
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 1s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 27s
2024-07-03 09:52:23 -04:00
eb28751263 canonical-ubuntu-24.04
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 0s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2024-07-03 09:50:55 -04:00
0552de6e18 test actions
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 2s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 18s
2024-07-03 09:47:01 -04:00
daf85d8248 update ubuntu image to use to ubuntu-24.04
Some checks failed
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 24s
Run tests / Explore-Gitea-Actions (push) Failing after 1s
2024-07-03 09:45:22 -04:00
a21dca5cfb update ubuntu image to use to ubuntu-latest
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 16s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 23s
2024-07-03 09:42:19 -04:00
33df78fbf4 test gittea actions
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 16s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 26s
2024-07-03 09:37:17 -04:00
cadd724990 test gittea actions
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 24s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 25s
2024-07-03 09:30:13 -04:00
b96880c1d1 Merge remote-tracking branch 'origin/main'
Some checks failed
Run tests / Explore-Gitea-Actions (push) Failing after 2m23s
Build Docs With Doxygen / Explore-Gitea-Actions (push) Successful in 19s
2024-07-01 14:23:14 -04:00
ea40c40725 Implement Matrix4x4::ToEulerAngle Matrix4x4::ctor(EulerAngle) Matrix4x4::ctor(Quaternion) Matrix4x4::ToQuat() 2024-07-01 14:23:03 -04:00
552715f443 Implement Matrix3x3::ToEulerAngle and Matrix3x3::ctor(EulerAngle) 2024-07-01 14:22:10 -04:00
cc564b14fe Implement EulerAngle::ToQuaternion EulerAngle::ToAxisAngle 2024-07-01 14:14:19 -04:00
133 changed files with 7644 additions and 2848 deletions

View File

@@ -1,10 +1,14 @@
name: Run tests
run-name: Run tests for ${{ gitea.repository }}.
name: Run ReCI Build Test
run-name: Run ReCI Build Test For ${{ gitea.repository }}.
on: [push]
jobs:
Explore-Gitea-Actions:
runs-on: ubuntu-22.04
env:
RECI_GIT: https://git.redacted.cc/maxine/ReCI
RECI: /RECI
JTEST_EXEC: ./cmake-build-debug/tests/J3MLTestSuite
steps:
- run: echo "The job was automatically triggered by a ${{ gitea.event_name }} event."
- run: echo "This job is now running on a ${{ runner.os }} server hosted by Gitea!"
@@ -13,12 +17,8 @@ jobs:
uses: actions/checkout@v3
- run: echo "The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "The workflow is now ready to run your tests on the runner."
- run: echo "Installing cmake"
- run: apt-get update && apt-get install -y cmake
- run: echo "Build ${{ gitea.repository }}"
- run: mkdir build
- run: cd build && cmake ../
- run: cd build && make
- run: echo "Build and run ${{ gitea.repository }} test suite"
- run: cd build && make test
- run: echo "Install toolchain and run ReCI build test"
- run: apt-get update && apt-get install -y lua5.3 git && git clone $RECI_GIT $RECI
- run: lua $RECI/reci.lua -f $RECI/scripts/buildtools.reci -f $RECI/scripts/buildtest.reci -r "JTEST_EXEC=$JTEST_EXEC"
- run: echo this only exists so I can rerun the action3
- run: echo "This job's status is ${{ job.status }}."

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.18)
cmake_minimum_required(VERSION 3.18...3.27)
PROJECT(J3ML
VERSION 1.1
LANGUAGES CXX
@@ -34,7 +34,7 @@ set_target_properties(J3ML PROPERTIES LINKER_LANGUAGE CXX)
CPMAddPackage(
NAME jtest
URL https://git.redacted.cc/josh/jtest/archive/Prerelease-5.zip
URL https://git.redacted.cc/josh/jtest/archive/Release-1.4.zip
)
target_include_directories(J3ML PUBLIC ${jtest_SOURCE_DIR}/include)
@@ -58,4 +58,4 @@ target_link_libraries(MathDemo ${PROJECT_NAME})
if(WIN32)
#target_compile_options(MathDemo PRIVATE -mwindows)
endif()
endif()

View File

@@ -48,13 +48,13 @@ PROJECT_NAME = J3ML
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER =
PROJECT_NUMBER = 3.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "Linear Algebra, Geometry, and Algorithms in C++"
PROJECT_BRIEF = "Linear Algebra, Geometry, and Algorithms in C++ (v3.1)"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55

View File

@@ -4,7 +4,7 @@
Yet Another C++ Math Standard
J3ML is a "Modern C++" C++ library designed to provide comprehensive support for 3D mathematical operations commonly used in computer graphics, game development, physics simulations, and related fields. It offers a wide range of functionalities to simplify the implementation of complex mathematical operations in your projects.
J3ML is a "Modern C++" library designed to provide comprehensive support for 3D mathematical operations commonly used in computer graphics, game development, physics simulations, and related fields. It offers a wide range of functionalities to simplify the implementation of complex mathematical operations in your projects.
![Static Badge](https://img.shields.io/badge/Lit-Based-%20)
@@ -50,7 +50,7 @@ Documentation is automatically generated from latest commit and is hosted at htt
# Contributing
Contributions to J3ML are welcome! If you find a bug, have a feature request, or would like to contribute code, please submit an issue or pull request to the GitHub repository.
Contributions to J3ML are welcome! If you find a bug, have a feature request, or would like to contribute code, please submit an issue or pull request to the repository.
# License
@@ -59,3 +59,5 @@ J3ML is licensed under the Public Domain. See the LICENSE file for details.
# Acknowledgements
J3ML is developed and maintained by Joshua O'Leary from Redacted Software and contributors. Special thanks to William J Tomasine II.
bump :3 :3 :3 :3 :3 :3 :3 :3 :3 :3 :3 :3 :3 :3 :3 :3 :3

View File

@@ -1,45 +0,0 @@
/// @file Bezier.h
/// @desc Cubic Bezier Curve impl. using De Casteljau's Method
/// @author Joshua O'Leary
/// @edited 20 June 2024
/// @version 2
/// @copyright (c) Redacted Software 2024
/// @license Unlicense - Public Domain
#pragma once
// Bezier Method:
// p = (1-t)^3 * P0 + 3*t*(1-t)^2*P1 + 3*t^2*(1-t)*P2 + t^3*P3;
// For cubics:
// p = (1-t)^2 * P0 + 2*(1-t)*t*P1 + t*t*P2;
// Transcribed from here: explicit form and derivative
// https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
#include "J3ML/LinearAlgebra/Vector2.h"
namespace J3ML::Algorithm
{
using namespace J3ML::LinearAlgebra;
template <typename T>
inline T Square(T f) { return f * f; }
template <typename T>
inline T Cube(T f) { return f * f * f; }
template <typename T>
inline T Bezier(float t, const T& p0, const T& p1, const T& p2, const T& p3)
{
return Cube(1 - t) * p0 + 3 * Square(1 - t) * t * p1 + 3 * (1 - t) * Square(t) * p2 + Cube(t) * p3;
}
inline Vector2 Bezier(float t, const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3);
// Tangent
Vector2 BezierDerivative(float t, const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3);
// Normal
Vector2 BezierNormal(float t, const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3);
}

View File

@@ -0,0 +1,89 @@
/// @file Bezier.hpp
/// @desc Cubic Bezier Curve impl. using De Casteljau's Method
/// @author Joshua O'Leary
/// @edited 20 June 2024
/// @version 2
/// @copyright (c) Redacted Software 2024
/// @license Unlicense - Public Domain
#pragma once
// Bezier Method:
// p = (1-t)^3 * P0 + 3*t*(1-t)^2*P1 + 3*t^2*(1-t)*P2 + t^3*P3;
// For cubics:
// p = (1-t)^2 * P0 + 2*(1-t)*t*P1 + t*t*P2;
// Transcribed from here: explicit form and derivative
// https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
#include "J3ML/LinearAlgebra/Vector2.hpp"
namespace J3ML::Algorithm
{
using namespace J3ML::LinearAlgebra;
template <typename T>
inline T Square(T f) { return f * f; }
template <typename T>
inline T Cube(T f) { return f * f * f; }
/// Four points P0, P1, P2, P3 in the plane space define a cubic Bezier curve.
/// The curve can be modeled as a polynomial of third order.
/// The curve starts at P0, going toward P1, and arrives at P3 coming from the direction of P2.
/// Usually, it will not pass through P1, or P2, these points are only there to provide directional information.
template <typename T>
inline T Bezier(float t, const T& p0, const T& p1, const T& p2, const T& p3)
{
return Cube(1 - t) * p0 + 3 * Square(1 - t) * t * p1 + 3 * (1 - t) * Square(t) * p2 + Cube(t) * p3;
}
/// Computes a point along a 2-dimensional Cubic Bezier curve.
/// @param t The normalized distance along the curve to compute, with range of [0, 1].
/// @param p0 The start-point of the curve.
/// @param p1
/// @param p2
/// @param p3 The end-point of the curve.
Vector2 Bezier(float t, const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3);
/// Computes a point along the tangent of a 2-dimensional Cubic Bezier Curve.
/// @param t The normalized distance along the curve to compute, with range of [0, 1].
/// @param p0 The start-point of the curve.
/// @param p1
/// @param p2
/// @param p3 The end-point of the curve.
Vector2 BezierDerivative(float t, const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3);
/// Computes a point along the normal of a 2-dimensional Cubic Bezier Curve.
/// @param t The normalized distance along the curve to compute, with range of [0, 1].
/// @param p0 The start-point of the curve.
/// @param p1 The first control point, which determines the direction at which the curve meets point 0.
/// @param p2 The second control point, which determines the direction at which the curve meets point 3.
/// @param p3 The end-point of the curve.
Vector2 BezierNormal(float t, const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3);
/// Computes a point along a 3-dimensional Cubic Bezier curve.
/// @param t The normalized distance along the curve to compute, with range of [0, 1].
/// @param p0 The start-point of the curve.
/// @param p1 The first control point, which determines the direction at which the curve meets point 0.
/// @param p2 The second control point, which determines the direction at which the curve meets point 3.
/// @param p3 The end-point of the curve.
Vector3 Bezier(float t, const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3);
/// Computes a point along the tangent of a 3-dimensional Cubic Bezier Curve.
/// @param t The normalized distance along the curve to compute, with range of [0, 1].
/// @param p0 The start-point of the curve.
/// @param p1 The first control point, which determines the direction at which the curve meets point 0.
/// @param p2 The second control point, which determines the direction at which the curve meets point 3.
/// @param p3 The end-point of the curve.
Vector3 BezierDerivative(float t, const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3);
/// Computes a point along the normal of a 3-dimensional Cubic Bezier Curve.
/// @param t The normalized distance along the curve to compute, with range of [0, 1].
/// @param p0 The start-point of the curve.
/// @param p1 The first control point, which determines the direction at which the curve meets point 0.
/// @param p2 The second control point, which determines the direction at which the curve meets point 3.
/// @param p3 The end-point of the curve.
Vector3 BezierNormal(float t, const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3);
}

View File

@@ -0,0 +1,3 @@
#pragma once

View File

@@ -1,10 +1,11 @@
// @file GJK.h
/// @file GJK.hpp
/// Implementation of the Gilbert-Johnson-Keerthi (GJK) convex polyhedron intersection test
#include <J3ML/LinearAlgebra.h>
#include <J3ML/Geometry.h>
#pragma once
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry.hpp>
namespace J3ML::Algorithms
{

View File

@@ -0,0 +1,17 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Parabola.hpp
/// @desc Algorithm for calculating a parabolic curve to be used in instantaneous bullet raycasting.
/// @edit 2024-10-22
#pragma once
namespace J3ML
{
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include "J3ML/J3ML.h"
#include "J3ML/J3ML.hpp"
namespace J3ML::Algorithm
{

View File

@@ -0,0 +1,40 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Reinterpret.h
/// @desc Reinterpret one type to another using union hackery.
/// @edit 2024-07-06
#pragma once
namespace J3ML
{
/// As per C99, union-reinterpret should now be safe: http://stackoverflow.com/questions/8511676/portable-data-reinterpretation
template <typename To, typename From>
union ReinterpretOp {
From from;
To to;
ReinterpretOp(From from_) : from(from_) {} // Do not make explicit!
operator To() const { return to; } // Do not make explicit!
};
template <typename To, typename From>
To ReinterpretAs(From input)
{
//ReinterpretOp<To, From> fi;
//fi.to = input;
return ReinterpretOp<To, From>(input); //fi.from;
}
}

View File

@@ -0,0 +1,18 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Parabola.hpp
/// @desc Algorithm for calculating a parabolic curve to be used in instantaneous bullet raycasting.
/// @edit 2024-10-22
#pragma once
namespace J3ML
{
/// @see class Polygon::Triangulate for current implementation.
}

View File

@@ -1,23 +0,0 @@
#include <J3ML/LinearAlgebra.h>
#pragma once
#include <J3ML/Geometry/AABB2D.h>
#include <J3ML/Geometry/Plane.h>
#include <J3ML/Geometry/Sphere.h>
#include <J3ML/Geometry/Line.h>
#include <J3ML/Geometry/LineSegment.h>
#include <J3ML/Geometry/Frustum.h>
#include <J3ML/Geometry/OBB.h>
#include <J3ML/Geometry/Capsule.h>
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/Polyhedron.h>
#include <J3ML/Geometry/QuadTree.h>
#include <J3ML/Geometry/Ray.h>
#include <J3ML/Geometry/Shape.h>
#include <J3ML/Geometry/Sphere.h>
#include <J3ML/Geometry/Triangle.h>
#include <J3ML/Geometry/Triangle2D.h>
#include <J3ML/Geometry/TriangleMesh.h>
using namespace J3ML::Geometry;

25
include/J3ML/Geometry.hpp Normal file
View File

@@ -0,0 +1,25 @@
#include <J3ML/LinearAlgebra.hpp>
#pragma once
#include <J3ML/Geometry/AABB2D.hpp>
#include <J3ML/Geometry/Plane.hpp>
#include <J3ML/Geometry/Sphere.hpp>
#include <J3ML/Geometry/Line.hpp>
#include <J3ML/Geometry/LineSegment.hpp>
#include <J3ML/Geometry/Frustum.hpp>
#include <J3ML/Geometry/OBB.hpp>
#include <J3ML/Geometry/Capsule.hpp>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/Polyhedron.hpp>
#include <J3ML/Geometry/QuadTree.hpp>
#include <J3ML/Geometry/Ray.hpp>
#include <J3ML/Geometry/Sphere.hpp>
#include <J3ML/Geometry/Triangle.hpp>
#include <J3ML/Geometry/Triangle2D.hpp>
#include <J3ML/Geometry/TriangleMesh.hpp>
#include <J3ML/Geometry/PBVolume.hpp>
#include <J3ML/Geometry/KDTree.hpp>
using namespace J3ML::Geometry;

View File

@@ -1,15 +1,35 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file AABB.hpp
/// @desc The Axis-Aligned Bounding Box geometry object.
/// @edit 2024-08-01
#pragma once
#include <format>
#include <optional>
#include <J3ML/LinearAlgebra.h>
#include <J3ML/Geometry/Common.h>
#include <J3ML/Geometry/Shape.h>
#include "J3ML/Algorithm/RNG.h"
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Algorithm/RNG.hpp>
namespace J3ML::Geometry
{
// TODO: Move this somewhere else to do goofy experiments!
template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>, T>>
float fdiv(T a, T b) {
return (float)a/(b);
}
using namespace J3ML::LinearAlgebra;
using J3ML::Algorithm::RNG;
@@ -47,12 +67,25 @@ namespace J3ML::Geometry
@note Since an AABB cannot generally represent an OBB, this conversion is not exact, but the returned AABB
specifies a larger volume.
@see class OBB. */
explicit AABB(const OBB &obb);
explicit AABB(const OBB &obb) {
SetFrom(obb);
}
/// Constructs this AABB to enclose the given Sphere.
/** @see class Sphere. */
explicit AABB(const Sphere &s);
explicit AABB(const Sphere &s) {
SetFrom(s);
}
/// Returns the diameter vector of this AABB.
/** @note For AABB, Diagonal() and Size() are the same concept. These functions are provided for symmetry with the OBB class.
@see Size(), HalfDiagonal() */
Vector3 Diagonal() const { return Size(); }
/// Returns Diagonal()/2.
/// @see Diagonal(), HalfSize()
Vector3 HalfDiagonal() const { return HalfSize(); }
static AABB FromCenterAndSize(const Vector3 &center, const Vector3 &size);
@@ -93,6 +126,7 @@ namespace J3ML::Geometry
/// @return The center point of this AABB.
Vector3 Centroid() const;
Vector3 CenterPoint() const;
/// Returns the side lengths of this AABB in x, y and z directions.
/** The returned vector is equal to the diagonal vector of this AABB, i.e. it spans from the
@@ -184,6 +218,7 @@ namespace J3ML::Geometry
float SurfaceArea() const;
Vector3 GetClosestPoint(const Vector3& point) const;
void Translate(const Vector3& offset);
AABB Translated(const Vector3& offset) const;
void Scale(const Vector3& scale);
@@ -329,12 +364,27 @@ namespace J3ML::Geometry
void Enclose(const LineSegment &lineSegment);
void Enclose(const OBB &obb);
void Enclose(const Sphere &sphere);
void Enclose(const Triangle &triangle);
void Enclose(const Capsule &capsule);
void Enclose(const Frustum &frustum);
void Enclose(const Polygon &polygon);
void Enclose(const Polyhedron &polyhedron);
void Enclose(const Vector3 *pointArray, int numPoints);
void Enclose(const Frustum &frustum) {
//Enclose(frustum.MinimalEnclosingAABB());
}
void Enclose(const Polygon &polygon) {
//Enclose(polygon.MinimalEnclosingAABB());
}
void Enclose(const Polyhedron &polyhedron) {
//Enclose(polyhedron.MinimalEnclosingAABB());
}
void Enclose(const Vector3 *pointArray, int numPoints) {
assert(pointArray || numPoints == 0);
if (!pointArray)
return;
for (int i = 0; i < numPoints; ++i)
Enclose(pointArray[i]);
}
bool TestAxis(const Vector3& axis, const Vector3& v0, const Vector3& v1, const Vector3& v2) const;

View File

@@ -1,54 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector2.h>
#include "Shape.h"
namespace J3ML::Geometry
{
using LinearAlgebra::Vector2;
// CaveGame AABB
class AABB2D : public Shape2D
{
public:
Vector2 minPoint;
Vector2 maxPoint;
AABB2D() {}
AABB2D(const Vector2& min, const Vector2& max):
minPoint(min), maxPoint(max)
{}
float Width() const;
float Height() const;
float DistanceSq(const Vector2& pt) const;
void SetNegativeInfinity();
void Enclose(const Vector2& point);
bool Intersects(const AABB2D& rhs) const;
bool Contains(const AABB2D& rhs) const;
bool Contains(const Vector2& pt) const;
bool Contains(int x, int y) const;
bool IsDegenerate() const;
bool HasNegativeVolume() const;
bool IsFinite() const;
Vector2 PosInside(const Vector2 &normalizedPos) const;
Vector2 ToNormalizedLocalSpace(const Vector2 &pt) const;
AABB2D operator+(const Vector2& pt) const;
AABB2D operator-(const Vector2& pt) const;
};
}

View File

@@ -0,0 +1,102 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file AABB2D.hpp
/// @desc A 2D Axis-Aligned Bounding Box structure.
/// @edit 2024-08-01
/// @note On backlog, low-priority.
#pragma once
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
/// See Christer Ericson's Real-time Collision Detection, p. 87, or
/// James Arvo's "Transforming Axis-aligned Bounding Boxes" in Graphics Gems 1, pp. 548-550.
/// http://www.graphicsgems.org/
template <typename Matrix>
void AABB2DTransformAsAABB2D(AABB2D& aabb, Matrix& m);
namespace J3ML::Geometry
{
using LinearAlgebra::Vector2;
// CaveGame AABB
class AABB2D : public Shape2D
{
public:
Vector2 minPoint;
Vector2 maxPoint;
AABB2D() {}
AABB2D(const Vector2& min, const Vector2& max):
minPoint(min), maxPoint(max)
{}
[[nodiscard]] float Width() const;
[[nodiscard]] float Height() const;
Vector2 Centroid();
[[nodiscard]] float DistanceSq(const Vector2& pt) const;
void SetNegativeInfinity();
void Enclose(const Vector2& point);
[[nodiscard]] bool Intersects(const AABB2D& rhs) const;
[[nodiscard]] bool Contains(const AABB2D& rhs) const;
[[nodiscard]] bool Contains(const Vector2& pt) const;
[[nodiscard]] bool Contains(int x, int y) const;
[[nodiscard]] bool IsDegenerate() const;
[[nodiscard]] bool HasNegativeVolume() const;
[[nodiscard]] bool IsFinite() const;
Vector2 PosInside(const Vector2 &normalizedPos) const;
Vector2 ToNormalizedLocalSpace(const Vector2 &pt) const;
AABB2D operator+(const Vector2& pt) const;
AABB2D operator-(const Vector2& pt) const;
Vector2 CornerPoint(int cornerIndex);
void TransformAsAABB(const Matrix3x3& transform);
void TransformAsAABB(const Matrix4x4& transform);
};
template<typename Matrix>
void AABB2DTransformAsAABB2D(AABB2D &aabb, Matrix &m) {
float ax = m[0][0] * aabb.minPoint.x;
float bx = m[0][0] * aabb.maxPoint.x;
float ay = m[0][1] * aabb.minPoint.y;
float by = m[0][1] * aabb.maxPoint.y;
float ax2 = m[1][0] * aabb.minPoint.x;
float bx2 = m[1][0] * aabb.maxPoint.x;
float ay2 = m[1][1] * aabb.minPoint.y;
float by2 = m[1][1] * aabb.maxPoint.y;
aabb.minPoint.x = J3ML::Math::Min(ax, bx) + J3ML::Math::Min(ay, by) + m[0][3];
aabb.maxPoint.x = J3ML::Math::Max(ax, bx) + J3ML::Math::Max(ay, by) + m[0][3];
aabb.minPoint.y = J3ML::Math::Min(ax2, bx2) + J3ML::Math::Min(ay2, by2) + m[1][3];
aabb.maxPoint.y = J3ML::Math::Max(ax2, bx2) + J3ML::Math::Max(ay2, by2) + m[1][3];
}
}

View File

@@ -1,21 +1,31 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Capsule.hpp
/// @desc The Capsule geometry object.
/// @edit 2024-08-01
#pragma once
#include "LineSegment.h"
#include "Shape.h"
#include <J3ML/LinearAlgebra.h>
#include <J3ML/Geometry/Common.h>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/Geometry/LineSegment.hpp>
namespace J3ML::Geometry
{
/// A 3D cylinder with spherical ends.
class Capsule : public Shape
{
public:
// Specifies the two inner points of this capsule
/// Specifies the two inner points of this capsule
LineSegment l;
// Specifies the radius of this capsule
/// Specifies the radius of this capsule
float r;
public:
/// The default constructor does not initialize any members of this class.
@@ -34,8 +44,17 @@ namespace J3ML::Geometry
@see l, r. */
Capsule(const Vector3& bottomPt, const Vector3& topPt, float radius);
/// Constructs a new capsule from a sphere.
/** This conversion results in a capsule which has its both endpoints at the exact same coordinates, and hence the
length of the inner line segment is set to 0. */
void SetFrom(const Sphere& s);
/// Sets this Capsule to a degenerate negative-volume state.
void SetDegenerate();
/// Quickly returns an arbitrary point inside this Capsule. Used in GJK intersection test.
inline Vector3 AnyPointFast() const { return l.A; }
[[nodiscard]] Vector3 AnyPointFast() const;
/// Generates a point that perhaps lies inside this capsule.
/** @param height A normalized value between [0,1]. This specifies the point position along the height line of this capsule.
@@ -46,10 +65,10 @@ namespace J3ML::Geometry
Vector3 UniformPointPerhapsInside(float height, float x, float y) const;
/// Returns the Sphere defining the 'bottom' section of this Capsule (corresponding to the endpoint l.a)
Sphere SphereA() const;
[[nodiscard]] Sphere SphereA() const;
/// Returns the Sphere defining the 'top' section of this Capsule (corresponding to the endpoint l.b)
Sphere SphereB() const;
[[nodiscard]] Sphere SphereB() const;
/// Computes the extreme point of this Capsule in the given direction.
/** An extreme point is a farthest point of this Capsule in the given direction. Given a direction,
@@ -57,18 +76,22 @@ namespace J3ML::Geometry
@param direction The direction vector of the direction to find the extreme point. This vector may
be unnormalized, but may not be null.
@return The extreme point of this Capsule in the given direction. */
Vector3 ExtremePoint(const Vector3 &direction) const;
[[nodiscard]] Vector3 ExtremePoint(const Vector3 &direction) const;
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
/// Tests if this Capsule is degenerate.
/** @return True if this Capsule does not span a strictly positive volume. */
bool IsDegenerate() const;
[[nodiscard]] bool IsDegenerate() const;
/// Computes the total height of this capsule, i.e. LineLength() + Diameter().
/** <img src="CapsuleFunctions.png" />
@see LineLength(). */
float Height() const;
[[nodiscard]] float Height() const;
/// Computes the distance of the two inner points of this capsule. @see Height.
[[nodiscard]] float LineLength() const;
/// Computes the diameter of this capsule.
float Diameter() const;
[[nodiscard]] float Diameter() const;
/// Returns the bottom-most point of this Capsule.
/** <img src="CapsuleFunctions.png" />
@note The bottom-most point is only a naming convention, and does not correspond to the bottom-most point along any world axis. The returned
@@ -76,41 +99,42 @@ namespace J3ML::Geometry
@note The bottom-most point of the capsule is different than the point l.a. The returned point is the point at the very far
edge of this capsule, and does not lie on the internal line. See the attached diagram.
@see Top(), l. */
Vector3 Bottom() const;
[[nodiscard]] Vector3 Bottom() const;
/// Returns the center point of this Capsule.
/** <img src="doc/static/docs/CapsuleFunctions.png" />
@return The point (l.a + l.b) / 2. This point is the center of mass for this capsule.
@see l, Bottom(), Top(). */
Vector3 Center() const;
Vector3 Centroid() const; ///< [similarOverload: Center]
[[nodiscard]] Vector3 Center() const;
[[nodiscard]] Vector3 Centroid() const; ///< [similarOverload: Center]
/// Returns the direction from the bottommost point towards the topmost point of this Capsule.
/** <img src="CapsuleFunctions.png" />
@return The normalized direction vector from l.a to l.b.
@see l. */
Vector3 UpDirection() const;
[[nodiscard]] Vector3 UpDirection() const;
/// Computes the volume of this Capsule.
/** @return pi * r^2 * |b-a| + 4 * pi * r^2 / 3.
@see SurfaceArea(). */
float Volume() const;
[[nodiscard]] float Volume() const;
/// Computes the surface area of this Capsule.
/** @return 2 * pi * r * |b-a| + 4 * pi * r^2.
@see Volume(). */
float SurfaceArea() const;
[[nodiscard]] float SurfaceArea() const;
/// Returns the cross-section circle at the given height of this Capsule.
/** <img src="CapsuleFunctions.png" />
@param l A normalized parameter between [0,1]. l == 0 returns a degenerate circle of radius 0 at the bottom of this Capsule, and l == 1
will return a degenerate circle of radius 0 at the top of this Capsule. */
//Circle CrossSection(float l) const;
Circle CrossSection(float l) const;
Vector3 ExtremePoint(const Vector3& direction);
/// Returns the smallest AABB that encloses this capsule.
/** @see MinimalEnclosingOBB(). */
AABB MinimalEnclosingAABB() const;
[[nodiscard]] AABB MinimalEnclosingAABB() const;
/// Returns the smallest OBB that encloses this capsule.
/** @see MinimalEnclosingAABB(). */
@@ -132,7 +156,7 @@ namespace J3ML::Geometry
@note The topmost point of the capsule is different than the point l.b. The returned point is the point at the very far
edge of this capsule, and does not lie on the internal line. See the attached diagram.
@see Bottom(), l. */
Vector3 Top() const;
[[nodiscard]] Vector3 Top() const;
/// Applies a transformation to this capsule.
/** @param transform The transformation to apply to this capsule. This transformation must be

View File

@@ -0,0 +1,164 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Circle.hpp
/// @desc The Circle geometry object.
/// @edit 2024-08-01
#pragma once
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/LinearAlgebra.hpp>
namespace J3ML
{
/// A two-dimensional circle in 3D space.
/// This class represents both a hollow circle (only edge) and a solid circle (disc).
class Circle : public Shape
{
public:
/// The center position of this circle.
Vector3 Position;
/// The normal direction of this circle.
/** A circle is a two-dimensional object in 3D space. This normal vector (together with the Position)
specifies the plane in which this circle lies in.
This vector is always normalized. If you assign to this member directly, be sure to only assign normalized vectors. */
Vector3 Normal;
/// The radius of the circle.
/** This parameter must be strictly positive to specify a non-degenerate circle. If zero is specified, this circle
is considered to be degenerate. */
float Radius;
/// The default constructor does not initialize any members of this class.
/** This means that the values of members Position, Normal, and Radius are all undefined after creating
a new circle using this default constructor. Remember to assign them before use.
@see Position, Normal, Radius. */
Circle() {}
/// Constructs a new circle by explicitly specifying the member variables.
/** @param center The center point of the circle.
@param normal The direction vector that specifies the orientation of this circle.
This vector must be normalized, the constructor will not normalize the vector for you (for performance reasons).
@param radius The radius of the circle.
@see Position, Normal, Radius. */
Circle(const Vector3& center, const Vector3& normal, float radius);
/// Returns a normalized direction vector to the 'U direction' of the circle.
/** This vector lies on the plane of this circle.
The U direction specifies the first basis vector of a local space of this circle. */
Vector3 BasisU() const;
/// Returns a normalized direction vector to the 'V direction' of the circle.
/** This vector lies on the plane of this circle.
The V direction specifies the second basis vector of a local space of this circle. */
Vector3 BasisV() const;
/// Returns a point at the edge of this circle.
/** @param angleRadians The direction of the point to get. A full circle is generated by the range [0, 2*pi],
but it is ok to pass in values outside this range.
@note This function is equivalent to calling GetPoint(float angleRadians, float d) with a value of d == 1.
@return A point in world space at the edge of this circle. */
Vector3 GetPoint(float angleRadians) const;
/// Returns a point inside this circle.
/** @param angleRadians The direction of the point to get. A full circle is generated by the range [0, 2*pi],
but it is ok to pass in values outside this range.
@param d A value in the range [0, 1] that specifies the normalized distance of the point from the center of the circle.
A value of 0 returns the center point of this circle. A value of 1 returns a point at the edge of this circle.
The range of d is not restricted, so it is ok to pass in values larger than 1 to generate a point lying completely
outside the circle. */
Vector3 GetPoint(float angleRadians, float d) const;
/// Returns the center point of this circle.
/** This point is also the center of mass for this circle. The functions CenterPoint() and Centroid() are equivalent.
@see Position. */
Vector3 CenterPoint() const { return Position; }
Vector3 Centroid() const { return Position;}
/// Computes an extreme point of this Circle/Disc in the given direction.
/** An extreme point is a farthest point of this Circle/Disc in the given direction. Given a direction,
this point is not necessarily unique.
@param direction The direction vector of the direction to find the extreme point. This vector may
be unnormalized, but may not be null.
@return An extreme point of this Circle/Disc in the given direction. The returned point is always at
the edge of this Circle. */
Vector3 ExtremePoint(const Vector3& direction) const;
/// Computes the plane this circle is contained in.
/** All of the points of this circle lie inside this plane.
@see class Plane. */
Plane ContainingPlane() const;
/// Translates this Circle in world space.
/** @param offset The amount of displacement to apply to this circle, in world space coordinates.
@see Transform(). */
void Translate(const Vector3& offset);
/// Applies a transformation to this Circle.
/** @param transform The transformation to apply to this Circle. This transformation must be
affine, and must contain an orthogonal set of column vectors (may not contain shear or projection).
The transformation can only contain uniform scale, and may not contain mirroring.
@see Translate(), Scale(), classes Matrix3x3, Matrix4x4, Quaternion. */
void Transform(const Matrix3x3& transform);
void Transform(const Matrix4x4& transform);
void Transform(const Quaternion& transform);
/// Tests if the given point is contained at the edge of this circle.
/** @param point The target point to test.
@param maxDistance The epsilon threshold to test the distance against. This effectively turns the circle into a torus
for this test.
@see DistanceToEdge(), DistanceToDisc(), ClosestPointToEdge(), ClosestPointToDisc().
@todo Implement DiscContains(Vector3/LineSegment/Triangle). */
bool EdgeContains(const Vector3& point, float maxDistance = 1e-6f) const;
/// Computes the distance of the given object to the edge of this circle.
/** @todo Implement DistanceToEdge(Ray/LineSegment/Line).
@return The distance of the given point to the edge of this circle. If the point is contained on this circle,
the value 0 is returned.
@see DistanceToEdge(), DistanceToDisc(), ClosestPointToDisc().*/
float DistanceToEdge(const Vector3& point) const;
/// Computes the distance of the given object to this disc (filled circle).
/** If the point is contained inside this disc, the value 0 is returned.
@see DistanceToEdge(), ClosestPointToEdge(), ClosestPointToDisc().
@todo Implement DistanceToDisc(Ray/LineSegment/Line). */
float DistanceToDisc(const Vector3& point) const;
/// Computes the closest point on the edge of this circle to the given object.
/** @todo Implement ClosestPointToEdge(Ray/LineSegment/Line).
@see DistanceToEdge(), DistanceToDisc(), ClosestPointToDisc(). */
Vector3 ClosestPointToEdge(const Vector3& point) const;
/// Computes the closest point on the disc of this circle to the given object.
/** @todo Implement ClosestPointToDisc(Ray/LineSegment/Line).
@see DistanceToEdge(), DistanceToDisc(), ClosestPointToEdge(). */
Vector3 ClosestPointToDisc(const Vector3& point) const;
/// Tests this circle for an intersection against the given plane.
/** @note For Circle-Plane intersection, there is no need to differentiate between a hollow or filled circle(disc).
@return The number of intersection points found for this circle and the given plane.
@see IntersectsDisc(). */
int Intersects(const Plane& plane, Vector3* pt1, Vector3* pt2) const;
int Intersects(const Plane& plane) const;
/// Tests this disc for an intersection against the given object.
/** @see Intersects(). */
bool IntersectsDisc(const Line& line) const;
bool IntersectsDisc(const LineSegment& lineSegment) const;
bool IntersectsDisc(const Ray& ray) const;
/// Tests if this circle intersects the faces of the given OBB.
/** @param obb The bounding box to test against. This box is treated as "hollow", i.e. only the faces of the OBB are considered to be
a part of the OBB.
@return A vector that contains all the detected points of intersection for this circle and the given OBB. If the circle is fully
contained inside the OBB, or is fully outside the OBB, no intersection occurs, and the returned vector has zero elements.
@see Intersects(), IntersectsDisc(). */
std::vector<Vector3> IntersectsFaces(const OBB& obb) const;
std::vector<Vector3> IntersectsFaces(const AABB& aabb) const;
};
Circle operator *(const Matrix3x3& transform, const Circle& circle);
Circle operator *(const Matrix4x4& transform, const Circle& circle);
Circle operator *(const Quaternion& transform, const Circle& circle);
std::ostream& operator << (std::ostream& o, const Circle& circle);
}

View File

@@ -4,24 +4,27 @@
// Forward declarations for classes that include each other
namespace J3ML::Geometry
{
class Shape;
class AABB2D;
class AABB;
class Capsule;
class Frustum;
class LineSegment;
class LineSegment2D;
class Line;
class OBB;
class OBB2D;
class Plane;
class Polygon;
class Polyhedron;
template<typename T> class QuadTree;
class Ray;
class Shape;
class Sphere;
class Circle;
class Triangle;
class Triangle2D;
class TriangleMesh;
template <int N> class PBVolume;
}
// Methods required by Geometry types
@@ -36,4 +39,6 @@ namespace J3ML::Geometry
bool operator==(const Interval& rhs) const = default;
};
}
}
using namespace J3ML::Geometry;

View File

@@ -3,10 +3,9 @@
//
#pragma once
#include <J3ML/Geometry/Common.h>
#include "Plane.h"
#include "Shape.h"
#include <J3ML/LinearAlgebra.h>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/LinearAlgebra.hpp>
namespace J3ML::Geometry
{
@@ -84,7 +83,7 @@ namespace J3ML::Geometry
/// Specifies whether this frustum is a perspective or an orthographic frustum.
FrustumType type;
/// Specifies whether the [-1, 1] or [0, 1] range is used for the post-projective depth range.
FrustumProjectiveSpace projectiveSpace;
FrustumProjectiveSpace projectiveSpace ;
/// Specifies the chirality of world and view spaces
FrustumHandedness handedness;
/// The eye point of this frustum
@@ -142,175 +141,250 @@ namespace J3ML::Geometry
Matrix4x4 viewProjectionMatrix;
public:
/// The default constructor creates an uninitialized Frustum object.
/** This means that the values of the members type, projectiveSpace, handedness, pos, front, up, nearPlaneDistance, farPlaneDistance, horizontalFov/orthographicWidth and
verticalFov/orthographicHeight are all NaN after creating a new Frustum using this
default constructor. Remember to assign to them before use.
@note As an exception to other classes in MathGeoLib, this class initializes its members to NaNs, whereas the other classes leave the members uninitialized. This difference
is because the Frustum class implements a caching mechanism where world, projection and viewProj matrices are recomputed on demand, which does not work nicely together
if the defaults were uninitialized.
*/
/// The default constructor creates an uninitialized Frustum object.
/** This means that the values of the members type, projectiveSpace, handedness, pos, front, up, nearPlaneDistance, farPlaneDistance, horizontalFov/orthographicWidth and
verticalFov/orthographicHeight are all NaN after creating a new Frustum using this
default constructor. Remember to assign to them before use.
@note As an exception to other classes in MathGeoLib, this class initializes its members to NaNs, whereas the other classes leave the members uninitialized. This difference
is because the Frustum class implements a caching mechanism where world, projection and viewProj matrices are recomputed on demand, which does not work nicely together
if the defaults were uninitialized. */
Frustum();
/// Quickly returns an arbitrary point inside this Frustum. Used in GJK intersection test.
inline Vector3 AnyPointFast() const { return CornerPoint(0); }
static Frustum CreateFrustumFromCamera(const CoordinateFrame& cam, float aspect, float fovY, float zNear, float zFar);
public:
/// Quickly returns an arbitrary point inside this Frustum. Used in GJK intersection test.
[[nodiscard]] Vector3 AnyPointFast() const { return CornerPoint(0); }
/// Returns the tightest AABB that contains this Frustum.
/** This function computes the optimal minimum volume AABB that encloses this Frustum.
@note Since an AABB cannot generally represent a Frustum, this conversion is not exact, but the returned AABB
specifies a larger volume.
@see MinimalEnclosingOBB(), ToPolyhedron(). */
AABB MinimalEnclosingAABB() const;
[[nodiscard]] AABB MinimalEnclosingAABB() const;
[[nodiscard]] bool IsFinite() const;
/// Returns the tightest OBB that encloses this Frustum.
/** This function computes the optimal minimum volume OBB that encloses this Frustum.
@note If the type of this frustum is Perspective, this conversion is not exact, but the returned OBB specifies
a larger volume. If the type of this Frustum is orthographic, this conversion is exact, since the shape of an
orthographic Frustum is an OBB.
@see MinimalEnclosingAABB(), ToPolyhedron(). */
OBB MinimalEnclosingOBB() const;
[[nodiscard]] OBB MinimalEnclosingOBB(float expandGuardband = 1e-5f) const;
/// Sets the type of this Frustum.
/** @note Calling this function recomputes the cached view and projection matrices of this Frustum.
@see SetViewPlaneDistances(), SetFrame(), SetPos(), SetFront(), SetUp(), SetPerspective(), SetOrthographic(), ProjectiveSpace(), Handedness(). */
void SetKind(FrustumProjectiveSpace projectiveSpace, FrustumHandedness handedness);
/// Sets the depth clip distances of this Frustum.
/** @param nearPlaneDistance The z distance from the eye point to the position of the Frustum near clip plane. Always pass a positive value here.
@param farPlaneDistance The z distance from the eye point to the position of the Frustum far clip plane. Always pass a value that is larger than nearClipDistance.
/** @param n The z distance from the eye point to the position of the Frustum near clip plane. Always pass a positive value here.
@param f The z distance from the eye point to the position of the Frustum far clip plane. Always pass a value that is larger than nearClipDistance.
@note Calling this function recomputes the cached projection matrix of this Frustum.
@see SetKind(), SetFrame(), SetPos(), SetFront(), SetUp(), SetPerspective(), SetOrthographic(), NearPlaneDistance(), FarPlaneDistance(). */
void SetViewPlaneDistances(float nearPlaneDistance, float farPlaneDistance);
void SetViewPlaneDistances(float n, float f);
/// Specifies the full coordinate space of this Frustum in one call.
/** @note Calling this function recomputes the cached world matrix of this Frustum.
@note As a micro-optimization, prefer this function over the individual SetPos/SetFront/SetUp functions if you need to do a batch of two or more changes, to avoid
redundant recomputation of the world matrix.
@see SetKind(), SetViewPlaneDistances(), SetPos(), SetFront(), SetUp(), SetPerspective(), SetOrthographic(), Pos(), Front(), Up(). */
void SetFrame(const Vector3& pos, const Vector3& front, const Vector3& up);
/// Sets the world-space position of this Frustum.
/** @note Calling this function recomputes the cached world matrix of this Frustum.
@see SetKind(), SetViewPlaneDistances(), SetFrame(), SetFront(), SetUp(), SetPerspective(), SetOrthographic(), Pos(). */
void SetPos(const Vector3& pos);
/// Sets the world-space direction the Frustum eye is looking towards.
/** @note Calling this function recomputes the cached world matrix of this Frustum.
@see SetKind(), SetViewPlaneDistances(), SetFrame(), SetPos(), SetUp(), SetPerspective(), SetOrthographic(), Front(). */
void SetFront(const Vector3& front);
/// Sets the world-space camera up direction vector of this Frustum.
/** @note Calling this function recomputes the cached world matrix of this Frustum.
@see SetKind(), SetViewPlaneDistances(), SetFrame(), SetPos(), SetFront(), SetPerspective(), SetOrthographic(), Up(). */
void SetUp(const Vector3& up);
/// Makes this Frustum use a perspective projection formula with the given FOV parameters.
/** A Frustum that uses the perspective projection is shaped like a pyramid that is cut from the top, and has a
base with a rectangular area.
@note Calling this function recomputes the cached projection matrix of this Frustum.
@see SetKind(), SetViewPlaneDistances(), SetFrame(), SetPos(), SetFront(), SetUp(), SetOrthographic(), HorizontalFov(), VerticalFov(), SetHorizontalFovAndAspectRatio(), SetVerticalFovAndAspectRatio(). */
void SetPerspective(float horizontalFov, float verticalFov);
void SetPerspective(float h, float v);
/// Makes this Frustum use an orthographic projection formula with the given FOV parameters.
/** A Frustum that uses the orthographic projection is shaded like a cube (an OBB).
@note Calling this function recomputes the cached projection matrix of this Frustum.
@see SetKind(), SetViewPlaneDistances(), SetFrame(), SetPos(), SetFront(), SetUp(), SetOrthographic(), OrthographicWidth(), OrthographicHeight(). */
void SetOrthographic(float orthographicWidth, float orthographicHeight);
void SetOrthographic(float w, float h);
/// Returns the handedness of the projection formula used by this Frustum.
/** @see SetKind(), FrustumHandedness. */
FrustumHandedness Handedness() const { return handedness; }
[[nodiscard]] FrustumHandedness Handedness() const { return handedness; }
/// Returns the type of the projection formula used by this Frustum.
/** @see SetPerspective(), SetOrthographic(), FrustumType. */
FrustumType Type() const { return type; }
[[nodiscard]] FrustumType Type() const { return type; }
/// Returns the convention of the post-projective space used by this Frustum.
/** @see SetKind(), FrustumProjectiveSpace. */
FrustumProjectiveSpace ProjectiveSpace() const { return projectiveSpace;}
[[nodiscard]] FrustumProjectiveSpace ProjectiveSpace() const { return projectiveSpace;}
/// Returns the world-space position of this Frustum.
/** @see SetPos(), Front(), Up(). */
const Vector3 &Pos() const {return pos;}
[[nodiscard]] const Vector3 &Pos() const {return pos;}
/// Returns the world-space camera look-at direction of this Frustum.
/** @see Pos(), SetFront(), Up(). */
const Vector3 &Front() const { return front; }
[[nodiscard]] const Vector3 &Front() const { return front; }
/// Returns the world-space camera up direction of this Frustum.
/** @see Pos(), Front(), SetUp(). */
const Vector3 &Up() const { return up; }
[[nodiscard]] const Vector3 &Up() const { return up; }
/// Returns the distance from the Frustum eye to the near clip plane.
/** @see SetViewPlaneDistances(), FarPlaneDistance(). */
float NearPlaneDistance() const { return nearPlaneDistance; }
[[nodiscard]] float NearPlaneDistance() const { return nearPlaneDistance; }
/// Returns the distance from the Frustum eye to the far clip plane.
/** @see SetViewPlaneDistances(), NearPlaneDistance(). */
float FarPlaneDistance() const { return farPlaneDistance;}
[[nodiscard]] float FarPlaneDistance() const { return farPlaneDistance;}
/// Returns the horizontal field-of-view used by this Frustum, in radians.
/** @note Calling this function when the Frustum is not set to use perspective projection will return values that are meaningless.
@see SetPerspective(), Type(), VerticalFov(). */
float HorizontalFov() const { return horizontalFov;}
[[nodiscard]] float HorizontalFov() const { return horizontalFov;}
/// Returns the vertical field-of-view used by this Frustum, in radians.
/** @note Calling this function when the Frustum is not set to use perspective projection will return values that are meaningless.
@see SetPerspective(), Type(), HorizontalFov(). */
float VerticalFov() const { return verticalFov;}
[[nodiscard]] float VerticalFov() const { return verticalFov;}
/// Returns the world-space width of this Frustum.
/** @note Calling this function when the Frustum is not set to use orthographic projection will return values that are meaningless.
@see SetOrthographic(), Type(), OrthographicHeight(). */
float OrthographicWidth() const { return orthographicWidth; }
[[nodiscard]] float OrthographicWidth() const { return orthographicWidth; }
/// Returns the world-space height of this Frustum.
/** @note Calling this function when the Frustum is not set to use orthographic projection will return values that are meaningless.
@see SetOrthographic(), Type(), OrthographicWidth(). */
float OrthograhpicHeight() const { return orthographicHeight; }
[[nodiscard]] float OrthograhpicHeight() const { return orthographicHeight; }
/// Returns the number of line segment edges that this Frustum is made up of, which is always 12.
/** This function is used in template-based algorithms to provide an unified API for iterating over the features of a Polyhedron. */
int NumEdges() const { return 12; }
static int NumEdges() { return 12; }
/// Returns the aspect ratio of the view rectangle on the near plane.
/** The aspect ratio is the ratio of the width of the viewing rectangle to its height. This can also be computed by
the expression horizontalFov / verticalFov. To produce a proper non-stretched image when rendering, this
aspect ratio should match the aspect ratio of the actual render target (e.g. 4:3, 16:9 or 16:10 in full screen mode).
@see horizontalFov, verticalFov. */
float AspectRatio() const;
[[nodiscard]] float AspectRatio() const;
/// Makes this Frustum use a perspective projection formula with the given horizontal FOV parameter and aspect ratio.
/** Specifies the horizontal and vertical field-of-view values for this Frustum based on the given horizontal FOV
and the screen size aspect ratio.
@note Calling this function recomputes the cached projection matrix of this Frustum.
@see SetPerspective(), SetVerticalFovAndAspectRatio(). */
void SetHorizontalFovAndAspectRatio(float horizontalFov, float aspectRatio);
void SetHorizontalFovAndAspectRatio(float hFov, float aspectRatio);
/// Makes this Frustum use a perspective projection formula with the given vertical FOV parameter and aspect ratio.
/** Specifies the horizontal and vertical field-of-view values for this Frustum based on the given vertical FOV
and the screen size aspect ratio.
@note Calling this function recomputes the cached projection matrix of this Frustum.
@see SetPerspective(), SetHorizontalFovAndAspectRatio(). */
void SetVerticalFovAndAspectRatio(float verticalFov, float aspectRatio);
void SetVerticalFovAndAspectRatio(float vFov, float aspectRatio);
Vector3 CornerPoint(int cornerIndex) const;
/// Finds a ray in world space that originates at the eye point and looks in the given direction inside the frustum.
/** The (x,y) coordinate specifies the normalized viewport coordinate through which the ray passes.
Both x and y must be in the range [-1,1].
Specifying (-1, -1) returns the bottom-left corner of the near plane.
The point (1, 1) corresponds to the top-right corner of the near plane. */
Ray UnProject(float x, float y) const;
Vector3 NearPlanePos(float x, float y) const;
Vector3 FarPlanePos(float x, float y) const;
Ray UnProject(const Vector2& xy) const;
Ray UnProjectFromNearPlane(float x, float y) const;
[[nodiscard]] LineSegment UnProjectLineSegment(float x, float y) const;
Vector3 PointInside(float x, float y, float z) const;
Vector3 PointInside(const Vector3& xyz) const;
[[nodiscard]] Vector3 CenterPoint() const;
[[nodiscard]] Vector3 CornerPoint(int cornerIndex) const;
/// Returns a point on the near plane.
/** @param x A value in the range [-1, 1].
@param y A value in the range [-1, 1].
Specifying (-1, -1) returns the bottom-left corner of the near plane.
The point (1, 1) corresponds to the top-right corner of the near plane.
@note This coordinate space is called the normalized viewport coordinate space.
@see FarPlanePos(). */
[[nodiscard]] Vector3 NearPlanePos(float x, float y) const;
[[nodiscard]] Vector3 NearPlanePos(const Vector2& point) const;
/// Returns a point on the far plane.
/** @param x A value in the range [-1, 1].
@param y A value in the range [-1, 1].
Specifying (-1, -1) returns the bottom-left corner of the far plane.
The point (1, 1) corresponds to the top-right corner of the far plane.
@note This coordinate space is called the normalized viewport coordinate space.
@see NearPlanePos(). */
[[nodiscard]] Vector3 FarPlanePos(float x, float y) const;
[[nodiscard]] Vector3 FarPlanePos(const Vector2& point) const;
/// Computes the direction vector that points logically to the right-hand side of the Frustum.
/** This vector together with the member variables 'front' and 'up' form the orthonormal basis of the view frustum.
@see pos, front. */
Vector3 WorldRight() const;
[[nodiscard]] Vector3 WorldRight() const;
[[nodiscard]] Plane TopPlane() const; ///< [similarOverload: LeftPlane] [hideIndex]
[[nodiscard]] Plane BottomPlane() const; ///< [similarOverload: LeftPlane] [hideIndex]
[[nodiscard]] Plane RightPlane() const; ///< [similarOverload: LeftPlane] [hideIndex]
Plane TopPlane() const; ///< [similarOverload: LeftPlane] [hideIndex]
Plane BottomPlane() const; ///< [similarOverload: LeftPlane] [hideIndex]
Plane RightPlane() const; ///< [similarOverload: LeftPlane] [hideIndex]
/// Returns the plane equation of the specified side of this Frustum.
/** The normal vector of the returned plane points outwards from the volume inside the frustum.
This means the negative half-space of the Frustum is the space inside the Frustum.
[indexTitle: Left/Right/Top/BottomPlane]
@see NearPlane(), FarPlane(), GetPlane(), GetPlanes(). */
Plane LeftPlane() const;
[[nodiscard]] Plane LeftPlane() const;
/// Computes the plane equation of the far plane of this Frustum. [similarOverload: NearPlane]
/** The normal vector of the returned plane points outwards from the volume inside the frustum, i.e. away from the eye point.
(towards front). This means the negative half-space of the Frustum is the space inside the Frustum.
@see front, FarPlane(), LeftPlane(), RightPlane(), TopPlane(), BottomPlane(), GetPlane(), GetPlanes(). */
Plane FarPlane() const;
[[nodiscard]] Plane FarPlane() const;
/// Computes the plane equation of the near plane of this Frustum.
/** The normal vector of the returned plane points outwards from the volume inside the frustum, i.e. towards the eye point
(towards -front). This means the negative half-space of the Frustum is the space inside the Frustum.
@see front, FarPlane(), LeftPlane(), RightPlane(), TopPlane(), BottomPlane(), GetPlane(), GetPlanes(). */
Plane NearPlane() const;
[[nodiscard]] Plane NearPlane() const;
/// Computes the width of the near plane quad in world space units.
/** @see NearPlaneHeight(). */
float NearPlaneWidth() const;
[[nodiscard]] float NearPlaneWidth() const;
/// Computes the height of the near plane quad in world space units.
/** @see NearPlaneHeight(). */
float NearPlaneHeight() const;
[[nodiscard]] float NearPlaneHeight() const;
/// Returns the specified plane of this frustum.
/** The normal vector of the returned plane points outwards from the volume inside the frustum.
@param faceIndex A number in the range [0,5], which returns the plane at the selected index from
the array { near, far, left, right, top, bottom} */
Plane GetPlane(int faceIndex) const;
/// Returns all six planes of this Frustum.
/** The planes will be output in the order { near, far, left, right, top, bottom }.
@param outArray [out] A pointer to an array of at least 6 elements. This pointer will receive the planes of this Frustum.
This pointer may not be null.
@see GetPlane(), NearPlane(), FarPlane(), LeftPlane(), RightPlane(), TopPlane(), BottomPlane(). */
void GetPlanes(Plane *outArray) const;
/// Moves this Frustum by the given offset vector.
/** @note This function operates in-place.
@param offset The world space offset to apply to the position of this Frustum.
@see Transform(). */
void Translate(const Vector3& offset);
/// Applies a transformation to this Frustum.
/** @param transform The transformation to apply to this Frustum. This transformation must be
* affine, and must contain an orthogoal set of column vectors (may not contain shear or projection).
@@ -325,13 +399,56 @@ namespace J3ML::Geometry
/** This function returns a Polyhedron representation of this Frustum. This conversion is exact, meaning that the returned
Polyhedron represents exactly the same set of points that this Frustum does.
@see MinimalEnclosingAABB(), MinimalEnclosingOBB(). */
Polyhedron ToPolyhedron() const;
[[nodiscard]] Polyhedron ToPolyhedron() const;
/// Converts this Frustum to a PBVolume.
/** This function returns a plane-bounded volume representation of this Frustum. The conversion is exact, meaning that the
returned PBVolume<6> represents exactly the same set of points that this Frustum does.
@see ToPolyhedron(). */
//PBVolume<6> ToPBVolume() const;
[[nodiscard]] PBVolume<6> ToPBVolume() const;
[[nodiscard]] Vector3 Project(const Vector3& point) const;
/// Computes the matrix that transforms from the world (global) space to the projection space of this Frustum.
/** The matrix computed by this function is simply the concatenation ProjectionMatrix()*ViewMatrix(). This order
of concatenation follows the M*v convention of transforming vectors (as opposed to the v*M convention). This
multiplication order is used, since the matrices ProjectionMatrix() and ViewMatrix() also follow the M*v convention.
@return A matrix that performs the world->view->proj transformation. This matrix is neither invertible or
orthonormal. The returned matrix is built to use the convention Matrix * vector
to map a point between these spaces. (as opposed to the convention v*M).
@see WorldMatrix(), ViewMatrix(), ProjectionMatrix(). */
[[nodiscard]] Matrix4x4 ViewProjMatrix() const;
[[nodiscard]] Matrix4x4 ComputeViewProjMatrix() const;
/// Computes the matrix that transforms from the view space to the world (global) space of this Frustum.
/** @note The returned matrix is the inverse of the matrix returned by ViewMatrix().
@return An orthonormal affine matrix that performs the view->world transformation. The returned
matrix is built to use the convention Matrix * vector to map a point between these spaces.
(as opposed to the convention v*M).
@see ViewMatrix(), ProjectionMatrix(), ViewProjMatrix(). */
[[nodiscard]] Matrix4x4 WorldMatrix() const;
[[nodiscard]] Matrix4x4 ComputeWorldMatrix() const;
/// Computes the matrix that transforms from the world (global) space to the view space of this Frustum.
/** @note The returned matrix is the inverse of the matrix returned by WorldMatrix().
@return An orthonormal affine matrix that performs the world->view transformation. The returned
matrix is built to use the convention Matrix * vector to map a point between these spaces.
(as opposed to the convention v*M).
@see WorldMatrix(), ProjectionMatrix(), ViewProjMatrix(). */
[[nodiscard]] Matrix4x4 ViewMatrix() const;
[[nodiscard]] Matrix4x4 ComputeViewMatrix() const;
/// Computes the matrix that projects from the view space to the projection space of this Frustum.
/** @return A projection matrix that performs the view->proj transformation. This matrix is neither
invertible or orthonormal. The returned matrix is built to use the convention Matrix * vector
to map a point between these spaces. (as opposed to the convention v*M).
@see WorldMatrix(), ViewMatrix(), ViewProjMatrix(). */
[[nodiscard]] Matrix4x4 ProjectionMatrix() const;
[[nodiscard]] Matrix4x4 ComputeProjectionMatrix() const;
/// Tests if the given object is fully contained inside this Frustum.
/** This function returns true if the given object lies inside this Frustum, and false otherwise.
@@ -339,21 +456,27 @@ namespace J3ML::Geometry
due to float inaccuracies, this cannot generally be relied upon.
@todo Add Contains(Circle/Disc/Sphere/Capsule).
@see Distance(), Intersects(), ClosestPoint(). */
bool Contains(const Vector3 &point) const;
bool Contains(const LineSegment &lineSegment) const;
bool Contains(const Triangle &triangle) const;
bool Contains(const Polygon &polygon) const;
bool Contains(const AABB &aabb) const;
bool Contains(const OBB &obb) const;
bool Contains(const Frustum &frustum) const;
bool Contains(const Polyhedron &polyhedron) const;
[[nodiscard]] bool Contains(const Vector3 &point) const;
[[nodiscard]] bool Contains(const LineSegment &lineSegment) const;
[[nodiscard]] bool Contains(const Triangle &triangle) const;
[[nodiscard]] bool Contains(const Polygon &polygon) const;
[[nodiscard]] bool Contains(const AABB &aabb) const;
[[nodiscard]] bool Contains(const OBB &obb) const;
[[nodiscard]] bool Contains(const Frustum &frustum) const;
[[nodiscard]] bool Contains(const Polyhedron &polyhedron) const;
/// Computes the distance between this Frustum and the given object.
/** This function finds the nearest pair of points on this and the given object, and computes their distance.
If the two objects intersect, or one object is contained inside the other, the returned distance is zero.
@todo Add Frustum::Distance(Line/Ray/LineSegment/Plane/Triangle/Polygon/Circle/Disc/AABB/OBB/Capsule/Frustum/Polyhedron).
@see Contains(), Intersects(), ClosestPoint(). */
float Distance(const Vector3 &point) const;
[[nodiscard]] float Distance(const Vector3 &point) const;
/// Computes the closest point inside this frustum to the given point.
/** If the target point lies inside this Frustum, then that point is returned.
@see Distance(), Contains(), Intersects().
@todo Add ClosestPoint(Line/Ray/LineSegment/Plane/Triangle/Polygon/Circle/Disc/AABB/OBB/Capsule/Frustum/Polyhedron). */
[[nodiscard]] Vector3 ClosestPoint(const Vector3& point) const;
/// Tests whether this Frustum and the given object intersect.
/** Both objects are treated as "solid", meaning that if one of the objects is fully contained inside
@@ -362,18 +485,19 @@ namespace J3ML::Geometry
The first parameter of this function specifies the other object to test against.
@see Contains(), Distance(), ClosestPoint().
@todo Add Intersects(Circle/Disc). */
bool Intersects(const Ray& ray) const;
//bool Intersects(const Line& line) const;
bool Intersects(const LineSegment& lineSegment) const;
bool Intersects(const AABB& aabb) const;
bool Intersects(const OBB& obb) const;
bool Intersects(const Plane& plane) const;
bool Intersects(const Triangle& triangle) const;
bool Intersects(const Polygon& lineSegment) const;
bool Intersects(const Sphere& aabb) const;
bool Intersects(const Capsule& obb) const;
bool Intersects(const Frustum& plane) const;
bool Intersects(const Polyhedron& triangle) const;
[[nodiscard]] bool Intersects(const Ray& ray) const;
[[nodiscard]] bool Intersects(const Line &line) const;
[[nodiscard]] bool Intersects(const LineSegment& lineSegment) const;
[[nodiscard]] bool Intersects(const AABB& aabb) const;
[[nodiscard]] bool Intersects(const OBB& obb) const;
[[nodiscard]] bool Intersects(const Plane& plane) const;
[[nodiscard]] bool Intersects(const Triangle& triangle) const;
[[nodiscard]] bool Intersects(const Polygon& lineSegment) const;
[[nodiscard]] bool Intersects(const Sphere& aabb) const;
[[nodiscard]] bool Intersects(const Capsule& obb) const;
[[nodiscard]] bool Intersects(const Frustum& plane) const;
[[nodiscard]] bool Intersects(const Polyhedron& triangle) const;
/// Projects this Frustum onto the given 1D axis direction vector.
/** This function collapses this Frustum onto an 1D axis for the purposes of e.g. separate axis test computations.
The function returns a 1D range [outMin, outMax] denoting the interval of the projection.
@@ -387,9 +511,23 @@ namespace J3ML::Geometry
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
LineSegment Edge(int edgeIndex) const;
[[nodiscard]] LineSegment Edge(int edgeIndex) const;
bool Intersects(const Line &line) const;
/// Maps a point from the normalized viewport space to the screen space
/** In normalized viewport space, top-left: (-1, 1), top-right: (1,1), bottom-left: (-1, -1), bottom-right: (-1, 1)
In screen space: top-left: (0, 0), top-right: (0, screenWidth-1), bottom-left: (0, screenHeight-1), bottom-right: (screenWidth-1, screenHeight-1).
This mapping is affine.
@see ScreenToViewportSpace(). */
static Vector2 ViewportToScreenSpace(float x, float y, int screenWidth, int screenHeight);
static Vector2 ViewportToScreenSpace(const Vector2& point, int screenWidth, int screenHeight);
/// Maps a point from screen space to normalized viewport space.
/** This function computes the inverse function of ViewportToScreenSpace(). This mapping is affine.
@see ViewportToScreenSpace(). */
static Vector2 ScreenToViewportSpace(float x, float y, int screenWidth, int screenHeight);
static Vector2 ScreenToViewportSpace(const Vector2& point, int screenWidth, int screenHeight);
};
Frustum operator * (const Matrix3x3& transform, const Frustum& frustum);

View File

@@ -0,0 +1,109 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Icosahedron.hpp
/// @desc Icosahedron class implementation, borrowed from http://www.songho.ca/opengl/gl_sphere.html#icosphere
/// @edit 2024-10-22
/** Polyhedron with 12 vertices, 30 edges, and 20 faces (triangles) for OpenGL
If the radius is r, then the length of the edge is (r / sin(2pi/5))
Vertices of icosahedron are constructed with spherical coords by aligning
the north pole to (0,0,r) and the south pole to (0,0,-r). Other 10 vertices
are computed by rotating 72 degrees along y-axis at the elevation angle
+/- 26.565 (=arctan(1/2)).
The unwrapped (paper model) of icosahedron and texture map look like this:
// (S,0) 3S 5S 7S 9S
// /\ /\ /\ /\ /\ : 1st row (5 triangles) //
// /__\/__\/__\/__\/__\ //
// T \ /\ /\ /\ /\ /\ : 2nd row (10 triangles) //
// \/__\/__\/__\/__\/__\ //
// 2T \ /\ /\ /\ /\ / : 3rd row (5 triangles) //
// \/ \/ \/ \/ \/ //
// 2S 4S 6S 8S (10S,3T)
// where S = 186/2048 = 0.0908203
// T = 322/1024 = 0.3144531
// If a texture size is 2048x1024, S=186, T=322
AUTHOR: Song Ho Ahn (song.ahn@gmail.com)
*/
#include <vector>
#include "Color4.hpp"
#pragma once
namespace J3ML
{
class Icosahedron
{
public:
float radius;
float edgeLength;
Icosahedron(float radius = 1.0f);
float Radius() const { return radius; }
void Radius(float new_radius) {radius = new_radius;}
float EdgeLength() const { return edgeLength;}
void EdgeLength(float new_edge_length) { edgeLength = new_edge_length;}
// for vertex data
unsigned int VertexCount() const;
unsigned int NormalCount() const;
unsigned int TexCoordCount() const;
unsigned int IndexCount() const;
unsigned int LineIndexCount() const;
unsigned int TriangleCount() const;
unsigned int VertexSize() const;
unsigned int NormalSize() const;
unsigned int TexCoordSize() const;
unsigned int IndexSize() const;
unsigned int LineIndexSize() const;
const float* Vertices() const;
const float* Normals() const;
const float* TexCoords() const;
const unsigned int* Indices() const;
const unsigned int* LineIndices() const;
// for interleaved vertices: V/N/T
unsigned int InterleavedVertexCount() const;
unsigned int InterleavedVertexSize() const;
int InterleavedStride() const;
const float* InterleavedVertices() const;
// draw in VertexArray mode
void draw() const;
void drawLines(const Color4& lineColor) const;
void drawWithLines(const Color4& lineColor) const;
protected:
private:
// static functions
static void computeFaceNormal(float v1[3], float v2[3], float v3[3], float n[3]);
// member functions
void updateRadius();
std::vector<float> computeVertices();
void buildVertices();
void buildInterleavedVertices();
void addVertices(float v1[3], float v2[3], float v3[3]);
void addNormals(float n1[3], float n2[3], float n3[3]);
void addTexCoords(float t1[2], float t2[2], float t3[2]);
void addIndices(unsigned int i1, unsigned int i2, unsigned int i3);
void addLineIndices(unsigned int indexFrom);
// member vars
//float radius;
//float edgeLength;
std::vector<float> vertices;
std::vector<float> normals;
std::vector<float> texCoords;
std::vector<unsigned int> indices;
std::vector<unsigned int> lineIndices;
};
}

View File

@@ -1,12 +0,0 @@
#pragma once
namespace J3ML::Geometry
{
/// A KD-tree accelleration structure for static geometry.
class KdTree
{
};
}

View File

@@ -0,0 +1,83 @@
#pragma once
namespace J3ML::Geometry
{
enum CardinalAxis
{
AxisX = 0,
AxisY,
AxisZ,
AxisNone
};
struct KdTreeNode
{
/// If this is an inner node, specifies along which axis this node is split.
/// If this is a leaf, has the value AxisNone.
unsigned splitAxis : 2;
/// If this is an inner node, specifies the index/offset to the child node pair.
/// If this is a leaf, the value is undefined.
unsigned childIndex : 30;
union
{
/// If this is an inner node, specifies the position along the cardinal axis of the split.
float splitPos;
/// If this is a leaf, specifies the index/ofset to the object bucket for this leaf.
/// If zero, this leaf does not have a bucket associated with it. (empty leaf)
u32 bucketIndex;
};
/// If true, this leaf does not contain any objects.
bool IsEmptyLeaf() const { assert(IsLeaf()); return bucketIndex == 0; }
bool IsLeaf() const { return splitAxis == AxisNone; }
int LeftChildIndex() const { return (int)childIndex; }
int RightChildIndex() const { return (int) childIndex+1; }
CardinalAxis SplitAxis() const { return (CardinalAxis) splitAxis;}
};
/// A KD-tree accelleration structure for static geometry.
template <typename T>
class KdTree
{
public:
KdTree() {}
//~KDTree() { /* TODO: FILL */}
void AddObjects(const T *objects, int numObjects);
void Build();
void Clear();
u32* Bucket(int bucketIndex);
const u32* Bucket(int bucketIndex) const;
T& Object(int objectIndex);
const T& Object(int objectIndex) const;
int NumObjects() const;
int NumNodes() const;
int NumLeaves() const;
int NumInnerNodes() const;
int TreeHeight() const;
KdTreeNode* Root();
const KdTreeNode* Root() const;
bool IsPartOfThisTree(const KdTreeNode *node) const;
//const AABB& BoundingAABB() const { return rootAABB;}
/// Traverses a ray through this kD-tree, and calls the given leafCallback function for each leaf of the tree.
/// Uses the "recursive B" method from Vlastimil Havran's thesis.
template <typename Func>
void RayQuery(const Ray& r, Func &leaftCallback);
template <typename Func>
void AABBQuery(const AABB& aabb, Func& leafCallback);
private:
static const int maxNodes = 256 * 1024;
static const int maxTreeDepth = 30;
std::vector<KdTreeNode> nodes;
//std::vector<u8, Aligned
};
}

View File

@@ -1,19 +1,20 @@
#pragma once
#include "LineSegment.h"
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/LineSegment.hpp>
namespace J3ML::Geometry
{
class Line
class Line : public Shape
{
public:
Vector3 Position; /// Specifies the origin of this line.
Vector3 Direction; /// The normalized direction vector of this ray.
public:
static void
ClosestPointLineLine(const Vector3 &v0, const Vector3 &v10, const Vector3 &v2, const Vector3 &v32, float &d,
float &d2);
Line(const Vector3& position, const Vector3& direction) : Position(position), Direction(direction) {}
static void ClosestPointLineLine(const Vector3 &v0, const Vector3 &v10, const Vector3 &v2, const Vector3 &v32, float &d, float &d2);
Vector3 ClosestPoint(const J3ML::Geometry::LineSegment &other, float &d, float &d2) const;

View File

@@ -1,92 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.h>
#include "Plane.h"
#include <J3ML/Geometry/Common.h>
namespace J3ML::Geometry
{
/// Clamps the given input value to the range [min, max].
/** @see Clamp01(), Min(), Max(). */
template<typename T>
T Clamp(const T &val, const T &floor, const T &ceil)
{
assert(floor <= ceil);
return val <= ceil ? (val >= floor ? val : floor) : ceil;
}
/// Clamps the given input value to the range [0, 1].
/** @see Clamp(), Min(), Max(). */
template<typename T>
T Clamp01(const T &val) { return Clamp(val, T(0), T(1)); }
using LinearAlgebra::Vector3;
class LineSegment
{
public:
Vector3 A;
Vector3 B;
public:
LineSegment();
LineSegment(const Vector3& a, const Vector3& b);
/// Computes the closest point on this line segment to the given object.
/** @param d [out] If specified, this parameter receives the normalized distance along
this line segment which specifies the closest point on this line segment to
the specified point.
@return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Vector3 &point) const;
bool Contains(const Vector3& point, float distanceThreshold = 1e-3f) const;
/// Quickly returns an arbitrary point inside this LineSegment. Used in GJK intersection test.
inline Vector3 AnyPointFast() const { return A; }
/// Computes an extreme point of this LineSegment in the given direction.
/** An extreme point is a farthest point along this LineSegment in the given direction. Given a direction,
this point is not necessarily unique.
@param direction The direction vector of the direction to find the extreme point. This vector may
be unnormalized, but may not be null.
@return An extreme point of this LineSegment in the given direction. The returned point is always
either a or b.
@see a, b.*/
Vector3 ExtremePoint(const Vector3 &direction) const;
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
Vector3 GetPoint(float d) const;
Vector3 ClosestPoint(const Vector3 &point, float &d) const;
Vector3 ClosestPoint(const Ray &other, float &d, float &d2) const;
float Distance(const Vector3 &point) const;
float DistanceSq(const Vector3& point) const;
float Distance(const Vector3 &point, float &d) const;
float Distance(const Ray &other) const;
float Distance(const Ray &other, float &d) const;
float Distance(const Ray &other, float &d, float &d2) const;
float Distance(const Line &other) const;
float Distance(const Line &other, float &d) const;
float Distance(const Line &other, float &d, float &d2) const;
float Distance(const LineSegment &other) const;
float Distance(const LineSegment &other, float &d) const;
float Distance(const LineSegment &other, float &d, float &d2) const;
float Distance(const Plane& other) const;
Vector3 Dir() const;
float Length() const;
float LengthSq() const;
float DistanceSq(const LineSegment &other) const;
Vector3 ClosestPoint(const LineSegment &other, float &d, float &d2) const;
Vector3 ClosestPoint(const Line &other, float &d, float &d2) const;
bool Intersects(const LineSegment &segment) const;
};
LineSegment operator *(const Matrix4x4 &transform, const LineSegment &l);
}

View File

@@ -0,0 +1,244 @@
#pragma once
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
namespace J3ML::Geometry
{
/// A line segment in 3D space is a finite line with a start and end point.
class LineSegment : public Shape2D
{
public:
Vector3 A; /// The starting point of this line segment.
Vector3 B; /// The end point of this line segment.
public:
/// The default constructor does not initialize any members of this class.
/** This means that the values of the members A and B are undefined after creating a new LineSegment using this
default constructor. Remember to assign to them before use.
@see A, B. */
LineSegment();
/// Constructs a line segment through the given end points.
/** @see a, b. */
LineSegment(const Vector3& a, const Vector3& b);
/// Constructs a line segment from a ray or a line.
/** This constructor takes the ray/line origin position as the starting point of this line segment, and defines the end point
of the line segment using the given distance parameter.
@param d The distance along the ray/line for the end point of this line segment. This will set b = ray.pos + d * ray.dir
as the end point. When converting a ray to a line segment, it is possible to pass in a d value < 0, but in that case
the resulting line segment will not lie on the ray.
@see a, b, classes Ray, Line, Line::GetPoint(), Ray::GetPoint() */
explicit LineSegment(const Ray &ray, float d);
explicit LineSegment(const Line &line, float d);
/// Tests if the given point or line segment is contained on this line segment.
/** @param distanceThreshold Because a line segment is an one-dimensional object in 3D space, an epsilon value
is used as a threshold for this test. This effectively transforms this line segment to a capsule with
the radius indicated by this value.
@return True if this line segment contains the given point or line segment.
@see Intersects, ClosestPoint(), Distance(). */
bool Contains(const Vector3& point, float distanceThreshold = 1e-3f) const;
bool Contains(const LineSegment &lineSegment, float distanceThreshold = 1e-3f) const;
/// Quickly returns an arbitrary point inside this LineSegment. Used in GJK intersection test.
Vector3 AnyPointFast() const;
/// Computes an extreme point of this LineSegment in the given direction.
/** An extreme point is a farthest point along this LineSegment in the given direction. Given a direction,
this point is not necessarily unique.
@param direction The direction vector of the direction to find the extreme point. This vector may
be unnormalized, but may not be null.
@return An extreme point of this LineSegment in the given direction. The returned point is always
either a or b.
@see a, b.*/
[[nodiscard]] Vector3 ExtremePoint(const Vector3 &direction) const;
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
/// Translates this LineSegment in world space.
/** @param offset The amount of displacement to apply to this LineSegment, in world space coordinates.
@see Transform(). */
void Translate(const Vector3 &offset);
/// Applies a transformation to this line.
/** This function operates in-place.
@see Translate(), classes Matrix3x3, Matrix4x4, Quaternion, Transform(). */
void Transform(const Matrix3x3 &transform);
void Transform(const Matrix4x4 &transform);
void Transform(const Quaternion &transform);
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
/// Returns a point on the line.
/** @param d The normalized distance along the line segment to compute. If a value in the range [0, 1] is passed, then the
returned point lies along this line segment. If some other value is specified, the returned point lies on the
line defined by this line segment, but not inside the interval from a to b.
@note The meaning of d here differs from Line::GetPoint and Ray::GetPoint. For the class LineSegment,
GetPoint(0) returns a, and GetPoint(1) returns b. This means that GetPoint(1) will not generally be exactly one unit
away from the starting point of this line segment, as is the case with Line and Ray.
@return (1-d)*a + d*b.
@see a, b, Line::GetPoint(), Ray::GetPoint(). */
[[nodiscard]] Vector3 GetPoint(float d) const;
/// Returns the center point of this line segment.
/** This function is the same as calling GetPoint(0.5f), but provided here as conveniency.
@see GetPoint(). */
[[nodiscard]] Vector3 CenterPoint() const;
/// Reverses the direction of this line segment.
/** This function swaps the start and end points of this line segment so that it runs from b to a.
This does not have an effect on the set of points represented by this line segment, but it reverses
the direction of the vector returned by Dir().
@note This function operates in-place.
@see a, b, Dir(). */
void Reverse();
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Vector3& point) const;
/// Computes the closest point on this line segment to the given object.
/** @param d [out] If specified, this parameter receives the normalized distance along
this line segment which specifies the closest point on this line segment to
the specified point.
@return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Vector3& point, float& d) const;
/** @param d2 [out] If specified, this parameter receives the (normalized, in case of line segment)
distance along the other line object which specifies the closest point on that line to
this line segment. */
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Ray& other) const;
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Ray& other, float &d) const;
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Ray& other, float &d, float &d2) const;
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Line& other) const;
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Line& other, float &d) const;
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const Line &other, float &d, float &d2) const;
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const LineSegment& other) const;
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const LineSegment& other, float &d) const;
/// Computes the closest point on this line segment to the given object.
/** @return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector3 ClosestPoint(const LineSegment& other, float &d, float &d2) const;
[[nodiscard]] float Distance(const Vector3 &point) const;
[[nodiscard]] float DistanceSq(const Vector3& point) const;
[[nodiscard]] float DistanceSq(const LineSegment &other) const;
float Distance(const Vector3 &point, float &d) const;
[[nodiscard]] float Distance(const Ray &other) const;
float Distance(const Ray &other, float &d) const;
float Distance(const Ray &other, float &d, float &d2) const;
[[nodiscard]] float Distance(const Line &other) const;
float Distance(const Line &other, float &d) const;
float Distance(const Line &other, float &d, float &d2) const;
[[nodiscard]] float Distance(const LineSegment &other) const;
float Distance(const LineSegment &other, float &d) const;
float Distance(const LineSegment &other, float &d, float &d2) const;
[[nodiscard]] float Distance(const Plane& other) const;
/// Returns the normalized direction vector that points in the direction a->b.
/** @note The returned vector is normalized, meaning that its length is 1, not |b-a|.
@see a, b. */
[[nodiscard]] Vector3 Dir() const;
/// Computes the length of this line segment.
/** @return |b-a|.
@see a, b. */
[[nodiscard]] float Length() const;
/// Computes the squared length of this line segment.
/** Calling this function is faster than calling Length(), since this function avoids computing a square root.
If you only need to compare lengths to each other and are not interested in the actual length values,
you can compare by using LengthSq(), instead of Length(), since Sqrt() is an order-preserving
(monotonous and non-decreasing) function. [similarOverload: Length] */
[[nodiscard]] float LengthSq() const;
/// Tests if this line segment is finite.
/** A line segment is <b><i>finite</i></b> if its endpoints a and b do not contain floating-point NaNs or +/-infs
in them.
@return True if both a and b have finite floating-point values. */
[[nodiscard]] bool IsFinite() const;
/// Tests whether this line segment and the given object intersect.
/** Both objects are treated as "solid", meaning that if one of the objects is fully contained inside
another, this function still returns true. (for example, if this line segment is contained inside a sphere)
@todo Output intersection point. */
bool Intersects(const Plane& plane) const;
bool Intersects(const Plane& plane, float* d) const;
bool Intersects(const Triangle& triangle, float* d, Vector3* intersectionPoint) const;
bool Intersects(const Sphere& s, Vector3* intersectionPoint = 0, Vector3* intersectionNormal = 0, float* d = 0) const;
bool Intersects(const AABB& aabb, float& dNear, float& dFar) const;
bool Intersects(const AABB& aabb) const;
bool Intersects(const OBB& obb, float& dNear, float& dFar) const;
bool Intersects(const OBB& obb) const;
bool Intersects(const Capsule& capsule) const;
bool Intersects(const Polygon& polygon) const;
bool Intersects(const Frustum& frustum) const;
bool Intersects(const Polyhedron& polyhedron) const;
/** @param epsilon If testing intersection between two line segments, a distance threshold value is used to account
for floating-point inaccuracies. */
bool Intersects(const LineSegment &segment, float epsilon = 1e-3f) const;
// TODO: Implement Circle class.
// TODO: This signature will be moved to bool Intersects(const Disc& disc) const;
//bool IntersectsDisc(const Circle& disc) const;
/// Converts this LineSegment to a Ray.
/** The pos member of the returned Ray will be equal to a, and the dir member equal to Dir().
@see class Ray, ToLine() */
[[nodiscard]] Ray ToRay() const;
/// Converts this LineSegment to a Line.
/** The pos member of the returned Line will be equal to a, and the dir member equal to Dir().
@see class Line, ToRay() */
[[nodiscard]] Line ToLine() const;
/// Tests if this line segment represents the same set of points than the given line segment.
/** @param distanceThreshold Specifies how much distance threshold to allow in the comparison.
@return True if a == rhs.a && b == rhs.b, or, a == rhs.b && b = rhs.a, within the given epsilon. */
bool Equals(const LineSegment &rhs, float distanceThreshold = 1e-3f) const;
/// Compares whether this LineSegment and the given LineSegment are identical bit-by-bit in the underlying representation.
/** @note Prefer using this over e.g. memcmp, since there can be SSE-related padding in the structures. */
//bool BitEquals(const LineSegment &other) const { return a.BitEquals(other.a) && b.BitEquals(other.b); }
/// Ret
};
LineSegment operator *(const Matrix4x4 &transform, const LineSegment &l);
}

View File

@@ -0,0 +1,185 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file LineSegment2D.hpp
/// @desc A 2D representation of a finite line between two points.
/// @edit 2024-10-22
#pragma once
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
#include <J3ML/LinearAlgebra/Matrix4x4.hpp>
namespace J3ML::Geometry
{
/// A line segment in 2D space is a finite line with a start and end point.
class LineSegment2D {
public:
/// The starting point of this line segment.
Vector2 A;
/// The end point of this line segment.
Vector2 B;
/// The default constructor does not initialize any members of this class.
/** This means that the values of the members a and b are undefined after creating a new LineSegment2D using this
default constructor. Remember to assign to them before use.
@see A, B. */
LineSegment2D() {}
/// Constructs a line segment through the given end points.
LineSegment2D(const Vector2 &a, const Vector2 &b);
/// Returns a point on the line.
/** @param d The normalized distance along the line segment to compute. If a value in the range [0, 1] is passed, then the
returned point lies along this line segment. If some other value is specified, the returned point lies on the line
defined by this line segment, but *not* inside the interval from a to b.
@note The meaning of d here differs from Line2D::GetPoint and Ray2D::GetPoint. For the class LineSegment2D,
GetPoint(0) returns a, and getPoint(1) returns b. This means that GetPoint(1) will not generally be exactly one unit
away from the starting point of this line segment, as is the case with Line2D and Ray2D.
@return (1-d)*a + d*b;
@see a, b, Line2D::GetPoint(), Ray2D::GetPoint() */
Vector2 GetPoint(float d) const;
/// Returns the center point of this line segment.
/** This function is the same as calling GetPoint(0.5f), but provided here as convenience.
@see GetPoint(). */
Vector2 CenterPoint() const;
Vector2 Centroid() const;
/// Reverses the direction of this line segment.
/** This function swaps the start and end points of this line segment so that it runs from b to a.
This does not have an effect on the set of points represented by this line segment, but it reverses
the direction of the vector returned by Dir().
@note This function operates in-place.
@see a, b, Dir(). */
void Reverse();
/// Returns the normalized direction vector that points in the direction a->b.
/** @note The returned vector is normalized, meaning that its length is 1, not |b-a|.
@see a, b. */
Vector2 Dir() const;
/// Quickly returns an arbitrary point inside this LineSegment2D. Used in GJK intersection test.
Vector2 AnyPointFast() const;
/// Computes an extreme point of this LineSegment2D in the given direction.
/** An extreme point is a farthest point along this LineSegment2D in the given direction. Given a direction,
this point is not necessarily unique.
@param direction The direction vector of the direction to find the extreme point. This vector may
be un-normalized, but may not be null.
@return An extreme point of this LineSegment2D in the given direction. The returned point is always
either a or b.
@see a, b. **/
Vector2 ExtremePoint(const Vector2 &direction) const;
Vector2 ExtremePoint(const Vector2 &direction, float &projectionDistance) const;
/// Translates this LineSegment2D in world space.
/** @param offset The amount of displacement to apply to this LineSegment2D, in world space coordinates.
@see Transform(). */
void Translate(const Vector2& offset);
/// Applies a transformation to this line.
/** This function operates in-place.
@see Translate(), Transform(), classes Matrix3x3, Matrix4x4, Quaternion*/
void Transform(const Matrix3x3& transform);
void Transform(const Matrix4x4& transform);
void Transform(const Quaternion& transform);
/// Computes the length of this line segment.
/** @return |b - a|
@see a, b. */
float Length() const;
/// Computes the squared length of this line segment.
/** Calling this function is faster than calling Length(), since this function avoids computing a square root.
If you only need to compare lengths to each other and are not interested in the actual length values,
you can compare using LengthSq(), instead of Length(), since Sqrt() is an order-preserving,
(monotonous and non-decreasing) function. */
float LengthSq() const;
float LengthSquared() const;
/// Tests if this line segment is finite
/** A line segment is finite if its endpoints a and b do not contain floating-point NaNs for +/- infs
in them.
@return True if both a and b have finite floating-point values. */
bool IsFinite() const;
/// Tests if this line segment represents the same set of points than the the given line segment.
/** @param epsilon Specifies how much distance threshold to allow in the comparison.
@return True if a == rhs.a && b == rhs.b, or, a == rhs.b && b == rhs.a, within the given epsilon. */
bool Equals(const LineSegment2D& rhs, float epsilon = 1e-3f) const;
/// Tests if the given point or line segment is contained on this line segment.
/** @param epsilon Because a line segment is a one-dimensional object in 3D space, an epsilon value
is used as a threshold for this test. This effectively transforms this line segment to a capsule with
the radius indicated by this value.
@return True if this line segment contains the given point or line segment.
@see Intersects, ClosestPoint(), Distance(). */
bool Contains(const Vector2& point, float epsilon = 1e-3f) const;
bool Contains(const LineSegment2D& rhs, float epsilon = 1e-3f) const;
/// Computes the closest point on this line segment to the given object.
/** @param d If specified, this parameter receives the normalized distance along
this line segment which specifies the closest point on this line segment to
the specified point.
@return The closest point on this line segment to the given object.
@see Contains(), Distance(), Intersects(). */
Vector2 ClosestPoint(const Vector2& point) const;
Vector2 ClosestPoint(const Vector2& point, float& d) const;
/** @param d2 [out] If specified, this parameter receives the (normalized, in case of line segment)
distance along the other line object which specifies the closest point on that line to
this line segment. */
Vector2 ClosestPoint(const LineSegment2D& other) const;
Vector2 ClosestPoint(const LineSegment2D& other, float& d) const;
Vector2 ClosestPoint(const LineSegment2D& other, float& d, float& d2) const;
/// Computes the distance between this line segment and the given object.
/** @param d [out] If specified, this parameter receives the normalized distance along
this line segment which specifies the closest point on this line segment to
the specified point.
@return The distance between this line segment and the given object.
@see */
float Distance(const Vector2& point) const;
float Distance(const Vector2& point, float& d) const;
/** @param d2 [out] If specified, this parameter receives the (normalized, in case of line segment)
distance along the other line object which specifies the closest point on that line to
this line segment. */
float Distance(const LineSegment2D& other) const;
float Distance(const LineSegment2D& other, float& d) const;
float Distance(const LineSegment2D& other, float& d, float& d2) const;
float DistanceSq(const Vector2& point) const;
float DistanceSq(const LineSegment2D& other) const;
/** @param epsilon If testing intersection between two line segments, a distance threshold value is used to account
for floating point inaccuracies. */
bool Intersects(const LineSegment2D& lineSegment, float epsilon = 1e-3f) const;
/// Projects this LineSegment2D onto the given 1D axis direction vector.
/** This function collapses this LineSegment2D onto a 1D axis for the purposes of e.g. separate axis test computations.
This function returns a 1D range [outMin, outNax] denoting the interval of the projection.
@param direction The 1D axis to project to. This vector may be unnormalized, in which case the output
of this function gets scaled by the length of this vector.
@param outMin [out] Returns the minimum extent of this vector along the projection axis.
@param outMax [out] Returns the maximum extent of this vector along the projection axis. */
void ProjectToAxis(const Vector2& direction, float& outMin, float& outMax) const;
protected:
private:
};
LineSegment2D operator * (const Matrix3x3& transform, const LineSegment2D& line);
LineSegment2D operator * (const Matrix4x4& transform, const LineSegment2D& line);
LineSegment2D operator * (const Quaternion& transform, const LineSegment2D& line);
}

View File

@@ -1,8 +1,8 @@
#pragma once
#include <J3ML/Geometry/Common.h>
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/Polyhedron.h>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/LinearAlgebra.hpp>
namespace J3ML::Geometry {
@@ -108,5 +108,30 @@ namespace J3ML::Geometry {
Matrix4x4 LocalToWorld() const;
Matrix4x4 WorldToLocal() const;
Vector3 ClosestPoint(const Vector3 &targetPoint) const;
/// Expands this OBB to enclose the given object. The axis directions of this OBB remain intact.
/** This function operates in-place. This function does not necessarily result in an OBB that is an
optimal fit for the previous OBB and the given point. */
void Enclose(const Vector3 & vector3);
float Distance(const Vector3 &point) const;
/// Tests if the given object is fully contained inside this OBB.
/** This function returns true if the given object lies inside this OBB, and false otherwise.
@note The comparison is performed using less-or-equal, so the faces of this OBB count as being inside,
but due to float inaccuracies, this cannot generally be relied upon. */
bool Contains(const Vector3& point) const;
bool Contains(const LineSegment&) const;
bool Contains(const AABB&) const;
bool Contains(const OBB&) const;
bool Contains(const Triangle&) const;
bool Contains(const Polygon&) const;
bool Contains(const Frustum&) const;
bool Contains(const Polyhedron&) const;
};
}

View File

@@ -0,0 +1,243 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file PBVolume.hpp
/// @desc Implements a convex polyhedron data structure.
/// @edit 2024-07-09
#pragma once
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/Polyhedron.hpp>
#include <J3ML/Geometry/Plane.hpp>
#include <J3ML/Geometry/Sphere.hpp>
namespace J3ML::Geometry
{
enum class CullTestResult
{
// The tested objects don't intersect - they are fully disjoint.
Outside,
// The tested object is at least not fully contained inside the other object, but no other information is known.
// The objects might intersect or be disjoint.
NotContained,
// The tested object is fully contained inside the other object.
Inside
};
/// PBVolume is a "plane bounded volume", a convex polyhedron represented by a set
/// of planes. The number of planes is fixed at compile time so that compilers are able
template <int N>
class PBVolume
{
public:
Plane p[N];
int NumPlanes() const { return N; }
bool Contains(const Vector3& point) const {
for (int i = 0; i< N; ++i)
if (p[i].SignedDistance(point) > 0.f)
return false;
return true;
}
CullTestResult InsideOrIntersects(const AABB& aabb) const {
CullTestResult result = CullTestResult::Inside;
for (int i = 0; i < N; ++i) {
Vector3 nPoint;
Vector3 pPoint;
nPoint.x = (p[i].normal.x < 0.f ? aabb.maxPoint.x : aabb.minPoint.x);
nPoint.y = (p[i].normal.y < 0.f ? aabb.maxPoint.y : aabb.minPoint.y);
nPoint.z = (p[i].normal.z < 0.f ? aabb.maxPoint.z : aabb.minPoint.z);
// Find the n and p points of the aabb. (The nearest and farthes corners relative to the plane)
//const vec &sign = npPointsSignLUT[((p[i].normal.z >= 0.f) ? 4 : 0) +
// ((p[i].normal.y >= 0.f) ? 2 : 0) +
// ((p[i].normal.x >= 0.f) ? 1 : 0)];
//const vec nPoint = c + sign * r;
//const vec pPoint = c - sign * r;
float a = p[i].SignedDistance(nPoint);
if (a >= 0.f)
return CullTestResult::Outside; // The AABB is certainly outside this PBVolume.
a = p[i].SignedDistance(pPoint);
if (a >= 0.f)
result = CullTestResult::NotContained; // At least one vertex is outside this PBVolume. The whole AABB can't possibly be contained in this PBVolume.
}
// We can return here either TestInside or TestNotContained, but it's possible that the AABB was outside the frustum, and we
// just failed to find a separating axis.
return result;
}
CullTestResult InsideOrIntersects(const Sphere& sphere) const {
CullTestResult result = CullTestResult::Inside;
for(int i = 0; i < N; ++i)
{
float d = p[i].SignedDistance(sphere.Position);
if (d >= sphere.Radius)
return CullTestResult::Outside;
else if (d >= -sphere.Radius)
result = CullTestResult::NotContained;
}
return result;
}
private:
/// A helper struct used only internally in ToPolyhedron.
struct CornerPt
{
int ptIndex; // Index to the Polyhedron list of vertices.
int j, k; // The two plane faces in addition to the main plane that make up this point.
};
bool ContainsExcept(const Vector3 &point, int i, int j, int k) const
{
for(int l = 0; l < N; ++l)
if (l != i && l != j && l != k && p[l].SignedDistance(point) > 0.f)
return false;
return true;
}
public:
Polyhedron ToPolyhedron() const {
Polyhedron ph;
std::vector<CornerPt> faces[N];
for (int i = 0; i < N-2; ++i)
for (int j = i+1; j < N-1; ++j)
for (int k = j+1; k < N; ++k)
{
Vector3 corner;
bool intersects = p[i].Intersects(p[j], p[k], 0, &corner);
if (intersects && ContainsExcept(corner, i, j, k)) {
CornerPt pt;
// Find if this vertex is duplicate of an existing vertex.
bool found = false;
for (size_t l = 0; i < ph.v.size(); ++l)
if (Vector3(ph.v[l]).Equals(corner)) {
found = true;
pt.ptIndex = (int)l;
break;
}
if (!found) // New vertex?
{
ph.v.push_back(corner);
pt.ptIndex = (int)ph.v.size()-1;
} // else existing corner point
pt.j = j;
pt.k = k;
faces[i].push_back(pt);
pt.j = i;
pt.k = k;
faces[j].push_back(pt);
pt.j = i;
pt.k = j;
faces[k].push_back(pt);
}
}
// Check if we got a degenerate polyhedron?
if (ph.v.size() <= 1)
return ph;
else if (ph.v.size() == 2) {
// Create a degenerate face that's an edge.
Polyhedron::Face face;
face.v.push_back(0);
face.v.push_back(1);
ph.f.push_back(face);
return ph;
} else if (ph.v.size() == 3) {
// Create a degenerate face that's a triangle.
Polyhedron::Face face;
face.v.push_back(0);
face.v.push_back(1);
face.v.push_back(2);
ph.f.push_back(face);
return ph;
}
// Connect the edges in each face using selection sort.
for(int i = 0; i < N; ++i)
{
std::vector<CornerPt> &pt = faces[i];
if (pt.size() < 3)
continue;
for(size_t j = 0; j < pt.size()-1; ++j)
{
CornerPt &prev = pt[j];
bool found = false;
for(size_t k = j+1; k < pt.size(); ++k)
{
CornerPt &cur = pt[k];
if (cur.j == prev.k)
{
Swap(cur, pt[j+1]);
found = true;
break;
}
if (cur.k == prev.k)
{
Swap(cur.j, cur.k);
Swap(cur, pt[j+1]);
found = true;
break;
}
}
assert(found);
}
assert(pt[0].j == pt[pt.size()-1].k);
Polyhedron::Face face;
for(size_t j = 0; j < pt.size(); ++j)
{
face.v.push_back(pt[j].ptIndex);
}
ph.f.push_back(face);
}
// Fix up winding directions.
for(size_t i = 0; i < ph.f.size(); ++i)
{
// TODO: Why problem?
//Plane face = ph.FacePlane((int)i);
//for(size_t j = 0; j < ph.v.size(); ++j)
//{
// if (face.SignedDistance(ph.v[j]) > 1e-3f)
// {
// ph.f[i].FlipWindingOrder();
// break;
// }
//}
}
return ph;
}
/// Computes the set intersection of this PBVolume and the PBVolume rhs.
/// That is, returns the convex set of points that are contained in both this and rhs.
/// Set intersection is symmetric, so a.SetIntersection(b) is the same as b.SetIntersection(a).
/// @note The returned PBVolume may contain redundant planes, these are not pruned.
template<int M>
PBVolume<N+M> SetIntersection(const PBVolume<M> &rhs) const
{
PBVolume<N+M> res;
for(int i = 0; i < N; ++i)
res.p[i] = p[i];
for(int i = 0; i < M; ++i)
res.p[N+i] = rhs.p[i];
return res;
}
};
}

View File

@@ -1,12 +1,12 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.h>
#include "Shape.h"
#include "Ray.h"
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry/Forward.hpp>
namespace J3ML::Geometry
{
using J3ML::LinearAlgebra::Vector3;
class Plane : public Shape
{
@@ -15,7 +15,10 @@ namespace J3ML::Geometry
Vector3 Normal;
float distance = 0.f;
public:
Plane() : Shape() {}
Plane() {}
/// Constructs a plane by directly specifying the normal and distance parameters.
Plane(const Vector3& normal, float d);
Plane(const Vector3& v1, const Vector3 &v2, const Vector3& v3);
Plane(const Vector3& pos, const Vector3& norm);
Plane(const Line &line, const Vector3 &normal);
@@ -82,5 +85,22 @@ namespace J3ML::Geometry
LineSegment Project(const LineSegment &segment);
Vector3 Project(const Vector3 &point) const;
/// Projects the given point to the negative half-space of this plane.
Vector3 ProjectToNegativeHalf(const Vector3& point) const;
/// Projects the given point to the positive half-space of this plane.
Vector3 ProjectToPositiveHalf(const Vector3& point) const;
bool IsParallel(const Plane& plane, float epsilon = 1e-3f) const {
return Normal.Equals(plane.Normal, epsilon);
}
/// Returns true if the two planes are equal, and their normals are oriented to the same direction.
bool Equals(const Plane& other, float epsilon = 1e-3f) const {
return IsParallel(other, epsilon) && Math::EqualAbs(distance, other.distance, epsilon);
}
};
}

View File

@@ -1,19 +1,30 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Polygon.hpp
/// @desc The Polygon geometry object.
/// @edit 2024-08-01
#pragma once
#include <J3ML/Geometry/Common.h>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/LinearAlgebra.hpp>
#include <vector>
#include "Shape.h"
#include "J3ML/LinearAlgebra.h"
namespace J3ML::Geometry {
class Polygon : public Shape
class Polygon : public Shape2D
{
public:
std::vector<Vector3> vertices;
/// Quickly returns an arbitrary point inside this AABB. Used in GJK intersection test.
Vector3 AnyPointFast() const { return !vertices.empty() ? vertices[0] : Vector3::NaN; }
Vector3 AnyPointFast() const;
AABB MinimalEnclosingAABB() const;
int NumVertices() const;
@@ -80,6 +91,14 @@ namespace J3ML::Geometry {
Vector3 ClosestPoint(const LineSegment &lineSegment, Vector3 *lineSegmentPt) const;
Vector3 ClosestPoint(const Vector3 &point) const;
/// Converts this Polygon to a Polyhedron representation.
/** This function will create a Polyhedron with two faces, one for the front face of this Polygon,
and one for the back face.
@todo Add ToPolyhedron(float polygonThickness)
@see Triangulate(), MinimalEnclosingAABB(). */
Polyhedron ToPolyhedron() const;
protected:

View File

@@ -1,111 +0,0 @@
#pragma once
#include <J3ML/Geometry/Common.h>
#include <J3ML/Geometry/Shape.h>
#include <vector>
#include <J3ML/LinearAlgebra/Vector3.h>
namespace J3ML::Geometry
{
using namespace J3ML::LinearAlgebra;
// Represents a three-dimensional closed geometric solid defined by flat polygonal faces.
class Polyhedron : public Shape
{
public:
// Stores a list of indices of a single face of a Polygon
struct Face
{
// Specifies the indices of the corner vertices of the polyhedron.
// Indices point to the polyhedron vertex array.
// The face vertices should all lie on the same plane.
// The positive direction of the plane (the direction the face outwards normal points)
// is the one where the vertices are wound in counter-clockwise order.
std::vector<int> v;
// Reverses the winding order of this face. This has the effect of reversing the direction
// the normal of this face points to.
void FlipWindingOrder();
};
// Specifies the vertices of this polyhedron.
std::vector<Vector3> v;
std::vector<Face> f;
int NumVertices() const {return (int)v.size();}
int NumFaces() const { return (int)f.size();}
AABB MinimalEnclosingAABB() const;
Vector3 Vertex(int vertexIndex) const;
bool Contains(const Vector3&) const;
bool Contains(const LineSegment&) const;
bool Contains(const Triangle&) const;
bool Contains(const Polygon&) const;
bool Contains(const AABB&) const;
bool Contains(const OBB&) const;
bool Contains(const Frustum&) const;
bool Contains(const Polyhedron&) const;
bool ContainsConvex(const Vector3&, float epsilon = 1e-4f) const;
bool ContainsConvex(const LineSegment&) const;
bool ContainsConvex(const Triangle&) const;
bool Intersects(const Line&) const;
bool Intersects(const LineSegment&) const;
bool Intersects(const Ray&) const;
bool Intersects(const Plane&) const;
bool Intersects(const Polyhedron&) const;
bool Intersects(const AABB&) const;
bool Intersects(const OBB&) const;
bool Intersects(const Triangle&) const;
bool Intersects(const Polygon&) const;
bool Intersects(const Frustum&) const;
bool Intersects(const Sphere&) const;
bool Intersects(const Capsule& capsule) const;
bool IsClosed() const;
Plane FacePlane(int faceIndex) const;
std::vector<Polygon> Faces() const;
int NumEdges() const;
LineSegment Edge(int edgeIndex) const;
std::vector<std::pair<int, int>> EdgeIndices() const;
std::vector<LineSegment> Edges() const;
Polygon FacePolygon(int faceIndex) const;
Vector3 FaceNormal(int faceIndex) const;
bool IsConvex() const;
Vector3 ApproximateConvexCentroid() const;
int ExtremeVertex(const Vector3 &direction) const;
Vector3 ExtremePoint(const Vector3 &direction) const;
/// Tests if the given face of this Polyhedron contains the given point.
bool FaceContains(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness = 1e-3f) const;
/// A helper for Contains() and FaceContains() tests: Returns a positive value if the given point is contained in the given face,
/// and a negative value if the given point is outside the face. The magnitude of the return value reports a pseudo-distance
/// from the point to the nearest edge of the face polygon. This is used as a robustness/stability criterion to estimate how
/// numerically believable the result is.
float FaceContainmentDistance2D(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness = 1e-5f) const;
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
Vector3 ClosestPoint(const LineSegment& lineSegment, Vector3 *lineSegmentPt) const;
protected:
private:
};
}

View File

@@ -0,0 +1,133 @@
#pragma once
#include <vector>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/LinearAlgebra.hpp>
namespace J3ML::Geometry
{
// Represents a three-dimensional closed geometric solid defined by flat polygonal faces.
class Polyhedron : public Shape
{
public:
// Stores a list of indices of a single face of a Polygon
struct Face
{
// Specifies the indices of the corner vertices of the polyhedron.
// Indices point to the polyhedron vertex array.
// The face vertices should all lie on the same plane.
// The positive direction of the plane (the direction the face outwards normal points)
// is the one where the vertices are wound in counter-clockwise order.
std::vector<int> v;
// Reverses the winding order of this face. This has the effect of reversing the direction
// the normal of this face points to.
void FlipWindingOrder();
};
// Specifies the vertices of this polyhedron.
std::vector<Vector3> v;
std::vector<Face> f;
public:
/// The default constructor creates a null polyhedron.
/** The null polyhedron has 0 vertices and 0 faces.
@see IsNull(). */
Polyhedron() = default;
[[nodiscard]] int NumVertices() const {return (int)v.size();}
[[nodiscard]] int NumFaces() const { return (int)f.size();}
[[nodiscard]] AABB MinimalEnclosingAABB() const;
[[nodiscard]] Vector3 Vertex(int vertexIndex) const;
[[nodiscard]] bool Contains(const Vector3&) const;
[[nodiscard]] bool Contains(const LineSegment&) const;
[[nodiscard]] bool Contains(const Triangle&) const;
[[nodiscard]] bool Contains(const Polygon&) const;
[[nodiscard]] bool Contains(const AABB&) const;
[[nodiscard]] bool Contains(const OBB&) const;
[[nodiscard]] bool Contains(const Frustum&) const;
[[nodiscard]] bool Contains(const Polyhedron&) const;
[[nodiscard]] bool ContainsConvex(const Vector3&, float epsilon = 1e-4f) const;
[[nodiscard]] bool ContainsConvex(const LineSegment&) const;
[[nodiscard]] bool ContainsConvex(const Triangle&) const;
/// Tests whether this polyhedron and the given object intersect.
/** Both objects are treated as "solid", meaning that if one of the objects is fully contained inside
another, this function still returns true. (e.g. in case a line segment is contained inside this polyhedron,
or this polyhedron is contained inside a sphere, etc.)
@return True if an intersection occurs or one of the objects is contained inside the other, false otherwise.
@note This function assumes that this polyhedron is closed and the edges are not self-intersecting.
@see Contains(), ContainsConvex(), ClosestPoint(), ClosestPointConvex(), Distance(), IntersectsConvex().
@todo Add Intersects(Circle/Disc). */
[[nodiscard]] bool Intersects(const Line&) const;
[[nodiscard]] bool Intersects(const LineSegment&) const;
[[nodiscard]] bool Intersects(const Ray&) const;
[[nodiscard]] bool Intersects(const Plane&) const;
[[nodiscard]] bool Intersects(const Polyhedron&) const;
[[nodiscard]] bool Intersects(const AABB& aabb) const;
[[nodiscard]] bool Intersects(const OBB&) const;
[[nodiscard]] bool Intersects(const Triangle&) const;
[[nodiscard]] bool Intersects(const Polygon&) const;
[[nodiscard]] bool Intersects(const Frustum&) const;
[[nodiscard]] bool Intersects(const Sphere&) const;
[[nodiscard]] bool Intersects(const Capsule& capsule) const;
[[nodiscard]] bool IsClosed() const;
[[nodiscard]] Plane FacePlane(int faceIndex) const;
[[nodiscard]] std::vector<Polygon> Faces() const;
[[nodiscard]] int NumEdges() const;
[[nodiscard]] LineSegment Edge(int edgeIndex) const;
[[nodiscard]] std::vector<std::pair<int, int>> EdgeIndices() const;
[[nodiscard]] std::vector<LineSegment> Edges() const;
[[nodiscard]] Polygon FacePolygon(int faceIndex) const;
[[nodiscard]] Vector3 FaceNormal(int faceIndex) const;
[[nodiscard]] bool IsConvex() const;
/// Returns true if the Euler formula (V + F - E == 2) holds for this Polyhedron.
/** The running time is O(E) ~ O(V).
@see NumVertices(), NumEdges(), NumFaces(). */
[[nodiscard]] bool EulerFormulaHolds() const;
[[nodiscard]] Vector3 ApproximateConvexCentroid() const;
[[nodiscard]] int ExtremeVertex(const Vector3 &direction) const;
[[nodiscard]] Vector3 ExtremePoint(const Vector3 &direction) const;
/// Tests if the given face of this Polyhedron contains the given point.
[[nodiscard]] bool FaceContains(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness = 1e-3f) const;
/// A helper for Contains() and FaceContains() tests: Returns a positive value if the given point is contained in the given face,
/// and a negative value if the given point is outside the face. The magnitude of the return value reports a pseudo-distance
/// from the point to the nearest edge of the face polygon. This is used as a robustness/stability criterion to estimate how
/// numerically believable the result is.
[[nodiscard]] float FaceContainmentDistance2D(int faceIndex, const Vector3 &worldSpacePoint, float polygonThickness = 1e-5f) const;
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
Vector3 ClosestPoint(const LineSegment& lineSegment, Vector3 *lineSegmentPt) const;
[[nodiscard]] Vector3 ClosestPoint(const Vector3& point) const;
/// Returns true if this polyhedron has 0 vertices and 0 faces.
bool IsNull() const { return v.empty() && f.empty(); }
protected:
private:
};
}

View File

@@ -2,14 +2,13 @@
#include <vector>
#include <cstdint>
#include <J3ML/LinearAlgebra/Vector2.h>
#include "AABB2D.h"
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/Geometry/AABB2D.hpp>
namespace J3ML::Geometry {
using LinearAlgebra::Vector2;
template<typename T>
class QuadTree {
/// A fixed split rule for all QuadTrees: A QuadTree leaf node is only ever split if the leaf contains at least this many objects.
@@ -366,4 +365,4 @@ namespace J3ML::Geometry {
leaf->objects.pop_back();
}
}
}
}

View File

@@ -1,67 +0,0 @@
//
// Created by dawsh on 1/25/24.
//
#pragma once
#include <J3ML/LinearAlgebra/Vector3.h>
#include <vector>
#include "TriangleMesh.h"
#include "Frustum.h"
#include "OBB.h"
namespace J3ML::Geometry
{
using J3ML::LinearAlgebra::Vector3;
// RaycastResult structure containing the first object the ray collides with,
// the surface intersection point,
// and the surface normal at the point of intersection.
struct RaycastResult
{
Vector3 Intersection;
Vector3 SurfaceNormal;
bool Hit;
Shape* Target;
static RaycastResult NoHit() { return {Vector3::NaN, Vector3::NaN, false, nullptr};}
};
// A ray in 3D space is a line that starts from an origin point and extends to infinity in one direction
class Ray
{
public:
// The position of this ray.
Vector3 Origin;
// The normalized direction vector of this ray.
// @note: For proper functionality, this direction vector needs to always be normalized
Vector3 Direction;
Ray() {}
Ray(const Vector3& pos, const Vector3& dir);
//explicit Ray(const Line& line);
explicit Ray(const LineSegment& lineSegment);
bool IsFinite() const;
Vector3 GetPoint(float distance) const;
RaycastResult Cast(const Triangle& target, float maxDistance = 99999999);
RaycastResult Cast(const Plane& target, float maxDistance = 99999999);
RaycastResult Cast(const AABB& target, float maxDistance = 99999999);
// https://gdbooks.gitbooks.io/3dcollisions/content/Chapter3/raycast_sphere.html
RaycastResult Cast(const Sphere& target, float maxDistance = 99999999);
RaycastResult Cast(const OBB& target, float maxDistance = 99999999);
RaycastResult Cast(const Capsule& target, float maxDistance = 99999999);
RaycastResult Cast(const Frustum& target, float maxDistance = 99999999);
RaycastResult Cast(const TriangleMesh& target, float maxDistance = 9999999);
// Returns a RaycastResult structure containing the first object the ray collides with,
// the surface intersection point,
// and the surface normal at the point of intersection.
RaycastResult Cast(std::vector<Shape> shapes, float maxDistance = 99999999);
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
Vector3 ClosestPoint(const LineSegment&, float &d, float &d2) const;
Vector3 ClosestPoint(const Vector3 &targetPoint, float &d) const;
};
}

View File

@@ -0,0 +1,164 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Ray.hpp
/// @desc The Ray geometry object. Used for Raycasting - intersection testing against geometric solids.
/// @edit 2024-07-06
#pragma once
#include <format>
#include <J3ML/LinearAlgebra.hpp>
#include <vector>
#include <ostream>
#include <iosfwd>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
namespace J3ML::Geometry
{
using J3ML::LinearAlgebra::Vector3;
// RaycastResult structure containing the first object the ray collides with,
// the surface intersection point,
// and the surface normal at the point of intersection.
struct RaycastResult
{
Vector3 Intersection;
Vector3 SurfaceNormal;
bool Hit;
//Shape* Target;
static RaycastResult NoHit() { return {Vector3::NaN, Vector3::NaN, false};}
};
/// A ray in 3D space is a line that starts from an origin point and extends to infinity in one direction
class Ray : public Shape
{
public: // Properties
// The position of this ray.
Vector3 Origin;
// The normalized direction vector of this ray.
// @note: For proper functionality, this direction vector needs to always be normalized
Vector3 Direction;
public: // Constructors
// The default constructor does not initialize any members of this class.
/** This means that the values of the members pos and dir are undefined after creating a new Ray using this
default constructor. Remember to assign to them before use.
@see pos, dir. */
Ray() = default;
/// Constructs a new ray by explicitly specifying the member variables.
/** @param pos The origin position of the ray.
@param dir The direction of the ray. This vector must be normalized, this function will not normalize
the vector for you (for performance reasons).
@see pos, dir. */
Ray(const Vector3& pos, const Vector3& dir);
/// Converts a Line to a Ray.
/** This conversion simply copies the members pos and dir over from the given Line to this Ray.
This meansthat the new ray starts at the same position, but only extends to one direction in space, instead of two.
@see class Line, ToLine() */
explicit Ray(const Line& line);
/// Converts a LineSegment to a Ray.
/** This constructor sets pos = lineSegment.a, and dir = (lineSegment.b - lineSegment.a).Normalized()
@see class LineSegment(), ToLineSegment() */
explicit Ray(const LineSegment& lineSegment);
public: // Methods
bool IsFinite() const;
/// Gets a point along the ray at the given distance.
/** Use this function to convert a 1D parametric point along the ray to a 3D point in the linear space.
@param distance The point to compute. GetPoint(0) will return pos. GetPoint(t) will return a point
at distance |t| from pos. Passing in negative values is allowed, but in that case, the
returned point does not actually lie on this Ray.
@return pos + distance * dir
@see pos, dir. */
Vector3 GetPoint(float distance) const;
void Translate(const Vector3& offset);
void Transform(const Matrix3x3& transform);
void Transform(const Matrix4x4& transform);
void Transform(const Quaternion& transform);
bool Contains(const Vector3& point, float distanceThreshold = 1e-3f) const;
bool Contains(const LineSegment& lineSegment, float distanceThreshhold = 1e-3f) const;
bool Equals(const Ray& otherRay, float epsilon = 1e-3f);
bool BitEquals(const Ray& other);
float Distance(const Ray& other) const;
float Distance(const Ray& other, float& d) const;
float Distance(const Ray& other, float&d, float& d2) const;
float Distance(const Line& other) const;
float Distance(const Line& other, float& d) const;
float Distance(const Line& other, float& d, float& d2) const;
float Distance(const LineSegment& other) const;
float Distance(const LineSegment& other, float& d) const;
float Distance(const LineSegment& other, float& d, float& d2) const;
float Distance(const Sphere& other) const;
float Distance(const Capsule& other) const;
Vector3 ClosestPoint(const Vector3& targetPoint) const;
Vector3 ClosestPoint(const Vector3 &targetPoint, float &d) const;
Vector3 ClosestPoint(const Ray& other) const;
Vector3 ClosestPoint(const Ray& other, float& d) const;
Vector3 ClosestPoint(const Ray& other, float& d, float& d2) const;
Vector3 ClosestPoint(const Line& other) const;
Vector3 ClosestPoint(const Line& other, float& d) const;
Vector3 ClosestPoint(const Line& other, float& d, float& d2) const;
Vector3 ClosestPoint(const LineSegment& other) const;
Vector3 ClosestPoint(const LineSegment& other, float& d) const;
Vector3 ClosestPoint(const LineSegment&, float &d, float &d2) const;
bool Intersects(const Triangle& triangle, float* d, Vector3* intersectionPoint) const;
bool Intersects(const Triangle& triangle) const;
bool Intersects(const Plane& plane, float* d);
bool Intersects(const Plane& plane) const;
bool Intersects(const Sphere& s, Vector3* intersectionPoint, Vector3* intersectionNormal, float* d) const;
bool Intersects(const Sphere& s) const;
bool Intersects(const AABB& aabb, float& dNear, float& dFar) const;
bool Intersects(const AABB& aabb) const;
bool Intersects(const OBB& aabb, float& dNear, float& dFar) const;
bool Intersects(const OBB& aabb) const;
bool Intersects(const Capsule& aabb) const;
bool Intersects(const Polygon& aabb) const;
bool Intersects(const Frustum& aabb) const;
bool Intersects(const Polyhedron& aabb) const;
Line ToLine() const;
LineSegment ToLineSegment(float d) const;
LineSegment ToLineSegment(float dStart, float dEnd) const;
RaycastResult Cast(const Triangle& target, float maxDistance = 99999999);
RaycastResult Cast(const Plane& target, float maxDistance = 99999999);
RaycastResult Cast(const AABB& target, float maxDistance = 99999999);
// https://gdbooks.gitbooks.io/3dcollisions/content/Chapter3/raycast_sphere.html
RaycastResult Cast(const Sphere& target, float maxDistance = 99999999);
RaycastResult Cast(const OBB& target, float maxDistance = 99999999);
RaycastResult Cast(const Capsule& target, float maxDistance = 99999999);
RaycastResult Cast(const Frustum& target, float maxDistance = 99999999);
RaycastResult Cast(const TriangleMesh& target, float maxDistance = 9999999);
// Returns a RaycastResult structure containing the first object the ray collides with,
// the surface intersection point,
// and the surface normal at the point of intersection.
// RaycastResult Cast(std::vector<Shape> shapes, float maxDistance = 99999999);
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
[[nodiscard]] std::string ToString() const;
};
std::ostream& operator << (std::ostream& o, const Ray& ray);
}

View File

@@ -1,28 +0,0 @@
#pragma once
namespace J3ML::Geometry
{
class GeometricPrimitive
{
public:
protected:
private:
};
class Shape
{
public:
virtual ~Shape() = default; //Polymorphic for dynamic_cast.
protected:
private:
};
class Shape2D
{
public:
virtual ~Shape2D() = default;
protected:
private:
};
}

View File

@@ -0,0 +1,8 @@
#pragma once
class Shape {
public:
virtual ~Shape() = default;
};
class Shape2D : public Shape{};

View File

@@ -1,29 +1,57 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Sphere.hpp
/// @desc The Sphere geometry object.
/// @edit 2024-07-06
#pragma once
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry.h>
#include <J3ML/LinearAlgebra/Matrix3x3.h>
#include <J3ML/LinearAlgebra/Matrix4x4.h>
#include <J3ML/Geometry/LineSegment.h>
#include <J3ML/Geometry/TriangleMesh.h>
#include <J3ML/Geometry/Shape.h>
namespace J3ML::Geometry
{
using J3ML::LinearAlgebra::Matrix3x3;
using J3ML::LinearAlgebra::Matrix4x4;
// A mathematical representation of a 3-dimensional sphere
class Sphere : public Shape
{
public:
Vector3 Position;
float Radius;
public:
public: // Properties
Vector3 Position; // The center point of this sphere.
float Radius; /// The radius of this sphere.
public: // Constructors
/// The default constructor does not initialize any members of this class.
/** This means that the values of the members pos and r are undefined after creating a new Sphere using this
default constructor. Remember to assign to them before use.
@see pos, r. */
Sphere() {}
/// Constructs a sphere with a given position and radius.
/** @param radius A value > 0 constructs a sphere with positive volume. A value of <= 0 is valid, and constructs a degenerate sphere.
@see pos, r, IsFinite(), IsDegenerate() */
Sphere(const Vector3& pos, float radius) : Position(pos), Radius(radius) {}
/// Constructs a sphere that passes through the given two points.
/** The constructed sphere will be the minimal sphere that encloses the given two points. The center point of this
sphere will lie midway between pointA and pointB, and the radius will be half the distance between pointA and
pointB. Both input points are assumed to be finite. */
Sphere(const Vector3 &pointA, const Vector3 &pointB);
/// Constructs a sphere that passes through the given three points.
/** @note The resulting sphere may not be the minimal enclosing sphere for the three points! */
Sphere(const Vector3 &pointA, const Vector3 &pointB, const Vector3 &pointC);
/// Constructs a sphere that passes through the given four points.
/** @note The resulting sphere may not be the minimal enclosing sphere for the four points! */
Sphere(const Vector3 &pointA, const Vector3 &pointB, const Vector3 &pointC, const Vector3 &pointD);
public:
/// Generates a random point on the surface of this sphere
/** The points are distributed uniformly.
This function uses the rejection method to generate a uniform distribution of points on the surface.
@@ -58,8 +86,15 @@ namespace J3ML::Geometry
Vector3 ExtremePoint(const Vector3 &direction, float &projectionDistance) const;
Vector3 Centroid() const { return Position; }
Vector3 CenterPos() const { return Centroid(); }
/// Translates this Sphere in world space.
/** @param offset The amount of displacement to apply to this Sphere, in world space coordinates.
@see Transform(). */
void Translate(const Vector3& offset);
void Transform(const Matrix3x3& transform);
void Transform(const Matrix4x4& transform);
@@ -79,8 +114,10 @@ namespace J3ML::Geometry
[[nodiscard]] bool Contains(const Vector3& point, float epsilon) const;
[[nodiscard]] bool Contains(const LineSegment& lineseg) const;
TriangleMesh GenerateUVSphere() const {}
TriangleMesh GenerateIcososphere() const {}
TriangleMesh GenerateUVSphere(int subdivisions = 10.f) const;
TriangleMesh GenerateIcososphere() const;
TriangleMesh GenerateCubesphere() const;
void ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const;
};

View File

@@ -1,19 +1,26 @@
#pragma once
#include "J3ML/LinearAlgebra/Vector3.h"
#include <J3ML/Geometry/Common.h>
#include <J3ML/LinearAlgebra.h>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/LinearAlgebra.hpp>
#include <cfloat>
namespace J3ML::Geometry
{
class Triangle
class Triangle : public Shape
{
public:
Vector3 V0;
Vector3 V1;
Vector3 V2;
public:
public:
static int NumFaces() { return 1; }
static int NumEdges() { return 3; }
static int NumVertices() { return 3; }
public:
float DistanceSq(const Vector3 &point) const;
/// Returns a new triangle, translated with a direction vector
@@ -28,6 +35,10 @@ namespace J3ML::Geometry
AABB BoundingAABB() const;
Vector3 Centroid() const;
Vector3 CenterPoint() const;
/// Tests if the given object is fully contained inside this triangle.
/** @param triangleThickness An epsilon threshold value to use for this test. triangleThicknessSq is the squared version of this parameter.
This specifies the maximum distance the given object can lie from the plane defined by this triangle.
@@ -37,6 +48,9 @@ namespace J3ML::Geometry
bool Contains(const LineSegment& lineSeg, float triangleThickness = 1e-3f) const;
bool Contains(const Triangle& triangle, float triangleThickness = 1e-3f) const;
bool Intersects(const LineSegment& lineSegment, float* d = 0, Vector3* intersectionPoint = 0) const;
/// Project the triangle onto an axis, and returns the min and max value with the axis as a unit
Interval ProjectionInterval(const Vector3& axis) const;
void ProjectToAxis(const Vector3 &axis, float &dMin, float &dMax) const;
@@ -123,6 +137,7 @@ namespace J3ML::Geometry
Vector3 Vertex(int i) const;
LineSegment Edge(int i) const;
Triangle(const Vector3& v0, const Vector3& v1, const Vector3& v2) : V0(v0), V1(v1), V2(v2) {}
};

View File

@@ -1,9 +0,0 @@
#pragma once
namespace J3ML::Geometry
{
class Triangle2D {
public:
};
}

View File

@@ -0,0 +1,242 @@
#pragma once
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/LinearAlgebra/Forward.hpp>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/Geometry/Forward.hpp>
#include <J3ML/Algorithm/RNG.hpp>
namespace J3ML::Geometry
{
using J3ML::LinearAlgebra::Vector2;
using J3ML::Algorithm::RNG;
/// Specifies a triangle through three points in 2D space.
/// This class stores three member vertices A, B, and C to specify the triangle.
/** @note The order in which the vertices are stored in this data structure is important.
The triangles (a,b,c) and (a,c,b) are otherwise equivalent, but their plane normals point to the opposite directions.
@see PlaneCCW(), PlaneCW() */
class Triangle2D : public Shape2D {
public:
Vector2 A; /// The first Triangle endpoint.
Vector2 B; /// The second Triangle endpoint.
Vector2 C; /// The third Triangle endpoint.
public:
/// The default constructor does not initialize any members of this class.
/** This means that the values of the members a, b, and c are undefined after creating a new Triangle2D using this
default constructor. Remember to assign to them before use. */
Triangle2D() {}
/// Constructs a triangle from three given endpoints.
/** The normal of the plane will be constructed to point towards the halfspace where
the vertices a, b, and c wind in counter-clockwise order. */
Triangle2D(const Vector2& a, const Vector2& b, const Vector2& c);
public:
static int NumFaces() { return 1; }
static int NumEdges() { return 3; }
static int NumVertices() { return 3; }
/// Translates this Triangle2D in world space.
/** @param offset The amount of displacement to apply to this Triangle2D, in world space coordinates.
@see Transform(). */
void Translate(const Vector2& offset);
/// Applies a transformation to this Triangle2D, in-place.
/** @see Translate(), classes Matrix3x3, Matrix4x4, Quaternion. */
void Transform(const Matrix3x3& transform);
void Transform(const Matrix4x4& transform);
/// Expresses the given point in barycentric (u,v,w) coordinates.
/** @note There are two different conventions for representing barycentric coordinates. One uses a
(u, v, w) triplet with the equation pt = u*a + v*b + w*c, and the other uses a (u,v) pair
with the equation pt = a + u*(b-a) + v*(c-a). These two are equivalent. Use the mappings
(u,v) -> (1-u-v, u, v) and (u,v,w)->(v,w) to convert between these two representations.
@param point The point of the vector space to express in barycentric coordinates. This point should
lie in the plane formed by this triangle.
@return The factors (u,v,w) that satisfy the weighted sum equation point == u*a + v*b + w*c.
@see BarycentricUV(), BarycentricInsideTriangle(), Point(), http://mathworld.wolfram.com/BarycentricCoordinates.html */
Vector3 BarycentricUVW(const Vector2& point) const;
/// Expresses the given point in barycentric (u, v) coordinates.
/** @note There are two different conventions for representing barycentric coordinates. One uses a
(u, v, w) triplet with the equation pt = u*a + v*b + w*c, and the other uses a (u,v) pair
with the equation pt = a + u*(b-a) + v*(c-a). These two are equivalent. Use the mappings
(u,v) -> (1-u-v, u, v) and (u,v,w)->(v,w) to convert between these two representations.
@param point The point to express in barycentric coordinates. This point should lie in the plane
formed by this triangle.
@return The factors (u, v) that satisfy the weighted sum equation point == a + u*(b-a) + v*(c-a)
@see BarycentricUVW(), BarycentricInsideTriangle(), Point() */
Vector2 BarycentricUV(const Vector2& point) const;
/// Tests if the given barycentric UVW coordinates lie inside a triangle.
/** A barycentric UVW coordinate represents a point inside a triangle if
a) 0 <= u,v,w <= 1 and
b) u+v+w == 1.
@param uvw The barycentric vector containing the barycentric (u,v,w) coordinates.
@return True if the given coordinates lie inside a triangle.
@see BarycentricUV(), BarycentricUVW(), Point(). */
static bool BarycentricInsideTriangle(const Vector3& uvw);
/// Returns the point at the given barycentric coordinates.
/** This function computes the vector space point at the given barycentric coordinates.
@param uvw The barycentric UVW coordiante triplet. The condition u+v+w == 1 should hold for the input coordinate.
If 0 <= u,v,w <= 1, the returned point lies inside this triangle.
@return u*a + v*b + w*c. */
Vector2 Point(const Vector3& uvw) const;
Vector2 Point(float u, float v, float w) const;
/** These functions are an alternate form of Point(u,v,w) for the case when the barycentric coordinates are
represented as a (u,v) pair and not as a (u,v,w) triplet. This function is provided for convenience
and effectively just computes Point(1-u-v, u, v).
@param uv The barycentric UV coordinates. If 0 <= u,v <= 1 and u+v <= 1, then the returned point lies inside
this triangle.
@return a + (b-a)*u + (c-a)*v.
@see BarycentricUV(), BarycentricUVW(), BarycentricInsideTriangle(). */
Vector2 Point(const Vector2& uv) const;
Vector2 Point(float u, float v) const;
/// Computes the center of mass of this triangle.
/** @return The point (a+b+c)/3. */
Vector2 Centroid() const;
/// Identical to Centroid(), we provide both to enable common signature with AABB2D, and OBB2D to allow them in template algorithms.
Vector2 CenterPoint() const { return Centroid();}
/// Computes the surface area of this triangle.
/** @return The surface area of this triangle.
@see Perimeter(), Area2D(), SignedArea(). */
float Area() const;
/// Computes the total edge length of this triangle.
/** @return |a-b| + |b-c| + |c-a|.
@see Area(), Edge(). */
float Perimeter() const;
/// Returns a pointer to the vertices of this triangle. The array contains three elements.
Vector2* VertexArrayPtr() { return &A;}
const Vector2* VertexArrayPtr() const { return &A;}
/// Returns a vertex of this triangle.
/** @param i The vertex of the triangle to get: 0, 1, or 2.
@return Vertex(0) returns the point A, Vertex(1) returns the point B, and Vertex(2) returns the point C.
@note If an index outside [0,2] is passed, an assume() failure occurs, and a NaN vector is returned.
@see Edge(). */
Vector2 Vertex(int i) const;
Vector2 CornerPoint(int i) const { return Vertex(i);} // An alias for Vertex() to be able to mesh Triangle2D into templatized algorithms.
//LineSegment2D Edge(int i) const; // TODO: Implement class LineSegment2D
/// Quickly returns an arbitrary point inside this Triangle2D. Used in GJK intersection test.
inline Vector2 AnyPointFast() const { return A;}
/// Computes an extreme point of this Triangle2D in the given direction.
/** An extreme point is the farthest point of this Triangle2D in the given direction. Given a direction,
this point is not necessarily unique.
@param direction THe direction vector of the direction to find the extreme point. This vector may
be unnormalized, but may not be null.
@return An extreme point of this Triangle2D in the given direction. The returned point is always a
vertex of this Triangle2D.
@see Vertex(). */
Vector2 ExtremePoint(const Vector2& direction) const;
Vector2 ExtremePoint(const Vector2& direction, float& projectionDistance) const;
/// Returns the tight AABB2D that encloses this Triangle2D.
AABB2D BoundingAABB() const;
/// Computes the surface area of the given 2D triangle.
/** This math library does not have a separate class for for 2D triangles. To compute the area of a 2D triangle,
use this Triangle2D class and set z=0 for each coordinate, or use this helper function.
@see Area(), SignedArea().*/
static float Area2D(const Vector2& p1, const Vector2& p2, const Vector2& p3);
/// Relates the barycentric coordinate of the given point to the surface area of triangle abc.
/** This function computes the ratio of the signed area of the triangle (point, b, c) to the signed area of
the triangle (a,b,c). This is the same as computng the barycentric u-coordinate of the given point
on the triangle (a,b,c).
@see Area(), Area2D(), BarycentricUVW(). */
static float SignedArea(const Vector2& point, const Vector2& a, const Vector2& b, const Vector2& c);
/// Returns true if this triangle is finite.
/** A triangle is <b><i>finite</i></b> if its vertices a,b, and c do not contain floating-point NaNs or +/-infs
in them.
@return True if each coordinate of each vertex of this triangle has a finite floating-point value.
@see A, B, C, IsDegenerate(), ::IsFinite(), IsInf(), IsNan(), IsFinite() */
bool IsFinite() const;
/// Returns true if this triangle is degenerate.
/** A triangle is <b><i>degenerate</i></b> if it is not finite, or if the surface area of the triangle is
close to zero.
@param epsilon The threshold to test against. If two vertices of this triangle are closer than this, the
triangle is considered degenerate.
@see a, b, c, IsFinite() */
bool IsDegenerate(float epsilon = 1e-3f) const;
/// Returns true if the triangle defined by the three given points is degenerate.
static bool IsDegenerate(const Vector2& p1, const Vector2& p2, const Vector2& p3, float epsilon = 1e-3f);
/// In some templated algorithms, the input can either be a Triangle2D or a Polygon2D. Provide trivial Polygon2D-specific API
/// for compatibility in those template functions.
bool IsConvex() const { return true;}
bool IsPlanar() const { return true;}
bool IsSimple() const { return true;}
/// Tests if the given object is fully contained inside this triangle.
/** @param triangleThickness An epsilon threshold value to use for this test. triangleThicknessSq is the squared version of this parameter.
This specifies the maximum distance the given object can lie from the plane defined by this triangle.
@see Distance(), Intersects(), ClosestPoint(). */
bool Contains(const Vector2& point, float triangleThicknessSq = 1e-5f) const;
/// Computes the distance between this triangle and the given object.
/** This function finds the nearest pair of points on this and the given object, and computes their distance.
If the two objects intersect, or one object is contained inside the other, the returned distance is zero.
@see Contains(), Intersects(), ClosestPoint(). */
float Distance(const Vector2& point) const;
float DistanceSquared(const Vector2& point) const;
float DistanceSq(const Vector2& point) const { return DistanceSquared(point); }
/// A helper function used in line-triangle tests.
static float IntersectLineTri(const Vector2& linePos, const Vector2& lineDir,
const Vector2& v0, const Vector2& v1, const Vector2& v2,
float& u, float& v);
/// Projects this triangle onto the given axis.
/** This function is used in SAT tests (separate axis theorem) to check the interval this triangle
lies in on an 1D line.
@param axis THe axis to project onto. This vector can be unnormalized.
@param dMin [out] Returns the minimum extent of this triangle on the given axis.
@param dMax [out] Returns the maximum extent of this triangle on the given axis. */
void ProjectToAxis(const Vector2& axis, float& dMin, float& dMax) const;
int UniqueFaceNormals(Vector2* out) const;
int UniqueEdgeNormals(Vector2* out) const;
/// Computes the closest point on this triangle to the given object.
/** If the other object intersects this triangle, the function will return an arbitrary point inside
the region of intersection.
@see Contains(), Distance(), Intersects(), ClosestPointToTriangleEdge(). */
Vector2 ClosestPoint(const Vector2& point) const;
//Vector2 ClosestPoint(const LineSegment2D& lineSegment, Vector2* otherPt = 0) const;
/// Generates a random point inside this Triangle2D.
/** The points are distrubited uniformly.
The implementation of this function is based on Graphics Gems 1, p. 25:
"1.5 Generating random points on triangles. Method 2." The Method 1 presented in the book
uses a Sqrt() instead of the if().
@param rng A pre-seeded random number generator object that is to be used by this function to generate random values.
@see class RNG, RandomPointOnEdge(), RandomVertex(), Point() */
Vector2 RandomPointInside(RNG& rng) const;
/// Choose a corner vertex of this triangle2D at random.
/** This function returns one of the vertices {a, b, c} uniformly at random.
@param rng A pre-seeded random number generator object that is to be used by this function to generate random values.
@see class RNG(), RandomPointInside(), RandomPointOnEdge(), Vertex().*/
Vector2 RandomVertex(RNG& rng) const;
/// Generates a random point on the edge of this Triangle2D.
/** The points are distributed uniformly.
This function requires that this triangle is not degenerate. This it is, an assume() error will be printed,
and the return value will be undefined.
@param rng A pre-seeded random number generator object that is to be used by this function to generate random values.
@see class RNG, RandomPointInside, RandomVertex(), Edge(), class LineSegment2D, IsDegenerate(). */
Vector2 RandomPointOnEdge(RNG& rng) const;
std::string ToString() const;
bool Equals(const Triangle2D& rhs, float epsilon = 1e-3f) const {
return A.Equals(rhs.A, epsilon) && B.Equals(rhs.B, epsilon) && C.Equals(rhs.C, epsilon);
}
};
std::ostream& operator << (std::ostream& o, const Triangle2D& triangle);
}

View File

@@ -1,9 +0,0 @@
#pragma once
namespace J3ML::Geometry
{
class TriangleMesh
{
};
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include <vector>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry/Shape.hpp>
#include <J3ML/Geometry/Forward.hpp>
namespace J3ML::Geometry
{
class TriangleMesh : public Shape
{
public:
/// Default constructor for a triangle mesh.
TriangleMesh(int expectedPolygonCount = 1000);
public:
std::vector<Vector3> Vertices;
std::vector<Vector3> Normals;
std::vector<Vector3> UVs;
std::vector<u64> Indices;
std::vector<float> GenerateVertexList();
//std::vector<Triangle> GenerateTriangleList();
public:
private:
//std::vector<Triangle> cachedTriangleList;
};
}

View File

@@ -1,253 +0,0 @@
#pragma once
//
// Created by josh on 12/25/2023.
//
#include <cstdint>
#include <cmath>
#include <string>
#include <cassert>
// TODO: Move to separate math lib, find duplicates!
template <typename T>
void Swap(T &a, T &b)
{
T temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
namespace J3ML::SizedIntegralTypes
{
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using s8 = int8_t;
using s16 = int16_t;
using s32 = int32_t;
using s64 = int64_t;
}
namespace J3ML::SizedFloatTypes
{
using f32 = float;
using f64 = double;
using f128 = long double;
}
using namespace J3ML::SizedIntegralTypes;
using namespace J3ML::SizedFloatTypes;
//On windows there is no shorthand for pi???? - Redacted.
#ifdef _WIN32
#define M_PI 3.14159265358979323846
#endif
namespace J3ML::Math
{
bool EqualAbs(float a, float b, float epsilon = 1e-3f);
float RecipFast(float x);
// Coming soon: Units Namespace
// For Dimensional Analysis
/*
namespace Units
{
struct Unit {};
struct Meters : public Unit { };
struct ImperialInches : public Unit {};
struct Time : public Unit {};
struct Grams : public Unit {};
struct MetersPerSecond : public Unit {};
template <typename TUnit>
struct Quantity
{
public:
float Value;
};
struct Mass : public Quantity<Grams> {};
struct Length : public Quantity<Meters> { };
struct Velocity : public Quantity<MetersPerSecond>{ };
class MetrixPrefix
{
public:
std::string Prefix;
std::string Symbol;
int Power;
float InverseMultiply(float input) const
{
return std::pow(input, -Power);
}
float Multiply(float input) const
{
return std::pow(input, Power);
}
};
namespace Prefixes
{
static constexpr MetrixPrefix Tera {"tera", "T", 12};
static constexpr MetrixPrefix Giga {"giga", "G", 9};
static constexpr MetrixPrefix Mega {"mega", "M", 6};
static constexpr MetrixPrefix Kilo {"kilo", "k", 3};
static constexpr MetrixPrefix Hecto {"hecto", "h", 2};
static constexpr MetrixPrefix Deca {"deca", "da", 1};
static constexpr MetrixPrefix None {"", "", 0};
static constexpr MetrixPrefix Deci {"", "", 0};
static constexpr MetrixPrefix Centi {"", "", 0};
static constexpr MetrixPrefix Milli {"", "", 0};
static constexpr MetrixPrefix Micro {"", "", 0};
static constexpr MetrixPrefix Nano {"", "", 0};
static constexpr MetrixPrefix Pico {"", "", 0};
}
Length operator ""_meters(long double value)
{
return {(float)value};
}
Length operator ""_m(long double value)
{
return {(float)value};
}
constexpr Length operator ""_kilometers(long double value)
{
return Length {(float)value};
}
Length operator ""_km(long double value)
{
return {(float)value};
}
Length operator ""_centimeters(long double value)
{
return {(float)value};
}
Length operator ""_cm(long double value)
{
return {(float)value};
}
Length operator ""_millimeters(long double value)
{
return {(float)value};
}
Length operator ""_mm(long double value)
{
return {(float)value};
}
Velocity operator ""_mps(long double value)
{
return {(float)value};
}
Velocity operator ""_meters_per_second(long double value)
{
return {(float)value};
}
Velocity operator ""_kmps(long double value)
{
return {(float)value};
}
}*/
#pragma region Constants
static const float Pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f;
static const float RecipSqrt2Pi = 0.3989422804014326779399460599343818684758586311649346576659258296706579258993018385012523339073069364f;
static const float GoldenRatio = 1.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911375f;
#pragma endregion
#pragma region Math Functions
float Radians(float degrees);
float Degrees(float radians);
struct NumberRange
{
float LowerBound;
float UpperBound;
};
float NormalizeToRange(float input, float fromLower, float fromUpper, float toLower, float toUpper);
float NormalizeToRange(float input, const NumberRange& from, const NumberRange& to);
// auto rotation_normalized = NormalizeToRange(inp, {0, 360}, {-1, 1});
inline float Lerp(float a, float b, float t);
/// Linearly interpolates from a to b, under the modulus mod.
/// This function takes evaluates a and b in the range [0, mod] and takes the shorter path to reach from a to b.
inline float LerpMod(float a, float b, float mod, float t);
/// Computes the lerp factor a and b have to be Lerp()ed to get x.
inline float InverseLerp(float a, float b, float x);
/// See http://msdn.microsoft.com/en-us/library/bb509665(v=VS.85).aspx
inline float Step(float y, float x);
/// See http://msdn.microsoft.com/en-us/library/bb509658(v=vs.85).aspx
inline float Ramp(float min, float max, float x);
inline float PingPongMod(float x, float mod);
inline float Sqrt(float x);
inline float FastSqrt(float x);
/// Returns 1/Sqrt(x). (The reciprocal of the square root of x)
inline float RSqrt(float x);
inline float FastRSqrt(float x);
#pragma endregion
namespace BitTwiddling
{
/// Parses a string of form "011101010" to a u32
u32 BinaryStringToValue(const char* s);
/// Returns the number of 1's set in the given value.
inline int CountBitsSet(u32 value);
}
namespace Interp
{
inline float SmoothStart(float t);
}
struct Rotation
{
public:
Rotation();
Rotation(float value);
float valueInRadians;
float ValueInRadians() const;
float ValueInDegrees() const;
Rotation operator+(const Rotation& rhs);
};
Rotation operator ""_rad(long double rads);
Rotation operator ""_radians(long double rads);
Rotation operator ""_deg(long double rads);
Rotation operator ""_degrees(long double rads);
}

380
include/J3ML/J3ML.hpp Normal file
View File

@@ -0,0 +1,380 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file J3ML.h
/// @desc Core mathematical and utility functions, concrete types, and math constants.
/// @edit 2024-07-05
#pragma once
#include <cstdint>
#include <cmath>
#include <string>
#include <cassert>
#include <vector>
/// TODO: Implement lookup tables.
#ifdef USE_LOOKUP_TABLES
static float fast_cossin_table[MAX_CIRCLE_ANGLE];
#endif
#include <J3ML/Algorithm/Reinterpret.hpp>
/// Swaps two elements in-place without copying their data.
template <typename T>
void Swap(T &a, T &b)
{
T temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
/// Clean symbolic names for integers of specific size.
namespace J3ML::SizedIntegralTypes
{
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using s8 = int8_t;
using s16 = int16_t;
using s32 = int32_t;
using s64 = int64_t;
}
namespace J3ML::SizedFloatTypes
{
// TODO: Use C++23 <stdfloat>
using f16 = float;
using f32 = float;
using f64 = double;
using f128 = long double;
}
using namespace J3ML::SizedIntegralTypes;
using namespace J3ML::SizedFloatTypes;
namespace J3ML::Math::BitTwiddling
{
/// Parses a string of form "011101010" to a u32
u32 BinaryStringToValue(const char* s);
/// Returns the number of 1's set in the given value.
inline int CountBitsSet(u32 value);
}
// TODO: Implement "Wrappers" for most standard math functions.
// We want to later-on implement lookup tables and SSE as conditional macros.
namespace J3ML::Math::Constants {
/// sqrt(2pi) ^ -1
constexpr float RecipSqrt2Pi = 0.3989422804014326779399460599343818684758586311649346576659258296706579258993018385012523339073069364;
/// pi - https://www.mathsisfun.com/numbers/pi.html
constexpr float Pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
/// pi * 0.5.
constexpr float HalfPi = 1.5707963267948966192313216916397514420985846996875529104874722961539082031431044993140174126710585;
/// e - https://www.mathsisfun.com/numbers/e-eulers-number.html
constexpr float EulersNumber = 2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274;
/// 2pi - The ratio of a circle's circumferecne to its radius, and the number of radians in one turn.
constexpr float Tau = 6.28318530717958647692;
/// sqrt(2)
constexpr float PythagorasConstant = 1.41421356237309504880;
/// sqrt(3)
constexpr float TheodorusConstant = 1.73205080756887729352;
/// Golden Ratio
constexpr float Phi = 1.61803398874989484820;
/// ln 2
constexpr float NaturalLog2 = 0.6931471805599453094172321214581765680755001343602552541206800094933936219696947156058633269964186875;
/// ln 10
constexpr float NaturalLog10 = 2.3025850929940456840179914546843642076011014886287729760333279009675726096773524802359972050895982983;
constexpr float Infinity = INFINITY;
constexpr float NegativeInfinity = -INFINITY;
constexpr float NotANumber = NAN;
}
/// This set of functions may be set to use lookup tables or SIMD operations.
/// If no options are set, they will default to using standard library implementation.
#undef USE_LOOKUP_TABLES /// Pre-computed lookup tables.
#undef USE_SSE /// Streaming SIMD Extensions (x86)
#undef USE_NEON /// ARM Vector Processing
#undef USE_AVX /// Advanced Vector Extensions (x86)
namespace J3ML::Math::Functions {
/// Clamps the given input value to the range [min, max].
/** @see Clamp01(), Min(), Max(). */
template<typename T>
T Clamp(const T &val, const T &floor, const T &ceil)
{
assert(floor <= ceil);
return val <= ceil ? (val >= floor ? val : floor) : ceil;
}
/// Clamps the given input value to the range [0, 1].
/** @see Clamp(), Min(), Max(). */
template<typename T>
T Clamp01(const T &val) { return Clamp(val, T(0), T(1)); }
/// Computes the smaller of the two values.
/** @see Clamp(), Clamp01(), Max() */
template <typename T>
T Min(const T& a, const T& b) {
return a <= b ? a : b;
}
/// Computes the larger of two values.
/** @see Clamp(), Clamp01(), Max() */
template <typename T>
T Max(const T& a, const T& b) {
return a >= b ? a : b;
}
/// Computes the smallest in an arbitrary list of values.
/** @see Clamp(), Clamp01(), Max() */
template <typename T>
T Min(const std::initializer_list<T>& list) {
T minimum = list[0];
for (T entry : list) {
if (entry <= minimum)
minimum = entry;
}
return minimum;
}
/// Computes the largest in an arbitrary list of values.
/** @see Clamp(), Clamp01(), Max() */
template <typename T>
T Max(const std::initializer_list<T>& list) {
T maximum = list[0];
for (T entry : list) {
if (entry >= maximum)
maximum = entry;
}
return maximum;
}
/** @return True if a > b. */
template <typename T>
bool GreaterThan(const T& a, const T& b) {
return a > b;
}
/** @return True if a < b. */
template <typename T>
bool LessThan(const T& a, const T& b) {
return a < b;
}
/** @return The absolute value of a. */
template <typename T>
T Abs(const T& a) {
return a >= 0 ? a : -a;
}
template<> inline float Abs(const float& a) {
#ifdef USE_SSE
#else
return a >= 0 ? a : -a;
#endif
}
/// @return True if a and b are equal, using operator ==().
template <typename T>
bool EqualExact(const T& a, const T& b) {
return a == b;
}
/** Compares the two values for equality up to a small epsilon. */
bool Equal(float a, float b, float epsilon = 1e-3f);
/** Compares the two values for equality up to a small epsilon. */
bool Equal(double a, double b, float epsilon = 1e-3f);
/** Compares the two values for equality, allowing the given amount of absolute error. */
bool EqualAbs(float a, float b, float epsilon = 1e-3f);
/// Computes the relative error of the two variables.
float RelativeError(float a, float b);
template <typename T> bool IsFinite(T) { return true;}
template<> inline bool IsFinite(float f) { return (ReinterpretAs<u32>(f) << 1) < 0xFF000000u; }
template<> inline bool IsFinite(double d) { return (ReinterpretAs<u64>(d) << 1) < 0xFFE0000000000000ULL; }
template<> inline bool IsFinite(u32 i) { return (i << 1) < 0xFF000000u; }
template<> inline bool IsFinite(u64 i) { return (i << 1) < 0xFFE0000000000000ULL;}
inline bool IsNotANumber(float f) { return (ReinterpretAs<u32>(f) << 1) > 0xFF000000u; }
inline bool IsNotANumber(double d) { return (ReinterpretAs<u64>(d) << 1) > 0xFFE0000000000000ULL; }
inline bool IsInfinite(float f) { return (ReinterpretAs<u32>(f) << 1) == 0xFF000000u; }
inline bool IsInfinite(double d) { return (ReinterpretAs<u64>(d) << 1) == 0xFFE0000000000000ULL; }
float Radians(float deg); /// Converts the given amount of degrees into radians.
float Degrees(float rad); /// Converts the given amount of radians into degrees.
float Sin(float x); /// Computes the sine of x, in radians.
float Cos(float x); /// Computes the cosine of x, in radians.
float Tan(float x); /// Computes the tangent of x, in radians.
/// Simultaneously computes both sine and cosine of x, in radians.
/// This yields a small performance increase over computing them separately.
/// @see Sin(), Cos().
void SinCos(float x, float& outSin, float& outCos);
float Asin(float x); /// Computes the inverse sine of x, in radians.
float Acos(float x); /// Computes the inverse cosine of x, in radians.
float Atan(float x); /// Computes the inverse tangent of x, in radians.
float Atan2(float y, float x); /// Computes the signed (principal value) inverse tangent of y/x, in radians.
float Sinh(float x); /// Computes the hyperbolic sine of x, in radians.
float Cosh(float x); /// Computes the hyperbolic cosine of x, in radians.
float Tanh(float x); /// Computes the hyperbolic tangent of x, in radians.
bool IsPow2(u32 number); /// Returns true if the given number is a power of 2.
bool IsPow2(u64 number); /// Returns true if the given number is a power of 2.
float PowInt(float base, int exponent); /// Raises the given base to an integral exponent.
float Pow(float base, float exponent); /// Raises the given base to a float exponent.
float Exp(float exp); /// Returns e (the constant 2.71828...) raised to the given power.
float Log(float base, float value); /// Computes a logarithm of the given value in the specified base.
float Log2(float value); /// Computes a logarithm in base-2.
float Ln(float value); /// Computes a logarithm in the natural base (using e as the base).
float Log10(float value); /// Computes a logarithm in base-10;
float Ceil(float f); /// Returns f rounded up to the next integer, as float.
int CeilInt(float f); /// Returns f rounded up to the next integer, as integer.
float Floor(float f); /// Returns f rounded down to the previous integer, as float.
int FloorInt(float f); /// Returns f rounded down to the previous integer, as integer.
float Round(float f); /// Returns f rounded to the nearest integer, as float.
int RoundInt(float f); /// Returns f rounded to the nearest integer, as int.
float Round(float f, float decimalPlaces); /// Returns f rounded to the given decimal places.
float Sign(float f); /// Returns -1 or 1 depending on the sign of f.
///
float SignOrZero(float f, float epsilon = 1e-8f);
/// Formats larger numbers into shortened 'Truncated' string representations.
/// 2241 -> 2.2k, 55421 -> 55.4k, 1000000 -> 1.0M
std::string Truncate(float input);
float RecipFast(float x);
struct NumberRange
{
float LowerBound;
float UpperBound;
};
float NormalizeToRange(float input, float fromLower, float fromUpper, float toLower, float toUpper);
float NormalizeToRange(float input, const NumberRange& from, const NumberRange& to);
// auto rotation_normalized = NormalizeToRange(inp, {0, 360}, {-1, 1});
/// Linearly interpolates between a and b.
/** @param t A value between [0,1].
@param a The first endpoint to lerp between.
@param b The second endpoint to lerp between.
@return This function computes a + t*(b-a). That is, if t==0, this function returns a. If t==1, this function returns b.
Otherwise, the returned value linearly moves from a to b as t ranges from 0 to 1.
@see LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(), Frac(). */
float Lerp(float a, float b, float t);
/// Linearly interpolates from a to b, under the modulus mod.
/** This function takes evaluates a and b in the range [0, mod] and takes the shorter path to reach from a to b.
@see Lerp(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(), Frac(). */
float LerpMod(float a, float b, float mod, float t);
/// Computes the lerp factor a and b have to be Lerp()ed to get x.
/// /** @see Lerp(), LerpMod(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(), Frac(). */
float InverseLerp(float a, float b, float x);
/// See http://msdn.microsoft.com/en-us/library/bb509665(v=VS.85).aspx
float Step(float y, float x);
/// See http://msdn.microsoft.com/en-us/library/bb509658(v=vs.85).aspx
float Ramp(float min, float max, float x);
/// Limits x to the range [0, mod], but instead of wrapping around from mod to 0, the result will move back
/// from mod to 0 as x goes from mod to 2*mod.
/** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), Mod(), ModPos(), Frac(). */
float PingPongMod(float x, float mod);
/// Computes a floating-point modulus.
/// This function returns a value in the range ]-mod, mod[.
/** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), ModPos(), Frac(). */
float Mod(float x, float mod);
/// Computes a floating-point modulus using an integer as the modulus.
float Mod(float x, int mod);
/// Computes a floating-point modulus, but restricts the output to the range [0, mod[.
/** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), Frac(). */
float ModPos(float x, float mod);
/// Computes a floating-point modulus, but restricts the output to the range [0, mod[.
float ModPos(float x, int mod);
/// Returns the fractional part of x.
/** @see Lerp(), LerpMod(), InvLerp(), Step(), SmoothStep(), PingPongMod(), Mod(), ModPos(). */
float Frac(float x);
float Sqrt(float x); /// Returns the square root of x.
float FastSqrt(float x); /// Computes a fast approximation of the square root of x.
float RSqrt(float x); /// Returns 1/Sqrt(x). (The reciprocal of the square root of x)
float FastRSqrt(float x); /// SSE implementation of reciprocal square root.
float Recip(float x); /// Returns 1/x, the reciprocal of x.
float RecipFast(float x); /// Returns 1/x, the reciprocal of x, using a fast approximation (SSE rcp instruction).
namespace Interp
{
inline float SmoothStart(float t);
}
}
namespace J3ML::Math {
using namespace Math::Constants;
using namespace Math::Functions;
struct Rotation
{
public:
Rotation();
Rotation(float value);
float valueInRadians;
float ValueInRadians() const;
float ValueInDegrees() const;
Rotation operator+(const Rotation& rhs);
};
Rotation operator ""_rad(long double rads);
Rotation operator ""_radians(long double rads);
Rotation operator ""_deg(long double rads);
Rotation operator ""_degrees(long double rads);
}

View File

@@ -15,16 +15,16 @@
// Library Code //
#include "J3ML/LinearAlgebra/Vector2.h"
#include "J3ML/LinearAlgebra/Vector3.h"
#include "J3ML/LinearAlgebra/Vector4.h"
#include "J3ML/LinearAlgebra/Quaternion.h"
#include "J3ML/LinearAlgebra/AxisAngle.h"
#include "J3ML/LinearAlgebra/EulerAngle.h"
#include "J3ML/LinearAlgebra/Matrix2x2.h"
#include "J3ML/LinearAlgebra/Matrix3x3.h"
#include "J3ML/LinearAlgebra/Matrix4x4.h"
#include "J3ML/LinearAlgebra/Transform2D.h"
#include "J3ML/LinearAlgebra/CoordinateFrame.h"
#include "J3ML/LinearAlgebra/Vector2.hpp"
#include "J3ML/LinearAlgebra/Vector3.hpp"
#include "J3ML/LinearAlgebra/Vector4.hpp"
#include "J3ML/LinearAlgebra/Quaternion.hpp"
#include "J3ML/LinearAlgebra/AxisAngle.hpp"
#include "J3ML/LinearAlgebra/EulerAngle.hpp"
#include "J3ML/LinearAlgebra/Matrix2x2.hpp"
#include "J3ML/LinearAlgebra/Matrix3x3.hpp"
#include "J3ML/LinearAlgebra/Matrix4x4.hpp"
#include "J3ML/LinearAlgebra/Transform2D.hpp"
#include "J3ML/LinearAlgebra/CoordinateFrame.hpp"
using namespace J3ML::LinearAlgebra;

View File

@@ -1,6 +1,5 @@
#pragma once
#include <J3ML/J3ML.h>
#include <J3ML/J3ML.hpp>
namespace J3ML::LinearAlgebra {

View File

@@ -1,29 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/EulerAngle.h>
#include <J3ML/LinearAlgebra/Quaternion.h>
#include <J3ML/LinearAlgebra/AxisAngle.h>
#include <J3ML/LinearAlgebra/Vector3.h>
namespace J3ML::LinearAlgebra
{
/// Transitional datatype, not useful for internal representation of rotation
/// But has uses for conversion and manipulation.
class AxisAngle {
public:
Vector3 axis;
float angle;
public:
AxisAngle();
explicit AxisAngle(const Quaternion& q);
explicit AxisAngle(const EulerAngle& e);
AxisAngle(const Vector3 &axis, float angle);
EulerAngle ToEulerAngleXYZ() const;
Quaternion ToQuaternion() const;
static AxisAngle FromEulerAngleXYZ(const EulerAngle&);
};
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
namespace J3ML::LinearAlgebra {
class AxisAngle;
}
/// Transitional datatype, not useful for internal representation of rotation
/// But has uses for conversion and manipulation.
class J3ML::LinearAlgebra::AxisAngle {
public:
Vector3 axis;
// Radians.
float angle;
public:
AxisAngle();
explicit AxisAngle(const Quaternion& q);
explicit AxisAngle(const EulerAngleXYZ& e);
AxisAngle(const Vector3& axis, float angle);
};

View File

@@ -1,7 +1,7 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.h>
#include <J3ML/LinearAlgebra/Vector3.hpp>
namespace J3ML::LinearAlgebra
{

View File

@@ -0,0 +1,20 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.hpp>
namespace J3ML::LinearAlgebra {
class DirectionVectorRH;
}
/// Direction vector of a given Matrix3x3 RotationMatrix in a Right-handed coordinate space.
class J3ML::LinearAlgebra::DirectionVectorRH : public Vector3 {
private:
// This is purposefully not exposed because these types aren't usually convertable.
explicit DirectionVectorRH(const Vector3& rhs);
public:
static DirectionVectorRH Forward(const Matrix3x3& rhs);
static DirectionVectorRH Backward(const Matrix3x3& rhs);
static DirectionVectorRH Left(const Matrix3x3& rhs);
static DirectionVectorRH Right(const Matrix3x3& rhs);
static DirectionVectorRH Up(const Matrix3x3& rhs);
static DirectionVectorRH Down(const Matrix3x3& rhs);
};

View File

@@ -1,50 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.h>
#include <J3ML/LinearAlgebra/Quaternion.h>
#include <J3ML/LinearAlgebra/AxisAngle.h>
namespace J3ML::LinearAlgebra {
class AxisAngle;
// Essential Reading:
// http://www.essentialmath.com/GDC2012/GDC2012_JMV_Rotations.pdf
class EulerAngle {
public:
EulerAngle();
EulerAngle(float pitch, float yaw, float roll);
EulerAngle(const Vector3& vec) : pitch(vec.x), yaw(vec.y), roll(vec.z) {}
AxisAngle ToAxisAngle() const;
explicit EulerAngle(const Quaternion& rhs);
explicit EulerAngle(const AxisAngle& rhs);
/// TODO: Implement separate upper and lower bounds
/// Preserves internal value of euler angles, normalizes and clamps the output.
/// This does not solve gimbal lock!!!
float GetPitch(float pitch_limit) const;
float GetYaw(float yaw_limit) const;
float GetRoll(float roll_limit) const;
bool operator==(const EulerAngle& a) const;
void clamp();
// TODO: Euler Angles do not represent a vector, length doesn't apply, nor is this information meaningful for this data type.
// If you need a meaningful representation of length in 3d space, use a vector!!
[[nodiscard]] float length() const {
return 0;
}
// TODO: Implement
Vector3 unitVector() const;
EulerAngle movementAngle() const;
public:
float pitch;
float yaw;
float roll;
};
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/AxisAngle.hpp>
namespace J3ML::LinearAlgebra {
class EulerAngleXYZ;
}
class J3ML::LinearAlgebra::EulerAngleXYZ {
public:
public:
float roll = 0; // X
float pitch = 0; // Y
float yaw = 0; // Z
public:
EulerAngleXYZ(float roll, float pitch, float yaw);
public:
explicit EulerAngleXYZ(const Quaternion& rhs);
explicit EulerAngleXYZ(const AxisAngle& rhs);
explicit EulerAngleXYZ(const Matrix3x3& rhs);
};

View File

@@ -7,7 +7,7 @@ namespace J3ML::LinearAlgebra
class Vector3; // A type representing a position in a 3-dimensional coordinate space.
class Vector4; // A type representing a position in a 4-dimensional coordinate space.
class Angle2D; // Uses x,y components to represent a 2D rotation.
class EulerAngle; // Uses pitch,yaw,roll components to represent a 3D orientation.
class EulerAngleXYZ; // Uses pitch,yaw,roll components to represent a 3D orientation.
class AxisAngle; //
class CoordinateFrame; //
class Matrix2x2;
@@ -15,14 +15,20 @@ namespace J3ML::LinearAlgebra
class Matrix4x4;
class Transform2D;
class Transform3D;
class DirectionVectorRH; // A type representing a direction in 3D space.
class Quaternion;
using Position = Vector3;
template <int N> class PBVolume;
}
// Methods required by LinearAlgebra types
namespace J3ML::LinearAlgebra
{
}
}
using namespace J3ML::LinearAlgebra;

View File

@@ -2,8 +2,8 @@
/// Template Parameterized (Generic) Matrix Functions.
#include <J3ML/LinearAlgebra/Quaternion.h>
#include <J3ML/J3ML.h>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include "J3ML/J3ML.hpp"
namespace J3ML::LinearAlgebra {
@@ -148,8 +148,8 @@ namespace J3ML::LinearAlgebra {
template<typename Matrix>
void Set3x3PartRotateX(Matrix &m, float angle) {
float sinz, cosz;
sinz = std::sin(angle);
cosz = std::cos(angle);
sinz = Math::Sin(angle);
cosz = Math::Cos(angle);
m[0][0] = 1.f;
m[0][1] = 0.f;
@@ -170,8 +170,8 @@ namespace J3ML::LinearAlgebra {
template<typename Matrix>
void Set3x3PartRotateY(Matrix &m, float angle) {
float sinz, cosz;
sinz = std::sin(angle);
cosz = std::cos(angle);
sinz = Math::Sin(angle);
cosz = Math::Cos(angle);
m[0][0] = cosz;
m[0][1] = 0.f;

View File

@@ -1,9 +1,21 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Matrix.hpp
/// @desc Templated implementation of arbitrary-sized N-by-M matrices.
/// @edit 2024-08-01
/// @note On backlog, low-priority.
#pragma once
#include <cstddef>
#include <cstdlib>
#include <algorithm>
#include "Vector.h"
#include "Vector.hpp"
namespace J3ML::LinearAlgebra
{

View File

@@ -1,15 +1,26 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Matrix2x2.hpp
/// @desc A two-by-two Matrix object.
/// @edit 2024-08-01
/// @note On backlog, low-priority.
#pragma once
#include <J3ML/LinearAlgebra/Common.h>
#include <J3ML/LinearAlgebra.hpp>
namespace J3ML::LinearAlgebra {
class Matrix2x2 {
public:
enum { Rows = 3 };
enum { Cols = 3 };
enum { Rows = 2 };
enum { Cols = 2 };
static const Matrix2x2 Zero;
static const Matrix2x2 Identity;
static const Matrix2x2 NaN;
@@ -40,6 +51,9 @@ namespace J3ML::LinearAlgebra {
Vector2 operator * (const Vector2& rhs) const;
Matrix2x2 operator * (const Matrix2x2 &rhs) const;
inline float* ptr() { return &elems[0][0];}
[[nodiscard]] inline const float* ptr() const {return &elems[0][0];}
protected:
float elems[2][2];
};

View File

@@ -2,10 +2,11 @@
#include <J3ML/LinearAlgebra/Vector2.h>
#include <J3ML/LinearAlgebra/Vector3.h>
#include <J3ML/LinearAlgebra/Quaternion.h>
#include <J3ML/Algorithm/RNG.h>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <J3ML/LinearAlgebra/Quaternion.hpp>
#include <J3ML/LinearAlgebra/EulerAngle.hpp>
#include <J3ML/Algorithm/RNG.hpp>
using namespace J3ML::Algorithm;
@@ -61,6 +62,12 @@ namespace J3ML::LinearAlgebra {
Matrix3x3(const Vector3& col0, const Vector3& col1, const Vector3& col2);
/// Constructs this matrix3x3 from the given quaternion.
explicit Matrix3x3(const Quaternion& orientation);
/// Constructs this matrix3x3 from the given euler angle.
explicit Matrix3x3(const EulerAngleXYZ& orientation);
explicit Matrix3x3(const AxisAngle& orientation);
/// Constructs this Matrix3x3 from a pointer to an array of floats.
explicit Matrix3x3(const float *data);
/// Creates a new Matrix3x3 that rotates about one of the principal axes by the given angle.
@@ -149,6 +156,7 @@ namespace J3ML::LinearAlgebra {
/// Sets this matrix to perform a rotation about the given axis and angle.
void SetRotatePart(const Vector3& a, float angle);
void SetRotatePart(const AxisAngle& axisAngle);
/// Sets this matrix to perform the rotation expressed by the given quaternion.
void SetRotatePart(const Quaternion& quat);
@@ -232,18 +240,13 @@ namespace J3ML::LinearAlgebra {
/// Accesses this structure as a float array.
/// @return A pointer to the upper-left element. The data is contiguous in memory.
/// ptr[0] gives the element [0][0], ptr[1] is [0][1]. ptr[2] is [0][2]
inline float *ptr() { return &elems[0][0];}
[[nodiscard]] inline const float *ptr() const {return &elems[0][0];}
inline float* ptr() { return &elems[0][0];}
[[nodiscard]] inline const float* ptr() const {return &elems[0][0];}
/// Convers this rotation matrix to a quaternion.
/// This function assumes that the matrix is orthonormal (no shear or scaling) and does not perform any mirroring (determinant > 0)
[[nodiscard]] Quaternion ToQuat() const;
/// Attempts to convert this matrix to a quaternion. Returns false if the conversion cannot succeed (this matrix was not a rotation
/// matrix, and there is scaling ,shearing, or mirroring in this matrix)
bool TryConvertToQuat(Quaternion& q) const;
/// Returns the main diagonal.
/// The main diagonal consists of the elements at m[0][0], m[1][1], m[2][2]
[[nodiscard]] Vector3 Diagonal() const;
@@ -451,8 +454,6 @@ namespace J3ML::LinearAlgebra {
This occurs if this matrix has a negative determinant.*/
[[nodiscard]] bool HasNegativeScale() const;
/// Returns true if the column and row vectors of this matrix form an orthonormal set.
/// @note In math terms, there does not exist such a thing as an 'orthonormal matrix'. In math terms, a matrix
/// is orthogonal if the column and row vectors are orthogonal *unit* vectors.

View File

@@ -1,8 +1,8 @@
#pragma once
#include <J3ML/LinearAlgebra/Common.h>
#include <J3ML/Algorithm/RNG.h>
#include <J3ML/LinearAlgebra/Forward.hpp>
#include <J3ML/Algorithm/RNG.hpp>
#include <algorithm>
@@ -10,8 +10,6 @@ using namespace J3ML::Algorithm;
namespace J3ML::LinearAlgebra {
/// @brief A 4-by-4 matrix for affine transformations and perspective projections of 3D geometry.
/// This matrix can represent the most generic form of transformations for 3D objects,
/// including perspective projections, which a 4-by-3 cannot store, and translations, which a 3-by-3 cannot represent.
@@ -74,6 +72,7 @@ namespace J3ML::LinearAlgebra {
/// Constructs this Matrix4x4 from the given quaternion.
explicit Matrix4x4(const Quaternion& orientation);
/// Constructs this float4x4 from the given quaternion and translation.
/// Logically, the translation occurs after the rotation has been performed.
Matrix4x4(const Quaternion& orientation, const Vector3 &translation);
@@ -232,6 +231,10 @@ namespace J3ML::LinearAlgebra {
static Matrix4x4 FromTRS(const Vector3& translate, const Matrix3x3& rotate, const Vector3& scale);
static Matrix4x4 FromTRS(const Vector3& translate, const Matrix4x4& rotate, const Vector3& scale);
void SetCol3(int col, const Vector3 &xyz);
bool HasNegativeScale() const;
public:
/// Returns the translation part.
/** The translation part is stored in the fourth column of this matrix.
@@ -239,9 +242,9 @@ namespace J3ML::LinearAlgebra {
after applying rotation and scale. If this matrix represents a local->world space transformation for an object,
then this gives the world space position of the object.
@note This function assumes that this matrix does not contain projection (the fourth row of this matrix is [0 0 0 1]). */
Vector3 GetTranslatePart() const;
[[nodiscard]] Vector3 GetTranslatePart() const;
/// Returns the top-left 3x3 part of this matrix. This stores the rotation part of this matrix (if this matrix represents a rotation).
Matrix3x3 GetRotatePart() const;
[[nodiscard]] Matrix3x3 GetRotatePart() const;
/// Sets the translation part of this matrix.
/** This function sets the translation part of this matrix. These are the first three elements of the fourth column. All other entries are left untouched. */
@@ -287,36 +290,36 @@ namespace J3ML::LinearAlgebra {
/// Returns the given row.
/** @param The zero-based index [0, 3] of the row to get */
Vector4 GetRow(int row) const;
Vector4 Row(int row) const;
[[nodiscard]] Vector4 GetRow(int row) const;
[[nodiscard]] Vector4 Row(int row) const;
Vector4& Row(int row);
/// Returns only the first-three elements of the given row.
Vector3 GetRow3(int index) const;
Vector3 Row3(int i) const;
[[nodiscard]] Vector3 GetRow3(int index) const;
[[nodiscard]] Vector3 Row3(int i) const;
/// This method also allows assignment to the retrieved row.
Vector3& Row3(int row);
/// Returns the given column.
/** @param col The zero-based index [0, 3] of the column to get. */
Vector4 GetColumn(int index) const;
Vector4 Column(int index) const;
Vector4 Col(int i) const;
[[nodiscard]] Vector4 GetColumn(int index) const;
[[nodiscard]] Vector4 Column(int index) const;
[[nodiscard]] Vector4 Col(int i) const;
/// Returns only the first three elements of the given column.
Vector3 GetColumn3(int index) const;
Vector3 Column3(int index) const;
[[nodiscard]] Vector3 GetColumn3(int index) const;
[[nodiscard]] Vector3 Column3(int index) const;
Vector3 Col3(int i) const;
[[nodiscard]] Vector3 Col3(int i) const;
/// Returns the scaling performed by this matrix. This function assumes taht the last row is [0 0 0 1].
/// GetScale().x specifies the amount of scaling applied to the local x direction vector when it is transformed by this matrix.
/// i.e. GetScale()[i] equals Col[i].Length();
Vector3 GetScale() const;
[[nodiscard]] Vector3 GetScale() const;
float &At(int row, int col);
float At(int x, int y) const;
[[nodiscard]] float At(int x, int y) const;
void SwapColumns(int col1, int col2);
@@ -337,35 +340,48 @@ namespace J3ML::LinearAlgebra {
/** @return Returns true if the entries of this float4x4 are all finite, and do not contain NaN or infs. */
bool IsFinite() const;
/// Tests if this matrix is the identity matrix.
bool IsIdentity(float epsilon = 1e-3f) const;
/// Tests if this matrix has an inverse.
/** @return Returns true if this matrix can be inverted, up to the given epsilon. */
bool IsInvertible(float epsilon = 1e-3f) const;
Vector4 Diagonal() const;
Vector3 Diagonal3() const;
[[nodiscard]] Vector4 Diagonal() const;
[[nodiscard]] Vector3 Diagonal3() const;
/// Returns the local +X axis in world space.
/// This is the same as transforming the vector (1,0,0) by this matrix.
Vector3 WorldX() const;
[[nodiscard]] Vector3 WorldX() const;
/// Returns the local +Y axis in world space.
/// This is the same as transforming the vector (0,1,0) by this matrix.
Vector3 WorldY() const;
[[nodiscard]] Vector3 WorldY() const;
/// Returns the local +Z axis in world space.
/// This is the same as transforming the vector (0,0,1) by this matrix.
Vector3 WorldZ() const;
[[nodiscard]] Vector3 WorldZ() const;
/// Accesses this structure as a float array.
/// @return A pointer to the upper-left element. The data is contiguous in memory.
/// ptr[0] gives the element [0][0], ptr[1] is [0][1], ptr[2] is [0][2].
/// ptr[4] == [1][0], ptr[5] == [1][1], ..., and finally, ptr[15] == [3][3].
float *ptr() { return &elems[0][0]; }
const float *ptr() const { return &elems[0][0]; }
[[nodiscard]] const float *ptr() const { return &elems[0][0]; }
float Determinant3x3() const;
/// Computes the determinant of the upper-left 3x3 submatrix of this matrix.
[[nodiscard]] float Determinant3x3() const;
/// Computes the determinant of this matrix.
// If the determinant is nonzero, this matrix is invertible.
float Determinant() const;
[[nodiscard]] float Determinant() const;
#define SKIPNUM(val, skip) (val >= skip ? (val+1) : val)
/// Computes the determinant of this matrix.
/** If the determinant is nonzero, this matrix is invertible.
If the determinant is negative, this matrix performs reflection about some axis.
From http://msdn.microsoft.com/en-us/library/bb204853(VS.85).aspx :
"If the determinant is positive, the basis is said to be "positively" oriented (or right-handed).
If the determinant is negative, the basis is said to be "negatively" oriented (or left-handed)." */
float Determinant4() const;
#define SKIPNUM(val, skip) (val >= skip ? (val+1) : val)
float Minor(int i, int j) const;
@@ -548,6 +564,7 @@ namespace J3ML::LinearAlgebra {
/// Returns the sum of the diagonal elements of this matrix.
float Trace() const;
[[nodiscard]] Quaternion ToQuat() const;
/// Returns true if this Matrix4x4 is equal to the given Matrix4x4, up to given per-element epsilon.
bool Equals(const Matrix4x4& other, float epsilon = 1e-3f) const;

View File

@@ -1,261 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/Common.h>
#include <J3ML/Algorithm/RNG.h>
#include <cmath>
namespace J3ML::LinearAlgebra
{
class Quaternion {
public:
/// The identity quaternion performs no rotation when applied to a vector.
static const Quaternion Identity;
/// A compile-time constant Quaternion with the value (NAN, NAN, NAN, NAN).
/// For this constant, each element has the value of quiet NAN, or Not-A-Number.
/// @note Never compare a Quaternion to this value! Due to how IEEE floats work, "nan == nan" returns false!
/// That is, nothing is equal to NaN, not even NaN itself!
static const Quaternion NaN;
public:
/// The default constructor does not initialize any member values.
Quaternion();
/// Copy constructor
Quaternion(const Quaternion &rhs) = default;
/// Constructs a quaternion from the given data buffer.
/// @param data An array of four floats to use for the quaternion, in the order 'x,y,z,w.' (== 'i,j,k,w')
explicit Quaternion(const float *data);
explicit Quaternion(const Matrix3x3 &rotationMtrx);
explicit Quaternion(const Matrix4x4 &rotationMtrx);
/// @param x The factor of i.
/// @param y The factor of j.
/// @param z The factor of k.
/// @param w The scalar factor (or 'w').
/// @note The input data is not normalized after construction, this has to be done manually.
Quaternion(float X, float Y, float Z, float W);
/// Constructs this quaternion by specifying a rotation axis and the amount of rotation to be performed about that axis
/// @param rotationAxis The normalized rotation axis to rotate about. If using Vector4 version of the constructor, the w component of this vector must be 0.
/// @param rotationAngleRadians The angle to rotate by, in radians. For example, Pi/4.f equals to 45 degrees, Pi/2.f is 90 degrees, etc.
/// @see DegToRad()
Quaternion(const Vector3 &rotationAxis, float rotationAngleRadians);
Quaternion(const Vector4 &rotationAxis, float rotationAngleRadians);
explicit Quaternion(const Vector4& vector4);
explicit Quaternion(const EulerAngle& angle);
explicit Quaternion(const AxisAngle& angle);
/// Creates a LookAt quaternion.
/** A LookAt quaternion is a quaternion that orients an object to face towards a specified target direction.
@param localForward Specifies the forward direction in the local space of the object. This is the direction
the model is facing at in its own local/object space, often +X(1,0,0), +Y(0,1,0), or +Z(0,0,1).
The vector to pass in here depends on the conventions you or your modeling software is using, and it is best
to pick one convention for all your objects, and be consistent.
This input parameter must be a normalized vector.
@param targetDirection Specifies the desired world space direction the object should look at. This function
will compute a quaternion which will rotate the localForward vector to orient towards this targetDirection
vector. This input parameter must be a normalized vector.
@param localUp Specifies the up direction in the local space of the object. This is the up direction the model
was authored in, often +Y (0,1,0) or +Z (0,0,1). The vector to pass in here depends on the conventions you
or your modeling software is using, and it is best to pick one convention for all your objects, and be
consistent. This input parameter must be a normalized vector. This vector must be perpendicular to the
vector localForward, i.e. localForward.Dot(localUp) == 0.
@param worldUp Specifies the global up direction of the scene in world space. Simply rotating one vector to
coincide with another (localForward->targetDirection) would cause the up direction of the resulting
orientation to drift (e.g. the model could be looking at its target its head slanted sideways). To keep
the up direction straight, this function orients the localUp direction of the model to point towards
the specified worldUp direction (as closely as possible). The worldUp and targetDirection vectors cannot be
collinear, but they do not need to be perpendicular either.
@return A quaternion that maps the given local space forward direction vector to point towards the given target
direction, and the given local up direction towards the given target world up direction. For the returned
quaternion Q it holds that M * localForward = targetDirection, and M * localUp lies in the plane spanned
by the vectors targetDirection and worldUp.
@see RotateFromTo() */
static Quaternion LookAt(const Vector3& localForward, const Vector3& targetDirection, const Vector3& localUp, const Vector3& worldUp);
/// Creates a new Quaternion that rotates about the given axis by the given angle.
static Quaternion RotateAxisAngle(const AxisAngle& axisAngle);
/// Creates a new quaternion that rotates about the positive X axis by the given rotation.
static Quaternion RotateX(float angleRadians);
/// Creates a new quaternion that rotates about the positive Y axis by the given rotation.
static Quaternion RotateY(float angleRadians);
/// Creates a new quaternion that rotates about the positive Z axis by the given rotation.
static Quaternion RotateZ(float angleRadians);
/// Creates a new quaternion that rotates sourceDirection vector (in world space) to coincide with the
/// targetDirection vector (in world space).
/// Rotation is performed about the origin.
/// The vectors sourceDirection and targetDirection are assumed to be normalized.
/// @note There are multiple such rotations - this function returns the rotation that has the shortest angle
/// (when decomposed to axis-angle notation).
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection);
static Quaternion RotateFromTo(const Vector4& sourceDirection, const Vector4& targetDirection);
/// Creates a new quaternion that
/// 1. rotates sourceDirection vector to coincide with the targetDirection vector, and then
/// 2. rotates sourceDirection2 (which was transformed by 1.) to targetDirection2, but keeping the constraint that
/// sourceDirection must look at targetDirection
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection, const Vector3& sourceDirection2, const Vector3& targetDirection2);
/// Returns a uniformly random unitary quaternion.
static Quaternion RandomRotation(RNG &rng);
public:
void SetFromAxisAngle(const Vector3 &vector3, float between);
void SetFromAxisAngle(const Vector4 &vector4, float between);
void SetFrom(const AxisAngle& angle);
/// Inverses this quaternion in-place.
/// @note For optimization purposes, this function assumes that the quaternion is unitary, in which
/// case the inverse of the quaternion is simply just the same as its conjugate.
/// This function does not detect whether the operation succeeded or failed.
void Inverse();
/// Returns an inverted copy of this quaternion.
[[nodiscard]] Quaternion Inverted() const;
/// Computes the conjugate of this quaternion in-place.
void Conjugate();
/// Returns a conjugated copy of this quaternion.
[[nodiscard]] Quaternion Conjugated() const;
/// Inverses this quaternion in-place.
/// Call this function when the quaternion is not known beforehand to be normalized.
/// This function computes the inverse proper, and normalizes the result.
/// @note Because of the normalization, it does not necessarily hold that q * q.InverseAndNormalize() == id.
/// @return Returns the old length of this quaternion (not the old length of the inverse quaternion).
float InverseAndNormalize();
/// Returns the local +X axis in the post-transformed coordinate space. This is the same as transforming the vector (1,0,0) by this quaternion.
[[nodiscard]] Vector3 WorldX() const;
/// Returns the local +Y axis in the post-transformed coordinate space. This is the same as transforming the vector (0,1,0) by this quaternion.
[[nodiscard]] Vector3 WorldY() const;
/// Returns the local +Z axis in the post-transformed coordinate space. This is the same as transforming the vector (0,0,1) by this quaternion.
[[nodiscard]] Vector3 WorldZ() const;
/// Returns the axis of rotation for this quaternion.
[[nodiscard]] Vector3 Axis() const;
/// Returns the angle of rotation for this quaternion, in radians.
[[nodiscard]] float Angle() const;
[[nodiscard]] float LengthSquared() const;
[[nodiscard]] float Length() const;
[[nodiscard]] EulerAngle ToEulerAngle() const;
[[nodiscard]] Matrix3x3 ToMatrix3x3() const;
[[nodiscard]] Matrix4x4 ToMatrix4x4() const;
[[nodiscard]] Matrix4x4 ToMatrix4x4(const Vector3 &translation) const;
[[nodiscard]] Vector3 Transform(const Vector3& vec) const;
[[nodiscard]] Vector3 Transform(float X, float Y, float Z) const;
// Note: We only transform the x,y,z components of 4D vectors, w is left untouched
[[nodiscard]] Vector4 Transform(const Vector4& vec) const;
[[nodiscard]] Vector4 Transform(float X, float Y, float Z, float W) const;
[[nodiscard]] Quaternion Lerp(const Quaternion& b, float t) const;
static Quaternion Lerp(const Quaternion &source, const Quaternion& target, float t);
[[nodiscard]] Quaternion Slerp(const Quaternion& q2, float t) const;
static Quaternion Slerp(const Quaternion &source, const Quaternion& target, float t);
/// Returns the 'from' vector rotated towards the 'to' vector by the given normalized time parameter.
/** This function slerps the given 'form' vector toward the 'to' vector.
@param from A normalized direction vector specifying the direction of rotation at t=0
@param to A normalized direction vector specifying the direction of rotation at t=1
@param t The interpolation time parameter, in the range [0, 1]. Input values outside this range are
silently clamped to the [0, 1] interval.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
static Vector3 SlerpVector(const Vector3& from, const Vector3& to, float t);
/// Returns the 'from' vector rotated towards the 'to' vector by the given absolute angle, in radians.
/** This function slerps the given 'from' vector towards the 'to' vector.
@param from A normalized direction vector specifying the direction of rotation at angleRadians=0.
@param to A normalized direction vector specifying the target direction to rotate towards.
@param angleRadians The maximum angle to rotate the 'from' vector by, in the range [0, pi]. If the
angle between 'from' and 'to' is smaller than this angle, then the vector 'to' is returned.
Input values outside this range are silently clamped to the [0, pi] interval.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
static Vector3 SlerpVectorAbs(const Vector3 &from, const Vector3& to, float angleRadians);
/// Normalizes this quaternion in-place.
/// Returns the old length of this quaternion, or 0 if normalization failed.
float Normalize();
/// Returns a normalized copy of this quaternion.
[[nodiscard]] Quaternion Normalized() const;
/// Returns true if the length of this quaternion is one.
[[nodiscard]] bool IsNormalized(float epsilon = 1e-5f) const;
[[nodiscard]] bool IsInvertible(float epsilon = 1e-3f) const;
/// Returns true if the entries of this quaternion are all finite.
[[nodiscard]] bool IsFinite() const;
/// Returns true if this quaternion equals rhs, up to the given epsilon.
[[nodiscard]] bool Equals(const Quaternion& rhs, float epsilon = 1e-3f) const;
/// Compares whether this Quaternion and the given Quaternion are identical bit-by-bit in the underlying representation.
/// @note Prefer using this over e.g. memcmp, since there can be SSE-related padding in the structures.
bool BitEquals(const Quaternion& rhs) const;
/// @return A pointer to the first element (x). The data is contiguous in memory.
/// ptr[0] gives x, ptr[1] gives y, ptr[2] gives z, ptr[3] gives w.
inline float *ptr() { return &x; }
[[nodiscard]] inline const float *ptr() const { return &x; }
// Multiplies two quaternions together.
// The product q1 * q2 returns a quaternion that concatenates the two orientation rotations.
// The rotation q2 is applied first before q1.
Quaternion operator * (const Quaternion& rhs) const;
// Unsafe
Quaternion operator * (float scalar) const;
// Unsafe
Quaternion operator / (float scalar) const;
// Transforms the given vector by this Quaternion.
Vector3 operator * (const Vector3& rhs) const;
Vector4 operator * (const Vector4& rhs) const;
// Divides a quaternion by another. Divison "a / b" results in a quaternion that rotates the orientation b to coincide with orientation of
Quaternion operator / (const Quaternion& rhs) const;
Quaternion operator + (const Quaternion& rhs) const;
Quaternion operator + () const;
Quaternion operator - () const;
/// Computes the dot product of this and the given quaternion.
/// Dot product is commutative.
[[nodiscard]] float Dot(const Quaternion &quaternion) const;
/// Returns the angle between this and the target orientation (the shortest route) in radians.
[[nodiscard]] float AngleBetween(const Quaternion& target) const;
/// Returns the axis of rotation to get from this orientation to target orientation (the shortest route).
[[nodiscard]] Vector3 AxisFromTo(const Quaternion& target) const;
[[nodiscard]] AxisAngle ToAxisAngle() const;
void SetFromAxisAngle(const AxisAngle& axisAngle);
/// Sets this quaternion to represent the same rotation as the given matrix.
void Set(const Matrix3x3& matrix);
void Set(const Matrix4x4& matrix);
void Set(float x, float y, float z, float w);
void Set(const Quaternion& q);
void Set(const Vector4& v);
public:
float x;
float y;
float z;
float w;
};
}

View File

@@ -0,0 +1,240 @@
#pragma once
#include <J3ML/LinearAlgebra/Forward.hpp>
#include <J3ML/Algorithm/RNG.hpp>
#include <cmath>
namespace J3ML::LinearAlgebra {
class Quaternion;
}
class J3ML::LinearAlgebra::Quaternion {
public:
float x;
float y;
float z;
float w;
public:
/// The identity quaternion performs no rotation when applied to a vector.
static const Quaternion Identity;
/// A compile-time constant Quaternion with the value (NAN, NAN, NAN, NAN).
/// For this constant, each element has the value of quiet NAN, or Not-A-Number.
/// @note Never compare a Quaternion to this value! Due to how IEEE floats work, "nan == nan" returns false!
/// That is, nothing is equal to NaN, not even NaN itself!
static const Quaternion NaN;
public:
/// The default constructor does not initialize any member values.
Quaternion() = default;
/// Copy constructor
Quaternion(const Quaternion &rhs);
/// Quaternion from Matrix3x3
explicit Quaternion(const Matrix3x3& ro_mat);
/// Quaternion from Matrix4x4 RotatePart.
explicit Quaternion(const Matrix4x4& ro_mat);
/// Quaternion from EulerAngleXYZ.
explicit Quaternion(const EulerAngleXYZ& rhs);
/// Quaternion from AxisAngle.
explicit Quaternion(const AxisAngle& angle);
/// Quaternion from Vector4 (no conversion).
explicit Quaternion(const Vector4& vector4);
/// @param x The factor of i.
/// @param y The factor of j.
/// @param z The factor of k.
/// @param w The scalar factor (or 'w').
/// @note The input data is not normalized after construction, this has to be done manually.
Quaternion(float X, float Y, float Z, float W);
/// Creates a LookAt quaternion.
/** A LookAt quaternion is a quaternion that orients an object to face towards a specified target direction.
@param localForward Specifies the forward direction in the local space of the object. This is the direction
the model is facing at in its own local/object space, often +X(1,0,0), +Y(0,1,0), or +Z(0,0,1).
The vector to pass in here depends on the conventions you or your modeling software is using, and it is best
to pick one convention for all your objects, and be consistent.
This input parameter must be a normalized vector.
@param targetDirection Specifies the desired world space direction the object should look at. This function
will compute a quaternion which will rotate the localForward vector to orient towards this targetDirection
vector. This input parameter must be a normalized vector.
@param localUp Specifies the up direction in the local space of the object. This is the up direction the model
was authored in, often +Y (0,1,0) or +Z (0,0,1). The vector to pass in here depends on the conventions you
or your modeling software is using, and it is best to pick one convention for all your objects, and be
consistent. This input parameter must be a normalized vector. This vector must be perpendicular to the
vector localForward, i.e. localForward.Dot(localUp) == 0.
@param worldUp Specifies the global up direction of the scene in world space. Simply rotating one vector to
coincide with another (localForward->targetDirection) would cause the up direction of the resulting
orientation to drift (e.g. the model could be looking at its target its head slanted sideways). To keep
the up direction straight, this function orients the localUp direction of the model to point towards
the specified worldUp direction (as closely as possible). The worldUp and targetDirection vectors cannot be
collinear, but they do not need to be perpendicular either.
@return A quaternion that maps the given local space forward direction vector to point towards the given target
direction, and the given local up direction towards the given target world up direction. For the returned
quaternion Q it holds that M * localForward = targetDirection, and M * localUp lies in the plane spanned
by the vectors targetDirection and worldUp.
@see RotateFromTo() */
static Quaternion LookAt(const Vector3& localForward, const Vector3& targetDirection, const Vector3& localUp, const Vector3& worldUp);
/// Creates a new quaternion that rotates about the positive X axis by the given rotation.
static Quaternion RotateX(float rad);
/// Creates a new quaternion that rotates about the positive Y axis by the given rotation.
static Quaternion RotateY(float rad);
/// Creates a new quaternion that rotates about the positive Z axis by the given rotation.
static Quaternion RotateZ(float rad);
/// Creates a new quaternion that rotates sourceDirection vector (in world space) to coincide with the
/// targetDirection vector (in world space).
/// Rotation is performed about the origin.
/// The vectors sourceDirection and targetDirection are assumed to be normalized.
/// @note There are multiple such rotations - this function returns the rotation that has the shortest angle
/// (when decomposed to axis-angle notation).
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection);
static Quaternion RotateFromTo(const Vector4& sourceDirection, const Vector4& targetDirection);
/// Creates a new quaternion that
/// 1. rotates sourceDirection vector to coincide with the targetDirection vector, and then
/// 2. rotates sourceDirection2 (which was transformed by 1.) to targetDirection2, but keeping the constraint that
/// sourceDirection must look at targetDirection
static Quaternion RotateFromTo(const Vector3& sourceDirection, const Vector3& targetDirection, const Vector3& sourceDirection2, const Vector3& targetDirection2);
/// Returns a uniformly random unitary quaternion.
static Quaternion RandomRotation(RNG &rng);
public:
/// Inverses this quaternion in-place.
/// @note For optimization purposes, this function assumes that the quaternion is unitary, in which
/// case the inverse of the quaternion is simply just the same as its conjugate.
/// This function does not detect whether the operation succeeded or failed.
void Inverse();
/// Returns an inverted copy of this quaternion.
[[nodiscard]] Quaternion Inverted() const;
/// Computes the conjugate of this quaternion in-place.
void Conjugate();
/// Returns a conjugated copy of this quaternion.
[[nodiscard]] Quaternion Conjugated() const;
/// Inverses this quaternion in-place.
/// Call this function when the quaternion is not known beforehand to be normalized.
/// This function computes the inverse proper, and normalizes the result.
/// @note Because of the normalization, it does not necessarily hold that q * q.InverseAndNormalize() == id.
/// @return Returns the old length of this quaternion (not the old length of the inverse quaternion).
float InverseAndNormalize();
/// Returns the local +X axis in the post-transformed coordinate space. This is the same as transforming the vector (1,0,0) by this quaternion.
[[nodiscard]] Vector3 WorldX() const;
/// Returns the local +Y axis in the post-transformed coordinate space. This is the same as transforming the vector (0,1,0) by this quaternion.
[[nodiscard]] Vector3 WorldY() const;
/// Returns the local +Z axis in the post-transformed coordinate space. This is the same as transforming the vector (0,0,1) by this quaternion.
[[nodiscard]] Vector3 WorldZ() const;
/// Returns the axis of rotation for this quaternion.
[[nodiscard]] Vector3 Axis() const;
/// Returns the angle of rotation for this quaternion, in radians.
[[nodiscard]] float Angle() const;
[[nodiscard]] float LengthSquared() const;
[[nodiscard]] float Length() const;
[[nodiscard]] Matrix3x3 ToMatrix3x3() const;
[[nodiscard]] Matrix4x4 ToMatrix4x4() const;
[[nodiscard]] Matrix4x4 ToMatrix4x4(const Vector3 &translation) const;
[[nodiscard]] Vector3 Transform(const Vector3& vec) const;
[[nodiscard]] Vector3 Transform(float X, float Y, float Z) const;
// Note: We only transform the x,y,z components of 4D vectors, w is left untouched
[[nodiscard]] Vector4 Transform(const Vector4& vec) const;
[[nodiscard]] Vector4 Transform(float X, float Y, float Z, float W) const;
[[nodiscard]] Quaternion Lerp(const Quaternion& b, float t) const;
static Quaternion Lerp(const Quaternion &source, const Quaternion& target, float t);
[[nodiscard]] Quaternion Slerp(const Quaternion& q2, float t) const;
static Quaternion Slerp(const Quaternion &source, const Quaternion& target, float t);
/// Returns the 'from' vector rotated towards the 'to' vector by the given normalized time parameter.
/** This function slerps the given 'form' vector toward the 'to' vector.
@param from A normalized direction vector specifying the direction of rotation at t=0
@param to A normalized direction vector specifying the direction of rotation at t=1
@param t The interpolation time parameter, in the range [0, 1]. Input values outside this range are
silently clamped to the [0, 1] interval.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
static Vector3 SlerpVector(const Vector3& from, const Vector3& to, float t);
/// Returns the 'from' vector rotated towards the 'to' vector by the given absolute angle, in radians.
/** This function slerps the given 'from' vector towards the 'to' vector.
@param from A normalized direction vector specifying the direction of rotation at angleRadians=0.
@param to A normalized direction vector specifying the target direction to rotate towards.
@param angleRadians The maximum angle to rotate the 'from' vector by, in the range [0, pi]. If the
angle between 'from' and 'to' is smaller than this angle, then the vector 'to' is returned.
Input values outside this range are silently clamped to the [0, pi] interval.
@return A spherical linear interpolation of the vector 'from' towards the vector 'to'. */
static Vector3 SlerpVectorAbs(const Vector3 &from, const Vector3& to, float angleRadians);
/// Normalizes this quaternion in-place.
/// @returns false if failure, true if success.
[[nodiscard]] bool Normalize();
/// Returns a normalized copy of this quaternion.
[[nodiscard]] Quaternion Normalized() const;
/// Returns true if the length of this quaternion is one.
[[nodiscard]] bool IsNormalized(float epsilon = 1e-5f) const;
[[nodiscard]] bool IsInvertible(float epsilon = 1e-3f) const;
/// Returns true if the entries of this quaternion are all finite.
[[nodiscard]] bool IsFinite() const;
/// Returns true if this quaternion equals rhs, up to the given epsilon.
[[nodiscard]] bool Equals(const Quaternion& rhs, float epsilon = 1e-3f) const;
/// Compares whether this Quaternion and the given Quaternion are identical bit-by-bit in the underlying representation.
/// @note Prefer using this over e.g. memcmp, since there can be SSE-related padding in the structures.
bool BitEquals(const Quaternion& rhs) const;
/// @return A pointer to the first element (x). The data is contiguous in memory.
/// ptr[0] gives x, ptr[1] gives y, ptr[2] gives z, ptr[3] gives w.
inline float *ptr() { return &x; }
[[nodiscard]] inline const float *ptr() const { return &x; }
// Multiplies two quaternions together.
// The product q1 * q2 returns a quaternion that concatenates the two orientation rotations.
// The rotation q2 is applied first before q1.
Quaternion operator * (const Quaternion& rhs) const;
// Unsafe
Quaternion operator * (float scalar) const;
// Unsafe
Quaternion operator / (float scalar) const;
// Transforms the given vector by this Quaternion.
Vector3 operator * (const Vector3& rhs) const;
Vector4 operator * (const Vector4& rhs) const;
// Divides a quaternion by another. Divison "a / b" results in a quaternion that rotates the orientation b to coincide with orientation of
Quaternion operator / (const Quaternion& rhs) const;
Quaternion operator + (const Quaternion& rhs) const;
Quaternion operator + () const;
Quaternion operator - () const;
/// Computes the dot product of this and the given quaternion.
/// Dot product is commutative.
[[nodiscard]] float Dot(const Quaternion &quaternion) const;
/// Returns the angle between this and the target orientation (the shortest route) in radians.
[[nodiscard]] float AngleBetween(const Quaternion& target) const;
/// Returns the axis of rotation to get from this orientation to target orientation (the shortest route).
[[nodiscard]] Vector3 AxisFromTo(const Quaternion& target) const;
/// Sets this quaternion to represent the same rotation as the given matrix.
void Set(const Matrix3x3& matrix);
void Set(const Matrix4x4& matrix);
void Set(float x, float y, float z, float w);
void Set(const Quaternion& q);
void Set(const Vector4& v);
};

View File

@@ -0,0 +1,22 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file SparseMatrix.hpp
/// @desc Templated Matrix type in which most elements are expected to be zero, and thus are "compressed"
/// @edit 2024-07-06
#include <cstdlib>
namespace J3ML::LinearAlgebra
{
template <uint Rows, uint Cols>
class SparseMatrix
{
};
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include <J3ML/LinearAlgebra/Matrix3x3.h>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
namespace J3ML::LinearAlgebra {
class Transform2D {

View File

@@ -2,6 +2,8 @@
#include <cstddef>
#include <J3ML/Algorithm/RNG.hpp>
#include <J3ML/LinearAlgebra/Forward.hpp>
namespace J3ML::LinearAlgebra {
using namespace J3ML;
@@ -106,6 +108,16 @@ namespace J3ML::LinearAlgebra {
[[nodiscard]] bool IsZero(float epsilonSq = 1e-6f) const;
[[nodiscard]] bool IsPerpendicular(const Vector2& other, float epsilonSq=1e-5f) const;
/// Tests if two vectors are equal, up to the given epsilon.
/** @see IsPerpendicular(). */
bool Equals(const Vector2& rhs, float epsilon = 1e-3f) const {
return Math::EqualAbs(x, rhs.x, epsilon) &&
Math::EqualAbs(y, rhs.y, epsilon);
}
bool Equals(float x_, float y_, float epsilon = 1e-3f) const {
return Math::EqualAbs(x, x_, epsilon) &&
Math::EqualAbs(y, y_, epsilon);
}
bool operator == (const Vector2& rhs) const;
bool operator != (const Vector2& rhs) const;
@@ -228,8 +240,8 @@ namespace J3ML::LinearAlgebra {
/// Returns a normalized copy of this vector.
/** @note If the vector is zero and cannot be normalized, the vector (1, 0) is returned, and an error message is printed.
If you do not want to generate an error message on failure, but want to handle the failure yourself, use the
Normalize() function instead.
@see Normalize() */
Normalized() function instead.
@see Normalized() */
[[nodiscard]] Vector2 Normalized() const;
/// Linearly interpolates between two points.
@@ -369,7 +381,19 @@ namespace J3ML::LinearAlgebra {
@see Orthogonalize(), AreOrthogonal(), AreOrthonormal() */
static void Orthonormalize(Vector2& a, Vector2& b);
/// Returns a random Vector2 with each entry randomized between the range [minElem, maxElem].
static Vector2 RandomBox(Algorithm::RNG& rng, float minElem, float maxElem);
[[nodiscard]] std::string ToString() const;
};
Vector2 operator*(float lhs, const Vector2 &rhs);
}
std::ostream& operator << (std::ostream& out, const Vector2& rhs);
Vector2 Mul2D(const Matrix3x3& transform, const Vector2& v);
Vector2 MulPos2D(const Matrix4x4& transform, const Vector2& v);
Vector2 MulDir2D(const Matrix4x4& transform, const Vector2& v);
}

View File

@@ -1,11 +1,11 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector2.h>
#include <J3ML/LinearAlgebra/Vector3.h>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
#include <cstddef>
#include <cstdlib>
#include <J3ML/LinearAlgebra/Angle2D.h>
#include <J3ML/Algorithm/RNG.h>
#include <J3ML/LinearAlgebra/Angle2D.hpp>
#include <J3ML/Algorithm/RNG.hpp>
using namespace J3ML::Algorithm;
@@ -68,15 +68,15 @@ public:
/** @note Due to static data initialization order being undefined in C++, do NOT use this
member to initialize other static data in other compilation units! */
static const Vector3 NegativeInfinity;
/// Specifies a compile-time constant Vector3 with value (1,1,1).
/// Specifies a compile-time constant Vector3 with value (1,0,0).
/** @note Due to static data initialization order being undefined in C++, do NOT use this
member to initialize other static data in other compilation units! */
static const Vector3 UnitX;
/// Specifies a compile-time constant Vector3 with value (1,1,1).
/// Specifies a compile-time constant Vector3 with value (0,1,0).
/** @note Due to static data initialization order being undefined in C++, do NOT use this
member to initialize other static data in other compilation units! */
static const Vector3 UnitY;
/// Specifies a compile-time constant Vector3 with value (1,1,1).
/// Specifies a compile-time constant Vector3 with value (0,0,1).
/** @note Due to static data initialization order being undefined in C++, do NOT use this
member to initialize other static data in other compilation units! */
static const Vector3 UnitZ;
@@ -144,7 +144,7 @@ public:
@note If you have a non-const instance of this class, you can use this notation to set the elements of
this vector as well, e.g. vec.At(1) = 10.f; would set the y-component of this vector.
@see ptr(), operator [](). */
float At(int index) const;
[[nodiscard]] float At(int index) const;
float &At(int index);
/// Makes the given vectors linearly independent and normalized in length.
@@ -161,36 +161,54 @@ public:
static bool AreOrthonormal(const Vector3& a, const Vector3& b, float epsilon = 1e-3f);
static bool AreOrthonormal(const Vector3& a, const Vector3& b, const Vector3& c, float epsilon = 1e-3f);
Vector3 Abs() const;
[[nodiscard]] Vector3 Abs() const;
static Vector3 Abs(const Vector3& rhs);
/// Returns the DirectionVector for a given angle.
static Vector3 Direction(const Vector3 &rhs) ;
/// Scales this vector so that its new length is as given.
/** Calling this function is effectively the same as normalizing the vector first and then multiplying by newLength.
In the case of failure, this vector is set to (newLength, 0, 0), so calling this function will never result in an
unnormalized vector.
@note This function operates in-place.
@return The old length of this vector. If this function returns 0, the scaling failed, and this vector is arbitrarily
reset to (newLength, 0, 0). In case of failure, no error message is generated. You are expected to handle the failure
yourself.
@see ScaledToLength(). */
float ScaleToLength(float newLength);
/// Returns a scaled copy of this vector which has its new length as given.
/** This function assumes the length of this vector is not zero. In the case of failure, an error message is printed,
and the vector (newLength, 0, 0) is returned.
@see ScaleToLength(). */
[[nodiscard]] Vector3 ScaledToLength(float newLength) const;
[[nodiscard]] Vector3 ProjectToNorm(const Vector3& direction) const;
Vector3 ProjectToNorm(const Vector3& direction) const;
float GetX() const;
float GetY() const;
float GetZ() const;
[[nodiscard]] float GetX() const;
[[nodiscard]] float GetY() const;
[[nodiscard]] float GetZ() const;
void SetX(float newX);
void SetY(float newY);
void SetZ(float newZ);
bool IsWithinMarginOfError(const Vector3& rhs, float margin=0.001f) const;
bool IsNormalized(float epsilonSq = 1e-5f) const;
bool IsZero(float epsilonSq = 1e-6f) const;
bool IsPerpendicular(const Vector3& other, float epsilonSq=1e-5f) const;
[[nodiscard]] Vector2 XY() const { return {x, y};}
[[nodiscard]] bool IsWithinMarginOfError(const Vector3& rhs, float margin=0.001f) const;
[[nodiscard]] bool IsNormalized(float epsilonSq = 1e-5f) const;
[[nodiscard]] bool IsZero(float epsilonSq = 1e-6f) const;
[[nodiscard]] bool IsPerpendicular(const Vector3& other, float epsilonSq=1e-5f) const;
bool operator == (const Vector3& rhs) const;
bool operator != (const Vector3& rhs) const;
bool IsFinite() const;
float MinElement() const;
[[nodiscard]] bool IsFinite() const;
[[nodiscard]] float MinElement() const;
static float MinElement(const Vector3& of);
/// Normalizes this Vector3.
@@ -207,62 +225,77 @@ public:
/// Computes a new normalized direction vector that is perpendicular to this vector and the specified hint vector.
/** If this vector points toward the hint vector, the vector hint2 is returned instead.
@see AnotherPerpendicular(), Cross(). */
Vector3 Perpendicular(const Vector3 &hint = Vector3(0,1,0), const Vector3 &hint2 = Vector3(0,0,1)) const;
[[nodiscard]] Vector3 Perpendicular(const Vector3 &hint = Vector3(0,1,0), const Vector3 &hint2 = Vector3(0,0,1)) const;
Vector3 Min(const Vector3& min) const;
/// Returns another vector that is perpendicular to this vector and the vector returned by Perpendicular().
/** The set (this, Perpendicular(), AnotherPerpendicular()) forms a right-handed normalized 3D basis.
@see Perpendicular(), Cross(). */
Vector3 AnotherPerpendicular(const Vector3& hint = Vector3(0,1,0), const Vector3& hint2 = Vector3(0,0,1)) const;
/// Completes this vector to generate a perpendicular basis.
/** This function computes two new vectors b and c which are both orthogonal to this vector and to each other.
That is, the set { this, b, c} is an orthogonal set. The vectors b and c that are outputted are also normalized.
@param outB [out] Receives vector b.
@param outC [out] Receives vector c.
@note When calling this function, this vector should not be zero! */
void PerpendicularBasis(Vector3& outB, Vector3& outC) const;
[[nodiscard]] Vector3 Min(const Vector3& min) const;
static Vector3 Min(const Vector3& a, const Vector3& b, const Vector3& c);
static Vector3 Min(const Vector3& lhs, const Vector3& rhs);
Vector3 Max(const Vector3& max) const;
[[nodiscard]] Vector3 Max(const Vector3& max) const;
static Vector3 Max(const Vector3& a, const Vector3& b, const Vector3& c);
static Vector3 Max(const Vector3& lhs, const Vector3& rhs);
Vector3 Clamp(const Vector3& min, const Vector3& max) const;
[[nodiscard]] Vector3 Clamp(const Vector3& min, const Vector3& max) const;
static Vector3 Clamp(const Vector3& min, const Vector3& input, const Vector3& max);
/// Returns the magnitude between the two vectors.
float Distance(const Vector3& to) const;
[[nodiscard]] float Distance(const Vector3& to) const;
static float Distance(const Vector3& from, const Vector3& to);
//float Distance(const Ray&) const;
//float Distance(const LineSegment&) const;
//float Distance(const Plane&) const;
//float DIstance(const Triangle&) const;
float DistanceSquared(const Vector3& to) const;
[[nodiscard]] float DistanceSquared(const Vector3& to) const;
// Function Alias for DistanceSquared
float DistanceSq(const Vector3& to) const { return DistanceSquared(to); }
[[nodiscard]] float DistanceSq(const Vector3& to) const { return DistanceSquared(to); }
static float DistanceSquared(const Vector3& from, const Vector3& to);
float Length() const;
[[nodiscard]] float Length() const;
static float Length(const Vector3& of);
float LengthSquared() const;
[[nodiscard]] float LengthSquared() const;
static float LengthSquared(const Vector3& of);
/// Returns the length of the vector, which is sqrt(x^2 + y^2 + z^2)
float Magnitude() const;
[[nodiscard]] float Magnitude() const;
static float Magnitude(const Vector3& of);
/// Returns a float value equal to the magnitudes of the two vectors multiplied together and then multiplied by the cosine of the angle between them.
/// For normalized vectors, dot returns 1 if they point in exactly the same direction,
/// -1 if they point in completely opposite directions, and 0 if the vectors are perpendicular.
float Dot(const Vector3& rhs) const;
[[nodiscard]] float Dot(const Vector3& rhs) const;
static float Dot(const Vector3& lhs, const Vector3& rhs);
/// Projects one vector onto another and returns the result. (IDK)
Vector3 Project(const Vector3& rhs) const;
[[nodiscard]] Vector3 Project(const Vector3& rhs) const;
static Vector3 Project(const Vector3& lhs, const Vector3& rhs);
/// The cross product of two vectors results in a third vector which is perpendicular to the two input vectors.
/// The result's magnitude is equal to the magnitudes of the two inputs multiplied together and then multiplied by the sine of the angle between the inputs.
Vector3 Cross(const Vector3& rhs) const;
[[nodiscard]] Vector3 Cross(const Vector3& rhs) const;
static Vector3 Cross(const Vector3& lhs, const Vector3& rhs);
/// Returns a copy of this vector, resized to have a magnitude of 1, while preserving "direction"
/// @note If the vector is zero and cannot be normalized, the vector (1, 0, 0) is returned, and an error message is printed.
/// If you do not want to generate an error message on failure, but want to handle the failure yourself, use the Normalize() function instead.
/// @see Normalize()
Vector3 Normalized() const;
/// If you do not want to generate an error message on failure, but want to handle the failure yourself, use the Normalized() function instead.
/// @see Normalized()
[[nodiscard]] Vector3 Normalized() const;
static Vector3 Normalized(const Vector3& targ);
/// Normalizes this Vector3.
@@ -281,56 +314,56 @@ public:
/// Interpolates between the points and b by the interpolant t.
/// The parameter is (TODO: SHOULD BE!) clamped to the range[0, 1].
/// This is most commonly used to find a point some fraction of the wy along a line between two endpoints (eg. to move an object gradually between those points).
Vector3 Lerp(const Vector3& goal, float alpha) const;
[[nodiscard]] Vector3 Lerp(const Vector3& goal, float alpha) const;
static Vector3 Lerp(const Vector3& lhs, const Vector3& rhs, float alpha);
/// Returns the angle between this vector and the specified vector, in radians.
/** @note This function takes into account that this vector or the other vector can be unnormalized, and normalizes the computations.
If you are computing the angle between two normalized vectors, it is better to use AngleBetweenNorm().
@see AngleBetweenNorm(). */
Angle2D AngleBetween(const Vector3& rhs) const;
[[nodiscard]] Angle2D AngleBetween(const Vector3& rhs) const;
static Angle2D AngleBetween(const Vector3& lhs, const Vector3& rhs);
/// Adds two vectors
Vector3 operator+(const Vector3& rhs) const;
Vector3 Add(const Vector3& rhs) const;
[[nodiscard]] Vector3 Add(const Vector3& rhs) const;
static Vector3 Add(const Vector3& lhs, const Vector3& rhs);
/// Adds the vector(s, s, s) to this vector
Vector3 Add(float s) const;
[[nodiscard]] Vector3 Add(float s) const;
/// Subtracts two vectors
Vector3 operator-(const Vector3& rhs) const;
Vector3 Sub(const Vector3& rhs) const;
[[nodiscard]] Vector3 Sub(const Vector3& rhs) const;
static Vector3 Sub(const Vector3& lhs, const Vector3& rhs);
/// Multiplies this vector by a scalar value
Vector3 operator*(float rhs) const;
Vector3 Mul(float scalar) const;
[[nodiscard]] Vector3 Mul(float scalar) const;
static Vector3 Mul(const Vector3& lhs, float rhs);
/// Multiplies this vector by a vector, element-wise
/// @note Mathematically, the multiplication of two vectors is not defined in linear space structures,
/// but this function is provided here for syntactical convenience.
Vector3 Mul(const Vector3& rhs) const;
[[nodiscard]] Vector3 Mul(const Vector3& rhs) const;
/// Divides this vector by a scalar
Vector3 operator/(float rhs) const;
Vector3 Div(float scalar) const;
[[nodiscard]] Vector3 Div(float scalar) const;
static Vector3 Div(const Vector3& lhs, float rhs);
/// Divides this vector by a vector, element-wise
/// @note Mathematically, the multiplication of two vectors is not defined in linear space structures,
/// but this function is provided here for syntactical convenience
Vector3 Div(const Vector3& v) const;
[[nodiscard]] Vector3 Div(const Vector3& v) const;
/// Unary + operator
Vector3 operator+() const; // TODO: Implement
/// Unary - operator (Negation)
Vector3 operator-() const;
bool Equals(const Vector3& rhs, float epsilon = 1e-3f) const;
bool Equals(float _x, float _y, float _z, float epsilon = 1e-3f) const;
[[nodiscard]] bool Equals(const Vector3& rhs, float epsilon = 1e-3f) const;
[[nodiscard]] bool Equals(float _x, float _y, float _z, float epsilon = 1e-3f) const;
Vector3 &operator =(const Vector3& rhs);
Vector3& operator+=(const Vector3& rhs);
@@ -340,8 +373,13 @@ public:
void Set(float d, float d1, float d2);
[[nodiscard]] std::string ToString() const;
};
std::ostream& operator << (std::ostream& o, const Vector3& vector);
static Vector3 operator*(float lhs, const Vector3& rhs)
{
return rhs * lhs;

View File

@@ -1,133 +0,0 @@
#pragma once
#include <cstddef>
#include <cmath>
#include <J3ML/LinearAlgebra/Common.h>
namespace J3ML::LinearAlgebra {
class Vector4 {
public:
// Default Constructor
Vector4();
// Constructs a new Vector4 with x,y,z values from a Vector3
Vector4(const Vector3& xyz, float w = 0);
// Constructs a new Vector4 with the value (X, Y, Z, W)
Vector4(float X, float Y, float Z, float W);
Vector4(const Vector4& copy) = default;
Vector4(Vector4&& move) = default;
Vector4& operator=(const Vector4& rhs);
float* ptr()
{
return &x;
}
Vector3 XYZ() const;
float GetX() const { return x; }
float GetY() const { return y; }
float GetZ() const { return z; }
float GetW() const { return w; }
#if MUTABLE
void SetX(float newX) { x = newX;}
void SetY(float newY) { y = newY;}
void SetZ(float newZ) { z = newZ;}
void SetW(float newW) { w = newW;}
#endif
static const Vector4 Zero;
static const Vector4 NaN;
float operator[](std::size_t index) const;
float &operator[](std::size_t index);
bool IsWithinMarginOfError(const Vector4& rhs, float margin=0.0001f) const;
float LengthSqXYZ() const;
bool IsNormalized3(float epsilonSq = 1e-5f) const
{
return std::abs(LengthSqXYZ()-1.f) <= epsilonSq;
}
bool IsNormalized4(float epsilonSq = 1e-5f) const
{
return std::abs(LengthSquared()-1.f) <= epsilonSq;
}
bool IsNormalized(float epsilonSq = 1e-5f) const { return IsNormalized4(epsilonSq); }
bool IsZero(float epsilonSq = 1e-6f) const;
bool IsFinite() const;
bool IsPerpendicular(const Vector4& other, float epsilonSq=1e-5f) const
{
float dot = Dot(other);
return dot*dot <= epsilonSq * LengthSquared() * other.LengthSquared();
}
bool IsPerpendicular3(const Vector4& other, float epsilonSq = 1e-5f) const
{
}
bool operator==(const Vector4& rhs) const;
bool operator!=(const Vector4& rhs) const;
bool Equals(const Vector4& rhs, float epsilon = 1e-3f) const;
bool Equals(float _x, float _y, float _z, float _w, float epsilon = 1e-3f) const;
Vector4 Min(const Vector4& min) const;
Vector4 Max(const Vector4& max) const;
Vector4 Clamp(const Vector4& min, const Vector4& max) const;
float Distance(const Vector4& to) const;
float Length() const;
float LengthSquared() const;
float Magnitude() const;
float Dot(const Vector4& rhs) const;
Vector4 Project(const Vector4& rhs) const;
// While it is feasable to compute a cross-product in four dimensions
// the cross product only has the orthogonality property in 3 and 7 dimensions
// You should consider instead looking at Gram-Schmidt Orthogonalization
// to find orthonormal vectors.
Vector4 Cross3(const Vector3& rhs) const;
Vector4 Cross3(const Vector4& rhs) const;
Vector4 Cross(const Vector4& rhs) const { return Cross3(rhs); }
Vector4 Normalize() const;
Vector4 Lerp(const Vector4& goal, float alpha) const;
float AngleBetween(const Vector4& rhs) const;
// Adds two vectors
Vector4 operator+(const Vector4& rhs) const;
Vector4 Add(const Vector4& rhs) const;
static Vector4 Add(const Vector4& lhs, const Vector4& rhs);
// Subtracts two vectors
Vector4 operator-(const Vector4& rhs) const;
Vector4 Sub(const Vector4& rhs) const;
static Vector4 Sub(const Vector4& lhs, const Vector4& rhs);
// Multiplies this vector by a scalar value
Vector4 operator*(float rhs) const;
Vector4 Mul(float scalar) const;
static Vector4 Mul(const Vector4& lhs, float rhs);
// Divides this vector by a scalar
Vector4 operator/(float rhs) const;
Vector4 Div(float scalar) const;
static Vector4 Div(const Vector4& rhs, float scalar);
Vector4 operator+() const; // Unary + Operator
Vector4 operator-() const; // Unary - Operator (Negation)
public:
#if MUTABLE
float x;
float y;
float z;
float w;
#else
float x = 0;
float y = 0;
float z = 0;
float w = 0;
#endif
};
}

View File

@@ -0,0 +1,272 @@
#pragma once
#include <cstddef>
#include <cmath>
#include <J3ML/LinearAlgebra/Forward.hpp>
namespace J3ML::LinearAlgebra {
/// A 3D vector of form (x,y,z,w) in a 4D homogeneous coordinate space.
/** This class has two sets of member functions. The functions ending in a suffix '3' operate only on the
(x, y, z) part, ignoring the w component (or assuming a value of 0 or 1, where expectable). The functions
without the '3' suffix operate on all four elements of the vector. */
class Vector4 {
public:
enum { Size = 4 };
public:
static const Vector4 Zero;
static const Vector4 NaN;
public:
/// The default constructor does not initialize any members of this class.
/** This means that the values of the members x, y, z, and w are all undefined after creating a new Vector4 using
this default constructor. Remember to assign to them before use.
@see x, y, z, w. */
Vector4();
/// Constructs a new Vector4 with x,y,z values from a Vector3
explicit Vector4(const Vector3& xyz, float w = 0);
/// Constructs a new Vector4 with the value (X, Y, Z, W)
/** @note If you are constructing a float4 from an array of consecutive values, always prefer calling "float4(ptr);" instead of "float4(ptr[0], ptr[1], ptr[2], ptr[3]);"
because there is a considerable SIMD performance benefit in the first form.
@see x, y, z, w. */
Vector4(float X, float Y, float Z, float W);
/// The Vector4 copy constructor.
Vector4(const Vector4& copy) { Set(copy); }
Vector4(Vector4&& move) = default;
Vector4& operator=(const Vector4& rhs);
/// Constructs this Vector4 from a C array, to the value (data[0], data[1], data[2], data[3]).
/** @param data An array containing four elements for x, y, z, and w. This pointer may not be null. */
explicit Vector4(const float* data);
/// Casts this Vector4 to a C array.
/** This function does not allocate new memory or make a copy of this Vector4. This function simply
returns a C pointer view to this data structure. Use ptr()[0] to access the x component of this Vector4,
ptr()[1] to access y, ptr()[2] to access z, and ptr()[3] to access the w component of this Vector4.
@note Since the returned pointer points to this class, do not dereference the pointer after this
Vector has been deleted. You should never store a copy of the returned pointer.
@note This function is provided for compatibility with other APIs which require raw C pointer access
to vectors. Avoid using this function in general, and instead always use the operator [] of this
class to access the elements of this vector by index. */
inline float* ptr();
[[nodiscard]] const float* ptr() const;
/// Accesses an element of this vector using array notation.
/** @param index The element to get. Pass in 0 for x, 1 for y, 2 for z and 3 for w.
@note If you have a non-const instance of this class, you can use this notation to set the elements of
this vector as well, e.g. vec[1] = 10.f; would set the y-component of this vector. */
float operator[](std::size_t index) const;
float &operator[](std::size_t index);
/// Accesses an element of this vector.
/** @param index The element to get. Pass in 0 for x, 1 for y, 2 for z and 3 for w.
@note If you have a non-const instance of this class, you can use this notation to set the elements of
this vector as well, e.g. vec.At(1) = 10.f; would set the y-component of this vector. */
float At(int index) const;
float& At(int index);
/// Returns the (x,y) part of this vector.
[[nodiscard]] Vector2 XY() const;
/// Returns the (x,y,z) part of this vector.
[[nodiscard]] Vector3 XYZ() const;
[[nodiscard]] float GetX() const { return x; }
[[nodiscard]] float GetY() const { return y; }
[[nodiscard]] float GetZ() const { return z; }
[[nodiscard]] float GetW() const { return w; }
void SetX(float newX) { x = newX;}
void SetY(float newY) { y = newY;}
void SetZ(float newZ) { z = newZ;}
void SetW(float newW) { w = newW;}
/// Sets all elements of this vector.
/** @see x, y, z, w, At(). */
void Set(float x, float y, float z, float w);
void Set(const Vector4& rhs);
[[nodiscard]] bool IsWithinMarginOfError(const Vector4& rhs, float margin=0.0001f) const;
/// Computes the squared length of the (x,y,z) part of this vector.
[[nodiscard]] float LengthSqXYZ() const;
/// Tests if the length of the (x, y, z) part of this vector is one, up to the given epsilon.
/** @see NormalizeW(), IsWZeroOrOne(), IsZero3(), IsZero4(), IsNormalized4(). */
[[nodiscard]] bool IsNormalized3(float epsilonSq = 1e-5f) const;
/// Returns true if the length of this vector is 1, up to the given epsilon.
/** This function takes into account all the four components of this vector when calculating the norm.
@see NormalizeW(), IsWZeroOrOne(), IsZero3(), IsZero4(), IsNormalized3(). */
[[nodiscard]] bool IsNormalized4(float epsilonSq = 1e-5f) const;
[[nodiscard]] bool IsNormalized(float epsilonSq = 1e-5f) const;
/// Tests if the (x, y, z) part of this vector is equal to (0,0,0), up to the given epsilon.
/** @see NormalizeW(), IsWZeroOrOne(), IsZero4(), IsNormalized3(), IsNormalized4(). */
[[nodiscard]] bool IsZero3(float epsilonSq = 1e-6f) const;
/// Returns true if this vector is equal to (0,0,0,0), up to the given epsilon.
/** @see NormalizeW(), IsWZeroOrOne(), IsZero3(), IsNormalized3(), IsNormalized4(). */
[[nodiscard]] bool IsZero(float epsilonSq = 1e-6f) const;
[[nodiscard]] bool IsZero4(float epsilonSq = 1e-6f) const;
/// Tests if this vector contains valid finite elements.
[[nodiscard]] bool IsFinite() const;
/// Tests if the (x, y, z) parts of two vectors are perpendicular to each other.
[[nodiscard]] bool IsPerpendicular(const Vector4& other, float epsilonSq=1e-5f) const;
[[nodiscard]] bool IsPerpendicular3(const Vector4& other, float epsilonSq = 1e-5f) const;
/// Divides each element by w to produce a Vector4 of form (x, y, z, 1).
/** This function performs the <b>perspective divide</b> or the <b>homogeneous divide</b> on this vector, which is the
process of dividing each element of this vector by w. If the w component of this vector is zero before division, the
result of this vector will be undefined.
@note This function operates in-place.
@see IsWZeroOrOne(). */
void NormalizeW();
bool operator==(const Vector4& rhs) const;
bool operator!=(const Vector4& rhs) const;
[[nodiscard]] bool Equals(const Vector4& rhs, float epsilon = 1e-3f) const;
[[nodiscard]] bool Equals(float _x, float _y, float _z, float _w, float epsilon = 1e-3f) const;
/// Returns an element-wise minimum of this and the vector (ceil, ceil, ceil, ceil).
/** Each element that is larger than ceil is replaced by ceil. */
[[nodiscard]] Vector4 Min(float ceil) const;
/// Returns an element-wise minimum of this and the given vector.
/** Each element that is larger than ceil is replaced by ceil.
@see Max(), Clamp(). */
[[nodiscard]] Vector4 Min(const Vector4& ceil) const;
/// Returns an element-wise maximum of this and the vector (floor, floor, floor, floor).
/** Each element that is smaller than floor is replaced by floor. */
[[nodiscard]] Vector4 Max(float floor) const;
/// Returns an element-wise maximum of this and the given vector.
/** Each element that is smaller than floor is replaced by floor.
@see Min(), Clamp(). */
[[nodiscard]] Vector4 Max(const Vector4& floor) const;
/// Returns a vector that has floor <= this[i] <= ceil for each element.
[[nodiscard]] Vector4 Clamp(float floor, float ceil) const;
/// Limits each element of this vector between the corresponding elements in floor and ceil.
/** @see Min(), Max(), Clamp01(). */
[[nodiscard]] Vector4 Clamp(const Vector4& floor, const Vector4& ceil) const;
/// Limits each element of this vector in the range [0, 1].
/** @see Min(), Max(), Clamp(). */
[[nodiscard]] Vector4 Clamp01() const;
[[nodiscard]] float Distance(const Vector4& to) const;
/// Computes the length of this vector.
/** @return Sqrt(x*x + y*y + z*z + w*w).
@see LengthSq3(), Length3(), LengthSq4(), Normalize3(), Normalize4(). */
[[nodiscard]] float Length4() const;
[[nodiscard]] float Length() const;
/// Computes the squared length of this vector.
/** Calling this function is faster than calling Length4(), since this function avoids computing a square root.
If you only need to compare lengths to each other, but are not interested in the actual length values,
you can compare by using LengthSq4(), instead of Length4(), since Sqrt() is an order-preserving
(monotonous and non-decreasing) function.
@return x*x + y*y + z*z + w*w.
@see Length3(), LengthSq3(), Length4(), Normalize3(), Normalize4(). */
[[nodiscard]] float LengthSquared4() const;
[[nodiscard]] float LengthSquared() const;
[[nodiscard]] float LengthSq4() const;
[[nodiscard]] float LengthSq() const;
/// Computes the length of the (x, y, z) part of this vector.
/** @note This function ignores the w component of this vector.
@return Sqrt(x*x + y*y + z*z).
@see LengthSq3(), LengthSq4(), Length4(), Normalize3(), Normalize4(). */
[[nodiscard]] float Length3() const { return std::sqrt(x*x + y*y + z*z); }
/// Computes the squared length of the (x, y, z) part of this vector.
/** Calling this function is faster than calling Length3(), since this function avoids computing a square root.
If you only need to compare lengths to each other, but are not interested in the actual length values,
you can compare by using LengthSq3(), instead of Length3(), since Sqrt() is an order-preserving
(monotonous and non-decreasing) function.
@note This function ignores the w component of this vector.
@return x*x + y*y + z*z.
@see Length3(), LengthSq4(), Length4(), Normalize3(), Normalize4(). */
[[nodiscard]] float LengthSq3() const;
[[nodiscard]] float LengthSquared3() const;
[[nodiscard]] float Magnitude() const;
[[nodiscard]] float Dot(const Vector4& rhs) const;
/// Computes the dot product of the (x, y, z) parts of this and the given float4.
/** @note This function ignores the w component of this vector (assumes w=0).
@see Dot4(), Cross3(). */
[[nodiscard]] float Dot3(const Vector3& rhs) const;
[[nodiscard]] float Dot3(const Vector4& rhs) const;
[[nodiscard]] Vector4 Project(const Vector4& rhs) const;
// While it is feasable to compute a cross-product in four dimensions
// the cross product only has the orthogonality property in 3 and 7 dimensions
// You should consider instead looking at Gram-Schmidt Orthogonalization
// to find orthonormal vectors.
[[nodiscard]] Vector4 Cross3(const Vector3& rhs) const;
[[nodiscard]] Vector4 Cross3(const Vector4& rhs) const;
[[nodiscard]] Vector4 Cross(const Vector4& rhs) const;
[[nodiscard]] Vector4 Normalized() const;
[[nodiscard]] Vector4 Lerp(const Vector4& goal, float alpha) const;
[[nodiscard]] float AngleBetween(const Vector4& rhs) const;
/// Adds two vectors. [indexTitle: operators +,-,*,/]
/** This function is identical to the member function Add().
@return float4(x + v.x, y + v.y, z + v.z, w + v.w); */
Vector4 operator +(const Vector4& rhs) const;
/// Adds a vector to this vector. [IndexTitle: Add/Sub/Mul/Div]
/// @return (x+v.x, y+v.y, z+v.z, w+v.w).
[[nodiscard]] Vector4 Add(const Vector4& rhs) const;
static Vector4 Add(const Vector4& lhs, const Vector4& rhs);
/// Subtracts the given vector from this vector. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Sub().
@return float4(x - v.x, y - v.y, z - v.z, w - v.w); */
Vector4 operator -(const Vector4& rhs) const;
[[nodiscard]] Vector4 Sub(const Vector4& rhs) const;
static Vector4 Sub(const Vector4& lhs, const Vector4& rhs);
/// Multiplies this vector by a scalar. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Mul().
@return float4(x * scalar, y * scalar, z * scalar, w * scalar); */
Vector4 operator *(float rhs) const;
[[nodiscard]] Vector4 Mul(float scalar) const;
static Vector4 Mul(const Vector4& lhs, float rhs);
/// Divides this vector by a scalar. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Div().
@return float4(x / scalar, y / scalar, z / scalar, w * scalar); */
Vector4 operator /(float rhs) const;
[[nodiscard]] Vector4 Div(float scalar) const;
static Vector4 Div(const Vector4& rhs, float scalar);
Vector4 operator +() const; // Unary + Operator
/// Performs an unary negation of this vector. [similarOverload: operator+] [hideIndex]
/** This function is identical to the member function Neg().
@return float4(-x, -y, -z, -w). */
Vector4 operator -() const;
public:
float x = 0;
float y = 0;
float z = 0;
float w = 0;
void Normalize();
};
}

8
include/J3ML/Math.hpp Normal file
View File

@@ -0,0 +1,8 @@
//
// Created by dawsh on 7/6/24.
//
#ifndef MATH_HPP
#define MATH_HPP
#endif //MATH_HPP

7
include/J3ML/SSE.hpp Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
namespace J3ML
{
}

View File

@@ -1,9 +1,30 @@
/// Josh's 3D Math Library
/// A C++20 Library for 3D Math, Computer Graphics, and Scientific Computing.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file main.cpp
/// @desc Demonstrates J3ML features.
/// @edit 2024-07-06
#include <iostream>
#include <J3ML/Geometry.h>
#include <J3ML/J3ML.h>
#include <J3ML/Geometry.hpp>
#include "J3ML/J3ML.hpp"
int main(int argc, char** argv)
{
for (int i = 10; i < 9999999; i*=1.5f) {
std::cout << J3ML::Math::Functions::Truncate(i) << std::endl;
}
Ray a({420, 0, 0}, {1, 0, 0});
std::cout << a << std::endl;
std::cout << "j3ml demo coming soon" << std::endl;
return 0;
}

View File

@@ -1,11 +1,13 @@
#include <J3ML/Algorithm/Bezier.h>
#include <J3ML/Algorithm/Bezier.hpp>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Vector3.hpp>
namespace J3ML::Algorithm
{
using namespace J3ML::LinearAlgebra;
Vector2 BezierNormal(float t, const Vector2 &p0, const Vector2 &p1,
const Vector2 &p2, const Vector2 &p3) {
auto derived = BezierDerivative(t, p0, p1, p2, p3);
Vector2 derived = BezierDerivative(t, p0, p1, p2, p3);
return derived.Normalized();
}
@@ -16,5 +18,24 @@ namespace J3ML::Algorithm
Vector2 Bezier(float t, const Vector2 &p0, const Vector2 &p1, const Vector2 &p2, const Vector2 &p3) {
return {Bezier(t, p0.x, p1.x, p2.x, p3.x), Bezier(t, p0.y, p1.y, p2.y, p3.y)};
}
Vector3 BezierDerivative(float t, const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3)
{
return 3 * Square(1 - t) * (p1 - p0) + 6 * (1 - t) * t * (p2 - p1) + 3 * Square(t) * (p3 - p2);
}
Vector3 BezierNormal(float t, const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3)
{
Vector3 derived = BezierDerivative(t, p0, p1, p2, p3);
return derived.Normalized();
}
Vector3 Bezier(float t, const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3)
{
return {
Bezier(t, p0.x, p1.x, p2.x, p3.x),
Bezier(t, p0.y, p1.y, p2.y, p3.y),
Bezier(t, p0.z, p1.z, p2.z, p3.z)};
}
}

View File

@@ -1,6 +1,6 @@
#include <J3ML/Algorithm/GJK.h>
#include <J3ML/Algorithm/GJK.hpp>
#include <J3ML/Geometry.h>
#include <J3ML/Geometry.hpp>
namespace J3ML::Algorithms
{

View File

@@ -1,4 +1,4 @@
#include <J3ML/Algorithm/RNG.h>
#include <J3ML/Algorithm/RNG.hpp>
#include <stdexcept>
#include <cassert>
@@ -36,7 +36,7 @@ namespace J3ML::Algorithm {
u32 RNG::Int()
{
assert(modulus != 0);
//assert(modulus != 0);
/// TODO: Convert to using Shrage's method for approximate factorization (Numerical Recipes in C)
// Currently we cast everything to 64-bit to avoid overflow, which is quite dumb.
@@ -65,33 +65,16 @@ namespace J3ML::Algorithm {
return num;
}
/// Jesus-Fuck ~ Josh
/// As per C99, union-reinterpret should now be safe: http://stackoverflow.com/questions/8511676/portable-data-reinterpretation
union FloatIntReinterpret
{
float f;
u32 i;
};
template <typename To, typename From>
union ReinterpretOp {
To to;
From from;
};
template <typename To, typename From>
To ReinterpretAs(From input)
{
ReinterpretOp<To, From> fi {};
fi.to = input;
return fi.from;
}
float RNG::Float() {
u32 i = ((u32)Int() & 0x007FFFFF /* random mantissa */) | 0x3F800000 /* fixed exponent */;
// TODO: PROPERLY fix this.
auto f = (float)i / 1e31; //ReinterpretAs<float, u32>(i); // f is now in range [1, 2[
//f -= 1.f; // Map to range [0, 1[
auto f = ReinterpretAs<float, u32>(i); // f is now in range [1, 2[
f -= 1.f; // Map to range [0, 1[
assert(f >= 0.f);
assert(f < 1.f);
return f;
@@ -99,11 +82,11 @@ namespace J3ML::Algorithm {
float RNG::Float01Incl() {
for (int i = 0; i < 100; ++i) {
for (int i = 0; i < 1000; ++i) {
u32 val = (u32)Int() & 0x00FFFFFF;
if (val > 0x800000)
continue;
else if (val = 0x800000)
else if (val == 0x800000)
return 1.f;
else {
val |= 0x3F800000;

View File

@@ -1,17 +1,18 @@
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/AABB.hpp>
#include <cassert>
#include <J3ML/Geometry/Plane.h>
#include <J3ML/Geometry/Sphere.h>
#include <J3ML/Geometry/Plane.hpp>
#include <J3ML/Geometry/Sphere.hpp>
//#include <J3ML/Geometry/OBB.h>
#include <J3ML/Geometry/LineSegment.h>
#include <J3ML/Geometry/Triangle.h>
#include <J3ML/Geometry/Polygon.h>
#include <J3ML/Geometry/Frustum.h>
#include <J3ML/Geometry/Capsule.h>
#include <J3ML/Geometry/Ray.h>
#include <J3ML/Geometry/TriangleMesh.h>
#include <J3ML/Geometry/Polyhedron.h>
#include <J3ML/Algorithm/RNG.h>
#include <J3ML/Geometry/LineSegment.hpp>
#include <J3ML/Geometry/Triangle.hpp>
#include <J3ML/Geometry/Polygon.hpp>
#include <J3ML/Geometry/Frustum.hpp>
#include <J3ML/Geometry/Capsule.hpp>
#include <J3ML/Geometry/Ray.hpp>
#include <J3ML/Geometry/TriangleMesh.hpp>
#include <J3ML/Geometry/Polyhedron.hpp>
#include <J3ML/Algorithm/RNG.hpp>
#include <J3ML/Geometry/OBB.hpp>
namespace J3ML::Geometry {
@@ -72,6 +73,8 @@ namespace J3ML::Geometry {
return (minPoint+maxPoint) * 0.5f;
}
Vector3 AABB::CenterPoint() const { return Centroid(); }
Vector3 AABB::Size() const {
return this->maxPoint - this->minPoint;
}
@@ -274,6 +277,24 @@ namespace J3ML::Geometry {
Vector3 d = obb.r.x * absAxis0 + obb.r.y * absAxis1 + obb.r.z * absAxis2;
}
void AABB::Enclose(const Sphere &sphere) {
Vector3 radiusCubed(sphere.Radius);
Enclose(sphere.Position - radiusCubed, sphere.Position + radiusCubed);
}
void AABB::Enclose(const Triangle &triangle) {
// TODO: Implement alternate min or comparison operators for Vector3
Enclose(Vector3::Min(triangle.V0, triangle.V1, triangle.V2), Vector3::Max(triangle.V0, triangle.V1, triangle.V2));
}
void AABB::Enclose(const Capsule &capsule) {
Vector3 radiusCubed(capsule.r);
minPoint = Vector3::Min(minPoint, Vector3::Min(capsule.l.A, capsule.l.B) - radiusCubed);
maxPoint = Vector3::Max(maxPoint, Vector3::Max(capsule.l.A, capsule.l.B) - radiusCubed);
Enclose(minPoint, maxPoint);
}
Vector3 AABB::GetClosestPoint(const Vector3 &point) const {
Vector3 result = point;
if (point.x > this->maxPoint.x)
@@ -300,12 +321,12 @@ namespace J3ML::Geometry {
return result;
}
AABB::AABB(const Vector3 &min, const Vector3 &max) : Shape(), minPoint(min), maxPoint(max)
AABB::AABB(const Vector3 &min, const Vector3 &max) : minPoint(min), maxPoint(max)
{
}
AABB::AABB() : Shape() {}
AABB::AABB() {}
float Max(float a, float b)
{
@@ -457,7 +478,58 @@ namespace J3ML::Geometry {
}
bool AABB::Intersects(const AABB& aabb) const {
return Intersection(aabb).has_value();
// If any of the cardinal X,Y,Z axes is a separating axis, then
// there is no intersection.
return minPoint.x < aabb.maxPoint.x &&
minPoint.y < aabb.maxPoint.y &&
minPoint.z < aabb.maxPoint.z &&
aabb.minPoint.x < maxPoint.x &&
aabb.minPoint.y < maxPoint.y &&
aabb.minPoint.z < maxPoint.z;
}
TriangleMesh AABB::Triangulate(int numFacesX, int numFacesY, int numFacesZ, bool ccwIsFrontFacing) const {
TriangleMesh mesh;
assert(numFacesX >= 1);
assert(numFacesX >= 1);
assert(numFacesX >= 1);
// Generate both X-Y planes.
int i = 0;
for (int face = 0; face < 6; ++face) // Faces run in the order -X, +X, -Y, +Y, -Z, +Z.
{
int numFacesU;
int numFacesV;
bool flip = (face == 1 || face == 2 || face == 5);
if (ccwIsFrontFacing)
flip = !flip;
if (face == 0 || face == 1) {
numFacesU = numFacesY;
numFacesV = numFacesZ;
} else if (face == 2 || face == 3) {
numFacesU = numFacesX;
numFacesV = numFacesZ;
} else // if (face == 4 || face == 5)
{
numFacesU = numFacesX;
numFacesV = numFacesY;
}
for (int x = 0; x < numFacesU; ++x) {
for (int y = 0; numFacesV; ++y) {
float u = (float)x / (numFacesU);
float v = (float)y / (numFacesV);
float u2 = (float)(x+1) / (numFacesU);
float v2 = (float)(y+1) / (numFacesV);
//mesh.Vertices[i] = FacePoint(face, u, v);
//mesh.Vertices[i+1] =
// TODO: Come back to this once TriangleMesh is proper fleshed out
}
}
}
return mesh;
}
std::optional<AABB> AABB::Intersection(const AABB& rhs) const {

View File

@@ -1,4 +1,4 @@
#include <J3ML/Geometry/AABB2D.h>
#include <J3ML/Geometry/AABB2D.hpp>
namespace J3ML::Geometry
{
@@ -77,4 +77,20 @@ namespace J3ML::Geometry
a.maxPoint = maxPoint - pt;
return a;
}
Vector2 AABB2D::Centroid() {
return (minPoint + (maxPoint / 2.f));
}
Vector2 AABB2D::CornerPoint(int cornerIndex) {
assert(0 <= cornerIndex && cornerIndex <= 3);
switch(cornerIndex)
{
default:
case 0: return minPoint;
case 1: return {minPoint.x, maxPoint.y};
case 2: return {maxPoint.x, minPoint.y};
case 3: return maxPoint;
}
}
}

View File

@@ -1,8 +1,8 @@
#include <J3ML/Algorithm/GJK.h>
#include <J3ML/Geometry/Capsule.h>
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/Sphere.h>
#include <J3ML/Geometry/Polygon.h>
#include <J3ML/Algorithm/GJK.hpp>
#include <J3ML/Geometry/Capsule.hpp>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/Sphere.hpp>
#include <J3ML/Geometry/Polygon.hpp>
namespace J3ML::Geometry
{
@@ -106,6 +106,14 @@ namespace J3ML::Geometry
return polyhedron.Intersects(*this);
}
Sphere Capsule::SphereA() const {
return Sphere(l.A, r);
}
Sphere Capsule::SphereB() const {
return Sphere(l.B, r);
}
Vector3 Capsule::ExtremePoint(const Vector3 &direction) const {
float len = direction.Length();
assert(len > 0.f);
@@ -118,8 +126,79 @@ namespace J3ML::Geometry
return extremePoint;
}
bool Capsule::IsDegenerate() const {
return r <= 0.f; // Ask Bill :p
}
float Capsule::Height() const {
return LineLength() + Diameter();
}
Capsule Capsule::Translated(const Vector3 &offset) const
{
return Capsule(l.A + offset, l.B + offset, r);
}
Vector3 Capsule::Bottom() const { return l.A - UpDirection() * r;}
Vector3 Capsule::Center() const { return l.CenterPoint();}
Vector3 Capsule::Centroid() const { return Center(); }
float Capsule::Diameter() const { return 2.f * r;}
float Capsule::LineLength() const { return l.Length(); }
void Capsule::SetFrom(const Sphere &s) {
l = LineSegment(s.Position, s.Position);
r = s.Radius;
}
void Capsule::SetDegenerate() {
r = -1.f;
}
Vector3 Capsule::AnyPointFast() const { return l.A; }
Vector3 Capsule::UniformPointPerhapsInside(float height, float x, float y) const {
return MinimalEnclosingOBB().PointInside(height, x, y);
}
Vector3 Capsule::UpDirection() const
{
Vector3 d = l.B - l.A;
d.Normalize(); // Will always result in a normalized vector, even if l.a == l.b.
return d;
}
Vector3 Capsule::Top() const
{
return l.B + UpDirection() * r;
}
float Capsule::Volume() const
{
return Math::Pi * r * r * LineLength() + 4.f * Math::Pi * r * r * r / 3.f;
}
float Capsule::SurfaceArea() const
{
return 2.f * Math::Pi * r * LineLength() + 4.f * Math::Pi * r * r;
}
OBB Capsule::MinimalEnclosingOBB() const {
OBB obb;
obb.axis[0] = UpDirection();
obb.axis[0].PerpendicularBasis(obb.axis[1], obb.axis[2]);
obb.pos = Center();
obb.r[0] = Height() * 0.5f;
obb.r[1] = r;
obb.r[2] = r;
return obb;
}
}

View File

@@ -0,0 +1,38 @@
#include <J3ML/Geometry/Circle.hpp>
namespace J3ML
{
Circle::Circle(const Vector3& center, const Vector3& norm, float radius)
: Position(center), Normal(norm), Radius(radius)
{}
Vector3 Circle::BasisU() const { return Normal.Perpendicular(); }
Vector3 Circle::BasisV() const { return Normal.AnotherPerpendicular();}
Vector3 Circle::GetPoint(float angleRadians) const
{
float sin, cos;
Math::SinCos(angleRadians, sin, cos);
return Position + Radius * (sin * BasisU() + cos * BasisV());
}
Vector3 Circle::GetPoint(float angleRadians, float d) const
{
float sin, cos;
Math::SinCos(angleRadians, sin, cos);
return Position + Radius * d * (sin * BasisU() + cos * BasisV());
}
Vector3 Circle::ExtremePoint(const Vector3& direction) const
{
Vector3 d = direction - direction.ProjectToNorm(Normal);
if (d.IsZero())
return Position;
else
return Position + d.ScaledToLength(Radius);
}
}

View File

@@ -1,4 +1,4 @@
#include <J3ML/Geometry/Common.h>
#include <J3ML/Geometry/Forward.hpp>
namespace J3ML::Geometry {

View File

@@ -1,11 +1,10 @@
#include <J3ML/Geometry/Common.h>
#include <J3ML/Geometry/Frustum.h>
#include <J3ML/Geometry/Frustum.hpp>
#include <cmath>
#include "J3ML/Geometry/AABB.h"
#include <J3ML/Geometry/Polyhedron.h>
#include <J3ML/Geometry/LineSegment.h>
#include <J3ML/Geometry/Polygon.h>
#include <J3ML/Algorithm/GJK.h>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/Polyhedron.hpp>
#include <J3ML/Geometry/LineSegment.hpp>
#include <J3ML/Geometry/Polygon.hpp>
#include <J3ML/Algorithm/GJK.hpp>
namespace J3ML::Geometry
@@ -96,6 +95,22 @@ namespace J3ML::Geometry
}
}
Vector2 Frustum::ViewportToScreenSpace(float x, float y, int screenWidth, int screenHeight) {
return {(x + 1.f) * 0.5f * (screenWidth - 1.f), (1.f - y) * 0.5f * (screenHeight - 1.f)};
}
Vector2 Frustum::ViewportToScreenSpace(const Vector2 &point, int screenWidth, int screenHeight) {
return ViewportToScreenSpace(point.x, point.y, screenWidth, screenHeight);
}
Vector2 Frustum::ScreenToViewportSpace(float x, float y, int screenWidth, int screenHeight) {
return {x * 2.f / (screenWidth -1.f) - 1.f, 1.f - y * 2.f / (screenHeight - 1.f)};
}
Vector2 Frustum::ScreenToViewportSpace(const Vector2 &point, int screenWidth, int screenHeight) {
return ScreenToViewportSpace(point.x, point.y, screenWidth, screenHeight);
}
Vector3 Frustum::ExtremePoint(const Vector3 &direction, float &projectionDistance) const
{
Vector3 corners[8];
@@ -130,6 +145,19 @@ namespace J3ML::Geometry
return frustum;
}
PBVolume<6> Frustum::ToPBVolume() const {
PBVolume<6> frustumVolume;
frustumVolume.p[0] = NearPlane();
frustumVolume.p[1] = LeftPlane();
frustumVolume.p[2] = RightPlane();
frustumVolume.p[3] = TopPlane();
frustumVolume.p[4] = BottomPlane();
frustumVolume.p[5] = FarPlane();
return frustumVolume;
}
AABB Frustum::MinimalEnclosingAABB() const {
AABB aabb;
aabb.SetNegativeInfinity();
@@ -138,6 +166,141 @@ namespace J3ML::Geometry
return aabb;
}
bool Frustum::IsFinite() const {
return pos.IsFinite() && front.IsFinite() && up.IsFinite() && Math::IsFinite(nearPlaneDistance)
&& Math::IsFinite(farPlaneDistance) && Math::IsFinite(horizontalFov) && Math::IsFinite(verticalFov);
}
OBB Frustum::MinimalEnclosingOBB(float expandGuardband) const {
assert(IsFinite());
assert(front.IsNormalized());
assert(up.IsNormalized());
OBB obb;
obb.pos = pos + (nearPlaneDistance + farPlaneDistance) * 0.5f * front;
obb.axis[1] = up;
obb.axis[2] = -front;
obb.axis[0] = Vector3::Cross(obb.axis[1], obb.axis[2]);
obb.r = Vector3::Zero;
for (int i = 0; i < 8; ++i)
obb.Enclose(CornerPoint(i));
// Expand the generated OBB very slightly to avoid numerical issues when
// testing whether this Frustum actually is contained inside the generated OBB.
obb.r.x += expandGuardband;
obb.r.y += expandGuardband;
obb.r.z += expandGuardband;
return obb;
}
void Frustum::SetKind(FrustumProjectiveSpace projectiveSpace, FrustumHandedness handedness) {
}
void Frustum::SetViewPlaneDistances(float n, float f) {
nearPlaneDistance = n;
farPlaneDistance = f;
ProjectionMatrixChanged();
}
void Frustum::SetFrame(const Vector3 &pos, const Vector3 &front, const Vector3 &up) {
this->pos = pos;
this->front = front;
this->up = up;
WorldMatrixChanged();
}
void Frustum::SetPos(const Vector3 &pos) {
this->pos = pos;
WorldMatrixChanged();
}
void Frustum::SetFront(const Vector3 &front) {
this->front = front;
WorldMatrixChanged();
}
void Frustum::SetUp(const Vector3 &up) {
this->up = up;
WorldMatrixChanged();
}
void Frustum::SetPerspective(float h, float v) {
type = FrustumType::Perspective;
this->horizontalFov = h;
this->verticalFov = v;
ProjectionMatrixChanged();
}
void Frustum::SetOrthographic(float w, float h) {
type = FrustumType::Orthographic;
orthographicWidth = w;
orthographicHeight = h;
ProjectionMatrixChanged();
}
float Frustum::AspectRatio() const {
return Math::Tan(horizontalFov* 0.5f) / Math::Tan(verticalFov*0.5f);
}
void Frustum::SetHorizontalFovAndAspectRatio(float hFov, float aspectRatio) {
type = FrustumType::Perspective;
horizontalFov = hFov;
verticalFov = 2.f * Math::Atan(Math::Tan(hFov * 0.5f) / aspectRatio);
ProjectionMatrixChanged();
}
void Frustum::SetVerticalFovAndAspectRatio(float vFov, float aspectRatio) {
type = FrustumType::Perspective;
verticalFov = vFov;
horizontalFov = 2.f * Math::Atan(Math::Tan(vFov * 0.5f) * aspectRatio);
return ProjectionMatrixChanged();
}
Ray Frustum::UnProject(float x, float y) const {
assert(x >= -1.f);
assert(x <= 1.f);
assert(y >= -1.f);
assert(y <= 1.f);
if (type == FrustumType::Perspective) {
Vector3 nearPlanePos = NearPlanePos(x, y);
return Ray(pos, (nearPlanePos - pos).Normalized());
} else
return UnProjectFromNearPlane(x, y);
}
Ray Frustum::UnProject(const Vector2 &xy) const {
return UnProject(xy.x, xy.y);
}
Ray Frustum::UnProjectFromNearPlane(float x, float y) const {
return UnProjectLineSegment(x, y).ToRay();
}
LineSegment Frustum::UnProjectLineSegment(float x, float y) const {
Vector3 nearPlanePos = NearPlanePos(x, y);
Vector3 farPlanePos = FarPlanePos(x, y);
return {nearPlanePos, farPlanePos};
}
Vector3 Frustum::PointInside(float x, float y, float z) const {
assert(z >= 0.f);
assert(z <= 1.f);
return UnProjectLineSegment(x, y).GetPoint(z);
}
Vector3 Frustum::PointInside(const Vector3 &xyz) const {
return PointInside(xyz.x, xyz.y, xyz.z);
}
Vector3 Frustum::CenterPoint() const {
return pos + (nearPlaneDistance + farPlaneDistance) * 0.5f * front;
}
Vector3 Frustum::CornerPoint(int cornerIndex) const {
assert(0 <= cornerIndex && cornerIndex <= 7);
switch(cornerIndex)
@@ -175,6 +338,8 @@ namespace J3ML::Geometry
}
}
Vector3 Frustum::NearPlanePos(const Vector2 &point) const { return NearPlanePos(point.x, point.y); }
Vector3 Frustum::FarPlanePos(float x, float y) const {
assert(type == FrustumType::Perspective || type == FrustumType::Orthographic);
@@ -195,6 +360,140 @@ namespace J3ML::Geometry
}
}
Vector3 Frustum::FarPlanePos(const Vector2 &point) const {
return FarPlanePos(point.x, point.y);
}
Plane Frustum::TopPlane() const {
if (type == FrustumType::Perspective) {
Vector3 topSide = front + Math::Tan(verticalFov * 0.5f) * up;
Vector3 right = WorldRight();
Vector3 topSideNormal = ((handedness == FrustumHandedness::Right) ? Vector3::Cross(right, topSide) : Vector3::Cross(topSide, right)).Normalized();
return Plane(pos, topSideNormal);
} else
{
return Plane(NearPlanePos(0.f, 1.f), up);
}
}
Plane Frustum::BottomPlane() const {
if (type == FrustumType::Perspective) {
Vector3 bottomSide = front - Math::Tan(verticalFov * 0.5f) * up;
Vector3 left = -WorldRight();
Vector3 bottomSideNormal = ((handedness == FrustumHandedness::Right) ? Vector3::Cross(left, bottomSide) : Vector3::Cross(bottomSide, left)).Normalized();
return Plane(pos, bottomSideNormal);
} else {
return Plane(NearPlanePos(0.f, -1.f), -up);
}
}
Plane Frustum::RightPlane() const {
if (type == FrustumType::Perspective) {
Vector3 right = WorldRight();
right.ScaleToLength(Math::Tan(horizontalFov * 0.5f));
Vector3 rightSide = front + right;
Vector3 rightSideNormal = ((handedness == FrustumHandedness::Right) ? Vector3::Cross(rightSide, up) : Vector3::Cross(up, rightSide)).Normalized();
return Plane(pos, rightSideNormal);
} else {
Vector3 right = WorldRight();
return Plane(NearPlanePos(1.f,0.f), right.Normalized());
}
}
Plane Frustum::LeftPlane() const {
if (type == FrustumType::Perspective) {
Vector3 left = -WorldRight();
left.ScaleToLength(Math::Tan(horizontalFov*0.5f));
Vector3 leftSide = front + left;
Vector3 leftSideNormal = ((handedness == FrustumHandedness::Right) ? Vector3::Cross(up, leftSide) : Vector3::Cross(leftSide, up)).Normalized();
return Plane(pos, leftSideNormal);
} else {
Vector3 left = -WorldRight();
return Plane(NearPlanePos(-1.f, 0.f), left.Normalized());
}
}
Plane Frustum::FarPlane() const {
return Plane(pos + front * farPlaneDistance, front);
}
Plane Frustum::NearPlane() const {
return Plane(pos + front * nearPlaneDistance, -front);
}
float Frustum::NearPlaneWidth() const {
if (type == FrustumType::Perspective)
return Math::Tan(horizontalFov*0.5f)*2.f * nearPlaneDistance;
else
return orthographicWidth;
}
float Frustum::NearPlaneHeight() const {
if (type == FrustumType::Perspective)
return Math::Tan(verticalFov*0.5f) * 2.f * nearPlaneDistance;
else
return orthographicWidth;
}
Plane Frustum::GetPlane(int faceIndex) const {
assert(0 <= faceIndex && faceIndex <= 5);
switch(faceIndex)
{
default: // For release builds where assume() is disabled, always return the first option if out-of-bounds.
case 0: return NearPlane();
case 1: return FarPlane();
case 2: return LeftPlane();
case 3: return RightPlane();
case 4: return TopPlane();
case 5: return BottomPlane();
}
}
void Frustum::GetPlanes(Plane *outArray) const {
assert(outArray);
for (int i = 0; i < 6; ++i)
outArray[i] = GetPlane(i);
}
void Frustum::Translate(const Vector3 &offset) {
pos += offset;
}
void Frustum::Transform(const Matrix3x3& transform) {
assert(transform.HasUniformScale());
pos = transform * pos;
front = transform * front;
float scaleFactor = front.Normalize();
up = (transform * up).Normalized();
nearPlaneDistance *= scaleFactor;
farPlaneDistance *= scaleFactor;
if (type == FrustumType::Orthographic) {
orthographicWidth *= scaleFactor;
orthographicHeight *= scaleFactor;
}
}
void Frustum::Transform(const Matrix4x4& transform) {
assert(transform.Row(3).Equals(0,0,0,1));
assert(transform.HasUniformScale());
// TODO: This is incorrect. Technically we need only a mat3x4 to implement this operation.
// But we choose to not implement this in our code, instead opting for only 3x3 and 4x4.
// Care should be taken when using this, it may need to be edited for mathematical correctness
Transform(transform.GetRotatePart());
}
void Frustum::Transform(const Quaternion& transform) {
Transform(transform.ToMatrix3x3());
}
Polyhedron Frustum::ToPolyhedron() const {
// Note to maintainer: this function is an exact copy of AABB::ToPolyhedron() and OBB::ToPolyhedron().
@@ -234,6 +533,182 @@ namespace J3ML::Geometry
return p;
}
Matrix4x4 Frustum::ViewProjMatrix() const { return viewProjectionMatrix; }
Matrix4x4 Frustum::ComputeViewProjMatrix() const { return ComputeProjectionMatrix() * ComputeViewMatrix();}
Vector3 Frustum::Project(const Vector3 &point) const {
Vector4 projectedPoint = ViewProjMatrix().Mul(Vector4(point, 1.f));
projectedPoint.NormalizeW();
return projectedPoint.XYZ();
}
Matrix4x4 Frustum::WorldMatrix() const { return worldMatrix;}
Matrix4x4 Frustum::ComputeWorldMatrix() const {
assert(pos.IsFinite());
//assert(up.IsNormalized(1e-3f));
assert(front.IsNormalized(1e-3f));
assert(up.IsPerpendicular(front));
Matrix4x4 m;
m.SetCol3(0, WorldRight().Normalized());
m.SetCol3(1, up);
if (handedness == FrustumHandedness::Right)
m.SetCol3(2, -front);
else
m.SetCol3(2, front);
m.SetCol3(3, pos);
assert(!m.HasNegativeScale());
return m;
}
Matrix4x4 Frustum::ViewMatrix() const { auto m = worldMatrix; m.InverseOrthonormal(); return m; }
Matrix4x4 Frustum::ComputeViewMatrix() const {
Matrix4x4 world = ComputeWorldMatrix();
world.InverseOrthonormal();
return world;
}
Matrix4x4 Frustum::ProjectionMatrix() const { return projectionMatrix;}
Matrix4x4 Frustum::ComputeProjectionMatrix() const {
if (type == FrustumType::Invalid || projectiveSpace == FrustumProjectiveSpace::Invalid)
return Matrix4x4::NaN;
//assert(type == FrustumType::Perspective || type == FrustumType::Orthographic);
//assert(projectiveSpace == FrustumProjectiveSpace::GL || projectiveSpace == FrustumProjectiveSpace::D3D);
//assert(handedness == FrustumHandedness::Left || handedness == FrustumHandedness::Right);
if (type == FrustumType::Perspective) {
if (projectiveSpace == FrustumProjectiveSpace::GL) {
if (handedness == FrustumHandedness::Right)
return Matrix4x4::OpenGLPerspProjRH(nearPlaneDistance, farPlaneDistance, NearPlaneWidth(), NearPlaneHeight());
else
return Matrix4x4::OpenGLPerspProjLH(nearPlaneDistance, farPlaneDistance, NearPlaneWidth(), NearPlaneHeight());
} else if (projectiveSpace == FrustumProjectiveSpace::D3D) {
if (handedness == FrustumHandedness::Right)
return Matrix4x4::D3DPerspProjRH(nearPlaneDistance, farPlaneDistance, NearPlaneWidth(), NearPlaneHeight());
else
return Matrix4x4::D3DPerspProjLH(nearPlaneDistance, farPlaneDistance, NearPlaneHeight(), NearPlaneHeight());
}
} else if (type == FrustumType::Orthographic) {
if (projectiveSpace == FrustumProjectiveSpace::GL) {
if (handedness == FrustumHandedness::Right)
return Matrix4x4::OpenGLOrthoProjRH(nearPlaneDistance, farPlaneDistance, orthographicWidth, orthographicHeight);
else if (handedness == FrustumHandedness::Left)
return Matrix4x4::OpenGLOrthoProjLH(nearPlaneDistance, farPlaneDistance, orthographicWidth, orthographicHeight);
} else if (projectiveSpace == FrustumProjectiveSpace::D3D) {
if (handedness == FrustumHandedness::Right)
return Matrix4x4::D3DOrthoProjRH(nearPlaneDistance, farPlaneDistance, orthographicWidth, orthographicHeight);
else
return Matrix4x4::D3DOrthoProjLH(nearPlaneDistance, farPlaneDistance, orthographicWidth, orthographicHeight);
}
}
//assert(false && "Not all values of Frustum were initialized properly! Please initialize correctly before calling Frustum::ProjectionMatrix()!");
return Matrix4x4::NaN;
}
bool Frustum::Contains(const Vector3 &point) const {
const float eps = 1e-3f;
const float position = 1.f + eps;
const float neg = -position;
Vector3 projected = Project(point);
if (projectiveSpace == FrustumProjectiveSpace::D3D) {
return neg <= projected.x && projected.x <= position &&
neg <= projected.y && projected.y <= position &&
-eps <= projected.z && projected.z <= position;
} else if (projectiveSpace == FrustumProjectiveSpace::GL) {
return neg <= projected.x && projected.x <= position &&
neg <= projected.y && projected.y <= position &&
neg <= projected.z && projected.z <= position;
} else {
// TODO: Make Frustum::Contains agnostic of the projection settings.
assert(false && "Not all values of Frustum were initialized properly! Please initialize correctly before calling Frustum::Contains()!");
return false;
}
}
bool Frustum::Contains(const LineSegment &lineSegment) const {
return Contains(lineSegment.A) && Contains(lineSegment.B);
}
bool Frustum::Contains(const Triangle &triangle) const {
return Contains(triangle.V0) && Contains(triangle.V1) && Contains(triangle.V2);
}
bool Frustum::Contains(const Polygon &polygon) const {
for (int i = 0; i < polygon.NumVertices(); ++i)
if (!Contains(polygon.Vertex(i)))
return false;
return true;
}
bool Frustum::Contains(const AABB &aabb) const {
for (int i = 0; i < 8; ++i)
if (!Contains(aabb.CornerPoint(i)))
return false;
return true;
}
bool Frustum::Contains(const OBB &obb) const {
for (int i = 0; i < 8; ++i)
if (!Contains(obb.CornerPoint(i)))
return false;
return true;
}
bool Frustum::Contains(const Frustum &frustum) const {
for (int i = 0; i < 8; ++i)
if (!Contains(frustum.CornerPoint(i)))
return false;
return true;
}
bool Frustum::Contains(const Polyhedron &polyhedron) const {
assert(polyhedron.IsClosed());
for (int i = 0; i < polyhedron.NumVertices(); ++i)
if (!Contains(polyhedron.Vertex(i)))
return false;
return true;
}
float Frustum::Distance(const Vector3 &point) const {
Vector3 pt = ClosestPoint(point);
return pt.Distance(point);
}
Vector3 Frustum::ClosestPoint(const Vector3 &point) const {
if (type == FrustumType::Orthographic) {
float frontHalfSize = (farPlaneDistance - nearPlaneDistance) * 0.5f;
float halfWidth = orthographicWidth * 0.5f;
float halfHeight = orthographicHeight * 0.5f;
Vector3 frustumCenter = pos + (frontHalfSize + nearPlaneDistance) * front;
Vector3 right = Vector3::Cross(front, up);
assert(right.IsNormalized());
Vector3 d = point - frustumCenter;
Vector3 closestPoint = frustumCenter;
closestPoint += Math::Clamp(Vector3::Dot(d, front), -frontHalfSize, frontHalfSize) * front;
closestPoint += Math::Clamp(Vector3::Dot(d, right), -halfWidth, halfWidth) * right;
closestPoint += Math::Clamp(Vector3::Dot(d, up), -halfHeight, halfHeight) * up;
return closestPoint;
} else {
return ToPolyhedron().ClosestPoint(point);
}
}
bool Frustum::Intersects(const Ray &ray) const {
return this->ToPolyhedron().Intersects(ray);
}
@@ -294,6 +769,22 @@ namespace J3ML::Geometry
return this->ToPolyhedron().Intersects(polyhedron);
}
void Frustum::WorldMatrixChanged() {
worldMatrix = ComputeWorldMatrix();
Matrix4x4 viewMatrix = worldMatrix;
viewMatrix.InverseOrthonormal();
viewProjectionMatrix = projectionMatrix * viewMatrix;
}
void Frustum::ProjectionMatrixChanged() {
projectionMatrix = ComputeProjectionMatrix();
if (!Math::IsNotANumber(worldMatrix[0][0])) {
Matrix4x4 viewMatrix = worldMatrix;
viewMatrix.InverseOrthonormal();
viewProjectionMatrix = projectionMatrix * viewMatrix;
}
}
Frustum::Frustum()
: type(FrustumType::Invalid),
pos(Vector3::NaN),
@@ -314,4 +805,6 @@ namespace J3ML::Geometry
else
return Vector3::Cross(up, front);
}
}
}

View File

@@ -0,0 +1,6 @@
#include <J3ML/Geometry/Icosahedron.hpp>
namespace J3ML
{
}

View File

@@ -1,6 +1,6 @@
#include <J3ML/LinearAlgebra.h>
#include <J3ML/Geometry/Line.h>
#include <J3ML/Geometry/LineSegment.h>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry/Line.hpp>
#include <J3ML/Geometry/LineSegment.hpp>
namespace J3ML::Geometry
{

View File

@@ -1,6 +1,9 @@
#include <J3ML/Geometry/LineSegment.h>
#include "J3ML/Geometry/Capsule.h"
#include <J3ML/Geometry/Line.h>
#include <J3ML/Geometry/LineSegment.hpp>
#include "J3ML/Geometry/Capsule.hpp"
#include <J3ML/Geometry/Line.hpp>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry/Plane.hpp>
#include <J3ML/Geometry/Ray.hpp>
namespace J3ML::Geometry {
@@ -9,6 +12,10 @@ namespace J3ML::Geometry {
}
LineSegment::LineSegment(const Ray &ray, float d): A(ray.Origin), B(ray.GetPoint(d)) {}
LineSegment::LineSegment(const Line &line, float d): A(line.Position), B(line.GetPoint(d)) {}
LineSegment::LineSegment() {}
void LineSegment::ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const
@@ -24,6 +31,10 @@ namespace J3ML::Geometry {
return (1.f - d) * A + d * B;
}
Vector3 LineSegment::CenterPoint() const { return (A+B) * 0.5f;}
void LineSegment::Reverse() { Swap(A, B);}
float LineSegment::Distance(const Vector3 &point, float &d) const
{
/// See Christer Ericson's Real-Time Collision Detection, p.130.
@@ -73,10 +84,23 @@ namespace J3ML::Geometry {
Vector3 LineSegment::ClosestPoint(const Vector3 &point, float &d) const {
Vector3 dir = B - A;
d = Clamp01(Vector3::Dot(point - A, dir) / dir.LengthSquared());
d = Math::Clamp01(Vector3::Dot(point - A, dir) / dir.LengthSquared());
return A + d * dir;
}
Vector3 LineSegment::ClosestPoint(const Ray &other) const { float d, d2; return ClosestPoint(other, d, d2);}
Vector3 LineSegment::ClosestPoint(const Ray &other, float &d) const { float d2; return ClosestPoint(other, d, d2);}
Vector3 LineSegment::ClosestPoint(const Ray &other, float &d, float &d2) const {
other.ClosestPoint(*this, d2, d);
return GetPoint(d);
}
Vector3 LineSegment::ClosestPoint(const Line &other) const { float d, d2; return ClosestPoint(other, d, d2); }
Vector3 LineSegment::ClosestPoint(const Line &other, float &d) const { float d2; return ClosestPoint(other, d, d2); }
Vector3 LineSegment::ClosestPoint(const Line &other, float &d, float &d2) const
{
Line::ClosestPointLineLine(other.Position, other.Direction, A,B - A, d2, d);
@@ -96,10 +120,9 @@ namespace J3ML::Geometry {
return GetPoint(d);
}
Vector3 LineSegment::ClosestPoint(const Ray &other, float &d, float &d2) const {
other.ClosestPoint(*this, d2, d);
return GetPoint(d);
}
Vector3 LineSegment::ClosestPoint(const LineSegment &other) const { float d, d2; return ClosestPoint(other, d, d2); }
Vector3 LineSegment::ClosestPoint(const LineSegment &other, float &d) const { float d2; return ClosestPoint(other, d, d2); }
Vector3 LineSegment::ClosestPoint(const LineSegment &other, float &d, float &d2) const
{
@@ -209,10 +232,14 @@ namespace J3ML::Geometry {
return A.DistanceSq(B);
}
bool LineSegment::Intersects(const LineSegment &segment) const {
bool LineSegment::IsFinite() const { return A.IsFinite() && B.IsFinite(); }
bool LineSegment::Intersects(const LineSegment &segment, float epsilon) const {
return false;
}
Vector3 LineSegment::AnyPointFast() const { return A; }
Vector3 LineSegment::ExtremePoint(const Vector3 &direction) const {
return Vector3::Dot(direction, B-A) >= 0.f ? B : A;
}
@@ -234,7 +261,15 @@ namespace J3ML::Geometry {
return GetPoint(d).Distance(other.GetPoint(d2));
}
Ray LineSegment::ToRay() const {
return Ray(A, Dir());
}
Line LineSegment::ToLine() const {
return Line(A, Dir());
}
LineSegment operator*(const Matrix4x4 &transform, const LineSegment &l) {
return LineSegment(transform.Mul(l.A), transform.Mul(l.B));
}
}
}

View File

@@ -0,0 +1,165 @@
#include <J3ML/Geometry/LineSegment2D.hpp>
void Line2DClosestPointLineLine(const Vector2& v0, const Vector2& v10, const Vector2& v2, const Vector2& v32, float& d, float& d2) // TODO: Move to Line2D when that exists
{
// assert(!v10.IsZero());
// assert(!v32.IsZero());
Vector2 v02 = v0 - v2;
float d0232 = v02.Dot(v32);
float d3210 = v32.Dot(v10);
float d3232 = v32.Dot(v32);
// assert(d3232 != 0.f); // Don't call with a zero direction vector.
float d0210 = v02.Dot(v10);
float d1010 = v10.Dot(v10);
float denom = d1010*d3232 - d3210*d3210;
d = (denom !=0) ? (d0232*d3210 - d0210*d3232) / denom : 0;
d2 = (d0232 + d * d3210) / d3232;
}
Geometry::LineSegment2D::LineSegment2D(const Vector2 &a, const Vector2 &b)
: A(a), B(b) {}
Vector2 Geometry::LineSegment2D::GetPoint(float d) const {
return (1.f - d) * A + d * B;
}
Vector2 Geometry::LineSegment2D::CenterPoint() const {
return (A + B) * 0.5f;
}
Vector2 Geometry::LineSegment2D::Centroid() const {
return CenterPoint();
}
void Geometry::LineSegment2D::Reverse() {
Swap(A, B);
}
Vector2 Geometry::LineSegment2D::Dir() const {
return (B - A).Normalized();
}
Vector2 Geometry::LineSegment2D::AnyPointFast() const { return A; }
Vector2 Geometry::LineSegment2D::ExtremePoint(const Vector2 &direction) const {
return Vector2::Dot(direction, B - A) >= 0.f ? B : A;
}
Vector2 Geometry::LineSegment2D::ExtremePoint(const Vector2 &direction, float &projectionDistance) const {
Vector2 extremePoint = ExtremePoint(direction);
projectionDistance = extremePoint.Dot(direction);
return extremePoint;
}
void Geometry::LineSegment2D::Translate(const Vector2 &offset) {
A += offset;
B += offset;
}
void Geometry::LineSegment2D::Transform(const Matrix3x3 &transform) {
A = transform.Mul(A);
B = transform.Mul(B);
}
void Geometry::LineSegment2D::Transform(const Matrix4x4 &transform) {
A = transform.Mul(A);
B = transform.Mul(B);
}
void Geometry::LineSegment2D::Transform(const Quaternion &transform) {
//A = transform.Mul(A);
//B = transform.Mul(B);
}
float Geometry::LineSegment2D::Length() const { return A.Distance(B); }
float Geometry::LineSegment2D::LengthSq() const { return A.DistanceSq(B); }
float Geometry::LineSegment2D::LengthSquared() const { return LengthSq(); }
bool Geometry::LineSegment2D::IsFinite() const { return A.IsFinite() && B.IsFinite(); }
bool Geometry::LineSegment2D::Equals(const Geometry::LineSegment2D &rhs, float epsilon) const {
return (A.Equals(rhs.A, epsilon) && B.Equals(rhs.B, epsilon) ||
A.Equals(rhs.B, epsilon) && B.Equals(rhs.A, epsilon));
}
bool Geometry::LineSegment2D::Contains(const Vector2 &point, float epsilon) const {
return ClosestPoint(point).DistanceSq(point) <= epsilon;
}
bool Geometry::LineSegment2D::Contains(const Geometry::LineSegment2D &rhs, float epsilon) const {
return Contains(rhs.A, epsilon) && Contains(rhs.B, epsilon);
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Vector2 &point) const {
float d;
return ClosestPoint(point, d);
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Vector2 &point, float &d) const {
Vector2 dir = B - A;
d = J3ML::Math::Clamp01(Vector2::Dot(point - A, dir) / dir.LengthSquared());
return A + d * dir;
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Geometry::LineSegment2D &other) const {
float d, d2;
return ClosestPoint(other, d, d2);
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Geometry::LineSegment2D &other, float &d) const {
float d2; return ClosestPoint(other, d, d2);
}
Vector2 Geometry::LineSegment2D::ClosestPoint(const Geometry::LineSegment2D &other, float &d, float &d2) const {
Vector2 dir = B - A;
Line2DClosestPointLineLine(A, B - A, other.A, other.B - other.A, d, d2);
return Vector2::Zero;
}
float Geometry::LineSegment2D::Distance(const Vector2 &point) const { float d; return Distance(point, d); }
float Geometry::LineSegment2D::Distance(const Vector2 &point, float &d) const {
/// See Christer Ericson's Real-Time Collision Detection, p. 130.
Vector2 closestPoint = ClosestPoint(point, d);
return closestPoint.Distance(point);
}
float Geometry::LineSegment2D::Distance(const Geometry::LineSegment2D &other) const { float d, d2; return Distance(other, d, d2);}
float Geometry::LineSegment2D::Distance(const Geometry::LineSegment2D &other, float &d) const { float d2; return Distance(other, d, d2); }
float Geometry::LineSegment2D::Distance(const Geometry::LineSegment2D &other, float &d, float &d2) const {
ClosestPoint(other, d, d2);
return GetPoint(d).Distance(other.GetPoint(d2));
}
float Geometry::LineSegment2D::DistanceSq(const Geometry::LineSegment2D &other) const {
float d, d2;
ClosestPoint(other, d, d2);
return GetPoint(d).DistanceSq(other.GetPoint(d2));
}
float Geometry::LineSegment2D::DistanceSq(const Vector2 &point) const {
float d;
/// See Christer Ericson's Real-Time Collision Detection, p.130.
Vector2 closestPoint = ClosestPoint(point, d);
return closestPoint.DistanceSq(point);
}
bool Geometry::LineSegment2D::Intersects(const Geometry::LineSegment2D &lineSegment, float epsilon) const {
return Distance(lineSegment) <= epsilon;
}
void Geometry::LineSegment2D::ProjectToAxis(const Vector2 &direction, float &outMin, float &outMax) const {
outMin = Vector2::Dot(direction, A);
outMax = Vector2::Dot(direction, B);
if (outMax < outMin)
Swap(outMin, outMax);
}

View File

@@ -1,12 +1,13 @@
#include <J3ML/Geometry/Shape.h>
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/LineSegment.h>
#include <J3ML/Geometry/Polyhedron.h>
#include <J3ML/Geometry/OBB.h>
#include <J3ML/Geometry/Sphere.h>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/LineSegment.hpp>
#include <J3ML/Geometry/Polyhedron.hpp>
#include <J3ML/Geometry/OBB.hpp>
#include <J3ML/Geometry/Plane.hpp>
#include <J3ML/Geometry/Sphere.hpp>
namespace J3ML::Geometry
{
namespace J3ML::Geometry {
using namespace J3ML::Math;
Polyhedron OBB::ToPolyhedron() const {
// Note to maintainer: This function is an exact copy of AABB::ToPolyhedron() and Frustum::ToPolyhedron()
@@ -22,12 +23,12 @@ namespace J3ML::Geometry
// generate the 6 faces of this OBB.
const int faces[6][4] =
{
{0, 1, 3, 2}, // X-
{4, 6, 7, 5}, // X+
{0, 4, 5, 1}, // Y-
{7, 6, 2, 3}, // Y+
{0, 2, 6, 4}, // Z-
{1, 5, 7, 3} // Z+
{0, 1, 3, 2}, // X-
{4, 6, 7, 5}, // X+
{0, 4, 5, 1}, // Y-
{7, 6, 2, 3}, // Y+
{0, 2, 6, 4}, // Z-
{1, 5, 7, 3} // Z+
};
for (int f = 0; f < 6; ++f)
@@ -405,6 +406,50 @@ namespace J3ML::Geometry
return m;
}
Vector3 OBB::ClosestPoint(const Vector3& targetPoint) const {
Vector3 d = targetPoint - pos;
Vector3 closestPoint = pos; // Start at the center point of the OBB.
for (int i = 0; i < 3; ++i) // Project the target onto the OBB axes and walk towards that point.
closestPoint += Clamp(Vector3::Dot(d, axis[i]), -r[i], r[i]) * axis[i];
return closestPoint;
}
void OBB::Enclose(const Vector3& point) {
Vector3 p = point - pos;
for (int i = 0; i < 3; ++i) {
assert(Math::EqualAbs(axis[i].Length(), 1.f));
float dist = p.Dot(axis[i]);
float distanceFromOBB = Math::Abs(dist) - r[i];
if (distanceFromOBB > 0.f) {
r[i] += distanceFromOBB * 0.5f;
if (dist > 0.f) // TODO: Optimize out this comparison!
pos += axis[i] * distanceFromOBB * 0.5f;
else
pos -= axis[i] * distanceFromOBB * 0.5f;
p = point - pos;
assert(Math::EqualAbs(Math::Abs(p.Dot(axis[i])), r[i], 1e-1f));
}
}
assert(Distance(point) <= 1e-3f);
}
float OBB::Distance(const Vector3& point) const {
Vector3 closestPoint = ClosestPoint(point);
return point.Distance(closestPoint);
}
bool OBB::Contains(const Vector3 &point) const {
Vector3 pt = point - pos;
return Abs(Vector3::Dot(pt, axis[0])) <= r[0] &&
Abs(Vector3::Dot(pt, axis[1])) <= r[1] &&
Abs(Vector3::Dot(pt, axis[2])) <= r[2];
}
Matrix4x4 OBB::LocalToWorld() const
{
// To produce a normalized local->world matrix, do the following.

View File

@@ -0,0 +1 @@
#include <J3ML/Geometry/PBVolume.hpp>

View File

@@ -1,9 +1,14 @@
#include <J3ML/Geometry/Plane.h>
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/OBB.h>
#include <J3ML/Geometry/Capsule.h>
#include <J3ML/Geometry/Polygon.h>
#include <J3ML/Geometry/Sphere.h>
#include <J3ML/Geometry/Plane.hpp>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/OBB.hpp>
#include <J3ML/Geometry/Capsule.hpp>
#include <J3ML/Geometry/Frustum.hpp>
#include <J3ML/Geometry/Line.hpp>
#include <J3ML/Geometry/Polygon.hpp>
#include <J3ML/Geometry/Polyhedron.hpp>
#include <J3ML/Geometry/Ray.hpp>
#include <J3ML/Geometry/Sphere.hpp>
#include <J3ML/Geometry/Triangle.hpp>
namespace J3ML::Geometry
{
@@ -130,14 +135,15 @@ namespace J3ML::Geometry
#endif
}
Plane::Plane(const Vector3 &normal, float d): Normal(normal), distance(d) {}
Plane::Plane(const Vector3 &v1, const Vector3 &v2, const Vector3 &v3) {
Set(v1, v2, v3);
}
Plane::Plane(const Vector3 &pos, const Vector3 &norm)
: Shape(), Position(pos), Normal(norm) {}
Plane::Plane(const Vector3 &pos, const Vector3 &norm) : Position(pos), Normal(norm) {}
bool Plane::Intersects(J3ML::Geometry::Ray ray, float *dist) const {
bool Plane::Intersects(Ray ray, float *dist) const {
float t;
bool success = IntersectLinePlane(Normal, this->distance, ray.Origin, ray.Direction, t);
if (dist)
@@ -231,6 +237,14 @@ namespace J3ML::Geometry
return projected;
}
Vector3 Plane::ProjectToNegativeHalf(const Vector3 &point) const {
return point - Math::Max(0.f, (Normal.Dot(point) - distance)) * Normal;
}
Vector3 Plane::ProjectToPositiveHalf(const Vector3 &point) const {
return point - Math::Min(0.f, (Vector3::Dot(Normal, point) - distance)) * Normal;
}
LineSegment Plane::Project(const LineSegment &lineSegment) {
return LineSegment(Project(lineSegment.A), Project(lineSegment.B));
}

View File

@@ -1,11 +1,13 @@
#include <J3ML/Geometry/Polygon.h>
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/Triangle.h>
#include "J3ML/Geometry/Plane.h"
#include "J3ML/Geometry/Line.h"
#include <J3ML/Algorithm/GJK.h>
#include <J3ML/Geometry/Polygon.hpp>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/Triangle.hpp>
#include "J3ML/Geometry/Plane.hpp"
#include "J3ML/Geometry/Line.hpp"
#include <J3ML/Algorithm/GJK.hpp>
namespace J3ML::Geometry {
Vector3 Polygon::AnyPointFast() const { return !vertices.empty() ? vertices[0] : Vector3::NaN; }
AABB Polygon::MinimalEnclosingAABB() const {
AABB aabb;
aabb.SetNegativeInfinity();
@@ -570,6 +572,19 @@ namespace J3ML::Geometry {
return closestPt;
}
Polyhedron Polygon::ToPolyhedron() const {
Polyhedron poly;
poly.v = vertices;
poly.f.push_back(Polyhedron::Face());
poly.f.push_back(Polyhedron::Face());
for(int i = 0; i < NumVertices(); ++i)
{
poly.f[0].v.push_back(i);
poly.f[1].v.push_back(NumVertices()-1-i);
}
return poly;
}
Vector3 Polygon::ClosestPoint(const LineSegment &lineSegment) const
{
return ClosestPoint(lineSegment, 0);

View File

@@ -1,13 +1,17 @@
#include <J3ML/Geometry/Polyhedron.h>
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/Triangle.h>
#include <J3ML/Geometry/LineSegment.h>
#include "J3ML/Geometry/Ray.h"
#include "J3ML/Geometry/Line.h"
#include <J3ML/Geometry/Polygon.h>
#include <J3ML/Geometry/Capsule.h>
#include <J3ML/Geometry/Polyhedron.hpp>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/Triangle.hpp>
#include <J3ML/Geometry/LineSegment.hpp>
#include "J3ML/Geometry/Ray.hpp"
#include "J3ML/Geometry/Line.hpp"
#include <J3ML/Geometry/Polygon.hpp>
#include <J3ML/Geometry/Capsule.hpp>
#include <set>
#include <cfloat>
#include <J3ML/Geometry/Frustum.hpp>
#include <J3ML/Geometry/OBB.hpp>
#include <J3ML/Geometry/Plane.hpp>
#include <J3ML/Geometry/Sphere.hpp>
namespace J3ML::Geometry
{
@@ -159,7 +163,7 @@ namespace J3ML::Geometry
Vector3 Dir = ((Vector3)v[f[j].v[0]] + (Vector3)v[f[j].v[1]] + (Vector3)v[f[j].v[2]]) * 0.333333333333f - point;
//if (Dir.Normalize() <= 0.f)
//if (Dir.Normalized() <= 0.f)
//continue;
Ray r(Vector3(), Dir);
@@ -368,7 +372,7 @@ namespace J3ML::Geometry
Vector4 b = Vector4(poly.v[face.v[1]], 1.f);
Vector4 c = Vector4(poly.v[face.v[2]], 1.f);
Vector4 normal = (b-a).Cross(c-a);
normal.Normalize();
normal.Normalized();
return normal;
// return ((vec)v[face.v[1]]-(vec)v[face.v[0]]).Cross((vec)v[face.v[2]]-(vec)v[face.v[0]]).Normalized();
}
@@ -386,7 +390,7 @@ namespace J3ML::Geometry
normal.z += (double(poly.v[v0].x) - poly.v[v1].x) * (double(poly.v[v0].y) + poly.v[v1].y); // Project on xy
v0 = v1;
}
normal.Normalize();
normal.Normalized();
return normal;
#if 0
cv bestNormal;
@@ -397,7 +401,7 @@ namespace J3ML::Geometry
{
cv c = poly.v[face.v[i]];
cv normal = (c-b).Cross(a-b);
float len = normal.Normalize();
float len = normal.Normalized();
if (len > bestLen)
{
bestLen = len;
@@ -441,7 +445,7 @@ namespace J3ML::Geometry
cv edge = cv(poly.v[i]) - cv(poly.v[bestV0]);
edge.Normalize();
cv normal = bestEdge.Cross(edge);
cs len = normal.Normalize();
cs len = normal.Normalized();
if (len > bestLen)
{
bestLen = len;
@@ -508,6 +512,8 @@ namespace J3ML::Geometry
return true;
}
bool Polyhedron::EulerFormulaHolds() const { return NumVertices() + NumFaces() - NumEdges() == 2;}
bool Polyhedron::ContainsConvex(const Vector3 &point, float epsilon) const
{
assert(IsConvex());
@@ -638,6 +644,64 @@ namespace J3ML::Geometry
return false;
}
template <typename T>
bool PolyhedronIntersectsAABB_OBB(const Polyhedron& p, const T& obj) {
if (p.Contains(obj.CenterPoint()))
return true;
if (obj.Contains(p.ApproximateConvexCentroid())) // @bug: This is not correct for concave polyhedrons!
return true;
// Test for each edge of the AABB / OBB whether this polyhedron intersects it.
for(int i = 0; i < obj.NumEdges(); ++i)
if (p.Intersects(obj.Edge(i)))
return true;
// Test for each edge of this polyhedron whether the AABB / OBB intersects it.
for(size_t i = 0; i < p.f.size(); ++i)
{
assert(!p.f[i].v.empty()); // Cannot have degenerate faces here, and for performance reasons, don't start checking for this condition in release mode!
int v0 = p.f[i].v.back();
Vector3 l0 = p.v[v0];
for(size_t j = 0; j < p.f[i].v.size(); ++j)
{
int v1 = p.f[i].v[j];
Vector3 l1 = p.v[v1];
if (v0 < v1 && obj.Intersects(LineSegment(l0, l1))) // If v0 < v1, then this line segment is the canonical one.
return true;
l0 = l1;
v0 = v1;
}
}
return false;
}
bool Polyhedron::Intersects(const AABB &aabb) const {
return PolyhedronIntersectsAABB_OBB(*this, aabb);
}
bool Polyhedron::Intersects(const OBB& obb) const {
return PolyhedronIntersectsAABB_OBB(*this, obb);
}
bool Polyhedron::Intersects(const Frustum& obb) const {
return PolyhedronIntersectsAABB_OBB(*this, obb);
}
bool Polyhedron::Intersects(const Triangle& obb) const {
return PolyhedronIntersectsAABB_OBB(*this, obb);
}
bool Polyhedron::Intersects(const Sphere& sphere) const {
Vector3 closestPt = ClosestPoint(sphere.Position);
return closestPt.DistanceSq(sphere.Position) <= sphere.Radius * sphere.Radius;
}
bool Polyhedron::Intersects(const Polygon& polygon) const {
return Intersects(polygon.ToPolyhedron());
}
bool Polyhedron::Intersects(const Capsule &capsule) const {
Vector3 pt, ptOnLineSegment;
pt = ClosestPoint(capsule.l, &ptOnLineSegment);
@@ -677,6 +741,22 @@ namespace J3ML::Geometry
return closestPt;
}
Vector3 Polyhedron::ClosestPoint(const Vector3 &point) const {
if (Contains(point))
return point;
Vector3 closestPoint = Vector3::NaN;
float closestDistance = Math::Infinity;
for (int i = 0; i < NumFaces(); ++i) {
Vector3 closestOnPoly = FacePolygon(i).ClosestPoint(point);
float d = closestOnPoly.DistanceSq(point);
if (d < closestDistance) {
closestPoint = closestOnPoly;
closestDistance = d;
}
}
return closestPoint;
}
}

View File

@@ -1,4 +1,4 @@
#include <J3ML/Geometry/QuadTree.h>
#include <J3ML/Geometry/QuadTree.hpp>
namespace Geometry
{

View File

@@ -1,6 +1,10 @@
#include <J3ML/Geometry/Ray.h>
#include <J3ML/Geometry/Sphere.h>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/Line.hpp>
#include <J3ML/Geometry/LineSegment.hpp>
#include <J3ML/Geometry/Plane.hpp>
#include <J3ML/Geometry/Ray.hpp>
#include <J3ML/Geometry/Sphere.hpp>
#include <ostream>
namespace J3ML::Geometry
{
@@ -17,6 +21,17 @@ namespace J3ML::Geometry
outMin = -INFINITY;
}
std::string Ray::ToString() const
{
return std::format("Ray(origin:[{}], direction:[{}])", Origin.ToString(), Direction.ToString());
}
std::ostream& operator<<(std::ostream& o, const Ray& ray)
{
o << ray.ToString();
return o;
}
RaycastResult Ray::Cast(const Sphere &target, float maxDistance)
{
Vector3 p0 = this->Origin;
@@ -54,7 +69,6 @@ namespace J3ML::Geometry
intersection,
normal,
true,
(Shape *) &target
};
}
@@ -101,7 +115,6 @@ namespace J3ML::Geometry
intersection,
normal,
true,
(Shape*)&target
};
}
@@ -129,7 +142,6 @@ namespace J3ML::Geometry
intersection,
normal,
true,
(Shape*) &target
};
return RaycastResult::NoHit();
@@ -199,7 +211,7 @@ namespace J3ML::Geometry
}
Vector3 Ray::GetPoint(float distance) const {
assert(Direction.IsNormalized());
//assert(Direction.IsNormalized());
return Origin + distance * Direction;
}
}

View File

@@ -1,4 +1,6 @@
#include <J3ML/Geometry/Sphere.h>
#include <J3ML/Geometry/LineSegment.hpp>
#include <J3ML/Geometry/Sphere.hpp>
#include <J3ML/Geometry/TriangleMesh.hpp>
namespace J3ML::Geometry
{
@@ -7,6 +9,107 @@ namespace J3ML::Geometry
return Contains(lineseg.A) && Contains(lineseg.B);
}
TriangleMesh Sphere::GenerateUVSphere(int subdivisions) const
{
// http://www.songho.ca/opengl/gl_sphere.html
TriangleMesh mesh;
float x, y, z, xy; // Vertex Position
float nx, ny, nz, lengthInv = 1.f / Radius; // Vertex Normal
float s, t; // Vertex TexCoord
int sectorCount = subdivisions;
int stackCount = subdivisions;
float sectorStep = 2.f * Math::Pi / sectorCount;
float stackStep = Math::Pi / stackCount;
float sectorAngle, stackAngle;
for (int i = 0; i <= stackCount; ++i)
{
stackAngle = Math::Pi / 2.f - i * stackStep; // starting from pi/2 to -pi/2
xy = Radius * Math::Cos(stackAngle); // r * cos(u)
z = Radius * Math::Sin(stackAngle); // r * sin(u)
// add (sectorCount + 1) vertices per stack
// first and last vertices have same position and normal, but different tex coords
for (int j = 0; j <= sectorCount; ++j)
{
sectorAngle = j * sectorStep; // starting from 0 to 2pi
// vertex position (x, y, z)
x = xy * Math::Cos(sectorAngle);
y = xy * Math::Sin(sectorAngle);
Vector3 vertex = {x, y, z};
mesh.Vertices.push_back(vertex);
// normalized vertex normal (nx, ny, nz)
nx = x * lengthInv;
ny = y * lengthInv;
nz = z * lengthInv;
Vector3 normal = {nx, ny, nz};
mesh.Normals.push_back(normal);
// vertex tex coord (s, t) range between [0, 1]
s = (float)j / sectorCount;
t = (float)i / stackCount;
Vector2 TexCoords = {s, t};
mesh.UVs.push_back(normal);
}
}
return mesh;
}
TriangleMesh Sphere::GenerateIcososphere() const
{
// Generate 12 vertices of an icosahedron for a given radius.
const float h_angle = Math::Pi / 180.f * 72.f; // 72 degree = 360 / 5;
const float v_angle = Math::Atan(1.f / 2.f);
TriangleMesh mesh;
int i1, i2;
float z, xy;
float hAngle1 = -Math::Pi / 2.f - h_angle / 2.f;
float hAngle2 = -Math::Pi / 2;
// the first top vertex at (0,0,r)
Vector3 top_vertex = {0, 0, Radius};
// compute 10 vertices at 1st and 2nd rows
for (int i = 1; i <= 5; ++i)
{
i1 = i * 3; // index for 1st row
i2 = (i + 5) * 3; // index for 2nd row
z = Radius * Math::Sin(v_angle); // elevation
xy = Radius * Math::Cos(v_angle); // length on XY plane
Vector3 vert_0 = {xy * Math::Cos(hAngle1), xy * Math::Sin(hAngle1), z};
Vector3 vert_1 = {xy * Math::Cos(hAngle2), xy * Math::Sin(hAngle2), -z};
// next horizontal angles
hAngle1 += h_angle;
hAngle2 += h_angle;
}
// the last bottom vertex at (0, 0, -r)
i1 = 11 * 3;
Vector3 last_vertex = {0,0, -Radius};
return mesh;
}
void Sphere::ProjectToAxis(const Vector3 &direction, float &outMin, float &outMax) const
{
float d = Vector3::Dot(direction, Position);
@@ -43,11 +146,11 @@ namespace J3ML::Geometry
}
float Sphere::Volume() const {
return 4.f * M_PI * Cube(Radius) / 3.f;
return 4.f * Math::Pi * Cube(Radius) / 3.f;
}
float Sphere::SurfaceArea() const {
return 4.f * M_PI * Cube(Radius) / 3.f;
return 4.f * Math::Pi * Cube(Radius) / 3.f;
}
bool Sphere::IsFinite() const {
@@ -108,4 +211,4 @@ namespace J3ML::Geometry
}
}
}

View File

@@ -1,12 +1,30 @@
#include <J3ML/Geometry/Triangle.h>
#include <J3ML/LinearAlgebra.h>
#include <J3ML/Geometry/AABB.h>
#include <J3ML/Geometry/LineSegment.h>
#include <J3ML/Geometry/Line.h>
#include <J3ML/Geometry/Capsule.h>
#include <J3ML/Geometry/Triangle.hpp>
#include <J3ML/LinearAlgebra.hpp>
#include <J3ML/Geometry/AABB.hpp>
#include <J3ML/Geometry/LineSegment.hpp>
#include <J3ML/Geometry/Line.hpp>
#include <J3ML/Geometry/Capsule.hpp>
#include <J3ML/Geometry/Plane.hpp>
namespace J3ML::Geometry
{
bool Triangle::Intersects(const LineSegment &l, float *d, Vector3 *intersectionPoint) const {
/** The Triangle-Line/LineSegment/Ray intersection tests are based on M&ouml;ller-Trumbore method:
"T. M&ouml;ller, B. Trumbore. Fast, Minimum Storage Ray/Triangle Intersection. 2005."
http://jgt.akpeters.com/papers/MollerTrumbore97/. */
float u,v;
float t = IntersectLineTri(l.A, l.B - l.A, V0, V1, V2, u, v);
bool success = (t >= 0.f && t <= 1.f);
if (!success)
return false;
if (d)
*d = t;
if (intersectionPoint)
*intersectionPoint = l.GetPoint(t);
return true;
}
Interval Triangle::ProjectionInterval(const Vector3& axis) const {
// https://gdbooks.gitbooks.io/3dcollisions/content/Chapter4/generic_sat.html
float min = axis.Dot(V0);
@@ -27,10 +45,9 @@ namespace J3ML::Geometry
}
Triangle Triangle::Translated(const Vector3& translation) const {
return {
V0 + translation,
V1 + translation,
V2 + translation
return {V0 + translation,
V1 + translation,
V2 + translation
};
}
@@ -105,6 +122,12 @@ namespace J3ML::Geometry
return aabb;
}
Vector3 Triangle::Centroid() const {
return (V0 + V1 + V2) * (1.f/3.f);
}
Vector3 Triangle::CenterPoint() const { return Centroid();}
bool Triangle::Intersects(const AABB &aabb) const {
return aabb.Intersects(*this);
}

View File

@@ -0,0 +1,136 @@
#include <J3ML/Geometry/Triangle2D.hpp>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <J3ML/LinearAlgebra/Matrix3x3.hpp>
#include <cfloat>
namespace J3ML::Geometry
{
Triangle2D::Triangle2D(const Vector2& a, const Vector2& b, const Vector2& c) : A(a), B(b), C(c) {}
void Triangle2D::Translate(const Vector2& offset)
{
A += offset;
B += offset;
C += offset;
}
void Triangle2D::Transform(const Matrix3x3& transform)
{
A = Mul2D(transform, A);
B = Mul2D(transform, B);
C = Mul2D(transform, C);
}
void Triangle2D::Transform(const Matrix4x4& transform)
{
A = MulPos2D(transform, A);
B = MulPos2D(transform, B);
C = MulPos2D(transform, C);
}
/// Implementation from Crister Ericson's Real-Time Collision Detection, pp. 51-52.
inline float TriArea2D(float x1, float y1, float x2, float y2, float x3, float y3)
{
return (x1-x2)*(y2-y3) - (x2-x3)*(y1-y2);
}
Vector3 Triangle2D::BarycentricUVW(const Vector2& point) const
{
// Implementation from Crister Ericson's Real-Time Collision Detection, pp. 51-52, adapted for 2D.
float nu, nv;
nu = TriArea2D(point.x, point.y, B.x, B.y, C.x, C.y);
nv = TriArea2D(point.x, point.y, C.x, C.y, A.x, A.y);
float u = nu;
float v = nv;
float w = 1.f - u - v;
return Vector3(u,v,w);
}
Vector2 Triangle2D::BarycentricUV(const Vector2& point) const
{
Vector3 uvw = BarycentricUVW(point);
return Vector2(uvw.y, uvw.z);
}
bool Triangle2D::BarycentricInsideTriangle(const Vector3& barycentric)
{
return barycentric.x >= 0.f && barycentric.y >= 0 && barycentric.z >= 0.f &&
Math::EqualAbs(barycentric.x + barycentric.y + barycentric.z, 1.f);
}
Vector2 Triangle2D::Point(float u, float v) const
{
// In case the triangle is far away from the origin but is small, the elements of 'a' will have large magnitudes,
// and the elements of (b-a) and (c-a) will be much smaller quantities. Therefore, be extra careful with the
// parenthesis and first sum the small floats together before adding it to the large one.
return A + ((B-A) * u + (C-A) * v);
}
Vector2 Triangle2D::Point(float u, float v, float w) const
{
return u * A + v * B + w * C;
}
Vector2 Triangle2D::Point(const Vector3 &uvw) const
{
return Point(uvw.x, uvw.y, uvw.z);
}
Vector2 Triangle2D::Point(const Vector2 &uv) const
{
return Point(uv.x, uv.y);
}
Vector2 Triangle2D::Centroid() const
{
return (A + B + C) * (1.f/3.f);
}
float Triangle2D::Perimeter() const
{
return A.Distance(B) + B.Distance(C) + C.Distance(A);
}
Vector2 Triangle2D::Vertex(int i) const
{
assert(0 <= i);
assert(i <= 2);
if (i == 0)
return A;
else if (i == 1)
return B;
else if (i == 2)
return C;
else
return Vector2::NaN;
}
Vector2 Triangle2D::ExtremePoint(const Vector2& direction) const
{
Vector2 mostExtreme = Vector2::NaN;
float mostExtremeDist = -FLT_MAX;
for (int i = 0; i < 3; ++i)
{
Vector2 pt = Vertex(i);
float d = Vector2::Dot(direction, pt);
if (d > mostExtremeDist)
{
mostExtremeDist = d;
mostExtreme = pt;
}
}
return mostExtreme;
}
//TODO must return on MSVC
/*
Vector2 Triangle2D::ExtremePoint(const Vector2& direction, float& projectionDistance) const
{
Vector2 extremePoint = ExtremePoint(direction);
projectionDistance = extremePoint.Dot(direction);
}
*/
}

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