Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2306 was 2306, checked in by rgrieder, 15 years ago
  • More dealings with not yet implemented scaling of physical objects.
  • Clearer handling of masses specified by setMass() (also taking into account mass of child WE)
  • Now calculating the inertia tensor as well according to mass and collisionShape
  • Bugfix when calling Bullet solver
  • Property svn:eol-style set to native
File size: 16.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            this->setCollisionType(None);
92        }
93    }
94
95    void WorldEntity::XMLPort(Element& xmlelement, XMLPort::Mode mode)
96    {
97        SUPER(WorldEntity, XMLPort, xmlelement, mode);
98
99        XMLPortParamTemplate(WorldEntity, "position", setPosition, getPosition, xmlelement, mode, const Vector3&);
100        XMLPortParamTemplate(WorldEntity, "orientation", setOrientation, getOrientation, xmlelement, mode, const Quaternion&);
101        XMLPortParamLoadOnly(WorldEntity, "lookat", lookAt_xmlport, xmlelement, mode);
102        XMLPortParamLoadOnly(WorldEntity, "direction", setDirection_xmlport, xmlelement, mode);
103        XMLPortParamLoadOnly(WorldEntity, "yaw", yaw_xmlport, xmlelement, mode);
104        XMLPortParamLoadOnly(WorldEntity, "pitch", pitch_xmlport, xmlelement, mode);
105        XMLPortParamLoadOnly(WorldEntity, "roll", roll_xmlport, xmlelement, mode);
106        XMLPortParamTemplate(WorldEntity, "scale3D", setScale3D, getScale3D, xmlelement, mode, const Vector3&);
107        XMLPortParam(WorldEntity, "scale", setScale, getScale, xmlelement, mode);
108
109        XMLPortParam(WorldEntity, "collisionType", setCollisionTypeStr, getCollisionTypeStr, xmlelement, mode);
110        //XMLPortParam(WorldEntity, "collisionRadius", setCollisionRadius, getCollisionRadius, xmlelement, mode);
111        XMLPortParam(WorldEntity, "mass", setMass, getMass, xmlelement, mode);
112
113        XMLPortObject(WorldEntity, WorldEntity, "attached", attach, getAttachedObject, xmlelement, mode);
114        XMLPortObject(WorldEntity, CollisionShape, "collisionShapes", attachCollisionShape, getAttachedCollisionShape, xmlelement, mode);
115    }
116
117    void WorldEntity::registerVariables()
118    {
119        REGISTERDATA(this->bActive_,  network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedActivity));
120        REGISTERDATA(this->bVisible_, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedVisibility));
121
122        REGISTERDATA(this->getScale3D().x, network::direction::toclient);
123        REGISTERDATA(this->getScale3D().y, network::direction::toclient);
124        REGISTERDATA(this->getScale3D().z, network::direction::toclient);
125
126        REGISTERDATA(this->parentID_, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::updateParent));
127    }
128
129    void WorldEntity::updateParent()
130    {
131        WorldEntity* parent = dynamic_cast<WorldEntity*>(Synchronisable::getSynchronisable(this->parentID_));
132        if (parent)
133            this->attachToParent(parent);
134    }
135
136    void WorldEntity::attach(WorldEntity* object)
137    {
138        // check first whether attaching is even allowed
139        if (object->hasPhysics())
140        {
141            if (!this->hasPhysics())
142                ThrowException(PhysicsViolation, "Cannot attach a physical object to a non physical one.");
143            else if (object->isDynamic())
144                ThrowException(PhysicsViolation, "Cannot attach a dynamic object to a WorldEntity.");
145            else if (object->isKinematic() && this->isDynamic())
146                ThrowException(PhysicsViolation, "Cannot attach a kinematic object to a dynamic one.");
147            else if (object->isKinematic())
148                ThrowException(NotImplemented, "Cannot attach a kinematic object to a static or kinematic one: Not yet implemented.");
149            else if (object->physicalBody_->isInWorld() || this->physicalBody_->isInWorld())
150                ThrowException(PhysicsViolation, "Cannot attach a physical object at runtime.");
151            else
152            {
153                // static to static/kinematic/dynamic --> merge shapes
154                this->childMass_ += object->getMass();
155                this->attachCollisionShape(object->getCollisionShape());
156                // Remove the btRigidBody from the child object.
157                // That also implies that cannot add a physics WE to the child afterwards.
158                object->setCollisionType(None);
159            }
160        }
161
162        if (object->getParent())
163            object->detachFromParent();
164        else
165        {
166            Ogre::Node* parent = object->node_->getParent();
167            if (parent)
168                parent->removeChild(object->node_);
169        }
170
171        this->node_->addChild(object->node_);
172        this->children_.insert(object);
173        object->parent_ = this;
174        object->parentID_ = this->getObjectID();
175    }
176
177    void WorldEntity::detach(WorldEntity* object)
178    {
179        this->node_->removeChild(object->node_);
180        this->children_.erase(object);
181        object->parent_ = 0;
182        object->parentID_ = (unsigned int)-1;
183
184//        this->getScene()->getRootSceneNode()->addChild(object->node_);
185    }
186
187    WorldEntity* WorldEntity::getAttachedObject(unsigned int index) const
188    {
189        unsigned int i = 0;
190        for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it)
191        {
192            if (i == index)
193                return (*it);
194            ++i;
195        }
196        return 0;
197    }
198
199    void WorldEntity::attachCollisionShape(CollisionShape* shape)
200    {
201        this->attachedShapes_.push_back(shape);
202
203        if (!this->collisionShape_ && shape->hasNoTransform())
204        {
205            // Set local scaling right when adding it. It can include both scaling parameters
206            // and shape parameters (like sphere radius)
207            shape->getCollisionShape()->setLocalScaling(shape->getTotalScaling());
208            this->collisionShape_ = shape;
209            // recalculate inertia tensor
210            if (this->isDynamic())
211            {
212                internalSetMassProps();
213            }
214        }
215        else
216        {
217            if (this->collisionShape_ && !this->collisionShape_->isCompoundShape())
218            {
219                // We have to create a new compound shape and add the old one first.
220                CollisionShape* thisShape = this->collisionShape_;
221                this->collisionShape_ = 0;
222                this->mergeCollisionShape(thisShape);
223            }
224            this->mergeCollisionShape(shape);
225        }
226
227        if (this->physicalBody_)
228        {
229            if (this->physicalBody_->isInWorld())
230                COUT(2) << "Warning: Cannot attach a physical object at runtime.";
231            else
232                this->physicalBody_->setCollisionShape(this->collisionShape_->getCollisionShape());
233        }
234    }
235
236    CollisionShape* WorldEntity::getAttachedCollisionShape(unsigned int index) const
237    {
238        if (index < this->attachedShapes_.size())
239            return attachedShapes_[index];
240        else
241            return 0;
242    }
243
244    void WorldEntity::mergeCollisionShape(CollisionShape* shape)
245    {
246        if (!this->collisionShape_)
247            this->collisionShape_ = new CompoundCollisionShape(this);
248        assert(this->collisionShape_->isCompoundShape());
249
250        // merge with transform
251        CompoundCollisionShape* compoundShape = static_cast<CompoundCollisionShape*>(this->collisionShape_);
252        assert(compoundShape);
253        compoundShape->addChildShape(shape);
254
255        // recalculate inertia tensor
256        internalSetMassProps();
257    }
258
259    void WorldEntity::setScale3D(const Vector3& scale)
260    {
261        if (this->hasPhysics())
262            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
263
264        this->node_->setScale(scale);
265    }
266
267    void WorldEntity::scale3D(const Vector3& scale)
268    {
269        if (this->hasPhysics())
270            ThrowException(NotImplemented, "Cannot set the scale of a physical object: Not yet implemented.");
271
272        this->node_->scale(scale);
273    }
274
275    void WorldEntity::setMass(float mass)
276    {
277        this->mass_ = mass;
278        if (!this->hasPhysics())
279            COUT(3) << "Warning: Setting the mass of a WorldEntity with no physics has no effect." << std::endl;
280        else if (this->physicalBody_->isInWorld())
281            COUT(2) << "Warning: Cannot set the physical mass at run time. Storing new mass." << std::endl;
282        else
283            internalSetMassProps();
284    }
285
286    void WorldEntity::internalSetMassProps()
287    {
288        assert(hasPhysics());
289
290        if ((this->isKinematic() || this->isStatic()) && (this->mass_ + this->childMass_) != 0.0f)
291        {
292            // Mass non zero is a bad idea for kinematic and static objects
293            this->physicalBody_->setMassProps(0.0f, btVector3(0, 0, 0));
294        }
295        else if (this->isDynamic() && (this->mass_ + this->childMass_) == 0.0f)
296        {
297            // Mass zero is not such a good idea for dynamic objects
298            this->physicalBody_->setMassProps(1.0f, this->getLocalInertia(1.0f));
299        }
300        else
301            this->physicalBody_->setMassProps(this->mass_, this->getLocalInertia(this->mass_ + this->childMass_));
302    }
303
304    btVector3 WorldEntity::getLocalInertia(btScalar mass) const
305    {
306        btVector3 inertia(0, 0, 0);
307        if (this->collisionShape_)
308            this->collisionShape_->getCollisionShape()->calculateLocalInertia(mass, inertia);
309        return inertia;
310    }
311
312    void WorldEntity::setCollisionType(CollisionType type)
313    {
314        // If we are already attached to a parent, this would be a bad idea..
315        if (this->parent_)
316            ThrowException(PhysicsViolation, "Cannot set the collision type of a WorldEntity with a parent");
317        else if (this->physicalBody_ && this->physicalBody_->isInWorld())
318            ThrowException(PhysicsViolation, "Warning: Cannot set the collision type at run time.");
319
320        // Check for type legality. Could be StaticEntity or MovableEntity
321        if (!this->isCollisionTypeLegal(type))
322            return; // exception gets issued anyway
323
324        // Check whether we have to create or destroy.
325        if (type != None && this->collisionType_ == None)
326        {
327            // Check whether there was some scaling applied.
328            if (!this->node_->getScale().positionEquals(Vector3(1, 1, 1), 0.001))
329                ThrowException(NotImplemented, "Cannot create a physical body if there is scaling applied to the node: Not yet implemented.");
330
331            // Create new rigid body
332            btCollisionShape* temp = 0;
333            if (this->collisionShape_)
334                temp = this->collisionShape_->getCollisionShape();
335            btRigidBody::btRigidBodyConstructionInfo bodyConstructionInfo(0, this, temp, btVector3(0,0,0));
336            this->physicalBody_ = new btRigidBody(bodyConstructionInfo);
337            this->physicalBody_->setUserPointer(this);
338        }
339        else if (type == None && this->collisionType_ != None)
340        {
341            // Destroy rigid body
342            if (this->physicalBody_->isInWorld())
343                this->getScene()->getPhysicalWorld()->removeRigidBody(this->physicalBody_);
344            delete this->physicalBody_;
345            this->physicalBody_ = 0;
346            this->collisionType_ = None;
347            return;
348        }
349
350        // Change type
351        switch (type)
352        {
353        case Dynamic:
354            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !(btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT));
355            break;
356        case Kinematic:
357            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT);
358            break;
359        case Static:
360            this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT);
361            break;
362        case None:
363            return; // this->collisionType_ was None too
364        }
365
366        // update our variable for faster checks
367        updateCollisionType();
368        assert(this->collisionType_ == type);
369
370        // update mass and inertia tensor
371        internalSetMassProps(); // type is not None! See case None: in switch
372    }
373
374    void WorldEntity::setCollisionTypeStr(const std::string& typeStr)
375    {
376        std::string typeStrLower = getLowercase(typeStr);
377        CollisionType type;
378        if (typeStrLower == "dynamic")
379            type = Dynamic;
380        else if (typeStrLower == "static")
381            type = Static;
382        else if (typeStrLower == "kinematic")
383            type = Kinematic;
384        else if (typeStrLower == "none")
385            type = None;
386        else
387            ThrowException(ParseError, std::string("Attempting to set an unknown collision type: '") + typeStr + "'.");
388        this->setCollisionType(type);
389    }
390
391    std::string WorldEntity::getCollisionTypeStr() const
392    {
393        switch (this->getCollisionType())
394        {
395            case Dynamic:
396                return "dynamic";
397            case Kinematic:
398                return "kinematic";
399            case Static:
400                return "static";
401            case None:
402                return "none";
403            default:
404                assert(false);
405                return "";
406        }
407    }
408
409    void WorldEntity::updateCollisionType()
410    {
411        if (!this->physicalBody_)
412            this->collisionType_ = None;
413        else if (this->physicalBody_->isKinematicObject())
414            this->collisionType_ = Kinematic;
415        else if (this->physicalBody_->isStaticObject())
416            this->collisionType_ = Static;
417        else
418            this->collisionType_ = Dynamic;
419    }
420
421    bool WorldEntity::checkPhysics() const
422    {
423        if (!this->physicalBody_)
424        {
425            assert(this->getCollisionType() == None);
426            COUT(2) << "WorldEntity was not fitted with physics, cannot set phyiscal property." << std::endl;
427            return false;
428        }
429        else
430            return true;
431    }
432}
Note: See TracBrowser for help on using the repository browser.