Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10274 was 10274, checked in by landauf, 9 years ago

fixed #419 http://www.orxonox.net/ticket/419
physics is only activated if a WorldEntity is active. so we should toggle physics if activity changes.

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