Parcourir la source

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

Conflicts:
	gameplay/gameplay.vcxproj.filters
	gameplay/src/Node.cpp
Steve Grenier il y a 13 ans
Parent
commit
cb927949f6
76 fichiers modifiés avec 1162 ajouts et 167 suppressions
  1. 15 12
      .gitignore
  2. 5 4
      README.md
  3. 14 4
      gameplay-encoder/gameplay-encoder.xcodeproj/project.pbxproj
  4. 6 0
      gameplay-encoder/src/FBXSceneEncoder.cpp
  5. 9 9
      gameplay-template/gameplay-template.xcodeproj/project.pbxproj
  6. 3 1
      gameplay-template/template.bar-descriptor.xml
  7. 6 0
      gameplay/gameplay.vcxproj
  8. 18 0
      gameplay/gameplay.vcxproj.filters
  9. 14 14
      gameplay/gameplay.xcodeproj/project.pbxproj
  10. BIN
      gameplay/res/logo_black.png
  11. BIN
      gameplay/res/logo_powered_black.png
  12. BIN
      gameplay/res/logo_powered_white.png
  13. BIN
      gameplay/res/logo_white.png
  14. BIN
      gameplay/res/textures/particle-default.png
  15. 50 2
      gameplay/src/Animation.cpp
  16. 44 9
      gameplay/src/Animation.h
  17. 2 2
      gameplay/src/AnimationClip.cpp
  18. 1 1
      gameplay/src/AnimationController.cpp
  19. 28 0
      gameplay/src/AnimationTarget.cpp
  20. 21 1
      gameplay/src/AnimationTarget.h
  21. 12 12
      gameplay/src/AudioBuffer.cpp
  22. 1 1
      gameplay/src/AudioController.cpp
  23. 1 1
      gameplay/src/AudioController.h
  24. 13 2
      gameplay/src/AudioListener.cpp
  25. 8 0
      gameplay/src/AudioListener.h
  26. 43 8
      gameplay/src/AudioSource.cpp
  27. 15 6
      gameplay/src/AudioSource.h
  28. 23 5
      gameplay/src/Base.h
  29. 20 0
      gameplay/src/Camera.cpp
  30. 9 0
      gameplay/src/Camera.h
  31. 38 0
      gameplay/src/CloneContext.cpp
  32. 86 0
      gameplay/src/CloneContext.h
  33. 1 1
      gameplay/src/Container.cpp
  34. 1 1
      gameplay/src/FileSystem.cpp
  35. 8 4
      gameplay/src/Font.h
  36. 1 1
      gameplay/src/Form.cpp
  37. 34 0
      gameplay/src/Game.h
  38. 14 0
      gameplay/src/Game.inl
  39. 10 0
      gameplay/src/Joint.cpp
  40. 24 0
      gameplay/src/Joint.h
  41. 26 0
      gameplay/src/Light.cpp
  42. 15 5
      gameplay/src/Light.h
  43. 18 0
      gameplay/src/Material.cpp
  44. 9 0
      gameplay/src/Material.h
  45. 80 2
      gameplay/src/MaterialParameter.cpp
  46. 11 2
      gameplay/src/MaterialParameter.h
  47. 1 1
      gameplay/src/MeshBatch.h
  48. 60 1
      gameplay/src/MeshSkin.cpp
  49. 29 0
      gameplay/src/MeshSkin.h
  50. 18 4
      gameplay/src/Model.cpp
  51. 11 2
      gameplay/src/Model.h
  52. 67 7
      gameplay/src/Node.cpp
  53. 55 10
      gameplay/src/Node.h
  54. 3 4
      gameplay/src/ParticleEmitter.cpp
  55. 3 3
      gameplay/src/ParticleEmitter.h
  56. 9 4
      gameplay/src/Pass.cpp
  57. 15 0
      gameplay/src/Pass.h
  58. 7 0
      gameplay/src/Platform.h
  59. 5 0
      gameplay/src/PlatformAndroid.cpp
  60. 7 0
      gameplay/src/PlatformMacOS.mm
  61. 6 0
      gameplay/src/PlatformQNX.cpp
  62. 5 0
      gameplay/src/PlatformWin32.cpp
  63. 7 0
      gameplay/src/PlatformiOS.mm
  64. 26 4
      gameplay/src/RenderState.cpp
  65. 23 5
      gameplay/src/RenderState.h
  66. 1 0
      gameplay/src/SpriteBatch.h
  67. 14 4
      gameplay/src/Technique.cpp
  68. 8 1
      gameplay/src/Technique.h
  69. 8 0
      gameplay/src/Transform.cpp
  70. 2 1
      gameplay/src/Transform.h
  71. 2 1
      gameplay/src/Vector2.cpp
  72. 3 1
      gameplay/src/Vector2.h
  73. 2 1
      gameplay/src/Vector3.cpp
  74. 3 1
      gameplay/src/Vector3.h
  75. 2 1
      gameplay/src/Vector4.cpp
  76. 3 1
      gameplay/src/Vector4.h

+ 15 - 12
.gitignore

@@ -43,7 +43,8 @@ Thumbs.db
 /gameplay-samples/sample00-mesh/Device-Coverage
 /gameplay-samples/sample00-mesh/Device-Profile
 /gameplay-samples/sample00-mesh/Device-Release
-/gameplay-samples/sample00-mesh/res/shaders
+/gameplay-samples/sample00-mesh/res/shaders
+/gameplay-samples/sample00-mesh/res/logo_powered_white.png
 /gameplay-samples/sample00-mesh/sample00-mesh.xcodeproj/xcuserdata
 /gameplay-samples/sample01-longboard/Debug
 /gameplay-samples/sample01-longboard/DebugMem
@@ -55,7 +56,8 @@ Thumbs.db
 /gameplay-samples/sample01-longboard/Device-Coverage
 /gameplay-samples/sample01-longboard/Device-Profile
 /gameplay-samples/sample01-longboard/Device-Release
-/gameplay-samples/sample01-longboard/res/shaders
+/gameplay-samples/sample01-longboard/res/shaders
+/gameplay-samples/sample01-longboard/res/logo_powered_white.png
 /gameplay-samples/sample01-longboard/sample01-longboard.xcodeproj/xcuserdata
 /gameplay-samples/sample02-spaceship/Debug
 /gameplay-samples/sample02-spaceship/DebugMem
@@ -67,7 +69,8 @@ Thumbs.db
 /gameplay-samples/sample02-spaceship/Device-Coverage
 /gameplay-samples/sample02-spaceship/Device-Profile
 /gameplay-samples/sample02-spaceship/Device-Release
-/gameplay-samples/sample02-spaceship/res/shaders
+/gameplay-samples/sample02-spaceship/res/shaders
+/gameplay-samples/sample02-spaceship/res/logo_powered_white.png
 /gameplay-samples/sample02-spaceship/sample02-spaceship.xcodeproj/xcuserdata
 /gameplay-samples/sample03-character/Debug
 /gameplay-samples/sample03-character/DebugMem
@@ -79,11 +82,17 @@ Thumbs.db
 /gameplay-samples/sample03-character/Device-Coverage
 /gameplay-samples/sample03-character/Device-Profile
 /gameplay-samples/sample03-character/Device-Release
-/gameplay-samples/sample03-character/res/shaders
+/gameplay-samples/sample03-character/res/shaders
+/gameplay-samples/sample03-character/res/logo_powered_white.png
 /gameplay-samples/sample03-character/sample03-character.xcodeproj/xcuserdata
 
 /gameplay-android/obj
-/gameplay-android/NUL
+/gameplay-android/NUL
+/gameplay/android/NUL
+/gameplay/android/proguard.cfg
+/gameplay/android/local.properties
+/gameplay/android/project.properties
+/gameplay/android/obj
 /gameplay-samples/sample00-mesh/android/src
 /gameplay-samples/sample00-mesh/android/assets
 /gameplay-samples/sample00-mesh/android/bin
@@ -128,11 +137,5 @@ Thumbs.db
 /gameplay-samples/sample03-character/android/libs
 /gameplay-samples/sample03-character/android/obj
 /gameplay-samples/sample03-character/android/NUL
-/gameplay-samples/sample01-longboard/NUL
-
-/gameplay/android/NUL
-/gameplay/android/proguard.cfg
-/gameplay/android/local.properties
-/gameplay/android/project.properties
-/gameplay/android/obj
+/gameplay-samples/sample01-longboard/NUL
 

+ 5 - 4
README.md

@@ -7,13 +7,14 @@ GamePlay is a open-source, cross-platform 3D native gaming framework making it e
 - Apple iOS 5 (using Apple XCode 4.3)
 
 ## Supported Desktop Platforms
-- Microsoft Windows XP/7 (using Microsoft Visual Studio 2010)
+- Microsoft Windows 7 (using Microsoft Visual Studio 2010)
 - Apple MacOS X (using Apple XCode 4.3)
 
-## Roadmap for 'next' branch
+## Roadmap for 'next' branch
+- Lua script bindings
 - Terrain and Water
-- Improvements to Lighting
-- More Samples and Tutorials
+- Lighting enhancements
+- Editor
 
 ## Licence
 The project is open sourced under the Apache 2.0 license.

+ 14 - 4
gameplay-encoder/gameplay-encoder.xcodeproj/project.pbxproj

@@ -57,6 +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 */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -168,6 +169,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>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -175,6 +177,7 @@
 			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 */,
@@ -310,6 +313,7 @@
 				42C8EE361472D7E700E43619 /* libxml2.dylib */,
 				42C8EE341472B60100E43619 /* libfreetype.a */,
 				42475D7B14720ECE00610A6A /* libdom.a */,
+				42DDE88415191CDA00D9B550 /* libpng.a */,
 			);
 			name = Libraries;
 			sourceTree = "<group>";
@@ -448,6 +452,7 @@
 					"../external-deps/freetype/include",
 					"../external-deps/collada-dom/include",
 					"../external-deps/collada-dom/include/1.4",
+					"../external-deps/libpng/include",
 				);
 				INFOPLIST_PREPROCESSOR_DEFINITIONS = "";
 				MACOSX_DEPLOYMENT_TARGET = 10.7;
@@ -455,7 +460,7 @@
 				OTHER_TEST_FLAGS = "";
 				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
 				SDKROOT = macosx;
-				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4";
+				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4 ./external-deps/libpng/include";
 				WARNING_CFLAGS = "";
 			};
 			name = Debug;
@@ -483,13 +488,14 @@
 					"../external-deps/freetype/include",
 					"../external-deps/collada-dom/include",
 					"../external-deps/collada-dom/include/1.4",
+					"../external-deps/libpng/include",
 				);
 				INFOPLIST_PREPROCESSOR_DEFINITIONS = "";
 				MACOSX_DEPLOYMENT_TARGET = 10.7;
 				OTHER_TEST_FLAGS = "";
 				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
 				SDKROOT = macosx;
