Browse Source

Fixes issue where all references to a rigid body were not being removed properly.
Added a new draw function to SpriteBatch and added a boolean flag to two draw functions to allow specifying the destination position as the center of the quad (instead of the bottom left).
Fixes a memory leak in SpriteBatch.
Removed const-ness from PhysicsRigidBody::getNode() (allows users to modify the node in situations where the node is not already available, for example, from inside a collision listener).
Added isActive() function to ParticleEmitter (returns whether there are any active particles in the given emitter).
Changes ParticleEmitter to draw sprites using the destination position as the center (instead of the bottom left).

Chris Culy 14 năm trước cách đây
mục cha
commit
2b19085e6a

+ 22 - 2
gameplay/src/ParticleEmitter.cpp

@@ -235,6 +235,24 @@ bool ParticleEmitter::isStarted() const
     return _started;
 }
 
+bool ParticleEmitter::isActive() const
+{
+    if (_started)
+        return true;
+
+    bool active = false;
+    for (unsigned int i = 0; i < _particleCount; i++)
+    {
+        if (_particles[i]._energy > 0)
+        {
+            active = true;
+            break;
+        }
+    }
+
+    return active;
+}
+
 void ParticleEmitter::emit(unsigned int particleCount)
 {
     // Limit particleCount so as not to go over _particleCountMax.
@@ -867,7 +885,8 @@ void ParticleEmitter::draw()
             {
                 Particle* p = &_particles[i];
                 _spriteBatch->draw(p->_position.x, p->_position.y, p->_position.z, p->_size, p->_size,
-                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color);
+                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color,
+                                   true);
             }
         }
         else
@@ -879,7 +898,8 @@ void ParticleEmitter::draw()
             {
                 Particle* p = &_particles[i];
                 _spriteBatch->draw(p->_position, p->_size, p->_size,
-                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color, pivot, p->_angle);
+                                   _spriteTextureCoords[p->_frame * 4], _spriteTextureCoords[p->_frame * 4 + 1], _spriteTextureCoords[p->_frame * 4 + 2], _spriteTextureCoords[p->_frame * 4 + 3], p->_color, pivot, p->_angle,
+                                   true);
             }
         }
 

+ 7 - 0
gameplay/src/ParticleEmitter.h

@@ -212,6 +212,13 @@ public:
      */
     bool isStarted() const;
 
