Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/objecthierarchy2/src/orxonox/gamestates/GSGraphics.cc @ 2344

Last change on this file since 2344 was 2344, checked in by rgrieder, 15 years ago

Completed destruction of static elements like XMLPort, Identifier, etc.
Of initially about 250 memory leaks (not in the actual meaning but the memory was never freed anyway) only 1 remains in TinyCpp.

  • Core class is now a normal Singleton that gets created and destroyed in main.
  • The same goes for Language, LuaBind, SignalHandler and PlayerManager.
  • Added a new std::set to the CommandExecutor so that the external ConsoleCommands can get destroyed too.
  • Code for destroying CommandLineArguments
  • Added destruction code for ConstructionCallbacks in Identifier
  • Moved internal identifier map (the one with the typeid(.) names) in a static function in Identifier. This was necessary in order to destroy ALL Identifiers with the static destruction function. Before it was possible to create an Identifier with having a class instance (that would call RegisterObject) for instance by simply accessing it via getIdentifier.
  • Removed a big memory leak in Button (forgot to destroy the ConfigValueContainers)
  • Added destruction code for InputBufferListenerTuples in InputBuffer destructor.
  • Added destruction code for load and save executors in both XMLPortParam and XMLPortObject
  • Added destruction code for ConsoleCommands in GSRoot, GSGraphics and GSLevel (temporary solution anyway)
  • Deleting the CEGUILua script module seems to work properly now, one memory leak less (GUIManager.cc)
  • Added global destruction calls in Main.cc
  • Property svn:eol-style set to native
