Browse Source

Updates physics properties definitions for collision objects.
All collision objects are now defined with the 'collisionObject' namespace and the type is specified using the 'type' attribute (set to 'CHARACTER', 'GHOST_OBJECT', 'RIGID_BODY').
Fixes a bug with physics status listeners and adds the PhysicsController::removeStatusListener function.
Fixes a bug with physics constraint cleanup.

Chris Culy 13 years ago
parent
commit
bb7a73550a

+ 24 - 13
gameplay/src/Node.cpp

@@ -1042,27 +1042,38 @@ PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
     SAFE_DELETE(_collisionObject);
 
     // Check if the properties is valid.
-    if (!properties || 
-        !(strcmp(properties->getNamespace(), "character") == 0 || 
-        strcmp(properties->getNamespace(), "ghostObject") == 0 || 
-        strcmp(properties->getNamespace(), "rigidBody") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load collision object from properties object: must be non-null object and have namespace equal to 'character', 'ghostObject', or 'rigidBody'.");
+        GP_ERROR("Failed to load collision object from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
         return NULL;
     }
 
-    if (strcmp(properties->getNamespace(), "character") == 0)
+    if (const char* type = properties->getString("type"))
     {
-        _collisionObject = PhysicsCharacter::create(this, properties);
-    }
-    else if (strcmp(properties->getNamespace(), "ghostObject") == 0)
-    {
-        _collisionObject = PhysicsGhostObject::create(this, properties);
+        if (strcmp(type, "CHARACTER") == 0)
+        {
+            _collisionObject = PhysicsCharacter::create(this, properties);
+        }
+        else if (strcmp(type, "GHOST_OBJECT") == 0)
+        {
+            _collisionObject = PhysicsGhostObject::create(this, properties);
+        }
+        else if (strcmp(type, "RIGID_BODY") == 0)
+        {
+            _collisionObject = PhysicsRigidBody::create(this, properties);
+        }
+        else
+        {
+            GP_ERROR("Unsupported collision object type '%s'.", type);
+            return NULL;
+        }
     }
-    else if (strcmp(properties->getNamespace(), "rigidBody") == 0)
+    else
     {
-        _collisionObject = PhysicsRigidBody::create(this, properties);
+        GP_ERROR("Failed to load collision object from properties object; required attribute 'type' is missing.");
+        return NULL;
     }
+    
     return _collisionObject;
 }
 

+ 15 - 2
gameplay/src/PhysicsCharacter.cpp

@@ -91,9 +91,22 @@ PhysicsCharacter::~PhysicsCharacter()
 PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
-    if (!properties || !(strcmp(properties->getNamespace(), "character") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load physics character from properties object: must be non-null object and have namespace equal to 'character'.");
+        GP_ERROR("Failed to load physics character from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
+        return NULL;
+    }
+
+    // Check that the type is specified and correct.
+    const char* type = properties->getString("type");
+    if (!type)
+    {
+        GP_ERROR("Failed to load physics character from properties object; required attribute 'type' is missing.");
+        return NULL;
+    }
+    if (strcmp(type, "CHARACTER") != 0)
+    {
+        GP_ERROR("Failed to load physics character from properties object; attribute 'type' must be equal to 'CHARACTER'.");
         return NULL;
     }
 

+ 14 - 17
gameplay/src/PhysicsCollisionShape.cpp

@@ -124,12 +124,9 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
     GP_ASSERT(node);
 
     // Check if the properties is valid and has a valid namespace.
-    if (!properties || 
-        !(strcmp(properties->getNamespace(), "character") == 0 || 
-        strcmp(properties->getNamespace(), "ghostObject") == 0 || 
-        strcmp(properties->getNamespace(), "rigidBody") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load physics collision shape from properties object: must be non-null object and have namespace equal to 'character', 'ghostObject', or 'rigidBody'.");
+        GP_ERROR("Failed to load physics collision shape from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
         return NULL;
     }
 
@@ -141,33 +138,33 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
     float height = -1.0f;
     bool centerIsAbsolute = false;
     const char* imagePath = NULL;
-    bool typeSpecified = false;
+    bool shapeSpecified = false;
 
     // Load the defined properties.
     properties->rewind();
     const char* name;
     while (name = properties->getNextProperty())
     {
-        if (strcmp(name, "type") == 0)
+        if (strcmp(name, "shape") == 0)
         {
-            std::string typeStr = properties->getString();
-            if (typeStr == "BOX")
+            std::string shapeStr = properties->getString();
+            if (shapeStr == "BOX")
                 type = SHAPE_BOX;
-            else if (typeStr == "SPHERE")
+            else if (shapeStr == "SPHERE")
                 type = SHAPE_SPHERE;
-            else if (typeStr == "MESH")
+            else if (shapeStr == "MESH")
                 type = SHAPE_MESH;
-            else if (typeStr == "HEIGHTFIELD")
+            else if (shapeStr == "HEIGHTFIELD")
                 type = SHAPE_HEIGHTFIELD;
-            else if (typeStr == "CAPSULE")
+            else if (shapeStr == "CAPSULE")
                 type = SHAPE_CAPSULE;
             else
             {
-                GP_ERROR("Could not create physics collision shape; unsupported value for collision shape type: '%s'.", typeStr.c_str());
+                GP_ERROR("Could not create physics collision shape; unsupported value for collision shape type: '%s'.", shapeStr.c_str());
                 return NULL;
             }
 
-            typeSpecified = true;
+            shapeSpecified = true;
         }
         else if (strcmp(name, "image") == 0)
         {
@@ -201,9 +198,9 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
         }
     }
 
-    if (!typeSpecified)
+    if (!shapeSpecified)
     {
-        GP_ERROR("Missing 'type' specifier for collision shape definition.");
+        GP_ERROR("Missing 'shape' specifier for collision shape definition.");
         return NULL;
     }
 

+ 22 - 0
gameplay/src/PhysicsController.cpp

@@ -45,6 +45,22 @@ void PhysicsController::addStatusListener(Listener* listener)
     _listeners->push_back(listener);
 }
 
+void PhysicsController::removeStatusListener(Listener* listener)
+{
+    GP_ASSERT(listener);
+    if (!_listeners)
+        return;
+
+    for (std::vector<Listener*>::iterator iter = _listeners->begin(); iter != _listeners->end(); iter++)
+    {
+        if (*iter == listener)
+        {
+            _listeners->erase(iter);
+            return;
+        }
+    }
+}
+
 PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
     checkConstraintRigidBodies(a, b);
@@ -1350,4 +1366,10 @@ int PhysicsController::DebugDrawer::getDebugMode() const
     return _mode;
 }
 
+PhysicsController::Listener::~Listener()
+{
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+    Game::getInstance()->getPhysicsController()->removeStatusListener(this);
+}
+
 }

+ 14 - 0
gameplay/src/PhysicsController.h

@@ -54,6 +54,13 @@ public:
          * Handles when a physics world status event occurs.
          */
         virtual void statusEvent(EventType type) = 0;
+
+    protected:
+
+        /**
+         * Destructor.
+         */
+        virtual ~Listener();
     };
 
     /**
@@ -89,6 +96,13 @@ public:
      */
     void addStatusListener(PhysicsController::Listener* listener);
 
+    /**
+     * Removes a listener to the physics controller.
+     * 
+     * @param listener The listener to remove.
+     */
+    void removeStatusListener(Listener* listener);
+
     /**
      * Creates a fixed constraint.
      * 

+ 15 - 2
gameplay/src/PhysicsGhostObject.cpp

@@ -47,9 +47,22 @@ PhysicsGhostObject::~PhysicsGhostObject()
 PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
-    if (!properties || !(strcmp(properties->getNamespace(), "ghostObject") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load ghost object from properties object: must be non-null object and have namespace equal to 'ghost'.");
+        GP_ERROR("Failed to load ghost object from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
+        return NULL;
+    }
+
+    // Check that the type is specified and correct.
+    const char* type = properties->getString("type");
+    if (!type)
+    {
+        GP_ERROR("Failed to load ghost object from properties object; required attribute 'type' is missing.");
+        return NULL;
+    }
+    if (strcmp(type, "GHOST_OBJECT") != 0)
+    {
+        GP_ERROR("Failed to load ghost object from properties object; attribute 'type' must be equal to 'GHOST_OBJECT'.");
         return NULL;
     }
 

+ 23 - 6
gameplay/src/PhysicsRigidBody.cpp

@@ -11,7 +11,7 @@ namespace gameplay
 {
 
 PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Definition& shape, const Parameters& parameters)
-        : PhysicsCollisionObject(node), _body(NULL), _mass(parameters.mass), _constraints(NULL)
+        : PhysicsCollisionObject(node), _body(NULL), _mass(parameters.mass), _constraints(NULL), _inDestructor(false)
 {
     GP_ASSERT(Game::getInstance()->getPhysicsController());
     GP_ASSERT(_node);
@@ -64,6 +64,7 @@ PhysicsRigidBody::~PhysicsRigidBody()
     GP_ASSERT(_node);
 
     // Clean up all constraints linked to this rigid body.
+    _inDestructor = true;
     if (_constraints)
     {
         for (unsigned int i = 0; i < _constraints->size(); ++i)
@@ -73,13 +74,13 @@ PhysicsRigidBody::~PhysicsRigidBody()
         SAFE_DELETE(_constraints);
     }
 
-    // Remove collision object from physics controller
+    // Remove collision object from physics controller.
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
     // Clean up the rigid body and its related objects.
     SAFE_DELETE(_body);
 
-    // Unregister node listener (only registered for heihgtfield collision shape types)
+    // Unregister node listener (only registered for heihgtfield collision shape types).
     if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
         _node->removeListener(this);
@@ -155,9 +156,22 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
-    if (!properties || !(strcmp(properties->getNamespace(), "rigidBody") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load rigid body from properties object: must be non-null object and have namespace equal to 'rigidBody'.");
+        GP_ERROR("Failed to load rigid body from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
+        return NULL;
+    }
+
+    // Check that the type is specified and correct.
+    const char* type = properties->getString("type");
+    if (!type)
+    {
+        GP_ERROR("Failed to load physics rigid body from properties object; required attribute 'type' is missing.");
+        return NULL;
+    }
+    if (strcmp(type, "RIGID_BODY") != 0)
+    {
+        GP_ERROR("Failed to load physics rigid body from properties object; attribute 'type' must be equal to 'RIGID_BODY'.");
         return NULL;
     }
 
@@ -290,7 +304,10 @@ void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
 
 void PhysicsRigidBody::removeConstraint(PhysicsConstraint* constraint)
 {
-    if (_constraints)
+    // Ensure that the rigid body has constraints and that we are
+    // not currently in the rigid body's destructor (in this case,
+    // the constraints will be destroyed from there).
+    if (_constraints && !_inDestructor)
     {
         for (unsigned int i = 0; i < _constraints->size(); ++i)
         {

+ 1 - 0
gameplay/src/PhysicsRigidBody.h

@@ -314,6 +314,7 @@ private:
     btRigidBody* _body;
     float _mass;
     std::vector<PhysicsConstraint*>* _constraints;
+    bool _inDestructor;
 
 };
 

+ 7 - 29
gameplay/src/SceneLoader.cpp

@@ -61,7 +61,7 @@ Scene* SceneLoader::load(const char* url)
         SceneNodeProperty::TRANSLATE | 
         SceneNodeProperty::TRANSPARENT |
         SceneNodeProperty::DYNAMIC);
-    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::CHARACTER | SceneNodeProperty::GHOSTOBJECT | SceneNodeProperty::RIGIDBODY);
+    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::COLLISION_OBJECT);
     createAnimations(scene);
 
     // Find the physics properties object.
@@ -197,9 +197,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
     if (snp._type == SceneNodeProperty::AUDIO ||
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::PARTICLE ||
-        snp._type == SceneNodeProperty::CHARACTER ||
-        snp._type == SceneNodeProperty::GHOSTOBJECT ||
-        snp._type == SceneNodeProperty::RIGIDBODY)
+        snp._type == SceneNodeProperty::COLLISION_OBJECT)
     {
         // Check to make sure the referenced properties object was loaded properly.
         Properties* p = _propertiesFromFile[snp._file];
@@ -255,9 +253,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             SAFE_RELEASE(particleEmitter);
             break;
         }
-        case SceneNodeProperty::CHARACTER:
-        case SceneNodeProperty::GHOSTOBJECT:
-        case SceneNodeProperty::RIGIDBODY:
+        case SceneNodeProperty::COLLISION_OBJECT:
         {
             // Check to make sure the referenced properties object was loaded properly.
             Properties* p = _propertiesFromFile[snp._file];
@@ -285,19 +281,9 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             }
 
             // Check to make sure the type of the namespace used to load the physics collision object is correct.
-            if (snp._type == SceneNodeProperty::CHARACTER && strcmp(p->getNamespace(), "character") != 0)
+            if (snp._type == SceneNodeProperty::COLLISION_OBJECT && strcmp(p->getNamespace(), "collisionObject") != 0)
             {
-                GP_ERROR("Attempting to set a 'character' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
-                return;
-            }
-            else if (snp._type == SceneNodeProperty::GHOSTOBJECT && strcmp(p->getNamespace(), "ghostObject") != 0)
-            {
-                GP_ERROR("Attempting to set a 'ghostObject' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
-                return;
-            }
-            else if (snp._type == SceneNodeProperty::RIGIDBODY && strcmp(p->getNamespace(), "rigidBody") != 0)
-            {
-                GP_ERROR("Attempting to set a 'rigidBody' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                GP_ERROR("Attempting to set a physics collision object on a node using a '%s' definition.", p->getNamespace());
                 return;
             }
             else
@@ -576,17 +562,9 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::PARTICLE, ns->getString());
                 }
-                else if (strcmp(name, "character") == 0)
-                {
-                    addSceneNodeProperty(sceneNode, SceneNodeProperty::CHARACTER, ns->getString());
-                }
-                else if (strcmp(name, "ghostObject") == 0)
-                {
-                    addSceneNodeProperty(sceneNode, SceneNodeProperty::GHOSTOBJECT, ns->getString());
-                }
-                else if (strcmp(name, "rigidBody") == 0)
+                else if (strcmp(name, "collisionObject") == 0)
                 {
-                    addSceneNodeProperty(sceneNode, SceneNodeProperty::RIGIDBODY, ns->getString());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::COLLISION_OBJECT, ns->getString());
                 }
                 else if (strcmp(name, "rigidBodyModel") == 0)
                 {

+ 7 - 9
gameplay/src/SceneLoader.h

@@ -49,15 +49,13 @@ private:
             AUDIO = 1,
             MATERIAL = 2,
             PARTICLE = 4,
-            CHARACTER = 8,
-            GHOSTOBJECT = 16,
-            RIGIDBODY = 32,
-            TRANSLATE = 64,
-            ROTATE = 128,
-            SCALE = 256,
-            URL = 512,
-            TRANSPARENT = 1024,
-            DYNAMIC = 2048
+            COLLISION_OBJECT = 8,
+            TRANSLATE = 16,
+            ROTATE = 32,
+            SCALE = 64,
+            URL = 128,
+            TRANSPARENT = 256,
+            DYNAMIC = 512
         };
 
         SceneNodeProperty(Type type, std::string file, std::string id, int index) : _type(type), _file(file), _id(id), _index(index) { }