Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/pch/src/orxonox/objects/worldentities/WorldEntity.cc @ 3186

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

Last part of the cleanup: world entities.

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