-				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4";
+				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4 ./external-deps/libpng/include";
 				WARNING_CFLAGS = "";
 			};
 			name = Release;
@@ -510,6 +516,7 @@
 					"../external-deps/freetype2/include",
 					"../external-deps/collada-dom/include",
 					"../external-deps/collada-dom/include/1.4",
+					"../external-deps/libpng/include",
 				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
@@ -518,10 +525,11 @@
 					"\"$(SRCROOT)/../external-deps/pcre/lib/macos\"",
 					"\"$(SRCROOT)/../../../Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug\"",
 					"\"$(SYSTEM_APPS_DIR)/Autodesk/FBXSDK20122/lib/gcc4/ub\"",
+					"\"$(SRCROOT)/../external-deps/libpng/lib/macos\"",
 				);
 				MACH_O_TYPE = mh_execute;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype2/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4 /Applications/Autodesk/FBXSDK20122/include";
+				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype2/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4 ../external-deps/libpng/include";
 			};
 			name = Debug;
 		};
@@ -541,6 +549,7 @@
 					"../external-deps/freetype2/include",
 					"../external-deps/collada-dom/include",
 					"../external-deps/collada-dom/include/1.4",
+					"../external-deps/libpng/include",
 				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
@@ -549,10 +558,11 @@
 					"\"$(SRCROOT)/../external-deps/pcre/lib/macos\"",
 					"\"$(SRCROOT)/../../../Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug\"",
 					"\"$(SYSTEM_APPS_DIR)/Autodesk/FBXSDK20122/lib/gcc4/ub\"",
+					"\"$(SRCROOT)/../external-deps/libpng/lib/macos\"",
 				);
 				MACH_O_TYPE = mh_execute;
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype2/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4 /Applications/Autodesk/FBXSDK20122/include";
+				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype2/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4 ../external-deps/libpng/include";
 			};
 			name = Release;
 		};

+ 6 - 0
gameplay-encoder/src/FBXSceneEncoder.cpp

@@ -250,6 +250,12 @@ void FBXSceneEncoder::loadScene(KFbxScene* fbxScene)
     KFbxColor ambientColor = fbxScene->GetGlobalSettings().GetAmbientColor();
     scene->setAmbientColor((float)ambientColor.mRed, (float)ambientColor.mGreen, (float)ambientColor.mBlue);
 
+	// Assign the first camera node (if there is one) in the scene as the active camera
+	// This ensures that if there's a camera in the scene that it is assigned as the 
+	// active camera.
+	// TODO: add logic to find the "active" camera node in the fbxScene
+	scene->setActiveCameraNode(scene->getFirstCameraNode());
+
     _gamePlayFile.addScene(scene);
 }
 

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

@@ -93,12 +93,12 @@
 		5B61611214CCC2200073B857 /* TEMPLATE_PROJECT-macos.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "TEMPLATE_PROJECT-macos.plist"; sourceTree = "<group>"; };
 		5B61612C14CCC24C0073B857 /* TEMPLATE_PROJECT-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TEMPLATE_PROJECT-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		5B61612E14CCC24D0073B857 /* TEMPLATE_PROJECT-ios.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TEMPLATE_PROJECT-ios.plist"; sourceTree = "<group>"; };
-		5B8D58A51512584A00DA5991 /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/CoreMotion.framework; sourceTree = DEVELOPER_DIR; };
-		5B8D58A61512584A00DA5991 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
-		5B8D58A71512584A00DA5991 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/OpenAL.framework; sourceTree = DEVELOPER_DIR; };
-		5B8D58A81512584A00DA5991 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; };
-		5B8D58A91512584A00DA5991 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
-		5B8D58AA1512584A00DA5991 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
+		5B8D58A51512584A00DA5991 /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/CoreMotion.framework; sourceTree = DEVELOPER_DIR; };
+		5B8D58A61512584A00DA5991 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
+		5B8D58A71512584A00DA5991 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/OpenAL.framework; sourceTree = DEVELOPER_DIR; };
+		5B8D58A81512584A00DA5991 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; };
+		5B8D58A91512584A00DA5991 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
+		5B8D58AA1512584A00DA5991 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
 		5BC4E77F150F879E00CBE1C0 /* gameplay.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = gameplay.xcodeproj; path = GAMEPLAY_PATH/gameplay/gameplay.xcodeproj; sourceTree = SOURCE_ROOT; };
 		5BC4E849150F911D00CBE1C0 /* shaders */ = {isa = PBXFileReference; lastKnownFileType = text; name = shaders; path = GAMEPLAY_PATH/gameplay/res/shaders; sourceTree = SOURCE_ROOT; };
 		5BC4E84A150F911D00CBE1C0 /* textures */ = {isa = PBXFileReference; lastKnownFileType = text; name = textures; path = GAMEPLAY_PATH/gameplay/res/textures; sourceTree = SOURCE_ROOT; };
@@ -175,7 +175,7 @@
 			isa = PBXGroup;
 			children = (
 				5BC4E825150F8CE600CBE1C0 /* GamePlay */,
-				5B61613A14CCC3590073B857 /* Mac OS X */,
+				5B61613A14CCC3590073B857 /* MacOSX */,
 				5B61613914CCC3560073B857 /* iOS */,
 			);
 			name = Frameworks;
@@ -218,7 +218,7 @@
 			name = iOS;
 			sourceTree = "<group>";
 		};
-		5B61613A14CCC3590073B857 /* Mac OS X */ = {
+		5B61613A14CCC3590073B857 /* MacOSX */ = {
 			isa = PBXGroup;
 			children = (
 				42C932C01491A0DB0098216A /* Cocoa.framework */,
@@ -226,7 +226,7 @@
 				42C933161491A5EB0098216A /* OpenGL.framework */,
 				42C9331E1491A67F0098216A /* OpenAL.framework */,
 			);
-			name = "Mac OS X";
+			name = "MacOSX";
 			sourceTree = "<group>";
 		};
 		5BC4E825150F8CE600CBE1C0 /* GamePlay */ = {

+ 3 - 1
gameplay-template/template.bar-descriptor.xml

@@ -38,7 +38,9 @@
     <author>TEMPLATE_AUTHOR</author>
 
     <!--  Unique author ID assigned by signing authority. Required if using debug tokens. -->
-    <!-- <authorId>ABC1234YjsnUk235h</authorId> -->
+    <!-- <authorId>gYAAgPkLP1tZlyYP1wiMaRFFNMw</authorId> -->
+    
+    <platformVersion>2.0.0.7971</platformVersion>
 
     <initialWindow>
         <aspectRatio>landscape</aspectRatio>

+ 6 - 0
gameplay/gameplay.vcxproj

@@ -30,6 +30,7 @@
     <ClCompile Include="src\Button.cpp" />
     <ClCompile Include="src\Camera.cpp" />
     <ClCompile Include="src\CheckBox.cpp" />
+    <ClCompile Include="src\CloneContext.cpp" />
     <ClCompile Include="src\Container.cpp" />
     <ClCompile Include="src\Control.cpp" />
     <ClCompile Include="src\Curve.cpp" />
@@ -120,6 +121,7 @@
     <ClInclude Include="src\Button.h" />
     <ClInclude Include="src\Camera.h" />
     <ClInclude Include="src\CheckBox.h" />
+    <ClInclude Include="src\CloneContext.h" />
     <ClInclude Include="src\Container.h" />
     <ClInclude Include="src\Control.h" />
     <ClInclude Include="src\Curve.h" />
@@ -195,6 +197,10 @@
     <ClInclude Include="src\Viewport.h" />
   </ItemGroup>
   <ItemGroup>
+    <None Include="res\logo_black.png" />
+    <None Include="res\logo_powered_black.png" />
+    <None Include="res\logo_powered_white.png" />
+    <None Include="res\logo_white.png" />
     <None Include="res\shaders\bumped-specular.fsh" />
     <None Include="res\shaders\bumped-specular.vsh" />
     <None Include="res\shaders\bumped.fsh" />

+ 18 - 0
gameplay/gameplay.vcxproj.filters

@@ -276,6 +276,9 @@
     <ClCompile Include="src\PhysicsCollisionShape.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\CloneContext.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -545,6 +548,9 @@
     <ClInclude Include="src\PhysicsCollisionShape.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\CloneContext.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
@@ -634,6 +640,18 @@
     <None Include="src\PlatformiOS.mm">
       <Filter>src</Filter>
     </None>
+    <None Include="res\logo_black.png">
+      <Filter>res</Filter>
+    </None>
+    <None Include="res\logo_powered_black.png">
+      <Filter>res</Filter>
+    </None>
+    <None Include="res\logo_powered_white.png">
+      <Filter>res</Filter>
+    </None>
+    <None Include="res\logo_white.png">
+      <Filter>res</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\PhysicsFixedConstraint.inl">

+ 14 - 14
gameplay/gameplay.xcodeproj/project.pbxproj

@@ -546,18 +546,18 @@
 		5B04C5CA14BFCFE100EB0071 /* libgameplay.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgameplay.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		5B04C5CB14BFD48500EB0071 /* gameplay-main-ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "gameplay-main-ios.mm"; path = "src/gameplay-main-ios.mm"; sourceTree = SOURCE_ROOT; };
 		5B04C5CC14BFD48500EB0071 /* PlatformiOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = PlatformiOS.mm; path = src/PlatformiOS.mm; sourceTree = SOURCE_ROOT; };
-		5B2BC7561512507500D176CD /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/CoreMotion.framework; sourceTree = DEVELOPER_DIR; };
+		5B2BC7561512507500D176CD /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/CoreMotion.framework; sourceTree = DEVELOPER_DIR; };
 		5B2BC75D1512514500D176CD /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; };
 		5B2BC75E1512514500D176CD /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
 		5B2BC7611512514D00D176CD /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		5B2BC7631512516B00D176CD /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = "<absolute>"; };
-		5B2BC766151251EB00D176CD /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
-		5B2BC767151251EB00D176CD /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
-		5B2BC768151251EB00D176CD /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
-		5B2BC769151251EB00D176CD /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/OpenAL.framework; sourceTree = DEVELOPER_DIR; };
-		5B2BC76A151251EB00D176CD /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; };
-		5B2BC76B151251EB00D176CD /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
-		5B2BC76C151251EB00D176CD /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
+		5B2BC766151251EB00D176CD /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
+		5B2BC767151251EB00D176CD /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
+		5B2BC768151251EB00D176CD /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
+		5B2BC769151251EB00D176CD /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/OpenAL.framework; sourceTree = DEVELOPER_DIR; };
+		5B2BC76A151251EB00D176CD /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/OpenGLES.framework; sourceTree = DEVELOPER_DIR; };
+		5B2BC76B151251EB00D176CD /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
+		5B2BC76C151251EB00D176CD /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
 		5B5ADCE214C22DF900AC6109 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = SDKs/MacOSX10.7.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
 		5B5ADCE414C22E1F00AC6109 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
 		5B5DB92D14C25B7B007755DB /* libbullet.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libbullet.a; path = "../external-deps/bullet/lib/ios/i386/libbullet.a"; sourceTree = "<group>"; };
@@ -884,7 +884,7 @@
 				42CD0DA9147D8EA80000361E /* libvorbisenc.a */,
 				42CD0DAA147D8EA80000361E /* libvorbisfile.a */,
 				42CCD555146EC1EB00353661 /* libpng.a */,
-				5B5ADCE114C22DC700AC6109 /* Mac OS X */,
+				5B5ADCE114C22DC700AC6109 /* MacOSX */,
 				5B5ADCE014C22DBE00AC6109 /* iOS */,
 			);
 			name = Libraries;
@@ -893,8 +893,8 @@
 		42CCD4AF146D811D00353661 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
-				5B04C5FE14BFE52F00EB0071 /* Mac OS X */,
 				5B04C5FD14BFE52300EB0071 /* iOS */,
+				5B04C5FE14BFE52F00EB0071 /* MacOSX */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -914,7 +914,7 @@
 			name = iOS;
 			sourceTree = "<group>";
 		};
