Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/physics/src/orxonox/gamestates/GSGraphics.cc @ 2299

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

Applied ugliest hack ever: When ticking the objects (code position of this is already a big hack) and dt is larger than 1.0f, we assume that the level was just loaded before and set dt = 0.
The problem is that usually, after loading, dt is for instance about 3s for my debug version. That translates all entities by a huge amount.

To actually solve this problem correctly, we will have some very intensive discussions about certain subjects..

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