فهرست منبع

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

Adam Blake 13 سال پیش
والد
کامیت
0213721be9
99فایلهای تغییر یافته به همراه3089 افزوده شده و 1288 حذف شده
  1. 15 12
      .gitignore
  2. 7 7
      README.md
  3. 15 5
      gameplay-encoder/gameplay-encoder.xcodeproj/project.pbxproj
  4. 3 1
      gameplay-encoder/gameplay-encoder.xcodeproj/xcshareddata/xcschemes/gameplay-encoder.xcscheme
  5. 6 0
      gameplay-encoder/src/FBXSceneEncoder.cpp
  6. 22 14
      gameplay-newproject.sh
  7. 12 63
      gameplay-template/gameplay-template.xcodeproj/project.pbxproj
  8. 3 1
      gameplay-template/template.bar-descriptor.xml
  9. 8 0
      gameplay/gameplay.vcxproj
  10. 25 1
      gameplay/gameplay.vcxproj.filters
  11. 29 15
      gameplay/gameplay.xcodeproj/project.pbxproj
  12. BIN
      gameplay/res/logo_black.png
  13. BIN
      gameplay/res/logo_powered_black.png
  14. BIN
      gameplay/res/logo_powered_white.png
  15. BIN
      gameplay/res/logo_white.png
  16. BIN
      gameplay/res/textures/particle-default.png
  17. 50 2
      gameplay/src/Animation.cpp
  18. 44 9
      gameplay/src/Animation.h
  19. 2 2
      gameplay/src/AnimationClip.cpp
  20. 1 1
      gameplay/src/AnimationController.cpp
  21. 29 0
      gameplay/src/AnimationTarget.cpp
  22. 21 1
      gameplay/src/AnimationTarget.h
  23. 12 12
      gameplay/src/AudioBuffer.cpp
  24. 1 1
      gameplay/src/AudioController.cpp
  25. 1 1
      gameplay/src/AudioController.h
  26. 14 8
      gameplay/src/AudioListener.cpp
  27. 9 0
      gameplay/src/AudioListener.h
  28. 44 8
      gameplay/src/AudioSource.cpp
  29. 16 6
      gameplay/src/AudioSource.h
  30. 24 6
      gameplay/src/Base.h
  31. 7 0
      gameplay/src/BoundingBox.cpp
  32. 10 1
      gameplay/src/BoundingBox.h
  33. 20 0
      gameplay/src/Camera.cpp
  34. 10 0
      gameplay/src/Camera.h
  35. 1 1
      gameplay/src/Container.cpp
  36. 1 1
      gameplay/src/FileSystem.cpp
  37. 8 4
      gameplay/src/Font.h
  38. 1 1
      gameplay/src/Form.cpp
  39. 12 1
      gameplay/src/Game.cpp
  40. 43 0
      gameplay/src/Game.h
  41. 14 0
      gameplay/src/Game.inl
  42. 10 0
      gameplay/src/Joint.cpp
  43. 24 0
      gameplay/src/Joint.h
  44. 26 0
      gameplay/src/Light.cpp
  45. 15 5
      gameplay/src/Light.h
  46. 19 0
      gameplay/src/Material.cpp
  47. 11 0
      gameplay/src/Material.h
  48. 80 2
      gameplay/src/MaterialParameter.cpp
  49. 11 2
      gameplay/src/MaterialParameter.h
  50. 1 1
      gameplay/src/MeshBatch.h
  51. 60 1
      gameplay/src/MeshSkin.cpp
  52. 29 0
      gameplay/src/MeshSkin.h
  53. 19 4
      gameplay/src/Model.cpp
  54. 13 3
      gameplay/src/Model.h
  55. 143 24
      gameplay/src/Node.cpp
  56. 187 43
      gameplay/src/Node.h
  57. 3 4
      gameplay/src/ParticleEmitter.cpp
  58. 3 3
      gameplay/src/ParticleEmitter.h
  59. 10 4
      gameplay/src/Pass.cpp
  60. 16 0
      gameplay/src/Pass.h
  61. 27 93
      gameplay/src/PhysicsCharacter.cpp
  62. 5 34
      gameplay/src/PhysicsCharacter.h
  63. 49 9
      gameplay/src/PhysicsCollisionObject.cpp
  64. 65 27
      gameplay/src/PhysicsCollisionObject.h
  65. 155 0
      gameplay/src/PhysicsCollisionShape.cpp
  66. 220 0
      gameplay/src/PhysicsCollisionShape.h
  67. 1 1
      gameplay/src/PhysicsConstraint.cpp
  68. 429 124
      gameplay/src/PhysicsController.cpp
  69. 25 59
      gameplay/src/PhysicsController.h
  70. 60 0
      gameplay/src/PhysicsGhostObject.cpp
  71. 59 0
      gameplay/src/PhysicsGhostObject.h
  72. 2 1
      gameplay/src/PhysicsMotionState.cpp
  73. 7 4
      gameplay/src/PhysicsMotionState.h
  74. 236 376
      gameplay/src/PhysicsRigidBody.cpp
  75. 153 183
      gameplay/src/PhysicsRigidBody.h
  76. 32 67
      gameplay/src/PhysicsRigidBody.inl
  77. 7 0
      gameplay/src/Platform.h
  78. 5 0
      gameplay/src/PlatformAndroid.cpp
  79. 7 0
      gameplay/src/PlatformMacOS.mm
  80. 6 0
      gameplay/src/PlatformQNX.cpp
  81. 6 0
      gameplay/src/PlatformWin32.cpp
  82. 7 0
      gameplay/src/PlatformiOS.mm
  83. 27 4
      gameplay/src/RenderState.cpp
  84. 23 5
      gameplay/src/RenderState.h
  85. 166 1
      gameplay/src/Scene.cpp
  86. 19 0
      gameplay/src/Scene.h
  87. 13 7
      gameplay/src/SceneLoader.cpp
  88. 6 0
      gameplay/src/SceneLoader.h
  89. 1 0
      gameplay/src/SpriteBatch.h
  90. 15 4
      gameplay/src/Technique.cpp
  91. 9 1
      gameplay/src/Technique.h
  92. 9 0
      gameplay/src/Transform.cpp
  93. 3 1
      gameplay/src/Transform.h
  94. 2 1
      gameplay/src/Vector2.cpp
  95. 3 1
      gameplay/src/Vector2.h
  96. 2 1
      gameplay/src/Vector3.cpp
  97. 3 1
      gameplay/src/Vector3.h
  98. 2 1
      gameplay/src/Vector4.cpp
  99. 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
 

+ 7 - 7
README.md

@@ -4,17 +4,17 @@ GamePlay is a open-source, cross-platform 3D native gaming framework making it e
 ## Supported Mobile Platforms
 - BlackBerry PlayBook 2 (using BlackBerry Native SDK 2)
 - Google Android 4 (using Google Android NDK 7)
-- Apple iOS 5 (using Apple XCode 4)
+- Apple iOS 5 (using Apple XCode 4.3)
 
 ## Supported Desktop Platforms
-- Microsoft Windows XP/7 (using Microsoft Visual Studio 2010)
-- Apple MacOS X (using Apple XCode 4)
-    * Both require [Creative OpenAL 1.1] (http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx)
+- 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.

+ 15 - 5
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>";
@@ -349,7 +353,7 @@
 		42475CDD147208A000610A6A /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 0420;
+				LastUpgradeCheck = 0430;
 			};
 			buildConfigurationList = 42475CE0147208A000610A6A /* Build configuration list for PBXProject "gameplay-encoder" */;
 			compatibilityVersion = "Xcode 3.2";
@@ -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;
 		};

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

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
+   LastUpgradeVersion = "0430"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -39,11 +40,12 @@
       </MacroExpansion>
    </TestAction>
    <LaunchAction
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.GDB"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
       launchStyle = "0"
       useCustomWorkingDirectory = "NO"
       buildConfiguration = "Debug"
+      ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
       allowLocationSimulation = "YES">
       <BuildableProductRunnable>

+ 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);
 }
 

+ 22 - 14
gameplay-newproject.sh

@@ -102,6 +102,7 @@ if [[ "$className" == "" ]]; then
 fi
 echo 
 
+
 echo
 echo "7. Enter the project path."
 echo
@@ -127,24 +128,31 @@ if [ -e $projPath ]; then
 	exit -2
 fi
 
-# Generate relative path from project folder to gameplay folder
-if [[ ${projPath:0:1} == "/" ]]; then
-	gpPathAbs=`pwd`
-	common_path=$projPath
-	back=
-	while [ "${gpPathAbs#$common_path}" = "${gpPathAbs}" ]; do
-		common_path=$(dirname $common_path)
-		back="../${back}"
-	done
-	gpPath=${back}${gpPathAbs#$common_path/}
-else
-	gpPath=$projPath
-fi
-
 # Make required source folder directories
+mkdir -p "$projPath"
 mkdir -p "$projPath/src"
 mkdir -p "$projPath/res"
 
+if [[ ${projPath:0:1} != "/" ]]; then
+	currPwd=`pwd`
+	projPath=`cd $projPath; pwd`
+	`cd $currPwd`	
+fi
+
+# Generate relative path from project folder to gameplay folder
+gpPathAbs=`pwd`
+common_path=$projPath
+back=
+while [ "${gpPathAbs#$common_path}" = "${gpPathAbs}" ]; do
+	common_path=$(dirname $common_path)
+	back="../${back}"
+done
+gpPath=${back}${gpPathAbs#$common_path/}
+if [[ ${gpPathAbs} == ${common_path} ]]; then
+	gpPath=${back}
+fi
+
+
 # Below does copy, then uses 'sed' with -i for inplace editing
 # Alternative below uses sed to do a input then output skipping the copy
 # sed "s/TEMPLATE_PROJECT/$projectName/g" "gameplay-template/gameplay-template.vcxproj" > "$projPath/$projName.vcxproj"

+ 12 - 63
gameplay-template/gameplay-template.xcodeproj/project.pbxproj

@@ -45,23 +45,6 @@
 		5BC4E852150F915300CBE1C0 /* textures in Copy GamePlay Bundle Resources */ = {isa = PBXBuildFile; fileRef = 5BC4E84A150F911D00CBE1C0 /* textures */; };
 /* End PBXBuildFile section */
 
-/* Begin PBXContainerItemProxy section */
-		5BC4E7BF150F8B7B00CBE1C0 /* PBXContainerItemProxy */ = {
-			isa = PBXContainerItemProxy;
-			containerPortal = 5BC4E77F150F879E00CBE1C0 /* gameplay.xcodeproj */;
-			proxyType = 2;
-			remoteGlobalIDString = 4234D99A14686C52003031B3;
-			remoteInfo = "gameplay-macos";
-		};
-		5BC4E7C1150F8B7B00CBE1C0 /* PBXContainerItemProxy */ = {
-			isa = PBXContainerItemProxy;
-			containerPortal = 5BC4E77F150F879E00CBE1C0 /* gameplay.xcodeproj */;
-			proxyType = 2;
-			remoteGlobalIDString = 5B04C5CA14BFCFE100EB0071;
-			remoteInfo = "gameplay-ios";
-		};
-/* End PBXContainerItemProxy section */
-
 /* Begin PBXCopyFilesBuildPhase section */
 		5BC4E84D150F912B00CBE1C0 /* Copy GamePlay Bundle Resources */ = {
 			isa = PBXCopyFilesBuildPhase;
@@ -110,15 +93,15 @@
 		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; };
-		5BC4E77F150F879E00CBE1C0 /* gameplay.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = gameplay.xcodeproj; path = /Developer/../Users/bslack/src/git/GamePlay/gameplay/gameplay.xcodeproj; sourceTree = "<absolute>"; };
-		5BC4E849150F911D00CBE1C0 /* shaders */ = {isa = PBXFileReference; lastKnownFileType = folder; name = shaders; path = "/Users/bslack/src/git/GamePlay/gameplay-template/../gameplay/res/shaders"; sourceTree = "<absolute>"; };
-		5BC4E84A150F911D00CBE1C0 /* textures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = textures; path = "/Users/bslack/src/git/GamePlay/gameplay-template/../gameplay/res/textures"; sourceTree = "<absolute>"; };
+		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; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -192,7 +175,7 @@
 			isa = PBXGroup;
 			children = (
 				5BC4E825150F8CE600CBE1C0 /* GamePlay */,
-				5B61613A14CCC3590073B857 /* Mac OS X */,
+				5B61613A14CCC3590073B857 /* MacOSX */,
 				5B61613914CCC3560073B857 /* iOS */,
 			);
 			name = Frameworks;
@@ -235,7 +218,7 @@
 			name = iOS;
 			sourceTree = "<group>";
 		};
-		5B61613A14CCC3590073B857 /* Mac OS X */ = {
+		5B61613A14CCC3590073B857 /* MacOSX */ = {
 			isa = PBXGroup;
 			children = (
 				42C932C01491A0DB0098216A /* Cocoa.framework */,
@@ -243,16 +226,7 @@
 				42C933161491A5EB0098216A /* OpenGL.framework */,
 				42C9331E1491A67F0098216A /* OpenAL.framework */,
 			);
-			name = "Mac OS X";
-			sourceTree = "<group>";
-		};
-		5BC4E7BB150F8B7B00CBE1C0 /* Products */ = {
-			isa = PBXGroup;
-			children = (
-				5BC4E7C0150F8B7B00CBE1C0 /* libgameplay.a */,
-				5BC4E7C2150F8B7B00CBE1C0 /* libgameplay.a */,
-			);
-			name = Products;
+			name = "MacOSX";
 			sourceTree = "<group>";
 		};
 		5BC4E825150F8CE600CBE1C0 /* GamePlay */ = {
@@ -324,12 +298,6 @@
 			mainGroup = 42C932B11491A0DB0098216A;
 			productRefGroup = 42C932BD1491A0DB0098216A /* Products */;
 			projectDirPath = "";
-			projectReferences = (
-				{
-					ProductGroup = 5BC4E7BB150F8B7B00CBE1C0 /* Products */;
-					ProjectRef = 5BC4E77F150F879E00CBE1C0 /* gameplay.xcodeproj */;
-				},
-			);
 			projectRoot = "";
 			targets = (
 				42C932BB1491A0DB0098216A /* TEMPLATE_PROJECT-MacOSX */,
@@ -338,23 +306,6 @@
 		};
 /* End PBXProject section */
 
-/* Begin PBXReferenceProxy section */
-		5BC4E7C0150F8B7B00CBE1C0 /* libgameplay.a */ = {
-			isa = PBXReferenceProxy;
-			fileType = archive.ar;
-			path = libgameplay.a;
-			remoteRef = 5BC4E7BF150F8B7B00CBE1C0 /* PBXContainerItemProxy */;
-			sourceTree = BUILT_PRODUCTS_DIR;
-		};
-		5BC4E7C2150F8B7B00CBE1C0 /* libgameplay.a */ = {
-			isa = PBXReferenceProxy;
-			fileType = archive.ar;
-			path = libgameplay.a;
-			remoteRef = 5BC4E7C1150F8B7B00CBE1C0 /* PBXContainerItemProxy */;
-			sourceTree = BUILT_PRODUCTS_DIR;
-		};
-/* End PBXReferenceProxy section */
-
 /* Begin PBXResourcesBuildPhase section */
 		42C932BA1491A0DB0098216A /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
@@ -497,7 +448,6 @@
 					"\"GAMEPLAY_PATH/external-deps/oggvorbis/lib/macos\"",
 				);
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SDKROOT = "";
 				USER_HEADER_SEARCH_PATHS = "";
 				WRAPPER_EXTENSION = app;
 			};
@@ -524,7 +474,6 @@
 					"\"GAMEPLAY_PATH/external-deps/oggvorbis/lib/macos\"",
 				);
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SDKROOT = "";
 				USER_HEADER_SEARCH_PATHS = "";
 				WRAPPER_EXTENSION = app;
 			};

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

+ 8 - 0
gameplay/gameplay.vcxproj

@@ -63,10 +63,12 @@
     <ClCompile Include="src\ParticleEmitter.cpp" />
     <ClCompile Include="src\PhysicsCharacter.cpp" />
     <ClCompile Include="src\PhysicsCollisionObject.cpp" />
+    <ClCompile Include="src\PhysicsCollisionShape.cpp" />
     <ClCompile Include="src\PhysicsConstraint.cpp" />
     <ClCompile Include="src\PhysicsController.cpp" />
     <ClCompile Include="src\PhysicsFixedConstraint.cpp" />
     <ClCompile Include="src\PhysicsGenericConstraint.cpp" />
+    <ClCompile Include="src\PhysicsGhostObject.cpp" />
     <ClCompile Include="src\PhysicsHingeConstraint.cpp" />
     <ClCompile Include="src\PhysicsMotionState.cpp" />
     <ClCompile Include="src\PhysicsRigidBody.cpp" />
@@ -152,10 +154,12 @@
     <ClInclude Include="src\ParticleEmitter.h" />
     <ClInclude Include="src\PhysicsCharacter.h" />
     <ClInclude Include="src\PhysicsCollisionObject.h" />
+    <ClInclude Include="src\PhysicsCollisionShape.h" />
     <ClInclude Include="src\PhysicsConstraint.h" />
     <ClInclude Include="src\PhysicsController.h" />
     <ClInclude Include="src\PhysicsFixedConstraint.h" />
     <ClInclude Include="src\PhysicsGenericConstraint.h" />
+    <ClInclude Include="src\PhysicsGhostObject.h" />
     <ClInclude Include="src\PhysicsHingeConstraint.h" />
     <ClInclude Include="src\PhysicsMotionState.h" />
     <ClInclude Include="src\PhysicsRigidBody.h" />
@@ -191,6 +195,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" />

+ 25 - 1
gameplay/gameplay.vcxproj.filters

@@ -270,6 +270,12 @@
     <ClCompile Include="src\PhysicsCollisionObject.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\PhysicsGhostObject.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\PhysicsCollisionShape.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -533,6 +539,12 @@
     <ClInclude Include="src\TimeListener.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\PhysicsGhostObject.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\PhysicsCollisionShape.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
@@ -622,6 +634,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">
@@ -667,4 +691,4 @@
       <Filter>src</Filter>
     </None>
   </ItemGroup>
-</Project>
+</Project>

+ 29 - 15
gameplay/gameplay.xcodeproj/project.pbxproj

@@ -308,6 +308,10 @@
 		5B2BC773151251EB00D176CD /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B2BC76C151251EB00D176CD /* UIKit.framework */; };
 		5BB0823D14C6FEC40019975F /* Mouse.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BB0823C14C6FEC40019975F /* Mouse.h */; };
 		5BB0823E14C6FEC40019975F /* Mouse.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BB0823C14C6FEC40019975F /* Mouse.h */; };
+		5BBE143E1513E400003FB362 /* PhysicsGhostObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BBE143C1513E400003FB362 /* PhysicsGhostObject.cpp */; };
+		5BBE143F1513E400003FB362 /* PhysicsGhostObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BBE143C1513E400003FB362 /* PhysicsGhostObject.cpp */; };
+		5BBE14401513E400003FB362 /* PhysicsGhostObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BBE143D1513E400003FB362 /* PhysicsGhostObject.h */; };
+		5BBE14411513E400003FB362 /* PhysicsGhostObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BBE143D1513E400003FB362 /* PhysicsGhostObject.h */; };
 		5BC4E73F150F843D00CBE1C0 /* AbsoluteLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BD52634150F822A004C9099 /* AbsoluteLayout.cpp */; };
 		5BC4E740150F843D00CBE1C0 /* AbsoluteLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BD52635150F822A004C9099 /* AbsoluteLayout.h */; };
 		5BC4E741150F843D00CBE1C0 /* Button.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BD52636150F822A004C9099 /* Button.cpp */; };
@@ -542,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>"; };
@@ -565,6 +569,8 @@
 		5BB0823814C6FEB10019975F /* gameplay-main-android.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gameplay-main-android.cpp"; path = "src/gameplay-main-android.cpp"; sourceTree = SOURCE_ROOT; };
 		5BB0823914C6FEB10019975F /* PlatformAndroid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PlatformAndroid.cpp; path = src/PlatformAndroid.cpp; sourceTree = SOURCE_ROOT; };
 		5BB0823C14C6FEC40019975F /* Mouse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Mouse.h; path = src/Mouse.h; sourceTree = SOURCE_ROOT; };
+		5BBE143C1513E400003FB362 /* PhysicsGhostObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PhysicsGhostObject.cpp; path = src/PhysicsGhostObject.cpp; sourceTree = SOURCE_ROOT; };
+		5BBE143D1513E400003FB362 /* PhysicsGhostObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicsGhostObject.h; path = src/PhysicsGhostObject.h; sourceTree = SOURCE_ROOT; };
 		5BC4E7D4150F8C3C00CBE1C0 /* res */ = {isa = PBXFileReference; lastKnownFileType = folder; path = res; sourceTree = "<group>"; };
 		5BD52634150F822A004C9099 /* AbsoluteLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AbsoluteLayout.cpp; path = src/AbsoluteLayout.cpp; sourceTree = SOURCE_ROOT; };
 		5BD52635150F822A004C9099 /* AbsoluteLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AbsoluteLayout.h; path = src/AbsoluteLayout.h; sourceTree = SOURCE_ROOT; };
@@ -791,6 +797,8 @@
 				42CD0E09147D8FF50000361E /* PhysicsGenericConstraint.inl */,
 				42CD0E0A147D8FF50000361E /* PhysicsHingeConstraint.cpp */,
 				42CD0E0B147D8FF50000361E /* PhysicsHingeConstraint.h */,
+				5BBE143C1513E400003FB362 /* PhysicsGhostObject.cpp */,
+				5BBE143D1513E400003FB362 /* PhysicsGhostObject.h */,
 				42CD0E0C147D8FF50000361E /* PhysicsMotionState.cpp */,
 				42CD0E0D147D8FF50000361E /* PhysicsMotionState.h */,
 				42CD0E0E147D8FF50000361E /* PhysicsRigidBody.cpp */,
@@ -876,7 +884,7 @@
 				42CD0DA9147D8EA80000361E /* libvorbisenc.a */,
 				42CD0DAA147D8EA80000361E /* libvorbisfile.a */,
 				42CCD555146EC1EB00353661 /* libpng.a */,
-				5B5ADCE114C22DC700AC6109 /* Mac OS X */,
+				5B5ADCE114C22DC700AC6109 /* MacOSX */,
 				5B5ADCE014C22DBE00AC6109 /* iOS */,
 			);
 			name = Libraries;
@@ -885,8 +893,8 @@
 		42CCD4AF146D811D00353661 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
-				5B04C5FE14BFE52F00EB0071 /* Mac OS X */,
 				5B04C5FD14BFE52300EB0071 /* iOS */,
+				5B04C5FE14BFE52F00EB0071 /* MacOSX */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -906,7 +914,7 @@
 			name = iOS;
 			sourceTree = "<group>";
 		};
