Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2328 was 2313, checked in by rgrieder, 16 years ago

Added body queue to Scene: Physical objects now request to be added to the physical world.

  • Property svn:eol-style set to native
File size: 17.4 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 <OgreSceneManager.h>
35
36#include "BulletCollision/CollisionShapes/btCollisionShape.h"
37#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h"
38#include "BulletDynamics/Dynamics/btRigidBody.h"
39
40#include "util/Exception.h"
41#include "util/Convert.h"
42#include "core/CoreIncludes.h"
43#include "core/XMLPort.h"
44
45#include "objects/Scene.h"
46#include "objects/worldentities/collisionshapes/CollisionShape.h"
47#include "objects/worldentities/collisionshapes/CompoundCollisionShape.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    WorldEntity::WorldEntity(BaseObject* creator) : BaseObject(creator), network::Synchronisable(creator)
59    {
60        RegisterObject(WorldEntity);
61
62        assert(this->getScene());
63        assert(this->getScene()->getRootSceneNode());
64
65        this->node_ = this->getScene()->getRootSceneNode()->createChildSceneNode();
66
67        this->parent_ = 0;
68        this->parentID_ = (unsigned int)-1;
69
70        this->node_->setPosition(Vector3::ZERO);
71        this->node_->setOrientation(Quaternion::IDENTITY);
72
73        // Default behaviour does not include physics
74        this->physicalBody_ = 0;
75        this->collisionShape_ = 0;
76        this->mass_ = 0;
77        this->childMass_ = 0;
78        this->collisionType_ = None;
79
80        this->registerVariables();
81    }
82
83    WorldEntity::~WorldEntity()
84    {
85        if (this->isInitialized())
86        {
87            this->node_->detachAllObjects();
88            if (this->getScene()->getSceneManager())
89                this->getScene()->getSceneManager()->destroySceneNode(this->node_->getName());
90
91            if (this->physicalBody_)
92            {
93                if (this->physicalBody_->isInWorld())
94                    this->getScene()->getPhysicalWorld()->removeRigidBody(this->physicalBody_);
95                delete this->physicalBody_;
96            }
97            // TODO: Delete collisionShapes
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        XMLPortParam(WorldEntity, "collisionType", setCollisionTypeStr, getCollisionTypeStr, xmlelement, mode);
116        //XMLPortParam(WorldEntity, "collisionRadius", setCollisionRadius, getCollisionRadius, xmlelement, mode);
117        XMLPortParam(WorldEntity, "mass", setMass, getMass, xmlelement, mode);
118
119        XMLPortObject(WorldEntity, WorldEntity, "attached", attach, getAttachedObject, xmlelement, mode);
120        XMLPortObject(WorldEntity, CollisionShape, "collisionShapes", attachCollisionShape, getAttachedCollisionShape, xmlelement, mode);
121    }
122
123    void WorldEntity::registerVariables()
124    {
125        REGISTERDATA(this->bActive_,  network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedActivity));
126        REGISTERDATA(this->bVisible_, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedVisibility));
127
128        REGISTERDATA(this->getScale3D().x, network::direction::toclient);
129        REGISTERDATA(this->getScale3D().y, network::direction::toclient);
130        REGISTERDATA(this->getScale3D().z, network::direction::toclient);
131
132        REGISTERDATA(this->parentID_, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::updateParent));
133    }
134
135    void WorldEntity::updateParent()
136    {
137        WorldEntity* parent = dynamic_cast<WorldEntity*>(Synchronisable::getSynchronisable(this->parentID_));
138        if (parent)
139            this->attachToParent(parent);
140    }
141
142    void WorldEntity::attach(WorldEntity* object)
143    {
144        // check first whether attaching is even allowed
145        if (object->hasPhysics())
146        {
147            if (!this->hasPhysics())
148                ThrowException(PhysicsViolation, "Cannot attach a physical object to a non physical one.");
149            else if (object->isDynamic())
150                ThrowException(PhysicsViolation, "Cannot attach a dynamic object to a WorldEntity.");
151            else if (object->isKinematic() && this->isDynamic())
152                ThrowException(PhysicsViolation, "Cannot attach a kinematic object to a dynamic one.");
153            else if (object->isKinematic())
154                ThrowException(NotImplemented, "Cannot attach a kinematic object to a static or kinematic one: Not yet implemented.");
155            else
156            {
157                if (this->physicalBody_->isInWorld())
158                    removeFromPhysicalWorld();
159                if (object->physicalBody_->isInWorld())
160                    this->getScene()->removeRigidBody(object->physicalBody_);
161
162                // static to static/kinematic/dynamic --> merge shapes
163                this->childMass_ += object->getMass();
164                this->attachCollisionShape(object->getCollisionShape());
165                // Remove the btRigidBody from the child object.
166                // That also implies that cannot add a physics WE to the child afterwards.
167                object->setCollisionType(None);
168
169                addToPhysicalWorld();
170            }
171        }
172
173        if (object->getParent())
174            object->detachFromParent();
175        else
176        {
177            Ogre::Node* parent = object->node_->getParent();
178            if (parent)
179                parent->removeChild(object->node_);
180        }
181
182        this->node_->addChild(object->node_);
183        this->children_.insert(object);
184        object->parent_ = this;
185        object->parentID_ = this->getObjectID();
186    }
187
188    void WorldEntity::detach(WorldEntity* object)
189    {
190        this->node_->removeChild(object->node_);
191        this->children_.erase(object);
192        object->parent_ = 0;
193        object->parentID_ = (unsigned int)-1;
194
195//        this->getScene()->getRootSceneNode()->addChild(object->node_);
196    }
197
198    WorldEntity* WorldEntity::getAttachedObject(unsigned int index) const
199    {
200        unsigned int i = 0;
201        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
202        {
203            if (i == index)
204                return (*it);
205            ++i;
206        }
207        return 0;
208    }
209
210    void WorldEntity::attachCollisionShape(CollisionShape* shape)
211    {
212        this->attachedShapes_.push_back(shape);
213
214        if (!this->collisionShape_ && shape->hasNoTransform())
215        {
216            // Set local scaling right when adding it. It can include both scaling parameters
217            // and shape parameters (like sphere radius)
218            shape->getCollisionShape()->setLocalScaling(shape->getTotalScaling());
219            this->collisionShape_ = shape;
220            // recalculate inertia tensor
221            if (this->isDynamic())
222                internalSetMassProps();
223        }
224        else
225        {
226            if (this->collisionShape_ && !this->collisionShape_->isCompoundShape())
227            {
228                // We have to create a new compound shape and add the old one first.
229                CollisionShape* thisShape = this->collisionShape_;
230                this->collisionShape_ = 0;
231                this->mergeCollisionShape(thisShape);
232            }
233            this->mergeCollisionShape(shape);
234        }
235
236        if (this->physicalBody_)
237        {
238            if (this->physicalBody_->isInWorld())
239            {
240                COUT(2) << "Warning: Attaching collision shapes at run time causes its physical body to be removed and added again.";
241                removeFromPhysicalWorld();
242            }
243            this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
244        }
245
246        addToPhysicalWorld();
247    }
248
249    CollisionShape* WorldEntity::getAttachedCollisionShape(unsigned int index) const
250    {
251        if (index < this->attachedShapes_.size())
252            return attachedShapes_[index];
253        else
254            return 0;
255    }
256
257    void WorldEntity::mergeCollisionShape(CollisionShape* shape)
258    {
259        if (!this->collisionShape_)
260            this->collisionShape_ = new CompoundCollisionShape(this);
261        assert(this->collisionShape_->isCompoundShape());
262
263        // merge with transform
264        CompoundCollisionShape* compoundShape = static_cast<CompoundCollisionShape*>(this->collisionShape_);
265        assert(compoundShape);
266        compoundShape->addChildShape(shape);
267
268        // recalculate inertia tensor
269        internalSetMassProps();
270    }
271
272    void WorldEntity::addToPhysicalWorld() const
273    {
274        if (this->isActive() && this->hasPhysics() && !this->physicalBody_->isInWorld())
275            this->getScene()->addRigidBody(this->physicalBody_);
276    }
277
278    void WorldEntity::removeFromPhysicalWorld() const
279    {
280        if (this->hasPhysics())
281            this->getScene()->removeRigidBody(this->physicalBody_);
282    }
283
284    void WorldEntity::setScale3D(const Vector3& scale)
285    {
286        if (this->hasPhysics())
287            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
288
289        this->node_->setScale(scale);
290    }
291
292    void WorldEntity::scale3D(const Vector3& scale)
293    {
294        if (this->hasPhysics())
295            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
296
297        this->node_->scale(scale);
298    }
299
300    void WorldEntity::setMass(float mass)
301    {
302        this->mass_ = mass;
303        if (!this->hasPhysics())
304            COUT(3) << "Warning: Setting the mass of a WorldEntity with no physics has no effect." << std::endl;
305        else
306        {
307            if (this->physicalBody_->isInWorld())
308            {
309                COUT(2) << "Warning: Setting the mass of a WorldEntity at run time causes its physical body to be removed and added again." << std::endl;
310                removeFromPhysicalWorld();
311            }
312            internalSetMassProps();
313        }
314
315        addToPhysicalWorld();
316    }
317
318    void WorldEntity::internalSetMassProps()
319    {
320        assert(hasPhysics());
321
322        if ((this->isKinematic() || this->isStatic()) && (this->mass_ + this->childMass_) != 0.0f)
323        {
324            // Mass non zero is a bad idea for kinematic and static objects
325            this->physicalBody_->setMassProps(0.0f, btVector3(0, 0, 0));
326        }
327        else if (this->isDynamic() && (this->mass_ + this->childMass_) == 0.0f)
328        {
329            // Mass zero is not such a good idea for dynamic objects
330            this->physicalBody_->setMassProps(1.0f, this->getLocalInertia(1.0f));
331        }
332        else
333            this->physicalBody_->setMassProps(this->mass_, this->getLocalInertia(this->mass_ + this->childMass_));
334    }
335
336    btVector3 WorldEntity::getLocalInertia(btScalar mass) const
337    {
338        btVector3 inertia(0, 0, 0);
339        if (this->collisionShape_)
340            this->collisionShape_->getCollisionShape()->calculateLocalInertia(mass, inertia);
341        return inertia;
342    }
343
344    void WorldEntity::setCollisionType(CollisionType type)
345    {
346        // If we are already attached to a parent, this would be a bad idea..
347        if (this->parent_)
348            ThrowException(PhysicsViolation, "Cannot set the collision type of a WorldEntity with a parent");
349        else if (this->physicalBody_ && this->physicalBody_->isInWorld())
350            ThrowException(PhysicsViolation, "Warning: Cannot set the collision type at run time.");
351
352        // Check for type legality. Could be StaticEntity or MovableEntity
353        if (!this->isCollisionTypeLegal(type))
354            return; // exception gets issued anyway
355
356        // Check whether we have to create or destroy.
357        if (type != None && this->collisionType_ == None)
358        {
359            // Check whether there was some scaling applied.
360            if (!this->node_->getScale().positionEquals(Vector3(1, 1, 1), 0.001))
361                ThrowException(NotImplemented, "Cannot create a physical body if there is scaling applied to the node: Not yet implemented.");
362
363            // Create new rigid body
364            btCollisionShape* temp = 0;
365            if (this->collisionShape_)
366                temp = this->collisionShape_->getCollisionShape();
367            btRigidBody::btRigidBodyConstructionInfo bodyConstructionInfo(0, this, temp, btVector3(0,0,0));
368            this->physicalBody_ = new btRigidBody(bodyConstructionInfo);
369            this->physicalBody_->setUserPointer(this);
370        }
371        else if (type == None && this->collisionType_ != None)
372        {
373            // Destroy rigid body
374            assert(this->physicalBody_);
375            removeFromPhysicalWorld();
376            delete this->physicalBody_;
377            this->physicalBody_ = 0;
378            this->collisionType_ = None;
379            return;
380        }
381
382        // Change type
383        switch (type)
384        {
385        case Dynamic:
386            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !(btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT));
387            break;
388        case Kinematic:
389            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT);
390            break;
391        case Static:
392            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
393            break;
394        case None:
395            return; // this->collisionType_ was None too
396        }
397
398        // update our variable for faster checks
399        updateCollisionType();
400        assert(this->collisionType_ == type);
401
402        // update mass and inertia tensor
403        internalSetMassProps(); // type is not None! See case None: in switch
404
405        addToPhysicalWorld();
406    }
407
408    void WorldEntity::setCollisionTypeStr(const std::string& typeStr)
409    {
410        std::string typeStrLower = getLowercase(typeStr);
411        CollisionType type;
412        if (typeStrLower == "dynamic")
413            type = Dynamic;
414        else if (typeStrLower == "static")
415            type = Static;
416        else if (typeStrLower == "kinematic")
417            type = Kinematic;
418        else if (typeStrLower == "none")
419            type = None;
420        else
421            ThrowException(ParseError, std::string("Attempting to set an unknown collision type: '") + typeStr + "'.");
422        this->setCollisionType(type);
423    }
424
425    std::string WorldEntity::getCollisionTypeStr() const
426    {
427        switch (this->getCollisionType())
428        {
429            case Dynamic:
430                return "dynamic";
431            case Kinematic:
432                return "kinematic";
433            case Static:
434                return "static";
435            case None:
436                return "none";
437            default:
438                assert(false);
439                return "";
440        }
441    }
442
443    void WorldEntity::updateCollisionType()
444    {
445        if (!this->physicalBody_)
446            this->collisionType_ = None;
447        else if (this->physicalBody_->isKinematicObject())
448            this->collisionType_ = Kinematic;
449        else if (this->physicalBody_->isStaticObject())
450            this->collisionType_ = Static;
451        else
452            this->collisionType_ = Dynamic;
453    }
454
455    bool WorldEntity::checkPhysics() const
456    {
457        if (!this->physicalBody_)
458        {
459            assert(this->getCollisionType() == None);
460            COUT(2) << "WorldEntity was not fitted with physics, cannot set phyiscal property." << std::endl;
461            return false;
462        }
463        else
464            return true;
465    }
466}
Note: See TracBrowser for help on using the repository browser.