2
0
Эх сурвалжийг харах

This is the group for gaming/gameplay

Sean Paul Taylor 14 жил өмнө
commit
23328a79fd
100 өөрчлөгдсөн 12177 нэмэгдсэн , 0 устгасан
  1. 224 0
      gameplay-encoder/gameplay-binary.txt
  2. 172 0
      gameplay-encoder/gameplay-encoder.vcxproj
  3. 213 0
      gameplay-encoder/gameplay-encoder.vcxproj.filters
  4. 7 0
      gameplay-encoder/gameplay-encoder.vcxproj.user
  5. 57 0
      gameplay-encoder/src/Animation.cpp
  6. 42 0
      gameplay-encoder/src/Animation.h
  7. 134 0
      gameplay-encoder/src/AnimationChannel.cpp
  8. 73 0
      gameplay-encoder/src/AnimationChannel.h
  9. 32 0
      gameplay-encoder/src/Base.cpp
  10. 54 0
      gameplay-encoder/src/Base.h
  11. 125 0
      gameplay-encoder/src/Camera.cpp
  12. 58 0
      gameplay-encoder/src/Camera.h
  13. 47 0
      gameplay-encoder/src/CameraInstance.cpp
  14. 36 0
      gameplay-encoder/src/CameraInstance.h
  15. 102 0
      gameplay-encoder/src/DAEChannelTarget.cpp
  16. 95 0
      gameplay-encoder/src/DAEChannelTarget.h
  17. 17 0
      gameplay-encoder/src/DAEPolygonInput.cpp
  18. 23 0
      gameplay-encoder/src/DAEPolygonInput.h
  19. 1934 0
      gameplay-encoder/src/DAESceneEncoder.cpp
  20. 221 0
      gameplay-encoder/src/DAESceneEncoder.h
  21. 37 0
      gameplay-encoder/src/Effect.cpp
  22. 38 0
      gameplay-encoder/src/Effect.h
  23. 135 0
      gameplay-encoder/src/FileIO.cpp
  24. 121 0
      gameplay-encoder/src/FileIO.h
  25. 53 0
      gameplay-encoder/src/Font.cpp
  26. 50 0
      gameplay-encoder/src/Font.h
  27. 116 0
      gameplay-encoder/src/GPBDecoder.cpp
  28. 49 0
      gameplay-encoder/src/GPBDecoder.h
  29. 250 0
      gameplay-encoder/src/GPBFile.cpp
  30. 110 0
      gameplay-encoder/src/GPBFile.h
  31. 40 0
      gameplay-encoder/src/Glyph.cpp
  32. 33 0
      gameplay-encoder/src/Glyph.h
  33. 137 0
      gameplay-encoder/src/Light.cpp
  34. 77 0
      gameplay-encoder/src/Light.h
  35. 52 0
      gameplay-encoder/src/LightInstance.cpp
  36. 40 0
      gameplay-encoder/src/LightInstance.h
  37. 38 0
      gameplay-encoder/src/Material.cpp
  38. 37 0
      gameplay-encoder/src/Material.h
  39. 38 0
      gameplay-encoder/src/MaterialParameter.cpp
  40. 35 0
      gameplay-encoder/src/MaterialParameter.h
  41. 349 0
      gameplay-encoder/src/Matrix.cpp
  42. 148 0
      gameplay-encoder/src/Matrix.h
  43. 204 0
      gameplay-encoder/src/Mesh.cpp
  44. 73 0
      gameplay-encoder/src/Mesh.h
  45. 103 0
      gameplay-encoder/src/MeshPart.cpp
  46. 92 0
      gameplay-encoder/src/MeshPart.h
  47. 153 0
      gameplay-encoder/src/MeshSkin.cpp
  48. 86 0
      gameplay-encoder/src/MeshSkin.h
  49. 39 0
      gameplay-encoder/src/MeshSubSet.cpp
  50. 28 0
      gameplay-encoder/src/MeshSubSet.h
  51. 77 0
      gameplay-encoder/src/Model.cpp
  52. 44 0
      gameplay-encoder/src/Model.h
  53. 303 0
      gameplay-encoder/src/Node.cpp
  54. 178 0
      gameplay-encoder/src/Node.h
  55. 49 0
      gameplay-encoder/src/NodeInstance.cpp
  56. 28 0
      gameplay-encoder/src/NodeInstance.h
  57. 71 0
      gameplay-encoder/src/Object.cpp
  58. 148 0
      gameplay-encoder/src/Object.h
  59. 360 0
      gameplay-encoder/src/Quaternion.cpp
  60. 309 0
      gameplay-encoder/src/Quaternion.h
  61. 86 0
      gameplay-encoder/src/Reference.cpp
  62. 54 0
      gameplay-encoder/src/Reference.h
  63. 68 0
      gameplay-encoder/src/ReferenceTable.cpp
  64. 47 0
      gameplay-encoder/src/ReferenceTable.h
  65. 116 0
      gameplay-encoder/src/Scene.cpp
  66. 70 0
      gameplay-encoder/src/Scene.h
  67. 72 0
      gameplay-encoder/src/StringUtil.cpp
  68. 22 0
      gameplay-encoder/src/StringUtil.h
  69. 317 0
      gameplay-encoder/src/TTFFontEncoder.cpp
  70. 33 0
      gameplay-encoder/src/TTFFontEncoder.h
  71. 0 0
      gameplay-encoder/src/Transform.cpp
  72. 53 0
      gameplay-encoder/src/Transform.h
  73. 323 0
      gameplay-encoder/src/Vector2.cpp
  74. 335 0
      gameplay-encoder/src/Vector2.h
  75. 363 0
      gameplay-encoder/src/Vector3.cpp
  76. 367 0
      gameplay-encoder/src/Vector3.h
  77. 366 0
      gameplay-encoder/src/Vector4.cpp
  78. 365 0
      gameplay-encoder/src/Vector4.h
  79. 105 0
      gameplay-encoder/src/Vertex.cpp
  80. 95 0
      gameplay-encoder/src/Vertex.h
  81. 75 0
      gameplay-encoder/src/VertexElement.cpp
  82. 34 0
      gameplay-encoder/src/VertexElement.h
  83. 234 0
      gameplay-encoder/src/main.cpp
  84. 11 0
      gameplay-resources/.project
  85. 41 0
      gameplay-resources/res/shaders/bumped-specular.fsh
  86. 43 0
      gameplay-resources/res/shaders/bumped-specular.vsh
  87. 32 0
      gameplay-resources/res/shaders/bumped.fsh
  88. 36 0
      gameplay-resources/res/shaders/bumped.vsh
  89. 36 0
      gameplay-resources/res/shaders/colored-specular.fsh
  90. 27 0
      gameplay-resources/res/shaders/colored-specular.vsh
  91. 24 0
      gameplay-resources/res/shaders/colored.fsh
  92. 20 0
      gameplay-resources/res/shaders/colored.vsh
  93. 40 0
      gameplay-resources/res/shaders/diffuse-specular.fsh
  94. 32 0
      gameplay-resources/res/shaders/diffuse-specular.vsh
  95. 25 0
      gameplay-resources/res/shaders/diffuse.fsh
  96. 25 0
      gameplay-resources/res/shaders/diffuse.vsh
  97. 56 0
      gameplay-resources/res/shaders/parallax-specular.fsh
  98. 43 0
      gameplay-resources/res/shaders/parallax-specular.vsh
  99. 49 0
      gameplay-resources/res/shaders/parallax.fsh
  100. 43 0
      gameplay-resources/res/shaders/parallax.vsh

+ 224 - 0
gameplay-encoder/gameplay-binary.txt

@@ -0,0 +1,224 @@
+gameplay Binary file format
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+File Description
+================
+A sample binary file format supporting definition of primitve and builtin objects.
+
+File Extension and Mime Type
+============================
+File extension is '.bbb' and the mime type is 'application/bbb'
+
+File Structure
+===============
+
+Section      Name            Type
+------------------------------------------------------------------------------------------------------
+Header
+             Identifier      byte[9]     = { '«', 'G', 'P', 'B', '»', '\r', '\n', '\x1A', '\n' } 
+             Version         byte[2]     = { 1, 0 }
+             References      Reference[]
+Data
+             Objects         Object[]
+
+Objects
+===========
+Supported object types are defined in the table below. Object with unique ids are included
+in the Reference table (see below).
+
+References
+===========
+A Reference is an Object that has a unique id. The Reference contains the unique id of the
+object, a uint for the TypeID a uint for the offset into the package for the object definition.
+
+ID's
+===========
+Object ID's are represented as a string which is guaranteed to be unique per file.
+Any object which host an object id should be added to the header Reference table so that it can
+be consumed by reference internally and externally.
+
+Xrefs
+===========
+Xrefs are string with a specific format used for referencing objects internal and external to
+a package. An xref with the format "#id" references an object within the current package with
+the given ID. Xrefs can also have the format "file#id", where "file" is the name of package file
+(relative to the locaiton of the current package) and "id" is the unique identifier of an object
+in that package.
+
+Primitives
+===========
+
+Name            Description
+------------------------------------------------------------------------------------------------------
+string          8-bit char array prefixed by unint for length encoding.
+bool            8-bit unsigned char   false=0, true=1.
+byte            8-bit unsigned char
+uint            32-bit unsigned int, stored as four bytes, lowest byte first.
+int             32-bit signed int, stored as four bytes, lowest byte first.
+float           32-bit float, stored as four bytes, with the least significant 
+                    byte of the mantissa first, and the exponent byte last.
+enum X          A uint which is restricted to values from the given enum name "X".
+
+Arrays
+======
+Arrays of constant length are simply the array of data for the expected type. 
+Arrays of dynamic size length(uint) encoded followed by expected type of data.
+Notation:   byte[3]  - constant length notation = byte[3]
+            int[]    - dynamic length notation = length+int[count]
+            Mesh[]   - dynamic length notation = length+Mesh[count]
+
+Enums
+=====
+
+enum VertexUsage
+{
+    POSITION = 1,
+    NORMAL = 2,
+    COLOR = 3,
+    TANGENT = 4,
+    BINORMAL = 5,
+    BLENDWEIGHTS = 6,
+    BLENDINDICES = 7,
+    TEXCOORD0 = 8,
+    TEXCOORD1 = 9,
+    TEXCOORD2 = 10,
+    TEXCOORD3 = 11,
+    TEXCOORD4 = 12,
+    TEXCOORD5 = 13,
+    TEXCOORD6 = 14,
+    TEXCOORD7 = 15
+}
+
+enum FontStyle
+{
+    PLAIN = 0,
+    BOLD = 1,
+    ITALIC = 2,
+    BOLD_ITALIC = 4
+}
+
+enum PrimitiveType
+{
+    TRIANGLES = GL_TRIANGLES (4),
+    TRIANGLE_STRIP = GL_TRIANGLE_STRIP (5),
+    LINES = GL_LINES (1),
+    LINE_STRIP = GL_LINE_STRIP (3),
+    POINTS = GL_POINTS (0)
+}
+
+enum IndexFormat
+{
+    INDEX8 = GL_UNSIGNED_BYTE (0x1401),
+    INDEX16 = GL_UNSIGNED_SHORT (0x1403),
+    INDEX32 = GL_UNSIGNED_INT (0x1405)
+}
+
+enum NodeType
+{
+    NODE = 1,
+    JOINT = 2
+}
+
+
+Object Definitions
+==================
+TypeID->Name    Member                  Type
+------------------------------------------------------------------------------------------------------
+Reference
+                id                      string
+                type                    uint
+                offset                  uint
+------------------------------------------------------------------------------------------------------
+1->Scene
+                nodes                   Node[]
+                activeCameraNode        xref:Node
+                ambientColor            float[3]
+------------------------------------------------------------------------------------------------------
+2->Node
+                type                    enum NodeType
+                transform               float[16]
+                children                Node[]
+                camera                  Camera
+                light                   Light
+                model                   Model
+------------------------------------------------------------------------------------------------------
+3->Animation
+                channels                AnimationChannel[]
+-----------------------------------------------------------------------------------------------------
+4->AnimationChannel
+                targetId                string
+                targetAttribute         uint
+                keyTimes                float[]  (milliseconds)
+                values                  float[]
+                tangents_in             float[]
+                tangents_out            float[]
+                interpolation           uint[]
+------------------------------------------------------------------------------------------------------
+11->Model
+                mesh                    xref:Mesh
+                meshSkin                MeshSkin
+                materials               Material[]
+------------------------------------------------------------------------------------------------------
+16->Material
+                parameters              MaterialParameter[] { string name, float[] value, uint type }
+                effect                  xref:Effect
+------------------------------------------------------------------------------------------------------
+17->Effect
+                vertexShader            string
+                fragmentShader          string
+------------------------------------------------------------------------------------------------------
+32->Camera
+                cameraType              byte {perspective|orthographic}
+                aspectRatio             float
+                nearPlane               float
+                farPlane                float
+                [ cameraType : perspective
+                  fieldOfView           float
+                ]
+                [ cameraType : orthographic
+                  magnification         float[2]
+                ]
+------------------------------------------------------------------------------------------------------
+33->Light
+                lightType               byte {ambient|directional|spot|point}
+                color                   float[3]
+                [ lightType : spot
+                  constantAttenuation   float
+                  linearAttenuation     float
+                  quadraticAttenuation  float
+                  falloffAngle          float
+                  falloffExponent       float
+                ]
+                [ lightType : point
+                  constantAttenuation   float
+                  linearAttenuation     float
+                  quadraticAttenuation  float
+                ]
+------------------------------------------------------------------------------------------------------
+34->Mesh
+                vertexFormat            VertexElement[] { enum VertexUsage usage, unint size }
+                vertices                byte[]
+                parts                   MeshPart[]
+                boundingBox             BoundingBox { float[3] min, float[3] max }
+                boundingSphere          BoundingSphere { float[3] center, float radius }
+------------------------------------------------------------------------------------------------------
+35->MeshPart
+                primitiveType           enum PrimitiveType
+                indexFormat             enum IndexFormat
+                indices                 byte[]
+------------------------------------------------------------------------------------------------------
+36->MeshSkin
+                bindShape               float[16]
+                rootJoint               string
+                joints                  xref:Node[]
+                jointsBindPoses         float[] // 16 * joints.length
+------------------------------------------------------------------------------------------------------
+128->Font
+                family                  string
+                style                   enum FontStyle
+                size                    uint
+                charset                 string
+                glyphs                  Glyph[] { uint index, uint width, float[4] uvCoords }
+                texMapWidth             uint
+                texMapHeight            uint
+                texMap                  byte[]

+ 172 - 0
gameplay-encoder/gameplay-encoder.vcxproj

@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="src\Animation.cpp" />
+    <ClCompile Include="src\AnimationChannel.cpp" />
+    <ClCompile Include="src\Base.cpp" />
+    <ClCompile Include="src\Camera.cpp" />
+    <ClCompile Include="src\CameraInstance.cpp" />
+    <ClCompile Include="src\DAEChannelTarget.cpp" />
+    <ClCompile Include="src\DAESceneEncoder.cpp" />
+    <ClCompile Include="src\Effect.cpp" />
+    <ClCompile Include="src\FileIO.cpp" />
+    <ClCompile Include="src\Font.cpp" />
+    <ClCompile Include="src\GPBFile.cpp" />
+    <ClCompile Include="src\Glyph.cpp" />
+    <ClCompile Include="src\GPBDecoder.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" />
+    <ClCompile Include="src\Matrix.cpp" />
+    <ClCompile Include="src\Mesh.cpp" />
+    <ClCompile Include="src\Model.cpp" />
+    <ClCompile Include="src\MeshPart.cpp" />
+    <ClCompile Include="src\MeshSkin.cpp" />
+    <ClCompile Include="src\Node.cpp" />
+    <ClCompile Include="src\Object.cpp" />
+    <ClCompile Include="src\Quaternion.cpp" />
+    <ClCompile Include="src\Reference.cpp" />
+    <ClCompile Include="src\ReferenceTable.cpp" />
+    <ClCompile Include="src\Scene.cpp" />
+    <ClCompile Include="src\StringUtil.cpp" />
+    <ClCompile Include="src\Transform.cpp" />
+    <ClCompile Include="src\TTFFontEncoder.cpp" />
+    <ClCompile Include="src\Vector2.cpp" />
+    <ClCompile Include="src\Vector3.cpp" />
+    <ClCompile Include="src\Vector4.cpp" />
+    <ClCompile Include="src\Vertex.cpp" />
+    <ClCompile Include="src\VertexElement.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="src\Animation.h" />
+    <ClInclude Include="src\AnimationChannel.h" />
+    <ClInclude Include="src\Base.h" />
+    <ClInclude Include="src\Camera.h" />
+    <ClInclude Include="src\CameraInstance.h" />
+    <ClInclude Include="src\DAEChannelTarget.h" />
+    <ClInclude Include="src\DAESceneEncoder.h" />
+    <ClInclude Include="src\Effect.h" />
+    <ClInclude Include="src\FileIO.h" />
+    <ClInclude Include="src\Font.h" />
+    <ClInclude Include="src\GPBFile.h" />
+    <ClInclude Include="src\Glyph.h" />
+    <ClInclude Include="src\GPBDecoder.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" />
+    <ClInclude Include="src\Mesh.h" />
+    <ClInclude Include="src\Model.h" />
+    <ClInclude Include="src\MeshPart.h" />
+    <ClInclude Include="src\MeshSkin.h" />
+    <ClInclude Include="src\Node.h" />
+    <ClInclude Include="src\Object.h" />
+    <ClInclude Include="src\Quaternion.h" />
+    <ClInclude Include="src\Reference.h" />
+    <ClInclude Include="src\ReferenceTable.h" />
+    <ClInclude Include="src\Scene.h" />
+    <ClInclude Include="src\StringUtil.h" />
+    <ClInclude Include="src\Transform.h" />
+    <ClInclude Include="src\TTFFontEncoder.h" />
+    <ClInclude Include="src\Vector2.h" />
+    <ClInclude Include="src\Vector3.h" />
+    <ClInclude Include="src\Vector4.h" />
+    <ClInclude Include="src\Vertex.h" />
+    <ClInclude Include="src\VertexElement.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{9D69B743-4872-4DD1-8E30-0087C64298D7}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>gameplay-encoder</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <OutDir>$(Configuration)\</OutDir>
+    <IntDir>$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <OutDir>$(Configuration)\</OutDir>
+    <IntDir>$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level4</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;NO_BOOST;NO_ZAE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../external-deps/freetype2/include;../external-deps/collada-dom/include;../external-deps/collada-dom/include/1.4</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>;../external-deps/freetype2/lib;../external-deps/collada-dom/lib</AdditionalLibraryDirectories>
+      <AdditionalDependencies>freetype245.lib;libcollada14dom22-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy /Y "$(ProjectDir)..\external-deps\collada-dom\lib\*.dll" "$(TargetDir)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;NO_BOOST;NO_ZAE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>../external-deps/freetype2/include;../external-deps/collada-dom/include;../external-deps/collada-dom/include/1.4</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>freetype245.lib;libcollada14dom22-d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>;../external-deps/freetype2/lib;../external-deps/collada-dom/lib</AdditionalLibraryDirectories>
+    </Link>
+    <PostBuildEvent>
+      <Command>copy /Y "$(ProjectDir)..\external-deps\collada-dom\lib\*.dll" "$(TargetDir)"</Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 213 - 0
gameplay-encoder/gameplay-encoder.vcxproj.filters

@@ -0,0 +1,213 @@
+<?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\Camera.cpp">
+      <Filter>Objects\Camera</Filter>
+    </ClCompile>
+    <ClCompile Include="src\CameraInstance.cpp">
+      <Filter>Objects\Camera</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Font.cpp">
+      <Filter>Objects\Font</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Glyph.cpp">
+      <Filter>Objects\Font</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Light.cpp">
+      <Filter>Objects\Light</Filter>
+    </ClCompile>
+    <ClCompile Include="src\LightInstance.cpp">
+      <Filter>Objects\Light</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Material.cpp">
+      <Filter>Objects\Material</Filter>
+    </ClCompile>
+    <ClCompile Include="src\MaterialParameter.cpp">
+      <Filter>Objects\Material</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Mesh.cpp">
+      <Filter>Objects\Mesh</Filter>
+    </ClCompile>
+    <ClCompile Include="src\MeshPart.cpp">
+      <Filter>Objects\Mesh</Filter>
+    </ClCompile>
+    <ClCompile Include="src\MeshSkin.cpp">
+      <Filter>Objects\Mesh</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Node.cpp">
+      <Filter>Objects\Node</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Reference.cpp">
+      <Filter>Objects</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Object.cpp">
+      <Filter>Objects</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Model.cpp">
+      <Filter>Objects\Mesh</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Scene.cpp">
+      <Filter>Objects</Filter>
+    </ClCompile>
+    <ClCompile Include="src\GPBDecoder.cpp" />
+    <ClCompile Include="src\Animation.cpp">
+      <Filter>Objects\Animation</Filter>
+    </ClCompile>
+    <ClCompile Include="src\AnimationChannel.cpp">
+      <Filter>Objects\Animation</Filter>
+    </ClCompile>
+    <ClCompile Include="src\GPBFile.cpp" />
+    <ClCompile Include="src\DAEChannelTarget.cpp" />
+    <ClCompile Include="src\Base.cpp">
+      <Filter>Objects</Filter>
+    </ClCompile>
+    <ClCompile Include="src\FileIO.cpp" />
+    <ClCompile Include="src\StringUtil.cpp" />
+    <ClCompile Include="src\Effect.cpp">
+      <Filter>Objects\Material</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Vertex.cpp" />
+    <ClCompile Include="src\VertexElement.cpp" />
+    <ClCompile Include="src\Quaternion.cpp">
+      <Filter>Math</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Vector4.cpp">
+      <Filter>Math</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Vector3.cpp">
+      <Filter>Math</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Vector2.cpp">
+      <Filter>Math</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Transform.cpp">
+      <Filter>Math</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Matrix.cpp">
+      <Filter>Math</Filter>
+    </ClCompile>
+  </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>
+    <ClInclude Include="src\Camera.h">
+      <Filter>Objects\Camera</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Font.h">
+      <Filter>Objects\Font</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Glyph.h">
+      <Filter>Objects\Font</Filter>
+    </ClInclude>
+    <ClInclude Include="src\LightInstance.h">
+      <Filter>Objects\Light</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Light.h">
+      <Filter>Objects\Light</Filter>
+    </ClInclude>
+    <ClInclude Include="src\MaterialParameter.h">
+      <Filter>Objects\Material</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Material.h">
+      <Filter>Objects\Material</Filter>
+    </ClInclude>
+    <ClInclude Include="src\MeshSkin.h">
+      <Filter>Objects\Mesh</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Mesh.h">
+      <Filter>Objects\Mesh</Filter>
+    </ClInclude>
+    <ClInclude Include="src\MeshPart.h">
+      <Filter>Objects\Mesh</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Node.h">
+      <Filter>Objects\Node</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Reference.h">
+      <Filter>Objects</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Object.h">
+      <Filter>Objects</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Model.h">
+      <Filter>Objects\Mesh</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Scene.h">
+      <Filter>Objects</Filter>
+    </ClInclude>
+    <ClInclude Include="src\GPBDecoder.h" />
+    <ClInclude Include="src\AnimationChannel.h">
+      <Filter>Objects\Animation</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Animation.h">
+      <Filter>Objects\Animation</Filter>
+    </ClInclude>
+    <ClInclude Include="src\GPBFile.h" />
+    <ClInclude Include="src\DAEChannelTarget.h" />
+    <ClInclude Include="src\Base.h">
+      <Filter>Objects</Filter>
+    </ClInclude>
+    <ClInclude Include="src\FileIO.h" />
+    <ClInclude Include="src\StringUtil.h" />
+    <ClInclude Include="src\Effect.h">
+      <Filter>Objects\Material</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Vertex.h" />
+    <ClInclude Include="src\VertexElement.h" />
+    <ClInclude Include="src\Quaternion.h">
+      <Filter>Math</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Vector4.h">
+      <Filter>Math</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Vector3.h">
+      <Filter>Math</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Vector2.h">
+      <Filter>Math</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Transform.h">
+      <Filter>Math</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Matrix.h">
+      <Filter>Math</Filter>
+    </ClInclude>
+  </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>
+    </Filter>
+  </ItemGroup>
+</Project>

+ 7 - 0
gameplay-encoder/gameplay-encoder.vcxproj.user

@@ -0,0 +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>C:\svn\gaming\gameplay\gameplay-samples\sample03-character\res\meshes\test_Collada_DAE.dae</LocalDebuggerCommandArguments>
+    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+  </PropertyGroup>
+</Project>

+ 57 - 0
gameplay-encoder/src/Animation.cpp

@@ -0,0 +1,57 @@
+#include "Animation.h"
+
+namespace gameplay
+{
+
+Animation::Animation(void)
+{
+}
+
+Animation::~Animation(void)
+{
+}
+
+unsigned int Animation::getTypeId(void) const
+{
+    return ANIMATION_ID;
+}
+const char* Animation::getElementName(void) const
+{
+    return "Animation";
+}
+
+void Animation::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    // Write AnimationChannels
+    write(channels.size(), file);
+    for (std::vector<AnimationChannel*>::iterator i = channels.begin(); i != channels.end(); i++)
+    {
+        (*i)->writeBinary(file);
+    }
+}
+
+void Animation::writeText(FILE* file)
+{   
+    fprintElementStart(file);
+    if (channels.size() > 0 )
+    {
+        for (std::vector<AnimationChannel*>::iterator i = channels.begin(); i != channels.end(); i++)
+        {
+            (*i)->writeText(file);
+        }
+    }
+    fprintElementEnd(file);
+}
+
+void Animation::add(AnimationChannel* animationChannel)
+{
+    channels.push_back(animationChannel);
+}
+
+unsigned int Animation::getAnimationChannelCount() const
+{
+    return channels.size();
+}
+
+}

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

@@ -0,0 +1,42 @@
+#ifndef ANIMATION_H_
+#define ANIMATION_H_
+
+#include "Object.h"
+#include "AnimationChannel.h"
+
+namespace gameplay
+{
+
+class Animation : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Animation(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Animation(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    void add(AnimationChannel* animationChannel);
+    /**
+     * Returns the number of animation channels contained in this animation.
+     * 
+     * @return The number of animation channels.
+     */
+    unsigned int getAnimationChannelCount() const;
+
+private:
+    std::vector<AnimationChannel*> channels;
+};
+
+}
+#endif

+ 134 - 0
gameplay-encoder/src/AnimationChannel.cpp

@@ -0,0 +1,134 @@
+#include "AnimationChannel.h"
+
+namespace gameplay
+{
+
+AnimationChannel::AnimationChannel(void) :
+    targetAttrib(0)
+{
+}
+
+AnimationChannel::~AnimationChannel(void)
+{
+}
+
+unsigned int AnimationChannel::getTypeId(void) const
+{
+    return ANIMATIONCHANNEL_ID;
+}
+const char* AnimationChannel::getElementName(void) const
+{
+    return "AnimationChannel";
+}
+
+void AnimationChannel::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    write(targetId, file);
+    write(targetAttrib, file);
+    write(keytimes, file);
+    write(keyValues, file);
+    write(tangentsIn, file);
+    write(tangentsOut, file);
+    write(interpolations, file);
+}
+
+void AnimationChannel::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "targetId", targetId);
+    fprintfElement(file, "targetAttrib", targetAttrib);
+    fprintfElement(file, "%f ", "keytimes", keytimes);
+    fprintfElement(file, "%f ", "values", keyValues);
+    fprintfElement(file, "%f ", "tangentsIn", tangentsIn);
+    fprintfElement(file, "%f ", "tangentsOut", tangentsOut);
+    fprintfElement(file, "%u ", "interpolations", interpolations);
+    fprintElementEnd(file);
+}
+
+void AnimationChannel::setTargetId(const std::string str)
+{
+    targetId = str;
+}
+
+void AnimationChannel::setTargetAttribute(unsigned int attrib)
+{
+    targetAttrib = attrib;
+}
+
+void AnimationChannel::setKeyTimes(const std::vector<float>& values)
+{
+    keytimes = values;
+}
+
+void AnimationChannel::setKeyValues(const std::vector<float>& values)
+{
+    keyValues = values;
+}
+
+void AnimationChannel::setTangentsIn(const std::vector<float>& values)
+{
+    tangentsIn = values;
+}
+
+void AnimationChannel::setTangentsOut(const std::vector<float>& values)
+{
+    tangentsOut = values;
+}
+
+void AnimationChannel::setInterpolations(const std::vector<unsigned int>& values)
+{
+    interpolations = values;
+}
+
+const std::vector<float>& AnimationChannel::getKeyValues() const
+{
+    return keyValues;
+}
+
+unsigned int AnimationChannel::getInterpolationType(const char* str)
+{
+    unsigned int value = 0;
+    switch (str[0])
+    {
+    case 'L':
+        if (strcmp(str, "LINEAR") == 0)
+        {
+            value = AnimationChannel::LINEAR;
+        }
+        break;
+    case 'B':
+        if (strcmp(str, "BEZIER") == 0)
+        {
+            value = AnimationChannel::BEZIER;
+        }
+        else if (strcmp(str, "BSPLINE") == 0)
+        {
+            value = AnimationChannel::BSPLINE;
+        }
+        break;
+    case 'C':
+        if (strcmp(str, "CARDINAL") == 0)
+        {
+            value = AnimationChannel::CARDINAL;
+        }
+        break;
+    case 'H':
+        if (strcmp(str, "HERMITE") == 0)
+        {
+            value = AnimationChannel::HERMITE;
+        }
+        break;
+    case 'S':
+        if (strcmp(str, "STEP") == 0)
+        {
+            value = AnimationChannel::STEP;
+        }
+        break;
+    default:
+        break;
+    }
+    return value;
+}
+
+}

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

@@ -0,0 +1,73 @@
+#ifndef ANIMATIONCHANNEL_H_
+#define ANIMATIONCHANNEL_H_
+
+#include "Object.h"
+
+namespace gameplay
+{
+
+class AnimationChannel : public Object
+{
+public:
+
+    enum InterpolationTypes
+    {
+        LINEAR = 1, 
+        BEZIER = 2, 
+        CARDINAL = 3, 
+        HERMITE = 4, 
+        BSPLINE = 5, 
+        STEP = 6
+    };
+
+    /**
+     * Constructor.
+     */
+    AnimationChannel(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~AnimationChannel(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    void setTargetId(const std::string str);
+    void setTargetAttribute(unsigned int attrib);
+
+    void setKeyTimes(const std::vector<float>& values);
+    void setKeyValues(const std::vector<float>& values);
+    void setTangentsIn(const std::vector<float>& values);
+    void setTangentsOut(const std::vector<float>& values);
+    void setInterpolations(const std::vector<unsigned int>& values);
+
+    const std::vector<float>& getKeyValues() const;
+
+    /**
+     * Returns the interpolation type value for the given string or zero if not valid.
+     * Example: "LINEAR" returns AnimationChannel::LINEAR
+     * 
+     * @param str Interpolation such as "LINEAR" or "BSPLINE".
+     * @return A value from InterpolationTypes enum or zero if not valid.
+     */
+    static unsigned int getInterpolationType(const char* str);
+
+
+private:
+
+    std::string targetId;
+    unsigned int targetAttrib;
+    std::vector<float> keytimes;
+    std::vector<float> keyValues;
+    std::vector<float> tangentsIn;
+    std::vector<float> tangentsOut;
+    std::vector<unsigned int> interpolations;
+};
+
+
+}
+#endif
+

+ 32 - 0
gameplay-encoder/src/Base.cpp

@@ -0,0 +1,32 @@
+#include "Base.h"
+
+namespace gameplay
+{
+
+void fillArray(float values[], float value, size_t length)
+{
+    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;
+}
+
+}

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

@@ -0,0 +1,54 @@
+#ifndef COMMON_H_
+#define COMMON_H_
+
+#include <assert.h>
+#include <map>
+
+#ifndef M_1_PI        
+#define M_1_PI                    0.31830988618379067154
+#endif
+#define MATH_FLOAT_SMALL        1.0e-37f
+#define MATH_TOLERANCE            2e-37f
+#define MATH_E                     2.71828182845904523536f
+#define MATH_LOG10E                0.4342944819032518f
+#define MATH_LOG2E                1.442695040888963387f
+#define MATH_PI                    3.14159265358979323846f
+#define MATH_PIOOVER2            1.57079632679489661923f
+#define MATH_PIOOVER4             M_PI_4
+#define MATH_PIX2                 6.28318530717958647693f
+#define MATH_EPSILON            0.000001f
+#define MATH_DEG_TO_RAD(x)         ((x) * 0.0174532925f)
+#define MATH_RAD_TO_DEG(x)        ((x)* 57.29577951f)
+#define MATH_RANDOM_MINUS1_1()  ((2.0f*((float)rand()/RAND_MAX))-1.0f) // Returns a random float between -1 and 1.
+#define MATH_RANDOM_0_1()       ((float)rand()/RAND_MAX) // Returns a random float between 0 and 1.
+
+namespace gameplay
+{
+
+enum VertexUsage
+{
+    UNKNOWN = 0,
+    POSITION = 1,
+    NORMAL = 2,
+    COLOR = 3,
+    TANGENT = 4,
+    BINORMAL = 5,
+    BLENDWEIGHTS = 6,
+    BLENDINDICES = 7,
+    TEXCOORD0 = 8,
+    TEXCOORD1 = 9,
+    TEXCOORD2 = 10,
+    TEXCOORD3 = 11,
+    TEXCOORD4 = 12,
+    TEXCOORD5 = 13,
+    TEXCOORD6 = 14,
+    TEXCOORD7 = 15
+};
+
+
+void fillArray(float values[], float value, size_t length);
+void setIdentityMatrix(float values[]);
+
+}
+#endif
+

+ 125 - 0
gameplay-encoder/src/Camera.cpp

@@ -0,0 +1,125 @@
+#include <assert.h>
+#include "Camera.h"
+
+namespace gameplay
+{
+
+Camera::Camera(void) :
+    fieldOfView(0.0f),
+    aspectRatio(0.0f),
+    nearPlane(0.0f),
+    farPlane(0.0f),
+    viewportWidth(0.0f),
+    viewportHeight(0.0f)
+{
+}
+
+Camera::~Camera(void)
+{
+}
+
+void Camera::setFieldOfView(float value)
+{
+    fieldOfView = value;
+}
+
+
+void Camera::setAspectRatio(float value)
+{
+    aspectRatio = value;
+}
+
+
+void Camera::setNearPlane(float value)
+{
+    nearPlane = value;
+}
+
+
+void Camera::setFarPlane(float value)
+{
+    farPlane = value;
+}
+
+unsigned int Camera::getTypeId(void) const
+{
+    return CAMERA_ID;
+}
+const char* Camera::getElementName(void) const
+{
+    return "Camera";
+}
+
+void Camera::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    write(cameraType, file);
+    write(aspectRatio, file);
+    write(nearPlane, file);
+    write(farPlane, file);
+
+    if (cameraType == CameraPerspective)
+    {
+        write(fieldOfView, file);
+    }
+    else if (cameraType == CameraOrthographic)
+    {
+        write(getViewPortWidth(), file);
+        write(getViewPortHeight(), file);
+    }
+    else
+    {
+        assert(false);
+    }
+}
+void Camera::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "cameraType", cameraType == CameraPerspective ? "CameraPerspective" : "CameraOrthographic");
+    fprintfElement(file, "aspectRatio", aspectRatio);
+    fprintfElement(file, "nearPlane", nearPlane);
+    fprintfElement(file, "farPlane", farPlane);
+
+    if (cameraType == CameraPerspective)
+    {
+        fprintfElement(file, "fieldOfView", fieldOfView);
+    }
+    else if (cameraType == CameraOrthographic)
+    {
+        fprintfElement(file, "viewportWidth", viewportWidth);
+        fprintfElement(file, "viewportHeight", viewportHeight);
+    }
+    else
+    {
+        assert(false);
+    }
+    fprintElementEnd(file);
+}
+
+void Camera::setPerspective()
+{
+    cameraType = CameraPerspective;
+}
+void Camera::setOrthographic()
+{
+    cameraType = CameraOrthographic;
+}
+void Camera::setViewportWidth(float width)
+{
+    viewportWidth = width;
+}
+void Camera::setViewportHeight(float height)
+{
+    viewportHeight = height;
+}
+
+float Camera::getViewPortWidth()
+{
+    return viewportWidth;
+}
+float Camera::getViewPortHeight()
+{
+    return viewportHeight;
+}
+
+}

+ 58 - 0
gameplay-encoder/src/Camera.h

@@ -0,0 +1,58 @@
+#ifndef CAMERA_H_
+#define CAMERA_H_
+
+#include "Object.h"
+
+namespace gameplay
+{
+
+class Camera : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Camera(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Camera(void);
+    void setFieldOfView(float value);
+    void setAspectRatio(float value);
+    void setNearPlane(float value);
+    void setFarPlane(float value);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    void setPerspective();
+    void setOrthographic();
+    void setViewportWidth(float width);
+    void setViewportHeight(float height);
+
+    float getViewPortWidth();
+    float getViewPortHeight();
+
+    enum CameraType
+    {
+        CameraPerspective = 1,
+        CameraOrthographic = 2
+    };
+
+private:
+    unsigned char cameraType;
+    float viewport[4];
+    float fieldOfView;
+    float aspectRatio;
+    float nearPlane;
+    float farPlane;
+    float viewportWidth;
+    float viewportHeight;
+};
+
+}
+#endif

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

@@ -0,0 +1,47 @@
+#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;
+}
+
+}

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

@@ -0,0 +1,36 @@
+#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

+ 102 - 0
gameplay-encoder/src/DAEChannelTarget.cpp

