Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2429 was 2429, checked in by rgrieder, 15 years ago

Added XML Parameters for restitution, angular factor, linear damping, angular damping and friction to WorldEntity.

  • Property svn:eol-style set to native
File size: 23.6 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 <OgreSceneNode.h>
35#include <OgreSceneManager.h>
36#include "BulletDynamics/Dynamics/btRigidBody.h"
37
38#include "util/Exception.h"
39#include "util/Convert.h"
40#include "core/CoreIncludes.h"
41#include "core/XMLPort.h"
42
43#include "objects/Scene.h"
44#include "objects/collisionshapes/CompoundCollisionShape.h"
45
46namespace orxonox
47{
48    const Vector3 WorldEntity::FRONT = Vector3::NEGATIVE_UNIT_Z;
49    const Vector3 WorldEntity::BACK  = Vector3::UNIT_Z;
50    const Vector3 WorldEntity::LEFT  = Vector3::NEGATIVE_UNIT_X;
51    const Vector3 WorldEntity::RIGHT = Vector3::UNIT_X;
52    const Vector3 WorldEntity::DOWN  = Vector3::NEGATIVE_UNIT_Y;
53    const Vector3 WorldEntity::UP    = Vector3::UNIT_Y;
54
55    WorldEntity::WorldEntity(BaseObject* creator) : BaseObject(creator), network::Synchronisable(creator)
56    {
57        RegisterObject(WorldEntity);
58
59        assert(this->getScene());
60        assert(this->getScene()->getRootSceneNode());
61
62        this->node_ = this->getScene()->getRootSceneNode()->createChildSceneNode();
63
64        this->parent_ = 0;
65        this->parentID_ = (unsigned int)-1;
66
67        this->node_->setPosition(Vector3::ZERO);
68        this->node_->setOrientation(Quaternion::IDENTITY);
69
70        // Default behaviour does not include physics
71        this->physicalBody_ = 0;
72        this->bPhysicsActive_ = false;
73        this->collisionShape_ = new CompoundCollisionShape(this);
74        this->collisionType_ = None;
75        this->collisionTypeSynchronised_ = None;
76        this->mass_           = 0;
77        this->childrenMass_   = 0;
78        // Use bullet default values
79        this->restitution_    = 0;
80        this->angularFactor_  = 1;
81        this->linearDamping_  = 0;
82        this->angularDamping_ = 0;
83        this->friction_       = 0.5;
84
85        this->registerVariables();
86    }
87
88    WorldEntity::~WorldEntity()
89    {
90        if (this->isInitialized())
91        {
92            this->node_->detachAllObjects();
93            if (this->getScene()->getSceneManager())
94                this->getScene()->getSceneManager()->destroySceneNode(this->node_->getName());
95
96            // TODO: Detach from parent and detach all children.
97
98            if (this->physicalBody_)
99            {
100                this->deactivatePhysics();
101                delete this->physicalBody_;
102            }
103            delete this->collisionShape_;
104        }
105    }
106
107    void WorldEntity::XMLPort(Element& xmlelement, XMLPort::Mode mode)
108    {
109        SUPER(WorldEntity, XMLPort, xmlelement, mode);
110
111        XMLPortParamTemplate(WorldEntity, "position",    setPosition,    getPosition,    xmlelement, mode, const Vector3&);
112        XMLPortParamTemplate(WorldEntity, "orientation", setOrientation, getOrientation, xmlelement, mode, const Quaternion&);
113        XMLPortParamTemplate(WorldEntity, "scale3D",     setScale3D,     getScale3D,     xmlelement, mode, const Vector3&);
114        XMLPortParam        (WorldEntity, "scale",       setScale,       getScale,       xmlelement, mode);
115        XMLPortParamLoadOnly(WorldEntity, "lookat",      lookAt_xmlport,       xmlelement, mode);
116        XMLPortParamLoadOnly(WorldEntity, "direction",   setDirection_xmlport, xmlelement, mode);
117        XMLPortParamLoadOnly(WorldEntity, "yaw",         yaw_xmlport,          xmlelement, mode);
118        XMLPortParamLoadOnly(WorldEntity, "pitch",       pitch_xmlport,        xmlelement, mode);
119        XMLPortParamLoadOnly(WorldEntity, "roll",        roll_xmlport,         xmlelement, mode);
120
121        // Physics
122        XMLPortParam(WorldEntity, "collisionType",  setCollisionTypeStr, getCollisionTypeStr, xmlelement, mode);
123        XMLPortParam(WorldEntity, "mass",           setMass,             getMass,             xmlelement, mode);
124        XMLPortParam(WorldEntity, "restitution",    setRestitution,      getRestitution,      xmlelement, mode);
125        XMLPortParam(WorldEntity, "angularFactor",  setAngularFactor,    getAngularFactor,    xmlelement, mode);
126        XMLPortParam(WorldEntity, "linearDamping",  setLinearDamping,    getLinearDamping,    xmlelement, mode);
127        XMLPortParam(WorldEntity, "angularDamping", setAngularDamping,   getAngularDamping,   xmlelement, mode);
128        XMLPortParam(WorldEntity, "friction",       setFriction,         getFriction,         xmlelement, mode);
129
130        // Other attached WorldEntities
131        XMLPortObject(WorldEntity, WorldEntity, "attached", attach, getAttachedObject, xmlelement, mode);
132        // Attached collision shapes
133        XMLPortObject(WorldEntity, CollisionShape, "collisionShapes", attachCollisionShape, getAttachedCollisionShape, xmlelement, mode);
134    }
135
136    void WorldEntity::registerVariables()
137    {
138        REGISTERDATA(this->bActive_,        network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedActivity));
139        REGISTERDATA(this->bVisible_,       network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedVisibility));
140
141        REGISTERDATA(this->getScale3D(),    network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::scaleChanged));
142
143        REGISTERDATA((int&)this->collisionTypeSynchronised_,
144                                            network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::collisionTypeChanged));
145        REGISTERDATA(this->mass_,           network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::massChanged));
146        REGISTERDATA(this->restitution_,    network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::restitutionChanged));
147        REGISTERDATA(this->angularFactor_,  network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::angularFactorChanged));
148        REGISTERDATA(this->linearDamping_,  network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::linearDampingChanged));
149        REGISTERDATA(this->angularDamping_, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::angularDampingChanged));
150        REGISTERDATA(this->friction_,       network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::frictionChanged));
151        REGISTERDATA(this->bPhysicsActiveSynchronised_,
152                                            network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::physicsActivityChanged));
153
154        REGISTERDATA(this->parentID_,       network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::parentChanged));
155    }
156
157    void WorldEntity::parentChanged()
158    {
159        WorldEntity* parent = dynamic_cast<WorldEntity*>(Synchronisable::getSynchronisable(this->parentID_));
160        if (parent)
161            this->attachToParent(parent);
162    }
163
164    void WorldEntity::collisionTypeChanged()
165    {
166        if (this->collisionTypeSynchronised_ != Dynamic &&
167            this->collisionTypeSynchronised_ != Kinematic &&
168            this->collisionTypeSynchronised_ != Static &&
169            this->collisionTypeSynchronised_ != None)
170        {
171            CCOUT(1) << "Error when collsion Type was received over network. Unknown enum value:" << this->collisionTypeSynchronised_ << std::endl;
172        }
173        else if (this->collisionTypeSynchronised_ != collisionType_)
174        {
175            if (this->parent_)
176                CCOUT(2) << "Warning: Network connection tried to set the collision type of an attached WE. Ignoring." << std::endl;
177            else
178                this->setCollisionType(this->collisionTypeSynchronised_);
179        }
180    }
181
182    void WorldEntity::physicsActivityChanged()
183    {
184        if (this->bPhysicsActiveSynchronised_)
185            this->activatePhysics();
186        else
187            this->deactivatePhysics();
188    }
189
190    void WorldEntity::attach(WorldEntity* object)
191    {
192        // check first whether attaching is even allowed
193        if (object->hasPhysics())
194        {
195            if (!this->hasPhysics())
196                ThrowException(PhysicsViolation, "Cannot attach a physical object to a non physical one.");
197            else if (object->isDynamic())
198                ThrowException(PhysicsViolation, "Cannot attach a dynamic object to a WorldEntity.");
199            else if (object->isKinematic() && this->isDynamic())
200                ThrowException(PhysicsViolation, "Cannot attach a kinematic object to a dynamic one.");
201            else if (object->isKinematic())
202                ThrowException(NotImplemented, "Cannot attach a kinematic object to a static or kinematic one: Not yet implemented.");
203            else
204            {
205                object->deactivatePhysics();
206            }
207        }
208
209        if (object->getParent())
210            object->detachFromParent();
211        else
212        {
213            Ogre::Node* parent = object->node_->getParent();
214            if (parent)
215                parent->removeChild(object->node_);
216        }
217
218        this->node_->addChild(object->node_);
219        this->children_.insert(object);
220        object->parent_ = this;
221        object->parentID_ = this->getObjectID();
222
223        // collision shapes
224        this->attachCollisionShape(object->getCollisionShape());
225        // mass
226        this->childrenMass_ += object->getMass();
227        recalculateMassProps();
228    }
229
230    void WorldEntity::detach(WorldEntity* object)
231    {
232        // collision shapes
233        this->detachCollisionShape(object->getCollisionShape());
234        // mass
235        if (object->getMass() > 0.0f)
236        {
237            this->childrenMass_ -= object->getMass();
238            recalculateMassProps();
239        }
240
241        this->node_->removeChild(object->node_);
242        this->children_.erase(object);
243        object->parent_ = 0;
244        object->parentID_ = (unsigned int)-1;
245//        this->getScene()->getRootSceneNode()->addChild(object->node_);
246
247        // Note: It is possible that the object has physics but was disabled when attaching
248        object->activatePhysics();
249    }
250
251    WorldEntity* WorldEntity::getAttachedObject(unsigned int index) const
252    {
253        unsigned int i = 0;
254        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
255        {
256            if (i == index)
257                return (*it);
258            ++i;
259        }
260        return 0;
261    }
262
263    void WorldEntity::attachOgreObject(Ogre::MovableObject* object)
264    {
265        this->node_->attachObject(object);
266    }
267
268    void WorldEntity::detachOgreObject(Ogre::MovableObject* object)
269    {
270        this->node_->detachObject(object);
271    }
272
273    Ogre::MovableObject* WorldEntity::detachOgreObject(const Ogre::String& name)
274    {
275        return this->node_->detachObject(name);
276    }
277
278    void WorldEntity::attachCollisionShape(CollisionShape* shape)
279    {
280        this->collisionShape_->addChildShape(shape);
281        // Note: this->collisionShape_ already notifies us of any changes.
282    }
283
284    void WorldEntity::detachCollisionShape(CollisionShape* shape)
285    {
286        this->collisionShape_->removeChildShape(shape);
287        // Note: this->collisionShape_ already notifies us of any changes.
288    }
289
290    CollisionShape* WorldEntity::getAttachedCollisionShape(unsigned int index) const
291    {
292        return this->collisionShape_->getChildShape(index);
293    }
294
295    void WorldEntity::activatePhysics()
296    {
297        if (this->isActive() && this->hasPhysics() && !this->isPhysicsActive() && !this->parent_)
298        {
299            this->getScene()->addRigidBody(this->physicalBody_);
300            this->bPhysicsActive_ = true;
301        }
302    }
303
304    void WorldEntity::deactivatePhysics()
305    {
306        if (this->isPhysicsActive())
307        {
308            this->getScene()->removeRigidBody(this->physicalBody_);
309            this->bPhysicsActive_ = false;
310        }
311    }
312
313    bool WorldEntity::addedToPhysicalWorld() const
314    {
315        return this->physicalBody_ && this->physicalBody_->isInWorld();
316    }
317
318#ifndef _NDEBUG
319    const Vector3& WorldEntity::getPosition() const
320    {
321        return this->node_->getPosition();
322    }
323
324    const Quaternion& WorldEntity::getOrientation() const
325    {
326        return this->node_->getOrientation();
327    }
328
329    const Vector3& WorldEntity::getScale3D() const
330    {
331        return this->node_->getScale();
332    }
333#endif
334
335    const Vector3& WorldEntity::getWorldPosition() const
336    {
337        return this->node_->_getDerivedPosition();
338    }
339
340    const Quaternion& WorldEntity::getWorldOrientation() const
341    {
342        return this->node_->_getDerivedOrientation();
343    }
344
345    void WorldEntity::translate(const Vector3& distance, TransformSpace::Space relativeTo)
346    {
347        switch (relativeTo)
348        {
349        case TransformSpace::Local:
350            // position is relative to parent so transform downwards
351            this->setPosition(this->getPosition() + this->getOrientation() * distance);
352            break;
353        case TransformSpace::Parent:
354            this->setPosition(this->getPosition() + distance);
355            break;
356        case TransformSpace::World:
357            // position is relative to parent so transform upwards
358            if (this->node_->getParent())
359                setPosition(getPosition() + (node_->getParent()->_getDerivedOrientation().Inverse() * distance)
360                    / node_->getParent()->_getDerivedScale());
361            else
362                this->setPosition(this->getPosition() + distance);
363            break;
364        }
365    }
366
367    void WorldEntity::rotate(const Quaternion& rotation, TransformSpace::Space relativeTo)
368    {
369        switch(relativeTo)
370        {
371        case TransformSpace::Local:
372            this->setOrientation(this->getOrientation() * rotation);
373            break;
374        case TransformSpace::Parent:
375            // Rotations are normally relative to local axes, transform up
376            this->setOrientation(rotation * this->getOrientation());
377            break;
378        case TransformSpace::World:
379            // Rotations are normally relative to local axes, transform up
380            this->setOrientation(this->getOrientation() * this->getWorldOrientation().Inverse()
381                * rotation * this->getWorldOrientation());
382            break;
383        }
384    }
385
386    void WorldEntity::lookAt(const Vector3& target, TransformSpace::Space relativeTo, const Vector3& localDirectionVector)
387    {
388        Vector3 origin;
389        switch (relativeTo)
390        {
391        case TransformSpace::Local:
392            origin = Vector3::ZERO;
393            break;
394        case TransformSpace::Parent:
395            origin = this->getPosition();
396            break;
397        case TransformSpace::World:
398            origin = this->getWorldPosition();
399            break;
400        }
401        this->setDirection(target - origin, relativeTo, localDirectionVector);
402    }
403
404    void WorldEntity::setDirection(const Vector3& direction, TransformSpace::Space relativeTo, const Vector3& localDirectionVector)
405    {
406        Quaternion savedOrientation(this->getOrientation());
407        Ogre::Node::TransformSpace ogreRelativeTo;
408        switch (relativeTo)
409        {
410        case TransformSpace::Local:
411            ogreRelativeTo = Ogre::Node::TS_LOCAL; break;
412        case TransformSpace::Parent:
413            ogreRelativeTo = Ogre::Node::TS_PARENT; break;
414        case TransformSpace::World:
415            ogreRelativeTo = Ogre::Node::TS_WORLD; break;
416        }
417        this->node_->setDirection(direction, ogreRelativeTo, localDirectionVector);
418        Quaternion newOrientation(this->node_->getOrientation());
419        this->node_->setOrientation(savedOrientation);
420        this->setOrientation(newOrientation);
421    }
422
423    void WorldEntity::setScale3D(const Vector3& scale)
424    {
425        if (this->hasPhysics())
426            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
427
428        this->node_->setScale(scale);
429    }
430
431    void WorldEntity::setCollisionType(CollisionType type)
432    {
433        // If we are already attached to a parent, this would be a bad idea..
434        if (this->parent_)
435            ThrowException(PhysicsViolation, "Cannot set the collision type of a WorldEntity with a parent");
436        else if (this->addedToPhysicalWorld())
437            ThrowException(PhysicsViolation, "Cannot set the collision type at run time.");
438
439        // Check for type legality. Could be StaticEntity or MobileEntity
440        if (!this->isCollisionTypeLegal(type))
441            return; // exception gets issued anyway
442        if (type != None && !this->getScene()->hasPhysics())
443            ThrowException(PhysicsViolation, "Cannot have physical bodies in a non physical scene");
444
445        // Check whether we have to create or destroy.
446        if (type != None && this->collisionType_ == None)
447        {
448            // Check whether there was some scaling applied.
449            if (!this->node_->getScale().positionEquals(Vector3(1, 1, 1), 0.001))
450                ThrowException(NotImplemented, "Cannot create a physical body if there is scaling applied to the node: Not yet implemented.");
451
452            // Create new rigid body
453            btRigidBody::btRigidBodyConstructionInfo bodyConstructionInfo(0, this, this->collisionShape_->getCollisionShape());
454            this->physicalBody_ = new btRigidBody(bodyConstructionInfo);
455            this->physicalBody_->setUserPointer(this);
456            this->physicalBody_->setActivationState(DISABLE_DEACTIVATION);
457        }
458        else if (type == None && this->collisionType_ != None)
459        {
460            // Destroy rigid body
461            assert(this->physicalBody_);
462            deactivatePhysics();
463            delete this->physicalBody_;
464            this->physicalBody_ = 0;
465            this->collisionType_ = None;
466            this->collisionTypeSynchronised_ = None;
467            return;
468        }
469
470        // Change type
471        switch (type)
472        {
473        case Dynamic:
474            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !(btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT));
475            break;
476        case Kinematic:
477            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT);
478            break;
479        case Static:
480            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
481            break;
482        case None:
483            return; // this->collisionType_ was None too
484        }
485
486        // Only sets this->collisionShape_
487        // However the assertion is to ensure that the internal bullet setting is right
488        updateCollisionType();
489        assert(this->collisionType_ == type);
490
491        // update mass and inertia tensor
492        recalculateMassProps();
493        resetPhysicsProps();
494        activatePhysics();
495    }
496
497    void WorldEntity::setCollisionTypeStr(const std::string& typeStr)
498    {
499        std::string typeStrLower = getLowercase(typeStr);
500        CollisionType type;
501        if (typeStrLower == "dynamic")
502            type = Dynamic;
503        else if (typeStrLower == "static")
504            type = Static;
505        else if (typeStrLower == "kinematic")
506            type = Kinematic;
507        else if (typeStrLower == "none")
508            type = None;
509        else
510            ThrowException(ParseError, std::string("Attempting to set an unknown collision type: '") + typeStr + "'.");
511        this->setCollisionType(type);
512    }
513
514    std::string WorldEntity::getCollisionTypeStr() const
515    {
516        switch (this->getCollisionType())
517        {
518            case Dynamic:
519                return "dynamic";
520            case Kinematic:
521                return "kinematic";
522            case Static:
523                return "static";
524            case None:
525                return "none";
526            default:
527                assert(false);
528                return "";
529        }
530    }
531
532    void WorldEntity::updateCollisionType()
533    {
534        if (!this->physicalBody_)
535            this->collisionType_ = None;
536        else if (this->physicalBody_->isKinematicObject())
537            this->collisionType_ = Kinematic;
538        else if (this->physicalBody_->isStaticObject())
539            this->collisionType_ = Static;
540        else
541            this->collisionType_ = Dynamic;
542        this->collisionTypeSynchronised_ = this->collisionType_;
543    }
544
545    void WorldEntity::notifyChildMassChanged() // Called by a child WE
546    {
547        // Note: CollisionShape changes of a child get handled over the internal CompoundCollisionShape already
548        // Recalculate mass
549        this->childrenMass_ = 0.0f;
550        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
551            this->childrenMass_ += (*it)->getMass();
552        recalculateMassProps();
553        // Notify parent WE
554        if (this->parent_)
555            parent_->notifyChildMassChanged();
556    }
557
558    void WorldEntity::notifyCollisionShapeChanged() // called by this->collisionShape_
559    {
560        if (hasPhysics())
561        {
562            // Bullet doesn't like sudden changes of the collision shape, so we remove and add it again
563            if (this->addedToPhysicalWorld())
564            {
565                this->deactivatePhysics();
566                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
567                this->activatePhysics();
568            }
569            else
570                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
571        }
572        recalculateMassProps();
573    }
574
575    void WorldEntity::recalculateMassProps()
576    {
577        if (this->hasPhysics())
578        {
579            if (this->isStatic())
580            {
581                // Just set everything to zero
582                this->physicalBody_->setMassProps(0.0f, btVector3(0, 0, 0));
583            }
584            else if ((this->mass_ + this->childrenMass_) == 0.0f)
585            {
586                // Use default values to avoid very large or very small values
587                CCOUT(4) << "Warning: Setting the internal physical mass to 1.0 because mass_ is 0.0." << std::endl;
588                this->physicalBody_->setMassProps(1.0f, this->collisionShape_->getLocalInertia(1.0f));
589            }
590            else
591            {
592                float mass = this->mass_ + this->childrenMass_;
593                this->physicalBody_->setMassProps(mass, this->collisionShape_->getLocalInertia(mass));
594            }
595        }
596    }
597
598    void WorldEntity::resetPhysicsProps()
599    {
600        if (this->hasPhysics())
601        {
602            this->physicalBody_->setRestitution(this->restitution_);
603            this->physicalBody_->setAngularFactor(this->angularFactor_);
604            this->physicalBody_->setDamping(this->linearDamping_, this->angularDamping_);
605            this->physicalBody_->setFriction(this->friction_);
606        }
607    }
608}
Note: See TracBrowser for help on using the repository browser.