Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/objects/worldentities/WorldEntity.cc @ 3077

Last change on this file since 3077 was 3077, checked in by landauf, 15 years ago

Added Attacher - a class which attaches itself to an object with a given name

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