Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/modularships/src/orxonox/Scene.cc @ 10071

Last change on this file since 10071 was 10053, checked in by noep, 10 years ago

Fixed yet another segfault (which we hadn't seen yet).
Cleared emptyLevel, created two testlevels (testing with boxes)

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