Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/orxonox/GraphicsEngine.cc @ 1640

Last change on this file since 1640 was 1640, checked in by rgrieder, 16 years ago

When starting with no arguments, the GUI is loaded, which consists merely of 2 buttons: start and exit (quite self explanatory). But it does work on my box (still no cmake support).

  • Property svn:eol-style set to native
File size: 16.2 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 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007, Felix Schulthess
26 *
27 */
28
29/**
30@file
31@brief
32    Implementation of an partial interface to Ogre.
33*/
34
35#include "OrxonoxStableHeaders.h"
36#include "GraphicsEngine.h"
37
38#include <fstream>
39
40#include <OgreConfigFile.h>
41#include <OgreException.h>
42#include <OgreLogManager.h>
43#include <OgreRoot.h>
44#include <OgreSceneManager.h>
45#include <OgreTextureManager.h>
46#include <OgreViewport.h>
47
48#include "core/CoreIncludes.h"
49#include "core/ConfigValueIncludes.h"
50#include "core/Debug.h"
51#include "core/CommandExecutor.h"
52#include "core/ConsoleCommand.h"
53#include "core/Exception.h"
54
55#include "overlays/console/InGameConsole.h"
56#include "overlays/OverlayGroup.h"
57#include "tools/ParticleInterface.h"
58#include "Settings.h"
59#include "tools/WindowEventListener.h"
60
61#include "objects/CameraHandler.h"
62
63namespace orxonox
64{
65    GraphicsEngine* GraphicsEngine::singletonRef_s = 0;
66
67    /**
68    @brief
69        Returns the singleton instance.
70    @return
71        The only instance of GraphicsEngine.
72    */
73    /*static*/ GraphicsEngine& GraphicsEngine::getSingleton()
74    {
75        assert(singletonRef_s);
76        return *singletonRef_s;
77    }
78
79    /**
80    @brief
81        Non-initialising constructor.
82    */
83    GraphicsEngine::GraphicsEngine()
84        : root_(0)
85        , renderWindow_(0)
86        , levelSceneManager_(0)
87        , viewport_(0)
88    {
89        RegisterObject(GraphicsEngine);
90
91        singletonRef_s = this;
92
93        this->detailLevelParticle_ = 0;
94
95        this->setConfigValues();
96        CCOUT(4) << "Constructed" << std::endl;
97    }
98
99    void GraphicsEngine::setConfigValues()
100    {
101        SetConfigValue(resourceFile_,    "resources.cfg").description("Location of the resources file in the data path.");
102        SetConfigValue(ogreConfigFile_,  "ogre.cfg").description("Location of the Ogre config file");
103        SetConfigValue(ogrePluginsFile_, "plugins.cfg").description("Location of the Ogre plugins file");
104        SetConfigValue(ogreLogFile_,     "ogre.log").description("Logfile for messages from Ogre. \
105                                                                 Use \"\" to suppress log file creation.");
106        SetConfigValue(ogreLogLevelTrivial_ , 5).description("Corresponding orxonox debug level for ogre Trivial");
107        SetConfigValue(ogreLogLevelNormal_  , 4).description("Corresponding orxonox debug level for ogre Normal");
108        SetConfigValue(ogreLogLevelCritical_, 2).description("Corresponding orxonox debug level for ogre Critical");
109
110        unsigned int old = this->detailLevelParticle_;
111        SetConfigValue(detailLevelParticle_, 2).description("O: off, 1: low, 2: normal, 3: high");
112
113        if (this->detailLevelParticle_ != old)
114            for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
115                it->detailLevelChanged(this->detailLevelParticle_);
116    }
117
118    /**
119    @brief
120        Destroys all the Ogre related objects
121    */
122    GraphicsEngine::~GraphicsEngine()
123    {
124        CCOUT(4) << "Destroying objects..." << std::endl;
125        Ogre::WindowEventUtilities::removeWindowEventListener(this->renderWindow_, this);
126        if (this->root_)
127            delete this->root_;
128        this->root_ = 0;
129        this->levelSceneManager_ = 0;
130        this->renderWindow_ = 0;
131
132#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
133        // delete the ogre log and the logManager (since we have created it).
134        if (Ogre::LogManager::getSingletonPtr() != 0)
135        {
136            Ogre::LogManager::getSingleton().getDefaultLog()->removeListener(this);
137            Ogre::LogManager::getSingleton().destroyLog(Ogre::LogManager::getSingleton().getDefaultLog());
138            delete Ogre::LogManager::getSingletonPtr();
139        }
140        CCOUT(4) << "Destroying objects done" << std::endl;
141#endif
142
143        singletonRef_s = 0;
144    }
145
146    /**
147    @brief
148        Creates the Ogre Root object and sets up the ogre log.
149    */
150    void GraphicsEngine::setup()
151    {
152        CCOUT(3) << "Setting up..." << std::endl;
153
154        // TODO: LogManager doesn't work on linux platform. The why is yet unknown.
155#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
156        // create a new logManager
157        Ogre::LogManager* logger = new Ogre::LogManager();
158        CCOUT(4) << "Ogre LogManager created" << std::endl;
159
160        // create our own log that we can listen to
161        Ogre::Log *myLog;
162        if (this->ogreLogFile_ == "")
163            myLog = logger->createLog("ogre.log", true, false, true);
164        else
165            myLog = logger->createLog(this->ogreLogFile_, true, false, false);
166        CCOUT(4) << "Ogre Log created" << std::endl;
167
168        myLog->setLogDetail(Ogre::LL_BOREME);
169        myLog->addListener(this);
170#endif
171
172        // Root will detect that we've already created a Log
173        CCOUT(4) << "Creating Ogre Root..." << std::endl;
174
175        if (ogrePluginsFile_ == "")
176        {
177            COUT(2) << "Warning: Ogre plugins file set to \"\". Defaulting to plugins.cfg" << std::endl;
178            ModifyConfigValue(ogrePluginsFile_, tset, "plugins.cfg");
179        }
180        if (ogreConfigFile_ == "")
181        {
182            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
183            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
184        }
185        if (ogreLogFile_ == "")
186        {
187            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
188            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
189        }
190
191        root_ = new Ogre::Root(ogrePluginsFile_, ogreConfigFile_, ogreLogFile_);
192
193        if (!root_->getInstalledPlugins().size())
194        {
195            ThrowException(PluginsNotFound, "No Ogre plugins declared. Cannot load Ogre.");
196        }
197
198#if 0 // Ogre 1.4.3 doesn't support setDebugOutputEnabled(.)
199//#if ORXONOX_PLATFORM != ORXONOX_PLATFORM_WIN32
200        // tame the ogre ouput so we don't get all the mess in the console
201        Ogre::Log* defaultLog = Ogre::LogManager::getSingleton().getDefaultLog();
202        defaultLog->setDebugOutputEnabled(false);
203        defaultLog->setLogDetail(Ogre::LL_BOREME);
204        defaultLog->addListener(this);
205#endif
206
207        CCOUT(4) << "Creating Ogre Root done" << std::endl;
208
209        // specify where Ogre has to look for resources. This call doesn't parse anything yet!
210        declareRessourceLocations();
211
212        CCOUT(3) << "Set up done." << std::endl;
213    }
214
215    void GraphicsEngine::declareRessourceLocations()
216    {
217        CCOUT(4) << "Declaring Resources" << std::endl;
218        //TODO: Specify layout of data file and maybe use xml-loader
219        //TODO: Work with ressource groups (should be generated by a special loader)
220
221        if (resourceFile_ == "")
222        {
223            COUT(2) << "Warning: Ogre resource file set to \"\". Defaulting to resources.cfg" << std::endl;
224            ModifyConfigValue(resourceFile_, tset, "resources.cfg");
225        }
226
227        // Load resource paths from data file using configfile ressource type
228        Ogre::ConfigFile cf;
229        try
230        {
231            cf.load(Settings::getDataPath() + resourceFile_);
232        }
233        catch (Ogre::Exception& ex)
234        {
235            COUT(1) << ex.getFullDescription() << std::endl;
236            COUT(0) << "Have you forgotten to set the data path in orxnox.ini?" << std::endl;
237            throw;
238        }
239
240        // Go through all sections & settings in the file
241        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
242
243        std::string secName, typeName, archName;
244        while (seci.hasMoreElements())
245        {
246            try
247            {
248                secName = seci.peekNextKey();
249                Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
250                Ogre::ConfigFile::SettingsMultiMap::iterator i;
251                for (i = settings->begin(); i != settings->end(); ++i)
252                {
253                    typeName = i->first; // for instance "FileSystem" or "Zip"
254                    archName = i->second; // name (and location) of archive
255
256                    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
257                        std::string(Settings::getDataPath() + archName), typeName, secName);
258                }
259            }
260            catch (Ogre::Exception& ex)
261            {
262                COUT(1) << ex.getFullDescription() << std::endl;
263            }
264        }
265    }
266
267    bool GraphicsEngine::loadRenderer()
268    {
269        CCOUT(4) << "Configuring Renderer" << std::endl;
270
271        // check for file existence because Ogre displays exceptions if not
272        std::ifstream probe;
273        probe.open(ogreConfigFile_.c_str());
274        if (!probe)
275        {
276            // create a zero sized file
277            std::ofstream creator;
278            creator.open(ogreConfigFile_.c_str());
279            creator.close();
280        }
281        else
282            probe.close();
283
284        if (!root_->restoreConfig())
285            if (!root_->showConfigDialog())
286                return false;
287
288        CCOUT(4) << "Creating render window" << std::endl;
289        try
290        {
291            this->renderWindow_ = root_->initialise(true, "OrxonoxV2");
292        }
293        catch (...)
294        {
295            COUT(2) << "Error: There was an exception when initialising Ogre Root." << std::endl;
296            return false;
297        }
298
299        if (!root_->isInitialised())
300        {
301            CCOUT(2) << "Error: Initialising Ogre root object failed." << std::endl;
302            return false;
303        }
304        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, this);
305        //Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
306
307        // create a full screen viewport in which to render the scene and the loading screen
308        // The GUI uses its own one to be able to render on top of everything.
309        // That explains why the Z order here is only 1 and not 0 (top most)
310        this->viewport_ = this->renderWindow_->addViewport(0, 1);
311
312        return true;
313    }
314
315    bool GraphicsEngine::initialiseResources()
316    {
317        CCOUT(4) << "Initialising resources" << std::endl;
318        //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
319        try
320        {
321            Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
322            /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
323            for (unsigned int i = 0; i < str.size(); i++)
324            {
325            Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
326            }*/
327        }
328        catch (Ogre::Exception& e)
329        {
330            CCOUT(2) << "Error: There was an Error when initialising the resources." << std::endl;
331            CCOUT(2) << "ErrorMessage: " << e.getFullDescription() << std::endl;
332            return false;
333        }
334        return true;
335    }
336
337    /**
338    @brief
339        Creates the SceneManager
340    */
341    bool GraphicsEngine::createNewScene()
342    {
343        CCOUT(4) << "Creating new SceneManager..." << std::endl;
344        if (levelSceneManager_)
345        {
346            CCOUT(2) << "SceneManager already exists! Skipping." << std::endl;
347            return false;
348        }
349        this->levelSceneManager_ = this->root_->createSceneManager(Ogre::ST_GENERIC, "LevelSceneManager");
350        CCOUT(3) << "Created SceneManager: " << levelSceneManager_ << std::endl;
351        return true;
352    }
353
354    /**
355    @brief
356        Returns the window handle of the render window.
357        At least the InputHandler uses this to create the OIS::InputManager
358    @return
359        The window handle of the render window
360    */
361    size_t GraphicsEngine::getWindowHandle()
362    {
363        if (this->renderWindow_)
364        {
365            size_t windowHnd = 0;
366            this->renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
367            return windowHnd;
368        }
369        else
370            return 0;
371    }
372
373    /**
374    @brief
375        Get the width of the render window
376    @return
377        The width of the render window
378    */
379    int GraphicsEngine::getWindowWidth() const
380    {
381        if (this->renderWindow_)
382            return this->renderWindow_->getWidth();
383        else
384            return 0;
385    }
386
387    /**
388    @brief
389        Get the height of the render window
390    @return
391        The height of the render window
392    */
393    int GraphicsEngine::getWindowHeight() const
394    {
395        if (this->renderWindow_)
396            return this->renderWindow_->getHeight();
397        else
398            return 0;
399    }
400
401    /**
402    @brief
403        Returns the window aspect ratio height/width.
404    @return
405        The window aspect ratio
406    */
407    float GraphicsEngine::getWindowAspectRatio() const
408    {
409        if (this->renderWindow_)
410            return (float)this->renderWindow_->getHeight() / (float)this->renderWindow_->getWidth();
411        else
412            return 1.0f;
413    }
414
415    /**
416    @brief
417        Method called by the LogListener interface from Ogre.
418        We use it to capture Ogre log messages and handle it ourselves.
419    @param message
420        The message to be logged
421    @param lml
422        The message level the log is using
423    @param maskDebug
424        If we are printing to the console or not
425    @param logName
426        The name of this log (so you can have several listeners
427        for different logs, and identify them)
428    */
429    void GraphicsEngine::messageLogged(const std::string& message,
430        Ogre::LogMessageLevel lml, bool maskDebug, const std::string &logName)
431    {
432        int orxonoxLevel;
433        switch (lml)
434        {
435        case Ogre::LML_TRIVIAL:
436            orxonoxLevel = this->ogreLogLevelTrivial_;
437            break;
438        case Ogre::LML_NORMAL:
439            orxonoxLevel = this->ogreLogLevelNormal_;
440            break;
441        case Ogre::LML_CRITICAL:
442            orxonoxLevel = this->ogreLogLevelCritical_;
443            break;
444        default:
445            orxonoxLevel = 0;
446        }
447        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
448            << "Ogre: " << message << std::endl;
449    }
450
451    /**
452    @brief
453        Window has moved.
454    @param rw
455        The render window it occured in
456    */
457    void GraphicsEngine::windowMoved(Ogre::RenderWindow *rw)
458    {
459        for (Iterator<orxonox::WindowEventListener> it = ObjectList<orxonox::WindowEventListener>::start(); it; ++it)
460            it->windowMoved();
461    }
462
463    /**
464    @brief
465        Window has resized.
466    @param rw
467        The render window it occured in
468    @note
469        GraphicsEngine has a render window stored itself. This is the same
470        as rw. But we have to be careful when using multiple render windows!
471    */
472    void GraphicsEngine::windowResized(Ogre::RenderWindow *rw)
473    {
474        for (Iterator<orxonox::WindowEventListener> it = ObjectList<orxonox::WindowEventListener>::start(); it; ++it)
475            it->windowResized(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
476    }
477
478    /**
479    @brief
480        Window focus has changed.
481    @param rw
482        The render window it occured in
483    */
484    void GraphicsEngine::windowFocusChanged(Ogre::RenderWindow *rw)
485    {
486        for (Iterator<orxonox::WindowEventListener> it = ObjectList<orxonox::WindowEventListener>::start(); it; ++it)
487            it->windowFocusChanged();
488    }
489
490    /**
491    @brief
492        Window was closed.
493    @param rw
494        The render window it occured in
495    */
496    void GraphicsEngine::windowClosed(Ogre::RenderWindow *rw)
497    {
498        // using CommandExecutor in order to avoid depending on Orxonox.h.
499        CommandExecutor::execute("exit", false);
500    }
501
502}
Note: See TracBrowser for help on using the repository browser.