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
27namespace 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;
40 __forceinline SetBVHNBounds (BVH* bvh) : bvh(bvh) {}
41
42 __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
89 __forceinline CreateMortonLeaf (TriangleMesh* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
90 : mesh(mesh), morton(morton), geomID_(geomID) {}
91
92 __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
145 __forceinline CreateMortonLeaf (TriangleMesh* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
146 : mesh(mesh), morton(morton), geomID_(geomID) {}
147
148 __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
199 __forceinline CreateMortonLeaf (TriangleMesh* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
200 : mesh(mesh), morton(morton), geomID_(geomID) {}
201
202 __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
264 __forceinline CreateMortonLeaf (QuadMesh* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
265 : mesh(mesh), morton(morton), geomID_(geomID) {}
266
267 __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
321 __forceinline CreateMortonLeaf (UserGeometry* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
322 : mesh(mesh), morton(morton), geomID_(geomID) {}
323
324 __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
365 __forceinline CreateMortonLeaf (Instance* mesh, unsigned int geomID, BVHBuilderMorton::BuildPrim* morton)
366 : mesh(mesh), morton(morton), geomID_(geomID) {}
367
368 __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 {
405 __forceinline CalculateMeshBounds (Mesh* mesh)
406 : mesh(mesh) {}
407
408 __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
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 */
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
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)
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