Przeglądaj źródła

- Interim commit on Physics development (before getting changes from SVN).

Chris Culy 14 lat temu
rodzic
commit
6777395ca4
51 zmienionych plików z 2314 dodań i 649 usunięć
  1. 7 1
      .gitignore
  2. 9 0
      gameplay.sln
  3. 17 8
      gameplay/.cproject
  4. 7 2
      gameplay/gameplay.vcxproj
  5. 16 1
      gameplay/gameplay.vcxproj.filters
  6. 18 78
      gameplay/src/Animation.cpp
  7. 0 7
      gameplay/src/Animation.h
  8. 59 27
      gameplay/src/AnimationClip.cpp
  9. 31 5
      gameplay/src/AnimationClip.h
  10. 2 1
      gameplay/src/AnimationController.cpp
  11. 1 0
      gameplay/src/AnimationController.h
  12. 0 16
      gameplay/src/AnimationValue.cpp
  13. 0 16
      gameplay/src/AnimationValue.h
  14. 3 0
      gameplay/src/Base.h
  15. 43 11
      gameplay/src/Curve.cpp
  16. 42 2
      gameplay/src/Curve.h
  17. 1 1
      gameplay/src/Frustum.cpp
  18. 1 1
      gameplay/src/Frustum.h
  19. 12 1
      gameplay/src/Game.cpp
  20. 10 0
      gameplay/src/Game.h
  21. 27 5
      gameplay/src/Joint.cpp
  22. 17 0
      gameplay/src/Joint.h
  23. 47 50
      gameplay/src/MaterialParameter.cpp
  24. 5 2
      gameplay/src/MaterialParameter.h
  25. 2 1
      gameplay/src/Mesh.cpp
  26. 7 0
      gameplay/src/Mesh.h
  27. 7 38
      gameplay/src/MeshSkin.cpp
  28. 3 16
      gameplay/src/MeshSkin.h
  29. 1 0
      gameplay/src/Model.cpp
  30. 20 1
      gameplay/src/Node.cpp
  31. 27 1
      gameplay/src/Node.h
  32. 25 20
      gameplay/src/Package.cpp
  33. 47 3
      gameplay/src/Package.h
  34. 231 75
      gameplay/src/ParticleEmitter.cpp
  35. 347 22
      gameplay/src/ParticleEmitter.h
  36. 143 0
      gameplay/src/PhysicsController.cpp
  37. 83 0
      gameplay/src/PhysicsController.h
  38. 60 0
      gameplay/src/PhysicsMotionState.h
  39. 147 0
      gameplay/src/PhysicsRigidBody.cpp
  40. 164 0
      gameplay/src/PhysicsRigidBody.h
  41. 4 0
      gameplay/src/PlatformWin32.cpp
  42. 78 55
      gameplay/src/Properties.cpp
  43. 261 24
      gameplay/src/Properties.h
  44. 1 0
      gameplay/src/Quaternion.h
  45. 40 9
      gameplay/src/SpriteBatch.cpp
  46. 28 0
      gameplay/src/SpriteBatch.h
  47. 51 51
      gameplay/src/Transform.cpp
  48. 85 84
      gameplay/src/Transform.h
  49. 69 6
      gameplay/src/Tree.h
  50. 4 4
      gameplay/src/Vector3.cpp
  51. 4 4
      gameplay/src/Vector3.h

+ 7 - 1
.gitignore

@@ -59,4 +59,10 @@
 /gameplay-samples/sample03-character/Device-Debug
 /gameplay-samples/sample03-character/Device-Coverage
 /gameplay-samples/sample03-character/Device-Profile
-/gameplay-samples/sample03-character/Device-Release
+/gameplay-samples/sample03-character/Device-Release
+/gameplay-samples/sample04-catapult/Simulator
+/gameplay-samples/sample04-catapult/Debug
+/gameplay-samples/sample04-catapult/Device-Debug
+/gameplay-samples/sample04-catapult/Device-Release
+/gameplay-samples/sample04-catapult/Release
+/gameplay-demos

+ 9 - 0
gameplay.sln

@@ -53,6 +53,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample03-character", "gamep
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gameplay-encoder", "gameplay-encoder\gameplay-encoder.vcxproj", "{9D69B743-4872-4DD1-8E30-0087C64298D7}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample04-catapult", "gameplay-samples\sample04-catapult\sample04-catapult.vcxproj", "{FA260001-5B2E-41B7-86DD-C7F26DF3A485}"
+	ProjectSection(ProjectDependencies) = postProject
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -83,6 +88,10 @@ Global
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.Debug|Win32.Build.0 = Debug|Win32
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.Release|Win32.ActiveCfg = Release|Win32
 		{9D69B743-4872-4DD1-8E30-0087C64298D7}.Release|Win32.Build.0 = Release|Win32
+		{FA260001-5B2E-41B7-86DD-C7F26DF3A485}.Debug|Win32.ActiveCfg = Debug|Win32
+		{FA260001-5B2E-41B7-86DD-C7F26DF3A485}.Debug|Win32.Build.0 = Debug|Win32
+		{FA260001-5B2E-41B7-86DD-C7F26DF3A485}.Release|Win32.ActiveCfg = Release|Win32
+		{FA260001-5B2E-41B7-86DD-C7F26DF3A485}.Release|Win32.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 17 - 8
gameplay/.cproject

@@ -19,7 +19,7 @@
 						<toolChain id="com.qnx.qcc.toolChain.staticLib.debug.1686166742" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
 							<option id="com.qnx.qcc.option.cpu.545743487" name="Target CPU:" superClass="com.qnx.qcc.option.cpu" value="com.qnx.qcc.option.gen.cpu.armle-v7" valueType="enumerated"/>
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.158841187" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Device-Debug}" id="cdt.managedbuild.target.gnu.builder.base.2098111998" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+							<builder buildPath="${workspace_loc:/gameplay/Device-Debug}" id="cdt.managedbuild.target.gnu.builder.base.2098111998" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
 							<tool id="com.qnx.qcc.tool.compiler.96907942" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
 								<option id="com.qnx.qcc.option.compile.debug.1765481355" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.security.311918799" name="Enhanced Security (-fstack-protector-all)" superClass="com.qnx.qcc.option.compiler.security" value="true" valueType="boolean"/>
@@ -27,6 +27,7 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.2133604142" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}\..\external-deps\bullet\include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/usr/include/freetype2"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
@@ -71,7 +72,7 @@
 						<toolChain id="com.qnx.qcc.toolChain.staticLib.release.11990035" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
 							<option id="com.qnx.qcc.option.cpu.33079389" name="Target CPU:" superClass="com.qnx.qcc.option.cpu" value="com.qnx.qcc.option.gen.cpu.armle-v7" valueType="enumerated"/>
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.1117051584" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Device-Release}" id="cdt.managedbuild.target.gnu.builder.base.1199322737" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+							<builder buildPath="${workspace_loc:/gameplay/Device-Release}" id="cdt.managedbuild.target.gnu.builder.base.1199322737" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
 							<tool id="com.qnx.qcc.tool.compiler.1345567866" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
 								<option id="com.qnx.qcc.option.compiler.optlevel.1056793982" name="Optimization Level" superClass="com.qnx.qcc.option.compiler.optlevel" value="com.qnx.qcc.option.compiler.optlevel.2" valueType="enumerated"/>
 								<option id="com.qnx.qcc.option.compiler.security.324540233" name="Enhanced Security (-fstack-protector-all)" superClass="com.qnx.qcc.option.compiler.security" value="true" valueType="boolean"/>
@@ -79,6 +80,7 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1670164593" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}\..\external-deps\bullet\include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/usr/include/freetype2"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
@@ -121,7 +123,7 @@
 						<toolChain id="com.qnx.qcc.toolChain.staticLib.profile.1494216018" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
 							<option id="com.qnx.qcc.option.cpu.1727548796" name="Target CPU:" superClass="com.qnx.qcc.option.cpu" value="com.qnx.qcc.option.gen.cpu.armle-v7" valueType="enumerated"/>
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.1288889025" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Device-Profile}" id="cdt.managedbuild.target.gnu.builder.base.831558871" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+							<builder buildPath="${workspace_loc:/gameplay/Device-Profile}" id="cdt.managedbuild.target.gnu.builder.base.831558871" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
 							<tool id="com.qnx.qcc.tool.compiler.1281156842" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
 								<option id="com.qnx.qcc.option.compile.debug.626405189" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.profile2.1207899085" name="Build for Profiling (Function Instrumentation) (-finstrument-functions)" superClass="com.qnx.qcc.option.compiler.profile2" value="true" valueType="boolean"/>
@@ -130,6 +132,7 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1503059677" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}\..\external-deps\bullet\include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/usr/include/freetype2"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
@@ -175,7 +178,7 @@
 						<toolChain id="com.qnx.qcc.toolChain.staticLib.coverage.1939228131" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
 							<option id="com.qnx.qcc.option.cpu.832672244" name="Target CPU:" superClass="com.qnx.qcc.option.cpu" value="com.qnx.qcc.option.gen.cpu.armle-v7" valueType="enumerated"/>
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.987519072" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Device-Coverage}" id="cdt.managedbuild.target.gnu.builder.base.121802503" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+							<builder buildPath="${workspace_loc:/gameplay/Device-Coverage}" id="cdt.managedbuild.target.gnu.builder.base.121802503" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
 							<tool id="com.qnx.qcc.tool.compiler.306557636" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
 								<option id="com.qnx.qcc.option.compile.debug.168813234" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.coverage.1032644527" name="Build for Code Coverage (-Wc,-ftest-coverage -Wc,-fprofile-arcs)" superClass="com.qnx.qcc.option.compiler.coverage" value="true" valueType="boolean"/>
@@ -184,6 +187,7 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1769677874" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}\..\external-deps\bullet\include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/usr/include/freetype2"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
@@ -228,7 +232,7 @@
 					<folderInfo id="com.qnx.qcc.configuration.staticLib.debug.559445444." name="/" resourcePath="">
 						<toolChain id="com.qnx.qcc.toolChain.staticLib.debug.1260117207" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.795462400" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Simulator}" id="cdt.managedbuild.target.gnu.builder.base.10075032" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+							<builder buildPath="${workspace_loc:/gameplay/Simulator}" id="cdt.managedbuild.target.gnu.builder.base.10075032" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
 							<tool id="com.qnx.qcc.tool.compiler.1004416224" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
 								<option id="com.qnx.qcc.option.compile.debug.1122485646" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.security.1671403331" name="Enhanced Security (-fstack-protector-all)" superClass="com.qnx.qcc.option.compiler.security" value="true" valueType="boolean"/>
@@ -236,6 +240,7 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.847642559" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}\..\external-deps\bullet\include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/usr/include/freetype2"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
@@ -279,7 +284,7 @@
 					<folderInfo id="com.qnx.qcc.configuration.staticLib.profile.191203500." name="/" resourcePath="">
 						<toolChain id="com.qnx.qcc.toolChain.staticLib.profile.1691673400" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.1730932164" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Simulator-Profile}" id="cdt.managedbuild.target.gnu.builder.base.854227640" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+							<builder buildPath="${workspace_loc:/gameplay/Simulator-Profile}" id="cdt.managedbuild.target.gnu.builder.base.854227640" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
 							<tool id="com.qnx.qcc.tool.compiler.417488704" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
 								<option id="com.qnx.qcc.option.compile.debug.1290366598" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.profile2.216911941" name="Build for Profiling (Function Instrumentation) (-finstrument-functions)" superClass="com.qnx.qcc.option.compiler.profile2" value="true" valueType="boolean"/>
@@ -288,6 +293,7 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.513622172" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}\..\external-deps\bullet\include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/usr/include/freetype2"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
@@ -332,7 +338,7 @@
 					<folderInfo id="com.qnx.qcc.configuration.staticLib.coverage.796584174." name="/" resourcePath="">
 						<toolChain id="com.qnx.qcc.toolChain.staticLib.coverage.349788538" name="QNX QCC" superClass="com.qnx.qcc.toolChain">
 							<targetPlatform archList="all" binaryParser="com.qnx.tools.ide.qde.core.QDEBynaryParser" id="com.qnx.qcc.targetPlatform.1819308065" osList="all" superClass="com.qnx.qcc.targetPlatform"/>
-							<builder buildPath="${workspace_loc:/gameplay/Simulator-Coverage}" id="cdt.managedbuild.target.gnu.builder.base.66192685" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+							<builder buildPath="${workspace_loc:/gameplay/Simulator-Coverage}" id="cdt.managedbuild.target.gnu.builder.base.66192685" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
 							<tool id="com.qnx.qcc.tool.compiler.563072865" name="QCC Compiler" superClass="com.qnx.qcc.tool.compiler">
 								<option id="com.qnx.qcc.option.compile.debug.1789973550" name="Debug (-g)" superClass="com.qnx.qcc.option.compile.debug" value="true" valueType="boolean"/>
 								<option id="com.qnx.qcc.option.compiler.coverage.1289221781" name="Build for Code Coverage (-Wc,-ftest-coverage -Wc,-fprofile-arcs)" superClass="com.qnx.qcc.option.compiler.coverage" value="true" valueType="boolean"/>
@@ -341,6 +347,7 @@
 									<listOptionValue builtIn="false" value="_FORTIFY_SOURCE=2"/>
 								</option>
 								<option id="com.qnx.qcc.option.compiler.includePath.1685994750" name="Include Directories (-I)" superClass="com.qnx.qcc.option.compiler.includePath" valueType="includePath">
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}}\..\external-deps\bullet\include&quot;"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/usr/include/freetype2"/>
 									<listOptionValue builtIn="false" value="${QNX_TARGET}/../target-override/usr/include"/>
 								</option>
@@ -398,6 +405,8 @@
 			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="com.qnx.tools.ide.qde.managedbuilder.core.qccScannerInfo"/>
 		</scannerConfigBuildInfo>
 	</storageModule>
-	<storageModule moduleId="refreshScope"/>
+	<storageModule moduleId="refreshScope" versionNumber="1">
+		<resource resourceType="PROJECT" workspacePath="/gameplay"/>
+	</storageModule>
 	<storageModule moduleId="com.qnx.tools.ide.qde.core.QNXProjectProperties"/>
 </cproject>

+ 7 - 2
gameplay/gameplay.vcxproj

@@ -43,6 +43,8 @@
     <ClCompile Include="src\Node.cpp" />
     <ClCompile Include="src\Package.cpp" />
     <ClCompile Include="src\ParticleEmitter.cpp" />
+    <ClCompile Include="src\PhysicsController.cpp" />
+    <ClCompile Include="src\PhysicsRigidBody.cpp" />
     <ClCompile Include="src\Plane.cpp" />
     <ClCompile Include="src\PlatformQNX.cpp" />
     <ClCompile Include="src\PlatformWin32.cpp" />
@@ -100,6 +102,9 @@
     <ClInclude Include="src\Node.h" />
     <ClInclude Include="src\Package.h" />
     <ClInclude Include="src\ParticleEmitter.h" />
+    <ClInclude Include="src\PhysicsController.h" />
+    <ClInclude Include="src\PhysicsMotionState.h" />
+    <ClInclude Include="src\PhysicsRigidBody.h" />
     <ClInclude Include="src\Plane.h" />
     <ClInclude Include="src\Platform.h" />
     <ClInclude Include="src\Properties.h" />
@@ -165,7 +170,7 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\external-deps\bullet\include;..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -181,7 +186,7 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\external-deps\bullet\include;..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>

+ 16 - 1
gameplay/gameplay.vcxproj.filters

@@ -156,6 +156,12 @@
     <ClCompile Include="src\Properties.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\PhysicsController.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\PhysicsRigidBody.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -323,5 +329,14 @@
     <ClInclude Include="src\Tree.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\PhysicsController.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\PhysicsMotionState.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\PhysicsRigidBody.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
-</Project>
+</Project>

+ 18 - 78
gameplay/src/Animation.cpp

@@ -15,8 +15,6 @@
 #define ANIMATION_DEFAULT_CLIP_SUFFIX "__default__clip"
 #define ANIMATION_INDEFINITE_STR "INDEFINITE"
 #define ANIMATION_DEFAULT_CLIP 0
-#define ANIMATION_ROTATE_OFFSET 0
-#define ANIMATION_SRT_OFFSET 3
 
 namespace gameplay
 {
@@ -93,18 +91,12 @@ void Animation::createClips(const char* animationFile)
     Properties* pAnim = Properties::create(animationFile);
     assert(pAnim);
 
-    const vector<Properties*>* vAnim = pAnim->getProperties();
-    
-    Properties* animation = vAnim->front();
+    Properties* animation = pAnim->getNextNamespace();
     int frameCount = animation->getInt("frameCount");
 
-    const std::vector<Properties*>* clips = animation->getProperties();
-    vector<Properties*>::const_iterator itr;
-
-    for (itr = clips->begin(); itr < clips->end(); itr++)
+    const Properties* pClip = animation->getNextNamespace();
+    while (pClip != NULL)
     {
-        Properties* pClip = *itr;
-
         int begin = pClip->getInt("begin");
         int end = pClip->getInt("end");
 
@@ -115,7 +107,7 @@ void Animation::createClips(const char* animationFile)
         {
             if (((std::string)repeat).compare(ANIMATION_INDEFINITE_STR) == 0)
             {
-                clip->setRepeatCount(ANIMATION_REPEAT_COUNT_INDEFINITE);
+                clip->setRepeatCount(AnimationClip::REPEAT_INDEFINITE);
             }
             else
             {
@@ -124,6 +116,16 @@ void Animation::createClips(const char* animationFile)
                 clip->setRepeatCount(value);
             }
         }
+
+        const char* speed = pClip->getString("speed");
+        if (speed)
+        {
+            float value;
+            sscanf(speed, "%f", &value);
+            clip->setSpeed(value);
+        }
+
+        pClip = animation->getNextNamespace();
     }
 
     SAFE_DELETE(pAnim);
@@ -197,18 +199,7 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
 
     Curve* curve = new Curve(keyCount, propertyComponentCount);
     if (target->_targetType == AnimationTarget::TRANSFORM)
-    {
-        switch (propertyId)
-        {
-            case TRANSFORM_ANIMATE_ROTATE: 
-            case TRANSFORM_ANIMATE_ROTATE_TRANSLATE:
-                curve->_rotationOffset = ANIMATION_ROTATE_OFFSET;
-                break;
-            case TRANSFORM_ANIMATE_SCALE_ROTATE_TRANSLATE:
-                curve->_rotationOffset = ANIMATION_SRT_OFFSET;
-                break;
-        }
-    }
+        curve->setRotationOffset(propertyId);
 
     float lowest = keyTimes[0];
     float duration = keyTimes[keyCount-1] - lowest;
@@ -216,9 +207,9 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
     unsigned int pointOffset = 0;
     for (unsigned int i = 0; i < keyCount; i++)
     {
-        pointOffset = i * propertyComponentCount;
         keyTimes[i] = (keyTimes[i] - lowest) / duration;
         curve->setPoint(i, keyTimes[i], keyValues + pointOffset, (Curve::InterpolationType) type);
+        pointOffset += propertyComponentCount;
     }
 
     Channel* channel = new Channel(target, propertyId, curve, duration);
@@ -235,18 +226,7 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
 
     Curve* curve = new Curve(keyCount, propertyComponentCount);
     if (target->_targetType == AnimationTarget::TRANSFORM)
-    {
-        switch (propertyId)
-        {
-            case TRANSFORM_ANIMATE_ROTATE: 
-            case TRANSFORM_ANIMATE_ROTATE_TRANSLATE:
-                curve->_rotationOffset = ANIMATION_ROTATE_OFFSET;
-                break;
-            case TRANSFORM_ANIMATE_SCALE_ROTATE_TRANSLATE:
-                curve->_rotationOffset = ANIMATION_SRT_OFFSET;
-                break;
-        }
-    }
+        curve->setRotationOffset(propertyId);
 
     float lowest = keyTimes[0];
     float duration = keyTimes[keyCount-1] - lowest;
@@ -254,9 +234,9 @@ Animation::Channel* Animation::createChannel(AnimationTarget* target, int proper
     unsigned int pointOffset = 0;
     for (unsigned int i = 0; i < keyCount; i++)
     {
-        pointOffset = i * propertyComponentCount;
         keyTimes[i] = (keyTimes[i] - lowest) / duration;
         curve->setPoint(i, keyTimes[i], keyValues + pointOffset, (Curve::InterpolationType) type, keyTangentIn + pointOffset, keyTangentOut + pointOffset);
+        pointOffset += propertyComponentCount;
     }
 
     Channel* channel = new Channel(target, propertyId, curve, duration);
@@ -296,44 +276,4 @@ AnimationClip* Animation::findClip(const char* id) const
     return NULL;
 }
 
-void Animation::transfer(Animation* animation)
-{
-    // WARNING: This function is leaking some memory, but it should be removed when this hack is removed!
-
-    unsigned int count = animation->_channels.size();
-    for (unsigned int i = 0; i < count; i++)
-    {
-        Channel* channel = animation->_channels[i];
-        addChannel(channel);
-    }
-
-    // AnimationChannel is not reference counted so they should not belong to two Animations.
-    // Delete the AnimationChannel array and all of the clips.
-    /*std::vector<Channel*>::iterator channelIter = animation->_channels.begin();
-    
-    while (channelIter != animation->_channels.end())
-    {
-        Channel* channel = *channelIter;
-        channelIter = animation->_channels.erase(channelIter);
-        SAFE_DELETE(channel);
-    }
-    */
-    /*std::vector<AnimationClip*>::iterator clipIter = animation->_clips.begin();
-    
-    while (clipIter != animation->_clips.end())
-    {
-        AnimationClip* clip = *clipIter;
-        clipIter = animation->_clips.erase(clipIter);
-        SAFE_RELEASE(clip);
-    }*/
-    /*
-    animation->_channelCount = 0;
-    SAFE_DELETE_ARRAY(animation->_channels);
-    for (unsigned int i = 0; i < animation->_channelCount; i++)
-    {
-        SAFE_RELEASE(animation->_clips[i]);
-    }
-    SAFE_DELETE_ARRAY(animation->_clips);*/
-}
-
 }