-		5B04C5FE14BFE52F00EB0071 /* Mac OS X */ = {
+		5B04C5FE14BFE52F00EB0071 /* MacOSX */ = {
 			isa = PBXGroup;
 			children = (
 				5B2BC7631512516B00D176CD /* libz.dylib */,
@@ -923,7 +923,7 @@
 				5B2BC75E1512514500D176CD /* OpenGL.framework */,
 				4234D99D14686C52003031B3 /* Cocoa.framework */,
 			);
-			name = "Mac OS X";
+			name = MacOSX;
 			sourceTree = "<group>";
 		};
 		5B5ADCE014C22DBE00AC6109 /* iOS */ = {
@@ -940,12 +940,12 @@
 			name = iOS;
 			sourceTree = "<group>";
 		};
-		5B5ADCE114C22DC700AC6109 /* Mac OS X */ = {
+		5B5ADCE114C22DC700AC6109 /* MacOSX */ = {
 			isa = PBXGroup;
 			children = (
 				5B5ADCE214C22DF900AC6109 /* libz.dylib */,
 			);
-			name = "Mac OS X";
+			name = MacOSX;
 			sourceTree = "<group>";
 		};
 /* End PBXGroup section */

BIN
gameplay/res/logo_black.png


BIN
gameplay/res/logo_powered_black.png


BIN
gameplay/res/logo_powered_white.png


BIN
gameplay/res/logo_white.png


BIN
gameplay/res/textures/particle-default.png


+ 50 - 2
gameplay/src/Animation.cpp

@@ -27,6 +27,11 @@ Animation::Animation(const char* id, AnimationTarget* target, int propertyId, un
     createChannel(target, propertyId, keyCount, keyTimes, keyValues, keyInValue, keyOutValue, type);
 }
 
+Animation::Animation(const char* id)
+    : _controller(Game::getInstance()->getAnimationController()), _id(id), _duration(0), _defaultClip(NULL), _clips(NULL)
+{
+}
+
 Animation::~Animation()
 {
     if (_defaultClip)
@@ -54,8 +59,9 @@ Animation::~Animation()
 }
 
 Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration)
-    : _animation(animation), _target(target), _propertyId(propertyId), _curve(curve), _duration(duration)
+    : _animation(animation), _target(target), _propertyId(propertyId), _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));
 
@@ -64,12 +70,47 @@ Animation::Channel::Channel(Animation* animation, AnimationTarget* target, int p
     _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();
+    _target->addChannel(this);
+}
+
 Animation::Channel::~Channel()
 {
-    SAFE_DELETE(_curve);
+    SAFE_RELEASE(_curveRef);
     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();
@@ -372,4 +413,11 @@ void Animation::setTransformRotationOffset(Curve* curve, unsigned int propertyId
     return;
 }
 
+Animation* Animation::clone()
+{
+    Animation* animation = new Animation(getId());
+    _controller->addAnimation(animation);
+    return animation;
+}
+
 }

+ 44 - 9
gameplay/src/Animation.h

@@ -3,6 +3,7 @@
 
 #include "Ref.h"
 #include "Properties.h"
+#include "Curve.h"
 
 namespace gameplay
 {
@@ -10,7 +11,6 @@ namespace gameplay
 class AnimationTarget;
 class AnimationController;
 class AnimationClip;
-class Curve;
 
 /**
  * Defines a generic property animation.
@@ -96,6 +96,26 @@ public:
 
 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.
      *
@@ -112,23 +132,21 @@ private:
     private:
 
         Channel(Animation* animation, AnimationTarget* target, int propertyId, Curve* curve, unsigned long duration);
-        Channel(const Channel& copy);
+        Channel(const Channel& copy, Animation* animation, AnimationTarget* target);
+        Channel(const Channel&); // Hidden copy constructor.
         ~Channel();
+        Channel& operator=(const Channel&); // Hidden copy assignment operator.
+        Curve* getCurve() const;
 
         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.
-        Curve* _curve;                        // The curve used to represent the animation data.
+        CurveRef* _curveRef;                  // The curve used to represent the animation data.
         unsigned long _duration;              // The length of the animation (in milliseconds).
     };
 
     /**
-     * Constructor.
-     */
-    Animation();
-
-    /**
-     * Constructor.
+     * Hidden copy constructor.
      */
     Animation(const Animation& copy);
 
@@ -142,11 +160,21 @@ private:
      */
     Animation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, unsigned int type);
 
+    /**
+     * Constructor.
+     */
+    Animation(const char* id);
+
     /**
      * Destructor.
      */
     ~Animation();
 
+    /**
+     * Hidden copy assignment operator.
+     */
+    Animation& operator=(const Animation&);
+
     /**
      * Creates the default clip.
      */
@@ -191,6 +219,13 @@ private:
      * Sets the rotation offset in a Curve representing a Transform's animation data.
      */
     void setTransformRotationOffset(Curve* curve, unsigned int propertyId);
+
+    /**
+     * Clones this animation.
+     * 
+     * @return The newly created animation.
+     */
+    Animation* clone();
     
     AnimationController* _controller;       // The AnimationController that this Animation will run on.
     std::string _id;                        // The Animation's ID.

+ 2 - 2
gameplay/src/AnimationClip.cpp

@@ -19,7 +19,7 @@ AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long
     unsigned int channelCount = _animation->_channels.size();    
     for (unsigned int i = 0; i < channelCount; i++)
     {
-        _values.push_back(new AnimationValue(_animation->_channels[i]->_curve->getComponentCount()));
+        _values.push_back(new AnimationValue(_animation->_channels[i]->getCurve()->getComponentCount()));
     }
 }
 
@@ -446,7 +446,7 @@ bool AnimationClip::update(unsigned long elapsedTime, std::list<AnimationTarget*
             activeTargets->push_front(target);
 
         // Evaluate the point on Curve
-        channel->_curve->evaluate(percentComplete, value->_value);
+        channel->getCurve()->evaluate(percentComplete, value->_value);
         // Set the animation value on the target property.
         target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight);
     }

+ 1 - 1
gameplay/src/AnimationController.cpp

@@ -229,7 +229,7 @@ Animation* AnimationController::createAnimation(const char* id, AnimationTarget*
     
     const char* keyOutStr = animationProperties->getString("keyOut");
     float* keyOut = NULL;
-    if(keyOutStr)
+    if (keyOutStr)
     {   
         keyOut = new float[components];
         startOffset = 0;

+ 28 - 0
gameplay/src/AnimationTarget.cpp

@@ -118,6 +118,34 @@ void AnimationTarget::deleteChannel(Animation::Channel* channel)
     }
 }
 
+void AnimationTarget::cloneInto(AnimationTarget* target, CloneContext &context) const
+{
+    if (_animationChannels)
+    {
+        for (std::vector<Animation::Channel*>::const_iterator it = _animationChannels->begin(); it != _animationChannels->end(); ++it)
+        {
+            Animation::Channel* channel = *it;
+            assert(channel->_animation);
+
+            bool animationCloned = false;
+
+            // Don't clone the Animaton if it is already in the CloneContext.
+            Animation* animation = context.findClonedAnimation(channel->_animation);
+            if (animation == NULL)
+            {
+                animation = channel->_animation->clone();
+                animationCloned = true;
+            }
+            assert(animation);
+
+            context.registerClonedAnimation(channel->_animation, animation);
+            
+            Animation::Channel* channelCopy = new Animation::Channel(*channel, animation, target);
+            animation->addChannel(channelCopy);
+        }
+    }
+}
+
 }
 
 

+ 21 - 1
gameplay/src/AnimationTarget.h

@@ -3,6 +3,7 @@
 
 #include "Curve.h"
 #include "AnimationController.h"
+#include "CloneContext.h"
 
 namespace gameplay
 {
@@ -44,6 +45,7 @@ public:
      * 
      * @param propertyId The ID of the property on the AnimationTarget to set the animation property value on.
      * @param value The container to set the animation property value in.
+     * @param blendWeight The blend weight.
      */
     virtual void setAnimationPropertyValue(int propertyId, AnimationValue* value, float blendWeight = 1.0f) = 0;
 
@@ -65,10 +67,28 @@ protected:
      */
     virtual ~AnimationTarget();
 
-    void addChannel(Animation::Channel* animation);
+    /**
+     * Adds the given animation channel to this animation target.
+     * 
+     * @param channel The animation channel to add.
+     */
+    void addChannel(Animation::Channel* channel);
 
+    /**
+     * Deletes the given animation channel from this animation target.
+     * 
+     * @param channel The animation channel to delete.
+     */
     void deleteChannel(Animation::Channel* channel);
 
+    /**
+     * Copies data from this animation target into the given target for the purpose of cloning.
+     * 
+     * @param target The target to copy into.
+     * @param context The clone context.
+     */
+    void cloneInto(AnimationTarget* target, CloneContext &context) const;
+
     TargetType _targetType;             // The type of target this is.
 
     char _animationPropertyBitFlag;     // Bit flag used to indicate which properties on the AnimationTarget are currently animating.

+ 12 - 12
gameplay/src/AudioBuffer.cpp

@@ -132,23 +132,23 @@ cleanup:
     return NULL;
 #else
     // Get the file header in order to determine the type.
-    AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
+    AAsset* asset = AAssetManager_open(__assetManager, path, AASSET_MODE_RANDOM);
     char header[12];
     if (AAsset_read(asset, header, 12) != 12)
     {
         LOG_ERROR_VARG("Invalid audio buffer file: %s", path);
         return NULL;
-    }
-
-    // Get the file descriptor for the audio file.
-    off_t start, length;
-    int fd = AAsset_openFileDescriptor(asset, &start, &length);
-    if (fd < 0)
-    {
-        LOG_ERROR_VARG("Failed to open file descriptor for asset: %s", path);
-        return NULL;
-    }
-    AAsset_close(asset);
+    }
+
+    // Get the file descriptor for the audio file.
+    off_t start, length;
+    int fd = AAsset_openFileDescriptor(asset, &start, &length);
+    if (fd < 0)
+    {
+        LOG_ERROR_VARG("Failed to open file descriptor for asset: %s", path);
+        return NULL;
+    }
+    AAsset_close(asset);
     SLDataLocator_AndroidFD data = {SL_DATALOCATOR_ANDROIDFD, fd, start, length};
 
     // Set the appropriate mime type information.

