Explorar el Código

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

Kieran Cunney hace 13 años
padre
commit
fa30b7eba5

+ 4 - 1
gameplay/gameplay.vcxproj

@@ -48,6 +48,7 @@
     <ClCompile Include="src\gameplay-main-win32.cpp" />
     <ClCompile Include="src\gameplay-main-win32.cpp" />
     <ClCompile Include="src\Image.cpp" />
     <ClCompile Include="src\Image.cpp" />
     <ClCompile Include="src\Joint.cpp" />
     <ClCompile Include="src\Joint.cpp" />
+    <ClCompile Include="src\Joystick.cpp" />
     <ClCompile Include="src\Label.cpp" />
     <ClCompile Include="src\Label.cpp" />
     <ClCompile Include="src\Layout.cpp" />
     <ClCompile Include="src\Layout.cpp" />
     <ClCompile Include="src\Light.cpp" />
     <ClCompile Include="src\Light.cpp" />
@@ -138,6 +139,7 @@
     <ClInclude Include="src\gameplay.h" />
     <ClInclude Include="src\gameplay.h" />
     <ClInclude Include="src\Image.h" />
     <ClInclude Include="src\Image.h" />
     <ClInclude Include="src\Joint.h" />
     <ClInclude Include="src\Joint.h" />
+    <ClInclude Include="src\Joystick.h" />
     <ClInclude Include="src\Keyboard.h" />
     <ClInclude Include="src\Keyboard.h" />
     <ClInclude Include="src\Label.h" />
     <ClInclude Include="src\Label.h" />
     <ClInclude Include="src\Layout.h" />
     <ClInclude Include="src\Layout.h" />
@@ -233,6 +235,7 @@
     <None Include="src\Image.inl" />
     <None Include="src\Image.inl" />
     <None Include="src\MathUtil.inl" />
     <None Include="src\MathUtil.inl" />
     <None Include="src\MathUtilNeon.inl" />
     <None Include="src\MathUtilNeon.inl" />
+    <None Include="src\Joystick.inl" />
     <None Include="src\Matrix.inl" />
     <None Include="src\Matrix.inl" />
     <None Include="src\MeshBatch.inl" />
     <None Include="src\MeshBatch.inl" />
     <None Include="src\Plane.inl" />
     <None Include="src\Plane.inl" />
@@ -362,4 +365,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
   </ImportGroup>
-</Project>
+</Project>

+ 10 - 4
gameplay/gameplay.vcxproj.filters

@@ -279,6 +279,9 @@
     <ClCompile Include="src\FlowLayout.cpp">
     <ClCompile Include="src\FlowLayout.cpp">
       <Filter>src</Filter>
       <Filter>src</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="src\Joystick.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
     <ClInclude Include="src\Animation.h">
@@ -553,13 +556,13 @@
     </ClInclude>
     </ClInclude>
     <ClInclude Include="src\FlowLayout.h">
     <ClInclude Include="src\FlowLayout.h">
       <Filter>src</Filter>
       <Filter>src</Filter>
-    </ClInclude>
-    <ClInclude Include="src\ScrollLayout.h">
+    </ClInclude>
+    <ClInclude Include="src\Joystick.h">
       <Filter>src</Filter>
       <Filter>src</Filter>
     </ClInclude>
     </ClInclude>
     <ClInclude Include="src\MathUtil.h">
     <ClInclude Include="src\MathUtil.h">
       <Filter>src</Filter>
       <Filter>src</Filter>
-    </ClInclude>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
     <None Include="res\shaders\bumped-specular.vsh">
@@ -664,6 +667,9 @@
     <None Include="src\MathUtilNeon.inl">
     <None Include="src\MathUtilNeon.inl">
       <Filter>src</Filter>
       <Filter>src</Filter>
     </None>
     </None>
+    <None Include="src\Joystick.inl">
+      <Filter>src</Filter>
+    </None>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <None Include="src\PhysicsFixedConstraint.inl">
     <None Include="src\PhysicsFixedConstraint.inl">
@@ -709,4 +715,4 @@
       <Filter>src</Filter>
       <Filter>src</Filter>
     </None>
     </None>
   </ItemGroup>
   </ItemGroup>
-</Project>
+</Project>

+ 2 - 2
gameplay/src/AbsoluteLayout.cpp

@@ -40,7 +40,7 @@ Layout::Type AbsoluteLayout::getType()
     return Layout::LAYOUT_ABSOLUTE;
     return Layout::LAYOUT_ABSOLUTE;
 }
 }
 
 
-void AbsoluteLayout::update(const Container* container)
+void AbsoluteLayout::update(const Container* container, const Vector2& offset)
 {
 {
     GP_ASSERT(container);
     GP_ASSERT(container);
 
 
@@ -53,7 +53,7 @@ void AbsoluteLayout::update(const Container* container)
         GP_ASSERT(control);
         GP_ASSERT(control);
 
 
         align(control, container);
         align(control, container);
-        control->update(container->getClip(), Vector2::zero());
+        control->update(container, offset);
     }
     }
 }
 }
 
 

+ 1 - 1
gameplay/src/AbsoluteLayout.h

@@ -41,7 +41,7 @@ protected:
      *
      *
      * @param container The container to update.
      * @param container The container to update.
      */
      */
-    void update(const Container* container);
+    void update(const Container* container, const Vector2& offset);
 
 
 private:
 private:
     
     

+ 15 - 2
gameplay/src/Bundle.cpp

@@ -598,10 +598,14 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
     if (sceneContext)
     if (sceneContext)
     {
     {
         node = sceneContext->findNode(id, true);
         node = sceneContext->findNode(id, true);
+        if (node)
+            node->addRef();
     }
     }
     else if (nodeContext)
     else if (nodeContext)
     {
     {
         node = nodeContext->findNode(id, true);
         node = nodeContext->findNode(id, true);
+        if (node)
+            node->addRef();
     }
     }
 
 
     if (node == NULL)
     if (node == NULL)
@@ -704,7 +708,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
             if (fseek(_file, sizeof(float) * 16, SEEK_CUR) != 0)
             if (fseek(_file, sizeof(float) * 16, SEEK_CUR) != 0)
             {
             {
                 GP_ERROR("Failed to skip over node transform for node '%s'.", id);
                 GP_ERROR("Failed to skip over node transform for node '%s'.", id);
-                return false;
+                return NULL;
             }
             }
             readString(_file);
             readString(_file);
 
 
@@ -724,6 +728,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
                 }
                 }
             }
             }
 
 
+            iter->second->addRef();
             return iter->second;
             return iter->second;
         }
         }
         else
         else
@@ -1108,6 +1113,7 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
                     Joint* joint = static_cast<Joint*>(n);
                     Joint* joint = static_cast<Joint*>(n);
                     joint->setInverseBindPose(skinData->inverseBindPoseMatrices[j]);
                     joint->setInverseBindPose(skinData->inverseBindPoseMatrices[j]);
                     skinData->skin->setJoint(joint, j);
                     skinData->skin->setJoint(joint, j);
+                    SAFE_RELEASE(joint);
                 }
                 }
             }
             }
         }
         }
@@ -1120,6 +1126,7 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
             GP_ASSERT(node);
             GP_ASSERT(node);
             Node* parent = node->getParent();
             Node* parent = node->getParent();
             
             
+            std::vector<Node*> loadedNodes;
             while (true)
             while (true)
             {
             {
                 if (parent)
                 if (parent)
@@ -1165,13 +1172,19 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
                     }
                     }
 
 
                     if (nodeID != rootJoint->getId())
                     if (nodeID != rootJoint->getId())