+ 0 - 7
gameplay/src/Animation.h

@@ -85,13 +85,6 @@ public:
      */
     void stop(const char* clipId = NULL);
 
-    /**
-     * Transfers all of the animation channels from the given animation to this animation.
-     * 
-     * @param animation The animation to transfer the animation channels from.
-     */
-    void transfer(Animation* animation);
-
 private:
 
     /**

+ 59 - 27
gameplay/src/AnimationClip.cpp

@@ -12,7 +12,7 @@ namespace gameplay
 {
 
 AnimationClip::AnimationClip(const char* id, Animation* animation, unsigned long startTime, unsigned long endTime)
-    : _id(id), _animation(animation), _startTime(startTime), _endTime(endTime), _elapsedTime(0), _channelCount(animation->_channels.size()), _repeatCount(1.0f), _speed(1.0f), _isPlaying(false), _beginListeners(NULL), _endListeners(NULL)
+    : _id(id), _animation(animation), _startTime(startTime), _endTime(endTime), _elapsedTime(0), _runningTime(0), _channelCount(animation->_channels.size()), _repeatCount(1.0f), _speed(1.0f), _isPlaying(false), _beginListeners(NULL), _endListeners(NULL)
 {
     assert(0 <= startTime && startTime <= animation->_duration && 0 <= endTime && endTime <= animation->_duration);
 
@@ -87,11 +87,11 @@ unsigned long AnimationClip::getElaspedTime() const
 
 void AnimationClip::setRepeatCount(float repeatCount)
 {
-    assert(repeatCount == ANIMATION_REPEAT_COUNT_INDEFINITE || repeatCount > 0.0f);
+    assert(repeatCount == REPEAT_INDEFINITE || repeatCount > 0.0f);
 
     _repeatCount = repeatCount;
 
-    if (repeatCount == ANIMATION_REPEAT_COUNT_INDEFINITE)
+    if (repeatCount == REPEAT_INDEFINITE)
     {
         _activeDuration = _duration;
     }
@@ -106,6 +106,28 @@ float AnimationClip::getRepeatCount() const
     return _repeatCount;
 }
 
+void AnimationClip::setActiveDuration(unsigned long duration)
+{
+    if (duration == REPEAT_INDEFINITE)
+    {
+        _repeatCount = REPEAT_INDEFINITE;
+        _activeDuration = _duration;
+    }
+    else
+    {
+        _activeDuration = _duration;
+        _repeatCount = (float) _activeDuration / (float) _duration;
+    }
+}
+
+unsigned long AnimationClip::getActiveDuration() const
+{
+    if (_repeatCount == REPEAT_INDEFINITE)
+        return REPEAT_INDEFINITE;
+
+    return _activeDuration;
+}
+
 void AnimationClip::setSpeed(float speed)
 {
     _speed = speed;
@@ -151,59 +173,69 @@ void AnimationClip::addEndListener(AnimationClip::Listener* listener)
 bool AnimationClip::update(unsigned long elapsedTime)
 {
     if (!_isPlaying)
-    {
-        // Initialize animation to play.
-        _isPlaying = true;
-        _elapsedTime = 0;
         onBegin();
-    }
 
     // Update elapsed time.
-    if (_speed < 0.0f && _repeatCount != ANIMATION_REPEAT_COUNT_INDEFINITE)
-        _elapsedTime -= elapsedTime * _speed;
-    else
-        _elapsedTime += elapsedTime * _speed;
-    
-    bool animationComplete = false;
+    _elapsedTime += elapsedTime;
+    _runningTime += elapsedTime * _speed;
+
     float percentComplete = 0.0f;
 
     // Check to see if clip is complete.
-    if (_repeatCount != ANIMATION_REPEAT_COUNT_INDEFINITE && _elapsedTime >= _activeDuration)
+    if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0 && _runningTime >= (long) _activeDuration) || (_speed < 0 && _runningTime <= 0)))
     {
-        percentComplete = _repeatCount;
-        animationComplete = true;
         _isPlaying = false;
+        if (_speed >= 0)
+            percentComplete = _activeDuration % _duration; // Get's the fractional part of the final repeat.
+        else
+            percentComplete = 0.0f; // If we are negative speed, the end value should be 0.
     }
     else
     {
-        percentComplete = (float) _elapsedTime / _duration;
+        // Gets portion/fraction of the repeat.
+        percentComplete = (float) (_runningTime % _duration);
     }
 
-    // Get out fractional part of percent complete (can ignore repeats!)
-    // Add in start time, so we evaluate the correct part of the curve.
-    float intPart;
-    percentComplete = (std::min)((float)(_startTime + std::modf(percentComplete, &intPart) * _duration) / _animation->_duration, 1.0f);
+    // Add back in start time, and divide by the total animation's duration to get the actual percentage complete
+    percentComplete = (float)(_startTime + percentComplete) / (float) _animation->_duration;
 
     // Evaluate this clip.
+    Animation::Channel* channel = NULL;
+    AnimationValue* value = NULL;
     for (unsigned int i = 0; i < _channelCount; i++)
     {
-        AnimationValue* value = _values.at(i);
+        channel = _animation->_channels[i];
+        value = _values.at(i);
 
         // Evaluate point on curve.
-        _animation->_channels[i]->_curve->evaluate(percentComplete, value->_currentValue);
+        channel->_curve->evaluate(percentComplete, value->_currentValue);
         
         // Set the animation value on the target property.
-        _animation->_channels[i]->_target->setAnimationPropertyValue(_animation->_channels[i]->_propertyId, value);
+        channel->_target->setAnimationPropertyValue(channel->_propertyId, value);
     }
 
-    if (animationComplete)
+    // When ended. Probably should move to it's own method so we can call it when the clip is ended early.
+    if (!_isPlaying)
         onEnd();
 
-    return animationComplete;
+    return !_isPlaying;
 }
 
 void AnimationClip::onBegin()
 {
+    // Initialize animation to play.
+    _isPlaying = true;
+    _elapsedTime = 0;
+
+    if (_speed > 0)
+    {
+        _runningTime = 0;
+    }
+    else
+    {
+        _runningTime = _activeDuration;
+    }
+
     if (_beginListeners)
     {
         std::vector<Listener*>::iterator listener = _beginListeners->begin();

+ 31 - 5
gameplay/src/AnimationClip.h

@@ -10,10 +10,6 @@
 #include "Curve.h"
 #include "Animation.h"
 
-/**
- * Defines an indefinite repeat count. Used to specify when an AnimationClip will repeat indefinitely until stopped.
- */
-#define ANIMATION_REPEAT_COUNT_INDEFINITE           (-FLT_MAX)
 
 namespace gameplay
 {
@@ -31,6 +27,11 @@ class AnimationClip : public Ref
 
 public:
 
+    /**
+     * Defines a constant for indefinitely repeating an AnimationClip.
+     */
+    static const int REPEAT_INDEFINITE = 0; 
+
     /**
      * Defines an animation event listener.
      */
@@ -96,7 +97,9 @@ public:
     unsigned long getElaspedTime() const;
 
     /**
-     * Sets the AnimationClip's repeat count.
+     * Sets the AnimationClip's repeat count. Overrides repeat duration.
+     *
+     * Use ANIMATION_REPEAT_INDEFINITE to play the AnimationClip indefinitely.
      * 
      * @param repeatCount The repeat count to set on the AnimationClip.
      */
@@ -109,6 +112,22 @@ public:
      */
     float getRepeatCount() const;
 
+    /**
+     * Sets the AnimationClip's active duration. Overrides repeat count.
+     *
+     * Use ANIMATION_REPEAT_INDEFINITE to play the AnimationClip indefinitely.
+     *
+     * @param duration The active duration that is set on the AnimationClip.
+     */
+    void setActiveDuration(unsigned long duration);
+
+    /**
+     * Gets the AnimationClip's active duration.
+     * 
+     * @return the AnimationClip's active duration.
+     */
+    unsigned long getActiveDuration() const;
+
     /**
      * Set the AnimationClip's running speed. 
      *
@@ -181,8 +200,14 @@ private:
      */
     bool update(unsigned long elapsedTime);
 
+    /**
+     * Handles when the AnimationClip begins.
+     */
     void onBegin();
 
+    /**
+     * Handles when the AnimationClip ends.
+     */
     void onEnd();
 
     std::string _id;                          // AnimationClip ID.
@@ -190,6 +215,7 @@ private:
     unsigned long _startTime;                 // Start time of the clip.
     unsigned long _endTime;                   // End time of the clip.
     unsigned long _elapsedTime;               // Time elapsed while the clip is running.
+    long _runningTime;                        // Keeps track of the Animation's relative time in respect to the active duration.
     unsigned int _channelCount;               // The number of channels in the clip.
     std::vector<AnimationValue*> _values;     // AnimationValue holder.
     float _repeatCount;                       // The clip's repeat count.

+ 2 - 1
gameplay/src/AnimationController.cpp

@@ -46,7 +46,7 @@ Animation* AnimationController::createAnimation(const char* id, AnimationTarget*
     if (animation != NULL)
         return NULL;
     
-    animation = new Animation(id, target, propertyId, keyCount, keyTimes, keyValues, keyTangentIn, keyTangentOut, (unsigned int) type);
+    animation = new Animation(id, target, propertyId, keyCount, keyTimes, keyValues, keyTangentIn, keyTangentOut, type);
 
     addAnimation(animation);
 
@@ -158,6 +158,7 @@ void AnimationController::schedule(AnimationClip* clip)
     if (clip->_isPlaying)
     {
         _runningClips.remove(clip);
+        clip->_isPlaying = false;
     }
     else
     {

+ 1 - 0
gameplay/src/AnimationController.h

@@ -91,6 +91,7 @@ public:
      * Finds the animation with the given ID.
      *
      * @param id The ID of the animation to get. NULL if the Animation is not found.
+     * 
      * @return The animation, or NULL if not found.
      */
     Animation* getAnimation(const char* id) const;

+ 0 - 16
gameplay/src/AnimationValue.cpp

@@ -12,14 +12,10 @@ AnimationValue::AnimationValue(unsigned int componentCount)
   : _componentCount(componentCount)
 {
     _currentValue = new float[_componentCount];
-    //_initialValue = new float[_componentCount];
 }
 
 AnimationValue::~AnimationValue()
 {
-    /*
-    SAFE_DELETE_ARRAY(_initialValue);
-    */
     SAFE_DELETE_ARRAY(_currentValue);
 }
 
@@ -51,16 +47,4 @@ void AnimationValue::setFloat(float* value, unsigned int offset, unsigned int le
     memcpy(_currentValue, value + offset, length * sizeof(float));
 }
 
-/*
-void AnimationValue::setInitialToCurrent()
-{
-    memcpy(_initialValue, _currentValue, sizeof(float) * _componentCount);
-}
-
-void AnimationValue::setCurrentToInitial()
-{
-    memcpy(_currentValue, _initialValue, sizeof(float) * _componentCount);
-}
-*/
-
 }

+ 0 - 16
gameplay/src/AnimationValue.h

@@ -76,23 +76,7 @@ private:
      */
     ~AnimationValue();
 
-    /**
-     * Sets the initial values to the current value.
-     */
-    //void setInitialToCurrent();
-
-    /**
-     * Set the current value to the initial value.
-     */
-    //void setCurrentToInitial();
-
-    /**
-     * Get the initial value.
-     */
-    //void getOriginalValue();
-
     unsigned int _componentCount;   // The number of float values for the property.
-    //float* _initialValue;         // The initial value of the property.
     float* _currentValue;           // The current value of the property.
 };
 

+ 3 - 0
gameplay/src/Base.h

@@ -116,6 +116,9 @@ extern void printError(const char* format, ...);
 #include <alut.h>
 #endif
 
+// Bullet Physics
+#include <btBulletDynamicsCommon.h>
+
 // Graphics (OpenGLES/OpenGL/png)
 #define WINDOW_VSYNC        1
 #define WINDOW_FULLSCREEN   0

+ 43 - 11
gameplay/src/Curve.cpp

@@ -6,6 +6,9 @@
 #include "Curve.h"
 #include "Transform.h"
 
+#define ANIMATION_ROTATE_OFFSET 0
+#define ANIMATION_SRT_OFFSET 3
+
 namespace gameplay
 {
 
@@ -106,17 +109,9 @@ void Curve::evaluate(float time, float* dst)
         return;
     }
 
-    // Locate the points we are interpolating between.
-    unsigned int index = 0;
-    for (unsigned int i = 0; i < _pointCount - 1; i++)
-    {
-        if (_points[i].time <= time && time <= _points[i + 1].time)
-        {
-            index = i;
-            break;
-        }
-    }
-
+    // Locate the points we are interpolating between using a binary search.
+    unsigned int index = determineIndex(time);
+    
     Point* from = _points + index;
     Point* to = _points + (index + 1);
 
@@ -391,4 +386,41 @@ void Curve::interpolateQuaternion(float s, Point* from, Point* to, float* dst)
     dst[3] = result.w;
 }
 
+void Curve::setRotationOffset(int propertyId)
+{
+    switch (propertyId)
+    {
+        case Transform::ANIMATE_ROTATE: 
+        case Transform::ANIMATE_ROTATE_TRANSLATE:
+            _rotationOffset = ANIMATION_ROTATE_OFFSET;
+            break;
+        case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
+            _rotationOffset = ANIMATION_SRT_OFFSET;
+            break;
+    }
+}
+
+int Curve::determineIndex(float time)
+{
+    unsigned int min = 0;
+    unsigned int max = _pointCount - 1;
+    unsigned int mid = 0;
+
+    // Do a binary search to determine the index.
+    do 
+    {
+        mid = (min + max) >> 1;
+
+        if (time >= _points[mid].time && time <= _points[mid + 1].time)
+            return mid;
+        else if (time < _points[mid].time)
+            max = mid - 1;
+        else
+            min = mid + 1;
+    } while (min <= max);
+    
+    // We should never hit this!
+    return -1;
+}
+
 }

+ 42 - 2
gameplay/src/Curve.h

@@ -21,16 +21,46 @@ class Curve
 public:
 
     /**
-     * Defines the type of tangent.
+     * Defines the type of interpolation.
+     *
+     * Note: InterpolationType::BEZIER requires control points and InterpolationType::HERMITE requires tangents.
      */
     enum InterpolationType
     {
+        /**
+         * Bezier interpolation. Requires that two control points are set for each segment.
+         */
         BEZIER,
+
+        /**
+         * B-Spline interpolation. Uses the points as control points, and the curve is guaranteed to only pass through the
+         * first and last point.
+         */
         BSPLINE,
+
+        /**
+         * Flat. A form of Hermite interpolation that generates flat tangents for you. The tangents have a value equal to 0.
+         */
         FLAT,
+
+        /**
+         * Hermite interpolation. Requires that two tangents for each segment.
+         */
         HERMITE,
+
+        /**
+         * Linear interpolation.
+         */
         LINEAR,
+
+        /** 
+         * Smooth. A form of Hermite interpolation that generates tangents for each segment based on the points prior to and after the segment.
+         */
         SMOOTH,
+
+        /**
+         * Discrete interpolation.
+         */ 
         STEP
     };
 
@@ -172,10 +202,20 @@ private:
      */
     void interpolateQuaternion(float s, Point* from, Point* to, float* dst);
 
+    /**
+     * Marks the offset of the rotation component within the point's value data span.
+     */
+    void setRotationOffset(int propertyId);
+
+    /**
+     * Determines the current keyframe to interpolate from based on the specified time.
+     */ 
+    int determineIndex(float time);
+
     unsigned int _pointCount;      // Number of points on the curve.
     unsigned int _componentCount;  // Number of components on the curve.
     unsigned int _componentSize;   // The component size (in bytes).
-    int _rotationOffset;           // Offset for the rotation component.
+    signed char _rotationOffset;   // Offset for the rotation component.
     Point* _points;                // The points on the curve.
 };
 

+ 1 - 1
gameplay/src/Frustum.cpp

@@ -59,7 +59,7 @@ const Plane& Frustum::getTop() const
     return _top;
 }
 
-void Frustum::getMatrix(Matrix* dst)
+void Frustum::getMatrix(Matrix* dst) const
 {
     dst->set(_matrix);
 }

+ 1 - 1
gameplay/src/Frustum.h

@@ -104,7 +104,7 @@ public:
      * 
      * @param dst The projection matrix to copy into.
      */
-    void getMatrix(Matrix* dst);
+    void getMatrix(Matrix* dst) const;
 
     /**
      * Gets the corners of the frustum in the specified array.

+ 12 - 1
gameplay/src/Game.cpp

@@ -92,6 +92,7 @@ bool Game::startup()
 
     _animationController.initialize();
     _audioController.initialize();
+	_physicsController.initialize();
 
     // Call user initialization.
     initialize();
@@ -109,6 +110,7 @@ void Game::shutdown()
 
         _animationController.finalize();
         _audioController.finalize();
+        _physicsController.finalize();
     }
 
     _state = UNINITIALIZED;
@@ -122,6 +124,7 @@ void Game::pause()
         _pausedTimeLast = Platform::getAbsoluteTime();
         _animationController.pause();
         _audioController.pause();
+        _physicsController.pause();
     }
 }
 
@@ -133,6 +136,7 @@ void Game::resume()
         _pausedTimeTotal += Platform::getAbsoluteTime() - _pausedTimeLast;
         _animationController.resume();
         _audioController.resume();
+        _physicsController.resume();
     }
 }
 
@@ -152,8 +156,10 @@ void Game::frame()
     long elapsedTime = (frameTime - lastFrameTime);
     lastFrameTime = frameTime;
 
-    // Update the schedule and running animations.
+    // Update the scheduled and running animations.
     _animationController.update(elapsedTime);
+    // Update the physics.
+    _physicsController.update(elapsedTime);
     // Application Update.
     update(elapsedTime);
 
@@ -192,6 +198,11 @@ const AudioController& Game::getAudioController() const
     return _audioController;
 }
 
+PhysicsController* Game::getPhysicsController()
+{
+	return &_physicsController;
+}
+
 void Game::menu()
 {
 }

+ 10 - 0
gameplay/src/Game.h

@@ -8,6 +8,7 @@
 #include "Input.h"
 #include "AudioController.h"
 #include "AnimationController.h"
+#include "PhysicsController.h"
 
 namespace gameplay
 {
@@ -151,6 +152,14 @@ public:
      */
     AnimationController* getAnimationController();
 
+    /**
+     * Gets the physics controller for managing control of physics
+     * associated with the game.
+     * 
+     * @return The physics controller for this game.
+     */
+    PhysicsController* getPhysicsController();
+
     /**
      * Menu callback on menu events.
      */
@@ -251,6 +260,7 @@ private:
     unsigned int _height;                       // The game's display height.
     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.
 };
 
 }

+ 27 - 5
gameplay/src/Joint.cpp