@@ -0,0 +1,102 @@
+
+#include "DAEChannelTarget.h"
+
+
+namespace gameplay
+{
+
+DAEChannelTarget::DAEChannelTarget(const domChannel* channelRef) : channel(NULL), targetElement(NULL)
+{
+    channel = channelRef;
+    const std::string target = channelRef->getTarget();
+    size_t index = target.find('/');
+    if (index == std::string::npos)
+    {
+        // If the string doesn't contain a '/' then the whole string is the id
+        // and there are no sid's being targeted.
+        targetId = target;
+    }
+    else
+    {
+        // The targetId is the part before the first '/'
+        targetId = target.substr(0, index);
+
+        // each '/' denotes another sid
+        size_t start;
+        size_t end;
+        do
+        {
+            start = index + 1;
+            end = target.find('/', start);
+        
+            std::string sub;
+            if (end == std::string::npos)
+            {
+                sub = target.substr(start);
+                // break;
+            }
+            else
+            {
+                sub = target.substr(start, end - start);
+                index = end + 1;
+            }
+            attributeIds.push_back(sub);
+        } while(end != std::string::npos);
+    }
+
+}
+
+DAEChannelTarget::~DAEChannelTarget(void)
+{
+}
+
+daeElement* DAEChannelTarget::getTargetElement()
+{
+    if (!targetElement && targetId.length() > 0)
+    {
+        daeSIDResolver resolver(channel->getDocument()->getDomRoot(), targetId.c_str());
+        targetElement = resolver.getElement();
+    }
+    return targetElement;
+}
+
+const std::string& DAEChannelTarget::getTargetId() const
+{
+    return targetId;
+}
+
+size_t DAEChannelTarget::getTargetAttributeCount() const
+{
+    return attributeIds.size();
+}
+
+daeElement* DAEChannelTarget::getTargetAttribute(size_t index)
+{
+    if (index >= attributeIds.size())
+    {
+        return NULL;
+    }
+    const std::string& att = attributeIds[index];
+    std::string sid = att.substr(0, att.find('.'));
+    daeSIDResolver resolver(getTargetElement(), sid.c_str());
+    return resolver.getElement();
+}
+
+void DAEChannelTarget::getPropertyName(size_t index, std::string* str)
+{
+    if (index < attributeIds.size())
+    {
+        // The property name is the string segment after the '.'
+        // The propery is optional so it might not be found.
+        const std::string& att = attributeIds[index];
+        size_t i = att.find('.');
+        if (i != std::string::npos && i < att.size())
+        {   
+            str->assign(att.substr(i+1));
+            return;
+        }
+    }
+    str->clear();
+}
+
+}

+ 95 - 0
gameplay-encoder/src/DAEChannelTarget.h

@@ -0,0 +1,95 @@
+#ifndef CHANNELTARGET_H_
+#define CHANNELTARGET_H_
+
+#include <dae.h>
+#include <dae/daeSIDResolver.h>
+#include <dae/domAny.h>
+#include <dom/domCOLLADA.h>
+#include <dom/domConstants.h>
+#include <dom/domElements.h>
+#include <dom/domProfile_COMMON.h>
+
+#include <vector>
+
+#include "Base.h"
+
+namespace gameplay
+{
+
+/**
+ * DAEChannelTarget represents the target attribute of the COLLADA dom element "channel".
+ * The syntax for the channel target is in the COLLADA spec under "COLLADA Target Addressing".
+ */
+class DAEChannelTarget
+{
+public:
+
+    /**
+     * Constructs the DAEChannelTarget from the given channel element.
+     */
+    DAEChannelTarget(const domChannel* channelRef);
+
+    /**
+     * Destructor.
+     */
+    virtual ~DAEChannelTarget(void);
+
+    /**
+     * Returns a pointer to the dom element that this targeted.
+     * 
+     * @return Pointer to the dom element or NULL if not found.
+     */
+    daeElement* getTargetElement();
+
+    /**
+     * Returns the target ID string.
+     */
+    const std::string& getTargetId() const;
+
+    /**
+     * Returns the number of target attributes for this channel target.
+     */
+    size_t getTargetAttributeCount() const;
+
+    /**
+     * Returns the attribute element at the given index.
+     */
+    daeElement* getTargetAttribute(size_t index);
+
+    /**
+     * Returns property name of the attribute at the given index.
+     * The property name is copied to str.
+     * If the attribute is not found or it doesn't have a property, str is cleared.
+     * 
+     * @param index Index of the attribute.
+     * @param str Destination string to copy the property name to.
+     */
+    void getPropertyName(size_t index, std::string* str);
+
+private:
+    /**
+     * Pointer to the <channel> element. 
+     */
+    const domChannel* channel;
+
+    domElement* targetElement;
+
+    /**
+     * The first part is the id attribute of an element in the instance document 
+     * or a dot segment (".") indicating that this is a relative address.
+     */
+    std::string targetId;
+
+    /**
+     * A channel target can have zero or more target attributes.
+     * Each target attribute my have a property.
+     * Example: "cube/Translate.X/Translate.Y"
+     * Result: attributeIds will contain 2 elements. "Translate.X" and "Translate.Y"
+     * Refer to the COLLADA spec "COLLADA Target Addressing".
+     */
+    std::vector<std::string> attributeIds;
+};
+
+}
+
+#endif

+ 17 - 0
gameplay-encoder/src/DAEPolygonInput.cpp

@@ -0,0 +1,17 @@
+#include "DAEPolygonInput.h"
+
+namespace gameplay
+{
+
+DAEPolygonInput::DAEPolygonInput(void) :
+    offset(0),
+    type(0),
+    accessor(NULL)
+{
+}
+
+DAEPolygonInput::~DAEPolygonInput(void)
+{
+}
+
+}

+ 23 - 0
gameplay-encoder/src/DAEPolygonInput.h

@@ -0,0 +1,23 @@
+#ifndef DAEPOLYGONINPUT_H_
+#define DAEPOLYGONINPUT_H_
+
+#include <dom/domTypes.h>
+#include <dom/domAccessor.h>
+
+namespace gameplay
+{
+
+class DAEPolygonInput
+{
+public:
+    DAEPolygonInput(void);
+    virtual ~DAEPolygonInput(void);
+
+    unsigned int offset;
+    int type;
+    domListOfFloats sourceValues;
+    domAccessor* accessor;
+};
+
+}
+#endif

+ 1934 - 0
gameplay-encoder/src/DAESceneEncoder.cpp

@@ -0,0 +1,1934 @@
+/*
+ * DAESceneEncoder.h
+ */
+
+#include <algorithm>
+
+#include "DAESceneEncoder.h"
+
+using namespace gameplay;
+
+DAESceneEncoder::DAESceneEncoder()
+    : _collada(NULL), file(NULL), vertexBlendWeights(NULL), vertexBlendIndices(NULL)
+{
+}
+
+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++ )
+    {
+        if ( inputArray[i]->getOffset() > maxOffset )
+        {
+            maxOffset = (unsigned int)inputArray[i]->getOffset();
+        }
+    }
+
+    return maxOffset;
+}
+
+void DAESceneEncoder::triangulate(DAE* dae)
+{
+    daeDatabase* dataBase = dae->getDatabase();
+
+    int geometryCount = (int)(dataBase->getElementCount(0, "geometry"));
+    for (int i = 0; i < geometryCount; i++)
+    {
+        // Find the next geometry element.
+        domGeometry* domGeometry;
+        dataBase->getElement((daeElement**)&domGeometry, i, 0, "geometry");
+
+        // Get the mesh out of the geometry.
+        const domMeshRef domMesh = domGeometry->getMesh();
+        if (!domMesh)
+        {
+            continue;
+        }
+
+        // Loop over all the polygons elements.
+        int polygonsCount = (int)(domMesh->getPolygons_array().getCount());
+        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)
+        {
+            domPolygons* domPolygons = domMesh->getPolygons_array().get(0);
+            // Remove the polygons from the mesh.
+            domMesh->removeChildElement(domPolygons);
+        }
+
+        // Loop over all the polylist elements.
+        int polylistCount = (int)(domMesh->getPolylist_array().getCount());
+        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)
+        {
+            domPolylist* domPolylist = domMesh->getPolylist_array().get(0);
+            // Remove the polylist from the mesh.
+            domMesh->removeChildElement(domPolylist);
+        }
+    }
+}
+
+void DAESceneEncoder::createTrianglesFromPolygons(domMesh* domMesh, domPolygons* domPolygons)
+{
+    // Create a new <triangles> inside the mesh that has the same material as the <polygons>.
+    domTriangles* triangles = (domTriangles*)domMesh->createAndPlace("triangles");
+    triangles->setCount(0);
+    triangles->setMaterial(domPolygons->getMaterial());
+    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++)
+    {
+        triangles->placeElement(domPolygons->getInput_array()[i]->clone());
+    }
+    
+    // Get the number of inputs and primitives for the polygons array.
+    unsigned int inputCount = getMaxOffset(domPolygons->getInput_array()) + 1;
+    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++)
+    {
+        // Check the polygons for consistancy (some exported files have had the wrong number of indices).
+        domP* domCurrentP = domPolygons->getP_array()[j];
+        int elementCount = (int)(domCurrentP->getValue().getCount());
+        if ( (elementCount % inputCount) != 0 )
+        {
+            // Skip this case.
+        }
+        else
+        {
+            unsigned int triangleCount = (elementCount / inputCount) - 2;
+            
+            // 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++)
+            {
+                // First vertex.
+                for (unsigned int l = 0; l < inputCount; l++)
+                {
+                    domTrianglesP->getValue().append(domCurrentP->getValue()[l]);
+                }
+                // Second vertex.
+                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++)
+                {
+                    domTrianglesP->getValue().append(domCurrentP->getValue()[index + l]);
+                }
+                triangles->setCount(triangles->getCount() + 1);
+            }
+        }
+    }
+}
+
+void DAESceneEncoder::createTrianglesFromPolylist(domMesh* domMesh, domPolylist* domPolylist)
+{
+    // Create a new <triangles> inside the mesh that has the same material as the <polylist>.
+    domTriangles* triangles = (domTriangles*)domMesh->createAndPlace("triangles");
+    triangles->setMaterial(domPolylist->getMaterial());
+    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++)
+    {
+        triangles->placeElement(domPolylist->getInput_array()[i]->clone());
+    }
+    
+    // Get the number of inputs and primitives for the polygons array.
+    unsigned int inputCount = getMaxOffset(domPolylist->getInput_array()) + 1;
+    unsigned int primitiveCount = domPolylist->getVcount()->getValue().getCount();
+    
+    unsigned int offset = 0;
+    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++)
+    {
+        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++)
+        {
+            // First vertex.
+            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++)
+            {
+                domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + index + l]);
+            }
+            // Third vertex.
+            index += inputCount;
+            for (unsigned int l = 0; l < inputCount; l++)
+            {
+                domTrianglesP->getValue().append(domPolylist->getP()->getValue()[offset + index + l]);
+            }
+            
+            trianglesProcessed++;
+        }
+        
+        offset += (unsigned int)domPolylist->getVcount()->getValue()[j] * inputCount;
+    }
+    triangles->setCount(trianglesProcessed);
+}
+
+void DAESceneEncoder::write(const std::string& filepath, const char* nodeId, bool text)
+{
+    std::string filenameOnly = getFilenameFromFilePath(filepath);
+    std::string dstPath = filepath.substr(0, filepath.find_last_of('/'));
+    
+    // Load the collada document
+    _collada = new DAE();
+    if (_collada->load(filepath.c_str()) != DAE_OK)
+    {
+        fprintf(stderr,"Error: COLLADA failed to open file:%s\n", filepath.c_str());
+        if (_collada)
+        {
+            delete _collada;
+            _collada = NULL;
+        }
+        return;
+    }
+
+    // Get the dom tree
+    domCOLLADA* dom = _collada->getDom(filepath.c_str());
+    if (!dom)
+    {
+        fprintf(stderr,"Error: COLLADA failed to the dom for file:%s\n", filepath.c_str());
+        if (_collada)
+        {
+            delete _collada;
+            _collada = NULL;
+        }
+        return;
+    }
+    
+    // Run collada conditioners
+    triangulate(_collada);
+
+    // Find the <visual_scene> element within the <scene>
+    const domCOLLADA::domSceneRef& domScene = dom->getScene();
+    daeElement* scene = NULL;
+    if (domScene && domScene->getInstance_visual_scene())
+    {
+        scene = domScene->getInstance_visual_scene()->getUrl().getElement();
+        if (scene)
+        {
+            if (nodeId == NULL)
+            {
+                // If the -n <node_id> parameter was not passed then write out the entire scene.
+                loadScene((domVisual_scene*)scene);
+            }
+            else
+            {
+                // Resolve/Search for the node the user specified with the -n <node_id> parameter.
+                daeSIDResolver resolver(scene, nodeId);
+                const domNode* node = daeSafeCast<domNode>(resolver.getElement());
+                if (node)
+                {
+                    //createNode(node, NULL);
+                }
+                else
+                {
+                    fprintf(stderr,"COLLADA File loaded to the dom, but node was not found with -n%s.\n", nodeId);
+                }
+            }
+        }
+        else
+        {
+             fprintf(stderr,"COLLADA File loaded to the dom, but query for the dom assets failed.\n");
+        }
+    }
+    else
+    {
+        fprintf(stderr, "COLLADA File loaded to the dom, but missing <visual_scene>.\n");
+    }
+    
+    // The animations should be loaded last
+    loadAnimations(dom);
+
+    std::string dstFilename = dstPath;
+    dstFilename.append(1, '/');
+    dstFilename.append(getFilenameNoExt(filenameOnly));
+
+    _gamePlayFile.adjust();
+
+    if (text)
+    {
+        _gamePlayFile.saveText(dstFilename + ".xml");
+    }
+    else
+    {
+        _gamePlayFile.saveBinary(dstFilename + ".gpb");
+    }
+    
+    // Cleanup
+    if (file)
+    {
+        fclose(file);
+    }
+    if (_collada)
+    {
+        delete _collada;
+        _collada = NULL;
+    }
+}
+
+void DAESceneEncoder::loadAnimations(const domCOLLADA* dom)
+{
+    // Call loadAnimation on all <animation> elements in all <library_animations>
+    const domLibrary_animations_Array& animationLibrary = dom->getLibrary_animations_array();
+    size_t animationLibraryCount = animationLibrary.getCount();
+    for (size_t i = 0; i < animationLibraryCount; i++)
+    {
+        const domLibrary_animationsRef& animationsRef = animationLibrary.get(i);
+        const domAnimation_Array& animationArray = animationsRef->getAnimation_array();
+        size_t animationCount = animationArray.getCount();
+        for (size_t i = 0; i < animationCount; i++)
+        {
+            const domAnimationRef& animationRef = animationArray.get(i);
+            loadAnimation(animationRef);
+        }
+    }
+}
+
+void DAESceneEncoder::loadAnimation(const domAnimation* animationRef)
+{
+    // <channel> points to one <sampler>
+    // <sampler> points to multiple <input> elements
+
+    Animation* animation = new Animation();
+    animation->setId(findAnimationId(animationRef));
+
+    // <channel>
+    const domChannel_Array& channelArray = animationRef->getChannel_array();
+    size_t channelArrayCount = channelArray.getCount();
+    for (size_t i = 0; i < channelArrayCount; i++)
+    {
+        AnimationChannel* animationChannel = new AnimationChannel();
+
+        const domChannelRef& channelRef = channelArray.get(i);
+
+        // <sampler>
+        daeElement* samplerElement = channelRef->getSource().getElement();
+        assert(samplerElement);
+        const domSampler* sampler = static_cast<domSampler*>(samplerElement);
+
+        // <input>
+        const domInputLocal_Array& inputArray = sampler->getInput_array();
+        size_t inputArrayCount = inputArray.getCount();
+        for (size_t i = 0; i < inputArrayCount; i++)
+        {
+            const domInputLocalRef& inputLocal = inputArray.get(i);
+
+            // <source>
+            daeElement* sourceElement = inputLocal->getSource().getElement();
+            const domSource* source = static_cast<domSource*>(sourceElement);
+
+            std::string semantic = inputLocal->getSemantic();
+            if (equals(semantic, "INTERPOLATION"))
+            {
+                // Interpolation source is a list of strings
+                loadInterpolation(source, animationChannel);
+            }
+            else
+            {
+                // The other sources are lists of floats.
+                std::vector<float> floats;
+                copyFloats(source->getFloat_array(), &floats);
+                if (equals(semantic, "INPUT"))
+                {
+                    // TODO: Ensure param name is TIME?
+                    for (std::vector<float>::iterator i = floats.begin(); i != floats.end(); i++)
+                    {
+                        // Convert seconds to milliseconds
+                        *i = *i * 1000.0f;
+                    }
+                    animationChannel->setKeyTimes(floats);
+                }
+                else if (equals(semantic, "OUTPUT"))
+                {
+                    animationChannel->setKeyValues(floats);
+                }
+                else if (equals(semantic, "IN_TANGENT"))
+                {
+                    animationChannel->setTangentsIn(floats);
+                }
+                else if (equals(semantic, "OUT_TANGENT"))
+                {
+                    animationChannel->setTangentsOut(floats);
+                }
+            }
+        }
+        // get target attribute enum value
+        loadTarget(channelRef, animationChannel);
+        
+        // Set the ID of the AnimationChannel
+        std::string channelId = channelRef->getTarget();
+        replace(channelId.begin(), channelId.end(), '/', '_');
+        // ensure no duplicate IDs
+        while (_gamePlayFile.idExists(channelId))
+        {
+            channelId.append("_");
+        }
+        animationChannel->setId(channelId);
+
+        // Animations that target a joint should be grouped with the other animations for that skin.
+        // If this animation channel targets a joint then add the animation channel to the skin's animation instead.
+        DAEChannelTarget channelTarget(channelRef);
+        daeElement* element = channelTarget.getTargetElement();
+        daeInt type = element->typeID();
+        if (type == domNode::ID())
+        {
+            const domNode* node = daeSafeCast<domNode>(element);
+            if (node->getType() == NODETYPE_JOINT)
+            {
+                const char* id = node->getId();
+                if (id)
+                {
+                    Animation* skinAnimation = _gamePlayFile.findAnimationForJoint(id);
+                    if (skinAnimation)
+                    {
+                        // TODO: Assert will fail if <controller> doesn't have an ID.
+                        assert(skinAnimation->getId().length() > 0);
+                        skinAnimation->add(animationChannel);
+                        _gamePlayFile.addAnimation(skinAnimation);
+                        continue;
+                    }
+                }
+            }
+        }
+
+        animation->add(animationChannel);
+    }
+    if (animation->getAnimationChannelCount() > 0)
+    {
+        _gamePlayFile.addAnimation(animation);
+    }
+    else
+    {
+        delete animation;
+    }
+}
+
+void DAESceneEncoder::loadInterpolation(const domSource* source, AnimationChannel* animationChannel)
+{
+    // COLLADA stores the interpolations as a list of strings while GBP uses unsigned int
+    std::vector<unsigned int> values;
+    const domListOfNames& names = source->getName_array()->getValue();
+    size_t count = (size_t)names.getCount();
+    values.resize(count);
+    if (count > 0)
+    {
+        for (size_t i = 0; i < count; i++)
+        {
+            values[i] = AnimationChannel::getInterpolationType(names.get(i));
+        }
+
+        // If all of the interpolation types are the same then only store the interpolation once
+        // 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++)
+        {
+            if (firstType != values[i])
+            {
+                allEqual = false;
+                break;
+            }
+        }
+        if (allEqual)
+        {
+            values.resize(1);
+        }
+    }
+    animationChannel->setInterpolations(values);
+}
+
+void DAESceneEncoder::loadTarget(const domChannel* channelRef, AnimationChannel* animationChannel)
+{
+    unsigned int targetProperty = 0;
+    DAEChannelTarget channelTarget(channelRef);
+
+    // 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++)
+    {
+        std::string prop;
+        channelTarget.getPropertyName(i, &prop);
+        daeElement* attributeElement = channelTarget.getTargetAttribute(i);
+        if (attributeElement)
+        {
+            daeInt type = attributeElement->typeID();
+            if (type == domRotate::ID())
+            {
+                // <rotate>
+                const domRotate* rotate = daeSafeCast<domRotate>(attributeElement);
+
+                if (prop.size() > 0)
+                {
+                    if (equalsIgnoreCase(prop, "ANGLE"))
+                    {
+                        targetProperty = Transform::ANIMATE_ROTATE;
+
+                        // get the rotation axis
+                        const domFloat4& f = rotate->getValue();
+                        float x = (float)f.get(0);
+                        float y = (float)f.get(1);
+                        float z = (float)f.get(2);
+                        
+                        // Get the angle values that were already read
+                        const std::vector<float>& keyValues = animationChannel->getKeyValues();
+                        size_t size = keyValues.size();
+                        assert(size > 0);
+                        // COLLADA only targeted a single prop but GBP requires all 4 rotate values.
+                        // 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++)
+                        {
+                            size_t k = j * 4;
+                            floats[k+0] = x;
+                            floats[k+1] = y;
+                            floats[k+2] = z;
+                            floats[k+3] = keyValues[j]; // angle
+                        }
+                        animationChannel->setKeyValues(floats);
+                    }
+                }
+            }
+            else if (type == domScale::ID())
+            {
+                // <scale>
+                //const domScale* scale = daeSafeCast<domScale>(attributeElement);
+                if (equalsIgnoreCase(prop, "X"))
+                {
+                    targetProperty = Transform::ANIMATE_SCALE_X;
+                }
+                else if (equalsIgnoreCase(prop, "Y"))
+                {
+                    targetProperty = Transform::ANIMATE_SCALE_Y;
+                }
+                else if (equalsIgnoreCase(prop, "Z"))
+                {
+                    targetProperty = Transform::ANIMATE_SCALE_Z;
+                }
+                else
+                {
+                    targetProperty = Transform::ANIMATE_SCALE;
+                }
+            }
+            else if (type == domTranslate::ID())
+            {
+                // <translate>
+                //const domTranslate* translate = daeSafeCast<domTranslate>(attributeElement);
+                if (equalsIgnoreCase(prop, "X"))
+                {
+                    targetProperty = Transform::ANIMATE_TRANSLATE_X;
+                }
+                else if (equalsIgnoreCase(prop, "Y"))
+                {
+                    targetProperty = Transform::ANIMATE_TRANSLATE_Y;
+                }
+                else if (equalsIgnoreCase(prop, "Z"))
+                {
+                    targetProperty = Transform::ANIMATE_TRANSLATE_Z;
+                }
+                else
+                {
+                    targetProperty = Transform::ANIMATE_TRANSLATE;
+                }
+            }
+            else if (type == domMatrix::ID())
+            {
+                // If the animation is targetting a matrix then convert it into
+                // a scale, rotate, translate animation by decomposing the matrix.
+                targetProperty = Transform::ANIMATE_SCALE_ROTATE_TRANSLATE;
+
+                const std::vector<float>& keyValues = animationChannel->getKeyValues();
+                assert(keyValues.size() % 16 == 0);
+                // The matrix was 16 floats and the new values will be 10 floats
+                size_t newSize = keyValues.size() / 16 * 10;
+                std::vector<float> floats(newSize);
+
+                size_t matrixCount = keyValues.size() / 16;
+                for (size_t i = 0; i < matrixCount; i++)
+                {
+                    size_t j = i * 16;
+                    // COLLADA used row-major but the Matrix class uses column-major
+                    Matrix matrix(
+                        keyValues[j+0], keyValues[j+4], keyValues[j+8], keyValues[j+12], 
+                        keyValues[j+1], keyValues[j+5], keyValues[j+9], keyValues[j+13], 
+                        keyValues[j+2], keyValues[j+6], keyValues[j+10], keyValues[j+14], 
+                        keyValues[j+3], keyValues[j+7], keyValues[j+11], keyValues[j+15]);
+                    Vector3 scale;
+                    Quaternion rotation;
+                    Vector3 translation;
+                    matrix.decompose(&scale, &rotation, &translation);
+
+                    size_t k = i * 10;
+                    floats[k+0] = scale.x;
+                    floats[k+1] = scale.y;
+                    floats[k+2] = scale.z;
+                    floats[k+3] = rotation.x;
+                    floats[k+4] = rotation.y;
+                    floats[k+5] = rotation.z;
+                    floats[k+6] = rotation.w;
+                    floats[k+7] = translation.x;
+                    floats[k+8] = translation.y;
+                    floats[k+9] = translation.z;
+                }
+                animationChannel->setKeyValues(floats);
+            }
+        }
+    }
+    animationChannel->setTargetAttribute(targetProperty);
+    animationChannel->setTargetId(channelTarget.getTargetId());
+}
+
+std::string DAESceneEncoder::findAnimationId(const domAnimation* animationRef)
+{
+    std::string animationId;
+    const char* str = animationRef->getId();
+    if (str)
+    {
+        animationId = str;
+        // check if this ID already exists
+        if (_gamePlayFile.idExists(animationId))
+        {
+            // choose a different id
+            animationId.append("_animation");
+        }
+    }
+    if (animationId.length() == 0)
+    {
+        animationId.assign("animation"); // TODO: Remove this later
+    }
+    // ensure no duplicate IDs
+    while (_gamePlayFile.idExists(animationId))
+    {
+        animationId.append("_");
+    }
+    return animationId;
+}
+
+void DAESceneEncoder::copyFloats(const domFloat_array* source, std::vector<float>* target)
+{
+    std::vector<float>& t = *target;
+
+    size_t count = (size_t)source->getCount();
+    t.resize(count);
+    const domListOfFloats& listOfFloats = source->getValue();
+    for (size_t i = 0; i < count; i++)
+    {   
+        t[i] = (float)listOfFloats.get(i);
+    }
+}
+
+void DAESceneEncoder::loadScene(const domVisual_scene* visualScene)
+{
+    Scene* scene = new Scene();
+
+    const domNode_Array& nodes = visualScene->getNode_array();
+    scene->setId(visualScene->getId());
+    size_t childCount = nodes.getCount();
+    for (size_t i = 0; i < childCount; i++)
+    {
+        scene->add(loadNode(nodes[i], NULL));
+    }
+    
+    Node* activeCameraNode = findSceneActiveCameraNode(visualScene, scene);
+    if (activeCameraNode)
+    {
+        scene->setActiveCameraNode(activeCameraNode);
+    }
+
+    _gamePlayFile.addScene(scene);
+}
+
+Node* DAESceneEncoder::findSceneActiveCameraNode(const domVisual_scene* visualScene, Scene* scene)
+{
+    // Loops through each evaluate_scene's render until an active camera node is found.
+    // Returns the first one found.
+
+    // 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++)
+    {
+        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++)
+        {
+            xsAnyURI cameraNodeURI = renders[i]->getCamera_node();
+            domNode* nodeRef = daeSafeCast<domNode>(cameraNodeURI.getElement());
+            if (nodeRef)
+            {
+                std::string id = nodeRef->getId();
+                Node* node = _gamePlayFile.getNode(id.c_str());
+                if (node)
+                {
+                    return node;
+                }
+            }
+        }
+    }
+    // Find the first node in the scene that contains a camera.
+    return scene->getFirstCameraNode();
+}
+
+Node* DAESceneEncoder::loadNode(domNode* n, Node* parent)
+{
+    Node* node = NULL;
+
+    // Check if this node has already been loaded
+    const char* id = n->getID();
+    if (id && strlen(id) > 0)
+    {
+        node = _gamePlayFile.getNode(n->getID());
+        if (node)
+            return node;
+    }
+    
+    // Load the node
+    node = new Node();
+
+    if (parent)
+    {
+        parent->addChild(node);
+    }
+    
+    if (n->getType() == NODETYPE_JOINT)
+    {
+        node->setIsJoint(true);
+    }
+
+    // Set node id
+    node->setId(n->getId());
+
+    // If this node has an id then add it to the ref table
+    _gamePlayFile.addNode(node);
+
+    transformNode(n, node);
+    loadControllerInstance(n, node);
+    loadCameraInstance(n, node);
+    loadLightInstance(n, node);
+    loadGeometryInstance(n, node);
+    
+    // Load child nodes
+    const domNode_Array& childNodes = n->getNode_array();
+    size_t childCount = childNodes.getCount();
+    for (size_t i = 0; i < childCount; i++)
+    {
+        loadNode(childNodes.get(i), node);
+    }
+    return node;
+}
+
+void DAESceneEncoder::transformNode(domNode* domNode, Node* node)
+{
+    // Apply the transform.
+    // Note that we only honor the first matrix transform specified for the DOM node.
+    const domMatrix_Array& matrixArray = domNode->getMatrix_array();
+    if (matrixArray.getCount() > 0)
+    {
+        const domMatrixRef& matrix = matrixArray.get(0);
+        if (!matrix)
+            return;
+        const domFloat4x4& tx = matrix->getValue();
+        float transform[] = {(float)tx.get(0), (float)tx.get(4), (float)tx.get(8), (float)tx.get(12),
+                              (float)tx.get(1), (float)tx.get(5), (float)tx.get(9), (float)tx.get(13),
+                              (float)tx.get(2), (float)tx.get(6), (float)tx.get(10), (float)tx.get(14),
+                              (float)tx.get(3), (float)tx.get(7), (float)tx.get(11), (float)tx.get(15)};
+        node->setTransformMatrix(transform);
+    }
+    else
+    {
+        Matrix transform;
+        calcTransform(domNode, transform);
+        node->setTransformMatrix(transform.m);
+    }
+
+    // TODO: Handle transforming by other types (SRT, etc) (see "Node" child elements spec)
+
+    /*Vector3 scale;
+    Quaternion rotation;
+    Vector3 translation;
+
+    localTransform.Decompose(&scale, &rotation, &translation);
+
+    node->SetScale(scale);
+    node->SetRotation(rotation);
+    node->SetTranslation(translation);*/
+}
+
+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++)
+    {
+        daeElementRef childElement = children[i];
+        switch (childElement->getElementType())
+        {
+            case COLLADA_TYPE::TRANSLATE:
+            {
+                domTranslate* translateNode = dynamic_cast<domTranslate*>(static_cast<daeElement*>(childElement));
+                float x = (float)translateNode->getValue().get(0);
+                float y = (float)translateNode->getValue().get(1);
+                float z = (float)translateNode->getValue().get(2);
+                dstTransform.translate(x, y, z);
+                break;
+            }
+            case COLLADA_TYPE::ROTATE:
+            {
+                domRotate* rotateNode = dynamic_cast<domRotate*>(static_cast<daeElement*>(childElement));
+                float x = (float)rotateNode->getValue().get(0);
+                float y = (float)rotateNode->getValue().get(1);
+                float z = (float)rotateNode->getValue().get(2);
+                float angle = MATH_DEG_TO_RAD((float)rotateNode->getValue().get(3));
+                if (x == 1 && y == 0 && z == 0)
+                {
+                    dstTransform.rotateX(angle);
+                }
+                else if (x == 0 && y == 1 && z == 0)
+                {
+                    dstTransform.rotateY(angle);
+                }
+                else if (x == 0 && y == 0 && z == 1)
+                {
+                    dstTransform.rotateZ(angle);
+                }
+                else
+                {
+                    dstTransform.rotate(x, y, z, angle);
+                }
+                break;
+            }
+            case COLLADA_TYPE::SCALE:
+            {
+                domScale* scaleNode = dynamic_cast<domScale*>(static_cast<daeElement*>(childElement));
+                float x = (float)scaleNode->getValue().get(0);
+                float y = (float)scaleNode->getValue().get(1);
+                float z = (float)scaleNode->getValue().get(2);
+                dstTransform.scale(x, y, z);
+                break;
+            }
+            case COLLADA_TYPE::SKEW:
+            case COLLADA_TYPE::LOOKAT:
+            default:
+            {
+                warning("Skew or lookat transform found but not supported.");
+                break;
+            }
+        }
+    }
+}
+
+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++)
+    {
+        // Get the camrea object
+        const domInstance_camera* cameraInstanceRef = instanceCameras.get(i);
+        xsAnyURI cameraURI = cameraInstanceRef->getUrl();
+        domCamera* cameraRef = daeSafeCast<domCamera>(cameraURI.getElement());
+
+        if (cameraRef)
+        {
+            CameraInstance* cameraInstance = loadCamera(cameraRef);
+            if (cameraInstance)
+            {
+                node->setCameraInstance(cameraInstance);
+            }
+        }
+        else
+        {
+            // warning
+        }
+    }
+}
+
+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++)
+    {
+        // Get the camrea object
+        const domInstance_light* lightInstanceRef = instanceLights.get(i);
+        xsAnyURI lightURI = lightInstanceRef->getUrl();
+        domLight* lightRef = daeSafeCast<domLight>(lightURI.getElement());
+
+        if (lightRef)
+        {
+            LightInstance* lightInstance = loadLight(lightRef);
+            if (lightInstance)
+            {
+                node->setLightInstance(lightInstance);
+            }
+        }
+        else
+        {
+            // warning
+        }
+    }
+}
+
+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++)
+    {
+        // Get the geometry object
+        const domInstance_geometryRef geometryInstanceRef = instanceGeometries.get(i);
+        xsAnyURI geometryURI = geometryInstanceRef->getUrl();
+        domGeometry* geometry = daeSafeCast<domGeometry>(geometryURI.getElement());
+
+        // Load the model from this geometry
+        if (geometry)
+        {
+            Model* model = loadGeometry(geometry, geometryInstanceRef->getBind_material());
+            if (model)
+            {
+                node->setModel(model);
+            }
+        }
+        else
+        {
+            warning(std::string("Failed to resolve geometry url: ") + geometryURI.getURI());
+        }
+    }
+}
+
+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++)
+    {
+        const domInstance_controllerRef controllerInstanceRef = instanceControllers.get(i);
+        xsAnyURI controllerURI = controllerInstanceRef->getUrl();
+        domController* controllerRef = daeSafeCast<domController>(controllerURI.getElement());
+
+        if (controllerRef)
+        {
+            const domSkin* skinElement = controllerRef->getSkin();
+            if (skinElement)
+            {
+                Model* model = loadSkin(skinElement);
+                if (model)
+                {
+                    // use only the first skeleton
+                    domInstance_controller::domSkeleton_Array& skeletons = controllerInstanceRef->getSkeleton_array();
+                    if (skeletons.getCount() == 0)
+                    {
+                        warning("No skeletons found for instance controller: ");
+                        delete model;
+                        continue;
+                    }
+                    else if (skeletons.getCount() > 1)
+                    {
+                        // Only log a warning, don't exit
+                        warning("More than one skeleton found for instance controller. Using first skeleton only.");
+                    }
+                    // Load the skeleton for this skin
+                    loadSkeleton((domInstance_controller::domSkeleton*)skeletons.get((size_t)0), model->getSkin());
+
+                    node->setModel(model);
+
+                    // TODO: Load animations
+                }
+            }
+        }
+        else
+        {
+            // warning
+        }
+        jointLookupTable.clear();
+        jointInverseBindPoseMatrices.clear();
+    }
+}
+
+CameraInstance* DAESceneEncoder::loadCamera(const domCamera* cameraRef)
+{
+    ///////////////////////////// CAMERA
+
+    // check if camera is already added to gamePlayFile
+    const char* id = cameraRef->getId();
+    Camera* camera = _gamePlayFile.getCamera(id);
+    if (camera == NULL)
+    {
+        camera = new Camera();
+        camera->setId(id);
+
+        // Optics
+        const domCamera::domOpticsRef opticsRef = cameraRef->getOptics();
+        if (opticsRef.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->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?
+            }
+
+            // 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;
+}
+
+LightInstance* 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());
+
+        const domLight::domTechnique_commonRef techRef = lightRef->getTechnique_common();
+
+        // Ambient light
+        {
+            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));
+            }
+        }
+
+        // Directional light
+        {
+            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));
+            }
+        }
+
+        // Spot light
+        {
+            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));
+                
+                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& quadAtt = spotRef->getQuadratic_attenuation();
+                if (quadAtt.cast())
+                {
+                    light->setQuadraticAttenuation((float)quadAtt->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
+        {
+            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->setConstantAttenuation((float)constAtt->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());
+                }
+            }
+        }
+
+        _gamePlayFile.addLight(light);
+    }
+    LightInstance* lightInstance = new LightInstance();
+    lightInstance->setLight(light);
+    return lightInstance;
+}
+
+void DAESceneEncoder::loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin)
+{
+    xsAnyURI skeletonUri = skeletonElement->getValue(); 
+    daeString skeletonId = skeletonUri.getID();
+    daeSIDResolver resolver(skeletonUri.getElement(), skeletonId);
+    domNode* rootNode = daeSafeCast<domNode>(resolver.getElement());
+    
+    // Get the lookup scene id (sid) and joint index.     
+    std::string sid = std::string(rootNode->getSid());
+    std::string id = std::string(skeletonId);   
+
+    // Has the root joint been loaded yet?
+    Node* rootJoint = (Node*)_gamePlayFile.getFromRefTable(id);
+
+    // The root node is not loaded yet, so let's load it now
+    if (rootJoint == NULL)
+    {
+        // Find the top most parent of rootNode that has not yet been loaded
+        domNode* topLevelParent = rootNode;
+        while (
+            topLevelParent->getParent() &&
+            topLevelParent->getParent()->getElementType() == COLLADA_TYPE::NODE &&
+            _gamePlayFile.getFromRefTable(topLevelParent->getParent()->getID()) == NULL)
+        {
+            topLevelParent = (domNode*)topLevelParent->getParent();
+        }
+
+        // Is the parent of this node loaded yet?
+        Node* parentNode = NULL;
+        if (topLevelParent->getParent() && 
+            topLevelParent->getParent()->getElementType() == COLLADA_TYPE::NODE &&
+            _gamePlayFile.getFromRefTable(topLevelParent->getParent()->getID()) != NULL)
+        {
+            parentNode = (Node*)_gamePlayFile.getFromRefTable(topLevelParent->getParent()->getID());
+        }
+
+        // Finally, load the node hierarchy that includes the root node
+        rootJoint = loadNode(topLevelParent, parentNode);
+    }
+    
+    if (rootJoint == NULL)
+    {
+        // This shouldn't really happen..
+        rootJoint = new Node();
+        rootJoint->setId(id);
+        _gamePlayFile.addNode(rootJoint);
+    }
+
+    skin->setNode(rootJoint);
+    
+    // Resolve and set joints array for skin
+    std::list<Node*> joints;
+    const std::list<std::string>& jointNames = skin->getJointNames();
+    for (std::list<std::string>::const_iterator i = jointNames.begin(); i != jointNames.end(); i++)
+    {
+        Object* obj = _gamePlayFile.getFromRefTable(*i);
+        if (obj)
+        {
+            Node* node = (Node*)obj;
+            joints.push_back(node);
+        }
+    }
+    skin->setJoints(joints);
+}
+
+Model* DAESceneEncoder::loadSkin(const domSkin* skinElement)
+{
+    ///////////////////////////// SKIN
+    Model* model = new Model();
+    MeshSkin* skin = new MeshSkin();
+
+    // MeshSkins have an animation container but we need to find an appropriate ID for it.
+    // If the <controller> has an ID, use that ID as the animation's ID.
+    // Need to break const qualified in order to get the parent.
+    daeElement* e = ((domSkin*)skinElement)->getParent();
+    if (e->getElementType() == COLLADA_TYPE::CONTROLLER)
+    {
+        const domController* controller = (domController*)e;
+        const char* controllerId = controller->getId();
+        if (controllerId)
+        {
+            skin->setAnimationId(controllerId);
+        }
+    }
+
+    // Bind Shape Matrix
+    const domSkin::domBind_shape_matrix* bindShapeMatrix = skinElement->getBind_shape_matrix();
+    if (bindShapeMatrix)
+    {
+        const domFloat4x4& m = bindShapeMatrix->getValue();
+        float transform[] = {(float)m.get(0), (float)m.get(4), (float)m.get(8),  (float)m.get(12),
+                             (float)m.get(1), (float)m.get(5), (float)m.get(9),  (float)m.get(13),
+                             (float)m.get(2), (float)m.get(6), (float)m.get(10), (float)m.get(14),
+                             (float)m.get(3), (float)m.get(7), (float)m.get(11), (float)m.get(15)};
+        skin->setBindShape(transform);
+    }
+
+    // Read and set our joints
+    domSkin::domJointsRef joints = skinElement->getJoints();
+    domInputLocal_Array& jointInputs = joints->getInput_array();
+
+
+    // 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++)
+    {
+        domInputLocalRef input = jointInputs.get(i);    
+        std::string inputSemantic = std::string(input->getSemantic());                
+        domURIFragmentType* sourceURI = &input->getSource();
+        sourceURI->resolveElement();                
+        domSource* source = (domSource*)(daeElement*)sourceURI->getElement();
+
+        if (equals(inputSemantic, "JOINT"))
+        {
+            // Get the joint Ids's
+            std::list<std::string> list;
+            getJointNames(source, list);
+
+            // Go through the joint list and conver them from sid to id because the sid information is
+            // lost when converting to the gameplay binary format.
+            for (std::list<std::string>::iterator i = list.begin(); i != list.end(); i++)
+            {   
+                daeSIDResolver resolver(source->getDocument()->getDomRoot(), i->c_str());
+                daeElement* element = resolver.getElement();
+                if (element && element->getElementType() == COLLADA_TYPE::NODE)
+                {
+                    domNode* node = dynamic_cast<domNode*>(static_cast<daeElement*>(element));
+                    const char* nodeId = node->getId();
+                    if (nodeId && !equals(*i, nodeId))
+                    {
+                        *i = nodeId;
+                    }
+                }
+            }
+
+            // Get the joint count and set the capacities for both the
+            jointCount = list.size();
+            jointInverseBindPoseMatrices.reserve(jointCount);
+            unsigned int j = 0;
+            for (std::list<std::string>::const_iterator i = list.begin(); i != list.end(); i++)
+            {
+                jointLookupTable[*i] = j++;
+            }
+            skin->setJointNames(list);
+        }
+    }
+
+    // Make sure we have some joints
+    if (jointCount == 0)
+    {
+        warning("No joints found for skin: ");
+        return NULL;
+    }
+
+    // Process "INV_BIND_MATRIX" next
+    for (unsigned int i = 0; i < jointInputs.getCount(); i++)
+    {
+        domInputLocalRef input = jointInputs.get(i);    
+        std::string inputSemantic = std::string(input->getSemantic());                
+        domURIFragmentType* sourceURI = &input->getSource();
+        sourceURI->resolveElement();                
+        domSource* source = (domSource*)(daeElement*)sourceURI->getElement();
+
+        if (equals(inputSemantic, "INV_BIND_MATRIX"))
+        {
+            domListOfFloats& matrixFloats = source->getFloat_array()->getValue();
+            //unsigned int matrixFloatsCount = (unsigned int)source->getFloat_array()->getCount();
+            unsigned int jointIndex = 0;
+
+            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), 
+                              (float)matrixFloats.get(jointIndex + 2), (float)matrixFloats.get(jointIndex + 6), (float)matrixFloats.get(jointIndex + 10), (float)matrixFloats.get(jointIndex + 14), 
+                              (float)matrixFloats.get(jointIndex + 3), (float)matrixFloats.get(jointIndex + 7), (float)matrixFloats.get(jointIndex + 11), (float)matrixFloats.get(jointIndex + 15));    
+
+                jointInverseBindPoseMatrices.push_back(matrix);
+                jointIndex += 16;
+            }
+        }
+    }
+
+    skin->setBindPoses(jointInverseBindPoseMatrices);
+
+    // Get the vertex weights inputs
+    domSkin::domVertex_weights* vertexWeights =  skinElement->getVertex_weights();        
+    domInputLocalOffset_Array& vertexWeightsInputs = vertexWeights->getInput_array();
+    unsigned int vertexWeightsCount = (unsigned int)vertexWeights->getCount();    
+    domListOfFloats jointWeights;    
+
+    for (unsigned int i = 0; i < jointInputs.getCount(); i++)
+    {
+        domInputLocalOffsetRef input = vertexWeightsInputs.get(i);
+        std::string inputSemantic = std::string(input->getSemantic());                
+        domURIFragmentType* sourceURI = &input->getSource();
+        sourceURI->resolveElement();                
+        domSource* source = (domSource*)(daeElement*)sourceURI->getElement();                
+
+        if (equals(inputSemantic, "WEIGHT"))
+        {
+            domFloat_array* weights = source->getFloat_array();
+            if (weights)
+                jointWeights = weights->getValue();
+        }
+    }    
+    
+    // Get the number of joint influences per vertex
+    domSkin::domVertex_weights::domVcount* vCountElement = vertexWeights->getVcount();    
+    domListOfUInts skinVertexInfluenceCounts = vCountElement->getValue();                
+    // Get the joint/weight pair data.
+    domSkin::domVertex_weights::domV* vElement = vertexWeights->getV();
+    domListOfInts skinVertexJointWeightPairIndices = vElement->getValue();
+        
+    // Get the vertex influence count for any given vertex (up to max of 4)
+    unsigned int maxVertexInfluencesCount = SCENE_SKIN_VERTEXINFLUENCES_MAX;    
+    skin->setVertexInfluenceCount(maxVertexInfluencesCount);
+
+    // Get the vertex blend weights and joint indices and
+    // allocate our vertex blend weights and blend indices arrays.
+    // These will be used and cleaned up later in LoadMesh
+    int skinVertexInfluenceCountTotal = skinVertexInfluenceCounts.getCount();
+    int totalVertexInfluencesCount = vertexWeightsCount * maxVertexInfluencesCount;
+    vertexBlendWeights = new float[totalVertexInfluencesCount];
+    vertexBlendIndices = new unsigned int[totalVertexInfluencesCount];
+
+    // Preset the default blend weights to 0.0f (no effect) and blend indices to 0 (uses the first which when multiplied
+    // will have no effect anyhow.
+    memset(vertexBlendWeights, 0, totalVertexInfluencesCount * sizeof(float));        
+    memset(vertexBlendIndices , 0, totalVertexInfluencesCount * sizeof(unsigned int));    
+    
+    int vOffset = 0;        
+    int weightOffset = 0;    
+
+    // Go through all the skin vertex influence weights from the indexed data.    
+    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);        
+        float vertexInfluencesTotalWeights = 0.0f;
+        std::vector<SkinnedVertexWeightPair> vertexInfluences;
+        //vertexInfluences.SetCapacity(vertexInfluenceCount);
+
+        // Get the index/weight pairs and some the weight totals while at it.
+        for (unsigned int j = 0; j < vertexInfluenceCount; j++)
+        {        
+            float weight = (float)jointWeights.get((unsigned int)skinVertexJointWeightPairIndices[vOffset + 1]);
+            int index = (int)skinVertexJointWeightPairIndices[vOffset];            
+            
+            // Set invalid index corresponding weights to zero
+            if (index < 0 || index > (int)vertexWeightsCount)
+            {
+                weight = 0.0f;
+                index = 0;
+            }
+
+            SkinnedVertexWeightPair pair(weight, index);
+            vertexInfluences.push_back(pair);
+            vertexInfluencesTotalWeights += weight;
+
+            vOffset+=2;
+        }
+
+        // Get up the the maximum vertex weight influence count.
+         for (unsigned int j = 0; j < maxVertexInfluencesCount; j++)
+        {            
+            if (j < vertexInfluenceCount)                
+            {        
+                SkinnedVertexWeightPair pair = vertexInfluences[j];
+                vertexBlendIndices[weightOffset] = pair.BlendIndex;
+                    
+                if (vertexInfluencesTotalWeights > 0.0f)        
+                {                
+                    vertexBlendWeights[weightOffset] = pair.BlendWeight;
+                }
+                else
+                {
+                    if (j == 0)
+                    {
+                        vertexBlendWeights[weightOffset] = 1.0f;
+                    }
+                    else
+                    {
+                        vertexBlendWeights[weightOffset] = 0.0f;
+                    }
+                }
+            }
+
+            weightOffset++;
+        }
+    }
+
+    model->setSkin(skin);
+
+    ///////////////////////////////////////////////////////////
+    // get geometry
+    xsAnyURI geometryURI = skinElement->getSource();
+    domGeometry* geometry = daeSafeCast<domGeometry>(geometryURI.getElement());
+    if (geometry)
+    {
+        const domMesh* meshElement = geometry->getMesh();
+        if (meshElement)
+        {
+            Mesh* mesh = loadMesh(meshElement, geometry->getId());
+            if (mesh)
+            {
+                model->setMesh(mesh);
+            }
+        }
+    }
+    ///////////////////////////////////////////////////////////
+
+    return model;
+}
+
+Model* DAESceneEncoder::loadGeometry(const domGeometry* geometry, const domBind_materialRef bindMaterial)
+{
+    // Does this geometry have a valid mesh?
+    // Get the mesh for the geometry (if it has one)
+    const domMesh* meshElement = geometry->getMesh();
+    if (meshElement == NULL)
+    {
+        warning(std::string("No mesh found for geometry: ") + geometry->getId());
+        return NULL;
+    }
+
+    ///////////////////////////// GEOMETRY
+
+    // Load the mesh for this model
+    Mesh* mesh = loadMesh(meshElement, geometry->getId());
+    if (mesh == NULL)
+    {
+        return NULL;
+    }
+
+    // Mesh instance
+    Model* model = new Model();
+    model->setMesh(mesh);
+    return model;
+}
+
+Mesh* DAESceneEncoder::loadMesh(const domMesh* meshElement, const std::string& geometryId)
+{
+    const domTriangles_Array& trianglesArray = meshElement->getTriangles_array();
+    unsigned int trianglesArrayCount = (unsigned int)trianglesArray.getCount();
+
+    // Ensure the data is exported as triangles.
+    if (trianglesArrayCount == 0)
+    {
+        warning(std::string("Geometry mesh has no triangles: ") + geometryId);
+        return NULL;
+    }
+
+    // Check if this mesh already exists
+    Mesh* mesh = _gamePlayFile.getMesh(geometryId.c_str());
+    if (mesh)
+    {
+        return mesh;
+    }
+    mesh = new Mesh();
+    mesh->setId(geometryId.c_str());
+    
+    std::vector<DAEPolygonInput*> polygonInputs;
+
+    // Quickly just go through each triangles array and make sure they have the same number of inputs
+    // with the same layout.
+    // const domSource_Array& sourceArray = meshElement->getSource_array();
+    const domInputLocal_Array& vertexArray = meshElement->getVertices()->getInput_array();
+
+    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++)
+    {
+        const domTrianglesRef& triangles = trianglesArray.get(i);
+        const domInputLocalOffset_Array& inputArray = triangles->getInput_array();
+
+        // If not set then determine the number of input for all the triangles.
+        if (inputCount == -1)
+        {
+            inputCount = (unsigned int)inputArray.getCount();
+
+            for (unsigned int j = 0; j < inputCount; j++)
+            {
+                const domInputLocalOffsetRef& input = inputArray.get(j);
+                std::string inputSemantic = input->getSemantic();
+
+                // If its a vertex first do an extra lookup for the inclusive inputs
+                if (equals(inputSemantic, "VERTEX"))
+                {
+                    unsigned int vertexArrayCount = (unsigned int)vertexArray.getCount();
+                    for (unsigned int k = 0; k < vertexArrayCount; k++)
+                    {
+                        const domInputLocalRef& vertexInput = vertexArray.get(k);
+                        
+                        std::string semantic = std::string(vertexInput->getSemantic());
+                        int type = getVertexUsageType(semantic);
+                        if (type == -1)
+                        {
+                            warning(std::string("Vertex semantic (") + semantic + ") is invalid/unsupported for geometry mesh: " + geometryId);
+                        }
+
+                        DAEPolygonInput* polygonInput = new DAEPolygonInput();
+                        domURIFragmentType& sourceURI = vertexInput->getSource();
+                        sourceURI.resolveElement();
+                        domSource* source = (domSource*)(daeElement*)sourceURI.getElement();
+                        polygonInput->offset = 0;
+                        polygonInput->sourceValues = source->getFloat_array()->getValue();
+                        polygonInput->type = type;
+                        polygonInputs.push_back(polygonInput);
+                    }
+                }
+                else
+                {
+                    std::string semantic = input->getSemantic();
+                    int type = getVertexUsageType(semantic);
+                    if (type == -1)
+                    {
+                        warning(std::string("Semantic (") + semantic + ") is invalid/unsupported for geometry mesh: " + geometryId);
+                    }
+
+                    DAEPolygonInput* polygonInput = new DAEPolygonInput();
+                    domURIFragmentType& sourceURI = input->getSource();
+                    sourceURI.resolveElement();
+                    domSource* source = (domSource*)(daeElement*)sourceURI.getElement();
+                    polygonInput->offset = (unsigned int)input->getOffset();
+                    polygonInput->sourceValues = source->getFloat_array()->getValue();
+                    polygonInput->type = type;
+
+                    // Get the accessor info
+                    const domSource::domTechnique_commonRef& technique = source->getTechnique_common();
+                    if (technique.cast())
+                    {
+                        const domAccessorRef& accessor = technique->getAccessor();
+                        polygonInput->accessor = accessor;
+                    }
+
+                    polygonInputs.push_back(polygonInput);
+                }
+            }
+        }
+        else
+        {
+            // 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 i = 0; i < polygonInputs.size(); i++)
+                    delete polygonInputs[i];
+                warning(std::string("Triangles do not all have the same number of input sources for geometry mesh: ") + geometryId);
+                return false;
+            }
+            else
+            {
+                // TODO: Check if they are in the same order...
+            }
+        }
+    }
+    
+    // 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++)
+    {
+        // Subset to be built.
+        MeshPart* subset = new MeshPart();
+
+        // All of the information about the triangles and the sources to access the data from.
+        domTriangles* triangles = daeSafeCast<domTriangles>(trianglesArray.get(i));
+
+        // Parse the material for this subset
+        //std::string materialName = triangles->getMaterial() == NULL ? _T("") : triangles->getMaterial();
+        //if (materialName.size() > 0)
+        ///    subset->material = ParseMaterial(bindMaterial, materialName);
+
+        //const domInputLocalOffset_Array& inputArray = triangles->getInput_array();
+        const domListOfUInts& polyInts = triangles->getP()->getValue();
+        unsigned int polyIntsCount = (unsigned int)polyInts.getCount();
+        unsigned int poly = 0;
+        unsigned int inputSourceCount = (unsigned int)polygonInputs.size();
+        unsigned int maxOffset = 0;
+
+        // Go through the polygon indices for each input source retrieve the values
+        // and iterate by its offset.
+        Vertex vertex;
+
+        for (unsigned int k = 0; k < inputSourceCount && poly < polyIntsCount;)
+        {
+            const domListOfFloats& source = polygonInputs[k]->sourceValues;
+            unsigned int offset = polygonInputs[k]->offset;
+            if (offset > maxOffset)
+                maxOffset = offset;
+            int type = polygonInputs[k]->type;
+
+            unsigned int polyIndex = (unsigned int) polyInts.get(poly + offset);
+            switch (type)
+            {
+            case POSITION:
+                vertex.reset();
+                if (vertexBlendWeights && vertexBlendIndices)
+                {
+                    vertex.hasWeights = true;
+
+                    vertex.blendWeights.x =  vertexBlendWeights[polyIndex * 4];
+                    vertex.blendWeights.y =  vertexBlendWeights[polyIndex * 4 + 1];
+                    vertex.blendWeights.z =  vertexBlendWeights[polyIndex * 4 + 2];
+                    vertex.blendWeights.w =  vertexBlendWeights[polyIndex * 4 + 3];
+
+                    vertex.blendIndices.x =  (float)vertexBlendIndices[polyIndex * 4];
+                    vertex.blendIndices.y =  (float)vertexBlendIndices[polyIndex * 4 + 1];
+                    vertex.blendIndices.z =  (float)vertexBlendIndices[polyIndex * 4 + 2];
+                    vertex.blendIndices.w =  (float)vertexBlendIndices[polyIndex * 4 + 3];
+                }
+
+                vertex.position.x = (float)source.get(polyIndex * 3);
+                vertex.position.y = (float)source.get(polyIndex * 3 + 1);
+                vertex.position.z = (float)source.get(polyIndex * 3 + 2);
+                break;
+        
+            case NORMAL:
+                vertex.hasNormal = true;
+                vertex.normal.x = (float)source.get(polyIndex * 3);
+                vertex.normal.y = (float)source.get(polyIndex * 3 + 1);
+                vertex.normal.z = (float)source.get(polyIndex * 3 + 2);
+                break;
+
+            // TODO: Handle reading of per-vertex colors.
+            // HOW do we know how many color components to read?
+            // We must examine the Collada input accessor and read the stride/count to verify this - not ONLY for Color, but we should be doing this for ALL components (i.e. Position, Normal, etc).
+//            case Color:
+//                vertex.hasColor = true;
+//                vertex.Diffuse.R = (float)source.get(polyIndex * 3);
+//                vertex.Diffuse.G = (float)source.get(polyIndex * 3 + 1);
+//                vertex.Diffuse.B = (float)source.get(polyIndex * 3 + 2);
+//                vertex.Diffuse.A = (float)source.get(polyIndex * 3 + 3);
+//                break;
+
+            case TANGENT:
+                vertex.hasTangent = true;
+                vertex.tangent.x = (float)source.get(polyIndex * 3);
+                vertex.tangent.y = (float)source.get(polyIndex * 3 + 1);
+                vertex.tangent.z = (float)source.get(polyIndex * 3 + 2);
+                break;
+
+            case BINORMAL:
+                vertex.hasBinormal = true;
+                vertex.binormal.x = (float)source.get(polyIndex * 3);
+                vertex.binormal.y = (float)source.get(polyIndex * 3 + 1);
+                vertex.binormal.z = (float)source.get(polyIndex * 3 + 2);
+                break;
+
+            case TEXCOORD0:
+                vertex.hasTexCoord = true;
+                if (polygonInputs[k]->accessor)
+                {
+                    // TODO: This assumes (s, t) are first
+                    unsigned int stride = (unsigned int)polygonInputs[k]->accessor->getStride();
+                    vertex.texCoord.x = (float)source.get(polyIndex * stride);
+                    vertex.texCoord.y = (float)source.get(polyIndex * stride + 1);
+                }
+                else
+                {
+                    vertex.texCoord.x = (float)source.get(polyIndex * 2);
+                    vertex.texCoord.y = (float)source.get(polyIndex * 2 + 1);
+                }
+                break;
+            }
+
+            // On the last input source attempt to add the vertex or index an existing one.
+            if (k == (inputSourceCount - 1))
+            {
+                // Only add unique vertices, use a hashtable and compare the hash functions of the
+                // vertices. If they exist simply lookup the index of the existing ones.
+                // otherwise add and new one and index it.
+                unsigned int index;
+                if (mesh->contains(vertex))
+                {
+                    index = mesh->getVertexIndex(vertex);
+                }
+                else
+                {
+                    index = mesh->addVertex(vertex);
+                }
+                subset->addIndex(index);
+
+                poly += (maxOffset+1);
+                k = 0;
+            }
+            else
+            {
+                k++;
+            }
+        }
+        // Add our new subset for the mesh.
+        mesh->addMeshPart(subset);
+    }
+    
+    bool hasNormals = mesh->vertices[0].hasNormal;
+    bool hasColors = mesh->vertices[0].hasColor;
+    bool hasTangents = mesh->vertices[0].hasTangent;
+    bool hasBinormals = mesh->vertices[0].hasBinormal;
+    bool hasTexCoords = mesh->vertices[0].hasTexCoord;
+    bool hasWeights = mesh->vertices[0].hasWeights;
+
+    // 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);
+    
+    // Normals
+    if (hasNormals)
+    {
+        mesh->addVetexAttribute(NORMAL, 3);
+    }
+    // Tangents
+    if (hasTangents)
+    {
+        mesh->addVetexAttribute(TANGENT, 3);
+    }
+    // Binormals
+    if (hasBinormals)
+    {
+        mesh->addVetexAttribute(BINORMAL, 3);
+    }
+    // Texture Coordinates
+    if (hasTexCoords)
+    {
+        mesh->addVetexAttribute(TEXCOORD0, 2);
+    }
+    // Diffuse Color
+    if (hasColors)
+    {
+        mesh->addVetexAttribute(COLOR, 3);
+    }
+    // Skinning BlendWeights BlendIndices
+    if (hasWeights /*vertexBlendWeights && vertexBlendIndices*/)
+    {
+        mesh->addVetexAttribute(BLENDWEIGHTS, 4);
+        mesh->addVetexAttribute(BLENDINDICES, 4);
+    }
+
+    _gamePlayFile.addMesh(mesh);
+    return mesh;
+}
+
+void DAESceneEncoder::warning(const std::string& message)
+{
+    printf("Warning: %s\n", message.c_str());
+}
+
+void DAESceneEncoder::warning(const char* message)
+{
+    printf("Warning: %s\n", message);
+}
+
+void DAESceneEncoder::getJointNames(domSource* source, std::list<std::string>& list)
+{
+    // BLENDER used name_array
+    const domName_arrayRef& nameArray = source->getName_array();
+    if (nameArray.cast())
+    {
+        domListOfNames& ids = nameArray->getValue();
+        size_t jointCount = (size_t)nameArray->getCount();
+        for (size_t j = 0; j < jointCount; j++)
+        {
+            list.push_back(std::string(ids.get(j)));
+        }
+    }
+    else
+    {
+        // Seymour used IDREF_array
+        const domIDREF_arrayRef& idArray = source->getIDREF_array();
+        if (idArray.cast())
+        {
+            xsIDREFS& ids = idArray->getValue();
+            size_t jointCount = (size_t)idArray->getCount();
+            for (size_t j = 0; j < jointCount; j++)
+            {
+                list.push_back(std::string(ids.get(j).getID()));
+            }
+        }
+    }
+}
+
+int DAESceneEncoder::getVertexUsageType(const std::string& semantic)
+{
+    int type = -1;
+    if (semantic.length() > 0)
+    {
+        switch (semantic[0])
+        {
+        case 'P':
+            if (equals(semantic, "POSITION"))
+            {
+                type = POSITION;
+            }
+            break;
+        case 'N':
+            if (equals(semantic, "NORMAL"))
+            {
+                type = NORMAL;
+            }
+        case 'C':
+            if (equals(semantic, "COLOR"))
+            {
+                type = COLOR;
+            }
+        case 'T':
+            if (equals(semantic, "TANGENT"))
+            {
+                type = TANGENT;
+            }
+            else if (equals(semantic, "TEXCOORD"))
+            {
+                type = TEXCOORD0;
+            }
+        case 'B':
+            if (equals(semantic, "BINORMAL"))
+            {
+                type = BINORMAL;
+            }
+        default:
+            break;
+        }
+    }
+    return type;
+}
+
+DAESceneEncoder::DAEPolygonInput::DAEPolygonInput(void) :
+    offset(0),
+    type(0),
+    accessor(NULL)
+{
+}
+
+DAESceneEncoder::DAEPolygonInput::~DAEPolygonInput(void)
+{
+}