-                        loadNode(nodeID.c_str(), sceneContext, nodeContext);
+                        loadedNodes.push_back(loadNode(nodeID.c_str(), sceneContext, nodeContext));
 
 
                     break;
                     break;
                 }
                 }
             }
             }
 
 
             skinData->skin->setRootJoint(rootJoint);
             skinData->skin->setRootJoint(rootJoint);
+
+            // Release all the nodes that we loaded since the nodes are now owned by the mesh skin.
+            for (unsigned int i = 0; i < loadedNodes.size(); i++)
+            {
+                SAFE_RELEASE(loadedNodes[i]);
+            }
         }
         }
 
 
         // Remove the joint hierarchy from the scene since it is owned by the mesh skin.
         // Remove the joint hierarchy from the scene since it is owned by the mesh skin.

+ 2 - 2
gameplay/src/CheckBox.cpp

@@ -94,9 +94,9 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
     return Button::touchEvent(evt, x, y, contactIndex);
     return Button::touchEvent(evt, x, y, contactIndex);
 }
 }
 
 
-void CheckBox::update(const Rectangle& clip, const Vector2& offset)
+void CheckBox::update(const Control* container, const Vector2& offset)
 {
 {
-    Label::update(clip, offset);
+    Label::update(container, offset);
 
 
     Vector2 size;
     Vector2 size;
     if (_imageSize.isZero())
     if (_imageSize.isZero())

+ 2 - 2
gameplay/src/CheckBox.h

@@ -118,9 +118,9 @@ protected:
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this control's parent container.
+     * @param container This control's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw the checkbox icon associated with this control.
      * Draw the checkbox icon associated with this control.

+ 38 - 33
gameplay/src/Container.cpp

@@ -10,6 +10,7 @@
 #include "RadioButton.h"
 #include "RadioButton.h"
 #include "Slider.h"
 #include "Slider.h"
 #include "TextBox.h"
 #include "TextBox.h"
+#include "Joystick.h"
 #include "Game.h"
 #include "Game.h"
 
 
 namespace gameplay
 namespace gameplay
@@ -135,6 +136,10 @@ void Container::addControls(Theme* theme, Properties* properties)
         {
         {
             control = TextBox::create(controlStyle, controlSpace);
             control = TextBox::create(controlStyle, controlSpace);
         }
         }
+        else if (controlName == "JOYSTICK")
+        {
+            control = Joystick::create(controlStyle, controlSpace);
+        }
         else
         else
         {
         {
             GP_ERROR("Failed to create control; unrecognized control name '%s'.", controlName.c_str());
             GP_ERROR("Failed to create control; unrecognized control name '%s'.", controlName.c_str());
@@ -285,10 +290,10 @@ Animation* Container::getAnimation(const char* id) const
     return NULL;
     return NULL;
 }
 }
 
 
-void Container::update(const Rectangle& clip, const Vector2& offset)
+void Container::update(const Control* container, const Vector2& offset)
 {
 {
     // Update this container's viewport.
     // Update this container's viewport.
-    Control::update(clip, offset);
+    Control::update(container, offset);
 
 
     // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
     // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
     if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
     if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
@@ -297,6 +302,8 @@ void Container::update(const Rectangle& clip, const Vector2& offset)
         _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
         _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
         _scrollBarRightCap = getImage("scrollBarRightCap", _state);
         _scrollBarRightCap = getImage("scrollBarRightCap", _state);
 
 
+        GP_ASSERT(_scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap);
+
         _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
         _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
     }
     }
 
 
@@ -305,6 +312,8 @@ void Container::update(const Rectangle& clip, const Vector2& offset)
         _scrollBarTopCap = getImage("scrollBarTopCap", _state);
         _scrollBarTopCap = getImage("scrollBarTopCap", _state);
         _scrollBarVertical = getImage("verticalScrollBar", _state);
         _scrollBarVertical = getImage("verticalScrollBar", _state);
         _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
         _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
+
+        GP_ASSERT(_scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap);
         
         
         _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
         _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
     }
     }
@@ -313,15 +322,15 @@ void Container::update(const Rectangle& clip, const Vector2& offset)
     std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
     std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
 
 
     GP_ASSERT(_layout);
     GP_ASSERT(_layout);
-    _layout->update(this);
-
     if (_scroll != SCROLL_NONE)
     if (_scroll != SCROLL_NONE)
-        this->updateScroll(this);
+        updateScroll();
+    else
+        _layout->update(this, Vector2::zero());
 }
 }
 
 
-void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight)
+void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight)
 {
 {
-    if (_skin && needsClear)
+    if (needsClear)
     {
     {
         GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
         GL_ASSERT( glEnable(GL_SCISSOR_TEST) );
         GL_ASSERT( glClearColor(0, 0, 0, 0) );
         GL_ASSERT( glClearColor(0, 0, 0, 0) );
@@ -332,6 +341,11 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
         GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
         GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
 
 
         needsClear = false;
         needsClear = false;
+        cleared = true;
+    }
+    else if (!cleared)
+    {
+        needsClear = true;
     }
     }
 
 
     spriteBatch->begin();
     spriteBatch->begin();
@@ -346,7 +360,7 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
         GP_ASSERT(control);
         GP_ASSERT(control);
         if (!needsClear || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
         if (!needsClear || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
         {
         {
-            control->draw(spriteBatch, _viewportClipBounds, needsClear, targetHeight);
+            control->draw(spriteBatch, _viewportClipBounds, needsClear, cleared, targetHeight);
             Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
             Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
         }
         }
     }
     }
@@ -358,7 +372,7 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
 
 
         spriteBatch->begin();
         spriteBatch->begin();
 
 
-        if (_scrollBarBounds.height > 0 && (_scrolling || _velocity.y) && _scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap)
+        if (_scrollBarBounds.height > 0 && (_scrolling || _velocity.y))
         {
         {
             const Rectangle& topRegion = _scrollBarTopCap->getRegion();
             const Rectangle& topRegion = _scrollBarTopCap->getRegion();
             const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
             const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
@@ -388,7 +402,7 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
             spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, bottomUVs.u1, bottomUVs.v1, bottomUVs.u2, bottomUVs.v2, bottomColor, clipRegion);
             spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, bottomUVs.u1, bottomUVs.v1, bottomUVs.u2, bottomUVs.v2, bottomColor, clipRegion);
         }
         }
 
 
-        if (_scrollBarBounds.width > 0 && (_scrolling || _velocity.x) && _scrollBarLeftCap && _scrollBarHorizontal && _scrollBarRightCap)
+        if (_scrollBarBounds.width > 0 && (_scrolling || _velocity.x))
         {
         {
             const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
             const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
             const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
             const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
@@ -518,9 +532,8 @@ bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int con
         break;
         break;
     }
     }
 
 
-    if (!eventConsumed)
+    if (!eventConsumed && _scroll != SCROLL_NONE)
     {
     {
-        // Pass the event on to the layout.
         if (touchEventScroll(evt, x - xPos, y - yPos, contactIndex))
         if (touchEventScroll(evt, x - xPos, y - yPos, contactIndex))
         {
         {
             _dirty = true;
             _dirty = true;
@@ -586,24 +599,21 @@ Layout::Type Container::getLayoutType(const char* layoutString)
     }
     }
 }
 }
 
 
-void Container::updateScroll(const Container* container)
+void Container::updateScroll()
 {
 {
-    GP_ASSERT(container);
-
     // Update Time.
     // Update Time.
     static long lastFrameTime = Game::getGameTime();
     static long lastFrameTime = Game::getGameTime();
     long frameTime = Game::getGameTime();
     long frameTime = Game::getGameTime();
     long elapsedTime = (frameTime - lastFrameTime);
     long elapsedTime = (frameTime - lastFrameTime);
     lastFrameTime = frameTime;
     lastFrameTime = frameTime;
 
 
-    const Rectangle& containerBounds = container->getBounds();
-    const Theme::Border& containerBorder = container->getBorder(container->getState());
-    const Theme::Padding& containerPadding = container->getPadding();
+    const Theme::Border& containerBorder = getBorder(_state);
+    const Theme::Padding& containerPadding = getPadding();
 
 
     // Calculate total width and height.
     // Calculate total width and height.
     float totalWidth = 0;
     float totalWidth = 0;
     float totalHeight = 0;
     float totalHeight = 0;
-    std::vector<Control*> controls = container->getControls();
+    std::vector<Control*> controls = getControls();
     unsigned int controlsCount = controls.size();
     unsigned int controlsCount = controls.size();
     for (unsigned int i = 0; i < controlsCount; i++)
     for (unsigned int i = 0; i < controlsCount; i++)
     {
     {
@@ -625,10 +635,10 @@ void Container::updateScroll(const Container* container)
         }
         }
     }
     }
 
 
-    float vWidth = container->getImageRegion("verticalScrollBar", container->getState()).width;
-    float hHeight = container->getImageRegion("horizontalScrollBar", container->getState()).height;
-    float clipWidth = containerBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
-    float clipHeight = containerBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
+    float vWidth = getImageRegion("verticalScrollBar", _state).width;
+    float hHeight = getImageRegion("horizontalScrollBar", _state).height;
+    float clipWidth = _bounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right - vWidth;
+    float clipHeight = _bounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom - hHeight;
 
 
     // Apply and dampen inertia.
     // Apply and dampen inertia.
     if (!_scrolling && !_velocity.isZero())
     if (!_scrolling && !_velocity.isZero())
@@ -687,11 +697,7 @@ void Container::updateScroll(const Container* container)
                          scrollWidth, scrollHeight);
                          scrollWidth, scrollHeight);
 
 
     // Position controls within scroll area.
     // Position controls within scroll area.
-    for (unsigned int i = 0; i < controlsCount; i++)
-    {
-        Control* control = controls.at(i);
-        control->update(container->getClip(), _scrollPosition);
-    }
+    _layout->update(this, _scrollPosition);
 }
 }
 
 
 bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
@@ -704,7 +710,7 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
         _velocity.set(0, 0);
         _velocity.set(0, 0);
         _scrolling = true;
         _scrolling = true;
         _startTimeX = _startTimeY = 0;
         _startTimeX = _startTimeY = 0;
-        break;
+        return true;
 
 
     case Touch::TOUCH_MOVE:
     case Touch::TOUCH_MOVE:
         if (_scrolling)
         if (_scrolling)
@@ -748,12 +754,12 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
         break;
         break;
 
 
     case Touch::TOUCH_RELEASE:
     case Touch::TOUCH_RELEASE:
+        _scrolling = false;
         long timeSinceLastMove = Game::getAbsoluteTime() - _lastTime;
         long timeSinceLastMove = Game::getAbsoluteTime() - _lastTime;
         if (timeSinceLastMove > STOP_TIME)
         if (timeSinceLastMove > STOP_TIME)
         {
         {
             _velocity.set(0, 0);
             _velocity.set(0, 0);
-            _scrolling = false;
-            break;
+            return true;
         }
         }
 
 
         int dx = _lastX - _firstX;
         int dx = _lastX - _firstX;
@@ -773,8 +779,7 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
 
 
         _velocity.set(vx, vy);
         _velocity.set(vx, vy);
 
 
-        _scrolling = false;
-        break;
+        return true;
     }
     }
 
 
     return false;
     return false;

+ 4 - 4
gameplay/src/Container.h

@@ -177,9 +177,9 @@ protected:
      * Updates each control within this container,
      * Updates each control within this container,
      * and positions them according to the container's layout.
      * and positions them according to the container's layout.
      *
      *
-     * @param clip The clipping rectangle of this container's parent container.
+     * @param container This container's parent container.
      */
      */
-    virtual void update(const Rectangle& clip, const Vector2& offset);
+    virtual void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Touch callback on touch events.  Controls return true if they consume the touch event.
      * Touch callback on touch events.  Controls return true if they consume the touch event.
@@ -229,12 +229,12 @@ protected:
      */
      */
     void addControls(Theme* theme, Properties* properties);
     void addControls(Theme* theme, Properties* properties);
 
 
-    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight);
 
 
     /**
     /**
      * Update scroll position and velocity.
      * Update scroll position and velocity.
      */
      */
-    void updateScroll(const Container* container);
+    void updateScroll();
 
 
     bool touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
     bool touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
 

+ 11 - 5
gameplay/src/Control.cpp

@@ -731,8 +731,11 @@ void Control::notifyListeners(Listener::EventType eventType)
     }
     }
 }
 }
 
 
