Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 6057 was 5929, checked in by rgrieder, 15 years ago

Merged core5 branch back to the trunk.
Key features include clean level unloading and an extended XML event system.

Two important notes:
Delete your keybindings.ini files! * or you will still get parser errors when loading the key bindings.
Delete build_dir/lib/modules/libgamestates.module! * or orxonox won't start.
Best thing to do is to delete the build folder ;)

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