@@ -9,7 +9,7 @@ namespace gameplay
 {
 
 Joint::Joint(const char* id)
-    : Node(id), _jointMatrixDirty(true)
+    : Node(id), _jointMatrixDirty(true), _skin(NULL)
 {
 }
 
@@ -43,10 +43,6 @@ void Joint::updateJointMatrix(const Matrix& bindShape, Vector4* matrixPalette)
     {
         _jointMatrixDirty = false;
 
-        float r = (float)rand( ) / (float)RAND_MAX;
-        Matrix w = getWorldMatrix();
-        w.translate(r, r, r);
-
         Matrix t;
         Matrix::multiply(getWorldMatrix(), getInverseBindPose(), &t);
         Matrix::multiply(t, bindShape, &t);
@@ -68,4 +64,30 @@ void Joint::setInverseBindPose(const Matrix& m)
     _jointMatrixDirty = true;
 }
 
+const Matrix& Joint::getWorldMatrix() const
+{
+    return Node::getWorldMatrix();
+}
+
+const Matrix& Joint::getJointMatrix() const
+{
+    // If this is the root joint, then we 
+    // also apply the transform of the model
+    // that the skin is attached to to get the
+    // actual world matrix.
+    if (_parent == NULL)
+    {
+        if (_skin != NULL)
+            Matrix::multiply(_skin->_model->getNode()->getWorldMatrix(), Node::getWorldMatrix(), &_jointWorld);
+        else if (_firstChild != NULL && ((Joint*)_firstChild)->_skin != NULL)
+            Matrix::multiply(((Joint*)_firstChild)->_skin->_model->getNode()->getWorldMatrix(), Node::getWorldMatrix(), &_jointWorld);
+    }
+    else
+    {
+        memcpy((void*)_jointWorld.m, Node::getWorldMatrix().m, MATRIX_SIZE);
+    }
+
+    return _jointWorld;
+}
+
 }

+ 17 - 0
gameplay/src/Joint.h

@@ -35,6 +35,20 @@ public:
      */
     const Matrix& getInverseBindPose() const;
 
+    /**
+     * Gets the world matrix corresponding to this node.
+     *
+     * @return The world matrix of this node.
+     */
+    const Matrix& getWorldMatrix() const;
+
+    /**
+     * Gets the matrix corresponding to this joint.
+     *
+     * @return The matrix of this joint.
+     */
+    const Matrix& getJointMatrix() const;
+
 protected:
 
     /**
@@ -51,6 +65,7 @@ protected:
      * Creates a new joint with the given id.
      * 
      * @param id ID string.
+     * 
      * @return Newly created joint.
      */
     static Joint* create(const char* id);
@@ -68,6 +83,8 @@ protected:
 
     Matrix _bindPose;
     bool _jointMatrixDirty;
+    MeshSkin* _skin;
+    mutable Matrix _jointWorld;
 };
 
 }

+ 47 - 50
gameplay/src/MaterialParameter.cpp