+    /**
+     * Gets whether this ParticleEmitter is currently active (i.e. if any of its particles are alive).
+     * 
+     * @return Whether this ParticleEmitter is currently active.
+     */
+    bool isActive() const;
+
     /**
      * Generates an arbitrary number of particles all at once.  Each newly emitted
      * particle has its properties assigned within the ranges defined by its ParticleEmitter.

+ 38 - 17
gameplay/src/PhysicsController.cpp

@@ -14,6 +14,7 @@ namespace gameplay
 const int PhysicsController::DIRTY         = 0x01;
 const int PhysicsController::COLLISION     = 0x02;
 const int PhysicsController::REGISTERED    = 0x04;
+const int PhysicsController::REMOVE        = 0x08;
 
 PhysicsController::PhysicsController()
   : _collisionConfiguration(NULL), _dispatcher(NULL),
@@ -158,8 +159,11 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
             std::vector<PhysicsRigidBody::Listener*>::const_iterator iter = collisionInfo._listeners.begin();
             for (; iter != collisionInfo._listeners.end(); iter++)
             {
-                (*iter)->collisionEvent(PhysicsRigidBody::Listener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
-                    Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
+                if ((collisionInfo._status & REMOVE) == 0)
+                {
+                    (*iter)->collisionEvent(PhysicsRigidBody::Listener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
+                        Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
+                }
             }
         }
     }
@@ -167,6 +171,9 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
     {
         CollisionInfo& collisionInfo = _collisionStatus[pair];
 
+        // Initialized the status for the new entry.
+        collisionInfo._status = 0;
+
         // Add the appropriate listeners.
         PhysicsRigidBody::CollisionPair p1(pair._rbA, NULL);
         if (_collisionStatus.count(p1) > 0)
@@ -192,8 +199,11 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
         std::vector<PhysicsRigidBody::Listener*>::iterator iter = collisionInfo._listeners.begin();
         for (; iter != collisionInfo._listeners.end(); iter++)
         {
-            (*iter)->collisionEvent(PhysicsRigidBody::Listener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
-                Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
+            if ((collisionInfo._status & REMOVE) == 0)
+            {
+                (*iter)->collisionEvent(PhysicsRigidBody::Listener::COLLIDING, pair, Vector3(cp.getPositionWorldOnA().x(), cp.getPositionWorldOnA().y(), cp.getPositionWorldOnA().z()),
+                    Vector3(cp.getPositionWorldOnB().x(), cp.getPositionWorldOnB().y(), cp.getPositionWorldOnB().z()));
+            }
         }
     }
 
@@ -297,12 +307,24 @@ void PhysicsController::update(long elapsedTime)
     // During collision processing, if a collision occurs, the status is 
     // set to COLLISION and the DIRTY bit is cleared. Then, after collision processing 
     // is finished, if a given status is still dirty, the COLLISION bit is cleared.
+    //
+    // If an entry was marked for removal in the last frame, remove it now.
 
     // Dirty the collision status cache entries.
     std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
-    for (; iter != _collisionStatus.end(); iter++)
+    for (; iter != _collisionStatus.end();)
     {
-        iter->second._status |= DIRTY;
+        if ((iter->second._status & REMOVE) != 0)
+        {
+            std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator eraseIter = iter;
+            iter++;
+            _collisionStatus.erase(eraseIter);
+        }
+        else
+        {
+            iter->second._status |= DIRTY;
+            iter++;
+        }
     }
 
     // Go through the collision status cache and perform all registered collision tests.
@@ -312,7 +334,7 @@ void PhysicsController::update(long elapsedTime)
         // If this collision pair was one that was registered for listening, then perform the collision test.
         // (In the case where we register for all collisions with a rigid body, there will be a lot
         // of collision pairs in the status cache that we did not explicitly register for.)
-        if ((iter->second._status & REGISTERED) != 0)
+        if ((iter->second._status & REGISTERED) != 0 && (iter->second._status & REMOVE) == 0)
         {
             if (iter->first._rbB)
                 _world->contactPairTest(iter->first._rbA->_body, iter->first._rbB->_body, *this);
@@ -343,8 +365,13 @@ void PhysicsController::update(long elapsedTime)
 
 void PhysicsController::addCollisionListener(PhysicsRigidBody::Listener* listener, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
 {
-    // Add the listener and ensure the status includes that this collision pair is registered.
     PhysicsRigidBody::CollisionPair pair(rbA, rbB);
+
+    // Make sure the status of the entry is initialized properly.
+    if (_collisionStatus.count(pair) == 0)
+        _collisionStatus[pair]._status = 0;
+
+    // Add the listener and ensure the status includes that this collision pair is registered.
     _collisionStatus[pair]._listeners.push_back(listener);
     if ((_collisionStatus[pair]._status & PhysicsController::REGISTERED) == 0)
         _collisionStatus[pair]._status |= PhysicsController::REGISTERED;
@@ -396,18 +423,12 @@ void PhysicsController::removeRigidBody(PhysicsRigidBody* rigidBody)
         }
     }
 
-    // Find all references to the rigid body in the collision status cache and remove them.
+    // Find all references to the rigid body in the collision status cache and mark them for removal.
     std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
-    for (; iter != _collisionStatus.end();)
+    for (; iter != _collisionStatus.end(); iter++)
     {
         if (iter->first._rbA == rigidBody || iter->first._rbB == rigidBody)
-        {
-            std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator eraseIter = iter;
-            iter++;
-            _collisionStatus.erase(eraseIter);
-        }
-        else
-            iter++;
+            iter->second._status |= REMOVE;
     }
 }
 

+ 1 - 0
gameplay/src/PhysicsController.h

@@ -210,6 +210,7 @@ private:
     static const int DIRTY;
     static const int COLLISION;
     static const int REGISTERED;
+    static const int REMOVE;
 
     // Represents the collision listeners and status for a given collision pair (used by the collision status cache).
     struct CollisionInfo

+ 1 - 1
gameplay/src/PhysicsRigidBody.h

@@ -217,7 +217,7 @@ public:
      * 
      * @return The node.
      */
