Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/netp4/src/orxonox/objects/worldentities/WorldEntity.cc @ 3113

Last change on this file since 3113 was 3110, checked in by rgrieder, 16 years ago

Removed old msvc specific support for precompiled header files.

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