#include #include #include #include "Environment.h" #include "Mesh.h" #include "KdTree.h" #include "ViewCell.h" #include "Beam.h" namespace GtpVisibilityPreprocessor { int KdNode::mailID = 1; KdNode::KdNode(KdInterior *parent):mParent(parent), mailbox(0) { if (parent) mDepth = parent->mDepth+1; else mDepth = 0; } KdInterior::~KdInterior() { // recursivly destroy children DEL_PTR(mFront); DEL_PTR(mBack); } KdTree::KdTree() { mRoot = new KdLeaf(NULL, 0); Environment::GetSingleton()->GetIntValue("KdTree.Termination.maxNodes", mTermMaxNodes); Environment::GetSingleton()->GetIntValue("KdTree.Termination.maxDepth", mTermMaxDepth); Environment::GetSingleton()->GetIntValue("KdTree.Termination.minCost", mTermMinCost); Environment::GetSingleton()->GetFloatValue("KdTree.Termination.maxCostRatio", mMaxCostRatio); Environment::GetSingleton()->GetFloatValue("KdTree.Termination.ct_div_ci", mCt_div_ci); Environment::GetSingleton()->GetFloatValue("KdTree.splitBorder", mSplitBorder); Environment::GetSingleton()->GetBoolValue("KdTree.sahUseFaces", mSahUseFaces); char splitType[64]; Environment::GetSingleton()->GetStringValue("KdTree.splitMethod", splitType); mSplitMethod = SPLIT_SPATIAL_MEDIAN; if (strcmp(splitType, "spatialMedian") == 0) mSplitMethod = SPLIT_SPATIAL_MEDIAN; else if (strcmp(splitType, "objectMedian") == 0) mSplitMethod = SPLIT_OBJECT_MEDIAN; else if (strcmp(splitType, "SAH") == 0) mSplitMethod = SPLIT_SAH; else { cerr<<"Wrong kd split type "<; // first construct a leaf that will get subdivide KdLeaf *leaf = (KdLeaf *) mRoot; mStat.nodes = 1; mBox.Initialize(); ObjectContainer::const_iterator mi; for ( mi = leaf->mObjects.begin(); mi != leaf->mObjects.end(); mi++) { // cout<<(*mi)->GetBox()<GetBox()); } ProcessLeafObjects(leaf, NULL); cout <<"KdTree Root Box:"< tStack; // stack tStack; tStack.push(tdata); AxisAlignedBox3 backBox, frontBox; while (!tStack.empty()) { // cout< mTermMaxNodes) { // if ( GetMemUsage() > maxMemory ) { // count statistics on unprocessed leafs while (!tStack.empty()) { EvaluateLeafStats(tStack.top()); tStack.pop(); } break; } TraversalData data = tStack.top(); tStack.pop(); KdNode *node = SubdivideNode((KdLeaf *) data.mNode, data.mBox, backBox, frontBox ); if (result == NULL) result = node; if (!node->IsLeaf()) { KdInterior *interior = (KdInterior *) node; // push the children on the stack tStack.push(TraversalData(interior->mBack, backBox, data.mDepth+1)); tStack.push(TraversalData(interior->mFront, frontBox, data.mDepth+1)); } else { EvaluateLeafStats(data); } } return result; } bool KdTree::TerminationCriteriaMet(const KdLeaf *leaf) { // cerr<<"\n OBJECTS="<mObjects.size()<mObjects.size() <= mTermMinCost) || (leaf->mDepth >= mTermMaxDepth); } int KdTree::SelectPlane(KdLeaf *leaf, const AxisAlignedBox3 &box, float &position ) { int axis = -1; switch (mSplitMethod) { case SPLIT_SPATIAL_MEDIAN: { axis = box.Size().DrivingAxis(); position = (box.Min()[axis] + box.Max()[axis])*0.5f; break; } case SPLIT_SAH: { int objectsBack, objectsFront; float costRatio; bool mOnlyDrivingAxis = false; if (mOnlyDrivingAxis) { axis = box.Size().DrivingAxis(); costRatio = BestCostRatio(leaf, box, axis, position, objectsBack, objectsFront); } else { costRatio = MAX_FLOAT; for (int i=0; i < 3; i++) { float p; float r = BestCostRatio(leaf, box, i, p, objectsBack, objectsFront); if (r < costRatio) { costRatio = r; axis = i; position = p; } } } if (costRatio > mMaxCostRatio) { //cout<<"Too big cost ratio "<mParent); node->mAxis = axis; node->mPosition = position; node->mBox = box; backBBox = box; frontBBox = box; // first count ray sides int objectsBack = 0; int objectsFront = 0; backBBox.SetMax(axis, position); frontBBox.SetMin(axis, position); ObjectContainer::const_iterator mi; for ( mi = leaf->mObjects.begin(); mi != leaf->mObjects.end(); mi++) { // determine the side of this ray with respect to the plane AxisAlignedBox3 box = (*mi)->GetBox(); if (box.Max(axis) > position ) objectsFront++; if (box.Min(axis) < position ) objectsBack++; } KdLeaf *back = new KdLeaf(node, objectsBack); KdLeaf *front = new KdLeaf(node, objectsFront); // replace a link from node's parent if ( leaf->mParent ) leaf->mParent->ReplaceChildLink(leaf, node); // and setup child links node->SetupChildLinks(back, front); for (mi = leaf->mObjects.begin(); mi != leaf->mObjects.end(); mi++) { // determine the side of this ray with respect to the plane AxisAlignedBox3 box = (*mi)->GetBox(); if (box.Max(axis) >= position ) front->mObjects.push_back(*mi); if (box.Min(axis) < position ) back->mObjects.push_back(*mi); mStat.objectRefs -= (int)leaf->mObjects.size(); mStat.objectRefs += objectsBack + objectsFront; } ProcessLeafObjects(back, leaf); ProcessLeafObjects(front, leaf); delete leaf; return node; } void KdTreeStatistics::Print(ostream &app) const { app << "===== KdTree statistics ===============\n"; app << "#N_NODES ( Number of nodes )\n" << nodes << "\n"; app << "#N_LEAVES ( Number of leaves )\n" << Leaves() << "\n"; app << "#N_SPLITS ( Number of splits in axes x y z dx dy dz)\n"; for (int i=0; i<7; i++) app << splits[i] <<" "; app < evaluate stats for leafs KdLeaf *leaf = (KdLeaf *)data.mNode; if (data.mDepth > mTermMaxDepth) mStat.maxDepthNodes++; if ( (int)(leaf->mObjects.size()) < mTermMinCost) mStat.minCostNodes++; if ( (int)(leaf->mObjects.size()) > mStat.maxObjectRefs) mStat.maxObjectRefs = (int)leaf->mObjects.size(); } void KdTree::SortSplitCandidates( KdLeaf *node, const int axis ) { splitCandidates->clear(); int requestedSize = 2*(int)node->mObjects.size(); // creates a sorted split candidates array if (splitCandidates->capacity() > 500000 && requestedSize < (int)(splitCandidates->capacity()/10) ) { delete splitCandidates; splitCandidates = new vector; } splitCandidates->reserve(requestedSize); // insert all queries for(ObjectContainer::const_iterator mi = node->mObjects.begin(); mi != node->mObjects.end(); mi++) { AxisAlignedBox3 box = (*mi)->GetBox(); splitCandidates->push_back(SortableEntry(SortableEntry::BOX_MIN, box.Min(axis), *mi) ); splitCandidates->push_back(SortableEntry(SortableEntry::BOX_MAX, box.Max(axis), *mi) ); } stable_sort(splitCandidates->begin(), splitCandidates->end()); } float KdTree::BestCostRatio( KdLeaf *node, const AxisAlignedBox3 &box, const int axis, float &position, int &objectsBack, int &objectsFront ) { SortSplitCandidates(node, axis); // go through the lists, count the number of objects left and right // and evaluate the following cost funcion: // C = ct_div_ci + (ol + or)/queries float totalIntersections = 0.0f; vector::const_iterator ci; for(ci = splitCandidates->begin(); ci < splitCandidates->end(); ci++) if ((*ci).type == SortableEntry::BOX_MIN) { totalIntersections += (*ci).intersectable->IntersectionComplexity(); } float intersectionsLeft = 0; float intersectionsRight = totalIntersections; int objectsLeft = 0, objectsRight = (int)node->mObjects.size(); float minBox = box.Min(axis); float maxBox = box.Max(axis); float boxArea = box.SurfaceArea(); float minBand = minBox + mSplitBorder*(maxBox - minBox); float maxBand = minBox + (1.0f - mSplitBorder)*(maxBox - minBox); float minSum = 1e20f; for(ci = splitCandidates->begin(); ci < splitCandidates->end(); ci++) { switch ((*ci).type) { case SortableEntry::BOX_MIN: objectsLeft++; intersectionsLeft += (*ci).intersectable->IntersectionComplexity(); break; case SortableEntry::BOX_MAX: objectsRight--; intersectionsRight -= (*ci).intersectable->IntersectionComplexity(); break; } if ((*ci).value > minBand && (*ci).value < maxBand) { AxisAlignedBox3 lbox = box; AxisAlignedBox3 rbox = box; lbox.SetMax(axis, (*ci).value); rbox.SetMin(axis, (*ci).value); float sum; if (mSahUseFaces) sum = intersectionsLeft*lbox.SurfaceArea() + intersectionsRight*rbox.SurfaceArea(); else sum = objectsLeft*lbox.SurfaceArea() + objectsRight*rbox.SurfaceArea(); // cout<<"pos="<<(*ci).value<<"\t q=("< tStack; float maxt = 1e6; float mint = 0; Intersectable::NewMail(); if (!mBox.GetMinMaxT(ray, &mint, &maxt)) return 0; if (mint < 0) mint = 0; maxt += Limits::Threshold; Vector3 entp = ray.Extrap(mint); Vector3 extp = ray.Extrap(maxt); KdNode *node = mRoot; KdNode *farChild; float position; int axis; while (1) { if (!node->IsLeaf()) { KdInterior *in = (KdInterior *) node; position = in->mPosition; axis = in->mAxis; if (entp[axis] <= position) { if (extp[axis] <= position) { node = in->mBack; // cases N1,N2,N3,P5,Z2,Z3 continue; } else { // case N4 node = in->mBack; farChild = in->mFront; } } else { if (position <= extp[axis]) { node = in->mFront; // cases P1,P2,P3,N5,Z1 continue; } else { node = in->mFront; farChild = in->mBack; // case P4 } } // $$ modification 3.5.2004 - hints from Kamil Ghais // case N4 or P4 float tdist = (position - ray.GetLoc(axis)) / ray.GetDir(axis); tStack.push(RayTraversalData(farChild, extp, maxt)); extp = ray.GetLoc() + ray.GetDir()*tdist; maxt = tdist; } else { // compute intersection with all objects in this leaf KdLeaf *leaf = (KdLeaf *) node; if (ray.mFlags & Ray::STORE_KDLEAVES) ray.kdLeaves.push_back(leaf); ObjectContainer::const_iterator mi; for ( mi = leaf->mObjects.begin(); mi != leaf->mObjects.end(); mi++) { Intersectable *object = *mi; if (!object->Mailed() ) { object->Mail(); if (ray.mFlags & Ray::STORE_TESTED_OBJECTS) ray.testedObjects.push_back(object); static int oi=1; if (MeshDebug) cout<<"Object "<CastRay(ray); if (MeshDebug) { if (ray.intersections.size()) cout<<"nearest t="< 1.0f) break; RayTraversalData &s = tStack.top(); node = s.mNode; extp = s.mExitPoint; maxt = s.mMaxT; tStack.pop(); } } return hits; } int KdTree::CastLineSegment(const Vector3 &origin, const Vector3 &termination, ViewCellContainer &viewcells) { int hits = 0; float mint = 0.0f, maxt = 1.0f; const Vector3 dir = termination - origin; stack tStack; Intersectable::NewMail(); //maxt += Limits::Threshold; Vector3 entp = origin; Vector3 extp = termination; KdNode *node = mRoot; KdNode *farChild; float position; int axis; while (1) { if (!node->IsLeaf()) { KdInterior *in = dynamic_cast(node); position = in->mPosition; axis = in->mAxis; if (entp[axis] <= position) { if (extp[axis] <= position) { node = in->mBack; // cases N1,N2,N3,P5,Z2,Z3 continue; } else { // case N4 node = in->mBack; farChild = in->mFront; } } else { if (position <= extp[axis]) { node = in->mFront; // cases P1,P2,P3,N5,Z1 continue; } else { node = in->mFront; farChild = in->mBack; // case P4 } } // $$ modification 3.5.2004 - hints from Kamil Ghais // case N4 or P4 float tdist = (position - origin[axis]) / dir[axis]; //tStack.push(RayTraversalData(farChild, extp, maxt)); //TODO extp = origin + dir * tdist; maxt = tdist; } else { // compute intersection with all objects in this leaf KdLeaf *leaf = dynamic_cast(node); // add view cell to intersections ViewCell *vc = leaf->mViewCell; if (!vc->Mailed()) { vc->Mail(); viewcells.push_back(vc); ++ hits; } // get the next node from the stack if (tStack.empty()) break; entp = extp; mint = maxt; RayTraversalData &s = tStack.top(); node = s.mNode; extp = s.mExitPoint; maxt = s.mMaxT; tStack.pop(); } } return hits; } void KdTree::CollectObjects(const AxisAlignedBox3 &box, ObjectContainer &objects) { stack nodeStack; nodeStack.push(mRoot); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); if (node->IsLeaf()) { KdLeaf *leaf = (KdLeaf *)node; for (int j=0; j < leaf->mObjects.size(); j++) { Intersectable *object = leaf->mObjects[j]; if (!object->Mailed() && Overlap(box, object->GetBox())) { object->Mail(); objects.push_back(object); } } } else { KdInterior *interior = (KdInterior *)node; if ( box.Max()[interior->mAxis] > interior->mPosition ) nodeStack.push(interior->mFront); if (box.Min()[interior->mAxis] < interior->mPosition) nodeStack.push(interior->mBack); } } } void KdTree::CollectObjects(KdNode *n, ObjectContainer &objects) { stack nodeStack; nodeStack.push(n); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); if (node->IsLeaf()) { KdLeaf *leaf = (KdLeaf *)node; for (int j=0; j < leaf->mObjects.size(); j++) { Intersectable *object = leaf->mObjects[j]; if (!object->Mailed()) { object->Mail(); objects.push_back(object); } } } else { KdInterior *interior = (KdInterior *)node; nodeStack.push(interior->mFront); nodeStack.push(interior->mBack); } } } // Find random neighbor which was not mailed KdNode * KdTree::FindRandomNeighbor(KdNode *n, bool onlyUnmailed ) { stack nodeStack; nodeStack.push(mRoot); AxisAlignedBox3 box = GetBox(n); int mask = rand(); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); if (node->IsLeaf()) { if ( node != n && (!onlyUnmailed || !node->Mailed()) ) return node; } else { KdInterior *interior = (KdInterior *)node; if (interior->mPosition > box.Max(interior->mAxis)) nodeStack.push(interior->mBack); else if (interior->mPosition < box.Min(interior->mAxis)) nodeStack.push(interior->mFront); else { // random decision if (mask&1) nodeStack.push(interior->mBack); else nodeStack.push(interior->mFront); mask = mask>>1; } } } return NULL; } int KdTree::FindNeighbors(KdNode *n, vector &neighbors, bool onlyUnmailed ) { stack nodeStack; nodeStack.push(mRoot); AxisAlignedBox3 box = GetBox(n); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); if (node->IsLeaf()) { if ( node != n && (!onlyUnmailed || !node->Mailed()) ) neighbors.push_back(node); } else { KdInterior *interior = (KdInterior *)node; if (interior->mPosition > box.Max(interior->mAxis)) nodeStack.push(interior->mBack); else if (interior->mPosition < box.Min(interior->mAxis)) nodeStack.push(interior->mFront); else { // random decision nodeStack.push(interior->mBack); nodeStack.push(interior->mFront); } } } return (int)neighbors.size(); } // Find random neighbor which was not mailed KdNode * KdTree::GetRandomLeaf(const Plane3 &plane) { stack nodeStack; nodeStack.push(mRoot); int mask = rand(); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); if (node->IsLeaf()) { return node; } else { KdInterior *interior = (KdInterior *)node; KdNode *next; if (GetBox(interior->mBack).Side(plane) < 0) next = interior->mFront; else if (GetBox(interior->mFront).Side(plane) < 0) next = interior->mBack; else { // random decision if (mask&1) next = interior->mBack; else next = interior->mFront; mask = mask>>1; } nodeStack.push(next); } } return NULL; } void KdTree::CollectLeaves(vector &leaves) { stack nodeStack; nodeStack.push(mRoot); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); if (node->IsLeaf()) { KdLeaf *leaf = (KdLeaf *)node; leaves.push_back(leaf); } else { KdInterior *interior = (KdInterior *)node; nodeStack.push(interior->mBack); nodeStack.push(interior->mFront); } } } void KdTree::CreateAndCollectViewCells(ViewCellContainer &vc) const { stack nodeStack; nodeStack.push(mRoot); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); if (node->IsLeaf()) { KdLeaf *leaf = (KdLeaf *)node; // kdtree used as view cell container => create view cell KdViewCell *viewCell = new KdViewCell(); leaf->mViewCell = viewCell; // push back pointer to this leaf viewCell->mLeaf = leaf; vc.push_back(viewCell); } else { KdInterior *interior = (KdInterior *)node; nodeStack.push(interior->mBack); nodeStack.push(interior->mFront); } } } int KdTree::CollectLeafPvs() { int totalPvsSize = 0; stack nodeStack; nodeStack.push(mRoot); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); if (node->IsLeaf()) { KdLeaf *leaf = (KdLeaf *)node; for (int j=0; j < leaf->mObjects.size(); j++) { Intersectable *object = leaf->mObjects[j]; if (!object->Mailed()) { object->Mail(); // add this node to pvs of all nodes it can see KdPvsMap::iterator ni = object->mKdPvs.mEntries.begin(); for (; ni != object->mKdPvs.mEntries.end(); ni++) { KdNode *node = (*ni).first; // $$ JB TEMPORARY solution -> should add object PVS or explictly computed // kd tree PVS float contribution; if (leaf->mKdPvs.AddSample(node, 1.0f, contribution)) totalPvsSize++; } } } } else { KdInterior *interior = (KdInterior *)node; nodeStack.push(interior->mFront); nodeStack.push(interior->mBack); } } return totalPvsSize; } KdNode * KdTree::GetRandomLeaf(const bool onlyUnmailed) { stack nodeStack; nodeStack.push(mRoot); int mask = rand(); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); if (node->IsLeaf()) { if ( (!onlyUnmailed || !node->Mailed()) ) return node; } else { KdInterior *interior = (KdInterior *)node; // random decision if (mask&1) nodeStack.push(interior->mBack); else nodeStack.push(interior->mFront); mask = mask>>1; } } return NULL; } int KdTree::CastBeam( Beam &beam ) { stack nodeStack; nodeStack.push(mRoot); while (!nodeStack.empty()) { KdNode *node = nodeStack.top(); nodeStack.pop(); int side = beam.ComputeIntersection(GetBox(node)); switch (side) { case -1: beam.mKdNodes.push_back(node); break; case 0: if (node->IsLeaf()) beam.mKdNodes.push_back(node); else { KdInterior *interior = (KdInterior *)node; KdNode *first = interior->mBack; KdNode *second = interior->mFront; if (interior->mAxis < 3) { // spatial split -> decide on the order of the nodes if (beam.mPlanes[0].mNormal[interior->mAxis] > 0) swap(first, second); } nodeStack.push(first); nodeStack.push(second); } break; // default: cull } } if (beam.mFlags & Beam::STORE_OBJECTS) { vector::const_iterator it, it_end = beam.mKdNodes.end(); Intersectable::NewMail(); for (it = beam.mKdNodes.begin(); it != it_end; ++ it) { CollectObjects(*it, beam.mObjects); } } return (int)beam.mKdNodes.size(); } void KdTree::ProcessLeafObjects(KdLeaf *leaf, KdLeaf *parent) const { ObjectContainer::const_iterator oit, oit_end = leaf->mObjects.end(); for (oit = leaf->mObjects.begin(); oit != oit_end; ++ oit) { Intersectable *object = *oit; if (parent) { set::iterator kdit = object->mKdLeaves.find(parent); // remove parent leaf if (kdit != object->mKdLeaves.end()) object->mKdLeaves.erase(kdit); } object->mKdLeaves.insert(leaf); if (object->mKdLeaves.size() > 1) leaf->mMultipleObjects.push_back(object); } } }