+ 1 - 1
gameplay/src/AudioController.cpp

@@ -170,7 +170,7 @@ void AudioController::update(long elapsedTime)
     {
 #ifndef __ANDROID__
         alListenerf(AL_GAIN, listener->getGain());
-        alListenerfv(AL_ORIENTATION, (ALfloat*)&listener->getOrientationForward());
+        alListenerfv(AL_ORIENTATION, (ALfloat*)listener->getOrientation());
         alListenerfv(AL_VELOCITY, (ALfloat*)&listener->getVelocity());
         alListenerfv(AL_POSITION, (ALfloat*)&listener->getPosition());
 #else

+ 1 - 1
gameplay/src/AudioController.h

@@ -58,7 +58,7 @@ private:
     ALCdevice* _alcDevice;
     ALCcontext* _alcContext;
 #else
-    SLObjectItf _engineObject;
+    SLObjectItf _engineObject;
     SLEngineItf _engineEngine;
     SLObjectItf _outputMixObject;
     SLObjectItf _listenerObject;

+ 13 - 2
gameplay/src/AudioListener.cpp

@@ -21,6 +21,8 @@ AudioListener::~AudioListener()
 
 AudioListener* AudioListener::getInstance()
 {
+    if (!__audioListenerInstance)
+        new AudioListener();
     return __audioListenerInstance;
 }
 
@@ -54,6 +56,11 @@ void AudioListener::setVelocity(const Vector3& velocity)
     _velocity = velocity;
 }
 
+const float* AudioListener::getOrientation() const
+{
+    return (const float*)&_orientation[0];
+}
+
 const Vector3& AudioListener::getOrientationForward() const 
 { 
     return _orientation[0]; 
@@ -106,8 +113,12 @@ void AudioListener::transformChanged(Transform* transform, long cookie)
 {
     if (transform)
     {
-        setPosition(transform->getTranslation());
-        setOrientation(transform->getForwardVector(), transform->getUpVector());
+        Node* node = static_cast<Node*>(transform);
+        setPosition(node->getTranslationWorld());
+        
+        Vector3 up;
+        node->getWorldMatrix().getUpVector(&up);
+        setOrientation(node->getForwardVectorWorld(), up);
     }
 }
 

+ 8 - 0
gameplay/src/AudioListener.h

@@ -67,6 +67,14 @@ public:
      */
     void setVelocity(const Vector3& velocity);
 
+    /**
+     * Gets the float pointer to the orientation of the audio listener.
+     * Orientation is represented as 6 floats. (forward.x, forward.y, forward.z, up.x, up.y, up.z).
+     * 
+     * @return Pointer to the 6 orientation float values.
+     */
+    const float* getOrientation() const;
+
     /**
      * Gets the forward orientation vector of the audio listener.
      *

+ 43 - 8
gameplay/src/AudioSource.cpp

@@ -26,37 +26,37 @@ AudioSource::AudioSource(AudioBuffer* buffer, const SLObjectItf& player)
 {
     // Get the different interfaces for the OpenSL audio player that we need.
     SLresult result = (*_playerObject)->GetInterface(_playerObject, SL_IID_3DDOPPLER, &_playerDoppler);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get 3D doppler interface for OpenSL audio player.");
     }
     
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_3DLOCATION, &_playerLocation);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get 3D location interface for OpenSL audio player.");
     }
 
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_PLAY, &_playerPlay);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get play interface for OpenSL audio player.");
     }
 
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_PITCH, &_playerPitch);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get rate pitch interface for OpenSL audio player.");
     }
 
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_SEEK, &_playerSeek);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get seek interface for OpenSL audio player.");
     }
 
     result = (*_playerObject)->GetInterface(_playerObject, SL_IID_VOLUME, &_playerVolume);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::AudioSource() - Failed to get volume interface for OpenSL audio player.");
     }
@@ -159,7 +159,7 @@ AudioSource* AudioSource::create(const char* path)
     }
 
     result = (*player)->Realize(player, SL_BOOLEAN_FALSE);
-    if(result != SL_RESULT_SUCCESS)
+    if (result != SL_RESULT_SUCCESS)
     {
         WARN("AudioSource::create - Failed to realize OpenSL audio player.");
     }
@@ -454,6 +454,8 @@ void AudioSource::setNode(Node* node)
         if (_node)
         {
             _node->addListener(this);
+            // Update the audio source position.
+            transformChanged(_node, 0);
         }
     }
 }
@@ -461,7 +463,8 @@ void AudioSource::setNode(Node* node)
 void AudioSource::transformChanged(Transform* transform, long cookie)
 {
 #ifndef __ANDROID__
-    alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&transform->getTranslation());
+    if (_node)
+        alSourcefv(_alSource, AL_POSITION, (const ALfloat*)&_node->getTranslationWorld());
 #else
     if (_playerLocation)
     {
@@ -478,4 +481,36 @@ void AudioSource::transformChanged(Transform* transform, long cookie)
 #endif
 }
 
+AudioSource* AudioSource::clone(CloneContext &context) const
+{
+#ifndef __ANDROID__
+    ALuint alSource = 0;
+    alGenSources(1, &alSource);
+    if (alGetError() != AL_NO_ERROR)
+    {
+        LOG_ERROR("AudioSource::createAudioSource - Error generating audio source.");
+        return NULL;
+    }
+    AudioSource* audioClone = new AudioSource(_buffer, alSource);
+#else
+    // TODO: Implement cloning audio source for Android
+    AudioSource* audioClone = new AudioSource(AudioBuffer* buffer, const SLObjectItf& player);
+
+#endif
+
+    audioClone->setLooped(isLooped());
+    audioClone->setGain(getGain());
+    audioClone->setPitch(getPitch());
+    audioClone->setVelocity(getVelocity());
+    if (Node* node = audioClone->getNode())
+    {
+        Node* clonedNode = context.findClonedNode(node);
+        if (clonedNode)
+        {
+            audioClone->setNode(clonedNode);
+        }
+    }
+    return audioClone;
+}
+
 }

+ 15 - 6
gameplay/src/AudioSource.h

@@ -176,15 +176,24 @@ private:
      */
     void transformChanged(Transform* transform, long cookie);
 
+    /**
+     * Clones the audio source and returns a new audio source.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created audio source.
+     */
+    AudioSource* clone(CloneContext &context) const;
+
 #ifndef __ANDROID__
     ALuint _alSource;
 #else
-    SLObjectItf _playerObject;
-    SL3DDopplerItf _playerDoppler;
-    SL3DLocationItf _playerLocation;
-    SLPlayItf _playerPlay;
-    SLPitchItf _playerPitch;
-    SLSeekItf _playerSeek;
+    SLObjectItf _playerObject;
+    SL3DDopplerItf _playerDoppler;
+    SL3DLocationItf _playerLocation;
+    SLPlayItf _playerPlay;
+    SLPitchItf _playerPitch;
+    SLSeekItf _playerSeek;
     SLVolumeItf _playerVolume;
     SLmillibel _maxVolume;
 #endif

+ 23 - 5
gameplay/src/Base.h

@@ -98,7 +98,6 @@ extern void printError(const char* format, ...);
 #endif
 #define WARN(x) printError(x)
 #define WARN_VARG(x, ...) printError(x, __VA_ARGS__)
-
 #endif
 
 // Bullet Physics
@@ -204,10 +203,10 @@ extern void printError(const char* format, ...);
     #define OPENGL_ES_PVR    
 #elif WIN32
     #define WIN32_LEAN_AND_MEAN
-	#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG                      0x8C00
-	#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG                      0x8C01
-	#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG                     0x8C02
-	#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG                     0x8C03
+    #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG                      0x8C00
+    #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG                      0x8C01
+    #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG                     0x8C02
+    #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG                     0x8C03
     #include <GL/glew.h>
 #elif __APPLE__
     #include "TargetConditionals.h"
@@ -322,4 +321,23 @@ extern GLenum __gl_error_code;
 extern void amain(struct android_app* state);
 #endif
 
+
+// Assert has special behavior on Windows (for Visual Studio).
+#ifdef WIN32
+#ifdef assert
+#undef assert
+#endif
+#ifdef _DEBUG
+#define assert(expression) do { \
+    if (!(expression)) \
+    { \
+        printError("Assertion \'" #expression "\' failed."); \
+        __debugbreak(); \
+    } } while (0)
+
+#else
+#define assert(expression) do { (void)sizeof(expression); } while (0)
+#endif
+#endif
+
 #endif

+ 20 - 0
gameplay/src/Camera.cpp

@@ -343,6 +343,26 @@ void Camera::pickRay(const Viewport* viewport, float x, float y, Ray* dst)
     dst->set(nearPoint, direction);
 }
 
