2
0
Эх сурвалжийг харах

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

Conflicts:
	gameplay/src/Form.h
seanpaultaylor 13 жил өмнө
parent
commit
743f73d710
51 өөрчлөгдсөн 1049 нэмэгдсэн , 603 устгасан
  1. 0 2
      gameplay/gameplay.vcxproj
  2. 0 6
      gameplay/gameplay.vcxproj.filters
  3. 16 17
      gameplay/src/AnimationClip.cpp
  4. 8 7
      gameplay/src/AnimationClip.h
  5. 1 1
      gameplay/src/AnimationController.cpp
  6. 1 1
      gameplay/src/AnimationController.h
  7. 0 1
      gameplay/src/AnimationTarget.cpp
  8. 1 1
      gameplay/src/AudioController.cpp
  9. 1 1
      gameplay/src/AudioController.h
  10. 5 14
      gameplay/src/Base.h
  11. 1 1
      gameplay/src/Button.h
  12. 249 118
      gameplay/src/Container.cpp
  13. 40 5
      gameplay/src/Container.h
  14. 36 9
      gameplay/src/Control.cpp
  15. 40 9
      gameplay/src/Control.h
  16. 1 0
      gameplay/src/FlowLayout.cpp
  17. 120 60
      gameplay/src/Form.cpp
  18. 36 6
      gameplay/src/Form.h
  19. 16 14
      gameplay/src/Game.cpp
  20. 11 11
      gameplay/src/Game.h
  21. 12 2
      gameplay/src/Joystick.cpp
  22. 2 2
      gameplay/src/Label.cpp
  23. 15 1
      gameplay/src/Node.cpp
  24. 24 0
      gameplay/src/Node.h
  25. 6 8
      gameplay/src/ParticleEmitter.cpp
  26. 2 3
      gameplay/src/ParticleEmitter.h
  27. 65 5
      gameplay/src/PhysicsCollisionObject.cpp
  28. 46 8
      gameplay/src/PhysicsCollisionObject.h
  29. 2 3
      gameplay/src/PhysicsConstraint.cpp
  30. 23 19
      gameplay/src/PhysicsController.cpp
  31. 23 6
      gameplay/src/PhysicsController.h
  32. 0 1
      gameplay/src/PhysicsGenericConstraint.cpp
  33. 0 72
      gameplay/src/PhysicsMotionState.cpp
  34. 0 61
      gameplay/src/PhysicsMotionState.h
  35. 9 3
      gameplay/src/PhysicsRigidBody.cpp
  36. 9 5
      gameplay/src/PhysicsRigidBody.h
  37. 18 2
      gameplay/src/Platform.h
  38. 26 10
      gameplay/src/PlatformAndroid.cpp
  39. 33 24
      gameplay/src/PlatformMacOSX.mm
  40. 28 18
      gameplay/src/PlatformQNX.cpp
  41. 37 23
      gameplay/src/PlatformWin32.cpp
  42. 26 17
      gameplay/src/PlatformiOS.mm
  43. 4 1
      gameplay/src/SceneLoader.cpp
  44. 6 6
      gameplay/src/ScreenDisplayer.h
  45. 1 0
      gameplay/src/Slider.cpp
  46. 5 8
      gameplay/src/SpriteBatch.cpp
  47. 2 2
      gameplay/src/SpriteBatch.h
  48. 19 8
      gameplay/src/TextBox.cpp
  49. 1 1
      gameplay/src/TextBox.h
  50. 19 0
      gameplay/src/Theme.cpp
  51. 3 0
      gameplay/src/Theme.h

+ 0 - 2
gameplay/gameplay.vcxproj

@@ -73,7 +73,6 @@
     <ClCompile Include="src\PhysicsGenericConstraint.cpp" />
     <ClCompile Include="src\PhysicsGhostObject.cpp" />
     <ClCompile Include="src\PhysicsHingeConstraint.cpp" />
-    <ClCompile Include="src\PhysicsMotionState.cpp" />
     <ClCompile Include="src\PhysicsRigidBody.cpp" />
     <ClCompile Include="src\PhysicsSocketConstraint.cpp" />
     <ClCompile Include="src\PhysicsSpringConstraint.cpp" />
@@ -167,7 +166,6 @@
     <ClInclude Include="src\PhysicsGenericConstraint.h" />
     <ClInclude Include="src\PhysicsGhostObject.h" />
     <ClInclude Include="src\PhysicsHingeConstraint.h" />
-    <ClInclude Include="src\PhysicsMotionState.h" />
     <ClInclude Include="src\PhysicsRigidBody.h" />
     <ClInclude Include="src\PhysicsSocketConstraint.h" />
     <ClInclude Include="src\PhysicsSpringConstraint.h" />

+ 0 - 6
gameplay/gameplay.vcxproj.filters

@@ -192,9 +192,6 @@
     <ClCompile Include="src\PhysicsSpringConstraint.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\PhysicsMotionState.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
     <ClCompile Include="src\SceneLoader.cpp">
       <Filter>src</Filter>
     </ClCompile>
@@ -440,9 +437,6 @@
     <ClInclude Include="src\PhysicsController.h">
       <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\PhysicsMotionState.h">
-      <Filter>src</Filter>
-    </ClInclude>
     <ClInclude Include="src\PhysicsRigidBody.h">
       <Filter>src</Filter>
     </ClInclude>

+ 16 - 17
gameplay/src/AnimationClip.cpp

@@ -84,7 +84,7 @@ unsigned long AnimationClip::getEndTime() const
     return _endTime;
 }
 
-unsigned long AnimationClip::getElaspedTime() const
+float AnimationClip::getElaspedTime() const
 {
     return _elapsedTime;
 }
@@ -120,7 +120,7 @@ void AnimationClip::setActiveDuration(unsigned long duration)
     else
     {
         _activeDuration = _duration;
-        _repeatCount = (float) _activeDuration / (float) _duration;
+        _repeatCount = (float)_activeDuration / (float)_duration;
     }
 }
 
@@ -242,7 +242,7 @@ void AnimationClip::crossFade(AnimationClip* clip, unsigned long duration)
     // Set and intiliaze this clip to fade out
     setClipStateBit(CLIP_IS_FADING_OUT_STARTED_BIT);
     setClipStateBit(CLIP_IS_FADING_OUT_BIT);
-    _crossFadeOutElapsed = 0L;
+    _crossFadeOutElapsed = 0.0f;
     _crossFadeOutDuration = duration;
     
     // If this clip is currently not playing, we should start playing it.
@@ -282,8 +282,8 @@ void AnimationClip::addListener(AnimationClip::Listener* listener, unsigned long
                 // otherwise, it will just be set the next time the clip gets played.
                 if (isClipStateBitSet(CLIP_IS_PLAYING_BIT))
                 {
-                    unsigned long currentTime = _elapsedTime % _duration;
-                    GP_ASSERT(**_listenerItr);
+                    float currentTime = fmodf(_elapsedTime, (float)_duration);
+                    GP_ASSERT(**_listenerItr || *_listenerItr == _listeners->end());
                     if ((_speed >= 0.0f && currentTime < eventTime && (*_listenerItr == _listeners->end() || eventTime < (**_listenerItr)->_eventTime)) || 
                         (_speed <= 0 && currentTime > eventTime && (*_listenerItr == _listeners->begin() || eventTime > (**_listenerItr)->_eventTime)))
                         *_listenerItr = itr;
@@ -313,7 +313,7 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
     _endListeners->push_back(listener);
 }
 
-bool AnimationClip::update(unsigned long elapsedTime)
+bool AnimationClip::update(float elapsedTime)
 {
     if (isClipStateBitSet(CLIP_IS_PAUSED_BIT))
     {
@@ -338,9 +338,9 @@ bool AnimationClip::update(unsigned long elapsedTime)
             _elapsedTime = _activeDuration + _elapsedTime;
     }
 
-    unsigned long currentTime = 0L;
+    float currentTime = 0.0f;
     // Check to see if clip is complete.
-    if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0.0f && _elapsedTime >= (long) _activeDuration) || (_speed <= 0.0f && _elapsedTime <= 0L)))
+    if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0.0f && _elapsedTime >= _activeDuration) || (_speed <= 0.0f && _elapsedTime <= 0.0f)))
     {
         resetClipStateBit(CLIP_IS_STARTED_BIT);
         
@@ -349,34 +349,33 @@ bool AnimationClip::update(unsigned long elapsedTime)
             // If _duration == 0, we have a "pose". Just set currentTime to 0.
             if (_duration == 0)
             {
-                currentTime = 0L;
+                currentTime = 0.0f;
             }
             else
             {
-                currentTime = _activeDuration % _duration; // Get's the fractional part of the final repeat.
-                if (currentTime == 0L)
+                currentTime = (float)(_activeDuration % _duration); // Get's the fractional part of the final repeat.
+                if (currentTime == 0.0f)
                     currentTime = _duration;
             }
         }
         else
         {
-            currentTime = 0L; // If we are negative speed, the end value should be 0.
+            currentTime = 0.0f; // If we are negative speed, the end value should be 0.
         }
     }
     else
     {
         // If _duration == 0, we have a "pose". Just set currentTime to 0.
         if (_duration == 0)
-            currentTime = 0L;
+            currentTime = 0.0f;
         else // Gets portion/fraction of the repeat.
-            currentTime = _elapsedTime % _duration;
+            currentTime = fmodf(_elapsedTime, _duration);
     }
 
     // Notify any listeners of Animation events.
     if (_listeners)
     {
         GP_ASSERT(_listenerItr);
-        GP_ASSERT(**_listenerItr);
 
         if (_speed >= 0.0f)
         {
@@ -407,7 +406,7 @@ bool AnimationClip::update(unsigned long elapsedTime)
     // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
     GP_ASSERT(_animation);
     GP_ASSERT(_animation->_duration > 0);
-    float percentComplete = (float)(_startTime + currentTime) / (float) _animation->_duration;
+    float percentComplete = ((float)_startTime + currentTime) / (float)_animation->_duration;
     
     if (isClipStateBitSet(CLIP_IS_FADING_OUT_BIT))
     {
@@ -429,7 +428,7 @@ bool AnimationClip::update(unsigned long elapsedTime)
         if (_crossFadeOutElapsed < _crossFadeOutDuration)
         {
             // Calculate this clip's blend weight.
-            float tempBlendWeight = (float) (_crossFadeOutDuration - _crossFadeOutElapsed) / (float) _crossFadeOutDuration;
+            float tempBlendWeight = ((float)_crossFadeOutDuration - _crossFadeOutElapsed) / (float)_crossFadeOutDuration;
             
             // If this clip is fading in, adjust the crossfade clip's weight to be a percentage of your current blend weight
             if (isClipStateBitSet(CLIP_IS_FADING_IN_BIT))

+ 8 - 7
gameplay/src/AnimationClip.h

@@ -103,7 +103,7 @@ public:
      *
      * @return The elapsed time of the AnimationClip (in milliseconds).
      */
-    unsigned long getElaspedTime() const;
+    float getElaspedTime() const;
 
     /**
      * Sets the AnimationClip's repeat count. Overrides repeat duration.
@@ -126,7 +126,7 @@ public:
      *
      * Use REPEAT_INDEFINITE to play the AnimationClip indefinitely.
      *
-     * @param duration The active duration that is set on the AnimationClip.
+     * @param duration The active duration that is set on the AnimationClip, in milliseconds.
      */
     void setActiveDuration(unsigned long duration);
 
@@ -140,7 +140,7 @@ public:
     /**
      * Gets the AnimationClip's duration.
      *
-     * @return the AnimationClip's duration.
+     * @return the AnimationClip's duration, in milliseconds.
      */
     unsigned long getDuration() const;
 
@@ -283,7 +283,7 @@ private:
     /**
      * Updates the animation with the elapsed time.
      */
-    bool update(unsigned long elapsedTime);
+    bool update(float elapsedTime);
 
     /**
      * Handles when the AnimationClip begins.
@@ -319,10 +319,10 @@ private:
     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.
-    unsigned long _timeStarted;                         // The game time when this clip was actually started.
-    long _elapsedTime;                                  // Time elapsed while the clip is running.
+    double _timeStarted;                                // The game time when this clip was actually started.
+    float _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.
+    float _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.
     std::vector<AnimationValue*> _values;               // AnimationValue holder.
@@ -333,4 +333,5 @@ private:
 };
 
 }
+
 #endif

+ 1 - 1
gameplay/src/AnimationController.cpp

@@ -93,7 +93,7 @@ void AnimationController::unschedule(AnimationClip* clip)
         _state = IDLE;
 }
 
-void AnimationController::update(long elapsedTime)
+void AnimationController::update(float elapsedTime)
 {
     if (_state != RUNNING)
         return;

+ 1 - 1
gameplay/src/AnimationController.h

@@ -94,7 +94,7 @@ private:
     /**
      * Callback for when the controller receives a frame update event.
      */
-    void update(long elapsedTime);
+    void update(float elapsedTime);
     
     State _state;                                 // The current state of the AnimationController.
     std::list<AnimationClip*> _runningClips;      // A list of running AnimationClips.

+ 0 - 1
gameplay/src/AnimationTarget.cpp

@@ -419,7 +419,6 @@ Animation::Channel* AnimationTarget::getChannel(const char* id) const
     if (_animationChannels)
     {
         std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
-        GP_ASSERT(*itr);
 
         if (id == NULL)
             return (*itr);

+ 1 - 1
gameplay/src/AudioController.cpp

@@ -91,7 +91,7 @@ void AudioController::resume()
     }
 }
 
-void AudioController::update(long elapsedTime)
+void AudioController::update(float elapsedTime)
 {
     AudioListener* listener = AudioListener::getInstance();
     if (listener)

+ 1 - 1
gameplay/src/AudioController.h

@@ -52,7 +52,7 @@ private:
     /**
      * Controller update.
      */
-    void update(long elapsedTime);
+    void update(float elapsedTime);
 
 
     ALCdevice* _alcDevice;

+ 5 - 14
gameplay/src/Base.h

@@ -39,6 +39,8 @@ using std::size_t;
 using std::min;
 using std::max;
 using std::modf;
+using std::fmodf;
+using std::fmodl;
 
 // Common
 #ifndef NULL
@@ -60,20 +62,9 @@ extern void printError(const char* format, ...);
 
 // Assert macros.
 #ifdef _DEBUG
-#ifdef WIN32
-#define GP_FORCE_ASSERTION_FAILURE do { __debugbreak(); } while (0)
-#else
-#define GP_FORCE_ASSERTION_FAILURE do { assert(0); } while (0)
-#endif
-#define GP_ASSERT(expression) do { \
-    if (!(expression)) \
-    { \
-        printError("%s -- Assertion '" #expression "' failed.\n", __current__func__); \
-        GP_FORCE_ASSERTION_FAILURE; \
-    } } while (0)
+#define GP_ASSERT(expression) assert(expression)
 #else
-#define GP_FORCE_ASSERTION_FAILURE do { (void)sizeof(int); } while (0)
-#define GP_ASSERT(expression) do { (void)sizeof(expression); } while (0)
+#define GP_ASSERT(expression)
 #endif
 
 // Error macro.
@@ -85,7 +76,7 @@ extern void printError(const char* format, ...);
         printError("%s -- ", __current__func__); \
         printError(__VA_ARGS__); \
         printError("\n"); \
-        GP_FORCE_ASSERTION_FAILURE; \
+        assert(0); \
         std::exit(-1); \
     } while (0)
 #endif

+ 1 - 1
gameplay/src/Button.h

@@ -71,7 +71,7 @@ protected:
 
 private:
 
-    /*
+    /**
      * Constructor.
      */
     Button(const Button& copy);

+ 249 - 118
gameplay/src/Container.cpp

@@ -26,11 +26,12 @@ Container::Container()
       _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
       _scroll(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
       _scrollBarsAutoHide(false), _scrollBarOpacity(1.0f), _scrolling(false),
-       _scrollingFirstX(0), _scrollingFirstY(0),
-      _scrollingLastX(0), _scrollingLastY(0),
+       _scrollingFirstX(0), _scrollingFirstY(0), _scrollingLastX(0), _scrollingLastY(0),
       _scrollingStartTimeX(0), _scrollingStartTimeY(0), _scrollingLastTime(0),
       _scrollingVelocity(Vector2::zero()), _scrollingFriction(1.0f),
-      _scrollingRight(false), _scrollingDown(false), _scrollBarOpacityClip(NULL), _zIndexDefault(0)
+      _scrollingRight(false), _scrollingDown(false),
+      _scrollingMouseVertically(false), _scrollingMouseHorizontally(false),
+      _scrollBarOpacityClip(NULL), _zIndexDefault(0), _totalWidth(0), _totalHeight(0)
 {
 }
 
@@ -108,9 +109,7 @@ void Container::addControls(Theme* theme, Properties* properties)
         }
         else
         {
-            Theme::Style::Overlay* overlay = Theme::Style::Overlay::create();
-            controlStyle = new Theme::Style(theme, "", 1.0f / theme->_texture->getWidth(), 1.0f / theme->_texture->getHeight(),
-                Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, overlay, overlay);
+            controlStyle = theme->getEmptyStyle();
         }
 
         std::string controlName(controlSpace->getNamespace());
@@ -499,81 +498,15 @@ bool Container::isDirty()
 
 bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
-    if (!isEnabled())
-    {
-        return false;
-    }
-
-    bool eventConsumed = false;
-    const Theme::Border& border = getBorder(_state);
-    const Theme::Padding& padding = getPadding();
-    float xPos = border.left + padding.left;
-    float yPos = border.top + padding.top;
-
-    Vector2* offset = NULL;
-    if (_scroll != SCROLL_NONE)
-    {
-        offset = &_scrollPosition;
-    }
-
-    std::vector<Control*>::const_iterator it;
-    for (it = _controls.begin(); it < _controls.end(); it++)
-    {
-        Control* control = *it;
-        GP_ASSERT(control);
-        if (!control->isEnabled())
-        {
-            continue;
-        }
-
-        const Rectangle& bounds = control->getBounds();
-        float boundsX = bounds.x;
-        float boundsY = bounds.y;
-        if (offset)
-        {
-            boundsX += offset->x;
-            boundsY += offset->y;
-        }
-
-        if (control->getState() != Control::NORMAL ||
-            (evt == Touch::TOUCH_PRESS &&
-                x >= xPos + boundsX &&
-                x <= xPos + boundsX + bounds.width &&
-                y >= yPos + boundsY &&
-                y <= yPos + boundsY + bounds.height))
-        {
-            // Pass on the event's clip relative to the control.
-            eventConsumed |= control->touchEvent(evt, x - xPos - boundsX, y - yPos - boundsY, contactIndex);
-        }
-    }
-
-    if (!isEnabled())
-    {
-        return (_consumeTouchEvents | eventConsumed);
-    }
-
-    switch (evt)
-    {
-    case Touch::TOUCH_PRESS:
-        setState(Control::FOCUS);
-        break;
-    case Touch::TOUCH_RELEASE:
-        setState(Control::NORMAL);
-        break;
-    }
-
-    if (!eventConsumed && _scroll != SCROLL_NONE)
-    {
-        if (touchEventScroll(evt, x - xPos, y - yPos, contactIndex))
-        {
-            _dirty = true;
-        }
-    }
+    return pointerEvent(false, evt, x, y, (int)contactIndex);
+}
 
-    return (_consumeTouchEvents | eventConsumed);
+bool Container::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    return pointerEvent(true, evt, x, y, wheelDelta);
 }
 
-void Container::keyEvent(Keyboard::KeyEvent evt, int key)
+bool Container::keyEvent(Keyboard::KeyEvent evt, int key)
 {
     std::vector<Control*>::const_iterator it;
     for (it = _controls.begin(); it < _controls.end(); it++)
@@ -587,9 +520,12 @@ void Container::keyEvent(Keyboard::KeyEvent evt, int key)
 
         if (control->isContainer() || control->getState() == Control::FOCUS)
         {
-            control->keyEvent(evt, key);
+            if (control->keyEvent(evt, key))
+                return _consumeInputEvents;
         }
     }
+
+    return false;
 }
 
 bool Container::isContainer()
