Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/buildsystem2/src/orxonox/gamestates/GSGraphics.cc @ 2534

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