+ 221 - 0
gameplay-encoder/src/DAESceneEncoder.h

@@ -0,0 +1,221 @@
+/*
+ * DAESceneEncoder.h
+ */
+
+#ifndef DAESCENEEENCODER_H_
+#define DAESCENEEENCODER_H_
+
+#include <iostream>
+#include <list>
+#include <vector>
+
+#include <dae.h>
+#include <dae/daeSIDResolver.h>
+#include <dae/domAny.h>
+#include <dom/domCOLLADA.h>
+#include <dom/domConstants.h>
+#include <dom/domElements.h>
+#include <dom/domProfile_COMMON.h>
+#include <dom/domCamera.h>
+
+#include "Base.h"
+#include "StringUtil.h"
+#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"
+#include "Model.h"
+#include "Scene.h"
+#include "Animation.h"
+#include "AnimationChannel.h"
+#include "Vertex.h"
+#include "Matrix.h"
+#include "Transform.h"
+#include "DAEChannelTarget.h"
+#include "GPBFile.h"
+
+using namespace gameplay;
+
+/**
+ * Class for binary encoding a Collada (DAE) file.
+ */
+class DAESceneEncoder
+{
+public:
+
+    static const unsigned int SCENE_SKIN_VERTEXINFLUENCES_MAX = 4;
+    
+    /**
+     * Constructor.
+     */
+    DAESceneEncoder();
+    
+    /**
+     * Destructor.
+     */
+    ~DAESceneEncoder();
+    
+    /**
+     * Writes out encoded Collada 1.4 file.
+     */
+    void write(const std::string& filepath, const char* nodeId, bool text);
+
+private:
+
+    class DAEPolygonInput
+    {
+    public:
+        DAEPolygonInput(void);
+        /**
+         * Destructor.
+         */
+        virtual ~DAEPolygonInput(void);
+
+        unsigned int offset;
+        int type;
+        domListOfFloats sourceValues;
+        domAccessor* accessor;
+    };
+
+    class SkinnedVertexWeightPair
+    {
+    public:
+        float BlendWeight;
+        unsigned int BlendIndex;
+
+        SkinnedVertexWeightPair(float blendWeight, unsigned int blendIndex) : BlendWeight(blendWeight), BlendIndex(blendIndex)
+        {                
+        }            
+
+        bool operator < (const SkinnedVertexWeightPair& value) const
+        {
+            return value.BlendWeight < BlendWeight;
+        }
+    };
+    
+    void triangulate(DAE* dae);
+    
+    void createTrianglesFromPolygons(domMesh* domMesh, domPolygons* domPolygons);
+    void createTrianglesFromPolylist(domMesh* domMesh, domPolylist* domPolylist);
+
+    Node* loadNode(domNode* node, Node* parent);
+
+    void loadScene(const domVisual_scene* visualScene);
+    void loadCameraInstance(const domNode* n, Node* node);
+    void loadControllerInstance(const domNode* n, Node* node);
+    void loadLightInstance(const domNode* n, Node* node);
+    void loadGeometryInstance(const domNode* n, Node* node);
+
+    /**
+     * Loads all animations from the given COLLADA dom into the GamePlayFile.
+     */
+    void loadAnimations(const domCOLLADA* dom);
+
+    /**
+     * Loads a COLLADA animation element.
+     * 
+     * @param animationRef Pointer to the animation dom element to load from.
+     */
+    void loadAnimation(const domAnimation* animationRef);
+
+    CameraInstance* loadCamera(const domCamera* cameraRef);
+    LightInstance* loadLight(const domLight* lightRef);
+    Model* loadSkin(const domSkin* skinElement);
+    Model* loadGeometry(const domGeometry* geometry, const domBind_materialRef bindMaterial);
+
+    void loadSkeleton(domInstance_controller::domSkeleton* skeletonElement, MeshSkin* skin);
+    
+    /**
+     * Loads interpolation curve data from the given source into the animation.
+     * 
+     * @param source The source dom element to load interpolation curves from.
+     * @param animation The destination animation to copy to.
+     */
+    void loadInterpolation(const domSource* source, AnimationChannel* animation);
+
+    /**
+     * Returns the active camera node for the given scene.
+     * 
+     * @param visualScene The collada visual scene node.
+     * @param scene The gameplay scene node.
+     * @return The active camera node or NULL if one was not found.
+     */
+    Node* findSceneActiveCameraNode(const domVisual_scene* visualScene, Scene* scene);
+
+    /**
+     * Loads and returns a mesh (geometry). If the mesh has already been loaded, it is simply returned.
+     */
+    Mesh* loadMesh(const domMesh* meshElement, const std::string& geometryId);
+
+    /**
+     * Sets the transform of node from the domNode transform.
+     */
+    void transformNode(domNode* domNode, Node* node);
+
+    /**
+     * Calculates the transform from a domNode. (<translate>, <rotate>, <scale>)
+     */
+    void calcTransform(domNode* domNode, Matrix& dstTransform);
+
+    /**
+     * Gets the joint names for the given source and appends them to the given list.
+     */
+    void getJointNames(domSource* source, std::list<std::string>& list);
+
+    void warning(const std::string& message);
+    void warning(const char* message);
+
+    /**
+     * Loads the target data into the animation from the given channel's target.
+     * Example: <channel target="Cube/location.X" />
+     * Target ID is "Cube"
+     * The target attribute is "location.X"
+     * 
+     * @param channelRef Pointer to the channel dom element.
+     * @param animation The animation to copy to.
+     */
+    void loadTarget(const domChannel* channelRef, AnimationChannel* animation);
+
+    /**
+     * Finds the ID for an animation.
+     * If the COLLADA animation element doesn't have an ID then this method will trying 
+     * to find an appropriate ID for the animation.
+     * 
+     * @param animationRef The COLLADA animation element to find an ID for.
+     * @return The ID string for the animation.
+     */
+    std::string findAnimationId(const domAnimation* animationRef);
+
+    /**
+     * Copies float values from a domFloat_array to a std::vector<float>.
+     * 
+     * @param source Source of float values to copy from.
+     * @param target Destination float vector to copy to.
+     */
+    static void copyFloats(const domFloat_array* source, std::vector<float>* target);
+
+    /**
+     * Returns the VertexUsage value for the given semantic string.
+     * 
+     * @param semantic The semantic attribute string from the COLLADA <input> element.
+     * @param The VertexUsage or -1 if the string was not recognized.
+     */
+    static int getVertexUsageType(const std::string& semantic);
+    
+    DAE* _collada;        // Collada datastore in memory to read from.
+    FILE* file;        // Output file to write to.
+    GPBFile _gamePlayFile;
+
+    std::map<std::string, int> jointLookupTable;
+    std::vector<Matrix>jointInverseBindPoseMatrices;
+    float* vertexBlendWeights;
+    unsigned int* vertexBlendIndices;
+
+};
+
+#endif

+ 37 - 0
gameplay-encoder/src/Effect.cpp

@@ -0,0 +1,37 @@
+#include "Effect.h"
+
+namespace gameplay
+{
+
+Effect::Effect(void)
+{
+}
+
+Effect::~Effect(void)
+{
+}
+
+unsigned int Effect::getTypeId(void) const
+{
+    return EFFECT_ID;
+}
+const char* Effect::getElementName(void) const
+{
+    return "Effect";
+}
+
+void Effect::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    write(vertexShader, file);
+    write(fragmentShader, file);
+}
+void Effect::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "vertexShader", vertexShader);
+    fprintfElement(file, "fragmentShader", fragmentShader);
+    fprintElementEnd(file);
+}
+
+}

+ 38 - 0
gameplay-encoder/src/Effect.h

@@ -0,0 +1,38 @@
+#ifndef EFFECT_H_
+#define EFFECT_H_
+
+#include "Object.h"
+
+namespace gameplay
+{
+
+class Effect : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Effect(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Effect(void);
+
+    virtual unsigned int getTypeId(void) const;
+
+    virtual const char* getElementName(void) const;
+
+    virtual void writeBinary(FILE* file);
+
+    virtual void writeText(FILE* file);
+
+    std::string vertexShader;
+    std::string fragmentShader;
+};
+
+
+}
+#endif
+

+ 135 - 0
gameplay-encoder/src/FileIO.cpp

@@ -0,0 +1,135 @@
+#include "FileIO.h"
+#include <assert.h>
+
+namespace gameplay
+{
+
+// Writing ot a binary file //
+
+void write(unsigned char value, FILE* file)
+{
+    size_t r = fwrite(&value, sizeof(unsigned char), 1, file);
+    assert(r == 1);
+}
+
+void write(char value, FILE* file)
+{
+    size_t r = fwrite(&value, sizeof(char), 1, file);
+    assert(r == 1);
+}
+
+void write(const char* str, FILE* file)
+{
+    size_t length = strlen(str);
+    size_t r = fwrite(str, 1, length, file);
+    assert(r == length);
+}
+
+void write(unsigned int value, FILE* file)
+{
+    size_t r = fwrite(&value, sizeof(unsigned int), 1, file);
+    assert(r == 1);
+}
+
+void write(unsigned short value, FILE* file)
+{
+    size_t r = fwrite(&value, sizeof(unsigned short), 1, file);
+    assert(r == 1);
+}
+
+void write(bool value, FILE* file)
+{
+    // write booleans as a unsigned char
+    unsigned char b = value;
+    write(b, file);
+}
+void write(float value, FILE* file)
+{
+    fwrite(&value, sizeof(float), 1, file);
+}
+void write(const float* values, int length, FILE* file)
+{
+    for (int i = 0; i < length; i++)
+    {
+        write(values[i], file);
+    }
+}
+void write(const std::string& str, FILE* file)
+{
+    // Write the length of the string
+    write(str.size(), file);
+    // Write the array of characters of the string
+    write(str.c_str(), file);
+}
+
+void writeZero(FILE* file)
+{
+    write((unsigned int)0, file);
+}
+
+// Writing to a text 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, "%f ", values[i]);
+    }
+    fprintf(file, "</%s>\n", elementName);
+}
+void fprintfElement(FILE* file, const char* elementName, const char* value)
+{
+    fprintf(file, "<%s>%s</%s>\n", elementName, value, elementName);
+}
+void fprintfElement(FILE* file, const char* elementName, const std::string& value)
+{
+    fprintf(file, "<%s>%s</%s>\n", elementName, value.c_str(), elementName);
+}
+void fprintfElement(FILE* file, const char* elementName, float value)
+{
+    fprintf(file, "<%s>%f</%s>\n", elementName, value, elementName);
+}
+void fprintfElement(FILE* file, const char* elementName, unsigned int value)
+{
+    fprintf(file, "<%s>%u</%s>\n", elementName, value, elementName);
+}
+void fprintfElement(FILE* file, const char* elementName, unsigned char value)
+{
+    fprintf(file, "<%s>%u</%s>\n", elementName, value, (unsigned int)elementName);
+}
+
+void fprintfMatrix4f(FILE* file, const float* m)
+{
+    for (size_t i = 0; i < 16; i ++)
+    {
+        float v = m[i];
+        if (v == 1.0f)
+            fprintf(file, "1.0 ");
+        else if (v == 0.0)
+            fprintf(file, "0.0 ");
+        else
+            fprintf(file, "%f ",v);
+    }
+}
+void skipString(FILE* file)
+{
+    // get the size of the char array
+    unsigned int length = 0;
+    fread(&length, sizeof(unsigned int), 1, file);
+    // skip over the unsigned int length
+    fseek(file, sizeof(unsigned int), SEEK_CUR);
+    if (length > 0)
+    {
+        // Skip over the array of chars
+        long seek = (long)(length * sizeof(char));
+        fseek(file, seek, SEEK_CUR);
+    }
+}
+
+void skipUint(FILE* file)
+{
+    fseek(file, sizeof(unsigned int), SEEK_CUR);
+}
+
+}

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

@@ -0,0 +1,121 @@
+#ifndef FILEIO_H_
+#define FILEIO_H_
+
+#include <iostream>
+#include <list>
+#include <vector>
+
+#include "Base.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.
+ */
+void fprintfElement(FILE* file, const char* elementName, float value);
+void fprintfElement(FILE* file, const char* elementName, unsigned int value);
+void fprintfElement(FILE* file, const char* elementName, unsigned char value);
+void fprintfElement(FILE* file, const char* elementName, const char* value);
+void fprintfElement(FILE* file, const char* elementName, const std::string& value);
+void fprintfElement(FILE* file, const char* elementName, const float values[], int length);
+
+template <class T>
+void fprintfElement(FILE* file, const char* format, const char* elementName, std::vector<T> list)
+{
+    fprintf(file, "<%s>", elementName);
+    std::vector<T>::const_iterator i;
+    for (i = list.begin(); i != list.end(); i++)
+    {
+        fprintf(file, format, *i);
+    }
+    fprintf(file, "</%s>\n", elementName);
+}
+
+template <class T>
+void fprintfElement(FILE* file, const char* format, const char* elementName, std::list<T> list)
+{
+    fprintf(file, "<%s>", elementName);
+    std::list<T>::const_iterator i;
+    for (i = list.begin(); i != list.end(); i++)
+    {
+        fprintf(file, format, *i);
+    }
+    fprintf(file, "</%s>\n", elementName);
+}
+
+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.
+ */
+void write(unsigned char value, FILE* file);
+void write(char value, FILE* file);
+void write(const char* str, FILE* file);
+void write(unsigned int value, FILE* file);
+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.
+ */
+void write(const std::string& str, FILE* file);
+
+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.
+ */
+template <class T>
+void write(std::list<T> list, FILE* file)
+{
+    // First write the size of the list
+    write(list.size(), file);
+    // Then write each element
+    std::list<T>::const_iterator i;
+    for (i = list.begin(); i != list.end(); i++)
+    {
+        write(*i, 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.
+ */
+template <class T>
+void write(std::vector<T> vector, FILE* file)
+{
+    // First write the size of the vector
+    write(vector.size(), file);
+    // Then write each element
+    std::vector<T>::const_iterator 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.
+ */
+void skipString(FILE* file);
+
+void skipUint(FILE* file);
+
+}
+#endif
+

+ 53 - 0
gameplay-encoder/src/Font.cpp

@@ -0,0 +1,53 @@
+#include "Font.h"
+
+namespace gameplay
+{
+
+Font::Font(void) :
+    style(0),
+    size(0),
+    texMapWidth(0),
+    texMapHeight(0)
+{
+}
+
+Font::~Font(void)
+{
+}
+
+unsigned int Font::getTypeId(void) const
+{
+    return FONT_ID;
+}
+const char* Font::getElementName(void) const
+{
+    return "Font";
+}
+
+void Font::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    write(family, file);
+    write(style, file);
+    write(size, file);
+    write(charset, file);
+    writeBinaryObjects(glyphs, file);
+    write(texMapWidth, file);
+    write(texMapHeight, file);
+    write(texMap, file);
+}
+void Font::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "family", family);
+    fprintfElement(file, "style", style);
+    fprintfElement(file, "size", size);
+    fprintfElement(file, "alphabet", charset);
+    //fprintfElement(file, "glyphs", glyphs);
+    fprintfElement(file, "texMapWidth", texMapWidth);
+    fprintfElement(file, "texMapHeight", texMapHeight);
+    //fprintfElement(file, "texMap", texMap);
+    fprintElementEnd(file);
+}
+
+}

+ 50 - 0
gameplay-encoder/src/Font.h

