Prechádzať zdrojové kódy

Merge branch 'next' of https://github.com/blackberry-gaming/GamePlay into next-ablake

Adam Blake 14 rokov pred
rodič
commit
3c021751fe

+ 23 - 15
gameplay/src/Animation.cpp

@@ -67,7 +67,6 @@ Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int p
 Animation::Channel::~Channel()
 {
     SAFE_DELETE(_curve);
-    _animation->removeChannel(this);
     SAFE_RELEASE(_animation);
 }
 
@@ -122,10 +121,10 @@ AnimationClip* Animation::getClip(const char* id)
     }
 }
 
-void Animation::play(const char* id)
+void Animation::play(const char* clipId)
 {
     // If id is NULL, play the default clip.
-    if (id == NULL)
+    if (clipId == NULL)
     {
         if (_defaultClip == NULL)
             createDefaultClip();
@@ -135,32 +134,41 @@ void Animation::play(const char* id)
     else
     {
         // Find animation clip.. and play.
-        AnimationClip* clip = findClip(id);
+        AnimationClip* clip = findClip(clipId);
         if (clip != NULL)
-        {
             clip->play();
-        }
     }
 }
 
-void Animation::stop(const char* id)
+void Animation::stop(const char* clipId)
 {
     // If id is NULL, play the default clip.
-    if (id == NULL)
+    if (clipId == NULL)
     {
-        if (_defaultClip == NULL)
-            createDefaultClip();
-
-        _defaultClip->stop();
+        if (_defaultClip)
+            _defaultClip->stop();
     }
     else
     {
         // Find animation clip.. and play.
-        AnimationClip* clip = findClip(id);
+        AnimationClip* clip = findClip(clipId);
         if (clip != NULL)
-        {
             clip->stop();
-        }
+    }
+}
+
+void Animation::pause(const char * clipId)
+{
+    if (clipId == NULL)
+    {
+        if (_defaultClip)
+            _defaultClip->pause();
+    }
+    else
+    {
+        AnimationClip* clip = findClip(clipId);
+        if (clip != NULL)
+            clip->pause();
     }
 }
 

+ 8 - 0
gameplay/src/Animation.h

@@ -82,6 +82,13 @@ public:
      */
     void stop(const char* clipId = NULL);
 
+    /** 
+     * Pauses the AnimationClip with the specified name.
+     *
+     * @param clipId The ID of the AnimationClip to pause. If NULL, pauses the default clip.
+     */
+    void pause(const char* clipId = NULL);
+
 private:
 
     /**
@@ -92,6 +99,7 @@ private:
      */
     class Channel
     {
+        friend class AnimationController;
         friend class AnimationClip;
         friend class Animation;
         friend class AnimationTarget;

+ 26 - 7
gameplay/src/AnimationClip.cpp

@@ -163,6 +163,13 @@ void AnimationClip::play()
 {
     if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
     {
+        // If paused, reset the bit and return.
+        if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
+        {
+            resetClipStateBit(CLIP_IS_PAUSED_BIT);
+            return;
+        }
+
         // If the clip is set to be removed, reset the flag.
         if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
             resetClipStateBit(CLIP_IS_MARKED_FOR_REMOVAL_BIT);
@@ -183,15 +190,23 @@ void AnimationClip::stop()
 {
     if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
     {
-        // If the clip was slated to be restarted, reset this flag.
-        if (isClipStateBitSet(CLIP_IS_RESTARTED_BIT))
-            resetClipStateBit(CLIP_IS_RESTARTED_BIT);
+        // Reset the restarted and paused bits. 
+        resetClipStateBit(CLIP_IS_RESTARTED_BIT);
+        resetClipStateBit(CLIP_IS_PAUSED_BIT);
 
         // Mark the clip to removed from the AnimationController.
         setClipStateBit(CLIP_IS_MARKED_FOR_REMOVAL_BIT);
     }
 }
 
+void AnimationClip::pause()
+{
+    if (isClipStateBitSet(CLIP_IS_PLAYING_BIT) && !isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
+    {
+        setClipStateBit(CLIP_IS_PAUSED_BIT);
+    }
+}
+
 void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
 {
     assert(clip);
@@ -291,7 +306,11 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
 
 bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*>* activeTargets)
 {
-    if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
+    if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
+    {
+        return false;
+    }
+    else if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT))
     {   // If the marked for removal bit is set, it means stop() was called on the AnimationClip at some point
         // after the last update call. Reset the flag, and return true so the AnimationClip is removed from the 
         // running clips on the AnimationController.
@@ -490,17 +509,17 @@ void AnimationClip::onEnd()
     }
 }
 
-bool AnimationClip::isClipStateBitSet(char bit) const
+bool AnimationClip::isClipStateBitSet(unsigned char bit) const
 {
     return (_stateBits & bit) == bit;
 }
 
-void AnimationClip::setClipStateBit(char bit)
+void AnimationClip::setClipStateBit(unsigned char bit)
 {
     _stateBits |= bit;
 }
 
-void AnimationClip::resetClipStateBit(char bit)
+void AnimationClip::resetClipStateBit(unsigned char bit)
 {
     _stateBits &= ~bit;
 }

+ 18 - 12
gameplay/src/AnimationClip.h

@@ -186,6 +186,11 @@ public:
      */
     void stop();
 
+    /**
+     * Pauses the AnimationClip.
+     */
+    void pause();
+
     /**
      * Fades this clip out, and the specified clip in over the given duration.
      *
@@ -223,14 +228,15 @@ private:
     /**
      * State bits.
      */
-    static const char CLIP_IS_PLAYING_BIT = 0x01;             // Bit representing whether AnimationClip is a running clip in AnimationController
-    static const char CLIP_IS_STARTED_BIT = 0x02;             // Bit representing whether the AnimationClip has actually been started (ie: received first call to update())
-    static const char CLIP_IS_FADING_OUT_STARTED_BIT = 0x04;  // Bit representing that a cross fade has started.
-    static const char CLIP_IS_FADING_OUT_BIT = 0x08;          // Bit representing whether the clip is fading out.
-    static const char CLIP_IS_FADING_IN_BIT = 0x10;           // Bit representing whether the clip is fading out.
-    static const char CLIP_IS_MARKED_FOR_REMOVAL_BIT = 0x20;  // Bit representing whether the clip has ended and should be removed from the AnimationController.
-    static const char CLIP_IS_RESTARTED_BIT = 0x40;           // Bit representing if the clip should be restarted by the AnimationController.
-    static const char CLIP_ALL_BITS = 0x7F;                   // Bit mask for all the state bits.
+    static const unsigned char CLIP_IS_PLAYING_BIT = 0x01;             // Bit representing whether AnimationClip is a running clip in AnimationController
+    static const unsigned char CLIP_IS_STARTED_BIT = 0x02;             // Bit representing whether the AnimationClip has actually been started (ie: received first call to update())
+    static const unsigned char CLIP_IS_FADING_OUT_STARTED_BIT = 0x04;  // Bit representing that a cross fade has started.
+    static const unsigned char CLIP_IS_FADING_OUT_BIT = 0x08;          // Bit representing whether the clip is fading out.
+    static const unsigned char CLIP_IS_FADING_IN_BIT = 0x10;           // Bit representing whether the clip is fading out.
+    static const unsigned char CLIP_IS_MARKED_FOR_REMOVAL_BIT = 0x20;  // Bit representing whether the clip has ended and should be removed from the AnimationController.
+    static const unsigned char CLIP_IS_RESTARTED_BIT = 0x40;           // Bit representing if the clip should be restarted by the AnimationController.
+    static const unsigned char CLIP_IS_PAUSED_BIT = 0x80;              // Bit representing if the clip is currently paused.
+    static const unsigned char CLIP_ALL_BITS = 0xFF;                   // Bit mask for all the state bits.
 
     /**
      * ListenerEvent.
@@ -291,24 +297,24 @@ private:
     /**
      * Determines whether the given bit is set in the AnimationClip's state.
      */
-    bool isClipStateBitSet(char bit) const;
+    bool isClipStateBitSet(unsigned char bit) const;
 
     /**
      * Sets the given bit in the AnimationClip's state.
      */
-    void setClipStateBit(char bit);
+    void setClipStateBit(unsigned char bit);
 
     /**
      * Resets the given bit in the AnimationClip's state.
      */
-    void resetClipStateBit(char bit);
+    void resetClipStateBit(unsigned char bit);
 
     std::string _id;                                    // AnimationClip ID.
     Animation* _animation;                              // The Animation this clip is created from.
     unsigned long _startTime;                           // Start time of the clip.
     unsigned long _endTime;                             // End time of the clip.
     unsigned long _duration;                            // The total duration.
-    char _stateBits;                                    // Bit flag used to keep track of the clip's current state.
+    unsigned char _stateBits;                           // Bit flag used to keep track of the clip's current state.
     float _repeatCount;                                 // The clip's repeat count.
     unsigned long _activeDuration;                      // The active duration of the clip.
     float _speed;                                       // The speed that the clip is playing. Default is 1.0. Negative goes in reverse.

+ 42 - 17
gameplay/src/AnimationController.cpp

@@ -13,7 +13,14 @@ AnimationController::AnimationController()
 
 AnimationController::~AnimationController()
 {
-    destroyAllAnimations();
+    std::vector<Animation*>::iterator itr = _animations.begin();
+    for ( ; itr != _animations.end(); itr++)
+    {
+        Animation* temp = *itr;
+        SAFE_RELEASE(temp);
+    }
+
+    _animations.clear();
 }
 
 Animation* AnimationController::createAnimation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, Curve::InterpolationType type)
@@ -111,12 +118,9 @@ void AnimationController::stopAllAnimations()
     while (clipIter != _runningClips.end())
     {
         AnimationClip* clip = *clipIter;
-        clipIter++;
         clip->stop();
+        clipIter++;
     }
-    _runningClips.clear();
-
-    _state = IDLE;
 }
 
 Animation* AnimationController::createAnimation(const char* id, AnimationTarget* target, Properties* animationProperties)
@@ -269,7 +273,13 @@ void AnimationController::initialize()
 
 void AnimationController::finalize()
 {
-    stopAllAnimations();
+    std::list<AnimationClip*>::iterator itr = _runningClips.begin();
+    for ( ; itr != _runningClips.end(); itr++)
+    {
+        AnimationClip* clip = *itr;
+        SAFE_RELEASE(clip);
+    }
+    _runningClips.clear();
     _state = STOPPED;
 }
 
@@ -366,30 +376,45 @@ void AnimationController::addAnimation(Animation* animation)
 
 void AnimationController::destroyAnimation(Animation* animation)
 {
-    std::vector<Animation*>::iterator itr = _animations.begin();
+    assert(animation);
+
+    std::vector<Animation::Channel*>::iterator cItr = animation->_channels.begin();
+    for (; cItr != animation->_channels.end(); cItr++)
+    {
+        Animation::Channel* channel = *cItr;
+        channel->_target->deleteChannel(channel);
+    }
 
-    while (itr != _animations.end())
+    std::vector<Animation*>::iterator aItr = _animations.begin();
+    while (aItr != _animations.end())
     {
-        if (animation == *itr)
+        if (animation == *aItr)
         {
-            Animation* animation = *itr;
-            _animations.erase(itr);
-            SAFE_RELEASE(animation);
+            Animation* temp = *aItr;
+            SAFE_RELEASE(temp);
+            _animations.erase(aItr);
             return;
         }
-        itr++;
+        aItr++;
     }
 }
 
 void AnimationController::destroyAllAnimations()
 {
-    std::vector<Animation*>::iterator itr = _animations.begin();
+    std::vector<Animation*>::iterator aItr = _animations.begin();
     
-    while (itr != _animations.end())
+    while (aItr != _animations.end())
     {
-        Animation* animation = *itr;
+        Animation* animation = *aItr;
+        std::vector<Animation::Channel*>::iterator cItr = animation->_channels.begin();
+        for (; cItr != animation->_channels.end(); cItr++)
+        {
+            Animation::Channel* channel = *cItr;
+            channel->_target->deleteChannel(channel);
+        }
+
         SAFE_RELEASE(animation);
-        itr++;
+        aItr++;
     }
 
     _animations.clear();

+ 10 - 10
gameplay/src/AnimationController.h

@@ -110,6 +110,16 @@ public:
      * Stops all AnimationClips currently playing on the AnimationController.
      */
     void stopAllAnimations();
+
+    /**
+     * Removes the given animation from this AnimationTarget.
+     */
+    void destroyAnimation(Animation* animation);
+
+    /**
+     * Removes all animations from the AnimationTarget.
+     */ 
+    void destroyAllAnimations();
        
 private:
 
@@ -193,16 +203,6 @@ private:
      * Adds an animation on this AnimationTarget.
      */ 
     void addAnimation(Animation* animation);
-
-    /**
-     * Removes the given animation from this AnimationTarget.
-     */
-    void destroyAnimation(Animation* animation);
-
-    /**
-     * Removes all animations from the AnimationTarget.
-     */ 
-    void destroyAllAnimations();
     
     State _state;                               // The current state of the AnimationController.
     std::list<AnimationClip*> _runningClips;    // A list of running AnimationClips.

+ 23 - 0
gameplay/src/AnimationTarget.cpp

@@ -19,6 +19,7 @@ AnimationTarget::~AnimationTarget()
         while (itr != _animationChannels->end())
         {
             Animation::Channel* channel = (*itr);
+            channel->_animation->removeChannel(channel);
             SAFE_DELETE(channel);
             itr++;
         }
@@ -95,6 +96,28 @@ int AnimationTarget::getPropertyId(TargetType type, const char* propertyIdStr)
     return -1;
 }
 
+void AnimationTarget::deleteChannel(Animation::Channel* channel)
+{
+    if (_animationChannels)
+    {
+        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+        for ( ; itr != _animationChannels->end(); itr++)
+        {
+            Animation::Channel* temp = *itr;
+            if (channel == temp)
+            {
+                SAFE_DELETE(channel);
+                _animationChannels->erase(itr);
+
+                if (_animationChannels->empty())
+                    SAFE_DELETE(_animationChannels);
+
+                return;
+            }
+        }
+    }
+}
+
 }
 
 

+ 2 - 0
gameplay/src/AnimationTarget.h

@@ -67,6 +67,8 @@ protected:
 
     void addChannel(Animation::Channel* animation);
 
+    void deleteChannel(Animation::Channel* channel);
+
     TargetType _targetType;             // The type of target this is.
 
     char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.

+ 4 - 4
gameplay/src/Node.cpp

@@ -718,12 +718,12 @@ void Node::setParticleEmitter(ParticleEmitter* emitter)
     }
 }
 
-PhysicsRigidBody* Node::getPhysicsRigidBody() const
+PhysicsRigidBody* Node::getRigidBody() const
 {
     return _physicsRigidBody;
 }
 
-void Node::setPhysicsRigidBody(PhysicsRigidBody::Type type, float mass, float friction,
+void Node::setRigidBody(PhysicsRigidBody::Type type, float mass, float friction,
         float restitution, float linearDamping, float angularDamping)
 {
     SAFE_DELETE(_physicsRigidBody);
@@ -732,14 +732,14 @@ void Node::setPhysicsRigidBody(PhysicsRigidBody::Type type, float mass, float fr
         _physicsRigidBody = new PhysicsRigidBody(this, type, mass, friction, restitution, linearDamping, angularDamping);
 }
 
-void Node::setPhysicsRigidBody(const char* filePath)
+void Node::setRigidBody(const char* filePath)
 {
     SAFE_DELETE(_physicsRigidBody);
 
     _physicsRigidBody = PhysicsRigidBody::create(this, filePath);
 }
 
-void Node::setPhysicsRigidBody(Properties* properties)
+void Node::setRigidBody(Properties* properties)
 {
     SAFE_DELETE(_physicsRigidBody);
 

+ 4 - 4
gameplay/src/Node.h

@@ -354,7 +354,7 @@ public:
      *
      * @return The pointer to this node's physics rigid body or NULL.
      */
-    PhysicsRigidBody* getPhysicsRigidBody() const;
+    PhysicsRigidBody* getRigidBody() const;
 
     /**
      * Sets (or disables) the physics rigid body for this node.
@@ -371,7 +371,7 @@ public:
      * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
      * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
      */
-    void setPhysicsRigidBody(PhysicsRigidBody::Type type, float mass = 0.0f, float friction = 0.5f,
+    void setRigidBody(PhysicsRigidBody::Type type, float mass = 0.0f, float friction = 0.5f,
         float restitution = 0.0f, float linearDamping = 0.0f, float angularDamping = 0.0f);
 
     /**
@@ -379,14 +379,14 @@ public:
      * 
      * @param filePath The path to the file that contains the rigid body definition.
      */
-    void setPhysicsRigidBody(const char* filePath);
+    void setRigidBody(const char* filePath);
 
     /**
      * Sets the physics rigid body for this node from the given properties object.
      * 
      * @param properties The properties object defining the rigid body (must have namespace equal to 'rigidbody').
      */
-    void setPhysicsRigidBody(Properties* properties);
+    void setRigidBody(Properties* properties);
 
     /**
      * Returns the bounding sphere for the Node, in world space.

+ 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.

+ 1 - 1
gameplay/src/PhysicsConstraint.cpp

@@ -155,7 +155,7 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 
 Vector3 PhysicsConstraint::offsetByCenterOfMass(const Node* node, const Vector3& v)
 {
-    btVector3 centerOfMassOffset = ((PhysicsMotionState*)node->getPhysicsRigidBody()->_body->getMotionState())->_centerOfMassOffset.getOrigin();
+    btVector3 centerOfMassOffset = ((PhysicsMotionState*)node->getRigidBody()->_body->getMotionState())->_centerOfMassOffset.getOrigin();
     return Vector3(v.x + centerOfMassOffset.x(), v.y + centerOfMassOffset.y(), v.z + centerOfMassOffset.z());
 }
 

+ 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/PhysicsMotionState.cpp

@@ -21,7 +21,7 @@ PhysicsMotionState::~PhysicsMotionState()
 
 void PhysicsMotionState::getWorldTransform(btTransform &transform) const
 {
-    if (_node->getPhysicsRigidBody() && _node->getPhysicsRigidBody()->isKinematic())
+    if (_node->getRigidBody() && _node->getRigidBody()->isKinematic())
         updateTransformFromNode();
 
     transform = _centerOfMassOffset.inverse() * _worldTransform;

+ 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;
 }

+ 4 - 4
gameplay/src/SceneLoader.cpp

@@ -385,7 +385,7 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
                         // Set the specified model during physics rigid body creation.
                         Model* model = node->getModel();
                         node->setModel(modelNode->getModel());
-                        node->setPhysicsRigidBody(p);
+                        node->setRigidBody(p);
                         node->setModel(model);
                     }
                 }
@@ -393,7 +393,7 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
             else if (!node->getModel())
                 WARN_VARG("Attempting to set a rigid body on node '%s', which has no model.", _nodeProperties[i]._nodeID);
             else
-                node->setPhysicsRigidBody(p);
+                node->setRigidBody(p);
         }
     }
 }
@@ -807,7 +807,7 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
                 WARN_VARG("Node '%s' to be used as 'rigidBodyA' for constraint %s cannot be found.", name, constraint->getId());
                 continue;
             }
-            PhysicsRigidBody* rbA = rbANode->getPhysicsRigidBody();
+            PhysicsRigidBody* rbA = rbANode->getRigidBody();
             if (!rbA)
             {
                 WARN_VARG("Node '%s' to be used as 'rigidBodyA' does not have a rigid body.", name);
@@ -828,7 +828,7 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
                     WARN_VARG("Node '%s' to be used as 'rigidBodyB' for constraint %s cannot be found.", name, constraint->getId());
                     continue;
                 }
-                rbB = rbBNode->getPhysicsRigidBody();
+                rbB = rbBNode->getRigidBody();
                 if (!rbB)
                 {
                     WARN_VARG("Node '%s' to be used as 'rigidBodyB' does not have a rigid body.", name);

+ 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)
@@ -205,23 +206,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);
@@ -239,13 +250,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.