source: GTP/trunk/App/Demos/Vis/HillyTerrain/OGRE/TerrainFrameListener.cpp @ 2501

Revision 2501, 57.4 KB checked in by mattausch, 17 years ago (diff)
Line 
1#include <OgreNoMemoryMacros.h>
2#include <CEGUI/CEGUI.h>
3#include <../CEGUIRenderer/include/OgreCEGUIRenderer.h>
4#include <../CEGUIRenderer/include/OgreCEGUIResourceProvider.h>
5#include <../CEGUIRenderer/include/OgreCEGUITexture.h>
6#include <OgreMemoryMacros.h>
7#include "TerrainFrameListener.h"
8#include "OgrePlatformQueryManager.h"
9#include "VisibilityInfo.h"
10#include "OgreOcclusionQueriesQueryManager.h"
11#include "TestCullingTerrainApplication.h"
12#include <string>
13
14
15//-- captions for overlays
16String TerrainFrameListener::msAlgorithmCaptions[] =
17{
18        "Coherent Hierarchical Culling",
19        "View Frustum Culling",
20        "Random Update Culling",
21        "Stop and Wait Culling",
22        "Standard Rendering"
23};
24
25//-- captions for overlays
26String TerrainFrameListener::msApprevAlgorithmCaptions[] =
27{
28        "CHC",
29        "VFC",
30        "RUC",
31        "SWC",
32        "DEF",
33        "INT"
34};
35
36
37String TerrainFrameListener::msQueryTypeCaptions[] =
38{
39        "from camera",
40        "from viewpoint"
41};
42
43String TerrainFrameListener::msQueryRelativeVisCaptions[] =
44{
45        "visible pixels",
46        "relative visibility"
47};
48
49String TerrainFrameListener::msQueryMethodCaptions[] =
50{
51        "occlusion queries",
52        "item buffer"
53};
54
55Real TerrainFrameListener::msObjectTerrainOffsets[] =
56{
57        0,
58        -0.1,
59        //7,
60        0
61};
62
63Real TerrainFrameListener::msObjectScales[] =
64{
65        0.07,
66        0.03,
67        //0.1,
68        0.03
69};
70
71String TerrainFrameListener::msObjectCaptions[] =
72{
73        "robot",
74        "athene",
75        "natFX_Tree1_LOD2",
76        //"tree2",
77        //"HongKong_Tower",
78        "ninja"
79        //"ogrehead"
80};
81
82// output file for frame info
83const char* frames_out_filename = "frame.out";
84// output file for object positions / orientations
85const char* objects_out_filename = "objects.out";
86       
87std::ofstream video_out("video.lst");
88
89// if we are recording a demo
90static const bool DEMO_HACK = false;
91
92//using namespace GtpVisibility;
93
94//-----------------------------------------------------------------------
95TerrainFrameListener::TerrainFrameListener(RenderWindow* win, Camera* cam,
96                                                                                   SceneManager *sceneManager,
97                                                                                   CEGUI::Renderer *renderer,
98                                                                                   TerrainContentGenerator *sceneGenerator,
99                                                                                   Camera *vizCamera,
100                                                                                   SceneNode *camNode,
101                                                                                   Light *sunLight,
102                                                                                   TestCullingTerrainApplication *app):
103mCamera(cam),
104mWindow(win),
105mNumScreenShots(0),
106mTimeDelay(0),
107mSceneDetailIndex(0),
108mMoveScale(0.0f),
109mRotScale(0.0f),
110mTranslateVector(Vector3::ZERO),
111mAniso(1),
112mFiltering(TFO_BILINEAR),
113mGUIRenderer(renderer),
114mSceneMgr(sceneManager),
115mCurrentObject(NULL),
116mTerrainContentGenerator(sceneGenerator),
117mVisibilityThreshold(0),
118mAssumedVisibility(0),
119mCurrentAlgorithm(GtpVisibility::VisibilityEnvironment::COHERENT_HIERARCHICAL_CULLING),
120mNodeVizMode(NODEVIZ_NONE),
121mVizCameraHeight(Real(2000.0)),
122mCamNode(camNode),
123mAppState(WALKTHROUGH),
124mCurrentFrame(0),
125mReplayTimeElapsed(0),
126mRotateSpeed(72),
127mMoveSpeed(50),
128mVizCamera(vizCamera),
129mStatsOn(true),
130mShutdownRequested(false),
131mLMouseDown(false),
132mRMouseDown(false),
133mShowOctree(false),
134mShowViewCells(false),
135mUseDepthPass(false),
136mTestGeometryForVisibleLeaves(false),
137mTestGeometryBounds(false),
138mShowVisualization(false),
139mCullCamera(false),
140mRecordFrames(false),
141mShowShadows(false),
142mShowHelp(false),
143mDisplayCameraDetails(false),
144mVisualizeCulledNodes(false),
145mSunLight(sunLight),
146mShiftPressed(false),
147mShowQueryStats(false),
148mQueryManager(NULL),
149mVisibilityManager(NULL),
150mDelayedQueriesIssued(0.0),
151mDelayedTraversedNodes(0.0),
152mCurrentObjectType(0),
153mApplication(app),
154mUseAnimation(false),
155mDeleteObjects(false),
156mUseItemBuffer(false),
157mItemBufferMode(GtpVisibility::QueryManager::PATCH_VISIBILITY),
158mRecordVideo(false),
159mPureRenderTimeFps(0.0),
160mNumVideoFrames(0),
161mPrecomputedFps(0),
162mRecordDemo(false),
163mSavePrecomputedFps(false),
164mUseBufferedInputMouse(false),
165mVizScale(25),
166mUseViewCells(false),
167mViewCellsLoaded(false),
168mFloorDist(2),
169mFlushQueue(false),
170m_iShowBoundingBoxes(0),
171m_iBoundingBoxLevel(0),
172mMoveFactor(1000)
173{
174        mEventProcessor = new EventProcessor();
175       
176        mEventProcessor->initialise(win);
177        mEventProcessor->startProcessingEvents();
178        mEventProcessor->addMouseListener(this);
179        mEventProcessor->addMouseMotionListener(this);
180        mEventProcessor->addKeyListener(this);
181
182        mInputDevice = mEventProcessor->getInputReader();       
183
184        mInputDevice->setBufferedInput(true, mUseBufferedInputMouse);
185
186        // create ray query executor, used to place objects in terrain
187        mRayQueryExecutor = new RayQueryExecutor(mSceneMgr);
188       
189
190        //////////
191        //-- overlays
192       
193        mDebugOverlay = OverlayManager::getSingleton().getByName("Core/DebugOverlay");
194        mHelpOverlay = OverlayManager::getSingleton().getByName("Example/Visibility/HelpOverlay");
195        mQueryOverlay = OverlayManager::getSingleton().getByName("Example/Visibility/QueryOverlay");
196        mCullStatsOverlay = OverlayManager::getSingleton().getByName("Example/Visibility/CullStatsOverlay");
197
198        String ext = "Example/Visibility/";
199
200        //-- overlays
201   
202        initVisStatsOverlay(); // visibility stats overlay
203        initHelpOverlay();     // help overlay
204        initQueryOverlay();    // visibility query stats overlay
205       
206        // show stats overlays
207        if (!DEMO_HACK)
208        {
209                showStats(true);
210        }
211        else
212        {
213                mMyStatsOverlay = OverlayManager::getSingleton().getByName("Example/Visibility/MyStatsOverlay");
214                mMyStatsAlgorithmInfo = OverlayManager::getSingleton().getOverlayElement("Example/Visibility/MyAlgorithmInfo");
215                mMyStatsAlgorithmInfo->setCaption("");
216       
217                const int top = 10;
218                mMyStatsAlgorithmInfo->setTop(top);
219
220                char str[100]; sprintf(str,": %d", 0);
221                mMyStatsFpsInfo = OverlayManager::getSingleton().getOverlayElement("Example/Visibility/MyFpsInfo");
222                mMyStatsFpsInfo->setCaption(str);
223
224                mMyStatsOverlay->show();
225        }
226
227        // loading view cells overlay
228        mLoadingOverlay = OverlayManager::getSingleton().getByName("Example/Visibility/LoadingOverlay");
229        mMyLoadingInfo = OverlayManager::getSingleton().getOverlayElement("Example/Visibility/Loading/MyLoadingInfo");
230        mMyLoadingInfo->setCaption("loading view cells ...");
231
232        mLoadingOverlay->hide();
233
234        // hack: larger magnification for terrain to show single objects
235        if (TestCullingTerrainApplication::msShowHillyTerrain)
236                mVizScale = 25;
237        else
238                mVizScale = 1;
239
240        // the scale factor for the visualized bounding boxes
241        mSceneMgr->setOption("NodeVizScale", &mVizScale);
242
243        int assumedIncr;
244        mSceneMgr->getOption("AssumedVisibility", &assumedIncr);
245        changeAssumedVisibility(assumedIncr);
246
247        /////
248        // test geometry boundaries instead bounding box
249        mSceneMgr->getOption("TestGeometryBounds", &mTestGeometryBounds);
250        setTestGeometryBounds(mTestGeometryBounds);
251
252        /////////////////////////////////
253        // culling algorithm
254
255        bool isNormalExecution;
256               
257        mSceneMgr->getOption("NormalExecution", &isNormalExecution);
258       
259        if (isNormalExecution)
260        {
261                // no algorithm
262                mCurrentAlgorithm = GtpVisibility::VisibilityEnvironment::NUM_CULLING_MANAGERS;
263        }
264        else
265        {
266                mSceneMgr->getOption("Algorithm", &mCurrentAlgorithm);
267        }
268       
269        // apply the chosen culling algorithm
270        applyCurrentAlgorithm();
271       
272
273        ///////////////////////////////
274        // set scene manager options
275       
276        //////////
277        mSceneMgr->getOption("TestGeometryForVisibleLeaves", &mTestGeometryForVisibleLeaves);
278        setTestGeometryForVisibleLeaves(mTestGeometryForVisibleLeaves);
279
280        /////////////
281        mSceneMgr->getOption("UseDepthPass", &mUseDepthPass);
282
283        if (mUseDepthPass)
284                mUseDepthPassInfo->setCaption(": true");
285        else
286                mUseDepthPassInfo->setCaption(": false");
287       
288        mSceneMgr->getOption("FlushQueue", &mFlushQueue);
289       
290        //////////
291        if (mFlushQueue)
292                LogManager::getSingleton().logMessage("initial setting: flushing queue");
293        else
294                LogManager::getSingleton().logMessage("initial setting: not flushing queue");
295
296        /////////////
297        mSceneMgr->setOption("ShowOctree", &mShowOctree);
298        mSceneMgr->setOption("ShowViewCells", &mShowViewCells);
299        mSceneMgr->setOption("CullCamera", &mCullCamera);
300        mSceneMgr->setOption("PrepareVisualization", &mShowVisualization);
301       
302        applyObjectType();
303
304
305        ///////////////
306        // initialize timer
307
308        mTimer = Root::getSingleton().getTimer();
309        mTimeFrameEnded = mTimeFrameStarted = mTimer->getMilliseconds();
310       
311        // reset statistics
312        mWalkthroughStats.Reset();
313
314}
315//-----------------------------------------------------------------------
316void TerrainFrameListener::switchMouseMode()
317{
318        mUseBufferedInputMouse = !mUseBufferedInputMouse;
319        mInputDevice->setBufferedInput(true, mUseBufferedInputMouse);
320
321        if (!mUseBufferedInputMouse)
322                CEGUI::MouseCursor::getSingleton().hide();
323        else
324                CEGUI::MouseCursor::getSingleton().show();
325}
326//-----------------------------------------------------------------------
327TerrainFrameListener::~TerrainFrameListener()
328{
329        OGRE_DELETE(mRayQueryExecutor);
330        OGRE_DELETE(mEventProcessor);
331        OGRE_DELETE(mQueryManager);
332}
333//-----------------------------------------------------------------------
334void TerrainFrameListener::mouseMoved(MouseEvent *e)
335{
336        // Update CEGUI with the mouse motion
337    CEGUI::System::getSingleton().injectMouseMove(e->getRelX() *
338                mGUIRenderer->getWidth(), e->getRelY() * mGUIRenderer->getHeight());
339}
340//-----------------------------------------------------------------------
341void TerrainFrameListener::mousePressed(MouseEvent* e)
342{
343        // Left mouse button down
344        if (e->getButtonID() & InputEvent::BUTTON0_MASK)
345        {
346                CEGUI::MouseCursor::getSingleton().hide();
347
348                // Setup the ray scene query
349                Ray mouseRay = mCamera->getCameraToViewportRay(e->getX(), e->getY());
350                Real val = Math::RangeRandom(0, 360); // random rotation
351
352                // get results, create a node/entity on the position
353                mCurrentObject =
354                        mTerrainContentGenerator->GenerateSceneObject(mouseRay.getOrigin(),
355                                                                                                                  Vector3(val, 0, 0),
356                                                                                                                  msObjectCaptions[mCurrentObjectType]);
357               
358                mLMouseDown = true;
359        }
360        // Right mouse button down
361        /*else if (e->getButtonID() & InputEvent::BUTTON1_MASK)
362        {
363         CEGUI::MouseCursor::getSingleton().hide();
364         mRMouseDown = true;
365        }*/
366}
367//-----------------------------------------------------------------------
368void TerrainFrameListener::mouseDragDropped(MouseEvent* e)
369{
370        // Left mouse button up
371    if (e->getButtonID() & InputEvent::BUTTON0_MASK)
372    {
373                CEGUI::MouseCursor::getSingleton().show();
374               
375            mLMouseDown = false;
376    }
377    // Right mouse button up
378    else if (e->getButtonID() & InputEvent::BUTTON1_MASK)
379    {
380        CEGUI::MouseCursor::getSingleton().show();
381
382                mRMouseDown = false;
383    }
384}
385//-----------------------------------------------------------------------
386void TerrainFrameListener::mouseReleased(MouseEvent* e)
387{
388    // Left mouse button up
389    if (e->getButtonID() & InputEvent::BUTTON0_MASK)
390    {
391                CEGUI::MouseCursor::getSingleton().show();
392               
393                // start animation: only robot has animation phases
394                if (mCurrentObject && (mCurrentObjectType == TestCullingTerrainApplication::ROBOT))
395                {
396                        // HACK: not neccesary the last element
397                        Entity *ent = mTerrainContentGenerator->GetGeneratedEntities()->back();
398                        EntityState *entState = new EntityState(ent, EntityState::MOVING, Math::RangeRandom(0.5, 1.5));
399                        mApplication->getEntityStates().push_back(entState);
400                }
401
402            mLMouseDown = false;
403    }
404    // Right mouse button up
405    else if (e->getButtonID() & InputEvent::BUTTON1_MASK)
406    {
407        CEGUI::MouseCursor::getSingleton().show();
408
409        mRMouseDown = false;
410    }
411}
412//-----------------------------------------------------------------------
413void TerrainFrameListener::mouseDragged(MouseEvent *e)
414{
415        // If we are dragging the left mouse button.             
416        if (mLMouseDown)
417    {
418                if (!mCurrentObject)
419                        return;
420
421                Vector3 queryResult;
422                Ray mouseRay = mCamera->getCameraToViewportRay(e->getX(), e->getY());
423
424                if (mRayQueryExecutor->executeRayQuery(&queryResult, mouseRay))
425                {
426                        // apply offset so object is ON terrain
427                        queryResult.y += msObjectTerrainOffsets[mCurrentObjectType];
428                        mCurrentObject->setPosition(queryResult);
429                }
430        }
431}
432//-----------------------------------------------------------------------
433bool TerrainFrameListener::frameStarted(const FrameEvent &evt)
434{
435        if (mWindow->isClosed())
436        {
437        return false;
438        }
439       
440        if (mDeleteObjects)
441        {
442                mApplication->deleteEntityStates();
443                mTerrainContentGenerator->RemoveGeneratedObjects();
444                mDeleteObjects = false;
445        }
446
447        if (mUseAnimation)
448        {
449                // update animation phases
450                mApplication->updateAnimations(evt.timeSinceLastFrame);
451        }
452
453        if (mDisplayCameraDetails)  // Print camera details
454    {       
455        mWindow->setDebugText("P: " + StringConverter::toString(mCamera->getDerivedPosition()) +
456                        " " + "O: " + StringConverter::toString(mCamera->getDerivedOrientation()));
457    }
458
459        //-- setup what is needed for immediate mouse/key movement
460        if (mTimeDelay >= 0)
461        {
462                mTimeDelay -= evt.timeSinceLastFrame;
463        }
464       
465        // If this is the first frame, pick a speed
466        if (evt.timeSinceLastFrame == 0)
467        {
468                mMoveScale = 1;
469                mRotScale = 0.1;
470        }
471        // Otherwise scale movement units by time passed since last frame
472        else
473        {
474                // Move about 100 units per second,
475                mMoveScale = mMoveSpeed * evt.timeSinceLastFrame;
476                // Take about 10 seconds for full rotation
477                mRotScale = mRotateSpeed * evt.timeSinceLastFrame;
478        }
479
480        mRotX = 0;
481        mRotY = 0;
482        mTranslateVector = Vector3::ZERO;
483
484        if (!processUnbufferedKeyInput(evt))
485        {
486                return false;
487        }
488        if (!mUseBufferedInputMouse)
489        {
490                if (!processUnbufferedMouseInput(evt))
491                {       
492                        return false;   
493                }
494        }
495
496        if (mShowVisualization)
497        {
498                ////////////////
499                //-- set parameters for visualization
500
501                // draw octree bounding boxes because
502                // interesting for chc visualization
503                if (mNodeVizMode != NODEVIZ_RENDER_PVS)
504                {
505                        mSceneMgr->setOption("ShowOctree", &mShowVisualization);
506                }
507               
508                ///////////////
509                //-- setup visualization camera
510
511                mVizCamera->setPosition(0, 0, 0);
512                mVizCamera->setOrientation(Quaternion::IDENTITY);
513
514                Vector3 camPos = mCamNode->getPosition();
515                mVizCamera->setPosition(camPos.x, mVizCameraHeight, camPos.z);
516
517                // point down -Z axis
518                mVizCamera->pitch(Radian(Degree(270.0)));
519
520                // rotation arounnd X axis
521                mVizCamera->yaw(Math::ATan2(-mCamera->getDerivedDirection().x,
522                        -mCamera->getDerivedDirection().z));
523               
524                // move by a constant so view plane is on "bottom" of viewport
525                mVizCamera->moveRelative(Vector3(0, mMoveFactor, 0));
526        }
527        else
528        {
529                // frame start time
530                mTimeFrameStarted = mTimer->getMilliseconds();
531        }
532
533        //////////////
534        //-- set application state
535
536        switch (mAppState)
537        {
538        case REPLAY:
539                /// set the current camera data to loaded frame information
540                setCurrentFrameInfo(evt.timeSinceLastFrame);
541
542                // HACK for demo: save new frame rates for the same walkthrough
543                if (mSavePrecomputedFps)
544                {
545                        addFrameInfo(mPrecomputedFpsFrameInfo, mCamNode, evt.timeSinceLastFrame);
546                }
547
548                break;
549        case WALKTHROUGH:
550                //-- recording the camera settings per frame
551                if (mRecordFrames)
552                {
553                        addFrameInfo(mFrameInfo, mCamNode, evt.timeSinceLastFrame);
554                        // print recording message
555                        mWindow->setDebugText("Recording frame " +
556                                StringConverter::toString((int)mFrameInfo.size() - 1));
557                }
558
559                // move camera according to input
560                moveCamera();
561
562                // clamp camera so we always walk along the terrain
563                if (TestCullingTerrainApplication::msShowHillyTerrain)
564                {
565                        mApplication->Clamp2Terrain(mCamNode, 5);
566                        //Ogre::LogManager::getSingleton().logMessage("clamp to terrain");
567                }
568                else
569                {
570                        mApplication->Clamp2FloorPlane(mFloorDist);
571                        //Ogre::LogManager::getSingleton().logMessage("clamp to floor");
572                }
573
574                break;
575
576        default:
577                Ogre::LogManager::getSingleton().logMessage("should not come here");
578                break;
579        };     
580
581        return true;
582}
583//-----------------------------------------------------------------------
584void TerrainFrameListener::applyVisibilityQuery(bool fromPoint,
585                                                                                                bool relativeVisibility,
586                                                                                                bool useItemBuffer)
587{
588        int itemBufferMode = useItemBuffer ? mItemBufferMode : 0;
589       
590        int queryModes = 0;
591        queryModes |= GtpVisibility::QueryManager::PATCH_VISIBILITY;
592        queryModes |= GtpVisibility::QueryManager::GEOMETRY_VISIBILITY;
593        queryModes |= GtpVisibility::QueryManager::NODE_VISIBILITY;
594
595        // no visibility manager available => no visibility scene manager, return
596        GtpVisibility::VisibilityManager *visManager = NULL;   
597        if (!mSceneMgr->getOption("VisibilityManager", &visManager))
598        {
599                Ogre::LogManager::getSingleton().logMessage("no vismanager found");
600                return;
601        }
602
603        PlatformHierarchyInterface *hierarchyInterface = NULL;
604        if (!mSceneMgr->getOption("HierarchyInterface", &hierarchyInterface))
605        {
606                Ogre::LogManager::getSingleton().logMessage("no hierarchy interface found");
607                return;
608        }
609
610        const bool approximateVisibility = false;
611
612        mQueryManager = new OcclusionQueriesQueryManager(hierarchyInterface,
613                                                                                                         mWindow->getViewport(0),
614                                                                                                         queryModes,
615                                                                                                         itemBufferMode);
616
617        visManager->SetQueryManager(mQueryManager);
618
619        NodeInfoContainer visibleNodes;
620        MeshInfoContainer visibleGeometry;
621        PatchInfoContainer visiblePatches;
622
623        if (fromPoint)
624        {
625                mQueryManager->ComputeFromPointVisibility(mCamNode->getPosition(),
626                                                                                                  &visibleNodes,
627                                                                                                  &visibleGeometry,
628                                                                                                  &visiblePatches,
629                                                                                                  relativeVisibility,
630                                                                                                  approximateVisibility);
631        }
632        else
633        {
634                mQueryManager->ComputeCameraVisibility(*mCamera,
635                                                                                           &visibleNodes,
636                                                                                           &visibleGeometry,
637                                                                                           &visiblePatches,
638                                                                                           relativeVisibility,
639                                                                                           approximateVisibility);
640        }
641               
642        std::stringstream d;
643        d << "Query mode: " << queryModes << ", "
644          << msQueryTypeCaptions[fromPoint ?  1 : 0].c_str() << " "
645          << msQueryRelativeVisCaptions[relativeVisibility ? 1 : 0].c_str() << " "
646      << msQueryMethodCaptions[useItemBuffer ? 1 : 0].c_str();
647        LogManager::getSingleton().logMessage(d.str());
648
649
650        float averageNodeVis = 0, averageGeometryVis = 0, averagePatchVis = 0;
651        int geomSize = 0, nodesSize = 0, patchSize = 0;
652
653        /////////////////
654        //-- apply queries on geometry level
655
656        MeshInfoContainer::iterator geomIt, geomIt_end = visibleGeometry.end();
657
658        for (geomIt = visibleGeometry.begin(); geomIt != geomIt_end; ++geomIt)
659        {
660                // add if not 0
661                if ((*geomIt).GetVisiblePixels())
662                {
663                        float vis = relativeVisibility ?
664                                (*geomIt).ComputeRelativeVisibility() : (float)(*geomIt).GetVisiblePixels();
665       
666                        averageGeometryVis += vis;
667                        ++ geomSize;
668                       
669                        std::stringstream d;
670                        d << "Geometry " << geomSize << " id: " << (*geomIt).GetSource()->getSubEntity(0)->getId()
671                          << " visibility: "  << (*geomIt).GetVisiblePixels() << ", " << (*geomIt).GetProjectedPixels();
672                        LogManager::getSingleton().logMessage(d.str());
673                }
674        }
675
676
677        ////////////////
678        //-- apply queries on node level
679
680        NodeInfoContainer::iterator nodesIt, nodesIt_end = visibleNodes.end();
681
682        for (nodesIt = visibleNodes.begin(); nodesIt != nodesIt_end; ++nodesIt)
683        {
684                // add if not 0
685                if ((*nodesIt).GetVisiblePixels())
686                {
687                        float vis = relativeVisibility ?
688                                (*nodesIt).ComputeRelativeVisibility() : (float)(*nodesIt).GetVisiblePixels();
689               
690                        averageNodeVis += vis;
691                        ++ nodesSize;
692
693                        std::stringstream d; d << "Node visibility: " << vis;
694                        LogManager::getSingleton().logMessage(d.str());
695                }       
696        }
697
698
699        ////////////////
700        //-- apply queries on patch level
701
702
703        PatchInfoContainer::iterator patchIt, patchIt_end = visiblePatches.end();
704
705        for (patchIt = visiblePatches.begin(); patchIt != patchIt_end; ++ patchIt)
706        {
707                // add if not 0
708                if ((*patchIt).GetVisiblePixels())
709                {
710                        float vis = relativeVisibility ?
711                                (*patchIt).ComputeRelativeVisibility() : (float)(*patchIt).GetVisiblePixels();
712               
713                        averagePatchVis += vis;
714                        ++ patchSize;
715
716                        std::stringstream d; d << "Patch visibility: " << vis;
717                        LogManager::getSingleton().logMessage(d.str());
718                }       
719        }
720
721        ///////////////////////////////////////////////////////////////
722        //-- update visibility queries stats
723
724        if (nodesSize)
725                averageNodeVis /= (float)nodesSize;
726        if (geomSize)
727                averageGeometryVis /= (float)geomSize;
728        if (patchSize)
729                averagePatchVis /= (float)patchSize;
730
731
732    try
733        {
734                char str[100];
735               
736                sprintf(str, ": %s, %s, %s",
737                                msQueryTypeCaptions[fromPoint ?  1 : 0].c_str(),
738                                msQueryRelativeVisCaptions[relativeVisibility ? 1 : 0].c_str(),
739                                msQueryMethodCaptions[useItemBuffer ? 1 : 0].c_str());
740
741                mQueryTypeInfo->setCaption(str);
742
743                sprintf(str, ": %d", (int)nodesSize);
744                mQueryVisibleNodesInfo->setCaption(str);
745       
746                sprintf(str,": %d", (int)geomSize);
747                mQueryVisibleGeometryInfo->setCaption(str);
748               
749                sprintf(str,": %d", (int)patchSize);
750                mQueryVisiblePatchInfo->setCaption(str);
751
752                sprintf(str,": %3.3f", averageNodeVis);
753                mQueryNodeVisibilityInfo->setCaption(str);
754
755                sprintf(str,": %3.3f", averageGeometryVis);
756                mQueryGeometryVisibilityInfo->setCaption(str);
757
758                sprintf(str,": %3.3f", averagePatchVis);
759                mQueryPatchVisibilityInfo->setCaption(str);
760        }
761        catch (...)
762        {
763                // ignore
764        }
765
766        // show the results
767        if (!mShowQueryStats && !mShowHelp)
768        {
769                mQueryOverlay->show();
770                mShowQueryStats = true;
771        }
772
773        delete mQueryManager;
774}
775
776
777bool TerrainFrameListener::processUnbufferedMouseInput(const FrameEvent& evt)
778{
779        // Rotation factors, may not be used if the second mouse button is pressed.
780    // If the second mouse button is pressed, then the mouse movement results in
781    // sliding the camera, otherwise we rotate.
782    if (mInputDevice->getMouseButton(1))
783    {
784                mTranslateVector.x += mInputDevice->getMouseRelativeX() * 0.13;
785                mTranslateVector.y -= mInputDevice->getMouseRelativeY() * 0.13;
786        }
787        else
788        {
789                mRotX = Degree(-mInputDevice->getMouseRelativeX() * 0.13);
790                mRotY = Degree(-mInputDevice->getMouseRelativeY() * 0.13);
791        }
792
793        return true;
794}
795//-----------------------------------------------------------------------
796bool TerrainFrameListener::frameEnded(const FrameEvent& evt)
797{
798        if (mShutdownRequested)
799                return false;
800
801        // timer end time
802        if (!mShowVisualization)
803        {
804                mTimeFrameEnded = mTimer->getMilliseconds();
805        }
806
807    updateStats();
808
809        if (mRecordVideo) // record current frame
810        {
811                takeVideoFrame(video_out);
812        }
813
814        //-- IMPORTANT: must be set, otherwise terrain is not rendered correctly
815        mSceneMgr->endFrame();
816
817        if (mTimeDelay <= 0) // simulates approx. one second
818                mTimeDelay = 1.0;
819
820        return true;
821}
822//-----------------------------------------------------------------------
823void TerrainFrameListener::moveCamera()
824{
825        // move node rather than camera so orientation is right in the visualization
826        mCamNode->yaw(mRotX, Ogre::Node::TS_WORLD);
827        //mCamNode->rotate(Vector3(0,1,0), mRotX, Ogre::Node::TS_WORLD);
828        mCamNode->pitch(mRotY);
829
830        mCamNode->translate(mCamNode->getLocalAxes(), mTranslateVector);
831}
832//-----------------------------------------------------------------------
833void TerrainFrameListener::writeFrames(const std::string filename,
834                                                                           const FrameInfoContainer &frameInfo) const
835{
836        std::ofstream ofstr(filename.c_str());
837        std::vector<frame_info>::const_iterator it, it_end;
838        it_end = frameInfo.end();
839
840        int i = 0;
841
842        for (it = frameInfo.begin(); it < it_end; ++ it, ++ i)
843        {
844                ofstr << StringConverter::toString((*it).position) << " "
845                          << StringConverter::toString((*it).orientation) << " "
846                          << StringConverter::toString((*it).timeElapsed) << " "
847                          << StringConverter::toString((*it).fps) << "\n";
848        }
849
850        std::stringstream d; d << "saved " << i << " frames to file " << filename;
851        Ogre::LogManager::getSingleton().logMessage(d.str());
852
853        ofstr.close();
854}
855//-----------------------------------------------------------------------
856void TerrainFrameListener::loadFrames(const std::string filename, 
857                                                                          FrameInfoContainer &frameInfo)
858{
859        std::ifstream ifstr(filename.c_str());
860        char line[256];
861        frame_info info;
862
863        // reset current values
864        frameInfo.clear();
865       
866        mCurrentFrame = 0;
867        int i = 0;
868        while (!ifstr.eof())
869        {
870                ifstr.getline(line, 256);
871                sscanf(line, "%f %f %f %f %f %f %f %f %f", &info.position.x, &info.position.y, &info.position.z,
872                           &info.orientation.w, &info.orientation.x, &info.orientation.y, &info.orientation.z,
873                           &info.timeElapsed, &info.fps);
874               
875                mFrameInfo.push_back(info);
876               
877                ++ i;
878        }
879       
880        std::stringstream d;
881        d << "loaded " << i << " frames from file " << filename;
882        Ogre::LogManager::getSingleton().logMessage(d.str());
883
884        ifstr.close();
885}
886//-----------------------------------------------------------------------
887void TerrainFrameListener::nextAppState()
888{
889        mCurrentFrame = 0;
890        int lastState = mAppState;
891
892        // transition to the next state
893        mAppState = (mAppState + 1) % STATE_NUM;
894
895        // if last state was replay state: post process
896        if (lastState == REPLAY)
897        {
898        // reset debug text
899                mWindow->setDebugText("");
900                               
901                // hack for producing demo:
902                // we produced precomputed fps during the last replay =>
903                // save them to file
904                if (mSavePrecomputedFps)
905                {
906                        std::string filename = msApprevAlgorithmCaptions[mCurrentAlgorithm] + "_frames.out";
907
908                        writeFrames(filename, mPrecomputedFpsFrameInfo);
909                        mPrecomputedFpsFrameInfo.clear();
910                }
911
912                std::stringstream d;
913                mWalkthroughStats.Print(d, msApprevAlgorithmCaptions[mCurrentAlgorithm]);
914               
915                LogManager::getSingleton().logMessage(d.str());
916        }
917       
918        //-- replay recorded walkthrough
919        if (mAppState == REPLAY)
920        {
921                // no standard recording during replay
922                mRecordFrames = false;
923
924                // no stats information
925                mWindow->setDebugText("");
926
927                // clear previous walktrough
928                mFrameInfo.clear();
929
930                std::string filename;
931               
932                // if recording demo,
933                // we use precomputed fps which corresponds to current method,
934                // e.g., for chc we load precomputed chc fps from disc.
935                if (mRecordDemo)
936                {
937                        filename = msApprevAlgorithmCaptions[mCurrentAlgorithm] + "_frames.out";
938                }
939                else // standard filename
940                {
941                        filename = frames_out_filename;
942                }
943
944                //-- load recorded walkthrough from disk
945                loadFrames(filename, mFrameInfo);
946               
947               
948                // if there are still no recorded frames,
949                // no walkthrough was recorded => set next state
950                if (mFrameInfo.empty())
951                {
952                        nextAppState();
953                }
954                else // actual replay
955                {
956                        mWindow->setDebugText("Replay");
957                       
958                        // reset, because we measure fps stats during walkthrough
959                        // (warning: average fps broken)
960                        mWindow->resetStatistics();
961                        mWalkthroughStats.Reset();
962
963                        //-- initialise frame data
964                        mReplayTimeElapsed = 0;
965
966                        mCamNode->setPosition(mFrameInfo[0].position);
967                        mCamNode->setOrientation(mFrameInfo[0].orientation);
968                }
969        }
970
971}
972//-----------------------------------------------------------------------
973void TerrainFrameListener::toggleRecord()
974{
975        mRecordFrames = !mRecordFrames;
976
977        if (mRecordFrames)
978        {
979                // starting new recording => clear old frame info
980                mFrameInfo.clear();
981        }
982        else // recording just ended => write frame info to file
983        {
984                writeFrames(frames_out_filename, mFrameInfo);
985                mWindow->setDebugText("");
986        }
987}
988//-----------------------------------------------------------------------
989void TerrainFrameListener::changeThreshold(int incr)
990{
991        mVisibilityThreshold += incr;
992
993        if (mVisibilityThreshold < 0)
994        {
995                mVisibilityThreshold = 0;
996        }
997
998        char str[100]; sprintf(str,": %d", mVisibilityThreshold);
999
1000        mSceneMgr->setOption("Threshold", &mVisibilityThreshold);
1001        mThresholdInfo->setCaption(str);
1002}
1003//-----------------------------------------------------------------------
1004void TerrainFrameListener::changeAssumedVisibility(int incr)
1005{
1006        mAssumedVisibility += incr;
1007
1008        if (mAssumedVisibility < 0)
1009                mAssumedVisibility = 0;
1010
1011        char str[100]; sprintf(str,": %d", mAssumedVisibility);
1012
1013        mSceneMgr->setOption("AssumedVisibility", &mAssumedVisibility);
1014        mAssumedVisibilityInfo->setCaption(str);
1015}
1016//-----------------------------------------------------------------------
1017void TerrainFrameListener::changeVizScale(const int incr)
1018{
1019        mVizScale += incr;
1020
1021        if (mVizScale < 1)
1022                mVizScale = 1;
1023
1024        mSceneMgr->setOption("NodeVizScale", &mVizScale);
1025}
1026//-----------------------------------------------------------------------
1027void TerrainFrameListener::changeFloorDist(const float incr)
1028{
1029        mFloorDist += incr;
1030
1031        if (mFloorDist < 2)
1032        {       
1033                mFloorDist = 2;
1034        }
1035}
1036//-----------------------------------------------------------------------
1037void TerrainFrameListener::zoomVizCamera(int zoom)
1038{
1039        mVizCameraHeight += zoom;
1040
1041        if(mVizCameraHeight < 0)
1042                mVizCameraHeight = 0;
1043}
1044//-----------------------------------------------------------------------
1045void TerrainFrameListener::nextAlgorithm()
1046{
1047        // possible algorithms: 3 culling algorithms + standard rendering
1048        mCurrentAlgorithm = (mCurrentAlgorithm + 1) %
1049                (GtpVisibility::VisibilityEnvironment::NUM_CULLING_MANAGERS + 1);
1050        applyCurrentAlgorithm();
1051}
1052//-----------------------------------------------------------------------
1053void TerrainFrameListener::applyObjectType()
1054{
1055        if (mCurrentObjectType >= 3) // TODO: define a constant
1056                mCurrentObjectType = 0;
1057
1058        // parameters for new object
1059        mTerrainContentGenerator->SetOffset(msObjectTerrainOffsets[mCurrentObjectType]);
1060        Real scale = msObjectScales[mCurrentObjectType];
1061        mTerrainContentGenerator->SetScale(Vector3(scale, scale, scale));
1062
1063        mCurrentObjectTypeInfo->setCaption(": " + msObjectCaptions[mCurrentObjectType]);
1064}
1065//-----------------------------------------------------------------------
1066void TerrainFrameListener::applyCurrentAlgorithm()
1067{
1068        bool isNormalExecution;
1069       
1070        if (mCurrentAlgorithm < GtpVisibility::VisibilityEnvironment::NUM_CULLING_MANAGERS)
1071        {
1072                isNormalExecution = false;
1073                mSceneMgr->setOption("Algorithm", &mCurrentAlgorithm);
1074        }
1075        else
1076        {       // standard rendering without changed render queue flow
1077                isNormalExecution = true;
1078        }
1079       
1080        mSceneMgr->setOption("NormalExecution", &isNormalExecution);
1081        mAlgorithmInfo->setCaption(": " + msAlgorithmCaptions[mCurrentAlgorithm]);
1082
1083        if (1)
1084        {
1085                std::stringstream d; d << "algorithm: " << msAlgorithmCaptions[mCurrentAlgorithm];
1086                Ogre::LogManager::getSingleton().logMessage(d.str());
1087        }
1088}
1089//-----------------------------------------------------------------------
1090void TerrainFrameListener::updateStats()
1091{
1092        unsigned int opt = 0;
1093        char str[100];
1094       
1095        static String currFpsString = "Current FPS: ";
1096        static String avgFpsString = "Average FPS: ";
1097        static String bestFpsString = "Best FPS: ";
1098        static String worstFpsString = "Worst FPS: ";
1099        static String trisString = "Triangle Count: ";
1100
1101        int currentFps;
1102       
1103        // HACK for demo: use precomputed FPS instead of real FPS
1104        if (mRecordDemo)
1105        {
1106                currentFps = mPrecomputedFps;
1107        }
1108        else
1109        {
1110                currentFps = mWindow->getStatistics().lastFPS;
1111        }
1112
1113#if 0
1114        // HACK: take pure rendering time, only measures the render call
1115        long pureRenderTime = mTimeFrameEnded - mTimeFrameStarted;
1116
1117        if (pureRenderTime)
1118        {
1119                mPureRenderTimeFps = 1000.0f / (float) pureRenderTime;
1120        }
1121        currentFps = mPureRenderRenderTimeFps;
1122        //std::stringstream d; d << "Pure render time fps: " << mPureRenderTimeFps << "\n";
1123        //Ogre::LogManager::getSingleton().logMessage(d.str());
1124#endif
1125       
1126        unsigned int nodeInfo[3];
1127    mSceneMgr->getOption("NumRenderedNodes", nodeInfo);
1128        mSceneMgr->getOption("NumQueryCulledNodes", nodeInfo+1);
1129        mSceneMgr->getOption("NumFrustumCulledNodes", nodeInfo+2);
1130
1131
1132        mWalkthroughStats.UpdateFrame(currentFps,
1133                                                                  mWindow->getBestFPS(),
1134                                                                  mWindow->getWorstFPS(),
1135                                                                  (int)mWindow->getTriangleCount(),
1136                                                                  nodeInfo[0],
1137                                                                  nodeInfo[1],
1138                                                                  nodeInfo[2]);
1139
1140        // HACK: compute average fps ourselfs, because ogre avg. fps is wrong
1141        // TODO: update only once per second
1142        float avgFps = (float)mWalkthroughStats.mAccFps / (float)(mWalkthroughStats.mFrameCount);
1143       
1144
1145        // update stats when necessary
1146    try
1147        {
1148                OverlayElement* guiAvg = OverlayManager::getSingleton().getOverlayElement("Core/AverageFps");
1149                OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("Core/CurrFps");
1150                OverlayElement* guiBest = OverlayManager::getSingleton().getOverlayElement("Core/BestFps");
1151                OverlayElement* guiWorst = OverlayManager::getSingleton().getOverlayElement("Core/WorstFps");
1152
1153                const RenderTarget::FrameStats& stats = mWindow->getStatistics();
1154
1155                // HACK: take newly computed avg. fps instead of Ogre avg fps and update only once per second
1156                if (mTimeDelay < 0)
1157                {               
1158                        guiAvg->setCaption(avgFpsString + StringConverter::toString(avgFps) + " ms");
1159                        //guiCurr->setCaption(currFpsString + StringConverter::toString(currentFps));
1160                }
1161
1162                if (0)
1163                {
1164                        std::stringstream d;
1165                        d << "fps: " << StringConverter::toString(currentFps) << ", "
1166                          << "avg fps: " << StringConverter::toString(avgFps);
1167                        LogManager::getSingleton().logMessage(d.str());
1168                }
1169
1170                //guiAvg->setCaption(avgFps + StringConverter::toString(stats.avgFPS));
1171                guiCurr->setCaption(currFpsString + StringConverter::toString(currentFps));
1172               
1173                //std::stringstream d; d << "frame rate :" << stats.lastFPS;
1174                //Ogre::LogManager::getSingleton().logMessage(d.str());
1175
1176                guiBest->setCaption(bestFpsString + StringConverter::toString(stats.bestFPS)
1177                                                        + " " + StringConverter::toString(stats.bestFrameTime) + " ms");
1178                guiWorst->setCaption(worstFpsString + StringConverter::toString(stats.worstFPS)
1179                                                         + " " + StringConverter::toString(stats.worstFrameTime) + " ms");
1180
1181                OverlayElement* guiTris = OverlayManager::getSingleton().getOverlayElement("Core/NumTris");
1182        guiTris->setCaption(trisString + StringConverter::toString(stats.triangleCount));
1183               
1184                //LogManager::getSingleton().logMessage(StringConverter::toString(stats.triangleCount));
1185
1186                OverlayElement* guiDbg = OverlayManager::getSingleton().getOverlayElement("Core/DebugText");
1187                guiDbg->setCaption(mWindow->getDebugText());
1188
1189
1190                //-- culling stats
1191
1192                mSceneMgr->getOption("NumFrustumCulledNodes", &opt); sprintf(str,": %d", opt);
1193                mFrustumCulledNodesInfo->setCaption(str);
1194
1195                mSceneMgr->getOption("NumQueryCulledNodes", &opt); sprintf(str,": %d", opt);
1196                mQueryCulledNodesInfo->setCaption(str);
1197       
1198                mSceneMgr->getOption("NumHierarchyNodes", &opt); sprintf(str,": %d", opt);
1199                mHierarchyNodesInfo->setCaption(str);
1200
1201                mSceneMgr->getOption("NumRenderedNodes", &opt); sprintf(str,": %d", opt);
1202                mRenderedNodesInfo->setCaption(str);
1203
1204                sprintf(str,": %d", mTerrainContentGenerator->GetObjectCount());
1205                mObjectsCountInfo->setCaption(str);
1206
1207                // take old value into account in order to create no sudden changes
1208                mSceneMgr->getOption("NumQueriesIssued", &opt);
1209                mDelayedQueriesIssued = mDelayedQueriesIssued * 0.8 + (float)opt * 0.2f;
1210                sprintf(str,": %d", (int)mDelayedQueriesIssued);
1211                mQueriesIssuedInfo->setCaption(str);
1212
1213                mSceneMgr->getOption("NumTraversedNodes", &opt);
1214                mDelayedTraversedNodes = mDelayedTraversedNodes * 0.8 + (float)opt * 0.2f;
1215                sprintf(str,": %d", (int)mDelayedTraversedNodes);
1216                mTraversedNodesInfo->setCaption(str);
1217
1218                if (mRecordVideo)
1219                {
1220                        // update stats for demo
1221                        mMyStatsAlgorithmInfo->setCaption(msApprevAlgorithmCaptions[mCurrentAlgorithm]);
1222                        sprintf(str,": %d", (int)currentFps);
1223                        mMyStatsFpsInfo->setCaption(str);
1224                }
1225
1226        }
1227        catch (...)
1228        {
1229                // ignore
1230        }
1231}
1232//-----------------------------------------------------------------------
1233void TerrainFrameListener::setTestGeometryForVisibleLeaves(bool testGeometryForVisibleLeaves)
1234{
1235        mSceneMgr->setOption("TestGeometryForVisibleLeaves", &mTestGeometryForVisibleLeaves);
1236       
1237        // disable optimization for transparent objects, becaue otherwise visible
1238        // transparents could be skipped
1239        if (mTestGeometryForVisibleLeaves)
1240                mTestGeometryForVisibleLeavesInfo->setCaption(": true");
1241        else
1242                mTestGeometryForVisibleLeavesInfo->setCaption(": false");
1243}
1244//-----------------------------------------------------------------------
1245void TerrainFrameListener::setTestGeometryBounds(bool testGeometryBounds)
1246{
1247        mSceneMgr->setOption("TestGeometryBounds", &mTestGeometryBounds);
1248       
1249        if (mTestGeometryBounds)
1250                LogManager::getSingleton().logMessage("Testing geometry boundaries");
1251        else
1252                LogManager::getSingleton().logMessage("Testing bounding box of hierarchy node");
1253}
1254
1255//-----------------------------------------------------------------------
1256void TerrainFrameListener::toggleShowOctree()
1257{
1258        mShowOctree = !mShowOctree;
1259        mSceneMgr->setOption("ShowOctree", &mShowOctree);
1260}
1261//-----------------------------------------------------------------------
1262void TerrainFrameListener::toggleShowViewCells()
1263{
1264        mShowViewCells = !mShowViewCells;
1265        mSceneMgr->setOption("ShowViewCells", &mShowViewCells);
1266}
1267//-----------------------------------------------------------------------
1268void TerrainFrameListener::toggleUseViewCells()
1269{
1270        // HACK: no view cells for hilly terrain
1271        if (mApplication->msShowHillyTerrain)
1272                return;
1273
1274        mUseViewCells = !mUseViewCells;
1275
1276        // load on demand
1277        if (mUseViewCells)// && !mViewCellsLoaded)
1278        {
1279                LogManager::getSingleton().logMessage("using view cells");
1280                mLoadingOverlay->show();
1281
1282                // call once to load view cell loading overlay
1283                mWindow->update();
1284                mViewCellsLoaded = mApplication->LoadViewCells(mApplication->mViewCellsFilename);
1285               
1286                if (!mViewCellsLoaded)
1287                        LogManager::getSingleton().logMessage("loading view cells failed");
1288                else
1289                        LogManager::getSingleton().logMessage("view cells successfully loaded");
1290       
1291                mLoadingOverlay->hide();
1292        }
1293
1294        if (mUseViewCells)
1295                mViewCellsInfo->setCaption(": on");
1296        else
1297                mViewCellsInfo->setCaption(": off");
1298
1299        mSceneMgr->setOption("UseViewCells", &mUseViewCells);
1300}
1301//-----------------------------------------------------------------------
1302void TerrainFrameListener::toggleUseDepthPass()
1303{
1304        mUseDepthPass = !mUseDepthPass;
1305
1306        mSceneMgr->setOption("UseDepthPass", &mUseDepthPass);
1307       
1308        if (mUseDepthPass)
1309        {
1310                mUseDepthPassInfo->setCaption(": true");
1311        }
1312        else
1313        {
1314                mUseDepthPassInfo->setCaption(": false");
1315        }
1316}
1317//-----------------------------------------------------------------------
1318void TerrainFrameListener::toggleFlushQueue()
1319{
1320        mFlushQueue = !mFlushQueue;
1321
1322        mSceneMgr->setOption("FlushQueue", &mFlushQueue);
1323       
1324        if (mFlushQueue)
1325        {
1326                LogManager::getSingleton().logMessage("flushing queue");
1327                //mFlushQueueInfo->setCaption(": true");
1328        }
1329        else
1330        {
1331                LogManager::getSingleton().logMessage("not flushing queue");
1332                //mFlushQueueInfo->setCaption(": false");
1333        }
1334}
1335
1336void TerrainFrameListener::showBoundingBoxes()
1337{
1338        m_iShowBoundingBoxes++;
1339        m_iShowBoundingBoxes%=2;
1340        mSceneMgr->setOption("ShowBiHierarchy", (void*)&m_iShowBoundingBoxes);
1341}
1342
1343
1344//-----------------------------------------------------------------------
1345void TerrainFrameListener::toggleShowViz()
1346{
1347        mVisualizeCulledNodes = mShowVisualization = !mShowVisualization;
1348       
1349        // create viewport with priority VIZ_VIEWPORT_Z_ORDER:
1350        // will be rendered over standard viewport
1351        if (mShowVisualization)
1352        {       
1353                Viewport *vizvp = mWindow->addViewport(mVizCamera,
1354                        VIZ_VIEWPORT_Z_ORDER, 0.6, 0.6, 0.4, 0.4);
1355                               
1356                vizvp->setBackgroundColour(ColourValue(0.0, 0.3, 0.2, 1));
1357
1358                vizvp->setOverlaysEnabled(false);
1359                // Alter the camera aspect ratio to match the viewport
1360        mVizCamera->setAspectRatio(Real(vizvp->getActualWidth()) /
1361                                                                   Real(vizvp->getActualHeight()));
1362               
1363                mSceneMgr->setOption("VisualizeCulledNodes", &mVisualizeCulledNodes);
1364       
1365        }
1366        else
1367        {
1368                // remove visualization viewport
1369                mWindow->removeViewport(VIZ_VIEWPORT_Z_ORDER);
1370
1371                // octree bounding boxes are shown for visualization purpose, reset now
1372                mSceneMgr->setOption("ShowOctree", &mShowOctree);
1373        }
1374}
1375//-----------------------------------------------------------------------
1376void TerrainFrameListener::toggleShowShadows()
1377{
1378        mShowShadows = !mShowShadows;
1379
1380        mSunLight->setCastShadows(mShowShadows);
1381
1382        if (mShowShadows)
1383        {
1384                mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE);
1385                //mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_MODULATIVE);
1386                //mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);           
1387        }
1388        else
1389        {
1390                mSceneMgr->setShadowTechnique(SHADOWTYPE_NONE);
1391        }
1392
1393}
1394//-----------------------------------------------------------------------
1395void TerrainFrameListener::nextNodeVizMode()
1396{
1397        mNodeVizMode = (mNodeVizMode + 1) % NODEVIZ_MODES_NUM;
1398
1399        bool renderNodesForViz =
1400                (mNodeVizMode == NODEVIZ_RENDER_NODES) ||
1401                (mNodeVizMode == NODEVIZ_RENDER_NODES_AND_CONTENT);
1402
1403        bool renderNodesContentForViz = (mNodeVizMode == NODEVIZ_RENDER_NODES_AND_CONTENT);
1404        //bool renderNodesContentForViz = mNodeVizMode == NODEVIZ_RENDER_GEOMETRY;
1405
1406        bool renderPvsForViz = (mNodeVizMode == NODEVIZ_RENDER_PVS);
1407
1408        mSceneMgr->setOption("RenderNodesForViz", &renderNodesForViz);
1409        mSceneMgr->setOption("RenderNodesContentForViz", &renderNodesContentForViz);
1410        mSceneMgr->setOption("RenderPvsForViz", &renderPvsForViz);
1411}
1412//-----------------------------------------------------------------------
1413void TerrainFrameListener::keyPressed(KeyEvent* e)
1414{
1415        // hide exact visibility query overlay
1416        if (mShowQueryStats)
1417        {
1418                mQueryOverlay->hide();
1419                mShowQueryStats = false;
1420        }
1421
1422        switch(e->getKey())
1423        {
1424       
1425        case KC_SUBTRACT:
1426                changeThreshold(-10);
1427                break;
1428        case KC_ADD:
1429                changeThreshold(10);
1430                break;
1431
1432        // assumed visible #frames
1433        case KC_9:
1434                changeAssumedVisibility(-5);
1435                break;
1436        case KC_0:
1437                changeAssumedVisibility(5);
1438                break;
1439
1440        case KC_ESCAPE:
1441                mShutdownRequested = true;
1442                e->consume();
1443                return;
1444        // the visibility algorithm
1445        case KC_SPACE:
1446                nextAlgorithm();
1447                break;
1448        case KC_LSHIFT:
1449                mShiftPressed = true;
1450                break;
1451        case KC_DELETE:
1452                mDeleteObjects = true;
1453                break;
1454        // not working yet
1455        case KC_C:
1456                if (mItemBufferMode != GtpVisibility::QueryManager::GEOMETRY_VISIBILITY)
1457                {
1458                        mItemBufferMode = GtpVisibility::QueryManager::GEOMETRY_VISIBILITY;
1459                }
1460                else
1461                {
1462                        mItemBufferMode = GtpVisibility::QueryManager::PATCH_VISIBILITY;
1463                }
1464                break;
1465         case KC_E: // hack for recording demo using precomputed fps
1466                mSavePrecomputedFps = !mSavePrecomputedFps;
1467                break;
1468    case KC_F:
1469                nextFilter();
1470                break;
1471        case KC_G:
1472                mTestGeometryForVisibleLeaves = !mTestGeometryForVisibleLeaves;
1473                setTestGeometryForVisibleLeaves(mTestGeometryForVisibleLeaves);
1474                break;
1475        case KC_H:
1476                toggleShowShadows();
1477                break;
1478        case KC_L:
1479                toggleShowViewCells();
1480                break;
1481        case KC_M: // hack for recording demo using precomputed fps
1482                mRecordDemo = !mRecordDemo;
1483                break;
1484        case KC_O:
1485                switchMouseMode();
1486                break;
1487        case KC_P:
1488                toggleDisplayCameraDetails();
1489                break;
1490        // if view cells + PVS are used for rendering
1491        case KC_V:
1492                toggleUseViewCells();
1493                break;
1494        case KC_Q:
1495                mTestGeometryBounds = !mTestGeometryBounds;
1496                setTestGeometryBounds(mTestGeometryBounds);
1497                break;
1498        case KC_R:
1499                nextSceneDetailLevel();
1500                break;
1501        case KC_T:
1502                toggleShowOctree();
1503                break;
1504        case KC_U:
1505                mCamNode->resetOrientation();
1506                mCamNode->setPosition(mApplication->mInitialPosition);
1507                break;
1508        case KC_X:
1509                toggleUseDepthPass();
1510                break;
1511        case KC_I:
1512                toggleFlushQueue();
1513                break;
1514        case KC_Z:
1515                mCamNode->resetOrientation();
1516                break;
1517        case KC_B:
1518                showBoundingBoxes();
1519                break;
1520        case KC_3:
1521                if (m_iBoundingBoxLevel>0) m_iBoundingBoxLevel--;
1522                mSceneMgr->setOption("HiLiteLevel", (void*)&m_iBoundingBoxLevel);
1523                break;
1524        case KC_4:
1525                m_iBoundingBoxLevel++;
1526                mSceneMgr->setOption("HiLiteLevel", (void*)&m_iBoundingBoxLevel);
1527                break;
1528
1529
1530        ///////////////
1531        //-- visualization
1532
1533        case KC_1:
1534                toggleShowViz();
1535                break;
1536        case KC_2:
1537                nextNodeVizMode();
1538                break;
1539       
1540        case KC_F12:
1541                {
1542                        LogManager::getSingleton().logMessage("generating 500 objects");
1543                        // generate new objects
1544                        const int numObjects = 500;
1545                        mApplication->generateScene(numObjects, mCurrentObjectType);
1546                }
1547                break;
1548        case KC_F1:
1549                toggleShowHelp();
1550                break;
1551        case KC_F2:
1552                mStatsOn = !mStatsOn;
1553                showStats(mStatsOn);
1554                break;
1555        case KC_F3:
1556                nextAppState();
1557                break;
1558        case KC_F4:
1559                toggleRecord();
1560                break;
1561        case KC_F5:
1562                {
1563                        mSceneMgr->_findVisibleObjects(mCamera, false);
1564                        if (0) applyVisibilityQuery(false, mShiftPressed, mUseItemBuffer);
1565                        break;
1566                }
1567        case KC_F6:
1568                {
1569                        const bool fromPoint = true;
1570                        if (1) applyVisibilityQuery(fromPoint, mShiftPressed, mUseItemBuffer);
1571                        break;
1572                }       
1573        // change type of object to be generated
1574        case KC_F7:
1575                ++ mCurrentObjectType;
1576                applyObjectType();
1577                break;
1578        case KC_F8:
1579                mTerrainContentGenerator->WriteObjects(objects_out_filename);
1580                break;
1581        case KC_F9:
1582                mUseAnimation = !mUseAnimation;
1583                break;
1584        case KC_F10:
1585                mRecordVideo = !mRecordVideo;
1586                break;         
1587        case KC_F11:
1588                takeScreenshot();
1589                break;
1590        default:
1591                break;
1592        }
1593
1594        CEGUI::System::getSingleton().injectKeyDown(e->getKey());
1595        CEGUI::System::getSingleton().injectChar(e->getKeyChar());
1596        e->consume();
1597}
1598//-----------------------------------------------------------------------
1599void TerrainFrameListener::keyReleased(KeyEvent* e)
1600{
1601        if (e->getKey() == KC_LSHIFT)
1602        {
1603                mShiftPressed = false;
1604        }
1605       
1606        CEGUI::System::getSingleton().injectKeyUp(e->getKey());
1607        e->consume();
1608}
1609//-----------------------------------------------------------------------
1610void TerrainFrameListener::keyClicked(KeyEvent* e)
1611{
1612        // Do nothing
1613        e->consume();
1614}
1615//-----------------------------------------------------------------------
1616void TerrainFrameListener::addFrameInfo(FrameInfoContainer &frameInfos,
1617                                                                                SceneNode *camNode,
1618                                                                                Real timeElapsed)
1619{
1620        frame_info info;
1621
1622        info.orientation = mCamNode->getOrientation();
1623        info.position = mCamNode->getPosition();
1624        info.timeElapsed = timeElapsed;
1625        info.fps = mWindow->getStatistics().lastFPS;
1626
1627        frameInfos.push_back(info);
1628}
1629//-----------------------------------------------------------------------
1630void TerrainFrameListener::setCurrentFrameInfo(const Real timeElapsed)
1631{
1632        //-- find current frame relative to elapsed frame time         
1633        mReplayTimeElapsed -= timeElapsed;
1634       
1635        while ((mReplayTimeElapsed <= 0) && (mCurrentFrame < (int)mFrameInfo.size() - 1))
1636        {
1637                mReplayTimeElapsed += mFrameInfo[mCurrentFrame ++].timeElapsed;
1638        }
1639
1640
1641        // TODO: crashes here if recording / replaying on the same time!!
1642        const frame_info new_frame = mFrameInfo[mCurrentFrame];
1643        const frame_info old_frame = mFrameInfo[mCurrentFrame - 1];
1644               
1645        /////////////
1646        //-- interpolate frames
1647        Real factor = 1;
1648
1649        if (old_frame.timeElapsed > 0)
1650        {
1651                factor = mReplayTimeElapsed / old_frame.timeElapsed;
1652        }
1653
1654        const Vector3 camPos = old_frame.position + factor
1655                * (new_frame.position - old_frame.position);
1656
1657        // interpolate the orientation
1658        const Quaternion camOrienation =
1659                Quaternion::Slerp(factor, old_frame.orientation, new_frame.orientation, true);
1660
1661        // HACK for demo: interpolate precomputed fps
1662        mPrecomputedFps = old_frame.fps + factor * (new_frame.fps - old_frame.fps);
1663
1664        mCamNode->setPosition(camPos);
1665        mCamNode->setOrientation(camOrienation);
1666       
1667        // stop replaying after one full walkthrough
1668        if (mCurrentFrame == (int)mFrameInfo.size() - 1)
1669        {
1670                nextAppState();
1671        }
1672}
1673//-----------------------------------------------------------------------   
1674bool TerrainFrameListener::processUnbufferedKeyInput(const FrameEvent& evt)
1675{
1676        bool cursorPressed = false;
1677       
1678        /* Move camera forward by keypress. */
1679    if (mInputDevice->isKeyDown(KC_UP) || mInputDevice->isKeyDown(KC_W))
1680        {
1681                mTranslateVector.z = -mMoveScale;
1682                cursorPressed = true;
1683        }
1684    /* Move camera backward by keypress. */
1685    if (mInputDevice->isKeyDown(KC_DOWN) || mInputDevice->isKeyDown(KC_S))
1686    {
1687                mTranslateVector.z = mMoveScale;
1688                cursorPressed = true;
1689    }
1690
1691        if (mInputDevice->isKeyDown(KC_A))
1692        {
1693                mTranslateVector.x -= mMoveScale;
1694                mTranslateVector.y += mMoveScale;
1695
1696                cursorPressed = true;
1697        }
1698
1699        if (mInputDevice->isKeyDown(KC_D))
1700        {
1701                mTranslateVector.x += mMoveScale;
1702                mTranslateVector.y -= mMoveScale;
1703
1704                cursorPressed = true;
1705        }
1706
1707    if (mInputDevice->isKeyDown(KC_RIGHT))
1708    {
1709        mCamNode->yaw(-mRotScale, Ogre::Node::TS_WORLD);
1710                cursorPressed = true;
1711    }
1712
1713    if (mInputDevice->isKeyDown(KC_LEFT))
1714    {
1715        mCamNode->yaw(mRotScale, Ogre::Node::TS_WORLD);
1716                cursorPressed = true;
1717    }
1718
1719        // visualization camera
1720        if (mInputDevice->isKeyDown(KC_3))
1721                zoomVizCamera(50);
1722
1723        if (mInputDevice->isKeyDown(KC_4))
1724                zoomVizCamera(-50);
1725
1726        // scale of the visualized objects
1727#if 0
1728        if (mInputDevice->isKeyDown(KC_5))
1729                changeVizScale(-1);
1730
1731        if (mInputDevice->isKeyDown(KC_6))
1732                changeVizScale(1);
1733#else
1734
1735        const int mFactor = 20;
1736        if (mInputDevice->isKeyDown(KC_5))
1737                mMoveFactor += mFactor;
1738
1739        if (mInputDevice->isKeyDown(KC_6))
1740                mMoveFactor -= mFactor;
1741#endif
1742
1743        // distance to floor
1744        if (mInputDevice->isKeyDown(KC_7))
1745                changeFloorDist(-1);
1746
1747        if (mInputDevice->isKeyDown(KC_8))
1748                changeFloorDist(1);
1749
1750
1751        // show the results
1752        if (cursorPressed && mShowQueryStats)
1753        {
1754                mQueryOverlay->hide();
1755                mShowQueryStats = false;
1756        }
1757
1758    // Return true to continue rendering
1759    return true;
1760}
1761//-----------------------------------------------------------------------
1762void TerrainFrameListener::nextFilter()
1763{
1764        switch (mFiltering)
1765        {
1766        case TFO_BILINEAR:
1767                mFiltering = TFO_TRILINEAR;
1768                mAniso = 1;
1769                break;
1770        case TFO_TRILINEAR:
1771                mFiltering = TFO_ANISOTROPIC;
1772                mAniso = 8;
1773                break;
1774        case TFO_ANISOTROPIC:
1775                mFiltering = TFO_BILINEAR;
1776                mAniso = 1;
1777                break;
1778        default:
1779                break;
1780        }
1781
1782    MaterialManager::getSingleton().setDefaultTextureFiltering(mFiltering);
1783    MaterialManager::getSingleton().setDefaultAnisotropy(mAniso);
1784
1785        // reload stats
1786    showStats(mStatsOn);
1787}
1788//-----------------------------------------------------------------------
1789void TerrainFrameListener::nextSceneDetailLevel()
1790{
1791#if OGRE_103
1792        mSceneDetailIndex = (mSceneDetailIndex + 1) % 3;
1793        switch (mSceneDetailIndex)
1794        {
1795                case 0:
1796                        mCamera->setDetailLevel(SDL_SOLID);
1797                        break;
1798                case 1:
1799                        mCamera->setDetailLevel(SDL_WIREFRAME);
1800                        break;
1801                case 2:
1802                        mCamera->setDetailLevel(SDL_POINTS);
1803                        break;
1804        }
1805#endif
1806}
1807//-----------------------------------------------------------------------
1808void TerrainFrameListener::takeVideoFrame(std::ofstream &ofstr)
1809{
1810        char name[50];
1811
1812        sprintf(name, "frame_%05d.tga", ++mNumVideoFrames);
1813    mWindow->writeContentsToFile(name);
1814    //mWindow->setDebugText(String("Wrote ") + name);
1815
1816        ofstr << name << "\n";
1817}
1818//-----------------------------------------------------------------------
1819void TerrainFrameListener::takeScreenshot()
1820{
1821        char name[50];
1822
1823        sprintf(name, "screenshot_%05d.png", ++mNumScreenShots);
1824    mWindow->writeContentsToFile(name);
1825    //mWindow->setDebugText(String("Wrote ") + name);
1826}
1827//-----------------------------------------------------------------------
1828void TerrainFrameListener::toggleDisplayCameraDetails()
1829{
1830        mDisplayCameraDetails = !mDisplayCameraDetails;
1831       
1832    if (!mDisplayCameraDetails)
1833        {
1834                mWindow->setDebugText("");
1835        }
1836}
1837//-----------------------------------------------------------------------
1838void TerrainFrameListener::showStats(bool show)
1839{
1840        if (mDebugOverlay && mCullStatsOverlay)
1841        {
1842                if (show)
1843                {
1844                        mDebugOverlay->show();
1845                        mCullStatsOverlay->show();
1846                }
1847                else
1848                {
1849                        mDebugOverlay->hide();
1850                        mCullStatsOverlay->hide();
1851                }
1852        }
1853}
1854
1855//-----------------------------------------------------------------------
1856void TerrainFrameListener::toggleShowHelp()
1857{
1858        mShowHelp = !mShowHelp;
1859
1860        if (mShowHelp)
1861        {
1862                mHelpOverlay->show();
1863        }
1864        else
1865        {
1866                mHelpOverlay->hide();
1867        }
1868}
1869//-----------------------------------------------------------------------
1870void TerrainFrameListener::initOverlayElement(OverlayElement **elInfo, String ext,
1871                                                                                          String name, int top, String caption)
1872{
1873        OverlayElement *el =
1874                OverlayManager::getSingleton().getOverlayElement(ext + name);
1875
1876        (*elInfo) = OverlayManager::getSingleton().getOverlayElement(ext + name + "Info");
1877        (*elInfo)->setCaption(caption);
1878
1879        el->setTop(top);
1880        (*elInfo)->setTop(top);
1881}
1882//-----------------------------------------------------------------------
1883void TerrainFrameListener::initHelpOverlayElement(String name, int top)
1884{
1885        OverlayElement *el = OverlayManager::getSingleton().getOverlayElement(
1886                "Example/Visibility/Help/" + name);
1887
1888        el->setTop(top);
1889}
1890//-----------------------------------------------------------------------
1891void TerrainFrameListener::initHelpOverlay()
1892{
1893        const int vert_space = 15;
1894        int top = 30;
1895
1896        initHelpOverlayElement("ShowHelp", top); top += vert_space;
1897        initHelpOverlayElement("Stats", top); top += vert_space;
1898        initHelpOverlayElement("AppState", top); top += vert_space;
1899        initHelpOverlayElement("Recorded", top); top += vert_space;
1900        initHelpOverlayElement("Animation", top); top += vert_space;
1901        initHelpOverlayElement("Video", top); top += vert_space;
1902        initHelpOverlayElement("Screenshots", top); top += vert_space;
1903        initHelpOverlayElement("WriteOut", top); top += vert_space;
1904
1905
1906        top +=vert_space;
1907        initHelpOverlayElement("SceneDetail", top); top += vert_space;
1908        initHelpOverlayElement("DisplayCameraDetails", top); top += vert_space;
1909        initHelpOverlayElement("DisplayOctree", top); top += vert_space;
1910        initHelpOverlayElement("UseShadows", top); top += vert_space;
1911        initHelpOverlayElement("Filter", top); top += vert_space;
1912
1913        //-- visualization
1914        top += vert_space;
1915        initHelpOverlayElement("VizSection", top); top += vert_space;
1916        initHelpOverlayElement("Viz", top); top += vert_space;
1917        initHelpOverlayElement("NextVizMode", top); top += vert_space;
1918        initHelpOverlayElement("ZoomViz", top); top += vert_space;
1919
1920
1921        //-- visibility queries
1922        top += vert_space;
1923        initHelpOverlayElement("VisQuery", top); top += vert_space;
1924        initHelpOverlayElement("FromCameraQuery", top); top += vert_space;
1925        initHelpOverlayElement("FromPointQuery", top); top += vert_space;
1926        initHelpOverlayElement("QueryType", top); top += vert_space;
1927        initHelpOverlayElement("QueryTarget", top); top += vert_space;
1928
1929        //-- object generation
1930        top += vert_space;
1931        initHelpOverlayElement("SceneObjects", top); top += vert_space;
1932        initHelpOverlayElement("PlaceObjects", top); top += vert_space;
1933        initHelpOverlayElement("GenerateObjects", top); top += vert_space;
1934        initHelpOverlayElement("RemoveObjects", top); top += vert_space;
1935        initHelpOverlayElement("DropObject", top); top += vert_space;
1936
1937        OverlayElement *helpPanel = OverlayManager::getSingleton().getOverlayElement(
1938                "Example/Visibility/Help/HelpPanel");
1939
1940        helpPanel->setHeight(top + 10);
1941}
1942//-----------------------------------------------------------------------
1943void TerrainFrameListener::initVisStatsOverlay()
1944{
1945        const int border_height = 10;
1946        const int vert_space = 15;
1947
1948        //-- visibility culling stats overlay
1949        int top = border_height;
1950
1951        String ext = "Example/Visibility/";
1952       
1953        initOverlayElement(&mAlgorithmInfo, ext, "Algorithm", top,
1954                ": " + msAlgorithmCaptions[mCurrentAlgorithm]); top += vert_space;
1955
1956        initOverlayElement(&mThresholdInfo, ext, "Threshold", top, ": 0"); top += vert_space;
1957        initOverlayElement(&mTestGeometryForVisibleLeavesInfo, ext,
1958                                           "TestGeometryForVisibleLeaves", top, ": true"); top += vert_space;
1959        initOverlayElement(&mUseDepthPassInfo, ext, "UseDepthPass", top, ": false"); top += vert_space;
1960        initOverlayElement(&mAssumedVisibilityInfo, ext, "AssumedVisibility", top, ": 0"); top += vert_space;
1961        initOverlayElement(&mCurrentObjectTypeInfo, ext, "CurrentObjectType", top, ": "); top += vert_space;
1962        initOverlayElement(&mViewCellsInfo, ext, "ViewCells", top, ": "); top += vert_space;
1963        //initOverlayElement(&mHelpInfo, ext, "Help", top, ": "); top += vert_space;
1964
1965        OverlayElement *optionsPanel = OverlayManager::getSingleton().
1966                getOverlayElement("Example/Visibility/VisibilityPanel");
1967
1968        optionsPanel->setHeight(top + border_height);
1969
1970        top = border_height;
1971        //ext = "Example/Visibility/";
1972        initOverlayElement(&mFrustumCulledNodesInfo, ext, "FrustumCulledNodes", top, ": 0"); top += vert_space;
1973        initOverlayElement(&mQueryCulledNodesInfo, ext, "QueryCulledNodes", top, ": 0"); top += vert_space;
1974        initOverlayElement(&mTraversedNodesInfo, ext, "TraversedNodes", top, ": 0"); top += vert_space;
1975        initOverlayElement(&mHierarchyNodesInfo, ext, "HierarchyNodes", top, ": 0"); top += vert_space;
1976        initOverlayElement(&mRenderedNodesInfo, ext, "RenderedNodes", top, ": 0"); top += vert_space;
1977        initOverlayElement(&mObjectsCountInfo, ext, "ObjectsCount", top, ": 0"); top += vert_space;
1978        initOverlayElement(&mQueriesIssuedInfo, ext, "QueriesIssued", top, ": 0"); top += vert_space;
1979
1980        OverlayElement *visPanel = OverlayManager::getSingleton().
1981                getOverlayElement("Example/Visibility/VisibilityStatsPanel");
1982
1983        visPanel->setHeight(top + border_height);
1984}
1985//-----------------------------------------------------------------------
1986void TerrainFrameListener::initQueryOverlay()
1987{
1988        const int border_height = 10;
1989        const int vert_space = 15;
1990
1991        //-- visibility culling stats overlay
1992        int top = border_height + 25;
1993
1994        const String ext = "Example/Visibility/Query/";
1995           
1996        initOverlayElement(&mQueryTypeInfo , ext, "QueryType", top,     ": 0"); top += vert_space;
1997       
1998        initOverlayElement(&mQueryVisibleNodesInfo , ext, "VisibleNodes", top,  ": 0"); top += vert_space;
1999        initOverlayElement(&mQueryVisibleGeometryInfo , ext, "VisibleGeometry", top,    ": 0"); top += vert_space;
2000        initOverlayElement(&mQueryVisiblePatchInfo , ext, "VisiblePatches", top,        ": 0"); top += vert_space;
2001       
2002        initOverlayElement(&mQueryNodeVisibilityInfo , ext, "NodeVisibility", top,      ": 0"); top += vert_space;
2003        initOverlayElement(&mQueryGeometryVisibilityInfo , ext, "GeometryVisibility", top,      ": 0"); top += vert_space;
2004        initOverlayElement(&mQueryPatchVisibilityInfo , ext, "PatchVisibility", top,    ": 0"); top += vert_space;
2005
2006        OverlayElement *queryPanel =
2007                OverlayManager::getSingleton().getOverlayElement("Example/Visibility/Query/QueryPanel");
2008
2009        queryPanel->setHeight(top + border_height);
2010}
2011//-----------------------------------------------------------------------
2012void TerrainFrameListener::setAlgorithm(const int algorithm)
2013{
2014        mCurrentAlgorithm = algorithm;
2015        applyCurrentAlgorithm();
2016}
Note: See TracBrowser for help on using the repository browser.