Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/physics/src/orxonox/objects/worldentities/WorldEntity.cc @ 2426

Last change on this file since 2426 was 2426, checked in by rgrieder, 15 years ago
  • Handled translate, rotate, yaw, pitch, roll, lookAt and setDirection internally in WE. That only leaves setPosition and setOrientation to be pure virtual.
  • Removed loads of redundant and now obsolete code in MobileEntity and StaticEntity
  • Removed OgreSceneNode.h from WorldEntity in debug builds. getPosition, getOrientation and getScale3D are only inline in release mode. That should speed up dev builds a lot (the include is about 8300 line without Vector3, Quaternion, etc.)

Important:

  • Fixed a bug or introduced one: the lookAt(…) function in WE had default TransformSpace "TS_LOCAL", but that makes no sense the way I see it. Consider a SpaceShip and you would like it to face an asteroid. Then TS_LOCAL would yield weird results. The XMLPort attribute "lookAt" used the default value. That's where the bug might have been fixed or (I don't believe so) introduced.
  • Property svn:eol-style set to native
File size: 21.6 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/CompoundCollisionShape.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    WorldEntity::WorldEntity(BaseObject* creator) : BaseObject(creator), network::Synchronisable(creator)
56    {
57        RegisterObject(WorldEntity);
58
59        assert(this->getScene());
60        assert(this->getScene()->getRootSceneNode());
61
62        this->node_ = this->getScene()->getRootSceneNode()->createChildSceneNode();
63
64        this->parent_ = 0;
65        this->parentID_ = (unsigned int)-1;
66
67        this->node_->setPosition(Vector3::ZERO);
68        this->node_->setOrientation(Quaternion::IDENTITY);
69
70        // Default behaviour does not include physics
71        this->physicalBody_ = 0;
72        this->bPhysicsActive_ = false;
73        this->collisionShape_ = new CompoundCollisionShape(this);
74        this->mass_ = 0;
75        this->childrenMass_ = 0;
76        this->collisionType_ = None;
77        this->collisionTypeSynchronised_ = None;
78
79        this->registerVariables();
80    }
81
82    WorldEntity::~WorldEntity()
83    {
84        if (this->isInitialized())
85        {
86            this->node_->detachAllObjects();
87            if (this->getScene()->getSceneManager())
88                this->getScene()->getSceneManager()->destroySceneNode(this->node_->getName());
89
90            // TODO: Detach from parent and detach all children.
91
92            if (this->physicalBody_)
93            {
94                this->deactivatePhysics();
95                delete this->physicalBody_;
96            }
97            delete this->collisionShape_;
98        }
99    }
100
101    void WorldEntity::XMLPort(Element& xmlelement, XMLPort::Mode mode)
102    {
103        SUPER(WorldEntity, XMLPort, xmlelement, mode);
104
105        XMLPortParamTemplate(WorldEntity, "position", setPosition, getPosition, xmlelement, mode, const Vector3&);
106        XMLPortParamTemplate(WorldEntity, "orientation", setOrientation, getOrientation, xmlelement, mode, const Quaternion&);
107        XMLPortParamLoadOnly(WorldEntity, "lookat", lookAt_xmlport, xmlelement, mode);
108        XMLPortParamLoadOnly(WorldEntity, "direction", setDirection_xmlport, xmlelement, mode);
109        XMLPortParamLoadOnly(WorldEntity, "yaw", yaw_xmlport, xmlelement, mode);
110        XMLPortParamLoadOnly(WorldEntity, "pitch", pitch_xmlport, xmlelement, mode);
111        XMLPortParamLoadOnly(WorldEntity, "roll", roll_xmlport, xmlelement, mode);
112        XMLPortParamTemplate(WorldEntity, "scale3D", setScale3D, getScale3D, xmlelement, mode, const Vector3&);
113        XMLPortParam(WorldEntity, "scale", setScale, getScale, xmlelement, mode);
114
115        // Physics
116        XMLPortParam(WorldEntity, "collisionType", setCollisionTypeStr, getCollisionTypeStr, xmlelement, mode);
117        XMLPortParam(WorldEntity, "mass", setMass, getMass, xmlelement, mode);
118
119        // Other attached WorldEntities
120        XMLPortObject(WorldEntity, WorldEntity, "attached", attach, getAttachedObject, xmlelement, mode);
121        // Attached collision shapes
122        XMLPortObject(WorldEntity, CollisionShape, "collisionShapes", attachCollisionShape, getAttachedCollisionShape, xmlelement, mode);
123    }
124
125    void WorldEntity::registerVariables()
126    {
127        REGISTERDATA(this->bActive_,     network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedActivity));
128        REGISTERDATA(this->bVisible_,    network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedVisibility));
129
130        REGISTERDATA(this->getScale3D(), network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::scaleChanged));
131
132        REGISTERDATA((int&)this->collisionTypeSynchronised_,
133                                         network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::collisionTypeChanged));
134        REGISTERDATA(this->mass_,        network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::massChanged));
135        REGISTERDATA(this->bPhysicsActiveSynchronised_,
136                                         network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::physicsActivityChanged));
137
138        REGISTERDATA(this->parentID_,    network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::updateParent));
139    }
140
141    void WorldEntity::updateParent()
142    {
143        WorldEntity* parent = dynamic_cast<WorldEntity*>(Synchronisable::getSynchronisable(this->parentID_));
144        if (parent)
145            this->attachToParent(parent);
146    }
147
148    void WorldEntity::collisionTypeChanged()
149    {
150        if (this->collisionTypeSynchronised_ != Dynamic &&
151            this->collisionTypeSynchronised_ != Kinematic &&
152            this->collisionTypeSynchronised_ != Static &&
153            this->collisionTypeSynchronised_ != None)
154        {
155            CCOUT(1) << "Error when collsion Type was received over network. Unknown enum value:" << this->collisionTypeSynchronised_ << std::endl;
156        }
157        else if (this->collisionTypeSynchronised_ != collisionType_)
158        {
159            if (this->parent_)
160                CCOUT(2) << "Warning: Network connection tried to set the collision type of an attached WE. Ignoring." << std::endl;
161            else
162                this->setCollisionType(this->collisionTypeSynchronised_);
163        }
164    }
165
166    void WorldEntity::massChanged()
167    {
168        this->setMass(this->mass_);
169    }
170
171    void WorldEntity::physicsActivityChanged()
172    {
173        if (this->bPhysicsActiveSynchronised_)
174            this->activatePhysics();
175        else
176            this->deactivatePhysics();
177    }
178
179    void WorldEntity::attach(WorldEntity* object)
180    {
181        // check first whether attaching is even allowed
182        if (object->hasPhysics())
183        {
184            if (!this->hasPhysics())
185                ThrowException(PhysicsViolation, "Cannot attach a physical object to a non physical one.");
186            else if (object->isDynamic())
187                ThrowException(PhysicsViolation, "Cannot attach a dynamic object to a WorldEntity.");
188            else if (object->isKinematic() && this->isDynamic())
189                ThrowException(PhysicsViolation, "Cannot attach a kinematic object to a dynamic one.");
190            else if (object->isKinematic())
191                ThrowException(NotImplemented, "Cannot attach a kinematic object to a static or kinematic one: Not yet implemented.");
192            else
193            {
194                object->deactivatePhysics();
195            }
196        }
197
198        if (object->getParent())
199            object->detachFromParent();
200        else
201        {
202            Ogre::Node* parent = object->node_->getParent();
203            if (parent)
204                parent->removeChild(object->node_);
205        }
206
207        this->node_->addChild(object->node_);
208        this->children_.insert(object);
209        object->parent_ = this;
210        object->parentID_ = this->getObjectID();
211
212        // collision shapes
213        this->attachCollisionShape(object->getCollisionShape());
214        // mass
215        this->childrenMass_ += object->getMass();
216        recalculatePhysicsProps();
217    }
218
219    void WorldEntity::detach(WorldEntity* object)
220    {
221        // collision shapes
222        this->detachCollisionShape(object->getCollisionShape());
223        // mass
224        if (object->getMass() > 0.0f)
225        {
226            this->childrenMass_ -= object->getMass();
227            recalculatePhysicsProps();
228        }
229
230        this->node_->removeChild(object->node_);
231        this->children_.erase(object);
232        object->parent_ = 0;
233        object->parentID_ = (unsigned int)-1;
234//        this->getScene()->getRootSceneNode()->addChild(object->node_);
235
236        // Note: It is possible that the object has physics but was disabled when attaching
237        object->activatePhysics();
238    }
239
240    WorldEntity* WorldEntity::getAttachedObject(unsigned int index) const
241    {
242        unsigned int i = 0;
243        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
244        {
245            if (i == index)
246                return (*it);
247            ++i;
248        }
249        return 0;
250    }
251
252    void WorldEntity::attachOgreObject(Ogre::MovableObject* object)
253    {
254        this->node_->attachObject(object);
255    }
256
257    void WorldEntity::detachOgreObject(Ogre::MovableObject* object)
258    {
259        this->node_->detachObject(object);
260    }
261
262    Ogre::MovableObject* WorldEntity::detachOgreObject(const Ogre::String& name)
263    {
264        return this->node_->detachObject(name);
265    }
266
267    void WorldEntity::attachCollisionShape(CollisionShape* shape)
268    {
269        this->collisionShape_->addChildShape(shape);
270        // Note: this->collisionShape_ already notifies us of any changes.
271    }
272
273    void WorldEntity::detachCollisionShape(CollisionShape* shape)
274    {
275        this->collisionShape_->removeChildShape(shape);
276        // Note: this->collisionShape_ already notifies us of any changes.
277    }
278
279    CollisionShape* WorldEntity::getAttachedCollisionShape(unsigned int index) const
280    {
281        return this->collisionShape_->getChildShape(index);
282    }
283
284    void WorldEntity::activatePhysics()
285    {
286        if (this->isActive() && this->hasPhysics() && !this->isPhysicsActive() && !this->parent_)
287        {
288            this->getScene()->addRigidBody(this->physicalBody_);
289            this->bPhysicsActive_ = true;
290        }
291    }
292
293    void WorldEntity::deactivatePhysics()
294    {
295        if (this->isPhysicsActive())
296        {
297            this->getScene()->removeRigidBody(this->physicalBody_);
298            this->bPhysicsActive_ = false;
299        }
300    }
301
302    bool WorldEntity::addedToPhysicalWorld() const
303    {
304        return this->physicalBody_ && this->physicalBody_->isInWorld();
305    }
306
307#ifndef _NDEBUG
308    const Vector3& WorldEntity::getPosition() const
309    {
310        return this->node_->getPosition();
311    }
312
313    const Quaternion& WorldEntity::getOrientation() const
314    {
315        return this->node_->getOrientation();
316    }
317
318    const Vector3& WorldEntity::getScale3D() const
319    {
320        return this->node_->getScale();
321    }
322#endif
323
324    const Vector3& WorldEntity::getWorldPosition() const
325    {
326        return this->node_->_getDerivedPosition();
327    }
328
329    const Quaternion& WorldEntity::getWorldOrientation() const
330    {
331        return this->node_->_getDerivedOrientation();
332    }
333
334    void WorldEntity::translate(const Vector3& distance, TransformSpace::Space relativeTo)
335    {
336        switch (relativeTo)
337        {
338        case TransformSpace::Local:
339            // position is relative to parent so transform downwards
340            this->setPosition(this->getPosition() + this->getOrientation() * distance);
341            break;
342        case TransformSpace::Parent:
343            this->setPosition(this->getPosition() + distance);
344            break;
345        case TransformSpace::World:
346            // position is relative to parent so transform upwards
347            if (this->node_->getParent())
348                setPosition(getPosition() + (node_->getParent()->_getDerivedOrientation().Inverse() * distance)
349                    / node_->getParent()->_getDerivedScale());
350            else
351                this->setPosition(this->getPosition() + distance);
352            break;
353        }
354    }
355
356    void WorldEntity::rotate(const Quaternion& rotation, TransformSpace::Space relativeTo)
357    {
358        switch(relativeTo)
359        {
360        case TransformSpace::Local:
361            this->setOrientation(this->getOrientation() * rotation);
362            break;
363        case TransformSpace::Parent:
364            // Rotations are normally relative to local axes, transform up
365            this->setOrientation(rotation * this->getOrientation());
366            break;
367        case TransformSpace::World:
368            // Rotations are normally relative to local axes, transform up
369            this->setOrientation(this->getOrientation() * this->getWorldOrientation().Inverse()
370                * rotation * this->getWorldOrientation());
371            break;
372        }
373    }
374
375    void WorldEntity::lookAt(const Vector3& target, TransformSpace::Space relativeTo, const Vector3& localDirectionVector)
376    {
377        Vector3 origin;
378        switch (relativeTo)
379        {
380        case TransformSpace::Local:
381            origin = Vector3::ZERO;
382            break;
383        case TransformSpace::Parent:
384            origin = this->getPosition();
385            break;
386        case TransformSpace::World:
387            origin = this->getWorldPosition();
388            break;
389        }
390        this->setDirection(target - origin, relativeTo, localDirectionVector);
391    }
392
393    void WorldEntity::setDirection(const Vector3& direction, TransformSpace::Space relativeTo, const Vector3& localDirectionVector)
394    {
395        Quaternion savedOrientation(this->getOrientation());
396        Ogre::Node::TransformSpace ogreRelativeTo;
397        switch (relativeTo)
398        {
399        case TransformSpace::Local:
400            ogreRelativeTo = Ogre::Node::TS_LOCAL; break;
401        case TransformSpace::Parent:
402            ogreRelativeTo = Ogre::Node::TS_PARENT; break;
403        case TransformSpace::World:
404            ogreRelativeTo = Ogre::Node::TS_WORLD; break;
405        }
406        this->node_->setDirection(direction, ogreRelativeTo, localDirectionVector);
407        Quaternion newOrientation(this->node_->getOrientation());
408        this->node_->setOrientation(savedOrientation);
409        this->setOrientation(newOrientation);
410    }
411
412    void WorldEntity::setScale3D(const Vector3& scale)
413    {
414        if (this->hasPhysics())
415            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
416
417        this->node_->setScale(scale);
418    }
419
420    void WorldEntity::setMass(float mass)
421    {
422        this->mass_ = mass;
423        recalculatePhysicsProps();
424    }
425
426    void WorldEntity::setCollisionType(CollisionType type)
427    {
428        // If we are already attached to a parent, this would be a bad idea..
429        if (this->parent_)
430            ThrowException(PhysicsViolation, "Cannot set the collision type of a WorldEntity with a parent");
431        else if (this->addedToPhysicalWorld())
432            ThrowException(PhysicsViolation, "Warning: Cannot set the collision type at run time.");
433
434        // Check for type legality. Could be StaticEntity or MobileEntity
435        if (!this->isCollisionTypeLegal(type))
436            return; // exception gets issued anyway
437
438        // Check whether we have to create or destroy.
439        if (type != None && this->collisionType_ == None)
440        {
441            // Check whether there was some scaling applied.
442            if (!this->node_->getScale().positionEquals(Vector3(1, 1, 1), 0.001))
443                ThrowException(NotImplemented, "Cannot create a physical body if there is scaling applied to the node: Not yet implemented.");
444
445            // Create new rigid body
446            btCollisionShape* temp = 0;
447            btRigidBody::btRigidBodyConstructionInfo bodyConstructionInfo(0, this, this->collisionShape_->getCollisionShape());
448            bodyConstructionInfo.m_restitution = 1;
449            this->physicalBody_ = new btRigidBody(bodyConstructionInfo);
450            this->physicalBody_->setUserPointer(this);
451            this->physicalBody_->setActivationState(DISABLE_DEACTIVATION);
452        }
453        else if (type == None && this->collisionType_ != None)
454        {
455            // Destroy rigid body
456            assert(this->physicalBody_);
457            deactivatePhysics();
458            delete this->physicalBody_;
459            this->physicalBody_ = 0;
460            this->collisionType_ = None;
461            this->collisionTypeSynchronised_ = None;
462            return;
463        }
464
465        // Change type
466        switch (type)
467        {
468        case Dynamic:
469            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !(btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT));
470            break;
471        case Kinematic:
472            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT);
473            break;
474        case Static:
475            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
476            break;
477        case None:
478            return; // this->collisionType_ was None too
479        }
480
481        // Only sets this->collisionShape_
482        // However the assertion is to ensure that the internal bullet setting is right
483        updateCollisionType();
484        assert(this->collisionType_ == type);
485
486        // update mass and inertia tensor
487        recalculatePhysicsProps();
488        activatePhysics();
489    }
490
491    void WorldEntity::setCollisionTypeStr(const std::string& typeStr)
492    {
493        std::string typeStrLower = getLowercase(typeStr);
494        CollisionType type;
495        if (typeStrLower == "dynamic")
496            type = Dynamic;
497        else if (typeStrLower == "static")
498            type = Static;
499        else if (typeStrLower == "kinematic")
500            type = Kinematic;
501        else if (typeStrLower == "none")
502            type = None;
503        else
504            ThrowException(ParseError, std::string("Attempting to set an unknown collision type: '") + typeStr + "'.");
505        this->setCollisionType(type);
506    }
507
508    std::string WorldEntity::getCollisionTypeStr() const
509    {
510        switch (this->getCollisionType())
511        {
512            case Dynamic:
513                return "dynamic";
514            case Kinematic:
515                return "kinematic";
516            case Static:
517                return "static";
518            case None:
519                return "none";
520            default:
521                assert(false);
522                return "";
523        }
524    }
525
526    void WorldEntity::updateCollisionType()
527    {
528        if (!this->physicalBody_)
529            this->collisionType_ = None;
530        else if (this->physicalBody_->isKinematicObject())
531            this->collisionType_ = Kinematic;
532        else if (this->physicalBody_->isStaticObject())
533            this->collisionType_ = Static;
534        else
535            this->collisionType_ = Dynamic;
536        this->collisionTypeSynchronised_ = this->collisionType_;
537    }
538
539    void WorldEntity::notifyChildMassChanged() // Called by a child WE
540    {
541        // Note: CollisionShape changes of a child get handled over the internal CompoundCollisionShape already
542        // Recalculate mass
543        this->childrenMass_ = 0.0f;
544        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
545            this->childrenMass_ += (*it)->getMass();
546        recalculatePhysicsProps();
547        // Notify parent WE
548        if (this->parent_)
549            parent_->notifyChildMassChanged();
550    }
551
552    void WorldEntity::notifyCollisionShapeChanged() // called by this->collisionShape_
553    {
554        if (hasPhysics())
555        {
556            // Bullet doesn't like sudden changes of the collision shape, so we remove and add it again
557            if (this->addedToPhysicalWorld())
558            {
559                this->deactivatePhysics();
560                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
561                this->activatePhysics();
562            }
563            else
564                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
565        }
566        recalculatePhysicsProps();
567    }
568
569    void WorldEntity::recalculatePhysicsProps()
570    {
571        if (this->hasPhysics())
572        {
573            if (this->isStatic())
574            {
575                // Just set everything to zero
576                this->physicalBody_->setMassProps(0.0f, btVector3(0, 0, 0));
577            }
578            else if ((this->mass_ + this->childrenMass_) == 0.0f)
579            {
580                // Use default values to avoid very large or very small values
581                CCOUT(4) << "Warning: Setting the internal physical mass to 1.0 because mass_ is 0.0." << std::endl;
582                this->physicalBody_->setMassProps(1.0f, this->collisionShape_->getLocalInertia(1.0f));
583            }
584            else
585            {
586                float mass = this->mass_ + this->childrenMass_;
587                this->physicalBody_->setMassProps(mass, this->collisionShape_->getLocalInertia(mass));
588            }
589        }
590    }
591}
Note: See TracBrowser for help on using the repository browser.