File size: 19.9 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "OrxonoxStableHeaders.h"
30#include "GSGraphics.h"
31
32#include <fstream>
33#include <OgreConfigFile.h>
34#include <OgreFrameListener.h>
35#include <OgreRoot.h>
36#include <OgreLogManager.h>
37#include <OgreException.h>
38#include <OgreRenderWindow.h>
39#include <OgreRenderSystem.h>
40#include <OgreTextureManager.h>
41#include <OgreViewport.h>
42#include <OgreWindowEventUtilities.h>
43
44#include "util/Debug.h"
45#include "util/Exception.h"
46#include "core/ConsoleCommand.h"
47#include "core/ConfigValueIncludes.h"
48#include "core/CoreIncludes.h"
49#include "core/Core.h"
50#include "core/input/InputManager.h"
51#include "core/input/KeyBinder.h"
52#include "core/input/ExtendedInputState.h"
53#include "core/Loader.h"
54#include "core/XMLFile.h"
55#include "overlays/console/InGameConsole.h"
56#include "gui/GUIManager.h"
57#include "tools/WindowEventListener.h"
58#include "Settings.h"
59
60// for compatibility
61#include "GraphicsEngine.h"
62
63namespace orxonox
64{
65    GSGraphics::GSGraphics()
66        : GameState<GSRoot>("graphics")
67        , renderWindow_(0)
68        , viewport_(0)
69        , bWindowEventListenerUpdateRequired_(false)
70        , inputManager_(0)
71        , console_(0)
72        , guiManager_(0)
73        , ogreRoot_(0)
74        , ogreLogger_(0)
75        , graphicsEngine_(0)
76        , masterKeyBinder_(0)
77        , frameCount_(0)
78        , statisticsRefreshCycle_(0)
79        , statisticsStartTime_(0)
80        , statisticsStartCount_(0)
81        , tickTime_(0)
82        , debugOverlay_(0)
83    {
84        RegisterRootObject(GSGraphics);
85        setConfigValues();
86    }
87
88    GSGraphics::~GSGraphics()
89    {
90    }
91
92    void GSGraphics::setConfigValues()
93    {
94        SetConfigValue(resourceFile_,    "resources.cfg")
95            .description("Location of the resources file in the data path.");
96        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
97            .description("Location of the Ogre config file");
98        SetConfigValue(ogrePluginsFile_, "plugins.cfg")
99            .description("Location of the Ogre plugins file");
100        SetConfigValue(ogreLogFile_,     "ogre.log")
101            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
102        SetConfigValue(ogreLogLevelTrivial_ , 5)
103            .description("Corresponding orxonox debug level for ogre Trivial");
104        SetConfigValue(ogreLogLevelNormal_  , 4)
105            .description("Corresponding orxonox debug level for ogre Normal");
106        SetConfigValue(ogreLogLevelCritical_, 2)
107            .description("Corresponding orxonox debug level for ogre Critical");
108        SetConfigValue(statisticsRefreshCycle_, 200000)
109            .description("Sets the time in microseconds interval at which average fps, etc. get updated.");
110        SetConfigValue(defaultMasterKeybindings_, "def_masterKeybindings.ini")
111            .description("Filename of default master keybindings.");
112    }
113
114    void GSGraphics::enter()
115    {
116        Core::setShowsGraphics(true);
117
118        // initialise graphics engine. Doesn't load the render window yet!
119        graphicsEngine_ = new GraphicsEngine();
120
121        // Ogre setup procedure
122        setupOgre();
123        this->declareResources();
124        this->loadRenderer();    // creates the render window
125        // TODO: Spread this so that this call only initialises things needed for the Console and GUI
126        this->initialiseResources();
127
128        // We want to get informed whenever an object of type WindowEventListener is created
129        // in order to later update the window size.
130        bWindowEventListenerUpdateRequired_ = false;
131        RegisterConstructionCallback(GSGraphics, orxonox::WindowEventListener, requestWindowEventListenerUpdate);
132
133        // load debug overlay
134        COUT(3) << "Loading Debug Overlay..." << std::endl;
135        this->debugOverlay_ = new XMLFile(Settings::getDataPath() + "overlay/debug.oxo");
136        Loader::open(debugOverlay_);
137
138        // Calls the InputManager which sets up the input devices.
139        // The render window width and height are used to set up the mouse movement.
140        inputManager_ = new InputManager();
141        size_t windowHnd = 0;
142        this->renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
143        inputManager_->initialise(windowHnd, renderWindow_->getWidth(), renderWindow_->getHeight(), true);
144        // Configure master input state with a KeyBinder
145        masterKeyBinder_ = new KeyBinder();
146        masterKeyBinder_->loadBindings("masterKeybindings.ini", defaultMasterKeybindings_);
147        inputManager_->getMasterInputState()->addKeyHandler(masterKeyBinder_);
148
149        // Load the InGameConsole
150        console_ = new InGameConsole();
151        console_->initialise(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
152
153        // load the CEGUI interface
154        guiManager_ = new GUIManager();
155        guiManager_->initialise(this->renderWindow_);
156
157        // reset frame counter
158        this->frameCount_ = 0;
159        this->tickTime_ = 0;
160        statisticsStartTime_ = 0;
161        statisticsStartCount_ = 0;
162
163        // add console commands
164        FunctorMember<GSGraphics>* functor1 = createFunctor(&GSGraphics::printScreen);
165        functor1->setObject(this);
166        ccPrintScreen_ = createConsoleCommand(functor1, "printScreen");
167        CommandExecutor::addConsoleCommandShortcut(ccPrintScreen_);
168    }
169
170    void GSGraphics::leave()
171    {
172        using namespace Ogre;
173
174        delete this->ccPrintScreen_;
175
176        // remove our WindowEventListener first to avoid bad calls after the window has been destroyed
177        Ogre::WindowEventUtilities::removeWindowEventListener(this->renderWindow_, this);
178
179        delete this->guiManager_;
180
181        delete this->console_;
182
183        //inputManager_->getMasterInputState()->removeKeyHandler(this->masterKeyBinder_);
184        delete this->masterKeyBinder_;
185        delete this->inputManager_;
186
187        Loader::unload(this->debugOverlay_);
188        delete this->debugOverlay_;
189
190        // destroy render window
191        RenderSystem* renderer = this->ogreRoot_->getRenderSystem();
192        renderer->destroyRenderWindow("Orxonox");
193
194        /*** CODE SNIPPET, UNUSED ***/
195        // Does the opposite of initialise()
196        //ogreRoot_->shutdown();
197        // Remove all resources and resource groups
198        //StringVector groups = ResourceGroupManager::getSingleton().getResourceGroups();
199        //for (StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
200        //{
201        //    ResourceGroupManager::getSingleton().destroyResourceGroup(*it);
202        //}
203
204        //ParticleSystemManager::getSingleton().removeAllTemplates();
205
206        // Shutdown the render system
207        //this->ogreRoot_->setRenderSystem(0);
208
209        delete this->ogreRoot_;
210
211#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
212        // delete the ogre log and the logManager (since we have created it).
213        this->ogreLogger_->getDefaultLog()->removeListener(this);
214        this->ogreLogger_->destroyLog(Ogre::LogManager::getSingleton().getDefaultLog());
215        delete this->ogreLogger_;
216#endif
217
218        delete graphicsEngine_;
219
220        Core::setShowsGraphics(false);
221    }
222
223    /**
224        Main loop of the orxonox game.
225        We use the Ogre::Timer to measure time since it uses the most precise
226        method an a platform (however the windows timer lacks time when under
227        heavy kernel load!).
228        There is a simple mechanism to measure the average time spent in our
229        ticks as it may indicate performance issues.
230        A note about the Ogre::FrameListener: Even though we don't use them,
231        they still get called. However, the delta times are not correct (except
232        for timeSinceLastFrame, which is the most important). A little research
233        as shown that there is probably only one FrameListener that doesn't even
234        need the time. So we shouldn't run into problems.
235    */
236    void GSGraphics::ticked(const Clock& time)
237    {
238        unsigned long long timeBeforeTick = time.getRealMicroseconds();
239        float dt = time.getDeltaTime();
240
241        this->inputManager_->tick(dt);
242        // tick console
243        this->console_->tick(dt);
244        this->tickChild(time);
245
246        if (this->bWindowEventListenerUpdateRequired_)
247        {
248            // Update all WindowEventListeners for the case a new one was created.
249            this->windowResized(this->renderWindow_);
250            this->bWindowEventListenerUpdateRequired_ = false;
251        }
252
253        unsigned long long timeAfterTick = time.getRealMicroseconds();
254
255        tickTime_ += (unsigned int)(timeAfterTick - timeBeforeTick);
256        if (timeAfterTick > statisticsStartTime_ + statisticsRefreshCycle_)
257        {
258            GraphicsEngine::getInstance().setAverageTickTime(
259                (float)tickTime_ * 0.001f / (frameCount_ - statisticsStartCount_));
260            float avgFPS = (float)(frameCount_ - statisticsStartCount_)
261                / (timeAfterTick - statisticsStartTime_) * 1000000.0;
262            GraphicsEngine::getInstance().setAverageFramesPerSecond(avgFPS);
263
264            tickTime_ = 0;
265            statisticsStartCount_ = frameCount_;
266            statisticsStartTime_  = timeAfterTick;
267        }
268
269        // don't forget to call _fireFrameStarted in ogre to make sure
270        // everything goes smoothly
271        Ogre::FrameEvent evt;
272        evt.timeSinceLastFrame = dt;
273        evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
274        ogreRoot_->_fireFrameStarted(evt);
275
276        // Pump messages in all registered RenderWindows
277        // This calls the WindowEventListener objects.
278        Ogre::WindowEventUtilities::messagePump();
279        // make sure the window stays active even when not focused
280        // (probably only necessary on windows)
281        this->renderWindow_->setActive(true);
282
283        // render
284        ogreRoot_->_updateAllRenderTargets();
285
286        // again, just to be sure ogre works fine
287        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
288
289        ++frameCount_;
290    }
291
292    /**
293    @brief
294        Creates the Ogre Root object and sets up the ogre log.
295    */
296    void GSGraphics::setupOgre()
297    {
298        COUT(3) << "Setting up Ogre..." << std::endl;
299
300        // TODO: LogManager doesn't work on oli platform. The why is yet unknown.
301#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
302        // create a new logManager
303        ogreLogger_ = new Ogre::LogManager();
304        COUT(4) << "Ogre LogManager created" << std::endl;
305
306        // create our own log that we can listen to
307        Ogre::Log *myLog;
308        if (this->ogreLogFile_ == "")
309            myLog = ogreLogger_->createLog("ogre.log", true, false, true);
310        else
311            myLog = ogreLogger_->createLog(this->ogreLogFile_, true, false, false);
312        COUT(4) << "Ogre Log created" << std::endl;
313
314        myLog->setLogDetail(Ogre::LL_BOREME);
315        myLog->addListener(this);
316#endif
317
318        // Root will detect that we've already created a Log
319        COUT(4) << "Creating Ogre Root..." << std::endl;
320
321        if (ogrePluginsFile_ == "")
322        {
323            COUT(2) << "Warning: Ogre plugins file set to \"\". Defaulting to plugins.cfg" << std::endl;
324            ModifyConfigValue(ogrePluginsFile_, tset, "plugins.cfg");
325        }
326        if (ogreConfigFile_ == "")
327        {
328            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
329            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
330        }
331        if (ogreLogFile_ == "")
332        {
333            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
334            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
335        }
336
337        // check for config file existence because Ogre displays (caught) exceptions if not
338        std::ifstream probe;
339        probe.open(ogreConfigFile_.c_str());
340        if (!probe)
341        {
342            // create a zero sized file
343            std::ofstream creator;
344            creator.open(ogreConfigFile_.c_str());
345            creator.close();
346        }
347        else
348            probe.close();
349
350        ogreRoot_ = new Ogre::Root(ogrePluginsFile_, ogreConfigFile_, ogreLogFile_);
351
352#if 0 // Ogre 1.4.3 doesn't yet support setDebugOutputEnabled(.)
353#if ORXONOX_PLATFORM != ORXONOX_PLATFORM_WIN32
354        // tame the ogre ouput so we don't get all the mess in the console
355        Ogre::Log* defaultLog = Ogre::LogManager::getSingleton().getDefaultLog();
356        defaultLog->setDebugOutputEnabled(false);
357        defaultLog->setLogDetail(Ogre::LL_BOREME);
358        defaultLog->addListener(this);
359#endif
360#endif
361
362        COUT(3) << "Ogre set up done." << std::endl;
363    }
364
365    void GSGraphics::declareResources()
366    {
367        CCOUT(4) << "Declaring Resources" << std::endl;
368        //TODO: Specify layout of data file and maybe use xml-loader
369        //TODO: Work with ressource groups (should be generated by a special loader)
370
371        if (resourceFile_ == "")
372        {
373            COUT(2) << "Warning: Ogre resource file set to \"\". Defaulting to resources.cfg" << std::endl;
374            ModifyConfigValue(resourceFile_, tset, "resources.cfg");
375        }
376
377        // Load resource paths from data file using configfile ressource type
378        Ogre::ConfigFile cf;
379        try
380        {
381            cf.load(Settings::getDataPath() + resourceFile_);
382        }
383        catch (...)
384        {
385            //COUT(1) << ex.getFullDescription() << std::endl;
386            COUT(0) << "Have you forgotten to set the data path in orxnox.ini?" << std::endl;
387            throw;
388        }
389
390        // Go through all sections & settings in the file
391        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
392
393        std::string secName, typeName, archName;
394        while (seci.hasMoreElements())
395        {
396            try
397            {
398                secName = seci.peekNextKey();
399                Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
400                Ogre::ConfigFile::SettingsMultiMap::iterator i;
401                for (i = settings->begin(); i != settings->end(); ++i)
402                {
403                    typeName = i->first; // for instance "FileSystem" or "Zip"
404                    archName = i->second; // name (and location) of archive
405
406                    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
407                        std::string(Settings::getDataPath() + archName), typeName, secName);
408                }
409            }
410            catch (Ogre::Exception& ex)
411            {
412                COUT(1) << ex.getFullDescription() << std::endl;
413            }
414        }
415    }
416
417    void GSGraphics::loadRenderer()
418    {
419        CCOUT(4) << "Configuring Renderer" << std::endl;
420
421        if (!ogreRoot_->restoreConfig())
422            if (!ogreRoot_->showConfigDialog())
423                ThrowException(InitialisationFailed, "Could not show Ogre configuration dialogue.");
424
425        CCOUT(4) << "Creating render window" << std::endl;
426
427        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
428
429        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, this);
430
431        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
432
433        // create a full screen default viewport
434        this->viewport_ = this->renderWindow_->addViewport(0, 0);
435    }
436
437    void GSGraphics::initialiseResources()
438    {
439        CCOUT(4) << "Initialising resources" << std::endl;
440        //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
441        //try
442        //{
443            Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
444            /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
445            for (unsigned int i = 0; i < str.size(); i++)
446            {
447            Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
448            }*/
449        //}
450        //catch (...)
451        //{
452        //    CCOUT(2) << "Error: There was a serious error when initialising the resources." << std::endl;
453        //    throw;
454        //}
455    }
456
457    /**
458    @brief
459        Method called by the LogListener interface from Ogre.
460        We use it to capture Ogre log messages and handle it ourselves.
461    @param message
462        The message to be logged
463    @param lml
464        The message level the log is using
465    @param maskDebug
466        If we are printing to the console or not
467    @param logName
468        The name of this log (so you can have several listeners
469        for different logs, and identify them)
470    */
471    void GSGraphics::messageLogged(const std::string& message,
472        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
473    {
474        int orxonoxLevel;
475        switch (lml)
476        {
477        case Ogre::LML_TRIVIAL:
478            orxonoxLevel = this->ogreLogLevelTrivial_;
479            break;
480        case Ogre::LML_NORMAL:
481            orxonoxLevel = this->ogreLogLevelNormal_;
482            break;
483        case Ogre::LML_CRITICAL:
484            orxonoxLevel = this->ogreLogLevelCritical_;
485            break;
486        default:
487            orxonoxLevel = 0;
488        }
489        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
490            << "Ogre: " << message << std::endl;
491    }
492
493    /**
494    @brief
495        Window has moved.
496    @param rw
497        The render window it occured in
498    */
499    void GSGraphics::windowMoved(Ogre::RenderWindow *rw)
500    {
501        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
502            it->windowMoved();
503    }
504
505    /**
506    @brief
507        Window has resized.
508    @param rw
509        The render window it occured in
510    @note
511        GraphicsEngine has a render window stored itself. This is the same
512        as rw. But we have to be careful when using multiple render windows!
513    */
514    void GSGraphics::windowResized(Ogre::RenderWindow *rw)
515    {
516        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
517            it->windowResized(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
518
519        // OIS needs this under linux even if we only use relative input measurement.
520        if (this->inputManager_)
521            this->inputManager_->setWindowExtents(renderWindow_->getWidth(), renderWindow_->getHeight());
522    }
523
524    /**
525    @brief
526        Window focus has changed.
527    @param rw
528        The render window it occured in
529    */
530    void GSGraphics::windowFocusChange(Ogre::RenderWindow *rw)
531    {
532        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
533            it->windowFocusChanged();
534
535        // instruct InputManager to clear the buffers (core library so we cannot use the interface)
536        if (this->inputManager_)
537            this->inputManager_->clearBuffers();
538    }
539
540    /**
541    @brief
542        Window was closed.
543    @param rw
544        The render window it occured in
545    */
546    void GSGraphics::windowClosed(Ogre::RenderWindow *rw)
547    {
548        this->requestState("root");
549    }
550
551    void GSGraphics::printScreen()
552    {
553        if (this->renderWindow_)
554        {
555            this->renderWindow_->writeContentsToTimestampedFile("shot_", ".jpg");
556        }
557    }
558}
Note: See TracBrowser for help on using the repository browser.