Move Vectormath main
C++20 game and graphics mathematics
Loading...
Searching...
No Matches
Projection.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <optional>
5#include <type_traits>
6
7#include <mv/math/Angles.hpp>
8#include <mv/math/Mat4.hpp>
9
10namespace mv::math
11{
12 enum class Handedness
13 {
14 Left,
15 Right
16 };
17
18 enum class ClipDepth
19 {
22 };
23
24 enum class DepthDirection
25 {
26 Forward,
28 };
29
30 namespace detail
31 {
32 template <typename T>
33 [[nodiscard]] constexpr T NearClipDepth(
35 {
37 {
38 return T(1);
39 }
40 return clipDepth == ClipDepth::ZeroToOne ? T(0) : T(-1);
41 }
42
43 template <typename T>
44 [[nodiscard]] constexpr T FarClipDepth(
46 {
48 {
49 return T(1);
50 }
51 return clipDepth == ClipDepth::ZeroToOne ? T(0) : T(-1);
52 }
53 } // namespace detail
54
55 // Perspective construction is derived from the required NDC values at
56 // the near and far planes. The resulting convention variants were
57 // cross-checked against DirectXMath XMMatrixPerspectiveFovLH/RH (MIT) and
58 // GLM matrix_clip_space.inl perspective functions (MIT).
59 template <typename T>
60 requires std::is_floating_point_v<T>
61 [[nodiscard]] inline std::optional<Mat4<T>> TryPerspectiveFov(
65 T farPlane,
69 {
71 constexpr T Pi = static_cast<T>(3.14159265358979323846264338327950288L);
72 if (!(fieldOfView > T(0) && fieldOfView < Pi) ||
73 !(aspectRatio > T(0)) || !(nearPlane > T(0)) ||
74 !(farPlane > nearPlane) || !std::isfinite(fieldOfView) ||
75 !std::isfinite(aspectRatio) || !std::isfinite(nearPlane) ||
76 !std::isfinite(farPlane))
77 {
78 return std::nullopt;
79 }
80
81 const T halfTangent = std::tan(fieldOfView * T(0.5));
82 if (!(halfTangent > T(0)) || !std::isfinite(halfTangent))
83 {
84 return std::nullopt;
85 }
86
87 const T yScale = T(1) / halfTangent;
88 const T xScale = yScale / aspectRatio;
89 const T nearDepth = detail::NearClipDepth<T>(clipDepth, depthDirection);
90 const T farDepth = detail::FarClipDepth<T>(clipDepth, depthDirection);
91 const T inverseDistanceDifference = T(1) / nearPlane - T(1) / farPlane;
92 const T translation =
95 const T handednessSign = handedness == Handedness::Left ? T(1) : T(-1);
97
98 const Mat4<T> result(xScale, T(0), T(0), T(0), T(0), yScale, T(0), T(0),
99 T(0), T(0), depthScale, handednessSign, T(0), T(0),
100 translation, T(0));
101 return result.IsFinite() ? std::optional<Mat4<T>>(result)
102 : std::nullopt;
103 }
104
105 // Infinite-far perspective is the analytic farPlane -> infinity limit of
106 // TryPerspectiveFov, avoiding a large finite sentinel and its lost depth
107 // precision.
108 template <typename T>
109 requires std::is_floating_point_v<T>
110 [[nodiscard]] inline std::optional<Mat4<T>> TryPerspectiveFovInfinite(
113 T nearPlane,
117 {
119 constexpr T Pi = static_cast<T>(3.14159265358979323846264338327950288L);
120 if (!(fieldOfView > T(0) && fieldOfView < Pi) ||
121 !(aspectRatio > T(0)) || !(nearPlane > T(0)) ||
122 !std::isfinite(fieldOfView) || !std::isfinite(aspectRatio) ||
123 !std::isfinite(nearPlane))
124 {
125 return std::nullopt;
126 }
127
128 const T halfTangent = std::tan(fieldOfView * T(0.5));
129 if (!(halfTangent > T(0)) || !std::isfinite(halfTangent))
130 {
131 return std::nullopt;
132 }
133
134 const T yScale = T(1) / halfTangent;
135 const T xScale = yScale / aspectRatio;
136 const T nearDepth = detail::NearClipDepth<T>(clipDepth, depthDirection);
137 const T farDepth = detail::FarClipDepth<T>(clipDepth, depthDirection);
139 const T handednessSign = handedness == Handedness::Left ? T(1) : T(-1);
141
142 const Mat4<T> result(xScale, T(0), T(0), T(0), T(0), yScale, T(0), T(0),
143 T(0), T(0), depthScale, handednessSign, T(0), T(0),
144 translation, T(0));
145 return result.IsFinite() ? std::optional<Mat4<T>>(result)
146 : std::nullopt;
147 }
148
149 // Orthographic construction maps physical camera-space distances to the
150 // requested NDC interval. Variants were cross-checked against DirectXMath
151 // XMMatrixOrthographicOffCenterLH/RH (MIT) and GLM matrix_clip_space.inl
152 // orthographic functions (MIT).
153 template <typename T>
154 requires std::is_floating_point_v<T>
155 [[nodiscard]] inline std::optional<Mat4<T>> TryOrthographicOffCenter(
156 T left,
157 T right,
158 T bottom,
159 T top,
160 T nearPlane,
161 T farPlane,
165 {
166 if (right == left || top == bottom || !(farPlane > nearPlane) ||
167 !std::isfinite(left) || !std::isfinite(right) ||
168 !std::isfinite(bottom) || !std::isfinite(top) ||
169 !std::isfinite(nearPlane) || !std::isfinite(farPlane))
170 {
171 return std::nullopt;
172 }
173
174 const T nearDepth = detail::NearClipDepth<T>(clipDepth, depthDirection);
175 const T farDepth = detail::FarClipDepth<T>(clipDepth, depthDirection);
176 const T handednessSign = handedness == Handedness::Left ? T(1) : T(-1);
177 const T depthPerDistance =
181
182 const T inverseWidth = T(1) / (right - left);
183 const T inverseHeight = T(1) / (top - bottom);
184 const Mat4<T> result(T(2) * inverseWidth, T(0), T(0), T(0), T(0),
185 T(2) * inverseHeight, T(0), T(0), T(0), T(0),
186 depthScale, T(0), -(right + left) * inverseWidth,
188 T(1));
189 return result.IsFinite() ? std::optional<Mat4<T>>(result)
190 : std::nullopt;
191 }
192} // namespace mv::math
constexpr T Value() const noexcept
Definition Angles.hpp:18
constexpr T FarClipDepth(ClipDepth clipDepth, DepthDirection depthDirection) noexcept
constexpr T NearClipDepth(ClipDepth clipDepth, DepthDirection depthDirection) noexcept
std::optional< Mat4< T > > TryPerspectiveFov(Radians< T > verticalFieldOfView, T aspectRatio, T nearPlane, T farPlane, Handedness handedness, ClipDepth clipDepth, DepthDirection depthDirection) noexcept
std::optional< Mat4< T > > TryOrthographicOffCenter(T left, T right, T bottom, T top, T nearPlane, T farPlane, Handedness handedness, ClipDepth clipDepth, DepthDirection depthDirection) noexcept
constexpr T Pi
Definition Constants.hpp:9
std::optional< Mat4< T > > TryPerspectiveFovInfinite(Radians< T > verticalFieldOfView, T aspectRatio, T nearPlane, Handedness handedness, ClipDepth clipDepth, DepthDirection depthDirection) noexcept