Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/Scene.cc @ 11080

Last change on this file since 11080 was 11080, checked in by landauf, 8 years ago

merged shaders back to trunk (pps project from HS 2012)

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