@@ -0,0 +1,50 @@
+#ifndef FONT_H_
+#define FONT_H_
+
+#include "Object.h"
+#include "Glyph.h"
+
+namespace gameplay
+{
+
+class Font : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Font(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Font(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    std::string family;
+    unsigned int style;
+    unsigned int size;
+    std::string  charset;
+    std::list<Glyph*> glyphs;
+    unsigned int texMapWidth;
+    unsigned int texMapHeight;
+    std::list<unsigned char> texMap;
+
+    enum FontStyle
+    {
+        PLAIN = 0,
+        BOLD = 1,
+        ITALIC = 2,
+        BOLD_ITALIC = 4
+    };
+};
+
+
+}
+#endif
+

+ 116 - 0
gameplay-encoder/src/GPBDecoder.cpp

@@ -0,0 +1,116 @@
+#include "GPBDecoder.h"
+
+namespace gameplay
+{
+
+GPBDecoder::GPBDecoder(void) : file(NULL), outFile(NULL)
+{
+}
+
+
+GPBDecoder::~GPBDecoder(void)
+{
+}
+
+void GPBDecoder::readBinary(const std::string& filepath)
+{
+    // open files
+    file = fopen(filepath.c_str(), "rb");
+    std::string outfilePath = filepath;
+    outfilePath += ".xml";
+    outFile = fopen(outfilePath.c_str(), "w");
+
+    // read and write files
+    assert(validateHeading());
+
+    fprintf(outFile, "<root>\n");
+    readRefs();
+    fprintf(outFile, "</root>\n");
+
+
+    // close files
+    fclose(outFile);
+    outFile = NULL;
+
+    fclose(file);
+    file = NULL;
+}
+
+bool GPBDecoder::validateHeading()
+{
+    const size_t HEADING_SIZE = 9;
+    const char identifier[] = { '«', 'G', 'P', 'B', '»', '\r', '\n', '\x1A', '\n' };
+
+    char heading[HEADING_SIZE];
+    for (size_t i = 0; i < HEADING_SIZE; i++)
+    {
+        if (heading[i] != identifier[i])
+        {
+            return false;
+        }
+    }
+    // read version
+    unsigned char version[2];
+    fread(version, sizeof(unsigned char), 2, file);
+    // don't care about version
+
+    return true;
+}
+
+void GPBDecoder::readRefs()
+{
+    fprintf(outFile, "<RefTable>\n");
+    // read number of refs
+    unsigned int refCount;
+    assert(read(&refCount));
+    for (size_t i = 0; i < refCount; i++)
+    {
+        readRef();
+    }
+    fprintf(outFile, "</RefTable>\n");
+}
+
+void GPBDecoder::readRef()
+{   
+    std::string xref = readString(file);
+    unsigned int type, offset;
+    assert(read(&type));
+    assert(read(&offset));
+    
+    fprintf(outFile, "<Reference>\n");
+    fprintfElement(outFile, "xref", xref);
+    fprintfElement(outFile, "type", type);
+    fprintfElement(outFile, "offset", offset);
+    fprintf(outFile, "</Reference>\n");
+}
+
+bool GPBDecoder::read(unsigned int* ptr)
+{
+    return fread(ptr, sizeof(unsigned int), 1, file) == 1;
+}
+
+std::string GPBDecoder::readString(FILE* fp)
+{
+    unsigned int length;
+    if (fread(&length, 4, 1, fp) != 1)
+    {
+        return std::string();
+    }
+
+    char* str = new char[length + 1];
+    if (length > 0)
+    {
+        if (fread(str, 1, length, fp) != length)
+        {
+            delete[] str;
+            return std::string();
+        }
+    }
+
+    str[length] = '\0';
+    std::string result(str);
+    delete[] str;
+    return result;
+}
+
+};

+ 49 - 0
gameplay-encoder/src/GPBDecoder.h

@@ -0,0 +1,49 @@
+/*
+ * GamePlayFile.h
+ */
+
+#ifndef GPBDECODER_H_
+#define GPBDECODER_H_
+
+#include <iostream>
+#include <list>
+#include <assert.h>
+
+#include "FileIO.h"
+
+namespace gameplay
+{
+/**
+ * This class is used for decoding a GPB file for the purpose of debugging.
+ */
+class GPBDecoder
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    GPBDecoder(void);
+
+    /**
+     * Destructor.
+     */
+    ~GPBDecoder(void);
+
+    void readBinary(const std::string& filepath);
+    bool validateHeading();
+
+    void readRefs();
+    void readRef();
+
+    bool read(unsigned int* ptr);
+    std::string readString(FILE* fp);
+
+private:
+    FILE* file;
+    FILE* outFile;
+};
+
+}
+
+#endif

+ 250 - 0
gameplay-encoder/src/GPBFile.cpp

@@ -0,0 +1,250 @@
+#include "GPBFile.h"
+
+namespace gameplay
+{
+
+GPBFile::GPBFile(void)
+    : file(NULL)
+{
+}
+
+GPBFile::~GPBFile(void)
+{
+}
+
+void GPBFile::saveBinary(const std::string& filepath)
+{
+    file = fopen(filepath.c_str(), "w+b");
+
+    // identifier
+    char identifier[] = { '«', 'G', 'P', 'B', '»', '\r', '\n', '\x1A', '\n' };
+    fwrite(identifier, 1, sizeof(identifier), file);
+
+    // version
+    fwrite(VERSION, 1, sizeof(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++)
+    {
+        (*i)->writeBinary(file);
+    }
+
+    // Objects
+    write(objects.size(), file);
+    for (std::list<Object*>::const_iterator i = objects.begin(); i != objects.end(); i++)
+    {
+        (*i)->writeBinary(file);
+    }
+
+    refTable.updateOffsets(file);
+    
+    fclose(file);
+}
+
+void GPBFile::saveText(const std::string& filepath)
+{
+    file = fopen(filepath.c_str(), "w");
+
+    fprintf(file, "<root>\n");
+
+    // write refs
+    refTable.writeText(file);
+
+    // meshes
+    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++)
+    {
+        (*i)->writeText(file);
+    }
+
+    fprintf(file, "</root>");
+
+    fclose(file);
+}
+
+void GPBFile::add(Object* obj)
+{
+    objects.push_back(obj);
+}
+
+void GPBFile::addScene(Scene* scene)
+{
+    addToRefTable(scene);
+    objects.push_back(scene);
+}
+
+void GPBFile::addCamera(Camera* camera)
+{
+    addToRefTable(camera);
+    cameras.push_back(camera);
+}
+
+void GPBFile::addLight(Light* light)
+{
+    addToRefTable(light);
+    lights.push_back(light);
+}
+
+void GPBFile::addMesh(Mesh* mesh)
+{
+    addToRefTable(mesh);
+    geometry.push_back(mesh);
+}
+
+void GPBFile::addNode(Node* node)
+{
+    addToRefTable(node);
+    nodes.push_back(node);
+}
+
+void GPBFile::addAnimation(Animation* animation)
+{
+    if (!idExists(animation->getId()))
+    {
+        addToRefTable(animation);
+        objects.push_back(animation);
+    }
+}
+
+void GPBFile::addToRefTable(Object* obj)
+{
+    if (obj)
+    {
+        const std::string& id = obj->getId();
+        if (id.length() > 0)
+        {
+            if (refTable.get(id) == NULL)
+                refTable.add(id, obj);
+        }
+    }
+}
+
+Object* GPBFile::getFromRefTable(const std::string& id)
+{
+    return refTable.get(id);
+}
+
+bool GPBFile::idExists(const std::string& id)
+{
+    return refTable.get(id) != NULL;
+}
+
+Camera* GPBFile::getCamera(const char* id)
+{
+    // TODO: O(n) search is not ideal
+    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)
+        {
+            return *i;
+        }
+    }
+    return NULL;
+}
+
+Light* GPBFile::getLight(const char* id)
+{
+    // TODO: O(n) search is not ideal
+    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)
+        {
+            return *i;
+        }
+    }
+    return NULL;
+}
+
+Mesh* GPBFile::getMesh(const char* id)
+{
+    // TODO: O(n) search is not ideal
+    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)
+        {
+            return *i;
+        }
+    }
+    return NULL;
+}
+
+Node* GPBFile::getNode(const char* id)
+{
+    // TODO: O(n) search is not ideal
+    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)
+        {
+            return *i;
+        }
+    }
+    return NULL;
+}
+
+void GPBFile::adjust()
+{
+    // calculate the ambient color for each scene
+    for (std::list<Object*>::iterator i = objects.begin(); i != objects.end(); i++)
+    {
+        Object* obj = *i;
+        if (obj->getTypeId() == Object::SCENE_ID)
+        {
+            Scene* scene = dynamic_cast<Scene*>(obj);
+            scene->calcAmbientColor();
+        }
+    }
+
+    // TODO:
+    // remove ambient lights
+    // for each node
+    //   if node has ambient light
+    //     if node has no camera, mesh or children but 1 ambient light
+    //       delete node and remove from ref table
+    //     delete light and remove from ref table
+    // 
+    // merge animations if possible
+    //   Search for animations that have the same target and key times and see if they can be merged.
+    //   Blender will output a simple translation animation to 3 separate animations with the same key times but targetting X, Y and Z.
+    //   This can be merged into one animation. Same for scale animations.
+}
+
+Animation* GPBFile::findAnimationForJoint(const char* id)
+{
+    for (std::map<std::string, Reference>::iterator i = refTable.begin(); i != refTable.end(); i++)
+    {
+        Reference& ref = i->second;
+        Object* obj = ref.getObj();
+        if (obj->getTypeId() == Object::NODE_ID)
+        {
+            Node* node = (Node*)obj;
+            Model* model = node->getModel();
+            if (model)
+            {
+                MeshSkin* skin = model->getSkin();
+                if (skin)
+                {
+                    if (skin->hasJoint(id))
+                    {
+                        return skin->getAnimation();
+                    }
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+}

+ 110 - 0
gameplay-encoder/src/GPBFile.h

@@ -0,0 +1,110 @@
+/*
+ * GPBFile.h
+ */
+
+#ifndef GPBFILE_H_
+#define GPBFILE_H_
+
+#include <iostream>
+#include <list>
+
+#include "FileIO.h"
+#include "Object.h"
+#include "Scene.h"
+#include "Node.h"
+#include "Camera.h"
+#include "Light.h"
+#include "Mesh.h"
+#include "Reference.h"
+#include "ReferenceTable.h"
+#include "Animation.h"
+#include "AnimationChannel.h"
+
+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, 0};
+
+class GPBFile
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    GPBFile(void);
+
+    /**
+     * Destructor.
+     */
+    ~GPBFile(void);
+
+    /**
+     * Saves the GPBFile as a binary file at filepath.
+     *
+     * @param filepath The file name and path to save to.
+     */
+    void saveBinary(const std::string& filepath);
+    /**
+     * Saves the GPBFile as a text file at filepath.
+     *
+     * @param filepath The file name and path to save to.
+     */
+    void saveText(const std::string& filepath);
+    
+    void add(Object* obj);
+    void addScene(Scene* scene);
+    void addCamera(Camera* camera);
+    void addLight(Light* light);
+    void addMesh(Mesh* mesh);
+    void addNode(Node* node);
+    void addAnimation(Animation* animation);
+
+    /**
+     * Adds the given object to the ref table.
+     */
+    void addToRefTable(Object* obj);
+    /**
+     * Returns the object with the given id. Returns NULL if not found.
+     */
+    Object* getFromRefTable(const std::string& id);
+
+    /**
+    * Returns true if the id was found in the ref table.
+    */
+    bool idExists(const std::string& id);
+
+    Camera* getCamera(const char* id);
+    Light* getLight(const char* id);
+    Mesh* getMesh(const char* id);
+    Node* getNode(const char* id);
+
+    /**
+     * Adjusts the game play binary file before it is written.
+     */
+    void adjust();
+
+    /**
+     * Returns the animation that the given joint's animation channel's should be added to.
+     * 
+     * @param id The ID of the joint.
+     * @return The animation belonging to the skin that the joint is part of.
+     */
+    Animation* findAnimationForJoint(const char* id);
+
+private:
+
+    FILE* file;
+    std::list<Object*> objects;
+    std::list<Camera*> cameras;
+    std::list<Light*> lights;
+    std::list<Mesh*> geometry;
+    std::list<Node*> nodes; 
+    ReferenceTable refTable;
+};
+
+}
+#endif

+ 40 - 0
gameplay-encoder/src/Glyph.cpp

@@ -0,0 +1,40 @@
+#include "Glyph.h"
+
+namespace gameplay
+{
+
+Glyph::Glyph(void) :
+    index(0),
+    width(0)
+{
+    fillArray(uvCoords, 0.0f, 4);
+}
+
+
+Glyph::~Glyph(void)
+{
+}
+const char* Glyph::getElementName(void) const
+{
+    return "Glyph";
+}
+
+void Glyph::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+
+    write(index, file);
+    write(width, file);
+    write(uvCoords, 4, file);
+
+}
+void Glyph::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "index", index);
+    fprintfElement(file, "width", width);
+    fprintfElement(file, "uvCoords", uvCoords, 4);
+    fprintElementEnd(file);
+}
+
+}

+ 33 - 0
gameplay-encoder/src/Glyph.h

@@ -0,0 +1,33 @@
+#ifndef GLYPH_H_
+#define GLYPH_H_
+
+#include "Object.h"
+
+namespace gameplay
+{
+
+class Glyph : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Glyph(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Glyph(void);
+
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    unsigned int index;
+    unsigned int width;
+    float uvCoords[4];
+};
+
+}
+#endif

+ 137 - 0
gameplay-encoder/src/Light.cpp

@@ -0,0 +1,137 @@
+#include "Light.h"
+
+namespace gameplay
+{
+
+Light::Light(void) :
+    lightType(0),
+    constantAttenuation(0.0f),
+    linearAttenuation(0.0f),
+    quadraticAttenuation(0.0f),
+    falloffAngle(0.0f),
+    falloffExponent(0.0f)
+{
+    fillArray(color, 0.0f, COLOR_SIZE);
+}
+
+Light::~Light(void)
+{
+}
+
+unsigned int Light::getTypeId(void) const
+{
+    return LIGHT_ID;
+}
+const char* Light::getElementName(void) const
+{
+    return "Light";
+}
+void Light::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    write(lightType, file);
+    write(color, COLOR_SIZE, file);
+
+    if (lightType == SpotLight)
+    {
+        write(constantAttenuation, file);
+        write(linearAttenuation, file);
+        write(quadraticAttenuation, file);
+        write(falloffAngle, file);
+        write(falloffExponent, file);
+    }
+    else if (lightType == PointLight)
+    {
+        write(constantAttenuation, file);
+        write(linearAttenuation, file);
+        write(quadraticAttenuation, file);
+    }
+
+}
+void Light::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "lightType", lightType);
+    fprintfElement(file, "color", color, COLOR_SIZE);
+
+    if (lightType == SpotLight)
+    {
+        fprintfElement(file, "constantAttenuation", constantAttenuation);
+        fprintfElement(file, "linearAttenuation", linearAttenuation);
+        fprintfElement(file, "quadraticAttenuation", quadraticAttenuation);
+        fprintfElement(file, "falloffAngle", falloffAngle);
+        fprintfElement(file, "falloffExponent", falloffExponent);
+    }
+    else if (lightType == PointLight)
+    {
+        fprintfElement(file, "constantAttenuation", constantAttenuation);
+        fprintfElement(file, "linearAttenuation", linearAttenuation);
+        fprintfElement(file, "quadraticAttenuation", quadraticAttenuation);
+    }
+    fprintElementEnd(file);
+}
+
+float Light::getRed() const
+{
+    return color[0];
+}
+float Light::getGreen() const
+{
+    return color[1];
+}
+float Light::getBlue() const
+{
+    return color[2];
+}
+
+bool Light::isAmbient() const
+{
+    return lightType == AmbientLight;
+}
+
+void Light::setAmbientLight()
+{
+    lightType = AmbientLight;
+}
+void Light::setDirectionalLight()
+{
+    lightType = DirectionalLight;
+}
+void Light::setPointLight()
+{
+    lightType = PointLight;
+}
+void Light::setSpotLight()
+{
+    lightType = SpotLight;
+}
+
+void Light::setColor(float r, float g, float b)
+{
+    color[0] = r;
+    color[1] = g;
+    color[2] = b;
+}
+
+void Light::setConstantAttenuation(float value)
+{
+    constantAttenuation = value;
+}
+void Light::setLinearAttenuation(float value)
+{
+    linearAttenuation = value;
+}
+void Light::setQuadraticAttenuation(float value)
+{
+    quadraticAttenuation = value;
+}
+void Light::setFalloffAngle(float value)
+{
+    falloffAngle = value;
+}
+void Light::setFalloffExponent(float value)
+{
+    falloffExponent = value;
+}
+
+}

+ 77 - 0
gameplay-encoder/src/Light.h

@@ -0,0 +1,77 @@
+#ifndef LIGHT_H_
+#define LIGHT_H_
+
+#include "Object.h"
+
+namespace gameplay
+{
+
+class Light : public Object
+{
+public:
+
+    static const int COLOR_SIZE = 3;
+
+    /**
+     * Constructor.
+     */
+    Light(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Light(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    float getRed() const;
+    float getGreen() const;
+    float getBlue() const;
+
+    bool isAmbient() const;
+
+    /**
+     * Sets the light type to ambient.
+     */
+    void setAmbientLight();
+    void setDirectionalLight();
+    void setPointLight();
+    void setSpotLight();
+
+    void setColor(float r, float g, float b);
+    void setColor(float r, float g, float b, float a);
+
+    void setConstantAttenuation(float value);
+    void setLinearAttenuation(float value);
+    void setQuadraticAttenuation(float value);
+    void setFalloffAngle(float value);
+    void setFalloffExponent(float value);
+
+    enum LightType
+    {   
+        DirectionalLight = 1,
+        PointLight = 2,
+        SpotLight = 3,
+        AmbientLight = 255
+    };
+
+private:
+
+    unsigned char lightType;
+    float color[COLOR_SIZE];
+
+    float constantAttenuation;
+    float linearAttenuation;
+    float quadraticAttenuation;
+    float falloffAngle;
+    float falloffExponent;
+};
+
+
+
+}
+#endif
+

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

@@ -0,0 +1,52 @@
+#include "LightInstance.h"
+#include "assert.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)
+{
+    //assert(ref != NULL);
+    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();
+}
+
+}

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

@@ -0,0 +1,40 @@
+#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
+

+ 38 - 0
gameplay-encoder/src/Material.cpp

@@ -0,0 +1,38 @@
+#include "Material.h"
+
+namespace gameplay
+{
+
+Material::Material(void) :
+    effect(NULL)
+{
+}
+
+Material::~Material(void)
+{
+}
+
+unsigned int Material::getTypeId(void) const
+{
+    return MATERIAL_ID;
+}
+const char* Material::getElementName(void) const
+{
+    return "Material";
+}
+
+void Material::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    //write(parameters, file);
+    //write(effect, file);
+}
+void Material::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    //fprintfElement(file, "parameters", parameters);
+    //fprintfElement(file, "effect", effect);
+    fprintElementEnd(file);
+}
+
+}

+ 37 - 0
gameplay-encoder/src/Material.h

@@ -0,0 +1,37 @@
+#ifndef MATERIAL_H_
+#define MATERIAL_H_
+
+#include "Object.h"
+#include "MaterialParameter.h"
+#include "Effect.h"
+
+namespace gameplay
+{
+
+class Material : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Material(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Material(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    std::list<MaterialParameter> parameters;
+    Effect* effect;
+};
+
+
+}
+#endif
+

+ 38 - 0
gameplay-encoder/src/MaterialParameter.cpp

@@ -0,0 +1,38 @@
+#include "MaterialParameter.h"
+
+namespace gameplay
+{
+
+MaterialParameter::MaterialParameter(void) :
+    type(0)
+{
+}
+
+MaterialParameter::~MaterialParameter(void)
+{
+}
+
+unsigned int MaterialParameter::getTypeId(void) const
+{
+    return MATERIAL_ID;
+}
+const char* MaterialParameter::getElementName(void) const
+{
+    return "MaterialParameter";
+}
+
+void MaterialParameter::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    write(value, file);
+    write(type, file);
+}
+void MaterialParameter::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "%f ", "value", value);
+    fprintfElement(file, "type", type);
+    fprintElementEnd(file);
+}
+
+}

+ 35 - 0
gameplay-encoder/src/MaterialParameter.h

@@ -0,0 +1,35 @@
+#ifndef MATERIALPARAMETER_H_
+#define MATERIALPARAMETER_H_
+
+#include "Object.h"
+
+namespace gameplay
+{
+
+class MaterialParameter : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    MaterialParameter(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~MaterialParameter(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    std::vector<float> value;
+    unsigned int type;
+};
+
+
+}
+#endif
+

+ 349 - 0
gameplay-encoder/src/Matrix.cpp

@@ -0,0 +1,349 @@
+#include "Matrix.h"
+
+namespace gameplay
+{
+
+
+Matrix::Matrix(void)
+{
+    setIdentity(m);
+}
+
+Matrix::Matrix(float m0, float m1, float m2, float m3, 
+               float m4, float m5, float m6, float m7, 
+               float m8, float m9, float m10, float m11, 
+               float m12, float m13, float m14, float m15)
+{
+    m[0] = m0;
+    m[1] = m1;
+    m[2] = m2;
+    m[3] = m3;
+    m[4] = m4;
+    m[5] = m5;
+    m[6] = m6;
+    m[7] = m7;
+    m[8] = m8;
+    m[9] = m9;
+    m[10] = m10;
+    m[11] = m11;
+    m[12] = m12;
+    m[13] = m13;
+    m[14] = m14;
+    m[15] = m15;
+}
+
+Matrix::~Matrix(void)
+{
+}
+
+void Matrix::setIdentity(float* matrix)
+{
+    memcpy(matrix, MATRIX4F_IDENTITY, MATRIX4F_SIZE);
+}
+
+void Matrix::createRotation(float x, float y, float z, float angle, float* dst)
+{
+    // Make sure the input axis is normalized
+    float n = x*x + y*y + z*z;
+    if (n != 1.0f)
+    {
+        // Not normalized
+        n = sqrtf(n);
+        if (n > 0.000001f) // prevent divide too close to zero
+        {
+            n = 1.0f / n;
+            x *= n;
+            y *= n;
+            z *= n;
+        }
+    }
+
+    float c = cos(angle);
+    float s = sin(angle);
+
+    float t = 1.0f - c;
+    float tx = t * x;
+    float ty = t * y;
+    float tz = t * z;
+    float txy = tx * y;
+    float txz = tx * z;
+    float tyz = ty * z;
+    float sx = s * x;
+    float sy = s * y;
+    float sz = s * z;
+    
+    dst[0] = c + tx*x;
+    dst[1] = txy + sz;
+    dst[2] = txz - sy;
+    dst[3] = 0.0f;
+
+    dst[4] = txy - sz;
+    dst[5] = c + ty*y;
+    dst[6] = tyz + sx;
+    dst[7] = 0.0f;
+
+    dst[8] = txz + sy;
+    dst[9] = tyz - sx;
+    dst[10] = c + tz*z;
+    dst[11] = 0.0f;
+
+    dst[12] = 0.0f;
+    dst[13] = 0.0f;
+    dst[14] = 0.0f;
+    dst[15] = 1.0f;
+}
+
+void Matrix::createRotationX(float angle, float* dst)
+{
+    setIdentity(dst);
+
+    float c = cos(angle);
+    float s = sin(angle);
+
+    dst[5]  = c;
+    dst[6]  = s;
+    dst[9]  = -s;
+    dst[10] = c;
+}
+
+void Matrix::createRotationY(float angle, float* dst)
+{
+    setIdentity(dst);
+
+    float c = cos(angle);
+    float s = sin(angle);
+
+    dst[0]  = c;
+    dst[2]  = -s;
+    dst[8]  = s;
+    dst[10] = c;
+}
+
+void Matrix::createRotationZ(float angle, float* dst)
+{
+    setIdentity(dst);
+
+    float c = cos(angle);
+    float s = sin(angle);
+
+    dst[0] = c;
+    dst[1] = s;
+    dst[4] = -s;
+    dst[5] = c;
+}
+
+void Matrix::createTranslation(float x, float y, float z, float* dst)
+{
+    setIdentity(dst);
+    dst[12] = x;
+    dst[13] = y;
+    dst[14] = z;
+}
+
+void Matrix::createScale(float x, float y, float z, float* dst)
+{
+    setIdentity(dst);
+    dst[0]  = x;
+    dst[5]  = y;
+    dst[10] = z;
+}
+
+float* Matrix::getArray()
+{
+    return m;
+}
+
+void Matrix::translate(float x, float y, float z)
+{
+    float t[16];
+    createTranslation(x, y, z, t);
+    multiply(m, t, m);
+}
+
+void Matrix::scale(float x, float y, float z)
+{
+    float s[16];
+    createScale(x, y, z, s);
+    multiply(m, s, m);
+}
+
+void Matrix::rotate(float x, float y, float z, float angle)
+{
+    float r[16];
+    createRotation(x, y, z, angle, r);
+    multiply(m, r, m);
+}
+
+void Matrix::rotateX(float angle)
+{
+    float r[16];
+    createRotationX(angle, r);
+    multiply(m, r, m);
+}
+
+void Matrix::rotateY(float angle)
+{
+    float r[16];
+    createRotationY(angle, r);
+    multiply(m, r, m);
+}
+
+void Matrix::rotateZ(float angle)
+{
+    float r[16];
+    createRotationZ(angle, r);
+    multiply(m, r, m);
+}
+
+void Matrix::multiply(const float* m1, const float* m2, float* dst)
+{
+    // Support the case where m1 or m2 is the same array as dst.
+    float product[16];
+    product[0]  = m1[0] * m2[0]  + m1[4] * m2[1] + m1[8]   * m2[2]  + m1[12] * m2[3];
+    product[1]  = m1[1] * m2[0]  + m1[5] * m2[1] + m1[9]   * m2[2]  + m1[13] * m2[3];
+    product[2]  = m1[2] * m2[0]  + m1[6] * m2[1] + m1[10]  * m2[2]  + m1[14] * m2[3];
+    product[3]  = m1[3] * m2[0]  + m1[7] * m2[1] + m1[11]  * m2[2]  + m1[15] * m2[3];
+
+    product[4]  = m1[0] * m2[4]  + m1[4] * m2[5] + m1[8]   * m2[6]  + m1[12] * m2[7];
+    product[5]  = m1[1] * m2[4]  + m1[5] * m2[5] + m1[9]   * m2[6]  + m1[13] * m2[7];
+    product[6]  = m1[2] * m2[4]  + m1[6] * m2[5] + m1[10]  * m2[6]  + m1[14] * m2[7];
+    product[7]  = m1[3] * m2[4]  + m1[7] * m2[5] + m1[11]  * m2[6]  + m1[15] * m2[7];
+
+    product[8]  = m1[0] * m2[8]  + m1[4] * m2[9] + m1[8]   * m2[10] + m1[12] * m2[11];
+    product[9]  = m1[1] * m2[8]  + m1[5] * m2[9] + m1[9]   * m2[10] + m1[13] * m2[11];
+    product[10] = m1[2] * m2[8]  + m1[6] * m2[9] + m1[10]  * m2[10] + m1[14] * m2[11];
+    product[11] = m1[3] * m2[8]  + m1[7] * m2[9] + m1[11]  * m2[10] + m1[15] * m2[11];
+
+    product[12] = m1[0] * m2[12] + m1[4] * m2[13] + m1[8]  * m2[14] + m1[12] * m2[15];
+    product[13] = m1[1] * m2[12] + m1[5] * m2[13] + m1[9]  * m2[14] + m1[13] * m2[15];
+    product[14] = m1[2] * m2[12] + m1[6] * m2[13] + m1[10] * m2[14] + m1[14] * m2[15];
+    product[15] = m1[3] * m2[12] + m1[7] * m2[13] + m1[11] * m2[14] + m1[15] * m2[15];
+    memcpy(dst, product, MATRIX4F_SIZE);
+}
+
+bool Matrix::decompose(Vector3* scale, Quaternion* rotation, Vector3* translation) const
+{
+    if (translation)
+    {
+        // Extract the translation
+        translation->x = m[12];
+        translation->y = m[13];
+        translation->z = m[14];
+    }
+
+    // nothing left to do
+    if (scale == NULL && rotation == NULL)
+        return true;
+
+    // Extract the scale.
+    // This is simply the length of each axis (row/column) in the matrix.
+    Vector3 xaxis(m[0], m[1], m[2]);
+    float scaleX = xaxis.length();
+
+    Vector3 yaxis(m[4], m[5], m[6]);
+    float scaleY = yaxis.length();
+
+    Vector3 zaxis(m[8], m[9], m[10]);
+    float scaleZ = zaxis.length();
+
+    // Determine if we have a negative scale (true if determinant is less than zero).
+    // In this case, we simply negate a single axis of the scale.
+    float det = determinant();
+    if (det < 0)
+        scaleZ = -scaleZ;
+
+    if (scale)
+    {
+        scale->x = scaleX;
+        scale->y = scaleY;
+        scale->z = scaleZ;
+    }
+
+    // nothing left to do
+    if (rotation == NULL)
+        return true;
+
+    // scale too close to zero, can't decompose rotation
+    if (scaleX < MATH_TOLERANCE || scaleY < MATH_TOLERANCE || fabs(scaleZ) < MATH_TOLERANCE)
+        return false;
+
+    float rn;
+
+    // Factor the scale out of the matrix axes
+    rn = 1.0f / scaleX;
+    xaxis.x *= rn;
+    xaxis.y *= rn;
+    xaxis.z *= rn;
+
+    rn = 1.0f / scaleY;
+    yaxis.x *= rn;
+    yaxis.y *= rn;
+    yaxis.z *= rn;
+
+    rn = 1.0f / scaleZ;
+    zaxis.x *= rn;
+    zaxis.y *= rn;
+    zaxis.z *= rn;
+
+    // Now calculate the rotation from the resulting matrix (axes)
+    float trace = xaxis.x + yaxis.y + zaxis.z + 1.0f;
+
+    if (trace > MATH_TOLERANCE)
+    {
+        float s = 0.5f / sqrt(trace);
+        rotation->w = 0.25f / s;
+        rotation->x = ( yaxis.z - zaxis.y ) * s;
+        rotation->y = ( zaxis.x - xaxis.z ) * s;
+        rotation->z = ( xaxis.y - yaxis.x ) * s;
+    }
+    else
+    {
+        if (xaxis.x > yaxis.y && xaxis.x > zaxis.z)
+        {
+            float s = 2.0f * sqrt(1.0f + xaxis.x - yaxis.y - zaxis.z);
+            rotation->w = (yaxis.z - zaxis.y ) / s;
+            rotation->x = 0.25f * s;
+            rotation->y = (yaxis.x + xaxis.y ) / s;
+            rotation->z = (zaxis.x + xaxis.z ) / s;
+        }
+        else if (yaxis.y > zaxis.z)
+        {
+            float s = 2.0f * sqrt(1.0f + yaxis.y - xaxis.x - zaxis.z);
+            rotation->w = (zaxis.x - xaxis.z ) / s;
+            rotation->x = (yaxis.x + xaxis.y ) / s;
+            rotation->y = 0.25f * s;
+            rotation->z = (zaxis.y + yaxis.z ) / s;
+        }
+        else
+        {
+            float s = 2.0f * sqrt(1.0f + zaxis.z - xaxis.x - yaxis.y );
+            rotation->w = (xaxis.y - yaxis.x ) / s;
+            rotation->x = (zaxis.x + xaxis.z ) / s;
+            rotation->y = (zaxis.y + yaxis.z ) / s;
+            rotation->z = 0.25f * s;
+        }
+    }
+
+    return true;
+}
+
+float Matrix::determinant() const
+{
+    float a0 = m[0] * m[5] - m[1] * m[4];
+    float a1 = m[0] * m[6] - m[2] * m[4];
+    float a2 = m[0] * m[7] - m[3] * m[4];
+    float a3 = m[1] * m[6] - m[2] * m[5];
+    float a4 = m[1] * m[7] - m[3] * m[5];
+    float a5 = m[2] * m[7] - m[3] * m[6];
+    float b0 = m[8] * m[13] - m[9] * m[12];
+    float b1 = m[8] * m[14] - m[10] * m[12];
+    float b2 = m[8] * m[15] - m[11] * m[12];
+    float b3 = m[9] * m[14] - m[10] * m[13];
+    float b4 = m[9] * m[15] - m[11] * m[13];
+    float b5 = m[10] * m[15] - m[11] * m[14];
+
+    // Calculate the determinant
+    return (a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0);
+}
+
+}

+ 148 - 0
gameplay-encoder/src/Matrix.h

@@ -0,0 +1,148 @@
+#ifndef MATRIX_H_
+#define MATRIX_H_
+
+#include "Vector4.h"
+#include "Vector3.h"
+#include "Vector2.h"
+#include "Quaternion.h"
+#include "FileIO.h"
+
+#define MATRIX4F_SIZE   (sizeof(float) * 16)
+#define PI 3.14159265f
+#define TORADIANS(degrees) (degrees * (PI / 180.0f))
+#define TODEGREES(radians) (radians * (180.0f / PI))
+
+
+namespace gameplay
+{
+
+/**
+ * The identify matrix.
+ */
+static const float MATRIX4F_IDENTITY[16] = 
+{
+    1.0f, 0.0f, 0.0f, 0.0f,
+    0.0f, 1.0f, 0.0f, 0.0f,
+    0.0f, 0.0f, 1.0f, 0.0f,
+    0.0f, 0.0f, 0.0f, 1.0f
+};
+
+class Matrix
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Matrix(void);
+
+    /**
+     * Constructor.
+     */
+    Matrix(float m0, float m1, float m2, float m3, 
+           float m4, float m5, float m6, float m7, 
+           float m8, float m9, float m10, float m11, 
+           float m12, float m13, float m14, float m15);
+
+    /**
+     * Destructor.
+     */
+    ~Matrix(void);
+
+    /**
+     * Computes the determinant of this matrix.
+     *
+     * @return The determinant.
+     */
+    float determinant() const;
+
+    /**
+     * Decomposes the scale, rotation and translation components of this matrix.
+     *
+     * @param scale The scale.
+     * @param rotation The rotation.
+     * @param translation The translation.
+     */
+    bool decompose(Vector3* scale, Quaternion* rotation, Vector3* translation) const;
+
+    /**
+     * Sets the given matrix to be the identity matrix.
+     */
+    static void setIdentity(float* matrix);
+
+    /**
+     * Multiplies two matrices and stores the results in dst. 
+     * m1 and m2 may be the same as dst.
+     */
+    static void multiply(const float* m1, const float* m2, float* dst);
+
+    /**
+     * Creates a scale matrix for the givent x,y,z scale factors.
+     */
+    static void createScale(float x, float y, float z, float* dst);
+
+    /**
+     * Creates a rotation matrix from the given axis and angle in degrees.
+     */
+    static void createRotation(float x, float y, float z, float angle, float* dst);
+    
+    /**
+     * Creates a rotation matrix for the given angle in the x axis and angle in degrees
+     */
+    void createRotationX(float angle, float* dst);
+    
+    /**
+     * Creates a rotation matrix for the given angle in the y axis and angle in degrees
+     */
+    void createRotationY(float angle, float* dst);
+        
+    /**
+     * Creates a rotation matrix for the given angle in the z axis and angle in degrees
+     */
+    void createRotationZ(float angle, float* dst);
+    
+    /**
+     * Creates a translation matrix for the given x, y ,x values.
+     */
+    static void createTranslation(float x, float y, float z, float* dst);
+
+    /**
+     * Returns the pointer to the float array.
+     */
+    float* getArray();
+
+    /**
+     * Translates the matrix by the x, y, z values.
+     */
+    void translate(float x, float y, float z);
+
+    /**
+     * Scales the matrix by the x, y, z factors.
+     */
+    void scale(float x, float y, float z);
+
+    /**
+     * Rotates the matrix by the axies specified and angle.
+     */
+    void rotate(float x, float y, float z, float angle);
+
+    /**
+     * Rotates the matrix on the y-axis by the specified angle in degrees.
+     */
+    void rotateX(float angle);
+    
+    /**
+     * Rotates the matrix on the y-axis by the specified angle in degrees.
+     */
+    void rotateY(float angle);
+
+    /**
+     * Rotates the matrix on the z-axis by the specified angle in degrees.
+     */
+    void rotateZ(float angle);
+
+    float m[16];
+};
+
+}
+#endif

+ 204 - 0
gameplay-encoder/src/Mesh.cpp

@@ -0,0 +1,204 @@
+#include "Mesh.h"
+#include <cmath>
+
+namespace gameplay
+{
+
+Mesh::Mesh(void)
+{
+}
+
+Mesh::~Mesh(void)
+{
+}
+
+unsigned int Mesh::getTypeId(void) const
+{
+    return MESH_ID;
+}
+
+const char* Mesh::getElementName(void) const
+{
+    return "Mesh";
+}
+
+void Mesh::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    // vertex formats
+    write(vertexFormats.size(), file);
+    for (std::vector<VertexElement>::iterator i = vertexFormats.begin(); i != vertexFormats.end(); i++)
+    {
+        i->writeBinary(file);
+    }
+    // vertices
+    writeBinaryVertices(file);
+    // parts
+    writeBinaryObjects(parts, file);
+}
+
+void Mesh::writeBinaryVertices(FILE* file)
+{
+    if (vertices.size() > 0)
+    {
+        // Assumes that all vertices are the same size.
+        // Write the number of bytes for the vertex data
+        const Vertex& vertex = vertices.front();
+        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++)
+        {
+            // Write this vertex
+            i->writeBinary(file);
+        }
+    }
+    else
+    {
+        // No vertex data
+        write((unsigned int)0, file);
+    }
+
+    // Write bounds
+    computeBounds();
+    write(&bounds.min.x, 3, file);
+    write(&bounds.max.x, 3, file);
+    write(&bounds.center.x, 3, file);
+    write(bounds.radius, file);
+}
+
+void Mesh::writeText(FILE* file)
+{
+    fprintElementStart(file);
+
+    // for each VertexFormat
+    if (vertices.size() > 0 )
+    {
+        for (std::vector<VertexElement>::iterator i = vertexFormats.begin(); i != vertexFormats.end(); i++)
+        {
+            i->writeText(file);
+        }
+    }
+
+    // for each Vertex
+    fprintf(file, "<vertices count=\"%u\">\n", vertices.size());
+    for (std::vector<Vertex>::iterator i = vertices.begin(); i != vertices.end(); i++)
+    {
+        i->writeText(file);
+    }
+    fprintf(file, "</vertices>\n");
+
+    // write bounds
+    computeBounds();
+    fprintf(file, "<bounds>\n");
+    fprintf(file, "<min>\n");
+    bounds.min.writeText(file);
+    fprintf(file, "</min>\n");
+    fprintf(file, "<max>\n");
+    bounds.max.writeText(file);
+    fprintf(file, "</max>\n");
+    fprintf(file, "<center>\n");
+    bounds.center.writeText(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++)
+    {
+        (*i)->writeText(file);
+    }
+
+    fprintElementEnd(file);
+}
+
+void Mesh::addMeshPart(MeshPart* part)
+{
+    parts.push_back(part);
+}
+
+void Mesh::addMeshPart(Vertex* vertex)
+{
+    vertices.push_back(*vertex);
+}
+
+void Mesh::addVetexAttribute(unsigned int usage, unsigned int count)
+{
+    vertexFormats.push_back(VertexElement(usage, count));
+}
+
+size_t Mesh::getVertexCount() const
+{
+    return vertices.size();
+}
+
+bool Mesh::contains(const Vertex& vertex) const
+{
+    return vertexLookupTable.count(vertex) > 0;
+}
+
+unsigned int Mesh::addVertex(const Vertex& vertex)
+{
+    unsigned int index = getVertexCount();
+    vertices.push_back(vertex);
+    vertexLookupTable[vertex] = index;
+    return index;
+}
+
+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;
+}
+
+void Mesh::computeBounds()
+{
+    bounds.min.x = bounds.min.y = bounds.min.z = FLT_MAX;
+    bounds.max.x = bounds.max.y = bounds.max.z = FLT_MIN;
+    bounds.center.x = bounds.center.y = bounds.center.z = 0.0f;
+    bounds.radius = 0.0f;
+    
+    // for each vertex
+    Vector3 avgPos;
+    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)
+            bounds.min.x = i->position.x;
+        if (i->position.y < bounds.min.y)
+            bounds.min.y = i->position.y;
+        if (i->position.z < bounds.min.z)
+            bounds.min.z = i->position.z;
+        if (i->position.x > bounds.max.x)
+            bounds.max.x = i->position.x;
+        if (i->position.y > bounds.max.y)
+            bounds.max.y = i->position.y;
+        if (i->position.z > bounds.max.z)
+            bounds.max.z = i->position.z;
+
+        avgPos.x += i->position.x;
+        avgPos.y += i->position.y;
+        avgPos.z += i->position.z;
+    }
+
+    // Compute center point
+    bounds.center.x = avgPos.x / (float)vertices.size();
+    bounds.center.y = avgPos.y / (float)vertices.size();
+    bounds.center.z = avgPos.z / (float)vertices.size();
+
+    // 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++)
+    {
+        float d = Vector3::distanceSquared(bounds.center, i->position);
+        if (d > bounds.radius)
+            bounds.radius = d;
+    }
+
+    // Convert squared distance to distance for radius
+    bounds.radius = std::sqrtf(bounds.radius);
+}
+
+}

