Просмотр исходного кода

Merge pull request #27 from blackberry-gaming/next

Next
Sean Paul Taylor 14 лет назад
Родитель
Сommit
942ea4f295

+ 2 - 2
gameplay.doxyfile

@@ -26,7 +26,7 @@ DOXYFILE_ENCODING      = UTF-8
 # identify the project. Note that if you do not use Doxywizard you need 
 # to put quotes around the project name if it contains spaces.
 
-PROJECT_NAME           = GamePlay
+PROJECT_NAME           = gameplay
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number. 
 # This could be handy for archiving the generated documentation or 
@@ -45,7 +45,7 @@ PROJECT_BRIEF          =
 # exceed 55 pixels and the maximum width should not exceed 200 pixels. 
 # Doxygen will copy the logo to the output directory.
 
-PROJECT_LOGO           = C:/Code/GamePlay/gameplay-logo.png
+PROJECT_LOGO           = 
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
 # base path where the generated documentation will be put. 

+ 99 - 48
gameplay/src/AnimationClip.cpp

@@ -10,9 +10,9 @@ namespace gameplay
 
 AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long startTime, unsigned long endTime)
     : _id(id), _animation(animation), _startTime(startTime), _endTime(endTime), _duration(_endTime - _startTime), _repeatCount(1.0f), 
-      _activeDuration(_duration * _repeatCount), _speed(1.0f), _isPlaying(false), _timeStarted(0), _elapsedTime(0), _runningTime(0), 
-      _crossFadeToClip(NULL), _crossFadeStart(0), _crossFadeOutElapsed(0), _crossFadeOutDuration(0), _blendWeight(1.0f), 
-      _isFadingOutStarted(false), _isFadingOut(false), _isFadingIn(false), _beginListeners(NULL), _endListeners(NULL)
+      _activeDuration(_duration * _repeatCount), _speed(1.0f), _isPlaying(false), _timeStarted(0), _elapsedTime(0), 
+      _crossFadeToClip(NULL), _crossFadeOutElapsed(0), _crossFadeOutDuration(0), _blendWeight(1.0f), _isFadingOutStarted(false), 
+      _isFadingOut(false), _isFadingIn(false), _beginListeners(NULL), _endListeners(NULL), _listeners(NULL)
 {
     assert(0 <= startTime && startTime <= animation->_duration && 0 <= endTime && endTime <= animation->_duration);
     
@@ -36,6 +36,7 @@ AnimationClip::~AnimationClip()
     SAFE_RELEASE(_crossFadeToClip);
     SAFE_DELETE(_beginListeners);
     SAFE_DELETE(_endListeners);
+    SAFE_DELETE(_listeners);
 }
 
 const char* AnimationClip::getID() const
@@ -151,37 +152,71 @@ void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
 {
     assert(clip);
 
-    if (clip->_isPlaying && clip->_isFadingOut)
-    {
-        clip->_isFadingOut = false;
-        clip->_crossFadeToClip->_isFadingIn = false;
-        SAFE_RELEASE(clip->_crossFadeToClip);
-    }
-
-    // If I already have a clip I'm fading too.. release it.
+    // If I already have a clip I'm fading to and it's not the same as the given clip release it.
+    // Assign the new clip and increase it's ref count.
+    // TODO: Do I need to anything else to the crossFade clip here before releasing?
     if (_crossFadeToClip)
         SAFE_RELEASE(_crossFadeToClip);
-
-    // Assign the clip we're fading to, and increase its ref count.
+        
     _crossFadeToClip = clip;
     _crossFadeToClip->addRef();
         
-    // Set the fade in clip to fading in, and set the duration of the fade in.
+    // Set the crossfade clip to fading in, and initialize it's blend weight to zero.
     _crossFadeToClip->_isFadingIn = true;
+    _crossFadeToClip->_blendWeight = 0.0f;
     
-    // Set this clip to fade out, and reset the elapsed time for the fade out.
+    // Set this clip to fade out, set the fade duration and reset the fade elapsed time.
     _isFadingOut = true;
     _crossFadeOutElapsed = 0;
     _crossFadeOutDuration = duration;
-    _crossFadeStart = (Game::getGameTime() - _timeStarted);
     _isFadingOutStarted = true;
     
+    // If this clip is currently not playing, we should start playing it.
     if (!_isPlaying)
         play();
 
+    // Start playing the cross fade clip.
     _crossFadeToClip->play(); 
 }
 
+void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long eventTime)
+{
+    assert(listener);
+    assert(eventTime < _duration);
+
+    listener->_listenerTime = eventTime;
+
+    if (!_listeners)
+    {
+        _listeners = new ListenerList;
+        _listeners->_list.push_front(listener);
+        if (_isPlaying)
+            _listeners->_listItr = _listeners->_list.begin();
+    }
+    else
+    {
+        for (std::list<Listener*>::iterator itr = _listeners->_list.begin(); itr != _listeners->_list.begin(); itr++)
+        {
+            if (eventTime < (*itr)->_listenerTime)
+            {
+                itr = _listeners->_list.insert(itr, listener);
+
+                // If playing, update the iterator if we need to.
+                // otherwise, it will just be set the next time the clip gets played.
+                if (_isPlaying)
+                {
+                    unsigned long currentTime = _elapsedTime % _duration;
+                    if ((_speed >= 0.0f && currentTime < eventTime && eventTime < (*_listeners->_listItr)->_listenerTime) || 
+                        (_speed <= 0 && currentTime > eventTime && eventTime > (*_listeners->_listItr)->_listenerTime))
+                        _listeners->_listItr = itr;
+                }
+
+                return;
+            }
+        }
+    }
+}
+
 void AnimationClip::addBeginListener(AnimationClip::Listener* listener)
 {
     if (!_beginListeners)
@@ -200,83 +235,92 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
 
 bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*>* activeTargets)
 {
-    float speed = _speed;
     if (!_isPlaying)
-    {
         onBegin();
-        _elapsedTime = Game::getGameTime() - _timeStarted;
-        _runningTime = _elapsedTime * speed;
-    }
-    else
-    {
-        // Update elapsed time.
-        _elapsedTime += elapsedTime;
-        _runningTime += elapsedTime * speed;
-    }
-
-    float percentComplete = 0.0f;
+    else 
+        _elapsedTime += elapsedTime * _speed;
 
+    unsigned long currentTime = 0L;
     // Check to see if clip is complete.
-    if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0 && _runningTime >= (long) _activeDuration) || (_speed < 0 && _runningTime <= 0)))
+    if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0.0f && _elapsedTime >= (long) _activeDuration) || (_speed <= 0.0f && _elapsedTime <= 0L)))
     {
         _isPlaying = false;
-        if (_speed >= 0)
+        if (_speed >= 0.0f)
         {
-            percentComplete = _activeDuration % _duration; // Get's the fractional part of the final repeat.
-            if (percentComplete == 0.0f)
-                percentComplete = _duration;
+            currentTime = _activeDuration % _duration; // Get's the fractional part of the final repeat.
+            if (currentTime == 0L)
+                currentTime = _duration;
         }
         else
         {
-            percentComplete = 0.0f; // If we are negative speed, the end value should be 0.
+            currentTime = 0L; // If we are negative speed, the end value should be 0.
         }
     }
     else
     {
         // Gets portion/fraction of the repeat.
-        percentComplete = (float) (_runningTime % _duration);
+        currentTime = _elapsedTime % _duration;
+    }
+
+    // Notify any listeners of Animation events.
+    if (_listeners)
+    {
+        while (_listeners->_listItr != _listeners->_list.end() && (_speed >= 0.0f && currentTime % _duration >= (*_listeners->_listItr)->_listenerTime) || 
+                                                     (_speed <= 0.0f && currentTime % _duration <= (*_listeners->_listItr)->_listenerTime))
+        {
+            
+            (*_listeners->_listItr)->animationEvent(this, Listener::DEFAULT);
+            (_listeners->_listItr)++;
+        }
     }
 
     // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
