Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/libraries/core/GraphicsManager.cc @ 8820

Last change on this file since 8820 was 8820, checked in by landauf, 13 years ago

fixed some stuff that became visible after the recent changes in the output system:

  • fixed ogre exception because of included files in xml template files (it was searching for templates/includes/*.oxi instead of includes/*.oxi)
  • initializing worldentities with mass 1 instead of 0 to avoid a warning
  • Loader::open now also supports a bVerbose flag to hide output for internal xml files (like resource files)
  • 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 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
25 *   Co-authors:
26 *      Felix Schulthess
27 *
28 */
29
30#include "GraphicsManager.h"
31
32#include <cstdlib>
33#include <fstream>
34#include <sstream>
35#include <boost/filesystem.hpp>
36#include <boost/shared_array.hpp>
37
38#include <OgreFrameListener.h>
39#include <OgreRoot.h>
40#include <OgreLogManager.h>
41#include <OgreRenderWindow.h>
42#include <OgreRenderSystem.h>
43#include <OgreResourceGroupManager.h>
44#include <OgreTextureManager.h>
45#include <OgreViewport.h>
46#include <OgreWindowEventUtilities.h>
47
48#include "SpecialConfig.h"
49#include "util/Clock.h"
50#include "util/Convert.h"
51#include "util/Exception.h"
52#include "util/StringUtils.h"
53#include "util/SubString.h"
54#include "ConfigValueIncludes.h"
55#include "CoreIncludes.h"
56#include "Core.h"
57#include "Game.h"
58#include "GameMode.h"
59#include "GUIManager.h"
60#include "Loader.h"
61#include "PathConfig.h"
62#include "ViewportEventListener.h"
63#include "WindowEventListener.h"
64#include "XMLFile.h"
65#include "command/ConsoleCommand.h"
66#include "input/InputManager.h"
67
68namespace orxonox
69{
70    static const std::string __CC_GraphicsManager_group = "GraphicsManager";
71    static const std::string __CC_setScreenResolution_name = "setScreenResolution";
72    static const std::string __CC_setFSAA_name = "setFSAA";
73    static const std::string __CC_setVSync_name = "setVSync";
74    DeclareConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name, &prototype::string__uint_uint_bool);
75    DeclareConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name, &prototype::string__string);
76    DeclareConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name, &prototype::string__bool);
77
78    static const std::string __CC_printScreen_name = "printScreen";
79    DeclareConsoleCommand(__CC_printScreen_name, &prototype::void__void);
80
81    class OgreWindowEventListener : public Ogre::WindowEventListener
82    {
83    public:
84        void windowResized     (Ogre::RenderWindow* rw)
85            { orxonox::WindowEventListener::resizeWindow(rw->getWidth(), rw->getHeight()); }
86        void windowFocusChange (Ogre::RenderWindow* rw)
87            { orxonox::WindowEventListener::changeWindowFocus(rw->isActive()); }
88        void windowClosed      (Ogre::RenderWindow* rw)
89            { orxonox::Game::getInstance().stop(); }
90        void windowMoved       (Ogre::RenderWindow* rw)
91            { orxonox::WindowEventListener::moveWindow(); }
92    };
93
94    GraphicsManager* GraphicsManager::singletonPtr_s = 0;
95
96    GraphicsManager::GraphicsManager(bool bLoadRenderer)
97        : ogreWindowEventListener_(new OgreWindowEventListener())
98        , renderWindow_(0)
99        , viewport_(0)
100        , lastFrameStartTime_(0.0f)
101        , lastFrameEndTime_(0.0f)
102        , destructionHelper_(this)
103    {
104        RegisterObject(GraphicsManager);
105
106        this->setConfigValues();
107
108        // Ogre setup procedure (creating Ogre::Root)
109        this->loadOgreRoot();
110
111        // At first, add the root paths of the data directories as resource locations
112        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getDataPathString(), "FileSystem");
113        // Load resources
114        resources_.reset(new XMLFile("DefaultResources.oxr"));
115        resources_->setLuaSupport(false);
116        Loader::open(resources_.get(), ClassTreeMask(), false);
117
118        // Only for runs in the build directory (not installed)
119        if (PathConfig::buildDirectoryRun())
120            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getExternalDataPathString(), "FileSystem");
121
122        extResources_.reset(new XMLFile("resources.oxr"));
123        extResources_->setLuaSupport(false);
124        Loader::open(extResources_.get(), ClassTreeMask(), false);
125
126        if (bLoadRenderer)
127        {
128            // Reads the ogre config and creates the render window
129            this->upgradeToGraphics();
130        }
131    }
132
133    void GraphicsManager::destroy()
134    {
135        Loader::unload(debugOverlay_.get());
136
137        Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow_, ogreWindowEventListener_);
138        ModifyConsoleCommand(__CC_printScreen_name).resetFunction();
139        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name).resetFunction();
140        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name).resetFunction();
141        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name).resetFunction();
142
143        // Undeclare the resources
144        Loader::unload(resources_.get());
145        Loader::unload(extResources_.get());
146
147        safeObjectDelete(&ogreRoot_);
148        safeObjectDelete(&ogreLogger_);
149        safeObjectDelete(&ogreWindowEventListener_);
150    }
151
152    void GraphicsManager::setConfigValues()
153    {
154        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
155            .description("Location of the Ogre config file");
156        SetConfigValue(ogrePlugins_, specialConfig::ogrePlugins)
157            .description("Comma separated list of all plugins to load.");
158        SetConfigValue(ogreLogFile_,     "ogre.log")
159            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
160    }
161
162    /**
163    @brief
164        Loads the renderer and creates the render window if not yet done so.
165    @remarks
166        This operation is irreversible without recreating the GraphicsManager!
167        So if it throws you HAVE to recreate the GraphicsManager!!!
168        It therefore offers almost no exception safety.
169    */
170    void GraphicsManager::upgradeToGraphics()
171    {
172        if (renderWindow_ != NULL)
173            return;
174
175        // load all the required plugins for Ogre
176        this->loadOgrePlugins();
177
178        this->loadRenderer();
179
180        // Initialise all resources (do this AFTER the renderer has been loaded!)
181        // Note: You can only do this once! Ogre will check whether a resource group has
182        // already been initialised. If you need to load resources later, you will have to
183        // choose another resource group.
184        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
185    }
186
187    /**
188    @brief
189        Creates the Ogre Root object and sets up the ogre log.
190    */
191    void GraphicsManager::loadOgreRoot()
192    {
193        orxout(internal_info) << "Setting up Ogre..." << endl;
194
195        if (ogreConfigFile_.empty())
196        {
197            orxout(internal_warning) << "Ogre config file set to \"\". Defaulting to config.cfg" << endl;
198            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
199        }
200        if (ogreLogFile_.empty())
201        {
202            orxout(internal_warning) << "Ogre log file set to \"\". Defaulting to ogre.log" << endl;
203            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
204        }
205
206        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
207        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
208
209        // create a new logManager
210        // Ogre::Root will detect that we've already created a Log
211        ogreLogger_ = new Ogre::LogManager();
212        orxout(internal_info) << "Ogre LogManager created" << endl;
213
214        // create our own log that we can listen to
215        Ogre::Log *myLog;
216        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
217        orxout(internal_info) << "Ogre Log created" << endl;
218
219        myLog->setLogDetail(Ogre::LL_BOREME);
220        myLog->addListener(this);
221
222        orxout(internal_info) << "Creating Ogre Root..." << endl;
223
224        // check for config file existence because Ogre displays (caught) exceptions if not
225        if (!boost::filesystem::exists(ogreConfigFilepath))
226        {
227            // create a zero sized file
228            std::ofstream creator;
229            creator.open(ogreConfigFilepath.string().c_str());
230            creator.close();
231        }
232
233        // Leave plugins file empty. We're going to do that part manually later
234        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string());
235
236        orxout(internal_info) << "Ogre set up done." << endl;
237    }
238
239    void GraphicsManager::loadOgrePlugins()
240    {
241        // Plugin path can have many different locations...
242        std::string pluginPath = specialConfig::ogrePluginsDirectory;
243#ifdef DEPENDENCY_PACKAGE_ENABLE
244        if (!PathConfig::buildDirectoryRun())
245        {
246#  if defined(ORXONOX_PLATFORM_WINDOWS)
247            pluginPath = PathConfig::getExecutablePathString();
248#  elif defined(ORXONOX_PLATFORM_APPLE)
249            // TODO: Where are the plugins being installed to?
250            pluginPath = PathConfig::getExecutablePathString();
251#  endif
252        }
253#endif
254
255#ifdef ORXONOX_PLATFORM_WINDOWS
256        // Add OGRE plugin path to the environment. That way one plugin could
257        // also depend on another without problems on Windows
258        const char* currentPATH = getenv("PATH");
259        std::string newPATH = pluginPath;
260        if (currentPATH != NULL)
261            newPATH = std::string(currentPATH) + ';' + newPATH;
262        putenv(const_cast<char*>(("PATH=" + newPATH).c_str()));
263#endif
264
265        // Do some SubString magic to get the comma separated list of plugins
266        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '{', '}', false, '\0');
267        for (unsigned int i = 0; i < plugins.size(); ++i)
268            ogreRoot_->loadPlugin(pluginPath + '/' + plugins[i]);
269    }
270
271    void GraphicsManager::loadRenderer()
272    {
273        orxout(internal_info) << "GraphicsManager: Configuring Renderer" << endl;
274
275        bool updatedConfig = Core::getInstance().getOgreConfigTimestamp() > Core::getInstance().getLastLevelTimestamp();
276        if (updatedConfig)
277            orxout(user_info)<< "Ogre config file has changed, but no level was started since then. Displaying config dialogue again to verify the changes." << endl;
278
279        if (!ogreRoot_->restoreConfig() || updatedConfig)
280        {
281            if (!ogreRoot_->showConfigDialog())
282                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue canceled.");
283            else
284                Core::getInstance().updateOgreConfigTimestamp();
285        }
286
287        orxout(internal_info) << "Creating render window" << endl;
288
289        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
290        // Propagate the size of the new winodw
291        this->ogreWindowEventListener_->windowResized(renderWindow_);
292
293        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_);
294
295        // create a full screen default viewport
296        // Note: This may throw when adding a viewport with an existing z-order!
297        //       But in our case we only have one viewport for now anyway, therefore
298        //       no ScopeGuards or anything to handle exceptions.
299        this->viewport_ = this->renderWindow_->addViewport(0, 0);
300
301        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(Ogre::MIP_UNLIMITED);
302
303        // add console commands
304        ModifyConsoleCommand(__CC_printScreen_name).setFunction(&GraphicsManager::printScreen, this);
305        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name).setFunction(&GraphicsManager::setScreenResolution, this);
306        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name).setFunction(&GraphicsManager::setFSAA, this);
307        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name).setFunction(&GraphicsManager::setVSync, this);
308    }
309
310    void GraphicsManager::loadDebugOverlay()
311    {
312        // Load debug overlay to show info about fps and tick time
313        orxout(internal_info) << "Loading Debug Overlay..." << endl;
314        debugOverlay_.reset(new XMLFile("debug.oxo"));
315        Loader::open(debugOverlay_.get(), ClassTreeMask(), false);
316    }
317
318    /**
319    @note
320        A note about the Ogre::FrameListener: Even though we don't use them,
321        they still get called.
322    */
323    void GraphicsManager::postUpdate(const Clock& time)
324    {
325        // Time before rendering
326        uint64_t timeBeforeTick = time.getRealMicroseconds();
327
328        // Ogre's time keeping object
329        Ogre::FrameEvent evt;
330
331        // Translate to Ogre float times before the update
332        float temp = lastFrameStartTime_;
333        lastFrameStartTime_ = (float)timeBeforeTick * 0.000001f;
334        evt.timeSinceLastFrame = lastFrameStartTime_ - temp;
335        evt.timeSinceLastEvent = lastFrameStartTime_ - lastFrameEndTime_;
336
337        // Ogre requires the time too
338        ogreRoot_->_fireFrameStarted(evt);
339
340        // Pump messages in all registered RenderWindows
341        // This calls the WindowEventListener objects.
342        Ogre::WindowEventUtilities::messagePump();
343        // Make sure the window stays active even when not focused
344        // (probably only necessary on windows)
345        this->renderWindow_->setActive(true);
346
347        // Render frame
348        ogreRoot_->_updateAllRenderTargets();
349
350        uint64_t timeAfterTick = time.getRealMicroseconds();
351        // Subtract the time used for rendering from the tick time counter
352        Game::getInstance().subtractTickTime((int32_t)(timeAfterTick - timeBeforeTick));
353
354        // Translate to Ogre float times after the update
355        temp = lastFrameEndTime_;
356        lastFrameEndTime_ = (float)timeBeforeTick * 0.000001f;
357        evt.timeSinceLastFrame = lastFrameEndTime_ - temp;
358        evt.timeSinceLastEvent = lastFrameEndTime_ - lastFrameStartTime_;
359
360        // Ogre also needs the time after the frame finished
361        ogreRoot_->_fireFrameEnded(evt);
362    }
363
364    void GraphicsManager::setCamera(Ogre::Camera* camera)
365    {
366        Ogre::Camera* oldCamera = this->viewport_->getCamera();
367
368        this->viewport_->setCamera(camera);
369        GUIManager::getInstance().setCamera(camera);
370
371        for (ObjectList<ViewportEventListener>::iterator it = ObjectList<ViewportEventListener>::begin(); it != ObjectList<ViewportEventListener>::end(); ++it)
372            it->cameraChanged(this->viewport_, oldCamera);
373    }
374
375    /**
376    @brief
377        Method called by the LogListener interface from Ogre.
378        We use it to capture Ogre log messages and handle it ourselves.
379    @param message
380        The message to be logged
381    @param lml
382        The message level the log is using
383    @param maskDebug
384        If we are printing to the console or not
385    @param logName
386        The name of this log (so you can have several listeners
387        for different logs, and identify them)
388    */
389    void GraphicsManager::messageLogged(const std::string& message,
390        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
391    {
392        OutputLevel orxonoxLevel;
393        std::string introduction;
394        // Do not show caught OGRE exceptions in front
395        if (message.find("EXCEPTION") != std::string::npos)
396        {
397            orxonoxLevel = level::internal_error;
398            introduction = "Ogre, caught exception: ";
399        }
400        else
401        {
402            switch (lml)
403            {
404            case Ogre::LML_TRIVIAL:
405                orxonoxLevel = level::verbose_more;
406                break;
407            case Ogre::LML_NORMAL:
408                orxonoxLevel = level::verbose;
409                break;
410            case Ogre::LML_CRITICAL:
411                orxonoxLevel = level::internal_warning;
412                break;
413            default:
414                orxonoxLevel = level::debug_output;
415            }
416            introduction = "Ogre: ";
417        }
418
419        orxout(orxonoxLevel, context::ogre) << introduction << message << endl;
420    }
421
422    size_t GraphicsManager::getRenderWindowHandle()
423    {
424        size_t windowHnd = 0;
425        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
426        return windowHnd;
427    }
428
429    bool GraphicsManager::isFullScreen() const
430    {
431        return this->renderWindow_->isFullScreen();
432    }
433
434    unsigned int GraphicsManager::getWindowWidth() const
435    {
436        return this->renderWindow_->getWidth();
437    }
438
439    unsigned int GraphicsManager::getWindowHeight() const
440    {
441        return this->renderWindow_->getHeight();
442    }
443
444    bool GraphicsManager::hasVSyncEnabled() const
445    {
446        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
447        Ogre::ConfigOptionMap::iterator it = options.find("VSync");
448        if (it != options.end())
449            return (it->second.currentValue == "Yes");
450        else
451            return false;
452    }
453
454    std::string GraphicsManager::getFSAAMode() const
455    {
456        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
457        Ogre::ConfigOptionMap::iterator it = options.find("FSAA");
458        if (it != options.end())
459            return it->second.currentValue;
460        else
461            return "";
462    }
463
464    std::string GraphicsManager::setScreenResolution(unsigned int width, unsigned int height, bool fullscreen)
465    {
466        // workaround to detect if the colour depth should be written to the config file
467        bool bWriteColourDepth = false;
468        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
469        Ogre::ConfigOptionMap::iterator it = options.find("Video Mode");
470        if (it != options.end())
471            bWriteColourDepth = (it->second.currentValue.find('@') != std::string::npos);
472
473        if (bWriteColourDepth)
474        {
475            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
476                                                                    + " x " + multi_cast<std::string>(height)
477                                                                    + " @ " + multi_cast<std::string>(this->getRenderWindow()->getColourDepth()) + "-bit colour");
478        }
479        else
480        {
481            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
482                                                                    + " x " + multi_cast<std::string>(height));
483        }
484
485        this->ogreRoot_->getRenderSystem()->setConfigOption("Full Screen", fullscreen ? "Yes" : "No");
486
487        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
488
489        if (validate == "")
490        {
491            GraphicsManager::getInstance().getRenderWindow()->setFullscreen(fullscreen, width, height);
492            this->ogreRoot_->saveConfig();
493            Core::getInstance().updateOgreConfigTimestamp();
494            // Also reload the input devices
495            InputManager::getInstance().reload();
496        }
497
498        return validate;
499    }
500
501    std::string GraphicsManager::setFSAA(const std::string& mode)
502    {
503        this->ogreRoot_->getRenderSystem()->setConfigOption("FSAA", mode);
504
505        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
506
507        if (validate == "")
508        {
509            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
510            this->ogreRoot_->saveConfig();
511            Core::getInstance().updateOgreConfigTimestamp();
512        }
513
514        return validate;
515    }
516
517    std::string GraphicsManager::setVSync(bool vsync)
518    {
519        this->ogreRoot_->getRenderSystem()->setConfigOption("VSync", vsync ? "Yes" : "No");
520
521        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
522
523        if (validate == "")
524        {
525            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
526            this->ogreRoot_->saveConfig();
527            Core::getInstance().updateOgreConfigTimestamp();
528        }
529
530        return validate;
531    }
532
533    void GraphicsManager::printScreen()
534    {
535        assert(this->renderWindow_);
536        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".png");
537    }
538}
Note: See TracBrowser for help on using the repository browser.