Przeglądaj źródła

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

seanpaultaylor 12 lat temu
rodzic
commit
1ad73e2fd4
78 zmienionych plików z 3276 dodań i 1379 usunięć
  1. 30 30
      gameplay.sln
  2. 1 1
      gameplay/gameplay.vcxproj
  3. 12 1
      gameplay/src/AIStateMachine.cpp
  4. 1 0
      gameplay/src/AudioBuffer.cpp
  5. 4 4
      gameplay/src/AudioSource.cpp
  6. 32 2
      gameplay/src/Bundle.cpp
  7. 8 0
      gameplay/src/Bundle.h
  8. 1 5
      gameplay/src/Container.cpp
  9. 2 2
      gameplay/src/Control.cpp
  10. 44 0
      gameplay/src/FileSystem.cpp
  11. 17 0
      gameplay/src/FileSystem.h
  12. 3 5
      gameplay/src/Form.cpp
  13. 1 3
      gameplay/src/Form.h
  14. 1 1
      gameplay/src/Game.cpp
  15. 1 3
      gameplay/src/Game.h
  16. 6 2
      gameplay/src/Gamepad.cpp
  17. 3 3
      gameplay/src/ImageControl.cpp
  18. 3 3
      gameplay/src/Material.cpp
  19. 5 0
      gameplay/src/MaterialParameter.cpp
  20. 14 4
      gameplay/src/MaterialParameter.h
  21. 7 1
      gameplay/src/Node.cpp
  22. 3 3
      gameplay/src/ParticleEmitter.cpp
  23. 5 4
      gameplay/src/Pass.cpp
  24. 13 5
      gameplay/src/Platform.cpp
  25. 74 6
      gameplay/src/Properties.cpp
  26. 27 4
      gameplay/src/Properties.h
  27. 154 68
      gameplay/src/RenderState.cpp
  28. 15 21
      gameplay/src/RenderState.h
  29. 4 2
      gameplay/src/SceneLoader.cpp
  30. 26 9
      gameplay/src/ScriptController.cpp
  31. 1 0
      gameplay/src/Technique.cpp
  32. 25 17
      gameplay/src/Terrain.cpp
  33. 1 33
      gameplay/src/lua/lua_Game.cpp
  34. 2 0
      gameplay/src/lua/lua_Global.cpp
  35. 76 0
      gameplay/src/lua/lua_ImageControl.cpp
  36. 2 0
      gameplay/src/lua/lua_ImageControl.h
  37. 57 2
      install.bat
  38. 1 1
      install.sh
  39. 1 0
      newproject.sh
  40. 1 0
      samples/browser/res/common/forms/formFlowLayout.form
  41. 1 0
      samples/browser/res/common/forms/formScrolling.form
  42. 1 0
      samples/browser/res/common/forms/formSelect.form
  43. 1 0
      samples/browser/res/common/forms/formVerticalLayout.form
  44. 2 1
      samples/browser/res/common/forms/formZOrder.form
  45. 44 34
      samples/browser/src/FormsSample.cpp
  46. 1 1
      samples/browser/src/FormsSample.h
  47. 1 1
      samples/character/src/CharacterGame.cpp
  48. 4 1
      samples/particles/res/editor.form
  49. 4 1
      samples/racer/src/RacerGame.cpp
  50. 60 25
      template/template-CMakeLists.txt
  51. 6 0
      tools/encoder/CMakeLists.txt
  52. 8 0
      tools/encoder/gameplay-encoder.vcxproj
  53. 24 0
      tools/encoder/gameplay-encoder.vcxproj.filters
  54. 31 0
      tools/encoder/src/Constants.cpp
  55. 33 0
      tools/encoder/src/Constants.h
  56. 14 1
      tools/encoder/src/EncoderArguments.cpp
  57. 2 0
      tools/encoder/src/EncoderArguments.h
  58. 503 978
      tools/encoder/src/FBXSceneEncoder.cpp
  59. 79 0
      tools/encoder/src/FBXSceneEncoder.h
  60. 809 0
      tools/encoder/src/FBXUtil.cpp
  61. 206 0
      tools/encoder/src/FBXUtil.h
  62. 8 0
      tools/encoder/src/FileIO.cpp
  63. 5 0
      tools/encoder/src/FileIO.h
  64. 5 0
      tools/encoder/src/GPBFile.cpp
  65. 5 0
      tools/encoder/src/GPBFile.h
  66. 325 16
      tools/encoder/src/Material.cpp
  67. 74 11
      tools/encoder/src/Material.h
  68. 11 1
      tools/encoder/src/Mesh.cpp
  69. 3 0
      tools/encoder/src/Mesh.h
  70. 5 0
      tools/encoder/src/MeshSkin.cpp
  71. 2 0
      tools/encoder/src/MeshSkin.h
  72. 55 5
      tools/encoder/src/Model.cpp
  73. 3 1
      tools/encoder/src/Model.h
  74. 1 1
      tools/encoder/src/Object.h
  75. 82 0
      tools/encoder/src/Sampler.cpp
  76. 50 0
      tools/encoder/src/Sampler.h
  77. 2 2
      tools/encoder/src/TTFFontEncoder.cpp
  78. 122 54
      tools/luagen/gameplay-luagen.doxyfile

+ 30 - 30
gameplay.sln

@@ -149,6 +149,36 @@ Global
 		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|Win32.Build.0 = Release|Win32
 		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|x64.ActiveCfg = Release|x64
 		{D672DC66-3CE0-4878-B0D2-813CA731012F}.Release|x64.Build.0 = Release|x64
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|Win32.ActiveCfg = Debug|Win32
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|Win32.Build.0 = Debug|Win32
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|x64.ActiveCfg = Debug|x64
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|x64.Build.0 = Debug|x64
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|x64.ActiveCfg = DebugMem|x64
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|x64.Build.0 = DebugMem|x64
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerry.Build.0 = Release|BlackBerry
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|Win32.ActiveCfg = Release|Win32
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|Win32.Build.0 = Release|Win32
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|x64.ActiveCfg = Release|x64
+		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|x64.Build.0 = Release|x64
 		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
 		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
 		{9A515C8B-3320-4C5C-9754-211E91206C9D}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry
@@ -269,36 +299,6 @@ Global
 		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|Win32.Build.0 = Release|Win32
 		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|x64.ActiveCfg = Release|x64
 		{CB5ABFAA-EA69-E439-5A4D-3B9359916C71}.Release|x64.Build.0 = Release|x64
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerrySimulator.ActiveCfg = Debug|BlackBerrySimulator
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerrySimulator.Build.0 = Debug|BlackBerrySimulator
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|BlackBerrySimulator.Deploy.0 = Debug|BlackBerrySimulator
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|Win32.ActiveCfg = Debug|Win32
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|Win32.Build.0 = Debug|Win32
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|x64.ActiveCfg = Debug|x64
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Debug|x64.Build.0 = Debug|x64
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerry.ActiveCfg = DebugMem|BlackBerry
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerry.Build.0 = DebugMem|BlackBerry
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerry.Deploy.0 = DebugMem|BlackBerry
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerrySimulator.ActiveCfg = DebugMem|BlackBerrySimulator
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerrySimulator.Build.0 = DebugMem|BlackBerrySimulator
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|BlackBerrySimulator.Deploy.0 = DebugMem|BlackBerrySimulator
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|Win32.Build.0 = DebugMem|Win32
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|x64.ActiveCfg = DebugMem|x64
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.DebugMem|x64.Build.0 = DebugMem|x64
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerry.ActiveCfg = Release|BlackBerry
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerry.Build.0 = Release|BlackBerry
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerry.Deploy.0 = Release|BlackBerry
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerrySimulator.ActiveCfg = Release|BlackBerrySimulator
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerrySimulator.Build.0 = Release|BlackBerrySimulator
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|BlackBerrySimulator.Deploy.0 = Release|BlackBerrySimulator
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|Win32.ActiveCfg = Release|Win32
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|Win32.Build.0 = Release|Win32
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|x64.ActiveCfg = Release|x64
-		{04EAF3E5-0F9E-AF4D-53F9-269CE114211F}.Release|x64.Build.0 = Release|x64
 		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|BlackBerry.ActiveCfg = Debug|BlackBerry
 		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|BlackBerry.Build.0 = Debug|BlackBerry
 		{82522888-E09A-ED48-AD7D-247237B37B3A}.Debug|BlackBerry.Deploy.0 = Debug|BlackBerry

+ 1 - 1
gameplay/gameplay.vcxproj

@@ -900,7 +900,7 @@
       </PrecompiledHeader>
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>__BB10__;_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>USE_BLACKBERRY_GAMEPAD;_ITERATOR_DEBUG_LEVEL=0;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)src;..\external-deps\lua\include;..\external-deps\bullet\include;..\external-deps\openal\include\AL;..\external-deps\alut\include\AL;..\external-deps\oggvorbis\include;..\external-deps\glew\include;..\external-deps\libpng\include;..\external-deps\zlib\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>
       </RuntimeTypeInfo>

+ 12 - 1
gameplay/src/AIStateMachine.cpp

@@ -25,7 +25,18 @@ AIStateMachine::~AIStateMachine()
     {
         (*itr)->release();
     }
-    SAFE_RELEASE(AIState::_empty);
+
+    if (AIState::_empty)
+    {
+        if (AIState::_empty->getRefCount() == 1)
+        {
+            SAFE_RELEASE(AIState::_empty);
+        }
+        else
+        {
+            AIState::_empty->release();
+        }
+    }
 }
 
 AIAgent* AIStateMachine::getAgent() const

+ 1 - 0
gameplay/src/AudioBuffer.cpp

@@ -327,6 +327,7 @@ bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
             }
         }
     }
+    return false;
 }
 
 bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)

+ 4 - 4
gameplay/src/AudioSource.cpp

@@ -77,18 +77,18 @@ AudioSource* AudioSource::create(Properties* properties)
         return NULL;
     }
 
-    const char* path = properties->getString("path");
-    if (path == NULL)
+    std::string path;
+    if (!properties->getPath("path", &path))
     {
         GP_ERROR("Audio file failed to load; the file path was not specified.");
         return NULL;
     }
 
     // Create the audio source.
-    AudioSource* audio = AudioSource::create(path);
+    AudioSource* audio = AudioSource::create(path.c_str());
     if (audio == NULL)
     {
-        GP_ERROR("Audio file '%s' failed to load properly.", path);
+        GP_ERROR("Audio file '%s' failed to load properly.", path.c_str());
         return NULL;
     }
 

+ 32 - 2
gameplay/src/Bundle.cpp

@@ -286,6 +286,24 @@ const char* Bundle::getIdFromOffset(unsigned int offset) const
     return NULL;
 }
 
+const std::string& Bundle::getMaterialPath()
+{
+    if (_materialPath.empty())
+    {
+        int pos = _path.find_last_of('.');
+        if (pos > 2)
+        {
+            _materialPath = _path.substr(0, pos);
+            _materialPath.append(".material");
+            if (!FileSystem::fileExists(_materialPath.c_str()))
+            {
+                _materialPath.clear();
+            }
+        }
+    }
+    return _materialPath;
+}
+
 Bundle::Reference* Bundle::seekTo(const char* id, unsigned int type)
 {
     Reference* ref = find(id);
@@ -968,8 +986,20 @@ Model* Bundle::readModel(const char* nodeId)
             }
             if (materialCount > 0)
             {
-                // TODO: Material loading not supported yet.
-                GP_WARN("Material loading is not yet supported.");
+                for (unsigned int i = 0; i < materialCount; ++i)
+                {
+                    std::string materialName = readString(_stream);
+                    std::string materialPath = getMaterialPath();
+                    materialPath.append("#");
+                    materialPath.append(materialName);
+                    Material* material = Material::create(materialPath.c_str());
+                    if (material)
+                    {
+                        int partIndex = model->getMesh()->getPartCount() > 0 ? i : -1;
+                        model->setMaterial(material, partIndex);
+                        SAFE_RELEASE(material);
+                    }
+                }
             }
             return model;
         }

+ 8 - 0
gameplay/src/Bundle.h

@@ -189,6 +189,13 @@ private:
      */
     const char* getIdFromOffset(unsigned int offset) const;
 
+    /**
+     * Gets the path to the bundle's default material file, if it exists.
+     * 
+     * @return The bundle's default material path. Returns an empty string if the default material does not exist.
+     */
+    const std::string& getMaterialPath();
+
     /**
      * Seeks the file pointer to the object with the given ID and type
      * and returns the relevant Reference.
@@ -427,6 +434,7 @@ private:
     bool skipNode();
 
     std::string _path;
+    std::string _materialPath;
     unsigned int _referenceCount;
     Reference* _references;
     Stream* _stream;

+ 1 - 5
gameplay/src/Container.cpp

@@ -1643,11 +1643,7 @@ bool Container::pointerEvent(bool mouse, char evt, int x, int y, int data)
     }
 
     release();
-    if (x > _clipBounds.x && x <= _clipBounds.x + _clipBounds.width &&
-        y > _clipBounds.y && y <= _clipBounds.y + _clipBounds.height)
-        return (_consumeInputEvents | eventConsumed);
-    else
-        return eventConsumed;
+    return (_consumeInputEvents | eventConsumed);
 }
 
 Container::Scroll Container::getScroll(const char* scroll)

+ 2 - 2
gameplay/src/Control.cpp

@@ -7,7 +7,7 @@ namespace gameplay
 
 Control::Control()
     : _id(""), _state(Control::NORMAL), _bounds(Rectangle::empty()), _clipBounds(Rectangle::empty()), _viewportClipBounds(Rectangle::empty()),
-    _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(true), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
+    _clearBounds(Rectangle::empty()), _dirty(true), _consumeInputEvents(false), _alignment(ALIGN_TOP_LEFT), _isAlignmentSet(false), _autoWidth(false), _autoHeight(false), _listeners(NULL), _visible(true),
     _zIndex(-1), _contactIndex(INVALID_CONTACT_INDEX), _focusIndex(-1), _parent(NULL), _styleOverridden(false), _skin(NULL)
 {
     addScriptEvent("controlEvent", "<Control>[Control::Listener::EventType]");
@@ -41,7 +41,7 @@ void Control::initialize(Theme::Style* style, Properties* properties)
     _autoWidth = properties->getBool("autoWidth");
     _autoHeight = properties->getBool("autoHeight");
 
-    _consumeInputEvents = properties->getBool("consumeInputEvents", true);
+    _consumeInputEvents = properties->getBool("consumeInputEvents", false);
 
     _visible = properties->getBool("visible", true);
 

+ 44 - 0
gameplay/src/FileSystem.cpp

@@ -10,9 +10,12 @@
     #include <windows.h>
     #include <tchar.h>
     #include <stdio.h>
+    #include <direct.h>
     #define gp_stat _stat
     #define gp_stat_struct struct stat
 #else
+    #define __EXT_POSIX2
+    #include <libgen.h>
     #include <dirent.h>
     #define gp_stat stat
     #define gp_stat_struct struct stat
@@ -560,6 +563,47 @@ void FileSystem::createFileFromAsset(const char* path)
 #endif
 }
 
+std::string FileSystem::dirname(const char* path)
+{
+    if (path == NULL || strlen(path) == 0)
+    {
+        return "";
+    }
+#ifdef WIN32
+    char drive[_MAX_DRIVE];
+    char dir[_MAX_DIR];
+    _splitpath(path, drive, dir, NULL, NULL);
+    std::string dirname;
+    size_t driveLength = strlen(drive);
+    if (driveLength > 0)
+    {
+        dirname.reserve(driveLength + strlen(dir));
+        dirname.append(drive);
+        dirname.append(dir);
+    }
+    else
+    {
+        dirname.assign(dir);
+    }
+    std::replace(dirname.begin(), dirname.end(), '\\', '/');
+    return dirname;
+#else
+    // dirname() modifies the input string so create a temp string
+    std::string dirname;
+    char* tempPath = new char[strlen(path)];
+    strcpy(tempPath, path);
+    char* dir = ::dirname(tempPath);
+    if (dir && strlen(dir) > 0)
+    {
+        dirname.assign(dir);
+        // dirname() strips off the trailing '/' so add it back to be consistent with Windows
+        dirname.append("/");
+    }
+    SAFE_DELETE_ARRAY(tempPath);
+    return dirname;
+#endif
+}
+
 std::string FileSystem::getExtension(const char* path)
 {
     const char* str = strrchr(path, '.');

+ 17 - 0
gameplay/src/FileSystem.h

@@ -2,6 +2,7 @@
 #define FILESYSTEM_H_
 
 #include "Stream.h"
+#include <string>
 
 namespace gameplay
 {
@@ -181,6 +182,22 @@ public:
      */
     static void createFileFromAsset(const char* path);
 
+    /**
+     * Returns the directory name up to and including the trailing '/'.
+     * 
+     * This is a lexical method so it does not verify that the directory exists.
+     * Back slashes will be converted to forward slashes.
+     * 
+     * - "res/image.png" will return "res/"
+     * - "image.png" will return ""
+     * - "c:\foo\bar\image.png" will return "c:/foo/bar/"
+     * 
+     * @param The file path. May be relative or absolute, forward or back slashes. May be NULL.
+     * 
+     * @return The directory name with the trailing '/'. Returns "" if path is NULL or the path does not contain a directory.
+     */
+    static std::string dirname(const char* path);
+
     /**
      * Returns the extension of the given file path.
      *

+ 3 - 5
gameplay/src/Form.cpp

@@ -151,7 +151,7 @@ Form* Form::create(const char* url)
     }
     form->initialize(style, formProperties);
 
-    form->_consumeInputEvents = formProperties->getBool("consumeInputEvents", true);
+    form->_consumeInputEvents = formProperties->getBool("consumeInputEvents", false);
 
     // Alignment
     if ((form->_alignment & Control::ALIGN_BOTTOM) == Control::ALIGN_BOTTOM)
@@ -722,7 +722,7 @@ bool Form::mouseEventInternal(Mouse::MouseEvent evt, int x, int y, int wheelDelt
     return false;
 }
 
-bool Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+void Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
     for (size_t i = 0; i < __forms.size(); ++i)
     {
@@ -732,11 +732,9 @@ bool Form::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, uns
         if (form->isEnabled() && form->isVisible() && form->getState() == FOCUS)
         {
             if (form->gamepadEvent(evt, gamepad, analogIndex))
-                return true;
+                return;
         }
     }
-
-    return false;
 }
 
 bool Form::projectPoint(int x, int y, Vector3* point)

+ 1 - 3
gameplay/src/Form.h

@@ -222,11 +222,9 @@ private:
     /**
      * Propagate gamepad events to enabled forms.
      *
-     * @return True if the gamepad event is consumed, false otherwise.
-     *
      * @see Control::gamepadEvent
      */
-    static bool gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
+    static void gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex);
 
     /**
      * Get the next highest power of two of an integer.  Used when creating framebuffers.

+ 1 - 1
gameplay/src/Game.cpp

@@ -509,7 +509,7 @@ void Game::gestureTapEvent(int x, int y)
 {
 }
 
-void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+void Game::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
 {
 }
 

+ 1 - 3
gameplay/src/Game.h

@@ -445,13 +445,11 @@ public:
     /**
      * Gamepad callback on gamepad events.  Override to receive Gamepad::CONNECTED_EVENT 
      * and Gamepad::DISCONNECTED_EVENT, and store the Gamepad* in order to poll it from update().
-     * Or, handle all gamepad input through BUTTON, JOYSTICK and TRIGGER events.
      *
      * @param evt The gamepad event that occurred.
      * @param gamepad The gamepad that generated the event.
-     * @param analogIndex If this is a JOYSTICK_EVENT or TRIGGER_EVENT, the index of the joystick or trigger whose value changed.
      */
-    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
+    virtual void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
 
     /**
      * Gets the current number of gamepads currently connected to the system.

+ 6 - 2
gameplay/src/Gamepad.cpp

@@ -140,12 +140,14 @@ void Gamepad::bindGamepadControls(Container* container)
         else if (std::strcmp("joystick", control->getType()) == 0)
         {
             Joystick* joystick = (Joystick*)control;
+            joystick->setConsumeInputEvents(true);
             _uiJoysticks[joystick->getIndex()] = joystick;
             _joystickCount++;
         }
         else if (std::strcmp("button", control->getType()) == 0)
         {
             Button* button = (Button*)control;
+            button->setConsumeInputEvents(true);
             _uiButtons[button->getDataBinding()] = button;
             _buttonCount++;
         }
@@ -328,7 +330,8 @@ unsigned int Gamepad::getJoystickCount() const
 
 void Gamepad::getJoystickValues(unsigned int joystickId, Vector2* outValue) const
 {
-    GP_ASSERT(joystickId < _joystickCount);
+    if (joystickId >= _joystickCount)
+        return;
 
     if (_form)
     {
@@ -356,7 +359,8 @@ unsigned int Gamepad::getTriggerCount() const
 
 float Gamepad::getTriggerValue(unsigned int triggerId) const
 {
-    GP_ASSERT(triggerId < _triggerCount);
+    if (triggerId >= _triggerCount)
+        return 0.0f;
 
     if (_form)
     {

+ 3 - 3
gameplay/src/ImageControl.cpp

@@ -47,10 +47,10 @@ void ImageControl::initialize(Theme::Style* style, Properties* properties)
 
     Control::initialize(style, properties);
 
-    const char* path = properties->getString("path");
-    if (path)
+    std::string path;
+    if (properties->getPath("path", &path))
     {
-        setImage(path);
+        setImage(path.c_str());
     }
 
     if (properties->exists("srcRegion"))

+ 3 - 3
gameplay/src/Material.cpp

@@ -412,8 +412,8 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
             }
 
             // Get the texture path.
-            const char* path = ns->getString("path");
-            if (path == NULL || strlen(path) == 0)
+            std::string path;
+            if (!ns->getPath("path", &path))
             {
                 GP_ERROR("Texture sampler '%s' is missing required image file path.", name);
                 continue;
@@ -428,7 +428,7 @@ void Material::loadRenderState(RenderState* renderState, Properties* properties)
 
             // Set the sampler parameter.
             GP_ASSERT(renderState->getParameter(name));
-            Texture::Sampler* sampler = renderState->getParameter(name)->setValue(path, mipmap);
+            Texture::Sampler* sampler = renderState->getParameter(name)->setValue(path.c_str(), mipmap);
             if (sampler)
             {
                 sampler->setWrapMode(wrapS, wrapT);

+ 5 - 0
gameplay/src/MaterialParameter.cpp

@@ -668,4 +668,9 @@ void MaterialParameter::cloneInto(MaterialParameter* materialParameter) const
     this->AnimationTarget::cloneInto(materialParameter, context);
 }
 
+MaterialParameter::MethodBinding::MethodBinding(MaterialParameter* param) :
+    _parameter(param), _autoBinding(false)
+{
+}
+
 }

+ 14 - 4
gameplay/src/MaterialParameter.h

@@ -243,10 +243,19 @@ private:
      */
     class MethodBinding : public Ref
     {
+        friend class RenderState;
+
     public:
+
         virtual void setValue(Effect* effect) = 0;
 
     protected:
+
+        /**
+         * Constructor.
+         */
+        MethodBinding(MaterialParameter* param);
+
         /**
          * Destructor.
          */
@@ -256,6 +265,9 @@ private:
          * Hidden copy assignment operator.
          */
         MethodBinding& operator=(const MethodBinding&);
+
+        MaterialParameter* _parameter;
+        bool _autoBinding;
     };
 
     /**
@@ -269,7 +281,6 @@ private:
         MethodValueBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod);
         void setValue(Effect* effect);
     private:
-        MaterialParameter* _parameter;
         ClassType* _instance;
         ValueMethod _valueMethod;
 
@@ -287,7 +298,6 @@ private:
         MethodArrayBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod, CountMethod countMethod);
         void setValue(Effect* effect);
     private:
-        MaterialParameter* _parameter;
         ClassType* _instance;
         ValueMethod _valueMethod;
         CountMethod _countMethod;
@@ -363,7 +373,7 @@ void MaterialParameter::bindValue(ClassType* classInstance, ParameterType (Class
 
 template <class ClassType, class ParameterType>
 MaterialParameter::MethodValueBinding<ClassType, ParameterType>::MethodValueBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod) :
-    _parameter(param), _instance(instance), _valueMethod(valueMethod)
+    MethodBinding(param), _instance(instance), _valueMethod(valueMethod)
 {
 }
 
@@ -375,7 +385,7 @@ void MaterialParameter::MethodValueBinding<ClassType, ParameterType>::setValue(E
 
 template <class ClassType, class ParameterType>
 MaterialParameter::MethodArrayBinding<ClassType, ParameterType>::MethodArrayBinding(MaterialParameter* param, ClassType* instance, ValueMethod valueMethod, CountMethod countMethod) :
-    _parameter(param), _instance(instance), _valueMethod(valueMethod), _countMethod(countMethod)
+    MethodBinding(param), _instance(instance), _valueMethod(valueMethod), _countMethod(countMethod)
 {
 }
 

+ 7 - 1
gameplay/src/Node.cpp

@@ -980,11 +980,13 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
     Node* copy = cloneSingleNode(context);
     GP_ASSERT(copy);
 
+    // Find our current last child
     Node* lastChild = NULL;
     for (Node* child = getFirstChild(); child != NULL; child = child->getNextSibling())
     {
         lastChild = child;
     }
+
     // Loop through the nodes backwards because addChild adds the node to the front.
     for (Node* child = lastChild; child != NULL; child = child->getPreviousSibling())
     {
@@ -993,6 +995,7 @@ Node* Node::cloneRecursive(NodeCloneContext &context) const
         copy->addChild(childCopy);
         childCopy->release();
     }
+
     return copy;
 }
 
@@ -1029,7 +1032,10 @@ void Node::cloneInto(Node* node, NodeCloneContext &context) const
     }
     node->_world = _world;
     node->_bounds = _bounds;
-    node->_userData = _userData;
+
+    // Note: Do not clone _userData - we can't make any assumptions about its content and how it's managed,
+    // so it's the caller's responsibility to clone user data if needed.
+
     if (_tags)
     {
         node->_tags = new std::map<std::string, std::string>(_tags->begin(), _tags->end());

+ 3 - 3
gameplay/src/ParticleEmitter.cpp

@@ -111,8 +111,8 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
 
     // Load sprite properties.
     // Path to image file is required.
-    const char* texturePath = sprite->getString("path");
-    if (!texturePath || strlen(texturePath) == 0)
+    std::string texturePath;
+    if (!sprite->getPath("path", &texturePath))
     {
         GP_ERROR("Failed to load particle emitter: required image file path ('path') is missing.");
         return NULL;
@@ -184,7 +184,7 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
     bool orbitAcceleration = properties->getBool("orbitAcceleration");
 
     // Apply all properties to a newly created ParticleEmitter.
-    ParticleEmitter* emitter = ParticleEmitter::create(texturePath, textureBlending, particleCountMax);
+    ParticleEmitter* emitter = ParticleEmitter::create(texturePath.c_str(), textureBlending, particleCountMax);
     if (!emitter)
     {
         GP_ERROR("Failed to create particle emitter.");

+ 5 - 4
gameplay/src/Pass.cpp

@@ -54,10 +54,10 @@ void Pass::setVertexAttributeBinding(VertexAttributeBinding* binding)
     }
 }
 
- VertexAttributeBinding* Pass::getVertexAttributeBinding() const
- {
-     return _vaBinding;
- }
+VertexAttributeBinding* Pass::getVertexAttributeBinding() const
+{
+    return _vaBinding;
+}
 
 void Pass::bind()
 {
@@ -92,6 +92,7 @@ Pass* Pass::clone(Technique* technique, NodeCloneContext &context) const
     effect->addRef();
     Pass* pass = new Pass(getId(), technique, effect);
     RenderState::cloneInto(pass, context);
+    pass->_parent = technique;
     return pass;
 }
 

+ 13 - 5
gameplay/src/Platform.cpp

@@ -57,11 +57,19 @@ void Platform::resizeEventInternal(unsigned int width, unsigned int height)
 
 void Platform::gamepadEventInternal(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
-    if (!Form::gamepadEventInternal(evt, gamepad, analogIndex))
-    {
-        Game::getInstance()->gamepadEvent(evt, gamepad, analogIndex);
-        Game::getInstance()->getScriptController()->gamepadEvent(evt, gamepad, analogIndex);
-    }
+	switch(evt)
+	{
+	case Gamepad::CONNECTED_EVENT:
+	case Gamepad::DISCONNECTED_EVENT:
+		Game::getInstance()->gamepadEvent(evt, gamepad);
+        Game::getInstance()->getScriptController()->gamepadEvent(evt, gamepad);
+		break;
+	case Gamepad::BUTTON_EVENT:
+	case Gamepad::JOYSTICK_EVENT:
+	case Gamepad::TRIGGER_EVENT:
+		Form::gamepadEventInternal(evt, gamepad, analogIndex);
+		break;
+	}
 }
 
 void Platform::gamepadEventConnectedInternal(GamepadHandle handle,  unsigned int buttonCount, unsigned int joystickCount, unsigned int triggerCount,

+ 74 - 6
gameplay/src/Properties.cpp

@@ -26,12 +26,14 @@ void calculateNamespacePath(const std::string& urlString, std::string& fileStrin
 Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
 
 Properties::Properties()
+    : _dirPath(NULL), _parent(NULL)
 {
 }
 
 Properties::Properties(const Properties& copy)
-    : _namespace(copy._namespace), _id(copy._id), _parentID(copy._parentID), _properties(copy._properties)
+    : _namespace(copy._namespace), _id(copy._id), _parentID(copy._parentID), _properties(copy._properties), _dirPath(NULL), _parent(copy._parent)
 {
+    setDirectoryPath(copy._dirPath);
     _namespaces = std::vector<Properties*>();
     std::vector<Properties*>::const_iterator it;
     for (it = copy._namespaces.begin(); it < copy._namespaces.end(); ++it)
@@ -44,12 +46,14 @@ Properties::Properties(const Properties& copy)
 
 
 Properties::Properties(Stream* stream)
+    : _dirPath(NULL), _parent(NULL)
 {
     readProperties(stream);
     rewind();
 }
 
-Properties::Properties(Stream* stream, const char* name, const char* id, const char* parentID) : _namespace(name)
+Properties::Properties(Stream* stream, const char* name, const char* id, const char* parentID, Properties* parent)
+    : _namespace(name), _dirPath(NULL), _parent(parent)
 {
     if (id)
     {
@@ -104,6 +108,7 @@ Properties* Properties::create(const char* url)
         p = p->clone();
         SAFE_DELETE(properties);
     }
+    p->setDirectoryPath(FileSystem::dirname(fileString.c_str()));
     return p;
 }
 
@@ -248,7 +253,7 @@ void Properties::readProperties(Stream* stream)
                     }
 
                     // New namespace without an ID.
-                    Properties* space = new Properties(stream, name, NULL, parentID);
+                    Properties* space = new Properties(stream, name, NULL, parentID, this);
                     _namespaces.push_back(space);
 
                     // If the namespace ends on this line, seek to right after the '}' character.
@@ -290,7 +295,7 @@ void Properties::readProperties(Stream* stream)
                         }
 
                         // Create new namespace.
-                        Properties* space = new Properties(stream, name, value, parentID);
+                        Properties* space = new Properties(stream, name, value, parentID, this);
                         _namespaces.push_back(space);
 
                         // If the namespace ends on this line, seek to right after the '}' character.
@@ -311,7 +316,7 @@ void Properties::readProperties(Stream* stream)
                         if (c == '{')
                         {
                             // Create new namespace.
-                            Properties* space = new Properties(stream, name, value, parentID);
+                            Properties* space = new Properties(stream, name, value, parentID, this);
                             _namespaces.push_back(space);
                         }
                         else
@@ -339,6 +344,7 @@ void Properties::readProperties(Stream* stream)
 
 Properties::~Properties()
 {
+    SAFE_DELETE(_dirPath);
     for (size_t i = 0, count = _namespaces.size(); i < count; ++i)
     {
         SAFE_DELETE(_namespaces[i]);
@@ -961,6 +967,41 @@ bool Properties::getColor(const char* name, Vector4* out) const
     return false;
 }
 
+bool Properties::getPath(const char* name, std::string* path) const
+{
+    GP_ASSERT(name && path);
+    const char* valueString = getString(name);
+    if (valueString)
+    {
+        if (FileSystem::fileExists(valueString))
+        {
+            path->assign(valueString);
+            return true;
+        }
+        else
+        {
+            const Properties* prop = this;
+            while (prop != NULL)
+            {
+                // Search for the file path relative to the bundle file
+                const std::string* dirPath = prop->_dirPath;
+                if (dirPath != NULL && !dirPath->empty())
+                {
+                    std::string relativePath = *dirPath;
+                    relativePath.append(valueString);
+                    if (FileSystem::fileExists(relativePath.c_str()))
+                    {
+                        path->assign(relativePath);
+                        return true;
+                    }
+                }
+                prop = prop->_parent;
+            }
+        }
+    }
+    return false;
+}
+
 Properties* Properties::clone()
 {
     Properties* p = new Properties();
@@ -970,17 +1011,44 @@ Properties* Properties::clone()
     p->_parentID = _parentID;
     p->_properties = _properties;
     p->_propertiesItr = p->_properties.end();
+    p->setDirectoryPath(_dirPath);
 
     for (size_t i = 0, count = _namespaces.size(); i < count; i++)
     {
         GP_ASSERT(_namespaces[i]);
-        p->_namespaces.push_back(_namespaces[i]->clone());
+        Properties* child = _namespaces[i]->clone();
+        p->_namespaces.push_back(child);
+        child->_parent = p;
     }
     p->_namespacesItr = p->_namespaces.end();
 
     return p;
 }
 
+void Properties::setDirectoryPath(const std::string* path)
+{
+    if (path)
+    {
+        setDirectoryPath(*path);
+    }
+    else
+    {
+        SAFE_DELETE(_dirPath);
+    }
+}
+
+void Properties::setDirectoryPath(const std::string& path)
+{
+    if (_dirPath == NULL)
+    {
+        _dirPath = new std::string(path);
+    }
+    else
+    {
+        _dirPath->assign(path);
+    }
+}
+
 void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath)
 {
     // If the url references a specific namespace within the file,

+ 27 - 4
gameplay/src/Properties.h

@@ -8,7 +8,7 @@
 
 namespace gameplay
 {
-
+class Properties;
 /**
  * Defines a utility for loading text files in the GamePlay "properties" files
  * and reading primitive types and GamePlay math classes out of them.
@@ -377,20 +377,38 @@ public:
      */
     bool getColor(const char* name, Vector4* out) const;
 
