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

Revision 2512, 57.3 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
616        visManager->SetQueryManager(mQueryManager);
617
618        NodeInfoContainer visibleNodes;
619        MeshInfoContainer visibleGeometry;
620        PatchInfoContainer visiblePatches;
621
622        if (fromPoint)
623        {
624                mQueryManager->ComputeFromPointVisibility(mCamNode->getPosition(),
625                                                                                                  &visibleNodes,
626                                                                                                  &visibleGeometry,
627                                                                                                  &visiblePatches,
628                                                                                                  relativeVisibility,
629                                                                                                  approximateVisibility);
630        }
631        else
632        {
633                mQueryManager->ComputeCameraVisibility(*mCamera,
634                                                                                           &visibleNodes,
635                                                                                           &visibleGeometry,
636                                                                                           &visiblePatches,
637                                                                                           relativeVisibility,
638                                                                                           approximateVisibility);
639        }
640               
641        std::stringstream d;
642        d << "Query mode: " << queryModes << ", "
643          << msQueryTypeCaptions[fromPoint ?  1 : 0].c_str() << " "
644          << msQueryRelativeVisCaptions[relativeVisibility ? 1 : 0].c_str() << " "
645      << msQueryMethodCaptions[useItemBuffer ? 1 : 0].c_str();
646        LogManager::getSingleton().logMessage(d.str());
647
648
649        float averageNodeVis = 0, averageGeometryVis = 0, averagePatchVis = 0;
650        int geomSize = 0, nodesSize = 0, patchSize = 0;
651
652        /////////////////
653        //-- apply queries on geometry level
654
655        MeshInfoContainer::iterator geomIt, geomIt_end = visibleGeometry.end();
656
657        for (geomIt = visibleGeometry.begin(); geomIt != geomIt_end; ++geomIt)
658        {
659                // add if not 0
660                if ((*geomIt).GetVisiblePixels())
661                {
662                        float vis = relativeVisibility ?
663                                (*geomIt).ComputeRelativeVisibility() : (float)(*geomIt).GetVisiblePixels();
664       
665                        averageGeometryVis += vis;
666                        ++ geomSize;
667                       
668                        std::stringstream d;
669                        d << "Geometry " << geomSize << " id: " << (*geomIt).GetSource()->getSubEntity(0)->getId()
670                          << " visibility: "  << (*geomIt).GetVisiblePixels() << ", " << (*geomIt).GetProjectedPixels();
671                        LogManager::getSingleton().logMessage(d.str());
672                }
673        }
674
675
676        ////////////////
677        //-- apply queries on node level
678
679        NodeInfoContainer::iterator nodesIt, nodesIt_end = visibleNodes.end();
680
681        for (nodesIt = visibleNodes.begin(); nodesIt != nodesIt_end; ++nodesIt)
682        {
683                // add if not 0
684                if ((*nodesIt).GetVisiblePixels())
685                {
686                        float vis = relativeVisibility ?
687                                (*nodesIt).ComputeRelativeVisibility() : (float)(*nodesIt).GetVisiblePixels();
688               
689                        averageNodeVis += vis;
690                        ++ nodesSize;
691
692                        std::stringstream d; d << "Node visibility: " << vis;
693                        LogManager::getSingleton().logMessage(d.str());
694                }       
695        }
696
697
698        ////////////////
699        //-- apply queries on patch level
700
701
702        PatchInfoContainer::iterator patchIt, patchIt_end = visiblePatches.end();
703
704        for (patchIt = visiblePatches.begin(); patchIt != patchIt_end; ++ patchIt)
705        {
706                // add if not 0
707                if ((*patchIt).GetVisiblePixels())
708                {
709                        float vis = relativeVisibility ?
710                                (*patchIt).ComputeRelativeVisibility() : (float)(*patchIt).GetVisiblePixels();
711               
712                        averagePatchVis += vis;
713                        ++ patchSize;
714
715                        std::stringstream d; d << "Patch visibility: " << vis;
716                        LogManager::getSingleton().logMessage(d.str());
717                }       
718        }
719
720        ///////////////////////////////////////////////////////////////
721        //-- update visibility queries stats
722
723        if (nodesSize)
724                averageNodeVis /= (float)nodesSize;
725        if (geomSize)
726                averageGeometryVis /= (float)geomSize;
727        if (patchSize)
728                averagePatchVis /= (float)patchSize;
729
730
731    try
732        {
733                char str[100];
734               
735                sprintf(str, ": %s, %s, %s",
736                                msQueryTypeCaptions[fromPoint ?  1 : 0].c_str(),
737                                msQueryRelativeVisCaptions[relativeVisibility ? 1 : 0].c_str(),
738                                msQueryMethodCaptions[useItemBuffer ? 1 : 0].c_str());
739
740                mQueryTypeInfo->setCaption(str);
741
742                sprintf(str, ": %d", (int)nodesSize);
743                mQueryVisibleNodesInfo->setCaption(str);
744       
745                sprintf(str,": %d", (int)geomSize);
746                mQueryVisibleGeometryInfo->setCaption(str);
747               
748                sprintf(str,": %d", (int)patchSize);
749                mQueryVisiblePatchInfo->setCaption(str);
750
751                sprintf(str,": %3.3f", averageNodeVis);
752                mQueryNodeVisibilityInfo->setCaption(str);
753
754                sprintf(str,": %3.3f", averageGeometryVis);
755                mQueryGeometryVisibilityInfo->setCaption(str);
756
757                sprintf(str,": %3.3f", averagePatchVis);
758                mQueryPatchVisibilityInfo->setCaption(str);
759        }
760        catch (...)
761        {
762                // ignore
763        }
764
765        // show the results
766        if (!mShowQueryStats && !mShowHelp)
767        {
768                mQueryOverlay->show();
769                mShowQueryStats = true;
770        }
771
772        delete mQueryManager;
773}
774
775
776bool TerrainFrameListener::processUnbufferedMouseInput(const FrameEvent& evt)
777{
778        // Rotation factors, may not be used if the second mouse button is pressed.
779    // If the second mouse button is pressed, then the mouse movement results in
780    // sliding the camera, otherwise we rotate.
781    if (mInputDevice->getMouseButton(1))
782    {
783                mTranslateVector.x += mInputDevice->getMouseRelativeX() * 0.13;
784                mTranslateVector.y -= mInputDevice->getMouseRelativeY() * 0.13;
785        }
786        else
787        {
788                mRotX = Degree(-mInputDevice->getMouseRelativeX() * 0.13);
789                mRotY = Degree(-mInputDevice->getMouseRelativeY() * 0.13);
790        }
791
792        return true;
793}
794//-----------------------------------------------------------------------
795bool TerrainFrameListener::frameEnded(const FrameEvent& evt)
796{
797        if (mShutdownRequested)
798                return false;
799
800        // timer end time
801        if (!mShowVisualization)
802        {
803                mTimeFrameEnded = mTimer->getMilliseconds();
804        }
805
806    updateStats();
807
808        if (mRecordVideo) // record current frame
809        {
810                takeVideoFrame(video_out);
811        }
812
813        //-- IMPORTANT: must be set, otherwise terrain is not rendered correctly
814        mSceneMgr->endFrame();
815
816        if (mTimeDelay <= 0) // simulates approx. one second
817                mTimeDelay = 1.0;
818
819        return true;
820}
821//-----------------------------------------------------------------------
822void TerrainFrameListener::moveCamera()
823{
824        // move node rather than camera so orientation is right in the visualization
825        mCamNode->yaw(mRotX, Ogre::Node::TS_WORLD);
826        //mCamNode->rotate(Vector3(0,1,0), mRotX, Ogre::Node::TS_WORLD);
827        mCamNode->pitch(mRotY);
828
829        mCamNode->translate(mCamNode->getLocalAxes(), mTranslateVector);
830}
831//-----------------------------------------------------------------------
832void TerrainFrameListener::writeFrames(const std::string filename,
833                                                                           const FrameInfoContainer &frameInfo) const
834{
835        std::ofstream ofstr(filename.c_str());
836        std::vector<frame_info>::const_iterator it, it_end;
837        it_end = frameInfo.end();
838
839        int i = 0;
840
841        for (it = frameInfo.begin(); it < it_end; ++ it, ++ i)
842        {
843                ofstr << StringConverter::toString((*it).position) << " "
844                          << StringConverter::toString((*it).orientation) << " "
845                          << StringConverter::toString((*it).timeElapsed) << " "
846                          << StringConverter::toString((*it).fps) << "\n";
847        }
848
849        std::stringstream d; d << "saved " << i << " frames to file " << filename;
850        Ogre::LogManager::getSingleton().logMessage(d.str());
851
852        ofstr.close();
853}
854//-----------------------------------------------------------------------
855void TerrainFrameListener::loadFrames(const std::string filename, 
856                                                                          FrameInfoContainer &frameInfo)
857{
858        std::ifstream ifstr(filename.c_str());
859        char line[256];
860        frame_info info;
861
862        // reset current values
863        frameInfo.clear();
864       
865        mCurrentFrame = 0;
866        int i = 0;
867        while (!ifstr.eof())
868        {
869                ifstr.getline(line, 256);
870                sscanf(line, "%f %f %f %f %f %f %f %f %f", &info.position.x, &info.position.y, &info.position.z,
871                           &info.orientation.w, &info.orientation.x, &info.orientation.y, &info.orientation.z,
872                           &info.timeElapsed, &info.fps);
873               
874                mFrameInfo.push_back(info);
875               
876                ++ i;
877        }
878       
879        std::stringstream d;
880        d << "loaded " << i << " frames from file " << filename;
881        Ogre::LogManager::getSingleton().logMessage(d.str());
882
883        ifstr.close();
884}
885//-----------------------------------------------------------------------
886void TerrainFrameListener::nextAppState()
887{
888        mCurrentFrame = 0;
889        int lastState = mAppState;
890
891        // transition to the next state
892        mAppState = (mAppState + 1) % STATE_NUM;
893
894        // if last state was replay state: post process
895        if (lastState == REPLAY)
896        {
897        // reset debug text
898                mWindow->setDebugText("");
899                               
900                // hack for producing demo:
901                // we produced precomputed fps during the last replay =>
902                // save them to file
903                if (mSavePrecomputedFps)
904                {
905                        std::string filename = msApprevAlgorithmCaptions[mCurrentAlgorithm] + "_frames.out";
906
907                        writeFrames(filename, mPrecomputedFpsFrameInfo);
908                        mPrecomputedFpsFrameInfo.clear();
909                }
910
911                std::stringstream d;
912                mWalkthroughStats.Print(d, msApprevAlgorithmCaptions[mCurrentAlgorithm]);
913               
914                LogManager::getSingleton().logMessage(d.str());
915        }
916       
917        //-- replay recorded walkthrough
918        if (mAppState == REPLAY)
919        {
920                // no standard recording during replay
921                mRecordFrames = false;
922
923                // no stats information
924                mWindow->setDebugText("");
925
926                // clear previous walktrough
927                mFrameInfo.clear();
928
929                std::string filename;
930               
931                // if recording demo,
932                // we use precomputed fps which corresponds to current method,
933                // e.g., for chc we load precomputed chc fps from disc.
934                if (mRecordDemo)
935                {
936                        filename = msApprevAlgorithmCaptions[mCurrentAlgorithm] + "_frames.out";
937                }
938                else // standard filename
939                {
940                        filename = frames_out_filename;
941                }
942
943                //-- load recorded walkthrough from disk
944                loadFrames(filename, mFrameInfo);
945               
946               
947                // if there are still no recorded frames,
948                // no walkthrough was recorded => set next state
949                if (mFrameInfo.empty())
950                {
951                        nextAppState();
952                }
953                else // actual replay
954                {
955                        mWindow->setDebugText("Replay");
956                       
957                        // reset, because we measure fps stats during walkthrough
958                        // (warning: average fps broken)
959                        mWindow->resetStatistics();
960                        mWalkthroughStats.Reset();
961
962                        //-- initialise frame data
963                        mReplayTimeElapsed = 0;
964
965                        mCamNode->setPosition(mFrameInfo[0].position);
966                        mCamNode->setOrientation(mFrameInfo[0].orientation);
967                }
968        }
969
970}
971//-----------------------------------------------------------------------
972void TerrainFrameListener::toggleRecord()
973{
974        mRecordFrames = !mRecordFrames;
975
976        if (mRecordFrames)
977        {
978                // starting new recording => clear old frame info
979                mFrameInfo.clear();
980        }
981        else // recording just ended => write frame info to file
982        {
983                writeFrames(frames_out_filename, mFrameInfo);
984                mWindow->setDebugText("");
985        }
986}
987//-----------------------------------------------------------------------
988void TerrainFrameListener::changeThreshold(int incr)
989{
990        mVisibilityThreshold += incr;
991
992        if (mVisibilityThreshold < 0)
993        {
994                mVisibilityThreshold = 0;
995        }
996
997        char str[100]; sprintf(str,": %d", mVisibilityThreshold);
998
999        mSceneMgr->setOption("Threshold", &mVisibilityThreshold);
1000        mThresholdInfo->setCaption(str);
1001}
1002//-----------------------------------------------------------------------
1003void TerrainFrameListener::changeAssumedVisibility(int incr)
1004{
1005        mAssumedVisibility += incr;
1006
1007        if (mAssumedVisibility < 0)
1008                mAssumedVisibility = 0;
1009
1010        char str[100]; sprintf(str,": %d", mAssumedVisibility);
1011
1012        mSceneMgr->setOption("AssumedVisibility", &mAssumedVisibility);
1013        mAssumedVisibilityInfo->setCaption(str);
1014}
1015//-----------------------------------------------------------------------
1016void TerrainFrameListener::changeVizScale(const int incr)
1017{
1018        mVizScale += incr;
1019
1020        if (mVizScale < 1)
1021                mVizScale = 1;
1022
1023        mSceneMgr->setOption("NodeVizScale", &mVizScale);
1024}
1025//-----------------------------------------------------------------------
1026void TerrainFrameListener::changeFloorDist(const float incr)
1027{
1028        mFloorDist += incr;
1029
1030        if (mFloorDist < 2)
1031        {       
1032                mFloorDist = 2;
1033        }
1034}
1035//-----------------------------------------------------------------------
1036void TerrainFrameListener::zoomVizCamera(int zoom)
1037{
1038        mVizCameraHeight += zoom;
1039
1040        if(mVizCameraHeight < 0)
1041                mVizCameraHeight = 0;
1042}
1043//-----------------------------------------------------------------------
1044void TerrainFrameListener::nextAlgorithm()
1045{
1046        // possible algorithms: 3 culling algorithms + standard rendering
1047        mCurrentAlgorithm = (mCurrentAlgorithm + 1) %
1048                (GtpVisibility::VisibilityEnvironment::NUM_CULLING_MANAGERS + 1);
1049        applyCurrentAlgorithm();
1050}
1051//-----------------------------------------------------------------------
1052void TerrainFrameListener::applyObjectType()
1053{
1054        if (mCurrentObjectType >= 3) // TODO: define a constant
1055                mCurrentObjectType = 0;
1056
1057        // parameters for new object
1058        mTerrainContentGenerator->SetOffset(msObjectTerrainOffsets[mCurrentObjectType]);
1059        Real scale = msObjectScales[mCurrentObjectType];
1060        mTerrainContentGenerator->SetScale(Vector3(scale, scale, scale));
1061
1062        mCurrentObjectTypeInfo->setCaption(": " + msObjectCaptions[mCurrentObjectType]);
1063}
1064//-----------------------------------------------------------------------
1065void TerrainFrameListener::applyCurrentAlgorithm()
1066{
1067        bool isNormalExecution;
1068       
1069        if (mCurrentAlgorithm < GtpVisibility::VisibilityEnvironment::NUM_CULLING_MANAGERS)
1070        {
1071                isNormalExecution = false;
1072                mSceneMgr->setOption("Algorithm", &mCurrentAlgorithm);
1073        }
1074        else
1075        {       // standard rendering without changed render queue flow
1076                isNormalExecution = true;
1077        }
1078       
1079        mSceneMgr->setOption("NormalExecution", &isNormalExecution);
1080        mAlgorithmInfo->setCaption(": " + msAlgorithmCaptions[mCurrentAlgorithm]);
1081
1082        if (1)
1083        {
1084                std::stringstream d; d << "algorithm: " << msAlgorithmCaptions[mCurrentAlgorithm];
1085                Ogre::LogManager::getSingleton().logMessage(d.str());
1086        }
1087}
1088//-----------------------------------------------------------------------
1089void TerrainFrameListener::updateStats()
1090{
1091        unsigned int opt = 0;
1092        char str[100];
1093       
1094        static String currFpsString = "Current FPS: ";
1095        static String avgFpsString = "Average FPS: ";
1096        static String bestFpsString = "Best FPS: ";
1097        static String worstFpsString = "Worst FPS: ";
1098        static String trisString = "Triangle Count: ";
1099
1100        int currentFps;
1101       
1102        // HACK for demo: use precomputed FPS instead of real FPS
1103        if (mRecordDemo)
1104        {
1105                currentFps = mPrecomputedFps;
1106        }
1107        else
1108        {
1109                currentFps = mWindow->getStatistics().lastFPS;
1110        }
1111
1112#if 0
1113        // HACK: take pure rendering time, only measures the render call
1114        long pureRenderTime = mTimeFrameEnded - mTimeFrameStarted;
1115
1116        if (pureRenderTime)
1117        {
1118                mPureRenderTimeFps = 1000.0f / (float) pureRenderTime;
1119        }
1120        currentFps = mPureRenderRenderTimeFps;
1121        //std::stringstream d; d << "Pure render time fps: " << mPureRenderTimeFps << "\n";
1122        //Ogre::LogManager::getSingleton().logMessage(d.str());
1123#endif
1124       
1125        unsigned int nodeInfo[3];
1126    mSceneMgr->getOption("NumRenderedNodes", nodeInfo);
1127        mSceneMgr->getOption("NumQueryCulledNodes", nodeInfo+1);
1128        mSceneMgr->getOption("NumFrustumCulledNodes", nodeInfo+2);
1129
1130
1131        mWalkthroughStats.UpdateFrame(currentFps,
1132                                                                  mWindow->getBestFPS(),
1133                                                                  mWindow->getWorstFPS(),
1134                                                                  (int)mWindow->getTriangleCount(),
1135                                                                  nodeInfo[0],
1136                                                                  nodeInfo[1],
1137                                                                  nodeInfo[2]);
1138
1139        // HACK: compute average fps ourselfs, because ogre avg. fps is wrong
1140        // TODO: update only once per second
1141        float avgFps = (float)mWalkthroughStats.mAccFps / (float)(mWalkthroughStats.mFrameCount);
1142       
1143
1144        // update stats when necessary
1145    try
1146        {
1147                OverlayElement* guiAvg = OverlayManager::getSingleton().getOverlayElement("Core/AverageFps");
1148                OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("Core/CurrFps");
1149                OverlayElement* guiBest = OverlayManager::getSingleton().getOverlayElement("Core/BestFps");
1150                OverlayElement* guiWorst = OverlayManager::getSingleton().getOverlayElement("Core/WorstFps");
1151
1152                const RenderTarget::FrameStats& stats = mWindow->getStatistics();
1153
1154                // HACK: take newly computed avg. fps instead of Ogre avg fps and update only once per second
1155                if (mTimeDelay < 0)
1156                {               
1157                        guiAvg->setCaption(avgFpsString + StringConverter::toString(avgFps) + " ms");
1158                        //guiCurr->setCaption(currFpsString + StringConverter::toString(currentFps));
1159                }
1160
1161                if (0)
1162                {
1163                        std::stringstream d;
1164                        d << "fps: " << StringConverter::toString(currentFps) << ", "
1165                          << "avg fps: " << StringConverter::toString(avgFps);
1166                        LogManager::getSingleton().logMessage(d.str());
1167                }
1168
1169                //guiAvg->setCaption(avgFps + StringConverter::toString(stats.avgFPS));
1170                guiCurr->setCaption(currFpsString + StringConverter::toString(currentFps));
1171               
1172                //std::stringstream d; d << "frame rate :" << stats.lastFPS;
1173                //Ogre::LogManager::getSingleton().logMessage(d.str());
1174
1175                guiBest->setCaption(bestFpsString + StringConverter::toString(stats.bestFPS)
1176                                                        + " " + StringConverter::toString(stats.bestFrameTime) + " ms");
1177                guiWorst->setCaption(worstFpsString + StringConverter::toString(stats.worstFPS)
1178                                                         + " " + StringConverter::toString(stats.worstFrameTime) + " ms");
1179
1180                OverlayElement* guiTris = OverlayManager::getSingleton().getOverlayElement("Core/NumTris");
1181        guiTris->setCaption(trisString + StringConverter::toString(stats.triangleCount));
1182               
1183                //LogManager::getSingleton().logMessage(StringConverter::toString(stats.triangleCount));
1184
1185                OverlayElement* guiDbg = OverlayManager::getSingleton().getOverlayElement("Core/DebugText");
1186                guiDbg->setCaption(mWindow->getDebugText());
1187
1188
1189                //-- culling stats
1190
1191                mSceneMgr->getOption("NumFrustumCulledNodes", &opt); sprintf(str,": %d", opt);
1192                mFrustumCulledNodesInfo->setCaption(str);
1193
1194                mSceneMgr->getOption("NumQueryCulledNodes", &opt); sprintf(str,": %d", opt);
1195                mQueryCulledNodesInfo->setCaption(str);
1196       
1197                mSceneMgr->getOption("NumHierarchyNodes", &opt); sprintf(str,": %d", opt);
1198                mHierarchyNodesInfo->setCaption(str);
1199
1200                mSceneMgr->getOption("NumRenderedNodes", &opt); sprintf(str,": %d", opt);
1201                mRenderedNodesInfo->setCaption(str);
1202
1203                sprintf(str,": %d", mTerrainContentGenerator->GetObjectCount());
1204                mObjectsCountInfo->setCaption(str);
1205
1206                // take old value into account in order to create no sudden changes
1207                mSceneMgr->getOption("NumQueriesIssued", &opt);
1208                mDelayedQueriesIssued = mDelayedQueriesIssued * 0.8 + (float)opt * 0.2f;
1209                sprintf(str,": %d", (int)mDelayedQueriesIssued);
1210                mQueriesIssuedInfo->setCaption(str);
1211
1212                mSceneMgr->getOption("NumTraversedNodes", &opt);
1213                mDelayedTraversedNodes = mDelayedTraversedNodes * 0.8 + (float)opt * 0.2f;
1214                sprintf(str,": %d", (int)mDelayedTraversedNodes);
1215                mTraversedNodesInfo->setCaption(str);
1216
1217                if (mRecordVideo)
1218                {
1219                        // update stats for demo
1220                        mMyStatsAlgorithmInfo->setCaption(msApprevAlgorithmCaptions[mCurrentAlgorithm]);
1221                        sprintf(str,": %d", (int)currentFps);
1222                        mMyStatsFpsInfo->setCaption(str);
1223                }
1224
1225        }
1226        catch (...)
1227        {
1228                // ignore
1229        }
1230}
1231//-----------------------------------------------------------------------
1232void TerrainFrameListener::setTestGeometryForVisibleLeaves(bool testGeometryForVisibleLeaves)
1233{
1234        mSceneMgr->setOption("TestGeometryForVisibleLeaves", &mTestGeometryForVisibleLeaves);
1235       
1236        // disable optimization for transparent objects, becaue otherwise visible
1237        // transparents could be skipped
1238        if (mTestGeometryForVisibleLeaves)
1239                mTestGeometryForVisibleLeavesInfo->setCaption(": true");
1240        else
1241                mTestGeometryForVisibleLeavesInfo->setCaption(": false");
1242}
1243//-----------------------------------------------------------------------
1244void TerrainFrameListener::setTestGeometryBounds(bool testGeometryBounds)
1245{
1246        mSceneMgr->setOption("TestGeometryBounds", &mTestGeometryBounds);
1247       
1248        if (mTestGeometryBounds)
1249                LogManager::getSingleton().logMessage("Testing geometry boundaries");
1250        else
1251                LogManager::getSingleton().logMessage("Testing bounding box of hierarchy node");
1252}
1253
1254//-----------------------------------------------------------------------
1255void TerrainFrameListener::toggleShowOctree()
1256{
1257        mShowOctree = !mShowOctree;
1258        mSceneMgr->setOption("ShowOctree", &mShowOctree);
1259}
1260//-----------------------------------------------------------------------
1261void TerrainFrameListener::toggleShowViewCells()
1262{
1263        mShowViewCells = !mShowViewCells;
1264        mSceneMgr->setOption("ShowViewCells", &mShowViewCells);
1265}
1266//-----------------------------------------------------------------------
1267void TerrainFrameListener::toggleUseViewCells()
1268{
1269        // HACK: no view cells for hilly terrain
1270        if (mApplication->msShowHillyTerrain)
1271                return;
1272
1273        mUseViewCells = !mUseViewCells;
1274
1275        // load on demand
1276        if (mUseViewCells)// && !mViewCellsLoaded)
1277        {
1278                LogManager::getSingleton().logMessage("using view cells");
1279                mLoadingOverlay->show();
1280
1281                // call once to load view cell loading overlay
1282                mWindow->update();
1283                mViewCellsLoaded = mApplication->LoadViewCells(mApplication->mViewCellsFilename);
1284               
1285                if (!mViewCellsLoaded)
1286                        LogManager::getSingleton().logMessage("loading view cells failed");
1287                else
1288                        LogManager::getSingleton().logMessage("view cells successfully loaded");
1289       
1290                mLoadingOverlay->hide();
1291        }
1292
1293        if (mUseViewCells)
1294                mViewCellsInfo->setCaption(": on");
1295        else
1296                mViewCellsInfo->setCaption(": off");
1297
1298        mSceneMgr->setOption("UseViewCells", &mUseViewCells);
1299}
1300//-----------------------------------------------------------------------
1301void TerrainFrameListener::toggleUseDepthPass()
1302{
1303        mUseDepthPass = !mUseDepthPass;
1304
1305        mSceneMgr->setOption("UseDepthPass", &mUseDepthPass);
1306       
1307        if (mUseDepthPass)
1308        {
1309                mUseDepthPassInfo->setCaption(": true");
1310        }
1311        else
1312        {
1313                mUseDepthPassInfo->setCaption(": false");
1314        }
1315}
1316//-----------------------------------------------------------------------
1317void TerrainFrameListener::toggleFlushQueue()
1318{
1319        mFlushQueue = !mFlushQueue;
1320
1321        mSceneMgr->setOption("FlushQueue", &mFlushQueue);
1322       
1323        if (mFlushQueue)
1324        {
1325                LogManager::getSingleton().logMessage("flushing queue");
1326                //mFlushQueueInfo->setCaption(": true");
1327        }
1328        else
1329        {
1330                LogManager::getSingleton().logMessage("not flushing queue");
1331                //mFlushQueueInfo->setCaption(": false");
1332        }
1333}
1334
1335void TerrainFrameListener::showBoundingBoxes()
1336{
1337        m_iShowBoundingBoxes++;
1338        m_iShowBoundingBoxes%=2;
1339        mSceneMgr->setOption("ShowBiHierarchy", (void*)&m_iShowBoundingBoxes);
1340}
1341
1342
1343//-----------------------------------------------------------------------
1344void TerrainFrameListener::toggleShowViz()
1345{
1346        mVisualizeCulledNodes = mShowVisualization = !mShowVisualization;
1347       
1348        // create viewport with priority VIZ_VIEWPORT_Z_ORDER:
1349        // will be rendered over standard viewport
1350        if (mShowVisualization)
1351        {       
1352                Viewport *vizvp = mWindow->addViewport(mVizCamera,
1353                        VIZ_VIEWPORT_Z_ORDER, 0.6, 0.6, 0.4, 0.4);
1354                               
1355                vizvp->setBackgroundColour(ColourValue(0.0, 0.3, 0.2, 1));
1356
1357                vizvp->setOverlaysEnabled(false);
1358                // Alter the camera aspect ratio to match the viewport
1359        mVizCamera->setAspectRatio(Real(vizvp->getActualWidth()) /
1360                                                                   Real(vizvp->getActualHeight()));
1361               
1362                mSceneMgr->setOption("VisualizeCulledNodes", &mVisualizeCulledNodes);
1363       
1364        }
1365        else
1366        {
1367                // remove visualization viewport
1368                mWindow->removeViewport(VIZ_VIEWPORT_Z_ORDER);
1369
1370                // octree bounding boxes are shown for visualization purpose, reset now
1371                mSceneMgr->setOption("ShowOctree", &mShowOctree);
1372        }
1373}
1374//-----------------------------------------------------------------------
1375void TerrainFrameListener::toggleShowShadows()
1376{
1377        mShowShadows = !mShowShadows;
1378
1379        mSunLight->setCastShadows(mShowShadows);
1380
1381        if (mShowShadows)
1382        {
1383                mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE);
1384                //mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_MODULATIVE);
1385                //mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);           
1386        }
1387        else
1388        {
1389                mSceneMgr->setShadowTechnique(SHADOWTYPE_NONE);
1390        }
1391
1392}
1393//-----------------------------------------------------------------------
1394void TerrainFrameListener::nextNodeVizMode()
1395{
1396        mNodeVizMode = (mNodeVizMode + 1) % NODEVIZ_MODES_NUM;
1397
1398        bool renderNodesForViz =
1399                (mNodeVizMode == NODEVIZ_RENDER_NODES) ||
1400                (mNodeVizMode == NODEVIZ_RENDER_NODES_AND_CONTENT);
1401
1402        bool renderNodesContentForViz = (mNodeVizMode == NODEVIZ_RENDER_NODES_AND_CONTENT);
1403        //bool renderNodesContentForViz = mNodeVizMode == NODEVIZ_RENDER_GEOMETRY;
1404
1405        bool renderPvsForViz = (mNodeVizMode == NODEVIZ_RENDER_PVS);
1406
1407        mSceneMgr->setOption("RenderNodesForViz", &renderNodesForViz);
1408        mSceneMgr->setOption("RenderNodesContentForViz", &renderNodesContentForViz);
1409        mSceneMgr->setOption("RenderPvsForViz", &renderPvsForViz);
1410}
1411//-----------------------------------------------------------------------
1412void TerrainFrameListener::keyPressed(KeyEvent* e)
1413{
1414        // hide exact visibility query overlay
1415        if (mShowQueryStats)
1416        {
1417                mQueryOverlay->hide();
1418                mShowQueryStats = false;
1419        }
1420
1421        switch(e->getKey())
1422        {
1423       
1424        case KC_SUBTRACT:
1425                changeThreshold(-10);
1426                break;
1427        case KC_ADD:
1428                changeThreshold(10);
1429                break;
1430
1431        // assumed visible #frames
1432        case KC_9:
1433                changeAssumedVisibility(-5);
1434                break;
1435        case KC_0:
1436                changeAssumedVisibility(5);
1437                break;
1438
1439        case KC_ESCAPE:
1440                mShutdownRequested = true;
1441                e->consume();
1442                return;
1443        // the visibility algorithm
1444        case KC_SPACE:
1445                nextAlgorithm();
1446                break;
1447        case KC_LSHIFT:
1448                mShiftPressed = true;
1449                break;
1450        case KC_DELETE:
1451                mDeleteObjects = true;
1452                break;
1453        // not working yet
1454        case KC_C:
1455                if (mItemBufferMode != GtpVisibility::QueryManager::GEOMETRY_VISIBILITY)
1456                {
1457                        mItemBufferMode = GtpVisibility::QueryManager::GEOMETRY_VISIBILITY;
1458                }
1459                else
1460                {
1461                        mItemBufferMode = GtpVisibility::QueryManager::PATCH_VISIBILITY;
1462                }
1463                break;
1464         case KC_E: // hack for recording demo using precomputed fps
1465                mSavePrecomputedFps = !mSavePrecomputedFps;
1466                break;
1467    case KC_F:
1468                nextFilter();
1469                break;
1470        case KC_G:
1471                mTestGeometryForVisibleLeaves = !mTestGeometryForVisibleLeaves;
1472                setTestGeometryForVisibleLeaves(mTestGeometryForVisibleLeaves);
1473                break;
1474        case KC_H:
1475                toggleShowShadows();
1476                break;
1477        case KC_L:
1478                toggleShowViewCells();
1479                break;
1480        case KC_M: // hack for recording demo using precomputed fps
1481                mRecordDemo = !mRecordDemo;
1482                break;
1483        case KC_O:
1484                switchMouseMode();
1485                break;
1486        case KC_P:
1487                toggleDisplayCameraDetails();
1488                break;
1489        // if view cells + PVS are used for rendering
1490        case KC_V:
1491                toggleUseViewCells();
1492                break;
1493        case KC_Q:
1494                mTestGeometryBounds = !mTestGeometryBounds;
1495                setTestGeometryBounds(mTestGeometryBounds);
1496                break;
1497        case KC_R:
1498                nextSceneDetailLevel();
1499                break;
1500        case KC_T:
1501                toggleShowOctree();
1502                break;
1503        case KC_U:
1504                mCamNode->resetOrientation();
1505                mCamNode->setPosition(mApplication->mInitialPosition);
1506                break;
1507        case KC_X:
1508                toggleUseDepthPass();
1509                break;
1510        case KC_I:
1511                toggleFlushQueue();
1512                break;
1513        case KC_Z:
1514                mCamNode->resetOrientation();
1515                break;
1516        case KC_B:
1517                showBoundingBoxes();
1518                break;
1519        case KC_3:
1520                if (m_iBoundingBoxLevel>0) m_iBoundingBoxLevel--;
1521                mSceneMgr->setOption("HiLiteLevel", (void*)&m_iBoundingBoxLevel);
1522                break;
1523        case KC_4:
1524                m_iBoundingBoxLevel++;
1525                mSceneMgr->setOption("HiLiteLevel", (void*)&m_iBoundingBoxLevel);
1526                break;
1527
1528
1529        ///////////////
1530        //-- visualization
1531
1532        case KC_1:
1533                toggleShowViz();
1534                break;
1535        case KC_2:
1536                nextNodeVizMode();
1537                break;
1538       
1539        case KC_F12:
1540                {
1541                        LogManager::getSingleton().logMessage("generating 500 objects");
1542                        // generate new objects
1543                        const int numObjects = 500;
1544                        mApplication->generateScene(numObjects, mCurrentObjectType);
1545                }
1546                break;
1547        case KC_F1:
1548                toggleShowHelp();
1549                break;
1550        case KC_F2:
1551                mStatsOn = !mStatsOn;
1552                showStats(mStatsOn);
1553                break;
1554        case KC_F3:
1555                nextAppState();
1556                break;
1557        case KC_F4:
1558                toggleRecord();
1559                break;
1560        case KC_F5:
1561                {
1562                        mSceneMgr->_findVisibleObjects(mCamera, false);
1563                        if (0) applyVisibilityQuery(false, mShiftPressed, mUseItemBuffer);
1564                        break;
1565                }
1566        case KC_F6:
1567                {
1568                        const bool fromPoint = true;
1569                        if (1) applyVisibilityQuery(fromPoint, mShiftPressed, mUseItemBuffer);
1570                        break;
1571                }       
1572        // change type of object to be generated
1573        case KC_F7:
1574                ++ mCurrentObjectType;
1575                applyObjectType();
1576                break;
1577        case KC_F8:
1578                mTerrainContentGenerator->WriteObjects(objects_out_filename);
1579                break;
1580        case KC_F9:
1581                mUseAnimation = !mUseAnimation;
1582                break;
1583        case KC_F10:
1584                mRecordVideo = !mRecordVideo;
1585                break;         
1586        case KC_F11:
1587                takeScreenshot();
1588                break;
1589        default:
1590                break;
1591        }
1592
1593        CEGUI::System::getSingleton().injectKeyDown(e->getKey());
1594        CEGUI::System::getSingleton().injectChar(e->getKeyChar());
1595        e->consume();
1596}
1597//-----------------------------------------------------------------------
1598void TerrainFrameListener::keyReleased(KeyEvent* e)
1599{
1600        if (e->getKey() == KC_LSHIFT)
1601        {
1602                mShiftPressed = false;
1603        }
1604       
1605        CEGUI::System::getSingleton().injectKeyUp(e->getKey());
1606        e->consume();
1607}
1608//-----------------------------------------------------------------------
1609void TerrainFrameListener::keyClicked(KeyEvent* e)
1610{
1611        // Do nothing
1612        e->consume();
1613}
1614//-----------------------------------------------------------------------
1615void TerrainFrameListener::addFrameInfo(FrameInfoContainer &frameInfos,
1616                                                                                SceneNode *camNode,
1617                                                                                Real timeElapsed)
1618{
1619        frame_info info;
1620
1621        info.orientation = mCamNode->getOrientation();
1622        info.position = mCamNode->getPosition();
1623        info.timeElapsed = timeElapsed;
1624        info.fps = mWindow->getStatistics().lastFPS;
1625
1626        frameInfos.push_back(info);
1627}
1628//-----------------------------------------------------------------------
1629void TerrainFrameListener::setCurrentFrameInfo(const Real timeElapsed)
1630{
1631        //-- find current frame relative to elapsed frame time         
1632        mReplayTimeElapsed -= timeElapsed;
1633       
1634        while ((mReplayTimeElapsed <= 0) && (mCurrentFrame < (int)mFrameInfo.size() - 1))
1635        {
1636                mReplayTimeElapsed += mFrameInfo[mCurrentFrame ++].timeElapsed;
1637        }
1638
1639
1640        // TODO: crashes here if recording / replaying on the same time!!
1641        const frame_info new_frame = mFrameInfo[mCurrentFrame];
1642        const frame_info old_frame = mFrameInfo[mCurrentFrame - 1];
1643               
1644        /////////////
1645        //-- interpolate frames
1646        Real factor = 1;
1647
1648        if (old_frame.timeElapsed > 0)
1649        {
1650                factor = mReplayTimeElapsed / old_frame.timeElapsed;
1651        }
1652
1653        const Vector3 camPos = old_frame.position + factor
1654                * (new_frame.position - old_frame.position);
1655
1656        // interpolate the orientation
1657        const Quaternion camOrienation =
1658                Quaternion::Slerp(factor, old_frame.orientation, new_frame.orientation, true);
1659
1660        // HACK for demo: interpolate precomputed fps
1661        mPrecomputedFps = old_frame.fps + factor * (new_frame.fps - old_frame.fps);
1662
1663        mCamNode->setPosition(camPos);
1664        mCamNode->setOrientation(camOrienation);
1665       
1666        // stop replaying after one full walkthrough
1667        if (mCurrentFrame == (int)mFrameInfo.size() - 1)
1668        {
1669                nextAppState();
1670        }
1671}
1672//-----------------------------------------------------------------------   
1673bool TerrainFrameListener::processUnbufferedKeyInput(const FrameEvent& evt)
1674{
1675        bool cursorPressed = false;
1676       
1677        /* Move camera forward by keypress. */
1678    if (mInputDevice->isKeyDown(KC_UP) || mInputDevice->isKeyDown(KC_W))
1679        {
1680                mTranslateVector.z = -mMoveScale;
1681                cursorPressed = true;
1682        }
1683    /* Move camera backward by keypress. */
1684    if (mInputDevice->isKeyDown(KC_DOWN) || mInputDevice->isKeyDown(KC_S))
1685    {
1686                mTranslateVector.z = mMoveScale;
1687                cursorPressed = true;
1688    }
1689
1690        if (mInputDevice->isKeyDown(KC_A))
1691        {
1692                mTranslateVector.x -= mMoveScale;
1693                mTranslateVector.y += mMoveScale;
1694
1695                cursorPressed = true;
1696        }
1697
1698        if (mInputDevice->isKeyDown(KC_D))
1699        {
1700                mTranslateVector.x += mMoveScale;
1701                mTranslateVector.y -= mMoveScale;
1702
1703                cursorPressed = true;
1704        }
1705
1706    if (mInputDevice->isKeyDown(KC_RIGHT))
1707    {
1708        mCamNode->yaw(-mRotScale, Ogre::Node::TS_WORLD);
1709                cursorPressed = true;
1710    }
1711
1712    if (mInputDevice->isKeyDown(KC_LEFT))
1713    {
1714        mCamNode->yaw(mRotScale, Ogre::Node::TS_WORLD);
1715                cursorPressed = true;
1716    }
1717
1718        // visualization camera
1719        if (mInputDevice->isKeyDown(KC_3))
1720                zoomVizCamera(50);
1721
1722        if (mInputDevice->isKeyDown(KC_4))
1723                zoomVizCamera(-50);
1724
1725        // scale of the visualized objects
1726#if 0
1727        if (mInputDevice->isKeyDown(KC_5))
1728                changeVizScale(-1);
1729
1730        if (mInputDevice->isKeyDown(KC_6))
1731                changeVizScale(1);
1732#else
1733
1734        const int mFactor = 20;
1735        if (mInputDevice->isKeyDown(KC_5))
1736                mMoveFactor += mFactor;
1737
1738        if (mInputDevice->isKeyDown(KC_6))
1739                mMoveFactor -= mFactor;
1740#endif
1741
1742        // distance to floor
1743        if (mInputDevice->isKeyDown(KC_7))
1744                changeFloorDist(-1);
1745
1746        if (mInputDevice->isKeyDown(KC_8))
1747                changeFloorDist(1);
1748
1749
1750        // show the results
1751        if (cursorPressed && mShowQueryStats)
1752        {
1753                mQueryOverlay->hide();
1754                mShowQueryStats = false;
1755        }
1756
1757    // Return true to continue rendering
1758    return true;
1759}
1760//-----------------------------------------------------------------------
1761void TerrainFrameListener::nextFilter()
1762{
1763        switch (mFiltering)
1764        {
1765        case TFO_BILINEAR:
1766                mFiltering = TFO_TRILINEAR;
1767                mAniso = 1;
1768                break;
1769        case TFO_TRILINEAR:
1770                mFiltering = TFO_ANISOTROPIC;
1771                mAniso = 8;
1772                break;
1773        case TFO_ANISOTROPIC:
1774                mFiltering = TFO_BILINEAR;
1775                mAniso = 1;
1776                break;
1777        default:
1778                break;
1779        }
1780
1781    MaterialManager::getSingleton().setDefaultTextureFiltering(mFiltering);
1782    MaterialManager::getSingleton().setDefaultAnisotropy(mAniso);
1783
1784        // reload stats
1785    showStats(mStatsOn);
1786}
1787//-----------------------------------------------------------------------
1788void TerrainFrameListener::nextSceneDetailLevel()
1789{
1790#if OGRE_103
1791        mSceneDetailIndex = (mSceneDetailIndex + 1) % 3;
1792        switch (mSceneDetailIndex)
1793        {
1794                case 0:
1795                        mCamera->setDetailLevel(SDL_SOLID);
1796                        break;
1797                case 1:
1798                        mCamera->setDetailLevel(SDL_WIREFRAME);
1799                        break;
1800                case 2:
1801                        mCamera->setDetailLevel(SDL_POINTS);
1802                        break;
1803        }
1804#endif
1805}
1806//-----------------------------------------------------------------------
1807void TerrainFrameListener::takeVideoFrame(std::ofstream &ofstr)
1808{
1809        char name[50];
1810
1811        sprintf(name, "frame_%05d.tga", ++mNumVideoFrames);
1812    mWindow->writeContentsToFile(name);
1813    //mWindow->setDebugText(String("Wrote ") + name);
1814
1815        ofstr << name << "\n";
1816}
1817//-----------------------------------------------------------------------
1818void TerrainFrameListener::takeScreenshot()
1819{
1820        char name[50];
1821
1822        sprintf(name, "screenshot_%05d.png", ++mNumScreenShots);
1823    mWindow->writeContentsToFile(name);
1824    //mWindow->setDebugText(String("Wrote ") + name);
1825}
1826//-----------------------------------------------------------------------
1827void TerrainFrameListener::toggleDisplayCameraDetails()
1828{
1829        mDisplayCameraDetails = !mDisplayCameraDetails;
1830       
1831    if (!mDisplayCameraDetails)
1832        {
1833                mWindow->setDebugText("");
1834        }
1835}
1836//-----------------------------------------------------------------------
1837void TerrainFrameListener::showStats(bool show)
1838{
1839        if (mDebugOverlay && mCullStatsOverlay)
1840        {
1841                if (show)
1842                {
1843                        mDebugOverlay->show();
1844                        mCullStatsOverlay->show();
1845                }
1846                else
1847                {
1848                        mDebugOverlay->hide();
1849                        mCullStatsOverlay->hide();
1850                }
1851        }
1852}
1853
1854//-----------------------------------------------------------------------
1855void TerrainFrameListener::toggleShowHelp()
1856{
1857        mShowHelp = !mShowHelp;
1858
1859        if (mShowHelp)
1860        {
1861                mHelpOverlay->show();
1862        }
1863        else
1864        {
1865                mHelpOverlay->hide();
1866        }
1867}
1868//-----------------------------------------------------------------------
1869void TerrainFrameListener::initOverlayElement(OverlayElement **elInfo, String ext,
1870                                                                                          String name, int top, String caption)
1871{
1872        OverlayElement *el =
1873                OverlayManager::getSingleton().getOverlayElement(ext + name);
1874
1875        (*elInfo) = OverlayManager::getSingleton().getOverlayElement(ext + name + "Info");
1876        (*elInfo)->setCaption(caption);
1877
1878        el->setTop(top);
1879        (*elInfo)->setTop(top);
1880}
1881//-----------------------------------------------------------------------
1882void TerrainFrameListener::initHelpOverlayElement(String name, int top)
1883{
1884        OverlayElement *el = OverlayManager::getSingleton().getOverlayElement(
1885                "Example/Visibility/Help/" + name);
1886
1887        el->setTop(top);
1888}
1889//-----------------------------------------------------------------------
1890void TerrainFrameListener::initHelpOverlay()
1891{
1892        const int vert_space = 15;
1893        int top = 30;
1894
1895        initHelpOverlayElement("ShowHelp", top); top += vert_space;
1896        initHelpOverlayElement("Stats", top); top += vert_space;
1897        initHelpOverlayElement("AppState", top); top += vert_space;
1898        initHelpOverlayElement("Recorded", top); top += vert_space;
1899        initHelpOverlayElement("Animation", top); top += vert_space;
1900        initHelpOverlayElement("Video", top); top += vert_space;
1901        initHelpOverlayElement("Screenshots", top); top += vert_space;
1902        initHelpOverlayElement("WriteOut", top); top += vert_space;
1903
1904
1905        top +=vert_space;
1906        initHelpOverlayElement("SceneDetail", top); top += vert_space;
1907        initHelpOverlayElement("DisplayCameraDetails", top); top += vert_space;
1908        initHelpOverlayElement("DisplayOctree", top); top += vert_space;
1909        initHelpOverlayElement("UseShadows", top); top += vert_space;
1910        initHelpOverlayElement("Filter", top); top += vert_space;
1911
1912        //-- visualization
1913        top += vert_space;
1914        initHelpOverlayElement("VizSection", top); top += vert_space;
1915        initHelpOverlayElement("Viz", top); top += vert_space;
1916        initHelpOverlayElement("NextVizMode", top); top += vert_space;
1917        initHelpOverlayElement("ZoomViz", top); top += vert_space;
1918
1919
1920        //-- visibility queries
1921        top += vert_space;
1922        initHelpOverlayElement("VisQuery", top); top += vert_space;
1923        initHelpOverlayElement("FromCameraQuery", top); top += vert_space;
1924        initHelpOverlayElement("FromPointQuery", top); top += vert_space;
1925        initHelpOverlayElement("QueryType", top); top += vert_space;
1926        initHelpOverlayElement("QueryTarget", top); top += vert_space;
1927
1928        //-- object generation
1929        top += vert_space;
1930        initHelpOverlayElement("SceneObjects", top); top += vert_space;
1931        initHelpOverlayElement("PlaceObjects", top); top += vert_space;
1932        initHelpOverlayElement("GenerateObjects", top); top += vert_space;
1933        initHelpOverlayElement("RemoveObjects", top); top += vert_space;
1934        initHelpOverlayElement("DropObject", top); top += vert_space;
1935
1936        OverlayElement *helpPanel = OverlayManager::getSingleton().getOverlayElement(
1937                "Example/Visibility/Help/HelpPanel");
1938
1939        helpPanel->setHeight(top + 10);
1940}
1941//-----------------------------------------------------------------------
1942void TerrainFrameListener::initVisStatsOverlay()
1943{
1944        const int border_height = 10;
1945        const int vert_space = 15;
1946
1947        //-- visibility culling stats overlay
1948        int top = border_height;
1949
1950        String ext = "Example/Visibility/";
1951       
1952        initOverlayElement(&mAlgorithmInfo, ext, "Algorithm", top,
1953                ": " + msAlgorithmCaptions[mCurrentAlgorithm]); top += vert_space;
1954
1955        initOverlayElement(&mThresholdInfo, ext, "Threshold", top, ": 0"); top += vert_space;
1956        initOverlayElement(&mTestGeometryForVisibleLeavesInfo, ext,
1957                                           "TestGeometryForVisibleLeaves", top, ": true"); top += vert_space;
1958        initOverlayElement(&mUseDepthPassInfo, ext, "UseDepthPass", top, ": false"); top += vert_space;
1959        initOverlayElement(&mAssumedVisibilityInfo, ext, "AssumedVisibility", top, ": 0"); top += vert_space;
1960        initOverlayElement(&mCurrentObjectTypeInfo, ext, "CurrentObjectType", top, ": "); top += vert_space;
1961        initOverlayElement(&mViewCellsInfo, ext, "ViewCells", top, ": "); top += vert_space;
1962        //initOverlayElement(&mHelpInfo, ext, "Help", top, ": "); top += vert_space;
1963
1964        OverlayElement *optionsPanel = OverlayManager::getSingleton().
1965                getOverlayElement("Example/Visibility/VisibilityPanel");
1966
1967        optionsPanel->setHeight(top + border_height);
1968
1969        top = border_height;
1970        //ext = "Example/Visibility/";
1971        initOverlayElement(&mFrustumCulledNodesInfo, ext, "FrustumCulledNodes", top, ": 0"); top += vert_space;
1972        initOverlayElement(&mQueryCulledNodesInfo, ext, "QueryCulledNodes", top, ": 0"); top += vert_space;
1973        initOverlayElement(&mTraversedNodesInfo, ext, "TraversedNodes", top, ": 0"); top += vert_space;
1974        initOverlayElement(&mHierarchyNodesInfo, ext, "HierarchyNodes", top, ": 0"); top += vert_space;
1975        initOverlayElement(&mRenderedNodesInfo, ext, "RenderedNodes", top, ": 0"); top += vert_space;
1976        initOverlayElement(&mObjectsCountInfo, ext, "ObjectsCount", top, ": 0"); top += vert_space;
1977        initOverlayElement(&mQueriesIssuedInfo, ext, "QueriesIssued", top, ": 0"); top += vert_space;
1978
1979        OverlayElement *visPanel = OverlayManager::getSingleton().
1980                getOverlayElement("Example/Visibility/VisibilityStatsPanel");
1981
1982        visPanel->setHeight(top + border_height);
1983}
1984//-----------------------------------------------------------------------
1985void TerrainFrameListener::initQueryOverlay()
1986{
1987        const int border_height = 10;
1988        const int vert_space = 15;
1989
1990        //-- visibility culling stats overlay
1991        int top = border_height + 25;
1992
1993        const String ext = "Example/Visibility/Query/";
1994           
1995        initOverlayElement(&mQueryTypeInfo , ext, "QueryType", top,     ": 0"); top += vert_space;
1996       
1997        initOverlayElement(&mQueryVisibleNodesInfo , ext, "VisibleNodes", top,  ": 0"); top += vert_space;
1998        initOverlayElement(&mQueryVisibleGeometryInfo , ext, "VisibleGeometry", top,    ": 0"); top += vert_space;
1999        initOverlayElement(&mQueryVisiblePatchInfo , ext, "VisiblePatches", top,        ": 0"); top += vert_space;
2000       
2001        initOverlayElement(&mQueryNodeVisibilityInfo , ext, "NodeVisibility", top,      ": 0"); top += vert_space;
2002        initOverlayElement(&mQueryGeometryVisibilityInfo , ext, "GeometryVisibility", top,      ": 0"); top += vert_space;
2003        initOverlayElement(&mQueryPatchVisibilityInfo , ext, "PatchVisibility", top,    ": 0"); top += vert_space;
2004
2005        OverlayElement *queryPanel =
2006                OverlayManager::getSingleton().getOverlayElement("Example/Visibility/Query/QueryPanel");
2007
2008        queryPanel->setHeight(top + border_height);
2009}
2010//-----------------------------------------------------------------------
2011void TerrainFrameListener::setAlgorithm(const int algorithm)
2012{
2013        mCurrentAlgorithm = algorithm;
2014        applyCurrentAlgorithm();
2015}
Note: See TracBrowser for help on using the repository browser.