+ 73 - 0
gameplay-encoder/src/Mesh.h

@@ -0,0 +1,73 @@
+#ifndef MESH_H_
+#define MESH_H_
+
+#include "Object.h"
+#include "MeshPart.h"
+#include "VertexElement.h"
+
+namespace gameplay
+{
+
+class Mesh : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Mesh(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Mesh(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+
+    virtual void writeBinary(FILE* file);
+    void writeBinaryVertices(FILE* file);
+
+    virtual void writeText(FILE* file);
+    void writeText(FILE* file, const Vertex& vertex);
+    void writeText(FILE* file, const Vector3& v);
+
+    void addMeshPart(MeshPart* part);
+    void addMeshPart(Vertex* vertex);
+    void addVetexAttribute(unsigned int usage, unsigned int count);
+
+    size_t getVertexCount() const;
+
+    /**
+     * Returns true if this MeshPart contains the given Vertex.
+     */
+    bool contains(const Vertex& vertex) const;
+
+    /**
+     * Adds a vertex to this MeshPart and returns the index.
+     */
+    unsigned int addVertex(const Vertex& vertex);
+
+    unsigned int getVertexIndex(const Vertex& vertex);
+
+    std::vector<Vertex> vertices;
+    std::vector<MeshPart*> parts;
+    struct
+    {
+        Vector3 min;
+        Vector3 max;
+        Vector3 center;
+        float radius;
+    } bounds;
+    std::map<Vertex, unsigned int> vertexLookupTable;
+
+private:
+
+    void computeBounds();
+
+    std::vector<VertexElement> vertexFormats;
+
+};
+
+}
+#endif

+ 103 - 0
gameplay-encoder/src/MeshPart.cpp

@@ -0,0 +1,103 @@
+#include "MeshPart.h"
+
+namespace gameplay
+{
+
+MeshPart::MeshPart(void) :
+    primitiveType(TRIANGLES),
+    indexFormat(INDEX8)
+{
+}
+
+MeshPart::~MeshPart(void)
+{
+}
+
+unsigned int MeshPart::getTypeId(void) const
+{
+    return MESHPART_ID;
+}
+const char* MeshPart::getElementName(void) const
+{
+    return "MeshPart";
+}
+void MeshPart::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+
+    write(primitiveType, file);
+    write(indexFormat, 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++)
+    {
+        writeBinaryIndex(*i, file);
+    }
+}
+void MeshPart::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "primitiveType", primitiveType);
+    fprintfElement(file, "indexFormat", indexFormat);
+    fprintfElement(file, "%d ", "indices", indices);
+    fprintElementEnd(file);
+}
+
+void MeshPart::addIndex(unsigned int index)
+{
+    updateIndexFormat(index);
+    indices.push_back(index);
+}
+
+size_t MeshPart::getIndicesCount() const
+{
+    return indices.size();
+}
+
+unsigned int MeshPart::indicesByteSize() const
+{
+    return indices.size() * indexFormatSize();
+}
+
+unsigned int MeshPart::indexFormatSize() const
+{
+    switch (indexFormat)
+    {
+    case INDEX32:
+        return 4;
+    case INDEX16:
+        return 2;
+    case INDEX8:
+    default:
+        return 1;
+    }
+}
+
+void MeshPart::writeBinaryIndex(unsigned int index, FILE* file)
+{
+    switch (indexFormat)
+    {
+    case INDEX32:
+        write(index, file);
+        break;
+    case INDEX16:
+        write((unsigned short)index, file);
+        break;
+    case INDEX8:
+    default:
+        write((unsigned char)index, file);
+        break;
+    }
+}
+
+void MeshPart::updateIndexFormat(unsigned int newIndex)
+{
+    if (newIndex >= 65536)
+        indexFormat = INDEX32;
+    else if (newIndex >= 256 && indexFormat != INDEX32)
+        indexFormat = INDEX16;
+}
+
+}

+ 92 - 0
gameplay-encoder/src/MeshPart.h

@@ -0,0 +1,92 @@
+#ifndef MESHPART_H_
+#define MESHPART_H_
+
+#include <vector>
+
+#include "Base.h"
+#include "Object.h"
+#include "Vertex.h"
+
+namespace gameplay
+{
+
+class MeshPart : public Object
+{
+public:
+
+    enum PrimitiveType
+    {
+        TRIANGLES = 4,      // GL_TRIANGLES
+        TRIANGLE_STRIP = 5, // GL_TRIANGLE_STRIP
+        LINES = 1,          // GL_LINES
+        LINE_STRIP = 3,     // GL_LINE_STRIP
+        POINTS = 0          // GL_POINTS
+    };
+
+    enum IndexFormat
+    {
+        INDEX8  = 0x1401, // GL_UNSIGNED_BYTE
+        INDEX16 = 0x1403, // GL_UNSIGNED_SHORT
+        INDEX32 = 0x1405  // GL_UNSIGNED_INT
+    };
+
+
+    /**
+     * Constructor.
+     */
+    MeshPart(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~MeshPart(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    /**
+     * Adds an index to the list of indices.
+     */
+    void addIndex(unsigned int index);
+
+    /**
+     * Returns the number of indices.
+     */
+    size_t getIndicesCount() const;
+
+private:
+
+    /**
+     * Returns the size of the indices array in bytes.
+     */
+    unsigned int indicesByteSize() const;
+
+    /**
+     * Returns the size of an index.
+     */
+    unsigned int indexFormatSize() const;
+
+    /**
+     * Updates the index format if newIndex is larger than 256 or 65536.
+     */
+    void updateIndexFormat(unsigned int newIndex);
+
+    /**
+     * Writes the index to the binary file stream.
+     * The number of bytes written depends on indexFormat.
+     */
+    void writeBinaryIndex(unsigned int index, FILE* file);
+
+private:
+    unsigned int primitiveType;
+    unsigned int indexFormat;
+    std::vector<unsigned int> indices;
+};
+
+
+
+}
+#endif
+

+ 153 - 0
gameplay-encoder/src/MeshSkin.cpp

@@ -0,0 +1,153 @@
+#include "MeshSkin.h"
+#include "Node.h"
+#include "StringUtil.h"
+
+namespace gameplay
+{
+
+MeshSkin::MeshSkin(void) :
+    influences(NULL),
+    vertexInfluenceCount(0),
+    animation(NULL)
+{
+    setIdentityMatrix(bindShape);
+}
+
+MeshSkin::~MeshSkin(void)
+{
+}
+
+unsigned int MeshSkin::getTypeId(void) const
+{
+    return MESHPART_ID;
+}
+
+const char* MeshSkin::getElementName(void) const
+{
+    return "MeshSkin";
+}
+
+void MeshSkin::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    // bindShape
+    write(bindShape, 16, file);
+    // root joint
+    write(influences->getId(), file);
+    /*
+    if (influences != NULL)
+    {
+        influences->writeBinaryXref(file);
+    }
+    else
+    {
+        write((unsigned int)0, file);
+    }
+    */
+    // joints
+    write(joints.size(), file);
+    for (std::list<Node*>::const_iterator i = joints.begin(); i != joints.end(); i++)
+    {
+        (*i)->writeBinaryXref(file);
+    }
+    // bindPoses
+    write(bindPoses, file);
+}
+
+void MeshSkin::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintf(file, "<bindShape>");
+    fprintfMatrix4f(file, bindShape);
+    fprintf(file, "</bindShape>");
+    if (influences != NULL)
+    {
+        fprintf(file, "<ref xref=\"#%s\"/>", influences->getId().c_str());
+    }
+    fprintf(file, "<joints>");
+    for (std::list<std::string>::const_iterator i = jointNames.begin(); i != jointNames.end(); i++)
+    {
+        fprintf(file, "%s ", i->c_str());
+    }
+    fprintf(file, "</joints>\n");
+    fprintf(file, "<bindPoses count=\"%u\">", bindPoses.size());
+    for (std::list<float>::const_iterator i = bindPoses.begin(); i != bindPoses.end(); i++)
+    {
+        fprintf(file, "%f ", *i);
+    }
+    fprintf(file, "</bindPoses>\n");
+    fprintElementEnd(file);
+}
+
+void MeshSkin::setBindShape(const float data[])
+{
+    for (int i = 0; i < 16; i++)
+    {
+        bindShape[i] = data[i];
+    }
+}
+
+void MeshSkin::setVertexInfluenceCount(unsigned int count)
+{
+    vertexInfluenceCount = count;
+}
+
+void MeshSkin::setNode(Node* node)
+{
+    influences = node;
+}
+
+void MeshSkin::setJointNames(const std::list<std::string>& list)
+{
+    jointNames = list;
+}
+
+const std::list<std::string>& MeshSkin::getJointNames()
+{
+    return jointNames;
+}
+
+void MeshSkin::setJoints(const std::list<Node*>& list)
+{
+    joints = list;
+}
+
+void MeshSkin::setBindPoses(std::vector<Matrix>& list)
+{
+    for (std::vector<Matrix>::iterator i = list.begin(); i != list.end(); i++)
+    {   
+        float* a = i->m;
+        for (int j = 0; j < 16; j++)
+        {
+            bindPoses.push_back(a[j]);
+        }
+    }
+}
+
+bool MeshSkin::hasJoint(const char* id)
+{
+    for (std::list<std::string>::iterator i = jointNames.begin(); i != jointNames.end(); i++)
+    {
+        if (equals(*i, id))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+Animation* MeshSkin::getAnimation()
+{
+    if (!animation)
+    {
+        animation = new Animation();
+    }
+    return animation;
+}
+
+void MeshSkin::setAnimationId(const char* id)
+{
+    getAnimation()->setId(id);
+}
+
+}

+ 86 - 0
gameplay-encoder/src/MeshSkin.h

@@ -0,0 +1,86 @@
+#ifndef MESHSKIN_H_
+#define MESHSKIN_H_
+
+#include <vector>
+
+#include "Base.h"
+#include "Object.h"
+#include "Matrix.h"
+#include "Animation.h"
+
+namespace gameplay
+{
+
+class Node;
+
+class MeshSkin : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    MeshSkin(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~MeshSkin(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    void setBindShape(const float data[]);
+
+    void setVertexInfluenceCount(unsigned int count);
+
+    void setNode(Node* node);
+
+    void setJointNames(const std::list<std::string>& list);
+
+    const std::list<std::string>& getJointNames();
+
+    void setJoints(const std::list<Node*>& list);
+
+    void setBindPoses(std::vector<Matrix>& list);
+
+    /**
+     * Returns true if the MeshSkin contains a joint with the given ID.
+     * 
+     * @param id The ID of the joint to search for.
+     * @return True if the joint belongs to this skin, false otherwise.
+     */
+    bool hasJoint(const char* id);
+
+    /**
+     * Returns the animation that contains the animation channels that target this skin's joints.
+     * 
+     * @return The animation for this skin.
+     */
+    Animation* getAnimation();
+
+    /**
+     * Sets the id of the skin's animation.
+     * 
+     * @param id The new ID.
+     */
+    void setAnimationId(const char* id);
+
+private:
+
+    Node* influences;
+    float bindShape[16];
+    std::list<Node*> joints;
+    std::list<float> bindPoses;
+
+    std::list<std::string> jointNames;
+
+    unsigned int vertexInfluenceCount;
+
+    Animation* animation;
+};
+
+}
+#endif

+ 39 - 0
gameplay-encoder/src/MeshSubSet.cpp

@@ -0,0 +1,39 @@
+#include "MeshSubSet.h"
+
+namespace gameplay
+{
+
+MeshSubSet::MeshSubSet(void)
+{
+    
+}
+
+
+MeshSubSet::~MeshSubSet(void)
+{
+}
+
+byte MeshSubSet::getTypeId(void)
+{
+    return MESHPART_ID;
+}
+const char* MeshSubSet::getElementName(void)
+{
+    return "MeshSubSet";
+}
+
+void MeshSubSet::writeBinary(FILE* file)
+{
+    write(getTypeId(), file);
+
+}
+void MeshSubSet::writeText(FILE* file)
+{
+    fprintElementStart(file);
+
+    fprintfElement(file, "%d ", "indices", indices);
+
+    fprintElementEnd(file);
+}
+
+}

+ 28 - 0
gameplay-encoder/src/MeshSubSet.h

@@ -0,0 +1,28 @@
+#ifndef MESHSUBSET_H_

+#define MESHSUBSET_H_
+
+#include <vector>
+
+#include "Common.h"
+#include "Object.h"
+#include "Vertex.h"
+
+class MeshSubSet : public Object
+{
+public:
+    MeshSubSet(void);
+    virtual ~MeshSubSet(void);
+
+    virtual byte getTypeId(void);
+    virtual const char* getElementName(void);
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    std::vector<Vertex*> vertices;
+    std::vector<int> indices;
+    //Set<Vertex*, int> vertexLookupTable;
+    std::map<Vertex, int> vertexLookupTable;
+};
+
+

+#endif


+ 77 - 0
gameplay-encoder/src/Model.cpp

@@ -0,0 +1,77 @@
+#include "Model.h"
+
+namespace gameplay
+{
+
+Model::Model(void) :
+    ref(NULL),
+    meshSkin(NULL)
+{
+}
+
+Model::~Model(void)
+{
+}
+
+unsigned int Model::getTypeId(void) const
+{
+    return MODEL_ID;
+}
+const char* Model::getElementName(void) const
+{
+    return "Model";
+}
+void Model::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+
+    // xref:Mesh
+    if (ref != NULL)
+        ref->writeBinaryXref(file);
+    else
+        write((unsigned int)0, file);
+    // meshSkin
+    // Write one unsigned char to indicate if this model has a skin
+    if (meshSkin != NULL)
+    {
+        write((bool)true, file); // has a skin
+        meshSkin->writeBinary(file);
+    }
+    else
+    {
+        write((bool)false, file); // doesn't have a skin
+    }
+    // materials[]
+    writeBinaryObjects(materials, file);
+
+}
+void Model::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    if (ref != NULL)
+    {
+        fprintfElement(file, "ref", ref->getId());
+    }
+    if (meshSkin != NULL)
+    {
+        meshSkin->writeText(file);
+    }
+    fprintElementEnd(file);
+}
+
+MeshSkin* Model::getSkin()
+{
+    return meshSkin;
+}
+
+void Model::setMesh(Mesh* mesh)
+{
+    ref = mesh;
+}
+
+void Model::setSkin(MeshSkin* skin)
+{
+    meshSkin = skin;
+}
+
+}

+ 44 - 0
gameplay-encoder/src/Model.h

@@ -0,0 +1,44 @@
+#ifndef MODEL_H_
+#define MODEL_H_
+
+#include <list>
+
+#include "Object.h"
+#include "Mesh.h"
+#include "MeshSkin.h"
+#include "Material.h"
+
+namespace gameplay
+{
+
+class Model : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Model(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Model(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    void setMesh(Mesh* mesh);
+    MeshSkin* getSkin();
+    void setSkin(MeshSkin* skin);
+private:
+    Mesh* ref;
+    MeshSkin* meshSkin;
+    std::list<Material*> materials;
+};
+
+}
+#endif
+

+ 303 - 0
gameplay-encoder/src/Node.cpp

@@ -0,0 +1,303 @@
+#include "Node.h"
+
+#define NODE 1
+#define JOINT 2
+
+namespace gameplay
+{
+
+Node::Node(void) :
+    _childCount(0),
+    _nextSibling(NULL), _previousSibling(NULL), 
+    _firstChild(NULL), _lastChild(NULL), _parent(NULL),
+    camera(NULL), light(NULL), model(NULL), joint(false)
+{
+    setIdentityMatrix(transform);
+}
+
+Node::~Node(void)
+{
+}
+
+unsigned int Node::getTypeId(void) const
+{
+    return NODE_ID;
+}
+
+const char* Node::getElementName(void) const
+{
+    return "Node";
+}
+
+void Node::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+
+    // node type
+    unsigned int type = joint ? JOINT : NODE;
+    write(type, file);
+
+    write(transform, 16, file);
+    // children
+    write(getChildCount(), file); // write number of children
+    for (Node* node = getFirstChild(); node != NULL; node = node->getNextSibling())
+    {
+        node->writeBinary(file);
+    }
+
+    // camera
+    if (camera != NULL)
+    {
+        camera->writeBinary(file);
+    }
+    else
+    {
+        write((unsigned char)0, file);
+    }
+    // light
+    if (light != NULL && !light->isAmbient())
+    {
+        light->writeBinary(file);
+    }
+    else
+    {
+        write((unsigned char)0, file);
+    }
+    // mesh
+    if (model != NULL)
+    {
+        model->writeBinary(file);
+    }
+    else
+    {
+        writeZero(file);
+    }
+}
+void Node::writeText(FILE* file)
+{
+    if (isJoint())
+    {
+        fprintf(file, "<%s id=\"%s\" type=\"%s\">\n", getElementName(), id.c_str(), "JOINT");
+    }
+    else
+    {
+        fprintElementStart(file);
+    }
+    fprintf(file, "<transform>");
+    fprintfMatrix4f(file, transform);
+    fprintf(file, "</transform>\n");
+
+    // children
+    for (Node* node = getFirstChild(); node != NULL; node = node->getNextSibling())
+    {
+        node->writeText(file);
+    }
+    // camera
+    if (camera != NULL)
+    {
+        camera->writeText(file);
+    }
+    // light
+    if (light != NULL && !light->isAmbient())
+    {
+        light->writeText(file);
+    }
+    // mesh
+    if (model != NULL)
+    {
+        model->writeText(file);
+    }
+    fprintElementEnd(file);
+}
+
+void Node::addChild(Node* child)
+{
+    // If this child is already parented, remove it from its parent
+    if (child->_parent)
+    {
+        child->_parent->removeChild(child);
+    }
+
+    if (_firstChild == NULL)
+    {
+        // No children yet
+        _firstChild = child;
+        child->_previousSibling = NULL;
+    }
+    else
+    {
+        // We already have children, so append to them
+        child->_previousSibling = _lastChild;
+        _lastChild->_nextSibling = child;
+    }
+
+    child->_parent = this;
+    child->_nextSibling = NULL;
+    this->_lastChild = child;
+
+    ++_childCount;
+}
+
+
+void Node::removeChild(Node* child)
+{
+    // The child must have already been our child to remove it
+    if (child->_parent != this)
+    {
+        return;
+    }
+
+    // Remove the child by un-linking it from our child list
+    if (child->_nextSibling)
+    {
+        child->_nextSibling->_previousSibling = child->_previousSibling;
+    }
+    if (child->_previousSibling)
+    {
+        child->_previousSibling->_nextSibling = child->_nextSibling;
+    }
+
+    // Was this child our first or last child?
+    if (child == _firstChild)
+        _firstChild = child->_nextSibling;
+    if (child == _lastChild)
+        _lastChild = child->_previousSibling;
+
+    // Remove parent and sibling info from the child, now that it is no longer parented
+    child->_parent = NULL;
+    child->_nextSibling = NULL;
+    child->_previousSibling = NULL;
+
+    --_childCount;
+}
+
+void Node::removeChildren()
+{
+    Node* child;
+    while ((child = _firstChild) != NULL)
+    {
+        removeChild(child);
+    }
+}
+
+bool Node::hasChildren() const
+{
+    return (_firstChild != NULL);
+}
+
+unsigned int Node::getChildCount() const
+{
+    return _childCount;
+}
+
+Node* Node::getNextSibling() const
+{
+    return _nextSibling;
+}
+
+Node* Node::getPreviousSibling() const
+{
+    return _previousSibling;
+}
+
+Node* Node::getFirstChild() const
+{
+    return _firstChild;
+}
+
+Node* Node::getLastChild() const
+{
+    return _lastChild;
+}
+
+Node* Node::getParent() const
+{
+    return _parent;
+}
+
+void Node::setCameraInstance(CameraInstance* cameraInstance)
+{
+    camera = cameraInstance;
+}
+void Node::setLightInstance(LightInstance* lightInstance)
+{
+    light = lightInstance;
+}
+void Node::setModel(Model* model)
+{
+    this->model = model;
+}
+
+void Node::setTransformMatrix(float matrix[])
+{
+    for (int i = 0; i < 16; i++)
+    {
+        transform[i] = matrix[i];
+    }
+}
+
+void Node::setIsJoint(bool value)
+{
+    joint = value;
+}
+
+bool Node::isJoint()
+{
+    return joint;
+}
+
+Camera* Node::getCamera() const
+{
+    if (camera)
+    {
+        return camera->getCamera();
+    }
+    return NULL;
+}
+
+Light* Node::getLight() const
+{
+    if (light)
+    {
+        return light->getLight();
+    }
+    return NULL;
+}
+
+Model* Node::getModel() const
+{
+    if (model)
+    {
+        return model;
+    }
+    return NULL;
+}
+
+Node* Node::getFirstCameraNode() const
+{
+    if (hasCamera())
+    {
+        return (Node*)this;
+    }
+    for (Node* node = getFirstChild(); node != NULL; node = node->getNextSibling())
+    {
+        Node* n = node->getFirstCameraNode();
+        if (n)
+        {
+            return n;
+        }
+    }
+    return NULL;
+}
+
+bool Node::hasCamera() const
+{
+    return camera != NULL;
+}
+
+bool Node::hasLight() const
+{
+    return light != NULL;
+}
+
+} 

+ 178 - 0
gameplay-encoder/src/Node.h

@@ -0,0 +1,178 @@
+#ifndef NODE_H_
+#define NODE_H_
+
+#include <list>
+
+#include "Object.h"
+#include "CameraInstance.h"
+#include "LightInstance.h"
+#include "Model.h"
+
+namespace gameplay
+{
+
+class Node : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Node(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Node(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    /**
+     * Adds a child node.
+     *
+     * If the ownChild is true. The child is deleted when this
+     * node is deleted.
+     *
+     * @param child The child to add.
+     */
+    void addChild(Node* child);
+
+    /**
+     * Removes a child node.
+     *
+     * If no longer referenced it destroys the node.
+     * To avoid destroy on changing hierarchy, ensure you add first before removing.
+     *
+     * @param child The child to remove.
+     */
+    void removeChild(Node* child);
+
+    /**
+     * Removes all the child node.
+     */
+    void removeChildren();
+
+    /**
+     * Determines if this node has child nodes.
+     *
+     * @return true if it has 1 or more children; false if otherwise.
+     */
+    bool hasChildren() const;
+
+    /**
+     * Get the number of children for this node.
+     *
+     * @return The number of child nodes for this node.
+     */
+    unsigned int getChildCount() const;
+
+    /**
+     * Gets the next sibling node.
+     *
+     * @return The next sibling node.
+     */
+    Node* getNextSibling() const;
+
+    /**
+     * Gets the previous sibling node.
+     *
+     * @return The previous sibling node.
+     */
+    Node* getPreviousSibling() const;
+
+    /**
+     * Gets the first child node.
+     *
+     * @return The first child node.
+     */
+    Node* getFirstChild() const;
+
+    /**
+     * Gets the last child node.
+     *
+     * @return The last child node.
+     */
+    Node* getLastChild() const;
+
+    /**
+     * Gets the parent node.
+     *
+     * @return The parent node.
+     */
+    Node* getParent() const;
+
+    /**
+     * Returns the Camera for this node.
+     * 
+     * @return The camera for this node or NULL if no camera is set.
+     */
+    Camera* getCamera() const;
+
+    /**
+     * Returns the Light for this node.
+     * 
+     * @return The light for this node or NULL if no light is set.
+     */
+    Light* getLight() const;
+
+    /**
+     * Returns the Model of this node.
+     * 
+     * @return The model for this node or NULL if no model is set.
+     */
+    Model* getModel() const;
+
+    /**
+     * Sets the transform for this node.
+     */
+    void setTransformMatrix(float matrix[]);
+
+    void setCameraInstance(CameraInstance* cameraInstance);
+    void setLightInstance(LightInstance* lightInstance);
+    void setModel(Model* model);
+
+    /**
+     * Sets if this node is a joint node.
+     */
+    void setIsJoint(bool value);
+    /**
+     * Returns true if this is a joint node.
+     */
+    bool isJoint();
+
+    Node* getFirstCameraNode() const;
+
+    /**
+     * Returns true if this node has a camera.
+     */
+    bool hasCamera() const;
+
+    /**
+     * Returns true if this node has a light.
+     */
+    bool hasLight() const;
+    
+private:
+    float transform[16];
+
+    int _childCount;
+    Node* _nextSibling;
+    Node* _previousSibling;
+    Node* _firstChild;
+    Node* _lastChild;
+    Node* _parent;
+
+    CameraInstance* camera;
+    LightInstance* light;
+    Model* model;
+
+    bool joint;
+};
+
+
+}
+#endif
+

+ 49 - 0
gameplay-encoder/src/NodeInstance.cpp

@@ -0,0 +1,49 @@
+#include "NodeInstance.h"
+#include "Node.h"
+
+namespace gameplay
+{
+
+NodeInstance::NodeInstance(void) : ref(NULL)
+{
+}
+
+NodeInstance::~NodeInstance(void)
+{
+}
+
+unsigned int NodeInstance::getTypeId(void) const
+{
+    return NODEINSTANCE_ID;
+}
+
+const char* NodeInstance::getElementName(void) const
+{
+    return "NodeInstance";
+}
+
+void NodeInstance::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    if (ref != NULL)
+        ref->writeBinaryXref(file);
+    else
+        write((unsigned int)0, file);
+}
+
+void NodeInstance::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    if (ref != NULL)
+    {
+        fprintfElement(file, "ref", ref->getId());
+    }
+    fprintElementEnd(file);
+}
+
+void NodeInstance::setNode(Node* node)
+{
+    ref = node;
+}
+
+}

+ 28 - 0
gameplay-encoder/src/NodeInstance.h

@@ -0,0 +1,28 @@
+#ifndef NODEINSTANCE_H_
+#define NODEINSTANCE_H_
+
+#include "Object.h"
+
+namespace gameplay
+{
+
+class Node;
+
+class NodeInstance : public Object
+{
+public:
+    NodeInstance(void);
+    virtual ~NodeInstance(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    void setNode(Node* node);
+private:
+    Node* ref;
+};
+
+}
+#endif

+ 71 - 0
gameplay-encoder/src/Object.cpp

@@ -0,0 +1,71 @@
+#include "Object.h"
+
+namespace gameplay
+{
+
+Object::Object(void) : fposition(0)
+{
+}
+
+Object::~Object(void)
+{
+}
+
+unsigned int Object::getTypeId(void) const
+{
+    return 0;
+}
+
+void Object::writeBinary(FILE* file)
+{
+    saveFilePosition(file);
+}
+
+const std::string& Object::getId() const
+{
+    return id;
+}
+
+void Object::setId(const char* idStr)
+{
+    if (idStr)
+        id = idStr;
+}
+
+void Object::setId(const std::string& newId)
+{
+    if (newId.length() > 0)
+        id = newId;
+}
+
+void Object::fprintElementStart(FILE* file)
+{
+    if (id.length() > 0)
+        fprintf(file, "<%s id=\"%s\">\n", getElementName(), id.c_str());
+    else
+        fprintf(file, "<%s>\n", getElementName());
+}
+
+void Object::fprintElementEnd(FILE* file)
+{
+    fprintf(file, "</%s>\n", getElementName());
+}
+
+unsigned int Object::getFilePosition()
+{
+    return (unsigned int)fposition;
+}
+
+void Object::saveFilePosition(FILE* file)
+{
+    fposition = ftell(file);
+}
+
+void Object::writeBinaryXref(FILE* file)
+{
+    std::string xref("#");
+    xref.append(getId());
+    write(xref, file);
+}
+
+}

+ 148 - 0
gameplay-encoder/src/Object.h

@@ -0,0 +1,148 @@
+#ifndef OBJ_H_
+#define OBJ_H_
+
+#include <iostream>
+#include <string>
+#include <list>
+#include <vector>
+
+#include "Base.h"
+#include "FileIO.h"
+
+namespace gameplay
+{
+
+class Object
+{
+public:
+
+    // TypeID's
+    enum TypeID
+    {
+        SCENE_ID = 1,
+        NODE_ID = 2,
+        ANIMATION_ID = 3,
+        ANIMATIONCHANNEL_ID = 4,
+        NODEINSTANCE_ID = 8,
+        CAMERAINSTANCE_ID = 9,
+        LIGHTINSTANCE_ID = 10,
+        MODEL_ID = 11,
+        MATERIAL_ID = 16,
+        EFFECT_ID = 17,
+        CAMERA_ID = 32,
+        LIGHT_ID = 33,
+        MESH_ID = 34,
+        MESHPART_ID = 35,
+        MESHSKIN_ID = 36,
+        FONT_ID = 128,
+    };
+
+
+    /**
+     * Constructor.
+     */
+    Object(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Object(void);
+    /**
+     * Returns the Object TypeID.
+     */
+    virtual unsigned int getTypeId(void) const;
+    /**
+     * Returns the string element name of the object.
+     * Used for printing the gameplayfile as text.
+     */
+    virtual const char* getElementName(void) const = 0;
+    /**
+     * Writes this object to the file stream as binary.
+     */
+    virtual void writeBinary(FILE* file);
+    /**
+     * Writes this object to the file stream as text.
+     */
+    virtual void writeText(FILE* file) = 0;
+
+    /**
+     * Returns this objects id string.
+     */
+    const std::string& getId() const;
+    /**
+     * Sets this object's id string.
+     */
+    void setId(const char* id);
+    /**
+     * Sets this object's id string.
+     */
+    void setId(const std::string& id);
+    /**
+     * Prints an XML start element with the name of this object to the text file stream.
+     * Also prints the id as an attribute if the id length is greater than zero.
+     */
+    void fprintElementStart(FILE* file);
+    /**
+     * Prints an XML end element with the name of this object to the text file stream.
+     */
+    void fprintElementEnd(FILE* file);
+    /**
+     * Writes the xref of this object to the binary file stream.
+     */
+    void writeBinaryXref(FILE* file);
+
+    /**
+     * Returns the file position that this object was written to.
+     * An offset of zero means this object has not been written yet.
+     */
+    unsigned int getFilePosition();
+
+    /**
+     * Writes out a list of objects to a binary file stream.
+     */
+    template <class T>
+    static void writeBinaryObjects(std::list<T> list, FILE* file)
+    {
+        // First write the size of the list
+        write(list.size(), file);
+        // Then write each element
+        std::list<T>::const_iterator i;
+        for (i = list.begin(); i != list.end(); i++)
+        {
+            (*i)->writeBinary(file);
+        }
+    }
+
+    /**
+     * Writes out a vector of objects to a binary file stream.
+     */
+    template <class T>
+    static void writeBinaryObjects(std::vector<T> vector, FILE* file)
+    {
+        // First write the size of the vector
+        write(vector.size(), file);
+        // Then write each element
+        std::vector<T>::const_iterator i;
+        for (i = vector.begin(); i != vector.end(); i++)
+        {
+            (*i)->writeBinary(file);
+        }
+    }
+
+private:
+    /**
+     * Saves where this object was written to in the binary file.
+     */
+    void saveFilePosition(FILE* file);
+
+protected:
+
+    std::string id;
+
+private:
+    long fposition;
+};
+
+}
+
+#endif

+ 360 - 0
gameplay-encoder/src/Quaternion.cpp

@@ -0,0 +1,360 @@
+/*
+ * Quaternion.cpp
+ */
+
+#include "Base.h"
+#include "Quaternion.h"
+
+namespace gameplay
+{
+
+Quaternion::Quaternion()
+    : x(0.0f), y(0.0f), z(0.0f), w(1.0f)
+{
+}
+
+Quaternion::Quaternion(float x, float y, float z, float w)
+{
+    set(x, y, z, w);
+}
+
+Quaternion::Quaternion(float* array)
+{
+    set(array);
+}
+
+
+Quaternion::Quaternion(const Quaternion& copy)
+{
+    set(copy);
+}
+
+Quaternion::~Quaternion()
+{
+}
+
+const Quaternion& Quaternion::identity()
+{
+    static Quaternion* value = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
+    return *value;
+}
+
+const Quaternion& Quaternion::zero()
+{
+    static Quaternion* value = new Quaternion(0.0f, 0.0f, 0.0f, 0.0f);
+    return *value;
+}
+
+bool Quaternion::isIdentity() const
+{
+    return x == 0.0f && y == 0.0f && z == 0.0f && w == 1.0f;
+}
+
+bool Quaternion::isZero() const
+{
+    return x == 0.0f && y == 0.0f && z == 0.0f && z == 0.0f;
+}
+
+void Quaternion::createFromRotationMatrix(const Matrix& m, Quaternion* dst)
+{
+    assert(dst);
+
+    m.decompose(NULL, dst, NULL);
+}
+
+void Quaternion::createFromAxisAngle(const Vector3& axis, float angle, Quaternion* dst)
+{
+    assert(dst);
+
+    float halfAngle = angle * 0.5f;
+    float sinHalfAngle = sinf(halfAngle);
+
+    Vector3 normal(axis);
+    normal.normalize();
+    dst->x = normal.x * sinHalfAngle;
+    dst->y = normal.y * sinHalfAngle;
+    dst->z = normal.z * sinHalfAngle;
+    dst->w = cosf(halfAngle);
+}
+
+void Quaternion::conjugate()
+{
+    conjugate(this);
+}
+
+void Quaternion::conjugate(Quaternion* dst) const
+{
+    dst->x = -x;
+    dst->y = -y;
+    dst->z = -z;
+    dst->w =  w;
+}
+
+bool Quaternion::inverse()
+{
+    return inverse(this);
+}
+
+bool Quaternion::inverse(Quaternion* dst) const
+{
+    float n = x * x + y * y + z * z + w * w;
+    if (n == 1.0f)
+    {
+        dst->x = -x;
+        dst->y = -y;
+        dst->z = -z;
+        dst->w = w;
+
+        return true;
+    }
+
+    // too close to zero
+    if (n < 0.000001f)
+        return false;
+
+    n = 1.0f / n;
+    dst->x = -x * n;
+    dst->y = -y * n;
+    dst->z = -z * n;
+    dst->w = w * n;
+
+    return true;
+}
+
+void Quaternion::multiply(const Quaternion& q)
+{
+    multiply(*this, q, this);
+}
+
+void Quaternion::multiply(const Quaternion& q1, const Quaternion& q2, Quaternion* dst)
+{
+    float x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
+    float y = q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x;
+    float z = q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w;
+    float w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
+
+    dst->x = x;
+    dst->y = y;
+    dst->z = z;
+    dst->w = w;
+}
+
+void Quaternion::normalize()
+{
+    normalize(this);
+}
+
+void Quaternion::normalize(Quaternion* dst) const
+{
+    assert(dst);
+
+    if (this != dst)
+    {
+        dst->x = x;
+        dst->y = y;
+        dst->z = z;
+        dst->w = w;
+    }
+
+    float n = x * x + y * y + z * z + w * w;
+
+    // already normalized
+    if (n == 1.0f)
+        return;
+
+    n = sqrtf(n);
+    // too close to zero
+    if (n < 0.000001f)
+        return;
+
+    n = 1.0f / n;
+    dst->x *= n;
+    dst->y *= n;
+    dst->z *= n;
+    dst->w *= n;
+}
+
+void Quaternion::set(float x, float y, float z, float w)
+{
+    this->x = x;
+    this->y = y;
+    this->z = z;
+    this->w = w;
+}
+
+void Quaternion::set(float* array)
+{
+    assert(array);
+
+    x = array[0];
+    y = array[1];
+    z = array[2];
+    w = array[3];
+}
+
+void Quaternion::set(const Quaternion& q)
+{
+    this->x = q.x;
+    this->y = q.y;
+    this->z = q.z;
+    this->w = q.w;
+}
+
+void Quaternion::setIdentity()
+{
+    x = 0.0f;
+    y = 0.0f;
+    z = 0.0f;
+    w = 1.0f;
+}
+
+float Quaternion::toAxisAngle(Vector3* axis) const
+{
+    assert(axis);
+
+    Quaternion q(x, y, z, w);
+    q.normalize();
+    axis->x = q.x;
+    axis->y = q.y;
+    axis->z = q.z;
+    axis->normalize();
+
+    return (2.0f * acos(q.w));
+}
+
+void Quaternion::lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
+{
+    assert(dst);
+    assert(!(t < 0.0f || t > 1.0f));
+
+    float t1 = 1.0f - t;
+
+    dst->x = t1 * q1.x + t * q2.x;
+    dst->y = t1 * q1.y + t * q2.y;
+    dst->z = t1 * q1.z + t * q2.z;
+    dst->w = t1 * q1.w + t * q2.w;
+}
+
+void Quaternion::slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
+{
+    // Fast slerp implementation by kwhatmough:
+    // It contains no division operations, no trig, no inverse trig
+    // and no sqrt. Not only does this code tolerate small constraint
+    // errors in the input quaternions, it actually corrects for them.
+    assert(dst);
+    assert(!(t < 0.0f || t > 1.0f));
+
+    float halfY, alpha, beta;
+    float u, f1, f2a, f2b;
+    float ratio1, ratio2;
+    float halfSecHalfTheta, versHalfTheta;
+    float sqNotU, sqU;
+
+    float cosTheta = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+
+    // As usual in all slerp implementations, we fold theta.
+    alpha = cosTheta >= 0 ? 1.0f : -1.0f;
+    halfY = 1.0f + alpha * cosTheta;
+
+    // Here we bisect the interval, so we need to fold t as well.
+    f2b = t - 0.5f;
+    u = f2b >= 0 ? f2b : -f2b;
+    f2a = u - f2b;
+    f2b += u;
+    u += u;
+    f1 = 1.0f - u;
+
+    // One iteration of Newton to get 1-cos(theta / 2) to good accuracy.
+    halfSecHalfTheta = 1.09f - (0.476537f - 0.0903321f * halfY) * halfY;
+    halfSecHalfTheta *= 1.5f - halfY * halfSecHalfTheta * halfSecHalfTheta;
+    versHalfTheta = 1.0f - halfY * halfSecHalfTheta;
+
+    // Evaluate series expansions of the coefficients.
+    sqNotU = f1 * f1;
+    ratio2 = 0.0000440917108f * versHalfTheta;
+    ratio1 = -0.00158730159f + (sqNotU - 16.0f) * ratio2;
+    ratio1 = 0.0333333333f + ratio1 * (sqNotU - 9.0f) * versHalfTheta;
+    ratio1 = -0.333333333f + ratio1 * (sqNotU - 4.0f) * versHalfTheta;
+    ratio1 = 1.0f + ratio1 * (sqNotU - 1.0f) * versHalfTheta;
+
+    sqU = u * u;
+    ratio2 = -0.00158730159f + (sqU - 16.0f) * ratio2;
+    ratio2 = 0.0333333333f + ratio2 * (sqU - 9.0f) * versHalfTheta;
+    ratio2 = -0.333333333f + ratio2 * (sqU - 4.0f) * versHalfTheta;
+    ratio2 = 1.0f + ratio2 * (sqU - 1.0f) * versHalfTheta;
+
+    // Perform the bisection and resolve the folding done earlier.
+    f1 *= ratio1 * halfSecHalfTheta;
+    f2a *= ratio2;
+    f2b *= ratio2;
+    alpha *= f1 + f2a;
+    beta = f1 + f2b;
+
+    // Apply final coefficients to a and b as usual.
+    float w = alpha * q1.w + beta * q2.w;
+    float x = alpha * q1.x + beta * q2.x;
+    float y = alpha * q1.y + beta * q2.y;
+    float z = alpha * q1.z + beta * q2.z;
+
+    // This final adjustment to the quaternion's length corrects for
+    // any small constraint error in the inputs q1 and q2. But as you
+    // can see, it comes at the cost of 9 additional multiplication
+    // operations. If this error-correcting feature is not required,
+    // the following code may be removed.
+    f1 = 1.5f - 0.5f * (w * w + x * x + y * y + z * z);
+    dst->w = w * f1;
+    dst->x = x * f1;
+    dst->y = y * f1;
+    dst->z = z * f1;
+}
+
+void Quaternion::squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst)
+{
+    assert(dst);
+    assert(!(t < 0.0f || t > 1.0f));
+
+    Quaternion dstQ(0.0f, 0.0f, 0.0f, 1.0f);
+    Quaternion dstS(0.0f, 0.0f, 0.0f, 1.0f);
+
+    slerpForSquad(q1, q2, t, &dstQ);
+    slerpForSquad(s1, s2, t, &dstS);
+    slerpForSquad(dstQ, dstS, 2.0f * t * (1.0f - t), dst);
+}
+
+void Quaternion::slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst)
+{
+    // cos(omega) = q1 * q2;
+    // slerp(q1, q2, t) = (q1*sin((1-t)*omega) + q2*sin(t*omega))/sin(omega);
+    // q1 = +- q2, slerp(q1,q2,t) = q1.
+    // This is a straight-foward implementation of the formula of slerp. It does not do any sign switching.
+    float c = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
+
+    if (fabs(c) >= 1.0f)
+    {
+        dst->x = q1.x;
+        dst->y = q1.y;
+        dst->z = q1.z;
+        dst->w = q1.w;
+        return;
+    }
+
+    float omega = acos(c);
+    float s = sqrt(1.0f - c * c);
+    if (fabs(s) <= 0.00001f)
+    {
+        dst->x = q1.x;
+        dst->y = q1.y;
+        dst->z = q1.z;
+        dst->w = q1.w;
+        return;
+    }
+
+    float r1 = sin((1 - t) * omega) / s;
+    float r2 = sin(t * omega) / s;
+    dst->x = (q1.x * r1 + q2.x * r2);
+    dst->y = (q1.y * r1 + q2.y * r2);
+    dst->z = (q1.z * r1 + q2.z * r2);
+    dst->w = (q1.w * r1 + q2.w * r2);
+}
+
+}

