source: GTP/trunk/App/Demos/Illum/Ogre/src/SpaceStation/include/SpaceStation.h @ 2472

Revision 2472, 18.7 KB checked in by szirmay, 17 years ago (diff)
Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4    (Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2005 The OGRE Team
8Also see acknowledgements in Readme.html
9
10You may use this sample code for anything you like, it is not covered by the
11LGPL like the rest of the engine.
12-----------------------------------------------------------------------------
13*/
14
15/**
16    \file
17        Lighting.h
18    \brief
19        Shows lighting support in OGRE. Also demonstrates the use of billboards
20        and controllers for automatic time-relative behaviour.
21*/
22
23
24#include "ExampleApplication.h"
25#include "OgreIlluminationManager.h"
26#include "NXOgre.h"
27#include "SceneSerializer.h"
28#include "Characters.h"
29
30bool highQuality = true;
31
32Scene* NXScene = 0;
33FPSPlayer* gFPSPlayer;
34
35bool PP_glowON = true;
36bool PP_tonemapON = true;
37bool PP_dofON = true;
38
39Real lightpower = 32.0;
40Real glowCut = 50.6;
41Real glowTimeBlur = 0.6;
42Real glowStrech = 1.8;
43Real glowCombine = 0.5;
44
45bool hallLightON = true;
46bool smokeON = true;
47bool smokeVisible = true;
48Timer* smokeOffTimer;
49
50ParticleSystem* smokeSystem;
51
52Entity* LightButton;
53Entity* SmokeButton;
54
55AnimationState* scientistAnimState;
56
57// Listener class for frame updates
58class SpaceStationFrameListener : public ExampleFrameListener
59{
60protected:
61       
62        unsigned long framecount;
63        SceneNode* mainLightNode;
64        Light* light1;
65        Light* light2;
66        SceneNode* sysNode;
67       
68public:
69    SpaceStationFrameListener(RenderWindow* window, Camera* maincam, SceneNode* lightNode, Light* l, Light* l2, SceneNode* sysNode)
70        :ExampleFrameListener(window, maincam)
71                                 
72    {
73                mainLightNode = lightNode;
74                light1 = l;
75                light2 = l2;
76                framecount = 0;
77                mMoveSpeed /= 50.0;
78                this->sysNode = sysNode;
79
80                mStatsOn = false;
81                showDebugOverlay(mStatsOn);
82                smokeOffTimer = new Timer();
83                smokeOffTimer->reset();
84    }
85
86    bool frameStarted(const FrameEvent& evt)
87    {   
88                scientistAnimState->addTime(evt.timeSinceLastFrame);
89
90                if(smokeOffTimer->getMilliseconds()>10)
91                {
92                        if(smokeVisible)
93                        {
94                                smokeVisible = false;
95                                smokeSystem->setVisible(false);
96                        }
97                }
98
99                float dt = evt.timeSinceLastFrame;
100                static float rotate = 0;
101                rotate += dt * 100.0;
102                Vector3 dir(Math::Sin(Radian(Degree(rotate))), -1.4,Math::Cos(Radian(Degree(rotate))));
103                dir.normalise();
104                if(light1)
105                        light1->setDirection(dir);
106
107                if(light2)
108                {
109                        Vector3 pos = Vector3(-1.6,3,-25.5)+ Vector3(dir.x, 0, dir.z) * 0.3;
110                        light2->setPosition(pos);
111                        dir *= -1;
112                        dir.y = -2.0;
113                        dir.normalise();
114                        light2->setDirection(dir);
115                }
116               
117                return ExampleFrameListener::frameStarted(evt);     
118    }
119       
120        void turnSmokeOff()
121        {
122                smokeSystem->getEmitter(0)->setEmissionRate(0);
123                smokeOffTimer->reset();
124        }
125        void smokebuttonpressed()
126        {
127                hallLightON = !hallLightON;
128                                light2->setVisible(hallLightON);
129                        if(!hallLightON)
130                        {
131                                smokeON = false;
132                                turnSmokeOff();
133                        }
134        }
135        void lightbuttonpressed()
136        {
137                if(hallLightON)
138                {
139                        smokeON = !smokeON;
140                        if(smokeON)
141                        {
142                                smokeSystem->getEmitter(0)->setEmissionRate(3);
143                                smokeSystem->setVisible(true);
144                                smokeVisible = true;
145                        }
146                        else
147                        {
148                                turnSmokeOff();
149                        }
150                               
151                }
152        }
153
154        bool processUnbufferedKeyInput(const FrameEvent& evt)
155    {
156                gFPSPlayer->processKeyInputs(mInputDevice);
157                               
158                if (mInputDevice->isKeyDown(KC_ADD) && mTimeUntilNextToggle <= 0)
159                {
160                        if (mInputDevice->isKeyDown(KC_RSHIFT))
161                                lightpower /= 1.2;
162                        else
163                                lightpower *= 1.2;
164                        light2->setPowerScale(lightpower);
165                        mTimeUntilNextToggle = 0.2;
166                                mWindow->setDebugText(String("LightPower ") + StringConverter::toString(lightpower));
167                }
168                if (mInputDevice->isKeyDown(KC_SUBTRACT) && mTimeUntilNextToggle <= 0)
169                {
170                        if (mInputDevice->isKeyDown(KC_RSHIFT))
171                                glowCut /= 1.2;
172                        else
173                                glowCut *= 1.2;
174                        Ogre::Material* m = (Ogre::Material*) MaterialManager::getSingleton().getByName("GTP/Station/PostProc/GlowCut").getPointer();
175                        m->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("cutValue",glowCut);
176                        mTimeUntilNextToggle = 0.2;
177                        mWindow->setDebugText(String("GlowCut ") + StringConverter::toString(glowCut));
178                }
179                if (mInputDevice->isKeyDown(KC_MULTIPLY) && mTimeUntilNextToggle <= 0)
180                {
181                        if (mInputDevice->isKeyDown(KC_RSHIFT))
182                                glowTimeBlur /= 1.2;
183                        else
184                                glowTimeBlur *= 1.2;
185                        Ogre::Material* m = (Ogre::Material*) MaterialManager::getSingleton().getByName("GTP/Station/PostProc/GlowCut").getPointer();
186                        m->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("timeBlur", glowTimeBlur);
187                        mTimeUntilNextToggle = 0.2;
188                        mWindow->setDebugText(String("GlowTimeBlur ") + StringConverter::toString(glowTimeBlur));
189                }
190                if (mInputDevice->isKeyDown(KC_M))
191                {
192                        if (mInputDevice->isKeyDown(KC_RSHIFT))
193                                glowStrech -= 0.01;
194                        else
195                                glowStrech += 0.01;
196                        Ogre::Material* m = (Ogre::Material*) MaterialManager::getSingleton().getByName("GTP/Station/PostProc/GlowBlurH").getPointer();
197                        m->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("Stretch", glowStrech);
198                        m = (Ogre::Material*) MaterialManager::getSingleton().getByName("GTP/Station/PostProc/GlowBlurV").getPointer();
199                        m->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("Stretch", glowStrech);
200                        mWindow->setDebugText(String("GlowStretch ") + StringConverter::toString(glowStrech));
201                }
202                if (mInputDevice->isKeyDown(KC_N))
203                {
204                        if (mInputDevice->isKeyDown(KC_RSHIFT))
205                                glowCombine -= 0.01;
206                        else
207                                glowCombine += 0.01;
208                        Ogre::Material* m = (Ogre::Material*) MaterialManager::getSingleton().getByName("GTP/Station/GlowFinalCombine").getPointer();
209                        m->getTechnique(0)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("weight2", glowCombine);
210                        mTimeUntilNextToggle = 0.2;
211                        mWindow->setDebugText(String("GlowCombine ") + StringConverter::toString(glowCombine));
212                }
213
214                if (mInputDevice->isKeyDown(KC_3) && mTimeUntilNextToggle <= 0)
215                {
216                        PP_glowON = !PP_glowON;
217                        CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0),
218                                                                                                                "GTP/Station/PostProc/Glow", PP_glowON);
219                        mTimeUntilNextToggle = 0.2;
220                }
221                if (mInputDevice->isKeyDown(KC_4) && mTimeUntilNextToggle <= 0)
222                {
223                        PP_tonemapON = !PP_tonemapON;
224
225                        CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0),
226                                                                                                                "GTP/PostProc/ToneMap", PP_tonemapON);
227                       
228                        mTimeUntilNextToggle = 0.2;
229                }
230                if (mInputDevice->isKeyDown(KC_5) && mTimeUntilNextToggle <= 0)
231                {
232                        PP_dofON = !PP_dofON;
233                        CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0),
234                                                                                                                "GTP/Moria/PostProc/DoF", PP_dofON);
235                        mTimeUntilNextToggle = 0.2;
236                }
237
238                if( mInputDevice->isKeyDown( KC_ESCAPE) )
239        {           
240            return false;
241        }
242                if (mInputDevice->isKeyDown(KC_F) && mTimeUntilNextToggle <= 0)
243        {
244            mStatsOn = !mStatsOn;
245            showDebugOverlay(mStatsOn);
246
247            mTimeUntilNextToggle = 1;
248        }
249                if (mInputDevice->isKeyDown(KC_SYSRQ) && mTimeUntilNextToggle <= 0)
250        {
251                        char tmp[20];
252                        sprintf(tmp, "screenshot_%d.png", ++mNumScreenShots);
253            mWindow->writeContentsToFile(tmp);
254            mTimeUntilNextToggle = 0.5;
255                        mWindow->setDebugText(String("Wrote ") + tmp);
256        }
257                return true;
258                //return ExampleFrameListener::processUnbufferedKeyInput(evt);
259        }
260
261        bool processUnbufferedMouseInput(const FrameEvent& evt)
262        {
263                gFPSPlayer->processMouseInputs(mInputDevice);
264        //      return ExampleFrameListener::processUnbufferedMouseInput(evt);
265
266                static float lasttime = 0;
267                float thistime = Root::getSingleton().getTimer()->getMilliseconds();
268                float dt = ((float)(thistime - lasttime))/ 1000.0;
269                lasttime = thistime;
270
271                static float buttonChangeTime = 0;
272                buttonChangeTime += dt;
273                if( mInputDevice->getMouseButton( 1 ) && buttonChangeTime > 0.25)
274                {
275                        buttonChangeTime = 0;
276                       
277                        Vector3 PlayerPos = FPSPlayer::thePlayer->getPosition();
278                        float dist = (PlayerPos - Vector3(-1.5375, -0.73,-27.6)).length();
279                        if( dist < 1 && FPSPlayer::thePlayer->getDirection().dotProduct(Vector3(0,0,-1))>0)
280                        {
281                                if(PlayerPos.x > -1.5375)//lightbutton
282                                {
283                                        lightbuttonpressed();
284                                }
285                                else//smokebutton
286                                {
287                                        smokebuttonpressed();
288                                }
289                        }
290                }
291                return true;
292        }
293};
294
295/** Application class */
296class SpaceStationApplication : public ExampleApplication
297{
298
299
300
301
302protected:
303        SceneNode* kupolaNode;
304        SceneNode* roomLightNode;
305        SceneNode* hallLightNode;
306        Light* roomLight;
307        Light* hallLight;
308        SceneNode *pSysNode;
309       
310        void loadResources(void)
311        {
312                // Initialise, parse scripts etc
313                ResourceGroupManager::getSingleton().initialiseResourceGroup("Bootstrap");
314                ResourceGroupManager::getSingleton().initialiseResourceGroup("GTP_Basic");
315                ResourceGroupManager::getSingleton().initialiseResourceGroup("General");
316                ResourceGroupManager::getSingleton().initialiseResourceGroup("GTP_EnvMap");
317                ResourceGroupManager::getSingleton().initialiseResourceGroup("GTP_Caustic");
318                ResourceGroupManager::getSingleton().initialiseResourceGroup("PostProc");
319                ResourceGroupManager::getSingleton().initialiseResourceGroup("GTP_Particles");
320                ResourceGroupManager::getSingleton().initialiseResourceGroup("GTP_PathMap");
321                ResourceGroupManager::getSingleton().initialiseResourceGroup("GTP_Diffuse");
322                ResourceGroupManager::getSingleton().initialiseResourceGroup("GTP_SpaceStation");               
323        }
324
325        void createPostproc()
326        {               
327        /*      CompositorManager::getSingleton().addCompositor(mWindow->getViewport(0),
328                                                                                                                "GTP/SpaceStation/PostProc/DoF");
329                CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0),
330                                                                                                                "GTP/SpaceStation/PostProc/DoF", true);*/
331
332                CompositorManager::getSingleton().addCompositor(mWindow->getViewport(0),
333                                                                                                                "GTP/Station/PostProc/Glow");
334                CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0),
335                                                                                                                "GTP/Station/PostProc/Glow", true);
336               
337                CompositorManager::getSingleton().addCompositor(mWindow->getViewport(0),
338                                                                                                                "GTP/PostProc/ToneMap");
339                CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0),
340                                                                                                                "GTP/PostProc/ToneMap", true); 
341                /*             
342                CompositorManager::getSingleton().addCompositor(mWindow->getViewport(0),
343                                                                                                                "GTP/PostProc/MotionBlur");
344                CompositorManager::getSingleton().setCompositorEnabled(mWindow->getViewport(0),
345                                                                                                                "GTP/PostProc/MotionBlur", false);*/
346               
347        }
348   
349        void chooseSceneManager()
350        {
351                mSceneMgr = mRoot->createSceneManager("OctreeSceneManager");
352                //mSceneMgr = mRoot->createSceneManager("OcclusionCullingSceneManager");
353        }
354
355        void createScene(void) ///TOWERS
356    {
357                roomLight = hallLight = 0;
358
359                std::string occlusion_cfg("OcclusionCulling.cfg");
360                //mSceneMgr->setWorldGeometry(occlusion_cfg);
361               
362                //Overlay* mainOverlay = OverlayManager::getSingleton().getByName("MORIA/MainOverlay");
363                //mainOverlay->show();
364
365                createPostproc();
366
367                OgreIlluminationManager::getSingleton().setMainCamera(mCamera);
368                OgreIlluminationManager::getSingleton().setMainViewport(mWindow->getViewport(0));
369                 OgreIlluminationManager::getSingleton().setFocusingSM(false);
370           OgreIlluminationManager::getSingleton().setShadowMapSize(700);
371           OgreIlluminationManager::getSingleton().setBlurShadowMap(highQuality);
372           if(highQuality)
373                   OgreIlluminationManager::getSingleton().setShadowMapMaterialName(Light::LT_SPOTLIGHT, "GTP/Basic/Distance_Normalized");
374           else
375                   OgreIlluminationManager::getSingleton().setShadowMapMaterialName(Light::LT_SPOTLIGHT, "GTP/Basic/Depth");//GTP/Basic/Distance_Normalize
376           OgreIlluminationManager::getSingleton().setShadowMapMaterialName(Light::LT_POINT, "GTP/Basic/Distance_Normalized");
377           OgreIlluminationManager::getSingleton().setUseLISPSM(false);
378           OgreIlluminationManager::getSingleton().joinSharedRuns(false);
379                               
380                Root::getSingleton()._setCurrentSceneManager(mSceneMgr);
381                mCamera->setPosition(0,0,0);
382                //mCamera->setPosition(0,100,0);
383                //mCamera->lookAt(0,-1,0);
384                mCamera->setFOVy(Radian(Degree(80)));
385                mCamera->setNearClipDistance(0.001);
386                mCamera->setFarClipDistance(820);
387                // Set ambient light
388       // mSceneMgr->setAmbientLight(ColourValue(0.1, 0.1, 0.1));
389                //mSceneMgr->setSkyBox(true, "Examples/EveningSkyBox", 500);
390               
391                SceneNode* rootNode = mSceneMgr->getRootSceneNode();
392               
393                SceneSerializer s(mSceneMgr);
394                DataStreamPtr inputStream;                             
395                inputStream = ResourceGroupManager::getSingleton().openResource("station.level");       
396                World* NXWorld = new World("FrameListener: no, log: html");
397                NXWorld->getPhysXDriver()->createFrameListener(90);
398                NXScene = NXWorld->createScene("Main", mSceneMgr, "gravity: yes, floor: no");
399                s.setScene(NXScene);
400                s.setWorld(NXWorld);
401                s.parseScript(inputStream, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
402                s.parseEntryPoints("StationPrmEntryPoints.text");
403       
404                NXScene->createActor("bound", new MeshShape("stationbound.mesh"), Vector3(0,0,0), "static: yes");
405                NXScene->createActor("bound2", new MeshShape("stationbound2.mesh"), Vector3(0,0,0), "static: yes");
406               
407                hallLight = mSceneMgr->createLight("hallLight");
408                hallLight->setType(Light::LT_SPOTLIGHT);
409                hallLight->setCastShadows(true);
410        hallLight->setDiffuseColour(ColourValue::White);
411                hallLight->setSpecularColour(ColourValue::White);
412                hallLightNode = rootNode->createChildSceneNode();
413               
414                //hallLightNode->setPosition(-1.6,3,-25.5);             
415                hallLight->setPowerScale(200);
416                hallLight->setDirection(0,0,-1);
417                hallLightNode->attachObject(hallLight);
418                hallLight->setSpotlightRange(Degree(0),Degree(140));
419                hallLight->setAttenuation(20.0, 0, 0, 3);
420                hallLight->setVisible(hallLightON);
421
422               
423                roomLight = mSceneMgr->createLight("roomLight");
424                roomLight->setType(Light::LT_SPOTLIGHT);
425                roomLight->setCastShadows(true);
426        roomLight->setDiffuseColour(ColourValue::White);
427                roomLight->setSpecularColour(ColourValue::White);
428                roomLightNode = rootNode->createChildSceneNode();
429               
430                roomLightNode->setPosition(-7.5,1,-11);         
431                roomLight->setPowerScale(50);
432                roomLight->setDirection(0,0,-1);
433                roomLightNode->attachObject(roomLight);
434                roomLight->setSpotlightRange(Degree(0),Degree(140));
435                roomLight->setAttenuation(20.0, 0, 0, 3);
436        /*
437                BillboardSet* bbs = mSceneMgr->createBillboardSet("bb", 1);
438                bbs->setDefaultDimensions(0.3, 0.3);
439                bbs->createBillboard(Vector3::ZERO, ColourValue::White);
440                bbs->setMaterialName("Flare");
441                roomLightNode->attachObject(bbs);*/
442
443
444                Entity* CauCaster = mSceneMgr->createEntity("CauCaster", "sphere.mesh");
445                SceneNode* casterNode = rootNode->createChildSceneNode();
446                casterNode->setPosition(-1.5, 1.0, -25);
447                casterNode->setScale(0.75, .75, 0.75);
448                CauCaster->setMaterialName("SpaceStation_CausticCaster");
449                casterNode->attachObject(CauCaster);
450                casterNode->_updateBounds();
451                CauCaster->setCastShadows(true);
452
453                smokeSystem = mSceneMgr->createParticleSystem("psys1", "GTP/Station/Smoke");
454                smokeSystem->setKeepParticlesInLocalSpace(false);
455                SceneNode *pSysNode = rootNode->createChildSceneNode("psys1");
456                pSysNode->attachObject(smokeSystem);
457                smokeSystem->getEmitter(0)->setPosition(Vector3(0,-3.7,0));
458                smokeSystem->setCastShadows(false);
459                pSysNode->setPosition(-1.5, 3, -25);
460                smokeSystem->setVisible(smokeVisible);
461
462                LightButton = mSceneMgr->createEntity("lightButton", "cube.mesh");
463                SceneNode* lbNode = rootNode->createChildSceneNode();
464                lbNode->setScale(0.1,0.1,0.1);
465                lbNode->setPosition(-1.8, -0.73, -27.6);
466                lbNode->rotate(Vector3(1,0,0), Degree(30.0));
467                lbNode->attachObject(LightButton);
468                LightButton->setMaterialName("Station/redbutton");
469               
470                SmokeButton = mSceneMgr->createEntity("smokeButton", "cube.mesh");
471                SceneNode* sbNode = rootNode->createChildSceneNode();
472                sbNode->setScale(0.1,0.1,0.1);
473                sbNode->setPosition(-1.25, -0.73, -27.6);
474                sbNode->rotate(Vector3(1,0,0), Degree(30.0));
475                sbNode->attachObject(SmokeButton);
476                SmokeButton->setMaterialName("Station/bluebutton");
477
478                Entity* scientist = mSceneMgr->createEntity("scientist", "scientist.mesh");
479                //scientist->setMaterialName("GTP/Diffuse/Disc2Point");
480                SceneNode* aNode = rootNode->createChildSceneNode();
481                aNode->setPosition(-7.5,-1.3,-11);
482                aNode->setScale(0.02,0.02,0.02);
483                aNode->attachObject(scientist);
484                scientist->setCastShadows(true);
485                aNode->_updateBounds();
486                AxisAlignedBox bb4 = scientist->getWorldBoundingBox();
487                AxisAlignedBox bb3 = scientist->getBoundingBox();
488                Ogre::Sphere bs2 = scientist->getWorldBoundingSphere();
489                scientistAnimState = scientist->getAnimationState("idle");
490                scientistAnimState->setEnabled(true);
491
492                PlayerCharacter::frameListenerPriority = 20;
493       
494                gFPSPlayer = new FPSPlayer(NXScene, mSceneMgr, Vector3(0.4, 1.0, 0.35),/* Vector3(-0.6,-0.5,-24)*/Vector3(0, -0.5, 11.5), 1.15, mCamera, false);
495                /*gFPSPlayer->addPlayerMesh("FPSarm.mesh", 0.02, false);
496                gFPSPlayer->addWeaponModel("sword.mesh", "joint28");
497       
498                TrollCharacter* troll = new TrollCharacter("troll1", NXScene, mSceneMgr, Vector3(1.5, 3.0, 1.2), Vector3(15, 10, 15), false);
499                troll->addPlayerMesh("troll.mesh", 0.05, false);
500                troll->addWeaponModel("trollMace.mesh", "RightHand");
501
502                CharacterManager::getSingleton().mScene = NXScene;
503                CharacterManager::getSingleton().mSceneManager = mSceneMgr;*/
504
505                OgreIlluminationManager::getSingleton().initTechniques();
506   }
507       
508        void createPlane(Ogre::String _name, Ogre::String _material, Ogre::Vector3 _pos, Ogre::Vector2 _size, Ogre::Vector2 _subdivisions = Ogre::Vector2(1,1))
509        {
510                Plane _plane;
511        _plane.normal = Vector3::UNIT_Y;
512        _plane.d = 0;
513
514
515        MeshManager::getSingleton().createPlane(_name + ".plane",
516            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
517            _plane,_size.x,_size.y,1,1,true,1,_subdivisions.x,_subdivisions.y,Vector3::UNIT_Z);
518       
519                Entity *_entity = mSceneMgr->createEntity(_name + ".entity", _name + ".plane");
520        _entity->setMaterialName(_material);
521
522                SceneNode* _node = mSceneMgr->getRootSceneNode()->createChildSceneNode(_name + ".node");
523                _node->attachObject(_entity);
524                _node->setPosition(_pos);
525
526        }
527
528        void createFrameListener(void)
529    {
530                // This is where we instantiate our own frame listener
531                mFrameListener= new SpaceStationFrameListener(mWindow,
532                        mCamera, roomLightNode, roomLight, hallLight, pSysNode  );
533        mFrameListener->setPriority(10);       
534                mRoot->addFrameListener(mFrameListener);
535//              gFPSPlayer->setPriority(20);
536//              mRoot->addFrameListener(gFPSPlayer);
537                OgreIlluminationManager::getSingleton().setPriority(100);
538            mRoot->addFrameListener(&OgreIlluminationManager::getSingleton());
539         
540    }
541
542};
543
Note: See TracBrowser for help on using the repository browser.