-		5B04C5FE14BFE52F00EB0071 /* Mac OS X */ = {
+		5B04C5FE14BFE52F00EB0071 /* MacOSX */ = {
 			isa = PBXGroup;
 			children = (
 				5B2BC7631512516B00D176CD /* libz.dylib */,
@@ -915,7 +923,7 @@
 				5B2BC75E1512514500D176CD /* OpenGL.framework */,
 				4234D99D14686C52003031B3 /* Cocoa.framework */,
 			);
-			name = "Mac OS X";
+			name = MacOSX;
 			sourceTree = "<group>";
 		};
 		5B5ADCE014C22DBE00AC6109 /* iOS */ = {
@@ -932,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 */
@@ -1035,6 +1043,7 @@
 				5BD52669150F822A004C9099 /* VerticalLayout.h in Headers */,
 				5BD52671150F8258004C9099 /* PhysicsCharacter.h in Headers */,
 				5BD52675150F8258004C9099 /* PhysicsCollisionObject.h in Headers */,
+				5BBE14401513E400003FB362 /* PhysicsGhostObject.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1129,6 +1138,7 @@
 				5BC4E754150F843D00CBE1C0 /* TextBox.h in Headers */,
 				5BC4E756150F843D00CBE1C0 /* Theme.h in Headers */,
 				5BC4E758150F843D00CBE1C0 /* VerticalLayout.h in Headers */,
+				5BBE14411513E400003FB362 /* PhysicsGhostObject.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1175,7 +1185,7 @@
 		4234D98C14686BB6003031B3 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 0420;
+				LastUpgradeCheck = 0430;
 			};
 			buildConfigurationList = 4234D98F14686BB6003031B3 /* Build configuration list for PBXProject "gameplay" */;
 			compatibilityVersion = "Xcode 3.2";
@@ -1281,6 +1291,7 @@
 				5BD52668150F822A004C9099 /* VerticalLayout.cpp in Sources */,
 				5BD5266F150F8258004C9099 /* PhysicsCharacter.cpp in Sources */,
 				5BD52673150F8258004C9099 /* PhysicsCollisionObject.cpp in Sources */,
+				5BBE143E1513E400003FB362 /* PhysicsGhostObject.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1369,6 +1380,7 @@
 				5BC4E753150F843D00CBE1C0 /* TextBox.cpp in Sources */,
 				5BC4E755150F843D00CBE1C0 /* Theme.cpp in Sources */,
 				5BC4E757150F843D00CBE1C0 /* VerticalLayout.cpp in Sources */,
+				5BBE143F1513E400003FB362 /* PhysicsGhostObject.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1422,6 +1434,7 @@
 					"../external-deps/libpng/include",
 					"../external-deps/bullet/include",
 					"../external-deps/oggvorbis/include",
+					./gameplay,
 				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
@@ -1463,6 +1476,7 @@
 					"../external-deps/libpng/include",
 					"../external-deps/bullet/include",
 					"../external-deps/oggvorbis/include",
+					./gameplay,
 				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",

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;

+ 29 - 0
gameplay/src/AnimationTarget.cpp

@@ -2,6 +2,7 @@
 #include "AnimationTarget.h"
 #include "Animation.h"
 #include "Game.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -118,6 +119,34 @@ void AnimationTarget::deleteChannel(Animation::Channel* channel)
     }
 }
 
+void AnimationTarget::cloneInto(AnimationTarget* target, NodeCloneContext &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 clone context.
+            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

@@ -9,6 +9,7 @@ namespace gameplay
 
 class Animation;
 class AnimationValue;
+class NodeCloneContext;
 
 /**
  * Defines an interface allowing animation to target
@@ -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, NodeCloneContext &context) const;
+
     TargetType _targetType;             // The type of target this is.
 
     unsigned 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;

+ 14 - 8
gameplay/src/AudioListener.cpp

@@ -1,27 +1,24 @@
 #include "Base.h"
 #include "Node.h"
 #include "AudioListener.h"
+#include "Game.h"
 
 namespace gameplay
 {
 
-static AudioListener* __audioListenerInstance = NULL;
-
 AudioListener::AudioListener()
     : _gain(1.0f), _camera(NULL)
-
 {
-    assert(__audioListenerInstance == NULL);
-    __audioListenerInstance = this;
 }
 
 AudioListener::~AudioListener()
 {
+    SAFE_RELEASE(_camera);
 }
 
 AudioListener* AudioListener::getInstance()
 {
-    return __audioListenerInstance;
+    return Game::getInstance()->getAudioListener();
 }
 
 float AudioListener::getGain() const 
@@ -54,6 +51,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 +108,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);
     }
 }
 

+ 9 - 0
gameplay/src/AudioListener.h

@@ -15,6 +15,7 @@ class Camera;
 class AudioListener : public Transform::Listener
 {
     friend class AudioController;
+    friend class Game;
 
 public:
 
@@ -67,6 +68,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.
      *

+ 44 - 8
gameplay/src/AudioSource.cpp

@@ -4,6 +4,7 @@
 #include "AudioController.h"
 #include "AudioSource.h"
 #include "Game.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -26,37 +27,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 +160,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 +455,8 @@ void AudioSource::setNode(Node* node)
         if (_node)
         {
             _node->addListener(this);
+            // Update the audio source position.
+            transformChanged(_node, 0);
         }
     }
 }
@@ -461,7 +464,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 +482,36 @@ void AudioSource::transformChanged(Transform* transform, long cookie)
 #endif
 }
 
+AudioSource* AudioSource::clone(NodeCloneContext &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
+    _buffer->addRef();
+    audioClone->setLooped(isLooped());
+    audioClone->setGain(getGain());
+    audioClone->setPitch(getPitch());
+    audioClone->setVelocity(getVelocity());
+    if (Node* node = getNode())
+    {
+        Node* clonedNode = context.findClonedNode(node);
+        if (clonedNode)
+        {
+            audioClone->setNode(clonedNode);
+        }
+    }
+    return audioClone;
+}
+
 }

+ 16 - 6
gameplay/src/AudioSource.h

@@ -10,6 +10,7 @@ namespace gameplay
 
 class AudioBuffer;
 class Node;
+class NodeCloneContext;
 
 /**
  *  Declares an audio source in 3D space.
@@ -176,15 +177,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(NodeCloneContext &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

+ 24 - 6
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
@@ -144,7 +143,7 @@ extern void printError(const char* format, ...);
 #define MATH_LOG2E                  1.442695040888963387f
 #define MATH_PI                     3.14159265358979323846f
 #define MATH_PIOVER2                1.57079632679489661923f
-#define MATH_PIOVER4                M_PI_4
+#define MATH_PIOVER4                0.785398163397448309616f
 #define MATH_PIX2                   6.28318530717958647693f
 #define MATH_EPSILON                0.000001f
 #define MATH_CLAMP(x, lo, hi)       ((x < lo) ? lo : ((x > hi) ? hi : x))
@@ -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

+ 7 - 0
gameplay/src/BoundingBox.cpp

@@ -55,6 +55,13 @@ void BoundingBox::getCorners(Vector3* dst) const
     dst[7].set(min.x, max.y, min.z);
 }
 
+Vector3 BoundingBox::getCenter() const
+{
+	Vector3 center;
+	getCenter(&center);
+	return center;
+}
+
 void BoundingBox::getCenter(Vector3* dst) const
 {
     dst->set(min, max);

+ 10 - 1
gameplay/src/BoundingBox.h

@@ -53,6 +53,15 @@ public:
      */
     static const BoundingBox& empty();
 
+	/**
+     * Gets the center point of the bounding box.
+     *
+     * This method computes the center point of the box from its min and max.
+     *
+     * @return The center point of the bounding box.
+     */
+    Vector3 getCenter() const;
+
     /**
      * Gets the center point of the bounding box.
      *
@@ -71,7 +80,7 @@ public:
      * specify the far face starting at the upper left point when looking towards the origin from the negative
      * z-axis in a counter-clockwise fashion.
      *
-     * @param dst The array to store the corners in. Must be size 6.
+     * @param dst The array to store the corners in. Must be size 8.
      */
     void getCorners(Vector3* dst) const;
 

+ 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(NodeCloneContext &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;

+ 10 - 0
gameplay/src/Camera.h

@@ -10,6 +10,7 @@ namespace gameplay
 {
 
 class Node;
+class NodeCloneContext;
 
 /**
  * Defines a camera which acts as a view of a scene to be rendered.
@@ -245,6 +246,15 @@ private:
      */
     virtual ~Camera();
 
+    /**
+     * Clones the camera and returns a new camera.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created camera.
+     */
+    Camera* clone(NodeCloneContext &context) const;
+
     /**
      * @see Transform::Listener::transformChanged
      */

+ 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();

+ 12 - 1
gameplay/src/Game.cpp

@@ -17,7 +17,7 @@ Game::Game()
     : _initialized(false), _state(UNINITIALIZED), 
       _frameLastFPS(0), _frameCount(0), _frameRate(0), 
       _clearDepth(1.0f), _clearStencil(0),
-      _animationController(NULL), _audioController(NULL)
+      _animationController(NULL), _audioController(NULL), _physicsController(NULL), _audioListener(NULL)
 {
     assert(__gameInstance == NULL);
     __gameInstance = this;
@@ -124,6 +124,8 @@ void Game::shutdown()
         _physicsController->finalize();
         SAFE_DELETE(_physicsController);
 
+        SAFE_DELETE(_audioListener);
+
         RenderState::finalize();
     }
 
@@ -253,6 +255,15 @@ void Game::clear(ClearFlags flags, const Vector4& clearColor, float clearDepth,
     glClear(bits);
 }
 
+AudioListener* Game::getAudioListener()
+{
+    if (_audioListener == NULL)
+    {
+        _audioListener = new AudioListener();
+    }
+    return _audioListener;
+}
+
 void Game::menu()
 {
 }

+ 43 - 0
gameplay/src/Game.h

@@ -9,6 +9,7 @@
 #include "AudioController.h"
 #include "AnimationController.h"
 #include "PhysicsController.h"
+#include "AudioListener.h"
 #include "Vector4.h"
 #include "TimeListener.h"
 
@@ -184,6 +185,13 @@ public:
      */
     inline PhysicsController* getPhysicsController() const;
 
+    /**
+     * Gets the audio listener for 3D audio.
+     * 
+     * @return The audio listener for this game.
+     */
+    AudioListener* getAudioListener();
+
     /**
      * Menu callback on menu events.
      */
@@ -383,9 +391,44 @@ private:
     AnimationController* _animationController;  // Controls the scheduling and running of animations.
     AudioController* _audioController;          // Controls audio sources that are playing in the game.
     PhysicsController* _physicsController;      // Controls the simulation of a physics scene and entities.
+    AudioListener* _audioListener;              // The audio listener in 3D space.
     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(NodeCloneContext &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(NodeCloneContext &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(NodeCloneContext &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

@@ -8,6 +8,7 @@ namespace gameplay
 {
 
 class Node;
+class NodeCloneContext;
 
 /**
  * Defines a light.
@@ -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(NodeCloneContext &context) const;
+
     Light::Type _type;
     union
     {

+ 19 - 0
gameplay/src/Material.cpp

@@ -5,6 +5,7 @@
 #include "Technique.h"
 #include "Pass.h"
 #include "Properties.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -125,6 +126,24 @@ Material* Material::create(const char* vshPath, const char* fshPath, const char*
     return material;
 }
 
+Material* Material::clone(NodeCloneContext &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();

+ 11 - 0
gameplay/src/Material.h

@@ -8,6 +8,8 @@
 namespace gameplay
 {
 
+class NodeCloneContext;
+
 /**
  * Defines a material for an object to be rendered.
  *
@@ -68,6 +70,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(NodeCloneContext &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.

+ 19 - 4
gameplay/src/Model.cpp

@@ -4,6 +4,7 @@
 #include "Scene.h"
 #include "Technique.h"
 #include "Pass.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -190,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;
 }
@@ -209,7 +210,8 @@ void Model::setSkin(MeshSkin* skin)
 
         // Assign the new skin
         _skin = skin;
-        _skin->_model = this;
+        if (_skin)
+            _skin->_model = this;
     }
 }
 
@@ -357,6 +359,19 @@ void Model::validatePartCount()
     }
 }
 
+Model* Model::clone(NodeCloneContext &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)

+ 13 - 3
gameplay/src/Model.h

@@ -4,13 +4,14 @@
 #include "Mesh.h"
 #include "MeshSkin.h"
 #include "Material.h"
-#include "Node.h"
 
 namespace gameplay
 {
 
 class Package;
 class MeshSkin;
+class Node;
+class NodeCloneContext;
 
 /**
  * Defines a Model which is an instance of a Mesh that can be drawn
@@ -116,14 +117,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 +178,15 @@ private:
 
     void validatePartCount();
 
+    /**
+     * Clones the model and returns a new model.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The new cloned model.
+     */
+    Model* clone(NodeCloneContext &context);
+
     Mesh* _mesh;
     Material* _material;
     unsigned int _partCount;

+ 143 - 24
gameplay/src/Node.cpp

@@ -2,6 +2,10 @@
 #include "Node.h"
 #include "Scene.h"
 #include "Joint.h"
+#include "PhysicsRigidBody.h"
+#include "PhysicsGhostObject.h"
+#include "PhysicsCharacter.h"
+#include "Game.h"
 
 #define NODE_DIRTY_WORLD 1
 #define NODE_DIRTY_BOUNDS 2
@@ -12,8 +16,8 @@ namespace gameplay
 
 Node::Node(const char* id)
     : _scene(NULL), _firstChild(NULL), _nextSibling(NULL), _prevSibling(NULL), _parent(NULL), _childCount(NULL),
-    _camera(NULL), _light(NULL), _model(NULL), _form(NULL), _audioSource(NULL), _particleEmitter(NULL), _physicsRigidBody(NULL), 
-    _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true)
+    _camera(NULL), _light(NULL), _model(NULL), _form(NULL), _audioSource(NULL), _particleEmitter(NULL),
+	_collisionObject(NULL), _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true)
 {
     if (id)
     {
@@ -21,11 +25,6 @@ Node::Node(const char* id)
     }
 }
 
-Node::Node(const Node& node)
-{
-    // hidden
-}
-
 Node::~Node()
 {
     removeAllChildren();
@@ -45,7 +44,7 @@ Node::~Node()
     SAFE_RELEASE(_audioSource);
     SAFE_RELEASE(_particleEmitter);
     SAFE_RELEASE(_form);
-    SAFE_DELETE(_physicsRigidBody);
+    SAFE_DELETE(_collisionObject);
 }
 
 Node* Node::create(const char* id)
@@ -201,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);
     
@@ -231,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);
     
@@ -295,7 +294,7 @@ const Matrix& Node::getWorldMatrix() const
         // If we have a parent, multiply our parent world transform by our local
         // transform to obtain our final resolved world transform.
         Node* parent = getParent();
-        if (parent && (!_physicsRigidBody || _physicsRigidBody->isKinematic()) )
+		if (parent && (!_collisionObject || _collisionObject->isKinematic()))
         {
             Matrix::multiply(parent->getWorldMatrix(), getMatrix(), &_world);
         }
@@ -702,6 +701,70 @@ const BoundingSphere& Node::getBoundingSphere() const
     return _bounds;
 }
 
+
+Node* Node::clone() const
+{
+    NodeCloneContext context;
+    return cloneRecursive(context);
+}
+
+Node* Node::cloneSingleNode(NodeCloneContext &context) const
+{
+    Node* copy = Node::create(getId());
+    context.registerClonedNode(this, copy);
+    cloneInto(copy, context);
+    return copy;
+}
+
+Node* Node::cloneRecursive(NodeCloneContext &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, NodeCloneContext &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;
@@ -752,32 +815,88 @@ void Node::setParticleEmitter(ParticleEmitter* emitter)
     }
 }
 
-PhysicsRigidBody* Node::getRigidBody() const
+PhysicsCollisionObject* Node::getCollisionObject() const
+{
+    return _collisionObject;
+}
+
+PhysicsCollisionObject* Node::setCollisionObject(PhysicsCollisionObject::Type type, const PhysicsCollisionShape::Definition& shape, PhysicsRigidBody::Parameters* rigidBodyParameters)
+{
+	SAFE_DELETE(_collisionObject);
+
+	switch (type)
+	{
+	case PhysicsCollisionObject::RIGID_BODY:
+		{
+			_collisionObject = new PhysicsRigidBody(this, shape, rigidBodyParameters ? *rigidBodyParameters : PhysicsRigidBody::Parameters());
+		}
+		break;
+
+	case PhysicsCollisionObject::GHOST_OBJECT:
+		{
+			_collisionObject = new PhysicsGhostObject(this, shape);
+		}
+		break;
+
+	case PhysicsCollisionObject::CHARACTER:
+		{
+			_collisionObject = new PhysicsCharacter(this, shape);
+		}
+		break;
+	}
+
+	return _collisionObject;
+}
+
+PhysicsCollisionObject* Node::setCollisionObject(const char* filePath)
 {
-    return _physicsRigidBody;
+    SAFE_DELETE(_collisionObject);
+
+	// TODO: Support other collision object types from file
+    _collisionObject = PhysicsRigidBody::create(this, filePath);
+
+	return _collisionObject;
 }
 
-void Node::setRigidBody(PhysicsRigidBody::ShapeType type, float mass, float friction,
-        float restitution, float linearDamping, float angularDamping)
+PhysicsCollisionObject* Node::setCollisionObject(Properties* properties)
+{
+    SAFE_DELETE(_collisionObject);
+
+    _collisionObject = PhysicsRigidBody::create(this, properties);
+
+	return _collisionObject;
+}
+
+NodeCloneContext::NodeCloneContext()
 {
-    SAFE_DELETE(_physicsRigidBody);
     
-    if (type != PhysicsRigidBody::SHAPE_NONE)
-        _physicsRigidBody = new PhysicsRigidBody(this, type, mass, friction, restitution, linearDamping, angularDamping);
 }
 
-void Node::setRigidBody(const char* filePath)
+NodeCloneContext::~NodeCloneContext()
 {
-    SAFE_DELETE(_physicsRigidBody);
 
-    _physicsRigidBody = PhysicsRigidBody::create(this, filePath);
 }
 
-void Node::setRigidBody(Properties* properties)
+Animation* NodeCloneContext::findClonedAnimation(const Animation* animation)
 {
-    SAFE_DELETE(_physicsRigidBody);
+    AnimationMap::iterator it = _clonedAnimations.find(animation);
+    return it != _clonedAnimations.end() ? it->second : NULL;
+}
 
-    _physicsRigidBody = PhysicsRigidBody::create(this, properties);
+void NodeCloneContext::registerClonedAnimation(const Animation* original, Animation* clone)
+{
+    _clonedAnimations[original] = clone;
+}
+
+Node* NodeCloneContext::findClonedNode(const Node* node)
+{
+    NodeMap::iterator it = _clonedNodes.find(node);
+    return it != _clonedNodes.end() ? it->second : NULL;
+}
+
+void NodeCloneContext::registerClonedNode(const Node* original, Node* clone)
+{
+    _clonedNodes[original] = clone;
 }
 
 }

+ 187 - 43
gameplay/src/Node.h

@@ -9,6 +9,8 @@
 #include "AudioSource.h"
 #include "ParticleEmitter.h"
 #include "PhysicsRigidBody.h"
+#include "PhysicsCollisionObject.h"
+#include "PhysicsCollisionShape.h"
 #include "BoundingBox.h"
 
 namespace gameplay
@@ -121,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.
@@ -145,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.
@@ -365,44 +367,68 @@ public:
      */
     void setParticleEmitter(ParticleEmitter* emitter);
 
-    /**
-     * Returns the pointer to this node's physics rigid body or NULL.
-     *
-     * @return The pointer to this node's physics rigid body or NULL.
-     */
-    PhysicsRigidBody* getRigidBody() const;
-
-    /**
-     * Sets (or disables) the physics rigid body for this node.
+	/**
+	 * Returns the pointer to this node's physics collision object.
+	 *
+	 * The type of the returned collision object can be queried using
+	 * the PhysicsCollisionObject::getType() method.
+	 *
+	 * @return The pointer to this node's physics collision object.
+	 */
+	PhysicsCollisionObject* getCollisionObject() const;
+
+	/**
+	 * Sets (or disables) the physics collision object for this node.
+	 *
+	 * The supported collision object types include rigid bodies, ghost objects and 
+	 * characters.
+	 *
+	 * Rigid bodies are used to represent most physical objects in a game. The important
+	 * feature of rigid bodies is that they can be simulated by the physics system as other
+	 * rigid bodies or collision objects collide with them. To support this physics simulation,
+	 * rigid bodies require additional parameters, such as mass, friction and restitution to
+	 * define their physical features. These parameters can be passed into the
+	 * 'rigidBodyParameters' parameter.
+	 *
+	 * Ghost objects are a simple type of collision object that are not simulated. By default
+	 * they pass through other objects in the scene without affecting them. Ghost objects do
+	 * receive collision events however, which makes them useful for representing non-simulated
+	 * entities in a game that still require collision events, such as volumetric triggers, 
+	 * power-ups, etc.
+	 *
+	 * Characters are an extention of ghost objects which provide a number of additional features
+	 * for animating and moving characters within a game. Characters are represented as ghost
+	 * objects instead of rigid bodies to allow more direct control over character movement,
+	 * since attempting to model a physics character with a simulated rigid body usually results
+	 * in unresponse and unpredictable character movement. Unlike normal ghost objects,
+	 * characters to react to other characters and rigid bodies in the world. Characters react
+	 * to gravity and collide (and respond) with rigid bodies to allow them to walk on the ground,
+	 * slide along walls and walk up/down slopes and stairs.
+	 *
+	 * @param type The type of the collision object to set; to disable the physics
+	 *		collision object, pass PhysicsCollisionObject::NONE.
+	 * @param shape Definition of a physics collision shape to be used for this collision object.
+	 *		Use the static shape methods on the PhysicsCollisionShape class to specificy a shape
+	 *		definition, such as PhysicsCollisionShape::box().
+	 * @param rigidBodyParameters If type is PhysicsCollisionObject::RIGID_BODY, this
+	 *		must point to a valid rigid body parameters object containing information
+	 *		about the rigid body; otherwise, this parmater may be NULL.
+	 */
+	PhysicsCollisionObject* setCollisionObject(PhysicsCollisionObject::Type type, const PhysicsCollisionShape::Definition& shape, PhysicsRigidBody::Parameters* rigidBodyParameters = NULL);
+
+    /**
+     * Sets the physics collision object for this node using the definition in the given file.
      * 
-     * Note: This is only allowed for nodes that have a model attached to them.
-     *
-     * @param type The type of rigid body to set; to disable the physics rigid
-     *      body, pass PhysicsRigidBody#SHAPE_NONE.
-     * @param mass The mass of the rigid body, in kilograms.
-     * @param friction The friction of the rigid body (between 0.0 and 1.0, where 0.0 is
-     *      minimal friction and 1.0 is maximal friction).
-     * @param restitution The restitution of the rigid body (this controls the bounciness of
-     *      the rigid body; between 0.0 and 1.0, where 0.0 is minimal bounciness and 1.0 is maximal bounciness).
-     * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
-     * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
+     * @param filePath The path to the file that contains the collision object definition.
      */
-    void setRigidBody(PhysicsRigidBody::ShapeType type, float mass = 0.0f, float friction = 0.5f,
-        float restitution = 0.0f, float linearDamping = 0.0f, float angularDamping = 0.0f);
+    PhysicsCollisionObject* setCollisionObject(const char* filePath);
 
     /**
-     * Sets the physics rigid body for this node using the rigid body definition in the given file.
+     * Sets the physics collision object for this node from the given properties object.
      * 
-     * @param filePath The path to the file that contains the rigid body definition.
+     * @param properties The properties object defining the collision ojbect.
      */
-    void setRigidBody(const char* filePath);
-
-    /**
-     * Sets the physics rigid body for this node from the given properties object.
-     * 
-     * @param properties The properties object defining the rigid body (must have namespace equal to 'rigidbody').
-     */
-    void setRigidBody(Properties* properties);
+    PhysicsCollisionObject* setCollisionObject(Properties* properties);
 
     /**
      * Returns the bounding sphere for the Node, in world space.
@@ -425,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:
 
     /**
@@ -433,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(NodeCloneContext &context) const;
+
+    /**
+     * Recursively clones this node and its children.
+     * 
+     * @param context The clone context.
+     * 
+     * @return The newly created node.
+     */
+    Node* cloneRecursive(NodeCloneContext &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, NodeCloneContext &context) const;
 
     /**
      * Removes this node from its parent.
@@ -452,6 +506,9 @@ protected:
      */
     void transformChanged();
 
+    /**
+     * Called when this Node's hierarchy changes.
+     */
     void hierarchyChanged();
 
     /**
@@ -459,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;
@@ -472,13 +543,86 @@ protected:
     Form* _form;
     AudioSource* _audioSource;
     ParticleEmitter* _particleEmitter;
-    PhysicsRigidBody* _physicsRigidBody;
+    PhysicsCollisionObject* _collisionObject;
     mutable Matrix _world;
     mutable int _dirtyBits;
     bool _notifyHierarchyChanged;
     mutable BoundingSphere _bounds;
 };
 
+/**
+ * NodeCloneContext represents the context data that is kept when cloning a node.
+ * 
+ * The NodeCloneContext is used to make sure objects don't get cloned twice.
+ */
+class NodeCloneContext
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    NodeCloneContext();
+
+    /**
+     * Destructor.
+     */
+    ~NodeCloneContext();
+
+    /**
+     * 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.
+     */
+    NodeCloneContext(const NodeCloneContext&);
+
+    /**
+     * Hidden copy assignment operator.
+     */
+    NodeCloneContext& operator=(const NodeCloneContext&);
+
+private:
+    typedef std::map<const Animation*, Animation*> AnimationMap;
+    typedef std::map<const Node*, Node*> NodeMap;
+
+    AnimationMap _clonedAnimations;
+    NodeMap _clonedNodes;
+};
+
 }
 
 #endif