+ 309 - 0
gameplay-encoder/src/Quaternion.h

@@ -0,0 +1,309 @@
+/*
+ * Quaternion.h
+ */
+
+#ifndef QUATERNION_H_
+#define QUATERNION_H_
+
+#include "Vector3.h"
+#include "Matrix.h"
+
+namespace gameplay
+{
+
+class Matrix;
+
+/**
+ * Defines a 4-element quaternion that represents the orientation of an object in space.
+ *
+ * Quaternions are typically used as a replacement for euler angles and rotation matrices as a way to achieve smooth interpolation and avoid gimbal lock.
+ *
+ * Note that this quaternion class does not automatically keep the quaternion normalized. Therefore, care must be taken to normalize the quaternion when neccessary, by calling the normalize method.
+ * The package provides three methods for doing quaternion interpolation: lerp, slerp, and squad.
+ *
+ * lerp (linear interpolation): the interpolation curve gives a straight line in quaternion space. It is simple and fast to compute. The only problem is that it does not provide constant angular velocity. Note that a constant velocity is not necessarily a requirement for a curve;
+ * slerp (spherical linear interpolation): the interpolation curve forms a great arc on the quaternion unit sphere. Slerp provides constant angular velocity;
+ * squad (spherical spline interpolation): interpolating between a series of rotations using slerp leads to the following problems:
+ * the curve is not smooth at the control points;
+ * the angular velocity is not constant;
+ * the angular velocity is not continuous at the control points.
+ *
+ * Since squad is continuously differentiable, it remedies the first and third problems mentioned above.
+ * The slerp method provided here is intended for interpolation of principal rotations. It treats +q and -q as the same principal rotation and is at liberty to use the negative of either input. The resulting path is always the shorter arc.
+ *
+ * The lerp method provided here interpolates strictly in quaternion space. Note that the resulting path may pass through the origin if interpolating between a quaternion and its exact negative.
+ *
+ * As an example, consider the following quaternions
+ *
+ * q1 = (0.6, 0.8, 0.0, 0.0),
+ * q2 = (0.0, 0.6, 0.8, 0.0),
+ * q3 = (0.6, 0.0, 0.8, 0.0), and
+ * q4 = (-0.8, 0.0, -0.6, 0.0).
+ * For the point p = (1.0, 1.0, 1.0), the following figures show the trajectories of p using lerp, slerp, and squad.
+ *
+ * @image "http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/math/doc-files/LERP.PNG"
+ * @image "http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/math/doc-files/SLERP.PNG"
+ * @image "http://www.blackberry.com/developers/docs/7.0.0api/net/rim/device/api/math/doc-files/SQUAD.PNG"
+ */
+class Quaternion
+{
+public:
+
+    /** The x-value of the quaternion's vector component. */
+    float x;
+    /** The y-value of the quaternion's vector component. */
+    float y;
+    /** The z-value of the quaternion's vector component. */
+    float z;
+    /** The scalar component of the quaternion. */
+    float w;
+
+
+    /**
+     * Constructs a quaternion initialized to (0, 0, 0, 1).
+     */
+    Quaternion();
+
+    /**
+     * Constructs a quaternion initialized to (0, 0, 0, 1).
+     *
+     * @param x The x component of the quaternion.
+     * @param y The y component of the quaternion.
+     * @param z The z component of the quaternion.
+     * @param w The w component of the quaternion.
+     */
+    Quaternion(float x, float y, float z, float w);
+
+    /**
+     * Constructs a new quaternion from the values in the specified array.
+     *
+     * @param array
+     */
+    Quaternion(float* array);
+
+    /**
+     * Constructs a new quaternion that is a copy of the specified one.
+     *
+     * @param copy The quaternion to copy
+     */
+    Quaternion(const Quaternion& copy);
+
+    /**
+     * Destructor.
+     */
+    ~Quaternion();
+
+    /**
+     * Returns the identity quaternion.
+     *
+     * @return The quaternion.
+     */
+    static const Quaternion& identity();
+
+    /**
+     * Returns the quaternion with all zeros.
+     *
+     * @return The quaternion.
+     */
+    static const Quaternion& zero();
+
+    /**
+     * Determines if this quaterion is equal to the identity quaternion.
+     *
+     * @return true if the identity, false otherwise.
+     */
+    bool isIdentity() const;
+
+    /**
+     * Determines if this quaterion is all zeros.
+     *
+     * @return true if zeros, false otherwise.
+     */
+    bool isZero() const;
+
+    /**
+     * Create a quaternion equal to the rotational part of the specified matrix
+     * and stores the result in dst.
+     *
+     * @param m The matrix.
+     * @param dst A quaternion to store the conjugate in.
+     */
+    static void createFromRotationMatrix(const Matrix& m, Quaternion* dst);
+
+    /**
+     * Creates this quaternion equal to the rotation from the specified axis and angle
+     * and store the result in dst.
+     *
+     * @param axis A vector describing the axis of rotation.
+     * @param angle The angle of rotation, in radians.
+     * @param dst A quaternion to store the conjugate in.
+     */
+    static void createFromAxisAngle(const Vector3& axis, float angle, Quaternion* dst);
+
+    /**
+     * Sets this quaternion to the conjugate of itself.
+     */
+    void conjugate();
+
+    /**
+     * Gets the conjugate of this quaternion in dst.
+     *
+     * @param dst A quaternion to store the conjugate in.
+     */
+    void conjugate(Quaternion* dst) const;
+
+    /**
+     * Sets this quaternion to the inverse of itself.
+     *
+     * Note that the inverse of a quaternion is equal to its conjugate
+     * when the quaternion is unit-length. For this reason, it is more
+     * efficient to use the conjugate method directly when you know your
+     * quaternion is already unit-length.
+     *
+     * @return true if the inverse can be computed, false otherwise.
+     */
+    bool inverse();
+
+    /**
+     * Gets the inverse of this quaternion in dst.
+     *
+     * Note that the inverse of a quaternion is equal to its conjugate
+     * when the quaternion is unit-length. For this reason, it is more
+     * efficient to use the conjugate method directly when you know your
+     * quaternion is already unit-length.
+     *
+     * @param dst A quaternion to store the inverse in.
+     * @return true if the inverse can be computed, false otherwise.
+     */
+    bool inverse(Quaternion* dst) const;
+
+    /**
+     * Multiplies this quaternion by the specified one and stores the result in this quaternion.
+     *
+     * @param q The quaternion to multiply.
+     */
+    void multiply(const Quaternion& q);
+
+    /**
+     * Multiplies the specified quaternions and stores the result in dst.
+     *
+     * @param q1 The first quaternion.
+     * @param q2 The second quaternion.
+     * @param dst A quaternion to store the result in.
+     */
+    static void multiply(const Quaternion& q1, const Quaternion& q2, Quaternion* dst);
+
+    /**
+     * Normalizes this quaternion to have unit length.
+     *
+     * If the quaternion already has unit length or if the length
+     * of the quaternion is zero, this method does nothing.
+     */
+    void normalize();
+
+    /**
+     * Normalizes this quaternion and stores the result in dst.
+     *
+     * If the quaternion already has unit length or if the length
+     * of the quaternion is zero, this method simply copies
+     * this vector into dst.
+     *
+     * @param dst A quaternion to store the result in.
+     */
+    void normalize(Quaternion* dst) const;
+
+    /**
+     * Sets the elements of the quaternion to the specified values.
+     *
+     * @param x The new x-value.
+     * @param y The new y-value.
+     * @param z The new z-value.
+     * @param w The new w-value.
+     */
+    void set(float x, float y, float z, float w);
+
+    /**
+     * Sets the elements of the quaternion from the values in the specified array.
+     *
+     * @param array An array containing the elements of the quaternion in the order x, y, z, w.
+     */
+    void set(float* array);
+
+    /**
+     * Sets the elements of this quaternion to a copy of the specified quaternion.
+     *
+     * @param q The quaternion to copy.
+     */
+    void set(const Quaternion& q);
+
+    /**
+     * Sets this quaternion to be equal to the identity quaternion.
+     */
+    void setIdentity();
+
+    /**
+     * Converts this Quaternion4f to axis-angle notation. The axis is normalized.
+     *
+     * @param e The Vector3f which stores the axis.
+     * @return The angle (in radians).
+     */
+    float toAxisAngle(Vector3* e) const;
+
+    /**
+     * Interpolates between two quaternions using linear interpolation.
+     *
+     * The interpolation curve for linear interpolation between
+     * quaternions gives a straight line in quaternion space.
+     *
+     * @param q1 The first quaternion.
+     * @param q2 The second quaternion.
+     * @param t The interpolation coefficient.
+     * @param dst A quaternion to store the result in
+     */
+    static void lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
+
+    /**
+     * Interpolates between two quaternions using spherical linear interpolation.
+     *
+     * Spherical linear interpolation provides smooth transitions between different
+     * orientations and is often useful for animating models or cameras in 3D.
+     *
+     * Note: For accurate interpolation, the input quaternions must be at (or close to) unit length.
+     * This method does not automatically normalize the input quaternions, so it is up to the
+     * caller to ensure they call normalize beforehand, if necessary.
+     *
+     * @param q1 The first quaternion.
+     * @param q2 The second quaternion.
+     * @param t The interpolation coefficient.
+     * @param dst A quaternion to store the result in
+     */
+    static void slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
+
+    /**
+     * Interpolates over a series of quaternions using spherical spline interpolation.
+     *
+     * Spherical spline interpolation provides smooth transitions between different
+     * orientations and is often useful for animating models or cameras in 3D.
+     *
+     * Note: For accurate interpolation, the input quaternions must be unit.
+     * This method does not automatically normalize the input quaternions,
+     * so it is up to the caller to ensure they call normalize beforehand, if necessary.
+     *
+     * @param q1 The first quaternion.
+     * @param q2 The second quaternion.
+     * @param s1 The first control point
+     * @param s2 The second control point
+     * @param t The interpolation coefficient.
+     * @param dst A quaternion to store the result in
+     */
+    static void squad(const Quaternion& q1, const Quaternion& q2, const Quaternion& s1, const Quaternion& s2, float t, Quaternion* dst);
+
+
+private:
+
+    static void slerpForSquad(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
+
+};
+
+}
+#endif /* QUATERNION_H_ */

+ 86 - 0
gameplay-encoder/src/Reference.cpp

@@ -0,0 +1,86 @@
+#include "Reference.h"
+
+namespace gameplay
+{
+
+Reference::Reference(void) :
+    type(0),
+    offset(0),
+    ref(NULL)
+{
+}
+
+Reference::Reference(std::string _xref, Object* _ref) :
+    xref(_xref),
+    type(_ref->getTypeId()),
+    offset(0),
+    ref(_ref)
+{
+}
+
+Reference::~Reference(void)
+{
+}
+
+const char* Reference::getElementName(void) const
+{
+    return "Reference";
+}
+
+void Reference::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    write(xref, file);
+    write(type, file);
+    write(offset, file);
+}
+void Reference::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "xref", xref);
+    fprintfElement(file, "type", type);
+    fprintfElement(file, "offset", offset);
+    fprintElementEnd(file);
+}
+
+bool Reference::updateOffset(FILE* file)
+{
+    long newOffset = ref->getFilePosition();
+    return updateOffset(file, newOffset);
+}
+
+bool Reference::updateOffset(FILE* file, long newOffset)
+{
+    if (getFilePosition() > 0)
+    {
+        // save the current offset
+        long savedOffset = ftell(file);
+
+        // update the offset data for this
+        offset = newOffset;
+        // seek this Reference object in the file
+        fseek(file, getFilePosition(), SEEK_SET);
+
+        // skip over the object type
+        //fseek(file, sizeof(unsigned int), SEEK_CUR);
+        // skip over the id string
+        skipString(file);
+        // skip over the type
+        //skipUint(file);
+
+        // write over the old offset
+        write(offset, file);
+
+        // restore the offset
+        fseek(file, savedOffset, SEEK_SET);
+        return true;
+    }
+    return false;
+}
+
+Object* Reference::getObj()
+{
+    return ref;
+}
+
+}

+ 54 - 0
gameplay-encoder/src/Reference.h

@@ -0,0 +1,54 @@
+#ifndef REF_H_
+#define REF_H_
+
+#include "Object.h"
+
+namespace gameplay
+{
+
+class Reference : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Reference(void);
+    Reference(std::string _xref, Object* _ref);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Reference(void);
+
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    /**
+     * Updates the offset of this Reference object if it has already need written to file.
+     * @param file The file stream.
+     * @return True if the offset was updates, false otherwise.
+     */
+    bool updateOffset(FILE* file);
+
+    /**
+     * Updates the offset of this Reference object if it has already need written to file.
+     * @param file The file stream.
+     * @param newOffset The new file offset.
+     * @return True if the offset in the binary file was updated. False if this ref hasn't been written to file yet.
+     */
+    bool updateOffset(FILE* file, long newOffset);
+
+    Object* getObj();
+
+private:
+    std::string xref;
+    unsigned int type;
+    unsigned int offset;
+
+    Object* ref;
+};
+
+}
+#endif

+ 68 - 0
gameplay-encoder/src/ReferenceTable.cpp

@@ -0,0 +1,68 @@
+#include "ReferenceTable.h"
+
+namespace gameplay
+{
+
+ReferenceTable::ReferenceTable(void)
+{
+}
+
+ReferenceTable::~ReferenceTable(void)
+{
+}
+
+void ReferenceTable::add(std::string xref, Object* obj)
+{
+    table[xref] = Reference(xref, obj);
+}
+
+Object* ReferenceTable::get(const std::string& xref)
+{
+    std::map<std::string, Reference>::iterator it = table.find(xref);
+    if (it != table.end())
+    {
+        Reference ref = it->second;
+        return ref.getObj();
+    }
+    return NULL;
+}
+
+void ReferenceTable::writeBinary(FILE* file)
+{
+    write(table.size(), file);
+    for ( std::map<std::string, Reference>::iterator i=table.begin() ; i != table.end(); i++ )
+    {
+        i->second.writeBinary(file);
+    }
+}
+
+void ReferenceTable::writeText(FILE* file)
+{
+    fprintf(file, "<RefTable>\n");
+    for ( std::map<std::string, Reference>::iterator i=table.begin() ; i != table.end(); i++ )
+    {
+        i->second.writeText(file);
+    }
+    fprintf(file, "</RefTable>\n");
+}
+
+void ReferenceTable::updateOffsets(FILE* file)
+{
+    for (std::map<std::string, Reference>::iterator i = table.begin(); i != table.end(); i++)
+    {
+        Reference& ref = i->second;
+        ref.updateOffset(file);
+    }
+}
+
+std::map<std::string, Reference>::iterator ReferenceTable::begin()
+{
+    return table.begin();
+}
+
+std::map<std::string, Reference>::iterator ReferenceTable::end()
+{
+    return table.end();
+}
+
+}

+ 47 - 0
gameplay-encoder/src/ReferenceTable.h

@@ -0,0 +1,47 @@
+#ifndef REFTABLE_H_
+#define REFTABLE_H_
+
+#include <map>
+
+#include "FileIO.h"
+#include "Reference.h"
+#include "Object.h"
+
+namespace gameplay
+{
+
+/**
+ * Collection of unique Reference objects stored in a hashtable.
+ */
+class ReferenceTable
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    ReferenceTable(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~ReferenceTable(void);
+
+    void add(std::string xref, Object* obj);
+    Object* get(const std::string& xref);
+
+    void writeBinary(FILE* file);
+    void writeText(FILE* file);
+
+    void updateOffsets(FILE* file);
+
+    std::map<std::string, Reference>::iterator begin();
+    std::map<std::string, Reference>::iterator end();
+
+private:
+    std::map<std::string, Reference> table;
+};
+
+}
+#endif
+

+ 116 - 0
gameplay-encoder/src/Scene.cpp

@@ -0,0 +1,116 @@
+#include <algorithm>
+
+#include "Scene.h"
+
+namespace gameplay
+{
+
+Scene::Scene(void) : cameraNode(NULL)
+{
+    ambientColor[0] = 0.0f;
+    ambientColor[1] = 0.0f;
+    ambientColor[2] = 0.0f;
+}
+
+Scene::~Scene(void)
+{
+}
+
+unsigned int Scene::getTypeId(void) const
+{
+    return SCENE_ID;
+}
+
+const char* Scene::getElementName(void) const
+{
+    return "Scene";
+}
+
+void Scene::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    writeBinaryObjects(nodes, file);
+    if (cameraNode)
+    {
+        cameraNode->writeBinaryXref(file);
+    }
+    else
+    {
+        writeZero(file);
+    }
+    write(ambientColor, Light::COLOR_SIZE, file);
+}
+void Scene::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    for (std::list<Node*>::const_iterator i = nodes.begin(); i != nodes.end(); i++)
+    {
+        (*i)->writeText(file);
+    }
+    if (cameraNode)
+    {
+        fprintfElement(file, "activeCamera", cameraNode->getId());
+    }
+    fprintfElement(file, "ambientColor", ambientColor, Light::COLOR_SIZE);
+    fprintElementEnd(file);
+}
+
+void Scene::add(Node* node)
+{
+    nodes.push_back(node);
+}
+
+void Scene::setActiveCameraNode(Node* node)
+{
+    cameraNode = node;
+}
+
+Node* Scene::getFirstCameraNode() const
+{
+    for (std::list<Node*>::const_iterator i = nodes.begin(); i != nodes.end(); i++)
+    {
+        Node* n = (*i)->getFirstCameraNode();
+        if (n)
+        {
+            return n;
+        }
+    }
+    return NULL;
+}
+
+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++)
+    {
+        calcAmbientColor(*i, values);
+    }
+    
+    ambientColor[0] = std::min(values[0], 1.0f);
+    ambientColor[1] = std::min(values[1], 1.0f);
+    ambientColor[2] = std::min(values[2], 1.0f);
+}
+
+void Scene::calcAmbientColor(const Node* node, float* values) const
+{
+    if (!node)
+    {
+        return;
+    }
+    if (node->hasLight())
+    {
+        Light* light = node->getLight();
+        if (light->isAmbient())
+        {
+            values[0] += light->getRed();
+            values[1] += light->getGreen();
+            values[2] += light->getBlue();
+        }
+    }
+    for (Node* child = node->getFirstChild(); child != NULL; child = child->getNextSibling())
+    {
+        calcAmbientColor(child, values);
+    }
+}
+
+}

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

@@ -0,0 +1,70 @@
+#ifndef SCENE_H_
+#define SCENE_H_
+
+#include "Object.h"
+#include "Node.h"
+#include "FileIO.h"
+
+namespace gameplay
+{
+
+class Scene : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Scene(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Scene(void);
+
+    virtual unsigned int getTypeId(void) const;
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    /**
+     * Adds the given node as a child of this scene.
+     * 
+     * @param node The node to add.
+     */
+    void add(Node* node);
+
+    /**
+     * Sets the activate camera node. This node should contain a camera.
+     */
+    void setActiveCameraNode(Node* node);
+
+    /**
+     * Returns the first node the this scene that contains a camera.
+     */
+    Node* getFirstCameraNode() const;
+
+    /**
+     * Calculates the ambient color of this scene by combining all of the ambient lights
+     * that are in this scene.
+     */
+    void calcAmbientColor();
+
+private:
+
+    /**
+     * Recursively calculates the ambient color of the scene starting at the given node.
+     * The ambient light color is added to the givne float array.
+     * 
+     * @param node The node in this scene to traverse from.
+     * @param values Pointer to 3 floats that contains the calculated ambient color.
+     */
+    void calcAmbientColor(const Node* node, float* values) const;
+
+    std::list<Node*> nodes;
+    Node* cameraNode;
+    float ambientColor[Light::COLOR_SIZE];
+};
+
+}
+#endif

+ 72 - 0
gameplay-encoder/src/StringUtil.cpp

@@ -0,0 +1,72 @@
+#include "StringUtil.h"
+
+#include <string>
+
+namespace gameplay
+{
+
+inline char lowercase(char c)
+{
+    if (c >= 'A' && c <='Z')
+    {
+        c |= 0x20;
+    }
+    return c;
+}
+
+bool endsWith(const char* str, const char* suffix, bool ignoreCase)
+{
+    size_t length = strlen(str);
+    size_t suffixLength = strlen(suffix);
+
+    if (suffixLength > length)
+    {
+        return false;
+    }
+
+    size_t offset = length - suffixLength;
+
+    const char* p = str + offset;
+    while (*p != '\0')
+    {
+        if (ignoreCase)
+        {
+            if (lowercase(*p) != lowercase(*suffix))
+            {
+                return false;
+            }
+        }
+        else if (*p != *suffix)
+        {
+            return false;
+        }
+        
+        ++p;
+        ++suffix;
+    }
+    return true;
+}
+
+bool equals(const std::string& a, const char* b)
+{
+    return (a.compare(b) == 0);
+}
+
+bool equalsIgnoreCase(const std::string& a, const char* b)
+{
+    size_t bLength = strlen(b);
+    if (a.size() != bLength)
+    {
+        return false;
+    }
+    for (size_t i = 0; i < bLength; i++)
+    {
+        if (lowercase(a[i]) != lowercase(b[i]))
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+}

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

@@ -0,0 +1,22 @@
+#ifndef STRINGUTIL_H_
+#define STRINGUTIL_H_
+
+#include <string>
+
+namespace gameplay
+{
+
+bool endsWith(const char* str, const char* suffix, bool ignoreCase = true);
+
+/**
+ * Return true if the strings are equal. Case sensitive.
+ */
+bool equals(const std::string& a, const char* b);
+
+/**
+ * Returns true if the strings are equal. Case insensitive.
+ */
+bool equalsIgnoreCase(const std::string& a, const char* b);
+
+}
+#endif

+ 317 - 0
gameplay-encoder/src/TTFFontEncoder.cpp

@@ -0,0 +1,317 @@
+#include "TTFFontEncoder.h"
+
+
+void drawBitmap(unsigned char* dstBitmap, int x, int y, int dstWidth, unsigned char* srcBitmap, int srcWidth, int srcHeight)
+{
+    // offset dst bitmap by x,y.
+    dstBitmap +=  (x + (y * dstWidth));
+
+    for (int i = 0; i < srcHeight; i++)
+    {
+        memcpy(dstBitmap, (const void*)srcBitmap, srcWidth);
+        srcBitmap += srcWidth;
+        dstBitmap += dstWidth;
+    }
+}
+
+void writeUint(FILE* fp, unsigned int i)
+{
+    fwrite(&i, sizeof(unsigned int), 1, fp);
+}
+
+void writeFloat(FILE* fp, float f)
+{
+    fwrite(&f, sizeof(float), 1, fp);
+}
+
+void writeString(FILE* fp, const char* str)
+{
+    unsigned int len = strlen(str);
+    fwrite(&len, sizeof(unsigned int), 1, fp);
+    if (len > 0)
+    {
+        fwrite(str, 1, len, fp);
+    }
+}
+
+int writeFont(const char* filename, unsigned int fontSize, const char* id, bool fontpreview = false)
+{
+ 
+    Glyph glyphArray[END_INDEX - START_INDEX];
+    
+    // Initialize freetype library.
+    FT_Library library;
+    FT_Error error = FT_Init_FreeType(&library);
+    if (error)
+    {
+        fprintf(stderr, "FT_Init_FreeType error: %d \n", error);
+        return -1;
+    }
+    
+    // Initialize font face.
+    FT_Face face;
+    error = FT_New_Face(library, filename, 0, &face);
+    if (error)
+    {
+        fprintf(stderr, "FT_New_Face error: %d \n", error);
+        return -1;
+    }
+    
+    // Set the pixel size.
+    error = FT_Set_Char_Size(
+            face,           // handle to face object.
+            0,              // char_width in 1/64th of points.
+            fontSize * 64,   // char_height in 1/64th of points.
+            0,              // horizontal device resolution (defaults to 72 dpi if resolution (0, 0)).
+            0 );            // vertical device resolution.
+    
+    if (error)
+    {
+        fprintf(stderr, "FT_Set_Char_Size error: %d \n", error);
+        return -1;
+    }
+
+    /* 
+    error = FT_Set_Pixel_Sizes(face, FONT_SIZE, 0);
+    if (error)
+    {
+        fprintf(stderr, "FT_Set_Pixel_Sizes error : %d \n", error);
+        exit(1);
+    }
+    */
+
+    // Save glyph information (slot contains the actual glyph bitmap).
+    FT_GlyphSlot slot = face->glyph;
+    
+    int actualfontHeight = 0;
+    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++)
+    {
+        // Load glyph image into the slot (erase previous one) 
+        error = FT_Load_Char(face, ascii, FT_LOAD_RENDER);
+        if (error)
+            fprintf(stderr, "FT_Load_Char error : %d \n", error);
+        
+        int bitmapRows = slot->bitmap.rows;
+        actualfontHeight = (actualfontHeight < bitmapRows) ? bitmapRows : actualfontHeight;
+        
+        if (slot->bitmap.rows > slot->bitmap_top)
+          bitmapRows += (slot->bitmap.rows - slot->bitmap_top);
+        rowSize = (rowSize < bitmapRows) ? bitmapRows : rowSize;
+    }
+
+    // Include padding in the rowSize.
+    rowSize += GLYPH_PADDING;
+    
+    // Initialize with padding.
+    int penX = 0;
+    int penY = 0;
+    int row = 0;
+    
+    double powerOf2 = 2;
+    unsigned int imageWidth = 0;
+    unsigned int imageHeight = 0;
+    bool textureSizeFound = false;
+
+    int advance;
+    int i;
+
+    while (textureSizeFound == false)
+    {
+        imageWidth =  (unsigned int)pow(2.0, powerOf2);
+        imageHeight = (unsigned int)pow(2.0, powerOf2);
+        penX = 0;
+        penY = 0;
+        row = 0;
+
+        // 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++)
+        {
+            // Load glyph image into the slot (erase the previous one).
+            error = FT_Load_Char(face, ascii, FT_LOAD_RENDER);
+            if (error)
+                fprintf(stderr, "FT_Load_Char error : %d \n", error);
+
+            // Glyph image.
+            int glyphWidth = slot->bitmap.pitch;
+            int glyphHeight = slot->bitmap.rows;
+
+            advance = glyphWidth + GLYPH_PADDING; //((int)slot->advance.x >> 6) + GLYPH_PADDING;
+
+            // If we reach the end of the image wrap aroud to the next row.
+            if ((penX + advance) > (int)imageWidth)
+            {
+                penX = 0;
+                row += 1;
+                penY = row * rowSize;
+                if (penY + rowSize > (int)imageHeight)
+                {
+                    powerOf2++;
+                    break;
+                }
+            }
+
+            // penY should include the glyph offsets.
+            penY += (actualfontHeight - glyphHeight) + (glyphHeight - slot->bitmap_top);
+
+            // Set the pen position for the next glyph
+            penX += advance; // Move X to next glyph position
+            // Move Y back to the top of the row.
+            penY = row * rowSize;
+
+            if (ascii == (END_INDEX-1))
+                textureSizeFound = true;
+
+            i++;
+        }
+    }
+
+    // Try further to find a tighter texture size.
+    powerOf2 = 1;
+    for (;;)
+    {
+        if ((penY + rowSize) >= pow(2.0, powerOf2))
+            powerOf2++;
+        else
+        {
+            imageHeight = (int)pow(2.0, powerOf2);
+            break;
+        }
+    }
+    
+    // Allocate temporary image buffer to draw the glyphs into.
+    unsigned char* imageBuffer = (unsigned char *)malloc(imageWidth * imageHeight);
+    memset(imageBuffer, 0, imageWidth * imageHeight);
+    penX = 0;
+    penY = 0;
+    row = 0;
+    i = 0;
+    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);
+        if (error)
+            fprintf(stderr, "FT_Load_Char error : %d \n", error);
+
+        // Glyph image.
+        unsigned char* glyphBuffer =  slot->bitmap.buffer;
+        int glyphWidth = slot->bitmap.pitch;
+        int glyphHeight = slot->bitmap.rows;
+
+        advance = glyphWidth + GLYPH_PADDING;//((int)slot->advance.x >> 6) + GLYPH_PADDING;
+
+        // If we reach the end of the image wrap aroud to the next row.
+        if ((penX + advance) > (int)imageWidth)
+        {
+            penX = 0;
+            row += 1;
+            penY = row * rowSize;
+            if (penY + rowSize > (int)imageHeight)
+            {
+                fprintf(stderr, "Image size exceeded!");
+               return -1;
+            }
+
+        }
+        
+        // penY should include the glyph offsets.
+        penY += (actualfontHeight - glyphHeight) + (glyphHeight - slot->bitmap_top);
+
+        // Draw the glyph to the bitmap with a one pixel padding.
+        drawBitmap(imageBuffer, penX, penY, imageWidth, glyphBuffer, glyphWidth, glyphHeight);
+        
+        // Move Y back to the top of the row.
+        penY = row * rowSize;
+
+        glyphArray[i].index = ascii;
+        glyphArray[i].width = advance - GLYPH_PADDING;
+        
+        // Generate UV coords.
+        glyphArray[i].uvCoords[0] = (float)penX / (float)imageWidth;
+        glyphArray[i].uvCoords[1] = (float)penY / (float)imageHeight;
+        glyphArray[i].uvCoords[2] = (float)(penX + advance - GLYPH_PADDING) / (float)imageWidth;
+        glyphArray[i].uvCoords[3] = (float)(penY + rowSize) / (float)imageHeight;
+
+        // Set the pen position for the next glyph
+        penX += advance; // Move X to next glyph position
+        i++;
+    }
+    
+    unsigned int idlen = strlen(id);
+
+    // Write it to the id.gpb file.
+    char* fileName = (char*)malloc(idlen + 4);
+    strcpy(fileName, id);
+    strcat(fileName, ".gpb");
+
+    FILE *gpbFp = fopen(fileName, "wb");
+    
+    // File header and version.
+    char fileHeader[9]     = {'«', 'G', 'P', 'B', '»', '\r', '\n', '\x1A', '\n'};
+    char fileVersion[2]    = {1, 0};
+    fwrite(fileHeader, sizeof(char), 9, gpbFp);
+    fwrite(fileVersion, sizeof(char), 2, gpbFp);
+
+    // Write Ref table (for a single font)
+    writeUint(gpbFp, 1);                // Ref[] count
+    writeString(gpbFp, id);             // Ref id
+    writeUint(gpbFp, 128);              // Ref type
+    writeUint(gpbFp, ftell(gpbFp) + 4); // Ref offset (current pos + 4 bytes)
+    
+    // Write Font object.
+    
+    // Family name.
+    writeString(gpbFp, face->family_name);
+
+    // Style.
+    // TODO: Switch based on TTF style name and write appropriate font style unsigned int
+    // For now just hardcoding to 0.
+    //char* style = face->style_name;
+    writeUint(gpbFp, 0); // 0 == PLAIN
+
+    // Font size.
+    writeUint(gpbFp, rowSize);
+
+    // Character set.
+    // TODO: Empty for now
+    writeString(gpbFp, "");
+    
+    // Glyphs.
+    unsigned int glyphSetSize = END_INDEX - START_INDEX;
+    writeUint(gpbFp, glyphSetSize);
+    fwrite(&glyphArray, sizeof(Glyph), glyphSetSize, gpbFp);
+    
+    // Texture.
+    unsigned int textureSize = imageWidth * imageHeight;
+    writeUint(gpbFp, imageWidth);
+    writeUint(gpbFp, imageHeight);
+    writeUint(gpbFp, textureSize);
+    fwrite(imageBuffer, sizeof(unsigned char), textureSize, gpbFp);
+    
+    // Close file.
+    fclose(gpbFp);
+
+    printf("%s.gpb created successfully! \n", id);
+
+    if (fontpreview)
+    {
+        // Write out font map to an image.
+        strcpy(fileName, id);
+        strcat(fileName, ".pgm");
+
+        FILE *imageFp = fopen(fileName, "wb");
+        fprintf(imageFp, "P5 %d %d 255\n", imageWidth, imageHeight);
+        fwrite((const char *)imageBuffer, sizeof(unsigned char), imageWidth * imageHeight, imageFp);
+        fclose(imageFp);
+    }
+
+    // Cleanup resources.
+    free(imageBuffer);
+    
+    FT_Done_Face(face);
+    FT_Done_FreeType(library);
+    return 0;
+}

+ 33 - 0
gameplay-encoder/src/TTFFontEncoder.h

@@ -0,0 +1,33 @@
+#include <stdio.h>
+#include <ft2build.h>
+#include "string.h"
+#include <fstream>
+#include <math.h>
+
+#include FT_FREETYPE_H
+
+#define START_INDEX     32
+#define END_INDEX       127
+
+#define GLYPH_PADDING   4
+
+
+// Structure of Glyph.
+class Glyph
+{   
+public:
+    unsigned int index;
+    unsigned int width;
+    float uvCoords[4];
+};
+
+
+void drawBitmap(unsigned char* dstBitmap, int x, int y, int dstWidth, unsigned char* srcBitmap, int srcWidth, int srcHeight);
+
+void writeUint(FILE* fp, unsigned int i);
+
+void writeFloat(FILE* fp, float f);
+
+void writeString(FILE* fp, const char* str);
+
+int writeFont(const char* filename, unsigned int fontSize, const char* id, bool fontpreview);

+ 0 - 0
gameplay-encoder/src/Transform.cpp


+ 53 - 0
gameplay-encoder/src/Transform.h

@@ -0,0 +1,53 @@
+#ifndef TRANSFORM_H_
+#define TRANSFORM_H_
+
+namespace gameplay
+{
+
+class Transform
+{
+public:
+
+    enum TransformProperties
+    {
+        /**
+         * 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,
+
+        /**
+         * Rotation animation property. Data=qx,qy,qz,qw (as quaternion).
+         */
+        ANIMATE_ROTATE,
+
+        /**
+         * 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,
+
+        /**
+         * Rotation + Translation animation property(Rigid Body). Data=qx,qy,qz,qw,tx,ty,tz
+         */
+        ANIMATE_ROTATE_TRANSLATE,
+        /**
+         * Scale, Rotation + Translation animation property. Data=sx,sy,sz,qx,qy,qz,qw,tx,ty,tz
+         */
+        ANIMATE_SCALE_ROTATE_TRANSLATE
+    };
+};
+
+}
+
+#endif

+ 323 - 0
gameplay-encoder/src/Vector2.cpp