+Camera* Camera::clone(CloneContext &context) const
+{
+    Camera* cameraClone = NULL;
+    if (getCameraType() == PERSPECTIVE)
+    {
+        cameraClone = createPerspective(_fieldOfView, _aspectRatio, _nearPlane, _farPlane);
+    }
+    else if (getCameraType() == ORTHOGRAPHIC)
+    {
+        cameraClone = createOrthographic(getZoomX(), getZoomY(), getAspectRatio(), _nearPlane, _farPlane);
+    }
+    assert(cameraClone);
+
+    if (Node* node = context.findClonedNode(getNode()))
+    {
+        cameraClone->setNode(node);
+    }
+    return cameraClone;
+}
+
 void Camera::transformChanged(Transform* transform, long cookie)
 {
     _dirtyBits |= CAMERA_DIRTY_VIEW | CAMERA_DIRTY_INV_VIEW | CAMERA_DIRTY_INV_VIEW_PROJ | CAMERA_DIRTY_VIEW_PROJ | CAMERA_DIRTY_BOUNDS;

+ 9 - 0
gameplay/src/Camera.h

@@ -245,6 +245,15 @@ private:
      */
     virtual ~Camera();
 
+    /**
+     * Clones the camera and returns a new camera.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created camera.
+     */
+    Camera* clone(CloneContext &context) const;
+
     /**
      * @see Transform::Listener::transformChanged
      */

+ 38 - 0
gameplay/src/CloneContext.cpp

@@ -0,0 +1,38 @@
+#include "CloneContext.h"
+
+namespace gameplay
+{
+
+CloneContext::CloneContext()
+{
+    
+}
+
+CloneContext::~CloneContext()
+{
+
+}
+
+Animation* CloneContext::findClonedAnimation(const Animation* animation)
+{
+    AnimationMap::iterator it = _clonedAnimations.find(animation);
+    return it != _clonedAnimations.end() ? it->second : NULL;
+}
+
+void CloneContext::registerClonedAnimation(const Animation* original, Animation* clone)
+{
+    _clonedAnimations[original] = clone;
+}
+
+Node* CloneContext::findClonedNode(const Node* node)
+{
+    NodeMap::iterator it = _clonedNodes.find(node);
+    return it != _clonedNodes.end() ? it->second : NULL;
+}
+
+void CloneContext::registerClonedNode(const Node* original, Node* clone)
+{
+    _clonedNodes[original] = clone;
+}
+
+}

+ 86 - 0
gameplay/src/CloneContext.h

@@ -0,0 +1,86 @@
+#ifndef CLONECONTEXT_H_
+#define CLONECONTEXT_H_
+
+#include <map>
+
+namespace gameplay
+{
+    class Animation;
+    class Node;
+
+/**
+ * CloneContext represents the context data that is kept when cloning a node.
+ * 
+ * The CloneContext is used to make sure objects don't get cloned twice.
+ */
+class CloneContext
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    CloneContext();
+
+    /**
+     * Destructor.
+     */
+    ~CloneContext();
+
+    /**
+     * Finds the cloned animation of the given animation or NULL if this animation was not registered with this context.
+     * 
+     * @param animation The animation to search for the cloned copy of.
+     * 
+     * @return The cloned animation or NULL if not found.
+     */
+    Animation* findClonedAnimation(const Animation* animation);
+
+    /**
+     * Registers the cloned animation with this context so that it doesn't get cloned twice.
+     * 
+     * @param original The pointer to the original animation.
+     * @param clone The pointer to the cloned animation.
+     */
+    void registerClonedAnimation(const Animation* original, Animation* clone);
+
+    /**
+     * Finds the cloned node of the given node or NULL if this node was not registered with this context.
+     * 
+     * @param node The node to search for the cloned copy of.
+     * 
+     * @return The cloned node or NULL if not found.
+     */
+    Node* findClonedNode(const Node* node);
+
+    /**
+     * Registers the cloned node with this context so that it doens't get cloned twice.
+     * 
+     * @param original The pointer to the original node.
+     * @param clone The pointer to the cloned node.
+     */
+    void registerClonedNode(const Node* original, Node* clone);
+
+private:
+    
+    /**
+     * Hidden copy constructor.
+     */
+    CloneContext(const CloneContext&);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    CloneContext& operator=(const CloneContext&);
+
+private:
+    typedef std::map<const Animation*, Animation*> AnimationMap;
+    typedef std::map<const Node*, Node*> NodeMap;
+
+    AnimationMap _clonedAnimations;
+    NodeMap _clonedNodes;
+};
+
+}
+
+#endif

+ 1 - 1
gameplay/src/Container.cpp

@@ -34,7 +34,7 @@ namespace gameplay
     Container* Container::create(Layout::Type type)
     {
         Layout* layout = NULL;
-        switch(type)
+        switch (type)
         {
         case Layout::LAYOUT_ABSOLUTE:
             layout = AbsoluteLayout::create();

+ 1 - 1
gameplay/src/FileSystem.cpp

@@ -110,7 +110,7 @@ bool FileSystem::listFiles(const char* dirPath, std::vector<std::string>& files)
             filename.assign(wfilename.begin(), wfilename.end());
             files.push_back(filename);
         }
-    } while(FindNextFile(hFind, &FindFileData) != 0);
+    } while (FindNextFile(hFind, &FindFileData) != 0);
 
     FindClose(hFind);
     return true;

+ 8 - 4
gameplay/src/Font.h

@@ -157,8 +157,8 @@ public:
      *
      * @param text The text to measure.
      * @param size
-     * @param width Destination for the text's width.
-     * @param height Destination for the text's height.
+     * @param widthOut Destination for the text's width.
+     * @param heightOut Destination for the text's height.
      */
     void measureText(const char* text, unsigned int size, unsigned int* widthOut, unsigned int* heightOut);
 
@@ -177,11 +177,15 @@ public:
     void measureText(const char* text, const Rectangle& clip, unsigned int size, Rectangle* out,
                      Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool ignoreClip = false);
 
-    // Get an index into a string corresponding to the character nearest the given location within the clip region.
+    /**
+     * Get an index into a string corresponding to the character nearest the given location within the clip region.
+     */
     unsigned int getIndexAtLocation(const char* text, const Rectangle& clip, unsigned int size, const Vector2& inLocation, Vector2* outLocation,
                                     Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 
-    // Get the location of the character at the given index.
+    /**
+     * Get the location of the character at the given index.
+     */
     void getLocationAtIndex(const char* text, const Rectangle& clip, unsigned int size, Vector2* outLocation, const unsigned int destIndex,
                             Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 

+ 1 - 1
gameplay/src/Form.cpp

@@ -78,7 +78,7 @@ namespace gameplay
     Form* Form::create(const char* themeFile, Layout::Type type)
     {
         Layout* layout;
-        switch(type)
+        switch (type)
         {
         case Layout::LAYOUT_ABSOLUTE:
             layout = AbsoluteLayout::create();

+ 34 - 0
gameplay/src/Game.h

@@ -384,8 +384,42 @@ private:
     AudioController* _audioController;          // Controls audio sources that are playing in the game.
     PhysicsController* _physicsController;      // Controls the simulation of a physics scene and entities.
     std::priority_queue<TimeEvent, std::vector<TimeEvent>, std::less<TimeEvent> > _timeEvents; // Contains the scheduled time events.
+
+    friend class SplashDisplayer;
 };
 
+/**
+ * Used for displaying splash screens.
+ */
+class SplashDisplayer
+{
+public:
+
+    /**
+     * Displays a splash screen using the {@link Game#renderOnce} mechanism for at least the given amount of time.
+     * 
+     * @param instance See {@link Game#renderOnce}.
+     * @param method See {@link Game#renderOnce}.
+     * @param cookie See {@link Game#renderOnce}.
+     * @param time The minimum amount of time to display the splash screen (in milliseconds).
+     */
+    template <typename T> void run(T* instance, void (T::*method) (void*), void* cookie, long time);
+
+    /**
+     * Destructor.
+     */
+    ~SplashDisplayer();
+
+private:
+
+    long _time;
+    long _startTime;
+};
+
+#define displaySplash(instance, method, cookie, time) \
+    SplashDisplayer __##instance##SplashDisplayer; \
+    __##instance##SplashDisplayer.run(instance, method, cookie, time)
+
 }
 
 #include "Game.inl"

+ 14 - 0
gameplay/src/Game.inl

@@ -66,4 +66,18 @@ inline void Game::displayKeyboard(bool display)
     Platform::displayKeyboard(display);
 }
 
+template <typename T> void SplashDisplayer::run(T* instance, void (T::*method) (void*), void* cookie, long time)
+{
+    _time = time;
+    Game::getInstance()->renderOnce(instance, method, cookie);
+    _startTime = Game::getInstance()->getGameTime();
+}
+
+inline SplashDisplayer::~SplashDisplayer()
+{
+    long elapsedTime = Game::getInstance()->getGameTime() - _startTime;
+    if (elapsedTime < _time)
+        Platform::sleep(_time - (Game::getInstance()->getGameTime() - _startTime));
+}
+
 }

+ 10 - 0
gameplay/src/Joint.cpp

@@ -19,6 +19,16 @@ Joint* Joint::create(const char* id)
     return new Joint(id);
 }
 
+Node* Joint::cloneSingleNode(CloneContext &context) const
+{
+    Joint* copy = Joint::create(getId());
+    context.registerClonedNode(this, copy);
+    copy->_bindPose = _bindPose;
+    copy->_skinCount = _skinCount;
+    Node::cloneInto(copy, context);
+    return copy;
+}
+
 Node::Type Joint::getType() const
 {
     return Node::JOINT;

+ 24 - 0
gameplay/src/Joint.h

@@ -53,6 +53,16 @@ protected:
      */
     static Joint* create(const char* id);
 
+    /**
+     * Clones a single node and its data but not its children.
+     * This method returns a node pointer but actually creates a Joint.
+     * 
+     * @param context The clone context.
+     * 
+     * @return Pointer to the newly created joint.
+     */
+    virtual Node* cloneSingleNode(CloneContext &context) const;
+
     /**
      * Sets the inverse bind pose matrix.
      * 
@@ -64,6 +74,20 @@ protected:
 
     void transformChanged();
 
+private:
+
+    /**
+     * Hidden copy constructor.
+     */
+    Joint(const Joint& copy);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    Joint& operator=(const Joint&);
+
+protected:
+
     Matrix _bindPose;
     bool _jointMatrixDirty;
     unsigned int _skinCount;

+ 26 - 0
gameplay/src/Light.cpp

@@ -196,6 +196,32 @@ float Light::getOuterAngleCos()  const
     return _spot->outerAngleCos;
 }
 
