#include #include #include #include "ViewCell.h" #include "Plane3.h" #include "HierarchyManager.h" #include "Mesh.h" #include "common.h" #include "Environment.h" #include "Polygon3.h" #include "Ray.h" #include "AxisAlignedBox3.h" #include "Exporter.h" #include "Plane3.h" #include "ViewCellsManager.h" #include "Beam.h" #include "KdTree.h" #include "IntersectableWrapper.h" #include "VspTree.h" #include "OspTree.h" #include "BvHierarchy.h" namespace GtpVisibilityPreprocessor { #define USE_FIXEDPOINT_T 0 /*******************************************************************/ /* class HierarchyManager implementation */ /*******************************************************************/ HierarchyManager::HierarchyManager(const int objectSpaceSubdivisionType): mObjectSpaceSubdivisionType(objectSpaceSubdivisionType), mOspTree(NULL), mBvHierarchy(NULL) { switch(mObjectSpaceSubdivisionType) { case KD_BASED_OBJ_SUBDIV: mOspTree = new OspTree(); mOspTree->mVspTree = mVspTree; mOspTree->mHierarchyManager = this; break; case BV_BASED_OBJ_SUBDIV: mBvHierarchy = new BvHierarchy(); mBvHierarchy->mHierarchyManager = this; break; default: break; } // hierarchy manager links view space partition and object space partition mVspTree = new VspTree(); mVspTree->mHierarchyManager = this; mViewSpaceSubdivisionType = KD_BASED_VIEWSPACE_SUBDIV; ParseEnvironment(); } HierarchyManager::HierarchyManager(KdTree *kdTree): mObjectSpaceSubdivisionType(KD_BASED_OBJ_SUBDIV), mBvHierarchy(NULL) { mOspTree = new OspTree(*kdTree); mOspTree->mVspTree = mVspTree; mVspTree = new VspTree(); mVspTree->mHierarchyManager = this; mViewSpaceSubdivisionType = KD_BASED_VIEWSPACE_SUBDIV; ParseEnvironment(); } void HierarchyManager::ParseEnvironment() { Environment::GetSingleton()->GetFloatValue( "Hierarchy.Termination.minGlobalCostRatio", mTermMinGlobalCostRatio); Environment::GetSingleton()->GetIntValue( "Hierarchy.Termination.globalCostMissTolerance", mTermGlobalCostMissTolerance); Environment::GetSingleton()->GetBoolValue( "Hierarchy.Construction.startWithObjectSpace", mStartWithObjectSpace); Environment::GetSingleton()->GetIntValue( "Hierarchy.Termination.maxLeaves", mTermMaxLeaves); Environment::GetSingleton()->GetIntValue( "Hierarchy.Construction.type", mConstructionType); Environment::GetSingleton()->GetIntValue( "Hierarchy.Construction.minDepthForOsp", mMinDepthForObjectSpaceSubdivion); Environment::GetSingleton()->GetIntValue( "Hierarchy.Construction.minDepthForVsp", mMinDepthForViewSpaceSubdivion); Environment::GetSingleton()->GetBoolValue( "Hierarchy.Construction.repairQueue", mRepairQueue); Environment::GetSingleton()->GetBoolValue( "Hierarchy.Construction.useMultiLevel", mUseMultiLevelConstruction); Environment::GetSingleton()->GetIntValue( "Hierarchy.Construction.levels", mNumMultiLevels); Environment::GetSingleton()->GetIntValue( "Hierarchy.Construction.minStepsOfSameType", mMinStepsOfSameType); char subdivisionStatsLog[100]; Environment::GetSingleton()->GetStringValue("Hierarchy.subdivisionStats", subdivisionStatsLog); mSubdivisionStats.open(subdivisionStatsLog); Environment::GetSingleton()->GetBoolValue( "Hierarchy.Construction.recomputeSplitPlaneOnRepair", mRecomputeSplitPlaneOnRepair); Debug << "******** Hierachy Manager Options ***********" << endl; Debug << "max leaves: " << mTermMaxLeaves << endl; Debug << "min global cost ratio: " << mTermMinGlobalCostRatio << endl; Debug << "global cost miss tolerance: " << mTermGlobalCostMissTolerance << endl; Debug << "min depth for object space subdivision: " << mMinDepthForObjectSpaceSubdivion << endl; Debug << "repair queue: " << mRepairQueue << endl; Debug << "number of multilevels: " << mNumMultiLevels << endl; Debug << "recompute split plane on repair: " << mRecomputeSplitPlaneOnRepair << endl; Debug << "minimal number of steps from same type: " << mMinStepsOfSameType << endl; switch (mConstructionType) { case 0: Debug << "construction type: sequential" << endl; break; case 1: Debug << "construction type: interleaved" << endl; break; case 2: Debug << "construction type: gradient" << endl; break; case 3: Debug << "construction type: multilevel" << endl; break; default: Debug << "construction type " << mConstructionType << " unknown" << endl; break; } //Debug << "min render cost " << mMinRenderCostDecrease << endl; Debug << endl; } HierarchyManager::~HierarchyManager() { DEL_PTR(mOspTree); DEL_PTR(mVspTree); DEL_PTR(mBvHierarchy); } int HierarchyManager::GetObjectSpaceSubdivisionType() const { return mObjectSpaceSubdivisionType; } int HierarchyManager::GetViewSpaceSubdivisionType() const { return mViewSpaceSubdivisionType; } void HierarchyManager::SetViewCellsManager(ViewCellsManager *vcm) { mVspTree->SetViewCellsManager(vcm); if (mOspTree) { mOspTree->SetViewCellsManager(vcm); } else if (mBvHierarchy) { mBvHierarchy->SetViewCellsManager(vcm); } } void HierarchyManager::SetViewCellsTree(ViewCellsTree *vcTree) { mVspTree->SetViewCellsTree(vcTree); } VspTree *HierarchyManager::GetVspTree() { return mVspTree; } /* AxisAlignedBox3 HierarchyManager::GetViewSpaceBox() const { return mVspTree->mBoundingBox; }*/ AxisAlignedBox3 HierarchyManager::GetObjectSpaceBox() const { switch (mObjectSpaceSubdivisionType) { case KD_BASED_OBJ_SUBDIV: return mOspTree->mBoundingBox; case BV_BASED_OBJ_SUBDIV: return mBvHierarchy->mBoundingBox; default: // hack: empty box return AxisAlignedBox3(); } } SubdivisionCandidate *HierarchyManager::NextSubdivisionCandidate(SplitQueue &splitQueue) { SubdivisionCandidate *splitCandidate = splitQueue.Top(); splitQueue.Pop(); return splitCandidate; } void HierarchyManager::EvalSubdivisionStats() { // TODO const float objectSpaceMem = GetObjectSpaceMemUsage(); const float viewSpaceMem = mVspTree->GetMemUsage(); // calculate cost in MB const float memoryCost = mHierarchyStats.mMemory / (1024.0f * 1024.0f) + objectSpaceMem + viewSpaceMem; //cout << "pvs entries " << mHierarchyStats.pvsEntries << endl; AddSubdivisionStats(mHierarchyStats.Leaves(), mHierarchyStats.mRenderCostDecrease, mHierarchyStats.mTotalCost, mHierarchyStats.mPvsEntries, memoryCost, 1.0f / (mHierarchyStats.mTotalCost * memoryCost) ); } void HierarchyManager::AddSubdivisionStats(const int splits, const float renderCostDecr, const float totalRenderCost, const int pvsEntries, const float memory, const float renderCostPerStorage) { mSubdivisionStats << "#Splits\n" << splits << endl << "#RenderCostDecrease\n" << renderCostDecr << endl << "#TotalEntriesInPvs\n" << pvsEntries << endl << "#TotalRenderCost\n" << totalRenderCost << endl << "#Memory\n" << memory << endl << "#RcPerMb\n" << renderCostPerStorage << endl; } bool HierarchyManager::GlobalTerminationCriteriaMet(SubdivisionCandidate *candidate) const { const bool terminationCriteriaMet = (0 || (mHierarchyStats.Leaves() >= mTermMaxLeaves) //|| (mHierarchyStats.mGlobalCostMisses >= mTermGlobalCostMissTolerance) || (candidate->GlobalTerminationCriteriaMet()) //|| (mHierarchyStats.mRenderCostDecrease < mMinRenderCostDecrease) ); #if _DEBUG if (terminationCriteriaMet) { Debug << "hierarchy global termination criteria met:" << endl; Debug << "leaves: " << mHierarchyStats.Leaves() << " " << mTermMaxLeaves << endl; Debug << "cost misses: " << mHierarchyStats.mGlobalCostMisses << " " << mTermGlobalCostMissTolerance << endl; } #endif return terminationCriteriaMet; } void HierarchyManager::Construct(const VssRayContainer &sampleRays, const ObjectContainer &objects, AxisAlignedBox3 *forcedViewSpace) { switch (mConstructionType) { case MULTILEVEL: ConstructMultiLevel(sampleRays, objects, forcedViewSpace); break; case INTERLEAVED: case SEQUENTIAL: ConstructInterleaved(sampleRays, objects, forcedViewSpace); break; case GRADIENT: ConstructInterleavedWithGradient(sampleRays, objects, forcedViewSpace); break; default: break; } } void HierarchyManager::ConstructInterleavedWithGradient(const VssRayContainer &sampleRays, const ObjectContainer &objects, AxisAlignedBox3 *forcedViewSpace) { mHierarchyStats.Reset(); mHierarchyStats.Start(); mHierarchyStats.mNodes = 2; // create first nodes mVspTree->Initialise(sampleRays, forcedViewSpace); InitialiseObjectSpaceSubdivision(objects); // hack: assume that object space can be seen from view space mHierarchyStats.mTotalCost = (float)objects.size(); mHierarchyStats.mPvsEntries = 1; mHierarchyStats.mMemory = sizeof(PvsData) + sizeof(Intersectable *) / (1024.0f * 1024.0f); EvalSubdivisionStats(); Debug << "setting total cost to " << mHierarchyStats.mTotalCost << endl; const long startTime = GetTime(); cout << "Constructing view space / object space tree ... \n"; SplitQueue objectSpaceQueue; SplitQueue viewSpaceQueue; // use sah for evaluating osp tree construction // in the first iteration of the subdivision mSavedViewSpaceSubdivisionType = mViewSpaceSubdivisionType; mViewSpaceSubdivisionType = NO_VIEWSPACE_SUBDIV; //mSavedObjectSpaceSubdivisionType = mObjectSpaceSubdivisionType; // number of initial splits const int minSteps = mMinStepsOfSameType; float renderCostDecr = Limits::Infinity; SubdivisionCandidate *osc = PrepareObjectSpaceSubdivision(sampleRays, objects); objectSpaceQueue.Push(osc); ///////////////////////// // calulcate initial object space splits SubdivisionCandidateContainer dirtyVspList; // subdivide object space first // for first round, use sah splits. Once view space partition // has started, use render cost heuristics instead const int ospSteps = RunConstruction(objectSpaceQueue, dirtyVspList, renderCostDecr, minSteps); cout << "\n" << ospSteps << " object space partition steps taken" << endl; // create view space SubdivisionCandidate *vsc = PrepareViewSpaceSubdivision(sampleRays, objects); viewSpaceQueue.Push(vsc); // view space subdivision was constructed mViewSpaceSubdivisionType = mSavedViewSpaceSubdivisionType; // the priorities were calculated for driving sha. // => recalculate "real" priorities taking visibility into // account so we can compare to view space splits ResetQueue(objectSpaceQueue, false); // lower number of minsteps: the decicion of which domain to subdivide // should be decided using the render cost decrease from now //minSteps = 1; // This method subdivides view space / object space // in order to converge to some optimal cost for this partition // start with object space partiton // then optimizate view space partition for the current osp // and vice versa until iteration depth is reached. while (!(viewSpaceQueue.Empty() && objectSpaceQueue.Empty())) { const float vspPriority = viewSpaceQueue.Empty() ? 0 : viewSpaceQueue.Top()->GetPriority(); const float ospPriority = objectSpaceQueue.Empty() ? 0 : objectSpaceQueue.Top()->GetPriority(); cout << "new decicion, vsp: " << vspPriority << ", osp: " << ospPriority << endl; // should view or object space be subdivided further? if (ospPriority >= vspPriority) { // use splits of one kind until rendercost decrease of other domain is reached renderCostDecr = vspPriority; cout << "comparing with this render cost: " << renderCostDecr << endl; // dirtied view space candidates SubdivisionCandidateContainer dirtyVspList; // subdivide object space first // for first round, use sah splits. Once view space partition // has started, use render cost heuristics instead const int ospSteps = RunConstruction(objectSpaceQueue, dirtyVspList, renderCostDecr, minSteps); cout << "\n" << ospSteps << " object space partition steps taken" << endl; /// Repair split queue, i.e., affected view space candidates cout << "repairing queue ... " << endl; RepairQueue(dirtyVspList, viewSpaceQueue, true); cout << "\nrepaired " << (int)dirtyVspList.size() << " candidates" << endl; } else { // use splits of one kind until rendercost slope is reached renderCostDecr = ospPriority; cout << "comparing with this render cost: " << renderCostDecr << endl; ///////////////// // subdivide view space with respect to the objects SubdivisionCandidateContainer dirtyOspList; // process view space candidates const int vspSteps = RunConstruction(viewSpaceQueue, dirtyOspList, renderCostDecr, minSteps); cout << "\n" << vspSteps << " view space partition steps taken" << endl; // view space subdivision constructed mViewSpaceSubdivisionType = mSavedViewSpaceSubdivisionType; /// Repair split queue cout << "repairing queue ... " << endl; RepairQueue(dirtyOspList, objectSpaceQueue, true); cout << "repaired " << (int)dirtyOspList.size() << " candidates" << endl; } } cout << "\nfinished in " << TimeDiff(startTime, GetTime()) * 1e-3 << " secs" << endl; mHierarchyStats.Stop(); mVspTree->mVspStats.Stop(); FinishObjectSpaceSubdivision(objects); } void HierarchyManager::ConstructInterleaved(const VssRayContainer &sampleRays, const ObjectContainer &objects, AxisAlignedBox3 *forcedViewSpace) { mHierarchyStats.Reset(); mHierarchyStats.Start(); mHierarchyStats.mNodes = 2; // two nodes for view space and object space mHierarchyStats.mPvsEntries = 1; mHierarchyStats.mMemory = sizeof(PvsData) + sizeof(Intersectable *) / (1024.0f * 1024.0f); mHierarchyStats.mTotalCost = (float)objects.size(); mHierarchyStats.mRenderCostDecrease = 0; EvalSubdivisionStats(); Debug << "setting total cost to " << mHierarchyStats.mTotalCost << endl; const long startTime = GetTime(); cout << "Constructing view space / object space tree ... \n"; // create only roots mVspTree->Initialise(sampleRays, forcedViewSpace); InitialiseObjectSpaceSubdivision(objects); // use objects for evaluating vsp tree construction in the // first levels of the subdivision mSavedObjectSpaceSubdivisionType = mObjectSpaceSubdivisionType; mObjectSpaceSubdivisionType = NO_OBJ_SUBDIV; mSavedViewSpaceSubdivisionType = mViewSpaceSubdivisionType; mViewSpaceSubdivisionType = NO_VIEWSPACE_SUBDIV; // start view space subdivison immediately? if (StartViewSpaceSubdivision()) { // prepare vsp tree for traversal mViewSpaceSubdivisionType = mSavedViewSpaceSubdivisionType; SubdivisionCandidate *vspSc = PrepareViewSpaceSubdivision(sampleRays, objects); mTQueue.Push(vspSc); } // start object space subdivision immediately? if (StartObjectSpaceSubdivision()) { mObjectSpaceSubdivisionType = mSavedObjectSpaceSubdivisionType; SubdivisionCandidate *ospSc = PrepareObjectSpaceSubdivision(sampleRays, objects); mTQueue.Push(ospSc); } // begin subdivision RunConstruction(mRepairQueue, sampleRays, objects, forcedViewSpace); cout << "\nfinished in " << TimeDiff(startTime, GetTime()) * 1e-3 << " secs" << endl; mObjectSpaceSubdivisionType = mSavedObjectSpaceSubdivisionType; mViewSpaceSubdivisionType = mSavedViewSpaceSubdivisionType; mHierarchyStats.Stop(); mVspTree->mVspStats.Stop(); FinishObjectSpaceSubdivision(objects); } SubdivisionCandidate *HierarchyManager::PrepareViewSpaceSubdivision(const VssRayContainer &sampleRays, const ObjectContainer &objects) { cout << "\npreparing view space hierarchy construction ... " << endl; // hack: reset global cost misses mHierarchyStats.mGlobalCostMisses = 0; RayInfoContainer *viewSpaceRays = new RayInfoContainer(); SubdivisionCandidate *vsc = mVspTree->PrepareConstruction(sampleRays, *viewSpaceRays); mHierarchyStats.mTotalCost = mVspTree->mTotalCost; cout << "\nreseting cost for vsp, new total cost: " << mHierarchyStats.mTotalCost << endl; return vsc; } float HierarchyManager::GetObjectSpaceMemUsage() const { if (mObjectSpaceSubdivisionType == KD_BASED_OBJ_SUBDIV) { // TODO; } else if (mObjectSpaceSubdivisionType == BV_BASED_OBJ_SUBDIV) { return mBvHierarchy->GetMemUsage(); } return -1; } void HierarchyManager::InitialiseObjectSpaceSubdivision(const ObjectContainer &objects) { if (mObjectSpaceSubdivisionType == KD_BASED_OBJ_SUBDIV) { // TODO; } else if (mObjectSpaceSubdivisionType == BV_BASED_OBJ_SUBDIV) { mBvHierarchy->Initialise(objects); } } SubdivisionCandidate *HierarchyManager::PrepareObjectSpaceSubdivision(const VssRayContainer &sampleRays, const ObjectContainer &objects) { // hack: reset global cost misses mHierarchyStats.mGlobalCostMisses = 0; if (mObjectSpaceSubdivisionType == KD_BASED_OBJ_SUBDIV) { return PrepareOspTree(sampleRays, objects); } else if (mObjectSpaceSubdivisionType == BV_BASED_OBJ_SUBDIV) { return PrepareBvHierarchy(sampleRays, objects); } return NULL; } SubdivisionCandidate *HierarchyManager::PrepareBvHierarchy(const VssRayContainer &sampleRays, const ObjectContainer &objects) { const long startTime = GetTime(); cout << "preparing bv hierarchy construction ... " << endl; // compute first candidate SubdivisionCandidate *sc = mBvHierarchy->PrepareConstruction(sampleRays, objects); mHierarchyStats.mTotalCost = mBvHierarchy->mTotalCost; Debug << "\nreseting cost, new total cost: " << mHierarchyStats.mTotalCost << endl; cout << "finished bv hierarchy preparation in " << TimeDiff(startTime, GetTime()) * 1e-3 << " secs" << endl; return sc; } SubdivisionCandidate *HierarchyManager::PrepareOspTree(const VssRayContainer &sampleRays, const ObjectContainer &objects) { cout << "starting osp tree construction ... " << endl; RayInfoContainer *objectSpaceRays = new RayInfoContainer(); // start with one big kd cell - all objects can be seen from everywhere // note: only true for view space = object space // compute first candidate SubdivisionCandidate *osc = mOspTree->PrepareConstruction(sampleRays, objects, *objectSpaceRays); mHierarchyStats.mTotalCost = mOspTree->mTotalCost; Debug << "\nreseting cost for osp, new total cost: " << mHierarchyStats.mTotalCost << endl; return osc; } bool HierarchyManager::ApplySubdivisionCandidate(SubdivisionCandidate *sc, SplitQueue &splitQueue, const bool repairQueue) { const bool terminationCriteriaMet = GlobalTerminationCriteriaMet(sc); const bool success = sc->Apply(splitQueue, terminationCriteriaMet); if (!success) // split was not taken { return false; } /////////////// //-- split was successful => update stats and queue // cost ratio of cost decrease / totalCost const float costRatio = sc->GetRenderCostDecrease() / mHierarchyStats.mTotalCost; //Debug << "ratio: " << costRatio << " min ratio: " << mTermMinGlobalCostRatio << endl; if (costRatio < mTermMinGlobalCostRatio) { ++ mHierarchyStats.mGlobalCostMisses; } cout << sc->Type() << " "; ///////////// // update stats mHierarchyStats.mNodes += 2; mHierarchyStats.mTotalCost -= sc->GetRenderCostDecrease(); const int pvsEntriesIncr = sc->GetPvsEntriesIncr(); mHierarchyStats.mPvsEntries += pvsEntriesIncr; //cout << "pvs entries: " << pvsEntriesIncr << " " << mHierarchyStats.pvsEntries << endl; const int sizeOfEntry = sizeof(PvsData) + sizeof(Intersectable *); // memory size in byte mHierarchyStats.mMemory += float(pvsEntriesIncr * sizeOfEntry); mHierarchyStats.mRenderCostDecrease = sc->GetRenderCostDecrease(); // output stats EvalSubdivisionStats(); if (repairQueue) { // reevaluate candidates affected by the split for view space splits, // this would be object space splits and other way round vector dirtyList; sc->CollectDirtyCandidates(dirtyList, false); RepairQueue(dirtyList, splitQueue, mRecomputeSplitPlaneOnRepair); } return true; } int HierarchyManager::GetObjectSpaceSubdivisionDepth() const { int maxDepth = 0; if (mObjectSpaceSubdivisionType == KD_BASED_OBJ_SUBDIV) { maxDepth = mOspTree->mOspStats.maxDepth; } else if (mObjectSpaceSubdivisionType == BV_BASED_OBJ_SUBDIV) { maxDepth = mBvHierarchy->mBvhStats.maxDepth; } return maxDepth; } int HierarchyManager::GetObjectSpaceSubdivisionLeaves() const { int maxLeaves= 0; if (mObjectSpaceSubdivisionType == KD_BASED_OBJ_SUBDIV) { maxLeaves = mOspTree->mOspStats.Leaves(); } else if (mObjectSpaceSubdivisionType == BV_BASED_OBJ_SUBDIV) { maxLeaves = mBvHierarchy->mBvhStats.Leaves(); } return maxLeaves; } bool HierarchyManager::StartObjectSpaceSubdivision() const { // view space construction already started if (ObjectSpaceSubdivisionConstructed()) return false; // start immediately with object space subdivision? if (mStartWithObjectSpace) return true; // is the queue empty again? if (ViewSpaceSubdivisionConstructed() && mTQueue.Empty()) return true; // has the depth for subdivision been reached? return ((mConstructionType == INTERLEAVED) && (mMinStepsOfSameType <= mVspTree->mVspStats.nodes)); } bool HierarchyManager::StartViewSpaceSubdivision() const { // view space construction already started if (ViewSpaceSubdivisionConstructed()) return false; // start immediately with view space subdivision? if (!mStartWithObjectSpace) return true; // is the queue empty again? if (ObjectSpaceSubdivisionConstructed() && mTQueue.Empty()) return true; // has the depth for subdivision been reached? return ((mConstructionType == INTERLEAVED) && (mMinStepsOfSameType <= GetObjectSpaceSubdivisionLeaves())); } void HierarchyManager::RunConstruction(const bool repairQueue, const VssRayContainer &sampleRays, const ObjectContainer &objects, AxisAlignedBox3 *forcedViewSpace) { while (!FinishedConstruction()) { SubdivisionCandidate *sc = NextSubdivisionCandidate(mTQueue); /////////////////// //-- subdivide leaf node ApplySubdivisionCandidate(sc, mTQueue, repairQueue); // we use objects for evaluating vsp tree construction until // a certain depth once a certain depth existiert ... if (StartObjectSpaceSubdivision()) { mObjectSpaceSubdivisionType = mSavedObjectSpaceSubdivisionType; cout << "\nstarting object space subdivision after " << mVspTree->mVspStats.nodes << " (" << mMinStepsOfSameType << ") " << endl; SubdivisionCandidate *ospSc = PrepareObjectSpaceSubdivision(sampleRays, objects); cout << "reseting queue ... "; ResetQueue(mTQueue, mRecomputeSplitPlaneOnRepair); cout << "finished" << endl; mTQueue.Push(ospSc); } if (StartViewSpaceSubdivision()) { mViewSpaceSubdivisionType = mSavedViewSpaceSubdivisionType; cout << "\nstarting view space subdivision at depth " << GetObjectSpaceSubdivisionLeaves() << " (" << mMinStepsOfSameType << ") " << endl; SubdivisionCandidate *vspSc = PrepareViewSpaceSubdivision(sampleRays, objects); cout << "reseting queue ... "; ResetQueue(mTQueue, mRecomputeSplitPlaneOnRepair); cout << "finished" << endl; // push view space candidate mTQueue.Push(vspSc); } DEL_PTR(sc); } } void HierarchyManager::RunConstruction(const bool repairQueue) { //int i = 0; // main loop while (!FinishedConstruction()) { SubdivisionCandidate *sc = NextSubdivisionCandidate(mTQueue); //////// //-- subdivide leaf node of either type ApplySubdivisionCandidate(sc, mTQueue, repairQueue); DEL_PTR(sc); } } int HierarchyManager::RunConstruction(SplitQueue &splitQueue, SubdivisionCandidateContainer &dirtyCandidates, const float minRenderCostDecr, const int minSteps) { int steps = 0; SubdivisionCandidate::NewMail(); // main loop while (!splitQueue.Empty()) { SubdivisionCandidate *sc = NextSubdivisionCandidate(splitQueue); // minimum slope reached if ((sc->GetRenderCostDecrease() < minRenderCostDecr) && !(steps < minSteps)) { cout << "breaking on " << sc->GetRenderCostDecrease() << " smaller than " << minRenderCostDecr << endl; break; } //////// //-- subdivide leaf node of either type const bool repairQueue = false; const bool success = ApplySubdivisionCandidate(sc, splitQueue, repairQueue); if (success) { sc->CollectDirtyCandidates(dirtyCandidates, true); //cout << "collected " << dirtyCandidates.size() << "dirty candidates" << endl; ++ steps; } } return steps; } SubdivisionCandidate *HierarchyManager::ResetObjectSpaceSubdivision(const VssRayContainer &sampleRays, const ObjectContainer &objects) { // no object space subdivision yet if (!ObjectSpaceSubdivisionConstructed()) { return PrepareObjectSpaceSubdivision(sampleRays, objects); } SubdivisionCandidate *firstCandidate; // object space partition constructed => reconstruct switch (mObjectSpaceSubdivisionType) { case BV_BASED_OBJ_SUBDIV: { cout << "\nreseting bv hierarchy" << endl; Debug << "old bv hierarchy:\n " << mBvHierarchy->mBvhStats << endl; firstCandidate = mBvHierarchy->Reset(sampleRays, objects); mHierarchyStats.mTotalCost = mBvHierarchy->mTotalCost; // bug!! pvs entries could be much higher at this stage mHierarchyStats.mNodes = 2; mHierarchyStats.mPvsEntries = 1; mHierarchyStats.mRenderCostDecrease = 0; // evaluate stats before first subdivision EvalSubdivisionStats(); } break; case KD_BASED_OBJ_SUBDIV: // TODO default: firstCandidate = NULL; break; } return firstCandidate; } SubdivisionCandidate *HierarchyManager::ResetViewSpaceSubdivision(const VssRayContainer &sampleRays, const ObjectContainer &objects) { ViewCellsManager *vc = mVspTree->mViewCellsManager; // HACK: rather not destroy vsp tree DEL_PTR(mVspTree); mVspTree = new VspTree(); mVspTree->mHierarchyManager = this; mVspTree->mViewCellsManager = vc; SubdivisionCandidate *vsc = PrepareViewSpaceSubdivision(sampleRays, objects); mHierarchyStats.mNodes = 2; // bug!! pvs entries could be much higher at this stage mHierarchyStats.mPvsEntries = 1; mHierarchyStats.mRenderCostDecrease = 0; // evaluate new stats before first subdivsiion EvalSubdivisionStats(); return vsc; } void HierarchyManager::ConstructMultiLevel(const VssRayContainer &sampleRays, const ObjectContainer &objects, AxisAlignedBox3 *forcedViewSpace) { mHierarchyStats.Reset(); mHierarchyStats.Start(); mHierarchyStats.mNodes = 2; mHierarchyStats.mTotalCost = (float)objects.size(); Debug << "setting total cost to " << mHierarchyStats.mTotalCost << endl; const long startTime = GetTime(); cout << "Constructing view space / object space tree ... \n"; // initialise view / object space mVspTree->Initialise(sampleRays, forcedViewSpace); InitialiseObjectSpaceSubdivision(objects); // use sah for evaluating osp tree construction // in the first iteration of the subdivision mSavedViewSpaceSubdivisionType = mViewSpaceSubdivisionType; mViewSpaceSubdivisionType = NO_VIEWSPACE_SUBDIV; mSavedObjectSpaceSubdivisionType = mObjectSpaceSubdivisionType; mObjectSpaceSubdivisionType = NO_OBJ_SUBDIV; const int limit = mNumMultiLevels; int i = 0; // This method subdivides view space / object space // in order to converge to some optimal cost for this partition // start with object space partiton // then optimizate view space partition for the current osp // and vice versa until iteration depth is reached. while (1) { char subdivisionStatsLog[100]; sprintf(subdivisionStatsLog, "tests/i3d/subdivision-%04d.log", i); mSubdivisionStats.open(subdivisionStatsLog); // subdivide object space first ResetObjectSpaceSubdivision(sampleRays, objects); // process object space candidates RunConstruction(false); // object space subdivision constructed mObjectSpaceSubdivisionType = mSavedObjectSpaceSubdivisionType; cout << "iteration " << i << " of " << limit << " finished" << endl; mSubdivisionStats.close(); if ((i ++) >= limit) break; sprintf(subdivisionStatsLog, "tests/i3d/subdivision-%04d.log", i); mSubdivisionStats.open(subdivisionStatsLog); ///////////////// // subdivide view space with respect to the objects SubdivisionCandidate *vspVc = ResetViewSpaceSubdivision(sampleRays, objects); mTQueue.Push(vspVc); // view space subdivision constructed mViewSpaceSubdivisionType = mSavedViewSpaceSubdivisionType; // process view space candidates RunConstruction(false); cout << "iteration " << i << " of " << limit << " finished" << endl; mSubdivisionStats.close(); if ((i ++) >= limit) break; } cout << "\nfinished in " << TimeDiff(startTime, GetTime()) * 1e-3 << " secs" << endl; /*#if _DEBUG cout << "view space: " << GetViewSpaceBox() << endl; cout << "object space: " << GetObjectSpaceBox() << endl; #endif*/ mHierarchyStats.Stop(); mVspTree->mVspStats.Stop(); FinishObjectSpaceSubdivision(objects); } void HierarchyManager::OptimizeMultiLevel(const VssRayContainer &sampleRays, const ObjectContainer &objects, AxisAlignedBox3 *forcedViewSpace) { mHierarchyStats.Reset(); mHierarchyStats.Start(); mHierarchyStats.mNodes = 2; mHierarchyStats.mTotalCost = (float)objects.size(); Debug << "setting total cost to " << mHierarchyStats.mTotalCost << endl; const long startTime = GetTime(); cout << "Constructing view space / object space tree ... \n"; // initialise view / object space mVspTree->Initialise(sampleRays, forcedViewSpace); InitialiseObjectSpaceSubdivision(objects); // use sah for evaluating osp tree construction // in the first iteration of the subdivision mSavedViewSpaceSubdivisionType = mViewSpaceSubdivisionType; mViewSpaceSubdivisionType = NO_VIEWSPACE_SUBDIV; mSavedObjectSpaceSubdivisionType = mObjectSpaceSubdivisionType; mObjectSpaceSubdivisionType = NO_OBJ_SUBDIV; const int limit = mNumMultiLevels; int i = 0; // This method subdivides view space / object space // in order to converge to some optimal cost for this partition // start with object space partiton // then optimizate view space partition for the current osp // and vice versa until iteration depth is reached. while (1) { char subdivisionStatsLog[100]; sprintf(subdivisionStatsLog, "tests/i3d/subdivision-%04d.log", i); mSubdivisionStats.open(subdivisionStatsLog); // subdivide object space first ResetObjectSpaceSubdivision(sampleRays, objects); // process object space candidates RunConstruction(false); // object space subdivision constructed mObjectSpaceSubdivisionType = mSavedObjectSpaceSubdivisionType; cout << "iteration " << i << " of " << limit << " finished" << endl; mSubdivisionStats.close(); if ((i ++) >= limit) break; sprintf(subdivisionStatsLog, "tests/i3d/subdivision-%04d.log", i); mSubdivisionStats.open(subdivisionStatsLog); ///////////////// // subdivide view space with respect to the objects SubdivisionCandidate *vspVc = ResetViewSpaceSubdivision(sampleRays, objects); mTQueue.Push(vspVc); // view space subdivision constructed mViewSpaceSubdivisionType = mSavedViewSpaceSubdivisionType; // process view space candidates RunConstruction(false); cout << "iteration " << i << " of " << limit << " finished" << endl; mSubdivisionStats.close(); if ((i ++) >= limit) break; } cout << "\nfinished in " << TimeDiff(startTime, GetTime()) * 1e-3 << " secs" << endl; /*#if _DEBUG cout << "view space: " << GetViewSpaceBox() << endl; cout << "object space: " << GetObjectSpaceBox() << endl; #endif*/ mHierarchyStats.Stop(); mVspTree->mVspStats.Stop(); FinishObjectSpaceSubdivision(objects); } bool HierarchyManager::FinishedConstruction() const { return mTQueue.Empty(); } bool HierarchyManager::ObjectSpaceSubdivisionConstructed() const { switch (mObjectSpaceSubdivisionType) { case KD_BASED_OBJ_SUBDIV: return mOspTree && mOspTree->GetRoot(); case BV_BASED_OBJ_SUBDIV: return mBvHierarchy && mBvHierarchy->GetRoot(); default: return false; } } bool HierarchyManager::ViewSpaceSubdivisionConstructed() const { return mViewSpaceSubdivisionType != NO_VIEWSPACE_SUBDIV; //return mVspTree && mVspTree->GetRoot(); } void HierarchyManager::CollectDirtyCandidates(const SubdivisionCandidateContainer &chosenCandidates, SubdivisionCandidateContainer &dirtyList) { SubdivisionCandidateContainer::const_iterator sit, sit_end = chosenCandidates.end(); SubdivisionCandidate::NewMail(); for (sit = chosenCandidates.begin(); sit != sit_end; ++ sit) { (*sit)->CollectDirtyCandidates(dirtyList, true); } } void HierarchyManager::RepairQueue(const SubdivisionCandidateContainer &dirtyList, SplitQueue &splitQueue, const bool recomputeSplitPlaneOnRepair) { // for each update of the view space partition: // the candidates from object space partition which // have been afected by the view space split (the kd split candidates // which saw the view cell which was split) must be reevaluated // (maybe not locally, just reinsert them into the queue) // // vice versa for the view cells // for each update of the object space partition // reevaluate split candidate for view cells which saw the split kd cell // // the priority queue update can be solved by implementing a binary heap // (explicit data structure, binary tree) // *) inserting and removal is efficient // *) search is not efficient => store queue position with each // split candidate // collect list of "dirty" candidates const long startTime = GetTime(); if (0) cout << "repairing " << (int)dirtyList.size() << " candidates ... "; ///////////////////////////////// //-- reevaluate the dirty list SubdivisionCandidateContainer::const_iterator sit, sit_end = dirtyList.end(); for (sit = dirtyList.begin(); sit != sit_end; ++ sit) { SubdivisionCandidate* sc = *sit; const float rcd = sc->GetRenderCostDecrease(); splitQueue.Erase(sc); // erase from queue sc->EvalPriority(recomputeSplitPlaneOnRepair); // reevaluate #ifdef _DEBUG Debug << "candidate " << sc << " reevaluated\n" << "render cost decrease diff " << rcd - sc->GetRenderCostDecrease() << " old: " << rcd << " new " << sc->GetRenderCostDecrease() << endl; #endif splitQueue.Push(sc); // reinsert cout << "."; } const long endTime = GetTime(); const Real timeDiff = TimeDiff(startTime, endTime); mHierarchyStats.mRepairTime += timeDiff; if (0) cout << "repaired in " << timeDiff * 1e-3f << " secs" << endl; } void HierarchyManager::ResetQueue(SplitQueue &splitQueue, const bool recomputeSplitPlane) { SubdivisionCandidateContainer mCandidateBuffer; // remove from queue while (!splitQueue.Empty()) { SubdivisionCandidate *candidate = NextSubdivisionCandidate(splitQueue); // reevaluate local split plane and priority candidate->EvalPriority(recomputeSplitPlane); cout << "."; mCandidateBuffer.push_back(candidate); } // put back into queue SubdivisionCandidateContainer::const_iterator sit, sit_end = mCandidateBuffer.end(); for (sit = mCandidateBuffer.begin(); sit != sit_end; ++ sit) { splitQueue.Push(*sit); } } void HierarchyManager::ExportObjectSpaceHierarchy(OUT_STREAM &stream) { // the type of the view cells hierarchy switch (mObjectSpaceSubdivisionType) { case KD_BASED_OBJ_SUBDIV: stream << "" << endl; mOspTree->Export(stream); stream << endl << "" << endl; break; case BV_BASED_OBJ_SUBDIV: stream << "" << endl; mBvHierarchy->Export(stream); stream << endl << "" << endl; break; } } bool HierarchyManager::AddSampleToPvs(Intersectable *obj, const Vector3 &hitPoint, ViewCell *vc, const float pdf, float &contribution) const { if (!obj) return false; switch (mObjectSpaceSubdivisionType) { case NO_OBJ_SUBDIV: { // potentially visible objects return vc->AddPvsSample(obj, pdf, contribution); } case KD_BASED_OBJ_SUBDIV: { // potentially visible kd cells KdLeaf *leaf = mOspTree->GetLeaf(hitPoint/*ray->mOriginNode*/); return mOspTree->AddLeafToPvs(leaf, vc, pdf, contribution); } case BV_BASED_OBJ_SUBDIV: { BvhLeaf *leaf = mBvHierarchy->GetLeaf(obj); BvhIntersectable *bvhObj = mBvHierarchy->GetOrCreateBvhIntersectable(leaf); return vc->AddPvsSample(bvhObj, pdf, contribution); } default: return false; } } void HierarchyManager::PrintHierarchyStatistics(ostream &stream) const { stream << mHierarchyStats << endl; stream << "\nview space:" << endl << endl; stream << mVspTree->GetStatistics() << endl; stream << "\nobject space:" << endl << endl; switch (mObjectSpaceSubdivisionType) { case KD_BASED_OBJ_SUBDIV: { stream << mOspTree->GetStatistics() << endl; break; } case BV_BASED_OBJ_SUBDIV: { stream << mBvHierarchy->GetStatistics() << endl; break; } default: break; } } void HierarchyManager::ExportObjectSpaceHierarchy(Exporter *exporter, const ObjectContainer &objects, const AxisAlignedBox3 *bbox, const bool exportBounds) const { switch (mObjectSpaceSubdivisionType) { case KD_BASED_OBJ_SUBDIV: { ExportOspTree(exporter, objects); break; } case BV_BASED_OBJ_SUBDIV: { exporter->ExportBvHierarchy(*mBvHierarchy, 0, bbox, exportBounds); break; } default: break; } } void HierarchyManager::ExportOspTree(Exporter *exporter, const ObjectContainer &objects) const { if (0) exporter->ExportGeometry(objects); exporter->SetWireframe(); exporter->ExportOspTree(*mOspTree, 0); } Intersectable *HierarchyManager::GetIntersectable(const VssRay &ray, const bool isTermination) const { Intersectable *obj; Vector3 pt; KdNode *node; ray.GetSampleData(isTermination, pt, &obj, &node); if (!obj) return NULL; switch (mObjectSpaceSubdivisionType) { case HierarchyManager::KD_BASED_OBJ_SUBDIV: { KdLeaf *leaf = mOspTree->GetLeaf(pt, node); return mOspTree->GetOrCreateKdIntersectable(leaf); } case HierarchyManager::BV_BASED_OBJ_SUBDIV: { BvhLeaf *leaf = mBvHierarchy->GetLeaf(obj); return mBvHierarchy->GetOrCreateBvhIntersectable(leaf); } default: return obj; } } void HierarchyStatistics::Print(ostream &app) const { app << "=========== Hierarchy statistics ===============\n"; app << setprecision(4); app << "#N_CTIME ( Construction time [s] )\n" << Time() << " \n"; app << "#N_RTIME ( Repair time [s] )\n" << mRepairTime * 1e-3f << " \n"; app << "#N_NODES ( Number of nodes )\n" << mNodes << "\n"; app << "#N_INTERIORS ( Number of interior nodes )\n" << Interior() << "\n"; app << "#N_LEAVES ( Number of leaves )\n" << Leaves() << "\n"; app << "#N_PMAXDEPTH ( Maximal reached depth )\n" << mMaxDepth << endl; app << "#N_GLOBALCOSTMISSES ( Global cost misses )\n" << mGlobalCostMisses << endl; app << "========== END OF Hierarchy statistics ==========\n"; } static void RemoveRayRefs(const ObjectContainer &objects) { ObjectContainer::const_iterator oit, oit_end = objects.end(); for (oit = objects.begin(); oit != oit_end; ++ oit) { (*oit)->mVssRays.clear(); } } void HierarchyManager::FinishObjectSpaceSubdivision(const ObjectContainer &objects) const { switch (mObjectSpaceSubdivisionType) { case KD_BASED_OBJ_SUBDIV: { mOspTree->mOspStats.Stop(); break; } case BV_BASED_OBJ_SUBDIV: { mBvHierarchy->mBvhStats.Stop(); RemoveRayRefs(objects); break; } default: break; } } void HierarchyManager::ExportBoundingBoxes(OUT_STREAM &stream, const ObjectContainer &objects) { stream << "" << endl; if (mObjectSpaceSubdivisionType == KD_BASED_OBJ_SUBDIV) { KdIntersectableMap::const_iterator kit, kit_end = mOspTree->mKdIntersectables.end(); int id = 0; for (kit = mOspTree->mKdIntersectables.begin(); kit != kit_end; ++ kit, ++ id) { Intersectable *obj = (*kit).second; const AxisAlignedBox3 box = obj->GetBox(); obj->SetId(id); stream << "" << endl; } } else { ObjectContainer::const_iterator oit, oit_end = objects.end(); for (oit = objects.begin(); oit != oit_end; ++ oit) { const AxisAlignedBox3 box = (*oit)->GetBox(); stream << "GetId() << "\"" << " min=\"" << box.Min().x << " " << box.Min().y << " " << box.Min().z << "\"" << " max=\"" << box.Max().x << " " << box.Max().y << " " << box.Max().z << "\" />" << endl; } } stream << "" << endl; } }