-    inline const Node* getNode() const;
+    inline Node* getNode();
 
     /**
      * Gets the rigid body's restitution.

+ 1 - 1
gameplay/src/PhysicsRigidBody.inl

@@ -59,7 +59,7 @@ inline const Vector3& PhysicsRigidBody::getLinearVelocity() const
     return *_linearVelocity;
 }
 
-inline const Node* PhysicsRigidBody::getNode() const
+inline Node* PhysicsRigidBody::getNode()
 {
     return _node;
 }

+ 62 - 10
gameplay/src/SpriteBatch.cpp

@@ -69,6 +69,7 @@ SpriteBatch::SpriteBatch(const SpriteBatch& copy)
 SpriteBatch::~SpriteBatch()
 {
     SAFE_DELETE(_batch);
+    SAFE_RELEASE(__spriteEffect);
 }
 
 SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsigned int initialCapacity)
@@ -200,23 +201,33 @@ void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2&
 }
 
 void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
-                       const Vector2& rotationPoint, float rotationAngle)
+                       const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
 {
-    // Expand dst by scale into 4 points.
-    float x2 = dst.x + width;
-    float y2 = dst.y + height;
+    float x = dst.x;
+    float y = dst.y;
+
+    // Treat the given position as the center if the user specified it as such.
+    if (positionIsCenter)
+    {
+        x -= 0.5f * width;
+        y -= 0.5f * height;
+    }
+
+    // Expand the destination position by scale into 4 points.
+    float x2 = x + width;
+    float y2 = y + height;
     
-    Vector2 upLeft(dst.x, dst.y);
-    Vector2 upRight(x2, dst.y);
-    Vector2 downLeft(dst.x, y2);
+    Vector2 upLeft(x, y);
+    Vector2 upRight(x2, y);
+    Vector2 downLeft(x, y2);
     Vector2 downRight(x2, y2);
 
     // Rotate points around rotationAxis by rotationAngle.
     Vector2 pivotPoint(rotationPoint);
     pivotPoint.x *= width;
     pivotPoint.y *= height;
-    pivotPoint.x += dst.x;
-    pivotPoint.y += dst.y;
+    pivotPoint.x += x;
+    pivotPoint.y += y;
     upLeft.rotate(pivotPoint, rotationAngle);
     upRight.rotate(pivotPoint, rotationAngle);
     downLeft.rotate(pivotPoint, rotationAngle);
@@ -234,13 +245,54 @@ void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1,
     _batch->add(v, 4, indices, 4);
 }
 
+void SpriteBatch::draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height,
+    float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
+{
+    // Calculate the vertex positions.
+    Vector3 p[4];
+    p[0] = position - 0.5f * width * right;
+    p[1] = position + 0.5f * width * right;
+    p[2] = p[0] + height * forward;
+    p[3] = p[1] + height * forward;
+
+    // Calculate the rotation point.
+    Vector3 rp = p[0] + (rotationPoint.x * width * right) + (rotationPoint.y * height * forward);
+
+    // Rotate all points the specified amount about the given point (about the up vector).
+    Vector3 u;
+    Vector3::cross(right, forward, &u);
+    Matrix rotation;
+    Matrix::createRotation(u, rotationAngle, &rotation);
+    p[0] = (rotation * (p[0] - rp)) + rp;
+    p[1] = (rotation * (p[1] - rp)) + rp;
+    p[2] = (rotation * (p[2] - rp)) + rp;
+    p[3] = (rotation * (p[3] - rp)) + rp;
+
+    // Add the sprite vertex data to the batch.
+    static SpriteVertex v[4];
+    ADD_SPRITE_VERTEX(v[0], p[0].x, p[0].y, p[0].z, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[1], p[1].x, p[1].y, p[1].z, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[2], p[2].x, p[2].y, p[2].z, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[3], p[3].x, p[3].y, p[3].z, u2, v2, color.x, color.y, color.z, color.w);
+    
+    static const unsigned short indices[4] = { 0, 1, 2, 3 };
+    _batch->add(v, 4, const_cast<unsigned short*>(indices), 4);
+}
+
 void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color)
 {
     draw(x, y, 0, width, height, u1, v1, u2, v2, color);
 }
 
-void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color)
+void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, bool positionIsCenter)
 {
+    // Treat the given position as the center if the user specified it as such.
+    if (positionIsCenter)
+    {
+        x -= 0.5f * width;
+        y -= 0.5f * height;
+    }
+
     // Write sprite vertex data.
     float x2 = x + width;
     float y2 = y + height;

+ 24 - 2
gameplay/src/SpriteBatch.h

@@ -140,9 +140,30 @@ public:
      * @param rotationPoint The point to rotate around, relative to dst's x and y values.
      *                      (e.g. Use Vector2(0.5f, 0.5f) to rotate around the quad's center.)
      * @param rotationAngle The rotation angle.
+     * @param positionIsCenter Specified whether the given destination is to be the center of the sprite or not (if not, it is treated as the bottom-left).
      */
     void draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
-              const Vector2& rotationPoint, float rotationAngle);
+              const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter = false);
+    
+    /**
+     * Draws a single sprite, rotated about the implied up vector.
+     * 
+     * @param position The destination position.
+     * @param right The right vector of the sprite quad (should be normalized).
+     * @param forward The forward vector of the sprite quad (should be normalized).
+     * @param width The width of the sprite.
+     * @param height The height of the sprite.
+     * @param u1 Texture coordinate.
+     * @param v1 Texture coordinate.
+     * @param u2 Texture coordinate.
+     * @param v2 Texture coordinate.
+     * @param color The color to tint the sprite. Use white for no tint.
+     * @param rotationPoint The point to rotate around, relative to dst's x and y values.
+     *                      (e.g. Use Vector2(0.5f, 0.5f) to rotate around the quad's center.)
+     * @param rotationAngle The rotation angle.
+     */
+    void draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height, 
+        float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle);
 
     /**
      * Draws a single sprite.
@@ -172,8 +193,9 @@ public:
      * @param u2 Texture coordinate.
      * @param v2 Texture coordinate.
      * @param color The color to tint the sprite. Use white for no tint.
+     * @param positionIsCenter Specified whether the given destination is to be the center of the sprite or not (if not, it is treated as the bottom-left).
      */
-    void draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color);
+    void draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, bool positionIsCenter = false);
 
     /**
      * Ends sprite drawing.