+Light* Light::clone(CloneContext &context) const
+{
+    Light* lightClone = NULL;
+    switch (_type)
+    {
+    case DIRECTIONAL:
+        lightClone = createDirectional(getColor());
+        break;
+    case POINT:
+        lightClone = createPoint(getColor(), getRange());
+        break;
+    case SPOT:
+        lightClone = createSpot(getColor(), getRange(), getInnerAngle(), getOuterAngle());
+        break;
+    default:
+        assert(false);
+    }
+    assert(lightClone);
+
+    if (Node* node = context.findClonedNode(getNode()))
+    {
+        lightClone->setNode(node);
+    }
+    return lightClone;
+}
+
 Light::Directional::Directional(const Vector3& color)
     : color(color)
 {

+ 15 - 5
gameplay/src/Light.h

@@ -3,6 +3,7 @@
 
 #include "Ref.h"
 #include "Vector3.h"
+#include "CloneContext.h"
 
 namespace gameplay
 {
@@ -54,8 +55,8 @@ public:
      * 
      * @param color The light's color.
      * @param range The light's range.
-     * @param innerCosAngle The light's inner angle (in radians).
-     * @param outerCosAngle The light's outer angle (in radians).
+     * @param innerAngle The light's inner angle (in radians).
+     * @param outerAngle The light's outer angle (in radians).
      * 
      * @return The new spot light.
      */
@@ -104,7 +105,7 @@ public:
     /**
      * Sets the range of point or spot light.
      *
-     * @param range of point or spot light.
+     * @param range The range of point or spot light.
      */
     void setRange(float range);
 
@@ -125,7 +126,7 @@ public:
     /**
      * Sets the inner angle of a spot light (in radians).
      *
-     * @param inner angle of spot light (in radians).
+     * @param innerAngle The angle of spot light (in radians).
      */
     void setInnerAngle(float innerAngle);
 
@@ -139,7 +140,7 @@ public:
     /**
      * Sets the outer angle of a spot light (in radians).
      *
-     * @param outer angle of spot light (in radians).
+     * @param outerAngle The angle of spot light (in radians).
      */
     void setOuterAngle(float outerAngle);
 
@@ -223,6 +224,15 @@ private:
      */
     void setNode(Node* node);
 
+    /**
+     * Clones the light and returns a new light.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created light.
+     */
+    Light* clone(CloneContext &context) const;
+
     Light::Type _type;
     union
     {

+ 18 - 0
gameplay/src/Material.cpp

@@ -125,6 +125,24 @@ Material* Material::create(const char* vshPath, const char* fshPath, const char*
     return material;
 }
 
+Material* Material::clone(CloneContext &context) const
+{
+    Material* material = new Material();
+    RenderState::cloneInto(material, context);
+
+    for (std::vector<Technique*>::const_iterator it = _techniques.begin(); it != _techniques.end(); ++it)
+    {
+        const Technique* technique = *it;
+        Technique* techniqueClone = technique->clone(material, context);
+        material->_techniques.push_back(techniqueClone);
+        if (_currentTechnique == technique)
+        {
+            material->_currentTechnique = techniqueClone;
+        }
+    }
+    return material;
+}
+
 unsigned int Material::getTechniqueCount() const
 {
     return _techniques.size();

+ 9 - 0
gameplay/src/Material.h

@@ -68,6 +68,15 @@ public:
      */
     static Material* create(const char* vshPath, const char* fshPath, const char* defines = NULL);
 
+    /**
+     * Clones this material.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created material.
+     */
+    Material* clone(CloneContext &context) const;
+
     /**
      * Returns the number of techniques in the material.
      *

+ 80 - 2
gameplay/src/MaterialParameter.cpp

@@ -32,7 +32,7 @@ void MaterialParameter::clearValue()
             SAFE_DELETE_ARRAY(_value.intPtrValue);
             break;
         case MaterialParameter::METHOD:
-            SAFE_DELETE(_value.method);
+            SAFE_RELEASE(_value.method);
             break;
         }
 
@@ -294,7 +294,7 @@ unsigned int MaterialParameter::getAnimationPropertyComponentCount(int propertyI
     {
         case ANIMATE_UNIFORM:
         {
-            switch(_type)
+            switch (_type)
             {
                 // These types don't support animation.
                 case NONE:
@@ -478,4 +478,82 @@ void MaterialParameter::applyAnimationValue(AnimationValue* value, float blendWe
     }
 }
 
+void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
+{
+    materialParameter->_type = _type;
+    materialParameter->_count = _count;
+    materialParameter->_dynamic = _dynamic;
+    materialParameter->_uniform = _uniform;
+    switch (_type)
+    {
+    case NONE:
+        break;
+    case FLOAT:
+        materialParameter->setValue(_value.floatValue);
+        break;
+    case INT:
+        materialParameter->setValue(_value.intValue);
+        break;
+    case VECTOR2:
+    {
+        Vector2* value = reinterpret_cast<Vector2*>(_value.floatPtrValue);
+        if (_count == 1)
+        {
+            materialParameter->setValue(*value);
+        }
+        else
+        {
+            materialParameter->setValue(value, _count);
+        }
+        break;
+    }   
+    case VECTOR3:
+    {
+        Vector3* value = reinterpret_cast<Vector3*>(_value.floatPtrValue);
+        if (_count == 1)
+        {
+            materialParameter->setValue(*value);
+        }
+        else
+        {
+            materialParameter->setValue(value, _count);
+        }
+        break;
+    }
+    case VECTOR4:
+    {
+        Vector4* value = reinterpret_cast<Vector4*>(_value.floatPtrValue);
+        if (_count == 1)
+        {
+            materialParameter->setValue(*value);
+        }
+        else
+        {
+            materialParameter->setValue(value, _count);
+        }
+        break;
+    }
+    case MATRIX:
+    {
+        Matrix* value = reinterpret_cast<Matrix*>(_value.floatPtrValue);
+        if (_count == 1)
+        {
+            materialParameter->setValue(*value);
+        }
+        else
+        {
+            materialParameter->setValue(value, _count);
+        }
+        break;
+    }
+    case SAMPLER:
+        materialParameter->setValue(_value.samplerValue);
+        break;
+    case METHOD:
+        materialParameter->_value.method = _value.method;
+        materialParameter->_value.method->addRef();
+        break;
+    }
+}
+
 }

+ 11 - 2
gameplay/src/MaterialParameter.h

@@ -171,14 +171,16 @@ private:
     /**
      * Interface implemented by templated method bindings for simple storage and iteration.
      */
-    class MethodBinding
+    class MethodBinding : public Ref
     {
     public:
+        virtual void setValue(Effect* effect) = 0;
+
+    protected:
         /**
          * Destructor.
          */
         virtual ~MethodBinding() { }
-        virtual void setValue(Effect* effect) = 0;
     };
 
     /**
@@ -257,6 +259,13 @@ private:
 
     void applyAnimationValue(AnimationValue* value, float blendWeight, int components);
 
+    /**
+     * Copies the data from this MaterialParameter into the given MaterialParameter.
+     * 
+     * @param materialParameter The MaterialParameter to copy the data to.
+     */
+    void cloneInto(MaterialParameter* materialParameter) const;
+
     unsigned int _count;
     bool _dynamic;
     std::string _name;

+ 1 - 1
gameplay/src/MeshBatch.h

@@ -54,7 +54,7 @@ public:
     /**
      * Explicitly sets a new capacity for the batch.
      *
-     * @param The new batch capacity.
+     * @param capacity The new batch capacity.
      */
     void setCapacity(unsigned int capacity);
 

+ 60 - 1
gameplay/src/MeshSkin.cpp

@@ -9,7 +9,7 @@ namespace gameplay
 {
 
 MeshSkin::MeshSkin()
-    : _rootJoint(NULL), _matrixPalette(NULL), _model(NULL)
+    : _rootJoint(NULL), _rootNode(NULL), _matrixPalette(NULL), _model(NULL)
 {
 }
 
@@ -57,6 +57,37 @@ Joint* MeshSkin::getJoint(const char* id) const
     return NULL;
 }
 
+MeshSkin* MeshSkin::clone() const
+{
+    MeshSkin* skin = new MeshSkin();
+    skin->_bindShape = _bindShape;
+    if (_rootNode && _rootJoint)
+    {
+        const unsigned int jointCount = getJointCount();
+        skin->setJointCount(jointCount);
+
+        assert(skin->_rootNode == NULL);
+        skin->_rootNode = _rootNode->clone();
+        Node* node = skin->_rootNode->findNode(_rootJoint->getId());
+        assert(node);
+        skin->_rootJoint = static_cast<Joint*>(node);
+        for (unsigned int i = 0; i < jointCount; ++i)
+        {
+            Joint* oldJoint = getJoint(i);
+            
+            Joint* newJoint = static_cast<Joint*>(skin->_rootJoint->findNode(oldJoint->getId()));
+            if (!newJoint)
+            {
+                if (strcmp(skin->_rootJoint->getId(), oldJoint->getId()) == 0)
+                    newJoint = static_cast<Joint*>(skin->_rootJoint);
+            }
+            assert(newJoint);
+            skin->setJoint(newJoint, i);
+        }
+    }
+    return skin;
+}
+
 void MeshSkin::setJointCount(unsigned int jointCount)
 {
     // Erase the joints vector and release all joints
@@ -145,6 +176,21 @@ void MeshSkin::setRootJoint(Joint* joint)
     {
         _rootJoint->getParent()->addListener(this, 1);
     }
+
+    Node* newRootNode = _rootJoint;
+    if (newRootNode)
+    {
+        // Find the top level parent node of the root joint
+        for (Node* node = newRootNode->getParent(); node != NULL; node = node->getParent())
+        {
+            if (node->getParent() == NULL)
+            {
+                newRootNode = node;
+                break;
+            }
+        }
+    }
+    setRootNode(newRootNode);
 }
 
 void MeshSkin::transformChanged(Transform* transform, long cookie)
@@ -179,6 +225,19 @@ int MeshSkin::getJointIndex(Joint* joint) const
     return -1;
 }
 
+void MeshSkin::setRootNode(Node* node)
+{
+    if (_rootNode != node)
+    {
+        SAFE_RELEASE(_rootNode);
+        _rootNode = node;
+        if (_rootNode)
+        {
+            _rootNode->addRef();
+        }
+    }
+}
+
 void MeshSkin::clearJoints()
 {
     setRootJoint(NULL);

+ 29 - 0
gameplay/src/MeshSkin.h

@@ -10,6 +10,7 @@ namespace gameplay
 class Package;
 class Model;
 class Joint;
+class Node;
 
 /**
  * Represents the skin for a mesh.
@@ -115,10 +116,27 @@ private:
      */
     MeshSkin();
 
+    /**
+     * Hidden copy constructor.
+     */
+    MeshSkin(const MeshSkin&);
+
     /**
      * Destructor.
      */
     ~MeshSkin();
+    
+    /**
+     * Hidden copy assignment operator.
+     */
+    MeshSkin& operator=(const MeshSkin&);
+
+    /**
+     * Clones the MeshSkin and the joints that it references.
+     * 
+     * @return The newly created MeshSkin.
+     */
+    MeshSkin* clone() const;
 
     /**
      * Sets the number of joints that can be stored in this skin.
@@ -136,6 +154,13 @@ private:
      */
     void setJoint(Joint* joint, unsigned int index);
 
+    /**
+     * Sets the root node of this mesh skin.
+     * 
+     * @param node The node to set as the root node, may be NULL.
+     */
+    void setRootNode(Node* node);
+
     /**
      * Clears the list of joints and releases each joint.
      */
@@ -144,6 +169,10 @@ private:
     Matrix _bindShape;
     std::vector<Joint*> _joints;
     Joint* _rootJoint;
+    // Pointer to the root node of the mesh skin.
+    // The purpose is so that the joint hierarchy doesn't need to be in the scene.
+    // If the joints are not in the scene then something has to hold a reference to it.
+    Node* _rootNode;
 
     // Pointer to the array of palette matrices.
     // This array is passed to the vertex shader as a uniform.

+ 18 - 4
gameplay/src/Model.cpp

@@ -191,12 +191,12 @@ Material* Model::setMaterial(const char* materialPath, int partIndex)
     return material;
 }
 
-bool Model::hasPartMaterial(unsigned int partIndex) const
+bool Model::hasMaterial(unsigned int partIndex) const
 {
-    return (partIndex >= 0 && partIndex < _partCount && _partMaterials && _partMaterials[partIndex]);
+    return (partIndex < _partCount && _partMaterials && _partMaterials[partIndex]);
 }
 
-MeshSkin* Model::getSkin()
+MeshSkin* Model::getSkin() const
 {
     return _skin;
 }
@@ -210,7 +210,8 @@ void Model::setSkin(MeshSkin* skin)
 
         // Assign the new skin
         _skin = skin;
-        _skin->_model = this;
+        if (_skin)
+            _skin->_model = this;
     }
 }
 
@@ -358,6 +359,19 @@ void Model::validatePartCount()
     }
 }
 
