Ignore:
Timestamp:
09/29/06 22:42:25 (18 years ago)
Author:
mattausch
Message:
 
File:
1 edited

Legend:

Unmodified
Added
Removed
  • GTP/trunk/Lib/Vis/Preprocessing/src/ViewCellBsp.cpp

    r1418 r1545  
    1717namespace GtpVisibilityPreprocessor { 
    1818 
    19  
     19////////////// 
    2020//-- static members 
    2121 
     
    168168mRoot(NULL),  
    169169mUseAreaForPvs(false), 
    170 mGenerateViewCells(true), 
    171 mTimeStamp(1) 
     170mUsePredefinedViewCells(false), 
     171mTimeStamp(1), 
     172mViewCellsTree(NULL), 
     173 mOutOfBoundsCellPartOfTree(false) 
    172174{ 
    173175        Randomize(); // initialise random generator for heuristics 
    174  
    175         mOutOfBoundsCell = GetOrCreateOutOfBoundsCell(); 
    176  
     176        mOutOfBoundsCell = new BspViewCell(); 
     177 
     178        ///////// 
    177179        //-- termination criteria for autopartition 
     180 
    178181        Environment::GetSingleton()->GetIntValue("BspTree.Termination.maxDepth", mTermMaxDepth); 
    179182        Environment::GetSingleton()->GetIntValue("BspTree.Termination.minPvs", mTermMinPvs); 
     
    184187        Environment::GetSingleton()->GetFloatValue("BspTree.Termination.minAccRayLenght", mTermMinAccRayLength); 
    185188 
     189         
     190        ///////// 
    186191        //-- factors for bsp tree split plane heuristics 
     192 
    187193        Environment::GetSingleton()->GetFloatValue("BspTree.Factor.verticalSplits", mVerticalSplitsFactor); 
    188194        Environment::GetSingleton()->GetFloatValue("BspTree.Factor.largestPolyArea", mLargestPolyAreaFactor); 
     
    196202        Environment::GetSingleton()->GetFloatValue("BspTree.Termination.ct_div_ci", mCtDivCi); 
    197203 
     204 
     205        /////////// 
    198206        //-- termination criteria for axis aligned split 
     207 
    199208        Environment::GetSingleton()->GetFloatValue("BspTree.Termination.AxisAligned.ct_div_ci", mAxisAlignedCtDivCi); 
    200209        Environment::GetSingleton()->GetFloatValue("BspTree.Termination.maxCostRatio", mMaxCostRatio); 
     
    205214        Environment::GetSingleton()->GetIntValue("BspTree.Termination.AxisAligned.minObjects",  
    206215                                                         mTermMinObjectsForAxisAligned); 
     216         
     217         
     218        ////////////// 
    207219        //-- partition criteria 
     220 
    208221        Environment::GetSingleton()->GetIntValue("BspTree.maxPolyCandidates", mMaxPolyCandidates); 
    209222        Environment::GetSingleton()->GetIntValue("BspTree.maxRayCandidates", mMaxRayCandidates); 
     
    219232        mSubdivisionStats.open(subdivisionStatsLog); 
    220233 
     234        /////////////////////7 
     235 
     236        Debug << "BSP options: " << endl; 
    221237    Debug << "BSP max depth: " << mTermMaxDepth << endl; 
    222238        Debug << "BSP min PVS: " << mTermMinPvs << endl; 
     
    401417{ 
    402418        DEL_PTR(mRoot); 
    403  
    404         // TODO must be deleted if used!! 
    405         //if (mGenerateViewCells) DEL_PTR(mOutOfBoundsCell); 
    406 } 
    407  
    408 BspViewCell *BspTree::GetOutOfBoundsCell() 
    409 { 
    410         return mOutOfBoundsCell; 
     419         
     420        if (!mOutOfBoundsCellPartOfTree) 
     421        { 
     422                // out of bounds cell not part of tree => 
     423                // delete manually 
     424                DEL_PTR(mOutOfBoundsCell); 
     425        } 
    411426} 
    412427 
     
    418433 
    419434 
    420 BspViewCell *BspTree::GetOrCreateOutOfBoundsCell() 
    421 { 
    422         if (!mOutOfBoundsCell) 
    423         { 
    424                 mOutOfBoundsCell = new BspViewCell(); 
    425                 mOutOfBoundsCell->SetId(-1); 
    426                 mOutOfBoundsCell->SetValid(false); 
    427         } 
    428  
    429         return mOutOfBoundsCell; 
     435void BspTree::SetViewCellsTree(ViewCellsTree *viewCellsTree) 
     436{ 
     437        mViewCellsTree = viewCellsTree; 
    430438} 
    431439 
     
    433441void BspTree::InsertViewCell(ViewCellLeaf *viewCell) 
    434442{ 
     443        // don't generate new view cell, insert this view cell 
     444        mUsePredefinedViewCells = true; 
    435445        PolygonContainer *polys = new PolygonContainer(); 
    436446 
    437         // don't generate new view cell, insert this one 
    438         mGenerateViewCells = false; 
    439447        // extract polygons that guide the split process 
    440448        mStat.polys += AddMeshToPolygons(viewCell->GetMesh(), *polys, viewCell); 
    441         mBox.Include(viewCell->GetBox()); // add to BSP aabb 
     449        mBbox.Include(viewCell->GetBox()); // add to BSP aabb 
    442450 
    443451        InsertPolygons(polys); 
     
    459467                                                                 new BoundedRayContainer(),  
    460468                                                                 0,  
    461                                                                  mUseAreaForPvs ? mBox.SurfaceArea() : mBox.GetVolume(),  
     469                                                                 mUseAreaForPvs ? mBbox.SurfaceArea() : mBbox.GetVolume(),  
    462470                                                                 new BspNodeGeometry())); 
    463471 
     
    472480                        BspInterior *interior = dynamic_cast<BspInterior *>(tData.mNode); 
    473481 
     482                        /////////////////// 
    474483                        //-- filter view cell polygons down the tree until a leaf is reached 
    475484                        if (!tData.mPolygons->empty()) 
     
    489498                                 
    490499                                // extract view cells associated with the split polygons 
    491                                 ViewCellLeaf *frontViewCell = GetOrCreateOutOfBoundsCell(); 
    492                                 ViewCellLeaf *backViewCell = GetOrCreateOutOfBoundsCell(); 
     500                                ViewCellLeaf *frontViewCell = mOutOfBoundsCell; 
     501                                ViewCellLeaf *backViewCell = mOutOfBoundsCell; 
    493502                         
    494503                                BspTraversalData frontData(interior->GetFront(),  
     
    498507                                                                                   tData.mRays, 
    499508                                                                                   tData.mPvs, 
    500                                                                                    mUseAreaForPvs ? mBox.SurfaceArea() : mBox.GetVolume(),  
     509                                                                                   mUseAreaForPvs ? mBbox.SurfaceArea() : mBbox.GetVolume(),  
    501510                                                                                   new BspNodeGeometry()); 
    502511 
     
    507516                                                                                  tData.mRays, 
    508517                                                                                  tData.mPvs, 
    509                                                                                    mUseAreaForPvs ? mBox.SurfaceArea() : mBox.GetVolume(),  
     518                                                                                   mUseAreaForPvs ? mBbox.SurfaceArea() : mBbox.GetVolume(),  
    510519                                                                                  new BspNodeGeometry()); 
    511520 
    512                                 if (!mGenerateViewCells) 
     521                                if (mUsePredefinedViewCells) 
    513522                                { 
    514523                                        ExtractViewCells(frontData, 
     
    558567                } 
    559568                else 
     569                { 
    560570                        DEL_PTR(poly); 
     571                } 
    561572        } 
    562573        return (int)mesh->mFaces.size(); 
     
    572583   
    573584        int polysSize = 0; 
    574  
     585        cout << "here55 " << mBbox << endl; 
     586         
     587cout << "here99"; 
    575588        for (int i = 0; i < limit; ++ i) 
    576         { 
    577                 if (viewCells[i]->GetMesh()) // copy the mesh data to polygons 
    578                 { 
    579                         mBox.Include(viewCells[i]->GetBox()); // add to BSP tree aabb 
    580                         polysSize += AddMeshToPolygons(viewCells[i]->GetMesh(), polys, viewCells[i]); 
    581                 } 
    582         } 
    583  
     589        {cout << "q"; 
     590        Mesh *mesh = viewCells[i]->GetMesh(); 
     591                if (mesh)  
     592                {cout << "w"; 
     593                        // copy the mesh into polygons and add to BSP tree aabb 
     594                        mBbox.Include(viewCells[i]->GetBox());  
     595                        polysSize += AddMeshToPolygons(mesh, polys, viewCells[i]); 
     596                } 
     597        } 
     598cout << "here155 " << mBbox << endl; 
    584599        return polysSize; 
    585600} 
     
    593608        int limit = (maxObjects > 0) ?  
    594609                Min((int)objects.size(), maxObjects) : (int)objects.size(); 
    595    
     610  cout << "here88"; 
    596611        for (int i = 0; i < limit; ++i) 
    597         { 
     612        {cout << "e"; 
    598613                Intersectable *object = objects[i]; 
    599  
    600614                Mesh *mesh = NULL; 
    601615 
     
    616630                                mesh = new Mesh(); 
    617631                                mi->GetTransformedMesh(*mesh); 
    618                                  
    619632                                break; 
    620633                        } 
     
    624637                } 
    625638                 
    626         if (mesh) // copy the mesh data to polygons 
    627                 { 
    628                         if (addToBbox) 
    629                                 mBox.Include(object->GetBox()); // add to BSP tree aabb 
    630                                  
    631                         AddMeshToPolygons(mesh, polys, mOutOfBoundsCell); 
    632  
    633                         // cleanup 
    634                         if (object->Type() == Intersectable::TRANSFORMED_MESH_INSTANCE) 
    635                                 DEL_PTR(mesh); 
     639        if (!mesh) continue; 
     640                // copy the mesh data to polygons 
     641                if (addToBbox) 
     642                {cout << "b"; 
     643                        mBbox.Include(object->GetBox()); // add to BSP tree aabb 
     644                } 
     645 
     646                AddMeshToPolygons(mesh, polys, mOutOfBoundsCell); 
     647 
     648                // cleanup 
     649                if (object->Type() == Intersectable::TRANSFORMED_MESH_INSTANCE) 
     650                { 
     651                        DEL_PTR(mesh); 
    636652                }        
    637653        } 
     
    640656} 
    641657 
    642  
     658         
    643659void BspTree::Construct(const ViewCellContainer &viewCells) 
    644660{ 
     661        cout << "here5500000000" << endl; 
     662        // construct hierarchy over the given view cells 
     663        mUsePredefinedViewCells = true; 
     664 
    645665        mStat.nodes = 1; 
    646         mBox.Initialize();      // initialise bsp tree bounding box 
     666        mBbox.Initialize(); // initialise bsp tree bounding box 
    647667 
    648668        // copy view cell meshes into one big polygon soup 
     
    650670        mStat.polys = AddToPolygonSoup(viewCells, *polys); 
    651671 
    652         // view cells are given 
    653         mGenerateViewCells = false; 
     672        Exporter *expo = Exporter::GetExporter("dummy2.wrl"); 
     673        expo->ExportPolygons(*polys); 
     674        delete expo; 
    654675        // construct tree from the view cell polygons 
    655676        Construct(polys, new BoundedRayContainer()); 
     
    659680void BspTree::Construct(const ObjectContainer &objects) 
    660681{ 
     682        // generate new view cells for this type 
     683        mUsePredefinedViewCells = false; 
     684 
    661685        mStat.nodes = 1; 
    662         mBox.Initialize();      // initialise bsp tree bounding box 
     686        mBbox.Initialize();     // initialise bsp tree bounding box 
    663687         
    664688        PolygonContainer *polys = new PolygonContainer(); 
    665689 
    666         mGenerateViewCells = true; 
    667690        // copy mesh instance polygons into one big polygon soup 
    668691        mStat.polys = AddToPolygonSoup(objects, *polys); 
     
    677700        // preprocess: throw out polygons coincident to the view space box (not needed) 
    678701        PolygonContainer boxPolys; 
    679         mBox.ExtractPolys(boxPolys); 
     702        mBbox.ExtractPolys(boxPolys); 
    680703        vector<Plane3> boxPlanes; 
    681704 
     
    722745 
    723746 
    724  
    725747void BspTree::Construct(const RayContainer &sampleRays, 
    726748                                                AxisAlignedBox3 *forcedBoundingBox) 
    727749{ 
     750        // generate new view cells for this contruction type 
     751        mUsePredefinedViewCells = false; 
     752 
    728753    mStat.nodes = 1; 
    729         mBox.Initialize();      // initialise BSP tree bounding box 
    730          
     754 
    731755        if (forcedBoundingBox) 
    732                 mBox = *forcedBoundingBox; 
     756        { 
     757                mBbox = *forcedBoundingBox; 
     758        } 
     759        else 
     760        { 
     761                mBbox.Initialize(); // initialise BSP tree bounding box  
     762        } 
    733763 
    734764        PolygonContainer *polys = new PolygonContainer(); 
     
    737767        RayContainer::const_iterator rit, rit_end = sampleRays.end(); 
    738768 
    739         // generate view cells 
    740         mGenerateViewCells = true; 
    741  
    742769        long startTime = GetTime(); 
    743  
    744770        Debug << "**** Extracting polygons from rays ****\n"; 
    745771 
    746772        std::map<Face *, Polygon3 *> facePolyMap; 
    747773 
    748         //-- extract polygons intersected by the rays 
    749774        for (rit = sampleRays.begin(); rit != rit_end; ++ rit) 
    750775        { 
     776                ////////////// 
     777                //-- extract polygons intersected by the rays 
    751778                Ray *ray = *rit; 
    752779         
     
    803830        // compute bounding box 
    804831        if (!forcedBoundingBox) 
    805                 mBox.Include(*polys); 
    806  
     832        { 
     833                mBbox.Include(*polys); 
     834        } 
     835 
     836        //////////// 
    807837        //-- store rays 
    808838        for (rit = sampleRays.begin(); rit != rit_end; ++ rit) 
     
    812842 
    813843                float minT, maxT; 
    814                 if (mBox.GetRaySegment(*ray, minT, maxT)) 
     844                if (mBbox.GetRaySegment(*ray, minT, maxT)) 
    815845                        rays->push_back(new BoundedRay(ray, minT, maxT)); 
    816846        } 
     
    833863                                                AxisAlignedBox3 *forcedBoundingBox) 
    834864{ 
     865        // generate new view cells for this construction type 
     866        mUsePredefinedViewCells = false; 
     867 
    835868    mStat.nodes = 1; 
    836         mBox.Initialize();      // initialise BSP tree bounding box 
     869        mBbox.Initialize();     // initialise BSP tree bounding box 
    837870         
    838871        if (forcedBoundingBox) 
    839                 mBox = *forcedBoundingBox; 
     872        { 
     873                mBbox = *forcedBoundingBox; 
     874        } 
    840875 
    841876        BoundedRayContainer *rays = new BoundedRayContainer(); 
    842877        PolygonContainer *polys = new PolygonContainer(); 
    843878         
    844         mGenerateViewCells = true; 
    845  
    846879        // copy mesh instance polygons into one big polygon soup 
    847880        mStat.polys = AddToPolygonSoup(objects, *polys, 0, !forcedBoundingBox); 
    848881 
    849  
     882        /////// 
     883        //-- store rays 
    850884        RayContainer::const_iterator rit, rit_end = sampleRays.end(); 
    851  
    852         //-- store rays 
     885         
    853886        for (rit = sampleRays.begin(); rit != rit_end; ++ rit) 
    854887        { 
     
    857890 
    858891                float minT, maxT; 
    859                 if (mBox.GetRaySegment(*ray, minT, maxT)) 
     892                if (mBbox.GetRaySegment(*ray, minT, maxT)) 
    860893                        rays->push_back(new BoundedRay(ray, minT, maxT)); 
    861894        } 
     
    870903{ 
    871904        BspTraversalStack tStack; 
    872  
    873905        mRoot = new BspLeaf(); 
    874906 
     
    880912                                                   polys,  
    881913                                                   0,  
    882                                                    GetOrCreateOutOfBoundsCell(),  
     914                                                   mOutOfBoundsCell,  
    883915                                                   rays,  
    884916                                                   ComputePvsSize(*rays),  
     
    886918                                                   geom); 
    887919 
    888         mTotalCost = tData.mPvs * tData.mProbability / mBox.GetVolume(); 
     920        mTotalCost = tData.mPvs * tData.mProbability / mBbox.GetVolume(); 
    889921        mTotalPvsSize = tData.mPvs; 
    890922         
     
    950982BspNode *BspTree::Subdivide(BspTraversalStack &tStack, BspTraversalData &tData) 
    951983{ 
    952         //-- terminate traversal   
     984        //////// 
     985        //-- terminate traversal 
     986 
    953987        if (TerminationCriteriaMet(tData))               
    954988        { 
     
    957991                BspViewCell *viewCell; 
    958992 
    959                 // generate new view cell for each leaf 
    960                 if (mGenerateViewCells) 
    961                 { 
     993                if (!mUsePredefinedViewCells) 
     994                {       // generate new view cell for each leaf 
    962995                        viewCell = new BspViewCell(); 
    963996                } 
    964997                else 
    965998                { 
    966                         // add view cell to leaf 
     999                        // add predefined view cell to leaf 
    9671000                        viewCell = dynamic_cast<BspViewCell *>(tData.mViewCell); 
     1001                 
     1002                        /// out of bounds cell can be handled as any other cell 
     1003                        if (viewCell == mOutOfBoundsCell) 
     1004                                mOutOfBoundsCellPartOfTree = true; 
    9681005                } 
    9691006 
     
    9791016                        viewCell->SetVolume(probability); 
    9801017 
    981                 //-- add pvs 
     1018                 
     1019                /////////// 
     1020                //-- add pvs contribution of rays 
     1021 
    9821022                if (viewCell != mOutOfBoundsCell) 
    9831023                { 
     
    9891029                } 
    9901030 
    991                 if (1) 
    992                         EvaluateLeafStats(tData); 
    993                  
     1031                if (1)EvaluateLeafStats(tData); 
     1032                 
     1033 
     1034                //////// 
    9941035                //-- clean up 
    995                  
     1036 
    9961037                // discard polygons 
    9971038                CLEAR_CONTAINER(*tData.mPolygons); 
     
    10061047        } 
    10071048 
     1049        /////////// 
    10081050        //-- continue subdivision 
     1051 
    10091052        PolygonContainer coincident; 
    10101053         
     
    10191062                SubdivideNode(tData, tFrontData, tBackData, coincident); 
    10201063 
    1021  
    10221064        if (1) 
    10231065        { 
     
    10281070                float cBack = (float)tBackData.mPvs * tBackData.mProbability; 
    10291071 
    1030                 float costDecr = (cFront + cBack - cData) / mBox.GetVolume(); 
     1072                float costDecr = (cFront + cBack - cData) / mBbox.GetVolume(); 
    10311073                 
    10321074                mTotalCost += costDecr; 
     
    10401082        } 
    10411083 
    1042         // extract view cells from coincident polygons according to plane normal 
    1043     // only if front or back polygons are empty 
    1044         if (!mGenerateViewCells) 
     1084        // extract view cells from coincident polygons  
     1085        // with respect to the orientation of their normal 
     1086    // note: if front or back polygons are empty,  
     1087        // we get the valid in - out classification for the view cell 
     1088 
     1089        if (mUsePredefinedViewCells) 
    10451090        { 
    10461091                ExtractViewCells(tFrontData, 
     
    10571102        tStack.push(tBackData); 
    10581103 
    1059         // cleanup 
     1104        //////// 
     1105        //-- cleanup 
     1106 
    10601107        DEL_PTR(tData.mNode); 
    1061  
    10621108        DEL_PTR(tData.mPolygons); 
    10631109        DEL_PTR(tData.mRays); 
     
    10801126                coincident.begin(), it_end = coincident.end(); 
    10811127 
     1128        ////////// 
    10821129        //-- find first view cells in front and back leafs 
     1130 
    10831131        for (; !(foundFront && foundBack) && (it != it_end); ++ it) 
    10841132        { 
     
    11081156        // select subdivision plane 
    11091157        BspInterior *interior = new BspInterior(SelectPlane(leaf, tData));  
    1110          
    11111158         
    11121159#ifdef _DEBUG 
     
    11351182                                                                           *backData.mGeometry,  
    11361183                                                                           interior->mPlane, 
    1137                                                                            mBox, 
     1184                                                                           mBbox, 
    11381185                                                                           //0.000000000001); 
    11391186                                                                           mEpsilon); 
     
    12741321        const float ratio = newCost / oldCost; 
    12751322 
    1276  
    1277 #if 0 
     1323#ifdef _DEBUG 
    12781324  Debug << "====================" << endl; 
    12791325  Debug << "costRatio=" << ratio << " pos=" << position<<" t=" << (position - minBox)/(maxBox - minBox) 
     
    14301476        return Plane3(pt[0], pt[1], pt[2]); 
    14311477} 
     1478 
    14321479 
    14331480Plane3 BspTree::ChooseCandidatePlane3(const BoundedRayContainer &rays) const 
     
    16971744                                                           geomBack,  
    16981745                                                           candidatePlane, 
    1699                                                            mBox, 
     1746                                                           mBbox, 
    17001747                                                           mEpsilon); 
    17011748 
     
    18561903        if (mSplitPlaneStrategy & VERTICAL_AXIS) 
    18571904        { 
    1858                 Vector3 tinyAxis(0,0,0); tinyAxis[mBox.Size().TinyAxis()] = 1.0f; 
     1905                Vector3 tinyAxis(0,0,0); tinyAxis[mBbox.Size().TinyAxis()] = 1.0f; 
    18591906                // we put a penalty on the dot product between the "tiny" vertical axis 
    18601907                // and the split plane axis 
     
    19151962AxisAlignedBox3 BspTree::GetBoundingBox() const 
    19161963{ 
    1917         return mBox; 
     1964        return mBbox; 
    19181965} 
    19191966 
     
    19722019        float maxt, mint; 
    19732020 
    1974         if (!mBox.GetRaySegment(ray, mint, maxt)) 
     2021        if (!mBbox.GetRaySegment(ray, mint, maxt)) 
    19752022                return 0; 
    19762023 
     
    21942241                } 
    21952242        } 
     2243 
     2244        // also add out of bounds cell 
     2245        if (0 && !mOutOfBoundsCell->Mailed()) 
     2246        { 
     2247                mOutOfBoundsCell->Mail(); 
     2248                viewCells.push_back(mOutOfBoundsCell); 
     2249        } 
     2250 
     2251        cout << "here555 " << viewCells.size() << endl; 
     2252 
    21962253} 
    21972254 
     
    22472304                case Ray::COINCIDENT: // TODO: should really discard ray? 
    22482305                        frontRays.push_back(bRay); 
    2249                         //DEL_PTR(bRay); 
    22502306                        break; 
    22512307                case Ray::BACK: 
     
    23222378                                                                BspNodeGeometry &vcGeom) const 
    23232379{ 
     2380        // if false, cannot construct geometry for interior leaf 
     2381        if (!mViewCellsTree) 
     2382                return; 
     2383 
    23242384        ViewCellContainer leaves; 
    23252385        mViewCellsTree->CollectLeaves(vc, leaves); 
     
    23642424 
    23652425                for (int j = 0; j < 4; ++ j) 
    2366                         vertices.push_back(mBox.GetFace(i).mVertices[j]); 
     2426                        vertices.push_back(mBbox.GetFace(i).mVertices[j]); 
    23672427 
    23682428                Polygon3 *poly = new Polygon3(vertices); 
     
    25332593                                                                *bGeom, 
    25342594                                                                interior->GetPlane(), 
    2535                                                                 mBox, 
     2595                                                                mBbox, 
    25362596                                                                //0.0000001f); 
    25372597                                                                mEpsilon); 
     
    28072867        Debug << "leaves in queue: " << numLeaves << endl; 
    28082868 
    2809  
     2869#if 0 
     2870        ////////// 
    28102871        //-- collect the leaves which haven't been found by ray casting 
    2811 #if 0 
    28122872        cout << "finding additional merge candidates using geometry" << endl; 
    28132873        vector<BspLeaf *> leaves; 
     
    28222882 
    28232883 
    2824  
    28252884/***************************************************************/ 
    28262885/*              BspNodeGeometry Implementation                 */ 
     
    30503109bool BspNodeGeometry::Valid() const 
    30513110{ 
     3111        // geometry is degenerated 
    30523112        if (mPolys.size() < 4) 
    30533113                return false; 
     
    32943354                int id = -1; 
    32953355                if (leaf->GetViewCell() != mOutOfBoundsCell) 
     3356                { 
    32963357                        id = leaf->GetViewCell()->GetId(); 
     3358                } 
    32973359 
    32983360                stream << "<Leaf viewCellId=\"" << id << "\" />" << endl; 
Note: See TracChangeset for help on using the changeset viewer.