Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/modularships/src/orxonox/worldentities/WorldEntity.cc @ 10033

Last change on this file since 10033 was 10033, checked in by noep, 11 years ago

Tried to find&solve the segfault. The detach-process itself is not the problem, but the collision on a deleted shape being handled. Maybe try using a smart pointer in the handling process, so that the shape only gets deleted when the process completed?

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