@@ -632,17 +568,15 @@ Layout::Type Container::getLayoutType(const char* layoutString)
 void Container::updateScroll()
 {
     // Update Time.
-    static long lastFrameTime = Game::getGameTime();
-    long frameTime = Game::getGameTime();
-    long elapsedTime = (frameTime - lastFrameTime);
+    static double lastFrameTime = Game::getGameTime();
+    double frameTime = Game::getGameTime();
+    float elapsedTime = (float)(frameTime - lastFrameTime);
     lastFrameTime = frameTime;
 
     const Theme::Border& containerBorder = getBorder(_state);
     const Theme::Padding& containerPadding = getPadding();
 
     // Calculate total width and height.
-    float totalWidth = 0;
-    float totalHeight = 0;
     std::vector<Control*> controls = getControls();
     unsigned int controlsCount = controls.size();
     for (unsigned int i = 0; i < controlsCount; i++)
@@ -653,15 +587,15 @@ void Container::updateScroll()
         const Theme::Margin& margin = control->getMargin();
 
         float newWidth = bounds.x + bounds.width;
-        if (newWidth > totalWidth)
+        if (newWidth > _totalWidth)
         {
-            totalWidth = newWidth;
+            _totalWidth = newWidth;
         }
 
         float newHeight = bounds.y + bounds.height;
-        if (newHeight > totalHeight)
+        if (newHeight > _totalHeight)
         {
-            totalHeight = newHeight;
+            _totalHeight = newHeight;
         }
     }
 
@@ -690,15 +624,15 @@ void Container::updateScroll()
     }
 
     // Stop scrolling when the far edge is reached.
-    if (-_scrollPosition.x > totalWidth - clipWidth)
+    if (-_scrollPosition.x > _totalWidth - clipWidth)
     {
-        _scrollPosition.x = -(totalWidth - clipWidth);
+        _scrollPosition.x = -(_totalWidth - clipWidth);
         _scrollingVelocity.x = 0;
     }
     
-    if (-_scrollPosition.y > totalHeight - clipHeight)
+    if (-_scrollPosition.y > _totalHeight - clipHeight)
     {
-        _scrollPosition.y = -(totalHeight - clipHeight);
+        _scrollPosition.y = -(_totalHeight - clipHeight);
         _scrollingVelocity.y = 0;
     }
 
@@ -715,15 +649,15 @@ void Container::updateScroll()
     }
 
     float scrollWidth = 0;
-    if (clipWidth < totalWidth)
-        scrollWidth = (clipWidth / totalWidth) * clipWidth;
+    if (clipWidth < _totalWidth)
+        scrollWidth = (clipWidth / _totalWidth) * clipWidth;
 
     float scrollHeight = 0;
-    if (clipHeight < totalHeight)
-        scrollHeight = (clipHeight / totalHeight) * clipHeight;
+    if (clipHeight < _totalHeight)
+        scrollHeight = (clipHeight / _totalHeight) * clipHeight;
 
-    _scrollBarBounds.set(((-_scrollPosition.x) / totalWidth) * clipWidth,
-                         ((-_scrollPosition.y) / totalHeight) * clipHeight,
+    _scrollBarBounds.set(((-_scrollPosition.x) / _totalWidth) * clipWidth,
+                         ((-_scrollPosition.y) / _totalHeight) * clipHeight,
                          scrollWidth, scrollHeight);
 
     // If scroll velocity is 0 and scrollbars are not always visible, trigger fade-out animation.
@@ -731,8 +665,11 @@ void Container::updateScroll()
     {
         float to = 0;
         _scrollBarOpacity = 0.99f;
-        Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
-        _scrollBarOpacityClip = animation->getClip();
+        if (!_scrollBarOpacityClip)
+        {
+            Animation* animation = createAnimationFromTo("scrollbar-fade-out", ANIMATE_OPACITY, &_scrollBarOpacity, &to, Curve::QUADRATIC_IN_OUT, 500L);
+            _scrollBarOpacityClip = animation->getClip();
+        }
         _scrollBarOpacityClip->play();
     }
 
@@ -757,17 +694,39 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
             _scrollBarOpacityClip = NULL;
         }
         _scrollBarOpacity = 1.0f;
-        return true;
+        return _consumeInputEvents;
 
     case Touch::TOUCH_MOVE:
         if (_scrolling)
         {
+            double gameTime = Game::getAbsoluteTime();
+
             // Calculate the latest movement delta for the next update to use.
             int vx = x - _scrollingLastX;
             int vy = y - _scrollingLastY;
-            _scrollingVelocity.set(vx, vy);
-            _scrollPosition.x += vx;
-            _scrollPosition.y += vy;
+            if (_scrollingMouseVertically)
+            {
+                float yRatio = _totalHeight / _absoluteBounds.height;
+                vy *= yRatio;
+
+                _scrollingVelocity.set(0, -vy);
+                _scrollPosition.y -= vy;
+            }
+            else if (_scrollingMouseHorizontally)
+            {
+                float xRatio = _totalWidth / _absoluteBounds.width;
+                vx *= xRatio;
+
+                _scrollingVelocity.set(-vx, 0);
+                _scrollPosition.x -= vx;
+            }
+            else
+            {
+                _scrollingVelocity.set(vx, vy);
+                _scrollPosition.x += vx;
+                _scrollPosition.y += vy;
+            }
+
             _scrollingLastX = x;
             _scrollingLastY = y;
 
@@ -777,7 +736,7 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
             {
                 _scrollingFirstX = x;
                 _scrollingRight = goingRight;
-                _scrollingStartTimeX = Game::getAbsoluteTime();
+                _scrollingStartTimeX = gameTime;
             }
 
             bool goingDown = (vy > 0);
@@ -785,37 +744,39 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
             {
                 _scrollingFirstY = y;
                 _scrollingDown = goingDown;
-                _scrollingStartTimeY = Game::getAbsoluteTime();
+                _scrollingStartTimeY = gameTime;
             }
 
             if (!_scrollingStartTimeX)
-                _scrollingStartTimeX = Game::getAbsoluteTime();
+                _scrollingStartTimeX = gameTime;
 
             if (!_scrollingStartTimeY)
-                _scrollingStartTimeY = Game::getAbsoluteTime();
+                _scrollingStartTimeY = gameTime;
 
-            _scrollingLastTime = Game::getAbsoluteTime();
+            _scrollingLastTime = gameTime;
 
-            return true;
+            return _consumeInputEvents;
         }
         break;
 
     case Touch::TOUCH_RELEASE:
         _scrolling = false;
-        long timeSinceLastMove = Game::getAbsoluteTime() - _scrollingLastTime;
+        double gameTime = Game::getAbsoluteTime();
+        float timeSinceLastMove = (float)(gameTime - _scrollingLastTime);
         if (timeSinceLastMove > SCROLL_INERTIA_DELAY)
         {
             _scrollingVelocity.set(0, 0);
-            return true;
+            _scrollingMouseVertically = _scrollingMouseHorizontally = false;
+            return _consumeInputEvents;
         }
 
         int dx = _scrollingLastX - _scrollingFirstX;
         int dy = _scrollingLastY - _scrollingFirstY;
 
-        long timeTakenX = Game::getAbsoluteTime() - _scrollingStartTimeX;
-        float elapsedSecsX = (float)timeTakenX * 0.001f;
-        long timeTakenY = Game::getAbsoluteTime() - _scrollingStartTimeY;
-        float elapsedSecsY = (float)timeTakenY * 0.001f;
+        float timeTakenX = (float)(gameTime - _scrollingStartTimeX);
+        float elapsedSecsX = timeTakenX * 0.001f;
+        float timeTakenY = (float)(gameTime - _scrollingStartTimeY);
+        float elapsedSecsY = timeTakenY * 0.001f;
 
         float vx = dx;
         float vy = dy;
@@ -824,14 +785,184 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
         if (elapsedSecsY > 0)
             vy = (float)dy / elapsedSecsY;
 
-        _scrollingVelocity.set(vx, vy);
+        if (_scrollingMouseVertically)
+        {
+            float yRatio = _totalHeight / _absoluteBounds.height;
+            vy *= yRatio;
+            _scrollingVelocity.set(0, -vy);
+        }
+        else if (_scrollingMouseHorizontally)
+        {
+            float xRatio = _totalWidth / _absoluteBounds.width;
+            vx *= xRatio;
+            _scrollingVelocity.set(-vx, 0);
+        }
+        else
+        {
+            _scrollingVelocity.set(vx, vy);
+        }
 
-        return true;
+        _scrollingMouseVertically = _scrollingMouseHorizontally = false;
+        return _consumeInputEvents;
     }
 
     return false;
 }
 
