JPL Spatial
Sound spatialization and propagation library
Loading...
Searching...
No Matches
DirectionEncoding.h
Go to the documentation of this file.
1//
2// ██╗██████╗ ██╗ ██╗██████╗ ███████╗
3// ██║██╔══██╗ ██║ ██║██╔══██╗██╔════╝ ** JPLSpatial **
4// ██║██████╔╝ ██║ ██║██████╔╝███████╗
5// ██ ██║██╔═══╝ ██║ ██║██╔══██╗╚════██║ https://github.com/Jaytheway/JPLSpatial
6// ╚█████╔╝██║ ███████╗██║██████╔╝███████║
7// ╚════╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚══════╝
8//
9// Copyright 2024 Jaroslav Pevno, JPLSpatial is offered under the terms of the ISC license:
10//
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or
12// without fee is hereby granted, provided that the above copyright notice and this permission
13// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
14// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#pragma once
21
22#include <JPLSpatial/Core.h>
30
31#include <cmath>
32#include <numbers>
33#include <concepts>
34#include <algorithm>
35
36namespace JPL
37{
38 //==========================================================================
40 namespace Octahedron
41 {
42 namespace Internal
43 {
44 template <class T, class... Types>
45 concept AnyOf = (std::same_as<T, Types> || ...);
46
47 template<class EncodedType_>
49 {
51 static constexpr auto cBitsPerAxis = static_cast<EncodedType>(BitWidthOf<EncodedType>() / 2);
52 };
53 }
54
55 //======================================================================
61
62 //======================================================================
64 template<class T>
66
67 //======================================================================
71 template <size_t BitsPerAxis>
72 constexpr double cMaxComponentError = 4.0 / (static_cast<double>((1ULL << BitsPerAxis)) - 1.0);
73
74 template <size_t BitsPerAxis>
75 constexpr double cMaxVectorError = cMaxComponentError<BitsPerAxis> * std::numbers::sqrt3;
76
77 // Sanity check
78 static_assert(cMaxComponentError<8> >= 0.013); // ~ 0.0157
79 static_assert(cMaxComponentError<16> >= 2.5e-5); // ~ 6.1e-5
80
81 //======================================================================
83 [[nodiscard]] JPL_INLINE constexpr double MaxComponentError(size_t bitsPerAxis) noexcept
84 {
85 return 4.0 / (static_cast<double>((1ULL << bitsPerAxis)) - 1.0);
86 }
87
89 [[nodiscard]] JPL_INLINE constexpr double MaxVectorError(size_t bitsPerAxis) noexcept
90 {
91 return MaxComponentError(bitsPerAxis) * std::numbers::sqrt3;
92 }
93 }
94
95 //==========================================================================
97 template<Octahedron::CPrecision Precision>
98 class OctahedronEncoding;
99
100 //==========================================================================
106
107 //==========================================================================
109 template<Octahedron::CPrecision Precision>
111 {
112 public:
113 using EncodedType = typename Precision::EncodedType;
114 static constexpr auto cBitsPerAxis = Precision::cBitsPerAxis;
115
116 // To ensure correctness, math is done on this float type regardless of the compoennt type of user Vec3
117 using FloatType = std::conditional_t<(Precision::cBitsPerAxis > 23), double, float>;
118
119 static constexpr EncodedType cAxisMask = static_cast<EncodedType>((1llu << cBitsPerAxis) - 1u);
120
121 // Actual number of valid codes is 'cAxisRange - 2'
122 static constexpr size_t cAxisRange = cAxisMask + 1; // Note: this may overflow for 64 bit encoder
123
124 static constexpr auto cMaxComponentError = Octahedron::cMaxComponentError<cBitsPerAxis>;
125 static constexpr auto cMaxVectorError = Octahedron::cMaxVectorError<cBitsPerAxis>;
126
127 private:
128 static constexpr EncodedType cCenter = cAxisMask >> 1;
129 static constexpr EncodedType cPolePosZ = (cCenter << cBitsPerAxis) | cCenter; // (0, 0, 1)
130 static constexpr EncodedType cPoleNegZ = (cCenter << cBitsPerAxis) | 1; // (0, 0, -1)
131
132 static constexpr EncodedType cMaxComponentValue = cAxisMask - 1;
133
134 public:
135
140 template<CVec3 Vec3>
141 static constexpr EncodedType Encode(const Vec3& direction);
142
143 static simd_mask Encode(const simd& dirX, const simd& dirY, const simd& dirZ) requires(sizeof(EncodedType) <= sizeof(uint32));
144
150 template<CVec3 Vec3>
151 static constexpr Vec3 Decode(EncodedType encodedDirection);
152
153 static void Decode(const simd_mask& encodedDirection, simd& outX, simd& outY, simd& outZ) requires(sizeof(EncodedType) <= sizeof(uint32));
154
157 template<std::integral T>
158 static JPL_INLINE constexpr bool IsValidCode(T code) noexcept;
159
162 template<std::integral T>
163 static JPL_INLINE constexpr bool AreValidComponents(T x, T y) noexcept;
164
167 template<std::integral T>
168 static JPL_INLINE constexpr EncodedType CombineComponents(T x, T y) noexcept;
169
171 template<std::integral T>
172 static JPL_INLINE constexpr auto SanitizeCode(T code) noexcept;
173 };
174
175 //==========================================================================
180 template<CVec3 Vec3>
182
188 template<CVec3 Vec3>
189 JPL_INLINE constexpr Vec3 FromOctahedron32(uint32 encodedDirection) { return Octahedron32Bit::Decode<Vec3>(encodedDirection); }
190
191 //==========================================================================
193 // https://www.freesteel.co.uk/wpblog/2009/06/05/encoding-2d-angles-without-trigonometry/
194 // https://www.jeremyong.com/graphics/2023/01/09/tangent-spaces-and-diamond-encoding/
195
197 [[nodiscard]] inline constexpr float ToDiamond(Vec2 dir) noexcept
198 {
199 // Project to the unit diamond, then to the x-axis.
200 dir.X /= (Math::Abs(dir.X) + Math::Abs(dir.Y));
201
202 // Contract the x coordinate by a factor of 4 to represent all 4 quadrants in
203 // the unit range and remap
204
205 //const float pySign = Math::Sign2(dir.Y);
206 //return -pySign * 0.25f * dir.X + 0.5f + pySign * 0.25f;
207 const float pySignQuarter = Math::Sign2(dir.Y) * 0.25f;
208 return Math::FMA(-pySignQuarter, dir.X, (0.5f + pySignQuarter));
209 }
210
212 [[nodiscard]] inline constexpr Vec2 FromDiamond(float p) noexcept
213 {
214 Vec2 v;
215
216 // Remap p to the appropriate segment on the diamond
217 //const float pSign = Math::Sign2(p - 0.5f);
218 //v.X = -pSign * 4.0f * p + 1.0f + pSign * 2.0f;
219 //v.Y = pSign * (1.0f - Math::Abs(v.X));
220 const float pSign = Math::Sign2(p - 0.5f);
221 const float pSignN = -pSign;
222 v.X = Math::FMA(pSignN, 4.0f * p, Math::FMA(pSign, 2.0f, 1.0f));
223 v.Y = Math::FMA(pSignN, Math::Abs(v.X), pSign);
224
225 // Normalization extends the point on the diamond back to the unit circle
226 return v.Normalize();
227 }
228
232 {
233 // Project to the unit diamond, then to the x-axis.
234 x /= (abs(x) + abs(y));
235
236 // Contract the x coordinate by a factor of 4 to represent all 4 quadrants in
237 // the unit range and remap
238
239 //const float pySign = Math::Sign2(dir.Y);
240 //return -pySign * 0.25f * dir.X + 0.5f + pySign * 0.25f;
241 const simd pySignQuarter = Math::Sign2(y) * 0.25f;
243 }
244} // namespace JPL
245
246
247//==============================================================================
248//
249// Code beyond this point is implementation detail...
250//
251//==============================================================================
252
253// (enable in case of issues with (0,0,-1) direction)
254#define JPL_OCTAHEDRON_SPECIAL_CASE_HANDLING 0
255
256namespace JPL
257{
258 template<Octahedron::CPrecision Precision>
259 template<std::integral T>
261 {
262 return x != cAxisMask && y != cAxisMask;
263 }
264
265 template<Octahedron::CPrecision Precision>
266 template<std::integral T>
268 {
269 const auto x = static_cast<EncodedType>(code) & cAxisMask;
270 const auto y = (static_cast<EncodedType>(code) >> cBitsPerAxis) & cAxisMask;
271 return AreValidComponents(x, y);
272 }
273
274 template<Octahedron::CPrecision Precision>
275 template<std::integral T>
277 {
278 return (std::min(EncodedType(y), cMaxComponentValue) << cBitsPerAxis) | std::min(EncodedType(x), cMaxComponentValue);
279 }
280
281 template<Octahedron::CPrecision Precision>
282 template<std::integral T>
284 {
285 const EncodedType x = static_cast<EncodedType>(code) & cAxisMask;
286 const EncodedType y = (static_cast<EncodedType>(code) >> cBitsPerAxis) & cAxisMask;
287 return static_cast<T>((std::min(y, cMaxComponentValue) << cBitsPerAxis) | std::min(x, cMaxComponentValue));
288 }
289
290 template<Octahedron::CPrecision Precision>
291 template<CVec3 Vec3>
293 {
294 const Vec3 dirAbs = Abs(direction);
296
297 // It should be up to the user to ensure valid direction vector
298 JPL_ASSERT(!Math::IsNearlyZero(L1Norm) && "direction must be normalised & non-zero");
299#if 0
300 // Handle zero vector (map to positive Z pole or some other neutral value)
302 {
303 // return (static_cast<EncodedType>(center_val) << cBitsPerAxis) | center_val;
304 return cCenter; // return forward vector (we may or may not want different fallback)
305 }
306#endif
307
308#if JPL_OCTAHEDRON_SPECIAL_CASE_HANDLING
309 // --- Special Handling for Poles
310 // If it's a pole (only Z component is non-`zero)
311 constexpr auto cPoleEps = FloatType(1e-6);
312 const bool isPole = (dirAbs.X <= cPoleEps) & (dirAbs.Y <= cPoleEps);
313 if (isPole)
314 return (direction.Z < FloatType(0.0)) ? cPoleNegZ : cPolePosZ;
315 // ---------------------------
316#endif
317
318 const FloatType invL1Norm = FloatType(1.0) / L1Norm;
321
322 // Standard folding for Z < 0
323 if (GetZ(direction) < 0.0f)
324 {
325 const FloatType tempPx = px; // Store original px for py calculation
326 px = (FloatType(1.0) - Math::Abs(py)) * Math::Sign2(tempPx);
327 py = (FloatType(1.0) - Math::Abs(tempPx)) * Math::Sign2(py);
328 }
329
330 constexpr FloatType cHalfTexel = FloatType(0.5) / FloatType(cAxisMask);
331 px = std::clamp(px, FloatType(-1.0) + cHalfTexel, FloatType(1.0) - cHalfTexel);
332 py = std::clamp(py, FloatType(-1.0) + cHalfTexel, FloatType(1.0) - cHalfTexel);
333
334 // Map to [0,1)
335 const FloatType v0 = px * FloatType(0.5) + FloatType(0.5);
336 const FloatType v1 = py * FloatType(0.5) + FloatType(0.5);
337
338 // Truncate to requested precision
339 const auto dx = static_cast<EncodedType>(Math::Floor(v0 * FloatType(cAxisMask)));
340 const auto dy = static_cast<EncodedType>(Math::Floor(v1 * FloatType(cAxisMask)));
341
342 return (dy << cBitsPerAxis) | dx;
343 }
344
345 template<Octahedron::CPrecision Precision>
346 inline simd_mask OctahedronEncoding<Precision>::Encode(const simd& dirX, const simd& dirY, const simd& dirZ) requires(sizeof(EncodedType) <= sizeof(uint32))
347 {
348 const simd dirXAbs = abs(dirX);
349 const simd dirYAbs = abs(dirY);
350 const simd dirZAbs = abs(dirZ);
351 const simd L1Norm = dirXAbs + dirYAbs + dirZAbs;
352
353 // It should be up to the user to ensure valid direction vector
354 JPL_ASSERT(!Math::IsNearlyZero(L1Norm).any_of() && "direction must be normalised & non-zero");
355
356 static const simd zero = simd::zero();
357 static const simd one = simd::c_1();
358 static const simd zero_p5 = simd::c_0p5();
359
360 const simd invL1Norm = one / L1Norm;
361 simd px = dirX * invL1Norm;
362 simd py = dirY * invL1Norm;
363
364 // Standard folding for Z < 0
365 {
366 const simd_mask zIsLessThanZero = dirZ < zero;
367 const simd tempPx = px; // Store original px for py calculation
368
371 }
372
373 static const simd cHalfTexel = zero_p5 / simd(cAxisMask);
376
377 // Map to [0,1)
378 const simd v0 = px * zero_p5 + zero_p5;
379 const simd v1 = py * zero_p5 + zero_p5;
380
381 // Truncate to requested precision
382 const auto dx = (floor(v0 * simd(cAxisMask))).to_mask();
383 const auto dy = (floor(v1 * simd(cAxisMask))).to_mask();
384
385 return (dy << cBitsPerAxis) | dx;
386 }
387
388 template<Octahedron::CPrecision Precision>
389 template<CVec3 Vec3>
391 {
392 constexpr FloatType muInv = FloatType(1.0) / static_cast<FloatType>(cAxisMask);
393
394 EncodedType dx = encodedDirection & cAxisMask; // Low bits for dx
395 EncodedType dy = (encodedDirection >> cBitsPerAxis) & cAxisMask; // High bits for dy
396
397 // alias the padding rim to the last real texel
398 dx = std::min(dx, cMaxComponentValue);
399 dy = std::min(dy, cMaxComponentValue);
400
401#if JPL_OCTAHEDRON_SPECIAL_CASE_HANDLING
402 // --- Special Handling for Poles
403 // Check if it matches our special encoding for (0,0,-1)
404 if (encodedDirection == cPoleNegZ)
405 {
406 return Vec3(Internal::FloatOf<Vec3>(0.0),
409 }
410 // ---------------------------
411#endif
412
413 // Map from [0, 1] to [-1, 1]
414 // (the added 0.5 shifts the sample from the lower-left vertex to the texel’s centre)
415 const FloatType pxDecoded = FloatType(-1.0) + FloatType(2.0) * (FloatType(dx) + FloatType(0.5)) * muInv;
416 const FloatType pyDecoded = FloatType(-1.0) + FloatType(2.0) * (FloatType(dy) + FloatType(0.5)) * muInv;
417
418 // Keep as array in case FloatType != VectorFloatType
420 pxDecoded,
421 pyDecoded,
423 };
424
425 // "Unfold" the negative Z hemisphere
426 const FloatType t = std::max(FloatType(-direction[2]), FloatType(0.0));
427 direction[0] += (FloatType(direction[0]) > FloatType(0.0)) ? -t : t;
428 direction[1] += (FloatType(direction[1]) > FloatType(0.0)) ? -t : t;
429
430 return Normalized(Vec3{
434 });
435 }
436
437 template<Octahedron::CPrecision Precision>
439 {
440 static const simd muInv = simd::c_1() / static_cast<simd>(cAxisMask);
441
442 simd_mask dx = encodedDirection & cAxisMask; // Low bits for dx
443 simd_mask dy = (encodedDirection >> cBitsPerAxis) & cAxisMask; // High bits for dy
444
445 // alias the padding rim to the last real texel
446 static const simd_mask maxComponentValue(cMaxComponentValue);
449
450 static const simd zero = simd::zero();
451 static const simd one = simd::c_1();
452 static const simd two = simd(2.0f);
453 static const simd zero_p5 = simd::c_0p5();
454
455 // Map from [0, 1] to [-1, 1]
456 // (the added 0.5 shifts the sample from the lower-left vertex to the texel’s centre)
457 const simd pxDecoded = -one + two * (dx.to_simd() + zero_p5) * muInv;
458 const simd pyDecoded = -one + two * (dy.to_simd() + zero_p5) * muInv;
459
460 simd direction[3]{
461 pxDecoded,
462 pyDecoded,
464 };
465
466 // "Unfold" the negative Z hemisphere
467 const simd t = max(-direction[2], zero);
468 direction[0] += simd::select(direction[0] > zero, -t, t);
469 direction[1] += simd::select(direction[1] > zero, -t, t);
470
471 // Copy to the output and normalize
472 outX = direction[0];
473 outY = direction[1];
474 outZ = direction[2];
475
476 const simd lenSqr = fma(outX, outX, fma(outY, outY, outZ * outZ));
478 outX *= invSqrt;
479 outY *= invSqrt;
480 outZ *= invSqrt;
481 }
482
483 //==========================================================================
484 namespace UnitTests
485 {
486#if 0
487 // Encoder to test
489
490 namespace // (for validating actual values)
491 {
492 static constexpr MinimalVec3 OriginalVec = Normalized(MinimalVec3(-1.0f, -1.0f, 1.0f));
493 static constexpr MinimalVec3 Decoded = EncoderType::Decode<MinimalVec3>(EncoderType::Encode(OriginalVec));
494 static constexpr MinimalVec3 Delta = Abs(OriginalVec - Decoded);
495 static_assert(Delta.X < EncoderType::cMaxComponentError &&
496 Delta.Y < EncoderType::cMaxComponentError &&
497 Delta.Z < EncoderType::cMaxComponentError);
498 }
499
500 // Helper function to perform and assert a test case
501 template<class VectorType>
502 constexpr bool TestOctahedron(const VectorType& original)
503 {
504 const VectorType normalizedOriginal = Normalized(original);
505 const auto encoded = EncoderType::Encode(normalizedOriginal);
506 const VectorType decoded = EncoderType::Decode<VectorType>(encoded);
507
508 return Math::Abs(normalizedOriginal.X - decoded.X) < EncoderType::cMaxComponentError
509 && Math::Abs(normalizedOriginal.Y - decoded.Y) < EncoderType::cMaxComponentError
510 && Math::Abs(normalizedOriginal.Z - decoded.Z) < EncoderType::cMaxComponentError;
511 }
512
513 // --- 0. Zero Vector ---
514 // Note: zero input should be handled by the caller
515 //static_assert(TestOctahedron(VecMock(0.0f, 0.0f, 0.0f), cOctahedronEncodingTolerance));
516
517 // --- 1. Cardinal Axes ---
518 // Positive Z
519 static_assert(TestOctahedron(MinimalVec3(0.0f, 0.0f, 1.0f)));
520 // Negative Z
521 static_assert(TestOctahedron(MinimalVec3(0.0f, 0.0f, -1.0f)));
522
523 // Positive X (on Z>=0 plane)
524 static_assert(TestOctahedron(MinimalVec3(1.0f, 0.0f, 0.0f)));
525 // Negative X (on Z>=0 plane)
526 static_assert(TestOctahedron(MinimalVec3(-1.0f, 0.0f, 0.0f)));
527
528 // Positive Y (on Z>=0 plane)
529 static_assert(TestOctahedron(MinimalVec3(0.0f, 1.0f, 0.0f)));
530 // Negative Y (on Z>=0 plane)
531 static_assert(TestOctahedron(MinimalVec3(0.0f, -1.0f, 0.0f)));
532
533
534 // --- 2. Face Centers / Diagonals (Z >= 0 hemisphere) ---
535 // Your initial test case: (1,1,0) normalized is (0.707, 0.707, 0.0)
536 static_assert(TestOctahedron(MinimalVec3(1.0f, 1.0f, 0.0f)));
537 // Other quadrants
538 static_assert(TestOctahedron(MinimalVec3(-1.0f, 1.0f, 0.0f)));
539 static_assert(TestOctahedron(MinimalVec3(1.0f, -1.0f, 0.0f)));
540 static_assert(TestOctahedron(MinimalVec3(-1.0f, -1.0f, 0.0f)));
541
542 // Vectors purely in XY plane, not on axes
543 static_assert(TestOctahedron(MinimalVec3(1.0f, 0.5f, 0.0f)));
544 static_assert(TestOctahedron(MinimalVec3(-1.0f, 0.5f, 0.0f)));
545 static_assert(TestOctahedron(MinimalVec3(1.0f, -0.5f, 0.0f)));
546 static_assert(TestOctahedron(MinimalVec3(-1.0f, -0.5f, 0.0f)));
547
548 // Vectors with positive Z components (e.g., Octahedron face centers in positive Z)
549 static_assert(TestOctahedron(MinimalVec3(1.0f, 1.0f, 1.0f))); // (1/sqrt(3), 1/sqrt(3), 1/sqrt(3))
550 static_assert(TestOctahedron(MinimalVec3(-1.0f, 1.0f, 1.0f)));
551 static_assert(TestOctahedron(MinimalVec3(1.0f, -1.0f, 1.0f)));
552 static_assert(TestOctahedron(MinimalVec3(-1.0f, -1.0f, 1.0f)));
553 static_assert(TestOctahedron(MinimalVec3(0.5f, 0.5f, 1.0f)));
554
555
556 // --- 3. Face Centers / Diagonals (Z < 0 hemisphere) ---
557 // Negative Z analogs of the above
558 static_assert(TestOctahedron(MinimalVec3(1.0f, 1.0f, -1.0f))); // (1/sqrt(3), 1/sqrt(3), -1/sqrt(3))
559 static_assert(TestOctahedron(MinimalVec3(-1.0f, 1.0f, -1.0f)));
560 static_assert(TestOctahedron(MinimalVec3(1.0f, -1.0f, -1.0f)));
561 static_assert(TestOctahedron(MinimalVec3(-1.0f, -1.0f, -1.0f)));
562 static_assert(TestOctahedron(MinimalVec3(0.5f, 0.5f, -1.0f)));
563
564
565 // --- 4. Near-Zero (But not exactly zero) ---
566 // These test the L1 norm division and the handling of very small values.
567 static_assert(TestOctahedron(MinimalVec3(1e-6f, 1e-6f, 1.0f)));
568 static_assert(TestOctahedron(MinimalVec3(1e-6f, 1.0f, 1e-6f)));
569 static_assert(TestOctahedron(MinimalVec3(1.0f, 1e-6f, 1e-6f)));
570
571 static_assert(TestOctahedron(MinimalVec3(1e-6f, 1e-6f, -1.0f)));
572 static_assert(TestOctahedron(MinimalVec3(1e-6f, -1.0f, 1e-6f)));
573 static_assert(TestOctahedron(MinimalVec3(-1.0f, 1e-6f, 1e-6f)));
574
575
576 // --- 5. Known Problematic Quantization Values (if any appear during development) ---
577 // Add any specific values that caused issues during interactive testing or debugging.
578 static_assert(TestOctahedron(MinimalVec3(0.0f, 0.0995037183f, -0.995037138f)));
579
580
581 // Diamond encoding
582 static constexpr Vec2 dir2D = Vec2(-0.25534f, 4.5433f).Normalize();
583 static constexpr float dir2DEncoded = ToDiamond(dir2D);
584 static constexpr Vec2 dir2DDecoded = FromDiamond(dir2DEncoded);
586#endif
587 } // namespace UnitTests
588
589} // namespace JPL
#define JPL_ASSERT(inExpression,...)
Main assert macro, usage: JPL_ASSERT(condition, message) or JPL_ASSERT(condition)
Definition ErrorReporting.h:76
Forward declaration.
Definition DirectionEncoding.h:111
static constexpr EncodedType Encode(const Vec3 &direction)
Definition DirectionEncoding.h:292
static constexpr auto cMaxComponentError
Definition DirectionEncoding.h:124
static JPL_INLINE constexpr bool AreValidComponents(T x, T y) noexcept
Definition DirectionEncoding.h:260
static constexpr size_t cAxisRange
Definition DirectionEncoding.h:122
typename Precision::EncodedType EncodedType
Definition DirectionEncoding.h:113
static constexpr auto cBitsPerAxis
Definition DirectionEncoding.h:114
static constexpr EncodedType cAxisMask
Definition DirectionEncoding.h:119
static JPL_INLINE constexpr auto SanitizeCode(T code) noexcept
Alias the padding rim to the last real texel.
Definition DirectionEncoding.h:283
static constexpr auto cMaxVectorError
Definition DirectionEncoding.h:125
static JPL_INLINE constexpr bool IsValidCode(T code) noexcept
Definition DirectionEncoding.h:267
static constexpr Vec3 Decode(EncodedType encodedDirection)
Definition DirectionEncoding.h:390
static JPL_INLINE constexpr EncodedType CombineComponents(T x, T y) noexcept
Definition DirectionEncoding.h:276
std::conditional_t<(Precision::cBitsPerAxis > 23), double, float > FloatType
Definition DirectionEncoding.h:117
Constraint for valid precision types.
Definition DirectionEncoding.h:65
Definition DirectionEncoding.h:45
std::remove_cvref_t< decltype(GetX(std::declval< Vec3 >()))> FloatOf
Definition Vec3Math.h:36
JPL_INLINE constexpr T Floor(T value) noexcept
A constexpr implementation of floor.
Definition Math.h:96
JPL_INLINE constexpr T FMA(T a, T b, T c) noexcept
Inlined fuse multiply-add. Compiler in some circumstances is more eager to optimize this than std::fm...
Definition Math.h:186
JPL_INLINE constexpr T InvSqrt(T x) noexcept
Definition Math.h:283
JPL_INLINE constexpr bool IsNearlyZero(T value, T errorTolerance=JPL_FLOAT_EPS_V< T >) noexcept
Definition Math.h:146
JPL_INLINE constexpr auto Abs(const T &value) noexcept
Standard abs is not constexpr in C++20.
Definition Math.h:87
JPL_INLINE constexpr T Sign2(T value) noexcept
Sign2 returns -1 for negative values, 1 otherwise.
Definition Math.h:131
constexpr double cMaxComponentError
Definition DirectionEncoding.h:72
constexpr double cMaxVectorError
Definition DirectionEncoding.h:75
JPL_INLINE constexpr double MaxVectorError(size_t bitsPerAxis) noexcept
Run-time variant of 'cMaxVectorError'.
Definition DirectionEncoding.h:89
JPL_INLINE constexpr double MaxComponentError(size_t bitsPerAxis) noexcept
Run-time variant of 'cMaxComponentError'.
Definition DirectionEncoding.h:83
Definition AcousticMaterial.h:36
JPL_INLINE simd clamp(const simd &value, const simd &minV, const simd &maxV) noexcept
Element-wise clamp.
Definition SIMD.h:1838
constexpr Vec2 FromDiamond(float p) noexcept
Decode scalar [0, 1] to a 2D unit vector.
Definition DirectionEncoding.h:212
JPL_INLINE auto GetX(const Vec3Type &v) noexcept
Definition Vec3Traits.h:35
OctahedronEncoding< Octahedron::Precision8bits > Octahedron16Bit
Definition DirectionEncoding.h:103
JPL_INLINE constexpr uint32 ToOctahedron32(const Vec3 &direction)
Definition DirectionEncoding.h:181
JPL_INLINE constexpr Vec3 FromOctahedron32(uint32 encodedDirection)
Definition DirectionEncoding.h:189
JPL_INLINE auto GetZ(const Vec3Type &v) noexcept
Definition Vec3Traits.h:37
std::uint32_t uint32
Definition Core.h:311
JPL_INLINE simd floor(const simd &vec) noexcept
Element-wise floor.
Definition SIMD.h:1861
JPL_INLINE auto GetY(const Vec3Type &v) noexcept
Definition Vec3Traits.h:36
JPL_INLINE simd max(const simd &a, const simd &b) noexcept
Element-wise max.
Definition SIMD.h:1799
JPL_INLINE simd fma(const simd &mul1, const simd &mul2, const simd &addV) noexcept
Element-wise fused multiply-add.
Definition SIMD.h:1843
JPL_INLINE simd min(const simd &a, const simd &b) noexcept
Element-wise min.
Definition SIMD.h:1813
constexpr float ToDiamond(Vec2 dir) noexcept
"Diamond Encoding" of a 2D unit vector as per:
Definition DirectionEncoding.h:197
JPL_INLINE simd abs(const simd &vec) noexcept
Definition SIMD.h:1827
Definition MinimalVec3.h:37
Definition DirectionEncoding.h:49
EncodedType_ EncodedType
Definition DirectionEncoding.h:50
static constexpr auto cBitsPerAxis
Definition DirectionEncoding.h:51
Definition MinimalVec2.h:29
JPL_INLINE constexpr Vec2 & Normalize() noexcept
Definition MinimalVec2.h:37
Definition SIMD.h:207
Minimal 4-wide 32-bit float vector implementation for SIMD.
Definition SIMD.h:60
static JPL_INLINE simd c_1() noexcept
Definition SIMD.h:461
static JPL_INLINE simd c_0p5() noexcept
Definition SIMD.h:466
static JPL_INLINE simd zero() noexcept
Vector with all zeros.
Definition SIMD.h:541
static JPL_INLINE simd select(const simd_mask &mask, const simd &a, const simd &b) noexcept
Component-wise select, returns 'a' if mask is true, 'b' otherwise.
Definition SIMD.h:1034