[2072] | 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 | * Co-authors: |
---|
| 25 | * ... |
---|
| 26 | * |
---|
| 27 | */ |
---|
| 28 | |
---|
| 29 | #include "OrxonoxStableHeaders.h" |
---|
| 30 | #include "WorldEntity.h" |
---|
| 31 | |
---|
| 32 | #include <cassert> |
---|
| 33 | #include <OgreSceneManager.h> |
---|
[2292] | 34 | |
---|
[2192] | 35 | #include "BulletCollision/CollisionShapes/btSphereShape.h" |
---|
[2072] | 36 | |
---|
[2292] | 37 | #include "util/Exception.h" |
---|
| 38 | #include "util/Convert.h" |
---|
[2072] | 39 | #include "core/CoreIncludes.h" |
---|
| 40 | #include "core/XMLPort.h" |
---|
| 41 | |
---|
| 42 | #include "objects/Scene.h" |
---|
| 43 | |
---|
| 44 | namespace orxonox |
---|
| 45 | { |
---|
| 46 | const Vector3 WorldEntity::FRONT = Vector3::NEGATIVE_UNIT_Z; |
---|
| 47 | const Vector3 WorldEntity::BACK = Vector3::UNIT_Z; |
---|
| 48 | const Vector3 WorldEntity::LEFT = Vector3::NEGATIVE_UNIT_X; |
---|
| 49 | const Vector3 WorldEntity::RIGHT = Vector3::UNIT_X; |
---|
| 50 | const Vector3 WorldEntity::DOWN = Vector3::NEGATIVE_UNIT_Y; |
---|
| 51 | const Vector3 WorldEntity::UP = Vector3::UNIT_Y; |
---|
| 52 | |
---|
| 53 | WorldEntity::WorldEntity(BaseObject* creator) : BaseObject(creator), network::Synchronisable(creator) |
---|
| 54 | { |
---|
| 55 | RegisterObject(WorldEntity); |
---|
| 56 | |
---|
| 57 | assert(this->getScene()); |
---|
| 58 | assert(this->getScene()->getRootSceneNode()); |
---|
| 59 | |
---|
| 60 | this->node_ = this->getScene()->getRootSceneNode()->createChildSceneNode(); |
---|
| 61 | |
---|
| 62 | this->parent_ = 0; |
---|
| 63 | this->parentID_ = (unsigned int)-1; |
---|
| 64 | |
---|
| 65 | this->node_->setPosition(Vector3::ZERO); |
---|
| 66 | this->node_->setOrientation(Quaternion::IDENTITY); |
---|
| 67 | |
---|
[2192] | 68 | // Default behaviour does not include physics |
---|
| 69 | this->physicalBody_ = 0; |
---|
[2298] | 70 | updateCollisionType(); |
---|
[2192] | 71 | |
---|
[2072] | 72 | this->registerVariables(); |
---|
| 73 | } |
---|
| 74 | |
---|
| 75 | WorldEntity::~WorldEntity() |
---|
| 76 | { |
---|
| 77 | if (this->isInitialized()) |
---|
| 78 | { |
---|
| 79 | this->node_->detachAllObjects(); |
---|
| 80 | if (this->getScene()->getSceneManager()) |
---|
| 81 | this->getScene()->getSceneManager()->destroySceneNode(this->node_->getName()); |
---|
[2298] | 82 | |
---|
| 83 | this->setCollisionType(None); |
---|
[2072] | 84 | } |
---|
| 85 | } |
---|
| 86 | |
---|
| 87 | void WorldEntity::XMLPort(Element& xmlelement, XMLPort::Mode mode) |
---|
| 88 | { |
---|
| 89 | SUPER(WorldEntity, XMLPort, xmlelement, mode); |
---|
| 90 | |
---|
| 91 | XMLPortParamTemplate(WorldEntity, "position", setPosition, getPosition, xmlelement, mode, const Vector3&); |
---|
| 92 | XMLPortParamTemplate(WorldEntity, "orientation", setOrientation, getOrientation, xmlelement, mode, const Quaternion&); |
---|
| 93 | XMLPortParamLoadOnly(WorldEntity, "lookat", lookAt_xmlport, xmlelement, mode); |
---|
| 94 | XMLPortParamLoadOnly(WorldEntity, "direction", setDirection_xmlport, xmlelement, mode); |
---|
| 95 | XMLPortParamLoadOnly(WorldEntity, "yaw", yaw_xmlport, xmlelement, mode); |
---|
| 96 | XMLPortParamLoadOnly(WorldEntity, "pitch", pitch_xmlport, xmlelement, mode); |
---|
| 97 | XMLPortParamLoadOnly(WorldEntity, "roll", roll_xmlport, xmlelement, mode); |
---|
| 98 | XMLPortParamTemplate(WorldEntity, "scale3D", setScale3D, getScale3D, xmlelement, mode, const Vector3&); |
---|
| 99 | XMLPortParam(WorldEntity, "scale", setScale, getScale, xmlelement, mode); |
---|
| 100 | |
---|
[2298] | 101 | XMLPortParam(WorldEntity, "collisionType", setCollisionTypeStr, getCollisionTypeStr, xmlelement, mode); |
---|
[2292] | 102 | XMLPortParam(WorldEntity, "collisionRadius", setCollisionRadius, getCollisionRadius, xmlelement, mode); |
---|
| 103 | XMLPortParam(WorldEntity, "mass", setMass, getMass, xmlelement, mode); |
---|
| 104 | |
---|
[2298] | 105 | XMLPortObject(WorldEntity, WorldEntity, "attached", attach, getAttachedObject, xmlelement, mode); |
---|
| 106 | |
---|
| 107 | // Add the physical after loading because we cannot change its attributes without removing. |
---|
| 108 | if (getCollisionType() != None) |
---|
[2292] | 109 | this->getScene()->getPhysicalWorld()->addRigidBody(this->physicalBody_); |
---|
[2072] | 110 | } |
---|
| 111 | |
---|
| 112 | void WorldEntity::registerVariables() |
---|
| 113 | { |
---|
| 114 | REGISTERDATA(this->bActive_, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedActivity)); |
---|
| 115 | REGISTERDATA(this->bVisible_, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::changedVisibility)); |
---|
| 116 | |
---|
| 117 | REGISTERDATA(this->getScale3D().x, network::direction::toclient); |
---|
| 118 | REGISTERDATA(this->getScale3D().y, network::direction::toclient); |
---|
| 119 | REGISTERDATA(this->getScale3D().z, network::direction::toclient); |
---|
| 120 | |
---|
| 121 | REGISTERDATA(this->parentID_, network::direction::toclient, new network::NetworkCallback<WorldEntity>(this, &WorldEntity::updateParent)); |
---|
| 122 | } |
---|
| 123 | |
---|
| 124 | void WorldEntity::updateParent() |
---|
| 125 | { |
---|
| 126 | WorldEntity* parent = dynamic_cast<WorldEntity*>(Synchronisable::getSynchronisable(this->parentID_)); |
---|
| 127 | if (parent) |
---|
| 128 | this->attachToParent(parent); |
---|
| 129 | } |
---|
| 130 | |
---|
| 131 | void WorldEntity::attach(WorldEntity* object) |
---|
| 132 | { |
---|
| 133 | if (object->getParent()) |
---|
| 134 | object->detachFromParent(); |
---|
| 135 | else |
---|
| 136 | { |
---|
| 137 | Ogre::Node* parent = object->node_->getParent(); |
---|
| 138 | if (parent) |
---|
| 139 | parent->removeChild(object->node_); |
---|
| 140 | } |
---|
| 141 | |
---|
| 142 | this->node_->addChild(object->node_); |
---|
| 143 | this->children_.insert(object); |
---|
| 144 | object->parent_ = this; |
---|
| 145 | object->parentID_ = this->getObjectID(); |
---|
[2192] | 146 | |
---|
| 147 | // Do the physical connection if required |
---|
[2292] | 148 | //this->attachPhysicalObject(object); |
---|
[2072] | 149 | } |
---|
| 150 | |
---|
[2292] | 151 | //void WorldEntity::attachPhysicalObject(WorldEntity* object) |
---|
| 152 | //{ |
---|
| 153 | // StaticEntity* staticObject = dynamic_cast<StaticEntity*>(object); |
---|
| 154 | // if (staticObject != 0 && this->hasPhysics()) |
---|
| 155 | // { |
---|
| 156 | // btCompoundShape* compoundShape = dynamic_cast<btCompoundShape*>(this->physicalBody_->getCollisionShape()); |
---|
| 157 | // if (compoundShape == 0) |
---|
| 158 | // { |
---|
| 159 | // // create a compound shape and add both |
---|
| 160 | // compoundShape = new btCompoundShape(); |
---|
| 161 | // compoundShape->addChildShape(this->physicalBody_->getCollisionShape()); |
---|
| 162 | // compoundShape->addChildShape(staticObject->getCollisionShape()); |
---|
| 163 | // this->physicalBody_->setCollisionShape(); |
---|
| 164 | // } |
---|
| 165 | // else |
---|
| 166 | // { |
---|
| 167 | // compoundShape -> addChildShape(staticObject->getCollisionShape()); |
---|
| 168 | // } |
---|
| 169 | // } |
---|
| 170 | //} |
---|
[2201] | 171 | |
---|
[2072] | 172 | void WorldEntity::detach(WorldEntity* object) |
---|
| 173 | { |
---|
| 174 | this->node_->removeChild(object->node_); |
---|
| 175 | this->children_.erase(object); |
---|
| 176 | object->parent_ = 0; |
---|
| 177 | object->parentID_ = (unsigned int)-1; |
---|
| 178 | |
---|
| 179 | // this->getScene()->getRootSceneNode()->addChild(object->node_); |
---|
| 180 | } |
---|
| 181 | |
---|
| 182 | WorldEntity* WorldEntity::getAttachedObject(unsigned int index) const |
---|
| 183 | { |
---|
| 184 | unsigned int i = 0; |
---|
| 185 | for (std::set<WorldEntity*>::const_iterator it = this->children_.begin(); it != this->children_.end(); ++it) |
---|
| 186 | { |
---|
| 187 | if (i == index) |
---|
| 188 | return (*it); |
---|
| 189 | ++i; |
---|
| 190 | } |
---|
| 191 | return 0; |
---|
| 192 | } |
---|
[2192] | 193 | |
---|
[2292] | 194 | float WorldEntity::getMass() |
---|
[2192] | 195 | { |
---|
[2298] | 196 | if (!checkPhysics()) |
---|
[2292] | 197 | return 0.0f; |
---|
| 198 | |
---|
| 199 | return 1.0f/this->physicalBody_->getInvMass(); |
---|
| 200 | } |
---|
| 201 | |
---|
| 202 | void WorldEntity::setMass(float mass) |
---|
| 203 | { |
---|
[2298] | 204 | if (!checkPhysics()) |
---|
| 205 | return; |
---|
| 206 | else if (this->physicalBody_->isInWorld()) |
---|
| 207 | { |
---|
| 208 | CCOUT(2) << "Cannot set the physical mass at run time." << std::endl; |
---|
| 209 | assert(false); |
---|
| 210 | } |
---|
| 211 | else |
---|
| 212 | { |
---|
| 213 | this->physicalBody_->setMassProps(mass, btVector3(0,0,0)); |
---|
| 214 | updateCollisionType(); |
---|
| 215 | } |
---|
[2292] | 216 | } |
---|
| 217 | |
---|
[2298] | 218 | void WorldEntity::setCollisionType(CollisionType type) |
---|
[2292] | 219 | { |
---|
[2298] | 220 | // Check first whether we have to create or destroy. |
---|
| 221 | if (type != None && this->collisionType_ == None) |
---|
| 222 | { |
---|
| 223 | // Create new rigid body |
---|
| 224 | btRigidBody::btRigidBodyConstructionInfo bodyConstructionInfo(0, this, 0, btVector3(0,0,0)); |
---|
| 225 | this->physicalBody_ = new btRigidBody(bodyConstructionInfo); |
---|
| 226 | this->physicalBody_->setUserPointer(this); |
---|
[2292] | 227 | |
---|
[2298] | 228 | // Adjust parameters according to the node |
---|
| 229 | btTransform nodeTransform; |
---|
| 230 | //this-> |
---|
| 231 | } |
---|
| 232 | else if (type == None && this->collisionType_ != None) |
---|
| 233 | { |
---|
| 234 | // Destroy rigid body |
---|
| 235 | if (this->physicalBody_->isInWorld()) |
---|
| 236 | this->getScene()->getPhysicalWorld()->removeRigidBody(this->physicalBody_); |
---|
| 237 | if (this->physicalBody_->getCollisionShape()) |
---|
| 238 | delete this->physicalBody_->getCollisionShape(); |
---|
| 239 | delete this->physicalBody_; |
---|
| 240 | this->physicalBody_ = 0; |
---|
| 241 | this->collisionType_ = None; |
---|
| 242 | return; |
---|
| 243 | } |
---|
| 244 | |
---|
| 245 | // Check for type legality. Could be StaticEntity or MovableEntity |
---|
| 246 | if (!this->isCollisionTypeLegal(type)) |
---|
| 247 | return; // exception gets issued anyway |
---|
| 248 | |
---|
| 249 | // Change type |
---|
[2292] | 250 | switch (type) |
---|
| 251 | { |
---|
| 252 | case Dynamic: |
---|
| 253 | this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !(btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT)); |
---|
| 254 | break; |
---|
| 255 | case Kinematic: |
---|
| 256 | this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_STATIC_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT); |
---|
| 257 | break; |
---|
| 258 | case Static: |
---|
| 259 | this->physicalBody_->setCollisionFlags(this->physicalBody_->getCollisionFlags() & !btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT); |
---|
| 260 | break; |
---|
[2298] | 261 | case None: |
---|
| 262 | return; // this->collisionType_ was None too |
---|
[2292] | 263 | } |
---|
[2298] | 264 | |
---|
| 265 | // Mass non zero is a bad idea for kinematic and static objects |
---|
| 266 | if ((type == Kinematic || type == Static) && getMass() != 0.0f) |
---|
| 267 | this->setMass(0.0f); |
---|
| 268 | // Mass zero is not such a good idea for dynamic objects |
---|
| 269 | else if (type == Dynamic && getMass() == 0.0f) |
---|
| 270 | this->setMass(1.0f); |
---|
| 271 | |
---|
| 272 | // finally update our variable for faster checks |
---|
| 273 | updateCollisionType(); |
---|
[2292] | 274 | } |
---|
| 275 | |
---|
[2298] | 276 | void WorldEntity::updateCollisionType() |
---|
[2292] | 277 | { |
---|
| 278 | if (!this->physicalBody_) |
---|
[2298] | 279 | this->collisionType_ = None; |
---|
| 280 | else if (this->physicalBody_->isKinematicObject()) |
---|
| 281 | this->collisionType_ = Kinematic; |
---|
| 282 | else if (this->physicalBody_->isStaticObject()) |
---|
| 283 | this->collisionType_ = Static; |
---|
[2292] | 284 | else |
---|
[2298] | 285 | this->collisionType_ = Dynamic; |
---|
[2292] | 286 | } |
---|
| 287 | |
---|
[2298] | 288 | void WorldEntity::setCollisionTypeStr(const std::string& typeStr) |
---|
[2292] | 289 | { |
---|
[2298] | 290 | std::string typeStrLower = getLowercase(typeStr); |
---|
| 291 | CollisionType type; |
---|
| 292 | if (typeStrLower == "dynamic") |
---|
| 293 | type = Dynamic; |
---|
| 294 | else if (typeStrLower == "static") |
---|
| 295 | type = Static; |
---|
| 296 | else if (typeStrLower == "kinematic") |
---|
| 297 | type = Kinematic; |
---|
| 298 | else if (typeStrLower == "none") |
---|
| 299 | type = None; |
---|
[2292] | 300 | else |
---|
[2298] | 301 | ThrowException(ParseError, std::string("Attempting to set an unknown collision type: '") + typeStr + "'."); |
---|
| 302 | this->setCollisionType(type); |
---|
[2292] | 303 | } |
---|
| 304 | |
---|
| 305 | std::string WorldEntity::getCollisionTypeStr() |
---|
| 306 | { |
---|
| 307 | switch (this->getCollisionType()) |
---|
| 308 | { |
---|
[2298] | 309 | case Dynamic: |
---|
| 310 | return "dynamic"; |
---|
| 311 | case Kinematic: |
---|
| 312 | return "kinematic"; |
---|
| 313 | case Static: |
---|
| 314 | return "static"; |
---|
| 315 | case None: |
---|
| 316 | return "none"; |
---|
| 317 | default: |
---|
| 318 | assert(false); |
---|
| 319 | return ""; |
---|
[2292] | 320 | } |
---|
| 321 | } |
---|
| 322 | |
---|
| 323 | void WorldEntity::setCollisionRadius(float radius) |
---|
| 324 | { |
---|
[2298] | 325 | if (!checkPhysics()) |
---|
| 326 | return; |
---|
[2192] | 327 | |
---|
[2292] | 328 | // destroy old one first |
---|
[2192] | 329 | btCollisionShape* oldShape = this->physicalBody_->getCollisionShape(); |
---|
| 330 | if (oldShape) |
---|
| 331 | delete oldShape; |
---|
| 332 | |
---|
| 333 | this->physicalBody_->setCollisionShape(new btSphereShape(btScalar(radius))); |
---|
| 334 | } |
---|
| 335 | |
---|
[2292] | 336 | float WorldEntity::getCollisionRadius() |
---|
[2192] | 337 | { |
---|
[2298] | 338 | if (checkPhysics()) |
---|
[2192] | 339 | { |
---|
| 340 | btSphereShape* sphere = dynamic_cast<btSphereShape*>(this->physicalBody_->getCollisionShape()); |
---|
| 341 | if (sphere) |
---|
| 342 | return (float)sphere->getRadius(); |
---|
| 343 | } |
---|
| 344 | return 0.0f; |
---|
| 345 | } |
---|
[2298] | 346 | |
---|
| 347 | bool WorldEntity::checkPhysics() |
---|
| 348 | { |
---|
| 349 | if (!this->physicalBody_) |
---|
| 350 | { |
---|
| 351 | assert(this->getCollisionType() == None); |
---|
| 352 | COUT(2) << "WorldEntity was not fitted with physics, cannot set phyiscal property." << std::endl; |
---|
| 353 | return false; |
---|
| 354 | } |
---|
| 355 | else |
---|
| 356 | return true; |
---|
| 357 | } |
---|
[2072] | 358 | } |
---|