#ifndef _BvHierarchy_H__ #define _BvHierarchy_H__ #include #include "Mesh.h" #include "Containers.h" #include "Statistics.h" #include "VssRay.h" #include "RayInfo.h" #include "gzstream.h" #include "SubdivisionCandidate.h" #include "AxisAlignedBox3.h" #include "KdIntersectable.h" namespace GtpVisibilityPreprocessor { class ViewCellLeaf; class Plane3; class AxisAlignedBox3; class Ray; class ViewCellsStatistics; class ViewCellsManager; class MergeCandidate; class Beam; class ViewCellsTree; class Environment; class BvhInterior; class BvhLeaf; class BvhNode; class BvhIntersectable; class BvhTree; class VspTree; class ViewCellsContainer; /** View space partition statistics. */ class BvhStatistics: public StatisticsBase { public: // total number of nodes int nodes; // number of splits int splits; // maximal reached depth int maxDepth; // minimal depth int minDepth; // max depth nodes int maxDepthNodes; // minimum depth nodes int minDepthNodes; // max depth nodes int minPvsNodes; // nodes with minimum PVS int minRaysNodes; // max ray contribution nodes int maxRayContribNodes; // minimum area nodes int minProbabilityNodes; /// nodes termination because of max cost ratio; int maxCostNodes; // max number of rays per node int maxObjectRefs; /// number of invalid leaves int invalidLeaves; /// accumulated number of rays refs //int accumRays; // accumulated depth (used to compute average) int accumDepth; /// object references int objectRefs; // Constructor BvhStatistics() { Reset(); } int Nodes() const {return nodes;} int Interior() const { return nodes / 2; } int Leaves() const { return (nodes / 2) + 1; } // TODO: computation wrong double AvgDepth() const { return accumDepth / (double)Leaves(); } void Reset() { nodes = 0; splits = 0; maxDepth = 0; minDepth = 99999; accumDepth = 0; maxDepthNodes = 0; minPvsNodes = 0; minRaysNodes = 0; maxRayContribNodes = 0; minProbabilityNodes = 0; maxCostNodes = 0; invalidLeaves = 0; maxObjectRefs = 0; objectRefs = 0; } void Print(ostream &app) const; friend ostream &operator<<(ostream &s, const BvhStatistics &stat) { stat.Print(s); return s; } }; /** VspNode abstract class serving for interior and leaf node implementation */ class BvhNode { public: // types of vsp nodes enum {Interior, Leaf}; BvhNode::BvhNode(); BvhNode(const AxisAlignedBox3 &bbox); BvhNode(const AxisAlignedBox3 &bbox, BvhInterior *parent); virtual ~BvhNode(){}; /** Determines whether this node is a leaf or not @return true if leaf */ virtual bool IsLeaf() const = 0; //virtual int Type() const = 0; /** Determines whether this node is a root @return true if root */ virtual bool IsRoot() const; /** Returns parent node. */ BvhInterior *GetParent(); /** Sets parent node. */ void SetParent(BvhInterior *parent); /** The bounding box specifies the node extent. */ AxisAlignedBox3 GetBoundingBox() const { return mBoundingBox; } void SetBoundingBox(const AxisAlignedBox3 &boundingBox) { mBoundingBox = boundingBox; } //-- mailing options void Mail() { mMailbox = sMailId; } static void NewMail() { ++ sMailId; } bool Mailed() const { return mMailbox == sMailId; } static int sMailId; int mMailbox; /////////////////////////////////// protected: /// the bounding box of the node AxisAlignedBox3 mBoundingBox; /// parent of this node BvhInterior *mParent; }; /** BSP interior node implementation */ class BvhInterior: public BvhNode { public: /** Standard contructor taking a bounding box as argument. */ BvhInterior(const AxisAlignedBox3 &bbox); BvhInterior(const AxisAlignedBox3 &bbox, BvhInterior *parent); ~BvhInterior(); /** @return false since it is an interior node */ bool IsLeaf() const; //int Type() const; BvhNode *GetBack() { return mBack; } BvhNode *GetFront() { return mFront; } /** Replace front or back child with new child. */ void ReplaceChildLink(BvhNode *oldChild, BvhNode *newChild); /** Replace front and back child. */ void SetupChildLinks(BvhNode *front, BvhNode *back); friend ostream &operator<<(ostream &s, const BvhInterior &A) { return s << A.mBoundingBox; } protected: /// back node BvhNode *mBack; /// front node BvhNode *mFront; }; /** BSP leaf node implementation. */ class BvhLeaf: public BvhNode { public: /** Standard contructor taking a bounding box as argument. */ BvhLeaf(const AxisAlignedBox3 &bbox); BvhLeaf(const AxisAlignedBox3 &bbox, BvhInterior *parent); BvhLeaf(const AxisAlignedBox3 &bbox, BvhInterior *parent, const int numObjects); ~BvhLeaf(); /** @return true since it is an interior node */ bool IsLeaf() const; //int Type() const; SubdivisionCandidate *GetSubdivisionCandidate() const { return mSubdivisionCandidate; } public: /// Rays piercing this leaf. VssRayContainer mVssRays; /// objects ObjectContainer mObjects; /// universal counter int mCounter; protected: /// pointer to a split plane candidate splitting this leaf SubdivisionCandidate *mSubdivisionCandidate; }; typedef map BvhIntersectableMap; /** View Space Partitioning tree. */ class BvHierarchy { friend class ViewCellsParseHandlers; friend class HierarchyManager; public: /** Additional data which is passed down the BSP tree during traversal. */ class BvhTraversalData { public: /// the current node BvhLeaf *mNode; /// current depth int mDepth; /// rays piercing this node //RayInfoContainer *mRays; /// the volume of this node float mVolume; /// the bounding box of the node AxisAlignedBox3 mBoundingBox; /// how often this branch has missed the max-cost ratio int mMaxCostMisses; /// current axis int mAxis; /// current priority float mPriority; BvhTraversalData(): mNode(NULL), //mRays(NULL), mDepth(0), mVolume(0.0), mMaxCostMisses(0), mPriority(0), mAxis(0) {} BvhTraversalData(BvhLeaf *node, const int depth, //RayInfoContainer *rays, const float v): mNode(node), mDepth(depth), //mRays(rays), mVolume(v), mMaxCostMisses(0), mPriority(0), mAxis(0) {} BvhTraversalData( const int depth //,RayInfoContainer *rays ): mNode(NULL), mDepth(depth), //mRays(rays), mVolume(0), mMaxCostMisses(0), mAxis(0) {} /** Returns cost of the traversal data. */ float GetCost() const { //cout << mPriority << endl; return mPriority; } /// deletes contents and sets them to NULL void Clear() { //DEL_PTR(mRays); } friend bool operator<(const BvhTraversalData &a, const BvhTraversalData &b) { return a.GetCost() < b.GetCost(); } }; /** Candidate for a view space split. */ class BvhSubdivisionCandidate: public SubdivisionCandidate { public: static BvHierarchy *sBvHierarchy; /// parent data BvhTraversalData mParentData; ObjectContainer mFrontObjects; ObjectContainer mBackObjects; BvhSubdivisionCandidate(const BvhTraversalData &tData): mParentData(tData) {}; int Type() const { return OBJECT_SPACE; } void EvalPriority() { sBvHierarchy->EvalSubdivisionCandidate(*this); } bool GlobalTerminationCriteriaMet() const { return sBvHierarchy->GlobalTerminationCriteriaMet(mParentData); } BvhSubdivisionCandidate( const ObjectContainer &frontObjects, const ObjectContainer &backObjects, const BvhTraversalData &tData): mFrontObjects(frontObjects), mBackObjects(backObjects), mParentData(tData) {} }; /** Struct for traversing line segment. */ struct LineTraversalData { BvhNode *mNode; Vector3 mExitPoint; float mMaxT; LineTraversalData () {} LineTraversalData (BvhNode *n, const Vector3 &p, const float maxt): mNode(n), mExitPoint(p), mMaxT(maxt) {} }; //typedef std::priority_queue VspOspTraversalQueue; /** Default constructor creating an empty tree. */ BvHierarchy(); /** Default destructor. */ ~BvHierarchy(); /** Returns tree statistics. */ const BvhStatistics &GetStatistics() const; /** Returns bounding box of the specified node. */ AxisAlignedBox3 GetBoundingBox(BvhNode *node) const; /** Reads parameters from environment singleton. */ void ReadEnvironment(); /** Evaluates candidate for splitting. */ void EvalSubdivisionCandidate(BvhSubdivisionCandidate &splitData); /** Returns list of leaves with pvs smaller than a certain threshold. @param onlyUnmailed if only the unmailed leaves should be considered @param maxPvs the maximal pvs of a leaf to be added (-1 means unlimited) */ void CollectLeaves(vector &leaves) const; /** Returns bounding box of the whole tree (= bbox of root node) */ AxisAlignedBox3 GetBoundingBox()const; /** Returns root of the view space partitioning tree. */ BvhNode *GetRoot() const; /** A ray is cast possible intersecting the tree. @param the ray that is cast. @returns the number of intersections with objects stored in the tree. */ //int CastRay(Ray &ray); /** finds neighbouring leaves of this tree node. */ int FindNeighbors(BvhLeaf *n, vector &neighbors, const bool onlyUnmailed) const; /** Returns random leaf of BSP tree. @param halfspace defines the halfspace from which the leaf is taken. */ BvhLeaf *GetRandomLeaf(const Plane3 &halfspace); /** Returns random leaf of BSP tree. @param onlyUnmailed if only unmailed leaves should be returned. */ BvhLeaf *GetRandomLeaf(const bool onlyUnmailed = false); /** Casts line segment into the tree. @param origin the origin of the line segment @param termination the end point of the line segment @returns view cells intersecting the line segment. */ int CastLineSegment(const Vector3 &origin, const Vector3 &termination, ViewCellContainer &viewcells); /** Sets pointer to view cells manager. */ void SetViewCellsManager(ViewCellsManager *vcm); /** Writes tree to output stream */ bool Export(OUT_STREAM &stream); /** Returns or creates a new intersectable for use in a kd based pvs. The OspTree is responsible for destruction of the intersectable. */ BvhIntersectable *GetOrCreateBvhIntersectable(BvhNode *node); /** Collects rays. */ void CollectRays(const ObjectContainer &objects, VssRayContainer &rays) const; /** Intersects box with the tree and returns the number of intersected boxes. @returns number of view cells found */ int ComputeBoxIntersections( const AxisAlignedBox3 &box, ViewCellContainer &viewCells) const; /** Returns leaf the point pt lies in, starting from root. */ BvhLeaf *GetLeaf(Intersectable *obj, BvhNode *root = NULL) const; ViewCellsTree *GetViewCellsTree() const { return mViewCellsTree; } void SetViewCellsTree(ViewCellsTree *vt) { mViewCellsTree = vt; } /** Add the bvh leaf to the pvs of the view cell. */ bool AddLeafToPvs( BvhLeaf *leaf, ViewCell *vc, const float pdf, float &contribution); protected: /** Returns true if tree can be terminated. */ bool LocalTerminationCriteriaMet(const BvhTraversalData &data) const; /** Returns true if global tree can be terminated. */ bool GlobalTerminationCriteriaMet(const BvhTraversalData &data) const; // -------------------------------------------------------------- // For sorting objects // -------------------------------------------------------------- struct SortableEntry { /** There is a 3th "event" for rays which intersect a box in the middle. These "events" don't induce a change in pvs size, but may induce a change in view cell volume. */ enum EType { OBJECT, VIEWCELLS }; int mType; float mPos; VssRay *mRay; Intersectable *mObject; SortableEntry() {} SortableEntry( const int type, const float pos, Intersectable *obj, VssRay *ray): mType(type), mPos(pos), mObject(obj), mRay(ray) {} bool operator<(const SortableEntry &b) const { return mPos < b.mPos; } }; /** faster evaluation of split plane cost for kd axis aligned cells. */ float EvalLocalSplitCost(const BvhTraversalData &data, const AxisAlignedBox3 &box, const int axis, const float &position, float &pFront, float &pBack) const; /** Computes priority of the traversal data and stores it in tData. */ void EvalPriority(BvhTraversalData &tData) const; /** Evaluates render cost decrease of next split. */ float EvalRenderCostDecrease( const ObjectContainer &objectsLeft, const ObjectContainer &objectsRight, const BvhTraversalData &tData, float &normalizedOldRenderCost) const; /** Evaluates tree stats in the BSP tree leafs. */ void EvaluateLeafStats(const BvhTraversalData &data); /** Subdivides node using a best split priority queue. @param tQueue the best split priority queue @param splitCandidate the candidate for the next split @param globalCriteriaMet if the global termination criteria were already met @returns new root of the subtree */ BvhNode *Subdivide( SplitQueue &tQueue, SubdivisionCandidate *splitCandidate, const bool globalCriteriaMet); /** Subdivides leaf. @param leaf the leaf to be subdivided @param polys the polygons to be split @param frontPolys returns the polygons in front of the split plane @param backPolys returns the polygons in the back of the split plane @param rays the polygons to be filtered @param frontRays returns the polygons in front of the split plane @param backRays returns the polygons in the back of the split plane @returns the root of the subdivision */ BvhInterior *SubdivideNode( const ObjectContainer &frontObjects, const ObjectContainer &backObjects, const BvhTraversalData &tData, BvhTraversalData &frontData, BvhTraversalData &backData); /** Splits the objects for the next subdivision. @returns cost for this split */ float SelectObjectPartition( const BvhTraversalData &tData, ObjectContainer &frontObjects, ObjectContainer &backObjects); /** Writes the node to disk @note: should be implemented as visitor. */ void ExportNode(BvhNode *node, OUT_STREAM &stream); /** Returns estimated memory usage of tree. */ float GetMemUsage() const; ///////////////////////////// // Helper functions for local cost heuristics /** Sorts split candidates for cost heuristics using axis aligned splits. @param node the current node @param axis the current split axis */ void SortSubdivisionCandidates( const BvhTraversalData &data, const int axis, float minBand, float maxBand); /** Computes best cost for axis aligned planes. */ float EvalLocalCostHeuristics( const BvhTraversalData &tData, const int axis, ObjectContainer &objectsFront, ObjectContainer &objectsFBack); /** Evaluate the volume of view cells seeing the left and right child cell. */ void EvalRayContribution(const VssRay &ray, float &volLeft, float &volRight); /** Evaluates the influence on the pvs of the event. @param ve the visibility event @param pvsLeft updates the left pvs @param rightPvs updates the right pvs */ void EvalHeuristicsContribution( BvhLeaf *leaf, const SortableEntry &ci, float &volLeft, float &volRight, int &objectsLeft); /** Prepares objects for the cost heuristics. @returns sum of volume of associated view cells */ float PrepareHeuristics(const BvhTraversalData &tData); /** Prepares heuristics for a particular ray. */ float PrepareHeuristics(const VssRay &ray); void AddViewCellVolumeContri( ViewCell *vc, float &frontVol, float &backVol, float &frontAndBackVol) const; //////////////////////////////////////////////// /** Prepares construction for vsp and osp trees. */ AxisAlignedBox3 ComputeBoundingBox(const ObjectContainer &objects); void CollectDirtyCandidates(BvhSubdivisionCandidate *sc, vector &dirtyList); /** Collect view cells which see this kd leaf. */ void CollectViewCells(BvhLeaf *leaf, ViewCellContainer &viewCells); /** Rays will be clipped to the bounding box. */ void PreprocessRays( BvhLeaf *root, const VssRayContainer &sampleRays, RayInfoContainer &rays); void EvalSubdivisionStats(const SubdivisionCandidate &tData); void AddSubdivisionStats( const int viewCells, const float renderCostDecr, const float totalRenderCost); void AssociateObjectsWithRays(const VssRayContainer &rays); //float EvalViewCellsVolume(BvhLeaf *leaf) const; /* int RemoveParentViewCellsPvs(BvhLeaf *leaf, const RayInfoContainer &rays) const; int UpdateViewCellsPvs(BvhLeaf *leaf, const RayInfoContainer &rays) const; bool AddViewCellToObjectPvs( Intersectable *obj, ViewCell *vc, float &contribution, bool onlyMailed) const; */ bool IsObjectInLeaf(BvhLeaf *, Intersectable *object) const; void MailViewCells( const ObjectContainer &objects, const bool isFront, ViewCellContainer &collectedViewCells) const; SubdivisionCandidate *PrepareConstruction(const VssRayContainer &sampleRays, ObjectContainer &objects, AxisAlignedBox3 *forcedObjectSpace //, RayInfoContainer &rays ); float EvalViewCellsVolume(BvhLeaf *leaf) const; protected: /// pointer to the hierarchy of view cells ViewCellsTree *mViewCellsTree; VspTree *mVspTree; /// The view cells manager ViewCellsManager *mViewCellsManager; /// candidates for placing split planes during cost heuristics vector *mSubdivisionCandidates; /// Pointer to the root of the tree BvhNode *mRoot; /// Statistics for the object space partition BvhStatistics mBvhStats; /// box around the whole view domain AxisAlignedBox3 mBoundingBox; //-- local termination /// maximal possible depth int mTermMaxDepth; /// mininum probability float mTermMinVolume; /// minimal number of objects int mTermMinObjects; /// maximal contribution per ray float mTermMaxRayContribution; /// maximal acceptable cost ratio float mTermMaxCostRatio; /// tolerance value indicating how often the max cost ratio can be failed int mTermMissTolerance; //-- global criteria float mTermMinGlobalCostRatio; int mTermGlobalCostMissTolerance; int mGlobalCostMisses; /// maximal number of view cells int mTermMaxLeaves; /// maximal tree memory float mMaxMemory; /// the tree is out of memory bool mOutOfMemory; //-- split heuristics based parameters bool mUseCostHeuristics; /// balancing factor for PVS criterium float mCtDivCi; /// if only driving axis should be used for split bool mOnlyDrivingAxis; /// current time stamp (used for keeping split history) int mTimeStamp; // if rays should be stored in leaves bool mStoreRays; /// epsilon for geometric comparisons float mEpsilon; /// subdivision stats output file ofstream mSubdivisionStats; /// keeps track of cost during subdivision float mTotalCost; /// keeps track of overall pvs size during subdivision int mTotalPvsSize; /// number of currenly generated view cells int mCreatedLeaves; /// represents min and max band for sweep float mSplitBorder; /// weight between render cost decrease and node render cost float mRenderCostDecreaseWeight; /// stores the kd node intersectables used for pvs BvhIntersectableMap mBvhIntersectables; }; } #endif