Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 6184 was 5929, checked in by rgrieder, 15 years ago

Merged core5 branch back to the trunk.
Key features include clean level unloading and an extended XML event system.

Two important notes:
Delete your keybindings.ini files! * or you will still get parser errors when loading the key bindings.
Delete build_dir/lib/modules/libgamestates.module! * or orxonox won't start.
Best thing to do is to delete the build folder ;)

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