#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 "IntersectableWrapper.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; //class BvhSubdivisionCandidate; /** 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; // nodes with minimum objects int minObjectsNodes; // 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; minObjectsNodes = 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(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; /** 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 static void NewMail(const int reserve = 1) { sMailId += sReservedMailboxes; sReservedMailboxes = reserve; } void Mail() { mMailbox = sMailId; } bool Mailed() const { return mMailbox == sMailId; } void Mail(const int mailbox) { mMailbox = sMailId + mailbox; } bool Mailed(const int mailbox) const { return mMailbox == sMailId + mailbox; } int IncMail() { return ++ mMailbox - sMailId; } static int sMailId; int mMailbox; static int sReservedMailboxes; /////////////////////////////////// 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; 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; SubdivisionCandidate *GetSubdivisionCandidate()// const { return mSubdivisionCandidate; } void SetSubdivisionCandidate(SubdivisionCandidate *candidate) { mSubdivisionCandidate = candidate; } 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: BvhTraversalData(): mNode(NULL), mDepth(0), mProbability(0.0), mMaxCostMisses(0), mPriority(0), mAxis(0) {} BvhTraversalData(BvhLeaf *node, const int depth, const AxisAlignedBox3 &box, const float v): mNode(node), mDepth(depth), mBoundingBox(box), mProbability(v), mMaxCostMisses(0), mPriority(0), mAxis(0) {} /** Returns cost of the traversal data. */ float GetCost() const { return mPriority; } /// deletes contents and sets them to NULL void Clear() { DEL_PTR(mNode); } /// the current node BvhLeaf *mNode; /// current depth int mDepth; /// the probability that this node is seen float mProbability; /// 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; 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: BvhSubdivisionCandidate(const BvhTraversalData &tData): mParentData(tData) {}; ~BvhSubdivisionCandidate() { mParentData.Clear(); } 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) {} /// pointer to parent tree. static BvHierarchy *sBvHierarchy; /// parent data BvhTraversalData mParentData; /// the objects on the front of the potential split ObjectContainer mFrontObjects; /// the objects on the back of the potential split ObjectContainer mBackObjects; }; /** 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) {} }; /** 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 the objects during the heuristics */ struct SortableEntry { Intersectable *mObject; float mPos; SortableEntry() {} SortableEntry(Intersectable *obj, const float pos): mObject(obj), mPos(pos) {} bool operator<(const SortableEntry &b) const { return mPos < b.mPos; } }; /** Evaluate balanced object partition. */ float EvalLocalObjectPartition( const BvhTraversalData &tData, const int axis, ObjectContainer &objectsFront, ObjectContainer &objectsBack); float EvalSah( const BvhTraversalData &tData, const int axis, ObjectContainer &objectsFront, ObjectContainer &objectsBack); /** Computes priority of the traversal data and stores it in tData. */ void EvalPriority(BvhTraversalData &tData) const; /** Evaluates render cost of next split. */ float EvalRenderCost( const BvhTraversalData &tData, const ObjectContainer &objectsLeft, const ObjectContainer &objectsRight) 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); void ExportObjects(BvhLeaf *leaf, OUT_STREAM &stream); /** Returns estimated memory usage of tree. */ float GetMemUsage() const; /** Creates new root of hierarchy. */ void CreateRoot(const ObjectContainer &objects); ///////////////////////////// // 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); /** Computes best cost for axis aligned planes. */ float EvalLocalCostHeuristics( const BvhTraversalData &tData, const int axis, ObjectContainer &objectsFront, ObjectContainer &objectsFBack); /** Evaluates the contribution to the front and back volume when this object is changing sides in the bvs. @param object the object @param volLeft updates the left pvs @param volPvs updates the right pvs */ void EvalHeuristicsContribution( Intersectable *obj, float &volLeft, float &volRight); /** Prepares objects for the cost heuristics. @returns sum of volume of associated view cells */ float PrepareHeuristics(const BvhTraversalData &tData, const int axis); //////////////////////////////////////////////// /** Prepares construction for vsp and osp trees. */ AxisAlignedBox3 ComputeBoundingBox( const ObjectContainer &objects, const AxisAlignedBox3 *parentBox = NULL); void CollectDirtyCandidates( BvhSubdivisionCandidate *sc, vector &dirtyList); /** Collect view cells which see this bvh leaf. */ void CollectViewCells( const ObjectContainer &objects, ViewCellContainer &viewCells, const bool setCounter = false) const; /** Collects view cells which see an object. */ void CollectViewCells( Intersectable *object, ViewCellContainer &viewCells, const bool useMailBoxing, const bool setCounter = false) const; /** Rays will be clipped to the bounding box. */ void PreprocessRays( BvhLeaf *root, const VssRayContainer &sampleRays, RayInfoContainer &rays); /** Print the subdivision stats in the subdivison log. */ void PrintSubdivisionStats(const SubdivisionCandidate &tData); void AddSubdivisionStats( const int viewCells, const float renderCostDecr, const float totalRenderCost); void AssociateObjectsWithRays(const VssRayContainer &rays); bool IsObjectInLeaf(BvhLeaf *, Intersectable *object) const; SubdivisionCandidate *PrepareConstruction( const VssRayContainer &sampleRays, const ObjectContainer &objects ); float EvalViewCellsVolume(const ObjectContainer &objects) 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 mTermMinProbability; /// 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