Photon Engine 2.0.0-beta
A physically based renderer.
Loading...
Searching...
No Matches
math.h
Go to the documentation of this file.
1#pragma once
2
12#include "Math/constant.h"
13#include "Math/math_fwd.h"
14#include "Math/math_table.h"
15#include "Utility/utility.h"
16#include "Utility/traits.h"
17#include "Utility/TSpan.h"
18
19#include <Common/assertion.h>
20#include <Common/primitive_type.h>
21#include <Common/compiler.h>
22#include <Common/math_basics.h>
23#include <Common/utility.h>
24
25#include <cmath>
26#include <algorithm>
27#include <numeric>
28#include <type_traits>
29#include <utility>
30#include <limits>
31#include <array>
32#include <vector>
33#include <cstdint>
34#include <climits>
35#include <bit>
36#include <concepts>
37
38#if PH_COMPILER_IS_MSVC
39#include <intrin.h>
40#pragma intrinsic(_BitScanReverse)
41#pragma intrinsic(_umul128)
42#endif
43
44namespace ph::math
45{
46
47template<typename T>
48inline auto matrix2x2(const T e00, const T e01, const T e10, const T e11)
49 -> std::array<std::array<T, 2>, 2>
50{
51 return
52 {{
53 {{e00, e01}},
54 {{e10, e11}}
55 }};
56}
57
58template<typename T>
59inline T squared(const T value)
60{
61 return value * value;
62}
63
64// A fast, without sqrt(), nearly branchless method. Notice that Photon uses y-axis as the up/normal vector.
65// This static method implements the y-is-normal version which is different from the original paper.
66// (Reference: Frisvad, Jeppe Revall, "Building an Orthonormal Basis from a 3D Unit Vector Without Normalization",
67// Journal of Graphics Tools, 2012)
68// Note: This method seems to have larger numerical error (at least 10^3 larger than my naive method),
69// thus it is not used currently.
70//
71void form_orthonormal_basis_frisvad(const Vector3R& unitYaxis, Vector3R* const out_unitXaxis, Vector3R* const out_unitZaxis);
72
76template<typename T>
77inline T clamp(const T value, const T lowerBound, const T upperBound)
78{
79 // VS does not like integers in `std::isnan()`,
80 // see: https://stackoverflow.com/questions/61646166/how-to-resolve-fpclassify-ambiguous-call-to-overloaded-function
81 if constexpr(std::is_floating_point_v<T>)
82 {
83 PH_ASSERT(!std::isnan(value));
84 PH_ASSERT(!std::isnan(lowerBound));
85 PH_ASSERT(!std::isnan(upperBound));
86 }
87
88 PH_ASSERT_LE(lowerBound, upperBound);
89
90 return std::clamp(value, lowerBound, upperBound);
91}
92
97template<typename T>
98inline T safe_clamp(const T value, const T lowerBound, const T upperBound)
99{
100 // VS does not like integers in `std::isnan()`,
101 // see: https://stackoverflow.com/questions/61646166/how-to-resolve-fpclassify-ambiguous-call-to-overloaded-function
102 if constexpr(std::is_floating_point_v<T>)
103 {
104 PH_ASSERT(!std::isnan(lowerBound));
105 PH_ASSERT(!std::isnan(upperBound));
106 }
107
108 PH_ASSERT_LE(lowerBound, upperBound);
109
110 return std::isfinite(value) ? std::clamp(value, lowerBound, upperBound) : lowerBound;
111}
112
116template<typename T>
117inline T safe_rcp(const T value)
118{
119 if constexpr(std::is_floating_point_v<T>)
120 {
121 return std::isfinite(value) && value != 0 ? static_cast<T>(1) / value : static_cast<T>(0);
122 }
123 else
124 {
125 return std::abs(value) == 1 ? value : static_cast<T>(0);
126 }
127}
128
131template<typename T>
132inline T to_degrees(const T radians)
133{
134 return radians * (constant::rcp_pi<T> * T(180));
135}
136
139template<typename T>
140inline T to_radians(const T degrees)
141{
142 return degrees * (constant::pi<T> / T(180));
143}
144
153template<typename T>
154inline int sign(const T value)
155{
156 return (static_cast<T>(0) < value) - (static_cast<T>(0) > value);
157}
158
166// TODO: templatize
167inline uint32 next_power_of_2(uint32 value)
168{
169 PH_ASSERT(value <= (1UL << 31));
170
171 --value;
172
173 value |= value >> 1;
174 value |= value >> 2;
175 value |= value >> 4;
176 value |= value >> 8;
177 value |= value >> 16;
178
179 return value + 1;
180}
181
186template<typename T>
187inline T log2_floor(const T value)
188{
189 if constexpr(std::is_floating_point_v<T>)
190 {
191 PH_ASSERT_GT(value, T(0));
192
193 return static_cast<T>(std::floor(std::log2(value)));
194 }
195 else
196 {
197 constexpr T NUM_BITS = sizeof(T) * CHAR_BIT;
198
199 static_assert(std::is_integral_v<T>);
200
201 PH_ASSERT_GT(value, T(0));
202
203#if PH_COMPILER_IS_MSVC
204
205 unsigned long first1BitFromLeftIndex = std::numeric_limits<unsigned long>::max();
206 if constexpr(sizeof(T) <= sizeof(unsigned long))
207 {
208 const auto isIndexSet = _BitScanReverse(
209 &first1BitFromLeftIndex,
210 static_cast<unsigned long>(value));
211 PH_ASSERT_GT(isIndexSet, 0);
212 }
213 else
214 {
215 static_assert(sizeof(T) <= sizeof(unsigned __int64),
216 "size of T is too large");
217
218 const auto isIndexSet = _BitScanReverse64(
219 &first1BitFromLeftIndex,
220 static_cast<unsigned __int64>(value));
221 PH_ASSERT_GT(isIndexSet, 0);
222 }
223 PH_ASSERT_IN_RANGE_INCLUSIVE(first1BitFromLeftIndex, T(0), NUM_BITS - 1);
224
225 return static_cast<T>(first1BitFromLeftIndex);
226
227#elif PH_COMPILER_IS_CLANG || PH_COMPILER_IS_GNU
228
229 if constexpr(sizeof(T) <= sizeof(unsigned int))
230 {
231 const int numLeftZeros = __builtin_clz(static_cast<unsigned int>(value));
232 return static_cast<T>(sizeof(unsigned int) * CHAR_BIT - 1 - numLeftZeros);
233 }
234 else if constexpr(sizeof(T) <= sizeof(unsigned long))
235 {
236 const int numLeftZeros = __builtin_clzl(static_cast<unsigned long>(value));
237 return static_cast<T>(sizeof(unsigned long) * CHAR_BIT - 1 - numLeftZeros);
238 }
239 else
240 {
241 static_assert(sizeof(T) <= sizeof(unsigned long long),
242 "size of T is too large");
243
244 const int numLeftZeros = __builtin_clzll(static_cast<unsigned long long>(value));
245 return static_cast<T>(sizeof(unsigned long long) * CHAR_BIT - 1 - numLeftZeros);
246 }
247
248#endif
249 }
250}
251
257template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
258inline T fractional_part(const T value)
259{
260 long double integralPart;
261 return static_cast<T>(std::modf(static_cast<long double>(value), &integralPart));
262}
263
267template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
268inline T wrap(T value, const T lowerBound, const T upperBound)
269{
270 PH_ASSERT_GE(upperBound, lowerBound);
271
272 const T rangeSize = upperBound - lowerBound + static_cast<T>(1);
273
274 if(value < lowerBound)
275 {
276 value += rangeSize * ((lowerBound - value) / rangeSize + static_cast<T>(1));
277
278 // Possibly fail if <value> overflow
279 PH_ASSERT_GE(value, lowerBound);
280 }
281
282 return lowerBound + (value - lowerBound) % rangeSize;
283}
284
288template<typename T>
289inline bool is_even(const T value)
290{
291 static_assert(std::is_integral_v<T>,
292 "T must be an integer type.");
293
294 return value % 2 == 0;
295}
296
300template<typename T>
301inline bool is_odd(const T value)
302{
303 return !is_even(value);
304}
305
309bool is_same_hemisphere(const Vector3R& vector, const Vector3R& N);
310
316bool is_opposite_hemisphere(const Vector3R& vector, const Vector3R& N);
317
321template<typename T, std::size_t N>
322T summation(const std::array<T, N>& values, T initialValue = 0);
323
324template<typename T>
325T summation(const std::vector<T>& values, T initialValue = 0);
326
327template<typename T, std::size_t EXTENT = std::dynamic_extent>
328T summation(TSpanView<T, EXTENT> values, T initialValue = 0);
330
334template<typename T, std::size_t N>
335T product(const std::array<T, N>& values, T initialValue = 1);
336
337template<typename T>
338T product(const std::vector<T>& values, T initialValue = 1);
339
340template<typename T, std::size_t EXTENT = std::dynamic_extent>
341T product(TSpanView<T, EXTENT> values, T initialValue = 1);
343
344template<typename NumberType>
345inline constexpr NumberType bytes_to_KiB(const std::size_t numBytes)
346{
347 return static_cast<NumberType>(numBytes) / static_cast<NumberType>(constant::KiB);
348}
349
350template<typename NumberType>
351inline constexpr NumberType bytes_to_MiB(const std::size_t numBytes)
352{
353 return static_cast<NumberType>(numBytes) / static_cast<NumberType>(constant::MiB);
354}
355
356template<typename NumberType>
357inline constexpr NumberType bytes_to_GiB(const std::size_t numBytes)
358{
359 return static_cast<NumberType>(numBytes) / static_cast<NumberType>(constant::GiB);
360}
361
362template<typename NumberType>
363inline constexpr NumberType bytes_to_TiB(const std::size_t numBytes)
364{
365 return static_cast<NumberType>(numBytes) / static_cast<NumberType>(constant::TiB);
366}
367
368template<typename NumberType>
369inline constexpr NumberType bytes_to_PiB(const std::size_t numBytes)
370{
371 return static_cast<NumberType>(numBytes) / static_cast<NumberType>(constant::PiB);
372}
373
379inline std::pair<std::size_t, std::size_t> ith_evenly_divided_range(
380 const std::size_t rangeIndex,
381 const std::size_t totalSize,
382 const std::size_t numDivisions)
383{
384 // TODO: it is possible to generalize to signed range
385 // maybe use ith_evenly_divided_size() as function name and
386 // ith_evenly_divided_range() for signed/unsigned range
387
388 PH_ASSERT_GT(numDivisions, 0);
389 PH_ASSERT_LT(rangeIndex, numDivisions);
390
391 return
392 {
393 rangeIndex * totalSize / numDivisions,
394 (rangeIndex + 1) * totalSize / numDivisions
395 };
396}
397
407inline float fast_rcp_sqrt(float x)
408{
409 // TODO:
410 // 1. implement double version in the reference paper and templatize iteration step
411 // 2. is more iteration steps better than doing it in double
412 // 3. establish a standard, e.g., fast_<X>() is guaranteed to have max. rel. error < 1%
413
414 PH_ASSERT_GT(x, 0.0f);
415
416 const float halvedInput = 0.5f * x;
417
418 // Gives initial guess for later refinements
419 auto bits = bitwise_cast<std::uint32_t>(x);
420 bits = 0x5F375A86 - (bits >> 1);
421 x = bitwise_cast<float>(bits);
422
423 // Newton's method, each iteration increases accuracy.
424
425 // Iteration 1, max. relative error < 0.175125%
426 x = x * (1.5f - halvedInput * x * x);
427
428 // Iteration 2, disabled since <x> is already good enough
429 //x = x * (1.5f - halvedInput * x * x);
430
431 return x;
432}
433
436inline float fast_sqrt(const float x)
437{
438 return fast_rcp_sqrt(x) * x;
439}
440
447template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
448inline T reverse_bits(const T value)
449{
450 using namespace detail;
451
452 static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>,
453 "Unsupported type detected");
454
455 constexpr std::size_t NUM_BITS = sizeof(T) * CHAR_BIT;
456
457 // Note that arbitrary number of bits can be supported by careful handling of bitwise operations
458 static_assert(NUM_BITS % 8 == 0,
459 "Non-multiple-of-8 integer bits are not supported");
460
461 if constexpr(NUM_BITS == 8)
462 {
463 return T(table::detail::BITS8_REVERSE[value]);
464 }
465 else if constexpr(NUM_BITS == 16)
466 {
467 return (T(table::detail::BITS8_REVERSE[value & 0xFF]) << 8) |
468 (T(table::detail::BITS8_REVERSE[(value >> 8) & 0xFF]));
469 }
470 else if constexpr(NUM_BITS == 32)
471 {
472 return (T(table::detail::BITS8_REVERSE[value & 0xFF]) << 24) |
473 (T(table::detail::BITS8_REVERSE[(value >> 8) & 0xFF]) << 16) |
474 (T(table::detail::BITS8_REVERSE[(value >> 16) & 0xFF]) << 8) |
475 (T(table::detail::BITS8_REVERSE[(value >> 24) & 0xFF]));
476 }
477 else
478 {
479 static_assert(NUM_BITS == 64);
480
481 return (T(table::detail::BITS8_REVERSE[value & 0xFF]) << 56) |
482 (T(table::detail::BITS8_REVERSE[(value >> 8) & 0xFF]) << 48) |
483 (T(table::detail::BITS8_REVERSE[(value >> 16) & 0xFF]) << 40) |
484 (T(table::detail::BITS8_REVERSE[(value >> 24) & 0xFF]) << 32) |
485 (T(table::detail::BITS8_REVERSE[(value >> 32) & 0xFF]) << 24) |
486 (T(table::detail::BITS8_REVERSE[(value >> 40) & 0xFF]) << 16) |
487 (T(table::detail::BITS8_REVERSE[(value >> 48) & 0xFF]) << 8) |
488 (T(table::detail::BITS8_REVERSE[(value >> 56) & 0xFF]));
489 }
490}
491
496template<std::unsigned_integral UIntType, std::integral RangeType>
497inline UIntType set_bits_in_range(const UIntType bits, const RangeType beginBitIdx, const RangeType endBitIdx)
498{
499 // Inclusive as empty range is allowed, e.g., `beginBitIdx` == `endBitIdx`
500 PH_ASSERT_IN_RANGE_INCLUSIVE(beginBitIdx, 0, endBitIdx);
501 PH_ASSERT_IN_RANGE_INCLUSIVE(endBitIdx, beginBitIdx, sizeof_in_bits<UIntType>());
502
503 // Mask for the bits in range. Constructed by producing required number of 1's then shift
504 // by <beginBitIdx>.
505 // Note that if there is integer promotion to signed types, it should be fine--promoted type
506 // must be wider than the original unsigned type, the bit shifts will never reach sign bit.
507 //
508 const auto bitMask = static_cast<UIntType>(((UIntType(1) << (endBitIdx - beginBitIdx)) - 1) << beginBitIdx);
509
510 return bits | bitMask;
511}
512
517template<std::unsigned_integral UIntType, std::integral RangeType>
518inline UIntType clear_bits_in_range(const UIntType bits, const RangeType beginBitIdx, const RangeType endBitIdx)
519{
520 const auto bitMask = set_bits_in_range<UIntType, RangeType>(UIntType(0), beginBitIdx, endBitIdx);
521 return bits & (~bitMask);
522}
523
528template<std::unsigned_integral UIntType>
529inline UIntType flag_bit(const UIntType bitIdx)
530{
531 PH_ASSERT_IN_RANGE(bitIdx, 0, sizeof_in_bits<UIntType>());
532
533 return static_cast<UIntType>(1) << bitIdx;
534}
535
536template<std::unsigned_integral UIntType, UIntType BIT_IDX>
537inline consteval UIntType flag_bit()
538{
539 static_assert(0 <= BIT_IDX && BIT_IDX < sizeof_in_bits<UIntType>(),
540 "BIT_IDX must be within [0, # bits in UIntType)");
541
542 return static_cast<UIntType>(1) << BIT_IDX;
543}
545
546template<typename T, T MIN, T MAX, std::size_t N>
547inline std::array<T, N> evenly_spaced_array()
548{
549 static_assert(MAX > MIN);
550 static_assert(N >= 2);
551
552 constexpr T TOTAL_SIZE = MAX - MIN;
553
554 std::array<T, N> arr;
555 arr[0] = MIN;
556 for(std::size_t i = 1; i < N - 1; ++i)
557 {
558 // Calculate current fraction i/(N-1), and multiply it with the total size
559 // (we multiply total size with i fist, otherwise integral types will always result in 0)
560 arr[i] = MIN + static_cast<T>(i * TOTAL_SIZE / (N - 1));
561
562 PH_ASSERT_IN_RANGE_INCLUSIVE(arr[i], MIN, MAX);
563 }
564 arr[N - 1] = MAX;
565
566 return arr;
567}
568
569template<typename T>
570inline std::vector<T> evenly_spaced_vector(const T min, const T max, const std::size_t n)
571{
572 PH_ASSERT_GT(max, min);
573 PH_ASSERT_GE(n, 2);
574
575 const T totalSize = max - min;
576
577 std::vector<T> vec(n, 0);
578 vec[0] = min;
579 for(std::size_t i = 0; i < n; ++i)
580 {
581 // Calculate current fraction i/(n-1), and multiply it with the total size
582 // (we multiply total size with i fist, otherwise integral types will always result in 0)
583 vec[i] = min + static_cast<T>(i * totalSize / (n - 1));
584
585 PH_ASSERT_IN_RANGE_INCLUSIVE(vec[i], min, max);
586 }
587 vec[n - 1] = max;
588 return vec;
589}
590
598
600inline uint16 fp32_to_fp16_bits(const float32 value)
601{
602 // TODO: handle denormalized value and others (std::frexp seems interesting)
603 // https://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java
604
605 static_assert(std::numeric_limits<float32>::is_iec559);
606
607 if(std::abs(value) < 0.0000610352f)
608 {
609 return std::signbit(value) ? 0x8000 : 0x0000;
610 }
611
612 uint16 fp16Bits;
613 uint32 fp32Bits;
614
615 fp32Bits = std::bit_cast<uint32>(value);
616
617 // truncate the mantissa and doing (exp - 127 + 15)
618 fp16Bits = static_cast<uint16>((((fp32Bits & 0x7FFFFFFF) >> 13) - (0x38000000 >> 13)));
619 fp16Bits |= ((fp32Bits & 0x80000000) >> 16);// sign
620
621 return fp16Bits;
622}
623
626inline float32 fp16_bits_to_fp32(const uint16 fp16Bits)
627{
628 // TODO: handle denormalized value and others (std::frexp seems interesting)
629 // https://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java
630
631 static_assert(std::numeric_limits<float32>::is_iec559);
632
633 // 0 if all bits other than the sign bit are 0
634 if((fp16Bits & 0x7FFF) == 0)
635 {
636 return (fp16Bits & 0x8000) == 0 ? 0.0f : -0.0f;
637 }
638
639 uint32 fp32Bits = ((fp16Bits & 0x8000) << 16);
640 fp32Bits |= (((fp16Bits & 0x7FFF) << 13) + 0x38000000);
641
642 return std::bit_cast<float32>(fp32Bits);
643}
645
651// TODO: numeric analysis
652// TODO: long double is not needed for shorter integer types
653template<std::floating_point FloatType, std::integral IntType>
654inline FloatType normalize_integer(const IntType intVal)
655{
656 if constexpr(std::is_unsigned_v<IntType>)
657 {
658 return static_cast<FloatType>(
659 static_cast<long double>(intVal) / std::numeric_limits<IntType>::max());
660 }
661 else
662 {
663 return static_cast<FloatType>(intVal >= 0
664 ? static_cast<long double>(intVal) / std::numeric_limits<IntType>::max()
665 : -static_cast<long double>(intVal) / std::numeric_limits<IntType>::min());
666 }
667}
668
669// TODO: numeric analysis
670// TODO: long double is not needed for shorter integer types
671template<std::integral IntType, std::floating_point FloatType>
672inline IntType quantize_normalized_float(const FloatType floatVal)
673{
674 if constexpr(std::is_unsigned_v<IntType>)
675 {
676 PH_ASSERT_IN_RANGE_INCLUSIVE(floatVal, 0.0f, 1.0f);
677
678 return static_cast<IntType>(
679 std::round(static_cast<long double>(floatVal) * std::numeric_limits<IntType>::max()));
680 }
681 else
682 {
683 PH_ASSERT_IN_RANGE_INCLUSIVE(floatVal, -1.0f, 1.0f);
684
685 return static_cast<IntType>(floatVal >= 0
686 ? std::round( static_cast<long double>(floatVal) * std::numeric_limits<IntType>::max())
687 : std::round(-static_cast<long double>(floatVal) * std::numeric_limits<IntType>::min()));
688 }
689}
690
695inline void uint64_mul(const uint64 lhs, const uint64 rhs, uint64& out_high64, uint64& out_low64)
696{
697#if defined(__SIZEOF_INT128__)
698 const auto result128 = __uint128_t(lhs) * __uint128_t(rhs);
699 out_high64 = static_cast<uint64>(result128 >> 64);
700 out_low64 = static_cast<uint64>(result128);
701#elif PH_COMPILER_IS_MSVC
702 out_low64 = _umul128(lhs, rhs, &out_high64);
703#else
704 /*
705 Divide each 64-bit number into two 32-bit parts, then multiplying (or adding) any 32-bit
706 pairs cannot overflow if done using 64-bit arithmetic. If we multiply them using base 2^32
707 arithmetic, a total of 4 products must be shifted and added to obtain the final result.
708 Here is a simple illustration of the algorithm:
709
710 lhs = [ a ][ b ]
711 rhs = [ c ][ d ]
712
713 Treat this like performing a regular multiplication on paper. Note that the multiplication
714 is done in base 2^32:
715
716 [ a ][ b ]
717 x [ c ][ d ]
718 -------------------
719 [a*d][b*d]
720 + [a*c][b*c]
721 -------------------
722
723 The overflowing part of each partial product is the carry (32-bit carry). This is a good reference:
724 https://stackoverflow.com/questions/26852435/reasonably-portable-way-to-get-top-64-bits-from-64x64-bit-multiply
725 */
726
727 const uint64 a = lhs >> 32, b = lhs & 0xFFFFFFFF;
728 const uint64 c = rhs >> 32, d = rhs & 0xFFFFFFFF;
729
730 const uint64 ac = a * c;
731 const uint64 bc = b * c;
732 const uint64 ad = a * d;
733 const uint64 bd = b * d;
734
735 // This is the middle part of the above illustration, which is a sum of three 32-bit numbers
736 // (so it is 34-bit at most): two products b*c and a*d, and the carry from b*d
737 const uint64 mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
738
739 out_high64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
740 out_low64 = (mid34 << 32) | (bd & 0xFFFFFFFF);
741#endif
742}
743
747template<typename T, std::size_t N>
748T length(const std::array<T, N>& vec);
749
750template<typename T>
751T length(const std::vector<T>& vec);
752
753template<typename T, std::size_t EXTENT = std::dynamic_extent>
756
760template<typename T, std::size_t N>
761T length_squared(const std::array<T, N>& vec);
762
763template<typename T>
764T length_squared(const std::vector<T>& vec);
765
766template<typename T, std::size_t EXTENT = std::dynamic_extent>
769
773template<typename T, std::size_t N>
774T p_norm(const std::array<T, N>& vec);
775
776template<typename T>
777T p_norm(const std::vector<T>& vec);
778
779template<std::size_t P, typename T, std::size_t EXTENT = std::dynamic_extent>
782
788template<typename T, std::size_t N>
789void normalize(std::array<T, N>& vec);
790
791template<typename T>
792void normalize(std::vector<T>& vec);
793
794template<typename T, std::size_t EXTENT = std::dynamic_extent>
797
801template<typename T, std::size_t N>
802T dot_product(const std::array<T, N>& vecA, const std::array<T, N>& vecB);
803
804template<typename T>
805T dot_product(const std::vector<T>& vecA, const std::vector<T>& vecB);
806
807template<typename T, std::size_t EXTENT = std::dynamic_extent>
810
811}// end namespace ph::math
812
813#include "Math/math.ipp"
constexpr T rcp_pi
Value of .
Definition constant.h:21
constexpr std::size_t MiB
Definition constant.h:80
constexpr std::size_t GiB
Definition constant.h:81
constexpr std::size_t PiB
Definition constant.h:83
constexpr std::size_t TiB
Definition constant.h:82
constexpr T pi
Value of .
Definition constant.h:15
constexpr std::size_t KiB
Definition constant.h:79
constexpr std::array< unsigned char, 256 > BITS8_REVERSE
Definition math_table.h:353
Math functions and utilities.
Definition TransformInfo.h:10
T safe_clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound], fallback to lowerBound. If a floating-point value is non-...
Definition math.h:98
consteval UIntType flag_bit()
Definition math.h:537
bool is_odd(const T value)
Checks if a number is odd.
Definition math.h:301
T dot_product(const std::array< T, N > &vecA, const std::array< T, N > &vecB)
Treating input values as vectors and calculates their dot product.
Definition math.ipp:206
float32 fp16_bits_to_fp32(const uint16 fp16Bits)
Convert a 16-bit representation back to 32-bit float.
Definition math.h:626
void form_orthonormal_basis_frisvad(const Vector3R &unitYaxis, Vector3R *const out_unitXaxis, Vector3R *const out_unitZaxis)
Definition math.cpp:13
T safe_rcp(const T value)
Calculates the reciprocal of a value, fallback to 0. If the resulting reciprocal is non-finite (e....
Definition math.h:117
T length_squared(const std::array< T, N > &vec)
Treating input values as a vector and calculates its squared length.
Definition math.ipp:68
T reverse_bits(const T value)
Get an integral value with reversed bits.
Definition math.h:448
constexpr NumberType bytes_to_GiB(const std::size_t numBytes)
Definition math.h:357
uint32 next_power_of_2(uint32 value)
Gets the minimum power of 2 value that is >= value.
Definition math.h:167
constexpr NumberType bytes_to_TiB(const std::size_t numBytes)
Definition math.h:363
T to_degrees(const T radians)
Convert radians to degrees.
Definition math.h:132
bool is_even(const T value)
Checks if a number is even.
Definition math.h:289
T squared(const T value)
Definition math.h:59
IntType quantize_normalized_float(const FloatType floatVal)
Definition math.h:672
T summation(const std::array< T, N > &values, T initialValue=0)
Sum all values within a container together.
Definition math.ipp:9
FloatType normalize_integer(const IntType intVal)
Transform an integer to the range [0, 1] ([-1, 1] for signed integer). When transforming an unsigned ...
Definition math.h:654
T wrap(T value, const T lowerBound, const T upperBound)
Wraps an integer around [lower-bound, upper-bound]. For example, given a bound [-1,...
Definition math.h:268
void normalize(std::array< T, N > &vec)
Treating input values as a vector and normalize it. Notice that normalizing a integer typed vector wi...
Definition math.ipp:142
T p_norm(const std::array< T, N > &vec)
Treating input values as a vector and calculates its p-norm.
Definition math.ipp:91
constexpr NumberType bytes_to_MiB(const std::size_t numBytes)
Definition math.h:351
T length(const std::array< T, N > &vec)
Treating input values as a vector and calculates its length.
Definition math.ipp:50
std::array< T, N > evenly_spaced_array()
Definition math.h:547
T product(const std::array< T, N > &values, T initialValue=1)
Multiplies all values within a container together.
Definition math.ipp:27
int sign(const T value)
Extract the sign of value.
Definition math.h:154
std::pair< std::size_t, std::size_t > ith_evenly_divided_range(const std::size_t rangeIndex, const std::size_t totalSize, const std::size_t numDivisions)
Gets the i-th evenly divided range.
Definition math.h:379
bool is_same_hemisphere(const Vector3R &vector, const Vector3R &N)
Checks whether the specified vector is within the hemisphere defined by the normal vector N....
Definition math.cpp:42
T to_radians(const T degrees)
Convert degrees to radians.
Definition math.h:140
uint16 fp32_to_fp16_bits(const float32 value)
Convert a 32-bit float to 16-bit representation.
Definition math.h:600
T fractional_part(const T value)
Retrieve the fractional part of value (with the sign unchanged). The result is not guaranteed to be t...
Definition math.h:258
std::vector< T > evenly_spaced_vector(const T min, const T max, const std::size_t n)
Definition math.h:570
void uint64_mul(const uint64 lhs, const uint64 rhs, uint64 &out_high64, uint64 &out_low64)
Multiply two unsigined 64-bit numbers and get a 128-bit result.
Definition math.h:695
constexpr NumberType bytes_to_PiB(const std::size_t numBytes)
Definition math.h:369
UIntType set_bits_in_range(const UIntType bits, const RangeType beginBitIdx, const RangeType endBitIdx)
Set bits in the range to 1. The bits in [beginBitIdx, endBitIdx) will be set to 1,...
Definition math.h:497
float fast_rcp_sqrt(float x)
Computes 1/sqrt(x) in a fast but approximative way.
Definition math.h:407
constexpr NumberType bytes_to_KiB(const std::size_t numBytes)
Definition math.h:345
float fast_sqrt(const float x)
Computes sqrt(x) in a fast but approximative way.
Definition math.h:436
bool is_opposite_hemisphere(const Vector3R &vector, const Vector3R &N)
Checks whether the specified vector is within the hemisphere defined by the normal vector -N....
Definition math.cpp:49
T log2_floor(const T value)
Calculate a positive number's base 2 logarithm (floored).
Definition math.h:187
auto matrix2x2(const T e00, const T e01, const T e10, const T e11) -> std::array< std::array< T, 2 >, 2 >
Definition math.h:48
UIntType clear_bits_in_range(const UIntType bits, const RangeType beginBitIdx, const RangeType endBitIdx)
Set bits in the range to 0. The bits in [beginBitIdx, endBitIdx) will be set to 0,...
Definition math.h:518
T clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound]. None of value, lowerBound and upperBound can be NaN,...
Definition math.h:77
std::span< const T, EXTENT > TSpanView
Same as TSpan, except that the objects are const-qualified. Note that for pointer types,...
Definition TSpan.h:19
std::span< T, EXTENT > TSpan
A contiguous sequence of objects of type T. Effectively the same as std::span.
Definition TSpan.h:12
Target bitwise_cast(const Source &source)
Definition utility.h:126