source: trunk/VUT/GtpVisibilityPreprocessor/src/VspBspTree.h @ 492

Revision 492, 21.3 KB checked in by bittner, 18 years ago (diff)

Large merge - viewcells seem not functional now

Line 
1#ifndef _VspBspTree_H__
2#define _VspBspTree_H__
3
4#include "Mesh.h"
5#include "Containers.h"
6#include "Polygon3.h"
7#include <stack>
8#include "Statistics.h"
9#include "VssRay.h"
10#include "RayInfo.h"
11#include "ViewCellBsp.h"
12
13class ViewCell;
14//class BspViewCell;
15class Plane3;
16class VspBspTree; 
17class BspInterior;
18class BspNode;
19class AxisAlignedBox3;
20class Ray;
21class ViewCellsStatistics;
22class ViewCellsManager;
23class BspMergeCandidate;
24struct BspRay;
25
26
27/**
28        This is a view space partitioning specialised BSPtree. 
29        There are no polygon splits, but we split the sample rays.
30        The candidates for the next split plane are evaluated only
31        by checking the sampled visibility information.
32        The polygons are employed merely as candidates for the next split planes.
33*/
34class VspBspTree
35{
36public:
37       
38        /** Additional data which is passed down the BSP tree during traversal.
39        */
40        struct VspBspTraversalData
41        { 
42                /// the current node
43                BspNode *mNode;
44                /// polygonal data for splitting
45                PolygonContainer *mPolygons;
46                /// current depth
47                int mDepth;
48                /// rays piercing this node
49                RayInfoContainer *mRays;
50                /// area of current node
51                float mArea;
52                /// geometry of node as induced by planes
53                BspNodeGeometry *mGeometry;
54                /// pvs size
55                int mPvs;
56                /// how often this branch has missed the max-cost ratio
57                int mMaxCostMisses;
58
59
60                /** Returns average ray contribution.
61                */
62                float GetAvgRayContribution() const
63                {
64                        return (float)mPvs / ((float)mRays->size() + Limits::Small);
65                }
66
67
68                VspBspTraversalData():
69                mNode(NULL),
70                mPolygons(NULL),
71                mDepth(0),
72                mRays(NULL),
73                mPvs(0),
74                mArea(0.0),
75                mGeometry(NULL),
76                mMaxCostMisses(0)
77                {}
78               
79                VspBspTraversalData(BspNode *node,
80                                                        PolygonContainer *polys,
81                                                        const int depth,
82                                                        RayInfoContainer *rays,
83                                                        int pvs,
84                                                        float area,
85                                                        BspNodeGeometry *geom):
86                mNode(node),
87                mPolygons(polys),
88                mDepth(depth),
89                mRays(rays),
90                mPvs(pvs),
91                mArea(area),
92                mGeometry(geom),
93                mMaxCostMisses(0)
94                {}
95
96                VspBspTraversalData(PolygonContainer *polys,
97                                                        const int depth,
98                                                        RayInfoContainer *rays,
99                                                        BspNodeGeometry *geom):
100                mNode(NULL),
101                mPolygons(polys),
102                mDepth(depth),
103                mRays(rays),
104                mPvs(0),
105                mArea(0),
106                mGeometry(geom),
107                mMaxCostMisses(0)
108                {}
109
110                /** Returns cost of the traversal data.
111                */
112                float GetCost() const
113                {
114#if 1
115                        return mPvs * mArea;
116#endif
117#if 0
118                        return (float)(mPvs * (int)mRays->size());
119#endif
120#if 0
121                        return (float)mPvs;
122#endif
123#if 0
124                        return mArea * (float)mRays->size();
125#endif
126                }
127
128                // deletes contents and sets them to NULL
129                void Clear()
130                {
131                        DEL_PTR(mPolygons);
132                        DEL_PTR(mRays);
133                        DEL_PTR(mGeometry);
134                }
135
136                friend bool operator<(const VspBspTraversalData &a, const VspBspTraversalData &b)
137                {
138                        return a.GetCost() < b.GetCost();
139                }
140    };
141       
142        typedef std::priority_queue<VspBspTraversalData> VspBspTraversalStack;
143        //typedef std::stack<VspBspTraversalData> VspBspTraversalStack;
144
145        /** Default constructor creating an empty tree.
146        */
147        VspBspTree();
148
149        /** Default destructor.
150        */
151        ~VspBspTree();
152
153        /** Returns BSP Tree statistics.
154        */
155        const BspTreeStatistics &GetStatistics() const;
156 
157
158        /** Constructs the tree from a given set of rays.
159                @param sampleRays the set of sample rays the construction is based on
160                @param viewCells if not NULL, new view cells are
161                created in the leafs and stored in the container
162        */
163        void Construct(const VssRayContainer &sampleRays,
164                                   AxisAlignedBox3 *forcedBoundingBox);
165
166        /** Returns list of BSP leaves.
167        */
168        void CollectLeaves(vector<BspLeaf *> &leaves) const;
169
170        /** Returns box which bounds the whole tree.
171        */
172        AxisAlignedBox3 GetBoundingBox()const;
173
174        /** Returns root of BSP tree.
175        */
176        BspNode *GetRoot() const;
177
178        /** Exports VspBsp tree to file.
179        */
180        bool Export(const string filename);
181
182        /** Collects the leaf view cells of the tree
183                @param viewCells returns the view cells
184        */
185        void CollectViewCells(ViewCellContainer &viewCells) const;
186
187        /** A ray is cast possible intersecting the tree.
188                @param the ray that is cast.
189                @returns the number of intersections with objects stored in the tree.
190        */
191        int CastRay(Ray &ray);
192
193        /// bsp tree construction types
194        enum {FROM_INPUT_VIEW_CELLS, FROM_SCENE_GEOMETRY, FROM_SAMPLES};
195
196        /** finds neighbouring leaves of this tree node.
197        */
198        int FindNeighbors(BspNode *n,
199                                          vector<BspLeaf *> &neighbors,
200                                          const bool onlyUnmailed) const;
201
202        /** Constructs geometry associated with the half space intersections
203                leading to this node.
204        */
205        void ConstructGeometry(BspNode *n, PolygonContainer &cell) const;
206
207        /** Constructs geometry associated with the half space intersections
208                leading to this node.
209        */
210        void ConstructGeometry(BspViewCell *vc, PolygonContainer &cell) const;
211
212        /** Construct geometry and stores it in a geometry node container.
213        */
214        void ConstructGeometry(BspNode *n, BspNodeGeometry &cell) const;
215
216        /** Returns random leaf of BSP tree.
217                @param halfspace defines the halfspace from which the leaf is taken.
218        */
219        BspLeaf *GetRandomLeaf(const Plane3 &halfspace);
220
221        /** Returns random leaf of BSP tree.
222                @param onlyUnmailed if only unmailed leaves should be returned.
223        */
224        BspLeaf *GetRandomLeaf(const bool onlyUnmailed = false);
225
226        /** Returns epsilon of this tree.
227        */
228        float GetEpsilon() const;
229
230        /** Casts line segment into the tree.
231                @param origin the origin of the line segment
232                @param termination the end point of the line segment
233                @returns view cells intersecting the line segment.
234        */
235    int CastLineSegment(const Vector3 &origin,
236                                                const Vector3 &termination,
237                                                ViewCellContainer &viewcells);
238
239               
240        /** Sets pointer to view cells manager.
241        */
242        void SetViewCellsManager(ViewCellsManager *vcm);
243
244        /** Returns distance from node 1 to node 2.
245        */
246        int TreeDistance(BspNode *n1, BspNode *n2) const;
247
248        /** Merges view cells according to some cost heuristics.
249        */
250        int MergeViewCells(const VssRayContainer &rays);
251       
252        /** Refines view cells using shuffling, i.e., border leaves
253                of two view cells are exchanged if the resulting view cells
254                are tested to be "better" than the old ones.
255                @returns number of refined view cells
256        */
257        int RefineViewCells(const VssRayContainer &rays);
258
259        /** Collapses the tree with respect to the view cell partition,
260                i.e. leaves having the same view cell are collapsed.
261                @returns node of type leaf if the node could be collapsed,
262                this node otherwise
263        */
264        BspNode *CollapseTree(BspNode *node);
265
266  ViewCell *
267  GetViewCell(const Vector3 &point);
268
269        /** Constructs bsp rays for post processing and visualization.
270        */
271        void ConstructBspRays(vector<BspRay *> &bspRays,
272                                                  const VssRayContainer &rays);
273       
274        /** Merge view cells of leaves l1 and l2.
275        */
276        bool MergeViewCells(BspLeaf *l1, BspLeaf *l2) const;
277
278        /** Returns true if this view point is in a valid view space,
279                false otherwise.
280        */
281        bool ViewPointValid(const Vector3 &viewPoint) const;
282
283        /** Returns the view cell corresponding to
284                the space outside the valid view space.
285        */
286        BspViewCell *GetOutOfBoundsCell() const;
287
288protected:
289
290        // --------------------------------------------------------------
291        // For sorting objects
292        // --------------------------------------------------------------
293        struct SortableEntry
294        {
295                enum EType
296                {
297                        ERayMin,
298                        ERayMax
299                };
300
301                int type;
302                float value;
303                VssRay *ray;
304 
305                SortableEntry() {}
306                SortableEntry(const int t, const float v, VssRay *r):type(t),
307                                          value(v), ray(r)
308                {
309                }
310               
311                friend bool operator<(const SortableEntry &a, const SortableEntry &b)
312                {
313                        return a.value < b.value;
314                }
315        };
316
317        /** Shuffles the leaves, i.e., tests if exchanging
318                the leaves helps in improving the view cells.
319        */
320        bool ShuffleLeaves(BspLeaf *leaf1, BspLeaf *leaf2) const;
321
322        /** Helper function revalidating the view cell leaf list after merge.
323        */
324        void RepairVcLeafLists();
325
326        /** Evaluates tree stats in the BSP tree leafs.
327        */
328        void EvaluateLeafStats(const VspBspTraversalData &data);
329
330        /** Subdivides node with respect to the traversal data.
331            @param tStack current traversal stack
332                @param tData traversal data also holding node to be subdivided
333                @returns new root of the subtree
334        */
335        BspNode *Subdivide(VspBspTraversalStack &tStack,
336                                           VspBspTraversalData &tData);
337
338        /** Constructs the tree from the given traversal data.
339                @param polys stores set of polygons on which subdivision may be based
340                @param rays storesset of rays on which subdivision may be based
341        */
342        void Construct(const PolygonContainer &polys, RayInfoContainer *rays);
343
344        /** Selects the best possible splitting plane.
345                @param plane returns the split plane
346                @param leaf the leaf to be split
347                @param polys the polygon list on which the split decition is based
348                @param rays ray container on which selection may be based
349                @note the polygons can be reordered in the process
350                @returns true if the cost of the split is under maxCostRatio
351
352        */
353        bool SelectPlane(Plane3 &plane,
354                                         BspLeaf *leaf,
355                                         VspBspTraversalData &data);
356       
357        /** Strategies where the effect of the split plane is tested
358            on all input rays.
359
360                @returns the cost of the candidate split plane
361        */
362        float SplitPlaneCost(const Plane3 &candidatePlane,
363                                                 const VspBspTraversalData &data) const;
364
365
366        /** Subdivide leaf.
367                @param leaf the leaf to be subdivided
368               
369                @param polys the polygons to be split
370                @param frontPolys returns the polygons in front of the split plane
371                @param backPolys returns the polygons in the back of the split plane
372               
373                @param rays the polygons to be filtered
374                @param frontRays returns the polygons in front of the split plane
375                @param backRays returns the polygons in the back of the split plane
376
377                @returns the root of the subdivision
378        */
379
380        BspNode *SubdivideNode(VspBspTraversalData &tData,
381                                                   VspBspTraversalData &frontData,
382                                                   VspBspTraversalData &backData,
383                                                   PolygonContainer &coincident);
384
385        /** Selects the split plane in order to construct a tree with
386                certain characteristics (e.g., balanced tree, least splits,
387                2.5d aligned)
388                @param bestPlane returns the split plane
389                @param polygons container of polygons
390                @param rays bundle of rays on which the split can be based
391
392                @returns true if the overall cost is under maxCostRatio
393        */
394        bool SelectPlaneHeuristics(Plane3 &bestPlane,
395                                                           BspLeaf *leaf,
396                                                           VspBspTraversalData &data);
397
398        /** Extracts the meshes of the objects and adds them to polygons.
399                Adds object aabb to the aabb of the tree.
400                @param maxPolys the maximal number of objects to be stored as polygons
401                @returns the number of polygons
402        */
403        int AddToPolygonSoup(const ObjectContainer &objects,
404                                                 PolygonContainer &polys,
405                                                 int maxObjects = 0);
406
407        /** Extracts the meshes of the view cells and and adds them to polygons.
408                Adds view cell aabb to the aabb of the tree.
409                @param maxPolys the maximal number of objects to be stored as polygons
410                @returns the number of polygons
411        */
412        int AddToPolygonSoup(const ViewCellContainer &viewCells,
413                                                 PolygonContainer &polys,
414                                                 int maxObjects = 0);
415
416        /** Extract polygons of this mesh and add to polygon container.
417                @param mesh the mesh that drives the polygon construction
418                @param parent the parent intersectable this polygon is constructed from
419                @returns number of polygons
420        */
421        int AddMeshToPolygons(Mesh *mesh, PolygonContainer &polys, MeshInstance *parent);
422
423        /** Selects a plane axis aligned.
424        */
425        float SelectAxisAlignedPlane(Plane3 &plane,
426                                                                 const VspBspTraversalData &tData,
427                                                                 int &bestAxis);
428
429        /** Sorts split candidates for surface area heuristics for axis aligned splits.
430                @param polys the input for choosing split candidates
431                @param axis the current split axis
432                @param splitCandidates returns sorted list of split candidates
433        */
434        void SortSplitCandidates(const RayInfoContainer &rays, const int axis);
435
436        /** Computes best cost for axis aligned planes.
437        */
438        float BestCostRatioHeuristics(const RayInfoContainer &rays,
439                                                                  const AxisAlignedBox3 &box,
440                                                                  const int pvsSize,
441                                                                  const int &axis,
442                                                                  float &position);
443
444        /** Evaluates cost ratio for axis aligned splits.
445        */
446        float EvalCostRatio(const VspBspTraversalData &tData,
447                                                const AxisAlignedBox3 &box,
448                                                const int axis,
449                                                const float position,
450                                                int &raysBack,
451                                                int &raysFront,
452                                                int &pvsBack,
453                                                int &pvsFront);
454
455        /** Selects an axis aligned split plane.
456                @Returns true if split is valied
457        */
458        bool SelectAxisAlignedPlane(Plane3 &plane, const PolygonContainer &polys) const;
459
460        /** Subdivides the rays into front and back rays according to the split plane.
461               
462                @param plane the split plane
463                @param rays contains the rays to be split. The rays are
464                           distributed into front and back rays.
465                @param frontRays returns rays on the front side of the plane
466                @param backRays returns rays on the back side of the plane
467               
468                @returns the number of splits
469        */
470        int SplitRays(const Plane3 &plane,
471                                  RayInfoContainer &rays,
472                              RayInfoContainer &frontRays,
473                                  RayInfoContainer &backRays);
474
475
476        /** Extracts the split planes representing the space bounded by node n.
477        */
478        void ExtractHalfSpaces(BspNode *n, vector<Plane3> &halfSpaces) const;
479
480        /** Adds the object to the pvs of the front and back leaf with a given classification.
481
482                @param obj the object to be added
483                @param cf the ray classification regarding the split plane
484                @param frontPvs returns the PVS of the front partition
485                @param backPvs returns the PVS of the back partition
486       
487        */
488        void AddObjToPvs(Intersectable *obj, const int cf, int &frontPvs, int &backPvs) const;
489       
490        /** Computes PVS size induced by the rays.
491        */
492        int ComputePvsSize(const RayInfoContainer &rays) const;
493
494        /** Returns true if tree can be terminated.
495        */
496        inline bool TerminationCriteriaMet(const VspBspTraversalData &data) const;
497
498        /** Computes accumulated ray lenght of this rays.
499        */
500        float AccumulatedRayLength(const RayInfoContainer &rays) const;
501
502        /** Splits polygons with respect to the split plane.
503
504                @param plane the split plane
505                @param polys the polygons to be split. the polygons are consumed and
506                           distributed to the containers frontPolys, backPolys, coincident.
507                @param frontPolys returns the polygons in the front of the split plane
508                @param backPolys returns the polygons in the back of the split plane
509                @param coincident returns the polygons coincident to the split plane
510
511                @returns the number of splits   
512        */
513        int SplitPolygons(const Plane3 &plane,
514                                          PolygonContainer &polys,
515                                          PolygonContainer &frontPolys,
516                                          PolygonContainer &backPolys,
517                                          PolygonContainer &coincident) const;
518
519        /** Adds ray sample contributions to the PVS.
520                @param sampleContributions the number contributions of the samples
521                @param contributingSampels the number of contributing rays
522               
523        */
524        void AddToPvs(BspLeaf *leaf,
525                                  const RayInfoContainer &rays,
526                                  int &sampleContributions,
527                                  int &contributingSamples);
528
529
530        /** Collects candidates for the merge in the merge queue.
531                @returns number of leaves in queue
532        */
533        int CollectMergeCandidates();
534        /** Collects candidates for the merge in the merge queue.
535                @returns number of leaves in queue
536        */
537        int CollectMergeCandidates(const VssRayContainer &rays);
538
539        /** Take 3 ray endpoints, where two are minimum and one a maximum
540                point or the other way round.
541        */
542        Plane3 ChooseCandidatePlane(const RayInfoContainer &rays) const;
543
544        /** Take plane normal as plane normal and the midpoint of the ray.
545                PROBLEM: does not resemble any point where visibility is
546                likely to change
547        */
548        Plane3 ChooseCandidatePlane2(const RayInfoContainer &rays) const;
549
550        /** Fit the plane between the two lines so that the plane
551                has equal shortest distance to both lines.
552        */
553        Plane3 ChooseCandidatePlane3(const RayInfoContainer &rays) const;
554 
555        /** Shuffles, i.e. takes border leaf from view cell 1 and adds it
556                to view cell 2.
557        */
558        void ShuffleLeaf(BspLeaf *leaf,
559                                         BspViewCell *vc1,
560                                         BspViewCell *vc2) const;
561
562        /**
563                Checks if this traversal data corresponds to
564                a valid view space region.
565        */
566        bool CheckValid(const VspBspTraversalData &data) const;
567
568        /** Propagates valid flag up the tree.
569        */
570        void PropagateUpValidity(BspNode *node);
571
572        /** Creates or returns view cell corresponding to
573                the space outside the valid view space.
574        */
575        BspViewCell *GetOrCreateOutOfBoundsCell();
576
577        /// Pointer to the root of the tree
578        BspNode *mRoot;
579               
580        BspTreeStatistics mStat;
581
582        /// Strategies for choosing next split plane.
583        enum {NO_STRATEGY = 0,
584                  RANDOM_POLYGON = 1,
585                  AXIS_ALIGNED = 2,
586                  LEAST_RAY_SPLITS = 256,
587                  BALANCED_RAYS = 512,
588                  PVS = 1024
589                };
590
591        /// box around the whole view domain
592        AxisAlignedBox3 mBox;
593
594        /// minimal number of rays before subdivision termination
595        int mTermMinRays;
596        /// maximal possible depth
597        int mTermMaxDepth;
598        /// mininum area
599        float mTermMinArea;
600        /// mininum PVS
601        int mTermMinPvs;
602
603        /// minimal number of rays for axis aligned split
604        int mTermMinRaysForAxisAligned;
605        /// minimal number of objects for axis aligned split
606        int mTermMinObjectsForAxisAligned;
607        /// maximal contribution per ray
608        float mTermMaxRayContribution;
609        /// minimal accumulated ray length
610        float mTermMinAccRayLength;
611
612        /// strategy to get the best split plane
613        int mSplitPlaneStrategy;
614        /// number of candidates evaluated for the next split plane
615        int mMaxPolyCandidates;
616        /// number of candidates for split planes evaluated using the rays
617        int mMaxRayCandidates;
618        /// balancing factor for PVS criterium
619        float mCtDivCi;
620
621        //-- axis aligned split criteria
622        float mAxisAlignedCtDivCi;
623        /// spezifies the split border of the axis aligned split
624        float mAxisAlignedSplitBorder;
625
626        /// maximal acceptable cost ratio
627        float mTermMaxCostRatio;
628        /// tolerance value indicating how often the max cost ratio can be failed
629        int mTermMissTolerance;
630
631        //-- factors guiding the split plane heuristics
632        float mLeastRaySplitsFactor;
633        float mBalancedRaysFactor;
634        float mPvsFactor;
635
636        /// if area or accumulated ray lenght should be used for PVS heuristics
637        bool mPvsUseArea;
638        /// tolerance for polygon split
639        float mEpsilon;
640        /// maximal number of test rays used to evaluate candidate split plane
641        int mMaxTests;
642        /// normalizes different bsp split plane criteria
643        float mCostNormalizer;
644        /// maximal number of view cells
645        int mMaxViewCells;
646        /// minimal number of view cells
647        int mMergeMinViewCells;
648        /// maximal cost ratio for the merge
649        float mMergeMaxCostRatio;
650
651        // if rays should be stored in leaves
652        bool mStoreRays;
653       
654        /// if only driving axis should be used for split
655        bool mOnlyDrivingAxis;
656
657        ViewCellsManager *mViewCellsManager;
658
659        vector<SortableEntry> *mSplitCandidates;
660
661
662        typedef priority_queue<BspMergeCandidate> MergeQueue;
663
664        MergeQueue mMergeQueue;
665        /// if rays should be used to collect merge candidates
666        bool mUseRaysForMerge;
667        /// maximal allowed pvs so that view cell is valid
668        int mMaxPvs;
669
670        /// View cell corresponding to the space outside the valid view space
671        BspViewCell *mOutOfBoundsCell;
672
673private:
674       
675        static const float sLeastRaySplitsTable[5];
676        /** Evaluates split plane classification with respect to the plane's
677                contribution for balanced rays.
678        */
679        static const float sBalancedRaysTable[5];
680
681        /// Generates unique ids for PVS criterium
682        static void GenerateUniqueIdsForPvs();
683
684        //-- unique ids for PVS criterium
685        static int sFrontId;
686        static int sBackId;
687        static int sFrontAndBackId;
688};
689
690/**
691        Candidate for leaf merging based on priority.
692*/
693class BspMergeCandidate
694
695public:
696
697        BspMergeCandidate(BspLeaf *l1, BspLeaf *l2);
698
699        /** If this merge pair is still valid.
700        */
701        bool Valid() const;
702
703        /** Sets this merge candidate to be valid.
704        */
705        void SetValid();
706
707        friend bool operator<(const BspMergeCandidate &leafa, const BspMergeCandidate &leafb)
708        {
709                return leafb.GetMergeCost() < leafa.GetMergeCost();
710        }
711
712        void SetLeaf1(BspLeaf *l);
713        void SetLeaf2(BspLeaf *l);
714
715        BspLeaf *GetLeaf1();
716        BspLeaf *GetLeaf2();
717
718        /** Merge cost of this candidate pair.
719        */
720        float GetMergeCost() const;
721
722        /** Returns cost of leaf 1.
723        */
724        float GetLeaf1Cost() const;
725        /** Returns cost of leaf 2.
726        */
727        float GetLeaf2Cost() const;
728
729        /// maximal pvs size
730        static int sMaxPvsSize;
731        /// overall cost used to normalize cost ratio
732        static float sOverallCost;
733
734protected:
735
736        /** Cost of a view cell.
737        */
738        float GetCost(ViewCell *vc) const;
739        /** Evaluates the merge costs of the leaves.
740        */
741        void EvalMergeCost();
742
743        int mLeaf1Id;
744        int mLeaf2Id;
745
746        float mMergeCost;
747       
748        BspLeaf *mLeaf1;
749        BspLeaf *mLeaf2;
750};
751
752
753class MergeStatistics: public StatisticsBase
754{
755public:
756       
757        int merged;
758        int siblings;
759        int candidates;
760        int nodes;
761
762        int accTreeDist;
763        int maxTreeDist;
764       
765        Real collectTime;
766        Real mergeTime;
767
768        // Constructor
769        MergeStatistics()
770        {
771                Reset();
772        }
773       
774        double AvgTreeDist() const {return (double)accTreeDist / (double)merged;};
775
776        void Reset()
777        {
778                nodes = 0;
779                merged = 0;
780                siblings = 0;
781                candidates = 0;
782       
783                accTreeDist = 0;
784                maxTreeDist = 0;
785
786                collectTime = 0;
787                mergeTime = 0;
788        }
789
790        void Print(ostream &app) const;
791
792        friend ostream &operator<<(ostream &s, const MergeStatistics &stat)
793        {
794                stat.Print(s);
795                return s;
796        }
797};
798
799#endif
Note: See TracBrowser for help on using the repository browser.