+ 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);
 

+ 10 - 4
gameplay/src/Pass.cpp

@@ -2,6 +2,7 @@
 #include "Pass.h"
 #include "Technique.h"
 #include "Material.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -14,10 +15,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 +80,13 @@ void Pass::unbind()
     }
 }
 
+Pass* Pass::clone(Technique* technique, NodeCloneContext &context) const
+{
+    Effect* effect = getEffect();
+    effect->addRef();
+    Pass* pass = new Pass(getId(), technique, effect);
+    RenderState::cloneInto(pass, context);
+    return pass;
+}
+
 }

+ 16 - 0
gameplay/src/Pass.h

@@ -8,6 +8,7 @@ namespace gameplay
 {
 
 class Technique;
+class NodeCloneContext;
 
 /**
  * Defines a pass for an object to be rendered.
@@ -87,6 +88,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, NodeCloneContext &context) const;
+
     std::string _id;
     Technique* _technique;
     Effect* _effect;

+ 27 - 93
gameplay/src/PhysicsCharacter.cpp

@@ -63,60 +63,25 @@ protected:
 	btScalar _minSlopeDot;
 };
 
-PhysicsCharacter::PhysicsCharacter(Node* node, float radius, float height, const Vector3 center)
-    : _node(node), _motionState(NULL), _moveVelocity(0,0,0), _forwardVelocity(0.0f), _rightVelocity(0.0f),
+PhysicsCharacter::PhysicsCharacter(Node* node, const PhysicsCollisionShape::Definition& shape)
+    : PhysicsGhostObject(node, shape), _moveVelocity(0,0,0), _forwardVelocity(0.0f), _rightVelocity(0.0f),
     _fallVelocity(0, 0, 0), _currentVelocity(0,0,0), _normalizedVelocity(0,0,0),
     _colliding(false), _collisionNormal(0,0,0), _currentPosition(0,0,0),
-    _ghostObject(NULL), _collisionShape(NULL), _ignoreTransformChanged(0),
     _stepHeight(0.2f), _slopeAngle(0.0f), _cosSlopeAngle(0.0f), _physicsEnabled(true)
 {
-    setMaxSlopeAngle(45.0f);
+	setMaxSlopeAngle(45.0f);
 
-    node->addRef();
-    node->addListener(this);
-
-    // Create physics motion state for syncing transform between gameplay and bullet
-    Vector3 centerOfMassOffset(-center);
-    _motionState = new PhysicsMotionState(node, &centerOfMassOffset);
-
-    // Create ghost object, which is used as an efficient way to detect
-    // collisions between pairs of objects.
-    _ghostObject = bullet_new<btPairCachingGhostObject>();
-
-    // Set initial transform
-    _motionState->getWorldTransform(_ghostObject->getWorldTransform());
-
-    PhysicsController* pc = Game::getInstance()->getPhysicsController();
-
-    // Create a capsule collision shape (this is automatically deleted by PhysicsController when our collision object is removed)
-    _collisionShape = static_cast<btConvexShape*>(pc->createCapsule(radius, height - radius*2.0f));
-
-    // Set the collision shape on the ghost object (get it from the node's rigid body)
-    _ghostObject->setCollisionShape(_collisionShape);
-    _ghostObject->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT);
-
-    // Add the collision object to the physics system
-    pc->addCollisionObject(this);
+    // Set the collision flags on the ghost object to indicate it's a character
+    //_ghostObject->setCollisionFlags(_ghostObject->getCollisionFlags() | btCollisionObject::CF_CHARACTER_OBJECT);
 
     // Register ourselves as an action on the physics world so we are called back during physics ticks
-    pc->_world->addAction(this);
+    Game::getInstance()->getPhysicsController()->_world->addAction(this);
 }
 
 PhysicsCharacter::~PhysicsCharacter()
 {
-    // Remove ourself from physics system
-    PhysicsController* pc = Game::getInstance()->getPhysicsController();
-
-    pc->removeCollisionObject(this);
-
     // Unregister ourselves as action from world
-    pc->_world->removeAction(this);
-
-    SAFE_DELETE(_ghostObject);
-
-    _node->removeListener(this);
-    SAFE_RELEASE(_node);
-    SAFE_DELETE(_motionState);
+    Game::getInstance()->getPhysicsController()->_world->removeAction(this);
 }
 
 PhysicsCollisionObject::Type PhysicsCharacter::getType() const
@@ -139,16 +104,6 @@ void PhysicsCharacter::setPhysicsEnabled(bool enabled)
     _physicsEnabled = enabled;
 }
 
-btCollisionShape* PhysicsCharacter::getCollisionShape() const
-{
-    return _collisionShape;
-}
-
-Node* PhysicsCharacter::getNode() const
-{
-    return _node;
-}
-
 float PhysicsCharacter::getMaxStepHeight() const
 {
     return _stepHeight;
@@ -370,18 +325,6 @@ void PhysicsCharacter::updateCurrentVelocity()
     }
 }
 
-void PhysicsCharacter::transformChanged(Transform* transform, long cookie)
-{
-    if (!_ignoreTransformChanged)
-    {
-        // Update motion state with transform from node
-        _motionState->updateTransformFromNode();
-
-        // Update transform on ghost object
-        _motionState->getWorldTransform(_ghostObject->getWorldTransform());
-    }
-}
-
 void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep)
 {
     // First check for existing collisions and attempt to respond/fix them.
@@ -392,7 +335,7 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
     // dynamic objects (i.e. objects that moved and now intersect the character).
     if (_physicsEnabled)
     {
-        _colliding = fixCollision(collisionWorld);
+        //_colliding = fixCollision(collisionWorld);
         /*_colliding = false;
         int stepCount = 0;
 	    while (fixCollision(collisionWorld))
@@ -411,8 +354,9 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
     }
 
     // Update current and target world positions
-    btTransform transform = _ghostObject->getWorldTransform();
-    _currentPosition = transform.getOrigin();
+	Vector3 startPosition;
+	_node->getWorldMatrix().getTranslation(&startPosition);
+    _currentPosition = BV(startPosition);
 
     // Process movement in the up direction
     if (_physicsEnabled)
@@ -422,19 +366,11 @@ void PhysicsCharacter::updateAction(btCollisionWorld* collisionWorld, btScalar d
     stepForwardAndStrafe(collisionWorld, deltaTimeStep);
 
     // Process movement in the down direction
-    if (_physicsEnabled)
-        stepDown(collisionWorld, deltaTimeStep);
+	if (_physicsEnabled)
+		stepDown(collisionWorld, deltaTimeStep);
 
     // Set new position
-    transform.setOrigin(_currentPosition);
-
-    // Update world transform
-    ++_ignoreTransformChanged;
-    _motionState->setWorldTransform(transform);
-    --_ignoreTransformChanged;
-
-    // Update ghost object transform
-    _motionState->getWorldTransform(_ghostObject->getWorldTransform());
+	_node->translate(Vector3(_currentPosition.x(), _currentPosition.y(), _currentPosition.z()) - startPosition);
 }
 
 void PhysicsCharacter::stepUp(btCollisionWorld* collisionWorld, btScalar time)
@@ -509,12 +445,11 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
     }
 
     // Check for collisions by performing a bullet convex sweep test
-    btTransform start, end;
-	start.setIdentity();
-	end.setIdentity();
+    btTransform start = _ghostObject->getWorldTransform();
+	btTransform end = _ghostObject->getWorldTransform();
 
 	btScalar fraction = 1.0;
-	btScalar distance2 = (_currentPosition-targetPosition).length2();
+	btScalar distance2;
 
 	if (_colliding && (_normalizedVelocity.dot(_collisionNormal) > btScalar(0.0)))
 	{
@@ -530,16 +465,10 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 		btVector3 sweepDirNegative(_currentPosition - targetPosition);
 
 		ClosestNotMeConvexResultCallback callback(_ghostObject, sweepDirNegative, btScalar(0.0));
-		callback.m_collisionFilterGroup = _ghostObject->getBroadphaseHandle()->m_collisionFilterGroup;
-		callback.m_collisionFilterMask = _ghostObject->getBroadphaseHandle()->m_collisionFilterMask;
+		callback.m_collisionFilterGroup = btBroadphaseProxy::CharacterFilter;
+		callback.m_collisionFilterMask = btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter;
 
-        // Temporarily increase collision margin by a bit
-        //btScalar margin = _collisionShape->getMargin();
-        //_collisionShape->setMargin(margin + m_addedMargin);
-
-        _ghostObject->convexSweepTest(_collisionShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
-
-		//m_convexShape->setMargin(margin);
+		_ghostObject->convexSweepTest(_collisionShape->getShape<btConvexShape>(), start, end, callback, 0.01f/*collisionWorld->getDispatchInfo().m_allowedCcdPenetration*/);
 
 		fraction -= callback.m_closestHitFraction;
 
