Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/physics/src/orxonox/objects/worldentities/WorldEntity.cc @ 2407

Last change on this file since 2407 was 2407, checked in by rgrieder, 15 years ago
  • Removed debug output
  • Fixed a bug with parentID_ in CollisionShape
  • Reactivated overwrite mechanism in ControllableEntity
  • Build fix in WE for last commit.
  • Property svn:eol-style set to native
File size: 17.8 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 "WorldEntity.h"
32
33#include <cassert>
34#include <OgreSceneManager.h>
35
36#include "BulletCollision/CollisionShapes/btCollisionShape.h"
37#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h"
38#include "BulletDynamics/Dynamics/btRigidBody.h"
39
40#include "util/Exception.h"
41#include "util/Convert.h"
42#include "core/CoreIncludes.h"
43#include "core/XMLPort.h"
44
45#include "objects/Scene.h"
46#include "objects/collisionshapes/CollisionShape.h"
47#include "objects/collisionshapes/CompoundCollisionShape.h"
48
49namespace orxonox
50{
51    const Vector3 WorldEntity::FRONT = Vector3::NEGATIVE_UNIT_Z;
52    const Vector3 WorldEntity::BACK  = Vector3::UNIT_Z;
53    const Vector3 WorldEntity::LEFT  = Vector3::NEGATIVE_UNIT_X;
54    const Vector3 WorldEntity::RIGHT = Vector3::UNIT_X;
55    const Vector3 WorldEntity::DOWN  = Vector3::NEGATIVE_UNIT_Y;
56    const Vector3 WorldEntity::UP    = Vector3::UNIT_Y;
57
58    WorldEntity::WorldEntity(BaseObject* creator) : BaseObject(creator), network::Synchronisable(creator)
59    {
60        RegisterObject(WorldEntity);
61
62        assert(this->getScene());
63        assert(this->getScene()->getRootSceneNode());
64
65        this->node_ = this->getScene()->getRootSceneNode()->createChildSceneNode();
66
67        this->parent_ = 0;
68        this->parentID_ = (unsigned int)-1;
69
70        this->node_->setPosition(Vector3::ZERO);
71        this->node_->setOrientation(Quaternion::IDENTITY);
72
73        // Default behaviour does not include physics
74        this->physicalBody_ = 0;
75        this->collisionShape_ = new CompoundCollisionShape(this);
76        this->mass_ = 0;
77        this->childMass_ = 0;
78        this->collisionType_ = None;
79        this->collisionTypeSynchronised_ = None;
80
81        this->registerVariables();
82    }
83
84    WorldEntity::~WorldEntity()
85    {
86        if (this->isInitialized())
87        {
88            this->node_->detachAllObjects();
89            if (this->getScene()->getSceneManager())
90                this->getScene()->getSceneManager()->destroySceneNode(this->node_->getName());
91
92            if (this->physicalBody_)
93            {
94                if (this->physicalBody_->isInWorld())
95                    this->getScene()->getPhysicalWorld()->removeRigidBody(this->physicalBody_);
96                delete this->physicalBody_;
97            }
98            // TODO: Delete collisionShapes
99        }
100    }
101
102    void WorldEntity::XMLPort(Element& xmlelement, XMLPort::Mode mode)
103    {
104        SUPER(WorldEntity, XMLPort, xmlelement, mode);
105
106        XMLPortParamTemplate(WorldEntity, "position", setPosition, getPosition, xmlelement, mode, const Vector3&);
107        XMLPortParamTemplate(WorldEntity, "orientation", setOrientation, getOrientation, xmlelement, mode, const Quaternion&);
108        XMLPortParamLoadOnly(WorldEntity, "lookat", lookAt_xmlport, xmlelement, mode);
109        XMLPortParamLoadOnly(WorldEntity, "direction", setDirection_xmlport, xmlelement, mode);
110        XMLPortParamLoadOnly(WorldEntity, "yaw", yaw_xmlport, xmlelement, mode);
111        XMLPortParamLoadOnly(WorldEntity, "pitch", pitch_xmlport, xmlelement, mode);
112        XMLPortParamLoadOnly(WorldEntity, "roll", roll_xmlport, xmlelement, mode);
113        XMLPortParamTemplate(WorldEntity, "scale3D", setScale3D, getScale3D, xmlelement, mode, const Vector3&);
114        XMLPortParam(WorldEntity, "scale", setScale, getScale, xmlelement, mode);
115
116        // Physics
117        XMLPortParam(WorldEntity, "collisionType", setCollisionTypeStr, getCollisionTypeStr, xmlelement, mode);
118        XMLPortParam(WorldEntity, "mass", setMass, getMass, xmlelement, mode);
119
120        // Other attached WorldEntities
121        XMLPortObject(WorldEntity, WorldEntity, "attached", attach, getAttachedObject, xmlelement, mode);
122        // Attached collision shapes
123        XMLPortObject(WorldEntity, CollisionShape, "collisionShapes", attachCollisionShape, getAttachedCollisionShape, xmlelement, mode);
124    }
125
126    void WorldEntity::registerVariables()
127    {
128        REGISTERDATA(this->bActive_,     network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedActivity));
129        REGISTERDATA(this->bVisible_,    network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedVisibility));
130
131        // HACK: Removed the call because it gets called the first time as well
132        REGISTERDATA(this->getScale3D(), network::direction::toclient);//, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::scaleChanged));
133
134        int* collisionType = (int*)&this->collisionTypeSynchronised_;
135        REGISTERDATA(*collisionType, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::collisionTypeChanged));
136        REGISTERDATA(this->mass_,    network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::massChanged));
137
138        REGISTERDATA(this->parentID_, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::updateParent));
139    }
140
141    void WorldEntity::updateParent()
142    {
143        WorldEntity* parent = dynamic_cast<WorldEntity*>(Synchronisable::getSynchronisable(this->parentID_));
144        if (parent)
145            this->attachToParent(parent);
146    }
147
148    void WorldEntity::collisionTypeChanged()
149    {
150        if (this->collisionTypeSynchronised_ != Dynamic &&
151            this->collisionTypeSynchronised_ != Kinematic &&
152            this->collisionTypeSynchronised_ != Static &&
153            this->collisionTypeSynchronised_ != None)
154        {
155            CCOUT(1) << "Error when collsion Type was received over network. Unknown enum value:" << this->collisionTypeSynchronised_ << std::endl;
156        }
157        else if (this->collisionTypeSynchronised_ != collisionType_)
158        {
159            if (this->parent_)
160                CCOUT(2) << "Warning: Network connection tried to set the collision type of an attached WE. Ignoring." << std::endl;
161            else
162                this->setCollisionType(this->collisionTypeSynchronised_);
163        }
164    }
165
166    void WorldEntity::massChanged()
167    {
168        this->setMass(this->mass_);
169    }
170
171    void WorldEntity::attach(WorldEntity* object)
172    {
173        // check first whether attaching is even allowed
174        if (object->hasPhysics())
175        {
176            if (!this->hasPhysics())
177                ThrowException(PhysicsViolation, "Cannot attach a physical object to a non physical one.");
178            else if (object->isDynamic())
179                ThrowException(PhysicsViolation, "Cannot attach a dynamic object to a WorldEntity.");
180            else if (object->isKinematic() && this->isDynamic())
181                ThrowException(PhysicsViolation, "Cannot attach a kinematic object to a dynamic one.");
182            else if (object->isKinematic())
183                ThrowException(NotImplemented, "Cannot attach a kinematic object to a static or kinematic one: Not yet implemented.");
184            else
185            {
186                if (this->physicalBody_->isInWorld())
187                    removeFromPhysicalWorld();
188                if (object->physicalBody_->isInWorld())
189                    this->getScene()->removeRigidBody(object->physicalBody_);
190
191                // static to static/kinematic/dynamic --> merge shapes
192                this->childMass_ += object->getMass();
193                this->attachCollisionShape(object->getCollisionShape(), true);
194                // Remove the btRigidBody from the child object.
195                // That also implies that cannot add a physics WE to the child afterwards.
196                object->setCollisionType(None);
197
198                addToPhysicalWorld();
199            }
200        }
201
202        if (object->getParent())
203            object->detachFromParent();
204        else
205        {
206            Ogre::Node* parent = object->node_->getParent();
207            if (parent)
208                parent->removeChild(object->node_);
209        }
210
211        this->node_->addChild(object->node_);
212        this->children_.insert(object);
213        object->parent_ = this;
214        object->parentID_ = this->getObjectID();
215    }
216
217    void WorldEntity::detach(WorldEntity* object)
218    {
219        this->node_->removeChild(object->node_);
220        this->children_.erase(object);
221        object->parent_ = 0;
222        object->parentID_ = (unsigned int)-1;
223
224//        this->getScene()->getRootSceneNode()->addChild(object->node_);
225    }
226
227    WorldEntity* WorldEntity::getAttachedObject(unsigned int index) const
228    {
229        unsigned int i = 0;
230        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
231        {
232            if (i == index)
233                return (*it);
234            ++i;
235        }
236        return 0;
237    }
238
239    void WorldEntity::attachCollisionShape(CollisionShape* shape, bool bWorldEntityRoot)
240    {
241        this->collisionShape_->addChildShape(shape, bWorldEntityRoot);
242
243        if (this->physicalBody_)
244        {
245            if (this->physicalBody_->isInWorld())
246            {
247                COUT(2) << "Warning: Attaching collision shapes at run time causes its physical body to be removed and added again.";
248                removeFromPhysicalWorld();
249            }
250            if (this->collisionShape_->getCollisionShape())
251                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
252            // recalculate inertia tensor
253            internalSetMassProps();
254            addToPhysicalWorld();
255        }
256    }
257
258    CollisionShape* WorldEntity::getAttachedCollisionShape(unsigned int index) const
259    {
260        return this->collisionShape_->getChildShape(index);
261    }
262
263    void WorldEntity::addToPhysicalWorld() const
264    {
265        if (this->isActive() && this->hasPhysics() && !this->physicalBody_->isInWorld())
266            this->getScene()->addRigidBody(this->physicalBody_);
267    }
268
269    void WorldEntity::removeFromPhysicalWorld() const
270    {
271        if (this->hasPhysics())
272            this->getScene()->removeRigidBody(this->physicalBody_);
273    }
274
275    void WorldEntity::setScale3D(const Vector3& scale)
276    {
277        if (this->hasPhysics())
278            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
279
280        this->node_->setScale(scale);
281    }
282
283    void WorldEntity::scale3D(const Vector3& scale)
284    {
285        if (this->hasPhysics())
286            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
287
288        this->node_->scale(scale);
289    }
290
291    void WorldEntity::setMass(float mass)
292    {
293        this->mass_ = mass;
294        if (!this->hasPhysics())
295            return;
296        // TODO: Add this again when new network callbacks work properly
297            //COUT(3) << "Warning: Setting the mass of a WorldEntity with no physics has no effect." << std::endl;
298        else
299        {
300            if (this->physicalBody_->isInWorld())
301            {
302                COUT(2) << "Warning: Setting the mass of a WorldEntity at run time causes its physical body to be removed and added again." << std::endl;
303                removeFromPhysicalWorld();
304            }
305            internalSetMassProps();
306        }
307
308        addToPhysicalWorld();
309    }
310
311    void WorldEntity::internalSetMassProps()
312    {
313        assert(hasPhysics());
314
315        if (this->isStatic())
316        {
317            // Just set everything to zero
318            this->physicalBody_->setMassProps(0.0f, btVector3(0, 0, 0));
319        }
320        else if ((this->mass_ + this->childMass_) == 0.0f)
321        {
322            // Use default values to avoid very large or very small values
323            CCOUT(2) << "Warning: Setting the internal physical mass to 1.0 because mass_ is 0.0." << std::endl;
324            this->physicalBody_->setMassProps(1.0f, this->getLocalInertia(1.0f));
325        }
326        else
327            this->physicalBody_->setMassProps(this->mass_, this->getLocalInertia(this->mass_ + this->childMass_));
328    }
329
330    btVector3 WorldEntity::getLocalInertia(btScalar mass) const
331    {
332        btVector3 inertia(0, 0, 0);
333        if (this->collisionShape_->getCollisionShape())
334            this->collisionShape_->getCollisionShape()->calculateLocalInertia(mass, inertia);
335        return inertia;
336    }
337
338    void WorldEntity::setCollisionType(CollisionType type)
339    {
340        // If we are already attached to a parent, this would be a bad idea..
341        if (this->parent_)
342            ThrowException(PhysicsViolation, "Cannot set the collision type of a WorldEntity with a parent");
343        else if (this->physicalBody_ && this->physicalBody_->isInWorld())
344            ThrowException(PhysicsViolation, "Warning: Cannot set the collision type at run time.");
345
346        // Check for type legality. Could be StaticEntity or MovableEntity
347        if (!this->isCollisionTypeLegal(type))
348            return; // exception gets issued anyway
349
350        // Check whether we have to create or destroy.
351        if (type != None && this->collisionType_ == None)
352        {
353            // Check whether there was some scaling applied.
354            if (!this->node_->getScale().positionEquals(Vector3(1, 1, 1), 0.001))
355                ThrowException(NotImplemented, "Cannot create a physical body if there is scaling applied to the node: Not yet implemented.");
356
357            // Create new rigid body
358            btCollisionShape* temp = 0;
359            if (this->collisionShape_)
360                temp = this->collisionShape_->getCollisionShape();
361            btRigidBody::btRigidBodyConstructionInfo bodyConstructionInfo(0, this, temp, btVector3(0,0,0));
362            this->physicalBody_ = new btRigidBody(bodyConstructionInfo);
363            this->physicalBody_->setUserPointer(this);
364            this->physicalBody_->setActivationState(DISABLE_DEACTIVATION);
365        }
366        else if (type == None && this->collisionType_ != None)
367        {
368            // Destroy rigid body
369            assert(this->physicalBody_);
370            removeFromPhysicalWorld();
371            delete this->physicalBody_;
372            this->physicalBody_ = 0;
373            this->collisionType_ = None;
374            this->collisionTypeSynchronised_ = None;
375            return;
376        }
377
378        // Change type
379        switch (type)
380        {
381        case Dynamic:
382            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !(btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT));
383            break;
384        case Kinematic:
385            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT);
386            break;
387        case Static:
388            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
389            break;
390        case None:
391            return; // this->collisionType_ was None too
392        }
393
394        // update our variable for faster checks
395        updateCollisionType();
396        assert(this->collisionType_ == type);
397
398        // update mass and inertia tensor
399        internalSetMassProps(); // type is not None! See case None in switch
400
401        addToPhysicalWorld();
402    }
403
404    void WorldEntity::setCollisionTypeStr(const std::string& typeStr)
405    {
406        std::string typeStrLower = getLowercase(typeStr);
407        CollisionType type;
408        if (typeStrLower == "dynamic")
409            type = Dynamic;
410        else if (typeStrLower == "static")
411            type = Static;
412        else if (typeStrLower == "kinematic")
413            type = Kinematic;
414        else if (typeStrLower == "none")
415            type = None;
416        else
417            ThrowException(ParseError, std::string("Attempting to set an unknown collision type: '") + typeStr + "'.");
418        this->setCollisionType(type);
419    }
420
421    std::string WorldEntity::getCollisionTypeStr() const
422    {
423        switch (this->getCollisionType())
424        {
425            case Dynamic:
426                return "dynamic";
427            case Kinematic:
428                return "kinematic";
429            case Static:
430                return "static";
431            case None:
432                return "none";
433            default:
434                assert(false);
435                return "";
436        }
437    }
438
439    void WorldEntity::updateCollisionType()
440    {
441        if (!this->physicalBody_)
442            this->collisionType_ = None;
443        else if (this->physicalBody_->isKinematicObject())
444            this->collisionType_ = Kinematic;
445        else if (this->physicalBody_->isStaticObject())
446            this->collisionType_ = Static;
447        else
448            this->collisionType_ = Dynamic;
449        this->collisionTypeSynchronised_ = this->collisionType_;
450    }
451
452    bool WorldEntity::isPhysicsRunning() const
453    {
454        return this->physicalBody_ && this->physicalBody_->isInWorld();
455    }
456
457    bool WorldEntity::checkPhysics() const
458    {
459        if (!this->physicalBody_)
460        {
461            assert(this->getCollisionType() == None);
462            COUT(2) << "WorldEntity was not fitted with physics, cannot set phyiscal property." << std::endl;
463            return false;
464        }
465        else
466            return true;
467    }
468}
Note: See TracBrowser for help on using the repository browser.