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

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

Steve Grenier 13 лет назад
Родитель
Сommit
879ce17664

+ 1 - 3
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" />
@@ -365,4 +363,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>

+ 1 - 7
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>
@@ -715,4 +709,4 @@
       <Filter>src</Filter>
     </None>
   </ItemGroup>
-</Project>
+</Project>

+ 1 - 1
gameplay/src/Button.h

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

+ 245 - 114
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,7 +568,7 @@ Layout::Type Container::getLayoutType(const char* layoutString)
 void Container::updateScroll()
 {
     // Update Time.
-    double lastFrameTime = Game::getGameTime();
+    static double lastFrameTime = Game::getGameTime();
     double frameTime = Game::getGameTime();
     float elapsedTime = (float)(frameTime - lastFrameTime);
     lastFrameTime = frameTime;
@@ -641,8 +577,6 @@ void Container::updateScroll()
     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,36 +744,38 @@ 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;
-        float timeSinceLastMove = (float)(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;
 
-        float timeTakenX = (float)(Game::getAbsoluteTime() - _scrollingStartTimeX);
+        float timeTakenX = (float)(gameTime - _scrollingStartTimeX);
         float elapsedSecsX = timeTakenX * 0.001f;
-        float timeTakenY = (float)(Game::getAbsoluteTime() - _scrollingStartTimeY);
+        float timeTakenY = (float)(gameTime - _scrollingStartTimeY);
         float elapsedSecsY = timeTakenY * 0.001f;
 
         float vx = dx;
@@ -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)

+ 37 - 2
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;
     /**
@@ -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)

+ 35 - 8
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
@@ -883,9 +910,9 @@ protected:
     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

+ 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

@@ -543,63 +543,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);
                     }
                 }
             }
@@ -615,10 +572,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);
                 }
             }
         }
@@ -627,7 +581,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++)
@@ -636,9 +590,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;
 }
 
 int Form::nextHighestPowerOfTwo(int x)

+ 31 - 1
gameplay/src/Form.h

@@ -8,6 +8,7 @@
 #include "FrameBuffer.h"
 #include "Touch.h"
 #include "Keyboard.h"
+#include "Mouse.h"
 
 namespace gameplay
 {
@@ -175,9 +176,38 @@ private:
 
     /**
      * Propagate key events to enabled forms.
+     *
+     * @return Whether the key event was consumed by a form.
+     */
+    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 void keyEventInternal(Keyboard::KeyEvent evt, int key);
+    static bool mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelta);
 
+    /**
+     * 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);
+
+    /**
+     * 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 int nextHighestPowerOfTwo(int x);
 
     Theme* _theme;              // The Theme applied to this Form.

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

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

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

+ 16 - 15
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);
@@ -268,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
@@ -284,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++)
             {
@@ -306,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++)
             {
@@ -474,9 +475,9 @@ void PhysicsController::update(float 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);
         }
     }
 

+ 22 - 5
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;
@@ -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

+ 0 - 1
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"

+ 16 - 0
gameplay/src/Platform.h

@@ -3,6 +3,7 @@
 
 #include "Touch.h"
 #include "Keyboard.h"
+#include "Mouse.h"
 
 namespace gameplay
 {
@@ -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).
      *

+ 19 - 3
gameplay/src/PlatformAndroid.cpp

@@ -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;
         }
     }
@@ -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);
     }
 }
 

+ 25 - 16
gameplay/src/PlatformMacOSX.mm

@@ -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
@@ -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)
 {

+ 21 - 11
gameplay/src/PlatformQNX.cpp

@@ -768,7 +768,7 @@ double 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;
                     }
@@ -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)

+ 26 - 12
gameplay/src/PlatformWin32.cpp

@@ -290,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);
         }
@@ -301,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);
         }
@@ -312,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;
 
@@ -336,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)
             {
@@ -363,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:
@@ -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)

+ 17 - 8
gameplay/src/PlatformiOS.mm

@@ -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:

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