Przeglądaj źródła

Merge pull request #130 from blackberry-gaming/next

Next
Sean Paul Taylor 13 lat temu
rodzic
commit
92e667195d
49 zmienionych plików z 1207 dodań i 505 usunięć
  1. 3 4
      gameplay-encoder/gameplay-encoder.xcodeproj/xcshareddata/xcschemes/gameplay-encoder.xcscheme
  2. 6 6
      gameplay-template/gameplay-template.xcodeproj/project.pbxproj
  3. 15 2
      gameplay/.cproject
  4. 1 1
      gameplay/android/jni/Android.mk
  5. 7 1
      gameplay/gameplay.vcxproj
  6. 19 1
      gameplay/gameplay.vcxproj.filters
  7. 24 0
      gameplay/gameplay.xcodeproj/project.pbxproj
  8. 2 2
      gameplay/src/AbsoluteLayout.cpp
  9. 1 1
      gameplay/src/AbsoluteLayout.h
  10. 15 2
      gameplay/src/Bundle.cpp
  11. 2 2
      gameplay/src/CheckBox.cpp
  12. 2 2
      gameplay/src/CheckBox.h
  13. 38 33
      gameplay/src/Container.cpp
  14. 4 4
      gameplay/src/Container.h
  15. 11 5
      gameplay/src/Control.cpp
  16. 15 6
      gameplay/src/Control.h
  17. 3 2
      gameplay/src/FileSystem.cpp
  18. 2 2
      gameplay/src/FlowLayout.cpp
  19. 1 1
      gameplay/src/FlowLayout.h
  20. 1 2
      gameplay/src/Font.cpp
  21. 4 4
      gameplay/src/Form.cpp
  22. 9 1
      gameplay/src/FrameBuffer.cpp
  23. 4 0
      gameplay/src/FrameBuffer.h
  24. 2 1
      gameplay/src/Game.cpp
  25. 219 0
      gameplay/src/Joystick.cpp
  26. 147 0
      gameplay/src/Joystick.h
  27. 34 0
      gameplay/src/Joystick.inl
  28. 2 2
      gameplay/src/Label.cpp
  29. 2 2
      gameplay/src/Label.h
  30. 2 1
      gameplay/src/Layout.h
  31. 37 0
      gameplay/src/MathUtil.h
  32. 173 0
      gameplay/src/MathUtil.inl
  33. 229 0
      gameplay/src/MathUtilNeon.inl
  34. 12 319
      gameplay/src/Matrix.cpp
  35. 2 2
      gameplay/src/Plane.cpp
  36. 87 59
      gameplay/src/Properties.cpp
  37. 2 2
      gameplay/src/RadioButton.cpp
  38. 2 2
      gameplay/src/RadioButton.h
  39. 38 7
      gameplay/src/SceneLoader.cpp
  40. 1 0
      gameplay/src/SceneLoader.h
  41. 2 2
      gameplay/src/Slider.cpp
  42. 2 2
      gameplay/src/Slider.h
  43. 2 2
      gameplay/src/TextBox.cpp
  44. 2 2
      gameplay/src/TextBox.h
  45. 11 0
      gameplay/src/Texture.cpp
  46. 3 12
      gameplay/src/Vector3.cpp
  47. 3 3
      gameplay/src/VerticalLayout.cpp
  48. 1 1
      gameplay/src/VerticalLayout.h
  49. 1 0
      gameplay/src/gameplay.h

+ 3 - 4
gameplay-encoder/gameplay-encoder.xcodeproj/xcshareddata/xcschemes/gameplay-encoder.xcscheme

@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0430"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -23,8 +22,8 @@
       </BuildActionEntries>
    </BuildAction>
    <TestAction
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES"
       buildConfiguration = "Debug">
       <Testables>
@@ -41,7 +40,7 @@
    </TestAction>
    <LaunchAction
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       buildConfiguration = "Debug"

+ 6 - 6
gameplay-template/gameplay-template.xcodeproj/project.pbxproj

@@ -43,7 +43,7 @@
 
 /* Begin PBXFileReference section */
 		42438B521491AD2000D218B8 /* libgameplay.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgameplay.a; path = "~/Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug/libgameplay.a"; sourceTree = "<group>"; };
-		42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT-MacOSX.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		42C932C01491A0DB0098216A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
 		42C932ED1491A4CB0098216A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
 		42C932EF1491A5160098216A /* TemplateGame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TemplateGame.cpp; path = src/TemplateGame.cpp; sourceTree = SOURCE_ROOT; };
@@ -60,7 +60,7 @@
 		42C9332A1491A7390098216A /* libpng.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng.a; path = "GAMEPLAY_PATH/external-deps/libpng/lib/macosx/libpng.a"; sourceTree = "<group>"; };
 		42C9332D1491A7810098216A /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
 		5B61611214CCC2200073B857 /* TEMPLATE_PROJECT-macosx.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "TEMPLATE_PROJECT-macosx.plist"; sourceTree = "<group>"; };
-		5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		5B61612E14CCC24D0073B857 /* TEMPLATE_PROJECT-ios.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TEMPLATE_PROJECT-ios.plist"; sourceTree = "<group>"; };
 		5BAF2067152F2DDD003E2AC3 /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/CoreMotion.framework; sourceTree = DEVELOPER_DIR; };
 		5BAF2068152F2DDD003E2AC3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
@@ -133,8 +133,8 @@
 		42C932BD1491A0DB0098216A /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX.app */,
-				5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */,
+				42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT.app */,
+				5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT.app */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -225,7 +225,7 @@
 			);
 			name = "TEMPLATE_PROJECT-MacOSX";
 			productName = TEMPLATE_PROJECT;
-			productReference = 42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX.app */;
+			productReference = 42C932BC1491A0DB0098216A /* TEMPLATE_PROJECT.app */;
 			productType = "com.apple.product-type.application";
 		};
 		5B61611414CCC24C0073B857 /* TEMPLATE_PROJECT-iOS */ = {
@@ -244,7 +244,7 @@
 			);
 			name = "TEMPLATE_PROJECT-iOS";
 			productName = TEMPLATE_PROJECT;
