Ignore:
Timestamp:
11/23/06 12:11:35 (18 years ago)
Author:
gumbau
Message:
 
Location:
GTP/trunk/App/Demos/Geom/Demo_LodTrees
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • GTP/trunk/App/Demos/Geom/Demo_LodTrees/README.txt

    r1540 r1783  
    22-------- 
    33 
    4 This demo shows an example of the LodTree model in action. The trees in the scene 
    5 are associated to a LodTree object that manage their level of detail of both the  
    6 trunk and the leaves. The level of detail of the objects depend on the distance to  
    7 the camera. When the camera goes away from them, the level of detail decreases, and  
    8 when the camera gets closer to them the level of detail increases to restore the  
    9 original geometry of the model. 
     4This demo shows an example of the LodTree model in action. The scene is composed of  
     5several tree groups which are associated to a LodTree object that manage the level  
     6of detail of both their trunk and leaves. The level of detail of the objects depends 
     7on the distance to the camera. When the camera goes away from them, the level of detail  
     8decreases, and when the camera gets closer to them increases to restore the original  
     9geometry of the model. 
    1010 
    11 The level of detail begins to decrease at a certain distance of the forest, and stops 
     11The level of detail begins to decrease at a certain distance of each group, and stops 
    1212decreasing when the objects reach the their minimum LOD. This 'lodding' distance is 
    1313customizable from the source code of the demo. 
     14 
     15The user can compare the differences in performance disabling the LOD management while  
     16maintaining pressed F2. 
  • GTP/trunk/App/Demos/Geom/Demo_LodTrees/build/Release/terrain.cfg

    r1540 r1783  
    11# The main world texture (if you wish the terrain manager to create a material for you) 
    2 WorldTexture=terrain_texture.jpg 
     2WorldTexture=grass_1024.jpg 
    33 
    44# The detail texture (if you wish the terrain manager to create a material for you) 
    5 DetailTexture=terrain_detail.jpg 
     5DetailTexture=grass.png 
    66 
    77#number of times the detail texture will tile in a terrain tile 
    8 DetailTile=3 
     8DetailTile=8 
    99 
    1010# Heightmap source 
     
    1212 
    1313# Heightmap-source specific settings 
    14 Heightmap.image=terrain.png 
     14Heightmap.image=noise2.jpg 
    1515 
    1616# If you use RAW, fill in the below too 
     
    2323 
    2424# How large is a page of tiles (in vertices)? Must be (2^n)+1 
    25 PageSize=513 
     25PageSize=33 
    2626 
    2727# How large is each tile? Must be (2^n)+1 and be smaller than PageSize 
    28 TileSize=65 
     28TileSize=17 
    2929 
    3030# The maximum error allowed when determining which LOD to use 
     
    3535PageWorldZ=1500 
    3636# Maximum height of the terrain  
    37 MaxHeight=100 
     37MaxHeight=120 
    3838 
    3939# Upper LOD limit 
     
    6969 
    7070# The name of the material you will define to shade the terrain 
    71 CustomMaterialName=TestTerrainMaterial 
     71CustomMaterialName=Demos/TerrainMixed 
    7272 
    7373 
  • GTP/trunk/App/Demos/Geom/Demo_LodTrees/main.cpp

    r1561 r1783  
    1818 
    1919// Distance values 
    20 #define dist_min 300 
    21 #define dist_max 900 
    22  
    23 // Model name 
    24 #define model_name "arbol" 
    25  
     20#define dist_min 40 
     21#define dist_max 800 
    2622 
    2723//Global variables 
    28 Entity* entity; 
    29 Geometry::LodTreeLibrary* myTrees; 
    30 Ogre::Mesh *ogreMesh=NULL; 
    3124Geometry::GeoMeshLoader *meshloader=NULL; 
    3225bool force_maxLODfactor = false; 
    33  
    34 Ogre::Vector3 forest_center; 
     26const int num_tree_types=10; 
     27Vector3 *centers=NULL; 
    3528 
    3629ColourValue color=ColourValue::Red; 
     
    4437 
    4538char HelpString[]="(F1) Help\n" 
    46                         "This demo shows an example of the LodTree model in action. The trees in the scene\n" 
    47                         "are associated to a LodTree object that manage their level of detail of both the\n" 
    48                         "trunk and the leaves. The level of detail of the objects depend on the distance to\n" 
    49                         "the camera. When the camera goes away from them, the level of detail decreases, and\n" 
    50                         "when the camera gets closer to them the level of detail increases to restore the\n" 
    51                         "original geometry of the model.\n" 
    52                         "The current level of detail can be seen in real-time in the top-left corner of the screen.\n" 
    53                         "The level of detail begins to decrease at a certain distance of the forest, and stops\n" 
     39                        "This demo shows an example of the LodTree model in action. The scene is composed of\n" 
     40                        "several tree groups which are associated to a LodTree object that manage the level\n" 
     41                        "of detail of both their trunk and leaves. The level of detail of the objects depends\n" 
     42                        "on the distance to the camera. When the camera goes away from them, the level of detail\n" 
     43                        "decreases, and when the camera gets closer to them increases to restore the original\n" 
     44                        "geometry of the model.\n" 
     45                        "The level of detail begins to decrease at a certain distance of each group, and stops\n" 
    5446                        "decreasing when the objects reach the their minimum LOD. This 'lodding' distance is\n" 
    55                         "customizable from the source code of the demo."; 
     47                        "customizable from the source code of the demo.\n" 
     48                        "The user can compare the differences in performance disabling the LOD management while\n"  
     49                        "maintaining pressed F2."; 
    5650 
    5751char NoHelpString[]="(F1) Help\n"; 
     
    6256{ 
    6357        int manage; 
     58        Ogre::RaySceneQuery * raySceneQuery; 
     59        float *lodfactorBefore; // one lodfactor per LOD object type 
     60        Geometry::LodTreeLibrary **lod_tree_types; 
    6461 
    6562public: 
    6663 
    67     LodTreeFrameListener(RenderWindow* win, Camera* cam) 
     64        LodTreeFrameListener(RenderWindow* win, Camera* cam,RaySceneQuery *rsq,Geometry::LodTreeLibrary **lod_trees) 
    6865        : ExampleFrameListener(win, cam, false, false) 
    6966    { 
    7067                manage=1; 
     68                raySceneQuery=rsq; 
     69                lodfactorBefore=new float[num_tree_types]; 
     70                for (int i=0; i<num_tree_types; i++) 
     71                        lodfactorBefore[i]=-1; 
     72                lod_tree_types=lod_trees; 
    7173        } 
    7274 
     
    7476    { 
    7577                Vector3 dist;  
    76                 int distance=0,inc2=0,d; 
     78                int inc2=0,d; 
    7779                unsigned int nlod,diflods; 
    7880 
    7981                // Move upto 80 units/second 
    80                 Real MoveFactor = 180.0 * evt.timeSinceLastFrame; 
     82                Real MoveFactor = 120.0 * evt.timeSinceLastFrame; 
    8183 
    8284                // Copy the current state of the input devices 
     
    103105 
    104106                //LOD selection 
    105                 int difdist = dist_max - dist_min; 
    106          
    107                 int i=0; 
    108  
    109                 dist = forest_center - mCamera->getPosition();  
    110                 distance =dist.length(); 
    111  
    112                 float lodfactor = (float)(distance - dist_min) / (float)(dist_max - dist_min); 
    113                 lodfactor = 1.0f - lodfactor; 
    114  
    115                 if (lodfactor<0.0f) 
    116                         lodfactor=0.0f; 
    117                 if (lodfactor>1.0f) 
    118                         lodfactor=1.0f; 
    119  
    120                 if (force_maxLODfactor) 
    121                         lodfactor=1.0f; 
    122  
    123                 static float lodfactorBefore = -1.0f; 
    124                 if (fabsf(lodfactorBefore-lodfactor)>0.1f) 
    125                 { 
    126                         myTrees->GoToLod(lodfactor); 
    127 //                      DumpDataToOgreBuffers(ogreMesh,myTrees); 
    128                         lodfactorBefore=lodfactor; 
     107                for (int i=0; i<num_tree_types; i++) 
     108                {        
     109                        dist = centers[i] - mCamera->getPosition();  
     110                        int distance = dist.length(); 
     111 
     112                        float lodfactor = 0.0f; 
     113                        if (distance < dist_min) 
     114                                lodfactor = 1.0f; 
     115                        else if (distance > dist_max) 
     116                                lodfactor = 0.0f; 
     117                        else 
     118                        { 
     119                lodfactor = (float)(distance - dist_min) / (float)(dist_max - dist_min); 
     120                                lodfactor = 1.0f - lodfactor; 
     121                        } 
     122 
     123                        assert(lodfactor>=0.0f); 
     124                        assert(lodfactor<=1.0f); 
     125 
     126                        if (force_maxLODfactor) 
     127                                lodfactor=1.0f; 
     128 
     129                        if (fabsf(lodfactorBefore[i]-lodfactor)>0.1f) 
     130                        { 
     131                                lod_tree_types[i]->GoToLod(lodfactor); 
     132                                lodfactorBefore[i]=lodfactor; 
     133                        } 
    129134                } 
    130135 
     
    148153                        mTranslateVector.x = mMoveScale; 
    149154 
    150                 // Instead of moving the ship left and right, rotate it using yaw() 
    151155                if(mInputDevice->isKeyDown(Ogre::KC_LEFT)) 
    152156                  mCamera->yaw(mRotScale); 
     
    173177 
    174178                if(mInputDevice->isKeyDown(Ogre::KC_ESCAPE)) 
    175                 { 
    176                         //delete myTrees; 
    177179                    return false; 
    178                 } 
    179180 
    180181        if( mInputDevice->getMouseButton( 1 ) ) 
     
    189190        } 
    190191 
    191  
    192                 char cadena[256]; 
    193  
    194                                  
    195                 sprintf(cadena,"Distance: %d",distance); 
    196                  
    197                 mInfo->setCaption(cadena); 
    198  
    199                 sprintf(cadena,"LOD factor: %f",lodfactor); 
    200  
    201                 mInfo2->setCaption(cadena); 
    202  
    203192                mCamera->yaw(mRotX); 
    204193        mCamera->pitch(mRotY); 
    205194        mCamera->moveRelative(mTranslateVector); 
     195 
     196                // make the camera walk onto the ground 
     197                Ogre::Ray ray(mCamera->getPosition()+Ogre::Vector3(0.0f,200.0f,0.0f), Ogre::Vector3::NEGATIVE_UNIT_Y); 
     198                raySceneQuery->setRay(ray); 
     199                RaySceneQueryResult &rayRes = raySceneQuery->execute(); 
     200                RaySceneQueryResult::iterator itr = rayRes.begin(); 
     201                if (itr != rayRes.end() && itr->worldFragment) 
     202                        mCamera->setPosition(itr->worldFragment->singleIntersection+Ogre::Vector3(0.0f,15.0f,0.0f)); 
    206203 
    207204                return true; 
     
    230227        } 
    231228        virtual void SetIndex(unsigned int i, unsigned int index){ 
    232                 pIdx[i] = index; //lodStripsLib->dataRetrievalInterface->GetIndex(k+offset); 
     229                pIdx[i] = index; 
    233230        } 
    234231        virtual void End(){ 
     
    244241protected: 
    245242public: 
    246     LodTreeApplication(){} 
     243    LodTreeApplication(){  
     244                raySceneQuery=NULL;  
     245                centers=new Vector3[num_tree_types]; // one center per object  
     246        } 
    247247    ~LodTreeApplication(){} 
     248 
     249        Ogre::RaySceneQuery * raySceneQuery; 
     250        Geometry::LodTreeLibrary **lod_tree_types; 
    248251 
    249252protected: 
    250253     
     254    void chooseSceneManager(void) 
     255    { 
     256        // Get the SceneManager, in this case a generic one 
     257                mSceneMgr = mRoot->getSceneManager(Ogre::ST_EXTERIOR_CLOSE); 
     258    } 
    251259 
    252260 
     
    254262    void createScene(void) 
    255263    { 
    256 //              mat = new MaterialPtr[1]; 
    257                  
    258264        theCam = mCamera; 
    259                 theCam->setPosition(0,20,dist_min-40); 
     265                theCam->setPosition(500,100,dist_min+600); 
    260266        // Set ambient light 
    261267        mSceneMgr->setAmbientLight(ColourValue(0.4, 0.4, 0.4)); 
    262268                theCam->setNearClipDistance(0.1f); 
    263269 
     270                mSceneMgr->setFog( FOG_EXP, Ogre::ColourValue(0.4f,0.5f,0.6f), 0.001 ); 
     271 
    264272        // Create a directional light 
    265273                Light* l = mSceneMgr->createLight("MainLight"); 
    266274                l->setType(Light::LT_DIRECTIONAL); 
    267         l->setDirection(0.0,-1.0,0.0); 
     275        l->setDirection(0.1,-1.0,-0.2); 
    268276         
    269         // Define a floor plane mesh 
    270                 Plane plane( Vector3::UNIT_Y, 0 ); 
    271  
    272                 MeshManager::getSingleton().createPlane("ground", 
    273                         ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane, 
    274                         1500,1500,20,20,true,1,5,5,Vector3::UNIT_Z); 
    275  
    276                 Entity* suelo = mSceneMgr->createEntity( "GroundEntity", "ground" ); 
    277                 mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(suelo); 
    278                  
    279  
    280                 suelo->setMaterialName("Examples/GrassFloor"); 
    281                 suelo->setCastShadows(false);        
    282  
    283          
     277                // terrain 
     278                mSceneMgr->setWorldGeometry( "terrain.cfg" ); 
     279 
     280        
    284281        mSceneMgr->setSkyBox(true, "Examples/CloudyNoonSkyBox"); 
    285282 
     
    287284        SceneNode* myRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); 
    288285 
    289                 std::string model_file=model_name; 
    290                 model_file.append(".mesh"); 
    291  
    292                 //Models 
    293         entity = mSceneMgr->createEntity(model_name, "../../../OgreStuff/media/GT/ML11.mesh"); 
    294  
    295                 ogreMesh = entity->getMesh().getPointer(); 
    296  
    297                 // load LOD info from the object 
    298                 meshloader=new Geometry::GeoMeshLoader; 
    299                 Geometry::Mesh *themesh = meshloader->load("../../../OgreStuff/media/GT/ML11.mesh"); 
    300  
    301                 if (!meshloader->GetLodStripsData()) 
    302             OGRE_EXCEPT(1, "The loaded mesh does not contain LOD info for the trunk","LOD Demo"); 
    303                 if (!meshloader->GetTreeSimpSeq()) 
    304             OGRE_EXCEPT(1, "The loaded mesh does not contain LOD info for the foliage","LOD Demo"); 
    305  
    306                 myTrees = new Geometry::LodTreeLibrary( meshloader->GetLodStripsData(), 
    307                                                                                                 meshloader->GetTreeSimpSeq(), 
    308                                                                                                 themesh, 
    309                                                                                                 new CustomIndexData(ogreMesh)); 
    310  
    311                 entity->setNormaliseNormals(true); 
    312  
    313                 for (int submesh=0; submesh < ogreMesh->getNumSubMeshes(); submesh++) 
    314                 { 
    315                         bool istrunk = myTrees->GetLeavesSubMesh()!=submesh; 
    316                         if (istrunk) 
    317                                 entity->getSubEntity(submesh)->setMaterialName("Examples/ML11/trunk"); 
    318                         else 
    319                                 entity->getSubEntity(submesh)->setMaterialName("Examples/ML11/leaf"); 
    320                 } 
    321  
    322                 forest_center=Ogre::Vector3(150.0f,0.0f,0.0f); 
    323                 for (int i=1; i<5; i++) // 20 
    324                         for (int j=1; j<5; j++) // 20 
    325                         { 
    326                                 char newTreeName[16]=""; 
    327                                 sprintf(newTreeName,"arbol_%d_%d",i,j); 
    328                                 Ogre::SceneNode * auxnode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); 
    329                                 Ogre::Entity *auxent = entity->clone(newTreeName); 
    330                                 auxnode->attachObject( auxent ); 
    331                                 auxnode->scale(4.0f,4.0f,4.0f); 
    332                                 auxnode->rotate(Ogre::Vector3(0,0,1),Ogre::Degree(rand()%360)); 
    333                                 auxnode->rotate(Ogre::Vector3(1,0,0),Ogre::Radian(-3.14159f*0.5f),Ogre::Node::TS_WORLD); 
    334                                 float randomsepx = (float)((rand()%38)-19); 
    335                                 float randomsepy = (float)((rand()%32)-12); 
    336                                 auxnode->translate(i*80.0f+randomsepx,0.0f,-j*80.0f-randomsepx); 
    337                                 auxent->setNormaliseNormals(true); 
    338                         } 
    339  
    340                 if (!meshloader->GetLodStripsData() || !meshloader->GetTreeSimpSeq()) 
    341             OGRE_EXCEPT(1, "The loaded mesh does not contain any LOD info","LOD Demo"); 
    342  
     286                // initialize the rayscene queryer 
     287                raySceneQuery = mSceneMgr->createRayQuery(Ray()); 
     288 
     289                // the blacksmith 
     290                Vector3 BS_center=Ogre::Vector3(780.0f,200.0f,650.0f); 
     291                Ogre::Ray ray(BS_center, Ogre::Vector3::NEGATIVE_UNIT_Y); 
     292                raySceneQuery->setRay(ray); 
     293                RaySceneQueryResult &rayRes = raySceneQuery->execute(); 
     294                RaySceneQueryResult::iterator itr = rayRes.begin(); 
     295                if (itr != rayRes.end() && itr->worldFragment) 
     296                        BS_center=itr->worldFragment->singleIntersection-Ogre::Vector3(0.0f,1.0f,0.0f); 
     297 
     298                Ogre::Entity *entBS = mSceneMgr->createEntity("blacksmith","../../../OgreStuff/media/models/blacksmith.mesh"); 
     299                Ogre::SceneNode * nodeBS = mSceneMgr->getRootSceneNode()->createChildSceneNode(); 
     300                nodeBS->attachObject(entBS); 
     301                nodeBS->translate(BS_center); 
     302                nodeBS->scale(0.1f,0.1f,0.1f); 
     303 
     304                lod_tree_types = new Geometry::LodTreeLibrary*[num_tree_types]; 
     305 
     306                // trees 
     307                lod_tree_types[0] = CreateLODTrees("../../../OgreStuff/media/GT/ML06a_01.mesh",5,780,630,200,200,centers[0], 4.0f); 
     308                lod_tree_types[1] = CreateLODTrees("../../../OgreStuff/media/GT/ML13a_02.mesh",4,150,150,200,200,centers[1], 2.0f); 
     309                lod_tree_types[2] = CreateLODTrees("../../../OgreStuff/media/GT/ML15a_03.mesh",10,300,400,200,200,centers[2], 6.0f); 
     310                lod_tree_types[3] = CreateLODTrees("../../../OgreStuff/media/GT/ML06a_01.mesh",5,330,900,500,500,centers[3], 3.0f); 
     311                lod_tree_types[4] = CreateLODTrees("../../../OgreStuff/media/GT/ML13a_02.mesh",5,1100,900,300,300,centers[4], 2.0f); 
     312                lod_tree_types[5] = CreateLODTrees("../../../OgreStuff/media/GT/ML15a_03.mesh",10,1100,300,200,200,centers[5], 6.0f); 
     313                lod_tree_types[6] = CreateLODTrees("../../../OgreStuff/media/GT/ML06a_01.mesh",7,900,500,300,300,centers[6], 2.0f); 
     314                lod_tree_types[7] = CreateLODTrees("../../../OgreStuff/media/GT/ML13a_02.mesh",5,500,950,200,200,centers[7], 3.0f); 
     315                lod_tree_types[8] = CreateLODTrees("../../../OgreStuff/media/GT/ML15a_03.mesh",9,600,400,200,200,centers[8], 6.0f); 
     316                lod_tree_types[9] = CreateLODTrees("../../../OgreStuff/media/GT/ML06a_01.mesh",7,950,1100,200,200,centers[9], 2.5f); 
    343317 
    344318        // show overlay 
     
    350324            pOver->show(); 
    351325 
     326                mInfo->setCaption("Maintain F2 to disable LOD"); 
     327 
    352328    } 
     329 
     330    void destroyScene(void) 
     331        { 
     332                if (raySceneQuery) 
     333                        delete raySceneQuery; 
     334                delete[] lod_tree_types; 
     335        } 
    353336 
    354337    void createFrameListener(void) 
    355338    { 
    356         mFrameListener= new LodTreeFrameListener(mWindow, mCamera); 
     339        mFrameListener= new LodTreeFrameListener(mWindow, mCamera, raySceneQuery, lod_tree_types); 
    357340        mFrameListener->showDebugOverlay(true); 
    358341        mRoot->addFrameListener(mFrameListener); 
    359342    } 
     343 
     344        Geometry::LodTreeLibrary *CreateLODTrees(const char *filename, int numObjs, float cx, float cz, float w, float h, Vector3 &outcenter, float scale) 
     345        { 
     346                static int iname = 0; 
     347                char newMeshName[16]=""; 
     348                sprintf(newMeshName,"tree_%d",iname); 
     349 
     350 
     351                // load LOD info from the object 
     352                meshloader=new Geometry::GeoMeshLoader; 
     353                Geometry::Mesh *themesh = meshloader->load((char*)filename); 
     354 
     355                if (!meshloader->GetLodStripsData()) 
     356            OGRE_EXCEPT(1, "The loaded mesh does not contain LOD info for the trunk","LOD Demo"); 
     357                if (!meshloader->GetTreeSimpSeq()) 
     358            OGRE_EXCEPT(1, "The loaded mesh does not contain LOD info for the foliage","LOD Demo"); 
     359 
     360                Ogre::Entity *entity = mSceneMgr->createEntity(newMeshName,filename);            
     361                if (!entity->getMesh().unique()) 
     362                { 
     363                        static int iclone = 0; 
     364                        char newCloneMesh[16]=""; 
     365                        sprintf(newCloneMesh,"clonetree_%d",iclone++); 
     366                        entity->getMesh() = entity->getMesh()->clone(newCloneMesh); 
     367                } 
     368                Ogre::Mesh *ogreMesh = entity->getMesh().getPointer(); 
     369                entity->setNormaliseNormals(true); 
     370 
     371                Geometry::LodTreeLibrary *mytree = new Geometry::LodTreeLibrary( 
     372                                                                                                meshloader->GetLodStripsData(), 
     373                                                                                                meshloader->GetTreeSimpSeq(), 
     374                                                                                                themesh, 
     375                                                                                                new CustomIndexData(ogreMesh) ); 
     376 
     377                outcenter=Vector3::ZERO; 
     378 
     379                for (int i=0; i<numObjs; i++) 
     380                { 
     381                        char newTreeName[16]=""; 
     382                        sprintf(newTreeName,"arbol_%d_%d",iname,i); 
     383                        Ogre::SceneNode * auxnode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); 
     384                        Ogre::Entity *auxent = entity->clone(newTreeName); 
     385                        auxnode->attachObject( auxent ); 
     386 
     387                        float kx = (rand()%100)/100.0f; 
     388                        float kz = (rand()%100)/100.0f; 
     389 
     390                        Ogre::Vector3 treepos(kx*(cx-w*0.5f) + (1.0f-kx)*(cx+w*0.5f), 200.0f,  
     391                                                                  kz*(cz-h*0.5f) + (1.0f-kz)*(cz+h*0.5f)); 
     392 
     393                        Ogre::Ray ray(treepos,Ogre::Vector3::NEGATIVE_UNIT_Y); 
     394                        raySceneQuery->setRay(ray); 
     395                        RaySceneQueryResult &rayRes = raySceneQuery->execute(); 
     396                        RaySceneQueryResult::iterator itr = rayRes.begin(); 
     397                        if (itr != rayRes.end() && itr->worldFragment) 
     398                                treepos = itr->worldFragment->singleIntersection; 
     399 
     400                        auxnode->translate(treepos); 
     401                        auxnode->scale(scale,scale,scale); 
     402                        auxnode->rotate(Ogre::Vector3(0,1,0),Ogre::Degree(rand()%360)); 
     403                        auxent->setNormaliseNormals(true); 
     404                        outcenter+=treepos; 
     405                } 
     406                outcenter/=numObjs; 
     407                iname++; 
     408 
     409                return mytree; 
     410        } 
     411 
     412        float Rand(float minN, float maxN) 
     413        { 
     414                return (((rand()%1000)/1000.0f)*(maxN-minN))+minN; 
     415        } 
    360416 
    361417}; 
Note: See TracChangeset for help on using the changeset viewer.