@@ -548,7 +477,12 @@ void PhysicsCharacter::stepForwardAndStrafe(btCollisionWorld* collisionWorld, fl
 			// We hit something so can move only a fraction
 			//btScalar hitDistance = (callback.m_hitPointWorld - _currentPosition).length();
 
-            //_currentPosition.setInterpolate3(_currentPosition, targetPosition, callback.m_closestHitFraction);
+            //targetPosition = _currentPosition;
+			//targetPosition.setInterpolate3(_currentPosition, targetPosition, callback.m_closestHitFraction * 0.1f);
+
+			//btVector3 normalDir = callback.m_hitNormalWorld;
+			//normalDir.normalize();
+			//targetPosition = callback.m_hitPointWorld + (normalDir * 0.2f);
 
 			updateTargetPositionFromCollision(targetPosition, callback.m_hitNormalWorld);
 			btVector3 currentDir = targetPosition - _currentPosition;
@@ -600,7 +534,7 @@ void PhysicsCharacter::stepDown(btCollisionWorld* collisionWorld, btScalar time)
     ClosestNotMeConvexResultCallback callback(_ghostObject, btVector3(0, 1, 0), _cosSlopeAngle);
 	callback.m_collisionFilterGroup = _ghostObject->getBroadphaseHandle()->m_collisionFilterGroup;
 	callback.m_collisionFilterMask = _ghostObject->getBroadphaseHandle()->m_collisionFilterMask;
-    _ghostObject->convexSweepTest(_collisionShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
+	_ghostObject->convexSweepTest(_collisionShape->getShape<btConvexShape>(), start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration);
 	if (callback.hasHit())
 	{
         // Collision detected, fix it

+ 5 - 34
gameplay/src/PhysicsCharacter.h

@@ -1,10 +1,7 @@
 #ifndef PHYSICSCHARACTER_H_
 #define PHYSICSCHARACTER_H_
 
-#include "Node.h"
-#include "PhysicsRigidBody.h"
-#include "PhysicsMotionState.h"
-#include "Vector3.h"
+#include "PhysicsGhostObject.h"
 
 namespace gameplay
 {
@@ -24,9 +21,9 @@ namespace gameplay
  * clips can be setup for typical character animations, such as walk, run, jump,
  * etc; and the controller will handle blending between these animations as needed.
  */
-class PhysicsCharacter : public PhysicsCollisionObject, public Transform::Listener, public btActionInterface
+class PhysicsCharacter : public PhysicsGhostObject, public btActionInterface
 {
-    friend class PhysicsController;
+    friend class Node;
 
 public:
 
@@ -56,15 +53,6 @@ public:
      */
     PhysicsCollisionObject::Type getType() const;
 
-    /**
-     * Returns the character node for this PhysicsCharacter.
-     *
-     * @return The character Node.
-     *
-     * @see PhysicsCollisionObject::getNode.
-     */
-    Node* getNode() const;
-
     /**
      * Returns whether physics simulation is enabled for the physics character.
      *
@@ -260,11 +248,6 @@ public:
      */
     void jump(float height);
 
-    /**
-     * @see Transform::Listener::transformChanged
-     */
-    void transformChanged(Transform* transform, long cookie);
-
     /**
      * @see btActionInterface::updateAction
      */
@@ -282,11 +265,6 @@ protected:
      */
     btCollisionObject* getCollisionObject() const;
 
-    /**
-     * @see PhysicsCollisionObject::getCollisionShape
-     */
-    btCollisionShape* getCollisionShape() const;
-
 private:
 
     struct CharacterAnimation
@@ -307,11 +285,9 @@ private:
      * Use PhysicsController::createCharacter to create physics characters.
      *
      * @param node Scene node that represents the character.
-     * @param radius Radius of capsule volume used for character collisions.
-     * @param height Height of the capsule volume used for character collisions.
-     * @param center Center point of the capsule volume for the character.
+	 * @param shape Physis collision shape definition.
      */
-    PhysicsCharacter(Node* node, float radius, float height, const Vector3 center = Vector3::zero());
+	PhysicsCharacter(Node* node, const PhysicsCollisionShape::Definition& shape);
 
     /**
      * Destructor.
@@ -334,8 +310,6 @@ private:
 
     bool fixCollision(btCollisionWorld* world);
 
-    Node* _node;
-    PhysicsMotionState* _motionState;
     btVector3 _moveVelocity;
     float _forwardVelocity;
     float _rightVelocity;
@@ -347,10 +321,7 @@ private:
     btVector3 _currentPosition;
     std::map<const char*, CharacterAnimation> _animations;
     std::map<unsigned int, CharacterAnimation*> _layers;
-    btPairCachingGhostObject* _ghostObject;
-    btConvexShape* _collisionShape;
     btManifoldArray	_manifoldArray;
-    int _ignoreTransformChanged;
     float _stepHeight;
     float _slopeAngle;
     float _cosSlopeAngle;

+ 49 - 9
gameplay/src/PhysicsCollisionObject.cpp

@@ -6,12 +6,60 @@
 namespace gameplay
 {
 
-PhysicsCollisionObject::PhysicsCollisionObject()
+// Internal class used to implement the collidesWith(PhysicsCollisionObject*) function.
+struct CollidesWithCallback : public btCollisionWorld::ContactResultCallback
+{
+    btScalar addSingleResult(btManifoldPoint& cp, 
+		const btCollisionObject* a, int partIdA, int indexA, 
+		const btCollisionObject* b, int partIdB, int indexB)
+	{
+		result = true;
+		return 0.0f;
+	}
+
+    bool result;
+};
+
+PhysicsCollisionObject::PhysicsCollisionObject(Node* node)
+	: _node(node), _motionState(NULL), _collisionShape(NULL)
 {
 }
 
 PhysicsCollisionObject::~PhysicsCollisionObject()
 {
+	SAFE_DELETE(_motionState);
+
+	Game::getInstance()->getPhysicsController()->destroyShape(_collisionShape);
+}
+
+PhysicsCollisionShape::Type PhysicsCollisionObject::getShapeType() const
+{
+	return getCollisionShape()->getType();
+}
+
+Node* PhysicsCollisionObject::getNode() const
+{
+	return _node;
+}
+
+PhysicsCollisionShape* PhysicsCollisionObject::getCollisionShape() const
+{
+	return _collisionShape;
+}
+
+PhysicsMotionState* PhysicsCollisionObject::getMotionState() const
+{
+	return _motionState;
+}
+
+bool PhysicsCollisionObject::isKinematic() const
+{
+	return getCollisionObject()->isKinematicObject();
+}
+
+bool PhysicsCollisionObject::isDynamic() const
+{
+	return !getCollisionObject()->isStaticOrKinematicObject();
 }
 
 void PhysicsCollisionObject::addCollisionListener(CollisionListener* listener, PhysicsCollisionObject* object)
@@ -55,12 +103,4 @@ bool PhysicsCollisionObject::CollisionPair::operator < (const CollisionPair& col
     return false;
 }
 
-btScalar PhysicsCollisionObject::CollidesWithCallback::addSingleResult(btManifoldPoint& cp,
-    const btCollisionObject* a, int partIdA, int indexA, 
-    const btCollisionObject* b, int partIdB, int indexB)
-{
-    result = true;
-    return 0.0f;
-}
-
 }

+ 65 - 27
gameplay/src/PhysicsCollisionObject.h

@@ -2,6 +2,8 @@
 #define PHYSICSCOLLISIONOBJECT_H_
 
 #include "Vector3.h"
+#include "PhysicsCollisionShape.h"
+#include "PhysicsMotionState.h"
 
 namespace gameplay
 {
@@ -14,11 +16,12 @@ class Node;
 class PhysicsCollisionObject
 {
     friend class PhysicsController;
+	friend class PhysicsConstraint;
 
 public:
 
     /**
-     * Enumeration of all possible collision object types.
+     * Represents the different types of collision objects.
      */
     enum Type
     {
@@ -30,7 +33,17 @@ public:
         /**
          * PhysicsCharacter type.
          */
-        CHARACTER
+        CHARACTER,
+
+        /** 
+         * PhysicsGhostObject type.
+         */
+        GHOST_OBJECT,
+
+		/**
+		 * No collision object.
+		 */
+		NONE
     };
 
     /** 
@@ -109,15 +122,52 @@ public:
                                     const Vector3& contactPointB = Vector3::zero()) = 0;
     };
 
+	/**
+     * Virtual destructor.
+     */
+    virtual ~PhysicsCollisionObject();
+
     /**
      * Returns the type of the collision object.
      */
     virtual PhysicsCollisionObject::Type getType() const = 0;
 
+	/**
+	 * Returns the type of the shape for this collision object.
+	 */
+	PhysicsCollisionShape::Type getShapeType() const;
+
     /**
      * Returns the node associated with this collision object.
      */
-    virtual Node* getNode() const = 0;
+    Node* getNode() const;
+
+	/**
+     * Returns the collision shape.
+     *
+     * @return The collision shape.
+     */
+    PhysicsCollisionShape* getCollisionShape() const;
+
+	/**
+	 * Returns whether this collision object is kinematic.
+	 *
+	 * A kinematic collision object is an object that is not simulated by
+	 * the physics system and instead has its transform driven manually.
+	 *
+	 * @return Whether the collision object is kinematic.
+	 */
+	bool isKinematic() const;
+
+    /**
+     * Returns whether this collision object is dynamic.
+	 *
+	 * A dynamic collision object is simulated entirely by the physics system,
+	 * such as with dynamic rigid bodies. 
+     *
+     * @return Whether the collision object is dynamic.
+     */
+    bool isDynamic() const;
 
     /**
      * Adds a collision listener for this collision object.
@@ -148,12 +198,7 @@ protected:
     /**
      * Constructor.
      */
-    PhysicsCollisionObject();
-
-    /**
-     * Virtual destructor.
-     */
-    virtual ~PhysicsCollisionObject();
+    PhysicsCollisionObject(Node* node);
 
     /**
      * Returns the Bullet Physics collision object.
@@ -162,24 +207,17 @@ protected:
      */
     virtual btCollisionObject* getCollisionObject() const = 0;
 
-    /**
-     * Returns the Bullet Physics collision shape.
-     *
-     * @return The Bullet collision shape.
-     */
-    virtual btCollisionShape* getCollisionShape() const = 0;
-
-private:
-
-    // Internal class used to implement the collidesWith(PhysicsRigidBody*) function.
-    struct CollidesWithCallback : public btCollisionWorld::ContactResultCallback
-    {
-        btScalar addSingleResult(btManifoldPoint& cp, 
-                                 const btCollisionObject* a, int partIdA, int indexA, 
-                                 const btCollisionObject* b, int partIdB, int indexB);
-
-        bool result;
-    };
+	/**
+	 * Returns the physics motion state.
+	 *
+	 * @return The motion state object.
+	 */
+	PhysicsMotionState* getMotionState() const;
+
+	// Common member variables
+	Node* _node;
+    PhysicsMotionState* _motionState;
+    PhysicsCollisionShape* _collisionShape;
 
 };
 

+ 155 - 0
gameplay/src/PhysicsCollisionShape.cpp

@@ -0,0 +1,155 @@
+#include "Base.h"
+#include "PhysicsCollisionShape.h"
+
+namespace gameplay
+{
+
+PhysicsCollisionShape::PhysicsCollisionShape(Type type, btCollisionShape* shape)
+	: _type(type), _shape(shape)
+{
+	memset(&_shapeData, 0, sizeof(_shapeData));
+}
+
+PhysicsCollisionShape::~PhysicsCollisionShape()
+{
+	if (_shape)
+	{
+		// Cleanup shape-specific cached data
+		switch (_type)
+		{
+		case SHAPE_MESH:
+			if (_shapeData.meshData)
+			{
+				SAFE_DELETE_ARRAY(_shapeData.meshData->vertexData);
+				for (unsigned int i = 0; i < _shapeData.meshData->indexData.size(); i++)
+				{
+					SAFE_DELETE_ARRAY(_shapeData.meshData->indexData[i]);
+				}
+				SAFE_DELETE(_shapeData.meshData);
+			}
+			break;
+		case SHAPE_HEIGHTFIELD:
+			if (_shapeData.heightfieldData)
+			{
+				SAFE_DELETE_ARRAY(_shapeData.heightfieldData->heightData);
+				SAFE_DELETE(_shapeData.heightfieldData);
+			}
+			break;
+		}
+
+		// Free the bullet shape
+		SAFE_DELETE(_shape);
+	}
+}
+
+PhysicsCollisionShape::Type PhysicsCollisionShape::getType() const
+{
+	return _type;
+}
+
+PhysicsCollisionShape::Definition::Definition()
+	: isExplicit(false), centerAbsolute(false)
+{
+	memset(&data, 0, sizeof(data));
+}
+
+PhysicsCollisionShape::Definition::~Definition()
+{
+	switch (type)
+	{
+	case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+		SAFE_RELEASE(data.heightfield);
+		break;
+
+	case PhysicsCollisionShape::SHAPE_MESH:
+		SAFE_RELEASE(data.mesh);
+		break;
+	}
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::box()
+{
+	Definition d;
+	d.type = SHAPE_BOX;
+	d.isExplicit = false;
+	d.centerAbsolute = false;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::box(const Vector3& extents, const Vector3& center, bool absolute)
+{
+	Definition d;
+	d.type = SHAPE_BOX;
+	d.data.boxExtents = extents;
+	d.data.boxCenter = center;
+	d.isExplicit = true;
+	d.centerAbsolute = absolute;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::sphere()
+{
+	Definition d;
+	d.type = SHAPE_SPHERE;
+	d.isExplicit = false;
+	d.centerAbsolute = false;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::sphere(float radius, const Vector3& center, bool absolute)
+{
+	Definition d;
+	d.type = SHAPE_SPHERE;
+	d.data.sphereRadius = radius;
+	d.data.sphereCenter = center;
+	d.isExplicit  = true;
+	d.centerAbsolute = absolute;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule()
+{
+	Definition d;
+	d.type = SHAPE_CAPSULE;
+	d.isExplicit = false;
+	d.centerAbsolute = false;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::capsule(float radius, float height, const Vector3& center, bool absolute)
+{
+	Definition d;
+	d.type = SHAPE_CAPSULE;
+	d.data.capsuleRadius = radius;
+	d.data.capsuleHeight = height;
+	d.data.capsuleCenter = center;
+	d.isExplicit = true;
+	d.centerAbsolute = absolute;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::heightfield(Image* image)
+{
+	image->addRef();
+
+	Definition d;
+	d.type = SHAPE_HEIGHTFIELD;
+	d.data.heightfield = image;
+	d.isExplicit = true;
+	d.centerAbsolute = false;
+	return d;
+}
+
+PhysicsCollisionShape::Definition PhysicsCollisionShape::mesh(Mesh* mesh)
+{
+	mesh->addRef();
+
+	Definition d;
+	d.type = SHAPE_MESH;
+	d.data.mesh = mesh;
+	d.isExplicit = true;
+	d.centerAbsolute = false;
+	return d;
+}
+
+}

+ 220 - 0
gameplay/src/PhysicsCollisionShape.h

@@ -0,0 +1,220 @@
+#ifndef PHYSICSCOLLISIONSHAPE_H_
+#define PHYSICSCOLLISIONSHAPE_H_
+
+#include "Vector3.h"
+#include "Image.h"
+#include "Mesh.h"
+
+namespace gameplay
+{
+
+/**
+ * Base physics collision shape class that all supported shapes derive from.
+ */
+class PhysicsCollisionShape : public Ref
+{
+	friend class PhysicsController;
+	friend class PhysicsRigidBody;
+
+public:
+
+	/**
+	 * Defines the supported collision shape types.
+	 */
+	enum Type
+	{
+		SHAPE_BOX,
+		SHAPE_SPHERE,
+		SHAPE_CAPSULE,
+		SHAPE_MESH,
+		SHAPE_HEIGHTFIELD
+	};
+
+	/**
+	 * Structure representing the definition of a collision shape, which is used
+	 * during collision shape construction time.
+	 *
+	 * Use the static methods on the PhysicsCollisionShape class to return
+	 * 
+	 */
+	struct Definition
+	{
+		friend class PhysicsCollisionShape;
+		friend class PhysicsController;
+		friend class PhysicsRigidBody;
+
+	public:
+
+		~Definition();
+
+	private:
+
+		Definition();
+
+		// Shape type.
+		PhysicsCollisionShape::Type type;
+
+		// Shape data.
+		union
+		{
+			struct { Vector3 boxExtents, boxCenter; };
+			struct { Vector3 sphereCenter; float sphereRadius; };
+			struct { Vector3 capsuleCenter; float capsuleRadius, capsuleHeight; };
+			struct { Image* heightfield; };
+			struct { Mesh* mesh; };
+		} data;
+
+		// Whether the shape definition is explicit, or if it is inherited from node bounds.
+		bool isExplicit;
+
+		// Whether the center position is absolute or relative to the node position.
+		bool centerAbsolute;
+	};
+
+	/**
+	 * Returns the type of this collision shape.
+	 *
+	 * @return The collision shape type.
+	 */
+	PhysicsCollisionShape::Type getType() const;
+
+	/**
+	 * Returns the internal bullet physics shape object.
+	 *
+	 * @return The bullet shape object.
+	 */
+	template <class T> T* getShape() const
+	{
+		return static_cast<T*>(_shape);
+	}
+
+	/**
+	 * Returns the internal bullet physics shape object.
+	 *
+	 * @return The bullet shape object.
+	 */
+	template <> btCollisionShape* getShape<btCollisionShape>() const
+	{
+		return _shape;
+	}
+
+	/**
+	 * Defines a box shape, using the bounding volume of the node it is attached to.
+	 *
+	 * @return Definition of a box shape.
+	 */
+	static PhysicsCollisionShape::Definition box();
+
+	/**
+	 * Defines a box shape, using the specified shape information and center.
+	 *
+	 * @param extents Extents of the box shape along the x, y and z axes.
+	 * @param center Center point of the box.
+	 * @param absolute True to specifiy that the given center point is an absolute position.
+	 *		By default the center point is treated as relative to the location of the node
+	 *		that the shape is attached to.
+	 *
+	 * @return Definition of a box shape.
+	 */
+	static PhysicsCollisionShape::Definition box(const Vector3& extents, const Vector3& center = Vector3::zero(), bool absolute = false);
+
+	/**
+	 * Defines a sphere shape, using the bounding volume of the node it is attached to.
+	 *
+	 * @return Definition of a sphere shape.
+	 */
+	static PhysicsCollisionShape::Definition sphere();
+
+	/**
+	 * Defines a sphere shape, using the specified shape information and center.
+	 *
+	 * @param radius Radius of the sphere.
+	 * @param center Center point of the sphere.
+	 * @param absolute True to specifiy that the given center point is an absolute position.
+	 *		By default the center point is treated as relative to the location of the node
+	 *		that the shape is attached to.
+	 *
+	 * @return Definition of a sphere shape.
+	 */
+	static PhysicsCollisionShape::Definition sphere(float radius, const Vector3& center = Vector3::zero(), bool absolute = false);
+
+	/**
+	 * Defines a capsule shape, using the bounding volume of the node it is attached to.
+	 *
+	 * @return Definition of a capsule shape.
+	 */
+	static PhysicsCollisionShape::Definition capsule();
+
+	/**
+	 * Defines a capsule shape, using the specified shape information and center.
+	 *
+	 * @param radius Radius of the capsule.
+	 * @param height Height of the capsule.
+	 * @param center Center point of the capsule.
+	 * @param absolute True to specifiy that the given center point is an absolute position.
+	 *		By default the center point is treated as relative to the location of the node
+	 *		that the shape is attached to.
+	 *
+	 * @return Definition of a capsule shape.
+	 */
+	static PhysicsCollisionShape::Definition capsule(float radius, float height, const Vector3& center = Vector3::zero(), bool absolute = false);
+
+	/**
+	 * Defines a heightfield shape using the specified heightfield image.
+	 *
+	 * @return Definition of a heightfield shape.
+	 */
+	static PhysicsCollisionShape::Definition heightfield(Image* image);
+
+	/**
+	 * Defines a mesh shape using the specified mehs.
+	 *
+	 * @return Definition of a mesh shape.
+	 */
+	static PhysicsCollisionShape::Definition mesh(Mesh* mesh);
+
+private:
+
+	struct MeshData
+	{
+		float* vertexData;
+		std::vector<unsigned char*> indexData;
+	};
+
+	struct HeightfieldData
+	{
+		float* heightData;
+		unsigned int width;
+		unsigned int height;
+		mutable Matrix inverse;
+		mutable bool inverseIsDirty;
+	};
+
+	/**
+	 * Constructor.
+	 */
+	PhysicsCollisionShape(Type type, btCollisionShape* shape);
+
+	/**
+	 * Destructor.
+	 */
+	~PhysicsCollisionShape();
+
+	// Shape type
+	Type _type;
+
+	// Bullet shape object
+	btCollisionShape* _shape;
+
+	// Shape specific cached data
+	union
+	{
+		MeshData* meshData;
+		HeightfieldData* heightfieldData;
+	} _shapeData;
+
+};
+
+}
+
+#endif

+ 1 - 1
gameplay/src/PhysicsConstraint.cpp

@@ -155,7 +155,7 @@ Vector3 PhysicsConstraint::getWorldCenterOfMass(const Model* model)
 
 Vector3 PhysicsConstraint::offsetByCenterOfMass(const Node* node, const Vector3& v)
 {
-    btVector3 centerOfMassOffset = ((PhysicsMotionState*)node->getRigidBody()->_body->getMotionState())->_centerOfMassOffset.getOrigin();
+	btVector3 centerOfMassOffset = (node->getCollisionObject()->getMotionState())->_centerOfMassOffset.getOrigin();
     return Vector3(v.x + centerOfMassOffset.x(), v.y + centerOfMassOffset.y(), v.z + centerOfMassOffset.z());
 }
 

+ 429 - 124
gameplay/src/PhysicsController.cpp

@@ -1,8 +1,10 @@
 #include "Base.h"
-#include "Game.h"
-#include "MeshPart.h"
 #include "PhysicsController.h"
+#include "PhysicsRigidBody.h"
+#include "PhysicsCharacter.h"
 #include "PhysicsMotionState.h"
+#include "Game.h"
+#include "MeshPart.h"
 #include "Package.h"
 
 #include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
@@ -42,16 +44,6 @@ void PhysicsController::addStatusListener(Listener* listener)
     _listeners->push_back(listener);
 }
 
-PhysicsCharacter* PhysicsController::createCharacter(Node* node, float radius, float height, const Vector3& center)
-{
-    return new PhysicsCharacter(node, radius, height, center);
-}
-
-void PhysicsController::destroyCharacter(PhysicsCharacter* character)
-{
-    SAFE_DELETE(character);
-}
-
 PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
     checkConstraintRigidBodies(a, b);
@@ -163,7 +155,7 @@ PhysicsCollisionObject* PhysicsController::rayTest(const Ray& ray, float distanc
 btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA, int indexA, 
     const btCollisionObject* b, int partIdB, int indexB)
 {
-    // Get pointers to the PhysicsRigidBody objects.
+    // Get pointers to the PhysicsCollisionObject objects.
     PhysicsCollisionObject* rbA = Game::getInstance()->getPhysicsController()->getCollisionObject(a);
     PhysicsCollisionObject* rbB = Game::getInstance()->getPhysicsController()->getCollisionObject(b);
 
@@ -171,7 +163,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
     // we notify the listeners only if the pair was not colliding
     // during the previous frame. Otherwise, it's a new pair, so add a
     // new entry to the cache with the appropriate listeners and notify them.
-    PhysicsRigidBody::CollisionPair pair(rbA, rbB);
+    PhysicsCollisionObject::CollisionPair pair(rbA, rbB);
 
     CollisionInfo* collisionInfo;
     if (_collisionStatus.count(pair) > 0)
@@ -184,7 +176,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
         collisionInfo = &_collisionStatus[pair];
 
         // Add the appropriate listeners.
-        PhysicsRigidBody::CollisionPair p1(pair.objectA, NULL);
+        PhysicsCollisionObject::CollisionPair p1(pair.objectA, NULL);
         if (_collisionStatus.count(p1) > 0)
         {
             const CollisionInfo& ci = _collisionStatus[p1];
@@ -194,7 +186,7 @@ btScalar PhysicsController::addSingleResult(btManifoldPoint& cp, const btCollisi
                 collisionInfo->_listeners.push_back(*iter);
             }
         }
-        PhysicsRigidBody::CollisionPair p2(pair.objectB, NULL);
+        PhysicsCollisionObject::CollisionPair p2(pair.objectB, NULL);
         if (_collisionStatus.count(p2) > 0)
         {
             const CollisionInfo& ci = _collisionStatus[p2];
@@ -328,12 +320,12 @@ void PhysicsController::update(long elapsedTime)
     // If an entry was marked for removal in the last frame, remove it now.
 
     // Dirty the collision status cache entries.
-    std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
+    std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
     for (; iter != _collisionStatus.end();)
     {
         if ((iter->second._status & REMOVE) != 0)
         {
-            std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator eraseIter = iter;
+            std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo>::iterator eraseIter = iter;
             iter++;
             _collisionStatus.erase(eraseIter);
         }
@@ -410,11 +402,15 @@ void PhysicsController::addCollisionObject(PhysicsCollisionObject* object)
     switch (object->getType())
     {
     case PhysicsCollisionObject::RIGID_BODY:
-        _world->addRigidBody(static_cast<btRigidBody*>(object->getCollisionObject()));
+		_world->addRigidBody(static_cast<btRigidBody*>(object->getCollisionObject()));//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::AllFilter);
         break;
 
     case PhysicsCollisionObject::CHARACTER:
-        _world->addCollisionObject(object->getCollisionObject(), btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter);
+		_world->addCollisionObject(object->getCollisionObject());//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::AllFilter);//, btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::CharacterFilter );// | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::DefaultFilter*/ 0);
+        break;
+
+    case PhysicsCollisionObject::GHOST_OBJECT:
+		_world->addCollisionObject(object->getCollisionObject());//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter | btBroadphaseProxy::AllFilter);//, btBroadphaseProxy::DefaultFilter, btBroadphaseProxy::DefaultFilter | btBroadphaseProxy::StaticFilter | btBroadphaseProxy::CharacterFilter);
         break;
 
     default:
@@ -435,6 +431,7 @@ void PhysicsController::removeCollisionObject(PhysicsCollisionObject* object)
             break;
 
         case PhysicsCollisionObject::CHARACTER:
+        case PhysicsCollisionObject::GHOST_OBJECT:
             _world->removeCollisionObject(object->getCollisionObject());
             break;
 
@@ -444,28 +441,8 @@ void PhysicsController::removeCollisionObject(PhysicsCollisionObject* object)
         }
     }
 
-    // Release collision shape
-    if (object->getCollisionShape())
-    {
-        PhysicsCollisionShape* shape = reinterpret_cast<PhysicsCollisionShape*>(object->getCollisionShape()->getUserPointer());
-        if (shape)
-        {
-            if (shape->getRefCount() == 1)
-            {
-                std::vector<PhysicsCollisionShape*>::iterator shapeItr = std::find(_shapes.begin(), _shapes.end(), shape);
-                shape->release();
-                if (shapeItr != _shapes.end())
-                    _shapes.erase(shapeItr);
-            }
-            else
-            {
-                shape->release();
-            }
-        }
-    }
-
     // Find all references to the object in the collision status cache and mark them for removal.
-    std::map<PhysicsRigidBody::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
+    std::map<PhysicsCollisionObject::CollisionPair, CollisionInfo>::iterator iter = _collisionStatus.begin();
     for (; iter != _collisionStatus.end(); iter++)
     {
         if (iter->first.objectA == object || iter->first.objectB == object)
@@ -479,55 +456,207 @@ PhysicsCollisionObject* PhysicsController::getCollisionObject(const btCollisionO
     return reinterpret_cast<PhysicsCollisionObject*>(collisionObject->getUserPointer());
 }
 
-btCollisionShape* PhysicsController::createBox(const Vector3& min, const Vector3& max, const Vector3& scale)
+void getBoundingBox(Node* node, BoundingBox* out, bool merge = false)
 {
-    btVector3 halfExtents(scale.x * 0.5 * abs(max.x - min.x), scale.y * 0.5 * abs(max.y - min.y), scale.z * 0.5 * abs(max.z - min.z));
+	if (node->getModel())
+	{
+		if (merge)
+			out->merge(node->getModel()->getMesh()->getBoundingBox());
+		else
+		{
+			out->set(node->getModel()->getMesh()->getBoundingBox());
+			merge = true;
+		}
+	}
+
+	Node* child = node->getFirstChild();
+	while (child)
+	{
+		getBoundingBox(child, out, merge);
+		child = child->getNextSibling();
+	}
+}
 
-    // Return the box shape from the cache if it already exists.
-    for (unsigned int i = 0; i < _shapes.size(); i++)
+void getBoundingSphere(Node* node, BoundingSphere* out, bool merge = false)
+{
+	if (node->getModel())
+	{
+		if (merge)
+			out->merge(node->getModel()->getMesh()->getBoundingSphere());
+		else
+		{
+			out->set(node->getModel()->getMesh()->getBoundingSphere());
+			merge = true;
+		}
+	}
+
+	Node* child = node->getFirstChild();
+	while (child)
+	{
+		getBoundingSphere(child, out, merge);
+		child = child->getNextSibling();
+	}
+}
+
+void computeCenterOfMass(const Vector3& center, const Vector3& scale, Vector3* centerOfMassOffset)
+{
+	// Update center of mass offset
+	*centerOfMassOffset = center;
+	centerOfMassOffset->x *= scale.x;
+	centerOfMassOffset->y *= scale.y;
+	centerOfMassOffset->z *= scale.z;
+	centerOfMassOffset->negate();
+}
+
+PhysicsCollisionShape* PhysicsController::createShape(Node* node, const PhysicsCollisionShape::Definition& shape, Vector3* centerOfMassOffset)
+{
+	PhysicsCollisionShape* collisionShape = NULL;
+
+    // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
+    Vector3 scale;
+    node->getWorldMatrix().getScale(&scale);
+
+	switch (shape.type)
     {
-        if (_shapes[i]->_shape->getShapeType() == BOX_SHAPE_PROXYTYPE)
+	case PhysicsCollisionShape::SHAPE_BOX:
         {
-            btBoxShape* box = static_cast<btBoxShape*>(_shapes[i]->_shape);
-            if (box->getHalfExtentsWithMargin() == halfExtents)
-            {
-                _shapes[i]->addRef();
-                return box;
-            }
+			if (shape.isExplicit)
+			{
+				// Use the passed in box information
+				collisionShape = createBox(shape.data.boxExtents, Vector3::one());
+
+				if (shape.centerAbsolute)
+				{
+					computeCenterOfMass(shape.data.boxCenter, scale, centerOfMassOffset);
+				}
+				else
+				{
+					BoundingBox box;
+					getBoundingBox(node, &box);
+					computeCenterOfMass(box.getCenter() + shape.data.boxCenter, scale, centerOfMassOffset);
+				}
+			}
+			else
+			{
+				// Automatically compute bounding box from mesh's bounding box
+				BoundingBox box;
+				getBoundingBox(node, &box);
+				collisionShape = createBox(Vector3(std::abs(box.max.x - box.min.x), std::abs(box.max.y - box.min.y), std::abs(box.max.z - box.min.z)), scale);
+
+				computeCenterOfMass(box.getCenter(), scale, centerOfMassOffset);
+			}
+        }
+		break;
+
+	case PhysicsCollisionShape::SHAPE_SPHERE:
+        {
+			if (shape.isExplicit)
+			{
+				// Use the passed in sphere information
+				collisionShape = createSphere(shape.data.sphereRadius, Vector3::one());
+
+				if (shape.centerAbsolute)
+				{
+					computeCenterOfMass(shape.data.sphereCenter, scale, centerOfMassOffset);
+				}
+				else
+				{
+					BoundingSphere sphere;
+					getBoundingSphere(node, &sphere);
+					computeCenterOfMass(sphere.center + shape.data.sphereCenter, scale, centerOfMassOffset);
+				}
+			}
+			else
+			{
+				// Automatically compute bounding sphere from mesh's bounding sphere
+				BoundingSphere sphere;
+				getBoundingSphere(node, &sphere);
+				collisionShape = createSphere(sphere.radius, scale);
+
+				computeCenterOfMass(sphere.center, scale, centerOfMassOffset);
+			}
+        }
+		break;
+
+	case PhysicsCollisionShape::SHAPE_CAPSULE:
+		{
+			if (shape.isExplicit)
+			{
+				// Use the passed in capsule information
+				collisionShape = createCapsule(shape.data.capsuleRadius, shape.data.capsuleHeight, Vector3::one());
+
+				if (shape.centerAbsolute)
+				{
+					computeCenterOfMass(shape.data.capsuleCenter, scale, centerOfMassOffset);
+				}
+				else
+				{
+					BoundingBox box;
+					getBoundingBox(node, &box);
+					computeCenterOfMass(box.getCenter() + shape.data.capsuleCenter, scale, centerOfMassOffset);
+				}
+			}
+			else
+			{
+				// Compute a capsule shape that roughly matches the bounding box of the mesh
+				BoundingBox box;
+				getBoundingBox(node, &box);
+				float radius = std::max((box.max.x - box.min.x) * 0.5f, (box.max.z - box.min.z) * 0.5f);
+				float height = (box.max.y - box.min.y) - radius * 2.0f;
+				collisionShape = createCapsule(radius, height, scale);
+
+				computeCenterOfMass(box.getCenter(), scale, centerOfMassOffset);
+			}
+		}
+		break;
+
+	case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+		{
+			// Build heightfield rigid body from the passed in shape
+			collisionShape = createHeightfield(node, shape.data.heightfield, centerOfMassOffset);
+		}
+		break;
+
+	case PhysicsCollisionShape::SHAPE_MESH:
+        {
+			// Build mesh from passed in shape
+			collisionShape = createMesh(shape.data.mesh, scale);
         }
+		break;
     }
-    
-    // Create the box shape and add it to the cache.
-    btBoxShape* box = bullet_new<btBoxShape>(halfExtents);
-    _shapes.push_back(new PhysicsCollisionShape(box));
 
-    return box;
+	return collisionShape;
 }
 
-btCollisionShape* PhysicsController::createCapsule(float radius, float height)
+PhysicsCollisionShape* PhysicsController::createBox(const Vector3& extents, const Vector3& scale)
 {
-    // Return the capsule shape from the cache if it already exists.
-    for (unsigned int i = 0; i < _shapes.size(); i++)
+    btVector3 halfExtents(scale.x * 0.5 * extents.x, scale.y * 0.5 * extents.y, scale.z * 0.5 * extents.z);
+
+	PhysicsCollisionShape* shape;
+
+    // Return the box shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); ++i)
     {
-        if (_shapes[i]->_shape->getShapeType() == CAPSULE_SHAPE_PROXYTYPE)
+		shape = _shapes[i];
+		if (shape->getType() == PhysicsCollisionShape::SHAPE_BOX)
         {
-            btCapsuleShape* capsule = static_cast<btCapsuleShape*>(_shapes[i]->_shape);
-            if (capsule->getRadius() == radius && capsule->getHalfHeight() == 0.5f * height)
+			btBoxShape* box = static_cast<btBoxShape*>(shape->_shape);
+            if (box->getHalfExtentsWithMargin() == halfExtents)
             {
-                _shapes[i]->addRef();
-                return capsule;
+				shape->addRef();
+                return shape;
             }
         }
     }
-    
-    // Create the capsule shape and add it to the cache.
-    btCapsuleShape* capsule = bullet_new<btCapsuleShape>(radius, height);
-    _shapes.push_back(new PhysicsCollisionShape(capsule));
 
-    return capsule;
+    // Create the box shape and add it to the cache.
+	shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_BOX, bullet_new<btBoxShape>(halfExtents));
+    _shapes.push_back(shape);
+
+    return shape;
 }
 
-btCollisionShape* PhysicsController::createSphere(float radius, const Vector3& scale)
+PhysicsCollisionShape* PhysicsController::createSphere(float radius, const Vector3& scale)
 {
     // Since sphere shapes depend only on the radius, the best we can do is take
     // the largest dimension and apply that as the uniform scale to the rigid body.
@@ -536,48 +665,188 @@ btCollisionShape* PhysicsController::createSphere(float radius, const Vector3& s
         uniformScale = scale.y;
     if (uniformScale < scale.z)
         uniformScale = scale.z;
-    
+
+	float scaledRadius = radius * uniformScale;
+
+	PhysicsCollisionShape* shape;
+
     // Return the sphere shape from the cache if it already exists.
-    for (unsigned int i = 0; i < _shapes.size(); i++)
+    for (unsigned int i = 0; i < _shapes.size(); ++i)
     {
-        if (_shapes[i]->_shape->getShapeType() == SPHERE_SHAPE_PROXYTYPE)
+		shape = _shapes[i];
+		if (shape->getType() == PhysicsCollisionShape::SHAPE_SPHERE)
         {
-            btSphereShape* sphere = static_cast<btSphereShape*>(_shapes[i]->_shape);
-            if (sphere->getRadius() == uniformScale * radius)
+            btSphereShape* sphere = static_cast<btSphereShape*>(shape->_shape);
+            if (sphere->getRadius() == scaledRadius)
             {
-                _shapes[i]->addRef();
-                return sphere;
+                shape->addRef();
+                return shape;
             }
         }
     }
 
     // Create the sphere shape and add it to the cache.
-    btSphereShape* sphere = bullet_new<btSphereShape>(uniformScale * radius);
-    _shapes.push_back(new PhysicsCollisionShape(sphere));
+	shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_SPHERE, bullet_new<btSphereShape>(scaledRadius));
+    _shapes.push_back(shape);
 
-    return sphere;
+    return shape;
 }
 
-btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Vector3& scale)
+PhysicsCollisionShape* PhysicsController::createCapsule(float radius, float height, const Vector3& scale)
 {
-    assert(body);
+	float girthScale = scale.x;
+	if (girthScale < scale.z)
+		girthScale = scale.z;
+	float scaledRadius = radius * girthScale;
+	float scaledHeight = height * scale.y;
+
+	PhysicsCollisionShape* shape;
 
-    // Retrieve the mesh rigid body data from the node's mesh.
-    Model* model = body->_node ? body->_node->getModel() : NULL;
-    Mesh* mesh = model ? model->getMesh() : NULL;
-    if (mesh == NULL)
+    // Return the capsule shape from the cache if it already exists.
+    for (unsigned int i = 0; i < _shapes.size(); i++)
     {
-        LOG_ERROR("Cannot create mesh rigid body for node without model/mesh.");
-        return NULL;
+		shape = _shapes[i];
+		if (shape->getType() == PhysicsCollisionShape::SHAPE_CAPSULE)
+        {
+            btCapsuleShape* capsule = static_cast<btCapsuleShape*>(shape->_shape);
+            if (capsule->getRadius() == scaledRadius && capsule->getHalfHeight() == 0.5f * scaledHeight)
+            {
+                shape->addRef();
+                return shape;
+            }
+        }
     }
 
-    // Only support meshes with triangle list primitive types
-    if (mesh->getPrimitiveType() != Mesh::TRIANGLES)
+    // Create the capsule shape and add it to the cache.
+	shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_CAPSULE, bullet_new<btCapsuleShape>(scaledRadius, scaledHeight));
+    _shapes.push_back(shape);
+
+    return shape;
+}
+
+PhysicsCollisionShape* PhysicsController::createHeightfield(Node* node, Image* image, Vector3* centerOfMassOffset)
+{
+	// Get the dimensions of the heightfield.
+	// If the node has a mesh defined, use the dimensions of the bounding box for the mesh.
+	// Otherwise simply use the image dimensions (with a max height of 255).
+	float width, length, minHeight, maxHeight;
+	if (node->getModel() && node->getModel()->getMesh())
+	{
+		const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
+		width = box.max.x - box.min.x;
+		length = box.max.z - box.min.z;
+		minHeight = box.min.y;
+		maxHeight = box.max.y;
+	}
+	else
+	{
+		width = image->getWidth();
+		length = image->getHeight();
+		minHeight = 0.0f;
+		maxHeight = 255.0f;
+	}
+
+    // Get the size in bytes of a pixel (we ensure that the image's
+    // pixel format is actually supported before calling this constructor).
+    unsigned int pixelSize = 0;
+    switch (image->getFormat())
     {
-        LOG_ERROR("Cannot create mesh rigid body for mesh without TRIANGLES primitive type.");
-        return NULL;
+        case Image::RGB:
+            pixelSize = 3;
+            break;
+        case Image::RGBA:
+            pixelSize = 4;
+            break;
+		default:
+			LOG_ERROR("Unsupported pixel format for heightmap image.");
+			return NULL;
+    }
+
+    // Calculate the heights for each pixel.
+    float* heights = new float[image->getWidth() * image->getHeight()];
+	unsigned char* data = image->getData();
+    for (unsigned int x = 0, w = image->getWidth(); x < w; ++x)
+    {
+        for (unsigned int y = 0, h = image->getHeight(); y < h; ++y)
+        {
+            heights[x + y * w] = ((((float)data[(x + y * h) * pixelSize + 0]) +
+                ((float)data[(x + y * h) * pixelSize + 1]) +
+                ((float)data[(x + y * h) * pixelSize + 2])) / 768.0f) * (maxHeight - minHeight) + minHeight;
+        }
     }
 
+	PhysicsCollisionShape::HeightfieldData* heightfieldData = new PhysicsCollisionShape::HeightfieldData();
+	heightfieldData->heightData = NULL;
+	heightfieldData->inverseIsDirty = true;
+
+    // Generate the heightmap data needed for physics (one height per world unit).
+    unsigned int sizeWidth = width;
+    unsigned int sizeHeight = length;
+	heightfieldData->width = sizeWidth + 1;
+    heightfieldData->height = sizeHeight + 1;
+	heightfieldData->heightData = new float[heightfieldData->width * heightfieldData->height];
+    unsigned int heightIndex = 0;
+    float widthImageFactor = (float)(image->getWidth() - 1) / sizeWidth;
+    float heightImageFactor = (float)(image->getHeight() - 1) / sizeHeight;
+    float x = 0.0f;
+    float z = 0.0f;
+    for (unsigned int row = 0, z = 0.0f; row <= sizeHeight; row++, z += 1.0f)
+    {
+        for (unsigned int col = 0, x = 0.0f; col <= sizeWidth; col++, x += 1.0f)
+        {
+			heightIndex = row * heightfieldData->width + col;
+			heightfieldData->heightData[heightIndex] = calculateHeight(heights, image->getWidth(), image->getHeight(), x * widthImageFactor, (sizeHeight - z) * heightImageFactor);
+        }
+    }
+    SAFE_DELETE_ARRAY(heights);
+
+    // Offset the heightmap's center of mass according to the way that Bullet calculates the origin 
+    // of its heightfield collision shape; see documentation for the btHeightfieldTerrainShape for more info.
+    Vector3 s;
+    node->getWorldMatrix().getScale(&s);
+	centerOfMassOffset->set(0.0f, -(maxHeight - (0.5f * (maxHeight - minHeight))) / s.y, 0.0f);
+
+	// Create the bullet terrain shape
+	btHeightfieldTerrainShape* terrainShape = bullet_new<btHeightfieldTerrainShape>(
+		heightfieldData->width, heightfieldData->height, heightfieldData->heightData, 1.0f, minHeight, maxHeight, 1, PHY_FLOAT, false);
+
+	// Create our collision shape object and store heightfieldData in it
+	PhysicsCollisionShape* shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_HEIGHTFIELD, terrainShape);
+	shape->_shapeData.heightfieldData = heightfieldData;
+
+    _shapes.push_back(shape);
+
+    return shape;
+}
+
+PhysicsCollisionShape* PhysicsController::createMesh(Mesh* mesh, const Vector3& scale)
+{
+    assert(mesh);
+
+	// Only support meshes with triangle list primitive types
+	bool triMesh = true;
+	if (mesh->getPartCount() > 0)
+	{
+		for (unsigned int i = 0; i < mesh->getPartCount(); ++i)
+		{
+			if (mesh->getPart(i)->getPrimitiveType() != Mesh::TRIANGLES)
+			{
+				triMesh = false;
+				break;
+			}
+		}
+	}
+	else
+	{
+		triMesh = mesh->getPrimitiveType() == Mesh::TRIANGLES;
+	}
+
+	if (!triMesh)
+	{
+		LOG_ERROR("Mesh rigid bodies are currently only supported on meshes with TRIANGLES primitive type.");
+		return NULL;
+	}
+
     // The mesh must have a valid URL (i.e. it must have been loaded from a Package)
     // in order to fetch mesh data for computing mesh rigid body.
     if (strlen(mesh->getUrl()) == 0)
@@ -592,11 +861,15 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
         return NULL;
     }
 
+	// Create mesh data to be populated and store in returned collision shape
+	PhysicsCollisionShape::MeshData* shapeMeshData = new PhysicsCollisionShape::MeshData();
+	shapeMeshData->vertexData = NULL;
+
     // Copy the scaled vertex position data to the rigid body's local buffer.
     Matrix m;
     Matrix::createScale(scale, &m);
     unsigned int vertexCount = data->vertexCount;
-    body->_vertexData = new float[vertexCount * 3];
+	shapeMeshData->vertexData = new float[vertexCount * 3];
     Vector3 v;
     int vertexStride = data->vertexFormat.getVertexSize();
     for (unsigned int i = 0; i < data->vertexCount; i++)
@@ -605,7 +878,7 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
               *((float*)&data->vertexData[i * vertexStride + 1 * sizeof(float)]),
               *((float*)&data->vertexData[i * vertexStride + 2 * sizeof(float)]));
         v *= m;
-        memcpy(&(body->_vertexData[i * 3]), &v, sizeof(float) * 3);
+		memcpy(&(shapeMeshData->vertexData[i * 3]), &v, sizeof(float) * 3);
     }
 
     btTriangleIndexVertexArray* meshInterface = bullet_new<btTriangleIndexVertexArray>();
@@ -638,7 +911,7 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
 
             // Move the index data into the rigid body's local buffer.
             // Set it to NULL in the MeshPartData so it is not released when the data is freed.
-            body->_indexData.push_back(meshPart->indexData);
+			shapeMeshData->indexData.push_back(meshPart->indexData);
             meshPart->indexData = NULL;
 
             // Create a btIndexedMesh object for the current mesh part.
@@ -646,9 +919,9 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
             indexedMesh.m_indexType = indexType;
             indexedMesh.m_numTriangles = meshPart->indexCount / 3; // assume TRIANGLES primitive type
             indexedMesh.m_numVertices = meshPart->indexCount;
-            indexedMesh.m_triangleIndexBase = (const unsigned char*)body->_indexData[i];
+			indexedMesh.m_triangleIndexBase = (const unsigned char*)shapeMeshData->indexData[i];
             indexedMesh.m_triangleIndexStride = indexStride*3;
-            indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
+			indexedMesh.m_vertexBase = (const unsigned char*)shapeMeshData->vertexData;
             indexedMesh.m_vertexStride = sizeof(float)*3;
             indexedMesh.m_vertexType = PHY_FLOAT;
 
@@ -664,16 +937,16 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
         {
             indexData[i] = i;
         }
-        body->_indexData.push_back((unsigned char*)indexData);
+        shapeMeshData->indexData.push_back((unsigned char*)indexData);
 
         // Create a single btIndexedMesh object for the mesh interface.
         btIndexedMesh indexedMesh;
         indexedMesh.m_indexType = PHY_INTEGER;
         indexedMesh.m_numTriangles = data->vertexCount / 3; // assume TRIANGLES primitive type
         indexedMesh.m_numVertices = data->vertexCount;
-        indexedMesh.m_triangleIndexBase = body->_indexData[0];
+        indexedMesh.m_triangleIndexBase = shapeMeshData->indexData[0];
         indexedMesh.m_triangleIndexStride = sizeof(unsigned int);
-        indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
+        indexedMesh.m_vertexBase = (const unsigned char*)shapeMeshData->vertexData;
         indexedMesh.m_vertexStride = sizeof(float)*3;
         indexedMesh.m_vertexType = PHY_FLOAT;
 
@@ -681,8 +954,11 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
         meshInterface->addIndexedMesh(indexedMesh, indexedMesh.m_indexType);
     }
 
-    btBvhTriangleMeshShape* shape = bullet_new<btBvhTriangleMeshShape>(meshInterface, true);
-    _shapes.push_back(new PhysicsCollisionShape(shape));
+	// Create our collision shape object and store shapeMeshData in it
+	PhysicsCollisionShape* shape = new PhysicsCollisionShape(PhysicsCollisionShape::SHAPE_MESH, bullet_new<btBvhTriangleMeshShape>(meshInterface, true));
+	shape->_shapeData.meshData = shapeMeshData;
+
+    _shapes.push_back(shape);
 
     // Free the temporary mesh data now that it's stored in physics system
     SAFE_DELETE(data);
@@ -690,11 +966,52 @@ btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body, const Ve
     return shape;
 }
 
-btCollisionShape* PhysicsController::createHeightfield(int width, int height, void* heightfieldData, float minHeight, float maxHeight)
+void PhysicsController::destroyShape(PhysicsCollisionShape* shape)
 {
-    btCollisionShape* shape = bullet_new<btHeightfieldTerrainShape>(width, height, heightfieldData, 1.0f, minHeight, maxHeight, 1, PHY_FLOAT, false);
-    _shapes.push_back(new PhysicsCollisionShape(shape));
-    return shape;
+    if (shape)
+    {
+        if (shape->getRefCount() == 1)
+        {
+			// Remove shape from shape cache
+            std::vector<PhysicsCollisionShape*>::iterator shapeItr = std::find(_shapes.begin(), _shapes.end(), shape);
+            if (shapeItr != _shapes.end())
+                _shapes.erase(shapeItr);
+        }
+
+		// Release the shape
+		shape->release();
+    }
+}
+
+float PhysicsController::calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y)
+{
+    unsigned int x1 = x;
+    unsigned int y1 = y;
+    unsigned int x2 = x1 + 1;
+    unsigned int y2 = y1 + 1;
+    float tmp;
+    float xFactor = modf(x, &tmp);
+    float yFactor = modf(y, &tmp);
+    float xFactorI = 1.0f - xFactor;
+    float yFactorI = 1.0f - yFactor;
+
+    if (x2 >= width && y2 >= height)
+    {
+        return data[x1 + y1 * width];
+    }
+    else if (x2 >= width)
+    {
+        return data[x1 + y1 * width] * yFactorI + data[x1 + y2 * width] * yFactor;
+    }
+    else if (y2 >= height)
+    {
+        return data[x1 + y1 * width] * xFactorI + data[x2 + y1 * width] * xFactor;
+    }
+    else
+    {
+        return data[x1 + y1 * width] * xFactorI * yFactorI + data[x1 + y2 * width] * xFactorI * yFactor + 
+            data[x2 + y2 * width] * xFactor * yFactor + data[x2 + y1 * width] * xFactor * yFactorI;
+    }
 }
 
 void PhysicsController::addConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint)
@@ -738,7 +1055,7 @@ void PhysicsController::removeConstraint(PhysicsConstraint* constraint)
         }
     }
 }