+bool Container::mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    switch(evt)
+    {
+        case Mouse::MOUSE_PRESS_LEFT_BUTTON:
+        {
+            if (_scrollBarVertical)
+            {
+                float vWidth = _scrollBarVertical->getRegion().width;
+                Rectangle vBounds(_viewportBounds.x + _viewportBounds.width - vWidth,
+                                 _scrollBarBounds.y,
+                                 vWidth, _scrollBarBounds.height);
+
+                if (x + _viewportBounds.x >= vBounds.x &&
+                    x + _viewportBounds.x <= vBounds.x + vBounds.width)
+                {
+                    // Then we're within the horizontal bounds of the verticle scrollbar.
+                    // We want to either jump up or down, or drag the scrollbar itself.
+                    if (y < vBounds.y)
+                    {
+                        _scrollPosition.y += _totalHeight / 5.0f;
+                    }
+                    else if (y > vBounds.y + vBounds.height)
+                    {
+                        _scrollPosition.y -= _totalHeight / 5.0f;
+                    }
+                    else
+                    {
+                        _scrollingMouseVertically = true;
+                    }
+                }
+            }
+            
+            if (_scrollBarHorizontal)
+            {
+                float hHeight = _scrollBarHorizontal->getRegion().height;
+                Rectangle hBounds(_scrollBarBounds.x,
+                                  _viewportBounds.y + _viewportBounds.height - hHeight,
+                                  _scrollBarBounds.width, hHeight);
+            
+                if (y + _viewportBounds.y >= hBounds.y &&
+                         y + _viewportBounds.y <= hBounds.y + hBounds.height)
+                {
+                    // We're within the vertical bounds of the horizontal scrollbar.
+                    if (x < hBounds.x)
+                        _scrollPosition.x += _totalWidth / 5.0f;
+                    else if (x > hBounds.x + hBounds.width)
+                        _scrollPosition.x -= _totalWidth / 5.0f;
+                    else
+                        _scrollingMouseHorizontally = true;
+                }
+            }
+
+            return touchEventScroll(Touch::TOUCH_PRESS, x, y, 0);
+        }
+
+        case Mouse::MOUSE_MOVE:
+            return touchEventScroll(Touch::TOUCH_MOVE, x, y, 0);
+
+        case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
+            return touchEventScroll(Touch::TOUCH_RELEASE, x, y, 0);
+
+        case Mouse::MOUSE_WHEEL:
+            _scrollPosition.y += (_totalHeight / 10.0f) * wheelDelta;
+            return _consumeInputEvents;
+    }
+
+    return false;
+}
+
+bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
+{
+    if (!isEnabled())
+    {
+        return false;
+    }
+
+    bool eventConsumed = false;
+    const Theme::Border& border = getBorder(_state);
+    const Theme::Padding& padding = getPadding();
+    float xPos = border.left + padding.left;
+    float yPos = border.top + padding.top;
+
+    Vector2* offset = NULL;
+    if (_scroll != SCROLL_NONE)
+    {
+        offset = &_scrollPosition;
+    }
+
+    std::vector<Control*>::const_iterator it;
+    for (it = _controls.begin(); it < _controls.end(); it++)
+    {
+        Control* control = *it;
+        GP_ASSERT(control);
+        if (!control->isEnabled())
+        {
+            continue;
+        }
+
+        const Rectangle& bounds = control->getBounds();
+        float boundsX = bounds.x;
+        float boundsY = bounds.y;
+        if (offset)
+        {
+            boundsX += offset->x;
+            boundsY += offset->y;
+        }
+
+        if (control->getState() != Control::NORMAL ||
+            ((evt == Touch::TOUCH_PRESS ||
+              evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
+              evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
+              evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
+              evt == Mouse::MOUSE_WHEEL) &&
+                x >= xPos + boundsX &&
+                x <= xPos + boundsX + bounds.width &&
+                y >= yPos + boundsY &&
+                y <= yPos + boundsY + bounds.height))
+        {
+            // Pass on the event's clip relative to the control.
+            if (mouse)
+                eventConsumed |= control->mouseEvent((Mouse::MouseEvent)evt, x - xPos - boundsX, y - yPos - boundsY, data);
+            else
+                eventConsumed |= control->touchEvent((Touch::TouchEvent)evt, x - xPos - boundsX, y - yPos - boundsY, (unsigned int)data);
+        }
+    }
+
+    if (!isEnabled())
+    {
+        return (_consumeInputEvents | eventConsumed);
+    }
+
+    switch (evt)
+    {
+    case Touch::TOUCH_PRESS:
+        setState(Control::FOCUS);
+        break;
+    case Touch::TOUCH_RELEASE:
+        setState(Control::NORMAL);
+        break;
+    }
+
+    if (!eventConsumed && _scroll != SCROLL_NONE)
+    {
+        if (mouse && mouseEventScroll((Mouse::MouseEvent)evt, x - xPos, y - yPos, data) ||
+            (!mouse && touchEventScroll((Touch::TouchEvent)evt, x - xPos, y - yPos, (unsigned int)data)))
+        {
+            _dirty = true;
+        }
+    }
+
+    return (_consumeInputEvents | eventConsumed);
+}
+
 Container::Scroll Container::getScroll(const char* scroll)
 {
     if (!scroll)

+ 40 - 5
gameplay/src/Container.h

@@ -45,6 +45,8 @@ namespace gameplay
  */
 class Container : public Control
 {
+    friend class DropDownList;
+
 public:
 
     /**
@@ -241,11 +243,27 @@ protected:
      * @param evt The key event that occured.
      * @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.
+     *
+     * @return Whether the key event was consumed by this control.
      * 
      * @see Keyboard::KeyEvent
      * @see Keyboard::Key
      */
-    virtual void keyEvent(Keyboard::KeyEvent evt, int key);
+    virtual bool keyEvent(Keyboard::KeyEvent evt, int key);
+
+    /**
+     * Mouse callback on mouse events.
+     *
+     * @param evt The mouse event that occurred.
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     * @param wheelDelta The number of mouse wheel ticks. Positive is up (forward), negative is down (backward).
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
+     */
+    virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
     /**
      * Gets a Layout::Type enum from a matching string.
@@ -307,6 +325,10 @@ protected:
      */
     bool touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
+    bool mouseEventScroll(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
+
+    bool pointerEvent(bool mouse, char evt, int x, int y, int data);
+
     /**
      * Get a Scroll enum from a matching string.
      *
@@ -329,7 +351,7 @@ protected:
      */
     Theme::ThemeImage* _scrollBarTopCap;
     /**
-     * Scrollbar verticle image.
+     * Scrollbar vertical image.
      */
     Theme::ThemeImage* _scrollBarVertical;
     /**
@@ -391,15 +413,15 @@ protected:
     /** 
      * Time we started scrolling in the x
      */ 
-    long _scrollingStartTimeX;
+    double _scrollingStartTimeX;
     /** 
      * Time we started scrolling in the y
      */ 
-    long _scrollingStartTimeY;
+    double _scrollingStartTimeY;
     /** 
      * The last time we were scrolling
      */
-    long _scrollingLastTime;
+    double _scrollingLastTime;
     /** 
      * Speed to continue scrolling at after touch release.
      */ 
@@ -417,6 +439,16 @@ protected:
      */ 
     bool _scrollingDown;
 
+    /**
+     * Locked to scrolling vertically by grabbing the scrollbar with the mouse.
+     */
+    bool _scrollingMouseVertically;
+
+    /**
+     * Locked to scrolling horizontally by grabbing the scrollbar with the mouse.
+     */
+    bool _scrollingMouseHorizontally;
+
 private:
 
     /**
@@ -426,6 +458,9 @@ private:
 
     AnimationClip* _scrollBarOpacityClip;
     int _zIndexDefault;
+
+    float _totalWidth;
+    float _totalHeight;
 };
 
 

+ 36 - 9
gameplay/src/Control.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
-    _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false), _skin(NULL), _clearBounds(Rectangle::empty())
+    _dirty(true), _consumeInputEvents(true), _listeners(NULL), _styleOverridden(false), _skin(NULL), _clearBounds(Rectangle::empty())
 {
 }
 
@@ -607,14 +607,14 @@ Theme::Style::OverlayType Control::getOverlayType() const
     }
 }
 
-void Control::setConsumeTouchEvents(bool consume)
+void Control::setConsumeInputEvents(bool consume)
 {
-    _consumeTouchEvents = consume;
+    _consumeInputEvents = consume;
 }
     
-bool Control::getConsumeTouchEvents()
+bool Control::getConsumeInputEvents()
 {
-    return _consumeTouchEvents;
+    return _consumeInputEvents;
 }
 
 int Control::getZIndex() const
@@ -692,7 +692,7 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
     {
     case Touch::TOUCH_PRESS:
         notifyListeners(Listener::PRESS);
-        break;
+        return _consumeInputEvents;
             
     case Touch::TOUCH_RELEASE:
         // Always trigger Listener::RELEASE
@@ -704,14 +704,41 @@ bool Control::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
             notifyListeners(Listener::CLICK);
         }
-        break;
+        return _consumeInputEvents;
     }
 
-    return _consumeTouchEvents;
+    return false;
 }
 
-void Control::keyEvent(Keyboard::KeyEvent evt, int key)
+bool Control::keyEvent(Keyboard::KeyEvent evt, int key)
 {
+    return false;
+}
+
+bool Control::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (!isEnabled())
+    {
+        return false;
+    }
+
+    // By default, mouse events are either interpreted as touch events or ignored.
+    switch (evt)
+    {
+    case Mouse::MOUSE_PRESS_LEFT_BUTTON:
+        return touchEvent(Touch::TOUCH_PRESS, x, y, 0);
+
+    case Mouse::MOUSE_RELEASE_LEFT_BUTTON:
+        return touchEvent(Touch::TOUCH_RELEASE, x, y, 0);
+
+    case Mouse::MOUSE_MOVE:
+        return touchEvent(Touch::TOUCH_MOVE, x, y, 0);
+
+    default:
+        break;
+    }
+
+    return false;
 }
 
 void Control::notifyListeners(Listener::EventType eventType)

+ 40 - 9
gameplay/src/Control.h

@@ -9,6 +9,7 @@
 #include "ThemeStyle.h"
 #include "Touch.h"
 #include "Keyboard.h"
+#include "Mouse.h"
 
 namespace gameplay
 {
@@ -124,7 +125,17 @@ public:
             /**
              * Event triggered when the contents of a text box are modified.
              */
-            TEXT_CHANGED    = 0x10
+            TEXT_CHANGED    = 0x10,
+
+            /**
+             * Event triggered when a control is clicked with the middle mouse button.
+             */
+            MIDDLE_CLICK    = 0x20,
+
+            /**
+             * Event triggered when a control is clicked with the right mouse button.
+             */
+            RIGHT_CLICK     = 0x40,
         };
 
         /**
@@ -630,19 +641,19 @@ public:
     bool isEnabled();
 
     /**
-     * Set whether this control consumes touch events,
+     * Set whether this control consumes input events,
      * preventing them from being passed to the game.
      *
-     * @param consume Whether this control consumes touch events.
+     * @param consume Whether this control consumes input events.
      */
-    void setConsumeTouchEvents(bool consume);
+    void setConsumeInputEvents(bool consume);
 
     /**
      * Get whether this control consumes touch events.
      *
      * @return Whether this control consumes touch events.
      */
-    bool getConsumeTouchEvents();
+    bool getConsumeInputEvents();
 
     /**
      * Set the style this control will use when rendering.
@@ -738,11 +749,27 @@ protected:
      * @param evt The key event that occured.
      * @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.
+     *
+     * @return Whether the key event was consumed by this control.
      * 
      * @see Keyboard::KeyEvent
      * @see Keyboard::Key
      */
-    virtual void keyEvent(Keyboard::KeyEvent evt, int key);
+    virtual bool keyEvent(Keyboard::KeyEvent evt, int key);
+
+    /**
+     * Mouse callback on mouse events.
+     *
+     * @param evt The mouse event that occurred.
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     * @param wheelDelta The number of mouse wheel ticks. Positive is up (forward), negative is down (backward).
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
+     */
+    virtual bool mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
     /**
      * Called when a control's properties change.  Updates this control's internal rendering
@@ -872,15 +899,20 @@ protected:
      */
     Rectangle _viewportClipBounds;
 
+    /**
+     * Previous frame's absolute clip bounds, to be cleared if necessary.
+     */
+    Rectangle _clearBounds;         
+
     /**
      * If the control is dirty and need updating.
      */
     bool _dirty;
     
     /**
-     * Flag for whether the Control consume's touch events.
+     * Flag for whether the Control consumes input events.
      */
-    bool _consumeTouchEvents;
+    bool _consumeInputEvents;
     
     /**
      * The Control's Alignmnet
@@ -952,7 +984,6 @@ private:
     
     bool _styleOverridden;
     Theme::Skin* _skin;
-    Rectangle _clearBounds;         // Previous frame's absolute clip bounds, to be cleared if necessary.
 };
 
 }

+ 1 - 0
gameplay/src/FlowLayout.cpp

@@ -18,6 +18,7 @@ FlowLayout::FlowLayout(const FlowLayout& copy)
 
 FlowLayout::~FlowLayout()
 {
+    __instance = NULL;
 }
 
 FlowLayout* FlowLayout::create()

+ 120 - 60
gameplay/src/Form.cpp

@@ -527,63 +527,20 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
 
         if (form->isEnabled())
         {
-            Node* node = form->_node;
-            if (node)
+            if (form->_node)
             {
-                Scene* scene = node->getScene();
-                GP_ASSERT(scene);
-                Camera* camera = scene->getActiveCamera();
-
-                if (camera)
+                Vector3 point;
+                if (form->projectPoint(x, y, &point))
                 {
-                    // Get info about the form's position.
-                    Matrix m = node->getMatrix();
-                    Vector3 min(0, 0, 0);
-                    m.transformPoint(&min);
-
-                    // Unproject point into world space.
-                    Ray ray;
-                    camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
-
-                    // Find the quad's plane.
-                    // We know its normal is the quad's forward vector.
-                    Vector3 normal = node->getForwardVectorWorld();
-
-                    // To get the plane's distance from the origin,
-                    // we'll find the distance from the plane defined
-                    // by the quad's forward vector and one of its points
-                    // to the plane defined by the same vector and the origin.
-                    const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
-                    const float d = -(a*min.x) - (b*min.y) - (c*min.z);
-                    const float distance = fabs(d) /  sqrt(a*a + b*b + c*c);
-                    Plane plane(normal, -distance);
-
-                    // Check for collision with plane.
-                    float collisionDistance = ray.intersects(plane);
-                    if (collisionDistance != Ray::INTERSECTS_NONE)
+                    const Rectangle& bounds = form->getBounds();
+                    if (form->getState() == Control::FOCUS ||
+                        (evt == Touch::TOUCH_PRESS &&
+                         point.x >= bounds.x &&
+                         point.x <= bounds.x + bounds.width &&
+                         point.y >= bounds.y &&
+                         point.y <= bounds.y + bounds.height))
                     {
-                        // Multiply the ray's direction vector by collision distance
-                        // and add that to the ray's origin.
-                        Vector3 point = ray.getOrigin() + collisionDistance*ray.getDirection();
-
-                        // Project this point into the plane.
-                        m.invert();
-                        m.transformPoint(&point);
-
-                        // Pass the touch event on.
-                        const Rectangle& bounds = form->getBounds();
-                        if (form->getState() == Control::FOCUS ||
-                            (evt == Touch::TOUCH_PRESS &&
-                                point.x >= bounds.x &&
-                                point.x <= bounds.x + bounds.width &&
-                                point.y >= bounds.y &&
-                                point.y <= bounds.y + bounds.height))
-                        {
-                            if (form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex))
-                            {
-                                return true;
-                            }
-                        }
+                        return form->touchEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, contactIndex);
                     }
                 }
             }
@@ -599,10 +556,7 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
                         y <= bounds.y + bounds.height))
                 {
                     // Pass on the event's position relative to the form.
-                    if (form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex))
-                    {
-                        return true;
-                    }
+                    return form->touchEvent(evt, x - bounds.x, y - bounds.y, contactIndex);
                 }
             }
         }
@@ -611,7 +565,7 @@ bool Form::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int
     return false;
 }
 
-void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
+bool Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
     std::vector<Form*>::const_iterator it;
     for (it = __forms.begin(); it < __forms.end(); it++)
@@ -620,9 +574,115 @@ void Form::keyEventInternal(Keyboard::KeyEvent evt, int key)
         GP_ASSERT(form);
         if (form->isEnabled())
         {
-            form->keyEvent(evt, key);
+            if (form->keyEvent(evt, key))
+                return true;
         }
     }
+
+    return false;
+}
+
+bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    std::vector<Form*>::const_iterator it;
+    for (it = __forms.begin(); it < __forms.end(); it++)
+    {
+        Form* form = *it;
+        GP_ASSERT(form);
+
+        if (form->isEnabled())
+        {
+            if (form->_node)
+            {
+                Vector3 point;
+                if (form->projectPoint(x, y, &point))
+                {
+                    const Rectangle& bounds = form->getBounds();
+                    if (form->getState() == Control::FOCUS ||
+                        ((evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
+                          evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
+                          evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
+                          evt == Mouse::MOUSE_WHEEL) &&
+                         point.x >= bounds.x &&
+                         point.x <= bounds.x + bounds.width &&
+                         point.y >= bounds.y &&
+                         point.y <= bounds.y + bounds.height))
+                    {
+                        return form->mouseEvent(evt, point.x - bounds.x, bounds.height - point.y - bounds.y, wheelDelta);
+                    }
+                }
+            }
+            else
+            {
+                // Simply compare with the form's bounds.
+                const Rectangle& bounds = form->getBounds();
+                if (form->getState() == Control::FOCUS ||
+                    ((evt == Mouse::MOUSE_PRESS_LEFT_BUTTON ||
+                      evt == Mouse::MOUSE_PRESS_MIDDLE_BUTTON ||
+                      evt == Mouse::MOUSE_PRESS_RIGHT_BUTTON ||
+                      evt == Mouse::MOUSE_WHEEL) &&
+                        x >= bounds.x &&
+                        x <= bounds.x + bounds.width &&
+                        y >= bounds.y &&
+                        y <= bounds.y + bounds.height))
+                {
+                    // Pass on the event's position relative to the form.
+                    return form->mouseEvent(evt, x - bounds.x, y - bounds.y, wheelDelta);
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
+bool Form::projectPoint(int x, int y, Vector3* point)
+{
+    Scene* scene = _node->getScene();
+    GP_ASSERT(scene);
+    Camera* camera = scene->getActiveCamera();
+
+    if (camera)
+    {
+        // Get info about the form's position.
+        Matrix m = _node->getMatrix();
+        Vector3 min(0, 0, 0);
+        m.transformPoint(&min);
+
+        // Unproject point into world space.
+        Ray ray;
+        camera->pickRay(Game::getInstance()->getViewport(), x, y, &ray);
+
+        // Find the quad's plane.
+        // We know its normal is the quad's forward vector.
+        Vector3 normal = _node->getForwardVectorWorld();
+
+        // To get the plane's distance from the origin,
+        // we'll find the distance from the plane defined
+        // by the quad's forward vector and one of its points
+        // to the plane defined by the same vector and the origin.
+        const float& a = normal.x; const float& b = normal.y; const float& c = normal.z;
+        const float d = -(a*min.x) - (b*min.y) - (c*min.z);
+        const float distance = abs(d) /  sqrt(a*a + b*b + c*c);
+        Plane plane(normal, -distance);
+
+        // Check for collision with plane.
+        float collisionDistance = ray.intersects(plane);
+        if (collisionDistance != Ray::INTERSECTS_NONE)
+        {
+            // Multiply the ray's direction vector by collision distance
+            // and add that to the ray's origin.
+            point->set(ray.getOrigin() + collisionDistance*ray.getDirection());
+
+            // Project this point into the plane.
+            m.invert();
+            m.transformPoint(point);
+
+            return true;
+        }
+    }
+
+    return false;
 }
 
 unsigned int Form::nextPowerOfTwo(unsigned int v)

+ 36 - 6
gameplay/src/Form.h

@@ -8,6 +8,7 @@
 #include "FrameBuffer.h"
 #include "Touch.h"
 #include "Keyboard.h"
+#include "Mouse.h"
 
 namespace gameplay
 {
@@ -175,16 +176,45 @@ private:
 
     /**
      * Propagate key events to enabled forms.
+     *
+     * @return Whether the key event was consumed by a form.
      */
-    static void keyEventInternal(Keyboard::KeyEvent evt, int key);
+    static bool keyEventInternal(Keyboard::KeyEvent evt, int key);
 
+    /**
+     * Propagate mouse events to enabled forms.
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
+     */
+    static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
+
+    /**
+     * Get the next highest power of two of an integer.  Used when creating framebuffers.
+     *
+     * @param x The number to start with.
+     *
+     * @return The next highest power of two after x, or x if it is already a power of two.
+     */
     static unsigned int nextPowerOfTwo(unsigned int v);
+
+    /**
+     * Unproject a point (from a mouse or touch event) into the scene and then project it onto the form.
+     *
+     * @param x The x coordinate of the mouse/touch point.
+     * @param y The y coordinate of the mouse/touch point.
+     * @param point A destination vector to populate with the projected point, in the form's plane.
+     *
+     * @return True if the projected point lies within the form's plane, false otherwise.
+     */
+    bool projectPoint(int x, int y, Vector3* point);
 
-    Theme* _theme;              // The Theme applied to this Form.
-    Model* _quad;               // Quad for rendering this Form in world-space.
-    Node* _node;                // Node for transforming this Form in world-space.
-    FrameBuffer* _frameBuffer;  // FBO the Form is rendered into for texturing the quad.
-    Matrix _projectionMatrix;   // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
+    Theme* _theme;                      // The Theme applied to this Form.
+    Model* _quad;                       // Quad for rendering this Form in world-space.
+    Node* _node;                        // Node for transforming this Form in world-space.
+    FrameBuffer* _frameBuffer;          // FBO the Form is rendered into for texturing the quad.
+    Matrix _projectionMatrix;           // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
     Matrix _defaultProjectionMatrix;
     SpriteBatch* _spriteBatch;
 

+ 16 - 14
gameplay/src/Game.cpp

@@ -13,8 +13,8 @@ namespace gameplay
 {
 
 static Game* __gameInstance = NULL;
-long Game::_pausedTimeLast = 0L;
-long Game::_pausedTimeTotal = 0L;
+double Game::_pausedTimeLast = 0.0;
+double Game::_pausedTimeTotal = 0.0;
 
 Game::Game() 
     : _initialized(false), _state(UNINITIALIZED), 
@@ -48,12 +48,12 @@ Game* Game::getInstance()
     return __gameInstance;
 }
 
-long Game::getAbsoluteTime()
+double Game::getAbsoluteTime()
 {
     return Platform::getAbsoluteTime();
 }
 
-long Game::getGameTime()
+double Game::getGameTime()
 {
     return Platform::getAbsoluteTime() - _pausedTimeTotal;
 }
@@ -193,9 +193,9 @@ void Game::frame()
         GP_ASSERT(_physicsController);
 
         // Update Time.
-        static long lastFrameTime = Game::getGameTime();
-        long frameTime = Game::getGameTime();
-        long elapsedTime = (frameTime - lastFrameTime);
+        static double lastFrameTime = Game::getGameTime();
+        double frameTime = getGameTime();
+        float elapsedTime = (frameTime - lastFrameTime);
         lastFrameTime = frameTime;
 
         // Update the scheduled and running animations.
@@ -206,11 +206,13 @@ void Game::frame()
     
         // Update the physics.
         _physicsController->update(elapsedTime);
+
         // Application Update.
         update(elapsedTime);
 
         // Audio Rendering.
         _audioController->update(elapsedTime);
+
         // Graphics Rendering.
         render(elapsedTime);
 
@@ -220,7 +222,7 @@ void Game::frame()
         {
             _frameRate = _frameCount;
             _frameCount = 0;
-            _frameLastFPS = Game::getGameTime();
+            _frameLastFPS = getGameTime();
         }
     }
     else
@@ -303,7 +305,7 @@ void Game::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactI
 {
 }
 
-void Game::schedule(long timeOffset, TimeListener* timeListener, void* cookie)
+void Game::schedule(float timeOffset, TimeListener* timeListener, void* cookie)
 {
     GP_ASSERT(_timeEvents);
     TimeEvent timeEvent(getGameTime() + timeOffset, timeListener, cookie);
@@ -322,9 +324,9 @@ void Game::updateOnce()
     GP_ASSERT(_physicsController);
 
     // Update Time.
-    static long lastFrameTime = Game::getGameTime();
-    long frameTime = Game::getGameTime();
-    long elapsedTime = (frameTime - lastFrameTime);
+    static double lastFrameTime = getGameTime();
+    double frameTime = getGameTime();
+    float elapsedTime = (frameTime - lastFrameTime);
     lastFrameTime = frameTime;
 
     // Update the internal controllers.
@@ -358,7 +360,7 @@ void Game::loadConfig()
     }
 }
 
