44 template <
class T,
class... Types>
45 concept AnyOf = (std::same_as<T, Types> || ...);
47 template<
class EncodedType_>
71 template <
size_t BitsPerAxis>
74 template <
size_t BitsPerAxis>
85 return 4.0 / (
static_cast<double>((1ULL << bitsPerAxis)) - 1.0);
89 [[nodiscard]] JPL_INLINE
constexpr double MaxVectorError(
size_t bitsPerAxis)
noexcept
97 template<Octahedron::CPrecision Precision>
98 class OctahedronEncoding;
109 template<Octahedron::CPrecision Precision>
117 using FloatType = std::conditional_t<(Precision::cBitsPerAxis > 23),
double,
float>;
157 template<std::
integral T>
158 static JPL_INLINE
constexpr bool IsValidCode(T code)
noexcept;
162 template<std::
integral T>
167 template<std::
integral T>
171 template<std::
integral T>
172 static JPL_INLINE
constexpr auto SanitizeCode(T code)
noexcept;
207 const float pySignQuarter =
Math::Sign2(dir.Y) * 0.25f;
208 return Math::FMA(-pySignQuarter, dir.X, (0.5f + pySignQuarter));
221 const float pSignN = -pSign;
254#define JPL_OCTAHEDRON_SPECIAL_CASE_HANDLING 0
258 template<Octahedron::CPrecision Precision>
259 template<std::
integral T>
262 return x != cAxisMask && y != cAxisMask;
265 template<Octahedron::CPrecision Precision>
266 template<std::
integral T>
269 const auto x =
static_cast<EncodedType>(code) & cAxisMask;
270 const auto y = (
static_cast<EncodedType>(code) >> cBitsPerAxis) & cAxisMask;
271 return AreValidComponents(x, y);
274 template<Octahedron::CPrecision Precision>
275 template<std::
integral T>
278 return (std::min(
EncodedType(y), cMaxComponentValue) << cBitsPerAxis) | std::min(
EncodedType(x), cMaxComponentValue);
281 template<Octahedron::CPrecision Precision>
282 template<std::
integral T>
287 return static_cast<T
>((std::min(y, cMaxComponentValue) << cBitsPerAxis) | std::min(x, cMaxComponentValue));
290 template<Octahedron::CPrecision Precision>
294 const Vec3 dirAbs = Abs(direction);
308#if JPL_OCTAHEDRON_SPECIAL_CASE_HANDLING
311 constexpr auto cPoleEps =
FloatType(1e-6);
312 const bool isPole = (dirAbs.X <= cPoleEps) & (dirAbs.Y <= cPoleEps);
314 return (direction.Z <
FloatType(0.0)) ? cPoleNegZ : cPolePosZ;
323 if (
GetZ(direction) < 0.0f)
342 return (dy << cBitsPerAxis) | dx;
345 template<Octahedron::CPrecision Precision>
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;
360 const simd invL1Norm = one / L1Norm;
361 simd px = dirX * invL1Norm;
362 simd py = dirY * invL1Norm;
366 const simd_mask zIsLessThanZero = dirZ < zero;
367 const simd tempPx = px;
373 static const simd cHalfTexel = zero_p5 /
simd(cAxisMask);
374 px =
clamp(px, -one + cHalfTexel, one - cHalfTexel);
375 py =
clamp(py, -one + cHalfTexel, one - cHalfTexel);
378 const simd v0 = px * zero_p5 + zero_p5;
379 const simd v1 = py * zero_p5 + zero_p5;
382 const auto dx = (
floor(v0 *
simd(cAxisMask))).to_mask();
383 const auto dy = (
floor(v1 *
simd(cAxisMask))).to_mask();
385 return (dy << cBitsPerAxis) | dx;
388 template<Octahedron::CPrecision Precision>
395 EncodedType dy = (encodedDirection >> cBitsPerAxis) & cAxisMask;
398 dx = std::min(dx, cMaxComponentValue);
399 dy = std::min(dy, cMaxComponentValue);
401#if JPL_OCTAHEDRON_SPECIAL_CASE_HANDLING
404 if (encodedDirection == cPoleNegZ)
430 return Normalized(Vec3{
437 template<Octahedron::CPrecision Precision>
442 simd_mask dx = encodedDirection & cAxisMask;
443 simd_mask dy = (encodedDirection >> cBitsPerAxis) & cAxisMask;
446 static const simd_mask maxComponentValue(cMaxComponentValue);
447 dx =
min(dx, maxComponentValue);
448 dy =
min(dy, maxComponentValue);
457 const simd pxDecoded = -one + two * (dx.
to_simd() + zero_p5) * muInv;
458 const simd pyDecoded = -one + two * (dy.
to_simd() + zero_p5) * muInv;
463 one -
abs(pxDecoded) -
abs(pyDecoded)
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);
476 const simd lenSqr =
fma(outX, outX,
fma(outY, outY, outZ * outZ));
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);
501 template<
class VectorType>
502 constexpr bool TestOctahedron(
const VectorType& original)
504 const VectorType normalizedOriginal = Normalized(original);
505 const auto encoded = EncoderType::Encode(normalizedOriginal);
506 const VectorType decoded = EncoderType::Decode<VectorType>(encoded);
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;
519 static_assert(TestOctahedron(
MinimalVec3(0.0f, 0.0f, 1.0f)));
521 static_assert(TestOctahedron(
MinimalVec3(0.0f, 0.0f, -1.0f)));
524 static_assert(TestOctahedron(
MinimalVec3(1.0f, 0.0f, 0.0f)));
526 static_assert(TestOctahedron(
MinimalVec3(-1.0f, 0.0f, 0.0f)));
529 static_assert(TestOctahedron(
MinimalVec3(0.0f, 1.0f, 0.0f)));
531 static_assert(TestOctahedron(
MinimalVec3(0.0f, -1.0f, 0.0f)));
536 static_assert(TestOctahedron(
MinimalVec3(1.0f, 1.0f, 0.0f)));
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)));
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)));
549 static_assert(TestOctahedron(
MinimalVec3(1.0f, 1.0f, 1.0f)));
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)));
558 static_assert(TestOctahedron(
MinimalVec3(1.0f, 1.0f, -1.0f)));
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)));
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)));
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)));
578 static_assert(TestOctahedron(
MinimalVec3(0.0f, 0.0995037183f, -0.995037138f)));
583 static constexpr float dir2DEncoded =
ToDiamond(dir2D);
#define JPL_ASSERT(inExpression,...)
Main assert macro, usage: JPL_ASSERT(condition, message) or JPL_ASSERT(condition)
Definition ErrorReporting.h:80
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
std::uint32_t uint32
Definition Core.h:311
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
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
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
constexpr std::size_t BitWidthOf() noexcept
Definition Bits.h:58
OctahedronEncoding< Octahedron::Precision8bits > Octahedron16Bit
Definition DirectionEncoding.h:103
Definition MinimalVec3.h:37
float Y
Definition MinimalVec3.h:43
float Z
Definition MinimalVec3.h:43
float X
Definition MinimalVec3.h:43
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
float X
Definition MinimalVec2.h:30
float Y
Definition MinimalVec2.h:31
JPL_INLINE simd to_simd() const noexcept
Convert each component from an int to a float.
Definition SIMD.h:1610
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