Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/shaders_merge/src/orxonox/Scene.cc @ 11073

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

merged branch 'shaders' into branch 'shaders_merge'

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