-void Game::fireTimeEvents(long frameTime)
+void Game::fireTimeEvents(double frameTime)
 {
     while (_timeEvents->size() > 0)
     {
@@ -373,7 +375,7 @@ void Game::fireTimeEvents(long frameTime)
     }
 }
 
-Game::TimeEvent::TimeEvent(long time, TimeListener* timeListener, void* cookie)
+Game::TimeEvent::TimeEvent(double time, TimeListener* timeListener, void* cookie)
             : time(time), listener(timeListener), cookie(cookie)
 {
 }

+ 11 - 11
gameplay/src/Game.h

@@ -79,7 +79,7 @@ public:
      * 
      * @return The total absolute running time (in milliseconds).
      */
-    static long getAbsoluteTime();
+    static double getAbsoluteTime();
 
     /**
      * Gets the total game time (in milliseconds). This is the total accumulated game time (unpaused).
@@ -89,7 +89,7 @@ public:
      * 
      * @return The total game time (in milliseconds).
      */
-    static long getGameTime();
+    static double getGameTime();
 
     /**
      * Gets the game state.
@@ -297,7 +297,7 @@ public:
      * @param timeListener The TimeListener that will receive the event.
      * @param cookie The cookie data that the time event will contain.
      */
-    void schedule(long timeOffset, TimeListener* timeListener, void* cookie = 0);
+    void schedule(float timeOffset, TimeListener* timeListener, void* cookie = 0);
 
 protected:
 
@@ -324,7 +324,7 @@ protected:
      *
      * @param elapsedTime The elapsed game time.
      */
-    virtual void update(long elapsedTime) = 0;
+    virtual void update(float elapsedTime) = 0;
 
     /**
      * Render callback for handling rendering routines.
@@ -334,7 +334,7 @@ protected:
      *
      * @param elapsedTime The elapsed game time.
      */
-    virtual void render(long elapsedTime) = 0;
+    virtual void render(float elapsedTime) = 0;
 
     /**
      * Renders a single frame once and then swaps it to the display.
@@ -363,7 +363,7 @@ private:
     {
     public:
 
-        TimeEvent(long time, TimeListener* timeListener, void* cookie);
+        TimeEvent(double time, TimeListener* timeListener, void* cookie);
         // The comparator is used to determine the order of time events in the priority queue.
         bool operator<(const TimeEvent& v) const;
         
@@ -371,7 +371,7 @@ private:
          * The game time.
          * @see Game::getGameTime()
          */
-        long time;
+        double time;
         TimeListener* listener;
         void* cookie;
     };
@@ -398,7 +398,7 @@ private:
      * 
      * @param frameTime The current game frame time. Used to determine which time events need to be fired.
      */