-    
+
 PhysicsController::DebugDrawer::DebugDrawer()
     : _mode(btIDebugDraw::DBG_DrawAabb | btIDebugDraw::DBG_DrawConstraintLimits | btIDebugDraw::DBG_DrawConstraints | 
        btIDebugDraw::DBG_DrawContactPoints | btIDebugDraw::DBG_DrawWireframe), _viewProjection(NULL), _meshBatch(NULL)
@@ -755,7 +1072,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
         "    gl_Position = u_viewProjectionMatrix * a_position;\n"
         "}"
     };
-        
+
     // Fragment shader for drawing colored lines.
     const char* fs_str = 
     {
@@ -767,7 +1084,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
         "   gl_FragColor = v_color;\n"
         "}"
     };
-        
+
     Effect* effect = Effect::createFromSource(vs_str, fs_str);
     Material* material = Material::create(effect);
     material->getStateBlock()->setDepthTest(true);
@@ -778,7 +1095,7 @@ PhysicsController::DebugDrawer::DebugDrawer()
         VertexFormat::Element(VertexFormat::COLOR, 4),
     };
     _meshBatch = MeshBatch::create(VertexFormat(elements, 2), Mesh::LINES, material, false);
-    
+
     SAFE_RELEASE(material);
     SAFE_RELEASE(effect);
 }
@@ -804,7 +1121,7 @@ void PhysicsController::DebugDrawer::end()
 void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor)
 {
     static DebugDrawer::DebugVertex fromVertex, toVertex;
-    
+
     fromVertex.x = from.getX();
     fromVertex.y = from.getY();
     fromVertex.z = from.getZ();
@@ -855,16 +1172,4 @@ int	PhysicsController::DebugDrawer::getDebugMode() const
     return _mode;
 }
 
-PhysicsController::PhysicsCollisionShape::PhysicsCollisionShape(btCollisionShape* shape)
-    : _shape(shape)
-{
-    // Assign user pointer to allow efficient lookup of PhysicsCollisionShape from bullet object
-    shape->setUserPointer(this);
-}
-
-PhysicsController::PhysicsCollisionShape::~PhysicsCollisionShape()
-{
-    SAFE_DELETE(_shape);
-}
-
 }

+ 25 - 59
gameplay/src/PhysicsController.h

@@ -7,12 +7,12 @@
 #include "PhysicsHingeConstraint.h"
 #include "PhysicsSocketConstraint.h"
 #include "PhysicsSpringConstraint.h"
-#include "PhysicsRigidBody.h"
-#include "PhysicsCharacter.h"
+#include "PhysicsCollisionObject.h"
+#include "MeshBatch.h"
 
 namespace gameplay
 {
-    
+
 /**
  * Defines a class for controlling game physics.
  */
@@ -23,6 +23,7 @@ class PhysicsController : public btCollisionWorld::ContactResultCallback
     friend class PhysicsRigidBody;
     friend class PhysicsCharacter;
     friend class PhysicsCollisionObject;
+    friend class PhysicsGhostObject;
 
 public:
 
@@ -62,42 +63,6 @@ public:
      */
     void addStatusListener(PhysicsController::Listener* listener);
 
-    /**
-     * Creates a new PhysicsCharacter.
-     *
-     * The created character is added to the physics world and automatically receives
-     * physics updates to handle interactions and collisions between the character
-     * and other physics objects in the world. The character will continue to receive
-     * updates until it is destroyed via the destroyCharacter(PhysicsCharacter*) method.
-     *
-     * The node may point to any node in the scene that you wish to control as a character.
-     * When a PhysicsCharacter is created for a particular node, the game will normally
-     * perform all movement directly through the PhysicsCharacter interface and not through
-     * the node itself.
-     *
-     * The radius, height and center parameters define a capsule volume that is used
-     * to represent the character in the physics world. All collision handling is 
-     * performed using this capsule.
-     *
-     * Note that PhysicsCharacter should not be mixed with rigid bodies. Therefore, you 
-     * should ensure that the node (and any of its children) used to create the
-     * PhysicsCharacter does not have any rigid bodies assigned. Doing so will cause
-     * unexpected results.
-     *
-     * @param node Scene node that represents the character.
-     * @param radius Radius of capsule volume used for character collisions.
-     * @param height Height of the capsule volume used for character collisions.
-     * @param center Center point of the capsule volume for the character.
-     */
-    PhysicsCharacter* createCharacter(Node* node, float radius, float height, const Vector3& center = Vector3::zero());
-
-    /**
-     * Destroys a PhysicsCharacter and removes it from the physics world.
-     *
-     * @param character PhysicsCharacter to destroy.
-     */
-    void destroyCharacter(PhysicsCharacter* character);
-
     /**
      * Creates a fixed constraint.
      * 
@@ -264,16 +229,7 @@ private:
         int _status;
     };
 
-    // Wraps Bullet collision shapes (used for implementing shape caching).
-    struct PhysicsCollisionShape : public Ref
-    {
-        PhysicsCollisionShape(btCollisionShape* shape);
-        ~PhysicsCollisionShape();
-
-        btCollisionShape* _shape;
-    };
-
-    /**
+	/**
      * Constructor.
      */
     PhysicsController();
@@ -322,21 +278,31 @@ private:
     
     // Gets the corresponding GamePlay object for the given Bullet object.
     PhysicsCollisionObject* getCollisionObject(const btCollisionObject* collisionObject) const;
+
+	// Creates a collision shape for the given node and gameplay shape definition.
+	// Populates 'centerOfMassOffset' with the correct calculated center of mass offset.
+	PhysicsCollisionShape* createShape(Node* node, const PhysicsCollisionShape::Definition& shape, Vector3* centerOfMassOffset);
     
-    // Creates a box collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createBox(const Vector3& min, const Vector3& max, const Vector3& scale);
+    // Creates a box collision shape.
+    PhysicsCollisionShape* createBox(const Vector3& extents, const Vector3& scale);
+
+	// Creates a sphere collision shape.
+    PhysicsCollisionShape* createSphere(float radius, const Vector3& scale);
+
+    // Creates a capsule collision shape.
+    PhysicsCollisionShape* createCapsule(float radius, float height, const Vector3& scale);
 
-    // Creates a capsule collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createCapsule(float radius, float height);
+	// Creates a heightfield collision shape.
+    PhysicsCollisionShape* createHeightfield(Node* node, Image* image, Vector3* centerOfMassOffset);
 
-    // Creates a sphere collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createSphere(float radius, const Vector3& scale);
+    // Creates a triangle mesh collision shape.
+    PhysicsCollisionShape* createMesh(Mesh* mesh, const Vector3& scale);
 
-    // Creates a triangle mesh collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createMesh(PhysicsRigidBody* body, const Vector3& scale);
+	// Destroys a collision shape created through PhysicsController
+	void destroyShape(PhysicsCollisionShape* shape);
 
-    // Creates a heightfield collision shape to be used in the creation of a rigid body.
-    btCollisionShape* createHeightfield(int width, int height, void* heightfieldData, float minHeight, float maxHeight);
+	// Helper function for calculating heights from heightmap (image) or heightfield data.
+	static float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y);
 
     // Sets up the given constraint for the given two rigid bodies.
     void addConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint);

