Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/towerdefenseHS14/src/orxonox/worldentities/WorldEntity.cc @ 10169

Last change on this file since 10169 was 10144, checked in by maxima, 10 years ago

Geaenderte Turret Klassen hinzugefuegt. Neues Template towerturret. Dort wurde an den TDTower ein Turret attached. So schiesst der Tower nun auf die Enemies.

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