Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2423 was 2423, checked in by rgrieder, 15 years ago
  • Added detach functions to CollisionShapes
  • Added update functions across the CollisionShape hierarchy so that when you change the radius of a sphere, everything up to the WE gets updated.
  • Setting the btCollisionShape at run time doesn't work after all, fixed that (you can still do it, just a question of internals)
  • Improved network synchronisation
  • new WE function: addedToPhysicalWorld() to check whether we can still perform operations that are disallowed at run time (esp. StaticEntity)

Conclusively, I can say that right now, all operations considering physics should be handled automatically, bugs not withstanding.

  • Property svn:eol-style set to native
File size: 17.8 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#include "BulletDynamics/Dynamics/btRigidBody.h"
36
37#include "util/Exception.h"
38#include "util/Convert.h"
39#include "core/CoreIncludes.h"
40#include "core/XMLPort.h"
41
42#include "objects/Scene.h"
43#include "objects/collisionshapes/CompoundCollisionShape.h"
44
45namespace orxonox
46{
47    const Vector3 WorldEntity::FRONT = Vector3::NEGATIVE_UNIT_Z;
48    const Vector3 WorldEntity::BACK  = Vector3::UNIT_Z;
49    const Vector3 WorldEntity::LEFT  = Vector3::NEGATIVE_UNIT_X;
50    const Vector3 WorldEntity::RIGHT = Vector3::UNIT_X;
51    const Vector3 WorldEntity::DOWN  = Vector3::NEGATIVE_UNIT_Y;
52    const Vector3 WorldEntity::UP    = Vector3::UNIT_Y;
53
54    WorldEntity::WorldEntity(BaseObject* creator) : BaseObject(creator), network::Synchronisable(creator)
55    {
56        RegisterObject(WorldEntity);
57
58        assert(this->getScene());
59        assert(this->getScene()->getRootSceneNode());
60
61        this->node_ = this->getScene()->getRootSceneNode()->createChildSceneNode();
62
63        this->parent_ = 0;
64        this->parentID_ = (unsigned int)-1;
65
66        this->node_->setPosition(Vector3::ZERO);
67        this->node_->setOrientation(Quaternion::IDENTITY);
68
69        // Default behaviour does not include physics
70        this->physicalBody_ = 0;
71        this->bPhysicsActive_ = false;
72        this->collisionShape_ = new CompoundCollisionShape(this);
73        this->mass_ = 0;
74        this->childrenMass_ = 0;
75        this->collisionType_ = None;
76        this->collisionTypeSynchronised_ = None;
77
78        this->registerVariables();
79    }
80
81    WorldEntity::~WorldEntity()
82    {
83        if (this->isInitialized())
84        {
85            this->node_->detachAllObjects();
86            if (this->getScene()->getSceneManager())
87                this->getScene()->getSceneManager()->destroySceneNode(this->node_->getName());
88
89            // TODO: Detach from parent and detach all children.
90
91            if (this->physicalBody_)
92            {
93                this->deactivatePhysics();
94                delete this->physicalBody_;
95            }
96            delete this->collisionShape_;
97        }
98    }
99
100    void WorldEntity::XMLPort(Element& xmlelement, XMLPort::Mode mode)
101    {
102        SUPER(WorldEntity, XMLPort, xmlelement, mode);
103
104        XMLPortParamTemplate(WorldEntity, "position", setPosition, getPosition, xmlelement, mode, const Vector3&);
105        XMLPortParamTemplate(WorldEntity, "orientation", setOrientation, getOrientation, xmlelement, mode, const Quaternion&);
106        XMLPortParamLoadOnly(WorldEntity, "lookat", lookAt_xmlport, xmlelement, mode);
107        XMLPortParamLoadOnly(WorldEntity, "direction", setDirection_xmlport, xmlelement, mode);
108        XMLPortParamLoadOnly(WorldEntity, "yaw", yaw_xmlport, xmlelement, mode);
109        XMLPortParamLoadOnly(WorldEntity, "pitch", pitch_xmlport, xmlelement, mode);
110        XMLPortParamLoadOnly(WorldEntity, "roll", roll_xmlport, xmlelement, mode);
111        XMLPortParamTemplate(WorldEntity, "scale3D", setScale3D, getScale3D, xmlelement, mode, const Vector3&);
112        XMLPortParam(WorldEntity, "scale", setScale, getScale, xmlelement, mode);
113
114        // Physics
115        XMLPortParam(WorldEntity, "collisionType", setCollisionTypeStr, getCollisionTypeStr, xmlelement, mode);
116        XMLPortParam(WorldEntity, "mass", setMass, getMass, xmlelement, mode);
117
118        // Other attached WorldEntities
119        XMLPortObject(WorldEntity, WorldEntity, "attached", attach, getAttachedObject, xmlelement, mode);
120        // Attached collision shapes
121        XMLPortObject(WorldEntity, CollisionShape, "collisionShapes", attachCollisionShape, getAttachedCollisionShape, xmlelement, mode);
122    }
123
124    void WorldEntity::registerVariables()
125    {
126        REGISTERDATA(this->bActive_,     network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedActivity));
127        REGISTERDATA(this->bVisible_,    network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedVisibility));
128
129        REGISTERDATA(this->getScale3D(), network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::scaleChanged));
130
131        REGISTERDATA((int&)this->collisionTypeSynchronised_,
132                                         network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::collisionTypeChanged));
133        REGISTERDATA(this->mass_,        network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::massChanged));
134        REGISTERDATA(this->bPhysicsActiveSynchronised_,
135                                         network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::physicsActivityChanged));
136
137        REGISTERDATA(this->parentID_,    network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::updateParent));
138    }
139
140    void WorldEntity::updateParent()
141    {
142        WorldEntity* parent = dynamic_cast<WorldEntity*>(Synchronisable::getSynchronisable(this->parentID_));
143        if (parent)
144            this->attachToParent(parent);
145    }
146
147    void WorldEntity::collisionTypeChanged()
148    {
149        if (this->collisionTypeSynchronised_ != Dynamic &&
150            this->collisionTypeSynchronised_ != Kinematic &&
151            this->collisionTypeSynchronised_ != Static &&
152            this->collisionTypeSynchronised_ != None)
153        {
154            CCOUT(1) << "Error when collsion Type was received over network. Unknown enum value:" << this->collisionTypeSynchronised_ << std::endl;
155        }
156        else if (this->collisionTypeSynchronised_ != collisionType_)
157        {
158            if (this->parent_)
159                CCOUT(2) << "Warning: Network connection tried to set the collision type of an attached WE. Ignoring." << std::endl;
160            else
161                this->setCollisionType(this->collisionTypeSynchronised_);
162        }
163    }
164
165    void WorldEntity::massChanged()
166    {
167        this->setMass(this->mass_);
168    }
169
170    void WorldEntity::physicsActivityChanged()
171    {
172        if (this->bPhysicsActiveSynchronised_)
173            this->activatePhysics();
174        else
175            this->deactivatePhysics();
176    }
177
178    void WorldEntity::attach(WorldEntity* object)
179    {
180        // check first whether attaching is even allowed
181        if (object->hasPhysics())
182        {
183            if (!this->hasPhysics())
184                ThrowException(PhysicsViolation, "Cannot attach a physical object to a non physical one.");
185            else if (object->isDynamic())
186                ThrowException(PhysicsViolation, "Cannot attach a dynamic object to a WorldEntity.");
187            else if (object->isKinematic() && this->isDynamic())
188                ThrowException(PhysicsViolation, "Cannot attach a kinematic object to a dynamic one.");
189            else if (object->isKinematic())
190                ThrowException(NotImplemented, "Cannot attach a kinematic object to a static or kinematic one: Not yet implemented.");
191            else
192            {
193                object->deactivatePhysics();
194            }
195        }
196
197        if (object->getParent())
198            object->detachFromParent();
199        else
200        {
201            Ogre::Node* parent = object->node_->getParent();
202            if (parent)
203                parent->removeChild(object->node_);
204        }
205
206        this->node_->addChild(object->node_);
207        this->children_.insert(object);
208        object->parent_ = this;
209        object->parentID_ = this->getObjectID();
210
211        // collision shapes
212        this->attachCollisionShape(object->getCollisionShape());
213        // mass
214        this->childrenMass_ += object->getMass();
215        recalculatePhysicsProps();
216    }
217
218    void WorldEntity::detach(WorldEntity* object)
219    {
220        // collision shapes
221        this->detachCollisionShape(object->getCollisionShape());
222        // mass
223        if (object->getMass() > 0.0f)
224        {
225            this->childrenMass_ -= object->getMass();
226            recalculatePhysicsProps();
227        }
228
229        this->node_->removeChild(object->node_);
230        this->children_.erase(object);
231        object->parent_ = 0;
232        object->parentID_ = (unsigned int)-1;
233//        this->getScene()->getRootSceneNode()->addChild(object->node_);
234
235        // Note: It is possible that the object has physics but was disabled when attaching
236        object->activatePhysics();
237    }
238
239    WorldEntity* WorldEntity::getAttachedObject(unsigned int index) const
240    {
241        unsigned int i = 0;
242        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
243        {
244            if (i == index)
245                return (*it);
246            ++i;
247        }
248        return 0;
249    }
250
251    void WorldEntity::attachCollisionShape(CollisionShape* shape)
252    {
253        this->collisionShape_->addChildShape(shape);
254        // Note: this->collisionShape_ already notifies us of any changes.
255    }
256
257    void WorldEntity::detachCollisionShape(CollisionShape* shape)
258    {
259        this->collisionShape_->removeChildShape(shape);
260        // Note: this->collisionShape_ already notifies us of any changes.
261    }
262
263    CollisionShape* WorldEntity::getAttachedCollisionShape(unsigned int index) const
264    {
265        return this->collisionShape_->getChildShape(index);
266    }
267
268    void WorldEntity::activatePhysics()
269    {
270        if (this->isActive() && this->hasPhysics() && !this->isPhysicsActive() && !this->parent_)
271        {
272            this->getScene()->addRigidBody(this->physicalBody_);
273            this->bPhysicsActive_ = true;
274        }
275    }
276
277    void WorldEntity::deactivatePhysics()
278    {
279        if (this->isPhysicsActive())
280        {
281            this->getScene()->removeRigidBody(this->physicalBody_);
282            this->bPhysicsActive_ = false;
283        }
284    }
285
286    bool WorldEntity::addedToPhysicalWorld() const
287    {
288        return this->physicalBody_ && this->physicalBody_->isInWorld();
289    }
290
291    void WorldEntity::setScale3D(const Vector3& scale)
292    {
293        if (this->hasPhysics())
294            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
295
296        this->node_->setScale(scale);
297    }
298
299    void WorldEntity::scale3D(const Vector3& scale)
300    {
301        if (this->hasPhysics())
302            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
303
304        this->node_->scale(scale);
305    }
306
307    void WorldEntity::setMass(float mass)
308    {
309        this->mass_ = mass;
310        recalculatePhysicsProps();
311    }
312
313    void WorldEntity::setCollisionType(CollisionType type)
314    {
315        // If we are already attached to a parent, this would be a bad idea..
316        if (this->parent_)
317            ThrowException(PhysicsViolation, "Cannot set the collision type of a WorldEntity with a parent");
318        else if (this->addedToPhysicalWorld())
319            ThrowException(PhysicsViolation, "Warning: Cannot set the collision type at run time.");
320
321        // Check for type legality. Could be StaticEntity or MobileEntity
322        if (!this->isCollisionTypeLegal(type))
323            return; // exception gets issued anyway
324
325        // Check whether we have to create or destroy.
326        if (type != None && this->collisionType_ == None)
327        {
328            // Check whether there was some scaling applied.
329            if (!this->node_->getScale().positionEquals(Vector3(1, 1, 1), 0.001))
330                ThrowException(NotImplemented, "Cannot create a physical body if there is scaling applied to the node: Not yet implemented.");
331
332            // Create new rigid body
333            btCollisionShape* temp = 0;
334            btRigidBody::btRigidBodyConstructionInfo bodyConstructionInfo(0, this, this->collisionShape_->getCollisionShape());
335            bodyConstructionInfo.m_restitution = 1;
336            this->physicalBody_ = new btRigidBody(bodyConstructionInfo);
337            this->physicalBody_->setUserPointer(this);
338            this->physicalBody_->setActivationState(DISABLE_DEACTIVATION);
339        }
340        else if (type == None && this->collisionType_ != None)
341        {
342            // Destroy rigid body
343            assert(this->physicalBody_);
344            deactivatePhysics();
345            delete this->physicalBody_;
346            this->physicalBody_ = 0;
347            this->collisionType_ = None;
348            this->collisionTypeSynchronised_ = None;
349            return;
350        }
351
352        // Change type
353        switch (type)
354        {
355        case Dynamic:
356            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !(btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT));
357            break;
358        case Kinematic:
359            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT);
360            break;
361        case Static:
362            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
363            break;
364        case None:
365            return; // this->collisionType_ was None too
366        }
367
368        // Only sets this->collisionShape_
369        // However the assertion is to ensure that the internal bullet setting is right
370        updateCollisionType();
371        assert(this->collisionType_ == type);
372
373        // update mass and inertia tensor
374        recalculatePhysicsProps();
375        activatePhysics();
376    }
377
378    void WorldEntity::setCollisionTypeStr(const std::string& typeStr)
379    {
380        std::string typeStrLower = getLowercase(typeStr);
381        CollisionType type;
382        if (typeStrLower == "dynamic")
383            type = Dynamic;
384        else if (typeStrLower == "static")
385            type = Static;
386        else if (typeStrLower == "kinematic")
387            type = Kinematic;
388        else if (typeStrLower == "none")
389            type = None;
390        else
391            ThrowException(ParseError, std::string("Attempting to set an unknown collision type: '") + typeStr + "'.");
392        this->setCollisionType(type);
393    }
394
395    std::string WorldEntity::getCollisionTypeStr() const
396    {
397        switch (this->getCollisionType())
398        {
399            case Dynamic:
400                return "dynamic";
401            case Kinematic:
402                return "kinematic";
403            case Static:
404                return "static";
405            case None:
406                return "none";
407            default:
408                assert(false);
409                return "";
410        }
411    }
412
413    void WorldEntity::updateCollisionType()
414    {
415        if (!this->physicalBody_)
416            this->collisionType_ = None;
417        else if (this->physicalBody_->isKinematicObject())
418            this->collisionType_ = Kinematic;
419        else if (this->physicalBody_->isStaticObject())
420            this->collisionType_ = Static;
421        else
422            this->collisionType_ = Dynamic;
423        this->collisionTypeSynchronised_ = this->collisionType_;
424    }
425
426    void WorldEntity::notifyChildMassChanged() // Called by a child WE
427    {
428        // Note: CollisionShape changes of a child get handled over the internal CompoundCollisionShape already
429        // Recalculate mass
430        this->childrenMass_ = 0.0f;
431        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
432            this->childrenMass_ += (*it)->getMass();
433        recalculatePhysicsProps();
434        // Notify parent WE
435        if (this->parent_)
436            parent_->notifyChildMassChanged();
437    }
438
439    void WorldEntity::notifyCollisionShapeChanged() // called by this->collisionShape_
440    {
441        if (hasPhysics())
442        {
443            // Bullet doesn't like sudden changes of the collision shape, so we remove and add it again
444            if (this->addedToPhysicalWorld())
445            {
446                this->deactivatePhysics();
447                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
448                this->activatePhysics();
449            }
450            else
451                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
452        }
453        recalculatePhysicsProps();
454    }
455
456    void WorldEntity::recalculatePhysicsProps()
457    {
458        if (this->hasPhysics())
459        {
460            if (this->isStatic())
461            {
462                // Just set everything to zero
463                this->physicalBody_->setMassProps(0.0f, btVector3(0, 0, 0));
464            }
465            else if ((this->mass_ + this->childrenMass_) == 0.0f)
466            {
467                // Use default values to avoid very large or very small values
468                CCOUT(4) << "Warning: Setting the internal physical mass to 1.0 because mass_ is 0.0." << std::endl;
469                this->physicalBody_->setMassProps(1.0f, this->collisionShape_->getLocalInertia(1.0f));
470            }
471            else
472            {
473                float mass = this->mass_ + this->childrenMass_;
474                this->physicalBody_->setMassProps(mass, this->collisionShape_->getLocalInertia(mass));
475            }
476        }
477    }
478}
Note: See TracBrowser for help on using the repository browser.