@@ -0,0 +1,323 @@
+/*
+ * Vector2.cpp
+ */
+
+#include "Base.h"
+#include "Vector2.h"
+#include "FileIO.h"
+
+namespace gameplay
+{
+
+Vector2::Vector2()
+    : x(0.0f), y(0.0f)
+{
+}
+
+
+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;
+}
+
+
+const Vector2& Vector2::one()
+{
+    static Vector2* value = new Vector2(1.0f, 1.0f);
+    return *value;
+}
+
+
+const Vector2& Vector2::unitX()
+{
+    static Vector2* value = new Vector2(1.0f, 0.0f);
+    return *value;
+}
+
+
+const Vector2& Vector2::unitY()
+{
+    static Vector2* value = new Vector2(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);
+
+    dst->x = v1.x + v2.x;
+    dst->y = v1.y + v2.y;
+}
+
+
+void Vector2::clamp(const Vector2& min, const Vector2& max)
+{
+    assert(!( min.x > max.x || min.y > max.y ));
+
+    // Clamp the x value.
+    if ( x < min.x )
+        x = min.x;
+    if ( x > max.x )
+        x = max.x;
+
+    // Clamp the y value.
+    if ( y < min.y )
+        y = min.y;
+    if ( y > max.y )
+        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.
+    dst->x = v.x;
+    if ( dst->x < min.x )
+        dst->x = min.x;
+    if ( dst->x > max.x )
+        dst->x = max.x;
+
+    // Clamp the y value.
+    dst->y = v.y;
+    if ( dst->y < min.y )
+        dst->y = min.y;
+    if ( dst->y > max.y )
+        dst->y = max.y;
+}
+
+
+float Vector2::distance(const Vector2& v)
+{
+    float dx = v.x - x;
+    float dy = v.y - y;
+
+    return sqrtf(dx * dx + dy * dy);
+}
+
+
+float Vector2::distanceSquared(const Vector2& v)
+{
+    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()
+{
+    return sqrtf(x * x + y * y);
+}
+
+
+float Vector2::lengthSquared()
+{
+    return (x * x + y * y);
+}
+
+
+void Vector2::negate()
+{
+    x = -x;
+    y = -y;
+}
+
+
+void Vector2::normalize()
+{
+    normalize(this);
+}
+
+
+void Vector2::normalize(Vector2* dst)
+{
+    assert(dst);
+
+    if (dst != this)
+    {
+        dst->x = x;
+        dst->y = y;
+    }
+
+    float n = x * x + y * y;
+    // already normalized
+    if (n == 1.0f)
+        return;
+
+    n = sqrtf(n);
+    // too close to zero
+    if (n < MATH_TOLERANCE)
+        return;
+
+    n = 1.0f / n;
+    dst->x *= n;
+    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);
+    float cosAngle = cos(angle);
+
+    if (point.isZero())
+    {
+        float tempX = x * cosAngle - y * sinAngle;
+        y = y * cosAngle + x * sinAngle;
+        x = tempX;
+    }
+    else
+    {
+        float tempX = x - point.x;
+        float tempY = y - point.y;
+
+        x = tempX * cosAngle - tempY * sinAngle + point.x;
+        y = tempY * cosAngle + tempX * sinAngle + point.y;
+    }
+}
+
+
+void Vector2::set(float x, float y)
+{
+    this->x = x;
+    this->y = y;
+}
+
+
+void Vector2::set(float* array)
+{
+    assert(array);
+
+    x = array[0];
+    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);
+
+    dst->x = v1.x - v2.x;
+    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);
+}
+
+}
+
+

+ 335 - 0
gameplay-encoder/src/Vector2.h

@@ -0,0 +1,335 @@
+/*
+ * Vector2.h
+ */
+
+#ifndef VECTOR2_H_
+#define VECTOR2_H_
+
+
+namespace gameplay
+{
+// Forward declare
+class Matrix;
+
+/**
+ * Defines a 2-element floating point vector.
+ */
+class Vector2
+{
+public:
+
+    /**
+     * The x coordinate.
+     */
+    float x;
+
+    /**
+     * The y coordinate.
+     */
+    float y;
+
+    /**
+     * Constructs a new vector initialized to all zeros.
+     */
+    Vector2();
+
+    /**
+     * Constructs a new vector initialized to the specified values.
+     *
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     */
+    Vector2(float x, float y);
+
+    /**
+     * Constructs a new vector from the values in the specified array.
+     *
+     * @param array An array containing the elements of the vector in the order x, y.
+     */
+    Vector2(float* array);
+
+    /**
+     * Constructs a vector that describes the direction between the specified points.
+     *
+     * @param p1 The first point.
+     * @param p2 The second point.
+     */
+    Vector2(const Vector2& p1, const Vector2& p2);
+
+    /**
+     * Constructs a new vector that is a copy of the specified vector.
+     *
+     * @param copy The vector to copy.
+     */
+    Vector2(const Vector2& copy);
+
+    /**
+     * Destructor.
+     */
+    ~Vector2();
+
+    /**
+     * The zero vector
+     *
+     * @return The 2-element vector of 0s.
+     */
+    static const Vector2& zero();
+
+    /**
+     * The one vector.
+     *
+     * @return The 2-element vector of 1s.
+     */
+    static const Vector2& one();
+
+    /**
+     * The unit x vector.
+     *
+     * @return The 2-element unit vector along the x axis.
+     */
+    static const Vector2& unitX();
+
+    /**
+     * The unit y vector.
+     *
+     * @return The 2-element unit vector along the y axis.
+     */
+    static const Vector2& unitY();
+
+    /**
+     * Is this vector the all zeros.
+     *
+     * @return true if all zeros, false if otherwise.
+     */
+    bool isZero() const;
+
+    /**
+     * Is this vector all ones.
+     *
+     * @return true if all ones, false if otherwise.
+     */
+    bool isOne() const;
+
+    /**
+     * Returns the angle (in radians) between the specified vectors.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @return The angle between the two vectors, in radians.
+     */
+    static float angle(const Vector2& v1, const Vector2& v2);
+
+    /**
+     * Adds the elements of the specified vector to this one.
+     *
+     * @param v The vector to add.
+     */
+    void add(const Vector2& v);
+
+    /**
+     * Adds the specified vectors and stores the result in dst.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @param dst A vector to store the result in.
+     */
+    static void add(const Vector2& v1, const Vector2& v2, Vector2* dst);
+
+    /**
+     * Clamps this vector within the specified range.
+     *
+     * @param min The minimum value.
+     * @param max The maximum value.
+     */
+    void clamp(const Vector2& min, const Vector2& max);
+
+    /**
+     * Clamps the specified vector within the specified range and returns it in dst.
+     *
+     * @param v The vector to clamp.
+     * @param min The minimum value.
+     * @param max The maximum value.
+     * @param dst A vector to store the result in.
+     */
+    static void clamp(const Vector2& v, const Vector2& min, const Vector2& max, Vector2* dst);
+
+    /**
+     * Returns the distance between this vector and v.
+     *
+     * @param v The other vector.
+     * @return The distance between this vector and v.
+     * @see distanceSquared
+     */
+    float distance(const Vector2& v);
+
+    /**
+     * Returns the squared distance between this vector and v.
+     *
+     * When it is not neccessary to get the exact distance between
+     * two vectors (for example, when simply comparing the
+     * distance between different vectors), it is advised to use
+     * this method instead of distance.
+     *
+     * @param v The other vector.
+     * @return The squared distance between this vector and v.
+     * @see distance
+     */
+    float distanceSquared(const Vector2& v);
+
+    /**
+     * Returns the dot product of this vector and the specified vector.
+     *
+     * @param v The vector to compute the dot product with.
+     * @return The dot product.
+     */
+    float dot(const Vector2& v);
+
+    /**
+     * Returns the dot product between the specified vectors.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @return The dot product between the vectors.
+     */
+    static float dot(const Vector2& v1, const Vector2& v2);
+
+    /**
+     * Computes the length of this vector.
+     *
+     * @return The length of the vector.
+     * @see lengthSquared
+     */
+    float length();
+
+    /**
+     * Returns the squared length of this vector.
+     *
+     * When it is not neccessary to get the exact length of a
+     * vector (for example, when simply comparing the lengths of
+     * different vectors), it is advised to use this method
+     * instead of length.
+     *
+     * @return The squared length of the vector.
+     * @see length
+     */
+    float lengthSquared();
+
+    /**
+     * Negates this vector.
+     */
+    void negate();
+
+    /**
+     * Normalizes this vector.
+     *
+     * This method normalizes this Vector2 so that it is of
+     * unit length (in other words, the length of the vector
+     * after calling this method will be 1.0f). If the vector
+     * already has unit length or if the length of the vector
+     * is zero, this method does nothing.
+     */
+    void normalize();
+
+    /**
+     * Normalizes this vector and stores the result in dst.
+     *
+     * If the vector already has unit length or if the length
+     * of the vector is zero, this method simply copies the
+     * current vector into dst.
+     *
+     * @param dst the destination vector
+     */
+    void normalize(Vector2* dst);
+
+    /**
+     * Scales all elements of this vector by the specified value.
+     *
+     * @param scalar The scalar value.
+     */
+    void scale(float scalar);
+
+    /**
+     * Scales each element of this vector by the matching component of scale.
+     *
+     * @param scale The vector to scale by.
+     */
+    void scale(const Vector2& scale);
+
+    /**
+     * 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.
+     */
+    void rotate(const Vector2& point, float angle);
+
+    /**
+     * Sets the elements of this vector to the specified values.
+     *
+     * @param x The new x coordinate.
+     * @param y The new y coordinate.
+     */
+    void set(float x, float y);
+
+    /**
+     * Sets the elements of this vector from the values in the specified array.
+     *
+     * @param array An array containing the elements of the vector in the order x, y.
+     */
+    void set(float* array);
+
+    /**
+     * Sets the elements of this vector to those in the specified vector.
+     *
+     * @param v The vector to copy.
+     */
+    void set(const Vector2& v);
+
+    /**
+     * Sets this vector to the directional vector between the specified points.
+     */
+    void set(const Vector2& p1, const Vector2& p2);
+
+    /**
+     * Subtracts this vector and the specified vector as (this - v)
+     * and stores the result in this.
+     *
+     * @param v The vector to subtract.
+     */
+    void subtract(const Vector2& v);
+
+    /**
+     * Subtracts the specified vectors and stores the result in dst.
+     * The resulting vector is computed as (v1 - v2).
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @param dst The destination vector.
+     */
+    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;
+    }
+
+    /**
+     * Writes this vector to the binary file stream.
+     */
+    void writeBinary(FILE* file) const;
+
+    /**
+     * Writes this vector to a text file stream.
+     */
+    void writeText(FILE* file) const;
+};
+
+}
+
+#endif

+ 363 - 0
gameplay-encoder/src/Vector3.cpp

@@ -0,0 +1,363 @@
+/*
+ * Vector3.cpp
+ */
+
+#include "Base.h"
+#include "Vector3.h"
+#include "FileIO.h"
+
+namespace gameplay
+{
+
+Vector3::Vector3()
+    : x(0.0f), y(0.0f), z(0.0f)
+{
+}
+
+
+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()
+{
+}
+
+
+const Vector3& Vector3::zero()
+{
+    static Vector3* value = new Vector3(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;
+}
+
+
+const Vector3& Vector3::unitX()
+{
+    static Vector3* value = new Vector3(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;
+}
+
+
+const Vector3& Vector3::unitZ()
+{
+    static Vector3* value = new Vector3(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;
+}
+
+float Vector3::angle(const Vector3& v1, const Vector3& v2)
+{
+    float dx = v1.y * v2.z - v1.z * v2.y;
+    float dy = v1.z * v2.x - v1.x * v2.z;
+    float dz = v1.x * v2.y - v1.y * v2.x;
+
+    return atan2f(sqrtf(dx * dx + dy * dy + dz * dz) + MATH_FLOAT_SMALL, dot(v1, v2));
+}
+
+
+void Vector3::add(const Vector3& v)
+{
+    x += v.x;
+    y += v.y;
+    z += v.z;
+}
+
+
+void Vector3::add(const Vector3& v1, const Vector3& v2, Vector3* dst)
+{
+    assert(dst);
+
+    dst->x = v1.x + v2.x;
+    dst->y = v1.y + v2.y;
+    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));
+
+    // Clamp the x value.
+    if ( x < min.x )
+        x = min.x;
+    if ( x > max.x )
+        x = max.x;
+
+    // Clamp the y value.
+    if ( y < min.y )
+        y = min.y;
+    if ( y > max.y )
+        y = max.y;
+
+    // Clamp the z value.
+    if ( z < min.z )
+        z = min.z;
+    if ( z > max.z )
+        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.
+    dst->x = v.x;
+    if ( dst->x < min.x )
+        dst->x = min.x;
+    if ( dst->x > max.x )
+        dst->x = max.x;
+
+    // Clamp the y value.
+    dst->y = v.y;
+    if ( dst->y < min.y )
+        dst->y = min.y;
+    if ( dst->y > max.y )
+        dst->y = max.y;
+
+    // Clamp the z value.
+    dst->z = v.z;
+    if ( dst->z < min.z )
+        dst->z = min.z;
+    if ( dst->z > max.z )
+        dst->z = max.z;
+}
+
+
+void Vector3::cross(const Vector3& v)
+{
+    float tx = (y * v.z) - (z * v.y);
+    float ty = (z * v.x) - (x * v.z);
+    float tz = (x * v.y) - (y * v.x);
+    x = tx;
+    y = ty;
+    z = tz;
+}
+
+
+void Vector3::cross(const Vector3& v1, const Vector3& v2, Vector3* dst)
+{
+    assert(dst);
+
+    float x = (v1.y * v2.z) - (v1.z * v2.y);
+    float y = (v1.z * v2.x) - (v1.x * v2.z);
+    float z = (v1.x * v2.y) - (v1.y * v2.x);
+    dst->x = x;
+    dst->y = y;
+    dst->z = z;
+}
+
+
+float Vector3::distance(const Vector3& v)
+{
+    float dx = v.x - x;
+    float dy = v.y - y;
+    float dz = v.z - z;
+
+    return sqrtf(dx * dx + dy * dy + dz * dz);
+}
+
+
+float Vector3::distanceSquared(const Vector3& v)
+{
+    float dx = v.x - x;
+    float dy = v.y - y;
+    float dz = v.z - z;
+
+    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()
+{
+    return sqrtf(x * x + y * y + z * z);
+}
+
+
+float Vector3::lengthSquared()
+{
+    return (x * x + y * y + z * z);
+}
+
+
+void Vector3::negate()
+{
+    x = -x;
+    y = -y;
+    z = -z;
+}
+
+
+void Vector3::normalize()
+{
+    normalize(this);
+}
+
+
+void Vector3::normalize(Vector3* dst) const
+{
+    assert(dst);
+
+    if (dst != this)
+    {
+        dst->x = x;
+        dst->y = y;
+        dst->z = z;
+    }
+
+    float n = x * x + y * y + z * z;
+    // already normalized
+    if (n == 1.0f)
+        return;
+
+    n = sqrt(n);
+    // too close to zero
+    if (n < MATH_TOLERANCE)
+        return;
+
+    n = 1.0f / n;
+    dst->x *= n;
+    dst->y *= n;
+    dst->z *= n;
+}
+
+
+void Vector3::scale(float scalar)
+{
+    x *= scalar;
+    y *= scalar;
+    z *= scalar;
+}
+
+
+void Vector3::set(float x, float y, float z)
+{
+    this->x = x;
+    this->y = y;
+    this->z = z;
+}
+
+
+void Vector3::set(float* array)
+{
+    assert(array);
+
+    x = array[0];
+    y = array[1];
+    z = array[2];
+}
+
+
+void Vector3::set(const Vector3& v)
+{
+    this->x = v.x;
+    this->y = v.y;
+    this->z = v.z;
+}
+
+
+void Vector3::set(const Vector3& p1, const Vector3& p2)
+{
+    x = p2.x - p1.x;
+    y = p2.y - p1.y;
+    z = p2.z - p1.z;
+}
+
+
+void Vector3::subtract(const Vector3& v)
+{
+    x -= v.x;
+    y -= v.y;
+    z -= v.z;
+}
+
+
+void Vector3::subtract(const Vector3& v1, const Vector3& v2, Vector3* dst)
+{
+    assert(dst);
+
+    dst->x = v1.x - v2.x;
+    dst->y = v1.y - v2.y;
+    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);
+}
+
+}

+ 367 - 0
gameplay-encoder/src/Vector3.h

@@ -0,0 +1,367 @@
+/*
+ * Vector3.h
+ */
+
+#ifndef VECTOR3_H_
+#define VECTOR3_H_
+
+
+namespace gameplay
+{
+
+// Forward declare
+class Matrix;
+class Quaternion;
+
+/**
+ * Defines a 3-element floating point vector.
+ *
+ * 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 elements of the vector represent a position in 3D space.
+ */
+class Vector3
+{
+public:
+
+    /**
+     * The x-coordinate.
+     */
+    float x;
+
+    /**
+     * The y-coordinate.
+     */
+    float y;
+
+    /**
+     * The z-coordinate.
+     */
+    float z;
+
+    /**
+     * Constructs a new vector initialized to all zeros.
+     */
+    Vector3();
+
+    /**
+     * Constructs a new vector initialized to the specified values.
+     *
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param z The z coordinate.
+     */
+    Vector3(float x, float y, float z);
+
+    /**
+     * Constructs a new vector from the values in the specified array.
+     *
+     * @param array An array containing the elements of the vector in the order x, y, z.
+     */
+    Vector3(float* array);
+
+    /**
+     * Constructs a vector that describes the direction between the specified points.
+     *
+     * @param p1 The first point.
+     * @param p2 The second point.
+     */
+    Vector3(const Vector3& p1, const Vector3& p2);
+
+    /**
+     * Constructs a new vector that is a copy of the specified vector.
+     *
+     * @param copy The vector to copy.
+     */
+    Vector3(const Vector3& copy);
+
+    /**
+     * Destructor.
+     */
+    ~Vector3();
+
+    /**
+     * The zero vector
+     *
+     * @return The 3-element vector of 0s.
+     */
+    static const Vector3& zero();
+
+    /**
+     * The one vector.
+     *
+     * @return The 3-element vector of 1s.
+     */
+    static const Vector3& one();
+
+    /**
+     * The unit x vector.
+     *
+     * @return The 3-element unit vector along the x axis.
+     */
+    static const Vector3& unitX();
+
+    /**
+     * The unit y vector.
+     *
+     * @return The 3-element unit vector along the y axis.
+     */
+    static const Vector3& unitY();
+
+    /**
+     * The unit z vector.
+     *
+     * @return The 3-element unit vector along the z axis.
+     */
+    static const Vector3& unitZ();
+
+    /**
+     * Is this vector the all zeros.
+     *
+     * @return true if all zeros, false if otherwise.
+     */
+    bool isZero() const;
+
+    /**
+     * Is this vector all ones.
+     *
+     * @return true if all ones, false if otherwise.
+     */
+    bool isOne() const;
+
+    /**
+     * Returns the angle (in radians) between the specified vectors.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @return The angle between the two vectors, in radians.
+     */
+    static float angle(const Vector3& v1, const Vector3& v2);
+
+
+    /**
+     * Adds the elements of the specified vector to this one.
+     *
+     * @param v The vector to add.
+     */
+    void add(const Vector3& v);
+
+    /**
+     * Adds the specified vectors and stores the result in dst.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @param dst A vector to store the result in.
+     */
+    static void add(const Vector3& v1, const Vector3& v2, Vector3* dst);
+
+    /**
+     * Clamps this vector within the specified range.
+     *
+     * @param min The minimum value.
+     * @param max The maximum value.
+     */
+    void clamp(const Vector3& min, const Vector3& max);
+
+    /**
+     * Clamps the specified vector within the specified range and returns it in dst.
+     *
+     * @param v The vector to clamp.
+     * @param min The minimum value.
+     * @param max The maximum value.
+     * @param dst A vector to store the result in.
+     */
+    static void clamp(const Vector3& v, const Vector3& min, const Vector3& max, Vector3* dst);
+
+    /**
+     * Sets this vector to the cross product between itself and the specified vector.
+     *
+     * @param v the vector to compute the cross product with.
+     */
+    void cross(const Vector3& v);
+
+    /**
+     * Computes the cross product of the specified vectors and stores the result in dst.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @param dst A vector to store the result in.
+     */
+    static void cross(const Vector3& v1, const Vector3& v2, Vector3* dst);
+
+    /**
+     * Returns the distance between this vector and v.
+     *
+     * @param v The other vector.
+     * @return The distance between this vector and v.
+     * @see distanceSquared
+     */
+    float distance(const Vector3& v);
+
+    /**
+     * Returns the squared distance between this vector and v.
+     *
+     * When it is not neccessary to get the exact distance between
+     * two vectors (for example, when simply comparing the
+     * distance between different vectors), it is advised to use
+     * this method instead of distance.
+     *
+     * @param v The other vector.
+     * @return The squared distance between this vector and v.
+     * @see distance
+     */
+    float distanceSquared(const Vector3& v);
+
+    /**
+     * Returns the dot product of this vector and the specified vector.
+     *
+     * @param v The vector to compute the dot product with.
+     * @return The dot product.
+     */
+    float dot(const Vector3& v);
+
+    /**
+     * Returns the dot product between the specified vectors.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @return The dot product between the vectors.
+     */
+    static float dot(const Vector3& v1, const Vector3& v2);
+
+    /**
+     * Computes the length of this vector.
+     *
+     * @return The length of the vector.
+     * @see lengthSquared
+     */
+    float length();
+
+    /**
+     * Returns the squared length of this vector.
+     *
+     * When it is not neccessary to get the exact length of a
+     * vector (for example, when simply comparing the lengths of
+     * different vectors), it is advised to use this method
+     * instead of length.
+     *
+     * @return The squared length of the vector.
+     * @see length
+     */
+    float lengthSquared();
+
+    /**
+     * Negates this vector.
+     */
+    void negate();
+
+    /**
+     * Normalizes this vector.
+     *
+     * This method normalizes this Vector3 so that it is of
+     * unit length (in other words, the length of the vector
+     * after calling this method will be 1.0f). If the vector
+     * already has unit length or if the length of the vector
+     * is zero, this method does nothing.
+     */
+    void normalize();
+
+    /**
+     * Normalizes this vector and stores the result in dst.
+     *
+     * If the vector already has unit length or if the length
+     * of the vector is zero, this method simply copies the
+     * current vector into dst.
+     *
+     * @param dst the destination vector
+     */
+    void normalize(Vector3* dst) const;
+
+    /**
+     * Scales all elements of this vector by the specified value.
+     *
+     * @param scalar The scalar value.
+     */
+    void scale(float scalar);
+
+    /**
+     * Sets the elements of this vector to the specified values.
+     *
+     * @param x The new x coordinate.
+     * @param y The new y coordinate.
+     * @param z The new z coordinate.
+     */
+    void set(float x, float y, float z);
+
+    /**
+     * Sets the elements of this vector from the values in the specified array.
+     *
+     * @param array An array containing the elements of the vector in the order x, y, z.
+     */
+    void set(float* array);
+
+    /**
+     * Sets the elements of this vector to those in the specified vector.
+     *
+     * @param v The vector to copy.
+     */
+    void set(const Vector3& v);
+
+    /**
+     * Sets this vector to the directional vector between the specified points.
+     */
+    void set(const Vector3& p1, const Vector3& p2);
+
+    /**
+     * Subtracts this vector and the specified vector as (this - v)
+     * and stores the result in this.
+     *
+     * @param v The vector to subtract.
+     */
+    void subtract(const Vector3& v);
+
+    /**
+     * Subtracts the specified vectors and stores the result in dst.
+     * The resulting vector is computed as (v1 - v2).
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @param dst The destination vector.
+     */
+    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;
+    }
+
+    inline bool operator==(const Vector3& v) const
+    {
+        return x==v.x && y==v.y && z==v.z;
+    }
+
+    static float distanceSquared(const Vector3& v1, const Vector3& v2);
+
+    /**
+     * Writes this vector to the binary file stream.
+     */
+    void writeBinary(FILE* file) const;
+
+    void writeText(FILE* file) const;
+};
+
+}
+
+#endif

+ 366 - 0
gameplay-encoder/src/Vector4.cpp

@@ -0,0 +1,366 @@
+/*
+ * Vector4.cpp
+ */
+
+#include "Base.h"
+#include "Vector4.h"
+#include "FileIO.h"
+
+namespace gameplay
+{
+
+Vector4::Vector4()
+    : x(0.0f), y(0.0f), z(0.0f), w(0.0f)
+{
+}
+
+
+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()
+{
+}
+
+
+const Vector4& Vector4::zero()
+{
+    static Vector4* value = new Vector4(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;
+}
+
+
+const Vector4& Vector4::unitX()
+{
+    static Vector4* value = new Vector4(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;
+}
+
+
+const Vector4& Vector4::unitZ()
+{
+    static Vector4* value = new Vector4(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;
+}
+
+
+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;
+    float dy = v1.w * v2.y - v1.y * v2.w - v1.z * v2.x + v1.x * v2.z;
+    float dz = v1.w * v2.z - v1.z * v2.w - v1.x * v2.y + v1.y * v2.x;
+
+    return atan2f(sqrtf(dx * dx + dy * dy + dz * dz) + MATH_FLOAT_SMALL, dot(v1, v2));
+}
+
+
+void Vector4::add(const Vector4& v)
+{
+    x += v.x;
+    y += v.y;
+    z += v.z;
+    w += v.w;
+}
+
+
+void Vector4::add(const Vector4& v1, const Vector4& v2, Vector4* dst)
+{
+    assert(dst);
+
+    dst->x = v1.x + v2.x;
+    dst->y = v1.y + v2.y;
+    dst->z = v1.z + v2.z;
+    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));
+
+    // Clamp the x value.
+    if ( x < min.x )
+        x = min.x;
+    if ( x > max.x )
+        x = max.x;
+
+    // Clamp the y value.
+    if ( y < min.y )
+        y = min.y;
+    if ( y > max.y )
+        y = max.y;
+
+    // Clamp the z value.
+    if ( z < min.z )
+        z = min.z;
+    if ( z > max.z )
+        z = max.z;
+
+    // Clamp the z value.
+    if ( w < min.w )
+        w = min.w;
+    if ( w > max.w )
+        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.
+    dst->x = v.x;
+    if ( dst->x < min.x )
+        dst->x = min.x;
+    if ( dst->x > max.x )
+        dst->x = max.x;
+
+    // Clamp the y value.
+    dst->y = v.y;
+    if ( dst->y < min.y )
+        dst->y = min.y;
+    if ( dst->y > max.y )
+        dst->y = max.y;
+
+    // Clamp the z value.
+    dst->z = v.z;
+    if ( dst->z < min.z )
+        dst->z = min.z;
+    if ( dst->z > max.z )
+        dst->z = max.z;
+
+    // Clamp the w value.
+    dst->w = v.w;
+    if ( dst->w < min.w )
+        dst->w = min.w;
+    if ( dst->w > max.w )
+        dst->w = max.w;
+}
+
+
+float Vector4::distance(const Vector4& v)
+{
+    float dx = v.x - x;
+    float dy = v.y - y;
+    float dz = v.z - z;
+    float dw = v.w - w;
+
+    return sqrtf(dx * dx + dy * dy + dz * dz + dw * dw);
+}
+
+
+float Vector4::distanceSquared(const Vector4& v)
+{
+    float dx = v.x - x;
+    float dy = v.y - y;
+    float dz = v.z - z;
+    float dw = v.w - w;
+
+    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()
+{
+    return sqrtf(x * x + y * y + z * z + w * w);
+}
+
+
+float Vector4::lengthSquared()
+{
+    return (x * x + y * y + z * z + w * w);
+}
+
+
+void Vector4::negate()
+{
+    x = -x;
+    y = -y;
+    z = -z;
+    w = -w;
+}
+
+
+void Vector4::normalize()
+{
+    normalize(this);
+}
+
+
+void Vector4::normalize(Vector4* dst)
+{
+    assert(dst);
+
+    if (dst != this)
+    {
+        dst->x = x;
+        dst->y = y;
+        dst->z = z;
+        dst->w = w;
+    }
+
+    float n = x * x + y * y + z * z + w * w;
+    // already normalized
+    if (n == 1.0f)
+        return;
+
+    n = sqrt(n);
+    // too close to zero
+    if (n < MATH_TOLERANCE)
+        return;
+
+    n = 1.0f / n;
+    dst->x *= n;
+    dst->y *= n;
+    dst->z *= n;
+    dst->w *= n;
+}
+
+
+void Vector4::scale(float scalar)
+{
+    x *= scalar;
+    y *= scalar;
+    z *= scalar;
+    w *= scalar;
+}
+
+
+void Vector4::set(float x, float y, float z, float w)
+{
+    this->x = x;
+    this->y = y;
+    this->z = z;
+    this->w = w;
+}
+
+
+void Vector4::set(float* array)
+{
+    assert(array);
+
+    x = array[0];
+    y = array[1];
+    z = array[2];
+    w = array[3];
+}
+
+
+void Vector4::set(const Vector4& v)
+{
+    this->x = v.x;
+    this->y = v.y;
+    this->z = v.z;
+    this->w = v.w;
+}
+
+
+void Vector4::set(const Vector4& p1, const Vector4& p2)
+{
+    x = p2.x - p1.x;
+    y = p2.y - p1.y;
+    z = p2.z - p1.z;
+    w = p2.w - p1.w;
+}
+
+
+void Vector4::subtract(const Vector4& v)
+{
+    x -= v.x;
+    y -= v.y;
+    z -= v.z;
+    w -= v.w;
+}
+
+
+void Vector4::subtract(const Vector4& v1, const Vector4& v2, Vector4* dst)
+{
+    assert(dst);
+
+    dst->x = v1.x - v2.x;
+    dst->y = v1.y - v2.y;
+    dst->z = v1.z - v2.z;
+    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);
+}
+
+}

+ 365 - 0
gameplay-encoder/src/Vector4.h

@@ -0,0 +1,365 @@
+/*
+ * Vector4.h
+ */
+
+#ifndef VECTOR4_H_
+#define VECTOR4_H_
+
+#include "FileIO.h"
+
+namespace gameplay
+{
+
+// Forward declare
+class Matrix;
+
+/**
+ * Defines 4-element floating point vector.
+ */
+class Vector4
+{
+public:
+
+    /**
+     * The x-coordinate.
+     */
+    float x;
+
+    /**
+     * The y-coordinate.
+     */
+    float y;
+
+    /**
+     * The z-coordinate.
+     */
+    float z;
+
+    /**
+     * The w-coordinate.
+     */
+    float w;
+
+    /**
+     * Constructs a new vector initialized to all zeros.
+     */
+    Vector4();
+
+    /**
+     * Constructs a new vector initialized to the specified values.
+     *
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     * @param z The z coordinate.
+     * @param w The w coordinate.
+     */
+    Vector4(float x, float y, float z, float w);
+
+    /**
+     * Constructs a new vector from the values in the specified array.
+     *
+     * @param array An array containing the elements of the vector in the order x, y, z, w.
+     */
+    Vector4(float* array);
+
+    /**
+     * Constructs a vector that describes the direction between the specified points.
+     *
+     * @param p1 The first point.
+     * @param p2 The second point.
+     */
+    Vector4(const Vector4& p1, const Vector4& p2);
+
+    /**
+     * Constructor.
+     *
+     * Creates a new vector that is a copy of the specified vector.
+     *
+     * @param copy The vector to copy.
+     */
+    Vector4(const Vector4& copy);
+
+    /**
+     * Destructor.
+     */
+    ~Vector4();
+
+    /**
+     * The zero vector
+     *
+     * @return The 4-element vector of 0s.
+     */
+    static const Vector4& zero();
+
+    /**
+     * The one vector.
+     *
+     * @return The 4-element vector of 1s.
+     */
+    static const Vector4& one();
+
+    /**
+     * The unit x vector.
+     *
+     * @return The 4-element unit vector along the x axis.
+     */
+    static const Vector4& unitX();
+
+    /**
+     * The unit y vector.
+     *
+     * @return The 4-element unit vector along the y axis.
+     */
+    static const Vector4& unitY();
+
+    /**
+     * The unit z vector.
+     *
+     * @return The 4-element unit vector along the z axis.
+     */
+    static const Vector4& unitZ();
+
+    /**
+     * The unit w vector.
+     *
+     * @return The 4-element unit vector along the w axis.
+     */
+    static const Vector4& unitW();
+
+    /**
+     * Is this vector the all zeros.
+     *
+     * @return true if all zeros, false if otherwise.
+     */
+    bool isZero() const;
+
+    /**
+     * Is this vector all ones.
+     *
+     * @return true if all ones, false if otherwise.
+     */
+    bool isOne() const;
+
+    /**
+     * Returns the angle (in radians) between the specified vectors.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @return The angle between the two vectors, in radians.
+     */
+    static float angle(const Vector4& v1, const Vector4& v2);
+
+    /**
+     * Adds the elements of the specified vector to this one.
+     *
+     * @param v The vector to add.
+     */
+    void add(const Vector4& v);
+
+    /**
+     * Adds the specified vectors and stores the result in dst.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @param dst A vector to store the result in.
+     */
+    static void add(const Vector4& v1, const Vector4& v2, Vector4* dst);
+
+    /**
+     * Clamps this vector within the specified range.
+     *
+     * @param min The minimum value.
+     * @param max The maximum value.
+     */
+    void clamp(const Vector4& min, const Vector4& max);
+
+    /**
+     * Clamps the specified vector within the specified range and returns it in dst.
+     *
+     * @param v The vector to clamp.
+     * @param min The minimum value.
+     * @param max The maximum value.
+     * @param dst A vector to store the result in.
+     */
+    static void clamp(const Vector4& v, const Vector4& min, const Vector4& max, Vector4* dst);
+
+    /**
+     * Returns the distance between this vector and v.
+     *
+     * @param v The other vector.
+     * @return The distance between this vector and v.
+     * @see distanceSquared
+     */
+    float distance(const Vector4& v);
+
+    /**
+     * Returns the squared distance between this vector and v.
+     *
+     * When it is not neccessary to get the exact distance between
+     * two vectors (for example, when simply comparing the
+     * distance between different vectors), it is advised to use
+     * this method instead of distance.
+     *
+     * @param v The other vector.
+     * @return The squared distance between this vector and v.
+     * @see distance
+     */
+    float distanceSquared(const Vector4& v);
+
+    /**
+     * Returns the dot product of this vector and the specified vector.
+     *
+     * @param v The vector to compute the dot product with.
+     * @return The dot product.
+     */
+    float dot(const Vector4& v);
+
+    /**
+     * Returns the dot product between the specified vectors.
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @return The dot product between the vectors.
+     */
+    static float dot(const Vector4& v1, const Vector4& v2);
+
+    /**
+     * Computes the length of this vector.
+     *
+     * @return The length of the vector.
+     * @see lengthSquared
+     */
+    float length();
+
+    /**
+     * Returns the squared length of this vector.
+     *
+     * When it is not neccessary to get the exact length of a
+     * vector (for example, when simply comparing the lengths of
+     * different vectors), it is advised to use this method
+     * instead of length.
+     *
+     * @return The squared length of the vector.
+     * @see length
+     */
+    float lengthSquared();
+
+    /**
+     * Negates this vector.
+     */
+    void negate();
+
+    /**
+     * Normalizes this vector.
+     *
+     * This method normalizes this Vector4 so that it is of
+     * unit length (in other words, the length of the vector
+     * after calling this method will be 1.0f). If the vector
+     * already has unit length or if the length of the vector
+     * is zero, this method does nothing.
+     */
+    void normalize();
+
+    /**
+     * Normalizes this vector and stores the result in dst.
+     *
+     * If the vector already has unit length or if the length
+     * of the vector is zero, this method simply copies the
+     * current vector into dst.
+     *
+     * @param dst the destination vector
+     */
+    void normalize(Vector4* dst);
+
+    /**
+     * Scales all elements of this vector by the specified value.
+     *
+     * @param scalar The scalar value.
+     */
+    void scale(float scalar);
+
+    /**
+     * Sets the elements of this vector to the specified values.
+     *
+     * @param x The new x coordinate.
+     * @param y The new y coordinate.
+     * @param z The new z coordinate.
+     * @param w The new w coordinate.
+     */
+    void set(float x, float y, float z, float w);
+
+    /**
+     * Sets the elements of this vector from the values in the specified array.
+     *
+     * @param array An array containing the elements of the vector in the order x, y, z, w.
+     */
+    void set(float* array);
+
+    /**
+     * Sets the elements of this vector to those in the specified vector.
+     *
+     * @param v The vector to copy.
+     */
+    void set(const Vector4& v);
+
+    /**
+     * Sets this vector to the directional vector between the specified points.
+     */
+    void set(const Vector4& p1, const Vector4& p2);
+
+    /**
+     * Subtracts this vector and the specified vector as (this - v)
+     * and stores the result in this.
+     *
+     * @param v The vector to subtract.
+     */
+    void subtract(const Vector4& v);
+
+    /**
+     * Subtracts the specified vectors and stores the result in dst.
+     * The resulting vector is computed as (v1 - v2).
+     *
+     * @param v1 The first vector.
+     * @param v2 The second vector.
+     * @param dst The destination vector.
+     */
+    static void subtract(const Vector4& v1, const Vector4& v2, Vector4* dst);
+
+
+
+    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;
+    }
+
+    inline bool operator==(const Vector4& v) const
+    {
+        return x==v.x && y==v.y && z==v.z && w==v.w;
+    }
+
+    /**
+     * Writes this vector to the binary file stream.
+     */
+    void writeBinary(FILE* file) const;
+
+    void writeText(FILE* file) const;
+};
+
+}
+
+#endif

+ 105 - 0
gameplay-encoder/src/Vertex.cpp

@@ -0,0 +1,105 @@
+#include "Vertex.h"
+
+namespace gameplay
+{
+
+Vertex::Vertex(void)
+{
+    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;
+    if (hasNormal)
+        count += 3;
+    if (hasTangent)
+        count += 3;
+    if (hasBinormal)
+        count += 3;
+    if (hasTexCoord)
+        count += 2;
+    if (hasWeights)
+        count += 8;
+    return count * sizeof(float);
+}
+
+void Vertex::writeBinary(FILE* file) const
+{
+    position.writeBinary(file);
+    if (hasNormal)
+    {
+        normal.writeBinary(file);
+    }
+    if (hasTangent)
+    {
+        tangent.writeBinary(file);
+    }
+    if (hasBinormal)
+    {
+        binormal.writeBinary(file);
+    }
+    if (hasTexCoord)
+    {
+        texCoord.writeBinary(file);
+    }
+    // TODO add vertex color?
+    //if (hasColor)
+    //{
+    //    color.writeBinary(file);
+    //}
+    if (hasWeights)
+    {
+        blendWeights.writeBinary(file);
+        blendIndices.writeBinary(file);
+    }
+}
+
+void Vertex::writeText(FILE* file) const
+{
+    write("// position\n", file);
+    position.writeText(file);
+    if (hasNormal)
+    {
+        write("// normal\n", file);
+        normal.writeText(file);
+    }
+    if (hasTangent)
+    {
+        write("// tanget\n", file);
+        tangent.writeText(file);
+    }
+    if (hasBinormal)
+    {
+        write("// binormal\n", file);
+        binormal.writeText(file);
+    }
+    if (hasTexCoord)
+    {
+        write("// texCoord\n", file);
+        texCoord.writeText(file);
+    }
+    if (hasWeights)
+    {
+        write("// blendWeights\n", file);
+        blendWeights.writeText(file);
+        write("// blendIndices\n", file);
+        blendIndices.writeText(file);
+    }
+}
+
+}

