#include "Environment.h" #include "GvsPreprocessor.h" #include "GlRenderer.h" #include "VssRay.h" #include "ViewCellsManager.h" #include "Triangle3.h" #include "IntersectableWrapper.h" #include "Plane3.h" #include "RayCaster.h" #include "Exporter.h" #include "SamplingStrategy.h" #include "BvHierarchy.h" #include "Polygon3.h" #include "IntersectableWrapper.h" namespace GtpVisibilityPreprocessor { #define GVS_DEBUG 0 #define NOT_ACCOUNTED_OBJECT 0 #define ACCOUNTED_OBJECT 2 #define SHOW_QT_VISUALIZATION 0 static const float MIN_DIST = 0.001f; static ObjectContainer myobjects; /** Visualization structure for the GVS algorithm. */ struct VizStruct { Polygon3 *enlargedTriangle; Triangle3 originalTriangle; VssRay *ray; }; static vector vizContainer; GvsPreprocessor::GvsPreprocessor(): Preprocessor(), mSamplingType(SamplingStrategy::VIEWCELL_BASED_DISTRIBUTION), mProcessedViewCells(0), mCurrentViewCell(NULL), mCurrentViewPoint(Vector3(0.0f, 0.0f, 0.0f)) { Environment::GetSingleton()->GetIntValue("GvsPreprocessor.totalSamples", mTotalSamples); Environment::GetSingleton()->GetIntValue("GvsPreprocessor.initialSamples", mInitialSamples); Environment::GetSingleton()->GetIntValue("GvsPreprocessor.gvsSamplesPerPass", mGvsSamplesPerPass); Environment::GetSingleton()->GetIntValue("GvsPreprocessor.minContribution", mMinContribution); Environment::GetSingleton()->GetFloatValue("GvsPreprocessor.epsilon", mEps); Environment::GetSingleton()->GetFloatValue("GvsPreprocessor.threshold", mThreshold); Environment::GetSingleton()->GetBoolValue("GvsPreprocessor.perViewCell", mPerViewCell); Environment::GetSingleton()->GetIntValue("GvsPreprocessor.maxViewCells", mMaxViewCells); Environment::GetSingleton()->GetBoolValue("Preprocessor.evaluatePixelError", mEvaluatePixelError); Environment::GetSingleton()->GetBoolValue("ViewCells.useKdPvs", mUseKdPvs); char gvsStatsLog[100]; Environment::GetSingleton()->GetStringValue("GvsPreprocessor.stats", gvsStatsLog); mGvsStatsStream.open(gvsStatsLog); Debug << "Gvs preprocessor options" << endl; Debug << "number of total samples: " << mTotalSamples << endl; Debug << "number of initial samples: " << mInitialSamples << endl; cout << "number of gvs samples per pass: " << mGvsSamplesPerPass << endl; cout << "number of samples per pass: " << mSamplesPerPass << endl; Debug << "threshold: " << mThreshold << endl; Debug << "epsilon: " << mEps << endl; Debug << "stats: " << gvsStatsLog << endl; Debug << "per view cell: " << mPerViewCell << endl; Debug << "max view cells: " << mMaxViewCells << endl; Debug << "min contribution: " << mMinContribution << endl; if (1) mOnlyRandomSampling = false; else mOnlyRandomSampling = true; mGvsStats.Reset(); // hack: some generic statistical values that can be read // by an application using the preprocessor mGenericStats = 0; mGenericStats2 = 0; } GvsPreprocessor::~GvsPreprocessor() { ClearRayQueue(); } void GvsPreprocessor::ClearRayQueue() { // clean ray queue while (!mRayQueue.empty()) { // handle next ray VssRay *ray = mRayQueue.top(); mRayQueue.pop(); //delete ray; } } ViewCell *GvsPreprocessor::NextViewCell() { if (mProcessedViewCells == (int)mViewCells.size()) return NULL; // no more view cells ViewCell *vc = mViewCells[mProcessedViewCells]; if (!vc->GetMesh()) mViewCellsManager->CreateMesh(vc); mGvsStats.mViewCellId = vc->GetId(); Debug << "vc: " << vc->GetId() << endl; ++ mProcessedViewCells; return vc; } int GvsPreprocessor::CheckDiscontinuity(const VssRay ¤tRay, const Triangle3 &hitTriangle, const VssRay &oldRay) { // the predicted hitpoint: we expect to hit the same mesh again const Vector3 predictedHit = CalcPredictedHitPoint(currentRay, hitTriangle, oldRay); const float predictedLen = Magnitude(predictedHit - currentRay.mOrigin); const float len = Magnitude(currentRay.mTermination - currentRay.mOrigin); // distance large => this is likely to be a discontinuity #if 1 if ((predictedLen - len) > mThreshold) #else // rather use relative distance if ((predictedLen / len) > mThreshold) #endif { //cout << "r"; // apply reverse sampling to find the gap VssRay *newRay = ReverseSampling(currentRay, hitTriangle, oldRay); if (!newRay) return 1; // set flag for visualization //newRay->mFlags |= VssRay::ReverseSample; // check if ray is not further processed (ray not deleted because using ray pool) HandleRay(newRay); return 1; } return 0; } void GvsPreprocessor::CountObject(Intersectable *triObj) { if ((triObj->mCounter != (NOT_ACCOUNTED_OBJECT + 1)) && (triObj->mCounter != (ACCOUNTED_OBJECT + 1))) { ++ triObj->mCounter; ++ mGenericStats2; } } void GvsPreprocessor::UpdateStatsForVisualization(KdIntersectable *kdInt) { mViewCellsManager->UpdateStatsForViewCell(mCurrentViewCell, kdInt); // count new objects in pvs due to kd node conversion myobjects.clear(); mKdTree->CollectObjects(kdInt->GetItem(), myobjects); ObjectContainer::const_iterator oit, oit_end = myobjects.end(); for (oit = myobjects.begin(); oit != oit_end; ++ oit) CountObject(*oit); } bool GvsPreprocessor::HasContribution(VssRay &ray) { if (!ray.mTerminationObject) return false; bool result; if (!mPerViewCell) { // store the rays + the intersected view cells const bool storeViewCells = false; //GVS_DEBUG; mViewCellsManager->ComputeSampleContribution(ray, true, storeViewCells, true); result = ray.mPvsContribution > 0; } else // original per view cell gvs { Intersectable *obj = ray.mTerminationObject; // counter < 2 => not accounted for yet if (obj->mCounter < ACCOUNTED_OBJECT) { obj->mCounter += ACCOUNTED_OBJECT; mTrianglePvs.push_back(obj); mGenericStats = mTrianglePvs.size(); // exchange the triangle with the node in the pvs for kd pvs if (mUseKdPvs) { KdNode *node = mKdTree->GetPvsNode(ray.mTermination); if (!node->Mailed()) { // add to pvs node->Mail(); KdIntersectable *kdInt = mKdTree->GetOrCreateKdIntersectable(node); mCurrentViewCell->GetPvs().AddSampleDirty(kdInt, 1.0f); if (SHOW_QT_VISUALIZATION) UpdateStatsForVisualization(kdInt); } } result = true; } else { result = false; } } return result; } bool GvsPreprocessor::HandleRay(VssRay *vssRay) { if (!HasContribution(*vssRay)) return false; // cout << "t"; if (0 && GVS_DEBUG) mVssRays.push_back(new VssRay(*vssRay)); // add new ray to ray queue mRayQueue.push(vssRay); ++ mGvsStats.mTotalContribution; return true; } /** Creates 3 new vertices for triangle vertex with specified index. */ void GvsPreprocessor::CreateDisplacedVertices(VertexContainer &vertices, const Triangle3 &hitTriangle, const VssRay &ray, const int index) const { const int indexU = (index + 1) % 3; const int indexL = (index == 0) ? 2 : index - 1; const Vector3 a = hitTriangle.mVertices[index] - ray.GetOrigin(); const Vector3 b = hitTriangle.mVertices[indexU] - hitTriangle.mVertices[index]; const Vector3 c = hitTriangle.mVertices[index] - hitTriangle.mVertices[indexL]; const float len = Magnitude(a); const Vector3 dir1 = Normalize(CrossProd(a, b)); //N((pi-xp)×(pi+1- pi)); const Vector3 dir2 = Normalize(CrossProd(a, c)); // N((pi-xp)×(pi- pi-1)) const Vector3 dir3 = DotProd(dir2, dir1) > 0 ? // N((pi-xp)×di,i-1+di,i+1×(pi-xp)) Normalize(dir2 + dir1) : Normalize(CrossProd(a, dir1) + CrossProd(dir2, a)); // compute the new three hit points // pi, i + 1: pi+ e·|pi-xp|·di, j const Vector3 pt1 = hitTriangle.mVertices[index] + mEps * len * dir1; // pi, i - 1: pi+ e·|pi-xp|·di, j const Vector3 pt2 = hitTriangle.mVertices[index] + mEps * len * dir2; // pi, i: pi+ e·|pi-xp|·di, j const Vector3 pt3 = hitTriangle.mVertices[index] + mEps * len * dir3; vertices.push_back(pt2); vertices.push_back(pt3); vertices.push_back(pt1); } void GvsPreprocessor::EnlargeTriangle(VertexContainer &vertices, const Triangle3 &hitTriangle, const VssRay &ray) const { CreateDisplacedVertices(vertices, hitTriangle, ray, 0); CreateDisplacedVertices(vertices, hitTriangle, ray, 1); CreateDisplacedVertices(vertices, hitTriangle, ray, 2); } Vector3 GvsPreprocessor::CalcPredictedHitPoint(const VssRay &newRay, const Triangle3 &hitTriangle, const VssRay &oldRay) const { // find the intersection of the plane induced by the // hit triangle with the new ray Plane3 plane(hitTriangle.GetNormal(), hitTriangle.mVertices[0]); const Vector3 hitPt = plane.FindIntersection(newRay.mTermination, newRay.mOrigin); return hitPt; } static bool EqualVisibility(const VssRay &a, const VssRay &b) { return a.mTerminationObject == b.mTerminationObject; } int GvsPreprocessor::SubdivideEdge(const Triangle3 &hitTriangle, const Vector3 &p1, const Vector3 &p2, const VssRay &ray1, const VssRay &ray2, const VssRay &oldRay) { //cout <<"y"<CastRay(sray, mViewCellsManager->GetViewSpaceBox(), !mPerViewCell); ++ castRays; if (!newRay) return castRays; //newRay->mFlags |= VssRay::BorderSample; // add new ray to queue const bool enqueued = HandleRay(newRay); // subdivide further castRays += SubdivideEdge(hitTriangle, p1, p, ray1, *newRay, oldRay); castRays += SubdivideEdge(hitTriangle, p, p2, *newRay, ray2, oldRay); // this ray will not be further processed //if (!enqueued) delete newRay; return castRays; } int GvsPreprocessor::AdaptiveBorderSampling(const VssRay ¤tRay) { Intersectable *tObj = currentRay.mTerminationObject; // q matt: can this be possible if (!tObj) return 0; Triangle3 hitTriangle; // other types not implemented yet if (tObj->Type() == Intersectable::TRIANGLE_INTERSECTABLE) hitTriangle = static_cast(tObj)->GetItem(); else cout << "border sampling: " << Intersectable::GetTypeName(tObj) << " not yet implemented" << endl; //cout << "type: " << Intersectable::GetTypeName(tObj) << endl; VertexContainer enlargedTriangle; /// create 3 new hit points for each vertex EnlargeTriangle(enlargedTriangle, hitTriangle, currentRay); /// create rays from sample points and handle them SimpleRayContainer simpleRays; simpleRays.reserve(9); VertexContainer::const_iterator vit, vit_end = enlargedTriangle.end(); for (vit = enlargedTriangle.begin(); vit != vit_end; ++ vit) { const Vector3 rayDir = (*vit) - currentRay.GetOrigin(); SimpleRay sr(currentRay.GetOrigin(), rayDir, SamplingStrategy::GVS, 1.0f); simpleRays.AddRay(sr); } if (0) { // visualize enlarged triangles VizStruct dummy; dummy.enlargedTriangle = new Polygon3(enlargedTriangle); dummy.originalTriangle = hitTriangle; vizContainer.push_back(dummy); } // cast rays to triangle vertices and determine visibility VssRayContainer vssRays; // don't cast double rays as we need only the forward rays const bool castDoubleRays = !mPerViewCell; // cannot prune invalid rays because we have to compare adjacent rays. const bool pruneInvalidRays = false; ////////// //-- fill up simple rays with random rays so we can cast 16 //const int numRandomRays = 0; const int numRandomRays = 16 - (int)simpleRays.size(); ViewCellBasedDistribution vcStrat(*this, mCurrentViewCell); GenerateRays(numRandomRays, vcStrat, simpleRays); ///////////////////// // keep origin for per view cell sampling CastRays(simpleRays, vssRays, castDoubleRays, pruneInvalidRays); const int numBorderSamples = (int)vssRays.size() - numRandomRays; #if 0 // set flags VssRayContainer::const_iterator rit, rit_end = vssRays.end(); for (rit = vssRays.begin(); rit != rit_end; ++ rit, ++ i) (*rit)->mFlags |= VssRay::BorderSample; #endif int castRays = (int)simpleRays.size(); VssRayContainer invalidSamples; // handle rays EnqueueRays(vssRays, invalidSamples); // recursivly subdivide each edge for (int i = 0; i < numBorderSamples; ++ i) { castRays += SubdivideEdge(hitTriangle, enlargedTriangle[i], enlargedTriangle[(i + 1) % numBorderSamples], *vssRays[i], *vssRays[(i + 1) % numBorderSamples], currentRay); } mGvsStats.mBorderSamples += castRays; //CLEAR_CONTAINER(invalidSamples); //cout << "cast rays: " << castRays << endl; return castRays; } bool GvsPreprocessor::GetPassingPoint(const VssRay ¤tRay, const Triangle3 &occluder, const VssRay &oldRay, Vector3 &newPoint) const { //-- The plane p = (xp, hit(x), hit(xold)) is intersected //-- with the newly found occluder (xold is the previous ray from //-- which x was generated). On the intersecting line, we select a point //-- pnew which lies just outside of the new triangle so the ray //-- just passes through the gap const Plane3 plane(currentRay.GetOrigin(), currentRay.GetTermination(), oldRay.GetTermination()); Vector3 pt1, pt2; const bool intersects = occluder.GetPlaneIntersection(plane, pt1, pt2); if (!intersects) { //cerr << "big error!! no intersection " << pt1 << " " << pt2 << endl; return false; } // get the intersection point on the old ray const Plane3 triPlane(occluder.GetNormal(), occluder.mVertices[0]); const float t = triPlane.FindT(oldRay.mOrigin, oldRay.mTermination); const Vector3 pt3 = oldRay.mOrigin + t * (oldRay.mTermination - oldRay.mOrigin); // Evaluate new hitpoint just outside the triangle const float eps = mEps; // the point is chosen to be on the side closer to the original ray if (Distance(pt1, pt3) < Distance(pt2, pt3)) newPoint = pt1 + eps * (pt1 - pt2); else newPoint = pt2 + eps * (pt2 - pt1); //cout << "passing point: " << newPoint << endl << endl; return true; } VssRay *GvsPreprocessor::ReverseSampling(const VssRay ¤tRay, const Triangle3 &hitTriangle, const VssRay &oldRay) { // get triangle occluding the path to the hit mesh Triangle3 occluder; Intersectable *tObj = currentRay.mTerminationObject; // q: why can this happen? if (!tObj) return NULL; // other types not implemented yet if (tObj->Type() == Intersectable::TRIANGLE_INTERSECTABLE) { occluder = static_cast(tObj)->GetItem(); } else { cout << "reverse sampling: " << tObj->Type() << " not yet implemented" << endl; } // get a point which is passing just outside of the occluder Vector3 newPoint; // why is there sometimes no intersecton found? if (!GetPassingPoint(currentRay, occluder, oldRay, newPoint)) return NULL; const Vector3 predicted = CalcPredictedHitPoint(currentRay, hitTriangle, oldRay); Vector3 newDir, newOrigin; //-- Construct the mutated ray with xnew, //-- dir = predicted(x)- pnew as direction vector newDir = predicted - newPoint; // take xnew, p = intersect(viewcell, line(pnew, predicted(x)) as origin ? // difficult to say!! const float offset = 0.5f; newOrigin = newPoint - newDir * offset; ////////////// //-- for per view cell sampling, we must check for intersection //-- with the current view cell if (mPerViewCell) { // send ray to view cell static Ray ray; ray.Clear(); ray.Init(newOrigin, -newDir, Ray::LOCAL_RAY); //cout << "z"; // check if ray intersects view cell if (!mCurrentViewCell->CastRay(ray)) return NULL; Ray::Intersection &hit = ray.intersections[0]; //cout << "q"; // the ray starts from the view cell newOrigin = ray.Extrap(hit.mT); } const SimpleRay simpleRay(newOrigin, newDir, SamplingStrategy::GVS, 1.0f); VssRay *reverseRay = mRayCaster->CastRay(simpleRay, mViewCellsManager->GetViewSpaceBox(), !mPerViewCell); ++ mGvsStats.mReverseSamples; return reverseRay; } int GvsPreprocessor::CastInitialSamples(const int numSamples, const int sampleType) { const long startTime = GetTime(); // generate simple rays SimpleRayContainer simpleRays; ViewCellBasedDistribution vcStrat(*this, mCurrentViewCell); GenerateRays(numSamples, vcStrat, simpleRays); //cout << "sr: " << simpleRays.size() << endl; // generate vss rays VssRayContainer samples; const bool castDoubleRays = !mPerViewCell; const bool pruneInvalidRays = true; CastRays(simpleRays, samples, castDoubleRays, pruneInvalidRays); VssRayContainer invalidSamples; // add to ray queue EnqueueRays(samples, invalidSamples); //CLEAR_CONTAINER(invalidSamples); //Debug << "generated " << numSamples << " samples in " << TimeDiff(startTime, GetTime()) * 1e-3 << " secs" << endl; return (int)simpleRays.size(); } void GvsPreprocessor::EnqueueRays(VssRayContainer &samples, VssRayContainer &invalidSamples) { // add samples to ray queue VssRayContainer::const_iterator vit, vit_end = samples.end(); for (vit = samples.begin(); vit != vit_end; ++ vit) { VssRay *ray = *vit; HandleRay(ray); //if (!HandleRay(ray)) invalidSamples.push_back(ray); } } int GvsPreprocessor::ProcessQueue() { int castSamples = 0; ++ mGvsStats.mGvsPass; while (!mRayQueue.empty())//&& (mGvsStats.mTotalSamples + castSamples < mTotalSamples) ) { //cout << "queue size: " << mRayQueue.size() << endl; // handle next ray VssRay *ray = mRayQueue.top(); mRayQueue.pop(); const int newSamples = AdaptiveBorderSampling(*ray); castSamples += newSamples; //delete ray; } /*if (mRayCaster->mVssRayPool.mIndex > mSamplesPerPass) { cout << "warning: new samples: " << castSamples << " " << "queue: " << (int)mRayQueue.size() << endl; Debug << "warning: new samples: " << castSamples << " " << "queue: " << (int)mRayQueue.size() << endl; }*/ return castSamples; } void ExportVssRays(Exporter *exporter, const VssRayContainer &vssRays) { VssRayContainer vcRays, vcRays2, vcRays3; VssRayContainer::const_iterator rit, rit_end = vssRays.end(); // prepare some rays for output for (rit = vssRays.begin(); rit != rit_end; ++ rit) { //const float p = RandomValue(0.0f, (float)vssRays.size()); if (1)//(p < raysOut) { if ((*rit)->mFlags & VssRay::BorderSample) { vcRays.push_back(*rit); } else if ((*rit)->mFlags & VssRay::ReverseSample) { vcRays2.push_back(*rit); } else { vcRays3.push_back(*rit); } } } exporter->ExportRays(vcRays, RgbColor(1, 0, 0)); exporter->ExportRays(vcRays2, RgbColor(0, 1, 0)); exporter->ExportRays(vcRays3, RgbColor(1, 1, 1)); } void GvsPreprocessor::VisualizeViewCell(const ObjectContainer &objects) { Intersectable::NewMail(); Material m; char str[64]; sprintf(str, "pass%06d.wrl", mProcessedViewCells); Exporter *exporter = Exporter::GetExporter(str); if (!exporter) return; ObjectContainer::const_iterator oit, oit_end = objects.end(); for (oit = objects.begin(); oit != oit_end; ++ oit) { Intersectable *intersect = *oit; m = RandomMaterial(); exporter->SetForcedMaterial(m); exporter->ExportIntersectable(intersect); } cout << "vssrays: " << (int)mVssRays.size() << endl; ExportVssRays(exporter, mVssRays); ///////////////// //-- export view cell geometry //exporter->SetWireframe(); m.mDiffuseColor = RgbColor(0, 1, 0); exporter->SetForcedMaterial(m); //mViewCellsManager->ExportViewCellGeometry(exporter, vc, NULL, NULL); //mViewCellsManager->ExportViewCellGeometry(exporter, mCurrentViewCell, NULL, NULL); AxisAlignedBox3 bbox = mCurrentViewCell->GetMesh()->mBox; exporter->ExportBox(bbox); //exporter->SetFilled(); delete exporter; } void GvsPreprocessor::VisualizeViewCells() { char str[64]; sprintf(str, "tmp/pass%06d_%04d-", mProcessedViewCells, mPass); // visualization if (mGvsStats.mPassContribution > 0) { const bool exportRays = true; const bool exportPvs = true; mViewCellsManager->ExportSingleViewCells(mObjects, 10, false, exportPvs, exportRays, 1000, str); } // remove pass samples ViewCellContainer::const_iterator vit, vit_end = mViewCellsManager->GetViewCells().end(); for (vit = mViewCellsManager->GetViewCells().begin(); vit != vit_end; ++ vit) { (*vit)->DelRayRefs(); } } void GvsPreprocessor::ProcessViewCell() { // compute object that directly intersect view cell IntersectWithViewCell(); mGvsStats.mPerViewCellSamples = 0; int oldContribution = mGvsStats.mTotalContribution; int passSamples = 0; mGenericStats = 0; mGenericStats2 = 0; //while (mGvsStats.mPerViewCellSamples < mTotalSamples) while (1) { mRayCaster->InitPass(); // Ray queue empty => // cast a number of uniform samples to fill ray queue int newSamples = CastInitialSamples(mInitialSamples, mSamplingType); if (!mOnlyRandomSampling) newSamples += ProcessQueue(); passSamples += newSamples; mGvsStats.mPerViewCellSamples += newSamples; if (passSamples >= mGvsSamplesPerPass) { ++ mPass; mGvsStats.mPassContribution = mGvsStats.mTotalContribution - oldContribution; //////// //-- stats mGvsStats.mPass = mPass; cout << "\nPass " << mPass << " #samples: " << mGvsStats.mPerViewCellSamples << endl; cout << "contribution=" << mGvsStats.mPassContribution << " (of " << mMinContribution << ")" << endl; //mGenericStats = mGvsStats.mPassContribution; // termination criterium if (mGvsStats.mPassContribution < mMinContribution) break; // reset oldContribution = mGvsStats.mTotalContribution; mGvsStats.mPassContribution = 0; passSamples = 0; } } } void GvsPreprocessor::CompileViewCellsList() { // Receive list of view cells from view cells manager ViewCellPointsList *vcPoints = mViewCellsManager->GetViewCellPointsList(); if (!vcPoints->empty()) { cout << "processing view cell list" << endl; vector::const_iterator vit, vit_end = vcPoints->end(); for (vit = vcPoints->begin(); vit != vit_end; ++ vit) { mViewCells.push_back((*vit)->first); if (mViewCells.size() >= mMaxViewCells) break; } } else { while ((int)mViewCells.size() < mMaxViewCells) { if (0) { mViewCells.push_back(mViewCellsManager->GetViewCell((int)mViewCells.size())); continue; } // HACK const int tries = 10000; int i = 0; for (i = 0; i < tries; ++ i) { const int idx = (int)RandomValue(0.0f, (float)mViewCellsManager->GetNumViewCells() - 0.5f); ViewCell *viewCell = mViewCellsManager->GetViewCell(idx); if (!viewCell->Mailed()) { viewCell->Mail(); break; } mViewCells.push_back(viewCell); } if (i == tries) { cerr << "big error! no view cell found" << endl; break; } } } cout << "\ncomputing list of " << mViewCells.size() << " view cells" << endl; } void GvsPreprocessor::IntersectWithViewCell() { mCurrentViewCell->GetMesh()->ComputeBoundingBox(); AxisAlignedBox3 box = mCurrentViewCell->GetMesh()->mBox; //vector leaves; //mKdTree->GetBoxIntersections(box, leaves); ObjectContainer kdobjects; mKdTree->CollectKdObjects(box, kdobjects); ObjectContainer::const_iterator oit, oit_end = kdobjects.end(); for (oit = kdobjects.begin(); oit != oit_end; ++ oit) { // add to kdnode pvs KdIntersectable *kdInt = static_cast(*oit); //mCurrentViewCell->GetPvs().AddSampleDirty(kdInt, 1.0f); mCurrentViewCell->GetPvs().AddSampleDirtyCheck(kdInt, 1.0f); mViewCellsManager->UpdateStatsForViewCell(mCurrentViewCell, kdInt); myobjects.clear(); mKdTree->CollectObjects(kdInt->GetItem(), myobjects); // account for kd object pvs ObjectContainer::const_iterator oit, oit_end = myobjects.end(); for (oit = myobjects.begin(); oit != oit_end; ++ oit) { TriangleIntersectable *triObj = static_cast(*oit); CountObject(triObj); // the triangle itself intersects if (box.Intersects(triObj->GetItem())) { if ((triObj->mCounter < ACCOUNTED_OBJECT)) { triObj->mCounter += ACCOUNTED_OBJECT; mTrianglePvs.push_back(triObj); mGenericStats = mTrianglePvs.size(); } } } } } void GvsPreprocessor::PerViewCellComputation() { ViewCell *vc; while (vc = NextViewCell()) { // hack: reset counters ObjectContainer::const_iterator oit, oit_end = mObjects.end(); for (oit = mObjects.begin(); oit != oit_end; ++ oit) (*oit)->mCounter = NOT_ACCOUNTED_OBJECT; ComputeViewCell(vc); // $$ JB HACK compute just first viewcell // break; } } void GvsPreprocessor::PerViewCellComputation2() { ViewCell *vc; while (1) { if (!mRendererWidget) continue; ViewCell *vc = mViewCellsManager->GetViewCell(mRendererWidget->GetViewPoint()); // no valid view cell or view cell already computed if (!vc || !vc->GetPvs().Empty() || !mRendererWidget->mComputeGVS) continue; mRendererWidget->mComputeGVS = false; // hack: reset counters ObjectContainer::const_iterator oit, oit_end = mObjects.end(); for (oit = mObjects.begin(); oit != oit_end; ++ oit) (*oit)->mCounter = NOT_ACCOUNTED_OBJECT; ComputeViewCell(vc); ++ mProcessedViewCells; } } void GvsPreprocessor::StorePvs(const ObjectContainer &objectPvs) { ObjectContainer::const_iterator oit, oit_end = objectPvs.end(); for (oit = objectPvs.begin(); oit != oit_end; ++ oit) { mCurrentViewCell->GetPvs().AddSample(*oit, 1); } } void GvsPreprocessor::UpdatePvs(ViewCell *currentViewCell) { ObjectPvs newPvs; BvhLeaf::NewMail(); ObjectPvsIterator pit = currentViewCell->GetPvs().GetIterator(); // output PVS of view cell while (pit.HasMoreEntries()) { Intersectable *intersect = pit.Next(); BvhLeaf *bv = intersect->mBvhLeaf; if (!bv || bv->Mailed()) continue; bv->Mail(); //m.mDiffuseColor = RgbColor(1, 0, 0); newPvs.AddSampleDirty(bv, 1.0f); } newPvs.SimpleSort(); currentViewCell->SetPvs(newPvs); } void GvsPreprocessor::GetObjectPvs(ObjectContainer &objectPvs) const { objectPvs.reserve((int)mTrianglePvs.size()); BvhLeaf::NewMail(); ObjectContainer::const_iterator oit, oit_end = mTrianglePvs.end(); for (oit = mTrianglePvs.begin(); oit != oit_end; ++ oit) { Intersectable *intersect = *oit; BvhLeaf *bv = intersect->mBvhLeaf; // hack: reset counter (*oit)->mCounter = NOT_ACCOUNTED_OBJECT; if (!bv || bv->Mailed()) continue; bv->Mail(); objectPvs.push_back(bv); } } void GvsPreprocessor::GlobalComputation() { int passSamples = 0; int oldContribution = 0; while (mGvsStats.mTotalSamples < mTotalSamples) { mRayCaster->InitPass(); // Ray queue empty => // cast a number of uniform samples to fill ray queue int newSamples = CastInitialSamples(mInitialSamples, mSamplingType); if (!mOnlyRandomSampling) newSamples += ProcessQueue(); passSamples += newSamples; mGvsStats.mTotalSamples += newSamples; if (passSamples % (mGvsSamplesPerPass + 1) == mGvsSamplesPerPass) { ++ mPass; mGvsStats.mPassContribution = mGvsStats.mTotalContribution - oldContribution; //////// //-- stats //cout << "\nPass " << mPass << " #samples: " << mGvsStats.mTotalSamples << " of " << mTotalSamples << endl; mGvsStats.mPass = mPass; mGvsStats.Stop(); mGvsStats.Print(mGvsStatsStream); // reset oldContribution = mGvsStats.mTotalContribution; mGvsStats.mPassContribution = 0; passSamples = 0; if (GVS_DEBUG) VisualizeViewCells(); } } } bool GvsPreprocessor::ComputeVisibility() { cout << "Gvs Preprocessor started\n" << flush; const long startTime = GetTime(); //Randomize(0); mGvsStats.Reset(); mGvsStats.Start(); if (!mLoadViewCells) { /// construct the view cells from the scratch ConstructViewCells(); // reset pvs already gathered during view cells construction mViewCellsManager->ResetPvs(); cout << "finished view cell construction" << endl; } if (mPerViewCell) { #if 1 // provide list of view cells to compute CompileViewCellsList(); // start per view cell gvs PerViewCellComputation(); #else PerViewCellComputation2(); #endif } else { GlobalComputation(); } cout << "cast " << 2 * mGvsStats.mTotalSamples / (1e3f * TimeDiff(startTime, GetTime())) << "M rays/s" << endl; if (GVS_DEBUG) { Visualize(); CLEAR_CONTAINER(mVssRays); } // export the preprocessed information to a file if (0 && mExportVisibility) { ExportPreprocessedData(mVisibilityFileName); } // compute the pixel error of this visibility solution if (mEvaluatePixelError) { ComputeRenderError(); } return true; } void GvsPreprocessor::DeterminePvsObjects(VssRayContainer &rays) { // store triangle directly mViewCellsManager->DeterminePvsObjects(rays, true); } void GvsPreprocessor::Visualize() { Exporter *exporter = Exporter::GetExporter("gvs.wrl"); if (!exporter) return; vector::const_iterator vit, vit_end = vizContainer.end(); for (vit = vizContainer.begin(); vit != vit_end; ++ vit) { exporter->SetWireframe(); exporter->ExportPolygon((*vit).enlargedTriangle); //Material m; exporter->SetFilled(); Polygon3 poly = Polygon3((*vit).originalTriangle); exporter->ExportPolygon(&poly); } VssRayContainer::const_iterator rit, rit_end = mVssRays.end(); for (rit = mVssRays.begin(); rit != rit_end; ++ rit) { Intersectable *obj = (*rit)->mTerminationObject; exporter->ExportIntersectable(obj); } ExportVssRays(exporter, mVssRays); delete exporter; } void GvsStatistics::Print(ostream &app) const { app << "#ViewCells\n" << mViewCells << endl; app << "#Id\n" << mViewCellId << endl; app << "#Time\n" << mTimePerViewCell << endl;; app << "#TrianglePvs\n" << mTrianglePvs << endl; app << "#ObjectPvs\n" << mPerViewCellPvs << endl; app << "#PvsCost\n" << (int)mPvsCost << endl; #if 0 app << "#TotalPvs\n" << mTotalPvs << endl; app << "#TotalTime\n" << mTotalTime << endl; app << "#RaysPerSec\n" << RaysPerSec() << endl; app << "#PerViewCellSamples\n" << mPerViewCellSamples << endl; app << "#TotalSamples\n" << mTotalSamples << endl; app << "#SamplesContri\n" << mTotalContribution << endl; app << "#TotalTrianglePvs\n" << mTotalTrianglePvs << endl; #endif //app << "#ReverseSamples\n" << mReverseSamples << endl; //app << "#BorderSamples\n" << mBorderSamples << endl; //app << "#Pass\n" << mPass << endl; //app << "#ScDiff\n" << mPassContribution << endl; //app << "#GvsRuns\n" << mGvsPass << endl; app << endl; } void GvsPreprocessor::ComputeViewCell(ViewCell *vc) { KdNode::NewMail(); mCurrentViewCell = vc; const long startTime = GetTime(); cout << "\n***********************\n" << "computing view cell " << mProcessedViewCells << " (id: " << mCurrentViewCell->GetId() << ")" << endl; // compute the pvs of the current view cell ProcessViewCell(); mGvsStats.mTrianglePvs = (int)mTrianglePvs.size(); mGvsStats.mTotalTrianglePvs += mGvsStats.mTrianglePvs; if (!mUseKdPvs) { ObjectContainer objectPvs; // optain object pvs GetObjectPvs(objectPvs); // add pvs ObjectContainer::const_iterator it, it_end = objectPvs.end(); for (it = objectPvs.begin(); it != it_end; ++ it) { mCurrentViewCell->GetPvs().AddSampleDirty(*it, 1.0f); } cout << "triangle pvs of " << (int)mTrianglePvs.size() << " was converted to object pvs of " << (int)objectPvs.size() << endl; mGvsStats.mPerViewCellPvs = (int)objectPvs.size(); } else { mGvsStats.mPerViewCellPvs = mCurrentViewCell->GetPvs().GetSize(); mGvsStats.mPvsCost = mCurrentViewCell->GetPvs().EvalPvsCost(); cout << "id: " << mCurrentViewCell->GetId() << " pvs cost: " << mGvsStats.mPvsCost << " pvs tri: " << mTrianglePvs.size() << endl; } //////// //-- stats mGvsStats.mViewCells = mProcessedViewCells;//mPass; mGvsStats.mTotalPvs += mGvsStats.mPerViewCellPvs; mGvsStats.mTotalSamples += mGvsStats.mPerViewCellSamples; // timing const long currentTime = GetTime(); mGvsStats.mTimePerViewCell = TimeDiff(startTime, currentTime) * 1e-3f; mGvsStats.mTotalTime += mGvsStats.mTimePerViewCell; mGvsStats.Stop(); mGvsStats.Print(mGvsStatsStream); mTrianglePvs.clear(); } }