Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/shaders/src/orxonox/Scene.cc @ 9420

Last change on this file since 9420 was 9420, checked in by davidsa, 12 years ago

Added some documentation to orxonox::Scene

  • Property svn:eol-style set to native
File size: 14.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/**
31@file Scene.cc
32@brief Implementation of Scene Class
33*/
34
35
36#include "Scene.h"
37
38#include <OgreRoot.h>
39#include <OgreSceneManager.h>
40#include <OgreSceneManagerEnumerator.h>
41#include <OgreSceneNode.h>
42
43#include <BulletCollision/BroadphaseCollision/btAxisSweep3.h>
44#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
45#include <BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>
46#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
47
48#include "core/CoreIncludes.h"
49#include "core/GameMode.h"
50#include "core/GUIManager.h"
51#include "core/XMLPort.h"
52#include "tools/BulletConversions.h"
53#include "Radar.h"
54#include "worldentities/WorldEntity.h"
55#include "Level.h"
56#include "RenderQueueListener.h"
57
58namespace orxonox
59{
60    CreateFactory(Scene);
61   
62    /**
63    @brief
64        Constructor, it sets common standard paramters for a scene depending on whether it will be rendered or not.
65        It also makes sure we user our own render queue listener for rendering instead of the standard listener provided by Ogre
66    */
67    Scene::Scene(BaseObject* creator) : BaseObject(creator), Synchronisable(creator)
68    {
69        RegisterObject(Scene);
70
71        this->setScene(SmartPtr<Scene>(this, false), OBJECTID_UNKNOWN);
72        this->bShadows_ = true;
73        this->soundReferenceDistance_ = 20.0;
74
75        if (GameMode::showsGraphics())
76        {
77            assert(Ogre::Root::getSingletonPtr());
78            this->sceneManager_ = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
79            this->rootSceneNode_ = this->sceneManager_->getRootSceneNode();
80            RenderQueueListener* renderQueueListener = new RenderQueueListener();
81            this->sceneManager_->addRenderQueueListener(renderQueueListener);//add our own renderQueueListener
82
83            this->radar_ = new Radar();
84        }
85        else
86        {
87            // create a dummy SceneManager of our own since we don't have Ogre::Root.
88            this->sceneManager_ = new Ogre::DefaultSceneManager("");
89            this->rootSceneNode_ = this->sceneManager_->getRootSceneNode();
90
91            this->radar_ = 0;
92        }
93
94        // No physics yet, XMLPort will do that.
95        const int defaultMaxWorldSize = 100000;
96        this->negativeWorldRange_ = Vector3::UNIT_SCALE * -defaultMaxWorldSize;
97        this->positiveWorldRange_ = Vector3::UNIT_SCALE *  defaultMaxWorldSize;
98        this->gravity_ = Vector3::ZERO;
99        this->physicalWorld_   = 0;
100        this->solver_          = 0;
101        this->dispatcher_      = 0;
102        this->collisionConfig_ = 0;
103        this->broadphase_      = 0;
104
105        this->registerVariables();
106    }
107
108    Scene::~Scene()
109    {
110        if (this->isInitialized())
111        {
112            if (GameMode::showsGraphics())
113                Ogre::Root::getSingleton().destroySceneManager(this->sceneManager_);
114            else
115                delete this->sceneManager_;
116
117            if (this->radar_)
118                this->radar_->destroy();
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.2f, 0.2f, 0.2f, 1.0f));
130        XMLPortParam(Scene, "shadow", setShadow, getShadow, xmlelement, mode).defaultValues(true);
131        XMLPortParam(Scene, "soundReferenceDistance", setSoundReferenceDistance, getSoundReferenceDistance, xmlelement, mode);
132
133        XMLPortParam(Scene, "gravity", setGravity, getGravity, xmlelement, mode);
134        XMLPortParam(Scene, "negativeWorldRange", setNegativeWorldRange, getNegativeWorldRange, xmlelement, mode);
135        XMLPortParam(Scene, "positiveWorldRange", setPositiveWorldRange, getPositiveWorldRange, xmlelement, mode);
136        XMLPortParam(Scene, "hasPhysics", setPhysicalWorld, hasPhysics, xmlelement, mode).defaultValues(true);
137
138        XMLPortObjectExtended(Scene, BaseObject, "", addObject, getObject, xmlelement, mode, true, false);
139    }
140
141    void Scene::registerVariables()
142    {
143        registerVariable(this->skybox_,             VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_applySkybox));
144        registerVariable(this->ambientLight_,       VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_applyAmbientLight));
145        registerVariable(this->negativeWorldRange_, VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_negativeWorldRange));
146        registerVariable(this->positiveWorldRange_, VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_positiveWorldRange));
147        registerVariable(this->gravity_,            VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_gravity));
148        registerVariable(this->bHasPhysics_,        VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_hasPhysics));
149        registerVariable(this->bShadows_,           VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::networkcallback_applyShadows));
150        registerVariable(this->getLevel(),          VariableDirection::ToClient, new NetworkCallback<Scene>(this, &Scene::changedLevel));
151    }
152
153    void Scene::setNegativeWorldRange(const Vector3& range)
154    {
155        if (range.length() < 10.0f)
156        {
157            orxout(internal_warning) << "Setting the negative world range to a very small value: "
158                                     << multi_cast<std::string>(range) << endl;
159        }
160        if (this->hasPhysics())
161        {
162            orxout(internal_warning) << "Attempting to set the physical world range at run time. "
163                                     << "This causes a complete physical reload which might take some time." << endl;
164            this->setPhysicalWorld(false);
165            this->negativeWorldRange_ = range;
166            this->setPhysicalWorld(true);
167        }
168        else
169            this->negativeWorldRange_ = range;
170    }
171
172    void Scene::setPositiveWorldRange(const Vector3& range)
173    {
174        if (range.length() < 10.0f)
175        {
176            orxout(internal_warning) << "Setting the positive world range to a very small value: "
177                                     << multi_cast<std::string>(range) << endl;
178        }
179        if (this->hasPhysics())
180        {
181            orxout(internal_warning) << "Attempting to set the physical world range at run time. "
182                                     << "This causes a complete physical reload which might take some time." << endl;
183            this->setPhysicalWorld(false);
184            this->positiveWorldRange_ = range;
185            this->setPhysicalWorld(true);
186        }
187        else
188            this->positiveWorldRange_ = range;
189    }
190
191    void Scene::setGravity(const Vector3& gravity)
192    {
193        this->gravity_ = gravity;
194        if (this->hasPhysics())
195            this->physicalWorld_->setGravity(multi_cast<btVector3>(this->gravity_));
196    }
197
198    void Scene::setPhysicalWorld(bool wantPhysics)
199    {
200        this->bHasPhysics_ = wantPhysics;
201        if (wantPhysics && !hasPhysics())
202        {
203            // Note: These are all little known default classes and values.
204            //       It would require further investigation to properly dertermine the right choices.
205            this->broadphase_      = new bt32BitAxisSweep3(
206                multi_cast<btVector3>(this->negativeWorldRange_), multi_cast<btVector3>(this->positiveWorldRange_));
207            this->collisionConfig_ = new btDefaultCollisionConfiguration();
208            this->dispatcher_      = new btCollisionDispatcher(this->collisionConfig_);
209            this->solver_          = new btSequentialImpulseConstraintSolver();
210
211            this->physicalWorld_   = new btDiscreteDynamicsWorld(this->dispatcher_, this->broadphase_, this->solver_, this->collisionConfig_);
212            this->physicalWorld_->setGravity(multi_cast<btVector3>(this->gravity_));
213
214            // also set the collision callback variable.
215            // Note: This is a global variable which we assign a static function.
216            // TODO: Check whether this (or anything about Bullet) works with multiple physics engine instances.
217            gContactAddedCallback = &Scene::collisionCallback;
218        }
219        else if (!wantPhysics && hasPhysics())
220        {
221            // Remove all WorldEntities and shove them to the queue since they would still like to be in a physical world.
222            for (std::set<WorldEntity*>::const_iterator it = this->physicalObjects_.begin();
223                it != this->physicalObjects_.end(); ++it)
224            {
225                this->physicalWorld_->removeRigidBody((*it)->physicalBody_);
226                this->physicalObjectQueue_.insert(*it);
227            }
228            this->physicalObjects_.clear();
229
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            physicalWorld_->stepSimulation(dt, 60);
269        }
270    }
271
272    void Scene::setSkybox(const std::string& skybox)
273    {
274        try
275        {
276            if (GameMode::showsGraphics() && this->sceneManager_)
277                this->sceneManager_->setSkyBox(true, skybox);
278        }
279        catch (const Ogre::Exception&)
280        {
281            orxout(internal_error) << "Could not load skybox '" << skybox << "':" << endl;
282            orxout(internal_error) << Exception::handleMessage() << endl;
283        }
284
285        this->skybox_ = skybox;
286    }
287
288    void Scene::setAmbientLight(const ColourValue& colour)
289    {
290        if (GameMode::showsGraphics() && this->sceneManager_)
291            this->sceneManager_->setAmbientLight(colour);
292
293        this->ambientLight_ = colour;
294    }
295
296    void Scene::setShadow(bool bShadow)
297    {
298        if (GameMode::showsGraphics() && this->sceneManager_)
299        {
300            if (bShadow)
301                this->sceneManager_->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
302            else
303                this->sceneManager_->setShadowTechnique(Ogre::SHADOWTYPE_NONE);
304        }
305
306        this->bShadows_ = bShadow;
307    }
308
309    void Scene::addObject(BaseObject* object)
310    {
311        this->objects_.push_back(object);
312        object->setScene(this, this->getObjectID());
313    }
314
315    BaseObject* Scene::getObject(unsigned int index) const
316    {
317        unsigned int i = 0;
318        for (std::list<BaseObject*>::const_iterator it = this->objects_.begin(); it != this->objects_.end(); ++it)
319        {
320            if (i == index)
321                return (*it);
322            ++i;
323        }
324        return 0;
325    }
326
327    void Scene::addPhysicalObject(WorldEntity* object)
328    {
329        if (object)
330        {
331            std::set<WorldEntity*>::iterator it = this->physicalObjects_.find(object);
332            if (it != this->physicalObjects_.end())
333                return;
334
335            this->physicalObjectQueue_.insert(object);
336        }
337    }
338
339    void Scene::removePhysicalObject(WorldEntity* object)
340    {
341        // check queue first
342        std::set<WorldEntity*>::iterator it = this->physicalObjectQueue_.find(object);
343        if (it != this->physicalObjectQueue_.end())
344        {
345            this->physicalObjectQueue_.erase(it);
346            return;
347        }
348
349        it = this->physicalObjects_.find(object);
350        if (it == this->physicalObjects_.end())
351            return;
352        this->physicalObjects_.erase(it);
353
354        if (this->hasPhysics())
355            this->physicalWorld_->removeRigidBody(object->physicalBody_);
356    }
357
358    /*static*/ bool Scene::collisionCallback(btManifoldPoint& cp, const btCollisionObject* colObj0, int partId0,
359                                             int index0, const btCollisionObject* colObj1, int partId1, int index1)
360    {
361        // get the WorldEntity pointers
362        SmartPtr<WorldEntity> object0 = static_cast<WorldEntity*>(colObj0->getUserPointer());
363        SmartPtr<WorldEntity> object1 = static_cast<WorldEntity*>(colObj1->getUserPointer());
364
365        // false means that bullet will assume we didn't modify the contact
366        bool modified = false;
367        if (object0->isCollisionCallbackActive())
368            modified |= object0->collidesAgainst(object1, cp);
369        if (object1->isCollisionCallbackActive())
370            modified |= object1->collidesAgainst(object0, cp);
371
372        return modified;
373    }
374}
Note: See TracBrowser for help on using the repository browser.