+ 95 - 0
gameplay-encoder/src/Vertex.h

@@ -0,0 +1,95 @@
+#ifndef VERTEX_H_
+#define VERTEX_H_
+
+#include "FileIO.h"
+#include "Vector2.h"
+#include "Vector3.h"
+#include "Vector4.h"
+
+namespace gameplay
+{
+
+class Vertex
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Vertex(void);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Vertex(void);
+
+    Vector3 position;
+    Vector3 normal;
+    Vector3 tangent;
+    Vector3 binormal;
+    Vector2 texCoord;
+
+    Vector4 blendWeights;
+    Vector4 blendIndices;
+
+    bool hasNormal, hasTangent, hasBinormal, hasTexCoord, hasColor, hasWeights;
+
+    inline bool operator<(const Vertex& v) const
+    {
+        if (position == v.position)
+        {
+            if (normal == v.normal)
+            {
+                if (tangent == v.tangent)
+                {
+                    if (binormal == v.binormal)
+                    {
+                        if (texCoord == v.texCoord)
+                        {
+                            if (blendWeights == v.blendWeights)
+                            {
+                                if (blendIndices == v.blendIndices)
+                                {
+                                    return false;
+                                }
+                                return blendIndices < v.blendIndices;
+                            }
+                            return blendWeights < v.blendWeights;
+                        }
+                        return texCoord < v.texCoord;
+                    }
+                    return binormal < v.binormal;
+                }
+                return tangent < v.tangent;
+            }
+            return normal < v.normal;
+        }
+        return position < v.position;
+    }
+
+    inline bool operator==(const Vertex& v) const
+    {
+        return position==v.position && normal==v.normal && tangent==v.tangent && binormal==v.binormal && texCoord==v.texCoord && 
+            blendWeights==v.blendWeights && blendIndices==v.blendIndices;
+    }
+
+    void reset();
+
+    /**
+     * Returns the size of this vertex in bytes.
+     */
+    unsigned int byteSize() const;
+
+    /**
+     * Writes this vertex to the binary file stream.
+     */
+    void writeBinary(FILE* file) const;
+
+    /**
+     * Writes this vertex to a text file stream.
+     */
+    void writeText(FILE* file) const;
+};
+
+}
+#endif

+ 75 - 0
gameplay-encoder/src/VertexElement.cpp

@@ -0,0 +1,75 @@
+#include "VertexElement.h"
+
+namespace gameplay
+{
+
+VertexElement::VertexElement(unsigned int t, unsigned int c) :
+    usage(t),
+    size(c)
+{
+}
+
+VertexElement::~VertexElement(void)
+{
+}
+const char* VertexElement::getElementName(void) const
+{
+    return "VertexElement";
+}
+
+void VertexElement::writeBinary(FILE* file)
+{
+    Object::writeBinary(file);
+    write(usage, file);
+    write(size, file);
+}
+void VertexElement::writeText(FILE* file)
+{
+    fprintElementStart(file);
+    fprintfElement(file, "usage", usageStr(usage));
+    fprintfElement(file, "size", size);
+    fprintElementEnd(file);
+}
+
+const char* VertexElement::usageStr(unsigned int usage)
+{
+    switch (usage)
+    {
+        case UNKNOWN:
+            return "UNKNOWN";
+        case POSITION:
+            return "POSITION";
+        case NORMAL:
+            return "NORMAL";
+        case COLOR:
+            return "COLOR";
+        case TANGENT:
+            return "TANGENT";
+        case BINORMAL:
+            return "BINORMAL";
+        case BLENDWEIGHTS:
+            return "BLENDWEIGHTS";
+        case BLENDINDICES:
+            return "BLENDINDICES";
+        case TEXCOORD0:
+            return "TEXCOORD0";
+        case TEXCOORD1:
+            return "TEXCOORD1";
+        case TEXCOORD2:
+            return "TEXCOORD2";
+        case TEXCOORD3:
+            return "TEXCOORD3";
+        case TEXCOORD4:
+            return "TEXCOORD4";
+        case TEXCOORD5:
+            return "TEXCOORD5";
+        case TEXCOORD6:
+            return "TEXCOORD6";
+        case TEXCOORD7:
+            return "TEXCOORD7";
+        default:
+            return "";
+    }
+}
+
+}

+ 34 - 0
gameplay-encoder/src/VertexElement.h

@@ -0,0 +1,34 @@
+#ifndef VERTEXELEMENT_H_
+#define VERTEXELEMENT_H_
+
+#include "Object.h"
+
+namespace gameplay
+{
+
+class VertexElement : public Object
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    VertexElement(unsigned int t, unsigned int c);
+
+    /**
+     * Destructor.
+     */
+    virtual ~VertexElement(void);
+
+    virtual const char* getElementName(void) const;
+    virtual void writeBinary(FILE* file);
+    virtual void writeText(FILE* file);
+
+    static const char* usageStr(unsigned int usage);
+
+    unsigned int usage;
+    unsigned int size;
+};
+
+}
+#endif

+ 234 - 0
gameplay-encoder/src/main.cpp

@@ -0,0 +1,234 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+#include <string>
+#include <sys/stat.h>
+#ifdef WIN32
+    #define PATH_MAX    _MAX_PATH
+    #define realpath(A,B)    _fullpath(B,A,PATH_MAX)
+#endif
+
+#include "DAESceneEncoder.h"
+#include "TTFFontEncoder.h"
+#include "StringUtil.h"
+#include "GPBDecoder.h"
+
+using namespace std;
+using namespace gameplay;
+
+enum FileFormat
+{
+    FILEFORMAT_UNKNOWN,
+    FILEFORMAT_DAE,
+    FILEFORMAT_TTF,
+    FILEFORMAT_GPB
+};
+
+
+/**
+ * Gets the file format from the file path based on extension.
+ */
+FileFormat getFileFormat(const std::string& filename)
+{
+    // Extract the extension
+    std::string ext = "";
+    size_t pos = filename.find_last_of(".");
+    if (pos != std::string::npos)
+        ext = filename.substr(pos + 1);
+    
+    // Match every supported extension with its format constant
+    if (ext.compare("dae") == 0 || ext.compare("DAE") == 0)
+    {
+        return FILEFORMAT_DAE;
+    }
+    if (ext.compare("ttf") == 0 || ext.compare("TTF") == 0)
+    {
+        return FILEFORMAT_TTF;
+    }
+    if (ext.compare("gpb") == 0 || ext.compare("GPB") == 0)
+    {
+        return FILEFORMAT_GPB;
+    }
+
+    return FILEFORMAT_UNKNOWN;
+}
+
+
+/**
+ * Tests if a file exists on the file system.
+ */
+bool fileExists(const std::string& filename)
+{
+    struct stat buf;
+    if (stat(filename.c_str(), &buf) != -1)
+    {
+        return true;
+    }
+    return false;
+}
+
+
+/**
+ * Replaces all instance of oldChar with newChar in str.
+ */
+void replace_char(char* str, char oldChar, char newChar)
+{
+    for (; *str != '\0'; str++)
+    {
+        if (*str == oldChar)
+            *str = newChar;
+    }
+}
+
+std::string getFileName(const std::string& filepath)
+{
+    size_t index1 = filepath.find_last_of('\\');
+    size_t index2 = filepath.find_last_of('/');
+    size_t index = (index1 != -1 && index1 > index2 ? index1 : index2);
+    size_t length = filepath.length();
+    string filename = filepath.substr(index + 1, length);
+    length = filename.length();
+    string output = filename.substr(0, (length-4));
+    return output;
+    
+}
+
+
+std::string getRealpath(const std::string& filepath)
+{
+    char path[PATH_MAX + 1]; /* not sure about the "+ 1" */
+    realpath(filepath.c_str(), path);
+    replace_char(path, '\\', '/');
+    return std::string(path);
+}
+
+
+void usage()
+{
+    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,".ttf file options:\n");
+    fprintf(stderr,"-s<size of font> -p \n");
+    exit(8);
+}
+
+
+/**
+ * Main application entry point.
+ *
+ * @param argc The number of command line arguments
+ * @param argv The array of command line arguments.
+ *
+ * usage:   gameplay-encoder[options] <file_list>
+ * example: gameplay-encoder C:/assets/seymour.dae
+ * example: gameplay-encoder -i boy seymour.dae
+ *
+ * @stod: Improve argument parsing.
+ */
+int main(int argc, const char** argv)
+{
+    std::string filepath;
+    const char* id = NULL;
+    unsigned int size = 0;
+    bool fontpreview = false;
+    bool text = false;
+
+    if (argc <= 1)
+    {
+        usage();
+    }
+
+    if ( endsWith(argv[1], ".dae") || endsWith(argv[1], ".gpb"))
+    {
+        filepath = argv[1];
+    }
+    else
+    {
+        if (argc < 3)
+        {
+            usage();
+        }
+        else
+        {   
+            filepath = argv[argc-1];
+        }
+    }
+
+    // Check if the file exists.
+    if (!fileExists(filepath))
+    {
+        fprintf(stderr, "Error: File not found: %s\n", filepath.c_str());
+        return -1;
+    }
+    // File exists
+    fprintf(stderr, "Encoding file: %s\n", filepath.c_str());
+    
+    // Get the file format
+    FileFormat fileFormat = getFileFormat(filepath);
+
+    // Parse arguments
+    while ((argc > 1) && (argv[1][0] == '-'))
+    {
+        switch (argv[1][1])
+        {
+            case 'i':
+            case 'o':
+                id = &argv[1][2];
+                break;
+
+            case 's':
+                size = atoi(&argv[1][2]);
+                break;
+            
+            case 'p':
+                fontpreview = true;
+                break;
+            case 't':
+                text = true;
+                break;
+
+            default:
+                usage();
+        }
+        ++argv;
+        --argc;
+    }
+    
+
+    switch (fileFormat)
+    {
+    case FILEFORMAT_DAE:
+        {
+            std::string realpath = getRealpath(filepath);
+            DAESceneEncoder daeEncoder;
+            daeEncoder.write(realpath, id, text);
+            break;
+        }
+    case FILEFORMAT_TTF:
+        {
+            std::string realpath = getRealpath(filepath);
+            std::string id = getFileName(realpath);
+            writeFont(realpath.c_str(), size, id.c_str(), fontpreview);
+            break;
+        }
+
+    case FILEFORMAT_GPB:
+        {
+            std::string realpath = getRealpath(filepath);
+            GPBDecoder decoder;
+            decoder.readBinary(realpath);
+            break;
+        }
+               
+   default:
+        {
+            fprintf(stderr, "Error: Unsupported file format: %s\n", filepath.c_str());
+            return -1;
+        }
+    }
+
+    return 0;
+}
+

+ 11 - 0
gameplay-resources/.project

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>gameplay-resources</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+	</buildSpec>
+	<natures>
+	</natures>
+</projectDescription>

+ 41 - 0
gameplay-resources/res/shaders/bumped-specular.fsh

@@ -0,0 +1,41 @@
+precision highp float;
+
+// Uniforms
+uniform vec3 u_lightColor;                        // RGB color of the light
+uniform vec3 u_ambientColor;                    // Ambient color
+uniform float u_specularExponent;                // Specular exponent or shininess property.
+uniform sampler2D u_diffuseTexture;                // Diffuse texture
+uniform sampler2D u_normalMapTexture;            // Normal map texture
+
+// Inputs
+varying vec2 v_texCoord;                        // Texture Coordinate (u,v)
+varying vec3 v_lightDirectionTangentSpace;        // Direction of the light in tangent space
+varying vec3 v_cameraDirectionTangentSpace;     // Direction the camera is looking at in tangent space
+
+void main()
+{
+    // Fetch diffuse color from texture
+    vec4 diffuseColor = texture2D(u_diffuseTexture, v_texCoord);
+    
+    // Normalize vectors
+    vec3 cameraDirection = normalize(v_cameraDirectionTangentSpace);
+    vec3 lightDirection = normalize(v_lightDirectionTangentSpace);
+    // Fetch normals from the normal map
+    vec3 normalVector = normalize(texture2D(u_normalMapTexture, v_texCoord).rgb * 2.0 - 1.0);
+    
+    // Ambient
+    vec3 lightColor = u_ambientColor;
+
+    // Diffuse
+    float diffuseIntensity = max(0.0, dot(normalVector, lightDirection));
+    lightColor += (u_lightColor * diffuseIntensity);
+
+    // Specular
+    vec3 halfVector = normalize(cameraDirection + lightDirection);
+    float specularIntensity = pow(max(0.0, dot(normalVector, halfVector)), u_specularExponent);
+    
+    // Light the pixel.
+    gl_FragColor = diffuseColor;
+    gl_FragColor.rgb *= lightColor;
+    gl_FragColor.rgb += vec3(diffuseColor.rgb * specularIntensity);
+}

+ 43 - 0
gameplay-resources/res/shaders/bumped-specular.vsh

@@ -0,0 +1,43 @@
+// Uniforms
+uniform    vec3 u_lightDirection;                        // Direction of the light
+uniform mat4 u_worldViewProjectionMatrix;            // Matrix to transform a position to clip space.
+uniform mat4 u_inverseTransposeWorldViewMatrix;        // Matrix to transform a normal to view space.
+uniform mat4 u_worldMatrix;                            // Matrix to tranform a position to world space.
+uniform vec3 u_cameraPosition;                        // Position of the camera.
+
+// Inputs
+attribute vec4 a_position;                            // Vertex Position (x, y, z, w)
+attribute vec3 a_normal;                            // Vertex Normal (x, y, z)
+attribute vec2 a_texCoord;                            // Vertex Texture Coordinate (u, v)
+attribute vec3 a_tangent;                            // Vertex Tangent (x, y, z)
+attribute vec3 a_binormal;                            // Vertex Binormal (actually Bi-tangent) (x, y, z)
+
+// Outputs
+varying vec2 v_texCoord;                            // Texture Coordinate (u,v)
+varying vec3 v_lightDirectionTangentSpace;            // Direction of the light in tangent space.
+varying vec3 v_cameraDirectionTangentSpace;         // Direction the camera is looking at in tangent space.
+
+void main()
+{
+    // Transform position to clip space.
+    gl_Position = u_worldViewProjectionMatrix * a_position;
+
+    // Transform the normal, tangent and binormals to  view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix);
+    vec3 tangentVector  = inverseTransposeWorldViewMatrix * a_tangent;
+    vec3 normalVector = inverseTransposeWorldViewMatrix * a_normal;
+    vec3 binormalVector = inverseTransposeWorldViewMatrix * a_binormal;
+
+    // Create a transform to convert a vector to tangent space.
+    mat3 tangentSpaceTransformMatrix = mat3(tangentVector, binormalVector, normalVector);
+
+    // Transform light direction to tangent space.
+    v_lightDirectionTangentSpace = tangentSpaceTransformMatrix * u_lightDirection;
+
+    // Compute camera direction and transform it to tangent space.
+    vec4 positionWorldSpace = u_worldMatrix * a_position;
+    v_cameraDirectionTangentSpace = tangentSpaceTransformMatrix * (u_cameraPosition - positionWorldSpace.xyz);
+
+    // Pass on the texture coordinates to Fragment shader.
+    v_texCoord = a_texCoord;
+}

+ 32 - 0
gameplay-resources/res/shaders/bumped.fsh

@@ -0,0 +1,32 @@
+precision highp float;
+
+// Uniforms
+uniform vec3 u_lightColor;                    // RGB color of the light
+uniform vec3 u_ambientColor;                // Ambient color
+uniform sampler2D u_diffuseTexture;            // Texture
+uniform sampler2D u_normalMapTexture;        // Normalmap
+
+// Inputs
+varying vec3 v_lightDirectionTangentSpace;    // light direction
+varying vec2 v_texCoord;                    // Texture coordinate.
+
+void main()
+{
+    // Fetch normals from the normal map
+    vec3 normalVector = texture2D(u_normalMapTexture, v_texCoord).rgb * 2.0 - 1.0;
+    normalVector = normalize(normalVector);
+    
+    // Fetch diffuse color from texture
+    gl_FragColor = texture2D(u_diffuseTexture, v_texCoord);
+    
+    // Ambient
+    vec3 lightColor = u_ambientColor;
+
+    // Diffuse
+    vec3 lightDirection = normalize(v_lightDirectionTangentSpace);
+    float diffuseIntensity = clamp(dot(normalVector, lightDirection), 0.0, 1.0);
+    lightColor += (u_lightColor * diffuseIntensity);
+
+    // Light the fragment.
+    gl_FragColor.rgb *= lightColor;
+}

+ 36 - 0
gameplay-resources/res/shaders/bumped.vsh

@@ -0,0 +1,36 @@
+// Uniforms
+uniform vec3 u_lightDirection;                        // Direction of the light
+uniform mat4 u_worldViewProjectionMatrix;            // Matrix to transform a position to clip space.
+uniform mat4 u_inverseTransposeWorldViewMatrix;        // Matrix to transform a normal to view space.
+
+// Inputs
+attribute vec4 a_position;                            // Vertex Position (x, y, z, w)
+attribute vec3 a_normal;                            // Vertex Normal (x, y, z)
+attribute vec2 a_texCoord;                            // Vertex Texture Coordinate (u, v)
+attribute vec3 a_tangent;                            // Vertex Tangent (x, y, z)
+attribute vec3 a_binormal;                            // Vertex Binormal (actually Bi-tangent) (x, y, z)
+
+// Outputs
+varying vec3 v_lightDirectionTangentSpace;            // Direction of light in tangent space.
+varying vec2 v_texCoord;                            // Texture coordinate (u, v).
+
+void main()
+{
+    // Transform position to clip space.
+    gl_Position = u_worldViewProjectionMatrix * a_position;
+
+    // Transform normal, tangent and binormal to view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix);
+    vec3 tangent  = inverseTransposeWorldViewMatrix * a_tangent;
+    vec3 normal = inverseTransposeWorldViewMatrix * a_normal;
+    vec3 binormal = inverseTransposeWorldViewMatrix * a_binormal;
+
+    // Create a transform to convert a vector to tangent space.
+    mat3 tangentSpaceTransformMatrix = mat3(tangent, binormal, normal);
+    
+    // Transform light direction to tangent space.
+    v_lightDirectionTangentSpace = tangentSpaceTransformMatrix * u_lightDirection;
+
+    // Pass on the texture coordinates to Fragment shader.
+    v_texCoord = a_texCoord;
+}

+ 36 - 0
gameplay-resources/res/shaders/colored-specular.fsh

@@ -0,0 +1,36 @@
+precision highp float;
+
+// Uniforms
+uniform    vec3 u_lightDirection;        // Direction of the light
+uniform vec3 u_lightColor;            // RGB color of the light
+uniform vec3 u_ambientColor;        // Ambient color
+uniform vec4 u_diffuseColor;        // Diffuse color
+uniform float u_specularExponent;    // Specular exponent or shininess property.
+
+// Inputs
+varying vec3 v_normalVector;        // Normal vector in view space.
+varying vec3 v_cameraDirection;        // Camera direction.
+
+void main()
+{
+    // Normalize the vectors.
+    vec3 cameraDirection = normalize(v_cameraDirection);
+    vec3 lightDirection = normalize(u_lightDirection);
+    vec3 normalVector = normalize(v_normalVector);
+    
+    // Ambient
+    vec3 lightColor = u_ambientColor;
+
+    // Diffuse
+    float diffuseIntensity = max(0.0, dot(normalVector, lightDirection));
+    lightColor += (u_lightColor * diffuseIntensity);
+
+    // Specular
+    vec3 halfVector = normalize(cameraDirection + lightDirection);
+    float specularIntensity = pow(max(0.0, dot(normalVector, halfVector)), u_specularExponent);
+
+    // Light the pixel.
+    gl_FragColor = u_diffuseColor;
+    gl_FragColor.rgb *= lightColor;
+    gl_FragColor.rgb += (u_diffuseColor.rgb * specularIntensity);
+}

+ 27 - 0
gameplay-resources/res/shaders/colored-specular.vsh

@@ -0,0 +1,27 @@
+// Uniforms
+uniform mat4 u_worldViewProjectionMatrix;            // Matrix to transform a position to clip space.
+uniform mat4 u_inverseTransposeWorldViewMatrix;        // Matrix to transform a normal to view space.
+uniform mat4 u_worldMatrix;                            // Matrix to tranform a position to world space.
+uniform vec3 u_cameraPosition;                        // Position of the camera.
+
+// Inputs
+attribute vec4 a_position;                            // Vertex Position (x, y, z, w)
+attribute vec3 a_normal;                            // Vertex Normal (x, y, z)
+
+// Outputs
+varying vec3 v_normalVector;                        // NormalVector in view space.
+varying vec3 v_cameraDirection;                     // Camera direction
+
+void main()
+{
+    // Transform position to clip space.
+    gl_Position = u_worldViewProjectionMatrix * a_position;
+
+    // Transform normal to view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix);
+    v_normalVector = inverseTransposeWorldViewMatrix * a_normal;
+
+    // Compute the camera direction.
+    vec4 positionWorldSpace = u_worldMatrix * a_position;
+    v_cameraDirection = u_cameraPosition - positionWorldSpace.xyz;
+}

+ 24 - 0
gameplay-resources/res/shaders/colored.fsh

@@ -0,0 +1,24 @@
+precision highp float;
+
+// Uniforms
+uniform vec3 u_lightDirection;        // Direction of the light
+uniform vec3 u_lightColor;            // RGB color of the light
+uniform vec3 u_ambientColor;        // Ambient color
+uniform vec4 u_diffuseColor;        // Diffuse color
+
+// Inputs
+varying vec3 v_normalVector;        // NormalVector in view space (Normalized).
+
+void main()
+{
+    // Ambient
+    vec3 lightColor = u_ambientColor;
+
+    // Diffuse
+    float diffuseIntensity = max(dot(v_normalVector, u_lightDirection), 0.0);
+    lightColor += (u_lightColor * diffuseIntensity);
+
+    // Light the pixel.
+    gl_FragColor = u_diffuseColor;
+    gl_FragColor.rgb *= lightColor;
+}

+ 20 - 0
gameplay-resources/res/shaders/colored.vsh

@@ -0,0 +1,20 @@
+// Uniforms
+uniform mat4 u_worldViewProjectionMatrix;            // Matrix to transform a position to clip space.
+uniform mat4 u_inverseTransposeWorldViewMatrix;     // Matrix to transform a normal to view space.
+
+// Inputs
+attribute vec4 a_position;                            // Vertex Position (x, y, z, w).
+attribute vec3 a_normal;                            // Vertex Normal (x, y, z).
+
+// Outputs
+varying vec3 v_normalVector;                        // NormalVector in view space (Normalized).
+
+void main()
+{
+    // Transform position to clip space.
+    gl_Position = u_worldViewProjectionMatrix * a_position;
+
+    // Transform normal to view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix);
+    v_normalVector = normalize(inverseTransposeWorldViewMatrix * a_normal);
+}

+ 40 - 0
gameplay-resources/res/shaders/diffuse-specular.fsh

@@ -0,0 +1,40 @@
+precision highp float;
+
+// Uniforms
+uniform vec3 u_lightDirection;            // Direction of the light
+uniform vec3 u_lightColor;                // RGB color of the light
+uniform vec3 u_ambientColor;            // Ambient color
+uniform float u_specularExponent;        // Specular exponent or shininess property.
+uniform sampler2D u_diffuseTexture;        // Diffuse texture.
+
+// Inputs
+varying vec3 v_normalVector;            // NormalVector in view space.
+varying vec2 v_texCoord;                // Texture coordinate (u, v).
+varying vec3 v_cameraDirection;         // Camera direction
+
+void main()
+{
+    // Fetch diffuse color from texture.
+    vec4 diffuseColor = texture2D(u_diffuseTexture, v_texCoord);
+    
+    // Normalize the vectors.
+    vec3 cameraDirection = normalize(v_cameraDirection);
+    vec3 lightDirection = normalize(u_lightDirection);
+    vec3 normalVector = normalize(v_normalVector);
+    
+    // Ambient
+    vec3 lightColor = u_ambientColor;
+
+    // Diffuse
+    float diffuseIntensity = max(0.0, dot(normalVector, lightDirection));
+    lightColor += (u_lightColor * diffuseIntensity);
+
+    // Specular
+    vec3 halfVector = normalize(cameraDirection + lightDirection);
+    float specularIntensity = pow(max(0.0, dot(normalVector, halfVector)), u_specularExponent);
+
+    // Light the pixel
+    gl_FragColor = diffuseColor;
+    gl_FragColor.rgb *= lightColor;
+    gl_FragColor.rgb += vec3(diffuseColor.rgb * specularIntensity);
+}

+ 32 - 0
gameplay-resources/res/shaders/diffuse-specular.vsh

@@ -0,0 +1,32 @@
+// Uniforms
+uniform mat4 u_worldViewProjectionMatrix;            // Matrix to transform a position to clip space.
+uniform mat4 u_inverseTransposeWorldViewMatrix;        // Matrix to transform a normal to view space.
+uniform mat4 u_worldMatrix;                            // Matrix to tranform a position to world space.
+uniform vec3 u_cameraPosition;                        // Position of the camera.
+
+// Inputs
+attribute vec4 a_position;                            // Vertex Position (x, y, z, w)
+attribute vec3 a_normal;                            // Vertex Normal (x, y, z)
+attribute vec2 a_texCoord;                            // Vertex Texture Coordinate (u, v)
+
+// Outputs
+varying vec3 v_normalVector;                        // NormalVector in view space.
+varying vec2 v_texCoord;                            // Texture coordinate (u, v).
+varying vec3 v_cameraDirection;                     // Camera direction
+
+void main()
+{
+    // Transform position to clip space.
+    gl_Position = u_worldViewProjectionMatrix * a_position;
+
+    // Transform normal to view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix);
+    v_normalVector = inverseTransposeWorldViewMatrix * a_normal;
+
+    // Compute the camera direction.
+    vec4 positionWorldSpace = u_worldMatrix * a_position;
+    v_cameraDirection = u_cameraPosition - positionWorldSpace.xyz;
+
+    // Pass on the texture coordinates to Fragment shader.
+    v_texCoord = a_texCoord;
+}

+ 25 - 0
gameplay-resources/res/shaders/diffuse.fsh

@@ -0,0 +1,25 @@
+precision highp float;
+
+// Uniforms
+uniform vec3 u_lightDirection;              // Direction of the light
+uniform vec3 u_lightColor;                  // RGB color of the light
+uniform vec3 u_ambientColor;                // Ambient color
+uniform sampler2D u_diffuseTexture;         // Diffuse texture
+
+// Inputs
+varying vec3 v_normalVector;                // NormalVector in view space (Normalized).
+varying vec2 v_texCoord;                    // Texture coordinate (u, v).
+
+void main()
+{
+    // Ambient
+    vec3 lightColor = u_ambientColor;
+
+    // Diffuse
+    float diffuseIntensity = max(dot(v_normalVector, u_lightDirection), 0.0);
+    lightColor += (u_lightColor * diffuseIntensity);
+
+    // Light the pixel.
+    gl_FragColor = texture2D(u_diffuseTexture, v_texCoord);
+    gl_FragColor.rgb *= lightColor;
+}

+ 25 - 0
gameplay-resources/res/shaders/diffuse.vsh

@@ -0,0 +1,25 @@
+// Uniforms
+uniform mat4 u_worldViewProjectionMatrix;            // Matrix to transform a position to clip space.
+uniform mat4 u_inverseTransposeWorldViewMatrix;        // Matrix to transform a normal to view space.
+
+// Inputs
+attribute vec4 a_position;                            // Vertex Position (x, y, z, w).
+attribute vec3 a_normal;                            // Vertex Normal (x, y, z).
+attribute vec2 a_texCoord;                            // Vertex Texture Coordinate (u, v).
+
+// Outputs
+varying vec3 v_normalVector;                        // NormalVector in view space (Normalized).
+varying vec2 v_texCoord;                            // Texture coordinate (u, v).
+
+void main()
+{
+    // Transform position to clip space.
+    gl_Position = u_worldViewProjectionMatrix * a_position;
+
+    // Transform normal to view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix);
+    v_normalVector = normalize(inverseTransposeWorldViewMatrix * a_normal);
+
+    // Pass on texture coordinate to fragment shader.
+    v_texCoord = a_texCoord;
+}

+ 56 - 0
gameplay-resources/res/shaders/parallax-specular.fsh

@@ -0,0 +1,56 @@
+precision highp float;
+
+// Uniforms
+uniform vec3 u_lightColor;                        // RGB color of the light
+uniform vec3 u_ambientColor;                    // Ambient color
+uniform float u_specularExponent;                // Specular exponent or shininess property.
+uniform float u_parallaxHeight;                    // Parallax height
+uniform sampler2D u_diffuseTexture;                // Diffuse texture
+uniform sampler2D u_bumpMapTexture;                // Height map texture
+uniform sampler2D u_normalMapTexture;            // Normal map texture    
+
+// Inputs
+varying vec2 v_texCoord;                        // Texture coordinates (u,v)
+varying vec3 v_lightDirectionTangentSpace;        // Light direction in tangent space
+varying vec3 v_cameraDirectionTangentSpace;        // Camera direction in tangent space
+
+void main()
+{
+    // Normalize vectors.
+    vec3 lightDirection = normalize(v_lightDirectionTangentSpace);
+    vec3 cameraDirection = normalize(v_cameraDirectionTangentSpace);
+    
+    // Get the height in the bumpmap texture.
+    float height = texture2D(u_bumpMapTexture, v_texCoord).r;
+    
+    // Compute offset within the range (-parallax height to +parallax height).
+    float offset = u_parallaxHeight * (2.0 * height - 1.0);
+    vec2 parallaxTexCoord = v_texCoord + (cameraDirection.xy * offset);
+
+    // Compute offset again with the new texture coordinates to get better precision.
+    // Find out the mean height.
+    height += texture2D(u_bumpMapTexture, parallaxTexCoord).r;
+    offset = u_parallaxHeight * (height - 1.0);
+    parallaxTexCoord = v_texCoord + (cameraDirection.xy * offset);
+    
+    // Fetch diffuse color from texture.
+    vec4 diffuseColor = texture2D(u_diffuseTexture, parallaxTexCoord);
+    
+    // Ambient
+    vec3 lightColor = u_ambientColor;
+
+    // Diffuse
+    // Fetch normals from the normal map with the modified texture coordinates.
+    vec3 normalVector = normalize(texture2D(u_normalMapTexture, parallaxTexCoord).xyz * 2.0 - 1.0);
+    float diffuseIntensity = max(0.0, dot(normalVector, lightDirection));
+    lightColor += (u_lightColor * diffuseIntensity);
+
+    // Specular
+    vec3 halfVector = normalize(cameraDirection + lightDirection);
+    float specularIntensity = pow(max(0.0, dot(normalVector, halfVector)), u_specularExponent);
+
+    // Light the pixel.
+    gl_FragColor = diffuseColor;
+    gl_FragColor.rgb *= lightColor;
+    gl_FragColor.rgb += vec3(diffuseColor.rgb * specularIntensity);
+}

+ 43 - 0
gameplay-resources/res/shaders/parallax-specular.vsh

@@ -0,0 +1,43 @@
+// Uniforms
+uniform    vec3 u_lightDirection;                        // Direction of the light
+uniform mat4 u_worldViewProjectionMatrix;            // Matrix to transform a position to clip space.
+uniform mat4 u_inverseTransposeWorldViewMatrix;        // Matrix to transform a normal to view space.
+uniform mat4 u_worldMatrix;                            // Matrix to tranform a position to world space.
+uniform vec3 u_cameraPosition;                        // Position of the camera.
+
+// Inputs
+attribute vec4 a_position;                            // Vertex Position (x, y, z, w)
+attribute vec3 a_normal;                            // Vertex Normal (x, y, z)
+attribute vec2 a_texCoord;                            // Vertex Texture Coordinate (u, v)
+attribute vec3 a_tangent;                            // Vertex Tangent (x, y, z)
+attribute vec3 a_binormal;                            // Vertex Binormal (actually Bi-tangent) (x, y, z)
+
+// Outputs
+varying vec2 v_texCoord;                            // Texture Coordinate (u,v)
+varying vec3 v_lightDirectionTangentSpace;            // Direction of the light in tangent space.
+varying vec3 v_cameraDirectionTangentSpace;         // Direction the camera is looking at in tangent space.
+
+void main()
+{
+    // Transform position to clip space.
+    gl_Position = u_worldViewProjectionMatrix * a_position;
+
+    // Transform the normal, tangent and binormals to  view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix);
+    vec3 tangentVector  = inverseTransposeWorldViewMatrix * a_tangent;
+    vec3 normalVector = inverseTransposeWorldViewMatrix * a_normal;
+    vec3 binormalVector = inverseTransposeWorldViewMatrix * a_binormal;
+
+    // Create a transform to convert a vector to tangent space.
+    mat3 tangentSpaceTransformMatrix = mat3(tangentVector, binormalVector, normalVector);
+
+    // Transform light direction to tangent space.
+    v_lightDirectionTangentSpace = tangentSpaceTransformMatrix * u_lightDirection;
+
+    // Compute camera direction and transform it to tangent space.
+    vec4 positionWorldSpace = u_worldMatrix * a_position;
+    v_cameraDirectionTangentSpace = tangentSpaceTransformMatrix * (u_cameraPosition - positionWorldSpace.xyz);
+
+    // Pass on the texture coordinates to Fragment shader.
+    v_texCoord = a_texCoord;
+}

+ 49 - 0
gameplay-resources/res/shaders/parallax.fsh

@@ -0,0 +1,49 @@
+precision highp float;
+
+// Uniforms
+uniform vec3 u_lightColor;                        // RGB color of the light
+uniform vec3 u_ambientColor;                    // Ambient color
+uniform sampler2D u_diffuseTexture;                // Diffuse texture
+uniform sampler2D u_bumpMapTexture;                // Height map texture
+uniform sampler2D u_normalMapTexture;            // Normal map texture    
+uniform float u_parallaxHeight;                    // Parallax height
+
+// Inputs
+varying vec2 v_texCoord;                        // Texture coordinates (u,v)
+varying vec3 v_lightDirectionTangentSpace;        // Light direction in tangent space
+varying vec3 v_cameraDirectionTangentSpace;        // Camera direction in tangent space
+
+void main()
+{
+    // Normalize vectors.
+    vec3 lightDirection = normalize(v_lightDirectionTangentSpace);
+    vec3 cameraDirection = normalize(v_cameraDirectionTangentSpace);
+    
+    // Get the height in the bumpmap texture.
+    float height = texture2D(u_bumpMapTexture, v_texCoord).r;
+    
+    // Compute offset within the range (-parallax height to +parallax height).
+    float offset = u_parallaxHeight * (2.0 * height - 1.0);
+    vec2 parallaxTexCoord = v_texCoord + (cameraDirection.xy * offset);
+
+    // Compute offset again with the new texture coordinates to get better precision.
+    // Find out the mean height.
+    height += texture2D(u_bumpMapTexture, parallaxTexCoord).r;
+    offset = u_parallaxHeight * (height - 1.0);
+    parallaxTexCoord = v_texCoord + ( cameraDirection.xy * offset);
+    
+    // Fetch diffuse color from texture.
+    gl_FragColor = texture2D(u_diffuseTexture, parallaxTexCoord);
+    
+    // Ambient
+    vec3 lightColor = u_ambientColor;
+
+    // Diffuse
+    // Fetch normals from the normal map with the modified texture coordinates.
+    vec3 normalVector = normalize(texture2D(u_normalMapTexture, parallaxTexCoord).xyz * 2.0 - 1.0);
+    float diffuseIntensity = max(0.0, dot(normalVector, lightDirection));
+    lightColor += (u_lightColor * diffuseIntensity);
+
+    // Light the pixel.
+    gl_FragColor.rgb *= lightColor;
+}

+ 43 - 0
gameplay-resources/res/shaders/parallax.vsh

@@ -0,0 +1,43 @@
+// Uniforms
+uniform    vec3 u_lightDirection;                        // Direction of the light
+uniform mat4 u_worldViewProjectionMatrix;            // Matrix to transform a position to clip space.
+uniform mat4 u_inverseTransposeWorldViewMatrix;        // Matrix to transform a normal to view space.
+uniform mat4 u_worldMatrix;                            // Matrix to tranform a position to world space.
+uniform vec3 u_cameraPosition;                        // Position of the camera.
+
+// Inputs
+attribute vec4 a_position;                            // Vertex Position (x, y, z, w)
+attribute vec3 a_normal;                            // Vertex Normal (x, y, z)
+attribute vec2 a_texCoord;                            // Vertex Texture Coordinate (u, v)
+attribute vec3 a_tangent;                            // Vertex Tangent (x, y, z)
+attribute vec3 a_binormal;                            // Vertex Binormal (actually Bi-tangent) (x, y, z)
+
+// Outputs
+varying vec2 v_texCoord;                            // Texture Coordinate (u,v)
+varying vec3 v_lightDirectionTangentSpace;            // Direction of the light in tangent space.
+varying vec3 v_cameraDirectionTangentSpace;         // Direction the camera is looking at in tangent space.
+
+void main()
+{
+    // Transform position to clip space.
+    gl_Position = u_worldViewProjectionMatrix * a_position;
+
+    // Transform the normal, tangent and binormals to  view space.
+    mat3 inverseTransposeWorldViewMatrix = mat3(u_inverseTransposeWorldViewMatrix);
+    vec3 tangentVector  = inverseTransposeWorldViewMatrix * a_tangent;
+    vec3 normalVector = inverseTransposeWorldViewMatrix * a_normal;
+    vec3 binormalVector = inverseTransposeWorldViewMatrix * a_binormal;
+
+    // Create a transform to convert a vector to tangent space.
+    mat3 tangentSpaceTransformMatrix = mat3(tangentVector, binormalVector, normalVector);
+
+    // Transform light direction to tangent space.
+    v_lightDirectionTangentSpace = tangentSpaceTransformMatrix * u_lightDirection;
+
+    // Compute camera direction and transform it to tangent space.
+    vec4 positionWorldSpace = u_worldMatrix * a_position;
+    v_cameraDirectionTangentSpace = tangentSpaceTransformMatrix * (u_cameraPosition - positionWorldSpace.xyz);
+
+    // Pass on the texture coordinates to Fragment shader.
+    v_texCoord = a_texCoord;
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно