Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/rocket/src/libraries/core/GraphicsManager.cc @ 6522

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

Removed a ton of msvc warnings revealed with OGRE v1.7 (they removed the warning suppressors in OgrePrerequisites.h).
All of them are conversions from one type to another that might be lossy (mostly double to float, please always use "3.7f" instead of "3.7" as constants when using floats).

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