Move Vectormath main
C++20 game and graphics mathematics
Loading...
Searching...
No Matches
Project.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/Mat4.hpp>
8#include <mv/math/Vec2.hpp>
11
12namespace mv::math
13{
14 enum class ViewportYAxis
15 {
16 Up,
17 Down
18 };
19
20 template <typename T>
21 requires std::is_floating_point_v<T>
23 {
24 public:
25 [[nodiscard]] static std::optional<Viewport> TryFrom(
26 T x,
27 T y,
28 T width,
29 T height,
30 T minimumDepth = T(0),
32 {
33 if (!(width > T(0)) || !(height > T(0)) ||
34 !(maximumDepth > minimumDepth) || !std::isfinite(x) ||
35 !std::isfinite(y) || !std::isfinite(width) ||
36 !std::isfinite(height) || !std::isfinite(minimumDepth) ||
37 !std::isfinite(maximumDepth))
38 {
39 return std::nullopt;
40 }
42 UncheckedTag{});
43 }
44
46 {
47 return X_;
48 }
50 {
51 return Y_;
52 }
54 {
55 return Width_;
56 }
58 {
59 return Height_;
60 }
62 {
63 return MinimumDepth_;
64 }
66 {
67 return MaximumDepth_;
68 }
69
70 private:
71 struct UncheckedTag
72 {
73 };
74
75 Viewport(T x,
76 T y,
77 T width,
78 T height,
79 T minimumDepth,
80 T maximumDepth,
81 UncheckedTag) noexcept :
82 X_(x),
83 Y_(y),
84 Width_(width),
85 Height_(height),
86 MinimumDepth_(minimumDepth),
87 MaximumDepth_(maximumDepth)
88 {
89 }
90
91 T X_;
92 T Y_;
93 T Width_;
94 T Height_;
95 T MinimumDepth_;
96 T MaximumDepth_;
97 };
98
101
102 template <typename T>
103 [[nodiscard]] inline std::optional<Point3<T>> TryProjectPoint(
104 const Mat4<T>& worldToClip, const Point3<T>& worldPoint) noexcept
105 {
106 // Homogeneous projection follows the OpenGL/Direct3D pipeline and is
107 // cross-checked against GLM project/unProject and DirectXMath
108 // XMVector3Project/Unproject (both MIT). W failure is explicit.
109 const Vec4<T> clip =
110 Vec4<T>(worldPoint.X(), worldPoint.Y(), worldPoint.Z(), T(1)) *
112 if (clip.W() == T(0) || !std::isfinite(clip.W()))
113 {
114 return std::nullopt;
115 }
116 const Point3<T> result(clip.X() / clip.W(), clip.Y() / clip.W(),
117 clip.Z() / clip.W());
118 return std::isfinite(result.X()) && std::isfinite(result.Y()) &&
119 std::isfinite(result.Z())
120 ? std::optional<Point3<T>>(result)
121 : std::nullopt;
122 }
123
124 template <typename T>
125 [[nodiscard]] inline std::optional<Point3<T>> TryUnprojectPointWithInverse(
126 const Mat4<T>& clipToWorld,
127 const Point3<T>& normalizedDevicePoint) noexcept
128 {
129 const Vec4<T> world =
131 normalizedDevicePoint.Z(), T(1)) *
133 if (world.W() == T(0) || !std::isfinite(world.W()))
134 {
135 return std::nullopt;
136 }
137 const Point3<T> result(world.X() / world.W(), world.Y() / world.W(),
138 world.Z() / world.W());
139 return std::isfinite(result.X()) && std::isfinite(result.Y()) &&
140 std::isfinite(result.Z())
141 ? std::optional<Point3<T>>(result)
142 : std::nullopt;
143 }
144
145 template <typename T>
146 [[nodiscard]] inline std::optional<Point3<T>> TryUnprojectPoint(
147 const Mat4<T>& worldToClip,
148 const Point3<T>& normalizedDevicePoint) noexcept
149 {
150 const auto inverse = worldToClip.TryInverse();
153 : std::nullopt;
154 }
155
156 template <typename T>
157 [[nodiscard]] inline std::optional<Point3<T>> TryProjectToViewport(
158 const Mat4<T>& worldToClip,
159 const Point3<T>& worldPoint,
160 const Viewport<T>& viewport,
163 {
165 if (!ndc)
166 {
167 return std::nullopt;
168 }
169 const T normalizedX = ndc->X() * T(0.5) + T(0.5);
171 ? T(0.5) - ndc->Y() * T(0.5)
172 : ndc->Y() * T(0.5) + T(0.5);
174 ? ndc->Z()
175 : ndc->Z() * T(0.5) + T(0.5);
176 return Point3<T>(viewport.X() + normalizedX * viewport.Width(),
177 viewport.Y() + normalizedY * viewport.Height(),
178 viewport.MinimumDepth() +
179 normalizedDepth * (viewport.MaximumDepth() -
180 viewport.MinimumDepth()));
181 }
182
183 template <typename T>
184 [[nodiscard]] inline std::optional<Point3<T>>
186 const Mat4<T>& clipToWorld,
188 const Viewport<T>& viewport,
191 {
192 const T normalizedX =
193 (viewportPoint.X() - viewport.X()) / viewport.Width();
194 const T normalizedY =
195 (viewportPoint.Y() - viewport.Y()) / viewport.Height();
196 const T normalizedDepth =
197 (viewportPoint.Z() - viewport.MinimumDepth()) /
198 (viewport.MaximumDepth() - viewport.MinimumDepth());
199 const T ndcX = normalizedX * T(2) - T(1);
200 const T ndcY = yAxis == ViewportYAxis::Down ? T(1) - normalizedY * T(2)
201 : normalizedY * T(2) - T(1);
204 : normalizedDepth * T(2) - T(1);
207 }
208
209 template <typename T>
210 [[nodiscard]] inline std::optional<Ray3<T>> TryMakeViewportRay(
211 const Mat4<T>& clipToWorld,
213 const Viewport<T>& viewport,
217 {
219 ? viewport.MinimumDepth()
220 : viewport.MaximumDepth();
222 ? viewport.MaximumDepth()
223 : viewport.MinimumDepth();
224 const T sampleDepth = nearDepth + (farDepth - nearDepth) * T(0.5);
233 if (!nearPoint || !samplePoint)
234 {
235 return std::nullopt;
236 }
237 const auto direction =
239 return direction
241 : std::nullopt;
242 }
243
244 template <typename T>
245 [[nodiscard]] inline std::optional<Ray3<T>> TryMakePerspectiveViewportRay(
246 const Mat4<T>& clipToWorld,
247 const Point3<T>& cameraOrigin,
249 const Viewport<T>& viewport,
253 {
254 const auto nearRay =
257 if (!nearRay)
258 {
259 return std::nullopt;
260 }
261 const auto direction =
263 return direction
265 : std::nullopt;
266 }
267} // namespace mv::math
static std::optional< Direction3 > TryFrom(const Vec3< T > &value) noexcept
static std::optional< Ray3 > TryFromOriginDirection(const Point3< T > &origin, const Direction3< T > &direction) noexcept
Definition Ray3.hpp:23
T X() const noexcept
Definition Project.hpp:45
T Width() const noexcept
Definition Project.hpp:53
T MaximumDepth() const noexcept
Definition Project.hpp:65
T Height() const noexcept
Definition Project.hpp:57
static std::optional< Viewport > TryFrom(T x, T y, T width, T height, T minimumDepth=T(0), T maximumDepth=T(1)) noexcept
Definition Project.hpp:25
T Y() const noexcept
Definition Project.hpp:49
T MinimumDepth() const noexcept
Definition Project.hpp:61
std::optional< Ray3< T > > TryMakeViewportRay(const Mat4< T > &clipToWorld, const Vec2< T > &viewportPosition, const Viewport< T > &viewport, ClipDepth clipDepth, DepthDirection depthDirection, ViewportYAxis yAxis=ViewportYAxis::Down) noexcept
Definition Project.hpp:210
std::optional< Point3< T > > TryUnprojectFromViewportWithInverse(const Mat4< T > &clipToWorld, const Point3< T > &viewportPoint, const Viewport< T > &viewport, ClipDepth clipDepth, ViewportYAxis yAxis=ViewportYAxis::Down) noexcept
Definition Project.hpp:185
std::optional< Point3< T > > TryProjectPoint(const Mat4< T > &worldToClip, const Point3< T > &worldPoint) noexcept
Definition Project.hpp:103
std::optional< Point3< T > > TryUnprojectPoint(const Mat4< T > &worldToClip, const Point3< T > &normalizedDevicePoint) noexcept
Definition Project.hpp:146
std::optional< Ray3< T > > TryMakePerspectiveViewportRay(const Mat4< T > &clipToWorld, const Point3< T > &cameraOrigin, const Vec2< T > &viewportPosition, const Viewport< T > &viewport, ClipDepth clipDepth, DepthDirection depthDirection, ViewportYAxis yAxis=ViewportYAxis::Down) noexcept
Definition Project.hpp:245
std::optional< Point3< T > > TryUnprojectPointWithInverse(const Mat4< T > &clipToWorld, const Point3< T > &normalizedDevicePoint) noexcept
Definition Project.hpp:125
std::optional< Point3< T > > TryProjectToViewport(const Mat4< T > &worldToClip, const Point3< T > &worldPoint, const Viewport< T > &viewport, ClipDepth clipDepth, ViewportYAxis yAxis=ViewportYAxis::Down) noexcept
Definition Project.hpp:157