+ 60 - 0
gameplay/src/PhysicsGhostObject.cpp

@@ -0,0 +1,60 @@
+#include "Base.h"
+#include "PhysicsGhostObject.h"
+#include "Node.h"
+#include "Game.h"
+
+namespace gameplay
+{
+
+PhysicsGhostObject::PhysicsGhostObject(Node* node, const PhysicsCollisionShape::Definition& shape)
+    : PhysicsCollisionObject(node), _ghostObject(NULL)
+{
+	Vector3 centerOfMassOffset;
+    PhysicsController* physicsController = Game::getInstance()->getPhysicsController();
+
+    // Create and set the collision shape for the ghost object.
+	_collisionShape = physicsController->createShape(node, shape, &centerOfMassOffset);
+
+	// Create the ghost object.
+    _ghostObject = bullet_new<btPairCachingGhostObject>();
+	_ghostObject->setCollisionShape(_collisionShape->getShape<btCollisionShape>());
+
+    // Initialize a physics motion state object for syncing the transform.
+	_motionState = new PhysicsMotionState(_node, &centerOfMassOffset);
+    _motionState->getWorldTransform(_ghostObject->getWorldTransform());
+
+    // Add the ghost object to the physics world.
+    physicsController->addCollisionObject(this);
+
+	_node->addListener(this);
+}
+
+PhysicsGhostObject::~PhysicsGhostObject()
+{
+	_node->removeListener(this);
+
+    Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
+
+    SAFE_DELETE(_ghostObject);
+}
+
+PhysicsCollisionObject::Type PhysicsGhostObject::getType() const
+{
+    return GHOST_OBJECT;
+}
+
+btCollisionObject* PhysicsGhostObject::getCollisionObject() const
+{
+    return _ghostObject;
+}
+
+void PhysicsGhostObject::transformChanged(Transform* transform, long cookie)
+{
+    // Update the motion state with the transform from the node.
+    _motionState->updateTransformFromNode();
+
+    // Update the transform on the ghost object.
+    _motionState->getWorldTransform(_ghostObject->getWorldTransform());
+}
+
+}

+ 59 - 0
gameplay/src/PhysicsGhostObject.h

@@ -0,0 +1,59 @@
+#ifndef PHYSICSGHOSTOBJECT_H_
+#define PHYSICSGHOSTOBJECT_H_
+
+#include "PhysicsCollisionObject.h"
+#include "PhysicsRigidBody.h"
+#include "Transform.h"
+
+namespace gameplay
+{
+
+class PhysicsMotionState;
+
+/**
+ * Defines a class for physics ghost objects.
+ */
+class PhysicsGhostObject : public PhysicsCollisionObject, public Transform::Listener
+{
+    friend class Node;
+
+public:
+
+    /**
+     * @see PhysicsCollisionObject#getType
+     */
+    PhysicsCollisionObject::Type getType() const;
+
+	/**
+     * Used to synchronize the transform between GamePlay and Bullet.
+     */
+    void transformChanged(Transform* transform, long cookie);
+
+protected:
+
+    /**
+     * @see PhysicsCollisionObject::getCollisionObject
+     */
+    btCollisionObject* getCollisionObject() const;
+
+protected:
+
+    /**
+     * Constructor.
+     * 
+     * @param node The node to attach the ghost object to.
+     * @param shape The collision shape definition for the ghost object.
+     */
+	PhysicsGhostObject(Node* node, const PhysicsCollisionShape::Definition& shape);
+
+    /**
+     * Destructor.
+     */
+    virtual ~PhysicsGhostObject();
+
+    btPairCachingGhostObject* _ghostObject;
+};
+
+}
+
+#endif

+ 2 - 1
gameplay/src/PhysicsMotionState.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "PhysicsMotionState.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -22,7 +23,7 @@ PhysicsMotionState::~PhysicsMotionState()
 
 void PhysicsMotionState::getWorldTransform(btTransform &transform) const
 {
-    if (_node->getRigidBody() && _node->getRigidBody()->isKinematic())
+	if (_node->getCollisionObject() && _node->getCollisionObject()->isKinematic())
         updateTransformFromNode();
 
     transform = _centerOfMassOffset.inverse() * _worldTransform;

+ 7 - 4
gameplay/src/PhysicsMotionState.h

@@ -1,12 +1,13 @@
 #ifndef PHYSICSMOTIONSTATE_H_
 #define PHYSICSMOTIONSTATE_H_
 
-#include "Node.h"
-#include "PhysicsRigidBody.h"
+#include "Vector3.h"
 
 namespace gameplay
 {
 
+class Node;
+
 /**
  * Interface between GamePlay and Bullet to keep object transforms synchronized properly.
  * 
@@ -14,8 +15,10 @@ namespace gameplay
  */
 class PhysicsMotionState : public btMotionState
 {
-    friend class PhysicsRigidBody;
-    friend class PhysicsCharacter;
+	friend class PhysicsCollisionObject;
+	friend class PhysicsRigidBody;
+	friend class PhysicsGhostObject;
+	friend class PhysicsCharacter;
     friend class PhysicsConstraint;
 
 protected:

+ 236 - 376
gameplay/src/PhysicsRigidBody.cpp

@@ -1,217 +1,81 @@
 #include "Base.h"
+#include "PhysicsRigidBody.h"
+#include "PhysicsMotionState.h"
+#include "PhysicsController.h"
 #include "Game.h"
 #include "Image.h"
-#include "PhysicsController.h"
-#include "PhysicsMotionState.h"
-#include "PhysicsRigidBody.h"
+#include "MeshPart.h"
+#include "Node.h"
 
 namespace gameplay
 {
 
-// Internal values used for creating mesh, heightfield, and capsule rigid bodies.
-#define SHAPE_MESH ((PhysicsRigidBody::ShapeType)(PhysicsRigidBody::SHAPE_NONE + 1))
-#define SHAPE_HEIGHTFIELD ((PhysicsRigidBody::ShapeType)(PhysicsRigidBody::SHAPE_NONE + 2))
-#define SHAPE_CAPSULE ((PhysicsRigidBody::ShapeType)(PhysicsRigidBody::SHAPE_NONE + 3))
-
-// Helper function for calculating heights from heightmap (image) or heightfield data.
-static float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y);
-
-PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::ShapeType type, float mass, 
-    float friction, float restitution, float linearDamping, float angularDamping)
-        : _shape(NULL), _body(NULL), _node(node), _angularVelocity(NULL),
-        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
-        _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
+PhysicsRigidBody::PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Definition& shape, const Parameters& parameters)
+        : PhysicsCollisionObject(node), _body(NULL), _mass(parameters.mass), _constraints(NULL)
 {
-    // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
-    Vector3 scale;
-    node->getWorldMatrix().getScale(&scale);
+	// Create our collision sh ape
+	Vector3 centerOfMassOffset;
+	_collisionShape = Game::getInstance()->getPhysicsController()->createShape(node, shape, &centerOfMassOffset);
 
-    switch (type)
-    {
-        case SHAPE_BOX:
-        {
-            const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
-            _shape = Game::getInstance()->getPhysicsController()->createBox(box.min, box.max, scale);
-            break;
-        }
-        case SHAPE_SPHERE:
-        {
-            const BoundingSphere& sphere = node->getModel()->getMesh()->getBoundingSphere();
-            _shape = Game::getInstance()->getPhysicsController()->createSphere(sphere.radius, scale);
-            break;
-        }
-        case SHAPE_MESH:
-        {
-            _shape = Game::getInstance()->getPhysicsController()->createMesh(this, scale);
-            break;
-        }
-    }
-
-    // Use the center of the bounding sphere as the center of mass offset.
-    Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
-    c.x *= scale.x;
-    c.y *= scale.y;
-    c.z *= scale.z;
-    c.negate();
-
-    // Create the Bullet rigid body (we don't apply center of mass offsets on mesh rigid bodies).
-    if (c.lengthSquared() > MATH_EPSILON && type != SHAPE_MESH)
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
-    else
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
-
-    // Add the rigid body to the physics world.
-    Game::getInstance()->getPhysicsController()->addCollisionObject(this);
-}
-
-PhysicsRigidBody::PhysicsRigidBody(Node* node, Image* image, float mass,
-    float friction, float restitution, float linearDamping, float angularDamping)
-        : _shape(NULL), _body(NULL), _node(node), _angularVelocity(NULL),
-        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
-        _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
-{
-    // Get the width, length and minimum and maximum height of the heightfield.
-    const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
-    float width = box.max.x - box.min.x;
-    float minHeight = box.min.y;
-    float maxHeight = box.max.y;
-    float length = box.max.z - box.min.z;
-
-    // Get the size in bytes of a pixel (we ensure that the image's
-    // pixel format is actually supported before calling this constructor).
-    unsigned int pixelSize = 0;
-    switch (image->getFormat())
-    {
-        case Image::RGB:
-            pixelSize = 3;
-            break;
-        case Image::RGBA:
-            pixelSize = 4;
-            break;
-    }
-
-    // Calculate the heights for each pixel.
-    float* data = new float[image->getWidth() * image->getHeight()];
-    for (unsigned int x = 0; x < image->getWidth(); x++)
-    {
-        for (unsigned int y = 0; y < image->getHeight(); y++)
-        {
-            data[x + y * image->getWidth()] = ((((float)image->getData()[(x + y * image->getHeight()) * pixelSize + 0]) +
-                ((float)image->getData()[(x + y * image->getHeight()) * pixelSize + 1]) +
-                ((float)image->getData()[(x + y * image->getHeight()) * pixelSize + 2])) / 768.0f) * (maxHeight - minHeight) + minHeight;
-        }
-    }
+	// Create motion state object
+	_motionState = new PhysicsMotionState(node, (centerOfMassOffset.lengthSquared() > MATH_EPSILON) ? &centerOfMassOffset : NULL);
 
-    // Generate the heightmap data needed for physics (one height per world unit).
-    unsigned int sizeWidth = width;
-    unsigned int sizeHeight = length;
-    _width = sizeWidth + 1;
-    _height = sizeHeight + 1;
-    _heightfieldData = new float[_width * _height];
-    unsigned int heightIndex = 0;
-    float widthImageFactor = (float)(image->getWidth() - 1) / sizeWidth;
-    float heightImageFactor = (float)(image->getHeight() - 1) / sizeHeight;
-    float x = 0.0f;
-    float z = 0.0f;
-    for (unsigned int row = 0, z = 0.0f; row <= sizeHeight; row++, z += 1.0f)
-    {
-        for (unsigned int col = 0, x = 0.0f; col <= sizeWidth; col++, x += 1.0f)
-        {
-            heightIndex = row * _width + col;
-            _heightfieldData[heightIndex] = calculateHeight(data, image->getWidth(), image->getHeight(), x * widthImageFactor, (sizeHeight - z) * heightImageFactor);
-        }
-    }
-    SAFE_DELETE_ARRAY(data);
+    // If the mass is non-zero, then the object is dynamic so we calculate the local 
+    // inertia. However, if the collision shape is a triangle mesh, we don't calculate 
+    // inertia since Bullet doesn't currently support this.
+    btVector3 localInertia(0.0, 0.0, 0.0);
+	if (parameters.mass != 0.0 && _collisionShape->getType() != PhysicsCollisionShape::SHAPE_MESH)
+		_collisionShape->getShape<btCollisionShape>()->calculateLocalInertia(parameters.mass, localInertia);
 
-    // Create the heightfield collision shape.
-    _shape = Game::getInstance()->getPhysicsController()->createHeightfield(_width, _height, _heightfieldData, minHeight, maxHeight);
+    // Create the Bullet physics rigid body object.
+    btRigidBody::btRigidBodyConstructionInfo rbInfo(parameters.mass, _motionState, _collisionShape->getShape<btCollisionShape>(), localInertia);
+    rbInfo.m_friction = parameters.friction;
+    rbInfo.m_restitution = parameters.restitution;
+    rbInfo.m_linearDamping = parameters.linearDamping;
+    rbInfo.m_angularDamping = parameters.angularDamping;
 
-    // Offset the heightmap's center of mass according to the way that Bullet calculates the origin 
-    // of its heightfield collision shape; see documentation for the btHeightfieldTerrainShape for more info.
-    Vector3 s;
-    node->getWorldMatrix().getScale(&s);
-    Vector3 c (0.0f, -(maxHeight - (0.5f * (maxHeight - minHeight))) / s.y, 0.0f);
+	// Create + assign the new bullet rigid body object.
+	_body = bullet_new<btRigidBody>(rbInfo);
 
-    // Create the Bullet rigid body.
-    if (c.lengthSquared() > MATH_EPSILON)
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
-    else
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
+	// Set other initially defined properties.
+    setKinematic(parameters.kinematic);
+	setAnisotropicFriction(parameters.anisotropicFriction);
+	setGravity(parameters.gravity);
 
-    // Add the rigid body to the physics world.
+    // Add ourself to the physics world.
     Game::getInstance()->getPhysicsController()->addCollisionObject(this);
 
-    // Add the rigid body as a listener on the node's transform.
-    _node->addListener(this);
-}
-
-PhysicsRigidBody::PhysicsRigidBody(Node* node, float radius, float height, float mass, float friction,
-    float restitution, float linearDamping, float angularDamping)
-        : _shape(NULL), _body(NULL), _node(node), _angularVelocity(NULL),
-        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
-        _indexData(NULL), _heightfieldData(NULL), _inverse(NULL), _inverseIsDirty(true)
-{
-    // Get the node's world scale (we need to apply this during creation since rigid bodies don't scale dynamically).
-    Vector3 scale;
-    node->getWorldMatrix().getScale(&scale);
-
-    // Create the capsule collision shape.
-    _shape = Game::getInstance()->getPhysicsController()->createCapsule(radius, height);
-
-    // Use the center of the bounding sphere as the center of mass offset.
-    Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
-    c.x *= scale.x;
-    c.y *= scale.y;
-    c.z *= scale.z;
-    c.negate();
-
-    // Create the Bullet rigid body.
-    if (c.lengthSquared() > MATH_EPSILON)
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
-    else
-        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
-
-    // Add the rigid body to the physics world.
-    Game::getInstance()->getPhysicsController()->addCollisionObject(this);
+	if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+	{
+		// Add a listener on the node's transform so we can track dirty changes to calculate
+		// an inverse matrix for transforming heightfield points between world and local space.
+		_node->addListener(this);
+	}
 }
 
 PhysicsRigidBody::~PhysicsRigidBody()
 {
-    // Clean up all constraints linked to this rigid body.
-    PhysicsConstraint* ptr = NULL;
-    while (_constraints.size() > 0)
-    {
-        ptr = _constraints.back();
-        _constraints.pop_back();
-        SAFE_DELETE(ptr);
-    }
-
+	// Clean up all constraints linked to this rigid body.
+	if (_constraints)
+	{
+		for (unsigned int i = 0; i < _constraints->size(); ++i)
+		{
+			SAFE_DELETE((*_constraints)[i]);
+		}
+		SAFE_DELETE(_constraints);
+	}
+
+	// Remove collision object from physics controller
     Game::getInstance()->getPhysicsController()->removeCollisionObject(this);
 
     // Clean up the rigid body and its related objects.
-    if (_body)
-    {
-        if (_body->getMotionState())
-            delete _body->getMotionState();
-        SAFE_DELETE(_body);
-    }
+	SAFE_DELETE(_body);
 
-    SAFE_DELETE(_angularVelocity);
-    SAFE_DELETE(_anisotropicFriction);
-    SAFE_DELETE(_gravity);
-    SAFE_DELETE(_linearVelocity);
-    SAFE_DELETE_ARRAY(_vertexData);
-    for (unsigned int i = 0; i < _indexData.size(); i++)
-    {
-        SAFE_DELETE_ARRAY(_indexData[i]);
-    }
-    SAFE_DELETE_ARRAY(_heightfieldData);
-    SAFE_DELETE(_inverse);
-}
-
-Node* PhysicsRigidBody::getNode() const
-{
-    return _node;
+	// Unregister node listener (only registered for heihgtfield collision shape types)
+	if (_collisionShape->getType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+	{
+		_node->removeListener(this);
+	}
 }
 
 PhysicsCollisionObject::Type PhysicsRigidBody::getType() const
@@ -224,11 +88,6 @@ btCollisionObject* PhysicsRigidBody::getCollisionObject() const
     return _body;
 }
 
-btCollisionShape* PhysicsRigidBody::getCollisionShape() const
-{
-    return _shape;
-}
-
 void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativePosition)
 {
     // If the force is significant enough, activate the rigid body 
@@ -312,18 +171,13 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
     }
 
     // Set values to their defaults.
-    PhysicsRigidBody::ShapeType type = PhysicsRigidBody::SHAPE_NONE;
-    float mass = 0.0;
-    float friction = 0.5;
-    float restitution = 0.0;
-    float linearDamping = 0.0;
-    float angularDamping = 0.0;
-    bool kinematic = false;
-    Vector3* gravity = NULL;
-    Vector3* anisotropicFriction = NULL;
+	bool typeSpecified = false;
+	PhysicsCollisionShape::Type type;
+	Parameters parameters;
     const char* imagePath = NULL;
-    float radius = -1.0f;
-    float height = -1.0f;
+    float radius, height;
+	Vector3 center, min, max;
+	int bits = 0;
 
     // Load the defined properties.
     properties->rewind();
@@ -334,54 +188,53 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         {
             std::string typeStr = properties->getString();
             if (typeStr == "BOX")
-                type = SHAPE_BOX;
+                type = PhysicsCollisionShape::SHAPE_BOX;
             else if (typeStr == "SPHERE")
-                type = SHAPE_SPHERE;
+                type = PhysicsCollisionShape::SHAPE_SPHERE;
+			else if (typeStr == "CAPSULE")
+                type = PhysicsCollisionShape::SHAPE_CAPSULE;
+			else if (typeStr == "HEIGHTFIELD")
+                type = PhysicsCollisionShape::SHAPE_HEIGHTFIELD;
             else if (typeStr == "MESH")
-                type = SHAPE_MESH;
-            else if (typeStr == "HEIGHTFIELD")
-                type = SHAPE_HEIGHTFIELD;
-            else if (typeStr == "CAPSULE")
-                type = SHAPE_CAPSULE;
+                type = PhysicsCollisionShape::SHAPE_MESH;
             else
             {
                 WARN_VARG("Could not create rigid body; unsupported value for rigid body type: '%s'.", typeStr.c_str());
                 return NULL;
             }
+			typeSpecified = true;
         }
         else if (strcmp(name, "mass") == 0)
         {
-            mass = properties->getFloat();
+            parameters.mass = properties->getFloat();
         }
         else if (strcmp(name, "friction") == 0)
         {
-            friction = properties->getFloat();
+            parameters.friction = properties->getFloat();
         }
         else if (strcmp(name, "restitution") == 0)
         {
-            restitution = properties->getFloat();
+            parameters.restitution = properties->getFloat();
         }
         else if (strcmp(name, "linearDamping") == 0)
         {
-            linearDamping = properties->getFloat();
+            parameters.linearDamping = properties->getFloat();
         }
         else if (strcmp(name, "angularDamping") == 0)
         {
-            angularDamping = properties->getFloat();
+            parameters.angularDamping = properties->getFloat();
         }
         else if (strcmp(name, "kinematic") == 0)
         {
-            kinematic = properties->getBool();
+            parameters.kinematic = properties->getBool();
         }
-        else if (strcmp(name, "gravity") == 0)
+        else if (strcmp(name, "anisotropicFriction") == 0)
         {
-            gravity = new Vector3();
-            properties->getVector3(NULL, gravity);
+            properties->getVector3(NULL, &parameters.anisotropicFriction);
         }
-        else if (strcmp(name, "anisotropicFriction") == 0)
+        else if (strcmp(name, "gravity") == 0)
         {
-            anisotropicFriction = new Vector3();
-            properties->getVector3(NULL, anisotropicFriction);
+            properties->getVector3(NULL, &parameters.gravity);
         }
         else if (strcmp(name, "image") == 0)
         {
@@ -389,208 +242,215 @@ PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
         }
         else if (strcmp(name, "radius") == 0)
         {
-            radius = properties->getFloat();
+			radius = properties->getFloat();
+			bits |= 1;
         }
         else if (strcmp(name, "height") == 0)
         {
             height = properties->getFloat();
+			bits |= 2;
         }
+		else if (strcmp(name, "center") == 0)
+		{
+			properties->getVector3(NULL, &center);
+			bits |= 4;
+		}
+		else if (strcmp(name, "min") == 0)
+		{
+			properties->getVector3(NULL, &min);
+			bits |= 8;
+		}
+		else if (strcmp(name, "max") == 0)
+		{
+			properties->getVector3(NULL, &max);
+			bits |= 16;
+		}
     }
 