-    void fireTimeEvents(long frameTime);
+    void fireTimeEvents(double frameTime);
 
     /**
      * Loads the game configuration.
@@ -407,9 +407,9 @@ private:
 
     bool _initialized;                          // If game has initialized yet.
     State _state;                               // The game state.
-    static long _pausedTimeLast;                // The last time paused.
-    static long _pausedTimeTotal;               // The total time paused.
-    long _frameLastFPS;                         // The last time the frame count was updated.
+    static double _pausedTimeLast;              // The last time paused.
+    static double _pausedTimeTotal;             // The total time paused.
+    double _frameLastFPS;                       // The last time the frame count was updated.
     unsigned int _frameCount;                   // The current frame count.
     unsigned int _frameRate;                    // The current frame rate.
     unsigned int _width;                        // The game's display width.

+ 12 - 2
gameplay/src/Joystick.cpp

@@ -22,7 +22,7 @@ Joystick* Joystick::create(Theme::Style* style, Properties* properties)
 {
     Joystick* joystick = new Joystick();
     joystick->initialize(style, properties);
-    joystick->_consumeTouchEvents = false;
+    joystick->_consumeInputEvents = false;
 
     return joystick;
 }
@@ -59,7 +59,7 @@ void Joystick::addListener(Control::Listener* listener, int eventFlags)
         GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
     }
 
-    _consumeTouchEvents = true;
+    _consumeInputEvents = true;
 
     Control::addListener(listener, eventFlags);
 }
@@ -94,6 +94,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 if (_value != value)
                 {
                     _value.set(value);
+                    _dirty = true;
                     notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
 
@@ -114,6 +115,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                     if (_value != value)
                     {
                         _value.set(value);
+                        _dirty = true;
                         notifyListeners(Control::Listener::VALUE_CHANGED);
                     }
                 }
@@ -126,6 +128,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                     if (_value != value)
                     {
                         _value.set(value);
+                        _dirty = true;
                         notifyListeners(Control::Listener::VALUE_CHANGED);
                     }
                 }
@@ -146,6 +149,7 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
                 if (_value != value)
                 {
                     _value.set(value);
+                    _dirty = true;
                     notifyListeners(Control::Listener::VALUE_CHANGED);
                 }
 
@@ -161,6 +165,12 @@ bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned i
 void Joystick::update(const Control* container, const Vector2& offset)
 {
     Control::update(container, offset);
+
+    _clearBounds.x -= _radius;
+    _clearBounds.y -= _radius;
+    float radiusx2 = _radius + _radius;
+    _clearBounds.width += radiusx2;
+    _clearBounds.height += radiusx2;
 }
 
 void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)

+ 2 - 2
gameplay/src/Label.cpp

@@ -20,7 +20,7 @@ Label* Label::create(Theme::Style* style, Properties* properties)
 {
     Label* label = new Label();
     label->initialize(style, properties);
-    label->_consumeTouchEvents = false;
+    label->_consumeInputEvents = false;
 
     return label;
 }
@@ -49,7 +49,7 @@ void Label::addListener(Control::Listener* listener, int eventFlags)
         GP_ERROR("VALUE_CHANGED event is not applicable to this control.");
     }
 
-    _consumeTouchEvents = true;
+    _consumeInputEvents = true;
 
     Control::addListener(listener, eventFlags);
 }

+ 15 - 1
gameplay/src/Node.cpp

@@ -21,7 +21,7 @@ namespace gameplay
 {
 
 Node::Node(const char* id)
-    : _scene(NULL), _firstChild(NULL), _nextSibling(NULL), _prevSibling(NULL), _parent(NULL), _childCount(NULL),
+    : _scene(NULL), _firstChild(NULL), _nextSibling(NULL), _prevSibling(NULL), _parent(NULL), _childCount(0),
     _nodeFlags(NODE_FLAG_VISIBLE), _camera(NULL), _light(NULL), _model(NULL), _form(NULL), _audioSource(NULL), _particleEmitter(NULL),
     _collisionObject(NULL), _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true), _userData(NULL)
 {
@@ -547,6 +547,20 @@ Vector3 Node::getForwardVectorView() const
     return vector;
 }
 
+Vector3 Node::getRightVectorWorld() const
+{
+    Vector3 vector;
+    getWorldMatrix().getRightVector(&vector);
+    return vector;
+}
+
+Vector3 Node::getUpVectorWorld() const
+{
+    Vector3 vector;
+    getWorldMatrix().getUpVector(&vector);
+    return vector;
+}
+
 Vector3 Node::getActiveCameraTranslationWorld() const
 {
     Scene* scene = getScene();

+ 24 - 0
gameplay/src/Node.h

@@ -341,14 +341,38 @@ public:
 
     /**
      * Returns the forward vector of the Node in world space.
+     *
+     * @return The forward vector in world space.
      */
     Vector3 getForwardVectorWorld() const;
 
     /**
      *  Returns the forward vector of the Node in view space.
+     *
+     * @param normalize True to return the vector normalized, false (default) otherwise.
+     *
+     * @return The forwward vector in view space.
      */
     Vector3 getForwardVectorView() const;
 
+    /**
+     * Returns the right vector of the Node in world space.
+     *
+     * @param normalize True to return the vector normalized, false (default) otherwise.
+     *
+     * @return The right vector in world space.
+     */
+    Vector3 getRightVectorWorld() const;
+
+    /**
+     * Returns the up vector of the Node in world space.
+     *
+     * @param normalize True to return the vector normalized, false (default) otherwise.
+     *
+     * @return The up vector in world space.
+     */
+    Vector3 getUpVectorWorld() const;
+
     /**
      * Returns the translation vector of the currently active camera for this node's scene.
      *

+ 6 - 8
gameplay/src/ParticleEmitter.cpp

@@ -28,7 +28,7 @@ ParticleEmitter::ParticleEmitter(SpriteBatch* batch, unsigned int particleCountM
     _spriteBatch(batch), _spriteTextureBlending(BLEND_TRANSPARENT),  _spriteTextureWidth(0), _spriteTextureHeight(0), _spriteTextureWidthRatio(0), _spriteTextureHeightRatio(0), _spriteTextureCoords(NULL),
     _spriteAnimated(false),  _spriteLooped(false), _spriteFrameCount(1), _spriteFrameRandomOffset(0),_spriteFrameDuration(0L), _spriteFrameDurationSecs(0.0f), _spritePercentPerFrame(0.0f),
     _node(NULL), _orbitPosition(false), _orbitVelocity(false), _orbitAcceleration(false),
-    _timePerEmission(PARTICLE_EMISSION_RATE_TIME_INTERVAL), _timeLast(0L), _timeRunning(0L)
+    _timePerEmission(PARTICLE_EMISSION_RATE_TIME_INTERVAL), _timeRunning(0)
 {
     GP_ASSERT(particleCountMax);
     _particles = new Particle[particleCountMax];
@@ -228,7 +228,6 @@ void ParticleEmitter::setEmissionRate(unsigned int rate)
 void ParticleEmitter::start()
 {
     _started = true;
-    _timeLast = Game::getGameTime();
 }
 
 void ParticleEmitter::stop()
@@ -786,8 +785,7 @@ ParticleEmitter::TextureBlending ParticleEmitter::getTextureBlendingFromString(c
     }
 }
 
-
-void ParticleEmitter::update(long elapsedTime)
+void ParticleEmitter::update(float elapsedTime)
 {
     if (!isActive())
     {
@@ -795,7 +793,7 @@ void ParticleEmitter::update(long elapsedTime)
     }
 
     // Calculate the time passed since last update.
-    float elapsedSecs = (float)elapsedTime * 0.001f;
+    float elapsedSecs = elapsedTime * 0.001f;
 
     if (_started && _emissionRate)
     {
@@ -804,13 +802,13 @@ void ParticleEmitter::update(long elapsedTime)
 
         // How many particles should we emit this frame?
         GP_ASSERT(_timePerEmission);
-        unsigned int emitCount = _timeRunning / _timePerEmission;
-            
+        unsigned int emitCount = (unsigned int)(_timeRunning / _timePerEmission);
+
         if (emitCount)
         {
             if ((int)_timePerEmission > 0)
             {
-                _timeRunning %= (int)_timePerEmission;
+                _timeRunning = fmodl(_timeRunning, _timePerEmission);
             }
 
             emit(emitCount);

+ 2 - 3
gameplay/src/ParticleEmitter.h

@@ -608,7 +608,7 @@ public:
      *
      * @param elapsedTime The amount of time that has passed since the last call to update(), in milliseconds.
      */
-    void update(long elapsedTime);
+    void update(float elapsedTime);
 
     /**
      * Draws the particles currently being emitted.
@@ -733,8 +733,7 @@ private:
     bool _orbitVelocity;
     bool _orbitAcceleration;
     float _timePerEmission;
-    long _timeLast;
-    long _timeRunning;
+    double _timeRunning;
 };
 
 }

+ 65 - 5
gameplay/src/PhysicsCollisionObject.cpp

@@ -2,6 +2,7 @@
 #include "PhysicsCollisionObject.h"
 #include "PhysicsController.h"
 #include "Game.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -55,11 +56,6 @@ PhysicsCollisionShape* PhysicsCollisionObject::getCollisionShape() const
     return _collisionShape;
 }
 
-PhysicsMotionState* PhysicsCollisionObject::getMotionState() const
-{
-    return _motionState;
-}
-
 bool PhysicsCollisionObject::isKinematic() const
 {
     switch (getType())
@@ -151,4 +147,68 @@ bool PhysicsCollisionObject::CollisionPair::operator < (const CollisionPair& col
     return false;
 }
 
+PhysicsCollisionObject::PhysicsMotionState::PhysicsMotionState(Node* node, const Vector3* centerOfMassOffset) : _node(node),
+    _centerOfMassOffset(btTransform::getIdentity())
+{
+    if (centerOfMassOffset)
+    {
+        // Store the center of mass offset.
+        _centerOfMassOffset.setOrigin(BV(*centerOfMassOffset));
+    }
+
+    updateTransformFromNode();
+}
+
+PhysicsCollisionObject::PhysicsMotionState::~PhysicsMotionState()
+{
+}
+
+void PhysicsCollisionObject::PhysicsMotionState::getWorldTransform(btTransform &transform) const
+{
+    GP_ASSERT(_node);
+    if (_node->getCollisionObject() && _node->getCollisionObject()->isKinematic())
+        updateTransformFromNode();
+
+    transform = _centerOfMassOffset.inverse() * _worldTransform;
+}
+
+void PhysicsCollisionObject::PhysicsMotionState::setWorldTransform(const btTransform &transform)
+{
+    GP_ASSERT(_node);
+
+    _worldTransform = transform * _centerOfMassOffset;
+        
+    const btQuaternion& rot = _worldTransform.getRotation();
+    const btVector3& pos = _worldTransform.getOrigin();
+
+    _node->setRotation(rot.x(), rot.y(), rot.z(), rot.w());
+    _node->setTranslation(pos.x(), pos.y(), pos.z());
+}
+
+void PhysicsCollisionObject::PhysicsMotionState::updateTransformFromNode() const
+{
+    GP_ASSERT(_node);
+
+    // Store the initial world transform (minus the scale) for use by Bullet later on.
+    Quaternion rotation;
+    const Matrix& m = _node->getWorldMatrix();
+    m.getRotation(&rotation);
+
+    if (!_centerOfMassOffset.getOrigin().isZero())
+    {
+        // When there is a center of mass offset, we modify the initial world transformation
+        // so that when physics is initially applied, the object is in the correct location.
+        btTransform offset = btTransform(BQ(rotation), btVector3(0.0f, 0.0f, 0.0f)) * _centerOfMassOffset.inverse();
+
+        btVector3 origin(m.m[12] + _centerOfMassOffset.getOrigin().getX() + offset.getOrigin().getX(), 
+                         m.m[13] + _centerOfMassOffset.getOrigin().getY() + offset.getOrigin().getY(), 
+                         m.m[14] + _centerOfMassOffset.getOrigin().getZ() + offset.getOrigin().getZ());
+        _worldTransform = btTransform(BQ(rotation), origin);
+    }
+    else
+    {
+        _worldTransform = btTransform(BQ(rotation), btVector3(m.m[12], m.m[13], m.m[14]));
+    }
+}
+
 }

+ 46 - 8
gameplay/src/PhysicsCollisionObject.h

@@ -3,7 +3,6 @@
 
 #include "Vector3.h"
 #include "PhysicsCollisionShape.h"
-#include "PhysicsMotionState.h"
 
 namespace gameplay
 {
@@ -211,6 +210,52 @@ public:
 
 protected:
 
+    /**
+     * Interface between GamePlay and Bullet to keep object transforms synchronized properly.
+     * 
+     * @see btMotionState
+     */
+    class PhysicsMotionState : public btMotionState
+    {
+        friend class PhysicsConstraint;
+
+    public:
+
+        /**
+         * Creates a physics motion state for a rigid body.
+         * 
+         * @param node The node that owns the rigid body that the motion state is being created for.
+         * @param centerOfMassOffset The translation offset to the center of mass of the rigid body.
+         */
+        PhysicsMotionState(Node* node, const Vector3* centerOfMassOffset = NULL);
+
+        /**
+         * Destructor.
+         */
+        virtual ~PhysicsMotionState();
+
+        /**
+         * @see btMotionState#getWorldTransform
+         */
+        virtual void getWorldTransform(btTransform &transform) const;
+
+        /**
+         * @see btMotionState#setWorldTransform
+         */
+        virtual void setWorldTransform(const btTransform &transform);
+
+        /**
+         * Updates the motion state's world transform from the GamePlay Node object's world transform.
+         */
+        void updateTransformFromNode() const;
+
+    private:
+
+        Node* _node;
+        btTransform _centerOfMassOffset;
+        mutable btTransform _worldTransform;
+    };
+
     /**
      * Constructor.
      */
@@ -223,13 +268,6 @@ protected:
      */
     virtual btCollisionObject* getCollisionObject() const = 0;
 
-    /**
-     * Returns the physics motion state.
-     *
-     * @return The motion state object.
-     */
-    PhysicsMotionState* getMotionState() const;
-
     /**
      * Pointer to Node contained by this collision object.
      */ 

+ 2 - 3
gameplay/src/PhysicsConstraint.cpp

@@ -2,7 +2,6 @@
 #include "PhysicsConstraint.h"
 #include "Game.h"
 #include "Node.h"
-#include "PhysicsMotionState.h"
 #include "PhysicsRigidBody.h"
 
 namespace gameplay
@@ -166,8 +165,8 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 
 Vector3 PhysicsConstraint::offsetByCenterOfMass(const Node* node, const Vector3& v)
 {
-    GP_ASSERT(node && node->getCollisionObject() && node->getCollisionObject()->getMotionState());
-    btVector3 centerOfMassOffset = (node->getCollisionObject()->getMotionState())->_centerOfMassOffset.getOrigin();
+    GP_ASSERT(node && node->getCollisionObject() && node->getCollisionObject()->_motionState);
+    btVector3 centerOfMassOffset = node->getCollisionObject()->_motionState->_centerOfMassOffset.getOrigin();
     return Vector3(v.x + centerOfMassOffset.x(), v.y + centerOfMassOffset.y(), v.z + centerOfMassOffset.z());
 }
 

+ 23 - 19
gameplay/src/PhysicsController.cpp

@@ -2,7 +2,6 @@
 #include "PhysicsController.h"
 #include "PhysicsRigidBody.h"
 #include "PhysicsCharacter.h"
-#include "PhysicsMotionState.h"
 #include "Game.h"
 #include "MeshPart.h"
 #include "Bundle.h"
@@ -24,13 +23,15 @@ PhysicsController::PhysicsController()
   : _collisionConfiguration(NULL), _dispatcher(NULL),
     _overlappingPairCache(NULL), _solver(NULL), _world(NULL), _ghostPairCallback(NULL),
     _debugDrawer(NULL), _status(PhysicsController::Listener::DEACTIVATED), _listeners(NULL),
-    _gravity(btScalar(0.0), btScalar(-9.8), btScalar(0.0))
+    _gravity(btScalar(0.0), btScalar(-9.8), btScalar(0.0)), _collisionCallback(NULL)
 {
     // Default gravity is 9.8 along the negative Y axis.
+    _collisionCallback = new CollisionCallback(this);
 }
 
 PhysicsController::~PhysicsController()
 {
+    SAFE_DELETE(_collisionCallback);
     SAFE_DELETE(_ghostPairCallback);
     SAFE_DELETE(_debugDrawer);
     SAFE_DELETE(_listeners);
@@ -158,8 +159,11 @@ bool PhysicsController::rayTest(const Ray& ray, float distance, PhysicsControlle
 {
     GP_ASSERT(_world);
 
-    btCollisionWorld::ClosestRayResultCallback callback(BV(ray.getOrigin()), BV(distance * ray.getDirection()));
-    _world->rayTest(BV(ray.getOrigin()), BV(distance * ray.getDirection()), callback);
+    btVector3 rayFromWorld(BV(ray.getOrigin()));
+    btVector3 rayToWorld(rayFromWorld + BV(ray.getDirection() * distance));
+
+    btCollisionWorld::ClosestRayResultCallback callback(rayFromWorld, rayToWorld);
+    _world->rayTest(rayFromWorld, rayToWorld, callback);
     if (callback.hasHit())
     {
         if (result)
@@ -265,14 +269,14 @@ bool PhysicsController::sweepTest(PhysicsCollisionObject* object, const Vector3&
     return false;
 }
 
-btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, 
+btScalar PhysicsController::CollisionCallback::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, 
     const btCollisionObject* b, int partIdB, int indexB)
 {
-    GP_ASSERT(Game::getInstance()->getPhysicsController());
+    GP_ASSERT(_pc);
 
     // Get pointers to the PhysicsCollisionObject objects.
-    PhysicsCollisionObject* objectA = Game::getInstance()->getPhysicsController()->getCollisionObject(a);
-    PhysicsCollisionObject* objectB = Game::getInstance()->getPhysicsController()->getCollisionObject(b);
+    PhysicsCollisionObject* objectA = _pc->getCollisionObject(a);
+    PhysicsCollisionObject* objectB = _pc->getCollisionObject(b);
 
     // If the given collision object pair has collided in the past, then
     // we notify the listeners only if the pair was not colliding
@@ -281,20 +285,20 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
     PhysicsCollisionObject::CollisionPair pair(objectA, objectB);
 
     CollisionInfo* collisionInfo;
-    if (_collisionStatus.count(pair) > 0)
+    if (_pc->_collisionStatus.count(pair) > 0)
     {
-        collisionInfo = &_collisionStatus[pair];
+        collisionInfo = &_pc->_collisionStatus[pair];
     }
     else
     {
         // Add a new collision pair for these objects.
-        collisionInfo = &_collisionStatus[pair];
+        collisionInfo = &_pc->_collisionStatus[pair];
 
         // Add the appropriate listeners.
         PhysicsCollisionObject::CollisionPair p1(pair.objectA, NULL);
-        if (_collisionStatus.count(p1) > 0)
+        if (_pc->_collisionStatus.count(p1) > 0)
         {
-            const CollisionInfo& ci = _collisionStatus[p1];
+            const CollisionInfo& ci = _pc->_collisionStatus[p1];
             std::vector<PhysicsCollisionObject::CollisionListener*>::const_iterator iter = ci._listeners.begin();
             for (; iter != ci._listeners.end(); iter++)
             {
@@ -303,9 +307,9 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
             }
         }
         PhysicsCollisionObject::CollisionPair p2(pair.objectB, NULL);
-        if (_collisionStatus.count(p2) > 0)
+        if (_pc->_collisionStatus.count(p2) > 0)
         {
-            const CollisionInfo& ci = _collisionStatus[p2];
+            const CollisionInfo& ci = _pc->_collisionStatus[p2];
             std::vector<PhysicsCollisionObject::CollisionListener*>::const_iterator iter = ci._listeners.begin();
             for (; iter != ci._listeners.end(); iter++)
             {
@@ -381,7 +385,7 @@ void PhysicsController::resume()
     // Unused
 }
 
-void PhysicsController::update(long elapsedTime)
+void PhysicsController::update(float elapsedTime)
 {
     GP_ASSERT(_world);
 
@@ -390,7 +394,7 @@ void PhysicsController::update(long elapsedTime)
     //
     // Note that stepSimulation takes elapsed time in seconds
     // so we divide by 1000 to convert from milliseconds.
-    _world->stepSimulation((float)elapsedTime * 0.001, 10);
+    _world->stepSimulation(elapsedTime * 0.001f, 10);
 
     // If we have status listeners, then check if our status has changed.
     if (_listeners)
@@ -471,9 +475,9 @@ void PhysicsController::update(long elapsedTime)
         if ((iter->second._status & REGISTERED) != 0 && (iter->second._status & REMOVE) == 0)
         {
             if (iter->first.objectB)
-                _world->contactPairTest(iter->first.objectA->getCollisionObject(), iter->first.objectB->getCollisionObject(), *this);
+                _world->contactPairTest(iter->first.objectA->getCollisionObject(), iter->first.objectB->getCollisionObject(), *_collisionCallback);
             else
-                _world->contactTest(iter->first.objectA->getCollisionObject(), *this);
+                _world->contactTest(iter->first.objectA->getCollisionObject(), *_collisionCallback);
         }
     }
 

+ 23 - 6
gameplay/src/PhysicsController.h

@@ -16,7 +16,7 @@ namespace gameplay
 /**
  * Defines a class for controlling game physics.
  */
-class PhysicsController : public btCollisionWorld::ContactResultCallback
+class PhysicsController
 {
     friend class Game;
     friend class PhysicsConstraint;
@@ -264,14 +264,30 @@ public:
      */
     bool sweepTest(PhysicsCollisionObject* object, const Vector3& endPosition, PhysicsController::HitResult* result = NULL);
 
-protected:
+private:
 
     /**
-     * Internal function used for Bullet integration (do not use or override).
+     * Internal class used to integrate with Bullet collision callbacks.
      */
-    btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, const btCollisionObject* b, int partIdB, int indexB);    
+    class CollisionCallback : public btCollisionWorld::ContactResultCallback
+    {
+    public:
+        /**
+         * Constructor.
+         * 
+         * @param pc The physics controller that owns the callback.
+         */
+        CollisionCallback(PhysicsController* pc) : _pc(pc) {}
 
-private:
+    protected:
+        /**
+            * Internal function used for Bullet integration (do not use or override).
+            */
+        btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, const btCollisionObject* b, int partIdB, int indexB);    
+
+    private:
+        PhysicsController* _pc;
+    };
 
     // Internal constants for the collision status cache.
     static const int DIRTY;
@@ -321,7 +337,7 @@ private:
     /**
      * Controller update.
      */
-    void update(long elapsedTime);
+    void update(float elapsedTime);
 
     // Adds the given collision listener for the two given collision objects.
     void addCollisionListener(PhysicsCollisionObject::CollisionListener* listener, PhysicsCollisionObject* objectA, PhysicsCollisionObject* objectB);
@@ -459,6 +475,7 @@ private:
     std::vector<Listener*>* _listeners;
     Vector3 _gravity;
     std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo> _collisionStatus;
+    CollisionCallback* _collisionCallback;
 
 };
 

+ 0 - 1
gameplay/src/PhysicsGenericConstraint.cpp

@@ -1,7 +1,6 @@
 #include "Base.h"
 #include "PhysicsGenericConstraint.h"
 #include "Node.h"
-#include "PhysicsMotionState.h"
 #include "PhysicsRigidBody.h"
 
 namespace gameplay

+ 0 - 72
gameplay/src/PhysicsMotionState.cpp

@@ -1,72 +0,0 @@
-#include "Base.h"
-#include "PhysicsMotionState.h"
-#include "Node.h"
-
-namespace gameplay
-{
-
-PhysicsMotionState::PhysicsMotionState(Node* node, const Vector3* centerOfMassOffset) : _node(node),
-    _centerOfMassOffset(btTransform::getIdentity())
-{
-    if (centerOfMassOffset)
-    {
-        // Store the center of mass offset.
-        _centerOfMassOffset.setOrigin(BV(*centerOfMassOffset));
-    }
-
-    updateTransformFromNode();
-}
-
-PhysicsMotionState::~PhysicsMotionState()
-{
-}
-
-void PhysicsMotionState::getWorldTransform(btTransform &transform) const
-{
-    GP_ASSERT(_node);
-    if (_node->getCollisionObject() && _node->getCollisionObject()->isKinematic())
-        updateTransformFromNode();
-
-    transform = _centerOfMassOffset.inverse() * _worldTransform;
-}
-
-void PhysicsMotionState::setWorldTransform(const btTransform &transform)
-{
-    GP_ASSERT(_node);
-
-    _worldTransform = transform * _centerOfMassOffset;
-        
-    const btQuaternion& rot = _worldTransform.getRotation();
-    const btVector3& pos = _worldTransform.getOrigin();
-
-    _node->setRotation(rot.x(), rot.y(), rot.z(), rot.w());
-    _node->setTranslation(pos.x(), pos.y(), pos.z());
-}
-
-void PhysicsMotionState::updateTransformFromNode() const
-{
-    GP_ASSERT(_node);
-
-    // Store the initial world transform (minus the scale) for use by Bullet later on.
-    Quaternion rotation;
-    const Matrix& m = _node->getWorldMatrix();
-    m.getRotation(&rotation);
-
-    if (!_centerOfMassOffset.getOrigin().isZero())
-    {
-        // When there is a center of mass offset, we modify the initial world transformation
-        // so that when physics is initially applied, the object is in the correct location.
-        btTransform offset = btTransform(BQ(rotation), btVector3(0.0f, 0.0f, 0.0f)) * _centerOfMassOffset.inverse();
-
-        btVector3 origin(m.m[12] + _centerOfMassOffset.getOrigin().getX() + offset.getOrigin().getX(), 
-                         m.m[13] + _centerOfMassOffset.getOrigin().getY() + offset.getOrigin().getY(), 
-                         m.m[14] + _centerOfMassOffset.getOrigin().getZ() + offset.getOrigin().getZ());
-        _worldTransform = btTransform(BQ(rotation), origin);
-    }
-    else
-    {
-        _worldTransform = btTransform(BQ(rotation), btVector3(m.m[12], m.m[13], m.m[14]));
-    }
-}
-
-}

+ 0 - 61
gameplay/src/PhysicsMotionState.h

@@ -1,61 +0,0 @@
-#ifndef PHYSICSMOTIONSTATE_H_
-#define PHYSICSMOTIONSTATE_H_
-
-#include "Vector3.h"
-
-namespace gameplay
-{
-
-class Node;
-
-/**
- * Interface between GamePlay and Bullet to keep object transforms synchronized properly.
- * 
- * @see btMotionState
- */
-class PhysicsMotionState : public btMotionState
-{
-    friend class PhysicsCollisionObject;
-    friend class PhysicsRigidBody;
-    friend class PhysicsGhostObject;
-    friend class PhysicsCharacter;
-    friend class PhysicsConstraint;
-
-protected:
-
-    /**
-     * Creates a physics motion state for a rigid body.
-     * 
-     * @param node The node that owns the rigid body that the motion state is being created for.
-     * @param centerOfMassOffset The translation offset to the center of mass of the rigid body.
-     */
-    PhysicsMotionState(Node* node, const Vector3* centerOfMassOffset = NULL);
-
-    /**
-     * Destructor.
-     */
-    virtual ~PhysicsMotionState();
-
-    /**
-     * @see btMotionState#getWorldTransform
-     */
-    virtual void getWorldTransform(btTransform &transform) const;
-
-    /**
-     * @see btMotionState#setWorldTransform
-     */
-    virtual void setWorldTransform(const btTransform &transform);
-
-private:
-
-    // Updates the motion state's world transform from the GamePlay Node object's world transform.
-    void updateTransformFromNode() const;
-
-    Node* _node;
-    btTransform _centerOfMassOffset;
-    mutable btTransform _worldTransform;
-};
-
-}
-
-#endif

+ 9 - 3
gameplay/src/PhysicsRigidBody.cpp

@@ -1,6 +1,5 @@
 #include "Base.h"
 #include "PhysicsRigidBody.h"
-#include "PhysicsMotionState.h"
 #include "PhysicsController.h"
 #include "Game.h"
 #include "Image.h"
@@ -44,7 +43,6 @@ PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Defi
     // Set other initially defined properties.
     setKinematic(parameters.kinematic);
     setAnisotropicFriction(parameters.anisotropicFriction);
-    setGravity(parameters.gravity);
 
     // Add ourself to the physics world.
     Game::getInstance()->getPhysicsController()->addCollisionObject(this);
@@ -185,6 +183,7 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 
     // Set the rigid body parameters to their defaults.
     Parameters parameters;
+    Vector3* gravity = NULL;
 
     // Load the defined rigid body parameters.
     properties->rewind();
@@ -221,7 +220,8 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         }
         else if (strcmp(name, "gravity") == 0)
         {
-            properties->getVector3(NULL, &parameters.gravity);
+            gravity = new Vector3();
+            properties->getVector3(NULL, gravity);
         }
         else
         {
@@ -233,6 +233,12 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     PhysicsRigidBody* body = new PhysicsRigidBody(node, *shape, parameters);
     SAFE_DELETE(shape);
 
+    if (gravity)
+    {
+        body->setGravity(*gravity);
+        SAFE_DELETE(gravity);
+    }
+
     return body;
 }
 

+ 9 - 5
gameplay/src/PhysicsRigidBody.h

@@ -72,18 +72,22 @@ public:
         Vector3 anisotropicFriction;
 
         /**
-         * The gravity acceleration factor for the rigid body.
+         * Constructor.
          */
-        Vector3 gravity;
+        Parameters() : mass(0.0f), friction(0.5f), restitution(0.0f),
+            linearDamping(0.0f), angularDamping(0.0f),
+            kinematic(false), anisotropicFriction(Vector3::one())
+        {
+        }
 
         /**
          * Constructor.
          */
-        Parameters(float mass = 0.0f, float friction = 0.5f, float resititution = 0.0f,
+        Parameters(float mass, float friction = 0.5f, float resititution = 0.0f,
             float linearDamping = 0.0f, float angularDamping = 0.0f, bool kinematic = false,
-            const Vector3& anisotropicFriction = Vector3::one(), const Vector3& gravity = Vector3::zero())
+            const Vector3& anisotropicFriction = Vector3::one())
             : mass(mass), friction(friction), restitution(restitution), linearDamping(linearDamping), angularDamping(angularDamping),
-              kinematic(kinematic), anisotropicFriction(anisotropicFriction), gravity(gravity)
+              kinematic(kinematic), anisotropicFriction(anisotropicFriction)
         {
         }
     };

+ 18 - 2
gameplay/src/Platform.h

@@ -3,6 +3,7 @@
 
 #include "Touch.h"
 #include "Keyboard.h"
+#include "Mouse.h"
 
 namespace gameplay
 {
@@ -66,14 +67,14 @@ public:
      *
      * @return The absolute platform time. (in milliseconds)
      */
-    static long getAbsoluteTime();
+    static double getAbsoluteTime();
 
     /**
      * Sets the absolute platform time since the start of the message pump.
      *
      * @param time The time to set (in milliseconds).
      */
-    static void setAbsoluteTime(long time);
+    static void setAbsoluteTime(double time);
 
     /**
      * Gets whether vertical sync is enabled for the game display.
@@ -143,6 +144,21 @@ public:
      */
     static void keyEventInternal(Keyboard::KeyEvent evt, int key);
 
+    /**
+     * Mouse callback on mouse events. If the game does not consume the mouse move event or left mouse click event
+     * then it is interpreted as a touch event instead.
+     *
+     * @param evt The mouse event that occurred.
+     * @param x The x position of the mouse in pixels. Left edge is zero.
+     * @param y The y position of the mouse in pixels. Top edge is zero.
+     * @param wheelDelta The number of mouse wheel ticks. Positive is up (forward), negative is down (backward).
+     *
+     * @return True if the mouse event is consumed or false if it is not consumed.
+     *
+     * @see Mouse::MouseEvent
+     */
+    static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
+
     /**
      * Sleeps synchronously for the given amount of time (in milliseconds).
      *

+ 26 - 10
gameplay/src/PlatformAndroid.cpp

@@ -25,8 +25,8 @@ static EGLConfig __eglConfig = 0;
 static int __width;
 static int __height;
 static struct timespec __timespec;
-static long __timeStart;
-static long __timeAbsolute;
+static double __timeStart;
+static double __timeAbsolute;
 static bool __vsync = WINDOW_VSYNC;
 static ASensorManager* __sensorManager;
 static ASensorEventQueue* __sensorEventQueue;
@@ -47,10 +47,10 @@ PFNGLISVERTEXARRAYOESPROC glIsVertexArray = NULL;
 namespace gameplay
 {
 
-static long timespec2millis(struct timespec *a)
+static double timespec2millis(struct timespec *a)
 {
     GP_ASSERT(a);
-    return a->tv_sec*1000 + a->tv_nsec/1000000;
+    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
 }
 
 extern void printError(const char* format, ...)
@@ -628,11 +628,11 @@ 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);
+                    gameplay::Platform::keyEventInternal(Keyboard::KEY_CHAR, character);
                 break;
                     
             case AKEY_EVENT_ACTION_UP:
-                Game::getInstance()->keyEvent(Keyboard::KEY_RELEASE, getKey(keycode, metastate));
+                gameplay::Platform::keyEventInternal(Keyboard::KEY_RELEASE, getKey(keycode, metastate));
                 break;
         }
     }
@@ -858,16 +858,16 @@ unsigned int Platform::getDisplayHeight()
     return __height;
 }
     
-long Platform::getAbsoluteTime()
+double Platform::getAbsoluteTime()
 {
     clock_gettime(CLOCK_REALTIME, &__timespec);
-    long now = timespec2millis(&__timespec);
+    double now = timespec2millis(&__timespec);
     __timeAbsolute = now - __timeStart;
 
     return __timeAbsolute;
 }
 
-void Platform::setAbsoluteTime(long time)
+void Platform::setAbsoluteTime(double time)
 {
     __timeAbsolute = time;
 }
@@ -941,8 +941,24 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
+}
+
+void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
     }
 }
 

+ 33 - 24
gameplay/src/PlatformMacOSX.mm

@@ -21,8 +21,8 @@ static int __height = 720;
 static float ACCELEROMETER_FACTOR_X = 90.0f / __width;
 static float ACCELEROMETER_FACTOR_Y = 90.0f / __height;
 
-static long __timeStart;
-static long __timeAbsolute;
+static double __timeStart;
+static double __timeAbsolute;
 static bool __vsync = WINDOW_VSYNC;
 static float __pitch;
 static float __roll;
@@ -37,17 +37,17 @@ static char* __title = NULL;
 static bool __fullscreen = false;
 
 
-long getMachTimeInMilliseconds()
+double getMachTimeInMilliseconds()
 {
-    static const int64_t kOneMillion = 1000 * 1000;
+    static const double kOneMillion = 1000 * 1000;
     static mach_timebase_info_data_t s_timebase_info;
     
     if (s_timebase_info.denom == 0) 
         (void) mach_timebase_info(&s_timebase_info);
     
     // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
-    GP_ASSERT(kOneMillion * s_timebase_info.denom);
-    return (long)((mach_absolute_time() * s_timebase_info.numer) / (kOneMillion * s_timebase_info.denom));
+    GP_ASSERT(s_timebase_info.denom);
+    return ((double)mach_absolute_time() * (double)s_timebase_info.numer) / (kOneMillion * (double)s_timebase_info.denom);
 }
 
 
@@ -213,7 +213,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 
 - (void) mouse: (Mouse::MouseEvent) mouseEvent orTouchEvent: (Touch::TouchEvent) touchEvent x: (float) x y: (float) y s: (int) s 
 {
-    if (!Game::getInstance()->mouseEvent(mouseEvent, x, y, s))
+    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, s))
     {
         gameplay::Platform::touchEventInternal(touchEvent, x, y, 0);
     }
@@ -236,7 +236,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 - (void)mouseMoved:(NSEvent *) event 
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
 }
 
 - (void) mouseDragged: (NSEvent*) event
@@ -254,14 +254,14 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
      NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
     __lx = point.x;
     __ly = __height - point.y;    
-    _game->mouseEvent(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void) rightMouseUp: (NSEvent*) event
 {
    __rightMouseDown = false;
     NSPoint point = [event locationInWindow];
-    _game->mouseEvent(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void) rightMouseDragged: (NSEvent*) event
@@ -284,27 +284,27 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     
     // In right-mouse case, whether __rightMouseDown is true or false
     // this should not matter, mouse move is still occuring
-    _game->mouseEvent(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
 }
 
 - (void)otherMouseDown: (NSEvent *) event 
 {
     __otherMouseDown = true;
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    _game->mouseEvent(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void)otherMouseUp: (NSEvent *) event 
 {
     __otherMouseDown = false;
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    _game->mouseEvent(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, point.x, __height - point.y, 0);
 }
 
 - (void)otherMouseDragged: (NSEvent *) event 
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    _game->mouseEvent(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, point.x, __height - point.y, 0);
 }
 
 - (void) mouseEntered: (NSEvent*)event
@@ -315,7 +315,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
 - (void)scrollWheel: (NSEvent *) event 
 {
     NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
-    Game::getInstance()->mouseEvent(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
+    gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, point.x, __height - point.y, (int)([event deltaY] * 10.0f));
 }
 
 - (void) mouseExited: (NSEvent*)event
@@ -717,13 +717,13 @@ unsigned int Platform::getDisplayHeight()
     return __height;
 }
 
-long Platform::getAbsoluteTime()
+double Platform::getAbsoluteTime()
 {
     __timeAbsolute = getMachTimeInMilliseconds();
     return __timeAbsolute;
 }
 
-void Platform::setAbsoluteTime(long time)
+void Platform::setAbsoluteTime(double time)
 {
     __timeAbsolute = time;
 }
@@ -770,17 +770,26 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-    }
 }
     
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    Game::getInstance()->keyEvent(evt, key);
-    Form::keyEventInternal(evt, key);
+void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
+    }
 }
-    
 
 void Platform::sleep(long ms)
 {

+ 28 - 18
gameplay/src/PlatformQNX.cpp

@@ -22,8 +22,8 @@
 using namespace std;
 
 struct timespec __timespec;
-static long __timeStart;
-static long __timeAbsolute;
+static double __timeStart;
+static double __timeAbsolute;
 static bool __vsync = WINDOW_VSYNC;
 static screen_context_t __screenContext;
 static screen_window_t __screenWindow;
@@ -751,10 +751,10 @@ error:
 /**
  * Convert the timespec into milliseconds.
  */
-long timespec2millis(struct timespec *a)
+double timespec2millis(struct timespec *a)
 {
     GP_ASSERT(a);
-    return a->tv_sec*1000 + a->tv_nsec/1000000;
+    return (1000.0 * a->tv_sec) + (0.000001 * a->tv_nsec);
 }
 
 /**
@@ -768,7 +768,7 @@ long timespec2millis(struct timespec *a)
  */
 void mouseOrTouchEvent(Mouse::MouseEvent mouseEvent, Touch::TouchEvent touchEvent, int x, int y)
 {
-    if (!Game::getInstance()->mouseEvent(mouseEvent, x, y, 0))
+    if (!gameplay::Platform::mouseEventInternal(mouseEvent, x, y, 0))
     {
         Platform::touchEventInternal(touchEvent, x, y, 0);
     }
@@ -887,14 +887,14 @@ int Platform::enterMessagePump()
                             {
                                 move = false;
                                 mouse_pressed |= SCREEN_RIGHT_MOUSE_BUTTON;
-                                Game::getInstance()->mouseEvent(Mouse::MOUSE_PRESS_RIGHT_BUTTON, position[0], position[1], 0);
+                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_RIGHT_BUTTON, position[0], position[1], 0);
                             }
                         }
                         else if (mouse_pressed & SCREEN_RIGHT_MOUSE_BUTTON)
                         {
                             move = false;
                             mouse_pressed &= ~SCREEN_RIGHT_MOUSE_BUTTON;
-                            Game::getInstance()->mouseEvent(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, position[0], position[1], 0);
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_RIGHT_BUTTON, position[0], position[1], 0);
                         }
 
                         // Handle middle mouse.
@@ -904,14 +904,14 @@ int Platform::enterMessagePump()
                             {
                                 move = false;
                                 mouse_pressed |= SCREEN_MIDDLE_MOUSE_BUTTON;
-                                Game::getInstance()->mouseEvent(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, position[0], position[1], 0);
+                                gameplay::Platform::mouseEventInternal(Mouse::MOUSE_PRESS_MIDDLE_BUTTON, position[0], position[1], 0);
                             }
                         }
                         else if (mouse_pressed & SCREEN_MIDDLE_MOUSE_BUTTON)
                         {
                             move = false;
                             mouse_pressed &= ~SCREEN_MIDDLE_MOUSE_BUTTON;
-                            Game::getInstance()->mouseEvent(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, position[0], position[1], 0);
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, position[0], position[1], 0);
                         }
 
                         // Fire a move event if none of the buttons changed.
