Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Fixed yet another segfault (which we hadn't seen yet).
Cleared emptyLevel, created two testlevels (testing with boxes)

  • Property svn:eol-style set to native
File size: 37.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#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        this->collisionShape_->detach(shape);
583        // Note: this->collisionShape_ already notifies us of any changes.
584    }
585
586    //! Returns an attached collision Shape of this object (delegated to the internal CompoundCollisionShape)
587    CollisionShape* WorldEntity::getAttachedCollisionShape(unsigned int index)
588    {
589        return this->collisionShape_->getAttachedShape(index);
590    }
591
592    // Note: These functions are placed in WorldEntity.h as inline functions for the release build.
593#ifndef ORXONOX_RELEASE
594    const Vector3& WorldEntity::getPosition() const
595    {
596        return this->node_->getPosition();
597    }
598
599    const Quaternion& WorldEntity::getOrientation() const
600    {
601        return this->node_->getOrientation();
602    }
603
604    const Vector3& WorldEntity::getScale3D() const
605    {
606        return this->node_->getScale();
607    }
608#endif
609
610    //! Returns the position relative to the root space
611    const Vector3& WorldEntity::getWorldPosition() const
612    {
613        return this->node_->_getDerivedPosition();
614    }
615
616    //! Returns the orientation relative to the root space
617    const Quaternion& WorldEntity::getWorldOrientation() const
618    {
619        return this->node_->_getDerivedOrientation();
620    }
621
622    //! Returns the scaling applied relative to the root space in 3 coordinates
623    const Vector3& WorldEntity::getWorldScale3D() const
624    {
625        return this->node_->_getDerivedScale();
626    }
627
628    /**
629    @brief
630        Returns the scaling applied relative to the root space in 3 coordinates
631    @return
632        Returns the scaling if it is uniform, 1.0f otherwise.
633    */
634    float WorldEntity::getWorldScale() const
635    {
636        Vector3 scale = this->getWorldScale3D();
637        return (scale.x == scale.y && scale.x == scale.z) ? scale.x : 1;
638    }
639
640    /**
641    @brief
642        Sets the three dimensional scaling of this object.
643    @note
644        Scaling physical objects has not yet been implemented and is therefore forbidden.
645    */
646    void WorldEntity::setScale3D(const Vector3& scale)
647    {
648        // If physics is enabled scale the attached CollisionShape.
649        /*if (this->hasPhysics() && this->collisionShape_ != NULL)
650        {
651            this->collisionShape_->setScale3D(scale);
652        }*/
653
654        this->node_->setScale(scale);
655
656        this->changedScale();
657    }
658
659    /**
660    @brief
661        Translates this WorldEntity by a vector.
662    @param distance
663        The relative distance of the translation
664    @param relativeTo
665        The TransformSpace of this translation
666    */
667    void WorldEntity::translate(const Vector3& distance, TransformSpace relativeTo)
668    {
669        switch (relativeTo)
670        {
671        case WorldEntity::Local:
672            // position is relative to parent so transform downwards
673            this->setPosition(this->getPosition() + this->getOrientation() * distance);
674            break;
675        case WorldEntity::Parent:
676            this->setPosition(this->getPosition() + distance);
677            break;
678        case WorldEntity::World:
679            // position is relative to parent so transform upwards
680            if (this->node_->getParent())
681                setPosition(getPosition() + (node_->getParent()->_getDerivedOrientation().Inverse() * distance)
682                    / node_->getParent()->_getDerivedScale());
683            else
684                this->setPosition(this->getPosition() + distance);
685            break;
686        }
687    }
688
689    /**
690    @brief
691        Rotates this WorldEntity by a quaternion.
692    @param rotation
693        The desired relative rotation
694    @param relativeTo
695        The TransformSpace of this translation
696    */
697    void WorldEntity::rotate(const Quaternion& rotation, TransformSpace relativeTo)
698    {
699        switch(relativeTo)
700        {
701        case WorldEntity::Local:
702            this->setOrientation(this->getOrientation() * rotation);
703            break;
704        case WorldEntity::Parent:
705            // Rotations are normally relative to local axes, transform up
706            this->setOrientation(rotation * this->getOrientation());
707            break;
708        case WorldEntity::World:
709            // Rotations are normally relative to local axes, transform up
710            this->setOrientation(this->getOrientation() * this->getWorldOrientation().Inverse()
711                * rotation * this->getWorldOrientation());
712            break;
713        }
714    }
715
716    /**
717    @brief
718        Makes this WorldEntity look at a specific target location.
719    @param target
720        An absolute point in the space which defines the direction of the entity
721    @param relativeTo
722        The TransformSpace of this translation
723    @param localDirectionVector
724        The vector which normally describes the natural direction of the object, usually -Z.
725    */
726    void WorldEntity::lookAt(const Vector3& target, TransformSpace relativeTo, const Vector3& localDirectionVector)
727    {
728        Vector3 origin(0, 0, 0);
729        switch (relativeTo)
730        {
731        case WorldEntity::Local:
732            origin = Vector3::ZERO;
733            break;
734        case WorldEntity::Parent:
735            origin = this->getPosition();
736            break;
737        case WorldEntity::World:
738            origin = this->getWorldPosition();
739            break;
740        }
741        this->setDirection(target - origin, relativeTo, localDirectionVector);
742    }
743
744    /**
745    @brief
746        Makes this WorldEntity look in specific direction.
747    @param direction
748        A point relative to the position of the WorldEntity which defines its orientation
749    @param relativeTo
750        The TransformSpace of this translation
751    @param localDirectionVector
752        The vector which normally describes the natural direction of the object, usually -Z.
753    */
754    void WorldEntity::setDirection(const Vector3& direction, TransformSpace relativeTo, const Vector3& localDirectionVector)
755    {
756        Quaternion savedOrientation(this->getOrientation());
757        this->node_->setDirection(direction, static_cast<Ogre::Node::TransformSpace>(relativeTo), localDirectionVector);
758        Quaternion newOrientation(this->node_->getOrientation());
759        this->node_->setOrientation(savedOrientation);
760        this->setOrientation(newOrientation);
761    }
762
763    //! Activates physics if the CollisionType is not None.
764    void WorldEntity::activatePhysics()
765    {
766        if (this->isActive() && this->hasPhysics() && !this->isPhysicsActive() && !this->parent_)
767        {
768            this->getScene()->addPhysicalObject(this);
769            this->bPhysicsActive_ = true;
770            this->bPhysicsActiveSynchronised_ = true;
771        }
772    }
773
774    //! Deactivates physics but the CollisionType does not change.
775    void WorldEntity::deactivatePhysics()
776    {
777        if (this->isPhysicsActive())
778        {
779            this->getScene()->removePhysicalObject(this);
780            this->bPhysicsActive_ = false;
781            this->bPhysicsActiveSynchronised_ = false;
782        }
783    }
784
785    //! Tells whether the object has already been added to the Bullet physics World.
786    bool WorldEntity::addedToPhysicalWorld() const
787    {
788        return this->physicalBody_ && this->physicalBody_->isInWorld();
789    }
790
791    /**
792    @brief
793        Sets the CollisionType. This alters the object significantly!
794    @note
795        Operation does not work on attached WorldEntities.
796    */
797    void WorldEntity::setCollisionType(CollisionType type)
798    {
799        if (this->collisionType_ == type)
800            return;
801
802        // If we are already attached to a parent, this would be a bad idea..
803        if (this->parent_)
804        {
805            orxout(internal_warning) << "Cannot set the collision type of a WorldEntity with a parent." << endl;
806            return;
807        }
808
809        // Check for type legality. Could be StaticEntity or MobileEntity.
810        if (!this->isCollisionTypeLegal(type))
811            return;
812
813        if (this->isPhysicsActive())
814            this->deactivatePhysics();
815
816        bool bReactivatePhysics = true;
817        if (this->hasPhysics() && !this->isPhysicsActive())
818            bReactivatePhysics = false;
819
820        // Check whether we have to create or destroy.
821        if (type != None && this->collisionType_ == None)
822        {
823/*
824HACK HACK HACK
825            // Check whether there was some scaling applied.
826            if (!this->node_->getScale().positionEquals(Vector3(1, 1, 1), 0.001))
827            {
828                orxout(internal_warning) << "Cannot create a physical body if there is scaling applied to the node: Not yet implemented." << endl;
829                return;
830            }
831HACK HACK HACK
832*/
833            // Create new rigid body
834            btRigidBody::btRigidBodyConstructionInfo bodyConstructionInfo(0, this, this->collisionShape_->getCollisionShape());
835            this->physicalBody_ = new btRigidBody(bodyConstructionInfo);
836            this->physicalBody_->setUserPointer(this);
837            this->physicalBody_->setActivationState(DISABLE_DEACTIVATION);
838        }
839        else if (type == None && this->collisionType_ != None)
840        {
841            // Destroy rigid body
842            assert(this->physicalBody_);
843            deactivatePhysics();
844            delete this->physicalBody_;
845            this->physicalBody_ = 0;
846            this->collisionType_ = None;
847            this->collisionTypeSynchronised_ = None;
848            return;
849        }
850
851        // Change type
852        switch (type)
853        {
854        case Dynamic:
855            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT & !btCollisionObject::CF_KINEMATIC_OBJECT);
856            break;
857        case Kinematic:
858            this->physicalBody_->setCollisionFlags((this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT) | btCollisionObject::CF_KINEMATIC_OBJECT);
859            break;
860        case Static:
861            this->physicalBody_->setCollisionFlags((this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_KINEMATIC_OBJECT) | btCollisionObject::CF_STATIC_OBJECT);
862            break;
863        case None:
864            assert(false); // Doesn't happen
865            return;
866        }
867        this->collisionType_ = type;
868        this->collisionTypeSynchronised_ = type;
869
870        // update mass and inertia tensor
871        recalculateMassProps();
872        internalSetPhysicsProps();
873        collisionCallbackActivityChanged();
874        collisionResponseActivityChanged();
875        if (bReactivatePhysics)
876            activatePhysics();
877    }
878
879    //! Sets the CollisionType by string (used for the XMLPort)
880    void WorldEntity::setCollisionTypeStr(const std::string& typeStr)
881    {
882        const std::string& typeStrLower = getLowercase(typeStr);
883        CollisionType type;
884        if (typeStrLower == "dynamic")
885            type = Dynamic;
886        else if (typeStrLower == "static")
887            type = Static;
888        else if (typeStrLower == "kinematic")
889            type = Kinematic;
890        else if (typeStrLower == "none")
891            type = None;
892        else
893            ThrowException(ParseError, std::string("Attempting to set an unknown collision type: '") + typeStr + "'.");
894        this->setCollisionType(type);
895    }
896
897    //! Gets the CollisionType by string (used for the XMLPort)
898    std::string WorldEntity::getCollisionTypeStr() const
899    {
900        switch (this->getCollisionType())
901        {
902            case Dynamic:
903                return "dynamic";
904            case Kinematic:
905                return "kinematic";
906            case Static:
907                return "static";
908            case None:
909                return "none";
910            default:
911                assert(false);
912                return "";
913        }
914    }
915
916    /**
917    @brief
918        Recalculates the accumulated child mass and calls recalculateMassProps()
919        and notifies the parent of the change.
920    @note
921        Called by a child WE
922    */
923    void WorldEntity::notifyChildMassChanged()
924    {
925        // Note: CollisionShape changes of a child get handled over the internal CompoundCollisionShape already
926        // Recalculate mass
927        this->childrenMass_ = 0.0f;
928        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
929            this->childrenMass_ += (*it)->getMass();
930        recalculateMassProps();
931        // Notify parent WE
932        if (this->parent_)
933            parent_->notifyChildMassChanged();
934    }
935
936    /**
937    @brief
938        Undertakes the necessary steps to change the collision shape in Bullet, even at runtime.
939    @note
940        - called by this->collisionShape_
941        - May have a REALLY big overhead when called continuously at runtime, because then we need
942          to remove the physical body from Bullet and add it again.
943    */
944    void WorldEntity::notifyCollisionShapeChanged()
945    {
946        if (hasPhysics())
947        {
948            // Bullet doesn't like sudden changes of the collision shape, so we remove and add it again
949            if (this->addedToPhysicalWorld())
950            {
951                this->deactivatePhysics();
952                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
953                this->activatePhysics();
954            }
955            else
956                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
957        }
958        recalculateMassProps();
959    }
960
961    //! Updates all mass dependent parameters (mass, inertia tensor and child mass)
962    void WorldEntity::recalculateMassProps()
963    {
964        // Store local inertia for faster access. Evaluates to (0,0,0) if there is no collision shape.
965        float totalMass = this->mass_ + this->childrenMass_;
966        this->collisionShape_->calculateLocalInertia(totalMass, this->localInertia_);
967        if (this->hasPhysics())
968        {
969            if (this->isStatic())
970            {
971                // Just set everything to zero
972                this->physicalBody_->setMassProps(0.0f, btVector3(0, 0, 0));
973            }
974            else if (totalMass == 0.0f)
975            {
976                // Use default values to avoid very large or very small values
977                orxout(internal_warning) << "Setting the internal physical mass to 1.0 because mass_ is 0.0" << endl;
978                btVector3 inertia(0, 0, 0);
979                this->collisionShape_->calculateLocalInertia(1.0f, inertia);
980                this->physicalBody_->setMassProps(1.0f, inertia);
981            }
982            else
983            {
984                this->physicalBody_->setMassProps(totalMass, this->localInertia_);
985            }
986        }
987    }
988
989    //! Copies our own parameters for restitution, angular factor, damping and friction to the bullet rigid body.
990    void WorldEntity::internalSetPhysicsProps()
991    {
992        if (this->hasPhysics())
993        {
994            this->physicalBody_->setRestitution(this->restitution_);
995            this->physicalBody_->setAngularFactor(this->angularFactor_);
996            this->physicalBody_->setDamping(this->linearDamping_, this->angularDamping_);
997            this->physicalBody_->setFriction(this->friction_);
998        }
999    }
1000}
Note: See TracBrowser for help on using the repository browser.