Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/consolecommands3/src/libraries/core/GraphicsManager.cc @ 7621

Last change on this file since 7621 was 7276, checked in by landauf, 14 years ago

until now we could group words and values in console commands and XML values using parentheses (…). now we have to use braces {…}, because that works better in connection with Tcl. in practice however this feature was never used before, so this change shouldn't affect anything.

  • Property svn:eol-style set to native
File size: 17.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 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
25 *   Co-authors:
26 *      Felix Schulthess
27 *
28 */
29
30#include "GraphicsManager.h"
31
32#include <fstream>
33#include <sstream>
34#include <boost/filesystem.hpp>
35#include <boost/shared_array.hpp>
36
37#include <OgreArchiveFactory.h>
38#include <OgreArchiveManager.h>
39#include <OgreFrameListener.h>
40#include <OgreRoot.h>
41#include <OgreLogManager.h>
42#include <OgreRenderWindow.h>
43#include <OgreRenderSystem.h>
44#include <OgreResourceGroupManager.h>
45#include <OgreTextureManager.h>
46#include <OgreViewport.h>
47#include <OgreWindowEventUtilities.h>
48
49#include "SpecialConfig.h"
50#include "util/Clock.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 "Game.h"
57#include "GameMode.h"
58#include "Loader.h"
59#include "MemoryArchive.h"
60#include "PathConfig.h"
61#include "WindowEventListener.h"
62#include "XMLFile.h"
63#include "command/ConsoleCommand.h"
64
65namespace orxonox
66{
67    static const std::string __CC_printScreen_name = "printScreen";
68    DeclareConsoleCommand(__CC_printScreen_name, &prototype::void__void);
69
70    class OgreWindowEventListener : public Ogre::WindowEventListener
71    {
72    public:
73        void windowResized     (Ogre::RenderWindow* rw)
74            { orxonox::WindowEventListener::resizeWindow(rw->getWidth(), rw->getHeight()); }
75        void windowFocusChange (Ogre::RenderWindow* rw)
76            { orxonox::WindowEventListener::changeWindowFocus(); }
77        void windowClosed      (Ogre::RenderWindow* rw)
78            { orxonox::Game::getInstance().stop(); }
79        void windowMoved       (Ogre::RenderWindow* rw)
80            { orxonox::WindowEventListener::moveWindow(); }
81    };
82
83    GraphicsManager* GraphicsManager::singletonPtr_s = 0;
84
85    /**
86    @brief
87        Non-initialising constructor.
88    */
89    GraphicsManager::GraphicsManager(bool bLoadRenderer)
90        : ogreWindowEventListener_(new OgreWindowEventListener())
91#if OGRE_VERSION < 0x010600
92        , memoryArchiveFactory_(new MemoryArchiveFactory())
93#endif
94        , renderWindow_(0)
95        , viewport_(0)
96    {
97        RegisterObject(GraphicsManager);
98
99        this->setConfigValues();
100
101        // Ogre setup procedure (creating Ogre::Root)
102        this->loadOgreRoot();
103
104        // At first, add the root paths of the data directories as resource locations
105        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getDataPathString(), "FileSystem");
106        // Load resources
107        resources_.reset(new XMLFile("DefaultResources.oxr"));
108        resources_->setLuaSupport(false);
109        Loader::open(resources_.get());
110
111        // Only for development runs
112        if (PathConfig::isDevelopmentRun())
113        {
114            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getExternalDataPathString(), "FileSystem");
115            extResources_.reset(new XMLFile("resources.oxr"));
116            extResources_->setLuaSupport(false);
117            Loader::open(extResources_.get());
118        }
119
120        if (bLoadRenderer)
121        {
122            // Reads the ogre config and creates the render window
123            this->upgradeToGraphics();
124        }
125    }
126
127    /**
128    @brief
129        Destruction is done by the member scoped_ptrs.
130    */
131    GraphicsManager::~GraphicsManager()
132    {
133        Loader::unload(debugOverlay_.get());
134
135        Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow_, ogreWindowEventListener_.get());
136        ModifyConsoleCommand(__CC_printScreen_name).resetFunction();
137
138        // Undeclare the resources
139        Loader::unload(resources_.get());
140        if (PathConfig::isDevelopmentRun())
141            Loader::unload(extResources_.get());
142    }
143
144    void GraphicsManager::setConfigValues()
145    {
146        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
147            .description("Location of the Ogre config file");
148        SetConfigValue(ogrePluginsDirectory_, specialConfig::ogrePluginsDirectory)
149            .description("Folder where the Ogre plugins are located.");
150        SetConfigValue(ogrePlugins_, specialConfig::ogrePlugins)
151            .description("Comma separated list of all plugins to load.");
152        SetConfigValue(ogreLogFile_,     "ogre.log")
153            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
154        SetConfigValue(ogreLogLevelTrivial_ , 5)
155            .description("Corresponding orxonox debug level for ogre Trivial");
156        SetConfigValue(ogreLogLevelNormal_  , 4)
157            .description("Corresponding orxonox debug level for ogre Normal");
158        SetConfigValue(ogreLogLevelCritical_, 2)
159            .description("Corresponding orxonox debug level for ogre Critical");
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#if OGRE_VERSION < 0x010600
181        // WORKAROUND: There is an incompatibility for particle scripts when trying
182        // to support both Ogre 1.4 and 1.6. The hacky solution is to create
183        // scripts for the 1.6 version and then remove the inserted "particle_system"
184        // keyword. But we need to supply these new scripts as well, which is why
185        // there is an extra Ogre::Archive dealing with it in the memory.
186        using namespace Ogre;
187        ArchiveManager::getSingleton().addArchiveFactory(memoryArchiveFactory_.get());
188        const StringVector& groups = ResourceGroupManager::getSingleton().getResourceGroups();
189        // Travers all groups
190        for (StringVector::const_iterator itGroup = groups.begin(); itGroup != groups.end(); ++itGroup)
191        {
192            FileInfoListPtr files = ResourceGroupManager::getSingleton().findResourceFileInfo(*itGroup, "*.particle");
193            for (FileInfoList::const_iterator itFile = files->begin(); itFile != files->end(); ++itFile)
194            {
195                // open file
196                Ogre::DataStreamPtr input = ResourceGroupManager::getSingleton().openResource(itFile->filename, *itGroup, false);
197                std::stringstream output;
198                // Parse file and replace "particle_system" with nothing
199                while (!input->eof())
200                {
201                    std::string line = input->getLine();
202                    size_t pos = line.find("particle_system");
203                    if (pos != std::string::npos)
204                    {
205                        // 15 is the length of "particle_system"
206                        line.replace(pos, 15, "");
207                    }
208                    output << line << std::endl;
209                }
210                // Add file to the memory archive
211                shared_array<char> data(new char[output.str().size()]);
212                // Debug optimisations
213                const std::string& outputStr = output.str();
214                char* rawData = data.get();
215                for (unsigned i = 0; i < outputStr.size(); ++i)
216                    rawData[i] = outputStr[i];
217                MemoryArchive::addFile("particle_scripts_ogre_1.4_" + *itGroup, itFile->filename, data, output.str().size());
218            }
219            if (!files->empty())
220            {
221                // Declare the files, but using a new group
222                ResourceGroupManager::getSingleton().addResourceLocation("particle_scripts_ogre_1.4_" + *itGroup,
223                    "Memory", "particle_scripts_ogre_1.4_" + *itGroup);
224            }
225        }
226#endif
227
228        // Initialise all resources (do this AFTER the renderer has been loaded!)
229        // Note: You can only do this once! Ogre will check whether a resource group has
230        // already been initialised. If you need to load resources later, you will have to
231        // choose another resource group.
232        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
233    }
234
235    /**
236    @brief
237        Creates the Ogre Root object and sets up the ogre log.
238    */
239    void GraphicsManager::loadOgreRoot()
240    {
241        COUT(3) << "Setting up Ogre..." << std::endl;
242
243        if (ogreConfigFile_.empty())
244        {
245            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
246            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
247        }
248        if (ogreLogFile_.empty())
249        {
250            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
251            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
252        }
253
254        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
255        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
256
257        // create a new logManager
258        // Ogre::Root will detect that we've already created a Log
259        ogreLogger_.reset(new Ogre::LogManager());
260        COUT(4) << "Ogre LogManager created" << std::endl;
261
262        // create our own log that we can listen to
263        Ogre::Log *myLog;
264        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
265        COUT(4) << "Ogre Log created" << std::endl;
266
267        myLog->setLogDetail(Ogre::LL_BOREME);
268        myLog->addListener(this);
269
270        COUT(4) << "Creating Ogre Root..." << std::endl;
271
272        // check for config file existence because Ogre displays (caught) exceptions if not
273        if (!boost::filesystem::exists(ogreConfigFilepath))
274        {
275            // create a zero sized file
276            std::ofstream creator;
277            creator.open(ogreConfigFilepath.string().c_str());
278            creator.close();
279        }
280
281        // Leave plugins file empty. We're going to do that part manually later
282        ogreRoot_.reset(new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string()));
283
284        COUT(3) << "Ogre set up done." << std::endl;
285    }
286
287    void GraphicsManager::loadOgrePlugins()
288    {
289        // just to make sure the next statement doesn't segfault
290        if (ogrePluginsDirectory_.empty())
291            ogrePluginsDirectory_ = '.';
292
293        boost::filesystem::path folder(ogrePluginsDirectory_);
294        // Do some SubString magic to get the comma separated list of plugins
295        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '{', '}', false, '\0');
296        // Use backslash paths on Windows! file_string() already does that though.
297        for (unsigned int i = 0; i < plugins.size(); ++i)
298            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
299    }
300
301    void GraphicsManager::loadRenderer()
302    {
303        CCOUT(4) << "Configuring Renderer" << std::endl;
304
305        if (!ogreRoot_->restoreConfig())
306            if (!ogreRoot_->showConfigDialog())
307                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue failed.");
308
309        CCOUT(4) << "Creating render window" << std::endl;
310
311        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
312        // Propagate the size of the new winodw
313        this->ogreWindowEventListener_->windowResized(renderWindow_);
314
315        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_.get());
316
317        // create a full screen default viewport
318        // Note: This may throw when adding a viewport with an existing z-order!
319        //       But in our case we only have one viewport for now anyway, therefore
320        //       no ScopeGuards or anything to handle exceptions.
321        this->viewport_ = this->renderWindow_->addViewport(0, 0);
322
323        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(Ogre::MIP_UNLIMITED);
324
325        // add console commands
326        ModifyConsoleCommand(__CC_printScreen_name).setFunction(&GraphicsManager::printScreen, this);
327    }
328
329    void GraphicsManager::loadDebugOverlay()
330    {
331        // Load debug overlay to show info about fps and tick time
332        COUT(4) << "Loading Debug Overlay..." << std::endl;
333        debugOverlay_.reset(new XMLFile("debug.oxo"));
334        Loader::open(debugOverlay_.get());
335    }
336
337    /**
338    @note
339        A note about the Ogre::FrameListener: Even though we don't use them,
340        they still get called. However, the delta times are not correct (except
341        for timeSinceLastFrame, which is the most important). A little research
342        as shown that there is probably only one FrameListener that doesn't even
343        need the time. So we shouldn't run into problems.
344    */
345    void GraphicsManager::postUpdate(const Clock& time)
346    {
347        Ogre::FrameEvent evt;
348        evt.timeSinceLastFrame = time.getDeltaTime();
349        evt.timeSinceLastEvent = time.getDeltaTime(); // note: same time, but shouldn't matter anyway
350
351        // don't forget to call _fireFrameStarted to OGRE to make sure
352        // everything goes smoothly
353        ogreRoot_->_fireFrameStarted(evt);
354
355        // Pump messages in all registered RenderWindows
356        // This calls the WindowEventListener objects.
357        Ogre::WindowEventUtilities::messagePump();
358        // make sure the window stays active even when not focused
359        // (probably only necessary on windows)
360        this->renderWindow_->setActive(true);
361
362        // Time before rendering
363        uint64_t timeBeforeTick = time.getRealMicroseconds();
364
365        // Render frame
366        ogreRoot_->_updateAllRenderTargets();
367
368        uint64_t timeAfterTick = time.getRealMicroseconds();
369        // Subtract the time used for rendering from the tick time counter
370        Game::getInstance().subtractTickTime((int32_t)(timeAfterTick - timeBeforeTick));
371
372        // again, just to be sure OGRE works fine
373        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
374    }
375
376    void GraphicsManager::setCamera(Ogre::Camera* camera)
377    {
378        this->viewport_->setCamera(camera);
379    }
380
381    /**
382    @brief
383        Method called by the LogListener interface from Ogre.
384        We use it to capture Ogre log messages and handle it ourselves.
385    @param message
386        The message to be logged
387    @param lml
388        The message level the log is using
389    @param maskDebug
390        If we are printing to the console or not
391    @param logName
392        The name of this log (so you can have several listeners
393        for different logs, and identify them)
394    */
395    void GraphicsManager::messageLogged(const std::string& message,
396        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
397    {
398        int orxonoxLevel;
399        std::string introduction;
400        // Do not show caught OGRE exceptions in front
401        if (message.find("EXCEPTION") != std::string::npos)
402        {
403            orxonoxLevel = OutputLevel::Debug;
404            introduction = "Ogre, caught exception: ";
405        }
406        else
407        {
408            switch (lml)
409            {
410            case Ogre::LML_TRIVIAL:
411                orxonoxLevel = this->ogreLogLevelTrivial_;
412                break;
413            case Ogre::LML_NORMAL:
414                orxonoxLevel = this->ogreLogLevelNormal_;
415                break;
416            case Ogre::LML_CRITICAL:
417                orxonoxLevel = this->ogreLogLevelCritical_;
418                break;
419            default:
420                orxonoxLevel = 0;
421            }
422            introduction = "Ogre: ";
423        }
424        OutputHandler::getOutStream(orxonoxLevel)
425            << introduction << message << std::endl;
426    }
427
428    size_t GraphicsManager::getRenderWindowHandle()
429    {
430        size_t windowHnd = 0;
431        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
432        return windowHnd;
433    }
434
435    bool GraphicsManager::isFullScreen() const
436    {
437        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
438        if (options.find("Full Screen") != options.end())
439        {
440            if (options["Full Screen"].currentValue == "Yes")
441                return true;
442            else
443                return false;
444        }
445        else
446        {
447            COUT(0) << "Could not find 'Full Screen' render system option. Fix This!!!" << std::endl;
448            return false;
449        }
450    }
451
452    void GraphicsManager::printScreen()
453    {
454        assert(this->renderWindow_);
455        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".png");
456    }
457}
Note: See TracBrowser for help on using the repository browser.