Răsfoiți Sursa

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 ani în urmă
părinte
comite
bb7a73550a

+ 24 - 13
gameplay/src/Node.cpp

@@ -1042,27 +1042,38 @@ PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
     SAFE_DELETE(_collisionObject);
     SAFE_DELETE(_collisionObject);
 
 
     // Check if the properties is valid.
     // 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;
         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;
     return _collisionObject;
 }
 }
 
 

+ 15 - 2
gameplay/src/PhysicsCharacter.cpp

@@ -91,9 +91,22 @@ PhysicsCharacter::~PhysicsCharacter()
 PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
 PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // 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;
         return NULL;
     }
     }
 
 

+ 14 - 17
gameplay/src/PhysicsCollisionShape.cpp

@@ -124,12 +124,9 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
     GP_ASSERT(node);
     GP_ASSERT(node);
 
 
     // Check if the properties is valid and has a valid namespace.
     // 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;
         return NULL;
     }
     }
 
 
@@ -141,33 +138,33 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
     float height = -1.0f;
     float height = -1.0f;
     bool centerIsAbsolute = false;
     bool centerIsAbsolute = false;
     const char* imagePath = NULL;
     const char* imagePath = NULL;
-    bool typeSpecified = false;
+    bool shapeSpecified = false;
 
 
     // Load the defined properties.
     // Load the defined properties.
     properties->rewind();
     properties->rewind();
     const char* name;
     const char* name;
     while (name = properties->getNextProperty())
     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;
                 type = SHAPE_BOX;
-            else if (typeStr == "SPHERE")
+            else if (shapeStr == "SPHERE")
                 type = SHAPE_SPHERE;
                 type = SHAPE_SPHERE;
-            else if (typeStr == "MESH")
+            else if (shapeStr == "MESH")
                 type = SHAPE_MESH;
                 type = SHAPE_MESH;
-            else if (typeStr == "HEIGHTFIELD")
+            else if (shapeStr == "HEIGHTFIELD")
                 type = SHAPE_HEIGHTFIELD;
                 type = SHAPE_HEIGHTFIELD;
-            else if (typeStr == "CAPSULE")
+            else if (shapeStr == "CAPSULE")
                 type = SHAPE_CAPSULE;
                 type = SHAPE_CAPSULE;
             else
             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;
                 return NULL;
             }
             }
 
 
-            typeSpecified = true;
+            shapeSpecified = true;
         }
         }
         else if (strcmp(name, "image") == 0)
         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;
         return NULL;
     }
     }
 
 

+ 22 - 0
gameplay/src/PhysicsController.cpp

@@ -45,6 +45,22 @@ void PhysicsController::addStatusListener(Listener* listener)
     _listeners->push_back(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)
 PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
 {
     checkConstraintRigidBodies(a, b);
     checkConstraintRigidBodies(a, b);
@@ -1350,4 +1366,10 @@ int PhysicsController::DebugDrawer::getDebugMode() const
     return _mode;
     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.
          * Handles when a physics world status event occurs.
          */
          */
         virtual void statusEvent(EventType type) = 0;
         virtual void statusEvent(EventType type) = 0;
+
+    protected:
+
+        /**
+         * Destructor.
+         */
+        virtual ~Listener();
     };
     };
 
 
     /**
     /**
@@ -89,6 +96,13 @@ public:
      */
      */
     void addStatusListener(PhysicsController::Listener* listener);
     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.
      * Creates a fixed constraint.
      * 
      * 

+ 15 - 2
gameplay/src/PhysicsGhostObject.cpp

@@ -47,9 +47,22 @@ PhysicsGhostObject::~PhysicsGhostObject()
 PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
 PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // 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;
         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)
 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(Game::getInstance()->getPhysicsController());
     GP_ASSERT(_node);
     GP_ASSERT(_node);
@@ -64,6 +64,7 @@ PhysicsRigidBody::~PhysicsRigidBody()
     GP_ASSERT(_node);
     GP_ASSERT(_node);
 
 
     // Clean up all constraints linked to this rigid body.
     // Clean up all constraints linked to this rigid body.
+    _inDestructor = true;
     if (_constraints)
     if (_constraints)
     {
     {
         for (unsigned int i = 0; i < _constraints->size(); ++i)
         for (unsigned int i = 0; i < _constraints->size(); ++i)
@@ -73,13 +74,13 @@ PhysicsRigidBody::~PhysicsRigidBody()
         SAFE_DELETE(_constraints);
         SAFE_DELETE(_constraints);
     }
     }
 
 
-    // Remove collision object from physics controller
+    // Remove collision object from physics controller.
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
 
     // Clean up the rigid body and its related objects.
     // Clean up the rigid body and its related objects.
     SAFE_DELETE(_body);
     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)
     if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
     {
         _node->removeListener(this);
         _node->removeListener(this);
@@ -155,9 +156,22 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 {
 {
     // Check if the properties is valid and has a valid namespace.
     // 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;
         return NULL;
     }
     }
 
 
@@ -290,7 +304,10 @@ void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
 
 
 void PhysicsRigidBody::removeConstraint(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)
         for (unsigned int i = 0; i < _constraints->size(); ++i)
         {
         {

+ 1 - 0
gameplay/src/PhysicsRigidBody.h

@@ -314,6 +314,7 @@ private:
     btRigidBody* _body;
     btRigidBody* _body;
     float _mass;
     float _mass;
     std::vector<PhysicsConstraint*>* _constraints;
     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::TRANSLATE | 
         SceneNodeProperty::TRANSPARENT |
         SceneNodeProperty::TRANSPARENT |
         SceneNodeProperty::DYNAMIC);
         SceneNodeProperty::DYNAMIC);
-    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::CHARACTER | SceneNodeProperty::GHOSTOBJECT | SceneNodeProperty::RIGIDBODY);
+    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::COLLISION_OBJECT);
     createAnimations(scene);
     createAnimations(scene);
 
 
     // Find the physics properties object.
     // Find the physics properties object.
@@ -197,9 +197,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
     if (snp._type == SceneNodeProperty::AUDIO ||
     if (snp._type == SceneNodeProperty::AUDIO ||
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::PARTICLE ||
         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.
         // Check to make sure the referenced properties object was loaded properly.
         Properties* p = _propertiesFromFile[snp._file];
         Properties* p = _propertiesFromFile[snp._file];
@@ -255,9 +253,7 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             SAFE_RELEASE(particleEmitter);
             SAFE_RELEASE(particleEmitter);
             break;
             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.
             // Check to make sure the referenced properties object was loaded properly.
             Properties* p = _propertiesFromFile[snp._file];
             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.
             // 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;
                 return;
             }
             }
             else
             else
@@ -576,17 +562,9 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 {
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::PARTICLE, ns->getString());
                     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)
                 else if (strcmp(name, "rigidBodyModel") == 0)
                 {
                 {

+ 7 - 9
gameplay/src/SceneLoader.h

@@ -49,15 +49,13 @@ private:
             AUDIO = 1,
             AUDIO = 1,
             MATERIAL = 2,
             MATERIAL = 2,
             PARTICLE = 4,
             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) { }
         SceneNodeProperty(Type type, std::string file, std::string id, int index) : _type(type), _file(file), _id(id), _index(index) { }