Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation/src/orxonox/objects/worldentities/WorldEntity.cc @ 2540

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

Added comments to the WorldEntity except for the non physics section of the header file (going to do that some time later).

  • Property svn:eol-style set to native
File size: 33.4 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
45namespace orxonox
46{
47    const Vector3 WorldEntity::FRONT = Vector3::NEGATIVE_UNIT_Z;
48    const Vector3 WorldEntity::BACK  = Vector3::UNIT_Z;
49    const Vector3 WorldEntity::LEFT  = Vector3::NEGATIVE_UNIT_X;
50    const Vector3 WorldEntity::RIGHT = Vector3::UNIT_X;
51    const Vector3 WorldEntity::DOWN  = Vector3::NEGATIVE_UNIT_Y;
52    const Vector3 WorldEntity::UP    = Vector3::UNIT_Y;
53
54    /**
55    @brief
56        Creates a new WorldEntity that may immediately be used.
57        All the default values are being set here.
58    */
59    WorldEntity::WorldEntity(BaseObject* creator) : BaseObject(creator), Synchronisable(creator), collisionShape_(0)
60    {
61        RegisterObject(WorldEntity);
62
63        if (!this->getScene() || !this->getScene()->getRootSceneNode())
64            ThrowException(AbortLoading, "Can't create WorldEntity, no scene or no root-scenenode given.");
65
66        this->node_ = this->getScene()->getRootSceneNode()->createChildSceneNode();
67
68        this->parent_ = 0;
69        this->parentID_ = OBJECTID_UNKNOWN;
70
71        this->node_->setPosition(Vector3::ZERO);
72        this->node_->setOrientation(Quaternion::IDENTITY);
73
74
75        // Default behaviour does not include physics
76        // Note: CompoundCollisionShape is a Synchronisable, but must not be synchronised.
77        //       All objects will get attached on the client anyway, so we don't need synchronisation.
78        this->collisionShape_.setWorldEntityParent(this);
79        this->physicalBody_   = 0;
80        this->bPhysicsActive_ = false;
81        this->bPhysicsActiveSynchronised_    = false;
82        this->bPhysicsActiveBeforeAttaching_ = false;
83        this->collisionType_             = None;
84        this->collisionTypeSynchronised_ = None;
85        this->mass_           = 0;
86        this->childrenMass_   = 0;
87        // Using bullet default values
88        this->restitution_    = 0;
89        this->angularFactor_  = 1;
90        this->linearDamping_  = 0;
91        this->angularDamping_ = 0;
92        this->friction_       = 0.5;
93        this->bCollisionCallbackActive_ = false;
94
95        this->registerVariables();
96    }
97
98    /**
99    @brief
100        Destroys the WorldEntity AND ALL its children with it.
101    */
102    WorldEntity::~WorldEntity()
103    {
104        if (this->isInitialized())
105        {
106            if (this->parent_)
107                this->detachFromParent();
108
109            for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); )
110                delete (*(it++));
111
112            if (this->physicalBody_)
113            {
114                this->deactivatePhysics();
115                delete this->physicalBody_;
116            }
117
118            this->node_->detachAllObjects();
119            this->node_->removeAllChildren();
120
121            OrxAssert(this->getScene()->getSceneManager(), "No SceneManager defined in a WorldEntity.");
122            this->getScene()->getSceneManager()->destroySceneNode(this->node_->getName());
123        }
124    }
125
126    void WorldEntity::XMLPort(Element& xmlelement, XMLPort::Mode mode)
127    {
128        SUPER(WorldEntity, XMLPort, xmlelement, mode);
129
130        XMLPortParamTemplate(WorldEntity, "position",    setPosition,    getPosition,    xmlelement, mode, const Vector3&);
131        XMLPortParamTemplate(WorldEntity, "orientation", setOrientation, getOrientation, xmlelement, mode, const Quaternion&);
132        XMLPortParamTemplate(WorldEntity, "scale3D",     setScale3D,     getScale3D,     xmlelement, mode, const Vector3&);
133        XMLPortParam        (WorldEntity, "scale",       setScale,       getScale,       xmlelement, mode);
134        XMLPortParamLoadOnly(WorldEntity, "lookat",      lookAt_xmlport,       xmlelement, mode);
135        XMLPortParamLoadOnly(WorldEntity, "direction",   setDirection_xmlport, xmlelement, mode);
136        XMLPortParamLoadOnly(WorldEntity, "yaw",         yaw_xmlport,          xmlelement, mode);
137        XMLPortParamLoadOnly(WorldEntity, "pitch",       pitch_xmlport,        xmlelement, mode);
138        XMLPortParamLoadOnly(WorldEntity, "roll",        roll_xmlport,         xmlelement, mode);
139
140        // Physics
141        XMLPortParam(WorldEntity, "collisionType",  setCollisionTypeStr, getCollisionTypeStr, xmlelement, mode);
142        XMLPortParam(WorldEntity, "mass",           setMass,             getMass,             xmlelement, mode);
143        XMLPortParam(WorldEntity, "restitution",    setRestitution,      getRestitution,      xmlelement, mode);
144        XMLPortParam(WorldEntity, "angularFactor",  setAngularFactor,    getAngularFactor,    xmlelement, mode);
145        XMLPortParam(WorldEntity, "linearDamping",  setLinearDamping,    getLinearDamping,    xmlelement, mode);
146        XMLPortParam(WorldEntity, "angularDamping", setAngularDamping,   getAngularDamping,   xmlelement, mode);
147        XMLPortParam(WorldEntity, "friction",       setFriction,         getFriction,         xmlelement, mode);
148
149        // Other attached WorldEntities
150        XMLPortObject(WorldEntity, WorldEntity, "attached", attach, getAttachedObject, xmlelement, mode);
151        // Attached collision shapes
152        XMLPortObject(WorldEntity, CollisionShape, "collisionShapes", attachCollisionShape, getAttachedCollisionShape, xmlelement, mode);
153    }
154
155    void WorldEntity::registerVariables()
156    {
157        registerVariable(this->mainStateName_,  variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::changedMainState));
158
159        registerVariable(this->bActive_,        variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::changedActivity));
160        registerVariable(this->bVisible_,       variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::changedVisibility));
161
162        registerVariable(this->getScale3D(),    variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::scaleChanged));
163
164        // Physics stuff
165        registerVariable(this->mass_,           variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::massChanged));
166        registerVariable(this->restitution_,    variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::restitutionChanged));
167        registerVariable(this->angularFactor_,  variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::angularFactorChanged));
168        registerVariable(this->linearDamping_,  variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::linearDampingChanged));
169        registerVariable(this->angularDamping_, variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::angularDampingChanged));
170        registerVariable(this->friction_,       variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::frictionChanged));
171        registerVariable(this->bCollisionCallbackActive_,
172                                                variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::collisionCallbackActivityChanged));
173        registerVariable((int&)this->collisionTypeSynchronised_,
174                                                variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::collisionTypeChanged));
175        registerVariable(this->bPhysicsActiveSynchronised_,
176                                                variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::physicsActivityChanged));
177
178        // Attach to parent if necessary
179        registerVariable(this->parentID_,       variableDirection::toclient, new NetworkCallback<WorldEntity>(this, &WorldEntity::parentChanged));
180    }
181
182    /**
183    @brief
184        Network function that object this instance to its correct parent.
185    */
186    void WorldEntity::parentChanged()
187    {
188        if (this->parentID_ != OBJECTID_UNKNOWN)
189        {
190            WorldEntity* parent = dynamic_cast<WorldEntity*>(Synchronisable::getSynchronisable(this->parentID_));
191            if (parent)
192                this->attachToParent(parent);
193        }
194    }
195
196    /**
197    @brief
198        Attaches this object to a parent SceneNode.
199    @Remarks
200        Only use this method if you know exactly what you're doing!
201        Normally, attaching works internally by attaching WE's.
202    */
203    void WorldEntity::attachToNode(Ogre::SceneNode* node)
204    {
205        Ogre::Node* parent = this->node_->getParent();
206        if (parent)
207            parent->removeChild(this->node_);
208        node->addChild(this->node_);
209    }
210
211    /**
212    @brief
213        Detaches this object from a parent SceneNode.
214    @Remarks
215        Only use this method if you know exactly what you're doing!
216        Normally, attaching works internally by attaching WE's.
217    */
218    void WorldEntity::detachFromNode(Ogre::SceneNode* node)
219    {
220        node->removeChild(this->node_);
221//        this->getScene()->getRootSceneNode()->addChild(this->node_);
222    }
223
224    /**
225    @brief
226        Network callback for the collision type. Only change the type if it was valid.
227    */
228    void WorldEntity::collisionTypeChanged()
229    {
230        if (this->collisionTypeSynchronised_ != Dynamic &&
231            this->collisionTypeSynchronised_ != Kinematic &&
232            this->collisionTypeSynchronised_ != Static &&
233            this->collisionTypeSynchronised_ != None)
234        {
235            CCOUT(1) << "Error when collsion Type was received over network. Unknown enum value:" << this->collisionTypeSynchronised_ << std::endl;
236        }
237        else if (this->collisionTypeSynchronised_ != collisionType_)
238        {
239            if (this->parent_)
240                CCOUT(2) << "Warning: Network connection tried to set the collision type of an attached WE. Ignoring." << std::endl;
241            else
242                this->setCollisionType(this->collisionTypeSynchronised_);
243        }
244    }
245
246    //! Network callback for this->bPhysicsActive_
247    void WorldEntity::physicsActivityChanged()
248    {
249        if (this->bPhysicsActiveSynchronised_)
250            this->activatePhysics();
251        else
252            this->deactivatePhysics();
253    }
254
255    //! Function is called to set the right flags in Bullet (if there is physics at all)
256    void WorldEntity::collisionCallbackActivityChanged()
257    {
258        if (this->hasPhysics())
259        {
260            if (bCollisionCallbackActive_)
261                this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() |
262                    btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
263            else
264                this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() &
265                    ~btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
266        }
267    }
268
269    /**
270    @brief
271        Attaches a child WorldEntity to this object. This calls notifyBeingAttached()
272        of the child WE.
273    @Note
274        The collision shape of the child object gets attached nevertheless. That also means
275        that you can change the collision shape of the child and it correctly cascadeds the changes to this instance.
276        Be aware of this implication: When implementing attaching of kinematic objects to others, you have to change
277        this behaviour because you then might not want to merge the collision shapes.
278    */
279    void WorldEntity::attach(WorldEntity* object)
280    {
281        if (object == this)
282        {
283            COUT(2) << "Warning: Can't attach a WorldEntity to itself." << std::endl;
284            return;
285        }
286
287        if (!object->notifyBeingAttached(this))
288            return;
289
290        this->attachNode(object->node_);
291        this->children_.insert(object);
292
293        this->attachCollisionShape(&object->collisionShape_);
294        // mass
295        this->childrenMass_ += object->getMass();
296        recalculateMassProps();
297    }
298
299    /**
300    @brief
301        Function gets called when this object is being attached to a new parent.
302
303        This operation is only allowed if the collision types "like" each other.
304        - You cannot a attach a non physical object to a physical one.
305        - Dynamic object can NOT be attached at all.
306        - It is also not possible to attach a kinematic to a dynamic one.
307        - Attaching of kinematic objects otherwise is not yet supported.
308    */
309    bool WorldEntity::notifyBeingAttached(WorldEntity* newParent)
310    {
311        // check first whether attaching is even allowed
312        if (this->hasPhysics())
313        {
314            if (!newParent->hasPhysics())
315            {
316                COUT(2) << "Warning: Cannot attach a physical object to a non physical one." << std::endl;
317                return false;
318            }
319            else if (this->isDynamic())
320            {
321                COUT(2) << "Warning: Cannot attach a dynamic object to a WorldEntity." << std::endl;
322                return false;
323            }
324            else if (this->isKinematic() && newParent->isDynamic())
325            {
326                COUT(2) << "Warning: Cannot attach a kinematic object to a dynamic one." << std::endl;
327                return false;
328            }
329            else if (this->isKinematic())
330            {
331                COUT(2) << "Warning: Cannot attach a kinematic object to a static or kinematic one: Not yet implemented." << std::endl;
332                return false;
333            }
334        }
335
336        if (this->isPhysicsActive())
337            this->bPhysicsActiveBeforeAttaching_ = true;
338        this->deactivatePhysics();
339
340        if (this->parent_)
341            this->detachFromParent();
342
343        this->parent_ = newParent;
344        this->parentID_ = newParent->getObjectID();
345
346        // apply transform to collision shape
347        this->collisionShape_.setPosition(this->getPosition());
348        this->collisionShape_.setOrientation(this->getOrientation());
349        // TODO: Scale
350       
351        return true;
352    }
353
354    /**
355    @brief
356        Detaches a child WorldEntity from this instance.
357    */
358    void WorldEntity::detach(WorldEntity* object)
359    {
360        if (this->children_.find(object) == this->children_.end())
361        {
362            CCOUT(2) << "Warning: Cannot detach an object that is not a child." << std::endl;
363            return;
364        }
365
366        // collision shapes
367        this->detachCollisionShape(&object->collisionShape_);
368
369        // mass
370        if (object->getMass() > 0.0f)
371        {
372            this->childrenMass_ -= object->getMass();
373            recalculateMassProps();
374        }
375
376        this->detachNode(object->node_);
377        this->children_.erase(object);
378
379        object->notifyDetached();
380    }
381
382    /**
383    @brief
384        Function gets called when the object has been detached from its parent.
385    */
386    void WorldEntity::notifyDetached()
387    {
388        this->parent_ = 0;
389        this->parentID_ = OBJECTID_UNKNOWN;
390
391        // reset orientation of the collisionShape (cannot be set within a WE usually)
392        this->collisionShape_.setPosition(Vector3::ZERO);
393        this->collisionShape_.setOrientation(Quaternion::IDENTITY);
394        // TODO: Scale
395
396        if (this->bPhysicsActiveBeforeAttaching_)
397        {
398            this->activatePhysics();
399            this->bPhysicsActiveBeforeAttaching_ = false;
400        }
401    }
402
403    //! Returns an attached object (merely for XMLPort).
404    WorldEntity* WorldEntity::getAttachedObject(unsigned int index)
405    {
406        unsigned int i = 0;
407        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
408        {
409            if (i == index)
410                return (*it);
411            ++i;
412        }
413        return 0;
414    }
415
416    //! Attaches an Ogre::SceneNode to this WorldEntity.
417    void WorldEntity::attachNode(Ogre::SceneNode* node)
418    {
419        Ogre::Node* parent = node->getParent();
420        if (parent)
421            parent->removeChild(node);
422        this->node_->addChild(node);
423    }
424
425    //! Detaches an Ogre::SceneNode from this WorldEntity.
426    void WorldEntity::detachNode(Ogre::SceneNode* node)
427    {
428        this->node_->removeChild(node);
429//        this->getScene()->getRootSceneNode()->addChild(node);
430    }
431
432    //! Attaches an Ogre::MovableObject to this WorldEntity.
433    void WorldEntity::attachOgreObject(Ogre::MovableObject* object)
434    {
435        this->node_->attachObject(object);
436    }
437
438    //! Detaches an Ogre::MovableObject from this WorldEntity.
439    void WorldEntity::detachOgreObject(Ogre::MovableObject* object)
440    {
441        this->node_->detachObject(object);
442    }
443
444    //! Detaches an Ogre::MovableObject (by string) from this WorldEntity.
445    Ogre::MovableObject* WorldEntity::detachOgreObject(const Ogre::String& name)
446    {
447        return this->node_->detachObject(name);
448    }
449
450    //! Attaches a collision Shape to this object (delegated to the internal CompoundCollisionShape)
451    void WorldEntity::attachCollisionShape(CollisionShape* shape)
452    {
453        this->collisionShape_.attach(shape);
454        // Note: this->collisionShape_ already notifies us of any changes.
455    }
456
457    //! Detaches a collision Shape from this object (delegated to the internal CompoundCollisionShape)
458    void WorldEntity::detachCollisionShape(CollisionShape* shape)
459    {
460        this->collisionShape_.detach(shape);
461        // Note: this->collisionShape_ already notifies us of any changes.
462    }
463
464    //! Returns an attached collision Shape of this object (delegated to the internal CompoundCollisionShape)
465    CollisionShape* WorldEntity::getAttachedCollisionShape(unsigned int index)
466    {
467        return this->collisionShape_.getAttachedShape(index);
468    }
469
470    // Note: These functions are placed in WorldEntity.h as inline functions for the release build.
471#ifndef _NDEBUG
472    const Vector3& WorldEntity::getPosition() const
473    {
474        return this->node_->getPosition();
475    }
476
477    const Quaternion& WorldEntity::getOrientation() const
478    {
479        return this->node_->getOrientation();
480    }
481
482    const Vector3& WorldEntity::getScale3D() const
483    {
484        return this->node_->getScale();
485    }
486#endif
487
488    //! Returns the position relative to the root space
489    const Vector3& WorldEntity::getWorldPosition() const
490    {
491        return this->node_->_getDerivedPosition();
492    }
493
494    //! Returns the orientation relative to the root space
495    const Quaternion& WorldEntity::getWorldOrientation() const
496    {
497        return this->node_->_getDerivedOrientation();
498    }
499
500    //! Returns the scaling applied relative to the root space in 3 coordinates
501    const Vector3& WorldEntity::getWorldScale3D() const
502    {
503        return this->node_->_getDerivedScale();
504    }
505
506    /**
507    @brief
508        Returns the scaling applied relative to the root space in 3 coordinates
509    @return
510        Returns the scaling if it is uniform, 1.0f otherwise.
511    */
512    float WorldEntity::getWorldScale() const
513    {
514        Vector3 scale = this->getWorldScale3D();
515        return (scale.x == scale.y && scale.x == scale.z) ? scale.x : 1;
516    }
517
518    /**
519    @brief
520        Sets the three dimensional scaling of this object.
521    @Note
522        Scaling physical objects has not yet been implemented and is therefore forbidden.
523    */
524    void WorldEntity::setScale3D(const Vector3& scale)
525    {
526/*
527HACK HACK HACK
528        if (bScalePhysics && this->hasPhysics() && scale != Vector3::UNIT_SCALE)
529        {
530            CCOUT(2) << "Warning: Cannot set the scale of a physical object: Not yet implemented. Ignoring scaling." << std::endl;
531            return;
532        }
533HACK HACK HACK
534*/
535        this->node_->setScale(scale);
536
537        this->changedScale();
538    }
539
540    /**
541    @brief
542        Translates this WorldEntity by a vector.
543    @param relativeTo
544        @see TransformSpace::Enum
545    */
546    void WorldEntity::translate(const Vector3& distance, TransformSpace::Enum relativeTo)
547    {
548        switch (relativeTo)
549        {
550        case TransformSpace::Local:
551            // position is relative to parent so transform downwards
552            this->setPosition(this->getPosition() + this->getOrientation() * distance);
553            break;
554        case TransformSpace::Parent:
555            this->setPosition(this->getPosition() + distance);
556            break;
557        case TransformSpace::World:
558            // position is relative to parent so transform upwards
559            if (this->node_->getParent())
560                setPosition(getPosition() + (node_->getParent()->_getDerivedOrientation().Inverse() * distance)
561                    / node_->getParent()->_getDerivedScale());
562            else
563                this->setPosition(this->getPosition() + distance);
564            break;
565        }
566    }
567
568    /**
569    @brief
570        Rotates this WorldEntity by a quaternion.
571    @param relativeTo
572        @see TransformSpace::Enum
573    */
574    void WorldEntity::rotate(const Quaternion& rotation, TransformSpace::Enum relativeTo)
575    {
576        switch(relativeTo)
577        {
578        case TransformSpace::Local:
579            this->setOrientation(this->getOrientation() * rotation);
580            break;
581        case TransformSpace::Parent:
582            // Rotations are normally relative to local axes, transform up
583            this->setOrientation(rotation * this->getOrientation());
584            break;
585        case TransformSpace::World:
586            // Rotations are normally relative to local axes, transform up
587            this->setOrientation(this->getOrientation() * this->getWorldOrientation().Inverse()
588                * rotation * this->getWorldOrientation());
589            break;
590        }
591    }
592
593    /**
594    @brief
595        Makes this WorldEntity look a specific target location.
596    @param relativeTo
597        @see TransformSpace::Enum
598    @param localDirectionVector
599        The vector which normally describes the natural direction of the object, usually -Z.
600    */
601    void WorldEntity::lookAt(const Vector3& target, TransformSpace::Enum relativeTo, const Vector3& localDirectionVector)
602    {
603        Vector3 origin;
604        switch (relativeTo)
605        {
606        case TransformSpace::Local:
607            origin = Vector3::ZERO;
608            break;
609        case TransformSpace::Parent:
610            origin = this->getPosition();
611            break;
612        case TransformSpace::World:
613            origin = this->getWorldPosition();
614            break;
615        }
616        this->setDirection(target - origin, relativeTo, localDirectionVector);
617    }
618
619    /**
620    @brief
621        Makes this WorldEntity look in specific direction.
622    @param relativeTo
623        @see TransformSpace::Enum
624    @param localDirectionVector
625        The vector which normally describes the natural direction of the object, usually -Z.
626    */
627    void WorldEntity::setDirection(const Vector3& direction, TransformSpace::Enum relativeTo, const Vector3& localDirectionVector)
628    {
629        Quaternion savedOrientation(this->getOrientation());
630        Ogre::Node::TransformSpace ogreRelativeTo;
631        switch (relativeTo)
632        {
633        case TransformSpace::Local:
634            ogreRelativeTo = Ogre::Node::TS_LOCAL; break;
635        case TransformSpace::Parent:
636            ogreRelativeTo = Ogre::Node::TS_PARENT; break;
637        case TransformSpace::World:
638            ogreRelativeTo = Ogre::Node::TS_WORLD; break;
639        }
640        this->node_->setDirection(direction, ogreRelativeTo, localDirectionVector);
641        Quaternion newOrientation(this->node_->getOrientation());
642        this->node_->setOrientation(savedOrientation);
643        this->setOrientation(newOrientation);
644    }
645
646    //! Activates physics if the CollisionType is not None.
647    void WorldEntity::activatePhysics()
648    {
649        if (this->isActive() && this->hasPhysics() && !this->isPhysicsActive() && !this->parent_)
650        {
651            this->getScene()->addPhysicalObject(this);
652            this->bPhysicsActive_ = true;
653        }
654    }
655
656    //! Deactivates physics but the CollisionType does not change.
657    void WorldEntity::deactivatePhysics()
658    {
659        if (this->isPhysicsActive())
660        {
661            this->getScene()->removePhysicalObject(this);
662            this->bPhysicsActive_ = false;
663        }
664    }
665
666    //! Tells whether the object has already been added to the Bullet physics World.
667    bool WorldEntity::addedToPhysicalWorld() const
668    {
669        return this->physicalBody_ && this->physicalBody_->isInWorld();
670    }
671
672    /**
673    @brief
674        Sets the CollisionType. This alters the object significantly! @see CollisionType.
675    @Note
676        Operation does not work on attached WorldEntities.
677    */
678    void WorldEntity::setCollisionType(CollisionType type)
679    {
680        if (this->collisionType_ == type)
681            return;
682
683        // If we are already attached to a parent, this would be a bad idea..
684        if (this->parent_)
685        {
686            CCOUT(2) << "Warning: Cannot set the collision type of a WorldEntity with a parent." << std::endl;
687            return;
688        }
689
690        // Check for type legality. Could be StaticEntity or MobileEntity.
691        if (!this->isCollisionTypeLegal(type))
692            return;
693        if (type != None && !this->getScene()->hasPhysics())
694        {
695            CCOUT(2) << "Warning: Cannot have physical bodies in a non physical scene." << std::endl;
696            return;
697        }
698
699        if (this->isPhysicsActive())
700            this->deactivatePhysics();
701
702        bool bReactivatePhysics = true;
703        if (this->hasPhysics() && !this->isPhysicsActive())
704            bReactivatePhysics = false;
705
706        // Check whether we have to create or destroy.
707        if (type != None && this->collisionType_ == None)
708        {
709/*
710HACK HACK HACK
711            // Check whether there was some scaling applied.
712            if (!this->node_->getScale().positionEquals(Vector3(1, 1, 1), 0.001))
713            {
714                CCOUT(2) << "Warning: Cannot create a physical body if there is scaling applied to the node: Not yet implemented." << std::endl;
715                return;
716            }
717HACK HACK HACK
718*/
719            // Create new rigid body
720            btRigidBody::btRigidBodyConstructionInfo bodyConstructionInfo(0, this, this->collisionShape_.getCollisionShape());
721            this->physicalBody_ = new btRigidBody(bodyConstructionInfo);
722            this->physicalBody_->setUserPointer(this);
723            this->physicalBody_->setActivationState(DISABLE_DEACTIVATION);
724        }
725        else if (type == None && this->collisionType_ != None)
726        {
727            // Destroy rigid body
728            assert(this->physicalBody_);
729            deactivatePhysics();
730            delete this->physicalBody_;
731            this->physicalBody_ = 0;
732            this->collisionType_ = None;
733            this->collisionTypeSynchronised_ = None;
734            return;
735        }
736
737        // Change type
738        switch (type)
739        {
740        case Dynamic:
741            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !(btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT));
742            break;
743        case Kinematic:
744            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT);
745            break;
746        case Static:
747            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
748            break;
749        case None:
750            assert(false); // Doesn't happen
751            return;
752        }
753
754        // Only sets this->collisionType_
755        // However the assertion is to ensure that the internal bullet setting is right
756        updateCollisionType();
757        assert(this->collisionType_ == type);
758
759        // update mass and inertia tensor
760        recalculateMassProps();
761        internalSetPhysicsProps();
762        collisionCallbackActivityChanged();
763        if (bReactivatePhysics)
764            activatePhysics();
765    }
766
767    //! Sets the CollisionType by string (used for the XMLPort)
768    void WorldEntity::setCollisionTypeStr(const std::string& typeStr)
769    {
770        std::string typeStrLower = getLowercase(typeStr);
771        CollisionType type;
772        if (typeStrLower == "dynamic")
773            type = Dynamic;
774        else if (typeStrLower == "static")
775            type = Static;
776        else if (typeStrLower == "kinematic")
777            type = Kinematic;
778        else if (typeStrLower == "none")
779            type = None;
780        else
781            ThrowException(ParseError, std::string("Attempting to set an unknown collision type: '") + typeStr + "'.");
782        this->setCollisionType(type);
783    }
784
785    //! Gets the CollisionType by string (used for the XMLPort)
786    std::string WorldEntity::getCollisionTypeStr() const
787    {
788        switch (this->getCollisionType())
789        {
790            case Dynamic:
791                return "dynamic";
792            case Kinematic:
793                return "kinematic";
794            case Static:
795                return "static";
796            case None:
797                return "none";
798            default:
799                assert(false);
800                return "";
801        }
802    }
803
804    void WorldEntity::updateCollisionType()
805    {
806        if (!this->physicalBody_)
807            this->collisionType_ = None;
808        else if (this->physicalBody_->isKinematicObject())
809            this->collisionType_ = Kinematic;
810        else if (this->physicalBody_->isStaticObject())
811            this->collisionType_ = Static;
812        else
813            this->collisionType_ = Dynamic;
814        this->collisionTypeSynchronised_ = this->collisionType_;
815    }
816
817    /**
818    @brief
819        Recalculates the accumulated child mass and calls recalculateMassProps()
820        and notifies the parent of the change.
821    @Note
822        Called by a child WE
823    */
824    void WorldEntity::notifyChildMassChanged()
825    {
826        // Note: CollisionShape changes of a child get handled over the internal CompoundCollisionShape already
827        // Recalculate mass
828        this->childrenMass_ = 0.0f;
829        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
830            this->childrenMass_ += (*it)->getMass();
831        recalculateMassProps();
832        // Notify parent WE
833        if (this->parent_)
834            parent_->notifyChildMassChanged();
835    }
836
837    /**
838    @brief
839        Undertakes the necessary steps to change the collision shape in Bullet, even at runtime.
840    @Note
841        - called by this->collisionShape_
842        - May have a REALLY big overhead when called continuously at runtime, because then we need
843          to remove the physical body from Bullet and add it again.
844    */
845    void WorldEntity::notifyCollisionShapeChanged()
846    {
847        if (hasPhysics())
848        {
849            // Bullet doesn't like sudden changes of the collision shape, so we remove and add it again
850            if (this->addedToPhysicalWorld())
851            {
852                this->deactivatePhysics();
853                this->physicalBody_->setCollisionShape(this->collisionShape_.getCollisionShape());
854                this->activatePhysics();
855            }
856            else
857                this->physicalBody_->setCollisionShape(this->collisionShape_.getCollisionShape());
858        }
859        recalculateMassProps();
860    }
861
862    //! Updates all mass dependent parameters (mass, inertia tensor and child mass)
863    void WorldEntity::recalculateMassProps()
864    {
865        // Store local inertia for faster access. Evaluates to (0,0,0) if there is no collision shape.
866        float totalMass = this->mass_ + this->childrenMass_;
867        this->collisionShape_.calculateLocalInertia(totalMass, this->localInertia_);
868        if (this->hasPhysics())
869        {
870            if (this->isStatic())
871            {
872                // Just set everything to zero
873                this->physicalBody_->setMassProps(0.0f, btVector3(0, 0, 0));
874            }
875            else if ((this->mass_ + this->childrenMass_) == 0.0f)
876            {
877                // Use default values to avoid very large or very small values
878                CCOUT(4) << "Warning: Setting the internal physical mass to 1.0 because mass_ is 0.0" << std::endl;
879                btVector3 inertia(0, 0, 0);
880                this->collisionShape_.calculateLocalInertia(1.0f, inertia);
881                this->physicalBody_->setMassProps(1.0f, inertia);
882            }
883            else
884            {
885                this->physicalBody_->setMassProps(totalMass, this->localInertia_);
886            }
887        }
888    }
889
890    //! Copies our own parameters for restitution, angular factor, dampings and friction to the bullet rigid body.
891    void WorldEntity::internalSetPhysicsProps()
892    {
893        if (this->hasPhysics())
894        {
895            this->physicalBody_->setRestitution(this->restitution_);
896            this->physicalBody_->setAngularFactor(this->angularFactor_);
897            this->physicalBody_->setDamping(this->linearDamping_, this->angularDamping_);
898            this->physicalBody_->setFriction(this->friction_);
899        }
900    }
901}
Note: See TracBrowser for help on using the repository browser.