@@ -921,13 +921,13 @@ int Platform::enterMessagePump()
                         }
                         else if (move)
                         {
-                            Game::getInstance()->mouseEvent(Mouse::MOUSE_MOVE, position[0], position[1], 0);
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_MOVE, position[0], position[1], 0);
                         }
 
                         // Handle mouse wheel events.
                         if (wheel)
                         {
-                            Game::getInstance()->mouseEvent(Mouse::MOUSE_WHEEL, position[0], position[1], -wheel);
+                            gameplay::Platform::mouseEventInternal(Mouse::MOUSE_WHEEL, position[0], position[1], -wheel);
                         }
                         break;
                     }
@@ -1044,16 +1044,16 @@ unsigned int Platform::getDisplayHeight()
     return __screenWindowSize[1];
 }
 
-long Platform::getAbsoluteTime()
+double Platform::getAbsoluteTime()
 {
     clock_gettime(CLOCK_REALTIME, &__timespec);
-    long now = timespec2millis(&__timespec);
+    double now = timespec2millis(&__timespec);
     __timeAbsolute = now - __timeStart;
 
     return __timeAbsolute;
 }
 
-void Platform::setAbsoluteTime(long time)
+void Platform::setAbsoluteTime(double time)
 {
     __timeAbsolute = time;
 }
@@ -1137,15 +1137,25 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-    }
 }
 
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
-    Game::getInstance()->keyEvent(evt, key);
-    Form::keyEventInternal(evt, key);
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
+    }
 }
 
 void Platform::sleep(long ms)

+ 37 - 23
gameplay/src/PlatformWin32.cpp

@@ -14,9 +14,9 @@ using gameplay::printError;
 static int __width = 1280;
 static int __height = 720;
 
-static long __timeTicksPerMillis;
-static long __timeStart;
-static long __timeAbsolute;
+static double __timeTicksPerMillis;
+static double __timeStart;
+static double __timeAbsolute;
 static bool __vsync = WINDOW_VSYNC;
 static float __roll;
 static float __pitch;
@@ -276,11 +276,6 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
     switch (msg)
     {
-    case WM_PAINT:
-        gameplay::Game::getInstance()->frame();
-        SwapBuffers(__hdc);
-        return 0;
-
     case WM_CLOSE:
         DestroyWindow(__hwnd);
         return 0;
@@ -295,7 +290,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         int y = GET_Y_LPARAM(lParam);
 
         UpdateCapture(wParam);
-        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
+        if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_LEFT_BUTTON, x, y, 0))
         {
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_PRESS, x, y, 0);
         }
@@ -306,7 +301,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         int x = GET_X_LPARAM(lParam);
         int y = GET_Y_LPARAM(lParam);
 
-        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON, x, y, 0))
+        if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_LEFT_BUTTON, x, y, 0))
         {
             gameplay::Platform::touchEventInternal(gameplay::Touch::TOUCH_RELEASE, x, y, 0);
         }
@@ -317,21 +312,21 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         UpdateCapture(wParam);
         lx = GET_X_LPARAM(lParam);
         ly = GET_Y_LPARAM(lParam);
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_RIGHT_BUTTON, lx, ly, 0);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON, lx, ly, 0);
         break;
 
     case WM_RBUTTONUP:
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_RIGHT_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         UpdateCapture(wParam);
         break;
 
     case WM_MBUTTONDOWN:
         UpdateCapture(wParam);
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_PRESS_MIDDLE_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         break;
 
     case WM_MBUTTONUP:
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON,  GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_RELEASE_MIDDLE_BUTTON, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0);
         UpdateCapture(wParam);
         break;
 