-			productReference = 5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */;
+			productReference = 5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT.app */;
 			productType = "com.apple.product-type.application";
 		};
 /* End PBXNativeTarget section */

+ 15 - 2
gameplay/.cproject

@@ -29,8 +29,8 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
-								<option id="com.qnx.qcc.option.compiler.qccoptions.1968057343" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions" valueType="stringList"/>
-								<option id="com.qnx.qcc.option.compiler.ccoptions.1078137668" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
+								<option id="com.qnx.qcc.option.compiler.qccoptions.1968057343" name="QCC Options" superClass="com.qnx.qcc.option.compiler.qccoptions"/>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.1078137668" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
 									<listOptionValue builtIn="false" value="-mfpu=neon"/>
 								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.997142816" superClass="com.qnx.qcc.inputType.compiler"/>
@@ -86,6 +86,9 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.1122311163" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
+									<listOptionValue builtIn="false" value="-mfpu=neon"/>
+								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.1380846613" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.855139060" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -138,6 +141,9 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.1956270067" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
+									<listOptionValue builtIn="false" value="-mfpu=neon"/>
+								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.81809638" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.2145279747" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -193,6 +199,9 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.47607907" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList">
+									<listOptionValue builtIn="false" value="-mfpu=neon"/>
+								</option>
 								<inputType id="com.qnx.qcc.inputType.compiler.2007171407" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.1537562121" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -246,6 +255,7 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.1432778691" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1038720310" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.521146732" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -299,6 +309,7 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.663337616" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1961855927" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.1089440729" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -353,6 +364,7 @@
 									<listOptionValue builtIn="false" value="&quot;../../external-deps/oggvorbis/include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
+								<option id="com.qnx.qcc.option.compiler.ccoptions.346770186" name="Compiler Options (-Wc,)" superClass="com.qnx.qcc.option.compiler.ccoptions" valueType="stringList"/>
 								<inputType id="com.qnx.qcc.inputType.compiler.1658185881" superClass="com.qnx.qcc.inputType.compiler"/>
 							</tool>
 							<tool id="com.qnx.qcc.tool.assembler.746786008" name="QCC Assembler" superClass="com.qnx.qcc.tool.assembler">
@@ -411,4 +423,5 @@
 		<resource resourceType="PROJECT" workspacePath="/gameplay"/>
 	</storageModule>
 	<storageModule moduleId="com.qnx.tools.ide.qde.core.QNXProjectProperties"/>
+	<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
 </cproject>

+ 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 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 Joystick.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
 

+ 7 - 1
gameplay/gameplay.vcxproj

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

+ 19 - 1
gameplay/gameplay.vcxproj.filters

@@ -279,6 +279,9 @@
     <ClCompile Include="src\FlowLayout.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\Joystick.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -554,6 +557,12 @@
     <ClInclude Include="src\FlowLayout.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\Joystick.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\MathUtil.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
@@ -652,6 +661,15 @@
     <None Include="res\logo_white.png">
       <Filter>res</Filter>
     </None>
+    <None Include="src\MathUtil.inl">
+      <Filter>src</Filter>
+    </None>
+    <None Include="src\MathUtilNeon.inl">
+      <Filter>src</Filter>
+    </None>
+    <None Include="src\Joystick.inl">
+      <Filter>src</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\PhysicsFixedConstraint.inl">
@@ -697,4 +715,4 @@
       <Filter>src</Filter>
     </None>
   </ItemGroup>
-</Project>
+</Project>

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

@@ -18,6 +18,12 @@
 		422260D81537790F0011E3AB /* Bundle.h in Headers */ = {isa = PBXBuildFile; fileRef = 422260D51537790F0011E3AB /* Bundle.h */; };
 		422260D91537790F0011E3AB /* Bundle.h in Headers */ = {isa = PBXBuildFile; fileRef = 422260D51537790F0011E3AB /* Bundle.h */; };
 		4234D99E14686C52003031B3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4234D99D14686C52003031B3 /* Cocoa.framework */; };
+		4239DDEC157545A1005EA3F6 /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4239DDE9157545A1005EA3F6 /* Joystick.cpp */; };
+		4239DDED157545A1005EA3F6 /* Joystick.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4239DDE9157545A1005EA3F6 /* Joystick.cpp */; };
+		4239DDEE157545A1005EA3F6 /* Joystick.h in Headers */ = {isa = PBXBuildFile; fileRef = 4239DDEA157545A1005EA3F6 /* Joystick.h */; };
+		4239DDEF157545A1005EA3F6 /* Joystick.h in Headers */ = {isa = PBXBuildFile; fileRef = 4239DDEA157545A1005EA3F6 /* Joystick.h */; };
+		4239DDF4157545C1005EA3F6 /* MathUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4239DDF1157545C1005EA3F6 /* MathUtil.h */; };
+		4239DDF5157545C1005EA3F6 /* MathUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 4239DDF1157545C1005EA3F6 /* MathUtil.h */; };
 		4251B131152D049B002F6199 /* ScreenDisplayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4251B12E152D049B002F6199 /* ScreenDisplayer.h */; };
 		4251B132152D049B002F6199 /* ScreenDisplayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 4251B12E152D049B002F6199 /* ScreenDisplayer.h */; };
 		4251B133152D049B002F6199 /* ThemeStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4251B12F152D049B002F6199 /* ThemeStyle.cpp */; };
@@ -399,6 +405,12 @@
 		422260D51537790F0011E3AB /* Bundle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Bundle.h; path = src/Bundle.h; sourceTree = SOURCE_ROOT; };
 		4234D99A14686C52003031B3 /* libgameplay.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgameplay.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		4234D99D14686C52003031B3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+		4239DDE9157545A1005EA3F6 /* Joystick.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Joystick.cpp; path = src/Joystick.cpp; sourceTree = SOURCE_ROOT; };
+		4239DDEA157545A1005EA3F6 /* Joystick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Joystick.h; path = src/Joystick.h; sourceTree = SOURCE_ROOT; };
+		4239DDEB157545A1005EA3F6 /* Joystick.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Joystick.inl; path = src/Joystick.inl; sourceTree = SOURCE_ROOT; };
+		4239DDF1157545C1005EA3F6 /* MathUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MathUtil.h; path = src/MathUtil.h; sourceTree = SOURCE_ROOT; };
+		4239DDF2157545C1005EA3F6 /* MathUtil.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = MathUtil.inl; path = src/MathUtil.inl; sourceTree = SOURCE_ROOT; };
+		4239DDF3157545C1005EA3F6 /* MathUtilNeon.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = MathUtilNeon.inl; path = src/MathUtilNeon.inl; sourceTree = SOURCE_ROOT; };
 		4251B12E152D049B002F6199 /* ScreenDisplayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScreenDisplayer.h; path = src/ScreenDisplayer.h; sourceTree = SOURCE_ROOT; };
 		4251B12F152D049B002F6199 /* ThemeStyle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThemeStyle.cpp; path = src/ThemeStyle.cpp; sourceTree = SOURCE_ROOT; };
 		4251B130152D049B002F6199 /* ThemeStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThemeStyle.h; path = src/ThemeStyle.h; sourceTree = SOURCE_ROOT; };
@@ -761,6 +773,9 @@
 				4208DEE814A4079F00D3C511 /* Image.inl */,
 				42CD0DE4147D8FF50000361E /* Joint.cpp */,
 				42CD0DE5147D8FF50000361E /* Joint.h */,
+				4239DDE9157545A1005EA3F6 /* Joystick.cpp */,
+				4239DDEA157545A1005EA3F6 /* Joystick.h */,
+				4239DDEB157545A1005EA3F6 /* Joystick.inl */,
 				4208DEEB14A407B900D3C511 /* Keyboard.h */,
 				5BD52641150F822A004C9099 /* Label.cpp */,
 				5BD52642150F822A004C9099 /* Label.h */,
@@ -772,6 +787,9 @@
 				42CD0DE9147D8FF50000361E /* Material.h */,
 				42CD0DEA147D8FF50000361E /* MaterialParameter.cpp */,
 				42CD0DEB147D8FF50000361E /* MaterialParameter.h */,
+				4239DDF1157545C1005EA3F6 /* MathUtil.h */,
+				4239DDF2157545C1005EA3F6 /* MathUtil.inl */,
+				4239DDF3157545C1005EA3F6 /* MathUtilNeon.inl */,
 				42CD0DEC147D8FF50000361E /* Matrix.cpp */,
 				42CD0DED147D8FF50000361E /* Matrix.h */,
 				42CD0DEE147D8FF50000361E /* Matrix.inl */,
@@ -1064,6 +1082,8 @@
 				4251B135152D049B002F6199 /* ThemeStyle.h in Headers */,
 				422260D81537790F0011E3AB /* Bundle.h in Headers */,
 				426878AE153F4BB300844500 /* FlowLayout.h in Headers */,
+				4239DDEE157545A1005EA3F6 /* Joystick.h in Headers */,
+				4239DDF4157545C1005EA3F6 /* MathUtil.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1161,6 +1181,8 @@
 				4251B136152D049B002F6199 /* ThemeStyle.h in Headers */,
 				422260D91537790F0011E3AB /* Bundle.h in Headers */,
 				426878AF153F4BB300844500 /* FlowLayout.h in Headers */,
+				4239DDEF157545A1005EA3F6 /* Joystick.h in Headers */,
+				4239DDF5157545C1005EA3F6 /* MathUtil.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1317,6 +1339,7 @@
 				4271C08E15337C8200B89DA7 /* Layout.cpp in Sources */,
 				422260D61537790F0011E3AB /* Bundle.cpp in Sources */,
 				426878AC153F4BB300844500 /* FlowLayout.cpp in Sources */,
+				4239DDEC157545A1005EA3F6 /* Joystick.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1409,6 +1432,7 @@
 				4271C08F15337C8200B89DA7 /* Layout.cpp in Sources */,
 				422260D71537790F0011E3AB /* Bundle.cpp in Sources */,
 				426878AD153F4BB300844500 /* FlowLayout.cpp in Sources */,
+				4239DDED157545A1005EA3F6 /* Joystick.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 2 - 2
gameplay/src/AbsoluteLayout.cpp

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

+ 1 - 1
gameplay/src/AbsoluteLayout.h

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

+ 15 - 2
gameplay/src/Bundle.cpp

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

+ 2 - 2
gameplay/src/CheckBox.cpp

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

+ 2 - 2
gameplay/src/CheckBox.h

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

+ 38 - 33
gameplay/src/Container.cpp

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

+ 4 - 4
gameplay/src/Container.h

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

+ 11 - 5
gameplay/src/Control.cpp

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

+ 15 - 6
gameplay/src/Control.h

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

+ 3 - 2
gameplay/src/FileSystem.cpp

@@ -308,7 +308,8 @@ void createFileFromAsset(const char* path)
 
     GP_ASSERT(path);
     std::string fullPath(__resourcePath);
-    fullPath += FileSystem::resolvePath(path);
+    std::string resolvedPath = FileSystem::resolvePath(path);
+    fullPath += resolvedPath;
 
     std::string directoryPath = fullPath.substr(0, fullPath.rfind('/'));
     struct stat s;
@@ -320,7 +321,7 @@ void createFileFromAsset(const char* path)
     // for each time the process (game) runs.
     if (upToDateAssets.find(fullPath) == upToDateAssets.end())
     {
-        AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
+        AAsset* asset = AAssetManager_open(__assetManager, resolvedPath.c_str(), AASSET_MODE_RANDOM);
         if (asset)
         {
             const void* data = AAsset_getBuffer(asset);

+ 2 - 2
gameplay/src/FlowLayout.cpp

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

+ 1 - 1
gameplay/src/FlowLayout.h

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

+ 1 - 2
gameplay/src/Font.cpp

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

+ 4 - 4
gameplay/src/Form.cpp

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

+ 9 - 1
gameplay/src/FrameBuffer.cpp

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

+ 4 - 0
gameplay/src/FrameBuffer.h

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

+ 2 - 1
gameplay/src/Game.cpp

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

+ 219 - 0
gameplay/src/Joystick.cpp

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

+ 147 - 0
gameplay/src/Joystick.h

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

+ 34 - 0
gameplay/src/Joystick.inl

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

+ 2 - 2
gameplay/src/Label.cpp

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

+ 2 - 2
gameplay/src/Label.h

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

+ 2 - 1
gameplay/src/Layout.h

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

+ 37 - 0
gameplay/src/MathUtil.h

@@ -0,0 +1,37 @@
+#ifndef MATHUTIL_H_
+#define MATHUTIL_H_
+
+namespace gameplay
+{
+class MathUtil
+{
+	friend class Matrix;
+	friend class Vector3;
+
+private:
+
+	/** Matrix **/
+	inline static void addMatrix(const float* m, float scalar, float* dst);
+	inline static void addMatrix(const float* m1, const float* m2, float* dst);
+	inline static void multiplyMatrix(const float* m, float scalar, float* dst);
+	inline static void multiplyMatrix(const float* m1, const float* m2, float* dst);
+	inline static void negateMatrix(const float* m, float* dst);
+	inline static void subtractMatrix(const float* m1, const float* m2, float* dst);
+	inline static void transformVectorMatrix(const float* m, float x, float y, float z, float w, float* dst);
+	inline static void transformVectorMatrix(const float* m, const float* v, float* dst);
+	inline static void transposeMatrix(const float* m, float* dst);
+
+	/** Vector3 **/
+	inline static void crossVector3(const float* v1, const float* v2, float* dst);
+
+	MathUtil();
+};
+}
+
+#ifdef USE_NEON
+#include "MathUtilNeon.inl"
+#else
+#include "MathUtil.inl"
+#endif
+
+#endif /* MATHUTIL_H_ */

+ 173 - 0
gameplay/src/MathUtil.inl

@@ -0,0 +1,173 @@
+#define MATRIX_SIZE ( sizeof(float) * 16)
+
+namespace gameplay
+{
+
+inline void MathUtil::addMatrix(const float* m, float scalar, float* dst)
+{
+	dst[0]  = m[0]  + scalar;
+	dst[1]  = m[1]  + scalar;
+	dst[2]  = m[2]  + scalar;
+	dst[3]  = m[3]  + scalar;
+	dst[4]  = m[4]  + scalar;
+	dst[5]  = m[5]  + scalar;
+	dst[6]  = m[6]  + scalar;
+	dst[7]  = m[7]  + scalar;
+	dst[8]  = m[8]  + scalar;
+	dst[9]  = m[9]  + scalar;
+	dst[10] = m[10] + scalar;
+	dst[11] = m[11] + scalar;
+	dst[12] = m[12] + scalar;
+	dst[13] = m[13] + scalar;
+	dst[14] = m[14] + scalar;
+	dst[15] = m[15] + scalar;
+}
+
+inline void MathUtil::addMatrix(const float* m1, const float* m2, float* dst)
+{
+	dst[0]  = m1[0]  + m2[0];
+	dst[1]  = m1[1]  + m2[1];
+	dst[2]  = m1[2]  + m2[2];
+	dst[3]  = m1[3]  + m2[3];
+	dst[4]  = m1[4]  + m2[4];
+	dst[5]  = m1[5]  + m2[5];
+	dst[6]  = m1[6]  + m2[6];
+	dst[7]  = m1[7]  + m2[7];
+	dst[8]  = m1[8]  + m2[8];
+	dst[9]  = m1[9]  + m2[9];
+	dst[10] = m1[10] + m2[10];
+	dst[11] = m1[11] + m2[11];
+	dst[12] = m1[12] + m2[12];
+	dst[13] = m1[13] + m2[13];
+	dst[14] = m1[14] + m2[14];
+	dst[15] = m1[15] + m2[15];
+}
+
+inline void MathUtil::multiplyMatrix(const float* m, float scalar, float* dst)
+{
+	dst[0]  = m[0]  * scalar;
+	dst[1]  = m[1]  * scalar;
+	dst[2]  = m[2]  * scalar;
+	dst[3]  = m[3]  * scalar;
+	dst[4]  = m[4]  * scalar;
+	dst[5]  = m[5]  * scalar;
+	dst[6]  = m[6]  * scalar;
+	dst[7]  = m[7]  * scalar;
+	dst[8]  = m[8]  * scalar;
+	dst[9]  = m[9]  * scalar;
+	dst[10] = m[10] * scalar;
+	dst[11] = m[11] * scalar;
+	dst[12] = m[12] * scalar;
+	dst[13] = m[13] * scalar;
+	dst[14] = m[14] * scalar;
+	dst[15] = m[15] * scalar;
+}
+
+inline void MathUtil::multiplyMatrix(const float* m1, const float* m2, float* dst)
+{
+	// Support the case where m1 or m2 is the same array as dst.
+	float product[16];
+
+	product[0]  = m1[0] * m2[0]  + m1[4] * m2[1] + m1[8]   * m2[2]  + m1[12] * m2[3];
+	product[1]  = m1[1] * m2[0]  + m1[5] * m2[1] + m1[9]   * m2[2]  + m1[13] * m2[3];
+	product[2]  = m1[2] * m2[0]  + m1[6] * m2[1] + m1[10]  * m2[2]  + m1[14] * m2[3];
+	product[3]  = m1[3] * m2[0]  + m1[7] * m2[1] + m1[11]  * m2[2]  + m1[15] * m2[3];
+
+	product[4]  = m1[0] * m2[4]  + m1[4] * m2[5] + m1[8]   * m2[6]  + m1[12] * m2[7];
+	product[5]  = m1[1] * m2[4]  + m1[5] * m2[5] + m1[9]   * m2[6]  + m1[13] * m2[7];
+	product[6]  = m1[2] * m2[4]  + m1[6] * m2[5] + m1[10]  * m2[6]  + m1[14] * m2[7];
+	product[7]  = m1[3] * m2[4]  + m1[7] * m2[5] + m1[11]  * m2[6]  + m1[15] * m2[7];
+
+	product[8]  = m1[0] * m2[8]  + m1[4] * m2[9] + m1[8]   * m2[10] + m1[12] * m2[11];
+	product[9]  = m1[1] * m2[8]  + m1[5] * m2[9] + m1[9]   * m2[10] + m1[13] * m2[11];
+	product[10] = m1[2] * m2[8]  + m1[6] * m2[9] + m1[10]  * m2[10] + m1[14] * m2[11];
+	product[11] = m1[3] * m2[8]  + m1[7] * m2[9] + m1[11]  * m2[10] + m1[15] * m2[11];
+
+	product[12] = m1[0] * m2[12] + m1[4] * m2[13] + m1[8]  * m2[14] + m1[12] * m2[15];
+	product[13] = m1[1] * m2[12] + m1[5] * m2[13] + m1[9]  * m2[14] + m1[13] * m2[15];
+	product[14] = m1[2] * m2[12] + m1[6] * m2[13] + m1[10] * m2[14] + m1[14] * m2[15];
+	product[15] = m1[3] * m2[12] + m1[7] * m2[13] + m1[11] * m2[14] + m1[15] * m2[15];
+
+	memcpy(dst, product, MATRIX_SIZE);
+}
+
+inline void MathUtil::negateMatrix(const float* m, float* dst)
+{
+	dst[0]  = -m[0];
+	dst[1]  = -m[1];
+	dst[2]  = -m[2];
+	dst[3]  = -m[3];
+	dst[4]  = -m[4];
+	dst[5]  = -m[5];
+	dst[6]  = -m[6];
+	dst[7]  = -m[7];
+	dst[8]  = -m[8];
+	dst[9]  = -m[9];
+	dst[10] = -m[10];
+	dst[11] = -m[11];
+	dst[12] = -m[12];
+	dst[13] = -m[13];
+	dst[14] = -m[14];
+	dst[15] = -m[15];
+}
+
+inline void MathUtil::subtractMatrix(const float* m1, const float* m2, float* dst)
+{
+	dst[0]  = m1[0]  - m2[0];
+	dst[1]  = m1[1]  - m2[1];
+	dst[2]  = m1[2]  - m2[2];
+	dst[3]  = m1[3]  - m2[3];
+	dst[4]  = m1[4]  - m2[4];
+	dst[5]  = m1[5]  - m2[5];
+	dst[6]  = m1[6]  - m2[6];
+	dst[7]  = m1[7]  - m2[7];
+	dst[8]  = m1[8]  - m2[8];
+	dst[9]  = m1[9]  - m2[9];
+	dst[10] = m1[10] - m2[10];
+	dst[11] = m1[11] - m2[11];
+	dst[12] = m1[12] - m2[12];
+	dst[13] = m1[13] - m2[13];
+	dst[14] = m1[14] - m2[14];
+	dst[15] = m1[15] - m2[15];
+}
+
+inline void MathUtil::transformVectorMatrix(const float* m, float x, float y, float z, float w, float* dst)
+{
+	dst[0] = x * m[0] + y * m[4] + z * m[8] + w * m[12];
+	dst[1] = x * m[1] + y * m[5] + z * m[9] + w * m[13];
+	dst[2] = x * m[2] + y * m[6] + z * m[10] + w * m[14];
+}
+
+inline void MathUtil::transformVectorMatrix(const float* m, const float* v, float* dst)
+{
+	dst[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + v[3] * m[12];
+	dst[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + v[3] * m[13];
+	dst[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + v[3] * m[14];
+	dst[3] = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + v[3] * m[15];
+}
+
+inline void MathUtil::transposeMatrix(const float* m, float* dst)
+{
+	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, t, MATRIX_SIZE);
+}
+
+inline void MathUtil::crossVector3(const float* v1, const float* v2, float* dst)
+{
+	float x = (v1[1] * v2[2]) - (v1[2] * v2[1]);
+	float y = (v1[2] * v2[0]) - (v1[0] * v2[2]);
+	float z = (v1[0] * v2[1]) - (v1[1] * v2[0]);
+
+	dst[0] = x;
+	dst[1] = y;
+	dst[2] = z;
+}
+
+}
+
+

+ 229 - 0
gameplay/src/MathUtilNeon.inl

@@ -0,0 +1,229 @@
+namespace gameplay
+{
+
+inline void MathUtil::addMatrix(const float* m, float scalar, float* dst)
+{
+	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), "r"(m), "r"(&scalar)
+		: "q0", "q1", "q2", "q3", "q4", "q8", "q9", "q10", "q11", "memory"
+	);
+}
+
+inline void MathUtil::addMatrix(const float* m1, const float* m2, float* dst)
+{
+	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), "r"(m1), "r"(m2)
+		: "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
+	);
+}
+
+inline void MathUtil::multiplyMatrix(const float* m, float scalar, float* dst)
+{
+	asm volatile(
+		"vld1.32 	{d0[0]},	 	[%2]     	\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},   		[%0]! 		\n\t" // DST->M[m0-m7]
+		"vst1.32 	{q10-q11}, 		[%0]		\n\t" // DST->M[m8-m15]
+		:
+		: "r"(dst), "r"(m), "r"(&scalar)
+		: "q0", "q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11", "memory"
+	);
+}
+
+inline void MathUtil::multiplyMatrix(const float* m1, const float* m2, float* dst)
+{
+	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), "r"(m1), "r"(m2) // input - note *value* of pointer doesn't change.
+		: "memory", "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
+	);
+}
+
+inline void MathUtil::negateMatrix(const float* m, float* dst)
+{
+	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), "r"(m)
+		: "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "memory"
+	);
+}
+
+inline void MathUtil::subtractMatrix(const float* m1, const float* m2, float* dst)
+{
+	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), "r"(m1), "r"(m2)
+		: "q0", "q1", "q2", "q3", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "memory"
+	);
+}
+
+inline void MathUtil::transformVectorMatrix(const float* m, float x, float y, float z, float w, float* dst)
+{
+	asm volatile(
+		"vld1.32	{d0[0]},		[%1]	\n\t"	// V[x]
+		"vld1.32	{d0[1]},    	[%2]	\n\t"	// V[y]
+		"vld1.32	{d1[0]},		[%3]	\n\t"	// V[z]
+		"vld1.32	{d1[1]},		[%4]	\n\t"	// V[w]
+		"vld1.32	{d18 - d21},	[%5]!	\n\t"	// M[m0-m7]
+		"vld1.32	{d22 - d25},	[%5]	\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}, [%0]!        		\n\t"	// DST->V[x, y]
+		"vst1.32 {d27[0]}, [%0]        		\n\t"	// DST->V[z]
+		:
+		: "r"(dst), "r"(&x), "r"(&y), "r"(&z), "r"(&w), "r"(m)
+		: "q0", "q9", "q10","q11", "q12", "q13", "memory"
+	);
+}
+
+inline void MathUtil::transformVectorMatrix(const float* m, const float* v, float* dst)
+{
+	asm volatile
+	(
+		"vld1.32	{d0, d1}, [%1]		\n\t"   // V[x, y, z, w]
+		"vld1.32    {d18 - d21}, [%2]!  \n\t"   // M[m0-m7]
+		"vld1.32    {d22 - d25}, [%2]  \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}, [%0]    \n\t"   // DST->V
+		:
+		: "r"(dst), "r"(v), "r"(m)
+		: "q0", "q9", "q10","q11", "q12", "q13", "memory"
+	);
+}
+
+inline void MathUtil::transposeMatrix(const float* m, float* dst)
+{
+	asm volatile(
+		"vld4.32 {d0[0], d2[0], d4[0], d6[0]}, [%1]! 	\n\t" // DST->M[m0, m4, m8, m12] = M[m0-m3]
+		"vld4.32 {d0[1], d2[1], d4[1], d6[1]}, [%1]!	\n\t" // DST->M[m1, m5, m9, m12] = M[m4-m7]
+		"vld4.32 {d1[0], d3[0], d5[0], d7[0]}, [%1]!	\n\t" // DST->M[m2, m6, m10, m12] = M[m8-m11]
+		"vld4.32 {d1[1], d3[1], d5[1], d7[1]}, [%1] 	\n\t" // DST->M[m3, m7, m11, m12] = M[m12-m15]
+
+		"vst1.32 {q0-q1}, [%0]! 						\n\t" // DST->M[m0-m7]
+		"vst1.32 {q2-q3}, [%0] 							\n\t"  // DST->M[m8-m15]
+		:
+		: "r"(dst), "r"(m)
+		: "q0", "q1", "q2", "q3", "memory"
+	);
+}
+
+inline void MathUtil::crossVector3(const float* v1, const float* v2, float* dst)
+{
+	asm volatile(
+		"vld1.32 {d1[1]},  [%1] 		\n\t" //
+		"vld1.32 {d0},     [%2]         \n\t" //
+		"vmov.f32 s2, s1                \n\t" // q0 = (v1y, v1z, v1z, v1x)
+
+		"vld1.32 {d2[1]},  [%3]	    	\n\t" //
+		"vld1.32 {d3},     [%4]         \n\t" //
+		"vmov.f32 s4, s7          		\n\t" // q1 = (v2z, v2x, v2y, v2z)
+
+		"vmul.f32 d4, d0, d2  			\n\t" // x = v1y * v2z, y = v1z * v2x
+		"vmls.f32 d4, d1, d3  			\n\t" // x -= v1z * v2y, y-= v1x - v2z
+
+		"vmul.f32 d5, d3, d1[1]			\n\t" // z = v1x * v2y
+		"vmls.f32 d5, d0, d2[1]         \n\t" // z-= v1y * vx
+
+		"vst1.32 {d4}, 	  [%0]!    		\n\t" // V[x, y]
+		"vst1.32 {d5[0]}, [%0]     		\n\t" // V[z]
+		:
+		: "r"(dst), "r"(v1), "r"((v1+1)), "r"(v2), "r"((v2+1))
+		: "q0", "q1", "q2", "memory"
+	);
+}
+
+}

+ 12 - 319
gameplay/src/Matrix.cpp

@@ -1,8 +1,11 @@
 #include "Base.h"
 #include "Matrix.h"
 #include "Quaternion.h"
+#include "MathUtil.h"
 
+#ifndef MATRIX_SIZE
 #define MATRIX_SIZE     ( sizeof(float) * 16 )
+#endif
 
 namespace gameplay
 {
@@ -361,22 +364,7 @@ void Matrix::add(float scalar, Matrix* dst)
 {
     GP_ASSERT(dst);
 
-    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;
+    MathUtil::addMatrix(m, scalar, dst->m);
 }
 
 void Matrix::add(const Matrix& m)
@@ -388,44 +376,7 @@ 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"
-		"vld1.32 	{q2, q3}, 	[%1]! 	\n\t"
-    	"vld1.32 	{q8, q9}, 	[%2]! 	\n\t"
-		"vld1.32 	{q10, q11}, [%2]! 	\n\t"
-		"vadd.f32   q12, q0, q8 		\n\t"
-    	"vadd.f32   q13, q1, q9			\n\t"
-    	"vadd.f32   q14, q2, q10		\n\t"
-    	"vadd.f32   q15, q3, q11		\n\t"
-    	"vst1.32    {q12, q13}, [%0]!   \n\t"
-		"vst1.32    {q14, q15}, [%0]!   \n\t"
-		:
-        : "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);
 }
 
 bool Matrix::decompose(Vector3* scale, Quaternion* rotation, Vector3* translation) const
@@ -697,45 +648,7 @@ 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"
-    	"vld1.32	{q4-q5},  		[%1]!    	\n\t"
-		"vld1.32	{q6-q7},  		[%1]!		\n\t"
-
-    	"vmul.f32 	q8, q4, d0[0]    			\n\t"
-    	"vmul.f32 	q9, q5, d0[0]    			\n\t"
-		"vmul.f32 	q10, q6, d0[0]    			\n\t"
-		"vmul.f32 	q11, q7, d0[0]   		 	\n\t"
-
-    	"vst1.32 	{q8-q9},   		[%2]! 		\n\t"
-		"vst1.32 	{q10-q11}, 		[%2]!		\n\t"
-		:
-		: "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);
 }
 
 void Matrix::multiply(const Matrix& m)
@@ -747,71 +660,7 @@ 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"         // load first eight elements of matrix 0
-		"vld1.32     {d20 - d23}, [%1]!   \n\t"         // load second eight elements of matrix 0
-		"vld1.32     {d0 - d3}, [%2]!     \n\t"         // load first eight elements of matrix 1
-		"vld1.32     {d4 - d7}, [%2]!     \n\t"         // load second eight elements of matrix 1
-
-		"vmul.f32    q12, q8, d0[0]     \n\t"         // rslt col0  = (mat0 col0) * (mat1 col0 elt0)
-		"vmul.f32    q13, q8, d2[0]     \n\t"         // rslt col1  = (mat0 col0) * (mat1 col1 elt0)
-		"vmul.f32    q14, q8, d4[0]     \n\t"         // rslt col2  = (mat0 col0) * (mat1 col2 elt0)
-		"vmul.f32    q15, q8, d6[0]     \n\t"         // rslt col3  = (mat0 col0) * (mat1 col3 elt0)
-
-		"vmla.f32    q12, q9, d0[1]     \n\t"         // rslt col0 += (mat0 col1) * (mat1 col0 elt1)
-		"vmla.f32    q13, q9, d2[1]     \n\t"         // rslt col1 += (mat0 col1) * (mat1 col1 elt1)
-		"vmla.f32    q14, q9, d4[1]     \n\t"         // rslt col2 += (mat0 col1) * (mat1 col2 elt1)
-		"vmla.f32    q15, q9, d6[1]     \n\t"         // rslt col3 += (mat0 col1) * (mat1 col3 elt1)
-
-		"vmla.f32    q12, q10, d1[0]    \n\t"         // rslt col0 += (mat0 col2) * (mat1 col0 elt2)
-		"vmla.f32    q13, q10, d3[0]    \n\t"         // rslt col1 += (mat0 col2) * (mat1 col1 elt2)
-		"vmla.f32    q14, q10, d5[0]    \n\t"         // rslt col2 += (mat0 col2) * (mat1 col2 elt2)
-		"vmla.f32    q15, q10, d7[0]    \n\t"         // rslt col3 += (mat0 col2) * (mat1 col2 elt2)
-
-		"vmla.f32    q12, q11, d1[1]    \n\t"         // rslt col0 += (mat0 col3) * (mat1 col0 elt3)
-		"vmla.f32    q13, q11, d3[1]    \n\t"         // rslt col1 += (mat0 col3) * (mat1 col1 elt3)
-		"vmla.f32    q14, q11, d5[1]    \n\t"         // rslt col2 += (mat0 col3) * (mat1 col2 elt3)
-		"vmla.f32    q15, q11, d7[1]    \n\t"         // rslt col3 += (mat0 col3) * (mat1 col3 elt3)
-
-		"vst1.32    {d24 - d27}, [%0]!    \n\t"         // store first eight elements of result
-		"vst1.32    {d28 - d31}, [%0]!    \n\t"         // store second eight elements of result
-        
-        : // 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);
 }
 
 void Matrix::negate()
@@ -823,44 +672,7 @@ 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);
 }
 
 void Matrix::rotate(const Quaternion& q)
@@ -1005,45 +817,7 @@ 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"
-		"vld1.32 	{q2, q3}, 	[%1]! 	\n\t"
-    	"vld1.32 	{q8, q9}, 	[%2]! 	\n\t"
-		"vld1.32 	{q10, q11}, [%2]! 	\n\t"
-		"vsub.f32   q12, q0, q8 		\n\t"
-    	"vsub.f32   q13, q1, q9			\n\t"
-    	"vsub.f32   q14, q2, q10		\n\t"
-    	"vsub.f32   q15, q3, q11		\n\t"
-    	"vst1.32    {q12, q13}, [%0]!   \n\t"
-		"vst1.32    {q14, q15}, [%0]!   \n\t"
-		:
-        : "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);
 }
 
 void Matrix::transformPoint(Vector3* point) const
@@ -1072,37 +846,7 @@ void Matrix::transformVector(float x, float y, float z, float w, Vector3* dst) c
 {
     GP_ASSERT(dst);
 
-#ifdef USE_NEON
-
-    asm volatile(
-    	"vld1.32	{d0[0]},		[%0]	\n\t"	// load x
-		"vld1.32	{d0[1]},    	[%1]	\n\t"	// load y
-		"vld1.32	{d1[0]},		[%2]	\n\t"	// load z
-		"vld1.32	{d1[1]},		[%3]	\n\t"	// load w
-		"vld1.32	{d18 - d21},	[%4]!	\n\t"	// load first 8 elements of matrix m0-m7
-		"vld1.32	{d22 - d25},	[%4]!	\n\t"	// load second 8 elements of matrix m8-m15
-
-    	"vmul.f32 q13,  q9, d0[0]			\n\t"	// Q5 =  (m0-m3)*x
-    	"vmla.f32 q13, q10, d0[1]      		\n\t"	// Q5 +=  (m4-m7)*y
-    	"vmla.f32 q13, q11, d1[0]      		\n\t"	// Q5 +=  (m8-m11)*z
-		"vmla.f32 q13, q12, d1[1]      		\n\t"	// Q5 +=  (m12-m15)*w
-
-    	"vst1.32 {d26[0]}, [%5]!        	\n\t"	// store dst->x
-		"vst1.32 {d26[1]}, [%5]!        	\n\t"	// store dst->y
-		"vst1.32 {d27[0]}, [%5]!        	\n\t"	// store dst->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);
 }
 
 void Matrix::transformVector(Vector4* vector) const
@@ -1115,33 +859,7 @@ void Matrix::transformVector(const Vector4& vector, Vector4* dst) const
 {
     GP_ASSERT(dst);
 
-#ifdef USE_NEON
-
-    asm volatile
-    (
-    		"vld1.32	{d0, d1}, [%1]		\n\t"   //Q0 = v (x, y, z, w)
-    		"vld1.32    {d18 - d21}, [%0]!  \n\t"   //Q1 = M (m0-m7)
-    		"vld1.32    {d22 - d25}, [%0]!  \n\t"   //Q2 = M (m8-m15)
-
-    		"vmul.f32   q13, q9, d0[0]      \n\t"   //Q5 =  Q0*Q0[0]
-    		"vmla.f32   q13, q10, d0[1]     \n\t"   //Q5 += Q1*Q0[1]
-    		"vmla.f32   q13, q11, d1[0]     \n\t"   //Q5 += Q2*Q0[2]
-    		"vmla.f32   q13, q12, d1[1]     \n\t"   //Q5 += Q3*Q0[3]
-    		"vst1.32    {d26, d27}, [%2]    \n\t"   //Q4 = m+12
-    		:
-    		: "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);
 }
 
 void Matrix::translate(float x, float y, float z)
@@ -1175,32 +893,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"
-		"vld4.32 {d0[1], d2[1], d4[1], d6[1]}, [%0]! \n\t"
-		"vld4.32 {d1[0], d3[0], d5[0], d7[0]}, [%0]! \n\t"
-		"vld4.32 {d1[1], d3[1], d5[1], d7[1]}, [%0]! \n\t"
-
-		"vst1.32 {q0-q1}, [%1]! \n\t"
-		"vst1.32 {q2-q3}, [%1]! \n\t"
-    	:
-    	: "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::transposeMatrix(m, dst->m);
 }
 
 }

+ 2 - 2
gameplay/src/Plane.cpp

@@ -63,7 +63,7 @@ void Plane::intersection(const Plane& p1, const Plane& p2, const Plane& p3, Vect
                 p1._normal.z * p3._normal.y) + p3._normal.x * (p1._normal.y * p2._normal.z - p1._normal.z * p2._normal.y);
 
     // If the determinant is zero, then the planes do not all intersect.
-    if (det == 0.0f)
+    if (fabs(det) <= MATH_EPSILON)
         return;
 
     // Create 3 points, one on each plane.
@@ -161,7 +161,7 @@ float Plane::intersects(const Frustum& frustum) const
 float Plane::intersects(const Plane& plane) const
 {
     // Check if the planes intersect.
-    if (!isParallel(plane))
+    if ((_normal.x == plane._normal.x && _normal.y == plane._normal.y && _normal.z == plane._normal.z) || !isParallel(plane))
     {
         return Plane::INTERSECTS_INTERSECTING;
     }

+ 87 - 59
gameplay/src/Properties.cpp

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

+ 2 - 2
gameplay/src/RadioButton.cpp

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

+ 2 - 2
gameplay/src/RadioButton.h

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

+ 38 - 7
gameplay/src/SceneLoader.cpp

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

+ 1 - 0
gameplay/src/SceneLoader.h

@@ -105,6 +105,7 @@ private:
     static void splitURL(const std::string& url, std::string* file, std::string* id);
     
     
+    static std::map<std::string, Properties*> _propertiesFromFile;      // Holds the properties object for a given file.
     static std::map<std::string, Properties*> _properties;              // Holds the properties object for a given URL.
     static std::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.

+ 2 - 2
gameplay/src/Slider.cpp

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

+ 2 - 2
gameplay/src/Slider.h

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

+ 2 - 2
gameplay/src/TextBox.cpp

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

+ 2 - 2
gameplay/src/TextBox.h

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

+ 11 - 0
gameplay/src/Texture.cpp

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

+ 3 - 12
gameplay/src/Vector3.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "Vector3.h"
+#include "MathUtil.h"
 
 namespace gameplay
 {
@@ -165,24 +166,14 @@ void Vector3::clamp(const Vector3& v, const Vector3& min, const Vector3& max, Ve
 
 void Vector3::cross(const Vector3& v)
 {
-    float tx = (y * v.z) - (z * v.y);
-    float ty = (z * v.x) - (x * v.z);
-    float tz = (x * v.y) - (y * v.x);
-    x = tx;
-    y = ty;
-    z = tz;
+    cross(*this, v, this);
 }
 
 void Vector3::cross(const Vector3& v1, const Vector3& v2, Vector3* dst)
 {
     GP_ASSERT(dst);
 
-    float x = (v1.y * v2.z) - (v1.z * v2.y);
-    float y = (v1.z * v2.x) - (v1.x * v2.z);
-    float z = (v1.x * v2.y) - (v1.y * v2.x);
-    dst->x = x;
-    dst->y = y;
-    dst->z = z;
+    MathUtil::crossVector3((const float*)&v1, (const float*)&v2, (float*)dst);
 }
 
 float Vector3::distance(const Vector3& v) const

+ 3 - 3
gameplay/src/VerticalLayout.cpp

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

+ 1 - 1
gameplay/src/VerticalLayout.h

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

+ 1 - 0
gameplay/src/gameplay.h

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