1 // Copyright 2009-2021 Intel Corporation
2 // SPDX-License-Identifier: Apache-2.0
3 
4 #include "bvh.h"
5 #include "bvh_statistics.h"
6 #include "bvh_rotate.h"
7 #include "../common/profile.h"
8 #include "../../common/algorithms/parallel_prefix_sum.h"
9 
10 #include "../builders/primrefgen.h"
11 #include "../builders/bvh_builder_morton.h"
12 
13 #include "../geometry/triangle.h"
14 #include "../geometry/trianglev.h"
15 #include "../geometry/trianglei.h"
16 #include "../geometry/quadv.h"
17 #include "../geometry/quadi.h"
18 #include "../geometry/object.h"
19 #include "../geometry/instance.h"
20 
21 #if defined(__64BIT__)
22 #  define ROTATE_TREE 1 // specifies number of tree rotation rounds to perform
23 #else
24 #  define ROTATE_TREE 0 // do not use tree rotations on 32 bit platforms, barrier bit in NodeRef will cause issues
25 #endif
26 
27 namespace embree
28 {
29   namespace isa
30   {
31     template<int N>
32     struct SetBVHNBounds
33     {
34       typedef BVHN<N> BVH;
35       typedef typename BVH::NodeRef NodeRef;
36       typedef typename BVH::NodeRecord NodeRecord;
37       typedef typename BVH::AABBNode AABBNode;
38 
39       BVH* bvh;
SetBVHNBoundsembree::isa::SetBVHNBounds40       __forceinline SetBVHNBounds (BVH* bvh) : bvh(bvh) {}
41 
operator ()embree::isa::SetBVHNBounds42       __forceinline NodeRecord operator() (NodeRef ref, const NodeRecord* children, size_t num)
43       {
44         AABBNode* node = ref.getAABBNode();
45 
46         BBox3fa res = empty;
47         for (size_t i=0; i<num; i++) {
48           const BBox3fa b = children[i].bounds;
49           res.extend(b);
50           node->setRef(i,children[i].ref);
51           node->setBounds(i,b);
52         }
53 
54         BBox3fx result = (BBox3fx&)res;
55 #if ROTATE_TREE
56         if (N == 4)
57         {
58           size_t n = 0;
59           for (size_t i=0; i<num; i++)
60             n += children[i].bounds.lower.a;
61 
62           if (n >= 4096) {
63             for (size_t i=0; i<num; i++) {
64               if (children[i].bounds.lower.a < 4096) {
65                 for (int j=0; j<ROTATE_TREE; j++)
66                   BVHNRotate<N>::rotate(node->child(i));
67                 node->child(i).setBarrier();
68               }
69             }
70           }
71           result.lower.a = unsigned(n);
72         }
73 #endif
74 
75         return NodeRecord(ref,result);
76       }
77     };
78 
79     template<int N, typename Primitive>
80     struct CreateMortonLeaf;
81 
82     template<int N>
83     struct CreateMortonLeaf<N,Triangle4>
84     {
85       typedef BVHN<N> BVH;
86       typedef typename BVH::NodeRef NodeRef;
87       typedef typename BVH::NodeRecord NodeRecord;
88 
CreateMortonLeafembree::isa::CreateMortonLeaf89       __forceinline CreateMortonLeaf (TriangleMesh* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
90         : mesh(mesh), morton(morton), geomID_(geomID) {}
91 
operator ()embree::isa::CreateMortonLeaf92       __noinline NodeRecord operator() (const range<unsigned>& current, const FastAllocator::CachedAllocator& alloc)
93       {
94         vfloat4 lower(pos_inf);
95         vfloat4 upper(neg_inf);
96         size_t items = current.size();
97         size_t start = current.begin();
98         assert(items<=4);
99 
100         /* allocate leaf node */
101         Triangle4* accel = (Triangle4*) alloc.malloc1(sizeof(Triangle4),BVH::byteAlignment);
102         NodeRef ref = BVH::encodeLeaf((char*)accel,1);
103         vuint4 vgeomID = -1, vprimID = -1;
104         Vec3vf4 v0 = zero, v1 = zero, v2 = zero;
105         const TriangleMesh* __restrict__ const mesh = this->mesh;
106 
107         for (size_t i=0; i<items; i++)
108         {
109           const unsigned int primID = morton[start+i].index;
110           const TriangleMesh::Triangle& tri = mesh->triangle(primID);
111           const Vec3fa& p0 = mesh->vertex(tri.v[0]);
112           const Vec3fa& p1 = mesh->vertex(tri.v[1]);
113           const Vec3fa& p2 = mesh->vertex(tri.v[2]);
114           lower = min(lower,(vfloat4)p0,(vfloat4)p1,(vfloat4)p2);
115           upper = max(upper,(vfloat4)p0,(vfloat4)p1,(vfloat4)p2);
116           vgeomID [i] = geomID_;
117           vprimID [i] = primID;
118           v0.x[i] = p0.x; v0.y[i] = p0.y; v0.z[i] = p0.z;
119           v1.x[i] = p1.x; v1.y[i] = p1.y; v1.z[i] = p1.z;
120           v2.x[i] = p2.x; v2.y[i] = p2.y; v2.z[i] = p2.z;
121         }
122 
123         Triangle4::store_nt(accel,Triangle4(v0,v1,v2,vgeomID,vprimID));
124         BBox3fx box_o = BBox3fx((Vec3fx)lower,(Vec3fx)upper);
125 #if ROTATE_TREE
126         if (N == 4)
127           box_o.lower.a = unsigned(current.size());
128 #endif
129         return NodeRecord(ref,box_o);
130       }
131 
132     private:
133       TriangleMesh* mesh;
134       BVHBuilderMorton::BuildPrim* morton;
135       unsigned int geomID_ = std::numeric_limits<unsigned int>::max();
136     };
137 
138     template<int N>
139     struct CreateMortonLeaf<N,Triangle4v>
140     {
141       typedef BVHN<N> BVH;
142       typedef typename BVH::NodeRef NodeRef;
143       typedef typename BVH::NodeRecord NodeRecord;
144 
CreateMortonLeafembree::isa::CreateMortonLeaf145       __forceinline CreateMortonLeaf (TriangleMesh* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
146         : mesh(mesh), morton(morton), geomID_(geomID) {}
147 
operator ()embree::isa::CreateMortonLeaf148       __noinline NodeRecord operator() (const range<unsigned>& current, const FastAllocator::CachedAllocator& alloc)
149       {
150         vfloat4 lower(pos_inf);
151         vfloat4 upper(neg_inf);
152         size_t items = current.size();
153         size_t start = current.begin();
154         assert(items<=4);
155 
156         /* allocate leaf node */
157         Triangle4v* accel = (Triangle4v*) alloc.malloc1(sizeof(Triangle4v),BVH::byteAlignment);
158         NodeRef ref = BVH::encodeLeaf((char*)accel,1);
159         vuint4 vgeomID = -1, vprimID = -1;
160         Vec3vf4 v0 = zero, v1 = zero, v2 = zero;
161         const TriangleMesh* __restrict__ mesh = this->mesh;
162 
163         for (size_t i=0; i<items; i++)
164         {
165           const unsigned int primID = morton[start+i].index;
166           const TriangleMesh::Triangle& tri = mesh->triangle(primID);
167           const Vec3fa& p0 = mesh->vertex(tri.v[0]);
168           const Vec3fa& p1 = mesh->vertex(tri.v[1]);
169           const Vec3fa& p2 = mesh->vertex(tri.v[2]);
170           lower = min(lower,(vfloat4)p0,(vfloat4)p1,(vfloat4)p2);
171           upper = max(upper,(vfloat4)p0,(vfloat4)p1,(vfloat4)p2);
172           vgeomID [i] = geomID_;
173           vprimID [i] = primID;
174           v0.x[i] = p0.x; v0.y[i] = p0.y; v0.z[i] = p0.z;
175           v1.x[i] = p1.x; v1.y[i] = p1.y; v1.z[i] = p1.z;
176           v2.x[i] = p2.x; v2.y[i] = p2.y; v2.z[i] = p2.z;
177         }
178         Triangle4v::store_nt(accel,Triangle4v(v0,v1,v2,vgeomID,vprimID));
179         BBox3fx box_o = BBox3fx((Vec3fx)lower,(Vec3fx)upper);
180 #if ROTATE_TREE
181         if (N == 4)
182           box_o.lower.a = current.size();
183 #endif
184         return NodeRecord(ref,box_o);
185       }
186     private:
187       TriangleMesh* mesh;
188       BVHBuilderMorton::BuildPrim* morton;
189       unsigned int geomID_ = std::numeric_limits<unsigned int>::max();
190     };
191 
192     template<int N>
193     struct CreateMortonLeaf<N,Triangle4i>
194     {
195       typedef BVHN<N> BVH;
196       typedef typename BVH::NodeRef NodeRef;
197       typedef typename BVH::NodeRecord NodeRecord;
198 
CreateMortonLeafembree::isa::CreateMortonLeaf199       __forceinline CreateMortonLeaf (TriangleMesh* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
200         : mesh(mesh), morton(morton), geomID_(geomID) {}
201 
operator ()embree::isa::CreateMortonLeaf202       __noinline NodeRecord operator() (const range<unsigned>& current, const FastAllocator::CachedAllocator& alloc)
203       {
204         vfloat4 lower(pos_inf);
205         vfloat4 upper(neg_inf);
206         size_t items = current.size();
207         size_t start = current.begin();
208         assert(items<=4);
209 
210         /* allocate leaf node */
211         Triangle4i* accel = (Triangle4i*) alloc.malloc1(sizeof(Triangle4i),BVH::byteAlignment);
212         NodeRef ref = BVH::encodeLeaf((char*)accel,1);
213 
214         vuint4 v0 = zero, v1 = zero, v2 = zero;
215         vuint4 vgeomID = -1, vprimID = -1;
216         const TriangleMesh* __restrict__ const mesh = this->mesh;
217 
218         for (size_t i=0; i<items; i++)
219         {
220           const unsigned int primID = morton[start+i].index;
221           const TriangleMesh::Triangle& tri = mesh->triangle(primID);
222           const Vec3fa& p0 = mesh->vertex(tri.v[0]);
223           const Vec3fa& p1 = mesh->vertex(tri.v[1]);
224           const Vec3fa& p2 = mesh->vertex(tri.v[2]);
225           lower = min(lower,(vfloat4)p0,(vfloat4)p1,(vfloat4)p2);
226           upper = max(upper,(vfloat4)p0,(vfloat4)p1,(vfloat4)p2);
227           vgeomID[i] = geomID_;
228           vprimID[i] = primID;
229           unsigned int int_stride = mesh->vertices0.getStride()/4;
230           v0[i] = tri.v[0] * int_stride;
231           v1[i] = tri.v[1] * int_stride;
232           v2[i] = tri.v[2] * int_stride;
233         }
234 
235         for (size_t i=items; i<4; i++)
236         {
237           vgeomID[i] = vgeomID[0];
238           vprimID[i] = -1;
239           v0[i] = 0;
240           v1[i] = 0;
241           v2[i] = 0;
242         }
243         Triangle4i::store_nt(accel,Triangle4i(v0,v1,v2,vgeomID,vprimID));
244         BBox3fx box_o = BBox3fx((Vec3fx)lower,(Vec3fx)upper);
245 #if ROTATE_TREE
246         if (N == 4)
247           box_o.lower.a = current.size();
248 #endif
249         return NodeRecord(ref,box_o);
250       }
251     private:
252       TriangleMesh* mesh;
253       BVHBuilderMorton::BuildPrim* morton;
254       unsigned int geomID_ = std::numeric_limits<unsigned int>::max();
255     };
256 
257     template<int N>
258     struct CreateMortonLeaf<N,Quad4v>
259     {
260       typedef BVHN<N> BVH;
261       typedef typename BVH::NodeRef NodeRef;
262       typedef typename BVH::NodeRecord NodeRecord;
263 
CreateMortonLeafembree::isa::CreateMortonLeaf264       __forceinline CreateMortonLeaf (QuadMesh* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
265         : mesh(mesh), morton(morton), geomID_(geomID) {}
266 
operator ()embree::isa::CreateMortonLeaf267       __noinline NodeRecord operator() (const range<unsigned>& current, const FastAllocator::CachedAllocator& alloc)
268       {
269         vfloat4 lower(pos_inf);
270         vfloat4 upper(neg_inf);
271         size_t items = current.size();
272         size_t start = current.begin();
273         assert(items<=4);
274 
275         /* allocate leaf node */
276         Quad4v* accel = (Quad4v*) alloc.malloc1(sizeof(Quad4v),BVH::byteAlignment);
277         NodeRef ref = BVH::encodeLeaf((char*)accel,1);
278 
279         vuint4 vgeomID = -1, vprimID = -1;
280         Vec3vf4 v0 = zero, v1 = zero, v2 = zero, v3 = zero;
281         const QuadMesh* __restrict__ mesh = this->mesh;
282 
283         for (size_t i=0; i<items; i++)
284         {
285           const unsigned int primID = morton[start+i].index;
286           const QuadMesh::Quad& tri = mesh->quad(primID);
287           const Vec3fa& p0 = mesh->vertex(tri.v[0]);
288           const Vec3fa& p1 = mesh->vertex(tri.v[1]);
289           const Vec3fa& p2 = mesh->vertex(tri.v[2]);
290           const Vec3fa& p3 = mesh->vertex(tri.v[3]);
291           lower = min(lower,(vfloat4)p0,(vfloat4)p1,(vfloat4)p2,(vfloat4)p3);
292           upper = max(upper,(vfloat4)p0,(vfloat4)p1,(vfloat4)p2,(vfloat4)p3);
293           vgeomID [i] = geomID_;
294           vprimID [i] = primID;
295           v0.x[i] = p0.x; v0.y[i] = p0.y; v0.z[i] = p0.z;
296           v1.x[i] = p1.x; v1.y[i] = p1.y; v1.z[i] = p1.z;
297           v2.x[i] = p2.x; v2.y[i] = p2.y; v2.z[i] = p2.z;
298           v3.x[i] = p3.x; v3.y[i] = p3.y; v3.z[i] = p3.z;
299         }
300         Quad4v::store_nt(accel,Quad4v(v0,v1,v2,v3,vgeomID,vprimID));
301         BBox3fx box_o = BBox3fx((Vec3fx)lower,(Vec3fx)upper);
302 #if ROTATE_TREE
303         if (N == 4)
304           box_o.lower.a = current.size();
305 #endif
306         return NodeRecord(ref,box_o);
307       }
308     private:
309       QuadMesh* mesh;
310       BVHBuilderMorton::BuildPrim* morton;
311       unsigned int geomID_ = std::numeric_limits<unsigned int>::max();
312     };
313 
314     template<int N>
315     struct CreateMortonLeaf<N,Object>
316     {
317       typedef BVHN<N> BVH;
318       typedef typename BVH::NodeRef NodeRef;
319       typedef typename BVH::NodeRecord NodeRecord;
320 
CreateMortonLeafembree::isa::CreateMortonLeaf321       __forceinline CreateMortonLeaf (UserGeometry* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
322         : mesh(mesh), morton(morton), geomID_(geomID) {}
323 
operator ()embree::isa::CreateMortonLeaf324       __noinline NodeRecord operator() (const range<unsigned>& current, const FastAllocator::CachedAllocator& alloc)
325       {
326         vfloat4 lower(pos_inf);
327         vfloat4 upper(neg_inf);
328         size_t items = current.size();
329         size_t start = current.begin();
330 
331         /* allocate leaf node */
332         Object* accel = (Object*) alloc.malloc1(items*sizeof(Object),BVH::byteAlignment);
333         NodeRef ref = BVH::encodeLeaf((char*)accel,items);
334         const UserGeometry* mesh = this->mesh;
335 
336         BBox3fa bounds = empty;
337         for (size_t i=0; i<items; i++)
338         {
339           const unsigned int index = morton[start+i].index;
340           const unsigned int primID = index;
341           bounds.extend(mesh->bounds(primID));
342           new (&accel[i]) Object(geomID_,primID);
343         }
344 
345         BBox3fx box_o = (BBox3fx&)bounds;
346 #if ROTATE_TREE
347         if (N == 4)
348           box_o.lower.a = current.size();
349 #endif
350         return NodeRecord(ref,box_o);
351       }
352     private:
353       UserGeometry* mesh;
354       BVHBuilderMorton::BuildPrim* morton;
355       unsigned int geomID_ = std::numeric_limits<unsigned int>::max();
356     };
357 
358     template<int N>
359     struct CreateMortonLeaf<N,InstancePrimitive>
360     {
361       typedef BVHN<N> BVH;
362       typedef typename BVH::NodeRef NodeRef;
363       typedef typename BVH::NodeRecord NodeRecord;
364 
CreateMortonLeafembree::isa::CreateMortonLeaf365       __forceinline CreateMortonLeaf (Instance* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
366         : mesh(mesh), morton(morton), geomID_(geomID) {}
367 
operator ()embree::isa::CreateMortonLeaf368       __noinline NodeRecord operator() (const range<unsigned>& current, const FastAllocator::CachedAllocator& alloc)
369       {
370         vfloat4 lower(pos_inf);
371         vfloat4 upper(neg_inf);
372         size_t items = current.size();
373         size_t start = current.begin();
374         assert(items <= 1);
375 
376         /* allocate leaf node */
377         InstancePrimitive* accel = (InstancePrimitive*) alloc.malloc1(items*sizeof(InstancePrimitive),BVH::byteAlignment);
378         NodeRef ref = BVH::encodeLeaf((char*)accel,items);
379         const Instance* instance = this->mesh;
380 
381         BBox3fa bounds = empty;
382         for (size_t i=0; i<items; i++)
383         {
384           const unsigned int primID = morton[start+i].index;
385           bounds.extend(instance->bounds(primID));
386           new (&accel[i]) InstancePrimitive(instance, geomID_);
387         }
388 
389         BBox3fx box_o = (BBox3fx&)bounds;
390 #if ROTATE_TREE
391         if (N == 4)
392           box_o.lower.a = current.size();
393 #endif
394         return NodeRecord(ref,box_o);
395       }
396     private:
397       Instance* mesh;
398       BVHBuilderMorton::BuildPrim* morton;
399       unsigned int geomID_ = std::numeric_limits<unsigned int>::max();
400     };
401 
402     template<typename Mesh>
403     struct CalculateMeshBounds
404     {
CalculateMeshBoundsembree::isa::CalculateMeshBounds405       __forceinline CalculateMeshBounds (Mesh* mesh)
406         : mesh(mesh) {}
407 
operator ()embree::isa::CalculateMeshBounds408       __forceinline const BBox3fa operator() (const BVHBuilderMorton::BuildPrim& morton) {
409         return mesh->bounds(morton.index);
410       }
411 
412     private:
413       Mesh* mesh;
414     };
415 
416     template<int N, typename Mesh, typename Primitive>
417     class BVHNMeshBuilderMorton : public Builder
418     {
419       typedef BVHN<N> BVH;
420       typedef typename BVH::AABBNode AABBNode;
421       typedef typename BVH::NodeRef NodeRef;
422       typedef typename BVH::NodeRecord NodeRecord;
423 
424     public:
425 
BVHNMeshBuilderMorton(BVH * bvh,Mesh * mesh,unsigned int geomID,const size_t minLeafSize,const size_t maxLeafSize,const size_t singleThreadThreshold=DEFAULT_SINGLE_THREAD_THRESHOLD)426       BVHNMeshBuilderMorton (BVH* bvh, Mesh* mesh, unsigned int geomID, const size_t minLeafSize, const size_t maxLeafSize, const size_t singleThreadThreshold = DEFAULT_SINGLE_THREAD_THRESHOLD)
427         : bvh(bvh), mesh(mesh), morton(bvh->device,0), settings(N,BVH::maxBuildDepth,minLeafSize,min(maxLeafSize,Primitive::max_size()*BVH::maxLeafBlocks),singleThreadThreshold), geomID_(geomID) {}
428 
429       /* build function */
build()430       void build()
431       {
432         /* we reset the allocator when the mesh size changed */
433         if (mesh->numPrimitives != numPreviousPrimitives) {
434           bvh->alloc.clear();
435           morton.clear();
436         }
437         size_t numPrimitives = mesh->size();
438         numPreviousPrimitives = numPrimitives;
439 
440         /* skip build for empty scene */
441         if (numPrimitives == 0) {
442           bvh->set(BVH::emptyNode,empty,0);
443           return;
444         }
445 
446         /* preallocate arrays */
447         morton.resize(numPrimitives);
448         size_t bytesEstimated = numPrimitives*sizeof(AABBNode)/(4*N) + size_t(1.2f*Primitive::blocks(numPrimitives)*sizeof(Primitive));
449         size_t bytesMortonCodes = numPrimitives*sizeof(BVHBuilderMorton::BuildPrim);
450         bytesEstimated = max(bytesEstimated,bytesMortonCodes); // the first allocation block is reused to sort the morton codes
451         bvh->alloc.init(bytesMortonCodes,bytesMortonCodes,bytesEstimated);
452 
453         /* create morton code array */
454         BVHBuilderMorton::BuildPrim* dest = (BVHBuilderMorton::BuildPrim*) bvh->alloc.specialAlloc(bytesMortonCodes);
455         size_t numPrimitivesGen = createMortonCodeArray<Mesh>(mesh,morton,bvh->scene->progressInterface);
456 
457         /* create BVH */
458         SetBVHNBounds<N> setBounds(bvh);
459         CreateMortonLeaf<N,Primitive> createLeaf(mesh,geomID_,morton.data());
460         CalculateMeshBounds<Mesh> calculateBounds(mesh);
461         auto root = BVHBuilderMorton::build<NodeRecord>(
462           typename BVH::CreateAlloc(bvh),
463           typename BVH::AABBNode::Create(),
464           setBounds,createLeaf,calculateBounds,bvh->scene->progressInterface,
465           morton.data(),dest,numPrimitivesGen,settings);
466 
467         bvh->set(root.ref,LBBox3fa(root.bounds),numPrimitives);
468 
469 #if ROTATE_TREE
470         if (N == 4)
471         {
472           for (int i=0; i<ROTATE_TREE; i++)
473             BVHNRotate<N>::rotate(bvh->root);
474           bvh->clearBarrier(bvh->root);
475         }
476 #endif
477 
478         /* clear temporary data for static geometry */
479         if (bvh->scene->isStaticAccel()) {
480           morton.clear();
481         }
482         bvh->cleanup();
483       }
484 
clear()485       void clear() {
486         morton.clear();
487       }
488 
489     private:
490       BVH* bvh;
491       Mesh* mesh;
492       mvector<BVHBuilderMorton::BuildPrim> morton;
493       BVHBuilderMorton::Settings settings;
494       unsigned int geomID_ = std::numeric_limits<unsigned int>::max();
495       unsigned int numPreviousPrimitives = 0;
496     };
497 
498 #if defined(EMBREE_GEOMETRY_TRIANGLE)
BVH4Triangle4MeshBuilderMortonGeneral(void * bvh,TriangleMesh * mesh,unsigned int geomID,size_t mode)499     Builder* BVH4Triangle4MeshBuilderMortonGeneral  (void* bvh, TriangleMesh* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<4,TriangleMesh,Triangle4> ((BVH4*)bvh,mesh,geomID,4,4); }
500     Builder* BVH4Triangle4vMeshBuilderMortonGeneral (void* bvh, TriangleMesh* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<4,TriangleMesh,Triangle4v>((BVH4*)bvh,mesh,geomID,4,4); }
501     Builder* BVH4Triangle4iMeshBuilderMortonGeneral (void* bvh, TriangleMesh* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<4,TriangleMesh,Triangle4i>((BVH4*)bvh,mesh,geomID,4,4); }
502 #if defined(__AVX__)
503     Builder* BVH8Triangle4MeshBuilderMortonGeneral  (void* bvh, TriangleMesh* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<8,TriangleMesh,Triangle4> ((BVH8*)bvh,mesh,geomID,4,4); }
504     Builder* BVH8Triangle4vMeshBuilderMortonGeneral (void* bvh, TriangleMesh* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<8,TriangleMesh,Triangle4v>((BVH8*)bvh,mesh,geomID,4,4); }
505     Builder* BVH8Triangle4iMeshBuilderMortonGeneral (void* bvh, TriangleMesh* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<8,TriangleMesh,Triangle4i>((BVH8*)bvh,mesh,geomID,4,4); }
506 #endif
507 #endif
508 
509 #if defined(EMBREE_GEOMETRY_QUAD)
510     Builder* BVH4Quad4vMeshBuilderMortonGeneral (void* bvh, QuadMesh* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<4,QuadMesh,Quad4v>((BVH4*)bvh,mesh,geomID,4,4); }
511 #if defined(__AVX__)
512     Builder* BVH8Quad4vMeshBuilderMortonGeneral (void* bvh, QuadMesh* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<8,QuadMesh,Quad4v>((BVH8*)bvh,mesh,geomID,4,4); }
513 #endif
514 #endif
515 
516 #if defined(EMBREE_GEOMETRY_USER)
517     Builder* BVH4VirtualMeshBuilderMortonGeneral (void* bvh, UserGeometry* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<4,UserGeometry,Object>((BVH4*)bvh,mesh,geomID,1,BVH4::maxLeafBlocks); }
518 #if defined(__AVX__)
519     Builder* BVH8VirtualMeshBuilderMortonGeneral (void* bvh, UserGeometry* mesh, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<8,UserGeometry,Object>((BVH8*)bvh,mesh,geomID,1,BVH4::maxLeafBlocks); }
520 #endif
521 #endif
522 
523 #if defined(EMBREE_GEOMETRY_INSTANCE)
524     Builder* BVH4InstanceMeshBuilderMortonGeneral (void* bvh, Instance* mesh, Geometry::GTypeMask gtype, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<4,Instance,InstancePrimitive>((BVH4*)bvh,mesh,gtype,geomID,1,BVH4::maxLeafBlocks); }
525 #if defined(__AVX__)
526     Builder* BVH8InstanceMeshBuilderMortonGeneral (void* bvh, Instance* mesh, Geometry::GTypeMask gtype, unsigned int geomID, size_t mode) { return new class BVHNMeshBuilderMorton<8,Instance,InstancePrimitive>((BVH8*)bvh,mesh,gtype,geomID,1,BVH4::maxLeafBlocks); }
527 #endif
528 #endif
529 
530   }
531 }
532