-    // If the rigid body type is equal to mesh, check that the node's mesh's primitive type is supported.
-    if (type == SHAPE_MESH)
-    {
-        Mesh* mesh = node->getModel()->getMesh();
-
-        switch (mesh->getPrimitiveType())
-        {
-        case Mesh::TRIANGLES:
-            break;
-        case Mesh::LINES:
-        case Mesh::LINE_STRIP:
-        case Mesh::POINTS:
-        case Mesh::TRIANGLE_STRIP:
-            WARN("Mesh rigid bodies are currently only supported on meshes with primitive type equal to TRIANGLES.");
-
-            SAFE_DELETE(gravity);
-            SAFE_DELETE(anisotropicFriction);
-            return NULL;
-        }
-    }
+	if (!typeSpecified)
+	{
+		WARN("Missing 'type' specifier for rigid body definition.");
+		return NULL;
+	}
+
+	PhysicsRigidBody* body = NULL;
+
+	switch (type)
+	{
+	case PhysicsCollisionShape::SHAPE_BOX:
+		if ((bits & 8/*min*/) || (bits & 16/*max*/))
+		{
+			// Explicitly defined box shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::box(min, max), parameters);
+		}
+		else
+		{
+			// Auto box shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::box(), parameters);
+		}
+		break;
+
+	case PhysicsCollisionShape::SHAPE_SPHERE:
+		if ((bits & 4/*center*/) || (bits & 1/*radius*/))
+		{
+			// Explicitly defined sphere shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::sphere(radius, center), parameters);
+		}
+		else
+		{
+			// Auto sphere shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::sphere(), parameters);
+		}
+		break;
+
+    case PhysicsCollisionShape::SHAPE_CAPSULE:
+		if ((bits & 1/*radius*/) || (bits & 2/*height*/))
+		{
+			// Explicitly defined capsule shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::capsule(radius, height, center), parameters);
+		}
+		else
+		{
+			// Auto capsule shape
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::capsule(), parameters);
+		}
+        break;
+
+    case PhysicsCollisionShape::SHAPE_HEIGHTFIELD:
+		{
+			if (imagePath == NULL)
+			{
+				WARN("Heightfield rigid body requires an image path.");
+				return NULL;
+			}
+
+            // Load the image data from the given file path.
+            Image* image = Image::create(imagePath);
+            if (!image)
+			{
+				WARN_VARG("Failed to load heightmap image: %s", imagePath);
+                return NULL;
+			}
 
-    // Create the rigid body.
-    PhysicsRigidBody* body = NULL;
-    switch (type)
-    {
-        case SHAPE_HEIGHTFIELD:
-            if (imagePath == NULL)
-            {
-                WARN("Heightfield rigid body requires an image path.");
-            }
-            else
+            // Ensure that the image's pixel format is supported.
+            switch (image->getFormat())
             {
-                // Load the image data from the given file path.
-                Image* image = Image::create(imagePath);
-                if (!image)
+                case Image::RGB:
+                case Image::RGBA:
+                    break;
+                default:
+                    WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
                     return NULL;
-
-                // Ensure that the image's pixel format is supported.
-                switch (image->getFormat())
-                {
-                    case Image::RGB:
-                    case Image::RGBA:
-                        break;
-                    default:
-                        WARN_VARG("Heightmap: pixel format is not supported: %d", image->getFormat());
-                        return NULL;
-                }
-
-                body = new PhysicsRigidBody(node, image, mass, friction, restitution, linearDamping, angularDamping);
-                SAFE_RELEASE(image);
-            }
-            break;
-        case SHAPE_CAPSULE:
-            if (radius == -1.0f || height == -1.0f)
-            {
-                WARN("Both 'radius' and 'height' must be specified for a capsule rigid body.");
             }
-            else
-            {
-                body = new PhysicsRigidBody(node, radius, height, mass, friction, restitution, linearDamping, angularDamping);
-            }
-            break;
-        default:
-            body = new PhysicsRigidBody(node, type, mass, friction, restitution, linearDamping, angularDamping);
-            break;
-    }
 
-    // Set any initially defined properties.
-    if (kinematic)
-        body->setKinematic(kinematic);
-    if (gravity)
-        body->setGravity(*gravity);
-    if (anisotropicFriction)
-        body->setAnisotropicFriction(*anisotropicFriction);
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::heightfield(image), parameters);
 
-    // Clean up any loaded properties that are on the heap.
-    SAFE_DELETE(gravity);
-    SAFE_DELETE(anisotropicFriction);
+			SAFE_RELEASE(image);
+		}
+        break;
+
+	case PhysicsCollisionShape::SHAPE_MESH:
+		{
+			// Mesh is required on node
+			Mesh* mesh = node->getModel() ? node->getModel()->getMesh() : NULL;
+			if (mesh == NULL)
+			{
+				WARN("Cannot create mesh rigid body for node without mode/mesh.");
+				return NULL;
+			}
+
+			body = new PhysicsRigidBody(node, PhysicsCollisionShape::mesh(mesh), parameters);
+		}
+		break;
+
+	default:
+		break;
+	}
 
     return body;
 }
 
+void PhysicsRigidBody::setKinematic(bool kinematic)
+{
+    if (kinematic)
+    {
+        _body->setCollisionFlags(_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
+        _body->setActivationState(DISABLE_DEACTIVATION);
+    }
+    else
+    {
+        _body->setCollisionFlags(_body->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT);
+        _body->setActivationState(ACTIVE_TAG);
+    }
+}
+
 float PhysicsRigidBody::getHeight(float x, float y) const
 {
     // This function is only supported for heightfield rigid bodies.
-    if (_shape->getShapeType() != TERRAIN_SHAPE_PROXYTYPE)
+	if (_collisionShape->getType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
     {
         WARN("Attempting to get the height of a non-heightfield rigid body.");
         return 0.0f;
     }
 
     // Calculate the correct x, y position relative to the heightfield data.
-    if (_inverseIsDirty)
+	if (_collisionShape->_shapeData.heightfieldData->inverseIsDirty)
     {
-        if (_inverse == NULL)
-            _inverse = new Matrix();
-
-        _node->getWorldMatrix().invert(_inverse);
-        _inverseIsDirty = false;
+		_node->getWorldMatrix().invert(&_collisionShape->_shapeData.heightfieldData->inverse);
+		_collisionShape->_shapeData.heightfieldData->inverseIsDirty = false;
     }
 
-    Vector3 v = (*_inverse) * Vector3(x, 0.0f, y);
-    x = (v.x + (0.5f * (_width - 1))) * _width / (_width - 1);
-    y = (v.z + (0.5f * (_height - 1))) * _height / (_height - 1);
+	float w = _collisionShape->_shapeData.heightfieldData->width;
+	float h = _collisionShape->_shapeData.heightfieldData->height;
+
+    Vector3 v = _collisionShape->_shapeData.heightfieldData->inverse * Vector3(x, 0.0f, y);
+    x = (v.x + (0.5f * (w - 1))) * w / (w - 1);
+    y = (v.z + (0.5f * (h - 1))) * h / (h - 1);
 
     // Check that the x, y position is within the bounds.
-    if (x < 0.0f || x > _width || y < 0.0f || y > _height)
+    if (x < 0.0f || x > w || y < 0.0f || y > h)
     {
-        WARN_VARG("Attempting to get height at point '%f, %f', which is outside the range of the heightfield with width %d and height %d.", x, y, _width, _height);
+        WARN_VARG("Attempting to get height at point '%f, %f', which is outside the range of the heightfield with width %d and height %d.", x, y, w, h);
         return 0.0f;
     }
 
-    return calculateHeight(_heightfieldData, _width, _height, x, y);
-}
-
-btRigidBody* PhysicsRigidBody::createRigidBodyInternal(btCollisionShape* shape, float mass, Node* node,
-                                                       float friction, float restitution, float linearDamping, float angularDamping, 
-                                                       const Vector3* centerOfMassOffset)
-{
-    // If the mass is non-zero, then the object is dynamic so we calculate the local 
-    // inertia. However, if the collision shape is a triangle mesh, we don't calculate 
-    // inertia since Bullet doesn't currently support this.
-    btVector3 localInertia(0.0, 0.0, 0.0);
-    if (mass != 0.0 && shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE)
-        shape->calculateLocalInertia(mass, localInertia);
-
-    // Create the Bullet physics rigid body object.
-    PhysicsMotionState* motionState = new PhysicsMotionState(node, centerOfMassOffset);
-    btRigidBody::btRigidBodyConstructionInfo rbInfo(mass, motionState, shape, localInertia);
-    rbInfo.m_friction = friction;
-    rbInfo.m_restitution = restitution;
-    rbInfo.m_linearDamping = linearDamping;
-    rbInfo.m_angularDamping = angularDamping;
-    btRigidBody* body = bullet_new<btRigidBody>(rbInfo);
-
-    return body;
+	return PhysicsController::calculateHeight(_collisionShape->_shapeData.heightfieldData->heightData, w, h, x, y);
 }
 
 void PhysicsRigidBody::addConstraint(PhysicsConstraint* constraint)
 {
-    _constraints.push_back(constraint);
+	if (_constraints == NULL)
+		_constraints = new std::vector<PhysicsConstraint*>();
+
+    _constraints->push_back(constraint);
 }
 
 void PhysicsRigidBody::removeConstraint(PhysicsConstraint* constraint)
 {
-    for (unsigned int i = 0; i < _constraints.size(); i++)
-    {
-        if (_constraints[i] == constraint)
-        {
-            _constraints.erase(_constraints.begin() + i);
-            break;
-        }
-    }
+	if (_constraints)
+	{
+		for (unsigned int i = 0; i < _constraints->size(); ++i)
+		{
+			if ((*_constraints)[i] == constraint)
+			{
+				_constraints->erase(_constraints->begin() + i);
+				break;
+			}
+		}
+	}
 }
 
 bool PhysicsRigidBody::supportsConstraints()
 {
-    return _shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE && _shape->getShapeType() != TERRAIN_SHAPE_PROXYTYPE;
+	return (getShapeType() != PhysicsCollisionShape::SHAPE_HEIGHTFIELD && getShapeType() != PhysicsCollisionShape::SHAPE_MESH);
 }
 
 void PhysicsRigidBody::transformChanged(Transform* transform, long cookie)
 {
-    _inverseIsDirty = true;
-}
-
-float calculateHeight(float* data, unsigned int width, unsigned int height, float x, float y)
-{
-    unsigned int x1 = x;
-    unsigned int y1 = y;
-    unsigned int x2 = x1 + 1;
-    unsigned int y2 = y1 + 1;
-    float tmp;
-    float xFactor = modf(x, &tmp);
-    float yFactor = modf(y, &tmp);
-    float xFactorI = 1.0f - xFactor;
-    float yFactorI = 1.0f - yFactor;
-
-    if (x2 >= width && y2 >= height)
-    {
-        return data[x1 + y1 * width];
-    }
-    else if (x2 >= width)
-    {
-        return data[x1 + y1 * width] * yFactorI + data[x1 + y2 * width] * yFactor;
-    }
-    else if (y2 >= height)
-    {
-        return data[x1 + y1 * width] * xFactorI + data[x2 + y1 * width] * xFactor;
-    }
-    else
-    {
-        return data[x1 + y1 * width] * xFactorI * yFactorI + data[x1 + y2 * width] * xFactorI * yFactor + 
-            data[x2 + y2 * width] * xFactor * yFactor + data[x2 + y1 * width] * xFactor * yFactorI;
-    }
+	if (getShapeType() == PhysicsCollisionShape::SHAPE_HEIGHTFIELD)
+	{
+		_collisionShape->_shapeData.heightfieldData->inverseIsDirty = true;
+	}
 }
 
 }

+ 153 - 183
gameplay/src/PhysicsRigidBody.h

@@ -30,204 +30,235 @@ class PhysicsRigidBody : public PhysicsCollisionObject, public Transform::Listen
 
 public:
 
-    /**
-     * Represents the different types of rigid bodies.
-     */
-    enum ShapeType
-    {
-        SHAPE_BOX,
-        SHAPE_SPHERE,
-        SHAPE_NONE,
-        SHAPE_MAX = 10
-    };
+	/**
+	 * Rigid body construction parameters.
+	 */
+	struct Parameters
+	{
+		/**
+		 * The mass of the rigid body, in kilograms.
+		 */
+		float mass;
+
+		/**
+		 * The friction of the rigid body (non-zero values give best simulation results).
+		 */
+		float friction;
+
+		/**
+		 * The restitution of the rigid body (this controls the bounciness of
+		 * the rigid body; use zero for best simulation results).
+		 */
+        float restitution;
+
+		/**
+		 * The percentage of linear velocity lost per second (between 0.0 and 1.0).
+		 */
+		float linearDamping;
+
+		/**
+		 * The percentage of angular velocity lost per second (between 0.0 and 1.0).
+		 */
+		float angularDamping;
+
+		/**
+		 * Whether the rigid body is kinematic.
+		 */
+		bool kinematic;
+
+		/**
+		 * The ansitropic friction term for the rigid body.
+		 */
+		Vector3 anisotropicFriction;
+
+		/**
+		 * The gravity acceleration factor for the rigid body.
+		 */
+		Vector3 gravity;
+
+		/**
+		 * Constructor.
+		 */
+		Parameters(float mass = 0.0f, float friction = 0.5f, float resititution = 0.0f,
+			float linearDamping = 0.0f, float angularDamping = 0.0f, bool kinematic = false,
+			const Vector3& anisotropicFriction = Vector3::one(), const Vector3& gravity = Vector3::zero())
+			: mass(mass), friction(friction), restitution(restitution), linearDamping(linearDamping), angularDamping(angularDamping),
+			  kinematic(kinematic), anisotropicFriction(anisotropicFriction), gravity(gravity)
+		{
+		}
+	};
 
     /**
      * @see PhysicsCollisionObject#getType
      */
     PhysicsCollisionObject::Type getType() const;
 
-    /**
-     * Applies the given force to the rigid body (optionally, from the given relative position).
-     * 
-     * @param force The force to be applied.
-     * @param relativePosition The relative position from which to apply the force.
-     */
-    void applyForce(const Vector3& force, const Vector3* relativePosition = NULL);
+	/**
+	 * Gets the rigid body's mass.
+	 *
+	 * @return The mass.
+	 */
+	inline float getMass() const;
 
     /**
-     * Applies the given force impulse to the rigid body (optionally, from the given relative position).
+     * Gets the rigid body's friction.
      * 
-     * @param impulse The force impulse to be applied.
-     * @param relativePosition The relative position from which to apply the force.
+     * @return The friction.
      */
-    void applyImpulse(const Vector3& impulse, const Vector3* relativePosition = NULL);
+    inline float getFriction() const;
 
     /**
-     * Applies the given torque to the rigid body.
+     * Sets the rigid body's friction.
      * 
-     * @param torque The torque to be applied.
+     * @param friction The friction.
      */
-    void applyTorque(const Vector3& torque);
+    inline void setFriction(float friction);
 
-    /**
-     * Applies the given torque impulse to the rigid body.
-     * 
-     * @param torque The torque impulse to be applied.
-     */
-    void applyTorqueImpulse(const Vector3& torque);
+	/**
+	 * Gets the rigid body's restitution.
+	 *
+	 * @return The restitution.
+	 */
+	inline float getRestitution() const;
 
     /**
-     * Gets the rigid body's angular damping.
+     * Sets the rigid body's restitution (or bounciness).
      * 
-     * @return The angular damping.
+     * @param restitution The restitution.
      */
-    inline float getAngularDamping() const;
+    inline void setRestitution(float restitution);
 
-    /**
-     * Gets the rigid body's angular velocity.
-     * 
-     * @return The angular velocity.
-     */
-    inline const Vector3& getAngularVelocity() const;
+	/**
+	 * Gets the rigid body's linear damping.
+	 *
+	 * @return The linear damping.
+	 */
+	inline float getLinearDamping() const;
 
-    /**
-     * Gets the rigid body's anisotropic friction.
-     * 
-     * @return The anisotropic friction.
-     */
-    inline const Vector3& getAnisotropicFriction() const;
+	/**
+	 * Gets the rigid body's angular damping.
+	 *
+	 * @return The angular damping.
+	 */
+	inline float getAngularDamping() const;
 
     /**
-     * Gets the rigid body's friction.
+     * Sets the rigid body's linear and angular damping.
      * 
-     * @return The friction.
+     * @param linearDamping The linear damping; between 0.0 (minimum) and 1.0 (maximum).
+     * @param angularDamping The angular damping; between 0.0 (minimum) and 1.0 (maximum).
      */
-    inline float getFriction() const;
+    inline void setDamping(float linearDamping, float angularDamping);
 
-    /**
-     * Gets the gravity that affects the rigid body (this can
-     * be different from the global gravity; @see #setGravity).
+	/**
+     * Gets the rigid body's linear velocity.
      * 
-     * @return The gravity.
+     * @return The linear velocity.
      */
-    inline const Vector3& getGravity() const;
+    inline Vector3 getLinearVelocity() const;
 
     /**
-     * Gets the height at the given point (only for rigid bodies of type HEIGHTFIELD).
+     * Sets the rigid body's linear velocity.
      * 
-     * @param x The x position.
-     * @param y The y position.
-     * @return The height at the given point.
+     * @param velocity The linear velocity.
      */
-    float getHeight(float x, float y) const;
+    inline void setLinearVelocity(const Vector3& velocity);
 
     /**
-     * Gets the rigid body's linear damping.
+     * Gets the rigid body's angular velocity.
      * 
-     * @return The linear damping.
+     * @return The angular velocity.
      */
-    inline float getLinearDamping() const;
+    inline Vector3 getAngularVelocity() const;
 
     /**
-     * Gets the rigid body's linear velocity.
+     * Sets the rigid body's angular velocity.
      * 
-     * @return The linear velocity.
+     * @param velocity The angular velocity.
      */
-    inline const Vector3& getLinearVelocity() const;
+    inline void setAngularVelocity(const Vector3& velocity);
 
     /**
-     * Gets the node that the rigid body is attached to.
+     * Gets the rigid body's anisotropic friction.
      * 
-     * @return The node.
-     *
-     * @see PhysicsCollisionObject::getNode.
+     * @return The anisotropic friction.
      */
-    Node* getNode() const;
+    inline Vector3 getAnisotropicFriction() const;
 
     /**
-     * Gets the rigid body's restitution.
+     * Sets the rigid body's anisotropic friction.
      * 
-     * @return The restitution.
+     * @param friction The anisotropic friction.
      */
-    inline float getRestitution() const;
+    inline void setAnisotropicFriction(const Vector3& friction);
 
     /**
-     * Gets whether the rigid body is a kinematic rigid body or not.
+     * Gets the gravity that affects the rigid body (this can
+     * be different from the global gravity; @see #setGravity).
      * 
-     * @return Whether the rigid body is kinematic or not.
-     */
-    inline bool isKinematic() const;
-
-    /**
-     * Gets whether the rigid body is a static rigid body or not.
-     *
-     * @return Whether the rigid body is static.
-     */
-    inline bool isStatic() const;
-
-    /**
-     * Gets whether the rigid body is a dynamic rigid body or not.
-     *
-     * @return Whether the rigid body is dynamic.
+     * @return The gravity.
      */
-    inline bool isDynamic() const;
+    inline Vector3 getGravity() const;
 
     /**
-     * Sets the rigid body's angular velocity.
+     * Sets the rigid body's gravity (this overrides the global gravity for this rigid body).
      * 
-     * @param velocity The angular velocity.
+     * @param gravity The gravity.
      */
-    inline void setAngularVelocity(const Vector3& velocity);
+    inline void setGravity(const Vector3& gravity);
 
     /**
-     * Sets the rigid body's anisotropic friction.
+     * Sets whether the rigid body is a kinematic rigid body or not.
      * 
-     * @param friction The anisotropic friction.
+     * @param kinematic Whether the rigid body is kinematic or not.
      */
-    inline void setAnisotropicFriction(const Vector3& friction);
+    void setKinematic(bool kinematic);
 
     /**
-     * Sets the rigid body's linear and angular damping.
+     * Gets the height at the given point (only for rigid bodies of type HEIGHTFIELD).
      * 
-     * @param linearDamping The linear damping; between 0.0 (minimum) and 1.0 (maximum).
-     * @param angularDamping The angular damping; between 0.0 (minimum) and 1.0 (maximum).
+     * @param x The x position.
+     * @param y The y position.
+     * @return The height at the given point, or zero if this is not a heightfield rigid body.
      */
-    inline void setDamping(float linearDamping, float angularDamping);
+    float getHeight(float x, float y) const;
 
     /**
-     * Sets the rigid body's friction.
-     * 
-     * @param friction The friction.
+     * Gets whether the rigid body is a static rigid body or not.
+     *
+     * @return Whether the rigid body is static.
      */
-    inline void setFriction(float friction);
+    bool isStatic() const;
 
     /**
-     * Sets the rigid body's gravity (this overrides the global gravity for this rigid body).
+     * Applies the given force to the rigid body (optionally, from the given relative position).
      * 
-     * @param gravity The gravity.
+     * @param force The force to be applied.
+     * @param relativePosition The relative position from which to apply the force.
      */
-    inline void setGravity(const Vector3& gravity);
+    void applyForce(const Vector3& force, const Vector3* relativePosition = NULL);
 
     /**
-     * Sets whether the rigid body is a kinematic rigid body or not.
+     * Applies the given force impulse to the rigid body (optionally, from the given relative position).
      * 
-     * @param kinematic Whether the rigid body is kinematic or not.
+     * @param impulse The force impulse to be applied.
+     * @param relativePosition The relative position from which to apply the force.
      */
-    inline void setKinematic(bool kinematic);
+    void applyImpulse(const Vector3& impulse, const Vector3* relativePosition = NULL);
 
     /**
-     * Sets the rigid body's linear velocity.
+     * Applies the given torque to the rigid body.
      * 
-     * @param velocity The linear velocity.
+     * @param torque The torque to be applied.
      */
-    inline void setLinearVelocity(const Vector3& velocity);
+    void applyTorque(const Vector3& torque);
 
     /**
-     * Sets the rigid body's restitution (or bounciness).
+     * Applies the given torque impulse to the rigid body.
      * 
-     * @param restitution The restitution.
+     * @param torque The torque impulse to be applied.
      */
-    inline void setRestitution(float restitution);
+    void applyTorqueImpulse(const Vector3& torque);
 
 protected:
 
@@ -236,11 +267,6 @@ protected:
      */
     btCollisionObject* getCollisionObject() const;
 
-    /**
-     * @see PhysicsCollisionObject::getCollisionShape
-     */
-    btCollisionShape* getCollisionShape() const;
-
 private:
 
     /**
@@ -248,49 +274,10 @@ private:
      * 
      * @param node The node to create a rigid body for; note that the node must have
      *      a model attached to it prior to creating a rigid body for it.
-     * @param type The type of rigid body to set.
-     * @param mass The mass of the rigid body, in kilograms.
-     * @param friction The friction of the rigid body (non-zero values give best simulation results).
-     * @param restitution The restitution of the rigid body (this controls the bounciness of
-     *      the rigid body; use zero for best simulation results).
-     * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
-     * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
+     * @param shape The rigid body shape construction information.
+     * @param parameters The rigid body construction parameters.
      */
-    PhysicsRigidBody(Node* node, PhysicsRigidBody::ShapeType type, float mass, float friction = 0.5,
-        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
-
-    /**
-     * Creates a heightfield rigid body.
-     * 
-     * @param node The node to create the heightfield rigid body for; note that the node must have
-     *      a model attached to it prior to creating a rigid body for it.
-     * @param image The heightfield image.
-     * @param mass The mass of the rigid body, in kilograms.
-     * @param friction The friction of the rigid body (non-zero values give best simulation results).
-     * @param restitution The restitution of the rigid body (this controls the bounciness of
-     *      the rigid body; use zero for best simulation results).
-     * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
-     * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
-     */
-    PhysicsRigidBody(Node* node, Image* image, float mass, float friction = 0.5,
-        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
-
-    /**
-     * Creates a capsule rigid body.
-     * 
-     * @param node The node to create the heightfield rigid body for; note that the node must have
-     *      a model attached to it prior to creating a rigid body for it.
-     * @param radius The radius of the capsule.
-     * @param height The height of the cylindrical part of the capsule (not including the ends).
-     * @param mass The mass of the rigid body, in kilograms.
-     * @param friction The friction of the rigid body (non-zero values give best simulation results).
-     * @param restitution The restitution of the rigid body (this controls the bounciness of
-     *      the rigid body; use zero for best simulation results).
-     * @param linearDamping The percentage of linear velocity lost per second (between 0.0 and 1.0).
-     * @param angularDamping The percentage of angular velocity lost per second (between 0.0 and 1.0).
-     */
-    PhysicsRigidBody(Node* node, float radius, float height, float mass, float friction = 0.5,
-        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
+	PhysicsRigidBody(Node* node, const PhysicsCollisionShape::Definition& shape, const Parameters& parameters);
 
     /**
      * Destructor.
@@ -322,39 +309,22 @@ private:
      */
     static PhysicsRigidBody* create(Node* node, Properties* properties);
 
-    // Creates the underlying Bullet Physics rigid body object
-    // for a PhysicsRigidBody object using the given parameters.
-    static btRigidBody* createRigidBodyInternal(btCollisionShape* shape, float mass, Node* node,
-                                                float friction, float restitution, float linearDamping, float angularDamping,
-                                                const Vector3* centerOfMassOffset = NULL);
-
     // Adds a constraint to this rigid body.
     void addConstraint(PhysicsConstraint* constraint);
 
     // Removes a constraint from this rigid body (used by the constraint destructor).
     void removeConstraint(PhysicsConstraint* constraint);
-    
+
     // Whether or not the rigid body supports constraints fully.
     bool supportsConstraints();
 
     // Used for implementing getHeight() when the heightfield has a transform that can change.
     void transformChanged(Transform* transform, long cookie);
 
-    btCollisionShape* _shape;
     btRigidBody* _body;
-    Node* _node;
-    std::vector<PhysicsConstraint*> _constraints;
-    mutable Vector3* _angularVelocity;
-    mutable Vector3* _anisotropicFriction;
-    mutable Vector3* _gravity;
-    mutable Vector3* _linearVelocity;
-    float* _vertexData;
-    std::vector<unsigned char*> _indexData;
-    float* _heightfieldData;
-    unsigned int _width;
-    unsigned int _height;
-    mutable Matrix* _inverse;
-    mutable bool _inverseIsDirty;
+	float _mass;
+	std::vector<PhysicsConstraint*>* _constraints;
+
 };
 
 }

+ 32 - 67
gameplay/src/PhysicsRigidBody.inl

@@ -4,44 +4,29 @@
 namespace gameplay
 {
 
-inline float PhysicsRigidBody::getAngularDamping() const
+inline float PhysicsRigidBody::getMass() const
 {
-    return _body->getAngularDamping();
+	return _mass;
 }
 
-inline const Vector3& PhysicsRigidBody::getAngularVelocity() const
+inline float PhysicsRigidBody::getFriction() const
 {
-    if (!_angularVelocity)
-        _angularVelocity = new Vector3();
-
-    const btVector3& v = _body->getAngularVelocity();
-    _angularVelocity->set(v.x(), v.y(), v.z());
-    return *_angularVelocity;
+    return _body->getFriction();
 }
 
-inline const Vector3& PhysicsRigidBody::getAnisotropicFriction() const
+inline void PhysicsRigidBody::setFriction(float friction)
 {
-    if (!_anisotropicFriction)
-        _anisotropicFriction = new Vector3();
-
-    const btVector3& af = _body->getAnisotropicFriction();
-    _anisotropicFriction->set(af.x(), af.y(), af.z());
-    return *_anisotropicFriction;
+    _body->setFriction(friction);
 }
 
-inline float PhysicsRigidBody::getFriction() const
+inline float PhysicsRigidBody::getRestitution() const
 {
-    return _body->getFriction();
+    return _body->getRestitution();
 }
 
-inline const Vector3& PhysicsRigidBody::getGravity() const
+inline void PhysicsRigidBody::setRestitution(float restitution)
 {
-    if (!_gravity)
-        _gravity = new Vector3();
-
-    const btVector3& g = _body->getGravity();
-    _gravity->set(g.x(), g.y(), g.z());
-    return *_gravity;
+    _body->setRestitution(restitution);
 }
 
 inline float PhysicsRigidBody::getLinearDamping() const
@@ -49,34 +34,31 @@ inline float PhysicsRigidBody::getLinearDamping() const
     return _body->getLinearDamping();
 }
 
-inline const Vector3& PhysicsRigidBody::getLinearVelocity() const
+inline float PhysicsRigidBody::getAngularDamping() const
 {
-    if (!_linearVelocity)
-        _linearVelocity = new Vector3();
-
-    const btVector3& v = _body->getLinearVelocity();
-    _linearVelocity->set(v.x(), v.y(), v.z());
-    return *_linearVelocity;
+    return _body->getAngularDamping();
 }
 
-inline float PhysicsRigidBody::getRestitution() const
+inline void PhysicsRigidBody::setDamping(float linearDamping, float angularDamping)
 {
-    return _body->getRestitution();
+    _body->setDamping(linearDamping, angularDamping);
 }
 
-inline bool PhysicsRigidBody::isKinematic() const
+inline Vector3 PhysicsRigidBody::getLinearVelocity() const
 {
-    return _body->isKinematicObject();
+    const btVector3& v = _body->getLinearVelocity();
+	return Vector3(v.x(), v.y(), v.z());
 }
 
-inline bool PhysicsRigidBody::isStatic() const
+inline void PhysicsRigidBody::setLinearVelocity(const Vector3& velocity)
 {
-    return _body->isStaticObject();
+    _body->setLinearVelocity(BV(velocity));
 }
 
-inline bool PhysicsRigidBody::isDynamic() const
+inline Vector3 PhysicsRigidBody::getAngularVelocity() const
 {
-    return !_body->isStaticOrKinematicObject();
+    const btVector3& v = _body->getAngularVelocity();
+    return Vector3(v.x(), v.y(), v.z());
 }
 
 inline void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)
@@ -84,19 +66,21 @@ inline void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)
     _body->setAngularVelocity(BV(velocity));
 }
 
