Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation/src/orxonox/objects/Scene.cc @ 2466

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

Added callback for collisions. Every WorldEntity that collides against another will be called with the following function:
virtual bool collidesAgainst(WorldEntity* otherObject, btManifoldPoint& contactPoint);
btManifoldPoint is from Bullet and tells you more about the contact point, like position.

Per default, the callback is disabled. Enable it with this→enableCollisionCallback(), for instance in your derived class constructor.
Note that if you activate the callback for e.g. SpaceShips, but not for MovableEntities, then collidesAgainst will only be called in the SpaceShip. This could be changed however, just ask me.

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