Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2470 was 2468, checked in by rgrieder, 16 years ago

Bugfix for dedicated mode. Should work now except for an exception with an OrxonoxOverlay. Was that there before too?

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