@@ -341,7 +336,7 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         int y = GET_Y_LPARAM(lParam);
 
         // Allow Game::mouseEvent a chance to handle (and possibly consume) the event.
-        if (!gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
+        if (!gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_MOVE, x, y, 0))
         {
             if ((wParam & MK_LBUTTON) == MK_LBUTTON)
             {
@@ -368,7 +363,11 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     }
 
     case WM_MOUSEWHEEL:
-        gameplay::Game::getInstance()->mouseEvent(gameplay::Mouse::MOUSE_WHEEL, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), GET_WHEEL_DELTA_WPARAM(wParam) / 120);
+        tagPOINT point;
+        point.x = GET_X_LPARAM(lParam);
+        point.y = GET_Y_LPARAM(lParam);
+        ScreenToClient(__hwnd, &point);
+        gameplay::Platform::mouseEventInternal(gameplay::Mouse::MOUSE_WHEEL, point.x, point.y, GET_WHEEL_DELTA_WPARAM(wParam) / 120);
         break;
 
     case WM_KEYDOWN:
@@ -633,7 +632,7 @@ int Platform::enterMessagePump()
     // Get the initial time.
     LARGE_INTEGER tps;
     QueryPerformanceFrequency(&tps);
-    __timeTicksPerMillis = (long)(tps.QuadPart / 1000L);
+    __timeTicksPerMillis = (double)(tps.QuadPart / 1000L);
     LARGE_INTEGER queryTime;
     QueryPerformanceCounter(&queryTime);
     GP_ASSERT(__timeTicksPerMillis);
@@ -663,6 +662,11 @@ int Platform::enterMessagePump()
                 break;
             }
         }
+        else
+        {
+            _game->frame();
+            SwapBuffers(__hdc);
+        }
 
         // If we are done, then exit.
         if (_game->getState() == Game::UNINITIALIZED)
@@ -687,7 +691,7 @@ unsigned int Platform::getDisplayHeight()
     return __height;
 }
     
-long Platform::getAbsoluteTime()
+double Platform::getAbsoluteTime()
 {
     LARGE_INTEGER queryTime;
     QueryPerformanceCounter(&queryTime);
@@ -697,7 +701,7 @@ long Platform::getAbsoluteTime()
     return __timeAbsolute;
 }
 
-void Platform::setAbsoluteTime(long time)
+void Platform::setAbsoluteTime(double time)
 {
     __timeAbsolute = time;
 }
@@ -745,15 +749,25 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-    }
 }
 
 void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
 {
-    Game::getInstance()->keyEvent(evt, key);
-    Form::keyEventInternal(evt, key);
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
+    }
 }
 
 void Platform::sleep(long ms)

+ 26 - 17
gameplay/src/PlatformiOS.mm

@@ -37,14 +37,14 @@ extern const int WINDOW_SCALE = [[UIScreen mainScreen] scale];
 static AppDelegate *__appDelegate = NULL;
 static View* __view = NULL;
 
-static long __timeStart;
-static long __timeAbsolute;
+static double __timeStart;
+static double __timeAbsolute;
 static bool __vsync = WINDOW_VSYNC;
 static float __pitch;
 static float __roll;
 
 
-long getMachTimeInMilliseconds();
+double getMachTimeInMilliseconds();
 
 int getKey(unichar keyCode);
 
@@ -580,17 +580,17 @@ int getKey(unichar keyCode);
 @end
 
 
-long getMachTimeInMilliseconds()
+dobule getMachTimeInMilliseconds()
 {
-    static const int64_t kOneMillion = 1000 * 1000;
+    static const double kOneMillion = 1000 * 1000;
     static mach_timebase_info_data_t s_timebase_info;
     
     if (s_timebase_info.denom == 0) 
         (void) mach_timebase_info(&s_timebase_info);
     
     // mach_absolute_time() returns billionth of seconds, so divide by one million to get milliseconds
-    GP_ASSERT(kOneMillion * s_timebase_info.denom);
-    return (long)((mach_absolute_time() * s_timebase_info.numer) / (kOneMillion * s_timebase_info.denom));
+    GP_ASSERT(s_timebase_info.denom);
+    return ((double)mach_absolute_time() * (double)s_timebase_info.numer) / (kOneMillion * (double)s_timebase_info.denom);
 }
 
 int getKey(unichar keyCode) 
@@ -868,13 +868,13 @@ unsigned int Platform::getDisplayHeight()
     return size.height;
 }
 
-long Platform::getAbsoluteTime()
+dobule Platform::getAbsoluteTime()
 {
     __timeAbsolute = getMachTimeInMilliseconds();
     return __timeAbsolute;
 }
 
-void Platform::setAbsoluteTime(long time)
+void Platform::setAbsoluteTime(double time)
 {
     __timeAbsolute = time;
 }
@@ -928,17 +928,26 @@ void Platform::displayKeyboard(bool display)
 void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     if (!Form::touchEventInternal(evt, x, y, contactIndex))
-    {
         Game::getInstance()->touchEvent(evt, x, y, contactIndex);
-    }
-}
-
-void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
-{
-    Game::getInstance()->keyEvent(evt, key);
-    Form::keyEventInternal(evt, key);
 }
     
+void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
+{
+    if (!Form::keyEventInternal(evt, key))
+        Game::getInstance()->keyEvent(evt, key);
+}
+
+bool Platform::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
+{
+    if (Form::mouseEventInternal(evt, x, y, wheelDelta))
+    {
+        return true;
+    }
+    else
+    {
+        return Game::getInstance()->mouseEvent(evt, x, y, wheelDelta);
+    }
+}    
 
 void Platform::sleep(long ms)
 {

+ 4 - 1
gameplay/src/SceneLoader.cpp

@@ -131,7 +131,7 @@ void SceneLoader::addSceneNodeProperty(SceneNode& sceneNode, SceneNodeProperty::
 
     // If there is a non-GPB file that needs to be loaded later, add an 
     // empty entry to the properties table to signify it.
-    if (urlStr.length() > 0 && urlStr.find(".gpb") == urlStr.npos && _properties.count(urlStr) == 0)
+    if (urlStr.length() > 0 && urlStr.find(".") != urlStr.npos && urlStr.find(".gpb") == urlStr.npos && _properties.count(urlStr) == 0)
         _properties[urlStr] = NULL;
 
     // Add the node property to the list of node properties to be resolved later.
@@ -177,6 +177,9 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             return;
         }
 
+        // If the URL didn't specify a particular namespace within the file, pick the first one.
+        p = (strlen(p->getNamespace()) > 0) ? p : p->getNextNamespace();
+
         switch (snp._type)
         {
         case SceneNodeProperty::AUDIO:

+ 6 - 6
gameplay/src/ScreenDisplayer.h

@@ -27,7 +27,7 @@ public:
      * @param cookie See {@link Game#renderOnce}.
      * @param time The minimum amount of time to display the screen (in milliseconds).
      */
-    template <typename T> void run(T* instance, void (T::*method) (void*), void* cookie, long time);
+    template <typename T> void run(T* instance, void (T::*method) (void*), void* cookie, unsigned long time);
 
     /**
      * Destructor.
@@ -37,14 +37,14 @@ public:
 private:
 
     long _time;
-    long _startTime;
+    double _startTime;
 };
 
-inline ScreenDisplayer::ScreenDisplayer() : _time(0L), _startTime(0L)
+inline ScreenDisplayer::ScreenDisplayer() : _time(0L), _startTime(0)
 {
 }
 
-template <typename T> void ScreenDisplayer::run(T* instance, void (T::*method) (void*), void* cookie, long time)
+template <typename T> void ScreenDisplayer::run(T* instance, void (T::*method) (void*), void* cookie, unsigned long time)
 {
     _time = time;
     Game::getInstance()->renderOnce(instance, method, cookie);
@@ -53,9 +53,9 @@ template <typename T> void ScreenDisplayer::run(T* instance, void (T::*method) (
 
 inline ScreenDisplayer::~ScreenDisplayer()
 {
-    long elapsedTime = Game::getInstance()->getGameTime() - _startTime;
+    long elapsedTime = (long)(Game::getInstance()->getGameTime() - _startTime);
     if (elapsedTime < _time)
-        Platform::sleep(_time - (Game::getInstance()->getGameTime() - _startTime));
+        Platform::sleep(_time - elapsedTime);
 }
 
 /**

+ 1 - 0
gameplay/src/Slider.cpp

@@ -134,6 +134,7 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
         if (evt == Touch::TOUCH_RELEASE)
         {
             _state = NORMAL;
+            _dirty = true;
         }
         break;
     }

+ 5 - 8
gameplay/src/SpriteBatch.cpp

@@ -159,8 +159,9 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
 
     // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
-    material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getOrthoMatrix);
-
+    Game* game = Game::getInstance();
+    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &batch->_projectionMatrix);
+    material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getProjectionMatrix);
     return batch;
 }
 
@@ -431,15 +432,11 @@ Material* SpriteBatch::getMaterial() const
 
 void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
 {
-    // Bind the specified matrix to a parameter named 'u_projectionMatrix' (assumed to exist).
-    _batch->getMaterial()->getParameter("u_projectionMatrix")->setValue(matrix);
+    _projectionMatrix = matrix;
 }
 
-const Matrix& SpriteBatch::getOrthoMatrix() const
+const Matrix& SpriteBatch::getProjectionMatrix() const
 {
-    // Update matrix with ortho projection and return it.
-    Game* game = Game::getInstance();
-    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &_projectionMatrix);
     return _projectionMatrix;
 }
 

+ 2 - 2
gameplay/src/SpriteBatch.h

@@ -273,6 +273,8 @@ public:
      */
     void setProjectionMatrix(const Matrix& matrix);
 
+    const Matrix& getProjectionMatrix() const;
+
 private:
 
     /**
@@ -303,8 +305,6 @@ private:
      */
     SpriteBatch(const SpriteBatch& copy);
 
-    const Matrix& getOrthoMatrix() const;
-
     /**
      * Adds a single sprite to a SpriteVertex array.
      * 

+ 19 - 8
gameplay/src/TextBox.cpp

@@ -54,21 +54,21 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             _state = ACTIVE;
             Game::getInstance()->displayKeyboard(true);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         else if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
                  y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
         {
             setCaretLocation(x, y);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         else
         {
             _state = NORMAL;
             Game::getInstance()->displayKeyboard(false);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         break;
     case Touch::TOUCH_MOVE:
@@ -78,7 +78,7 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         {
             setCaretLocation(x, y);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         break;
     case Touch::TOUCH_RELEASE:
@@ -88,23 +88,25 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             setCaretLocation(x, y);
             _state = FOCUS;
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         else
         {
             _state = NORMAL;
             Game::getInstance()->displayKeyboard(false);
             _dirty = true;
-            return _consumeTouchEvents;
+            return _consumeInputEvents;
         }
         break;
     }
 
-    return _consumeTouchEvents;
+    return _consumeInputEvents;
 }
 
-void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
+bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 {
+    bool consume = false;
+
     if (_state == FOCUS)
     {
         switch (evt)
@@ -142,6 +144,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         notifyListeners(Listener::TEXT_CHANGED);
                         break;
                     }
@@ -158,6 +161,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex - 1,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         break;
                     }
                     case Keyboard::KEY_RIGHT_ARROW:
@@ -173,6 +177,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex + 1,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         break;
                     }
                     case Keyboard::KEY_UP_ARROW:
@@ -187,6 +192,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         break;
                     }
                     case Keyboard::KEY_DOWN_ARROW:
@@ -201,6 +207,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         font->getIndexAtLocation(_text.c_str(), _textBounds, fontSize, _caretLocation, &_caretLocation,
                             textAlignment, true, rightToLeft);
                         _dirty = true;
+                        consume = true;
                         break;
                     }
                 }
@@ -230,6 +237,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                                 textAlignment, true, rightToLeft);
 
                             _dirty = true;
+                            consume = true;
                         }
                         break;
                     }
@@ -240,6 +248,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     {
                         // Insert character into string.
                         _text.insert(textIndex, 1, (char)key);
+                        consume = true;
 
                         // Get new location of caret.
                         font->getLocationAtIndex(_text.c_str(), _textBounds, fontSize, &_caretLocation, textIndex + 1,
@@ -289,6 +298,8 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
     }
 
     _lastKeypress = key;
+
+    return (consume & _consumeInputEvents);
 }
 
 void TextBox::update(const Control* container, const Vector2& offset)

+ 1 - 1
gameplay/src/TextBox.h

@@ -104,7 +104,7 @@ protected:
      * @see Keyboard::KeyEvent
      * @see Keyboard::Key
      */
-    void keyEvent(Keyboard::KeyEvent evt, int key);
+    bool keyEvent(Keyboard::KeyEvent evt, int key);
 
     /**
      * Called when a control's properties change.  Updates this control's internal rendering

+ 19 - 0
gameplay/src/Theme.cpp

@@ -451,6 +451,25 @@ Theme::Style* Theme::getStyle(const char* name) const
     return NULL;
 }
 
+Theme::Style* Theme::getEmptyStyle()
+{
+    Theme::Style* emptyStyle = getStyle("EMPTY_STYLE");
+
+    if (!emptyStyle)
+    {
+        Theme::Style::Overlay* overlay = Theme::Style::Overlay::create();
+        overlay->addRef();
+        overlay->addRef();
+        overlay->addRef();
+        emptyStyle = new Theme::Style((Theme*)this, "EMPTY_STYLE", 1.0f / _texture->getWidth(), 1.0f / _texture->getHeight(),
+            Theme::Margin::empty(), Theme::Border::empty(), overlay, overlay, overlay, overlay);
+
+        _styles.push_back(emptyStyle);
+    }
+
+    return emptyStyle;
+}
+
 void Theme::setProjectionMatrix(const Matrix& matrix)
 {
     GP_ASSERT(_spriteBatch);

+ 3 - 0
gameplay/src/Theme.h

@@ -419,6 +419,9 @@ private:
 
     Theme::Style* getStyle(const char* id) const;
 
+    // Used when a control does not specify a style.
+    Theme::Style* getEmptyStyle();
+
     void setProjectionMatrix(const Matrix& matrix);
 
     SpriteBatch* getSpriteBatch() const;