-    percentComplete = (float)(_startTime + percentComplete) / (float) _animation->_duration;
+    float percentComplete = (float)(_startTime + currentTime) / (float) _animation->_duration;
     
     if (_isFadingOut)
     {
         if (_isFadingOutStarted) // Calculate elapsed time since the fade out begin.
         {
-            _crossFadeOutElapsed = (_elapsedTime - _crossFadeStart) * speed;
+            _crossFadeOutElapsed = (Game::getGameTime() - _crossFadeToClip->_timeStarted) * _speed; 
             _isFadingOutStarted = false;
         }
         else
         {
             // continue tracking elapsed time.
-            _crossFadeOutElapsed += elapsedTime * speed;
+            _crossFadeOutElapsed += elapsedTime * abs(_speed);
         }
 
         if (_crossFadeOutElapsed < _crossFadeOutDuration)
         {
+            // Calculate this clip's blend weight.
             float tempBlendWeight = (float) (_crossFadeOutDuration - _crossFadeOutElapsed) / (float) _crossFadeOutDuration;
-            _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight);
             
-            // adjust the clip your blending to's weight to be a percentage of your current blend weight
+            // If this clip is fading in, adjust the crossfade clip's weight to be a percentage of your current blend weight
             if (_isFadingIn)
             {
-                _crossFadeToClip->_blendWeight *= _blendWeight;
+                _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight) * _blendWeight;
                 _blendWeight -= _crossFadeToClip->_blendWeight;
             }
             else
             {
+                // Just set the blend weight.
+                _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight);
                 _blendWeight = tempBlendWeight;
             }
         }
         else
-        {   // Fade done.
+        {   // Fade is done.
+            // Set the crossFadeToClip's blend weight to 1
             _crossFadeToClip->_blendWeight = 1.0f;
-                
+            
+            // Adjust the crossfade clip's blend weight if this clip is also fading in.
             if (_isFadingIn)
                 _crossFadeToClip->_blendWeight *= _blendWeight;
 
+            // The crossfade clip is no longer fading in
             _crossFadeToClip->_isFadingIn = false;
+
+            // Release the crossfade clip, mark ourselves as done and set our blend weight to 0.
             SAFE_RELEASE(_crossFadeToClip);
             _blendWeight = 0.0f; 
             _isFadingOut = false;
@@ -330,17 +374,24 @@ void AnimationClip::onBegin()
 {
     // Initialize animation to play.
     _isPlaying = true;
-    _elapsedTime = 0;
 
-    if (_speed > 0)
+    if (_speed >= 0)
     {
-        _runningTime = 0;
+        _elapsedTime = 0;
+
+        if (_listeners)
+            _listeners->_listItr = _listeners->_list.begin();
     }
     else
     {
-        _runningTime = _activeDuration;
+        _elapsedTime = _activeDuration;
+
+        if (_listeners)
+            _listeners->_listItr = _listeners->_list.end();
     }
 
+    _elapsedTime += (Game::getGameTime() - _timeStarted) * _speed;
+
     // Notify begin listeners.. if any.
     if (_beginListeners)
     {

+ 46 - 23
gameplay/src/AnimationClip.h

@@ -32,13 +32,25 @@ public:
      */
     class Listener
     {
+        friend AnimationClip;
+
     public:
 
+        Listener() 
+            : _listenerTime(0L)
+        {
+        }
+
         /**
          * The type of animation event.
          */
         enum EventType 
         {
+            /**
+             * Default event type.
+             */
+            DEFAULT,
+
             /**
              * Event fired when the clip begins.
              */
@@ -54,6 +66,9 @@ public:
          * Handles when animation event occurs.
          */
         virtual void animationEvent(AnimationClip* clip, EventType type) = 0;
+
+    private:
+        unsigned long _listenerTime;
     };
 
     /**
@@ -190,6 +205,8 @@ public:
      */
     void addEndListener(AnimationClip::Listener* listener);
 
+    void addListener(AnimationClip::Listener* listener, unsigned long time);
+
 private:
 
     /**
@@ -227,29 +244,35 @@ private:
      */
     void onEnd();
 
-    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.
-    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.
-    bool _isPlaying;                          // A flag to indicate whether the clip is playing.
-    unsigned long _timeStarted;               // The game time when this clip was actually started.
-    unsigned long _elapsedTime;               // Time elapsed while the clip is running.
-    long _runningTime;                        // Keeps track of the Animation's relative time in respect to the active duration.
-    AnimationClip* _crossFadeToClip;          // The clip to cross fade to
-    unsigned long _crossFadeStart;            // The time at which the cross fade started.
-    unsigned long _crossFadeOutElapsed;       // The amount of time that has elapsed for the crossfade.
-    unsigned long _crossFadeOutDuration;      // The duration of the cross fade.
-    float _blendWeight;                       // The clip's blendweight
-    bool _isFadingOutStarted;                 // Flag to indicate if the cross fade started
-    bool _isFadingOut;                        // Flag to indicate if the clip is fading out
-    bool _isFadingIn;                         // Flag to indicate if the clip is fading in.
-    std::vector<AnimationValue*> _values;     // AnimationValue holder.
-    std::vector<Listener*>* _beginListeners;  // Collection of begin listeners on the clip
-    std::vector<Listener*>* _endListeners;    // Collection of end listeners on the clip
+    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.
+    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.
+    bool _isPlaying;                                // A flag to indicate whether the clip is playing.
+    unsigned long _timeStarted;                     // The game time when this clip was actually started.
+    long _elapsedTime;                              // Time elapsed while the clip is running.
+    AnimationClip* _crossFadeToClip;                // The clip to cross fade to.
+    unsigned long _crossFadeOutElapsed;             // The amount of time that has elapsed for the crossfade.
+    unsigned long _crossFadeOutDuration;            // The duration of the cross fade.
+    float _blendWeight;                             // The clip's blendweight.
+    bool _isFadingOutStarted;                       // Flag to indicate if the cross fade started.
+    bool _isFadingOut;                              // Flag to indicate if the clip is fading out.
+    bool _isFadingIn;                               // Flag to indicate if the clip is fading in.
+    std::vector<AnimationValue*> _values;           // AnimationValue holder.
+    std::vector<Listener*>* _beginListeners;        // Collection of begin listeners on the clip.
+    std::vector<Listener*>* _endListeners;          // Collection of end listeners on the clip.
+
+    struct ListenerList
+    {
+        std::list<Listener*> _list;             // List of listeners.
+        std::list<Listener*>::iterator _listItr; // Pointer to the next listener event to be triggered.
+    };
+
+    ListenerList* _listeners;                        // Collection of listeners on the clip.
 
 };
 

+ 0 - 1
gameplay/src/AnimationTarget.cpp

@@ -2,7 +2,6 @@
 #include "AnimationTarget.h"
 #include "Animation.h"
 #include "Game.h"
-#include "Transform.h"
 
 namespace gameplay
 {

+ 13 - 4
gameplay/src/Curve.cpp

@@ -703,6 +703,11 @@ void Curve::evaluate(float time, float* dst) const
     interpolateLinear(t, from, to, dst);
 }
 
+float Curve::lerp(float t, float from, float to)
+{
+    return lerpInl(t, from, to);
+}
+
 void Curve::setQuaternionOffset(unsigned int offset)
 {
     assert(offset <= (_componentCount - 4));
@@ -1078,7 +1083,7 @@ void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
             if (fromValue[i] == toValue[i])
                 dst[i] = fromValue[i];
             else
-                dst[i] = lerp(s, fromValue[i], toValue[i]);
+                dst[i] = lerpInl(s, fromValue[i], toValue[i]);
         }
     }
     else
@@ -1091,7 +1096,7 @@ void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
             if (fromValue[i] == toValue[i])
                 dst[i] = fromValue[i];
             else
-                dst[i] = lerp(s, fromValue[i], toValue[i]);
+                dst[i] = lerpInl(s, fromValue[i], toValue[i]);
         }
 
         // Handle quaternion component.
@@ -1103,18 +1108,22 @@ void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
             if (fromValue[i] == toValue[i])
                 dst[i] = fromValue[i];
             else
-                dst[i] = lerp(s, fromValue[i], toValue[i]);
+                dst[i] = lerpInl(s, fromValue[i], toValue[i]);
         }
     }
 }
 
 void Curve::interpolateQuaternion(float s, float* from, float* to, float* dst) const
