-
Notifications
You must be signed in to change notification settings - Fork 3
/
lbvh.h
2080 lines (1821 loc) · 62.9 KB
/
lbvh.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// The MIT License (MIT)
//
// Copyright (c) 2020 Taylor Holberton and contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! @file lbvh.h LBVH Header
//!
//! @brief This header contains all the code required to build
//! and traverse an LBVH. It uses templates all throughout the
//! code base and is therefore easily optimized by compilers and
//! easily extended to multiple floating point types and primitive types.
//!
//! See @ref lbvh::builder on building an BVH from this library.
//! It will require you to write a lambda functor or function object
//! that computes the bounding box of your primitive. Primitives themselves
//! are defined all throughout the code as template types, so they can be
//! anything.
//!
//! As an extra bit of functionality, there's also a BVH traverser
//! class bundled with this header. See @ref lbvh::traverser for details.
//! It will require you to write a lambda functor or a function object
//! that computes an intersection between a ray and your primitive type.
#pragma once
#include <algorithm>
#include <limits>
#include <vector>
#if (__cplusplus >= 201703L) && !(defined LBVH_NO_THREADS)
#include <execution>
#endif
#ifndef LBVH_NO_THREADS
#include <thread>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include <cmath>
#include <cstdint>
namespace lbvh {
//! This is the type used for size values.
using size_type = std::size_t;
//! This class is used to associate certain
//! types with a type size.
template<size_type type_size>
struct associated_types final
{};
//! \brief This class is used to describe
//! the amount of work that a task is assigned.
struct work_division final
{
//! The starting index of the work amount.
size_type idx;
//! The maximum divisions of the work.
size_type max;
};
//! \brief This is a task scheduler that schedules
//! only the current thread for work. It's
//! a placeholder if the user doesn't specifier
//! their own thread pool library.
class single_thread_scheduler final
{
public:
//! Issues a new task to be performed.
//! In this class, the task immediately is called in the current thread.
template<typename task_type, typename... arg_types>
inline void operator()(task_type task, arg_types... args) noexcept
{
task(work_division{ 0, 1 }, args...);
}
//! Indicates the maximum number of threads
//! to be invoked at a time.
inline size_type max_threads() const noexcept { return 1; }
};
#ifndef LBVH_NO_THREADS
//! \brief This class is used to schedule
//! tasks to be run on several threads. It
//! is not a full-fledged thread pool, as would
//! be desired by real time rendering projects.
//!
//! However, it does improve the performance of
//! the library substantially if it is used.
//!
//! The only reason this thread scheduler is considered
//! to be "naive" is because it creates and destroys threads
//! at each call. Ideally, the threads would be in a thread pool.
class naive_thread_scheduler final
{
//! The maximum number of threads to run.
size_type max_thread_count;
public:
//! Constructs a new fake task scheduler.
//! \param max_threads_ The maximum number of threads to run.
naive_thread_scheduler(
size_type max_threads_ = std::thread::hardware_concurrency())
: max_thread_count(max_threads_ ? max_threads_ : 1)
{}
//! Schedules a new task to be completed.
//! \tparam task_type The type of the task functor.
//! \tparam arg_types The arguments to pass to the thread.
template<typename task_type, typename... arg_types>
void operator()(task_type task, arg_types... args)
{
std::vector<std::thread> threads;
for (size_type i = 0; i < max_thread_count - 1; i++) {
work_division div{ i, max_thread_count };
threads.emplace_back(task, div, args...);
}
task(work_division{ max_thread_count - 1, max_thread_count }, args...);
for (auto& th : threads) {
th.join();
}
}
//! Indicates to the library the maximum number of threads
//! that may be invoked at a time. This is useful for determining
//! how much memory to allocate for some functions.
//!
//! \return The max number of threads that may be invoked at a time.
inline size_type max_threads() const noexcept { return max_thread_count; }
};
//! A type definition that uses the
//! naive thread scheduler.
using default_scheduler = naive_thread_scheduler;
#else // LBVH_NO_THREADS
//! A type definition that uses the
//! single thread scheduler.
using default_scheduler = single_thread_scheduler;
#endif // LBVH_NO_THREADS
//! \brief Calculates the highest bit for an integer type.
//!
//! \return An integer with the highest bit set to one.
template<typename int_type>
inline constexpr int_type
highest_bit() noexcept
{
return int_type(1) << ((sizeof(int_type) * 8) - 1);
}
//! Represents a single 2D vector.
//!
//! \tparam scalar_type The type of the vector components.
template<typename scalar_type>
struct vec2 final
{
//! The X component of the vector.
scalar_type x;
//! The Y component of the vector.
scalar_type y;
};
//! Represents a single 3D vector.
//!
//! \tparam scalar_type The type of the vector components.
template<typename scalar_type>
struct vec3 final
{
//! The X component of the vector.
scalar_type x;
//! The Y component of the vector.
scalar_type y;
//! The Z component of the vector.
scalar_type z;
};
//! Represents a single axis-aligned bounding box.
//! \tparam scalar_type The scalar type used for vector values.
template<typename scalar_type>
struct aabb final
{
//! The minimum points of the box.
vec3<scalar_type> min;
//! The maximum points of the box.
vec3<scalar_type> max;
};
//! An internal node within the BVH.
//! Points to two other nodes, which
//! may either be leaf nodes or other internal nodes.
//!
//! \tparam scalar_type The type used for the bounding
//! box vectors of the node.
template<typename scalar_type>
struct alignas(sizeof(scalar_type) * 8) node final
{
//! The type definition for a node index.
using index_type = typename associated_types<sizeof(scalar_type)>::uint_type;
//! The box that contains the node.
aabb<scalar_type> box;
//! The index to the left node.
//! The highest bit may be set to
//! one if this is suppose to point to a leaf.
index_type left;
//! The index to the right node.
index_type right;
//! The highest bit may be set to
//! one if this is suppose to point to a leaf.
//! Accesses the left index as a leaf index.
inline constexpr index_type left_leaf_index() const noexcept
{
return left & (highest_bit<index_type>() - 1);
}
//! Accesses the right index as a leaf index.
inline constexpr index_type right_leaf_index() const noexcept
{
return right & (highest_bit<index_type>() - 1);
}
//! Indicates if the left index points to a leaf.
inline constexpr bool left_is_leaf() const noexcept
{
return left & highest_bit<index_type>();
}
//! Indicates if the right index points to a leaf.
inline constexpr bool right_is_leaf() const noexcept
{
return right & highest_bit<index_type>();
}
};
//! This is the structure for the LBVH.
//! \tparam float_type The floating point type used for box vectors.
template<typename float_type>
class bvh final
{
public:
//! A type definition for BVH nodes.
using node_type = node<float_type>;
//! A type definition for a BVH node vector.
using node_vec = std::vector<node_type>;
//! Constructs a BVH from prebuilt internal nodes.
bvh(node_vec&& nodes_)
: nodes(std::move(nodes_))
{}
//! Accesses the beginning iterator.
inline auto begin() const noexcept { return nodes.begin(); }
//! Accesses the ending iterator.
inline auto end() const noexcept { return nodes.end(); }
//! Indicates the number of internal nodes in the BVH.
inline auto size() const noexcept { return nodes.size(); }
//! Accesses a reference to a node within the BVH.
//! An exception is thrown if the index is out of bounds.
//! To access nodes without bounds checking, use the [] operator.
//!
//! \param index The index of the node to access.
//!
//! \return A const-reference to the specified node.
inline const node_type& at(size_type index) const { return nodes.at(index); }
//! Accesses a reference to a node within the BVH.
//! This function does not perform bounds checking.
//!
//! \param index The index of the node to access.
//!
//! \returns A const-reference to the box type.
inline const node_type& operator[](size_type index) const noexcept
{
return nodes[index];
}
private:
//! The internal nodes of the BVH.
node_vec nodes;
};
//! \brief This class is used for the constructing of BVHs.
//! It is meant to be the first class declared by anyone using the library.
//! It takes a set of primitives, as well as a function object to calculate
//! their bounding boxes, and generates an LBVH from them. The only function
//! required to be implemented is the function object that converts primitives
//! to bounding boxes.
//!
//! \tparam scalar_type The scalar type to use for the box vectors.
//!
//! \tparam task_scheduler The scheduler type to use for construction tasks.
//! By default, this is the type of scheduler provided by the library.
template<typename scalar_type, typename task_scheduler = default_scheduler>
class builder final
{
//! Is passed the various work items during BVH construction.
task_scheduler scheduler;
public:
//! A type definition for a BVH.
using bvh_type = bvh<scalar_type>;
//! A type definition for a BVH node.
using node_type = node<scalar_type>;
//! A type definition for a node vector.
using node_vec = std::vector<node_type>;
//! Constructs a new BVH builder.
//! \param scheduler_ The task scheduler to distribute the work with.
builder(task_scheduler scheduler_ = task_scheduler())
: scheduler(scheduler_)
{}
//! Builds a BVH from an array of primitives.
//!
//! \param primitives The array of primitives to build the BVH for.
//!
//! \param count The number of primitives in the primitive array.
//!
//! \param converter The primitive to bounding box converter.
//! This should be implemented by the caller as a function object
//! that takes a single primitive and returns an instance of @ref aabb
//! that represents the bounding box for that primitive.
//!
//! \return A BVH built for the specified primitives.
template<typename primitive, typename aabb_converter>
bvh_type operator()(const primitive* primitives,
size_type count,
const aabb_converter& converter);
protected:
//! Fits BVH nodes with their appropriate boxes.
template<typename primitive, typename aabb_converter>
void fit_boxes(node_vec& nodes,
const primitive* primitives,
const aabb_converter& converter);
};
//! \brief This structure contains basic information
//! regarding a ray intersection with a BVH. It stores the index of the
//! primitive that was intersected as well as intersection info given by the
//! primitive type.
template<typename scalar_type, typename intersection_info>
struct intersection_container final
{
//! A type definition for an index, used for tracking the intersected
//! primitive.
using index = typename associated_types<sizeof(scalar_type)>::uint_type;
/// The index of the primitive that was hit.
index primitive = std::numeric_limits<index>::max();
/// Additional information regarding the intersection.
intersection_info info;
intersection_container() = default;
intersection_container(index prim, intersection_info&& info_)
: primitive(prim)
, info(std::move(info_))
{}
/// Indicates whether or not this is a valid intersection.
constexpr operator bool() const noexcept
{
return primitive != std::numeric_limits<index>::max();
}
};
//! \brief Contains data for a single ray.
//!
//! \tparam scalar_type The type used for the vector components.
template<typename scalar_type>
struct ray final
{
//! A type definition for vectors used by the ray class.
using vec_type = vec3<scalar_type>;
//! The starting position of the ray.
vec_type pos;
//! The direction at which the ray is pointing at.
//! Usually, this is not normalized.
vec_type dir;
//! The minimum distance that intersections can be accepted.
scalar_type tmin = 0;
//! The maximum distance that intersections can be accepted.
scalar_type tmax = std::numeric_limits<scalar_type>::infinity();
};
//! \brief Represents a single ray with
//! precomputed acceleration data.
//!
//! \tparam scalar_type The scalar type of the vector components.
template<typename scalar_type>
struct accel_ray final
{
//! A type definition for the ray used by this structure.
using ray_type = ray<scalar_type>;
//! A type definition for vector types used by this structure.
using vec3_type = vec3<scalar_type>;
//! The type to use for octant indices.
using index_type = typename associated_types<sizeof(scalar_type)>::uint_type;
//! The original ray from which the acceleration
//! data was computed.
ray_type r;
//! The reciprocal direction vector.
vec3_type rcp_dir;
//! The inverse position vector.
vec3_type inv_pos;
//! The indices for the octant that the ray falls into.
index_type octants[3];
//! The inverse octant indices.
index_type inv_octants[3];
};
//! \brief A packet of 3D vectors,
//! arranged for decent alignment.
//!
//! \tparam scalar_type The type of the vector components.
//!
//! \tparam dimensions The number of dimensions in the vector.
//!
//! \tparam count The number of elements per dimension.
template<typename scalar_type, size_type dimensions, size_type count>
struct vec_packet final
{
//! Indicates the total number of scalars in this vector packet.
static inline constexpr size_type value_count() noexcept
{
return dimensions * count;
}
//! Indicates the number of scalars per dimension.
static inline constexpr size_type size() noexcept { return count; }
//! Accesses a pointer to a dimension within the vector packet.
//!
//! \param d The index of the dimension to get the values of.
//!
//! \return A pointer to the start of the dimension values.
inline auto* operator[](size_type d) noexcept { return &values[d * count]; }
//! Accesses a pointer to a dimension within the vector packet.
//!
//! \param d The index of the dimension to get the values of.
//!
//! \return A pointer to the start of the dimension values.
inline const auto* operator[](size_type d) const noexcept
{
return &values[d * count];
}
//! The values of the vector packet.
//! Dimensions are non-interleaved in this array.
scalar_type values[value_count()]{};
};
//! This class is used to store a packet of rays.
//! A packet of rays can be traversed faster than
//! one ray at a time because it leads to better
//! caching, auto-vectorization, and better coherency.
//!
//! \tparam scalar_type The type to use for vector components.
//!
//! \tparam count The maximum number of rays to put into the packet.
template<typename scalar_type, size_type count>
struct ray_packet final
{
//! A type definition for ray indices.
using index_type = typename associated_types<sizeof(scalar_type)>::uint_type;
//! The position vectors of the rays.
vec_packet<scalar_type, 3, count> pos;
//! The direction vectors of the rays.
vec_packet<scalar_type, 3, count> dir;
//! The reciprocal direction vectors.
vec_packet<scalar_type, 3, count> rcp_dir;
//! These are the indices that each ray corresponds to.
//! Since a ray packet may be sorted multiple times
//! throughout a BVH traversal, tracking their original
//! indices allows them to be reordered after a BVH
//! node is exited.
index_type indices[count];
};
//! \brief This class is used for traversing a BVH.
//!
//! \tparam scalar_type The floating point type to use for vector components.
//!
//! \tparam primitive_type The type of the primitive being checked for
//! intersection.
//!
//! \tparam intersection_type The type used for indicating intersections.
template<typename scalar_type,
typename primitive_type,
typename intersection_info>
class traverser final
{
//! A reference to the BVH being traversed.
const bvh<scalar_type>& bvh_;
//! The primitives to check for intersection.
const primitive_type* primitives;
public:
using intersection = intersection_container<scalar_type, intersection_info>;
//! A type definition for a ray.
using ray_type = ray<scalar_type>;
//! Constructs a new traverser instance.
//! \param b The BVH to be traversed.
//! \param p The primitives to check for intersection in each box.
constexpr traverser(const bvh<scalar_type>& b,
const primitive_type* p) noexcept
: bvh_(b)
, primitives(p)
{}
//! \brief Traverses the BVH, returning the closest intersection that was
//! made.
//!
//! \tparam intersector_type Defined by the caller as a function object that
//! takes a primitive and a ray and returns an instance of @ref
//! intersection_type that indicates whether or not a hit was made.
template<typename intersector_type>
intersection operator()(const ray_type& ray,
const intersector_type& intersector) const noexcept;
};
//! \brief Contains the associated types for 32-bit sizes.
template<>
struct associated_types<4> final
{
//! The unsigned integer type to use for 32-bit types.
using uint_type = std::uint32_t;
//! The signed integer type to use for 32-bit types.
using int_type = std::int32_t;
//! The floating point type to use for 32-bit types.
using float_type = float;
};
//! \brief Contains the associated types for 64-bit sizes.
template<>
struct associated_types<8> final
{
//! The unsigned integer type to use for 64-bit types.
using uint_type = std::uint64_t;
//! The signed integer type to use for 64-bit types.
using int_type = std::int64_t;
//! The floating point type to use for 64-bit types.
using float_type = double;
};
//! \brief This namespace contains various math routines
//! for scalar and vector types. It may be useful to the
//! user when writing intersection functions or primitive
//! to box conversions.
namespace math {
//! \brief Calculates the minimum of two scalar values.
template<typename scalar_type>
inline constexpr scalar_type
min(scalar_type a, scalar_type b) noexcept
{
return (a < b) ? a : b;
}
//! \brief Calculates the maximum of two scalar values.
template<typename scalar_type>
inline constexpr scalar_type
max(scalar_type a, scalar_type b) noexcept
{
return (a > b) ? a : b;
}
//! \brief Calculates the Hadamard quotient of two vectors.
//!
//! \tparam scalar_type The scalar type of the vector components.
//!
//! \return The Hadamard quotient of the two vectors.
template<typename scalar_type>
auto
hadamard_div(const vec3<scalar_type>& a, const vec3<scalar_type>& b) noexcept
{
return vec3<scalar_type>{ a.x / b.x, a.y / b.y, a.z / b.z };
}
//! \brief Calculates the Hadamard product of two vectors.
//!
//! \tparam scalar_type The scalar type of the vector components.
//!
//! \return The Hadamard product of the two vectors.
template<typename scalar_type>
auto
hadamard_mul(const vec3<scalar_type>& a, const vec3<scalar_type>& b) noexcept
{
return vec3<scalar_type>{ a.x * b.x, a.y * b.y, a.z * b.z };
}
//! \brief Calculates the cross product between two 3D vectors.
//!
//! \return The cross product of @p a and @p b.
template<typename scalar_type>
inline constexpr vec3<scalar_type>
cross(const vec3<scalar_type>& a, const vec3<scalar_type>& b) noexcept
{
return vec3<scalar_type>{ (a.y * b.z) - (a.z * b.y),
(a.z * b.x) - (a.x * b.z),
(a.x * b.y) - (a.y * b.x) };
}
//! \brief Calculates the dot product of two 3D vectors.
//!
//! \return The dot product of @p a and @p b.
template<typename scalar_type>
inline constexpr scalar_type
dot(const vec3<scalar_type>& a, const vec3<scalar_type>& b) noexcept
{
scalar_type out = 0;
out += a.x * b.x;
out += a.y * b.y;
out += a.z * b.z;
return out;
}
//! \brief Calculates the minimum between two vectors.
//! This is used frequently in bounding box calculations.
//!
//! \tparam scalar_type The scalar type of the vector components.
//!
//! \return A vector containing the minimum components between @p a and @p b.
template<typename scalar_type>
auto
min(const vec3<scalar_type>& a, const vec3<scalar_type>& b) noexcept
{
return vec3<scalar_type>{ std::min(a.x, b.x),
std::min(a.y, b.y),
std::min(a.z, b.z) };
}
//! \brief Calculates the maximum between two vectors.
//! This is used frequently in bounding box calculations.
//!
//! \tparam scalar_type The scalar type of the vector components.
//!
//! \return A vector containing the maximum components between @p a and @p b.
template<typename scalar_type>
auto
max(const vec3<scalar_type>& a, const vec3<scalar_type>& b) noexcept
{
return vec3<scalar_type>{ std::max(a.x, b.x),
std::max(a.y, b.y),
std::max(a.z, b.z) };
}
//! \brief Calculates a vector with reciprocal values of another vector.
//! This is used for ray-box intersection acceleration.
//!
//! \tparam scalar_type The type used for vector components.
//!
//! \return A vector with reciprocal values of @p in.
template<typename scalar_type>
auto
reciprocal(const vec3<scalar_type>& in) noexcept
{
return vec3<scalar_type>{ 1 / in.x, 1 / in.y, 1 / in.z };
}
//! \brief Calculates the sum of two vectors.
//!
//! \tparam scalar_type The type of the vector components.
//!
//! \return The sum of @p a and @p b, as a vector.
template<typename scalar_type>
auto
operator+(const vec3<scalar_type>& a, const vec3<scalar_type>& b) noexcept
{
return vec3<scalar_type>{ a.x + b.x, a.y + b.y, a.z + b.z };
}
//! \brief Calculates the difference between two vectors.
//!
//! \tparam scalar_type The type of the vector components.
//!
//! \return The difference between @p a and @p b, as a vector.
template<typename scalar_type>
auto
operator-(const vec3<scalar_type>& a, const vec3<scalar_type>& b) noexcept
{
return vec3<scalar_type>{ a.x - b.x, a.y - b.y, a.z - b.z };
}
//! \brief Negates a vector.
//!
//! \return The negated result of @p in.
template<typename scalar_type>
inline auto
operator-(const vec3<scalar_type>& in) noexcept
{
return vec3<scalar_type>{ -in.x, -in.y, -in.z };
}
//! \brief Calculates the product between a vector and a scalar value.
//!
//! \tparam scalar_type The scalar type of the vector.
//!
//! \return The product between @p a and @p b.
template<typename scalar_type>
auto
operator*(const vec3<scalar_type>& a, scalar_type b) noexcept
{
return vec3<scalar_type>{ a.x * b, a.y * b, a.z * b };
}
//! \brief Normalizes a vector.
//! This is useful when dealing with trig functions.
//!
//! \return The normalized result of @p v.
template<typename scalar_type>
vec3<scalar_type>
normalize(const vec3<scalar_type>& v) noexcept
{
using std::sqrt;
auto l_inv = scalar_type(1) / sqrt(dot(v, v));
return v * l_inv;
}
//! \brief Multiplies a 2D vector by a scalar value.
//!
//! \return The product of @p a and @p b.
template<typename scalar_type>
vec2<scalar_type>
operator*(const vec2<scalar_type>& a, scalar_type b) noexcept
{
return vec2<scalar_type>{ a.x * b, a.y * b };
}
//! \brief Computes the sum of two 2D vectors.
//!
//! \return The sum of @p a and @p b.
template<typename scalar_type>
vec2<scalar_type>
operator+(const vec2<scalar_type>& a, const vec2<scalar_type>& b) noexcept
{
return vec2<scalar_type>{ a.x + b.x, a.y + b.y };
}
//! \brief This structure implements various vector packet operations.
//! Since these operations require various template parameters, they are
//! best implemented in a structure where type definitions can make the code
//! much more readable.
//!
//! \tparam scalar_type The scalar type of the vector components.
//!
//! \tparam dimensions The number of dimensions the vectors will have.
//!
//! \tparam count The number of elements per dimension for each vector.
template<typename scalar_type, size_type dimensions, size_type count>
struct vec_packet_ops final
{
//! A type definition for a vector packet.
using vec_packet_type = vec_packet<scalar_type, dimensions, count>;
//! The number of values in one vector packet.
static inline constexpr auto value_count() noexcept
{
return dimensions * count;
}
//! Computes the Hadamard product of two vectors.
//!
//! \return The Hadamard product of @p a and @p b.
static constexpr auto hadamard_mul(const vec_packet_type& a,
const vec_packet_type& b) noexcept
{
vec_packet_type out;
for (size_type i = 0; i < value_count(); i++) {
out.values[i] = a.values[i] * b.values[i];
}
return out;
}
//! Computes the sum of a vector packet and single scalar value.
//!
//! \param b This value is added to all components of @p a.
//!
//! \return The sum of @p a and @p b.
static constexpr auto add(const vec_packet_type& a, scalar_type b) noexcept
{
vec_packet_type out;
for (size_type i = 0; i < value_count(); i++) {
out.values[i] = a.values[i] + b;
}
return out;
}
//! Subtracts two vectors.
//!
//! \return The difference between @p a and @p b.
static constexpr auto sub(const vec_packet_type& a,
const vec_packet_type& b) noexcept
{
vec_packet_type out;
for (size_type i = 0; i < value_count(); i++) {
out.values[i] = a.values[i] - b.values[i];
}
return out;
}
//! Multiplies a vector packet by a scalar value.
//!
//! \return The product of @p a and @p b.
static constexpr auto mul(const vec_packet_type& a, scalar_type b) noexcept
{
vec_packet_type out;
for (size_type i = 0; i < value_count(); i++) {
out.values[i] = a.values[i] * b;
}
return out;
}
};
//! Adds a single scalar value to all components of @p a.
//!
//! \return The sume of @p a and @p b.
template<typename scalar_type, size_type dimensions, size_type count>
constexpr auto
operator+(const vec_packet<scalar_type, dimensions, count>& a,
scalar_type b) noexcept
{
return vec_packet_ops<scalar_type, dimensions, count>::add(a, b);
}
//! Subtracts two vector packets.
//!
//! \return The difference of @p a and @p b.
template<typename scalar_type, size_type dimensions, size_type count>
constexpr auto
operator-(const vec_packet<scalar_type, dimensions, count>& a,
const vec_packet<scalar_type, dimensions, count>& b) noexcept
{
return vec_packet_ops<scalar_type, dimensions, count>::sub(a, b);
}
//! Multiplies a vector packet by a single scalar value.
//!
//! \return The product of @p a and @p b, as a vector packet.
template<typename scalar_type, size_type dimensions, size_type count>
constexpr auto
operator*(const vec_packet<scalar_type, dimensions, count>& a,
scalar_type b) noexcept
{
return vec_packet_ops<scalar_type, dimensions, count>::mul(a, b);
}
//! Multiplies a 3D vector packet by a single 3D vector.
//!
//! \tparam scalar_type The scalar type of the vector components.
//!
//! \tparam count The number of elements per dimension in the vector packet.
//!
//! \return The product of @p a and @p b.
template<typename scalar_type, size_type count>
constexpr auto
hadamard_mul(const vec_packet<scalar_type, 3, count>& a,
const vec3<scalar_type>& b) noexcept
{
vec_packet<scalar_type, 3, count> out;
for (size_type i = 0; i < count; i++) {
out[0][i] = a[0][i] * b.x;
out[1][i] = a[1][i] * b.y;
out[2][i] = a[2][i] * b.z;
}
return out;
}
//! \brief Subtracts a single 3D vector from a 3D vector packet.
//!
//! \tparam scalar_type The type used by the vector components.
//!
//! \tparam count The number of elements in one dimension.
//!
//! \return The difference between @p a and @p b.
template<typename scalar_type, size_type count>
constexpr auto
operator-(const vec_packet<scalar_type, 3, count>& a,
const vec3<scalar_type>& b) noexcept
{
vec_packet<scalar_type, 3, count> out;
for (size_type i = 0; i < count; i++) {
out[0][i] = a[0][i] - b.x;
out[1][i] = a[1][i] - b.y;
out[2][i] = a[2][i] - b.z;
}
return out;
}
} // namespace math
//! \brief This namespaces contains implementation details
//! of the library. It shouldn't be used by the end-user.
namespace detail {
using namespace lbvh::math;
//! \brief Counts leading zeroes of a 32-bit integer.
inline auto
clz(std::uint32_t n) noexcept
{
#ifdef _MSC_VER
return __lzcnt(n);
#else
return __builtin_clz(n);
#endif
}
//! \brief Counts leading zeroes of a 64-bit integer.
inline auto
clz(std::uint64_t n) noexcept
{
#ifdef _MSC_VER
return __lzcnt64(n);
#else
return __builtin_clzll(n);
#endif
}
//! \brief Gets the sign of a number, as an integer.
//!
//! \return The sign of @p n. If @p n is negative,
//! then negative one is returned. If @p n is greater
//! than or equal to positive zero, then positive one
//! is returned.
template<typename scalar_type>
inline constexpr scalar_type
sign(scalar_type n) noexcept
{
return (n < 0) ? -1 : 1;
}
//! Divides and rounds up to the nearest integer.
//!
//! \tparam int_type The integer type to divide with.
//!
//! \return The quotient between @p n and @ d, rounded up.
template<typename int_type>
inline constexpr int_type
ceil_div(int_type n, int_type d)
{
return (n + (d - 1)) / d;
}
//! Used for iterating a range of numbers in a for loop.
struct loop_range final
{
//! The beginning index of the range.
size_type begin;
//! The non-inclusive ending index of the range.
size_type end;
//! Constructs a loop range for an array and work division.
//! This makes it easy to divide work required on an array
//! into a given work division.
//!
//! \param div The work division given by the scheduler.
//!
//! \param array_size The size of the array being worked on.
loop_range(const work_division& div, size_type array_size) noexcept
{
auto chunk_size = array_size / div.max;
begin = chunk_size * div.idx;
if ((div.idx + 1) == div.max) {
end = array_size;
} else {
end = begin + chunk_size;
}
}
};
//! Constructs an accelerated ray structure.
//!
//! \param r The ray from which to build the structure from.
template<typename scalar_type>
constexpr auto
make_accel_ray(const ray<scalar_type>& r) noexcept
{
using index_type = typename accel_ray<scalar_type>::index_type;
auto rcp_dir = reciprocal(r.dir);
index_type octants[3]{ (r.dir.x > 0) ? index_type(0) : index_type(3),
(r.dir.y > 0) ? index_type(1) : index_type(4),