Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core7/src/orxonox/Scene.cc @ 10570

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

not sure why Level was synchronized in Scene, but it doesn't seem to be necessary anymore

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