Przeglądaj źródła

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

setaylor 13 lat temu
rodzic
commit
dcd551f71d
55 zmienionych plików z 3963 dodań i 1719 usunięć
  1. 3 2
      gameplay-encoder/gameplay-encoder.vcxproj
  2. 9 6
      gameplay-encoder/gameplay-encoder.vcxproj.filters
  3. 16 20
      gameplay-encoder/gameplay-encoder.xcodeproj/project.pbxproj
  4. 1346 0
      gameplay-encoder/src/Curve.cpp
  5. 484 0
      gameplay-encoder/src/Curve.h
  6. 36 0
      gameplay-encoder/src/Curve.inl
  7. 3 0
      gameplay-encoder/src/FileIO.h
  8. 1 1
      gameplay-encoder/src/MeshSkin.cpp
  9. 2 2
      gameplay-encoder/src/Reference.h
  10. 2 0
      gameplay/gameplay.vcxproj
  11. 6 0
      gameplay/gameplay.vcxproj.filters
  12. 18 0
      gameplay/gameplay.xcodeproj/project.pbxproj
  13. 3 0
      gameplay/src/AbsoluteLayout.h
  14. 10 39
      gameplay/src/Animation.cpp
  15. 4 26
      gameplay/src/Animation.h
  16. 1 303
      gameplay/src/AnimationController.cpp
  17. 2 119
      gameplay/src/AnimationController.h
  18. 275 6
      gameplay/src/AnimationTarget.cpp
  19. 110 2
      gameplay/src/AnimationTarget.h
  20. 1 1
      gameplay/src/Button.cpp
  21. 14 5
      gameplay/src/Button.h
  22. 8 8
      gameplay/src/CheckBox.cpp
  23. 13 5
      gameplay/src/CheckBox.h
  24. 26 2
      gameplay/src/Container.cpp
  25. 23 4
      gameplay/src/Container.h
  26. 104 136
      gameplay/src/Control.cpp
  27. 131 63
      gameplay/src/Control.h
  28. 7 1
      gameplay/src/Curve.cpp
  29. 26 11
      gameplay/src/Curve.h
  30. 19 26
      gameplay/src/Form.cpp
  31. 19 14
      gameplay/src/Form.h
  32. 3 3
      gameplay/src/Game.cpp
  33. 2 2
      gameplay/src/Game.h
  34. 7 10
      gameplay/src/Label.cpp
  35. 11 1
      gameplay/src/Label.h
  36. 1 0
      gameplay/src/Material.h
  37. 63 0
      gameplay/src/Node.cpp
  38. 8 0
      gameplay/src/Node.h
  39. 2 1
      gameplay/src/Package.cpp
  40. 3 4
      gameplay/src/PlatformMacOS.mm
  41. 2 2
      gameplay/src/PlatformQNX.cpp
  42. 1 1
      gameplay/src/PlatformWin32.cpp
  43. 14 8
      gameplay/src/RadioButton.cpp
  44. 39 2
      gameplay/src/RadioButton.h
  45. 1 1
      gameplay/src/SceneLoader.cpp
  46. 15 15
      gameplay/src/Slider.cpp
  47. 46 0
      gameplay/src/Slider.h
  48. 80 70
      gameplay/src/TextBox.cpp
  49. 60 4
      gameplay/src/TextBox.h
  50. 2 466
      gameplay/src/Theme.cpp
  51. 158 315
      gameplay/src/Theme.h
  52. 471 0
      gameplay/src/ThemeStyle.cpp
  53. 223 0
      gameplay/src/ThemeStyle.h
  54. 6 12
      gameplay/src/VerticalLayout.cpp
  55. 23 0
      gameplay/src/VerticalLayout.h

+ 3 - 2
gameplay-encoder/gameplay-encoder.vcxproj

@@ -11,12 +11,12 @@
     </ProjectConfiguration>
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="..\gameplay\src\Curve.cpp" />
     <ClCompile Include="src\Animation.cpp" />
     <ClCompile Include="src\AnimationChannel.cpp" />
     <ClCompile Include="src\Base.cpp" />
     <ClCompile Include="src\BoundingVolume.cpp" />
     <ClCompile Include="src\Camera.cpp" />
+    <ClCompile Include="src\Curve.cpp" />
     <ClCompile Include="src\EncoderArguments.cpp" />
     <ClCompile Include="src\DAEChannelTarget.cpp" />
     <ClCompile Include="src\DAEOptimizer.cpp" />
@@ -55,12 +55,12 @@
     <ClCompile Include="src\VertexElement.cpp" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="..\gameplay\src\Curve.h" />
     <ClInclude Include="src\Animation.h" />
     <ClInclude Include="src\AnimationChannel.h" />
     <ClInclude Include="src\Base.h" />
     <ClInclude Include="src\BoundingVolume.h" />
     <ClInclude Include="src\Camera.h" />
+    <ClInclude Include="src\Curve.h" />
     <ClInclude Include="src\EncoderArguments.h" />
     <ClInclude Include="src\DAEChannelTarget.h" />
     <ClInclude Include="src\DAEOptimizer.h" />
@@ -98,6 +98,7 @@
     <ClInclude Include="src\VertexElement.h" />
   </ItemGroup>
   <ItemGroup>
+    <None Include="src\Curve.inl" />
     <None Include="src\Quaternion.inl" />
     <None Include="src\Vector2.inl" />
     <None Include="src\Vector3.inl" />

+ 9 - 6
gameplay-encoder/gameplay-encoder.vcxproj.filters

@@ -19,9 +19,6 @@
     <ClCompile Include="src\Camera.cpp">
       <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="..\gameplay\src\Curve.cpp">
-      <Filter>src</Filter>
-    </ClCompile>
     <ClCompile Include="src\DAEChannelTarget.cpp">
       <Filter>src</Filter>
     </ClCompile>
@@ -127,6 +124,9 @@
     <ClCompile Include="src\VertexElement.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\Curve.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\VertexElement.h">
@@ -150,9 +150,6 @@
     <ClInclude Include="src\Camera.h">
       <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="..\gameplay\src\Curve.h">
-      <Filter>src</Filter>
-    </ClInclude>
     <ClInclude Include="src\DAEChannelTarget.h">
       <Filter>src</Filter>
     </ClInclude>
@@ -252,6 +249,9 @@
     <ClInclude Include="src\Vertex.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\Curve.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\Vector2.inl">
@@ -266,6 +266,9 @@
     <None Include="src\Quaternion.inl">
       <Filter>src</Filter>
     </None>
+    <None Include="src\Curve.inl">
+      <Filter>src</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="src">

+ 16 - 20
gameplay-encoder/gameplay-encoder.xcodeproj/project.pbxproj

@@ -8,9 +8,9 @@
 
 /* Begin PBXBuildFile section */
 		42475D7C14720ECE00610A6A /* libdom.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42475D7B14720ECE00610A6A /* libdom.a */; };
+		4251B12C152D044B002F6199 /* Curve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4251B128152D044B002F6199 /* Curve.cpp */; };
 		42783423148D6F7500A6E27F /* FBXSceneEncoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4278341E148D6F7500A6E27F /* FBXSceneEncoder.cpp */; };
 		4283905914896E6C00E2B2F5 /* BoundingVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4283905714896E6C00E2B2F5 /* BoundingVolume.cpp */; };
-		4283906314896F1600E2B2F5 /* Curve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4283906114896F1600E2B2F5 /* Curve.cpp */; };
 		42C8EE0A14724CD700E43619 /* Animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42C8EDB714724CD700E43619 /* Animation.cpp */; };
 		42C8EE0B14724CD700E43619 /* AnimationChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42C8EDB914724CD700E43619 /* AnimationChannel.cpp */; };
 		42C8EE0C14724CD700E43619 /* Animations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42C8EDBB14724CD700E43619 /* Animations.cpp */; };
@@ -57,7 +57,7 @@
 		42C8EE3B1472DAAE00E43619 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 42C8EE3A1472DAAE00E43619 /* libbz2.dylib */; };
 		42D277591472EFA700D867A4 /* libpcre.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42D277571472EFA700D867A4 /* libpcre.a */; };
 		42D2775A1472EFA700D867A4 /* libpcrecpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42D277581472EFA700D867A4 /* libpcrecpp.a */; };
-		42DDE88515191CDA00D9B550 /* libpng.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42DDE88415191CDA00D9B550 /* libpng.a */; };
+		5BCD0643152CFC3C0071FAB5 /* libpng.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BCD0642152CFC3C0071FAB5 /* libpng.a */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -75,6 +75,10 @@
 /* Begin PBXFileReference section */
 		42475CE6147208A000610A6A /* gameplay-encoder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "gameplay-encoder"; sourceTree = BUILT_PRODUCTS_DIR; };
 		42475D7B14720ECE00610A6A /* libdom.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libdom.a; path = "../external-deps/collada-dom/lib/macos/libdom.a"; sourceTree = "<group>"; };
+		4251B128152D044B002F6199 /* Curve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Curve.cpp; path = src/Curve.cpp; sourceTree = SOURCE_ROOT; };
+		4251B129152D044B002F6199 /* Curve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Curve.h; path = src/Curve.h; sourceTree = SOURCE_ROOT; };
+		4251B12A152D044B002F6199 /* Curve.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Curve.inl; path = src/Curve.inl; sourceTree = SOURCE_ROOT; };
+		4251B12B152D044B002F6199 /* Quaternion.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Quaternion.inl; path = src/Quaternion.inl; sourceTree = SOURCE_ROOT; };
 		4278341E148D6F7500A6E27F /* FBXSceneEncoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FBXSceneEncoder.cpp; path = src/FBXSceneEncoder.cpp; sourceTree = SOURCE_ROOT; };
 		4278341F148D6F7500A6E27F /* FBXSceneEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBXSceneEncoder.h; path = src/FBXSceneEncoder.h; sourceTree = SOURCE_ROOT; };
 		42783420148D6F7500A6E27F /* Vector2.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Vector2.inl; path = src/Vector2.inl; sourceTree = SOURCE_ROOT; };
@@ -82,8 +86,6 @@
 		42783422148D6F7500A6E27F /* Vector4.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Vector4.inl; path = src/Vector4.inl; sourceTree = SOURCE_ROOT; };
 		4283905714896E6C00E2B2F5 /* BoundingVolume.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BoundingVolume.cpp; path = src/BoundingVolume.cpp; sourceTree = SOURCE_ROOT; };
 		4283905814896E6C00E2B2F5 /* BoundingVolume.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BoundingVolume.h; path = src/BoundingVolume.h; sourceTree = SOURCE_ROOT; };
-		4283906114896F1600E2B2F5 /* Curve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Curve.cpp; path = ../gameplay/src/Curve.cpp; sourceTree = "<group>"; };
-		4283906214896F1600E2B2F5 /* Curve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Curve.h; path = ../gameplay/src/Curve.h; sourceTree = "<group>"; };
 		42C8EDB714724CD700E43619 /* Animation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Animation.cpp; path = src/Animation.cpp; sourceTree = SOURCE_ROOT; };
 		42C8EDB814724CD700E43619 /* Animation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Animation.h; path = src/Animation.h; sourceTree = SOURCE_ROOT; };
 		42C8EDB914724CD700E43619 /* AnimationChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AnimationChannel.cpp; path = src/AnimationChannel.cpp; sourceTree = SOURCE_ROOT; };
@@ -169,7 +171,7 @@
 		42C8EE3A1472DAAE00E43619 /* libbz2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbz2.dylib; path = usr/lib/libbz2.dylib; sourceTree = SDKROOT; };
 		42D277571472EFA700D867A4 /* libpcre.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpcre.a; path = "../external-deps/pcre/lib/macos/libpcre.a"; sourceTree = "<group>"; };
 		42D277581472EFA700D867A4 /* libpcrecpp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpcrecpp.a; path = "../external-deps/pcre/lib/macos/libpcrecpp.a"; sourceTree = "<group>"; };
-		42DDE88415191CDA00D9B550 /* libpng.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng.a; path = "../external-deps/libpng/lib/macos/libpng.a"; sourceTree = "<group>"; };
+		5BCD0642152CFC3C0071FAB5 /* libpng.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng.a; path = "../external-deps/libpng/lib/macos/libpng.a"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -177,7 +179,6 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				42DDE88515191CDA00D9B550 /* libpng.a in Frameworks */,
 				42D277591472EFA700D867A4 /* libpcre.a in Frameworks */,
 				42D2775A1472EFA700D867A4 /* libpcrecpp.a in Frameworks */,
 				42C8EE351472B60100E43619 /* libfreetype.a in Frameworks */,
@@ -185,6 +186,7 @@
 				42C8EE3B1472DAAE00E43619 /* libbz2.dylib in Frameworks */,
 				42C8EE391472DAA300E43619 /* libz.dylib in Frameworks */,
 				42C8EE371472D7E700E43619 /* libxml2.dylib in Frameworks */,
+				5BCD0643152CFC3C0071FAB5 /* libpng.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -194,7 +196,6 @@
 		42475CDB147208A000610A6A = {
 			isa = PBXGroup;
 			children = (
-				4283906414896F3000E2B2F5 /* gameplay */,
 				42475CE9147208A000610A6A /* src */,
 				427D4F44147DC9080076760E /* Libraries */,
 				42475CE7147208A000610A6A /* Products */,
@@ -224,6 +225,9 @@
 				4283905814896E6C00E2B2F5 /* BoundingVolume.h */,
 				42C8EDBF14724CD700E43619 /* Camera.cpp */,
 				42C8EDC014724CD700E43619 /* Camera.h */,
+				4251B128152D044B002F6199 /* Curve.cpp */,
+				4251B129152D044B002F6199 /* Curve.h */,
+				4251B12A152D044B002F6199 /* Curve.inl */,
 				42C8EDC314724CD700E43619 /* DAEChannelTarget.cpp */,
 				42C8EDC414724CD700E43619 /* DAEChannelTarget.h */,
 				42C8EDC514724CD700E43619 /* DAEOptimizer.cpp */,
@@ -273,6 +277,7 @@
 				42C8EDF114724CD700E43619 /* Object.h */,
 				42C8EDF214724CD700E43619 /* Quaternion.cpp */,
 				42C8EDF314724CD700E43619 /* Quaternion.h */,
+				4251B12B152D044B002F6199 /* Quaternion.inl */,
 				42C8EDF414724CD700E43619 /* Reference.cpp */,
 				42C8EDF514724CD700E43619 /* Reference.h */,
 				42C8EDF614724CD700E43619 /* ReferenceTable.cpp */,
@@ -306,6 +311,7 @@
 		427D4F44147DC9080076760E /* Libraries */ = {
 			isa = PBXGroup;
 			children = (
+				5BCD0642152CFC3C0071FAB5 /* libpng.a */,
 				42D277571472EFA700D867A4 /* libpcre.a */,
 				42D277581472EFA700D867A4 /* libpcrecpp.a */,
 				42C8EE3A1472DAAE00E43619 /* libbz2.dylib */,
@@ -313,20 +319,10 @@
 				42C8EE361472D7E700E43619 /* libxml2.dylib */,
 				42C8EE341472B60100E43619 /* libfreetype.a */,
 				42475D7B14720ECE00610A6A /* libdom.a */,
-				42DDE88415191CDA00D9B550 /* libpng.a */,
 			);
 			name = Libraries;
 			sourceTree = "<group>";
 		};
