Explorar el Código

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

Conflicts:
	gameplay/gameplay.vcxproj.filters

Adds gameplay to linker in project properties for sample00-sample03.
Removes commented code from Matrix.
Has Matrix::transpose() call MathUtil::transpose.
Makes MathUtil's members private. It now friends the classes that utilize it. This is to prevent users from calling these functions, which do not handle error checking on parameters to ensure optimal performance.
Kieran Cunney hace 13 años
padre
commit
0c6fbcc9f9

+ 1 - 1
gameplay/android/jni/Android.mk

@@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir)/../../src
 
 include $(CLEAR_VARS)
 LOCAL_MODULE    := libgameplay
-LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Bundle.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp FlowLayout.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp Image.cpp Joint.cpp Label.cpp Layout.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp ScrollLayout.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp ThemeStyle.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
+LOCAL_SRC_FILES := AbsoluteLayout.cpp Animation.cpp AnimationClip.cpp AnimationController.cpp AnimationTarget.cpp AnimationValue.cpp AudioBuffer.cpp AudioController.cpp AudioListener.cpp AudioSource.cpp BoundingBox.cpp BoundingSphere.cpp Bundle.cpp Button.cpp Camera.cpp CheckBox.cpp Container.cpp Control.cpp Curve.cpp DebugNew.cpp DepthStencilTarget.cpp Effect.cpp FileSystem.cpp FlowLayout.cpp Font.cpp Form.cpp FrameBuffer.cpp Frustum.cpp Game.cpp gameplay-main-android.cpp Image.cpp Joint.cpp Label.cpp Layout.cpp Light.cpp Material.cpp MaterialParameter.cpp Matrix.cpp Mesh.cpp MeshBatch.cpp MeshPart.cpp MeshSkin.cpp Model.cpp Node.cpp ParticleEmitter.cpp Pass.cpp PhysicsCharacter.cpp PhysicsCollisionObject.cpp PhysicsCollisionShape.cpp PhysicsConstraint.cpp PhysicsController.cpp PhysicsFixedConstraint.cpp PhysicsGenericConstraint.cpp PhysicsGhostObject.cpp PhysicsHingeConstraint.cpp PhysicsMotionState.cpp PhysicsRigidBody.cpp PhysicsSocketConstraint.cpp PhysicsSpringConstraint.cpp Plane.cpp PlatformAndroid.cpp Properties.cpp Quaternion.cpp RadioButton.cpp Ray.cpp Rectangle.cpp Ref.cpp RenderState.cpp RenderTarget.cpp Scene.cpp SceneLoader.cpp Slider.cpp SpriteBatch.cpp Technique.cpp TextBox.cpp Texture.cpp Theme.cpp ThemeStyle.cpp Transform.cpp Vector2.cpp Vector3.cpp Vector4.cpp VertexAttributeBinding.cpp VertexFormat.cpp VerticalLayout.cpp
 LOCAL_CFLAGS := -D__ANDROID__ -I"../../external-deps/bullet/include" -I"../../external-deps/libpng/include" -I"../../external-deps/oggvorbis/include" -I"../../external-deps/openal/include"
 LOCAL_STATIC_LIBRARIES := android_native_app_glue
 

+ 0 - 2
gameplay/gameplay.vcxproj

@@ -90,7 +90,6 @@
     <ClCompile Include="src\RenderTarget.cpp" />
     <ClCompile Include="src\Scene.cpp" />
     <ClCompile Include="src\SceneLoader.cpp" />
-    <ClCompile Include="src\ScrollLayout.cpp" />
     <ClCompile Include="src\Slider.cpp" />
     <ClCompile Include="src\SpriteBatch.cpp" />
     <ClCompile Include="src\Technique.cpp" />
@@ -183,7 +182,6 @@
     <ClInclude Include="src\Scene.h" />
     <ClInclude Include="src\SceneLoader.h" />
     <ClInclude Include="src\ScreenDisplayer.h" />
-    <ClInclude Include="src\ScrollLayout.h" />
     <ClInclude Include="src\Slider.h" />
     <ClInclude Include="src\SpriteBatch.h" />
     <ClInclude Include="src\Technique.h" />

+ 2 - 5
gameplay/gameplay.vcxproj.filters

@@ -279,9 +279,6 @@
     <ClCompile Include="src\FlowLayout.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\ScrollLayout.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -556,13 +553,13 @@
     </ClInclude>
     <ClInclude Include="src\FlowLayout.h">
       <Filter>src</Filter>
-    </ClInclude>
+    </ClInclude>
     <ClInclude Include="src\ScrollLayout.h">
       <Filter>src</Filter>
     </ClInclude>
     <ClInclude Include="src\MathUtil.h">
       <Filter>src</Filter>
-    </ClInclude>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">

+ 0 - 12
gameplay/gameplay.xcodeproj/project.pbxproj

@@ -28,10 +28,6 @@
 		42554EA2152BC35C000ED910 /* PhysicsCollisionShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42554E9F152BC35C000ED910 /* PhysicsCollisionShape.cpp */; };
 		42554EA3152BC35C000ED910 /* PhysicsCollisionShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */; };
 		42554EA4152BC35C000ED910 /* PhysicsCollisionShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */; };
-		4265D9241559A3C300581EB0 /* ScrollLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4265D9221559A3C300581EB0 /* ScrollLayout.cpp */; };
-		4265D9251559A3C300581EB0 /* ScrollLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4265D9221559A3C300581EB0 /* ScrollLayout.cpp */; };
-		4265D9261559A3C300581EB0 /* ScrollLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 4265D9231559A3C300581EB0 /* ScrollLayout.h */; };
-		4265D9271559A3C300581EB0 /* ScrollLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 4265D9231559A3C300581EB0 /* ScrollLayout.h */; };
 		426878AC153F4BB300844500 /* FlowLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 426878AA153F4BB300844500 /* FlowLayout.cpp */; };
 		426878AD153F4BB300844500 /* FlowLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 426878AA153F4BB300844500 /* FlowLayout.cpp */; };
 		426878AE153F4BB300844500 /* FlowLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 426878AB153F4BB300844500 /* FlowLayout.h */; };
@@ -408,8 +404,6 @@
 		4251B130152D049B002F6199 /* ThemeStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThemeStyle.h; path = src/ThemeStyle.h; sourceTree = SOURCE_ROOT; };
 		42554E9F152BC35C000ED910 /* PhysicsCollisionShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PhysicsCollisionShape.cpp; path = src/PhysicsCollisionShape.cpp; sourceTree = SOURCE_ROOT; };
 		42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicsCollisionShape.h; path = src/PhysicsCollisionShape.h; sourceTree = SOURCE_ROOT; };
-		4265D9221559A3C300581EB0 /* ScrollLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ScrollLayout.cpp; path = src/ScrollLayout.cpp; sourceTree = SOURCE_ROOT; };
-		4265D9231559A3C300581EB0 /* ScrollLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScrollLayout.h; path = src/ScrollLayout.h; sourceTree = SOURCE_ROOT; };
 		426878AA153F4BB300844500 /* FlowLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FlowLayout.cpp; path = src/FlowLayout.cpp; sourceTree = SOURCE_ROOT; };
 		426878AB153F4BB300844500 /* FlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FlowLayout.h; path = src/FlowLayout.h; sourceTree = SOURCE_ROOT; };
 		4271C08D15337C8200B89DA7 /* Layout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Layout.cpp; path = src/Layout.cpp; sourceTree = SOURCE_ROOT; };
@@ -862,8 +856,6 @@
 				428390971489D6E800E2B2F5 /* SceneLoader.cpp */,
 				428390981489D6E800E2B2F5 /* SceneLoader.h */,
 				4251B12E152D049B002F6199 /* ScreenDisplayer.h */,
-				4265D9221559A3C300581EB0 /* ScrollLayout.cpp */,
-				4265D9231559A3C300581EB0 /* ScrollLayout.h */,
 				5BD52646150F822A004C9099 /* Slider.cpp */,
 				5BD52647150F822A004C9099 /* Slider.h */,
 				42CD0E2F147D8FF50000361E /* SpriteBatch.cpp */,
@@ -1072,7 +1064,6 @@
 				4251B135152D049B002F6199 /* ThemeStyle.h in Headers */,
 				422260D81537790F0011E3AB /* Bundle.h in Headers */,
 				426878AE153F4BB300844500 /* FlowLayout.h in Headers */,
-				4265D9261559A3C300581EB0 /* ScrollLayout.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1170,7 +1161,6 @@
 				4251B136152D049B002F6199 /* ThemeStyle.h in Headers */,
 				422260D91537790F0011E3AB /* Bundle.h in Headers */,
 				426878AF153F4BB300844500 /* FlowLayout.h in Headers */,
-				4265D9271559A3C300581EB0 /* ScrollLayout.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1327,7 +1317,6 @@
 				4271C08E15337C8200B89DA7 /* Layout.cpp in Sources */,
 				422260D61537790F0011E3AB /* Bundle.cpp in Sources */,
 				426878AC153F4BB300844500 /* FlowLayout.cpp in Sources */,
-				4265D9241559A3C300581EB0 /* ScrollLayout.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1420,7 +1409,6 @@
 				4271C08F15337C8200B89DA7 /* Layout.cpp in Sources */,
 				422260D71537790F0011E3AB /* Bundle.cpp in Sources */,
 				426878AD153F4BB300844500 /* FlowLayout.cpp in Sources */,
-				4265D9251559A3C300581EB0 /* ScrollLayout.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 67 - 49
gameplay/src/AudioBuffer.cpp

@@ -221,68 +221,86 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
         }
     }
 
-    // Read in the type of the next section of the file.
-    if (fread(stream, 1, 4, file) != 4)
-    {
-        GP_ERROR("Failed to read next section type (fact or data) from wave file.");
-        return false;
-    }
-
-    // Read the fact section if it is there.
-    if (memcmp(stream, "fact", 4) == 0)
+    // Read in the rest of the file a chunk (section) at a time.
+    while (true)
     {
-        if (fread(stream, 1, 4, file) != 4)
-        {
-            GP_ERROR("Failed to read fact section size from wave file.");
-            return false;
-        }
-
-        section_size  = stream[3]<<24;
-        section_size |= stream[2]<<16;
-        section_size |= stream[1]<<8;
-        section_size |= stream[0];
-
-        // Read in the fact section.
-        if (fread(stream, 1, section_size, file) != section_size)
+        // Check if we are at the end of the file without reading the data.
+        if (feof(file))
         {
-            GP_ERROR("Failed to read fact section from wave file.");
+            GP_ERROR("Failed to load wave file; file appears to have no data.");
             return false;
         }
 
         // Read in the type of the next section of the file.
         if (fread(stream, 1, 4, file) != 4)
         {
-            GP_ERROR("Failed to read next section type (should be data) from wave file.");
+            GP_ERROR("Failed to read next section type from wave file.");
             return false;
         }
-    }
-
-    // Should now be the data section which holds the decoded sample data.
-    if (memcmp(stream, "data", 4) != 0)
-    {
-        GP_ERROR("Failed to load wave file; file appears to have no data.");
-        return false;
-    }
 
-    // Read how much data is remaining and buffer it up.
-    unsigned int dataSize;
-    if (fread(&dataSize, sizeof(int), 1, file) != 1)
-    {
-        GP_ERROR("Failed to read size of data section from wave file.");
-        return false;
-    }
+        // Data chunk.
+        if (memcmp(stream, "data", 4) == 0)
+        {
+            // Read how much data is remaining and buffer it up.
+            unsigned int dataSize;
+            if (fread(&dataSize, sizeof(int), 1, file) != 1)
+            {
+                GP_ERROR("Failed to read size of data section from wave file.");
+                return false;
+            }
+
+            char* data = new char[dataSize];
+            if (fread(data, sizeof(char), dataSize, file) != dataSize)
+            {
+                GP_ERROR("Failed to load wave file; file is missing data.");
+                SAFE_DELETE_ARRAY(data);
+                return false;
+            }
+
+            AL_CHECK( alBufferData(buffer, format, data, dataSize, frequency) );
+            SAFE_DELETE_ARRAY(data);
 
-    char* data = new char[dataSize];
-    if (fread(data, sizeof(char), dataSize, file) != dataSize)
-    {
-        GP_ERROR("Failed to load wave file; file is missing data.");
-        SAFE_DELETE_ARRAY(data);
-        return false;
+            // We've read the data, so return now.
+            return true;
+        }
+        // Other chunk - could be any of the following:
+        // - Fact ("fact")
+        // - Wave List ("wavl")
+        // - Silent ("slnt")
+        // - Cue ("cue ")
+        // - Playlist ("plst")
+        // - Associated Data List ("list")
+        // - Label ("labl")
+        // - Note ("note")
+        // - Labeled Text ("ltxt")
+        // - Sampler ("smpl")
+        // - Instrument ("inst")
+        else
+        {
+            // Store the name of the chunk so we can report errors informatively.
+            char chunk[5] = { 0 };
+            memcpy(chunk, stream, 4);
+
+            // Read the chunk size.
+            if (fread(stream, 1, 4, file) != 4)
+            {
+                GP_ERROR("Failed to read size of '%s' chunk from wave file.", chunk);
+                return false;
+            }
+
+            section_size  = stream[3]<<24;
+            section_size |= stream[2]<<16;
+            section_size |= stream[1]<<8;
+            section_size |= stream[0];
+
+            // Seek past the chunk.
+            if (fseek(file, section_size, SEEK_CUR) != 0)
+            {
+                GP_ERROR("Failed to seek past '%s' chunk in wave file.", chunk);
+                return false;
+            }
+        }
     }
-
-    AL_CHECK( alBufferData(buffer, format, data, dataSize, frequency) );
-    SAFE_DELETE_ARRAY(data);
-    return true;
 }
     
 bool AudioBuffer::loadOgg(FILE* file, ALuint buffer)

+ 1 - 1
gameplay/src/CheckBox.cpp

@@ -5,7 +5,7 @@
 namespace gameplay
 {
 
-CheckBox::CheckBox() : _checked(false)
+CheckBox::CheckBox() : _checked(false), _image(NULL)
 {
 }
 

+ 1 - 1
gameplay/src/CheckBox.h

@@ -120,7 +120,7 @@ protected:
      *
      * @param clip The clipping rectangle of this control's parent container.
      */
-    void update(const Rectangle& clip, const Vector2& offset = Vector2::zero());
+    void update(const Rectangle& clip, const Vector2& offset);
 
     /**
      * Draw the checkbox icon associated with this control.

+ 379 - 9
gameplay/src/Container.cpp

@@ -4,7 +4,6 @@
 #include "AbsoluteLayout.h"
 #include "FlowLayout.h"
 #include "VerticalLayout.h"
-#include "ScrollLayout.h"
 #include "Label.h"
 #include "Button.h"
 #include "CheckBox.h"
@@ -16,7 +15,26 @@
 namespace gameplay
 {
 
-Container::Container() : _layout(NULL)
+    /**
+     * If the user stops scrolling for this amount of time (in millis) before
+     * touch/click release, don't apply inertia.
+     */
+    static const long STOP_TIME = 100L;
+
+    /**
+     * Factor to multiply friction by before applying to velocity.
+     */
+    static const float FRICTION_FACTOR = 5.0f;
+
+Container::Container()
+    : _layout(NULL), _scrollBarTopCap(NULL), _scrollBarVertical(NULL), _scrollBarBottomCap(NULL),
+      _scrollBarLeftCap(NULL), _scrollBarHorizontal(NULL), _scrollBarRightCap(NULL),
+      _scroll(SCROLL_NONE), _scrollBarBounds(Rectangle::empty()), _scrollPosition(Vector2::zero()),
+      _scrolling(false), _firstX(0), _firstY(0),
+      _lastX(0), _lastY(0),
+      _startTimeX(0), _startTimeY(0), _lastTime(0),
+      _velocity(Vector2::zero()), _friction(1.0f),
+      _goingRight(false), _goingDown(false), _zIndexDefault(0)
 {
 }
 
