// ================================================================ // $Id: lsds_kdtree.cpp,v 1.18 2005/04/16 09:34:21 bittner Exp $ // **************************************************************** /** The KD tree based LSDS */ // Initial coding by /** @author Jiri Bittner */ // Standard headers #include #include #include #include #include #include "RssTree.h" #include "Environment.h" #include "VssRay.h" #include "Intersectable.h" #include "Ray.h" #include "Containers.h" #include "ViewCell.h" #include "Exporter.h" #include "Preprocessor.h" #include "SceneGraph.h" namespace GtpVisibilityPreprocessor { #define DEBUG_SPLIT_COST 0 #define DEBUG_SPLITS 0 // Static variables int RssTreeLeaf::mailID = 0; inline void AddObject2Pvs(Intersectable *object, const int side, int &pvsBack, int &pvsFront) { if (!object) return; if (side <= 0) { if (!object->Mailed() && !object->Mailed(2)) { pvsBack++; if (object->Mailed(1)) object->Mail(2); else object->Mail(); } } if (side >= 0) { if (!object->Mailed(1) && !object->Mailed(2)) { pvsFront++; if (object->Mailed()) object->Mail(2); else object->Mail(1); } } } inline void AddViewcells2Pvs(const ViewCellContainer &viewcells, const int side, int &viewcellsBack, int &viewcellsFront) { ViewCellContainer::const_iterator it = viewcells.begin(); for (; it != viewcells.end(); ++it) { ViewCell *viewcell = *it; if (side <= 0) { if (!viewcell->Mailed() && !viewcell->Mailed(2)) { viewcellsBack++; if (viewcell->Mailed(1)) viewcell->Mail(2); else viewcell->Mail(); } } if (side >= 0) { if (!viewcell->Mailed(1) && !viewcell->Mailed(2)) { viewcellsFront++; if (viewcell->Mailed()) viewcell->Mail(2); else viewcell->Mail(1); } } } } // Constructor RssTree::RssTree() { Environment::GetSingleton()->GetIntValue("RssTree.maxDepth", termMaxDepth); Environment::GetSingleton()->GetIntValue("RssTree.minPvs", termMinPvs); Environment::GetSingleton()->GetIntValue("RssTree.minRays", termMinRays); Environment::GetSingleton()->GetFloatValue("RssTree.maxRayContribution", termMaxRayContribution); Environment::GetSingleton()->GetFloatValue("RssTree.maxCostRatio", termMaxCostRatio); Environment::GetSingleton()->GetFloatValue("RssTree.minSize", termMinSize); termMinSize = sqr(termMinSize); Environment::GetSingleton()->GetFloatValue("RssTree.refDirBoxMaxSize", refDirBoxMaxSize); refDirBoxMaxSize = sqr(refDirBoxMaxSize); Environment::GetSingleton()->GetFloatValue("RssTree.epsilon", epsilon); Environment::GetSingleton()->GetFloatValue("RssTree.ct_div_ci", ct_div_ci); Environment::GetSingleton()->GetFloatValue("RssTree.maxTotalMemory", maxTotalMemory); Environment::GetSingleton()->GetFloatValue("RssTree.maxStaticMemory", maxStaticMemory); Environment::GetSingleton()->GetFloatValue("RssTree.maxStaticMemory", maxStaticMemory); Environment::GetSingleton()->GetIntValue("RssTree.accessTimeThreshold", accessTimeThreshold); //= 1000; Environment::GetSingleton()->GetIntValue("RssTree.minCollapseDepth", minCollapseDepth); // int minCollapseDepth = 4; // pRefDirThresh = cos(0.5*M_PI - M_PI*refDirAngle/180.0); // cosRefDir = cos(M_PI*refDirAngle/180.0); // sinRefDir = sin(M_PI*refDirAngle/180.0); // split type char sname[128]; Environment::GetSingleton()->GetStringValue("RssTree.splitType", sname); string name(sname); if (name.compare("regular") == 0) splitType = ESplitRegular; else if (name.compare("heuristic") == 0) splitType = ESplitHeuristic; else if (name.compare("hybrid") == 0) splitType = ESplitHybrid; else { cerr<<"Invalid RssTree split type "<GetBoolValue("RssTree.randomize", randomize); Environment::GetSingleton()->GetBoolValue("RssTree.splitUseOnlyDrivingAxis", mSplitUseOnlyDrivingAxis); Environment::GetSingleton()->GetBoolValue("RssTree.interleaveDirSplits", mInterleaveDirSplits); Environment::GetSingleton()->GetIntValue("RssTree.dirSplitDepth", mDirSplitDepth); Environment::GetSingleton()->GetBoolValue("RssTree.importanceBasedCost", mImportanceBasedCost); Environment::GetSingleton()->GetIntValue("RssTree.maxRays", mMaxRays); Environment::GetSingleton()->GetBoolValue("RssTree.perObjectTree", mPerObjectTree); // mRoots; splitCandidates = new vector; } RssTree::~RssTree() { for (int i=0; i < mRoots.size(); i++) if (mRoots[i]) delete mRoots[i]; } void RssStatistics::Print(ostream &app) const { app << "###### RssTree statistics ######\n"; app << "#N_RAYS ( Number of rays )\n" << rays <mValidPvs) { Intersectable::NewMail(); int pvsSize = 0; for(RssTreeNode::RayInfoContainer::iterator ri = leaf->rays.begin(); ri != leaf->rays.end(); ri++) if ((*ri).mRay->IsActive()) { Intersectable *object; object = (*ri).GetObject(); if (object && !object->Mailed()) { pvsSize++; object->Mail(); } } leaf->SetPvsSize(pvsSize); ComputeImportance(leaf); } } bool RssTree::ClipRay( RssTreeNode::RayInfo &rayInfo, const AxisAlignedBox3 &box ) { float tmin, tmax; static Ray ray; cerr<<"Clip not reimplented yet...Quiting\n"; exit(1); ray.Init(rayInfo.GetOrigin(), rayInfo.GetDir(), Ray::LINE_SEGMENT); if (!box.ComputeMinMaxT(ray, &tmin, &tmax) || tmin>=tmax) return false; // now check if the ray origin lies inside the box if ( tmax < rayInfo.mRay->GetSize() ) { // this ray does not leave the box rayInfo.mRay->SetupEndPoints( ray.Extrap(tmax), rayInfo.mRay->mTermination ); return true; } return false; } void RssTree::Construct( ObjectContainer &objects, VssRayContainer &rays // forced bounding box is only used when computing from-box // visibility // AxisAlignedBox3 *forcedBoundingBox ) { cout<<"Constructing rss tree"<bbox.Initialize(); leaf->dirBBox.Initialize(); leaf->dirBBox.SetMin(2, 0.0f); leaf->dirBBox.SetMax(2, 1.0f); mRoots[i] = leaf; } // init spatial bounding boxes for (i = 0; i < objects.size(); i++) { GetRoot(objects[i])->bbox = objects[i]->GetBox(); } stat.nodes = i; stat.leaves = i; } else { mRoots.resize(1); RssTreeLeaf *leaf = new RssTreeLeaf(NULL, rays.size()); leaf->bbox.Initialize(); leaf->dirBBox.Initialize(); leaf->dirBBox.SetMin(2, 0.0f); leaf->dirBBox.SetMax(2, 1.0f); mRoots[0] = leaf; stat.nodes = 1; stat.leaves = 1; } for(VssRayContainer::const_iterator ri = rays.begin(); ri != rays.end(); ri++) { RssTreeNode::RayInfo info(*ri); // first construct a leaf that will get subdivide RssTreeLeaf *leaf = (RssTreeLeaf *) GetRoot(info.GetSourceObject()); leaf->AddRay(info); // leaf bbox contains bbox of origins only leaf->bbox.Include((*ri)->GetOrigin()); // include both origin and terminatin in the global bbox bbox.Include((*ri)->GetOrigin()); bbox.Include((*ri)->GetTermination()); Vector3 dVec = Vector3( (*ri)->GetDirParametrization(0), (*ri)->GetDirParametrization(1), 0 ); leaf->dirBBox.Include(dVec); dirBBox.Include(dVec); } // make the z axis (unused) a unit size // important for volume computation // if ( forcedBoundingBox ) // bbox = *forcedBoundingBox; cout<<"Bbox = "< maxMemory ) { // count statistics on unprocessed leafs while (!tStack.empty()) { // EvaluateLeafStats(tStack.top()); tStack.pop(); } break; } TraversalData data = tStack.top(); tStack.pop(); if (data.node->IsLeaf()) { RssTreeNode *node = SubdivideNode((RssTreeLeaf *) data.node, data.bbox, backBox, frontBox ); if (!node->IsLeaf()) { subdivided++; RssTreeInterior *interior = (RssTreeInterior *) node; // push the children on the stack tStack.push(TraversalData(interior->back, backBox, data.depth+1)); tStack.push(TraversalData(interior->front, frontBox, data.depth+1)); } else { // EvaluateLeafStats(data); } } else { RssTreeInterior *interior = (RssTreeInterior *) data.node; tStack.push(TraversalData(interior->back, GetBBox(interior->back), data.depth+1)); tStack.push(TraversalData(interior->front, GetBBox(interior->front), data.depth+1)); } } return subdivided; } RssTreeNode * RssTree::Subdivide(const TraversalData &tdata) { RssTreeNode *result = NULL; priority_queue tStack; // stack tStack; tStack.push(tdata); AxisAlignedBox3 backBox; AxisAlignedBox3 frontBox; int lastMem = 0; while (!tStack.empty()) { float mem = GetMemUsage(); if ( lastMem/10 != ((int)mem)/10) { cout< maxMemory ) { // count statistics on unprocessed leafs while (!tStack.empty()) { EvaluateLeafStats(tStack.top()); tStack.pop(); } break; } TraversalData data = tStack.top(); tStack.pop(); #if DEBUG_SPLITS Debug<<"#Splitting node"<Print(Debug); #endif RssTreeNode *node = SubdivideNode((RssTreeLeaf *) data.node, data.bbox, backBox, frontBox ); if (result == NULL) result = node; if (!node->IsLeaf()) { RssTreeInterior *interior = (RssTreeInterior *) node; // push the children on the stack tStack.push(TraversalData(interior->back, backBox, data.depth+1)); tStack.push(TraversalData(interior->front, frontBox, data.depth+1)); #if DEBUG_SPLITS Debug<<"#New nodes"<back->Print(Debug); interior->front->Print(Debug); Debug<<"#####################################"<rays.size(); // cout<<"ratio="<depth < mDirSplitDepth || mInterleaveDirSplits)) || (axis >= 3 && (leaf->depth >= mDirSplitDepth)) ) { nInfo[axis].axis = axis; if (!mSplitUseOnlyDrivingAxis || axis == sAxis || axis == dAxis) { if (splitType == ESplitRegular) { if (axis < 3) nInfo[axis].position = (sBox.Min()[axis] + sBox.Max()[axis])*0.5f; else nInfo[axis].position = (dBox.Min()[axis-3] + dBox.Max()[axis-3])*0.5f; EvalCostRatio(leaf, nInfo[axis]); } else if (splitType == ESplitHeuristic) { EvalCostRatioHeuristic( leaf, nInfo[axis] ); } else if (splitType == ESplitHybrid) { if (leaf->depth > 7) EvalCostRatioHeuristic( leaf, nInfo[axis] ); else { if (axis < 3) nInfo[axis].position = (sBox.Min()[axis] + sBox.Max()[axis])*0.5f; else nInfo[axis].position = (dBox.Min()[axis-3] + dBox.Max()[axis-3])*0.5f; EvalCostRatio(leaf, nInfo[axis] ); } } else { cerr<<"RssTree: Unknown split heuristics\n"; exit(1); } if ( bestAxis == -1) bestAxis = axis; else if ( nInfo[axis].costRatio < nInfo[bestAxis].costRatio ) bestAxis = axis; } } info = nInfo[bestAxis]; } void RssTree::EvalCostRatioHeuristic( RssTreeLeaf *leaf, SplitInfo &info ) { AxisAlignedBox3 box; float minBox, maxBox; if (info.axis < 3) { box = GetBBox(leaf); minBox = box.Min(info.axis); maxBox = box.Max(info.axis); } else { box = GetDirBBox(leaf); minBox = box.Min(info.axis-3); maxBox = box.Max(info.axis-3); } SortSplitCandidates(leaf, info.axis); // go through the lists, count the number of objects left and right // and evaluate the following cost funcion: // C = ct_div_ci + (ql*rl + qr*rr)/queries SplitInfo currInfo; currInfo.axis = info.axis; currInfo.raysBack = 0; currInfo.raysFront = leaf->rays.size(); currInfo.pvsBack = 0; currInfo.pvsFront = leaf->GetPvsSize(); float sizeBox = maxBox - minBox; float minBand = minBox + 0.1f*(maxBox - minBox); float maxBand = minBox + 0.9f*(maxBox - minBox); // best cost ratio info.costRatio = 1e20f; currInfo.viewCells = 0; Intersectable::NewMail(); // set all object as belonging to the fron pvs for(RssTreeNode::RayInfoContainer::iterator ri = leaf->rays.begin(); ri != leaf->rays.end(); ri++) if ((*ri).mRay->IsActive()) { Intersectable *object = (*ri).GetObject(); if (object) if (!object->Mailed()) { object->Mail(); object->mCounter = 1; } else object->mCounter++; // and do the same for all viewcells ViewCellContainer::const_iterator it = (*ri).mRay->mViewCells.begin(); for (; it != (*ri).mRay->mViewCells.end(); ++it) { ViewCell *viewcell = *it; if (!viewcell->Mailed()) { currInfo.viewCells++; viewcell->Mail(); viewcell->mCounter = 1; } else viewcell->mCounter++; } } currInfo.viewCellsBack = 0; currInfo.viewCellsFront = currInfo.viewCells; Intersectable::NewMail(); for(vector::const_iterator ci = splitCandidates->begin(); ci < splitCandidates->end(); ci++) { switch ((*ci).type) { case SortableEntry::ERayMin: { currInfo.raysFront--; currInfo.raysBack++; RssTreeNode::RayInfo *rayInfo = (RssTreeNode::RayInfo *) (*ci).data; Intersectable *object = rayInfo->GetObject(); if (object) { if (!object->Mailed()) { object->Mail(); currInfo.pvsBack++; } if (--object->mCounter == 0) currInfo.pvsFront--; } ViewCellContainer::const_iterator it = rayInfo->mRay->mViewCells.begin(); for (; it != rayInfo->mRay->mViewCells.end(); ++it) { ViewCell *viewcell = *it; if (!viewcell->Mailed()) { viewcell->Mail(); currInfo.viewCellsBack++; } if (--viewcell->mCounter == 0) currInfo.viewCellsFront--; } break; } } float position = (*ci).value; if (position > minBand && position < maxBand) { currInfo.position = position; GetCostRatio( leaf, currInfo); // cout<<"pos="<<(*ci).value<<"\t q=("<clear(); int requestedSize = 2*(node->rays.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(RssTreeNode::RayInfoContainer::const_iterator ri = node->rays.begin(); ri < node->rays.end(); ri++) { if ((*ri).mRay->IsActive()) { if (axis < 3) { splitCandidates->push_back(SortableEntry(SortableEntry::ERayMin, (*ri).GetOrigin(axis), (void *)&(*ri)) ); } else { float pos = (*ri).GetDirParametrization(axis-3); splitCandidates->push_back(SortableEntry(SortableEntry::ERayMin, pos, (void *)&(*ri)) ); } } } stable_sort(splitCandidates->begin(), splitCandidates->end()); } void RssTree::EvaluateLeafStats(const TraversalData &data) { // the node became a leaf -> evaluate stats for leafs RssTreeLeaf *leaf = (RssTreeLeaf *)data.node; if (data.depth >= termMaxDepth) stat.maxDepthNodes++; // if ( (int)(leaf->rays.size()) < termMinCost) // stat.minCostNodes++; if ( leaf->GetPvsSize() <= termMinPvs) stat.minPvsNodes++; if ( leaf->GetPvsSize() <= termMinRays) stat.minRaysNodes++; if (leaf->GetAvgRayContribution() > termMaxRayContribution ) stat.maxRayContribNodes++; if (SqrMagnitude(data.bbox.Size()) <= termMinSize) { stat.minSizeNodes++; } if ( (int)(leaf->rays.size()) > stat.maxRayRefs) stat.maxRayRefs = leaf->rays.size(); } bool RssTree::TerminationCriteriaSatisfied(RssTreeLeaf *leaf) { return ( (leaf->GetPvsSize() <= termMinPvs) || (leaf->rays.size() <= termMinRays) || // (leaf->GetAvgRayContribution() > termMaxRayContribution ) || (leaf->depth >= termMaxDepth) || (SqrMagnitude(GetBBox(leaf).Size()) <= termMinSize) ); } RssTreeNode * RssTree::SubdivideNode( RssTreeLeaf *leaf, const AxisAlignedBox3 &box, AxisAlignedBox3 &backBBox, AxisAlignedBox3 &frontBBox ) { if (TerminationCriteriaSatisfied(leaf)) { #if 0 if (leaf->depth >= termMaxDepth) { cout<<"Warning: max depth reached depth="<<(int)leaf->depth<<" rays="<rays.size()<mT << endl; // determine the side of this ray with respect to the plane int side = node->ComputeRaySide(*ri); if (side == 1) front->AddRay(*ri); else back->AddRay(*ri); } else (*ri).mRay->Unref(); } // distribute the total number of rays according to the distribution // of rays which remained // front->mTotalRays = front->rays.size()*leaf->mTotalRays/leaf->rays.size(); // back->mTotalRays = back->rays.size()*leaf->mTotalRays/leaf->rays.size(); #if 0 front->SetPvsSize(pvsFront); back->SetPvsSize(pvsBack); // compute entropy as well front->ComputeEntropyImportance(); back->ComputeEntropyImportance(); #else UpdatePvsSize(front); UpdatePvsSize(back); #endif // update stats stat.rayRefs -= (int)leaf->rays.size(); stat.rayRefs += info.raysBack + info.raysFront; delete leaf; return node; } int RssTree::ReleaseMemory(const int time) { stack tstack; // find a node in the tree which subtree will be collapsed int maxAccessTime = time - accessTimeThreshold; int released; PushRoots(tstack); while (!tstack.empty()) { RssTreeNode *node = tstack.top(); tstack.pop(); if (!node->IsLeaf()) { RssTreeInterior *in = (RssTreeInterior *)node; // cout<<"depth="<<(int)in->depth<<" time="<lastAccessTime<depth >= minCollapseDepth && in->lastAccessTime <= maxAccessTime) { released = CollapseSubtree(node, time); break; } if (in->back->GetAccessTime() < in->front->GetAccessTime()) { tstack.push(in->front); tstack.push(in->back); } else { tstack.push(in->back); tstack.push(in->front); } } } while (tstack.empty()) { // could find node to collaps... // cout<<"Could not find a node to release "< maxTotalMemory) { ReleaseMemory( pass ); } AxisAlignedBox3 backBBox, frontBBox; // subdivide the node node = SubdivideNode(leaf, leafBBox, backBBox, frontBBox ); } return node; } void RssTree::UpdateRays( VssRayContainer &remove, VssRayContainer &add ) { RssTreeLeaf::NewMail(); // schedule rays for removal for(VssRayContainer::const_iterator ri = remove.begin(); ri != remove.end(); ri++) { (*ri)->ScheduleForRemoval(); } int inactive=0; for(VssRayContainer::const_iterator ri = remove.begin(); ri != remove.end(); ri++) { if ((*ri)->ScheduledForRemoval()) // RemoveRay(*ri, NULL, false); // !!! BUG - with true it does not work correctly - aggreated delete RemoveRay(*ri, NULL, true); else inactive++; } // cout<<"all/inactive"< mMaxRays) { PruneRays(mMaxRays); // UpdateTreeStatistics(); } } void RssTree::RemoveRay(VssRay *ray, vector *affectedLeaves, const bool removeAllScheduledRays ) { stack tstack; PushRoots(tstack, RssTreeLeaf::RayInfo(ray)); RayTraversalData data; // cout<<"Number of ray refs = "<RefCount()<IsLeaf()) { // split the set of rays in two groups intersecting the // two subtrees TraverseInternalNode(data, tstack); } else { // remove the ray from the leaf // find the ray in the leaf and swap it with the last ray... RssTreeLeaf *leaf = (RssTreeLeaf *)data.node; if (!leaf->Mailed()) { leaf->Mail(); if (affectedLeaves) affectedLeaves->push_back(leaf); if (removeAllScheduledRays) { int tail = (int)leaf->rays.size()-1; for (int i=0; i < (int)(leaf->rays.size()); i++) { if (leaf->rays[i].mRay->ScheduledForRemoval()) { // find a ray to replace it with while (tail >= i && leaf->rays[tail].mRay->ScheduledForRemoval()) { stat.removedRayRefs++; leaf->rays[tail].mRay->Unref(); leaf->rays.pop_back(); tail--; } if (tail < i) break; stat.removedRayRefs++; leaf->rays[i].mRay->Unref(); leaf->rays[i] = leaf->rays[tail]; leaf->rays.pop_back(); tail--; } } } } if (!removeAllScheduledRays) for (int i=0; i < (int)leaf->rays.size(); i++) { if (leaf->rays[i].mRay == ray) { stat.removedRayRefs++; ray->Unref(); leaf->rays[i] = leaf->rays[leaf->rays.size()-1]; leaf->rays.pop_back(); // check this ray again break; } } } } if (ray->RefCount() != 0) { cerr<<"Error: Number of remaining refs = "<RefCount()< tstack; RssTreeNode *root = GetRoot(info.GetSourceObject()); tstack.push(RayTraversalData(root, info)); RayTraversalData data; while (!tstack.empty()) { data = tstack.top(); tstack.pop(); if (!data.node->IsLeaf()) { TraverseInternalNode(data, tstack); } else { // remove the ray from the leaf // find the ray in the leaf and swap it with the last ray... RssTreeLeaf *leaf = (RssTreeLeaf *)data.node; leaf->AddRay(data.rayData); stat.addedRayRefs++; } } } void RssTree::TraverseInternalNode( RayTraversalData &data, stack &tstack) { RssTreeInterior *in = (RssTreeInterior *) data.node; // determine the side of this ray with respect to the plane int side = in->ComputeRaySide(data.rayData ); if (side == 1) tstack.push(RayTraversalData(in->front, data.rayData)); else tstack.push(RayTraversalData(in->back, data.rayData)); } int RssTree::CollapseSubtree(RssTreeNode *sroot, const int time) { // first count all rays in the subtree // use mail 1 for this purpose stack tstack; int rayCount = 0; int totalRayCount = 0; int collapsedNodes = 0; #if DEBUG_COLLAPSE cout<<"Collapsing subtree"< tstack; PushRoots(tstack); Intersectable::NewMail(); int pvsSize = 0; while (!tstack.empty()) { RssTreeNode *node = tstack.top(); tstack.pop(); if (node->IsLeaf()) { RssTreeLeaf *leaf = (RssTreeLeaf *)node; for(RssTreeNode::RayInfoContainer::iterator ri = leaf->rays.begin(); ri != leaf->rays.end(); ri++) if ((*ri).mRay->IsActive()) { Intersectable *object; object = (*ri).GetObject(); if (object && !object->Mailed()) { pvsSize++; object->Mail(); } } } else { RssTreeInterior *in = (RssTreeInterior *)node; if (in->axis < 3) { if (box.Max(in->axis) >= in->position ) tstack.push(in->front); if (box.Min(in->axis) <= in->position ) tstack.push(in->back); } else { // both nodes for directional splits tstack.push(in->front); tstack.push(in->back); } } } return pvsSize; } int RssTree::CollectPvs(const AxisAlignedBox3 &box, ObjectContainer &pvs ) const { stack tstack; PushRoots(tstack); Intersectable::NewMail(); while (!tstack.empty()) { RssTreeNode *node = tstack.top(); tstack.pop(); if (node->IsLeaf()) { RssTreeLeaf *leaf = (RssTreeLeaf *)node; for(RssTreeNode::RayInfoContainer::iterator ri = leaf->rays.begin(); ri != leaf->rays.end(); ri++) if ((*ri).mRay->IsActive()) { Intersectable *object; object = (*ri).GetObject(); if (object && !object->Mailed()) { pvs.push_back(object); object->Mail(); } } } else { RssTreeInterior *in = (RssTreeInterior *)node; if (in->axis < 3) { if (box.Max(in->axis) >= in->position ) tstack.push(in->front); if (box.Min(in->axis) <= in->position ) tstack.push(in->back); } else { // both nodes for directional splits tstack.push(in->front); tstack.push(in->back); } } } return pvs.size(); } void RssTree::UpdateTreeStatistics() { stack tstack; PushRoots(tstack); float sumPvsSize = 0.0f; float sumRayContribution = 0.0f; float sumWeightedRayContribution = 0.0f; float sumImportance = 0.0f; float sumPvsEntropy = 0.0f; float sumRayLengthEntropy = 0.0f; float sumRays = 0.0f; int leaves = 0; while (!tstack.empty()) { RssTreeNode *node = tstack.top(); tstack.pop(); if (node->IsLeaf()) { leaves++; RssTreeLeaf *leaf = (RssTreeLeaf *)node; UpdatePvsSize(leaf); sumPvsSize += leaf->GetPvsSize(); sumRayContribution += leaf->GetAvgRayContribution(); RssTreeNode::RayInfoContainer::const_iterator it = leaf->rays.begin(); for (;it != leaf->rays.end(); ++it) { float weight, contribution; GetRayContribution(*it, weight, contribution); sumWeightedRayContribution += weight*contribution; } // sumPvsEntropy += leaf->mPvsEntropy; // sumRayLengthEntropy += leaf->mRayLengthEntropy; sumRays += leaf->rays.size(); float imp = leaf->GetImportance(); // if (imp > 1.0f) // cout<<"warning imp > 1.0f:"<front); tstack.push(in->back); } } stat.avgPvsSize = sumPvsSize/(float)leaves; stat.avgRays = sumRays/(float)leaves; stat.avgRayContribution = sumRayContribution/(float)leaves; // avgPvsEntropy = sumPvsEntropy/(float)leaves; // avgRayLengthEntropy = sumRayLengthEntropy/(float)leaves; stat.avgImportance = sumImportance/(float)leaves; stat.avgWeightedRayContribution = sumWeightedRayContribution/(float)sumRays; stat.rayRefs = (int)sumRays; } int RssTree::GenerateRays( const float ratioPerLeaf, SimpleRayContainer &rays) { stack tstack; PushRoots(tstack); while (!tstack.empty()) { RssTreeNode *node = tstack.top(); tstack.pop(); if (node->IsLeaf()) { RssTreeLeaf *leaf = (RssTreeLeaf *)node; float c = leaf->GetImportance(); int num = (c*ratioPerLeaf + 0.5); // cout<SetWireframe(); // exporter->ExportKdTree(*mKdTree); exporter->SetWireframe(); exporter->ExportScene(preprocessor->mSceneGraph->mRoot); exporter->SetWireframe(); exporter->ExportBox(box); exporter->SetFilled(); counter++; } int numberOfTries = numberOfRays*4; int generated = 0; Intersectable::NewMail(); vector pvs(0); pvs.reserve(leaf->GetPvsSize()); for(RssTreeNode::RayInfoContainer::iterator ri = leaf->rays.begin(); ri != leaf->rays.end(); ri++) { Intersectable *object = (*ri).GetObject(); if (object) { if (!object->Mailed()) { object->Mail(); object->mCounter = 1; pvs.push_back(object); if (exporter) exporter->ExportIntersectable(object); } else { object->mCounter++; } } } // sort objects based on their mCounter sort(pvs.begin(), pvs.end(), Intersectable::GreaterCounter); float extendedConvexCombinationProb = 0.0f; //0.7f float silhouetteCheckPercentage = 1.0f; //0.5f for (int i=0; generated < numberOfRays && i < numberOfTries; i++) { bool useExtendedConvexCombination = ((nrays >= 2) && (Random(1.0f) < extendedConvexCombinationProb)); Vector3 origin, direction; // generate half of convex combination and half of random rays if (useExtendedConvexCombination) { // pickup 2 random rays int r1, r2; PickEdgeRays(leaf, r1, r2); Vector3 o1 = leaf->rays[r1].GetOrigin(); Vector3 o2 = leaf->rays[r2].GetOrigin(); const float overlap = 0.0f; float w1, w2; GenerateExtendedConvexCombinationWeights2(w1, w2, overlap); origin = w1*o1 + w2*o2; GenerateExtendedConvexCombinationWeights2(w1, w2, overlap); direction = w1*leaf->rays[r1].GetDir() + w2*leaf->rays[r2].GetDir(); } else { Vector3 dirVector; Vector3 pVector; Vector3 dVector; bool useHalton = true; if (useHalton) { // generate a random 5D vector pVector = Vector3(leaf->halton.GetNumber(1), leaf->halton.GetNumber(2), leaf->halton.GetNumber(3)); dVector = Vector3(leaf->halton.GetNumber(4), leaf->halton.GetNumber(5), 0.0f); leaf->halton.GenerateNext(); } else { pVector = Vector3(RandomValue(0.0f, 1.0f), RandomValue(0.0f, 1.0f), RandomValue(0.0f, 1.0f)); dVector = Vector3(RandomValue(0.0f, 1.0f), RandomValue(0.0f, 1.0f), 0.0f); } origin = box.GetPoint(pVector); dirVector = dirBox.GetPoint(dVector); direction = Vector3(sin(dirVector.x), sin(dirVector.y), cos(dirVector.x)); } // shift the origin a little bit direction.Normalize(); // float dist = Min(avgLength*0.5f, Magnitude(GetBBox(leaf).Size())); float dist = 0.0f; float minT, maxT; // compute interection of the ray with the box if (box.ComputeMinMaxT(origin, direction, &minT, &maxT) && minT < maxT) dist = maxT; origin += direction*dist; bool intersects = false; if (i > numberOfRays*(1.0f - silhouetteCheckPercentage)) { if (exporter) { VssRay *ray = new VssRay(origin, origin + 100*direction, NULL, NULL); selectedRays.push_back(ray); } // check whether the ray does not intersect already visible objects Ray traversalRay; traversalRay.Init(origin, direction, Ray::LOCAL_RAY); for(vector::const_iterator oi = pvs.begin(); oi != pvs.end(); oi++) { Intersectable *object = *oi; // do not test every object if (Random(1.0f) > 0.5f) break; if ( object->CastRay(traversalRay) ) { intersects = true; break; } } } if (!intersects) { //cout<<"dir vector.x="<rays[j].mRay->RefCount()<rays[i].mRay; } } leaf->rays.resize(j); int removed = (i-j); stat.rayRefs -= removed; return removed; } int RssTree::PruneRaysRandom(RssTreeLeaf *leaf, const float ratio) { int i; int j; for (j=0, i=0; i < leaf->rays.size(); i++) { if (Random(1.0f) < ratio) { // copy a valid sample if (i!=j) leaf->rays[j] = leaf->rays[i]; j++; } else { // delete the ray leaf->rays[i].mRay->Unref(); if (leaf->rays[i].mRay->RefCount() != 0) { cerr<<"Error: refcount!=0, but"<rays[j].mRay->RefCount()<rays[i].mRay; } } leaf->rays.resize(j); int removed = (i-j); stat.rayRefs -= removed; return removed; } int RssTree::PruneRaysContribution(RssTreeLeaf *leaf, const float ratio) { int i; if (leaf->rays.size() == 0) return 0; sort(leaf->rays.begin(), leaf->rays.end(), RssTreeLeaf::RayInfo::GreaterWeightedPvsContribution); int desired = ratio*leaf->rays.size(); int removed = leaf->rays.size() - desired; for (i=desired; i < leaf->rays.size(); i++) { // delete the ray leaf->rays[i].mRay->Unref(); if (leaf->rays[i].mRay->RefCount() != 0) { cerr<<"Error: refcount!=0, but"<rays[i].mRay->RefCount()<rays[i].mRay; } leaf->rays.resize(desired); stat.rayRefs -= removed; return removed; } int RssTree::PruneRays( const int desired ) { bool globalPrunning = true; stack tstack; int prunned = 0; Debug<<"Prunning rays...\nOriginal size "<= allRays.size() ) return 0; float contributionThreshold = allRays[desired]->mWeightedPvsContribution; PushRoots(tstack); while (!tstack.empty()) { RssTreeNode *node = tstack.top(); tstack.pop(); if (node->IsLeaf()) { RssTreeLeaf *leaf = (RssTreeLeaf *)node; prunned += PruneRays(leaf, contributionThreshold); } else { RssTreeInterior *in = (RssTreeInterior *)node; // both nodes for directional splits tstack.push(in->front); tstack.push(in->back); } } } else { // prune random rays from each leaf so that the ratio's remain the same PushRoots(tstack); float ratio = desired/(float)stat.rayRefs; while (!tstack.empty()) { RssTreeNode *node = tstack.top(); tstack.pop(); if (node->IsLeaf()) { RssTreeLeaf *leaf = (RssTreeLeaf *)node; // prunned += PruneRaysRandom(leaf, ratio); prunned += PruneRaysContribution(leaf, ratio); } else { RssTreeInterior *in = (RssTreeInterior *)node; // both nodes for directional splits tstack.push(in->front); tstack.push(in->back); } } } Debug<<"Remained "< leaves; CollectLeaves(leaves); sort(leaves.begin(), leaves.end(), GreaterContribution); float sumContrib = 0.0; int i; int k = 0; for (i=0; i < leaves.size() && k < numberOfLeaves; i++) if (ValidLeaf(leaves[i])) { float c = leaves[i]->GetImportance(); sumContrib += c; // cout<<"ray contrib "<GetImportance(); int num = fixedPerLeaf + (int)(c*ratioPerLeaf + 0.5f); GenerateLeafRays(leaf, num, rays); } return rays.size(); } float RssTree::GetAvgPvsSize() { stack tstack; PushRoots(tstack); int sumPvs = 0; int leaves = 0; while (!tstack.empty()) { RssTreeNode *node = tstack.top(); tstack.pop(); if (node->IsLeaf()) { RssTreeLeaf *leaf = (RssTreeLeaf *)node; // update pvs size UpdatePvsSize(leaf); sumPvs += leaf->GetPvsSize(); leaves++; } else { RssTreeInterior *in = (RssTreeInterior *)node; // both nodes for directional splits tstack.push(in->front); tstack.push(in->back); } } return sumPvs/(float)leaves; } float weightAbsContributions = 0.0f; // if small very high importance of the last sample // if 1.0f then weighs = 1 1/2 1/3 1/4 float passSampleWeightDecay = 1.0f; void RssTree::GetRayContribution(const RssTreeNode::RayInfo &info, float &weight, float &contribution) { VssRay *ray = info.mRay; weight = 1.0f/(mCurrentPass - ray->mPass + passSampleWeightDecay); contribution = weightAbsContributions*ray->mPvsContribution + (1.0f - weightAbsContributions)*ray->mRelativePvsContribution; // store the computed value info.mRay->mWeightedPvsContribution = weight*contribution; } float RssTree::GetSampleWeight(const int pass) { int passDiff = mCurrentPass - pass; float weight; if (1) weight = 1.0f/(passDiff + passSampleWeightDecay); else switch (passDiff) { case 0: weight = 1.0f; break; default: weight = 0.0f; break; // case 1: // weight = 0.5f; // break; // case 2: // weight = 0.25f; // break; // case 3: // weight = 0.12f; // break; // case 4: // weight = 0.06f; // break; // default: // weight = 0.03f; // break; } return weight; } void RssTree::ComputeImportance(RssTreeLeaf *leaf) { if (0) leaf->mImportance = leaf->GetAvgRayContribution(); else { RssTreeNode::RayInfoContainer::const_iterator it = leaf->rays.begin(), it_end = leaf->rays.end(); float sumContributions = 0.0f; float sumRelContributions = 0.0f; float sumWeights = 0.0f; for (; it != it_end; ++it) { VssRay *ray = (*it).mRay; float weight = GetSampleWeight(ray->mPass); sumContributions += weight*ray->mPvsContribution; sumRelContributions += weight*ray->mRelativePvsContribution; // sumWeights += weight; sumWeights += 1.0f; } // $$ // sumWeights = leaf->mTotalRays; if (sumWeights != 0.0f) leaf->mImportance = (weightAbsContributions*sumContributions + (1.0f - weightAbsContributions)*sumRelContributions)/sumWeights; else leaf->mImportance = 0.0f; } // return GetAvgRayContribution()*mImportance; //return GetAvgRayContribution(); } float RssTreeLeaf::GetImportance() const { return mImportance; } float RssTreeLeaf::ComputePvsEntropy() const { int samples = 0; Intersectable::NewMail(); // set all object as belonging to the fron pvs for(RssTreeNode::RayInfoContainer::const_iterator ri = rays.begin(); ri != rays.end(); ri++) if ((*ri).mRay->IsActive()) { Intersectable *object = (*ri).GetObject(); if (object) { if (!object->Mailed()) { object->Mail(); object->mCounter = 1; } else object->mCounter++; samples++; } } float entropy = 0.0f; if (samples > 1) { Intersectable::NewMail(); for(RayInfoContainer::const_iterator ri = rays.begin(); ri != rays.end(); ri++) if ((*ri).mRay->IsActive()) { Intersectable *object = (*ri).GetObject(); if (object) { if (!object->Mailed()) { object->Mail(); float p = object->mCounter/(float)samples; entropy -= p*log(p); } } } entropy = entropy/log((float)samples); } else entropy = 1.0f; return entropy; } float RssTreeLeaf::ComputeRayLengthEntropy() const { // get sum of all ray lengths // consider only passing rays or originating rays float sum = 0.0f; int samples = 0; int i=0; for(RayInfoContainer::const_iterator ri = rays.begin(); ri != rays.end(); ri++) if ((*ri).mRay->IsActive()) { // float s; // if (i == 0) // s = 200; // else // s = 100; // i++; sum += (*ri).mRay->GetSize(); samples++; } float entropy = 0.0f; if (samples > 1) { i = 0; for(RayInfoContainer::const_iterator ri = rays.begin(); ri != rays.end(); ri++) if ((*ri).mRay->IsActive()) { // float s; // if (i==0) // s = 200; // else // s = 100; // i++; // float p = s/sum; float p = (*ri).mRay->GetSize()/sum; entropy -= p*log(p); } entropy = entropy/log((float)samples); } else entropy = 1.0f; return entropy; } float RssTreeLeaf::ComputeEntropyImportance() const { // mEntropy = 1.0f - ComputeRayLengthEntropy(); return 1.0f - ComputePvsEntropy(); } float RssTreeLeaf::ComputeRayContributionImportance() const { // mPvsEntropy = ComputePvsEntropy(); // mRayLengthEntropy = ComputeRayLengthEntropy(); // mEntropy = 1.0f - ComputeRayLengthEntropy(); return 1.0f - ComputePvsEntropy(); } AxisAlignedBox3 RssTree::GetShrankedBBox(const RssTreeNode *node) const { if (node->parent == NULL) return bbox; if (!node->IsLeaf()) return ((RssTreeInterior *)node)->bbox; // evaluate bounding box from the ray origins AxisAlignedBox3 box; box.Initialize(); RssTreeLeaf *leaf = (RssTreeLeaf *)node; for(RssTreeNode::RayInfoContainer::iterator ri = leaf->rays.begin(); ri != leaf->rays.end(); ri++) if ((*ri).mRay->IsActive()) { box.Include((*ri).GetOrigin()); } return box; } int RssTree::CollectRays(VssRayContainer &rays, const int number) { VssRayContainer allRays; CollectRays(allRays); int desired = min(number, (int)allRays.size()); float prob = desired/(float)allRays.size(); while (rays.size() < desired) { VssRayContainer::const_iterator it = allRays.begin(); for (; it != allRays.end() && rays.size() < desired; it++) { if (Random(1.0f) < prob) rays.push_back(*it); } } return (int)rays.size(); } int RssTree::CollectRays(VssRayContainer &rays ) { VssRay::NewMail(); stack tstack; PushRoots(tstack); while (!tstack.empty()) { RssTreeNode *node = tstack.top(); tstack.pop(); if (node->IsLeaf()) { RssTreeLeaf *leaf = (RssTreeLeaf *)node; // update pvs size RssTreeNode::RayInfoContainer::const_iterator it = leaf->rays.begin(); for (;it != leaf->rays.end(); ++it) if (!(*it).mRay->Mailed()) { (*it).mRay->Mail(); rays.push_back((*it).mRay); } } else { RssTreeInterior *in = (RssTreeInterior *)node; // both nodes for directional splits tstack.push(in->front); tstack.push(in->back); } } return (int)rays.size(); } RssTreeNode * RssTree::GetRoot(Intersectable *object) const { if (mPerObjectTree && object) { int id = object->GetId()-1; if (id < 0 || id >= mRoots.size()) { Debug<<"Error: object Id out of range, Id="<