+Model* Model::clone(CloneContext &context)
+{
+    Model* model = Model::create(getMesh());
+    if (getSkin())
+    {
+        model->setSkin(getSkin()->clone());
+    }
+    Material* materialClone = getMaterial()->clone(context);
+    model->setMaterial(materialClone); // TODO: Don't forget material parts
+    materialClone->release();
+    return model;
+}
+
 void Model::setMaterialNodeBinding(Material *material)
 {
     if (_node)

+ 11 - 2
gameplay/src/Model.h

@@ -116,14 +116,14 @@ public:
      *
      * @return True if a custom MeshPart material is set for the specified index, false otherwise.
      */
-    bool hasPartMaterial(unsigned int partIndex) const;
+    bool hasMaterial(unsigned int partIndex) const;
 
     /**
      * Returns the MeshSkin.
      * 
      * @return The MeshSkin, or NULL if one is not set.
      */
-    MeshSkin* getSkin();
+    MeshSkin* getSkin() const;
 
     /**
      * Returns the node that is associated with this model.
@@ -177,6 +177,15 @@ private:
 
     void validatePartCount();
 
+    /**
+     * Clones the model and returns a new model.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The new cloned model.
+     */
+    Model* clone(CloneContext &context);
+
     Mesh* _mesh;
     Material* _material;
     unsigned int _partCount;

+ 67 - 7
gameplay/src/Node.cpp

@@ -5,6 +5,7 @@
 #include "PhysicsRigidBody.h"
 #include "PhysicsGhostObject.h"
 #include "PhysicsCharacter.h"
+#include "Game.h"
 
 #define NODE_DIRTY_WORLD 1
 #define NODE_DIRTY_BOUNDS 2
@@ -24,11 +25,6 @@ Node::Node(const char* id)
     }
 }
 
-Node::Node(const Node& node)
-{
-    // hidden
-}
-
 Node::~Node()
 {
     removeAllChildren();
@@ -204,7 +200,7 @@ unsigned int Node::getChildCount() const
     return _childCount;
 }
 
-Node* Node::findNode(const char* id, bool recursive, bool exactMatch)
+Node* Node::findNode(const char* id, bool recursive, bool exactMatch) const
 {
     assert(id);
     
@@ -234,7 +230,7 @@ Node* Node::findNode(const char* id, bool recursive, bool exactMatch)
     return NULL;
 }   
 
-unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool recursive, bool exactMatch)
+unsigned int Node::findNodes(const char* id, std::vector<Node*>& nodes, bool recursive, bool exactMatch) const
 {
     assert(id);
     
@@ -705,6 +701,70 @@ const BoundingSphere& Node::getBoundingSphere() const
     return _bounds;
 }
 
+
+Node* Node::clone() const
+{
+    CloneContext context;
+    return cloneRecursive(context);
+}
+
+Node* Node::cloneSingleNode(CloneContext &context) const
+{
+    Node* copy = Node::create(getId());
+    context.registerClonedNode(this, copy);
+    cloneInto(copy, context);
+    return copy;
+}
+
+Node* Node::cloneRecursive(CloneContext &context) const
+{
+    Node* copy = cloneSingleNode(context);
+
+    for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
+    {
+        Node* childCopy = child->cloneRecursive(context);
+        copy->addChild(childCopy); // TODO: Does child order matter?
+        childCopy->release();
+    }
+    return copy;
+}
+
+void Node::cloneInto(Node* node, CloneContext &context) const
+{
+    Transform::cloneInto(node, context);
+
+    // TODO: Clone the rest of the node data.
+    //node->setCamera(getCamera());
+    //node->setLight(getLight());
+
+    if (Camera* camera = getCamera())
+    {
+        Camera* cameraClone = camera->clone(context);
+        node->setCamera(cameraClone);
+        cameraClone->release();
+    }
+    if (Light* light = getLight())
+    {
+        Light* lightClone = lightClone = light->clone(context);
+        node->setLight(lightClone);
+        lightClone->release();
+    }
+    if (AudioSource* audio = getAudioSource())
+    {
+        AudioSource* audioClone = audio->clone(context);
+        node->setAudioSource(audioClone);
+        audioClone->release();
+    }
+    if (Model* model = getModel())
+    {
+        Model* modelClone = model->clone(context);
+        node->setModel(modelClone);
+        modelClone->release();
+    }
+    node->_world = _world;
+    node->_bounds = _bounds;
+}
+
 AudioSource* Node::getAudioSource() const
 {
     return _audioSource;

+ 55 - 10
gameplay/src/Node.h

@@ -123,18 +123,18 @@ public:
     /**
      * Returns the first child node that matches the given ID.
      *
-     * This method checks the specified ID against its own ID, as well as its 
-     * immediate children nodes. If recursive is true, it also traverses the
-     * Node's hierarchy.
+     * This method checks the specified ID against its immediate child nodes 
+     * but does not check the ID against itself.
+     * If recursive is true, it also traverses the Node's hierarchy with a breadth first search.
      *
      * @param id The ID of the child to find.
-     * @param recursive true to search recursively all the node's children, false for only direct children.
+     * @param recursive True to search recursively all the node's children, false for only direct children.
      * @param exactMatch true if only nodes whose ID exactly matches the specified ID are returned,
      *        or false if nodes that start with the given ID are returned.
      * 
      * @return The Node found or NULL if not found.
      */
-    Node* findNode(const char* id, bool recursive = true, bool exactMatch = true);
+    Node* findNode(const char* id, bool recursive = true, bool exactMatch = true) const;
 
     /**
      * Returns all child nodes that match the given ID.
@@ -147,7 +147,7 @@ public:
      * 
      * @return The number of matches found.
      */
-    unsigned int findNodes(const char* id, std::vector<Node*>& nodes, bool recursive = true, bool exactMatch = true);
+    unsigned int findNodes(const char* id, std::vector<Node*>& nodes, bool recursive = true, bool exactMatch = true) const;
 
     /**
      * Gets the scene.
@@ -451,6 +451,13 @@ public:
      */
     const BoundingSphere& getBoundingSphere() const;
 
+    /**
+     * Clones the node and all of its child nodes.
+     * 
+     * @return A new node.
+     */
+    Node* clone() const;
+
 protected:
 
     /**
@@ -459,14 +466,35 @@ protected:
     Node(const char* id);
 
     /**
-     * Copy constructor.
+     * Destructor.
      */
-    Node(const Node& copy);
+    virtual ~Node();
 
     /**
-     * Destructor.
+     * Clones a single node and its data but not its children.
+     * 
+     * @param context The clone context.
+     * 
+     * @return Pointer to the newly created node.
      */
-    virtual ~Node();
+    virtual Node* cloneSingleNode(CloneContext &context) const;
+
+    /**
+     * Recursively clones this node and its children.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created node.
+     */
+    Node* cloneRecursive(CloneContext &context) const;
+
+    /**
+     * Copies the data from this node into the given node.
+     * 
+     * @param node The node to copy the data to.
+     * @param context The clone context.
+     */
+    void cloneInto(Node* node, CloneContext &context) const;
 
     /**
      * Removes this node from its parent.
@@ -478,6 +506,9 @@ protected:
      */
     void transformChanged();
 
+    /**
+     * Called when this Node's hierarchy changes.
+     */
     void hierarchyChanged();
 
     /**
@@ -485,6 +516,20 @@ protected:
      */
     void setBoundsDirty();
 
+private:
+
+    /**
+     * Hidden copy constructor.
+     */
+    Node(const Node& copy);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    Node& operator=(const Node&);
+
+protected:
+
     Scene* _scene;
     std::string _id;
     Node* _firstChild;

+ 3 - 4
gameplay/src/ParticleEmitter.cpp

@@ -50,12 +50,11 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
     Texture* texture = NULL;
     texture = Texture::create(textureFile, true);    
 
-    if (!texture)
+	if (!texture)
     {
-        // Use default texture.
-        texture = Texture::create("../gameplay/res/textures/particle-default.png", true);
+        LOG_ERROR_VARG("Error creating ParticleEmitter: Could not read texture file: %s", textureFile);
+        return NULL;
     }
-    assert(texture);
 
     // Use default SpriteBatch material.
     SpriteBatch* batch =  SpriteBatch::create(texture, NULL, particleCountMax);

+ 3 - 3
gameplay/src/ParticleEmitter.h

@@ -432,8 +432,8 @@ public:
      * Gets the maximum rotation speed of each emitted particle.
      * This determines the speed of rotation of each particle's screen-facing billboard.
      *
-     * @param min The minimum rotation speed (per particle).
-     * @param max The maximum rotation speed (per particle).
+     * @param speedMin The minimum rotation speed (per particle).
+     * @param speedMax The maximum rotation speed (per particle).
      */
     void setRotationPerParticle(float speedMin, float speedMax);
 
@@ -494,7 +494,7 @@ public:
     /**
      * Sets whether particles cycle through the sprite frames.
      *
-     * @param animating Whether to animate particles through the sprite frames.
+     * @param animated Whether to animate particles through the sprite frames.
      */
     void setSpriteAnimated(bool animated);
 

+ 9 - 4
gameplay/src/Pass.cpp

@@ -14,10 +14,6 @@ Pass::Pass(const char* id, Technique* technique, Effect* effect) :
     RenderState::_parent = _technique;
 }
 
-Pass::Pass(const Pass& copy)
-{
-}
-
 Pass::~Pass()
 {
     SAFE_RELEASE(_effect);
@@ -83,4 +79,13 @@ void Pass::unbind()
     }
 }
 
+Pass* Pass::clone(Technique* technique, CloneContext &context) const
+{
+    Effect* effect = getEffect();
+    effect->addRef();
+    Pass* pass = new Pass(getId(), technique, effect);
+    RenderState::cloneInto(pass, context);
+    return pass;
+}
+
 }

+ 15 - 0
gameplay/src/Pass.h

@@ -87,6 +87,21 @@ private:
      */
     static Pass* create(const char* id, Technique* technique, const char* vshPath, const char* fshPath, const char* defines);
 