@@ -49,9 +67,6 @@ Container* Container::create(Layout::Type type)
     case Layout::LAYOUT_VERTICAL:
         layout = VerticalLayout::create();
         break;
-    case Layout::LAYOUT_SCROLL:
-        layout = ScrollLayout::create();
-        break;
     }
 
     Container* container = new Container();
@@ -67,6 +82,7 @@ Container* Container::create(Theme::Style* style, Properties* properties, Theme*
     const char* layoutString = properties->getString("layout");
     Container* container = Container::create(getLayoutType(layoutString));
     container->initialize(style, properties);
+    container->_scroll = getScroll(properties->getString("scroll"));
     container->addControls(theme, properties);
 
     return container;
@@ -128,6 +144,11 @@ void Container::addControls(Theme* theme, Properties* properties)
         if (control)
         {
             addControl(control);
+
+            if (control->getZIndex() == -1)
+            {
+                control->setZIndex(_zIndexDefault++);
+            }
         }
 
         // Get the next control.
@@ -225,6 +246,20 @@ const std::vector<Control*>& Container::getControls() const
     return _controls;
 }
 
+void Container::setScroll(Scroll scroll)
+{
+    if (scroll != _scroll)
+    {
+        _scroll = scroll;
+        _dirty = true;
+    }
+}
+
+Container::Scroll Container::getScroll() const
+{
+    return _scroll;
+}
+
 Animation* Container::getAnimation(const char* id) const
 {
     std::vector<Control*>::const_iterator itr = _controls.begin();
@@ -255,8 +290,33 @@ void Container::update(const Rectangle& clip, const Vector2& offset)
     // Update this container's viewport.
     Control::update(clip, offset);
 
+    // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
+    if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
+    {
+        _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
+        _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
+        _scrollBarRightCap = getImage("scrollBarRightCap", _state);
+
+        _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
+    }
+
+    if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
+    {
+        _scrollBarTopCap = getImage("scrollBarTopCap", _state);
+        _scrollBarVertical = getImage("verticalScrollBar", _state);
+        _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
+        
+        _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
+    }
+
+    // Sort controls by Z-Order.
+    std::sort(_controls.begin(), _controls.end(), &sortControlsByZOrder);
+
     GP_ASSERT(_layout);
     _layout->update(this);
+
+    if (_scroll != SCROLL_NONE)
+        this->updateScroll(this);
 }
 
 void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight)
@@ -274,7 +334,9 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
         needsClear = false;
     }
 
+    spriteBatch->begin();
     Control::drawBorder(spriteBatch, clip);
+    spriteBatch->end();
 
     std::vector<Control*>::const_iterator it;
     Rectangle boundsUnion = Rectangle::empty();
@@ -289,7 +351,84 @@ void Container::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needs
         }
     }
 
-    _dirty = false;
+    if (_scroll != SCROLL_NONE)
+    {
+        // Draw scroll bars.
+        Rectangle clipRegion(_viewportClipBounds);
+
+        spriteBatch->begin();
+
+        if (_scrollBarBounds.height > 0 && (_scrolling || _velocity.y) && _scrollBarTopCap && _scrollBarVertical && _scrollBarBottomCap)
+        {
+            const Rectangle& topRegion = _scrollBarTopCap->getRegion();
+            const Theme::UVs& topUVs = _scrollBarTopCap->getUVs();
+            Vector4 topColor = _scrollBarTopCap->getColor();
+
+            const Rectangle& verticalRegion = _scrollBarVertical->getRegion();
+            const Theme::UVs& verticalUVs = _scrollBarVertical->getUVs();
+            Vector4 verticalColor = _scrollBarVertical->getColor();
+
+            const Rectangle& bottomRegion = _scrollBarBottomCap->getRegion();
+            const Theme::UVs& bottomUVs = _scrollBarBottomCap->getUVs();
+            Vector4 bottomColor = _scrollBarBottomCap->getColor();
+
+            clipRegion.width += verticalRegion.width;
+
+            Rectangle bounds(_viewportBounds.x + _viewportBounds.width - verticalRegion.width,
+                             _viewportBounds.y + _scrollBarBounds.y,
+                             topRegion.width, topRegion.height);
+            spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, topUVs.u1, topUVs.v1, topUVs.u2, topUVs.v2, topColor, clipRegion);
+
+            bounds.y += topRegion.height;
+            bounds.height = _scrollBarBounds.height - topRegion.height - bottomRegion.height;
+            spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, verticalUVs.u1, verticalUVs.v1, verticalUVs.u2, verticalUVs.v2, verticalColor, clipRegion);
+
+            bounds.y += bounds.height;
+            bounds.height = bottomRegion.height;
+            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)
+        {
+            const Rectangle& leftRegion = _scrollBarLeftCap->getRegion();
+            const Theme::UVs& leftUVs = _scrollBarLeftCap->getUVs();
+            Vector4 leftColor = _scrollBarLeftCap->getColor();
+
+            const Rectangle& horizontalRegion = _scrollBarHorizontal->getRegion();
+            const Theme::UVs& horizontalUVs = _scrollBarHorizontal->getUVs();
+            Vector4 horizontalColor = _scrollBarHorizontal->getColor();
+
+            const Rectangle& rightRegion = _scrollBarRightCap->getRegion();
+            const Theme::UVs& rightUVs = _scrollBarRightCap->getUVs();
+            Vector4 rightColor = _scrollBarRightCap->getColor();
+
+            clipRegion.height += horizontalRegion.height;
+        
+            Rectangle bounds(_viewportBounds.x + _scrollBarBounds.x,
+                             _viewportBounds.y + _viewportBounds.height - horizontalRegion.height,
+                             leftRegion.width, leftRegion.height);
+            spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, leftUVs.u1, leftUVs.v1, leftUVs.u2, leftUVs.v2, leftColor, clipRegion);
+
+            bounds.x += leftRegion.width;
+            bounds.width = _scrollBarBounds.width - leftRegion.width - rightRegion.width;
+            spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, horizontalUVs.u1, horizontalUVs.v1, horizontalUVs.u2, horizontalUVs.v2, horizontalColor, clipRegion);
+
+            bounds.x += bounds.width;
+            bounds.width = rightRegion.width;
+            spriteBatch->draw(bounds.x, bounds.y, bounds.width, bounds.height, rightUVs.u1, rightUVs.v1, rightUVs.u2, rightUVs.v2, rightColor, clipRegion);
+        }
+
+        spriteBatch->end();
+
+        if (_velocity.isZero())
+        {
+            _dirty = false;
+        }
+    }
+    else
+    {
+        _dirty = false;
+    }
 }
 
 bool Container::isDirty()
@@ -328,9 +467,9 @@ bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int con
     float yPos = border.top + padding.top;
 
     Vector2* offset = NULL;
-    if (_layout->getType() == Layout::LAYOUT_SCROLL)
+    if (_scroll != SCROLL_NONE)
     {
-        offset = &((ScrollLayout*)_layout)->_scrollPosition;
+        offset = &_scrollPosition;
     }
 
     std::vector<Control*>::const_iterator it;
@@ -382,7 +521,7 @@ bool Container::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int con
     if (!eventConsumed)
     {
         // Pass the event on to the layout.
-        if (_layout->touchEvent(evt, x - xPos, y - yPos, contactIndex))
+        if (touchEventScroll(evt, x - xPos, y - yPos, contactIndex))
         {
             _dirty = true;
         }
@@ -447,4 +586,235 @@ Layout::Type Container::getLayoutType(const char* layoutString)
     }
 }
 
+void Container::updateScroll(const Container* container)
+{
+    GP_ASSERT(container);
+
+    // Update Time.
+    static long lastFrameTime = Game::getGameTime();
+    long frameTime = Game::getGameTime();
+    long elapsedTime = (frameTime - lastFrameTime);
+    lastFrameTime = frameTime;
+
+    const Rectangle& containerBounds = container->getBounds();
+    const Theme::Border& containerBorder = container->getBorder(container->getState());
+    const Theme::Padding& containerPadding = container->getPadding();
+
+    // Calculate total width and height.
+    float totalWidth = 0;
+    float totalHeight = 0;
+    std::vector<Control*> controls = container->getControls();
+    unsigned int controlsCount = controls.size();
+    for (unsigned int i = 0; i < controlsCount; i++)
+    {
+        Control* control = controls.at(i);
+
+        const Rectangle& bounds = control->getBounds();
+        const Theme::Margin& margin = control->getMargin();
+
+        float newWidth = bounds.x + bounds.width;
+        if (newWidth > totalWidth)
+        {
+            totalWidth = newWidth;
+        }
+
+        float newHeight = bounds.y + bounds.height;
+        if (newHeight > totalHeight)
+        {
+            totalHeight = newHeight;
+        }
+    }
+
+    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;
+
+    // Apply and dampen inertia.
+    if (!_scrolling && !_velocity.isZero())
+    {
+        // Calculate the time passed since last update.
+        float elapsedSecs = (float)elapsedTime * 0.001f;
+
+        _scrollPosition.x += _velocity.x * elapsedSecs;
+        _scrollPosition.y += _velocity.y * elapsedSecs;
+
+        float dampening = 1.0f - _friction * FRICTION_FACTOR * elapsedSecs;
+        _velocity.x *= dampening;
+        _velocity.y *= dampening;
+
+        if (abs(_velocity.x) < 100.0f)
+            _velocity.x = 0.0f;
+        if (abs(_velocity.y) < 100.0f)
+            _velocity.y = 0.0f;
+    }
+
+    // Stop scrolling when the far edge is reached.
+    if (-_scrollPosition.x > totalWidth - clipWidth)
+    {
+        _scrollPosition.x = -(totalWidth - clipWidth);
+        _velocity.x = 0;
+    }
+    
+    if (-_scrollPosition.y > totalHeight - clipHeight)
+    {
+        _scrollPosition.y = -(totalHeight - clipHeight);
+        _velocity.y = 0;
+    }
+
+    if (_scrollPosition.x > 0)
+    {
+        _scrollPosition.x = 0;
+        _velocity.x = 0;
+    }
+
+    if (_scrollPosition.y > 0)
+    {
+        _scrollPosition.y = 0;
+        _velocity.y = 0;
+    }
+
+    float scrollWidth = 0;
+    if (clipWidth < totalWidth)
+        scrollWidth = (clipWidth / totalWidth) * clipWidth;
+
+    float scrollHeight = 0;
+    if (clipHeight < totalHeight)
+        scrollHeight = (clipHeight / totalHeight) * clipHeight;
+
+    _scrollBarBounds.set(((-_scrollPosition.x) / totalWidth) * clipWidth,
+                         ((-_scrollPosition.y) / totalHeight) * clipHeight,
+                         scrollWidth, scrollHeight);
+
+    // Position controls within scroll area.
+    for (unsigned int i = 0; i < controlsCount; i++)
+    {
+        Control* control = controls.at(i);
+        control->update(container->getClip(), _scrollPosition);
+    }
+}
+
+bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
+{
+    switch(evt)
+    {
+    case Touch::TOUCH_PRESS:
+        _lastX = _firstX = x;
+        _lastY = _firstY = y;
+        _velocity.set(0, 0);
+        _scrolling = true;
+        _startTimeX = _startTimeY = 0;
+        break;
+
+    case Touch::TOUCH_MOVE:
+        if (_scrolling)
+        {
+            // Calculate the latest movement delta for the next update to use.
+            int vx = x - _lastX;
+            int vy = y - _lastY;
+            _velocity.set(vx, vy);
+            _scrollPosition.x += vx;
+            _scrollPosition.y += vy;
+            _lastX = x;
+            _lastY = y;
+
+            // If the user changes direction, reset the start time and position.
+            bool goingRight = (vx > 0);
+            if (goingRight != _goingRight)
+            {
+                _firstX = x;
+                _goingRight = goingRight;
+                _startTimeX = Game::getAbsoluteTime();
+            }
+
+            bool goingDown = (vy > 0);
+            if (goingDown != _goingDown)
+            {
+                _firstY = y;
+                _goingDown = goingDown;
+                _startTimeY = Game::getAbsoluteTime();
+            }
+
+            if (!_startTimeX)
+                _startTimeX = Game::getAbsoluteTime();
+
+            if (!_startTimeY)
+                _startTimeY = Game::getAbsoluteTime();
+
+            _lastTime = Game::getAbsoluteTime();
+
+            return true;
+        }
+        break;
+
+    case Touch::TOUCH_RELEASE:
+        long timeSinceLastMove = Game::getAbsoluteTime() - _lastTime;
+        if (timeSinceLastMove > STOP_TIME)
+        {
+            _velocity.set(0, 0);
+            _scrolling = false;
+            break;
+        }
+
+        int dx = _lastX - _firstX;
+        int dy = _lastY - _firstY;
+
+        long timeTakenX = Game::getAbsoluteTime() - _startTimeX;
+        float elapsedSecsX = (float)timeTakenX * 0.001f;
+        long timeTakenY = Game::getAbsoluteTime() - _startTimeY;
+        float elapsedSecsY = (float)timeTakenY * 0.001f;
+
+        float vx = dx;
+        float vy = dy;
+        if (elapsedSecsX > 0)
+            vx = (float)dx / elapsedSecsX;
+        if (elapsedSecsY > 0)
+            vy = (float)dy / elapsedSecsY;
+
+        _velocity.set(vx, vy);
+
+        _scrolling = false;
+        break;
+    }
+
+    return false;
+}
+
+Container::Scroll Container::getScroll(const char* scroll)
+{
+    if (!scroll)
+        return Container::SCROLL_NONE;
+
+    if (strcmp(scroll, "SCROLL_NONE") == 0)
+    {
+        return Container::SCROLL_NONE;
+    }
+    else if (strcmp(scroll, "SCROLL_HORIZONTAL") == 0)
+    {
+        return Container::SCROLL_HORIZONTAL;
+    }
+    else if (strcmp(scroll, "SCROLL_VERTICAL") == 0)
+    {
+        return Container::SCROLL_VERTICAL;
+    }
+    else if (strcmp(scroll, "SCROLL_BOTH") == 0)
+    {
+        return Container::SCROLL_BOTH;
+    }
+    else
+    {
+        GP_ERROR("Failed to get corresponding scroll state for unsupported value '%s'.", scroll);
+    }
+
+    return Container::SCROLL_NONE;
+}
+
+bool sortControlsByZOrder(Control* c1, Control* c2)
+{
+    if (c1->getZIndex() < c2->getZIndex())
+        return true;
+
+    return false;
+}
+
 }

+ 84 - 24
gameplay/src/Container.h

@@ -25,6 +25,7 @@ namespace gameplay
          size        = <width, height>   // Size of the container, measured in pixels.
          width       = <width>   // Can be used in place of 'size', e.g. with 'autoHeight = true'
          height      = <height>  // Can be used in place of 'size', e.g. with 'autoWidth = true'
