Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/cpp11_v2/src/orxonox/Scene.cc @ 10917

Last change on this file since 10917 was 10916, checked in by landauf, 10 years ago

use actual types instead of 'auto'. only exception is for complicated template types, e.g. when iterating over a map

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