+    /**
+     * Hidden copy assignment operator.
+     */
+    Pass& operator=(const Pass&);
+
+    /**
+     * Clones the Pass and assigns it the given Technique.
+     * 
+     * @param technique The technique to assign to the new Pass.
+     * @param context The clone context.
+     * 
+     * @return The newly created Pass.
+     */
+    Pass* clone(Technique* technique, CloneContext &context) const;
+
     std::string _id;
     Technique* _technique;
     Effect* _effect;

+ 7 - 0
gameplay/src/Platform.h

@@ -123,6 +123,13 @@ public:
 
     static void keyEventInternal(Keyboard::KeyEvent evt, int key);
 
+    /**
+     * Sleeps synchronously for the given amount of time (in milliseconds).
+     *
+     * @param ms How long to sleep (in milliseconds).
+     */
+    static void sleep(long ms);
+
 private:
 
     /**

+ 5 - 0
gameplay/src/PlatformAndroid.cpp

@@ -848,6 +848,11 @@ void Platform::displayKeyboard(bool display)
         __displayKeyboard = false;
 }
 
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
+
 }
 
 #endif

+ 7 - 0
gameplay/src/PlatformMacOS.mm

@@ -5,6 +5,8 @@
 #include "FileSystem.h"
 #include "Game.h"
 
+#include <unistd.h>
+
 #import <Cocoa/Cocoa.h>
 #import <QuartzCore/CVDisplayLink.h>
 #import <OpenGL/OpenGL.h>
@@ -687,6 +689,11 @@ void Platform::touchEventInternal(Touch::TouchEvent evt, int x, int y, unsigned
     }
 }
 
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
+
 }
 
 #endif

+ 6 - 0
gameplay/src/PlatformQNX.cpp

@@ -4,6 +4,7 @@
 #include "Platform.h"
 #include "FileSystem.h"
 #include "Game.h"
+#include <unistd.h>
 #include <sys/keycodes.h>
 #include <screen/screen.h>
 #include <input/screen_helpers.h>
@@ -1098,6 +1099,11 @@ void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
     Form::keyEventInternal(evt, key);
 }
 
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
+
 }
 
 #endif

+ 5 - 0
gameplay/src/PlatformWin32.cpp

@@ -682,6 +682,11 @@ void Platform::keyEventInternal(Keyboard::KeyEvent evt, int key)
     Form::keyEventInternal(evt, key);
 }
 
+void Platform::sleep(long ms)
+{
+    Sleep(ms);
+}
+
 }
 
 #endif

+ 7 - 0
gameplay/src/PlatformiOS.mm

@@ -5,6 +5,8 @@
 #include "FileSystem.h"
 #include "Game.h"
 
+#include <unistd.h>
+
 #import <UIKit/UIKit.h>
 #import <QuartzCore/QuartzCore.h>
 #import <CoreMotion/CoreMotion.h>
@@ -853,6 +855,11 @@ void Platform::displayKeyboard(bool display)
         else [__view dismissKeyboard];
     }
 }
+
+void Platform::sleep(long ms)
+{
+    usleep(ms * 1000);
+}
     
 }
 

+ 26 - 4
gameplay/src/RenderState.cpp

@@ -21,10 +21,6 @@ RenderState::RenderState()
 {
 }
 
-RenderState::RenderState(const RenderState& copy)
-{
-}
-
 RenderState::~RenderState()
 {
     SAFE_RELEASE(_state);
@@ -311,6 +307,32 @@ RenderState* RenderState::getTopmost(RenderState* below)
     return NULL;
 }
 
+void RenderState::cloneInto(RenderState* renderState, CloneContext& context) const
+{
+    for (std::map<std::string, AutoBinding>::const_iterator it = _autoBindings.begin(); it != _autoBindings.end(); ++it)
+    {
+        renderState->setParameterAutoBinding(it->first.c_str(), it->second);
+    }
+    for (std::vector<MaterialParameter*>::const_iterator it = _parameters.begin(); it != _parameters.end(); ++it)
+    {
+        const MaterialParameter* param = *it;
+
+        MaterialParameter* paramCopy = new MaterialParameter(param->getName());
+        param->cloneInto(paramCopy);
+
+        renderState->_parameters.push_back(paramCopy);
+    }
+    renderState->_parent = _parent;
+    if (Node* node = context.findClonedNode(_nodeBinding))
+    {
+        renderState->setNodeBinding(node);
+    }
+    if (_state)
+    {
+        renderState->setStateBlock(_state);
+    }
+}
+
 RenderState::StateBlock::StateBlock()
     : _blendEnabled(false), _cullFaceEnabled(false), _depthTestEnabled(false), _depthWriteEnabled(false),
       _srcBlend(RenderState::BLEND_ONE), _dstBlend(RenderState::BLEND_ONE), _bits(0L)

+ 23 - 5
gameplay/src/RenderState.h

@@ -2,6 +2,7 @@
 #define RENDERSTATE_H_
 
 #include "Ref.h"
+#include "CloneContext.h"
 
 namespace gameplay
 {
@@ -289,11 +290,6 @@ protected:
      */
     RenderState();
 
-    /**
-     * Hidden copy constructor.
-     */
-    RenderState(const RenderState& copy);
-
     /**
      * Destructor.
      */
@@ -336,6 +332,28 @@ protected:
      */
     RenderState* getTopmost(RenderState* below);
 
+    /**
+     * Copies the data from this RenderState into the given RenderState.
+     * 
+     * @param renderState The RenderState to copy the data to.
+     * @param context The clone context.
+     */
+    void cloneInto(RenderState* renderState, CloneContext& context) const;
+
+private:
+
+    /**
+     * Hidden copy constructor.
+     */
+    RenderState(const RenderState& copy);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    RenderState& operator=(const RenderState&);
+
+protected:
+
     mutable std::vector<MaterialParameter*> _parameters;
     std::map<std::string, AutoBinding> _autoBindings;
     Node* _nodeBinding;

+ 1 - 0
gameplay/src/SpriteBatch.h

@@ -192,6 +192,7 @@ public:
      * @param u2 Texture coordinate.
      * @param v2 Texture coordinate.
      * @param color The color to tint the sprite. Use white for no tint.
+     * @param clip The clip rectangle.
      */
     void draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip);
 

+ 14 - 4
gameplay/src/Technique.cpp

@@ -13,10 +13,6 @@ Technique::Technique(const char* id, Material* material)
     RenderState::_parent = material;
 }
 
-Technique::Technique(const Technique& m)
-{
-}
-
 Technique::~Technique()
 {
     // Destroy all the passes.
@@ -57,4 +53,18 @@ Pass* Technique::getPass(const char* id) const
     return NULL;
 }
 
+Technique* Technique::clone(Material* material, CloneContext &context) const
+{
+    Technique* technique = new Technique(getId(), material);
+    technique->_material = material;
+    for (std::vector<Pass*>::const_iterator it = _passes.begin(); it != _passes.end(); ++it)
+    {
+        Pass* pass = *it;
+        Pass* passCopy = pass->clone(technique, context);
+        technique->_passes.push_back(passCopy);
+    }
+    RenderState::cloneInto(technique, context);
+    return technique;
+}
+
 }

+ 8 - 1
gameplay/src/Technique.h

@@ -51,13 +51,20 @@ private:
     /**
      * Hidden copy constructor.
      */
-    Technique(const Technique& t);
+    Technique(const Technique&);
 
     /**
      * Destructor.
      */
     ~Technique();
 
+    /**
+     * Hidden copy assignment operator.
+     */
+    Technique& operator=(const Technique&);
+
+    Technique* clone(Material* material, CloneContext &context) const;
+
     std::string _id;
     Material* _material;
     std::vector<Pass*> _passes;

+ 8 - 0
gameplay/src/Transform.cpp

@@ -780,6 +780,14 @@ void Transform::transformChanged()
     }
 }
 
+void Transform::cloneInto(Transform* transform, CloneContext &context) const
+{
+    AnimationTarget::cloneInto(transform, context);
+    transform->_scale.set(_scale);
+    transform->_rotation.set(_rotation);
+    transform->_translation.set(_translation);
+}
+
 void Transform::applyAnimationValueScaleX(float sx, float blendWeight)
 {
     if ((_animationPropertyBitFlag & ANIMATION_SCALE_X_BIT) != ANIMATION_SCALE_X_BIT)

+ 2 - 1
gameplay/src/Transform.h

@@ -674,7 +674,7 @@ public:
      * Transforms the specified vector and stores the
      * result in the original vector.
      *
-     * @param normal The vector to transform.
+     * @param vector The vector to transform.
      */
     void transformVector(Vector3* vector);
 
@@ -737,6 +737,7 @@ protected:
 
     void dirty();
     virtual void transformChanged();
+    void cloneInto(Transform* transform, CloneContext &context) const;
 
     Vector3 _scale;
     Quaternion _rotation;

+ 2 - 1
gameplay/src/Vector2.cpp

@@ -165,9 +165,10 @@ void Vector2::negate()
     y = -y;
 }
 
-void Vector2::normalize()
+Vector2& Vector2::normalize()
 {
     normalize(this);
+    return *this;
 }
 
 void Vector2::normalize(Vector2* dst)

+ 3 - 1
gameplay/src/Vector2.h

@@ -231,8 +231,10 @@ public:
      * after calling this method will be 1.0f). If the vector
      * already has unit length or if the length of the vector
      * is zero, this method does nothing.
+     * 
+     * @return This vector, after the normalization occurs.
      */
-    void normalize();
+    Vector2& normalize();
 
     /**
      * Normalizes this vector and stores the result in dst.

+ 2 - 1
gameplay/src/Vector3.cpp

@@ -230,9 +230,10 @@ void Vector3::negate()
     z = -z;
 }
 
-void Vector3::normalize()
+Vector3& Vector3::normalize()
 {
     normalize(this);
+    return *this;
 }
 
 void Vector3::normalize(Vector3* dst) const

+ 3 - 1
gameplay/src/Vector3.h

@@ -278,8 +278,10 @@ public:
      * after calling this method will be 1.0f). If the vector
      * already has unit length or if the length of the vector
      * is zero, this method does nothing.
+     * 
+     * @return This vector, after the normalization occurs.
      */
-    void normalize();
+    Vector3& normalize();
 
     /**
      * Normalizes this vector and stores the result in dst.

+ 2 - 1
gameplay/src/Vector4.cpp

@@ -233,9 +233,10 @@ void Vector4::negate()
     w = -w;
 }
 
-void Vector4::normalize()
+Vector4& Vector4::normalize()
 {
     normalize(this);
+    return *this;
 }
 
 void Vector4::normalize(Vector4* dst)

+ 3 - 1
gameplay/src/Vector4.h

@@ -269,8 +269,10 @@ public:
      * after calling this method will be 1.0f). If the vector
      * already has unit length or if the length of the vector
      * is zero, this method does nothing.
+     * 
+     * @return This vector, after the normalization occurs.
      */
-    void normalize();
+    Vector4& normalize();
 
     /**
      * Normalizes this vector and stores the result in dst.