+         scroll      = <Container::Scroll constant>
   
          // All the nested controls within this container.
          container 
@@ -45,6 +46,17 @@ class Container : public Control
 {
 public:
 
+    /**
+     * The definition for container scrolling.
+     */
+    enum Scroll
+    {
+        SCROLL_NONE        = 0,
+        SCROLL_HORIZONTAL  = 0x01,
+        SCROLL_VERTICAL    = 0x02,
+        SCROLL_BOTH = SCROLL_HORIZONTAL | SCROLL_VERTICAL
+    };
+
     /**
      * Get this container's layout.
      *
@@ -114,6 +126,15 @@ public:
      */
     const std::vector<Control*>& getControls() const;
 
+    /**
+     * Sets the scrolling for the container.
+     *
+     * @param scroll The scroll for the 
+     */
+    void setScroll(Scroll scroll);
+
+    Scroll getScroll() const;
+
     /**
      * Gets the first animation in the control with the specified ID.
      *
@@ -160,29 +181,6 @@ protected:
      */
     virtual void update(const Rectangle& clip, const Vector2& offset);
 
-    /**
-     * Draws the themed border and background of this container and all its controls.
-     *
-     * @param spriteBatch The sprite batch containing this container's border images.
-     * @param clip The clipping rectangle of this container's parent container.
-     */
-    //void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip, const Vector2& offset = Vector2::zero());
-
-    /**
-     * Draws the icons of all controls within this container.
-     *
-     * @param spriteBatch The sprite batch containing this control's icons.
-     * @param clip The clipping rectangle of this container's parent container.
-     */
-    //virtual void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
-
-    /**
-     * Draws the text of all controls within this container.
-     *
-     * @param clip The clipping rectangle of this container's parent container.
-     */
-    //virtual void drawText(const Rectangle& clip);
-
     /**
      * Touch callback on touch events.  Controls return true if they consume the touch event.
      *
@@ -231,6 +229,17 @@ protected:
      */
     void addControls(Theme* theme, Properties* properties);
 
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
+
+    /**
+     * Update scroll position and velocity.
+     */
+    void updateScroll(const Container* container);
+
+    bool touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
+
+    static Scroll getScroll(const char* scroll);
+
     /**
      * The container's layout.
      */
@@ -241,13 +250,64 @@ protected:
      */
     std::vector<Control*> _controls;
 
+    Theme::ThemeImage* _scrollBarTopCap;
+    Theme::ThemeImage* _scrollBarVertical;
+    Theme::ThemeImage* _scrollBarBottomCap;
+    Theme::ThemeImage* _scrollBarLeftCap;
+    Theme::ThemeImage* _scrollBarHorizontal;
+    Theme::ThemeImage* _scrollBarRightCap;
+
+    // Flag representing whether scrolling is enabled, and in which directions.
+    Scroll _scroll;
+
+    // Data required when scrolling is enabled.
+
+    /**
+     * x, width: Horizontal scrollbar position and size.
+     * y, height: Vertical scrollbar position and size.
+     */
+    Rectangle _scrollBarBounds;
+
+    // How far this layout has been scrolled in each direction.
+    Vector2 _scrollPosition;
+
+    // Whether the user is currently touching / holding the mouse down
+    // within this layout's container.
+    bool _scrolling;
+
+    // First touch point.
+    int _firstX;
+    int _firstY;
+
+    // Latest touch point.
+    int _lastX;
+    int _lastY;
+
+    // Time recorded on touch down.
+    long _startTimeX;
+    long _startTimeY;
+    long _lastTime;
+
+    // Speed to continue scrolling at after touch release.
+    Vector2 _velocity;
+
+    // Friction dampens velocity.
+    float _friction;
+
+    // Detect a change in scroll direction.
+    bool _goingRight;
+    bool _goingDown;
+
 private:
 
     Container(const Container& copy);
 
-    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
+    int _zIndexDefault;
 };
 
+// Sort funtion for use with _controls.sort(), based on Z-Order.
+bool sortControlsByZOrder(Control* c1, Control* c2);
+
 }
 
 #endif

+ 26 - 0
gameplay/src/Control.cpp

@@ -43,6 +43,15 @@ void Control::initialize(Theme::Style* style, Properties* properties)
     _autoWidth = properties->getBool("autoWidth");
     _autoHeight = properties->getBool("autoHeight");
 
+    if (properties->exists("zIndex"))
+    {
+        _zIndex = properties->getInt("zIndex");
+    }
+    else
+    {
+        _zIndex = -1;
+    }
+
     Vector2 position;
     Vector2 size;
     if (properties->exists("position"))
@@ -608,6 +617,20 @@ bool Control::getConsumeTouchEvents()
     return _consumeTouchEvents;
 }
 
+int Control::getZIndex() const
+{
+    return _zIndex;
+}
+
+void Control::setZIndex(int zIndex)
+{
+    if (zIndex != _zIndex)
+    {
+        _zIndex = zIndex;
+        _dirty = true;
+    }
+}
+
 void Control::addListener(Control::Listener* listener, int eventFlags)
 {
     GP_ASSERT(listener);
@@ -888,8 +911,11 @@ void Control::draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsCl
         GL_ASSERT( glDisable(GL_SCISSOR_TEST) );
     }
 
+    spriteBatch->begin();
     drawBorder(spriteBatch, clip);
     drawImages(spriteBatch, clip);
+    spriteBatch->end();
+
     drawText(clip);
     _dirty = false;
 }

+ 8 - 2
gameplay/src/Control.h

@@ -659,6 +659,10 @@ public:
      */
     Theme::Style* getStyle() const;
 
+    int getZIndex() const;
+
+    void setZIndex(int zOrder);
+
     /**
      * Add a listener to be notified of specific events affecting
      * this control.  Event types can be OR'ed together.
@@ -757,6 +761,8 @@ protected:
      */
     virtual void drawText(const Rectangle& clip);
 
+    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
+
     /**
      * Initialize properties common to STATE_ALL Controls.
      */
@@ -887,6 +893,8 @@ protected:
      */
     float _opacity;
 
+    int _zIndex;
+
 private:
 
     /*
@@ -919,8 +927,6 @@ private:
      * @param clip The clipping rectangle of this control's parent container.
      */
     virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
-
-    virtual void draw(SpriteBatch* spriteBatch, const Rectangle& clip, bool needsClear, float targetHeight);
     
     bool _styleOverridden;
     Theme::Skin* _skin;

+ 42 - 60
gameplay/src/Form.cpp

@@ -2,7 +2,6 @@
 #include "Form.h"
 #include "AbsoluteLayout.h"
 #include "FlowLayout.h"
-#include "ScrollLayout.h"
 #include "VerticalLayout.h"
 #include "Game.h"
 #include "Theme.h"
@@ -76,9 +75,6 @@ Form* Form::create(const char* url)
     case Layout::LAYOUT_VERTICAL:
         layout = VerticalLayout::create();
         break;
-    case Layout::LAYOUT_SCROLL:
-        layout = ScrollLayout::create();
-        break;
     default:
         GP_ERROR("Unsupported layout type '%d'.", getLayoutType(layoutString));
     }
@@ -90,6 +86,10 @@ Form* Form::create(const char* url)
     form->_layout = layout;
     form->_theme = theme;
 
+    // Get default projection matrix.
+    Game* game = Game::getInstance();
+    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
+
     const char* styleName = formProperties->getString("style");
     form->initialize(theme->getStyle(styleName), formProperties);
 
@@ -112,12 +112,11 @@ Form* Form::create(const char* url)
         form->_bounds.x = Game::getInstance()->getWidth() * 0.5f - form->_bounds.width * 0.5f;
     }
 
+    form->_scroll = getScroll(formProperties->getString("scroll"));
+
     // Add all the controls to the form.
     form->addControls(theme, formProperties);
 
-    Game* game = Game::getInstance();
-    Matrix::createOrthographicOffCenter(0, game->getWidth(), game->getHeight(), 0, 0, 1, &form->_defaultProjectionMatrix);
-
     SAFE_DELETE(properties);
 
     __forms.push_back(form);
@@ -193,6 +192,19 @@ void Form::setSize(float width, float height)
         _spriteBatch = SpriteBatch::create(_frameBuffer->getRenderTarget()->getTexture());
         GP_ASSERT(_spriteBatch);
 
+        // Clear FBO.
+        _frameBuffer->bind();
+        Game* game = Game::getInstance();
+        Rectangle prevViewport = game->getViewport();
+        game->setViewport(Rectangle(0, 0, width, height));
+        _theme->setProjectionMatrix(_projectionMatrix);
+        GL_ASSERT( glClearColor(0, 0, 0, 0) );
+        GL_ASSERT( glClear(GL_COLOR_BUFFER_BIT) );
+        GL_ASSERT( glClearColor(0, 0, 0, 1) );
+        _theme->setProjectionMatrix(_defaultProjectionMatrix);
+        FrameBuffer::bindDefault();
+        game->setViewport(prevViewport);
+
         _bounds.width = width;
         _bounds.height = height;
         _dirty = true;
@@ -385,8 +397,30 @@ void Form::update()
         _skin = getSkin(_state);
         _opacity = getOpacity(_state);
 
+        // Get scrollbar images and diminish clipping bounds to make room for scrollbars.
+        if ((_scroll & SCROLL_HORIZONTAL) == SCROLL_HORIZONTAL)
+        {
+            _scrollBarLeftCap = getImage("scrollBarLeftCap", _state);
+            _scrollBarHorizontal = getImage("horizontalScrollBar", _state);
+            _scrollBarRightCap = getImage("scrollBarRightCap", _state);
+
+            _viewportClipBounds.height -= _scrollBarHorizontal->getRegion().height;
+        }
+
+        if ((_scroll & SCROLL_VERTICAL) == SCROLL_VERTICAL)
+        {
+            _scrollBarTopCap = getImage("scrollBarTopCap", _state);
+            _scrollBarVertical = getImage("verticalScrollBar", _state);
+            _scrollBarBottomCap = getImage("scrollBarBottomCap", _state);
+        
+            _viewportClipBounds.width -= _scrollBarVertical->getRegion().width;
+        }
+
         GP_ASSERT(_layout);
         _layout->update(this);
+
+        if (_scroll != SCROLL_NONE)
+            this->updateScroll(this);
     }
 }
 
@@ -417,7 +451,7 @@ void Form::draw()
 
         GP_ASSERT(_theme);
         _theme->setProjectionMatrix(_projectionMatrix);
-        draw(_theme->getSpriteBatch(), _viewportClipBounds);
+        Container::draw(_theme->getSpriteBatch(), Rectangle(0, 0, _bounds.width, _bounds.height), _skin == NULL, _bounds.height);
         _theme->setProjectionMatrix(_defaultProjectionMatrix);
 
         // Rebind the default framebuffer and game viewport.
@@ -446,58 +480,6 @@ void Form::draw()
     }
 }
 
-void Form::draw(SpriteBatch* spriteBatch, const Rectangle& clip)
-{
-    GP_ASSERT(spriteBatch);
-
-    std::vector<Control*>::const_iterator it;
-
-    // Batch each font individually.
-    std::set<Font*>::const_iterator fontIter;
-    for (fontIter = _theme->_fonts.begin(); fontIter != _theme->_fonts.end(); fontIter++)
-    {
-        Font* font = *fontIter;
-        if (font)
-        {
-            font->begin();
-        }
-    }
-
-    // Batch for all themed border and background sprites.
-    spriteBatch->begin();
-
-    // Draw the form's border and background.
-    // We don't pass the form's position to itself or it will be applied twice!
-    Control::drawBorder(spriteBatch, Rectangle(0, 0, _bounds.width, _bounds.height));
-
-    Rectangle boundsUnion = Rectangle::empty();
-    for (it = _controls.begin(); it < _controls.end(); it++)
-    {
-        Control* control = *it;
-        GP_ASSERT(control);
-
-        if (_skin || control->isDirty() || control->_clearBounds.intersects(boundsUnion))
-        {
-            control->draw(spriteBatch, clip, _skin == NULL, _bounds.height);
-            Rectangle::combine(control->_clearBounds, boundsUnion, &boundsUnion);
-        }
-    }
-
-    // Done all batching.
-    spriteBatch->end();
-
-    for (fontIter = _theme->_fonts.begin(); fontIter != _theme->_fonts.end(); fontIter++)
-    {
-        Font* font = *fontIter;
-        if (font)
-        {
-            font->end();
-        }
-    }
-
-    _dirty = false;
-}
-
 void Form::initializeQuad(Mesh* mesh)
 {
     // Release current model.

+ 0 - 8
gameplay/src/Form.h

@@ -166,14 +166,6 @@ private:
      */
     void initializeQuad(Mesh* mesh);
 
-    /**
-     * Draw this form into the current framebuffer.
-     *
-     * @param spriteBatch The sprite batch containing this form's theme texture.
-     * @param clip The form's clipping rectangle.
-     */
-    void draw(SpriteBatch* spriteBatch, const Rectangle& clip);
-
     /**
      * Propagate touch events to enabled forms.
      *

+ 2 - 0
gameplay/src/Label.cpp

@@ -86,7 +86,9 @@ void Label::drawText(const Rectangle& clip)
     // Draw the text.
     if (_font)
     {
+        _font->begin();
         _font->drawText(_text.c_str(), _textBounds, _textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_viewportClipBounds);
+        _font->end();
     }
 
     _dirty = false;

+ 2 - 2
gameplay/src/Layout.cpp

@@ -25,12 +25,12 @@ void Layout::align(Control* control, const Container* container)
 
         if (control->_autoWidth)
         {
-            controlBounds.width = clipWidth;
+            controlBounds.width = clipWidth - controlMargin.left - controlMargin.right;
         }
 
         if (control->_autoHeight)
         {
-            controlBounds.height = clipHeight;
+            controlBounds.height = clipHeight - controlMargin.top - controlMargin.bottom;
         }
 
         // Vertical alignment

+ 5 - 4
gameplay/src/MathUtil.h

@@ -5,9 +5,10 @@ namespace gameplay
 {
 class MathUtil
 {
-	//friend class Matrix;
-	//friend class Vector3;
-public:
+	friend class Matrix;
+	friend class Vector3;
+
+private:
 
 	/** Matrix **/
 	inline static void addMatrix(const float* m, float scalar, float* dst);
@@ -22,7 +23,7 @@ public:
 
 	/** Vector3 **/
 	inline static void crossVector3(const float* v1, const float* v2, float* dst);
-private:
+
 	MathUtil();
 };
 }

+ 5 - 359
gameplay/src/Matrix.cpp