-		4283906414896F3000E2B2F5 /* gameplay */ = {
-			isa = PBXGroup;
-			children = (
-				4283906114896F1600E2B2F5 /* Curve.cpp */,
-				4283906214896F1600E2B2F5 /* Curve.h */,
-			);
-			name = gameplay;
-			sourceTree = "<group>";
-		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -418,8 +414,8 @@
 				42C8EE3214724CD700E43619 /* Vertex.cpp in Sources */,
 				42C8EE3314724CD700E43619 /* VertexElement.cpp in Sources */,
 				4283905914896E6C00E2B2F5 /* BoundingVolume.cpp in Sources */,
-				4283906314896F1600E2B2F5 /* Curve.cpp in Sources */,
 				42783423148D6F7500A6E27F /* FBXSceneEncoder.cpp in Sources */,
+				4251B12C152D044B002F6199 /* Curve.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -523,8 +519,8 @@
 					"\"$(SRCROOT)/../external-deps/freetype2/lib/macos\"",
 					"\"$(SRCROOT)/../external-deps/collada-dom/lib/macos\"",
 					"\"$(SRCROOT)/../external-deps/pcre/lib/macos\"",
-					"\"$(SRCROOT)/../../../Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug\"",
 					"\"$(SRCROOT)/../external-deps/libpng/lib/macos\"",
+					"\"$(SRCROOT)\"",
 				);
 				MACH_O_TYPE = mh_execute;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -555,8 +551,8 @@
 					"\"$(SRCROOT)/../external-deps/freetype2/lib/macos\"",
 					"\"$(SRCROOT)/../external-deps/collada-dom/lib/macos\"",
 					"\"$(SRCROOT)/../external-deps/pcre/lib/macos\"",
-					"\"$(SRCROOT)/../../../Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug\"",
 					"\"$(SRCROOT)/../external-deps/libpng/lib/macos\"",
+					"\"$(SRCROOT)\"",
 				);
 				MACH_O_TYPE = mh_execute;
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 1346 - 0
gameplay-encoder/src/Curve.cpp

@@ -0,0 +1,1346 @@
+// Purposely not including Base.h here, or any other gameplay dependencies
+// so this class can be reused between gameplay and gameplay-encoder.
+#include "Curve.h"
+#include "Quaternion.h"
+#include <cassert>
+#include <cmath>
+#include <memory>
+
+using std::memcpy;
+using std::fabs;
+using std::sqrt;
+using std::cos;
+using std::sin;
+using std::exp;
+using std::strcmp;
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef MATH_PI
+#define MATH_PI 3.14159265358979323846f
+#endif
+
+#ifndef MATH_PIOVER2 
+#define MATH_PIOVER2 1.57079632679489661923f
+#endif
+
+#ifndef MATH_PIX2
+#define MATH_PIX2 6.28318530717958647693f
+#endif
+
+// Object deletion macro
+#ifndef SAFE_DELETE
+#define SAFE_DELETE(x) \
+    if (x) \
+    { \
+        delete x; \
+        x = NULL; \
+    }
+#endif
+
+// Array deletion macro
+#ifndef SAFE_DELETE_ARRAY
+#define SAFE_DELETE_ARRAY(x) \
+    if (x) \
+    { \
+        delete[] x; \
+        x = NULL; \
+    }
+#endif
+
+
+namespace gameplay
+{
+
+Curve::Curve(unsigned int pointCount, unsigned int componentCount)
+    : _pointCount(pointCount), _componentCount(componentCount), _componentSize(sizeof(float)*componentCount), _quaternionOffset(NULL), _points(NULL)
+{
+    _points = new Point[_pointCount];
+    for (unsigned int i = 0; i < _pointCount; i++)
+    {
+        _points[i].time = 0.0f;
+        _points[i].value = new float[_componentCount];
+        _points[i].inValue = new float[_componentCount];
+        _points[i].outValue = new float[_componentCount];
+        _points[i].type = LINEAR;
+    }
+    _points[_pointCount - 1].time = 1.0f;
+}
+
+Curve::~Curve()
+{
+    SAFE_DELETE_ARRAY(_points);
+    SAFE_DELETE_ARRAY(_quaternionOffset);
+}
+
+Curve::Point::Point()
+    : time(0.0f), value(NULL), inValue(NULL), outValue(NULL)
+{
+}
+
+Curve::Point::~Point()
+{
+    SAFE_DELETE_ARRAY(value);
+    SAFE_DELETE_ARRAY(inValue);
+    SAFE_DELETE_ARRAY(outValue);
+}
+
+unsigned int Curve::getPointCount() const
+{
+    return _pointCount;
+}
+
+unsigned int Curve::getComponentCount() const
+{
+    return _componentCount;
+}
+
+float Curve::getStartTime() const
+{
+    return _points[0].time;
+}
+
+float Curve::getEndTime() const
+{
+    return _points[_pointCount-1].time;
+}
+
+void Curve::setPoint(unsigned int index, float time, float* value, InterpolationType type)
+{
+    setPoint(index, time, value, type, NULL, NULL);
+}
+
+void Curve::setPoint(unsigned int index, float time, float* value, InterpolationType type, float* inValue, float* outValue)
+{
+    assert(index < _pointCount && time >= 0.0f && time <= 1.0f && !(index == 0 && time != 0.0f) && !(index == _pointCount - 1 && time != 1.0f));
+
+    _points[index].time = time;
+    _points[index].type = type;
+
+    if (value)
+        memcpy(_points[index].value, value, _componentSize);
+
+    if (inValue)
+        memcpy(_points[index].inValue, inValue, _componentSize);
+
+    if (outValue)
+        memcpy(_points[index].outValue, outValue, _componentSize);
+}
+
+void Curve::setTangent(unsigned int index, InterpolationType type, float* inValue, float* outValue)
+{
+    assert(index < _pointCount);
+
+    _points[index].type = type;
+
+    if (inValue)
+        memcpy(_points[index].inValue, inValue, _componentSize);
+
+    if (outValue)
+        memcpy(_points[index].outValue, outValue, _componentSize);
+}
+
+void Curve::evaluate(float time, float* dst) const
+{
+    assert(dst && time >= 0 && time <= 1.0f);
+
+    // Check if we are at or beyond the bounds of the curve.
+    if (time <= _points[0].time)
+    {
+        memcpy(dst, _points[0].value, _componentSize);
+        return;
+    }
+    else if (time >= _points[_pointCount - 1].time)
+    {
+        memcpy(dst, _points[_pointCount - 1].value, _componentSize);
+        return;
+    }
+
+    // Locate the points we are interpolating between using a binary search.
+    unsigned int index = determineIndex(time);
+    
+    Point* from = _points + index;
+    Point* to = _points + (index + 1);
+
+    // Calculate the fractional time between the two points.
+    float scale = (to->time - from->time);
+    float t = (time - from->time) / scale;
+
+    // Calculate the value of the curve discretely if appropriate.
+    switch (from->type)
+    {
+        case BEZIER:
+        {
+            interpolateBezier(t, from, to, dst);
+            return;
+        }
+        case BSPLINE:
+        {
+            Point* c0;
+            Point* c1;
+            if (index == 0)
+            {
+                c0 = from;
+            }
+            else
+            {
+                c0 = (_points + index - 1);
+            }
+            
+            if (index == _pointCount - 2)
+            {
+                c1 = to;
+            }
+            else
+            {
+                c1 = (_points + index + 2);
+            }
+            interpolateBSpline(t, c0, from, to, c1, dst);
+            return;
+        }
+        case FLAT:
+        {
+            interpolateHermiteFlat(t, from, to, dst);
+            return;
+        }
+        case HERMITE:
+        {
+            interpolateHermite(t, from, to, dst);
+            return;
+        }
+        case LINEAR:
+        {
+            // Can just break here because linear formula follows switch
+            break;
+        }
+        case SMOOTH:
+        {
+            interpolateHermiteSmooth(t, index, from, to, dst);
+            return;
+        }
+        case STEP:
+        {
+            memcpy(dst, from->value, _componentSize);
+            return;
+        }
+        case QUADRATIC_IN:
+        {
+            t *= t;
+            break;
+        }
+        case QUADRATIC_OUT:
+        {
+            t *= -(t - 2.0f);
+            break;
+        }
+        case QUADRATIC_IN_OUT:
+        {
+            float tx2 = t * 2.0f;
+
+            if (tx2 < 1.0f)
+                t = 0.5f * (tx2 * tx2);
+            else
+            {
+                float temp = tx2 - 1.0f;
+                t = 0.5f * (-( temp * (temp - 2.0f)) + 1.0f);
+            }
+            break;
+        }
+        case QUADRATIC_OUT_IN:
+        {
+            if (t < 0.5f)
+            {
+                t = 2.0f * t * (1.0f - t);
+            }
+            else
+            {
+                t = 1.0f + 2.0f * t * (t - 1.0f);
+            }
+            break;
+        }
+        case CUBIC_IN:
+        {
+            t *= t * t;
+            break;
+        }
+        case CUBIC_OUT:
+        {
+            t--;
+            t = t * t * t + 1;
+            break;
+        }
+        case CUBIC_IN_OUT:
+        {
+            if ((t *= 2.0f) < 1.0f)
+            {
+                t = t * t * t * 0.5f;
+            }
+            else
+            {
+                t -= 2.0f;
+                t = (t * t * t + 2.0f) * 0.5f;
+            }
+            break;
+        }
+        case CUBIC_OUT_IN:
+        {
+            t = (2.0f * t - 1.0f);
+            t = (t * t * t + 1) * 0.5f;
+            break;
+        }
+        case QUARTIC_IN:
+        {
+            t *= t * t * t;
+            break;
+        }
+        case QUARTIC_OUT:
+        {
+            t--;
+            t = -(t * t * t * t) + 1.0f;
+            break;
+        }
+        case QUARTIC_IN_OUT:
+        {
+            t *= 2.0f;
+            if (t < 1.0f)
+            {
+                t = 0.5f * t * t * t * t;
+            }
+            else
+            {
+                t -= 2.0f;
+                t = -0.5f * (t * t * t * t - 2.0f);
+            }
+            break;
+        }
+        case QUARTIC_OUT_IN:
+        {
+            t = 2.0f * t - 1.0f;
+            if (t < 0.0f)
+            {
+                t = 0.5f * (-(t * t) * t * t + 1.0f);
+            }
+            else
+            {
+                t = 0.5f * (t * t * t * t + 1.0f);
+            }
+            break;
+        }
+        case QUINTIC_IN:
+        {
+            t *= t * t * t * t;
+            break;
+        }
+        case QUINTIC_OUT:
+        {
+            t--;
+            t = t * t * t * t * t + 1.0f;
+            break;
+        }
+        case QUINTIC_IN_OUT:
+        {
+            t *= 2.0f;
+            if (t < 1.0f)
+            {
+                t = 0.5f * t * t * t * t * t;
+            }
+            else
+            {
+                t -= 2.0f;
+                t = 0.5f * (t * t * t * t * t + 2.0f);
+            }
+            break;
+        }
+        case QUINTIC_OUT_IN:
+        {
+            t = 2.0f * t - 1.0f;
+            t = 0.5f * (t * t * t * t * t + 1.0f);
+            break;
+        }
+        case SINE_IN:
+        {
+            t = -(cos(t * MATH_PIOVER2) - 1.0f);
+            break;
+        }
+        case SINE_OUT:
+        {
+            t = sin(t * MATH_PIOVER2);
+            break;
+        }
+        case SINE_IN_OUT:
+        {
+            t = -0.5f * (cos(MATH_PI * t) - 1.0f);
+            break;
+        }
+        case SINE_OUT_IN:
+        {
+            if (t < 0.5f)
+            {
+                t = 0.5f * sin(MATH_PI * t);
+            }
+            else
+            {
+                t = -0.5f * cos(MATH_PIOVER2 * (2.0f * t - 1.0f)) + 1.0f;
+            }
+            break;
+        }
+        case EXPONENTIAL_IN:
+        {
+            if (t != 0.0f)
+            {
+                t = exp(10.0f * (t - 1.0f));
+            }
+            break;
+        }
+        case EXPONENTIAL_OUT:
+        {
+            if (t != 1.0f)
+            {
+                t = -exp(-10.0f * t) + 1.0f;
+            }
+            break;
+        }
+        case EXPONENTIAL_IN_OUT:
+        {
+            if (t != 0.0f && t != 1.0f)
+            {
+                if (t < 0.5f)
+                {
+                    t = 0.5f * exp(10.0f * (2.0f * t - 1.0f));
+                }
+                else
+                {
+                    t = -0.5f * exp(10.0f * (-2.0f * t + 1.0f)) + 1.0f;
+                }
+            }
+            break;
+        }
+        case EXPONENTIAL_OUT_IN:
+        {
+            if (t != 0.0f && t != 1.0f)
+            {
+                if (t < 0.5f)
+                {
+                    t = -0.5f * exp(-20.0f * t) + 0.5f;
+                }
+                else
+                {
+                    t = 0.5f * exp(20.0f * (t - 1.0f)) + 0.5f;
+                }
+            }
+            break;
+        }
+        case CIRCULAR_IN:
+        {
+            t = -(sqrt(1.0f - t * t) - 1.0f);
+            break;
+        }
+        case CIRCULAR_OUT:
+        {
+            t--;
+            t = sqrt(1.0f - t * t);
+            break;
+        }
+        case CIRCULAR_IN_OUT:
+        {
+            t *= 2.0f;
+            if (t < 1.0f)
+            {
+                t = 0.5f * (-sqrt((1.0f - t * t)) + 1.0f);
+            }
+            else
+            {
+                t -= 2.0f;
+                t = 0.5f * (sqrt((1.0f - t * t)) + 1.0f);
+            }
+            break;
+        }
+        case CIRCULAR_OUT_IN:
+        {
+            t = 2.0f * t - 1.0f;
+            if (t < 0.0f)
+            {
+                t = 0.5f * sqrt(1.0f - t * t);
+            }
+            else
+            {
+                t = 0.5f * (2.0f - sqrt(1.0f - t * t));
+            }
+            break;
+        }
+        case ELASTIC_IN:
+        {
+            if (t != 0.0f && t != 1.0f)
+            {
+                t = t - 1.0f;
+                t = -1.0f * ( exp(10.0f * t) * sin( (t - 0.075f) * MATH_PIX2 / 0.3f ) );
+            }
+            break;
+        }
+        case ELASTIC_OUT:
+        {
+            if (t != 0.0f && t != 1.0f)
+            {
+                t = exp(-10.0f * t) * sin((t - 0.075f) * MATH_PIX2 / 0.3f) + 1.0f;
+            }
+            break;
+        }
+        case ELASTIC_IN_OUT:
+        {
+            if (t != 0.0f && t != 1.0f)
+            {
+                t = 2.0f * t - 1.0f;
+                if (t < 0.0f)
+                {
+                    t = -0.5f * (exp((10 * t)) * sin(((t - 0.1125f) * MATH_PIX2 / 0.45f)));
+                }
+                else
+                {
+                    t = 0.5f * exp((-10 * t)) * sin(((t - 0.1125f) * MATH_PIX2 / 0.45f)) + 1.0f;
+                }
+            }
+            break;
+        }
+        case ELASTIC_OUT_IN:
+        {
+            if (t != 0.0f && t != 1.0f)
+            {
+                t *= 2.0f;
+                if (t < 1.0f)
+                {
+                    t = 0.5f * (exp((-10 * t)) * sin(((t - 0.1125f) * (MATH_PIX2) / 0.45f))) + 0.5f;
+                }
+                else
+                {
+                    t = 0.5f * (exp((10 *(t - 2))) * sin(((t - 0.1125f) * (MATH_PIX2) / 0.45f))) + 0.5f;
+                }
+            }
+            break;
+        }
+        case OVERSHOOT_IN:
+        {
+            t = t * t * (2.70158f * t - 1.70158f);
+            break;
+        }
+        case OVERSHOOT_OUT:
+        {
+            t--;
+            t = t * t * (2.70158f * t + 1.70158f) + 1;
+            break;
+        }
+        case OVERSHOOT_IN_OUT:
+        {
+            t *= 2.0f;
+            if (t < 1.0f)
+            {
+                t = 0.5f * t * t * (3.5949095f * t - 2.5949095f);
+            }
+            else
+            {
+                t -= 2.0f;
+                t = 0.5f * (t * t * (3.5949095f * t + 2.5949095f) + 2.0f);
+            }
+            break;
+        }
+        case OVERSHOOT_OUT_IN:
+        {
+            t = 2.0f * t - 1.0f;
+            if (t < 0.0f)
+            {
+                t = 0.5f * (t * t * (3.5949095f * t + 2.5949095f) + 1.0f);
+            }
+            else
+            {
+                t = 0.5f * (t * t * (3.5949095f * t - 2.5949095f) + 1.0f);
+            }
+            break;
+        }
+        case BOUNCE_IN:
+        {
+            t = 1.0f - t;
+
+            if (t < 0.36363636363636365f)
+            {
+                t = 7.5625f * t * t;
+            }
+            else if (t < 0.7272727272727273f)
+            {
+                t -= 0.5454545454545454f;
+                t = 7.5625f * t * t + 0.75f;
+            }
+            else if (t < 0.9090909090909091f)
+            {
+                t -= 0.8181818181818182f;
+                t = 7.5625f * t * t + 0.9375f;
+            }
+            else
+            {
+                t -= 0.9545454545454546f;
+                t = 7.5625f * t * t + 0.984375f;
+            }
+
+            t = 1.0f - t;
+            break;
+        }
+        case BOUNCE_OUT:
+        {
+            if (t < 0.36363636363636365f)
+            {
+                t = 7.5625f * t * t;
+            }
+            else if (t < 0.7272727272727273f)
+            {
+                t -= 0.5454545454545454f;
+                t = 7.5625f * t * t + 0.75f;
+            }
+            else if (t < 0.9090909090909091f)
+            {
+                t -= 0.8181818181818182f;
+                t = 7.5625f * t * t + 0.9375f;
+            }
+            else
+            {
+                t -= 0.9545454545454546f;
+                t = 7.5625f * t * t + 0.984375f;
+            }
+            break;
+        }
+        case BOUNCE_IN_OUT:
+        {
+            if (t < 0.5f)
+            {
+                t = 1.0f - t * 2.0f;
+
+                if (t < 0.36363636363636365f)
+                {
+                    t = 7.5625f * t * t;
+                }
+                else if (t < 0.7272727272727273f)
+                {
+                    t -= 0.5454545454545454f;
+                    t = 7.5625f * t * t + 0.75f;
+                }
+                else if (t < 0.9090909090909091f)
+                {
+                    t -= 0.8181818181818182f;
+                    t = 7.5625f * t * t + 0.9375f;
+                }
+                else
+                {
+                    t -= 0.9545454545454546f;
+                    t = 7.5625f * t * t + 0.984375f;
+                }
+
+                t = (1.0f - t) * 0.5f;
+            }
+            else
+            {
+                t = t * 2.0f - 1.0f;
+                if (t < 0.36363636363636365f)
+                {
+                    t = 7.5625f * t * t;
+                }
+                else if (t < 0.7272727272727273f)
+                {
+                    t -= 0.5454545454545454f;
+                    t = 7.5625f * t * t + 0.75f;
+                }
+                else if (t < 0.9090909090909091f)
+                {
+                    t -= 0.8181818181818182f;
+                    t = 7.5625f * t * t + 0.9375f;
+                }
+                else
+                {
+                    t -= 0.9545454545454546f;
+                    t = 7.5625f * t * t + 0.984375f;
+                }
+
+                t = 0.5f * t + 0.5f;
+            }
+            break;
+        }
+        case BOUNCE_OUT_IN:
+        {
+            if (t < 0.1818181818f)
+            {
+                t = 15.125f * t * t;
+            }
+            else if (t < 0.3636363636f)
+            {
+                t = 1.5f + (-8.250000001f + 15.125f * t) * t;
+            }
+            else if (t < 0.4545454546f)
+            {
+                t = 3.0f + (-12.375f + 15.125f * t) * t;
+            }
+            else if (t < 0.5f)
+            {
+                t = 3.9375f + (-14.4375f + 15.125f * t) * t;
+            }
+            else if (t <= 0.5454545455f)
+            {
+                t = -3.625000004f + (15.81250001f - 15.125f * t) * t;
+            }
+            else if (t <= 0.6363636365f)
+            {
+                t = -4.75f + (17.875f - 15.125f * t) * t;
+            }
+            else if (t <= 0.8181818180f)
+            {
+                t = -7.374999995f + (21.99999999f - 15.125f * t) * t;
+            }
+            else
+            {
+                t = -14.125f + (30.25f - 15.125f * t) * t;
+            }
+            break;
+        }
+    }
+
+    interpolateLinear(t, from, to, dst);
+}
+
+float Curve::lerp(float t, float from, float to)
+{
+    return lerpInl(t, from, to);
+}
+
+void Curve::setQuaternionOffset(unsigned int offset)
+{
+    assert(offset <= (_componentCount - 4));
+
+    if (!_quaternionOffset)
+        _quaternionOffset = new unsigned int[1];
+    
+    *_quaternionOffset = offset;
+}
+
+void Curve::interpolateBezier(float s, Point* from, Point* to, float* dst) const
+{
+    float s_2 = s * s;
+    float eq0 = 1 - s;
+    float eq0_2 = eq0 * eq0;
+    float eq1 = eq0_2 * eq0;
+    float eq2 = 3 * s * eq0_2;
+    float eq3 = 3 * s_2 * eq0;
+    float eq4 = s_2 * s;
+
+    float* fromValue = from->value;
+    float* toValue = to->value;
+    float* outValue = from->outValue;
+    float* inValue = to->inValue;
+
+
+    if (!_quaternionOffset)
+    {
+        for (unsigned int i = 0; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = bezier(eq1, eq2, eq3, eq4, fromValue[i], outValue[i], toValue[i], inValue[i]);
+        }
+    }
+    else
+    {
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
+        unsigned int i = 0;
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = bezier(eq1, eq2, eq3, eq4, fromValue[i], outValue[i], toValue[i], inValue[i]);
+        }
+
+        // Handle quaternion component.
+        float interpTime = bezier(eq1, eq2, eq3, eq4, from->time, outValue[i], to->time, inValue[i]);
+        interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
+        
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = bezier(eq1, eq2, eq3, eq4, fromValue[i], outValue[i], toValue[i], inValue[i]);
+        }
+    }
+}
+
+void Curve::interpolateBSpline(float s, Point* c0, Point* c1, Point* c2, Point* c3, float* dst) const
+{   
+    float s_2 = s * s;
+    float s_3 = s_2 * s;
+    float eq0 = (-s_3 + 3 * s_2 - 3 * s + 1) / 6.0f;
+    float eq1 = (3 * s_3 - 6 * s_2 + 4) / 6.0f;
+    float eq2 = (-3 * s_3 + 3 * s_2 + 3 * s + 1) / 6.0f;
+    float eq3 = s_3 / 6.0f;
+
+    float* c0Value = c0->value;
+    float* c1Value = c1->value;
+    float* c2Value = c2->value;
+    float* c3Value = c3->value;
+
+    if (!_quaternionOffset)
+    {
+        for (unsigned int i = 0; i < _componentCount; i++)
+        {
+            if (c1Value[i] == c2Value[i])
+                dst[i] = c1Value[i];
+            else
+                dst[i] = bspline(eq0, eq1, eq2, eq3, c0Value[i], c1Value[i], c2Value[i], c3Value[i]);
+        }
+    }
+    else
+    {
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
+        unsigned int i = 0;
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (c1Value[i] == c2Value[i])
+                dst[i] = c1Value[i];
+            else
+                dst[i] = bspline(eq0, eq1, eq2, eq3, c0Value[i], c1Value[i], c2Value[i], c3Value[i]);
+        }
+
+        // Handle quaternion component.
+        float interpTime;
+        if (c0->time == c1->time)
+            interpTime = bspline(eq0, eq1, eq2, eq3, -c0->time, c1->time, c2->time, c3->time);
+        else if (c2->time == c3->time)
+            interpTime = bspline(eq0, eq1, eq2, eq3, c0->time, c1->time, c2->time, -c3->time); 
+        else
+            interpTime = bspline(eq0, eq1, eq2, eq3, c0->time, c1->time, c2->time, c3->time);
+        interpolateQuaternion(s, (c1Value + i) , (c2Value + i), (dst + i));
+            
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
+        {
+            if (c1Value[i] == c2Value[i])
+                dst[i] = c1Value[i];
+            else
+                dst[i] = bspline(eq0, eq1, eq2, eq3, c0Value[i], c1Value[i], c2Value[i], c3Value[i]);
+        }
+    }
+}
+
+void Curve::interpolateHermite(float s, Point* from, Point* to, float* dst) const
+{
+    // Calculate the hermite basis functions.
+    float s_2 = s * s;                   // t^2
+    float s_3 = s_2 * s;                 // t^3
+    float h00 = 2 * s_3 - 3 * s_2 + 1;   // basis function 0
+    float h01 = -2 * s_3 + 3 * s_2;      // basis function 1
+    float h10 = s_3 - 2 * s_2 + s;       // basis function 2
+    float h11 = s_3 - s_2;               // basis function 3
+
+    float* fromValue = from->value;
+    float* toValue = to->value;
+    float* outValue = from->outValue;
+    float* inValue = to->inValue;
+
+    if (!_quaternionOffset)
+    {
+        for (unsigned int i = 0; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermite(h00, h01, h10, h11, fromValue[i], outValue[i], toValue[i], inValue[i]);
+        }
+    }
+    else
+    {
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
+        unsigned int i = 0;
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermite(h00, h01, h10, h11, fromValue[i], outValue[i], toValue[i], inValue[i]);
+        }
+
+        // Handle quaternion component.
+        float interpTime = hermite(h00, h01, h10, h11, from->time, outValue[i], to->time, inValue[i]);
+        interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
+        
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermite(h00, h01, h10, h11, fromValue[i], outValue[i], toValue[i], inValue[i]);
+        }
+    }
+}
+
+void Curve::interpolateHermiteFlat(float s, Point* from, Point* to, float* dst) const
+{
+    // Calculate the hermite basis functions.
+    float s_2 = s * s;                   // t^2
+    float s_3 = s_2 * s;                 // t^3
+    float h00 = 2 * s_3 - 3 * s_2 + 1;   // basis function 0
+    float h01 = -2 * s_3 + 3 * s_2;      // basis function 1
+
+    float* fromValue = from->value;
+    float* toValue = to->value;
+
+    if (!_quaternionOffset)
+    {
+        for (unsigned int i = 0; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermiteFlat(h00, h01, fromValue[i], toValue[i]);
+        }
+    }
+    else
+    {
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
+        unsigned int i = 0;
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermiteFlat(h00, h01, fromValue[i], toValue[i]);
+        }
+
+        // Handle quaternion component.
+        float interpTime = hermiteFlat(h00, h01, from->time, to->time);
+        interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
+        
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = hermiteFlat(h00, h01, fromValue[i], toValue[i]);
+        }
+    }
+}
+
+void Curve::interpolateHermiteSmooth(float s, unsigned int index, Point* from, Point* to, float* dst) const
+{
+    // Calculate the hermite basis functions.
+    float s_2 = s * s;                   // t^2
+    float s_3 = s_2 * s;                 // t^3
+    float h00 = 2 * s_3 - 3 * s_2 + 1;   // basis function 0
+    float h01 = -2 * s_3 + 3 * s_2;      // basis function 1
+    float h10 = s_3 - 2 * s_2 + s;       // basis function 2
+    float h11 = s_3 - s_2;               // basis function 3
+
+    float inValue;
+    float outValue;
+
+    float* fromValue = from->value;
+    float* toValue = to->value;
+
+    if (!_quaternionOffset)
+    {
+        for (unsigned int i = 0; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+            {
+                dst[i] = fromValue[i];
+            }
+            else
+            {
+                if (index == 0)
+                {
+                    outValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    outValue = (toValue[i] - (from - 1)->value[i]) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
+                }
+
+                if (index == _pointCount - 2)
+                {
+                    inValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    inValue = ((to + 1)->value[i] - fromValue[i]) * ((to->time - from->time) / ((to + 1)->time - from->time));
+                }
+
+                dst[i] = hermiteSmooth(h00, h01, h10, h11, fromValue[i], outValue, toValue[i], inValue);
+            }
+        }
+    }
+    else
+    {
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
+        unsigned int i = 0;
+        for (i = 0; i < quaternionOffset; i++)
+        {   
+            if (fromValue[i] == toValue[i])
+            {
+                dst[i] = fromValue[i];
+            }
+            else
+            {    
+                if (index == 0)
+                {
+                    outValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    outValue = (toValue[i] - (from - 1)->value[i]) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
+                }
+
+                if (index == _pointCount - 2)
+                {
+                    inValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    inValue = ((to + 1)->value[i] - fromValue[i]) * ((to->time - from->time) / ((to + 1)->time - from->time));
+                }
+
+                dst[i] = hermiteSmooth(h00, h01, h10, h11, fromValue[i], outValue, toValue[i], inValue);
+            }
+        }
+
+        // Handle quaternion component.
+        if (index == 0)
+        {
+            outValue = to->time - from->time;
+        }
+        else
+        {
+            outValue = (to->time - (from - 1)->time) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
+        }
+
+        if (index == _pointCount - 2)
+        {
+            inValue = to->time - from->time;
+        }
+        else
+        {
+            inValue = ((to + 1)->time - from->time) * ((to->time - from->time) / ((to + 1)->time - from->time));
+        }
+
+        float interpTime = hermiteSmooth(h00, h01, h10, h11, from->time, outValue, to->time, inValue);
+        interpolateQuaternion(interpTime, (fromValue + i), (toValue + i), (dst + i));
+        
+        // Handle remaining components (if any) as scalars
+        for (i += 4; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+            {
+                dst[i] = fromValue[i];
+            }
+            else
+            {
+                // Interpolate as scalar.
+                if (index == 0)
+                {
+                    outValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    outValue = (toValue[i] - (from - 1)->value[i]) * ((from->time - (from - 1)->time) / (to->time - (from - 1)->time));
+                }
+
+                if (index == _pointCount - 2)
+                {
+                    inValue = toValue[i] - fromValue[i];
+                }
+                else
+                {
+                    inValue = ((to + 1)->value[i] - fromValue[i]) * ((to->time - from->time) / ((to + 1)->time - from->time));
+                }
+
+                dst[i] = hermiteSmooth(h00, h01, h10, h11, fromValue[i], outValue, toValue[i], inValue);
+            }
+        }
+    }
+}
+
+void Curve::interpolateLinear(float s, Point* from, Point* to, float* dst) const
+{
+    float* fromValue = from->value;
+    float* toValue = to->value;
+
+    if (!_quaternionOffset)
+    {
+        for (unsigned int i = 0; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = lerpInl(s, fromValue[i], toValue[i]);
+        }
+    }
+    else
+    {
+        // Interpolate any values up to the quaternion offset as scalars.
+        unsigned int quaternionOffset = *_quaternionOffset;
+        unsigned int i = 0;
+        for (i = 0; i < quaternionOffset; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = lerpInl(s, fromValue[i], toValue[i]);
+        }
+
+        // Handle quaternion component.
+        interpolateQuaternion(s, (fromValue + i), (toValue + i), (dst + i));
+        
+        // handle any remaining components as scalars
+        for (i += 4; i < _componentCount; i++)
+        {
+            if (fromValue[i] == toValue[i])
+                dst[i] = fromValue[i];
+            else
+                dst[i] = lerpInl(s, fromValue[i], toValue[i]);
+        }
+    }
+}
+
+void Curve::interpolateQuaternion(float s, float* from, float* to, float* dst) const
+{
+    // Evaluate.
+    if (s >= 0)
+    {
+        Quaternion::slerp(from[0], from[1], from[2], from[3], to[0], to[1], to[2], to[3], s, dst, dst + 1, dst + 2, dst + 3);
+    }
+    else
+        Quaternion::slerp(to[0], to[1], to[2], to[3], from[0], from[1], from[2], from[3], s, dst, dst + 1, dst + 2, dst + 3);
+
+    //((Quaternion*) dst)->normalize();
+}
+
+int Curve::determineIndex(float time) const
+{
+    unsigned int min = 0;
+    unsigned int max = _pointCount - 1;
+    unsigned int mid = 0;
+
+    // Do a binary search to determine the index.
+    do 
+    {
+        mid = (min + max) >> 1;
+
+        if (time >= _points[mid].time && time <= _points[mid + 1].time)
+            return mid;
+        else if (time < _points[mid].time)
+            max = mid - 1;
+        else
+            min = mid + 1;
+    } while (min <= max);
+    
+    // We should never hit this!
+    return -1;
+}
+
+int Curve::getInterpolationType(const char* curveId)
+{
+    if (strcmp(curveId, "BEZIER") == 0)
+    {
+        return Curve::BEZIER;
+    }
+    else if (strcmp(curveId, "BSPLINE") == 0)
+    {
+        return Curve::BSPLINE;
+    }
+    else if (strcmp(curveId, "FLAT") == 0)
+    {
+        return Curve::FLAT;
+    }
+    else if (strcmp(curveId, "HERMITE") == 0)
+    {
+        return Curve::HERMITE;
+    }
+    else if (strcmp(curveId, "LINEAR") == 0)
+    {
+        return Curve::LINEAR;
+    }
+    else if (strcmp(curveId, "SMOOTH") == 0)
+    {
+        return Curve::SMOOTH;
+    }
+    else if (strcmp(curveId, "STEP") == 0)
+    {
+        return Curve::STEP;
+    }
+    else if (strcmp(curveId, "QUADRATIC_IN") == 0)
+    {
+        return Curve::QUADRATIC_IN;
+    }
+    else if (strcmp(curveId, "QUADRATIC_OUT") == 0)
+    {
+        return Curve::QUADRATIC_OUT;
+    }
+    else if (strcmp(curveId, "QUADRATIC_IN_OUT") == 0)
+    {
+        return Curve::QUADRATIC_IN_OUT;
+    }
+    else if (strcmp(curveId, "QUADRATIC_OUT_IN") == 0)
+    {
+        return Curve::QUADRATIC_OUT_IN;
+    }
+    else if (strcmp(curveId, "CUBIC_IN") == 0)
+    {
+        return Curve::CUBIC_IN;
+    }
+    else if (strcmp(curveId, "CUBIC_OUT") == 0)
+    {
+        return Curve::CUBIC_OUT;
+    }
+    else if (strcmp(curveId, "CUBIC_IN_OUT") == 0)
+    {
+        return Curve::CUBIC_IN_OUT;
+    }
+    else if (strcmp(curveId, "CUBIC_OUT_IN") == 0)
+    {
+        return Curve::CUBIC_OUT_IN;
+    }
+    else if (strcmp(curveId, "QUARTIC_IN") == 0)
+    {
+        return Curve::QUARTIC_IN;
+    }
+    else if (strcmp(curveId, "QUARTIC_OUT") == 0)
+    {
+        return Curve::QUARTIC_OUT;
+    }
+    else if (strcmp(curveId, "QUARTIC_IN_OUT") == 0)
+    {
+        return Curve::QUARTIC_IN_OUT;
+    }
+    else if (strcmp(curveId, "QUARTIC_OUT_IN") == 0)
+    {
+        return Curve::QUARTIC_OUT_IN;
+    }
+    else if (strcmp(curveId, "QUINTIC_IN") == 0)
+    {
+        return Curve::QUINTIC_IN;
+    }
+    else if (strcmp(curveId, "QUINTIC_OUT") == 0)
+    {
+        return Curve::QUINTIC_OUT;
+    }
+    else if (strcmp(curveId, "QUINTIC_IN_OUT") == 0)
+    {
+        return Curve::QUINTIC_IN_OUT;
+    }
+    else if (strcmp(curveId, "QUINTIC_OUT_IN") == 0)
+    {
+        return Curve::QUINTIC_OUT_IN;
+    }
+    else if (strcmp(curveId, "SINE_IN") == 0)
+    {
+        return Curve::SINE_IN;
+    }
+    else if (strcmp(curveId, "SINE_OUT") == 0)
+    {
+        return Curve::SINE_OUT;
+    }
+    else if (strcmp(curveId, "SINE_IN_OUT") == 0)
+    {
+        return Curve::SINE_IN_OUT;
+    }
+    else if (strcmp(curveId, "SINE_OUT_IN") == 0)
+    {
+        return Curve::SINE_OUT_IN;
+    }
+    else if (strcmp(curveId, "EXPONENTIAL_IN") == 0)
+    {
+        return Curve::EXPONENTIAL_IN;
+    }
+    else if (strcmp(curveId, "EXPONENTIAL_OUT") == 0)
+    {
+        return Curve::EXPONENTIAL_OUT;
+    }
+    else if (strcmp(curveId, "EXPONENTIAL_IN_OUT") == 0)
+    {
+        return Curve::EXPONENTIAL_IN_OUT;
+    }
+    else if (strcmp(curveId, "EXPONENTIAL_OUT_IN") == 0)
+    {
+        return Curve::EXPONENTIAL_OUT_IN;
+    }
+    else if (strcmp(curveId, "CIRCULAR_IN") == 0)
+    {
+        return Curve::CIRCULAR_IN;
+    }
+    else if (strcmp(curveId, "CIRCULAR_OUT") == 0)
+    {
+        return Curve::CIRCULAR_OUT;
+    }
+    else if (strcmp(curveId, "CIRCULAR_IN_OUT") == 0)
+    {
+        return Curve::CIRCULAR_IN_OUT;
+    }
+    else if (strcmp(curveId, "CIRCULAR_OUT_IN") == 0)
+    {
+        return Curve::CIRCULAR_OUT_IN;
+    }
+    else if (strcmp(curveId, "ELASTIC_IN") == 0)
+    {
+        return Curve::ELASTIC_IN;
+    }
+    else if (strcmp(curveId, "ELASTIC_OUT") == 0)
+    {
+        return Curve::ELASTIC_OUT;
+    }
+    else if (strcmp(curveId, "ELASTIC_IN_OUT") == 0)
+    {
+        return Curve::ELASTIC_IN_OUT;
+    }
+    else if (strcmp(curveId, "ELASTIC_OUT_IN") == 0)
+    {
+        return Curve::ELASTIC_OUT_IN;
+    }
+    else if (strcmp(curveId, "OVERSHOOT_IN") == 0)
+    {
+        return Curve::OVERSHOOT_IN;
+    }
+    else if (strcmp(curveId, "OVERSHOOT_OUT") == 0)
+    {
+        return Curve::OVERSHOOT_OUT;
+    }
+    else if (strcmp(curveId, "OVERSHOOT_IN_OUT") == 0)
+    {
+        return Curve::OVERSHOOT_IN_OUT;
+    }
+    else if (strcmp(curveId, "OVERSHOOT_OUT_IN") == 0)
+    {
+        return Curve::OVERSHOOT_OUT_IN;
+    }
+    else if (strcmp(curveId, "BOUNCE_IN") == 0)
+    {
+        return Curve::BOUNCE_IN;
+    }
+    else if (strcmp(curveId, "BOUNCE_OUT") == 0)
+    {
+        return Curve::BOUNCE_OUT;
+    }
+    else if (strcmp(curveId, "BOUNCE_IN_OUT") == 0)
+    {
+        return Curve::BOUNCE_IN_OUT;
+    }
+    else if (strcmp(curveId, "BOUNCE_OUT_IN") == 0)
+    {
+        return Curve::BOUNCE_OUT_IN;
+    }
+
+    return -1;
+}
+
+}

+ 484 - 0
gameplay-encoder/src/Curve.h

@@ -0,0 +1,484 @@
+#ifndef CURVE_H_
+#define CURVE_H_
+
+namespace gameplay
+{
+
+/**
+ * Represents an n-dimensional curve.
+ */
+class Curve
+{
+    friend class Animation;
+    friend class AnimationClip;
+    friend class AnimationController;
+    friend class MeshSkin;
+
+public:
+
+    /**
+     * Types of interpolation.
+     *
+     * Defines how the points in the curve are connected.
+     *
+     * Note: InterpolationType::BEZIER requires control points and InterpolationType::HERMITE requires tangents.
+     */
+    enum InterpolationType
+    {
+        /**
+         * Bezier Interpolation. 
+         *
+         * Requires that two control points are set for each segment.
+         */
+        BEZIER,
+
+        /**
+         * B-Spline Interpolation. 
+         *
+         * Uses the points as control points, and the curve is guaranteed to only pass through the
+         * first and last point.
+         */
+        BSPLINE,
+
+        /**
+         * Flat Interpolation. 
+         * 
+         * A form of Hermite interpolation that generates flat tangents for you. The tangents have a value equal to 0.
+         */
+        FLAT,
+
+        /**
+         * Hermite Interpolation. 
+         *
+         * Requires that two tangents for each segment.
+         */
+        HERMITE,
+
+        /**
+         * Linear Interpolation.
+         */
+        LINEAR,
+
+        /** 
+         * Smooth Interpolation. 
+         *
+         * A form of Hermite interpolation that generates tangents for each segment based on the points prior to and after the segment.
+         */
+        SMOOTH,
+
+        /**
+         * Discrete Interpolation.
+         */ 
+        STEP,
+
+        /**
+         * Quadratic-In Interpolation.
+         */
+        QUADRATIC_IN, 
+        
+        /**
+         * Quadratic-Out Interpolation.
+         */
+        QUADRATIC_OUT,
+
+        /**
+         * Quadratic-In-Out Interpolation.
+         */
+        QUADRATIC_IN_OUT,
+
+        /**
+         * Quadratic-Out-In Interpolation.
+         */
+        QUADRATIC_OUT_IN,
+
+        /**
+         * Cubic-In Interpolation.
+         */
+        CUBIC_IN,
+        
+        /**
+         * Cubic-Out Interpolation.
+         */
+        CUBIC_OUT,
+        
+        /**
+         * Cubic-In-Out Interpolation.
+         */
+        CUBIC_IN_OUT,
+        
+        /**
+         * Cubic-Out-In Interpolation.
+         */
+        CUBIC_OUT_IN,
+
+        /**
+         * Quartic-In Interpolation.
+         */
+        QUARTIC_IN,
+
+        /**
+         * Quartic-Out Interpolation.
+         */
+        QUARTIC_OUT,
+
+        /**
+         * Quartic-In-Out Interpolation.
+         */
+        QUARTIC_IN_OUT,
+
+        /**
+         * Quartic-Out-In Interpolation.
+         */
+        QUARTIC_OUT_IN,
+
+        /**
+         * Quintic-In Interpolation.
+         */
+        QUINTIC_IN,
+        
+        /**
+         * Quintic-Out Interpolation.
+         */
+        QUINTIC_OUT,
+        
+        /**
+         * Quintic-In-Out Interpolation.
+         */
+        QUINTIC_IN_OUT,
+        
+        /**
+         * Quintic-Out-In Interpolation.
+         */
+        QUINTIC_OUT_IN,
+        
+        /**
+         * Sine-In Interpolation.
+         */
+        SINE_IN,
+        
+        /**
+         * Sine-Out Interpolation.
+         */
+        SINE_OUT,
+        
+        /**
+         * Sine-In-Out Interpolation.
+         */
+        SINE_IN_OUT,
+        
+        /**
+         * Sine-Out-In Interpolation.
+         */
+        SINE_OUT_IN,
+
+        /**
+         * Exponential-In Interpolation.
+         */
+        EXPONENTIAL_IN,
+
+        /**
+         * Exponential-Out Interpolation.
+         */
+        EXPONENTIAL_OUT,
+
+        /**
+         * Exponential-In-Out Interpolation.
+         */
+        EXPONENTIAL_IN_OUT,
+
+        /**
+         * Exponential-Out-In Interpolation.
+         */
+        EXPONENTIAL_OUT_IN,
+
+        /**
+         * Circular-In Interpolation.
+         */
+        CIRCULAR_IN,
+
+        /**
+         * Circular-Out Interpolation.
+         */
+        CIRCULAR_OUT,
+
+        /**
+         * Circular-In-Out Interpolation.
+         */
+        CIRCULAR_IN_OUT,
+
+        /**
+         * Circular-Out-In Interpolation.
+         */
+        CIRCULAR_OUT_IN,
+
+        /**
+         * Elastic-In Interpolation.
+         */
+        ELASTIC_IN,
+
+        /**
+         * Elastic-Out Interpolation.
+         */
+        ELASTIC_OUT,
+
+        /**
+         * Elastic-In-Out Interpolation.
+         */
+        ELASTIC_IN_OUT,
+
+        /**
+         * Elastic-Out-In Interpolation.
+         */
+        ELASTIC_OUT_IN,
+
+        /**
+         * Overshoot-In Interpolation.
+         */
+        OVERSHOOT_IN,
+
+        /**
+         * Overshoot-Out Interpolation.
+         */
+        OVERSHOOT_OUT,
+
+        /**
+         * Overshoot-In-Out Interpolation.
+         */
+        OVERSHOOT_IN_OUT,
+
+        /**
+         * Overshoot-Out-In Interpolation.
+         */
+        OVERSHOOT_OUT_IN,
+
+        /**
+         * Bounce-In Interpolation.
+         */
+        BOUNCE_IN,
+
+        /**
+         * Bounce-Out Interpolation.
+         */
+        BOUNCE_OUT,
+
+        /**
+         * Bounce-In-Out Interpolation.
+         */
+        BOUNCE_IN_OUT,
+
+        /**
+         * Bounce-Out-In Interpolation.
+         */
+        BOUNCE_OUT_IN
+    };
+
+
+    /**
+     * Constructs a new curve and the specified parameters.
+     *
+     * @param pointCount The number of points in the curve.
+     * @param componentCount The number of float component values per key value.
+     */
+    Curve(unsigned int pointCount, unsigned int componentCount);
+
+    /**
+     * Destructor.
+     */
+    ~Curve();
+
+    /**
+     * Gets the number of points in the curve.
+     *
+     * @return The number of points in the curve.
+     */
+    unsigned int getPointCount() const;
+
+    /**
+     * Gets the number of float component values per points.
+     *
+     * @return The number of float component values per point.
+     */
+    unsigned int getComponentCount() const;
+
+    /**
+     * Returns the start time for the curve.
+     *
+     * @return The curve's start time.
+     */
+    float getStartTime() const;
+
+    /**
+     * Returns the end time for the curve.
+     *
+     * @return The curve's end time.
+     */
+    float getEndTime() const;
+
+    /**
+     * Sets the given point values on the curve the curve.
+     *
+     * @param index The index of the point.
+     * @param time The time for the key.
+     * @param value The point to add.
+     * @param type The curve interpolation type.
+     */
+    void setPoint(unsigned int index, float time, float* value, InterpolationType type);
+
+    /**
+     * Sets the given point on the curve for the specified index and the specified parameters.
+     *
+     * @param index The index of the point.
+     * @param time The time of the point within the curve.
+     * @param value The value of the point to copy the data from.
+     * @param type The curve interpolation type.
+     * @param inValue The tangent approaching the point.
+     * @param outValue The tangent leaving the point.
+     */
+    void setPoint(unsigned int index, float time, float* value, InterpolationType type, float* inValue, float* outValue);
+
+    /**
+     * Sets the tangents for a point on the curve specified by the index.
+     *
+     * @param index The index of the point.
+     * @param type The curve interpolation type.
+     * @param inValue The tangent approaching the point.
+     * @param outValue The tangent leaving the point.
+     */
+    void setTangent(unsigned int index, InterpolationType type, float* inValue, float* outValue);
+    
+    /**
+     * Evaluates the curve at the given position value (between 0.0 and 1.0 inclusive).
+     *
+     * @param time The position to evaluate the curve at.
+     * @param dst The evaluated value of the curve at the given time.
+     */
+    void evaluate(float time, float* dst) const;
+
+    /**
+     * Linear interpolation function.
+     */
+    static float lerp(float t, float from, float to);
+
+private:
+
+    /**
+     * Defines a single point within a curve.
+     */
+    class Point
+    {
+    public:
+
+        /** The time of the point within the curve. */
+        float time;
+        /** The value of the point. */
+        float* value;
+        /** The value of the tangent when approaching this point (from the previous point in the curve). */
+        float* inValue;
+        /** The value of the tangent when leaving this point (towards the next point in the curve). */
+        float* outValue;
+        /** The type of interpolation to use between this point and the next point. */
+        InterpolationType type;
+
+        /**
+         * Constructor.
+         */
+        Point();
+
+        /**
+         * Destructor.
+         */
+        ~Point();
+    };
+
+    /**
+     * Constructor.
+     */
+    Curve();
+
+    /**
+     * Constructor.
+     */
+    Curve(const Curve& copy);
+
+    /**
+     * Bezier interpolation function.
+     */
+    void interpolateBezier(float s, Point* from, Point* to, float* dst) const;
+
+    /**
+     * Bspline interpolation function.
+     */
+    void interpolateBSpline(float s, Point* c0, Point* c1, Point* c2, Point* c3, float* dst) const;
+
+    /**
+     * Hermite interpolation function.
+     */
+    void interpolateHermite(float s, Point* from, Point* to, float* dst) const;
+
+    /**
+     * Hermite interpolation function.
+     */
+    void interpolateHermiteFlat(float s, Point* from, Point* to, float* dst) const;
+
+    /**
+     * Hermite interpolation function.
+     */
+    void interpolateHermiteSmooth(float s, unsigned int index, Point* from, Point* to, float* dst) const;
+
+    /** 
+     * Linear interpolation function.
+     */ 
+    void interpolateLinear(float s, Point* from, Point* to, float* dst) const;
+
+    /**
+     * Quaternion interpolation function.
+     */
+    void interpolateQuaternion(float s, float* from, float* to, float* dst) const;
+    
+    /**
+     * Determines the current keyframe to interpolate from based on the specified time.
+     */ 
+    int determineIndex(float time) const;
+
+    /**
+     * Sets the offset for the beginning of a Quaternion piece of data within the curve's value span at the specified
+     * index. The next four components of data starting at the given index will be interpolated as a Quaternion.
+     * This function will assert an error if the given index is greater than the component size subtracted by the four components required
+     * to store a quaternion.
+     * 
+     * @param index The index of the Quaternion rotation data.
+     */
+    void setQuaternionOffset(unsigned int index);
+
+    /**
+     * Gets the InterpolationType value for the given string ID
+     *
+     * @param interpolationId The string representation of the InterpolationType
+     * @return the InterpolationType value; -1 if the string does not represent an InterpolationType.
+     */
+    static int getInterpolationType(const char* interpolationId);
+
+    unsigned int _pointCount;           // Number of points on the curve.
+    unsigned int _componentCount;       // Number of components on the curve.
+    unsigned int _componentSize;        // The component size (in bytes).
+    unsigned int* _quaternionOffset;    // Offset for the rotation component.
+    Point* _points;                     // The points on the curve.
+};
+
+inline static float bezier(float eq0, float eq1, float eq2, float eq3, float from, float out, float to, float in);
+
+inline static float bspline(float eq0, float eq1, float eq2, float eq3, float c0, float c1, float c2, float c3);
+
+inline static float hermite(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
+
+inline static float hermiteFlat(float h00, float h01, float from, float to);
+
+inline static float hermiteSmooth(float h00, float h01, float h10, float h11, float from, float out, float to, float in);
+
+inline static float lerpInl(float s, float from, float to);
+
+}
+
+#include "Curve.inl"
+
+#endif

+ 36 - 0
gameplay-encoder/src/Curve.inl

@@ -0,0 +1,36 @@
+
+
+namespace gameplay
+{
+
+inline float bezier(float eq0, float eq1, float eq2, float eq3, float from, float out, float to, float in)
+{
+    return from * eq0 + out * eq1 + in * eq2 + to * eq3;
+}
+
+inline float bspline(float eq0, float eq1, float eq2, float eq3, float c0, float c1, float c2, float c3)
+{
+    return c0 * eq0 + c1 * eq1 + c2 * eq2 + c3 * eq3;
+}
+
+inline float hermite(float h00, float h01, float h10, float h11, float from, float out, float to, float in)
+{
+    return h00 * from + h01 * to + h10 * out + h11 * in;
+}
+
+inline float hermiteFlat(float h00, float h01, float from, float to)
+{
+    return h00 * from + h01 * to;
+}
+
+inline float hermiteSmooth(float h00, float h01, float h10, float h11, float from, float out, float to, float in)
+{
+    return h00 * from + h01 * to + h10 * out + h11 * in;
+}
+
+inline float lerpInl(float s, float from, float to)
+{
+    return from + (to - from) * s;
+}
+
+}

+ 3 - 0
gameplay-encoder/src/FileIO.h

@@ -1,6 +1,9 @@
 #ifndef FILEIO_H_
 #define FILEIO_H_
 
+#include <cstdio>
+#include <list>
+#include <vector>
 
 #include "Vector2.h"
 #include "Vector3.h"

+ 1 - 1
gameplay-encoder/src/MeshSkin.cpp

@@ -6,7 +6,7 @@
 #include "GPBFile.h"
 #include "Animations.h"
 #include "Transform.h"
-#include "../../gameplay/src/Curve.h"
+#include "Curve.h"
 #include "Matrix.h"
 
 namespace gameplay

+ 2 - 2
gameplay-encoder/src/Reference.h

@@ -1,5 +1,5 @@
-#ifndef REF_H_
-#define REF_H_
+#ifndef REFERENCE_H_
+#define REFERENCE_H_
 
 #include "Object.h"
 

+ 2 - 0
gameplay/gameplay.vcxproj

@@ -94,6 +94,7 @@
     <ClCompile Include="src\TextBox.cpp" />
     <ClCompile Include="src\Texture.cpp" />
     <ClCompile Include="src\Theme.cpp" />
+    <ClCompile Include="src\ThemeStyle.cpp" />
     <ClCompile Include="src\Transform.cpp" />
     <ClCompile Include="src\Vector2.cpp" />
     <ClCompile Include="src\Vector3.cpp" />
@@ -183,6 +184,7 @@
     <ClInclude Include="src\TextBox.h" />
     <ClInclude Include="src\Texture.h" />
     <ClInclude Include="src\Theme.h" />
+    <ClInclude Include="src\ThemeStyle.h" />
     <ClInclude Include="src\TimeListener.h" />
     <ClInclude Include="src\Touch.h" />
     <ClInclude Include="src\Transform.h" />

+ 6 - 0
gameplay/gameplay.vcxproj.filters

@@ -273,6 +273,9 @@
     <ClCompile Include="src\PhysicsCollisionShape.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\ThemeStyle.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -542,6 +545,9 @@
     <ClInclude Include="src\ScreenDisplayer.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\ThemeStyle.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">

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

@@ -14,6 +14,12 @@
 		4208DEEC14A407B900D3C511 /* Keyboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 4208DEEB14A407B900D3C511 /* Keyboard.h */; };
 		4208DEEE14A407D500D3C511 /* Touch.h in Headers */ = {isa = PBXBuildFile; fileRef = 4208DEED14A407D500D3C511 /* Touch.h */; };
 		4234D99E14686C52003031B3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4234D99D14686C52003031B3 /* Cocoa.framework */; };
+		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 */; };
+		4251B134152D049B002F6199 /* ThemeStyle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4251B12F152D049B002F6199 /* ThemeStyle.cpp */; };
+		4251B135152D049B002F6199 /* ThemeStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 4251B130152D049B002F6199 /* ThemeStyle.h */; };
+		4251B136152D049B002F6199 /* ThemeStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 4251B130152D049B002F6199 /* ThemeStyle.h */; };
 		42554EA1152BC35C000ED910 /* PhysicsCollisionShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42554E9F152BC35C000ED910 /* PhysicsCollisionShape.cpp */; };
 		42554EA2152BC35C000ED910 /* PhysicsCollisionShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42554E9F152BC35C000ED910 /* PhysicsCollisionShape.cpp */; };
 		42554EA3152BC35C000ED910 /* PhysicsCollisionShape.h in Headers */ = {isa = PBXBuildFile; fileRef = 42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */; };
@@ -387,6 +393,9 @@
 		4208DEED14A407D500D3C511 /* Touch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Touch.h; path = src/Touch.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; };
+		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; };
 		42554E9F152BC35C000ED910 /* PhysicsCollisionShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PhysicsCollisionShape.cpp; path = src/PhysicsCollisionShape.cpp; sourceTree = SOURCE_ROOT; };
 		42554EA0152BC35C000ED910 /* PhysicsCollisionShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicsCollisionShape.h; path = src/PhysicsCollisionShape.h; sourceTree = SOURCE_ROOT; };
 		428390971489D6E800E2B2F5 /* SceneLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SceneLoader.cpp; path = src/SceneLoader.cpp; sourceTree = SOURCE_ROOT; };
@@ -839,6 +848,7 @@
 				42CD0E2E147D8FF50000361E /* Scene.h */,
 				428390971489D6E800E2B2F5 /* SceneLoader.cpp */,
 				428390981489D6E800E2B2F5 /* SceneLoader.h */,
+				4251B12E152D049B002F6199 /* ScreenDisplayer.h */,
 				5BD52646150F822A004C9099 /* Slider.cpp */,
 				5BD52647150F822A004C9099 /* Slider.h */,
 				42CD0E2F147D8FF50000361E /* SpriteBatch.cpp */,
@@ -852,6 +862,8 @@
 				5BD5264C150F822A004C9099 /* TimeListener.h */,
 				5BD5264A150F822A004C9099 /* Theme.cpp */,
 				5BD5264B150F822A004C9099 /* Theme.h */,
+				4251B12F152D049B002F6199 /* ThemeStyle.cpp */,
+				4251B130152D049B002F6199 /* ThemeStyle.h */,
 				4208DEED14A407D500D3C511 /* Touch.h */,
 				42CD0E35147D8FF50000361E /* Transform.cpp */,
 				42CD0E36147D8FF50000361E /* Transform.h */,
@@ -1044,6 +1056,8 @@
 				5BD52675150F8258004C9099 /* PhysicsCollisionObject.h in Headers */,
 				5BBE14401513E400003FB362 /* PhysicsGhostObject.h in Headers */,
 				42554EA3152BC35C000ED910 /* PhysicsCollisionShape.h in Headers */,
+				4251B131152D049B002F6199 /* ScreenDisplayer.h in Headers */,
+				4251B135152D049B002F6199 /* ThemeStyle.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1139,6 +1153,8 @@
 				5BC4E758150F843D00CBE1C0 /* VerticalLayout.h in Headers */,
 				5BBE14411513E400003FB362 /* PhysicsGhostObject.h in Headers */,
 				42554EA4152BC35C000ED910 /* PhysicsCollisionShape.h in Headers */,
+				4251B132152D049B002F6199 /* ScreenDisplayer.h in Headers */,
+				4251B136152D049B002F6199 /* ThemeStyle.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1292,6 +1308,7 @@
 				5BD52673150F8258004C9099 /* PhysicsCollisionObject.cpp in Sources */,
 				5BBE143E1513E400003FB362 /* PhysicsGhostObject.cpp in Sources */,
 				42554EA1152BC35C000ED910 /* PhysicsCollisionShape.cpp in Sources */,
+				4251B133152D049B002F6199 /* ThemeStyle.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1381,6 +1398,7 @@
 				5BC4E757150F843D00CBE1C0 /* VerticalLayout.cpp in Sources */,
 				5BBE143F1513E400003FB362 /* PhysicsGhostObject.cpp in Sources */,
 				42554EA2152BC35C000ED910 /* PhysicsCollisionShape.cpp in Sources */,
+				4251B134152D049B002F6199 /* ThemeStyle.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 3 - 0
gameplay/src/AbsoluteLayout.h

@@ -16,6 +16,7 @@ class AbsoluteLayout : public Layout
     friend class Container;
 
 public:
+
     /**
      * Get the type of this Layout.
      *
@@ -24,6 +25,7 @@ public:
     Layout::Type getType();
 
 protected:
+
     /**
      * Create an AbsoluteLayout.
      *
@@ -42,6 +44,7 @@ protected:
     void update(const Container* container);
 
 private:
+
     AbsoluteLayout();
     AbsoluteLayout(const AbsoluteLayout& copy);
     virtual ~AbsoluteLayout();

+ 10 - 39
gameplay/src/Animation.cpp

@@ -34,6 +34,8 @@ Animation::Animation(const char* id)
 
 Animation::~Animation()
 {
+    _channels.clear();
+
     if (_defaultClip)
     {
         if (_defaultClip->isClipStateBitSet(AnimationClip::CLIP_IS_PLAYING_BIT))
@@ -59,58 +61,31 @@ Animation::~Animation()
 }
 
 Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration)
-    : _animation(animation), _target(target), _propertyId(propertyId), _duration(duration)
+    : _animation(animation), _target(target), _propertyId(propertyId), _curve(curve), _duration(duration)
 {
-    _curveRef = Animation::CurveRef::create(curve);
     // get property component count, and ensure the property exists on the AnimationTarget by getting the property component count.
     assert(_target->getAnimationPropertyComponentCount(propertyId));
 
-    _animation->addRef();
-
     _target->addChannel(this);
 }
 
 Animation::Channel::Channel(const Channel& copy, Animation* animation, AnimationTarget* target)
-    : _animation(animation), _target(target), _propertyId(copy._propertyId), _duration(copy._duration)
-{
-    _curveRef = copy._curveRef;
-    _curveRef->addRef();
-
-    _animation->addRef();
+    : _animation(animation), _target(target), _propertyId(copy._propertyId), _curve(copy._curve), _duration(copy._duration)
+{
     _target->addChannel(this);
 }
 
 Animation::Channel::~Channel()
 {
-    SAFE_RELEASE(_curveRef);
+    SAFE_RELEASE(_curve);
     SAFE_RELEASE(_animation);
 }
 
 Curve* Animation::Channel::getCurve() const
-{
-    return _curveRef->getCurve();
-}
-
-Animation::CurveRef* Animation::CurveRef::create(Curve* curve)
-{
-    return new CurveRef(curve);
-}
-
-Curve* Animation::CurveRef::getCurve() const
 {
     return _curve;
 }
 
-Animation::CurveRef::CurveRef(Curve* curve)
-    : _curve(curve)
-{
-}
-
-Animation::CurveRef::~CurveRef()
-{
-    SAFE_DELETE(_curve);
-}
-
 const char* Animation::getId() const
 {
     return _id.c_str();
@@ -231,7 +206,7 @@ void Animation::createDefaultClip()
 }
 
 void Animation::createClips(Properties* animationProperties, unsigned int frameCount)
-{   
+{
     assert(animationProperties);
     
     Properties* pClip = animationProperties->getNextNamespace();
@@ -301,7 +276,7 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
     unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
     assert(propertyComponentCount > 0);
 
-    Curve* curve = new Curve(keyCount, propertyComponentCount);
+    Curve* curve = Curve::create(keyCount, propertyComponentCount);
     if (target->_targetType == AnimationTarget::TRANSFORM)
         setTransformRotationOffset(curve, propertyId);
 
@@ -337,7 +312,7 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
     unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
     assert(propertyComponentCount > 0);
 
-    Curve* curve = new Curve(keyCount, propertyComponentCount);
+    Curve* curve = Curve::create(keyCount, propertyComponentCount);
     if (target->_targetType == AnimationTarget::TRANSFORM)
         setTransformRotationOffset(curve, propertyId);
     
@@ -385,16 +360,13 @@ void Animation::removeChannel(Channel* channel)
         if (channel == chan) 
         {
             _channels.erase(itr);
-            itr = _channels.end();
+            return;
         }
         else
         {
             itr++;
         }
     }
-
-    if (_channels.empty())
-        _controller->destroyAnimation(this);
 }
 
 void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId)
@@ -416,7 +388,6 @@ void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId
 Animation* Animation::clone()
 {
     Animation* animation = new Animation(getId());
-    _controller->addAnimation(animation);
     return animation;
 }
 

+ 4 - 26
gameplay/src/Animation.h

@@ -22,7 +22,6 @@ class AnimationClip;
  */
 class Animation : public Ref
 {
-    friend class AnimationController;
     friend class AnimationClip;
     friend class AnimationTarget;
     friend class Package;
@@ -93,29 +92,9 @@ public:
      * Returns true if this animation targets the given AnimationTarget.
      */
     bool targets(AnimationTarget* target) const;
-
+    
 private:
 
-    /**
-     * Defines a reference counted Curve wrapper.
-     * 
-     * Multiple channels can share the same Curve.
-     */
-    class CurveRef : public Ref
-    {
-    public:
-        static CurveRef* create(Curve* curve);
-        Curve* getCurve() const;
-
-    private:
-        CurveRef(Curve* curve);
-        CurveRef(const CurveRef&); // Hidden copy constructor.
-        ~CurveRef();
-        CurveRef& operator=(const CurveRef&); // Hidden copy assignment operator.
-
-        Curve* _curve;
-    };
-
     /**
      * Defines a channel which holds the target, target property, curve values, and duration.
      *
@@ -124,7 +103,6 @@ private:
      */
     class Channel
     {
-        friend class AnimationController;
         friend class AnimationClip;
         friend class Animation;
         friend class AnimationTarget;
@@ -141,7 +119,7 @@ private:
         Animation* _animation;                // Reference to the animation this channel belongs to.
         AnimationTarget* _target;             // The target of this channel.
         int _propertyId;                      // The target property this channel targets.
-        CurveRef* _curveRef;                  // The curve used to represent the animation data.
+        Curve* _curve;                        // The curve used to represent the animation data.
         unsigned long _duration;              // The length of the animation (in milliseconds).
     };
 
@@ -169,12 +147,12 @@ private:
      * Destructor.
      */
     ~Animation();
-
+    
     /**
      * Hidden copy assignment operator.
      */
     Animation& operator=(const Animation&);
-
+    
     /**
      * Creates the default clip.
      */

+ 1 - 303
gameplay/src/AnimationController.cpp

@@ -7,125 +7,12 @@ namespace gameplay
 {
 
 AnimationController::AnimationController()
-    : _state(STOPPED), _animations(NULL)
+    : _state(STOPPED)
 {
 }
 
 AnimationController::~AnimationController()
 {
-    std::vector<Animation*>::iterator itr = _animations.begin();
-    for ( ; itr != _animations.end(); itr++)
-    {
-        Animation* temp = *itr;
-        SAFE_RELEASE(temp);
-    }
-
-    _animations.clear();
-}
-
-Animation* AnimationController::createAnimation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, Curve::InterpolationType type)
-{
-    assert(type != Curve::BEZIER && type != Curve::HERMITE);
-    assert(keyCount >= 2 && keyTimes && keyValues && target);
-
-    Animation* animation = new Animation(id, target, propertyId, keyCount, keyTimes, keyValues, type);
-
-    addAnimation(animation);
-    
-    return animation;
-}
-
-Animation* AnimationController::createAnimation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type)
-{
-    assert(target && keyCount >= 2 && keyTimes && keyValues && keyInValue && keyOutValue);
-    Animation* animation = new Animation(id, target, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
-
-    addAnimation(animation);
-
-    return animation;
-}
-
-Animation* AnimationController::createAnimation(const char* id, AnimationTarget* target, const char* animationFile)
-{
-    assert(target && animationFile);
-    
-    Properties* p = Properties::create(animationFile);
-    assert(p);
-
-    Animation* animation = createAnimation(id, target, p->getNextNamespace());
-
-    SAFE_DELETE(p);
-
-    return animation;
-}
-
-Animation* AnimationController::createAnimationFromTo(const char* id, AnimationTarget* target, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration)
-{
-    const unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
-    float* keyValues = new float[2 * propertyComponentCount];
-
-    memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
-    memcpy(keyValues + propertyComponentCount, to, sizeof(float) * propertyComponentCount);
-
-    unsigned long* keyTimes = new unsigned long[2];
-    keyTimes[0] = 0;
-    keyTimes[1] = duration;
-
-    Animation* animation = createAnimation(id, target, propertyId, 2, keyTimes, keyValues, type);
-
-    SAFE_DELETE_ARRAY(keyValues);
-    SAFE_DELETE_ARRAY(keyTimes);
-    
-    return animation;
-}
-
-Animation* AnimationController::createAnimationFromBy(const char* id, AnimationTarget* target, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration)
-{
-    const unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);
-    float* keyValues = new float[2 * propertyComponentCount];
-
-    memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
-    memcpy(keyValues + propertyComponentCount, by, sizeof(float) * propertyComponentCount);
-
-    unsigned long* keyTimes = new unsigned long[2];
-    keyTimes[0] = 0;
-    keyTimes[1] = duration;
-
-    Animation* animation = createAnimation(id, target, propertyId, 2, keyTimes, keyValues, type);
-
-    SAFE_DELETE_ARRAY(keyValues);
-    SAFE_DELETE_ARRAY(keyTimes);
-
-    return animation;
-}
-
-Animation* AnimationController::getAnimation(const char* id) const
-{
-    unsigned int animationCount = _animations.size();
-    for (unsigned int i = 0; i < animationCount; i++)
-    {
-        if (_animations.at(i)->_id.compare(id) == 0)
-        {
-            return _animations.at(i);
-        }
-    }
-    return NULL;
-}
-
-Animation* AnimationController::getAnimation(AnimationTarget* target) const
-{
-    if (!target)
-        return NULL;
-    const unsigned int animationCount = _animations.size();
-    for (unsigned int i = 0; i < animationCount; ++i)
-    {
-        Animation* animation = _animations[i];
-        if (animation->targets(target))
-        {
-            return animation;
-        }
-    }
-    return NULL;
 }
 
 void AnimationController::stopAllAnimations() 
@@ -139,144 +26,6 @@ void AnimationController::stopAllAnimations()
     }
 }
 
-Animation* AnimationController::createAnimation(const char* id, AnimationTarget* target, Properties* animationProperties)
-{
-    assert(target && animationProperties);
-    assert(std::strcmp(animationProperties->getNamespace(), "animation") == 0);
-    
-    const char* propertyIdStr = animationProperties->getString("property");
-    assert(propertyIdStr);
-    
-    // Get animation target property id
-    int propertyId = AnimationTarget::getPropertyId(target->_targetType, propertyIdStr);
-    assert(propertyId != -1);
-    
-    unsigned int keyCount = animationProperties->getInt("keyCount");
-    assert(keyCount > 0);
-
-    const char* keyTimesStr = animationProperties->getString("keyTimes");
-    assert(keyTimesStr);
-    
-    const char* keyValuesStr = animationProperties->getString("keyValues");
-    assert(keyValuesStr);
-    
-    const char* curveStr = animationProperties->getString("curve");
-    assert(curveStr);
-    
-    char delimeter = ' ';
-    unsigned int startOffset = 0;
-    unsigned int endOffset = (unsigned int)std::string::npos;
-    
-    unsigned long* keyTimes = new unsigned long[keyCount];
-    for (unsigned int i = 0; i < keyCount; i++)
-    {
-        endOffset = static_cast<std::string>(keyTimesStr).find_first_of(delimeter, startOffset);
-        if (endOffset != std::string::npos)
-        {
-            keyTimes[i] = std::strtoul(static_cast<std::string>(keyTimesStr).substr(startOffset, endOffset - startOffset).c_str(), NULL, 0);
-        }
-        else
-        {
-            keyTimes[i] = std::strtoul(static_cast<std::string>(keyTimesStr).substr(startOffset, static_cast<std::string>(keyTimesStr).length()).c_str(), NULL, 0);
-        }
-        startOffset = endOffset + 1;
-    }
-
-    startOffset = 0;
-    endOffset = (unsigned int)std::string::npos;
-    
-    int componentCount = target->getAnimationPropertyComponentCount(propertyId);
-    assert(componentCount > 0);
-    
-    unsigned int components = keyCount * componentCount;
-    
-    float* keyValues = new float[components];
-    for (unsigned int i = 0; i < components; i++)
-    {
-        endOffset = static_cast<std::string>(keyValuesStr).find_first_of(delimeter, startOffset);
-        if (endOffset != std::string::npos)
-        {   
-            keyValues[i] = std::atof(static_cast<std::string>(keyValuesStr).substr(startOffset, endOffset - startOffset).c_str());
-        }
-        else
-        {
-            keyValues[i] = std::atof(static_cast<std::string>(keyValuesStr).substr(startOffset, static_cast<std::string>(keyValuesStr).length()).c_str());
-        }
-        startOffset = endOffset + 1;
-    }
-
-    const char* keyInStr = animationProperties->getString("keyIn");
-    float* keyIn = NULL;
-    if (keyInStr)
-    {
-        keyIn = new float[components];
-        startOffset = 0;
-        endOffset = (unsigned int)std::string::npos;
-        for (unsigned int i = 0; i < components; i++)
-        {
-            endOffset = static_cast<std::string>(keyInStr).find_first_of(delimeter, startOffset);
-            if (endOffset != std::string::npos)
-            {   
-                keyIn[i] = std::atof(static_cast<std::string>(keyInStr).substr(startOffset, endOffset - startOffset).c_str());
-            }
-            else
-            {
-                keyIn[i] = std::atof(static_cast<std::string>(keyInStr).substr(startOffset, static_cast<std::string>(keyInStr).length()).c_str());
-            }
-            startOffset = endOffset + 1;
-        }
-    }
-    
-    const char* keyOutStr = animationProperties->getString("keyOut");
-    float* keyOut = NULL;
-    if (keyOutStr)
-    {   
-        keyOut = new float[components];
-        startOffset = 0;
-        endOffset = (unsigned int)std::string::npos;
-        for (unsigned int i = 0; i < components; i++)
-        {
-            endOffset = static_cast<std::string>(keyOutStr).find_first_of(delimeter, startOffset);
-            if (endOffset != std::string::npos)
-            {   
-                keyOut[i] = std::atof(static_cast<std::string>(keyOutStr).substr(startOffset, endOffset - startOffset).c_str());
-            }
-            else
-            {
-                keyOut[i] = std::atof(static_cast<std::string>(keyOutStr).substr(startOffset, static_cast<std::string>(keyOutStr).length()).c_str());
-            }
-            startOffset = endOffset + 1;
-        }
-    }
-
-    int curve = Curve::getInterpolationType(curveStr);
-
-    Animation* animation = NULL;
-    if (keyIn && keyOut)
-    {
-        animation = createAnimation(id, target, propertyId, keyCount, keyTimes, keyValues, keyIn, keyOut, (Curve::InterpolationType)curve);
-    }
-    else
-    {
-        animation = createAnimation(id, target, propertyId, keyCount, keyTimes, keyValues, (Curve::InterpolationType) curve);
-    }
-
-    SAFE_DELETE(keyOut);
-    SAFE_DELETE(keyIn);
-    SAFE_DELETE(keyValues);
-    SAFE_DELETE(keyTimes);
-
-    Properties* pClip = animationProperties->getNextNamespace();
-    if (pClip && std::strcmp(pClip->getNamespace(), "clip") == 0)
-    {
-        int frameCount = animationProperties->getInt("frameCount");
-        assert(frameCount > 0);
-        animation->createClips(animationProperties, (unsigned int) frameCount);
-    }
-
-    return animation;
-}
-
 AnimationController::State AnimationController::getState() const
 {
     return _state;
@@ -385,55 +134,4 @@ void AnimationController::update(long elapsedTime)
         _state = IDLE;
 }
 
-void AnimationController::addAnimation(Animation* animation)
-{
-    _animations.push_back(animation);
-}
-
-void AnimationController::destroyAnimation(Animation* animation)
-{
-    assert(animation);
-
-    std::vector<Animation::Channel*>::iterator cItr = animation->_channels.begin();
-    for (; cItr != animation->_channels.end(); cItr++)
-    {
-        Animation::Channel* channel = *cItr;
-        channel->_target->deleteChannel(channel);
-    }
-
-    std::vector<Animation*>::iterator aItr = _animations.begin();
-    while (aItr != _animations.end())
-    {
-        if (animation == *aItr)
-        {
-            Animation* temp = *aItr;
-            SAFE_RELEASE(temp);
-            _animations.erase(aItr);
-            return;
-        }
-        aItr++;
-    }
-}
-
-void AnimationController::destroyAllAnimations()
-{
-    std::vector<Animation*>::iterator aItr = _animations.begin();
-    
-    while (aItr != _animations.end())
-    {
-        Animation* animation = *aItr;
-        std::vector<Animation::Channel*>::iterator cItr = animation->_channels.begin();
-        for (; cItr != animation->_channels.end(); cItr++)
-        {
-            Animation::Channel* channel = *cItr;
-            channel->_target->deleteChannel(channel);
-        }
-
-        SAFE_RELEASE(animation);
-        aItr++;
-    }
-
-    _animations.clear();
-}
-
 }

+ 2 - 119
gameplay/src/AnimationController.h

@@ -21,110 +21,10 @@ class AnimationController
 
 public:
 
-    /**
-     * Creates an animation on this target from a set of key value and key time pairs. 
-     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
-     * 
-     * @param id The ID of the animation.
-     * @param target The animation target.
-     * @param propertyId The property on this target to animate.
-     * @param keyCount The number of keyframes in the animation. Must be greater than one.
-     * @param keyTimes The list of key times for the animation (in milliseconds).
-     * @param keyValues The list of key values for the animation.
-     * @param type The curve interpolation type.
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, Curve::InterpolationType type);
-
-    /**
-     * Creates an animation on this target from a set of key value and key time pairs.
-     * 
-     * @param id The ID of the animation.
-     * @param target The animation target.
-     * @param propertyId The property on this target to animate.
-     * @param keyCount The number of keyframes in the animation. Must be greater than one.
-     * @param keyTimes The list of key times for the animation (in milliseconds).
-     * @param keyValues The list of key values for the animation.
-     * @param keyInValue The list of key in values for the animation.
-     * @param keyOutValue The list of key out values for the animation.
-     * @param type The curve interpolation type.
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type);
-
-    /**
-     * Creates an animation on this target using the data from the given properties object. 
-     * 
-     * @param id The ID of the animation.
-     * @param target The animation target.
-     * @param animationFile The animation file defining the animation data.
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimation(const char* id, AnimationTarget* target, const char* animationFile);
-
-    /**
-     * Creates a simple two keyframe from-to animation.
-     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
-     *
-     * @param id The ID of the animation.
-     * @param target The animation target.
-     * @param propertyId The property on this target to animate.
-     * @param from The values to animate from.
-     * @param to The values to animate to.
-     * @param type The curve interpolation type.
-     * @param duration The duration of the animation (in milliseconds).
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimationFromTo(const char* id, AnimationTarget* target, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration);
-
-    /**
-     * Creates a simple two keyframe from-by animation.
-     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
-     *
-     * @param id The ID of the animation.
-     * @param target The animation target.
-     * @param propertyId The property on this target to animate.
-     * @param from The values to animate from.
-     * @param by The values to animate by.
-     * @param type The curve interpolation type.
-     * @param duration The duration of the animation (in milliseconds).
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimationFromBy(const char* id, AnimationTarget* target, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration);
-
-    /**
-     * Finds the animation with the given ID.
-     *
-     * @param id The ID of the animation to get. NULL if the Animation is not found.
-     * 
-     * @return The animation, or NULL if not found.
-     */
-    Animation* getAnimation(const char* id) const;
-
-    /**
-     * Returns the first animation that targets the given AnimationTarget.
-     */
-    Animation* getAnimation(AnimationTarget* target) const;
-
     /** 
      * Stops all AnimationClips currently playing on the AnimationController.
      */
     void stopAllAnimations();
-
-    /**
-     * Removes the given animation from this AnimationTarget.
-     */
-    void destroyAnimation(Animation* animation);
-
-    /**
-     * Removes all animations from the AnimationTarget.
-     */ 
-    void destroyAllAnimations();
        
 private:
 
@@ -150,17 +50,6 @@ private:
      * Destructor.
      */
     ~AnimationController();
-    
-    /**
-     * Creates an animation on this target using the data from the given properties object. 
-     * 
-     * @param id The ID of the animation.
-     * @param target The animation target.
-     * @param properties The properties object defining the animation data.
-     *
-     * @return The newly created animation.
-     */
-    Animation* createAnimation(const char* id, AnimationTarget* target, Properties* animationProperties);
 
     /**
      * Gets the controller's state.
@@ -203,16 +92,10 @@ private:
      * Callback for when the controller receives a frame update event.
      */
     void update(long elapsedTime);
-
-    /**
-     * Adds an animation on this AnimationTarget.
-     */ 
-    void addAnimation(Animation* animation);
     
-    State _state;                               // The current state of the AnimationController.
-    std::list<AnimationClip*> _runningClips;    // A list of running AnimationClips.
+    State _state;                                 // The current state of the AnimationController.
+    std::list<AnimationClip*> _runningClips;      // A list of running AnimationClips.
     std::list<AnimationTarget*> _activeTargets;   // A list of animating AnimationTargets.
-    std::vector<Animation*> _animations;        // A list of animations registered with the AnimationController
 };
 
 }

+ 275 - 6
gameplay/src/AnimationTarget.cpp

@@ -29,12 +29,251 @@ AnimationTarget::~AnimationTarget()
     }
 }
 
-void AnimationTarget::addChannel(Animation::Channel* channel)
+Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, Curve::InterpolationType type)
 {
-    if (_animationChannels == NULL)
-        _animationChannels = new std::vector<Animation::Channel*>;
+    assert(type != Curve::BEZIER && type != Curve::HERMITE);
+    assert(keyCount >= 1 && keyTimes && keyValues);
 
-    _animationChannels->push_back(channel);
+    Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, type);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type)
+{
+    assert(keyCount >= 1 && keyTimes && keyValues && keyInValue && keyOutValue);
+    Animation* animation = new Animation(id, this, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimation(const char* id, const char* animationFile)
+{
+    assert(animationFile);
+    
+    Properties* p = Properties::create(animationFile);
+    assert(p);
+
+    Animation* animation = createAnimation(id, p->getNextNamespace());
+
+    SAFE_DELETE(p);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimationFromTo(const char* id, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration)
+{
+    const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
+    float* keyValues = new float[2 * propertyComponentCount];
+
+    memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
+    memcpy(keyValues + propertyComponentCount, to, sizeof(float) * propertyComponentCount);
+
+    unsigned long* keyTimes = new unsigned long[2];
+    keyTimes[0] = 0;
+    keyTimes[1] = duration;
+
+    Animation* animation = createAnimation(id, propertyId, 2, keyTimes, keyValues, type);
+
+    SAFE_DELETE_ARRAY(keyValues);
+    SAFE_DELETE_ARRAY(keyTimes);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimationFromBy(const char* id, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration)
+{
+    const unsigned int propertyComponentCount = getAnimationPropertyComponentCount(propertyId);
+    float* keyValues = new float[2 * propertyComponentCount];
+
+    memcpy(keyValues, from, sizeof(float) * propertyComponentCount);
+    memcpy(keyValues + propertyComponentCount, by, sizeof(float) * propertyComponentCount);
+
+    unsigned long* keyTimes = new unsigned long[2];
+    keyTimes[0] = 0;
+    keyTimes[1] = duration;
+
+    Animation* animation = createAnimation(id, propertyId, 2, keyTimes, keyValues, type);
+
+    SAFE_DELETE_ARRAY(keyValues);
+    SAFE_DELETE_ARRAY(keyTimes);
+
+    return animation;
+}
+
+Animation* AnimationTarget::createAnimation(const char* id, Properties* animationProperties)
+{
+    assert(animationProperties);
+    assert(std::strcmp(animationProperties->getNamespace(), "animation") == 0);
+    
+    const char* propertyIdStr = animationProperties->getString("property");
+    assert(propertyIdStr);
+    
+    // Get animation target property id
+    int propertyId = AnimationTarget::getPropertyId(_targetType, propertyIdStr);
+    assert(propertyId != -1);
+    
+    unsigned int keyCount = animationProperties->getInt("keyCount");
+    assert(keyCount > 0);
+
+    const char* keyTimesStr = animationProperties->getString("keyTimes");
+    assert(keyTimesStr);
+    
+    const char* keyValuesStr = animationProperties->getString("keyValues");
+    assert(keyValuesStr);
+    
+    const char* curveStr = animationProperties->getString("curve");
+    assert(curveStr);
+    
+    char delimeter = ' ';
+    unsigned int startOffset = 0;
+    unsigned int endOffset = (unsigned int)std::string::npos;
+    
+    unsigned long* keyTimes = new unsigned long[keyCount];
+    for (unsigned int i = 0; i < keyCount; i++)
+    {
+        endOffset = static_cast<std::string>(keyTimesStr).find_first_of(delimeter, startOffset);
+        if (endOffset != std::string::npos)
+        {
+            keyTimes[i] = std::strtoul(static_cast<std::string>(keyTimesStr).substr(startOffset, endOffset - startOffset).c_str(), NULL, 0);
+        }
+        else
+        {
+            keyTimes[i] = std::strtoul(static_cast<std::string>(keyTimesStr).substr(startOffset, static_cast<std::string>(keyTimesStr).length()).c_str(), NULL, 0);
+        }
+        startOffset = endOffset + 1;
+    }
+
+    startOffset = 0;
+    endOffset = (unsigned int)std::string::npos;
+    
+    int componentCount = getAnimationPropertyComponentCount(propertyId);
+    assert(componentCount > 0);
+    
+    unsigned int components = keyCount * componentCount;
+    
+    float* keyValues = new float[components];
+    for (unsigned int i = 0; i < components; i++)
+    {
+        endOffset = static_cast<std::string>(keyValuesStr).find_first_of(delimeter, startOffset);
+        if (endOffset != std::string::npos)
+        {   
+            keyValues[i] = std::atof(static_cast<std::string>(keyValuesStr).substr(startOffset, endOffset - startOffset).c_str());
+        }
+        else
+        {
+            keyValues[i] = std::atof(static_cast<std::string>(keyValuesStr).substr(startOffset, static_cast<std::string>(keyValuesStr).length()).c_str());
+        }
+        startOffset = endOffset + 1;
+    }
+
+    const char* keyInStr = animationProperties->getString("keyIn");
+    float* keyIn = NULL;
+    if (keyInStr)
+    {
+        keyIn = new float[components];
+        startOffset = 0;
+        endOffset = (unsigned int)std::string::npos;
+        for (unsigned int i = 0; i < components; i++)
+        {
+            endOffset = static_cast<std::string>(keyInStr).find_first_of(delimeter, startOffset);
+            if (endOffset != std::string::npos)
+            {   
+                keyIn[i] = std::atof(static_cast<std::string>(keyInStr).substr(startOffset, endOffset - startOffset).c_str());
+            }
+            else
+            {
+                keyIn[i] = std::atof(static_cast<std::string>(keyInStr).substr(startOffset, static_cast<std::string>(keyInStr).length()).c_str());
+            }
+            startOffset = endOffset + 1;
+        }
+    }
+    
+    const char* keyOutStr = animationProperties->getString("keyOut");
+    float* keyOut = NULL;
+    if (keyOutStr)
+    {   
+        keyOut = new float[components];
+        startOffset = 0;
+        endOffset = (unsigned int)std::string::npos;
+        for (unsigned int i = 0; i < components; i++)
+        {
+            endOffset = static_cast<std::string>(keyOutStr).find_first_of(delimeter, startOffset);
+            if (endOffset != std::string::npos)
+            {   
+                keyOut[i] = std::atof(static_cast<std::string>(keyOutStr).substr(startOffset, endOffset - startOffset).c_str());
+            }
+            else
+            {
+                keyOut[i] = std::atof(static_cast<std::string>(keyOutStr).substr(startOffset, static_cast<std::string>(keyOutStr).length()).c_str());
+            }
+            startOffset = endOffset + 1;
+        }
+    }
+
+    int curve = Curve::getInterpolationType(curveStr);
+
+    Animation* animation = NULL;
+    if (keyIn && keyOut)
+    {
+        animation = createAnimation(id, propertyId, keyCount, keyTimes, keyValues, keyIn, keyOut, (Curve::InterpolationType)curve);
+    }
+    else
+    {
+        animation = createAnimation(id, propertyId, keyCount, keyTimes, keyValues, (Curve::InterpolationType) curve);
+    }
+
+    SAFE_DELETE(keyOut);
+    SAFE_DELETE(keyIn);
+    SAFE_DELETE(keyValues);
+    SAFE_DELETE(keyTimes);
+
+    Properties* pClip = animationProperties->getNextNamespace();
+    if (pClip && std::strcmp(pClip->getNamespace(), "clip") == 0)
+    {
+        int frameCount = animationProperties->getInt("frameCount");
+        assert(frameCount > 0);
+        animation->createClips(animationProperties, (unsigned int) frameCount);
+    }
+
+    return animation;
+}
+
+void AnimationTarget::destroyAnimation(const char* id)
+{
+    // Find the animation with the specified ID.
+    Animation::Channel* channel = getChannel(id);
+    if (channel == NULL)
+        return;
+
+    // Remove this target's channel from animation, and from the target's list of channels.
+    channel->_animation->removeChannel(channel);
+    removeChannel(channel);
+
+    SAFE_DELETE(channel);
+}
+
+Animation* AnimationTarget::getAnimation(const char* id) const
+{
+    if (_animationChannels)
+    {
+        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+
+        if (id == NULL)
+            return (*itr)->_animation;
+
+        Animation::Channel* channel = NULL;
+        for (; itr != _animationChannels->end(); itr++)
+        {
+            channel = (Animation::Channel*)(*itr);
+            if (channel->_animation->_id.compare(id) == 0)
+            {
+                return channel->_animation;
+            }
+        }
+    }
+
+    return NULL;
 }
 
 int AnimationTarget::getPropertyId(TargetType type, const char* propertyIdStr)
@@ -97,7 +336,15 @@ int AnimationTarget::getPropertyId(TargetType type, const char* propertyIdStr)
     return -1;
 }
 
-void AnimationTarget::deleteChannel(Animation::Channel* channel)
+void AnimationTarget::addChannel(Animation::Channel* channel)
+{
+    if (_animationChannels == NULL)
+        _animationChannels = new std::vector<Animation::Channel*>;
+
+    _animationChannels->push_back(channel);
+}
+
+void AnimationTarget::removeChannel(Animation::Channel* channel)
 {
     if (_animationChannels)
     {
@@ -107,7 +354,6 @@ void AnimationTarget::deleteChannel(Animation::Channel* channel)
             Animation::Channel* temp = *itr;
             if (channel == temp)
             {
-                SAFE_DELETE(channel);
                 _animationChannels->erase(itr);
 
                 if (_animationChannels->empty())
@@ -119,6 +365,29 @@ void AnimationTarget::deleteChannel(Animation::Channel* channel)
     }
 }
 
+Animation::Channel* AnimationTarget::getChannel(const char* id) const
+{
+    if (_animationChannels)
+    {
+        std::vector<Animation::Channel*>::iterator itr = _animationChannels->begin();
+
+        if (id == NULL)
+            return (*itr);
+
+        Animation::Channel* channel = NULL;
+        for (; itr != _animationChannels->end(); itr++)
+        {
+            channel = (Animation::Channel*)(*itr);
+            if (channel->_animation->_id.compare(id) == 0)
+            {
+                return channel;
+            }
+        }
+    }
+
+    return NULL;
+}
+
 void AnimationTarget::cloneInto(AnimationTarget* target, NodeCloneContext &context) const
 {
     if (_animationChannels)

+ 110 - 2
gameplay/src/AnimationTarget.h

@@ -23,6 +23,100 @@ class AnimationTarget
 
 public:
 
+    /**
+     * Creates an animation on this target from a set of key value and key time pairs. 
+     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
+     * 
+     * @param id The ID of the animation.
+     * @param target The animation target.
+     * @param propertyId The property on this target to animate.
+     * @param keyCount The number of keyframes in the animation. Must be greater than one.
+     * @param keyTimes The list of key times for the animation (in milliseconds).
+     * @param keyValues The list of key values for the animation.
+     * @param type The curve interpolation type.
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, Curve::InterpolationType type);
+
+    /**
+     * Creates an animation on this target from a set of key value and key time pairs.
+     * 
+     * @param id The ID of the animation.
+     * @param target The animation target.
+     * @param propertyId The property on this target to animate.
+     * @param keyCount The number of keyframes in the animation. Must be greater than one.
+     * @param keyTimes The list of key times for the animation (in milliseconds).
+     * @param keyValues The list of key values for the animation.
+     * @param keyInValue The list of key in values for the animation.
+     * @param keyOutValue The list of key out values for the animation.
+     * @param type The curve interpolation type.
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimation(const char* id, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type);
+
+    /**
+     * Creates an animation on this target using the data from the given properties object. 
+     * 
+     * @param id The ID of the animation.
+     * @param target The animation target.
+     * @param animationFile The animation file defining the animation data.
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimation(const char* id, const char* animationFile);
+
+    /**
+     * Creates an animation on this target using the data from the given properties object. 
+     * 
+     * @param id The ID of the animation.
+     * @param target The animation target.
+     * @param properties The properties object defining the animation data.
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimation(const char* id, Properties* animationProperties);
+
+    /**
+     * Creates a simple two keyframe from-to animation.
+     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
+     *
+     * @param id The ID of the animation.
+     * @param target The animation target.
+     * @param propertyId The property on this target to animate.
+     * @param from The values to animate from.
+     * @param to The values to animate to.
+     * @param type The curve interpolation type.
+     * @param duration The duration of the animation (in milliseconds).
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimationFromTo(const char* id, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration);
+
+    /**
+     * Creates a simple two keyframe from-by animation.
+     * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.
+     *
+     * @param id The ID of the animation.
+     * @param target The animation target.
+     * @param propertyId The property on this target to animate.
+     * @param from The values to animate from.
+     * @param by The values to animate by.
+     * @param type The curve interpolation type.
+     * @param duration The duration of the animation (in milliseconds).
+     *
+     * @return The newly created animation.
+     */
+    Animation* createAnimationFromBy(const char* id, int propertyId, float* from, float* by, Curve::InterpolationType type, unsigned long duration);
+
+    /**
+     * Destroys the animation with the specified ID. Destroys the first animation if ID is NULL.
+     *
+     * @param The animation to destroy.
+     */ 
+    void destroyAnimation(const char* id = NULL);
+
     /**
      * Abstract method to return the property component count of the given property ID on the AnimationTarget.
      * 
@@ -49,6 +143,13 @@ public:
      */
     virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f) = 0;
 
+    /** 
+     * Gets the animation with the specified ID. If the ID is NULL, this function will return the first animation it finds.
+     *
+     * @param id The name of the animation to get.
+     */
+    Animation* getAnimation(const char* id = NULL) const;
+
 protected:
     
     enum TargetType
@@ -75,11 +176,18 @@ protected:
     void addChannel(Animation::Channel* channel);
 
     /**
-     * Deletes the given animation channel from this animation target.
+     * Removes the given animation channel from this animation target.
      * 
      * @param channel The animation channel to delete.
      */
-    void deleteChannel(Animation::Channel* channel);
+    void removeChannel(Animation::Channel* channel);
+
+    /**
+     * Gets the Animation::Channel that belongs to the Animation with the specified ID.
+     *
+     * @param id The ID of the Animation the Channel belongs to.
+     */
+    Animation::Channel* getChannel(const char* id) const;
 
     /**
      * Copies data from this animation target into the given target for the purpose of cloning.

+ 1 - 1
gameplay/src/Button.cpp

@@ -14,7 +14,7 @@ namespace gameplay
     Button* Button::create(Theme::Style* style, Properties* properties)
     {
         Button* button = new Button();
-        button->init(style, properties);
+        button->initialize(style, properties);
 
         return button;
     }

+ 14 - 5
gameplay/src/Button.h

@@ -14,9 +14,9 @@ namespace gameplay
  *
  * The following properties are available for buttons:
  *
- * button <Button ID>
+ * button <buttonID>
  * {
- *      style       = <Style ID>
+ *      style       = <styleID>
  *      position    = <x, y>
  *      size        = <width, height>
  *      text        = <string>
@@ -27,6 +27,17 @@ class Button : public Label
     friend class Container;
 
 protected:
+
+    /**
+     * Constructor.
+     */
+    Button();
+
+    /**
+     * Destructor.
+     */
+    virtual ~Button();
+
     /**
      * Create a button with a given style and properties.
      *
@@ -51,10 +62,8 @@ protected:
      */
     bool touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex);
 
-    Button();
-    virtual ~Button();
-
 private:
+
     Button(const Button& copy);
 };
 

+ 8 - 8
gameplay/src/CheckBox.cpp

@@ -22,7 +22,7 @@ CheckBox::~CheckBox()
 CheckBox* CheckBox::create(Theme::Style* style, Properties* properties)
 {
     CheckBox* checkBox = new CheckBox();
-    checkBox->init(style, properties);
+    checkBox->initialize(style, properties);
     properties->getVector2("iconSize", &checkBox->_imageSize);
     checkBox->_checked = properties->getBool("checked");
 
@@ -68,8 +68,8 @@ bool CheckBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int cont
         {
             if (_state == Control::ACTIVE)
             {
-                if (x > 0 && x <= _bounds.width &&
-                    y > 0 && y <= _bounds.height)
+                if (x > 0 && x <= _clipBounds.width &&
+                    y > 0 && y <= _clipBounds.height)
                 {
                     _checked = !_checked;
                     notifyListeners(Control::Listener::VALUE_CHANGED);
@@ -123,7 +123,7 @@ void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     // Left, v-center.
     // TODO: Set an alignment for icons.
     const Theme::Border border = getBorder(_state);
-    const Theme::Padding padding = _style->getPadding();
+    const Theme::Padding padding = getPadding();
     float opacity = getOpacity(_state);
 
     if (_checked)
@@ -143,8 +143,8 @@ void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
             size.set(_imageSize);
         }
 
-        Vector2 pos(clip.x + _position.x + border.left + padding.left,
-            clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+        Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
+            clip.y + _bounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
         spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
     }
@@ -165,8 +165,8 @@ void CheckBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
             size.set(_imageSize);
         }
 
-        Vector2 pos(clip.x + _position.x + border.left + padding.left,
-            clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+        Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
+            clip.y + _bounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
         spriteBatch->draw(pos.x, pos.y, size.x, size.y, unselected.u1, unselected.v1, unselected.u2, unselected.v2, unselectedColor, _clip);
     }

+ 13 - 5
gameplay/src/CheckBox.h

@@ -14,7 +14,7 @@ namespace gameplay
  *
  * The following properties are available for checkboxes:
  *
- * checkBox <CheckBox ID>
+ * checkBox <checkBoxID>
  * {
  *      style       = <Style ID>
  *      position    = <x, y>
@@ -24,11 +24,12 @@ namespace gameplay
  *      iconSize    = <width, height>   // The size to draw the checkbox icon, if different from its size in the texture.
  * }
  */
-class CheckBox : public Button //, public AnimationClip::Listener
+class CheckBox : public Button
 {
     friend class Container;
 
 public:
+
     /**
      * Gets whether this checkbox is checked.
      *
@@ -63,10 +64,16 @@ public:
      */
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
-  //  virtual void animationEvent(AnimationClip* clip, EventType type);
-
 protected:
+
+    /**
+     * Constructor.
+     */
     CheckBox();
+
+    /**
+     * Destructor.
+     */
     ~CheckBox();
 
     /**
@@ -97,7 +104,7 @@ protected:
      * Called when a control's properties change.  Updates this control's internal rendering
      * properties, such as its text viewport.
      *
-     * @param position The control's position within its container.
+     * @param clip The clipping rectangle of this control's parent container.
      */
     void update(const Rectangle& clip);
 
@@ -113,6 +120,7 @@ protected:
     Vector2 _imageSize;  // The size to draw the checkbox icon, if different from its size in the texture.
 
 private:
+
     CheckBox(const CheckBox& copy);
 };
 

+ 26 - 2
gameplay/src/Container.cpp

@@ -56,7 +56,7 @@ namespace gameplay
     {
         const char* layoutString = properties->getString("layout");
         Container* container = Container::create(getLayoutType(layoutString));
-        container->init(style, properties);
+        container->initialize(style, properties);
         container->addControls(theme, properties);
 
         return container;
@@ -202,6 +202,30 @@ namespace gameplay
         return _controls;
     }
 
+    Animation* Container::getAnimation(const char* id) const
+    {
+        std::vector<Control*>::const_iterator itr = _controls.begin();
+        std::vector<Control*>::const_iterator end = _controls.end();
+        
+        Control* control = NULL;
+        for (; itr != end; itr++)
+        {
+            control = *itr;
+            Animation* animation = control->getAnimation(id);
+            if (animation)
+                return animation;
+
+            if (control->isContainer())
+            {
+                animation = ((Container*)control)->getAnimation(id);
+                if (animation)
+                    return animation;
+            }
+        }
+
+        return NULL;
+    }
+
     void Container::update(const Rectangle& clip)
     {
         // Update this container's viewport.
@@ -295,7 +319,7 @@ namespace gameplay
                 continue;
             }
 
-            const Rectangle& bounds = control->getBounds();
+            const Rectangle& bounds = control->getClipBounds();
             if (control->getState() != Control::NORMAL ||
                 (evt == Touch::TOUCH_PRESS &&
                  x >= xPos + bounds.x &&

+ 23 - 4
gameplay/src/Container.h

@@ -20,10 +20,11 @@ namespace gameplay
  *      position = <x, y>               // Position of the container on-screen, measured in pixels.
  *      size     = <width, height>      // Size of the container, measured in pixels.
  *   
- *      // All the controls within this container.
- *      container{}
- *      label{}
- *      textBox{}
+ *      // All the nested controls within this container.
+ *      container { }
+
+ *      label { }
+ *      textBox { }
  *      button{}
  *      checkBox{}
  *      radioButton{}
@@ -33,6 +34,7 @@ namespace gameplay
 class Container : public Control
 {
 public:
+
     /**
      * Get this container's layout.
      *
@@ -102,8 +104,24 @@ public:
      */
     std::vector<Control*> getControls() const;
 
+    /**
+     * Gets the first animation in the control with the specified ID.
+     *
+     * @param id The ID of the animation to get. Returns the first animation if ID is NULL.
+     * @return The first animation with the specified ID.
+     */
+    Animation* getAnimation(const char* id = NULL) const;
+
 protected:
+
+    /**
+     * Constructor.
+     */
     Container();
+
+    /**
+     * Destructor.
+     */
     virtual ~Container();
 
     /**
@@ -207,6 +225,7 @@ protected:
     std::vector<Control*> _controls;    // List of controls within this container.
 
 private:
+
     Container(const Container& copy);
 };
 

+ 104 - 136
gameplay/src/Control.cpp

@@ -5,8 +5,8 @@
 namespace gameplay
 {
     Control::Control()
-        : _id(""), _state(Control::NORMAL), _position(Vector2::zero()), _size(Vector2::zero()), _bounds(Rectangle::empty()), _clip(Rectangle::empty()),
-            _autoWidth(true), _autoHeight(true), _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false)
+        : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _clip(Rectangle::empty()),
+            _dirty(true), _consumeTouchEvents(true), _listeners(NULL), _styleOverridden(false)
     {
     }
 
@@ -18,7 +18,7 @@ namespace gameplay
     {
         if (_listeners)
         {
-            for (ListenerMap::const_iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
+            for (std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->begin(); itr != _listeners->end(); itr++)
             {
                 std::list<Listener*>* list = itr->second;
                 SAFE_DELETE(list);
@@ -32,14 +32,17 @@ namespace gameplay
         }
     }
 
-    void Control::init(Theme::Style* style, Properties* properties)
+    void Control::initialize(Theme::Style* style, Properties* properties)
     {
         _style = style;
 
-        properties->getVector2("position", &_position);
-        properties->getVector2("size", &_size);
+        Vector2 position;
+        Vector2 size;
+        properties->getVector2("position", &position);
+        properties->getVector2("size", &size);
+        _bounds.set(position.x, position.y, size.x, size.y);
 
-        _state = Control::getStateFromString(properties->getString("state"));
+        _state = Control::getState(properties->getString("state"));
 
         const char* id = properties->getId();
         if (id)
@@ -51,87 +54,59 @@ namespace gameplay
         return _id.c_str();
     }
 
-    void Control::setPosition(float x, float y, unsigned long duration)
+    void Control::setPosition(float x, float y)
     {
-        if (duration > 0L)
-        {
-            AnimationController* animationController = Game::getInstance()->getAnimationController();
-            float from[2] = { _position.x, _position.y };
-            float to[2] = { x, y };
-            Animation* moveAnimation = animationController->createAnimationFromTo("Control::setPosition", this, Control::ANIMATE_POSITION,
-                from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration);
-            AnimationClip* clip = moveAnimation->getClip();
-            clip->play();
-        }
-        else
-        {
-            _position.set(x, y);
-        }
+        _bounds.x = x;
+        _bounds.y = y;
+        _dirty = true;
+    }
 
+    void Control::setSize(float width, float height)
+    {
+        _bounds.width = width;
+        _bounds.height = height;
         _dirty = true;
     }
 
-    const Vector2& Control::getPosition() const
+    void Control::setBounds(const Rectangle& bounds)
     {
-        return _position;
+        _bounds.set(bounds);
     }
 
-    void Control::setSize(float width, float height, unsigned long duration)
+    const Rectangle& Control::getBounds() const
     {
-        if (duration > 0L)
-        {
-            AnimationController* animationController = Game::getInstance()->getAnimationController();
-            float from[2] = { _size.x, _size.y };
-            float to[2] = { width, height };
-            Animation* resizeAnimation = animationController->createAnimationFromTo("Control::setSize", this, Control::ANIMATE_SIZE,
-                from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration);
-            AnimationClip* clip = resizeAnimation->getClip();
-            clip->play();
-        }
-        else
-        {
-            _size.set(width, height);
-        }
+        return _bounds;
+    }
 
-        _dirty = true;
+    float Control::getX() const
+    {
+        return _bounds.x;
     }
 
-    const Vector2& Control::getSize() const
+    float Control::getY() const
     {
-        return _size;
+        return _bounds.y;
     }
 
-    void Control::setOpacity(float opacity, unsigned char states, unsigned long duration)
+    float Control::getWidth() const
+    {
+        return _bounds.width;
+    }
+
+    float Control::getHeight() const
+    {
+        return _bounds.height;
+    }
+
+    void Control::setOpacity(float opacity, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
-            if (duration > 0L)
-            {
-                float from[1] = { overlays[i]->getOpacity() };
-                float to[1] = { opacity };
-
-                // Fun with chaining.
-                Game::getInstance()->getAnimationController()->createAnimationFromTo("Overlay::setOpacity", overlays[i], Theme::Style::Overlay::ANIMATE_OPACITY,
-                    from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration)->getClip()->play();
-            }
-            else
-            {
-                overlays[i]->setOpacity(opacity);
-            }
-        }
-        
-        if (duration > 0L)
-        {
-            // All this animation does is make sure this control sets its dirty flag during the animation.
-            float from[1] = { 0.0f };
-            float to[1] = { 1.0f };
-
-            Game::getInstance()->getAnimationController()->createAnimationFromTo("Control::setOpacity", this, Control::ANIMATE_OPACITY,
-                from, to, gameplay::Curve::QUADRATIC_IN_OUT, duration)->getClip()->play();
+            overlays[i]->setOpacity(opacity);
         }
         
         _dirty = true;
@@ -145,10 +120,10 @@ namespace gameplay
     void Control::setBorder(float top, float bottom, float left, float right, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setBorder(top, bottom, left, right);
         }
@@ -164,10 +139,10 @@ namespace gameplay
     void Control::setSkinRegion(const Rectangle& region, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setSkinRegion(region, _style->_tw, _style->_th);
         }
@@ -188,10 +163,10 @@ namespace gameplay
     void Control::setSkinColor(const Vector4& color, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setSkinColor(color);
         }
@@ -229,10 +204,10 @@ namespace gameplay
     void Control::setImageRegion(const char* id, const Rectangle& region, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setImageRegion(id, region, _style->_tw, _style->_th);
         }
@@ -248,10 +223,10 @@ namespace gameplay
     void Control::setImageColor(const char* id, const Vector4& color, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setImageColor(id, color);
         }
@@ -272,10 +247,10 @@ namespace gameplay
     void Control::setCursorRegion(const Rectangle& region, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setCursorRegion(region, _style->_tw, _style->_th);
         }
@@ -291,10 +266,10 @@ namespace gameplay
     void Control::setCursorColor(const Vector4& color, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setCursorColor(color);
         }
@@ -315,10 +290,10 @@ namespace gameplay
     void Control::setFont(Font* font, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setFont(font);
         }
@@ -334,10 +309,10 @@ namespace gameplay
     void Control::setFontSize(unsigned int fontSize, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setFontSize(fontSize);
         }
@@ -353,10 +328,10 @@ namespace gameplay
     void Control::setTextColor(const Vector4& color, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setTextColor(color);
         }
@@ -372,10 +347,10 @@ namespace gameplay
     void Control::setTextAlignment(Font::Justify alignment, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setTextAlignment(alignment);
         }
@@ -391,10 +366,10 @@ namespace gameplay
     void Control::setTextRightToLeft(bool rightToLeft, unsigned char states)
     {
         overrideStyle();
-        Theme::Style::Overlay* overlays[MAX_OVERLAYS] = { 0 };
+        Theme::Style::Overlay* overlays[Theme::Style::OVERLAY_MAX] = { 0 };
         getOverlays(states, overlays);
 
-        for (int i = 0; i < MAX_OVERLAYS - 1 && overlays[i]; ++i)
+        for (int i = 0; i < Theme::Style::OVERLAY_MAX - 1 && overlays[i]; ++i)
         {
             overlays[i]->setTextRightToLeft(rightToLeft);
         }
@@ -407,9 +382,9 @@ namespace gameplay
         return getOverlay(state)->getTextRightToLeft();
     }
 
-    const Rectangle& Control::getBounds() const
+    const Rectangle& Control::getClipBounds() const
     {
-        return _bounds;
+        return _clipBounds;
     }
 
     const Rectangle& Control::getClip() const
@@ -417,13 +392,6 @@ namespace gameplay
         return _clip;
     }
 
-    void Control::setAutoSize(bool width, bool height)
-    {
-        _autoWidth = width;
-        _autoHeight = height;
-        _dirty = true;
-    }
-
     void Control::setStyle(Theme::Style* style)
     {
         if (style != _style)
@@ -529,7 +497,7 @@ namespace gameplay
             _listeners = new std::map<Listener::EventType, std::list<Listener*>*>();
         }
 
-        ListenerMap::const_iterator itr = _listeners->find(eventType);
+        std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
         if (itr == _listeners->end())
         {
             _listeners->insert(std::make_pair(eventType, new std::list<Listener*>()));
@@ -558,8 +526,8 @@ namespace gameplay
             notifyListeners(Listener::RELEASE);
 
             // Only trigger Listener::CLICK if both PRESS and RELEASE took place within the control's bounds.
-            if (x > 0 && x <= _bounds.width &&
-                y > 0 && y <= _bounds.height)
+            if (x > 0 && x <= _clipBounds.width &&
+                y > 0 && y <= _clipBounds.height)
             {
                 notifyListeners(Listener::CLICK);
             }
@@ -577,7 +545,7 @@ namespace gameplay
     {
         if (_listeners)
         {
-            ListenerMap::const_iterator itr = _listeners->find(eventType);
+            std::map<Listener::EventType, std::list<Listener*>*>::const_iterator itr = _listeners->find(eventType);
             if (itr != _listeners->end())
             {
                 std::list<Listener*>* listenerList = itr->second;
@@ -592,10 +560,10 @@ namespace gameplay
     void Control::update(const Rectangle& clip)
     {
         // Calculate the bounds.
-        float x = clip.x + _position.x;
-        float y = clip.y + _position.y;
-        float width = _size.x;
-        float height = _size.y;
+        float x = clip.x + _bounds.x;
+        float y = clip.y + _bounds.y;
+        float width = _bounds.width;
+        float height = _bounds.height;
 
         float clipX2 = clip.x + clip.width;
         float x2 = x + width;
@@ -611,7 +579,7 @@ namespace gameplay
             height = clipY2 - y;
         }
 
-        _bounds.set(_position.x, _position.y, width, height);
+        _clipBounds.set(_bounds.x, _bounds.y, width, height);
 
         // Calculate the clipping viewport.
         const Theme::Border& border = getBorder(_state);
@@ -619,8 +587,8 @@ namespace gameplay
 
         x +=  border.left + padding.left;
         y +=  border.top + padding.top;
-        width = _size.x - border.left - padding.left - border.right - padding.right;
-        height = _size.y - border.top - padding.top - border.bottom - padding.bottom;
+        width = _bounds.width - border.left - padding.left - border.right - padding.right;
+        height = _bounds.height - border.top - padding.top - border.bottom - padding.bottom;
 
         _textBounds.set(x, y, width, height);
 
@@ -653,7 +621,7 @@ namespace gameplay
 
     void Control::drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip)
     {
-        Vector2 pos(clip.x + _position.x, clip.y + _position.y);
+        Vector2 pos(clip.x + _bounds.x, clip.y + _bounds.y);
 
         // Get the border and background images for this control's current state.
         //Theme::UVs topLeft, top, topRight, left, center, right, bottomLeft, bottom, bottomRight;
@@ -673,18 +641,18 @@ namespace gameplay
         Vector4 skinColor = getSkinColor(_state);
         skinColor.w *= getOpacity(_state);
 
-        float midWidth = _size.x - border.left - border.right;
-        float midHeight = _size.y - border.top - border.bottom;
+        float midWidth = _bounds.width - border.left - border.right;
+        float midHeight = _bounds.height - border.top - border.bottom;
         float midX = pos.x + border.left;
         float midY = pos.y + border.top;
-        float rightX = pos.x + _size.x - border.right;
-        float bottomY = pos.y + _size.y - border.bottom;
+        float rightX = pos.x + _bounds.width - border.right;
+        float bottomY = pos.y + _bounds.height - border.bottom;
 
         // Draw themed border sprites.
         if (!border.left && !border.right && !border.top && !border.bottom)
         {
             // No border, just draw the image.
-            spriteBatch->draw(pos.x, pos.y, _size.x, _size.y, center.u1, center.v1, center.u2, center.v2, skinColor, clip);
+            spriteBatch->draw(pos.x, pos.y, _bounds.width, _bounds.height, center.u1, center.v1, center.u2, center.v2, skinColor, clip);
         }
         else
         {
@@ -697,7 +665,7 @@ namespace gameplay
             if (border.left)
                 spriteBatch->draw(pos.x, midY, border.left, midHeight, left.u1, left.v1, left.u2, left.v2, skinColor, clip);
             if (border.left && border.right && border.top && border.bottom)
-                spriteBatch->draw(pos.x + border.left, pos.y + border.top, _size.x - border.left - border.right, _size.y - border.top - border.bottom,
+                spriteBatch->draw(pos.x + border.left, pos.y + border.top, _bounds.width - border.left - border.right, _bounds.height - border.top - border.bottom,
                     center.u1, center.v1, center.u2, center.v2, skinColor, clip);
             if (border.right)
                 spriteBatch->draw(rightX, midY, border.right, midHeight, right.u1, right.v1, right.u2, right.v2, skinColor, clip);
@@ -728,7 +696,7 @@ namespace gameplay
         return false;
     }
 
-    Control::State Control::getStateFromString(const char* state)
+    Control::State Control::getState(const char* state)
     {
         if (!state)
         {
@@ -781,24 +749,24 @@ namespace gameplay
         switch(propertyId)
         {
         case ANIMATE_POSITION:
-            value->setFloat(0, _position.x);
-            value->setFloat(1, _position.y);
+            value->setFloat(0, _bounds.x);
+            value->setFloat(1, _bounds.y);
             break;
         case ANIMATE_SIZE:
-            value->setFloat(0, _size.x);
-            value->setFloat(1, _size.y);
+            value->setFloat(0, _clipBounds.width);
+            value->setFloat(1, _clipBounds.height);
             break;
         case ANIMATE_POSITION_X:
-            value->setFloat(0, _position.x);
+            value->setFloat(0, _bounds.x);
             break;
         case ANIMATE_POSITION_Y:
-            value->setFloat(0, _position.y);
+            value->setFloat(0, _bounds.y);
             break;
         case ANIMATE_SIZE_WIDTH:
-            value->setFloat(0, _size.x);
+            value->setFloat(0, _clipBounds.width);
             break;
         case ANIMATE_SIZE_HEIGHT:
-            value->setFloat(0, _size.y);
+            value->setFloat(0, _clipBounds.height);
             break;
         case ANIMATE_OPACITY:
         default:
@@ -845,9 +813,9 @@ namespace gameplay
         }
         else
         {
-            x = Curve::lerp(blendWeight, _position.x, x);
+            x = Curve::lerp(blendWeight, _bounds.x, x);
         }
-        _position.x = x;
+        _bounds.x = x;
         _dirty = true;
     }
     
@@ -859,9 +827,9 @@ namespace gameplay
         }
         else
         {
-            y = Curve::lerp(blendWeight, _position.y, y);
+            y = Curve::lerp(blendWeight, _bounds.y, y);
         }
-        _position.y = y;
+        _bounds.y = y;
         _dirty = true;
     }
     
@@ -873,9 +841,9 @@ namespace gameplay
         }
         else
         {
-            width = Curve::lerp(blendWeight, _size.x, width);
+            width = Curve::lerp(blendWeight, _clipBounds.width, width);
         }
-        _size.x = width;
+        _clipBounds.width = width;
         _dirty = true;
     }
 
@@ -887,9 +855,9 @@ namespace gameplay
         }
         else
         {
-            height = Curve::lerp(blendWeight, _size.y, height);
+            height = Curve::lerp(blendWeight, _clipBounds.height, height);
         }
-        _size.y = height;
+        _clipBounds.height = height;
         _dirty = true;
     }
 

+ 131 - 63
gameplay/src/Control.h

@@ -6,14 +6,10 @@
 #include "Vector2.h"
 #include "SpriteBatch.h"
 #include "Theme.h"
+#include "ThemeStyle.h"
 #include "Touch.h"
 #include "Keyboard.h"
 
-/**
- * Default duration of UI animations.
- */
-#define DEFAULT_UI_ANIMATION_DURATION 200L
-
 namespace gameplay
 {
 
@@ -29,32 +25,82 @@ class Control : public Ref, public AnimationTarget
     friend class VerticalLayout;
 
 public:
+
     /**
      * The possible states a control can be in.
      */
     enum State
     {
+        /**
+         * State of an enabled but inactive control.
+         */
         NORMAL = 0x01,
+
+        /**
+         * State of a control currently in focus.
+         */
         FOCUS = 0x02,
+
+        /**
+         * State of a control that is currently being acted on,
+         * e.g. through touch or mouse-click events.
+         */
         ACTIVE = 0x04,
+
+        /**
+         * State of a control that has been disabled.
+         */
         DISABLED = 0x08,
     };
 
-    // Only for setting control states
+    /**
+     * A constant used for setting themed attributes on all control states simultaneously.
+     */
     static const unsigned char STATE_ALL = NORMAL | FOCUS | ACTIVE | DISABLED;
 
+    /**
+     * Implement Control::Listener and call Control::addListener()
+     * in order to listen for events on controls.
+     */
     class Listener
     {
     public:
         enum EventType
         {
+            /**
+             * Mouse-down or touch-press event.
+             */
             PRESS           = 0x01,
+
+            /**
+             * Mouse-up or touch-release event.
+             */
             RELEASE         = 0x02,
+
+            /**
+             * Event triggered after consecutive PRESS and RELEASE events take place
+             * within the bounds of a control.
+             */
             CLICK           = 0x04,
+
+            /**
+             * Event triggered when the value of a slider, check box, or radio button
+             * changes.
+             */
             VALUE_CHANGED   = 0x08,
+
+            /**
+             * Event triggered when the contents of a text box are modified.
+             */
             TEXT_CHANGED    = 0x10
         };
 
+        /**
+         * Method called by controls when an event is triggered.
+         * 
+         * @param control The control triggering the event.
+         * @param evt The event triggered.
+         */
         virtual void controlEvent(Control* control, EventType evt) = 0;
     };
 
@@ -106,33 +152,60 @@ public:
      * @param x The x coordinate.
      * @param y The y coordinate.
      */
-    void setPosition(float x, float y, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
+    void setPosition(float x, float y);
 
     /**
-     * Get the position of this control relative to its parent container.
+     * Set the desired size of this control, including its border and padding, before clipping.
      *
-     * @return The position of this control relative to its parent container.
+     * @param width The width.
+     * @param height The height.
      */
-    const Vector2& getPosition() const;
+    void setSize(float width, float height);
 
     /**
-     * Set the desired size of this control, including its border and padding, before clipping.
+     * Set the bounds of this control, relative to its parent container and including its
+     * border and padding, before clipping.
      *
-     * @param width The width.
-     * @param height The height.
+     * @param bounds The new bounds to set.
      */
-    void setSize(float width, float height, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
+    void setBounds(const Rectangle& bounds);
 
     /**
-     * Get the desired size of this control, including its border and padding, before clipping.
+     * Get the bounds of this control, relative to its parent container and including its
+     * border and padding, before clipping.
      *
-     * @return The size of this control.
+     * @return The bounds of this control.
      */
-    const Vector2& getSize() const;
+    const Rectangle& getBounds() const;
 
-    // Themed properties.
+    /**
+     * Get the x coordinate of this control's bounds.
+     *
+     * @return The x coordinate of this control's bounds.
+     */
+    float getX() const;
     
-    //void setBorder(const Theme::Border& border, unsigned char states = STATE_ALL);
+    /**
+     * Get the y coordinate of this control's bounds.
+     *
+     * @return The y coordinate of this control's bounds.
+     */
+    float getY() const;
+
+    /**
+     * Get the width of this control's bounds.
+     *
+     * @return The width of this control's bounds.
+     */
+    float getWidth() const;
+
+    /**
+     * Get the height of this control's bounds.
+     *
+     * @return The height of this control's bounds.
+     */
+    float getHeight() const;
+
     /**
      * Set the size of this control's border.
      *
@@ -420,9 +493,8 @@ public:
      * @param opacity The new opacity.
      * @param states The states to set this property on.
      *               One or more members of the Control::State enum, ORed together.
-     * @param duration The duration to animate opacity by.
      */
-    void setOpacity(float opacity, unsigned char states = STATE_ALL, unsigned long duration = DEFAULT_UI_ANIMATION_DURATION);
+    void setOpacity(float opacity, unsigned char states = STATE_ALL);
 
     /**
      * Get the opacity of this control for a given state. 
@@ -433,17 +505,12 @@ public:
      */
     float getOpacity(State state = NORMAL) const;
 
-    // TODO
-    // Controls must state the names of the images they use, for the purposes of a future UI editor.
-    //virtual std::vector<std::string> getImageNames() = 0;
-
-    // Control state.
     /**
      * Get the bounds of this control, relative to its parent container, after clipping.
      *
      * @return The bounds of this control.
      */
-    const Rectangle& getBounds() const;
+    const Rectangle& getClipBounds() const;
 
     /**
      * Get the content area of this control, in screen coordinates, after clipping.
@@ -452,18 +519,6 @@ public:
      */
     const Rectangle& getClip() const;
 
-    /**
-     * Set width and/or height to auto-size to size a Control to tightly fit
-     * its text and themed visual elements (CheckBox / RadioButton toggle etc.).
-     *
-     * Similarly set this on the width and/or height of a Container to tightly fit
-     * the Container around STATE_ALL its children.
-     *
-     * @param width Whether to automatically determine this Control's width.
-     * @param height Whether to automatically determine this Control's height.
-     */
-    void setAutoSize(bool width, bool height);
-
     /**
      * Change this control's state.
      *
@@ -552,7 +607,15 @@ public:
     void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
 
 protected:
+
+    /**
+     * Constructor.
+     */
     Control();
+
+    /**
+     * Destructor.
+     */
     virtual ~Control();
 
     /**
@@ -596,16 +659,6 @@ protected:
      */
     virtual void update(const Rectangle& clip);
 
-private:
-    /**
-     * Draws the themed border and background of a control.
-     *
-     * @param spriteBatch The sprite batch containing this control's border images.
-     * @param clip The clipping rectangle of this control's parent container.
-     */
-    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
-
-protected:
     /**
      * Draw the images associated with this control.
      *
@@ -624,46 +677,51 @@ protected:
     /**
      * Initialize properties common to STATE_ALL Controls.
      */
-    virtual void init(Theme::Style* style, Properties* properties);
+    virtual void initialize(Theme::Style* style, Properties* properties);
 
     /**
      * Container and classes that extend it should implement this and return true.
+     *
+     * @return true if this object is of class Container, false otherwise.
      */
     virtual bool isContainer();
 
     /**
      * Returns whether this control has been modified and requires an update.
+     *
+     * @return Whether this control has been modified and requires an update.
      */
     virtual bool isDirty();
 
     /**
      * Get a Control::State enum from a matching string.
+     *
+     * @param state The string to match.
+     *
+     * @return The Control::State enum that matches the given string.
      */
-    static State getStateFromString(const char* state);
+    static State getState(const char* state);
 
     /**
-     * Notify STATE_ALL listeners of a specific event.
+     * Notify this control's listeners of a specific event.
+     *
+     * @param eventType The event to trigger.
      */
     void notifyListeners(Listener::EventType eventType);
 
-    void addSpecificListener(Control::Listener* listener, Listener::EventType eventType);
-
     std::string _id;
     State _state;           // Determines overlay used during draw().
-    Vector2 _position;      // Position, relative to parent container's clipping window.
-    Vector2 _size;          // Desired size.  Will be clipped.
-    Rectangle _bounds;      // The position and size of this control, relative to parent container's bounds, including border and padding, after clipping.
+    Rectangle _bounds;      // Position, relative to parent container's clipping window, and desired size.
+    Rectangle _clipBounds;  // The position and size of this control, relative to parent container's bounds, including border and padding, after clipping.
     Rectangle _textBounds;  // The position and size of this control's text area, before clipping.  Used for text alignment.
     Rectangle _clip;        // Clipping window of this control's content, after clipping.
-    bool _autoWidth;
-    bool _autoHeight;
     bool _dirty;
     bool _consumeTouchEvents;
     Theme::Style* _style;
-    typedef std::map<Listener::EventType, std::list<Listener*>*> ListenerMap;
-    ListenerMap* _listeners;
+    std::map<Listener::EventType, std::list<Listener*>*>* _listeners;
 
 private:
+
     // Animation blending bits.
     static const char ANIMATION_POSITION_X_BIT = 0x01;
     static const char ANIMATION_POSITION_Y_BIT = 0x02;
@@ -679,6 +737,18 @@ private:
     void applyAnimationValueSizeHeight(float height, float blendWeight);
     void applyAnimationValueOpacity();
 
+    Control(const Control& copy);
+
+    /**
+     * Draws the themed border and background of a control.
+     *
+     * @param spriteBatch The sprite batch containing this control's border images.
+     * @param clip The clipping rectangle of this control's parent container.
+     */
+    virtual void drawBorder(SpriteBatch* spriteBatch, const Rectangle& clip);
+
+    void addSpecificListener(Control::Listener* listener, Listener::EventType eventType);
+
     // Gets the overlays requested in the overlayTypes bitflag.
     Theme::Style::Overlay** getOverlays(unsigned char overlayTypes, Theme::Style::Overlay** overlays);
 
@@ -689,8 +759,6 @@ private:
 
     // Ensures that this control has a copy of its style so that it can override it without affecting other controls.
     void overrideStyle();
-
-    Control(const Control& copy);
 };
 
 }

+ 7 - 1
gameplay/src/Curve.cpp

@@ -54,6 +54,11 @@ using std::strcmp;
 namespace gameplay
 {
 
+Curve* Curve::create(unsigned int pointCount, unsigned int componentCount)
+{
+    return new Curve(pointCount, componentCount);
+}
+
 Curve::Curve(unsigned int pointCount, unsigned int componentCount)
     : _pointCount(pointCount), _componentCount(componentCount), _componentSize(sizeof(float)*componentCount), _quaternionOffset(NULL), _points(NULL)
 {
@@ -146,8 +151,9 @@ void Curve::evaluate(float time, float* dst) const
 {
     assert(dst && time >= 0 && time <= 1.0f);
 
+    // Check if the point count is 1.
     // Check if we are at or beyond the bounds of the curve.
-    if (time <= _points[0].time)
+    if (_pointCount == 1 || time <= _points[0].time)
     {
         memcpy(dst, _points[0].value, _componentSize);
         return;

+ 26 - 11
gameplay/src/Curve.h

@@ -1,14 +1,17 @@
 #ifndef CURVE_H_
 #define CURVE_H_
 
+#include "Ref.h"
+
 namespace gameplay
 {
 
 /**
  * Represents an n-dimensional curve.
  */
-class Curve
+class Curve : public Ref
 {
+    friend class AnimationTarget;
     friend class Animation;
     friend class AnimationClip;
     friend class AnimationController;
@@ -272,19 +275,13 @@ public:
         BOUNCE_OUT_IN
     };
 
-
     /**
-     * Constructs a new curve and the specified parameters.
-     *
+     * Creates a new curve.
+     * 
      * @param pointCount The number of points in the curve.
      * @param componentCount The number of float component values per key value.
      */
-    Curve(unsigned int pointCount, unsigned int componentCount);
-
-    /**
-     * Destructor.
-     */
-    ~Curve();
+    static Curve* create(unsigned int pointCount, unsigned int componentCount);
 
     /**
      * Gets the number of points in the curve.
@@ -396,10 +393,28 @@ private:
     Curve();
 
     /**
-     * Constructor.
+     * Constructs a new curve and the specified parameters.
+     *
+     * @param pointCount The number of points in the curve.
+     * @param componentCount The number of float component values per key value.
+     */
+    Curve(unsigned int pointCount, unsigned int componentCount);
+
+    /**
+     * Hidden copy constructor.
      */
     Curve(const Curve& copy);
 
+    /**
+     * Destructor.
+     */
+    ~Curve();
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    Curve& operator=(const Curve&);
+
     /**
      * Bezier interpolation function.
      */

+ 19 - 26
gameplay/src/Form.cpp

@@ -58,24 +58,9 @@ namespace gameplay
         // Create new form with given ID, theme and layout.
         const char* themeFile = formProperties->getString("theme");
         const char* layoutString = formProperties->getString("layout");
-        Form* form = Form::create(themeFile, getLayoutType(layoutString));
-
-        Theme* theme = form->_theme;
-        const char* styleName = formProperties->getString("style");
-        form->init(theme->getStyle(styleName), formProperties);
-
-        // Add all the controls to the form.
-        form->addControls(theme, formProperties);
-
-        SAFE_DELETE(properties);
-
-        return form;
-    }
-
-    Form* Form::create(const char* themeFile, Layout::Type type)
-    {
+        
         Layout* layout;
-        switch (type)
+        switch (getLayoutType(layoutString))
         {
         case Layout::LAYOUT_ABSOLUTE:
             layout = AbsoluteLayout::create();
@@ -95,6 +80,15 @@ namespace gameplay
         form->_layout = layout;
         form->_theme = theme;
 
+        //Theme* theme = form->_theme;
+        const char* styleName = formProperties->getString("style");
+        form->initialize(theme->getStyle(styleName), formProperties);
+
+        // Add all the controls to the form.
+        form->addControls(theme, formProperties);
+
+        SAFE_DELETE(properties);
+
         __forms.push_back(form);
 
         return form;
@@ -136,9 +130,9 @@ namespace gameplay
         if (_node && !_quad)
         {
             // Set this Form up to be 3D by initializing a quad, projection matrix and viewport.
-            setQuad(0.0f, 0.0f, _size.x, _size.y);
+            setQuad(0.0f, 0.0f, _bounds.width, _bounds.height);
 
-            Matrix::createOrthographicOffCenter(0, _size.x, _size.y, 0, 0, 1, &_projectionMatrix);
+            Matrix::createOrthographicOffCenter(0, _bounds.width, _bounds.height, 0, 0, 1, &_projectionMatrix);
             _theme->setProjectionMatrix(_projectionMatrix);
             
             _node->setModel(_quad);
@@ -147,7 +141,7 @@ namespace gameplay
 
     void Form::update()
     {
-        Container::update(Rectangle(0, 0, _size.x, _size.y));
+        Container::update(Rectangle(0, 0, _bounds.width, _bounds.height));
     }
 
     void Form::draw()
@@ -171,7 +165,7 @@ namespace gameplay
                 Game* game = Game::getInstance();
                 Rectangle prevViewport = game->getViewport();
                 
-                game->setViewport(Rectangle(_position.x, _position.y, _size.x, _size.y));
+                game->setViewport(Rectangle(_bounds.x, _bounds.y, _bounds.width, _bounds.height));
 
                 draw(_theme->getSpriteBatch(), _clip);
 
@@ -199,7 +193,7 @@ namespace gameplay
 
         // Draw the form's border and background.
         // We don't pass the form's position to itself or it will be applied twice!
-        Control::drawBorder(spriteBatch, Rectangle(0, 0, _size.x, _size.y));
+        Control::drawBorder(spriteBatch, Rectangle(0, 0, _bounds.width, _bounds.height));
 
         // Draw each control's border and background.
         for (it = _controls.begin(); it < _controls.end(); it++)
@@ -244,7 +238,6 @@ namespace gameplay
         // Set the common render state block for the material
         RenderState::StateBlock* stateBlock = _theme->getSpriteBatch()->getStateBlock();
         stateBlock->setDepthWrite(true);
-        //material->setStateBlock(_theme->getSpriteBatch()->getStateBlock());
         material->setStateBlock(stateBlock);
 
         // Bind the WorldViewProjection matrix
@@ -259,7 +252,7 @@ namespace gameplay
         // Use the FrameBuffer to texture the quad.
         if (!_frameBuffer->getRenderTarget())
         {
-            RenderTarget* rt = RenderTarget::create(_id.c_str(), _size.x, _size.y);
+            RenderTarget* rt = RenderTarget::create(_id.c_str(), _bounds.width, _bounds.height);
             _frameBuffer->setRenderTarget(rt);
             SAFE_RELEASE(rt);
         }
@@ -327,7 +320,7 @@ namespace gameplay
                             m.transformPoint(&point);
 
                             // Pass the touch event on.
-                            const Rectangle& bounds = form->getBounds();
+                            const Rectangle& bounds = form->getClipBounds();
                             if (form->getState() == Control::FOCUS ||
                                 (evt == Touch::TOUCH_PRESS &&
                                  point.x >= bounds.x &&
@@ -346,7 +339,7 @@ namespace gameplay
                 else
                 {
                     // Simply compare with the form's bounds.
-                    const Rectangle& bounds = form->getBounds();
+                    const Rectangle& bounds = form->getClipBounds();
                     if (form->getState() == Control::FOCUS ||
                         (evt == Touch::TOUCH_PRESS &&
                          x >= bounds.x &&

+ 19 - 14
gameplay/src/Form.h

@@ -37,13 +37,13 @@ public:
      *      size     = <width, height>      // Size of the form, measured in pixels.
      *   
      *      // All the controls within this form.
-     *      container{}
-     *      label{}
-     *      textBox{}
-     *      button{}
-     *      checkBox{}
-     *      radioButton{}
-     *      slider{}
+     *      container { }
+     *      label { }
+     *      textBox { }
+     *      button { }
+     *      checkBox { }
+     *      radioButton { }
+     *      slider { }
      * }
      *
      * @param path Path to the properties file to create a new form from.
@@ -105,13 +105,22 @@ public:
      */
     void draw();
 
-protected:
+private:
     
+    /**
+     * Constructor.
+     */
     Form();
 
-    virtual ~Form();
+    /**
+     * Constructor.
+     */
+    Form(const Form& copy);
 
-    static Form* create(const char* textureFile, Layout::Type type);
+    /**
+     * Destructor.
+     */
+    virtual ~Form();
 
     /**
      * Initialize a quad for this form in order to draw it in 3D.
@@ -145,10 +154,6 @@ protected:
     Node* _node;                // Node for transforming this Form in world-space.
     FrameBuffer* _frameBuffer;  // FBO the Form is rendered into for texturing the quad.
     Matrix _projectionMatrix;   // Orthographic projection matrix to be set on SpriteBatch objects when rendering into the FBO.
-
-private:
-
-    Form(const Form& copy);
 };
 
 }

+ 3 - 3
gameplay/src/Game.cpp

@@ -130,9 +130,9 @@ void Game::shutdown()
         SAFE_DELETE(_audioListener);
 
         RenderState::finalize();
+        
+        _state = UNINITIALIZED;
     }
-
-    _state = UNINITIALIZED;
 }
 
 void Game::pause()
@@ -159,7 +159,7 @@ void Game::resume()
     }
 }
 
-void Game::exit()
+void Game::end()
 {
     shutdown();
 }

+ 2 - 2
gameplay/src/Game.h

@@ -119,9 +119,9 @@ public:
     void resume();
 
     /**
-     * Exits the game.
+     * Ends the game.
      */
-    void exit();
+    void end();
 
     /**
      * Platform frame delagate.

+ 7 - 10
gameplay/src/Label.cpp

@@ -18,14 +18,14 @@ namespace gameplay
     Label* Label::create(Theme::Style* style, Properties* properties)
     {
         Label* label = new Label();
-        label->init(style, properties);
+        label->initialize(style, properties);
 
         return label;
     }
 
-    void Label::init(Theme::Style* style, Properties* properties)
+    void Label::initialize(Theme::Style* style, Properties* properties)
     {
-        Control::init(style, properties);
+        Control::initialize(style, properties);
 
         const char* text = properties->getString("text");
         if (text)
@@ -69,16 +69,13 @@ namespace gameplay
         if (_text.size() <= 0)
             return;
 
-        // TODO: Batch all labels that use the same font.
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Font* font = overlay->getFont();
-
-        Vector4 textColor = overlay->getTextColor();
-        textColor.w *= overlay->getOpacity();
+        Font* font = getFont(_state);
+        Vector4 textColor = getTextColor(_state);
+        textColor.w *= getOpacity(_state);
 
         // Draw the text.
         font->begin();
-        font->drawText(_text.c_str(), _textBounds, textColor, overlay->getFontSize(), overlay->getTextAlignment(), true, overlay->getTextRightToLeft(), &_clip);
+        font->drawText(_text.c_str(), _textBounds, textColor, getFontSize(_state), getTextAlignment(_state), true, getTextRightToLeft(_state), &_clip);
         font->end();
 
         _dirty = false;

+ 11 - 1
gameplay/src/Label.h

@@ -25,6 +25,7 @@ class Label : public Control
     friend class Container;
 
 public:
+
     /**
      * Set the text for this label to display.
      *
@@ -52,7 +53,15 @@ public:
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
 protected:
+
+    /**
+     * Constructor.
+     */
     Label();
+
+    /**
+     * Destructor.
+     */
     virtual ~Label();
 
     /**
@@ -68,7 +77,7 @@ protected:
     /**
      * Initialize this label.
      */
-    virtual void init(Theme::Style* style, Properties* properties);
+    virtual void initialize(Theme::Style* style, Properties* properties);
 
     /**
      * Draw this label's text.
@@ -80,6 +89,7 @@ protected:
     std::string _text;      // The text displayed by this label.
 
 private:
+
     Label(const Label& copy);
 };
 

+ 1 - 0
gameplay/src/Material.h

@@ -23,6 +23,7 @@ class Material : public RenderState
     friend class Technique;
     friend class Pass;
     friend class RenderState;
+    friend class Node;
 
 public:
 

+ 63 - 0
gameplay/src/Node.cpp

@@ -594,6 +594,69 @@ void Node::setBoundsDirty()
         _parent->setBoundsDirty();
 }
 
+Animation* Node::getAnimation(const char* id) const
+{
+    Animation* animation = ((AnimationTarget*)this)->getAnimation(id);
+    if (animation)
+        return animation;
+    
+    // See if this node has a model, then drill down.
+    Model* model = this->getModel();
+    if (model)
+    {
+        // Check to see if there's any animations with the ID on the joints.
+        MeshSkin* skin = model->getSkin();
+        if (skin)
+        {
+            Joint* rootJoint = skin->getRootJoint();
+            if (rootJoint)
+            {
+                animation = rootJoint->getAnimation(id);
+                if (animation)
+                    return animation;
+            }
+        }
+
+        // Check to see if any of the model's material parameter's has an animation
+        // with the given ID.
+        Material* material = model->getMaterial();
+        if (material)
+        {
+            // How to access material parameters? hidden on the Material::RenderState.
+            std::vector<MaterialParameter*>::iterator itr = material->_parameters.begin();
+            for (; itr != material->_parameters.end(); itr++)
+            {
+                animation = ((MaterialParameter*)(*itr))->getAnimation(id);
+                if (animation)
+                    return animation;
+            }
+        }
+    }
+
+    // look through form for animations.
+    Form* form = this->getForm();
+    if (form)
+    {
+        animation = form->getAnimation(id);
+        if (animation)
+            return animation;
+    }
+
+    // Look through this node's children for an animation with the specified ID.
+    unsigned int childCount = this->getChildCount();
+    Node* child = this->getFirstChild();
+    for (unsigned int i = 0; i < childCount; i++)
+    {
+        animation = child->getAnimation(id);
+        if (animation)
+            return animation;
+
+        child = child->getNextSibling();
+    }
+    
+    return NULL;
+}
+
 Camera* Node::getCamera() const
 {
     return _camera;

+ 8 - 0
gameplay/src/Node.h

@@ -343,6 +343,14 @@ public:
      */
     Vector3 getActiveCameraTranslationView() const;
 
+    /**
+     * Gets the first animation in the node hierarchy with the specified ID.
+     *
+     * @param id The ID of the animation to get. Returns the first animation if ID is NULL.
+     * @return The first animation with the specified ID.
+     */
+    Animation* getAnimation(const char* id = NULL) const;
+
     /**
      * Returns the pointer to this node's camera.
      *

+ 2 - 1
gameplay/src/Package.cpp

@@ -964,11 +964,12 @@ Animation* Package::readAnimationChannel(Scene* scene, Animation* animation, con
         if (animation == NULL)
         {
             // TODO: This code currently assumes LINEAR only
-            animation = controller->createAnimation(animationId, target, targetAttribute, keyTimesCount, &keyTimes[0], &values[0], Curve::LINEAR);
+            animation = target->createAnimation(animationId, targetAttribute, keyTimesCount, &keyTimes[0], &values[0], Curve::LINEAR);
         }
         else
         {
             animation->createChannel(target, targetAttribute, keyTimesCount, &keyTimes[0], &values[0], Curve::LINEAR);
+            animation->addRef();
         }
     }
 

+ 3 - 4
gameplay/src/PlatformMacOS.mm

@@ -67,7 +67,7 @@ static View* __view = NULL;
 -(void)windowWillClose:(NSNotification*)note 
 {
     [lock lock];
-    _game->exit();
+    _game->end();
     [lock unlock];
     [[NSApplication sharedApplication] terminate:self];
 }
@@ -88,10 +88,9 @@ static View* __view = NULL;
     [lock lock];
 
     [[self openGLContext] makeCurrentContext];
-    
     CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
     
-    if (_game && _game->getState() == Game::RUNNING)       
+    if (_game && _game->getState() == Game::RUNNING)  
         _game->frame();
     
     CGLFlushDrawable((CGLContextObj)[[self openGLContext] CGLContextObj]);
@@ -176,7 +175,7 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
     CVDisplayLinkStop(displayLink);
     CVDisplayLinkRelease(displayLink);
     
-    _game->exit();
+    _game->end();
     
     [lock unlock];
 

+ 2 - 2
gameplay/src/PlatformQNX.cpp

@@ -962,7 +962,7 @@ int Platform::enterMessagePump()
                     _game->menu();
                     break;
                 case NAVIGATOR_EXIT:
-                    _game->exit();
+                    _game->end();
                     break;
                 }
             }
@@ -990,7 +990,7 @@ int Platform::enterMessagePump()
         rc = eglSwapBuffers(__eglDisplay, __eglSurface);
         if (rc != EGL_TRUE)
         {
-            _game->exit();
+            _game->end();
             perror("eglSwapBuffers");
             break;
         }

+ 1 - 1
gameplay/src/PlatformWin32.cpp

@@ -589,7 +589,7 @@ int Platform::enterMessagePump()
 
             if (msg.message == WM_QUIT)
             {
-                _game->exit();
+                _game->end();
                 break;
             }
         }

+ 14 - 8
gameplay/src/RadioButton.cpp

@@ -27,7 +27,7 @@ RadioButton::~RadioButton()
 RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
 {
     RadioButton* radioButton = new RadioButton();
-    radioButton->init(style, properties);
+    radioButton->initialize(style, properties);
 
     properties->getVector2("imageSize", &radioButton->_imageSize);
 
@@ -48,6 +48,11 @@ RadioButton* RadioButton::create(Theme::Style* style, Properties* properties)
     return radioButton;
 }
 
+bool RadioButton::isSelected() const
+{
+    return _selected;
+}
+
 void RadioButton::setImageSize(float width, float height)
 {
     _imageSize.set(width, height);
@@ -82,8 +87,8 @@ bool RadioButton::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int c
         {
             if (_state == Control::ACTIVE)
             {
-                if (x > 0 && x <= _bounds.width &&
-                    y > 0 && y <= _bounds.height)
+                if (x > 0 && x <= _clipBounds.width &&
+                    y > 0 && y <= _clipBounds.height)
                 {
                     if (!_selected)
                     {
@@ -110,6 +115,7 @@ void RadioButton::clearSelected(const std::string& groupId)
         {
             radioButton->_selected = false;
             radioButton->_dirty = true;
+            radioButton->notifyListeners(Listener::VALUE_CHANGED);
         }
     }
 }
@@ -119,7 +125,7 @@ void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     // Left, v-center.
     // TODO: Set an alignment for radio button images.
     const Theme::Border border = getBorder(_state);
-    const Theme::Padding padding = _style->getPadding();
+    const Theme::Padding padding = getPadding();
     float opacity = getOpacity(_state);
 
     if (_selected)
@@ -139,8 +145,8 @@ void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
             size.set(_imageSize);
         }
 
-        Vector2 pos(clip.x + _position.x + border.left + padding.left,
-            clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+        Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
+            clip.y + _bounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
         spriteBatch->draw(pos.x, pos.y, size.x, size.y, selected.u1, selected.v1, selected.u2, selected.v2, selectedColor, _clip);
     }
@@ -161,8 +167,8 @@ void RadioButton::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
             size.set(_imageSize);
         }
 
-        Vector2 pos(clip.x + _position.x + border.left + padding.left,
-            clip.y + _position.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
+        Vector2 pos(clip.x + _bounds.x + border.left + padding.left,
+            clip.y + _bounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f - size.y / 2.0f);
 
         spriteBatch->draw(pos.x, pos.y, size.x, size.y, unselected.u1, unselected.v1, unselected.u2, unselected.v2, unselectedColor, _clip);
     }

+ 39 - 2
gameplay/src/RadioButton.h

@@ -36,7 +36,7 @@ public:
      *
      * @return Whether this radio button is currently selected.
      */
-    bool isSelected();
+    bool isSelected() const;
 
     /**
      * Set the size to draw the radio button icon.
@@ -66,7 +66,15 @@ public:
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
 protected:
+
+    /**
+     * Constructor.
+     */
     RadioButton();
+
+    /**
+     * Destructor.
+     */
     virtual ~RadioButton();
 
     /**
@@ -79,13 +87,41 @@ protected:
      */
     static RadioButton* create(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 clip The clipping rectangle of this control's parent container.
+     */
     void update(const Rectangle& clip);
 
+    /**
+     * 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.
+     */
     void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
-    // Clear the _selected flag of all radio buttons in the given group.
+    /**
+     * Clear the _selected flag of all radio buttons in the given group.
+     *
+     * @param groupID The group to clear.
+     */
     static void clearSelected(const std::string& groupId);
 
     std::string _groupId;
@@ -93,6 +129,7 @@ protected:
     Vector2 _imageSize;
 
 private:
+
     RadioButton(const RadioButton& copy);
 };
 

+ 1 - 1
gameplay/src/SceneLoader.cpp

@@ -668,7 +668,7 @@ void SceneLoader::createAnimations(const Scene* scene)
             }
         }
 
-        Game::getInstance()->getAnimationController()->createAnimation(_animations[i]._animationID, node, p);
+        node->createAnimation(_animations[i]._animationID, p);
     }
 }
 

+ 15 - 15
gameplay/src/Slider.cpp

@@ -14,7 +14,7 @@ Slider::~Slider()
 Slider* Slider::create(Theme::Style* style, Properties* properties)
 {
     Slider* slider = new Slider();
-    slider->init(style, properties);
+    slider->initialize(style, properties);
 
     slider->_min = properties->getFloat("min");
     slider->_max = properties->getFloat("max");
@@ -91,8 +91,8 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
     case Touch::TOUCH_MOVE:
     case Touch::TOUCH_RELEASE:
         if (_state == ACTIVE &&
-            x > 0 && x <= _bounds.width &&
-            y > 0 && y <= _bounds.height)
+            x > 0 && x <= _clipBounds.width &&
+            y > 0 && y <= _clipBounds.height)
         {
             // Horizontal case.
             const Theme::Border& border = getBorder(_state);
@@ -101,7 +101,7 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
             const Rectangle& maxCapRegion = getImageRegion("maxCap", _state);
 
             float markerPosition = ((float)x - maxCapRegion.width - border.left - padding.left) /
-                (_bounds.width - border.left - border.right - padding.left - padding.right - minCapRegion.width - maxCapRegion.width);
+                (_clipBounds.width - border.left - border.right - padding.left - padding.right - minCapRegion.width - maxCapRegion.width);
             
             if (markerPosition > 1.0f)
             {
@@ -113,7 +113,7 @@ bool Slider::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contac
             }
 
             float oldValue = _value;
-            _value = markerPosition * _max;
+            _value = (markerPosition * (_max - _min)) + _min;
             if (_step > 0.0f)
             {
                 float stepDistance = _step / (_max - _min);
@@ -167,23 +167,23 @@ void Slider::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
     trackColor.w *= opacity;
 
     // Draw order: track, caps, marker.
-    float midY = clip.y + _bounds.y + (_bounds.height - border.bottom - padding.bottom) / 2.0f;
-    Vector2 pos(clip.x + _bounds.x + border.left + padding.left, midY - trackRegion.height / 2.0f);
-    spriteBatch->draw(pos.x, pos.y, _bounds.width, trackRegion.height, track.u1, track.v1, track.u2, track.v2, trackColor);
+    float midY = clip.y + _clipBounds.y + (_clipBounds.height - border.bottom - padding.bottom) / 2.0f;
+    Vector2 pos(clip.x + _clipBounds.x + border.left + padding.left, midY - trackRegion.height / 2.0f);
+    spriteBatch->draw(pos.x, pos.y, _clipBounds.width, trackRegion.height, track.u1, track.v1, track.u2, track.v2, trackColor, _clip);
 
     pos.y = midY - minCapRegion.height * 0.5f;
     pos.x -= minCapRegion.width * 0.5f;
-    spriteBatch->draw(pos.x, pos.y, minCapRegion.width, minCapRegion.height, minCap.u1, minCap.v1, minCap.u2, minCap.v2, minCapColor);
+    spriteBatch->draw(pos.x, pos.y, minCapRegion.width, minCapRegion.height, minCap.u1, minCap.v1, minCap.u2, minCap.v2, minCapColor, _clip);
         
-    pos.x = clip.x + _bounds.x + _bounds.width - border.right - padding.right - maxCapRegion.width * 0.5f;
-    spriteBatch->draw(pos.x, pos.y, maxCapRegion.width, maxCapRegion.height, maxCap.u1, maxCap.v1, maxCap.u2, maxCap.v2, maxCapColor);
+    pos.x = clip.x + _clipBounds.x + _clipBounds.width - border.right - padding.right - maxCapRegion.width * 0.5f;
+    spriteBatch->draw(pos.x, pos.y, maxCapRegion.width, maxCapRegion.height, maxCap.u1, maxCap.v1, maxCap.u2, maxCap.v2, maxCapColor, _clip);
 
     // Percent across.
-    float markerPosition = _value / (_max - _min);
-    markerPosition *= _bounds.width - border.left - padding.left - border.right - padding.right - minCapRegion.width * 0.5f - maxCapRegion.width * 0.5f - markerRegion.width;
-    pos.x = clip.x + _bounds.x + border.left + padding.left + minCapRegion.width * 0.5f + markerPosition;
+    float markerPosition = (_value - _min) / (_max - _min);
+    markerPosition *= _clipBounds.width - border.left - padding.left - border.right - padding.right - minCapRegion.width * 0.5f - maxCapRegion.width * 0.5f - markerRegion.width;
+    pos.x = clip.x + _clipBounds.x + border.left + padding.left + minCapRegion.width * 0.5f + markerPosition;
     pos.y = midY - markerRegion.height / 2.0f;
-    spriteBatch->draw(pos.x, pos.y, markerRegion.width, markerRegion.height, marker.u1, marker.v1, marker.u2, marker.v2, markerColor);
+    spriteBatch->draw(pos.x, pos.y, markerRegion.width, markerRegion.height, marker.u1, marker.v1, marker.u2, marker.v2, markerColor, _clip);
 }
 
 }

+ 46 - 0
gameplay/src/Slider.h

@@ -32,6 +32,7 @@ class Slider : public Label
     friend class Container;
 
 public:
+
     /**
      * Set the minimum value that can be set on this slider.
      *
@@ -90,16 +91,60 @@ public:
      */
     float getValue();
 
+    /**
+     * 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);
 
 protected:
+
+    /**
+     * Constructor.
+     */
     Slider();
+
+    /**
+     * Destructor.
+     */
     ~Slider();
 
+    /**
+     * Create a slider with a given style and properties.
+     *
+     * @param style The style to apply to this slider.
+     * @param properties The properties to set on this slider.
+     *
+     * @return The new slider.
+     */
     static Slider* create(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);
 
+    /**
+     * 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.
+     */
     void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
     float _min;
@@ -108,6 +153,7 @@ protected:
     float _value;
 
 private:
+
     Slider(const Slider& copy);
 };
 

+ 80 - 70
gameplay/src/TextBox.cpp

@@ -19,7 +19,7 @@ TextBox::~TextBox()
 TextBox* TextBox::create(Theme::Style* style, Properties* properties)
 {
     TextBox* textBox = new TextBox();
-    textBox->init(style, properties);
+    textBox->initialize(style, properties);
 
     return textBox;
 }
@@ -29,15 +29,6 @@ int TextBox::getLastKeypress()
     return _lastKeypress;
 }
 
-void TextBox::setCursorLocation(int x, int y)
-{
-    Theme::Border border = getBorder(_state);
-    Theme::Padding padding = getPadding();
-
-    _cursorLocation.set(x - border.left - padding.left + _clip.x,
-                       y - border.top - padding.top + _clip.y);
-}
-
 void TextBox::addListener(Control::Listener* listener, int eventFlags)
 {
     if ((eventFlags & Listener::VALUE_CHANGED) == Listener::VALUE_CHANGED)
@@ -66,8 +57,8 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
             _dirty = true;
             return _consumeTouchEvents;
         }
-        else if (!(x > 0 && x <= _bounds.width &&
-                    y > 0 && y <= _bounds.height))
+        else if (!(x > 0 && x <= _clipBounds.width &&
+                    y > 0 && y <= _clipBounds.height))
         {
             _state = NORMAL;
             Game::getInstance()->displayKeyboard(false);
@@ -77,19 +68,19 @@ bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int conta
         break;
     case Touch::TOUCH_MOVE:
         if (_state == FOCUS &&
-            x > 0 && x <= _bounds.width &&
-            y > 0 && y <= _bounds.height)
+            x > 0 && x <= _clipBounds.width &&
+            y > 0 && y <= _clipBounds.height)
         {
-            setCursorLocation(x, y);
+            setCaretLocation(x, y);
             _dirty = true;
             return _consumeTouchEvents;
         }
         break;
     case Touch::TOUCH_RELEASE:
-        if (x > 0 && x <= _bounds.width &&
-            y > 0 && y <= _bounds.height)
+        if (x > 0 && x <= _clipBounds.width &&
+            y > 0 && y <= _clipBounds.height)
         {
-            setCursorLocation(x, y);
+            setCaretLocation(x, y);
             _state = FOCUS;
             _dirty = true;
             return _consumeTouchEvents;
@@ -115,7 +106,7 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         // TODO: Move cursor to beginning of line.
                         // This only works for left alignment...
                         
-                        //_cursorLocation.x = _clip.x;
+                        //_caretLocation.x = _clip.x;
                         //_dirty = true;
                         break;
                     }
@@ -126,66 +117,71 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                     }
                     case Keyboard::KEY_DELETE:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
-                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
 
+                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                            textAlignment, true, rightToLeft);
                         _text.erase(textIndex, 1);
-                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex,
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         notifyListeners(Listener::TEXT_CHANGED);
                         break;
                     }
                     case Keyboard::KEY_LEFT_ARROW:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
-                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
-
-                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex - 1,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
+
+                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                            textAlignment, true, rightToLeft);
+                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex - 1,
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         break;
                     }
                     case Keyboard::KEY_RIGHT_ARROW:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
-                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
-
-                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex + 1,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
+
+                        unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                            textAlignment, true, rightToLeft);
+                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex + 1,
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         break;
                     }
                     case Keyboard::KEY_UP_ARROW:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
-
-                        _cursorLocation.y -= fontSize;
-                        font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
+
+                        _caretLocation.y -= fontSize;
+                        font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         break;
                     }
                     case Keyboard::KEY_DOWN_ARROW:
                     {
-                        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                        Font* font = overlay->getFont();
-                        unsigned int fontSize = overlay->getFontSize();
-
-                        _cursorLocation.y += fontSize;
-                        font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        Font* font = getFont(_state);
+                        unsigned int fontSize = getFontSize(_state);
+                        Font::Justify textAlignment = getTextAlignment(_state);
+                        bool rightToLeft = getTextRightToLeft(_state);
+
+                        _caretLocation.y += fontSize;
+                        font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                            textAlignment, true, rightToLeft);
                         _dirty = true;
                         break;
                     }
@@ -195,11 +191,13 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
 
             case Keyboard::KEY_CHAR:
             {
-                Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-                Font* font = overlay->getFont();
-                unsigned int fontSize = overlay->getFontSize();
-                unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _cursorLocation, &_cursorLocation,
-                    overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                Font* font = getFont(_state);
+                unsigned int fontSize = getFontSize(_state);
+                Font::Justify textAlignment = getTextAlignment(_state);
+                bool rightToLeft = getTextRightToLeft(_state);
+
+                unsigned int textIndex = font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+                    textAlignment, true, rightToLeft);
 
                 switch (key)
                 {
@@ -209,8 +207,8 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         {
                             --textIndex;
                             _text.erase(textIndex, 1);
-                            font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex,
-                                overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                            font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex,
+                                textAlignment, true, rightToLeft);
 
                             _dirty = true;
                         }
@@ -225,8 +223,8 @@ void TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
                         _text.insert(textIndex, 1, (char)key);
 
                         // Get new location of cursor.
-                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_cursorLocation, textIndex + 1,
-                            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+                        font->getLocationAtIndex(_text.c_str(), _clip, fontSize, &_caretLocation, textIndex + 1,
+                            textAlignment, true, rightToLeft);
                 
                         _dirty = true;
                         break;
@@ -250,10 +248,13 @@ void TextBox::update(const Rectangle& clip)
     // Get index into string and cursor location from the last recorded touch location.
     if (_state == FOCUS)
     {
-        Theme::Style::Overlay* overlay = _style->getOverlay(getOverlayType());
-        Font* font = overlay->getFont();
-        font->getIndexAtLocation(_text.c_str(), _clip, overlay->getFontSize(), _cursorLocation, &_cursorLocation,
-            overlay->getTextAlignment(), true, overlay->getTextRightToLeft());
+        Font* font = getFont(_state);
+        unsigned int fontSize = getFontSize(_state);
+        Font::Justify textAlignment = getTextAlignment(_state);
+        bool rightToLeft = getTextRightToLeft(_state);
+
+        font->getIndexAtLocation(_text.c_str(), _clip, fontSize, _caretLocation, &_caretLocation,
+            textAlignment, true, rightToLeft);
     }
 }
 
@@ -269,11 +270,20 @@ void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
             const Theme::UVs uvs = getImageUVs("textCaret", _state);
             unsigned int fontSize = getFontSize(_state);
 
-            spriteBatch->draw(_cursorLocation.x - (region.width / 2.0f), _cursorLocation.y, region.width, fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
+            spriteBatch->draw(_caretLocation.x - (region.width / 2.0f), _caretLocation.y, region.width, fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color);
         }
     }
 
     _dirty = false;
 }
 
+void TextBox::setCaretLocation(int x, int y)
+{
+    Theme::Border border = getBorder(_state);
+    Theme::Padding padding = getPadding();
+
+    _caretLocation.set(x - border.left - padding.left + _clip.x,
+                       y - border.top - padding.top + _clip.y);
+}
+
 }

+ 60 - 4
gameplay/src/TextBox.h

@@ -31,6 +31,7 @@ class TextBox : public Label
     friend class Container;
 
 public:
+
     /**
      * Add a listener to be notified of specific events affecting
      * this control.  Event types can be OR'ed together.
@@ -43,31 +44,86 @@ public:
      */
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
+    /**
+     * Get the last key pressed within this text box.
+     *
+     * @return The last key pressed within this text box.
+     */
     int getLastKeypress();
 
 protected:
+
+    /**
+     * Constructor.
+     */
     TextBox();
+
+    /**
+     * Destructor.
+     */
     ~TextBox();
 
+    /**
+     * Create a text box with a given style and properties.
+     *
+     * @param style The style to apply to this text box.
+     * @param properties The properties to set on this text box.
+     *
+     * @return The new text box.
+     */
     static TextBox* create(Theme::Style* style, Properties* properties);
 
-    void setCursorLocation(int x, int y);
-
+    /**
+     * 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);
 
+    /**
+     * Keyboard callback on key events.
+     *
+     * @param evt The key event that occured.
+     * @param key If evt is KEY_PRESS or KEY_RELEASE then key is the key code from Keyboard::Key.
+     *            If evt is KEY_CHAR then key is the unicode value of the character.
+     * 
+     * @see Keyboard::KeyEvent
+     * @see Keyboard::Key
+     */
     void keyEvent(Keyboard::KeyEvent evt, int key);
 
+    /**
+     * 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.
+     */
     void update(const Rectangle& clip);
 
-    // Draw the cursor.
+    /**
+     * 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.
+     */
     void drawImages(SpriteBatch* spriteBatch, const Rectangle& clip);
 
-    Vector2 _cursorLocation;
+    Vector2 _caretLocation;
     unsigned int textIndex;
     int _lastKeypress;
 
 private:
+
     TextBox(const TextBox& copy);
+
+    void setCaretLocation(int x, int y);
 };
 
 }

+ 2 - 466
gameplay/src/Theme.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "Theme.h"
+#include "ThemeStyle.h"
 
 namespace gameplay
 {
@@ -9,7 +10,7 @@ namespace gameplay
     {
     }
 
-    Theme::Theme(const Theme* theme)
+    Theme::Theme(const Theme& theme)
     {
     }
 
@@ -711,471 +712,6 @@ namespace gameplay
     {
         return _color;
     }
-
-    /****************
-     * Theme::Style *
-     ****************/
-    Theme::Style::Style(const char* id, float tw, float th,
-            const Theme::Margin& margin, const Theme::Padding& padding,
-            Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled)
-        : _id(id), _tw(tw), _th(th), _margin(margin), _padding(padding)
-    {
-        _overlays[OVERLAY_NORMAL] = normal;
-        _overlays[OVERLAY_FOCUS] = focus;
-        _overlays[OVERLAY_ACTIVE] = active;
-        _overlays[OVERLAY_DISABLED] = disabled;
-    }
-
-    Theme::Style::Style(const Style& copy)
-    {
-        _id = copy._id;
-        _margin = copy._margin;
-        _padding = copy._padding;
-        _tw = copy._tw;
-        _th = copy._th;
-
-        for (int i = 0; i < MAX_OVERLAYS; i++)
-        {
-            _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
-        }
-    }
-
-    Theme::Style::~Style()
-    {
-        for (unsigned int i = 0; i < MAX_OVERLAYS; i++)
-        {
-            SAFE_RELEASE(_overlays[i]);
-        }
-    }
-    
-    const char* Theme::Style::getId() const
-    {
-        return _id.data();
-    }
-
-    Theme::Style::Overlay* Theme::Style::getOverlay(OverlayType overlayType) const
-    {
-        return _overlays[overlayType];
-    }
-
-    void Theme::Style::setMargin(float top, float bottom, float left, float right)
-    {
-        _margin.top = top;
-        _margin.bottom = bottom;
-        _margin.left = left;
-        _margin.right = right;
-    }
-
-    const Theme::Margin& Theme::Style::getMargin() const
-    {
-        return _margin;
-    }
-
-    void Theme::Style::setPadding(float top, float bottom, float left, float right)
-    {
-        _padding.top = top;
-        _padding.bottom = bottom;
-        _padding.left = left;
-        _padding.right = right;
-    }
-
-    const Theme::Padding& Theme::Style::getPadding() const
-    {
-        return _padding;
-    }
-    
-    /*************************
-     * Theme::Style::Overlay *
-     *************************/
-    Theme::Style::Overlay* Theme::Style::Overlay::create()
-    {
-        Overlay* overlay = new Overlay();
-        return overlay;
-    }
-
-    Theme::Style::Overlay::Overlay() : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL)
-    {
-    }
-
-    Theme::Style::Overlay::Overlay(const Overlay& copy) : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL)
-    {
-        if (copy._skin)
-        {
-            _skin = new Skin(*copy._skin);
-        }
-        if (copy._cursor)
-        {
-            _cursor = new Image(*copy._cursor);
-        }
-        if (copy._imageList)
-        {
-            _imageList = new ImageList(*copy._imageList);
-        }
-
-        _font = copy._font;
-        _fontSize = copy._fontSize;
-        _alignment = copy._alignment;
-        _textRightToLeft = copy._textRightToLeft;
-        _textColor = Vector4(copy._textColor);
-        _opacity = copy._opacity;
-
-        if (_font)
-        {
-            _font->addRef();
-        }
-    }
-
-    Theme::Style::Overlay::~Overlay()
-    {
-        SAFE_RELEASE(_skin);
-        SAFE_RELEASE(_imageList);
-        SAFE_RELEASE(_cursor);
-        SAFE_RELEASE(_font);
-    }
-
-    float Theme::Style::Overlay::getOpacity() const
-    {
-        return _opacity;
-    }
-
-    void Theme::Style::Overlay::setOpacity(float opacity)
-    {
-        _opacity = opacity;
-    }
-
-    void Theme::Style::Overlay::setBorder(float top, float bottom, float left, float right)
-    {
-        if (_skin)
-        {
-            _skin->_border.top = top;
-            _skin->_border.bottom = bottom;
-            _skin->_border.left = left;
-            _skin->_border.right = right;
-        }
-    }
-
-    const Theme::Border& Theme::Style::Overlay::getBorder() const
-    {
-        if (_skin)
-        {
-            return _skin->getBorder();
-        }
-        else
-        {
-            return Theme::Border::empty();
-        }
-    }
-
-    void Theme::Style::Overlay::setSkinColor(const Vector4& color)
-    {
-        if (_skin)
-        {
-            _skin->_color.set(color);
-        }
-    }
-
-    const Vector4& Theme::Style::Overlay::getSkinColor() const
-    {
-        if (_skin)
-        {
-            return _skin->getColor();
-        }
-
-        return Vector4::one();
-    }
-
-    void Theme::Style::Overlay::setSkinRegion(const Rectangle& region, float tw, float th)
-    {
-        assert(_skin);
-        _skin->setRegion(region, tw, th);
-    }
-
-    const Rectangle& Theme::Style::Overlay::getSkinRegion() const
-    {
-        if (_skin)
-        {
-            return _skin->getRegion();
-        }
-
-        return Rectangle::empty();
-    }
-
-    const Theme::UVs& Theme::Style::Overlay::getSkinUVs(Theme::Skin::SkinArea area) const
-    {
-        if (_skin)
-        {
-            return _skin->_uvs[area];
-        }
-
-        return UVs::empty();
-    }
-
-    Font* Theme::Style::Overlay::getFont() const
-    {
-        return _font;
-    }
-
-    void Theme::Style::Overlay::setFont(Font* font)
-    {
-        if (_font != font)
-        {
-            SAFE_RELEASE(_font);
-
-            _font = font;
-
-            if (_font)
-            {
-                _font->addRef();
-            }
-        }
-    }
-
-    unsigned int Theme::Style::Overlay::getFontSize() const
-    {
-        return _fontSize;
-    }
-
-    void Theme::Style::Overlay::setFontSize(unsigned int fontSize)
-    {
-        _fontSize = fontSize;
-    }
-
-    Font::Justify Theme::Style::Overlay::getTextAlignment() const
-    {
-        return _alignment;
-    }
-
-    void Theme::Style::Overlay::setTextAlignment(Font::Justify alignment)
-    {
-        _alignment = alignment;
-    }
-
-    bool Theme::Style::Overlay::getTextRightToLeft() const
-    {
-        return _textRightToLeft;
-    }
-
-    void Theme::Style::Overlay::setTextRightToLeft(bool rightToLeft)
-    {
-        _textRightToLeft = rightToLeft;
-    }
-
-    const Vector4& Theme::Style::Overlay::getTextColor() const
-    {
-        return _textColor;
-    }
-
-    void Theme::Style::Overlay::setTextColor(const Vector4& color)
-    {
-        _textColor = color;
-    }
-
-    const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
-    {
-        Image* image = _imageList->getImage(id);
-        if (image)
-        {
-            return image->getRegion();
-        }
-        else
-        {
-            return Rectangle::empty();
-        }
-    }
-    
-    void Theme::Style::Overlay::setImageRegion(const char* id, const Rectangle& region, float tw, float th)
-    {
-        Image* image = _imageList->getImage(id);
-        assert(image);
-        image->_region.set(region);
-        generateUVs(tw, th, region.x, region.y, region.width, region.height, &(image->_uvs));
-    }
-
-    const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
-    {
-        Image* image = _imageList->getImage(id);
-        if (image)
-        {
-            return image->getColor();
-        }
-        else
-        {
-            return Vector4::zero();
-        }
-    }
-
-    void Theme::Style::Overlay::setImageColor(const char* id, const Vector4& color)
-    {
-        Image* image = _imageList->getImage(id);
-        assert(image);
-        image->_color.set(color);
-    }
-
-    const Theme::UVs& Theme::Style::Overlay::getImageUVs(const char* id) const
-    {
-        Image* image = _imageList->getImage(id);
-        if (image)
-        {
-            return image->getUVs();
-        }
-        else
-        {
-            return UVs::empty();
-        }
-    }
-
-    const Rectangle& Theme::Style::Overlay::getCursorRegion() const
-    {
-        if (_cursor)
-        {
-            return _cursor->getRegion();
-        }
-        else
-        {
-            return Rectangle::empty();
-        }
-    }
-    
-    void Theme::Style::Overlay::setCursorRegion(const Rectangle& region, float tw, float th)
-    {
-        assert(_cursor);
-        _cursor->_region.set(region);
-        generateUVs(tw, th, region.x, region.y, region.width, region.height, &(_cursor->_uvs));
-    }
-
-    const Vector4& Theme::Style::Overlay::getCursorColor() const
-    {
-        if (_cursor)
-        {
-            return _cursor->getColor();
-        }
-        else
-        {
-            return Vector4::zero();
-        }
-    }
-
-    void Theme::Style::Overlay::setCursorColor(const Vector4& color)
-    {
-        assert(_cursor);
-        _cursor->_color.set(color);
-    }
-
-    const Theme::UVs& Theme::Style::Overlay::getCursorUVs() const
-    {
-        if (_cursor)
-        {
-            return _cursor->getUVs();
-        }
-        else
-        {
-            return UVs::empty();
-        }
-    }
-
-    void Theme::Style::Overlay::setSkin(Skin* skin)
-    {
-        if (_skin != skin)
-        {
-            SAFE_RELEASE(_skin);
-            _skin = skin;
-
-            if (skin)
-            {
-                skin->addRef();
-            }
-        }
-    }
-
-    Theme::Skin* Theme::Style::Overlay::getSkin() const
-    {
-        return _skin;
-    }
-
-    void Theme::Style::Overlay::setCursor(Image* cursor)
-    {
-        if (_cursor != cursor)
-        {
-            SAFE_RELEASE(_cursor);
-            _cursor = cursor;
-
-            if (cursor)
-            {
-                cursor->addRef();
-            }
-        }
-    }
-
-    Theme::Image* Theme::Style::Overlay::getCursor() const
-    {
-        return _cursor;
-    }
-            
-    void Theme::Style::Overlay::setImageList(ImageList* imageList)
-    {
-        if (_imageList != imageList)
-        {
-            SAFE_RELEASE(_imageList);
-            _imageList = imageList;
-
-            if (imageList)
-            {
-                imageList->addRef();
-            }
-        }
-    }
-    
-    Theme::ImageList* Theme::Style::Overlay::getImageList() const
-    {
-        return _imageList;
-    }
-
-    // Implementation of AnimationHandler
-    unsigned int Theme::Style::Overlay::getAnimationPropertyComponentCount(int propertyId) const
-    {
-        switch(propertyId)
-        {
-        case Theme::Style::Overlay::ANIMATE_OPACITY:
-            return 1;
-        default:
-            return -1;
-        }
-    }
-
-    void Theme::Style::Overlay::getAnimationPropertyValue(int propertyId, AnimationValue* value)
-    {
-        switch(propertyId)
-        {
-        case ANIMATE_OPACITY:
-            value->setFloat(0, _opacity);
-            break;
-        default:
-            break;
-        }
-    }
-
-    void Theme::Style::Overlay::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
-    {
-        switch(propertyId)
-        {
-            case ANIMATE_OPACITY:
-            {
-                float opacity = value->getFloat(0);
-                if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
-                {
-                    _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
-                }
-                else
-                {
-                    opacity = Curve::lerp(blendWeight, _opacity, opacity);
-                }
-                _opacity = opacity;
-                break;
-            }
-            default:
-                break;
-        }
-    }
     
     /**
      * Theme utility methods.

+ 158 - 315
gameplay/src/Theme.h

@@ -11,109 +11,122 @@
 namespace gameplay
 {
 
-static const unsigned int MAX_OVERLAYS = 4;
-static const unsigned int MAX_OVERLAY_REGIONS = 9;
-
 /**
  * A theme is created and stored as part of a form and represents its appearance.
  * Once loaded, the appearance properties can be retrieved from their style IDs and set on other
- * UI controls.  A Theme has one property, 'texture', which points to an image containing
- * all the Icon, Cursor, Slider, and Skin sprites used by the theme.  Each set of sprites within
- * the texture is described in its own namespace.  The rest of the Theme consists of Style namespaces.
- * A Style describes the border, margin, and padding of a Control, what icons and cursors (if any) are
- * associated with a Control, and Font properties to apply to a Control's text.
+ * UI controls.  A Theme has one property, 'texture', which points to a texture atlas containing
+ * all the images used by the theme.  Cursor images, skins, and lists of images used by controls
+ * are defined in their own namespaces.  The rest of the Theme consists of Style namespaces.
+ * A Style describes the border, margin, and padding of a Control, what images, skins, and cursors
+ * are associated with a Control, and Font properties to apply to a Control's text.
  *
  * Below is an explanation of the properties that can be set within themes:
  *
  * theme
  * {
- *    texture = <Path to texture>
- *
- *    // Describes the images used for CheckBox and RadioButton icons.
- *    icon <iconID>
- *    {
- *        size            =   <width, height>     // Size of this icon.
- *        offPosition     =   <x, y>              // Position of the unchecked / unselected image.
- *        onPosition      =   <x, y>              // Position of the checked / selected image.
- *        color           =   <#ffffffff>         // Tint to apply to icon.
- *    }
- *   
- *    cursor <cursorID>
- *    {
- *        region  =   <x, y, width, height>   // Region within the texture of cursor sprite.
- *        color   =   <#ffffffff>             // Tint to apply to cursor.
- *    }
- *   
- *    slider <sliderID>
- *    {
- *        minCapRegion    =   <x, y, width, height>   // Left / bottom slider cap.
- *        maxCapRegion    =   <x, y, width, height>   // Right / top slider cap.
- *        markerRegion    =   <x, y, width, height>   // Shows the slider's current position.
- *        trackRegion     =   <x, y, width, height>   // Track the marker slides along.
- *        color           =   <#ffffffff>             // Tint to apply to slider sprites.
- *    }
- *   
- *    // Defines the border and background of a Control.
- *    Skin <SkinID>
- *    {
- *        // The corners and edges of the given region will be used as border sprites.
- *        border
- *        {
- *            top     =   <int>   // Height of top border, top corners.
- *            bottom  =   <int>   // Height of bottom border, bottom corners.
- *            left    =   <int>   // Width of left border, left corners.
- *            right   =   <int>   // Width of right border, right corners.
- *        }
- *       
- *        region  =   <x, y, width, height>   // Total Skin region including entire border.
- *        color   =   <#ffffffff>             // Tint to apply to Skin sprites.
- *    }
- *   
- *    style <styleID>
- *    {
- *        // Layouts may make use of a style's margin to put space between adjacent controls.
- *        margin
- *        {
- *            top     =   <int>
- *            bottom  =   <int>
- *            left    =   <int>
- *            right   =   <int>        
- *        }
- *       
- *        // Padding is the space between a control's border and its content.
- *        padding
- *        {
- *            top     =   <int>
- *            bottom  =   <int>
- *            left    =   <int>
- *            right   =   <int>        
- *        }
- *       
- *        // This overlay is used when a control is enabled but not active or focused.
- *        normal
- *        {
- *            Skin   =   <SkinID>               // Skin to use for border and background sprites.
- *            checkBox    =   <iconID>                    // Icon to use for checkbox sprites.
- *            radioButton =   <iconID>                    // Icon to use for radioButton sprites.
- *            slider      =   <sliderID>                  // Slider to use for slider sprites.
- *            mouseCursor =   <cursorID>                  // Cursor to use when the mouse is over this control.
- *            textCursor  =   <cursorID>                  // Cursor to use within a textBox.
- *            font        =   <Path to font>              // Font to use for rendering text.
- *            fontSize    =   <int>                       // Size of text.
- *            textColor   =   <#ffffffff>                 // Color of text.
- *            alignment   =   <Text alignment constant>   // Member of Font::Justify enum.
- *            rightToLeft =   <bool>                      // Whether to draw text from right to left.
- *        }
- *       
- *        // This overlay is used when the control is in focus.
- *        // If not specified, the 'normal' overlay will be used.
- *        focus{}
- *       
- *        // This overlay is used when the control is active.
- *        // (Touch or mouse is down within the control.)
- *        // If not specified, the 'normal' overlay will be used.
- *        active{}
- *    }
+ *     texture = <Path to texture>
+ * 
+ *     // Describes a single image, to be used as a cursor.
+ *     cursor <Cursor ID>
+ *     {
+ *         region = <x, y, width, height>
+ *         color = <#ffffffff>
+ *     }
+ * 
+ *     // Describes all the images used by a control for one or more states.
+ *     imageList <ImageList ID>
+ *     {
+ *         image checked
+ *         {
+ *             region = <x, y, width, height>
+ *         }
+ * 
+ *         image unchecked
+ *         {
+ *             region = <x, y, width, height>
+ *             color = <#fffffffff>            // Optionally override image-list color.
+ *         }
+ * 
+ *         color = <#fffffffff>    // Default blend color for images that don't specify their own.
+ *     }
+ *     
+ *     // Defines the border and background of a Control.
+ *     skin <Skin ID>
+ *     {
+ *         // The corners and edges of the given region will be used as border sprites.
+ *         border
+ *         {
+ *             top     =   <int>   // Height of top border, top corners.
+ *             bottom  =   <int>   // Height of bottom border, bottom corners.
+ *             left    =   <int>   // Width of left border, left corners.
+ *             right   =   <int>   // Width of right border, right corners.
+ *         }
+ *         
+ *         region  =   <x, y, width, height>   // Total container region including entire border.
+ *         color   =   <#ffffffff>             // Tint to apply to skin.
+ *     }
+ *     
+ *     style <Style ID>
+ *     {
+ *         // Layouts may make use of a style's margin to put space between adjacent controls.
+ *         margin
+ *         {
+ *             top     =   <int>
+ *             bottom  =   <int>
+ *             left    =   <int>
+ *             right   =   <int>        
+ *         }
+ *         
+ *         // Padding is the space between a control's border and its content.
+ *         padding
+ *         {
+ *             top     =   <int>
+ *             bottom  =   <int>
+ *             left    =   <int>
+ *             right   =   <int>        
+ *         }
+ *         
+ *         // Properties used when in control is in the normal state.
+ *         stateNormal
+ *         {
+ *             skin   =   <Skin ID>             // Skin to use for border and background sprites.
+ *             imageList = <ImageList ID>
+ * 
+ *             cursor      =   <Cursor ID>                 // Cursor to use when the mouse is over this control.
+ *             font        =   <Path to font>              // Font to use for rendering text.
+ *             fontSize    =   <int>                       // Size of text.
+ *             textColor   =   <#ffffffff>                 // Color of text.
+ *             alignment   =   <Text alignment constant>   // Member of Font::Justify enum.
+ *             rightToLeft =   <bool>                      // Whether to draw text from right to left.
+ *             opacity     =   <float>                     // Opacity to apply to all text/border/icon colors.
+ *         }
+ *         
+ *         // Properties used when in control is in the focus state
+ *         // If not specified, the 'normal' overlay will be used.
+ *         stateFocus
+ *         {
+ *             skin   =   <Skin ID>             // Skin to use for border and background sprites.
+ *             ...
+ *         }
+ *         
+ *         // Properties used when in control is in the focus. 
+ *         // This is when a touch/mouse is down within the control.
+ *         // If not specified, the 'normal' overlay will be used.
+ *         stateActive
+ *         {
+ *             skin   =   <Skin ID>             // Skin to use for border and background sprites.
+ *             ...
+ *         }
+ * 
+ *         // Properties used when in control is in the focus. 
+ *         // This is when a touch/mouse is down within the control.
+ *         // If not specified, the 'normal' overlay will be used.
+ *         state-disabled
+ *         {
+ *             skin   =   <Skin ID>             // Skin to use for border and background sprites.
+ *             ...        
+ *         }
+ *     }
  * }
  *
  */
@@ -125,40 +138,23 @@ class Theme: public Ref
     friend class Skin;
 
 public:
-    class Style;
-    class Cursor;
-
-private:
-    /**
-     * Creates an instance of a Theme from a theme file.
-     *
-     * @param path Path to a theme file.
-     *
-     * @return A new Theme.
-     */
-    static Theme* create(const char* path);
 
     /**
-     * Returns style with the given name.
-     *
-     * @param id ID of the style (as specified in the Theme file).
+     * Class representing a set of themed attributes that can be
+     * assigned to a control.
      *
-     * @return Instance of the Style.
+     * Defined in "ThemeStyle.h"
      */
-    Theme::Style* getStyle(const char* id) const;
-
-    void setProjectionMatrix(const Matrix& matrix);
-
-    SpriteBatch* getSpriteBatch() const;
+    class Style;
+    friend class Style;
 
-public:
     /**
      * Struct representing the UV coordinates of a rectangular image.
      */
-    typedef class UVs
+    struct UVs
     {
-    public:
         UVs();
+
         UVs(float u1, float v1, float u2, float v2);
 
         static const UVs& empty();
@@ -167,15 +163,14 @@ public:
         float v1;
         float u2;
         float v2;
-    } UVs;
+    };
 
     /**
      * Struct representing margin, border, and padding areas by
      * the width or height of each side.
      */
-    typedef class SideRegions
+    typedef struct SideRegions
     {
-    public:
         SideRegions() : top(0), bottom(0), left(0), right(0) {}
 
         static const SideRegions& empty();
@@ -186,12 +181,20 @@ public:
         float right;
     } Margin, Border, Padding;
 
+private:
+
+    /**
+     * Class representing an image within the theme's texture atlas.
+     * An image has a region and a blend color in addition to an ID.
+     * UV coordinates are calculated from the region and can be retrieved.
+     */
     class Image : public Ref
     {
         friend class Theme;
         friend class Control;
 
     public:
+
         const char* getId() const;
 
         const UVs& getUVs() const;
@@ -201,7 +204,9 @@ public:
         const Vector4& getColor() const;
 
     private:
+
         Image(float tw, float th, const Rectangle& region, const Vector4& color);
+
         ~Image();
 
         static Image* create(float tw, float th, Properties* properties, const Vector4& defaultColor);
@@ -212,19 +217,28 @@ public:
         Vector4 _color;
     };
 
+    /**
+     * Class representing a collection of theme images.  An image list
+     * can be assigned to each overlay of a style, and controls
+     * using the style can then retrieve images by ID in order to draw themselves.
+     */
     class ImageList : public Ref
     {
         friend class Theme;
         friend class Control;
 
     public:
+
         const char* getId() const;
 
         Image* getImage(const char* imageId) const;
 
     private:
+
         ImageList(const Vector4& color);
+
         ImageList(const ImageList& copy);
+
         ~ImageList();
 
         static ImageList* create(float tw, float th, Properties* properties);
@@ -242,6 +256,7 @@ public:
         friend class Theme;
 
     public:
+
         enum SkinArea
         {
             TOP_LEFT, TOP, TOP_RIGHT,
@@ -280,8 +295,9 @@ public:
         const Vector4& getColor() const;
 
     private:
+
         Skin(float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color);
-        //Skin(const Skin& copy);
+        
         ~Skin();
 
         static Skin* create(const char* id, float tw, float th, const Rectangle& region, const Theme::Border& border, const Vector4& color);
@@ -290,217 +306,44 @@ public:
     
         std::string _id;
         Theme::Border _border;
-        UVs _uvs[MAX_OVERLAY_REGIONS];
+        UVs _uvs[9];
         Vector4 _color;
         Rectangle _region;
         float _tw, _th;
     };
-    
-    /**
-     * This class represents the appearance of a control.  A style can have padding and margin values,
-     * as well as overlays for each of the control's states.  Each overlay in turn can reference
-     * the above classes to determine the border, background, cursor, and icon settings to use for
-     * a particular state.
-     */
-    class Style
-    {
-        friend class Theme;
-        friend class Control;
-
-    public:
-        class Overlay;
-
-        enum OverlayType
-        {
-            OVERLAY_NORMAL,
-            OVERLAY_FOCUS,
-            OVERLAY_ACTIVE,
-            OVERLAY_DISABLED
-        };
-
-        /**
-         * Returns the Id of this Style.
-         */
-        const char* getId() const;
-
-        /**
-         * Gets an overlay from the overlay type.
-         */
-        Theme::Style::Overlay* getOverlay(OverlayType overlayType) const;
-
-        /**
-         * Gets the Padding region of this style.
-         */
-        const Theme::Padding& getPadding() const;
-
-        /**
-         * Gets the Margin region of this style.
-         */
-        const Theme::Margin& getMargin() const;
-
-        /**
-         * Set this size of this Style's padding.
-         *
-         * Padding is the space between a Control's content (all icons and text) and its border.
-         */
-        void setPadding(float top, float bottom, float left, float right);
-
-        /**
-         * Set the size of this Style's margin.
-         *
-         * The margin is used by Layouts other than AbsoluteLayout to put space between Controls.
-         */
-        void setMargin(float top, float bottom, float left, float right);
-       
-        /**
-         * This class represents a control's overlay for one of the 3 modes: normal, focussed or active.
-         */
-        class Overlay : public Ref, public AnimationTarget
-        {
-            friend class Theme;
-            friend class Style;
-
-        public:
-            /**
-             * Animate this overlay's opacity property.  Data = opacity
-             */
-            static const int ANIMATE_OPACITY = 1;
-
-           /**
-            * Returns the Overlay type.
-            */
-            OverlayType getType();
-
-            float getOpacity() const;
-            void setOpacity(float opacity);
-
-            // setBorder(const Theme::Border& border) ??
-            void setBorder(float top, float bottom, float left, float right);
-            const Border& getBorder() const;
-
-            void setSkinColor(const Vector4& color);
-            const Vector4& getSkinColor() const;
-
-            void setSkinRegion(const Rectangle& region, float tw, float th);
-            const Rectangle& getSkinRegion() const;
-
-            const Theme::UVs& getSkinUVs(Theme::Skin::SkinArea area) const;
-
-           /**
-            * Gets a font associated with this overlay.
-            */
-            Font* getFont() const;
-
-            void setFont(Font* font);
-
-            // Font size.
-            unsigned int getFontSize() const;
-            void setFontSize(unsigned int fontSize);
-
-            // Alignment.
-            Font::Justify getTextAlignment() const;
-            void setTextAlignment(Font::Justify alignment);
-            
-            // Text direction.
-            bool getTextRightToLeft() const;
-            void setTextRightToLeft(bool rightToLeft);
-
-            const Vector4& getTextColor() const;
-            void setTextColor(const Vector4& color); 
-
-            const Rectangle& getImageRegion(const char* id) const;
-            void setImageRegion(const char* id, const Rectangle& region, float tw, float th);
-
-            const Vector4& getImageColor(const char* id) const;
-            void setImageColor(const char* id, const Vector4& color);
-
-            const Theme::UVs& getImageUVs(const char* id) const;
-
-            const Rectangle& getCursorRegion() const;
-            void setCursorRegion(const Rectangle& region, float tw, float th);
-
-            const Vector4& getCursorColor() const;
-            void setCursorColor(const Vector4& color);
-
-            const Theme::UVs& getCursorUVs() const;
-
-            /**
-             * @see AnimationTarget#getAnimationPropertyComponentCount
-             */
-            unsigned int getAnimationPropertyComponentCount(int propertyId) const;
-
-            /**
-             * @see AnimationTarget#getAnimationProperty
-             */
-            void getAnimationPropertyValue(int propertyId, AnimationValue* value);
-
-            /**
-             * @see AnimationTarget#setAnimationProperty
-             */
-            void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
-        
-        private:
-            Overlay();
-            Overlay(const Overlay& copy);
-            ~Overlay();
-
-            static Overlay* create();
-
-            void setSkin(Skin* Skin);
-            Skin* getSkin() const;
-
-            void setCursor(Image* cursor);
-            Image* getCursor() const;
-            
-            void setImageList(ImageList* imageList);
-            ImageList* getImageList() const;
-
-            static const char ANIMATION_OPACITY_BIT = 0x01;
-            void applyAnimationValueOpacity(float opacity, float blendWeight);
-
-            Skin* _skin;
-            Image* _cursor;
-            ImageList* _imageList;
-            Font* _font;
-            unsigned int _fontSize;
-            Font::Justify _alignment;
-            bool _textRightToLeft;
-            Vector4 _textColor;
-            float _opacity;
-        };
-
-    private:
-        Style(const char* id, float tw, float th,
-            const Theme::Margin& margin, const Theme::Padding& padding,
-            Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled);
-        Style(const Style& style);
-        ~Style();
-        
-        std::string _id;
-        Margin _margin;
-        Padding _padding;
-        Overlay* _overlays[MAX_OVERLAYS];
-        float _tw;
-        float _th;
-    };
 
-private:
     /**
      * Constructor.
      */
     Theme();
 
     /**
-     * Copy Constructor.
+     * Constructor.
      */
-    Theme(const Theme* theme);
+    Theme(const Theme& theme);
 
     /**
      * Destructor.
      */
     ~Theme();
 
+    /**
+     * Creates an instance of a Theme from a theme file.
+     *
+     * @param path Path to a theme file.
+     *
+     * @return A new Theme.
+     */
+    static Theme* create(const char* path);
+
+    Theme::Style* getStyle(const char* id) const;
+
+    void setProjectionMatrix(const Matrix& matrix);
+
+    SpriteBatch* getSpriteBatch() const;
+
     static void generateUVs(float tw, float th, float x, float y, float width, float height, UVs* uvs);
+
     void lookUpSprites(const Properties* overlaySpace, ImageList** imageList, Image** mouseCursor, Skin** skin);
 
     std::string _path;

+ 471 - 0
gameplay/src/ThemeStyle.cpp

@@ -0,0 +1,471 @@
+#include "ThemeStyle.h"
+
+namespace gameplay
+{
+
+/****************
+ * Theme::Style *
+ ****************/
+Theme::Style::Style(const char* id, float tw, float th,
+        const Theme::Margin& margin, const Theme::Padding& padding,
+        Theme::Style::Overlay* normal, Theme::Style::Overlay* focus, Theme::Style::Overlay* active, Theme::Style::Overlay* disabled)
+    : _id(id), _tw(tw), _th(th), _margin(margin), _padding(padding)
+{
+    _overlays[OVERLAY_NORMAL] = normal;
+    _overlays[OVERLAY_FOCUS] = focus;
+    _overlays[OVERLAY_ACTIVE] = active;
+    _overlays[OVERLAY_DISABLED] = disabled;
+}
+
+Theme::Style::Style(const Style& copy)
+{
+    _id = copy._id;
+    _margin = copy._margin;
+    _padding = copy._padding;
+    _tw = copy._tw;
+    _th = copy._th;
+
+    for (int i = 0; i < OVERLAY_MAX; i++)
+    {
+        _overlays[i] = new Theme::Style::Overlay(*copy._overlays[i]);
+    }
+}
+
+Theme::Style::~Style()
+{
+    for (unsigned int i = 0; i < OVERLAY_MAX; i++)
+    {
+        SAFE_RELEASE(_overlays[i]);
+    }
+}
+    
+const char* Theme::Style::getId() const
+{
+    return _id.data();
+}
+
+Theme::Style::Overlay* Theme::Style::getOverlay(OverlayType overlayType) const
+{
+    return _overlays[overlayType];
+}
+
+void Theme::Style::setMargin(float top, float bottom, float left, float right)
+{
+    _margin.top = top;
+    _margin.bottom = bottom;
+    _margin.left = left;
+    _margin.right = right;
+}
+
+const Theme::Margin& Theme::Style::getMargin() const
+{
+    return _margin;
+}
+
+void Theme::Style::setPadding(float top, float bottom, float left, float right)
+{
+    _padding.top = top;
+    _padding.bottom = bottom;
+    _padding.left = left;
+    _padding.right = right;
+}
+
+const Theme::Padding& Theme::Style::getPadding() const
+{
+    return _padding;
+}
+    
+/*************************
+    * Theme::Style::Overlay *
+    *************************/
+Theme::Style::Overlay* Theme::Style::Overlay::create()
+{
+    Overlay* overlay = new Overlay();
+    return overlay;
+}
+
+Theme::Style::Overlay::Overlay() : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL)
+{
+}
+
+Theme::Style::Overlay::Overlay(const Overlay& copy) : _skin(NULL), _cursor(NULL), _imageList(NULL), _font(NULL)
+{
+    if (copy._skin)
+    {
+        _skin = new Skin(*copy._skin);
+    }
+    if (copy._cursor)
+    {
+        _cursor = new Image(*copy._cursor);
+    }
+    if (copy._imageList)
+    {
+        _imageList = new ImageList(*copy._imageList);
+    }
+
+    _font = copy._font;
+    _fontSize = copy._fontSize;
+    _alignment = copy._alignment;
+    _textRightToLeft = copy._textRightToLeft;
+    _textColor = Vector4(copy._textColor);
+    _opacity = copy._opacity;
+
+    if (_font)
+    {
+        _font->addRef();
+    }
+}
+
+Theme::Style::Overlay::~Overlay()
+{
+    SAFE_RELEASE(_skin);
+    SAFE_RELEASE(_imageList);
+    SAFE_RELEASE(_cursor);
+    SAFE_RELEASE(_font);
+}
+
+float Theme::Style::Overlay::getOpacity() const
+{
+    return _opacity;
+}
+
+void Theme::Style::Overlay::setOpacity(float opacity)
+{
+    _opacity = opacity;
+}
+
+void Theme::Style::Overlay::setBorder(float top, float bottom, float left, float right)
+{
+    if (_skin)
+    {
+        _skin->_border.top = top;
+        _skin->_border.bottom = bottom;
+        _skin->_border.left = left;
+        _skin->_border.right = right;
+    }
+}
+
+const Theme::Border& Theme::Style::Overlay::getBorder() const
+{
+    if (_skin)
+    {
+        return _skin->getBorder();
+    }
+    else
+    {
+        return Theme::Border::empty();
+    }
+}
+
+void Theme::Style::Overlay::setSkinColor(const Vector4& color)
+{
+    if (_skin)
+    {
+        _skin->_color.set(color);
+    }
+}
+
+const Vector4& Theme::Style::Overlay::getSkinColor() const
+{
+    if (_skin)
+    {
+        return _skin->getColor();
+    }
+
+    return Vector4::one();
+}
+
+void Theme::Style::Overlay::setSkinRegion(const Rectangle& region, float tw, float th)
+{
+    assert(_skin);
+    _skin->setRegion(region, tw, th);
+}
+
+const Rectangle& Theme::Style::Overlay::getSkinRegion() const
+{
+    if (_skin)
+    {
+        return _skin->getRegion();
+    }
+
+    return Rectangle::empty();
+}
+
+const Theme::UVs& Theme::Style::Overlay::getSkinUVs(Theme::Skin::SkinArea area) const
+{
+    if (_skin)
+    {
+        return _skin->_uvs[area];
+    }
+
+    return UVs::empty();
+}
+
+Font* Theme::Style::Overlay::getFont() const
+{
+    return _font;
+}
+
+void Theme::Style::Overlay::setFont(Font* font)
+{
+    if (_font != font)
+    {
+        SAFE_RELEASE(_font);
+
+        _font = font;
+
+        if (_font)
+        {
+            _font->addRef();
+        }
+    }
+}
+
+unsigned int Theme::Style::Overlay::getFontSize() const
+{
+    return _fontSize;
+}
+
+void Theme::Style::Overlay::setFontSize(unsigned int fontSize)
+{
+    _fontSize = fontSize;
+}
+
+Font::Justify Theme::Style::Overlay::getTextAlignment() const
+{
+    return _alignment;
+}
+
+void Theme::Style::Overlay::setTextAlignment(Font::Justify alignment)
+{
+    _alignment = alignment;
+}
+
+bool Theme::Style::Overlay::getTextRightToLeft() const
+{
+    return _textRightToLeft;
+}
+
+void Theme::Style::Overlay::setTextRightToLeft(bool rightToLeft)
+{
+    _textRightToLeft = rightToLeft;
+}
+
+const Vector4& Theme::Style::Overlay::getTextColor() const
+{
+    return _textColor;
+}
+
+void Theme::Style::Overlay::setTextColor(const Vector4& color)
+{
+    _textColor = color;
+}
+
+const Rectangle& Theme::Style::Overlay::getImageRegion(const char* id) const
+{
+    Image* image = _imageList->getImage(id);
+    if (image)
+    {
+        return image->getRegion();
+    }
+    else
+    {
+        return Rectangle::empty();
+    }
+}
+    
+void Theme::Style::Overlay::setImageRegion(const char* id, const Rectangle& region, float tw, float th)
+{
+    Image* image = _imageList->getImage(id);
+    assert(image);
+    image->_region.set(region);
+    generateUVs(tw, th, region.x, region.y, region.width, region.height, &(image->_uvs));
+}
+
+const Vector4& Theme::Style::Overlay::getImageColor(const char* id) const
+{
+    Image* image = _imageList->getImage(id);
+    if (image)
+    {
+        return image->getColor();
+    }
+    else
+    {
+        return Vector4::zero();
+    }
+}
+
+void Theme::Style::Overlay::setImageColor(const char* id, const Vector4& color)
+{
+    Image* image = _imageList->getImage(id);
+    assert(image);
+    image->_color.set(color);
+}
+
+const Theme::UVs& Theme::Style::Overlay::getImageUVs(const char* id) const
+{
+    Image* image = _imageList->getImage(id);
+    if (image)
+    {
+        return image->getUVs();
+    }
+    else
+    {
+        return UVs::empty();
+    }
+}
+
+const Rectangle& Theme::Style::Overlay::getCursorRegion() const
+{
+    if (_cursor)
+    {
+        return _cursor->getRegion();
+    }
+    else
+    {
+        return Rectangle::empty();
+    }
+}
+    
+void Theme::Style::Overlay::setCursorRegion(const Rectangle& region, float tw, float th)
+{
+    assert(_cursor);
+    _cursor->_region.set(region);
+    generateUVs(tw, th, region.x, region.y, region.width, region.height, &(_cursor->_uvs));
+}
+
+const Vector4& Theme::Style::Overlay::getCursorColor() const
+{
+    if (_cursor)
+    {
+        return _cursor->getColor();
+    }
+    else
+    {
+        return Vector4::zero();
+    }
+}
+
+void Theme::Style::Overlay::setCursorColor(const Vector4& color)
+{
+    assert(_cursor);
+    _cursor->_color.set(color);
+}
+
+const Theme::UVs& Theme::Style::Overlay::getCursorUVs() const
+{
+    if (_cursor)
+    {
+        return _cursor->getUVs();
+    }
+    else
+    {
+        return UVs::empty();
+    }
+}
+
+void Theme::Style::Overlay::setSkin(Skin* skin)
+{
+    if (_skin != skin)
+    {
+        SAFE_RELEASE(_skin);
+        _skin = skin;
+
+        if (skin)
+        {
+            skin->addRef();
+        }
+    }
+}
+
+Theme::Skin* Theme::Style::Overlay::getSkin() const
+{
+    return _skin;
+}
+
+void Theme::Style::Overlay::setCursor(Image* cursor)
+{
+    if (_cursor != cursor)
+    {
+        SAFE_RELEASE(_cursor);
+        _cursor = cursor;
+
+        if (cursor)
+        {
+            cursor->addRef();
+        }
+    }
+}
+
+Theme::Image* Theme::Style::Overlay::getCursor() const
+{
+    return _cursor;
+}
+            
+void Theme::Style::Overlay::setImageList(ImageList* imageList)
+{
+    if (_imageList != imageList)
+    {
+        SAFE_RELEASE(_imageList);
+        _imageList = imageList;
+
+        if (imageList)
+        {
+            imageList->addRef();
+        }
+    }
+}
+    
+Theme::ImageList* Theme::Style::Overlay::getImageList() const
+{
+    return _imageList;
+}
+
+// Implementation of AnimationHandler
+unsigned int Theme::Style::Overlay::getAnimationPropertyComponentCount(int propertyId) const
+{
+    switch(propertyId)
+    {
+    case Theme::Style::Overlay::ANIMATE_OPACITY:
+        return 1;
+    default:
+        return -1;
+    }
+}
+
+void Theme::Style::Overlay::getAnimationPropertyValue(int propertyId, AnimationValue* value)
+{
+    switch(propertyId)
+    {
+    case ANIMATE_OPACITY:
+        value->setFloat(0, _opacity);
+        break;
+    default:
+        break;
+    }
+}
+
+void Theme::Style::Overlay::setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight)
+{
+    switch(propertyId)
+    {
+        case ANIMATE_OPACITY:
+        {
+            float opacity = value->getFloat(0);
+            if ((_animationPropertyBitFlag & ANIMATION_OPACITY_BIT) != ANIMATION_OPACITY_BIT)
+            {
+                _animationPropertyBitFlag |= ANIMATION_OPACITY_BIT;
+            }
+            else
+            {
+                opacity = Curve::lerp(blendWeight, _opacity, opacity);
+            }
+            _opacity = opacity;
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+}

+ 223 - 0
gameplay/src/ThemeStyle.h

@@ -0,0 +1,223 @@
+#ifndef THEMESTYLE_H_
+#define THEMESTYLE_H_
+
+#include "Base.h"
+#include "Ref.h"
+#include "Font.h"
+#include "Rectangle.h"
+#include "Texture.h"
+#include "Properties.h"
+#include "Theme.h"
+
+namespace gameplay
+{
+
+/**
+ * This class represents the appearance of a control.  A style can have padding and margin values,
+ * as well as overlays for each of the control's states.  Each overlay in turn can reference
+ * other theme classes to determine the border, background, cursor, and image settings to use for
+ * a particular state, as well as color and font settings, etcetera.
+ */
+class Theme::Style
+{
+    friend class Theme;
+    friend class Control;
+
+private:
+
+    /**
+     * A style has one overlay for each possible control state.
+     */
+    enum OverlayType
+    {
+        OVERLAY_NORMAL,
+        OVERLAY_FOCUS,
+        OVERLAY_ACTIVE,
+        OVERLAY_DISABLED,
+        OVERLAY_MAX
+    };
+
+    /**
+     * This class represents a control's overlay for one of its states.
+     */
+    class Overlay : public Ref, public AnimationTarget
+    {
+        friend class Theme;
+        friend class Theme::Style;
+        friend class Control;
+
+    private:
+
+        static const int ANIMATE_OPACITY = 1;
+        static const char ANIMATION_OPACITY_BIT = 0x01;
+
+        Overlay();
+            
+        Overlay(const Overlay& copy);
+            
+        ~Overlay();
+
+        static Overlay* create();
+
+        OverlayType getType();
+
+        float getOpacity() const;
+
+        void setOpacity(float opacity);
+
+        void setBorder(float top, float bottom, float left, float right);
+
+        const Theme::Border& getBorder() const;
+
+        void setSkinColor(const Vector4& color);
+
+        const Vector4& getSkinColor() const;
+
+        void setSkinRegion(const Rectangle& region, float tw, float th);
+
+        const Rectangle& getSkinRegion() const;
+
+        const Theme::UVs& getSkinUVs(Theme::Skin::SkinArea area) const;
+
+        Font* getFont() const;
+
+        void setFont(Font* font);
+
+        unsigned int getFontSize() const;
+
+        void setFontSize(unsigned int fontSize);
+
+        Font::Justify getTextAlignment() const;
+
+        void setTextAlignment(Font::Justify alignment);
+            
+        bool getTextRightToLeft() const;
+
+        void setTextRightToLeft(bool rightToLeft);
+
+        const Vector4& getTextColor() const;
+
+        void setTextColor(const Vector4& color); 
+
+        const Rectangle& getImageRegion(const char* id) const;
+
+        void setImageRegion(const char* id, const Rectangle& region, float tw, float th);
+
+        const Vector4& getImageColor(const char* id) const;
+
+        void setImageColor(const char* id, const Vector4& color);
+
+        const Theme::UVs& getImageUVs(const char* id) const;
+
+        const Rectangle& getCursorRegion() const;
+
+        void setCursorRegion(const Rectangle& region, float tw, float th);
+
+        const Vector4& getCursorColor() const;
+
+        void setCursorColor(const Vector4& color);
+
+        const Theme::UVs& getCursorUVs() const;
+
+        /**
+         * @see AnimationTarget#getAnimationPropertyComponentCount
+         */
+        unsigned int getAnimationPropertyComponentCount(int propertyId) const;
+
+        /**
+         * @see AnimationTarget#getAnimationProperty
+         */
+        void getAnimationPropertyValue(int propertyId, AnimationValue* value);
+
+        /**
+         * @see AnimationTarget#setAnimationProperty
+         */
+        void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f);
+       
+        void setSkin(Theme::Skin* Skin);
+
+        Theme::Skin* getSkin() const;
+
+        void setCursor(Theme::Image* cursor);
+            
+        Theme::Image* getCursor() const;
+            
+        void setImageList(Theme::ImageList* imageList);
+            
+        Theme::ImageList* getImageList() const;
+
+        void applyAnimationValueOpacity(float opacity, float blendWeight);
+
+        Skin* _skin;
+        Theme::Image* _cursor;
+        Theme::ImageList* _imageList;
+        Font* _font;
+        unsigned int _fontSize;
+        Font::Justify _alignment;
+        bool _textRightToLeft;
+        Vector4 _textColor;
+        float _opacity;
+    };
+
+    /**
+     * Constructor.
+     */
+    Style(const char* id, float tw, float th,
+          const Theme::Margin& margin, const Theme::Padding& padding,
+          Overlay* normal, Overlay* focus, Overlay* active, Overlay* disabled);
+
+    /**
+     * Constructor.
+     */
+    Style(const Style& style);
+
+    /**
+     * Destructor.
+     */
+    ~Style();
+
+    /**
+     * Returns the Id of this Style.
+     */
+    const char* getId() const;
+
+    /**
+     * Gets an overlay from the overlay type.
+     */
+    Overlay* getOverlay(OverlayType overlayType) const;
+
+    /**
+     * Gets the Padding region of this style.
+     */
+    const Theme::Padding& getPadding() const;
+
+    /**
+     * Gets the Margin region of this style.
+     */
+    const Theme::Margin& getMargin() const;
+
+    /**
+     * Set this size of this Style's padding.
+     *
+     * Padding is the space between a Control's content (all icons and text) and its border.
+     */
+    void setPadding(float top, float bottom, float left, float right);
+
+    /**
+     * Set the size of this Style's margin.
+     *
+     * The margin is used by Layouts other than AbsoluteLayout to put space between Controls.
+     */
+    void setMargin(float top, float bottom, float left, float right);
+        
+    std::string _id;
+    float _tw;
+    float _th;
+    Theme::Margin _margin;
+    Theme::Padding _padding;
+    Overlay* _overlays[OVERLAY_MAX];
+};
+
+}
+
+#endif

+ 6 - 12
gameplay/src/VerticalLayout.cpp

@@ -44,16 +44,10 @@ namespace gameplay
     void VerticalLayout::update(const Container* container)
     {
         // Need border, padding.
-        Theme::Style* style = container->getStyle();
+        //Theme::Style* style = container->getStyle();
         Theme::Border border = container->getBorder(container->getState());
-        /*
-        Theme::Skin* skin = style->getOverlay(container->getOverlayType())->getSkin();
-        if (skin)
-        {
-            border = skin->getBorder();
-        }
-        */
-        Theme::Padding padding = style->getPadding();
+        //Theme::Padding padding = style->getPadding();
+        Theme::Padding padding = container->getPadding();
 
         float yPosition = 0;
 
@@ -77,12 +71,12 @@ namespace gameplay
         {
             Control* control = controls.at(i);
 
-            const Rectangle& bounds = control->getBounds();
-            const Theme::Margin& margin = control->getStyle()->getMargin();
+            const Rectangle& bounds = control->getClipBounds();
+            const Theme::Margin& margin = control->getMargin();
 
             yPosition += margin.top;
 
-            control->setPosition(0, yPosition, 0L);
+            control->setPosition(0, yPosition);
             if (control->isDirty())
             {
                 control->update(container->getClip());

+ 23 - 0
gameplay/src/VerticalLayout.h

@@ -17,6 +17,7 @@ class VerticalLayout : public Layout
     friend class Container;
 
 public:
+
     /**
      * Set whether this layout will start laying out controls from the bottom of the container.
      * This setting defaults to 'false', meaning controls will start at the top.
@@ -40,16 +41,38 @@ public:
     Layout::Type getType();
 
 protected:
+
+    /**
+     * Constructor.
+     */
     VerticalLayout();
+
+    /**
+     * Destructor.
+     */
     virtual ~VerticalLayout();
 
+    /**
+     * Create a VerticalLayout.
+     *
+     * @return a VerticalLayout object.
+     */
     static VerticalLayout* create();
 
+    /**
+     * Update the controls contained by the specified container.
+     *
+     * Controls are placed next to one another vertically until
+     * the bottom-most edge of the container is reached.
+     *
+     * @param container The container to update.
+     */
     void update(const Container* container);
 
     bool _bottomToTop;
 
 private:
+
     VerticalLayout(const VerticalLayout& copy);
 };