00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "luxrays/accelerators/qbvhaccel.h"
00023 #include "luxrays/core/utils.h"
00024 #include "luxrays/core/context.h"
00025
00026 using namespace luxrays;
00027
00028
00029
00030 QBVHAccel::QBVHAccel(const Context *context,
00031 u_int mp, u_int fst, u_int sf) : fullSweepThreshold(fst),
00032 skipFactor(sf), maxPrimsPerLeaf(mp), ctx(context) {
00033 initialized = false;
00034 preprocessedMesh = NULL;
00035 mesh = NULL;
00036 meshIDs = NULL;
00037 meshTriangleIDs = NULL;
00038 }
00039
00040 QBVHAccel::~QBVHAccel() {
00041 if (initialized) {
00042 FreeAligned(prims);
00043 FreeAligned(nodes);
00044
00045 if (preprocessedMesh) {
00046 preprocessedMesh->Delete();
00047 delete preprocessedMesh;
00048 }
00049 delete[] meshIDs;
00050 delete[] meshTriangleIDs;
00051 }
00052 }
00053
00054
00055 void QBVHAccel::Init(const std::deque<Mesh *> &meshes, const unsigned int totalVertexCount,
00056 const unsigned int totalTriangleCount) {
00057 assert (!initialized);
00058
00059 preprocessedMesh = TriangleMesh::Merge(totalVertexCount, totalTriangleCount,
00060 meshes, &meshIDs, &meshTriangleIDs);
00061 assert (mesh->GetTotalVertexCount() == totalVertexCount);
00062 assert (mesh->GetTotalTriangleCount() == totalTriangleCount);
00063
00064 LR_LOG(ctx, "Total vertices memory usage: " << totalVertexCount * sizeof(Point) / 1024 << "Kbytes");
00065 LR_LOG(ctx, "Total triangles memory usage: " << totalTriangleCount * sizeof(Triangle) / 1024 << "Kbytes");
00066
00067 Init(preprocessedMesh);
00068 }
00069
00070 void QBVHAccel::Init(const Mesh *m) {
00071 assert (!initialized);
00072
00073 mesh = m;
00074 const unsigned int totalTriangleCount = mesh->GetTotalTriangleCount();
00075
00076
00077 u_int *primsIndexes = new u_int[totalTriangleCount + 3];
00078
00079
00080
00081
00082
00083
00084
00085 nNodes = 0;
00086 maxNodes = 1;
00087 for (u_int layer = ((totalTriangleCount + maxPrimsPerLeaf - 1) / maxPrimsPerLeaf + 3) / 4; layer != 1; layer = (layer + 3) / 4)
00088 maxNodes += layer;
00089 nodes = AllocAligned<QBVHNode>(maxNodes);
00090 for (u_int i = 0; i < maxNodes; ++i)
00091 nodes[i] = QBVHNode();
00092
00093
00094
00095
00096 BBox *primsBboxes = new BBox[totalTriangleCount];
00097 Point *primsCentroids = new Point[totalTriangleCount];
00098
00099 BBox centroidsBbox;
00100
00101 const Point *verts = mesh->GetVertices();
00102 const Triangle *triangles = mesh->GetTriangles();
00103
00104
00105 for (u_int i = 0; i < totalTriangleCount; ++i) {
00106
00107 primsIndexes[i] = i;
00108
00109
00110 primsBboxes[i] = triangles[i].WorldBound(verts);
00111 primsBboxes[i].Expand(RAY_EPSILON);
00112 primsCentroids[i] = (primsBboxes[i].pMin + primsBboxes[i].pMax) * .5f;
00113
00114
00115 worldBound = Union(worldBound, primsBboxes[i]);
00116 centroidsBbox = Union(centroidsBbox, primsCentroids[i]);
00117 }
00118
00119
00120 primsIndexes[totalTriangleCount] = totalTriangleCount - 1;
00121 primsIndexes[totalTriangleCount + 1] = totalTriangleCount - 1;
00122 primsIndexes[totalTriangleCount + 2] = totalTriangleCount - 1;
00123
00124
00125 LR_LOG(ctx, "Building QBVH, primitives: " << totalTriangleCount << ", initial nodes: " << maxNodes);
00126
00127 nQuads = 0;
00128 BuildTree(0, totalTriangleCount, primsIndexes, primsBboxes, primsCentroids,
00129 worldBound, centroidsBbox, -1, 0, 0);
00130
00131 prims = AllocAligned<QuadTriangle>(nQuads);
00132 nQuads = 0;
00133 PreSwizzle(0, primsIndexes);
00134
00135 LR_LOG(ctx, "QBVH completed with " << nNodes << "/" << maxNodes << " nodes");
00136 LR_LOG(ctx, "Total QBVH memory usage: " << nNodes * sizeof(QBVHNode) / 1024 << "Kbytes");
00137 LR_LOG(ctx, "Total QBVH QuadTriangle count: " << nQuads);
00138
00139
00140 delete[] primsBboxes;
00141 delete[] primsCentroids;
00142 delete[] primsIndexes;
00143 }
00144
00145
00146
00147 void QBVHAccel::BuildTree(u_int start, u_int end, u_int *primsIndexes,
00148 BBox *primsBboxes, Point *primsCentroids, const BBox &nodeBbox,
00149 const BBox ¢roidsBbox, int32_t parentIndex, int32_t childIndex, int depth) {
00150
00151
00152 if (end - start <= maxPrimsPerLeaf) {
00153 CreateTempLeaf(parentIndex, childIndex, start, end, nodeBbox);
00154 return;
00155 }
00156
00157 int32_t currentNode = parentIndex;
00158 int32_t leftChildIndex = childIndex;
00159 int32_t rightChildIndex = childIndex + 1;
00160
00161
00162 int bins[NB_BINS];
00163
00164 BBox binsBbox[NB_BINS];
00165
00166
00167
00168
00169
00170
00171
00172 for (u_int i = 0; i < NB_BINS; ++i)
00173 bins[i] = 0;
00174
00175 u_int step = (end - start < fullSweepThreshold) ? 1 : skipFactor;
00176
00177
00178
00179
00180 const int axis = centroidsBbox.MaximumExtent();
00181
00182
00183
00184 const float k0 = centroidsBbox.pMin[axis];
00185 const float k1 = NB_BINS / (centroidsBbox.pMax[axis] - k0);
00186
00187
00188
00189 if (k1 == INFINITY) {
00190 if (end - start > 64)
00191 LR_LOG(ctx, "QBVH unable to handle geometry, too many primitives with the same centroid");
00192 CreateTempLeaf(parentIndex, childIndex, start, end, nodeBbox);
00193 return;
00194 }
00195
00196
00197
00198 if (depth % 2 == 0) {
00199 currentNode = CreateIntermediateNode(parentIndex, childIndex, nodeBbox);
00200 leftChildIndex = 0;
00201 rightChildIndex = 2;
00202 }
00203
00204 for (u_int i = start; i < end; i += step) {
00205 u_int primIndex = primsIndexes[i];
00206
00207
00208
00209 const int binId = Min(NB_BINS - 1, Floor2Int(k1 * (primsCentroids[primIndex][axis] - k0)));
00210
00211 bins[binId]++;
00212 binsBbox[binId] = Union(binsBbox[binId], primsBboxes[primIndex]);
00213 }
00214
00215
00216
00217
00218
00219
00220 int nbPrimsLeft[NB_BINS];
00221 int nbPrimsRight[NB_BINS];
00222
00223 BBox bboxesLeft[NB_BINS];
00224 BBox bboxesRight[NB_BINS];
00225
00226
00227 float vLeft[NB_BINS];
00228 float vRight[NB_BINS];
00229
00230 BBox currentBboxLeft, currentBboxRight;
00231 int currentNbLeft = 0, currentNbRight = 0;
00232
00233 for (int i = 0; i < NB_BINS; ++i) {
00234
00235
00236
00237 currentNbLeft += bins[i];
00238 nbPrimsLeft[i] = currentNbLeft;
00239
00240 currentBboxLeft = Union(currentBboxLeft, binsBbox[i]);
00241 bboxesLeft[i] = currentBboxLeft;
00242
00243 vLeft[i] = currentBboxLeft.SurfaceArea();
00244
00245
00246
00247
00248 int rightIndex = NB_BINS - 1 - i;
00249 currentNbRight += bins[rightIndex];
00250 nbPrimsRight[rightIndex] = currentNbRight;
00251
00252 currentBboxRight = Union(currentBboxRight, binsBbox[rightIndex]);
00253 bboxesRight[rightIndex] = currentBboxRight;
00254
00255 vRight[rightIndex] = currentBboxRight.SurfaceArea();
00256 }
00257
00258 int minBin = -1;
00259 float minCost = INFINITY;
00260
00261
00262 for (int i = 0; i < NB_BINS - 1; ++i) {
00263 float cost = vLeft[i] * nbPrimsLeft[i] +
00264 vRight[i + 1] * nbPrimsRight[i + 1];
00265 if (cost < minCost) {
00266 minBin = i;
00267 minCost = cost;
00268 }
00269 }
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280 float splitPos = centroidsBbox.pMin[axis] + (minBin + 1) *
00281 (centroidsBbox.pMax[axis] - centroidsBbox.pMin[axis]) / NB_BINS;
00282
00283
00284 BBox leftChildBbox, rightChildBbox;
00285 BBox leftChildCentroidsBbox, rightChildCentroidsBbox;
00286
00287 u_int storeIndex = start;
00288 for (u_int i = start; i < end; ++i) {
00289 u_int primIndex = primsIndexes[i];
00290
00291 if (primsCentroids[primIndex][axis] <= splitPos) {
00292
00293 primsIndexes[i] = primsIndexes[storeIndex];
00294 primsIndexes[storeIndex] = primIndex;
00295 ++storeIndex;
00296
00297
00298
00299 leftChildBbox = Union(leftChildBbox, primsBboxes[primIndex]);
00300 leftChildCentroidsBbox = Union(leftChildCentroidsBbox, primsCentroids[primIndex]);
00301 } else {
00302
00303
00304 rightChildBbox = Union(rightChildBbox, primsBboxes[primIndex]);
00305 rightChildCentroidsBbox = Union(rightChildCentroidsBbox, primsCentroids[primIndex]);
00306 }
00307 }
00308
00309
00310 BuildTree(start, storeIndex, primsIndexes, primsBboxes, primsCentroids,
00311 leftChildBbox, leftChildCentroidsBbox, currentNode,
00312 leftChildIndex, depth + 1);
00313 BuildTree(storeIndex, end, primsIndexes, primsBboxes, primsCentroids,
00314 rightChildBbox, rightChildCentroidsBbox, currentNode,
00315 rightChildIndex, depth + 1);
00316 }
00317
00318
00319
00320 void QBVHAccel::CreateTempLeaf(int32_t parentIndex, int32_t childIndex,
00321 u_int start, u_int end, const BBox &nodeBbox) {
00322
00323 if (parentIndex < 0) {
00324
00325 nNodes = 1;
00326 parentIndex = 0;
00327 }
00328
00329
00330
00331
00332 u_int nbPrimsTotal = end - start;
00333
00334 QBVHNode &node = nodes[parentIndex];
00335
00336 node.SetBBox(childIndex, nodeBbox);
00337
00338
00339
00340 u_int quads = (nbPrimsTotal + 3) / 4;
00341
00342
00343 node.InitializeLeaf(childIndex, quads, start);
00344
00345 nQuads += quads;
00346 }
00347
00348 void QBVHAccel::PreSwizzle(int32_t nodeIndex, u_int *primsIndexes) {
00349 for (int i = 0; i < 4; ++i) {
00350 if (nodes[nodeIndex].ChildIsLeaf(i))
00351 CreateSwizzledLeaf(nodeIndex, i, primsIndexes);
00352 else
00353 PreSwizzle(nodes[nodeIndex].children[i], primsIndexes);
00354 }
00355 }
00356
00357 void QBVHAccel::CreateSwizzledLeaf(int32_t parentIndex, int32_t childIndex,
00358 u_int *primsIndexes) {
00359 QBVHNode &node = nodes[parentIndex];
00360 if (node.LeafIsEmpty(childIndex))
00361 return;
00362 const u_int startQuad = nQuads;
00363 const u_int nbQuads = node.NbQuadsInLeaf(childIndex);
00364
00365 u_int primOffset = node.FirstQuadIndexForLeaf(childIndex);
00366 u_int primNum = nQuads;
00367
00368 const Point *vertices = mesh->GetVertices();
00369 const Triangle *triangles = mesh->GetTriangles();
00370
00371 for (u_int q = 0; q < nbQuads; ++q) {
00372 new (&prims[primNum]) QuadTriangle(triangles, vertices, primsIndexes[primOffset],
00373 primsIndexes[primOffset + 1], primsIndexes[primOffset + 2], primsIndexes[primOffset + 3]);
00374
00375 ++primNum;
00376 primOffset += 4;
00377 }
00378 nQuads += nbQuads;
00379 node.InitializeLeaf(childIndex, nbQuads, startQuad);
00380 }
00381
00382
00383
00384 bool QBVHAccel::Intersect(const Ray *ray, RayHit *rayHit) const {
00385
00386
00387 QuadRay ray4(*ray);
00388 __m128 invDir[3];
00389 invDir[0] = _mm_set1_ps(1.f / ray->d.x);
00390 invDir[1] = _mm_set1_ps(1.f / ray->d.y);
00391 invDir[2] = _mm_set1_ps(1.f / ray->d.z);
00392
00393 int signs[3];
00394 ray->GetDirectionSigns(signs);
00395
00396
00397
00398 int todoNode = 0;
00399 int32_t nodeStack[64];
00400 nodeStack[0] = 0;
00401
00402 while (todoNode >= 0) {
00403
00404 if (!QBVHNode::IsLeaf(nodeStack[todoNode])) {
00405 QBVHNode &node = nodes[nodeStack[todoNode]];
00406 --todoNode;
00407
00408
00409 const int32_t visit = node.BBoxIntersect(ray4, invDir, signs);
00410
00411 switch (visit) {
00412 case (0x1 | 0x0 | 0x0 | 0x0):
00413 nodeStack[++todoNode] = node.children[0];
00414 break;
00415 case (0x0 | 0x2 | 0x0 | 0x0):
00416 nodeStack[++todoNode] = node.children[1];
00417 break;
00418 case (0x1 | 0x2 | 0x0 | 0x0):
00419 nodeStack[++todoNode] = node.children[0];
00420 nodeStack[++todoNode] = node.children[1];
00421 break;
00422 case (0x0 | 0x0 | 0x4 | 0x0):
00423 nodeStack[++todoNode] = node.children[2];
00424 break;
00425 case (0x1 | 0x0 | 0x4 | 0x0):
00426 nodeStack[++todoNode] = node.children[0];
00427 nodeStack[++todoNode] = node.children[2];
00428 break;
00429 case (0x0 | 0x2 | 0x4 | 0x0):
00430 nodeStack[++todoNode] = node.children[1];
00431 nodeStack[++todoNode] = node.children[2];
00432 break;
00433 case (0x1 | 0x2 | 0x4 | 0x0):
00434 nodeStack[++todoNode] = node.children[0];
00435 nodeStack[++todoNode] = node.children[1];
00436 nodeStack[++todoNode] = node.children[2];
00437 break;
00438 case (0x0 | 0x0 | 0x0 | 0x8):
00439 nodeStack[++todoNode] = node.children[3];
00440 break;
00441 case (0x1 | 0x0 | 0x0 | 0x8):
00442 nodeStack[++todoNode] = node.children[0];
00443 nodeStack[++todoNode] = node.children[3];
00444 break;
00445 case (0x0 | 0x2 | 0x0 | 0x8):
00446 nodeStack[++todoNode] = node.children[1];
00447 nodeStack[++todoNode] = node.children[3];
00448 break;
00449 case (0x1 | 0x2 | 0x0 | 0x8):
00450 nodeStack[++todoNode] = node.children[0];
00451 nodeStack[++todoNode] = node.children[1];
00452 nodeStack[++todoNode] = node.children[3];
00453 break;
00454 case (0x0 | 0x0 | 0x4 | 0x8):
00455 nodeStack[++todoNode] = node.children[2];
00456 nodeStack[++todoNode] = node.children[3];
00457 break;
00458 case (0x1 | 0x0 | 0x4 | 0x8):
00459 nodeStack[++todoNode] = node.children[0];
00460 nodeStack[++todoNode] = node.children[2];
00461 nodeStack[++todoNode] = node.children[3];
00462 break;
00463 case (0x0 | 0x2 | 0x4 | 0x8):
00464 nodeStack[++todoNode] = node.children[1];
00465 nodeStack[++todoNode] = node.children[2];
00466 nodeStack[++todoNode] = node.children[3];
00467 break;
00468 case (0x1 | 0x2 | 0x4 | 0x8):
00469 nodeStack[++todoNode] = node.children[0];
00470 nodeStack[++todoNode] = node.children[1];
00471 nodeStack[++todoNode] = node.children[2];
00472 nodeStack[++todoNode] = node.children[3];
00473 break;
00474 }
00475 } else {
00476
00477
00478
00479 const int32_t leafData = nodeStack[todoNode];
00480 --todoNode;
00481
00482 if (QBVHNode::IsEmpty(leafData))
00483 continue;
00484
00485
00486 const u_int nbQuadPrimitives = QBVHNode::NbQuadPrimitives(leafData);
00487
00488 const u_int offset = QBVHNode::FirstQuadIndex(leafData);
00489
00490 for (u_int primNumber = offset; primNumber < (offset + nbQuadPrimitives); ++primNumber)
00491 prims[primNumber].Intersect(ray4, *ray, rayHit);
00492 }
00493 }
00494
00495 return !rayHit->Miss();
00496 }