@@ -361,48 +361,6 @@ void Matrix::add(float scalar)
 void Matrix::add(float scalar, Matrix* dst)
 {
     GP_ASSERT(dst);
-/*
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32 {q0, q1}, [%1]! 	\n\t" // M[m0-m7]
-    	"vld1.32 {q2, q3}, [%1] 	\n\t" // M[m8-m15]
-    	"vld1.32 {d8[0]},  [%2] 	\n\t" // s
-    	"vmov.f32 s17, s16          \n\t" // s
-    	"vmov.f32 s18, s16          \n\t" // s
-    	"vmov.f32 s19, s16          \n\t" // s
-    	"vadd.f32 q8, q0, q4  	\n\t" // DST->M[m0-m3] = M[m0-m3] + s
-    	"vadd.f32 q9, q1, q4 	\n\t" // DST->M[m4-m7] = M[m4-m7] + s
-    	"vadd.f32 q10, q2, q4 	\n\t" // DST->M[m8-m11] = M[m8-m11] + s
-    	"vadd.f32 q11, q3, q4 	\n\t" // DST->M[m12-m15] = M[m12-m15] + s
-
-    	"vst1.32 {q8, q9}, [%0]!  \n\t" // DST->M[m0-m7]
-    	"vst1.32 {q10, q11}, [%0]   \n\t" // DST->M[m8-m15]
-    	:
-    	: "r"(dst->m), "r"(m), "r"(&scalar)
-    	: "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11", "memory"
-    );
-
-#else
-
-    dst->m[0]  = m[0]  + scalar;
-    dst->m[1]  = m[1]  + scalar;
-    dst->m[2]  = m[2]  + scalar;
-    dst->m[3]  = m[3]  + scalar;
-    dst->m[4]  = m[4]  + scalar;
-    dst->m[5]  = m[5]  + scalar;
-    dst->m[6]  = m[6]  + scalar;
-    dst->m[7]  = m[7]  + scalar;
-    dst->m[8]  = m[8]  + scalar;
-    dst->m[9]  = m[9]  + scalar;
-    dst->m[10] = m[10] + scalar;
-    dst->m[11] = m[11] + scalar;
-    dst->m[12] = m[12] + scalar;
-    dst->m[13] = m[13] + scalar;
-    dst->m[14] = m[14] + scalar;
-    dst->m[15] = m[15] + scalar;
-
-#endif*/
 
     MathUtil::addMatrix(m, scalar, dst->m);
 }
@@ -415,47 +373,6 @@ void Matrix::add(const Matrix& m)
 void Matrix::add(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
     GP_ASSERT(dst);
-/*
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32 	{q0, q1}, 	[%1]! 	\n\t" // M1[m0-m7]
-		"vld1.32 	{q2, q3}, 	[%1] 	\n\t" // M1[m8-m15]
-    	"vld1.32 	{q8, q9}, 	[%2]! 	\n\t" // M2[m0-m7]
-		"vld1.32 	{q10, q11}, [%2]  	\n\t" // M2[m8-m15]
-
-    	"vadd.f32   q12, q0, q8 		\n\t" // DST->M[m0-m3] = M1[m0-m3] + M2[m0-m3]
-    	"vadd.f32   q13, q1, q9			\n\t" // DST->M[m4-m7] = M1[m4-m7] + M2[m4-m7]
-    	"vadd.f32   q14, q2, q10		\n\t" // DST->M[m8-m11] = M1[m8-m11] + M2[m8-m11]
-    	"vadd.f32   q15, q3, q11		\n\t" // DST->M[m12-m15] = M1[m12-m15] + M2[m12-m15]
-
-    	"vst1.32    {q12, q13}, [%0]!   \n\t" // DST->M[m0-m7]
-		"vst1.32    {q14, q15}, [%0]    \n\t" // DST->M[m8-m15]
-		:
-        : "r"(dst->m), "r"(m1.m), "r"(m2.m)
-        : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
-    );
-
-#else
-
-    dst->m[0]  = m1.m[0]  + m2.m[0];
-    dst->m[1]  = m1.m[1]  + m2.m[1];
-    dst->m[2]  = m1.m[2]  + m2.m[2];
-    dst->m[3]  = m1.m[3]  + m2.m[3];
-    dst->m[4]  = m1.m[4]  + m2.m[4];
-    dst->m[5]  = m1.m[5]  + m2.m[5];
-    dst->m[6]  = m1.m[6]  + m2.m[6];
-    dst->m[7]  = m1.m[7]  + m2.m[7];
-    dst->m[8]  = m1.m[8]  + m2.m[8];
-    dst->m[9]  = m1.m[9]  + m2.m[9];
-    dst->m[10] = m1.m[10] + m2.m[10];
-    dst->m[11] = m1.m[11] + m2.m[11];
-    dst->m[12] = m1.m[12] + m2.m[12];
-    dst->m[13] = m1.m[13] + m2.m[13];
-    dst->m[14] = m1.m[14] + m2.m[14];
-    dst->m[15] = m1.m[15] + m2.m[15];
-
-#endif*/
 
     MathUtil::addMatrix(m1.m, m2.m, dst->m);
 }
@@ -728,46 +645,7 @@ void Matrix::multiply(float scalar, Matrix* dst) const
 void Matrix::multiply(const Matrix& m, float scalar, Matrix* dst)
 {
     GP_ASSERT(dst);
-/*
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32 	{d0[0]},	 	[%0]     	\n\t" // M[m0-m7]
-    	"vld1.32	{q4-q5},  		[%1]!    	\n\t" // M[m8-m15]
-		"vld1.32	{q6-q7},  		[%1]		\n\t" // s
-
-    	"vmul.f32 	q8, q4, d0[0]    			\n\t" // DST->M[m0-m3] = M[m0-m3] * s
-    	"vmul.f32 	q9, q5, d0[0]    			\n\t" // DST->M[m4-m7] = M[m4-m7] * s
-		"vmul.f32 	q10, q6, d0[0]    			\n\t" // DST->M[m8-m11] = M[m8-m11] * s
-		"vmul.f32 	q11, q7, d0[0]   		 	\n\t" // DST->M[m12-m15] = M[m12-m15] * s
-
-    	"vst1.32 	{q8-q9},   		[%2]! 		\n\t" // DST->M[m0-m7]
-		"vst1.32 	{q10-q11}, 		[%2]		\n\t" // DST->M[m8-m15]
-		:
-		: "r"(&scalar), "r"(m.m), "r"(dst->m)
-		: "q0", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "memory"
-	);
-
-#else
-
-    dst->m[0]  = m.m[0]  * scalar;
-    dst->m[1]  = m.m[1]  * scalar;
-    dst->m[2]  = m.m[2]  * scalar;
-    dst->m[3]  = m.m[3]  * scalar;
-    dst->m[4]  = m.m[4]  * scalar;
-    dst->m[5]  = m.m[5]  * scalar;
-    dst->m[6]  = m.m[6]  * scalar;
-    dst->m[7]  = m.m[7]  * scalar;
-    dst->m[8]  = m.m[8]  * scalar;
-    dst->m[9]  = m.m[9]  * scalar;
-    dst->m[10] = m.m[10] * scalar;
-    dst->m[11] = m.m[11] * scalar;
-    dst->m[12] = m.m[12] * scalar;
-    dst->m[13] = m.m[13] * scalar;
-    dst->m[14] = m.m[14] * scalar;
-    dst->m[15] = m.m[15] * scalar;
-
-#endif*/
+
     MathUtil::multiplyMatrix(m.m, scalar, dst->m);
 }
 
@@ -779,72 +657,6 @@ void Matrix::multiply(const Matrix& m)
 void Matrix::multiply(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
 	GP_ASSERT(dst);
-/*
-#ifdef USE_NEON // if set, neon unit is present.
-
-    asm volatile
-    (
-        "vld1.32	 {d16 - d19}, [%1]!	  \n\t"       // M1[m0-m7]
-		"vld1.32     {d20 - d23}, [%1]    \n\t"       // M1[m8-m15]
-		"vld1.32     {d0 - d3}, [%2]!     \n\t"       // M2[m0-m7]
-		"vld1.32     {d4 - d7}, [%2]      \n\t"       // M2[m8-m15]
-
-		"vmul.f32    q12, q8, d0[0]     \n\t"         // DST->M[m0-m3] = M1[m0-m3] * M2[m0]
-		"vmul.f32    q13, q8, d2[0]     \n\t"         // DST->M[m4-m7] = M1[m4-m7] * M2[m4]
-		"vmul.f32    q14, q8, d4[0]     \n\t"         // DST->M[m8-m11] = M1[m8-m11] * M2[m8]
-		"vmul.f32    q15, q8, d6[0]     \n\t"         // DST->M[m12-m15] = M1[m12-m15] * M2[m12]
-
-		"vmla.f32    q12, q9, d0[1]     \n\t"         // DST->M[m0-m3] += M1[m0-m3] * M2[m1]
-		"vmla.f32    q13, q9, d2[1]     \n\t"         // DST->M[m4-m7] += M1[m4-m7] * M2[m5]
-		"vmla.f32    q14, q9, d4[1]     \n\t"         // DST->M[m8-m11] += M1[m8-m11] * M2[m9]
-		"vmla.f32    q15, q9, d6[1]     \n\t"         // DST->M[m12-m15] += M1[m12-m15] * M2[m13]
-
-		"vmla.f32    q12, q10, d1[0]    \n\t"         // DST->M[m0-m3] += M1[m0-m3] * M2[m2]
-		"vmla.f32    q13, q10, d3[0]    \n\t"         // DST->M[m4-m7] += M1[m4-m7] * M2[m6]
-		"vmla.f32    q14, q10, d5[0]    \n\t"         // DST->M[m8-m11] += M1[m8-m11] * M2[m10]
-		"vmla.f32    q15, q10, d7[0]    \n\t"         // DST->M[m12-m15] += M1[m12-m15] * M2[m14]
-
-		"vmla.f32    q12, q11, d1[1]    \n\t"         // DST->M[m0-m3] += M1[m0-m3] * M2[m3]
-		"vmla.f32    q13, q11, d3[1]    \n\t"         // DST->M[m4-m7] += M1[m4-m7] * M2[m7]
-		"vmla.f32    q14, q11, d5[1]    \n\t"         // DST->M[m8-m11] += M1[m8-m11] * M2[m11]
-		"vmla.f32    q15, q11, d7[1]    \n\t"         // DST->M[m12-m15] += M1[m12-m15] * M2[m15]
-
-		"vst1.32    {d24 - d27}, [%0]!    \n\t"       // DST->M[m0-m7]
-		"vst1.32    {d28 - d31}, [%0]     \n\t"       // DST->M[m8-m15]
-        
-        : // output
-        : "r"(dst->m), "r"(m1.m), "r"(m2.m) // input - note *value* of pointer doesn't change.
-        : "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
-	);
-
-#else
-
-    // Support the case where m1 or m2 is the same array as dst.
-    float product[16];
-
-    product[0]  = m1.m[0] * m2.m[0]  + m1.m[4] * m2.m[1] + m1.m[8]   * m2.m[2]  + m1.m[12] * m2.m[3];
-    product[1]  = m1.m[1] * m2.m[0]  + m1.m[5] * m2.m[1] + m1.m[9]   * m2.m[2]  + m1.m[13] * m2.m[3];
-    product[2]  = m1.m[2] * m2.m[0]  + m1.m[6] * m2.m[1] + m1.m[10]  * m2.m[2]  + m1.m[14] * m2.m[3];
-    product[3]  = m1.m[3] * m2.m[0]  + m1.m[7] * m2.m[1] + m1.m[11]  * m2.m[2]  + m1.m[15] * m2.m[3];
-
-    product[4]  = m1.m[0] * m2.m[4]  + m1.m[4] * m2.m[5] + m1.m[8]   * m2.m[6]  + m1.m[12] * m2.m[7];
-    product[5]  = m1.m[1] * m2.m[4]  + m1.m[5] * m2.m[5] + m1.m[9]   * m2.m[6]  + m1.m[13] * m2.m[7];
-    product[6]  = m1.m[2] * m2.m[4]  + m1.m[6] * m2.m[5] + m1.m[10]  * m2.m[6]  + m1.m[14] * m2.m[7];
-    product[7]  = m1.m[3] * m2.m[4]  + m1.m[7] * m2.m[5] + m1.m[11]  * m2.m[6]  + m1.m[15] * m2.m[7];
-
-    product[8]  = m1.m[0] * m2.m[8]  + m1.m[4] * m2.m[9] + m1.m[8]   * m2.m[10] + m1.m[12] * m2.m[11];
-    product[9]  = m1.m[1] * m2.m[8]  + m1.m[5] * m2.m[9] + m1.m[9]   * m2.m[10] + m1.m[13] * m2.m[11];
-    product[10] = m1.m[2] * m2.m[8]  + m1.m[6] * m2.m[9] + m1.m[10]  * m2.m[10] + m1.m[14] * m2.m[11];
-    product[11] = m1.m[3] * m2.m[8]  + m1.m[7] * m2.m[9] + m1.m[11]  * m2.m[10] + m1.m[15] * m2.m[11];
-
-    product[12] = m1.m[0] * m2.m[12] + m1.m[4] * m2.m[13] + m1.m[8]  * m2.m[14] + m1.m[12] * m2.m[15];
-    product[13] = m1.m[1] * m2.m[12] + m1.m[5] * m2.m[13] + m1.m[9]  * m2.m[14] + m1.m[13] * m2.m[15];
-    product[14] = m1.m[2] * m2.m[12] + m1.m[6] * m2.m[13] + m1.m[10] * m2.m[14] + m1.m[14] * m2.m[15];
-    product[15] = m1.m[3] * m2.m[12] + m1.m[7] * m2.m[13] + m1.m[11] * m2.m[14] + m1.m[15] * m2.m[15];
-
-    memcpy(dst->m, product, MATRIX_SIZE);
-
-#endif*/
 
 	MathUtil::multiplyMatrix(m1.m, m2.m, dst->m);
 }
@@ -857,46 +669,7 @@ void Matrix::negate()
 void Matrix::negate(Matrix* dst) const
 {
     GP_ASSERT(dst);
-/*
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32 	{q0-q1},  [%1]! 	\n\t" // load m0-m7
-    	"vld1.32 	{q2-q3},  [%1]   	\n\t" // load m8-m15
-
-    	"vneg.f32 	q4, q0 				\n\t" // negate m0-m3
-    	"vneg.f32 	q5, q1 				\n\t" // negate m4-m7
-		"vneg.f32 	q6, q2 				\n\t" // negate m8-m15
-		"vneg.f32 	q7, q3 				\n\t" // negate m8-m15
-
-    	"vst1.32 	{q4-q5},  [%0]!		\n\t" // store m0-m7
-    	"vst1.32 	{q6-q7},  [%0]		\n\t" // store m8-m15
-    	:
-    	: "r"(dst->m), "r"(m)
-    	: "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory"
-    );
-
-#else
-
-    dst->m[0]  = -m[0];
-    dst->m[1]  = -m[1];
-    dst->m[2]  = -m[2];
-    dst->m[3]  = -m[3];
-    dst->m[4]  = -m[4];
-    dst->m[5]  = -m[5];
-    dst->m[6]  = -m[6];
-    dst->m[7]  = -m[7];
-    dst->m[8]  = -m[8];
-    dst->m[9]  = -m[9];
-    dst->m[10] = -m[10];
-    dst->m[11] = -m[11];
-    dst->m[12] = -m[12];
-    dst->m[13] = -m[13];
-    dst->m[14] = -m[14];
-    dst->m[15] = -m[15];
-
-#endif
-*/
+
     MathUtil::negateMatrix(m, dst->m);
 }
 
@@ -1041,49 +814,7 @@ void Matrix::subtract(const Matrix& m)
 void Matrix::subtract(const Matrix& m1, const Matrix& m2, Matrix* dst)
 {
     GP_ASSERT(dst);
-/*
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32 	{q0, q1}, 	[%1]! 	\n\t" // M1[m0-m7]
-		"vld1.32 	{q2, q3}, 	[%1] 	\n\t" // M1[m8-m15]
-    	"vld1.32 	{q8, q9}, 	[%2]! 	\n\t" // M2[m0-m7]
-		"vld1.32 	{q10, q11}, [%2] 	\n\t" // M2[m8-m15]
-
-		"vsub.f32   q12, q0, q8 		\n\t" // DST->M[m0-m3] = M1[m0-m3] - M2[m0-m3]
-    	"vsub.f32   q13, q1, q9			\n\t" // DST->M[m4-m7] = M1[m4-m7] - M2[m4-m7]
-    	"vsub.f32   q14, q2, q10		\n\t" // DST->M[m8-m11] = M1[m8-m11] - M2[m8-m11]
-    	"vsub.f32   q15, q3, q11		\n\t" // DST->M[m12-m15] = M1[m12-m15] - M2[m12-m15]
-
-    	"vst1.32    {q12, q13}, [%0]!   \n\t" // DST->M[m0-m7]
-		"vst1.32    {q14, q15}, [%0]    \n\t" // DST->M[m8-m15]
-		:
-        : "r"(dst->m), "r"(m1.m), "r"(m2.m)
-        : "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
-    );
-
-
-#else
-
-    dst->m[0]  = m1.m[0]  - m2.m[0];
-    dst->m[1]  = m1.m[1]  - m2.m[1];
-    dst->m[2]  = m1.m[2]  - m2.m[2];
-    dst->m[3]  = m1.m[3]  - m2.m[3];
-    dst->m[4]  = m1.m[4]  - m2.m[4];
-    dst->m[5]  = m1.m[5]  - m2.m[5];
-    dst->m[6]  = m1.m[6]  - m2.m[6];
-    dst->m[7]  = m1.m[7]  - m2.m[7];
-    dst->m[8]  = m1.m[8]  - m2.m[8];
-    dst->m[9]  = m1.m[9]  - m2.m[9];
-    dst->m[10] = m1.m[10] - m2.m[10];
-    dst->m[11] = m1.m[11] - m2.m[11];
-    dst->m[12] = m1.m[12] - m2.m[12];
-    dst->m[13] = m1.m[13] - m2.m[13];
-    dst->m[14] = m1.m[14] - m2.m[14];
-    dst->m[15] = m1.m[15] - m2.m[15];
-
-#endif
-*/
+
     MathUtil::subtractMatrix(m1.m, m2.m, dst->m);
 }
 
@@ -1112,37 +843,7 @@ void Matrix::transformVector(const Vector3& vector, Vector3* dst) const
 void Matrix::transformVector(float x, float y, float z, float w, Vector3* dst) const
 {
     GP_ASSERT(dst);
-/*
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32	{d0[0]},		[%0]	\n\t"	// V[x]
-		"vld1.32	{d0[1]},    	[%1]	\n\t"	// V[y]
-		"vld1.32	{d1[0]},		[%2]	\n\t"	// V[z]
-		"vld1.32	{d1[1]},		[%3]	\n\t"	// V[w]
-		"vld1.32	{d18 - d21},	[%4]!	\n\t"	// M[m0-m7]
-		"vld1.32	{d22 - d25},	[%4]	\n\t"	// M[m8-m15]
-
-    	"vmul.f32 q13,  q9, d0[0]			\n\t"	// DST->V = M[m0-m3] * V[x]
-    	"vmla.f32 q13, q10, d0[1]      		\n\t"	// DST->V += M[m4-m7] * V[y]
-    	"vmla.f32 q13, q11, d1[0]      		\n\t"	// DST->V += M[m8-m11] * V[z]
-		"vmla.f32 q13, q12, d1[1]      		\n\t"	// DST->V += M[m12-m15] * V[w]
-
-    	"vst1.32 {d26}, [%5]!        		\n\t"	// DST->V[x, y]
-		"vst1.32 {d27[0]}, [%5]        		\n\t"	// DST->V[z]
-		:
-    	: "r"(&x), "r"(&y), "r"(&z), "r"(&w), "r"(m), "r"(dst)
-        : "q0", "q9", "q10","q11", "q12", "q13", "memory"
-    );
-
-#else
-
-    dst->set(
-        x * m[0] + y * m[4] + z * m[8] + w * m[12],
-        x * m[1] + y * m[5] + z * m[9] + w * m[13],
-        x * m[2] + y * m[6] + z * m[10] + w * m[14]);
-
-#endif*/
+
     MathUtil::transformVectorMatrix(m, x, y, z, w, (float*)dst);
 }
 
@@ -1155,37 +856,7 @@ void Matrix::transformVector(Vector4* vector) const
 void Matrix::transformVector(const Vector4& vector, Vector4* dst) const
 {
     GP_ASSERT(dst);
-/*
-#ifdef USE_NEON
-
-    asm volatile
-    (
-    		"vld1.32	{d0, d1}, [%1]		\n\t"   // V[x, y, z, w]
-    		"vld1.32    {d18 - d21}, [%0]!  \n\t"   // M[m0-m7]
-    		"vld1.32    {d22 - d25}, [%0]  \n\t"    // M[m8-m15]
-
-    		"vmul.f32   q13, q9, d0[0]      \n\t"   // DST->V = M[m0-m3] * V[x]
-    		"vmla.f32   q13, q10, d0[1]     \n\t"   // DST->V = M[m4-m7] * V[y]
-    		"vmla.f32   q13, q11, d1[0]     \n\t"   // DST->V = M[m8-m11] * V[z]
-    		"vmla.f32   q13, q12, d1[1]     \n\t"   // DST->V = M[m12-m15] * V[w]
 
-    		"vst1.32    {d26, d27}, [%2]    \n\t"   // DST->V
-
-    		:
-    		: "r"(m), "r"(&vector), "r"(dst)
-    		: "q0", "q9", "q10","q11", "q12", "q13", "memory"
-    );
-
-#else
-
-    dst->set(
-        vector.x * m[0] + vector.y * m[4] + vector.z * m[8] + vector.w * m[12],
-        vector.x * m[1] + vector.y * m[5] + vector.z * m[9] + vector.w * m[13],
-        vector.x * m[2] + vector.y * m[6] + vector.z * m[10] + vector.w * m[14],
-        vector.x * m[3] + vector.y * m[7] + vector.z * m[11] + vector.w * m[15]);
-
-#endif
-*/
     MathUtil::transformVectorMatrix(m, (const float*) &vector, (float*)dst);
 }
 
@@ -1220,32 +891,7 @@ void Matrix::transpose(Matrix* dst) const
 {
     GP_ASSERT(dst);
 
-#ifdef USE_NEON
-    
-    asm volatile(
-    	"vld4.32 {d0[0], d2[0], d4[0], d6[0]}, [%0]! \n\t" // DST->M[m0, m4, m8, m12] = M[m0-m3]
-		"vld4.32 {d0[1], d2[1], d4[1], d6[1]}, [%0]! \n\t" // DST->M[m1, m5, m9, m12] = M[m4-m7]
-		"vld4.32 {d1[0], d3[0], d5[0], d7[0]}, [%0]! \n\t" // DST->M[m2, m6, m10, m12] = M[m8-m11]
-		"vld4.32 {d1[1], d3[1], d5[1], d7[1]}, [%0]  \n\t" // DST->M[m3, m7, m11, m12] = M[m12-m15]
-
-		"vst1.32 {q0-q1}, [%1]! \n\t" // DST->M[m0-m7]
-		"vst1.32 {q2-q3}, [%1] \n\t"  // DST->M[m8-m15]
-    	:
-    	: "r"(this->m), "r"(dst->m)
-    	: "q0", "q1", "q2", "q3", "memory"
-    );
-
-#else
-
-    float t[16] = {
-        m[0], m[4], m[8], m[12],
-        m[1], m[5], m[9], m[13],
-        m[2], m[6], m[10], m[14],
-        m[3], m[7], m[11], m[15]
-    };
-    memcpy(dst->m, t, MATRIX_SIZE);
-
-#endif
+    MathUtil::transpose(m, dst->m);
 }
 
 }

+ 24 - 13
gameplay/src/Node.cpp

@@ -1042,27 +1042,38 @@ PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
     SAFE_DELETE(_collisionObject);
 
     // Check if the properties is valid.
-    if (!properties || 
-        !(strcmp(properties->getNamespace(), "character") == 0 || 
-        strcmp(properties->getNamespace(), "ghostObject") == 0 || 
-        strcmp(properties->getNamespace(), "rigidBody") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load collision object from properties object: must be non-null object and have namespace equal to 'character', 'ghostObject', or 'rigidBody'.");
+        GP_ERROR("Failed to load collision object from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
         return NULL;
     }
 
-    if (strcmp(properties->getNamespace(), "character") == 0)
+    if (const char* type = properties->getString("type"))
     {
-        _collisionObject = PhysicsCharacter::create(this, properties);
-    }
-    else if (strcmp(properties->getNamespace(), "ghostObject") == 0)
-    {
-        _collisionObject = PhysicsGhostObject::create(this, properties);
+        if (strcmp(type, "CHARACTER") == 0)
+        {
+            _collisionObject = PhysicsCharacter::create(this, properties);
+        }
+        else if (strcmp(type, "GHOST_OBJECT") == 0)
+        {
+            _collisionObject = PhysicsGhostObject::create(this, properties);
+        }
+        else if (strcmp(type, "RIGID_BODY") == 0)
+        {
+            _collisionObject = PhysicsRigidBody::create(this, properties);
+        }
+        else
+        {
+            GP_ERROR("Unsupported collision object type '%s'.", type);
+            return NULL;
+        }
     }
-    else if (strcmp(properties->getNamespace(), "rigidBody") == 0)
+    else
     {
-        _collisionObject = PhysicsRigidBody::create(this, properties);
+        GP_ERROR("Failed to load collision object from properties object; required attribute 'type' is missing.");
+        return NULL;
     }
+    
     return _collisionObject;
 }
 

+ 1 - 1
gameplay/src/ParticleEmitter.cpp

@@ -795,7 +795,7 @@ void ParticleEmitter::update(long elapsedTime)
     }
 
     // Calculate the time passed since last update.
-    float elapsedSecs = (float)elapsedTime / 1000.0f;
+    float elapsedSecs = (float)elapsedTime * 0.001f;
 
     if (_started && _emissionRate)
     {

+ 15 - 2
gameplay/src/PhysicsCharacter.cpp

@@ -91,9 +91,22 @@ PhysicsCharacter::~PhysicsCharacter()
 PhysicsCharacter* PhysicsCharacter::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
-    if (!properties || !(strcmp(properties->getNamespace(), "character") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load physics character from properties object: must be non-null object and have namespace equal to 'character'.");
+        GP_ERROR("Failed to load physics character from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
+        return NULL;
+    }
+
+    // Check that the type is specified and correct.
+    const char* type = properties->getString("type");
+    if (!type)
+    {
+        GP_ERROR("Failed to load physics character from properties object; required attribute 'type' is missing.");
+        return NULL;
+    }
+    if (strcmp(type, "CHARACTER") != 0)
+    {
+        GP_ERROR("Failed to load physics character from properties object; attribute 'type' must be equal to 'CHARACTER'.");
         return NULL;
     }
 

+ 14 - 17
gameplay/src/PhysicsCollisionShape.cpp

@@ -124,12 +124,9 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
     GP_ASSERT(node);
 
     // Check if the properties is valid and has a valid namespace.
-    if (!properties || 
-        !(strcmp(properties->getNamespace(), "character") == 0 || 
-        strcmp(properties->getNamespace(), "ghostObject") == 0 || 
-        strcmp(properties->getNamespace(), "rigidBody") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load physics collision shape from properties object: must be non-null object and have namespace equal to 'character', 'ghostObject', or 'rigidBody'.");
+        GP_ERROR("Failed to load physics collision shape from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
         return NULL;
     }
 
@@ -141,33 +138,33 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
     float height = -1.0f;
     bool centerIsAbsolute = false;
     const char* imagePath = NULL;
-    bool typeSpecified = false;
+    bool shapeSpecified = false;
 
     // Load the defined properties.
     properties->rewind();
     const char* name;
     while (name = properties->getNextProperty())
     {
-        if (strcmp(name, "type") == 0)
+        if (strcmp(name, "shape") == 0)
         {
-            std::string typeStr = properties->getString();
-            if (typeStr == "BOX")
+            std::string shapeStr = properties->getString();
+            if (shapeStr == "BOX")
                 type = SHAPE_BOX;
-            else if (typeStr == "SPHERE")
+            else if (shapeStr == "SPHERE")
                 type = SHAPE_SPHERE;
-            else if (typeStr == "MESH")
+            else if (shapeStr == "MESH")
                 type = SHAPE_MESH;
-            else if (typeStr == "HEIGHTFIELD")
+            else if (shapeStr == "HEIGHTFIELD")
                 type = SHAPE_HEIGHTFIELD;
-            else if (typeStr == "CAPSULE")
+            else if (shapeStr == "CAPSULE")
                 type = SHAPE_CAPSULE;
             else
             {
-                GP_ERROR("Could not create physics collision shape; unsupported value for collision shape type: '%s'.", typeStr.c_str());
+                GP_ERROR("Could not create physics collision shape; unsupported value for collision shape type: '%s'.", shapeStr.c_str());
                 return NULL;
             }
 
-            typeSpecified = true;
+            shapeSpecified = true;
         }
         else if (strcmp(name, "image") == 0)
         {
@@ -201,9 +198,9 @@ PhysicsCollisionShape::Definition* PhysicsCollisionShape::Definition::create(Nod
         }
     }
 
-    if (!typeSpecified)
+    if (!shapeSpecified)
     {
-        GP_ERROR("Missing 'type' specifier for collision shape definition.");
+        GP_ERROR("Missing 'shape' specifier for collision shape definition.");
         return NULL;
     }
 

+ 22 - 0
gameplay/src/PhysicsController.cpp

@@ -45,6 +45,22 @@ void PhysicsController::addStatusListener(Listener* listener)
     _listeners->push_back(listener);
 }
 
+void PhysicsController::removeStatusListener(Listener* listener)
+{
+    GP_ASSERT(listener);
+    if (!_listeners)
+        return;
+
+    for (std::vector<Listener*>::iterator iter = _listeners->begin(); iter != _listeners->end(); iter++)
+    {
+        if (*iter == listener)
+        {
+            _listeners->erase(iter);
+            return;
+        }
+    }
+}
+
 PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
     checkConstraintRigidBodies(a, b);
@@ -1350,4 +1366,10 @@ int PhysicsController::DebugDrawer::getDebugMode() const
     return _mode;
 }
 
+PhysicsController::Listener::~Listener()
+{
+    GP_ASSERT(Game::getInstance()->getPhysicsController());
+    Game::getInstance()->getPhysicsController()->removeStatusListener(this);
+}
+
 }

+ 14 - 0
gameplay/src/PhysicsController.h

@@ -54,6 +54,13 @@ public:
          * Handles when a physics world status event occurs.
          */
         virtual void statusEvent(EventType type) = 0;
+
+    protected:
+
+        /**
+         * Destructor.
+         */
+        virtual ~Listener();
     };
 
     /**
@@ -89,6 +96,13 @@ public:
      */
     void addStatusListener(PhysicsController::Listener* listener);
 
+    /**
+     * Removes a listener to the physics controller.
+     * 
+     * @param listener The listener to remove.
+     */
+    void removeStatusListener(Listener* listener);
+
     /**
      * Creates a fixed constraint.
      * 

+ 15 - 2
gameplay/src/PhysicsGhostObject.cpp

@@ -47,9 +47,22 @@ PhysicsGhostObject::~PhysicsGhostObject()
 PhysicsGhostObject* PhysicsGhostObject::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
-    if (!properties || !(strcmp(properties->getNamespace(), "ghostObject") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load ghost object from properties object: must be non-null object and have namespace equal to 'ghost'.");
+        GP_ERROR("Failed to load ghost object from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
+        return NULL;
+    }
+
+    // Check that the type is specified and correct.
+    const char* type = properties->getString("type");
+    if (!type)
+    {
+        GP_ERROR("Failed to load ghost object from properties object; required attribute 'type' is missing.");
+        return NULL;
+    }
+    if (strcmp(type, "GHOST_OBJECT") != 0)
+    {
+        GP_ERROR("Failed to load ghost object from properties object; attribute 'type' must be equal to 'GHOST_OBJECT'.");
         return NULL;
     }
 

+ 23 - 6
gameplay/src/PhysicsRigidBody.cpp

@@ -11,7 +11,7 @@ namespace gameplay
 {
 
 PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Definition& shape, const Parameters& parameters)
-        : PhysicsCollisionObject(node), _body(NULL), _mass(parameters.mass), _constraints(NULL)
+        : PhysicsCollisionObject(node), _body(NULL), _mass(parameters.mass), _constraints(NULL), _inDestructor(false)
 {
     GP_ASSERT(Game::getInstance()->getPhysicsController());
     GP_ASSERT(_node);
@@ -64,6 +64,7 @@ PhysicsRigidBody::~PhysicsRigidBody()
     GP_ASSERT(_node);
 
     // Clean up all constraints linked to this rigid body.
+    _inDestructor = true;
     if (_constraints)
     {
         for (unsigned int i = 0; i < _constraints->size(); ++i)
@@ -73,13 +74,13 @@ PhysicsRigidBody::~PhysicsRigidBody()
         SAFE_DELETE(_constraints);
     }
 
-    // Remove collision object from physics controller
+    // Remove collision object from physics controller.
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
     // Clean up the rigid body and its related objects.
     SAFE_DELETE(_body);
 
-    // Unregister node listener (only registered for heihgtfield collision shape types)
+    // Unregister node listener (only registered for heihgtfield collision shape types).
     if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
         _node->removeListener(this);
@@ -155,9 +156,22 @@ void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
 PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
 {
     // Check if the properties is valid and has a valid namespace.
-    if (!properties || !(strcmp(properties->getNamespace(), "rigidBody") == 0))
+    if (!properties || !(strcmp(properties->getNamespace(), "collisionObject") == 0))
     {
-        GP_ERROR("Failed to load rigid body from properties object: must be non-null object and have namespace equal to 'rigidBody'.");
+        GP_ERROR("Failed to load rigid body from properties object: must be non-null object and have namespace equal to 'collisionObject'.");
+        return NULL;
+    }
+
+    // Check that the type is specified and correct.
+    const char* type = properties->getString("type");
+    if (!type)
+    {
+        GP_ERROR("Failed to load physics rigid body from properties object; required attribute 'type' is missing.");
+        return NULL;
+    }
+    if (strcmp(type, "RIGID_BODY") != 0)
+    {
+        GP_ERROR("Failed to load physics rigid body from properties object; attribute 'type' must be equal to 'RIGID_BODY'.");
         return NULL;
     }
 
@@ -290,7 +304,10 @@ void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
 
 void PhysicsRigidBody::removeConstraint(PhysicsConstraint* constraint)
 {
-    if (_constraints)
+    // Ensure that the rigid body has constraints and that we are
+    // not currently in the rigid body's destructor (in this case,
+    // the constraints will be destroyed from there).
+    if (_constraints && !_inDestructor)
     {
         for (unsigned int i = 0; i < _constraints->size(); ++i)
         {

+ 1 - 0
gameplay/src/PhysicsRigidBody.h

@@ -314,6 +314,7 @@ private:
     btRigidBody* _body;
     float _mass;
     std::vector<PhysicsConstraint*>* _constraints;
+    bool _inDestructor;
 
 };
 

+ 75 - 0
gameplay/src/Properties.cpp

@@ -145,6 +145,7 @@ void Properties::readProperties(FILE* file)
     char* parentID;
     char* rc;
     char* rcc;
+    char* rccc;
 
     while (true)
     {
@@ -205,6 +206,9 @@ void Properties::readProperties(FILE* file)
             {
                 parentID = NULL;
 
+                // Get the last character on the line (ignoring whitespace).
+                const char* lineEnd = trimWhiteSpace(line) + (strlen(trimWhiteSpace(line)) - 1);
+
                 // This line might begin or end a namespace,
                 // or it might be a key/value pair without '='.
 
@@ -213,6 +217,9 @@ void Properties::readProperties(FILE* file)
 
                 // Check for inheritance: ':'
                 rcc = strchr(line, ':');
+
+                // Check for '}' on same line.
+                rccc = strchr(line, '}');
             
                 // Get the name of the namespace.
                 name = strtok(line, " \t\n{");
@@ -231,6 +238,8 @@ void Properties::readProperties(FILE* file)
                 // Get its ID if it has one.
                 value = strtok(NULL, ":{");
                 value = trimWhiteSpace(value);
+
+                // Get its parent ID if it has one.
                 if (rcc != NULL)
                 {
                     parentID = strtok(NULL, "{");
@@ -239,18 +248,84 @@ void Properties::readProperties(FILE* file)
 
                 if (value != NULL && value[0] == '{')
                 {
+                    // If the namespace ends on this line, seek back to right before the '}' character.
+                    if (rccc && rccc == lineEnd)
+                    {
+                        if (fseek(file, -1, SEEK_CUR) != 0)
+                        {
+                            GP_ERROR("Failed to seek back to before a '}' character in properties file.");
+                            return;
+                        }
+                        while (fgetc(file) != '}')
+                        {
+                            if (fseek(file, -2, SEEK_CUR) != 0)
+                            {
+                                GP_ERROR("Failed to seek back to before a '}' character in properties file.");
+                                return;
+                            }
+                        }
+                        if (fseek(file, -1, SEEK_CUR) != 0)
+                        {
+                            GP_ERROR("Failed to seek back to before a '}' character in properties file.");
+                            return;
+                        }
+                    }
+
                     // New namespace without an ID.
                     Properties* space = new Properties(file, name, NULL, parentID);
                     _namespaces.push_back(space);
+
+                    // If the namespace ends on this line, seek to right after the '}' character.
+                    if (rccc && rccc == lineEnd)
+                    {
+                        if (fseek(file, 1, SEEK_CUR) != 0)
+                        {
+                            GP_ERROR("Failed to seek to immediately after a '}' character in properties file.");
+                            return;
+                        }
+                    }
                 }
                 else
                 {
                     // If '{' appears on the same line.
                     if (rc != NULL)
                     {
+                        // If the namespace ends on this line, seek back to right before the '}' character.
+                        if (rccc && rccc == lineEnd)
+                        {
+                            if (fseek(file, -1, SEEK_CUR) != 0)
+                            {
+                                GP_ERROR("Failed to seek back to before a '}' character in properties file.");
+                                return;
+                            }
+                            while (fgetc(file) != '}')
+                            {
+                                if (fseek(file, -2, SEEK_CUR) != 0)
+                                {
+                                    GP_ERROR("Failed to seek back to before a '}' character in properties file.");
+                                    return;
+                                }
+                            }
+                            if (fseek(file, -1, SEEK_CUR) != 0)
+                            {
+                                GP_ERROR("Failed to seek back to before a '}' character in properties file.");
+                                return;
+                            }
+                        }
+
                         // Create new namespace.
                         Properties* space = new Properties(file, name, value, parentID);
                         _namespaces.push_back(space);
+
+                        // If the namespace ends on this line, seek to right after the '}' character.
+                        if (rccc && rccc == lineEnd)
+                        {
+                            if (fseek(file, 1, SEEK_CUR) != 0)
+                            {
+                                GP_ERROR("Failed to seek to immediately after a '}' character in properties file.");
+                                return;
+                            }
+                        }
                     }
                     else
                     {

+ 116 - 245
gameplay/src/SceneLoader.cpp

@@ -6,13 +6,19 @@
 namespace gameplay
 {
 
-std::map<std::string, Properties*> SceneLoader::_propertiesFromFile;
+std::map<std::string, Properties*> SceneLoader::_properties;
 std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
 std::vector<SceneLoader::SceneNode> SceneLoader::_sceneNodes;
+std::string SceneLoader::_gpbPath;
 std::string SceneLoader::_path;
 
 Scene* SceneLoader::load(const char* url)
 {
+    // Get the file part of the url that we are loading the scene from.
+    std::string urlStr = url ? url : "";
+    std::string id;
+    splitURL(urlStr, &_path, &id);
+
     // Load the scene properties from file.
     Properties* properties = Properties::create(url);
     if (properties == NULL)
@@ -31,9 +37,9 @@ Scene* SceneLoader::load(const char* url)
     }
 
     // Get the path to the main GPB.
-    _path = sceneProperties->getString("path");
+    _gpbPath = sceneProperties->getString("path");
 
-    // Build the node URL/property and animation reference tables and load the referenced files.
+    // Build the node URL/property and animation reference tables and load the referenced files/store the inline properties objects.
     buildReferenceTables(sceneProperties);
     loadReferencedFiles();
 
@@ -61,7 +67,7 @@ Scene* SceneLoader::load(const char* url)
         SceneNodeProperty::TRANSLATE | 
         SceneNodeProperty::TRANSPARENT |
         SceneNodeProperty::DYNAMIC);
-    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::CHARACTER | SceneNodeProperty::GHOSTOBJECT | SceneNodeProperty::RIGIDBODY);
+    applyNodeProperties(scene, sceneProperties, SceneNodeProperty::COLLISION_OBJECT);
     createAnimations(scene);
 
     // Find the physics properties object.
@@ -83,17 +89,18 @@ Scene* SceneLoader::load(const char* url)
         loadPhysics(physics, scene);
 
     // Clean up all loaded properties objects.
-    std::map<std::string, Properties*>::iterator iter = _propertiesFromFile.begin();
-    for (; iter != _propertiesFromFile.end(); iter++)
+    std::map<std::string, Properties*>::iterator iter = _properties.begin();
+    for (; iter != _properties.end(); iter++)
     {
-        SAFE_DELETE(iter->second);
+        if (iter->first.find(_path) == iter->first.npos)
+            SAFE_DELETE(iter->second);
     }
 
     // Clean up the .scene file's properties object.
     SAFE_DELETE(properties);
 
     // Clear all temporary data stores.
-    _propertiesFromFile.clear();
+    _properties.clear();
     _animations.clear();
     _sceneNodes.clear();
 
@@ -102,46 +109,28 @@ Scene* SceneLoader::load(const char* url)
 
 void SceneLoader::addSceneAnimation(const char* animationID, const char* targetID, const char* url)
 {
-    // Calculate the file and id from the given url.
-    std::string file;
-    std::string id;
-    splitURL(url, &file, &id);
+    std::string urlStr = url ? url : "";
 
     // If there is a file that needs to be loaded later, add an 
     // empty entry to the properties table to signify it.
-    if (file.length() > 0 && _propertiesFromFile.count(file) == 0)
-        _propertiesFromFile[file] = NULL;
+    if (urlStr.length() > 0 && _properties.count(urlStr) == 0)
+        _properties[urlStr] = NULL;
 
     // Add the animation to the list of animations to be resolved later.
-    _animations.push_back(SceneAnimation(animationID, targetID, file, id));
+    _animations.push_back(SceneAnimation(animationID, targetID, urlStr));
 }
 
 void SceneLoader::addSceneNodeProperty(SceneNode& sceneNode, SceneNodeProperty::Type type, const char* url, int index)
 {
-    // Calculate the file and id from the given url.
-    std::string file;
-    std::string id;
-    splitURL(url, &file, &id);
+    std::string urlStr = url ? url : "";
 
     // 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 (file.length() > 0 && file.find(".gpb") == file.npos && _propertiesFromFile.count(file) == 0)
-        _propertiesFromFile[file] = NULL;
-
-    SceneNodeProperty prop(type, file, id, index);
-
-    // Parse for wildcharacter character (only supported on the URL attribute)
-    if (type == SceneNodeProperty::URL)
-    {
-        if (id.length() > 1 && id.at(id.length()-1) == '*')
-        {
-            prop._id = id.substr(0, id.length()-1);
-            sceneNode._exactMatch = false;
-        }
-    }
+    if (urlStr.length() > 0 && 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.
-    sceneNode._properties.push_back(prop);
+    sceneNode._properties.push_back(SceneNodeProperty(type, urlStr, index));
 }
 
 void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* sceneProperties, unsigned int typeFlags)
@@ -151,43 +140,19 @@ void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* scen
         SceneNode& sceneNode = _sceneNodes[i];
         GP_ASSERT(sceneNode._nodeID);
 
-        if (sceneNode._exactMatch)
+        // Find the node matching the specified ID.
+        Node* node = scene->findNode(sceneNode._nodeID);
+        if (!node)
         {
-            // Find the node matching the specified ID exactly.
-            Node* node = scene->findNode(sceneNode._nodeID);
-            if (!node)
-            {
-                GP_ERROR("Failed to set property for node '%s', which does not exist in the scene.", sceneNode._nodeID);
-                continue;
-            }
-
-            for (unsigned int j = 0, pcount = sceneNode._properties.size(); j < pcount; ++j)
-            {
-                SceneNodeProperty& snp = sceneNode._properties[j];
-                if (typeFlags & snp._type)
-                    applyNodeProperty(sceneNode, node, sceneProperties, snp, scene);
-            }
+            GP_ERROR("Failed to set property for node '%s', which does not exist in the scene.", sceneNode._nodeID);
+            continue;
         }
-        else
-        {
-            // Find all nodes matching the specified ID.
-            std::vector<Node*> nodes;
-            unsigned int nodeCount = scene->findNodes(sceneNode._nodeID, nodes, true, false);
-            if (nodeCount == 0)
-            {
-                GP_ERROR("Failed to set property for nodes with id matching '%s'; no such nodes exist in the scene.", sceneNode._nodeID);
-                continue;
-            }
-            
-            for (unsigned int j = 0, pcount = sceneNode._properties.size(); j < pcount; ++j)
-            {
-                SceneNodeProperty& snp = sceneNode._properties[j];
-                if ((typeFlags & snp._type) == 0)
-                    continue;
 
-                for (unsigned int k = 0; k < nodeCount; ++k)
-                    applyNodeProperty(sceneNode, nodes[k], sceneProperties, snp, scene);
-            }
+        for (unsigned int j = 0, pcount = sceneNode._properties.size(); j < pcount; ++j)
+        {
+            SceneNodeProperty& snp = sceneNode._properties[j];
+            if (typeFlags & snp._type)
+                applyNodeProperty(sceneNode, node, sceneProperties, snp, scene);
         }
     }
 }
@@ -197,35 +162,16 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
     if (snp._type == SceneNodeProperty::AUDIO ||
         snp._type == SceneNodeProperty::MATERIAL ||
         snp._type == SceneNodeProperty::PARTICLE ||
-        snp._type == SceneNodeProperty::CHARACTER ||
-        snp._type == SceneNodeProperty::GHOSTOBJECT ||
-        snp._type == SceneNodeProperty::RIGIDBODY)
+        snp._type == SceneNodeProperty::COLLISION_OBJECT)
     {
         // Check to make sure the referenced properties object was loaded properly.
-        Properties* p = _propertiesFromFile[snp._file];
+        Properties* p = _properties[snp._url];
         if (!p)
         {
-            GP_ERROR("The referenced node data in file '%s' failed to load.", snp._file.c_str());
+            GP_ERROR("The referenced node data at url '%s' failed to load.", snp._url.c_str());
             return;
         }
 
-        // If a specific namespace within the file was specified, load that namespace.
-        if (snp._id.size() > 0)
-        {
-            p = p->getNamespace(snp._id.c_str());
-            if (!p)
-            {
-                GP_ERROR("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
-                return;
-            }
-        }
-        else
-        {
-            // Otherwise, use the first namespace.
-            p->rewind();
-            p = p->getNextNamespace();
-        }
-
         switch (snp._type)
         {
         case SceneNodeProperty::AUDIO:
@@ -255,49 +201,12 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
             SAFE_RELEASE(particleEmitter);
             break;
         }
-        case SceneNodeProperty::CHARACTER:
-        case SceneNodeProperty::GHOSTOBJECT:
-        case SceneNodeProperty::RIGIDBODY:
+        case SceneNodeProperty::COLLISION_OBJECT:
         {
-            // Check to make sure the referenced properties object was loaded properly.
-            Properties* p = _propertiesFromFile[snp._file];
-            if (!p)
-            {
-                GP_ERROR("The referenced node data in file '%s' failed to load.", snp._file.c_str());
-                return;
-            }
-
-            // If a specific namespace within the file was specified, load that namespace.
-            if (snp._id.size() > 0)
-            {
-                p = p->getNamespace(snp._id.c_str());
-                if (!p)
-                {
-                    GP_ERROR("The referenced node data at '%s#%s' failed to load.", snp._file.c_str(), snp._id.c_str());
-                    return;
-                }
-            }
-            else
-            {
-                // Otherwise, use the first namespace.
-                p->rewind();
-                p = p->getNextNamespace();
-            }
-
             // Check to make sure the type of the namespace used to load the physics collision object is correct.
-            if (snp._type == SceneNodeProperty::CHARACTER && strcmp(p->getNamespace(), "character") != 0)
-            {
-                GP_ERROR("Attempting to set a 'character' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
-                return;
-            }
-            else if (snp._type == SceneNodeProperty::GHOSTOBJECT && strcmp(p->getNamespace(), "ghostObject") != 0)
+            if (snp._type == SceneNodeProperty::COLLISION_OBJECT && strcmp(p->getNamespace(), "collisionObject") != 0)
             {
-                GP_ERROR("Attempting to set a 'ghostObject' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
-                return;
-            }
-            else if (snp._type == SceneNodeProperty::RIGIDBODY && strcmp(p->getNamespace(), "rigidBody") != 0)
-            {
-                GP_ERROR("Attempting to set a 'rigidBody' (physics collision object attribute) on a node using a '%s' definition.", p->getNamespace());
+                GP_ERROR("Attempting to set a physics collision object on a node using a '%s' definition.", p->getNamespace());
                 return;
             }
             else
@@ -416,48 +325,24 @@ void SceneLoader::applyNodeUrls(Scene* scene)
             if (snp._type != SceneNodeProperty::URL)
                 continue;
 
-            if (snp._file.empty())
+            std::string file;
+            std::string id;
+            splitURL(snp._url, &file, &id);
+
+            if (file.empty())
             {
                 // The node is from the main GPB and should just be renamed.
 
                 // TODO: Should we do all nodes with this case first to allow users to stitch in nodes with
                 // IDs equal to IDs that were in the original GPB file but were changed in the scene file?
-                if (sceneNode._exactMatch)
+                Node* node = scene->findNode(id.c_str());
+                if (node)
                 {
-                    Node* node = scene->findNode(snp._id.c_str());
-                    if (node)
-                    {
-                        node->setId(sceneNode._nodeID);
-                    }
-                    else
-                    {
-                        GP_ERROR("Could not find node '%s' in main scene GPB file.", snp._id.c_str());
-                    }
+                    node->setId(sceneNode._nodeID);
                 }
                 else
                 {
-                    // Search for nodes using a partial match
-                    std::string partialMatch = snp._id;
-                    std::vector<Node*> nodes;
-                    unsigned int nodeCount = scene->findNodes(snp._id.c_str(), nodes, true, false);
-                    if (nodeCount > 0)
-                    {
-                        GP_ASSERT(sceneNode._nodeID);
-
-                        for (unsigned int k = 0; k < nodeCount; ++k)
-                        {
-                            // Construct a new node ID using _nodeID plus the remainder of the partial match.
-                            Node* node = nodes[k];
-                            GP_ASSERT(node);
-                            std::string newID(sceneNode._nodeID);
-                            newID += (node->getId() + snp._id.length());
-                            node->setId(newID.c_str());
-                        }
-                    }
-                    else
-                    {
-                        GP_ERROR("Could not find any nodes matching '%s' in main scene GPB file.", snp._id.c_str());
-                    }
+                    GP_ERROR("Could not find node '%s' in main scene GPB file.", id.c_str());
                 }
             }
             else
@@ -467,60 +352,26 @@ void SceneLoader::applyNodeUrls(Scene* scene)
                 // TODO: Revisit this to determine if we should cache Bundle objects for the duration of the scene
                 // load to prevent constantly creating/destroying the same externally referenced bundles each time
                 // a url with a file is encountered.
-                Bundle* tmpBundle = Bundle::create(snp._file.c_str());
+                Bundle* tmpBundle = Bundle::create(file.c_str());
                 if (tmpBundle)
                 {
-                    if (sceneNode._exactMatch)
+                    Node* node = tmpBundle->loadNode(id.c_str(), scene);
+                    if (node)
                     {
-                        Node* node = tmpBundle->loadNode(snp._id.c_str(), scene);
-                        if (node)
-                        {
-                            node->setId(sceneNode._nodeID);
-                            scene->addNode(node);
-                            SAFE_RELEASE(node);
-                        }
-                        else
-                        {
-                            GP_ERROR("Could not load node '%s' from GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
-                        }
+                        node->setId(sceneNode._nodeID);
+                        scene->addNode(node);
+                        SAFE_RELEASE(node);
                     }
                     else
                     {
-                        // Search for nodes in the bundle using a partial match
-                        std::string partialMatch = snp._id;
-                        unsigned int objectCount = tmpBundle->getObjectCount();
-                        unsigned int matchCount = 0;
-                        for (unsigned int k = 0; k < objectCount; ++k)
-                        {
-                            const char* objid = tmpBundle->getObjectID(k);
-                            if (strstr(objid, snp._id.c_str()) == objid)
-                            {
-                                // This object ID matches (starts with).
-                                // Try to load this object as a Node.
-                                Node* node = tmpBundle->loadNode(objid);
-                                if (node)
-                                {
-                                    // Construct a new node ID using _nodeID plus the remainder of the partial match.
-                                    std::string newID(sceneNode._nodeID);
-                                    newID += (node->getId() + snp._id.length());
-                                    node->setId(newID.c_str());
-                                    scene->addNode(node);
-                                    SAFE_RELEASE(node);
-                                    matchCount++;
-                                }
-                            }
-                        }
-                        if (matchCount == 0)
-                        {
-                            GP_ERROR("Could not find any nodes matching '%s' in GPB file '%s'.", snp._id.c_str(), snp._file.c_str());
-                        }
+                        GP_ERROR("Could not load node '%s' from GPB file '%s'.", id.c_str(), file.c_str());
                     }
 
                     SAFE_RELEASE(tmpBundle);
                 }
                 else
                 {
-                    GP_ERROR("Failed to load GPB file '%s' for node stitching.", snp._file.c_str());
+                    GP_ERROR("Failed to load GPB file '%s' for node stitching.", file.c_str());
                 }
             }
 
@@ -550,6 +401,42 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
             SceneNode& sceneNode = _sceneNodes[_sceneNodes.size()-1];
             sceneNode._nodeID = ns->getId();
 
+            // Parse the node's sub-namespaces.
+            Properties* subns;
+            std::string propertyUrl = _path + "#" + ns->getId() + "/";
+            while ((subns = ns->getNextNamespace()) != NULL)
+            {
+                if (strcmp(subns->getNamespace(), "audio") == 0)
+                {
+                    propertyUrl += "audio/" + std::string(subns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::AUDIO, propertyUrl.c_str());
+                    _properties[propertyUrl] = subns;
+                }
+                else if (strcmp(subns->getNamespace(), "material") == 0)
+                {
+                    propertyUrl += "material/" + std::string(subns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::MATERIAL, propertyUrl.c_str());
+                    _properties[propertyUrl] = subns;
+                }
+                else if (strcmp(subns->getNamespace(), "particle") == 0)
+                {
+                    propertyUrl += "particle/" + std::string(subns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::PARTICLE, propertyUrl.c_str());
+                    _properties[propertyUrl] = subns;
+                }
+                else if (strcmp(subns->getNamespace(), "collisionObject") == 0)
+                {
+                    propertyUrl += "collisionObject/" + std::string(subns->getId());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::COLLISION_OBJECT, propertyUrl.c_str());
+                    _properties[propertyUrl] = subns;
+                }
+                else
+                {
+                    GP_ERROR("Unsupported child namespace '%s' of 'node' namespace.", subns->getNamespace());
+                }
+            }
+
+            // Parse the node's attributes.
             while ((name = ns->getNextProperty()) != NULL)
             {
                 if (strcmp(name, "url") == 0)
@@ -576,17 +463,9 @@ void SceneLoader::buildReferenceTables(Properties* sceneProperties)
                 {
                     addSceneNodeProperty(sceneNode, SceneNodeProperty::PARTICLE, ns->getString());
                 }
-                else if (strcmp(name, "character") == 0)
-                {
-                    addSceneNodeProperty(sceneNode, SceneNodeProperty::CHARACTER, ns->getString());
-                }
-                else if (strcmp(name, "ghostObject") == 0)
+                else if (strcmp(name, "collisionObject") == 0)
                 {
-                    addSceneNodeProperty(sceneNode, SceneNodeProperty::GHOSTOBJECT, ns->getString());
-                }
-                else if (strcmp(name, "rigidBody") == 0)
-                {
-                    addSceneNodeProperty(sceneNode, SceneNodeProperty::RIGIDBODY, ns->getString());
+                    addSceneNodeProperty(sceneNode, SceneNodeProperty::COLLISION_OBJECT, ns->getString());
                 }
                 else if (strcmp(name, "rigidBodyModel") == 0)
                 {
@@ -682,21 +561,12 @@ void SceneLoader::createAnimations(const Scene* scene)
         }
 
         // Check to make sure the referenced properties object was loaded properly.
-        Properties* p = _propertiesFromFile[_animations[i]._file];
+        Properties* p = _properties[_animations[i]._url];
         if (!p)
         {
-            GP_ERROR("The referenced animation data in file '%s' failed to load.", _animations[i]._file.c_str());
+            GP_ERROR("The referenced animation data at url '%s' failed to load.", _animations[i]._url.c_str());
             continue;
         }
-        if (_animations[i]._id.size() > 0)
-        {
-            p = p->getNamespace(_animations[i]._id.c_str());
-            if (!p)
-            {
-                GP_ERROR("The referenced animation data at '%s#%s' failed to load.", _animations[i]._file.c_str(), _animations[i]._id.c_str());
-                continue;
-            }
-        }
 
         node->createAnimation(_animations[i]._animationID, p);
     }
@@ -811,10 +681,10 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
     GP_ASSERT(sceneProperties);
 
     // Load the main scene from the specified path.
-    Bundle* bundle = Bundle::create(_path.c_str());
+    Bundle* bundle = Bundle::create(_gpbPath.c_str());
     if (!bundle)
     {
-        GP_ERROR("Failed to load scene GPB file '%s'.", _path.c_str());
+        GP_ERROR("Failed to load scene GPB file '%s'.", _gpbPath.c_str());
         return NULL;
     }
 
@@ -822,7 +692,7 @@ Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
     Scene* scene = bundle->loadScene(NULL);
     if (!scene)
     {
-        GP_ERROR("Failed to load scene from '%s'.", _path.c_str());
+        GP_ERROR("Failed to load scene from '%s'.", _gpbPath.c_str());
         SAFE_RELEASE(bundle);
         return NULL;
     }
@@ -952,14 +822,17 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
 void SceneLoader::loadReferencedFiles()
 {
     // Load all referenced properties files.
-    std::map<std::string, Properties*>::iterator iter = _propertiesFromFile.begin();
-    for (; iter != _propertiesFromFile.end(); iter++)
+    std::map<std::string, Properties*>::iterator iter = _properties.begin();
+    for (; iter != _properties.end(); iter++)
     {
-        Properties* p = Properties::create(iter->first.c_str());
-        if (p == NULL)
-            GP_ERROR("Failed to load referenced file '%s'.", iter->first.c_str());
+        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());
 
-        iter->second = p;
+            iter->second = p;
+        }
     }
 }
 
@@ -1065,28 +938,26 @@ PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constrain
     return physicsConstraint;
 }
 
-void SceneLoader::splitURL(const char* url, std::string* file, std::string* id)
+void SceneLoader::splitURL(const std::string& url, std::string* file, std::string* id)
 {
-    if (!url)
+    if (url.empty())
     {
         // This is allowed since many scene node properties do not use the URL.
         return;
     }
 
-    std::string urlString = url;
-
     // Check if the url references a file (otherwise, it only references a node within the main GPB).
-    unsigned int loc = urlString.rfind(".");
-    if (loc != urlString.npos)
+    unsigned int loc = url.rfind(".");
+    if (loc != url.npos)
     {
         // If the url references a specific namespace within the file,
         // set the id out parameter appropriately. Otherwise, set the id out
         // parameter to the empty string so we know to load the first namespace.
-        loc = urlString.rfind("#");
-        if (loc != urlString.npos)
+        loc = url.rfind("#");
+        if (loc != url.npos)
         {
-            *file = urlString.substr(0, loc);
-            *id = urlString.substr(loc + 1);
+            *file = url.substr(0, loc);
+            *id = url.substr(loc + 1);
         }
         else
         {

+ 17 - 21
gameplay/src/SceneLoader.h

@@ -33,13 +33,12 @@ private:
      */
     struct SceneAnimation
     {
-        SceneAnimation(const char* animationID, const char* targetID, std::string file, std::string id)
-            : _animationID(animationID), _targetID(targetID), _file(file), _id(id) {}
+        SceneAnimation(const char* animationID, const char* targetID, std::string url)
+            : _animationID(animationID), _targetID(targetID), _url(url) {}
 
         const char* _animationID;
         const char* _targetID;
-        std::string _file;
-        std::string _id;
+        std::string _url;
     };
 
     struct SceneNodeProperty
@@ -49,31 +48,27 @@ private:
             AUDIO = 1,
             MATERIAL = 2,
             PARTICLE = 4,
-            CHARACTER = 8,
-            GHOSTOBJECT = 16,
-            RIGIDBODY = 32,
-            TRANSLATE = 64,
-            ROTATE = 128,
-            SCALE = 256,
-            URL = 512,
-            TRANSPARENT = 1024,
-            DYNAMIC = 2048
+            COLLISION_OBJECT = 8,
+            TRANSLATE = 16,
+            ROTATE = 32,
+            SCALE = 64,
+            URL = 128,
+            TRANSPARENT = 256,
+            DYNAMIC = 512
         };
 
-        SceneNodeProperty(Type type, std::string file, std::string id, int index) : _type(type), _file(file), _id(id), _index(index) { }
+        SceneNodeProperty(Type type, std::string url, int index) : _type(type), _url(url), _index(index) { }
 
         Type _type;
-        std::string _file;
-        std::string _id;
+        std::string _url;
         int _index;
     };
 
     struct SceneNode
     {
-        SceneNode() : _nodeID(""), _exactMatch(true) { }
+        SceneNode() : _nodeID("") { }
 
         const char* _nodeID;
-        bool _exactMatch;
         std::vector<SceneNodeProperty> _properties;
     };
 
@@ -107,13 +102,14 @@ private:
 
     static PhysicsConstraint* loadSpringConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
 
-    static void splitURL(const char* 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 path.
+    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<SceneNode> _sceneNodes;                          // Holds all the nodes+properties declared in the .scene file.
-    static std::string _path;                                           // The path of the main GPB for the scene being loaded.
+    static std::string _gpbPath;                                        // The path of the main GPB for the scene being loaded.
+    static std::string _path;                                           // The path of the scene file being loaded.
 };
 
 }

+ 0 - 140
gameplay/src/ScrollLayout.cpp

@@ -1,140 +0,0 @@
-#include "Base.h"
-#include "Control.h"
-#include "ScrollLayout.h"
-#include "Container.h"
-
-namespace gameplay
-{
-
-ScrollLayout::ScrollLayout() 
-    : _scrollPosition(Vector2::zero()), _lastX(0), _lastY(0), _scrolling(false),
-      _positionVertically(false), _positionHorizontally(false)
-{
-}
-
-ScrollLayout::ScrollLayout(const ScrollLayout& copy)
-{
-}
-
-ScrollLayout::~ScrollLayout()
-{
-}
-
-ScrollLayout* ScrollLayout::create()
-{
-    return new ScrollLayout();
-}
-
-Layout::Type ScrollLayout::getType()
-{
-    return Layout::LAYOUT_SCROLL;
-}
-
-void ScrollLayout::update(const Container* container)
-{
-    // Position controls if automatic positioning is enabled.
-    if (_positionVertically && _positionHorizontally)
-    {
-        // Treat as scrollable flow layout.
-    }
-    else if (_positionVertically)
-    {
-        // Scrollable vertical layout.
-    }
-    else if (_positionHorizontally)
-    {
-        // Scrollable horizontal layout.
-    }
-
-    // Calculate total width and height.
-    float totalWidth = 0;
-    float totalHeight = 0;
-    std::vector<Control*> controls = container->getControls();
-    unsigned int controlsCount = controls.size();
-    for (unsigned int i = 0; i < controlsCount; i++)
-    {
-        Control* control = controls.at(i);
-
-        const Rectangle& bounds = control->getBounds();
-        const Theme::Margin& margin = control->getMargin();
-
-        float newWidth = bounds.x + bounds.width + margin.left + margin.right;
-        if (newWidth > totalWidth)
-        {
-            totalWidth = newWidth;
-        }
-
-        float newHeight = bounds.y + bounds.height + margin.top + margin.bottom;
-        if (newHeight > totalHeight)
-        {
-            totalHeight = newHeight;
-        }
-    }
-
-    const Rectangle& containerBounds = container->getBounds();
-    const Theme::Border& containerBorder = container->getBorder(container->getState());
-    const Theme::Padding& containerPadding = container->getPadding();
-
-    float clipWidth = containerBounds.width - containerBorder.left - containerBorder.right - containerPadding.left - containerPadding.right;
-    float clipHeight = containerBounds.height - containerBorder.top - containerBorder.bottom - containerPadding.top - containerPadding.bottom;
-
-    // Stop scrolling when the far edge is reached.
-    if (-_scrollPosition.x > totalWidth - clipWidth)
-    {
-        _scrollPosition.x = -(totalWidth - clipWidth);
-    }
-    
-    if (-_scrollPosition.y > totalHeight - clipHeight)
-    {
-        _scrollPosition.y = -(totalHeight - clipHeight);
-    }
-
-    if (_scrollPosition.x > 0)
-    {
-        _scrollPosition.x = 0;
-    }
-
-    if (_scrollPosition.y > 0)
-    {
-        _scrollPosition.y = 0;
-    }
-
-    // Position controls within scroll area.
-    for (unsigned int i = 0; i < controlsCount; i++)
-    {
-        Control* control = controls.at(i);
-        control->update(container->getClip(), _scrollPosition);
-    }
-}
-
-bool ScrollLayout::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
-{
-    switch(evt)
-    {
-    case Touch::TOUCH_PRESS:
-        _lastX = x;
-        _lastY = y;
-        _scrolling = true;
-        break;
-
-    case Touch::TOUCH_MOVE:
-        if (_scrolling)
-        {
-            // Calculate the latest movement delta for the next update to use.
-            _scrollPosition.x += x - _lastX;
-            _scrollPosition.y += y - _lastY;
-            _lastX = x;
-            _lastY = y;
-            return true;
-        }
-        break;
-
-    case Touch::TOUCH_RELEASE:
-        _scrolling = false;
-        break;
-    }
-
-    return false;
-}
-
-}