@@ -305,16 +305,20 @@ void MaterialParameter::bind()
 
 unsigned int MaterialParameter::getAnimationPropertyComponentCount(int propertyId) const
 {
-    switch (MATERIALPARAMETER_ANIMATE_UNIFORM)
+    switch (propertyId)
     {
-        // These types don't support animation.
-        case NONE:
-        case MATRIX:
-        case TEXTURE:
-        case METHOD:
-            return 0;
-        default:
-            return _count;
+    case ANIMATE_UNIFORM:
+        switch(_type)
+        {
+            // These types don't support animation.
+            case NONE:
+            case MATRIX:
+            case TEXTURE:
+            case METHOD:
+                return 0;
+            default:
+                return _count;
+        }
     }
 
     return 0;
@@ -324,28 +328,25 @@ void MaterialParameter::getAnimationPropertyValue(int propertyId, AnimationValue
 {
     switch (propertyId)
     {
-        case MATERIALPARAMETER_ANIMATE_UNIFORM:
+    case ANIMATE_UNIFORM:
+        switch (_type)
         {
-            switch (_type)
-            {
-                case FLOAT:
-                    value->setFloat(0, _value.floatValue);
-                    break;
-                case INT:
-                    value->setFloat(0, _value.intValue);
-                    break;
-                case VECTOR2:
-                case VECTOR3:
-                case VECTOR4:
-                    for (unsigned int i = 0; i < _count; i++)
-                    {
-                        value->setFloat(i, _value.floatPtrValue[i]);
-                    }
-                    break;
-
-                // UNSUPPORTED: NONE, MATRIX, METHOD, TEXTURE 
-            }
+        case FLOAT:
+            value->setFloat(0, _value.floatValue);
+            break;
+        case INT:
+            value->setFloat(0, _value.intValue);
+            break;
+        case VECTOR2:
+        case VECTOR3:
+        case VECTOR4:
+            value->setFloat(_value.floatPtrValue, 0, _count);
+            break;
+        default:
+            // UNSUPPORTED: NONE, MATRIX, METHOD, TEXTURE 
+            break;
         }
+        break;
     }
 }
 
@@ -353,29 +354,25 @@ void MaterialParameter::setAnimationPropertyValue(int propertyId, AnimationValue
 {
     switch (propertyId)
     {
-        case MATERIALPARAMETER_ANIMATE_UNIFORM:
+    case ANIMATE_UNIFORM:
+        switch (_type)
         {
-            switch (_type)
-            {
-                case FLOAT:
-                    _value.floatValue = value->getFloat(0);
-                    break;
-                case INT:
-                    _value.intValue = value->getFloat(0);
-                    break;
-                case VECTOR2:
-                case VECTOR3:
-                case VECTOR4:
-                case MATRIX:
-                    for (unsigned int i = 0; i < _count; i++)
-                    {
-                        _value.floatPtrValue[i] = value->getFloat(i);
-                    }
-                    break;
-
-                // UNSUPPORTED: NONE, MATRIX, METHOD, TEXTURE 
-            }
+        case FLOAT:
+            _value.floatValue = value->getFloat(0);
+            break;
+        case INT:
+            _value.intValue = value->getFloat(0);
+            break;
+        case VECTOR2:
+        case VECTOR3:
+        case VECTOR4:
+            value->getFloat(_value.floatPtrValue, 0, _count);
+            break;
+        default:
+            // UNSUPPORTED: NONE, MATRIX, METHOD, TEXTURE 
+            break;
         }
+        break;
     }
 }
 

+ 5 - 2
gameplay/src/MaterialParameter.h

@@ -7,8 +7,6 @@
 
 #include "Material.h"
 
-#define MATERIALPARAMETER_ANIMATE_UNIFORM           1
-
 namespace gameplay
 {
 
@@ -33,6 +31,11 @@ class MaterialParameter : public AnimationTarget
 
 public:
 
+    /**
+     * MaterialParameter's animation target property.
+     */
+    static const int ANIMATE_UNIFORM = 1;
+
     /**
      * Sets the value of this parameter to a float value.
      */

+ 2 - 1
gameplay/src/Mesh.cpp

@@ -201,12 +201,14 @@ Mesh* Mesh::createLines(Vector3* points, unsigned int pointCount)
     SAFE_RELEASE(format);
     if (mesh == NULL)
     {
+        SAFE_DELETE_ARRAY(vertices);
         return NULL;
     }
 
     mesh->_primitiveType = LINE_STRIP;
     mesh->setVertexData(vertices, 0, pointCount);
 
+    SAFE_DELETE_ARRAY(vertices);
     return mesh;
 }
 
@@ -237,7 +239,6 @@ Mesh* Mesh::createBoundingBox(const BoundingBox& box)
         corners[5].x, corners[5].y, corners[5].z
     };
 
-
     VertexFormat::Element elements[] =
     {
         VertexFormat::Element(VertexFormat::POSITION, 3)

+ 7 - 0
gameplay/src/Mesh.h

@@ -80,6 +80,11 @@ public:
     /**
      * Constructs a new textured 2D quad.
      * 
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param width The width of the quad.
+     * @param height The height of the quad.
+     * 
      * @return The newly created mesh.
      */
     static Mesh* createQuad(float x, float y, float width, float height);
@@ -105,6 +110,8 @@ public:
      * 
      * @param points The array of points.
      * @param pointCount The number of points.
+     * 
+     * @return The newly created mesh.
      */
     static Mesh* createLines(Vector3* points, unsigned int pointCount);
 

+ 7 - 38
gameplay/src/MeshSkin.cpp

@@ -12,7 +12,7 @@
 namespace gameplay
 {
 
-MeshSkin::MeshSkin() : _rootJoint(NULL), _matrixPalette(NULL)
+MeshSkin::MeshSkin() : _matrixPalette(NULL), _model(NULL)
 {
 }
 
@@ -37,37 +37,16 @@ Joint* MeshSkin::getJoint(const char* id) const
 {
     assert(id);
 
-    if (_rootJoint)
+    for (unsigned int i = 0, count = _joints.size(); i < count; ++i)
     {
-        Node* n = _rootJoint->findNode(id, true);
-        if (n != NULL && n->getType() == Node::JOINT)
-        {
-            return static_cast<Joint*>(n);
-        }
-    }
-    else
-    {
-        for (unsigned int i = 0, count = _joints.size(); i < count; ++i)
-        {
-            Joint* j = _joints[i];
-            if (j && j->getId() != NULL && strcmp(j->getId(), id) == 0)
-                return j;
-        }
+        Joint* j = _joints[i];
+        if (j && j->getId() != NULL && strcmp(j->getId(), id) == 0)
+            return j;
     }
 
     return NULL;
 }
 
-Joint* MeshSkin::getRootJoint() const
-{
-    return _rootJoint;
-}
-
-void MeshSkin::setRootJoint(const char* id)
-{
-    _rootJoint = getJoint(id);
-}
-
 void MeshSkin::setJointCount(unsigned int jointCount)
 {
     // Erase the joints vector and release all joints
@@ -101,25 +80,15 @@ void MeshSkin::setJoint(Joint* joint, unsigned int index)
 {
     assert(index < _joints.size());
 
-    bool rootJoint = false;
-
     if (_joints[index])
     {
-        if (_joints[index] == _rootJoint)
-        {
-            rootJoint = true;
-        }
-
+        _joints[index]->_skin = NULL;
         SAFE_RELEASE(_joints[index]);
     }
 
     _joints[index] = joint;
+    _joints[index]->_skin = this;
     joint->addRef();
-
-    if (rootJoint)
-    {
-        _rootJoint = joint;
-    }
 }
 
 Vector4* MeshSkin::getMatrixPalette() const

+ 3 - 16
gameplay/src/MeshSkin.h

@@ -20,6 +20,7 @@ class MeshSkin
 {
     friend class Package;
     friend class Model;
+    friend class Joint;
 
 public:
 
@@ -46,20 +47,6 @@ public:
      */
     Joint* getJoint(const char* id) const;
 
-    /**
-     * Returns the root joint of the skin.
-     * 
-     * @return The root joint.
-     */
-    Joint* getRootJoint() const;
-
-    /**
-     * Sets the root joint for the skin.
-     *
-     * The specified ID must refer to a joint within this MeshSkin.
-     */
-    void setRootJoint(const char* id);
-
     /**
      * Returns the pointer to the Vector4 array for the purpose of binding to a shader.
      * 
@@ -76,7 +63,7 @@ public:
      */
     unsigned int getMatrixPaletteSize() const;
 
-private:
+//private:
 
     /**
      * Constructor.
@@ -119,7 +106,6 @@ private:
     Joint* getJoint(unsigned int index) const;
 
     Matrix _bindShape;
-    Joint* _rootJoint;
     std::vector<Joint*> _joints;
 
     // Pointer to the array of palette matrices.
@@ -127,6 +113,7 @@ private:
     // Each 4x3 row-wise matrix is represented as 3 Vector4's.
     // The number of Vector4's is (_joints.size() * 3).
     Vector4* _matrixPalette;
+    Model* _model;
 };
 
 }

+ 1 - 0
gameplay/src/Model.cpp

@@ -174,6 +174,7 @@ void Model::setSkin(MeshSkin* skin)
             delete _skin;
         }
         _skin = skin;
+        _skin->_model = this;
     }
 }
 

+ 20 - 1
gameplay/src/Node.cpp

@@ -16,7 +16,7 @@ namespace gameplay
 
 Node::Node(const char* id)
     : _scene(NULL), _camera(NULL), _light(NULL), _model(NULL), _audioSource(NULL), _particleEmitter(NULL),
-    _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true), _boundsType(NONE)
+    _physicsRigidBody(NULL), _dirtyBits(NODE_DIRTY_ALL), _notifyHierarchyChanged(true), _boundsType(NONE)
 {
     if (id)
     {
@@ -55,6 +55,8 @@ Node::~Node()
     SAFE_RELEASE(_camera);
     SAFE_RELEASE(_light);
     SAFE_RELEASE(_model);
+    SAFE_RELEASE(_audioSource);
+    SAFE_RELEASE(_particleEmitter);
 }
 
 Node* Node::create(const char* id)
@@ -636,6 +638,23 @@ void Node::setParticleEmitter(ParticleEmitter* emitter)
     }
 }
 
+PhysicsRigidBody* Node::getPhysicsRigidBody()
+{
+    return _physicsRigidBody;
+}
+
+void Node::setPhysicsRigidBody(PhysicsRigidBody::Type type, float mass, float friction,
+        float restitution, float linearDamping, float angularDamping)
+{
+    if (_physicsRigidBody)
+    {
+        SAFE_RELEASE(_physicsRigidBody);
+    }
+    
+    if (type != PhysicsRigidBody::PHYSICS_SHAPE_NONE)
+        _physicsRigidBody = new PhysicsRigidBody(this, type, mass, friction, restitution, linearDamping, angularDamping);
+}
+
 void Node::removeAllChildren()
 {
     _notifyHierarchyChanged = false;

+ 27 - 1
gameplay/src/Node.h

@@ -12,6 +12,7 @@
 #include "Model.h"
 #include "AudioSource.h"
 #include "ParticleEmitter.h"
+#include "PhysicsRigidBody.h"
 #include "BoundingBox.h"
 
 namespace gameplay
@@ -121,7 +122,7 @@ public:
      *
      * @return The world matrix of this node.
      */
-    const Matrix& getWorldMatrix() const;
+    virtual const Matrix& getWorldMatrix() const;
 
     /**
      * Gets the inverse transpose world view matrix corresponding to this node.
@@ -272,6 +273,30 @@ 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* getPhysicsRigidBody();
+
+    /**
+     * Assigns a physics rigid body to this node.
+     * 
+     * Note: This is only allowed for nodes that have a model attached to them.
+     *
+     * @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 (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 bouncyness of
+     *      the rigid body; between 0.0 and 1.0, where 0.0 is minimal bouncyness and 1.0 is maximal bouncyness).
+     * @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).
+     */
+    void setPhysicsRigidBody(PhysicsRigidBody::Type type, float mass, float friction = 0.5,
+        float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
+
     /**
      * Returns the bounding box for the Node, in world space.
      *
@@ -349,6 +374,7 @@ protected:
     Model* _model;
     AudioSource* _audioSource;
     ParticleEmitter* _particleEmitter;
+    PhysicsRigidBody* _physicsRigidBody;
     mutable Matrix _world;
     mutable int _dirtyBits;
     bool _notifyHierarchyChanged;

+ 25 - 20
gameplay/src/Package.cpp

@@ -14,8 +14,9 @@
 
 #define PACKAGE_TYPE_SCENE 1
 #define PACKAGE_TYPE_NODE 2
-#define PACKAGE_TYPE_ANIMATION 3
-#define PACKAGE_TYPE_ANIMATION_CHANNEL 4
+#define PACKAGE_TYPE_ANIMATIONS 3
+#define PACKAGE_TYPE_ANIMATION 4
+#define PACKAGE_TYPE_ANIMATION_CHANNEL 5
 #define PACKAGE_TYPE_MMODEL 10
 #define PACKAGE_TYPE_MATERIAL 16
 #define PACKAGE_TYPE_EFFECT 18
@@ -361,7 +362,7 @@ Scene* Package::loadScene(const char* id)
     for (unsigned int i = 0; i < _referenceCount; ++i)
     {
         Reference* ref = &_references[i];
-        if (ref->type == PACKAGE_TYPE_ANIMATION)
+        if (ref->type == PACKAGE_TYPE_ANIMATIONS)
         {
             // Found a match
             if (fseek(_file, ref->offset, SEEK_SET) != 0)
@@ -369,7 +370,7 @@ Scene* Package::loadScene(const char* id)
                 LOG_ERROR_VARG("Failed to seek to object '%s' in package '%s'.", ref->id.c_str(), _path.c_str());
                 return NULL;
             }
-            readAnimation(ref->id.c_str(), scene);
+            readAnimations(scene);
         }
     }
 
@@ -694,9 +695,6 @@ MeshSkin* Package::readMeshSkin(Scene* sceneContext, Node* nodeContext)
     MeshSkinData* skinData = new MeshSkinData();
     skinData->skin = meshSkin;
 
-    // Read root joint name
-    skinData->rootJoint = readString(_file);
-
     // Read joint count
     unsigned int jointCount;
     if (!read(&jointCount))
@@ -759,12 +757,6 @@ void Package::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
     {
         MeshSkinData* skinData = _meshSkins[i];
 
-        // Load the root joint first to ensure the full joint hierarchy is loaded if
-        // it was not already.
-        Node* rootJoint = loadNode(skinData->rootJoint.c_str(), sceneContext, nodeContext);
-        if (rootJoint == NULL || rootJoint->getType() != Node::JOINT)
-            continue;
-
         // Resolve all joints in skin joint list
         const unsigned int jointCount = skinData->joints.size();
         for (unsigned int j = 0; j < jointCount; ++j)
@@ -785,24 +777,21 @@ void Package::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
             }
         }
 
-        // Set the root joint (must do this after setting all joints above)
-        skinData->skin->setRootJoint(skinData->rootJoint.c_str());
-
         // Done with this MeshSkinData entry
         SAFE_DELETE(_meshSkins[i]);
     }
     _meshSkins.clear();
 }
 
-void Package::readAnimation(const char* id, Scene* scene)
+void Package::readAnimation(Scene* scene)
 {
-    const char* animationId = getIdFromOffset();
+    const std::string animationId = readString(_file);
 
     // read the number of animation channels in this animation
     unsigned int animationChannelCount;
     if (!read(&animationChannelCount))
     {
-        LOG_ERROR_VARG("Failed to read %s for %s: %s", "animationChannelCount", "animation", id);
+        LOG_ERROR_VARG("Failed to read %s for %s: %s", "animationChannelCount", "animation", animationId.c_str());
         return;
     }
 
@@ -810,7 +799,23 @@ void Package::readAnimation(const char* id, Scene* scene)
 
     for (unsigned int i = 0; i < animationChannelCount; i++)
     {
-        animation = readAnimationChannel(scene, animation, animationId);
+        animation = readAnimationChannel(scene, animation, animationId.c_str());
+    }
+}
+
+void Package::readAnimations(Scene* scene)
+{
+    // read the number of animations in this object
+    unsigned int animationCount;
+    if (!read(&animationCount))
+    {
+        LOG_ERROR_VARG("Failed to read %s for %s: %s", "animationCount", "Animations");
+        return;
+    }
+
+    for (unsigned int i = 0; i < animationCount; i++)
+    {
+        readAnimation(scene);
     }
 }
 

+ 47 - 3
gameplay/src/Package.h

@@ -38,6 +38,7 @@ public:
      * If id is NULL then the first scene found is loaded.
      * 
      * @param id The ID of the scene to load (NULL to load the first scene).
+     * 
      * @return The loaded scene, or NULL if the scene could not be loaded.
      */
     Scene* loadScene(const char* id = NULL);
@@ -117,7 +118,6 @@ private:
     {
     public:
         MeshSkin* skin;
-        std::string rootJoint;
         std::vector<std::string> joints;
         std::vector<Matrix> inverseBindPoseMatrices;
     };
@@ -148,7 +148,7 @@ private:
     const char* getIdFromOffset() const;
 
     /**
-     * Returns the ID of the object at the given file offset by searching through the reference table. 
+     * Returns the ID of the object at the given file offset by searching through the reference table.
      * Returns NULL if not found.
      *
      * @param offset The file offset.
@@ -193,8 +193,22 @@ private:
      */
     bool read(unsigned int* ptr);
 
+    /**
+     * Reads an unsigned char from the current file position.
+     * 
+     * @param ptr A pointer to load the value into.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
     bool read(unsigned char* ptr);
 
+    /**
+     * Reads a float from the current file position.
+     * 
+     * @param ptr A pointer to load the value into.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
     bool read(float* ptr);
 
     /**
@@ -217,6 +231,13 @@ private:
      */
     bool readMatrix(float* m);
 
+    /**
+     * Reads an xref string from the current file position.
+     * 
+     * @param id The string to load the ID string into.
+     * 
+     * @return True if successful, false if an error occurred.
+     */
     bool readXref(std::string& id);
 
     /**
@@ -255,7 +276,29 @@ private:
      */
     MeshSkin* readMeshSkin(Scene* sceneContext, Node* nodeContext);
 
-    void readAnimation(const char* id, Scene* scene);
+    /**
+     * Reads an animation from the current file position.
+     * 
+     * @param scene The scene to load the animations into.
+     */
+    void readAnimation(Scene* scene);
+
+    /**
+     * Reads an "animations" object from the current file position and all of the animations contained in it.
+     * 
+     * @param scene The scene to load the animations into.
+     */
+    void readAnimations(Scene* scene);
+
+    /**
+     * Reads an animation channel at the current file position into the given animation.
+     * 
+     * @param scene The scene that the animation is in.
+     * @param animation The animation to the load channel into.
+     * @param animationId The ID of the animation that this channel is loaded into.
+     * 
+     * @return The animation that the channel was loaded into.
+     */
     Animation* readAnimationChannel(Scene* scene, Animation* animation, const char* animationId);
 
     /**
@@ -271,6 +314,7 @@ private:
      */
     void resolveJointReferences(Scene* sceneContext, Node* nodeContext);
 
+private:
     std::string _path;
     unsigned int _referenceCount;
     Reference* _references;

+ 231 - 75
gameplay/src/ParticleEmitter.cpp

@@ -9,10 +9,11 @@
 #include "Quaternion.h"
 #include "Properties.h"
 
-#define PARTICLEEMITTER_EMISSION_RATE_DEFAULT 10
-
 namespace gameplay
 {
+    const int DEFAULT_EMISSION_RATE = 10;
+    const int DEFAULT_PARTICLE_COUNT_MAX = 100;
+    const ParticleEmitter::BlendMode DEFAULT_BLEND_MODE = ParticleEmitter::BLEND_TRANSPARENT;
 
 ParticleEmitter::ParticleEmitter(unsigned int particleCountMax, SpriteBatch* batch) :
     _particleCountMax(particleCountMax), _particleCount(0), _particles(NULL),
@@ -22,39 +23,27 @@ ParticleEmitter::ParticleEmitter(unsigned int particleCountMax, SpriteBatch* bat
     _sizeStartMin(1.0f), _sizeStartMax(1.0f), _sizeEndMin(1.0f), _sizeEndMax(1.0f),
     _energyMin(1000L), _energyMax(1000L),
     _angularVelocityMin(0.0f), _angularVelocityMax(0.0f),
+    _rotationAxis(Vector3::zero()), _rotationSpeedMin(0.0f), _rotationSpeedMax(0.0f), _rotation(Matrix::identity()),
     _colorStart(Color::white()), _colorStartVar(Color::empty()), _colorEnd(Color::white()), _colorEndVar(Color::empty()),
     _position(Vector3::zero()), _positionVar(Vector3::zero()),
     _velocity(Vector3::zero()), _velocityVar(Vector3::one()),
     _acceleration(Vector3::zero()), _accelerationVar(Vector3::zero()),
-    _emissionRate(PARTICLEEMITTER_EMISSION_RATE_DEFAULT),
-    _timePerEmission(0L), _timeLast(0L), _timeRunning(0L),
+    _emissionRate(DEFAULT_EMISSION_RATE),
+    _timePerEmission(0.0f), _timeLast(0L), _timeRunning(0L),
     _spriteFrameCount(1), _spriteFrameDuration(0L), _spriteFrameDurationSecs(0.0f), _spritePercentPerFrame(0.0f),
     _spriteRandomOffset(0), _spriteLooped(false), _spriteAnimating(false),
     _texCoords(NULL), _textureWidth(0), _textureHeight(0), _textureWidthRatio(0), _textureHeightRatio(0)
 {
     _particles = new Particle[particleCountMax];
-    _timePerEmission = 1000L / PARTICLEEMITTER_EMISSION_RATE_DEFAULT;
+    _timePerEmission = 1000.0f / (float)DEFAULT_EMISSION_RATE;
 }
 
 ParticleEmitter::~ParticleEmitter()
 {
     SAFE_RELEASE(_node);
-
-    if (_particles)
-    {
-        delete _particles;
-        _particles = NULL;
-    }
-    if (_texCoords)
-    {
-        delete _texCoords;
-        _texCoords = NULL;
-    }
-    if (_spriteBatch)
-    {
-        delete _spriteBatch;
-        _spriteBatch = NULL;
-    }
+    SAFE_DELETE(_spriteBatch);
+    SAFE_DELETE_ARRAY(_particles);
+    SAFE_DELETE_ARRAY(_texCoords);
 }
 
 ParticleEmitter* ParticleEmitter::create(const char* textureFile, unsigned int particleCountMax)
@@ -95,38 +84,85 @@ ParticleEmitter* ParticleEmitter::create(const char* particleFile)
     Properties* properties = Properties::create(particleFile);
     if (!properties)
     {
+        LOG_ERROR("Error loading ParticleEmitter: Could not load file.");
+        return NULL;
+    }
+
+    // Top level namespace is "particle <particleName>"
+    Properties* particle = properties->getNextNamespace();
+    if (!particle || strcmp(particle->getNamespace(), "particle") != 0)
+    {
+        LOG_ERROR("Error loading ParticleEmitter: No 'particle' namespace found.");
         return NULL;
     }
 
-    std::string spriteTexture = properties->getString("spriteTexture");
+    Properties* texture = particle->getNextNamespace();
+    if (!texture || strcmp(texture->getNamespace(), "texture") != 0)
+    {
+        LOG_ERROR("Error loading ParticleEmitter: No 'texture' namespace found.");
+        return NULL;
+    }
 
-    unsigned int particleCountMax = (unsigned int)properties->getInt("particleCountMax");
-    unsigned int emissionRate = (unsigned int)properties->getInt("emissionRate");
-    int sfactor = properties->getInt("sfactor");
-    int dfactor = properties->getInt("dfactor");
-    
-    bool orbitPosition = properties->getBool("orbitPosition");
-    bool orbitVelocity = properties->getBool("orbitVelocity");
-    bool orbitAcceleration = properties->getBool("orbitAcceleration");
-    bool ellipsoid = properties->getBool("ellipsoid");
-
-    float sizeStartMin = properties->getFloat("sizeStartMin");
-    float sizeStartMax = properties->getFloat("sizeStartMax");
-    float sizeEndMin = properties->getFloat("sizeEndMin");
-    float sizeEndMax = properties->getFloat("sizeEndMax");
-    long energyMin = properties->getLong("energyMin");
-    long energyMax = properties->getLong("energyMax");
-    float angularVelocityMin = properties->getFloat("angularVelocityMin");
-    float angularVelocityMax = properties->getFloat("angularVelocityMax");
+    // Load texture properties.
+    // Path to image file is required.
+    const char* spriteTexture = texture->getID();
+    if (strlen(spriteTexture) == 0)
+    {
+        LOG_ERROR("Error loading ParticleEmitter: No texture filename specified.");
+        return NULL;
+    }
+
+    int spriteFrameCount = texture->getInt("frameCount");
+    float spriteFrameDuration = texture->getFloat("frameDuration");
+    int spriteWidth = texture->getInt("width");
+    int spriteHeight = texture->getInt("height");
+    int spriteRandomOffset = texture->getInt("randomOffset");
+    bool spriteLooped = texture->getBool("looped");
+    bool spriteAnimating = texture->getBool("animating");
+
+    // Emitter properties.
+    unsigned int particleCountMax = (unsigned int)particle->getInt("particleCountMax");
+    if (particleCountMax == 0)
+    {
+        // Set sensible default.
+        particleCountMax = DEFAULT_PARTICLE_COUNT_MAX;
+    }
 
+    unsigned int emissionRate = (unsigned int)particle->getInt("emissionRate");
+    if (emissionRate == 0)
+    {
+        emissionRate = DEFAULT_EMISSION_RATE;
+    }
+
+    const char* blendModeString = particle->getString("blendMode");
+    const BlendMode blendMode = getBlendModeFromString(blendModeString);
+
+    bool orbitPosition = particle->getBool("orbitPosition");
+    bool orbitVelocity = particle->getBool("orbitVelocity");
+    bool orbitAcceleration = particle->getBool("orbitAcceleration");
+    bool ellipsoid = particle->getBool("ellipsoid");
+
+    // Scalar particle properties.
+    float sizeStartMin = particle->getFloat("sizeStartMin");
+    float sizeStartMax = particle->getFloat("sizeStartMax");
+    float sizeEndMin = particle->getFloat("sizeEndMin");
+    float sizeEndMax = particle->getFloat("sizeEndMax");
+    long energyMin = particle->getLong("energyMin");
+    long energyMax = particle->getLong("energyMax");
+    float angularVelocityMin = particle->getFloat("angularVelocityMin");
+    float angularVelocityMax = particle->getFloat("angularVelocityMax");
+    float rotationSpeedMin = particle->getFloat("rotationSpeedMin");
+    float rotationSpeedMax = particle->getFloat("rotationSpeedMax");
+
+    // Vector particle properties.
     Color colorStart;
     Color colorStartVar;
     Color colorEnd;
     Color colorEndVar;
-    properties->getColor("colorStart", &colorStart);
-    properties->getColor("colorStartVar", &colorStartVar);
-    properties->getColor("colorEnd", &colorEnd);
-    properties->getColor("colorEndVar", &colorEndVar);
+    particle->getColor("colorStart", &colorStart);
+    particle->getColor("colorStartVar", &colorStartVar);
+    particle->getColor("colorEnd", &colorEnd);
+    particle->getColor("colorEndVar", &colorEndVar);
 
     Vector3 position;
     Vector3 positionVar;
@@ -134,22 +170,19 @@ ParticleEmitter* ParticleEmitter::create(const char* particleFile)
     Vector3 velocityVar;
     Vector3 acceleration;
     Vector3 accelerationVar;
-    properties->getVector3("position", &position);
-    properties->getVector3("positionVar", &positionVar);
-    properties->getVector3("velocity", &velocity);
-    properties->getVector3("velocityVar", &velocityVar);
-    properties->getVector3("acceleration", &acceleration);
-    properties->getVector3("accelerationVar", &accelerationVar);
-    
-    int spriteFrameCount = properties->getInt("spriteFrameCount");
-    float spriteFrameDuration = properties->getFloat("spriteFrameDuration");
-    int spriteWidth = properties->getInt("spriteWidth");
-    int spriteHeight = properties->getInt("spriteHeight");
-    int spriteRandomOffset = properties->getInt("spriteRandomOffset");
-    bool spriteLooped = properties->getBool("spriteLooped");
-    bool spriteAnimating = properties->getBool("spriteAnimating");
-
-    ParticleEmitter* emitter = ParticleEmitter::create(spriteTexture.c_str(), particleCountMax);
+    Vector3 rotationAxis;
+    Vector3 rotationAxisVar;
+    particle->getVector3("position", &position);
+    particle->getVector3("positionVar", &positionVar);
+    particle->getVector3("velocity", &velocity);
+    particle->getVector3("velocityVar", &velocityVar);
+    particle->getVector3("acceleration", &acceleration);
+    particle->getVector3("accelerationVar", &accelerationVar);
+    particle->getVector3("rotationAxis", &rotationAxis);
+    particle->getVector3("rotationAxisVar", &rotationAxisVar);
+
+    // Apply all properties to a newly created ParticleEmitter.
+    ParticleEmitter* emitter = ParticleEmitter::create(spriteTexture, particleCountMax);
     emitter->setEmissionRate(emissionRate);
     emitter->setOrbitAroundOrigin(orbitPosition, orbitVelocity, orbitAcceleration);
     emitter->setEllipsoid(ellipsoid);
@@ -160,6 +193,7 @@ ParticleEmitter* ParticleEmitter::create(const char* particleFile)
     emitter->setPosition(position, positionVar);
     emitter->setVelocity(velocity, velocityVar);
     emitter->setAcceleration(acceleration, accelerationVar);
+    emitter->setRotation(rotationAxis, rotationAxisVar, rotationSpeedMin, rotationSpeedMax);
 
     emitter->setSpriteAnimating(spriteAnimating);
     emitter->setSpriteLooped(spriteLooped);
@@ -167,11 +201,9 @@ ParticleEmitter* ParticleEmitter::create(const char* particleFile)
     emitter->setSpriteFrameDuration(spriteFrameDuration);
     emitter->setSpriteFrameCoords(spriteFrameCount, spriteWidth, spriteHeight);
 
-    emitter->setBlendMode(sfactor, dfactor);
+    emitter->setBlendMode(blendMode);
 
-    //Properties::destroy(properties);
-    delete properties;
-    properties = NULL;
+    SAFE_DELETE(properties);
 
     return emitter;
 }
@@ -185,10 +217,10 @@ void ParticleEmitter::setNode(Node* node)
 {
     if (_node != node)
     {
-        // Disconnect our current transform.
+        // Disconnect our current node.
         SAFE_RELEASE(_node);
 
-        // Connect the new transform.
+        // Connect the new node.
         _node = node;
 
         if (_node)
@@ -196,7 +228,7 @@ void ParticleEmitter::setNode(Node* node)
             _node->addRef();
 
             // Bind this node's view projection matrix to this emitter's material.
-            _spriteBatch->getMaterial()->getParameter("sb_ortho_projection")->bindValue(node, &Node::getViewProjectionMatrix);
+            _spriteBatch->getMaterial()->getParameter("sb_ortho_projection")->bindValue(_node, &Node::getViewProjectionMatrix);
         }
     }
 }
@@ -333,6 +365,34 @@ void ParticleEmitter::setAngularVelocity(float min, float max)
     _angularVelocityMax = max;
 }
 
+const Vector3& ParticleEmitter::getRotationAxis() const
+{
+    return _rotationAxis;
+}
+
+const Vector3& ParticleEmitter::getRotationAxisVariance() const
+{
+    return _rotationAxisVar;
+}
+
+float ParticleEmitter::getRotationSpeedMin() const
+{
+    return _rotationSpeedMin;
+}
+
+float ParticleEmitter::getRotationSpeedMax() const
+{
+    return _rotationSpeedMax;
+}
+
+void ParticleEmitter::setRotation(const Vector3& axis, const Vector3& axisVariance, float speedMin, float speedMax)
+{
+    _rotationAxis.set(axis);
+    _rotationAxisVar.set(axisVariance);
+    _rotationSpeedMin = speedMin;
+    _rotationSpeedMax = speedMax;
+}
+
 const Vector3& ParticleEmitter::getAcceleration() const
 {
     return _acceleration;
@@ -356,8 +416,14 @@ unsigned int ParticleEmitter::getEmissionRate() const
 
 void ParticleEmitter::setEmissionRate(unsigned int rate)
 {
+    assert(rate);
     _emissionRate = rate;
-    _timePerEmission = 1000L / _emissionRate;
+    _timePerEmission = 1000.0f / (float)_emissionRate;
+}
+
+void ParticleEmitter::setBlendEnabled(bool blend)
+{
+    _spriteBatch->setBlendEnabled(blend);
 }
 
 void ParticleEmitter::setBlendMode(const GLenum sfactor, const GLenum dfactor)
@@ -365,6 +431,55 @@ void ParticleEmitter::setBlendMode(const GLenum sfactor, const GLenum dfactor)
     _spriteBatch->setBlendMode(sfactor, dfactor);
 }
 
+void ParticleEmitter::setBlendMode(BlendMode blendMode)
+{
+    switch (blendMode)
+    {
+        case BLEND_OPAQUE:
+            setBlendEnabled(false);
+            break;
+        case BLEND_TRANSPARENT:
+            setBlendEnabled(true);
+            setBlendMode(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+            break;
+        case BLEND_ADD:
+            setBlendEnabled(true);
+            setBlendMode(GL_ONE, GL_ONE);
+            break;
+        case BLEND_MULTIPLY:
+            setBlendEnabled(true);
+            setBlendMode(GL_ZERO, GL_SRC_COLOR);
+            break;
+    }
+}
+
+const ParticleEmitter::BlendMode ParticleEmitter::getBlendMode() const
+{
+    if (!_spriteBatch->getBlendEnabled())
+    {
+        return BLEND_OPAQUE;
+    }
+
+    GLenum sfactor;
+    GLenum dfactor;
+    _spriteBatch->getBlendMode(&sfactor, &dfactor);
+
+    if (sfactor == GL_SRC_ALPHA && dfactor == GL_ONE_MINUS_SRC_ALPHA)
+    {
+        return BLEND_TRANSPARENT;
+    }
+    else if (sfactor == GL_ONE && dfactor == GL_ONE)
+    {
+        return BLEND_ADD;
+    }
+    else if (sfactor == GL_ZERO && dfactor == GL_SRC_COLOR)
+    {
+        return BLEND_MULTIPLY;
+    }
+
+    return BLEND_CUSTOM;
+}
+
 void ParticleEmitter::setSpriteRandomOffset(int maxOffset)
 {
     _spriteRandomOffset = maxOffset;
@@ -470,6 +585,11 @@ void ParticleEmitter::setSpriteFrameCoords(unsigned int frameCount, int width, i
     setSpriteFrameCoords(frameCount, frameCoords);
 }
 
+bool ParticleEmitter::getActive() const
+{
+    return _active;
+}
+
 void ParticleEmitter::start()
 {
     _active = true;
@@ -553,6 +673,28 @@ inline void ParticleEmitter::generateColor(const Color& base, const Color& varia
     dst->a = base.a + variance.a * MATH_RANDOM_MINUS1_1();
 }
 
+const ParticleEmitter::BlendMode ParticleEmitter::getBlendModeFromString(const char* blendMode)
+{
+    if (strcmp(blendMode, "BLEND_OPAQUE") == 0 || strcmp(blendMode, "OPAQUE") == 0)
+    {
+        return BLEND_OPAQUE;
+    }
+    else if (strcmp(blendMode, "BLEND_TRANSPARENT") == 0 || strcmp(blendMode, "TRANSPARENT") == 0)
+    {
+        return BLEND_TRANSPARENT;
+    }
+    else if (strcmp(blendMode, "BLEND_ADD") == 0 || strcmp(blendMode, "ADD") == 0)
+    {
+        return BLEND_ADD;
+    }
+    else if (strcmp(blendMode, "BLEND_MULTIPLY") == 0 || strcmp(blendMode, "MULTIPLY") == 0)
+    {
+        return BLEND_MULTIPLY;
+    }
+
+    return DEFAULT_BLEND_MODE;
+}
+
 void ParticleEmitter::emit(unsigned int particleCount)
 {
     // Limit particleCount so as not to go over _particleCountMax.
@@ -584,11 +726,13 @@ void ParticleEmitter::emit(unsigned int particleCount)
         p->_sizeEnd = generateScalar(_sizeEndMin, _sizeEndMax);
         p->_angularVelocity = generateScalar(_angularVelocityMin, _angularVelocityMax);
         p->_angle = generateScalar(0.0f, p->_angularVelocity);
+        p->_rotationSpeed = generateScalar(_rotationSpeedMin, _rotationSpeedMax);
 
-        // Initial position, velocity and acceleration can be generated within ellipsoidal domains, or not.
+        // Only initial position can be generated within an ellipsoidal domain.
         generateVector(_position, _positionVar, &p->_position, _ellipsoid);
         generateVector(_velocity, _velocityVar, &p->_velocity, false);
         generateVector(_acceleration, _accelerationVar, &p->_acceleration, false);
+        generateVector(_rotationAxis, _rotationAxisVar, &p->_rotationAxis, false);
 
         // Initial position, velocity and acceleration can all be relative to the emitter's transform.
         // Rotate specified properties by the node's rotation.
@@ -607,6 +751,12 @@ void ParticleEmitter::emit(unsigned int particleCount)
             world.transformPoint(p->_acceleration, &p->_acceleration);
         }
 
+        // The rotation axis always orbits the node.
+        if (p->_rotationSpeed != 0.0f && !p->_rotationAxis.isZero())
+        {
+            world.transformPoint(p->_rotationAxis, &p->_rotationAxis);
+        }
+
         // Translate position relative to the node's world space.
         p->_position.add(translation);
 
@@ -628,11 +778,6 @@ void ParticleEmitter::emit(unsigned int particleCount)
 void ParticleEmitter::update(long elapsedTime)
 {
     // Calculate the time passed since last update.
-    //long time = Game::getGameTime();
-    //long timeDelta = time - _timeLast;
-    //float elapsedTime = (float)timeDelta / 1000.0f;
-    //_timeLast = time;
-
     float elapsedSecs = (float)elapsedTime / 1000.0f;
 
     if (_active && _emissionRate)
@@ -643,7 +788,10 @@ void ParticleEmitter::update(long elapsedTime)
         // How many particles should we emit this frame?
         unsigned int emitCount = _timeRunning / _timePerEmission;
             
-        _timeRunning %= _timePerEmission;
+        if ((int)_timePerEmission > 0)
+        {
+            _timeRunning %= (int)_timePerEmission;
+        }
 
         emit(emitCount);
     }
@@ -656,6 +804,14 @@ void ParticleEmitter::update(long elapsedTime)
 
         if (p->_energy > 0L)
         {
+            if (p->_rotationSpeed != 0.0f && !p->_rotationAxis.isZero())
+            {
+                Matrix::createRotation(p->_rotationAxis, p->_rotationSpeed * elapsedSecs, &_rotation);
+
+                _rotation.transformPoint(p->_velocity, &p->_velocity);
+                _rotation.transformPoint(p->_acceleration, &p->_acceleration);
+            }
+
             // Particle is still alive.
             p->_velocity.x += p->_acceleration.x * elapsedSecs;
             p->_velocity.y += p->_acceleration.y * elapsedSecs;

+ 347 - 22
gameplay/src/ParticleEmitter.h

@@ -19,7 +19,108 @@ namespace gameplay
 class Node;
 
 /**
- * Defines a particle emitter.
+ * Defines a particle emitter that can be made to simulate and render a particle system.
+ * Once created, the emitter can be set on a node in order to follow an object or be placed
+ * within a scene.
+ *
+ * A ParticleEmitter has a texture and a maximum number of particles that can be alive at
+ * once, both of which are set when the ParticleEmitter is created and cannot be changed
+ * from then on.  Particles are rendered as camera-facing billboards using the emitter's
+ * texture.  The ParticleEmitter's texture properties determine whether the texture
+ * is treated as a single image, a texture atlas or an animated sprite.
+ *
+ * A ParticleEmitter also has a number of properties that determine values assigned to
+ * individual particles it emits.  Scalar properties such as particle begin- and end-size
+ * are assigned within a minimum and maximum value; vector properties are assigned within
+ * the domain defined by a base vector and a variance vector as follows: The variance vector
+ * is multiplied by a random scalar between 1 and -1, and the base vector is added to this
+ * result.  This allows a ParticleEmitter to be created which emits particles with properties
+ * that are randomized, yet fit within a well-defined range.  To make a property deterministic,
+ * simply set the minimum to the same value as the maximum for that property or set its
+ * variance to the zero vector.
+ *
+ *
+ * Scalar properties:
+ *
+ * Begin-Size: The size of a newly emitted particle.
+ *
+ * End-Size: The size of a particle at the end of its lifetime.  A particle's size will
+ *           interpolate linearly between its begin-size and end-size over its lifetime.
+ *
+ * Energy: The length of time a particle will remain alive for.
+ *
+ * Angular Velocity: The speed and direction a particle will spin.  Since particles are
+ *                   rendered as billboards, no axis of rotation can be specified.
+ *                   Particles rotate around their center points, around the z-axis in
+ *                   screen space.
+ *
+ *
+ * Vector properties:
+ *
+ * Initial Position: The position of a new particle at the moment it is emitted, relative
+ *                   to the node its ParticleEmitter is set on.  This property is unique
+ *                   in that the initial positions of new particles can be restricted to
+ *                   fit within an ellipsoidal domain; see setEllipsoid().
+ *
+ * Initial Velocity: The velocity of a new particle at the moment it is emitted.  This
+ *                   property is measured in world coordinates per second and modifies
+ *                   a particle's current position each time ParticleEmitter::update()
+ *                   is called.
+ *
+ * Acceleration: The particle's change in velocity, measured in world coordinates per second.
+ *               This property modifies a particle's current position each time 
+ *               ParticleEmitter::update() is called.
+ *
+ * Begin-Color: The color of a newly emitted particle.
+ *
+ * End-Color: The color of a particle at the end of its lifetime.  A particle's color
+ *            will interpolate linearly between its begin-color and end-color over its
+ *            lifetime.
+ *
+ * The vector properties Initial Position, Initial Velocity and Acceleration can be set to
+ * orbit around the origin of a node a ParticleEmitter is set on by that node's rotation matrix.
+ * This allows the rotation of a node, and not just its position, to affect these properties of
+ * newly emitted particles.  An example of where this would be useful would be a water-fountain
+ * emitter attached to the nozzle of a hose.  The initial position and initial velocity would be
+ * set to orbit around the node's origin so that the water would always spray out in the direction
+ * the nozzle was facing.  However, acceleration would not be set to orbit the node's origin in
+ * order for gravity to continue to act in the same direction on water particles, no matter
+ * what direction they were originally aimed.
+ * 
+ * 
+ * Texture / Rendering properties:
+ *
+ * Particles are rendered as screen-facing billboards -- that is, the ParticleEmitter's texture
+ * is used to render particles as images that always face the camera.  For the simplest case,
+ * where the entire texture is used for every particle, the default texture settings can be used.
+ * However, a ParticleEmitter can also be configured to select one of several frames at random
+ * for each particle, or to render each particle as a sprite that animates through the frames
+ * over the course of its lifetime.
+ *
+ * Frame Count: The number of individual images / frames contained in the texture.
+ *
+ * Texture Coordinates: The coordinates within the texture used to render a specific frame.
+ *                      Using a texture that places the frames together, without padding,
+ *                      in left-to-right top-to-bottom order is recommended, as there is a utility
+ *                      method for generating the texture coordinates for such a texture atlas /
+ *                      sprite-map.  See setSpriteFrameCoords().
+ *
+ * Sprite Animating: Set this to enable sprite animation.
+ *
+ * Sprite Looped: If sprites are set to loop, each frame will last for the emitter's frameDuration.
+ *                If sprites are set not to loop, the animation will be timed so that the last frame
+ *                finishes just as a particle dies.  This setting has no effect if the sprite is not
+ *                animating.
+ *
+ * Sprite Random Offset: New particles are created with one of the sprite frames in the emitter's texture.
+ *                       If a maximum offset is set, a random frame from 0 to maxOffset will be selected.
+ *                       If sprite animation is disabled and this offset is set to Frame Count, each
+ *                       particle will use one of the sprite frames for its entire lifetime.
+ *
+ * Blend Mode: Sets the blend mode used by this particle emitter.  The given blend factors will
+ *             be set before rendering the particle system and then will be reset to their original
+ *             values.  Accepts the same symbolic constants as glBlendFunc().
+ *
  */
 class ParticleEmitter : public Ref
 {
@@ -27,13 +128,54 @@ class ParticleEmitter : public Ref
 
 public:
 
+    enum BlendMode
+    {
+        /** 
+         * Opaque blend mode.  Disables blending when rendering particles.
+         */
+        BLEND_OPAQUE,
+
+        /**
+         * Transparent blend mode.  Blends particles with source factor: GL_SRC_ALPHA,
+         * destination factor: GL_ONE_MINUS_SRC_ALPHA.
+         */
+        BLEND_TRANSPARENT,
+
+        /** 
+         * Additive blend mode.  Blends particles with source factor: GL_ONE,
+         * destination factor: GL_ONE.
+         */
+        BLEND_ADD,
+
+        /**
+         * Multiplicative blend mode.  Blends particles with source factor: GL_ZERO,
+         * destination factor: GL_SRC_COLOR.
+         */
+        BLEND_MULTIPLY,
+
+        /**
+         * Indicates that a preset is not in use when returned by getBlendMode().
+         * Has no effect if passed to setBlendMode(BlendMode).
+         * Call setBlendMode(GLenum, GLenum) to set a custom blend mode.
+         */
+        BLEND_CUSTOM
+    };
+
     /**
-     * Creates a particle emitter from a file.
+     * Creates a particle emitter from a .particle file.
+     *
+     * @param particleFile The .particle file to load.
+     * 
+     * @return An initialized ParticleEmitter.
      */
     static ParticleEmitter* create(const char* particleFile);
 
     /**
-     * Creates an empty ParticleEmitter.
+     * Creates an uninitialized ParticleEmitter.
+     *
+     * @param texturePath A path to the image to use as this ParticleEmitter's texture.
+     * @param particleCountMax The maximum number of particles that can be alive at one time
+     *                         in this ParticleEmitter's system.
      */
     static ParticleEmitter* create(const char* texturePath, unsigned int particleCountMax);
 
@@ -54,6 +196,10 @@ public:
     /**
      * Sets whether the vector properties of newly emitted particles are rotated around the node's position
      * by the node's rotation matrix.
+     *
+     * @param orbitPosition Whether to rotate initial particle positions by the node's rotation matrix.
+     * @param orbitVelocity Whether to rotate initial particle velocity vectors by the node's rotation matrix.
+     * @param orbitAcceleration Whether to rotate initial particle acceleration vectors by the node's rotation matrix.
      */
     void setOrbitAroundOrigin(bool orbitPosition, bool orbitVelocity, bool orbitAcceleration);
 
@@ -72,6 +218,8 @@ public:
      * Ellipsoidal domains are somewhat less efficient and only necessary when determining the positions of
      * newly emitted particles.  Call this method with 'true' to make initial position an ellipsoidal domain.
      * The default setting is 'false'.
+     *
+     * @param ellipsoid Whether initial particle positions are generated within an ellipsoidal domain.
      */
     void setEllipsoid(bool ellipsoid);
 
@@ -90,39 +238,68 @@ public:
     float getSizeStartMax() const;
 
     /**
-     * Gets the minimum size that each particle can be at the time when it is ended.
+     * Gets the minimum size that each particle can be at the end of its lifetime.
      *
-     * @return The minimum size that each particle can be at the time when it is ended.
+     * @return The minimum size that each particle can be at the end of its lifetime.
      */
     float getSizeEndMin() const;
 
     /**
-     * Gets the maximum size that each particle can be at the time when it is ended.
+     * Gets the maximum size that each particle can be at the end of its lifetime.
      *
-     * @return The maximum size that each particle can be at the time when it is ended.
+     * @return The maximum size that each particle can be at the end of its lifetime.
      */
     float getSizeEndMax() const;
 
     /**
      * Sets the minimum and maximum size that each particle can be at the time when it is spawned,
      * as well as the minimum and maximum size for particles to be at the end of their lifetimes.
+     *
+     * @param startMin The minimum size that each particle can be at the time when it is started.
+     * @param startMax The maximum size that each particle can be at the time when it is started.
+     * @param endMin The minimum size that each particle can be at the end of its lifetime.
+     * @param endMax The maximum size that each particle can be at the end of its lifetime.
      */
     void setSize(float startMin, float startMax, float endMin, float endMax);
     
+    /**
+     * Gets the base start color of emitted particles.
+     *
+     * @return The base start color of emitted particles.
+     */
     const Color& getColorStart() const;
 
+    /**
+     * Gets the variance of start color of emitted particles.
+     *
+     * @return The variance of start color of emitted particles.
+     */
     const Color& getColorStartVariance() const;
 
+    /**
+     * Gets the base end color of emitted particles.
+     *
+     * @return The base end color of emitted particles.
+     */
     const Color& getColorEnd() const;
 
+    /**
+     * Gets the variance of end color of emitted particles.
+     *
+     * @return The variance of end color of emitted particles.
+     */
     const Color& getColorEndVariance() const;
 
+    /**
+     * Set the start and end colors, and their variances, of particles in this emitter's system.
+     *
+     * @param start The base start color of emitted particles.
+     * @param startVariance The variance of start color of emitted particles.
+     * @param end The base end color of emitted particles.
+     * @param endVariance The variance of end color of emitted particles.
+     */
     void setColor(const Color& start, const Color& startVariance, const Color& end, const Color& endVariance);
 
-    void setColor(const Color& start, const Color& end);
-
-    void setColorVariance(const Color& startVariance, const Color& endVariance);
-
     /**
      * Gets the minimum lifetime of each particle, measured in milliseconds.
      *
@@ -203,37 +380,132 @@ public:
      */
     void setVelocity(const Vector3& velocity, const Vector3& velocityVariance);
 
+    /**
+     * Gets the base acceleration vector of particles.
+     * 
+     * @return The base acceleration vector of particles.
+     */
     const Vector3& getAcceleration() const;
 
+    /**
+     * Gets the variance of acceleration of particles.
+     * 
+     * @return The variance of acceleration of particles.
+     */
     const Vector3& getAccelerationVariance() const;
 
+    /**
+     * Sets the base acceleration vector and its allowed variance for this ParticleEmitter.
+     *
+     * @param acceleration The base acceleration vector of emitted particles.
+     * @param accelerationVariance The variance allowed in the acceleration of emitted particles.
+     */
     void setAcceleration(const Vector3& acceleration, const Vector3& accelerationVariance);
 
     /**
-     * Gets the minimum angular velocity of new particles.
+     * Gets the minimum angular velocity of emitted particles.
+     *
+     * @return The minimum angular velocity of emitted particles.
      */
     float getAngularVelocityMin() const;
 
     /**
-     * Gets the angular velocity variance of new particles.
+     * Gets the maximum angular velocity of emitted particles.
+     *
+     * @return The maximum angular velocity of emitted particles.
      */
     float getAngularVelocityMax() const;
 
     /**
-     * Sets the angular velocity around an axis of emitted particles.
+     * Sets the angular velocity of emitted particles.
+     * This determines the speed of rotation of the particle's screen-facing billboard,
+     * and is not to be confused with its axis and speed of rotation in space.
+     *
+     * @param min The minimum angular velocity.
+     * @param max The maximum angular velocity.
      */
     void setAngularVelocity(float min, float max);
 
     /**
-     * Sets the blend mode used by this particle emitter.
+     * Gets the base rotation axis of emitted particles.
+     *
+     * @return The base rotation axis of emitted particles.
+     */
+    const Vector3& getRotationAxis() const;
+
+    /**
+     * Gets the variance of the rotation axis of emitted particles.
+     *
+     * @return The variance of the rotation axis of emitted particles.
+     */
+    const Vector3& getRotationAxisVariance() const;
+
+    /**
+     * Gets the minimum rotation speed of emitted particles.
+     *
+     * @return The minimum rotation speed of emitted particles.
+     */
+    float getRotationSpeedMin() const;
+
+    /**
+     * Gets the maximum rotation speed of emitted particles.
+     *
+     * @return The maximum rotation speed of emitted particles.
+     */
+    float getRotationSpeedMax() const;
+
+    /**
+     * Sets the axis and speed of particle rotations.  A particle rotates around its
+     * rotation axis in space at its speed, measured in radians per second.  This should not
+     * be confused with a particle's angular velocity.
+     *
+     * @param axis The base rotation axis of emitted particles.
+     * @param axisVariance The variance of the rotation axis of emitted particles.
+     * @param speedMin The minimum rotation speed of emitted particles.
+     * @param speedMax The maximum rotation speed of emitted particles.
+     */
+    void setRotation(const Vector3& axis, const Vector3& axisVariance, float speedMin, float speedMax);
+
+    /**
+     * Enable or disable blending when rendering particles.
+     *
+     * @param blend Whether to enable blending.
+     */
+    void setBlendEnabled(bool blend);
+
+    /**
+     * Set blend mode from the ParticleEmitter::BlendMode enum of common settings.
+     *
+     * @param blendMode The blend mode to set.
+     */
+    void setBlendMode(BlendMode blendMode);
+
+    /**
+     * Sets the blend mode used by this particle emitter.  The given blend factors will
+     * be set before rendering the particle system and then will be reset to their original
+     * values.  Accepts the same symbolic constants as glBlendFunc().
+     *
+     * @param sfactor Specifies how the source blending factors are computed.
+     * @param dfactor Specifies how the destination blending factors are computed.
+     * 
+     * @see glBlendFunc()
      */
     void setBlendMode(const GLenum sfactor, const GLenum dfactor);
 
+    /**
+     * Gets the blend mode used by this particle emitter.
+     *
+     * @return The blend mode used by this particle emitter.
+     */
+    const BlendMode getBlendMode() const;
+
     /**
      * New particles are created with one of the sprite frames in the emitter's texture.
      * If a maximum offset is set, a random frame from 0 to maxOffset will be selected.
      * Set maxOffset to 0 (the default) for all particles to start on the first frame.
      * maxOffset will be clamped to frameCount.
+     *
+     * @param maxOffset The maximum sprite frame offset.
      */
     void setSpriteRandomOffset(int maxOffset);
 
@@ -245,53 +517,94 @@ public:
      * For other offsets, the final frame may be reached earlier.
      * If sprites are not set to animate, this setting has no effect.
      *
+     * @param looped Whether to loop animated sprites.
      * @see ParticleEmitter::setSpriteFrameDuration
      */
     void setSpriteLooped(bool looped);
 
     /**
      * Sets whether particles cycle through the sprite frames.
+     *
+     * @param animating Whether to animate particles through the sprite frames.
      */
     void setSpriteAnimating(bool animating);
 
     /**
      * If sprites are set to loop, each frame will last for this duration.
+     *
+     * @param duration The duration of a single sprite frame, in milliseconds.
      */
     void setSpriteFrameDuration(long duration);
 
     /**
-     * Sets texCoords manually.
+     * Sets the sprite's texture coordinates in texture space.
+     *
+     * @param frameCount The number of frames to set texture coordinates for.
+     * @param texCoords The texture coordinates for all frames, in texture space.
      */
     void setSpriteTexCoords(unsigned int frameCount, float* texCoords);
 
     /**
-     * Sets coords in pixels.
+     * Sets the sprite's texture coordinates in image space (pixels).
+     *
+     * @param frameCount The number of frames to set texture coordinates for.
+     * @param frameCoords A rectangle for each frame representing its position and size
+     *                    within the texture image, measured in pixels.
      */
     void setSpriteFrameCoords(unsigned int frameCount, Rectangle* frameCoords);
 
     /**
-     * When all frames are the same size, this method should be useful.
-     * This might even be the only method we should make public.
+     * Calculates and sets the sprite's texture coordinates based on the width and
+     * height of a single frame, measured in pixels.  This method assumes that there
+     * is no padding between sprite frames and that the first frame is in the top-left
+     * corner of the image.  Frames are ordered in the image from left to right, top to
+     * bottom.
+     *
+     * @param frameCount The number of frames to set texture coordinates for.
+     * @param width The width of a single frame, in pixels.
+     * @param height The height of a single frame, in pixels.
      */
     void setSpriteFrameCoords(unsigned int frameCount, int width, int height);
 
     /**
-     * Starts emitting particles over time.
+     * Gets whether this ParticleEmitter is currently active (started).
+     *
+     * @return Whether this ParticleEmitter is currently active.
+     */
+    bool getActive() const;
+
+    /**
+     * Starts emitting particles over time at this ParticleEmitter's emission rate.
+     *
+     * @see ParticleEmitter::emit()
      */
     void start();
 
     /**
      * Stops emitting particles over time.
+     *
+     * @see ParticleEmitter::emit()
      */
     void stop();
 
     /**
-     * Generates an arbitrary number of particles all at once.
+     * Generates an arbitrary number of particles all at once.  Each newly emitted
+     * particle has its properties assigned within the ranges defined by its ParticleEmitter.
+     *
+     * Note that the maximum number of particles that can be alive at once in a particle
+     * system is defined when a ParticleEmitter is created and cannot be changed.  A call
+     * to emit() cannot cause the particle system to exceed this maximum, so fewer or zero
+     * particles will be emitted if the maximum is or has been reached.
+     *
+     * @param particleCount The number of particles to emit immediately.
      */
     void emit(unsigned int particleCount);
 
     /**
      * Updates the particle system.
+     *
+     * @param elapsedTime The amount of time that has passed since the last call to update(),
+     *                    in milliseconds.
      */
     void update(long elapsedTime);
 
@@ -341,6 +654,11 @@ private:
      */
     void generateColor(const Color& base, const Color& variance, Color* dst);
 
+    /**
+     * Gets a BlendMode enum from a corresponding string.
+     */
+    static const BlendMode getBlendModeFromString(const char* blendMode);
+
     /**
      * Defines the data for a single particle in the system.
      */
@@ -351,11 +669,13 @@ private:
         Vector3 _position;
         Vector3 _velocity;
         Vector3 _acceleration;
+        Vector3 _rotationAxis;
         Color _colorStart;
         Color _colorEnd;
         Color _color;
         float _angularVelocity;
         float _angle;
+        float _rotationSpeed;
         long _energyStart;
         long _energy;
         float _sizeStart;
@@ -393,8 +713,13 @@ private:
     Vector3 _velocityVar;
     Vector3 _acceleration;
     Vector3 _accelerationVar;
+    Vector3 _rotationAxis;
+    Vector3 _rotationAxisVar;
+    float _rotationSpeedMin;
+    float _rotationSpeedMax;
+    Matrix _rotation;
     unsigned int _emissionRate;
-    long _timePerEmission;
+    float _timePerEmission;
     long _timeLast;
     long _timeRunning;
     

+ 143 - 0
gameplay/src/PhysicsController.cpp

@@ -0,0 +1,143 @@
+/*
+ * PhysicsController.cpp
+ */
+
+#include "Base.h"
+#include "PhysicsController.h"
+
+namespace gameplay
+{
+
+// Default gravity is 9.8 along the negative Y axis.
+PhysicsController::PhysicsController()
+	: _gravity(btScalar(0.0), btScalar(-9.8), btScalar(0.0)), _collisionConfiguration(NULL), _dispatcher(NULL),
+	_overlappingPairCache(NULL), _solver(NULL), _world(NULL)
+{
+}
+
+PhysicsController::~PhysicsController()
+{
+}
+
+void PhysicsController::setGravity(Vector3 gravity)
+{
+	_gravity.setX(gravity.x);
+	_gravity.setY(gravity.y);
+	_gravity.setZ(gravity.z);
+
+	if (_world)
+	{
+		_world->setGravity(_gravity);
+	}
+}
+
+void PhysicsController::initialize()
+{
+	// TODO: Should any of this be configurable?
+	_collisionConfiguration = new btDefaultCollisionConfiguration();
+	_dispatcher = new btCollisionDispatcher(_collisionConfiguration);
+	_overlappingPairCache = new btDbvtBroadphase();
+	_solver = new btSequentialImpulseConstraintSolver();
+
+	// Create the world.
+	_world = new btDiscreteDynamicsWorld(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
+	_world->setGravity(_gravity);
+}
+
+void PhysicsController::finalize()
+{
+	// Remove the rigid bodies from the world and delete them.
+	for (int i = _world->getNumCollisionObjects() - 1; i >= 0 ; i--)
+	{
+		btCollisionObject* obj = _world->getCollisionObjectArray()[i];
+		btRigidBody* body = btRigidBody::upcast(obj);
+		if (body && body->getMotionState())
+		{
+			delete body->getMotionState();
+		}
+		_world->removeCollisionObject(obj);
+		delete obj;
+	}
+
+	// Delete all of the collision shapes.
+	for (int i = 0; i < _shapes.size(); i++)
+	{
+		btCollisionShape* shape = _shapes[i];
+		_shapes[i] = 0;
+		delete shape;
+	}
+	_shapes.clear();
+
+	// Clean up the world and its various components.
+	delete _world; 
+	_world = NULL;
+
+	delete _solver; 
+	_solver = NULL;
+
+	delete _overlappingPairCache; 
+	_overlappingPairCache = NULL;
+	
+	delete _dispatcher; 
+	_dispatcher = NULL;
+	
+	delete _collisionConfiguration; 
+	_collisionConfiguration = NULL;
+}
+
+void PhysicsController::pause()
+{
+	// DUMMY FUNCTION
+}
+
+void PhysicsController::resume()
+{
+	// DUMMY FUNCTION
+}
+
+void PhysicsController::update(long elapsedTime)
+{
+	// Update the physics simulation, with a maximum
+	// of 10 simulation steps being performed in a given frame.
+	//
+	// Note that stepSimulation takes elapsed time in seconds
+	// so we divide by 1000 to convert from milliseconds.
+	_world->stepSimulation((float)elapsedTime * 0.001, 10);
+}
+
+btCollisionShape* PhysicsController::getBox(const Vector3& min, const Vector3& max, const btVector3& scale)
+{
+	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));
+	btBoxShape* box = new btBoxShape(halfExtents);
+	_shapes.push_back(box);
+
+	return box;
+}
+
+btCollisionShape* PhysicsController::getSphere(float radius, const btVector3& 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.
+	float uniformScale = scale.x();
+	if (uniformScale < scale.y())
+		uniformScale = scale.y();
+	if (uniformScale < scale.z())
+		uniformScale = scale.z();
+
+	btSphereShape* sphere = new btSphereShape(uniformScale * radius);
+	_shapes.push_back(sphere);
+
+	return sphere;
+}
+
+btCollisionShape* PhysicsController::getTriangleMesh(float* vertexData, int vertexPositionStride, unsigned char* indexData, Mesh::IndexFormat indexFormat)
+{
+	return NULL;
+}
+
+btCollisionShape* PhysicsController::getHeightfield(void* data, int width, int height)
+{
+	return NULL;
+}
+
+}

+ 83 - 0
gameplay/src/PhysicsController.h

@@ -0,0 +1,83 @@
+/*
+ * PhysicsController.h
+ */
+
+#ifndef PHYSICSCONTROLLER_H_
+#define PHYSICSCONTROLLER_H_
+
+#include "PhysicsRigidBody.h"
+
+namespace gameplay
+{
+	
+/**
+ * Defines a class for controlling game physics.
+ */
+class PhysicsController
+{
+	friend class Game;
+	friend class PhysicsRigidBody;
+
+public:
+    
+	/**
+	 * Destructor
+	 */
+	virtual ~PhysicsController();
+
+	/**
+	 * Sets the gravity vector for the simulated physics world.
+	 * 
+	 * @param gravity The gravity vector.
+	 */
+	void setGravity(Vector3 gravity);
+
+private:
+	
+	/**
+	 * Constructor
+	 */
+	PhysicsController();
+
+	/**
+	 * Controller initialize
+	 */
+	void initialize();
+
+	/**
+	 * Controller finalize
+	 */
+    void finalize();
+
+	/**
+	 * Controller pause
+	 */
+    void pause();
+
+	/**
+	 * Controller resume
+	 */
+    void resume();
+
+	/**
+	 * Controller update
+	 */
+    void update(long elapsedTime);
+
+	btCollisionShape* getBox(const Vector3& min, const Vector3& max, const btVector3& scale);
+	btCollisionShape* getSphere(float radius, const btVector3& scale);
+	btCollisionShape* getTriangleMesh(float* vertexData, int vertexPositionStride, unsigned char* indexData, Mesh::IndexFormat indexFormat);
+	btCollisionShape* getHeightfield(void* data, int width, int height);
+
+	btVector3 _gravity;
+	btDefaultCollisionConfiguration* _collisionConfiguration;
+	btCollisionDispatcher* _dispatcher;
+	btBroadphaseInterface* _overlappingPairCache;
+	btSequentialImpulseConstraintSolver* _solver;
+	btDynamicsWorld* _world;
+	btAlignedObjectArray<btCollisionShape*> _shapes;
+};
+
+}
+
+#endif

+ 60 - 0
gameplay/src/PhysicsMotionState.h

@@ -0,0 +1,60 @@
+/*
+ * PhysicsMotionState.h
+ */
+
+#ifndef PHYSICSMOTIONSTATE_H_
+#define PHYSICSMOTIONSTATE_H_
+
+#include "Node.h"
+#include "PhysicsRigidBody.h"
+#include "Transform.h"
+
+namespace gameplay
+{
+
+class PhysicsMotionState : public btMotionState
+{
+	friend class PhysicsRigidBody;
+
+	PhysicsMotionState(Node* node) : _node(node)
+	{
+        // Store the initial world transform (minus the scale) for use by Bullet later on.
+        Quaternion rotation;
+        const Matrix& m = _node->getWorldMatrix();
+        m.getRotation(&rotation);
+		_worldTransform = btTransform(btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w), 
+			btVector3(m.m[12], m.m[13], m.m[14]));
+	}
+
+public:
+
+	virtual ~PhysicsMotionState()
+	{
+    }
+
+    virtual void getWorldTransform(btTransform &transform) const
+	{
+		transform = _worldTransform;
+    }
+
+    virtual void setWorldTransform(const btTransform &transform)
+	{
+		// Calculate the actual world transform by 
+		// taking into account the center of mass offset.
+		_worldTransform = transform;
+
+        const btQuaternion& rot = _worldTransform.getRotation();
+		const btVector3& pos = _worldTransform.getOrigin();
+
+		_node->setRotation(rot.x(), rot.y(), rot.z(), rot.w());
+		_node->setTranslation(pos.x(), pos.y(), pos.z());
+    }
+
+private:
+	Node* _node;
+	btTransform _worldTransform;
+};
+
+}
+
+#endif

+ 147 - 0
gameplay/src/PhysicsRigidBody.cpp

@@ -0,0 +1,147 @@
+/*
+ * PhysicsRigidBody.cpp
+ */
+
+#include "Base.h"
+#include "Game.h"
+#include "PhysicsController.h"
+#include "PhysicsMotionState.h"
+#include "PhysicsRigidBody.h"
+
+namespace gameplay
+{
+
+PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, float mass, 
+		float friction, float restitution, float linearDamping, float angularDamping)
+        : _node(node), _body(NULL)
+{
+	switch (type)
+	{
+		case PhysicsRigidBody::PHYSICS_SHAPE_BOX:
+		{
+			const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
+
+            PhysicsController* physics = Game::getInstance()->getPhysicsController();
+            btCollisionShape* shape = physics->getBox(box.min, box.max, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
+			
+			_body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping);
+			break;
+		}
+		case PhysicsRigidBody::PHYSICS_SHAPE_SPHERE:
+		{
+			const BoundingSphere& sphere = node->getModel()->getMesh()->getBoundingSphere();
+
+			PhysicsController* physics = Game::getInstance()->getPhysicsController();
+			btCollisionShape* shape = physics->getSphere(sphere.radius, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
+
+            _body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping);
+			break;
+		}
+		case PhysicsRigidBody::PHYSICS_SHAPE_TRIANGLE_MESH:
+		{
+			//btTriangleIndexVertexArray meshShape(numTriangles, indexPointer, indexStride, numVertices, vertexPointer, vertexStride);
+			//btCollisionShape* shape = btBvhTriangleMeshShape(meshShape, true);
+
+			//_body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping);
+			break;
+		}
+		case PhysicsRigidBody::PHYSICS_SHAPE_HEIGHTFIELD:
+		{
+			//btCollisionShape* shape = btHeightfieldTerrainShape(width, length, data, scale, minHeight, maxHeight, upAxis, dataType, false);
+
+			//_body = createBulletRigidBody(shape, mass, node, friction, restitution, linearDamping, angularDamping);
+			break;
+		}
+	}
+}
+
+PhysicsRigidBody::~PhysicsRigidBody()
+{
+    if (_body)
+    {
+        if (_body->getMotionState())
+            delete _body->getMotionState();
+
+        delete _body;
+    }
+}
+
+void PhysicsRigidBody::applyForce(const Vector3& force, const Vector3* relativePosition)
+{
+    // If the force is significant enough, activate the rigid body 
+    // to make sure that it isn't sleeping and apply the force.
+    if (force.lengthSquared() > MATH_EPSILON)
+    {
+        _body->activate();
+        if (relativePosition)
+		    _body->applyForce(btVector3(force.x, force.y, force.z), btVector3(relativePosition->x, relativePosition->y, relativePosition->z));
+	    else
+		    _body->applyCentralForce(btVector3(force.x, force.y, force.z));
+    }
+}
+
+void PhysicsRigidBody::applyImpulse(const Vector3& impulse, const Vector3* relativePosition)
+{
+    // If the impulse is significant enough, activate the rigid body 
+    // to make sure that it isn't sleeping and apply the impulse.
+    if (impulse.lengthSquared() > MATH_EPSILON)
+    {
+        _body->activate();
+
+	    if (relativePosition)
+        {
+		    _body->applyImpulse(btVector3(impulse.x, impulse.y, impulse.z), btVector3(relativePosition->x, relativePosition->y, relativePosition->z));
+        }
+	    else
+		    _body->applyCentralImpulse(btVector3(impulse.x, impulse.y, impulse.z));
+    }
+}
+
+void PhysicsRigidBody::applyTorque(const Vector3& torque)
+{
+    // If the torque is significant enough, activate the rigid body 
+    // to make sure that it isn't sleeping and apply the torque.
+    if (torque.lengthSquared() > MATH_EPSILON)
+    {
+        _body->activate();
+	    _body->applyTorque(btVector3(torque.x, torque.y, torque.z));
+    }
+}
+
+void PhysicsRigidBody::applyTorqueImpulse(const Vector3& torque)
+{
+    // If the torque impulse is significant enough, activate the rigid body 
+    // to make sure that it isn't sleeping and apply the torque impulse.
+    if (torque.lengthSquared() > MATH_EPSILON)
+    {
+        _body->activate();
+        _body->applyTorqueImpulse(btVector3(torque.x, torque.y, torque.z));
+    }
+}
+
+btRigidBody* PhysicsRigidBody::createBulletRigidBody(btCollisionShape* shape, float mass, Node* node,
+    float friction, float restitution, float linearDamping, float angularDamping)
+{
+	// If the mass is non-zero, then the object is dynamic
+	// and we need to calculate the local inertia.
+	btVector3 localInertia(0.0, 0.0, 0.0);
+	if (mass != 0.0)
+		shape->calculateLocalInertia(mass, localInertia);
+
+	// Create the Bullet physics rigid body object.
+	PhysicsMotionState* motionState = new PhysicsMotionState(node);
+	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 = new btRigidBody(rbInfo);
+
+	// Add the rigid body to the physics world.
+	PhysicsController* physics = Game::getInstance()->getPhysicsController();
+	physics->_world->addRigidBody(body);
+
+	return body;
+}
+
+}

+ 164 - 0
gameplay/src/PhysicsRigidBody.h

@@ -0,0 +1,164 @@
+/*
+ * PhysicsRigidBody.h
+ */
+
+#ifndef PHYSICSRIGIDBODY_H_
+#define PHYSICSRIGIDBODY_H_
+
+#include "Mesh.h"
+#include "Ref.h"
+#include "Transform.h"
+#include "Vector3.h"
+
+namespace gameplay
+{
+
+class Node;
+
+/**
+ * Defines a class for physics rigid bodies.
+ */
+class PhysicsRigidBody : public Ref
+{
+public:
+
+	enum Type
+	{
+		PHYSICS_SHAPE_BOX,
+		PHYSICS_SHAPE_SPHERE,
+		PHYSICS_SHAPE_TRIANGLE_MESH,
+		PHYSICS_SHAPE_HEIGHTFIELD,
+        PHYSICS_SHAPE_NONE
+	};
+
+    void applyForce(const Vector3& force, const Vector3* relativePosition = NULL);
+	void applyImpulse(const Vector3& impulse, const Vector3* relativePosition = NULL);
+	void applyTorque(const Vector3& torque);
+    void applyTorqueImpulse(const Vector3& torque);
+
+    inline void setFriction(float friction);
+    inline void setRestitution(float restitution);
+    inline void setAnisotropicFriction(const Vector3& friction);
+    inline void setGravity(const Vector3& gravity);
+	inline float getFriction();
+    inline float getRestitution();
+    inline Vector3 getAnisotropicFriction();
+    inline Vector3 getGravity();
+
+    inline void setLinearVelocity(const Vector3& velocity);
+	inline void setAngularVelocity(const Vector3& velocity);
+
+	inline Vector3 getLinearVelocity();
+	inline Vector3 getAngularVelocity();
+
+	inline void setDamping(float linearDamping, float angularDamping);
+	inline float getLinearDamping();
+	inline float getAngularDamping();
+
+private:
+
+	friend class Node;
+	friend class PhysicsMotionState;
+
+    PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, float mass, 
+		float friction = 0.5, float restitution = 0.0, float linearDamping = 0.0, float angularDamping = 0.0);
+    ~PhysicsRigidBody();
+
+    /**
+     * Private copy constructor to disallow copying.
+     */
+    PhysicsRigidBody(const PhysicsRigidBody& body) {}
+
+	static btRigidBody* createBulletRigidBody(btCollisionShape* shape, float mass, Node* node,
+        float friction, float restitution, float linearDamping, float angularDamping);
+
+	btRigidBody* _body;
+    Node* _node;
+};
+
+// Inline functions.
+
+void PhysicsRigidBody::setFriction(float friction)
+{
+    _body->setFriction(friction);
+}
+
+void PhysicsRigidBody::setRestitution(float restitution)
+{
+    _body->setRestitution(restitution);
+}
+
+float PhysicsRigidBody::getFriction()
+{
+    return _body->getFriction();
+}
+
+float PhysicsRigidBody::getRestitution()
+{
+    return _body->getRestitution();
+}
+
+void PhysicsRigidBody::setLinearVelocity(const Vector3& velocity)
+{
+	_body->setLinearVelocity(btVector3(velocity.x, velocity.y, velocity.z));
+}
+
+void PhysicsRigidBody::setAngularVelocity(const Vector3& velocity)
+{
+	_body->setAngularVelocity(btVector3(velocity.x, velocity.y, velocity.z));
+}
+
+// TODO: We can't cache these Vector3 values, but should we have member variables
+// that are set each time instead of creating a new Vector3 every time?
+Vector3 PhysicsRigidBody::getLinearVelocity()
+{
+	const btVector3& v = _body->getLinearVelocity();
+	return Vector3(v.x(), v.y(), v.z());
+}
+
+Vector3 PhysicsRigidBody::getAngularVelocity()
+{
+	const btVector3& v = _body->getAngularVelocity();
+	return Vector3(v.x(), v.y(), v.z());
+}
+
+Vector3 PhysicsRigidBody::getAnisotropicFriction()
+{
+    const btVector3& af = _body->getAnisotropicFriction();
+    return Vector3(af.x(), af.y(), af.z());
+}
+
+Vector3 PhysicsRigidBody::getGravity()
+{
+    const btVector3& g = _body->getGravity();
+    return Vector3(g.x(), g.y(), g.z());
+}
+
+void PhysicsRigidBody::setDamping(float linearDamping, float angularDamping)
+{
+    _body->setDamping(linearDamping, angularDamping);
+}
+
+float PhysicsRigidBody::getLinearDamping()
+{
+	return _body->getLinearDamping();
+}
+
+float PhysicsRigidBody::getAngularDamping()
+{
+	return _body->getAngularDamping();
+}
+
+void PhysicsRigidBody::setAnisotropicFriction(const Vector3& friction)
+{
+    _body->setAnisotropicFriction(btVector3(friction.x, friction.y, friction.z));
+}
+
+void PhysicsRigidBody::setGravity(const Vector3& gravity)
+{
+    _body->setGravity(btVector3(gravity.x, gravity.y, gravity.z));
+}
+
+}
+
+#endif

+ 4 - 0
gameplay/src/PlatformWin32.cpp

@@ -521,6 +521,10 @@ int Platform::enterMessagePump()
             _game->frame();
             SwapBuffers(__hdc);
         }
+
+        // If we are done, then exit.
+        if (_game->getState() == Game::UNINITIALIZED)
+            break;
     }
 
     return msg.wParam;

+ 78 - 55
gameplay/src/Properties.cpp

@@ -12,6 +12,8 @@ namespace gameplay
 Properties::Properties(FILE* file)
 {
     readProperties(file);
+    _mapIt = _map.begin();
+    _propertiesIt = _properties.begin();
 }
 
 Properties::Properties(FILE* file, const char* name, const char* id) : _namespace(name)
@@ -21,6 +23,8 @@ Properties::Properties(FILE* file, const char* name, const char* id) : _namespac
         _id = id;
     }
     readProperties(file);
+    _mapIt = _map.begin();
+    _propertiesIt = _properties.begin();
 }
 
 Properties* Properties::create(const char* filePath)
@@ -159,7 +163,7 @@ void Properties::readProperties(FILE* file)
                             }
                             else
                             {
-                                _map[name] = string();
+                                _map[name] = std::string();
                             }
                         }
                     }
