Skyscraper 2.0
XrMath.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#pragma once
5
6#include <openxr/openxr.h>
7#include <DirectXMath.h>
8#include <stdexcept>
9#include <cmath>
10
11namespace xr::math {
12 constexpr float QuaternionEpsilon = 0.01f;
13
14 // A large number that can be used as maximum finite depth value, beyond which a value can be treated as infinity
15 constexpr float OneOverFloatEpsilon = 1.0f / std::numeric_limits<float>::epsilon();
16
17 namespace Pose {
18 constexpr XrPosef Identity();
19 constexpr XrPosef Translation(const XrVector3f& translation);
20
21 XrPosef LookAt(const XrVector3f& origin, const XrVector3f& forward, const XrVector3f& up);
22 XrPosef Multiply(const XrPosef& a, const XrPosef& b);
23 XrPosef Slerp(const XrPosef& a, const XrPosef& b, float alpha);
24 XrPosef Invert(const XrPosef& pose);
25
26 constexpr bool IsPoseValid(const XrSpaceLocation& location);
27 constexpr bool IsPoseTracked(const XrSpaceLocation& location);
28 constexpr bool IsPoseValid(const XrHandJointLocationEXT& jointLocation);
29 constexpr bool IsPoseTracked(const XrHandJointLocationEXT& jointLocation);
30 constexpr bool IsPoseValid(const XrViewState& viewState);
31 constexpr bool IsPoseTracked(const XrViewState& viewState);
32
33 template <typename Quaternion, typename Vector3>
34 inline XrPosef MakePose(const Quaternion& orientation, const Vector3& position);
35 } // namespace Pose
36
37 namespace Quaternion {
38 constexpr XrQuaternionf Identity();
39 bool IsNormalized(const XrQuaternionf& quaternion);
40 XrQuaternionf RotationAxisAngle(const XrVector3f& axis, float angleInRadians);
41 XrQuaternionf RotationRollPitchYaw(const XrVector3f& eulerAnglesInRadians);
42 XrQuaternionf Slerp(const XrQuaternionf& a, const XrQuaternionf& b, float alpha);
43 } // namespace Quaternion
44
45 struct NearFar {
46 float Near;
47 float Far;
48 };
49
51 XrPosef Pose;
52 XrFovf Fov;
54 };
55
56 // Type conversion between math types
57 template <typename X, typename Y>
58 constexpr const X& cast(const Y& value) = delete;
59
60 // Convert XR types to DX
61 DirectX::XMVECTOR XM_CALLCONV LoadXrVector2(const XrVector2f& vector);
62 DirectX::XMVECTOR XM_CALLCONV LoadXrVector3(const XrVector3f& vector);
63 DirectX::XMVECTOR XM_CALLCONV LoadXrVector4(const XrVector4f& vector);
64 DirectX::XMVECTOR XM_CALLCONV LoadXrQuaternion(const XrQuaternionf& quaternion);
65 DirectX::XMMATRIX XM_CALLCONV LoadXrPose(const XrPosef& rigidTransform);
66 DirectX::XMMATRIX XM_CALLCONV LoadInvertedXrPose(const XrPosef& rigidTransform);
67 DirectX::XMVECTOR XM_CALLCONV LoadXrExtent(const XrExtent2Df& extend);
68
69 // Convert DX types to XR
70 void XM_CALLCONV StoreXrVector2(XrVector2f* outVec, DirectX::FXMVECTOR inVec);
71 void XM_CALLCONV StoreXrVector3(XrVector3f* outVec, DirectX::FXMVECTOR inVec);
72 void XM_CALLCONV StoreXrVector4(XrVector4f* outVec, DirectX::FXMVECTOR inVec);
73 void XM_CALLCONV StoreXrQuaternion(XrQuaternionf* outQuat, DirectX::FXMVECTOR inQuat);
74 bool XM_CALLCONV StoreXrPose(XrPosef* out, DirectX::FXMMATRIX matrix);
75 void XM_CALLCONV StoreXrExtent(XrExtent2Df* extend, DirectX::FXMVECTOR inVec);
76
77 // Projection matrix math
78 DirectX::XMMATRIX ComposeProjectionMatrix(const XrFovf& fov, const NearFar& nearFar);
79 NearFar GetProjectionNearFar(const DirectX::XMFLOAT4X4& projectionMatrix);
80 XrFovf DecomposeProjectionMatrix(const DirectX::XMFLOAT4X4& projectionMatrix);
81} // namespace xr::math
82
83#pragma region Implementation
84
85namespace xr::math {
86 namespace detail {
87 template <typename X, typename Y>
88 constexpr const X& implement_math_cast(const Y& value) {
89 static_assert(std::is_trivially_copyable<X>::value, "Unsafe to cast between non-POD types.");
90 static_assert(std::is_trivially_copyable<Y>::value, "Unsafe to cast between non-POD types.");
91 static_assert(!std::is_pointer<X>::value, "Incorrect cast between pointer types.");
92 static_assert(!std::is_pointer<Y>::value, "Incorrect cast between pointer types.");
93 static_assert(sizeof(X) == sizeof(Y), "Incorrect cast between types with different sizes.");
94 return reinterpret_cast<const X&>(value);
95 }
96
97 template <typename X, typename Y>
98 constexpr X& implement_math_cast(Y& value) {
99 static_assert(std::is_trivially_copyable<X>::value, "Unsafe to cast between non-POD types.");
100 static_assert(std::is_trivially_copyable<Y>::value, "Unsafe to cast between non-POD types.");
101 static_assert(!std::is_pointer<X>::value, "Incorrect cast between pointer types.");
102 static_assert(!std::is_pointer<Y>::value, "Incorrect cast between pointer types.");
103 static_assert(sizeof(X) == sizeof(Y), "Incorrect cast between types with different sizes.");
104 return reinterpret_cast<X&>(value);
105 }
106 } // namespace detail
107
108
109#define DEFINE_CAST(X, Y) \
110 template <> \
111 constexpr const X& cast<X, Y>(const Y& value) { \
112 return detail::implement_math_cast<X>(value); \
113 }
114
115 static_assert(offsetof(DirectX::XMFLOAT2, x) == offsetof(XrVector2f, x));
116 static_assert(offsetof(DirectX::XMFLOAT2, y) == offsetof(XrVector2f, y));
117 DEFINE_CAST(XrVector2f, DirectX::XMFLOAT2);
118 DEFINE_CAST(DirectX::XMFLOAT2, XrVector2f);
119
120 static_assert(offsetof(DirectX::XMFLOAT3, x) == offsetof(XrVector3f, x));
121 static_assert(offsetof(DirectX::XMFLOAT3, y) == offsetof(XrVector3f, y));
122 static_assert(offsetof(DirectX::XMFLOAT3, z) == offsetof(XrVector3f, z));
123 DEFINE_CAST(XrVector3f, DirectX::XMFLOAT3);
124 DEFINE_CAST(DirectX::XMFLOAT3, XrVector3f);
125
126 static_assert(offsetof(DirectX::XMFLOAT4, x) == offsetof(XrVector4f, x));
127 static_assert(offsetof(DirectX::XMFLOAT4, y) == offsetof(XrVector4f, y));
128 static_assert(offsetof(DirectX::XMFLOAT4, z) == offsetof(XrVector4f, z));
129 static_assert(offsetof(DirectX::XMFLOAT4, w) == offsetof(XrVector4f, w));
130 DEFINE_CAST(XrVector4f, DirectX::XMFLOAT4);
131 DEFINE_CAST(DirectX::XMFLOAT4, XrVector4f);
132
133 static_assert(offsetof(DirectX::XMFLOAT4, x) == offsetof(XrQuaternionf, x));
134 static_assert(offsetof(DirectX::XMFLOAT4, y) == offsetof(XrQuaternionf, y));
135 static_assert(offsetof(DirectX::XMFLOAT4, z) == offsetof(XrQuaternionf, z));
136 static_assert(offsetof(DirectX::XMFLOAT4, w) == offsetof(XrQuaternionf, w));
137 DEFINE_CAST(XrQuaternionf, DirectX::XMFLOAT4);
138 DEFINE_CAST(DirectX::XMFLOAT4, XrQuaternionf);
139
140 static_assert(offsetof(DirectX::XMINT2, x) == offsetof(XrExtent2Di, width));
141 static_assert(offsetof(DirectX::XMINT2, y) == offsetof(XrExtent2Di, height));
142 DEFINE_CAST(XrExtent2Di, DirectX::XMINT2);
143 DEFINE_CAST(DirectX::XMINT2, XrExtent2Di);
144
145 static_assert(offsetof(DirectX::XMFLOAT2, x) == offsetof(XrExtent2Df, width));
146 static_assert(offsetof(DirectX::XMFLOAT2, y) == offsetof(XrExtent2Df, height));
147 DEFINE_CAST(XrExtent2Df, DirectX::XMFLOAT2);
148 DEFINE_CAST(DirectX::XMFLOAT2, XrExtent2Df);
149
150 static_assert(offsetof(DirectX::XMFLOAT4, x) == offsetof(XrColor4f, r));
151 static_assert(offsetof(DirectX::XMFLOAT4, y) == offsetof(XrColor4f, g));
152 static_assert(offsetof(DirectX::XMFLOAT4, z) == offsetof(XrColor4f, b));
153 static_assert(offsetof(DirectX::XMFLOAT4, w) == offsetof(XrColor4f, a));
154 DEFINE_CAST(XrColor4f, DirectX::XMFLOAT4);
155 DEFINE_CAST(DirectX::XMFLOAT4, XrColor4f);
156
157#undef DEFINE_CAST
158
159 // Shortcut non-templated overload of cast() function
160#define DEFINE_CAST(X, Y) \
161 constexpr const X& cast(const Y& value) { \
162 return detail::implement_math_cast<X>(value); \
163 } \
164 constexpr X& cast(Y& value) { \
165 return detail::implement_math_cast<X>(value); \
166 }
167
168 DEFINE_CAST(DirectX::XMFLOAT2, XrVector2f);
169 DEFINE_CAST(DirectX::XMFLOAT3, XrVector3f);
170 DEFINE_CAST(DirectX::XMFLOAT4, XrVector4f);
171 DEFINE_CAST(DirectX::XMFLOAT4, XrQuaternionf);
172 DEFINE_CAST(DirectX::XMFLOAT2, XrExtent2Df);
173#undef DEFINE_CAST
174
175#define VECTOR2F_OPERATOR(op) \
176 constexpr XrVector2f operator op(const XrVector2f& a, const XrVector2f& b) { \
177 return XrVector2f{a.x op b.x, a.y op b.y}; \
178 }
183#undef VECTOR2F_OPERATOR
184
185#define VECTOR2F_OPERATOR(op) \
186 constexpr XrVector2f operator op(const XrVector2f& a, float s) { \
187 return XrVector2f{a.x op s, a.y op s}; \
188 }
193#undef VECTOR2F_OPERATOR
194
195#define VECTOR2F_OPERATOR(op) \
196 constexpr XrVector2f operator op(float s, const XrVector2f& a) { \
197 return XrVector2f{s op a.x, s op a.y}; \
198 }
203#undef VECTOR2F_OPERATOR
204
205#define VECTOR3F_OPERATOR(op) \
206 constexpr XrVector3f operator op(const XrVector3f& a, const XrVector3f& b) { \
207 return XrVector3f{a.x op b.x, a.y op b.y, a.z op b.z}; \
208 }
213#undef VECTOR3F_OPERATOR
214
215#define VECTOR3F_OPERATOR(op) \
216 constexpr XrVector3f operator op(const XrVector3f& a, float s) { \
217 return XrVector3f{a.x op s, a.y op s, a.z op s}; \
218 }
223#undef VECTOR3F_OPERATOR
224
225#define VECTOR3F_OPERATOR(op) \
226 constexpr XrVector3f operator op(float s, const XrVector3f& a) { \
227 return XrVector3f{s op a.x, s op a.y, s op a.z}; \
228 }
233#undef VECTOR3F_OPERATOR
234
235 inline DirectX::XMVECTOR XM_CALLCONV LoadXrVector2(const XrVector2f& vector) {
236 return DirectX::XMLoadFloat2(&xr::math::cast(vector));
237 }
238
239 inline DirectX::XMVECTOR XM_CALLCONV LoadXrVector3(const XrVector3f& vector) {
240 return DirectX::XMLoadFloat3(&xr::math::cast(vector));
241 }
242
243 inline DirectX::XMVECTOR XM_CALLCONV LoadXrVector4(const XrVector4f& vector) {
244 return DirectX::XMLoadFloat4(&xr::math::cast(vector));
245 }
246
247 inline DirectX::XMVECTOR XM_CALLCONV LoadXrQuaternion(const XrQuaternionf& quaternion) {
248 return DirectX::XMLoadFloat4(&xr::math::cast(quaternion));
249 }
250
251 inline DirectX::XMVECTOR XM_CALLCONV LoadXrExtent(const XrExtent2Df& extend) {
252 return DirectX::XMLoadFloat2(&xr::math::cast(extend));
253 }
254
255 inline DirectX::XMMATRIX XM_CALLCONV LoadXrPose(const XrPosef& pose) {
256 const DirectX::XMVECTOR orientation = LoadXrQuaternion(pose.orientation);
257 const DirectX::XMVECTOR position = LoadXrVector3(pose.position);
258 DirectX::XMMATRIX matrix = DirectX::XMMatrixRotationQuaternion(orientation);
259 matrix.r[3] = DirectX::XMVectorAdd(matrix.r[3], position);
260 return matrix;
261 }
262
263 inline DirectX::XMMATRIX XM_CALLCONV LoadInvertedXrPose(const XrPosef& pose) {
264 return LoadXrPose(Pose::Invert(pose));
265 }
266
267 inline void XM_CALLCONV StoreXrVector2(XrVector2f* outVec, DirectX::FXMVECTOR inVec) {
268 DirectX::XMStoreFloat2(&detail::implement_math_cast<DirectX::XMFLOAT2>(*outVec), inVec);
269 }
270
271 inline void XM_CALLCONV StoreXrVector3(XrVector3f* outVec, DirectX::FXMVECTOR inVec) {
272 DirectX::XMStoreFloat3(&detail::implement_math_cast<DirectX::XMFLOAT3>(*outVec), inVec);
273 }
274
275 inline void XM_CALLCONV StoreXrVector4(XrVector4f* outVec, DirectX::FXMVECTOR inVec) {
276 DirectX::XMStoreFloat4(&detail::implement_math_cast<DirectX::XMFLOAT4>(*outVec), inVec);
277 }
278
279 inline void XM_CALLCONV StoreXrQuaternion(XrQuaternionf* outQuat, DirectX::FXMVECTOR inQuat) {
280 DirectX::XMStoreFloat4(&detail::implement_math_cast<DirectX::XMFLOAT4>(*outQuat), inQuat);
281 }
282
283 inline void XM_CALLCONV StoreXrExtent(XrExtent2Df* outVec, DirectX::FXMVECTOR inVec) {
284 DirectX::XMStoreFloat2(&detail::implement_math_cast<DirectX::XMFLOAT2>(*outVec), inVec);
285 }
286
287 inline bool XM_CALLCONV StoreXrPose(XrPosef* out, DirectX::FXMMATRIX matrix) {
288 DirectX::XMVECTOR position;
289 DirectX::XMVECTOR orientation;
290 DirectX::XMVECTOR scale;
291
292 if (!DirectX::XMMatrixDecompose(&scale, &orientation, &position, matrix)) {
293 return false; // Non-SRT matrix encountered
294 }
295
296 StoreXrQuaternion(&out->orientation, orientation);
297 StoreXrVector3(&out->position, position);
298 return true;
299 }
300
301 namespace Pose {
302 constexpr XrPosef Identity() {
303 return {{0, 0, 0, 1}, {0, 0, 0}};
304 }
305
306 constexpr XrPosef Translation(const XrVector3f& translation) {
307 XrPosef pose = Identity();
308 pose.position = translation;
309 return pose;
310 }
311
312 inline XrPosef LookAt(const XrVector3f& origin, const XrVector3f& forward, const XrVector3f& up) {
313 DirectX::XMMATRIX virtualToGazeOrientation =
314 DirectX::XMMatrixLookToRH(xr::math::LoadXrVector3(origin), xr::math::LoadXrVector3(forward), xr::math::LoadXrVector3(up));
315 XrPosef pose;
316 xr::math::StoreXrPose(&pose, DirectX::XMMatrixInverse(nullptr, virtualToGazeOrientation));
317
318 return pose;
319 }
320
321 inline XrPosef Slerp(const XrPosef& a, const XrPosef& b, float alpha) {
322 return MakePose(Quaternion::Slerp(a.orientation, b.orientation, alpha), a.position + (b.position - a.position) * alpha);
323 }
324
325 inline XrPosef Invert(const XrPosef& pose) {
326 const DirectX::XMVECTOR orientation = LoadXrQuaternion(pose.orientation);
327 const DirectX::XMVECTOR invertOrientation = DirectX::XMQuaternionConjugate(orientation);
328
329 const DirectX::XMVECTOR position = LoadXrVector3(pose.position);
330 const DirectX::XMVECTOR invertPosition = DirectX::XMVector3Rotate(DirectX::XMVectorNegate(position), invertOrientation);
331
332 XrPosef result;
333 StoreXrQuaternion(&result.orientation, invertOrientation);
334 StoreXrVector3(&result.position, invertPosition);
335 return result;
336 }
337
338 inline XrPosef Multiply(const XrPosef& a, const XrPosef& b) {
339 // Q: Quaternion, P: Position, R:Rotation, T:Translation
340 // (Qa Pa) * (Qb Pb)
341 // = Ra * Ta * Rb * Tb
342 // = Ra * (Ta * Rb) * Tb
343 // = Ra * RotationOf(Ta * Rb) * TranslationOf(Ta * Rb) * Tb
344 // => Rc = Ra * RotationOf(Ta * Rb)
345 // Qc = Qa * Qb;
346 // => Tc = TranslationOf(Ta * Rb) * Tb
347 // Pc = XMVector3Rotate(Pa, Qb) + Pb;
348
349 const DirectX::XMVECTOR pa = LoadXrVector3(a.position);
350 const DirectX::XMVECTOR qa = LoadXrQuaternion(a.orientation);
351 const DirectX::XMVECTOR pb = LoadXrVector3(b.position);
352 const DirectX::XMVECTOR qb = LoadXrQuaternion(b.orientation);
353
354 XrPosef c;
355 StoreXrQuaternion(&c.orientation, DirectX::XMQuaternionMultiply(qa, qb));
356 StoreXrVector3(&c.position, DirectX::XMVectorAdd(DirectX::XMVector3Rotate(pa, qb), pb));
357 return c;
358 }
359
360 constexpr bool IsPoseValid(XrSpaceLocationFlags locationFlags) {
361 constexpr XrSpaceLocationFlags PoseValidFlags = XR_SPACE_LOCATION_POSITION_VALID_BIT | XR_SPACE_LOCATION_ORIENTATION_VALID_BIT;
362 return (locationFlags & PoseValidFlags) == PoseValidFlags;
363 }
364
365 constexpr bool IsPoseTracked(XrSpaceLocationFlags locationFlags) {
366 constexpr XrSpaceLocationFlags PoseTrackedFlags =
367 XR_SPACE_LOCATION_POSITION_TRACKED_BIT | XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT;
368 return (locationFlags & PoseTrackedFlags) == PoseTrackedFlags;
369 }
370
371 constexpr bool IsPoseValid(const XrSpaceLocation& spaceLocation) {
372 return IsPoseValid(spaceLocation.locationFlags);
373 }
374
375 constexpr bool IsPoseTracked(const XrSpaceLocation& spaceLocation) {
376 return IsPoseTracked(spaceLocation.locationFlags);
377 }
378
379 constexpr bool IsPoseValid(const XrHandJointLocationEXT& jointLocation) {
380 return IsPoseValid(jointLocation.locationFlags);
381 }
382
383 constexpr bool IsPoseTracked(const XrHandJointLocationEXT& jointLocation) {
384 return IsPoseTracked(jointLocation.locationFlags);
385 }
386
387 constexpr bool IsPoseValid(const XrViewState& viewState) {
388 constexpr XrViewStateFlags PoseValidFlags = XR_VIEW_STATE_POSITION_VALID_BIT | XR_VIEW_STATE_ORIENTATION_VALID_BIT;
389 return (viewState.viewStateFlags & PoseValidFlags) == PoseValidFlags;
390 }
391
392 constexpr bool IsPoseTracked(const XrViewState& viewState) {
393 constexpr XrViewStateFlags PoseTrackedFlags = XR_VIEW_STATE_POSITION_TRACKED_BIT | XR_VIEW_STATE_ORIENTATION_TRACKED_BIT;
394 return (viewState.viewStateFlags & PoseTrackedFlags) == PoseTrackedFlags;
395 }
396
397 template <typename Quaternion, typename Vector3>
398 inline XrPosef MakePose(const Quaternion& orientation, const Vector3& position) {
399 return XrPosef{{orientation.x, orientation.y, orientation.z, orientation.w}, {position.x, position.y, position.z}};
400 }
401 } // namespace Pose
402
403 namespace Quaternion {
404 constexpr inline XrQuaternionf Identity() {
405 return {0, 0, 0, 1};
406 }
407
408 inline float Length(const XrQuaternionf& quaternion) {
409 DirectX::XMVECTOR vector = LoadXrQuaternion(quaternion);
410 return DirectX::XMVectorGetX(DirectX::XMVector4Length(vector));
411 }
412
413 inline bool IsNormalized(const XrQuaternionf& quaternion) {
414 return fabs(1 - Length(quaternion)) <= QuaternionEpsilon;
415 }
416
417 inline XrQuaternionf RotationAxisAngle(const XrVector3f& axis, float angleInRadians) {
418 XrQuaternionf q;
419 StoreXrQuaternion(&q, DirectX::XMQuaternionRotationAxis(LoadXrVector3(axis), angleInRadians));
420 return q;
421 }
422
423 inline XrQuaternionf RotationRollPitchYaw(const XrVector3f& anglesInRadians) {
424 XrQuaternionf q;
425 StoreXrQuaternion(&q, DirectX::XMQuaternionRotationRollPitchYaw(anglesInRadians.x, anglesInRadians.y, anglesInRadians.z));
426 return q;
427 }
428
429 inline XrQuaternionf Slerp(const XrQuaternionf& a, const XrQuaternionf& b, float alpha) {
430 DirectX::XMVECTOR qa = LoadXrQuaternion(a);
431 DirectX::XMVECTOR qb = LoadXrQuaternion(b);
432 DirectX::XMVECTOR qr = DirectX::XMQuaternionSlerp(qa, qb, alpha);
433 XrQuaternionf result;
434 StoreXrQuaternion(&result, qr);
435 return result;
436 }
437
438 } // namespace Quaternion
439
440 inline XrPosef operator*(const XrPosef& a, const XrPosef& b) {
441 return Pose::Multiply(a, b);
442 }
443
444 inline float Dot(const XrVector3f& a, const XrVector3f& b) {
445 return a.x * b.x + a.y * b.y + a.z * b.z;
446 }
447
448 inline float Length(const XrVector3f& v) {
449 return std::sqrt(Dot(v, v));
450 }
451
452 inline XrVector3f Normalize(const XrVector3f& a) {
453 return a / std::sqrt(Dot(a, a));
454 }
455
456 // 2 * n / (r - l) 0 0 0
457 // 0 2 * n / (t - b) 0 0
458 // (r + l) / (r - l) (t + b) / (t - b) f / (n - f) -1
459 // 0 0 n*f / (n - f) 0
460 inline DirectX::XMMATRIX ComposeProjectionMatrix(const XrFovf& fov, const NearFar& nearFar) {
461 const auto ValidateFovAngle = [](float angle) {
462 if (angle >= DirectX::XM_PIDIV2 || angle <= -DirectX::XM_PIDIV2) {
463 throw std::runtime_error("Invalid projection specification");
464 }
465 };
466 ValidateFovAngle(fov.angleLeft);
467 ValidateFovAngle(fov.angleRight);
468 ValidateFovAngle(fov.angleUp);
469 ValidateFovAngle(fov.angleDown);
470 if (fabs(fov.angleLeft - fov.angleRight) < std::numeric_limits<float>::epsilon() ||
471 fabs(fov.angleUp - fov.angleDown) < std::numeric_limits<float>::epsilon()) {
472 throw std::runtime_error("Invalid projection specification");
473 }
474
475 const float nearPlane = nearFar.Near;
476 const float farPlane = nearFar.Far;
477 const bool infNearPlane = isinf(nearPlane);
478 const bool infFarPlane = isinf(farPlane);
479
480 float l = tan(fov.angleLeft);
481 float r = tan(fov.angleRight);
482 float b = tan(fov.angleDown);
483 float t = tan(fov.angleUp);
484 if (!infNearPlane) {
485 l *= nearPlane;
486 r *= nearPlane;
487 b *= nearPlane;
488 t *= nearPlane;
489 }
490
491 if (nearPlane < 0.f || farPlane < 0.f) {
492 throw std::runtime_error("Invalid projection specification");
493 }
494
495 if (infNearPlane || infFarPlane) {
496 if (infNearPlane && infFarPlane) {
497 throw std::runtime_error("Invalid projection specification");
498 }
499
500 const float reciprocalWidth = 1.0f / (r - l);
501 const float reciprocalHeight = 1.0f / (t - b);
502
503 DirectX::XMFLOAT4X4 projectionMatrix;
504
505 float twoNearZ;
506 if (infNearPlane) {
507 twoNearZ = 2;
508
509 projectionMatrix._33 = 0.0f; // far / (near - far) = far / inf = 0
510 projectionMatrix._43 = farPlane; // near * far / (near - far) = far * (near / (near - far)) = far * (inf / inf) = far
511 } else {
512 twoNearZ = nearPlane + nearPlane;
513
514 projectionMatrix._33 = -1.0f; // far / (near - far) = inf / -inf = -1
515 projectionMatrix._43 = -nearPlane; // near * far / (near - far) = near * inf / -inf = -near
516 }
517
518 projectionMatrix._11 = twoNearZ * reciprocalWidth;
519 projectionMatrix._12 = 0.0f;
520 projectionMatrix._13 = 0.0f;
521 projectionMatrix._14 = 0.0f;
522
523 projectionMatrix._21 = 0.0f;
524 projectionMatrix._22 = twoNearZ * reciprocalHeight;
525 projectionMatrix._23 = 0.0f;
526 projectionMatrix._24 = 0.0f;
527
528 projectionMatrix._31 = (l + r) * reciprocalWidth;
529 projectionMatrix._32 = (t + b) * reciprocalHeight;
530 projectionMatrix._34 = -1.0f;
531
532 projectionMatrix._41 = 0.0f;
533 projectionMatrix._42 = 0.0f;
534 projectionMatrix._44 = 0.0f;
535
536 return DirectX::XMLoadFloat4x4(&projectionMatrix);
537 } else {
538 return DirectX::XMMatrixPerspectiveOffCenterRH(l, r, b, t, nearPlane, farPlane);
539 }
540 }
541
542 inline bool IsInfiniteNearPlaneProjectionMatrix(const DirectX::XMFLOAT4X4& p) {
543 return (p._33 == 0);
544 }
545
546 inline bool IsInfiniteFarPlaneProjectionMatrix(const DirectX::XMFLOAT4X4& p) {
547 return (p._33 == -1);
548 }
549
550 inline void ValidateProjectionMatrix(const DirectX::XMFLOAT4X4& p) {
551 // Reference equations on top of ComposeProjectionMatrix() above.
552 if (p._12 != 0 || p._13 != 0 || p._14 != 0 ||
553 // p._21 is not 0 on old MR devices, but small enough to be ignored. For future MR devices, it should be 0 (no shear)
554 p._23 != 0 || p._24 != 0 ||
555 // When near or far plane is infinite, p._33 is 0 or -1, respectively. They are valid cases.
556 p._34 != -1 || p._41 != 0 || p._42 != 0 || p._44 != 0) {
557 throw std::runtime_error("Invalid projection matrix");
558 }
559 }
560
561 inline NearFar GetProjectionNearFar(const DirectX::XMFLOAT4X4& p) {
563
564 NearFar d;
565
567 d.Near = std::numeric_limits<float>::infinity();
568 d.Far = p._43;
570 d.Near = -p._43;
571 d.Far = std::numeric_limits<float>::infinity();
572 } else {
573 // Reference equations on top of ComposeProjectionMatrix() above.
574 d.Near = p._43 / p._33;
575 d.Far = p._43 / (1 + p._33);
576 }
577
578 return d;
579 }
580
581 inline XrFovf DecomposeProjectionMatrix(const DirectX::XMFLOAT4X4& p) {
583
584 // n = m43 / m33
585 // f = m43 / (1 + m33)
586 // l = n * (m31 - 1) / m11 => angle left = atan2(l, n) => atan2(m31 - 1, m11)
587 // r = n * (m31 + 1) / m11 => so on
588 // b = n * (m32 - 1) / m22 => and
589 // t = n * (m32 + 1) / m22 => so forth
590 XrFovf fov;
591 fov.angleLeft = atan2(p._31 - 1, p._11);
592 fov.angleRight = atan2(p._31 + 1, p._11);
593 fov.angleDown = atan2(p._32 - 1, p._22);
594 fov.angleUp = atan2(p._32 + 1, p._22);
595 return fov;
596 }
597
598 template <uint32_t alignment>
599 inline constexpr uint32_t AlignTo(uint32_t n) {
600 static_assert((alignment & (alignment - 1)) == 0); // must be power-of-two
601 return (n + alignment - 1) & ~(alignment - 1);
602 }
603
604 inline constexpr uint32_t DivideRoundingUp(uint32_t x, uint32_t y) {
605 return (x + y - 1) / y;
606 }
607} // namespace xr::math
608
609#pragma endregion
#define VECTOR2F_OPERATOR(op)
Definition XrMath.h:175
#define VECTOR3F_OPERATOR(op)
#define DEFINE_CAST(X, Y)
Definition XrMath.h:109
Ogre::Vector3 Vector3
Definition globals.h:58
Ogre::Quaternion Quaternion
Definition globals.h:60
constexpr bool IsPoseValid(const XrSpaceLocation &location)
Definition XrMath.h:371
XrPosef Slerp(const XrPosef &a, const XrPosef &b, float alpha)
Definition XrMath.h:321
constexpr XrPosef Identity()
Definition XrMath.h:302
XrPosef Invert(const XrPosef &pose)
Definition XrMath.h:325
constexpr bool IsPoseTracked(const XrSpaceLocation &location)
Definition XrMath.h:375
XrPosef LookAt(const XrVector3f &origin, const XrVector3f &forward, const XrVector3f &up)
Definition XrMath.h:312
XrPosef MakePose(const Quaternion &orientation, const Vector3 &position)
Definition XrMath.h:398
constexpr XrPosef Translation(const XrVector3f &translation)
Definition XrMath.h:306
XrPosef Multiply(const XrPosef &a, const XrPosef &b)
Definition XrMath.h:338
bool IsNormalized(const XrQuaternionf &quaternion)
Definition XrMath.h:413
XrQuaternionf RotationAxisAngle(const XrVector3f &axis, float angleInRadians)
Definition XrMath.h:417
XrQuaternionf Slerp(const XrQuaternionf &a, const XrQuaternionf &b, float alpha)
Definition XrMath.h:429
constexpr XrQuaternionf Identity()
Definition XrMath.h:404
XrQuaternionf RotationRollPitchYaw(const XrVector3f &eulerAnglesInRadians)
Definition XrMath.h:423
float Length(const XrQuaternionf &quaternion)
Definition XrMath.h:408
constexpr const X & implement_math_cast(const Y &value)
Definition XrMath.h:88
bool IsInfiniteFarPlaneProjectionMatrix(const DirectX::XMFLOAT4X4 &p)
Definition XrMath.h:546
void XM_CALLCONV StoreXrExtent(XrExtent2Df *extend, DirectX::FXMVECTOR inVec)
Definition XrMath.h:283
DirectX::XMVECTOR XM_CALLCONV LoadXrExtent(const XrExtent2Df &extend)
Definition XrMath.h:251
void XM_CALLCONV StoreXrVector3(XrVector3f *outVec, DirectX::FXMVECTOR inVec)
Definition XrMath.h:271
constexpr uint32_t AlignTo(uint32_t n)
Definition XrMath.h:599
DirectX::XMMATRIX XM_CALLCONV LoadXrPose(const XrPosef &rigidTransform)
Definition XrMath.h:255
XrPosef operator*(const XrPosef &a, const XrPosef &b)
Definition XrMath.h:440
constexpr float QuaternionEpsilon
Definition XrMath.h:12
constexpr float OneOverFloatEpsilon
Definition XrMath.h:15
DirectX::XMVECTOR XM_CALLCONV LoadXrQuaternion(const XrQuaternionf &quaternion)
Definition XrMath.h:247
void XM_CALLCONV StoreXrQuaternion(XrQuaternionf *outQuat, DirectX::FXMVECTOR inQuat)
Definition XrMath.h:279
bool IsInfiniteNearPlaneProjectionMatrix(const DirectX::XMFLOAT4X4 &p)
Definition XrMath.h:542
constexpr const X & cast(const Y &value)=delete
bool XM_CALLCONV StoreXrPose(XrPosef *out, DirectX::FXMMATRIX matrix)
Definition XrMath.h:287
XrVector3f Normalize(const XrVector3f &a)
Definition XrMath.h:452
XrFovf DecomposeProjectionMatrix(const DirectX::XMFLOAT4X4 &projectionMatrix)
Definition XrMath.h:581
void XM_CALLCONV StoreXrVector4(XrVector4f *outVec, DirectX::FXMVECTOR inVec)
Definition XrMath.h:275
float Dot(const XrVector3f &a, const XrVector3f &b)
Definition XrMath.h:444
NearFar GetProjectionNearFar(const DirectX::XMFLOAT4X4 &projectionMatrix)
Definition XrMath.h:561
DirectX::XMVECTOR XM_CALLCONV LoadXrVector4(const XrVector4f &vector)
Definition XrMath.h:243
void XM_CALLCONV StoreXrVector2(XrVector2f *outVec, DirectX::FXMVECTOR inVec)
Definition XrMath.h:267
void ValidateProjectionMatrix(const DirectX::XMFLOAT4X4 &p)
Definition XrMath.h:550
DirectX::XMMATRIX ComposeProjectionMatrix(const XrFovf &fov, const NearFar &nearFar)
Definition XrMath.h:460
DirectX::XMVECTOR XM_CALLCONV LoadXrVector3(const XrVector3f &vector)
Definition XrMath.h:239
float Length(const XrVector3f &v)
Definition XrMath.h:448
DirectX::XMMATRIX XM_CALLCONV LoadInvertedXrPose(const XrPosef &rigidTransform)
Definition XrMath.h:263
constexpr uint32_t DivideRoundingUp(uint32_t x, uint32_t y)
Definition XrMath.h:604
DirectX::XMVECTOR XM_CALLCONV LoadXrVector2(const XrVector2f &vector)