+ 0 - 72
gameplay/src/ScrollLayout.h

@@ -1,72 +0,0 @@
-#ifndef SCROLLLAYOUT_H_
-#define SCROLLLAYOUT_H_
-
-#include "Layout.h"
-
-namespace gameplay
-{
-
-class ScrollLayout : public Layout
-{
-    friend class Form;
-    friend class Container;
-
-public:
-
-    /**
-     * Get the type of this Layout.
-     *
-     * @return Layout::LAYOUT_SCROLL
-     */
-    Layout::Type getType();
-
-protected:
-
-    /**
-     * Create a ScrollLayout.
-     *
-     * @return A ScrollLayout object.
-     */
-    static ScrollLayout* create();
-
-    /**
-     * Update the controls contained by the specified container.
-     *
-     * @param container The container to update.
-     */
-    void update(const Container* container);
-
-    bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
-
-private:
-
-    /**
-     * Constructor.
-     */
-    ScrollLayout();
-
-    /**
-     * Constructor.
-     */
-    ScrollLayout(const ScrollLayout& copy);
-
-    /**
-     * Destructor.
-     */
-    virtual ~ScrollLayout();
-
-    Vector2 _scrollPosition;
-
-    // Previous touch point.
-    int _lastX;
-    int _lastY;
-
-    bool _scrolling;
-
-    bool _positionVertically;
-    bool _positionHorizontally;
-};
-
-}
-
-#endif

+ 4 - 4
gameplay/src/Slider.cpp

@@ -172,10 +172,10 @@ void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     const Rectangle& markerRegion = _markerImage->getRegion();
     const Rectangle& trackRegion = _trackImage->getRegion();
 
-    const Theme::UVs minCap = _minImage->getUVs();
-    const Theme::UVs maxCap = _maxImage->getUVs();
-    const Theme::UVs marker = _markerImage->getUVs();
-    const Theme::UVs track = _trackImage->getUVs();
+    const Theme::UVs& minCap = _minImage->getUVs();
+    const Theme::UVs& maxCap = _maxImage->getUVs();
+    const Theme::UVs& marker = _markerImage->getUVs();
+    const Theme::UVs& track = _trackImage->getUVs();
 
     Vector4 minCapColor = _minImage->getColor();
     Vector4 maxCapColor = _maxImage->getColor();

+ 1 - 1
gameplay/src/Slider.h

@@ -160,7 +160,7 @@ protected:
 
     /**
      * The minimum value for the Slider.
-     */
+     */
     float _min;
     
     /**

+ 23 - 43
gameplay/src/SpriteBatch.cpp

@@ -83,9 +83,7 @@ SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsign
 
 SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int initialCapacity)
 {
-    GP_ASSERT(texture);
-    GP_ASSERT(texture->getWidth());
-    GP_ASSERT(texture->getHeight());
+    GP_ASSERT(texture != NULL);
 
     bool customEffect = (effect != NULL);
     if (!customEffect)
@@ -96,7 +94,7 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
             __spriteEffect = Effect::createFromSource(SPRITE_VSH, SPRITE_FSH);
             if (__spriteEffect == NULL)
             {
-                GP_ERROR("Unable to create default sprite effect.");
+                GP_ERROR("Unable to load sprite effect.");
                 return NULL;
             }
 
@@ -122,33 +120,25 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     }
     if (!samplerUniform)
     {
-        GP_ERROR("Failed to create sprite batch; required uniform of type GL_SAMPLER_2D not found in sprite effect.");
+        GP_ERROR("No uniform of type GL_SAMPLER_2D found in sprite effect.");
         SAFE_RELEASE(effect);
         return NULL;
     }
 
-    // Wrap the effect in a material.
+    // Wrap the effect in a material
     Material* material = Material::create(effect); // +ref effect
-    if (!material)
-    {
-        GP_ERROR("Failed to create material for sprite batch.");
-        SAFE_RELEASE(effect);
-        return NULL;
-    }
 
-    // Set initial material state.
-    GP_ASSERT(material->getStateBlock());
+    // Set initial material state
     material->getStateBlock()->setBlend(true);
     material->getStateBlock()->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
     material->getStateBlock()->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
 
-    // Bind the texture to the material as a sampler.
+    // Bind the texture to the material as a sampler
     Texture::Sampler* sampler = Texture::Sampler::create(texture); // +ref texture
-    GP_ASSERT(material->getParameter(samplerUniform->getName()));
     material->getParameter(samplerUniform->getName())->setValue(sampler);
     SAFE_RELEASE(sampler);
 
-    // Define the vertex format for the batch.
+    // Define the vertex format for the batch
     VertexFormat::Element vertexElements[] =
     {
         VertexFormat::Element(VertexFormat::POSITION, 3),
@@ -157,11 +147,11 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
     };
     VertexFormat vertexFormat(vertexElements, 3);
 
-    // Create the mesh batch.
+    // Create the mesh batch
     MeshBatch* meshBatch = MeshBatch::create(vertexFormat, Mesh::TRIANGLE_STRIP, material, true, initialCapacity > 0 ? initialCapacity : SPRITE_BATCH_DEFAULT_SIZE);
     material->release(); // don't call SAFE_RELEASE since material is used below
 
-    // Create the batch.
+    // Create the batch
     SpriteBatch* batch = new SpriteBatch();
     batch->_customEffect = customEffect;
     batch->_batch = meshBatch;
@@ -169,7 +159,6 @@ 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)
-    GP_ASSERT(material->getParameter("u_projectionMatrix"));
     material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getOrthoMatrix);
 
     return batch;
@@ -177,7 +166,6 @@ SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int
 
 void SpriteBatch::begin()
 {
-    GP_ASSERT(_batch);
     _batch->begin();
 }
 
@@ -218,11 +206,12 @@ void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2&
 void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
                        const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
 {
-    GP_ASSERT(_batch);
-
-    float x = dst.x;
-    float y = dst.y;
+    draw(dst.x, dst.y, dst.z, width, height, u1, v1, u2, v2, color, rotationPoint, rotationAngle, positionIsCenter);
+}
 
+void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
+          const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
+{
     // Treat the given position as the center if the user specified it as such.
     if (positionIsCenter)
     {
@@ -249,13 +238,13 @@ void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1,
     upRight.rotate(pivotPoint, rotationAngle);
     downLeft.rotate(pivotPoint, rotationAngle);
     downRight.rotate(pivotPoint, rotationAngle);
-    
+
     // Write sprite vertex data.
     static SpriteVertex v[4];
-    ADD_SPRITE_VERTEX(v[0], upLeft.x, upLeft.y, dst.z, u1, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX(v[1], upRight.x, upRight.y, dst.z, u1, v2, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX(v[2], downLeft.x, downLeft.y, dst.z, u2, v1, color.x, color.y, color.z, color.w);
-    ADD_SPRITE_VERTEX(v[3], downRight.x, downRight.y, dst.z, u2, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[0], downLeft.x, downLeft.y, z, u1, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[1], upLeft.x, upLeft.y, z, u1, v2, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[2], downRight.x, downRight.y, z, u2, v1, color.x, color.y, color.z, color.w);
+    ADD_SPRITE_VERTEX(v[3], upRight.x, upRight.y, z, u2, v2, color.x, color.y, color.z, color.w);
     
     static unsigned short indices[4] = { 0, 1, 2, 3 };
 
@@ -265,8 +254,6 @@ void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1,
 void SpriteBatch::draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height,
     float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
 {
-    GP_ASSERT(_batch);
-
     // Calculate the vertex positions.
     //static Vector3 p[4];
 
@@ -396,14 +383,14 @@ void SpriteBatch::addSprite(float x, float y, float width, float height, float u
 
 void SpriteBatch::draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
 {
-    GP_ASSERT(_batch);
+    GP_ASSERT(vertices);
+    GP_ASSERT(indices);
+
     _batch->add(vertices, vertexCount, indices, indexCount);
 }
 
 void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, bool positionIsCenter)
 {
-    GP_ASSERT(_batch);
-
     // Treat the given position as the center if the user specified it as such.
     if (positionIsCenter)
     {
@@ -427,28 +414,24 @@ void SpriteBatch::draw(float x, float y, float z, float width, float height, flo
 
 void SpriteBatch::end()
 {
-    // Finish and draw the batch.
-    GP_ASSERT(_batch);
+    // Finish and draw the batch
     _batch->end();
     _batch->draw();
 }
 
 RenderState::StateBlock* SpriteBatch::getStateBlock() const
 {
-    GP_ASSERT(_batch && _batch->getMaterial());
     return _batch->getMaterial()->getStateBlock();
 }
 
 Material* SpriteBatch::getMaterial() const
 {
-    GP_ASSERT(_batch);
     return _batch->getMaterial();
 }
 
 void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
 {
     // Bind the specified matrix to a parameter named 'u_projectionMatrix' (assumed to exist).
-    GP_ASSERT(_batch && _batch->getMaterial() && _batch->getMaterial()->getParameter("u_projectionMatrix"));
     _batch->getMaterial()->getParameter("u_projectionMatrix")->setValue(matrix);
 }
 
@@ -462,9 +445,6 @@ const Matrix& SpriteBatch::getOrthoMatrix() const
 
 bool SpriteBatch::clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2)
 {
-    GP_ASSERT(width);
-    GP_ASSERT(height);
-
     // Clip the rectangle given by { x, y, width, height } into clip.
     // We need to scale the uvs accordingly as we do this.
 

+ 3 - 0
gameplay/src/SpriteBatch.h

@@ -146,6 +146,9 @@ public:
     void draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
               const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter = false);
     
+    void draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
+              const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter = false);
+
     /**
      * Draws a single sprite, rotated about the implied up vector.
      * 

+ 1 - 1
gameplay/src/VerticalLayout.cpp

@@ -81,7 +81,7 @@ void VerticalLayout::update(const Container* container)
 
         yPosition += margin.top;
 
-        control->setPosition(0, yPosition);
+        control->setPosition(margin.left, yPosition);
         control->update(container->getClip(), Vector2::zero());
 
         yPosition += bounds.height + margin.bottom;