Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 12302 was 11795, checked in by landauf, 7 years ago

merged ogre1.9 (including cegui0.8) into new branch

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