-void Control::update(const Rectangle& clip, const Vector2& offset)
+void Control::update(const Control* container, const Vector2& offset)
 {
 {
+    const Rectangle& clip = container->getClip();
+    const Rectangle& absoluteViewport = container->_viewportBounds;
+
     _clearBounds.set(_absoluteClipBounds);
     _clearBounds.set(_absoluteClipBounds);
 
 
     // Calculate the clipped bounds.
     // Calculate the clipped bounds.
@@ -774,8 +777,8 @@ void Control::update(const Rectangle& clip, const Vector2& offset)
     _clipBounds.set(x, y, width, height);
     _clipBounds.set(x, y, width, height);
 
 
     // Calculate the absolute bounds.
     // Calculate the absolute bounds.
-    x = _bounds.x + offset.x + clip.x;
-    y = _bounds.y + offset.y + clip.y;
+    x = _bounds.x + offset.x + absoluteViewport.x;
+    y = _bounds.y + offset.y + absoluteViewport.y;
     _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
     _absoluteBounds.set(x, y, _bounds.width, _bounds.height);
 
 
     // Calculate the absolute viewport bounds.
     // Calculate the absolute viewport bounds.
@@ -899,7 +902,7 @@ void Control::drawText(const Rectangle& position)
 {
 {
 }
 }
 
 
-void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight)
+void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight)
 {
 {
     if (needsClear)
     if (needsClear)
     {
     {
@@ -961,8 +964,11 @@ Theme::ThemeImage* Control::getImage(const char* id, State state)
 {
 {
     Theme::Style::Overlay* overlay = getOverlay(state);
     Theme::Style::Overlay* overlay = getOverlay(state);
     GP_ASSERT(overlay);
     GP_ASSERT(overlay);
+    
     Theme::ImageList* imageList = overlay->getImageList();
     Theme::ImageList* imageList = overlay->getImageList();
-    GP_ASSERT(imageList);
+    if (!imageList)
+        return NULL;
+
     return imageList->getImage(id);
     return imageList->getImage(id);
 }
 }
 
 

+ 15 - 6
gameplay/src/Control.h

@@ -24,7 +24,6 @@ class Control : public Ref, public AnimationTarget
     friend class AbsoluteLayout;
     friend class AbsoluteLayout;
     friend class VerticalLayout;
     friend class VerticalLayout;
     friend class FlowLayout;
     friend class FlowLayout;
-    friend class ScrollLayout;
 
 
 public:
 public:
 
 
@@ -659,9 +658,19 @@ public:
      */
      */
     Theme::Style* getStyle() const;
     Theme::Style* getStyle() const;
 
 
+    /**
+     * Get this control's z-index.
+     *
+     * @return This control's z-index.
+     */
     int getZIndex() const;
     int getZIndex() const;
 
 
-    void setZIndex(int zOrder);
+    /**
+     * Set this control's z-index.
+     *
+     * @param zIndex The new z-index.
+     */
+    void setZIndex(int zIndex);
 
 
     /**
     /**
      * Add a listener to be notified of specific events affecting
      * Add a listener to be notified of specific events affecting
@@ -739,10 +748,10 @@ protected:
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this control's parent container.
-     * @param offset Layout-computed positioning offset to add to the control's position.
+     * @param container This control's parent container.
+     * @param offset Positioning offset to add to the control's position.
      */
      */
-    virtual void update(const Rectangle& clip, const Vector2& offset);
+    virtual void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.
@@ -761,7 +770,7 @@ protected:
      */
      */
     virtual void drawText(const Rectangle& clip);
     virtual void drawText(const Rectangle& clip);
 
 
-    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, bool cleared, float targetHeight);
 
 
     /**
     /**
      * Initialize properties common to STATE_ALL Controls.
      * Initialize properties common to STATE_ALL Controls.

+ 2 - 2
gameplay/src/FlowLayout.cpp

@@ -39,7 +39,7 @@ Layout::Type FlowLayout::getType()
     return Layout::LAYOUT_FLOW;
     return Layout::LAYOUT_FLOW;
 }
 }
 
 
-void FlowLayout::update(const Container* container)
+void FlowLayout::update(const Container* container, const Vector2& offset)
 {
 {
     GP_ASSERT(container);
     GP_ASSERT(container);
     const Rectangle& containerBounds = container->getBounds();
     const Rectangle& containerBounds = container->getBounds();
@@ -80,7 +80,7 @@ void FlowLayout::update(const Container* container)
         control->setPosition(xPosition, yPosition);
         control->setPosition(xPosition, yPosition);
         if (control->isDirty() || control->isContainer())
         if (control->isDirty() || control->isContainer())
         {
         {
-            control->update(container->getClip(), Vector2::zero());
+            control->update(container, offset);
         }
         }
 
 
         xPosition += bounds.width + margin.right;
         xPosition += bounds.width + margin.right;

+ 1 - 1
gameplay/src/FlowLayout.h

@@ -34,7 +34,7 @@ protected:
      *
      *
      * @param container The container to update.
      * @param container The container to update.
      */
      */
-    void update(const Container* container);
+    void update(const Container* container, const Vector2& offset);
 
 
 private:
 private:
 
 

+ 1 - 2
gameplay/src/Font.cpp

@@ -187,7 +187,6 @@ Font::Text* Font::createText(const char* text, const Rectangle& area, const Vect
     bool wrap, bool rightToLeft, const Rectangle* clip)
     bool wrap, bool rightToLeft, const Rectangle* clip)
 {
 {
     GP_ASSERT(text);
     GP_ASSERT(text);
-    GP_ASSERT(clip);
     GP_ASSERT(_glyphs);
     GP_ASSERT(_glyphs);
     GP_ASSERT(_batch);
     GP_ASSERT(_batch);
 
 
@@ -628,7 +627,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         }
         }
 
 
         bool draw = true;
         bool draw = true;
-        if (yPos < area.y)
+        if (yPos < area.y - size)
         {
         {
             // Skip drawing until line break or wrap.
             // Skip drawing until line break or wrap.
             draw = false;
             draw = false;

+ 4 - 4
gameplay/src/Form.cpp

@@ -417,10 +417,10 @@ void Form::update()
         }
         }
 
 
         GP_ASSERT(_layout);
         GP_ASSERT(_layout);
-        _layout->update(this);
-
         if (_scroll != SCROLL_NONE)
         if (_scroll != SCROLL_NONE)
-            this->updateScroll(this);
+            updateScroll();
+        else
+            _layout->update(this, Vector2::zero());
     }
     }
 }
 }
 
 
@@ -451,7 +451,7 @@ void Form::draw()
 
 
         GP_ASSERT(_theme);
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);
         _theme->setProjectionMatrix(_projectionMatrix);
-        Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height), _skin == NULL, _bounds.height);
+        Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height), _skin != NULL, false, _bounds.height);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
 
 
         // Rebind the default framebuffer and game viewport.
         // Rebind the default framebuffer and game viewport.

+ 9 - 1
gameplay/src/FrameBuffer.cpp

@@ -10,6 +10,7 @@ namespace gameplay
 
 
 static unsigned int __maxRenderTargets = 0;
 static unsigned int __maxRenderTargets = 0;
 static std::vector<FrameBuffer*> __frameBuffers;
 static std::vector<FrameBuffer*> __frameBuffers;
+static FrameBufferHandle __defaultHandle = 0;
 
 
 FrameBuffer::FrameBuffer(const char* id) :
 FrameBuffer::FrameBuffer(const char* id) :
     _id(id ? id : ""), _handle(0), _renderTargets(NULL), _depthStencilTarget(NULL)
     _id(id ? id : ""), _handle(0), _renderTargets(NULL), _depthStencilTarget(NULL)
@@ -41,6 +42,13 @@ FrameBuffer::~FrameBuffer()
     }
     }
 }
 }
 
 
+void FrameBuffer::initialize()
+{
+    GLint fbo;
+    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
+    __defaultHandle = (FrameBufferHandle)fbo;
+}
+
 FrameBuffer* FrameBuffer::create(const char* id)
 FrameBuffer* FrameBuffer::create(const char* id)
 {
 {
     // Create GL FBO resource.
     // Create GL FBO resource.
@@ -230,7 +238,7 @@ void FrameBuffer::bind()
 
 
 void FrameBuffer::bindDefault()
 void FrameBuffer::bindDefault()
 {
 {
-    GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, 0) );
+    GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, __defaultHandle) );
 }
 }
 
 
 }
 }

+ 4 - 0
gameplay/src/FrameBuffer.h