@@ -219,20 +223,41 @@ char* Properties::trimWhiteSpace(char *str)
     return str;
 }
 
-const map<string, string>* Properties::getMap() const
+const char* Properties::getNextProperty()
 {
-    return &_map;
+    if (_mapIt != _map.end())
+    {
+        if (!_mapIt->first.empty())
+        {
+            const char* c_str = _mapIt->first.c_str();
+            _mapIt++;
+            return c_str; //_mapIt++->first.c_str();
+        }
+    }
+    return NULL;
+}
+
+Properties* Properties::getNextNamespace()
+{
+    if (_propertiesIt < _properties.end())
+    {
+        Properties* space = *_propertiesIt;
+        _propertiesIt++;
+        return space;
+    }
+    return NULL;
 }
 
-const vector<Properties*>* Properties::getProperties() const
+void Properties::rewind()
 {
-    return &_properties;
+    _mapIt = _map.begin();
+    _propertiesIt = _properties.begin();
 }
 
-Properties* Properties::getProperties(const char* id) const
+Properties* Properties::getNamespace(const char* id) const
 {
     Properties* ret = NULL;
-    vector<Properties*>::const_iterator it;
+    std::vector<Properties*>::const_iterator it;
     
     for (it = _properties.begin(); it < _properties.end(); it++)
     {
@@ -243,7 +268,7 @@ Properties* Properties::getProperties(const char* id) const
         }
         
         // Search recursively.
-        ret = ret->getProperties(id);
+        ret = ret->getNamespace(id);
         if (ret != NULL)
         {
             return ret;
@@ -296,7 +321,7 @@ int Properties::getInt(const char* name) const
 {
     if (exists(name))
     {
-        string valueString = _map.find(name)->second;
+        std::string valueString = _map.find(name)->second;
 
         int value;
         int scanned;
@@ -316,7 +341,7 @@ float Properties::getFloat(const char* name) const
 {
     if (exists(name))
     {
-        string valueString = _map.find(name)->second;
+        std::string valueString = _map.find(name)->second;
 
         float value;
         int scanned;
@@ -332,40 +357,38 @@ float Properties::getFloat(const char* name) const
     return 0.0f;
 }
 
-void Properties::getFloatArray(const char* name, float* out, unsigned int length) const
+bool Properties::getFloatArray(const char* name, float* out, unsigned int count) const
 {
     assert(out);
 
     if (exists(name))
     {
-        string valueString = _map.find(name)->second;
+        std::string valueString = _map.find(name)->second;
 
-        for (unsigned int i = 0; i < length; i++)
+        for (unsigned int i = 0; i < count; i++)
         {
             int scanned;
             scanned = sscanf(valueString.c_str(), "%f", &out[i]);
             if (scanned != 1)
             {
                 LOG_ERROR_VARG("Error parsing property: %s", name);
-                out = NULL;
-                return;
+                return false;
             }
 
             int position = valueString.find(',');
             valueString.erase(0, position + 1);
+            return true;
         }
     }
-    else
-    {
-        out = NULL;
-    }
+    
+    return false;
 }
 
 long Properties::getLong(const char* name) const
 {
     if (exists(name))
     {
-        string valueString = _map.find(name)->second;
+        std::string valueString = _map.find(name)->second;
 
         long value;
         int scanned;
@@ -381,13 +404,13 @@ long Properties::getLong(const char* name) const
     return 0L;
 }
     
-void Properties::getMatrix(const char* name, Matrix* out) const
+bool Properties::getMatrix(const char* name, Matrix* out) const
 {
     assert(out);
 
     if (exists(name))
     {
-        string valueString = _map.find(name)->second;
+        std::string valueString = _map.find(name)->second;
 
         float m[16];
         int scanned;
@@ -398,24 +421,24 @@ void Properties::getMatrix(const char* name, Matrix* out) const
         {
             LOG_ERROR_VARG("Error parsing property: %s", name);
             out->setIdentity();
-            return;
+            return false;
         }
     
         out->set(m);
+        return true;
     }
-    else
-    {
-        out->setIdentity();
-    }
+
+    out->setIdentity();
+    return false;
 }
 
-void Properties::getVector2(const char* name, Vector2* out) const
+bool Properties::getVector2(const char* name, Vector2* out) const
 {
     assert(out);
 
     if (exists(name))
     {
-        string valueString = _map.find(name)->second;
+        std::string valueString = _map.find(name)->second;
 
         float x, y;
         int scanned;
@@ -424,24 +447,24 @@ void Properties::getVector2(const char* name, Vector2* out) const
         {
             LOG_ERROR_VARG("Error parsing property: %s", name);
             out->set(0.0f, 0.0f);
-            return;
+            return false;
         }
 
         out->set(x, y);
+        return true;
     }
-    else
-    {
-        out->set(0.0f, 0.0f);
-    }
+    
+    out->set(0.0f, 0.0f);
+    return false;
 }
 
-void Properties::getVector3(const char* name, Vector3* out) const
+bool Properties::getVector3(const char* name, Vector3* out) const
 {
     assert(out);
 
     if (exists(name))
     {
-        string valueString = _map.find(name)->second;
+        std::string valueString = _map.find(name)->second;
 
         float x, y, z;
         int scanned;
@@ -450,24 +473,24 @@ void Properties::getVector3(const char* name, Vector3* out) const
         {
             LOG_ERROR_VARG("Error parsing property: %s", name);
             out->set(0.0f, 0.0f, 0.0f);
-            return;
+            return false;
         }
 
         out->set(x, y, z);
+        return true;
     }
-    else
-    {
-        out->set(0.0f, 0.0f, 0.0f);
-    }
+    
+    out->set(0.0f, 0.0f, 0.0f);
+    return false;
 }
 
-void Properties::getVector4(const char* name, Vector4* out) const
+bool Properties::getVector4(const char* name, Vector4* out) const
 {
     assert(out);
 
     if (exists(name))
     {
-        string valueString = _map.find(name)->second;
+        std::string valueString = _map.find(name)->second;
 
         float x, y, z, w;
         int scanned;
@@ -476,24 +499,24 @@ void Properties::getVector4(const char* name, Vector4* out) const
         {
             LOG_ERROR_VARG("Error parsing property: %s", name);
             out->set(0.0f, 0.0f, 0.0f, 0.0f);
-            return;
+            return false;
         }
 
         out->set(x, y, z, w);
+        return true;
     }
-    else
-    {
-        out->set(0.0f, 0.0f, 0.0f, 0.0f);
-    }
+    
+    out->set(0.0f, 0.0f, 0.0f, 0.0f);
+    return false;
 }
 
-void Properties::getColor(const char* name, Color* out) const
+bool Properties::getColor(const char* name, Color* out) const
 {
     assert(out);
 
     if (exists(name))
     {
-        string valueString = _map.find(name)->second;
+        std::string valueString = _map.find(name)->second;
 
         float r, g, b, a;
         int scanned;
@@ -502,15 +525,15 @@ void Properties::getColor(const char* name, Color* out) const
         {
             LOG_ERROR_VARG("Error parsing property: %s", name);
             out->set(0.0f, 0.0f, 0.0f, 1.0f);
-            return;
+            return false;
         }
 
         out->set(r, g, b, a);
+        return true;
     }
-    else
-    {
-        out->set(0.0f, 0.0f, 0.0f, 1.0f);
-    }
+    
+    out->set(0.0f, 0.0f, 0.0f, 1.0f);
+    return false;
 }
 
 }

+ 261 - 24
gameplay/src/Properties.h

@@ -9,44 +9,194 @@
 #include "Matrix.h"
 #include "Color.h"
 
-using namespace std;
-
 namespace gameplay
 {
 
+/**
+ * Defines a utility for loading text files in the GamePlay "properties" files
+ * and reading primitive types and GamePlay math classes out of them.
+ *
+ * This class is used by ParticleEmitter, Animation and Materials to create objects
+ * of these types from text files.
+ *
+ * A properties file has very simple syntax and can contain only namespaces and
+ * name/value pairs (the properties of a namespace).  
+ * The file can have any file extension a user specifies.
+ *
+ * Here's an example of a simple
+ * file that uses all the available features of the markup language:
+ *
+ * --- File Start: example.properties ---
+ *
+ * // This is a comment.
+ *
+ * // This property is in the default namespace:
+ * integerProperty = 5
+ *
+ * // This line defines a namespace of type "mynamespace" without an ID:
+ * mynamespace
+ * {
+ *      // This namespace can be retrieved by searching for its ID, "spriteTexture":
+ *      texture spriteTexture 
+ *      {
+ *          fileName = sprite.png
+ *          width = 64
+ *          height = 64
+ *      }
+ *
+ *      // This property is in the "space" namespace:
+ *      booleanProperty = true
+ *
+ *      // It's legal to have a name without a value if you leave out the '=' character:
+ *      foo
+ *
+ *      // In fact, the '=' character is optional if you'd rather write:
+ *      bar 23
+ *
+ *      // But don't write this or you'll get an error:
+ *      // illegalProperty =
+ *
+ *      // Or this:
+ *      // = 15
+ *
+ *      // Properties objects let you retrieve values as various types.
+ *      floatProperty = 3.333
+ *      stringProperty = This is a string.
+ *      vector3Property = 1.0, 5.0, 3.55
+ *      colorProperty = 1.0, 0.4, 0.0, 1.0
+ * }
+ * --- File End ---
+ *
+ * Retrieving information out of a file like this could be done in two ways.  If the
+ * available namespaces and name/value pairs are known in advance they can be queried by ID or name.
+ * For example, if the namespace "spriteTexture" and its properties are required then they can
+ * be retrieved with a call to getNamespace() followed by calls to getString() and getInt().
+ * A namespace is stored and retrieved as a Properties object.
+ * Reading the spriteTexture properties out of the file above in this way could be done with the following code:
+ *
+ *      // Create the top-level Properties object.
+ *      Properties* properties = Properties::create("example.properties");
+ *      // Retrieve the "spriteTexture" namespace.
+ *      Properties* spriteTexture = properties->getNamespace("spriteTexture");
+ *
+ *      // Get the values of known texture properties out of the namespace.
+ *      const char* fileName = spriteTexture->getString("fileName");
+ *      int width = spriteTexture->getInt("width");
+ *      int height = spriteTexture->getInt("height");
+ *
+ *      // Deleting the top-level Properties object will clean up all nested namespaces.
+ *      SAFE_DELETE(properties);
+ *
+ * On the other hand, if the structure of the file is not known in advance its 
+ * namespaces and name/value pairs can be retrieved one by one using the getNextNamespace()
+ * and getNextProperty() methods.  The following method prints the contents of any properties file
+ * to the console:
+ *
+ * void printProperties(Properties* properties)
+ * {
+ *     // Print the name and ID of the current namespace.
+ *     const char* spacename = properties->getNamespace();
+ *     const char* id = properties->getID();
+ *     WARN_VARG("Namespace: %s  ID: %s\n{", spacename, id);
+ *
+ *     // Print all properties in this namespace.
+ *     const char* name = properties->getNextProperty();
+ *     const char* value = NULL;
+ *     while (name != NULL)
+ *     {
+ *         value = properties->getString(name);
+ *         WARN_VARG("%s = %s", name, value);
+ *         name = properties->getNextProperty();
+ *     }
+ *     WARN("}\n");
+ *
+ *     // Print the properties of every namespace within this one.
+ *     Properties* space = properties->getNextNamespace();
+ *     while (space != NULL)
+ *     {
+ *         printProperties(space);
+ *         space = properties->getNextNamespace();
+ *     }
+ *  }
+ *
+ * Note that this method does not keep track of the namespace hierarchy, but could be
+ * modified to do so.  Also note that nothing in a properties file indicates the type
+ * of a property. If the type is unknown, its string can be retrieved and interpreted
+ * as necessary.
+ */
 class Properties
 {
 public:
+
+    /**
+     * Creates a Properties runtime settings from a specified file path.
+     *
+     * @param filePath The file to create the properties from.
+     */
     static Properties* create(const char* filePath);
 
+    /**
+     * Destructor.
+     */
     ~Properties();
 
     /**
-     * Get the map of key/value pairs that were parsed into this Properties object.
+     * Get the name of the next property.
      */
-    const map<string, string>* getMap() const;
+    const char* getNextProperty();
 
-    // Get all inner namespaces.
-    const vector<Properties*>* getProperties() const;
+    /**
+     * Get the next namespace.
+     */
+    Properties* getNextNamespace();
 
-    // Get a specific namespace by ID.
-    Properties* getProperties(const char* id) const;
+    /** 
+     * Rewind the getNextProperty() and getNextNamespace() iterators
+     * to the beginning of the file.
+     */
+    void rewind();
+
+    /**
+     * Get a specific namespace by ID.  This method will perform a depth-first search
+     * on all namespaces and inner namespaces within this Property.
+     *
+     * @param The ID of a specific namespace.
+     * 
+     * @return A properties object with the given ID.
+     */
+    Properties* getNamespace(const char* id) const;
 
+    /**
+     * Get the name of this Property's namespace.
+     *
+     * @return The name of this Property's namespace.
+     */
     const char* getNamespace() const;
+
+    /**
+     * Get the ID of this Property's namespace. The ID should be a unique identifier,
+     * but its uniqueness is not enforced.
+     *
+     * @return The ID of this Property's namespace.
+     */
     const char* getID() const;
 
     /**
      * Check if a property with the given name is specified in this Properties object.
      *
      * @param name The name of the property to query.
+     * 
+     * @return True if the property exists, false otherwise.
      */
     bool exists(const char* name) const;
 
     /**
-     * Get the value of the given property as a string.  This can always be retrieved,
+     * Get the value of the given property as a string. This can always be retrieved,
      * whatever the intended type of the property.
      *
      * @param name The name of the property to interpret.
+     * 
+     * @return The value of the given property as a string, or the empty string if no property with that name exists.
      */
     const char* getString(const char* name) const;
 
@@ -54,28 +204,44 @@ public:
      * Interpret the value of the given property as a boolean.
      *
      * @param name The name of the property to interpret.
-     * @return true if the string value of the property is "true".  Otherwise, return false.
+     * 
+     * @return true if the property exists and its value is "true", otherwise false.
      */
     bool getBool(const char* name) const;
 
     /**
      * Interpret the value of the given property as an integer.
+     * If the property does not exist, zero will be returned.
+     * If the property exists but could not be scanned, an error will be logged and zero will be returned.
      *
      * @param name The name of the property to interpret.
+     * 
+     * @return The value of the given property interpreted as an integer.
+     *   Zero if the property does not exist or could not be scanned.
      */
     int getInt(const char* name) const;
 
     /**
      * Interpret the value of the given property as a floating-point number.
+     * If the property does not exist, zero will be returned.
+     * If the property exists but could not be scanned, an error will be logged and zero will be returned.
      *
      * @param name The name of the property to interpret.
+     * 
+     * @return The value of the given property interpreted as a float.
+     *   Zero if the property does not exist or could not be scanned.
      */
     float getFloat(const char* name) const;
 
     /**
      * Interpret the value of the given property as a long integer.
+     * If the property does not exist, zero will be returned.
+     * If the property exists but could not be scanned, an error will be logged and zero will be returned.
      *
      * @param name The name of the property to interpret.
+     * 
+     * @return The value of the given property interpreted as a long.
+     *   Zero if the property does not exist or could not be scanned.
      */
     long getLong(const char* name) const;
     
@@ -84,31 +250,102 @@ public:
      * the floats into the given array.
      *
      * @param name The name of the property to interpret.
+     * @param out The array to set to this property's interpreted value.
+     * @param count The number of float elements to read.
+     * 
+     * @return true if the property was found and all elements in the array parsed successfully, false if not.
      */
-    void getFloatArray(const char* name, float* out, unsigned int length) const;
+    bool getFloatArray(const char* name, float* out, unsigned int count) const;
     
-    // Math classes.
-    void getMatrix(const char* name, Matrix* out) const;
-    void getVector2(const char* name, Vector2* out) const;
-    void getVector3(const char* name, Vector3* out) const;
-    void getVector4(const char* name, Vector4* out) const;
-    void getColor(const char* name, Color* out) const;
+    /**
+     * Interpret the value of the given property as a Matrix.
+     * If the property does not exist, out will be set to the identity matrix.
+     * If the property exists but could not be scanned, an error will be logged and out will be set
+     * to the identity matrix.
+     *
+     * @param name The name of the property to interpret.
+     * @param out The matrix to set to this property's interpreted value.
+     * 
+     * @return True on success, false if the property does not exist or could not be scanned.
+     */
+    bool getMatrix(const char* name, Matrix* out) const;
+
+    /**
+     * Interpret the value of the given property as a Vector2.
+     * If the property does not exist, out will be set to Vector2(0.0f, 0.0f).
+     * If the property exists but could not be scanned, an error will be logged and out will be set
+     * to Vector2(0.0f, 0.0f).
+     *
+     * @param name The name of the property to interpret.
+     * @param out The matrix to set to this property's interpreted value.
+     * 
+     * @return True on success, false if the property does not exist or could not be scanned.
+     */
+    bool getVector2(const char* name, Vector2* out) const;
+
+    /**
+     * Interpret the value of the given property as a Vector3.
+     * If the property does not exist, out will be set to Vector3(0.0f, 0.0f, 0.0f).
+     * If the property exists but could not be scanned, an error will be logged and out will be set
+     * to Vector3(0.0f, 0.0f, 0.0f).
+     *
+     * @param name The name of the property to interpret.
+     * @param out The matrix to set to this property's interpreted value.
+     * 
+     * @return True on success, false if the property does not exist or could not be scanned.
+     */
+    bool getVector3(const char* name, Vector3* out) const;
+
+    /**
+     * Interpret the value of the given property as a Vector4.
+     * If the property does not exist, out will be set to Vector4(0.0f, 0.0f, 0.0f, 0.0f).
+     * If the property exists but could not be scanned, an error will be logged and out will be set
+     * to Vector4(0.0f, 0.0f, 0.0f, 0.0f).
+     *
+     * @param name The name of the property to interpret.
+     * @param out The matrix to set to this property's interpreted value.
+     * 
+     * @return True on success, false if the property does not exist or could not be scanned.
+     */
+    bool getVector4(const char* name, Vector4* out) const;
+
+    /**
+     * Interpret the value of the given property as a Color.
+     * If the property does not exist, out will be set to Color(0.0f, 0.0f, 0.0f, 0.0f).
+     * If the property exists but could not be scanned, an error will be logged and out will be set
+     * to Color(0.0f, 0.0f, 0.0f, 0.0f).
+     *
+     * @param name The name of the property to interpret.
+     * @param out The matrix to set to this property's interpreted value.
+     * 
+     * @return True on success, false if the property does not exist or could not be scanned.
+     */
+    bool getColor(const char* name, Color* out) const;
 
 private:
-    // Constructor from top of file.
+    
+    /**
+     * Constructor.
+     */
     Properties(FILE* file);
-    // Constructor from beginning of namespace.
+
+    /**
+     * Constructor. Read from the beginning of namespace specified
+     */
     Properties(FILE* file, const char* name, const char* id = NULL);
 
     void readProperties(FILE* file);
+
     void skipWhiteSpace(FILE* file);
-    char* trimWhiteSpace(char* str);
 
+    char* trimWhiteSpace(char* str);
 
-    string _namespace;
-    string _id;
-    map<string, string> _map;
-    vector<Properties*> _properties;
+    std::string _namespace;
+    std::string _id;
+    std::map<std::string, std::string> _map;
+    std::map<std::string, std::string>::const_iterator _mapIt;
+    std::vector<Properties*> _properties;
+    std::vector<Properties*>::const_iterator _propertiesIt;
 };
 
 }

+ 1 - 0
gameplay/src/Quaternion.h

@@ -177,6 +177,7 @@ public:
      * quaternion is already unit-length.
      *
      * @param dst A quaternion to store the inverse in.
+     * 
      * @return true if the inverse can be computed, false otherwise.
      */
     bool inverse(Quaternion* dst) const;

+ 40 - 9
gameplay/src/SpriteBatch.cpp

@@ -53,7 +53,7 @@ SpriteBatch::SpriteBatch() :
     _texture(NULL), _material(NULL), _vaPosition(-1), _vaTexCoord(-1), _vaColor(-1),
     _textureWidthRatio(0.0f), _textureHeightRatio(0.0f), _capacity(0), _count(0),
     _vertices(NULL), _verticesPtr(NULL), _indices(NULL), _indicesPtr(NULL), _index(0),
-    _sfactor(GL_SRC_ALPHA), _dfactor(GL_ONE_MINUS_SRC_ALPHA),
+    _blend(true), _sfactor(GL_SRC_ALPHA), _dfactor(GL_ONE_MINUS_SRC_ALPHA),
     _drawing(false), _projectionMatrix(NULL)
 {
 }
@@ -345,13 +345,21 @@ void SpriteBatch::end()
         GLint sfactorAlpha;
         GLint dfactorAlpha;
         glGetBooleanv(GL_BLEND, &blend);
-        glGetIntegerv(GL_BLEND_SRC_RGB, &sfactorRGB);
-        glGetIntegerv(GL_BLEND_DST_RGB, &dfactorRGB);
-        glGetIntegerv(GL_BLEND_SRC_ALPHA, &sfactorAlpha);
-        glGetIntegerv(GL_BLEND_DST_ALPHA, &dfactorAlpha);
 
-        glEnable(GL_BLEND);
-        glBlendFunc(_sfactor, _dfactor);
+        if (_blend)
+        {
+            glGetIntegerv(GL_BLEND_SRC_RGB, &sfactorRGB);
+            glGetIntegerv(GL_BLEND_DST_RGB, &dfactorRGB);
+            glGetIntegerv(GL_BLEND_SRC_ALPHA, &sfactorAlpha);
+            glGetIntegerv(GL_BLEND_DST_ALPHA, &dfactorAlpha);
+
+            glEnable(GL_BLEND);
+            glBlendFunc(_sfactor, _dfactor);
+        }
+        else
+        {
+            glDisable(GL_BLEND);
+        }
 
         // Bind our material.
         _material->bind();
@@ -377,11 +385,15 @@ void SpriteBatch::end()
         glDisableVertexAttribArray(_vaColor);
 
         // Reset blend function.
-        if (!blend)
+        if (blend)
+        {
+            glEnable(GL_BLEND);
+            glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
+        }
+        else
         {
             glDisable(GL_BLEND);
         }
-        glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
     }
 
     _drawing = false;
@@ -454,12 +466,31 @@ void SpriteBatch::resizeBatch(unsigned int capacity)
     }
 }
 
+void SpriteBatch::setBlendEnabled(bool blend)
+{
+    _blend = blend;
+}
+
+bool SpriteBatch::getBlendEnabled() const
+{
+    return _blend;
+}
+
 void SpriteBatch::setBlendMode(const GLenum sfactor, const GLenum dfactor)
 {
     _sfactor = sfactor;
     _dfactor = dfactor;
 }
 
+void SpriteBatch::getBlendMode(GLenum* sfactor, GLenum* dfactor)
+{
+    assert(sfactor);
+    assert(dfactor);
+
+    *sfactor = _sfactor;
+    *dfactor = _dfactor;
+}
+
 Material* SpriteBatch::getMaterial()
 {
     return _material;

+ 28 - 0
gameplay/src/SpriteBatch.h

@@ -157,12 +157,39 @@ public:
      */
     void end();
 
+    /**
+     * Enable or disable blending when this SpriteBatch is rendered.
+     *
+     * @param blend Whether to enable blending.
+     */
+    void setBlendEnabled(bool blend);
+
+    /**
+     * Gets whether blending is enabled on this SpriteBatch.
+     *
+     * @return Whether blending is enabled on this SpriteBatch.
+     */
+    bool getBlendEnabled() const;
+
     /**
      * Sets the blend mode to use when rendering.  This blend mode will be set before
      * rendering and then immediately reset to its previous value.
+     *
+     * @param sfactor Specifies how the source blending factors are computed.
+     * @param dfactor Specifies how the destination blending factors are computed.
+     *
+     * @see glBlendFunc()
      */
     void setBlendMode(const GLenum sfactor, const GLenum dfactor);
 
+    /**
+     * Get the blend factors used by this SpriteBatch.
+     *
+     * @param sfactor Pointer to store the source factor in.
+     * @param dfactor Pointer to store the destination factor in.
+     */
+    void getBlendMode(GLenum* sfactor, GLenum* dfactor);
+
     /**
      * Gets the material used by this batch.
      * 
@@ -203,6 +230,7 @@ private:
     unsigned short* _indices;
     unsigned short* _indicesPtr;
     unsigned short _index;
+    bool _blend;
     GLint _sfactor;
     GLint _dfactor;
     bool _drawing;

+ 51 - 51
gameplay/src/Transform.cpp

@@ -549,28 +549,28 @@ unsigned int Transform::getAnimationPropertyComponentCount(int propertyId) const
 {
     switch (propertyId)
     {
-        case TRANSFORM_ANIMATE_SCALE_X:
-        case TRANSFORM_ANIMATE_SCALE_Y:
-        case TRANSFORM_ANIMATE_SCALE_Z:
-        case TRANSFORM_ANIMATE_TRANSLATE_X:
-        case TRANSFORM_ANIMATE_TRANSLATE_Y:
-        case TRANSFORM_ANIMATE_TRANSLATE_Z:
+        case ANIMATE_SCALE_X:
+        case ANIMATE_SCALE_Y:
+        case ANIMATE_SCALE_Z:
+        case ANIMATE_TRANSLATE_X:
+        case ANIMATE_TRANSLATE_Y:
+        case ANIMATE_TRANSLATE_Z:
             return 1;
-        case TRANSFORM_ANIMATE_SCALE_XY:
-        case TRANSFORM_ANIMATE_SCALE_XZ:
-        case TRANSFORM_ANIMATE_SCALE_YZ:
-        case TRANSFORM_ANIMATE_TRANSLATE_XY:
-        case TRANSFORM_ANIMATE_TRANSLATE_XZ:
-        case TRANSFORM_ANIMATE_TRANSLATE_YZ:
+        case ANIMATE_SCALE_XY:
+        case ANIMATE_SCALE_XZ:
+        case ANIMATE_SCALE_YZ:
+        case ANIMATE_TRANSLATE_XY:
+        case ANIMATE_TRANSLATE_XZ:
+        case ANIMATE_TRANSLATE_YZ:
             return 2;
-        case TRANSFORM_ANIMATE_SCALE:
-        case TRANSFORM_ANIMATE_TRANSLATE:
+        case ANIMATE_SCALE:
+        case ANIMATE_TRANSLATE:
             return 3;
-        case TRANSFORM_ANIMATE_ROTATE:
+        case ANIMATE_ROTATE:
             return 4;
-        case TRANSFORM_ANIMATE_ROTATE_TRANSLATE:
+        case ANIMATE_ROTATE_TRANSLATE:
             return 7;
-        case TRANSFORM_ANIMATE_SCALE_ROTATE_TRANSLATE:
+        case ANIMATE_SCALE_ROTATE_TRANSLATE:
             return 10;
         default:
             return -1;
@@ -581,65 +581,65 @@ void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
 {
     switch (propertyId)
     {
-        case TRANSFORM_ANIMATE_SCALE:
+        case ANIMATE_SCALE:
             value->setFloat(0, _scale.x);
             value->setFloat(1, _scale.y);
             value->setFloat(2, _scale.z);
             break;
-        case TRANSFORM_ANIMATE_SCALE_X:
+        case ANIMATE_SCALE_X:
             value->setFloat(0, _scale.x);
             break;
-        case TRANSFORM_ANIMATE_SCALE_Y:
+        case ANIMATE_SCALE_Y:
             value->setFloat(0, _scale.y);
             break;
-        case TRANSFORM_ANIMATE_SCALE_Z:
+        case ANIMATE_SCALE_Z:
             value->setFloat(0, _scale.z);
             break;
-        case TRANSFORM_ANIMATE_SCALE_XY:
+        case ANIMATE_SCALE_XY:
             value->setFloat(0, _scale.x);
             value->setFloat(1, _scale.y);
             break;
-        case TRANSFORM_ANIMATE_SCALE_XZ:
+        case ANIMATE_SCALE_XZ:
             value->setFloat(0, _scale.x);
             value->setFloat(1, _scale.z);
             break;
-        case TRANSFORM_ANIMATE_SCALE_YZ:
+        case ANIMATE_SCALE_YZ:
             value->setFloat(0, _scale.y);
             value->setFloat(1, _scale.z);
             break;
-        case TRANSFORM_ANIMATE_ROTATE:
+        case ANIMATE_ROTATE:
             value->setFloat(0, _rotation.x);
             value->setFloat(1, _rotation.y);
             value->setFloat(2, _rotation.z);
             value->setFloat(3, _rotation.w);
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE:
+        case ANIMATE_TRANSLATE:
             value->setFloat(0, _translation.x);
             value->setFloat(1, _translation.y);
             value->setFloat(2, _translation.z);
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_X:
+        case ANIMATE_TRANSLATE_X:
             value->setFloat(0, _translation.x);
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_Y:
+        case ANIMATE_TRANSLATE_Y:
             value->setFloat(0, _translation.y);
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_Z:
+        case ANIMATE_TRANSLATE_Z:
             value->setFloat(0, _translation.z);
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_XY:
+        case ANIMATE_TRANSLATE_XY:
             value->setFloat(0, _translation.x);
             value->setFloat(1, _translation.y);
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_XZ:
+        case ANIMATE_TRANSLATE_XZ:
             value->setFloat(0, _translation.x);
             value->setFloat(1, _translation.z);
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_YZ:
+        case ANIMATE_TRANSLATE_YZ:
             value->setFloat(0, _translation.y);
             value->setFloat(1, _translation.z);
             break;
-        case TRANSFORM_ANIMATE_ROTATE_TRANSLATE:
+        case ANIMATE_ROTATE_TRANSLATE:
             value->setFloat(0, _rotation.x);
             value->setFloat(1, _rotation.y);
             value->setFloat(2, _rotation.z);
@@ -648,7 +648,7 @@ void Transform::getAnimationPropertyValue(int propertyId, AnimationValue* value)
             value->setFloat(5, _translation.y);
             value->setFloat(6, _translation.z);
             break;
-        case TRANSFORM_ANIMATE_SCALE_ROTATE_TRANSLATE:
+        case ANIMATE_SCALE_ROTATE_TRANSLATE:
             value->setFloat(0, _scale.x);
             value->setFloat(1, _scale.y);
             value->setFloat(2, _scale.z);
@@ -669,62 +669,62 @@ void Transform::setAnimationPropertyValue(int propertyId, AnimationValue* value)
 {
     switch (propertyId)
     {
-        case TRANSFORM_ANIMATE_SCALE:
+        case ANIMATE_SCALE:
             setScale(value->getFloat(0), value->getFloat(1), value->getFloat(2));
             break;
-        case TRANSFORM_ANIMATE_SCALE_X:
+        case ANIMATE_SCALE_X:
             setScaleX(value->getFloat(0));
             break;
-        case TRANSFORM_ANIMATE_SCALE_Y:
+        case ANIMATE_SCALE_Y:
             setScaleY(value->getFloat(0));
             break;
-        case TRANSFORM_ANIMATE_SCALE_Z:
+        case ANIMATE_SCALE_Z:
             setScaleZ(value->getFloat(0));
             break;
-        case TRANSFORM_ANIMATE_SCALE_XY:
+        case ANIMATE_SCALE_XY:
             setScaleX(value->getFloat(0));
             setScaleY(value->getFloat(1));
             break;
-        case TRANSFORM_ANIMATE_SCALE_XZ:
+        case ANIMATE_SCALE_XZ:
             setScaleX(value->getFloat(0));
             setScaleZ(value->getFloat(1));
             break;
-        case TRANSFORM_ANIMATE_SCALE_YZ:
+        case ANIMATE_SCALE_YZ:
             setScaleY(value->getFloat(0));
             setScaleZ(value->getFloat(1));
             break;
-        case TRANSFORM_ANIMATE_ROTATE:
+        case ANIMATE_ROTATE:
             setRotation(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE:
+        case ANIMATE_TRANSLATE:
             setTranslation(value->getFloat(0), value->getFloat(1), value->getFloat(2));
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_X:
+        case ANIMATE_TRANSLATE_X:
             setTranslationX(value->getFloat(0));
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_Y:
+        case ANIMATE_TRANSLATE_Y:
             setTranslationY(value->getFloat(0));
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_Z:
+        case ANIMATE_TRANSLATE_Z:
             setTranslationZ(value->getFloat(0));
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_XY:
+        case ANIMATE_TRANSLATE_XY:
             setTranslationX(value->getFloat(0));
             setTranslationY(value->getFloat(1));
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_XZ:
+        case ANIMATE_TRANSLATE_XZ:
             setTranslationX(value->getFloat(0));
             setTranslationZ(value->getFloat(1));
             break;
-        case TRANSFORM_ANIMATE_TRANSLATE_YZ:
+        case ANIMATE_TRANSLATE_YZ:
             setTranslationY(value->getFloat(0));
             setTranslationZ(value->getFloat(1));
             break;
-        case TRANSFORM_ANIMATE_ROTATE_TRANSLATE:
+        case ANIMATE_ROTATE_TRANSLATE:
             setRotation(value->getFloat(0), value->getFloat(1), value->getFloat(2), value->getFloat(3));
             setTranslation(value->getFloat(4), value->getFloat(5), value->getFloat(6));
             break;
-        case TRANSFORM_ANIMATE_SCALE_ROTATE_TRANSLATE:
+        case ANIMATE_SCALE_ROTATE_TRANSLATE:
             setScale(value->getFloat(0), value->getFloat(1), value->getFloat(2));
             setRotation(value->getFloat(3), value->getFloat(4), value->getFloat(5), value->getFloat(6));
             setTranslation(value->getFloat(7), value->getFloat(8), value->getFloat(9));

+ 85 - 84
gameplay/src/Transform.h

@@ -12,90 +12,6 @@
 #include <functional>
 #include "AnimationTarget.h"
 
-/**
- * Scale animation property. Data=sx,sy,sz
- */
-#define TRANSFORM_ANIMATE_SCALE                     1
-
-/**
- * Scale x animation property. Data=sx
- */
-#define TRANSFORM_ANIMATE_SCALE_X                   2
-
-/**
- * Scale y animation property. Data=sy
- */
-#define TRANSFORM_ANIMATE_SCALE_Y                   3
-
-/**
- * Scale z animation property. Data=sz
- */
-#define TRANSFORM_ANIMATE_SCALE_Z                   4
-
-/**
- * Scale xy animation property. Data=sx,sy
- */
-#define TRANSFORM_ANIMATE_SCALE_XY                  5
-
-/**
- * Scale xz animation property. Data=sx,sz
- */
-#define TRANSFORM_ANIMATE_SCALE_XZ                  6
-
-/**
- * Scale yz animation property. Data=sy,sz
- */
-#define TRANSFORM_ANIMATE_SCALE_YZ                  7
-
-/**
- * Rotation animation property. Data=qx,qy,qz,qw (as quaternion).
- */
-#define TRANSFORM_ANIMATE_ROTATE                    8
-
-/**
- * Translate animation property. Data=tx,ty,tz
- */
-#define TRANSFORM_ANIMATE_TRANSLATE                 9
-
-/**
- * Translate x animation property. Data=tx
- */
-#define TRANSFORM_ANIMATE_TRANSLATE_X               10
-
-/**
- * Translate y animation property. Data=ty
- */
-#define TRANSFORM_ANIMATE_TRANSLATE_Y               11
-
-/**
- * Translate z animation property. Data=tz
- */
-#define TRANSFORM_ANIMATE_TRANSLATE_Z               12
-
-/**
- * Translate xy animation property. Data=tx,ty
- */
-#define TRANSFORM_ANIMATE_TRANSLATE_XY              13
-
-/**
- * Translate xz animation property. Data=tx,tz
- */
-#define TRANSFORM_ANIMATE_TRANSLATE_XZ              14
-
-/**
- * Translate yz animation property. Data=ty,tz
- */
-#define TRANSFORM_ANIMATE_TRANSLATE_YZ              15
-
-/**
- * Rotation + Translation animation property (Rigid Body). Data=qx,qy,qz,qw,tx,ty,tz
- */
-#define TRANSFORM_ANIMATE_ROTATE_TRANSLATE          16
-
-/**
- * Scale, Rotation + Translation animation property. Data=sx,sy,sz,qx,qy,qz,qw,tx,ty,tz
- */
-#define TRANSFORM_ANIMATE_SCALE_ROTATE_TRANSLATE    17
 
 namespace gameplay
 {
@@ -120,6 +36,91 @@ class Transform : public AnimationTarget
 {
 public:
 
+    /**
+     * Scale animation property. Data=sx,sy,sz
+     */
+    static const int ANIMATE_SCALE = 1;
+
+    /**
+     * Scale x animation property. Data=sx
+     */
+    static const int ANIMATE_SCALE_X = 2;
+
+    /**
+     * Scale y animation property. Data=sy
+     */
+    static const int ANIMATE_SCALE_Y = 3;
+
+    /**
+     * Scale z animation property. Data=sz
+     */
+    static const int ANIMATE_SCALE_Z = 4;
+
+    /**
+     * Scale xy animation property. Data=sx,sy
+     */
+    static const int ANIMATE_SCALE_XY = 5;
+
+    /**
+     * Scale xz animation property. Data=sx,sz
+     */
+    static const int ANIMATE_SCALE_XZ = 6;
+
+    /**
+     * Scale yz animation property. Data=sy,sz
+     */
+    static const int ANIMATE_SCALE_YZ = 7;
+
+    /**
+     * Rotation animation property. Data=qx,qy,qz,qw (as quaternion).
+     */
+    static const int ANIMATE_ROTATE = 8;
+
+    /**
+     * Translate animation property. Data=tx,ty,tz
+     */
+    static const int ANIMATE_TRANSLATE = 9;
+
+    /**
+     * Translate x animation property. Data=tx
+     */
+    static const int ANIMATE_TRANSLATE_X = 10;
+
+    /**
+     * Translate y animation property. Data=ty
+     */
+    static const int ANIMATE_TRANSLATE_Y = 11;
+
+    /**
+     * Translate z animation property. Data=tz
+     */
+    static const int ANIMATE_TRANSLATE_Z = 12;
+
+    /**
+     * Translate xy animation property. Data=tx,ty
+     */
+    static const int ANIMATE_TRANSLATE_XY = 13;
+
+    /**
+     * Translate xz animation property. Data=tx,tz
+     */
+    static const int ANIMATE_TRANSLATE_XZ = 14;
+
+    /**
+     * Translate yz animation property. Data=ty,tz
+     */
+    static const int ANIMATE_TRANSLATE_YZ = 15;
+
+    /**
+     * Rotation + Translation animation property (Rigid Body). Data=qx,qy,qz,qw,tx,ty,tz
+     */
+    static const int ANIMATE_ROTATE_TRANSLATE = 16;
+
+    /**
+     * Scale, Rotation + Translation animation property. Data=sx,sy,sz,qx,qy,qz,qw,tx,ty,tz
+     */
+    static const int ANIMATE_SCALE_ROTATE_TRANSLATE = 17;
+
     /**
      * Listener interface for Transform events.
      */

+ 69 - 6
gameplay/src/Tree.h

@@ -9,48 +9,111 @@ namespace gameplay
 {
 
 /**
- * Defines a class that provides a tree hierarchy, which supporting
+ * Defines a class that provides a tree hierarchy, which includes
  * a doubly linked list of siblings and a list of children.
+ *
+ * This class is intended to be extended rather than used as a stand-alone
+ * data structure (this is reflected by the protected constructor). Classes
+ * wishing to expose a tree-like structure may inherit this class. An example
+ * is the Node class, which extends Tree<Node> to provide support for nesting of
+ * nodes within nodes and tracking of parent/child relationships in a scene
+ * graph.
  */
 template <class T>
 class Tree
 {
 public:
 
-    Tree();
-
     /**
-     * Destructor.
+     * Adds a child to this item in the tree.
+     *
+     * @param child The child to add.
      */
-    virtual ~Tree();
-
     virtual void addChild(T* child);
 
+    /**
+     * Removes a child from this item in the tree.
+     *
+     * @param child The child to remove.
+     */
     virtual void removeChild(T* child);
 
+    /**
+     * Removes all children under this item in the tree.
+     */
     virtual void removeAllChildren();
 
+    /**
+     * Returns the first child for this item in the tree.
+     *
+     * @return The first child.
+     */
     T* getFirstChild() const;
 
+    /**
+     * Returns the first sibling of this item in the tree.
+     *
+     * @return The first sibling.
+     */
     T* getNextSibling() const;
 
+    /**
+     * Returns the previous sibling to this item in the tree.
+     *
+     * @return The previous sibling.
+     */
     T* getPreviousSibling() const;
 
+    /**
+     * Returns the parent of this item in the tree.
+     *
+     * @return The parent.
+     */
     T* getParent() const;
 
+    /**
+     * Returns the number of direct children of this item.
+     *
+     * @return The number of children.
+     */
     unsigned int getChildCount() const;
 
 protected:
 
+    /**
+     * Constructor.
+     */
+    Tree();
+
+    /**
+     * Destructor.
+     */
+    virtual ~Tree();
+
     /**
      * Removes this item from its list.
      */
     void remove();
 
+    /**
+     * Called when a child is added to this item in the tree.
+     * 
+     * @param child The child that was added.
+     */
     virtual void childAdded(T* child);
 
+    /**
+     * Called when a child is removed from this item in the tree.
+     *
+     * @param child The child that was removed.
+     */
     virtual void childRemoved(T* child);
 
+    /**
+     * Called when the parent of this item changes.
+     *
+     * @param oldParent The previous parent for this item.
+     */
     virtual void parentChanged(T* oldParent);
 
     T* _firstChild;

+ 4 - 4
gameplay/src/Vector3.cpp

@@ -174,7 +174,7 @@ void Vector3::cross(const Vector3& v1, const Vector3& v2, Vector3* dst)
     dst->z = z;
 }
 
-float Vector3::distance(const Vector3& v)
+float Vector3::distance(const Vector3& v) const
 {
     float dx = v.x - x;
     float dy = v.y - y;
@@ -183,7 +183,7 @@ float Vector3::distance(const Vector3& v)
     return sqrtf(dx * dx + dy * dy + dz * dz);
 }
 
-float Vector3::distanceSquared(const Vector3& v)
+float Vector3::distanceSquared(const Vector3& v) const
 {
     float dx = v.x - x;
     float dy = v.y - y;
@@ -202,12 +202,12 @@ float Vector3::dot(const Vector3& v1, const Vector3& v2)
     return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
 }
 
-float Vector3::length()
+float Vector3::length() const
 {
     return sqrtf(x * x + y * y + z * z);
 }
 
-float Vector3::lengthSquared()
+float Vector3::lengthSquared() const
 {
     return (x * x + y * y + z * z);
 }

+ 4 - 4
gameplay/src/Vector3.h

@@ -199,7 +199,7 @@ public:
      * 
      * @see distanceSquared
      */
-    float distance(const Vector3& v);
+    float distance(const Vector3& v) const;
 
     /**
      * Returns the squared distance between this vector and v.
@@ -215,7 +215,7 @@ public:
      * 
      * @see distance
      */
-    float distanceSquared(const Vector3& v);
+    float distanceSquared(const Vector3& v) const;
 
     /**
      * Returns the dot product of this vector and the specified vector.
@@ -243,7 +243,7 @@ public:
      * 
      * @see lengthSquared
      */
-    float length();
+    float length() const;
 
     /**
      * Returns the squared length of this vector.
@@ -257,7 +257,7 @@ public:
      * 
      * @see length
      */
-    float lengthSquared();
+    float lengthSquared() const;
 
     /**
      * Negates this vector.