+    /**
+     * Gets the file path for the given property if the file exists.
+     * 
+     * This method will first search for the file relative to the working directory.
+     * If the file is not found then it will search relative to the directory the bundle file is in.
+     * 
+     * @param name The name of the property.
+     * @param path The string to copy the path to if the file exists.
+     * 
+     * @return True if the property exists and the file exists, false otherwise.
+     */
+    bool getPath(const char* name, std::string* path) const;
 
 private:
     
     /**
-     * Constructors.
+     * Constructor.
      */
     Properties();
+
+    /**
+     * Constructs the Properties class from a file.
+     *
+     * @param stream The stream used for reading the properties from file.
+     */
     Properties(Stream* stream);
     Properties(const Properties& copy);
 
     /**
-     * Constructor. Read from the beginning of namespace specified
+     * Constructor. Read from the beginning of namespace specified.
      */
-    Properties(Stream* stream, const char* name, const char* id = NULL, const char* parentID = NULL);
+    Properties(Stream* stream, const char* name, const char* id, const char* parentID, Properties* parent);
 
     void readProperties(Stream* stream);
 
@@ -407,6 +425,9 @@ private:
     // Clones the Properties object.
     Properties* clone();
 
+    void setDirectoryPath(const std::string* path);
+    void setDirectoryPath(const std::string& path);
+
     std::string _namespace;
     std::string _id;
     std::string _parentID;
@@ -414,6 +435,8 @@ private:
     std::map<std::string, std::string>::const_iterator _propertiesItr;
     std::vector<Properties*> _namespaces;
     std::vector<Properties*>::const_iterator _namespacesItr;
+    std::string* _dirPath;
+    Properties* _parent;
 };
 
 }

+ 154 - 68
gameplay/src/RenderState.cpp

@@ -227,79 +227,148 @@ void RenderState::applyAutoBinding(const char* uniformName, const char* autoBind
     MaterialParameter* param = getParameter(uniformName);
     GP_ASSERT(param);
 
+    bool bound = false;
+
     // First attempt to resolve the binding using custom registered resolvers.
     if (_customAutoBindingResolvers.size() > 0)
     {
         for (size_t i = 0, count = _customAutoBindingResolvers.size(); i < count; ++i)
         {
             if (_customAutoBindingResolvers[i](autoBinding, _nodeBinding, param))
-                return; // handled by custom resolver
+            {
+                // Handled by custom auto binding resolver
+                bound = true;
+                break;
+            }
         }
     }
 
     // Perform built-in resolution
-    if (strcmp(autoBinding, "WORLD_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getWorldMatrix);
-    }
-    else if (strcmp(autoBinding, "VIEW_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getViewMatrix);
-    }
-    else if (strcmp(autoBinding, "PROJECTION_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getProjectionMatrix);
-    }
-    else if (strcmp(autoBinding, "WORLD_VIEW_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getWorldViewMatrix);
-    }
-    else if (strcmp(autoBinding, "VIEW_PROJECTION_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getViewProjectionMatrix);
-    }
-    else if (strcmp(autoBinding, "WORLD_VIEW_PROJECTION_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getWorldViewProjectionMatrix);
-    }
-    else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldMatrix);
-    }
-    else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getInverseTransposeWorldViewMatrix);
-    }
-    else if (strcmp(autoBinding, "CAMERA_WORLD_POSITION") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationWorld);
-    }
-    else if (strcmp(autoBinding, "CAMERA_VIEW_POSITION") == 0)
-    {
-        param->bindValue(_nodeBinding, &Node::getActiveCameraTranslationView);
-    }
-    else if (strcmp(autoBinding, "MATRIX_PALETTE") == 0)
-    {
-        param->bindValue(this, &RenderState::autoBindingGetMatrixPalette, &RenderState::autoBindingGetMatrixPaletteSize);
-    }
-    else if (strcmp(autoBinding, "SCENE_AMBIENT_COLOR") == 0)
-    {
-        param->bindValue(this, &RenderState::autoBindingGetAmbientColor);
-    }
-    else if (strcmp(autoBinding, "SCENE_LIGHT_COLOR") == 0)
-    {
-        param->bindValue(this, &RenderState::autoBindingGetLightColor);
-    }
-    else if (strcmp(autoBinding, "SCENE_LIGHT_DIRECTION") == 0)
+    if (!bound)
     {
-        param->bindValue(this, &RenderState::autoBindingGetLightDirection);
+        bound = true;
+
+        if (strcmp(autoBinding, "WORLD_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetWorldMatrix);
+        }
+        else if (strcmp(autoBinding, "VIEW_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetViewMatrix);
+        }
+        else if (strcmp(autoBinding, "PROJECTION_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetProjectionMatrix);
+        }
+        else if (strcmp(autoBinding, "WORLD_VIEW_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetWorldViewMatrix);
+        }
+        else if (strcmp(autoBinding, "VIEW_PROJECTION_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetViewProjectionMatrix);
+        }
+        else if (strcmp(autoBinding, "WORLD_VIEW_PROJECTION_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetWorldViewProjectionMatrix);
+        }
+        else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetInverseTransposeWorldMatrix);
+        }
+        else if (strcmp(autoBinding, "INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetInverseTransposeWorldViewMatrix);
+        }
+        else if (strcmp(autoBinding, "CAMERA_WORLD_POSITION") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetCameraWorldPosition);
+        }
+        else if (strcmp(autoBinding, "CAMERA_VIEW_POSITION") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetCameraViewPosition);
+        }
+        else if (strcmp(autoBinding, "MATRIX_PALETTE") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetMatrixPalette, &RenderState::autoBindingGetMatrixPaletteSize);
+        }
+        else if (strcmp(autoBinding, "SCENE_AMBIENT_COLOR") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetAmbientColor);
+        }
+        else if (strcmp(autoBinding, "SCENE_LIGHT_COLOR") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetLightColor);
+        }
+        else if (strcmp(autoBinding, "SCENE_LIGHT_DIRECTION") == 0)
+        {
+            param->bindValue(this, &RenderState::autoBindingGetLightDirection);
+        }
+        else
+        {
+            bound = false;
+            GP_WARN("Unsupported auto binding type (%d).", autoBinding);
+        }
     }
-    else
+
+    if (bound)
     {
-        GP_WARN("Unsupported auto binding type (%d).", autoBinding);
+        // Mark parameter as an auto binding
+        if (param->_type == MaterialParameter::METHOD && param->_value.method)
+            param->_value.method->_autoBinding = true;
     }
 }
 
+const Matrix& RenderState::autoBindingGetWorldMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getWorldMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetViewMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getViewMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetProjectionMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getProjectionMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetWorldViewMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getWorldViewMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetViewProjectionMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getViewProjectionMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetWorldViewProjectionMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getWorldViewProjectionMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetInverseTransposeWorldMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getInverseTransposeWorldMatrix() : Matrix::identity();
+}
+
+const Matrix& RenderState::autoBindingGetInverseTransposeWorldViewMatrix() const
+{
+    return _nodeBinding ? _nodeBinding->getInverseTransposeWorldViewMatrix() : Matrix::identity();
+}
+
+Vector3 RenderState::autoBindingGetCameraWorldPosition() const
+{
+    return _nodeBinding ? _nodeBinding->getActiveCameraTranslationWorld() : Vector3::zero();
+}
+
+Vector3 RenderState::autoBindingGetCameraViewPosition() const
+{
+    return _nodeBinding ? _nodeBinding->getActiveCameraTranslationView() : Vector3::zero();
+}
+
 const Vector4* RenderState::autoBindingGetMatrixPalette() const
 {
     Model* model = _nodeBinding ? _nodeBinding->getModel() : NULL;
@@ -307,6 +376,13 @@ const Vector4* RenderState::autoBindingGetMatrixPalette() const
     return skin ? skin->getMatrixPalette() : NULL;
 }
 
+unsigned int RenderState::autoBindingGetMatrixPaletteSize() const
+{
+    Model* model = _nodeBinding ? _nodeBinding->getModel() : NULL;
+    MeshSkin* skin = model ? model->getSkin() : NULL;
+    return skin ? skin->getMatrixPaletteSize() : 0;
+}
+
 const Vector3& RenderState::autoBindingGetAmbientColor() const
 {
     Scene* scene = _nodeBinding ? _nodeBinding->getScene() : NULL;
@@ -326,13 +402,6 @@ const Vector3& RenderState::autoBindingGetLightDirection() const
     return scene ? scene->getLightDirection() : down;
 }
 
-unsigned int RenderState::autoBindingGetMatrixPaletteSize() const
-{
-    Model* model = _nodeBinding ? _nodeBinding->getModel() : NULL;
-    MeshSkin* skin = model ? model->getSkin() : NULL;
-    return skin ? skin->getMatrixPaletteSize() : 0;
-}
-
 void RenderState::bind(Pass* pass)
 {
     GP_ASSERT(pass);
@@ -406,6 +475,11 @@ void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context)
         const MaterialParameter* param = *it;
         GP_ASSERT(param);
 
+        // If this parameter is a method binding auto binding, don't clone it - it will get setup automatically
+        // via the cloned auto bindings instead.
+        if (param->_type == MaterialParameter::METHOD && param->_value.method && param->_value.method->_autoBinding)
+            continue;
+
         MaterialParameter* paramCopy = new MaterialParameter(param->getName());
         param->cloneInto(paramCopy);
 
@@ -415,11 +489,9 @@ void RenderState::cloneInto(RenderState* renderState, NodeCloneContext& context)
     // Clone our state block
     if (_state)
     {
-        // StateBlock contains only simple primitive data, so use the default assignment
-        // operator to do a memberwise copy.
-        *renderState->getStateBlock() = *_state;
+        _state->cloneInto(renderState->getStateBlock());
     }
-    
+
     // Notes:
     // 1. _nodeBinding should not be set here, it should be set by the caller.
     // 2. _parent should not be set here, since it's set in the constructor of Technique and Pass.
@@ -572,6 +644,20 @@ void RenderState::StateBlock::enableDepthWrite()
     }
 }
 
+void RenderState::StateBlock::cloneInto(StateBlock* state)
+{
+    GP_ASSERT(state);
+
+    state->_cullFaceEnabled = _cullFaceEnabled;
+    state->_depthTestEnabled = _depthTestEnabled;
+    state->_depthWriteEnabled = _depthWriteEnabled;
+    state->_depthFunction = _depthFunction;
+    state->_blendEnabled = _blendEnabled;
+    state->_blendSrc = _blendSrc;
+    state->_blendDst = _blendDst;
+    state->_bits = _bits;
+}
+
 static bool parseBoolean(const char* value)
 {
     GP_ASSERT(value);

+ 15 - 21
gameplay/src/RenderState.h

@@ -287,6 +287,8 @@ public:
 
         static void enableDepthWrite();
 
+        void cloneInto(StateBlock* state);
+
         // States
         bool _cullFaceEnabled;
         bool _depthTestEnabled;
@@ -476,31 +478,23 @@ private:
      */
     RenderState& operator=(const RenderState&);
 
-    /**
-     * Internal auto binding handler.
-     */
+    // Internal auto binding handler methods.
+    const Matrix& autoBindingGetWorldMatrix() const;
+    const Matrix& autoBindingGetViewMatrix() const;
+    const Matrix& autoBindingGetProjectionMatrix() const;
+    const Matrix& autoBindingGetWorldViewMatrix() const;
+    const Matrix& autoBindingGetViewProjectionMatrix() const;
+    const Matrix& autoBindingGetWorldViewProjectionMatrix() const;
+    const Matrix& autoBindingGetInverseTransposeWorldMatrix() const;
+    const Matrix& autoBindingGetInverseTransposeWorldViewMatrix() const;
+    Vector3 autoBindingGetCameraWorldPosition() const;
+    Vector3 autoBindingGetCameraViewPosition() const;
+    const Vector4* autoBindingGetMatrixPalette() const;
+    unsigned int autoBindingGetMatrixPaletteSize() const;
     const Vector3& autoBindingGetAmbientColor() const;
-
-    /**
-     * Internal auto binding handler.
-     */
     const Vector3& autoBindingGetLightColor() const;
-
-    /**
-     * Internal auto binding handler.
-     */
     const Vector3& autoBindingGetLightDirection() const;
 
-    /**
-     * Internal auto binding handler.
-     */
-    const Vector4* autoBindingGetMatrixPalette() const;
-
-    /**
-     * Internal auto binding handler.
-     */
-    unsigned int autoBindingGetMatrixPaletteSize() const;
-
 protected:
 
     /**

+ 4 - 2
gameplay/src/SceneLoader.cpp

@@ -44,9 +44,11 @@ Scene* SceneLoader::loadInternal(const char* url)
     }
 
     // Get the path to the main GPB.
-    const char* path = sceneProperties->getString("path");
-    if (path)
+    std::string path;
+    if (sceneProperties->getPath("path", &path))
+    {
         _gpbPath = path;
+    }
 
     // Build the node URL/property and animation reference tables and load the referenced files/store the inline properties objects.
     buildReferenceTables(sceneProperties);

+ 26 - 9
gameplay/src/ScriptController.cpp

@@ -713,12 +713,29 @@ void ScriptController::initialize()
         GP_ERROR("Failed to load custom loadfile() function with error: '%s'.", lua_tostring(_lua, -1));
     if (luaL_dostring(_lua, lua_dofile_function))
         GP_ERROR("Failed to load custom dofile() function with error: '%s'.", lua_tostring(_lua, -1));
+
+    // Write game command-line arguments to a global lua "arg" table
+    std::ostringstream args;
+    int argc;
+    char** argv;
+    Game::getInstance()->getArguments(&argc, &argv);
+    args << "arg = { }\n";
+    for (int i = 0; i < argc; ++i)
+    {
+        args << "arg[" << (i) << "] = [[" << argv[i] << "]]\n";
+    }
+    std::string argsStr = args.str();
+    if (argsStr.length() > 0)
+    {
+        if (luaL_dostring(_lua, argsStr.c_str()))
+            GP_ERROR("Failed to pass command-line arguments with error: '%s'.", lua_tostring(_lua, -1));
+    }
 }
 
 void ScriptController::initializeGame()
 {
     std::vector<std::string>& list = _callbacks[INITIALIZE];
-    for (size_t i = 0, count = list.size(); i < count; ++i)
+    for (size_t i = 0; i < list.size(); ++i)
         executeFunction<void>(list[i].c_str());
 }
 
@@ -740,7 +757,7 @@ void ScriptController::finalizeGame()
         _callbacks[i].clear();
 
 	// Fire script finalize callbacks
-    for (size_t i = 0, count = finalizeCallbacks.size(); i < count; ++i)
+    for (size_t i = 0; i < finalizeCallbacks.size(); ++i)
         executeFunction<void>(finalizeCallbacks[i].c_str());
 
     // Perform a full garbage collection cycle.
@@ -753,42 +770,42 @@ void ScriptController::finalizeGame()
 void ScriptController::update(float elapsedTime)
 {
     std::vector<std::string>& list = _callbacks[UPDATE];
-    for (size_t i = 0, count = list.size(); i < count; ++i)
+    for (size_t i = 0; i < list.size(); ++i)
         executeFunction<void>(list[i].c_str(), "f", elapsedTime);
 }
 
 void ScriptController::render(float elapsedTime)
 {
     std::vector<std::string>& list = _callbacks[RENDER];
-    for (size_t i = 0, count = list.size(); i < count; ++i)
+    for (size_t i = 0; i < list.size(); ++i)
         executeFunction<void>(list[i].c_str(), "f", elapsedTime);
 }
 
 void ScriptController::resizeEvent(unsigned int width, unsigned int height)
 {
     std::vector<std::string>& list = _callbacks[RESIZE_EVENT];
-    for (size_t i = 0, count = list.size(); i < count; ++i)
+    for (size_t i = 0; i < list.size(); ++i)
         executeFunction<void>(list[i].c_str(), "uiui", width, height);
 }
 
 void ScriptController::keyEvent(Keyboard::KeyEvent evt, int key)
 {
     std::vector<std::string>& list = _callbacks[KEY_EVENT];
-    for (size_t i = 0, count = list.size(); i < count; ++i)
+    for (size_t i = 0; i < list.size(); ++i)
         executeFunction<void>(list[i].c_str(), "[Keyboard::KeyEvent][Keyboard::Key]", evt, key);
 }
 
 void ScriptController::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
 {
     std::vector<std::string>& list = _callbacks[TOUCH_EVENT];
-    for (size_t i = 0, count = list.size(); i < count; ++i)
+    for (size_t i = 0; i < list.size(); ++i)
         executeFunction<void>(list[i].c_str(), "[Touch::TouchEvent]iiui", evt, x, y, contactIndex);
 }
 
 bool ScriptController::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheelDelta)
 {
     std::vector<std::string>& list = _callbacks[MOUSE_EVENT];
-    for (size_t i = 0, count = list.size(); i < count; ++i)
+    for (size_t i = 0; i < list.size(); ++i)
     {
         if (executeFunction<bool>(list[i].c_str(), "[Mouse::MouseEvent]iii", evt, x, y, wheelDelta))
             return true;
@@ -799,7 +816,7 @@ bool ScriptController::mouseEvent(Mouse::MouseEvent evt, int x, int y, int wheel
 void ScriptController::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
 {
     std::vector<std::string>& list = _callbacks[GAMEPAD_EVENT];
-    for (size_t i = 0, count = list.size(); i < count; ++i)
+    for (size_t i = 0; i < list.size(); ++i)
         executeFunction<void>(list[i].c_str(), "[Gamepad::GamepadEvent]<Gamepad>", evt, gamepad);
 }
 

+ 1 - 0
gameplay/src/Technique.cpp

@@ -65,6 +65,7 @@ Technique* Technique::clone(Material* material, NodeCloneContext &context) const
         technique->_passes.push_back(passCopy);
     }
     RenderState::cloneInto(technique, context);
+    technique->_parent = material;
     return technique;
 }
 

+ 25 - 17
gameplay/src/Terrain.cpp

@@ -96,8 +96,8 @@ Terrain* Terrain::create(const char* path, Properties* properties)
         if (pHeightmap)
         {
             // Read heightmap path
-            const char* heightmap = pHeightmap->getString("path");
-            if (strlen(heightmap) == 0)
+            std::string heightmap;
+            if (!pHeightmap->getPath("path", &heightmap))
             {
                 GP_WARN("No 'path' property supplied in heightmap section of terrain definition: %s", path);
                 if (!externalProperties)
@@ -105,11 +105,11 @@ Terrain* Terrain::create(const char* path, Properties* properties)
                 return NULL;
             }
 
-            std::string ext = FileSystem::getExtension(heightmap);
+            std::string ext = FileSystem::getExtension(heightmap.c_str());
             if (ext == ".PNG")
             {
                 // Read normalized height values from heightmap image
-                heightfield = HeightField::createFromImage(heightmap, 0, 1);
+                heightfield = HeightField::createFromImage(heightmap.c_str(), 0, 1);
             }
             else if (ext == ".RAW" || ext == ".R16")
             {
@@ -124,12 +124,12 @@ Terrain* Terrain::create(const char* path, Properties* properties)
                 }
 
                 // Read normalized height values from RAW file
-                heightfield = HeightField::createFromRAW(heightmap, (unsigned int)imageSize.x, (unsigned int)imageSize.y, 0, 1);
+                heightfield = HeightField::createFromRAW(heightmap.c_str(), (unsigned int)imageSize.x, (unsigned int)imageSize.y, 0, 1);
             }
             else
             {
                 // Unsupported heightmap format
-                GP_WARN("Unsupported heightmap format ('%s') in terrain definition: %s", heightmap, path);
+                GP_WARN("Unsupported heightmap format ('%s') in terrain definition: %s", heightmap.c_str(), path);
                 if (!externalProperties)
                     SAFE_DELETE(p);
                 return NULL;
@@ -138,8 +138,8 @@ Terrain* Terrain::create(const char* path, Properties* properties)
         else
         {
             // Try to read 'heightmap' as a simple string property
-            const char* heightmap = pTerrain->getString("heightmap");
-            if (heightmap == NULL || strlen(heightmap) == 0)
+            std::string heightmap;
+            if (!pTerrain->getPath("heightmap", &heightmap))
             {
                 GP_WARN("No 'heightmap' property supplied in terrain definition: %s", path);
                 if (!externalProperties)
@@ -147,11 +147,11 @@ Terrain* Terrain::create(const char* path, Properties* properties)
                 return NULL;
             }
 
-            std::string ext = FileSystem::getExtension(heightmap);
+            std::string ext = FileSystem::getExtension(heightmap.c_str());
             if (ext == ".PNG")
             {
                 // Read normalized height values from heightmap image
-                heightfield = HeightField::createFromImage(heightmap, 0, 1);
+                heightfield = HeightField::createFromImage(heightmap.c_str(), 0, 1);
             }
             else if (ext == ".RAW" || ext == ".R16")
             {
@@ -162,7 +162,7 @@ Terrain* Terrain::create(const char* path, Properties* properties)
             }
             else
             {
-                GP_WARN("Unsupported 'heightmap' format ('%s') in terrain definition: %s.", heightmap, path);
+                GP_WARN("Unsupported 'heightmap' format ('%s') in terrain definition: %s.", heightmap.c_str(), path);
                 if (!externalProperties)
                     SAFE_DELETE(p);
                 return NULL;
@@ -301,8 +301,10 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
                 else
                     ++index;
 
-                const char* textureMap = NULL;
-                const char* blendMap = NULL;
+                std::string textureMap;
+                const char* textureMapPtr = NULL;
+                std::string blendMap;
+                const char* blendMapPtr = NULL;
                 Vector2 textureRepeat;
                 int blendChannel = 0;
                 int row = -1, column = -1;
@@ -312,7 +314,10 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
                 Properties* t = lp->getNamespace("texture", true);
                 if (t)
                 {
-                    textureMap = t->getString("path");
+                    if (t->getPath("path", &textureMap))
+                    {
+                        textureMapPtr = textureMap.c_str();
+                    }
                     if (!t->getVector2("repeat", &textureRepeat))
                         textureRepeat.set(1,1);
                 }
@@ -320,7 +325,10 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
                 Properties* b = lp->getNamespace("blend", true);
                 if (b)
                 {
-                    blendMap = b->getString("path");
+                    if (b->getPath("path", &blendMap))
+                    {
+                        blendMapPtr = blendMap.c_str();
+                    }
                     const char* channel = b->getString("channel");
                     if (channel && strlen(channel) > 0)
                     {
@@ -342,9 +350,9 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale, unsigne
                 if (lp->exists("column"))
                     column = lp->getInt("column");
 
-                if (!terrain->setLayer(index, textureMap, textureRepeat, blendMap, blendChannel, row, column))
+                if (!terrain->setLayer(index, textureMapPtr, textureRepeat, blendMapPtr, blendChannel, row, column))
                 {
-                    GP_WARN("Failed to load terrain layer: %s", textureMap);
+                    GP_WARN("Failed to load terrain layer: %s", textureMap.c_str());
                 }
             }
         }

+ 1 - 33
gameplay/src/lua/lua_Game.cpp

@@ -404,41 +404,9 @@ int lua_Game_gamepadEvent(lua_State* state)
             lua_error(state);
             break;
         }
-        case 4:
-        {
-            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
-                (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL) &&
-                (lua_type(state, 3) == LUA_TUSERDATA || lua_type(state, 3) == LUA_TTABLE || lua_type(state, 3) == LUA_TNIL) &&
-                lua_type(state, 4) == LUA_TNUMBER)
-            {
-                // Get parameter 1 off the stack.
-                Gamepad::GamepadEvent param1 = (Gamepad::GamepadEvent)lua_enumFromString_GamepadGamepadEvent(luaL_checkstring(state, 2));
-
-                // Get parameter 2 off the stack.
-                bool param2Valid;
-                gameplay::ScriptUtil::LuaArray<Gamepad> param2 = gameplay::ScriptUtil::getObjectPointer<Gamepad>(3, "Gamepad", false, &param2Valid);
-                if (!param2Valid)
-                {
-                    lua_pushstring(state, "Failed to convert parameter 2 to type 'Gamepad'.");
-                    lua_error(state);
-                }
-
-                // Get parameter 3 off the stack.
-                unsigned int param3 = (unsigned int)luaL_checkunsigned(state, 4);
-
-                Game* instance = getInstance(state);
-                instance->gamepadEvent(param1, param2, param3);
-                
-                return 0;
-            }
-
-            lua_pushstring(state, "lua_Game_gamepadEvent - Failed to match the given parameters to a valid function signature.");
-            lua_error(state);
-            break;
-        }
         default:
         {
-            lua_pushstring(state, "Invalid number of parameters (expected 3 or 4).");
+            lua_pushstring(state, "Invalid number of parameters (expected 3).");
             lua_error(state);
             break;
         }

+ 2 - 0
gameplay/src/lua/lua_Global.cpp

@@ -22,6 +22,7 @@ void luaRegister_lua_Global()
     gameplay::ScriptUtil::setGlobalHierarchyPair("AnimationTarget", "TextBox");
     gameplay::ScriptUtil::setGlobalHierarchyPair("AnimationTarget", "Transform");
     gameplay::ScriptUtil::setGlobalHierarchyPair("Button", "CheckBox");
+    gameplay::ScriptUtil::setGlobalHierarchyPair("Button", "ImageControl");
     gameplay::ScriptUtil::setGlobalHierarchyPair("Button", "RadioButton");
     gameplay::ScriptUtil::setGlobalHierarchyPair("Container", "Form");
     gameplay::ScriptUtil::setGlobalHierarchyPair("Control", "Button");
@@ -36,6 +37,7 @@ void luaRegister_lua_Global()
     gameplay::ScriptUtil::setGlobalHierarchyPair("Control", "TextBox");
     gameplay::ScriptUtil::setGlobalHierarchyPair("Label", "Button");
     gameplay::ScriptUtil::setGlobalHierarchyPair("Label", "CheckBox");
+    gameplay::ScriptUtil::setGlobalHierarchyPair("Label", "ImageControl");
     gameplay::ScriptUtil::setGlobalHierarchyPair("Label", "RadioButton");
     gameplay::ScriptUtil::setGlobalHierarchyPair("Label", "Slider");
     gameplay::ScriptUtil::setGlobalHierarchyPair("Label", "TextBox");

+ 76 - 0
gameplay/src/lua/lua_ImageControl.cpp

@@ -4,9 +4,12 @@
 #include "Animation.h"
 #include "AnimationTarget.h"
 #include "Base.h"
+#include "Button.h"
 #include "Control.h"
 #include "Game.h"
+#include "Gamepad.h"
 #include "ImageControl.h"
+#include "Label.h"
 #include "Node.h"
 #include "Ref.h"
 #include "ScriptController.h"
@@ -64,6 +67,7 @@ void luaRegister_ImageControl()
         {"getSkinRegion", lua_ImageControl_getSkinRegion},
         {"getState", lua_ImageControl_getState},
         {"getStyle", lua_ImageControl_getStyle},
+        {"getText", lua_ImageControl_getText},
         {"getTextAlignment", lua_ImageControl_getTextAlignment},
         {"getTextColor", lua_ImageControl_getTextColor},
         {"getTextRightToLeft", lua_ImageControl_getTextRightToLeft},
@@ -106,6 +110,7 @@ void luaRegister_ImageControl()
         {"setSkinRegion", lua_ImageControl_setSkinRegion},
         {"setState", lua_ImageControl_setState},
         {"setStyle", lua_ImageControl_setStyle},
+        {"setText", lua_ImageControl_setText},
         {"setTextAlignment", lua_ImageControl_setTextAlignment},
         {"setTextColor", lua_ImageControl_setTextColor},
         {"setTextRightToLeft", lua_ImageControl_setTextRightToLeft},
@@ -2261,6 +2266,41 @@ int lua_ImageControl_getStyle(lua_State* state)
     return 0;
 }
 
+int lua_ImageControl_getText(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 1:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA))
+            {
+                ImageControl* instance = getInstance(state);
+                const char* result = instance->getText();
+
+                // Push the return value onto the stack.
+                lua_pushstring(state, result);
+
+                return 1;
+            }
+
+            lua_pushstring(state, "lua_ImageControl_getText - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 1).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_ImageControl_getTextAlignment(lua_State* state)
 {
     // Get the number of parameters.
@@ -4309,6 +4349,42 @@ int lua_ImageControl_setStyle(lua_State* state)
     return 0;
 }
 
+int lua_ImageControl_setText(lua_State* state)
+{
+    // Get the number of parameters.
+    int paramCount = lua_gettop(state);
+
+    // Attempt to match the parameters to a valid binding.
+    switch (paramCount)
+    {
+        case 2:
+        {
+            if ((lua_type(state, 1) == LUA_TUSERDATA) &&
+                (lua_type(state, 2) == LUA_TSTRING || lua_type(state, 2) == LUA_TNIL))
+            {
+                // Get parameter 1 off the stack.
+                const char* param1 = gameplay::ScriptUtil::getString(2, false);
+
+                ImageControl* instance = getInstance(state);
+                instance->setText(param1);
+                
+                return 0;
+            }
+
+            lua_pushstring(state, "lua_ImageControl_setText - Failed to match the given parameters to a valid function signature.");
+            lua_error(state);
+            break;
+        }
+        default:
+        {
+            lua_pushstring(state, "Invalid number of parameters (expected 2).");
+            lua_error(state);
+            break;
+        }
+    }
+    return 0;
+}
+
 int lua_ImageControl_setTextAlignment(lua_State* state)
 {
     // Get the number of parameters.

+ 2 - 0
gameplay/src/lua/lua_ImageControl.h

@@ -46,6 +46,7 @@ int lua_ImageControl_getSkinColor(lua_State* state);
 int lua_ImageControl_getSkinRegion(lua_State* state);
 int lua_ImageControl_getState(lua_State* state);
 int lua_ImageControl_getStyle(lua_State* state);
+int lua_ImageControl_getText(lua_State* state);
 int lua_ImageControl_getTextAlignment(lua_State* state);
 int lua_ImageControl_getTextColor(lua_State* state);
 int lua_ImageControl_getTextRightToLeft(lua_State* state);
@@ -88,6 +89,7 @@ int lua_ImageControl_setSkinColor(lua_State* state);
 int lua_ImageControl_setSkinRegion(lua_State* state);
 int lua_ImageControl_setState(lua_State* state);
 int lua_ImageControl_setStyle(lua_State* state);
+int lua_ImageControl_setText(lua_State* state);
 int lua_ImageControl_setTextAlignment(lua_State* state);
 int lua_ImageControl_setTextColor(lua_State* state);
 int lua_ImageControl_setTextRightToLeft(lua_State* state);

+ 57 - 2
install.bat

@@ -1,6 +1,6 @@
 @echo off
 REM
-REM gameplay-install.bat [master | next]
+REM install.bat [master | next]
 REM
 REM Download GamePlay resources from HTTP server and extract from ZIP
 REM
@@ -21,13 +21,50 @@ set filename=gameplay-deps-%branchname%
 echo Downloading %filename%.zip from %prefix%
 %~d0
 cd %~dp0
-> temp1.vbs ECHO Dim strFileURL, strHDLocation
+> temp.cs ECHO using System;
+>> temp.cs ECHO using System.Net;
+>> temp.cs ECHO using System.ComponentModel;
+>> temp.cs ECHO class Program
+>> temp.cs ECHO {
+>> temp.cs ECHO     static string file = "%filename%.zip";
+>> temp.cs ECHO     static string url = "%prefix%/" + file;
+>> temp.cs ECHO     static bool done = false;
+>> temp.cs ECHO     static void Main(string[] args)
+>> temp.cs ECHO     {
+>> temp.cs ECHO         try
+>> temp.cs ECHO         {
+>> temp.cs ECHO             WebClient client = new WebClient();
+>> temp.cs ECHO             client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);
+>> temp.cs ECHO             client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
+>> temp.cs ECHO             Console.Write("Downloading " + url + ": 0%%    ");
+>> temp.cs ECHO             client.DownloadFileAsync(new Uri(url), file);
+>> temp.cs ECHO             while (!done) System.Threading.Thread.Sleep(1000);
+>> temp.cs ECHO         }
+>> temp.cs ECHO         catch (Exception x)
+>> temp.cs ECHO         {
+>> temp.cs ECHO             Console.WriteLine("Error: " + x.Message);
+>> temp.cs ECHO         }
+>> temp.cs ECHO     }
+>> temp.cs ECHO     static void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
+>> temp.cs ECHO     {
+>> temp.cs ECHO         Console.Write("\rDownloading " + url + ": " + e.ProgressPercentage + "%%    ");
+>> temp.cs ECHO     }
+>> temp.cs ECHO     static void DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
+>> temp.cs ECHO     {
+>> temp.cs ECHO         Console.WriteLine("\rDownloading " + url + ": Done.    ");
+>> temp.cs ECHO         done = true;
+>> temp.cs ECHO     }
+>> temp.cs ECHO }
+> temp1.vbs ECHO WScript.Echo "Downloading using a fallback method. This might take a few minutes."
+>> temp1.vbs ECHO Dim strFileURL, strHDLocation
 >> temp1.vbs ECHO strFileURL = WScript.Arguments(0)
 >> temp1.vbs ECHO strHDLocation = WScript.Arguments(1)
 >> temp1.vbs ECHO Set objXMLHTTP = CreateObject("MSXML2.XMLHTTP")
 >> temp1.vbs ECHO objXMLHTTP.open "GET", strFileURL, false
+>> temp1.vbs ECHO WScript.Echo "Sending request..."
 >> temp1.vbs ECHO objXMLHTTP.send()
 >> temp1.vbs ECHO If objXMLHTTP.Status = 200 Then
+>> temp1.vbs ECHO WScript.Echo "Got response. Processing body..."
 >> temp1.vbs ECHO Set objADOStream = CreateObject("ADODB.Stream")
 >> temp1.vbs ECHO objADOStream.Open
 >> temp1.vbs ECHO objADOStream.Type = 1
@@ -36,13 +73,30 @@ cd %~dp0
 >> temp1.vbs ECHO Set objFSO = Createobject("Scripting.FileSystemObject")
 >> temp1.vbs ECHO If objFSO.Fileexists(strHDLocation) Then objFSO.DeleteFile strHDLocation
 >> temp1.vbs ECHO Set objFSO = Nothing
+>> temp1.vbs ECHO WScript.Echo "Saving result to a file..."
 >> temp1.vbs ECHO objADOStream.SaveToFile strHDLocation
 >> temp1.vbs ECHO objADOStream.Close
 >> temp1.vbs ECHO Set objADOStream = Nothing
+>> temp1.vbs ECHO WScript.Echo "Success."
 >> temp1.vbs ECHO End if
 >> temp1.vbs ECHO Set objXMLHTTP = Nothing
+
+if exist %windir%\Microsoft.NET\Framework\v2.0.50727\NUL (
+    %windir%\Microsoft.NET\Framework\v2.0.50727\csc temp.cs
+) else (
+if exist %windir%\Microsoft.NET\Framework\v4.0.30319\NUL (
+    %windir%\Microsoft.NET\Framework\v4.0.30319\csc temp.cs
+) else (
+    goto USE_VBS_AS_FALLBACK
+))
+temp.exe
+del temp.exe
+goto :EXTRACT
+
+:USE_VBS_AS_FALLBACK
 cscript temp1.vbs %prefix%/%filename%.zip %filename%.zip
 
+:EXTRACT
 echo Extracting %filename%.zip... please standby...
 %~d0
 cd %~dp0
@@ -56,6 +110,7 @@ cd %~dp0
 >> temp2.vbs ECHO objTarget.CopyHere objSource, intOptions
 cscript temp2.vbs %filename%.zip
 echo Cleaning up...
+del temp.cs
 del temp1.vbs
 del temp2.vbs
 del %filename%.zip

+ 1 - 1
install.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# ./gameplay-install.sh [master | next]
+# ./install.sh [master | next]
 #
 # Download GamePlay resources from HTTP server and extract from ZIP
 #

+ 1 - 0
newproject.sh

@@ -4,6 +4,7 @@ form basicControls
     layout = LAYOUT_ABSOLUTE
     style = basic
     size = 600, 600
+	consumeInputEvents = true
     	
 	label title
 	{

+ 1 - 0
samples/browser/res/common/forms/formFlowLayout.form

@@ -5,6 +5,7 @@ form flowLayout
     style = basic
     size = 600, 600
     scroll = SCROLL_BOTH
+	consumeInputEvents = true
 
     label label1
     {

+ 1 - 0
samples/browser/res/common/forms/formScrolling.form

@@ -6,6 +6,7 @@ form scrolling
     size = 600, 600
     scroll = SCROLL_BOTH
     scrollBarsAutoHide = true
+	consumeInputEvents = true
 
     label label1
     {

+ 1 - 0
samples/browser/res/common/forms/formSelect.form

@@ -6,6 +6,7 @@ form formSelect
     alignment = ALIGN_TOP_LEFT
     width = 200
     autoHeight = true
+	consumeInputEvents = true
 
 	radioButton form0
 	{

+ 1 - 0
samples/browser/res/common/forms/formVerticalLayout.form

@@ -5,6 +5,7 @@ form verticalLayout
     style = basic
     size = 600, 600
     scroll = SCROLL_BOTH
+	consumeInputEvents = true
 
     label label2
     {

+ 2 - 1
samples/browser/res/common/forms/formZOrder.form

@@ -4,7 +4,8 @@ form zOrder
     style = basic
     size = 600, 600
     scroll = SCROLL_BOTH
-
+	consumeInputEvents = true
+	
     label label1
     {
         style = basic

+ 44 - 34
samples/browser/src/FormsSample.cpp

@@ -7,6 +7,7 @@
 // Input bit-flags (powers of 2)
 #define KEY_A_MASK (1 << 0)
 #define KEY_B_MASK (1 << 1)
+#define KEY_SELECT_MASK (1 << 2)
 
 const static unsigned int __formsCount = 5;
 
@@ -185,6 +186,47 @@ void FormsSample::update(float elapsedTime)
     bool aDown = (_keyFlags & KEY_A_MASK);
     bool bDown = (_keyFlags & KEY_B_MASK);
 
+	// If no form is in focus, then we poll the gamepad for movement input.
+	if (_activeForm->getState() == Control::NORMAL &&
+        _formSelect->getState() == Control::NORMAL)
+    {
+        if (_gamepad->isButtonDown(Gamepad::BUTTON_A))
+            _keyFlags |= KEY_A_MASK;
+        else
+            _keyFlags &= ~KEY_A_MASK;
+
+        if (_gamepad->isButtonDown(Gamepad::BUTTON_B))
+            _keyFlags |= KEY_B_MASK;
+        else
+            _keyFlags &= ~KEY_B_MASK;
+
+        _gamepad->getJoystickValues(0, &_joysticks[0]);
+        _gamepad->getJoystickValues(1, &_joysticks[1]);
+    }
+
+    // We'll use a physical gamepad's MENU1 button as the "back" button.
+    if (!(_keyFlags & KEY_SELECT_MASK) && _gamepad->isButtonDown(Gamepad::BUTTON_MENU1))
+    {
+        _keyFlags |= KEY_SELECT_MASK;
+        if (_formSelect->getState() == Control::FOCUS)
+        {
+            _formSelect->setState(Control::NORMAL);
+        }
+        else if (_activeForm->getState() == Control::FOCUS)
+        {
+            _activeForm->setState(Control::NORMAL);
+            _formSelect->setState(Control::FOCUS);
+        }
+        else
+        {
+            _formSelect->setState(Control::FOCUS);
+        }
+    }
+    else if ((_keyFlags & KEY_SELECT_MASK) && !_gamepad->isButtonDown(Gamepad::BUTTON_MENU1))
+    {
+        _keyFlags &= ~KEY_SELECT_MASK;  
+    }
+
     if (_gamepad->isVirtual())
     {
         aDown |= _gamepad->isButtonDown(Gamepad::BUTTON_A);
@@ -375,8 +417,8 @@ void FormsSample::controlEvent(Control* control, EventType evt)
         control->createAnimationFromTo("opacityButton", Form::ANIMATE_OPACITY, from, to, Curve::LINEAR, 1000)->getClip()->play();
     }
 }
-
-void FormsSample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex)
+
+void FormsSample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
 {
     switch(evt)
     {
@@ -388,37 +430,5 @@ void FormsSample::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsi
         if (_gamepad && _gamepad->isVirtual())
             _gamepad->getForm()->setEnabled(true);
         break;
-    case Gamepad::BUTTON_EVENT:
-        if (_gamepad->isButtonDown(Gamepad::BUTTON_A))
-            _keyFlags |= KEY_A_MASK;
-        else
-            _keyFlags &= ~KEY_A_MASK;
-
-        if (_gamepad->isButtonDown(Gamepad::BUTTON_B))
-            _keyFlags |= KEY_B_MASK;
-        else
-            _keyFlags &= ~KEY_B_MASK;
-
-        // We'll use a physical gamepad's MENU1 button as the "back" button.
-        if (_gamepad->isButtonDown(Gamepad::BUTTON_MENU1))
-        {
-            if (_formSelect->getState() == Control::FOCUS)
-            {
-                _formSelect->setState(Control::NORMAL);
-            }
-            else if (_activeForm->getState() == Control::FOCUS)
-            {
-                _activeForm->setState(Control::NORMAL);
-                _formSelect->setState(Control::FOCUS);
-            }
-            else
-            {
-                _formSelect->setState(Control::FOCUS);
-            }
-        }
-        break;
-    case Gamepad::JOYSTICK_EVENT:
-        gamepad->getJoystickValues(analogIndex, &_joysticks[analogIndex]);
-        break;
     }
 }

+ 1 - 1
samples/browser/src/FormsSample.h

@@ -11,7 +11,7 @@ public:
     
     FormsSample();
 
-    void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad, unsigned int analogIndex = 0);
+    void gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad);
 
 protected:
 

+ 1 - 1
samples/character/src/CharacterGame.cpp

@@ -602,7 +602,7 @@ void CharacterGame::animationEvent(AnimationClip* clip, AnimationClip::Listener:
 
 void CharacterGame::clone()
 {
-    Node* clone = _scene->findNode("boyRotation")->clone();
+    Node* clone = _scene->findNode("boycharacter")->clone();
     Animation* cloneAnimation = clone->getAnimation();
 
     // Find the current clip and have the clone play that clip repeatedly.

+ 4 - 1
samples/particles/res/editor.form

@@ -10,6 +10,7 @@ form particleEditor
         layout = LAYOUT_VERTICAL
         position = 0, 0
         size = 160, 220
+        consumeInputEvents = true
 
         label title
         {
@@ -56,6 +57,7 @@ form particleEditor
         position = 0, 220
         layout = LAYOUT_VERTICAL
         size = 160, 210
+        consumeInputEvents = true
 
         // Burst emission
         button emit
@@ -108,6 +110,7 @@ form particleEditor
         style = noBorder
         size = 160, 50
         position = 0, 430
+        consumeInputEvents = true
 
         button zoomIn
         {
@@ -132,6 +135,7 @@ form particleEditor
         layout = LAYOUT_VERTICAL
         scroll = SCROLL_VERTICAL
         scrollBarsAutoHide = false
+        consumeInputEvents = true
 
         // Particle Size
         label titleSize
@@ -155,7 +159,6 @@ form particleEditor
             valueTextVisible = true
             valueTextAlignment = ALIGN_BOTTOM_HCENTER
             valueTextPrecision = 2
-            consumeInputEvents = false
         }
 
         slider startMax : startMin

+ 4 - 1
samples/racer/src/RacerGame.cpp

@@ -534,7 +534,10 @@ void RacerGame::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
                 _virtualGamepadClip->play();
             }
             _gamepad = _physicalGamepad;
-            _virtualGamepad->getForm()->setEnabled(false);
+			if (_virtualGamepad)
+			{
+				_virtualGamepad->getForm()->setEnabled(false);
+			}
         }
         else if (_virtualGamepad)
         {

+ 60 - 25
template/template-CMakeLists.txt

@@ -12,25 +12,52 @@ endif( CMAKE_SIZEOF_VOID_P EQUAL 8 )
 set(GAMEPLAY_SRC_PATH "GAMEPLAY_PATH") 
 set(GAMEPLAY_EXT_LIBS_PATH "${GAMEPLAY_SRC_PATH}/external-deps")
 
-set(TARGET_OS "LINUX")
-set(TARGET_OS_DIR "linux")
-
-set(GAME_OUTPUT_DIR "${CMAKE_SOURCE_DIR}/bin/${TARGET_OS_DIR}")
+IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
+    ADD_DEFINITIONS(-D__linux__)
+    SET(TARGET_OS "LINUX")
+    SET(TARGET_OS_DIR "linux")
+ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Windows")
+    IF(MSVC)
+        ADD_DEFINITIONS(-DMSVC)
+    ENDIF(MSVC)
+    ADD_DEFINITIONS(-DWIN32)
+    ADD_DEFINITIONS(-D_WINDOWS)
+    SET(TARGET_OS "WINDOWS")
+    SET(TARGET_OS_DIR "windows")
+ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
+
+set(GAME_OUTPUT_DIR "${CMAKE_BINARY_DIR}/bin/${TARGET_OS_DIR}")
 
 macro (append_gameplay_lib listToAppend)
     set(libName gameplay)
-    find_library(FOUND_LIB_${libName} ${libName} HINTS
-        "${GAMEPLAY_SRC_PATH}/cmake/gameplay" "${GAMEPLAY_SRC_PATH}/build/gameplay" "${GAMEPLAY_SRC_PATH}/gameplay/src")
-    set(${listToAppend} ${${listToAppend}} ${FOUND_LIB_${libName}})
+    IF (TARGET_OS STREQUAL "WINDOWS")
+		FIND_LIBRARY(${libName}_LIBRARY_RELEASE
+			NAMES ${libName}
+			PATHS "${GAMEPLAY_SRC_PATH}/gameplay/${TARGET_OS_DIR}/${ARCH_DIR}/Release"
+		)
+
+		FIND_LIBRARY(${libName}_LIBRARY_DEBUG
+			NAMES ${libName}
+			PATHS "${GAMEPLAY_SRC_PATH}/gameplay/${TARGET_OS_DIR}/${ARCH_DIR}/Debug"
+		)
+		SET(FOUND_LIB_${libName}
+			debug ${${libName}_LIBRARY_DEBUG}
+			optimized ${${libName}_LIBRARY_RELEASE}
+		)
+	ELSE (TARGET_OS STREQUAL "WINDOWS")
+		find_library(FOUND_LIB_${libName} ${libName} HINTS
+			"${GAMEPLAY_SRC_PATH}/cmake/gameplay" "${GAMEPLAY_SRC_PATH}/build/gameplay" "${GAMEPLAY_SRC_PATH}/gameplay/src")
+	ENDIF (TARGET_OS STREQUAL "WINDOWS")
+	set(${listToAppend} ${${listToAppend}} ${FOUND_LIB_${libName}})
 endmacro(append_gameplay_lib)
 
 macro (append_gameplay_ext_lib listToAppend libName libDirName)
     IF("${libDirName}" STREQUAL "")
-        find_library(FOUND_LIB_${libName} ${libName})
+		find_library(FOUND_LIB_${libName} NAMES ${libName} ${ARGN})
     ELSE("${libDirName}" STREQUAL "")
         set(pathToSearch
             "${GAMEPLAY_EXT_LIBS_PATH}/${libDirName}/lib/${TARGET_OS_DIR}/${ARCH_DIR}")
-        find_library(FOUND_LIB_${libName} ${libName} HINTS ${pathToSearch})
+		find_library(FOUND_LIB_${libName} NAMES ${libName} ${ARGN} HINTS ${pathToSearch})
     ENDIF("${libDirName}" STREQUAL "")
 
     set(${listToAppend} ${${listToAppend}} ${FOUND_LIB_${libName}})
@@ -67,28 +94,35 @@ include_directories(
     ${GAMEPLAY_SRC_PATH}/external-deps/oggvorbis/include
     ${GAMEPLAY_SRC_PATH}/external-deps/zlib/include
     ${GAMEPLAY_SRC_PATH}/external-deps/openal/include
+	${GAMEPLAY_SRC_PATH}/external-deps/openal/include/AL
     ${GAMEPLAY_SRC_PATH}/external-deps/glew/include
 )
 
 append_gameplay_lib(GAMEPLAY_LIBRARIES)
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "m" "" )
+append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "GLEW" "glew" "glew32")
 append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "lua" "lua")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "png" "libpng")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "z" "zlib")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "vorbis" "oggvorbis")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "ogg" "oggvorbis")
+append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "png" "libpng" "libpng14")
 append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "BulletDynamics" "bullet")
 append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "BulletCollision" "bullet")
 append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "LinearMath" "bullet")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "openal" "openal")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "GLEW" "glew")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "GL" "")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "rt" "" )
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "dl" "")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "X11" "")
-append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "pthread" "" )
-
-add_definitions(-D__linux__)
+append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "openal" "openal" "openal32")
+append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "vorbis" "oggvorbis" "libvorbis")
+append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "ogg" "oggvorbis" "libogg")
+append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "z" "zlib" "zlib")
+
+IF (TARGET_OS STREQUAL "LINUX")
+	append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "GL" "")
+	append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "m" "" )
+	append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "X11" "")
+	append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "dl" "")
+	append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "rt" "" )
+	append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "pthread" "" )
+ELSEIF (TARGET_OS STREQUAL "WINDOWS")
+	set(GAMEPLAY_LIBRARIES ${GAMEPLAY_LIBRARIES} "OpenGL32")
+	set(GAMEPLAY_LIBRARIES ${GAMEPLAY_LIBRARIES} "GLU32")
+	append_gameplay_ext_lib(GAMEPLAY_LIBRARIES "vorbisfile" "oggvorbis" "libvorbisfile")
+	ADD_DEFINITIONS(-D_ITERATOR_DEBUG_LEVEL=0)
+ENDIF (TARGET_OS STREQUAL "LINUX")
 
 source_group(res FILES ${GAME_RES} ${GAMEPLAY_RES} ${GAME_RES_SHADERS} ${GAME_RES_SHADERS_LIB})
 source_group(src FILES ${GAME_SRC})
@@ -99,6 +133,7 @@ set(GAME_SRC
 )
 
 add_executable(${GAME_NAME}
+	WIN32
     ${GAME_SRC}
 )
 
@@ -113,5 +148,5 @@ set_target_properties(${GAME_NAME} PROPERTIES
 #TODO: Copy res files to the bin dir, it is done that way so we can make post
 #processing to the the res files in the future like zipping or preparation to
 #per platfom format.
-copy_files(CopyRes * "${CMAKE_SOURCE_DIR}/res" "${GAME_OUTPUT_DIR}/res" 1)
-copy_files(CopyConfig *.config "${CMAKE_SOURCE_DIR}" "${GAME_OUTPUT_DIR}" 0)
+copy_files(CopyRes * "${CMAKE_SOURCE_DIR}/res" "$<TARGET_FILE_DIR:${GAME_NAME}>/res" 1)
+copy_files(CopyConfig *.config "${CMAKE_SOURCE_DIR}" "$<TARGET_FILE_DIR:${GAME_NAME}>" 0)

+ 6 - 0
tools/encoder/CMakeLists.txt

@@ -44,6 +44,8 @@ set(APP_SRC
 	src/BoundingVolume.h
 	src/Camera.cpp
 	src/Camera.h
+    src/Constants.cpp
+    src/Constants.h
 	src/Curve.cpp
 	src/Curve.h
 	src/Curve.inl
@@ -53,6 +55,8 @@ set(APP_SRC
 	src/EncoderArguments.h
 	src/FBXSceneEncoder.cpp
 	src/FBXSceneEncoder.h
+	src/FBXUtil.cpp
+	src/FBXUtil.h
 	src/FileIO.cpp
 	src/FileIO.h
 	src/Font.cpp
@@ -99,6 +103,8 @@ set(APP_SRC
 	src/Reference.h
 	src/ReferenceTable.cpp
 	src/ReferenceTable.h
+	src/Sampler.cpp
+	src/Sampler.h
 	src/Scene.cpp
 	src/Scene.h
 	src/StringUtil.cpp

+ 8 - 0
tools/encoder/gameplay-encoder.vcxproj

@@ -16,10 +16,12 @@
     <ClCompile Include="src\Base.cpp" />
     <ClCompile Include="src\BoundingVolume.cpp" />
     <ClCompile Include="src\Camera.cpp" />
+    <ClCompile Include="src\Constants.cpp" />
     <ClCompile Include="src\Curve.cpp" />
     <ClCompile Include="src\EncoderArguments.cpp" />
     <ClCompile Include="src\Effect.cpp" />
     <ClCompile Include="src\FBXSceneEncoder.cpp" />
+    <ClCompile Include="src\FBXUtil.cpp" />
     <ClCompile Include="src\FileIO.cpp" />
     <ClCompile Include="src\Font.cpp" />
     <ClCompile Include="src\GPBFile.cpp" />
@@ -34,6 +36,7 @@
     <ClCompile Include="src\MaterialParameter.cpp" />
     <ClCompile Include="src\Matrix.cpp" />
     <ClCompile Include="src\Mesh.cpp" />
+    <ClCompile Include="src\MeshSubSet.cpp" />
     <ClCompile Include="src\Model.cpp" />
     <ClCompile Include="src\MeshPart.cpp" />
     <ClCompile Include="src\MeshSkin.cpp" />
@@ -43,6 +46,7 @@
     <ClCompile Include="src\Quaternion.cpp" />
     <ClCompile Include="src\Reference.cpp" />
     <ClCompile Include="src\ReferenceTable.cpp" />
+    <ClCompile Include="src\Sampler.cpp" />
     <ClCompile Include="src\Scene.cpp" />
     <ClCompile Include="src\StringUtil.cpp" />
     <ClCompile Include="src\Transform.cpp" />
@@ -59,10 +63,12 @@
     <ClInclude Include="src\Base.h" />
     <ClInclude Include="src\BoundingVolume.h" />
     <ClInclude Include="src\Camera.h" />
+    <ClInclude Include="src\Constants.h" />
     <ClInclude Include="src\Curve.h" />
     <ClInclude Include="src\EncoderArguments.h" />
     <ClInclude Include="src\Effect.h" />
     <ClInclude Include="src\FBXSceneEncoder.h" />
+    <ClInclude Include="src\FBXUtil.h" />
     <ClInclude Include="src\FileIO.h" />
     <ClInclude Include="src\Font.h" />
     <ClInclude Include="src\GPBFile.h" />
@@ -76,6 +82,7 @@
     <ClInclude Include="src\MaterialParameter.h" />
     <ClInclude Include="src\Matrix.h" />
     <ClInclude Include="src\Mesh.h" />
+    <ClInclude Include="src\MeshSubSet.h" />
     <ClInclude Include="src\Model.h" />
     <ClInclude Include="src\MeshPart.h" />
     <ClInclude Include="src\MeshSkin.h" />
@@ -85,6 +92,7 @@
     <ClInclude Include="src\Quaternion.h" />
     <ClInclude Include="src\Reference.h" />
     <ClInclude Include="src\ReferenceTable.h" />
+    <ClInclude Include="src\Sampler.h" />
     <ClInclude Include="src\Scene.h" />
     <ClInclude Include="src\StringUtil.h" />
     <ClInclude Include="src\Thread.h" />

+ 24 - 0
tools/encoder/gameplay-encoder.vcxproj.filters

@@ -124,6 +124,18 @@
     <ClCompile Include="src\NormalMapGenerator.cpp">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\Constants.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\FBXUtil.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\MeshSubSet.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\Sampler.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\VertexElement.h">
@@ -249,6 +261,18 @@
     <ClInclude Include="src\NormalMapGenerator.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\Constants.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\FBXUtil.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\MeshSubSet.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\Sampler.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\Vector2.inl">

+ 31 - 0
tools/encoder/src/Constants.cpp

@@ -0,0 +1,31 @@
+#include "Constants.h"
+
+
+namespace gameplay
+{
+
+const std::string SPOT_LIGHT = "SPOT_LIGHT";
+const std::string POINT_LIGHT = "POINT_LIGHT";
+
+const std::string VERTEX_COLOR = "VERTEX_COLOR";
+
+const std::string SPECULAR = "SPECULAR";
+const std::string LINEAR = "LINEAR";
+const std::string CLAMP = "CLAMP";
+const std::string REPEAT = "REPEAT";
+const std::string LINEAR_MIPMAP_LINEAR = "LINEAR_MIPMAP_LINEAR";
+const std::string WORLD_VIEW_PROJECTION_MATRIX = "WORLD_VIEW_PROJECTION_MATRIX";
+
+const std::string TEXTURE_REPEAT = "TEXTURE_REPEAT";
+const std::string TEXTURE_OFFSET = "TEXTURE_OFFSET";
+
+const std::string u_diffuseTexture = "u_diffuseTexture";
+
+const std::string MIN_FILTER = "minFilter";
+const std::string MAG_FILTER = "magFilter";
+
+
+
+
+
+}

+ 33 - 0
tools/encoder/src/Constants.h

@@ -0,0 +1,33 @@
+#ifndef CONSTANTS_H_
+#define CONSTANTS_H_
+
+#include "string"
+
+namespace gameplay
+{
+
+extern const std::string SPOT_LIGHT;
+extern const std::string POINT_LIGHT;
+
+extern const std::string VERTEX_COLOR;
+
+extern const std::string SPECULAR;
+extern const std::string LINEAR;
+
+extern const std::string CLAMP;
+extern const std::string REPEAT;
+extern const std::string LINEAR_MIPMAP_LINEAR;
+extern const std::string WORLD_VIEW_PROJECTION_MATRIX;
+
+extern const std::string TEXTURE_REPEAT;
+extern const std::string TEXTURE_OFFSET;
+
+extern const std::string u_diffuseTexture;
+
+extern const std::string MIN_FILTER;
+extern const std::string MAG_FILTER;
+
+
+}
+
+#endif

+ 14 - 1
tools/encoder/src/EncoderArguments.cpp

@@ -24,7 +24,8 @@ EncoderArguments::EncoderArguments(size_t argc, const char** argv) :
     _fontPreview(false),
     _textOutput(false),
     _optimizeAnimations(false),
-    _animationGrouping(ANIMATIONGROUP_PROMPT)
+    _animationGrouping(ANIMATIONGROUP_PROMPT),
+    _outputMaterial(false)
 {
     __instance = this;
 
@@ -298,6 +299,11 @@ bool EncoderArguments::optimizeAnimationsEnabled() const
     return _optimizeAnimations;
 }
 
+bool EncoderArguments::outputMaterialEnabled() const
+{
+    return _outputMaterial;
+}
+
 const char* EncoderArguments::getNodeId() const
 {
     if (_nodeId.length() == 0)
@@ -478,6 +484,13 @@ void EncoderArguments::readOption(const std::vector<std::string>& options, size_
             }
         }
         break;
+    case 'm':
+        if (str.compare("-m") == 0)
+        {
+            // generate a material file
+            _outputMaterial = true;
+        }
+        break;
     case 'n':
         _normalMap = true;
         break;

+ 2 - 0
tools/encoder/src/EncoderArguments.h

@@ -154,6 +154,7 @@ public:
     bool fontPreviewEnabled() const;
     bool textOutputEnabled() const;
     bool optimizeAnimationsEnabled() const;
+    bool outputMaterialEnabled() const;
 
     const char* getNodeId() const;
     unsigned int getFontSize() const;
@@ -202,6 +203,7 @@ private:
     bool _textOutput;
     bool _optimizeAnimations;
     AnimationGroupOption _animationGrouping;
+    bool _outputMaterial;
 
     std::vector<std::string> _groupAnimationNodeId;
     std::vector<std::string> _groupAnimationAnimationId;

+ 503 - 978
tools/encoder/src/FBXSceneEncoder.cpp

@@ -2,195 +2,17 @@
 
 #include <algorithm>
 #include <string>
+#include <sstream>
 
 #include "FBXSceneEncoder.h"
-#include "EncoderArguments.h"
+#include "FBXUtil.h"
+#include "Sampler.h"
 
 using namespace gameplay;
-
-/**
- * Returns the aspect ratio from the given camera.
- * 
- * @param fbxCamera The FBX camera to get the aspect ratio from.
- * 
- * @return The aspect ratio from the camera.
- */
-static float getAspectRatio(FbxCamera* fbxCamera);
-
-/**
- * Returns the field of view Y from the given camera.
- * 
- * @param fbxCamera The camera to get the fiew of view from.
- * 
- * @return The field of view Y.
- */
-static float getFieldOfView(FbxCamera* fbxCamera);
-
-/**
- * Loads the texture coordinates from given mesh's polygon part into the vertex.
- * 
- * @param fbxMesh The mesh to get the polygon from.
- * @param uvs The UV list to load tex coords from.
- * @param uvSetIndex The UV set index of the uvs.
- * @param polyIndex The index of the polygon in the mesh.
- * @param posInPoly The position of the vertex in the polygon.
- * @param meshVertexIndex The index of the vertex in the mesh.
- * @param vertex The vertex to copy the texture coordinates to.
- */
-static void loadTextureCoords(FbxMesh* fbxMesh, const FbxGeometryElementUV* uvs, int uvSetIndex, int polyIndex, int posInPoly, int meshVertexIndex, Vertex* vertex);
-
-/**
- * Loads the normal from the mesh and adds it to the given vertex.
- * 
- * @param fbxMesh The mesh to get the polygon from.
- * @param vertexIndex The vertex index in the mesh.
- * @param controlPointIndex The control point index.
- * @param vertex The vertex to copy to.
- */
-static void loadNormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
-
-/**
- * Loads the tangent from the mesh and adds it to the given vertex.
- * 
- * @param fbxMesh The mesh to load from.
- * @param vertexIndex The index of the vertex within fbxMesh.
- * @param controlPointIndex The control point index.
- * @param vertex The vertex to copy to.
- */
-static void loadTangent(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
-
-/**
- * Loads the binormal from the mesh and adds it to the given vertex.
- * 
- * @param fbxMesh The mesh to load from.
- * @param vertexIndex The index of the vertex within fbxMesh.
- * @param controlPointIndex The control point index.
- * @param vertex The vertex to copy to.
- */
-static void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
-
-/**
- * Loads the vertex diffuse color from the mesh and adds it to the given vertex.
- * 
- * @param fbxMesh The mesh to load from.
- * @param vertexIndex The index of the vertex within fbxMesh.
- * @param controlPointIndex The control point index.
- * @param vertex The vertex to copy to.
- */
-static void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
-
-/**
- * Loads the blend weight and blend indices data into the vertex.
- * 
- * @param vertexWeights List of vertex weights. The x member contains the blendIndices. The y member contains the blendWeights.
- * @param vertex The vertex to copy the blend data to.
- */
-static void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex);
-
-/**
- * Loads the blend weights and blend indices from the given mesh.
- * 
- * Each element of weights is a list of Vector2s where "x" is the blend index and "y" is the blend weight.
- * 
- * @param fbxMesh The mesh to load from.
- * @param weights List of blend weights and blend indices for each vertex.
- * 
- * @return True if this mesh has a mesh skin, false otherwise.
- */
-static bool loadBlendWeights(FbxMesh* fbxMesh, std::vector<std::vector<Vector2> >& weights);
-
-/**
- * Copies from an FBX matrix to a float[16] array.
- */
-static void copyMatrix(const FbxMatrix& fbxMatrix, float* matrix);
-
-/**
- * Copies from an FBX matrix to a gameplay matrix.
- */
-static void copyMatrix(const FbxMatrix& fbxMatrix, Matrix& matrix);
-
-/**
- * Finds the min and max start time and stop time of the given animation curve.
- * 
- * startTime is updated if the animation curve contains a start time that is less than startTime.
- * stopTime is updated if the animation curve contains a stop time that is greater than stopTime.
- * frameRate is updated if the animation curve contains a frame rate that is greater than frameRate.
- * 
- * @param animCurve The animation curve to read from.
- * @param startTime The min start time. (in/out)
- * @param stopTime The max stop time. (in/out)
- * @param frameRate The frame rate. (in/out)
- */
-static void findMinMaxTime(FbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate);
-
-/**
- * Appends key frame data to the given node for the specified animation target attribute.
- * 
- * @param fbxNode The node to get the matrix transform from.
- * @param channel The aniamtion channel to write values into.
- * @param time The time of the keyframe.
- * @param scale The evaluated scale for the keyframe.
- * @param rotation The evalulated rotation for the keyframe.
- * @param translation The evalulated translation for the keyframe.
-
- */
-static void appendKeyFrame(FbxNode* fbxNode, AnimationChannel* channel, float time, const Vector3& scale, const Quaternion& rotation, const Vector3& translation);
-
-/**
- * Decomposes the given node's matrix transform at the given time and copies to scale, rotation and translation.
- * 
- * @param fbxNode The node to get the matrix transform from.
- * @param time The time to get the matrix transform from.
- * @param scale The scale to copy to.
- * @param rotation The rotation to copy to.
- * @param translation The translation to copy to.
- */
-static void decompose(FbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation);
-
-/**
- * Creates an animation channel that targets the given node and target attribute using the given key times and key values.
- * 
- * @param fbxNode The node to target.
- * @param targetAttrib The attribute type to target.
- * @param keyTimes The key times for the animation channel.
- * @param keyValues The key values for the animation channel.
- * 
- * @return The newly created animation channel.
- */
-static AnimationChannel* createAnimationChannel(FbxNode* fbxNode, unsigned int targetAttrib, const std::vector<float>& keyTimes, const std::vector<float>& keyValues);
-
-void addScaleChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime);
-
-void addTranslateChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime);
-
-/**
- * Determines if it is possible to automatically group animations for mesh skins.
- * 
- * @param fbxScene The FBX scene to search.
- * 
- * @return True if there is at least one mesh skin that has animations that can be grouped.
- */
-bool isGroupAnimationPossible(FbxScene* fbxScene);
-bool isGroupAnimationPossible(FbxNode* fbxNode);
-bool isGroupAnimationPossible(FbxMesh* fbxMesh);
-
-/**
- * Recursively generates the tangents and binormals for all nodes that were specified in the command line arguments.
- */
-void generateTangentsAndBinormals(FbxNode* fbxNode, const EncoderArguments& arguments);
-
-FbxAnimCurve* getCurve(FbxPropertyT<FbxDouble3>& prop, FbxAnimLayer* animLayer, const char* pChannel)
-{
-#if FBXSDK_VERSION_MAJOR == 2013 && FBXSDK_VERSION_MINOR == 1
-    return prop.GetCurve<FbxAnimCurve>(animLayer, pChannel);
-#else
-    return prop.GetCurve(animLayer, pChannel);
-#endif
-}
-
-////////////////////////////////////
-// Member Functions
-////////////////////////////////////
+using std::string;
+using std::vector;
+using std::map;
+using std::ostringstream;
 
 FBXSceneEncoder::FBXSceneEncoder()
     : _groupAnimation(NULL), _autoGroupAnimations(false)
@@ -201,7 +23,7 @@ FBXSceneEncoder::~FBXSceneEncoder()
 {
 }
 
-void FBXSceneEncoder::write(const std::string& filepath, const EncoderArguments& arguments)
+void FBXSceneEncoder::write(const string& filepath, const EncoderArguments& arguments)
 {
     FbxManager* sdkManager = FbxManager::Create();
     FbxIOSettings *ios = FbxIOSettings::Create(sdkManager, IOSROOT);
@@ -232,10 +54,17 @@ void FBXSceneEncoder::write(const std::string& filepath, const EncoderArguments&
     }
 
     if (arguments.tangentBinormalIdCount() > 0)
+    {
         generateTangentsAndBinormals(fbxScene->GetRootNode(), arguments);
+    }
 
     print("Loading Scene.");
     loadScene(fbxScene);
+    if (arguments.outputMaterialEnabled())
+    {
+        print("Load materials");
+        loadMaterials(fbxScene);
+    }
     print("Loading animations.");
     loadAnimations(fbxScene, arguments);
     sdkManager->Destroy();
@@ -247,14 +76,14 @@ void FBXSceneEncoder::write(const std::string& filepath, const EncoderArguments&
         _gamePlayFile.groupMeshSkinAnimations();
     }
     
-    std::string outputFilePath = arguments.getOutputFilePath();
+    string outputFilePath = arguments.getOutputFilePath();
 
     if (arguments.textOutputEnabled())
     {
         int pos = outputFilePath.find_last_of('.');
         if (pos > 2)
         {
-            std::string path = outputFilePath.substr(0, pos);
+            string path = outputFilePath.substr(0, pos);
             path.append(".xml");
             LOG(1, "Saving debug file: %s\n", path.c_str());
             if (!_gamePlayFile.saveText(path))
@@ -271,6 +100,51 @@ void FBXSceneEncoder::write(const std::string& filepath, const EncoderArguments&
             LOG(1, "Error writing binary file: %s\n", outputFilePath.c_str());
         }
     }
+
+    // Write the material file
+    if (arguments.outputMaterialEnabled())
+    {
+        int pos = outputFilePath.find_last_of('.');
+        if (pos > 2)
+        {
+            string path = outputFilePath.substr(0, pos);
+            path.append(".material");
+            writeMaterial(path);
+        }
+    }
+}
+
+bool FBXSceneEncoder::writeMaterial(const string& filepath)
+{
+    FILE* file = fopen(filepath.c_str(), "w");
+    if (!file)
+    {
+        return false;
+    }
+    // Finds the base materials that are used.
+    std::set<Material*> baseMaterialsToWrite;
+    for (map<string, Material*>::iterator it = _materials.begin(); it != _materials.end(); ++it)
+    {
+        baseMaterialsToWrite.insert(it->second->getParent());
+    }
+
+    // Write the base materials that are used.
+    for (std::set<Material*>::iterator it = baseMaterialsToWrite.begin(); it != baseMaterialsToWrite.end(); ++it)
+    {
+        Material* material = *it;
+        material->writeMaterial(file);
+        fprintf(file, "\n");
+    }
+
+    // Write all of the non-base materials.
+    for (map<string, Material*>::iterator it = _materials.begin(); it != _materials.end(); ++it)
+    {
+        (*it).second->writeMaterial(file);
+        fprintf(file, "\n");
+    }
+
+    fclose(file);
+    return true;
 }
 
 void FBXSceneEncoder::loadScene(FbxScene* fbxScene)
@@ -393,7 +267,7 @@ void FBXSceneEncoder::loadAnimationChannels(FbxAnimLayer* animLayer, FbxNode* fb
     assert(stopTime >= 0.0f);
 
     // Determine which animation channels to create
-    std::vector<unsigned int> channelAttribs;
+    vector<unsigned int> channelAttribs;
     if (sx && sy && sz)
     {
         if (rx || ry || rz)
@@ -478,7 +352,7 @@ void FBXSceneEncoder::loadAnimationChannels(FbxAnimLayer* animLayer, FbxNode* fb
     assert(channelCount > 0);
 
     // Allocate channel list
-    int channelStart = animation->getAnimationChannelCount();
+    const int channelStart = animation->getAnimationChannelCount();
     for (unsigned int i = 0; i < channelCount; ++i)
     {
         AnimationChannel* channel = new AnimationChannel();
@@ -519,13 +393,12 @@ void FBXSceneEncoder::loadAnimationChannels(FbxAnimLayer* animLayer, FbxNode* fb
         // Append keyframe data to all channels
         for (unsigned int i = channelStart, channelEnd = channelStart + channelCount; i < channelEnd; ++i)
         {
-            appendKeyFrame(fbxNode, animation->getAnimationChannel(i), time, scale, rotation, translation);
+            appendKeyFrame(fbxNode, animation->getAnimationChannel(i), (float)time, scale, rotation, translation);
         }
     }
 
     if (_groupAnimation != animation)
     {
-        // TODO explain
         _gamePlayFile.addAnimation(animation);
     }
 }
@@ -570,13 +443,13 @@ void FBXSceneEncoder::loadAnimations(FbxScene* fbxScene, const EncoderArguments&
     if (!animStack)
         return;
 
-    for (int i = 0; i < fbxScene->GetSrcObjectCount(FBX_TYPE(FbxAnimStack)); ++i)
+    for (int i = 0; i < fbxScene->GetSrcObjectCount<FbxAnimStack>(); ++i)
     {
-        FbxAnimStack* animStack = FbxCast<FbxAnimStack>(fbxScene->GetSrcObject(FBX_TYPE(FbxAnimStack), i));
-        int nbAnimLayers = animStack->GetMemberCount(FBX_TYPE(FbxAnimLayer));
+        FbxAnimStack* animStack = FbxCast<FbxAnimStack>(fbxScene->GetSrcObject<FbxAnimStack>(i));
+        int nbAnimLayers = animStack->GetMemberCount<FbxAnimLayer>();
         for (int l = 0; l < nbAnimLayers; ++l)
         {
-            FbxAnimLayer* animLayer = animStack->GetMember(FBX_TYPE(FbxAnimLayer), l);
+            FbxAnimLayer* animLayer = animStack->GetMember<FbxAnimLayer>(l);
             loadAnimationLayer(animLayer, fbxScene->GetRootNode(), arguments);
         }
     }
@@ -626,13 +499,14 @@ Node* FBXSceneEncoder::loadNode(FbxNode* fbxNode)
             node->addChild(child);
         }
     }
+    _nodeMap[fbxNode] = node;
     return node;
 }
 
 Mesh* FBXSceneEncoder::getMesh(FbxUInt64 meshId)
 {
     // Check if this mesh was already loaded.
-    std::map<FbxUInt64, Mesh*>::iterator it = _meshes.find(meshId);
+    map<FbxUInt64, Mesh*>::iterator it = _meshes.find(meshId);
     if (it != _meshes.end())
     {
         return it->second;
@@ -657,19 +531,185 @@ void FBXSceneEncoder::print(const char* str)
 void FBXSceneEncoder::transformNode(FbxNode* fbxNode, Node* node)
 {
     FbxAMatrix matrix;
+    float m[16];
+    
     if (fbxNode->GetCamera() || fbxNode->GetLight())
     {
-        // TODO: Why is this necessary for Camera and Light?
-        matrix.SetTRS(fbxNode->LclTranslation.Get(), fbxNode->LclRotation.Get(), fbxNode->LclScaling.Get());
+        FbxAMatrix rotateAdjust;
+        
+        if(fbxNode->GetLight())
+        {
+            /*
+             * according to the fbx-documentation the light's forward vector
+             * points along a node's negative Y axis.
+             * so we have to rotate it by 90° around the X-axis to correct it.
+             */
+            if(fbxNode->RotationActive.Get())
+            {
+                const FbxVector4& postRotation = fbxNode->PostRotation.Get();
+                fbxNode->SetPostRotation(FbxNode::eSourcePivot, FbxVector4(postRotation.mData[0] + 90.0,
+                                                                           postRotation.mData[1],
+                                                                           postRotation.mData[2])
+                                         );
+            }
+            else
+            {
+                // if the rotation is deactivated we have to rotate it anyway to get the correct transformation in the end
+                rotateAdjust.SetR(FbxVector4(-90.0, 0.0, 0.0));
+            }
+            
+            matrix = fbxNode->EvaluateLocalTransform() * rotateAdjust;
+        }
+        else if(fbxNode->GetCamera())
+        {
+            // TODO: use the EvaluateLocalTransform() function for the transformations for the camera
+            /*
+             * the current implementation ignores pre- and postrotation among others (usually happens with fbx-export from blender)
+             *
+             * Some info for a future implementation:
+             * according to the fbx-documentation the camera's forward vector
+             * points along a node's positive X axis.
+             * so we have to correct it if we use the EvaluateLocalTransform-function
+             * just rotating it by 90° around the Y axis (similar to above) doesn't work
+             */
+            matrix.SetTRS(fbxNode->LclTranslation.Get(), fbxNode->LclRotation.Get(), fbxNode->LclScaling.Get());
+        }
+        
+        copyMatrix(matrix, m);
+        node->setTransformMatrix(m);
     }
     else
     {
         matrix = fbxNode->EvaluateLocalTransform();
+        copyMatrix(matrix, m);
+        node->setTransformMatrix(m);
     }
+}
 
-    float m[16];
-    copyMatrix(matrix, m);
-    node->setTransformMatrix(m);
+Material* FBXSceneEncoder::getBaseMaterial(const char* id)
+{
+    map<string, Material*>::iterator it = _baseMaterials.find(string(id));
+    if (it != _baseMaterials.end())
+    {
+        return it->second;
+    }
+    return NULL;
+}
+
+static string getBaseMaterialName(Material* material)
+{
+    ostringstream baseName;
+    if (material->isTextured())
+    {
+        baseName << "Textured";
+    }
+    else
+    {
+        baseName << "Colored";
+    }
+
+    if (material->isLit())
+    {
+        if (material->isSpecular())
+        {
+            baseName << "Specular";
+        }
+    }
+    else
+    {
+        baseName << "Unlit";
+    }
+    return baseName.str();
+}
+
+Material* FBXSceneEncoder::findBaseMaterial(Material* material)
+{
+    string baseMaterialName = getBaseMaterialName(material);
+    Material* baseMaterial = getBaseMaterial(baseMaterialName.c_str());
+    if (baseMaterial)
+    {
+        return baseMaterial;
+    }
+    baseMaterial = createBaseMaterial(baseMaterialName, material);
+    _baseMaterials[baseMaterial->getId()] = baseMaterial;
+    return baseMaterial;
+}
+
+Node* FBXSceneEncoder::findNode(FbxNode* fbxNode)
+{
+    if (fbxNode)
+    {
+        map<FbxNode*, Node*>::const_iterator it = _nodeMap.find(fbxNode);
+        if (it != _nodeMap.end())
+        {
+            return it->second;
+        }
+    }
+    return NULL;
+}
+
+Material* FBXSceneEncoder::createBaseMaterial(const string& baseMaterialName, Material* childMaterial)
+{
+    Material* baseMaterial = new Material(baseMaterialName);
+    baseMaterial->setUniform("u_worldViewProjectionMatrix", "WORLD_VIEW_PROJECTION_MATRIX");
+    baseMaterial->setRenderState("cullFace", "true");
+    baseMaterial->setRenderState("depthTest", "true");
+    if (childMaterial->isLit())
+    {
+        baseMaterial->setLit(true);
+        baseMaterial->setUniform("u_inverseTransposeWorldViewMatrix", "INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX");
+        // Always use directional light
+        baseMaterial->setUniform("u_lightDirection", "SCENE_LIGHT_DIRECTION");
+        baseMaterial->setUniform("u_lightColor", "SCENE_LIGHT_COLOR");
+
+        if (childMaterial->isSpecular())
+        {
+            baseMaterial->addDefine(SPECULAR);
+            baseMaterial->setUniform("u_cameraPosition", "CAMERA_WORLD_POSITION");
+        }
+    }
+    if (childMaterial->isTextured())
+    {
+        if (childMaterial->isLit())
+        {
+            if (childMaterial->isBumped())
+            {
+                baseMaterial->setVertexShader("res/shaders/textured-bumped.vert");
+                baseMaterial->setFragmentShader("res/shaders/textured-bumped.frag");
+            }
+            else
+            {
+                baseMaterial->setVertexShader("res/shaders/textured.vert");
+                baseMaterial->setFragmentShader("res/shaders/textured.frag");
+            }
+        }
+        else
+        {
+            baseMaterial->setVertexShader("res/shaders/textured-unlit.vert");
+            baseMaterial->setFragmentShader("res/shaders/textured-unlit.frag");
+        }
+        Sampler* sampler = baseMaterial->createSampler(u_diffuseTexture);
+        sampler->set("mipmap", "true");
+        sampler->set("wrapS", CLAMP);
+        sampler->set("wrapT", CLAMP);
+        sampler->set(MIN_FILTER, LINEAR_MIPMAP_LINEAR);
+        sampler->set(MAG_FILTER, LINEAR);
+    }
+    else
+    {
+        if (childMaterial->isLit())
+        {
+            baseMaterial->setVertexShader("res/shaders/colored.vert");
+            baseMaterial->setFragmentShader("res/shaders/colored.frag");
+        }
+        else
+        {
+            baseMaterial->setVertexShader("res/shaders/colored-unlit.vert");
+            baseMaterial->setFragmentShader("res/shaders/colored-unlit.frag");
+        }
+    }
+    assert(baseMaterial);
+    return baseMaterial;
 }
 
 void FBXSceneEncoder::loadBindShapes(FbxScene* fbxScene)
@@ -711,7 +751,7 @@ void FBXSceneEncoder::loadCamera(FbxNode* fbxNode, Node* node)
     const char* name = fbxNode->GetName();
     if (name)
     {
-        std::string id(name);
+        string id(name);
         id.append("_Camera");
         camera->setId(id);
     }
@@ -752,7 +792,7 @@ void FBXSceneEncoder::loadLight(FbxNode* fbxNode, Node* node)
     const char* name = fbxNode->GetName();
     if (name)
     {
-        std::string id(name);
+        string id(name);
         id.append("_Light");
         light->setId(id);
     }
@@ -811,7 +851,7 @@ void FBXSceneEncoder::loadLight(FbxNode* fbxNode, Node* node)
             break;
         case FbxLight::eLinear:
             light->setLinearAttenuation((float)fbxLight->DecayStart.Get());
-            break;  
+            break;
         case FbxLight::eQuadratic:
             light->setQuadraticAttenuation((float)fbxLight->DecayStart.Get());
             break;
@@ -850,12 +890,257 @@ void FBXSceneEncoder::loadModel(FbxNode* fbxNode, Node* node)
         loadSkin(fbxMesh, model);
         if (model->getSkin())
         {
-            // TODO: explain
             node->resetTransformMatrix();
         }
     }
 }
 
+void FBXSceneEncoder::loadMaterials(FbxScene* fbxScene)
+{
+    FbxNode* rootNode = fbxScene->GetRootNode();
+    if (rootNode)
+    {
+        // Don't include the FBX root node
+        const int childCount = rootNode->GetChildCount();
+        for (int i = 0; i < childCount; ++i)
+        {
+            FbxNode* fbxNode = rootNode->GetChild(i);
+            if (fbxNode)
+            {
+                loadMaterial(fbxNode);
+            }
+        }
+    }
+}
+
+void FBXSceneEncoder::loadMaterial(FbxNode* fbxNode)
+{
+    Node* node = findNode(fbxNode);
+    Model* model = (node) ? node->getModel() : NULL;
+
+    const int materialCount = fbxNode->GetMaterialCount();
+    for (int index = 0; index < materialCount; ++index)
+    {
+        FbxSurfaceMaterial* fbxMaterial = fbxNode->GetMaterial(index);
+        const string materialName(fbxMaterial->GetName());
+        Material* material = NULL;
+        map<string, Material*>::iterator it = _materials.find(materialName);
+        if (it != _materials.end())
+        {
+            // This material was already loaded so don't load it again
+            material = it->second;
+        }
+        else
+        {
+            material = createMaterial(materialName, fbxMaterial, node);
+            _materials[materialName] = material;
+        }
+
+        if (materialCount == 1 && material && model)
+        {
+            model->setMaterial(material); // TODO: add support for materials per mesh part
+        }
+        else if (materialCount > 1 && material && model)
+        {
+            model->setMaterial(material, index);
+        }
+    }
+
+    const int childCount = fbxNode->GetChildCount();
+    for (int i = 0; i < childCount; ++i)
+    {
+        FbxNode* childNode = fbxNode->GetChild(i);
+        if (childNode)
+        {
+            loadMaterial(childNode);
+        }
+    }
+}
+
+void FBXSceneEncoder::loadMaterialTextures(FbxSurfaceMaterial* fbxMaterial, Material* material)
+{
+    FbxProperty fbxProperty;
+    int textureIndex;
+    FBXSDK_FOR_EACH_TEXTURE(textureIndex)
+    {
+        fbxProperty = fbxMaterial->FindProperty(FbxLayerElement::sTextureChannelNames[textureIndex]);
+        //FindAndDisplayTextureInfoByProperty(fbxProperty, lDisplayHeader, lMaterialIndex);
+        if ( fbxProperty.IsValid() )
+        {
+            int textureCount = fbxProperty.GetSrcObjectCount<FbxTexture>();
+            for (int j = 0; j < textureCount; ++j)
+            {
+                FbxLayeredTexture *layeredTexture = fbxProperty.GetSrcObject<FbxLayeredTexture>(j);
+                if (layeredTexture)
+                {
+                    //DisplayInt("    Layered Texture: ", j);
+                    FbxLayeredTexture *layeredTexture = fbxProperty.GetSrcObject<FbxLayeredTexture>(j);
+                    int lNbTextures = layeredTexture->GetSrcObjectCount<FbxTexture>();
+                    for (int k = 0; k<lNbTextures; ++k)
+                    {
+                        FbxTexture* fbxTexture = layeredTexture->GetSrcObject<FbxTexture>(k);
+                        if (fbxTexture)
+                        {
+                            /*
+                            if (pDisplayHeader){
+                                DisplayInt("    Textures connected to Material ", pMaterialIndex);
+                                pDisplayHeader = false;
+                            }
+                            */
+
+                            //NOTE the blend mode is ALWAYS on the LayeredTexture and NOT the one on the texture.
+                            //Why is that?  because one texture can be shared on different layered textures and might
+                            //have different blend modes.
+
+                            FbxLayeredTexture::EBlendMode lBlendMode;
+                            layeredTexture->GetTextureBlendMode(k, lBlendMode);
+                            //DisplayString("    Textures for ", pProperty.GetName());
+                            //DisplayInt("        Texture ", k);
+                            //DisplayTextureInfo(fbxTexture, (int) lBlendMode);
+                        }
+
+                    }
+                }
+                else if (FbxTexture* fbxTexture = fbxProperty.GetSrcObject<FbxTexture>(j))
+                {
+                    //no layered texture simply get on the property
+                    if (FbxFileTexture* fileTexture = FbxCast<FbxFileTexture>(fbxTexture))
+                    {
+                        loadMaterialFileTexture(fileTexture, material);
+                    }
+                }
+            }
+        }
+    }
+}
+
+void FBXSceneEncoder::loadMaterialFileTexture(FbxFileTexture* fileTexture, Material* material)
+{
+    FbxTexture::ETextureUse textureUse = fileTexture->GetTextureUse();
+    Sampler* sampler = NULL;
+    if (textureUse == FbxTexture::eStandard)
+    {
+        if (!material->getSampler("u_diffuseTexture"))
+            sampler = material->createSampler("u_diffuseTexture");
+    }
+    else if (textureUse == FbxTexture::eBumpNormalMap)
+    {
+        if (!material->getSampler("u_normalmapTexture"))
+            sampler = material->createSampler("u_normalmapTexture");
+    }
+    if (sampler)
+    {
+        sampler->set("absolutePath", fileTexture->GetFileName());
+        sampler->set("relativePath", fileTexture->GetRelativeFileName());
+        sampler->set("wrapS", fileTexture->GetWrapModeU() == FbxTexture::eClamp ? CLAMP : REPEAT);
+        sampler->set("wrapT", fileTexture->GetWrapModeV() == FbxTexture::eClamp ? CLAMP : REPEAT);
+        //sampler->set(MIN_FILTER, LINEAR_MIPMAP_LINEAR);
+        //sampler->set(MAG_FILTER, LINEAR);
+
+        if (textureUse == FbxTexture::eStandard)
+        {
+            double scaleU = fileTexture->GetScaleU();
+            double scaleV = fileTexture->GetScaleV();
+            if (scaleU != 1 || scaleV != 1)
+            {
+                ostringstream stream;
+                stream << scaleU << ", " << scaleV;
+                material->setUniform("u_textureRepeat", stream.str());
+                material->addDefine(TEXTURE_REPEAT);
+            }
+
+            double translationU = fileTexture->GetTranslationU();
+            double translationV = fileTexture->GetTranslationV();
+            if (translationU != 0 || translationV != 0)
+            {
+                ostringstream stream;
+                stream << translationU << ", " << translationV;
+                material->setUniform("u_textureOffset", stream.str());
+                material->addDefine(TEXTURE_OFFSET);
+            }
+        }
+    }
+}
+
+void FBXSceneEncoder::loadMaterialUniforms(FbxSurfaceMaterial* fbxMaterial, Material* material)
+{
+    
+    if ( fbxMaterial->GetClassId().Is(FbxSurfaceLambert::ClassId) )
+    {
+        FbxSurfaceLambert* lambert = FbxCast<FbxSurfaceLambert>(fbxMaterial);
+
+        if (material->isLit())
+        {
+            FbxDouble3 ambient = lambert->Ambient.Get();
+            if (!isBlack(ambient))
+            {
+                material->setUniform("u_ambientColor", toString(ambient));
+            }
+        }
+        if (!material->isTextured())
+        {
+            if (!material->isDefined(VERTEX_COLOR))
+            {
+                FbxDouble3 diffuse = lambert->Diffuse.Get();
+                if (!isBlack(diffuse))
+                {
+                    material->setUniform("u_diffuseColor", toString(diffuse, 1.0));
+                }
+            }
+        }
+    }
+    if (fbxMaterial->GetClassId().Is(FbxSurfacePhong::ClassId))
+    {
+        FbxSurfacePhong* phong = FbxCast<FbxSurfacePhong>(fbxMaterial);
+        //FbxDouble specularFactor = phong->SpecularFactor.Get();
+        if (material->isLit())
+        {
+            FbxDouble shininess = phong->Shininess.Get();
+            if (shininess > 0)
+            {
+                ostringstream stream;
+                stream << shininess;
+                material->setUniform("u_specularExponent", stream.str());
+                material->addDefine(SPECULAR);
+            }
+        }
+        //
+        //((FbxSurfacePhong *) fbxMaterial)->GetAmbientColor();
+        //((FbxSurfacePhong *) fbxMaterial)->GetDiffuseColor();
+    }
+}
+
+Material* FBXSceneEncoder::createMaterial(const string& name, FbxSurfaceMaterial* fbxMaterial, Node* node)
+{
+    assert(fbxMaterial);
+    Material* material = new Material(name);
+    Model* model = (node) ? node->getModel() : NULL;
+    Mesh* mesh = (model) ? model->getMesh() : NULL;
+    if (mesh)
+    {
+        // The material should be lit if the model has normals or there are lights in the scene.
+        material->setLit(mesh->hasNormals() || _gamePlayFile.getLightCount() > 0);
+        if (mesh->hasVertexColors())
+        {
+            material->addDefine(VERTEX_COLOR);
+        }
+    }
+    MeshSkin* skin = (model) ? model->getSkin() : NULL;
+    if (skin && skin->getJointCount() > 0)
+    {
+        material->setUniform("u_matrixPalette", "MATRIX_PALETTE");
+        material->addDefine("SKINNING");
+        ostringstream stream;
+        stream << "SKINNING_JOINT_COUNT " << skin->getJointCount();
+        material->addDefine(stream.str());
+    }
+    loadMaterialTextures(fbxMaterial, material);
+    loadMaterialUniforms(fbxMaterial, material);
+    material->setParent(findBaseMaterial(material));
+    assert(material);
+    return material;
+}
+
 void FBXSceneEncoder::loadSkin(FbxMesh* fbxMesh, Model* model)
 {
     const int deformerCount = fbxMesh->GetDeformerCount();
@@ -864,13 +1149,13 @@ void FBXSceneEncoder::loadSkin(FbxMesh* fbxMesh, Model* model)
         FbxDeformer* deformer = fbxMesh->GetDeformer(i);
         if (deformer->GetDeformerType() == FbxDeformer::eSkin)
         {
-            FbxSkin* fbxSkin = static_cast<FbxSkin*>(deformer);
+            FbxSkin* fbxSkin = FbxCast<FbxSkin>(deformer);
 
             MeshSkin* skin = new MeshSkin();
 
-            std::vector<std::string> jointNames;
-            std::vector<Node*> joints;
-            std::vector<Matrix> bindPoses;
+            vector<string> jointNames;
+            vector<Node*> joints;
+            vector<Matrix> bindPoses;
 
             const int clusterCount = fbxSkin->GetClusterCount();
             for (int j = 0; j < clusterCount; ++j)
@@ -916,14 +1201,14 @@ Mesh* FBXSceneEncoder::loadMesh(FbxMesh* fbxMesh)
     const char* name = fbxMesh->GetNode()->GetName();
     if (name)
     {
-        std::string id(name);
+        string id(name);
         id.append("_Mesh");
         mesh->setId(id);
     }
 
     // The number of mesh parts is equal to the number of materials that affect this mesh.
     // There is always at least one mesh part.
-    std::vector<MeshPart*> meshParts;
+    vector<MeshPart*> meshParts;
     const int materialCount = fbxMesh->GetNode()->GetMaterialCount();
     int meshPartSize = (materialCount > 0) ? materialCount : 1;
     for (int i = 0; i < meshPartSize; ++i)
@@ -932,7 +1217,7 @@ Mesh* FBXSceneEncoder::loadMesh(FbxMesh* fbxMesh)
     }
 
     // Find the blend weights and blend indices if this mesh is skinned.
-    std::vector<std::vector<Vector2> > weights;
+    vector<vector<Vector2> > weights;
     bool hasSkin = loadBlendWeights(fbxMesh, weights);
     
     // Get list of uv sets for mesh
@@ -1056,13 +1341,13 @@ void FBXSceneEncoder::triangulateRecursive(FbxNode* fbxNode)
 {
     // Triangulate all NURBS, patch and mesh under this node recursively.
     FbxNodeAttribute* nodeAttribute = fbxNode->GetNodeAttribute();
-
     if (nodeAttribute)
     {
-        if (nodeAttribute->GetAttributeType() == FbxNodeAttribute::eMesh ||
-            nodeAttribute->GetAttributeType() == FbxNodeAttribute::eNurbs ||
-            nodeAttribute->GetAttributeType() == FbxNodeAttribute::eNurbsSurface ||
-            nodeAttribute->GetAttributeType() == FbxNodeAttribute::ePatch)
+        FbxNodeAttribute::EType type = nodeAttribute->GetAttributeType();
+        if (type == FbxNodeAttribute::eMesh ||
+            type == FbxNodeAttribute::eNurbs ||
+            type == FbxNodeAttribute::eNurbsSurface ||
+            type == FbxNodeAttribute::ePatch)
         {
             FbxGeometryConverter converter(fbxNode->GetFbxManager());
             converter.TriangulateInPlace(fbxNode);
@@ -1076,764 +1361,4 @@ void FBXSceneEncoder::triangulateRecursive(FbxNode* fbxNode)
     }
 }
 
-////////////////////////////////////
-// Functions
-////////////////////////////////////
-
-float getAspectRatio(FbxCamera* fbxCamera)
-{
-    return (float)fbxCamera->FilmAspectRatio.Get();
-    /*
-    FbxCamera::ECameraAspectRatioMode camAspectRatioMode = fbxCamera->GetAspectRatioMode();
-    double aspectX = fbxCamera->AspectWidth.Get();
-    double aspectY = fbxCamera->AspectHeight.Get();
-    double aspectRatio = 1.333333;
-    switch ( camAspectRatioMode)
-    {
-    case FbxCamera::eWINDOW_SIZE:
-        aspectRatio = aspectX / aspectY;
-        break;
-    case FbxCamera::eFIXED_RATIO:
-        aspectRatio = aspectX;
-        break;
-    case FbxCamera::eFIXED_RESOLUTION:
-        aspectRatio = aspectX / aspectY * fbxCamera->GetPixelRatio();
-        break;
-    case FbxCamera::eFIXED_WIDTH:
-        aspectRatio = fbxCamera->GetPixelRatio() / aspectY;
-        break;
-    case FbxCamera::eFIXED_HEIGHT:
-        aspectRatio = fbxCamera->GetPixelRatio() * aspectX;
-        break;
-    default:
-        break;
-    }
-    return (float)aspectRatio;
-    */
-}
-
-inline double vfov(double hfov, double aspect)
-{
-    static const double MATH_PI_180 = 0.01745329251994329576923690768489;
-    static const double MATH_180_PI = 57.295779513082320876798154814105;
-    return (2.0 * atan((aspect) * tan( (hfov * MATH_PI_180) * 0.5)) * MATH_180_PI);
-}
-
-float getFieldOfView(FbxCamera* fbxCamera)
-{
-    double fieldOfViewX = 0.0;
-    double fieldOfViewY = 0.0;
-    double filmHeight = fbxCamera->GetApertureHeight();
-    double filmWidth = fbxCamera->GetApertureWidth() * fbxCamera->GetSqueezeRatio();
-    double apertureRatio = filmHeight / filmWidth;
-    if ( fbxCamera->GetApertureMode() == FbxCamera::eVertical)
-    {
-        fieldOfViewY = fbxCamera->FieldOfView.Get();
-    }
-    else if (fbxCamera->GetApertureMode() == FbxCamera::eHorizontal)
-    {
-        fieldOfViewX = fbxCamera->FieldOfView.Get();
-        fieldOfViewY = vfov( fieldOfViewX, apertureRatio);
-    }
-    else if (fbxCamera->GetApertureMode() == FbxCamera::eFocalLength)
-    {
-        fieldOfViewX = fbxCamera->ComputeFieldOfView(fbxCamera->FocalLength.Get());
-        fieldOfViewY = vfov( fieldOfViewX, apertureRatio);
-    }
-    else if (fbxCamera->GetApertureMode() == FbxCamera::eHorizAndVert)
-    {
-        fieldOfViewY = fbxCamera->FieldOfViewY.Get();
-    }
-    else
-    {
-        fieldOfViewY = 45.0;
-    }
-    return (float)fieldOfViewY;
-}
-
-void loadTextureCoords(FbxMesh* fbxMesh, const FbxGeometryElementUV* uvs, int uvSetIndex, int polyIndex, int posInPoly, int meshVertexIndex, Vertex* vertex)
-{
-    assert(fbxMesh && polyIndex >=0 && posInPoly >= 0);
-
-    const bool useIndex = uvs->GetReferenceMode() != FbxGeometryElement::eDirect;
-    const int indexCount = useIndex ? uvs->GetIndexArray().GetCount() : 0;
-    int uvIndex = -1;
-
-    switch (uvs->GetMappingMode())
-    {
-    case FbxGeometryElement::eByControlPoint:
-        {
-            // Get the index of the current vertex in control points array
-            int polyVertIndex = fbxMesh->GetPolygonVertex(polyIndex, posInPoly);
-
-            // The UV index depends on the reference mode
-            uvIndex = useIndex ? uvs->GetIndexArray().GetAt(polyVertIndex) : polyVertIndex;
-        }
-        break;
-
-    case FbxGeometryElement::eByPolygonVertex:
-        if (meshVertexIndex < indexCount)
-        {
-            uvIndex = useIndex ? uvs->GetIndexArray().GetAt(meshVertexIndex) : meshVertexIndex;
-        }
-        break;
-
-    default:
-        // Only support eByPolygonVertex and eByControlPoint mappings
-        break;
-    }
-
-    vertex->hasTexCoord[uvSetIndex] = true;
-
-    // Store UV information in vertex
-    if (uvIndex != -1)
-    {
-        FbxVector2 uvValue = uvs->GetDirectArray().GetAt(uvIndex);
-        vertex->texCoord[uvSetIndex].x = (float)uvValue[0];
-        vertex->texCoord[uvSetIndex].y = (float)uvValue[1];
-    }
-}
-
-void loadNormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
-{
-    if (fbxMesh->GetElementNormalCount() > 0)
-    {
-        // Get only the first
-        FbxGeometryElementNormal* normal = fbxMesh->GetElementNormal(0);
-        FbxGeometryElement::EMappingMode mappingMode = normal->GetMappingMode();
-        if (mappingMode == FbxGeometryElement::eByControlPoint)
-        {
-            switch (normal->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(controlPointIndex);
-                    vertex->hasNormal = true;
-                    vertex->normal.x = (float)vec4[0];
-                    vertex->normal.y = (float)vec4[1];
-                    vertex->normal.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = normal->GetIndexArray().GetAt(controlPointIndex);
-                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(id);
-                    vertex->hasNormal = true;
-                    vertex->normal.x = (float)vec4[0];
-                    vertex->normal.y = (float)vec4[1];
-                    vertex->normal.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
-        {
-            switch (normal->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(vertexIndex);
-                    vertex->hasNormal = true;
-                    vertex->normal.x = (float)vec4[0];
-                    vertex->normal.y = (float)vec4[1];
-                    vertex->normal.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = normal->GetIndexArray().GetAt(vertexIndex);
-                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(id);
-                    vertex->hasNormal = true;
-                    vertex->normal.x = (float)vec4[0];
-                    vertex->normal.y = (float)vec4[1];
-                    vertex->normal.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-void loadTangent(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
-{
-    if (fbxMesh->GetElementTangentCount() > 0)
-    {
-        // Get only the first tangent
-        FbxGeometryElementTangent* tangent = fbxMesh->GetElementTangent(0);
-        FbxGeometryElement::EMappingMode mappingMode = tangent->GetMappingMode();
-        if (mappingMode == FbxGeometryElement::eByControlPoint)
-        {
-            switch (tangent->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(controlPointIndex);
-                    vertex->hasTangent = true;
-                    vertex->tangent.x = (float)vec4[0];
-                    vertex->tangent.y = (float)vec4[1];
-                    vertex->tangent.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = tangent->GetIndexArray().GetAt(controlPointIndex);
-                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(id);
-                    vertex->hasTangent = true;
-                    vertex->tangent.x = (float)vec4[0];
-                    vertex->tangent.y = (float)vec4[1];
-                    vertex->tangent.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
-        {
-            switch (tangent->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(vertexIndex);
-                    vertex->hasTangent = true;
-                    vertex->tangent.x = (float)vec4[0];
-                    vertex->tangent.y = (float)vec4[1];
-                    vertex->tangent.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = tangent->GetIndexArray().GetAt(vertexIndex);
-                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(id);
-                    vertex->hasTangent = true;
-                    vertex->tangent.x = (float)vec4[0];
-                    vertex->tangent.y = (float)vec4[1];
-                    vertex->tangent.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
-{
-    if (fbxMesh->GetElementBinormalCount() > 0)
-    {
-        // Get only the first binormal.
-        FbxGeometryElementBinormal* binormal = fbxMesh->GetElementBinormal(0);
-        FbxGeometryElement::EMappingMode mappingMode = binormal->GetMappingMode();
-
-        if (mappingMode == FbxGeometryElement::eByControlPoint)
-        {
-            switch (binormal->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(controlPointIndex);
-                    vertex->hasBinormal = true;
-                    vertex->binormal.x = (float)vec4[0];
-                    vertex->binormal.y = (float)vec4[1];
-                    vertex->binormal.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = binormal->GetIndexArray().GetAt(controlPointIndex);
-                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(id);
-                    vertex->hasBinormal = true;
-                    vertex->binormal.x = (float)vec4[0];
-                    vertex->binormal.y = (float)vec4[1];
-                    vertex->binormal.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
-        {
-            switch (binormal->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(vertexIndex);
-                    vertex->hasBinormal = true;
-                    vertex->binormal.x = (float)vec4[0];
-                    vertex->binormal.y = (float)vec4[1];
-                    vertex->binormal.z = (float)vec4[2];
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = binormal->GetIndexArray().GetAt(vertexIndex);
-                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(id);
-                    vertex->hasBinormal = true;
-                    vertex->binormal.x = (float)vec4[0];
-                    vertex->binormal.y = (float)vec4[1];
-                    vertex->binormal.z = (float)vec4[2];
-                }
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
-{
-    if (fbxMesh->GetElementVertexColorCount() > 0)
-    {
-        // Get only the first vertex color.
-        FbxGeometryElementVertexColor* vertexColor = fbxMesh->GetElementVertexColor(0);
-        FbxGeometryElement::EMappingMode mappingMode = vertexColor->GetMappingMode();
-        if (mappingMode == FbxGeometryElement::eByControlPoint)
-        {
-            switch (vertexColor->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxColor color = vertexColor->GetDirectArray().GetAt(controlPointIndex);
-
-                    vertex->hasDiffuse = true;
-                    vertex->diffuse.x = (float)color.mRed;
-                    vertex->diffuse.y = (float)color.mGreen;
-                    vertex->diffuse.z = (float)color.mBlue;
-                    vertex->diffuse.w = (float)color.mAlpha;
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = vertexColor->GetIndexArray().GetAt(controlPointIndex);
-                    FbxColor color = vertexColor->GetDirectArray().GetAt(id);
-
-                    vertex->hasDiffuse = true;
-                    vertex->diffuse.x = (float)color.mRed;
-                    vertex->diffuse.y = (float)color.mGreen;
-                    vertex->diffuse.z = (float)color.mBlue;
-                    vertex->diffuse.w = (float)color.mAlpha;
-                }
-                break;
-            default:
-                break;
-            }
-        }
-        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
-        {
-            switch (vertexColor->GetReferenceMode())
-            {
-            case FbxGeometryElement::eDirect:
-                {
-                    FbxColor color = vertexColor->GetDirectArray().GetAt(vertexIndex);
-
-                    vertex->hasDiffuse = true;
-                    vertex->diffuse.x = (float)color.mRed;
-                    vertex->diffuse.y = (float)color.mGreen;
-                    vertex->diffuse.z = (float)color.mBlue;
-                    vertex->diffuse.w = (float)color.mAlpha;
-                }
-                break;
-            case FbxGeometryElement::eIndexToDirect:
-                {
-                    int id = vertexColor->GetIndexArray().GetAt(vertexIndex);
-                    FbxColor color = vertexColor->GetDirectArray().GetAt(id);
-
-                    vertex->hasDiffuse = true;
-                    vertex->diffuse.x = (float)color.mRed;
-                    vertex->diffuse.y = (float)color.mGreen;
-                    vertex->diffuse.z = (float)color.mBlue;
-                    vertex->diffuse.w = (float)color.mAlpha;
-                }
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex)
-{
-    size_t size = vertexWeights.size();
-
-    if (size >= 1)
-    {
-        vertex->hasWeights= true;
-        vertex->blendIndices.x = vertexWeights[0].x;
-        vertex->blendWeights.x = vertexWeights[0].y;
-    }
-    if (size >= 2)
-    {
-        vertex->blendIndices.y = vertexWeights[1].x;
-        vertex->blendWeights.y = vertexWeights[1].y;
-    }
-    if (size >= 3)
-    {
-        vertex->blendIndices.z = vertexWeights[2].x;
-        vertex->blendWeights.z = vertexWeights[2].y;
-    }
-    if (size >= 4)
-    {
-        vertex->blendIndices.w = vertexWeights[3].x;
-        vertex->blendWeights.w = vertexWeights[3].y;
-    }
-    //vertex->normalizeBlendWeight();
-}
-
-bool loadBlendWeights(FbxMesh* fbxMesh, std::vector<std::vector<Vector2> >& weights)
-{
-    assert(fbxMesh);
-    const int vertexCount = fbxMesh->GetControlPointsCount();
-
-    FbxSkin* fbxSkin = NULL;
-    const int deformerCount = fbxMesh->GetDeformerCount();
-    for (int i = 0; i < deformerCount; ++i)
-    {
-        FbxDeformer* deformer = fbxMesh->GetDeformer(i);
-        if (deformer->GetDeformerType() == FbxDeformer::eSkin)
-        {
-            fbxSkin = static_cast<FbxSkin*>(deformer);
-            weights.resize(vertexCount);
-
-            const int clusterCount = fbxSkin->GetClusterCount();
-            for (int j = 0; j < clusterCount; ++j)
-            {
-                FbxCluster* cluster = fbxSkin->GetCluster(j);
-                assert(cluster);
-                const int vertexIndexCount = cluster->GetControlPointIndicesCount();
-                for (int k = 0; k < vertexIndexCount; ++k)
-                {
-                    int index = cluster->GetControlPointIndices()[k];
-                    if (index >= vertexCount)
-                    {
-                        continue;
-                    }
-
-                    double weight = cluster->GetControlPointWeights()[k];
-                    if (weight == 0.0)
-                    {
-                        continue;
-                    }
-                    weights[index].push_back(Vector2((float)j, (float)weight));
-                }
-            }
-            // Only the first skin deformer will be loaded.
-            // There probably won't be more than one.
-            break;
-        }
-    }
-    return fbxSkin != NULL;
-}
-
-void findMinMaxTime(FbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate)
-{
-    FbxTime start, stop;
-    FbxTimeSpan timeSpan;
-    animCurve->GetTimeInterval(timeSpan);
-    start = timeSpan.GetStart();
-    stop = timeSpan.GetStop();
-    *startTime = std::min(*startTime, (float)start.GetMilliSeconds());
-    *stopTime = std::max(*stopTime, (float)stop.GetMilliSeconds());
-    *frameRate = std::max(*frameRate, (float)stop.GetFrameRate(FbxTime::eDefaultMode));
-}
-
-void appendKeyFrame(FbxNode* fbxNode, AnimationChannel* channel, float time, const Vector3& scale, const Quaternion& rotation, const Vector3& translation)
-{
-    // Write key time
-    channel->getKeyTimes().push_back(time);
-
-    // Write key values
-    std::vector<float>& keyValues = channel->getKeyValues();
-    switch (channel->getTargetAttribute())
-    {
-        case Transform::ANIMATE_SCALE:
-        {
-            keyValues.push_back(scale.x);
-            keyValues.push_back(scale.y);
-            keyValues.push_back(scale.z);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_X:
-        {
-            keyValues.push_back(scale.x);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_Y:
-        {
-            keyValues.push_back(scale.y);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_Z:
-        {
-            keyValues.push_back(scale.z);
-        }
-        break;
-
-        case Transform::ANIMATE_ROTATE:
-        {
-            keyValues.push_back(rotation.x);
-            keyValues.push_back(rotation.y);
-            keyValues.push_back(rotation.z);
-            keyValues.push_back(rotation.w);
-        }
-        break;
-
-        case Transform::ANIMATE_TRANSLATE:
-        {
-            keyValues.push_back(translation.x);
-            keyValues.push_back(translation.y);
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_TRANSLATE_X:
-        {
-            keyValues.push_back(translation.x);
-        }
-        break;
-
-        case Transform::ANIMATE_TRANSLATE_Y:
-        {
-            keyValues.push_back(translation.y);
-        }
-        break;
-
-        case Transform::ANIMATE_TRANSLATE_Z:
-        {
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_ROTATE_TRANSLATE:
-        {
-            keyValues.push_back(rotation.x);
-            keyValues.push_back(rotation.y);
-            keyValues.push_back(rotation.z);
-            keyValues.push_back(rotation.w);
-            keyValues.push_back(translation.x);
-            keyValues.push_back(translation.y);
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
-        {
-            keyValues.push_back(scale.x);
-            keyValues.push_back(scale.y);
-            keyValues.push_back(scale.z);
-            keyValues.push_back(rotation.x);
-            keyValues.push_back(rotation.y);
-            keyValues.push_back(rotation.z);
-            keyValues.push_back(rotation.w);
-            keyValues.push_back(translation.x);
-            keyValues.push_back(translation.y);
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_TRANSLATE:
-        {
-            keyValues.push_back(scale.x);
-            keyValues.push_back(scale.y);
-            keyValues.push_back(scale.z);
-            keyValues.push_back(translation.x);
-            keyValues.push_back(translation.y);
-            keyValues.push_back(translation.z);
-        }
-        break;
-
-        case Transform::ANIMATE_SCALE_ROTATE:
-        {
-            keyValues.push_back(scale.x);
-            keyValues.push_back(scale.y);
-            keyValues.push_back(scale.z);
-            keyValues.push_back(rotation.x);
-            keyValues.push_back(rotation.y);
-            keyValues.push_back(rotation.z);
-            keyValues.push_back(rotation.w);
-        }
-        break;
-
-        default:
-        {
-            LOG(1, "Warning: Invalid animatoin target (%d) attribute for node: %s.\n", channel->getTargetAttribute(), fbxNode->GetName());
-        }
-        return;
-    }
-}
-
-void decompose(FbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation)
-{
-    FbxAMatrix fbxMatrix;
-    Matrix matrix;
-    FbxTime kTime;
-    kTime.SetMilliSeconds((FbxLongLong)time);
-    fbxMatrix = fbxNode->EvaluateLocalTransform(kTime);
-    copyMatrix(fbxMatrix, matrix);
-    matrix.decompose(scale, rotation, translation);
-}
-
-AnimationChannel* createAnimationChannel(FbxNode* fbxNode, unsigned int targetAttrib, const std::vector<float>& keyTimes, const std::vector<float>& keyValues)
-{
-    AnimationChannel* channel = new AnimationChannel();
-    channel->setTargetId(fbxNode->GetName());
-    channel->setKeyTimes(keyTimes);
-    channel->setKeyValues(keyValues);
-    channel->setInterpolation(AnimationChannel::LINEAR);
-    channel->setTargetAttribute(targetAttrib);
-    return channel;
-}
-
-void addScaleChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime)
-{
-    std::vector<float> keyTimes;
-    std::vector<float> keyValues;
-    Vector3 scale;
-    Quaternion rotation;
-    Vector3 translation;
-
-    decompose(fbxNode, startTime, &scale, &rotation, &translation);
-    keyTimes.push_back(startTime);
-    keyValues.push_back(scale.x);
-    keyValues.push_back(scale.y);
-    keyValues.push_back(scale.z);
-
-    decompose(fbxNode, stopTime, &scale, &rotation, &translation);
-    keyTimes.push_back(stopTime);
-    keyValues.push_back(scale.x);
-    keyValues.push_back(scale.y);
-    keyValues.push_back(scale.z);
-
-    AnimationChannel* channel = createAnimationChannel(fbxNode, Transform::ANIMATE_SCALE, keyTimes, keyValues);
-    animation->add(channel);
-}
-
-void addTranslateChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime)
-{
-    std::vector<float> keyTimes;
-    std::vector<float> keyValues;
-    Vector3 scale;
-    Quaternion rotation;
-    Vector3 translation;
-
-    decompose(fbxNode, startTime, &scale, &rotation, &translation);
-    keyTimes.push_back(startTime);
-    keyValues.push_back(translation.x);
-    keyValues.push_back(translation.y);
-    keyValues.push_back(translation.z);
-
-    decompose(fbxNode, stopTime, &scale, &rotation, &translation);
-    keyTimes.push_back(stopTime);
-    keyValues.push_back(translation.x);
-    keyValues.push_back(translation.y);
-    keyValues.push_back(translation.z);
-
-    AnimationChannel* channel = createAnimationChannel(fbxNode, Transform::ANIMATE_TRANSLATE, keyTimes, keyValues);
-    animation->add(channel);
-}
-
-void copyMatrix(const FbxMatrix& fbxMatrix, float* matrix)
-{
-    int i = 0;
-    for (int row = 0; row < 4; ++row)
-    {
-        for (int col = 0; col < 4; ++col)
-        {
-            matrix[i++] = (float)fbxMatrix.Get(row, col);
-        }
-    }
-}
-
-void copyMatrix(const FbxMatrix& fbxMatrix, Matrix& matrix)
-{
-    int i = 0;
-    for (int row = 0; row < 4; ++row)
-    {
-        for (int col = 0; col < 4; ++col)
-        {
-            matrix.m[i++] = (float)fbxMatrix.Get(row, col);
-        }
-    }
-}
-
-bool isGroupAnimationPossible(FbxScene* fbxScene)
-{
-    FbxNode* rootNode = fbxScene->GetRootNode();
-    if (rootNode)
-    {
-        if (isGroupAnimationPossible(rootNode))
-            return true;
-    }
-    return false;
-}
-
-bool isGroupAnimationPossible(FbxNode* fbxNode)
-{
-    if (fbxNode)
-    {
-        FbxMesh* fbxMesh = fbxNode->GetMesh();
-        if (isGroupAnimationPossible(fbxMesh))
-            return true;
-        const int childCount = fbxNode->GetChildCount();
-        for (int i = 0; i < childCount; ++i)
-        {
-            if (isGroupAnimationPossible(fbxNode->GetChild(i)))
-                return true;
-        }
-    }
-    return false;
-}
-
-bool isGroupAnimationPossible(FbxMesh* fbxMesh)
-{
-    if (fbxMesh)
-    {
-        const int deformerCount = fbxMesh->GetDeformerCount();
-        for (int i = 0; i < deformerCount; ++i)
-        {
-            FbxDeformer* deformer = fbxMesh->GetDeformer(i);
-            if (deformer->GetDeformerType() == FbxDeformer::eSkin)
-            {
-                FbxSkin* fbxSkin = static_cast<FbxSkin*>(deformer);
-                if (fbxSkin)
-                {
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
-void generateTangentsAndBinormals(FbxNode* fbxNode, const EncoderArguments& arguments)
-{
-    if (!fbxNode)
-        return;
-    const char* name = fbxNode->GetName();
-    if (name && strlen(name) > 0)
-    {
-        FbxMesh* fbxMesh = fbxNode->GetMesh();
-        if (fbxMesh && arguments.isGenerateTangentBinormalId(std::string(name)))
-        {
-            fbxMesh->GenerateTangentsDataForAllUVSets();
-        }
-    }
-    // visit child nodes
-    const int childCount = fbxNode->GetChildCount();
-    for (int i = 0; i < childCount; ++i)
-    {
-        generateTangentsAndBinormals(fbxNode->GetChild(i), arguments);
-    }
-}
-
 #endif

+ 79 - 0
tools/encoder/src/FBXSceneEncoder.h

@@ -60,6 +60,15 @@ public:
      */
     void write(const std::string& filepath, const EncoderArguments& arguments);
 
+    /**
+     * Writes a material file.
+     * 
+     * @param filepath 
+     * 
+     * @return True if successful; false otherwise.
+     */
+    bool writeMaterial(const std::string& filepath);
+
 private:
 
     /**
@@ -126,6 +135,33 @@ private:
      */
     void loadModel(FbxNode* fbxNode, Node* node);
 
+    /**
+     * Loads materials for each node in the scene.
+     */
+    void loadMaterials(FbxScene* fbxScene);
+
+    /**
+     * Loads the material from the given node.
+     */
+    void loadMaterial(FbxNode* fbxNode);
+
+    void loadMaterialTextures(FbxSurfaceMaterial* fbxMaterial, Material* material);
+
+    void loadMaterialFileTexture(FbxFileTexture* fileTexture, Material* material);
+
+    void loadMaterialUniforms(FbxSurfaceMaterial* fbxMaterial, Material* material);
+
+    /**
+     * Creates a material from an FbxSurfaceMaterial.
+     * 
+     * @param name The name of the new material.
+     * @param fbxMaterial The FBX material to copy from.
+     * @param node The node that the material is being created for.
+     * 
+     * @return The newly created material.
+     */
+    Material* createMaterial(const std::string& name, FbxSurfaceMaterial* fbxMaterial, Node* node);
+
     /**
      * Loads the mesh skin from the given FBX mesh and adds it to the given GamePlay model.
      *
@@ -185,6 +221,34 @@ private:
      */
     void transformNode(FbxNode* fbxNode, Node* node);
 
+    /**
+     * Gets the base material that matches the given id. Returns NULL if not found.
+     */
+    Material* getBaseMaterial(const char* id);
+
+    /**
+     * Finds the base material for the given material.
+     * This will either return a previously loaded base material or 
+     * the base material will be created and returned. (Should never return NULL)
+     */
+    Material* findBaseMaterial(Material* material);
+
+    /**
+     * Finds the gameplay Node that was created from the given FbxNode.
+     * Returns NULL if not found.
+     */
+    Node* findNode(FbxNode* fbxNode);
+
+    /**
+     * Creates a base material with the given name from the given child material.
+     * 
+     * @param baseMaterialName The name of the base material to create.
+     * @param childMaterial The child material that the base material is being created for.
+     * 
+     * @return The newly created base material.
+     */
+    Material* createBaseMaterial(const std::string& baseMaterialName, Material* childMaterial);
+
     /**
      * Recursively triangules the meshes starting from the given node.
      * 
@@ -204,6 +268,21 @@ private:
      */
     std::map<FbxUInt64, Mesh*> _meshes;
 
+    /**
+     * The list of child materials that were loaded.
+     */
+    std::map<std::string, Material*> _materials;
+
+    /**
+     * The list of base materials that the child materials are derived from.
+     */
+    std::map<std::string, Material*> _baseMaterials;
+
+    /**
+     * A map for keeping track of which gameplay Node was created from a given FbxNode.
+     */
+    std::map<FbxNode*, Node*> _nodeMap;
+
     /**
      * The animation that channels should be added to if the user is using the -groupAnimation command line argument. May be NULL.
      */

+ 809 - 0
tools/encoder/src/FBXUtil.cpp

@@ -0,0 +1,809 @@
+#ifdef USE_FBX
+
+#include <algorithm>
+#include <string>
+#include <sstream>
+
+#include "FBXUtil.h"
+#include "Transform.h"
+#include "Vector3.h"
+#include "Vector2.h"
+
+using namespace gameplay;
+using std::string;
+using std::vector;
+using std::map;
+using std::ostringstream;
+
+float getAspectRatio(FbxCamera* fbxCamera)
+{
+    return (float)fbxCamera->FilmAspectRatio.Get();
+    /*
+    FbxCamera::ECameraAspectRatioMode camAspectRatioMode = fbxCamera->GetAspectRatioMode();
+    double aspectX = fbxCamera->AspectWidth.Get();
+    double aspectY = fbxCamera->AspectHeight.Get();
+    double aspectRatio = 1.333333;
+    switch ( camAspectRatioMode)
+    {
+    case FbxCamera::eWINDOW_SIZE:
+        aspectRatio = aspectX / aspectY;
+        break;
+    case FbxCamera::eFIXED_RATIO:
+        aspectRatio = aspectX;
+        break;
+    case FbxCamera::eFIXED_RESOLUTION:
+        aspectRatio = aspectX / aspectY * fbxCamera->GetPixelRatio();
+        break;
+    case FbxCamera::eFIXED_WIDTH:
+        aspectRatio = fbxCamera->GetPixelRatio() / aspectY;
+        break;
+    case FbxCamera::eFIXED_HEIGHT:
+        aspectRatio = fbxCamera->GetPixelRatio() * aspectX;
+        break;
+    default:
+        break;
+    }
+    return (float)aspectRatio;
+    */
+}
+
+inline double vfov(double hfov, double aspect)
+{
+    static const double MATH_PI_180 = 0.01745329251994329576923690768489;
+    static const double MATH_180_PI = 57.295779513082320876798154814105;
+    return (2.0 * atan((aspect) * tan( (hfov * MATH_PI_180) * 0.5)) * MATH_180_PI);
+}
+
+float getFieldOfView(FbxCamera* fbxCamera)
+{
+    double fieldOfViewX = 0.0;
+    double fieldOfViewY = 0.0;
+    double filmHeight = fbxCamera->GetApertureHeight();
+    double filmWidth = fbxCamera->GetApertureWidth() * fbxCamera->GetSqueezeRatio();
+    double apertureRatio = filmHeight / filmWidth;
+    if ( fbxCamera->GetApertureMode() == FbxCamera::eVertical)
+    {
+        fieldOfViewY = fbxCamera->FieldOfView.Get();
+    }
+    else if (fbxCamera->GetApertureMode() == FbxCamera::eHorizontal)
+    {
+        fieldOfViewX = fbxCamera->FieldOfView.Get();
+        fieldOfViewY = vfov( fieldOfViewX, apertureRatio);
+    }
+    else if (fbxCamera->GetApertureMode() == FbxCamera::eFocalLength)
+    {
+        fieldOfViewX = fbxCamera->ComputeFieldOfView(fbxCamera->FocalLength.Get());
+        fieldOfViewY = vfov( fieldOfViewX, apertureRatio);
+    }
+    else if (fbxCamera->GetApertureMode() == FbxCamera::eHorizAndVert)
+    {
+        fieldOfViewY = fbxCamera->FieldOfViewY.Get();
+    }
+    else
+    {
+        fieldOfViewY = 45.0;
+    }
+    return (float)fieldOfViewY;
+}
+
+void loadTextureCoords(FbxMesh* fbxMesh, const FbxGeometryElementUV* uvs, int uvSetIndex, int polyIndex, int posInPoly, int meshVertexIndex, Vertex* vertex)
+{
+    assert(fbxMesh && polyIndex >= 0 && posInPoly >= 0);
+
+    const bool useIndex = uvs->GetReferenceMode() != FbxGeometryElement::eDirect;
+    const int indexCount = useIndex ? uvs->GetIndexArray().GetCount() : 0;
+    int uvIndex = -1;
+
+    switch (uvs->GetMappingMode())
+    {
+    case FbxGeometryElement::eByControlPoint:
+        {
+            // Get the index of the current vertex in control points array
+            int polyVertIndex = fbxMesh->GetPolygonVertex(polyIndex, posInPoly);
+
+            // The UV index depends on the reference mode
+            uvIndex = useIndex ? uvs->GetIndexArray().GetAt(polyVertIndex) : polyVertIndex;
+        }
+        break;
+
+    case FbxGeometryElement::eByPolygonVertex:
+        if (meshVertexIndex < indexCount)
+        {
+            uvIndex = useIndex ? uvs->GetIndexArray().GetAt(meshVertexIndex) : meshVertexIndex;
+        }
+        break;
+
+    default:
+        // Only support eByPolygonVertex and eByControlPoint mappings
+        break;
+    }
+
+    vertex->hasTexCoord[uvSetIndex] = true;
+
+    // Store UV information in vertex
+    if (uvIndex != -1)
+    {
+        FbxVector2 uvValue = uvs->GetDirectArray().GetAt(uvIndex);
+        vertex->texCoord[uvSetIndex].x = (float)uvValue[0];
+        vertex->texCoord[uvSetIndex].y = (float)uvValue[1];
+    }
+}
+
+void loadNormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
+{
+    if (fbxMesh->GetElementNormalCount() > 0)
+    {
+        // Get only the first
+        FbxGeometryElementNormal* normal = fbxMesh->GetElementNormal(0);
+        FbxGeometryElement::EMappingMode mappingMode = normal->GetMappingMode();
+        if (mappingMode == FbxGeometryElement::eByControlPoint)
+        {
+            switch (normal->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(controlPointIndex);
+                    vertex->hasNormal = true;
+                    vertex->normal.x = (float)vec4[0];
+                    vertex->normal.y = (float)vec4[1];
+                    vertex->normal.z = (float)vec4[2];
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = normal->GetIndexArray().GetAt(controlPointIndex);
+                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(id);
+                    vertex->hasNormal = true;
+                    vertex->normal.x = (float)vec4[0];
+                    vertex->normal.y = (float)vec4[1];
+                    vertex->normal.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
+        {
+            switch (normal->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(vertexIndex);
+                    vertex->hasNormal = true;
+                    vertex->normal.x = (float)vec4[0];
+                    vertex->normal.y = (float)vec4[1];
+                    vertex->normal.z = (float)vec4[2];
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = normal->GetIndexArray().GetAt(vertexIndex);
+                    FbxVector4 vec4 = normal->GetDirectArray().GetAt(id);
+                    vertex->hasNormal = true;
+                    vertex->normal.x = (float)vec4[0];
+                    vertex->normal.y = (float)vec4[1];
+                    vertex->normal.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
+
+void loadTangent(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
+{
+    if (fbxMesh->GetElementTangentCount() > 0)
+    {
+        // Get only the first tangent
+        FbxGeometryElementTangent* tangent = fbxMesh->GetElementTangent(0);
+        FbxGeometryElement::EMappingMode mappingMode = tangent->GetMappingMode();
+        if (mappingMode == FbxGeometryElement::eByControlPoint)
+        {
+            switch (tangent->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(controlPointIndex);
+                    vertex->hasTangent = true;
+                    vertex->tangent.x = (float)vec4[0];
+                    vertex->tangent.y = (float)vec4[1];
+                    vertex->tangent.z = (float)vec4[2];
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = tangent->GetIndexArray().GetAt(controlPointIndex);
+                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(id);
+                    vertex->hasTangent = true;
+                    vertex->tangent.x = (float)vec4[0];
+                    vertex->tangent.y = (float)vec4[1];
+                    vertex->tangent.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
+        {
+            switch (tangent->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(vertexIndex);
+                    vertex->hasTangent = true;
+                    vertex->tangent.x = (float)vec4[0];
+                    vertex->tangent.y = (float)vec4[1];
+                    vertex->tangent.z = (float)vec4[2];
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = tangent->GetIndexArray().GetAt(vertexIndex);
+                    FbxVector4 vec4 = tangent->GetDirectArray().GetAt(id);
+                    vertex->hasTangent = true;
+                    vertex->tangent.x = (float)vec4[0];
+                    vertex->tangent.y = (float)vec4[1];
+                    vertex->tangent.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
+
+void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
+{
+    if (fbxMesh->GetElementBinormalCount() > 0)
+    {
+        // Get only the first binormal.
+        FbxGeometryElementBinormal* binormal = fbxMesh->GetElementBinormal(0);
+        FbxGeometryElement::EMappingMode mappingMode = binormal->GetMappingMode();
+
+        if (mappingMode == FbxGeometryElement::eByControlPoint)
+        {
+            switch (binormal->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(controlPointIndex);
+                    vertex->hasBinormal = true;
+                    vertex->binormal.x = (float)vec4[0];
+                    vertex->binormal.y = (float)vec4[1];
+                    vertex->binormal.z = (float)vec4[2];
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = binormal->GetIndexArray().GetAt(controlPointIndex);
+                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(id);
+                    vertex->hasBinormal = true;
+                    vertex->binormal.x = (float)vec4[0];
+                    vertex->binormal.y = (float)vec4[1];
+                    vertex->binormal.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
+        {
+            switch (binormal->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(vertexIndex);
+                    vertex->hasBinormal = true;
+                    vertex->binormal.x = (float)vec4[0];
+                    vertex->binormal.y = (float)vec4[1];
+                    vertex->binormal.z = (float)vec4[2];
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = binormal->GetIndexArray().GetAt(vertexIndex);
+                    FbxVector4 vec4 = binormal->GetDirectArray().GetAt(id);
+                    vertex->hasBinormal = true;
+                    vertex->binormal.x = (float)vec4[0];
+                    vertex->binormal.y = (float)vec4[1];
+                    vertex->binormal.z = (float)vec4[2];
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
+
+void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex)
+{
+    if (fbxMesh->GetElementVertexColorCount() > 0)
+    {
+        // Get only the first vertex color.
+        FbxGeometryElementVertexColor* vertexColor = fbxMesh->GetElementVertexColor(0);
+        FbxGeometryElement::EMappingMode mappingMode = vertexColor->GetMappingMode();
+        if (mappingMode == FbxGeometryElement::eByControlPoint)
+        {
+            switch (vertexColor->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxColor color = vertexColor->GetDirectArray().GetAt(controlPointIndex);
+
+                    vertex->hasDiffuse = true;
+                    vertex->diffuse.x = (float)color.mRed;
+                    vertex->diffuse.y = (float)color.mGreen;
+                    vertex->diffuse.z = (float)color.mBlue;
+                    vertex->diffuse.w = (float)color.mAlpha;
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = vertexColor->GetIndexArray().GetAt(controlPointIndex);
+                    FbxColor color = vertexColor->GetDirectArray().GetAt(id);
+
+                    vertex->hasDiffuse = true;
+                    vertex->diffuse.x = (float)color.mRed;
+                    vertex->diffuse.y = (float)color.mGreen;
+                    vertex->diffuse.z = (float)color.mBlue;
+                    vertex->diffuse.w = (float)color.mAlpha;
+                }
+                break;
+            default:
+                break;
+            }
+        }
+        else if (mappingMode == FbxGeometryElement::eByPolygonVertex)
+        {
+            switch (vertexColor->GetReferenceMode())
+            {
+            case FbxGeometryElement::eDirect:
+                {
+                    FbxColor color = vertexColor->GetDirectArray().GetAt(vertexIndex);
+
+                    vertex->hasDiffuse = true;
+                    vertex->diffuse.x = (float)color.mRed;
+                    vertex->diffuse.y = (float)color.mGreen;
+                    vertex->diffuse.z = (float)color.mBlue;
+                    vertex->diffuse.w = (float)color.mAlpha;
+                }
+                break;
+            case FbxGeometryElement::eIndexToDirect:
+                {
+                    int id = vertexColor->GetIndexArray().GetAt(vertexIndex);
+                    FbxColor color = vertexColor->GetDirectArray().GetAt(id);
+
+                    vertex->hasDiffuse = true;
+                    vertex->diffuse.x = (float)color.mRed;
+                    vertex->diffuse.y = (float)color.mGreen;
+                    vertex->diffuse.z = (float)color.mBlue;
+                    vertex->diffuse.w = (float)color.mAlpha;
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+}
+
+void loadBlendData(const vector<Vector2>& vertexWeights, Vertex* vertex)
+{
+    size_t size = vertexWeights.size();
+
+    if (size >= 1)
+    {
+        vertex->hasWeights= true;
+        vertex->blendIndices.x = vertexWeights[0].x;
+        vertex->blendWeights.x = vertexWeights[0].y;
+    }
+    if (size >= 2)
+    {
+        vertex->blendIndices.y = vertexWeights[1].x;
+        vertex->blendWeights.y = vertexWeights[1].y;
+    }
+    if (size >= 3)
+    {
+        vertex->blendIndices.z = vertexWeights[2].x;
+        vertex->blendWeights.z = vertexWeights[2].y;
+    }
+    if (size >= 4)
+    {
+        vertex->blendIndices.w = vertexWeights[3].x;
+        vertex->blendWeights.w = vertexWeights[3].y;
+    }
+    //vertex->normalizeBlendWeight();
+}
+
+bool loadBlendWeights(FbxMesh* fbxMesh, vector<vector<Vector2> >& weights)
+{
+    assert(fbxMesh);
+    const int vertexCount = fbxMesh->GetControlPointsCount();
+
+    FbxSkin* fbxSkin = NULL;
+    const int deformerCount = fbxMesh->GetDeformerCount();
+    for (int i = 0; i < deformerCount; ++i)
+    {
+        FbxDeformer* deformer = fbxMesh->GetDeformer(i);
+        if (deformer->GetDeformerType() == FbxDeformer::eSkin)
+        {
+            fbxSkin = FbxCast<FbxSkin>(deformer);
+            weights.resize(vertexCount);
+
+            const int clusterCount = fbxSkin->GetClusterCount();
+            for (int j = 0; j < clusterCount; ++j)
+            {
+                FbxCluster* cluster = fbxSkin->GetCluster(j);
+                assert(cluster);
+                const int vertexIndexCount = cluster->GetControlPointIndicesCount();
+                for (int k = 0; k < vertexIndexCount; ++k)
+                {
+                    int index = cluster->GetControlPointIndices()[k];
+                    if (index >= vertexCount)
+                    {
+                        continue;
+                    }
+
+                    double weight = cluster->GetControlPointWeights()[k];
+                    if (weight == 0.0)
+                    {
+                        continue;
+                    }
+                    weights[index].push_back(Vector2((float)j, (float)weight));
+                }
+            }
+            // Only the first skin deformer will be loaded.
+            // There probably won't be more than one.
+            break;
+        }
+    }
+    return fbxSkin != NULL;
+}
+
+void findMinMaxTime(FbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate)
+{
+    FbxTime start, stop;
+    FbxTimeSpan timeSpan;
+    animCurve->GetTimeInterval(timeSpan);
+    start = timeSpan.GetStart();
+    stop = timeSpan.GetStop();
+    *startTime = std::min(*startTime, (float)start.GetMilliSeconds());
+    *stopTime = std::max(*stopTime, (float)stop.GetMilliSeconds());
+    *frameRate = std::max(*frameRate, (float)stop.GetFrameRate(FbxTime::eDefaultMode));
+}
+
+void appendKeyFrame(FbxNode* fbxNode, AnimationChannel* channel, float time, const Vector3& scale, const Quaternion& rotation, const Vector3& translation)
+{
+    // Write key time
+    channel->getKeyTimes().push_back(time);
+
+    // Write key values
+    vector<float>& keyValues = channel->getKeyValues();
+    switch (channel->getTargetAttribute())
+    {
+        case Transform::ANIMATE_SCALE:
+        {
+            keyValues.push_back(scale.x);
+            keyValues.push_back(scale.y);
+            keyValues.push_back(scale.z);
+        }
+        break;
+
+        case Transform::ANIMATE_SCALE_X:
+        {
+            keyValues.push_back(scale.x);
+        }
+        break;
+
+        case Transform::ANIMATE_SCALE_Y:
+        {
+            keyValues.push_back(scale.y);
+        }
+        break;
+
+        case Transform::ANIMATE_SCALE_Z:
+        {
+            keyValues.push_back(scale.z);
+        }
+        break;
+
+        case Transform::ANIMATE_ROTATE:
+        {
+            keyValues.push_back(rotation.x);
+            keyValues.push_back(rotation.y);
+            keyValues.push_back(rotation.z);
+            keyValues.push_back(rotation.w);
+        }
+        break;
+
+        case Transform::ANIMATE_TRANSLATE:
+        {
+            keyValues.push_back(translation.x);
+            keyValues.push_back(translation.y);
+            keyValues.push_back(translation.z);
+        }
+        break;
+
+        case Transform::ANIMATE_TRANSLATE_X:
+        {
+            keyValues.push_back(translation.x);
+        }
+        break;
+
+        case Transform::ANIMATE_TRANSLATE_Y:
+        {
+            keyValues.push_back(translation.y);
+        }
+        break;
+
+        case Transform::ANIMATE_TRANSLATE_Z:
+        {
+            keyValues.push_back(translation.z);
+        }
+        break;
+
+        case Transform::ANIMATE_ROTATE_TRANSLATE:
+        {
+            keyValues.push_back(rotation.x);
+            keyValues.push_back(rotation.y);
+            keyValues.push_back(rotation.z);
+            keyValues.push_back(rotation.w);
+            keyValues.push_back(translation.x);
+            keyValues.push_back(translation.y);
+            keyValues.push_back(translation.z);
+        }
+        break;
+
+        case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
+        {
+            keyValues.push_back(scale.x);
+            keyValues.push_back(scale.y);
+            keyValues.push_back(scale.z);
+            keyValues.push_back(rotation.x);
+            keyValues.push_back(rotation.y);
+            keyValues.push_back(rotation.z);
+            keyValues.push_back(rotation.w);
+            keyValues.push_back(translation.x);
+            keyValues.push_back(translation.y);
+            keyValues.push_back(translation.z);
+        }
+        break;
+
+        case Transform::ANIMATE_SCALE_TRANSLATE:
+        {
+            keyValues.push_back(scale.x);
+            keyValues.push_back(scale.y);
+            keyValues.push_back(scale.z);
+            keyValues.push_back(translation.x);
+            keyValues.push_back(translation.y);
+            keyValues.push_back(translation.z);
+        }
+        break;
+
+        case Transform::ANIMATE_SCALE_ROTATE:
+        {
+            keyValues.push_back(scale.x);
+            keyValues.push_back(scale.y);
+            keyValues.push_back(scale.z);
+            keyValues.push_back(rotation.x);
+            keyValues.push_back(rotation.y);
+            keyValues.push_back(rotation.z);
+            keyValues.push_back(rotation.w);
+        }
+        break;
+
+        default:
+        {
+            LOG(1, "Warning: Invalid animatoin target (%d) attribute for node: %s.\n", channel->getTargetAttribute(), fbxNode->GetName());
+        }
+        return;
+    }
+}
+
+void decompose(FbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation)
+{
+    FbxAMatrix fbxMatrix;
+    Matrix matrix;
+    FbxTime kTime;
+    kTime.SetMilliSeconds((FbxLongLong)time);
+    fbxMatrix = fbxNode->EvaluateLocalTransform(kTime);
+    copyMatrix(fbxMatrix, matrix);
+    matrix.decompose(scale, rotation, translation);
+}
+
+AnimationChannel* createAnimationChannel(FbxNode* fbxNode, unsigned int targetAttrib, const vector<float>& keyTimes, const vector<float>& keyValues)
+{
+    AnimationChannel* channel = new AnimationChannel();
+    channel->setTargetId(fbxNode->GetName());
+    channel->setKeyTimes(keyTimes);
+    channel->setKeyValues(keyValues);
+    channel->setInterpolation(AnimationChannel::LINEAR);
+    channel->setTargetAttribute(targetAttrib);
+    return channel;
+}
+
+void addScaleChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime)
+{
+    vector<float> keyTimes;
+    vector<float> keyValues;
+    Vector3 scale;
+    Quaternion rotation;
+    Vector3 translation;
+
+    decompose(fbxNode, startTime, &scale, &rotation, &translation);
+    keyTimes.push_back(startTime);
+    keyValues.push_back(scale.x);
+    keyValues.push_back(scale.y);
+    keyValues.push_back(scale.z);
+
+    decompose(fbxNode, stopTime, &scale, &rotation, &translation);
+    keyTimes.push_back(stopTime);
+    keyValues.push_back(scale.x);
+    keyValues.push_back(scale.y);
+    keyValues.push_back(scale.z);
+
+    AnimationChannel* channel = createAnimationChannel(fbxNode, Transform::ANIMATE_SCALE, keyTimes, keyValues);
+    animation->add(channel);
+}
+
+void addTranslateChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime)
+{
+    vector<float> keyTimes;
+    vector<float> keyValues;
+    Vector3 scale;
+    Quaternion rotation;
+    Vector3 translation;
+
+    decompose(fbxNode, startTime, &scale, &rotation, &translation);
+    keyTimes.push_back(startTime);
+    keyValues.push_back(translation.x);
+    keyValues.push_back(translation.y);
+    keyValues.push_back(translation.z);
+
+    decompose(fbxNode, stopTime, &scale, &rotation, &translation);
+    keyTimes.push_back(stopTime);
+    keyValues.push_back(translation.x);
+    keyValues.push_back(translation.y);
+    keyValues.push_back(translation.z);
+
+    AnimationChannel* channel = createAnimationChannel(fbxNode, Transform::ANIMATE_TRANSLATE, keyTimes, keyValues);
+    animation->add(channel);
+}
+
+void copyMatrix(const FbxMatrix& fbxMatrix, float* matrix)
+{
+    int i = 0;
+    for (int row = 0; row < 4; ++row)
+    {
+        for (int col = 0; col < 4; ++col)
+        {
+            matrix[i++] = (float)fbxMatrix.Get(row, col);
+        }
+    }
+}
+
+void copyMatrix(const FbxMatrix& fbxMatrix, Matrix& matrix)
+{
+    int i = 0;
+    for (int row = 0; row < 4; ++row)
+    {
+        for (int col = 0; col < 4; ++col)
+        {
+            matrix.m[i++] = (float)fbxMatrix.Get(row, col);
+        }
+    }
+}
+
+bool isGroupAnimationPossible(FbxScene* fbxScene)
+{
+    FbxNode* rootNode = fbxScene->GetRootNode();
+    if (rootNode)
+    {
+        if (isGroupAnimationPossible(rootNode))
+            return true;
+    }
+    return false;
+}
+
+bool isGroupAnimationPossible(FbxNode* fbxNode)
+{
+    if (fbxNode)
+    {
+        FbxMesh* fbxMesh = fbxNode->GetMesh();
+        if (isGroupAnimationPossible(fbxMesh))
+            return true;
+        const int childCount = fbxNode->GetChildCount();
+        for (int i = 0; i < childCount; ++i)
+        {
+            if (isGroupAnimationPossible(fbxNode->GetChild(i)))
+                return true;
+        }
+    }
+    return false;
+}
+
+bool isGroupAnimationPossible(FbxMesh* fbxMesh)
+{
+    if (fbxMesh)
+    {
+        const int deformerCount = fbxMesh->GetDeformerCount();
+        for (int i = 0; i < deformerCount; ++i)
+        {
+            FbxDeformer* deformer = fbxMesh->GetDeformer(i);
+            if (deformer->GetDeformerType() == FbxDeformer::eSkin)
+            {
+                FbxSkin* fbxSkin = FbxCast<FbxSkin>(deformer);
+                if (fbxSkin)
+                {
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+bool isBlack(FbxDouble3& fbxDouble)
+{
+    return fbxDouble[0] == 0.0 && fbxDouble[1] == 0.0 && fbxDouble[2] == 0.0;
+}
+
+void generateTangentsAndBinormals(FbxNode* fbxNode, const EncoderArguments& arguments)
+{
+    if (!fbxNode)
+        return;
+    const char* name = fbxNode->GetName();
+    if (name && strlen(name) > 0)
+    {
+        FbxMesh* fbxMesh = fbxNode->GetMesh();
+        if (fbxMesh && arguments.isGenerateTangentBinormalId(string(name)))
+        {
+            fbxMesh->GenerateTangentsDataForAllUVSets();
+        }
+    }
+    // visit child nodes
+    const int childCount = fbxNode->GetChildCount();
+    for (int i = 0; i < childCount; ++i)
+    {
+        generateTangentsAndBinormals(fbxNode->GetChild(i), arguments);
+    }
+}
+
+FbxAnimCurve* getCurve(FbxPropertyT<FbxDouble3>& prop, FbxAnimLayer* animLayer, const char* pChannel)
+{
+#if FBXSDK_VERSION_MAJOR == 2013 && FBXSDK_VERSION_MINOR == 1
+    return prop.GetCurve<FbxAnimCurve>(animLayer, pChannel);
+#else
+    return prop.GetCurve(animLayer, pChannel);
+#endif
+}
+
+std::string toString(const FbxDouble3& fbxDouble)
+{
+    ostringstream stream;
+    stream << fbxDouble[0] << ", " << fbxDouble[1] << ", " << fbxDouble[2];
+    return stream.str();
+}
+
+std::string toString(const FbxDouble3& fbxDouble, double d)
+{
+    ostringstream stream;
+    stream << fbxDouble[0] << ", " << fbxDouble[1] << ", " << fbxDouble[2] << ", " << d;
+    return stream.str();
+}
+
+std::string toString(double value)
+{
+    ostringstream stream;
+    stream << value;
+    return stream.str();
+}
+
+#endif

+ 206 - 0
tools/encoder/src/FBXUtil.h

@@ -0,0 +1,206 @@
+#ifndef FBXUTIL_H_
+#define FBXUTIL_H_
+
+#ifdef USE_FBX
+
+#define FBXSDK_NEW_API
+
+#include <iostream>
+#include <list>
+#include <vector>
+#include <ctime>
+#ifdef WIN32
+    #pragma warning( disable : 4100 )
+    #pragma warning( disable : 4512 )
+#endif
+#include <fbxsdk.h>
+
+#include "Base.h"
+#include "Vertex.h"
+#include "Animation.h"
+#include "AnimationChannel.h"
+#include "EncoderArguments.h"
+
+using namespace gameplay;
+
+/**
+ * Returns the aspect ratio from the given camera.
+ * 
+ * @param fbxCamera The FBX camera to get the aspect ratio from.
+ * 
+ * @return The aspect ratio from the camera.
+ */
+float getAspectRatio(FbxCamera* fbxCamera);
+
+/**
+ * Returns the field of view Y from the given camera.
+ * 
+ * @param fbxCamera The camera to get the fiew of view from.
+ * 
+ * @return The field of view Y.
+ */
+float getFieldOfView(FbxCamera* fbxCamera);
+
+/**
+ * Loads the texture coordinates from given mesh's polygon part into the vertex.
+ * 
+ * @param fbxMesh The mesh to get the polygon from.
+ * @param uvs The UV list to load tex coords from.
+ * @param uvSetIndex The UV set index of the uvs.
+ * @param polyIndex The index of the polygon in the mesh.
+ * @param posInPoly The position of the vertex in the polygon.
+ * @param meshVertexIndex The index of the vertex in the mesh.
+ * @param vertex The vertex to copy the texture coordinates to.
+ */
+void loadTextureCoords(FbxMesh* fbxMesh, const FbxGeometryElementUV* uvs, int uvSetIndex, int polyIndex, int posInPoly, int meshVertexIndex, Vertex* vertex);
+
+/**
+ * Loads the normal from the mesh and adds it to the given vertex.
+ * 
+ * @param fbxMesh The mesh to get the polygon from.
+ * @param vertexIndex The vertex index in the mesh.
+ * @param controlPointIndex The control point index.
+ * @param vertex The vertex to copy to.
+ */
+void loadNormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
+
+/**
+ * Loads the tangent from the mesh and adds it to the given vertex.
+ * 
+ * @param fbxMesh The mesh to load from.
+ * @param vertexIndex The index of the vertex within fbxMesh.
+ * @param controlPointIndex The control point index.
+ * @param vertex The vertex to copy to.
+ */
+void loadTangent(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
+
+/**
+ * Loads the binormal from the mesh and adds it to the given vertex.
+ * 
+ * @param fbxMesh The mesh to load from.
+ * @param vertexIndex The index of the vertex within fbxMesh.
+ * @param controlPointIndex The control point index.
+ * @param vertex The vertex to copy to.
+ */
+void loadBinormal(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
+
+/**
+ * Loads the vertex diffuse color from the mesh and adds it to the given vertex.
+ * 
+ * @param fbxMesh The mesh to load from.
+ * @param vertexIndex The index of the vertex within fbxMesh.
+ * @param controlPointIndex The control point index.
+ * @param vertex The vertex to copy to.
+ */
+void loadVertexColor(FbxMesh* fbxMesh, int vertexIndex, int controlPointIndex, Vertex* vertex);
+
+/**
+ * Loads the blend weight and blend indices data into the vertex.
+ * 
+ * @param vertexWeights List of vertex weights. The x member contains the blendIndices. The y member contains the blendWeights.
+ * @param vertex The vertex to copy the blend data to.
+ */
+void loadBlendData(const std::vector<Vector2>& vertexWeights, Vertex* vertex);
+
+/**
+ * Loads the blend weights and blend indices from the given mesh.
+ * 
+ * Each element of weights is a list of Vector2s where "x" is the blend index and "y" is the blend weight.
+ * 
+ * @param fbxMesh The mesh to load from.
+ * @param weights List of blend weights and blend indices for each vertex.
+ * 
+ * @return True if this mesh has a mesh skin, false otherwise.
+ */
+bool loadBlendWeights(FbxMesh* fbxMesh, std::vector<std::vector<Vector2> >& weights);
+
+/**
+ * Copies from an FBX matrix to a float[16] array.
+ */
+void copyMatrix(const FbxMatrix& fbxMatrix, float* matrix);
+
+/**
+ * Copies from an FBX matrix to a gameplay matrix.
+ */
+void copyMatrix(const FbxMatrix& fbxMatrix, Matrix& matrix);
+
+/**
+ * Finds the min and max start time and stop time of the given animation curve.
+ * 
+ * startTime is updated if the animation curve contains a start time that is less than startTime.
+ * stopTime is updated if the animation curve contains a stop time that is greater than stopTime.
+ * frameRate is updated if the animation curve contains a frame rate that is greater than frameRate.
+ * 
+ * @param animCurve The animation curve to read from.
+ * @param startTime The min start time. (in/out)
+ * @param stopTime The max stop time. (in/out)
+ * @param frameRate The frame rate. (in/out)
+ */
+void findMinMaxTime(FbxAnimCurve* animCurve, float* startTime, float* stopTime, float* frameRate);
+
+/**
+ * Appends key frame data to the given node for the specified animation target attribute.
+ * 
+ * @param fbxNode The node to get the matrix transform from.
+ * @param channel The aniamtion channel to write values into.
+ * @param time The time of the keyframe.
+ * @param scale The evaluated scale for the keyframe.
+ * @param rotation The evalulated rotation for the keyframe.
+ * @param translation The evalulated translation for the keyframe.
+
+ */
+void appendKeyFrame(FbxNode* fbxNode, AnimationChannel* channel, float time, const Vector3& scale, const Quaternion& rotation, const Vector3& translation);
+
+/**
+ * Decomposes the given node's matrix transform at the given time and copies to scale, rotation and translation.
+ * 
+ * @param fbxNode The node to get the matrix transform from.
+ * @param time The time to get the matrix transform from.
+ * @param scale The scale to copy to.
+ * @param rotation The rotation to copy to.
+ * @param translation The translation to copy to.
+ */
+void decompose(FbxNode* fbxNode, float time, Vector3* scale, Quaternion* rotation, Vector3* translation);
+
+/**
+ * Creates an animation channel that targets the given node and target attribute using the given key times and key values.
+ * 
+ * @param fbxNode The node to target.
+ * @param targetAttrib The attribute type to target.
+ * @param keyTimes The key times for the animation channel.
+ * @param keyValues The key values for the animation channel.
+ * 
+ * @return The newly created animation channel.
+ */
+AnimationChannel* createAnimationChannel(FbxNode* fbxNode, unsigned int targetAttrib, const std::vector<float>& keyTimes, const std::vector<float>& keyValues);
+
+void addScaleChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime);
+
+void addTranslateChannel(Animation* animation, FbxNode* fbxNode, float startTime, float stopTime);
+
+/**
+ * Determines if it is possible to automatically group animations for mesh skins.
+ * 
+ * @param fbxScene The FBX scene to search.
+ * 
+ * @return True if there is at least one mesh skin that has animations that can be grouped.
+ */
+bool isGroupAnimationPossible(FbxScene* fbxScene);
+bool isGroupAnimationPossible(FbxNode* fbxNode);
+bool isGroupAnimationPossible(FbxMesh* fbxMesh);
+
+bool isBlack(FbxDouble3& fbxDouble);
+
+/**
+ * Recursively generates the tangents and binormals for all nodes that were specified in the command line arguments.
+ */
+void generateTangentsAndBinormals(FbxNode* fbxNode, const EncoderArguments& arguments);
+
+FbxAnimCurve* getCurve(FbxPropertyT<FbxDouble3>& prop, FbxAnimLayer* animLayer, const char* pChannel);
+
+std::string toString(const FbxDouble3& fbxDouble);
+std::string toString(const FbxDouble3& fbxDouble, double d);
+std::string toString(double value);
+
+#endif
+#endif

+ 8 - 0
tools/encoder/src/FileIO.cpp

@@ -178,6 +178,14 @@ void writeVectorText(const Vector4& v, FILE* file)
     fprintf(file, "%f %f %f %f\n", v.x, v.y, v.z, v.w);
 }
 
+void writeIndent(unsigned int indentLevel, FILE* file)
+{
+    for (unsigned int i = 0; i < indentLevel; ++i)
+    {
+        fprintf(file, "    ");
+    }
+}
+
 bool promptUserGroupAnimations()
 {
     char buffer[80];

+ 5 - 0
tools/encoder/src/FileIO.h

@@ -137,6 +137,11 @@ void writeVectorBinary(const Vector4& v, FILE* file);
 
 void writeVectorText(const Vector4& v, FILE* file);
 
+/**
+ * Writes a number of white space indentations to the file.
+ */
+void writeIndent(unsigned int indentLevel, FILE* file);
+
 /**
  * Prompts the user if they want to group animations automatically.
  * If the user enters an invalid response, the question is asked again.

+ 5 - 0
tools/encoder/src/GPBFile.cpp

@@ -297,6 +297,11 @@ Animations* GPBFile::getAnimations()
     return &_animations;
 }
 
+unsigned int GPBFile::getLightCount() const
+{
+    return (unsigned int)_lights.size();
+}
+
 void GPBFile::adjust()
 {
     // calculate the ambient color for each scene

+ 5 - 0
tools/encoder/src/GPBFile.h

@@ -97,6 +97,11 @@ public:
 
     Animations* getAnimations();
 
+    /**
+     * Returns the number of lights.
+     */
+    unsigned int getLightCount() const;
+
     /**
      * Adjusts the game play binary file before it is written.
      */

+ 325 - 16
tools/encoder/src/Material.cpp

@@ -1,39 +1,348 @@
-#include "Base.h"
 #include "Material.h"
+#include "FileIO.h"
+#include "StringUtil.h"
 
 namespace gameplay
 {
 
-Material::Material(void) :
-    _effect(NULL)
+using std::string;
+using std::vector;
+using std::map;
+
+Material::Material(const std::string& id) :
+    _parent(NULL), _id(id), _lit(false)
+{
+}
+
+Material::Material(const Material& c) :
+    _parent(c._parent),
+    _id(c._id),
+    _vertexShader(c._vertexShader),
+    _fragmentShader(c._fragmentShader),
+    _defines(c._defines),
+    _uniforms(c._uniforms),
+    _renderStates(c._renderStates)
 {
+    for (vector<Sampler*>::const_iterator it = c._samplers.begin(); it != c._samplers.end(); ++it)
+    {
+        _samplers.push_back(new Sampler(**it));
+    }
 }
 
 Material::~Material(void)
 {
 }
 
-unsigned int Material::getTypeId(void) const
+const string& Material::getId() const
+{
+    return _id;
+}
+
+void Material::setId(const char* id)
+{
+    if (id)
+        _id.assign(id);
+}
+
+Material* Material::getParent() const
+{
+    return _parent;
+}
+
+void Material::setParent(Material* material)
+{
+    if (material)
+        _parent = material;
+}
+
+void Material::addDefine(const string& name)
+{
+    if (!name.empty())
+    {
+        _defines[name] = string();
+    }
+}
+
+bool Material::isDefined(const string& name) const
+{
+    if (!name.empty())
+    {
+        return _defines.find(name) != _defines.end();
+    }
+    return false;
+}
+
+const char* Material::getUniform(const char* name) const
+{
+    map<string, string>::const_iterator it = _uniforms.find(string(name));
+    if (it != _uniforms.end())
+    {
+        return it->second.c_str();
+    }
+    return NULL;
+}
+
+void Material::setUniform(const string& name, const string& value)
+{
+    _uniforms[name] = value;
+}
+
+const char* Material::getRenderState(const char* name) const
+{
+    map<string, string>::const_iterator it = _renderStates.find(string(name));
+    if (it != _renderStates.end())
+    {
+        return it->second.c_str();
+    }
+    return NULL;
+}
+
+void Material::setRenderState(const string& name, const string& value)
+{
+    if (!name.empty())
+        _renderStates[name] = value;
+}
+
+void Material::setVertexShader(const char* path)
+{
+    if (path)
+        _vertexShader.assign(path);
+}
+
+void Material::setFragmentShader(const char* path)
+{
+    if (path)
+        _fragmentShader.assign(path);
+}
+
+Sampler* Material::createSampler(const string& id)
+{
+    Sampler* sampler = new Sampler(id.c_str());
+    sampler->set("mipmap", "true");
+    sampler->set("wrapS", CLAMP);
+    sampler->set("wrapT", CLAMP);
+    sampler->set(MIN_FILTER, LINEAR_MIPMAP_LINEAR);
+    sampler->set(MAG_FILTER, LINEAR);
+    _samplers.push_back(sampler);
+    return sampler;
+}
+
+Sampler* Material::getSampler(const string& id) const
+{
+    for (vector<Sampler*>::const_iterator it = _samplers.begin(); it != _samplers.end(); ++it)
+    {
+        Sampler* sampler = *it;
+        if (sampler->getId() == id)
+        {
+            return sampler;
+        }
+    }
+    return NULL;
+}
+
+bool Material::isTextured() const
+{
+    return !_samplers.empty();
+}
+
+bool Material::isBumped() const
+{
+    return getSampler("u_normalmapTexture") != NULL;
+}
+
+bool Material::isLit() const
+{
+    return _lit;
+}
+
+bool Material::isSpecular() const
 {
-    return MATERIAL_ID;
+    return isDefined(SPECULAR);
 }
-const char* Material::getElementName(void) const
+
+bool Material::isTextureRepeat() const
 {
-    return "Material";
+    return isDefined(TEXTURE_REPEAT);
 }
 
-void Material::writeBinary(FILE* file)
+bool Material::isVertexColor() const
 {
-    Object::writeBinary(file);
-    //write(_parameters, file);
-    //write(_effect, file);
+    return isDefined("VERTEX_COLOR");
 }
-void Material::writeText(FILE* file)
+
+bool Material::isSkinned() const
 {
-    fprintElementStart(file);
-    //fprintfElement(file, "parameters", _parameters);
-    //fprintfElement(file, "effect", _effect);
-    fprintElementEnd(file);
+    return isDefined("SKINNING");
+}
+
+bool Material::isModulateAlpha() const
+{
+    return isDefined("MODULATE_ALPHA");
+}
+
+void Material::setLit(bool value)
+{
+    _lit = value;
+}
+
+void Material::writeMaterial(FILE* file)
+{
+    fprintf(file, "material");
+    if (getId().length() > 0)
+    {
+        fprintf(file, " %s", getId().c_str());
+    }
+    if (_parent)
+    {
+         assert(_parent->getId().length() > 0);
+        fprintf(file, " : %s", _parent->getId().c_str());
+    }
+    fprintf(file, "\n");
+    fprintf(file, "{\n");
+    unsigned int indent = 1;
+
+    writeUniforms(file, indent);
+    writeSamplers(file, indent);
+    writeRenderStates(file, indent);
+    writeTechniqueAndPass(file, indent);
+
+    --indent;
+    writeIndent(indent, file);
+    fprintf(file, "}\n");
+}
+
+void Material::writeDefines(FILE* file, unsigned int& indent)
+{
+    writeIndent(indent, file);
+    fprintf(file, "defines = ");
+    for (map<string, string>::const_iterator it = _defines.begin(); it != _defines.end(); ++it)
+    {
+        if (it != _defines.begin())
+        {
+            fprintf(file, ";");
+        }
+        if (it->second.empty())
+        {
+            fprintf(file, "%s", it->first.c_str());
+        }
+        else
+        {
+            fprintf(file, "%s %s", it->first.c_str(), it->second.c_str());
+        }
+    }
+    fprintf(file, "\n");
+}
+
+void Material::writeUniforms(FILE* file, unsigned int& indent)
+{
+    for (map<string, string>::const_iterator it = _uniforms.begin(); it != _uniforms.end(); ++it)
+    {
+        writeIndent(indent, file);
+        fprintf(file, "%s = %s\n", it->first.c_str(), it->second.c_str());
+    }
+    if (!_uniforms.empty())
+    {
+        writeIndent(indent, file);
+        fprintf(file, "\n");
+    }
+}
+
+void Material::writeSamplers(FILE* file, unsigned int& indent)
+{
+    for (vector<Sampler*>::iterator it = _samplers.begin(); it != _samplers.end(); ++it)
+    {
+        Sampler* sampler = *it;
+        Sampler* parentSampler = NULL;
+        if (_parent)
+        {
+            parentSampler = _parent->getSampler(sampler->getId().c_str());
+        }
+        sampler->writeMaterial(file, indent, parentSampler);
+        fprintf(file, "\n");
+    }
+}
+
+void Material::writeRenderStates(FILE* file, unsigned int& indent)
+{
+    if (_renderStates.empty())
+        return;
+    writeIndent(indent, file);
+    fprintf(file, "renderState\n");
+    writeIndent(indent, file);
+    fprintf(file, "{\n");
+    ++indent;
+    for (map<string, string>::const_iterator it = _renderStates.begin(); it != _renderStates.end(); ++it)
+    {
+        writeIndent(indent, file);
+        fprintf(file, "%s = %s\n", it->first.c_str(), it->second.c_str());
+    }
+    --indent;
+    writeIndent(indent, file);
+    fprintf(file, "}\n");
+    writeIndent(indent, file);
+    fprintf(file, "\n");
+}
+
+void Material::writeTechniqueAndPass(FILE* file, unsigned int& indent)
+{
+    if (!_vertexShader.empty() || !_fragmentShader.empty() || !_defines.empty())
+    {
+        bool techniqueWritten = false;
+
+        if (!_vertexShader.empty() || 
+            !_fragmentShader.empty() || 
+            (!_defines.empty() && (!_parent || _parent->_defines != _defines)))
+        {
+            writeTechniqueOpening(file, indent);
+            techniqueWritten = true;
+        }
+        
+        if (!_vertexShader.empty())
+        {
+            writeIndent(indent, file);
+            fprintf(file, "%s = %s\n", "vertexShader", _vertexShader.c_str());
+        }
+        if (!_fragmentShader.empty())
+        {
+            writeIndent(indent, file);
+            fprintf(file, "%s = %s\n", "fragmentShader", _fragmentShader.c_str());
+        }
+        if (!_defines.empty())
+        {
+            if (!_parent || _parent->_defines != _defines)
+            {
+                writeDefines(file, indent);
+            }
+        }
+
+        if (techniqueWritten)
+        {
+            --indent;
+            writeIndent(indent, file);
+            fprintf(file, "}\n");
+
+            --indent;
+            writeIndent(indent, file);
+            fprintf(file, "}\n");
+        }
+    }
+}
+
+void Material::writeTechniqueOpening(FILE* file, unsigned int& indent)
+{
+    // write the techniques
+    writeIndent(indent, file);
+    fprintf(file, "technique\n");
+    writeIndent(indent, file);
+    fprintf(file, "{\n");
+    ++indent;
+
+    // write the passes
+    writeIndent(indent, file);
+    fprintf(file, "pass \n");
+    writeIndent(indent, file);
+    fprintf(file, "{\n");
+    ++indent;
 }
 
 }

+ 74 - 11
tools/encoder/src/Material.h

@@ -1,35 +1,98 @@
 #ifndef MATERIAL_H_
 #define MATERIAL_H_
 
-#include "Object.h"
-#include "MaterialParameter.h"
-#include "Effect.h"
+#include "Sampler.h"
+#include "Light.h"
+#include "Constants.h"
 
 namespace gameplay
 {
 
-class Material : public Object
+class Material
 {
 public:
 
     /**
      * Constructor.
      */
-    Material(void);
+    Material(const std::string& id);
+
+    Material(const Material&);
 
     /**
      * 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);
+    /**
+     * Returns this material's id string.
+     */
+    const std::string& getId() const;
+
+    /**
+     * Sets this material's id string.
+     */
+    void setId(const char* id);
+
+    Material* getParent() const;
+    void setParent(Material* material);
+
+    void addDefine(const std::string& name);
+    bool isDefined(const std::string& name) const;
+
+    const char* getUniform(const char* name) const;
+    void setUniform(const std::string& name, const std::string& value);
+
+    const char* getRenderState(const char* name) const;
+    void setRenderState(const std::string& name, const std::string& value);
+
+    void setVertexShader(const char* path);
+    void setFragmentShader(const char* path);
+
+    /**
+     * Creates a sampler and adds it to this material.
+     */
+    Sampler* createSampler(const std::string& id);
+    Sampler* getSampler(const std::string& id) const;
+    
+    bool isTextured() const;
+    bool isBumped() const;
+    bool isLit() const;
+    bool isSpecular() const;
+    bool isTextureRepeat() const;
+    bool isVertexColor() const;
+    bool isSkinned() const;
+    bool isModulateAlpha() const;
+
+    void setLit(bool value);
+
+    /**
+     * Writes this material to the given file.
+     */
+    void writeMaterial(FILE* file);
 
 private:
-    std::list<MaterialParameter> _parameters;
-    Effect* _effect;
+
+    Material& operator=(const Material&); // Hidden copy assignment operator.
+
+    void writeDefines(FILE* file, unsigned int& indent);
+    void writeUniforms(FILE* file, unsigned int& indent);
+    void writeSamplers(FILE* file, unsigned int& indent);
+    void writeRenderStates(FILE* file, unsigned int& indent);
+    void writeTechniqueAndPass(FILE* file, unsigned int& indent);
+    void writeTechniqueOpening(FILE* file, unsigned int& indent);
+
+private:
+    Material* _parent;
+    std::string _id;
+    std::string _vertexShader;
+    std::string _fragmentShader;
+    bool _lit;
+
+    std::map<std::string, std::string> _defines;
+    std::map<std::string, std::string> _uniforms;
+    std::map<std::string, std::string> _renderStates;
+    std::vector<Sampler*> _samplers;
 };
 
 }

+ 11 - 1
tools/encoder/src/Mesh.cpp

@@ -57,7 +57,7 @@ void Mesh::writeBinaryVertices(FILE* file)
     else
     {
         // No vertex data
-        write((unsigned int)0, file);
+        writeZero(file);
     }
 
     // Write bounds
@@ -166,6 +166,16 @@ unsigned int Mesh::getVertexIndex(const Vertex& vertex)
     return it->second;
 }
 
+bool Mesh::hasNormals() const
+{
+    return !vertices.empty() && vertices[0].hasNormal;
+}
+
+bool Mesh::hasVertexColors() const
+{
+    return !vertices.empty() && vertices[0].hasDiffuse;
+}
+
 void Mesh::computeBounds()
 {
     // If we have a Model with a MeshSkin associated with it,

+ 3 - 0
tools/encoder/src/Mesh.h

@@ -60,6 +60,9 @@ public:
 
     unsigned int getVertexIndex(const Vertex& vertex);
 
+    bool hasNormals() const;
+    bool hasVertexColors() const;
+
     void computeBounds();
 
     Model* model;

+ 5 - 0
tools/encoder/src/MeshSkin.cpp

@@ -86,6 +86,11 @@ void MeshSkin::writeText(FILE* file)
     fprintElementEnd(file);
 }
 
+unsigned int MeshSkin::getJointCount() const
+{
+    return _joints.size();
+}
+
 void MeshSkin::setBindShape(const float data[])
 {
     for (int i = 0; i < 16; ++i)

+ 2 - 0
tools/encoder/src/MeshSkin.h

@@ -34,6 +34,8 @@ public:
     virtual void writeBinary(FILE* file);
     virtual void writeText(FILE* file);
 
+    unsigned int getJointCount() const;
+
     void setBindShape(const float data[]);
 
     void setVertexInfluenceCount(unsigned int count);

+ 55 - 5
tools/encoder/src/Model.cpp

@@ -6,7 +6,8 @@ namespace gameplay
 
 Model::Model(void) :
     _mesh(NULL),
-    _meshSkin(NULL)
+    _meshSkin(NULL),
+    _material(NULL)
 {
 }
 
@@ -33,7 +34,7 @@ void Model::writeBinary(FILE* file)
     }
     else
     {
-        write((unsigned int)0, file);
+        writeZero(file);
     }
     // _meshSkin
     // Write one unsigned char to indicate if this model has a skin
@@ -46,9 +47,31 @@ void Model::writeBinary(FILE* file)
     {
         write((bool)false, file); // doesn't have a skin
     }
-    // materials[]
-    writeBinaryObjects(_materials, file);
-
+    // Write the list of materials or zero if there are no materials
+    if (_material && _materials.empty())
+    {
+        write((unsigned int)1, file);
+        write(_material->getId(), file);
+    }
+    else
+    {
+        write((unsigned int)_materials.size(), file);
+        if (_materials.size() > 0)
+        {
+            // Write the material names for each mesh part
+            for (unsigned int i = 0; i < _materials.size(); ++i)
+            {
+                if (Material* mat = _materials[i])
+                {
+                    write(mat->getId(), file);
+                }
+                else
+                {
+                    writeZero(file);
+                }
+            }
+        }
+    }
 }
 
 void Model::writeText(FILE* file)
@@ -64,6 +87,17 @@ void Model::writeText(FILE* file)
     {
         _meshSkin->writeText(file);
     }
+    if (_material)
+    {
+        fprintfElement(file, "material", _material->getId().c_str());
+    }
+    for (unsigned int i = 0; i < _materials.size(); ++i)
+    {
+        if (Material* mat = _materials[i])
+        {
+            fprintfElement(file, "material", mat->getId().c_str());
+        }
+    }
     fprintElementEnd(file);
 }
 
@@ -102,4 +136,20 @@ void Model::setSkin(MeshSkin* skin)
     }
 }
 
+void Model::setMaterial(Material* material, int partIndex)
+{
+    if (partIndex < 0)
+    {
+        _material = material;
+    }
+    else
+    {
+        if ((int)_materials.size() < partIndex + 1)
+        {
+            _materials.resize(partIndex + 1, (Material*)NULL);
+        }
+        _materials[partIndex] = material;
+    }
+}
+
 }

+ 3 - 1
tools/encoder/src/Model.h

@@ -32,12 +32,14 @@ public:
     void setMesh(Mesh* mesh);
     MeshSkin* getSkin();
     void setSkin(MeshSkin* skin);
+    void setMaterial(Material* material, int partIndex = -1);
 
 private:
 
     Mesh* _mesh;
     MeshSkin* _meshSkin;
-    std::list<Material*> _materials;
+    std::vector<Material*> _materials;
+    Material* _material;
 };
 
 }

+ 1 - 1
tools/encoder/src/Object.h

@@ -66,7 +66,7 @@ public:
     virtual void writeText(FILE* file) = 0;
 
     /**
-     * Returns this objects id string.
+     * Returns this object's id string.
      */
     const std::string& getId() const;
 

+ 82 - 0
tools/encoder/src/Sampler.cpp

@@ -0,0 +1,82 @@
+#include "Base.h"
+#include "Sampler.h"
+#include "FileIO.h"
+
+namespace gameplay
+{
+
+using std::string;
+using std::map;
+
+Sampler::Sampler(const char* id) :
+    _id(id)
+{
+}
+
+Sampler::~Sampler(void)
+{
+}
+
+const string& Sampler::getId() const
+{
+    return _id;
+}
+
+const char* Sampler::getString(const string& name)
+{
+    map<string, string>::const_iterator it = props.find(name);
+    if (it != props.end())
+    {
+        return it->second.c_str();
+    }
+    return NULL;
+}
+
+void Sampler::set(const string& name, const string& value)
+{
+    props[name] = value;
+}
+
+void Sampler::writeMaterial(FILE* file, unsigned int indent, Sampler* parent)
+{
+    writeIndent(indent, file);
+    fprintf(file, "sampler %s\n", _id.c_str());
+    writeIndent(indent, file);
+    fprintf(file, "{\n");
+    ++indent;
+
+    const char* relativePath = getString("relativePath");
+    if (relativePath)
+        set("path", relativePath);
+
+    writeProperty(file, "path", indent, parent);
+    writeProperty(file, "mipmap", indent, parent);
+    writeProperty(file, "wrapS", indent, parent);
+    writeProperty(file, "wrapT", indent, parent);
+    writeProperty(file, MIN_FILTER, indent, parent);
+    writeProperty(file, MAG_FILTER, indent, parent);
+    --indent;
+    writeIndent(indent, file);
+    fprintf(file, "}\n");
+}
+
+void Sampler::writeProperty(FILE* file, const string& name, unsigned int indent, Sampler* parent)
+{
+    const char* value = getString(name);
+    if (value != NULL)
+    {
+        const char* parentValue = NULL;
+        // Don't print the property if it is the same as the parent's property
+        if (parent != NULL)
+        {
+            parentValue = parent->getString(name);
+        }
+        if (parentValue == NULL || strcmp(value, parentValue) != 0)
+        {
+            writeIndent(indent, file);
+            fprintf(file, "%s = %s\n", name.c_str(), value);
+        }
+    }
+}
+
+}

+ 50 - 0
tools/encoder/src/Sampler.h

@@ -0,0 +1,50 @@
+#ifndef Sampler_H_
+#define Sampler_H_
+
+#include "Base.h"
+#include "Constants.h"
+
+namespace gameplay
+{
+
+class Sampler
+{
+public:
+
+    /**
+     * Constructor.
+     */
+    Sampler(const char* id);
+
+    /**
+     * Destructor.
+     */
+    virtual ~Sampler(void);
+
+    const std::string& getId() const;
+
+    const char* getString(const std::string& name);
+    void set(const std::string& name, const std::string& value);
+
+    /**
+     * Writes this sampler to a material file.
+     * 
+     * @param file The file pointer.
+     * @param indent The number of indentation levels.
+     * @param parent The parent sampler from this material's parent.
+     */
+    void writeMaterial(FILE* file, unsigned int indent, Sampler* parent = NULL);
+
+private:
+
+    void writeProperty(FILE* file, const std::string& name, unsigned int indent, Sampler* parent = NULL);
+
+private:
+
+    std::string _id;
+    std::map<std::string, std::string> props;
+};
+
+}
+
+#endif

+ 2 - 2
tools/encoder/src/TTFFontEncoder.cpp

@@ -223,8 +223,8 @@ int writeFont(const char* inFilePath, const char* outFilePath, unsigned int font
             if (penY + rowSize > (int)imageHeight)
             {
                 free(imageBuffer);
-				LOG(1, "Image size exceeded!");
-				return -1;
+                LOG(1, "Image size exceeded!");
+                return -1;
             }
         }
         

+ 122 - 54
tools/luagen/gameplay-luagen.doxyfile

@@ -1,4 +1,4 @@
-# Doxyfile 1.8.0
+# Doxyfile 1.8.3.1
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project
@@ -136,7 +136,9 @@ FULL_PATH_NAMES        = YES
 # only done if one of the specified strings matches the left-hand part of 
 # the path. The tag can be used to show relative paths in the file list. 
 # If left blank the directory from which doxygen is run is used as the 
-# path to strip.
+# path to strip. Note that you specify absolute paths here, but also 
+# relative paths, which will be relative from the directory where doxygen is 
+# started.
 
 STRIP_FROM_PATH        = 
 
@@ -239,14 +241,15 @@ OPTIMIZE_FOR_FORTRAN   = NO
 OPTIMIZE_OUTPUT_VHDL   = NO
 
 # Doxygen selects the parser to use depending on the extension of the files it 
-# parses. With this tag you can assign which parser to use for a given extension. 
-# Doxygen has a built-in mapping, but you can override or extend it using this 
-# tag. The format is ext=language, where ext is a file extension, and language 
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, 
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make 
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C 
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions 
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+# parses. With this tag you can assign which parser to use for a given 
+# extension. Doxygen has a built-in mapping, but you can override or extend it 
+# using this tag. The format is ext=language, where ext is a file extension, 
+# and language is one of the parsers supported by doxygen: IDL, Java, 
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, 
+# C++. For instance to make doxygen treat .inc files as Fortran files (default 
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note 
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the 
+# files are not read by doxygen.
 
 EXTENSION_MAPPING      = 
 
@@ -259,6 +262,13 @@ EXTENSION_MAPPING      =
 
 MARKDOWN_SUPPORT       = YES
 
+# When enabled doxygen tries to link words that correspond to documented classes, 
+# or namespaces to their corresponding documentation. Such a link can be 
+# prevented in individual cases by by putting a % sign in front of the word or 
+# globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT       = YES
+
 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
 # to include (a tag file for) the STL sources as input, then you should 
 # set this tag to YES in order to let doxygen match functions declarations and 
@@ -279,10 +289,10 @@ CPP_CLI_SUPPORT        = NO
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate getter 
-# and setter methods for a property. Setting this option to YES (the default) 
-# will make doxygen replace the get and set methods by a property in the 
-# documentation. This will only work if the methods are indeed getting or 
+# For Microsoft's IDL there are propget and propput attributes to indicate 
+# getter and setter methods for a property. Setting this option to YES (the 
+# default) will make doxygen replace the get and set methods by a property in 
+# the documentation. This will only work if the methods are indeed getting or 
 # setting a simple type. If this is not the case, or you want to show the 
 # methods anyway, you should set this option to NO.
 
@@ -372,7 +382,7 @@ EXTRACT_ALL            = NO
 
 EXTRACT_PRIVATE        = NO
 
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal 
 # scope will be included in the documentation.
 
 EXTRACT_PACKAGE        = NO
@@ -544,7 +554,8 @@ GENERATE_BUGLIST       = YES
 GENERATE_DEPRECATEDLIST= YES
 
 # The ENABLED_SECTIONS tag can be used to enable conditional 
-# documentation sections, marked by \if sectionname ... \endif.
+# documentation sections, marked by \if section-label ... \endif 
+# and \cond section-label ... \endcond blocks.
 
 ENABLED_SECTIONS       = 
 
@@ -564,12 +575,6 @@ MAX_INITIALIZER_LINES  = 30
 
 SHOW_USED_FILES        = NO
 
-# If the sources in your project are distributed over multiple directories 
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES       = NO
-
 # Set the SHOW_FILES tag to NO to disable the generation of the Files page. 
 # This will remove the Files entry from the Quick Index and from the 
 # Folder Tree View (if specified). The default is YES.
@@ -594,7 +599,7 @@ FILE_VERSION_FILTER    =
 
 # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed 
 # by doxygen. The layout file controls the global structure of the generated 
-# output files in an output format independent way. The create the layout file 
+# output files in an output format independent way. To create the layout file 
 # that represents doxygen's defaults, run doxygen with the -l option. 
 # You can optionally specify a file name after the option, if omitted 
 # DoxygenLayout.xml will be used as the name of the layout file.
@@ -607,7 +612,8 @@ LAYOUT_FILE            =
 # requires the bibtex tool to be installed. See also 
 # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style 
 # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this 
-# feature you need bibtex and perl available in the search path.
+# feature you need bibtex and perl available in the search path. Do not use 
+# file names with spaces, bibtex cannot handle them.
 
 CITE_BIB_FILES         = 
 
@@ -671,7 +677,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories 
 # with spaces.
 
-INPUT                  = ../gameplay/src
+INPUT                  = ../../gameplay/src
 
 # This tag can be used to specify the character encoding of the source files 
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
@@ -787,6 +793,13 @@ FILTER_SOURCE_FILES    = NO
 
 FILTER_SOURCE_PATTERNS = 
 
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that 
+# is part of the input, its contents will be placed on the main page (index.html). 
+# This can be useful if you have a project on for instance GitHub and want reuse 
+# the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = 
+
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
@@ -805,7 +818,7 @@ INLINE_SOURCES         = NO
 
 # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
 # doxygen to hide any special comment blocks from generated source code 
-# fragments. Normal C and C++ comments will always remain visible.
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
 
 STRIP_CODE_COMMENTS    = YES
 
@@ -907,13 +920,23 @@ HTML_FOOTER            =
 
 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
 # style sheet that is used by each HTML page. It can be used to 
-# fine-tune the look of the HTML output. If the tag is left blank doxygen 
-# will generate a default style sheet. Note that doxygen will try to copy 
-# the style sheet file to the HTML output directory, so don't put your own 
-# style sheet in the HTML output directory as well, or it will be erased!
+# fine-tune the look of the HTML output. If left blank doxygen will 
+# generate a default style sheet. Note that it is recommended to use 
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this 
+# tag will in the future become obsolete.
 
 HTML_STYLESHEET        = 
 
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional 
+# user-defined cascading style sheet that is included after the standard 
+# style sheets created by doxygen. Using this option one can overrule 
+# certain style aspects. This is preferred over using HTML_STYLESHEET 
+# since it does not replace the standard style sheet and is therefor more 
+# robust against future updates. Doxygen will copy the style sheet file to 
+# the output directory.
+
+HTML_EXTRA_STYLESHEET  = 
+
 # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or 
 # other source files which should be copied to the HTML output directory. Note 
 # that these files will be copied to the base HTML output directory. Use the 
@@ -954,20 +977,23 @@ HTML_COLORSTYLE_GAMMA  = 80
 
 HTML_TIMESTAMP         = YES
 
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
-# files or namespaces will be aligned in HTML using tables. If set to 
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = YES
-
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
 # documentation will contain sections that can be hidden and shown after the 
-# page has loaded. For this to work a browser that supports 
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+# page has loaded.
 
 HTML_DYNAMIC_SECTIONS  = NO
 
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of 
+# entries shown in the various tree structured indices initially; the user 
+# can expand and collapse entries dynamically later on. Doxygen will expand 
+# the tree to such a level that at most the specified number of entries are 
+# visible (unless a fully collapsed tree already exceeds this amount). 
+# So setting the number of entries 1 will produce a full collapsed tree by 
+# default. 0 is a special value representing an infinite number of entries 
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
 # If the GENERATE_DOCSET tag is set to YES, additional index files 
 # will be generated that can be used as input for Apple's Xcode 3 
 # integrated development environment, introduced with OSX 10.5 (Leopard). 
@@ -995,9 +1021,9 @@ DOCSET_FEEDNAME        = "Doxygen generated docs"
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify 
-# the documentation publisher. This should be a reverse domain-name style 
-# string, e.g. com.mycompany.MyDocSet.documentation.
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely 
+# identify the documentation publisher. This should be a reverse domain-name 
+# style string, e.g. com.mycompany.MyDocSet.documentation.
 
 DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
 
@@ -1146,11 +1172,6 @@ GENERATE_TREEVIEW      = NO
 
 ENUM_VALUES_PER_LINE   = 4
 
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, 
-# and Class Hierarchy pages using a tree view instead of an ordered list.
-
-USE_INLINE_TREES       = NO
-
 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
 # used to set the initial width (in pixels) of the frame in which the tree 
 # is shown.
@@ -1187,6 +1208,13 @@ FORMULA_TRANSPARENT    = YES
 
 USE_MATHJAX            = NO
 
+# When MathJax is enabled you can set the default output format to be used for 
+# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and 
+# SVG. The default value is HTML-CSS, which is slower, but has the best 
+# compatibility.
+
+MATHJAX_FORMAT         = HTML-CSS
+
 # When MathJax is enabled you need to specify the location relative to the 
 # HTML output directory using the MATHJAX_RELPATH option. The destination 
 # directory should contain the MathJax.js script. For instance, if the mathjax 
@@ -1214,15 +1242,55 @@ MATHJAX_EXTENSIONS     =
 SEARCHENGINE           = YES
 
 # When the SERVER_BASED_SEARCH tag is enabled the search engine will be 
-# implemented using a PHP enabled web server instead of at the web client 
-# using Javascript. Doxygen will generate the search PHP script and index 
-# file to put on the web server. The advantage of the server 
-# based approach is that it scales better to large projects and allows 
-# full text search. The disadvantages are that it is more difficult to setup 
-# and does not have live searching capabilities.
+# implemented using a web server instead of a web client using Javascript. 
+# There are two flavours of web server based search depending on the 
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for 
+# searching and an index file used by the script. When EXTERNAL_SEARCH is 
+# enabled the indexing and searching needs to be provided by external tools. 
+# See the manual for details.
 
 SERVER_BASED_SEARCH    = NO
 
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP 
+# script for searching. Instead the search results are written to an XML file 
+# which needs to be processed by an external indexer. Doxygen will invoke an 
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain 
+# the search results. Doxygen ships with an example indexer (doxyindexer) and 
+# search engine (doxysearch.cgi) which are based on the open source search engine 
+# library Xapian. See the manual for configuration details.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server 
+# which will returned the search results when EXTERNAL_SEARCH is enabled. 
+# Doxygen ships with an example search engine (doxysearch) which is based on 
+# the open source search engine library Xapian. See the manual for configuration 
+# details.
+
+SEARCHENGINE_URL       = 
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed 
+# search data is written to a file for indexing by an external tool. With the 
+# SEARCHDATA_FILE tag the name of this file can be specified.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the 
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is 
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple 
+# projects and redirect the results back to the right project.
+
+EXTERNAL_SEARCH_ID     = 
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen 
+# projects other than the one defined by this configuration file, but that are 
+# all added to the same external search index. Each project needs to have a 
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id 
+# of to a relative location where the documentation can be found. 
+# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
+
+EXTRA_SEARCH_MAPPINGS  = 
+
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
@@ -1713,7 +1781,7 @@ CALLER_GRAPH           = NO
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES 
 # then doxygen will show the dependencies a directory has on other directories 
 # in a graphical way. The dependency relations are determined by the #include 
 # relations between the files in the directories.