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

Revision 2352, 57.2 KB checked in by vizrt_christian_seidl, 17 years ago (diff)

FEATURE: Changes for BITerrainManager

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