-{        
+{
     // Evaluate.
     if (s >= 0)
+    {
         Quaternion::slerp(from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], s, dst, dst + 1, dst + 2, dst + 3);
+    }
     else
         Quaternion::slerp(to[0], to[1], to[2], to[3], from[0], from[1], from[2], from[3], s, dst, dst + 1, dst + 2, dst + 3);
+
+    //((Quaternion*) dst)->normalize();
 }
 
 int Curve::determineIndex(float time) const

+ 11 - 6
gameplay/src/Curve.h

@@ -354,6 +354,11 @@ public:
      */
     void evaluate(float time, float* dst) const;
 
+    /**
+     * Linear interpolation function.
+     */
+    static float lerp(float t, float from, float to);
+
 private:
 
     /**
@@ -460,17 +465,17 @@ private:
     Point* _points;                     // The points on the curve.
 };
 
-inline float bezier(float eq0, float eq1, float eq2, float eq3, float from, float out, float to, float in);
+inline static float bezier(float eq0, float eq1, float eq2, float eq3, float from, float out, float to, float in);
 
-inline float bspline(float eq0, float eq1, float eq2, float eq3, float c0, float c1, float c2, float c3);
+inline static float bspline(float eq0, float eq1, float eq2, float eq3, float c0, float c1, float c2, float c3);
 
-inline float hermite(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
+inline static float hermite(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
 
-inline float hermiteFlat(float h00, float h01, float from, float to);
+inline static float hermiteFlat(float h00, float h01, float from, float to);
 
-inline float hermiteSmooth(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
+inline static float hermiteSmooth(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
 
-inline float lerp(float s, float from, float to);
+inline static float lerpInl(float s, float from, float to);
 
 }
 

+ 1 - 1
gameplay/src/Curve.inl

@@ -28,7 +28,7 @@ inline float hermiteSmooth(float h00, float h01, float h10, float h11, float fro
     return h00 * from + h01 * to + h10 * out + h11 * in;
 }
 
-inline float lerp(float s, float from, float to)
+inline float lerpInl(float s, float from, float to)
 {
     return from + (to - from) * s;
 }

+ 2 - 1
gameplay/src/Game.h

@@ -197,7 +197,8 @@ public:
      * Keyboard callback on keyPress events.
      *
      * @param evt The key event that occured.
-     * @param key The key code that was pressed, released or repeated.
+     * @param key If evt is KEY_PRESS or KEY_RELEASE then key is the key code from Keyboard::Key.
+     *            If evt is KEY_CHAR then key is the unicode value of the character.
      * 
      * @see Keyboard::KeyEvent
      * @see Keyboard::Key

+ 12 - 14
gameplay/src/Keyboard.h

@@ -20,7 +20,8 @@ public:
     enum KeyEvent
     {
         KEY_PRESS,
-        KEY_RELEASE
+        KEY_RELEASE,
+        KEY_CHAR
     };
 
     /**
@@ -40,15 +41,11 @@ public:
         KEY_BACK_TAB          = 0x0089,
         KEY_RETURN            = 0x000D,
         KEY_CAPS_LOCK         = 0x00E5,
-        KEY_LEFT_SHIFT        = 0x00E1,
-        KEY_RIGHT_SHIFT       = 0x00E2,
-        KEY_LEFT_CTRL         = 0x00E3,
-        KEY_RIGHT_CTRL        = 0x00E4,
-        KEY_LEFT_ALT          = 0x00E9,
-        KEY_RIGHT_ALT         = 0x00EA,
+        KEY_SHIFT             = 0x00E1,
+        KEY_CTRL              = 0x00E3,
+        KEY_ALT               = 0x00E9,
         KEY_MENU              = 0x1067,
-        KEY_LEFT_HYPER        = 0x10ED,
-        KEY_RIGHT_HYPER       = 0x10EE,
+        KEY_HYPER             = 0x10ED,
         KEY_INSERT            = 0x1063,
         KEY_HOME              = 0x1050,
         KEY_PG_UP             = 0x1055,
@@ -88,13 +85,13 @@ public:
         KEY_F10               = 0x00C7,
         KEY_F11               = 0x00C8,
         KEY_F12               = 0x00C9,
-        KEY_CIRCUMFLEX        = 0x005E,
         KEY_SPACE             = ' ',
         KEY_EXCLAM            = '!',
         KEY_QUOTE             = '"',
         KEY_NUMBER            = '#',
         KEY_DOLLAR            = '$',
         KEY_PERCENT           = '%',
+        KEY_CIRCUMFLEX        = '^',
         KEY_AMPERSAND         = '&',
         KEY_APOSTROPHE        = '\'',
         KEY_LEFT_PARENTHESIS  = '(',
@@ -183,10 +180,11 @@ public:
         KEY_BAR               = '|',
         KEY_RIGHT_BRACE       = '}',
         KEY_TILDE             = '~',
-        KEY_EURO,
-        KEY_POUND,
-        KEY_YEN,
-        KEY_MIDDLE_DOT
+        KEY_EURO              = 0x20AC,
+        KEY_POUND             = 0x00A3,
+        KEY_YEN               = 0x00A5,
+        KEY_MIDDLE_DOT        = 0x0095,
+        KEY_SEARCH            = 0xFFAA
     };
 
 private:

+ 17 - 64
gameplay/src/MaterialParameter.cpp

@@ -382,8 +382,6 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
 void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
     assert(blendWeight >= 0.0f && blendWeight <= 1.0f);
-    if (blendWeight == 0.0f)
-        return;
 
     switch (propertyId)
     {
@@ -398,18 +396,11 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
                         if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
                         {
                             _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
-
-                            if (blendWeight != 1.0f)
-                                _value.floatValue = value->getFloat(0) * blendWeight;
-                            else
-                                _value.floatValue = value->getFloat(0);
+                            _value.floatValue = value->getFloat(0);
                         }
                         else
                         {
-                            if (blendWeight != 1.0f)
-                                _value.floatValue += value->getFloat(0) * blendWeight;
-                            else
-                                _value.floatValue += value->getFloat(0);
+                            _value.floatValue = Curve::lerp(blendWeight, _value.floatValue, value->getFloat(0));
                         }
                     }
                     else
@@ -420,52 +411,30 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
                 }
                 case INT:
                 {
-                    if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
+                    if (_count == 1)
                     {
-                        _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
-
-                        if (_count == 1)
+                        if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
                         {
-                            if (blendWeight != 1.0f)
-                                _value.intValue = value->getFloat(0) * blendWeight;
-                            else
-                                _value.intValue = value->getFloat(0);
+                            _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
+                            _value.intValue = value->getFloat(0);
                         }
                         else
                         {
-                            if (blendWeight != 1.0f)
-                            {
-                                for (unsigned int i = 0; i < _count; i++)
-                                    _value.intPtrValue[i] = value->getFloat(i) * blendWeight;
-                            }
-                            else
-                            {
-                                for (unsigned int i = 0; i < _count; i++)
-                                    _value.intPtrValue[i] = value->getFloat(i);
-                            }
+                            _value.intValue = Curve::lerp(blendWeight, _value.intValue, value->getFloat(0));
                         }
                     }
                     else
                     {
-                        if (_count == 1)
+                        if ((_animationPropertyBitFlag & ANIMATION_UNIFORM_BIT) != ANIMATION_UNIFORM_BIT)
                         {
-                            if (blendWeight != 1.0f)
-                                _value.intValue += value->getFloat(0) * blendWeight;
-                            else
-                                _value.intValue += value->getFloat(0);
+                            _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
+                            for (unsigned int i = 0; i < _count; i++)
+                                _value.intPtrValue[i] = value->getFloat(i);
                         }
                         else
                         {
-                            if (blendWeight != 1.0f)
-                            {
-                                for (unsigned int i = 0; i < _count; i++)
-                                    _value.intPtrValue[i] += value->getFloat(i) * blendWeight;
-                            }
-                            else
-                            {
-                                for (unsigned int i = 0; i < _count; i++)
-                                    _value.intPtrValue[i] += value->getFloat(i);
-                            }
+                            for (unsigned int i = 0; i < _count; i++)
+                                _value.intPtrValue[i] = Curve::lerp(blendWeight, _value.intPtrValue[i], value->getFloat(i));
                         }
                     }
                     break;
@@ -499,29 +468,13 @@ void MaterialParameter::applyAnimationValue(AnimationValue* value, float blendWe
     {
         _animationPropertyBitFlag |= ANIMATION_UNIFORM_BIT;
 
-        if (blendWeight != 1.0f)
-        {
-            for (unsigned int i = 0; i < count; i++)
-                _value.floatPtrValue[i] = value->getFloat(i) * blendWeight;
-        }
-        else
-        {
-            for (unsigned int i = 0; i < count; i++)
-                _value.floatPtrValue[i] = value->getFloat(i);
-        }
+        for (unsigned int i = 0; i < count; i++)
+            _value.floatPtrValue[i] = value->getFloat(i);
     }
     else
     {
-        if (blendWeight != 1.0f)
-        {
-            for (unsigned int i = 0; i < count; i++)
-                _value.floatPtrValue[i] += (value->getFloat(i) * blendWeight);
-        }
-        else
-        {
-            for (unsigned int i = 0; i < count; i++)
-                _value.floatPtrValue[i] += value->getFloat(i);
-        }
+        for (unsigned int i = 0; i < count; i++)
+            _value.floatPtrValue[i] = Curve::lerp(blendWeight, _value.floatPtrValue[i], value->getFloat(i));
     }
 }
 

+ 145 - 17
gameplay/src/PlatformAndroid.cpp

@@ -357,13 +357,11 @@ Keyboard::Key getKey(int keycode, int metastate)
         case AKEYCODE_PERIOD:
             return Keyboard::KEY_PERIOD;
         case AKEYCODE_ALT_LEFT:
-            return Keyboard::KEY_LEFT_ALT;
         case AKEYCODE_ALT_RIGHT:
-            return Keyboard::KEY_RIGHT_ALT;
+            return Keyboard::KEY_ALT;
         case AKEYCODE_SHIFT_LEFT:
-            return Keyboard::KEY_LEFT_SHIFT;
         case AKEYCODE_SHIFT_RIGHT:
-            return Keyboard::KEY_RIGHT_SHIFT;
+            return Keyboard::KEY_SHIFT;
         case AKEYCODE_TAB:
             return Keyboard::KEY_TAB;
         case AKEYCODE_SPACE:
@@ -398,33 +396,161 @@ Keyboard::Key getKey(int keycode, int metastate)
             return Keyboard::KEY_PG_UP;
         case AKEYCODE_PAGE_DOWN:
             return Keyboard::KEY_PG_DOWN;
+        case AKEYCODE_MENU:
+            return Keyboard::KEY_MENU;
+        case AKEYCODE_SEARCH:
+            return Keyboard::KEY_SEARCH;
         default:
             return Keyboard::KEY_NONE;
     }
 }
 
+/**
+ * Returns the unicode value for the given keycode or zero if the key is not a valid printable character.
+ */
+static int getUnicode(int keycode, int metastate)
+{
+    if (keycode == AKEYCODE_DEL)
+        return 0x0008;
+    // TODO: Doesn't support unicode currently.
+    Keyboard::Key key = getKey(keycode, metastate);
+    switch (key)
+    {
+    case Keyboard::KEY_BACKSPACE:
+        return 0x0008;
+    case Keyboard::KEY_TAB:
+        return 0x0009;
+    case Keyboard::KEY_RETURN:
+    case Keyboard::KEY_KP_ENTER:
+        return 0x000A;
+    case Keyboard::KEY_ESCAPE:
+        return 0x001B;
+    case Keyboard::KEY_SPACE:
+    case Keyboard::KEY_EXCLAM:
+    case Keyboard::KEY_QUOTE:
+    case Keyboard::KEY_NUMBER:
+    case Keyboard::KEY_DOLLAR:
+    case Keyboard::KEY_PERCENT:
+    case Keyboard::KEY_CIRCUMFLEX:
+    case Keyboard::KEY_AMPERSAND:
+    case Keyboard::KEY_APOSTROPHE:
+    case Keyboard::KEY_LEFT_PARENTHESIS:
+    case Keyboard::KEY_RIGHT_PARENTHESIS:
+    case Keyboard::KEY_ASTERISK:
+    case Keyboard::KEY_PLUS:
+    case Keyboard::KEY_COMMA:
+    case Keyboard::KEY_MINUS:
+    case Keyboard::KEY_PERIOD:
+    case Keyboard::KEY_SLASH:
+    case Keyboard::KEY_ZERO:
+    case Keyboard::KEY_ONE:
+    case Keyboard::KEY_TWO:
+    case Keyboard::KEY_THREE:
+    case Keyboard::KEY_FOUR:
+    case Keyboard::KEY_FIVE:
+    case Keyboard::KEY_SIX:
+    case Keyboard::KEY_SEVEN:
+    case Keyboard::KEY_EIGHT:
+    case Keyboard::KEY_NINE:
+    case Keyboard::KEY_COLON:
+    case Keyboard::KEY_SEMICOLON:
+    case Keyboard::KEY_LESS_THAN:
+    case Keyboard::KEY_EQUAL:
+    case Keyboard::KEY_GREATER_THAN:
+    case Keyboard::KEY_QUESTION:
+    case Keyboard::KEY_AT:
+    case Keyboard::KEY_CAPITAL_A:
+    case Keyboard::KEY_CAPITAL_B:
+    case Keyboard::KEY_CAPITAL_C:
+    case Keyboard::KEY_CAPITAL_D:
+    case Keyboard::KEY_CAPITAL_E:
+    case Keyboard::KEY_CAPITAL_F:
+    case Keyboard::KEY_CAPITAL_G:
+    case Keyboard::KEY_CAPITAL_H:
+    case Keyboard::KEY_CAPITAL_I:
+    case Keyboard::KEY_CAPITAL_J:
+    case Keyboard::KEY_CAPITAL_K:
+    case Keyboard::KEY_CAPITAL_L:
+    case Keyboard::KEY_CAPITAL_M:
+    case Keyboard::KEY_CAPITAL_N:
+    case Keyboard::KEY_CAPITAL_O:
+    case Keyboard::KEY_CAPITAL_P:
+    case Keyboard::KEY_CAPITAL_Q:
+    case Keyboard::KEY_CAPITAL_R:
+    case Keyboard::KEY_CAPITAL_S:
+    case Keyboard::KEY_CAPITAL_T:
+    case Keyboard::KEY_CAPITAL_U:
+    case Keyboard::KEY_CAPITAL_V:
+    case Keyboard::KEY_CAPITAL_W:
+    case Keyboard::KEY_CAPITAL_X:
+    case Keyboard::KEY_CAPITAL_Y:
+    case Keyboard::KEY_CAPITAL_Z:
+    case Keyboard::KEY_LEFT_BRACKET:
+    case Keyboard::KEY_BACK_SLASH:
+    case Keyboard::KEY_RIGHT_BRACKET:
+    case Keyboard::KEY_UNDERSCORE:
+    case Keyboard::KEY_GRAVE:
+    case Keyboard::KEY_A:
+    case Keyboard::KEY_B:
+    case Keyboard::KEY_C:
+    case Keyboard::KEY_D:
+    case Keyboard::KEY_E:
+    case Keyboard::KEY_F:
+    case Keyboard::KEY_G:
+    case Keyboard::KEY_H:
+    case Keyboard::KEY_I:
+    case Keyboard::KEY_J:
+    case Keyboard::KEY_K:
+    case Keyboard::KEY_L:
+    case Keyboard::KEY_M:
+    case Keyboard::KEY_N:
+    case Keyboard::KEY_O:
+    case Keyboard::KEY_P:
+    case Keyboard::KEY_Q:
+    case Keyboard::KEY_R:
+    case Keyboard::KEY_S:
+    case Keyboard::KEY_T:
+    case Keyboard::KEY_U:
+    case Keyboard::KEY_V:
+    case Keyboard::KEY_W:
+    case Keyboard::KEY_X:
+    case Keyboard::KEY_Y:
+    case Keyboard::KEY_Z:
+    case Keyboard::KEY_LEFT_BRACE:
+    case Keyboard::KEY_BAR:
+    case Keyboard::KEY_RIGHT_BRACE:
+    case Keyboard::KEY_TILDE:
+        return key;
+    default:
+        return 0;
+    }
+}
+
 // Process the next input event.
 static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
 {
     if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION)
     {
         int32_t data = AMotionEvent_getAction(event);
-        int contactIndex = data >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
         Touch::TouchEvent touchEvent;
-        switch (data & AMOTION_EVENT_ACTION_MASK)
+        size_t pointerCount = AMotionEvent_getPointerCount(event);
+        for (size_t i = 0; i < pointerCount; ++i)
         {
-            case AMOTION_EVENT_ACTION_DOWN:
-                touchEvent = Touch::TOUCH_PRESS;
-                break;
-            case AMOTION_EVENT_ACTION_UP:
-                touchEvent = Touch::TOUCH_RELEASE;
-                break;
-            case AMOTION_EVENT_ACTION_MOVE:
-                touchEvent = Touch::TOUCH_MOVE;
-                break;
+            switch (data & AMOTION_EVENT_ACTION_MASK)
+            {
+                case AMOTION_EVENT_ACTION_DOWN:
+                    touchEvent = Touch::TOUCH_PRESS;
+                    break;
+                case AMOTION_EVENT_ACTION_UP:
+                    touchEvent = Touch::TOUCH_RELEASE;
+                    break;
+                case AMOTION_EVENT_ACTION_MOVE:
+                    touchEvent = Touch::TOUCH_MOVE;
+                    break;
+            }
+            size_t pointerId = AMotionEvent_getPointerId(event, i);
+            Game::getInstance()->touchEvent(touchEvent, AMotionEvent_getX(event, i), AMotionEvent_getY(event, i), pointerId);
         }
-    
-        Game::getInstance()->touchEvent(touchEvent, AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0), contactIndex);
         return 1;
     } 
     else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY)
@@ -437,6 +563,8 @@ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
         {
             case AKEY_EVENT_ACTION_DOWN:
                 Game::getInstance()->keyEvent(Keyboard::KEY_PRESS, getKey(keycode, metastate));
+                if (int character = getUnicode(keycode, metastate))
+                    Game::getInstance()->keyEvent(Keyboard::KEY_CHAR, character);
                 break;
                     
             case AKEY_EVENT_ACTION_UP:

+ 8 - 8
gameplay/src/PlatformMacOS.mm

@@ -452,28 +452,28 @@ int getKey(unsigned short keyCode, unsigned int modifierFlags)
             _game->keyEvent((flags & NSAlphaShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CAPS_LOCK);
             break;
         case 0x38:
-            _game->keyEvent((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_LEFT_SHIFT);
+            _game->keyEvent((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
             break;
         case 0x3C:
-            _game->keyEvent((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_RIGHT_SHIFT);
+            _game->keyEvent((flags & NSShiftKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_SHIFT);
             break;
         case 0x3A:
-            _game->keyEvent((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_LEFT_ALT);
+            _game->keyEvent((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
             break;
         case 0x3D:
-            _game->keyEvent((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_RIGHT_ALT);
+            _game->keyEvent((flags & NSAlternateKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_ALT);
             break;
         case 0x3B:
-            _game->keyEvent((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_LEFT_CTRL);
+            _game->keyEvent((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
             break;
         case 0x3E:
-            _game->keyEvent((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_RIGHT_CTRL);
+            _game->keyEvent((flags & NSControlKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_CTRL);
             break;
         case 0x37:
-            _game->keyEvent((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_LEFT_HYPER);
+            _game->keyEvent((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
             break;
         case 0x36:
-            _game->keyEvent((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_RIGHT_HYPER);
+            _game->keyEvent((flags & NSCommandKeyMask) ? Keyboard::KEY_PRESS : Keyboard::KEY_RELEASE, Keyboard::KEY_HYPER);
             break;
     }
 }

+ 54 - 9
gameplay/src/PlatformQNX.cpp

@@ -75,21 +75,17 @@ static Keyboard::Key getKey(int qnxKeycode)
     case KEYCODE_CAPS_LOCK:
         return Keyboard::KEY_CAPS_LOCK;
     case KEYCODE_LEFT_SHIFT:
-        return Keyboard::KEY_LEFT_SHIFT;
     case KEYCODE_RIGHT_SHIFT:
-        return Keyboard::KEY_RIGHT_SHIFT;
+        return Keyboard::KEY_SHIFT;
     case KEYCODE_LEFT_CTRL:
-        return Keyboard::KEY_LEFT_CTRL;
     case KEYCODE_RIGHT_CTRL:
-        return Keyboard::KEY_RIGHT_CTRL;
+        return Keyboard::KEY_CTRL;
     case KEYCODE_LEFT_ALT:
-        return Keyboard::KEY_LEFT_ALT;
     case KEYCODE_RIGHT_ALT:
-        return Keyboard::KEY_RIGHT_ALT;
+        return Keyboard::KEY_ALT;
     case KEYCODE_LEFT_HYPER:
-        return Keyboard::KEY_LEFT_HYPER;
     case KEYCODE_RIGHT_HYPER:
-        return Keyboard::KEY_RIGHT_HYPER;
+        return Keyboard::KEY_HYPER;
     case KEYCODE_INSERT:
         return Keyboard::KEY_INSERT;
     case KEYCODE_HOME:
@@ -252,6 +248,14 @@ static Keyboard::Key getKey(int qnxKeycode)
         return Keyboard::KEY_QUOTE;
     case KEYCODE_APOSTROPHE:
         return Keyboard::KEY_APOSTROPHE;
+    case 0x20AC:
+        return Keyboard::KEY_EURO;
+    case KEYCODE_POUND_SIGN:
+        return Keyboard::KEY_POUND;
+    case KEYCODE_YEN_SIGN:
+        return Keyboard::KEY_YEN;
+    case KEYCODE_MIDDLE_DOT:
+        return Keyboard::KEY_MIDDLE_DOT;
     case KEYCODE_CAPITAL_A:
         return Keyboard::KEY_CAPITAL_A;
     case KEYCODE_A:
@@ -361,6 +365,37 @@ static Keyboard::Key getKey(int qnxKeycode)
     }
 }
 
+/**
+ * Returns the unicode value from the given QNX key code value.
+ * Some non-printable characters also have corresponding unicode values, such as backspace.
+ *
+ * @param qnxKeyCode The keyboard key code.
+ *
+ * @return The unicode value or 0 if the keycode did not represent a unicode key.
+ */
+static int getUnicode(int qnxKeyCode)
+{
+    if (qnxKeyCode >= KEYCODE_PC_KEYS && qnxKeyCode <= UNICODE_PRIVATE_USE_AREA_LAST)
+    {
+        switch (qnxKeyCode)
+        {
+        case KEYCODE_BACKSPACE:
+            return 0x0008;
+        case KEYCODE_TAB:
+            return 0x0009;
+        case KEYCODE_KP_ENTER:
+        case KEYCODE_RETURN:
+            return 0x000A;
+        case KEYCODE_ESCAPE:
+            return 0x001B;
+        // Win32 doesn't consider delete to be a key char.
+        default:
+            return 0;
+        }
+    }
+    return qnxKeyCode;
+}
+
 extern void printError(const char* format, ...)
 {
     va_list argptr;
@@ -859,7 +894,17 @@ int Platform::enterMessagePump()
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_FLAGS, &flags);
                         screen_get_event_property_iv(__screenEvent, SCREEN_PROPERTY_KEY_SYM, &value);
                         gameplay::Keyboard::KeyEvent evt = (flags & KEY_DOWN) ? gameplay::Keyboard::KEY_PRESS :  gameplay::Keyboard::KEY_RELEASE;
-                        Game::getInstance()->keyEvent(evt, getKey(value));
+                        // Suppress key repeats
+                        if ((flags & KEY_REPEAT) == 0)
+                        {
+                            Game::getInstance()->keyEvent(evt, getKey(value));
+                            if (evt == gameplay::Keyboard::KEY_PRESS && flags & KEY_SYM_VALID)
+                            {
+                                int unicode = getUnicode(value);
+                                if (unicode)
+                                    Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_CHAR, unicode);
+                            }
+                        }
                         break;
                     }
                 }

+ 30 - 19
gameplay/src/PlatformWin32.cpp

@@ -45,22 +45,19 @@ static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
         return gameplay::Keyboard::KEY_RETURN;
     case VK_CAPITAL:
         return gameplay::Keyboard::KEY_CAPS_LOCK;
-    case VK_LSHIFT:
-        return gameplay::Keyboard::KEY_LEFT_SHIFT;
-    case VK_RSHIFT:
-        return gameplay::Keyboard::KEY_RIGHT_SHIFT;
-    case VK_LCONTROL:
-        return gameplay::Keyboard::KEY_LEFT_CTRL;
-    case VK_RCONTROL:
-        return gameplay::Keyboard::KEY_RIGHT_CTRL;
-    case VK_LMENU:
-        return gameplay::Keyboard::KEY_LEFT_ALT;
-    case VK_RMENU:
-        return gameplay::Keyboard::KEY_RIGHT_ALT;
+    case VK_SHIFT:
+        return gameplay::Keyboard::KEY_SHIFT;
+    case VK_CONTROL:
+        return gameplay::Keyboard::KEY_CTRL;
+    case VK_MENU:
+        return gameplay::Keyboard::KEY_ALT;
+    case VK_APPS:
+        return gameplay::Keyboard::KEY_MENU;
     case VK_LWIN:
-        return gameplay::Keyboard::KEY_LEFT_HYPER;
     case VK_RWIN:
-        return gameplay::Keyboard::KEY_RIGHT_HYPER;
+        return gameplay::Keyboard::KEY_HYPER;
+    case VK_BROWSER_SEARCH:
+        return gameplay::Keyboard::KEY_SEARCH;
     case VK_INSERT:
         return gameplay::Keyboard::KEY_INSERT;
     case VK_HOME:
@@ -172,7 +169,7 @@ static gameplay::Keyboard::Key getKey(WPARAM win32KeyCode, bool shiftDown)
     case VK_OEM_2:
         return shiftDown ? gameplay::Keyboard::KEY_QUESTION : gameplay::Keyboard::KEY_SLASH;
     case VK_OEM_3:
-        return shiftDown ? gameplay::Keyboard::KEY_GRAVE : gameplay::Keyboard::KEY_TILDE;
+        return shiftDown ? gameplay::Keyboard::KEY_TILDE : gameplay::Keyboard::KEY_GRAVE;
     case VK_OEM_4:
         return shiftDown ? gameplay::Keyboard::KEY_LEFT_BRACE : gameplay::Keyboard::KEY_LEFT_BRACKET;
     case VK_OEM_5:
@@ -351,19 +348,33 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         break;
 
     case WM_KEYDOWN:
-        if (wParam == VK_LSHIFT || wParam == VK_RSHIFT)
+        if (wParam == VK_SHIFT)
             shiftDown = true;
 
-        gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown));
+        // Suppress key repeats
+        if ((lParam & 0x40000000) == 0)
+            gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_PRESS, getKey(wParam, shiftDown));
         break;
-
+        
     case WM_KEYUP:
-        if (wParam == VK_LSHIFT || wParam == VK_RSHIFT)
+        if (wParam == VK_SHIFT)
             shiftDown = false;
 
         gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_RELEASE, getKey(wParam, shiftDown));
         break;
 
+    case WM_CHAR:
+        // Suppress key repeats
+        if ((lParam & 0x40000000) == 0)
+            gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_CHAR, wParam);
+        break;
+
+    case WM_UNICHAR:
+        // Suppress key repeats
+        if ((lParam & 0x40000000) == 0)
+            gameplay::Game::getInstance()->keyEvent(gameplay::Keyboard::KEY_CHAR, wParam);
+        break;
+
     case WM_SETFOCUS:
         break;
 

+ 56 - 183
gameplay/src/Transform.cpp

@@ -650,189 +650,85 @@ void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
 {
     assert(blendWeight >= 0.0f && blendWeight <= 1.0f);
-    if (blendWeight == 0.0f)
-        return;
 
     switch (propertyId)
     {
         case ANIMATE_SCALE_UNIT:
         {
-            float scale = value->getFloat(0);
-
-            if (blendWeight != 1.0f)
-                scale *= blendWeight;
-
-            applyAnimationValueScaleX(scale);
-            applyAnimationValueScaleY(scale);
-            applyAnimationValueScaleZ(scale);
-            
+            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
+            applyAnimationValueScaleY(value->getFloat(0), blendWeight);
+            applyAnimationValueScaleZ(value->getFloat(0), blendWeight);
             break;
         }   
         case ANIMATE_SCALE:
         {
-            float sx = value->getFloat(0);
-            float sy = value->getFloat(1);
-            float sz = value->getFloat(2);
-            if (blendWeight != 1.0f)
-            {
-                sx *= blendWeight;
-                sy *= blendWeight;
-                sz *= blendWeight;
-            }
-
-            applyAnimationValueScaleX(sx);
-            applyAnimationValueScaleY(sy);
-            applyAnimationValueScaleZ(sz);
-
+            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
+            applyAnimationValueScaleY(value->getFloat(1), blendWeight);
+            applyAnimationValueScaleZ(value->getFloat(2), blendWeight);
             break;
         }
         case ANIMATE_SCALE_X:
         {
-            float sx = value->getFloat(0);
-
-            if (blendWeight != 1.0f)
-                sx *= blendWeight;
-
-            applyAnimationValueScaleX(sx);
-
+            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
             break;
         }
         case ANIMATE_SCALE_Y:
         {
-            float sy = value->getFloat(0);
-
-            if (blendWeight != 1.0f)
-                sy *= blendWeight;
-
-            applyAnimationValueScaleY(sy);
-
+            applyAnimationValueScaleY(value->getFloat(0), blendWeight);
             break;
         }
         case ANIMATE_SCALE_Z:
         {
-            float sz = value->getFloat(0);
-
-            if (blendWeight != 1.0f)
-                sz *= blendWeight;
-
-            applyAnimationValueScaleZ(sz);
-
+            applyAnimationValueScaleZ(value->getFloat(0), blendWeight);
             break;
         }
         case ANIMATE_ROTATE:
         {
             Quaternion q(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
-
-            if (blendWeight != 1.0f)
-                Quaternion::slerp(Quaternion::identity(), q, blendWeight, &q);
-
-            applyAnimationValueRotation(&q);
-            
+            applyAnimationValueRotation(&q, blendWeight);
             break;
         }
         case ANIMATE_TRANSLATE:
         {
-            float tx = value->getFloat(0);
-            float ty = value->getFloat(1);
-            float tz = value->getFloat(2);
-
-            if (blendWeight != 1.0f)
-            {
-                tx *= blendWeight;
-                ty *= blendWeight;
-                tz *= blendWeight;
-            }
-
-            applyAnimationValueTranslationX(tx);
-            applyAnimationValueTranslationY(ty);
-            applyAnimationValueTranslationZ(tz);
-            
+            applyAnimationValueTranslationX(value->getFloat(0), blendWeight);
+            applyAnimationValueTranslationY(value->getFloat(1), blendWeight);
+            applyAnimationValueTranslationZ(value->getFloat(2), blendWeight);
             break;
         }
         case ANIMATE_TRANSLATE_X:
         {
-            float tx = value->getFloat(0);
-
-            if (blendWeight != 1.0f)
-                tx *= blendWeight;
-
-            applyAnimationValueTranslationX(tx);
-
+            applyAnimationValueTranslationX(value->getFloat(0), blendWeight);
             break;
         }
         case ANIMATE_TRANSLATE_Y:
         {
-            float ty = value->getFloat(0);
-
-            if (blendWeight != 1.0f)
-                ty *= blendWeight;
-            
-            applyAnimationValueTranslationY(ty);
-
+            applyAnimationValueTranslationY(value->getFloat(0), blendWeight);
             break;
         }
         case ANIMATE_TRANSLATE_Z:
         {
-            float tz = value->getFloat(0);
-
-            if (blendWeight != 1.0f)
-                tz *= blendWeight;
-
-            applyAnimationValueTranslationZ(tz);
-
+            applyAnimationValueTranslationZ(value->getFloat(0), blendWeight);
             break;
         }
         case ANIMATE_ROTATE_TRANSLATE:
         {
             Quaternion q(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
-            float tx = value->getFloat(4);
-            float ty = value->getFloat(5);
-            float tz = value->getFloat(6);
-
-            if (blendWeight != 1.0f)
-            {
-                Quaternion::slerp(Quaternion::identity(), q, blendWeight, &q);
-                tx *= blendWeight;
-                ty *= blendWeight;
-                tz *= blendWeight;
-            }
-
-            applyAnimationValueRotation(&q);
-            applyAnimationValueTranslationX(tx);
-            applyAnimationValueTranslationY(ty);
-            applyAnimationValueTranslationZ(tz);
-            
+            applyAnimationValueRotation(&q, blendWeight);
+            applyAnimationValueTranslationX(value->getFloat(4), blendWeight);
+            applyAnimationValueTranslationY(value->getFloat(5), blendWeight);
+            applyAnimationValueTranslationZ(value->getFloat(6), blendWeight);
             break;
         }
         case ANIMATE_SCALE_ROTATE_TRANSLATE:
         {
-            float sx = value->getFloat(0);
-            float sy = value->getFloat(1);
-            float sz = value->getFloat(2);
+            applyAnimationValueScaleX(value->getFloat(0), blendWeight);
+            applyAnimationValueScaleY(value->getFloat(1), blendWeight);
+            applyAnimationValueScaleZ(value->getFloat(2), blendWeight);
             Quaternion q(value->getFloat(3), value->getFloat(4), value->getFloat(5), value->getFloat(6));
-            float tx = value->getFloat(7);
-            float ty = value->getFloat(8);
-            float tz = value->getFloat(9);
-
-            if (blendWeight != 1.0f)
-            {
-                sx *= blendWeight;
-                sy *= blendWeight;
-                sz *= blendWeight;
-                Quaternion::slerp(Quaternion::identity(), q, blendWeight, &q);
-                tx *= blendWeight;
-                ty *= blendWeight;
-                tz *= blendWeight;
-            }
-
-            applyAnimationValueScaleX(sx);
-            applyAnimationValueScaleY(sy);
-            applyAnimationValueScaleZ(sz);
-            applyAnimationValueRotation(&q);
-            applyAnimationValueTranslationX(tx);
-            applyAnimationValueTranslationY(ty);
-            applyAnimationValueTranslationZ(tz);
-            
+            applyAnimationValueRotation(&q, blendWeight);
+            applyAnimationValueTranslationX(value->getFloat(7), blendWeight);
+            applyAnimationValueTranslationY(value->getFloat(8), blendWeight);
+            applyAnimationValueTranslationZ(value->getFloat(9), blendWeight);
             break;
         }
         default:
@@ -884,97 +780,74 @@ void Transform::transformChanged()
     }
 }
 
-void Transform::applyAnimationValueScaleX(float sx)
+void Transform::applyAnimationValueScaleX(float sx, float blendWeight)
 {
     if ((_animationPropertyBitFlag & ANIMATION_SCALE_X_BIT) != ANIMATION_SCALE_X_BIT)
-    {
         _animationPropertyBitFlag |= ANIMATION_SCALE_X_BIT;
-        setScaleX(sx);
-    }
     else
-    {
-        _scale.x += sx;
-        dirty();
-    }
+        sx = Curve::lerp(blendWeight, _scale.x, sx);
+
+    setScaleX(sx);
 }
 
-void Transform::applyAnimationValueScaleY(float sy)
+void Transform::applyAnimationValueScaleY(float sy, float blendWeight)
 {
     if ((_animationPropertyBitFlag & ANIMATION_SCALE_Y_BIT) != ANIMATION_SCALE_Y_BIT)
-    {
         _animationPropertyBitFlag |= ANIMATION_SCALE_Y_BIT;
-        setScaleY(sy);
-    }
     else
-    {
-        _scale.y += sy;
-        dirty();
-    }
+        sy = Curve::lerp(blendWeight, _scale.y, sy);
+
+    setScaleY(sy);
 }
 
-void Transform::applyAnimationValueScaleZ(float sz)
+void Transform::applyAnimationValueScaleZ(float sz, float blendWeight)
 {
     if ((_animationPropertyBitFlag & ANIMATION_SCALE_Z_BIT) != ANIMATION_SCALE_Z_BIT)
-    {
         _animationPropertyBitFlag |= ANIMATION_SCALE_Z_BIT;
-        setScaleZ(sz);
-    }
     else
-    {
-        _scale.z += sz;
-        dirty();
-    }
+        sz = Curve::lerp(blendWeight, _scale.z, sz);
+
+    setScaleZ(sz);
 }
 
-void Transform::applyAnimationValueRotation(Quaternion* q)
+void Transform::applyAnimationValueRotation(Quaternion* q, float blendWeight)
 {
     if ((_animationPropertyBitFlag & ANIMATION_ROTATION_BIT) != ANIMATION_ROTATION_BIT)
-    {
         _animationPropertyBitFlag |= ANIMATION_ROTATION_BIT;
-        setRotation(*q);
-    }
     else
-    {
-        rotate(*q);
-    }
+        Quaternion::slerp(_rotation, *q, blendWeight, q);
+     
+    setRotation(*q);
 }
 
-void Transform::applyAnimationValueTranslationX(float tx)
+void Transform::applyAnimationValueTranslationX(float tx, float blendWeight)
 {
     if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_X_BIT) != ANIMATION_TRANSLATION_X_BIT)
-    {
         _animationPropertyBitFlag |= ANIMATION_TRANSLATION_X_BIT;
-        setTranslationX(tx);
-    }
     else
-    {
-        translateX(tx);
-    }
+        tx = Curve::lerp(blendWeight, _translation.x, tx);
+
+    setTranslationX(tx);
 }
 
-void Transform::applyAnimationValueTranslationY(float ty)
+void Transform::applyAnimationValueTranslationY(float ty, float blendWeight)
 {
     if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_Y_BIT) != ANIMATION_TRANSLATION_Y_BIT)
-    {
         _animationPropertyBitFlag |= ANIMATION_TRANSLATION_Y_BIT;
-        setTranslationY(ty);
-    }
     else
-    {
-        translateY(ty);
-    }
+        ty = Curve::lerp(blendWeight, _translation.y, ty);
+
+    setTranslationY(ty);
 }
 
-void Transform::applyAnimationValueTranslationZ(float tz)
+void Transform::applyAnimationValueTranslationZ(float tz, float blendWeight)
 {
     if ((_animationPropertyBitFlag & ANIMATION_TRANSLATION_Z_BIT) != ANIMATION_TRANSLATION_Z_BIT)
-    {
         _animationPropertyBitFlag |= ANIMATION_TRANSLATION_Z_BIT;
-        setTranslationZ(tz);
-    }
     else
-    {
-        translateZ(tz);
-    }
+        tz = Curve::lerp(blendWeight, _translation.z, tz);
+
+    setTranslationZ(tz);
 }
+
 }

+ 7 - 7
gameplay/src/Transform.h

@@ -754,13 +754,13 @@ private:
     static const char ANIMATION_TRANSLATION_Y_BIT = 0x20; 
     static const char ANIMATION_TRANSLATION_Z_BIT = 0x40; 
 
-    void applyAnimationValueScaleX(float sx);
-    void applyAnimationValueScaleY(float sy);
-    void applyAnimationValueScaleZ(float sz);
-    void applyAnimationValueRotation(Quaternion* q);
-    void applyAnimationValueTranslationX(float tx);
-    void applyAnimationValueTranslationY(float ty);
-    void applyAnimationValueTranslationZ(float tz);
+    void applyAnimationValueScaleX(float sx, float blendWeight);
+    void applyAnimationValueScaleY(float sy, float blendWeight);
+    void applyAnimationValueScaleZ(float sz, float blendWeight);
+    void applyAnimationValueRotation(Quaternion* q, float blendWeight);
+    void applyAnimationValueTranslationX(float tx, float blendWeight);
+    void applyAnimationValueTranslationY(float ty, float blendWeight);
+    void applyAnimationValueTranslationZ(float tz, float blendWeight);
 };
 
 }

+ 8 - 8
gameplay/src/gameplay.dox

@@ -1,8 +1,8 @@
-/** \mainpage GamePlay - 3D Game Framework
+/** \mainpage gameplay - 3D Game Framework
  *
  * \section intro Introduction
  *
- * GamePlay is a cross-platform, C++, 3D gaming framework that includes a runtime library, tools,
+ * The gameplay framework is a cross-platform, C++, 3D game framework that includes a runtime library, tools,
  * and learning content that allows developers to write games for mobile and desktop platforms
  * without worrying about platform details.
  *
@@ -12,12 +12,12 @@
  *
  * \section Project Features
  *
- * The GamePlay C++ runtime library offers a simple, well-defined, 3D gaming framework that's
+ * The gameplay C++ runtime library offers a simple, well-defined, 3D game framework that's
  * designed to get the most out of today's mobile and desktop platforms. Its purpose is to help you
  * create stunning, world-class, games that harness the power and performance of the platform without
  * being concerned about the platform details. This is accomplished through a set of C++ classes that
  * are built on top of the OS, providing access to the hardware, graphics, and audio libraries
- * (including EGL, OpenGL ES 2.0, and OpenAL 1.1). These classes allow you to:
+ * (including OpenGL ES 2.0, and OpenAL 1.1). These classes allow you to:
  *
  * <b>Build your game without worrying about platform details</b>
  * \li Drive your game with application initialization, update, and render callbacks.
@@ -25,15 +25,15 @@
  * \li Manage your game with control over the device's file system.
  *
  * <b>Create game components with ease</b>
- * \li Add visuals with scene, node, camera, light, mesh, texture, sprite, materials, animation, and physics classes.
+ * \li Add visuals with scene, node, camera, lights, mesh, texture, sprite, materials, animation, and physics classes.
  * \li Control and position the game using the viewport, camera, and audio listener.
  * \li Manage fundamental elements such as fonts, colors, and curves.
- * \li Update game data with built-in math classes for vectors, matrices, rays, planes, and their associated operations.
+ * \li Update game data with built-in math classes for vectors, matrices, rays, planes, bounding volumes and their associated operations.
  *
  * <b>Improve your game and learn</b>
- * \li Supports TrueType fonts and the Khronos COLLADA and FBX interchange formats to import and binary encode your 3D game assets.
+ * \li Supports TrueType fonts and the Khronos COLLADA and Autodesk FBX interchange scene formats to import and binary encode your 3D game assets.
  * \li Built-in materials and shaders to enhance your game's rendering.
- * \li Both mobile and desktop platforms are supported for ease of portability.
+ * \li Both mobile and desktop platforms are supported for ease of portability and tooling options.
  * \li Documentation, tutorials, and code samples are provided.
  *
  */