@@ -19,6 +19,8 @@ namespace gameplay
  */
  */
 class FrameBuffer : public Ref
 class FrameBuffer : public Ref
 {
 {
+    friend class Game;
+
 public:
 public:
 
 
     /**
     /**
@@ -118,6 +120,8 @@ private:
      */
      */
     ~FrameBuffer();
     ~FrameBuffer();
 
 
+    static void initialize();
+
     std::string _id;
     std::string _id;
     FrameBufferHandle _handle;
     FrameBufferHandle _handle;
     RenderTarget** _renderTargets;
     RenderTarget** _renderTargets;

+ 2 - 1
gameplay/src/Game.cpp

@@ -3,6 +3,7 @@
 #include "Platform.h"
 #include "Platform.h"
 #include "RenderState.h"
 #include "RenderState.h"
 #include "FileSystem.h"
 #include "FileSystem.h"
+#include "FrameBuffer.h"
 
 
 // Extern global variables
 // Extern global variables
 GLenum __gl_error_code = GL_NO_ERROR;
 GLenum __gl_error_code = GL_NO_ERROR;
@@ -92,8 +93,8 @@ bool Game::startup()
         return false;
         return false;
 
 
     setViewport(Rectangle(0.0f, 0.0f, (float)_width, (float)_height));
     setViewport(Rectangle(0.0f, 0.0f, (float)_width, (float)_height));
-
     RenderState::initialize();
     RenderState::initialize();
+    FrameBuffer::initialize();
 
 
     _animationController = new AnimationController();
     _animationController = new AnimationController();
     _animationController->initialize();
     _animationController->initialize();

+ 219 - 0
gameplay/src/Joystick.cpp

@@ -0,0 +1,219 @@
+#include "Base.h"
+#include "Joystick.h"
+
+#define INVALID_CONTACT_INDEX ((unsigned int)-1)
+
+namespace gameplay
+{
+
+Joystick::Joystick() : _contactIndex(INVALID_CONTACT_INDEX), _absolute(true)
+{
+}
+
+Joystick::Joystick(const Joystick& copy)
+{
+}
+
+Joystick::~Joystick()
+{
+}
+
+Joystick* Joystick::create(Theme::Style* style, Properties* properties)
+{
+    Joystick* joystick = new Joystick();
+    joystick->initialize(style, properties);
+    joystick->_consumeTouchEvents = false;
+
+    return joystick;
+}
+
+void Joystick::initialize(Theme::Style* style, Properties* properties)
+{
+    GP_ASSERT(properties);
+
+    Control::initialize(style, properties);
+
+    if (!properties->exists("radius"))
+    {
+        GP_ERROR("Failed to load joystick; required attribute 'radius' is missing.");
+        return;
+    }
+    _radius = properties->getFloat("radius");
+
+    Vector4 v;
+    if (properties->getVector4("region", &v))
+    {
+        _absolute = false;
+        _region = _bounds;
+        _bounds.x = v.x;
+        _bounds.y = v.y;
+        _bounds.width = v.z;
+        _bounds.height = v.w;
+    }
+}
+
+void Joystick::addListener(Control::Listener* listener, int eventFlags)
+{
+    if ((eventFlags & Listener::TEXT_CHANGED) == Listener::TEXT_CHANGED)
+    {
+        GP_ERROR("TEXT_CHANGED event is not applicable to this control.");
+    }
+
+    _consumeTouchEvents = true;
+
+    Control::addListener(listener, eventFlags);
+}
+
+bool Joystick::touchEvent(Touch::TouchEvent touchEvent, int x, int y, unsigned int contactIndex)
+{
+    switch (touchEvent)
+    {
+        case Touch::TOUCH_PRESS:
+        {
+            float dx = 0.0f;
+            float dy = 0.0f;
+
+            if (_absolute)
+            {
+                dx = x - _bounds.width * 0.5f;
+                dy = _bounds.height * 0.5f - y;
+            }
+            else
+            {
+                _region.x = x + _bounds.x - _region.width * 0.5f;
+                _region.y = y + _bounds.y - _region.height * 0.5f;
+            }
+
+            if ((dx >= -_radius && dx <= _radius) && (dy >= -_radius && dy <= _radius) && 
+                _contactIndex == INVALID_CONTACT_INDEX)
+            {
+                _contactIndex = contactIndex;
+                _displacement.set(0.0f, 0.0f);
+                
+                Vector2 value(0.0f, 0.0f);
+                if (_value != value)
+                {
+                    _value.set(value);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
+                }
+
+                _state = ACTIVE;
+            }
+        }
+        case Touch::TOUCH_MOVE:
+        {
+            if (_contactIndex == contactIndex)
+            {
+                float dx = x - ((!_absolute) ? _region.x - _bounds.x : 0.0f) - _region.width * 0.5f;
+                float dy = -(y - ((!_absolute) ? _region.y - _bounds.y : 0.0f) - _region.height * 0.5f);
+                if (((dx * dx) + (dy * dy)) <= (_radius * _radius))
+                {
+                    GP_ASSERT(_radius);
+                    Vector2 value(dx, dy);
+                    value.scale(1.0f / _radius);
+                    if (_value != value)
+                    {
+                        _value.set(value);
+                        notifyListeners(Control::Listener::VALUE_CHANGED);
+                    }
+                }
+                else
+                {
+                    Vector2 value(dx, dy);
+                    value.normalize();
+                    value.scale(_radius);
+                    value.normalize();
+                    if (_value != value)
+                    {
+                        _value.set(value);
+                        notifyListeners(Control::Listener::VALUE_CHANGED);
+                    }
+                }
+
+                _displacement.set(dx, dy);
+            }
+        }
+        break;
+        case Touch::TOUCH_RELEASE:
+        {
+            if (_contactIndex == contactIndex)
+            {
+                // Reset displacement and direction vectors.
+                _contactIndex = INVALID_CONTACT_INDEX;
+                _displacement.set(0.0f, 0.0f);
+                
+                Vector2 value(0.0f, 0.0f);
+                if (_value != value)
+                {
+                    _value.set(value);
+                    notifyListeners(Control::Listener::VALUE_CHANGED);
+                }
+
+                _state = NORMAL;
+            }
+        }
+        break;
+    }
+
+    return Control::touchEvent(touchEvent, x, y, contactIndex);
+}
+
+void Joystick::update(const Rectangle& clip, const Vector2& offset)
+{
+    Control::update(clip, offset);
+}
+
+void Joystick::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
+{
+    GP_ASSERT(spriteBatch);
+    spriteBatch->begin();
+
+    // If the joystick is not absolute, then only draw if it is active.
+    if (_absolute || (!_absolute && _state == ACTIVE))
+    {
+        if (_absolute)
+            _region = _bounds;
+
+        // Draw the outer image.
+        Theme::ThemeImage* outer = getImage("outer", _state);
+        if (outer)
+        {
+            // Get the uvs and color and draw.
+            const Theme::UVs& uvs = outer->getUVs();
+            const Vector4& color = outer->getColor();
+            spriteBatch->draw(_region.x, _region.y, _region.width, _region.height, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
+        }
+
+        // Draw the inner image.
+        Theme::ThemeImage* inner = getImage("inner", _state);
+        if (inner)
+        {
+            Rectangle region = _region;
+
+            // Adjust position to reflect displacement.
+            if (((_displacement.x * _displacement.x) + (_displacement.y * _displacement.y)) <= (_radius * _radius))
+            {
+                region.x += _displacement.x;
+                region.y += -_displacement.y;
+            }
+            else
+            {
+                // Find the point on the joystick's circular bound where the
+                // vector intersects. This is the position of the inner image.
+                Vector2 delta = Vector2(_displacement.x, -_displacement.y);
+                delta.normalize();
+                delta.scale(_radius);
+                region.x += delta.x;
+                region.y += delta.y;
+            }
+        
+            // Get the uvs and color and draw.
+            const Theme::UVs& uvs = inner->getUVs();
+            const Vector4& color = inner->getColor();
+            spriteBatch->draw(region.x, region.y, _region.width, _region.height, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
+        }
+    }
+    spriteBatch->end();
+}
+
+}

+ 146 - 0
gameplay/src/Joystick.h

@@ -0,0 +1,146 @@
+#ifndef JOYSTICK_H_
+#define JOYSTICK_H_
+
+#include "Control.h"
+
+namespace gameplay
+{
+
+class Joystick : public Control
+{
+    friend class Container;
+
+public:
+    
+    /**
+     * Add a listener to be notified of specific events affecting
+     * this control.  Event types can be OR'ed together.
+     * E.g. To listen to touch-press and touch-release events,
+     * pass <code>Control::Listener::TOUCH | Control::Listener::RELEASE</code>
+     * as the second parameter.
+     *
+     * @param listener The listener to add.
+     * @param eventFlags The events to listen for.
+     */
+    void addListener(Control::Listener* listener, int eventFlags);
+
+    /**
+     * Retrieves the value (2-dimensional direction) of the joystick.
+     * 
+     * @return The value of the joystick.
+     */
+    inline const Vector2& getValue() const;
+
+    /**
+     * Sets the region within which the joystick will be spontaneously created on a user's touch.
+     * 
+     * Note: This does not actually enable spontaneous joystick creation on touch input.
+     * To enable (or disable) absolute position explicitly, use #setAbsolute.
+     * 
+     * @param region The region to use.
+     */
+    inline void setRegion(const Rectangle& region);
+
+    /**
+     * Gets the region within which the joystick will be spontaneously created on a user's touch.
+     * 
+     * Note: just because the returned region is not empty does not mean that it is necessarily
+     * being used. If absolute positioning is not enabled, then it will be used (to check if
+     * absolute positioning is enabled, call #isAbsolute).
+     * 
+     * @return The region within which the joystick will be spontaneously created on a user's touch.
+     */
+    inline const Rectangle& getRegion() const;
+
+    /**
+     * Sets whether absolute positioning is enabled or not.
+     * 
+     * @param absolute Whether absolute positioning should be enabled or not.
+     */
+    inline void setAbsolute(bool absolute);
+
+    /**
+     * Retrieves whether absolute positioning is enabled or not.
+     * 
+     * @return <code>true</code> if absolute positioning is enabled; <code>false</code> otherwise.
+     */
+    inline bool isAbsolute() const;
+
+protected:
+    
+    /**
+     * Constructor.
+     */
+    Joystick();
+
+    /**
+     * Destructor.
+     */
+    virtual ~Joystick();
+
+    /**
+     * Create a joystick with a given style and properties.
+     *
+     * @param style The style to apply to this joystick.
+     * @param properties The properties to set on this joystick.
+     *
+     * @return The new joystick.
+     */
+    static Joystick* create(Theme::Style* style, Properties* properties);
+
+    /**
+     * Initialize this joystick.
+     */
+    virtual void initialize(Theme::Style* style, Properties* properties);
+
+    /**
+     * Touch callback on touch events.  Controls return true if they consume the touch event.
+     *
+     * @param evt The touch event that occurred.
+     * @param x The x position of the touch in pixels. Left edge is zero.
+     * @param y The y position of the touch in pixels. Top edge is zero.
+     * @param contactIndex The order of occurrence for multiple touch contacts starting at zero.
+     *
+     * @return Whether the touch event was consumed by the control.
+     *
+     * @see Touch::TouchEvent
+     */
+    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+    /**
+     * Called when a joystick's properties change. Updates this joystick's internal rendering properties.
+     *
+     * @param clip The clipping rectangle of this joystick's parent container.
+     * @param offset The scroll offset of this joystick's parent container.
+     */
+    void update(const Rectangle& clip, const Vector2& offset);
+
+    /**
+     * Draw the images associated with this control.
+     *
+     * @param spriteBatch The sprite batch containing this control's icons.
+     * @param clip The clipping rectangle of this control's parent container.
+     * @param offset Layout-computed positioning offset to add to the control's position.
+     */
+    void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
+
+private:
+
+    /**
+     * Copy constructor.
+     */
+    Joystick(const Joystick& copy);
+
+    float _radius;
+    unsigned int _contactIndex;
+    bool _absolute;
+    Vector2 _displacement;
+    Vector2 _value;
+    Rectangle _region;
+};
+
+}
+
+#include "Joystick.inl"
+
+#endif

+ 34 - 0
gameplay/src/Joystick.inl

@@ -0,0 +1,34 @@
+#include "Joystick.h"
+
+namespace gameplay
+{
+
+inline const Vector2& Joystick::getValue() const
+{
+    return _value;
+}
+
+inline void Joystick::setRegion(const Rectangle& region)
+{
+    if (_region.isEmpty())
+        _region = _bounds;
+
+    _bounds = region;
+}
+
+inline const Rectangle& Joystick::getRegion() const
+{
+    return _bounds;
+}
+
+inline void Joystick::setAbsolute(bool absolute)
+{
+    _absolute = absolute;
+}
+
+inline bool Joystick::isAbsolute() const
+{
+    return _absolute;
+}
+
+}

+ 2 - 2
gameplay/src/Label.cpp

@@ -67,9 +67,9 @@ const char* Label::getText()
     return _text.c_str();
     return _text.c_str();
 }
 }
 
 
-void Label::update(const Rectangle& clip, const Vector2& offset)
+void Label::update(const Control* container, const Vector2& offset)
 {
 {
-    Control::update(clip, offset);
+    Control::update(container, offset);
 
 
     _textBounds.set(_viewportBounds);
     _textBounds.set(_viewportBounds);
 
 

+ 2 - 2
gameplay/src/Label.h

@@ -90,10 +90,10 @@ protected:
      * Called when a label's properties change. Updates this label's internal rendering
      * Called when a label's properties change. Updates this label's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this label's parent container.
+     * @param container This label's parent container.
      * @param offset The scroll offset of this label's parent container.
      * @param offset The scroll offset of this label's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw this label's text.
      * Draw this label's text.

+ 2 - 1
gameplay/src/Layout.h

@@ -3,6 +3,7 @@
 
 
 #include "Ref.h"
 #include "Ref.h"
 #include "Touch.h"
 #include "Touch.h"
+#include "Vector2.h"
 
 
 namespace gameplay
 namespace gameplay
 {
 {
@@ -68,7 +69,7 @@ protected:
      *
      *
      * @param container The container to update.
      * @param container The container to update.
      */
      */
-    virtual void update(const Container* container) = 0;
+    virtual void update(const Container* container, const Vector2& offset) = 0;
 
 
     /**
     /**
      * Align a control within a container.
      * Align a control within a container.

+ 2 - 0
gameplay/src/Matrix.cpp

@@ -3,7 +3,9 @@
 #include "Quaternion.h"
 #include "Quaternion.h"
 #include "MathUtil.h"
 #include "MathUtil.h"
 
 
+#ifndef MATRIX_SIZE
 #define MATRIX_SIZE     ( sizeof(float) * 16 )
 #define MATRIX_SIZE     ( sizeof(float) * 16 )
+#endif
 
 
 namespace gameplay
 namespace gameplay
 {
 {

+ 87 - 59
gameplay/src/Properties.cpp

@@ -6,6 +6,10 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
+// Utility functions (shared with SceneLoader).
+void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
+Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
+
 Properties::Properties()
 Properties::Properties()
 {
 {
 }
 }
@@ -55,28 +59,11 @@ Properties* Properties::create(const char* url)
         return NULL;
         return NULL;
     }
     }
 
 
+    // Calculate the file and full namespace path from the specified url.
     std::string urlString = url;
     std::string urlString = url;
     std::string fileString;
     std::string fileString;
     std::vector<std::string> namespacePath;
     std::vector<std::string> namespacePath;
-
-    // If the url references a specific namespace within the file,
-    // calculate the full namespace path to the final namespace.
-    unsigned int loc = urlString.rfind("#");
-    if (loc != urlString.npos)
-    {
-        fileString = urlString.substr(0, loc);
-        std::string namespacePathString = urlString.substr(loc + 1);
-        while ((loc = namespacePathString.find("/")) != namespacePathString.npos)
-        {
-            namespacePath.push_back(namespacePathString.substr(0, loc));
-            namespacePathString = namespacePathString.substr(loc + 1);
-        }
-        namespacePath.push_back(namespacePathString);
-    }
-    else
-    {
-        fileString = url;
-    }
+    calculateNamespacePath(urlString, fileString, namespacePath);
 
 
     FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
     FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
     if (!file)
     if (!file)
@@ -86,52 +73,26 @@ Properties* Properties::create(const char* url)
     }
     }
 
 
     Properties* properties = new Properties(file);
     Properties* properties = new Properties(file);
-
     properties->resolveInheritance();
     properties->resolveInheritance();
-
     fclose(file);
     fclose(file);
 
 
-    // If the url references a specific namespace within the file,
-    // return the specified namespace or notify the user if it cannot be found.
-    Properties* originalProperties = properties;
-    if (namespacePath.size() > 0)
+    // Get the specified properties object.
+    Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
+    if (!p)
     {
     {
-        unsigned int size = namespacePath.size();
-        Properties* iter = properties->getNextNamespace();
-        for (unsigned int i = 0; i < size;)
-        {
-            while (true)
-            {
-                if (iter == NULL)
-                {
-                    GP_ERROR("Failed to load Properties object from URL '%s'.", url);
-                    return NULL;
-                }
-
-                if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
-                {
-                    if (i != size - 1)
-                    {
-                        properties = iter->getNextNamespace();
-                        iter = properties;
-                    }
-                    else
-                        properties = iter;
-
-                    i++;
-                    break;
-                }
-                
-                iter = properties->getNextNamespace();
-            }
-        }
+        GP_ERROR("Failed to load properties from url '%s'.", url);
+        return NULL;
+    }
 
 
-        properties = properties->clone();
-        SAFE_DELETE(originalProperties);
-        return properties;
+    // If the loaded properties object is not the root namespace,
+    // then we have to clone it and delete the root namespace
+    // so that we don't leak memory.
+    if (p != properties)
+    {
+        p = p->clone();
+        SAFE_DELETE(properties);
     }
     }
-    else
-        return properties;
+    return p;
 }
 }
 
 
 void Properties::readProperties(FILE* file)
 void Properties::readProperties(FILE* file)
@@ -1009,4 +970,71 @@ Properties* Properties::clone()
     return p;
     return p;
 }
 }
 
 
+void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath)
+{
+    // If the url references a specific namespace within the file,
+    // calculate the full namespace path to the final namespace.
+    unsigned int loc = urlString.rfind("#");
+    if (loc != urlString.npos)
+    {
+        fileString = urlString.substr(0, loc);
+        std::string namespacePathString = urlString.substr(loc + 1);
+        while ((loc = namespacePathString.find("/")) != namespacePathString.npos)
+        {
+            namespacePath.push_back(namespacePathString.substr(0, loc));
+            namespacePathString = namespacePathString.substr(loc + 1);
+        }
+        namespacePath.push_back(namespacePathString);
+    }
+    else
+    {
+        fileString = urlString;
+    }
+}
+
+Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath)
+{
+    // If the url references a specific namespace within the file,
+    // return the specified namespace or notify the user if it cannot be found.
+    Properties* originalProperties = properties;
+    if (namespacePath.size() > 0)
+    {
+        unsigned int size = namespacePath.size();
+        const char* tmp = namespacePath[0].c_str();
+        properties->rewind();
+        Properties* iter = properties->getNextNamespace();
+        for (unsigned int i = 0; i < size;)
+        {
+            while (true)
+            {
+                if (iter == NULL)
+                {
+                    GP_ERROR("Failed to load properties object from url.");
+                    return NULL;
+                }
+
+                if (strcmp(iter->getId(), namespacePath[i].c_str()) == 0)
+                {
+                    if (i != size - 1)
+                    {
+                        properties = iter->getNextNamespace();
+                        iter = properties;
+                    }
+                    else
+                        properties = iter;
+
+                    i++;
+                    break;
+                }
+                
+                iter = properties->getNextNamespace();
+            }
+        }
+
+        return properties;
+    }
+    else
+        return properties;
+}
+
 }
 }

+ 2 - 2
gameplay/src/RadioButton.cpp

@@ -122,9 +122,9 @@ void RadioButton::clearSelected(const std::string& groupId)
     }
     }
 }
 }
 
 
-void RadioButton::update(const Rectangle& clip, const Vector2& offset)
+void RadioButton::update(const Control* container, const Vector2& offset)
 {
 {
-    Label::update(clip, offset);
+    Label::update(container, offset);
 
 
     Vector2 size;
     Vector2 size;
     if (_imageSize.isZero())
     if (_imageSize.isZero())

+ 2 - 2
gameplay/src/RadioButton.h

@@ -112,9 +112,9 @@ protected:
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this control's parent container.
+     * @param container This control's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.

+ 38 - 7
gameplay/src/SceneLoader.cpp

@@ -6,6 +6,11 @@
 namespace gameplay
 namespace gameplay
 {
 {
 
 
+// Utility functions (shared with Properties).
+extern void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
+extern Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
+    
+std::map<std::string, Properties*> SceneLoader::_propertiesFromFile;
 std::map<std::string, Properties*> SceneLoader::_properties;
 std::map<std::string, Properties*> SceneLoader::_properties;
 std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
 std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
 std::vector<SceneLoader::SceneNode> SceneLoader::_sceneNodes;
 std::vector<SceneLoader::SceneNode> SceneLoader::_sceneNodes;
@@ -89,11 +94,11 @@ Scene* SceneLoader::load(const char* url)
         loadPhysics(physics, scene);
         loadPhysics(physics, scene);
 
 
     // Clean up all loaded properties objects.
     // Clean up all loaded properties objects.
-    std::map<std::string, Properties*>::iterator iter = _properties.begin();
-    for (; iter != _properties.end(); iter++)
+    _properties.clear();
+    std::map<std::string, Properties*>::iterator iter = _propertiesFromFile.begin();
+    for (; iter != _propertiesFromFile.end(); iter++)
     {
     {
-        if (iter->first.find(_path) == iter->first.npos)
-            SAFE_DELETE(iter->second);
+        SAFE_DELETE(iter->second);
     }
     }
 
 
     // Clean up the .scene file's properties object.
     // Clean up the .scene file's properties object.
@@ -827,10 +832,36 @@ void SceneLoader::loadReferencedFiles()
     {
     {
         if (iter->second == NULL)
         if (iter->second == NULL)
         {
         {
-            Properties* p = Properties::create(iter->first.c_str());
-            if (p == NULL)
-                GP_ERROR("Failed to load referenced file '%s'.", iter->first.c_str());
+            std::string fileString;
+            std::vector<std::string> namespacePath;
+            calculateNamespacePath(iter->first, fileString, namespacePath);
+
+            // Check if the referenced properties file has already been loaded.
+            Properties* properties = NULL;
+            std::map<std::string, Properties*>::iterator pffIter = _propertiesFromFile.find(fileString);
+            if (pffIter != _propertiesFromFile.end())
+            {
+                properties = pffIter->second;
+            }
+            else
+            {
+                properties = Properties::create(fileString.c_str());
+                if (properties == NULL)
+                {
+                    GP_ERROR("Failed to load referenced properties file '%s'.", fileString);
+                    continue;
+                }
+
+                // Add the properties object to the cache.
+                _propertiesFromFile.insert(std::make_pair(fileString, properties));
+            }
 
 
+            Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
+            if (!p)
+            {
+                GP_ERROR("Failed to load referenced properties from url '%s'.", iter->first.c_str());
+                continue;
+            }
             iter->second = p;
             iter->second = p;
         }
         }
     }
     }

+ 1 - 0
gameplay/src/SceneLoader.h

@@ -105,6 +105,7 @@ private:
     static void splitURL(const std::string& url, std::string* file, std::string* id);
     static void splitURL(const std::string& url, std::string* file, std::string* id);
     
     
     
     
+    static std::map<std::string, Properties*> _propertiesFromFile;      // Holds the properties object for a given file.
     static std::map<std::string, Properties*> _properties;              // Holds the properties object for a given URL.
     static std::map<std::string, Properties*> _properties;              // Holds the properties object for a given URL.
     static std::vector<SceneAnimation> _animations;                     // Holds the animations declared in the .scene file.
     static std::vector<SceneAnimation> _animations;                     // Holds the animations declared in the .scene file.
     static std::vector<SceneNode> _sceneNodes;                          // Holds all the nodes+properties declared in the .scene file.
     static std::vector<SceneNode> _sceneNodes;                          // Holds all the nodes+properties declared in the .scene file.

+ 2 - 2
gameplay/src/Slider.cpp

@@ -141,9 +141,9 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     return Control::touchEvent(evt, x, y, contactIndex);
     return Control::touchEvent(evt, x, y, contactIndex);
 }
 }
 
 
-void Slider::update(const Rectangle& clip, const Vector2& offset)
+void Slider::update(const Control* container, const Vector2& offset)
 {
 {
-    Label::update(clip, offset);
+    Label::update(container, offset);
 
 
     _minImage = getImage("minCap", _state);
     _minImage = getImage("minCap", _state);
     _maxImage = getImage("maxCap", _state);
     _maxImage = getImage("maxCap", _state);

+ 2 - 2
gameplay/src/Slider.h

@@ -153,10 +153,10 @@ protected:
      * Called when a slider's properties change. Updates this slider's internal rendering
      * Called when a slider's properties change. Updates this slider's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this slider's parent container.
+     * @param container This slider's parent container.
      * @param offset The scroll offset of this slider's parent container.
      * @param offset The scroll offset of this slider's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * The minimum value for the Slider.
      * The minimum value for the Slider.

+ 2 - 2
gameplay/src/TextBox.cpp

@@ -291,9 +291,9 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
     _lastKeypress = key;
     _lastKeypress = key;
 }
 }
 
 
-void TextBox::update(const Rectangle& clip, const Vector2& offset)
+void TextBox::update(const Control* container, const Vector2& offset)
 {
 {
-    Label::update(clip, offset);
+    Label::update(container, offset);
 
 
     _fontSize = getFontSize(_state);
     _fontSize = getFontSize(_state);
     _caretImage = getImage("textCaret", _state);
     _caretImage = getImage("textCaret", _state);

+ 2 - 2
gameplay/src/TextBox.h

@@ -110,9 +110,9 @@ protected:
      * Called when a control's properties change.  Updates this control's internal rendering
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      * properties, such as its text viewport.
      *
      *
-     * @param clip The clipping rectangle of this control's parent container.
+     * @param container This control's parent container.
      */
      */
-    void update(const Rectangle& clip, const Vector2& offset);
+    void update(const Control* container, const Vector2& offset);
 
 
     /**
     /**
      * Draw the images associated with this control.
      * Draw the images associated with this control.

+ 11 - 0
gameplay/src/Texture.cpp

@@ -637,6 +637,7 @@ Texture* Texture::createCompressedDDS(const char* path)
             {
             {
                 GP_ERROR("Failed to close file '%s'.", path);
                 GP_ERROR("Failed to close file '%s'.", path);
             }
             }
+            SAFE_DELETE_ARRAY(mipLevels);
             return NULL;
             return NULL;
         }
         }
 
 
@@ -676,6 +677,7 @@ Texture* Texture::createCompressedDDS(const char* path)
         {
         {
             GP_ERROR("Failed to close file '%s'.", path);
             GP_ERROR("Failed to close file '%s'.", path);
         }
         }
+        SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
         return NULL;
     }
     }
     else if (header.ddspf.dwFlags == 0x41/*DDPF_RGB|DDPF_ALPHAPIXELS*/)
     else if (header.ddspf.dwFlags == 0x41/*DDPF_RGB|DDPF_ALPHAPIXELS*/)
@@ -687,6 +689,7 @@ Texture* Texture::createCompressedDDS(const char* path)
         {
         {
             GP_ERROR("Failed to close file '%s'.", path);
             GP_ERROR("Failed to close file '%s'.", path);
         }
         }
+        SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
         return NULL;
     }
     }
     else
     else
@@ -697,6 +700,7 @@ Texture* Texture::createCompressedDDS(const char* path)
         {
         {
             GP_ERROR("Failed to close file '%s'.", path);
             GP_ERROR("Failed to close file '%s'.", path);
         }
         }
+        SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
         return NULL;
     }
     }
     
     
@@ -731,8 +735,15 @@ Texture* Texture::createCompressedDDS(const char* path)
         {
         {
             GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, i, internalFormat, mipLevels[i].width, mipLevels[i].height, 0, format, GL_UNSIGNED_INT, mipLevels[i].data) );
             GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, i, internalFormat, mipLevels[i].width, mipLevels[i].height, 0, format, GL_UNSIGNED_INT, mipLevels[i].data) );
         }
         }
+        
+        // Clean up the texture data.
+        SAFE_DELETE_ARRAY(mipLevels[i].data);
     }
     }
     
     
+
+    // Clean up mip levels structure.
+    SAFE_DELETE_ARRAY(mipLevels);
+
     return texture;
     return texture;
 }
 }
 
 

+ 3 - 3
gameplay/src/VerticalLayout.cpp

@@ -43,7 +43,7 @@ Layout::Type VerticalLayout::getType()
     return Layout::LAYOUT_VERTICAL;
     return Layout::LAYOUT_VERTICAL;
 }
 }
 
 
-void VerticalLayout::update(const Container* container)
+void VerticalLayout::update(const Container* container, const Vector2& offset)
 {
 {
     GP_ASSERT(container);
     GP_ASSERT(container);
 
 
@@ -73,7 +73,7 @@ void VerticalLayout::update(const Container* container)
     {
     {
         Control* control = controls.at(i);
         Control* control = controls.at(i);
         GP_ASSERT(control);
         GP_ASSERT(control);
-            
+
         align(control, container);
         align(control, container);
 
 
         const Rectangle& bounds = control->getBounds();
         const Rectangle& bounds = control->getBounds();
@@ -82,7 +82,7 @@ void VerticalLayout::update(const Container* container)
         yPosition += margin.top;
         yPosition += margin.top;
 
 
         control->setPosition(margin.left, yPosition);
         control->setPosition(margin.left, yPosition);
-        control->update(container->getClip(), Vector2::zero());
+        control->update(container, offset);
 
 
         yPosition += bounds.height + margin.bottom;
         yPosition += bounds.height + margin.bottom;
 
 

+ 1 - 1
gameplay/src/VerticalLayout.h

@@ -67,7 +67,7 @@ protected:
      *
      *
      * @param container The container to update.
      * @param container The container to update.
      */
      */
-    void update(const Container* container);
+    void update(const Container* container, const Vector2& offset);
 
 
     /**
     /**
      * Flag determining whether this layout will start laying out controls from the bottom of the container.
      * Flag determining whether this layout will start laying out controls from the bottom of the container.

+ 1 - 0
gameplay/src/gameplay.h

@@ -87,4 +87,5 @@
 #include "Layout.h"
 #include "Layout.h"
 #include "AbsoluteLayout.h"
 #include "AbsoluteLayout.h"
 #include "VerticalLayout.h"
 #include "VerticalLayout.h"
+#include "Joystick.h"