Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/gamestates/GSGraphics.cc @ 2087

Last change on this file since 2087 was 2087, checked in by landauf, 16 years ago

merged objecthierarchy branch back to trunk

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