//Performs a plane sweep across the triangles to find the best splitting plane using the SAH. //From "On building fast kd-Trees for Ray Tracing, and on doing that in O(N log N)" by I. Wald and V. Havran pair<pair<SplitPlane, SplitSide>, double> KDNode::findPlane(vector<Triangle*>& triangles, AABB boundingBox) { double bestCost = INFINITY; SplitPlane bestPlane(0, 0); SplitSide bestSide; for (int dim = 0; dim < 3; dim++) { vector<SweepEvent> events; for (auto t : triangles) { AABB b = t->getBoundingBox().intersection(boundingBox); if (b.getSize()[dim] == 0.0) { events.push_back(SweepEvent(t, b.getStartpoint()[dim], PLANAR)); } else { events.push_back(SweepEvent(t, b.getStartpoint()[dim], START)); events.push_back(SweepEvent(t, b.getEndpoint()[dim], END)); } } sort(events.begin(), events.end()); int left = 0; int planar = 0; int right = triangles.size(); for (int i = 0; i < events.size(); i++) { double coordinate = events[i].coordinate; SplitPlane plane(coordinate, dim); int pAdd = 0; int pPlan = 0; int pRem = 0; while (i < events.size() && events[i].coordinate == coordinate && events[i].type == END) { pRem++; i++; } while (i < events.size() && events[i].coordinate == coordinate && events[i].type == PLANAR) { pPlan++; i++; } while (i < events.size() && events[i].coordinate == coordinate && events[i].type == START) { pAdd++; i++; } planar = pPlan; right -= pPlan; right -= pRem; pair<double, SplitSide> result = SAH(plane, boundingBox, left, right, planar); if (result.first < bestCost) { bestCost = result.first; bestPlane = plane; bestSide = result.second; } left += pAdd; left += pPlan; planar = 0; } } return make_pair(make_pair(bestPlane, bestSide), bestCost); }
void AABBTree::findBestPlane(const vector<Triangle>& T, const AABB& V, SplittingPlane& p_est, float& C_est, PlaneSide& pside_est) { C_est = numeric_limits<float>::max(); for(int axis=0; axis<3; axis++) { vector<SplitEvent> events; events.reserve(T.size()*2); for(int i=0; i<T.size(); ++i) { auto ti = T[i]; AABB tibb = V.clip(ti); if(tibb.isPlanar()) { events.push_back(SplitEvent(ti, axis, tibb.minPos(axis), SplitEvent::LyingOnPlane)); } else { events.push_back(SplitEvent(ti, axis, tibb.minPos(axis), SplitEvent::StartingOnPlane)); events.push_back(SplitEvent(ti, axis, tibb.maxPos(axis), SplitEvent::EndingOnPlane)); } } // sort the events sort(events.begin(), events.end()); // test every candidate plane int NL = 0, NP = 0, NR = T.size(); for(int i=0; i<events.size(); ++i) { auto p = events[i].p; int tLyingOnPlane = 0, tEndingOnPlane=0, tStartingOnPlane=0; while( i<events.size() && events[i].p.pe == p.pe && events[i].type == SplitEvent::EndingOnPlane ) { ++tEndingOnPlane; i++; } while( i<events.size() && events[i].p.pe == p.pe && events[i].type == SplitEvent::LyingOnPlane ) { ++tLyingOnPlane; i++; } while( i<events.size() && events[i].p.pe == p.pe && events[i].type == SplitEvent::StartingOnPlane ) { ++tStartingOnPlane; i++; } NP = tLyingOnPlane; NR -= tLyingOnPlane; NR -= tEndingOnPlane; float C; PlaneSide ps; SAH(p, V, NL, NR, NP, C, ps); if(C < C_est) { C_est = C; p_est = p; pside_est = ps; } NL += tStartingOnPlane; NL += tLyingOnPlane; NP = 0; } } }
/* BVH construction based on the algorithm presented in the paper: * * "Ray Tracing Deformable Scenes Using Dynamic Bounding Volume Hierarchies". * Ingo Wald, Solomon Boulos, and Peter Shirley * ACM Transactions on Graphics. * Volume 26 Issue 1, 2007. */ void BVH::splitNode( BVHNode **node, std::deque< PrimitiveAABBArea > &s, std::size_t first, std::size_t last, float s_area ) { (*node) = new BVHNode(); (*node)->first_ = first; (*node)->last_ = last; (*node)->left_ = nullptr; (*node)->right_ = nullptr; std::deque< PrimitiveAABBArea > s_aux; float best_cost = cost_intersec_tri_ * ( last + 1 - first ); int best_axis = -1; int best_event = -1; for ( int axis = 1; axis <= 3; axis++ ) { switch( axis ) { case 1: std::sort( s.begin() + first, s.begin() + last + 1, Comparator::sortInX ); break; case 2: std::sort( s.begin() + first, s.begin() + last + 1, Comparator::sortInY ); break; case 3: std::sort( s.begin() + first, s.begin() + last + 1, Comparator::sortInZ ); break; } s_aux = std::deque< PrimitiveAABBArea >( s.begin() + first, s.begin() + last + 1 ); for ( std::size_t i = first; i <= last; i++ ) { if ( i == first ) { s[i].left_area_ = std::numeric_limits< float >::infinity(); s_aux[0].left_aabb_ = s_aux[0].aabb_; } else { s[i].left_area_ = s_aux[ i - first - 1 ].left_aabb_.getArea(); s_aux[ i - first ].left_aabb_ = s_aux[ i - first ].aabb_ + s_aux[ i - first - 1 ].left_aabb_; } } for ( long int i = last; i >= static_cast< long int >( first ); i-- ) { if ( i == static_cast< long int >( last ) ) { s[i].right_area_ = std::numeric_limits< float >::infinity(); s_aux[ last - first ].right_aabb_ = s_aux[ last - first ].aabb_; } else { s[i].right_area_ = s_aux[ i - first + 1 ].right_aabb_.getArea(); s_aux[ i - first ].right_aabb_ = s_aux[ i - first ].aabb_ + s_aux[ i - first + 1 ].right_aabb_; } float this_cost = SAH( i - first + 1, s[i].left_area_, last - i, s[i].right_area_, s_area ); if ( this_cost < best_cost ) { best_cost = this_cost; best_event = i; best_axis = axis; } } } if ( best_axis == -1 ) // This is a leaf node { primitives_inserted_ += last - first + 1; std::stringstream progress_stream; progress_stream << "\r BVH building progress ............: " << std::fixed << std::setw( 6 ) << std::setprecision( 2 ) << 100.0f * static_cast< float >( primitives_inserted_ ) / primitives_.size() << "%"; std::clog << progress_stream.str(); //if ( last == first ) // std::clog << "One primitive node!\n"; for ( long unsigned int i = first; i <= last; i++ ) { primitive_id_[i] = s[i].primitive_id_; if ( i == first ) (*node)->aabb_ = s[i].aabb_; else (*node)->aabb_ = (*node)->aabb_ + s[i].aabb_; } } else // This is an inner node { // Make inner node with best_axis / best_event switch( best_axis ) { case 1: std::sort( s.begin() + first, s.begin() + last + 1, Comparator::sortInX ); break; case 2: std::sort( s.begin() + first, s.begin() + last + 1, Comparator::sortInY ); break; case 3: std::sort( s.begin() + first, s.begin() + last + 1, Comparator::sortInZ ); break; } splitNode( &(*node)->left_, s, first, best_event, s_area ); splitNode( &(*node)->right_, s, best_event + 1, last, s_area ); (*node)->aabb_ = (*node)->left_->aabb_ + (*node)->right_->aabb_; } }