-inline void PhysicsRigidBody::setAnisotropicFriction(const Vector3& friction)
+inline Vector3 PhysicsRigidBody::getAnisotropicFriction() const
 {
-    _body->setAnisotropicFriction(BV(friction));
+    const btVector3& af = _body->getAnisotropicFriction();
+    return Vector3(af.x(), af.y(), af.z());
 }
 
-inline void PhysicsRigidBody::setDamping(float linearDamping, float angularDamping)
+inline void PhysicsRigidBody::setAnisotropicFriction(const Vector3& friction)
 {
-    _body->setDamping(linearDamping, angularDamping);
+    _body->setAnisotropicFriction(BV(friction));
 }
 
-inline void PhysicsRigidBody::setFriction(float friction)
+inline Vector3 PhysicsRigidBody::getGravity() const
 {
-    _body->setFriction(friction);
+    const btVector3& g = _body->getGravity();
+    return Vector3(g.x(), g.y(), g.z());
 }
 
 inline void PhysicsRigidBody::setGravity(const Vector3& gravity)
@@ -104,28 +88,9 @@ inline void PhysicsRigidBody::setGravity(const Vector3& gravity)
     _body->setGravity(BV(gravity));
 }
 
-inline void PhysicsRigidBody::setKinematic(bool kinematic)
-{
-    if (kinematic)
-    {
-        _body->setCollisionFlags(_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT);
-        _body->setActivationState(DISABLE_DEACTIVATION);
-    }
-    else
-    {
-        _body->setCollisionFlags(_body->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT);
-        _body->setActivationState(ACTIVE_TAG);
-    }
-}
-
-inline void PhysicsRigidBody::setLinearVelocity(const Vector3& velocity)
-{
-    _body->setLinearVelocity(BV(velocity));
-}
-
-inline void PhysicsRigidBody::setRestitution(float restitution)
+inline bool PhysicsRigidBody::isStatic() const
 {
-    _body->setRestitution(restitution);
+    return _body->isStaticObject();
 }
 
 }

+ 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

+ 6 - 0
gameplay/src/PlatformWin32.cpp

@@ -4,6 +4,7 @@
 #include "Platform.h"
 #include "FileSystem.h"
 #include "Game.h"
+#include "Form.h"
 #include <GL/wglew.h>
 
 // Default to 720p
@@ -681,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);
+}
     
 }
 

+ 27 - 4
gameplay/src/RenderState.cpp

@@ -3,6 +3,7 @@
 #include "Node.h"
 #include "Pass.h"
 #include "Technique.h"
+#include "Node.h"
 
 // Render state override bits
 #define RS_BLEND 1
@@ -21,10 +22,6 @@ RenderState::RenderState()
 {
 }
 
-RenderState::RenderState(const RenderState& copy)
-{
-}
-
 RenderState::~RenderState()
 {
     SAFE_RELEASE(_state);
@@ -311,6 +308,32 @@ RenderState* RenderState::getTopmost(RenderState* below)
     return NULL;
 }
 
+void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& 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

@@ -8,6 +8,7 @@ namespace gameplay
 
 class MaterialParameter;
 class Node;
+class NodeCloneContext;
 class Pass;
 
 class RenderState : public Ref
@@ -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, NodeCloneContext& 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;

+ 166 - 1
gameplay/src/Scene.cpp

@@ -2,11 +2,13 @@
 #include "AudioListener.h"
 #include "Scene.h"
 #include "SceneLoader.h"
+#include "MeshSkin.h"
+#include "Joint.h"
 
 namespace gameplay
 {
 
-Scene::Scene() : _activeCamera(NULL), _firstNode(NULL), _lastNode(NULL), _nodeCount(0), _bindAudioListenerToCamera(true)
+Scene::Scene() : _activeCamera(NULL), _firstNode(NULL), _lastNode(NULL), _nodeCount(0), _bindAudioListenerToCamera(true), _debugBatch(NULL)
 {
 }
 
@@ -288,4 +290,167 @@ void Scene::setAmbientColor(float red, float green, float blue)
     _ambientColor.set(red, green, blue);
 }
 
+Material* createDebugMaterial()
+{
+	// Vertex shader for drawing colored lines.
+	const char* vs_str = 
+	{
+		"uniform mat4 u_viewProjectionMatrix;\n"
+		"attribute vec4 a_position;\n"
+		"attribute vec4 a_color;\n"
+		"varying vec4 v_color;\n"
+		"void main(void) {\n"
+		"    v_color = a_color;\n"
+		"    gl_Position = u_viewProjectionMatrix * a_position;\n"
+		"}"
+	};
+
+	// Fragment shader for drawing colored lines.
+	const char* fs_str = 
+	{
+	#ifdef OPENGL_ES
+		"precision highp float;\n"
+	#endif
+		"varying vec4 v_color;\n"
+		"void main(void) {\n"
+		"   gl_FragColor = v_color;\n"
+		"}"
+	};
+
+	Effect* effect = Effect::createFromSource(vs_str, fs_str);
+	Material* material = Material::create(effect);
+	material->getStateBlock()->setDepthTest(true);
+
+	SAFE_RELEASE(effect);
+
+	return material;
+}
+
+struct DebugVertex
+{
+    float x, y, z;
+    float r, g, b, a;
+};
+
+void drawDebugLine(MeshBatch* batch, const Vector3& point1, const Vector3& point2, const Vector3& color)
+{
+    static DebugVertex verts[2];
+
+    verts[0].x = point1.x;
+    verts[0].y = point1.y;
+    verts[0].z = point1.z;
+	verts[0].r = color.x;
+    verts[0].g = color.y;
+    verts[0].b = color.z;
+    verts[0].a = 1.0f;
+
+    verts[1].x = point2.x;
+    verts[1].y = point2.y;
+    verts[1].z = point2.z;
+    verts[1].r = color.x;
+    verts[1].g = color.y;
+    verts[1].b = color.z;
+    verts[1].a = 1.0f;
+
+	batch->add(verts, 2);
+}
+
+#define DEBUG_BOX_COLOR Vector3(0, 1, 0)
+
+void drawDebugBox(MeshBatch* batch, const BoundingBox& box, const Matrix& matrix)
+{
+	// Transform box into world space (since we only store local boxes on mesh)
+	BoundingBox worldSpaceBox(box);
+	worldSpaceBox.transform(matrix);
+
+	// Get box corners
+    static Vector3 corners[8];
+    worldSpaceBox.getCorners(corners);
+
+	// Draw box lines
+	drawDebugLine(batch, corners[0], corners[1], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[1], corners[2], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[2], corners[3], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[3], corners[0], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[4], corners[5], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[5], corners[6], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[6], corners[7], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[7], corners[4], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[0], corners[7], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[1], corners[6], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[2], corners[5], DEBUG_BOX_COLOR);
+	drawDebugLine(batch, corners[3], corners[4], DEBUG_BOX_COLOR);
+}
+
+void drawDebugSphere(MeshBatch* batch, const BoundingSphere& sphere)
+{
+}
+
+void drawDebugNode(MeshBatch* batch, Node* node, unsigned int debugFlags)
+{
+	Model* model = node->getModel();
+
+	if ((debugFlags & Scene::DEBUG_BOXES) && model)
+	{
+		MeshSkin* skin = model->getSkin();
+		if (skin && skin->getRootJoint()->getParent())
+		{
+			// For skinned meshes that have a parent node to the skin's root joint,
+			// we need to transform the bounding volume by that parent node's transform
+			// as well to get the full skinned bounding volume.
+			drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix() * skin->getRootJoint()->getParent()->getWorldMatrix());
+		}
+		else
+		{
+			drawDebugBox(batch, model->getMesh()->getBoundingBox(), node->getWorldMatrix());
+		}
+	}
+
+	if ((debugFlags & Scene::DEBUG_SPHERES) && !node->getBoundingSphere().isEmpty())
+	{
+		drawDebugSphere(batch, node->getBoundingSphere());
+	}
+
+	Node* child = node->getFirstChild();
+	while (child)
+	{
+		drawDebugNode(batch, child, debugFlags);
+		child = child->getNextSibling();
+	}
+}
+
+void Scene::drawDebug(unsigned int debugFlags)
+{
+	if (_debugBatch == NULL)
+	{
+		Material* material = createDebugMaterial();
+
+		VertexFormat::Element elements[] =
+		{
+			VertexFormat::Element(VertexFormat::POSITION, 3),
+			VertexFormat::Element(VertexFormat::COLOR, 4)
+		};
+
+		_debugBatch = MeshBatch::create(VertexFormat(elements, 2), Mesh::LINES, material, false);
+
+		SAFE_RELEASE(material);
+	}
+
+	_debugBatch->begin();
+
+	Node* node = _firstNode;
+	while (node)
+	{
+		drawDebugNode(_debugBatch, node, debugFlags);
+		node = node->_nextSibling;
+	}
+
+	_debugBatch->end();
+
+	if (_activeCamera)
+		_debugBatch->getMaterial()->getParameter("u_viewProjectionMatrix")->setValue(_activeCamera->getViewProjectionMatrix());
+
+	_debugBatch->draw();
+}
+
 }

+ 19 - 0
gameplay/src/Scene.h

@@ -2,6 +2,7 @@
 #define SCENE_H_
 
 #include "Node.h"
+#include "MeshBatch.h"
 
 namespace gameplay
 {
@@ -13,6 +14,15 @@ class Scene : public Ref
 {
 public:
 
+	/**
+	 * Enumeration of supported scene debug flags for debug drawing.
+	 */
+	enum DebugFlags
+	{
+		DEBUG_BOXES = 1,
+		DEBUG_SPHERES = 2
+	};
+
     /**
      * Creates a new empty scene.
      * 
@@ -180,6 +190,14 @@ public:
     template <class T>
     void visit(T* instance, bool (T::*visitMethod)(Node*,void*), void* cookie = 0);
 
+	/**
+	 * Draws debugging information (bounding volumes, etc.) for the scene.
+	 *
+	 * @param debugFlags Bitwise combination of debug flags from mthe DebugFlags 
+	 *		enumeration, specifying which debugging information to draw.
+	 */
+	void drawDebug(unsigned int debugFlags);
+
 private:
 
     /**
@@ -211,6 +229,7 @@ private:
     unsigned int _nodeCount;
     Vector3 _ambientColor;
     bool _bindAudioListenerToCamera;
+	MeshBatch* _debugBatch;
 };
 
 template <class T>

+ 13 - 7
gameplay/src/SceneLoader.cpp

@@ -288,18 +288,24 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
                         WARN_VARG("Node '%s' does not have a model; attempting to use its model for rigid body creation.", name);
                     else
                     {
-                        // Set the specified model during physics rigid body creation.
+                        // Temporarily set rigidbody model on model to it's used during rigid body creation.
                         Model* model = node->getModel();
+						model->addRef(); // up ref count to prevent node from releasing the model when we swap it
                         node->setModel(modelNode->getModel());
-                        node->setRigidBody(p);
+
+						// Create collision object with new rigidbodymodel set.
+						node->setCollisionObject(p);
+
+						// Restore original model
                         node->setModel(model);
+						model->release(); // decrement temporarily added reference
                     }
                 }
             }
             else if (!node->getModel())
                 WARN_VARG("Attempting to set a rigid body on node '%s', which has no model.", sceneNode._nodeID);
             else
-                node->setRigidBody(p);
+				node->setCollisionObject(p);
             break;
         }
         default:
@@ -768,12 +774,12 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
                 WARN_VARG("Node '%s' to be used as 'rigidBodyA' for constraint %s cannot be found.", name, constraint->getId());
                 continue;
             }
-            PhysicsRigidBody* rbA = rbANode->getRigidBody();
-            if (!rbA)
+			if (!rbANode->getCollisionObject() || rbANode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
             {
                 WARN_VARG("Node '%s' to be used as 'rigidBodyA' does not have a rigid body.", name);
                 continue;
             }
+			PhysicsRigidBody* rbA = static_cast<PhysicsRigidBody*>(rbANode->getCollisionObject());
 
             // Attempt to load the second rigid body. If the second rigid body is not
             // specified, that is usually okay (only spring constraints require both and
@@ -789,12 +795,12 @@ void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
                     WARN_VARG("Node '%s' to be used as 'rigidBodyB' for constraint %s cannot be found.", name, constraint->getId());
                     continue;
                 }
-                rbB = rbBNode->getRigidBody();
-                if (!rbB)
+				if (!rbBNode->getCollisionObject() || rbBNode->getCollisionObject()->getType() != PhysicsCollisionObject::RIGID_BODY)
                 {
                     WARN_VARG("Node '%s' to be used as 'rigidBodyB' does not have a rigid body.", name);
                     continue;
                 }
+				rbB = static_cast<PhysicsRigidBody*>(rbBNode->getCollisionObject());
             }
 
             PhysicsConstraint* physicsConstraint = NULL;

+ 6 - 0
gameplay/src/SceneLoader.h

@@ -12,6 +12,12 @@ namespace gameplay
 
 /**
  * Helper class for loading scenes from .scene files.
+ *
+ * @todo Add support for loading ghost objects and characters for nodes.
+ * @todo Add support for explicitly specifying collision shapes for rigid bodies/ghost objects/characters.
+ * @todo Consider supporting 'rigidbodymodel' on models/meshes that are not part of the scene to allow
+ *		mesh data to be exported from a modelling tool for the sole purpose of representing a physics
+ *		rigid body, but not have it get loaded into the scene and rendering context.
  */
 class SceneLoader
 {

+ 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);
 

+ 15 - 4
gameplay/src/Technique.cpp

@@ -1,6 +1,7 @@
 #include "Base.h"
 #include "Technique.h"
 #include "Material.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -13,10 +14,6 @@ Technique::Technique(const char* id, Material* material)
     RenderState::_parent = material;
 }
 
-Technique::Technique(const Technique& m)
-{
-}
-
 Technique::~Technique()
 {
     // Destroy all the passes.
@@ -57,4 +54,18 @@ Pass* Technique::getPass(const char* id) const
     return NULL;
 }
 
+Technique* Technique::clone(Material* material, NodeCloneContext &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;
+}
+
 }

+ 9 - 1
gameplay/src/Technique.h

@@ -7,6 +7,7 @@ namespace gameplay
 {
 
 class Material;
+class NodeCloneContext;
 
 /**
  * Defines a technique for an object to be rendered.
@@ -51,13 +52,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, NodeCloneContext &context) const;
+
     std::string _id;
     Material* _material;
     std::vector<Pass*> _passes;

+ 9 - 0
gameplay/src/Transform.cpp

@@ -1,6 +1,7 @@
 #include "Base.h"
 #include "Transform.h"
 #include "Game.h"
+#include "Node.h"
 
 namespace gameplay
 {
@@ -780,6 +781,14 @@ void Transform::transformChanged()
     }
 }
 
+void Transform::cloneInto(Transform* transform, NodeCloneContext &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)

+ 3 - 1
gameplay/src/Transform.h

@@ -12,6 +12,7 @@ namespace gameplay
 
 class BoundingBox;
 class BoundingSphere;
+class NodeCloneContext;
 
 /**
  * Defines a 3-dimensional transformation.
@@ -674,7 +675,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 +738,7 @@ protected:
 
     void dirty();
     virtual void transformChanged();
+    void cloneInto(Transform* transform, NodeCloneContext &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.