Changeset 2752 for GTP/trunk/App
- Timestamp:
- 06/10/08 15:48:44 (16 years ago)
- Location:
- GTP/trunk/App/Demos/Vis/CHC_revisited
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
GTP/trunk/App/Demos/Vis/CHC_revisited/Bvh.cpp
r2751 r2752 1 #if TOIMPLEMENT2 3 1 #include <queue> 4 2 #include <stack> 5 3 #include "Bvh.h" 6 #include "yare.h"7 #include "PerfTimer.h"8 #include "Camera.h"9 #include "Settings.h"10 #include "Context.h"11 #include "TriangleBvh.h"12 #include "NodeGeometry.h"13 #include "Viewer.h"14 4 15 5 #include <fstream> … … 18 8 19 9 20 21 10 namespace CHCDemo 22 11 { 12 13 #if TOIMPLEMENT 23 14 24 15 #define INVALID_TEST ((unsigned int)-1) … … 2593 2584 } 2594 2585 2595 }2596 2597 2586 #endif 2587 2588 } -
GTP/trunk/App/Demos/Vis/CHC_revisited/Bvh.h
r2751 r2752 4 4 #define __BVH_H 5 5 6 7 #if TOIMPLEMENT 8 9 #include "Bounds.h" 10 #include "PerfTimer.h" 11 #include "NV_RenderPredictorRenderAction.h" 12 #include "VisibilityPredictor.h" 13 #include "HierarchyNode.h" 14 #include "FlexibleHeap.h" 15 6 #include "Geometry.h" 7 //#include "FlexibleHeap.h" 16 8 17 9 18 10 namespace CHCDemo 19 11 { 20 21 12 22 13 //////// 23 14 // Forward declarations 24 15 25 class Scene; 26 class Node; 16 class SceneGeometry; 27 17 class Camera; 28 class Box;29 class BvhLeaf;30 class BvhNode;31 class TriangleBvh;32 33 18 34 19 35 20 /** A node in the bv hierarchy. 36 21 */ 37 class BvhNode : public RenderableHierarchyNode, public Heapable22 class BvhNode 38 23 { 39 24 friend class Bvh; … … 50 35 void Reset(); 51 36 52 53 37 /// if the node is visible 54 38 bool mIsVisible; 55 /// if all the leaves are visible56 bool mIsFullyVisible;57 58 /// #pixels this node was visible for the last test59 int mVisiblePixels;60 39 /// #frames this node is assumed to stay visible 61 40 int mAssumedVisibleFrames; 62 /// #frames this node is assumed to stay visible63 int mRemainingVisibleFrames;64 65 /// the frame in which the node was last tested visible66 int mLastTestedVisibleFrame;67 41 /// the frame when this node was last touched during traversal 68 42 int mLastVisitedFrame; 69 /// frame id when the node turned visible70 int mTurnedVisibleFrame;71 /// when was the node last tested72 int mLastTestedFrame;73 74 43 /// #times this node was invisible (only valid if the node actually is invisible) 75 44 int mTimesInvisible; 76 /// #times this node was tested77 int mTimesTested;78 /// #times this node was tested79 int mTimesChangedClassification;80 /// The ratio of classification changes / tests81 float mAvgChangedClassification;82 83 45 /// if the node is view frustum culled 84 46 bool mIsFrustumCulled; 85 47 /// if the node is newly processed with no prior history available 86 48 bool mIsNew; 87 49 }; … … 99 61 /** Returns unique id for this node. 100 62 */ 101 inline int GetId() {return mId;}63 //inline int GetId() {return mId;} 102 64 /** Depth of this node in the tree. 103 65 */ 104 inline int GetDepth() const {return (int)mDepth;}66 inline int GetDepth() const {return mDepth;} 105 67 /** Returns parent of this bvh node, NULL if it is root. 106 68 */ … … 109 71 */ 110 72 inline int GetNumLeaves() const {return mNumLeaves;} 111 /** Box used for visualization.112 */113 Box *GetOrCreateVizBox();114 73 /** Reset the status of this node. 115 74 */ 116 75 virtual void ResetVisibility(); 117 76 118 virtual int GetType() { return BVH_NODE; }77 virtual int GetType() = 0; //{ return BVH_NODE; } 119 78 120 79 … … 140 99 */ 141 100 inline void SetRemainingVisibleFrames(const int t); 142 /** See set.143 */144 inline int GetRemainingVisibleFrames() const;145 101 /** Decrease the time this node is considered visible. 146 102 */ 147 inline void DecRemainingVisibleFrames(); 148 /** Returns the frame id when this node was tested 149 visible. 150 */ 151 inline void SetLastTestedVisibleFrame(const int t); 152 /** See set. 153 */ 154 inline int GetLastTestedVisibleFrame() const; 103 inline void DecAssumedVisibleFrames(); 155 104 /** Increases the #times this node was 156 105 successfully tested invisible. 157 106 */ 158 107 inline void IncTimesInvisible(); 159 /** If the subtree induced by this node 160 is fully visible. 161 */ 162 inline bool IsFullyVisible(); 163 108 164 109 inline int GetTimesInvisible() const; 165 110 inline void SetTimesInvisible(const int t); … … 170 115 inline int GetLastTestedFrame(); 171 116 inline void SetLastTestedFrame(const int lastTested); 172 173 inline void SetVisiblePixels(const int pixels);174 inline int GetVisiblePixels();175 176 inline void IncTimesTested();177 inline void IncTimesChangedClassficiation();178 179 inline int GetTimesTested();180 inline int GetTimesChangedClassification();181 117 182 118 inline bool IsViewFrustumCulled() const; … … 196 132 */ 197 133 inline void SetLastRenderedFrame(int lastRenderedFrame); 198 199 200 134 /** Does this node contain renderable geometry? 201 135 */ … … 206 140 207 141 208 /////////////// 209 //-- public stuff 210 211 /// Visibility measurements for the period this node was visible 212 VisibilityMeasurementBuffer mMeasurements; 142 protected: 143 144 ///////////// 145 213 146 /// some flags 214 147 int mFlags; 215 216 //int mNumFailedTests;217 218 protected:219 220 /** Marks the subtree induced by this node221 as fully visible.222 */223 inline void SetFullyVisible(const bool visible);224 225 226 /////////////227 228 148 /// the depth of this node 229 149 unsigned char mDepth; … … 232 152 /// the parent node 233 153 BvhNode *mParent; 234 235 154 /// stores the visibility related parameters 236 155 VisibilityInfo mVisibility; … … 252 171 253 172 // Indices to first and last triangle in the triangle array 254 // assumes the tr aingle are placed in continuous chunk of memory173 // assumes the triangle are placed in continuous chunk of memory 255 174 // however this need not be a global array! 256 175 … … 264 183 int mNumTestNodes; 265 184 int mIndicesPtr; 266 267 185 /// Area of this node 268 186 float mArea; … … 297 215 298 216 299 void BvhNode::SetLastTestedVisibleFrame(const int t)300 {301 mVisibility.mLastTestedVisibleFrame = t;302 }303 304 305 int BvhNode::GetLastTestedVisibleFrame() const306 {307 return mVisibility.mLastTestedVisibleFrame;308 }309 310 311 217 void BvhNode::IncTimesInvisible() 312 218 { … … 315 221 316 222 317 bool BvhNode::IsFullyVisible()318 {319 return mVisibility.mIsFullyVisible;320 }321 322 323 223 int BvhNode::GetTimesInvisible() const 324 224 { … … 333 233 334 234 335 int BvhNode::GetTurnedVisibleFrame() const336 {337 return mVisibility.mTurnedVisibleFrame;338 }339 340 341 void BvhNode::SetTurnedVisibleFrame(const int turnedVisibleFrame)342 {343 mVisibility.mTurnedVisibleFrame = turnedVisibleFrame;344 }345 346 347 int BvhNode::GetLastTestedFrame()348 {349 return mVisibility.mLastTestedFrame;350 }351 352 353 void BvhNode::SetLastTestedFrame(const int lastTested)354 {355 mVisibility.mLastTestedFrame = lastTested;356 }357 358 359 void BvhNode::SetVisiblePixels(const int pixels)360 {361 mVisibility.mVisiblePixels = pixels;362 }363 364 365 int BvhNode::GetVisiblePixels()366 {367 return mVisibility.mVisiblePixels;368 }369 370 371 void BvhNode::IncTimesTested()372 {373 ++ mVisibility.mTimesTested;374 }375 376 377 void BvhNode::IncTimesChangedClassficiation()378 {379 ++ mVisibility.mTimesChangedClassification;380 }381 382 383 int BvhNode::GetTimesTested()384 {385 return mVisibility.mTimesTested;386 }387 388 389 int BvhNode::GetTimesChangedClassification()390 {391 return mVisibility.mTimesChangedClassification;392 }393 394 395 235 bool BvhNode::IsViewFrustumCulled() const 396 236 { … … 452 292 } 453 293 454 455 void BvhNode::SetRemainingVisibleFrames(const int t) 456 { 457 mVisibility.mRemainingVisibleFrames = t; 458 } 459 460 461 int BvhNode::GetRemainingVisibleFrames() const 462 { 463 return mVisibility.mRemainingVisibleFrames; 464 } 465 466 467 void BvhNode::DecRemainingVisibleFrames() 468 { 469 -- mVisibility.mRemainingVisibleFrames; 470 } 471 294 void BvhNode::DecAssumedVisibleFrames() 295 { 296 -- mVisibility.mAssumedVisibleFrames; 297 } 472 298 473 299 … … 515 341 public: 516 342 517 BvhLeaf(BvhNode *parent): BvhNode(parent) , mTriangleBvh(NULL)343 BvhLeaf(BvhNode *parent): BvhNode(parent) 518 344 {} 519 345 … … 521 347 522 348 virtual bool IsLeaf() { return true; } 523 };524 525 526 struct NodeGeometry527 {528 virtual AxisAlignedBox3 GetBoundingBox() = 0;529 };530 531 532 struct ObjectGeometry533 {534 virtual AxisAlignedBox3 GetBoundingBox() = 0;535 };536 537 538 struct TriangleGeometry539 {540 virtual AxisAlignedBox3 GetBoundingBox() = 0;541 349 }; 542 350 … … 608 416 /** Returns geometry by reference (faster). 609 417 */ 610 NodeGeometry **GetGeometry(BvhNode *node, int &size);418 SceneGeometry **GetGeometry(BvhNode *node, int &size); 611 419 612 420 … … 636 444 /** sets frame dependent values. 637 445 */ 638 void InitFrame(Camera *camera, const int currentFrameId , Viewer *viewer);639 /** this gives the orthogonal distance from the viewpoint to the nearest BBox-Vertex446 void InitFrame(Camera *camera, const int currentFrameId); 447 /** this gives the orthogonal distance from the viewpoint to the nearest bounding box vertex 640 448 note that negative values can appear because culling is done only afterwards 641 449 */ … … 645 453 */ 646 454 void SetMaxDepthForTestingChildren(const int maxDepth); 647 /** Resize the visibility buffers of the bvh nodes.648 */649 void ResizeVisibilityBuffers(const int size);650 651 652 455 /** Pulls up the fully visible classification in the bvh. 653 456 */ … … 681 484 void SetAreaRatioThresholdForTestingChildren(const float ratio); 682 485 683 void SetVolRatioThresholdForTestingChildren(const float ratio);684 /** Returns depth of bvh hierarchy.685 */686 float GetAvgDepth() const;687 688 486 689 487 const BvhStats &GetBvhStats() const {return mBvhStats;} … … 691 489 void SetCollectTighterBoundsWithMaxLevel(bool t); 692 490 693 694 /////////////695 //-- timers696 697 mutable PerfTimer timeSetup;698 mutable PerfTimer timeIssueDrawElements;699 mutable PerfTimer timeViewFrustumCulling;700 mutable PerfTimer timeDistance;701 491 702 492 ////////////// … … 719 509 //////////////////// 720 510 721 /** Constructor taking the geometry and a pointer to the current render action. 722 */ 723 Bvh(const GeometryVector &geometry, 724 DistanceSortedRenderAction *const renderer); 511 /** Constructor loading the bvh from disc 512 */ 513 Bvh(const std::string &filename); 725 514 /** protected constructor: do nothing. 726 515 */ 727 Bvh(): mCamera(NULL), mFrameId(-1), mVertices(NULL), mRenderer(NULL) {}516 //Bvh(): mCamera(NULL), mFrameId(-1), mVertices(NULL), mRenderer(NULL) {} 728 517 /** Destructor. 729 518 */ … … 731 520 732 521 733 734 //-- sorting functions735 736 /** The method of subdividing objects into halves in some axis using spatial median737 */738 int SortTrianglesSpatialMedian(BvhLeaf *leaf, const int axis);739 /** The method of subdividing objects into halves in some axis740 using object median.741 */742 int SortTrianglesObjectMedian(BvhLeaf *leaf, const int axis, float &pos);743 /** sort triangles along the axis with respect to the splitting plane744 given by axis/position return index of the first tiangles belong745 to the front of the splitting plane.746 */747 int SortTriangles(BvhLeaf *leaf, const int axis, const float position);748 /** sort triangles by their area.749 */750 int SortTrianglesSurfaceArea(BvhLeaf *leaf, const float sa);751 752 753 522 ///////////// 754 755 756 /** Subdivides the current leaf.757 */758 BvhNode *SubdivideLeaf(BvhLeaf *leaf, const int parentAxis);759 /** select splitting plane using SAH - returns the position of the splitting plane.760 */761 float SelectPlaneSah(BvhLeaf *leaf, int &axis, float &minCost);762 /** evaluate the SAH cost of a particulkar splitting plane763 */764 float EvaluateSahCost(BvhLeaf *leaf, const int axis, const float position);765 523 766 524 void ComputeBvhStats(); 767 525 void PrintBvhStats() const; 768 526 769 /** Update children nodes recursively.770 */771 void UpdateBoxes(BvhNode *node);772 /** Update box for a leaf node773 */774 void UpdateLeafBox(BvhLeaf *leaf);775 /** Returns true if termination criteria met.776 */777 bool TerminationCriteriaMet(BvhLeaf *leaf) const;778 /** Compute unique ids for the bvh nodes.779 */780 void ComputeIds();781 /** Helper method that updates the number of leaves in the subtree under782 this node.783 */784 void UpdateNumLeaves(BvhNode *node) const;785 786 527 787 528 ////////// … … 791 532 792 533 int PrepareBoundingBoxesWithDrawArrays(const BvhNodeContainer &nodes); 793 void RenderBoundingBoxesWithDrawArrays( constint numNodes);534 void RenderBoundingBoxesWithDrawArrays(int numNodes); 794 535 795 536 int RenderBoundingBoxesImmediate(const BvhNodeContainer &nodes); … … 797 538 and restarts the strip only if wished. 798 539 */ 799 void RenderBoundingBoxImmediate(const BoundingBox &box, constbool restartStrip);540 void RenderBoundingBoxImmediate(const AxisAlignedBox3 &box, bool restartStrip); 800 541 /** Create the indices that each node needs to use vbo rendering. 801 542 */ … … 818 559 int PostProcessLeaves(BvhLeafContainer &leaves); 819 560 820 bool CreateTighterBoundsForLeaf(BvhLeaf *leaf, Point3f *triangles, constint triangleCount);821 822 Point3f *CollectTriangles(BvhLeaf *leaf, int &triangleCount);561 //bool CreateTighterBoundsForLeaf(BvhLeaf *leaf, Point3f *triangles, int triangleCount); 562 563 ///Point3f *CollectTriangles(BvhLeaf *leaf, int &triangleCount); 823 564 824 565 int IsWithinViewFrustumLocal(BvhNode *node); 825 566 826 int IsWithinViewFrustum(const BoundingBox &box, const int planeMask, const int preferredPlane); 827 /** Compute screen space projection of a bounding box. 828 */ 829 float ComputeScreenSpaceProjection(const BoundingBox &box) const; 830 831 float GetMinSquareDistance(const BoundingBox &box) const; 832 /** Computes the area of the triangles in a leaf. 833 */ 834 float ComputeGeometryArea(BvhLeaf *leaf, Point3f *triangles, const int triangleCount) const; 835 836 567 int IsWithinViewFrustum(const AxisAlignedBox3 &box, int planeMask, int preferredPlane); 568 569 float GetMinSquareDistance(const AxisAlignedBox3 &box) const; 570 837 571 838 572 //////////////////////// … … 841 575 BvhNode *mRoot; 842 576 /// pointers to the geometry associated with this node 843 NodeGeometry **mGeometry;844 /// #of entities of geometry577 SceneGeometry **mGeometry; 578 /// #of entities 845 579 size_t mGeometrySize; 846 580 847 848 /////////849 //-- termination criteria850 851 int mMaxGeometry;852 int mMaxTriangles;853 int mSplitType;854 int mNumNodes;855 int mMaxDepth;856 float mMinArea;857 581 858 582 //////////////// … … 868 592 int mFrameId; 869 593 /// a vertex array used if working with indexed arrays (without vbo) 870 Point3f *mVertices;594 //Point3f *mVertices; 871 595 /// indices used for draw array rendering 872 596 unsigned int *mIndices; … … 880 604 float mVolRatioThreshold; 881 605 882 /// pointer to the renderaction883 DistanceSortedRenderAction *const mRenderer;884 885 606 BvhStats mBvhStats; 886 607 887 HierarchyNodeContainer mTestNodes;608 //HierarchyNodeContainer mTestNodes; 888 609 889 610 unsigned int *mTestIndices; 890 891 611 /// a pointer to the end of the indices array 892 612 int mCurrentIndicesPtr; … … 894 614 float mScale; 895 615 896 float mAvgDepth; 897 898 Vector3f mNearPlane; 616 Vector3 mNearPlane; 899 617 float mNearPlaneD; 618 int mNumNodes; 900 619 }; 901 620 902 621 } 903 622 904 #endif905 623 #endif // __BVH_H -
GTP/trunk/App/Demos/Vis/CHC_revisited/Geometry.h
r2751 r2752 1 1 #ifndef GEOMETRY_H 2 2 #define GEOMETRY_H 3 4 #include "common.h" 5 #include "AxisAlignedBox3.h" 6 3 7 4 8 5 9 namespace CHCDemo 6 10 { 11 12 class Material; 13 14 7 15 8 16 /** Represents drawable geometry consisting of triangles … … 19 27 AxisAlignedBox3 mBoundingBox; 20 28 TriangleContainer mTriangles; 21 } 29 }; 22 30 23 31 … … 65 73 66 74 } 75 67 76 #endif // GEOMETRY_H -
GTP/trunk/App/Demos/Vis/CHC_revisited/Matrix4x4.cpp
r2751 r2752 615 615 } 616 616 617 // Rotate a direction vector... 618 Vector3 619 PlaneRotate(const Matrix4x4& tform, const Vector3& p) 617 618 Vector3 PlaneRotate(const Matrix4x4& tform, const Vector3& p) 620 619 { 621 620 // I sure hope that matrix is invertible... … … 626 625 627 626 // Transform a normal 628 Vector3 629 TransformNormal(const Matrix4x4& tform, const Vector3& n) 627 Vector3 TransformNormal(const Matrix4x4& tform, const Vector3& n) 630 628 { 631 629 Matrix4x4 use = NormalTransformMatrix(tform); -
GTP/trunk/App/Demos/Vis/CHC_revisited/Vector3.h
r2751 r2752 67 67 return (const float*) this; 68 68 } 69 70 69 71 70 const float &operator[] (const int inx) const … … 81 80 } 82 81 83 84 82 void SetValue(float a) 85 83 { … … 90 88 */ 91 89 int DrivingAxis(void) const; 92 93 90 /** returns the axis, where the std::vector has the smallest value 94 91 */ … … 100 97 return (x > y) ? ( (x > z) ? x : z) : ( (y > z) ? y : z); 101 98 } 102 103 99 /** Returns copy of this vector where all components are positiv. 104 100 */ … … 107 103 return Vector3(fabs(x), fabs(y), fabs(z)); 108 104 } 109 110 // normalizes the std::vector of unit size corresponding to given std::vector105 /** normalizes the std::vector of unit size corresponding to given std::vector 106 */ 111 107 inline void Normalize(); 112 113 108 /** Returns false if this std::vector has a nan component. 114 109 */ 115 110 bool CheckValidity() const; 116 117 /**118 ===> Using ArbitraryNormal() for constructing coord systems119 ===> is obsoleted by RightHandedBase() method (<JK> 12/20/03).120 121 Return an arbitrary normal to `v'.122 In fact it tries v x (0,0,1) an if the result is too small,123 it definitely does v x (0,1,0). It will always work for124 non-degenareted std::vector and is much faster than to use125 TangentVectors.126 127 @param v(in) The std::vector we want to find normal for.128 @return The normal std::vector to v.129 */130 friend inline Vector3 ArbitraryNormal(const Vector3 &v);131 132 111 /** 133 112 Find a right handed coordinate system with (*this) being … … 152 131 */ 153 132 void RightHandedBase(Vector3& U, Vector3& V) const; 133 134 135 // Unary operators 136 137 Vector3 operator+ () const; 138 Vector3 operator- () const; 139 140 // Assignment operators 141 142 Vector3& operator+= (const Vector3 &A); 143 Vector3& operator-= (const Vector3 &A); 144 Vector3& operator*= (const Vector3 &A); 145 Vector3& operator*= (float A); 146 Vector3& operator/= (float A); 147 148 149 ////////// 150 //-- friends 151 152 /** 153 ===> Using ArbitraryNormal() for constructing coord systems 154 ===> is obsoleted by RightHandedBase() method (<JK> 12/20/03). 155 156 Return an arbitrary normal to `v'. 157 In fact it tries v x (0,0,1) an if the result is too small, 158 it definitely does v x (0,1,0). It will always work for 159 non-degenareted std::vector and is much faster than to use 160 TangentVectors. 161 162 @param v(in) The std::vector we want to find normal for. 163 @return The normal std::vector to v. 164 */ 165 friend inline Vector3 ArbitraryNormal(const Vector3 &v); 154 166 155 167 /// Transforms a std::vector to the global coordinate frame. … … 162 174 */ 163 175 friend inline Vector3 ToGlobalFrame(const Vector3& loc, 164 const Vector3& U,165 const Vector3& V,166 const Vector3& N);176 const Vector3& U, 177 const Vector3& V, 178 const Vector3& N); 167 179 168 180 /// Transforms a std::vector to a local coordinate frame. … … 175 187 */ 176 188 friend inline Vector3 ToLocalFrame(const Vector3& loc, 177 178 const Vector3& V,179 const Vector3& N);189 const Vector3& U, 190 const Vector3& V, 191 const Vector3& N); 180 192 181 193 /// the magnitude=size of the std::vector … … 187 199 /// SqrMagnitude(v1-v2) 188 200 friend inline float SqrDistance(const Vector3 &v1, const Vector3 &v2); 189 190 // creates the std::vector of unit size corresponding to given std::vector 201 /// creates the std::vector of unit size corresponding to given std::vector 191 202 friend inline Vector3 Normalize(const Vector3 &A); 192 193 // Rotate a normal std::vector. 203 /// // Rotate a direction vector 194 204 friend Vector3 PlaneRotate(const Matrix4x4 &, const Vector3 &); 195 205 … … 209 219 // to the vectors `N','D', and `V'. Then 'N', 'U', and 'V' create 210 220 // the orthonormal base in space R3. 211 friend void TangentVectors(Vector3 &U, 212 Vector3 &V, // output 221 friend void TangentVectors(Vector3 &U, Vector3 &V, // output 213 222 const Vector3 &normal, // input 214 223 const Vector3 &dirIncoming); 215 224 216 // Unary operators217 Vector3 operator+ () const;218 Vector3 operator- () const;219 220 // Assignment operators221 Vector3& operator+= (const Vector3 &A);222 Vector3& operator-= (const Vector3 &A);223 Vector3& operator*= (const Vector3 &A);224 Vector3& operator*= (float A);225 Vector3& operator/= (float A);226 225 227 226 // Binary operators 227 228 228 friend inline Vector3 operator+ (const Vector3 &A, const Vector3 &B); 229 229 friend inline Vector3 operator- (const Vector3 &A, const Vector3 &B); -
GTP/trunk/App/Demos/Vis/CHC_revisited/common.h
r2751 r2752 20 20 21 21 struct Triangle3; 22 22 class BvhNode; 23 class BvhLeaf; 24 25 23 26 #if defined(_MSC_VER) 24 27 // use perftimer only on msvc … … 454 457 455 458 459 460 /////////// 461 //-- typedefs 462 463 typedef std::vector<BvhNode *> BvhNodeContainer; 464 typedef std::vector<BvhLeaf *> BvhLeafContainer; 456 465 typedef std::vector<Triangle3> TriangleContainer; 457 466 458 } 459 460 #endif 461 462 463 464 465 466 467 468 467 468 } 469 470 #endif 471 472 473 474 475 476 477 478
Note: See TracChangeset
for help on using the changeset viewer.