Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

merged branch core7 back to trunk

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