Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/Samples/DeferredShading/src/DeferredShadingDemo.cpp @ 5

Last change on this file since 5 was 5, checked in by anonymous, 17 years ago

=hoffentlich gehts jetzt

  • Property svn:executable set to *
File size: 19.3 KB
Line 
1/**
2Demo of Deferred Shading in OGRE using Multiple Render Targets and HLSL/GLSL high level
3language shaders.
4        // W.J. :wumpus: van der Laan 2005 //
5
6Deferred shading renders the scene to a 'fat' texture format, using a shader that outputs colour,
7normal, depth, and possible other attributes per fragment. Multi Render Target is required as we
8are dealing with many outputs which get written into multiple render textures in the same pass.
9
10After rendering the scene in this format, the shading (lighting) can be done as a post process.
11This means that lighting is done in screen space. Adding them requires nothing more than rendering
12a screenful quad; thus the method allows for an enormous amount of lights without noticable
13performance loss.
14
15Little lights affecting small area ("Minilights") can be even further optimised by rendering
16their convex bounding geometry. This is also shown in this demo by 6 swarming lights.
17
18The paper for GDC2004 on Deferred Shading can be found here:
19  http://www.talula.demon.co.uk/DeferredShading.pdf
20
21This demo source file is in the public domain.
22*/
23
24#include "Ogre.h"
25#include "ExampleApplication.h"
26#include "ExampleFrameListener.h"
27
28#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
29#define WIN32_LEAN_AND_MEAN
30#include "windows.h"
31#endif
32
33#include "DeferredShading.h"
34#include "MLight.h"
35#include "CreateSphere.h"
36class SharedData : public Ogre::Singleton<SharedData> {
37
38public:
39
40        SharedData()
41                : iRoot(0),
42                  iCamera(0),
43                  iWindow(0),
44                  mAnimState(0),
45                  mMLAnimState(0),
46                  iLight1(0), iLight2(0)
47        {
48                iActivate = false;
49        }
50
51                ~SharedData() {}
52
53                // shared data across the application
54                Real iLastFrameTime;
55                Root *iRoot;
56                Camera *iCamera;
57                RenderWindow *iWindow;
58
59                DeferredShadingSystem *iSystem;
60                bool iActivate;
61                bool iGlobalActivate;
62
63                // Animation state for big lights
64                AnimationState* mAnimState;
65                // Animation state for light swarm
66                AnimationState* mMLAnimState;
67
68                // overlay stuff.. too lazy to do a good thing for it
69                SceneManager *iSceneMgr;
70                RenderTarget *iSceneTarget;
71
72                MLight *iLight1, *iLight2;
73
74                std::vector<Node*> mLightNodes;
75
76};
77template<> SharedData* Singleton<SharedData>::ms_Singleton = 0;
78
79class RenderToTextureFrameListener : public ExampleFrameListener
80{
81protected:
82        Real timeoutDelay ;
83        Vector3 oldCamPos;
84        Quaternion oldCamOri;
85        DeferredShadingSystem::DSMode mode;
86public:
87        RenderToTextureFrameListener(RenderWindow* window, Camera* maincam)
88                :ExampleFrameListener(window, maincam), 
89                oldCamPos(0,0,0), oldCamOri(0,0,0,0)
90        {
91                timeoutDelay = 0;
92                mMoveSpeed = 200;
93                mode = (DeferredShadingSystem::DSMode)1;
94        }
95
96        bool frameStarted(const FrameEvent& evt)
97        {
98                if( ExampleFrameListener::frameStarted(evt) == false )
99                        return false;
100
101                SharedData::getSingleton().iLastFrameTime = evt.timeSinceLastFrame;
102
103                if (SharedData::getSingleton().mAnimState)
104            SharedData::getSingleton().mAnimState->addTime(evt.timeSinceLastFrame);
105                if (SharedData::getSingleton().mMLAnimState)
106            SharedData::getSingleton().mMLAnimState->addTime(evt.timeSinceLastFrame);
107               
108                // Only update fat buffer if something changed
109                bool somethingChanged = false;
110                if(mCamera->getPosition()!=oldCamPos || mCamera->getOrientation()!=oldCamOri)
111                {
112                        somethingChanged = true;
113                        oldCamPos = mCamera->getPosition();
114                        oldCamOri = mCamera->getOrientation();
115                }
116               
117                if(somethingChanged)
118                        SharedData::getSingleton().iSystem->update();
119                return true;
120        }
121
122        virtual bool processUnbufferedKeyInput(const FrameEvent& evt) {
123                using namespace OIS;
124                bool retval = ExampleFrameListener::processUnbufferedKeyInput(evt);
125
126                // "C" switch filters
127                if (mKeyboard->isKeyDown(KC_C) && timeoutDelay==0) 
128                {
129                        timeoutDelay = 0.5f;
130
131                        mode = (DeferredShadingSystem::DSMode)((int)mode+1);
132                        if(mode == DeferredShadingSystem::DSM_COUNT)
133                                mode = (DeferredShadingSystem::DSMode)1;
134
135                        SharedData::getSingleton().iSystem->setMode( mode );
136
137                        updateOverlays();
138                }
139
140                // "B" activate/deactivate minilight rendering
141                if (mKeyboard->isKeyDown(KC_B) && timeoutDelay==0) 
142                {
143                        timeoutDelay = 0.5f;
144                        SharedData::getSingleton().iActivate = !SharedData::getSingleton().iActivate;
145                        // Hide/show all minilights
146                        std::vector<Node*>::iterator i = SharedData::getSingleton().mLightNodes.begin();
147                        std::vector<Node*>::iterator iend = SharedData::getSingleton().mLightNodes.end();
148                        for(; i!=iend; ++i)
149                        {
150                                static_cast<SceneNode*>(*i)->setVisible(SharedData::getSingleton().iActivate, true);
151                        }
152                       
153                        updateOverlays();
154                }
155                // "G" activate/deactivate global light rendering
156                if (mKeyboard->isKeyDown(KC_G) && timeoutDelay==0) 
157                {
158                        timeoutDelay = 0.5f;
159                        SharedData::getSingleton().iGlobalActivate = !SharedData::getSingleton().iGlobalActivate;
160                        SharedData::getSingleton().iLight1->setVisible(SharedData::getSingleton().iGlobalActivate);
161                        SharedData::getSingleton().iLight2->setVisible(SharedData::getSingleton().iGlobalActivate);
162                        updateOverlays();
163                }
164
165                timeoutDelay -= evt.timeSinceLastFrame;
166                if (timeoutDelay <= 0) timeoutDelay = 0;
167
168                return retval;
169        }
170
171        void updateOverlays() 
172        {
173
174                OverlayManager::getSingleton().getOverlayElement( "Example/Shadows/ShadowTechniqueInfo" )
175                        ->setCaption( "" );
176
177                OverlayManager::getSingleton().getOverlayElement( "Example/Shadows/MaterialInfo" )
178                        ->setCaption( "");
179
180                OverlayManager::getSingleton().getOverlayElement( "Example/Shadows/ShadowTechnique" )
181                        ->setCaption( "[B] MiniLights active: " + StringConverter::toString( SharedData::getSingleton().iActivate ) );
182
183                std::string name;
184                switch(mode)
185                {
186                case DeferredShadingSystem::DSM_SINGLEPASS:
187                        name="SinglePass"; break;
188                case DeferredShadingSystem::DSM_MULTIPASS:
189                        name="MultiPass"; break;
190                case DeferredShadingSystem::DSM_SHOWCOLOUR:
191                        name="ShowColour"; break;
192                case DeferredShadingSystem::DSM_SHOWNORMALS:
193                        name="ShowNormals"; break;
194                case DeferredShadingSystem::DSM_SHOWDSP:
195                        name="ShowDSP"; break;
196                }
197                OverlayManager::getSingleton().getOverlayElement( "Example/Shadows/Materials" )
198                        ->setCaption( "[C] Change mode, current is \"" + name + "\"");
199
200                OverlayManager::getSingleton().getOverlayElement( "Example/Shadows/Info" )
201                        ->setCaption( "[G] Global lights active: " + StringConverter::toString( SharedData::getSingleton().iGlobalActivate ) );
202
203        }
204};
205
206
207class RenderToTextureApplication : public ExampleApplication, public RenderTargetListener
208{
209public:
210    RenderToTextureApplication() : mPlane(0) {
211                new SharedData();
212                mPlane = 0;
213                mSystem = 0;
214        }
215   
216        ~RenderToTextureApplication()
217    {
218                delete ( SharedData::getSingletonPtr() );
219
220        delete mPlane;
221                delete mSystem;
222        }
223
224
225protected:
226    MovablePlane* mPlane;
227    Entity* mPlaneEnt;
228    SceneNode* mPlaneNode;
229        DeferredShadingSystem *mSystem;
230
231    // Just override the mandatory create scene method
232    void createScene(void)
233    {
234                RenderSystem *rs = Root::getSingleton().getRenderSystem();
235                const RenderSystemCapabilities* caps = rs->getCapabilities();
236        if (!caps->hasCapability(RSC_VERTEX_PROGRAM) || !(caps->hasCapability(RSC_FRAGMENT_PROGRAM)))
237        {
238            OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your card does not support vertex and fragment programs, so cannot "
239                "run this demo. Sorry!", 
240                "DeferredShading::createScene");
241        }
242                if (caps->numMultiRenderTargets()<2)
243        {
244            OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your card does not support at least two simulataneous render targets, so cannot "
245                "run this demo. Sorry!", 
246                "DeferredShading::createScene");
247        }
248                MovableObject::setDefaultVisibilityFlags(0x00000001);
249                mSceneMgr->setVisibilityMask(0x00000001);
250                // Prepare athene mesh for normalmapping
251        MeshPtr pAthene = MeshManager::getSingleton().load("athene.mesh", 
252            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
253        unsigned short src, dest;
254        if (!pAthene->suggestTangentVectorBuildParams(VES_TANGENT, src, dest))
255            pAthene->buildTangentVectors(VES_TANGENT, src, dest);
256                // Prepare knot mesh for normal mapping
257                pAthene = MeshManager::getSingleton().load("knot.mesh", 
258            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
259        if (!pAthene->suggestTangentVectorBuildParams(VES_TANGENT, src, dest))
260            pAthene->buildTangentVectors(VES_TANGENT, src, dest);
261
262        // Set ambient light
263        mSceneMgr->setAmbientLight(ColourValue(0.2, 0.2, 0.15));
264        // Skybox
265        mSceneMgr->setSkyBox(true, "Test13/SkyBox");
266
267                // Create "root" node
268                SceneNode* rootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
269
270        // Create a prefab plane
271        mPlane = new MovablePlane("ReflectPlane");
272        mPlane->d = 0;
273        mPlane->normal = Vector3::UNIT_Y;
274        MeshManager::getSingleton().createCurvedPlane("ReflectionPlane", 
275            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
276                        *mPlane,
277                        2000, 2000, -1000,
278            20, 20, 
279                        true, 1, 10, 10, Vector3::UNIT_Z);
280        mPlaneEnt = mSceneMgr->createEntity( "Plane", "ReflectionPlane" );
281                mPlaneNode = rootNode->createChildSceneNode();
282        mPlaneNode->attachObject(mPlaneEnt);
283        mPlaneNode->translate(-5, -30, 0);
284        //mPlaneNode->roll(Degree(5));
285        mPlaneEnt->setMaterialName("Test13/Ground");
286
287        // Create an entity from a model (will be loaded automatically)
288        Entity* knotEnt = mSceneMgr->createEntity("Knot", "knot.mesh");
289                knotEnt->setMaterialName("Test13/RockWall");
290                knotEnt->setMeshLodBias(0.25f);
291
292        // Create an entity from a model (will be loaded automatically)
293        Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
294                ogreHead->getSubEntity(0)->setMaterialName("Test13/DeferredOgre/Eyes");// eyes
295                ogreHead->getSubEntity(1)->setMaterialName("Test13/DeferredOgre/Skin"); 
296                ogreHead->getSubEntity(2)->setMaterialName("Test13/DeferredOgre/EarRing"); // earrings
297                ogreHead->getSubEntity(3)->setMaterialName("Test13/DeferredOgre/Tusks"); // tusks
298                rootNode->createChildSceneNode( "Head" )->attachObject( ogreHead );
299
300        Entity* athena = mSceneMgr->createEntity("Athena", "athene.mesh");
301                athena->setMaterialName("Test13/DeferredAthena");
302                SceneNode *aNode = rootNode->createChildSceneNode();
303                aNode->attachObject( athena );
304                aNode->setPosition(-100, 40, 100);
305
306        // Add a whole bunch of extra entities to fill the scene a bit
307        Entity *cloneEnt;
308                int N=4;
309        for (int n = 0; n < N; ++n)
310        {
311                        float theta = 2.0f*Math::PI*(float)n/(float)N;
312            // Create a new node under the root
313            SceneNode* node = mSceneMgr->createSceneNode();
314            // Random translate
315            Vector3 nodePos;
316            nodePos.x = Math::SymmetricRandom() * 40.0 + Math::Sin(theta) * 500.0;
317            nodePos.y = Math::SymmetricRandom() * 20.0 - 40.0;
318            nodePos.z = Math::SymmetricRandom() * 40.0 + Math::Cos(theta) * 500.0;
319            node->setPosition(nodePos);
320                        Quaternion orientation(Math::SymmetricRandom(),Math::SymmetricRandom(),Math::SymmetricRandom(),Math::SymmetricRandom());
321                        orientation.normalise();
322                        node->setOrientation(orientation);
323            rootNode->addChild(node);
324            // Clone knot
325            char cloneName[12];
326            sprintf(cloneName, "Knot%d", n);
327            cloneEnt = knotEnt->clone(cloneName);
328            // Attach to new node
329            node->attachObject(cloneEnt);
330
331        }
332
333        mCamera->setPosition(-50, 100, 500);
334        mCamera->lookAt(0,0,0);
335
336                // show overlay
337                Overlay* overlay = OverlayManager::getSingleton().getByName("Example/ShadowsOverlay");   
338                overlay->show();
339
340                mSystem = new DeferredShadingSystem(mWindow->getViewport(0), mSceneMgr, mCamera);
341
342               
343                // Create a light
344        MLight* l = mSystem->createMLight();//"SunLight");
345        //l->setType(Light::LT_POINT);
346        //l->setPosition(600.0, 1000.0, 200.0);
347        l->setDiffuseColour(1.0f, 0.6f, 0.2f);
348        l->setSpecularColour(0.3f, 0.15f, 0.06f);
349                //l->setDiffuseColour(0.0f, 0.0f, 0.0f);
350                //l->setSpecularColour(0.0f, 0.0f, 0.0f);
351               
352                // Create moving light
353                MLight* l2 = mSystem->createMLight();//"MainLight2");
354        //l2->setType(Light::LT_POINT);
355        l2->setDiffuseColour(0.75f, 0.7f, 0.8f);
356                l2->setSpecularColour(0.85f, 0.9f, 1.0f);
357               
358                SceneNode *lightNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
359                lightNode->attachObject(l2);
360
361                // Create a track for the light
362        Animation* anim = mSceneMgr->createAnimation("LightTrack", 16);
363        // Spline it for nice curves
364        anim->setInterpolationMode(Animation::IM_SPLINE);
365        // Create a track to animate the camera's node
366        NodeAnimationTrack* track = anim->createNodeTrack(0, lightNode);
367        // Setup keyframes
368        TransformKeyFrame* key = track->createNodeKeyFrame(0); // A startposition
369        key->setTranslate(Vector3(300,300,-300));
370        key = track->createNodeKeyFrame(4);//B
371        key->setTranslate(Vector3(300,300,300));
372        key = track->createNodeKeyFrame(8);//C
373        key->setTranslate(Vector3(-300,300,300));
374        key = track->createNodeKeyFrame(12);//D
375        key->setTranslate(Vector3(-300,300,-300));
376                key = track->createNodeKeyFrame(16);//D
377        key->setTranslate(Vector3(300,300,-300));
378        // Create a new animation state to track this
379        SharedData::getSingleton().mAnimState = mSceneMgr->createAnimationState("LightTrack");
380        SharedData::getSingleton().mAnimState->setEnabled(true);
381
382                // Create some happy little lights
383                createSampleLights();
384
385                // safely setup application's (not postfilter!) shared data
386                SharedData::getSingleton().iCamera = mCamera;
387                SharedData::getSingleton().iRoot = mRoot;
388                SharedData::getSingleton().iWindow = mWindow;
389                SharedData::getSingleton().iActivate = true;
390                SharedData::getSingleton().iGlobalActivate = true;
391                SharedData::getSingleton().iSceneMgr = mSceneMgr;
392                SharedData::getSingleton().iSystem = mSystem;
393                SharedData::getSingleton().iLight1 = l;
394                SharedData::getSingleton().iLight2 = l2;
395        }
396
397    void createFrameListener(void)
398    {
399        mFrameListener= new RenderToTextureFrameListener(mWindow, mCamera);
400                // initialize overlays
401                static_cast<RenderToTextureFrameListener*>(mFrameListener)->updateOverlays();
402        mRoot->addFrameListener(mFrameListener);
403    }
404
405        void createSampleLights()
406        {
407                // Create some lights           
408                std::vector<MLight*> lights;
409                SceneNode *parentNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("LightsParent");
410                // Create light nodes
411                std::vector<Node*> nodes;
412
413                MLight *a = mSystem->createMLight();
414                SceneNode *an = parentNode->createChildSceneNode();
415                an->attachObject(a);
416                a->setAttenuation(1.0f, 0.001f, 0.002f);
417                //a->setAttenuation(1.0f, 0.000f, 0.000f);
418                an->setPosition(0,0,25);
419                a->setDiffuseColour(1,0,0);
420                //a->setSpecularColour(0.5,0,0);
421                lights.push_back(a);
422                nodes.push_back(an);
423
424                MLight *b = mSystem->createMLight();
425                SceneNode *bn = parentNode->createChildSceneNode();
426                bn->attachObject(b);
427                b->setAttenuation(1.0f, 0.001f, 0.003f);
428                bn->setPosition(25,0,0);
429                b->setDiffuseColour(1,1,0);
430                //b->setSpecularColour(0.5,0.5,0);
431                lights.push_back(b);
432                nodes.push_back(bn);
433
434                MLight *c = mSystem->createMLight();
435                SceneNode *cn = parentNode->createChildSceneNode();
436                cn->attachObject(c);
437                c->setAttenuation(1.0f, 0.001f, 0.004f);
438                cn->setPosition(0,0,-25);
439                c->setDiffuseColour(0,1,1);
440                c->setSpecularColour(0.25,1.0,1.0); // Cyan light has specular component
441                lights.push_back(c);
442                nodes.push_back(cn);
443
444                MLight *d = mSystem->createMLight();
445                SceneNode *dn = parentNode->createChildSceneNode();
446                dn->attachObject(d);
447                d->setAttenuation(1.0f, 0.002f, 0.002f);
448                dn->setPosition(-25,0,0);
449                d->setDiffuseColour(1,0,1);
450                d->setSpecularColour(0.0,0,0.0);
451                lights.push_back(d);
452                nodes.push_back(dn);
453
454                MLight *e = mSystem->createMLight();
455                SceneNode *en = parentNode->createChildSceneNode();
456                en->attachObject(e);
457                e->setAttenuation(1.0f, 0.002f, 0.0025f);
458                en->setPosition(25,0,25);
459                e->setDiffuseColour(0,0,1);
460                e->setSpecularColour(0,0,0);
461                lights.push_back(e);
462                nodes.push_back(en);
463               
464                MLight *f = mSystem->createMLight();
465                SceneNode *fn = parentNode->createChildSceneNode();
466                fn->attachObject(f);
467                f->setAttenuation(1.0f, 0.0015f, 0.0021f);
468                fn->setPosition(-25,0,-25);
469                f->setDiffuseColour(0,1,0);
470                f->setSpecularColour(0,0.0,0.0);
471                lights.push_back(f);
472                nodes.push_back(fn);
473
474                // Create marker meshes to show user where the lights are
475                Entity *ent;
476                createSphere("PointLightMesh", 1.0f, 5, 5);
477                for(std::vector<MLight*>::iterator i=lights.begin(); i!=lights.end(); ++i)
478                {
479                        ent = mSceneMgr->createEntity((*i)->getName()+"v", "PointLightMesh");
480                        String matname = (*i)->getName()+"m";
481                        // Create coloured material
482                        MaterialPtr mat = MaterialManager::getSingleton().create(matname,
483                ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
484            Pass* pass = mat->getTechnique(0)->getPass(0);
485            pass->setDiffuse(0.0f,0.0f,0.0f,1.0f);
486                        pass->setAmbient(0.0f,0.0f,0.0f);
487                        pass->setSelfIllumination((*i)->getDiffuseColour());
488
489                        ent->setMaterialName(matname);
490                        ent->setVisibilityFlags(DeferredShadingSystem::PostVisibilityMask);
491                        static_cast<SceneNode*>((*i)->getParentNode())->attachObject(ent);
492                }               
493
494                // Store nodes for hiding/showing
495                SharedData::getSingleton().mLightNodes = nodes;
496
497                // Do some animation for node a-f
498                // Generate helix structure
499                float seconds_per_station = 1.0f;
500                float r=35;
501                //Vector3 base(0,-30,0);
502                Vector3 base(-100, -30, 85);
503
504                float h=120;
505                const size_t s_to_top = 16;
506                const size_t stations = s_to_top*2-1;
507                float ascend = h/((float)s_to_top);
508                float stations_per_revolution = 3.5f;
509                size_t skip = 2; // stations between lights
510                Vector3 station_pos[stations];
511                for(int x=0; x<s_to_top; ++x)
512                {
513                        float theta = ((float)x/stations_per_revolution)*2.0f*Math::PI;
514                        station_pos[x] = base+Vector3(Math::Sin(theta)*r, ascend*x, Math::Cos(theta)*r);
515                }
516                for(int x=s_to_top; x<stations; ++x)
517                {
518                        float theta = ((float)x/stations_per_revolution)*2.0f*Math::PI;
519                        station_pos[x] = base+Vector3(Math::Sin(theta)*r, h-ascend*(x-s_to_top), Math::Cos(theta)*r);
520                }
521                // Create a track for the light swarm
522                Animation* anim = mSceneMgr->createAnimation("LightSwarmTrack", stations*seconds_per_station);
523                // Spline it for nice curves
524                anim->setInterpolationMode(Animation::IM_SPLINE);
525                for(unsigned int x=0; x<nodes.size(); ++x)
526                {
527                        // Create a track to animate the camera's node
528                        NodeAnimationTrack* track = anim->createNodeTrack(x, nodes[x]);
529                        for(int y=0; y<=stations; ++y)
530                        {
531                                // Setup keyframes
532                                TransformKeyFrame* key = track->createNodeKeyFrame(y*seconds_per_station); // A startposition
533                                key->setTranslate(station_pos[(x*skip+y)%stations]);
534                                // Make sure size of light doesn't change
535                                key->setScale(nodes[x]->getScale());
536                        }
537                }
538                // Create a new animation state to track this
539                SharedData::getSingleton().mMLAnimState = mSceneMgr->createAnimationState("LightSwarmTrack");
540                SharedData::getSingleton().mMLAnimState->setEnabled(true);
541        }
542
543};
544
545
546
547
548
549#ifdef __cplusplus
550extern "C" {
551#endif
552
553//#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
554//      INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
555//#else
556        int main(int argc, char *argv[])
557//#endif
558        {
559                // Create application object
560                RenderToTextureApplication app;
561
562                try {
563                        app.go();
564                } catch( Ogre::Exception& e ) {
565#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
566                        MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
567#else
568                        std::cerr << "An exception has occured: " <<
569                                e.getFullDescription().c_str() << std::endl;
570#endif
571                }
572
573                return 0;
574        }
575
576#ifdef __cplusplus
577}
578#endif
579
580
Note: See TracBrowser for help on using the repository browser.