Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 3099 was 3080, checked in by landauf, 16 years ago

Yet another bugfix.

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