Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/tutorial/src/orxonox/worldentities/WorldEntity.cc @ 7433

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

Merged presentation2 branch back to trunk.
Major new features:

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