Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/Scene.cc @ 11383

Last change on this file since 11383 was 11085, checked in by landauf, 9 years ago

enable glow shader in all scenes

  • Property svn:eol-style set to native
File size: 16.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 *      Fabian 'x3n' Landau
24 *      Reto Grieder (physics)
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30/**
31@file Scene.cc
32@brief Implementation of Scene Class
33*/
34
35
36#include "Scene.h"
37
38#include <OgreRoot.h>
39#include <OgreSceneManager.h>
40#include <OgreSceneManagerEnumerator.h>
41#include <OgreSceneNode.h>
42
43#include <BulletCollision/BroadphaseCollision/btAxisSweep3.h>
44#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
45#include <BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>
46#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
47
48#include "core/CoreIncludes.h"
49#include "core/GameMode.h"
50#include "core/GUIManager.h"
51#include "core/XMLPort.h"
52#include "core/command/ConsoleCommandIncludes.h"
53#include "tools/BulletConversions.h"
54#include "tools/BulletDebugDrawer.h"
55#include "tools/DebugDrawer.h"
56#include "Radar.h"
57#include "worldentities/WorldEntity.h"
58#include "Level.h"
59#include "RenderQueueListener.h"
60#include "graphics/GlobalShader.h"
61
62namespace orxonox
63{
64    RegisterClass(Scene);
65   
66    /**
67    @brief
68        Constructor, it sets common standard paramters for a scene depending on whether it will be rendered or not.
69        It also makes sure we user our own render queue listener for rendering instead of the standard listener provided by Ogre
70    */
71    SetConsoleCommand("Scene", "debugDrawPhysics", &Scene::consoleCommand_debugDrawPhysics).addShortcut().defaultValue(1, true).defaultValue(2, 0.5f);
72
73    Scene::Scene(Context* context) : BaseObject(context), Synchronisable(context), Context(context)
74    {
75        RegisterObject(Scene);
76
77        this->setScene(WeakPtr<Scene>(this), this->getObjectID()); // store a weak-pointer to itself (a strong-pointer would create a recursive dependency)
78
79        this->bShadows_ = true;
80        this->bDebugDrawPhysics_ = false;
81        this->debugDrawer_ = nullptr;
82        this->soundReferenceDistance_ = 20.0;
83        this->bIsUpdatingPhysics_ = false;
84
85        if (GameMode::showsGraphics())
86        {
87            assert(Ogre::Root::getSingletonPtr());
88            this->sceneManager_ = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
89            this->rootSceneNode_ = this->sceneManager_->getRootSceneNode();
90            this->renderQueueListener_ = new RenderQueueListener();
91            this->sceneManager_->addRenderQueueListener(this->renderQueueListener_);//add our own renderQueueListener
92
93            this->radar_ = new Radar();
94            this->glowShader_ = new GlobalShader(this);
95            this->glowShader_->setScene(WeakPtr<Scene>(this), this->getObjectID()); // avoid circular reference
96            this->glowShader_->getShader().setCompositorName("Glow");
97        }
98        else
99        {
100            // create a dummy SceneManager of our own since we don't have Ogre::Root.
101            this->sceneManager_ = new Ogre::DefaultSceneManager("");
102            this->rootSceneNode_ = this->sceneManager_->getRootSceneNode();
103
104            this->renderQueueListener_ = nullptr;
105            this->radar_ = nullptr;
106            this->glowShader_ = nullptr;
107        }
108
109        // No physics yet, XMLPort will do that.
110        const float defaultMaxWorldSize = 100000.0f;
111        this->negativeWorldRange_ = Vector3::UNIT_SCALE * -defaultMaxWorldSize;
112        this->positiveWorldRange_ = Vector3::UNIT_SCALE *  defaultMaxWorldSize;
113        this->gravity_ = Vector3::ZERO;
114        this->physicalWorld_   = nullptr;
115        this->solver_          = nullptr;
116        this->dispatcher_      = nullptr;
117        this->collisionConfig_ = nullptr;
118        this->broadphase_      = nullptr;
119
120        this->registerVariables();
121    }
122
123    Scene::~Scene()
124    {
125        if (this->isInitialized())
126        {
127            this->setPhysicalWorld(false);
128
129            if (this->radar_)
130                this->radar_->destroy();
131            if (this->glowShader_)
132                this->glowShader_->destroy();
133
134            if (GameMode::showsGraphics())
135            {
136                this->sceneManager_->removeRenderQueueListener(this->renderQueueListener_);
137                delete this->renderQueueListener_;
138                Ogre::Root::getSingleton().destroySceneManager(this->sceneManager_);
139            }
140            else
141                delete this->sceneManager_;
142        }
143    }
144
145    void Scene::XMLPort(Element& xmlelement, XMLPort::Mode mode)
146    {
147        SUPER(Scene, XMLPort, xmlelement, mode);
148
149        XMLPortParam(Scene, "skybox", setSkybox, getSkybox, xmlelement, mode);
150        XMLPortParam(Scene, "ambientlight", setAmbientLight, getAmbientLight, xmlelement, mode).defaultValues(ColourValue(0.2f, 0.2f, 0.2f, 1.0f));
151        XMLPortParam(Scene, "shadow", setShadow, getShadow, xmlelement, mode).defaultValues(true);
152        XMLPortParam(Scene, "soundReferenceDistance", setSoundReferenceDistance, getSoundReferenceDistance, xmlelement, mode);
153
154        XMLPortParam(Scene, "gravity", setGravity, getGravity, xmlelement, mode);
155        XMLPortParam(Scene, "negativeWorldRange", setNegativeWorldRange, getNegativeWorldRange, xmlelement, mode);
156        XMLPortParam(Scene, "positiveWorldRange", setPositiveWorldRange, getPositiveWorldRange, xmlelement, mode);
157        XMLPortParam(Scene, "hasPhysics", setPhysicalWorld, hasPhysics, xmlelement, mode).defaultValues(true);
158
159        XMLPortObjectExtended(Scene, BaseObject, "", addObject, getObject, xmlelement, mode, true, false);
160    }
161
162    void Scene::registerVariables()
163    {
164        registerVariable(this->skybox_,             VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_applySkybox));
165        registerVariable(this->ambientLight_,       VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_applyAmbientLight));
166        registerVariable(this->negativeWorldRange_, VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_negativeWorldRange));
167        registerVariable(this->positiveWorldRange_, VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_positiveWorldRange));
168        registerVariable(this->gravity_,            VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_gravity));
169        registerVariable(this->bHasPhysics_,        VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_hasPhysics));
170        registerVariable(this->bShadows_,           VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_applyShadows));
171    }
172
173    void Scene::setNegativeWorldRange(const Vector3& range)
174    {
175        if (range.length() < 10.0f)
176        {
177            orxout(internal_warning) << "Setting the negative world range to a very small value: "
178                                     << multi_cast<std::string>(range) << endl;
179        }
180        if (this->hasPhysics())
181        {
182            orxout(internal_warning) << "Attempting to set the physical world range at run time. "
183                                     << "This causes a complete physical reload which might take some time." << endl;
184            this->setPhysicalWorld(false);
185            this->negativeWorldRange_ = range;
186            this->setPhysicalWorld(true);
187        }
188        else
189            this->negativeWorldRange_ = range;
190    }
191
192    void Scene::setPositiveWorldRange(const Vector3& range)
193    {
194        if (range.length() < 10.0f)
195        {
196            orxout(internal_warning) << "Setting the positive world range to a very small value: "
197                                     << multi_cast<std::string>(range) << endl;
198        }
199        if (this->hasPhysics())
200        {
201            orxout(internal_warning) << "Attempting to set the physical world range at run time. "
202                                     << "This causes a complete physical reload which might take some time." << endl;
203            this->setPhysicalWorld(false);
204            this->positiveWorldRange_ = range;
205            this->setPhysicalWorld(true);
206        }
207        else
208            this->positiveWorldRange_ = range;
209    }
210
211    void Scene::setGravity(const Vector3& gravity)
212    {
213        this->gravity_ = gravity;
214        if (this->hasPhysics())
215            this->physicalWorld_->setGravity(multi_cast<btVector3>(this->gravity_));
216    }
217
218    void Scene::setPhysicalWorld(bool wantPhysics)
219    {
220        this->bHasPhysics_ = wantPhysics;
221        if (wantPhysics && !hasPhysics())
222        {
223            // Note: These are all little known default classes and values.
224            //       It would require further investigation to properly dertermine the right choices.
225            this->broadphase_      = new bt32BitAxisSweep3(
226                multi_cast<btVector3>(this->negativeWorldRange_), multi_cast<btVector3>(this->positiveWorldRange_));
227            this->collisionConfig_ = new btDefaultCollisionConfiguration();
228            this->dispatcher_      = new btCollisionDispatcher(this->collisionConfig_);
229            this->solver_          = new btSequentialImpulseConstraintSolver();
230
231            this->physicalWorld_   = new btDiscreteDynamicsWorld(this->dispatcher_, this->broadphase_, this->solver_, this->collisionConfig_);
232            this->physicalWorld_->setGravity(multi_cast<btVector3>(this->gravity_));
233
234            if (GameMode::showsGraphics() && this->sceneManager_)
235            {
236                this->debugDrawer_ = new BulletDebugDrawer(this->sceneManager_);
237                this->physicalWorld_->setDebugDrawer(this->debugDrawer_);
238            }
239
240            // also set the collision callback variable.
241            // Note: This is a global variable which we assign a static function.
242            // TODO: Check whether this (or anything about Bullet) works with multiple physics engine instances.
243            gContactAddedCallback = &Scene::collisionCallback;
244        }
245        else if (!wantPhysics && hasPhysics())
246        {
247            // Remove all WorldEntities and shove them to the queue since they would still like to be in a physical world.
248            for (WorldEntity* object : this->physicalObjects_)
249            {
250                this->physicalWorld_->removeRigidBody(object->physicalBody_);
251                this->physicalObjectQueue_.insert(object);
252            }
253            this->physicalObjects_.clear();
254
255            delete this->debugDrawer_;
256            delete this->physicalWorld_;
257            delete this->solver_;
258            delete this->dispatcher_;
259            delete this->collisionConfig_;
260            delete this->broadphase_;
261            this->physicalWorld_   = nullptr;
262            this->solver_          = nullptr;
263            this->dispatcher_      = nullptr;
264            this->collisionConfig_ = nullptr;
265            this->broadphase_      = nullptr;
266        }
267    }
268
269    void Scene::tick(float dt)
270    {
271        if (!GameMode::showsGraphics())
272        {
273            // We need to update the scene nodes if we don't render
274            this->rootSceneNode_->_update(true, false);
275        }
276        if (this->hasPhysics())
277        {
278            // TODO: This here is bad practice! It will slow down the first tick() by ages.
279            //       Rather have an initialise() method as well, called by the Level after everything has been loaded.
280            if (this->physicalObjectQueue_.size() > 0)
281            {
282                // Add all scheduled WorldEntities
283                for (WorldEntity* object : this->physicalObjectQueue_)
284                {
285                    this->physicalWorld_->addRigidBody(object->physicalBody_);
286                    this->physicalObjects_.insert(object);
287                }
288                this->physicalObjectQueue_.clear();
289            }
290
291            // Note: 60 means that Bullet will do physics correctly down to 1 frames per seconds.
292            //       Under that mark, the simulation will "loose time" and get unusable.
293            this->bIsUpdatingPhysics_ = true;
294            this->physicalWorld_->stepSimulation(dt, 60);
295            this->bIsUpdatingPhysics_ = false;
296
297            if (this->bDebugDrawPhysics_)
298                this->physicalWorld_->debugDrawWorld();
299        }
300    }
301
302    void Scene::setSkybox(const std::string& skybox)
303    {
304        try
305        {
306            if (GameMode::showsGraphics() && this->sceneManager_)
307                this->sceneManager_->setSkyBox(true, skybox);
308        }
309        catch (const Ogre::Exception&)
310        {
311            orxout(internal_error) << "Could not load skybox '" << skybox << "':" << endl;
312            orxout(internal_error) << Exception::handleMessage() << endl;
313        }
314
315        this->skybox_ = skybox;
316    }
317
318    void Scene::setAmbientLight(const ColourValue& colour)
319    {
320        if (GameMode::showsGraphics() && this->sceneManager_)
321            this->sceneManager_->setAmbientLight(colour);
322
323        this->ambientLight_ = colour;
324    }
325
326    void Scene::setShadow(bool bShadow)
327    {
328        if (GameMode::showsGraphics() && this->sceneManager_)
329        {
330            if (bShadow)
331                this->sceneManager_->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
332            else
333                this->sceneManager_->setShadowTechnique(Ogre::SHADOWTYPE_NONE);
334        }
335
336        this->bShadows_ = bShadow;
337    }
338
339    void Scene::addObject(BaseObject* object)
340    {
341        this->objects_.push_back(object);
342    }
343
344    BaseObject* Scene::getObject(unsigned int index) const
345    {
346        unsigned int i = 0;
347        for (BaseObject* object : this->objects_)
348        {
349            if (i == index)
350                return object;
351            ++i;
352        }
353        return nullptr;
354    }
355
356    void Scene::addPhysicalObject(WorldEntity* object)
357    {
358        if (object)
359        {
360            std::set<WorldEntity*>::iterator it = this->physicalObjects_.find(object);
361            if (it != this->physicalObjects_.end())
362                return;
363
364            this->physicalObjectQueue_.insert(object);
365        }
366    }
367
368    void Scene::removePhysicalObject(WorldEntity* object)
369    {
370        // check queue first
371        std::set<WorldEntity*>::iterator it = this->physicalObjectQueue_.find(object);
372        if (it != this->physicalObjectQueue_.end())
373        {
374            this->physicalObjectQueue_.erase(it);
375            return;
376        }
377
378        it = this->physicalObjects_.find(object);
379        if (it == this->physicalObjects_.end())
380            return;
381        this->physicalObjects_.erase(it);
382
383        if (this->hasPhysics())
384            this->physicalWorld_->removeRigidBody(object->physicalBody_);
385    }
386
387    /*static*/ bool Scene::collisionCallback(btManifoldPoint& cp, const btCollisionObject* colObj0, int partId0,
388                                             int index0, const btCollisionObject* colObj1, int partId1, int index1)
389    {
390        // get the WorldEntity pointers
391        StrongPtr<WorldEntity> object0 = static_cast<WorldEntity*>(colObj0->getUserPointer());
392        StrongPtr<WorldEntity> object1 = static_cast<WorldEntity*>(colObj1->getUserPointer());
393
394        // get the CollisionShape pointers
395        const btCollisionShape* cs0 = colObj0->getCollisionShape();
396        const btCollisionShape* cs1 = colObj1->getCollisionShape();
397
398        // false means that bullet will assume we didn't modify the contact
399        bool modified = false;
400        if (object0->isCollisionCallbackActive())
401            modified |= object0->collidesAgainst(object1, cs1, cp);
402        if (object1->isCollisionCallbackActive())
403            modified |= object1->collidesAgainst(object0, cs0, cp);
404
405        return modified;
406    }
407
408    void Scene::setDebugDrawPhysics(bool bDraw, bool bFill, float fillAlpha)
409    {
410        this->bDebugDrawPhysics_ = bDraw;
411        if (this->debugDrawer_)
412            this->debugDrawer_->configure(bFill, fillAlpha);
413    }
414
415    /*static*/ void Scene::consoleCommand_debugDrawPhysics(bool bDraw, bool bFill, float fillAlpha)
416    {
417        for (Scene* scene : ObjectList<Scene>())
418            scene->setDebugDrawPhysics(bDraw, bFill, fillAlpha);
419    }
420}
Note: See TracBrowser for help on using the repository browser.