瀏覽代碼

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

Steve Grenier 14 年之前
父節點
當前提交
7292ddf639
共有 100 個文件被更改,包括 5689 次插入1679 次删除
  1. 12 10
      .gitignore
  2. 57 0
      gameplay-encoder/README.md
  3. 9 5
      gameplay-encoder/gameplay-encoder.vcxproj
  4. 192 149
      gameplay-encoder/gameplay-encoder.vcxproj.filters
  5. 1 2
      gameplay-encoder/gameplay-encoder.vcxproj.user
  6. 22 0
      gameplay-encoder/gameplay-encoder.xcodeproj/project.pbxproj
  7. 2 2
      gameplay-encoder/src/Animation.cpp
  8. 126 2
      gameplay-encoder/src/AnimationChannel.cpp
  9. 16 0
      gameplay-encoder/src/AnimationChannel.h
  10. 2 2
      gameplay-encoder/src/Animations.cpp
  11. 1 21
      gameplay-encoder/src/Base.cpp
  12. 0 1
      gameplay-encoder/src/Base.h
  13. 0 54
      gameplay-encoder/src/CameraInstance.cpp
  14. 0 39
      gameplay-encoder/src/CameraInstance.h
  15. 2 2
      gameplay-encoder/src/DAEChannelTarget.cpp
  16. 4 54
      gameplay-encoder/src/DAEOptimizer.cpp
  17. 0 8
      gameplay-encoder/src/DAEOptimizer.h
  18. 211 232
      gameplay-encoder/src/DAESceneEncoder.cpp
  19. 2 4
      gameplay-encoder/src/DAESceneEncoder.h
  20. 60 10
      gameplay-encoder/src/DAEUtil.cpp
  21. 9 1
      gameplay-encoder/src/DAEUtil.h
  22. 43 11
      gameplay-encoder/src/EncoderArguments.cpp
  23. 5 0
      gameplay-encoder/src/EncoderArguments.h
  24. 1336 0
      gameplay-encoder/src/FBXSceneEncoder.cpp
  25. 222 0
      gameplay-encoder/src/FBXSceneEncoder.h
  26. 39 3
      gameplay-encoder/src/FileIO.cpp
  27. 30 8
      gameplay-encoder/src/FileIO.h
  28. 2 2
      gameplay-encoder/src/GPBDecoder.cpp
  29. 18 10
      gameplay-encoder/src/GPBFile.cpp
  30. 1 1
      gameplay-encoder/src/GPBFile.h
  31. 0 55
      gameplay-encoder/src/LightInstance.cpp
  32. 0 42
      gameplay-encoder/src/LightInstance.h
  33. 9 10
      gameplay-encoder/src/Mesh.cpp
  34. 1 1
      gameplay-encoder/src/MeshPart.cpp
  35. 4 3
      gameplay-encoder/src/MeshSkin.cpp
  36. 14 15
      gameplay-encoder/src/Node.cpp
  37. 11 6
      gameplay-encoder/src/Node.h
  38. 2 4
      gameplay-encoder/src/Object.h
  39. 3 3
      gameplay-encoder/src/ReferenceTable.cpp
  40. 10 3
      gameplay-encoder/src/Scene.cpp
  41. 5 0
      gameplay-encoder/src/Scene.h
  42. 20 1
      gameplay-encoder/src/StringUtil.cpp
  43. 4 0
      gameplay-encoder/src/StringUtil.h
  44. 5 5
      gameplay-encoder/src/TTFFontEncoder.cpp
  45. 20 16
      gameplay-encoder/src/Transform.h
  46. 15 61
      gameplay-encoder/src/Vector2.cpp
  47. 108 33
      gameplay-encoder/src/Vector2.h
  48. 77 0
      gameplay-encoder/src/Vector2.inl
  49. 31 72
      gameplay-encoder/src/Vector3.cpp
  50. 117 39
      gameplay-encoder/src/Vector3.h
  51. 82 0
      gameplay-encoder/src/Vector3.inl
  52. 33 65
      gameplay-encoder/src/Vector4.cpp
  53. 118 46
      gameplay-encoder/src/Vector4.h
  54. 89 0
      gameplay-encoder/src/Vector4.inl
  55. 28 26
      gameplay-encoder/src/Vertex.cpp
  56. 6 4
      gameplay-encoder/src/Vertex.h
  57. 16 3
      gameplay-encoder/src/main.cpp
  58. 7 2
      gameplay/gameplay.vcxproj
  59. 12 0
      gameplay/gameplay.vcxproj.filters
  60. 8 0
      gameplay/gameplay.xcodeproj/project.pbxproj
  61. 二進制
      gameplay/res/textures/particle-default.png
  62. 1 1
      gameplay/src/AnimationClip.h
  63. 19 0
      gameplay/src/AnimationController.cpp
  64. 12 0
      gameplay/src/AnimationController.h
  65. 6 0
      gameplay/src/AudioSource.cpp
  66. 8 0
      gameplay/src/AudioSource.h
  67. 8 44
      gameplay/src/Base.h
  68. 12 12
      gameplay/src/BoundingBox.cpp
  69. 2 2
      gameplay/src/BoundingSphere.cpp
  70. 1 1
      gameplay/src/Camera.cpp
  71. 40 1
      gameplay/src/DebugNew.h
  72. 329 212
      gameplay/src/Font.cpp
  73. 30 17
      gameplay/src/Font.h
  74. 0 1
      gameplay/src/Joint.cpp
  75. 8 6
      gameplay/src/Material.cpp
  76. 9 0
      gameplay/src/Material.h
  77. 1 1
      gameplay/src/Matrix.h
  78. 2 2
      gameplay/src/MeshPart.cpp
  79. 1 0
      gameplay/src/Model.cpp
  80. 15 1
      gameplay/src/Node.cpp
  81. 14 0
      gameplay/src/Node.h
  82. 58 14
      gameplay/src/Package.cpp
  83. 40 5
      gameplay/src/Package.h
  84. 42 40
      gameplay/src/ParticleEmitter.cpp
  85. 12 1
      gameplay/src/ParticleEmitter.h
  86. 326 56
      gameplay/src/PhysicsController.cpp
  87. 69 27
      gameplay/src/PhysicsController.h
  88. 1 1
      gameplay/src/PhysicsHingeConstraint.cpp
  89. 192 51
      gameplay/src/PhysicsRigidBody.cpp
  90. 36 13
      gameplay/src/PhysicsRigidBody.h
  91. 2 2
      gameplay/src/PlatformMacOS.mm
  92. 2 4
      gameplay/src/PlatformWin32.cpp
  93. 35 3
      gameplay/src/Properties.cpp
  94. 13 0
      gameplay/src/Properties.h
  95. 5 5
      gameplay/src/Ray.cpp
  96. 8 8
      gameplay/src/Rectangle.cpp
  97. 6 0
      gameplay/src/Scene.cpp
  98. 9 0
      gameplay/src/Scene.h
  99. 936 0
      gameplay/src/SceneLoader.cpp
  100. 100 0
      gameplay/src/SceneLoader.h

+ 12 - 10
.gitignore

@@ -13,6 +13,7 @@
 /Device-Coverage
 /Device-Profile
 /Device-Release
+/gameplay.xcworkspace/xcuserdata
 /gameplay/Debug
 /gameplay/DebugMem
 /gameplay/Release
@@ -23,8 +24,11 @@
 /gameplay/Device-Coverage
 /gameplay/Device-Profile
 /gameplay/Device-Release
+/gameplay/gameplay.xcodeproj/xcuserdata
 /gameplay-encoder/Debug
 /gameplay-encoder/Release
+/gameplay-encoder/gameplay-encoder.xcodeproj/xcuserdata
+/gameplay-internal
 /gameplay-samples/sample00-mesh/Debug
 /gameplay-samples/sample00-mesh/DebugMem
 /gameplay-samples/sample00-mesh/Release
@@ -35,6 +39,8 @@
 /gameplay-samples/sample00-mesh/Device-Coverage
 /gameplay-samples/sample00-mesh/Device-Profile
 /gameplay-samples/sample00-mesh/Device-Release
+/gameplay-samples/sample00-mesh/res/shaders
+/gameplay-samples/sample00-mesh/sample00-mesh.xcodeproj/xcuserdata
 /gameplay-samples/sample01-longboard/Debug
 /gameplay-samples/sample01-longboard/DebugMem
 /gameplay-samples/sample01-longboard/Release
@@ -45,6 +51,8 @@
 /gameplay-samples/sample01-longboard/Device-Coverage
 /gameplay-samples/sample01-longboard/Device-Profile
 /gameplay-samples/sample01-longboard/Device-Release
+/gameplay-samples/sample01-longboard/res/shaders
+/gameplay-samples/sample01-longboard/sample01-longboard.xcodeproj/xcuserdata
 /gameplay-samples/sample02-spaceship/Debug
 /gameplay-samples/sample02-spaceship/DebugMem
 /gameplay-samples/sample02-spaceship/Release
@@ -55,6 +63,8 @@
 /gameplay-samples/sample02-spaceship/Device-Coverage
 /gameplay-samples/sample02-spaceship/Device-Profile
 /gameplay-samples/sample02-spaceship/Device-Release
+/gameplay-samples/sample02-spaceship/res/shaders
+/gameplay-samples/sample02-spaceship/sample02-spaceship.xcodeproj/xcuserdata
 /gameplay-samples/sample03-character/Debug
 /gameplay-samples/sample03-character/DebugMem
 /gameplay-samples/sample03-character/Release
@@ -65,13 +75,5 @@
 /gameplay-samples/sample03-character/Device-Coverage
 /gameplay-samples/sample03-character/Device-Profile
 /gameplay-samples/sample03-character/Device-Release
-/gameplay-samples/sample04-sandbox/Debug
-/gameplay-samples/sample04-sandbox/DebugMem
-/gameplay-samples/sample04-sandbox/Release
-/gameplay-samples/sample04-sandbox/Simulator
-/gameplay-samples/sample04-sandbox/Simulator-Coverage
-/gameplay-samples/sample04-sandbox/Simulator-Profile
-/gameplay-samples/sample04-sandbox/Device-Debug
-/gameplay-samples/sample04-sandbox/Device-Coverage
-/gameplay-samples/sample04-sandbox/Device-Profile
-/gameplay-samples/sample04-sandbox/Device-Release
+/gameplay-samples/sample03-character/res/shaders
+/gameplay-samples/sample03-character/sample03-character.xcodeproj/xcuserdata

+ 57 - 0
gameplay-encoder/README.md

@@ -0,0 +1,57 @@
+## GamePlay Encoder
+GamePlay Encoder is a command-line tool for encoding/packaging games assets like fonts and 3D scene files
+into a binary package file format for the GamePlay runtime framework. 
+
+## TrueType Font Support
+TrueType Fonts conversion is enabled/built-in by default into gameplay-encoder via freetype 2 library.
+
+## COLLADA Scene Support
+COLLADA is enabled/built-in by default into gameplay-encoder via COLLADA-DOM library.
+Most major 3D DCC tools support the export of COLLADA 1.4.
+We also recommend you download and use OpenCOLADA (http://opencollada.org/)
+for Autodesk Maya and 3DS Max.
+
+## FBX Scene Support
+FBX support can easily be enabled in gameplay-encoder but requires an 
+additional installation of Autodesk FBX SDK. (http://www.autodesk.com/fbx).
+You must then rebuild gameplay-encoder with the follow platform/tooling instructions:
+
+### Building FBX Support on Windows 7 using Visual Studio 2010
+- Download and install the FBX SDK for Window VS2010. (http://www.autodesk.com/fbx)
+- Edit the project properties of "gameplay-encoder"
+- Add Preprocessor Definition "USE_FBX" (C++/Preprocessor)
+- Add the FBX SDK include directory to Additional Include Directories (C++/General)
+  * Example: C:/Program Files/Autodesk/FBX/FbxSdk/2012.2/include
+- Add the FBX lib directory to the Additional Library Directories (Linker/General)
+  * Example: C:/Program Files/Autodesk/FBX/FbxSdk/2012.2/lib/vs2010/x86
+- Add "fbxsdk-2012.2-mdd.lib" and "wininet.lib" to the Additional Dependencies (Linker/Input)
+  * Example: fbxsdk-2012.2-mdd.lib;wininet.lib
+- Add a post build event to copy the DLL (Build Events/Post-Build Event)
+  * Example: copy /Y "C:/Program Files/Autodesk/FBX/FbxSdk/2012.2/lib/vs2010/x86/fbxsdk-2012.2d.dll" "$(TargetDir)"
+- Build gameplay-encoder
+
+### Building FBX Support on Mac OS X using XCode 4
+- Download and install the FBX SDK for Mac OS X (http://www.autodesk.com/fbx)
+- Edit the project properties of target "gameplay-encoder".
+- Add Preprocessor Macro "USE_FBX" to both Debug/Release sections. (Build Settings)
+- Add the FBX include directory to Header Search Paths: (Build Settings)
+  * Example: /Applications/Autodesk/FBXSDK20122/include
+- Add the FBX library and dependency Library/Frameworks: (Build Phases -> Link Binary with Libraries)
+  * Example: /Applications/Autodesk/FBXSDK20122/lib/gcc4/ub/libfbxsdk-2012.2-static.a  (Add Other)
+  * Example: libiconv.dylib, Cocoa.framework, SystemConfiguration.framework
+- Build gameplay-encoder
+
+## Binary Format
+The GamePlay binary package format is well defined in the gameplay-encoder/gameplay-binary.txt file.
+
+## Binary Loading
+GamePlay binary package files can easily be loaded using the gameplay/Package.h which is part
+of the GamePlay runtime framework.
+
+## Disclaimer
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
+OTHER DEALINGS IN THE SOFTWARE.

+ 9 - 5
gameplay-encoder/gameplay-encoder.vcxproj

@@ -17,13 +17,13 @@
     <ClCompile Include="src\Base.cpp" />
     <ClCompile Include="src\BoundingVolume.cpp" />
     <ClCompile Include="src\Camera.cpp" />
-    <ClCompile Include="src\CameraInstance.cpp" />
     <ClCompile Include="src\EncoderArguments.cpp" />
     <ClCompile Include="src\DAEChannelTarget.cpp" />
     <ClCompile Include="src\DAEOptimizer.cpp" />
     <ClCompile Include="src\DAESceneEncoder.cpp" />
     <ClCompile Include="src\DAEUtil.cpp" />
     <ClCompile Include="src\Effect.cpp" />
+    <ClCompile Include="src\FBXSceneEncoder.cpp" />
     <ClCompile Include="src\FileIO.cpp" />
     <ClCompile Include="src\Font.cpp" />
     <ClCompile Include="src\GPBFile.cpp" />
@@ -31,7 +31,6 @@
     <ClCompile Include="src\GPBDecoder.cpp" />
     <ClCompile Include="src\Animations.cpp" />
     <ClCompile Include="src\Light.cpp" />
-    <ClCompile Include="src\LightInstance.cpp" />
     <ClCompile Include="src\main.cpp" />
     <ClCompile Include="src\Material.cpp" />
     <ClCompile Include="src\MaterialParameter.cpp" />
@@ -62,13 +61,13 @@
     <ClInclude Include="src\Base.h" />
     <ClInclude Include="src\BoundingVolume.h" />
     <ClInclude Include="src\Camera.h" />
-    <ClInclude Include="src\CameraInstance.h" />
     <ClInclude Include="src\EncoderArguments.h" />
     <ClInclude Include="src\DAEChannelTarget.h" />
     <ClInclude Include="src\DAEOptimizer.h" />
     <ClInclude Include="src\DAESceneEncoder.h" />
     <ClInclude Include="src\DAEUtil.h" />
     <ClInclude Include="src\Effect.h" />
+    <ClInclude Include="src\FBXSceneEncoder.h" />
     <ClInclude Include="src\FileIO.h" />
     <ClInclude Include="src\Font.h" />
     <ClInclude Include="src\GPBFile.h" />
@@ -76,7 +75,6 @@
     <ClInclude Include="src\GPBDecoder.h" />
     <ClInclude Include="src\Animations.h" />
     <ClInclude Include="src\Light.h" />
-    <ClInclude Include="src\LightInstance.h" />
     <ClInclude Include="src\Material.h" />
     <ClInclude Include="src\MaterialParameter.h" />
     <ClInclude Include="src\Matrix.h" />
@@ -99,6 +97,11 @@
     <ClInclude Include="src\Vertex.h" />
     <ClInclude Include="src\VertexElement.h" />
   </ItemGroup>
+  <ItemGroup>
+    <None Include="src\Vector2.inl" />
+    <None Include="src\Vector3.inl" />
+    <None Include="src\Vector4.inl" />
+  </ItemGroup>
   <PropertyGroup Label="Globals">
     <ProjectGuid>{9D69B743-4872-4DD1-8E30-0087C64298D7}</ProjectGuid>
     <Keyword>Win32Proj</Keyword>
@@ -176,7 +179,8 @@
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalDependencies>freetype245.lib;libcollada14dom22-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>;../external-deps/freetype2/lib/win32;../external-deps/collada-dom/lib/win32</AdditionalLibraryDirectories>
-      <IgnoreSpecificDefaultLibraries>MSVCRT</IgnoreSpecificDefaultLibraries>
+      <IgnoreSpecificDefaultLibraries>
+      </IgnoreSpecificDefaultLibraries>
     </Link>
     <PostBuildEvent>
       <Command>copy /Y "$(ProjectDir)..\external-deps\collada-dom\lib\win32\*.dll" "$(TargetDir)"</Command>

+ 192 - 149
gameplay-encoder/gameplay-encoder.vcxproj.filters

@@ -1,229 +1,272 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <ClCompile Include="src\DAESceneEncoder.cpp" />
-    <ClCompile Include="src\main.cpp" />
-    <ClCompile Include="src\ReferenceTable.cpp" />
-    <ClCompile Include="src\TTFFontEncoder.cpp" />
+    <ClCompile Include="src\Animation.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\AnimationChannel.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Animations.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Base.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\BoundingVolume.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
     <ClCompile Include="src\Camera.cpp">
-      <Filter>Objects\Camera</Filter>
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="..\gameplay\src\Curve.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\DAEChannelTarget.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\CameraInstance.cpp">
-      <Filter>Objects\Camera</Filter>
+    <ClCompile Include="src\DAEOptimizer.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\DAESceneEncoder.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\DAEUtil.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Effect.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\EncoderArguments.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\FBXSceneEncoder.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\FileIO.cpp">
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\Font.cpp">
-      <Filter>Objects\Font</Filter>
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\Glyph.cpp">
-      <Filter>Objects\Font</Filter>
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\GPBDecoder.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\GPBFile.cpp">
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\Light.cpp">
-      <Filter>Objects\Light</Filter>
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\LightInstance.cpp">
-      <Filter>Objects\Light</Filter>
+    <ClCompile Include="src\main.cpp">
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\Material.cpp">
-      <Filter>Objects\Material</Filter>
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\MaterialParameter.cpp">
-      <Filter>Objects\Material</Filter>
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Matrix.cpp">
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\Mesh.cpp">
-      <Filter>Objects\Mesh</Filter>
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\MeshPart.cpp">
-      <Filter>Objects\Mesh</Filter>
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\MeshSkin.cpp">
-      <Filter>Objects\Mesh</Filter>
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Node.cpp">
-      <Filter>Objects\Node</Filter>
+    <ClCompile Include="src\Model.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Reference.cpp">
-      <Filter>Objects</Filter>
+    <ClCompile Include="src\Node.cpp">
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\Object.cpp">
-      <Filter>Objects</Filter>
-    </ClCompile>
-    <ClCompile Include="src\Model.cpp">
-      <Filter>Objects\Mesh</Filter>
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Scene.cpp">
-      <Filter>Objects</Filter>
+    <ClCompile Include="src\Quaternion.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\GPBDecoder.cpp" />
-    <ClCompile Include="src\Animation.cpp">
-      <Filter>Objects\Animation</Filter>
+    <ClCompile Include="src\Reference.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\AnimationChannel.cpp">
-      <Filter>Objects\Animation</Filter>
+    <ClCompile Include="src\ReferenceTable.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\GPBFile.cpp" />
-    <ClCompile Include="src\DAEChannelTarget.cpp" />
-    <ClCompile Include="src\FileIO.cpp" />
-    <ClCompile Include="src\StringUtil.cpp" />
-    <ClCompile Include="src\Effect.cpp">
-      <Filter>Objects\Material</Filter>
+    <ClCompile Include="src\Scene.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Vertex.cpp" />
-    <ClCompile Include="src\VertexElement.cpp" />
-    <ClCompile Include="src\Quaternion.cpp">
-      <Filter>Math</Filter>
+    <ClCompile Include="src\StringUtil.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Vector4.cpp">
-      <Filter>Math</Filter>
+    <ClCompile Include="src\Transform.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Vector3.cpp">
-      <Filter>Math</Filter>
+    <ClCompile Include="src\TTFFontEncoder.cpp">
+      <Filter>src</Filter>
     </ClCompile>
     <ClCompile Include="src\Vector2.cpp">
-      <Filter>Math</Filter>
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Transform.cpp">
-      <Filter>Math</Filter>
+    <ClCompile Include="src\Vector3.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Matrix.cpp">
-      <Filter>Math</Filter>
+    <ClCompile Include="src\Vector4.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\DAEOptimizer.cpp" />
-    <ClCompile Include="src\DAEUtil.cpp" />
-    <ClCompile Include="src\EncoderArguments.cpp" />
-    <ClCompile Include="src\Animations.cpp">
-      <Filter>Objects\Animation</Filter>
+    <ClCompile Include="src\Vertex.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="..\gameplay\src\Curve.cpp" />
-    <ClCompile Include="src\BoundingVolume.cpp">
-      <Filter>Math</Filter>
+    <ClCompile Include="src\VertexElement.cpp">
+      <Filter>src</Filter>
     </ClCompile>
-    <ClCompile Include="src\Base.cpp" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="src\DAESceneEncoder.h" />
-    <ClInclude Include="src\ReferenceTable.h" />
-    <ClInclude Include="src\TTFFontEncoder.h" />
-    <ClInclude Include="src\CameraInstance.h">
-      <Filter>Objects\Camera</Filter>
+    <ClInclude Include="src\VertexElement.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Animation.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\AnimationChannel.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Animations.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Base.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\BoundingVolume.h">
+      <Filter>src</Filter>
     </ClInclude>
     <ClInclude Include="src\Camera.h">
-      <Filter>Objects\Camera</Filter>
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="..\gameplay\src\Curve.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\DAEChannelTarget.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\DAEOptimizer.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\DAESceneEncoder.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\DAEUtil.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Effect.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\EncoderArguments.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\FBXSceneEncoder.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\FileIO.h">
+      <Filter>src</Filter>
     </ClInclude>
     <ClInclude Include="src\Font.h">
-      <Filter>Objects\Font</Filter>
+      <Filter>src</Filter>
     </ClInclude>
     <ClInclude Include="src\Glyph.h">
-      <Filter>Objects\Font</Filter>
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\LightInstance.h">
-      <Filter>Objects\Light</Filter>
+    <ClInclude Include="src\GPBDecoder.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Light.h">
-      <Filter>Objects\Light</Filter>
+    <ClInclude Include="src\GPBFile.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\MaterialParameter.h">
-      <Filter>Objects\Material</Filter>
+    <ClInclude Include="src\Light.h">
+      <Filter>src</Filter>
     </ClInclude>
     <ClInclude Include="src\Material.h">
-      <Filter>Objects\Material</Filter>
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\MeshSkin.h">
-      <Filter>Objects\Mesh</Filter>
+    <ClInclude Include="src\MaterialParameter.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Matrix.h">
+      <Filter>src</Filter>
     </ClInclude>
     <ClInclude Include="src\Mesh.h">
-      <Filter>Objects\Mesh</Filter>
+      <Filter>src</Filter>
     </ClInclude>
     <ClInclude Include="src\MeshPart.h">
-      <Filter>Objects\Mesh</Filter>
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Node.h">
-      <Filter>Objects\Node</Filter>
+    <ClInclude Include="src\MeshSkin.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Reference.h">
-      <Filter>Objects</Filter>
+    <ClInclude Include="src\Model.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Object.h">
-      <Filter>Objects</Filter>
+    <ClInclude Include="src\Node.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Model.h">
-      <Filter>Objects\Mesh</Filter>
+    <ClInclude Include="src\Object.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Scene.h">
-      <Filter>Objects</Filter>
+    <ClInclude Include="src\Quaternion.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\GPBDecoder.h" />
-    <ClInclude Include="src\AnimationChannel.h">
-      <Filter>Objects\Animation</Filter>
+    <ClInclude Include="src\Reference.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Animation.h">
-      <Filter>Objects\Animation</Filter>
+    <ClInclude Include="src\ReferenceTable.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\GPBFile.h" />
-    <ClInclude Include="src\DAEChannelTarget.h" />
-    <ClInclude Include="src\FileIO.h" />
-    <ClInclude Include="src\StringUtil.h" />
-    <ClInclude Include="src\Effect.h">
-      <Filter>Objects\Material</Filter>
+    <ClInclude Include="src\Scene.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Vertex.h" />
-    <ClInclude Include="src\VertexElement.h" />
-    <ClInclude Include="src\Quaternion.h">
-      <Filter>Math</Filter>
+    <ClInclude Include="src\StringUtil.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Vector4.h">
-      <Filter>Math</Filter>
+    <ClInclude Include="src\Transform.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Vector3.h">
-      <Filter>Math</Filter>
+    <ClInclude Include="src\TTFFontEncoder.h">
+      <Filter>src</Filter>
     </ClInclude>
     <ClInclude Include="src\Vector2.h">
-      <Filter>Math</Filter>
-    </ClInclude>
-    <ClInclude Include="src\Transform.h">
-      <Filter>Math</Filter>
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Matrix.h">
-      <Filter>Math</Filter>
+    <ClInclude Include="src\Vector3.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\DAEOptimizer.h" />
-    <ClInclude Include="src\DAEUtil.h" />
-    <ClInclude Include="src\EncoderArguments.h" />
-    <ClInclude Include="src\Animations.h">
-      <Filter>Objects\Animation</Filter>
+    <ClInclude Include="src\Vector4.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="..\gameplay\src\Curve.h" />
-    <ClInclude Include="src\BoundingVolume.h">
-      <Filter>Math</Filter>
+    <ClInclude Include="src\Vertex.h">
+      <Filter>src</Filter>
     </ClInclude>
-    <ClInclude Include="src\Base.h" />
   </ItemGroup>
   <ItemGroup>
-    <Filter Include="Objects">
-      <UniqueIdentifier>{3be0a9f1-9de7-42e9-b264-89400ccf3236}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="Objects\Camera">
-      <UniqueIdentifier>{d2b8cf21-20ee-4838-8bb6-5adab779dc10}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="Objects\Font">
-      <UniqueIdentifier>{1c4dbad5-5351-4fd0-b498-745f7afcf866}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="Objects\Light">
-      <UniqueIdentifier>{c41a1929-d921-4d2a-9713-12831c0e3ce1}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="Objects\Material">
-      <UniqueIdentifier>{1d8618d6-dbed-40c0-bb22-43350b195ee1}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="Objects\Mesh">
-      <UniqueIdentifier>{80e09f25-6954-4415-b712-62a4a64174c3}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="Objects\Node">
-      <UniqueIdentifier>{a74ab546-c248-4bf8-b212-b590514e23e0}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="Objects\Animation">
-      <UniqueIdentifier>{dc9b3d2e-25ae-4613-83ce-844abf4f9bf5}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="Math">
-      <UniqueIdentifier>{4368ed2b-7fce-423a-a9cb-76a0fdc42858}</UniqueIdentifier>
+    <None Include="src\Vector2.inl">
+      <Filter>src</Filter>
+    </None>
+    <None Include="src\Vector3.inl">
+      <Filter>src</Filter>
+    </None>
+    <None Include="src\Vector4.inl">
+      <Filter>src</Filter>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Filter Include="src">
+      <UniqueIdentifier>{6393521c-00fb-48f6-b480-87f8d978a74f}</UniqueIdentifier>
     </Filter>
   </ItemGroup>
 </Project>

+ 1 - 2
gameplay-encoder/gameplay-encoder.vcxproj.user

@@ -1,8 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <LocalDebuggerCommandArguments>
-    </LocalDebuggerCommandArguments>
+    <LocalDebuggerCommandArguments>-groupAnimations group1 movements C:\Git\gaming\GamePlay\gameplay-encoder\res\seymour.dae</LocalDebuggerCommandArguments>
     <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
   </PropertyGroup>
 </Project>

+ 22 - 0
gameplay-encoder/gameplay-encoder.xcodeproj/project.pbxproj

@@ -8,6 +8,7 @@
 
 /* Begin PBXBuildFile section */
 		42475D7C14720ECE00610A6A /* libdom.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 42475D7B14720ECE00610A6A /* libdom.a */; };
+		42783423148D6F7500A6E27F /* FBXSceneEncoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4278341E148D6F7500A6E27F /* FBXSceneEncoder.cpp */; };
 		4283905914896E6C00E2B2F5 /* BoundingVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4283905714896E6C00E2B2F5 /* BoundingVolume.cpp */; };
 		4283906314896F1600E2B2F5 /* Curve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4283906114896F1600E2B2F5 /* Curve.cpp */; };
 		42C8EE0A14724CD700E43619 /* Animation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 42C8EDB714724CD700E43619 /* Animation.cpp */; };
@@ -75,6 +76,11 @@
 /* Begin PBXFileReference section */
 		42475CE6147208A000610A6A /* gameplay-encoder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "gameplay-encoder"; sourceTree = BUILT_PRODUCTS_DIR; };
 		42475D7B14720ECE00610A6A /* libdom.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libdom.a; path = "../external-deps/collada-dom/lib/macos/libdom.a"; sourceTree = "<group>"; };
+		4278341E148D6F7500A6E27F /* FBXSceneEncoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FBXSceneEncoder.cpp; path = src/FBXSceneEncoder.cpp; sourceTree = SOURCE_ROOT; };
+		4278341F148D6F7500A6E27F /* FBXSceneEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FBXSceneEncoder.h; path = src/FBXSceneEncoder.h; sourceTree = SOURCE_ROOT; };
+		42783420148D6F7500A6E27F /* Vector2.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Vector2.inl; path = src/Vector2.inl; sourceTree = SOURCE_ROOT; };
+		42783421148D6F7500A6E27F /* Vector3.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Vector3.inl; path = src/Vector3.inl; sourceTree = SOURCE_ROOT; };
+		42783422148D6F7500A6E27F /* Vector4.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Vector4.inl; path = src/Vector4.inl; sourceTree = SOURCE_ROOT; };
 		4283905714896E6C00E2B2F5 /* BoundingVolume.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BoundingVolume.cpp; path = src/BoundingVolume.cpp; sourceTree = SOURCE_ROOT; };
 		4283905814896E6C00E2B2F5 /* BoundingVolume.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BoundingVolume.h; path = src/BoundingVolume.h; sourceTree = SOURCE_ROOT; };
 		4283906114896F1600E2B2F5 /* Curve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Curve.cpp; path = ../gameplay/src/Curve.cpp; sourceTree = "<group>"; };
@@ -235,6 +241,8 @@
 				42C8EDCC14724CD700E43619 /* Effect.h */,
 				42C8EDCD14724CD700E43619 /* EncoderArguments.cpp */,
 				42C8EDCE14724CD700E43619 /* EncoderArguments.h */,
+				4278341E148D6F7500A6E27F /* FBXSceneEncoder.cpp */,
+				4278341F148D6F7500A6E27F /* FBXSceneEncoder.h */,
 				42C8EDCF14724CD700E43619 /* FileIO.cpp */,
 				42C8EDD014724CD700E43619 /* FileIO.h */,
 				42C8EDD114724CD700E43619 /* Font.cpp */,
@@ -286,10 +294,13 @@
 				42C8EDFF14724CD700E43619 /* TTFFontEncoder.h */,
 				42C8EE0014724CD700E43619 /* Vector2.cpp */,
 				42C8EE0114724CD700E43619 /* Vector2.h */,
+				42783420148D6F7500A6E27F /* Vector2.inl */,
 				42C8EE0214724CD700E43619 /* Vector3.cpp */,
 				42C8EE0314724CD700E43619 /* Vector3.h */,
+				42783421148D6F7500A6E27F /* Vector3.inl */,
 				42C8EE0414724CD700E43619 /* Vector4.cpp */,
 				42C8EE0514724CD700E43619 /* Vector4.h */,
+				42783422148D6F7500A6E27F /* Vector4.inl */,
 				42C8EE0614724CD700E43619 /* Vertex.cpp */,
 				42C8EE0714724CD700E43619 /* Vertex.h */,
 				42C8EE0814724CD700E43619 /* VertexElement.cpp */,
@@ -416,6 +427,7 @@
 				42C8EE3314724CD700E43619 /* VertexElement.cpp in Sources */,
 				4283905914896E6C00E2B2F5 /* BoundingVolume.cpp in Sources */,
 				4283906314896F1600E2B2F5 /* Curve.cpp in Sources */,
+				42783423148D6F7500A6E27F /* FBXSceneEncoder.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -455,6 +467,7 @@
 				OTHER_TEST_FLAGS = "";
 				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
 				SDKROOT = macosx;
+				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4";
 				WARNING_CFLAGS = "";
 			};
 			name = Debug;
@@ -488,6 +501,7 @@
 				OTHER_TEST_FLAGS = "";
 				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
 				SDKROOT = macosx;
+				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4";
 				WARNING_CFLAGS = "";
 			};
 			name = Release;
@@ -501,7 +515,9 @@
 				GCC_C_LANGUAGE_STANDARD = "compiler-default";
 				GCC_ENABLE_CPP_EXCEPTIONS = NO;
 				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
+				GCC_WARN_UNUSED_VARIABLE = NO;
 				HEADER_SEARCH_PATHS = (
 					"../external-deps/freetype2/include",
 					"../external-deps/collada-dom/include",
@@ -513,9 +529,11 @@
 					"\"$(SRCROOT)/../external-deps/collada-dom/lib/macos\"",
 					"\"$(SRCROOT)/../external-deps/pcre/lib/macos\"",
 					"\"$(SRCROOT)/../../../Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug\"",
+					"\"$(SYSTEM_APPS_DIR)/Autodesk/FBXSDK20122/lib/gcc4/ub\"",
 				);
 				MACH_O_TYPE = mh_execute;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype2/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4 /Applications/Autodesk/FBXSDK20122/include";
 			};
 			name = Debug;
 		};
@@ -528,7 +546,9 @@
 				GCC_C_LANGUAGE_STANDARD = "compiler-default";
 				GCC_ENABLE_CPP_EXCEPTIONS = NO;
 				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
+				GCC_WARN_UNUSED_VARIABLE = NO;
 				HEADER_SEARCH_PATHS = (
 					"../external-deps/freetype2/include",
 					"../external-deps/collada-dom/include",
@@ -540,9 +560,11 @@
 					"\"$(SRCROOT)/../external-deps/collada-dom/lib/macos\"",
 					"\"$(SRCROOT)/../external-deps/pcre/lib/macos\"",
 					"\"$(SRCROOT)/../../../Library/Developer/Xcode/DerivedData/gameplay-exiunaubxxjndaapmcqkaoeboiob/Build/Products/Debug\"",
+					"\"$(SYSTEM_APPS_DIR)/Autodesk/FBXSDK20122/lib/gcc4/ub\"",
 				);
 				MACH_O_TYPE = mh_execute;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "../external-deps/freetype2/include ../external-deps/collada-dom/include ../external-deps/collada-dom/include/1.4 /Applications/Autodesk/FBXSDK20122/include";
 			};
 			name = Release;
 		};

+ 2 - 2
gameplay-encoder/src/Animation.cpp

@@ -27,7 +27,7 @@ void Animation::writeBinary(FILE* file)
     // Animation writes its ID because it is not listed in the ref table.
     write(getId(), file);
     write(_channels.size(), file);
-    for (std::vector<AnimationChannel*>::iterator i = _channels.begin(); i != _channels.end(); i++)
+    for (std::vector<AnimationChannel*>::iterator i = _channels.begin(); i != _channels.end(); ++i)
     {
         (*i)->writeBinary(file);
     }
@@ -38,7 +38,7 @@ void Animation::writeText(FILE* file)
     fprintElementStart(file);
     if (_channels.size() > 0 )
     {
-        for (std::vector<AnimationChannel*>::iterator i = _channels.begin(); i != _channels.end(); i++)
+        for (std::vector<AnimationChannel*>::iterator i = _channels.begin(); i != _channels.end(); ++i)
         {
             (*i)->writeText(file);
         }

+ 126 - 2
gameplay-encoder/src/AnimationChannel.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "AnimationChannel.h"
+#include "Transform.h"
 
 namespace gameplay
 {
@@ -28,7 +29,7 @@ void AnimationChannel::writeBinary(FILE* file)
     write(_targetId, file);
     write(_targetAttrib, file);
     write(_keytimes.size(), file);
-    for (std::vector<float>::const_iterator i = _keytimes.begin(); i != _keytimes.end(); i++)
+    for (std::vector<float>::const_iterator i = _keytimes.begin(); i != _keytimes.end(); ++i)
     {
         write((unsigned long)*i, file);
     }
@@ -36,7 +37,6 @@ void AnimationChannel::writeBinary(FILE* file)
     write(_tangentsIn, file);
     write(_tangentsOut, file);
     write(_interpolations, file);
-
 }
 
 void AnimationChannel::writeText(FILE* file)
@@ -52,6 +52,12 @@ void AnimationChannel::writeText(FILE* file)
     fprintElementEnd(file);
 }
 
+void AnimationChannel::setInterpolation(unsigned int interpolation)
+{
+    _interpolations.clear();
+    _interpolations.push_back(interpolation);
+}
+
 const std::string& AnimationChannel::getTargetId() const
 {
     return _targetId;
@@ -122,6 +128,103 @@ void AnimationChannel::setInterpolations(const std::vector<unsigned int>& values
     _interpolations = values;
 }
 
+void AnimationChannel::removeDuplicates()
+{
+    if (_targetAttrib == Transform::ANIMATE_SCALE_ROTATE_TRANSLATE)
+    {
+        size_t prevIndex = 0;
+
+        std::vector<float>::iterator prevStart = _keyValues.begin();
+        std::vector<float>::iterator prevEnd   = _keyValues.begin() + 9;
+        
+        size_t i = 1;
+        for (i = 1; i < _keytimes.size(); ++i)
+        {
+            std::vector<float>::iterator start = _keyValues.begin() + i * 10;
+            std::vector<float>::iterator end = _keyValues.begin() + (i * 10 + 9);
+
+            if (!equal(prevStart, prevEnd, start))
+            {
+                if (i - prevIndex > 2)
+                {
+                    deleteRange(prevIndex+1, i);
+                    i = prevIndex;
+                    prevStart = _keyValues.begin() + i * 10;
+                    prevEnd = _keyValues.begin() + (i * 10 + 9);
+                }
+                else
+                {
+                    prevStart = start;
+                    prevEnd = end;
+                    prevIndex = i;
+                }
+            }
+        }
+        if (i - 1 - prevIndex >= 2)
+        {
+            deleteRange(prevIndex+1, i);
+        }
+    }
+}
+
+void AnimationChannel::convertToQuaternion()
+{
+    if (_targetAttrib == Transform::ANIMATE_ROTATE_X ||
+        _targetAttrib == Transform::ANIMATE_ROTATE_Y ||
+        _targetAttrib == Transform::ANIMATE_ROTATE_Z)
+    {
+        std::vector<float> newKeyValues;
+        newKeyValues.resize(_keyValues.size() * 4);
+        const size_t count = _keyValues.size();
+
+        float x = _targetAttrib == Transform::ANIMATE_ROTATE_X ? 1.0f : 0.0f;
+        float y = _targetAttrib == Transform::ANIMATE_ROTATE_Y ? 1.0f : 0.0f;
+        float z = _targetAttrib == Transform::ANIMATE_ROTATE_Z ? 1.0f : 0.0f;
+        for (size_t i = 0; i < count; ++i)
+        {
+            size_t j = i << 2;
+            newKeyValues[j] = x;
+            newKeyValues[j+1] = y;
+            newKeyValues[j+2] = z;
+            newKeyValues[j+3] = _keyValues[i];
+        }
+        setKeyValues(newKeyValues);
+        setTargetAttribute(Transform::ANIMATE_ROTATE);
+    }
+}
+
+void AnimationChannel::convertToTransform()
+{
+    if (_targetAttrib == Transform::ANIMATE_ROTATE_X ||
+        _targetAttrib == Transform::ANIMATE_ROTATE_Y ||
+        _targetAttrib == Transform::ANIMATE_ROTATE_Z)
+    {
+        std::vector<float> newKeyValues;
+        newKeyValues.resize(_keyValues.size() * 10);
+        const size_t count = _keyValues.size();
+
+        float x = _targetAttrib == Transform::ANIMATE_ROTATE_X ? 1.0f : 0.0f;
+        float y = _targetAttrib == Transform::ANIMATE_ROTATE_Y ? 1.0f : 0.0f;
+        float z = _targetAttrib == Transform::ANIMATE_ROTATE_Z ? 1.0f : 0.0f;
+        for (size_t i = 0; i < count; ++i)
+        {
+            size_t j = i << 2;
+            newKeyValues[j+0] = 1.0f;
+            newKeyValues[j+1] = 1.0f;
+            newKeyValues[j+2] = 1.0f;
+            newKeyValues[j+3] = x;
+            newKeyValues[j+4] = y;
+            newKeyValues[j+5] = z;
+            newKeyValues[j+6] = _keyValues[i];
+            newKeyValues[j+7] = 0.0f;
+            newKeyValues[j+8] = 0.0f;
+            newKeyValues[j+9] = 0.0f;
+        }
+        setKeyValues(newKeyValues);
+        setTargetAttribute(Transform::ANIMATE_SCALE_ROTATE_TRANSLATE);
+    }
+}
+
 unsigned int AnimationChannel::getInterpolationType(const char* str)
 {
     unsigned int value = 0;
@@ -167,4 +270,25 @@ unsigned int AnimationChannel::getInterpolationType(const char* str)
     return value;
 }
 
+void AnimationChannel::deleteRange(size_t begin, size_t end)
+{
+    // delete range
+    printf("delete %lu to %lu\n", begin, end - 1);
+
+    std::vector<float>::iterator a = _keyValues.begin() + begin * 10;
+    std::vector<float>::iterator b = _keyValues.begin() + end * 10;
+    _keyValues.erase(a, b);
+
+    a = _keytimes.begin() + begin;
+    b = _keytimes.begin() + end;
+    _keytimes.erase(a, b);
+
+    if (_interpolations.size() > 1)
+    {
+        std::vector<unsigned int>::iterator a = _interpolations.begin() + begin;
+        std::vector<unsigned int>::iterator b = _interpolations.begin() + end * 10;
+        _interpolations.erase(a, b);
+    }
+}
+
 }

+ 16 - 0
gameplay-encoder/src/AnimationChannel.h

@@ -36,6 +36,14 @@ public:
     virtual void writeText(FILE* file);
 
     const std::string& getTargetId() const;
+
+    /**
+     * Sets the interpolation type of the entire animation channel.
+     * 
+     * @param interpolation The interpolation type from InterpolationTypes enum.
+     */
+    void setInterpolation(unsigned int interpolation);
+
     void setTargetId(const std::string& str);
     void setTargetAttribute(unsigned int attrib);
 
@@ -52,6 +60,11 @@ public:
     const std::vector<float>& getTangentsOut() const;
     const std::vector<unsigned int>& getInterpolationTypes() const;
 
+    void removeDuplicates();
+
+    void convertToQuaternion();
+    void convertToTransform();
+
     /**
      * Returns the interpolation type value for the given string or zero if not valid.
      * Example: "LINEAR" returns AnimationChannel::LINEAR
@@ -62,6 +75,9 @@ public:
      */
     static unsigned int getInterpolationType(const char* str);
 
+private:
+
+    void deleteRange(size_t begin, size_t end);
 private:
 
     std::string _targetId;

+ 2 - 2
gameplay-encoder/src/Animations.cpp

@@ -28,7 +28,7 @@ void Animations::writeBinary(FILE* file)
 {
     Object::writeBinary(file);
     write(_animations.size(), file);
-    for (std::vector<Animation*>::iterator i = _animations.begin(); i != _animations.end(); i++)
+    for (std::vector<Animation*>::iterator i = _animations.begin(); i != _animations.end(); ++i)
     {
         (*i)->writeBinary(file);
     }
@@ -39,7 +39,7 @@ void Animations::writeText(FILE* file)
     fprintElementStart(file);
     if (_animations.size() > 0 )
     {
-        for (std::vector<Animation*>::iterator i = _animations.begin(); i != _animations.end(); i++)
+        for (std::vector<Animation*>::iterator i = _animations.begin(); i != _animations.end(); ++i)
         {
             (*i)->writeText(file);
         }

+ 1 - 21
gameplay-encoder/src/Base.cpp

@@ -5,30 +5,10 @@ namespace gameplay
 
 void fillArray(float values[], float value, size_t length)
 {
-    for (size_t i = 0; i < length; i++)
+    for (size_t i = 0; i < length; ++i)
     {
         values[i] = value;
     }
 }
 
-void setIdentityMatrix(float values[])
-{
-    values[0] = 1.0f;
-    values[1] = 0.0f;
-    values[2] = 0.0f;
-    values[3] = 0.0f;
-    values[4] = 0.0f;
-    values[5] = 1.0f;
-    values[6] = 0.0f;
-    values[7] = 0.0f;
-    values[8] = 0.0f;
-    values[9] = 0.0f;
-    values[10] = 1.0f;
-    values[11] = 0.0f;
-    values[12] = 0.0f;
-    values[13] = 0.0f;
-    values[14] = 0.0f;
-    values[15] = 1.0f;
-}
-
 }

+ 0 - 1
gameplay-encoder/src/Base.h

@@ -72,7 +72,6 @@ enum VertexUsage
 };
 
 void fillArray(float values[], float value, size_t length);
-void setIdentityMatrix(float values[]);
 
 #define ISZERO(x) (fabs(x) < 0.000001f)
 

+ 0 - 54
gameplay-encoder/src/CameraInstance.cpp

@@ -1,54 +0,0 @@
-#include "Base.h"
-#include "CameraInstance.h"
-
-namespace gameplay
-{
-
-CameraInstance::CameraInstance(void) : _ref(NULL)
-{
-
-}
-
-CameraInstance::~CameraInstance(void)
-{
-}
-
-unsigned int CameraInstance::getTypeId(void) const
-{
-    return CAMERAINSTANCE_ID;
-}
-const char* CameraInstance::getElementName(void) const
-{
-    return "CameraInstance";
-}
-
-void CameraInstance::writeBinary(FILE* file)
-{
-    if (_ref != NULL)
-    {
-        _ref->writeBinary(file);
-    }
-    else
-    {
-        write((unsigned int)0, file);
-    }
-}
-void CameraInstance::writeText(FILE* file)
-{
-    if (_ref != NULL)
-    {
-        _ref->writeText(file);
-    }
-}
-
-Camera* CameraInstance::getCamera()
-{
-    return _ref;
-}
-
-void CameraInstance::setCamera(Camera* camera)
-{
-    _ref = camera;
-}
-
-}

+ 0 - 39
gameplay-encoder/src/CameraInstance.h

@@ -1,39 +0,0 @@
-#ifndef CAMERAINSTANCE_H_
-#define CAMERAINSTANCE_H_
-
-#include "Object.h"
-#include "Camera.h"
-
-namespace gameplay
-{
-
-class CameraInstance : public Object
-{
-public:
-
-    /**
-     * Constructor.
-     */
-    CameraInstance(void);
-
-    /**
-     * Destructor.
-     */
-    virtual ~CameraInstance(void);
-
-    virtual unsigned int getTypeId(void) const;
-    virtual const char* getElementName(void) const;
-    virtual void writeBinary(FILE* file);
-    virtual void writeText(FILE* file);
-
-    Camera* getCamera();
-    void setCamera(Camera* camera);
-
-private:
-
-    Camera* _ref;
-};
-
-}
-
-#endif

+ 2 - 2
gameplay-encoder/src/DAEChannelTarget.cpp

@@ -1,7 +1,7 @@
+
 #include "Base.h"
 #include "DAEChannelTarget.h"
 
-
 namespace gameplay
 {
 
@@ -40,7 +40,7 @@ DAEChannelTarget::DAEChannelTarget(const domChannelRef channelRef) : _channel(ch
                 index = end + 1;
             }
             _attributeIds.push_back(sub);
-        } while(end != std::string::npos);
+        } while (end != std::string::npos);
     }
 
 }

+ 4 - 54
gameplay-encoder/src/DAEOptimizer.cpp

@@ -1,6 +1,5 @@
 #include "Base.h"
 #include "DAEOptimizer.h"
-#include "StringUtil.h"
 
 namespace gameplay
 {
@@ -38,7 +37,7 @@ void DAEOptimizer::combineAnimations(const std::string& nodeId, const std::strin
     // TODO: Make sure that there doesn't already exist an animation with this ID.
 
     // Move each of the channels to this animation
-    for (std::list<domChannelRef>::iterator i = channels.begin(); i != channels.end(); i++)
+    for (std::list<domChannelRef>::iterator i = channels.begin(); i != channels.end(); ++i)
     {
         moveChannelAndSouresToAnimation(*i, animation);
     }
@@ -47,55 +46,6 @@ void DAEOptimizer::combineAnimations(const std::string& nodeId, const std::strin
     deleteEmptyAnimations();
 }
 
-void DAEOptimizer::getAnimationChannels(const domNodeRef& node, std::list<domChannelRef>& channels)
-{
-    assert(node->getId());
-    std::string nodeIdSlash (node->getId());
-    nodeIdSlash.append("/");
-
-    domCOLLADA* root = (domCOLLADA*)node->getDocument()->getDomRoot();
-
-    domLibrary_animations_Array& animationLibrary = root->getLibrary_animations_array();
-    size_t animationLibraryCount = animationLibrary.getCount();
-    for (size_t i = 0; i < animationLibraryCount; i++)
-    {
-        domLibrary_animationsRef& animationsRef = animationLibrary.get(i);
-        domAnimation_Array& animationArray = animationsRef->getAnimation_array();
-        size_t animationCount = animationArray.getCount();
-        for (size_t j = 0; j < animationCount; j++)
-        {
-            domAnimationRef& animationRef = animationArray.get(j);
-            domChannel_Array& channelArray = animationRef->getChannel_array();
-            size_t channelArrayCount = channelArray.getCount();
-            for (size_t k = 0; k < channelArrayCount; k++)
-            {
-                domChannelRef& channel = channelArray.get(k);
-                const char* target = channel->getTarget();
-
-                // TODO: Assumes only one target per channel?
-                if (startsWith(target, nodeIdSlash.c_str()))
-                {
-                    channels.push_back(channel);
-                }
-            }
-        }
-    }
-
-    // Recursively do the same for all nodes
-    daeTArray< daeSmartRef<daeElement> > children;
-    node->getChildren(children);
-    size_t childCount = children.getCount();
-    for (size_t i = 0; i < childCount; i++)
-    {
-        daeElementRef childElement = children[i];
-        if (childElement->getElementType() == COLLADA_TYPE::NODE)
-        {
-            domNodeRef childNode = daeSafeCast<domNode>(childElement);
-            getAnimationChannels(childNode, channels);
-        }
-    }
-}
-
 void DAEOptimizer::deleteEmptyAnimations()
 {
     std::list<domAnimationRef> animations;
@@ -103,12 +53,12 @@ void DAEOptimizer::deleteEmptyAnimations()
     // Get the list of empty animations
     domLibrary_animations_Array& animationLibrary = _dom->getLibrary_animations_array();
     size_t animationLibraryCount = animationLibrary.getCount();
-    for (size_t i = 0; i < animationLibraryCount; i++)
+    for (size_t i = 0; i < animationLibraryCount; ++i)
     {
         domLibrary_animationsRef& animationsRef = animationLibrary.get(i);
         domAnimation_Array& animationArray = animationsRef->getAnimation_array();
         size_t animationCount = animationArray.getCount();
-        for (size_t j = 0; j < animationCount; j++)
+        for (size_t j = 0; j < animationCount; ++j)
         {
             domAnimationRef& animation = animationArray.get(j);
             if (isEmptyAnimation(animation))
@@ -119,7 +69,7 @@ void DAEOptimizer::deleteEmptyAnimations()
     }
 
     // Delete all of the empty animations
-    for (std::list<domAnimationRef>::iterator i = animations.begin(); i != animations.end(); i++)
+    for (std::list<domAnimationRef>::iterator i = animations.begin(); i != animations.end(); ++i)
     {
         daeElement::removeFromParent(*i);
     }

+ 0 - 8
gameplay-encoder/src/DAEOptimizer.h

@@ -33,14 +33,6 @@ public:
 
 private:
 
-    /**
-     * Gets all of the animation channels that target the given node and appends them to the list.
-     * 
-     * @param node The node that the animation channels target.
-     * @param channels The list of channels to append to.
-     */
-    void getAnimationChannels(const domNodeRef& node, std::list<domChannelRef>& channels);
-
     /**
      * Deletes all of the empty animations in the dom.
      */

+ 211 - 232
gameplay-encoder/src/DAESceneEncoder.cpp

@@ -1,4 +1,8 @@
+/*
+ * DAESceneEncoder.h
+ */
 #include "Base.h"
+
 #include "DAESceneEncoder.h"
 #include "DAEOptimizer.h"
 
@@ -16,28 +20,10 @@ DAESceneEncoder::~DAESceneEncoder()
 {
 }
 
-std::string getFilenameFromFilePath(const std::string& filepath)
-{
-    if (filepath.find_last_of("/") != std::string::npos)
-    {
-        return filepath.substr(filepath.find_last_of("/")+1);
-    }
-    return "";
-}
-
-std::string getFilenameNoExt(const std::string& filename)
-{
-    if (filename.find_last_of(".") != std::string::npos)
-    {
-        return filename.substr(0, filename.find_last_of("."));
-    }
-    return filename;
-}
-
 unsigned int getMaxOffset(domInputLocalOffset_Array& inputArray)
 {
     unsigned int maxOffset = 0;
-    for (unsigned int i = 0; i < (int)inputArray.getCount(); i++ )
+    for (unsigned int i = 0; i < (int)inputArray.getCount(); ++i)
     {
         if ( inputArray[i]->getOffset() > maxOffset )
         {
@@ -58,7 +44,7 @@ void DAESceneEncoder::optimizeCOLLADA(const EncoderArguments& arguments, domCOLL
     if (size > 0)
     {
         begin();
-        for (size_t i = 0; i < size; i++)
+        for (size_t i = 0; i < size; ++i)
         {
             optimizer.combineAnimations(groupAnimatioNodeIds[i], groupAnimatioIds[i]);
         }
@@ -78,7 +64,7 @@ void DAESceneEncoder::triangulate(DAE* dae)
     daeDatabase* dataBase = dae->getDatabase();
 
     int geometryCount = (int)(dataBase->getElementCount(0, "geometry"));
-    for (int i = 0; i < geometryCount; i++)
+    for (int i = 0; i < geometryCount; ++i)
     {
         // Find the next geometry element.
         domGeometry* domGeometry;
@@ -93,14 +79,14 @@ void DAESceneEncoder::triangulate(DAE* dae)
 
         // Loop over all the polygons elements.
         int polygonsCount = (int)(domMesh->getPolygons_array().getCount());
-        for (int j = 0; j < polygonsCount; j++)
+        for (int j = 0; j < polygonsCount; ++j)
         {
             // Get the polygons out of the mesh.
             domPolygons* domPolygons = domMesh->getPolygons_array()[j];
             // Create the triangles from the polygons
             createTrianglesFromPolygons(domMesh, domPolygons);
         }
-        while(domMesh->getPolygons_array().getCount() > 0)
+        while (domMesh->getPolygons_array().getCount() > 0)
         {
             domPolygons* domPolygons = domMesh->getPolygons_array().get(0);
             // Remove the polygons from the mesh.
@@ -109,14 +95,14 @@ void DAESceneEncoder::triangulate(DAE* dae)
 
         // Loop over all the polylist elements.
         int polylistCount = (int)(domMesh->getPolylist_array().getCount());
-        for (int j = 0; j < polylistCount; j++)
+        for (int j = 0; j < polylistCount; ++j)
         {
             // Get the polylist out of the mesh.
             domPolylist* domPolylist = domMesh->getPolylist_array()[j];
             // Create the triangles from the polygon list
             createTrianglesFromPolylist(domMesh, domPolylist);
         }
-        while(domMesh->getPolylist_array().getCount() > 0)
+        while (domMesh->getPolylist_array().getCount() > 0)
         {
             domPolylist* domPolylist = domMesh->getPolylist_array().get(0);
             // Remove the polylist from the mesh.
@@ -134,7 +120,7 @@ void DAESceneEncoder::createTrianglesFromPolygons(domMesh* domMesh, domPolygons*
     domP* domTrianglesP = (domP*)triangles->createAndPlace("p");
     
     // Give the new <triangles> the same <_dae> and <parameters> as the old  <polygons>.
-    for (unsigned int i = 0; i < domPolygons->getInput_array().getCount(); i++)
+    for (unsigned int i = 0; i < domPolygons->getInput_array().getCount(); ++i)
     {
         triangles->placeElement(domPolygons->getInput_array()[i]->clone());
     }
@@ -144,7 +130,7 @@ void DAESceneEncoder::createTrianglesFromPolygons(domMesh* domMesh, domPolygons*
     unsigned int primitiveCount = domPolygons->getP_array().getCount();
     
     // Triangulate all the primitives, this generates all the triangles in a single <p> element.
-    for (unsigned int j = 0; j < primitiveCount; j++)
+    for (unsigned int j = 0; j < primitiveCount; ++j)
     {
         // Check the polygons for consistancy (some exported files have had the wrong number of indices).
         domP* domCurrentP = domPolygons->getP_array()[j];
@@ -159,21 +145,21 @@ void DAESceneEncoder::createTrianglesFromPolygons(domMesh* domMesh, domPolygons*
             
             // Write out the primitives as triangles, just fan using the first element as the base.
             unsigned int index = inputCount;
-            for (unsigned int k = 0; k < triangleCount; k++)
+            for (unsigned int k = 0; k < triangleCount; ++k)
             {
                 // First vertex.
-                for (unsigned int l = 0; l < inputCount; l++)
+                for (unsigned int l = 0; l < inputCount; ++l)
                 {
                     domTrianglesP->getValue().append(domCurrentP->getValue()[l]);
                 }
                 // Second vertex.
-                for (unsigned int l = 0; l < inputCount; l++)
+                for (unsigned int l = 0; l < inputCount; ++l)
                 {
                     domTrianglesP->getValue().append(domCurrentP->getValue()[index + l]);
                 }
                 // Third vertex.
                 index += inputCount;
-                for (unsigned int l = 0; l < inputCount; l++)
+                for (unsigned int l = 0; l < inputCount; ++l)
                 {
                     domTrianglesP->getValue().append(domCurrentP->getValue()[index + l]);
                 }
@@ -191,7 +177,7 @@ void DAESceneEncoder::createTrianglesFromPolylist(domMesh* domMesh, domPolylist*
     domP* domTrianglesP = (domP*)triangles->createAndPlace("p");
     
     // Give the new <triangles> the same <_dae> and <parameters> as the old <polylist>.
-    for (int i = 0; i < (int)(domPolylist->getInput_array().getCount()); i++)
+    for (int i = 0; i < (int)(domPolylist->getInput_array().getCount()); ++i)
     {
         triangles->placeElement(domPolylist->getInput_array()[i]->clone());
     }
@@ -204,27 +190,27 @@ void DAESceneEncoder::createTrianglesFromPolylist(domMesh* domMesh, domPolylist*
     unsigned int trianglesProcessed = 0;
     
     // Triangulate all the primitives, this generates all the triangles in a single <p> element.
-    for (unsigned int j = 0; j < primitiveCount; j++)
+    for (unsigned int j = 0; j < primitiveCount; ++j)
     {
         unsigned int triangleCount = (unsigned int)domPolylist->getVcount()->getValue()[j] - 2;
         
         // Write out the primitives as triangles, just fan using the first element as the base.
         int index = inputCount;
-        for (unsigned int k = 0; k < triangleCount; k++)
+        for (unsigned int k = 0; k < triangleCount; ++k)
         {
             // First vertex.
-            for (unsigned int l = 0; l < inputCount; l++)
+            for (unsigned int l = 0; l < inputCount; ++l)
             {
                 domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + l]);
             }
             // Second vertex.
-            for (unsigned int l = 0; l < inputCount; l++)
+            for (unsigned int l = 0; l < inputCount; ++l)
             {
                 domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + index + l]);
             }
             // Third vertex.
             index += inputCount;
-            for (unsigned int l = 0; l < inputCount; l++)
+            for (unsigned int l = 0; l < inputCount; ++l)
             {
                 domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + index + l]);
             }
@@ -359,12 +345,12 @@ void DAESceneEncoder::loadAnimations(const domCOLLADA* dom)
     // Call loadAnimation on all <animation> elements in all <library_animations>
     const domLibrary_animations_Array& animationLibrarys = dom->getLibrary_animations_array();
     size_t animationLibrarysCount = animationLibrarys.getCount();
-    for (size_t i = 0; i < animationLibrarysCount; i++)
+    for (size_t i = 0; i < animationLibrarysCount; ++i)
     {
         const domLibrary_animationsRef& libraryAnimation = animationLibrarys.get(i);
         const domAnimation_Array& animationArray = libraryAnimation->getAnimation_array();
         size_t animationCount = animationArray.getCount();
-        for (size_t j = 0; j < animationCount; j++)
+        for (size_t j = 0; j < animationCount; ++j)
         {
             const domAnimationRef& animationRef = animationArray.get(j);
             loadAnimation(animationRef);
@@ -387,7 +373,7 @@ void DAESceneEncoder::loadAnimation(const domAnimationRef animationRef)
     // <channel>
     const domChannel_Array& channelArray = animationRef->getChannel_array();
     size_t channelArrayCount = channelArray.getCount();
-    for (size_t i = 0; i < channelArrayCount; i++)
+    for (size_t i = 0; i < channelArrayCount; ++i)
     {
         AnimationChannel* animationChannel = new AnimationChannel();
 
@@ -400,7 +386,7 @@ void DAESceneEncoder::loadAnimation(const domAnimationRef animationRef)
         // <input>
         const domInputLocal_Array& inputArray = sampler->getInput_array();
         size_t inputArrayCount = inputArray.getCount();
-        for (size_t j = 0; j < inputArrayCount; j++)
+        for (size_t j = 0; j < inputArrayCount; ++j)
         {
             const domInputLocalRef& inputLocal = inputArray.get(j);
 
@@ -421,7 +407,7 @@ void DAESceneEncoder::loadAnimation(const domAnimationRef animationRef)
                 if (equals(semantic, "INPUT"))
                 {
                     // TODO: Ensure param name is TIME?
-                    for (std::vector<float>::iterator k = floats.begin(); k != floats.end(); k++)
+                    for (std::vector<float>::iterator k = floats.begin(); k != floats.end(); ++k)
                     {
                         // Convert seconds to milliseconds
                         *k = *k * 1000.0f;
@@ -469,7 +455,7 @@ void DAESceneEncoder::loadInterpolation(const domSourceRef source, AnimationChan
     values.resize(count);
     if (count > 0)
     {
-        for (size_t i = 0; i < count; i++)
+        for (size_t i = 0; i < count; ++i)
         {
             values[i] = AnimationChannel::getInterpolationType(names.get(i));
         }
@@ -478,7 +464,7 @@ void DAESceneEncoder::loadInterpolation(const domSourceRef source, AnimationChan
         // instead of storing the same type for each key frame.
         unsigned int firstType = values[0];
         bool allEqual = true;
-        for (size_t i = 1; i < count; i++)
+        for (size_t i = 1; i < count; ++i)
         {
             if (firstType != values[i])
             {
@@ -508,7 +494,7 @@ bool DAESceneEncoder::loadTarget(const domChannelRef& channelRef, AnimationChann
     const char* targetId = channelTarget.getTargetId().c_str();
 
     // TODO: Do we want to support more than one? If yes then this needs to be fixed.
-    for (size_t i = 0; i < channelTarget.getTargetAttributeCount(); i++)
+    for (size_t i = 0; i < channelTarget.getTargetAttributeCount(); ++i)
     {
         std::string prop;
         channelTarget.getPropertyName(i, &prop);
@@ -544,7 +530,7 @@ bool DAESceneEncoder::loadTarget(const domChannelRef& channelRef, AnimationChann
                         // Convert (ANGLE ANGLE ANGLE) to (X Y Z ANGLE X Y Z ANGLE X Y Z ANGLE)
                         std::vector<float> floats(size * 4);
                         // Duplicate rotation axis. We will replace only the angle that COLLADA is targeting.
-                        for (size_t j = 0; j < size; j++)
+                        for (size_t j = 0; j < size; ++j)
                         {
                             size_t k = j * 4;
                             floats[k+0] = x;
@@ -620,7 +606,7 @@ bool DAESceneEncoder::loadTarget(const domChannelRef& channelRef, AnimationChann
                 std::vector<float> floats(newSize);
 
                 size_t matrixCount = keyValues.size() / 16;
-                for (size_t i = 0; i < matrixCount; i++)
+                for (size_t i = 0; i < matrixCount; ++i)
                 {
                     size_t j = i * 16;
                     // COLLADA used row-major but the Matrix class uses column-major
@@ -678,7 +664,7 @@ void DAESceneEncoder::copyFloats(const domFloat_array* source, std::vector<float
     size_t count = (size_t)source->getCount();
     t.resize(count);
     const domListOfFloats& listOfFloats = source->getValue();
-    for (size_t i = 0; i < count; i++)
+    for (size_t i = 0; i < count; ++i)
     {
         t[i] = (float)listOfFloats.get(i);
     }
@@ -691,7 +677,7 @@ void DAESceneEncoder::loadScene(const domVisual_scene* visualScene)
     const domNode_Array& nodes = visualScene->getNode_array();
     scene->setId(visualScene->getId());
     size_t childCount = nodes.getCount();
-    for (size_t i = 0; i < childCount; i++)
+    for (size_t i = 0; i < childCount; ++i)
     {
         scene->add(loadNode(nodes[i], NULL));
     }
@@ -713,11 +699,11 @@ Node* DAESceneEncoder::findSceneActiveCameraNode(const domVisual_scene* visualSc
     // Find the active camera
     const domVisual_scene::domEvaluate_scene_Array& evaluateScenes = visualScene->getEvaluate_scene_array();
     size_t evaluateSceneCount = evaluateScenes.getCount();
-    for (size_t i = 0; i < evaluateSceneCount; i++)
+    for (size_t i = 0; i < evaluateSceneCount; ++i)
     {
         const domVisual_scene::domEvaluate_scene::domRender_Array& renders = evaluateScenes[i]->getRender_array();
         size_t renderCount = renders.getCount();
-        for (size_t j = 0; j < renderCount; j++)
+        for (size_t j = 0; j < renderCount; ++j)
         {
             xsAnyURI cameraNodeURI = renders[i]->getCamera_node();
             domNode* nodeRef = daeSafeCast<domNode>(cameraNodeURI.getElement());
@@ -779,7 +765,7 @@ Node* DAESceneEncoder::loadNode(domNode* n, Node* parent)
     // Load child nodes
     const domNode_Array& childNodes = n->getNode_array();
     size_t childCount = childNodes.getCount();
-    for (size_t i = 0; i < childCount; i++)
+    for (size_t i = 0; i < childCount; ++i)
     {
         loadNode(childNodes.get(i), node);
     }
@@ -830,7 +816,7 @@ void DAESceneEncoder::calcTransform(domNode* domNode, Matrix& dstTransform)
     daeTArray<daeSmartRef<daeElement> > children;
     domNode->getChildren(children);
     size_t childCount = children.getCount();
-    for (size_t i = 0; i < childCount; i++)
+    for (size_t i = 0; i < childCount; ++i)
     {
         daeElementRef childElement = children[i];
         switch (childElement->getElementType())
@@ -895,7 +881,7 @@ void DAESceneEncoder::loadCameraInstance(const domNode* n, Node* node)
     // Does this node have any camera instances?
     const domInstance_camera_Array& instanceCameras = n->getInstance_camera_array();
     size_t instanceCameraCount = instanceCameras.getCount();
-    for (size_t i = 0; i < instanceCameraCount; i++)
+    for (size_t i = 0; i < instanceCameraCount; ++i)
     {
         // Get the camrea object
         const domInstance_camera* cameraInstanceRef = instanceCameras.get(i);
@@ -904,10 +890,10 @@ void DAESceneEncoder::loadCameraInstance(const domNode* n, Node* node)
 
         if (cameraRef)
         {
-            CameraInstance* cameraInstance = loadCamera(cameraRef);
-            if (cameraInstance)
+            Camera* camera = loadCamera(cameraRef);
+            if (camera)
             {
-                node->setCameraInstance(cameraInstance);
+                node->setCamera(camera);
             }
         }
         else
@@ -922,7 +908,7 @@ void DAESceneEncoder::loadLightInstance(const domNode* n, Node* node)
     // Does this node have any light instances?
     const domInstance_light_Array& instanceLights = n->getInstance_light_array();
     size_t instanceLightCount = instanceLights.getCount();
-    for (size_t i = 0; i < instanceLightCount; i++)
+    for (size_t i = 0; i < instanceLightCount; ++i)
     {
         // Get the camrea object
         const domInstance_light* lightInstanceRef = instanceLights.get(i);
@@ -931,10 +917,10 @@ void DAESceneEncoder::loadLightInstance(const domNode* n, Node* node)
 
         if (lightRef)
         {
-            LightInstance* lightInstance = loadLight(lightRef);
-            if (lightInstance)
+            Light* light = loadLight(lightRef);
+            if (light)
             {
-                node->setLightInstance(lightInstance);
+                node->setLight(light);
             }
         }
         else
@@ -949,7 +935,7 @@ void DAESceneEncoder::loadGeometryInstance(const domNode* n, Node* node)
     // Does this node have any geometry instances?
     const domInstance_geometry_Array& instanceGeometries = n->getInstance_geometry_array();
     size_t instanceGeometryCount = instanceGeometries.getCount();
-    for (size_t i = 0; i < instanceGeometryCount; i++)
+    for (size_t i = 0; i < instanceGeometryCount; ++i)
     {
         // Get the geometry object
         const domInstance_geometryRef geometryInstanceRef = instanceGeometries.get(i);
@@ -977,7 +963,7 @@ void DAESceneEncoder::loadControllerInstance(const domNode* n, Node* node)
     // Does this node have any controller instances?
     const domInstance_controller_Array& instanceControllers = n->getInstance_controller_array();
     size_t instanceControllerCount = instanceControllers.getCount();
-    for (size_t i = 0; i < instanceControllerCount; i++)
+    for (size_t i = 0; i < instanceControllerCount; ++i)
     {
         const domInstance_controllerRef instanceControllerRef = instanceControllers.get(i);
         xsAnyURI controllerURI = instanceControllerRef->getUrl();
@@ -1016,202 +1002,181 @@ void DAESceneEncoder::loadControllerInstance(const domNode* n, Node* node)
     }
 }
 
-CameraInstance* DAESceneEncoder::loadCamera(const domCamera* cameraRef)
+Camera* DAESceneEncoder::loadCamera(const domCamera* cameraRef)
 {
-    ///////////////////////////// CAMERA
+    Camera* camera = new Camera();
+    camera->setId(cameraRef->getId());
 
-    // check if camera is already added to gamePlayFile
-    const char* id = cameraRef->getId();
-    Camera* camera = _gamePlayFile.getCamera(id);
-    if (camera == NULL)
+    // Optics
+    const domCamera::domOpticsRef opticsRef = cameraRef->getOptics();
+    if (opticsRef.cast())
     {
-        camera = new Camera();
-        camera->setId(id);
+        const domCamera::domOptics::domTechnique_commonRef techRef = opticsRef->getTechnique_common();
 
-        // Optics
-        const domCamera::domOpticsRef opticsRef = cameraRef->getOptics();
-        if (opticsRef.cast())
+        // Orthographics
+        const domCamera::domOptics::domTechnique_common::domOrthographicRef orthographicRef = techRef->getOrthographic();
+        if (orthographicRef.cast())
         {
-            const domCamera::domOptics::domTechnique_commonRef techRef = opticsRef->getTechnique_common();
-
-            // Orthographics
-            const domCamera::domOptics::domTechnique_common::domOrthographicRef orthographicRef = techRef->getOrthographic();
-            if (orthographicRef.cast())
+            camera->setOrthographic();
+            camera->setAspectRatio((float)orthographicRef->getAspect_ratio()->getValue());
+            camera->setNearPlane((float)orthographicRef->getZnear()->getValue());
+            camera->setFarPlane((float)orthographicRef->getZfar()->getValue());
+
+            const domTargetableFloatRef xmag = orthographicRef->getXmag();
+            const domTargetableFloatRef ymag = orthographicRef->getYmag();
+            // Viewport width
+            if (xmag.cast())
             {
-                camera->setOrthographic();
-                camera->setNearPlane((float)orthographicRef->getZnear()->getValue());
-                camera->setFarPlane((float)orthographicRef->getZfar()->getValue());
-
-                const domTargetableFloatRef xmag = orthographicRef->getXmag();
-                const domTargetableFloatRef ymag = orthographicRef->getYmag();
-                // Viewport width
-                if (xmag.cast())
-                {
-                    camera->setViewportWidth((float)xmag->getValue());
-                }
-                // Viewport height
-                if (ymag.cast())
-                {
-                    camera->setViewportHeight((float)ymag->getValue());
-                }
-                // TODO: Viewport x and y?
+                camera->setViewportWidth((float)xmag->getValue());
             }
-
-            // Perspective
-            const domCamera::domOptics::domTechnique_common::domPerspectiveRef perspectiveRef = techRef->getPerspective();
-            if (perspectiveRef.cast())
+            // Viewport height
+            if (ymag.cast())
             {
-                camera->setPerspective();
-                camera->setNearPlane((float)perspectiveRef->getZnear()->getValue());
-                camera->setFarPlane((float)perspectiveRef->getZfar()->getValue());
+                camera->setViewportHeight((float)ymag->getValue());
+            }
+            // TODO: Viewport x and y?
+        }
 
-                float aspectRatio = -1.0f;
-                if (perspectiveRef->getAspect_ratio().cast())
-                {
-                    aspectRatio = (float)perspectiveRef->getAspect_ratio()->getValue();
-                    camera->setAspectRatio(aspectRatio);
-                }
-                if (perspectiveRef->getYfov().cast())
-                {
-                    camera->setFieldOfView((float)perspectiveRef->getYfov()->getValue());
-                }
-                else if (perspectiveRef->getXfov().cast() && aspectRatio > 0.0f)
-                {
-                    // The gameplaybinary stores the yfov but collada might have specified
-                    // an xfov and an aspect ratio. So use those to calculate the yfov.
-                    float xfov = (float)perspectiveRef->getXfov()->getValue();
-                    float yfov = xfov / aspectRatio;
-                    camera->setFieldOfView(yfov);
-                }
+        // Perspective
+        const domCamera::domOptics::domTechnique_common::domPerspectiveRef perspectiveRef = techRef->getPerspective();
+        if (perspectiveRef.cast())
+        {
+            camera->setPerspective();
+            camera->setNearPlane((float)perspectiveRef->getZnear()->getValue());
+            camera->setFarPlane((float)perspectiveRef->getZfar()->getValue());
+
+            float aspectRatio = -1.0f;
+            if (perspectiveRef->getAspect_ratio().cast())
+            {
+                aspectRatio = (float)perspectiveRef->getAspect_ratio()->getValue();
+                camera->setAspectRatio(aspectRatio);
+            }
+            if (perspectiveRef->getYfov().cast())
+            {
+                camera->setFieldOfView((float)perspectiveRef->getYfov()->getValue());
+            }
+            else if (perspectiveRef->getXfov().cast() && aspectRatio > 0.0f)
+            {
+                // The gameplaybinary stores the yfov but collada might have specified
+                // an xfov and an aspect ratio. So use those to calculate the yfov.
+                float xfov = (float)perspectiveRef->getXfov()->getValue();
+                float yfov = xfov / aspectRatio;
+                camera->setFieldOfView(yfov);
             }
         }
-        _gamePlayFile.addCamera(camera);
     }
-    CameraInstance* cameraInstance = new CameraInstance();
-    cameraInstance->setCamera(camera);
-    return cameraInstance;
+    _gamePlayFile.addCamera(camera);
+    return camera;
 }
 
-LightInstance* DAESceneEncoder::loadLight(const domLight* lightRef)
+Light* DAESceneEncoder::loadLight(const domLight* lightRef)
 {
-    ///////////////////////////// LIGHT
-
-    // check if light is already added to gamePlayFile
-    const char* id = lightRef->getId();
-    Light* light = _gamePlayFile.getLight(id);
-    if (light == NULL)
-    {
-        light = new Light();
-        light->setId(lightRef->getId());
+    Light* light = new Light();
+    light->setId(lightRef->getId());
 
-        const domLight::domTechnique_commonRef techRef = lightRef->getTechnique_common();
+    const domLight::domTechnique_commonRef techRef = lightRef->getTechnique_common();
 
-        // Ambient light
+    // Ambient light
+    {
+        const domLight::domTechnique_common::domAmbientRef ambientRef = techRef->getAmbient();
+        if (ambientRef.cast())
         {
-            const domLight::domTechnique_common::domAmbientRef ambientRef = techRef->getAmbient();
-            if (ambientRef.cast())
-            {
-                light->setAmbientLight();
-                // color
-                const domTargetableFloat3Ref float3Ref = ambientRef->getColor();
-                const domFloat3& color3 = float3Ref->getValue();
-                light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
-            }
+            light->setAmbientLight();
+            // color
+            const domTargetableFloat3Ref float3Ref = ambientRef->getColor();
+            const domFloat3& color3 = float3Ref->getValue();
+            light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
         }
+    }
 
-        // Directional light
+    // Directional light
+    {
+        const domLight::domTechnique_common::domDirectionalRef direcitonalRef = techRef->getDirectional();
+        if (direcitonalRef.cast())
         {
-            const domLight::domTechnique_common::domDirectionalRef direcitonalRef = techRef->getDirectional();
-            if (direcitonalRef.cast())
-            {
-                light->setDirectionalLight();
-                // color
-                const domTargetableFloat3Ref float3Ref = direcitonalRef->getColor();
-                const domFloat3& color3 = float3Ref->getValue();
-                light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
-            }
+            light->setDirectionalLight();
+            // color
+            const domTargetableFloat3Ref float3Ref = direcitonalRef->getColor();
+            const domFloat3& color3 = float3Ref->getValue();
+            light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
         }
+    }
 
-        // Spot light
+    // Spot light
+    {
+        const domLight::domTechnique_common::domSpotRef spotRef = techRef->getSpot();
+        if (spotRef.cast())
         {
-            const domLight::domTechnique_common::domSpotRef spotRef = techRef->getSpot();
-            if (spotRef.cast())
-            {
-                light->setSpotLight();
-                // color
-                const domTargetableFloat3Ref float3Ref = spotRef->getColor();
-                const domFloat3& color3 = float3Ref->getValue();
-                light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
+            light->setSpotLight();
+            // color
+            const domTargetableFloat3Ref float3Ref = spotRef->getColor();
+            const domFloat3& color3 = float3Ref->getValue();
+            light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
                 
-                const domTargetableFloatRef& constAtt = spotRef->getConstant_attenuation();
-                if (constAtt.cast())
-                {
-                    light->setConstantAttenuation((float)constAtt->getValue());
-                }
-
-                const domTargetableFloatRef& linearAtt = spotRef->getLinear_attenuation();
-                if (linearAtt.cast())
-                {
-                    light->setLinearAttenuation((float)linearAtt->getValue());
-                }
+            const domTargetableFloatRef& constAtt = spotRef->getConstant_attenuation();
+            if (constAtt.cast())
+            {
+                light->setConstantAttenuation((float)constAtt->getValue());
+            }
 
-                const domTargetableFloatRef& quadAtt = spotRef->getQuadratic_attenuation();
-                if (quadAtt.cast())
-                {
-                    light->setQuadraticAttenuation((float)quadAtt->getValue());
-                }
+            const domTargetableFloatRef& linearAtt = spotRef->getLinear_attenuation();
+            if (linearAtt.cast())
+            {
+                light->setLinearAttenuation((float)linearAtt->getValue());
+            }
 
-                const domTargetableFloatRef& falloffAngle = spotRef->getFalloff_angle();
-                if (falloffAngle.cast())
-                {
-                    light->setFalloffAngle((float)falloffAngle->getValue());
-                }
+            const domTargetableFloatRef& quadAtt = spotRef->getQuadratic_attenuation();
+            if (quadAtt.cast())
+            {
+                light->setQuadraticAttenuation((float)quadAtt->getValue());
+            }
 
-                const domTargetableFloatRef& falloffExp = spotRef->getFalloff_exponent();
-                if (falloffExp.cast())
-                {
-                    light->setFalloffExponent((float)falloffExp->getValue());
-                }
+            const domTargetableFloatRef& falloffAngle = spotRef->getFalloff_angle();
+            if (falloffAngle.cast())
+            {
+                light->setFalloffAngle((float)falloffAngle->getValue());
+            }
 
+            const domTargetableFloatRef& falloffExp = spotRef->getFalloff_exponent();
+            if (falloffExp.cast())
+            {
+                light->setFalloffExponent((float)falloffExp->getValue());
             }
         }
+    }
 
-        // Point light
+    // Point light
+    {
+        const domLight::domTechnique_common::domPointRef pointRef = techRef->getPoint();
+        if (pointRef.cast())
         {
-            const domLight::domTechnique_common::domPointRef pointRef = techRef->getPoint();
-            if (pointRef.cast())
+            light->setPointLight();
+            // color
+            const domTargetableFloat3Ref float3Ref = pointRef->getColor();
+            const domFloat3& color3 = float3Ref->getValue();
+            light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
+
+            const domTargetableFloatRef& constAtt = pointRef->getConstant_attenuation();
+            if (constAtt.cast())
             {
-                light->setPointLight();
-                // color
-                const domTargetableFloat3Ref float3Ref = pointRef->getColor();
-                const domFloat3& color3 = float3Ref->getValue();
-                light->setColor((float)color3.get(0), (float)color3.get(1), (float)color3.get(2));
-
-                const domTargetableFloatRef& constAtt = pointRef->getConstant_attenuation();
-                if (constAtt.cast())
-                {
-                    light->setConstantAttenuation((float)constAtt->getValue());
-                }
+                light->setConstantAttenuation((float)constAtt->getValue());
+            }
 
-                const domTargetableFloatRef& linearAtt = pointRef->getLinear_attenuation();
-                if (linearAtt.cast())
-                {
-                    light->setLinearAttenuation((float)linearAtt->getValue());
-                }
+            const domTargetableFloatRef& linearAtt = pointRef->getLinear_attenuation();
+            if (linearAtt.cast())
+            {
+                light->setLinearAttenuation((float)linearAtt->getValue());
+            }
 
-                const domTargetableFloatRef& quadAtt = pointRef->getQuadratic_attenuation();
-                if (quadAtt.cast())
-                {
-                    light->setQuadraticAttenuation((float)quadAtt->getValue());
-                }
+            const domTargetableFloatRef& quadAtt = pointRef->getQuadratic_attenuation();
+            if (quadAtt.cast())
+            {
+                light->setQuadraticAttenuation((float)quadAtt->getValue());
             }
         }
-
-        _gamePlayFile.addLight(light);
     }
-    LightInstance* lightInstance = new LightInstance();
-    lightInstance->setLight(light);
-    return lightInstance;
+    _gamePlayFile.addLight(light);
+    return light;
 }
 
 void DAESceneEncoder::loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin)
@@ -1300,7 +1265,7 @@ Model* DAESceneEncoder::loadSkin(const domSkin* skinElement)
 
     // Process "JOINT" input semantic first (we need to do this to set the joint count)
     unsigned int jointCount = 0;
-    for (unsigned int i = 0; i < jointInputs.getCount(); i++)
+    for (unsigned int i = 0; i < jointInputs.getCount(); ++i)
     {
         domInputLocalRef input = jointInputs.get(i);
         std::string inputSemantic = std::string(input->getSemantic());
@@ -1351,7 +1316,7 @@ Model* DAESceneEncoder::loadSkin(const domSkin* skinElement)
     }
 
     // Process "INV_BIND_MATRIX" next
-    for (unsigned int i = 0; i < jointInputs.getCount(); i++)
+    for (unsigned int i = 0; i < jointInputs.getCount(); ++i)
     {
         domInputLocalRef input = jointInputs.get(i);
         std::string inputSemantic = std::string(input->getSemantic());
@@ -1365,7 +1330,7 @@ Model* DAESceneEncoder::loadSkin(const domSkin* skinElement)
             //unsigned int matrixFloatsCount = (unsigned int)source->getFloat_array()->getCount();
             unsigned int jointIndex = 0;
 
-            for (unsigned int j = 0; j < jointCount; j++)
+            for (unsigned int j = 0; j < jointCount; ++j)
             {
                 Matrix matrix((float)matrixFloats.get(jointIndex + 0), (float)matrixFloats.get(jointIndex + 4), (float)matrixFloats.get(jointIndex + 8), (float)matrixFloats.get(jointIndex + 12),
                               (float)matrixFloats.get(jointIndex + 1), (float)matrixFloats.get(jointIndex + 5), (float)matrixFloats.get(jointIndex + 9), (float)matrixFloats.get(jointIndex + 13),
@@ -1386,7 +1351,7 @@ Model* DAESceneEncoder::loadSkin(const domSkin* skinElement)
     unsigned int vertexWeightsCount = (unsigned int)vertexWeights->getCount();
     domListOfFloats jointWeights;
 
-    for (unsigned int i = 0; i < jointInputs.getCount(); i++)
+    for (unsigned int i = 0; i < jointInputs.getCount(); ++i)
     {
         domInputLocalOffsetRef input = vertexWeightsInputs.get(i);
         std::string inputSemantic = std::string(input->getSemantic());
@@ -1432,7 +1397,7 @@ Model* DAESceneEncoder::loadSkin(const domSkin* skinElement)
     int weightOffset = 0;
 
     // Go through all the skin vertex influence weights from the indexed data.
-    for (int i = 0; i < skinVertexInfluenceCountTotal; i++)
+    for (int i = 0; i < skinVertexInfluenceCountTotal; ++i)
     {
         // Get the influence count and directly get the vertext blend weights and indices.
         unsigned int vertexInfluenceCount = (unsigned int)skinVertexInfluenceCounts.get(i);
@@ -1441,7 +1406,7 @@ Model* DAESceneEncoder::loadSkin(const domSkin* skinElement)
         //vertexInfluences.SetCapacity(vertexInfluenceCount);
 
         // Get the index/weight pairs and some the weight totals while at it.
-        for (unsigned int j = 0; j < vertexInfluenceCount; j++)
+        for (unsigned int j = 0; j < vertexInfluenceCount; ++j)
         {
             float weight = (float)jointWeights.get((unsigned int)skinVertexJointWeightPairIndices[vOffset + 1]);
             int index = (int)skinVertexJointWeightPairIndices[vOffset];
@@ -1461,7 +1426,7 @@ Model* DAESceneEncoder::loadSkin(const domSkin* skinElement)
         }
 
         // Get up the the maximum vertex weight influence count.
-         for (unsigned int j = 0; j < maxVertexInfluencesCount; j++)
+         for (unsigned int j = 0; j < maxVertexInfluencesCount; ++j)
         {
             if (j < vertexInfluenceCount)
             {
@@ -1569,7 +1534,7 @@ Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& g
     unsigned int inputCount = (unsigned int)-1;
 
     // Loop through our set of triangle lists (each list of triangles corresponds to a single MeshPart)
-    for (unsigned int i = 0; i < trianglesArrayCount; i++)
+    for (unsigned int i = 0; i < trianglesArrayCount; ++i)
     {
         const domTrianglesRef& triangles = trianglesArray.get(i);
         const domInputLocalOffset_Array& inputArray = triangles->getInput_array();
@@ -1579,7 +1544,8 @@ Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& g
         {
             inputCount = (unsigned int)inputArray.getCount();
 
-            for (unsigned int j = 0; j < inputCount; j++)
+            int texCoordCount = 0;
+            for (unsigned int j = 0; j < inputCount; ++j)
             {
                 const domInputLocalOffsetRef& input = inputArray.get(j);
                 std::string inputSemantic = input->getSemantic();
@@ -1588,7 +1554,7 @@ Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& g
                 if (equals(inputSemantic, "VERTEX"))
                 {
                     unsigned int vertexArrayCount = (unsigned int)vertexArray.getCount();
-                    for (unsigned int k = 0; k < vertexArrayCount; k++)
+                    for (unsigned int k = 0; k < vertexArrayCount; ++k)
                     {
                         const domInputLocalRef& vertexInput = vertexArray.get(k);
                         
@@ -1616,6 +1582,14 @@ Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& g
                     if (type == -1)
                     {
                         warning(std::string("Semantic (") + semantic + ") is invalid/unsupported for geometry mesh: " + geometryId);
+                        break;
+                    }
+                    if (type == TEXCOORD0)
+                    {
+                        // Some meshes have multiple texture coordinates
+                        assert(texCoordCount <= 7);
+                        type += texCoordCount;
+                        ++texCoordCount;
                     }
 
                     DAEPolygonInput* polygonInput = new DAEPolygonInput();
@@ -1643,7 +1617,7 @@ Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& g
             // If there is a triangle array with a different number of inputs, this is not supported.
             if (inputCount != (unsigned int)inputArray.getCount())
             {
-                for (size_t j = 0; j < polygonInputs.size(); j++)
+                for (size_t j = 0; j < polygonInputs.size(); ++j)
                 {
                     delete polygonInputs[j];
                 }
@@ -1659,7 +1633,7 @@ Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& g
     
     // Now we have validated that all input in all triangles are the same and in the same input layout.
     // Lets start to read them and build our subsets.
-    for (unsigned int i = 0; i < trianglesArrayCount; i++)
+    for (unsigned int i = 0; i < trianglesArrayCount; ++i)
     {
         // Subset to be built.
         MeshPart* subset = new MeshPart();
@@ -1765,6 +1739,11 @@ Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& g
                     vertex.texCoord.y = (float)source.get(polyIndex * 2 + 1);
                 }
                 break;
+
+            case TEXCOORD1:
+                // TODO
+                break;
+
             default:
                 break;
             }

+ 2 - 4
gameplay-encoder/src/DAESceneEncoder.h

@@ -5,9 +5,7 @@
 #include "Object.h"
 #include "Node.h"
 #include "Camera.h"
-#include "CameraInstance.h"
 #include "Light.h"
-#include "LightInstance.h"
 #include "Mesh.h"
 #include "MeshPart.h"
 #include "MeshSkin.h"
@@ -116,8 +114,8 @@ private:
      */
     void loadAnimation(const domAnimationRef animationRef);
 
-    CameraInstance* loadCamera(const domCamera* cameraRef);
-    LightInstance* loadLight(const domLight* lightRef);
+    Camera* loadCamera(const domCamera* cameraRef);
+    Light* loadLight(const domLight* lightRef);
     Model* loadSkin(const domSkin* skinElement);
     Model* loadGeometry(const domGeometry* geometry, const domBind_materialRef bindMaterial);
 

+ 60 - 10
gameplay-encoder/src/DAEUtil.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "DAEUtil.h"
+#include "StringUtil.h"
 
 namespace gameplay
 {
@@ -14,6 +15,55 @@ namespace gameplay
  */
 int getIndex(const domInstance_controller::domSkeleton_Array& skeletonArray, const domNodeRef& node);
 
+void getAnimationChannels(const domNodeRef& node, std::list<domChannelRef>& channels)
+{
+    assert(node->getId());
+    std::string nodeIdSlash (node->getId());
+    nodeIdSlash.append("/");
+
+    domCOLLADA* root = (domCOLLADA*)node->getDocument()->getDomRoot();
+
+    domLibrary_animations_Array& animationLibrary = root->getLibrary_animations_array();
+    size_t animationLibraryCount = animationLibrary.getCount();
+    for (size_t i = 0; i < animationLibraryCount; ++i)
+    {
+        domLibrary_animationsRef& animationsRef = animationLibrary.get(i);
+        domAnimation_Array& animationArray = animationsRef->getAnimation_array();
+        size_t animationCount = animationArray.getCount();
+        for (size_t j = 0; j < animationCount; ++j)
+        {
+            domAnimationRef& animationRef = animationArray.get(j);
+            domChannel_Array& channelArray = animationRef->getChannel_array();
+            size_t channelArrayCount = channelArray.getCount();
+            for (size_t k = 0; k < channelArrayCount; ++k)
+            {
+                domChannelRef& channel = channelArray.get(k);
+                const char* target = channel->getTarget();
+
+                // TODO: Assumes only one target per channel?
+                if (startsWith(target, nodeIdSlash.c_str()))
+                {
+                    channels.push_back(channel);
+                }
+            }
+        }
+    }
+
+    // Recursively do the same for all nodes
+    daeTArray< daeSmartRef<daeElement> > children;
+    node->getChildren(children);
+    size_t childCount = children.getCount();
+    for (size_t i = 0; i < childCount; ++i)
+    {
+        daeElementRef childElement = children[i];
+        if (childElement->getElementType() == COLLADA_TYPE::NODE)
+        {
+            domNodeRef childNode = daeSafeCast<domNode>(childElement);
+            getAnimationChannels(childNode, channels);
+        }
+    }
+}
+
 void getJointNames(const domSourceRef source, std::vector<std::string>& list)
 {
     // BLENDER used name_array
@@ -22,7 +72,7 @@ void getJointNames(const domSourceRef source, std::vector<std::string>& list)
     {
         domListOfNames& ids = nameArray->getValue();
         size_t jointCount = (size_t)nameArray->getCount();
-        for (size_t j = 0; j < jointCount; j++)
+        for (size_t j = 0; j < jointCount; ++j)
         {
             list.push_back(std::string(ids.get(j)));
         }
@@ -35,7 +85,7 @@ void getJointNames(const domSourceRef source, std::vector<std::string>& list)
         {
             xsIDREFS& ids = idArray->getValue();
             size_t jointCount = (size_t)idArray->getCount();
-            for (size_t j = 0; j < jointCount; j++)
+            for (size_t j = 0; j < jointCount; ++j)
             {
                 list.push_back(std::string(ids.get(j).getID()));
             }
@@ -48,7 +98,7 @@ void getJointNames(const domSkin* skin, std::vector<std::string>& list)
     const domSkin::domJointsRef& joints = skin->getJoints();
     const domInputLocal_Array& inputArray = joints->getInput_array();
     size_t inputCount = inputArray.getCount();
-    for (size_t i = 0; i < inputCount; i++)
+    for (size_t i = 0; i < inputCount; ++i)
     {
         const domInputLocalRef input = inputArray.get(i);
         const char* semantic = input->getSemantic();
@@ -72,7 +122,7 @@ domSource* getInputSource(const domChannelRef& channel)
         domSampler* sampler = daeSafeCast<domSampler>(element);
         const domInputLocal_Array& inputArray = sampler->getInput_array();
         size_t inputArrayCount = inputArray.getCount();
-        for (size_t i = 0; i < inputArrayCount; i++)
+        for (size_t i = 0; i < inputArrayCount; ++i)
         {
             const domInputLocalRef& input = inputArray.get(i);
             if (strcmp(input->getSemantic(), "INPUT") == 0)
@@ -108,7 +158,7 @@ const domSamplerRef getSampler(const domChannelRef& channel)
         
         const domSampler_Array& samplerArray = animation->getSampler_array();
         size_t count = samplerArray.getCount();
-        for (size_t i = 0; i < count; i++)
+        for (size_t i = 0; i < count; ++i)
         {
             const domSamplerRef& sampler = samplerArray.get(i);
             if (id.compare(sampler->getId()) == 0)
@@ -133,7 +183,7 @@ const domSourceRef getSource(const domInputLocalRef& inputLocal, const domAnimat
     const std::string& id = uri.id();
     const domSource_Array& sourceArray = animation->getSource_array();
     size_t count = sourceArray.getCount();
-    for (size_t i = 0; i < count; i++)
+    for (size_t i = 0; i < count; ++i)
     {
         const domSourceRef source = sourceArray.get(i);
         if (id.compare(source->getId()) == 0)
@@ -154,7 +204,7 @@ const domName_arrayRef getSourceNameArray(const domSourceRef& source)
     daeTArray<daeSmartRef<daeElement> > children;
     source->getChildren(children);
     size_t childCount = children.getCount();
-    for (size_t i = 0; i < childCount; i++)
+    for (size_t i = 0; i < childCount; ++i)
     {
         const daeElementRef element = children.get(i);
         if (element->getElementType() == COLLADA_TYPE::NAME_ARRAY)
@@ -223,7 +273,7 @@ bool equalKeyTimes(const domSource* s1, const domSource* s2)
         const domListOfFloats& list2 = f2->getValue();
 
         size_t count = (size_t)f1->getCount();
-        for (size_t i = 0; i < count; i++)
+        for (size_t i = 0; i < count; ++i)
         {
             if (list1.get(i) != list2.get(i))
             {
@@ -259,7 +309,7 @@ void moveChannelAndSouresToAnimation(domChannelRef& channel, domAnimationRef& an
 
         domInputLocal_Array& inputArray = sampler->getInput_array();
         size_t inputArrayCount = inputArray.getCount();
-        for (size_t i = 0; i < inputArrayCount; i++)
+        for (size_t i = 0; i < inputArrayCount; ++i)
         {
             inputArray = sampler->getInput_array();
             const domInputLocalRef& input = inputArray.get(i);
@@ -289,7 +339,7 @@ int getIndex(const domInstance_controller::domSkeleton_Array& skeletonArray, con
 {
     const std::string nodeId = node->getId();
     size_t count = skeletonArray.getCount();
-    for (size_t i = 0; i < count; i++)
+    for (size_t i = 0; i < count; ++i)
     {
         const domInstance_controller::domSkeletonRef& skeleton = skeletonArray.get(i);
         daeElementRef element = skeleton->getValue().getElement();

+ 9 - 1
gameplay-encoder/src/DAEUtil.h

@@ -4,6 +4,14 @@
 namespace gameplay
 {
 
+/**
+ * Gets all of the animation channels that target the given node and appends them to the list.
+ * 
+ * @param node The node that the animation channels target.
+ * @param channels The list of channels to append to.
+ */
+void getAnimationChannels(const domNodeRef& node, std::list<domChannelRef>& channels);
+
 /**
  * Gets the joint names for the given source and appends them to the given list.
  * 
@@ -39,7 +47,7 @@ domSource* getInputSource(const domChannelRef& channel);
 const domSamplerRef getSampler(const domChannelRef& channel);
 
 /**
- * Returns the source from the given sampler input. 
+ * Returns the source from the given sampler input.
  * Searchs within the given animation.
  * 
  * @param inputLocal The input element within a sampler.

+ 43 - 11
gameplay-encoder/src/EncoderArguments.cpp

@@ -1,4 +1,5 @@
 #include "Base.h"
+
 #include "EncoderArguments.h"
 #include "StringUtil.h"
 
@@ -27,12 +28,12 @@ EncoderArguments::EncoderArguments(size_t argc, const char** argv) :
         
         // read the options
         std::vector<std::string> options;
-        for (size_t i = 1; i < filePathIndex; i++)
+        for (size_t i = 1; i < filePathIndex; ++i)
         {
             options.push_back(argv[i]);
         }
         
-        for (size_t i = 0; i < options.size(); i++)
+        for (size_t i = 0; i < options.size(); ++i)
         {
             if (options[i][0] == '-')
             {
@@ -75,6 +76,24 @@ const std::vector<std::string>& EncoderArguments::getGroupAnimationAnimationId()
     return _groupAnimationAnimationId;
 }
 
+bool EncoderArguments::containsGroupNodeId(const std::string& nodeId) const
+{
+    return find(_groupAnimationNodeId.begin(), _groupAnimationNodeId.end(), nodeId) != _groupAnimationNodeId.end();
+}
+
+const std::string EncoderArguments::getAnimationId(const std::string& nodeId) const
+{
+    std::vector<std::string>::const_iterator it = find(_groupAnimationNodeId.begin(), _groupAnimationNodeId.end(), nodeId);
+    for (size_t i = 0, size = _groupAnimationNodeId.size(); i < size; ++i)
+    {
+        if (_groupAnimationNodeId[i].compare(nodeId) == 0)
+        {
+            return _groupAnimationAnimationId[i];
+        }
+    }
+    return "";
+}
+
 bool EncoderArguments::parseErrorOccured() const
 {
     return _parseError;
@@ -95,14 +114,23 @@ bool EncoderArguments::fileExists() const
 
 void EncoderArguments::printUsage() const
 {
-    fprintf(stderr,"Usage: gameplay-encoder [options] <filepath>\n");
-    fprintf(stderr,".dae file options:\n");
-    fprintf(stderr," -i <id>\tFilter by node ID\n");
-    fprintf(stderr," -t\tWrite text/xml\n");
-    fprintf(stderr," -groupAnimations <nodeID> <animationID>\tGroup all animation channels targetting the nodes into a new animation\n");
-    fprintf(stderr," -dae <filepath>\tOutput optimized DAE\n");
-    fprintf(stderr,".ttf file options:\n");
-    fprintf(stderr," -s <size of font> -p \n");
+    fprintf(stderr,"Usage: gameplay-encoder [options] <filepath>\n\n");
+    fprintf(stderr,"Supported file extensions:\n");
+    fprintf(stderr,"  .dae\t(COLLADA)\n");
+    fprintf(stderr,"  .fbx\t(FBX)\n");
+    fprintf(stderr,"  .ttf\t(TrueType Font)\n");
+    fprintf(stderr,"\n");
+    fprintf(stderr,"COLLADA and FBX file options:\n");
+    fprintf(stderr,"  -i<id>\t\tFilter by node ID.\n");
+    fprintf(stderr,"  -t\t\t\tWrite text/xml.\n");
+    fprintf(stderr,"  -groupAnimations <node id> <animation id>\n\t\t\tGroup all animation channels targetting the nodes into a new animation.\n");
+    fprintf(stderr,"\n");
+    fprintf(stderr,"COLLADA file options:\n");
+    fprintf(stderr,"  -dae <filepath>\tOutput optimized DAE.\n");
+    fprintf(stderr,"\n");
+    fprintf(stderr,"TTF file options:\n");
+    fprintf(stderr,"  -s <size of font>\tSize of the font.\n");
+    fprintf(stderr,"  -p\t\t\tOutput font preview.\n");
     exit(8);
 }
 
@@ -154,6 +182,10 @@ EncoderArguments::FileFormat EncoderArguments::getFileFormat() const
     {
         return FILEFORMAT_DAE;
     }
+    if (ext.compare("fbx") == 0 || ext.compare("FBX") == 0)
+    {
+        return FILEFORMAT_FBX;
+    }
     if (ext.compare("ttf") == 0 || ext.compare("TTF") == 0)
     {
         return FILEFORMAT_TTF;
@@ -268,7 +300,7 @@ std::string EncoderArguments::getRealPath(const std::string& filepath)
 
 void EncoderArguments::replace_char(char* str, char oldChar, char newChar)
 {
-    for (; *str != '\0'; str++)
+    for (; *str != '\0'; ++str)
     {
         if (*str == oldChar)
         {

+ 5 - 0
gameplay-encoder/src/EncoderArguments.h

@@ -15,6 +15,7 @@ public:
     {
         FILEFORMAT_UNKNOWN,
         FILEFORMAT_DAE,
+        FILEFORMAT_FBX,
         FILEFORMAT_TTF,
         FILEFORMAT_GPB
     };
@@ -49,9 +50,13 @@ public:
      */
     const std::string& getDAEOutputPath() const;
 
+
     const std::vector<std::string>& getGroupAnimationNodeId() const;
     const std::vector<std::string>& getGroupAnimationAnimationId() const;
 
+    bool containsGroupNodeId(const std::string& nodeId) const;
+    const std::string getAnimationId(const std::string& nodeId) const;
+
     /**
      * Returns true if an error occured while parsing the command line arguments.
      */

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

@@ -0,0 +1,1336 @@
+#ifdef USE_FBX
+
+#include <algorithm>
+#include <string>
+
+#include "FBXSceneEncoder.h"
+#include "EncoderArguments.h"
+
+using namespace gameplay;
+
+/**
+ * Returns the aspect ratio from the given camera.
+ * 
+ * @param fbxCamera The FBX camera to get the aspect ratio from.
+ * 
+ * @return The aspect ratio from the camera.
+ */
+float getAspectRatio(KFbxCamera* fbxCamera);
+
+/**
+ * Returns the field of view Y from the given camera.
+ * 
+ * @param fbxCamera The camera to get the fiew of view from.
+ * 
+ * @return The field of view Y.
+ */
+float getFieldOfView(KFbxCamera* fbxCamera);
+
+/**
+ * Loads the texture coordinates from given mesh's polygon part into the vertex.
+ * 
+ * @param fbxMesh The mesh to get the polygon from.
+ * @param polyIndex The index of the polygon.
+ * @param posInPoly The position in the polygon.
+ * @param vertex The vertex to copy the texture coordinates to.
+ */
+void loadTextureCoords(KFbxMesh* fbxMesh, int polyIndex, int posInPoly, Vertex* vertex);
+
+/**
+ * Loads the normal from the mesh and adds it to the given vertex.
+ * 
+ * @param fbxMesh The mesh to get the polygon from.
+ * @param vertexIndex The vertex index in the mesh.
+ * @param vertex The vertex to copy to.
+ */
+void loadNormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+
+/**
+ * Loads the tangent from the mesh and adds it to the given vertex.
+ * 
+ * @param fbxMesh The mesh to load from.
+ * @param vertexIndex The index of the vertex within fbxMesh.
+ * @param vertex The vertex to copy to.
+ */
+void loadTangent(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+
+/**
+ * Loads the binormal from the mesh and adds it to the given vertex.
+ * 
+ * @param fbxMesh The mesh to load from.
+ * @param vertexIndex The index of the vertex within fbxMesh.
+ * @param vertex The vertex to copy to.
+ */
+void loadBinormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex);
+
+/**
+ * Loads the blend weight and blend indices data into the vertex.
+ * 
+ * @param vertexWeights List of vertex weights. The x member contains the blendIndices. The y member contains the blendWeights.
+ * @param vertex The vertex to copy the blend data to.
+ */
+void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex);
+
+/**
+ * Loads the blend weights and blend indices from the given mesh.
+ * 
+ * Each element of weights is a list of Vector2s where "x" is the blend index and "y" is the blend weight.
+ * 
+ * @param fbxMesh The mesh to load from.
+ * @param weights List of blend weights and blend indices for each vertex.
+ * 
+ * @return True if this mesh has a mesh skin, false otherwise.
+ */
+bool loadBlendWeights(KFbxMesh* fbxMesh, std::vector<std::vector<Vector2> >& weights);
+
+/**
+ * Copies from an FBX matrix to a float[16] array.
+ */
+void copyMatrix(const KFbxMatrix& fbxMatrix, float* matrix);
+
+/**
+ * Copies from an FBX matrix to a gameplay matrix.
+ */
+void copyMatrix(const KFbxMatrix& fbxMatrix, Matrix& matrix);
+
+/**
+ * Finds the min and max start time and stop time of the given animation curve.
+ * 
+ * startTime is updated if the animation curve contains a start time that is less than startTime.
+ * stopTime is updated if the animation curve contains a stop time that is greater than stopTime.
+ * frameRate is updated if the animation curve contains a frame rate that is greater than frameRate.
+ * 
+ * @param animCurve The animation curve to read from.
+ * @param startTime The min start time. (in/out)
+ * @param stopTime The max stop time. (in/out)
+ * @param frameRate The frame rate. (in/out)
+ */
+void findMinMaxTime(KFbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate);
+
+/**
+ * Appends a key frame of the given node's transform at the given time.
+ * 
+ * @param fbxNode The node to get the matrix transform from.
+ * @param time The key time to add and the time to get the transform from.
+ * @param keyTimes The list of key times to append to.
+ * @param keyValues The list of key values to append to.
+ */
+void appendKeyFrame(KFbxNode* fbxNode, float time, std::vector<float>* keyTimes, std::vector<float>* keyValues);
+
+/**
+ * Decomposes the given node's matrix transform at the given time and copies to scale, rotation and translation.
+ * 
+ * @param fbxNode The node to get the matrix transform from.
+ * @param time The time to get the matrix transform from.
+ * @param scale The scale to copy to.
+ * @param rotation The rotation to copy to.
+ * @param translation The translation to copy to.
+ */
+void decompose(KFbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation);
+
+/**
+ * Creates an animation channel that targets the given node and target attribute using the given key times and key values.
+ * 
+ * @param fbxNode The node to target.
+ * @param targetAttrib The attribute type to target.
+ * @param keyTimes The key times for the animation channel.
+ * @param keyValues The key values for the animation channel.
+ * 
+ * @return The newly created animation channel.
+ */
+AnimationChannel* createAnimationChannel(KFbxNode* fbxNode, unsigned int targetAttrib, const std::vector<float>& keyTimes, const std::vector<float>& keyValues);
+
+void addScaleChannel(Animation* animation, KFbxNode* fbxNode, float startTime, float stopTime);
+
+void addTranslateChannel(Animation* animation, KFbxNode* fbxNode, float startTime, float stopTime);
+
+
+////////////////////////////////////
+// Member Functions
+////////////////////////////////////
+
+FBXSceneEncoder::FBXSceneEncoder()
+    : _groupAnimation(NULL)
+{
+}
+
+FBXSceneEncoder::~FBXSceneEncoder()
+{
+}
+
+void FBXSceneEncoder::write(const std::string& filepath, const EncoderArguments& arguments)
+{
+    KFbxSdkManager* sdkManager = KFbxSdkManager::Create();
+    KFbxIOSettings *ios = KFbxIOSettings::Create(sdkManager, IOSROOT);
+    sdkManager->SetIOSettings(ios);
+    KFbxImporter* importer = KFbxImporter::Create(sdkManager,"");
+    
+    if (!importer->Initialize(filepath.c_str(), -1, sdkManager->GetIOSettings()))
+    {
+        printf("Call to KFbxImporter::Initialize() failed.\n");
+        printf("Error returned: %s\n\n", importer->GetLastErrorString());
+        exit(-1);
+    }
+    
+    KFbxScene* fbxScene = KFbxScene::Create(sdkManager,"__FBX_SCENE__");
+
+    print("Loading FBX file.");
+    importer->Import(fbxScene);
+    importer->Destroy();
+    print("Loading Scene.");
+    loadScene(fbxScene);
+    print("Loading animations.");
+    loadAnimations(fbxScene, arguments);
+    sdkManager->Destroy();
+
+    print("Optimizing GamePlay Binary.");
+    _gamePlayFile.adjust();
+    
+    std::string filenameOnly = getFilenameFromFilePath(filepath);
+    std::string dstFilename = filepath.substr(0, filepath.find_last_of('/'));
+    dstFilename.append(1, '/');
+    dstFilename.append(getFilenameNoExt(filenameOnly));
+    
+    if (arguments.textOutputEnabled())
+    {
+        std::string outFile = dstFilename + ".xml";
+        fprintf(stderr, "Saving debug file: %s\n", outFile.c_str());
+        _gamePlayFile.saveText(outFile);
+    }
+    else
+    {
+        std::string outFile = dstFilename + ".gpb";
+        fprintf(stderr, "Saving binary file: %s\n", outFile.c_str());
+        _gamePlayFile.saveBinary(outFile);
+    }
+}
+
+void FBXSceneEncoder::loadScene(KFbxScene* fbxScene)
+{
+    Scene* scene = new Scene();
+    scene->setId(fbxScene->GetName());
+    if (scene->getId().length() == 0)
+    {
+        scene->setId("__SCENE__");
+    }
+
+    // Load all of the nodes and their contents.
+    KFbxNode* rootNode = fbxScene->GetRootNode();
+    if (rootNode)
+    {
+        print("Triangulate.");
+        triangulateRecursive(rootNode);
+
+        print("Load nodes.");
+        // Don't include the FBX root node in the GPB.
+        const int childCount = rootNode->GetChildCount();
+        for (int i = 0; i < childCount; ++i)
+        {
+            Node* node = loadNode(rootNode->GetChild(i));
+            if (node)
+            {
+                scene->add(node);
+            }
+        }
+    }
+
+    // Load the MeshSkin information from the scene's poses.
+    loadBindShapes(fbxScene);
+
+    // Find the ambient light of the scene
+    KFbxColor ambientColor = fbxScene->GetGlobalSettings().GetAmbientColor();
+    scene->setAmbientColor((float)ambientColor.mRed, (float)ambientColor.mGreen, (float)ambientColor.mBlue);
+
+    _gamePlayFile.addScene(scene);
+}
+
+void FBXSceneEncoder::loadAnimationChannels(KFbxAnimLayer* animLayer, KFbxNode* fbxNode, Animation* animation)
+{
+    const std::string* targetId = NULL;
+
+    const char* name = fbxNode->GetName();
+    Node* node = _gamePlayFile.getNode(name);
+    if (node)
+    {
+        targetId = &node->getId();
+    }
+    
+    // Determine which properties are animated on this node
+    // Find the transform at each key frame
+    // TODO: Ignore properties that are not animated (scale, rotation, translation)
+    // This should result in only one animation channel per animated node.
+
+    float startTime = FLT_MAX, stopTime = -1.0f, frameRate = FLT_MIN;
+    bool tx = false, ty = false, tz = false, rx = false, ry = false, rz = false, sx = false, sy = false, sz = false;
+    KFbxAnimCurve* animCurve = NULL;
+    animCurve = fbxNode->LclTranslation.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_T_X);
+    if (animCurve)
+    {
+        tx = true;
+        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
+    }
+    animCurve = fbxNode->LclTranslation.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_T_Y);
+    if (animCurve)
+    {
+        ty = true;
+        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
+    }
+    animCurve = fbxNode->LclTranslation.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_T_Z);
+    if (animCurve)
+    {
+        tz = true;
+        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
+    }
+    animCurve = fbxNode->LclRotation.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_R_X);
+    if (animCurve)
+    {
+        rx = true;
+        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
+    }
+    animCurve = fbxNode->LclRotation.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_R_Y);
+    if (animCurve)
+    {
+        ry = true;
+        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
+    }
+    animCurve = fbxNode->LclRotation.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_R_Z);
+    if (animCurve)
+    {
+        rz = true;
+        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
+    }
+    animCurve = fbxNode->LclScaling.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_S_X);
+    if (animCurve)
+    {
+        sx = true;
+        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
+    }
+    animCurve = fbxNode->LclScaling.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_S_Y);
+    if (animCurve)
+    {
+        sy = true;
+        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
+    }
+    animCurve = fbxNode->LclScaling.GetCurve<KFbxAnimCurve>(animLayer, KFCURVENODE_S_Z);
+    if (animCurve)
+    {
+        sz = true;
+        findMinMaxTime(animCurve, &startTime, &stopTime, &frameRate);
+    }
+
+    bool translate = tx | ty | tz;
+    bool scale = sx | sy | sz;
+    bool rotate = rx | ry | rz;
+
+    if (translate || rotate || scale)
+    {
+        assert(startTime != FLT_MAX);
+        assert(stopTime >= 0.0f);
+        AnimationChannel* channel = new AnimationChannel();
+        channel->setTargetId(name);
+        channel->setTargetAttribute(Transform::ANIMATE_SCALE_ROTATE_TRANSLATE);
+        
+        float increment = 1000.0f / frameRate;
+        std::vector<float> keyTimes;
+        std::vector<float> keyValues;
+        for (float time = startTime; time < stopTime; time += increment)
+        {
+            appendKeyFrame(fbxNode, time, &keyTimes, &keyValues);
+        }
+        // Add the last key frame at exactly stopTime
+        appendKeyFrame(fbxNode, stopTime, &keyTimes, &keyValues);
+
+        channel->setKeyTimes(keyTimes);
+        /*
+        std::vector<float> newKeyValues;
+        for (size_t i = 0, size = keyValues.size(); i < size; i += 10)
+        {
+            if (translate)
+            {
+                newKeyValues.push_back(keyValues[i+0]);
+                newKeyValues.push_back(keyValues[i+1]);
+                newKeyValues.push_back(keyValues[i+2]);
+            }
+            if (rotate)
+            {
+                newKeyValues.push_back(keyValues[i+3]);
+                newKeyValues.push_back(keyValues[i+4]);
+                newKeyValues.push_back(keyValues[i+5]);
+                newKeyValues.push_back(keyValues[i+6]);
+            }
+            if (scale)
+            {
+                newKeyValues.push_back(keyValues[i+7]);
+                newKeyValues.push_back(keyValues[i+8]);
+                newKeyValues.push_back(keyValues[i+9]);
+            }
+        }
+        channel->setKeyValues(newKeyValues);
+        */
+        channel->setKeyValues(keyValues);
+        channel->setInterpolation(AnimationChannel::LINEAR);
+        animation->add(channel);
+        /*
+        if (!translate)
+        {
+            addTranslateChannel(animation, fbxNode, startTime, stopTime);
+        }
+        if (!rotate)
+        {
+            printf("rotate?\n"); // TODO
+        }
+        if (!scale)
+        {
+            addScaleChannel(animation, fbxNode, startTime, stopTime);
+        }
+        */
+        if (_groupAnimation != animation)
+        {
+            // TODO explains
+            _gamePlayFile.addAnimation(animation);
+        }
+    }
+}
+
+void FBXSceneEncoder::loadAnimationLayer(KFbxAnimLayer* fbxAnimLayer, KFbxNode* fbxNode, const EncoderArguments& arguments)
+{
+    bool animationGroupId = false;
+    const char* name = fbxNode->GetName();
+    // Check if this node's animations are supposed to be grouped
+    if (name)
+    {
+        std::string str = name;
+        if (arguments.containsGroupNodeId(str))
+        {
+            animationGroupId = true;
+            _groupAnimation = new Animation();
+            _groupAnimation->setId(arguments.getAnimationId(str));
+        }
+    }
+    Animation* animation = _groupAnimation;
+    if (!_groupAnimation)
+    {
+        animation = new Animation();
+        animation->setId(name);
+    }
+    loadAnimationChannels(fbxAnimLayer, fbxNode, animation);
+
+    const int childCount = fbxNode->GetChildCount();
+    for (int modelCount = 0; modelCount < childCount; ++modelCount)
+    {
+        loadAnimationLayer(fbxAnimLayer, fbxNode->GetChild(modelCount), arguments);
+    }
+    if (animationGroupId)
+    {
+        _gamePlayFile.addAnimation(_groupAnimation);
+        _groupAnimation = NULL;
+    }
+}
+
+void FBXSceneEncoder::loadAnimations(KFbxScene* fbxScene, const EncoderArguments& arguments)
+{
+    KFbxAnimEvaluator* evaluator = fbxScene->GetEvaluator();
+    if (!evaluator)
+        return;
+    KFbxAnimStack* animStack = evaluator->GetContext();
+    if (!animStack)
+        return;
+
+    for (int i = 0; i < fbxScene->GetSrcObjectCount(FBX_TYPE(KFbxAnimStack)); ++i)
+    {
+        KFbxAnimStack* animStack = KFbxCast<KFbxAnimStack>(fbxScene->GetSrcObject(FBX_TYPE(KFbxAnimStack), i));
+        int nbAnimLayers = animStack->GetMemberCount(FBX_TYPE(KFbxAnimLayer));
+        for (int l = 0; l < nbAnimLayers; ++l)
+        {
+            KFbxAnimLayer* animLayer = animStack->GetMember(FBX_TYPE(KFbxAnimLayer), l);
+            loadAnimationLayer(animLayer, fbxScene->GetRootNode(), arguments);
+        }
+    }
+}
+
+Node* FBXSceneEncoder::loadNode(KFbxNode* fbxNode)
+{
+    Node* node = NULL;
+
+    // Check if this node has already been loaded
+    const char* id = fbxNode->GetName();
+    if (id && strlen(id) > 0)
+    {
+        node = _gamePlayFile.getNode(fbxNode->GetName());
+        if (node)
+        {
+            return node;
+        }
+    }
+    node = new Node();
+    if (id)
+    {
+        node->setId(id);
+    }
+    _gamePlayFile.addNode(node);
+
+    transformNode(fbxNode, node);
+    
+    loadCamera(fbxNode, node);
+    loadLight(fbxNode, node);
+    loadModel(fbxNode, node);
+
+    if (fbxNode->GetSkeleton())
+    {
+        // Indicate that this is a joint node for the purpose of debugging.
+        // The XML debug output will print that this node is a joint.
+        node->setIsJoint(true);
+    }
+
+    // Load child nodes
+    const int childCount = fbxNode->GetChildCount();
+    for (int i = 0; i < childCount; ++i)
+    {
+        Node* child = loadNode(fbxNode->GetChild(i));
+        if (child)
+        {
+            node->addChild(child);
+        }
+    }
+    return node;
+}
+
+Mesh* FBXSceneEncoder::getMesh(size_t meshId)
+{
+    // Check if this mesh was already loaded.
+    std::map<size_t, Mesh*>::iterator it = _meshes.find(meshId);
+    if (it != _meshes.end())
+    {
+        return it->second;
+    }
+    return NULL;
+}
+
+void FBXSceneEncoder::saveMesh(size_t meshId, Mesh* mesh)
+{
+    assert(mesh);
+    if (!getMesh(meshId))
+    {
+        _meshes[meshId] = mesh;
+    }
+}
+
+void FBXSceneEncoder::print(const char* str)
+{
+    fprintf(stderr,"%s\n", str);
+}
+
+void FBXSceneEncoder::transformNode(KFbxNode* fbxNode, Node* node)
+{
+    KFbxXMatrix matrix;
+    if (fbxNode->GetCamera() || fbxNode->GetLight())
+    {
+        // TODO: Why is this necessary for Camera and Light?
+        matrix.SetTRS(fbxNode->LclTranslation.Get(), fbxNode->LclRotation.Get(), fbxNode->LclScaling.Get());
+    }
+    else
+    {
+        matrix = fbxNode->EvaluateLocalTransform();
+    }
+
+    float m[16];
+    copyMatrix(matrix, m);
+    int i = 0;
+    for (int row = 0; row < 4; ++row)
+    {
+        for (int col = 0; col < 4; ++col)
+        {
+            m[i++] = (float)matrix.Get(row, col);
+        }
+    }
+    node->setTransformMatrix(m);
+}
+
+void FBXSceneEncoder::loadBindShapes(KFbxScene* fbxScene)
+{
+    float m[16];
+    const int poseCount = fbxScene->GetPoseCount();
+    for (int i = 0; i < poseCount; ++i)
+    {
+        KFbxPose* pose = fbxScene->GetPose(i);
+        assert(pose);
+        if (pose->IsBindPose() && pose->GetCount() > 0)
+        {
+            KFbxNode* fbxNode = pose->GetNode(0);
+            if (fbxNode->GetMesh() != NULL)
+            {
+                Node* node = _gamePlayFile.getNode(fbxNode->GetName());
+                assert(node && node->getModel());
+
+                Model* model = node->getModel();
+                if (model && model->getSkin())
+                {
+                    MeshSkin* skin = model->getSkin();
+                    copyMatrix(pose->GetMatrix(0), m);
+                    skin->setBindShape(m);
+                }
+            }
+        }
+    }
+}
+
+void FBXSceneEncoder::loadCamera(KFbxNode* fbxNode, Node* node)
+{
+    KFbxCamera* fbxCamera = fbxNode->GetCamera();
+    if (!fbxCamera)
+    {
+        return;
+    }
+    Camera* camera = new Camera();
+    const char* name = fbxNode->GetName();
+    if (name)
+    {
+        std::string id(name);
+        id.append("_Camera");
+        camera->setId(id);
+    }
+    camera->setAspectRatio(getAspectRatio(fbxCamera));
+    camera->setNearPlane((float)fbxCamera->NearPlane.Get());
+    camera->setFarPlane((float)fbxCamera->FarPlane.Get());
+
+    if (fbxCamera->ProjectionType.Get() == KFbxCamera::eORTHOGONAL)
+    {
+        camera->setOrthographic();
+        camera->setViewportWidth((float)fbxCamera->GetApertureWidth());
+        camera->setViewportWidth((float)fbxCamera->GetApertureHeight());
+        // xmag in FBX can be calculated from: OrthoZoom * 30.0 / 2.0
+        camera->setViewportWidth((float)fbxCamera->OrthoZoom.Get() * 15.0f);
+    }
+    else if (fbxCamera->ProjectionType.Get() == KFbxCamera::ePERSPECTIVE)
+    {
+        camera->setPerspective();
+        camera->setFieldOfView(getFieldOfView(fbxCamera));
+    }
+    else
+    {
+        warning("Unknown camera type in node");
+        return;
+    }
+    _gamePlayFile.addCamera(camera);
+    node->setCamera(camera);
+}
+
+void FBXSceneEncoder::loadLight(KFbxNode* fbxNode, Node* node)
+{
+    KFbxLight* fbxLight = fbxNode->GetLight();
+    if (!fbxLight)
+    {
+        return;
+    }
+    Light* light = new Light();
+    const char* name = fbxNode->GetName();
+    if (name)
+    {
+        std::string id(name);
+        id.append("_Light");
+        light->setId(id);
+    }
+
+    fbxDouble3 color = fbxLight->Color.Get();
+    light->setColor((float)color[0], (float)color[1], (float)color[2]);
+    
+    switch (fbxLight->LightType.Get())
+    {
+    case KFbxLight::ePOINT:
+        light->setPointLight();
+        // TODO: range
+        break;
+    case KFbxLight::eDIRECTIONAL:
+        light->setDirectionalLight();
+        break;
+    case KFbxLight::eSPOT:
+        light->setSpotLight();
+        // TODO: range and angles
+        break;
+    default:
+        warning("Unknown light type in node.");
+        return;
+    }
+
+    _gamePlayFile.addLight(light);
+    node->setLight(light);
+}
+
+void FBXSceneEncoder::loadModel(KFbxNode* fbxNode, Node* node)
+{
+    KFbxMesh* fbxMesh = fbxNode->GetMesh();
+    if (!fbxMesh)
+    {
+        return;
+    }
+    if (fbxMesh->IsTriangleMesh())
+    {
+        Mesh* mesh = loadMesh(fbxMesh);
+        Model* model = new Model();
+        model->setMesh(mesh);
+        node->setModel(model);
+        loadSkin(fbxMesh, model);
+        if (model->getSkin())
+        {
+            // TODO: explain
+            node->resetTransformMatrix();
+        }
+    }
+}
+
+void FBXSceneEncoder::loadSkin(KFbxMesh* fbxMesh, Model* model)
+{
+    const int deformerCount = fbxMesh->GetDeformerCount();
+    for (int i = 0; i < deformerCount; ++i)
+    {
+        KFbxDeformer* deformer = fbxMesh->GetDeformer(i);
+        if (deformer->GetDeformerType() == KFbxDeformer::eSKIN)
+        {
+            KFbxSkin* fbxSkin = static_cast<KFbxSkin*>(deformer);
+
+            MeshSkin* skin = new MeshSkin();
+
+            std::vector<std::string> jointNames;
+            std::vector<Node*> joints;
+            std::vector<Matrix> bindPoses;
+
+            const int clusterCount = fbxSkin->GetClusterCount();
+            for (int j = 0; j < clusterCount; ++j)
+            {
+                KFbxCluster* cluster = fbxSkin->GetCluster(j);
+                assert(cluster);
+                KFbxNode* linkedNode = cluster->GetLink();
+                assert(linkedNode);
+                if (linkedNode->GetSkeleton())
+                {
+                    const char* jointName = linkedNode->GetName();
+                    assert(jointName);
+                    jointNames.push_back(jointName);
+                    Node* joint = loadNode(linkedNode);
+                    assert(joint);
+                    joints.push_back(joint);
+
+                    KFbxXMatrix matrix;
+                    cluster->GetTransformLinkMatrix(matrix);
+                    Matrix m;
+                    copyMatrix(matrix.Inverse(), m);
+                    bindPoses.push_back(m);
+                }
+            }
+            skin->setJointNames(jointNames);
+            skin->setJoints(joints);
+            skin->setBindPoses(bindPoses);
+            model->setSkin(skin);
+            break;
+        }
+    }
+}
+
+Mesh* FBXSceneEncoder::loadMesh(KFbxMesh* fbxMesh)
+{
+    // Check if this mesh has already been loaded.
+    Mesh* mesh = getMesh(fbxMesh->GetUniqueID());
+    if (mesh)
+    {
+        return mesh;
+    }
+    mesh = new Mesh();
+    // GamePlay requires that a mesh have a unique ID but KFbxMesh doesn't have a string ID.
+    const char* name = fbxMesh->GetNode()->GetName();
+    if (name)
+    {
+        std::string id(name);
+        id.append("_Mesh");
+        mesh->setId(id);
+    }
+
+    // The number of mesh parts is equal to the number of materials that affect this mesh.
+    // There is always at least one mesh part.
+    std::vector<MeshPart*> meshParts;
+    const int materialCount = fbxMesh->GetNode()->GetMaterialCount();
+    int meshPartSize = (materialCount > 0) ? materialCount : 1;
+    for (int i = 0; i < meshPartSize; ++i)
+    {
+        meshParts.push_back(new MeshPart());
+    }
+
+    // Find the blend weights and blend indices if this mesh is skinned.
+    std::vector<std::vector<Vector2> > weights;
+    bool hasSkin = loadBlendWeights(fbxMesh, weights);
+    
+    int vertexIndex = 0;
+    KFbxVector4* controlPoints = fbxMesh->GetControlPoints();
+    const int polygonCount = fbxMesh->GetPolygonCount();
+    for (int polyIndex = 0; polyIndex < polygonCount; ++polyIndex)
+    {
+        const int polygonSize = fbxMesh->GetPolygonSize(polyIndex);
+        for (int posInPoly = 0; posInPoly < polygonSize; ++posInPoly)
+        {
+            int controlPointIndex = fbxMesh->GetPolygonVertex(polyIndex, posInPoly);
+            Vertex vertex;
+
+            KFbxVector4& position = controlPoints[controlPointIndex];
+            vertex.position.x = (float)position[0];
+            vertex.position.y = (float)position[1];
+            vertex.position.z = (float)position[2];
+
+            loadTextureCoords(fbxMesh, polyIndex, posInPoly, &vertex);
+            loadNormal(fbxMesh, vertexIndex, &vertex);
+            loadTangent(fbxMesh, vertexIndex, &vertex);
+            loadBinormal(fbxMesh, vertexIndex, &vertex);
+            // TODO: loadDiffuseColors
+
+            if (hasSkin)
+            {
+                loadBlendData(weights[controlPointIndex], &vertex);
+            }
+
+            // Determine which mesh part this vertex index should be added to based on the material that affects it.
+            int meshPartIndex = 0;
+            const int elementMatrialCount = fbxMesh->GetElementMaterialCount();
+            for (int k = 0; k < elementMatrialCount; ++k)
+            {
+                KFbxGeometryElementMaterial* elementMaterial = fbxMesh->GetElementMaterial(k);
+                meshPartIndex = elementMaterial->GetIndexArray().GetAt(polyIndex);
+            }
+
+            // Add the vertex to the mesh if it hasn't already been added and find the vertex index.
+            unsigned int index;
+            if (mesh->contains(vertex))
+            {
+                index = mesh->getVertexIndex(vertex);
+            }
+            else
+            {
+                index = mesh->addVertex(vertex);
+            }
+            meshParts[meshPartIndex]->addIndex(index);
+            vertexIndex++;
+        }
+    }
+
+    const size_t meshpartsSize = meshParts.size();
+    for (size_t i = 0; i < meshpartsSize; ++i)
+    {
+        mesh->addMeshPart(meshParts[i]);
+    }
+
+    // The order that the vertex elements are add to the list matters.
+    // It should be the same order as how the Vertex data is written.
+
+    // Position
+    mesh->addVetexAttribute(POSITION, 3);
+
+    const Vertex& vertex = mesh->vertices[0];
+    // Normals
+    if (vertex.hasNormal)
+    {
+        mesh->addVetexAttribute(NORMAL, 3);
+    }
+    // Tangents
+    if (vertex.hasTangent)
+    {
+        mesh->addVetexAttribute(TANGENT, 3);
+    }
+    // Binormals
+    if (vertex.hasBinormal)
+    {
+        mesh->addVetexAttribute(BINORMAL, 3);
+    }
+    // Texture Coordinates
+    if (vertex.hasTexCoord)
+    {
+        mesh->addVetexAttribute(TEXCOORD0, 2);
+    }
+    // Diffuse Color
+    if (vertex.hasColor)
+    {
+        mesh->addVetexAttribute(COLOR, 3);
+    }
+    // Skinning BlendWeights BlendIndices
+    if (vertex.hasWeights)
+    {
+        mesh->addVetexAttribute(BLENDWEIGHTS, 4);
+        mesh->addVetexAttribute(BLENDINDICES, 4);
+    }
+
+    _gamePlayFile.addMesh(mesh);
+    saveMesh(fbxMesh->GetUniqueID(), mesh);
+    return mesh;
+}
+
+void FBXSceneEncoder::triangulateRecursive(KFbxNode* fbxNode)
+{
+    // Triangulate all NURBS, patch and mesh under this node recursively.
+    KFbxNodeAttribute* nodeAttribute = fbxNode->GetNodeAttribute();
+
+    if (nodeAttribute)
+    {
+        if (nodeAttribute->GetAttributeType() == KFbxNodeAttribute::eMESH ||
+            nodeAttribute->GetAttributeType() == KFbxNodeAttribute::eNURB ||
+            nodeAttribute->GetAttributeType() == KFbxNodeAttribute::eNURBS_SURFACE ||
+            nodeAttribute->GetAttributeType() == KFbxNodeAttribute::ePATCH)
+        {
+            KFbxGeometryConverter converter(fbxNode->GetFbxSdkManager());
+            converter.TriangulateInPlace(fbxNode);
+        }
+    }
+
+    const int childCount = fbxNode->GetChildCount();
+    for (int childIndex = 0; childIndex < childCount; ++childIndex)
+    {
+        triangulateRecursive(fbxNode->GetChild(childIndex));
+    }
+}
+
+void FBXSceneEncoder::warning(const std::string& message)
+{
+    printf("Warning: %s\n", message.c_str());
+}
+
+void FBXSceneEncoder::warning(const char* message)
+{
+    printf("Warning: %s\n", message);
+}
+
+////////////////////////////////////
+// Functions
+////////////////////////////////////
+
+float getAspectRatio(KFbxCamera* fbxCamera)
+{
+    return (float)fbxCamera->FilmAspectRatio.Get();
+    /*
+    KFbxCamera::ECameraAspectRatioMode camAspectRatioMode = fbxCamera->GetAspectRatioMode();
+    double aspectX = fbxCamera->AspectWidth.Get();
+    double aspectY = fbxCamera->AspectHeight.Get();
+    double aspectRatio = 1.333333;
+    switch ( camAspectRatioMode)
+    {
+    case KFbxCamera::eWINDOW_SIZE:
+        aspectRatio = aspectX / aspectY;
+        break;
+    case KFbxCamera::eFIXED_RATIO:
+        aspectRatio = aspectX;
+        break;
+    case KFbxCamera::eFIXED_RESOLUTION:
+        aspectRatio = aspectX / aspectY * fbxCamera->GetPixelRatio();
+        break;
+    case KFbxCamera::eFIXED_WIDTH:
+        aspectRatio = fbxCamera->GetPixelRatio() / aspectY;
+        break;
+    case KFbxCamera::eFIXED_HEIGHT:
+        aspectRatio = fbxCamera->GetPixelRatio() * aspectX;
+        break;
+    default:
+        break;
+    }
+    return (float)aspectRatio;
+    */
+}
+
+inline double vfov(double hfov, double aspect)
+{
+    static const double MATH_PI_180 = 0.01745329251994329576923690768489;
+    static const double MATH_180_PI = 57.295779513082320876798154814105;
+    return (2.0 * atan((aspect) * tan( (hfov * MATH_PI_180) * 0.5)) * MATH_180_PI);
+}
+
+float getFieldOfView(KFbxCamera* fbxCamera)
+{
+    double fieldOfViewX = 0.0;
+    double fieldOfViewY = 0.0;
+    double filmHeight = fbxCamera->GetApertureHeight();
+    double filmWidth = fbxCamera->GetApertureWidth() * fbxCamera->GetSqueezeRatio();
+    double apertureRatio = filmHeight / filmWidth;
+    if ( fbxCamera->GetApertureMode() == KFbxCamera::eVERTICAL)
+    {
+        fieldOfViewY = fbxCamera->FieldOfView.Get();
+    }
+    else if (fbxCamera->GetApertureMode() == KFbxCamera::eHORIZONTAL)
+    {
+        fieldOfViewX = fbxCamera->FieldOfView.Get();
+        fieldOfViewY = vfov( fieldOfViewX, apertureRatio);
+    }
+    else if (fbxCamera->GetApertureMode() == KFbxCamera::eFOCAL_LENGTH)
+    {
+        fieldOfViewX = fbxCamera->ComputeFieldOfView(fbxCamera->FocalLength.Get());
+        fieldOfViewY = vfov( fieldOfViewX, apertureRatio);
+    }
+    else if (fbxCamera->GetApertureMode() == KFbxCamera::eHORIZONTAL_AND_VERTICAL)
+    {
+        fieldOfViewY = fbxCamera->FieldOfViewY.Get();
+    }
+    else
+    {
+        fieldOfViewY = 45.0;
+    }
+    return (float)fieldOfViewY;
+}
+
+void loadTextureCoords(KFbxMesh* fbxMesh, int polyIndex, int posInPoly, Vertex* vertex)
+{
+    assert(fbxMesh && polyIndex >=0 && posInPoly >= 0);
+    if (fbxMesh->GetElementUVCount() > 0)
+    {
+        // Get only the first UV coordinates.
+        KFbxGeometryElementUV* leUV = fbxMesh->GetElementUV(0);
+        switch (leUV->GetMappingMode())
+        {
+        case KFbxGeometryElement::eBY_CONTROL_POINT:
+            switch (leUV->GetReferenceMode())
+            {
+            case KFbxGeometryElement::eDIRECT:
+                vertex->hasTexCoord = true;
+                vertex->texCoord.x = (float)leUV->GetDirectArray().GetAt(polyIndex)[0];
+                vertex->texCoord.y = (float)leUV->GetDirectArray().GetAt(polyIndex)[1];
+                break;
+            case KFbxGeometryElement::eINDEX_TO_DIRECT:
+                {
+                    int id = leUV->GetIndexArray().GetAt(polyIndex);
+                    vertex->hasTexCoord = true;
+                    vertex->texCoord.x = (float)leUV->GetDirectArray().GetAt(id)[0];
+                    vertex->texCoord.y = (float)leUV->GetDirectArray().GetAt(id)[1];
+                }
+                break;
+            default:
+                break;
+            }
+            break;
+        case KFbxGeometryElement::eBY_POLYGON_VERTEX:
+            {
+                int lTextureUVIndex = fbxMesh->GetTextureUVIndex(polyIndex, posInPoly);
+                switch (leUV->GetReferenceMode())
+                {
+                case KFbxGeometryElement::eDIRECT:
+                case KFbxGeometryElement::eINDEX_TO_DIRECT:
+                    vertex->hasTexCoord = true;
+                    vertex->texCoord.x = (float)leUV->GetDirectArray().GetAt(lTextureUVIndex)[0];
+                    vertex->texCoord.y = (float)leUV->GetDirectArray().GetAt(lTextureUVIndex)[1];
+                    break;
+                default:
+                    break;
+                }
+            }
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+void loadNormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex)
+{
+    if (fbxMesh->GetElementNormalCount() > 0)
+    {
+        // Get only the first
+        KFbxGeometryElementNormal* leNormal = fbxMesh->GetElementNormal(0);
+        if (leNormal->GetMappingMode() == KFbxGeometryElement::eBY_POLYGON_VERTEX)
+        {
+            switch (leNormal->GetReferenceMode())
+            {
+            case KFbxGeometryElement::eDIRECT:
+                {
+                    KFbxVector4 vec4 = leNormal->GetDirectArray().GetAt(vertexIndex);
+                    vertex->hasNormal = true;
+                    vertex->normal.x = (float)vec4[0];
+                    vertex->normal.y = (float)vec4[1];
+                    vertex->normal.z = (float)vec4[2];
+                }
+                break;
+            case KFbxGeometryElement::eINDEX_TO_DIRECT:
+                {
+                    int id = leNormal->GetIndexArray().GetAt(vertexIndex);
+                    KFbxVector4 vec4 = leNormal->GetDirectArray().GetAt(id);
+                    vertex->hasNormal = true;
+                    vertex->normal.x = (float)vec4[0];
+                    vertex->normal.y = (float)vec4[1];
+                    vertex->normal.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
+
+void loadTangent(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex)
+{
+    if (fbxMesh->GetElementTangentCount() > 0)
+    {
+        // Get only the first tangent
+        KFbxGeometryElementTangent* leTangent = fbxMesh->GetElementTangent(0);
+        if (leTangent->GetMappingMode() == KFbxGeometryElement::eBY_POLYGON_VERTEX)
+        {
+            switch (leTangent->GetReferenceMode())
+            {
+            case KFbxGeometryElement::eDIRECT:
+                {
+                    KFbxVector4 vec4 = leTangent->GetDirectArray().GetAt(vertexIndex);
+                    vertex->hasTangent = true;
+                    vertex->tangent.x = (float)vec4[0];
+                    vertex->tangent.y = (float)vec4[1];
+                    vertex->tangent.z = (float)vec4[2];
+                }
+                break;
+            case KFbxGeometryElement::eINDEX_TO_DIRECT:
+                {
+                    int id = leTangent->GetIndexArray().GetAt(vertexIndex);
+                    KFbxVector4 vec4 = leTangent->GetDirectArray().GetAt(id);
+                    vertex->hasTangent = true;
+                    vertex->tangent.x = (float)vec4[0];
+                    vertex->tangent.y = (float)vec4[1];
+                    vertex->tangent.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
+
+void loadBinormal(KFbxMesh* fbxMesh, int vertexIndex, Vertex* vertex)
+{
+    if (fbxMesh->GetElementBinormalCount() > 0)
+    {
+        // Get only the first binormal.
+        KFbxGeometryElementBinormal* leBinormal = fbxMesh->GetElementBinormal(0);
+        if (leBinormal->GetMappingMode() == KFbxGeometryElement::eBY_POLYGON_VERTEX)
+        {
+            switch (leBinormal->GetReferenceMode())
+            {
+            case KFbxGeometryElement::eDIRECT:
+                {
+                    KFbxVector4 vec4 = leBinormal->GetDirectArray().GetAt(vertexIndex);
+                    vertex->hasBinormal = true;
+                    vertex->binormal.x = (float)vec4[0];
+                    vertex->binormal.y = (float)vec4[1];
+                    vertex->binormal.z = (float)vec4[2];
+                }
+                break;
+            case KFbxGeometryElement::eINDEX_TO_DIRECT:
+                {
+                    int id = leBinormal->GetIndexArray().GetAt(vertexIndex);
+                    KFbxVector4 vec4 = leBinormal->GetDirectArray().GetAt(id);
+                    vertex->hasBinormal = true;
+                    vertex->binormal.x = (float)vec4[0];
+                    vertex->binormal.y = (float)vec4[1];
+                    vertex->binormal.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
+
+void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex)
+{
+    size_t size = vertexWeights.size();
+
+    if (size >= 1)
+    {
+        vertex->hasWeights= true;
+        vertex->blendIndices.x = vertexWeights[0].x;
+        vertex->blendWeights.x = vertexWeights[0].y;
+    }
+    if (size >= 2)
+    {
+        vertex->blendIndices.y = vertexWeights[1].x;
+        vertex->blendWeights.y = vertexWeights[1].y;
+    }
+    if (size >= 3)
+    {
+        vertex->blendIndices.z = vertexWeights[2].x;
+        vertex->blendWeights.z = vertexWeights[2].y;
+    }
+    if (size >= 4)
+    {
+        vertex->blendIndices.w = vertexWeights[3].x;
+        vertex->blendWeights.w = vertexWeights[3].y;
+    }
+    //vertex->normalizeBlendWeight();
+}
+
+bool loadBlendWeights(KFbxMesh* fbxMesh, std::vector<std::vector<Vector2> >& weights)
+{
+    assert(fbxMesh);
+    const int vertexCount = fbxMesh->GetControlPointsCount();
+
+    KFbxSkin* fbxSkin = NULL;
+    const int deformerCount = fbxMesh->GetDeformerCount();
+    for (int i = 0; i < deformerCount; ++i)
+    {
+        KFbxDeformer* deformer = fbxMesh->GetDeformer(i);
+        if (deformer->GetDeformerType() == KFbxDeformer::eSKIN)
+        {
+            fbxSkin = static_cast<KFbxSkin*>(deformer);
+            weights.resize(vertexCount);
+
+            const int clusterCount = fbxSkin->GetClusterCount();
+            for (int j = 0; j < clusterCount; ++j)
+            {
+                KFbxCluster* cluster = fbxSkin->GetCluster(j);
+                assert(cluster);
+                KFbxNode* linkedNode = cluster->GetLink();
+                assert(linkedNode);
+
+                const int vertexIndexCount = cluster->GetControlPointIndicesCount();
+                for (int k = 0; k < vertexIndexCount; ++k)
+                {
+                    int index = cluster->GetControlPointIndices()[k];
+                    if (index >= vertexCount)
+                    {
+                        continue;
+                    }
+
+                    double weight = cluster->GetControlPointWeights()[k];
+                    if (weight == 0.0)
+                    {
+                        continue;
+                    }
+                    weights[index].push_back(Vector2((float)j, (float)weight));
+                }
+            }
+            // Only the first skin deformer will be loaded.
+            // There probably won't be more than one.
+            break;
+        }
+    }
+    return fbxSkin != NULL;
+}
+
+void findMinMaxTime(KFbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate)
+{
+    KTime start, stop;
+    animCurve->GetTimeInterval(start, stop);
+    *startTime = std::min(*startTime, (float)start.GetMilliSeconds());
+    *stopTime = std::max(*stopTime, (float)stop.GetMilliSeconds());
+    *frameRate = std::max(*frameRate, (float)stop.GetFrameRate(KTime::eDEFAULT_MODE));
+}
+
+void appendKeyFrame(KFbxNode* fbxNode, float time, std::vector<float>* keyTimes, std::vector<float>* keyValues)
+{
+    KFbxXMatrix fbxMatrix;
+    Matrix matrix;
+    KTime kTime;
+    kTime.SetMilliSeconds((kLongLong)time);
+    fbxMatrix = fbxNode->EvaluateLocalTransform(kTime);
+    copyMatrix(fbxMatrix, matrix);
+
+    Vector3 scale;
+    Quaternion rotation;
+    Vector3 translation;
+    matrix.decompose(&scale, &rotation, &translation);
+
+    keyTimes->push_back(time);
+    keyValues->push_back(scale.x);
+    keyValues->push_back(scale.y);
+    keyValues->push_back(scale.z);
+    keyValues->push_back(rotation.x);
+    keyValues->push_back(rotation.y);
+    keyValues->push_back(rotation.z);
+    keyValues->push_back(rotation.w);
+    keyValues->push_back(translation.x);
+    keyValues->push_back(translation.y);
+    keyValues->push_back(translation.z);
+}
+
+void decompose(KFbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation)
+{
+    KFbxXMatrix fbxMatrix;
+    Matrix matrix;
+    KTime kTime;
+    kTime.SetMilliSeconds((kLongLong)time);
+    fbxMatrix = fbxNode->EvaluateLocalTransform(kTime);
+    copyMatrix(fbxMatrix, matrix);
+    matrix.decompose(scale, rotation, translation);
+}
+
+AnimationChannel* createAnimationChannel(KFbxNode* fbxNode, unsigned int targetAttrib, const std::vector<float>& keyTimes, const std::vector<float>& keyValues)
+{
+    AnimationChannel* channel = new AnimationChannel();
+    channel->setTargetId(fbxNode->GetName());
+    channel->setKeyTimes(keyTimes);
+    channel->setKeyValues(keyValues);
+    channel->setInterpolation(AnimationChannel::LINEAR);
+    channel->setTargetAttribute(targetAttrib);
+    return channel;
+}
+
+void addScaleChannel(Animation* animation, KFbxNode* fbxNode, float startTime, float stopTime)
+{
+    std::vector<float> keyTimes;
+    std::vector<float> keyValues;
+    Vector3 scale;
+    Quaternion rotation;
+    Vector3 translation;
+
+    decompose(fbxNode, startTime, &scale, &rotation, &translation);
+    keyTimes.push_back(startTime);
+    keyValues.push_back(scale.x);
+    keyValues.push_back(scale.y);
+    keyValues.push_back(scale.z);
+
+    decompose(fbxNode, stopTime, &scale, &rotation, &translation);
+    keyTimes.push_back(stopTime);
+    keyValues.push_back(scale.x);
+    keyValues.push_back(scale.y);
+    keyValues.push_back(scale.z);
+
+    AnimationChannel* channel = createAnimationChannel(fbxNode, Transform::ANIMATE_SCALE, keyTimes, keyValues);
+    animation->add(channel);
+}
+
+void addTranslateChannel(Animation* animation, KFbxNode* fbxNode, float startTime, float stopTime)
+{
+    std::vector<float> keyTimes;
+    std::vector<float> keyValues;
+    Vector3 scale;
+    Quaternion rotation;
+    Vector3 translation;
+
+    decompose(fbxNode, startTime, &scale, &rotation, &translation);
+    keyTimes.push_back(startTime);
+    keyValues.push_back(translation.x);
+    keyValues.push_back(translation.y);
+    keyValues.push_back(translation.z);
+
+    decompose(fbxNode, stopTime, &scale, &rotation, &translation);
+    keyTimes.push_back(stopTime);
+    keyValues.push_back(translation.x);
+    keyValues.push_back(translation.y);
+    keyValues.push_back(translation.z);
+
+    AnimationChannel* channel = createAnimationChannel(fbxNode, Transform::ANIMATE_TRANSLATE, keyTimes, keyValues);
+    animation->add(channel);
+}
+
+void copyMatrix(const KFbxMatrix& fbxMatrix, float* matrix)
+{
+    int i = 0;
+    for (int row = 0; row < 4; ++row)
+    {
+        for (int col = 0; col < 4; ++col)
+        {
+            matrix[i++] = (float)fbxMatrix.Get(row, col);
+        }
+    }
+}
+
+void copyMatrix(const KFbxMatrix& fbxMatrix, Matrix& matrix)
+{
+    int i = 0;
+    for (int row = 0; row < 4; ++row)
+    {
+        for (int col = 0; col < 4; ++col)
+        {
+            matrix.m[i++] = (float)fbxMatrix.Get(row, col);
+        }
+    }
+}
+
+#endif

+ 222 - 0
gameplay-encoder/src/FBXSceneEncoder.h

@@ -0,0 +1,222 @@
+#ifndef FBXSCENEEENCODER_H_
+#define FBXSCENEEENCODER_H_
+
+#ifdef USE_FBX
+
+#include <iostream>
+#include <list>
+#include <vector>
+#include <ctime>
+#ifdef WIN32
+    #pragma warning( disable : 4100 )
+    #pragma warning( disable : 4512 )
+#endif
+#include <fbxsdk.h>
+
+#include "Base.h"
+#include "StringUtil.h"
+#include "Object.h"
+#include "Node.h"
+#include "Camera.h"
+#include "Light.h"
+#include "Mesh.h"
+#include "MeshPart.h"
+#include "MeshSkin.h"
+#include "Model.h"
+#include "Scene.h"
+#include "Animation.h"
+#include "AnimationChannel.h"
+#include "Vertex.h"
+#include "Matrix.h"
+#include "Transform.h"
+#include "GPBFile.h"
+#include "EncoderArguments.h"
+
+using namespace gameplay;
+
+/**
+ * Class for binary encoding an FBX file.
+ */
+class FBXSceneEncoder
+{
+public:
+
+    static const unsigned int SCENE_SKIN_VERTEXINFLUENCES_MAX = 4;
+    
+    /**
+     * Constructor.
+     */
+    FBXSceneEncoder();
+
+    /**
+     * Destructor.
+     */
+    ~FBXSceneEncoder();
+    
+    /**
+     * Writes out encoded FBX file.
+     */
+    void write(const std::string& filepath, const EncoderArguments& arguments);
+
+private:
+
+    /**
+     * Loads the scene.
+     * 
+     * @param fbxScene The FBX scene to load.
+     */
+    void loadScene(KFbxScene* fbxScene);
+
+    /**
+     * Loads all of the animatiosn from the given FBX scene.
+     * 
+     * @param fbxScene The scene to load animations from.
+     * @param arguments The command line arguments passed to the encoder.
+     */
+    void loadAnimations(KFbxScene* fbxScene, const EncoderArguments& arguments);
+
+    /**
+     * Loads the animations from the given FBX animation layer recursively starting from fbxNode.
+     * 
+     * @param fbxAnimLayer The FBX animation layer to load from.
+     * @param fbxNode The node to start loading animations from.
+     * @param arguments The command line arguments passed to the encoder.
+     */
+    void loadAnimationLayer(KFbxAnimLayer* fbxAnimLayer, KFbxNode* fbxNode, const EncoderArguments& arguments);
+
+    /**
+     * Loads animation channels from the given node and adds the channels to the given animation.
+     * 
+     * @param pAnimLayer The FBX animation layer to load from.
+     * @param fbxNode The node to load animation channels from.
+     * @param animation The animation to add the channels to.
+     */
+    void loadAnimationChannels(KFbxAnimLayer* pAnimLayer, KFbxNode* fbxNode, Animation* animation);
+
+    /**
+     * Loads the bind shape for all mesh skins that have be loaded so far.
+     * 
+     * @param fbxScene The FBX scene to read the bind shapes from.
+     */
+    void loadBindShapes(KFbxScene* fbxScene);
+
+    /**
+     * Loads the camera from the given FBX node and adds to it to the given GamePlay node.
+     * 
+     * @param fbxNode The FBX node to load from.
+     * @param node The GamePlay node to add to.
+     */
+    void loadCamera(KFbxNode* fbxNode, Node* node);
+
+    /**
+     * Loads the light from the given FBX node and adds to it to the given GamePlay node.
+     * 
+     * @param fbxNode The FBX node to load from.
+     * @param node The GamePlay node to add to.
+     */
+    void loadLight(KFbxNode* fbxNode, Node* node);
+    
+    /**
+     * Loads the model from the given FBX node and adds to it to the given GamePlay node.
+     *
+     * @param fbxNode The FBX node to load from.
+     * @param node The GamePlay node to add to.
+     */
+    void loadModel(KFbxNode* fbxNode, Node* node);
+
+    /**
+     * Loads the mesh skin from the given FBX mesh and adds it to the given GamePlay model.
+     *
+     * @param fbxMesh The FBX mesh to load the skin from.
+     * @param model The model to add the skin to.
+     */
+    void loadSkin(KFbxMesh* fbxMesh, Model* model);
+    
+    /**
+     * Loads the FBX Node and creates a GamePlay Node.
+     * 
+     * @param fbxNode The FBX Node to load.
+     * 
+     * @return The newly created Node or NULL if the node could not be loaded.
+     */
+    Node* loadNode(KFbxNode* fbxNode);
+    
+    /**
+     * Loads the FbxMesh and returns a GamePlay mesh.
+     * If the fbxMesh has already been loaded then the same instance of mesh will be returned.
+     * 
+     * @param fbxMesh The FBX Mesh to load.
+     * 
+     * @return The GamePlay mesh that was loaded from the FBX Mesh.
+     */
+    Mesh* loadMesh(KFbxMesh* fbxMesh);
+
+    /**
+     * Gets the Mesh that was saved with the given ID. Returns NULL if a match is not found.
+     * 
+     * @param meshId The ID of the FbxMesh to search for.
+     * 
+     * @return The mesh that was saved with the ID or NULL if none was found.
+     */
+    Mesh* getMesh(size_t meshId);
+
+    /**
+     * Saves the Mesh with the given id.
+     * 
+     * @param meshId The ID of the FbxMesh to use as a key.
+     * @param mesh The mesh to save.
+     */
+    void saveMesh(size_t meshId, Mesh* mesh);
+    
+    /**
+     * Prints a message.
+     *
+     * @param str The string to print.
+     */
+    void print(const char* str);
+
+    /**
+     * Transforms the GamePlay Node using the transform data from the FBX Node.
+     * 
+     * @param fbxNode The FBX node to get the transfrom data from
+     * @param node The GamePlay Node to copy the transform to.
+     */
+    void transformNode(KFbxNode* fbxNode, Node* node);
+
+    /**
+     * Recursively triangules the meshes starting from the given node.
+     * 
+     * @param fbxNode The node to start triangulating from.
+     */
+    static void triangulateRecursive(KFbxNode* fbxNode);
+
+    /**
+     * Prints a warning message.
+     */
+    static void warning(const std::string& message);
+
+    /**
+     * Prints a warning message.
+     */
+    static void warning(const char* message);
+
+private:
+
+    /**
+     * The GamePlay file that is populated while reading the FBX file.
+     */
+    GPBFile _gamePlayFile;
+
+    /**
+     * The collection of meshes for the purpose of making sure that the same model is not loaded twice. (Mesh instancing)
+     */
+    std::map<size_t, Mesh*> _meshes;
+
+    /**
+     * The animation that channels should be added to it the user is using the -groupAnimation command line argument. May be NULL.
+     */
+    Animation* _groupAnimation;
+};
+
+#endif
+#endif

+ 39 - 3
gameplay-encoder/src/FileIO.cpp

@@ -55,7 +55,7 @@ void write(float value, FILE* file)
 }
 void write(const float* values, int length, FILE* file)
 {
-    for (int i = 0; i < length; i++)
+    for (int i = 0; i < length; ++i)
     {
         write(values[i], file);
     }
@@ -77,8 +77,8 @@ void writeZero(FILE* file)
 
 void fprintfElement(FILE* file, const char* elementName, const float values[], int length)
 {
-    fprintf(file, "<%s>", elementName);
-    for (int i = 0; i < length; i++)
+    fprintf(file, "<%s count=\"%d\">", elementName, length);
+    for (int i = 0; i < length; ++i)
     {
         fprintf(file, "%f ", values[i]);
     }
@@ -144,4 +144,40 @@ void skipUint(FILE* file)
     fseek(file, sizeof(unsigned int), SEEK_CUR);
 }
 
+void writeVectorBinary(const Vector2& v, FILE* file)
+{
+    write(v.x, file);
+    write(v.y, file);
+}
+
+void writeVectorText(const Vector2& v, FILE* file)
+{
+    fprintf(file, "%f %f\n", v.x, v.y);
+}
+
+void writeVectorBinary(const Vector3& v, FILE* file)
+{
+    write(v.x, file);
+    write(v.y, file);
+    write(v.z, file);
+}
+
+void writeVectorText(const Vector3& v, FILE* file)
+{
+    fprintf(file, "%f %f %f\n", v.x, v.y, v.z);
+}
+
+void writeVectorBinary(const Vector4& v, FILE* file)
+{
+    write(v.x, file);
+    write(v.y, file);
+    write(v.z, file);
+    write(v.w, file);
+}
+
+void writeVectorText(const Vector4& v, FILE* file)
+{
+    fprintf(file, "%f %f %f %f\n", v.x, v.y, v.z, v.w);
+}
+
 }

+ 30 - 8
gameplay-encoder/src/FileIO.h

@@ -1,11 +1,17 @@
 #ifndef FILEIO_H_
 #define FILEIO_H_
 
+
+#include "Vector2.h"
+#include "Vector3.h"
+#include "Vector4.h"
+
 namespace gameplay
 {
 
 /**
  * Writes an XML element to the specified file stream.
+ * 
  * @param file Pointer to a FILE object that identifies the stream.
  * @param elementName Name of the XML element to write.
  * @param value Value to write.
@@ -20,9 +26,9 @@ void fprintfElement(FILE* file, const char* elementName, const float values[], i
 template <class T>
 void fprintfElement(FILE* file, const char* format, const char* elementName, std::vector<T> list)
 {
-    fprintf(file, "<%s>", elementName);
+    fprintf(file, "<%s count=\"%lu\">", elementName, list.size());
     typename std::vector<T>::const_iterator i;
-    for (i = list.begin(); i != list.end(); i++)
+    for (i = list.begin(); i != list.end(); ++i)
     {
         fprintf(file, format, *i);
     }
@@ -32,9 +38,9 @@ void fprintfElement(FILE* file, const char* format, const char* elementName, std
 template <class T>
 void fprintfElement(FILE* file, const char* format, const char* elementName, std::list<T> list)
 {
-    fprintf(file, "<%s>", elementName);
+    fprintf(file, "<%s count=\"%lu\">", elementName, list.size());
     typename std::list<T>::const_iterator i;
-    for (i = list.begin(); i != list.end(); i++)
+    for (i = list.begin(); i != list.end(); ++i)
     {
         fprintf(file, format, *i);
     }
@@ -45,6 +51,7 @@ void fprintfMatrix4f(FILE* file, const float* m);
 
 /**
  * Writes binary data to the given file stream.
+ * 
  * @param value The value to be written
  * @param file The binary file stream.
  */
@@ -57,6 +64,7 @@ void write(unsigned short value, FILE* file);
 void write(bool value, FILE* file);
 void write(float value, FILE* file);
 void write(const float* values, int length, FILE* file);
+
 /**
  * Writes the length of the string and the string bytes to the binary file stream.
  */
@@ -66,6 +74,7 @@ void writeZero(FILE* file);
 
 /**
  * Writes the length of the list and writes each element value to the binary file stream.
+ * 
  * @param list The list to write.
  * @param file The binary file stream.
  */
@@ -76,7 +85,7 @@ void write(std::list<T> list, FILE* file)
     write(list.size(), file);
     // Then write each element
     typename std::list<T>::const_iterator i;
-    for (i = list.begin(); i != list.end(); i++)
+    for (i = list.begin(); i != list.end(); ++i)
     {
         write(*i, file);
     }
@@ -84,6 +93,7 @@ void write(std::list<T> list, FILE* file)
 
 /**
  * Writes the length of the vector and writes each element value to the binary file stream.
+ * 
  * @param vector The vector to write.
  * @param file The binary file stream.
  */
@@ -94,23 +104,35 @@ void write(std::vector<T> vector, FILE* file)
     write(vector.size(), file);
     // Then write each element
     typename std::vector<T>::const_iterator i;
-    for (i = vector.begin(); i != vector.end(); i++)
+    for (i = vector.begin(); i != vector.end(); ++i)
     {
         write(*i, file);
     }
 }
 
-
 /**
  * Skips over the string at the current file stream offset by moving the file position.
  * Assumes the current position points to the unsigned int length of the string.
  * The string is assumed to be a char array.
- * @param The file stream.
+ * 
+ * @param file The file stream.
  */
 void skipString(FILE* file);
 
 void skipUint(FILE* file);
 
+void writeVectorBinary(const Vector2& v, FILE* file);
+
+void writeVectorText(const Vector2& v, FILE* file);
+
+void writeVectorBinary(const Vector3& v, FILE* file);
+
+void writeVectorText(const Vector3& v, FILE* file);
+
+void writeVectorBinary(const Vector4& v, FILE* file);
+
+void writeVectorText(const Vector4& v, FILE* file);
+
 }
 
 #endif

+ 2 - 2
gameplay-encoder/src/GPBDecoder.cpp

@@ -43,7 +43,7 @@ bool GPBDecoder::validateHeading()
     const char identifier[] = { '«', 'G', 'P', 'B', '»', '\r', '\n', '\x1A', '\n' };
 
     char heading[HEADING_SIZE];
-    for (size_t i = 0; i < HEADING_SIZE; i++)
+    for (size_t i = 0; i < HEADING_SIZE; ++i)
     {
         if (heading[i] != identifier[i])
         {
@@ -64,7 +64,7 @@ void GPBDecoder::readRefs()
     // read number of refs
     unsigned int refCount;
     assert(read(&refCount));
-    for (size_t i = 0; i < refCount; i++)
+    for (size_t i = 0; i < refCount; ++i)
     {
         readRef();
     }

+ 18 - 10
gameplay-encoder/src/GPBFile.cpp

@@ -30,21 +30,21 @@ void GPBFile::saveBinary(const std::string& filepath)
     fwrite(identifier, 1, sizeof(identifier), _file);
 
     // version
-    fwrite(VERSION, 1, sizeof(VERSION), _file);
+    fwrite(GPB_VERSION, 1, sizeof(GPB_VERSION), _file);
 
     // write refs
     _refTable.writeBinary(_file);
 
     // meshes
     write(_geometry.size(), _file);
-    for (std::list<Mesh*>::const_iterator i = _geometry.begin(); i != _geometry.end(); i++)
+    for (std::list<Mesh*>::const_iterator i = _geometry.begin(); i != _geometry.end(); ++i)
     {
         (*i)->writeBinary(_file);
     }
 
     // Objects
     write(_objects.size(), _file);
-    for (std::list<Object*>::const_iterator i = _objects.begin(); i != _objects.end(); i++)
+    for (std::list<Object*>::const_iterator i = _objects.begin(); i != _objects.end(); ++i)
     {
         (*i)->writeBinary(_file);
     }
@@ -64,13 +64,13 @@ void GPBFile::saveText(const std::string& filepath)
     _refTable.writeText(_file);
 
     // meshes
-    for (std::list<Mesh*>::const_iterator i = _geometry.begin(); i != _geometry.end(); i++)
+    for (std::list<Mesh*>::const_iterator i = _geometry.begin(); i != _geometry.end(); ++i)
     {
         (*i)->writeText(_file);
     }
 
     // Objects
-    for (std::list<Object*>::const_iterator i = _objects.begin(); i != _objects.end(); i++)
+    for (std::list<Object*>::const_iterator i = _objects.begin(); i != _objects.end(); ++i)
     {
         (*i)->writeText(_file);
     }
@@ -155,8 +155,10 @@ bool GPBFile::idExists(const std::string& id)
 
 Camera* GPBFile::getCamera(const char* id)
 {
+    if (!id)
+        return NULL;
     // TODO: O(n) search is not ideal
-    for (std::list<Camera*>::const_iterator i = _cameras.begin(); i != _cameras.end(); i++)
+    for (std::list<Camera*>::const_iterator i = _cameras.begin(); i != _cameras.end(); ++i)
     {
         const std::string& _id = (*i)->getId();
         if (_id.length() > 0 && strncmp(id, _id.c_str(), 255) == 0)
@@ -169,8 +171,10 @@ Camera* GPBFile::getCamera(const char* id)
 
 Light* GPBFile::getLight(const char* id)
 {
+    if (!id)
+        return NULL;
     // TODO: O(n) search is not ideal
-    for (std::list<Light*>::const_iterator i = _lights.begin(); i != _lights.end(); i++)
+    for (std::list<Light*>::const_iterator i = _lights.begin(); i != _lights.end(); ++i)
     {
         const std::string& _id = (*i)->getId();
         if (_id.length() > 0 && strncmp(id, _id.c_str(), 255) == 0)
@@ -183,8 +187,10 @@ Light* GPBFile::getLight(const char* id)
 
 Mesh* GPBFile::getMesh(const char* id)
 {
+    if (!id)
+        return NULL;
     // TODO: O(n) search is not ideal
-    for (std::list<Mesh*>::const_iterator i = _geometry.begin(); i != _geometry.end(); i++)
+    for (std::list<Mesh*>::const_iterator i = _geometry.begin(); i != _geometry.end(); ++i)
     {
         const std::string& _id = (*i)->getId();
         if (_id.length() > 0 && strncmp(id, _id.c_str(), 255) == 0)
@@ -197,8 +203,10 @@ Mesh* GPBFile::getMesh(const char* id)
 
 Node* GPBFile::getNode(const char* id)
 {
+    if (!id)
+        return NULL;
     // TODO: O(n) search is not ideal
-    for (std::list<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); i++)
+    for (std::list<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i)
     {
         const std::string& _id = (*i)->getId();
         if (_id.length() > 0 && strncmp(id, _id.c_str(), 255) == 0)
@@ -217,7 +225,7 @@ Animations* GPBFile::getAnimations()
 void GPBFile::adjust()
 {
     // calculate the ambient color for each scene
-    for (std::list<Object*>::iterator i = _objects.begin(); i != _objects.end(); i++)
+    for (std::list<Object*>::iterator i = _objects.begin(); i != _objects.end(); ++i)
     {
         Object* obj = *i;
         if (obj->getTypeId() == Object::SCENE_ID)

+ 1 - 1
gameplay-encoder/src/GPBFile.h

@@ -21,7 +21,7 @@ namespace gameplay
  * Increment the version number when making a change that break binary compatibility.
  * [0] is major, [1] is minor.
  */
-const unsigned char VERSION[2] = {1, 1};
+const unsigned char GPB_VERSION[2] = {1, 1};
 
 /**
  * The GamePlay Binary file class handles writing the GamePlay Binary file.

+ 0 - 55
gameplay-encoder/src/LightInstance.cpp

@@ -1,55 +0,0 @@
-#include "Base.h"
-#include "LightInstance.h"
-
-namespace gameplay
-{
-
-LightInstance::LightInstance(void) : _ref(NULL)
-{
-
-}
-
-LightInstance::~LightInstance(void)
-{
-}
-
-unsigned int LightInstance::getTypeId(void) const
-{
-    return LIGHTINSTANCE_ID;
-}
-const char* LightInstance::getElementName(void) const
-{
-    return "LightInstance";
-}
-
-void LightInstance::writeBinary(FILE* file)
-{
-    if (_ref != NULL)
-    {
-        _ref->writeBinary(file);
-    }
-}
-void LightInstance::writeText(FILE* file)
-{
-    if (_ref != NULL)
-    {
-        _ref->writeText(file);
-    }
-}
-
-Light* LightInstance::getLight() const
-{
-    return _ref;
-}
-
-void LightInstance::setLight(Light* light)
-{
-    _ref = light;
-}
-
-bool LightInstance::isAmbient() const
-{
-    return _ref != NULL && _ref->isAmbient();
-}
-
-}

+ 0 - 42
gameplay-encoder/src/LightInstance.h

@@ -1,42 +0,0 @@
-#ifndef LIGHTINSTANCE_H_
-#define LIGHTINSTANCE_H_
-
-#include "Object.h"
-#include "Light.h"
-
-namespace gameplay
-{
-
-class LightInstance : public Object
-{
-public:
-
-    /**
-     * Constructor.
-     */
-    LightInstance(void);
-
-    /**
-     * Destructor.
-     */
-    virtual ~LightInstance(void);
-
-    virtual unsigned int getTypeId(void) const;
-    virtual const char* getElementName(void) const;
-    virtual void writeBinary(FILE* file);
-    virtual void writeText(FILE* file);
-
-    Light* getLight() const;
-    void setLight(Light* light);
-
-    bool isAmbient() const;
-
-private:
-
-    Light* _ref;
-
-};
-
-}
-
-#endif

+ 9 - 10
gameplay-encoder/src/Mesh.cpp

@@ -48,7 +48,7 @@ void Mesh::writeBinaryVertices(FILE* file)
         write(vertices.size() * vertex.byteSize(), file); // (vertex count) * (vertex size)
 
         // for each vertex
-        for (std::vector<Vertex>::const_iterator i = vertices.begin(); i != vertices.end(); i++)
+        for (std::vector<Vertex>::const_iterator i = vertices.begin(); i != vertices.end(); ++i)
         {
             // Write this vertex
             i->writeBinary(file);
@@ -83,7 +83,7 @@ void Mesh::writeText(FILE* file)
 
     // for each Vertex
     fprintf(file, "<vertices count=\"%lu\">\n", vertices.size());
-    for (std::vector<Vertex>::iterator i = vertices.begin(); i != vertices.end(); i++)
+    for (std::vector<Vertex>::iterator i = vertices.begin(); i != vertices.end(); ++i)
     {
         i->writeText(file);
     }
@@ -93,19 +93,19 @@ void Mesh::writeText(FILE* file)
     computeBounds();
     fprintf(file, "<bounds>\n");
     fprintf(file, "<min>\n");
-    bounds.min.writeText(file);
+    writeVectorText(bounds.min, file);
     fprintf(file, "</min>\n");
     fprintf(file, "<max>\n");
-    bounds.max.writeText(file);
+    writeVectorText(bounds.max, file);
     fprintf(file, "</max>\n");
     fprintf(file, "<center>\n");
-    bounds.center.writeText(file);
+    writeVectorText(bounds.center, file);
     fprintf(file, "</center>\n");
     fprintf(file, "<radius>%f</radius>\n", bounds.radius);
     fprintf(file, "</bounds>\n");
 
     // for each MeshPart
-    for (std::vector<MeshPart*>::iterator i = parts.begin(); i != parts.end(); i++)
+    for (std::vector<MeshPart*>::iterator i = parts.begin(); i != parts.end(); ++i)
     {
         (*i)->writeText(file);
     }
@@ -165,7 +165,6 @@ unsigned int Mesh::getVertexIndex(const Vertex& vertex)
 {
     std::map<Vertex,unsigned int>::iterator it;
     it = vertexLookupTable.find(vertex);
-    // TODO: Remove it from the map because we are going to delete it
     return it->second;
 }
 
@@ -185,7 +184,7 @@ void Mesh::computeBounds()
     bounds.center.x = bounds.center.y = bounds.center.z = 0.0f;
     bounds.radius = 0.0f;
 
-    for (std::vector<Vertex>::const_iterator i = vertices.begin(); i != vertices.end(); i++)
+    for (std::vector<Vertex>::const_iterator i = vertices.begin(); i != vertices.end(); ++i)
     {
         // Update min/max for this vertex
         if (i->position.x < bounds.min.x)
@@ -208,9 +207,9 @@ void Mesh::computeBounds()
 
     // Compute radius by looping through all points again and finding the max
     // distance between the center point and each vertex position
-    for (std::vector<Vertex>::const_iterator i = vertices.begin(); i != vertices.end(); i++)
+    for (std::vector<Vertex>::const_iterator i = vertices.begin(); i != vertices.end(); ++i)
     {
-        float d = Vector3::distanceSquared(bounds.center, i->position);
+        float d = bounds.center.distanceSquared(i->position);
         if (d > bounds.radius)
         {
             bounds.radius = d;

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

@@ -32,7 +32,7 @@ void MeshPart::writeBinary(FILE* file)
     // write the number of bytes
     write(indicesByteSize(), file);
     // for each index
-    for (std::vector<unsigned int>::const_iterator i = _indices.begin(); i != _indices.end(); i++)
+    for (std::vector<unsigned int>::const_iterator i = _indices.begin(); i != _indices.end(); ++i)
     {
         writeBinaryIndex(*i, file);
     }

+ 4 - 3
gameplay-encoder/src/MeshSkin.cpp

@@ -7,6 +7,7 @@
 #include "Animations.h"
 #include "Transform.h"
 #include "../../gameplay/src/Curve.h"
+#include "Matrix.h"
 
 namespace gameplay
 {
@@ -14,7 +15,7 @@ namespace gameplay
 MeshSkin::MeshSkin(void) :
     _vertexInfluenceCount(0)
 {
-    setIdentityMatrix(_bindShape);
+    Matrix::setIdentity(_bindShape);
 }
 
 MeshSkin::~MeshSkin(void)
@@ -87,7 +88,7 @@ void MeshSkin::writeText(FILE* file)
 
 void MeshSkin::setBindShape(const float data[])
 {
-    for (int i = 0; i < 16; i++)
+    for (int i = 0; i < 16; ++i)
     {
         _bindShape[i] = data[i];
     }
@@ -115,7 +116,7 @@ void MeshSkin::setJoints(const std::vector<Node*>& list)
 
 void MeshSkin::setBindPoses(std::vector<Matrix>& list)
 {
-    for (std::vector<Matrix>::iterator i = list.begin(); i != list.end(); i++)
+    for (std::vector<Matrix>::iterator i = list.begin(); i != list.end(); ++i)
     {
         _bindPoses.push_back(*i);
     }

+ 14 - 15
gameplay-encoder/src/Node.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "Node.h"
+#include "Matrix.h"
 
 #define NODE 1
 #define JOINT 2
@@ -13,7 +14,6 @@ Node::Node(void) :
     _firstChild(NULL), _lastChild(NULL), _parent(NULL),
     _camera(NULL), _light(NULL), _model(NULL), _joint(false)
 {
-    setIdentityMatrix(_transform.m);
 }
 
 Node::~Node(void)
@@ -220,14 +220,16 @@ Node* Node::getParent() const
     return _parent;
 }
 
-void Node::setCameraInstance(CameraInstance* cameraInstance)
+void Node::setCamera(Camera* camera)
 {
-    _camera = cameraInstance;
+    _camera = camera;
 }
-void Node::setLightInstance(LightInstance* lightInstance)
+
+void Node::setLight(Light* light)
 {
-    _light = lightInstance;
+    _light = light;
 }
+
 void Node::setModel(Model* model)
 {
     _model = model;
@@ -257,6 +259,11 @@ const Matrix& Node::getWorldMatrix() const
     return _worldTransform;
 }
 
+void Node::resetTransformMatrix()
+{
+    Matrix::setIdentity(_transform.m);
+}
+
 void Node::setIsJoint(bool value)
 {
     _joint = value;
@@ -269,20 +276,12 @@ bool Node::isJoint()
 
 Camera* Node::getCamera() const
 {
-    if (_camera)
-    {
-        return _camera->getCamera();
-    }
-    return NULL;
+    return _camera;
 }
 
 Light* Node::getLight() const
 {
-    if (_light)
-    {
-        return _light->getLight();
-    }
-    return NULL;
+    return _light;
 }
 
 Model* Node::getModel() const

+ 11 - 6
gameplay-encoder/src/Node.h

@@ -2,8 +2,8 @@
 #define NODE_H_
 
 #include "Object.h"
-#include "CameraInstance.h"
-#include "LightInstance.h"
+#include "Camera.h"
+#include "Light.h"
 #include "Model.h"
 
 namespace gameplay
@@ -138,8 +138,13 @@ public:
      */
     const Matrix& getWorldMatrix() const;
 
-    void setCameraInstance(CameraInstance* cameraInstance);
-    void setLightInstance(LightInstance* lightInstance);
+    /*
+     * Resets the node's transform matrix to the identity matrix.
+     */
+    void resetTransformMatrix();
+
+    void setCamera(Camera* camera);
+    void setLight(Light* light);
     void setModel(Model* model);
 
     /**
@@ -176,8 +181,8 @@ private:
     Node* _lastChild;
     Node* _parent;
 
-    CameraInstance* _camera;
-    LightInstance* _light;
+    Camera* _camera;
+    Light* _light;
     Model* _model;
 
     bool _joint;

+ 2 - 4
gameplay-encoder/src/Object.h

@@ -22,8 +22,6 @@ public:
         ANIMATION_ID = 4,
         ANIMATIONCHANNEL_ID = 5,
         NODEINSTANCE_ID = 8,
-        CAMERAINSTANCE_ID = 9,
-        LIGHTINSTANCE_ID = 10,
         MODEL_ID = 11,
         MATERIAL_ID = 16,
         EFFECT_ID = 17,
@@ -113,7 +111,7 @@ public:
         write(list.size(), file);
         // Then write each element
         typename std::list<T>::const_iterator i;
-        for (i = list.begin(); i != list.end(); i++)
+        for (i = list.begin(); i != list.end(); ++i)
         {
             (*i)->writeBinary(file);
         }
@@ -129,7 +127,7 @@ public:
         write(vector.size(), file);
         // Then write each element
         typename std::vector<T>::const_iterator i;
-        for (i = vector.begin(); i != vector.end(); i++)
+        for (i = vector.begin(); i != vector.end(); ++i)
         {
             (*i)->writeBinary(file);
         }

+ 3 - 3
gameplay-encoder/src/ReferenceTable.cpp

@@ -31,7 +31,7 @@ Object* ReferenceTable::get(const std::string& xref)
 void ReferenceTable::writeBinary(FILE* file)
 {
     write(_table.size(), file);
-    for ( std::map<std::string, Reference>::iterator i=_table.begin() ; i != _table.end(); i++ )
+    for ( std::map<std::string, Reference>::iterator i=_table.begin() ; i != _table.end(); ++i)
     {
         i->second.writeBinary(file);
     }
@@ -40,7 +40,7 @@ void ReferenceTable::writeBinary(FILE* file)
 void ReferenceTable::writeText(FILE* file)
 {
     fprintf(file, "<RefTable>\n");
-    for ( std::map<std::string, Reference>::iterator i=_table.begin() ; i != _table.end(); i++ )
+    for ( std::map<std::string, Reference>::iterator i=_table.begin() ; i != _table.end(); ++i)
     {
         i->second.writeText(file);
     }
@@ -49,7 +49,7 @@ void ReferenceTable::writeText(FILE* file)
 
 void ReferenceTable::updateOffsets(FILE* file)
 {
-    for (std::map<std::string, Reference>::iterator i = _table.begin(); i != _table.end(); i++)
+    for (std::map<std::string, Reference>::iterator i = _table.begin(); i != _table.end(); ++i)
     {
         Reference& ref = i->second;
         ref.updateOffset(file);

+ 10 - 3
gameplay-encoder/src/Scene.cpp

@@ -43,7 +43,7 @@ void Scene::writeBinary(FILE* file)
 void Scene::writeText(FILE* file)
 {
     fprintElementStart(file);
-    for (std::list<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); i++)
+    for (std::list<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i)
     {
         (*i)->writeText(file);
     }
@@ -67,7 +67,7 @@ void Scene::setActiveCameraNode(Node* node)
 
 Node* Scene::getFirstCameraNode() const
 {
-    for (std::list<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); i++)
+    for (std::list<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i)
     {
         Node* n = (*i)->getFirstCameraNode();
         if (n)
@@ -81,7 +81,7 @@ Node* Scene::getFirstCameraNode() const
 void Scene::calcAmbientColor()
 {
     float values[3] = {0.0f, 0.0f, 0.0f};
-    for (std::list<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); i++)
+    for (std::list<Node*>::const_iterator i = _nodes.begin(); i != _nodes.end(); ++i)
     {
         calcAmbientColor(*i, values);
     }
@@ -91,6 +91,13 @@ void Scene::calcAmbientColor()
     _ambientColor[2] = std::min(values[2], 1.0f);
 }
 
+void Scene::setAmbientColor(float red, float green, float blue)
+{
+    _ambientColor[0] = red;
+    _ambientColor[1] = green;
+    _ambientColor[2] = blue;
+}
+
 void Scene::calcAmbientColor(const Node* node, float* values) const
 {
     if (!node)

+ 5 - 0
gameplay-encoder/src/Scene.h

@@ -50,6 +50,11 @@ public:
      */
     void calcAmbientColor();
 
+    /**
+     * Sets the scene's ambient color.
+     */
+    void setAmbientColor(float red, float green, float blue);
+
 private:
 
     /**

+ 20 - 1
gameplay-encoder/src/StringUtil.cpp

@@ -89,7 +89,7 @@ bool equalsIgnoreCase(const std::string& a, const char* b)
     {
         return false;
     }
-    for (size_t i = 0; i < bLength; i++)
+    for (size_t i = 0; i < bLength; ++i)
     {
         if (lowercase(a[i]) != lowercase(b[i]))
         {
@@ -99,4 +99,23 @@ bool equalsIgnoreCase(const std::string& a, const char* b)
     return true;
 }
 
+std::string getFilenameFromFilePath(const std::string& filepath)
+{
+    if (filepath.find_last_of("/") != std::string::npos)
+    {
+        return filepath.substr(filepath.find_last_of("/")+1);
+    }
+    return "";
+}
+
+
+std::string getFilenameNoExt(const std::string& filename)
+{
+    if (filename.find_last_of(".") != std::string::npos)
+    {
+        return filename.substr(0, filename.find_last_of("."));
+    }
+    return filename;
+}
+
 }

+ 4 - 0
gameplay-encoder/src/StringUtil.h

@@ -17,6 +17,10 @@ bool equals(const std::string& a, const char* b);
  */
 bool equalsIgnoreCase(const std::string& a, const char* b);
 
+std::string getFilenameFromFilePath(const std::string& filepath);
+
+std::string getFilenameNoExt(const std::string& filename);
+
 }
 
 #endif

+ 5 - 5
gameplay-encoder/src/TTFFontEncoder.cpp

@@ -10,7 +10,7 @@ void drawBitmap(unsigned char* dstBitmap, int x, int y, int dstWidth, unsigned c
     // offset dst bitmap by x,y.
     dstBitmap +=  (x + (y * dstWidth));
 
-    for (int i = 0; i < srcHeight; i++)
+    for (int i = 0; i < srcHeight; ++i)
     {
         memcpy(dstBitmap, (const void*)srcBitmap, srcWidth);
         srcBitmap += srcWidth;
@@ -91,7 +91,7 @@ int writeFont(const char* filename, unsigned int fontSize, const char* id, bool
     int rowSize = 0; // Stores the total number of rows required to all glyphs.
     
     // Find the width of the image.
-    for (unsigned char ascii = START_INDEX; ascii < END_INDEX; ascii++)
+    for (unsigned char ascii = START_INDEX; ascii < END_INDEX; ++ascii)
     {
         // Load glyph image into the slot (erase previous one)
         error = FT_Load_Char(face, ascii, FT_LOAD_RENDER);
@@ -136,7 +136,7 @@ int writeFont(const char* filename, unsigned int fontSize, const char* id, bool
 
         // Find out the squared texture size that would fit all the require font glyphs.
         i = 0;
-        for (unsigned char ascii = START_INDEX; ascii < END_INDEX; ascii++)
+        for (unsigned char ascii = START_INDEX; ascii < END_INDEX; ++ascii)
         {
             // Load glyph image into the slot (erase the previous one).
             error = FT_Load_Char(face, ascii, FT_LOAD_RENDER);
@@ -203,7 +203,7 @@ int writeFont(const char* filename, unsigned int fontSize, const char* id, bool
     penY = 0;
     row = 0;
     i = 0;
-    for (unsigned char ascii = START_INDEX; ascii < END_INDEX; ascii++)
+    for (unsigned char ascii = START_INDEX; ascii < END_INDEX; ++ascii)
     {
         // Load glyph image into the slot (erase the previous one).
         error = FT_Load_Char(face, ascii, FT_LOAD_RENDER);
@@ -268,7 +268,7 @@ int writeFont(const char* filename, unsigned int fontSize, const char* id, bool
     // File header and version.
     char fileHeader[9]     = {'«', 'G', 'P', 'B', '»', '\r', '\n', '\x1A', '\n'};
     fwrite(fileHeader, sizeof(char), 9, gpbFp);
-    fwrite(gameplay::VERSION, sizeof(char), 2, gpbFp);
+    fwrite(gameplay::GPB_VERSION, sizeof(char), 2, gpbFp);
 
     // Write Ref table (for a single font)
     writeUint(gpbFp, 1);                // Ref[] count

+ 20 - 16
gameplay-encoder/src/Transform.h

@@ -14,37 +14,41 @@ public:
          * Scale animation property. Data=sx,sy,sz
          */
         ANIMATE_SCALE = 1,
-        ANIMATE_SCALE_X,
-        ANIMATE_SCALE_Y,
-        ANIMATE_SCALE_Z,
-        ANIMATE_SCALE_XY,
-        ANIMATE_SCALE_XZ,
-        ANIMATE_SCALE_YZ,
+        ANIMATE_SCALE_X = 2,
+        ANIMATE_SCALE_Y = 3,
+        ANIMATE_SCALE_Z = 4,
+        ANIMATE_SCALE_XY = 5,
+        ANIMATE_SCALE_XZ = 6,
+        ANIMATE_SCALE_YZ = 7,
 
         /**
          * Rotation animation property. Data=qx,qy,qz,qw (as quaternion).
          */
-        ANIMATE_ROTATE,
+        ANIMATE_ROTATE = 8,
 
         /**
          * Translation animation property. Data=tx,ty,tz
          */
-        ANIMATE_TRANSLATE,
-        ANIMATE_TRANSLATE_X,
-        ANIMATE_TRANSLATE_Y,
-        ANIMATE_TRANSLATE_Z,
-        ANIMATE_TRANSLATE_XY,
-        ANIMATE_TRANSLATE_XZ,
-        ANIMATE_TRANSLATE_YZ,
+        ANIMATE_TRANSLATE = 9,
+        ANIMATE_TRANSLATE_X = 10,
+        ANIMATE_TRANSLATE_Y = 11,
+        ANIMATE_TRANSLATE_Z = 12,
+        ANIMATE_TRANSLATE_XY = 13,
+        ANIMATE_TRANSLATE_XZ = 14,
+        ANIMATE_TRANSLATE_YZ = 15,
 
         /**
          * Rotation + Translation animation property(Rigid Body). Data=qx,qy,qz,qw,tx,ty,tz
          */
-        ANIMATE_ROTATE_TRANSLATE,
+        ANIMATE_ROTATE_TRANSLATE = 16,
         /**
          * Scale, Rotation + Translation animation property. Data=sx,sy,sz,qx,qy,qz,qw,tx,ty,tz
          */
-        ANIMATE_SCALE_ROTATE_TRANSLATE
+        ANIMATE_SCALE_ROTATE_TRANSLATE = 17,
+
+        ANIMATE_ROTATE_X = 18,
+        ANIMATE_ROTATE_Y = 19,
+        ANIMATE_ROTATE_Z = 20
     };
 };
 

+ 15 - 61
gameplay-encoder/src/Vector2.cpp

@@ -1,6 +1,5 @@
 #include "Base.h"
 #include "Vector2.h"
-#include "FileIO.h"
 
 namespace gameplay
 {
@@ -10,90 +9,76 @@ Vector2::Vector2()
 {
 }
 
-
 Vector2::Vector2(float x, float y)
 {
     set(x, y);
 }
 
-
 Vector2::Vector2(float* array)
 {
     set(array);
 }
 
-
 Vector2::Vector2(const Vector2& p1, const Vector2& p2)
 {
     set(p1, p2);
 }
 
-
 Vector2::Vector2(const Vector2& copy)
 {
     set(copy);
 }
 
-
 Vector2::~Vector2()
 {
 }
 
-
 const Vector2& Vector2::zero()
 {
-    static Vector2* value = new Vector2(0.0f, 0.0f);
-    return *value;
+    static Vector2 value(0.0f, 0.0f);
+    return value;
 }
 
-
 const Vector2& Vector2::one()
 {
-    static Vector2* value = new Vector2(1.0f, 1.0f);
-    return *value;
+    static Vector2 value(1.0f, 1.0f);
+    return value;
 }
 
-
 const Vector2& Vector2::unitX()
 {
-    static Vector2* value = new Vector2(1.0f, 0.0f);
-    return *value;
+    static Vector2 value(1.0f, 0.0f);
+    return value;
 }
 
-
 const Vector2& Vector2::unitY()
 {
-    static Vector2* value = new Vector2(0.0f, 1.0f);
-    return *value;
+    static Vector2 value(0.0f, 1.0f);
+    return value;
 }
 
-
 bool Vector2::isZero() const
 {
     return x == 0.0f && y == 0.0f;
 }
 
-
 bool Vector2::isOne() const
 {
     return x == 1.0f && y == 1.0f;
 }
 
-
 float Vector2::angle(const Vector2& v1, const Vector2& v2)
 {
     float dz = v1.x * v2.y - v1.y * v2.x;
     return atan2f(fabsf(dz) + MATH_FLOAT_SMALL, dot(v1, v2));
 }
 
-
 void Vector2::add(const Vector2& v)
 {
     x += v.x;
     y += v.y;
 }
 
-
 void Vector2::add(const Vector2& v1, const Vector2& v2, Vector2* dst)
 {
     assert(dst);
@@ -102,7 +87,6 @@ void Vector2::add(const Vector2& v1, const Vector2& v2, Vector2* dst)
     dst->y = v1.y + v2.y;
 }
 
-
 void Vector2::clamp(const Vector2& min, const Vector2& max)
 {
     assert(!( min.x > max.x || min.y > max.y ));
@@ -120,13 +104,12 @@ void Vector2::clamp(const Vector2& min, const Vector2& max)
         y = max.y;
 }
 
-
 void Vector2::clamp(const Vector2& v, const Vector2& min, const Vector2& max, Vector2* dst)
 {
     assert(dst);
     assert(!( min.x > max.x || min.y > max.y ));
 
-    // Clamp the y value.
+    // Clamp the x value.
     dst->x = v.x;
     if ( dst->x < min.x )
         dst->x = min.x;
@@ -141,8 +124,7 @@ void Vector2::clamp(const Vector2& v, const Vector2& min, const Vector2& max, Ve
         dst->y = max.y;
 }
 
-
-float Vector2::distance(const Vector2& v)
+float Vector2::distance(const Vector2& v) const
 {
     float dx = v.x - x;
     float dy = v.y - y;
@@ -150,52 +132,44 @@ float Vector2::distance(const Vector2& v)
     return sqrt(dx * dx + dy * dy);
 }
 
-
-float Vector2::distanceSquared(const Vector2& v)
+float Vector2::distanceSquared(const Vector2& v) const
 {
     float dx = v.x - x;
     float dy = v.y - y;
     return (dx * dx + dy * dy);
 }
 
-
 float Vector2::dot(const Vector2& v)
 {
     return (x * v.x + y * v.y);
 }
 
-
 float Vector2::dot(const Vector2& v1, const Vector2& v2)
 {
     return (v1.x * v2.x + v1.y * v2.y);
 }
 
-
-float Vector2::length()
+float Vector2::length() const
 {
     return sqrt(x * x + y * y);
 }
 
-
-float Vector2::lengthSquared()
+float Vector2::lengthSquared() const
 {
     return (x * x + y * y);
 }
 
-
 void Vector2::negate()
 {
     x = -x;
     y = -y;
 }
 
-
 void Vector2::normalize()
 {
     normalize(this);
 }
 
-
 void Vector2::normalize(Vector2* dst)
 {
     assert(dst);
@@ -207,12 +181,12 @@ void Vector2::normalize(Vector2* dst)
     }
 
     float n = x * x + y * y;
-    // already normalized
+    // Already normalized.
     if (n == 1.0f)
         return;
 
     n = sqrt(n);
-    // too close to zero
+    // Too close to zero.
     if (n < MATH_TOLERANCE)
         return;
 
@@ -221,21 +195,18 @@ void Vector2::normalize(Vector2* dst)
     dst->y *= n;
 }
 
-
 void Vector2::scale(float scalar)
 {
     x *= scalar;
     y *= scalar;
 }
 
-
 void Vector2::scale(const Vector2& scale)
 {
     x *= scale.x;
     y *= scale.y;
 }
 
-
 void Vector2::rotate(const Vector2& point, float angle)
 {
     float sinAngle = sin(angle);
@@ -257,14 +228,12 @@ void Vector2::rotate(const Vector2& point, float angle)
     }
 }
 
-
 void Vector2::set(float x, float y)
 {
     this->x = x;
     this->y = y;
 }
 
-
 void Vector2::set(float* array)
 {
     assert(array);
@@ -273,28 +242,24 @@ void Vector2::set(float* array)
     y = array[1];
 }
 
-
 void Vector2::set(const Vector2& v)
 {
     this->x = v.x;
     this->y = v.y;
 }
 
-
 void Vector2::set(const Vector2& p1, const Vector2& p2)
 {
      x = p2.x - p1.x;
      y = p2.y - p1.y;
 }
 
-
 void Vector2::subtract(const Vector2& v)
 {
     x -= v.x;
     y -= v.y;
 }
 
-
 void Vector2::subtract(const Vector2& v1, const Vector2& v2, Vector2* dst)
 {
     assert(dst);
@@ -303,15 +268,4 @@ void Vector2::subtract(const Vector2& v1, const Vector2& v2, Vector2* dst)
     dst->y = v1.y - v2.y;
 }
 
-void Vector2::writeBinary(FILE* file) const
-{
-    write(x, file);
-    write(y, file);
-}
-
-void Vector2::writeText(FILE* file) const
-{
-    fprintf(file, "%f %f\n", x, y);
-}
-
 }

+ 108 - 33
gameplay-encoder/src/Vector2.h

@@ -4,7 +4,6 @@
 namespace gameplay
 {
 
-// Forward declare
 class Matrix;
 
 /**
@@ -65,44 +64,44 @@ public:
     ~Vector2();
 
     /**
-     * The zero vector
+     * Returns the zero vector.
      *
      * @return The 2-element vector of 0s.
      */
     static const Vector2& zero();
 
     /**
-     * The one vector.
+     * Returns the one vector.
      *
      * @return The 2-element vector of 1s.
      */
     static const Vector2& one();
 
     /**
-     * The unit x vector.
+     * Returns the unit x vector.
      *
      * @return The 2-element unit vector along the x axis.
      */
     static const Vector2& unitX();
 
     /**
-     * The unit y vector.
+     * Returns the unit y vector.
      *
      * @return The 2-element unit vector along the y axis.
      */
     static const Vector2& unitY();
 
     /**
-     * Is this vector the all zeros.
+     * Indicates whether this vector contains all zeros.
      *
-     * @return true if all zeros, false if otherwise.
+     * @return true if this vector contains all zeros, false otherwise.
      */
     bool isZero() const;
 
     /**
-     * Is this vector all ones.
+     * Indicates whether this vector contains all ones.
      *
-     * @return true if all ones, false if otherwise.
+     * @return true if this vector contains all ones, false otherwise.
      */
     bool isOne() const;
 
@@ -112,7 +111,7 @@ public:
      * @param v1 The first vector.
      * @param v2 The second vector.
      * 
-     * @return The angle between the two vectors, in radians.
+     * @return The angle between the two vectors (in radians).
      */
     static float angle(const Vector2& v1, const Vector2& v2);
 
@@ -156,9 +155,10 @@ public:
      * @param v The other vector.
      * 
      * @return The distance between this vector and v.
+     * 
      * @see distanceSquared
      */
-    float distance(const Vector2& v);
+    float distance(const Vector2& v) const;
 
     /**
      * Returns the squared distance between this vector and v.
@@ -171,9 +171,10 @@ public:
      * @param v The other vector.
      * 
      * @return The squared distance between this vector and v.
+     * 
      * @see distance
      */
-    float distanceSquared(const Vector2& v);
+    float distanceSquared(const Vector2& v) const;
 
     /**
      * Returns the dot product of this vector and the specified vector.
@@ -198,9 +199,10 @@ public:
      * Computes the length of this vector.
      *
      * @return The length of the vector.
+     * 
      * @see lengthSquared
      */
-    float length();
+    float length() const;
 
     /**
      * Returns the squared length of this vector.
@@ -211,9 +213,10 @@ public:
      * instead of length.
      *
      * @return The squared length of the vector.
+     * 
      * @see length
      */
-    float lengthSquared();
+    float lengthSquared() const;
 
     /**
      * Negates this vector.
@@ -238,7 +241,7 @@ public:
      * of the vector is zero, this method simply copies the
      * current vector into dst.
      *
-     * @param dst the destination vector
+     * @param dst The destination vector.
      */
     void normalize(Vector2* dst);
 
@@ -260,7 +263,7 @@ public:
      * Rotates this vector by angle (specified in radians) around the given point.
      *
      * @param point The point to rotate around.
-     * @param angle The angle to rotate by, in radians.
+     * @param angle The angle to rotate by (in radians).
      */
     void rotate(const Vector2& point, float angle);
 
@@ -288,12 +291,15 @@ public:
 
     /**
      * Sets this vector to the directional vector between the specified points.
+     * 
+     * @param p1 The first point.
+     * @param p2 The second point.
      */
     void set(const Vector2& p1, const Vector2& p2);
 
     /**
      * Subtracts this vector and the specified vector as (this - v)
-     * and stores the result in this.
+     * and stores the result in this vector.
      *
      * @param v The vector to subtract.
      */
@@ -309,30 +315,99 @@ public:
      */
     static void subtract(const Vector2& v1, const Vector2& v2, Vector2* dst);
 
-    inline bool operator<(const Vector2& v) const
-    {
-        if (x == v.x)
-        {
-            return y < v.y;
-        }
-        return x < v.x;
-    }
-    inline bool operator==(const Vector2& v) const
-    {
-        return x==v.x && y==v.y;
-    }
+    /**
+     * Calculates the sum of this vector with the given vector.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @param v The vector to add.
+     * @return The vector sum.
+     */
+    inline Vector2 operator+(const Vector2& v);
+
+    /**
+     * Adds the given vector to this vector.
+     * 
+     * @param v The vector to add.
+     * @return This vector, after the addition occurs.
+     */
+    inline Vector2& operator+=(const Vector2& v);
 
     /**
-     * Writes this vector to the binary file stream.
+     * Calculates the sum of this vector with the given vector.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @param v The vector to add.
+     * @return The vector sum.
      */
-    void writeBinary(FILE* file) const;
+    inline Vector2 operator-(const Vector2& v);
 
     /**
-     * Writes this vector to a text file stream.
+     * Subtracts the given vector from this vector.
+     * 
+     * @param v The vector to subtract.
+     * @return This vector, after the subtraction occurs.
      */
-    void writeText(FILE* file) const;
+    inline Vector2& operator-=(const Vector2& v);
+
+    /**
+     * Calculates the negation of this vector.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @return The negation of this vector.
+     */
+    inline Vector2 operator-();
+
+    /**
+     * Calculates the scalar product of this vector with the given value.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @param x The value to scale by.
+     * @return The scaled vector.
+     */
+    inline Vector2 operator*(float x);
+
+    /**
+     * Scales this vector by the given value.
+     * 
+     * @param x The value to scale by.
+     * @return This vector, after the scale occurs.
+     */
+    inline Vector2& operator*=(float x);
+
+    /**
+     * Determines if this vector is less than the given vector.
+     * 
+     * @param v The vector to compare against.
+     * 
+     * @return True if this vector is less than the given vector, false otherwise.
+     */
+    inline bool operator<(const Vector2& v) const;
+
+    /**
+     * Determines if this vector is equal to the given vector.
+     * 
+     * @param v The vector to compare against.
+     * 
+     * @return True if this vector is equal to the given vector, false otherwise.
+     */
+    inline bool operator==(const Vector2& v) const;
 };
 
+/**
+ * Calculates the scalar product of the given vector with the given value.
+ * 
+ * @param x The value to scale by.
+ * @param v The vector to scale.
+ * @return The scaled vector.
+ */
+inline Vector2 operator*(float x, const Vector2& v);
+
 }
 
+#include "Vector2.inl"
+
 #endif

+ 77 - 0
gameplay-encoder/src/Vector2.inl

@@ -0,0 +1,77 @@
+/** 
+ * Vector2.inl
+ */
+
+#include "Vector2.h"
+
+namespace gameplay
+{
+
+inline Vector2 Vector2::operator+(const Vector2& v)
+{
+    Vector2 result(*this);
+    result.add(v);
+    return result;
+}
+
+inline Vector2& Vector2::operator+=(const Vector2& v)
+{
+    add(v);
+    return *this;
+}
+
+inline Vector2 Vector2::operator-(const Vector2& v)
+{
+    Vector2 result(*this);
+    result.subtract(v);
+    return result;
+}
+
+inline Vector2& Vector2::operator-=(const Vector2& v)
+{
+    subtract(v);
+    return *this;
+}
+
+inline Vector2 Vector2::operator-()
+{
+    Vector2 result(*this);
+    result.negate();
+    return result;
+}
+
+inline Vector2 Vector2::operator*(float x)
+{
+    Vector2 result(*this);
+    result.scale(x);
+    return result;
+}
+
+inline Vector2& Vector2::operator*=(float x)
+{
+    scale(x);
+    return *this;
+}
+
+inline bool Vector2::operator<(const Vector2& v) const
+{
+    if (x == v.x)
+    {
+        return y < v.y;
+    }
+    return x < v.x;
+}
+
+inline bool Vector2::operator==(const Vector2& v) const
+{
+    return x==v.x && y==v.y;
+}
+
+inline Vector2 operator*(float x, const Vector2& v)
+{
+    Vector2 result(v);
+    result.scale(x);
+    return result;
+}
+
+}

+ 31 - 72
gameplay-encoder/src/Vector3.cpp

@@ -1,6 +1,5 @@
 #include "Base.h"
 #include "Vector3.h"
-#include "FileIO.h"
 
 namespace gameplay
 {
@@ -10,77 +9,80 @@ Vector3::Vector3()
 {
 }
 
-
 Vector3::Vector3(float x, float y, float z)
 {
     set(x, y, z);
 }
 
-
 Vector3::Vector3(float* array)
 {
     set(array);
 }
 
-
 Vector3::Vector3(const Vector3& p1, const Vector3& p2)
 {
     set(p1, p2);
 }
 
-
 Vector3::Vector3(const Vector3& copy)
 {
     set(copy);
 }
 
+Vector3 Vector3::fromColor(unsigned int color)
+{
+    float components[3];
+    int componentIndex = 0;
+    for (int i = 2; i >= 0; --i)
+    {
+        int component = (color >> i*8) & 0x0000ff;
+
+        components[componentIndex++] = static_cast<float>(component) / 255.0f;
+    }
+
+    Vector3 value(components);
+    return value;
+}
 
 Vector3::~Vector3()
 {
 }
 
-
 const Vector3& Vector3::zero()
 {
-    static Vector3* value = new Vector3(0.0f, 0.0f, 0.0f);
-    return *value;
+    static Vector3 value(0.0f, 0.0f, 0.0f);
+    return value;
 }
 
-
 const Vector3& Vector3::one()
 {
-    static Vector3* value = new Vector3(1.0f, 1.0f, 1.0f);
-    return *value;
+    static Vector3 value(1.0f, 1.0f, 1.0f);
+    return value;
 }
 
-
 const Vector3& Vector3::unitX()
 {
-    static Vector3* value = new Vector3(1.0f, 0.0f, 0.0f);
-    return *value;
+    static Vector3 value(1.0f, 0.0f, 0.0f);
+    return value;
 }
 
-
 const Vector3& Vector3::unitY()
 {
-    static Vector3* value = new Vector3(0.0f, 1.0f, 0.0f);
-    return *value;
+    static Vector3 value(0.0f, 1.0f, 0.0f);
+    return value;
 }
 
-
 const Vector3& Vector3::unitZ()
 {
-    static Vector3* value = new Vector3(0.0f, 0.0f, 1.0f);
-    return *value;
+    static Vector3 value(0.0f, 0.0f, 1.0f);
+    return value;
 }
 
-
 bool Vector3::isZero() const
 {
     return x == 0.0f && y == 0.0f && z == 0.0f;
 }
 
-
 bool Vector3::isOne() const
 {
     return x == 1.0f && y == 1.0f && z == 1.0f;
@@ -95,7 +97,6 @@ float Vector3::angle(const Vector3& v1, const Vector3& v2)
     return atan2f(sqrt(dx * dx + dy * dy + dz * dz) + MATH_FLOAT_SMALL, dot(v1, v2));
 }
 
-
 void Vector3::add(const Vector3& v)
 {
     x += v.x;
@@ -103,7 +104,6 @@ void Vector3::add(const Vector3& v)
     z += v.z;
 }
 
-
 void Vector3::add(const Vector3& v1, const Vector3& v2, Vector3* dst)
 {
     assert(dst);
@@ -113,7 +113,6 @@ void Vector3::add(const Vector3& v1, const Vector3& v2, Vector3* dst)
     dst->z = v1.z + v2.z;
 }
 
-
 void Vector3::clamp(const Vector3& min, const Vector3& max)
 {
     assert(!( min.x > max.x || min.y > max.y || min.z > max.z));
@@ -137,13 +136,12 @@ void Vector3::clamp(const Vector3& min, const Vector3& max)
         z = max.z;
 }
 
-
 void Vector3::clamp(const Vector3& v, const Vector3& min, const Vector3& max, Vector3* dst)
 {
     assert(dst);
     assert(!( min.x > max.x || min.y > max.y || min.z > max.z));
 
-    // Clamp the y value.
+    // Clamp the x value.
     dst->x = v.x;
     if ( dst->x < min.x )
         dst->x = min.x;
@@ -165,7 +163,6 @@ void Vector3::clamp(const Vector3& v, const Vector3& min, const Vector3& max, Ve
         dst->z = max.z;
 }
 
-
 void Vector3::cross(const Vector3& v)
 {
     float tx = (y * v.z) - (z * v.y);
@@ -176,7 +173,6 @@ void Vector3::cross(const Vector3& v)
     z = tz;
 }
 
-
 void Vector3::cross(const Vector3& v1, const Vector3& v2, Vector3* dst)
 {
     assert(dst);
@@ -189,8 +185,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;
@@ -199,8 +194,7 @@ float Vector3::distance(const Vector3& v)
     return sqrt(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;
@@ -209,31 +203,26 @@ float Vector3::distanceSquared(const Vector3& v)
     return (dx * dx + dy * dy + dz * dz);
 }
 
-
 float Vector3::dot(const Vector3& v)
 {
     return (x * v.x + y * v.y + z * v.z);
 }
 
-
 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 sqrt(x * x + y * y + z * z);
 }
 
-
-float Vector3::lengthSquared()
+float Vector3::lengthSquared() const
 {
     return (x * x + y * y + z * z);
 }
 
-
 void Vector3::negate()
 {
     x = -x;
@@ -241,13 +230,11 @@ void Vector3::negate()
     z = -z;
 }
 
-
 void Vector3::normalize()
 {
     normalize(this);
 }
 
-
 void Vector3::normalize(Vector3* dst) const
 {
     assert(dst);
@@ -260,12 +247,12 @@ void Vector3::normalize(Vector3* dst) const
     }
 
     float n = x * x + y * y + z * z;
-    // already normalized
+    // Already normalized.
     if (n == 1.0f)
         return;
 
     n = sqrt(n);
-    // too close to zero
+    // Too close to zero.
     if (n < MATH_TOLERANCE)
         return;
 
@@ -275,7 +262,6 @@ void Vector3::normalize(Vector3* dst) const
     dst->z *= n;
 }
 
-
 void Vector3::scale(float scalar)
 {
     x *= scalar;
@@ -283,7 +269,6 @@ void Vector3::scale(float scalar)
     z *= scalar;
 }
 
-
 void Vector3::set(float x, float y, float z)
 {
     this->x = x;
@@ -291,7 +276,6 @@ void Vector3::set(float x, float y, float z)
     this->z = z;
 }
 
-
 void Vector3::set(float* array)
 {
     assert(array);
@@ -301,7 +285,6 @@ void Vector3::set(float* array)
     z = array[2];
 }
 
-
 void Vector3::set(const Vector3& v)
 {
     this->x = v.x;
@@ -309,7 +292,6 @@ void Vector3::set(const Vector3& v)
     this->z = v.z;
 }
 
-
 void Vector3::set(const Vector3& p1, const Vector3& p2)
 {
     x = p2.x - p1.x;
@@ -317,7 +299,6 @@ void Vector3::set(const Vector3& p1, const Vector3& p2)
     z = p2.z - p1.z;
 }
 
-
 void Vector3::subtract(const Vector3& v)
 {
     x -= v.x;
@@ -325,7 +306,6 @@ void Vector3::subtract(const Vector3& v)
     z -= v.z;
 }
 
-
 void Vector3::subtract(const Vector3& v1, const Vector3& v2, Vector3* dst)
 {
     assert(dst);
@@ -335,25 +315,4 @@ void Vector3::subtract(const Vector3& v1, const Vector3& v2, Vector3* dst)
     dst->z = v1.z - v2.z;
 }
 
-float Vector3::distanceSquared(const Vector3& v1, const Vector3& v2)
-{
-    float dx = v2.x - v1.x;
-    float dy = v2.y - v1.y;
-    float dz = v2.z - v1.z;
-
-    return (dx * dx + dy * dy + dz * dz);
-}
-
-void Vector3::writeBinary(FILE* file) const
-{
-    write(x, file);
-    write(y, file);
-    write(z, file);
-}
-
-void Vector3::writeText(FILE* file) const
-{
-    fprintf(file, "%f %f %f\n", x, y, z);
-}
-
 }

+ 117 - 39
gameplay-encoder/src/Vector3.h

@@ -4,7 +4,6 @@
 namespace gameplay
 {
 
-// Forward declare
 class Matrix;
 class Quaternion;
 
@@ -14,7 +13,7 @@ class Quaternion;
  * When using a vector to represent a surface normal,
  * the vector should typically be normalized.
  * Other uses of directional vectors may wish to leave
- * the magnitude of the vector in-tact. When used as a point,
+ * the magnitude of the vector intact. When used as a point,
  * the elements of the vector represent a position in 3D space.
  */
 class Vector3
@@ -72,57 +71,67 @@ public:
      */
     Vector3(const Vector3& copy);
 
+    /**
+     * Creates a new vector from an integer interpreted as an RGB value.
+     * E.g. 0xff0000 represents red or the vector (1, 0, 0).
+     *
+     * @param color The integer to interpret as an RGB value.
+     *
+     * @return A vector corresponding to the interpreted RGB color.
+     */
+    static Vector3 fromColor(unsigned int color);
+
     /**
      * Destructor.
      */
     ~Vector3();
 
     /**
-     * The zero vector
+     * Returns the zero vector.
      *
      * @return The 3-element vector of 0s.
      */
     static const Vector3& zero();
 
     /**
-     * The one vector.
+     * Returns the one vector.
      *
      * @return The 3-element vector of 1s.
      */
     static const Vector3& one();
 
     /**
-     * The unit x vector.
+     * Returns the unit x vector.
      *
      * @return The 3-element unit vector along the x axis.
      */
     static const Vector3& unitX();
 
     /**
-     * The unit y vector.
+     * Returns the unit y vector.
      *
      * @return The 3-element unit vector along the y axis.
      */
     static const Vector3& unitY();
 
     /**
-     * The unit z vector.
+     * Returns the unit z vector.
      *
      * @return The 3-element unit vector along the z axis.
      */
     static const Vector3& unitZ();
 
     /**
-     * Is this vector the all zeros.
+     * Indicates whether this vector contains all zeros.
      *
-     * @return true if all zeros, false if otherwise.
+     * @return true if this vector contains all zeros, false otherwise.
      */
     bool isZero() const;
 
     /**
-     * Is this vector all ones.
+     * Indicates whether this vector contains all ones.
      *
-     * @return true if all ones, false if otherwise.
+     * @return true if this vector contains all ones, false otherwise.
      */
     bool isOne() const;
 
@@ -132,7 +141,7 @@ public:
      * @param v1 The first vector.
      * @param v2 The second vector.
      * 
-     * @return The angle between the two vectors, in radians.
+     * @return The angle between the two vectors (in radians).
      */
     static float angle(const Vector3& v1, const Vector3& v2);
 
@@ -174,7 +183,7 @@ public:
     /**
      * Sets this vector to the cross product between itself and the specified vector.
      *
-     * @param v the vector to compute the cross product with.
+     * @param v The vector to compute the cross product with.
      */
     void cross(const Vector3& v);
 
@@ -193,9 +202,10 @@ public:
      * @param v The other vector.
      * 
      * @return The distance between this vector and v.
+     * 
      * @see distanceSquared
      */
-    float distance(const Vector3& v);
+    float distance(const Vector3& v) const;
 
     /**
      * Returns the squared distance between this vector and v.
@@ -208,9 +218,10 @@ public:
      * @param v The other vector.
      * 
      * @return The squared distance between this vector and v.
+     * 
      * @see distance
      */
-    float distanceSquared(const Vector3& v);
+    float distanceSquared(const Vector3& v) const;
 
     /**
      * Returns the dot product of this vector and the specified vector.
@@ -235,9 +246,10 @@ public:
      * Computes the length of this vector.
      *
      * @return The length of the vector.
+     * 
      * @see lengthSquared
      */
-    float length();
+    float length() const;
 
     /**
      * Returns the squared length of this vector.
@@ -248,9 +260,10 @@ public:
      * instead of length.
      *
      * @return The squared length of the vector.
+     * 
      * @see length
      */
-    float lengthSquared();
+    float lengthSquared() const;
 
     /**
      * Negates this vector.
@@ -275,7 +288,7 @@ public:
      * of the vector is zero, this method simply copies the
      * current vector into dst.
      *
-     * @param dst the destination vector
+     * @param dst The destination vector.
      */
     void normalize(Vector3* dst) const;
 
@@ -316,7 +329,7 @@ public:
 
     /**
      * Subtracts this vector and the specified vector as (this - v)
-     * and stores the result in this.
+     * and stores the result in this vector.
      *
      * @param v The vector to subtract.
      */
@@ -332,34 +345,99 @@ public:
      */
     static void subtract(const Vector3& v1, const Vector3& v2, Vector3* dst);
 
-    inline bool operator<(const Vector3& v) const
-    {
-        if (x == v.x)
-        {
-            if (y == v.y)
-            {
-                return z < v.z;
-            }
-            return y < v.y;
-        }
-        return x < v.x;
-    }
+    /**
+     * Calculates the sum of this vector with the given vector.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @param v The vector to add.
+     * @return The vector sum.
+     */
+    inline Vector3 operator+(const Vector3& v);
 
-    inline bool operator==(const Vector3& v) const
-    {
-        return x==v.x && y==v.y && z==v.z;
-    }
+    /**
+     * Adds the given vector to this vector.
+     * 
+     * @param v The vector to add.
+     * @return This vector, after the addition occurs.
+     */
+    inline Vector3& operator+=(const Vector3& v);
 
-    static float distanceSquared(const Vector3& v1, const Vector3& v2);
+    /**
+     * Calculates the sum of this vector with the given vector.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @param v The vector to add.
+     * @return The vector sum.
+     */
+    inline Vector3 operator-(const Vector3& v);
 
     /**
-     * Writes this vector to the binary file stream.
+     * Subtracts the given vector from this vector.
+     * 
+     * @param v The vector to subtract.
+     * @return This vector, after the subtraction occurs.
      */
-    void writeBinary(FILE* file) const;
+    inline Vector3& operator-=(const Vector3& v);
 
-    void writeText(FILE* file) const;
+    /**
+     * Calculates the negation of this vector.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @return The negation of this vector.
+     */
+    inline Vector3 operator-();
+
+    /**
+     * Calculates the scalar product of this vector with the given value.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @param x The value to scale by.
+     * @return The scaled vector.
+     */
+    inline Vector3 operator*(float x);
+
+    /**
+     * Scales this vector by the given value.
+     * 
+     * @param x The value to scale by.
+     * @return This vector, after the scale occurs.
+     */
+    inline Vector3& operator*=(float x);
+
+    /**
+     * Determines if this vector is less than the given vector.
+     * 
+     * @param v The vector to compare against.
+     * 
+     * @return True if this vector is less than the given vector, false otherwise.
+     */
+    inline bool operator<(const Vector3& v) const;
+
+    /**
+     * Determines if this vector is equal to the given vector.
+     * 
+     * @param v The vector to compare against.
+     * 
+     * @return True if this vector is equal to the given vector, false otherwise.
+     */
+    inline bool operator==(const Vector3& v) const;
 };
 
+/**
+ * Calculates the scalar product of the given vector with the given value.
+ * 
+ * @param x The value to scale by.
+ * @param v The vector to scale.
+ * @return The scaled vector.
+ */
+inline Vector3 operator*(float x, const Vector3& v);
+
 }
 
+#include "Vector3.inl"
+
 #endif

+ 82 - 0
gameplay-encoder/src/Vector3.inl

@@ -0,0 +1,82 @@
+/** 
+ * Vector3.inl
+ */
+
+#include "Vector3.h"
+#include "Matrix.h"
+
+namespace gameplay
+{
+
+inline Vector3 Vector3::operator+(const Vector3& v)
+{
+    Vector3 result(*this);
+    result.add(v);
+    return result;
+}
+
+inline Vector3& Vector3::operator+=(const Vector3& v)
+{
+    add(v);
+    return *this;
+}
+
+inline Vector3 Vector3::operator-(const Vector3& v)
+{
+    Vector3 result(*this);
+    result.subtract(v);
+    return result;
+}
+
+inline Vector3& Vector3::operator-=(const Vector3& v)
+{
+    subtract(v);
+    return *this;
+}
+
+inline Vector3 Vector3::operator-()
+{
+    Vector3 result(*this);
+    result.negate();
+    return result;
+}
+
+inline Vector3 Vector3::operator*(float x)
+{
+    Vector3 result(*this);
+    result.scale(x);
+    return result;
+}
+
+inline Vector3& Vector3::operator*=(float x)
+{
+    scale(x);
+    return *this;
+}
+
+inline bool Vector3::operator<(const Vector3& v) const
+{
+    if (x == v.x)
+    {
+        if (y == v.y)
+        {
+            return z < v.z;
+        }
+        return y < v.y;
+    }
+    return x < v.x;
+}
+
+inline bool Vector3::operator==(const Vector3& v) const
+{
+    return x==v.x && y==v.y && z==v.z;
+}
+
+inline Vector3 operator*(float x, const Vector3& v)
+{
+    Vector3 result(v);
+    result.scale(x);
+    return result;
+}
+
+}

+ 33 - 65
gameplay-encoder/src/Vector4.cpp

@@ -1,6 +1,5 @@
 #include "Base.h"
 #include "Vector4.h"
-#include "FileIO.h"
 
 namespace gameplay
 {
@@ -10,90 +9,91 @@ Vector4::Vector4()
 {
 }
 
-
 Vector4::Vector4(float x, float y, float z, float w)
 {
     set(x, y, z, w);
 }
 
-
 Vector4::Vector4(float* src)
 {
     set(src);
 }
 
-
 Vector4::Vector4(const Vector4& p1, const Vector4& p2)
 {
     set(p1, p2);
 }
 
-
 Vector4::Vector4(const Vector4& copy)
 {
     set(copy);
 }
 
+Vector4 Vector4::fromColor(unsigned int color)
+{
+    float components[4];
+    int componentIndex = 0;
+    for (int i = 3; i >= 0; --i)
+    {
+        int component = (color >> i*8) & 0x000000ff;
+
+        components[componentIndex++] = static_cast<float>(component) / 255.0f;
+    }
+
+    Vector4 value(components);
+    return value;
+}
 
 Vector4::~Vector4()
 {
 }
 
-
 const Vector4& Vector4::zero()
 {
-    static Vector4* value = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
-    return *value;
+    static Vector4 value(0.0f, 0.0f, 0.0f, 0.0f);
+    return value;
 }
 
-
 const Vector4& Vector4::one()
 {
-    static Vector4* value = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
-    return *value;
+    static Vector4 value(1.0f, 1.0f, 1.0f, 1.0f);
+    return value;
 }
 
-
 const Vector4& Vector4::unitX()
 {
-    static Vector4* value = new Vector4(1.0f, 0.0f, 0.0f, 0.0f);
-    return *value;
+    static Vector4 value(1.0f, 0.0f, 0.0f, 0.0f);
+    return value;
 }
 
-
 const Vector4& Vector4::unitY()
 {
-    static Vector4* value = new Vector4(0.0f, 1.0f, 0.0f, 0.0f);
-    return *value;
+    static Vector4 value(0.0f, 1.0f, 0.0f, 0.0f);
+    return value;
 }
 
-
 const Vector4& Vector4::unitZ()
 {
-    static Vector4* value = new Vector4(0.0f, 0.0f, 1.0f, 0.0f);
-    return *value;
+    static Vector4 value(0.0f, 0.0f, 1.0f, 0.0f);
+    return value;
 }
 
-
 const Vector4& Vector4::unitW()
 {
-    static Vector4* value = new Vector4(0.0f, 0.0f, 0.0f, 1.0f);
-    return *value;
+    static Vector4 value(0.0f, 0.0f, 0.0f, 1.0f);
+    return value;
 }
 
-
 bool Vector4::isZero() const
 {
     return x == 0.0f && y == 0.0f && z == 0.0f && w == 0.0f;
 }
 
-
 bool Vector4::isOne() const
 {
     return x == 1.0f && y == 1.0f && z == 1.0f && z == 1.0f;
 }
 
-
 float Vector4::angle(const Vector4& v1, const Vector4& v2)
 {
     float dx = v1.w * v2.x - v1.x * v2.w - v1.y * v2.z + v1.z * v2.y;
@@ -103,7 +103,6 @@ float Vector4::angle(const Vector4& v1, const Vector4& v2)
     return atan2f(sqrt(dx * dx + dy * dy + dz * dz) + MATH_FLOAT_SMALL, dot(v1, v2));
 }
 
-
 void Vector4::add(const Vector4& v)
 {
     x += v.x;
@@ -112,7 +111,6 @@ void Vector4::add(const Vector4& v)
     w += v.w;
 }
 
-
 void Vector4::add(const Vector4& v1, const Vector4& v2, Vector4* dst)
 {
     assert(dst);
@@ -123,7 +121,6 @@ void Vector4::add(const Vector4& v1, const Vector4& v2, Vector4* dst)
     dst->w = v1.w + v2.w;
 }
 
-
 void Vector4::clamp(const Vector4& min, const Vector4& max)
 {
     assert(!( min.x > max.x || min.y > max.y || min.z > max.z || min.w > max.w));
@@ -153,13 +150,12 @@ void Vector4::clamp(const Vector4& min, const Vector4& max)
         w = max.w;
 }
 
-
 void Vector4::clamp(const Vector4& v, const Vector4& min, const Vector4& max, Vector4* dst)
 {
     assert(dst);
     assert(!( min.x > max.x || min.y > max.y || min.z > max.z || min.w > max.w));
 
-    // Clamp the y value.
+    // Clamp the x value.
     dst->x = v.x;
     if ( dst->x < min.x )
         dst->x = min.x;
@@ -188,8 +184,7 @@ void Vector4::clamp(const Vector4& v, const Vector4& min, const Vector4& max, Ve
         dst->w = max.w;
 }
 
-
-float Vector4::distance(const Vector4& v)
+float Vector4::distance(const Vector4& v) const
 {
     float dx = v.x - x;
     float dy = v.y - y;
@@ -199,8 +194,7 @@ float Vector4::distance(const Vector4& v)
     return sqrt(dx * dx + dy * dy + dz * dz + dw * dw);
 }
 
-
-float Vector4::distanceSquared(const Vector4& v)
+float Vector4::distanceSquared(const Vector4& v) const
 {
     float dx = v.x - x;
     float dy = v.y - y;
@@ -210,31 +204,27 @@ float Vector4::distanceSquared(const Vector4& v)
     return (dx * dx + dy * dy + dz * dz + dw * dw);
 }
 
-
 float Vector4::dot(const Vector4& v)
 {
     return (x * v.x + y * v.y + z * v.z + w * v.w);
 }
 
-
 float Vector4::dot(const Vector4& v1, const Vector4& v2)
 {
     return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w);
 }
 
-
-float Vector4::length()
+float Vector4::length() const
 {
     return sqrt(x * x + y * y + z * z + w * w);
 }
 
 
-float Vector4::lengthSquared()
+float Vector4::lengthSquared() const
 {
     return (x * x + y * y + z * z + w * w);
 }
 
-
 void Vector4::negate()
 {
     x = -x;
@@ -243,13 +233,11 @@ void Vector4::negate()
     w = -w;
 }
 
-
 void Vector4::normalize()
 {
     normalize(this);
 }
 
-
 void Vector4::normalize(Vector4* dst)
 {
     assert(dst);
@@ -263,12 +251,12 @@ void Vector4::normalize(Vector4* dst)
     }
 
     float n = x * x + y * y + z * z + w * w;
-    // already normalized
+    // Already normalized.
     if (n == 1.0f)
         return;
 
     n = sqrt(n);
-    // too close to zero
+    // Too close to zero.
     if (n < MATH_TOLERANCE)
         return;
 
@@ -279,7 +267,6 @@ void Vector4::normalize(Vector4* dst)
     dst->w *= n;
 }
 
-
 void Vector4::scale(float scalar)
 {
     x *= scalar;
@@ -288,7 +275,6 @@ void Vector4::scale(float scalar)
     w *= scalar;
 }
 
-
 void Vector4::set(float x, float y, float z, float w)
 {
     this->x = x;
@@ -297,7 +283,6 @@ void Vector4::set(float x, float y, float z, float w)
     this->w = w;
 }
 
-
 void Vector4::set(float* array)
 {
     assert(array);
@@ -308,7 +293,6 @@ void Vector4::set(float* array)
     w = array[3];
 }
 
-
 void Vector4::set(const Vector4& v)
 {
     this->x = v.x;
@@ -317,7 +301,6 @@ void Vector4::set(const Vector4& v)
     this->w = v.w;
 }
 
-
 void Vector4::set(const Vector4& p1, const Vector4& p2)
 {
     x = p2.x - p1.x;
@@ -326,7 +309,6 @@ void Vector4::set(const Vector4& p1, const Vector4& p2)
     w = p2.w - p1.w;
 }
 
-
 void Vector4::subtract(const Vector4& v)
 {
     x -= v.x;
@@ -335,7 +317,6 @@ void Vector4::subtract(const Vector4& v)
     w -= v.w;
 }
 
-
 void Vector4::subtract(const Vector4& v1, const Vector4& v2, Vector4* dst)
 {
     assert(dst);
@@ -346,17 +327,4 @@ void Vector4::subtract(const Vector4& v1, const Vector4& v2, Vector4* dst)
     dst->w = v1.w - v2.w;
 }
 
-void Vector4::writeBinary(FILE* file) const
-{
-    write(x, file);
-    write(y, file);
-    write(z, file);
-    write(w, file);
-}
-
-void Vector4::writeText(FILE* file) const
-{
-    fprintf(file, "%f %f %f %f\n", x, y, z, w);
-}
-
 }

+ 118 - 46
gameplay-encoder/src/Vector4.h

@@ -1,12 +1,9 @@
 #ifndef VECTOR4_H_
 #define VECTOR4_H_
 
-#include "FileIO.h"
-
 namespace gameplay
 {
 
-// Forward declare
 class Matrix;
 
 /**
@@ -75,64 +72,74 @@ public:
      */
     Vector4(const Vector4& copy);
 
+    /**
+     * Creates a new vector from an integer interpreted as an RGBA value.
+     * E.g. 0xff0000ff represents opaque red or the vector (1, 0, 0, 1).
+     *
+     * @param color The integer to interpret as an RGBA value.
+     *
+     * @return A vector corresponding to the interpreted RGBA color.
+     */
+    static Vector4 fromColor(unsigned int color);
+
     /**
      * Destructor.
      */
     ~Vector4();
 
     /**
-     * The zero vector
+     * Returns the zero vector.
      *
      * @return The 4-element vector of 0s.
      */
     static const Vector4& zero();
 
     /**
-     * The one vector.
+     * Returns the one vector.
      *
      * @return The 4-element vector of 1s.
      */
     static const Vector4& one();
 
     /**
-     * The unit x vector.
+     * Returns the unit x vector.
      *
      * @return The 4-element unit vector along the x axis.
      */
     static const Vector4& unitX();
 
     /**
-     * The unit y vector.
+     * Returns the unit y vector.
      *
      * @return The 4-element unit vector along the y axis.
      */
     static const Vector4& unitY();
 
     /**
-     * The unit z vector.
+     * Returns the unit z vector.
      *
      * @return The 4-element unit vector along the z axis.
      */
     static const Vector4& unitZ();
 
     /**
-     * The unit w vector.
+     * Returns the unit w vector.
      *
      * @return The 4-element unit vector along the w axis.
      */
     static const Vector4& unitW();
 
     /**
-     * Is this vector the all zeros.
+     * Indicates whether this vector contains all zeros.
      *
-     * @return true if all zeros, false if otherwise.
+     * @return true if this vector contains all zeros, false otherwise.
      */
     bool isZero() const;
 
     /**
-     * Is this vector all ones.
+     * Indicates whether this vector contains all ones.
      *
-     * @return true if all ones, false if otherwise.
+     * @return true if this vector contains all ones, false otherwise.
      */
     bool isOne() const;
 
@@ -142,7 +149,7 @@ public:
      * @param v1 The first vector.
      * @param v2 The second vector.
      * 
-     * @return The angle between the two vectors, in radians.
+     * @return The angle between the two vectors (in radians).
      */
     static float angle(const Vector4& v1, const Vector4& v2);
 
@@ -186,9 +193,10 @@ public:
      * @param v The other vector.
      * 
      * @return The distance between this vector and v.
+     * 
      * @see distanceSquared
      */
-    float distance(const Vector4& v);
+    float distance(const Vector4& v) const;
 
     /**
      * Returns the squared distance between this vector and v.
@@ -201,9 +209,10 @@ public:
      * @param v The other vector.
      * 
      * @return The squared distance between this vector and v.
+     * 
      * @see distance
      */
-    float distanceSquared(const Vector4& v);
+    float distanceSquared(const Vector4& v) const;
 
     /**
      * Returns the dot product of this vector and the specified vector.
@@ -228,9 +237,10 @@ public:
      * Computes the length of this vector.
      *
      * @return The length of the vector.
+     * 
      * @see lengthSquared
      */
-    float length();
+    float length() const;
 
     /**
      * Returns the squared length of this vector.
@@ -241,9 +251,10 @@ public:
      * instead of length.
      *
      * @return The squared length of the vector.
+     * 
      * @see length
      */
-    float lengthSquared();
+    float lengthSquared() const;
 
     /**
      * Negates this vector.
@@ -268,7 +279,7 @@ public:
      * of the vector is zero, this method simply copies the
      * current vector into dst.
      *
-     * @param dst the destination vector
+     * @param dst The destination vector.
      */
     void normalize(Vector4* dst);
 
@@ -305,12 +316,15 @@ public:
 
     /**
      * Sets this vector to the directional vector between the specified points.
+     * 
+     * @param p1 The first point.
+     * @param p2 The second point.
      */
     void set(const Vector4& p1, const Vector4& p2);
 
     /**
      * Subtracts this vector and the specified vector as (this - v)
-     * and stores the result in this.
+     * and stores the result in this vector.
      *
      * @param v The vector to subtract.
      */
@@ -326,41 +340,99 @@ public:
      */
     static void subtract(const Vector4& v1, const Vector4& v2, Vector4* dst);
 
+    /**
+     * Calculates the sum of this vector with the given vector.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @param v The vector to add.
+     * @return The vector sum.
+     */
+    inline Vector4 operator+(const Vector4& v);
+
+    /**
+     * Adds the given vector to this vector.
+     * 
+     * @param v The vector to add.
+     * @return This vector, after the addition occurs.
+     */
+    inline Vector4& operator+=(const Vector4& v);
+
+    /**
+     * Calculates the sum of this vector with the given vector.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @param v The vector to add.
+     * @return The vector sum.
+     */
+    inline Vector4 operator-(const Vector4& v);
+
+    /**
+     * Subtracts the given vector from this vector.
+     * 
+     * @param v The vector to subtract.
+     * @return This vector, after the subtraction occurs.
+     */
+    inline Vector4& operator-=(const Vector4& v);
 
+    /**
+     * Calculates the negation of this vector.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @return The negation of this vector.
+     */
+    inline Vector4 operator-();
 
-    inline bool operator<(const Vector4& v) const
-    {
-        if (x == v.x)
-        {
-            if (y == v.y)
-            {
-                if (z < v.z)
-                {
-                    if (w < v.w)
-                    {
-                        return w < v.w;
-                    }
-                }
-                return z < v.z;
-            }
-            return y < v.y;
-        }
-        return x < v.x;
-    }
+    /**
+     * Calculates the scalar product of this vector with the given value.
+     * 
+     * Note: this does not modify this vector.
+     * 
+     * @param x The value to scale by.
+     * @return The scaled vector.
+     */
+    inline Vector4 operator*(float x);
 
-    inline bool operator==(const Vector4& v) const
-    {
-        return x==v.x && y==v.y && z==v.z && w==v.w;
-    }
+    /**
+     * Scales this vector by the given value.
+     * 
+     * @param x The value to scale by.
+     * @return This vector, after the scale occurs.
+     */
+    inline Vector4& operator*=(float x);
 
     /**
-     * Writes this vector to the binary file stream.
+     * Determines if this vector is less than the given vector.
+     * 
+     * @param v The vector to compare against.
+     * 
+     * @return True if this vector is less than the given vector, false otherwise.
      */
-    void writeBinary(FILE* file) const;
+    inline bool operator<(const Vector4& v) const;
 
-    void writeText(FILE* file) const;
+    /**
+     * Determines if this vector is equal to the given vector.
+     * 
+     * @param v The vector to compare against.
+     * 
+     * @return True if this vector is equal to the given vector, false otherwise.
+     */
+    inline bool operator==(const Vector4& v) const;
 };
 
+/**
+ * Calculates the scalar product of the given vector with the given value.
+ * 
+ * @param x The value to scale by.
+ * @param v The vector to scale.
+ * @return The scaled vector.
+ */
+inline Vector4 operator*(float x, const Vector4& v);
+
 }
 
+#include "Vector4.inl"
+
 #endif

+ 89 - 0
gameplay-encoder/src/Vector4.inl

@@ -0,0 +1,89 @@
+/** 
+ * Vector4.inl
+ */
+
+#include "Matrix.h"
+#include "Vector4.h"
+
+namespace gameplay
+{
+
+inline Vector4 Vector4::operator+(const Vector4& v)
+{
+    Vector4 result(*this);
+    result.add(v);
+    return result;
+}
+
+inline Vector4& Vector4::operator+=(const Vector4& v)
+{
+    add(v);
+    return *this;
+}
+
+inline Vector4 Vector4::operator-(const Vector4& v)
+{
+    Vector4 result(*this);
+    result.subtract(v);
+    return result;
+}
+
+inline Vector4& Vector4::operator-=(const Vector4& v)
+{
+    subtract(v);
+    return *this;
+}
+
+inline Vector4 Vector4::operator-()
+{
+    Vector4 result(*this);
+    result.negate();
+    return result;
+}
+
+inline Vector4 Vector4::operator*(float x)
+{
+    Vector4 result(*this);
+    result.scale(x);
+    return result;
+}
+
+inline Vector4& Vector4::operator*=(float x)
+{
+    scale(x);
+    return *this;
+}
+
+inline bool Vector4::operator<(const Vector4& v) const
+{
+    if (x == v.x)
+    {
+        if (y == v.y)
+        {
+            if (z < v.z)
+            {
+                if (w < v.w)
+                {
+                    return w < v.w;
+                }
+            }
+            return z < v.z;
+        }
+        return y < v.y;
+    }
+    return x < v.x;
+}
+
+inline bool Vector4::operator==(const Vector4& v) const
+{
+    return x==v.x && y==v.y && z==v.z && w==v.w;
+}
+
+inline Vector4 operator*(float x, const Vector4& v)
+{
+    Vector4 result(v);
+    result.scale(x);
+    return result;
+}
+
+}

+ 28 - 26
gameplay-encoder/src/Vertex.cpp

@@ -5,24 +5,14 @@ namespace gameplay
 {
 
 Vertex::Vertex(void)
+    : hasNormal(false), hasTangent(false), hasBinormal(false), hasTexCoord(false), hasColor(false), hasWeights(false)
 {
-    reset();
 }
 
 Vertex::~Vertex(void)
 {
 }
 
-void Vertex::reset()
-{
-    hasNormal = false;
-    hasTangent = false;
-    hasBinormal = false;
-    hasTexCoord = false;
-    hasColor = false;
-    hasWeights = false;
-}
-
 unsigned int Vertex::byteSize() const
 {
     unsigned int count = 3;
@@ -41,66 +31,78 @@ unsigned int Vertex::byteSize() const
 
 void Vertex::writeBinary(FILE* file) const
 {
-    position.writeBinary(file);
+    writeVectorBinary(position, file);
     if (hasNormal)
     {
-        normal.writeBinary(file);
+        writeVectorBinary(normal, file);
     }
     if (hasTangent)
     {
-        tangent.writeBinary(file);
+        writeVectorBinary(tangent, file);
     }
     if (hasBinormal)
     {
-        binormal.writeBinary(file);
+        writeVectorBinary(binormal, file);
     }
     if (hasTexCoord)
     {
-        texCoord.writeBinary(file);
+        writeVectorBinary(texCoord, file);
     }
     // TODO add vertex color?
     //if (hasColor)
     //{
-    //    color.writeBinary(file);
+    //    writeVectorBinary(color, file);
     //}
     if (hasWeights)
     {
-        blendWeights.writeBinary(file);
-        blendIndices.writeBinary(file);
+        writeVectorBinary(blendWeights, file);
+        writeVectorBinary(blendIndices, file);
     }
 }
 
 void Vertex::writeText(FILE* file) const
 {
     write("// position\n", file);
-    position.writeText(file);
+    writeVectorText(position, file);
     if (hasNormal)
     {
         write("// normal\n", file);
-        normal.writeText(file);
+        writeVectorText(normal, file);
     }
     if (hasTangent)
     {
         write("// tanget\n", file);
-        tangent.writeText(file);
+        writeVectorText(tangent, file);
     }
     if (hasBinormal)
     {
         write("// binormal\n", file);
-        binormal.writeText(file);
+        writeVectorText(binormal, file);
     }
     if (hasTexCoord)
     {
         write("// texCoord\n", file);
-        texCoord.writeText(file);
+        writeVectorText(texCoord, file);
     }
     if (hasWeights)
     {
         write("// blendWeights\n", file);
-        blendWeights.writeText(file);
+        writeVectorText(blendWeights, file);
         write("// blendIndices\n", file);
-        blendIndices.writeText(file);
+        writeVectorText(blendIndices, file);
     }
 }
 
+void Vertex::normalizeBlendWeight()
+{
+    float total = blendWeights.x + blendWeights.y + blendWeights.z + blendWeights.w;
+    if (total > 0.0f)
+    {
+        blendWeights.x = blendWeights.x / total;
+        blendWeights.y = blendWeights.y / total;
+        blendWeights.z = blendWeights.z / total;
+        blendWeights.w = blendWeights.w / total;
+    }   
+}
+
 }

+ 6 - 4
gameplay-encoder/src/Vertex.h

@@ -21,7 +21,7 @@ public:
     /**
      * Destructor.
      */
-    virtual ~Vertex(void);
+    ~Vertex(void);
 
     Vector3 position;
     Vector3 normal;
@@ -73,8 +73,6 @@ public:
             blendWeights==v.blendWeights && blendIndices==v.blendIndices;
     }
 
-    void reset();
-
     /**
      * Returns the size of this vertex in bytes.
      */
@@ -89,8 +87,12 @@ public:
      * Writes this vertex to a text file stream.
      */
     void writeText(FILE* file) const;
-};
 
+    /**
+     * Normalizes the blend weights of this vertex so that they add up to 1.0.
+     */
+    void normalizeBlendWeight();
+};
 }
 
 #endif

+ 16 - 3
gameplay-encoder/src/main.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "DAESceneEncoder.h"
+#include "FBXSceneEncoder.h"
 #include "TTFFontEncoder.h"
 #include "GPBDecoder.h"
 #include "EncoderArguments.h"
@@ -54,21 +55,33 @@ int main(int argc, const char** argv)
     {
     case EncoderArguments::FILEFORMAT_DAE:
         {
-            std::string realpath = arguments.getFilePath();
+            std::string realpath(arguments.getFilePath());
             DAESceneEncoder daeEncoder;
             daeEncoder.write(realpath, arguments);
             break;
         }
+    case EncoderArguments::FILEFORMAT_FBX:
+        {
+#ifdef USE_FBX
+            std::string realpath(arguments.getFilePath());
+            FBXSceneEncoder fbxEncoder;
+            fbxEncoder.write(realpath, arguments);
+            break;
+#else
+            fprintf(stderr, "Error: FBX not enabled. Install the FBX SDK and use the preprocessor definition USE_FBX.\n");
+            return -1;
+#endif
+        }
     case EncoderArguments::FILEFORMAT_TTF:
         {
-            std::string realpath = arguments.getFilePath();
+            std::string realpath(arguments.getFilePath());
             std::string id = getFileName(realpath);
             writeFont(realpath.c_str(), arguments.getFontSize(), id.c_str(), arguments.fontPreviewEnabled());
             break;
         }
     case EncoderArguments::FILEFORMAT_GPB:
         {
-            std::string realpath = arguments.getFilePath();
+            std::string realpath(arguments.getFilePath());
             GPBDecoder decoder;
             decoder.readBinary(realpath);
             break;

+ 7 - 2
gameplay/gameplay.vcxproj

@@ -69,6 +69,7 @@
     <ClCompile Include="src\Ref.cpp" />
     <ClCompile Include="src\RenderState.cpp" />
     <ClCompile Include="src\Scene.cpp" />
+    <ClCompile Include="src\SceneLoader.cpp" />
     <ClCompile Include="src\SpriteBatch.cpp" />
     <ClCompile Include="src\Technique.cpp" />
     <ClCompile Include="src\Texture.cpp" />
@@ -134,6 +135,7 @@
     <ClInclude Include="src\Ref.h" />
     <ClInclude Include="src\RenderState.h" />
     <ClInclude Include="src\Scene.h" />
+    <ClInclude Include="src\SceneLoader.h" />
     <ClInclude Include="src\SpriteBatch.h" />
     <ClInclude Include="src\Technique.h" />
     <ClInclude Include="src\Texture.h" />
@@ -166,6 +168,7 @@
     <None Include="res\shaders\solid.vsh" />
     <None Include="res\shaders\textured.fsh" />
     <None Include="res\shaders\textured.vsh" />
+    <None Include="res\textures\particle-default.png" />
     <None Include="src\BoundingBox.inl" />
     <None Include="src\BoundingSphere.inl" />
     <None Include="src\gameplay-main-macos.mm" />
@@ -244,10 +247,11 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>..\external-deps\bullet\include;..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\oggvorbis\include;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>
       </RuntimeTypeInfo>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -260,9 +264,10 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;GAMEPLAY_MEM_LEAK_DETECTION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;GAMEPLAY_MEM_LEAK_DETECTION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>..\external-deps\bullet\include;..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\oggvorbis\include;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>true</RuntimeTypeInfo>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>

+ 12 - 0
gameplay/gameplay.vcxproj.filters

@@ -10,6 +10,9 @@
     <Filter Include="res\shaders">
       <UniqueIdentifier>{be0b36f1-49ed-4a06-9f1f-57c654a554fe}</UniqueIdentifier>
     </Filter>
+    <Filter Include="res\textures">
+      <UniqueIdentifier>{7c4ef2fb-63f2-4d5a-b31e-0dc78329abfd}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="src\Animation.cpp">
@@ -204,6 +207,9 @@
     <ClCompile Include="src\PhysicsMotionState.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\SceneLoader.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\Animation.h">
@@ -395,6 +401,9 @@
     <ClInclude Include="src\PhysicsSocketConstraint.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\SceneLoader.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="res\shaders\bumped-specular.vsh">
@@ -463,6 +472,9 @@
     <None Include="src\PlatformMacOS.mm">
       <Filter>src</Filter>
     </None>
+    <None Include="res\textures\particle-default.png">
+      <Filter>res\textures</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\PhysicsFixedConstraint.inl">

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

@@ -9,6 +9,8 @@
 /* Begin PBXBuildFile section */
 		4220A6E8146B122B00CAEB3A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4220A6E7146B122B00CAEB3A /* QuartzCore.framework */; };
 		4234D99E14686C52003031B3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4234D99D14686C52003031B3 /* Cocoa.framework */; };
+		428390991489D6E800E2B2F5 /* SceneLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 428390971489D6E800E2B2F5 /* SceneLoader.cpp */; };
+		4283909A1489D6E800E2B2F5 /* SceneLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 428390981489D6E800E2B2F5 /* SceneLoader.h */; };
 		4299EFA9146AC94300FF4A73 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4299EFA8146AC94300FF4A73 /* OpenGL.framework */; };
 		4299EFAB146AC94B00FF4A73 /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4299EFAA146AC94B00FF4A73 /* OpenAL.framework */; };
 		42CCD554146EC1DD00353661 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 42CCD553146EC1DD00353661 /* libz.dylib */; };
@@ -159,6 +161,8 @@
 		4220A6E7146B122B00CAEB3A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = SDKs/MacOSX10.7.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
 		4234D99A14686C52003031B3 /* libgameplay.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgameplay.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		4234D99D14686C52003031B3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+		428390971489D6E800E2B2F5 /* SceneLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SceneLoader.cpp; path = src/SceneLoader.cpp; sourceTree = SOURCE_ROOT; };
+		428390981489D6E800E2B2F5 /* SceneLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SceneLoader.h; path = src/SceneLoader.h; sourceTree = SOURCE_ROOT; };
 		4299EFA8146AC94300FF4A73 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = SDKs/MacOSX10.7.sdk/System/Library/Frameworks/OpenGL.framework; sourceTree = DEVELOPER_DIR; };
 		4299EFAA146AC94B00FF4A73 /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = SDKs/MacOSX10.7.sdk/System/Library/Frameworks/OpenAL.framework; sourceTree = DEVELOPER_DIR; };
 		42CCD553146EC1DD00353661 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = SDKs/MacOSX10.7.sdk/usr/lib/libz.dylib; sourceTree = DEVELOPER_DIR; };
@@ -488,6 +492,8 @@
 				42CD0E2C147D8FF50000361E /* RenderTarget.h */,
 				42CD0E2D147D8FF50000361E /* Scene.cpp */,
 				42CD0E2E147D8FF50000361E /* Scene.h */,
+				428390971489D6E800E2B2F5 /* SceneLoader.cpp */,
+				428390981489D6E800E2B2F5 /* SceneLoader.h */,
 				42CD0E2F147D8FF50000361E /* SpriteBatch.cpp */,
 				42CD0E30147D8FF50000361E /* SpriteBatch.h */,
 				42CD0E31147D8FF50000361E /* Technique.cpp */,
@@ -614,6 +620,7 @@
 				42CD0EC8147D8FF60000361E /* VertexAttributeBinding.h in Headers */,
 				42CD0ECA147D8FF60000361E /* VertexFormat.h in Headers */,
 				42CD0ECC147D8FF60000361E /* Viewport.h in Headers */,
+				4283909A1489D6E800E2B2F5 /* SceneLoader.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -736,6 +743,7 @@
 				42CD0EC7147D8FF60000361E /* VertexAttributeBinding.cpp in Sources */,
 				42CD0EC9147D8FF60000361E /* VertexFormat.cpp in Sources */,
 				42CD0ECB147D8FF60000361E /* Viewport.cpp in Sources */,
+				428390991489D6E800E2B2F5 /* SceneLoader.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

二進制
gameplay/res/textures/particle-default.png


+ 1 - 1
gameplay/src/AnimationClip.h

@@ -51,7 +51,7 @@ public:
         };
 
         /**
-         * Handles when a transform has changed.
+         * Handles when animation event occurs.
          */
         virtual void animationEvent(AnimationClip* clip, EventType type) = 0;
     };

+ 19 - 0
gameplay/src/AnimationController.cpp

@@ -47,6 +47,25 @@ Animation* AnimationController::createAnimation(const char* id, AnimationTarget*
     return animation;
 }
 
+Animation* AnimationController::createAnimation(const char* id, AnimationTarget* target, Properties* p)
+{
+    Animation* animation = getAnimation(id);
+
+    if (animation != NULL)
+        return NULL;
+    
+    // TODO: Implement loading from a properties object here.
+    /*
+    animation = new Animation(id, target, p);
+
+    addAnimation(animation);
+
+    target->addAnimation(animation);
+    */
+
+    return animation;
+}
+
 Animation* AnimationController::createAnimationFromTo(const char* id, AnimationTarget* target, int propertyId, float* from, float* to, Curve::InterpolationType type, unsigned long duration)
 {
     const unsigned int propertyComponentCount = target->getAnimationPropertyComponentCount(propertyId);

+ 12 - 0
gameplay/src/AnimationController.h

@@ -4,6 +4,7 @@
 #include "AnimationClip.h"
 #include "Animation.h"
 #include "AnimationTarget.h"
+#include "Properties.h"
 
 namespace gameplay
 {
@@ -52,6 +53,17 @@ public:
      */
     Animation* createAnimation(const char* id, AnimationTarget* target, int propertyId, unsigned int keyCount, unsigned long* keyTimes, float* keyValues, float* keyInValue, float* keyOutValue, Curve::InterpolationType type);
 
+    /**
+     * Creates an animation on this target using the data from the given properties object. 
+     * 
+     * @param id The ID of the animation.
+     * @param target The animation target.
+     * @param properties The properties object defining the animation data.
+     *
+     * @return The newly created animation, or NULL if an animation with the given ID already exists.
+     */
+    Animation* createAnimation(const char* id, AnimationTarget* target, Properties* p);
+
     /**
      * Creates a simple two keyframe from-to animation.
      * Cannot use Curve::BEZIER or CURVE::HERMITE as the interpolation type since they require tangents/control points.

+ 6 - 0
gameplay/src/AudioSource.cpp

@@ -49,6 +49,12 @@ AudioSource* AudioSource::create(const char* path)
     return new AudioSource(buffer, alSource);
 }
 
+AudioSource* AudioSource::create(Properties* properties)
+{
+    // TODO: Implement this!
+    return NULL;
+}
+
 AudioSource::State AudioSource::getState() const
 {
     ALint state;

+ 8 - 0
gameplay/src/AudioSource.h

@@ -41,6 +41,14 @@ public:
      */
     static AudioSource* create(const char* path);
 
+    /**
+     * Create an audio source from the given properties object.
+     * 
+     * @param properties The properties object defining the audio source (must have namespace equal to 'audio').
+     * @return The newly created audio source, or <code>NULL</code> if the audio source failed to load.
+     */
+    static AudioSource* create(Properties* properties);
+
     /**
      * Plays the audio source.
      */

+ 8 - 44
gameplay/src/Base.h

@@ -35,6 +35,8 @@ using std::isdigit;
 using std::toupper;
 using std::tolower;
 using std::size_t;
+using std::min;
+using std::max;
 
 // Common
 #ifndef NULL
@@ -69,33 +71,8 @@ extern void printError(const char* format, ...);
 // Bullet Physics
 #include <btBulletDynamicsCommon.h>
 
-// Since Bullet overrides new, we have to allocate objects manually using its
-// aligned allocation function when we turn on memory leak detection in GamePlay.
-#ifdef GAMEPLAY_MEM_LEAK_DETECTION
-#define BULLET_NEW(type, name, ...) \
-    type* name = (type*)btAlignedAlloc(sizeof(type), 16); \
-    type __##name##_tmp (__VA_ARGS__); \
-    memcpy(name, &__##name##_tmp, sizeof(type))
-
-#define BULLET_DELETE(name) \
-    if (name) \
-    { \
-        btAlignedFree(name); \
-        name = NULL; \
-    }
-
-#else
-#define BULLET_NEW(type, name, ...) \
-    type* name = new type(__VA_ARGS__)
-
-#define BULLET_DELETE(name) SAFE_DELETE(name)
-#endif
-
-
 // Debug new for memory leak detection
-#ifdef GAMEPLAY_MEM_LEAK_DETECTION
 #include "DebugNew.h"
-#endif
 
 // Object deletion macro
 #define SAFE_DELETE(x) \
@@ -140,6 +117,11 @@ extern void printError(const char* format, ...);
 #define M_1_PI                      0.31830988618379067154
 #endif
 
+// NOMINMAX makes sure that windef.h doesn't add macros min and max
+#ifdef WIN32
+    #define NOMINMAX
+#endif
+
 // Audio (OpenAL)
 #ifdef __QNX__
 #include <AL/al.h>
@@ -259,25 +241,7 @@ extern GLenum __gl_error_code;
 #define GL_LAST_ERROR() __gl_error_code
 
 
-#if defined(WIN32) || defined(__APPLE__)
-
-    inline float fminf(float a, float b)
-    {
-      return a < b ? a : b;
-    }
-    inline float fmin(float a, float b)
-    {
-      return a < b ? a : b;
-    }
-    inline float fmaxf(float a, float b)
-    {
-      return a > b ? a : b;
-    }
-    inline float fmax(float a, float b)
-    {
-      return a > b ? a : b;
-    }
-        
+#if defined(WIN32)
     #pragma warning( disable : 4172 )
     #pragma warning( disable : 4244 )
     #pragma warning( disable : 4311 )

+ 12 - 12
gameplay/src/BoundingBox.cpp

@@ -206,14 +206,14 @@ bool BoundingBox::isEmpty() const
 void BoundingBox::merge(const BoundingBox& box)
 {
     // Calculate the new minimum point.
-    min.x = fminf(min.x, box.min.x);
-    min.y = fminf(min.y, box.min.y);
-    min.z = fminf(min.z, box.min.z);
+    min.x = std::min(min.x, box.min.x);
+    min.y = std::min(min.y, box.min.y);
+    min.z = std::min(min.z, box.min.z);
 
     // Calculate the new maximum point.
-    max.x = fmaxf(max.x, box.max.x);
-    max.y = fmaxf(max.y, box.max.y);
-    max.z = fmaxf(max.z, box.max.z);
+    max.x = std::max(max.x, box.max.x);
+    max.y = std::max(max.y, box.max.y);
+    max.z = std::max(max.z, box.max.z);
 }
 
 void BoundingBox::merge(const BoundingSphere& sphere)
@@ -222,14 +222,14 @@ void BoundingBox::merge(const BoundingSphere& sphere)
     float radius = sphere.radius;
 
     // Calculate the new minimum point for the merged bounding box.
-    min.x = fminf(min.x, center.x - radius);
-    min.y = fminf(min.y, center.y - radius);
-    min.z = fminf(min.z, center.z - radius);
+    min.x = std::min(min.x, center.x - radius);
+    min.y = std::min(min.y, center.y - radius);
+    min.z = std::min(min.z, center.z - radius);
 
     // Calculate the new maximum point for the merged bounding box.
-    max.x = fmaxf(max.x, center.x + radius);
-    max.y = fmaxf(max.y, center.y + radius);
-    max.z = fmaxf(max.z, center.z + radius);
+    max.x = std::max(max.x, center.x + radius);
+    max.y = std::max(max.y, center.y + radius);
+    max.z = std::max(max.z, center.z + radius);
 }
 
 void BoundingBox::set(const Vector3& min, const Vector3& max)

+ 2 - 2
gameplay/src/BoundingSphere.cpp

@@ -290,8 +290,8 @@ void BoundingSphere::transform(const Matrix& matrix)
     // Calculate the sphere's new radius from the radii in each direction (take the largest).
     matrix.decompose(&translate, NULL, NULL);
     float r = radius * translate.x;
-    r = fmaxf(radius, radius * translate.y);
-    r = fmaxf(radius, radius * translate.z);
+    r = max(radius, radius * translate.y);
+    r = max(radius, radius * translate.z);
     radius = r;
 }
 

+ 1 - 1
gameplay/src/Camera.cpp

@@ -160,7 +160,7 @@ const Matrix& Camera::getViewMatrix() const
         if (_node)
         {
             // The view matrix is the inverse of our transform matrix.
-            _node->getMatrix().invert(&_view);
+            _node->getWorldMatrix().invert(&_view);
         }
         else
         {

+ 40 - 1
gameplay/src/DebugNew.h

@@ -4,7 +4,7 @@
 /**
  * Global overrides of the new and delete operators for memory tracking.
  * This file is only included when memory leak detection is explicitly
- * request via the pre-processor defintion GAMEPLAY_MEM_LEAK_DETECTION.
+ * request via the pre-processor definition GAMEPLAY_MEM_LEAK_DETECTION.
  */
 #ifdef GAMEPLAY_MEM_LEAK_DETECTION
 
@@ -37,4 +37,43 @@ void operator delete[] (void* p, const char* file, int line) throw();
 #define new DEBUG_NEW
 
 #endif
+
+// Since Bullet overrides new, we define custom functions to allocate Bullet objects that undef
+// 'new' before allocation and redefine it to our custom version afterwards (we support 0-2 parameter constructors).
+template<typename T> T* bullet_new()
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T();
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T();
+#endif
+}
+
+template<typename T, typename T1> T* bullet_new(T1 t1)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION 
+#undef new 
+    T* t = new T(t1);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1);
+#endif
+}
+
+template<typename T, typename T1, typename T2> T* bullet_new(T1 t1, T2 t2)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2);
+#endif
+}
+
 #endif

+ 329 - 212
gameplay/src/Font.cpp

@@ -164,70 +164,133 @@ Font* Font::create(const char* family, Style style, unsigned int size, Glyph* gl
     return font;
 }
 
+unsigned int Font::getSize()
+{
+    return _size;
+}
+
 void Font::begin()
 {
     _batch->begin();
 }
 
-void Font::drawText(const char* text, int x, int y, const Vector4& color, float scale, bool rightToLeft)
+void Font::drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft)
 {
-    char* rightToLeftText = NULL;
+    float scale = (float)size / _size;
+    char* cursor = NULL;
     if (rightToLeft)
     {
-        rightToLeftText = new char[strlen(text) + 1];
-        strcpy(rightToLeftText, text);
-        reverseLines(rightToLeftText);
+        cursor = const_cast<char*>(text);
     }
 
-    const int length = strlen(text);
-    const int size = (int)_size * scale;
     int xPos = x, yPos = y;
-    for (int i = 0; i < length; ++i)
-    {       
-	    char c = 0;
+    bool done = false;
+
+    while (!done)
+    {
+        int length;
+        int startIndex;
+        int iteration;
         if (rightToLeft)
         {
-            c = rightToLeftText[i];
+            char delimiter = cursor[0];
+            while (!done &&
+                   (delimiter == ' ' ||
+                   delimiter == '\t' ||
+                   delimiter == '\r' ||
+                   delimiter == '\n' ||
+                   delimiter == 0))
+            {
+                switch (delimiter)
+                {
+                case ' ':
+                    xPos += size>>1;
+                    break;
+                case '\r':
+                case '\n':
+                    yPos += size;
+                    xPos = x;
+                    break;
+                case '\t':
+                    xPos += (size>>1)*4;
+                    break;
+                case 0:
+                    done = true;
+                    break;
+                }
+
+                if (!done)
+                {
+                    ++cursor;
+                    delimiter = cursor[0];
+                }
+            }
+
+            length = strcspn(cursor, "\r\n");
+            startIndex = length - 1;
+            iteration = -1;
         }
         else
-		{
-            c = text[i];
+        {
+            length = strlen(text);
+            startIndex = 0;
+            iteration = 1;
         }
 
-        // Draw this character.
-        switch (c)
+        for (int i = startIndex; i < length && i >= 0; i += iteration)
         {
-        case ' ':
-            xPos += size>>1;
-            break;
-        case '\r':
-        case '\n':
-            yPos += size;
-            xPos = x;
-            break;
-        case '\t':
-            xPos += (size>>1)*4;
-            break;
-        default:
-            int index = c - 32; // HACK for ASCII
-            if (index >= 0 && index < (int)_glyphCount)
+            char c = 0;
+            if (rightToLeft)
+            {
+                c = cursor[i];
+            }
+            else
+            {
+                c = text[i];
+            }
+
+            // Draw this character.
+            switch (c)
             {
-                Glyph& g = _glyphs[index];
-                _batch->draw(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
-                xPos += g.width * scale + (size>>3);
+            case ' ':
+                xPos += size>>1;
+                break;
+            case '\r':
+            case '\n':
+                yPos += size;
+                xPos = x;
                 break;
+            case '\t':
+                xPos += (size>>1)*4;
+                break;
+            default:
+                int index = c - 32; // HACK for ASCII
+                if (index >= 0 && index < (int)_glyphCount)
+                {
+                    Glyph& g = _glyphs[index];
+                    _batch->draw(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
+                    xPos += g.width * scale + (size>>3);
+                    break;
+                }
             }
         }
-    }
 
-    SAFE_DELETE(rightToLeftText);
+        if (rightToLeft)
+        {
+            cursor += length;   
+        }
+        else
+        {
+            done = true;
+        }
+    }
 }
 
-void Font::drawText(const char* text, const Rectangle& area, const Vector4& color, float scale, Justify justify, bool wrap, bool rightToLeft)
+void Font::drawText(const char* text, const Rectangle& area, const Vector4& color, unsigned int size, Justify justify, bool wrap, bool rightToLeft)
 {
+    float scale = (float)size / _size;
     char* token = const_cast<char*>(text);
     const int length = strlen(text);
-    const int fontSize = (int)(_size * scale);
     int yPos = area.y;
 
     Justify vAlign = static_cast<Justify>(justify & 0xF0);
@@ -242,23 +305,14 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         hAlign = ALIGN_LEFT;
     }
 
-    char* rightToLeftText = NULL;
-    if (rightToLeft)
-    {
-        rightToLeftText = new char[strlen(text) + 1];
-        strcpy(rightToLeftText, text);
-        token = rightToLeftText;
-    }
-    else
-    {
-        token = const_cast<char*>(text);
-    }
+    token = const_cast<char*>(text);
 
     // For alignments other than top-left, need to calculate the y position to begin drawing from
-    // and the starting x position of each line.  For right-to-left text that wraps, need to insert
-    // newlines where lines wrap even for top-left alignment.
+    // and the starting x position of each line.  For right-to-left text, need to determine
+    // the number of characters on each line.
     std::vector<int> xPositions;
-    if (vAlign != ALIGN_TOP || hAlign != ALIGN_LEFT || (rightToLeft && wrap))
+    std::vector<unsigned int> lineLengths;
+    if (vAlign != ALIGN_TOP || hAlign != ALIGN_LEFT || rightToLeft)
     {
         int lineWidth = 0;
         int delimWidth = 0;
@@ -267,6 +321,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         {
             // Go a word at a time.
             bool reachedEOF = false;
+            unsigned int lineLength = 0;
             while (token[0] != 0)
             {
                 unsigned int tokenWidth = 0;
@@ -282,30 +337,25 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
                     switch (delimiter)
                     {
                         case ' ':
-                            delimWidth += fontSize>>1;
+                            delimWidth += size>>1;
+                            lineLength++;
                             break;
                         case '\r':
                         case '\n':
-                            yPos += fontSize;
+                            yPos += size;
 
                             if (lineWidth > 0)
                             {
-                                int hWhitespace = area.width - lineWidth;
-                                if (hAlign == ALIGN_HCENTER)
-                                {
-                                    xPositions.push_back(area.x + hWhitespace / 2);
-                                }
-                                else if (hAlign == ALIGN_RIGHT)
-                                {
-                                    xPositions.push_back(area.x + hWhitespace);
-                                }
+                                addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
                             }
 
                             lineWidth = 0;
+                            lineLength = 0;
                             delimWidth = 0;
                             break;
                         case '\t':
-                            delimWidth += (fontSize>>1)*4;
+                            delimWidth += (size>>1)*4;
+                            lineLength++;
                             break;
                         case 0:
                             reachedEOF = true;
@@ -327,33 +377,26 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
                 }
 
                 unsigned int tokenLength = strcspn(token, " \r\n\t");
-                tokenWidth += getTokenWidth(token, tokenLength, scale);
+                tokenWidth += getTokenWidth(token, tokenLength, size, scale);
 
                 // Wrap if necessary.
                 if (lineWidth + tokenWidth + delimWidth > area.width)
                 {
-                    yPos += fontSize;
+                    yPos += size;
 
                     // Push position of current line.
-                    int hWhitespace = area.width - lineWidth;
-
-                    if (hAlign == ALIGN_HCENTER)
-                    {
-                        xPositions.push_back(area.x + hWhitespace / 2);
-                    }
-                    else if (hAlign == ALIGN_RIGHT)
+                    if (lineLength)
                     {
-                        xPositions.push_back(area.x + hWhitespace);
+                        addLineInfo(area, lineWidth, lineLength-1, hAlign, &xPositions, &lineLengths, rightToLeft);
                     }
-
-                    if (rightToLeft)
+                    else
                     {
-                        // Is this sane?
-                        token[-1] = '\n';
+                        addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
                     }
 
                     // Move token to the next line.
                     lineWidth = 0;
+                    lineLength = 0;
                     delimWidth = 0;
                 }
                 else
@@ -363,6 +406,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
                 }
 
                 lineWidth += tokenWidth;
+                lineLength += tokenLength;
                 token += tokenLength;
             }
 
@@ -379,15 +423,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
             }
 
             // Calculation of final horizontal position.
-            int hWhitespace = area.width - lineWidth;
-            if (hAlign == ALIGN_HCENTER)
-            {
-                xPositions.push_back(area.x + hWhitespace / 2);
-            }
-            else if (hAlign == ALIGN_RIGHT)
-            {
-                xPositions.push_back(area.x + hWhitespace);
-            }
+            addLineInfo(area, lineWidth, lineLength, hAlign, &xPositions, &lineLengths, rightToLeft);
         }
         else
         {
@@ -397,7 +433,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
                 char delimiter = token[0];
                 while (delimiter == '\n')
                 {
-                    yPos += fontSize;
+                    yPos += size;
                     ++token;
                     delimiter = token[0];
                 }
@@ -408,16 +444,8 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
                     tokenLength = strlen(token);
                 }
 
-                int lineWidth = getTokenWidth(token, tokenLength, scale);
-                int whitespace = area.width - lineWidth;
-                if (hAlign == ALIGN_HCENTER)
-                {
-                    xPositions.push_back(area.x + whitespace / 2);
-                }
-                else if (hAlign == ALIGN_RIGHT)
-                {
-                    xPositions.push_back(area.x + whitespace);
-                }
+                int lineWidth = getTokenWidth(token, tokenLength, size, scale);
+                addLineInfo(area, lineWidth, tokenLength, hAlign, &xPositions, &lineLengths, rightToLeft);
 
                 token += tokenLength;
             }
@@ -448,83 +476,58 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         xPos = *xPositionsIt++;
     }
 
+    token = const_cast<char*>(text);
+    
+    int iteration = 1;
+    unsigned int lineLength;
+    unsigned int currentLineLength = 0;
+    char* lineStart;
+    std::vector<unsigned int>::const_iterator lineLengthsIt;
     if (rightToLeft)
     {
-        reverseLines(rightToLeftText);
-        token = rightToLeftText;
-    }
-    else
-    {
-        token = const_cast<char*>(text);
+        lineStart = token;
+        lineLengthsIt = lineLengths.begin();
+        lineLength = *lineLengthsIt++;
+        token += lineLength - 1;
+        iteration = -1;
     }
 
     while (token[0] != 0)
     {
         // Handle delimiters until next token.
-        char delimiter = token[0];
-        bool nextLine = true;
-        bool reachedEOF = false;
-        while (delimiter == ' ' ||
-               delimiter == '\t' ||
-               delimiter == '\r' ||
-               delimiter == '\n' ||
-               delimiter == 0)
-        {
-            switch (delimiter)
-            {
-                case ' ':
-                    xPos += fontSize>>1;
-                    break;
-                case '\r':
-                case '\n':
-                    yPos += fontSize;
-
-                    // Only use next xPos for first newline character (in case of multiple consecutive newlines).
-                    if (nextLine)
-                    {
-                        if (xPositionsIt != xPositions.end())
-                        {
-                            xPos = *xPositionsIt++;
-                        }
-                        else
-                        {
-                            xPos = area.x;
-                        }
-                        nextLine = false;
-                    }
-                    break;
-                case '\t':
-                    xPos += (fontSize>>1)*4;
-                    break;
-                case 0:
-                    reachedEOF = true;
-                    break;
-            }
-
-            if (reachedEOF)
-            {
-                break;
-            }
-
-            token++;
-            delimiter = token[0];
-        }
-
-        if (reachedEOF || token == NULL)
+        if (!handleDelimiters(&token, size, iteration, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
         {
             break;
         }
 
-        unsigned int tokenLength = strcspn(token, " \r\n\t");
-        delimiter = token[tokenLength];
         bool truncated = false;
-        unsigned int tokenWidth = getTokenWidth(token, tokenLength, scale);
+        unsigned int tokenLength;
+        unsigned int tokenWidth;
+        unsigned int startIndex;
+        if (rightToLeft)
+        {
+            tokenLength = getReversedTokenLength(token, text);
+            currentLineLength += tokenLength;
+            token -= (tokenLength - 1);
+            tokenWidth = getTokenWidth(token, tokenLength, size, scale);
+            iteration = -1;
+            startIndex = tokenLength - 1;
+        }
+        else
+        {
+            tokenLength = strcspn(token, " \r\n\t");
+            tokenWidth = getTokenWidth(token, tokenLength, size, scale);
+            iteration = 1;
+            startIndex = 0;
+        }
 
         // Wrap if necessary.
         if (wrap &&
-            xPos + tokenWidth > area.x + area.width)
+            xPos + tokenWidth > area.x + area.width ||
+            (rightToLeft && currentLineLength > lineLength))
         {
-            yPos += fontSize;
+            yPos += size;
+            currentLineLength = tokenLength;
 
             if (xPositionsIt != xPositions.end())
             {
@@ -539,7 +542,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
         bool draw = true;
         if (yPos < area.y)
         {
-            // Skip drawing until linebreak or wrap.
+            // Skip drawing until line break or wrap.
             draw = false;
         }
         else if (yPos > area.y + area.height)
@@ -548,7 +551,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
             break;
         }
 
-        for (unsigned int i = 0; i < tokenLength; ++i)
+        for (int i = startIndex; i < (int)tokenLength && i >= 0; i += iteration)
         {
             char c = token[i];
             int glyphIndex = c - 32; // HACK for ASCII
@@ -557,7 +560,7 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
             {
                 Glyph& g = _glyphs[glyphIndex];
 
-                if (xPos + (int)(g.width*scale) >= area.x + area.width)
+                if (xPos + (int)(g.width*scale) > area.x + area.width)
                 {
                     // Truncate this line and go on to the next one.
                     truncated = true;
@@ -568,32 +571,75 @@ void Font::drawText(const char* text, const Rectangle& area, const Vector4& colo
                     // Draw this character.
                     if (draw)
                     {
-                        _batch->draw(xPos, yPos, g.width * scale, fontSize, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
+                        _batch->draw(xPos, yPos, g.width * scale, size, g.uvs[0], g.uvs[1], g.uvs[2], g.uvs[3], color);
                     }
                 }
-                xPos += g.width*scale + (fontSize>>3);
+                xPos += g.width*scale + (size>>3);
             }
         }
 
         if (!truncated)
         {
-            // Get next token.
-            token += tokenLength;
+            if (rightToLeft)
+            {
+                if (token == lineStart)
+                {
+                    token += lineLength;
+                    
+                    // Now handle delimiters going forwards.
+                    if (!handleDelimiters(&token, size, 1, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
+                    {
+                        break;
+                    }
+
+                    if (lineLengthsIt != lineLengths.end())
+                    {
+                        lineLength = *lineLengthsIt++;
+                    }
+                    lineStart = token;
+                    token += lineLength-1;
+                }
+                else
+                {
+                    token--;
+                }
+            }
+            else
+            {
+                token += tokenLength;
+            }
         }
         else
         {
-            // Skip the rest of this line.
-            unsigned int tokenLength = strcspn(token, "\n");
+            if (rightToLeft)
+            {
+                token = lineStart + lineLength;
+                
+                if (!handleDelimiters(&token, size, 1, area.x, &xPos, &yPos, &currentLineLength, &xPositionsIt, xPositions.end()))
+                {
+                    break;
+                }
 
-            if (tokenLength > 0)
-            {                
-                // Get first token of next line.
-                token += tokenLength;
+                if (lineLengthsIt != lineLengths.end())
+                {
+                    lineLength = *lineLengthsIt++;
+                }
+                lineStart = token;
+                token += lineLength-1;
+            }
+            else
+            {
+                // Skip the rest of this line.
+                unsigned int tokenLength = strcspn(token, "\n");
+
+                if (tokenLength > 0)
+                {                
+                    // Get first token of next line.
+                    token += tokenLength;
+                }
             }
         }
     }
-
-    SAFE_DELETE(rightToLeftText);
 }
 
 void Font::end()
@@ -601,11 +647,11 @@ void Font::end()
     _batch->end();
 }
 
-void Font::measureText(const char* text, unsigned int* width, unsigned int* height, float scale)
+void Font::measureText(const char* text, unsigned int size, unsigned int* width, unsigned int* height)
 {
+    float scale = (float)size / _size;
     const int length = strlen(text);
     char* token = const_cast<char*>(text);
-    unsigned int fontSize = _size * scale;
 
     *width = 0;
     *height = 0;
@@ -615,12 +661,12 @@ void Font::measureText(const char* text, unsigned int* width, unsigned int* heig
     {
         while (token[0] == '\n')
         {
-            *height += fontSize;
+            *height += size;
             ++token;
         }
 
         unsigned int tokenLength = strcspn(token, "\n");
-        unsigned int tokenWidth = getTokenWidth(token, tokenLength, scale);
+        unsigned int tokenWidth = getTokenWidth(token, tokenLength, size, scale);
         if (tokenWidth > *width)
         {
             *width = tokenWidth;
@@ -630,8 +676,9 @@ void Font::measureText(const char* text, unsigned int* width, unsigned int* heig
     }
 }
 
-void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewport, float scale, Justify justify, bool wrap, bool clipped)
+void Font::measureText(const char* text, const Rectangle& viewport, unsigned int size, Rectangle* out, Justify justify, bool wrap, bool ignoreClip)
 {
+    float scale = (float)size / _size;
     Justify vAlign = static_cast<Justify>(justify & 0xF0);
     if (vAlign == 0)
     {
@@ -644,7 +691,6 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
         hAlign = ALIGN_LEFT;
     }
 
-    int fontSize = (int)(_size * scale);
     char* token = const_cast<char*>(text);
     std::vector<bool> emptyLines;
     std::vector<Vector2> lines;
@@ -669,12 +715,12 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
                 switch (delimiter)
                 {
                     case ' ':
-                        delimWidth += fontSize>>1;
+                        delimWidth += size>>1;
                         break;
                     case '\r':
                     case '\n':
                         // Add line-height to vertical cursor.
-                        yPos += fontSize;
+                        yPos += size;
 
                         if (lineWidth > 0)
                         {
@@ -705,7 +751,7 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
                         delimWidth = 0;
                         break;
                     case '\t':
-                        delimWidth += (fontSize>>1)*4;
+                        delimWidth += (size>>1)*4;
                         break;
                     case 0:
                         reachedEOF = true;
@@ -728,13 +774,13 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
 
             // Measure the next token.
             unsigned int tokenLength = strcspn(token, " \r\n\t");
-            unsigned int tokenWidth = getTokenWidth(token, tokenLength, scale);
+            unsigned int tokenWidth = getTokenWidth(token, tokenLength, size, scale);
 
             // Wrap if necessary.
             if (lineWidth + tokenWidth + delimWidth > viewport.width)
             {
                 // Add line-height to vertical cursor.
-                yPos += fontSize;
+                yPos += size;
 
                 // Determine horizontal position and width.
                 int hWhitespace = viewport.width - lineWidth;
@@ -776,7 +822,7 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
                 if (nextLine)
                 {
                     // Add line-height to vertical cursor.
-                    yPos += fontSize * (emptyLinesCount+1);
+                    yPos += size * (emptyLinesCount+1);
                     nextLine = false;
                     emptyLinesCount = 0;
                     emptyLines.push_back(false);
@@ -794,7 +840,7 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
 
             // Measure the next line.
             unsigned int tokenLength = strcspn(token, "\n");
-            lineWidth = getTokenWidth(token, tokenLength, scale);
+            lineWidth = getTokenWidth(token, tokenLength, size, scale);
             
             // Determine horizontal position and width.
             int xPos = viewport.x;
@@ -814,7 +860,7 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
             token += tokenLength;
         }
 
-        yPos += fontSize;
+        yPos += size;
     }
 
     if (wrap)
@@ -852,24 +898,24 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
 
     int clippedTop = 0;
     int clippedBottom = 0;
-    if (clipped)
+    if (!ignoreClip)
     {
         // Trim rect to fit text that would actually be drawn within the given viewport.
         if (y >= viewport.y)
         {
             // Text goes off the bottom of the viewport.
-            clippedBottom = (height - viewport.height) / fontSize + 1;
+            clippedBottom = (height - viewport.height) / size + 1;
             if (clippedBottom > 0)
             {
                 // Also need to crop empty lines above non-empty lines that have been clipped.
                 unsigned int emptyIndex = emptyLines.size() - clippedBottom;
                 while (emptyIndex < emptyLines.size() && emptyLines[emptyIndex] == true)
                 {
-                    height -= fontSize;
+                    height -= size;
                     emptyIndex++;
                 }
 
-                height -= fontSize * clippedBottom;
+                height -= size * clippedBottom;
             }
             else
             {
@@ -879,7 +925,7 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
         else
         {
             // Text goes above the top of the viewport.
-            clippedTop = (viewport.y - y) / fontSize + 1;
+            clippedTop = (viewport.y - y) / size + 1;
             if (clippedTop < 0)
             {
                 clippedTop = 0;
@@ -889,25 +935,25 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
             unsigned int emptyIndex = clippedTop;
             while (emptyIndex < emptyLines.size() && emptyLines[emptyIndex] == true)
             {
-                y += fontSize;
-                height -= fontSize;
+                y += size;
+                height -= size;
                 emptyIndex++;
             }
 
             if (vAlign == ALIGN_VCENTER)
             {
                 // In this case lines may be clipped off the bottom as well.
-                clippedBottom = (height - viewport.height + vWhitespace/2 + 0.01) / fontSize + 1;
+                clippedBottom = (height - viewport.height + vWhitespace/2 + 0.01) / size + 1;
                 if (clippedBottom > 0)
                 {
                     emptyIndex = emptyLines.size() - clippedBottom;
                     while (emptyIndex < emptyLines.size() && emptyLines[emptyIndex] == true)
                     {
-                        height -= fontSize;
+                        height -= size;
                         emptyIndex++;
                     }
 
-                    height -= fontSize * clippedBottom;
+                    height -= size * clippedBottom;
                 }
                 else
                 {
@@ -915,13 +961,13 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
                 }
             }
 
-            y = y + fontSize * clippedTop;
-            height = height - fontSize * clippedTop;
+            y = y + size * clippedTop;
+            height = height - size * clippedTop;
         }
     }
 
     // Determine left-most x coordinate and largest width out of lines that have not been clipped.
-    for (unsigned int i = clippedTop; i < lines.size() - clippedBottom; ++i)
+    for (int i = clippedTop; i < (int)lines.size() - clippedBottom; ++i)
     {
         if (lines[i].x < x)
         {
@@ -933,7 +979,7 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
         }
     }
 
-    if (clipped)
+    if (!ignoreClip)
     {
         // Guarantee that the output rect will fit within the viewport.
         out->x = (x >= viewport.x)? x : viewport.x;
@@ -950,11 +996,10 @@ void Font::measureText(const char* text, Rectangle* out, const Rectangle& viewpo
     }
 }
 
-unsigned int Font::getTokenWidth(const char* token, unsigned int length, float scale)
+unsigned int Font::getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale)
 {
     // Calculate width of word or line.
     unsigned int tokenWidth = 0;
-    const int size = (int)(_size * scale);
     for (unsigned int i = 0; i < length; ++i)
     {
         char c = token[i];
@@ -980,23 +1025,95 @@ unsigned int Font::getTokenWidth(const char* token, unsigned int length, float s
     return tokenWidth;
 }
 
-void Font::reverseLines(char* text)
+unsigned int Font::getReversedTokenLength(const char* token, const char* bufStart)
+{
+    char* cursor = const_cast<char*>(token);
+    char c = cursor[0];
+    unsigned int length = 0;
+
+    while (cursor != bufStart && c != ' ' && c != '\r' && c != '\n' && c != '\t')
+    {
+        length++;
+        cursor--;
+        c = cursor[0];
+    }
+
+    if (cursor == bufStart)
+    {
+        length++;
+    }
+
+    return length;
+}
+
+bool Font::handleDelimiters(char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
+                      std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd)
 {
-    // Naive approach: reverse each line, then render left-to-right as usual.
-    while (text[0] != 0)
+    char delimiter = *token[0];
+    bool nextLine = true;
+    while (delimiter == ' ' ||
+            delimiter == '\t' ||
+            delimiter == '\r' ||
+            delimiter == '\n' ||
+            delimiter == 0)
     {
-        while (text[0] == '\n')
+        switch (delimiter)
         {
-            ++text;
+            case ' ':
+                *xPos += size>>1;
+                (*lineLength)++;
+                break;
+            case '\r':
+            case '\n':
+                *yPos += size;
+
+                // Only use next xPos for first newline character (in case of multiple consecutive newlines).
+                if (nextLine)
+                {
+                    if (*xPositionsIt != xPositionsEnd)
+                    {
+                        *xPos = **xPositionsIt;
+                        (*xPositionsIt)++;
+                    }
+                    else
+                    {
+                        *xPos = areaX;
+                    }
+                    nextLine = false;
+                    *lineLength = 0;
+                }
+                break;
+            case '\t':
+                *xPos += (size>>1)*4;
+                (*lineLength)++;
+                break;
+            case 0:
+                return false;
         }
 
-        unsigned int textLength = strcspn(text, "\n");
+        *token += iteration;
+        delimiter = *token[0];
+    }
 
-        std::string line(text, textLength);
-        std::string reversedLine(line.rbegin(), line.rend());
-        memcpy(text, reversedLine.c_str(), textLength);
+    return true;
+}
 
-        text += textLength;
+void Font::addLineInfo(const Rectangle& area, int lineWidth, int lineLength, Justify hAlign,
+                       std::vector<int>* xPositions, std::vector<unsigned int>* lineLengths, bool rightToLeft)
+{
+    int hWhitespace = area.width - lineWidth;
+    if (hAlign == ALIGN_HCENTER)
+    {
+        (*xPositions).push_back(area.x + hWhitespace / 2);
+    }
+    else if (hAlign == ALIGN_RIGHT)
+    {
+        (*xPositions).push_back(area.x + hWhitespace);
+    }
+
+    if (rightToLeft)
+    {
+        (*lineLengths).push_back(lineLength);
     }
 }
 

+ 30 - 17
gameplay/src/Font.h

@@ -109,11 +109,21 @@ public:
      */
     static Font* create(const char* family, Style style, unsigned int size, Glyph* glyphs, int glyphCount, Texture* texture);
 
+    /**
+     * Returns the font size (max height of glyphs) in pixels.
+     */
+    unsigned int getSize();
+
     /**
      * Begins text drawing for this font.
      */
     void begin();
 
+    /**
+     * Ends text batching for this font and renders all drawn text.
+     */
+    void end();
+
     /**
      * Draws the specified text in a solid color, with a scaling factor.
      *
@@ -121,37 +131,34 @@ public:
      * @param x The viewport x position to draw text at.
      * @param y The viewport y position to draw text at.
      * @param color The color of text.
-     * @param scale The scaling factor.
+     * @param size The size to draw text.
      */
-    void drawText(const char* text, int x, int y, const Vector4& color, float scale = 1.0f, bool rightToLeft = false);
+    void drawText(const char* text, int x, int y, const Vector4& color, unsigned int size, bool rightToLeft = false);
 
     /**
      * Draws the specified text within a rectangular area, with a specified alignment and scale.
      * Clips text outside the viewport.  Optionally wraps text to fit within the width of the viewport.
      *
      * @param text The text to draw.
-     * @param viewport The viewport area to draw within.  Text starts from the top-left of this rectangle.
+     * @param clip The viewport area to draw within.  Text will be clipped outside this rectangle.
      * @param color The color of text.
-     * @param scale The text's scaling factor.
+     * @param size The size to draw text.
      * @param justify Justification of text within the viewport.
      * @param wrap Wraps text to fit within the width of the viewport if true.
+     * @param rightToLeft
      */
-    void drawText(const char* text, const Rectangle& viewport, const Vector4& color, float scale = 1.0f,
+    void drawText(const char* text, const Rectangle& clip, const Vector4& color, unsigned int size,
                   Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool rightToLeft = false);
 
-    /**
-     * Ends text drawing for this font.
-     */
-    void end();
-
     /**
      * Measures a string's width and height without alignment, wrapping or clipping.
      *
      * @param text The text to measure.
+     * @param size
      * @param width Destination for the text's width.
      * @param height Destination for the text's height.
      */
-    void measureText(const char* text, unsigned int* width, unsigned int* height, float scale = 1.0f);
+    void measureText(const char* text, unsigned int size, unsigned int* widthOut, unsigned int* heightOut);
 
     /**
      * Measures a string's bounding box after alignment, wrapping and clipping within a viewport.
@@ -162,11 +169,11 @@ public:
      * @param scale The scaling factor to apply.
      * @param justify Justification of text within the viewport.
      * @param wrap Whether to measure text with wrapping applied.
-     * @param clipped Whether to clip 'out' to the viewport.  Set true for the bounds of what would actually be drawn
-     *                within the given viewport; false for bounds that are guaranteed to fit the entire string of text.
+     * @param ignoreClip Whether to clip 'out' to the viewport.  Set false for the bounds of what would actually be drawn
+     *                within the given viewport; true for bounds that are guaranteed to fit the entire string of text.
      */
-    void measureText(const char* text, Rectangle* out, const Rectangle& viewport,
-                     float scale = 1.0f, Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool clipped = true);
+    void measureText(const char* text, const Rectangle& clip, unsigned int size, Rectangle* out,
+                     Justify justify = ALIGN_TOP_LEFT, bool wrap = true, bool ignoreClip = false);
 
 
 private:
@@ -187,8 +194,14 @@ private:
     ~Font();
 
     // Utilities
-    unsigned int getTokenWidth(const char* token, unsigned int length, float scale);
-    void reverseLines(char* text);
+    unsigned int getTokenWidth(const char* token, unsigned int length, unsigned int size, float scale);
+    unsigned int getReversedTokenLength(const char* token, const char* bufStart);
+
+    // Returns false if EOF was reached, true otherwise.
+    bool handleDelimiters(char** token, const unsigned int size, const int iteration, const int areaX, int* xPos, int* yPos, unsigned int* lineLength,
+                          std::vector<int>::const_iterator* xPositionsIt, std::vector<int>::const_iterator xPositionsEnd);
+    void addLineInfo(const Rectangle& area, int lineWidth, int lineLength, Justify hAlign,
+                     std::vector<int>* xPositions, std::vector<unsigned int>* lineLengths, bool rightToLeft);
 
     std::string _path;
     std::string _id;

+ 0 - 1
gameplay/src/Joint.cpp

@@ -27,7 +27,6 @@ Node::Type Joint::getType() const
 void Joint::transformChanged()
 {
     Node::transformChanged();
-
     _jointMatrixDirty = true;
 }
 

+ 8 - 6
gameplay/src/Material.cpp

@@ -43,12 +43,18 @@ Material* Material::create(const char* materialPath)
         return NULL;
     }
 
+    Material* material = create(properties->getNextNamespace());
+    SAFE_DELETE(properties);
+
+    return material;
+}
+
+Material* Material::create(Properties* materialProperties)
+{
     // Check if the Properties is valid and has a valid namespace.
-    Properties* materialProperties = properties->getNextNamespace();
     assert(materialProperties);
     if (!materialProperties || !(strcmp(materialProperties->getNamespace(), "material") == 0))
     {
-        SAFE_DELETE(properties);
         return NULL;
     }
 
@@ -64,7 +70,6 @@ Material* Material::create(const char* materialPath)
             if (!loadTechnique(material, techniqueProperties))
             {
                 SAFE_RELEASE(material);
-                SAFE_DELETE(properties);
                 return NULL;
             }
         }
@@ -73,9 +78,6 @@ Material* Material::create(const char* materialPath)
     // Load uniform value parameters for this material
     loadRenderState(material, materialProperties);
 
-    // Material properties no longer required
-    SAFE_DELETE(properties);
-
     // Set the current technique to the first found technique
     if (material->getTechniqueCount() > 0)
     {

+ 9 - 0
gameplay/src/Material.h

@@ -34,6 +34,15 @@ public:
      */
     static Material* create(const char* materialPath);
 
+    /**
+     * Creates a material from the specified properties object.
+     * 
+     * @param materialProperties The properties object defining the 
+     *      material (must have namespace equal to 'material').
+     * @return A new Material.
+     */
+    static Material* create(Properties* materialProperties);
+
     /**
      * Creates a material from the specified effect.
      *

+ 1 - 1
gameplay/src/Matrix.h

@@ -933,6 +933,6 @@ inline Vector4 operator*(const Matrix& m, const Vector4& v);
 
 }
 
-//#include "Matrix.inl"
+#include "Matrix.inl"
 
 #endif

+ 2 - 2
gameplay/src/MeshPart.cpp

@@ -39,7 +39,7 @@ MeshPart* MeshPart::create(Mesh* mesh, unsigned int meshIndex, Mesh::PrimitiveTy
         return NULL;
     }
 
-    unsigned int indexSize;
+    unsigned int indexSize = 0;
     switch (indexFormat)
     {
     case Mesh::INDEX8:
@@ -105,7 +105,7 @@ void MeshPart::setIndexData(void* indexData, unsigned int indexStart, unsigned i
 {
     GL_ASSERT( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer) );
 
-    unsigned int indexSize;
+    unsigned int indexSize = 0;
     switch (_indexFormat)
     {
     case Mesh::INDEX8:

+ 1 - 0
gameplay/src/Model.cpp

@@ -218,6 +218,7 @@ void Model::setNode(Node* node)
 
 void Model::draw(bool wireframe)
 {
+    wireframe &= (_mesh->getPrimitiveType() == Mesh::TRIANGLES) | (_mesh->getPrimitiveType() == Mesh::TRIANGLE_STRIP);
     unsigned int count = _mesh->getPartCount();
     if (count == 0)
     {

+ 15 - 1
gameplay/src/Node.cpp

@@ -285,7 +285,7 @@ const Matrix& Node::getWorldMatrix() const
         // If we have a parent, multiply our parent world transform by our local
         // transform to obtain our final resolved world transform.
         Node* parent = getParent();
-        if (parent && !_physicsRigidBody)
+        if (parent && (!_physicsRigidBody || _physicsRigidBody->isKinematic()) )
         {
             Matrix::multiply(parent->getWorldMatrix(), getMatrix(), &_world);
         }
@@ -707,4 +707,18 @@ void Node::setPhysicsRigidBody(PhysicsRigidBody::Type type, float mass, float fr
         _physicsRigidBody = new PhysicsRigidBody(this, type, mass, friction, restitution, linearDamping, angularDamping);
 }
 
+void Node::setPhysicsRigidBody(const char* filePath)
+{
+    SAFE_DELETE(_physicsRigidBody);
+
+    _physicsRigidBody = PhysicsRigidBody::create(this, filePath);
+}
+
+void Node::setPhysicsRigidBody(Properties* properties)
+{
+    SAFE_DELETE(_physicsRigidBody);
+
+    _physicsRigidBody = PhysicsRigidBody::create(this, properties);
+}
+
 }

+ 14 - 0
gameplay/src/Node.h

@@ -369,6 +369,20 @@ public:
     void setPhysicsRigidBody(PhysicsRigidBody::Type type, float mass = 0.0f, float friction = 0.5f,
         float restitution = 0.0f, float linearDamping = 0.0f, float angularDamping = 0.0f);
 
+    /**
+     * Sets the physics rigid body for this node using the rigid body definition in the given file.
+     * 
+     * @param filePath The path to the file that contains the rigid body definition.
+     */
+    void setPhysicsRigidBody(const char* filePath);
+
+    /**
+     * Sets the physics rigid body for this node from the given properties object.
+     * 
+     * @param properties The properties object defining the rigid body (must have namespace equal to 'rigidbody').
+     */
+    void setPhysicsRigidBody(Properties* properties);
+
     /**
      * Returns the bounding sphere for the Node, in world space.
      *

+ 58 - 14
gameplay/src/Package.cpp

@@ -3,6 +3,7 @@
 #include "FileSystem.h"
 #include "MeshPart.h"
 #include "Scene.h"
+#include "SceneLoader.h"
 #include "Joint.h"
 
 #define GPB_PACKAGE_VERSION_MAJOR 1
@@ -118,6 +119,11 @@ Package* Package::create(const char* path)
 
     // Open the package
     FILE* fp = FileSystem::openFile(path, "rb");
+    if (!fp)
+    {
+        WARN_VARG("Failed to open file: '%s'.", path);
+        return NULL;
+    }
 
     // Read the GPG header info
     char sig[9];
@@ -279,6 +285,11 @@ bool Package::readMatrix(float* m)
 }
 
 Scene* Package::loadScene(const char* id)
+{
+    return loadScene(id, NULL);
+}
+
+Scene* Package::loadScene(const char* id, const std::vector<std::string>* nodesWithMeshRB)
 {
     clearLoadSession();
 
@@ -310,7 +321,7 @@ Scene* Package::loadScene(const char* id)
         // Read each child directly into the scene
         for (unsigned int i = 0; i < childrenCount; i++)
         {
-            Node* node = readNode(scene, NULL);
+            Node* node = readNode(scene, NULL, nodesWithMeshRB);
             if (node)
             {
                 scene->addNode(node);
@@ -372,12 +383,17 @@ Scene* Package::loadScene(const char* id)
 }
 
 Node* Package::loadNode(const char* id)
+{
+    return loadNode(id, false);
+}
+
+Node* Package::loadNode(const char* id, bool loadWithMeshRBSupport)
 {
     assert(id);
 
     clearLoadSession();
 
-    Node* node = loadNode(id, NULL, NULL);
+    Node* node = loadNode(id, NULL, NULL, loadWithMeshRBSupport);
    
     if (node)
     {
@@ -387,7 +403,7 @@ Node* Package::loadNode(const char* id)
     return node;
 }
 
-Node* Package::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
+Node* Package::loadNode(const char* id, Scene* sceneContext, Node* nodeContext, bool loadWithMeshRBSupport)
 {
     assert(id);
 
@@ -413,13 +429,22 @@ Node* Package::loadNode(const char* id, Scene* sceneContext, Node* nodeContext)
             return NULL;
         }
 
-        node = readNode(sceneContext, nodeContext);
+        if (loadWithMeshRBSupport)
+        {
+            std::vector<std::string> nodesWithMeshRBSupport;
+            nodesWithMeshRBSupport.push_back(id);
+            node = readNode(sceneContext, nodeContext, &nodesWithMeshRBSupport);
+        }
+        else
+        {
+            node = readNode(sceneContext, nodeContext, NULL);
+        }
     }
 
     return node;
 }
 
-Node* Package::readNode(Scene* sceneContext, Node* nodeContext)
+Node* Package::readNode(Scene* sceneContext, Node* nodeContext, const std::vector<std::string>* nodesWithMeshRB)
 {
     const char* id = getIdFromOffset();
 
@@ -470,7 +495,7 @@ Node* Package::readNode(Scene* sceneContext, Node* nodeContext)
         // Read each child
         for (unsigned int i = 0; i < childrenCount; i++)
         {
-            Node* child = readNode(sceneContext, nodeContext);
+            Node* child = readNode(sceneContext, nodeContext, nodesWithMeshRB);
             if (child)
             {
                 node->addChild(child);
@@ -495,8 +520,23 @@ Node* Package::readNode(Scene* sceneContext, Node* nodeContext)
         SAFE_RELEASE(light);
     }
 
+    // Check if this node's id is in the list of nodes to be loaded with
+    // mesh rigid body support so that when we load the model we keep the proper data.
+    bool loadWithMeshRBSupport = false;
+    if (nodesWithMeshRB)
+    {
+        for (unsigned int i = 0; i < nodesWithMeshRB->size(); i++)
+        {
+            if (strcmp((*nodesWithMeshRB)[i].c_str(), id) == 0)
+            {
+                loadWithMeshRBSupport = true;
+                break;
+            }
+        }
+    }
+
     // Read model
-    Model* model = readModel(sceneContext, nodeContext);
+    Model* model = readModel(sceneContext, nodeContext, loadWithMeshRBSupport, node->getId());
     if (model)
     {
         node->setModel(model);
@@ -627,14 +667,14 @@ Light* Package::readLight()
     return light;
 }
 
-Model* Package::readModel(Scene* sceneContext, Node* nodeContext)
+Model* Package::readModel(Scene* sceneContext, Node* nodeContext, bool loadWithMeshRBSupport, const char* nodeId)
 {
     // Read mesh
     Mesh* mesh = NULL;
     std::string xref = readString(_file);
     if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
     {
-        mesh = loadMesh(xref.c_str() + 1);
+        mesh = loadMesh(xref.c_str() + 1, loadWithMeshRBSupport, nodeId);
         if (mesh)
         {
             Model* model = Model::create(mesh);
@@ -762,7 +802,7 @@ void Package::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
             {
                 jointId = jointId.substr(1, jointId.length() - 1);
 
-                Node* n = loadNode(jointId.c_str(), sceneContext, nodeContext);
+                Node* n = loadNode(jointId.c_str(), sceneContext, nodeContext, false);
                 if (n && n->getType() == Node::JOINT)
                 {
                     Joint* joint = static_cast<Joint*>(n);
@@ -871,7 +911,7 @@ Animation* Package::readAnimationChannel(Scene* scene, Animation* animation, con
     float* values = NULL;
     float* tangentsIn = NULL;
     float* tangentsOut = NULL;
-    float* interpolation = NULL;
+    unsigned int* interpolation = NULL;
 
     // length of the arrays
     unsigned int keyTimesCount;
@@ -956,7 +996,7 @@ Animation* Package::readAnimationChannel(Scene* scene, Animation* animation, con
     return animation;
 }
 
-Mesh* Package::loadMesh(const char* id)
+Mesh* Package::loadMesh(const char* id, bool loadWithMeshRBSupport, const char* nodeId)
 {
     // save the file position
     long position = ftell(_file);
@@ -1038,6 +1078,8 @@ Mesh* Package::loadMesh(const char* id)
         return NULL;
     }
     mesh->setVertexData(vertexData, 0, vertexCount);
+    if (loadWithMeshRBSupport)
+        SceneLoader::addMeshRigidBodyData(nodeId, mesh, vertexData, vertexByteCount);
     SAFE_DELETE_ARRAY(vertexData);
 
     // Set mesh bounding volumes
@@ -1074,7 +1116,7 @@ Mesh* Package::loadMesh(const char* id)
         }
 
         Mesh::IndexFormat indexFormat = (Mesh::IndexFormat)iFormat;
-        unsigned int indexSize;
+        unsigned int indexSize = 0;
         switch (indexFormat)
         {
         case Mesh::INDEX8:
@@ -1097,6 +1139,8 @@ Mesh* Package::loadMesh(const char* id)
             return NULL;
         }
         part->setIndexData(indexData, 0, indexCount);
+        if (loadWithMeshRBSupport)
+            SceneLoader::addMeshRigidBodyData(nodeId, indexData, iByteCount);
         SAFE_DELETE_ARRAY(indexData);
     }
 
@@ -1174,7 +1218,7 @@ Font* Package::loadFont(const char* id)
     }
 
     // Load the texture for the font
-    Texture* texture = Texture::create(Texture::ALPHA, width, height, textureData);
+    Texture* texture = Texture::create(Texture::ALPHA, width, height, textureData, true);
 
     // Free the texture data (no longer needed)
     SAFE_DELETE_ARRAY(textureData);

+ 40 - 5
gameplay/src/Package.h

@@ -15,6 +15,8 @@ namespace gameplay
  */
 class Package : public Ref
 {
+    friend class SceneLoader;
+
 public:
 
     /**
@@ -109,9 +111,8 @@ private:
         ~Reference();
     };
 
-    class MeshSkinData
+    struct MeshSkinData
     {
-    public:
         MeshSkin* skin;
         std::vector<std::string> joints;
         std::vector<Matrix> inverseBindPoseMatrices;
@@ -172,12 +173,46 @@ private:
      */
     Reference* seekToFirstType(unsigned int type);
 
+    /**
+     * Loads the scene with the specified ID from the package, and loads the specified nodes with mesh rigid body support.
+     * 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).
+     * @param nodesWithMeshRB A list of the IDs of the nodes within the scene that 
+     *      should be loaded with support for triangle mesh rigid bodies.
+     * 
+     * @return The loaded scene, or NULL if the scene could not be loaded.
+     */
+    Scene* loadScene(const char* id, const std::vector<std::string>* nodesWithMeshRB);
+
+    /**
+     * Loads a node with the specified ID from the package, optionally with mesh rigid body support.
+     *
+     * @param id The ID of the node to load in the package.
+     * @param loadWithMeshRBSupport Whether or not to load the node with mesh rigid body support.
+     * 
+     * @return The loaded node, or NULL if the node could not be loaded.
+     */
+    Node* loadNode(const char* id, bool loadWithMeshRBSupport);
+
     /**
      * Internal method to load a node.
      *
      * Only one of node or scene should be passed as non-NULL (or neither).
      */
-    Node* loadNode(const char* id, Scene* sceneContext, Node* nodeContext);
+    Node* loadNode(const char* id, Scene* sceneContext, Node* nodeContext, bool loadWithMeshRBSupport);
+
+    /**
+     * Loads a mesh with the specified ID from the package.
+     *
+     * @param id The ID of the mesh to load.
+     * @param loadWithMeshRBSupport Whether to load the mesh with 
+     *      support for triangle mesh rigid bodies or not.
+     * @param nodeId The id of the mesh's model's parent node.
+     * 
+     * @return The loaded mesh, or NULL if the mesh could not be loaded.
+     */
+    Mesh* loadMesh(const char* id, bool loadWithMeshRBSupport, const char* nodeId);
 
     /**
      * Reads an unsigned int from the current file position.
@@ -241,7 +276,7 @@ private:
      * 
      * @return A pointer to new node or NULL if there was an error.
      */
-    Node* readNode(Scene* sceneContext, Node* nodeContext);
+    Node* readNode(Scene* sceneContext, Node* nodeContext, const std::vector<std::string>* nodesWithMeshRB);
 
     /**
      * Reads a camera from the current file position.
@@ -262,7 +297,7 @@ private:
      * 
      * @return A pointer to a new model or NULL if there was an error.
      */
-    Model* readModel(Scene* sceneContext, Node* nodeContext);
+    Model* readModel(Scene* sceneContext, Node* nodeContext, bool loadWithMeshRBSupport, const char* nodeId);
 
     /**
      * Reads a mesh skin from the current file position.

+ 42 - 40
gameplay/src/ParticleEmitter.cpp

@@ -52,7 +52,7 @@ ParticleEmitter* ParticleEmitter::create(const char* textureFile, TextureBlendin
     if (!texture)
     {
         // Use default texture.
-        texture = Texture::create("../gameplay-resources/res/textures/particle-default.png", true);
+        texture = Texture::create("../gameplay/res/textures/particle-default.png", true);
     }
     assert(texture);
 
@@ -88,18 +88,24 @@ ParticleEmitter* ParticleEmitter::create(const char* particleFile)
         return NULL;
     }
 
-    // Top level namespace is "particle <particleName>"
-    Properties* particle = properties->getNextNamespace();
-    if (!particle || strcmp(particle->getNamespace(), "particle") != 0)
+    ParticleEmitter* particle = create(properties->getNextNamespace());
+    SAFE_DELETE(properties);
+
+    return particle;
+}
+
+ParticleEmitter* ParticleEmitter::create(Properties* properties)
+{
+    if (!properties || strcmp(properties->getNamespace(), "particle") != 0)
     {
-        LOG_ERROR_VARG("Error loading ParticleEmitter: No 'particle' namespace found: %s", particleFile);
+        LOG_ERROR("Error loading ParticleEmitter: No 'particle' namespace found");
         return NULL;
     }
 
-    Properties* sprite = particle->getNextNamespace();
+    Properties* sprite = properties->getNextNamespace();
     if (!sprite || strcmp(sprite->getNamespace(), "sprite") != 0)
     {
-        LOG_ERROR_VARG("Error loading ParticleEmitter: No 'sprite' namespace found: %s", particleFile);
+        LOG_ERROR("Error loading ParticleEmitter: No 'sprite' namespace found");
         return NULL;
     }
 
@@ -108,7 +114,7 @@ ParticleEmitter* ParticleEmitter::create(const char* particleFile)
     const char* texturePath = sprite->getString("path");
     if (strlen(texturePath) == 0)
     {
-        LOG_ERROR_VARG("Error loading ParticleEmitter: No texture path specified: %s, in %s", texturePath, particleFile);
+        LOG_ERROR_VARG("Error loading ParticleEmitter: No texture path specified: %s", texturePath);
         return NULL;
     }
 
@@ -123,36 +129,36 @@ ParticleEmitter* ParticleEmitter::create(const char* particleFile)
     float spriteFrameDuration = sprite->getFloat("frameDuration");
 
     // Emitter properties.
-    unsigned int particleCountMax = (unsigned int)particle->getInt("particleCountMax");
+    unsigned int particleCountMax = (unsigned int)properties->getInt("particleCountMax");
     if (particleCountMax == 0)
     {
         // Set sensible default.
         particleCountMax = PARTICLE_COUNT_MAX;
     }
 
-    unsigned int emissionRate = (unsigned int)particle->getInt("emissionRate");
+    unsigned int emissionRate = (unsigned int)properties->getInt("emissionRate");
     if (emissionRate == 0)
     {
         emissionRate = PARTICLE_EMISSION_RATE;
     }
 
-    bool ellipsoid = particle->getBool("ellipsoid");
+    bool ellipsoid = properties->getBool("ellipsoid");
 
-    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 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");
 
     Vector4 colorStart;
     Vector4 colorStartVar;
     Vector4 colorEnd;
     Vector4 colorEndVar;
-    particle->getVector4("colorStart", &colorStart);
-    particle->getVector4("colorStartVar", &colorStartVar);
-    particle->getVector4("colorEnd", &colorEnd);
-    particle->getVector4("colorEndVar", &colorEndVar);
+    properties->getVector4("colorStart", &colorStart);
+    properties->getVector4("colorStartVar", &colorStartVar);
+    properties->getVector4("colorEnd", &colorEnd);
+    properties->getVector4("colorEndVar", &colorEndVar);
 
     Vector3 position;
     Vector3 positionVar;
@@ -162,21 +168,21 @@ ParticleEmitter* ParticleEmitter::create(const char* particleFile)
     Vector3 accelerationVar;
     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);
-    float rotationPerParticleSpeedMin = particle->getFloat("rotationPerParticleSpeedMin");
-    float rotationPerParticleSpeedMax = particle->getFloat("rotationPerParticleSpeedMax");
-    float rotationSpeedMin = particle->getFloat("rotationSpeedMin");
-    float rotationSpeedMax = particle->getFloat("rotationSpeedMax");
-    particle->getVector3("rotationAxis", &rotationAxis);
-    particle->getVector3("rotationAxisVar", &rotationAxisVar);
-    bool orbitPosition = particle->getBool("orbitPosition");
-    bool orbitVelocity = particle->getBool("orbitVelocity");
-    bool orbitAcceleration = particle->getBool("orbitAcceleration");
+    properties->getVector3("position", &position);
+    properties->getVector3("positionVar", &positionVar);
+    properties->getVector3("velocity", &velocity);
+    properties->getVector3("velocityVar", &velocityVar);
+    properties->getVector3("acceleration", &acceleration);
+    properties->getVector3("accelerationVar", &accelerationVar);
+    float rotationPerParticleSpeedMin = properties->getFloat("rotationPerParticleSpeedMin");
+    float rotationPerParticleSpeedMax = properties->getFloat("rotationPerParticleSpeedMax");
+    float rotationSpeedMin = properties->getFloat("rotationSpeedMin");
+    float rotationSpeedMax = properties->getFloat("rotationSpeedMax");
+    properties->getVector3("rotationAxis", &rotationAxis);
+    properties->getVector3("rotationAxisVar", &rotationAxisVar);
+    bool orbitPosition = properties->getBool("orbitPosition");
+    bool orbitVelocity = properties->getBool("orbitVelocity");
+    bool orbitAcceleration = properties->getBool("orbitAcceleration");
 
     // Apply all properties to a newly created ParticleEmitter.
     ParticleEmitter* emitter = ParticleEmitter::create(texturePath, textureBlending, particleCountMax);
@@ -199,8 +205,6 @@ ParticleEmitter* ParticleEmitter::create(const char* particleFile)
 
     emitter->setOrbit(orbitPosition, orbitVelocity, orbitAcceleration);
 
-    SAFE_DELETE(properties);
-
     return emitter;
 }
 
@@ -811,7 +815,6 @@ void ParticleEmitter::update(long elapsedTime)
                     {
                         ++p->_frame;
                     }
-                    break;
                 }
                 else
                 {
@@ -827,7 +830,6 @@ void ParticleEmitter::update(long elapsedTime)
                             p->_frame = 0;
                         }
                     }
-                    break;
                 }
             }
         }

+ 12 - 1
gameplay/src/ParticleEmitter.h

@@ -8,6 +8,7 @@
 #include "Texture.h"
 #include "Rectangle.h"
 #include "SpriteBatch.h"
+#include "Properties.h"
 
 namespace gameplay
 {
@@ -158,13 +159,23 @@ public:
      */
     static ParticleEmitter* create(const char* particleFile);
 
+    /**
+     * Creates a particle emitter from the specified properties object.
+     * 
+     * @param properties The properties object defining the 
+     *      particle emitter (must have namespace equal to 'particle').
+     * @return The newly created particle emitter, or <code>NULL</code> if the particle emitter failed to load.
+     */
+    static ParticleEmitter* create(Properties* properties);
+
     /**
      * Creates an uninitialized ParticleEmitter.
      *
      * @param texturePath A path to the image to use as this ParticleEmitter's texture.
+     * @param textureBlending The type of texture blending to be used for the particles emitted.
      * @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, TextureBlending blending,  unsigned int particleCountMax);
+    static ParticleEmitter* create(const char* texturePath, TextureBlending textureBlending,  unsigned int particleCountMax);
 
     /**
      * Sets the emission rate, measured in particles per second.

+ 326 - 56
gameplay/src/PhysicsController.cpp

@@ -1,16 +1,29 @@
 #include "Base.h"
 #include "Game.h"
+#include "MeshPart.h"
 #include "PhysicsController.h"
 #include "PhysicsMotionState.h"
+#include "SceneLoader.h"
+
+// The initial capacity of the Bullet debug drawer's vertex batch.
+#define INITIAL_CAPACITY 280
 
 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), _status(Listener::DEACTIVATED), _listeners(NULL)
+  : _collisionConfiguration(NULL), _dispatcher(NULL),
+    _overlappingPairCache(NULL), _solver(NULL), _world(NULL), _debugDrawer(NULL), 
+    _status(PhysicsController::Listener::DEACTIVATED), _listeners(NULL),
+    _gravity(btScalar(0.0), btScalar(-9.8), btScalar(0.0))
+{
+    // Default gravity is 9.8 along the negative Y axis.
+}
+
+PhysicsController::~PhysicsController()
 {
+    SAFE_DELETE(_debugDrawer);
+    SAFE_DELETE(_listeners);
 }
 
 void PhysicsController::addStatusListener(Listener* listener)
@@ -21,22 +34,19 @@ void PhysicsController::addStatusListener(Listener* listener)
     _listeners->push_back(listener);
 }
 
-PhysicsController::~PhysicsController()
-{
-    SAFE_DELETE(_listeners);
-}
-
 PhysicsFixedConstraint* PhysicsController::createFixedConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
+    checkConstraintRigidBodies(a, b);
     PhysicsFixedConstraint* constraint = new PhysicsFixedConstraint(a, b);
-    setupConstraint(a, b, constraint);
+    addConstraint(a, b, constraint);
     return constraint;
 }
 
 PhysicsGenericConstraint* PhysicsController::createGenericConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
+    checkConstraintRigidBodies(a, b);
     PhysicsGenericConstraint* constraint = new PhysicsGenericConstraint(a, b);
-    setupConstraint(a, b, constraint);
+    addConstraint(a, b, constraint);
     return constraint;
 }
 
@@ -44,9 +54,9 @@ PhysicsGenericConstraint* PhysicsController::createGenericConstraint(PhysicsRigi
     const Quaternion& rotationOffsetA, const Vector3& translationOffsetA, PhysicsRigidBody* b,
     const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
 {
-    PhysicsGenericConstraint* constraint = new PhysicsGenericConstraint(a, rotationOffsetA, 
-        translationOffsetA, b, rotationOffsetB, translationOffsetB);
-    setupConstraint(a, b, constraint);
+    checkConstraintRigidBodies(a, b);
+    PhysicsGenericConstraint* constraint = new PhysicsGenericConstraint(a, rotationOffsetA, translationOffsetA, b, rotationOffsetB, translationOffsetB);
+    addConstraint(a, b, constraint);
     return constraint;
 }
 
@@ -54,42 +64,43 @@ PhysicsHingeConstraint* PhysicsController::createHingeConstraint(PhysicsRigidBod
     const Quaternion& rotationOffsetA, const Vector3& translationOffsetA, PhysicsRigidBody* b, 
     const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
 {
-    PhysicsHingeConstraint* constraint = new PhysicsHingeConstraint(a, rotationOffsetA, 
-        translationOffsetA, b, rotationOffsetB, translationOffsetB);
-    setupConstraint(a, b, constraint);
+    checkConstraintRigidBodies(a, b);
+    PhysicsHingeConstraint* constraint = new PhysicsHingeConstraint(a, rotationOffsetA, translationOffsetA, b, rotationOffsetB, translationOffsetB);
+    addConstraint(a, b, constraint);
     return constraint;
 }
 
 PhysicsSocketConstraint* PhysicsController::createSocketConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
+    checkConstraintRigidBodies(a, b);
     PhysicsSocketConstraint* constraint = new PhysicsSocketConstraint(a, b);
-    setupConstraint(a, b, constraint);
+    addConstraint(a, b, constraint);
     return constraint;
 }
 
 PhysicsSocketConstraint* PhysicsController::createSocketConstraint(PhysicsRigidBody* a,
     const Vector3& translationOffsetA, PhysicsRigidBody* b, const Vector3& translationOffsetB)
 {
-    PhysicsSocketConstraint* constraint = new PhysicsSocketConstraint(a,
-        translationOffsetA, b, translationOffsetB);
-    setupConstraint(a, b, constraint);
+    checkConstraintRigidBodies(a, b);
+    PhysicsSocketConstraint* constraint = new PhysicsSocketConstraint(a,translationOffsetA, b, translationOffsetB);
+    addConstraint(a, b, constraint);
     return constraint;
 }
 
 PhysicsSpringConstraint* PhysicsController::createSpringConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b)
 {
+    checkConstraintRigidBodies(a, b);
     PhysicsSpringConstraint* constraint = new PhysicsSpringConstraint(a, b);
-    setupConstraint(a, b, constraint);
+    addConstraint(a, b, constraint);
     return constraint;
 }
 
-PhysicsSpringConstraint* PhysicsController::createSpringConstraint(PhysicsRigidBody* a,
-    const Quaternion& rotationOffsetA, const Vector3& translationOffsetA, PhysicsRigidBody* b, 
-    const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
+PhysicsSpringConstraint* PhysicsController::createSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,           
+                                                                   PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB)
 {
-    PhysicsSpringConstraint* constraint = new PhysicsSpringConstraint(a, rotationOffsetA, 
-        translationOffsetA, b, rotationOffsetB, translationOffsetB);
-    setupConstraint(a, b, constraint);
+    checkConstraintRigidBodies(a, b);
+    PhysicsSpringConstraint* constraint = new PhysicsSpringConstraint(a, rotationOffsetA, translationOffsetA, b, rotationOffsetB, translationOffsetB);
+    addConstraint(a, b, constraint);
     return constraint;
 }
 
@@ -103,9 +114,14 @@ void PhysicsController::setGravity(const Vector3& gravity)
     _gravity = gravity;
 
     if (_world)
-    {
         _world->setGravity(btVector3(_gravity.x, _gravity.y, _gravity.z));
-    }
+}
+
+void PhysicsController::drawDebug(const Matrix& viewProjection)
+{
+    _debugDrawer->begin(viewProjection);
+    _world->debugDrawWorld();
+    _debugDrawer->end();
 }
 
 void PhysicsController::initialize()
@@ -118,6 +134,10 @@ void PhysicsController::initialize()
     // Create the world.
     _world = new btDiscreteDynamicsWorld(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
     _world->setGravity(btVector3(_gravity.x, _gravity.y, _gravity.z));
+
+    // Set up debug drawing.
+    _debugDrawer = new DebugDrawer();
+    _world->setDebugDrawer(_debugDrawer);
 }
 
 void PhysicsController::finalize()
@@ -132,12 +152,12 @@ void PhysicsController::finalize()
 
 void PhysicsController::pause()
 {
-    // DUMMY FUNCTION
+    // Unused
 }
 
 void PhysicsController::resume()
 {
-    // DUMMY FUNCTION
+    // Unused
 }
 
 void PhysicsController::update(long elapsedTime)
@@ -189,6 +209,7 @@ void PhysicsController::update(long elapsedTime)
                 (*_listeners)[k]->statusEvent(_status);
             }
         }
+        
     }
 
     // All statuses are set with the DIRTY bit before collision processing occurs.
@@ -256,23 +277,50 @@ void PhysicsController::update(long elapsedTime)
         }
     }
 }
+    
 
 void PhysicsController::addRigidBody(PhysicsRigidBody* body)
 {
     _world->addRigidBody(body->_body);
     _bodies.push_back(body);
 }
+    
+void PhysicsController::removeRigidBody(PhysicsRigidBody* rigidBody)
+{
+    // Find the rigid body and remove it from the world.
+    for (int i = _world->getNumCollisionObjects() - 1; i >= 0 ; i--)
+    {
+        btCollisionObject* obj = _world->getCollisionObjectArray()[i];
+        if (rigidBody->_body == obj)
+        {
+            _world->removeCollisionObject(obj);
+            break;
+        }
+    }
+}
+
+PhysicsRigidBody* PhysicsController::getRigidBody(const btCollisionObject* collisionObject)
+{
+    // Find the rigid body and remove it from the world.
+    for (unsigned int i = 0; i < _bodies.size(); i++)
+    {
+        if (_bodies[i]->_body == collisionObject)
+            return _bodies[i];
+    }
+    
+    return NULL;
+}
 
-btCollisionShape* PhysicsController::getBox(const Vector3& min, const Vector3& max, const btVector3& scale)
+btCollisionShape* PhysicsController::createBox(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));
-    BULLET_NEW(btBoxShape, box, halfExtents);
+    btBoxShape* box = bullet_new<btBoxShape>(halfExtents);
     _shapes.push_back(box);
 
     return box;
 }
 
-btCollisionShape* PhysicsController::getSphere(float radius, const btVector3& scale)
+btCollisionShape* PhysicsController::createSphere(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.
@@ -281,23 +329,139 @@ btCollisionShape* PhysicsController::getSphere(float radius, const btVector3& sc
         uniformScale = scale.y();
     if (uniformScale < scale.z())
         uniformScale = scale.z();
-
-    BULLET_NEW(btSphereShape, sphere, uniformScale * radius);
+    
+    btSphereShape* sphere = bullet_new<btSphereShape>(uniformScale * radius);
     _shapes.push_back(sphere);
-
+    
     return sphere;
 }
 
-PhysicsRigidBody* PhysicsController::getPhysicsRigidBody(const btCollisionObject* collisionObject)
+btCollisionShape* PhysicsController::createMesh(PhysicsRigidBody* body)
 {
-    // Find the rigid body and remove it from the world.
-    for (unsigned int i = 0; i < _bodies.size(); i++)
+    // Retrieve the mesh rigid body data from the loaded scene.
+    const SceneLoader::MeshRigidBodyData* data = SceneLoader::getMeshRigidBodyData(body->_node->getId());
+
+    // Copy the scaled vertex position data to the rigid body's local buffer.
+    Matrix m;
+    Matrix::createScale(body->_node->getScaleX(), body->_node->getScaleY(), body->_node->getScaleZ(), &m);
+    unsigned int vertexCount = data->mesh->getVertexCount();
+    body->_vertexData = new float[vertexCount * 3];
+    Vector3 v;
+    int vertexStride = data->mesh->getVertexFormat()->getVertexSize();
+    for (unsigned int i = 0; i < vertexCount; i++)
     {
-        if (_bodies[i]->_body == collisionObject)
-            return _bodies[i];
+        v.set(*((float*)&data->vertexData[i * vertexStride + 0 * sizeof(float)]),
+              *((float*)&data->vertexData[i * vertexStride + 1 * sizeof(float)]),
+              *((float*)&data->vertexData[i * vertexStride + 2 * sizeof(float)]));
+        v *= m;
+        memcpy(&(body->_vertexData[i * 3]), &v, sizeof(float) * 3);
     }
+    
+    btTriangleIndexVertexArray* meshInterface = bullet_new<btTriangleIndexVertexArray>();
 
-    return NULL;
+    if (data->mesh->getPartCount() > 0)
+    {
+        PHY_ScalarType indexType = PHY_UCHAR;
+        int indexStride = 0;
+        MeshPart* meshPart = NULL;
+        for (unsigned int i = 0; i < data->mesh->getPartCount(); i++)
+        {
+            meshPart = data->mesh->getPart(i);
+
+            switch (meshPart->getIndexFormat())
+            {
+            case Mesh::INDEX8:
+                indexType = PHY_UCHAR;
+                indexStride = 1;
+                break;
+            case Mesh::INDEX16:
+                indexType = PHY_SHORT;
+                indexStride = 2;
+                break;
+            case Mesh::INDEX32:
+                indexType = PHY_INTEGER;
+                indexStride = 4;
+                break;
+            }
+
+            // Copy the index data to the rigid body's local buffer.
+            unsigned int indexDataSize = meshPart->getIndexCount() * indexStride;
+            unsigned char* indexData = new unsigned char[indexDataSize];
+            memcpy(indexData, data->indexData[i], indexDataSize);
+            body->_indexData.push_back(indexData);
+
+            // Create a btIndexedMesh object for the current mesh part.
+            btIndexedMesh indexedMesh;
+            indexedMesh.m_indexType = indexType;
+            indexedMesh.m_numTriangles = meshPart->getIndexCount() / 3;
+            indexedMesh.m_numVertices = meshPart->getIndexCount();
+            indexedMesh.m_triangleIndexBase = (const unsigned char*)body->_indexData[i];
+            indexedMesh.m_triangleIndexStride = indexStride;
+            indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
+            indexedMesh.m_vertexStride = sizeof(float)*3;
+            indexedMesh.m_vertexType = PHY_FLOAT;
+
+            // Add the indexed mesh data to the mesh interface.
+            meshInterface->addIndexedMesh(indexedMesh, indexType);
+        }
+    }
+    else
+    {
+        // Generate index data for the mesh locally in the rigid body.
+        unsigned int* indexData = new unsigned int[data->mesh->getVertexCount()];
+        for (unsigned int i = 0; i < data->mesh->getVertexCount(); i++)
+        {
+            indexData[i] = i;
+        }
+        body->_indexData.push_back((unsigned char*)indexData);
+
+        // Create a single btIndexedMesh object for the mesh interface.
+        btIndexedMesh indexedMesh;
+        indexedMesh.m_indexType = PHY_INTEGER;
+        indexedMesh.m_numTriangles = data->mesh->getVertexCount() / 3;
+        indexedMesh.m_numVertices = data->mesh->getVertexCount();
+        indexedMesh.m_triangleIndexBase = body->_indexData[0];
+        indexedMesh.m_triangleIndexStride = sizeof(unsigned int);
+        indexedMesh.m_vertexBase = (const unsigned char*)body->_vertexData;
+        indexedMesh.m_vertexStride = sizeof(float)*3;
+        indexedMesh.m_vertexType = PHY_FLOAT;
+
+        // Set the data in the mesh interface.
+        meshInterface->addIndexedMesh(indexedMesh, indexedMesh.m_indexType);
+    }
+
+    btBvhTriangleMeshShape* shape = bullet_new<btBvhTriangleMeshShape>(meshInterface, true);
+    _shapes.push_back(shape);
+
+    return shape;
+}
+
+void PhysicsController::addConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint)
+{
+    a->addConstraint(constraint);
+    if (b)
+    {
+        b->addConstraint(constraint);
+    }
+    
+    _world->addConstraint(constraint->_constraint);
+}
+
+bool PhysicsController::checkConstraintRigidBodies(PhysicsRigidBody* a, PhysicsRigidBody* b)
+{
+    if (!a->supportsConstraints())
+    {
+        WARN_VARG("Rigid body '%s' does not support constraints; unexpected behavior may occur.", a->_node->getId());
+        return false;
+    }
+    
+    if (b && !b->supportsConstraints())
+    {
+        WARN_VARG("Rigid body '%s' does not support constraints; unexpected behavior may occur.", b->_node->getId());
+        return false;
+    }
+
+    return true;
 }
 
 void PhysicsController::removeConstraint(PhysicsConstraint* constraint)
@@ -313,30 +477,136 @@ void PhysicsController::removeConstraint(PhysicsConstraint* constraint)
         }
     }
 }
+    
+PhysicsController::DebugDrawer::DebugDrawer()
+    : _mode(btIDebugDraw::DBG_DrawAabb | btIDebugDraw::DBG_DrawConstraintLimits | btIDebugDraw::DBG_DrawConstraints | 
+       btIDebugDraw::DBG_DrawContactPoints | btIDebugDraw::DBG_DrawWireframe), _effect(NULL), _positionAttrib(0), _colorAttrib(0),
+       _viewProjectionMatrixUniform(NULL), _viewProjection(NULL), _vertexData(NULL), _vertexCount(0), _vertexDataSize(0)
+{
+    // Unused
+}
 
-void PhysicsController::removeRigidBody(PhysicsRigidBody* rigidBody)
+PhysicsController::DebugDrawer::~DebugDrawer()
 {
-    // Find the rigid body and remove it from the world.
-    for (int i = _world->getNumCollisionObjects() - 1; i >= 0 ; i--)
+    SAFE_RELEASE(_effect);
+    SAFE_DELETE_ARRAY(_vertexData);
+}
+
+void PhysicsController::DebugDrawer::begin(const Matrix& viewProjection)
+{
+    _viewProjection = &viewProjection;
+    _vertexCount = 0;
+}
+
+void PhysicsController::DebugDrawer::end()
+{
+    // Lazy load the effect for drawing.
+    if (!_effect)
     {
-        btCollisionObject* obj = _world->getCollisionObjectArray()[i];
-        if (rigidBody->_body == obj)
+        // Vertex shader for drawing colored lines.
+        const char* vs_str = 
         {
-            _world->removeCollisionObject(obj);
-            break;
-        }
+            "uniform mat4 u_viewProjectionMatrix;\n"
+            "attribute vec4 a_position;\n"
+            "attribute vec4 a_color;\n"
+            "varying vec4 v_color;\n"
+            "void main(void) {\n"
+            "    v_color = a_color;\n"
+            "    gl_Position = u_viewProjectionMatrix * a_position;\n"
+            "}"
+        };
+        
+        // Fragment shader for drawing colored lines.
+        const char* fs_str = 
+        {
+        #ifdef OPENGL_ES
+            "precision highp float;\n"
+        #endif
+            "varying vec4 v_color;\n"
+            "void main(void) {\n"
+            "   gl_FragColor = v_color;\n"
+            "}"
+        };
+        
+        _effect = Effect::createFromSource(vs_str, fs_str);
+        _positionAttrib = _effect->getVertexAttribute("a_position");
+        _colorAttrib = _effect->getVertexAttribute("a_color");
+        _viewProjectionMatrixUniform = _effect->getUniform("u_viewProjectionMatrix");
     }
+    
+    // Bind the effect and set the vertex attributes.
+    _effect->bind();
+    GL_ASSERT( glEnableVertexAttribArray(_positionAttrib) );
+    GL_ASSERT( glEnableVertexAttribArray(_colorAttrib) );
+    GL_ASSERT( glVertexAttribPointer(_positionAttrib, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 7, _vertexData) );
+    GL_ASSERT( glVertexAttribPointer(_colorAttrib, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 7, &_vertexData[3]) );
+    
+    // Set the camera's view projection matrix and draw.
+    _effect->setValue( _viewProjectionMatrixUniform, _viewProjection);
+    GL_ASSERT( glDrawArrays(GL_LINES, 0, _vertexCount / 7) );
 }
 
-void PhysicsController::setupConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint)
+void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor)
 {
-    a->addConstraint(constraint);
-    if (b)
+    // Allocate extra space in the vertex data batch if it is needed.
+    if (_vertexDataSize - _vertexCount < 14)
     {
-        b->addConstraint(constraint);
+        if (_vertexDataSize > 0)
+        {
+            unsigned int newVertexDataSize = _vertexDataSize * 2;
+            float* newVertexData = new float[newVertexDataSize];
+            memcpy(newVertexData, _vertexData, _vertexDataSize * sizeof(float));
+            SAFE_DELETE_ARRAY(_vertexData);
+            _vertexData = newVertexData;
+            _vertexDataSize = newVertexDataSize;
+        }
+        else
+        {
+            _vertexDataSize = INITIAL_CAPACITY;
+            _vertexData = new float[_vertexDataSize];
+        }
     }
+    
+    // Create the vertex data for the line and copy it into the batch.
+    float vertexData[] = 
+    {
+        from.getX(), from.getY(), from.getZ(), 
+        fromColor.getX(), fromColor.getY(), fromColor.getZ(), 1.0f,
+        to.getX(), to.getY(), to.getZ(),
+        toColor.getX(), toColor.getY(), toColor.getZ(), 1.0f
+    };
+    memcpy(&_vertexData[_vertexCount], vertexData, sizeof(float) * 14);
+    _vertexCount += 14;
+}
 
-    _world->addConstraint(constraint->_constraint);
+void PhysicsController::DebugDrawer::drawLine(const btVector3& from, const btVector3& to, const btVector3& color)
+{
+    drawLine(from, to, color, color);
+}
+
+void PhysicsController::DebugDrawer::drawContactPoint(const btVector3& pointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color)
+{
+    drawLine(pointOnB, pointOnB + normalOnB, color);
+}
+
+void PhysicsController::DebugDrawer::reportErrorWarning(const char* warningString)
+{
+    WARN(warningString);
+}
+
+void PhysicsController::DebugDrawer::draw3dText(const btVector3& location, const char* textString)
+{
+    WARN("Physics debug drawing: 3D text is not supported.");
+}
+
+void PhysicsController::DebugDrawer::setDebugMode(int mode)
+{
+    _mode = mode;
+}
+
+int	PhysicsController::DebugDrawer::getDebugMode() const
+{
+    return _mode;
 }
 
 }

+ 69 - 27
gameplay/src/PhysicsController.h

@@ -47,17 +47,17 @@ public:
         };
 
         /**
-         * Handles when the physics world status changes.
+         * Handles when a physics world status event occurs.
          */
         virtual void statusEvent(EventType type) = 0;
     };
 
     /**
-     * Adds a status listener.
+     * Adds a listener to the physics controller.
      * 
      * @param listener The listener to add.
      */
-    void addStatusListener(Listener* listener);
+    void addStatusListener(PhysicsController::Listener* listener);
 
     /**
      * Creates a fixed constraint.
@@ -93,10 +93,8 @@ public:
      * @param translationOffsetB The translation offset for the second rigid body
      *      (in its local space) with respect to the constraint joint (optional).
      */
-    PhysicsGenericConstraint* createGenericConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, 
-                                                     const Vector3& translationOffsetA, PhysicsRigidBody* b = NULL, 
-                                                     const Quaternion& rotationOffsetB = Quaternion(), 
-                                                     const Vector3& translationOffsetB = Vector3());
+    PhysicsGenericConstraint* createGenericConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA, 
+                                                      PhysicsRigidBody* b = NULL, const Quaternion& rotationOffsetB = Quaternion(), const Vector3& translationOffsetB = Vector3());
 
     /**
      * Creates a hinge constraint.
@@ -113,10 +111,8 @@ public:
      * @param translationOffsetB The translation offset for the second rigid body
      *      (in its local space) with respect to the constraint joint (optional).
      */
-    PhysicsHingeConstraint* createHingeConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, 
-                                                  const Vector3& translationOffsetA, PhysicsRigidBody* b = NULL, 
-                                                  const Quaternion& rotationOffsetB = Quaternion(), 
-                                                  const Vector3& translationOffsetB = Vector3());
+    PhysicsHingeConstraint* createHingeConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
+                                                  PhysicsRigidBody* b = NULL, const Quaternion& rotationOffsetB = Quaternion(), const Vector3& translationOffsetB = Vector3());
 
     /**
      * Creates a socket constraint so that the rigid body (or bodies) is
@@ -169,9 +165,8 @@ public:
      * @param translationOffsetB The translation offset for the second rigid body
      *      (in its local space) with respect to the constraint joint (optional).
      */
-    PhysicsSpringConstraint* createSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, 
-                                                    const Vector3& translationOffsetA, PhysicsRigidBody* b, 
-                                                    const Quaternion& rotationOffsetB, const Vector3& translationOffsetB);
+    PhysicsSpringConstraint* createSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,          
+                                                    PhysicsRigidBody* b, const Quaternion& rotationOffsetB, const Vector3& translationOffsetB);
 
     /**
      * Gets the gravity vector for the simulated physics world.
@@ -186,6 +181,13 @@ public:
      * @param gravity The gravity vector.
      */
     void setGravity(const Vector3& gravity);
+    
+    /**
+     * Draws debugging information (rigid body outlines, etc.) using the given view projection matrix.
+     * 
+     * @param viewProjection The view projection matrix to use when drawing.
+     */
+    void drawDebug(const Matrix& viewProjection);
 
 private:
 
@@ -226,35 +228,75 @@ private:
 
     // Adds the given rigid body to the world.
     void addRigidBody(PhysicsRigidBody* body);
-
-    // Creates a box collision shape to be used in the creation of a rigid body.
-    btCollisionShape* getBox(const Vector3& min, const Vector3& max, const btVector3& scale);
-
+    
+    // Removes the given rigid body from the simulated physics world.
+    void removeRigidBody(PhysicsRigidBody* rigidBody);
+    
     // Gets the corresponding GamePlay object for the given Bullet object.
-    PhysicsRigidBody* getPhysicsRigidBody(const btCollisionObject* collisionObject);
+    PhysicsRigidBody* getRigidBody(const btCollisionObject* collisionObject);
+    
+    // Creates a box collision shape to be used in the creation of a rigid body.
+    btCollisionShape* createBox(const Vector3& min, const Vector3& max, const btVector3& scale);
 
     // Creates a sphere collision shape to be used in the creation of a rigid body.
-    btCollisionShape* getSphere(float radius, const btVector3& scale);
+    btCollisionShape* createSphere(float radius, const btVector3& scale);
+
+    // Creates a triangle mesh collision shape to be used in the creation of a rigid body.
+    btCollisionShape* createMesh(PhysicsRigidBody* body);
 
+    // Sets up the given constraint for the given two rigid bodies.
+    void addConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint);
+
+    // Checks whether constraints are supported for the given rigid bodies and emits a warning if they are not.
+    bool checkConstraintRigidBodies(PhysicsRigidBody* a, PhysicsRigidBody* b);
+    
     // Removes the given constraint from the simulated physics world.
     void removeConstraint(PhysicsConstraint* constraint);
+    
+    // Draws Bullet debug information.
+    class DebugDrawer : public btIDebugDraw
+    {
+    public:
 
-    // Removes the given rigid body from the simulated physics world.
-    void removeRigidBody(PhysicsRigidBody* rigidBody);
-
-    // Sets up the given constraint for the given two rigid bodies.
-    void setupConstraint(PhysicsRigidBody* a, PhysicsRigidBody* b, PhysicsConstraint* constraint);
+        DebugDrawer();        
+        ~DebugDrawer();
+        
+        void begin(const Matrix& viewProjection);
+        void end();
+
+        // Overridden Bullet functions from btIDebugDraw.
+        void drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor);        
+        void drawLine(const btVector3& from, const btVector3& to, const btVector3& color);        
+        void drawContactPoint(const btVector3& pointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color);        
+        void reportErrorWarning(const char* warningString);        
+        void draw3dText(const btVector3& location, const char* textString);        
+        void setDebugMode(int mode);        
+        int	getDebugMode() const;
+        
+    private:
+        
+        int _mode;
+        Effect* _effect;
+        VertexAttribute _positionAttrib;
+        VertexAttribute _colorAttrib;
+        Uniform* _viewProjectionMatrixUniform;
+        const Matrix* _viewProjection;
+        float* _vertexData;
+        unsigned int _vertexCount;
+        unsigned int _vertexDataSize;
+    };
     
-    Vector3 _gravity;
     btDefaultCollisionConfiguration* _collisionConfiguration;
     btCollisionDispatcher* _dispatcher;
     btBroadphaseInterface* _overlappingPairCache;
     btSequentialImpulseConstraintSolver* _solver;
     btDynamicsWorld* _world;
     btAlignedObjectArray<btCollisionShape*> _shapes;
+    DebugDrawer* _debugDrawer;
     Listener::EventType _status;
-    std::vector<PhysicsRigidBody*> _bodies;
     std::vector<Listener*>* _listeners;
+    std::vector<PhysicsRigidBody*> _bodies;
+    Vector3 _gravity;
 };
 
 }

+ 1 - 1
gameplay/src/PhysicsHingeConstraint.cpp

@@ -41,7 +41,7 @@ PhysicsHingeConstraint::PhysicsHingeConstraint(PhysicsRigidBody* a, const Quater
     
 PhysicsHingeConstraint::~PhysicsHingeConstraint()
 {
-    // DUMMY FUNCTION
+    // Unused
 }
 
 }

+ 192 - 51
gameplay/src/PhysicsRigidBody.cpp

@@ -7,57 +7,50 @@
 namespace gameplay
 {
 
-const int PhysicsRigidBody::Listener::DIRTY = 0x01;
-const int PhysicsRigidBody::Listener::COLLISION = 0x02;
-const int PhysicsRigidBody::Listener::REGISTERED = 0x04;
+const int PhysicsRigidBody::Listener::DIRTY         = 0x01;
+const int PhysicsRigidBody::Listener::COLLISION     = 0x02;
+const int PhysicsRigidBody::Listener::REGISTERED    = 0x04;
+
+// Internal value used for creating mesh rigid bodies.
+#define SHAPE_MESH ((PhysicsRigidBody::Type)(PhysicsRigidBody::SHAPE_NONE + 1))
 
 PhysicsRigidBody::PhysicsRigidBody(Node* node, PhysicsRigidBody::Type type, float mass, 
         float friction, float restitution, float linearDamping, float angularDamping)
         : _shape(NULL), _body(NULL), _node(node), _listeners(NULL), _angularVelocity(NULL),
-        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL)
+        _anisotropicFriction(NULL), _gravity(NULL), _linearVelocity(NULL), _vertexData(NULL),
+        _indexData(NULL)
 {
     switch (type)
     {
-        case PhysicsRigidBody::SHAPE_BOX:
+        case SHAPE_BOX:
         {
             const BoundingBox& box = node->getModel()->getMesh()->getBoundingBox();
-
-            PhysicsController* physics = Game::getInstance()->getPhysicsController();
-            _shape = physics->getBox(box.min, box.max, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
-            
-            // Use the center of the bounding box as the center of mass offset.
-            Vector3 c(box.min, box.max);
-            c.scale(0.5f);
-            c.add(box.min);
-            c.negate();
-
-            if (c.lengthSquared() > MATH_EPSILON)
-                _body = createBulletRigidBody(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
-            else
-                _body = createBulletRigidBody(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
-
+            _shape = Game::getInstance()->getPhysicsController()->createBox(box.min, box.max, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
             break;
         }
-        case PhysicsRigidBody::SHAPE_SPHERE:
+        case SHAPE_SPHERE:
         {
             const BoundingSphere& sphere = node->getModel()->getMesh()->getBoundingSphere();
-
-            PhysicsController* physics = Game::getInstance()->getPhysicsController();
-            _shape = physics->getSphere(sphere.radius, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
-
-            // Use the center of the bounding sphere as the center of mass offset.
-            Vector3 c(sphere.center);
-            c.negate();
-
-            if (c.lengthSquared() > MATH_EPSILON)
-                _body = createBulletRigidBody(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
-            else
-                _body = createBulletRigidBody(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
-
+            _shape = Game::getInstance()->getPhysicsController()->createSphere(sphere.radius, btVector3(node->getScaleX(), node->getScaleY(), node->getScaleZ()));
+            break;
+        }
+        case SHAPE_MESH:
+        {
+            _shape = Game::getInstance()->getPhysicsController()->createMesh(this);
             break;
         }
     }
 
+    // Use the center of the bounding sphere as the center of mass offset.
+    Vector3 c(node->getModel()->getMesh()->getBoundingSphere().center);
+    c.negate();
+
+    // Create the Bullet rigid body.
+    if (c.lengthSquared() > MATH_EPSILON)
+        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping, &c);
+    else
+        _body = createRigidBodyInternal(_shape, mass, node, friction, restitution, linearDamping, angularDamping);
+
     // Add the rigid body to the physics world.
     Game::getInstance()->getPhysicsController()->addRigidBody(this);
 }
@@ -79,11 +72,10 @@ PhysicsRigidBody::~PhysicsRigidBody()
         if (_body->getMotionState())
             delete _body->getMotionState();
 
-        if (_shape)
-            BULLET_DELETE(_shape);
+        SAFE_DELETE(_shape);
 
         Game::getInstance()->getPhysicsController()->removeRigidBody(this);
-        BULLET_DELETE(_body);
+        SAFE_DELETE(_body);
     }
 
     SAFE_DELETE(_listeners);
@@ -91,6 +83,11 @@ PhysicsRigidBody::~PhysicsRigidBody()
     SAFE_DELETE(_anisotropicFriction);
     SAFE_DELETE(_gravity);
     SAFE_DELETE(_linearVelocity);
+    SAFE_DELETE_ARRAY(_vertexData);
+    for (unsigned int i = 0; i < _indexData.size(); i++)
+    {
+        SAFE_DELETE_ARRAY(_indexData[i]);
+    }
 }
 
 void PhysicsRigidBody::addCollisionListener(Listener* listener, PhysicsRigidBody* body)
@@ -166,13 +163,150 @@ bool PhysicsRigidBody::collidesWith(PhysicsRigidBody* body)
     return callback.result;
 }
 
-btRigidBody* PhysicsRigidBody::createBulletRigidBody(btCollisionShape* shape, float mass, Node* node,
-    float friction, float restitution, float linearDamping, float angularDamping, const Vector3* centerOfMassOffset)
+PhysicsRigidBody* PhysicsRigidBody::create(Node* node, const char* filePath)
+{
+    assert(filePath);
+
+    // Load the rigid body properties from file.
+    Properties* properties = Properties::create(filePath);
+    assert(properties);
+    if (properties == NULL)
+    {
+        WARN_VARG("Failed to load rigid body file: %s", filePath);
+        return NULL;
+    }
+
+    PhysicsRigidBody* body = create(node, properties->getNextNamespace());
+    SAFE_DELETE(properties);
+
+    return body;
+}
+
+PhysicsRigidBody* PhysicsRigidBody::create(Node* node, Properties* properties)
+{
+    // Check if the properties is valid and has a valid namespace.
+    assert(properties);
+    if (!properties || !(strcmp(properties->getNamespace(), "rigidbody") == 0))
+    {
+        WARN("Failed to load rigid body from properties object: must be non-null object and have namespace equal to \'rigidbody\'.");
+        return NULL;
+    }
+
+    // Set values to their defaults.
+    PhysicsRigidBody::Type type = PhysicsRigidBody::SHAPE_NONE;
+    float mass = 0.0;
+    float friction = 0.5;
+    float restitution = 0.0;
+    float linearDamping = 0.0;
+    float angularDamping = 0.0;
+    bool kinematic = false;
+    Vector3* gravity = NULL;
+    Vector3* anisotropicFriction = NULL;
+
+    // Load the defined properties.
+    properties->rewind();
+    const char* name;
+    while (name = properties->getNextProperty())
+    {
+        if (strcmp(name, "type") == 0)
+        {
+            std::string typeStr = properties->getString();
+            if (typeStr == "BOX")
+                type = SHAPE_BOX;
+            else if (typeStr == "SPHERE")
+                type = SHAPE_SPHERE;
+            else if (typeStr == "MESH")
+                type = SHAPE_MESH;
+            else
+            {
+                WARN_VARG("Could not create rigid body; unsupported value for rigid body type: '%s'.", typeStr.c_str());
+                return NULL;
+            }
+        }
+        else if (strcmp(name, "mass") == 0)
+        {
+            mass = properties->getFloat();
+        }
+        else if (strcmp(name, "friction") == 0)
+        {
+            friction = properties->getFloat();
+        }
+        else if (strcmp(name, "restitution") == 0)
+        {
+            restitution = properties->getFloat();
+        }
+        else if (strcmp(name, "linearDamping") == 0)
+        {
+            linearDamping = properties->getFloat();
+        }
+        else if (strcmp(name, "angularDamping") == 0)
+        {
+            angularDamping = properties->getFloat();
+        }
+        else if (strcmp(name, "kinematic") == 0)
+        {
+            kinematic = properties->getBool();
+        }
+        else if (strcmp(name, "gravity") == 0)
+        {
+            gravity = new Vector3();
+            properties->getVector3(NULL, gravity);
+        }
+        else if (strcmp(name, "anisotropicFriction") == 0)
+        {
+            anisotropicFriction = new Vector3();
+            properties->getVector3(NULL, anisotropicFriction);
+        }
+    }
+
+    // If the rigid body type is equal to mesh, check that the node's mesh's primitive type is supported.
+    if (type == SHAPE_MESH)
+    {
+        Mesh* mesh = node->getModel()->getMesh();
+
+        switch (mesh->getPrimitiveType())
+        {
+        case Mesh::TRIANGLES:
+            break;
+        case Mesh::LINES:
+        case Mesh::LINE_STRIP:
+        case Mesh::POINTS:
+        case Mesh::TRIANGLE_STRIP:
+            WARN("Mesh rigid bodies are currently only supported on meshes with primitive type equal to TRIANGLES.");
+
+            SAFE_DELETE(gravity);
+            SAFE_DELETE(anisotropicFriction);
+            return NULL;
+        }
+    }
+
+    // Create the rigid body.
+    PhysicsRigidBody* body = new PhysicsRigidBody(node, type, mass, friction, restitution, linearDamping, angularDamping);
+
+    // Set any initially defined properties.
+    if (kinematic)
+        body->setKinematic(kinematic);
+    if (gravity)
+        body->setGravity(*gravity);
+    if (anisotropicFriction)
+        body->setAnisotropicFriction(*anisotropicFriction);
+
+    // Clean up any loaded properties that are on the heap.
+    SAFE_DELETE(gravity);
+    SAFE_DELETE(anisotropicFriction);
+
+    return body;
+}
+
+btRigidBody* PhysicsRigidBody::createRigidBodyInternal(btCollisionShape* shape, float mass, Node* node,
+                                                       float friction, float restitution, float linearDamping, float angularDamping, 
+                                                       const Vector3* centerOfMassOffset)
 {
-    // If the mass is non-zero, then the object is dynamic
-    // and we need to calculate the local inertia.
+    // If the mass is non-zero, then the object is dynamic so we calculate the local 
+    // inertia. However, if the collision shape is a triangle mesh, we don't calculate 
+    // inertia since Bullet doesn't currently support this.
     btVector3 localInertia(0.0, 0.0, 0.0);
-    if (mass != 0.0)
+    if (mass != 0.0 && shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE)
         shape->calculateLocalInertia(mass, localInertia);
 
     // Create the Bullet physics rigid body object.
@@ -182,7 +316,7 @@ btRigidBody* PhysicsRigidBody::createBulletRigidBody(btCollisionShape* shape, fl
     rbInfo.m_restitution = restitution;
     rbInfo.m_linearDamping = linearDamping;
     rbInfo.m_angularDamping = angularDamping;
-    BULLET_NEW(btRigidBody, body, rbInfo);
+    btRigidBody* body = bullet_new<btRigidBody>(rbInfo);
 
     return body;
 }
@@ -204,23 +338,29 @@ void PhysicsRigidBody::removeConstraint(PhysicsConstraint* constraint)
     }
 }
 
+bool PhysicsRigidBody::supportsConstraints()
+{
+    return _shape->getShapeType() != TRIANGLE_MESH_SHAPE_PROXYTYPE;
+}
+
 PhysicsRigidBody::CollisionPair::CollisionPair(PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
     : _rbA(rbA), _rbB(rbB)
 {
-    // Unsued
+    // Unused
 }
 
 PhysicsRigidBody::Listener::~Listener()
 {
-    // Unsued
+    // Unused
 }
 
-btScalar PhysicsRigidBody::Listener::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a,
-    int partIdA, int indexA, const btCollisionObject* b, int partIdB, int indexB)
+btScalar PhysicsRigidBody::Listener::addSingleResult(btManifoldPoint& cp, 
+                                                     const btCollisionObject* a, int partIdA, int indexA, 
+                                                     const btCollisionObject* b, int partIdB, int indexB)
 {
     // Get pointers to the PhysicsRigidBody objects.
-    PhysicsRigidBody* rbA = Game::getInstance()->getPhysicsController()->getPhysicsRigidBody(a);
-    PhysicsRigidBody* rbB = Game::getInstance()->getPhysicsController()->getPhysicsRigidBody(b);
+    PhysicsRigidBody* rbA = Game::getInstance()->getPhysicsController()->getRigidBody(a);
+    PhysicsRigidBody* rbB = Game::getInstance()->getPhysicsController()->getRigidBody(b);
     
     // If the given rigid body pair has collided in the past, then
     // we notify the listener only if the pair was not colliding
@@ -244,8 +384,9 @@ btScalar PhysicsRigidBody::Listener::addSingleResult(btManifoldPoint& cp, const
     return 0.0f;
 }
 
-btScalar PhysicsRigidBody::CollidesWithCallback::addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA,
-            int indexA, const btCollisionObject* b, int partIdB, int indexB)
+btScalar PhysicsRigidBody::CollidesWithCallback::addSingleResult(btManifoldPoint& cp, 
+                                                                 const btCollisionObject* a, int partIdA, int indexA, 
+                                                                 const btCollisionObject* b, int partIdB, int indexB)
 {
     result = true;
     return 0.0f;

+ 36 - 13
gameplay/src/PhysicsRigidBody.h

@@ -86,20 +86,18 @@ public:
          * @param contactPoint The point (in world space) where the collision occurred.
          */
         virtual void collisionEvent(const CollisionPair& collisionPair, const Vector3& contactPoint) = 0;
-
-        /**
-         * Internal function used for Bullet integration (do not use or override).
-         */
-        btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObject* a, int partIdA,
-            int indexA, const btCollisionObject* b, int partIdB, int indexB);
         
     protected:
-
-        /** 
-         * Holds the collision status for each pair of rigid bodies. 
+        
+        /**
+         * Internal function used for Bullet integration (do not use or override).
          */
-        std::map<CollisionPair, int> _collisionStatus;
+        btScalar addSingleResult(btManifoldPoint& cp, 
+                                 const btCollisionObject* a, int partIdA, int indexA, 
+                                 const btCollisionObject* b, int partIdB, int indexB);
 
+        std::map<CollisionPair, int> _collisionStatus;  // Holds the collision status for each pair of rigid bodies. 
+        
     private:
 
         // Internal constant.
@@ -312,17 +310,40 @@ private:
      */
     PhysicsRigidBody(const PhysicsRigidBody& body);
 
+    /**
+     * Creates a rigid body from the rigid body file at the given path.
+     * 
+     * @param node The node to create a rigid body for; note that the node must have
+     *      a model attached to it prior to creating a rigid body for it.
+     * @param filePath The path to the rigid body file.
+     * @return The rigid body or <code>NULL</code> if the rigid body could not be loaded.
+     */
+    static PhysicsRigidBody* create(Node* node, const char* filePath);
+
+    /**
+     * Creates a rigid body from the specified properties object.
+     * 
+     * @param node The node to create a rigid body for; note that the node must have
+     *      a model attached to it prior to creating a rigid body for it.
+     * @param properties The properties object defining the rigid body (must have namespace equal to 'rigidbody').
+     * @return The newly created rigid body, or <code>NULL</code> if the rigid body failed to load.
+     */
+    static PhysicsRigidBody* create(Node* node, Properties* properties);
+
     // Creates the underlying Bullet Physics rigid body object
     // for a PhysicsRigidBody object using the given parameters.
-    static btRigidBody* createBulletRigidBody(btCollisionShape* shape, float mass, Node* node,
-                                              float friction, float restitution, float linearDamping, float angularDamping,
-                                              const Vector3* centerOfMassOffset = NULL);
+    static btRigidBody* createRigidBodyInternal(btCollisionShape* shape, float mass, Node* node,
+                                                float friction, float restitution, float linearDamping, float angularDamping,
+                                                const Vector3* centerOfMassOffset = NULL);
 
     // Adds a constraint to this rigid body.
     void addConstraint(PhysicsConstraint* constraint);
 
     // Removes a constraint from this rigid body (used by the constraint destructor).
     void removeConstraint(PhysicsConstraint* constraint);
+    
+    // Whether or not the rigid body supports constraints fully.
+    bool supportsConstraints();
 
     // Internal class used to implement the collidesWith(PhysicsRigidBody*) function.
     struct CollidesWithCallback : public btCollisionWorld::ContactResultCallback
@@ -343,6 +364,8 @@ private:
     mutable Vector3* _anisotropicFriction;
     mutable Vector3* _gravity;
     mutable Vector3* _linearVelocity;
+    float* _vertexData;
+    std::vector<unsigned char*> _indexData;
 };
 
 }

+ 2 - 2
gameplay/src/PlatformMacOS.mm

@@ -217,8 +217,8 @@ static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTime
         __pitch -= (float)(point.y - (WINDOW_HEIGHT - __ly)) * ACCELEROMETER_Y_FACTOR;
     
         // Clamp the values to the valid range.
-        __roll = fmaxf(fminf(__roll, 90.0), -90.0);
-        __pitch = fmaxf(fminf(__pitch, 90.0), -90.0);
+        __roll = max(min(__roll, 90.0f), -90.0f);
+        __pitch = max(min(__pitch, 90.0f), -90.0f);
     
         // Update the last X/Y values.
         __lx = point.x;

+ 2 - 4
gameplay/src/PlatformWin32.cpp

@@ -5,8 +5,6 @@
 #include "FileSystem.h"
 #include "Game.h"
 
-using namespace std;
-
 static long __timeTicksPerMillis;
 static long __timeStart;
 static long __timeAbsolute;
@@ -307,8 +305,8 @@ LRESULT CALLBACK __WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
             __pitch += (float)(HIWORD(lParam) - ly) * ACCELEROMETER_Y_FACTOR;
 
             // Clamp the values to the valid range.
-            __roll = max(min(__roll, 90.0), -90.0);
-            __pitch = max(min(__pitch, 90.0), -90.0);
+            __roll = max(min(__roll, 90.0f), -90.0f);
+            __pitch = max(min(__pitch, 90.0f), -90.0f);
 
             // Update the last X/Y values.
             lx = LOWORD(lParam);

+ 35 - 3
gameplay/src/Properties.cpp

@@ -1,6 +1,7 @@
 #include "Base.h"
 #include "Properties.h"
 #include "FileSystem.h"
+#include "Quaternion.h"
 
 namespace gameplay
 {
@@ -48,10 +49,14 @@ void Properties::readProperties(FILE* file)
     char* value;
     char* rc;
 
-    while (!feof(file))
+    while (true)
     {
         skipWhiteSpace(file);
 
+        // Stop when we have reached the end of the file.
+        if (feof(file))
+            break;
+
         // Read the next line.
         rc = fgets(line, 2048, file);
         if (rc == NULL)
@@ -187,8 +192,10 @@ void Properties::skipWhiteSpace(FILE* file)
         c = fgetc(file);
     } while (isspace(c));
 
-    // We found a non-whitespace character; put the cursor back in front of it.
-    fseek(file, -1, SEEK_CUR);
+    // If we are not at the end of the file, then since we found a
+    // non-whitespace character, we put the cursor back in front of it.
+    if (c != EOF)
+        fseek(file, -1, SEEK_CUR);
 }
 
 char* Properties::trimWhiteSpace(char *str)
@@ -577,6 +584,31 @@ bool Properties::getVector4(const char* name, Vector4* out) const
     return false;
 }
 
+bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) const
+{
+    assert(out);
+
+    const char* valueString = getString(name);
+    if (valueString)
+    {
+        float x, y, z, theta;
+        int scanned;
+        scanned = sscanf(valueString, "%f,%f,%f,%f", &x, &y, &z, &theta);
+        if (scanned != 4)
+        {
+            LOG_ERROR_VARG("Error parsing property: %s", name);
+            out->set(0.0f, 0.0f, 0.0f, 1.0f);
+            return false;
+        }
+
+        out->set(Vector3(x, y, z), MATH_DEG_TO_RAD(theta));
+        return true;
+    }
+    
+    out->set(0.0f, 0.0f, 0.0f, 1.0f);
+    return false;
+}
+
 bool Properties::getColor(const char* name, Vector3* out) const
 {
     assert(out);

+ 13 - 0
gameplay/src/Properties.h

@@ -326,6 +326,19 @@ public:
      */
     bool getVector4(const char* name, Vector4* out) const;
 
+    /**
+     * Interpret the value of the given property as a Quaternion specified as an axis angle.
+     * If the property does not exist, out will be set to Quaternion().
+     * If the property exists but could not be scanned, an error will be logged and out will be set
+     * to Quaternion().
+     *
+     * @param name The name of the property to interpret, or NULL to return the current property's value.
+     * @param out The quaternion 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 getQuaternionFromAxisAngle(const char* name, Quaternion* out) const;
+
     /**
      * Interpret the value of the given property as an RGB color in hex and write this color to a Vector3.
      * E.g. 0xff0000 represents red and sets the vector to (1, 0, 0).

+ 5 - 5
gameplay/src/Ray.cpp

@@ -94,11 +94,11 @@ float Ray::intersects(const Frustum& frustum) const
 
     // Otherwise, the intersection distance is the minimum positive intersection distance.
     float d = (nD > 0.0f) ? nD : 0.0f;
-    d = (fD > 0.0f) ? ( (d == 0.0f) ? fD : fminf(fD, d) ) : d;
-    d = (lD > 0.0f) ? ( (d == 0.0f) ? lD : fminf(lD, d) ) : d;
-    d = (rD > 0.0f) ? ( (d == 0.0f) ? rD : fminf(rD, d) ) : d;
-    d = (tD > 0.0f) ? ( (d == 0.0f) ? bD : fminf(bD, d) ) : d;
-    d = (bD > 0.0f) ? ( (d == 0.0f) ? tD : fminf(tD, d) ) : d;
+    d = (fD > 0.0f) ? ( (d == 0.0f) ? fD : min(fD, d) ) : d;
+    d = (lD > 0.0f) ? ( (d == 0.0f) ? lD : min(lD, d) ) : d;
+    d = (rD > 0.0f) ? ( (d == 0.0f) ? rD : min(rD, d) ) : d;
+    d = (tD > 0.0f) ? ( (d == 0.0f) ? bD : min(bD, d) ) : d;
+    d = (bD > 0.0f) ? ( (d == 0.0f) ? tD : min(tD, d) ) : d;
 
     return d;
 }

+ 8 - 8
gameplay/src/Rectangle.cpp

@@ -89,10 +89,10 @@ bool Rectangle::contains(const Rectangle& r) const
 
 bool Rectangle::intersects(float x, float y, float width, float height) const
 {
-    const float left   = fmaxf(this->x, x);
-    const float top    = fmaxf(this->y, y);
-    const float right  = fminf(x + width, x + width);
-    const float bottom = fminf(y + height, y + height);
+    const float left   = max(this->x, x);
+    const float top    = max(this->y, y);
+    const float right  = min(x + width, x + width);
+    const float bottom = min(y + height, y + height);
 
     return (right > left && bottom > top);
 }
@@ -104,10 +104,10 @@ bool Rectangle::intersects(const Rectangle& r) const
 
 void Rectangle::combine(const Rectangle& r1, const Rectangle& r2, Rectangle* dst)
 {
-    dst->x = fminf(r1.x, r2.x);
-    dst->y = fminf(r1.y, r2.y);
-    dst->width = fmaxf(r1.x + r1.width, r2.x + r2.width) - dst->x;
-    dst->height = fmaxf(r1.y + r1.height, r2.y + r2.height) - dst->y;
+    dst->x = min(r1.x, r2.x);
+    dst->y = min(r1.y, r2.y);
+    dst->width = max(r1.x + r1.width, r2.x + r2.width) - dst->x;
+    dst->height = max(r1.y + r1.height, r2.y + r2.height) - dst->y;
 }
 
 void Rectangle::inflate(float horizontalAmount, float verticalAmount)

+ 6 - 0
gameplay/src/Scene.cpp

@@ -1,6 +1,7 @@
 #include "Base.h"
 #include "AudioListener.h"
 #include "Scene.h"
+#include "SceneLoader.h"
 
 namespace gameplay
 {
@@ -36,6 +37,11 @@ Scene* Scene::createScene()
     return new Scene();
 }
 
+Scene* Scene::load(const char* filePath)
+{
+    return SceneLoader::load(filePath);
+}
+
 const char* Scene::getId() const
 {
     return _id.c_str();

+ 9 - 0
gameplay/src/Scene.h

@@ -20,6 +20,15 @@ public:
      */
     static Scene* createScene();
 
+    /**
+     * Loads a scene from the given '.scene' file.
+     * 
+     * @param filePath The path to the '.scene' file to load from.
+     * @return The loaded scene or <code>NULL</code> if the scene
+     *      could not be loaded from the given file.
+     */
+    static Scene* load(const char* filePath);
+
     /**
      * Gets the identifier for the scene.
      *

+ 936 - 0
gameplay/src/SceneLoader.cpp

@@ -0,0 +1,936 @@
+#include "Base.h"
+#include "Game.h"
+#include "Package.h"
+#include "SceneLoader.h"
+
+namespace gameplay
+{
+
+// Static member variables.
+std::map<std::string, Properties*> SceneLoader::_propertiesFromFile;
+std::vector<SceneLoader::SceneAnimation> SceneLoader::_animations;
+std::vector<SceneLoader::SceneNodeProperty> SceneLoader::_nodeProperties;
+std::vector<std::string> SceneLoader::_nodesWithMeshRB;
+std::map<std::string, SceneLoader::MeshRigidBodyData>* SceneLoader::_meshRigidBodyData = NULL;
+
+Scene* SceneLoader::load(const char* filePath)
+{
+    assert(filePath);
+
+    // Load the scene properties from file.
+    Properties* properties = Properties::create(filePath);
+    assert(properties);
+    if (properties == NULL)
+    {
+        WARN_VARG("Failed to load scene file: %s", filePath);
+        return NULL;
+    }
+
+    // Check if the properties object is valid and has a valid namespace.
+    Properties* sceneProperties = properties->getNextNamespace();
+    assert(sceneProperties);
+    if (!sceneProperties || !(strcmp(sceneProperties->getNamespace(), "scene") == 0))
+    {
+        WARN("Failed to load scene from properties object: must be non-null object and have namespace equal to 'scene'.");
+        SAFE_DELETE(properties);
+        return NULL;
+    }
+    
+
+    // Build the node URL/property and animation reference tables and load the referenced files.
+    buildReferenceTables(sceneProperties);
+    loadReferencedFiles();
+
+    // Calculate the node IDs that need to be loaded with mesh rigid body support.
+    calculateNodesWithMeshRigidBodies(sceneProperties);
+
+    // Set up for storing the mesh rigid body data.
+    if (_nodesWithMeshRB.size() > 0)
+    {
+        // We do not currently support loading more than one scene simultaneously.
+        if (_meshRigidBodyData)
+        {
+            WARN("Attempting to load multiple scenes simultaneously; mesh rigid bodies will not load properly.");
+        }
+
+        _meshRigidBodyData = new std::map<std::string, MeshRigidBodyData>();
+    }
+
+    // Load the main scene data from GPB and apply the global scene properties.
+    Scene* scene = loadMainSceneData(sceneProperties);
+    if (!scene)
+    {
+        SAFE_DELETE(properties);
+        return NULL;
+    }
+
+    // First apply the node url properties. Following that,
+    // apply the normal node properties and create the animations.
+    applyNodeUrls(scene);
+    applyNodeProperties(scene, sceneProperties);
+    createAnimations(scene);
+
+    // Find the physics properties object.
+    Properties* physics = NULL;
+    Properties* ns = NULL;
+    sceneProperties->rewind();
+    while (true)
+    {
+        Properties* ns = sceneProperties->getNextNamespace();
+        if (strcmp(ns->getNamespace(), "physics") == 0)
+        {
+            physics = ns;
+            break;
+        }
+    }
+
+    // Load physics properties and constraints.
+    if (physics)
+        loadPhysics(physics, scene);
+
+    // Clean up all loaded properties objects.
+    std::map<std::string, Properties*>::iterator iter = _propertiesFromFile.begin();
+    for (; iter != _propertiesFromFile.end(); iter++)
+    {
+        SAFE_DELETE(iter->second);
+    }
+
+    // Clean up the .scene file's properties object.
+    SAFE_DELETE(properties);
+
+    // Clean up mesh rigid body data.
+    if (_meshRigidBodyData)
+    {
+        std::map<std::string, MeshRigidBodyData>::iterator iter = _meshRigidBodyData->begin();
+        for (; iter != _meshRigidBodyData->end(); iter++)
+        {
+            for (unsigned int i = 0; i < iter->second.indexData.size(); i++)
+            {
+                SAFE_DELETE_ARRAY(iter->second.indexData[i]);
+            }
+
+            SAFE_DELETE_ARRAY(iter->second.vertexData);
+        }
+
+        SAFE_DELETE(_meshRigidBodyData);
+    }
+
+    // Clear all temporary data stores.
+    _propertiesFromFile.clear();
+    _animations.clear();
+    _nodeProperties.clear();
+    _nodesWithMeshRB.clear();
+
+    return scene;
+}
+
+void SceneLoader::addMeshRigidBodyData(std::string id, Mesh* mesh, unsigned char* vertexData, unsigned int vertexByteCount)
+{
+    if (!_meshRigidBodyData)
+    {
+        WARN("Attempting to add mesh rigid body data outside of scene loading; ignoring request.");
+        return;
+    }
+
+    (*_meshRigidBodyData)[id].mesh = mesh;
+    (*_meshRigidBodyData)[id].vertexData = new unsigned char[vertexByteCount];
+    memcpy((*_meshRigidBodyData)[id].vertexData, vertexData, vertexByteCount);
+}
+
+void SceneLoader::addMeshRigidBodyData(std::string id, unsigned char* indexData, unsigned int indexByteCount)
+{
+    if (!_meshRigidBodyData)
+    {
+        WARN("Attempting to add mesh rigid body data outside of scene loading; ignoring request.");
+        return;
+    }
+
+    unsigned char* indexDataCopy = new unsigned char[indexByteCount];
+    memcpy(indexDataCopy, indexData, indexByteCount);
+    (*_meshRigidBodyData)[id].indexData.push_back(indexDataCopy);
+}
+
+void SceneLoader::addSceneAnimation(const char* animationID, const char* targetID, const char* url)
+{
+    // Calculate the file and id from the given url.
+    std::string file;
+    std::string id;
+    splitURL(url, &file, &id);
+    
+    // If there is a file that needs to be loaded later, add an 
+    // empty entry to the properties table to signify it.
+    if (file.length() > 0 && _propertiesFromFile.count(file) == 0)
+        _propertiesFromFile[file] = NULL;
+
+    // Add the animation to the list of animations to be resolved later.
+    _animations.push_back(SceneAnimation(animationID, targetID, file, id));
+}
+
+void SceneLoader::addSceneNodeProperty(SceneNodeProperty::Type type, const char* nodeID, const char* url)
+{
+    // Calculate the file and id from the given url.
+    std::string file;
+    std::string id;
+    splitURL(url, &file, &id);
+    
+    // If there is a non-GPB file that needs to be loaded later, add an 
+    // empty entry to the properties table to signify it.
+    if (file.length() > 0 && file.find(".gpb") == file.npos && _propertiesFromFile.count(file) == 0)
+        _propertiesFromFile[file] = NULL;
+
+    // Add the node property to the list of node properties to be resolved later.
+    _nodeProperties.push_back(SceneNodeProperty(type, nodeID, file, id));
+}
+
+void SceneLoader::applyNodeProperties(const Scene* scene, const Properties* sceneProperties)
+{
+    // Apply all of the remaining scene node properties.
+    for (unsigned int i = 0; i < _nodeProperties.size(); i++)
+    {
+        // If the referenced node doesn't exist in the scene, then we
+        // can't do anything so we skip to the next scene node property.
+        Node* node = scene->findNode(_nodeProperties[i]._nodeID);
+        if (!node)
+        {
+            WARN_VARG("Attempting to set a property for node '%s', which does not exist in the scene.", _nodeProperties[i]._nodeID);
+            continue;
+        }
+
+        if (_nodeProperties[i]._type == SceneNodeProperty::AUDIO ||
+            _nodeProperties[i]._type == SceneNodeProperty::MATERIAL ||
+            _nodeProperties[i]._type == SceneNodeProperty::PARTICLE ||
+            _nodeProperties[i]._type == SceneNodeProperty::RIGIDBODY)
+        {
+            // Check to make sure the referenced properties object was loaded properly.
+            Properties* p = _propertiesFromFile[_nodeProperties[i]._file];
+            if (!p)
+            {
+                WARN_VARG("The referenced node data in file '%s' failed to load.", _nodeProperties[i]._file.c_str());
+                continue;
+            }
+
+            // If a specific namespace within the file was specified, load that namespace.
+            if (_nodeProperties[i]._id.size() > 0)
+            {
+                p = p->getNamespace(_nodeProperties[i]._id.c_str());
+                if (!p)
+                {
+                    WARN_VARG("The referenced node data at '%s#%s' failed to load.", _nodeProperties[i]._file.c_str(), _nodeProperties[i]._id.c_str());
+                    continue;
+                }
+            }
+            else
+            {
+                // Otherwise, use the first namespace.
+                p->rewind();
+                p = p->getNextNamespace();
+            }
+
+            switch (_nodeProperties[i]._type)
+            {
+            case SceneNodeProperty::AUDIO:
+            {
+                AudioSource* audioSource = AudioSource::create(p);
+                node->setAudioSource(audioSource);
+                SAFE_RELEASE(audioSource);
+                break;
+            }
+            case SceneNodeProperty::MATERIAL:
+                if (!node->getModel())
+                    WARN_VARG("Attempting to set a material on node '%s', which has no model.", _nodeProperties[i]._nodeID);
+                else
+                {
+                    Material* material = Material::create(p);
+                    node->getModel()->setMaterial(material);
+                    SAFE_RELEASE(material);
+                }
+
+                break;
+            case SceneNodeProperty::PARTICLE:
+            {
+                ParticleEmitter* particleEmitter = ParticleEmitter::create(p);
+                node->setParticleEmitter(particleEmitter);
+                SAFE_RELEASE(particleEmitter);
+                break;
+            }
+            case SceneNodeProperty::RIGIDBODY:
+            {
+                // If the scene file specifies a rigid body model, use it for creating the rigid body.
+                Properties* np = sceneProperties->getNamespace(_nodeProperties[i]._nodeID);
+                const char* name = NULL;
+                if (np && (name = np->getString("rigidbodymodel")))
+                {
+                    Node* modelNode = scene->findNode(name);
+                    if (!modelNode)
+                        WARN_VARG("Node '%s' does not exist; attempting to use its model for rigid body creation.", name);
+                    else
+                    {
+                        if (!modelNode->getModel())
+                            WARN_VARG("Node '%s' does not have a model; attempting to use its model for rigid body creation.", name);
+                        else
+                        {
+                            // Set the specified model during physics rigid body creation.
+                            Model* model = node->getModel();
+                            node->setModel(modelNode->getModel());
+                            node->setPhysicsRigidBody(p);
+                            node->setModel(model);
+                        }
+                    }
+                }
+                else if (!node->getModel())
+                    WARN_VARG("Attempting to set a rigid body on node '%s', which has no model.", _nodeProperties[i]._nodeID);
+                else
+                    node->setPhysicsRigidBody(p);
+
+                break;
+            }
+            default:
+                // This cannot happen.
+                break;
+            }
+        }
+        else
+        {
+            Properties* np = sceneProperties->getNamespace(_nodeProperties[i]._nodeID);
+            const char* name = NULL;
+
+            switch (_nodeProperties[i]._type)
+            {
+            case SceneNodeProperty::TRANSLATE:
+            {
+                Vector3 t;
+                if (np && np->getVector3("translate", &t))
+                    node->setTranslation(t);
+                break;
+            }
+            case SceneNodeProperty::ROTATE:
+            {
+                Quaternion r;
+                if (np && np->getQuaternionFromAxisAngle("rotate", &r))
+                    node->setRotation(r);
+                break;
+            }
+            case SceneNodeProperty::SCALE:
+            {
+                Vector3 s;
+                if (np && np->getVector3("scale", &s))
+                    node->setScale(s);
+                break;
+            }
+            default:
+                WARN_VARG("Unsupported node property type: %d.", _nodeProperties[i]._type);
+                break;
+            }
+        }
+
+        
+    }
+}
+
+void SceneLoader::applyNodeUrls(Scene* scene)
+{
+    // Apply all URL node properties so that when we go to apply
+    // the other node properties, the node is in the scene.
+    for (unsigned int i = 0; i < _nodeProperties.size(); )
+    {
+        if (_nodeProperties[i]._type == SceneNodeProperty::URL)
+        {
+            // Make sure that the ID we are using to insert the node into the scene with is unique.
+            if (scene->findNode(_nodeProperties[i]._nodeID) != NULL)
+                WARN_VARG("Attempting to insert or rename a node to an ID that already exists: ID='%s'", _nodeProperties[i]._nodeID);
+            else
+            {
+                // If a file was specified, load the node from file and then insert it into the scene with the new ID.
+                if (_nodeProperties[i]._file.size() > 0)
+                {
+                    Package* tmpPackage = Package::create(_nodeProperties[i]._file.c_str());
+                    if (!tmpPackage)
+                        WARN_VARG("Failed to load GPB file '%s' for node stitching.", _nodeProperties[i]._file.c_str());
+                    else
+                    {
+                        bool loadWithMeshRBSupport = false;
+                        for (unsigned int j = 0; j < _nodesWithMeshRB.size(); j++)
+                        {
+                            if (_nodeProperties[i]._id == _nodesWithMeshRB[j])
+                            {
+                                loadWithMeshRBSupport = true;
+                                break;
+                            }
+                        }
+
+                        Node* node = tmpPackage->loadNode(_nodeProperties[i]._id.c_str(), loadWithMeshRBSupport);
+                        if (!node)
+                            WARN_VARG("Could not load node '%s' in GPB file '%s'.", _nodeProperties[i]._id.c_str(), _nodeProperties[i]._file.c_str());
+                        else
+                        {
+                            node->setId(_nodeProperties[i]._nodeID);
+                            scene->addNode(node);
+                        }
+                        
+                        SAFE_RELEASE(tmpPackage);
+                    }
+                }
+                else
+                {
+                    // TODO: Should we do all nodes with this case first to allow users to stitch in nodes with
+                    // IDs equal to IDs that were in the original GPB file but were changed in the scene file?
+
+                    // Otherwise, the node is from the main GPB and should just be renamed.
+                    Node* node = scene->findNode(_nodeProperties[i]._id.c_str());
+                    if (!node)
+                        WARN_VARG("Could not find node '%s' in main scene GPB file.", _nodeProperties[i]._id.c_str());
+                    else
+                        node->setId(_nodeProperties[i]._nodeID);
+                }
+            }
+
+            // Remove the node property since we are done applying it.
+            _nodeProperties.erase(_nodeProperties.begin() + i);
+        }
+        else
+            i++;
+    }
+}
+
+void SceneLoader::buildReferenceTables(Properties* sceneProperties)
+{
+    // Go through the child namespaces of the scene.
+    Properties* ns;
+    const char* name = NULL;
+    while (ns = sceneProperties->getNextNamespace())
+    {
+        if (strcmp(ns->getNamespace(), "node") == 0)
+        {
+            if (strlen(ns->getId()) == 0)
+            {
+                WARN("Nodes must have an ID; skipping the current node.");
+                continue;
+            }
+
+            while (name = ns->getNextProperty())
+            {
+                if (strcmp(name, "url") == 0)
+                {
+                    addSceneNodeProperty(SceneNodeProperty::URL, ns->getId(), ns->getString());
+                }
+                else if (strcmp(name, "audio") == 0)
+                {
+                    addSceneNodeProperty(SceneNodeProperty::AUDIO, ns->getId(), ns->getString());
+                }
+                else if (strcmp(name, "material") == 0)
+                {
+                    addSceneNodeProperty(SceneNodeProperty::MATERIAL, ns->getId(), ns->getString());
+                }
+                else if (strcmp(name, "particle") == 0)
+                {
+                    addSceneNodeProperty(SceneNodeProperty::PARTICLE, ns->getId(), ns->getString());
+                }
+                else if (strcmp(name, "rigidbody") == 0)
+                {
+                    addSceneNodeProperty(SceneNodeProperty::RIGIDBODY, ns->getId(), ns->getString());
+                }
+                else if (strcmp(name, "rigidbodymodel") == 0)
+                {
+                    // Ignore this for now. We process this when we do rigid body creation.
+                }
+                else if (strcmp(name, "translate") == 0)
+                {
+                    addSceneNodeProperty(SceneNodeProperty::TRANSLATE, ns->getId());
+                }
+                else if (strcmp(name, "rotate") == 0)
+                {
+                    addSceneNodeProperty(SceneNodeProperty::ROTATE, ns->getId());
+                }
+                else if (strcmp(name, "scale") == 0)
+                {
+                    addSceneNodeProperty(SceneNodeProperty::SCALE, ns->getId());
+                }
+                else
+                {
+                    WARN_VARG("Unsupported node property: %s = %s", name, ns->getString());
+                }
+            }
+        }
+        else if (strcmp(ns->getNamespace(), "animations") == 0)
+        {
+            // Load all the animations.
+            Properties* animation;
+            while (animation = ns->getNextNamespace())
+            {
+                if (strcmp(animation->getNamespace(), "animation") == 0)
+                {
+                    const char* animationID = animation->getId();
+                    if (strlen(animationID) == 0)
+                    {
+                        WARN("Animations must have an ID; skipping the current animation.");
+                        continue;
+                    }
+
+                    const char* url = animation->getString("url");
+                    if (!url)
+                    {
+                        WARN_VARG("Animations must have a URL; skipping animation '%s'.", animationID);
+                        continue;
+                    }
+                    const char* targetID = animation->getString("target");
+                    if (!targetID)
+                    {
+                        WARN_VARG("Animations must have a target; skipping animation '%s'.", animationID);
+                        continue;
+                    }
+
+                    addSceneAnimation(animationID, targetID, url);
+                }
+                else
+                {
+                    WARN_VARG("Unsupported child namespace (of 'animations'): %s", ns->getNamespace());
+                }
+            }
+        }
+        else if (strcmp(ns->getNamespace(), "physics") == 0)
+        {
+            // Note: we don't load physics until the whole scene file has been 
+            // loaded so that all node references (i.e. for constraints) can be resolved.
+        }
+        else
+        {
+            WARN_VARG("Unsupported child namespace (of 'scene'): %s", ns->getNamespace());
+        }
+    }
+}
+
+void SceneLoader::calculateNodesWithMeshRigidBodies(const Properties* sceneProperties)
+{
+    const char* name = NULL;
+
+    // Make a list of all nodes with triangle mesh rigid bodies.
+    for (unsigned int i = 0; i < _nodeProperties.size(); i++)
+    {
+        if (_nodeProperties[i]._type == SceneNodeProperty::RIGIDBODY)
+        {
+            Properties* p = _propertiesFromFile[_nodeProperties[i]._file];
+            if (p)
+            {
+                if (_nodeProperties[i]._id.size() > 0)
+                {
+                    p = p->getNamespace(_nodeProperties[i]._id.c_str());
+                }
+                else
+                {
+                    p = p->getNextNamespace();
+                }
+
+                if (p && strcmp(p->getNamespace(), "rigidbody") == 0 &&
+                    strcmp(p->getString("type"), "MESH") == 0)
+                {
+                    // If the node specifies a rigidbodymodel, then use
+                    // that node's ID; otherwise, use its ID.
+                    Properties* p = sceneProperties->getNamespace(_nodeProperties[i]._nodeID);
+                    if (p && (name = p->getString("rigidbodymodel")))
+                        _nodesWithMeshRB.push_back(name);
+                    else
+                        _nodesWithMeshRB.push_back(_nodeProperties[i]._nodeID);
+                }
+            }
+        }
+    }
+}
+
+void SceneLoader::createAnimations(const Scene* scene)
+{
+    // Create the scene animations.
+    for (unsigned int i = 0; i < _animations.size(); i++)
+    {
+        // If the target node doesn't exist in the scene, then we
+        // can't do anything so we skip to the next animation.
+        Node* node = scene->findNode(_animations[i]._targetID);
+        if (!node)
+        {
+            WARN_VARG("Attempting to create an animation targeting node '%s', which does not exist in the scene.", _animations[i]._targetID);
+            continue;
+        }
+
+        // Check to make sure the referenced properties object was loaded properly.
+        Properties* p = _propertiesFromFile[_animations[i]._file];
+        if (!p)
+        {
+            WARN_VARG("The referenced animation data in file '%s' failed to load.", _animations[i]._file.c_str());
+            continue;
+        }
+        if (_animations[i]._id.size() > 0)
+        {
+            p = p->getNamespace(_animations[i]._id.c_str());
+            if (!p)
+            {
+                WARN_VARG("The referenced animation data at '%s#%s' failed to load.", _animations[i]._file.c_str(), _animations[i]._id.c_str());
+                continue;
+            }
+        }
+
+        Game::getInstance()->getAnimationController()->createAnimation(_animations[i]._animationID, node, p);
+    }
+}
+
+const SceneLoader::MeshRigidBodyData* SceneLoader::getMeshRigidBodyData(std::string id)
+{
+    if (!_meshRigidBodyData)
+    {
+        WARN("Attempting to get mesh rigid body data, but none has been loaded; ignoring request.");
+        return NULL;
+    }
+
+    return (_meshRigidBodyData->count(id) > 0) ? &(*_meshRigidBodyData)[id] : NULL;
+}
+
+PhysicsConstraint* SceneLoader::loadGenericConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
+{
+    PhysicsGenericConstraint* physicsConstraint;
+
+    // Create the constraint from the specified properties.
+    Quaternion roA;
+    Vector3 toA;
+    bool offsetSpecified = constraint->getQuaternionFromAxisAngle("rotationOffsetA", &roA);
+    offsetSpecified |= constraint->getVector3("translationOffsetA", &toA);
+
+    if (offsetSpecified)
+    {
+        if (rbB)
+        {
+            Quaternion roB;
+            Vector3 toB;
+            constraint->getQuaternionFromAxisAngle("rotationOffsetB", &roB);
+            constraint->getVector3("translationOffsetB", &toB);
+
+            physicsConstraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(rbA, roA, toB, rbB, roB, toB);
+        }
+        else
+        {
+            physicsConstraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(rbA, roA, toA);
+        }
+    }
+    else
+    {
+        physicsConstraint = Game::getInstance()->getPhysicsController()->createGenericConstraint(rbA, rbB);
+    }
+
+    // Set the optional parameters that were specified.
+    Vector3 v;
+    if (constraint->getVector3("angularLowerLimit", &v))
+        physicsConstraint->setAngularLowerLimit(v);
+    if (constraint->getVector3("angularUpperLimit", &v))
+        physicsConstraint->setAngularUpperLimit(v);
+    if (constraint->getVector3("linearLowerLimit", &v))
+        physicsConstraint->setLinearLowerLimit(v);
+    if (constraint->getVector3("linearUpperLimit", &v))
+        physicsConstraint->setLinearUpperLimit(v);
+
+    return physicsConstraint;
+}
+
+PhysicsConstraint* SceneLoader::loadHingeConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
+{
+    PhysicsHingeConstraint* physicsConstraint = NULL;
+
+    // Create the constraint from the specified properties.
+    Quaternion roA;
+    Vector3 toA;
+    constraint->getQuaternionFromAxisAngle("rotationOffsetA", &roA);
+    constraint->getVector3("translationOffsetA", &toA);
+    if (rbB)
+    {
+        Quaternion roB;
+        Vector3 toB;
+        constraint->getQuaternionFromAxisAngle("rotationOffsetB", &roB);
+        constraint->getVector3("translationOffsetB", &toB);
+
+        physicsConstraint = Game::getInstance()->getPhysicsController()->createHingeConstraint(rbA, roA, toB, rbB, roB, toB);
+    }
+    else
+    {
+        physicsConstraint = Game::getInstance()->getPhysicsController()->createHingeConstraint(rbA, roA, toA);
+    }
+
+    // Attempt to load the hinge limits first as a Vector3 and if that doesn't work, try loading as a Vector2.
+    // We do this because the user can specify just the min and max angle, or both angle along with bounciness.
+    Vector3 fullLimits;
+    Vector2 angleLimits;
+    if (constraint->getVector3("limits", &fullLimits))
+        physicsConstraint->setLimits(MATH_DEG_TO_RAD(fullLimits.x), MATH_DEG_TO_RAD(fullLimits.y), fullLimits.z);
+    else if (constraint->getVector2("limits", &angleLimits))
+        physicsConstraint->setLimits(angleLimits.x, angleLimits.y);
+
+    return physicsConstraint;
+}
+
+Scene* SceneLoader::loadMainSceneData(const Properties* sceneProperties)
+{
+    // Load the main scene from the specified path.
+    const char* path = sceneProperties->getString("path");
+    Package* package = Package::create(path);
+    if (!package)
+    {
+        WARN_VARG("Failed to load scene GPB file '%s'.", path);
+        return NULL;
+    }
+    
+    Scene* scene = package->loadScene(NULL, &_nodesWithMeshRB);
+    if (!scene)
+    {
+        WARN_VARG("Failed to load scene from '%s'.", path);
+        SAFE_RELEASE(package);
+        return NULL;
+    }
+
+    // Go through the supported scene properties and apply them to the scene.
+    const char* name = sceneProperties->getString("activeCamera");
+    if (name)
+    {
+        Node* camera = scene->findNode(name);
+        if (camera && camera->getCamera())
+            scene->setActiveCamera(camera->getCamera());
+    }
+
+    SAFE_RELEASE(package);
+    return scene;
+}
+
+void SceneLoader::loadPhysics(Properties* physics, Scene* scene)
+{
+    // Go through the supported global physics properties and apply them.
+    Vector3 gravity;
+    if (physics->getVector3("gravity", &gravity))
+        Game::getInstance()->getPhysicsController()->setGravity(gravity);
+
+    Properties* constraint;
+    const char* name;
+    while (constraint = physics->getNextNamespace())
+    {
+        if (strcmp(constraint->getNamespace(), "constraint") == 0)
+        {
+            // Get the constraint type.
+            std::string type = constraint->getString("type");
+
+            // Attempt to load the first rigid body. If the first rigid body cannot
+            // be loaded or found, then continue to the next constraint (error).
+            name = constraint->getString("rigidBodyA");
+            if (!name)
+            {
+                WARN_VARG("Missing property 'rigidBodyA' for constraint %s", constraint->getId());
+                continue;
+            }
+            Node* rbANode = scene->findNode(name);
+            if (!rbANode)
+            {
+                WARN_VARG("Node '%s' to be used as 'rigidBodyA' for constraint %s cannot be found.", name, constraint->getId());
+                continue;
+            }
+            PhysicsRigidBody* rbA = rbANode->getPhysicsRigidBody();
+            if (!rbA)
+            {
+                WARN_VARG("Node '%s' to be used as 'rigidBodyA' does not have a rigid body.", name);
+                continue;
+            }
+
+            // Attempt to load the second rigid body. If the second rigid body is not
+            // specified, that is usually okay (only spring constraints require both and
+            // we check that below), but if the second rigid body is specified and it doesn't
+            // load properly, then continue to the next constraint (error).
+            name = constraint->getString("rigidBodyB");
+            PhysicsRigidBody* rbB = NULL;
+            if (name)
+            {
+                Node* rbBNode = scene->findNode(name);
+                if (!rbBNode)
+                {
+                    WARN_VARG("Node '%s' to be used as 'rigidBodyB' for constraint %s cannot be found.", name, constraint->getId());
+                    continue;
+                }
+                rbB = rbBNode->getPhysicsRigidBody();
+                if (!rbB)
+                {
+                    WARN_VARG("Node '%s' to be used as 'rigidBodyB' does not have a rigid body.", name);
+                    continue;
+                }
+            }
+
+            PhysicsConstraint* physicsConstraint = NULL;
+
+            // Load the constraint based on its type.
+            if (type == "FIXED")
+            {
+                physicsConstraint = Game::getInstance()->getPhysicsController()->createFixedConstraint(rbA, rbB);
+            }
+            else if (type == "GENERIC")
+            {
+                physicsConstraint = loadGenericConstraint(constraint, rbA, rbB);
+            }
+            else if (type == "HINGE")
+            {
+                physicsConstraint = loadHingeConstraint(constraint, rbA, rbB);
+            }
+            else if (type == "SOCKET")
+            {
+                physicsConstraint = loadSocketConstraint(constraint, rbA, rbB);
+            }
+            else if (type == "SPRING")
+            {
+                physicsConstraint = loadSpringConstraint(constraint, rbA, rbB);
+            }
+            
+            // If the constraint failed to load, continue on to the next one.
+            if (!physicsConstraint)
+                    continue;
+
+            // If the breaking impulse was specified, apply it to the constraint.
+            if (constraint->getString("breakingImpulse"))
+                physicsConstraint->setBreakingImpulse(constraint->getFloat("breakingImpulse"));
+        }
+        else
+        {
+            WARN_VARG("Unsupported child namespace (of 'physics'): %s", physics->getNamespace());
+        }
+    }
+}
+
+void SceneLoader::loadReferencedFiles()
+{
+    // Load all referenced properties files.
+    std::map<std::string, Properties*>::iterator iter = _propertiesFromFile.begin();
+    for (; iter != _propertiesFromFile.end(); iter++)
+    {
+        Properties* p = Properties::create(iter->first.c_str());
+        assert(p);
+        if (p == NULL)
+            WARN_VARG("Failed to load referenced file: %s", iter->first.c_str());
+
+        iter->second = p;
+    }
+}
+
+PhysicsConstraint* SceneLoader::loadSocketConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
+{
+    PhysicsSocketConstraint* physicsConstraint = NULL;
+    Vector3 toA;
+    bool offsetSpecified = constraint->getVector3("translationOffsetA", &toA);
+
+    if (offsetSpecified)
+    {
+        if (rbB)
+        {
+            Vector3 toB;
+            constraint->getVector3("translationOffsetB", &toB);
+
+            physicsConstraint = Game::getInstance()->getPhysicsController()->createSocketConstraint(rbA, toA, rbB, toB);
+        }
+        else
+        {
+            physicsConstraint = Game::getInstance()->getPhysicsController()->createSocketConstraint(rbA, toA);
+        }
+    }
+    else
+    {
+        physicsConstraint = Game::getInstance()->getPhysicsController()->createSocketConstraint(rbA, rbB);
+    }
+
+    return physicsConstraint;
+}
+
+PhysicsConstraint* SceneLoader::loadSpringConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB)
+{
+    if (!rbB)
+    {
+        WARN("Spring constraints require two rigid bodies.");
+        return NULL;
+    }
+
+    PhysicsSpringConstraint* physicsConstraint = NULL;
+
+    // Create the constraint from the specified properties.
+    Quaternion roA, roB;
+    Vector3 toA, toB;
+    bool offsetsSpecified = constraint->getQuaternionFromAxisAngle("rotationOffsetA", &roA);
+    offsetsSpecified |= constraint->getVector3("translationOffsetA", &toA);
+    offsetsSpecified |= constraint->getQuaternionFromAxisAngle("rotationOffsetB", &roB);
+    offsetsSpecified |= constraint->getVector3("translationOffsetB", &toB);
+
+    if (offsetsSpecified)
+    {
+        physicsConstraint = Game::getInstance()->getPhysicsController()->createSpringConstraint(rbA, roA, toB, rbB, roB, toB);
+    }
+    else
+    {
+        physicsConstraint = Game::getInstance()->getPhysicsController()->createSpringConstraint(rbA, rbB);
+    }
+
+    // Set the optional parameters that were specified.
+    Vector3 v;
+    if (constraint->getVector3("angularLowerLimit", &v))
+        physicsConstraint->setAngularLowerLimit(v);
+    if (constraint->getVector3("angularUpperLimit", &v))
+        physicsConstraint->setAngularUpperLimit(v);
+    if (constraint->getVector3("linearLowerLimit", &v))
+        physicsConstraint->setLinearLowerLimit(v);
+    if (constraint->getVector3("linearUpperLimit", &v))
+        physicsConstraint->setLinearUpperLimit(v);
+    if (constraint->getString("angularDampingX"))
+        physicsConstraint->setAngularDampingX(constraint->getFloat("angularDampingX"));
+    if (constraint->getString("angularDampingY"))
+        physicsConstraint->setAngularDampingY(constraint->getFloat("angularDampingY"));
+    if (constraint->getString("angularDampingZ"))
+        physicsConstraint->setAngularDampingZ(constraint->getFloat("angularDampingZ"));
+    if (constraint->getString("angularStrengthX"))
+        physicsConstraint->setAngularStrengthX(constraint->getFloat("angularStrengthX"));
+    if (constraint->getString("angularStrengthY"))
+        physicsConstraint->setAngularStrengthY(constraint->getFloat("angularStrengthY"));
+    if (constraint->getString("angularStrengthZ"))
+        physicsConstraint->setAngularStrengthZ(constraint->getFloat("angularStrengthZ"));
+    if (constraint->getString("linearDampingX"))
+        physicsConstraint->setLinearDampingX(constraint->getFloat("linearDampingX"));
+    if (constraint->getString("linearDampingY"))
+        physicsConstraint->setLinearDampingY(constraint->getFloat("linearDampingY"));
+    if (constraint->getString("linearDampingZ"))
+        physicsConstraint->setLinearDampingZ(constraint->getFloat("linearDampingZ"));
+    if (constraint->getString("linearStrengthX"))
+        physicsConstraint->setLinearStrengthX(constraint->getFloat("linearStrengthX"));
+    if (constraint->getString("linearStrengthY"))
+        physicsConstraint->setLinearStrengthY(constraint->getFloat("linearStrengthY"));
+    if (constraint->getString("linearStrengthZ"))
+        physicsConstraint->setLinearStrengthZ(constraint->getFloat("linearStrengthZ"));
+
+    return physicsConstraint;
+}
+
+void SceneLoader::splitURL(const char* url, std::string* file, std::string* id)
+{
+    if (!url)
+        return;
+
+    std::string urlString = url;
+
+    // Check if the url references a file (otherwise, it only references a node within the main GPB).
+    unsigned int loc = urlString.rfind(".");
+    if (loc != urlString.npos)
+    {
+        // If the url references a specific namespace within the file,
+        // set the id out parameter appropriately. Otherwise, set the id out
+        // parameter to the empty string so we know to load the first namespace.
+        loc = urlString.rfind("#");
+        if (loc != urlString.npos)
+        {
+            *file = urlString.substr(0, loc);
+            *id = urlString.substr(loc + 1);
+        }
+        else
+        {
+            *file = url;
+            *id = std::string();
+        }
+    }
+    else
+    {
+        *file = std::string();
+        *id = url;
+    }
+}
+
+}

+ 100 - 0
gameplay/src/SceneLoader.h

@@ -0,0 +1,100 @@
+#ifndef SCENELOADER_H_
+#define SCENELOADER_H_
+
+#include "Base.h"
+#include "Mesh.h"
+#include "PhysicsRigidBody.h"
+#include "Properties.h"
+#include "Scene.h"
+
+namespace gameplay
+{
+
+/**
+ * Helper class for loading scenes from .scene files.
+ */
+class SceneLoader
+{
+    friend class Package;
+    friend class PhysicsController;
+    friend class Scene;
+
+private:
+
+    // Main interface to Scene::load(const char*).
+    static Scene* load(const char* filePath);
+    
+    // ------------------------------------------------------------------------
+    // Helper structures and functions for SceneLoader::load(const char*).
+
+    struct MeshRigidBodyData
+    {
+        Mesh* mesh;
+        unsigned char* vertexData;
+        std::vector<unsigned char*> indexData;
+    };
+
+    struct SceneAnimation
+    {
+        SceneAnimation(const char* animationID, const char* targetID, std::string file, std::string id)
+            : _animationID(animationID), _targetID(targetID), _file(file), _id(id) {}
+
+        const char* _animationID;
+        const char* _targetID;
+        std::string _file;
+        std::string _id;
+    };
+
+    struct SceneNodeProperty
+    {
+        enum Type { AUDIO, MATERIAL, PARTICLE, RIGIDBODY, TRANSLATE, ROTATE, SCALE, URL };
+
+        SceneNodeProperty(Type type, const char* nodeID, std::string file, std::string id)
+            : _type(type), _nodeID(nodeID), _file(file), _id(id) {}
+
+        Type _type;
+        const char* _nodeID;
+        std::string _file;
+        std::string _id;
+    };
+
+    static void addMeshRigidBodyData(std::string id, Mesh* mesh, unsigned char* vertexData, unsigned int vertexByteCount);
+    static void addMeshRigidBodyData(std::string id, unsigned char* indexData, unsigned int indexByteCount);
+    static void addSceneAnimation(const char* animationID, const char* targetID, const char* url);
+    static void addSceneNodeProperty(SceneNodeProperty::Type type, const char* nodeID, const char* url = NULL);
+    static void applyNodeProperties(const Scene* scene, const Properties* sceneProperties);
+    static void applyNodeUrls(Scene* scene);
+    static void buildReferenceTables(Properties* sceneProperties);
+    static void calculateNodesWithMeshRigidBodies(const Properties* sceneProperties);
+    static void createAnimations(const Scene* scene);
+    static const MeshRigidBodyData* getMeshRigidBodyData(std::string id);
+    static PhysicsConstraint* loadGenericConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
+    static PhysicsConstraint* loadHingeConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
+    static Scene* loadMainSceneData(const Properties* sceneProperties);
+    static void loadPhysics(Properties* physics, Scene* scene);
+    static void loadReferencedFiles();
+    static PhysicsConstraint* loadSocketConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
+    static PhysicsConstraint* loadSpringConstraint(const Properties* constraint, PhysicsRigidBody* rbA, PhysicsRigidBody* rbB);
+    static void splitURL(const char* url, std::string* file, std::string* id);
+    
+    // ------------------------------------------------------------------------
+
+    // Holds the properties object for a given file path.
+    static std::map<std::string, Properties*> _propertiesFromFile;
+
+    // Holds the animations declared in the .scene file.
+    static std::vector<SceneAnimation> _animations;
+
+    // Holds all the node properties declared in the .scene file.
+    static std::vector<SceneNodeProperty> _nodeProperties;
+
+    // Holds the node IDs that need to be loaded with mesh rigid body support.
+    static std::vector<std::string> _nodesWithMeshRB;
+
+    // Stores the mesh data needed for triangle mesh rigid body support.
+    static std::map<std::string, MeshRigidBodyData>* _meshRigidBodyData;
+};
+
+}
+
+#endif

Some files were not shown because too many files changed in this diff