source: GTP/trunk/Lib/Vis/Preprocessing/src/OspTree.h @ 1379

Revision 1379, 20.7 KB checked in by mattausch, 18 years ago (diff)

fixed sah for objeect partition
loader for single triangles also for x3d

Line 
1#ifndef _OspTree_H__
2#define _OspTree_H__
3
4#include <stack>
5
6#include "Mesh.h"
7#include "Containers.h"
8#include "Statistics.h"
9#include "VssRay.h"
10#include "RayInfo.h"
11#include "gzstream.h"
12#include "SubdivisionCandidate.h"
13
14
15namespace GtpVisibilityPreprocessor {
16
17class ViewCellLeaf;
18class Plane3;
19class AxisAlignedBox3;
20class Ray;
21class ViewCellsStatistics;
22class ViewCellsManager;
23class MergeCandidate;
24class Beam;
25class ViewCellsTree;
26class Environment;
27class VspInterior;
28class VspLeaf;
29class VspNode;
30class KdNode;
31class KdInterior;
32class KdLeaf;
33class OspTree;
34class KdIntersectable;
35class KdTree;
36class VspTree;
37class KdTreeStatistics;
38class SubdivisionCandidate;
39class HierarchyManager;
40
41
42
43/** View space partition statistics.
44*/
45class OspTreeStatistics: public StatisticsBase
46{
47public:
48        // total number of nodes
49        int nodes;
50        // number of splits
51        int splits[3];
52       
53        // maximal reached depth
54        int maxDepth;
55        // minimal depth
56        int minDepth;
57       
58        // max depth nodes
59        int maxDepthNodes;
60        // minimum depth nodes
61        int minDepthNodes;
62        // max depth nodes
63        int minPvsNodes;
64        // minimum area nodes
65        int minProbabilityNodes;
66        /// nodes termination because of max cost ratio;
67        int maxCostNodes;
68        // max number of objects per node
69        int maxObjectRefs;
70        int objectRefs;
71        /// samples contributing to pvs
72        int contributingSamples;
73        /// sample contributions to pvs
74        int sampleContributions;
75        /// largest pvs
76        int maxPvs;
77        /// number of invalid leaves
78        int invalidLeaves;
79        /// accumulated number of rays refs
80        int accumRays;
81        /// potentially visible objects from this leaf
82        int pvs;
83
84        // accumulated depth (used to compute average)
85        int accumDepth;
86
87        // Constructor
88        OspTreeStatistics()
89        {
90                Reset();
91        }
92
93        int Nodes() const {return nodes;}
94        int Interior() const { return nodes / 2; }
95        int Leaves() const { return (nodes / 2) + 1; }
96       
97        // TODO: computation wrong
98        double AvgDepth() const { return accumDepth / (double)Leaves();};
99       
100
101        void Reset()
102        {
103                nodes = 0;
104                for (int i = 0; i < 3; ++ i)
105                        splits[i] = 0;
106               
107                maxDepth = 0;
108                minDepth = 99999;
109                accumDepth = 0;
110        pvs = 0;
111                maxDepthNodes = 0;
112                minPvsNodes = 0;
113       
114                minProbabilityNodes = 0;
115                maxCostNodes = 0;
116
117                contributingSamples = 0;
118                sampleContributions = 0;
119
120                maxPvs = 0;
121                invalidLeaves = 0;
122                objectRefs = 0;
123
124                maxObjectRefs = 0;
125        }
126
127
128        void Print(ostream &app) const;
129
130        friend ostream &operator<<(ostream &s, const OspTreeStatistics &stat)
131        {
132                stat.Print(s);
133                return s;
134        }
135};
136
137
138typedef map<KdNode *, KdIntersectable *> KdIntersectableMap;
139
140
141/** View Space Partitioning tree.
142*/
143class OspTree
144{
145        friend class ViewCellsParseHandlers;
146        friend class HierarchyManager;
147
148public:
149       
150        /** Additional data which is passed down the BSP tree during traversal.
151        */
152        class OspTraversalData
153        { 
154        public:
155                /// the current node
156                KdLeaf *mNode;
157                /// current depth
158                int mDepth;
159                /// rays piercing this node
160                RayInfoContainer *mRays;
161                /// the probability that this node contains view point
162                float mProbability;
163                /// the bounding box of the node
164                AxisAlignedBox3 mBoundingBox;
165                /// pvs size
166                float mRenderCost;
167                /// how often this branch has missed the max-cost ratio
168                int mMaxCostMisses;
169                // current axis
170                int mAxis;
171                // current priority
172                float mPriority;
173
174
175                OspTraversalData():
176                mNode(NULL),
177                mRays(NULL),
178                mDepth(0),
179                mRenderCost(0),
180                mProbability(0.0),
181                mMaxCostMisses(0),
182                mPriority(0),
183                mAxis(0)
184                {}
185               
186                OspTraversalData(KdLeaf *node,
187                                                 const int depth,
188                         RayInfoContainer *rays,
189                                                 const float rc,
190                                                 const float p,
191                                                 const AxisAlignedBox3 &box):
192                mNode(node),
193                mDepth(depth),
194                mRays(rays),
195                mRenderCost(rc),
196                mProbability(p),
197                mBoundingBox(box),
198                mMaxCostMisses(0),
199                mPriority(0),
200                mAxis(0)
201                {}
202
203                OspTraversalData(const int depth,
204                        RayInfoContainer *rays,
205                        const AxisAlignedBox3 &box):
206                mNode(NULL),
207                mDepth(depth),
208                mRays(rays),
209                mRenderCost(0),
210                mProbability(0),
211                mMaxCostMisses(0),
212                mAxis(0),
213                mBoundingBox(box)
214                {}
215
216                /** Returns cost of the traversal data.
217                */
218                float GetCost() const
219                {
220                        //cout << mPriority << endl;
221                        return mPriority;
222                }
223
224                /// deletes contents and sets them to NULL
225                void Clear()
226                {
227                        DEL_PTR(mRays);
228                }
229
230
231                friend bool operator<(const OspTraversalData &a, const OspTraversalData &b)
232                {
233                        return a.GetCost() < b.GetCost();
234                }
235    };
236
237        /** Candidate for a view space split.
238        */
239        class OspSubdivisionCandidate: public SubdivisionCandidate
240        { 
241        public:
242                static OspTree* sOspTree;
243
244                /// the current split plane
245                AxisAlignedPlane mSplitPlane;
246                /// parent data
247                OspTraversalData mParentData;
248               
249                OspSubdivisionCandidate(const OspTraversalData &tData): mParentData(tData)
250                {};
251
252                int Type() const { return OBJECT_SPACE; }
253       
254                void EvalPriority()
255                {
256                        sOspTree->EvalSubdivisionCandidate(*this);     
257                }
258
259                bool GlobalTerminationCriteriaMet() const
260                {
261                        return sOspTree->GlobalTerminationCriteriaMet(mParentData);
262                }
263
264
265                OspSubdivisionCandidate(const AxisAlignedPlane &plane, const OspTraversalData &tData):
266                mSplitPlane(plane), mParentData(tData)
267                {}
268        };
269
270        /** Struct for traversing line segment.
271        */
272        struct LineTraversalData
273        {
274                KdNode *mNode;
275                Vector3 mExitPoint;
276               
277                float mMaxT;
278   
279                LineTraversalData () {}
280                LineTraversalData (KdNode *n, const Vector3 &p, const float maxt):
281                mNode(n), mExitPoint(p), mMaxT(maxt) {}
282        };
283
284        /** Default constructor creating an empty tree.
285        */
286        OspTree();
287
288        /** Copies tree from a kd tree.
289        */
290        OspTree(const KdTree &kdTree);
291
292        /** Default destructor.
293        */
294        ~OspTree();
295
296        /** Returns tree statistics.
297        */
298        const OspTreeStatistics &GetStatistics() const;
299 
300        /** Returns bounding box of the specified node.
301        */
302        AxisAlignedBox3 GetBoundingBox(KdNode *node) const;
303
304        /** Returns list of leaves with pvs smaller than
305                a certain threshold.
306                @param onlyUnmailed if only the unmailed leaves should be considered
307                @param maxPvs the maximal pvs of a leaf to be added (-1 means unlimited)
308        */
309       
310        void CollectLeaves(vector<KdLeaf *> &leaves) const;
311
312        /** Returns bounding box of the whole tree (= bbox of root node)
313        */
314        AxisAlignedBox3 GetBoundingBox()const;
315
316        /** Returns root of the view space partitioning tree.
317        */
318        KdNode *GetRoot() const;
319
320        /** Collects the leaf view cells of the tree
321                @param viewCells returns the view cells
322        */
323        void CollectViewCells(ViewCellContainer &viewCells, bool onlyValid) const;
324
325        /** A ray is cast possible intersecting the tree.
326                @param the ray that is cast.
327                @returns the number of intersections with objects stored in the tree.
328        */
329        int CastRay(Ray &ray);
330
331        /** finds neighbouring leaves of this tree node.
332        */
333        int FindNeighbors(KdLeaf *n,
334                                          vector<VspLeaf *> &neighbors,
335                                          const bool onlyUnmailed) const;
336
337        /** Returns random leaf of BSP tree.
338                @param halfspace defines the halfspace from which the leaf is taken.
339        */
340        KdLeaf *GetRandomLeaf(const Plane3 &halfspace);
341
342        /** Returns random leaf of BSP tree.
343                @param onlyUnmailed if only unmailed leaves should be returned.
344        */
345        KdLeaf *GetRandomLeaf(const bool onlyUnmailed = false);
346
347        /** Returns epsilon of this tree.
348        */
349        float GetEpsilon() const;
350
351        /** Casts line segment into the tree.
352                @param origin the origin of the line segment
353                @param termination the end point of the line segment
354                @returns view cells intersecting the line segment.
355        */
356    int CastLineSegment(const Vector3 &origin,
357                                                const Vector3 &termination,
358                                                ViewCellContainer &viewcells);
359               
360        /** Sets pointer to view cells manager.
361        */
362        void SetViewCellsManager(ViewCellsManager *vcm);
363
364        /** Writes tree to output stream
365        */
366        bool Export(OUT_STREAM &stream);
367
368        /** Returns or creates a new intersectable for use in a kd based pvs.
369                The OspTree is responsible for destruction of the intersectable.
370        */
371        KdIntersectable *GetOrCreateKdIntersectable(KdNode *node);
372
373        /** Collects rays stored in the leaves.
374        */
375        void CollectRays(VssRayContainer &rays);
376
377        /** Intersects box with the tree and returns the number of intersected boxes.
378                @returns number of view cells found
379        */
380        int ComputeBoxIntersections(const AxisAlignedBox3 &box,
381                                                                ViewCellContainer &viewCells) const;
382
383
384        /** Returns kd leaf the point pt lies in, starting from root.
385        */
386        KdLeaf *GetLeaf(const Vector3 &pt, KdNode *root = NULL) const;
387
388
389        ViewCellsTree *GetViewCellsTree() const { return mViewCellsTree; }
390
391        void SetViewCellsTree(ViewCellsTree *vt) { mViewCellsTree = vt; }
392
393        float EvalRenderCost(const VssRayContainer &myrays);
394        float EvalLeafCost(const OspTraversalData &tData);
395
396        /** Adds this objects to the kd leaf objects.
397                @warning: Can corrupt the tree
398        */
399        void InsertObjects(KdNode *node, const ObjectContainer &objects);
400
401        /** Add the leaf to the pvs of the view cell.
402        */
403        bool AddLeafToPvs(
404                KdLeaf *leaf,
405                ViewCell *vc,
406                const float pdf,
407                float &contribution);
408
409protected:
410
411        // --------------------------------------------------------------
412        // For sorting objects
413        // --------------------------------------------------------------
414        struct SortableEntry
415        {
416                /** There is a 3th "event" for rays which intersect a
417                        box in the middle. These "events" don't induce a change in
418                        pvs size, but may induce a change in view cell volume.
419                */
420                enum EType
421                {
422                        BOX_MIN,
423                        BOX_MAX,
424                        BOX_INTERSECT
425                };
426
427                int mType;
428                //int mPvs;
429                float mPos;
430                VssRay *mRay;
431               
432                Intersectable *mObject;
433
434                SortableEntry() {}
435
436                SortableEntry(const int type,
437                        //const float pvs,
438                        const float pos,
439                        Intersectable *obj,
440                        VssRay *ray):
441                mType(type),
442                //mPvs(pvs),
443                mPos(pos),
444                mObject(obj),
445                mRay(ray)
446                {}
447
448                bool operator<(const SortableEntry &b) const
449                {
450                        return mPos < b.mPos;
451                }
452        };
453
454 
455        /** faster evaluation of split plane cost for kd axis aligned cells.
456        */
457        float EvalLocalSplitCost(const OspTraversalData &data,
458                                                         const AxisAlignedBox3 &box,
459                                                         const int axis,
460                                                         const float &position,
461                                                         float &pFront,
462                                                         float &pBack) const;
463
464        /** Evaluates candidate for splitting.
465        */
466        void EvalSubdivisionCandidate(OspSubdivisionCandidate &splitData);
467
468        /** Computes priority of the traversal data and stores it in tData.
469        */
470        void EvalPriority(OspTraversalData &tData) const;
471
472        /** Evaluates render cost decrease of next split.
473        */
474        float EvalRenderCostDecrease(const AxisAlignedPlane &candidatePlane,
475                                                                 const OspTraversalData &data,
476                                                                 float &normalizedOldRenderCost) const;
477
478
479        /** Collects view cells in the subtree under root.
480        */
481        void CollectViewCells(KdNode *root,
482                                                  bool onlyValid,
483                                                  ViewCellContainer &viewCells,
484                                                  bool onlyUnmailed = false) const;
485
486        /** Evaluates tree stats in the BSP tree leafs.
487        */
488        void EvaluateLeafStats(const OspTraversalData &data);
489
490        /** Subdivides node using a best split priority queue.
491            @param tQueue the best split priority queue
492                @param splitCandidate the candidate for the next split
493                @param globalCriteriaMet if the global termination criteria were already met
494                @returns new root of the subtree
495        */
496        KdNode *Subdivide(SplitQueue &tQueue,
497                                          SubdivisionCandidate *splitCandidate,
498                                          const bool globalCriteriaMet);
499       
500        /** Subdivides leaf.
501                @param leaf the leaf to be subdivided
502               
503                @param polys the polygons to be split
504                @param frontPolys returns the polygons in front of the split plane
505                @param backPolys returns the polygons in the back of the split plane
506               
507                @param rays the polygons to be filtered
508                @param frontRays returns the polygons in front of the split plane
509                @param backRays returns the polygons in the back of the split plane
510
511                @returns the root of the subdivision
512        */
513        KdInterior *SubdivideNode(
514                const AxisAlignedPlane &splitPlane,
515                const OspTraversalData &tData,
516                OspTraversalData &frontData,
517                OspTraversalData &backData);
518
519        void SplitObjects(KdLeaf *leaf,
520                                          const AxisAlignedPlane & splitPlane,
521                                          const ObjectContainer &objects,
522                                          ObjectContainer &front,
523                                          ObjectContainer &back);
524
525        /** does some post processing on the objects in the new child leaves.
526        */
527        void ProcessMultipleRefs(KdLeaf *leaf) const;
528
529        /** Sorts split candidates for cost heuristics using axis aligned splits.
530                @param node the current node
531                @param axis the current split axis
532        */
533        void SortSubdivisionCandidates(const OspTraversalData &data,
534                                                         const int axis,
535                                                         float minBand,
536                                                         float maxBand);
537
538        /** Computes best cost for axis aligned planes.
539        */
540        float EvalLocalCostHeuristics(const OspTraversalData &tData,
541                const AxisAlignedBox3 &box,
542                const int axis,
543                float &position,
544                int &objectsFront,
545                int &objectsBack);
546
547        /** Subdivides the rays into front and back rays according to the split plane.
548               
549                @param plane the split plane
550                @param rays contains the rays to be split. The rays are
551                           distributed into front and back rays.
552                @param frontRays returns rays on the front side of the plane
553                @param backRays returns rays on the back side of the plane
554               
555                @returns the number of splits
556        */
557        int SplitRays(const AxisAlignedPlane &plane,
558                RayInfoContainer &rays,
559                RayInfoContainer &frontRays,
560                RayInfoContainer &backRays) const;
561
562        int FilterRays(KdLeaf *leaf, const RayInfoContainer &rays, RayInfoContainer &filteredRays);
563
564        /** Adds the object to the pvs of the front and back leaf with a given classification.
565
566                @param obj the object to be added
567                @param cf the ray classification regarding the split plane
568                @param frontPvs returns the PVS of the front partition
569                @param backPvs returns the PVS of the back partition
570       
571        */
572        void UpdateObjPvsContri(Intersectable *obj,
573                                         const int cf,
574                                         float &frontPvs,
575                                         float &backPvs,
576                                         float &totalPvs) const;
577       
578        /** Returns true if tree can be terminated.
579        */
580        bool LocalTerminationCriteriaMet(const OspTraversalData &data) const;
581
582        /** Returns true if global tree can be terminated.
583        */
584        bool GlobalTerminationCriteriaMet(const OspTraversalData &data) const;
585
586        /** Selects an axis aligned for the next split.
587                @returns cost for this split
588        */
589        float SelectSplitPlane(
590                const OspTraversalData &tData,
591                AxisAlignedPlane &plane);
592       
593        /** Propagates valid flag up the tree.
594        */
595        void PropagateUpValidity(KdNode *node);
596
597        /** Writes the node to disk
598                @note: should be implemented as visitor.
599        */
600        void ExportNode(KdNode *node, OUT_STREAM &stream);
601
602        /** Returns estimated memory usage of tree.
603        */
604        float GetMemUsage() const;
605
606        /** Evaluate the contributions of view cell volume of the left and the right view cell.
607        */
608        void EvalRayContribution(
609                KdLeaf *leaf,
610                const VssRay &ray,
611                float &renderCost);
612       
613        void EvalViewCellContribution(
614                KdLeaf *leaf,
615                ViewCell *viewCell,
616                float &renderCost);
617
618        /** Evaluates the influence on the pvs of the event.
619                @param ve the visibility event
620                @param pvsLeft updates the left pvs
621                @param rightPvs updates the right pvs
622        */
623        void EvalHeuristicsContribution(KdLeaf *leaf,
624                const SortableEntry &ci,
625                float &renderCost,
626                ViewCellContainer &touchedViewCells);
627
628        /** Prepares objects for the cost heuristics.
629                @returns pvs size of the node
630        */
631        float PrepareHeuristics(
632                const OspTraversalData &tData,
633                ViewCellContainer &touchedViewCells);
634
635        /** Prepares heuristics for a particular ray.
636        */
637        void PrepareHeuristics(
638                const VssRay &ray,
639                ViewCellContainer &touchedViewCells);
640
641        /** Prepares construction for vsp and osp trees.
642        */
643        void ComputeBoundingBox(const ObjectContainer &objects);
644
645        void CollectDirtyCandidates(OspSubdivisionCandidate *sc,
646                vector<SubdivisionCandidate *> &dirtyList);
647
648        /** Collect view cells which see this kd leaf.
649        */
650        void CollectViewCells(KdLeaf *leaf,
651                ViewCellContainer &viewCells);
652
653        /** Rays will be clipped to the bounding box.
654        */
655        void PreprocessRays(
656                KdLeaf *root,
657                const VssRayContainer &sampleRays,
658                RayInfoContainer &rays);
659
660        /** Reads parameters from environment singleton.
661        */
662        void ReadEnvironment();
663
664        /** Returns true if the specified ray end points is inside the kd leaf.
665                @param isTermination if origin or termination point should be checked
666        */
667        bool EndPointInsideNode(KdLeaf *leaf, VssRay &ray, bool isTermination) const;
668
669        void AddViewCellVolumeContri(
670                ViewCell *vc,
671                float &frontVol,
672                float &backVol,
673                float &frontAndBackVol) const;
674
675        /** Classifies and mail view cell with respect to the heuristics contribution.
676                Set view cell mail box according to it's influence on
677                front (0), back (1) and front / back node (2).
678        */
679        void  MailViewCell(ViewCell *vc, const int cf) const;
680
681        int ClassifyRay(VssRay *ray, KdLeaf *leaf, const AxisAlignedPlane &plane) const;
682
683        void EvalSubdivisionStats(const SubdivisionCandidate &tData);
684
685        void AddSubdivisionStats(const int viewCells,
686                const float renderCostDecr,
687                const float totalRenderCost);
688
689        void EvalViewCellsForHeuristics(const VssRay &ray,
690                float &volLeft,
691                float &volRight);
692
693        float EvalViewCellsVolume(KdLeaf *leaf, const RayInfoContainer &rays) const;
694       
695        int RemoveParentViewCellsPvs(KdLeaf *leaf,
696                                                                          const RayInfoContainer &rays
697                                                                          ) const;
698
699        int UpdateViewCellsPvs(KdLeaf *leaf, const RayInfoContainer &rays) const;
700int CheckViewCellsPvs(KdLeaf *leaf,
701                                                           const RayInfoContainer &rays) const;
702        bool AddViewCellToObjectPvs(
703                Intersectable *obj,
704                ViewCell *vc,
705                float &contribution,
706                bool onlyMailed) const;
707
708        int ClassifyRays(
709                const RayInfoContainer &rays,
710                KdLeaf *leaf,
711                const AxisAlignedPlane &plane,
712                RayInfoContainer &frontRays,
713                RayInfoContainer &backRays) const;
714
715        void CollectTouchedViewCells(
716                const RayInfoContainer &rays,
717                ViewCellContainer &touchedViewCells) const;
718
719        void AddObjectContribution(
720                KdLeaf *leaf,
721                Intersectable * obj,
722                ViewCellContainer &touchedViewCells,
723                float &renderCost);
724
725        void SubtractObjectContribution(
726                KdLeaf *leaf,
727                Intersectable * obj,
728                ViewCellContainer &touchedViewCells,
729                float &renderCost);
730
731        SubdivisionCandidate *PrepareConstruction(
732                const VssRayContainer &sampleRays,
733                const ObjectContainer &objects,
734                RayInfoContainer &rays);
735
736
737protected:
738       
739        /// pointer to the hierarchy of view cells
740        ViewCellsTree *mViewCellsTree;
741
742        /// pointer to the view space partition
743        /// note: should be handled over the hierarchy manager
744        VspTree *mVspTree;
745
746        /// pointer to the hierarchy manager
747        HierarchyManager *mHierarchyManager;
748
749        /// The view cells manager
750        ViewCellsManager *mViewCellsManager;
751
752        /// candidates for placing split planes during cost heuristics
753        vector<SortableEntry> *mSubdivisionCandidates;
754
755        /// Pointer to the root of the tree
756        KdNode *mRoot;
757               
758        /// Statistics for the object space partition
759        OspTreeStatistics mOspStats;
760       
761        /// box around the whole view domain
762        AxisAlignedBox3 mBoundingBox;
763
764
765        //////////////////////////////
766        //-- local termination
767
768        /// maximal possible depth
769        int mTermMaxDepth;
770        /// mininum probability
771        float mTermMinProbability;
772        /// minimal number of objects
773        int mTermMinObjects;
774        /// maximal contribution per ray
775        float mTermMaxRayContribution;
776        /// maximal acceptable cost ratio
777        float mTermMaxCostRatio;
778        /// tolerance value indicating how often the max cost ratio can be failed
779        int mTermMissTolerance;
780
781
782        ////////////////////////
783        //-- global criteria
784
785        float mTermMinGlobalCostRatio;
786        int mTermGlobalCostMissTolerance;
787        int mGlobalCostMisses;
788
789        /// maximal number of view cells
790        int mTermMaxLeaves;
791        /// maximal tree memory
792        float mMaxMemory;
793        /// the tree is out of memory
794        bool mOutOfMemory;
795
796
797        ///////////////////////////////////////////
798        //-- split heuristics based parameters
799       
800        bool mUseCostHeuristics;
801        /// balancing factor for PVS criterium
802        float mCtDivCi;
803        /// if only driving axis should be used for split
804        bool mOnlyDrivingAxis;
805        /// represents min and max band for sweep
806        float mSplitBorder;
807
808        ////////////////////////////////////////////////
809
810        /// current time stamp (used for keeping split history)
811        int mTimeStamp;
812        // if rays should be stored in leaves
813        bool mStoreRays;
814        /// epsilon for geometric comparisons
815        float mEpsilon;
816        /// subdivision stats output file
817        ofstream  mSubdivisionStats;
818        /// keeps track of cost during subdivision
819        float mTotalCost;
820        /// keeps track of overall pvs size during subdivision
821        int mTotalPvsSize;
822        /// number of currenly generated view cells
823        int mCreatedLeaves;
824       
825        /// weight between  render cost decrease and node render cost
826        float mRenderCostDecreaseWeight;
827
828    /// stores the kd node intersectables used for pvs
829        KdIntersectableMap mKdIntersectables;
830       
831private:
832
833        bool mCopyFromKdTree;
834};
835
836
837}
838
839#endif
Note: See TracBrowser for help on using the repository browser.