Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7910 was 7910, checked in by landauf, 13 years ago

Ok, here comes a tricky one: we knew the destructor of WorldEntity is dangerous because it deletes its children - this works only if no child deletes the subsequent child in the set because then the iterator would become invalid. However this is never the case, right?

Wrong! If you define a CameraPosition with "absolute" flag, it will not be attached to a ControllableEntity but to its parent! The CE however still deletes the CameraPosition. This led to very rare and random crashes while destroying the Pong level (the only level where an absolute camera is used).

  • fixed this issue by using a safe destruction loop
  • fixed another issue in the same loop if a child has a smart pointer pointing to it, thus not being destroyed, hence it has to be detached
  • performance improvement in detach()

Thanks to Jo for sending me the crash log!

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