Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/objecthierarchy/src/orxonox/gamestates/GSGraphics.cc @ 2078

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