浏览代码

Merge branch 'next'

seanpaultaylor 10 年之前
父节点
当前提交
6240e85eb2
共有 82 个文件被更改,包括 3135 次插入624 次删除
  1. 21 62
      .gitignore
  2. 4 0
      CHANGES.md
  3. 3 3
      README.md
  4. 12 9
      gameplay/gameplay.pro
  5. 5 5
      gameplay/gameplay.vcxproj
  6. 4 8
      gameplay/gameplay.xcodeproj/project.pbxproj
  7. 4 4
      gameplay/src/AnimationClip.cpp
  8. 6 0
      gameplay/src/AudioController.cpp
  9. 3 0
      gameplay/src/Base.h
  10. 24 12
      gameplay/src/Container.cpp
  11. 17 10
      gameplay/src/Control.cpp
  12. 5 0
      gameplay/src/Control.h
  13. 10 0
      gameplay/src/DebugNew.cpp
  14. 3 1
      gameplay/src/Font.cpp
  15. 24 8
      gameplay/src/FrameBuffer.cpp
  16. 3 3
      gameplay/src/FrameBuffer.h
  17. 1 1
      gameplay/src/Label.h
  18. 11 0
      gameplay/src/Matrix.cpp
  19. 12 0
      gameplay/src/Matrix.h
  20. 1 1
      gameplay/src/ParticleEmitter.cpp
  21. 46 2
      gameplay/src/PlatformAndroid.cpp
  22. 2 0
      gameplay/src/PlatformLinux.cpp
  23. 2 0
      gameplay/src/PlatformMacOSX.mm
  24. 2 0
      gameplay/src/PlatformWindows.cpp
  25. 2 0
      gameplay/src/PlatformiOS.mm
  26. 7 4
      gameplay/src/Properties.cpp
  27. 1 0
      gameplay/src/Properties.h
  28. 50 0
      gameplay/src/Quaternion.cpp
  29. 33 0
      gameplay/src/Quaternion.h
  30. 2 0
      gameplay/src/Ref.cpp
  31. 2 2
      gameplay/src/RenderTarget.cpp
  32. 1 1
      gameplay/src/RenderTarget.h
  33. 9 1
      gameplay/src/SceneLoader.cpp
  34. 2 1
      gameplay/src/SceneLoader.h
  35. 2 2
      gameplay/src/ScriptController.cpp
  36. 1 0
      gameplay/src/Terrain.cpp
  37. 11 3
      gameplay/src/TerrainPatch.cpp
  38. 17 0
      gameplay/src/TextBox.cpp
  39. 5 0
      gameplay/src/TextBox.h
  40. 110 37
      gameplay/src/Texture.cpp
  41. 16 3
      gameplay/src/Texture.h
  42. 1 0
      gameplay/src/Theme.cpp
  43. 7 23
      gameplay/src/Transform.cpp
  44. 0 1
      gameplay/src/Vector3.h
  45. 2 0
      gameplay/src/gameplay-main-android.cpp
  46. 2 0
      gameplay/src/gameplay-main-ios.mm
  47. 2 0
      gameplay/src/gameplay-main-linux.cpp
  48. 2 0
      gameplay/src/gameplay-main-macosx.mm
  49. 2 0
      gameplay/src/gameplay-main-windows.cpp
  50. 40 29
      gameplay/src/org/gameplay3d/GamePlayNativeActivity.java
  51. 1 1
      install.bat
  52. 1 1
      install.sh
  53. 190 190
      samples/browser/res/common/sprites/sprite.scene
  54. 23 21
      samples/browser/sample-browser.pro
  55. 6 6
      samples/browser/sample-browser.vcxproj
  56. 3 1
      samples/browser/src/SpriteSample.cpp
  57. 48 0
      samples/character/game.config
  58. 24 20
      samples/character/sample-character.pro
  59. 6 6
      samples/character/sample-character.vcxproj
  60. 72 0
      samples/racer/game.config
  61. 24 21
      samples/racer/sample-racer.pro
  62. 6 6
      samples/racer/sample-racer.vcxproj
  63. 21 19
      samples/spaceship/sample-spaceship.pro
  64. 6 6
      samples/spaceship/sample-spaceship.vcxproj
  65. 22 20
      template/TEMPLATE_PROJECT.pro
  66. 2 2
      template/template.vcxproj
  67. 5 1
      tools/encoder/CMakeLists.txt
  68. 12 8
      tools/encoder/gameplay-encoder.vcxproj
  69. 12 0
      tools/encoder/gameplay-encoder.vcxproj.filters
  70. 0 15
      tools/encoder/gameplay-encoder.vcxproj.user
  71. 18 14
      tools/encoder/gameplay-encoder.xcodeproj/project.pbxproj
  72. 122 0
      tools/encoder/src/Base.cpp
  73. 6 0
      tools/encoder/src/Base.h
  74. 42 5
      tools/encoder/src/EncoderArguments.cpp
  75. 16 1
      tools/encoder/src/EncoderArguments.h
  76. 29 15
      tools/encoder/src/FBXSceneEncoder.cpp
  77. 1000 0
      tools/encoder/src/TMXSceneEncoder.cpp
  78. 82 0
      tools/encoder/src/TMXSceneEncoder.h
  79. 518 0
      tools/encoder/src/TMXTypes.cpp
  80. 252 0
      tools/encoder/src/TMXTypes.h
  81. 8 5
      tools/encoder/src/main.cpp
  82. 4 4
      tools/luagen/gameplay-luagen.vcxproj

+ 21 - 62
.gitignore

@@ -8,44 +8,25 @@
 ehthumbs.db
 Icon?
 Thumbs.db
-/.metadata
+.metadata
 .settings
-/169.254.0.1
-/usb
 /ipch
 /build
 /cmake
 /Debug
 /Release
-/Simulator
-/Simulator-Coverage
-/Simulator-Profile
-/Device-Debug
-/Device-Coverage
-/Device-Profile
-/Device-Release
-/gameplay-internal
 /api/xml
 /bin
 /external-deps
-/gameplay-samples
-/gameplay-api/html
-/gameplay-docs
 /api/html
-/gameplay-tests
+Makefile
+Makefile.Debug
+Makefile.Release
 gameplay.xcworkspace/xcshareddata/gameplay.xccheckout
 
 /gameplay/Debug
 /gameplay/DebugMem
 /gameplay/Release
-/gameplay/Simulator
-/gameplay/Simulator-Coverage
-/gameplay/Simulator-Profile
-/gameplay/Device-Debug
-/gameplay/Device-Coverage
-/gameplay/Device-Profile
-/gameplay/Device-Release
-/gameplay/windows
 /gameplay/android/NUL
 /gameplay/android/proguard.cfg
 /gameplay/android/proguard-project.txt
@@ -56,29 +37,32 @@ gameplay.xcworkspace/xcshareddata/gameplay.xccheckout
 /gameplay/android/obj
 /gameplay.xcworkspace/xcuserdata
 /gameplay/gameplay.xcodeproj/xcuserdata
+/gameplay/gameplay.vcxproj.user
 
+/tools/encoder
 /tools/encoder/Debug
 /tools/encoder/Release
 /tools/encoder/gameplay-encoder.xcodeproj/xcuserdata
+/tools/encoder/gameplay-encoder.xcodeproj/project.xcworkspace/
+/tools/encoder/gameplay-encoder.vcxproj.user
 
+/tools/luagen
 /tools/luagen/Release
 /tools/luagen/Debug
 /tools/luagen/gameplay-luagen.xcodeproj/xcuserdata
+/tools/luagen/gameplay-luagen.xcodeproj/project.xcworkspace/
 /tools/luagen/doxygen_entrydb_680.tmp
 /tools/luagen/doxygen_objdb_680.tmp
 /tools/luagen/xml
-/tools/luagen/gameplay-luagen.xcodeproj/project.xcworkspace/
+
+/tools/editor
+/tools/editor/src
+/tools/editor/Debug
+/tools/editor/Release
 
 /samples/browser/Debug
 /samples/browser/DebugMem
 /samples/browser/Release
-/samples/browser/Simulator
-/samples/browser/Simulator-Coverage
-/samples/browser/Simulator-Profile
-/samples/browser/Device-Debug
-/samples/browser/Device-Coverage
-/samples/browser/Device-Profile
-/samples/browser/Device-Release
 /samples/browser/res/shaders
 /samples/browser/res/ui
 /samples/browser/res/logo_powered_white.png
@@ -93,20 +77,11 @@ gameplay.xcworkspace/xcshareddata/gameplay.xccheckout
 /samples/browser/android/local.properties
 /samples/browser/android/proguard-project.txt
 /samples/browser/sample-browser.xcodeproj/xcuserdata
+/samples/browser/sample-browser.vcxproj.user
 
 /samples/character/Debug
 /samples/character/DebugMem
 /samples/character/Release
-/samples/character/Simulator
-/samples/character/Simulator-Coverage
-/samples/character/Simulator-Profile
-/samples/character/Device-Debug
-/samples/character/Device-Debug-QC
-/samples/character/Device-Coverage
-/samples/character/Device-Profile
-/samples/character/Device-Release
-/samples/character/Device-Release-QC
-/samples/character/game.config
 /samples/character/res/shaders
 /samples/character/res/ui
 /samples/character/res/logo_powered_white.png
@@ -123,20 +98,12 @@ gameplay.xcworkspace/xcshareddata/gameplay.xccheckout
 /samples/character/android/proguard.cfg
 /samples/character/res/gamepad.xcf
 /samples/character/sample-character.xcodeproj/xcuserdata
+/samples/character/sample-character.vcxproj.user
+/samples/character/game.config
 
 /samples/racer/Debug
 /samples/racer/DebugMem
 /samples/racer/Release
-/samples/racer/Simulator
-/samples/racer/Simulator-Coverage
-/samples/racer/Simulator-Profile
-/samples/racer/Device-Debug
-/samples/racer/Device-Debug-QC
-/samples/racer/Device-Coverage
-/samples/racer/Device-Profile
-/samples/racer/Device-Release
-/samples/racer/Device-Release-QC
-/samples/racer/game.config
 /samples/racer/res/shaders
 /samples/racer/res/ui
 /samples/racer/res/logo_powered_white.png
@@ -152,17 +119,12 @@ gameplay.xcworkspace/xcshareddata/gameplay.xccheckout
 /samples/racer/android/proguard-project.txt
 /samples/racer/android/proguard.cfg
 /samples/racer/sample-racer.xcodeproj/xcuserdata
+/samples/racer/sample-racer.vcxproj.user
+/samples/racer/game.config
 
 /samples/spaceship/Debug
 /samples/spaceship/DebugMem
 /samples/spaceship/Release
-/samples/spaceship/Simulator
-/samples/spaceship/Simulator-Coverage
-/samples/spaceship/Simulator-Profile
-/samples/spaceship/Device-Debug
-/samples/spaceship/Device-Coverage
-/samples/spaceship/Device-Profile
-/samples/spaceship/Device-Release
 /samples/spaceship/res/shaders
 /samples/spaceship/res/ui
 /samples/spaceship/res/logo_powered_white.png
@@ -178,7 +140,4 @@ gameplay.xcworkspace/xcshareddata/gameplay.xccheckout
 /samples/spaceship/android/proguard.cfg
 /samples/spaceship/android/proguard-project.txt
 /samples/spaceship/sample-spaceship.xcodeproj/xcuserdata
-
-/tools/encoder/windows
-/tools/luagen/windows
-/tools/encoder/gameplay-encoder.xcodeproj/project.xcworkspace/
+/samples/spaceship/sample-spaceship.vcxproj.user

+ 4 - 0
CHANGES.md

@@ -1,3 +1,7 @@
+## v4.0.0
+
+- Adds support for Visual Studio 2015.
+
 ## v3.0.0
 
 - Adds support for 2D Sprite, TileSet and Text.

+ 3 - 3
README.md

@@ -1,8 +1,8 @@
 <img src="https://raw.githubusercontent.com/gameplay3d/GamePlay/master/gameplay/res/icon.png" width=100/>
 
-## GamePlay v3.0.0
+## GamePlay v4.0.0
 
-GamePlay is an open-source, cross-platform native C++ game framework for creating 2D/3D mobile and desktop games.
+GamePlay is an open-source, cross-platform, C++ game framework/engine for creating 2D/3D mobile and desktop games.
 
 - [Website](http://www.gameplay3d.io/)
 - [Wiki](https://github.com/gameplay3d/GamePlay/wiki)
@@ -18,7 +18,7 @@ GamePlay is an open-source, cross-platform native C++ game framework for creatin
 - [Android](https://github.com/gameplay3d/GamePlay/wiki/Android-NDK-Setup)
 
 ## Roadmap for 'next' branch
-- [4.0.0](https://github.com/gameplay3d/GamePlay/milestones/4.0.0)
+- [5.0.0](https://github.com/gameplay3d/GamePlay/milestones/5.0.0)
 - [backlog](https://github.com/gameplay3d/GamePlay/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone)
 
 ## License

+ 12 - 9
gameplay/gameplay.pro

@@ -3,12 +3,14 @@
 # Project created by QtCreator
 #
 #-------------------------------------------------
-
 QT -= core gui
-
 TARGET = gameplay
 TEMPLATE = lib
 CONFIG += staticlib
+CONFIG += c++11
+CONFIG -= qt
+
+#DEFINES += GP_NO_PLATFORM
 
 SOURCES += src/AbsoluteLayout.cpp \
     src/AIAgent.cpp \
@@ -522,16 +524,13 @@ HEADERS += src/AbsoluteLayout.h \
     src/lua/lua_VertexFormatElement.h \
     src/lua/lua_VerticalLayout.h
 
-CONFIG += c++11
-
 INCLUDEPATH += $$PWD/../gameplay/src
 INCLUDEPATH += $$PWD/../external-deps/include
+DEFINES += GP_USE_GAMEPAD
 
-# linux
 linux: SOURCES += src/PlatformLinux.cpp
 linux: SOURCES += src/gameplay-main-linux.cpp
 linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
-linux: DEFINES += GP_USE_GAMEPAD
 linux: DEFINES += __linux__
 linux: INCLUDEPATH += /usr/include/gtk-2.0
 linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/gtk-2.0/include
@@ -547,16 +546,20 @@ linux: INCLUDEPATH += /usr/include/pixman-1
 linux: INCLUDEPATH += /usr/include/libpng12
 linux: INCLUDEPATH += /usr/include/harfbuzz
 
-# macosx
 macx: OBJECTIVE_SOURCES += src/PlatformMacOSX.mm
 macx: OBJECTIVE_SOURCES += src/gameplay-main-macosx.mm
 macx: QMAKE_CXXFLAGS += -x c++ -stdlib=libc++ -w -arch x86_64
 macx: QMAKE_OBJECTIVE_CFLAGS += -x objective-c++ -stdlib=libc++ -w -arch x86_64
-macx: DEFINES += GP_USE_GAMEPAD
-macx: LIBS += -L$$PWD/../../external-deps/lib/macosx/x86_64/ -lgameplay-deps
 macx: LIBS += -F/System/Library/Frameworks -framework GameKit
 macx: LIBS += -F/System/Library/Frameworks -framework IOKit
 macx: LIBS += -F/System/Library/Frameworks -framework QuartzCore
 macx: LIBS += -F/System/Library/Frameworks -framework OpenAL
 macx: LIBS += -F/System/Library/Frameworks -framework OpenGL
 macx: LIBS += -F/System/Library/Frameworks -framework Cocoa
+
+win32: SOURCES += src/PlatformWindows.cpp
+win32: SOURCES += src/gameplay-main-windows.cpp
+win32: DEFINES += WIN32 _UNICODE UNICODE
+win32: INCLUDEPATH += $$(DXSDK_DIR)Include
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34100
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34189

+ 5 - 5
gameplay/gameplay.vcxproj

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="DebugMem|x64">
       <Configuration>DebugMem</Configuration>
@@ -569,20 +569,20 @@
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>false</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -616,7 +616,7 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;GP_USE_GAMEPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>$(ProjectDir)src;..\external-deps\include;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>$(ProjectDir)src;..\external-deps\include;$(DXSDK_DIR)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>
       </RuntimeTypeInfo>
       <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>

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

@@ -2262,6 +2262,8 @@
 		4234D99114686BB6003031B3 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = src/gameplay.h;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx;
 				SUPPORTED_PLATFORMS = "iphonesimulator macosx iphoneos";
@@ -2272,6 +2274,8 @@
 		4234D99214686BB6003031B3 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = src/gameplay.h;
 				SDKROOT = macosx;
 				SUPPORTED_PLATFORMS = "iphonesimulator macosx iphoneos";
 				VALID_ARCHS = "armv7 armv7s arm64 i386 x86_64";
@@ -2291,8 +2295,6 @@
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
 				GCC_OPTIMIZATION_LEVEL = 0;
-				GCC_PRECOMPILE_PREFIX_HEADER = NO;
-				GCC_PREFIX_HEADER = "";
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"DEBUG=1",
 					"$(inherited)",
@@ -2332,8 +2334,6 @@
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
 				GCC_C_LANGUAGE_STANDARD = gnu99;
 				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
-				GCC_PRECOMPILE_PREFIX_HEADER = NO;
-				GCC_PREFIX_HEADER = "";
 				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
@@ -2369,8 +2369,6 @@
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
 				GCC_OPTIMIZATION_LEVEL = 0;
-				GCC_PRECOMPILE_PREFIX_HEADER = NO;
-				GCC_PREFIX_HEADER = "";
 				GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
 				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
 				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
@@ -2415,8 +2413,6 @@
 				FRAMEWORK_SEARCH_PATHS = "$(inherited)";
 				GCC_C_LANGUAGE_STANDARD = gnu99;
 				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
-				GCC_PRECOMPILE_PREFIX_HEADER = NO;
-				GCC_PREFIX_HEADER = "";
 				GCC_PREPROCESSOR_DEFINITIONS = "";
 				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = NO;

+ 4 - 4
gameplay/src/AnimationClip.cpp

@@ -588,7 +588,7 @@ bool AnimationClip::update(float elapsedTime)
 
 void AnimationClip::onBegin()
 {
-    addRef();
+    this->addRef();
 
     // Initialize animation to play.
     setClipStateBit(CLIP_IS_STARTED_BIT);
@@ -622,12 +622,12 @@ void AnimationClip::onBegin()
     // Fire script begin event
     fireScriptEvent<void>(GP_GET_SCRIPT_EVENT(AnimationClip, clipBegin), this);
 
-    release();
+    this->release();
 }
 
 void AnimationClip::onEnd()
 {
-    addRef();
+    this->addRef();
 
     _blendWeight = 1.0f;
     resetClipStateBit(CLIP_ALL_BITS);
@@ -647,7 +647,7 @@ void AnimationClip::onEnd()
     // Fire script end event
     fireScriptEvent<void>(GP_GET_SCRIPT_EVENT(AnimationClip, clipEnd), this);
 
-    release();
+    this->release();
 }
 
 bool AnimationClip::isClipStateBitSet(unsigned char bit) const

+ 6 - 0
gameplay/src/AudioController.cpp

@@ -81,11 +81,17 @@ void AudioController::pause()
         _pausingSource = NULL;
         itr++;
     }
+#ifdef ALC_SOFT_pause_device
+    alcDevicePauseSOFT(_alcDevice);
+#endif
 }
 
 void AudioController::resume()
 {   
     alcMakeContextCurrent(_alcContext);
+#ifdef ALC_SOFT_pause_device
+    alcDeviceResumeSOFT(_alcDevice);
+#endif
 
     std::set<AudioSource*>::iterator itr = _playingSources.begin();
 

+ 3 - 0
gameplay/src/Base.h

@@ -182,6 +182,8 @@ extern int strcmpnocase(const char* s1, const char* s2);
 #ifdef __ANDROID__
     #include <AL/al.h>
     #include <AL/alc.h>
+    #define AL_ALEXT_PROTOTYPES
+    #include <AL/alext.h>
 #elif WIN32
     #define AL_LIBTYPE_STATIC
     #include <AL/al.h>
@@ -218,6 +220,7 @@ using std::va_list;
     #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
     #define glClearDepth glClearDepthf
     #define OPENGL_ES
+    #define GP_USE_VAO
 #elif WIN32
         #define WIN32_LEAN_AND_MEAN
         #define GLEW_STATIC

+ 24 - 12
gameplay/src/Container.cpp

@@ -67,6 +67,7 @@ Container::~Container()
     std::vector<Control*>::iterator it;
     for (it = _controls.begin(); it < _controls.end(); it++)
     {
+        (*it)->_parent = nullptr;
         SAFE_RELEASE((*it));
     }
     SAFE_RELEASE(_layout);
@@ -424,6 +425,8 @@ const Vector2& Container::getScrollPosition() const
 void Container::setScrollPosition(const Vector2& scrollPosition)
 {
     _scrollPosition = scrollPosition;
+    setDirty(DIRTY_BOUNDS);
+    setChildrenDirty(DIRTY_BOUNDS, true);
 }
 
 Animation* Container::getAnimation(const char* id) const
@@ -541,9 +544,6 @@ void Container::updateState(State state)
 
 void Container::updateBounds()
 {
-    // Compute total bounds of container
-    Control::updateBounds();
-
     // Handle automatically sizing based on our children
     if (_autoSize != AUTO_SIZE_NONE)
     {
@@ -554,9 +554,11 @@ void Container::updateBounds()
             for (size_t i = 0, count = _controls.size(); i < count; ++i)
             {
                 Control* ctrl = _controls[i];
-                if (ctrl->isVisible() && !ctrl->isXPercentage() && !ctrl->isWidthPercentage())
+                if (ctrl->isVisible() && !ctrl->isWidthPercentage())
                 {
-                    float w = ctrl->getX() + ctrl->getWidth();
+                    float w = ctrl->getWidth() + ctrl->getMargin().right;
+                    if (!ctrl->isXPercentage())
+                        w += ctrl->getX();
                     if (width < w)
                         width = w;
                 }
@@ -572,9 +574,11 @@ void Container::updateBounds()
             for (size_t i = 0, count = _controls.size(); i < count; ++i)
             {
                 Control* ctrl = _controls[i];
-                if (ctrl->isVisible() && !ctrl->isYPercentage() && !ctrl->isHeightPercentage())
+                if (ctrl->isVisible() && !ctrl->isHeightPercentage())
                 {
-                    float h = ctrl->getY() + ctrl->getHeight();
+                    float h = ctrl->getHeight() + ctrl->getMargin().bottom;
+                    if (!ctrl->isYPercentage())
+                        h += ctrl->getY();
                     if (height < h)
                         height = h;
                 }
@@ -584,6 +588,9 @@ void Container::updateBounds()
         }
     }
 
+    // Compute total bounds of container
+    Control::updateBounds();
+
     // Update layout to position children correctly within us
     GP_ASSERT(_layout);
     _layout->update(this);
@@ -631,7 +638,7 @@ bool Container::updateChildBounds()
             if (changed)
             {
                 Control* parent = this;
-                while (parent && parent->_autoSize != AUTO_SIZE_NONE)
+                while (parent && (parent->_autoSize != AUTO_SIZE_NONE || static_cast<Container *>(parent)->getLayout()->getType() != Layout::LAYOUT_ABSOLUTE))
                 {
                     parent->setDirty(DIRTY_BOUNDS);
                     parent = parent->_parent;
@@ -1081,6 +1088,9 @@ void Container::updateScroll()
     {
         Control* control = _controls[i];
 
+        if (!control->isVisible())
+            continue;
+
         const Rectangle& bounds = control->getBounds();
         const Theme::Margin& margin = control->getMargin();
 
@@ -1129,34 +1139,35 @@ void Container::updateScroll()
     }
 
     // Stop scrolling when the far edge is reached.
+    Vector2 lastScrollPosition(_scrollPosition);
+
     if (-_scrollPosition.x > _totalWidth - clipWidth)
     {
         _scrollPosition.x = -(_totalWidth - clipWidth);
         _scrollingVelocity.x = 0;
-        dirty = true;
     }
     
     if (-_scrollPosition.y > _totalHeight - clipHeight)
     {
         _scrollPosition.y = -(_totalHeight - clipHeight);
         _scrollingVelocity.y = 0;
-        dirty = true;
     }
 
     if (_scrollPosition.x > 0)
     {
         _scrollPosition.x = 0;
         _scrollingVelocity.x = 0;
-        dirty = true;
     }
 
     if (_scrollPosition.y > 0)
     {
         _scrollPosition.y = 0;
         _scrollingVelocity.y = 0;
-        dirty = true;
     }
 
+    if (_scrollPosition != lastScrollPosition)
+        dirty = true;
+
     float scrollWidth = 0;
     if (clipWidth < _totalWidth)
         scrollWidth = (clipWidth / _totalWidth) * clipWidth;
@@ -1286,6 +1297,7 @@ bool Container::touchEventScroll(Touch::TouchEvent evt, int x, int y, unsigned i
             _scrollingLastTime = gameTime;
             setDirty(DIRTY_BOUNDS);
             setChildrenDirty(DIRTY_BOUNDS, true);
+            updateScroll();
             return false;
         }
         break;

+ 17 - 10
gameplay/src/Control.cpp

@@ -973,6 +973,11 @@ void Control::addListener(Control::Listener* listener, int eventFlags)
     {
         addSpecificListener(listener, Control::Listener::TEXT_CHANGED);
     }
+
+    if ((eventFlags & Control::Listener::ACTIVATED) == Control::Listener::ACTIVATED)
+    {
+        addSpecificListener(listener, Control::Listener::ACTIVATED);
+    }
 }
 
 void Control::removeListener(Control::Listener* listener)
@@ -1055,7 +1060,7 @@ void Control::notifyListeners(Control::Listener::EventType eventType)
     // This method runs untrusted code by notifying listeners of events.
     // If the user calls exit() or otherwise releases this control, we
     // need to keep it alive until the method returns.
-    addRef();
+    this->addRef();
 
     controlEvent(eventType);
 
@@ -1075,7 +1080,7 @@ void Control::notifyListeners(Control::Listener::EventType eventType)
 
     fireScriptEvent<void>(GP_GET_SCRIPT_EVENT(Control, controlEvent), dynamic_cast<void*>(this), eventType);
 
-    release();
+    this->release();
 }
 
 void Control::controlEvent(Control::Listener::EventType evt)
@@ -1127,15 +1132,15 @@ bool Control::updateBoundsInternal(const Vector2& offset)
         _dirtyBits &= ~DIRTY_STATE;
     }
 
-    // Clear our dirty bounds bit
-    bool dirtyBounds = (_dirtyBits & DIRTY_BOUNDS) != 0;
-    _dirtyBits &= ~DIRTY_BOUNDS;
-
     // If we are a container, always update child bounds first
     bool changed = false;
     if (isContainer())
         changed = static_cast<Container*>(this)->updateChildBounds();
 
+    // Clear our dirty bounds bit
+    bool dirtyBounds = (_dirtyBits & DIRTY_BOUNDS) != 0;
+    _dirtyBits &= ~DIRTY_BOUNDS;
+
     if (dirtyBounds)
     {
         // Store old bounds so we can determine if they change
@@ -1167,12 +1172,14 @@ void Control::updateBounds()
 
     const Rectangle parentAbsoluteBounds = _parent ? _parent->_viewportBounds : Rectangle(0, 0, game->getViewport().width, game->getViewport().height);
 
+    const Theme::Margin& margin = _style->getMargin();
+
     // Calculate local unclipped bounds.
     _bounds.set(_relativeBounds);
     if (isXPercentage())
-        _bounds.x *= parentAbsoluteBounds.width;
+        _bounds.x = _bounds.x * parentAbsoluteBounds.width + margin.left;
     if (isYPercentage())
-        _bounds.y *= parentAbsoluteBounds.height;
+        _bounds.y = _bounds.y * parentAbsoluteBounds.height + margin.top;
     if (isWidthPercentage())
         _bounds.width *= parentAbsoluteBounds.width;
     if (isHeightPercentage())
@@ -1207,7 +1214,7 @@ void Control::updateBounds()
         }
         else if ((_alignment & Control::ALIGN_VCENTER) == Control::ALIGN_VCENTER)
         {
-            _bounds.y = clipHeight * 0.5f - _bounds.height * 0.5f;
+            _bounds.y = clipHeight * 0.5f - _bounds.height * 0.5f + (margin.top - margin.bottom) * 0.5f;
         }
         else if ((_alignment & Control::ALIGN_TOP) == Control::ALIGN_TOP)
         {
@@ -1221,7 +1228,7 @@ void Control::updateBounds()
         }
         else if ((_alignment & Control::ALIGN_HCENTER) == Control::ALIGN_HCENTER)
         {
-            _bounds.x = clipWidth * 0.5f - _bounds.width * 0.5f;
+            _bounds.x = clipWidth * 0.5f - _bounds.width * 0.5f + (margin.left - margin.right) * 0.5f;
         }
         else if ((_alignment & Control::ALIGN_LEFT) == Control::ALIGN_LEFT)
         {

+ 5 - 0
gameplay/src/Control.h

@@ -169,6 +169,11 @@ public:
              */
             RIGHT_CLICK     = 0x40,
 
+            /**
+             * Event triggered when a control is activated in another manner (such as pressing enter in text control)
+             */
+            ACTIVATED       = 0x80,
+
             /**
              * Event triggered when a control gains focus.
              */

+ 10 - 0
gameplay/src/DebugNew.cpp

@@ -4,6 +4,8 @@
 #include <exception>
 #include <cstdio>
 #include <cstdarg>
+#include <thread>
+#include <mutex>
 
 #ifdef WIN32
 #include <windows.h>
@@ -31,6 +33,12 @@ struct MemoryAllocationRecord
 MemoryAllocationRecord* __memoryAllocations = 0;
 int __memoryAllocationCount = 0;
 
+static std::mutex& getMemoryAllocationMutex()
+{
+    static std::mutex m;
+    return m;
+}
+
 void* debugAlloc(std::size_t size, const char* file, int line);
 void debugFree(void* p);
 
@@ -105,6 +113,7 @@ void* debugAlloc(std::size_t size, const char* file, int line)
     // Move memory pointer past record
     mem += sizeof(MemoryAllocationRecord);
 
+    std::lock_guard<std::mutex> lock(getMemoryAllocationMutex());
     rec->address = (unsigned long)mem;
     rec->size = (unsigned int)size;
     rec->file = file;
@@ -194,6 +203,7 @@ void debugFree(void* p)
     }
 
     // Link this item out
+    std::lock_guard<std::mutex> lock(getMemoryAllocationMutex());
     if (__memoryAllocations == rec)
         __memoryAllocations = rec->next;
     if (rec->prev)

+ 3 - 1
gameplay/src/Font.cpp

@@ -1371,9 +1371,11 @@ int Font::getIndexOrLocation(const char* text, const Rectangle& area, unsigned i
                 }
 
                 // Check against inLocation.
+                //  Note: g.width is smaller than g.advance, so if I only check against g.width, I will 
+                //  miss locations towards the right of the character.
                 if (destIndex == (int)charIndex ||
                     (destIndex == -1 &&
-                    inLocation.x >= xPos && inLocation.x < floor(xPos + g.width*scale + spacing) &&
+                    inLocation.x >= xPos && inLocation.x < floor(xPos + g.advance*scale + spacing) &&
                     inLocation.y >= yPos && inLocation.y < yPos + size))
                 {
                     outLocation->x = xPos;

+ 24 - 8
gameplay/src/FrameBuffer.cpp

@@ -76,13 +76,13 @@ FrameBuffer* FrameBuffer::create(const char* id)
     return create(id, 0, 0);
 }
 
-FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned int height)
+FrameBuffer* FrameBuffer::create(const char* id, unsigned int width, unsigned int height, Texture::Format format)
 {
     RenderTarget* renderTarget = NULL;
     if (width > 0 && height > 0)
     {
         // Create a default RenderTarget with same ID.
-        renderTarget = RenderTarget::create(id, width, height);
+        renderTarget = RenderTarget::create(id, width, height, format);
         if (renderTarget == NULL)
         {
             GP_ERROR("Failed to create render target for frame buffer.");
@@ -195,8 +195,24 @@ void FrameBuffer::setRenderTarget(RenderTarget* target, unsigned int index, GLen
 
         // Now set this target as the color attachment corresponding to index.
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
-        GLenum attachment = GL_COLOR_ATTACHMENT0 + index;
-        GL_ASSERT( glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, textureTarget, _renderTargets[index]->getTexture()->getHandle(), 0) );
+        GLenum attachment;
+        if (target->getTexture()->getFormat() == Texture::DEPTH)
+        {
+            attachment = GL_DEPTH_ATTACHMENT;
+            GL_ASSERT( glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, textureTarget, _renderTargets[index]->getTexture()->getHandle(), 0));
+#ifndef OPENGL_ES            
+            glDrawBuffer(GL_NONE);
+            glReadBuffer(GL_NONE);
+#elif defined(GL_ES_VERSION_3_0) && GL_ES_VERSION_3_0
+            glDrawBuffers(0, NULL);
+#endif
+        }
+        else
+        {
+            attachment = GL_COLOR_ATTACHMENT0 + index;
+            GL_ASSERT( glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, textureTarget, _renderTargets[index]->getTexture()->getHandle(), 0) );
+        }
+
         GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
         if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
         {
@@ -274,9 +290,9 @@ bool FrameBuffer::isDefault() const
     return (this == _defaultFrameBuffer);
 }
 
-FrameBuffer* FrameBuffer::bind()
+FrameBuffer* FrameBuffer::bind(GLenum type)
 {
-    GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _handle) );
+    GL_ASSERT( glBindFramebuffer(type, _handle) );
     FrameBuffer* previousFrameBuffer = _currentFrameBuffer;
     _currentFrameBuffer = this;
     return previousFrameBuffer;
@@ -303,9 +319,9 @@ Image* FrameBuffer::createScreenshot(Image::Format format)
     return screenshot;
 }
 
-FrameBuffer* FrameBuffer::bindDefault()
+FrameBuffer* FrameBuffer::bindDefault(GLenum type)
 {
-    GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, _defaultFrameBuffer->_handle) );
+    GL_ASSERT( glBindFramebuffer(type, _defaultFrameBuffer->_handle) );
     _currentFrameBuffer = _defaultFrameBuffer;
     return _defaultFrameBuffer;
 }

+ 3 - 3
gameplay/src/FrameBuffer.h

@@ -59,7 +59,7 @@ public:
      * @return A newly created FrameBuffer.
      * @script{create}
      */
-    static FrameBuffer* create(const char* id, unsigned int width, unsigned int height);
+    static FrameBuffer* create(const char* id, unsigned int width, unsigned int height, Texture::Format format = Texture::RGBA);
 
     /**
      * Get a named FrameBuffer from its ID.
@@ -159,7 +159,7 @@ public:
      *
      * @ return The currently bound framebuffer.
      */
-    FrameBuffer* bind();
+    FrameBuffer* bind(GLenum type = GL_FRAMEBUFFER);
 
     /**
      * Records a screenshot of what is stored on the current FrameBuffer.
@@ -183,7 +183,7 @@ public:
      *
      * @ return The default framebuffer.
      */
-    static FrameBuffer* bindDefault();
+    static FrameBuffer* bindDefault(GLenum type = GL_FRAMEBUFFER);
 
     /**
      * Gets the currently bound FrameBuffer.

+ 1 - 1
gameplay/src/Label.h

@@ -47,7 +47,7 @@ public:
      *
      * @param text The text to display.
      */
-    void setText(const char* text);
+    virtual void setText(const char* text);
 
     /**
      * Get the text displayed by this label.

+ 11 - 0
gameplay/src/Matrix.cpp

@@ -393,6 +393,17 @@ void Matrix::createRotationZ(float angle, Matrix* dst)
     dst->m[5] = c;
 }
 
+void Matrix::createFromEuler(float yaw, float pitch, float roll, Matrix* dst)
+{
+	GP_ASSERT(dst);
+
+	memcpy(dst, MATRIX_IDENTITY, MATRIX_SIZE);
+	
+	dst->rotateY(yaw);
+	dst->rotateX(pitch);
+	dst->rotateZ(roll);
+}
+
 void Matrix::createTranslation(const Vector3& translation, Matrix* dst)
 {
     GP_ASSERT(dst);

+ 12 - 0
gameplay/src/Matrix.h

@@ -8,6 +8,7 @@ namespace gameplay
 {
 
 class Plane;
+class Quaternion;
 
 /**
  * Defines a 4 x 4 floating point matrix representing a 3D transformation.
@@ -317,6 +318,17 @@ public:
      */
     static void createRotationZ(float angle, Matrix* dst);
 
+	/**
+	* Creates a matrix describing the yaw, pitch and roll rotations
+	*
+	* @param yaw The yaw angle (in radians)
+	* @param pitch The pitch angle (in radians)
+	* @param roll The roll angle (in radians)
+	* @param dst A matrix to store the result in.
+	*/
+	static void createFromEuler(float yaw, float pitch, float roll, Matrix* dst);
+
+
     /**
      * Creates a translation matrix.
      *

+ 1 - 1
gameplay/src/ParticleEmitter.cpp

@@ -118,7 +118,7 @@ ParticleEmitter* ParticleEmitter::create(Properties* properties)
     bool spriteAnimated = sprite->getBool("animated");
     bool spriteLooped = sprite->getBool("looped");
     int spriteFrameCount = sprite->getInt("frameCount");
-    int spriteFrameRandomOffset = sprite->getInt("frameRandomOffset");
+    int spriteFrameRandomOffset = min(sprite->getInt("frameRandomOffset"), spriteFrameCount);
     float spriteFrameDuration = sprite->getFloat("frameDuration");
 
     // Emitter properties.

+ 46 - 2
gameplay/src/PlatformAndroid.cpp

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef __ANDROID__
 
 #include "Base.h"
@@ -324,7 +325,6 @@ static bool initEGL()
     
     if (strstr(__glExtensions, "GL_OES_vertex_array_object") || strstr(__glExtensions, "GL_ARB_vertex_array_object"))
     {
-        // Disable VAO extension for now.
         glBindVertexArray = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
         glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES");
         glGenVertexArrays = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
@@ -757,6 +757,16 @@ static float clampFuzz(float value, float fuzz)
 // Process the next input event.
 static int32_t engine_handle_input(struct android_app* app, AInputEvent* event)
 {
+    if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY
+        && AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN
+        && AKeyEvent_getKeyCode(event) == AKEYCODE_BACK
+        && __displayKeyboard
+        )
+    {
+        Game::getInstance()->displayKeyboard(false);
+        return 1;
+    }
+
     int32_t deviceId = AInputEvent_getDeviceId(event);
     int32_t source = AInputEvent_getSource(event);
 
@@ -1241,6 +1251,36 @@ static void engine_handle_cmd(struct android_app* app, int32_t cmd)
     }
 }
 
+// Patch andoid_native_app_glue.c process_input function since it freezes app when user tries to hide keyboard
+// See: http://stackoverflow.com/questions/15913080/crash-when-closing-soft-keyboard-while-using-native-activity
+#include <android/log.h>
+#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
+#define LOGV(...)  ((void)0)
+static void process_input( struct android_app* app, struct android_poll_source* source)
+{
+    AInputEvent* event = NULL;
+
+    while(AInputQueue_getEvent(app->inputQueue, &event) >= 0)
+    {
+        int type = AInputEvent_getType(event);
+        LOGV("New input event: type=%d\n", AInputEvent_getType(event));
+
+        bool skip_predispatch
+            =  AInputEvent_getType(event)  == AINPUT_EVENT_TYPE_KEY
+            && AKeyEvent_getKeyCode(event) == AKEYCODE_BACK;
+
+        // skip predispatch (all it does is send to the IME)
+        if (!skip_predispatch && AInputQueue_preDispatchEvent(app->inputQueue, event))
+            return;
+
+        int32_t handled = 0;
+        if (app->onInputEvent != NULL)
+            handled = app->onInputEvent(app, event);
+        AInputQueue_finishEvent(app->inputQueue, event, handled);
+    }
+}
+
+
 Platform::Platform(Game* game)
     : _game(game)
 {
@@ -1312,6 +1352,9 @@ int Platform::enterMessagePump()
     __state->onAppCmd = engine_handle_cmd;
     __state->onInputEvent = engine_handle_input;
     
+    // Apply patch to native process_input function
+    __state->inputPollSource.process = process_input;
+
     // Prepare to monitor accelerometer.
     __sensorManager = ASensorManager_getInstance();
     __accelerometerSensor = ASensorManager_getDefaultSensor(__sensorManager, ASENSOR_TYPE_ACCELEROMETER);
@@ -1399,7 +1442,7 @@ int Platform::enterMessagePump()
                 }
             }
         }
-            
+
         // Display the keyboard.
         gameplay::displayKeyboard(__state, __displayKeyboard);
     }
@@ -1787,4 +1830,5 @@ JNIEXPORT void JNICALL Java_org_gameplay3d_GamePlayNativeActivity_screenOrientat
 }
 
 #endif
+#endif
 

+ 2 - 0
gameplay/src/PlatformLinux.cpp

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef __linux__
 
 #include "Base.h"
@@ -1745,3 +1746,4 @@ std::string Platform::displayFileDialog(size_t mode, const char* title, const ch
 }
 
 #endif
+#endif

+ 2 - 0
gameplay/src/PlatformMacOSX.mm

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef __APPLE__
 
 #include "Base.h"
@@ -2391,3 +2392,4 @@ std::string Platform::displayFileDialog(size_t mode, const char* title, const ch
 }
 
 #endif
+#endif

+ 2 - 0
gameplay/src/PlatformWindows.cpp

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef WIN32
 
 #include "Base.h"
@@ -1437,3 +1438,4 @@ std::string Platform::displayFileDialog(size_t mode, const char* title, const ch
 }
 
 #endif
+#endif

+ 2 - 0
gameplay/src/PlatformiOS.mm

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef __APPLE__
 
 #include "Base.h"
@@ -1730,3 +1731,4 @@ std::string Platform::displayFileDialog(size_t mode, const char* title, const ch
 }
 
 #endif
+#endif

+ 7 - 4
gameplay/src/Properties.cpp

@@ -26,12 +26,12 @@ void calculateNamespacePath(const std::string& urlString, std::string& fileStrin
 Properties* getPropertiesFromNamespacePath(Properties* properties, const std::vector<std::string>& namespacePath);
 
 Properties::Properties()
-    : _variables(NULL), _dirPath(NULL), _parent(NULL)
+    : _variables(NULL), _dirPath(NULL), _visited(false), _parent(NULL)
 {
 }
 
 Properties::Properties(const Properties& copy)
-    : _namespace(copy._namespace), _id(copy._id), _parentID(copy._parentID), _properties(copy._properties), _variables(NULL), _dirPath(NULL), _parent(copy._parent)
+    : _namespace(copy._namespace), _id(copy._id), _parentID(copy._parentID), _properties(copy._properties), _variables(NULL), _dirPath(NULL), _visited(false), _parent(copy._parent)
 {
     setDirectoryPath(copy._dirPath);
     _namespaces = std::vector<Properties*>();
@@ -45,14 +45,14 @@ Properties::Properties(const Properties& copy)
 }
 
 Properties::Properties(Stream* stream)
-    : _variables(NULL), _dirPath(NULL), _parent(NULL)
+    : _variables(NULL), _dirPath(NULL), _visited(false), _parent(NULL)
 {
     readProperties(stream);
     rewind();
 }
 
 Properties::Properties(Stream* stream, const char* name, const char* id, const char* parentID, Properties* parent)
-    : _namespace(name), _variables(NULL), _dirPath(NULL), _parent(parent)
+    : _namespace(name), _variables(NULL), _dirPath(NULL), _visited(false), _parent(parent)
 {
     if (id)
     {
@@ -459,9 +459,11 @@ void Properties::resolveInheritance(const char* id)
         // If the namespace has a parent ID, find the parent.
         if (!derived->_parentID.empty())
         {
+            derived->_visited = true;
             Properties* parent = getNamespace(derived->_parentID.c_str());
             if (parent)
             {
+                GP_ASSERT(!parent->_visited);
                 resolveInheritance(parent->getId());
 
                 // Copy the child.
@@ -490,6 +492,7 @@ void Properties::resolveInheritance(const char* id)
                 // Delete the child copy.
                 SAFE_DELETE(overrides);
             }
+            derived->_visited = false;
         }
 
         // Resolve inheritance within this namespace.

+ 1 - 0
gameplay/src/Properties.h

@@ -563,6 +563,7 @@ private:
     std::vector<Properties*>::const_iterator _namespacesItr;
     std::vector<Property>* _variables;
     std::string* _dirPath;
+    bool _visited;
     Properties* _parent;
 };
 

+ 50 - 0
gameplay/src/Quaternion.cpp

@@ -60,6 +60,27 @@ bool Quaternion::isZero() const
     return x == 0.0f && y == 0.0f && z == 0.0f && w == 0.0f;
 }
 
+void Quaternion::createFromEuler(float yaw, float pitch, float roll, Quaternion* dst)
+{
+	GP_ASSERT(dst);
+
+	pitch *= 0.5f;
+	yaw *= 0.5f;
+	roll *= 0.5f;
+
+	float sinp = sin(pitch);
+	float siny = sin(yaw);
+	float sinr = sin(roll);
+	float cosp = cos(pitch);
+	float cosy = cos(yaw);
+	float cosr = cos(roll);
+
+	dst->w = cosp * cosy * cosr + sinp * siny * sinr;
+	dst->x = sinp * cosy * cosr - cosp * siny * sinr;
+	dst->y = cosp * siny * cosr + sinp * cosy * sinr;
+	dst->z = cosp * cosy * sinr - sinp * siny * cosr;
+}
+
 void Quaternion::createFromRotationMatrix(const Matrix& m, Quaternion* dst)
 {
     m.getRotation(dst);
@@ -80,6 +101,17 @@ void Quaternion::createFromAxisAngle(const Vector3& axis, float angle, Quaternio
     dst->w = cosf(halfAngle);
 }
 
+void Quaternion::computeEuler(float* yaw, float* pitch, float* roll)
+{
+	GP_ASSERT(yaw);
+	GP_ASSERT(pitch);
+	GP_ASSERT(roll);
+
+	*pitch = std::atan2(2 * (w*x + y*z), 1 - 2 * (x*x + y*y));
+	*yaw = std::asin(2 * (w*y - z*x));
+	*roll = atan2(2 * (w*z + x*y), 1 - 2 * (y*y + z*z));
+}
+
 void Quaternion::conjugate()
 {
     conjugate(this);
@@ -183,6 +215,24 @@ void Quaternion::normalize(Quaternion* dst) const
     dst->w *= n;
 }
 
+void Quaternion::rotatePoint(const Vector3& point, Vector3* dst) const
+{
+	Quaternion vecQuat;
+	Quaternion resQuat;
+	vecQuat.x = point.x;
+	vecQuat.y = point.y;
+	vecQuat.z = point.z;
+	vecQuat.w = 0.0f;
+
+	Quaternion conQuat;
+	this->conjugate(&conQuat);
+
+	resQuat = vecQuat * conQuat;
+	resQuat = (*this) * resQuat;
+
+	dst->set(resQuat.x, resQuat.y, resQuat.z);
+}
+
 void Quaternion::set(float x, float y, float z, float w)
 {
     this->x = x;

+ 33 - 0
gameplay/src/Quaternion.h

@@ -139,6 +139,17 @@ public:
      */
     bool isZero() const;
 
+	/**
+	* Creates this quaternion equal to the rotation from the specified euler angles
+	* and stores the result in dst.
+	*
+	* @param yaw The yaw angle (in radians)
+	* @param pitch The pitch angle (in radians)
+	* @param roll The roll angle (in radians)
+	* @param dst A quaternion to store the result in.
+	*/
+	static void createFromEuler(float yaw, float pitch, float roll, Quaternion* dst);
+
     /**
      * Creates a quaternion equal to the rotational part of the specified matrix
      * and stores the result in dst.
@@ -158,6 +169,16 @@ public:
      */
     static void createFromAxisAngle(const Vector3& axis, float angle, Quaternion* dst);
 
+	/**
+	* Calculates (in radians) the yaw, pitch and roll angles of this quaternion
+	* and stores the results in the specified pointers.
+	*
+	* @param yaw The returned yaw angle
+	* @param pitch The returned pitch angle
+	* @param roll The returned roll angle
+	*/
+	void computeEuler(float* yaw, float* pitch, float* roll);
+	
     /**
      * Sets this quaternion to the conjugate of itself.
      */
@@ -231,6 +252,18 @@ public:
      */
     void normalize(Quaternion* dst) const;
 
+	/**
+	* Rotate the specified point by this quaternion
+	* and stores the result in dst
+	*
+	* Note: The point must normalized.
+	*
+	* @param point The vector to rotate.
+	* @param dst The vector to store the result.
+
+	*/
+	void rotatePoint(const Vector3& point, Vector3* dst) const;
+
     /**
      * Sets the elements of the quaternion to the specified values.
      *

+ 2 - 0
gameplay/src/Ref.cpp

@@ -32,11 +32,13 @@ Ref::~Ref()
 
 void Ref::addRef()
 {
+    GP_ASSERT(_refCount > 0 && _refCount < 1000000);
     ++_refCount;
 }
 
 void Ref::release()
 {
+    GP_ASSERT(_refCount > 0 && _refCount < 1000000);
     if ((--_refCount) <= 0)
     {
 #ifdef GP_USE_MEM_LEAK_DETECTION

+ 2 - 2
gameplay/src/RenderTarget.cpp

@@ -23,10 +23,10 @@ RenderTarget::~RenderTarget()
     }
 }
 
-RenderTarget* RenderTarget::create(const char* id, unsigned int width, unsigned int height)
+RenderTarget* RenderTarget::create(const char* id, unsigned int width, unsigned int height, Texture::Format format)
 {
     // Create a new texture with the given width.
-    Texture* texture = Texture::create(Texture::RGBA, width, height, NULL, false);
+    Texture* texture = Texture::create(format, width, height, NULL, false);
     if (texture == NULL)
     {
         GP_ERROR("Failed to create texture for render target.");

+ 1 - 1
gameplay/src/RenderTarget.h

@@ -29,7 +29,7 @@ public:
      * @return A newly created RenderTarget.
      * @script{create}
      */
-    static RenderTarget* create(const char* id, unsigned int width, unsigned int height);
+    static RenderTarget* create(const char* id, unsigned int width, unsigned int height, Texture::Format format = Texture::RGBA);
 
     /**
      * Create a RenderTarget from the given Texture and add it to the list of

+ 9 - 1
gameplay/src/SceneLoader.cpp

@@ -99,7 +99,8 @@ Scene* SceneLoader::loadInternal(const char* url)
         SceneNodeProperty::SCRIPT |
         SceneNodeProperty::SPRITE |
         SceneNodeProperty::TILESET |
-        SceneNodeProperty::TEXT);
+        SceneNodeProperty::TEXT |
+        SceneNodeProperty::ENABLED);
     applyNodeProperties(sceneProperties, SceneNodeProperty::COLLISION_OBJECT);
 
     // Apply node tags
@@ -439,6 +440,9 @@ void SceneLoader::applyNodeProperty(SceneNode& sceneNode, Node* node, const Prop
         case SceneNodeProperty::SCRIPT:
             node->addScript(snp._value.c_str());
             break;
+        case SceneNodeProperty::ENABLED:
+            node->setEnabled(snp._value.compare("true") == 0);
+            break;
         default:
             GP_ERROR("Unsupported node property type (%d).", snp._type);
             break;
@@ -852,6 +856,10 @@ void SceneLoader::parseNode(Properties* ns, SceneNode* parent, const std::string
         {
             addSceneNodeProperty(sceneNode, SceneNodeProperty::SCRIPT, ns->getString());
         }
+        else if (strcmp(name, "enabled") == 0)
+        {
+            addSceneNodeProperty(sceneNode, SceneNodeProperty::ENABLED, ns->getString());
+        }
         else
         {
             GP_ERROR("Unsupported node property: %s = %s", name, ns->getString());

+ 2 - 1
gameplay/src/SceneLoader.h

@@ -61,7 +61,8 @@ private:
             SCRIPT = 2048,
             SPRITE = 4096,
             TILESET = 8192,
-            TEXT = 16384
+            TEXT = 16384,
+            ENABLED = 32768
         };
 
         SceneNodeProperty(Type type, const std::string& value, int index, bool isUrl);

+ 2 - 2
gameplay/src/ScriptController.cpp

@@ -2,7 +2,7 @@
 #include "FileSystem.h"
 #include "ScriptController.h"
 
-#ifndef NO_LUA_BINDINGS
+#ifndef GP_NO_LUA_BINDINGS
 #include "lua/lua_all_bindings.h"
 #endif
 
@@ -716,7 +716,7 @@ void ScriptController::initialize()
         GP_ERROR("Failed to initialize Lua scripting engine.");
     luaL_openlibs(_lua);
 
-#ifndef NO_LUA_BINDINGS
+#ifndef GP_NO_LUA_BINDINGS
     lua_RegisterAllBindings();
     ScriptUtil::registerFunction("convert", ScriptController::convert);
 #endif

+ 1 - 0
gameplay/src/Terrain.cpp

@@ -262,6 +262,7 @@ Terrain* Terrain::create(HeightField* heightfield, const Vector3& scale,
     if (normalMapPath)
     {
         terrain->_normalMap = Texture::Sampler::create(normalMapPath, true);
+        terrain->_normalMap->setWrapMode(Texture::CLAMP, Texture::CLAMP);
         GP_ASSERT( terrain->_normalMap->getTexture()->getType() == Texture::TEXTURE_2D );
     }
 

+ 11 - 3
gameplay/src/TerrainPatch.cpp

@@ -48,7 +48,12 @@ TerrainPatch::~TerrainPatch()
     {
         deleteLayer(*_layers.begin());
     }
-    SAFE_RELEASE(_camera);
+    
+    if (_camera != NULL)
+    {
+    	_camera->removeListener(this);
+    	SAFE_RELEASE(_camera);
+    }
 }
 
 TerrainPatch* TerrainPatch::create(Terrain* terrain, unsigned int index,
@@ -197,8 +202,8 @@ void TerrainPatch::addLOD(float* heights, unsigned int width, unsigned int heigh
             v += 3;
 
             // Compute texture coord
-            v[0] = (float)x / width;
-            v[1] = 1.0f - (float)z / height;
+            v[0] = (float)x / (width-1);
+            v[1] = 1.0f - (float)z / (height-1);
             if (xskirt)
             {
                 float offset = verticalSkirtSize / width;
@@ -414,6 +419,9 @@ int TerrainPatch::addSampler(const char* path)
     // Add a new sampler to the list
     Texture::Sampler* sampler = Texture::Sampler::create(texture);
     texture->release();
+
+    // This may need to be clamp in some cases to prevent edge bleeding?  Possibly a
+    // configuration variable in the future.
     sampler->setWrapMode(Texture::REPEAT, Texture::REPEAT);
     sampler->setFilterMode(Texture::LINEAR_MIPMAP_LINEAR, Texture::LINEAR);
     if (firstAvailableIndex != -1)

+ 17 - 0
gameplay/src/TextBox.cpp

@@ -257,6 +257,7 @@ bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
             {
                 case Keyboard::KEY_RETURN:
                     // TODO: Support multi-line
+                    notifyListeners(Control::Listener::ACTIVATED);
                     break;
                 case Keyboard::KEY_ESCAPE:
                     break;
@@ -394,6 +395,16 @@ unsigned int TextBox::drawText(Form* form, const Rectangle& clip)
     return 0;
 }
 
+void TextBox::setText(char const *text)
+{
+    Label::setText(text);
+    if (_caretLocation > _text.length())
+    {
+        _caretLocation = _text.length();
+    }
+    notifyListeners(Control::Listener::TEXT_CHANGED);
+}
+
 void TextBox::setCaretLocation(int x, int y)
 {
     Control::State state = getState();
@@ -450,7 +461,13 @@ void TextBox::setCaretLocation(int x, int y)
     }
 
     if (index != -1)
+    {
         _caretLocation = index;
+    }
+    else
+    {
+        _caretLocation = _text.length();
+    }
 }
 
 void TextBox::getCaretLocation(Vector2* p)

+ 5 - 0
gameplay/src/TextBox.h

@@ -112,6 +112,11 @@ public:
 
     virtual void addListener(Control::Listener* listener, int eventFlags);
 
+    /**
+     * Update the text being edited.
+     */
+    void setText(char const *text) override;
+
 protected:
 
     /**

+ 110 - 37
gameplay/src/Texture.cpp

@@ -161,12 +161,86 @@ Texture* Texture::create(Image* image, bool generateMipmaps)
     }
 }
 
+GLint Texture::getFormatInternal(Format format)
+{
+    switch (format)
+    {
+        case Texture::RGB888:
+        case Texture::RGB565:
+            return GL_RGB;
+        case Texture::RGBA8888:
+        case Texture::RGBA4444:
+        case Texture::RGBA5551:
+            return GL_RGBA;
+        case Texture::ALPHA:
+            return GL_ALPHA;
+        case Texture::DEPTH:
+#if !defined(OPENGL_ES) || defined(GL_ES_VERSION_3_0)
+            return GL_DEPTH_COMPONENT32F;
+#else
+            return GL_DEPTH_COMPONENT;
+#endif
+        default:
+            return 0;
+    }
+}
+
+GLenum Texture::getFormatTexel(Format format)
+{
+    switch (format)
+    {
+        case Texture::RGB888:
+        case Texture::RGBA8888:
+        case Texture::ALPHA:
+            return GL_UNSIGNED_BYTE;
+        case Texture::RGB565:
+            return GL_UNSIGNED_SHORT_5_6_5;
+        case Texture::RGBA4444:
+            return GL_UNSIGNED_SHORT_4_4_4_4;
+        case Texture::RGBA5551:
+            return GL_UNSIGNED_SHORT_5_5_5_1;
+        case Texture::DEPTH:
+#if !defined(OPENGL_ES) || defined(GL_ES_VERSION_3_0)
+            return GL_FLOAT;
+#else
+            return GL_UNSIGNED_INT;
+#endif
+        default:
+            return 0;
+    }
+}
+
+size_t Texture::getFormatBPP(Format format)
+{
+        switch (format)
+        {
+            case Texture::RGB565:
+            case Texture::RGBA4444:
+            case Texture::RGBA5551:
+                return 2;
+            case Texture::RGB888:
+                return 3;
+            case Texture::RGBA8888:
+                return 4;
+            case Texture::ALPHA:
+                return 1;
+            default:
+                return 0;
+        }
+}
+
 Texture* Texture::create(Format format, unsigned int width, unsigned int height, const unsigned char* data, bool generateMipmaps, Texture::Type type)
 {
     GP_ASSERT( type == Texture::TEXTURE_2D || type == Texture::TEXTURE_CUBE );
 
     GLenum target = (GLenum)type;
 
+    GLint internalFormat = getFormatInternal(format);
+    GP_ASSERT( internalFormat != 0 );
+
+    GLenum texelType = getFormatTexel(format);
+    GP_ASSERT( texelType != 0 );
+
     // Create the texture.
     GLuint textureId;
     GL_ASSERT( glGenTextures(1, &textureId) );
@@ -180,45 +254,49 @@ Texture* Texture::create(Format format, unsigned int width, unsigned int height,
 #endif
 
     // Load the texture
+    size_t bpp = getFormatBPP(format);
     if (type == Texture::TEXTURE_2D)
     {
-        // Texture 2D
-        GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, (GLenum)format, width, height, 0, (GLenum)format, GL_UNSIGNED_BYTE, data) );
+        GLenum f = (format == Texture::DEPTH) ? GL_DEPTH_COMPONENT : internalFormat;
+        GL_ASSERT( glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, f, texelType, data) );
     }
     else
     {
         // Get texture size
         unsigned int textureSize = width * height;
-        switch (format)
+        if (bpp == 0)
         {
-            case Texture::RGB:
-                textureSize *= 3;
-                break;
-            case Texture::RGBA:
-                textureSize *= 4;
-                break;
-            case Texture::ALPHA:
-                break;
-            case Texture::UNKNOWN:
-                if (data)
-                {
-                    glDeleteTextures(1, &textureId);
-                    GP_ERROR("Failed to determine texture size because format is UNKNOWN.");
-                    return NULL;
-                }
-                break;
+            glDeleteTextures(1, &textureId);
+            GP_ERROR("Failed to determine texture size because format is UNKNOWN.");
+            return NULL;
         }
+        textureSize *= bpp;
         // Texture Cube
         for (unsigned int i = 0; i < 6; i++)
         {
             const unsigned char* texturePtr = (data == NULL) ? NULL : &data[i * textureSize];
-            GL_ASSERT( glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, (GLenum)format, width, height, 0, (GLenum)format, GL_UNSIGNED_BYTE, texturePtr) );
+            GL_ASSERT( glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalFormat, width, height, 0, internalFormat, texelType, texturePtr) );
         }
     }
 
     // Set initial minification filter based on whether or not mipmaping was enabled.
-    Filter minFilter = generateMipmaps ? NEAREST_MIPMAP_LINEAR : LINEAR;
-    GL_ASSERT( glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter) );
+    Filter minFilter;
+    if (format == Texture::DEPTH)
+    {
+    	minFilter = NEAREST;
+    	GL_ASSERT( glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST) );
+    	GL_ASSERT( glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST) );
+    	GL_ASSERT( glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) );
+    	GL_ASSERT( glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) );
+#if !defined(OPENGL_ES) || defined(GL_ES_VERSION_3_0) && GL_ES_VERSION_3_0
+    	GL_ASSERT( glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, GL_NONE) );
+#endif    	
+    }
+    else
+    {
+    	minFilter = generateMipmaps ? NEAREST_MIPMAP_LINEAR : LINEAR;
+    	GL_ASSERT( glTexParameteri(target, GL_TEXTURE_MIN_FILTER, minFilter) );
+    }
 
     Texture* texture = new Texture();
     texture->_handle = textureId;
@@ -227,10 +305,12 @@ Texture* Texture::create(Format format, unsigned int width, unsigned int height,
     texture->_width = width;
     texture->_height = height;
     texture->_minFilter = minFilter;
+    texture->_internalFormat = internalFormat;
+    texture->_texelType = texelType;
+    texture->_bpp = bpp;
     if (generateMipmaps)
-    {
         texture->generateMipmaps();
-    }
+
 
     // Restore the texture id
     GL_ASSERT( glBindTexture((GLenum)__currentTextureType, __currentTextureId) );
@@ -264,6 +344,9 @@ Texture* Texture::create(TextureHandle handle, int width, int height, Format for
     texture->_format = format;
     texture->_width = width;
     texture->_height = height;
+    texture->_internalFormat = getFormatInternal(format);
+    texture->_texelType = getFormatTexel(format);
+    texture->_bpp = getFormatBPP(format);
 
     return texture;
 }
@@ -279,27 +362,17 @@ void Texture::setData(const unsigned char* data)
 
     if (_type == Texture::TEXTURE_2D)
     {
-        GL_ASSERT( glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _width, _height, (GLenum)_format, GL_UNSIGNED_BYTE, data) );
+        GL_ASSERT( glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _width, _height, _internalFormat, _texelType, data) );
     }
     else
     {
         // Get texture size
         unsigned int textureSize = _width * _height;
-        switch (_format)
-        {
-            case Texture::RGB:
-                textureSize *= 3;
-                break;
-            case Texture::RGBA:
-                textureSize *= 4;
-                break;
-            case Texture::ALPHA:
-                break;
-        }
+        textureSize *= _bpp;
         // Texture Cube
         for (unsigned int i = 0; i < 6; i++)
         {
-            GL_ASSERT( glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, _width, _height, (GLenum)_format, GL_UNSIGNED_BYTE, &data[i * textureSize]) );
+            GL_ASSERT( glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, _width, _height, _internalFormat, _texelType, &data[i * textureSize]) );
         }
     }
 

+ 16 - 3
gameplay/src/Texture.h

@@ -24,9 +24,15 @@ public:
     enum Format
     {
         UNKNOWN = 0,
-        RGB     = GL_RGB,
-        RGBA    = GL_RGBA,
-        ALPHA   = GL_ALPHA
+        RGB,
+        RGB888 = RGB,
+        RGB565,
+        RGBA,
+        RGBA8888 = RGBA,
+        RGBA4444,
+        RGBA5551,
+        ALPHA,
+        DEPTH,
     };
 
     /**
@@ -325,6 +331,9 @@ private:
     static GLubyte* readCompressedPVRTCLegacy(const char* path, Stream* stream, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount, unsigned int* faceCount, GLenum faces[6]);
 
     static int getMaskByteIndex(unsigned int mask);
+    static GLint getFormatInternal(Format format);
+    static GLenum getFormatTexel(Format format);
+    static size_t getFormatBPP(Format format);
 
     std::string _path;
     TextureHandle _handle;
@@ -340,6 +349,10 @@ private:
     Wrap _wrapR;
     Filter _minFilter;
     Filter _magFilter;
+
+    GLint _internalFormat;
+    GLenum _texelType;
+    size_t _bpp;
 };
 
 }

+ 1 - 0
gameplay/src/Theme.cpp

@@ -73,6 +73,7 @@ Theme* Theme::getDefault()
         if (!__defaultTheme)
         {
             // Create an empty theme so that UI's with no theme don't just crash
+            GP_WARN("Creating default (empty) UI Theme.");
             __defaultTheme = new Theme();
             unsigned int color = 0x00000000;
             __defaultTheme->_texture = Texture::create(Texture::RGBA, 1, 1, (unsigned char*)&color, false);

+ 7 - 23
gameplay/src/Transform.cpp

@@ -99,43 +99,27 @@ const char* Transform::getTypeName() const
 
 const Matrix& Transform::getMatrix() const
 {
-    if (_matrixDirtyBits)
+    if (_matrixDirtyBits & (DIRTY_TRANSLATION | DIRTY_ROTATION | DIRTY_SCALE))
     {
         if (!isStatic())
         {
-            bool hasTranslation = !_translation.isZero();
             bool hasScale = !_scale.isOne();
             bool hasRotation = !_rotation.isIdentity();
 
             // Compose the matrix in TRS order since we use column-major matrices with column vectors and
             // multiply M*v (as opposed to XNA and DirectX that use row-major matrices with row vectors and multiply v*M).
-            if (hasTranslation || (_matrixDirtyBits & DIRTY_TRANSLATION) == DIRTY_TRANSLATION)
+            Matrix::createTranslation(_translation, &_matrix);
+            if (hasRotation)
             {
-                Matrix::createTranslation(_translation, &_matrix);
-                if (hasRotation || (_matrixDirtyBits & DIRTY_ROTATION) == DIRTY_ROTATION)
-                {
-                    _matrix.rotate(_rotation);
-                }
-                if (hasScale || (_matrixDirtyBits & DIRTY_SCALE) == DIRTY_SCALE)
-                {
-                    _matrix.scale(_scale);
-                }
+                _matrix.rotate(_rotation);
             }
-            else if (hasRotation || (_matrixDirtyBits & DIRTY_ROTATION) == DIRTY_ROTATION)
+            if (hasScale)
             {
-                Matrix::createRotation(_rotation, &_matrix);
-                if (hasScale || (_matrixDirtyBits & DIRTY_SCALE) == DIRTY_SCALE)
-                {
-                    _matrix.scale(_scale);
-                }
-            }
-            else if (hasScale || (_matrixDirtyBits & DIRTY_SCALE) == DIRTY_SCALE)
-            {
-                Matrix::createScale(_scale, &_matrix);
+                _matrix.scale(_scale);
             }
         }
 
-        _matrixDirtyBits &= ~DIRTY_TRANSLATION & ~DIRTY_ROTATION & ~DIRTY_SCALE;
+        _matrixDirtyBits &= ~(DIRTY_TRANSLATION | DIRTY_ROTATION | DIRTY_SCALE);
     }
 
     return _matrix;

+ 0 - 1
gameplay/src/Vector3.h

@@ -5,7 +5,6 @@ namespace gameplay
 {
 
 class Matrix;
-class Quaternion;
 
 /**
  * Defines a 3-element floating point vector.

+ 2 - 0
gameplay/src/gameplay-main-android.cpp

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef __ANDROID__
 
 #include <android_native_app_glue.h>
@@ -29,3 +30,4 @@ void android_main(struct android_app* state)
 }
 
 #endif
+#endif

+ 2 - 0
gameplay/src/gameplay-main-ios.mm

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef __APPLE__
 
 #import <Foundation/Foundation.h>
@@ -26,3 +27,4 @@ int main(int argc, char** argv)
 }
 
 #endif
+#endif

+ 2 - 0
gameplay/src/gameplay-main-linux.cpp

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef __linux__
 
 #include "gameplay.h"
@@ -23,3 +24,4 @@ int main(int argc, char** argv)
 }
 
 #endif
+#endif

+ 2 - 0
gameplay/src/gameplay-main-macosx.mm

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef __APPLE__
 
 #import <Foundation/Foundation.h>
@@ -25,4 +26,5 @@ int main(int argc, char** argv)
     return result;
 }
 
+#endif
 #endif

+ 2 - 0
gameplay/src/gameplay-main-windows.cpp

@@ -1,3 +1,4 @@
+#ifndef GP_NO_PLATFORM
 #ifdef WIN32
 
 #include "gameplay.h"
@@ -23,3 +24,4 @@ extern "C" int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LP
 }
 
 #endif
+#endif

+ 40 - 29
gameplay/src/org/gameplay3d/GamePlayNativeActivity.java

@@ -25,13 +25,34 @@ import android.view.OrientationEventListener;
  * that are not offered directly the gameplay3d framework such as platform events, access to 
  * android user-interface, life-cycle events for saving game state and custom plug-ins/extensions.
  */
-public class GamePlayNativeActivity extends NativeActivity
-    implements InputManager.InputDeviceListener {
+public class GamePlayNativeActivity extends NativeActivity {
     
     static {
         System.loadLibrary("gameplay");
     }
-    
+
+    private class GamePlayInputDeviceListener
+        implements InputManager.InputDeviceListener {
+        @Override
+        public void onInputDeviceAdded(int deviceId) {
+            getGamepadDevice(deviceId);
+        }
+
+        @Override
+        public void onInputDeviceRemoved(int deviceId) {
+            InputDevice device = _gamepadDevices.get(deviceId);
+            if (device != null) {
+                _gamepadDevices.remove(deviceId);
+                Log.v(TAG, "Gamepad disconnected:id=" + deviceId);
+                gamepadEventDisconnectedImpl(deviceId);
+            }
+        }
+
+        @Override
+        public void onInputDeviceChanged(int deviceId) {
+        }
+    }
+
     private static final String TAG = "GamePlayNativeActivity";
     
     @Override
@@ -39,7 +60,11 @@ public class GamePlayNativeActivity extends NativeActivity
         Log.i(TAG, "onCreate");
         super.onCreate(savedInstanceState);
         _gamepadDevices = new SparseArray<InputDevice>();
-        _inputManager = (InputManager)getSystemService(Context.INPUT_SERVICE);
+        Log.v(TAG, "Build version: " + Build.VERSION.SDK_INT);
+        if (Build.VERSION.SDK_INT >= 16) {
+            _inputManager = (InputManager)getSystemService(Context.INPUT_SERVICE);
+            _inputDeviceListener = new GamePlayInputDeviceListener();
+        }
 
         if (Build.VERSION.SDK_INT >= 19)
         {
@@ -77,37 +102,22 @@ public class GamePlayNativeActivity extends NativeActivity
     protected void onResume() {
         super.onResume();
         orientationListener.enable();
-        _inputManager.registerInputDeviceListener(this, null);
-        int[] ids = InputDevice.getDeviceIds();
-        for (int i = 0; i < ids.length; i++) {
-            getGamepadDevice(ids[i]);
+        if (_inputManager != null) {
+            _inputManager.registerInputDeviceListener(_inputDeviceListener, null);
+            int[] ids = InputDevice.getDeviceIds();
+            for (int i = 0; i < ids.length; i++) {
+                getGamepadDevice(ids[i]);
+            }
         }
     }
     
     @Override
     protected void onPause() {
         orientationListener.disable();
-        _inputManager.unregisterInputDeviceListener(this);
-        super.onPause();
-    }
-    
-    @Override
-    public void onInputDeviceAdded(int deviceId) {
-        getGamepadDevice(deviceId);
-    }
-
-    @Override
-    public void onInputDeviceRemoved(int deviceId) {
-        InputDevice device = _gamepadDevices.get(deviceId);
-        if (device != null) {
-            _gamepadDevices.remove(deviceId);
-            Log.v(TAG, "Gamepad disconnected:id=" + deviceId);
-            gamepadEventDisconnectedImpl(deviceId);
+        if (_inputManager != null) {
+            _inputManager.unregisterInputDeviceListener(_inputDeviceListener);
         }
-    }
-    
-    @Override
-    public void onInputDeviceChanged(int deviceId) {
+        super.onPause();
     }
     
     private void onGamepadConnected(int deviceId, String deviceName) {
@@ -143,7 +153,8 @@ public class GamePlayNativeActivity extends NativeActivity
     private static native void gamepadEventDisconnectedImpl(int deviceId);
     private static native void screenOrientationChanged(int orientation);
     
-    private InputManager _inputManager;
+    private InputManager _inputManager = null;
     private SparseArray<InputDevice> _gamepadDevices;
     private OrientationEventListener orientationListener;
+    GamePlayInputDeviceListener _inputDeviceListener = null;
 }

+ 1 - 1
install.bat

@@ -7,7 +7,7 @@ REM
 REM Helps prevent repo bloat due to large binary files
 REM
 
-set prefix=https://github.com/gameplay3d/GamePlay/releases/download/v3.0.0
+set prefix=https://github.com/gameplay3d/GamePlay/releases/download/v4.0.0
 
 set filename=gameplay-deps
 

+ 1 - 1
install.sh

@@ -7,7 +7,7 @@
 # Helps prevent repo bloat due to large binary files
 #
 
-prefix=https://github.com/gameplay3d/GamePlay/releases/download/v3.0.0
+prefix=https://github.com/gameplay3d/GamePlay/releases/download/v4.0.0
 
 filename=gameplay-deps
 

+ 190 - 190
samples/browser/res/common/sprites/sprite.scene

@@ -1,193 +1,193 @@
 scene spriteSample
 {
-	// Width and height are expected to be 1280x720
-	node camera
-	{
-		camera
-		{
-			type = ORTHOGRAPHIC
-			nearPlane = 0
-			farPlane = 100
-
-			// zoomX default is game width
-			// zoomY default is game height
-			// aspectRatio default is game width / game height
-		}
-		// width and height are divided in half
-		translate = 640, 360, 0
-	}
-
-	// Background sprite
-	node background
-	{
-		sprite
-		{
-			path = res/common/sprites/background.png
-
-			// game width * 5
-			width = 6400
-			height = 720
-		}
-	}
-
-	// Level floor
-	node floor
-	{
-		tileset
-		{
-			path = res/common/sprites/level.png
-
-			tileWidth = 70
-			tileHeight = 70
-			rows = 3
-			columns = 7
-
-			tile
-			{
-				cell = 0, 0
-				source = 568, 284
-			}
-			tile
-			{
-				cell = 1, 0
-				source = 568, 284
-			}
-			tile
-			{
-				cell = 2, 0
-				source = 568, 284
-			}
-			tile
-			{
-				cell = 3, 0
-				source = 568, 284
-			}
-			tile
-			{
-				cell = 4, 0
-				source = 497, 284
-			}
-
-			tile
-			{
-				cell = 0, 1
-				source = 568, 0
-			}
-			tile
-			{
-				cell = 1, 1
-				source = 568, 0
-			}
-			tile
-			{
-				cell = 2, 1
-				source = 568, 0
-			}
-			tile
-			{
-				cell = 3, 1
-				source = 568, 0
-			}
-			tile
-			{
-				cell = 4, 1
-				source = 710, 142
-			}
-			tile
-			{
-				cell = 5, 1
-				source = 497, 284
-			}
-
-			tile
-			{
-				cell = 0, 2
-				source = 568, 0
-			}
-			tile
-			{
-				cell = 1, 2
-				source = 568, 0
-			}
-			tile
-			{
-				cell = 2, 2
-				source = 568, 0
-			}
-			tile
-			{
-				cell = 3, 2
-				source = 568, 0
-			}
-			tile
-			{
-				cell = 4, 2
-				source = 568, 0
-			}
-			tile
-			{
-				cell = 5, 2
-				source = 710, 142
-			}
-			tile
-			{
-				cell = 6, 2
-				source = 497, 284
-			}
-		}
-	}
-
-	node player
-	{
-		sprite
-		{
-			path = res/common/sprites/player1.png
-
-			width = 72
-			height = 97
-			source = 67, 196, 66, 92
-			frameCount = 13
-		}
-
-		// Position player at lower-left. Y position is floor's tileset height (tileHeight * rows)
-		translate = 0, 210, 0
-	}
-
-	node rocket
-	{
-		sprite
-		{
-			path = res/common/sprites/rocket.png
-
-			width = 128
-			height = 128
-			blendMode = BLEND_ADDITIVE
-			anchor = 0.5, 0.3
-			offset = OFFSET_ANCHOR
-		}
-
-		translate = 1280, 0, 0
-		rotate = 0, 0, 1, -45
-	}
-
-	node water
-	{
-		// Sprite drawable set in code because Effect isn't supported
-		translate = 0, -50, 0
-	}
-
-	node text
-	{
-		text
-		{
-			font = res/ui/arial.gpb
-
-			text = P1
-			size = 18
-			color = 0, 0, 1, 1
-		}
-	}
-
-	// Set active camera
-	activeCamera = camera
+    // Width and height are expected to be 1280x720
+    node camera
+    {
+        camera
+        {
+            type = ORTHOGRAPHIC
+            nearPlane = 0
+            farPlane = 100
+
+            // zoomX default is game width
+            // zoomY default is game height
+            // aspectRatio default is game width / game height
+        }
+        // width and height are divided in half
+        translate = 640, 360, 0
+    }
+
+    // Background sprite
+    node background
+    {
+        sprite
+        {
+            path = res/common/sprites/background.png
+
+            // game width * 5
+            width = 6400
+            height = 720
+        }
+    }
+
+    // Level floor
+    node floor
+    {
+        tileset
+        {
+            path = res/common/sprites/level.png
+
+            tileWidth = 70
+            tileHeight = 70
+            rows = 3
+            columns = 7
+
+            tile
+            {
+                cell = 0, 0
+                source = 568, 284
+            }
+            tile
+            {
+                cell = 1, 0
+                source = 568, 284
+            }
+            tile
+            {
+                cell = 2, 0
+                source = 568, 284
+            }
+            tile
+            {
+                cell = 3, 0
+                source = 568, 284
+            }
+            tile
+            {
+                cell = 4, 0
+                source = 497, 284
+            }
+
+            tile
+            {
+                cell = 0, 1
+                source = 568, 0
+            }
+            tile
+            {
+                cell = 1, 1
+                source = 568, 0
+            }
+            tile
+            {
+                cell = 2, 1
+                source = 568, 0
+            }
+            tile
+            {
+                cell = 3, 1
+                source = 568, 0
+            }
+            tile
+            {
+                cell = 4, 1
+                source = 710, 142
+            }
+            tile
+            {
+                cell = 5, 1
+                source = 497, 284
+            }
+
+            tile
+            {
+                cell = 0, 2
+                source = 568, 0
+            }
+            tile
+            {
+                cell = 1, 2
+                source = 568, 0
+            }
+            tile
+            {
+                cell = 2, 2
+                source = 568, 0
+            }
+            tile
+            {
+                cell = 3, 2
+                source = 568, 0
+            }
+            tile
+            {
+                cell = 4, 2
+                source = 568, 0
+            }
+            tile
+            {
+                cell = 5, 2
+                source = 710, 142
+            }
+            tile
+            {
+                cell = 6, 2
+                source = 497, 284
+            }
+        }
+    }
+
+    node player
+    {
+        node text
+        {
+            text
+            {
+                font = res/ui/arial.gpb
+
+                text = P1
+                size = 18
+                color = 0, 0, 1, 1
+            }
+        }
+
+        sprite
+        {
+            path = res/common/sprites/player1.png
+
+            width = 72
+            height = 97
+            source = 67, 196, 66, 92
+            frameCount = 13
+        }
+
+        // Position player at lower-left. Y position is floor's tileset height (tileHeight * rows)
+        translate = 0, 210, 0
+    }
+
+    node rocket
+    {
+        sprite
+        {
+            path = res/common/sprites/rocket.png
+
+            width = 128
+            height = 128
+            blendMode = BLEND_ADDITIVE
+            anchor = 0.5, 0.3
+            offset = OFFSET_ANCHOR
+        }
+
+        translate = 1280, 0, 0
+        rotate = 0, 0, 1, -45
+    }
+
+    node water
+    {
+        // Sprite drawable set in code because Effect isn't supported
+        translate = 0, -50, 0
+    }
+
+    // Set active camera
+    activeCamera = camera
 }

+ 23 - 21
samples/browser/sample-browser.pro

@@ -3,11 +3,11 @@
 # Project created by QtCreator
 #
 #-------------------------------------------------
-
 QT -= core gui
-
 TARGET = sample-browser
 TEMPLATE = app
+CONFIG += c++11
+CONFIG -= qt
 
 SOURCES += src/Audio3DSample.cpp \
     src/AudioSample.cpp \
@@ -63,16 +63,12 @@ HEADERS += src/Audio3DSample.h \
     src/TriangleSample.h \
     src/WaterSample.h
 
-CONFIG += c++11
-
 INCLUDEPATH += $$PWD/../../gameplay/src
 INCLUDEPATH += $$PWD/../../external-deps/include
-LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
-PRE_TARGETDEPS += $$PWD/../../gameplay/Debug/libgameplay.a
-
-linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
-linux: DEFINES += GP_USE_GAMEPAD
+DEFINES += GP_USE_GAMEPAD
+    
 linux: DEFINES += __linux__
+linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
 linux: INCLUDEPATH += /usr/include/gtk-2.0
 linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/gtk-2.0/include
 linux: INCLUDEPATH += /usr/include/atk-1.0
@@ -86,24 +82,16 @@ linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/glib-2.0/include
 linux: INCLUDEPATH += /usr/include/pixman-1
 linux: INCLUDEPATH += /usr/include/libpng12
 linux: INCLUDEPATH += /usr/include/harfbuzz
+linux: LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
 linux: LIBS += -L$$PWD/../../external-deps/lib/linux/x86_64/ -lgameplay-deps
-linux: LIBS += -lm
-linux: LIBS += -lGL
-linux: LIBS += -lrt
-linux: LIBS += -ldl
-linux: LIBS += -lX11
-linux: LIBS += -lpthread
-linux: LIBS += -lgtk-x11-2.0
-linux: LIBS += -lglib-2.0
-linux: LIBS += -lgobject-2.0
-linux: PRE_TARGETDEPS += $$PWD/../../external-deps/lib/linux/x86_64/libgameplay-deps.a
+linux: LIBS += -lm -lGL -lrt -ldl -lX11 -lpthread -lgtk-x11-2.0 -lglib-2.0 -lgobject-2.0
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/shaders ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/ui ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/../../gameplay/res/logo_powered_white.png ../res$$escape_expand(\n\t))
 
 macx: QMAKE_CXXFLAGS += -x c++ -stdlib=libc++ -w -arch x86_64
 macx: QMAKE_OBJECTIVE_CFLAGS += -x objective-c++ -stdlib=libc++ -w -arch x86_64
-macx: DEFINES += GP_USE_GAMEPAD
+macx: LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
 macx: LIBS += -L$$PWD/../../external-deps/lib/macosx/x86_64/ -lgameplay-deps
 macx: LIBS += -F/System/Library/Frameworks -framework GameKit
 macx: LIBS += -F/System/Library/Frameworks -framework IOKit
@@ -114,7 +102,8 @@ macx: LIBS += -F/System/Library/Frameworks -framework Cocoa
 macx: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/shaders ../res$$escape_expand(\n\t))
 macx: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/ui ../res$$escape_expand(\n\t))
 macx: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/../../gameplay/res/logo_powered_white.png ../res$$escape_expand(\n\t))
-macx
+macx: ICON = icon.png
+macx 
 {
     icon.files = icon.png
     icon.path = Contents/Resources
@@ -129,3 +118,16 @@ macx
     QMAKE_BUNDLE_DATA += res
 }
 
+win32: DEFINES += WIN32 _WINDOWS _UNICODE UNICODE
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/../../gameplay/Debug/debug/ -lgameplay
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/../../gameplay/Release/release/ -lgameplay
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/../../external-deps/lib/windows/x86_64/Debug/ -lgameplay-deps
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/../../external-deps/lib/windows/x86_64/Release/ -lgameplay-deps
+win32: LIBS += -lOpenGL32 -lGLU32 -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32
+win32: LIBS += -L$$(DXSDK_DIR)Lib\x64 -lXInput
+win32: INCLUDEPATH += $$(DXSDK_DIR)Include
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34100
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34189
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\..\..\gameplay\res\shaders res\shaders\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\..\..\gameplay\res\ui res\ui\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(copy ..\..\..\gameplay\res\logo_powered_white.png res$$escape_expand(\n\t))

+ 6 - 6
samples/browser/sample-browser.vcxproj

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="DebugMem|x64">
       <Configuration>DebugMem</Configuration>
@@ -24,20 +24,20 @@
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -70,7 +70,7 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;GP_USE_GAMEPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..\gameplay\src;..\..\external-deps\include;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\include</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\..\gameplay\src;..\..\external-deps\include;$(DXSDK_DIR)include</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>true</RuntimeTypeInfo>
       <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
     </ClCompile>
@@ -78,7 +78,7 @@
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>OpenGL32.lib;GLU32.lib;gameplay.lib;gameplay-deps.lib;XInput.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\..\external-deps\lib\windows\x86_64\Debug;..\..\gameplay\Debug;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\Lib\x64</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\..\external-deps\lib\windows\x86_64\Debug;..\..\gameplay\Debug;$(DXSDK_DIR)Lib\x64</AdditionalLibraryDirectories>
     </Link>
     <CustomBuildStep>
       <Command>

+ 3 - 1
samples/browser/src/SpriteSample.cpp

@@ -53,7 +53,8 @@ void SpriteSample::initialize()
     // Setup player text
     Node* playerTextNode = _scene->findNode("text");
     playerTextNode->addRef();
-    _scene->removeNode(playerTextNode); //XXX This is because SceneLoader doesn't support loading child nodes for other nodes
+    _scene->removeNode(playerTextNode);
+    //XXX This is because SceneLoader doesn't support loading child nodes for other nodes
     _playerNode->addChild(playerTextNode);
 
     playerTextNode->translateY(_playerSprite->getHeight());
@@ -62,6 +63,7 @@ void SpriteSample::initialize()
     playerText->setWidth(_playerSprite->getWidth());
     SAFE_RELEASE(playerTextNode);
 
+
     // Custom Effect in sprite
     Effect* waterEffect = Effect::createFromFile("res/shaders/sprite.vert", "res/common/sprites/water2d.frag");
     Sprite* waterSprite = Sprite::create("res/common/sprites/water2d.png", getWidth() * 5, getHeight() / 3, waterEffect);

+ 48 - 0
samples/character/game.config

@@ -0,0 +1,48 @@
+window
+{
+    title = Character
+    width = 1280
+    height = 720
+    fullscreen = false
+}
+
+aliases
+{
+    backboard = res/dxt/backboard.dds
+    basketball = res/dxt/basketball.dds
+    basketballnet = res/dxt/basketballnet.dds
+    book = res/dxt/book.dds
+    bookshelf = res/dxt/bookshelf.dds
+    character = res/dxt/boy.dds
+    decals = res/dxt/decals.dds
+    door = res/dxt/door.dds
+    doorframe = res/dxt/doorframe.dds
+    easel = res/dxt/easel.dds
+    floor = res/dxt/floor.dds
+    floortiles = res/dxt/floortiles.dds
+    gamepad = res/dxt/gamepad.dds
+    playtable = res/dxt/playtable.dds
+    shadow = res/dxt/shadow.dds
+    storageorganizer = res/dxt/storageorganizer.dds
+    tableleg1 = res/dxt/tableleg1.dds
+    tableleg2 = res/dxt/tableleg2.dds
+    tableleg3 = res/dxt/tableleg3.dds
+    tableleg4 = res/dxt/tableleg4.dds
+    tabletop = res/dxt/tabletop.dds
+    tiles = res/dxt/tiles.dds
+    tilesn = res/dxt/tilesn.dds
+    toybox = res/dxt/toybox.dds
+    walleast = res/dxt/walleast.dds
+    wallnorth = res/dxt/wallnorth.dds
+    walloverhang = res/dxt/walloverhang.dds
+    wallsouth = res/dxt/wallsouth.dds
+    wallwest = res/dxt/wallwest.dds
+    windowledge = res/dxt/windowledge.dds
+    wood = res/dxt/wood.dds
+    woodn = res/dxt/woodn.dds
+}
+
+gamepads
+{
+    form = res/common/gamepad.form
+}

+ 24 - 20
samples/character/sample-character.pro

@@ -3,26 +3,22 @@
 # Project created by QtCreator
 #
 #-------------------------------------------------
-
 QT -= core gui
-
 TARGET = sample-character
 TEMPLATE = app
+CONFIG += c++11
+CONFIG -= qt
 
 SOURCES += src/CharacterGame.cpp
 
 HEADERS += src/CharacterGame.h 
 
-CONFIG += c++11
-
 INCLUDEPATH += $$PWD/../../gameplay/src
 INCLUDEPATH += $$PWD/../../external-deps/include
-LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
-PRE_TARGETDEPS += $$PWD/../../gameplay/Debug/libgameplay.a
+DEFINES += GP_USE_GAMEPAD
 
-linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
-linux: DEFINES += GP_USE_GAMEPAD
 linux: DEFINES += __linux__
+linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
 linux: INCLUDEPATH += /usr/include/gtk-2.0
 linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/gtk-2.0/include
 linux: INCLUDEPATH += /usr/include/atk-1.0
@@ -36,24 +32,17 @@ linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/glib-2.0/include
 linux: INCLUDEPATH += /usr/include/pixman-1
 linux: INCLUDEPATH += /usr/include/libpng12
 linux: INCLUDEPATH += /usr/include/harfbuzz
+linux: LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
 linux: LIBS += -L$$PWD/../../external-deps/lib/linux/x86_64/ -lgameplay-deps
-linux: LIBS += -lm
-linux: LIBS += -lGL
-linux: LIBS += -lrt
-linux: LIBS += -ldl
-linux: LIBS += -lX11
-linux: LIBS += -lpthread
-linux: LIBS += -lgtk-x11-2.0
-linux: LIBS += -lglib-2.0
-linux: LIBS += -lgobject-2.0
-linux: PRE_TARGETDEPS += $$PWD/../../external-deps/lib/linux/x86_64/libgameplay-deps.a
+linux: LIBS += -lm -lGL -lrt -ldl -lX11 -lpthread -lgtk-x11-2.0 -lglib-2.0 -lgobject-2.0
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/shaders ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/ui ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/../../gameplay/res/logo_powered_white.png ../res$$escape_expand(\n\t))
+linux: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/game.dxt.config game.config$$escape_expand(\n\t))
 
 macx: QMAKE_CXXFLAGS += -x c++ -stdlib=libc++ -w -arch x86_64
 macx: QMAKE_OBJECTIVE_CFLAGS += -x objective-c++ -stdlib=libc++ -w -arch x86_64
-macx: DEFINES += GP_USE_GAMEPAD
+macx: LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
 macx: LIBS += -L$$PWD/../../external-deps/lib/macosx/x86_64/ -lgameplay-deps
 macx: LIBS += -F/System/Library/Frameworks -framework GameKit
 macx: LIBS += -F/System/Library/Frameworks -framework IOKit
@@ -61,7 +50,7 @@ macx: LIBS += -F/System/Library/Frameworks -framework QuartzCore
 macx: LIBS += -F/System/Library/Frameworks -framework OpenAL
 macx: LIBS += -F/System/Library/Frameworks -framework OpenGL
 macx: LIBS += -F/System/Library/Frameworks -framework Cocoa
-macx: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/shaders ../res$$escape_expand(\n\t))
+macx: QMAKE_ += $$quote(rsync -rau $$PWD/../../gameplay/res/shaders ../res$$escape_expand(\n\t))
 macx: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/ui ../res$$escape_expand(\n\t))
 macx: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/../../gameplay/res/logo_powered_white.png ../res$$escape_expand(\n\t))
 macx
@@ -78,3 +67,18 @@ macx
     res.path = Contents/Resources
     QMAKE_BUNDLE_DATA += res
 }
+
+win32: DEFINES += WIN32 _WINDOWS _UNICODE UNICODE
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/../../gameplay/Debug/debug/ -lgameplay
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/../../gameplay/Release/release/ -lgameplay
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/../../external-deps/lib/windows/x86_64/Debug/ -lgameplay-deps
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/../../external-deps/lib/windows/x86_64/Release/ -lgameplay-deps
+win32: LIBS += -lOpenGL32 -lGLU32 -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32
+win32: LIBS += -L$$(DXSDK_DIR)Lib\x64 -lXInput
+win32: INCLUDEPATH += $$(DXSDK_DIR)Include
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34100
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34189
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\..\..\gameplay\res\shaders res\shaders\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\..\..\gameplay\res\ui res\ui\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(copy ..\..\..\gameplay\res\logo_powered_white.png res$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(copy /Y ..\game.dxt.config game.config$$escape_expand(\n\t))

+ 6 - 6
samples/character/sample-character.vcxproj

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="DebugMem|x64">
       <Configuration>DebugMem</Configuration>
@@ -24,20 +24,20 @@
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -68,7 +68,7 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;GP_USE_GAMEPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..\gameplay\src;..\..\external-deps\include;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\include;</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\..\gameplay\src;..\..\external-deps\include;$(DXSDK_DIR)include;</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>
       </RuntimeTypeInfo>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
@@ -77,7 +77,7 @@
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>OpenGL32.lib;GLU32.lib;gameplay.lib;gameplay-deps.lib;XInput.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\..\external-deps\lib\windows\x86_64\Debug;..\..\gameplay\Debug;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\Lib\x64</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\..\external-deps\lib\windows\x86_64\Debug;..\..\gameplay\Debug;$(DXSDK_DIR)Lib\x64</AdditionalLibraryDirectories>
     </Link>
     <CustomBuildStep>
       <Command>copy /Y "$(ProjectDir)game.dxt.config" "$(ProjectDir)game.config"</Command>

+ 72 - 0
samples/racer/game.config

@@ -0,0 +1,72 @@
+window
+{
+    title = Racer
+    width = 1280
+    height = 720
+    fullscreen = false
+}
+
+scripts
+{
+    initialize = res/common/racer.lua#initialize
+    update = res/common/racer.lua#update
+    render = res/common/racer.lua#render
+    finalize = res/common/racer.lua#finalize
+    keyEvent = res/common/racer.lua#keyEvent
+    touchEvent = res/common/racer.lua#touchEvent
+}
+
+aliases 
+{
+    car = res/dxt/car.png
+    chain_fence_1 = res/dxt/chain_fence_1.dds 
+    design_11 = res/dxt/design_11.dds 
+    d_tree = res/dxt/d_tree.dds 
+    gamepad = res/dxt/gamepad.dds 
+    grassbunch_1 = res/dxt/grassbunch_1.dds 
+    grassbunch_2 = res/dxt/grassbunch_2.dds 
+    gravel_2 = res/dxt/gravel_2.dds 
+    panel_1 = res/dxt/panel_1.dds 
+    panel_10 = res/dxt/panel_10.dds 
+    panel_11 = res/dxt/panel_11.dds 
+    panel_12 = res/dxt/panel_12.dds 
+    panel_13 = res/dxt/panel_13.dds 
+    panel_14 = res/dxt/panel_14.dds 
+    panel_15 = res/dxt/panel_15.dds 
+    panel_16 = res/dxt/panel_16.dds 
+    panel_2 = res/dxt/panel_2.dds 
+    panel_3 = res/dxt/panel_3.dds 
+    panel_4 = res/dxt/panel_4.dds 
+    panel_5 = res/dxt/panel_5.dds 
+    panel_6 = res/dxt/panel_6.dds 
+    panel_7 = res/dxt/panel_7.dds 
+    panel_8 = res/dxt/panel_8.dds 
+    panel_9 = res/dxt/panel_9.dds 
+    panorama_2 = res/dxt/panorama_2.dds 
+    panorama_3 = res/dxt/panorama_3.dds 
+    rails = res/dxt/rails.dds 
+    shrub_1 = res/dxt/shrub_1.dds 
+    shrub_2 = res/dxt/shrub_2.dds 
+    shrub_3 = res/dxt/shrub_3.dds 
+    textures_8 = res/dxt/textures_8.dds 
+    track_1 = res/dxt/track_1.dds 
+    tree = res/dxt/tree.dds 
+    tree_2 = res/dxt/tree_2.dds 
+    tree_3 = res/dxt/tree_3.dds 
+    tree_4 = res/dxt/tree_4.dds 
+    tree_5 = res/dxt/tree_5.dds 
+    tree_6 = res/dxt/tree_6.dds 
+    tree_7 = res/dxt/tree_7.dds 
+    trunk_1 = res/dxt/trunk_1.dds 
+    trunk_2 = res/dxt/trunk_2.dds 
+} 
+
+gamepad
+{
+    form = res/common/gamepad.form
+}
+
+ui
+{
+    theme = res/ui/default.theme
+}

+ 24 - 21
samples/racer/sample-racer.pro

@@ -3,25 +3,22 @@
 # Project created by QtCreator
 #
 #-------------------------------------------------
-
 QT -= core gui
-
 TARGET = sample-racer
 TEMPLATE = app
+CONFIG += c++11
+CONFIG -= qt
 
 SOURCES += src/RacerGame.cpp
 
 HEADERS += src/RacerGame.h 
 
-CONFIG += c++11
 INCLUDEPATH += $$PWD/../../gameplay/src
 INCLUDEPATH += $$PWD/../../external-deps/include
-LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
-PRE_TARGETDEPS += $$PWD/../../gameplay/Debug/libgameplay.a
+DEFINES += GP_USE_GAMEPAD
 
-linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
-linux: DEFINES += GP_USE_GAMEPAD
 linux: DEFINES += __linux__
+linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
 linux: INCLUDEPATH += /usr/include/gtk-2.0
 linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/gtk-2.0/include
 linux: INCLUDEPATH += /usr/include/atk-1.0
@@ -35,25 +32,17 @@ linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/glib-2.0/include
 linux: INCLUDEPATH += /usr/include/pixman-1
 linux: INCLUDEPATH += /usr/include/libpng12
 linux: INCLUDEPATH += /usr/include/harfbuzz
+linux: LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
 linux: LIBS += -L$$PWD/../../external-deps/lib/linux/x86_64/ -lgameplay-deps
-linux: LIBS += -lm
-linux: LIBS += -lGL
-linux: LIBS += -lrt
-linux: LIBS += -ldl
-linux: LIBS += -lX11
-linux: LIBS += -lpthread
-linux: LIBS += -lgtk-x11-2.0
-linux: LIBS += -lglib-2.0
-linux: LIBS += -lgobject-2.0
-linux: PRE_TARGETDEPS += $$PWD/../../external-deps/lib/linux/x86_64/libgameplay-deps.a
+linux: LIBS += -lm -lGL -lrt -ldl -lX11 -lpthread -lgtk-x11-2.0 -lglib-2.0 -lgobject-2.0
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/shaders ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/ui ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/../../gameplay/res/logo_powered_white.png ../res$$escape_expand(\n\t))
-linux: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/game.dxt.config ../game.config$$escape_expand(\n\t))
+linux: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/game.dxt.config game.config$$escape_expand(\n\t))
 
 macx: QMAKE_CXXFLAGS += -x c++ -stdlib=libc++ -w -arch x86_64
 macx: QMAKE_OBJECTIVE_CFLAGS += -x objective-c++ -stdlib=libc++ -w -arch x86_64
-macx: DEFINES += GP_USE_GAMEPAD
+macx: LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
 macx: LIBS += -L$$PWD/../../external-deps/lib/macosx/x86_64/ -lgameplay-deps
 macx: LIBS += -F/System/Library/Frameworks -framework GameKit
 macx: LIBS += -F/System/Library/Frameworks -framework IOKit
@@ -64,8 +53,7 @@ macx: LIBS += -F/System/Library/Frameworks -framework Cocoa
 macx: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/shaders ../res$$escape_expand(\n\t))
 macx: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/ui ../res$$escape_expand(\n\t))
 macx: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/../../gameplay/res/logo_powered_white.png ../res$$escape_expand(\n\t))
-macx: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/../game.dxt.config ../game.config$$escape_expand(\n\t))
-macx
+macx 
 {
     icon.files = icon.png
     icon.path = Contents/Resources
@@ -79,3 +67,18 @@ macx
     res.path = Contents/Resources
     QMAKE_BUNDLE_DATA += res
 }
+
+win32: DEFINES += WIN32 _WINDOWS _UNICODE UNICODE
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/../../gameplay/Debug/debug/ -lgameplay
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/../../gameplay/Release/release/ -lgameplay
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/../../external-deps/lib/windows/x86_64/Debug/ -lgameplay-deps
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/../../external-deps/lib/windows/x86_64/Release/ -lgameplay-deps
+win32: LIBS += -lOpenGL32 -lGLU32 -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32
+win32: LIBS += -L$$(DXSDK_DIR)Lib\x64 -lXInput
+win32: INCLUDEPATH += $$(DXSDK_DIR)Include
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34100
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34189
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\..\..\gameplay\res\shaders res\shaders\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\..\..\gameplay\res\ui res\ui\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(copy ..\..\..\gameplay\res\logo_powered_white.png res$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(copy /Y ..\game.dxt.config game.config$$escape_expand(\n\t))

+ 6 - 6
samples/racer/sample-racer.vcxproj

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="DebugMem|x64">
       <Configuration>DebugMem</Configuration>
@@ -24,20 +24,20 @@
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -73,7 +73,7 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;GP_USE_GAMEPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..\gameplay\src;..\..\external-deps\include;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\include;</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\..\gameplay\src;..\..\external-deps\include;$(DXSDK_DIR)include;</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>true</RuntimeTypeInfo>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
     </ClCompile>
@@ -81,7 +81,7 @@
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>OpenGL32.lib;GLU32.lib;gameplay.lib;gameplay-deps.lib;XInput.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\..\external-deps\lib\windows\x86_64\Debug;..\..\gameplay\Debug;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\Lib\x64</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\..\external-deps\lib\windows\x86_64\Debug;..\..\gameplay\Debug;$(DXSDK_DIR)Lib\x64</AdditionalLibraryDirectories>
     </Link>
     <CustomBuildStep>
       <Command>copy /Y "$(ProjectDir)game.dxt.config" "$(ProjectDir)game.config"</Command>

+ 21 - 19
samples/spaceship/sample-spaceship.pro

@@ -3,26 +3,22 @@
 # Project created by QtCreator
 #
 #-------------------------------------------------
-
 QT -= core gui
-
 TARGET = sample-spaceship
 TEMPLATE = app
+CONFIG += c++11
+CONFIG -= qt
 
 SOURCES += src/SpaceshipGame.cpp
 
 HEADERS += src/SpaceshipGame.h 
 
-CONFIG += c++11
-
 INCLUDEPATH += $$PWD/../../gameplay/src
 INCLUDEPATH += $$PWD/../../external-deps/include
-LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
-PRE_TARGETDEPS += $$PWD/../../gameplay/Debug/libgameplay.a
+DEFINES += GP_USE_GAMEPAD
 
-linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
-linux: DEFINES += GP_USE_GAMEPAD
 linux: DEFINES += __linux__
+linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
 linux: INCLUDEPATH += /usr/include/gtk-2.0
 linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/gtk-2.0/include
 linux: INCLUDEPATH += /usr/include/atk-1.0
@@ -36,24 +32,16 @@ linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/glib-2.0/include
 linux: INCLUDEPATH += /usr/include/pixman-1
 linux: INCLUDEPATH += /usr/include/libpng12
 linux: INCLUDEPATH += /usr/include/harfbuzz
+linux: LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
 linux: LIBS += -L$$PWD/../../external-deps/lib/linux/x86_64/ -lgameplay-deps
-linux: LIBS += -lm
-linux: LIBS += -lGL
-linux: LIBS += -lrt
-linux: LIBS += -ldl
-linux: LIBS += -lX11
-linux: LIBS += -lpthread
-linux: LIBS += -lgtk-x11-2.0
-linux: LIBS += -lglib-2.0
-linux: LIBS += -lgobject-2.0
-linux: PRE_TARGETDEPS += $$PWD/../../external-deps/lib/linux/x86_64/libgameplay-deps.a
+linux: LIBS += -lm -lGL -lrt -ldl -lX11 -lpthread -lgtk-x11-2.0 -lglib-2.0 -lgobject-2.0
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/shaders ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/../../gameplay/res/ui ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/../../gameplay/res/logo_powered_white.png ../res$$escape_expand(\n\t))
 
 macx: QMAKE_CXXFLAGS += -x c++ -stdlib=libc++ -w -arch x86_64
 macx: QMAKE_OBJECTIVE_CFLAGS += -x objective-c++ -stdlib=libc++ -w -arch x86_64
-macx: DEFINES += GP_USE_GAMEPAD
+macx: LIBS += -L$$PWD/../../gameplay/Debug/ -lgameplay
 macx: LIBS += -L$$PWD/../../external-deps/lib/macosx/x86_64/ -lgameplay-deps
 macx: LIBS += -F/System/Library/Frameworks -framework GameKit
 macx: LIBS += -F/System/Library/Frameworks -framework IOKit
@@ -78,3 +66,17 @@ macx
     res.path = Contents/Resources
     QMAKE_BUNDLE_DATA += res
 }
+
+win32: DEFINES += WIN32 _WINDOWS _UNICODE UNICODE
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/../../gameplay/Debug/debug/ -lgameplay
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/../../gameplay/Release/release/ -lgameplay
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/../../external-deps/lib/windows/x86_64/Debug/ -lgameplay-deps
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/../../external-deps/lib/windows/x86_64/Release/ -lgameplay-deps
+win32: LIBS += -lOpenGL32 -lGLU32 -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32
+win32: LIBS += -L$$(DXSDK_DIR)Lib\x64 -lXInput
+win32: INCLUDEPATH += $$(DXSDK_DIR)Include
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34100
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34189
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\..\..\gameplay\res\shaders res\shaders\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\..\..\gameplay\res\ui res\ui\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(copy ..\..\..\gameplay\res\logo_powered_white.png res$$escape_expand(\n\t))

+ 6 - 6
samples/spaceship/sample-spaceship.vcxproj

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="DebugMem|x64">
       <Configuration>DebugMem</Configuration>
@@ -24,20 +24,20 @@
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugMem|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -65,14 +65,14 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;GP_USE_GAMEPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>..\..\gameplay\src;..\..\external-deps\include;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\include;</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>..\..\gameplay\src;..\..\external-deps\include;$(DXSDK_DIR)include;</AdditionalIncludeDirectories>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>OpenGL32.lib;GLU32.lib;gameplay.lib;gameplay-deps.lib;XInput.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>..\..\external-deps\lib\windows\x86_64\Debug;..\..\gameplay\Debug;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\Lib\x64</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\..\external-deps\lib\windows\x86_64\Debug;..\..\gameplay\Debug;$(DXSDK_DIR)Lib\x64</AdditionalLibraryDirectories>
     </Link>
     <CustomBuildStep>
       <Command>

+ 22 - 20
template/TEMPLATE_PROJECT.pro

@@ -3,26 +3,22 @@
 # Project created by QtCreator
 #
 #-------------------------------------------------
-
 QT -= core gui
-
 TARGET = TEMPLATE_PROJECT
 TEMPLATE = app
+CONFIG += c++11
+CONFIG -= qt
 
 SOURCES += src/TemplateGame.cpp
 
 HEADERS += src/TemplateGame.h
 
-CONFIG += c++11
-
 INCLUDEPATH += GAMEPLAY_PATH/gameplay/src
 INCLUDEPATH += GAMEPLAY_PATH/external-deps/include
-LIBS += -L$$PWD/GAMEPLAY_PATH/gameplay/Debug/ -lgameplay
-PRE_TARGETDEPS += $$PWD/GAMEPLAY_PATH/gameplay/Debug/libgameplay.a
+DEFINES += GP_USE_GAMEPAD
 
-linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
-linux: DEFINES += GP_USE_GAMEPAD
 linux: DEFINES += __linux__
+linux: QMAKE_CXXFLAGS += -lstdc++ -pthread -w
 linux: INCLUDEPATH += /usr/include/gtk-2.0
 linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/gtk-2.0/include
 linux: INCLUDEPATH += /usr/include/atk-1.0
@@ -36,24 +32,16 @@ linux: INCLUDEPATH += /usr/lib/x86_64-linux-gnu/glib-2.0/include
 linux: INCLUDEPATH += /usr/include/pixman-1
 linux: INCLUDEPATH += /usr/include/libpng12
 linux: INCLUDEPATH += /usr/include/harfbuzz
+linux: LIBS += -L$$PWD/GAMEPLAY_PATH/gameplay/Debug/ -lgameplay
 linux: LIBS += -L$$PWD/GAMEPLAY_PATH/external-deps/lib/linux/x86_64/ -lgameplay-deps
-linux: LIBS += -lm
-linux: LIBS += -lGL
-linux: LIBS += -lrt
-linux: LIBS += -ldl
-linux: LIBS += -lX11
-linux: LIBS += -lpthread
-linux: LIBS += -lgtk-x11-2.0
-linux: LIBS += -lglib-2.0
-linux: LIBS += -lgobject-2.0
-linux: PRE_TARGETDEPS += $$PWD/GAMEPLAY_PATH/external-deps/lib/linux/x86_64/libgameplay-deps.a
+linux: LIBS += -lm -lGL -lrt -ldl -lX11 -lpthread -lgtk-x11-2.0 -lglib-2.0 -lgobject-2.0
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/GAMEPLAY_PATH/gameplay/res/shaders ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/GAMEPLAY_PATH/gameplay/res/ui ../res$$escape_expand(\n\t))
 linux: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/GAMEPLAY_PATH/gameplay/res/logo_powered_white.png ../res$$escape_expand(\n\t))
 
 macx: QMAKE_CXXFLAGS += -x c++ -stdlib=libc++ -w -arch x86_64
 macx: QMAKE_OBJECTIVE_CFLAGS += -x objective-c++ -stdlib=libc++ -w -arch x86_64
-macx: DEFINES += GP_USE_GAMEPAD
+macx: LIBS += -L$$PWD/GAMEPLAY_PATH/gameplay/Debug/ -lgameplay
 macx: LIBS += -L$$PWD/GAMEPLAY_PATH/external-deps/lib/macosx/x86_64/ -lgameplay-deps
 macx: LIBS += -F/System/Library/Frameworks -framework GameKit
 macx: LIBS += -F/System/Library/Frameworks -framework IOKit
@@ -64,7 +52,7 @@ macx: LIBS += -F/System/Library/Frameworks -framework Cocoa
 macx: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/GAMEPLAY_PATH/gameplay/res/shaders ../res$$escape_expand(\n\t))
 macx: QMAKE_POST_LINK += $$quote(rsync -rau $$PWD/GAMEPLAY_PATH/gameplay/res/ui ../res$$escape_expand(\n\t))
 macx: QMAKE_POST_LINK += $$quote(cp -rf $$PWD/GAMEPLAY_PATH/gameplay/res/logo_powered_white.png ../res$$escape_expand(\n\t))
-macx
+macx 
 {
     icon.files = icon.png
     icon.path = Contents/Resources
@@ -78,3 +66,17 @@ macx
     res.path = Contents/Resources
     QMAKE_BUNDLE_DATA += res
 }
+
+win32: DEFINES += WIN32 _WINDOWS _UNICODE UNICODE
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/GAMEPLAY_PATH/gameplay/Debug/debug/ -lgameplay
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/GAMEPLAY_PATH/gameplay/Release/release/ -lgameplay
+win32: CONFIG(debug, debug|release): LIBS += -L$$PWD/GAMEPLAY_PATH/external-deps/lib/windows/x86_64/Debug/ -lgameplay-deps
+win32: CONFIG(release, debug|release): LIBS += -L$$PWD/GAMEPLAY_PATH/external-deps/lib/windows/x86_64/Release/ -lgameplay-deps
+win32: LIBS += -lOpenGL32 -lGLU32 -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lodbc32 -lodbccp32
+win32: LIBS += -L$$(DXSDK_DIR)Lib\x64 -lXInput
+win32: INCLUDEPATH += $$(DXSDK_DIR)Include
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34100
+win32: QMAKE_CXXFLAGS_WARN_ON -= -w34189
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\GAMEPLAY_PATH\gameplay\res\shaders res\shaders\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(xcopy ..\GAMEPLAY_PATH\gameplay\res\ui res\ui\* /s /y /d$$escape_expand(\n\t))
+win32: QMAKE_POST_LINK += $$quote(copy ..\GAMEPLAY_PATH\gameplay\res\logo_powered_white.png res$$escape_expand(\n\t))

+ 2 - 2
template/template.vcxproj

@@ -70,7 +70,7 @@
       <WarningLevel>Level3</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;GP_USE_GAMEPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>GAMEPLAY_PATH\gameplay\src;GAMEPLAY_PATH\external-deps\include;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\include;</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>GAMEPLAY_PATH\gameplay\src;GAMEPLAY_PATH\external-deps\include;$(DXSDK_DIR)include;</AdditionalIncludeDirectories>
       <RuntimeTypeInfo>true</RuntimeTypeInfo>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
     </ClCompile>
@@ -78,7 +78,7 @@
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalDependencies>OpenGL32.lib;GLU32.lib;gameplay.lib;gameplay-deps.lib;XInput.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>GAMEPLAY_PATH\external-deps\lib\windows\x86_64\Debug;GAMEPLAY_PATH\gameplay\Debug;$(MSBuildProgramFiles32)\Microsoft DirectX SDK (June 2010)\Lib\x64</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>GAMEPLAY_PATH\external-deps\lib\windows\x86_64\Debug;GAMEPLAY_PATH\gameplay\Debug;$(DXSDK_DIR)Lib\x64</AdditionalLibraryDirectories>
     </Link>
     <CustomBuildStep>
       <Command>

+ 5 - 1
tools/encoder/CMakeLists.txt

@@ -27,7 +27,7 @@ set(APP_LIBRARIES
     pthread
 )
 
-add_definitions(-lstdc++ -ldl -lfbxsdk -lgameplay-deps -lfreetype -lpthread)
+add_definitions(-std=c++11 -lstdc++ -ldl -lfbxsdk -lgameplay-deps -lfreetype -lpthread)
 
 set( APP_NAME gameplay-encoder )
 
@@ -116,6 +116,10 @@ set(APP_SRC
     src/Transform.h
     src/TTFFontEncoder.cpp
     src/TTFFontEncoder.h
+    src/TMXSceneEncoder.cpp
+    src/TMXSceneEncoder.h
+    src/TMXTypes.cpp
+    src/TMXTypes.h
     src/Vector2.cpp
     src/Vector2.h
     src/Vector2.inl

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

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|x64">
       <Configuration>Debug</Configuration>
@@ -50,6 +50,8 @@
     <ClCompile Include="src\Sampler.cpp" />
     <ClCompile Include="src\Scene.cpp" />
     <ClCompile Include="src\StringUtil.cpp" />
+    <ClCompile Include="src\TMXSceneEncoder.cpp" />
+    <ClCompile Include="src\TMXTypes.cpp" />
     <ClCompile Include="src\Transform.cpp" />
     <ClCompile Include="src\TTFFontEncoder.cpp" />
     <ClCompile Include="src\Vector2.cpp" />
@@ -98,6 +100,8 @@
     <ClInclude Include="src\Scene.h" />
     <ClInclude Include="src\StringUtil.h" />
     <ClInclude Include="src\Thread.h" />
+    <ClInclude Include="src\TMXSceneEncoder.h" />
+    <ClInclude Include="src\TMXTypes.h" />
     <ClInclude Include="src\Transform.h" />
     <ClInclude Include="src\TTFFontEncoder.h" />
     <ClInclude Include="src\Vector2.h" />
@@ -124,14 +128,14 @@
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -160,14 +164,14 @@
       <WarningLevel>Level4</WarningLevel>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>USE_FBX;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>C:/Program Files/Autodesk/FBX/FBX SDK/2015.1/include;../../external-deps/include</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>C:/Program Files/Autodesk/FBX/FBX SDK/2016.1.2/include;../../external-deps/include</AdditionalIncludeDirectories>
       <DisableLanguageExtensions>
       </DisableLanguageExtensions>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalLibraryDirectories>C:/Program Files/Autodesk/FBX/FBX SDK/2015.1/lib/vs2013/x64/debug;../../external-deps/lib/windows/x86_64/Debug</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>C:/Program Files/Autodesk/FBX/FBX SDK/2016.1.2/lib/vs2015/x64/debug;../../external-deps/lib/windows/x86_64/Debug</AdditionalLibraryDirectories>
       <AdditionalDependencies>libfbxsdk-md.lib;freetype.lib;gameplay-deps.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>MSVCRT</IgnoreSpecificDefaultLibraries>
     </Link>
@@ -185,7 +189,7 @@
       <FunctionLevelLinking>true</FunctionLevelLinking>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <PreprocessorDefinitions>USE_FBX;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>C:/Program Files/Autodesk/FBX/FBX SDK/2015.1/include;../../external-deps/include</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>C:/Program Files/Autodesk/FBX/FBX SDK/2016.1.2/include;../../external-deps/include</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
@@ -193,7 +197,7 @@
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
       <AdditionalDependencies>libfbxsdk-md.lib;freetype.lib;gameplay-deps.lib;%(AdditionalDependencies)</AdditionalDependencies>
-      <AdditionalLibraryDirectories>C:/Program Files/Autodesk/FBX/FBX SDK/2015.1/lib/vs2013/x64/release;../../external-deps/lib/windows/x86_64/Release</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>C:/Program Files/Autodesk/FBX/FBX SDK/2016.1.2/lib/vs2015/x64/release;../../external-deps/lib/windows/x86_64/Release</AdditionalLibraryDirectories>
       <IgnoreSpecificDefaultLibraries>
       </IgnoreSpecificDefaultLibraries>
     </Link>
@@ -205,4 +209,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>

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

@@ -139,6 +139,12 @@
     <ClCompile Include="src\edtaa3func.c">
       <Filter>src</Filter>
     </ClCompile>
+    <ClCompile Include="src\TMXSceneEncoder.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
+    <ClCompile Include="src\TMXTypes.cpp">
+      <Filter>src</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\VertexElement.h">
@@ -279,6 +285,12 @@
     <ClInclude Include="src\edtaa3func.h">
       <Filter>src</Filter>
     </ClInclude>
+    <ClInclude Include="src\TMXSceneEncoder.h">
+      <Filter>src</Filter>
+    </ClInclude>
+    <ClInclude Include="src\TMXTypes.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\Vector2.inl">

+ 0 - 15
tools/encoder/gameplay-encoder.vcxproj.user

@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <LocalDebuggerCommandArguments />
-    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
-    <LocalDebuggerEnvironment />
-    <LocalDebuggerWorkingDirectory>.\Debug</LocalDebuggerWorkingDirectory>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <LocalDebuggerEnvironment />
-    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
-    <LocalDebuggerCommandArguments />
-    <LocalDebuggerWorkingDirectory>.\Release</LocalDebuggerWorkingDirectory>
-  </PropertyGroup>
-</Project>

+ 18 - 14
tools/encoder/gameplay-encoder.xcodeproj/project.pbxproj

@@ -59,6 +59,8 @@
 		43BD156A1A581FBE003CA5FF /* libgameplay-deps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 43BD15691A581FBE003CA5FF /* libgameplay-deps.a */; };
 		B661733F16A61CE40083A307 /* Image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B661733D16A61CE40083A307 /* Image.cpp */; };
 		B661734316A61CFA0083A307 /* NormalMapGenerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B661734116A61CFA0083A307 /* NormalMapGenerator.cpp */; };
+		C0575FA21B4C5C3A007B96B0 /* TMXSceneEncoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C0575F9E1B4C5C3A007B96B0 /* TMXSceneEncoder.cpp */; };
+		C0575FA31B4C5C3A007B96B0 /* TMXTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C0575FA01B4C5C3A007B96B0 /* TMXTypes.cpp */; };
 		C076C905174F6D2E00645678 /* Constants.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C076C8FF174F6D2E00645678 /* Constants.cpp */; };
 		C076C906174F6D2E00645678 /* FBXUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C076C901174F6D2E00645678 /* FBXUtil.cpp */; };
 		C076C907174F6D2E00645678 /* Sampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C076C903174F6D2E00645678 /* Sampler.cpp */; };
@@ -177,6 +179,10 @@
 		B661733E16A61CE40083A307 /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Image.h; path = src/Image.h; sourceTree = SOURCE_ROOT; };
 		B661734116A61CFA0083A307 /* NormalMapGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NormalMapGenerator.cpp; path = src/NormalMapGenerator.cpp; sourceTree = SOURCE_ROOT; };
 		B661734216A61CFA0083A307 /* NormalMapGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NormalMapGenerator.h; path = src/NormalMapGenerator.h; sourceTree = SOURCE_ROOT; };
+		C0575F9E1B4C5C3A007B96B0 /* TMXSceneEncoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TMXSceneEncoder.cpp; path = src/TMXSceneEncoder.cpp; sourceTree = SOURCE_ROOT; };
+		C0575F9F1B4C5C3A007B96B0 /* TMXSceneEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TMXSceneEncoder.h; path = src/TMXSceneEncoder.h; sourceTree = SOURCE_ROOT; };
+		C0575FA01B4C5C3A007B96B0 /* TMXTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TMXTypes.cpp; path = src/TMXTypes.cpp; sourceTree = SOURCE_ROOT; };
+		C0575FA11B4C5C3A007B96B0 /* TMXTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TMXTypes.h; path = src/TMXTypes.h; sourceTree = SOURCE_ROOT; };
 		C076C8FF174F6D2E00645678 /* Constants.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Constants.cpp; path = src/Constants.cpp; sourceTree = SOURCE_ROOT; };
 		C076C900174F6D2E00645678 /* Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Constants.h; path = src/Constants.h; sourceTree = SOURCE_ROOT; };
 		C076C901174F6D2E00645678 /* FBXUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FBXUtil.cpp; path = src/FBXUtil.cpp; sourceTree = SOURCE_ROOT; };
@@ -324,6 +330,10 @@
 				42C8EDFD14724CD700E43619 /* Transform.h */,
 				42C8EDFE14724CD700E43619 /* TTFFontEncoder.cpp */,
 				42C8EDFF14724CD700E43619 /* TTFFontEncoder.h */,
+				C0575F9E1B4C5C3A007B96B0 /* TMXSceneEncoder.cpp */,
+				C0575F9F1B4C5C3A007B96B0 /* TMXSceneEncoder.h */,
+				C0575FA11B4C5C3A007B96B0 /* TMXTypes.h */,
+				C0575FA01B4C5C3A007B96B0 /* TMXTypes.cpp */,
 				42C8EE0014724CD700E43619 /* Vector2.cpp */,
 				42C8EE0114724CD700E43619 /* Vector2.h */,
 				42783420148D6F7500A6E27F /* Vector2.inl */,
@@ -420,6 +430,7 @@
 				42C8EE1A14724CD700E43619 /* GPBFile.cpp in Sources */,
 				42C8EE1B14724CD700E43619 /* Light.cpp in Sources */,
 				42C8EE1D14724CD700E43619 /* main.cpp in Sources */,
+				C0575FA21B4C5C3A007B96B0 /* TMXSceneEncoder.cpp in Sources */,
 				42C8EE1E14724CD700E43619 /* Material.cpp in Sources */,
 				42C8EE1F14724CD700E43619 /* MaterialParameter.cpp in Sources */,
 				42C8EE2014724CD700E43619 /* Matrix.cpp in Sources */,
@@ -448,6 +459,7 @@
 				4251B12C152D044B002F6199 /* Curve.cpp in Sources */,
 				F18DCD0615D554B800DB35DB /* Heightmap.cpp in Sources */,
 				B661733F16A61CE40083A307 /* Image.cpp in Sources */,
+				C0575FA31B4C5C3A007B96B0 /* TMXTypes.cpp in Sources */,
 				B661734316A61CFA0083A307 /* NormalMapGenerator.cpp in Sources */,
 				C076C905174F6D2E00645678 /* Constants.cpp in Sources */,
 				C076C906174F6D2E00645678 /* FBXUtil.cpp in Sources */,
@@ -462,8 +474,8 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++98";
-				CLANG_CXX_LIBRARY = "libstdc++";
+				CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
 				CLANG_WARN_EMPTY_BODY = YES;
@@ -473,7 +485,7 @@
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_C_LANGUAGE_STANDARD = c11;
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
 				GCC_LINK_WITH_DYNAMIC_LIBRARIES = NO;
@@ -483,7 +495,6 @@
 					USE_FBX,
 				);
 				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
-				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
@@ -510,8 +521,8 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++98";
-				CLANG_CXX_LIBRARY = "libstdc++";
+				CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_WARN_BOOL_CONVERSION = YES;
 				CLANG_WARN_CONSTANT_CONVERSION = YES;
 				CLANG_WARN_EMPTY_BODY = YES;
@@ -522,11 +533,10 @@
 				COPY_PHASE_STRIP = YES;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_C_LANGUAGE_STANDARD = c11;
 				GCC_ENABLE_OBJC_EXCEPTIONS = YES;
 				GCC_LINK_WITH_DYNAMIC_LIBRARIES = NO;
 				GCC_PREPROCESSOR_DEFINITIONS = USE_FBX;
-				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
@@ -551,10 +561,7 @@
 		42475CF1147208A100610A6A /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				CLANG_CXX_LANGUAGE_STANDARD = "compiler-default";
-				CLANG_CXX_LIBRARY = "compiler-default";
 				GCC_C_LANGUAGE_STANDARD = "compiler-default";
-				GCC_ENABLE_CPP_EXCEPTIONS = NO;
 				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
 				GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
@@ -575,10 +582,7 @@
 		42475CF2147208A100610A6A /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				CLANG_CXX_LANGUAGE_STANDARD = "compiler-default";
-				CLANG_CXX_LIBRARY = "compiler-default";
 				GCC_C_LANGUAGE_STANDARD = "compiler-default";
-				GCC_ENABLE_CPP_EXCEPTIONS = NO;
 				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
 				GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;

+ 122 - 0
tools/encoder/src/Base.cpp

@@ -23,4 +23,126 @@ std::string getBaseName(const std::string& filepath)
     return output;
 }
 
+/*
+base64.cpp and base64.h (combined into base.h/base.cpp)
+
+Copyright (C) 2004-2008 René Nyffenegger
+
+This source code is provided 'as-is', without any express or implied
+warranty. In no event will the author be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this source code must not be misrepresented; you must not
+claim that you wrote the original source code. If you use this source code
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original source code.
+
+3. This notice may not be removed or altered from any source distribution.
+
+René Nyffenegger [email protected]
+
+*/
+
+static const std::string base64_chars =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+"abcdefghijklmnopqrstuvwxyz"
+"0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+    return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+    std::string ret;
+    int i = 0;
+    int j = 0;
+    unsigned char char_array_3[3];
+    unsigned char char_array_4[4];
+
+    while (in_len--) {
+        char_array_3[i++] = *(bytes_to_encode++);
+        if (i == 3) {
+            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+            char_array_4[3] = char_array_3[2] & 0x3f;
+
+            for (i = 0; (i <4); i++)
+                ret += base64_chars[char_array_4[i]];
+            i = 0;
+        }
+    }
+
+    if (i)
+    {
+        for (j = i; j < 3; j++)
+            char_array_3[j] = '\0';
+
+        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+        char_array_4[3] = char_array_3[2] & 0x3f;
+
+        for (j = 0; (j < i + 1); j++)
+            ret += base64_chars[char_array_4[j]];
+
+        while ((i++ < 3))
+            ret += '=';
+
+    }
+
+    return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+    int in_len = (int)encoded_string.size(); //XXX Added cast
+    int i = 0;
+    int j = 0;
+    int in_ = 0;
+    unsigned char char_array_4[4], char_array_3[3];
+    std::string ret;
+
+    while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+        char_array_4[i++] = encoded_string[in_]; in_++;
+        if (i == 4) {
+            for (i = 0; i <4; i++)
+                char_array_4[i] = (unsigned char)base64_chars.find(char_array_4[i]); //XXX Added cast
+
+            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+            for (i = 0; (i < 3); i++)
+                ret += char_array_3[i];
+            i = 0;
+        }
+    }
+
+    if (i) {
+        for (j = i; j <4; j++)
+            char_array_4[j] = 0;
+
+        for (j = 0; j <4; j++)
+            char_array_4[j] = (unsigned char)base64_chars.find(char_array_4[j]); //XXX Added cast
+
+        char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+        char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+        for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+    }
+
+    return ret;
+}
+
+
 }

+ 6 - 0
tools/encoder/src/Base.h

@@ -38,6 +38,9 @@ using std::endl;
     #pragma warning( disable : 4267 )
     #pragma warning( disable : 4311 )
     #pragma warning( disable : 4390 )
+    #pragma warning( disable : 4456 )
+    #pragma warning( disable : 4458 )
+    #pragma warning( disable : 4477 )
     #pragma warning( disable : 4701 )
     #pragma warning( disable : 4800 )
     #pragma warning( disable : 4996 )
@@ -101,6 +104,9 @@ void fillArray(float values[], float value, size_t length);
  */
 std::string getBaseName(const std::string& filepath);
 
+std::string base64_encode(unsigned char const*, unsigned int len);
+std::string base64_decode(std::string const& s);
+
 #define ISZERO(x) (fabs(x) < MATH_EPSILON)
 #define ISONE(x) ((x - 1.0f) < MATH_EPSILON)
 

+ 42 - 5
tools/encoder/src/EncoderArguments.cpp

@@ -28,7 +28,8 @@ EncoderArguments::EncoderArguments(size_t argc, const char** argv) :
     _textOutput(false),
     _optimizeAnimations(false),
     _animationGrouping(ANIMATIONGROUP_PROMPT),
-    _outputMaterial(false)
+    _outputMaterial(false),
+    _generateTextureGutter(false)
 {
     __instance = this;
 
@@ -81,11 +82,23 @@ const std::string& EncoderArguments::getFilePath() const
     return _filePath;
 }
 
+const std::string EncoderArguments::getFileDirPath() const
+{
+    int pos = _filePath.find_last_of('/');
+    return (pos == -1 ? _filePath : _filePath.substr(0, pos));
+}
+
 const char* EncoderArguments::getFilePathPointer() const
 {
     return _filePath.c_str();
 }
 
+const std::string EncoderArguments::getFileName() const
+{
+    int pos = _filePath.find_last_of('/');
+    return (pos == -1 ? _filePath : _filePath.substr(pos + 1));
+}
+
 std::string EncoderArguments::getOutputDirPath() const
 {
     if (_fileOutputPath.size() > 0)
@@ -104,6 +117,8 @@ std::string EncoderArguments::getOutputFileExtension() const
 {
     switch (getFileFormat())
     {
+    case FILEFORMAT_TMX:
+        return ".scene";
     case FILEFORMAT_PNG:
     case FILEFORMAT_RAW:
         if (_normalMap)
@@ -271,6 +286,11 @@ void EncoderArguments::printUsage() const
         "\t\tMultiple -h arguments can be supplied to generate more than one \n" \
         "\t\theightmap. For 24-bit packed height data use -hp instead of -h.\n" \
     "\n" \
+    "TMX file options:\n" \
+    "  -tg\tEnable texture gutter's around tiles. This will modify any referenced\n" \
+    "  \ttile sets to add a 1px border around it to prevent seams.\n"
+    "  -tg:none\tDo not priduce a texture gutter.\n"
+    "\n" \
     "Normal map options:\n" \
         "  -n\t\tGenerate normal map (requires input file of type PNG or RAW)\n" \
         "  -s\t\tSize/resolution of the input heightmap image (required for RAW files)\n" \
@@ -319,6 +339,11 @@ bool EncoderArguments::outputMaterialEnabled() const
     return _outputMaterial;
 }
 
+bool EncoderArguments::generateTextureGutter() const
+{
+    return _generateTextureGutter;
+}
+
 const char* EncoderArguments::getNodeId() const
 {
     if (_nodeId.length() == 0)
@@ -350,18 +375,22 @@ EncoderArguments::FileFormat EncoderArguments::getFileFormat() const
         ext[i] = (char)tolower(ext[i]);
     
     // Match every supported extension with its format constant
-    if (ext.compare("dae") == 0)
-    {
-        return FILEFORMAT_DAE;
-    }
     if (ext.compare("fbx") == 0)
     {
         return FILEFORMAT_FBX;
     }
+    if (ext.compare("tmx") == 0)
+    {
+        return FILEFORMAT_TMX;
+    }
     if (ext.compare("ttf") == 0)
     {
         return FILEFORMAT_TTF;
     }
+    if (ext.compare("otf") == 0)
+    {
+        return FILEFORMAT_OTF;
+    }
     if (ext.compare("gpb") == 0)
     {
         return FILEFORMAT_GPB;
@@ -652,6 +681,14 @@ void EncoderArguments::readOption(const std::vector<std::string>& options, size_
                 _tangentBinormalId.insert(nodeId);
             }
         }
+        else if (str.compare("-textureGutter:none") == 0 || str.compare("-tg:none") == 0)
+        {
+            _generateTextureGutter = false;
+        }
+        else if (str.compare("-textureGutter") == 0 || str.compare("-tg") == 0)
+        {
+            _generateTextureGutter = true;
+        }
         break;
     case 'v':
         (*index)++;

+ 16 - 1
tools/encoder/src/EncoderArguments.h

@@ -18,9 +18,10 @@ public:
     enum FileFormat
     {
         FILEFORMAT_UNKNOWN,
-        FILEFORMAT_DAE,
         FILEFORMAT_FBX,
+        FILEFORMAT_TMX,
         FILEFORMAT_TTF,
+        FILEFORMAT_OTF,
         FILEFORMAT_GPB,
         FILEFORMAT_PNG,
         FILEFORMAT_RAW
@@ -74,11 +75,22 @@ public:
      */
     const std::string& getFilePath() const;
 
+    /**
+     * Returns the path/folder.
+     * Example: "C:/dir"
+     */
+    const std::string getFileDirPath() const;
+
     /**
      * Returns the char pointer to the file path string.
      */
     const char* getFilePathPointer() const;
 
+    /**
+     * Get the file name of the input file.
+     */
+    const std::string getFileName() const;
+
     /**
      * Returns the output path/folder.
      * Example: "C:/dir"
@@ -164,6 +176,8 @@ public:
 
     bool outputMaterialEnabled() const;
 
+    bool generateTextureGutter() const;
+
     const char* getNodeId() const;
 
     static std::string getRealPath(const std::string& filepath);
@@ -210,6 +224,7 @@ private:
     bool _optimizeAnimations;
     AnimationGroupOption _animationGrouping;
     bool _outputMaterial;
+    bool _generateTextureGutter;
 
     std::vector<std::string> _groupAnimationNodeId;
     std::vector<std::string> _groupAnimationAnimationId;

+ 29 - 15
tools/encoder/src/FBXSceneEncoder.cpp

@@ -571,17 +571,26 @@ void FBXSceneEncoder::transformNode(FbxNode* fbxNode, Node* node)
         }
         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());
+			/*
+			* according to the fbx-documentation the camera's forward vector
+			* points along a node's positive X axis.
+			* so we have to rotate it by 90 around the Y-axis to correct it.
+			*/
+			if (fbxNode->RotationActive.Get())
+			{
+				const FbxVector4& postRotation = fbxNode->PostRotation.Get();
+				fbxNode->SetPostRotation(FbxNode::eSourcePivot, FbxVector4(postRotation.mData[0],
+					postRotation.mData[1] + 90.0,
+					postRotation.mData[2]));
+			}
+			else
+			{
+				// usually for the fbx-exporter in Blender 2.75
+				// if rotation is deactivated we have to rotate the local transform in 90°
+				rotateAdjust.SetR(FbxVector4(0, 90.0, 0.0));
+			}
+
+			matrix = fbxNode->EvaluateLocalTransform() * rotateAdjust;
         }
         
         copyMatrix(matrix, m);
@@ -728,9 +737,13 @@ void FBXSceneEncoder::loadCamera(FbxNode* fbxNode, Node* node)
         id.append("_Camera");
         camera->setId(id);
     }
+
+	// Clip planes have to by divided by the forward scale of the camera (x-axis) to get right values
+	float scale = (float)fbxCamera->GetNode()->LclScaling.Get()[0];
+
     camera->setAspectRatio(getAspectRatio(fbxCamera));
-    camera->setNearPlane((float)fbxCamera->NearPlane.Get());
-    camera->setFarPlane((float)fbxCamera->FarPlane.Get());
+	camera->setNearPlane((float)fbxCamera->NearPlane.Get() / scale);
+	camera->setFarPlane((float)fbxCamera->FarPlane.Get() / scale);
 
     if (fbxCamera->ProjectionType.Get() == FbxCamera::eOrthogonal)
     {
@@ -843,7 +856,7 @@ void FBXSceneEncoder::loadLight(FbxNode* fbxNode, Node* node)
 void FBXSceneEncoder::loadModel(FbxNode* fbxNode, Node* node)
 {
     FbxMesh* fbxMesh = fbxNode->GetMesh();
-    if (!fbxMesh)
+	if (!fbxMesh || fbxMesh->GetPolygonVertexCount() == 0)
     {
         return;
     }
@@ -1243,7 +1256,8 @@ Mesh* FBXSceneEncoder::loadMesh(FbxMesh* fbxMesh)
     const size_t meshpartsSize = meshParts.size();
     for (size_t i = 0; i < meshpartsSize; ++i)
     {
-        mesh->addMeshPart(meshParts[i]);
+        if (meshParts[i]->getIndicesCount() > 0)
+            mesh->addMeshPart(meshParts[i]);
     }
 
     // The order that the vertex elements are add to the list matters.

+ 1000 - 0
tools/encoder/src/TMXSceneEncoder.cpp

@@ -0,0 +1,1000 @@
+#include <string>
+
+#include <zlib.h>
+
+#include "TMXSceneEncoder.h"
+
+using namespace gameplay;
+using namespace tinyxml2;
+using std::string;
+
+#ifdef GP_4_SPACE_TABS
+#define TAB_STRING(count) string((count) * 4, ' ')
+#else
+#define TAB_STRING(count) string((count), '\t')
+#endif
+
+#define BUFFER_SIZE 256
+
+#ifdef WIN32
+#define snprintf(s, n, fmt, ...) sprintf((s), (fmt), __VA_ARGS__)
+#endif
+
+TMXSceneEncoder::TMXSceneEncoder() :
+    _tabCount(0)
+{
+}
+
+TMXSceneEncoder::~TMXSceneEncoder()
+{
+}
+
+void TMXSceneEncoder::write(const EncoderArguments& arguments)
+{
+    XMLDocument xmlDoc;
+    XMLError err;
+    if ((err = xmlDoc.LoadFile(arguments.getFilePath().c_str())) != XML_NO_ERROR)
+    {
+        LOG(1, "Call to XMLDocument::LoadFile() failed.\n");
+        LOG(1, "Error returned: %d\n\n", err);
+        return;
+    }
+    
+    // Parse the Tiled map
+    TMXMap map;
+    string inputDirectory = arguments.getFileDirPath();
+
+    LOG(2, "Parsing .tmx file.\n");
+    if (!parseTmx(xmlDoc, map, inputDirectory))
+    {
+        return;
+    }
+
+    // Apply a gutter, or skirt, around the tiles to prevent gaps
+    if (arguments.generateTextureGutter())
+    {
+        LOG(2, "Bulding gutter tilesets.\n");
+        buildTileGutter(map, inputDirectory, arguments.getOutputDirPath());
+    }
+
+    // Write the tile map
+    string fileName = arguments.getFileName();
+    int pos = fileName.find_last_of('.');
+
+    LOG(2, "Writing .scene file.\n");
+    writeScene(map, arguments.getOutputFilePath(), (pos == -1 ? fileName : fileName.substr(0, pos)));
+}
+
+bool TMXSceneEncoder::parseTmx(const XMLDocument& xmlDoc, TMXMap& map, const string& inputDirectory) const
+{
+    const XMLElement* xmlMap = xmlDoc.FirstChildElement("map");
+    if (!xmlMap)
+    {
+        LOG(1, "Missing root <map> element.\n");
+        return false;
+    }
+
+    // Read in the map values //XXX should compact this so XML attribute parsing is a little nicer
+    unsigned int uiValue;
+    int iValue;
+    const char* attValue = xmlMap->Attribute("width");
+    sscanf(attValue, "%u", &uiValue);
+    map.setWidth(uiValue);
+
+    attValue = xmlMap->Attribute("height");
+    sscanf(attValue, "%u", &uiValue);
+    map.setHeight(uiValue);
+
+    float fValue;
+    attValue = xmlMap->Attribute("tilewidth");
+    sscanf(attValue, "%f", &fValue);
+    map.setTileWidth(fValue);
+
+    attValue = xmlMap->Attribute("tileheight");
+    sscanf(attValue, "%f", &fValue);
+    map.setTileHeight(fValue);
+
+    // Now we load all tilesets
+    const XMLElement* xmlTileSet = xmlMap->FirstChildElement("tileset");
+    while (xmlTileSet)
+    {
+        TMXTileSet tileSet;
+
+        attValue = xmlTileSet->Attribute("firstgid");
+        sscanf(attValue, "%u", &uiValue);
+        tileSet.setFirstGid(uiValue);
+
+        XMLDocument sourceXmlDoc;
+        const XMLElement* xmlTileSetToLoad;
+        attValue = xmlTileSet->Attribute("source");
+        if (attValue)
+        {
+            XMLError err;
+            string tsxLocation = buildFilePath(inputDirectory, attValue);
+            if ((err = sourceXmlDoc.LoadFile(tsxLocation.c_str())) != XML_NO_ERROR)
+            {
+                LOG(1, "Could not load tileset's source TSX.\n");
+                return false;
+            }
+            xmlTileSetToLoad = sourceXmlDoc.RootElement();
+        }
+        else
+        {
+            xmlTileSetToLoad = xmlTileSet;
+        }
+
+        // Maximum tile size
+        attValue = xmlTileSetToLoad->Attribute("tilewidth");
+        if (attValue)
+        {
+            sscanf(attValue, "%u", &uiValue);
+            tileSet.setMaxTileWidth(uiValue);
+        }
+        else
+        {
+            tileSet.setMaxTileWidth(map.getTileWidth());
+        }
+        attValue = xmlTileSetToLoad->Attribute("tileheight");
+        if (attValue)
+        {
+            sscanf(attValue, "%u", &uiValue);
+            tileSet.setMaxTileHeight(uiValue);
+        }
+        else
+        {
+            tileSet.setMaxTileHeight(map.getTileHeight());
+        }
+
+        // Spacing and margin
+        attValue = xmlTileSetToLoad->Attribute("spacing");
+        if (attValue)
+        {
+            sscanf(attValue, "%u", &uiValue);
+            tileSet.setSpacing(uiValue);
+        }
+        attValue = xmlTileSetToLoad->Attribute("margin");
+        if (attValue)
+        {
+            sscanf(attValue, "%u", &uiValue);
+            tileSet.setMargin(uiValue);
+        }
+
+        // Tile offset
+        const XMLElement* xmlTileOffset = xmlTileSetToLoad->FirstChildElement("tileoffset");
+        if (xmlTileOffset)
+        {
+            Vector2 offset;
+
+            attValue = xmlTileOffset->Attribute("x");
+            sscanf(attValue, "%d", &iValue);
+            offset.x = iValue;
+            attValue = xmlTileOffset->Attribute("y");
+            sscanf(attValue, "%d", &iValue);
+            offset.y = iValue;
+
+            tileSet.setOffset(offset);
+        }
+
+        // Load image source. Don't worry about <data> or trans
+        const XMLElement* xmlTileSetImage = xmlTileSetToLoad->FirstChildElement("image");
+        if (!xmlTileSetImage)
+        {
+            LOG(1, "Could not find <image> element for tileset.\n");
+            return false;
+        }
+        tileSet.setImagePath(xmlTileSetImage->Attribute("source"));
+
+        if (xmlTileSetImage->Attribute("width") && xmlTileSetImage->Attribute("height"))
+        {
+            attValue = xmlTileSetImage->Attribute("width");
+            sscanf(attValue, "%u", &uiValue);
+            tileSet.setImageWidth(uiValue);
+
+            attValue = xmlTileSetImage->Attribute("height");
+            sscanf(attValue, "%u", &uiValue);
+            tileSet.setImageHeight(uiValue);
+        }
+        else
+        {
+            // Load the image itself to get the width
+            string imageLocation = buildFilePath(inputDirectory, tileSet.getImagePath());
+            int pos = imageLocation.find_last_of('.');
+            if (imageLocation.substr(pos).compare(".png") != 0)
+            {
+                LOG(1, "TileSet image must be a PNG. %s\n", imageLocation.c_str());
+                return false;
+            }
+
+            Image* img = Image::create(imageLocation.c_str());
+            if (!img)
+            {
+                LOG(1, "Could not load TileSet image. %s\n", imageLocation.c_str());
+                return false;
+            }
+            tileSet.setImageWidth(img->getWidth());
+            tileSet.setImageHeight(img->getHeight());
+            SAFE_DELETE(img);
+        }
+
+        //TODO: tiles (specifically, tile animations) if possible. May require marking tiles as special tiles, and spinning them off to Sprites
+        //<tile id="relId"><animation><frame tileid="relId" duration="numInMS"></...></...></...>
+
+        // Save the tileset
+        map.addTileSet(tileSet);
+
+        xmlTileSet = xmlTileSet->NextSiblingElement("tileset");
+    }
+
+    // Load the layers
+    const XMLElement* xmlLayer = xmlMap->FirstChildElement("layer");
+    while (xmlLayer)
+    {
+        TMXLayer* layer = new TMXLayer();
+
+        parseBaseLayerProperties(xmlLayer, layer);
+
+        // Load properties
+        attValue = xmlLayer->Attribute("width");
+        if (attValue)
+        {
+            sscanf(attValue, "%u", &uiValue);
+            layer->setWidth(uiValue);
+        }
+        else
+        {
+            layer->setWidth(map.getWidth());
+        }
+
+        attValue = xmlLayer->Attribute("height");
+        if (attValue)
+        {
+            sscanf(attValue, "%u", &uiValue);
+            layer->setHeight(uiValue);
+        }
+        else
+        {
+            layer->setHeight(map.getHeight());
+        }
+
+        // Load tiles
+        layer->setupTiles();
+        auto data = loadDataElement(xmlLayer->FirstChildElement("data"));
+        size_t dataSize = data.size();
+        for (int i = 0; i < dataSize; i++)
+        {
+            //XXX this might depend on map's renderorder... not sure
+            unsigned int x = i % layer->getWidth();
+            unsigned int y = i / layer->getWidth();
+            layer->setTile(x, y, data[i]);
+        }
+
+        // Save layer
+        map.addLayer(layer);
+
+        xmlLayer = xmlLayer->NextSiblingElement("layer");
+    }
+
+    // Load image layers
+    const XMLElement* xmlImgLayer = xmlMap->FirstChildElement("imagelayer");
+    while (xmlImgLayer)
+    {
+        TMXImageLayer* imgLayer = new TMXImageLayer();
+
+        parseBaseLayerProperties(xmlImgLayer, imgLayer);
+
+        // Load properties
+        attValue = xmlImgLayer->Attribute("x");
+        if (attValue)
+        {
+            sscanf(attValue, "%d", &iValue);
+            imgLayer->setX(iValue);
+        }
+        attValue = xmlImgLayer->Attribute("y");
+        if (attValue)
+        {
+            sscanf(attValue, "%d", &iValue);
+            imgLayer->setY(iValue);
+        }
+
+        // Load image source. Don't worry about <data>, trans, width, or height
+        const XMLElement* xmlImage = xmlImgLayer->FirstChildElement("image");
+        if (!xmlImage)
+        {
+            LOG(1, "Could not find <image> element for the image layer.\n");
+            return false;
+        }
+        imgLayer->setImagePath(xmlImage->Attribute("source"));
+
+        // Save image layer
+        map.addLayer(imgLayer);
+
+        xmlImgLayer = xmlImgLayer->NextSiblingElement("imagelayer");
+    }
+
+    return true;
+}
+
+void TMXSceneEncoder::parseBaseLayerProperties(const tinyxml2::XMLElement* xmlBaseLayer, gameplay::TMXBaseLayer* layer) const
+{
+    layer->setName(xmlBaseLayer->Attribute("name"));
+
+    float fValue;
+    unsigned int uiValue;
+
+    const char* attValue = xmlBaseLayer->Attribute("opacity");
+    if (attValue)
+    {
+        sscanf(attValue, "%f", &fValue);
+        layer->setOpacity(fValue);
+    }
+
+    attValue = xmlBaseLayer->Attribute("visible");
+    if (attValue)
+    {
+        sscanf(attValue, "%u", &uiValue);
+        layer->setVisible(uiValue == 1);
+    }
+
+    const XMLElement* xmlProperties = xmlBaseLayer->FirstChildElement("properties");
+    if (xmlProperties)
+    {
+        TMXProperties& properties = layer->getProperties();
+        const XMLElement* xmlProperty = xmlProperties->FirstChildElement("property");
+        while (xmlProperty)
+        {
+            properties[xmlProperty->Attribute("name")] = xmlProperty->Attribute("value");
+
+            xmlProperty = xmlProperty->NextSiblingElement("property");
+        }
+    }
+}
+
+void TMXSceneEncoder::buildTileGutter(TMXMap& map, const string& inputDirectory, const string& outputDirectory)
+{
+#define ACTUAL_ADJUST_TILESET(imgPath, spacing, margin) tileset.setImagePath((imgPath)); \
+    tileset.setSpacing(tileset.getSpacing() + (spacing), false); \
+    tileset.setMargin(tileset.getMargin() + (margin), false); \
+    tileset.setImageWidth(TMXTileSet::calculateImageDimension(tileset.getHorizontalTileCount(), tileset.getMaxTileWidth(), tileset.getSpacing(), tileset.getMargin())); \
+    tileset.setImageHeight(TMXTileSet::calculateImageDimension(tileset.getVerticalTileCount(), tileset.getMaxTileHeight(), tileset.getSpacing(), tileset.getMargin()))
+#define ADJUST_TILESET(imgPath) ACTUAL_ADJUST_TILESET(imgPath, 2, 1)
+#define UNDO_ADJUST_TILESET(imgPath) ACTUAL_ADJUST_TILESET(imgPath, -2, -1)
+
+    std::unordered_map<std::string, std::string> processedFiles;
+    std::unordered_map<std::string, std::string> reverseLookupProcessedFiles;
+
+    unsigned int tilesetCount = map.getTileSetCount();
+    for (unsigned int i = 0; i < tilesetCount; i++)
+    {
+        TMXTileSet& tileset = map.getTileSet(i);
+
+        // See if the tileset was already processed. Then we can skip it
+        auto potentialEasyProcess = processedFiles.find(tileset.getImagePath());
+        if (potentialEasyProcess != processedFiles.end())
+        {
+            ADJUST_TILESET(potentialEasyProcess->second);
+            continue;
+        }
+
+        // Process tileset
+        string orgImgPath = tileset.getImagePath();
+        string imgPath = orgImgPath;
+        string inputFile = buildFilePath(inputDirectory, imgPath);
+        int pos = imgPath.find_last_of('.');
+        if (pos == -1)
+        {
+            imgPath = imgPath + "_guttered";
+        }
+        else
+        {
+            imgPath = imgPath.substr(0, pos) + "_guttered" + imgPath.substr(pos);
+        }
+        string outputFile = buildFilePath(outputDirectory, imgPath);
+
+        if (buildTileGutterTileset(tileset, inputFile, outputFile))
+        {
+            // Update tileset
+            ADJUST_TILESET(imgPath);
+
+            // Remember image paths
+            processedFiles[orgImgPath] = imgPath;
+            reverseLookupProcessedFiles[imgPath] = orgImgPath;
+        }
+        else
+        {
+            // Revert all processed tilemaps
+            for (unsigned int k = 0; k < i; k++)
+            {
+                tileset = map.getTileSet(k);
+
+                // Revert to original image and settings
+                orgImgPath = reverseLookupProcessedFiles[tileset.getImagePath()];
+                UNDO_ADJUST_TILESET(orgImgPath);
+
+                // Delete the processed image, if they exist
+                auto it = processedFiles.find(orgImgPath);
+                if (it != processedFiles.end())
+                {
+                    outputFile = buildFilePath(outputDirectory, it->second);
+                    if (remove(outputFile.c_str()) != 0)
+                    {
+                        LOG(3, "Could not remove '%s' during tileset revert.\n", outputFile.c_str());
+                    }
+                    processedFiles.erase(it);
+                }
+            }
+            LOG(1, "Failed to process '%s'. Reverting all tileset gutters.\n", orgImgPath.c_str());
+            return;
+        }
+    }
+
+#undef UNDO_ADJUST_TILESET
+#undef ADJUST_TILESET
+#undef ACTUAL_ADJUST_TILESET
+}
+
+bool TMXSceneEncoder::buildTileGutterTileset(const TMXTileSet& tileset, const string& inputFile, const string& outputFile)
+{
+#define COPY_CONTENTS(sx, sy, dx, dy, w, h) copyImage(outputData, static_cast<unsigned char*>(inputImage->getData()), \
+    inputImageWidth, outputImageWidth, bpp, \
+    (sx), (sy), (dx), (dy), (w), (h))
+
+    // Setup images
+    const Image* inputImage = Image::create(inputFile.c_str());
+    if (!inputImage)
+    {
+        return false;
+    }
+
+    unsigned int tilesetWidth = tileset.getHorizontalTileCount();
+    unsigned int tilesetHeight = tileset.getVerticalTileCount();
+    Vector2 tileSize = Vector2(tileset.getMaxTileWidth(), tileset.getMaxTileHeight());
+    unsigned int bpp = 0;
+    switch (inputImage->getFormat())
+    {
+        case Image::LUMINANCE:
+            bpp = 1;
+            break;
+        case Image::RGB:
+            bpp = 3;
+            break;
+        case Image::RGBA:
+            bpp = 4;
+            break;
+        default:
+            LOG(4, "Unknown image format. Possibly need update by developer.\n")
+            return false;
+    }
+
+    Image* outputImage = Image::create(inputImage->getFormat(),
+        TMXTileSet::calculateImageDimension(tilesetWidth, tileSize.x, tileset.getSpacing() + 2, tileset.getMargin() + 1),
+        TMXTileSet::calculateImageDimension(tilesetHeight, tileSize.y, tileset.getSpacing() + 2, tileset.getMargin() + 1));
+
+    // Get a couple variables so we don't constantly call functions (they aren't inline)
+    unsigned int inputImageWidth = inputImage->getWidth();
+    unsigned int inputImageHeight = inputImage->getHeight();
+    unsigned int outputImageWidth = outputImage->getWidth();
+    unsigned int outputImageHeight = outputImage->getHeight();
+    unsigned char* outputData = static_cast<unsigned char*>(outputImage->getData());
+    unsigned int tilesetSpacing = tileset.getSpacing();
+    unsigned int tilesetMargin = tileset.getMargin();
+
+    // Copy margin
+    if (tilesetMargin != 0)
+    {
+        // Horizontal
+        unsigned int avaliable = inputImageWidth;
+        unsigned int remaining = outputImageWidth;
+        unsigned int use = min(avaliable, remaining);
+
+        while (remaining > 0)
+        {
+            // Top
+            COPY_CONTENTS(0, 0, outputImageWidth - remaining, 0, use, tilesetMargin);
+
+            // Bottom
+            COPY_CONTENTS(0, inputImageHeight - tilesetMargin - 1, outputImageWidth - remaining, outputImageHeight - tilesetMargin - 1, use, tilesetMargin);
+
+            remaining -= use;
+            use = min(avaliable, remaining);
+        }
+
+        // Vertical
+        avaliable = inputImageHeight;
+        remaining = outputImageHeight;
+        use = min(avaliable, remaining);
+
+        while (remaining > 0)
+        {
+            // Left
+            COPY_CONTENTS(0, 0, 0, outputImageHeight - remaining, tilesetMargin, use);
+
+            // Right
+            COPY_CONTENTS(inputImageWidth - tilesetMargin - 1, 0, inputImageWidth - tilesetMargin - 1, outputImageHeight - remaining, tilesetMargin, use);
+
+            remaining -= use;
+            use = min(avaliable, remaining);
+        }
+    }
+
+    // Copy the contents of each tile
+    for (unsigned int y = 0; y < tilesetHeight; y++)
+    {
+        for (unsigned int x = 0; x < tilesetWidth; x++)
+        {
+            Vector2 src = TMXTileSet::calculateTileOrigin(Vector2(x, y), tileSize, tilesetSpacing, tilesetMargin);
+            Vector2 dst = TMXTileSet::calculateTileOrigin(Vector2(x, y), tileSize, tilesetSpacing + 2, tilesetMargin + 1);
+
+            COPY_CONTENTS(src.x, src.y, dst.x, dst.y, tileSize.x, tileSize.y);
+        }
+    }
+    
+    // Extend the edges of the tiles to produce the "gutter"
+    unsigned int gutterContents = outputImageHeight - ((tilesetMargin + 1) * 2);
+    for (unsigned int x = 0; x < tilesetWidth; x++)
+    {
+        Vector2 pos = TMXTileSet::calculateTileOrigin(Vector2(x, 0), tileSize, tilesetSpacing + 2, tilesetMargin + 1);
+        
+        // Left
+        copyImage(outputData, outputData,
+            outputImageWidth, outputImageWidth, bpp,
+            pos.x, pos.y, pos.x - 1, pos.y, 1, gutterContents);
+
+        // Right
+        copyImage(outputData, outputData,
+            outputImageWidth, outputImageWidth, bpp,
+            pos.x + tileSize.x - 1, pos.y, pos.x + tileSize.x, pos.y, 1, gutterContents);
+    }
+    gutterContents = outputImageWidth - ((tilesetMargin + 1) * 2);
+    for (unsigned int y = 0; y < tilesetHeight; y++)
+    {
+        Vector2 pos = TMXTileSet::calculateTileOrigin(Vector2(0, y), tileSize, tilesetSpacing + 2, tilesetMargin + 1);
+
+        // Top
+        copyImage(outputData, outputData,
+            outputImageWidth, outputImageWidth, bpp,
+            pos.x, pos.y, pos.x, pos.y - 1, gutterContents, 1);
+
+        // Bottom
+        copyImage(outputData, outputData,
+            outputImageWidth, outputImageWidth, bpp,
+            pos.x, pos.y + tileSize.y - 1, pos.x, pos.y + tileSize.y, gutterContents, 1);
+    }
+
+    // Save and cleanup
+    outputImage->save(outputFile.c_str());
+    SAFE_DELETE(inputImage);
+    SAFE_DELETE(outputImage);
+
+    return true;
+
+#undef COPY_CONTENTS
+}
+
+//XXX could probably seperate the writing process to a seperate class (PropertyWriter...)
+#define WRITE_PROPERTY_BLOCK_START(str) writeLine(file, (str)); \
+    writeLine(file, "{"); \
+    _tabCount++
+#define WRITE_PROPERTY_BLOCK_END() _tabCount--; \
+    writeLine(file, "}")
+#define WRITE_PROPERTY_BLOCK_VALUE(name, value) writeLine(file, string(name) + " = " + (value))
+#define WRITE_PROPERTY_DIRECT(value) writeLine(file, (value))
+#define WRITE_PROPERTY_NEWLINE() file << std::endl
+
+void TMXSceneEncoder::writeScene(const TMXMap& map, const string& outputFilepath, const string& sceneName)
+{
+    // Prepare for writing the scene
+    std::ofstream file(outputFilepath.c_str(), std::ofstream::out | std::ofstream::trunc);
+
+    unsigned int layerCount = map.getLayerCount();
+
+    // Write initial scene
+    WRITE_PROPERTY_BLOCK_START("scene " + sceneName);
+
+    // Write all layers
+    for (unsigned int i = 0; i < layerCount; i++)
+    {
+        const TMXBaseLayer* layer = map.getLayer(i);
+        TMXLayerType type = layer->getType();
+        if (type == TMXLayerType::NormalLayer)
+        {
+            writeTileset(map, dynamic_cast<const TMXLayer*>(layer), file);
+            WRITE_PROPERTY_NEWLINE();
+        }
+        else if (type == TMXLayerType::ImageLayer)
+        {
+            writeSprite(dynamic_cast<const TMXImageLayer*>(layer), file);
+            WRITE_PROPERTY_NEWLINE();
+        }
+    }
+
+    WRITE_PROPERTY_BLOCK_END();
+
+    // Cleanup
+    file.flush();
+    file.close();
+}
+
+// This is actually a misnomer. What is a Layer in Tiled/TMX is a TileSet for GamePlay3d. TileSet in Tiled/TMX is something different.
+void TMXSceneEncoder::writeTileset(const TMXMap& map, const TMXLayer* tileset, std::ofstream& file)
+{
+    if (!tileset || !tileset->hasTiles())
+    {
+        return;
+    }
+
+    std::set<unsigned int> tilesets = tileset->getTilesetsUsed(map);
+    if (tilesets.size() == 0)
+    {
+        return;
+    }
+
+    char buffer[BUFFER_SIZE];
+    WRITE_PROPERTY_BLOCK_START("node " + tileset->getName());
+
+    if (tilesets.size() > 1)
+    {
+        unsigned int i = 0;
+        for (auto it = tilesets.begin(); it != tilesets.end(); it++, i++)
+        {
+            snprintf(buffer, BUFFER_SIZE, "node tileset_%d", i);
+            WRITE_PROPERTY_BLOCK_START(buffer);
+
+            const TMXTileSet& tmxTileset = map.getTileSet(*it);
+            writeSoloTileset(map, tmxTileset, *tileset, file, *it);
+
+            const Vector2& tileOffset = tmxTileset.getOffset();
+            if (!(tileOffset == Vector2::zero()))
+            {
+                // Tile offset moves the tiles, not the origin of each tile
+                snprintf(buffer, BUFFER_SIZE, "translate = %d, %d, 0", static_cast<int>(tileOffset.x), static_cast<int>(tileOffset.y));
+                WRITE_PROPERTY_NEWLINE();
+                WRITE_PROPERTY_DIRECT(buffer);
+            }
+
+            WRITE_PROPERTY_BLOCK_END();
+            if ((i + 1) != tilesets.size())
+            {
+                WRITE_PROPERTY_NEWLINE();
+            }
+        }
+    }
+    else
+    {
+        const TMXTileSet& tmxTileset = map.getTileSet(*(tilesets.begin()));
+        writeSoloTileset(map, tmxTileset, *tileset, file);
+
+        const Vector2& tileOffset = tmxTileset.getOffset();
+        if (!(tileOffset == Vector2::zero()))
+        {
+            // Tile offset moves the tiles, not the origin of each tile
+            snprintf(buffer, BUFFER_SIZE, "translate = %d, %d, 0", static_cast<int>(tileOffset.x), static_cast<int>(tileOffset.y));
+            WRITE_PROPERTY_NEWLINE();
+            WRITE_PROPERTY_DIRECT(buffer);
+        }
+    }
+
+    writeNodeProperties(tileset->getVisible(), tileset->getProperties(), file);
+    WRITE_PROPERTY_BLOCK_END();
+}
+
+void TMXSceneEncoder::writeSoloTileset(const TMXMap& map, const gameplay::TMXTileSet& tmxTileset, const TMXLayer& tileset, std::ofstream& file, unsigned int resultOnlyForTileset)
+{
+    WRITE_PROPERTY_BLOCK_START("tileset");
+
+    // Write tile path
+    WRITE_PROPERTY_BLOCK_VALUE("path", tmxTileset.getImagePath());
+    WRITE_PROPERTY_NEWLINE();
+
+    // Write tile size
+    //XXX if tile sizes are incorrect, make sure to update TMXLayer::getTileStart too
+    char buffer[BUFFER_SIZE];
+    snprintf(buffer, BUFFER_SIZE, "tileWidth = %u", tmxTileset.getMaxTileWidth());
+    WRITE_PROPERTY_DIRECT(buffer);
+    snprintf(buffer, BUFFER_SIZE, "tileHeight = %u", tmxTileset.getMaxTileHeight());
+    WRITE_PROPERTY_DIRECT(buffer);
+
+    // Write tileset size
+    snprintf(buffer, BUFFER_SIZE, "columns = %u", tileset.getWidth());
+    WRITE_PROPERTY_DIRECT(buffer);
+    snprintf(buffer, BUFFER_SIZE, "rows = %u", tileset.getHeight());
+    WRITE_PROPERTY_DIRECT(buffer);
+    WRITE_PROPERTY_NEWLINE();
+
+    // Write opacity
+    if (tileset.getOpacity() < 1.0f)
+    {
+        snprintf(buffer, BUFFER_SIZE, "opacity = %f", tileset.getOpacity());
+        WRITE_PROPERTY_DIRECT(buffer);
+        WRITE_PROPERTY_NEWLINE();
+    }
+
+    // Write tiles
+    unsigned int tilesetHeight = tileset.getHeight();
+    unsigned int tilesetWidth = tileset.getWidth();
+    for (unsigned int y = 0; y < tilesetHeight; y++)
+    {
+        bool tilesWritten = false;
+        for (unsigned int x = 0; x < tilesetWidth; x++)
+        {
+            Vector2 startPos = tileset.getTileStart(x, y, map, resultOnlyForTileset);
+            if (startPos.x < 0 || startPos.y < 0)
+            {
+                continue;
+            }
+
+            tilesWritten = true;
+            WRITE_PROPERTY_BLOCK_START("tile");
+            snprintf(buffer, BUFFER_SIZE, "cell = %u, %u", x, y);
+            WRITE_PROPERTY_DIRECT(buffer);
+            snprintf(buffer, BUFFER_SIZE, "source = %u, %u", static_cast<unsigned int>(startPos.x), static_cast<unsigned int>(startPos.y));
+            WRITE_PROPERTY_DIRECT(buffer);
+            WRITE_PROPERTY_BLOCK_END();
+        }
+        if (tilesWritten && ((y + 1) != tilesetHeight))
+        {
+            WRITE_PROPERTY_NEWLINE();
+        }
+    }
+
+    WRITE_PROPERTY_BLOCK_END();
+}
+
+void TMXSceneEncoder::writeSprite(const gameplay::TMXImageLayer* imageLayer, std::ofstream& file)
+{
+    if (!imageLayer)
+    {
+        return;
+    }
+
+    WRITE_PROPERTY_BLOCK_START("node " + imageLayer->getName());
+
+    // Sprite
+    {
+        WRITE_PROPERTY_BLOCK_START("sprite");
+
+        WRITE_PROPERTY_BLOCK_VALUE("path", imageLayer->getImagePath());
+        WRITE_PROPERTY_NEWLINE();
+
+        WRITE_PROPERTY_BLOCK_END();
+    }
+
+    writeNodeProperties(imageLayer->getVisible(), imageLayer->getPosition(), imageLayer->getProperties(), file);
+    WRITE_PROPERTY_BLOCK_END();
+}
+
+void TMXSceneEncoder::writeNodeProperties(bool enabled, const Vector2& pos, const TMXProperties& properties, std::ofstream& file, bool seperatorLineWritten)
+{
+    char buffer[BUFFER_SIZE];
+    if (!enabled)
+    {
+        // Default is true, so only write it false
+        WRITE_PROPERTY_NEWLINE();
+        seperatorLineWritten = true;
+        WRITE_PROPERTY_DIRECT("enabled = false");
+    }
+    if (!(pos == Vector2::zero()))
+    {
+        if (!seperatorLineWritten)
+        {
+            WRITE_PROPERTY_NEWLINE();
+            seperatorLineWritten = true;
+        }
+        snprintf(buffer, BUFFER_SIZE, "translate = %d, %d, 0", static_cast<int>(pos.x), static_cast<int>(pos.y));
+        WRITE_PROPERTY_DIRECT(buffer);
+    }
+
+    if (properties.size() > 0)
+    {
+        WRITE_PROPERTY_NEWLINE();
+        WRITE_PROPERTY_BLOCK_START("tags");
+        for (auto it = properties.begin(); it != properties.end(); it++)
+        {
+            WRITE_PROPERTY_BLOCK_VALUE(it->first, it->second);
+        }
+        WRITE_PROPERTY_BLOCK_END();
+    }
+}
+
+#undef WRITE_PROPERTY_NEWLINE
+#undef WRITE_PROPERTY_DIRECT
+#undef WRITE_PROPERTY_BLOCK_VALUE
+#undef WRITE_PROPERTY_BLOCK_END
+#undef WRITE_PROPERTY_BLOCK_START
+
+void TMXSceneEncoder::writeLine(std::ofstream& file, const string& line) const
+{
+    file << TAB_STRING(_tabCount) << line << std::endl;
+}
+
+bool isBase64(char c)
+{
+    return (c >= 'a' && c <= 'z') ||
+        (c >= 'A' && c <= 'Z') ||
+        (c >= '0' && c <= '9') ||
+        (c == '=');
+}
+
+std::vector<unsigned int> TMXSceneEncoder::loadDataElement(const XMLElement* data)
+{
+    if (!data)
+    {
+        return std::vector<unsigned int>();
+    }
+
+    const char* encoding = data->Attribute("encoding");
+    const char* compression = data->Attribute("compression");
+
+    const char* attValue = "0";
+    unsigned int tileGid;
+    std::vector<unsigned int> tileData;
+
+    if (!compression && !encoding)
+    {
+        const XMLElement* xmlTile = data->FirstChildElement("tile");
+        while (xmlTile)
+        {
+            attValue = xmlTile->Attribute("gid");
+            sscanf(attValue, "%u", &tileGid);
+            tileData.push_back(tileGid);
+
+            xmlTile = xmlTile->NextSiblingElement("tile");
+        }
+    }
+    else
+    {
+        if (strcmp(encoding, "csv") == 0)
+        {
+            if (compression)
+            {
+                LOG(1, "Compression is not supported with CSV encoding.\n");
+                exit(-1);
+            }
+
+            const char* rawCsvData = data->GetText();
+            int start = 0;
+            char* endptr;
+            // Skip everything before values
+            while (rawCsvData[start] == ' ' || rawCsvData[start] == '\n' || rawCsvData[start] == '\r' || rawCsvData[start] == '\t')
+            {
+                start++;
+            }
+            // Iterate through values. Skipping to next value when done
+            while (rawCsvData[start])
+            {
+                tileData.push_back(strtoul(rawCsvData + start, &endptr, 10));
+                start = endptr - rawCsvData;
+                while (rawCsvData[start] == ' ' || rawCsvData[start] == ',' || rawCsvData[start] == '\n' || rawCsvData[start] == '\r' || rawCsvData[start] == '\t')
+                {
+                    start++;
+                }
+            }
+        }
+        else if (strcmp(encoding, "base64") == 0)
+        {
+            string base64Data = data->GetText();
+            if (base64Data.size() > 0 && (!isBase64(base64Data[0]) || !isBase64(base64Data[base64Data.size() - 1])))
+            {
+                int start = 0;
+                size_t end = base64Data.size() - 1;
+                while (!isBase64(base64Data[start]))
+                {
+                    start++;
+                }
+                while (!isBase64(base64Data[end]))
+                {
+                    end--;
+                }
+                base64Data = base64Data.substr(start, end - start + 1);
+            }
+            string byteData = base64_decode(base64Data);
+
+            if (compression)
+            {
+                int err;
+                const unsigned int buffer_size = 4096;
+                char buffer[buffer_size];
+                string decomData;
+
+                if (strcmp(compression, "zlib") == 0 || strcmp(compression, "gzip") == 0)
+                {
+                    z_stream dString;
+                    dString.zalloc = Z_NULL;
+                    dString.zfree = Z_NULL;
+                    dString.opaque = Z_NULL;
+                    dString.next_in = (Bytef*)byteData.c_str();
+                    dString.avail_in = byteData.size();
+
+                    if (strcmp(compression, "zlib") == 0)
+                    {
+                        // zlib
+                        if ((err = inflateInit(&dString)) != Z_OK)
+                        {
+                            LOG(1, "ZLIB inflateInit failed. Error: %d.\n", err);
+                            exit(-1);
+                        }
+                    }
+                    else
+                    {
+                        // gzip
+                        if ((err = inflateInit2(&dString, 16+MAX_WBITS)) != Z_OK)
+                        {
+                            LOG(1, "ZLIB inflateInit2 failed. Error: %d.\n", err);
+                            exit(-1);
+                        }
+                    }
+
+                    do
+                    {
+                        dString.next_out = (Bytef*)buffer;
+                        dString.avail_out = buffer_size;
+                        err = inflate(&dString, Z_NO_FLUSH);
+                        switch (err)
+                        {
+                            case Z_NEED_DICT:
+                            case Z_DATA_ERROR:
+                            case Z_MEM_ERROR:
+                                inflateEnd(&dString);
+                                LOG(1, "ZLIB inflate failed. Error: %d.\n", err);
+                                exit(-1);
+                                break;
+                        }
+
+                        string decomBlock(buffer, buffer_size - dString.avail_out);
+                        decomData += decomBlock;
+                    } while (err != Z_STREAM_END);
+
+                    inflateEnd(&dString);
+                }
+                else
+                {
+                    LOG(1, "Unknown compression: %s.\n", compression);
+                    exit(-1);
+                }
+
+                byteData = decomData;
+            }
+
+            size_t byteDataSize = byteData.size();
+            for (unsigned int i = 0; i < byteDataSize; i += 4)
+            {
+                unsigned int gid = static_cast<unsigned char>(byteData[i + 0]) |
+                    (static_cast<unsigned char>(byteData[i + 1]) << 8u) | 
+                    (static_cast<unsigned char>(byteData[i + 2]) << 16u) |
+                    (static_cast<unsigned char>(byteData[i + 3]) << 24u);
+                tileData.push_back(gid);
+            }
+        }
+        else
+        {
+            LOG(1, "Unknown encoding: %s.\n", encoding);
+            exit(-1);
+        }
+    }
+
+    return tileData;
+}
+
+std::string TMXSceneEncoder::buildFilePath(const std::string& directory, const std::string& file)
+{
+    return EncoderArguments::getRealPath(directory + "/" + file);
+}
+
+void TMXSceneEncoder::copyImage(unsigned char* dst, const unsigned char* src,
+    unsigned int srcWidth, unsigned int dstWidth, unsigned int bpp,
+    unsigned int srcx, unsigned int srcy, unsigned int dstx, unsigned int dsty, unsigned int width, unsigned int height)
+{
+    unsigned int sizePerRow = width * bpp;
+    for (unsigned int y = 0; y < height; y++)
+    {
+        const unsigned char* srcPtr = &src[((y + srcy) * srcWidth + srcx) * bpp];
+        unsigned char* dstPtr = &dst[((y + dsty) * dstWidth + dstx) * bpp];
+        if (src == dst)
+        {
+            memmove(dstPtr, srcPtr, sizePerRow);
+        }
+        else
+        {
+            memcpy(dstPtr, srcPtr, sizePerRow);
+        }
+    }
+}

+ 82 - 0
tools/encoder/src/TMXSceneEncoder.h

@@ -0,0 +1,82 @@
+#ifndef TMXSCENEEENCODER_H_
+#define TMXSCENEEENCODER_H_
+
+#include <fstream>
+#include <unordered_map>
+#include <tinyxml2.h>
+
+#include "Base.h"
+#include "Vector2.h"
+#include "TMXTypes.h"
+#include "EncoderArguments.h"
+#include "Image.h"
+
+/**
+ * Class for encoding an TMX file.
+ */
+class TMXSceneEncoder
+{
+public:
+    /**
+     * Constructor.
+     */
+    TMXSceneEncoder();
+
+    /**
+     * Destructor.
+     */
+    ~TMXSceneEncoder();
+
+    /**
+     * Writes out encoded TMX file.
+     */
+    void write(const gameplay::EncoderArguments& arguments);
+
+private:
+    static std::vector<unsigned int> loadDataElement(const tinyxml2::XMLElement* data);
+    static inline std::string buildFilePath(const std::string& directory, const std::string& file);
+    static void copyImage(unsigned char* dst, const unsigned char* src,
+        unsigned int srcWidth, unsigned int dstWidth, unsigned int bpp,
+        unsigned int srcx, unsigned int srcy, unsigned int dstx, unsigned int dsty, unsigned int width, unsigned int height);
+
+    // Parsing
+    bool parseTmx(const tinyxml2::XMLDocument& xmlDoc, gameplay::TMXMap& map, const std::string& inputDirectory) const;
+    void parseBaseLayerProperties(const tinyxml2::XMLElement* xmlBaseLayer, gameplay::TMXBaseLayer* layer) const;
+
+    // Gutter
+    void buildTileGutter(gameplay::TMXMap& map, const std::string& inputDirectory, const std::string& outputDirectory);
+    bool buildTileGutterTileset(const gameplay::TMXTileSet& tileset, const std::string& inputFile, const std::string& outputFile);
+
+    // Writing
+    void writeScene(const gameplay::TMXMap& map, const std::string& outputFilepath, const std::string& sceneName);
+
+    void writeTileset(const gameplay::TMXMap& map, const gameplay::TMXLayer* layer, std::ofstream& file);
+    void writeSoloTileset(const gameplay::TMXMap& map, const gameplay::TMXTileSet& tmxTileset, const gameplay::TMXLayer& tileset, std::ofstream& file, unsigned int resultOnlyForTileset = TMX_INVALID_ID);
+
+    void writeSprite(const gameplay::TMXImageLayer* imageLayer, std::ofstream& file);
+
+    void writeNodeProperties(bool enabled, std::ofstream& file, bool seperatorLineWritten = false);
+    void writeNodeProperties(bool enabled, const gameplay::TMXProperties& properties, std::ofstream& file, bool seperatorLineWritten = false);
+    void writeNodeProperties(bool enabled, const gameplay::Vector2& pos, std::ofstream& file, bool seperatorLineWritten = false);
+    void writeNodeProperties(bool enabled, const gameplay::Vector2& pos, const gameplay::TMXProperties& properties, std::ofstream& file, bool seperatorLineWritten = false);
+
+    void writeLine(std::ofstream& file, const std::string& line) const;
+
+    unsigned int _tabCount;
+};
+
+inline void TMXSceneEncoder::writeNodeProperties(bool enabled, std::ofstream& file, bool seperatorLineWritten)
+{
+    writeNodeProperties(enabled, gameplay::Vector2::zero(), file, seperatorLineWritten);
+}
+inline void TMXSceneEncoder::writeNodeProperties(bool enabled, const gameplay::TMXProperties& properties, std::ofstream& file, bool seperatorLineWritten)
+{
+    writeNodeProperties(enabled, gameplay::Vector2::zero(), properties, file, seperatorLineWritten);
+}
+inline void TMXSceneEncoder::writeNodeProperties(bool enabled, const gameplay::Vector2& pos, std::ofstream& file, bool seperatorLineWritten)
+{
+    gameplay::TMXProperties noOp;
+    writeNodeProperties(enabled, pos, noOp, file, seperatorLineWritten);
+}
+
+#endif

+ 518 - 0
tools/encoder/src/TMXTypes.cpp

@@ -0,0 +1,518 @@
+#include "TMXTypes.h"
+
+using namespace gameplay;
+
+// TMXMap
+TMXMap::TMXMap()
+    : _width(0), _height(0),
+    _tileWidth(0.0f), _tileHeight(0.0f),
+    _tileSets(), _layers()
+{
+}
+
+TMXMap::~TMXMap()
+{
+    for (auto it = _layers.begin(); it != _layers.end(); it++)
+    {
+        delete *it;
+    }
+}
+
+void TMXMap::setWidth(unsigned int value)
+{
+    _width = value;
+}
+
+void TMXMap::setHeight(unsigned int value)
+{
+    _height = value;
+}
+
+unsigned int TMXMap::getWidth() const
+{
+    return _width;
+}
+
+unsigned int TMXMap::getHeight() const
+{
+    return _height;
+}
+
+void TMXMap::setTileWidth(float value)
+{
+    _tileWidth = value;
+}
+
+void TMXMap::setTileHeight(float value)
+{
+    _tileHeight = value;
+}
+
+float TMXMap::getTileWidth() const
+{
+    return _tileWidth;
+}
+
+float TMXMap::getTileHeight() const
+{
+    return _tileHeight;
+}
+
+bool TMXMap::isValidTileId(unsigned int tileId) const
+{
+    if (tileId != 0)
+    {
+        unsigned int tileSet = findTileSet(tileId);
+        return tileSet < _tileSets.size();
+    }
+
+    return false;
+}
+
+unsigned int TMXMap::findTileSet(unsigned int tileId) const
+{
+    if (tileId == 0)
+    {
+        return (unsigned int)_tileSets.size();
+    }
+
+    for (int i = (int)_tileSets.size() - 1; i >= 0; i--)
+    {
+        if (_tileSets[i].getFirstGid() > tileId)
+        {
+            continue;
+        }
+        return i;
+    }
+
+    return (unsigned int)_tileSets.size();
+}
+
+void TMXMap::addTileSet(const TMXTileSet& tileset)
+{
+    _tileSets.push_back(tileset);
+}
+
+void TMXMap::addLayer(const TMXBaseLayer* layer)
+{
+   _layers.push_back(layer);
+}
+
+unsigned int TMXMap::getTileSetCount() const
+{
+    return (unsigned int)_tileSets.size();
+}
+
+unsigned int TMXMap::getLayerCount() const
+{
+    return (unsigned int)_layers.size();
+}
+
+TMXTileSet& TMXMap::getTileSet(unsigned int index)
+{
+    return _tileSets[index];
+}
+
+const TMXTileSet& TMXMap::getTileSet(unsigned int index) const
+{
+    return _tileSets[index];
+}
+
+const TMXBaseLayer* TMXMap::getLayer(unsigned int index) const
+{
+    return _layers[index];
+}
+
+// TMXTileSet
+TMXTileSet::TMXTileSet()
+    : _path(""),
+    _imgWidth(0), _imgHeight(0), _horzTileCount(0), _vertTileCount(0),
+    _firstGid(0), _maxTileWidth(0), _maxTileHeight(0),
+    _offset(), _spacing(0), _margin(0)
+{
+}
+
+void TMXTileSet::setImagePath(const std::string& path)
+{
+    _path = path;
+}
+
+const std::string& TMXTileSet::getImagePath() const
+{
+    return _path;
+}
+
+void TMXTileSet::setFirstGid(unsigned int gid)
+{
+    _firstGid = gid;
+}
+
+unsigned int TMXTileSet::getFirstGid() const
+{
+    return _firstGid;
+}
+
+void TMXTileSet::setMaxTileWidth(unsigned int value, bool adjustTiles)
+{
+    _maxTileWidth = value;
+    if (adjustTiles && _maxTileWidth != 0)
+    {
+        setImageWidth(_imgWidth);
+    }
+}
+
+unsigned int TMXTileSet::getMaxTileWidth() const
+{
+    return _maxTileWidth;
+}
+
+void TMXTileSet::setMaxTileHeight(unsigned int value, bool adjustTiles)
+{
+    _maxTileHeight = value;
+    if (adjustTiles && _maxTileHeight != 0)
+    {
+        setImageWidth(_imgHeight);
+    }
+}
+
+unsigned int TMXTileSet::getMaxTileHeight() const
+{
+    return _maxTileHeight;
+}
+
+void TMXTileSet::setSpacing(unsigned int value, bool adjustTiles)
+{
+    _spacing = value;
+    if (adjustTiles)
+    {
+        if (_maxTileWidth != 0)
+        {
+            setImageWidth(_imgWidth);
+        }
+        if (_maxTileHeight != 0)
+        {
+            setImageWidth(_imgHeight);
+        }
+    }
+}
+
+unsigned int TMXTileSet::getSpacing() const
+{
+    return _spacing;
+}
+
+void TMXTileSet::setMargin(unsigned int value, bool adjustTiles)
+{
+    _margin = value;
+    if (adjustTiles)
+    {
+        if (_maxTileWidth != 0)
+        {
+            setImageWidth(_imgWidth);
+        }
+        if (_maxTileHeight != 0)
+        {
+            setImageWidth(_imgHeight);
+        }
+    }
+}
+
+unsigned int TMXTileSet::getMargin() const
+{
+    return _margin;
+}
+
+void TMXTileSet::setImageWidth(unsigned int value)
+{
+    _imgWidth = value;
+    _horzTileCount = (value - (_margin * 2) + (_spacing ? _spacing : 0)) / (_maxTileWidth + _spacing);
+}
+
+void TMXTileSet::setImageHeight(unsigned int value)
+{
+    _imgHeight = value;
+    _vertTileCount = (value - (_margin * 2) + (_spacing ? _spacing : 0)) / (_maxTileHeight + _spacing);
+}
+
+unsigned int TMXTileSet::getHorizontalTileCount() const
+{
+    return _horzTileCount;
+}
+
+unsigned int TMXTileSet::getVerticalTileCount() const
+{
+    return _vertTileCount;
+}
+
+void TMXTileSet::setOffset(const Vector2& off)
+{
+    _offset = off;
+}
+
+const Vector2& TMXTileSet::getOffset() const
+{
+    return _offset;
+}
+
+unsigned int TMXTileSet::calculateImageDimension(unsigned int tileCount, unsigned int tileSize, unsigned int spacing, unsigned int margin)
+{
+    return tileCount * (tileSize + spacing) + (margin * 2) - spacing;
+}
+
+Vector2 TMXTileSet::calculateTileOrigin(const Vector2& pos, const Vector2& tileSize, unsigned int spacing, unsigned int margin)
+{
+    float xpos = (tileSize.x + spacing) * pos.x;
+    float ypos = (tileSize.y + spacing) * pos.y;
+
+    return Vector2(xpos + margin, ypos + margin);
+}
+
+// TMXBaseLayer
+TMXBaseLayer::TMXBaseLayer(TMXLayerType type)
+    : _name(""), _type(type), _opacity(1.0f), _visible(true), _properties()
+{
+}
+
+TMXBaseLayer::~TMXBaseLayer()
+{
+}
+
+TMXLayerType TMXBaseLayer::getType() const
+{
+    return _type;
+}
+
+void TMXBaseLayer::setName(const std::string& value)
+{
+    _name = value;
+}
+
+const std::string& TMXBaseLayer::getName() const
+{
+    return _name;
+}
+
+void TMXBaseLayer::setOpacity(float value)
+{
+    _opacity = value;
+}
+
+float TMXBaseLayer::getOpacity() const
+{
+    return _opacity;
+}
+
+void TMXBaseLayer::setVisible(bool value)
+{
+    _visible = value;
+}
+
+bool TMXBaseLayer::getVisible() const
+{
+    return _visible;
+}
+
+TMXProperties& TMXBaseLayer::getProperties()
+{
+    return _properties;
+}
+
+const TMXProperties& TMXBaseLayer::getProperties() const
+{
+    return _properties;
+}
+
+// TMXLayer
+#define FLIPPED_HORIZONTALLY_FLAG 0x80000000
+#define FLIPPED_VERTICALLY_FLAG 0x40000000
+#define FLIPPED_DIAGONALLY_FLAG 0x20000000
+
+TMXLayer::TMXLayer()
+    : TMXBaseLayer(TMXLayerType::NormalLayer),
+    _width(0), _height(0),
+    _tiles()
+{
+}
+
+TMXLayer::~TMXLayer()
+{
+}
+
+void TMXLayer::setWidth(unsigned int value)
+{
+    _width = value;
+}
+
+unsigned int TMXLayer::getWidth() const
+{
+    return _width;
+}
+
+void TMXLayer::setHeight(unsigned int value)
+{
+    _height = value;
+}
+
+unsigned int TMXLayer::getHeight() const
+{
+    return _height;
+}
+
+void TMXLayer::setupTiles()
+{
+    if (_tiles.size() != (_width * _height))
+    {
+        _tiles.resize(_width * _height);
+    }
+}
+
+void TMXLayer::setTile(unsigned int x, unsigned int y, unsigned int gid)
+{
+    struct layer_tile tile = {
+        tile.gid = gid & ~(FLIPPED_HORIZONTALLY_FLAG |
+                       FLIPPED_VERTICALLY_FLAG |
+                       FLIPPED_DIAGONALLY_FLAG),
+        tile.horz_flip = (gid & FLIPPED_HORIZONTALLY_FLAG) != 0,
+        tile.vert_flip = (gid & FLIPPED_VERTICALLY_FLAG) != 0,
+        tile.diag_flip = (gid & FLIPPED_DIAGONALLY_FLAG) != 0,
+    };
+
+    _tiles[x + y * _width] = tile;
+}
+
+const TMXLayer::layer_tile& TMXLayer::getTileStruct(unsigned int x, unsigned int y) const
+{
+    return _tiles[x + y * _width];
+}
+
+unsigned int TMXLayer::getTile(unsigned int x, unsigned int y) const
+{
+    return getTileStruct(x, y).gid;
+}
+
+bool TMXLayer::isEmptyTile(unsigned int x, unsigned int y) const
+{
+    return getTileStruct(x, y).gid == 0;
+}
+
+Vector2 TMXLayer::getTileStart(unsigned int x, unsigned int y, const TMXMap& map, unsigned int resultOnlyForTileset) const
+{
+    // Get the tile
+    unsigned int gid = getTileStruct(x, y).gid;
+    if (gid == 0)
+    {
+        return Vector2(-1, -1);
+    }
+
+    // Get the tileset for the tile
+    unsigned int tileset_id = map.findTileSet(gid);
+    if (tileset_id == map.getTileSetCount() || 
+        (resultOnlyForTileset != TMX_INVALID_ID && tileset_id != resultOnlyForTileset))
+    {
+        return Vector2(-1, -1);
+    }
+    const TMXTileSet& tileset = map.getTileSet(tileset_id);
+    if (tileset.getHorizontalTileCount() == 0)
+    {
+        return Vector2(-1, -1);
+    }
+
+    // Calculate the tile position
+    unsigned int horzTileCount = tileset.getHorizontalTileCount();
+    unsigned int adjusted_gid = gid - tileset.getFirstGid();
+
+    return TMXTileSet::calculateTileOrigin(Vector2(static_cast<float>(adjusted_gid % horzTileCount), static_cast<float>(adjusted_gid / horzTileCount)),
+        Vector2(static_cast<float>(tileset.getMaxTileWidth()), static_cast<float>(tileset.getMaxTileHeight())),
+        tileset.getSpacing(), 
+        tileset.getMargin());
+}
+
+bool TMXLayer::hasTiles() const
+{
+    size_t tile_size = _tiles.size();
+    for (unsigned int i = 0; i < tile_size; i++)
+    {
+        if (_tiles[i].gid != 0)
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+std::set<unsigned int> TMXLayer::getTilesetsUsed(const TMXMap& map) const
+{
+    std::set<unsigned int> tilesets;
+
+    unsigned int tileset_size = map.getTileSetCount();
+    size_t tile_size = _tiles.size();
+    for (unsigned int i = 0; i < tile_size; i++)
+    {
+        unsigned int gid = _tiles[i].gid;
+        if (gid == 0)
+        {
+            // Empty tile
+            continue;
+        }
+        unsigned int tileset = map.findTileSet(gid);
+        if (tileset == tileset_size)
+        {
+            // Could not find tileset
+            continue;
+        }
+        tilesets.insert(tileset);
+        if (tilesets.size() == tileset_size)
+        {
+            // Don't need to continue checking, we have every possible tileset
+            break;
+        }
+    }
+
+    return tilesets;
+}
+
+// TMXImageLayer
+TMXImageLayer::TMXImageLayer()
+    : TMXBaseLayer(TMXLayerType::ImageLayer),
+    _pos(), _path("")
+{
+}
+
+TMXImageLayer::~TMXImageLayer()
+{
+}
+
+void TMXImageLayer::setX(int value)
+{
+    _pos.x = static_cast<float>(value);
+}
+
+int TMXImageLayer::getX() const
+{
+    return static_cast<int>(_pos.x);
+}
+
+void TMXImageLayer::setY(int value)
+{
+    _pos.y = static_cast<float>(value);
+}
+
+int TMXImageLayer::getY() const
+{
+    return static_cast<int>(_pos.y);
+}
+
+const Vector2& TMXImageLayer::getPosition() const
+{
+    return _pos;
+}
+
+void TMXImageLayer::setImagePath(const std::string& path)
+{
+    _path = path;
+}
+
+const std::string& TMXImageLayer::getImagePath() const
+{
+    return _path;
+}

+ 252 - 0
tools/encoder/src/TMXTypes.h

@@ -0,0 +1,252 @@
+#ifndef TMXTYPES_H_
+#define TMXTYPES_H_
+
+#include <vector>
+#include <set>
+#include <map>
+#include <string>
+
+#include "Vector2.h"
+
+namespace gameplay
+{
+
+typedef std::map<std::string, std::string> TMXProperties;
+
+/**
+ * Represents the tiles that make up a map.
+ */
+class TMXTileSet
+{
+public:
+    /**
+     * Constructor.
+     */
+    TMXTileSet();
+
+    void setImagePath(const std::string& path);
+    const std::string& getImagePath() const;
+
+    void setFirstGid(unsigned int gid);
+    unsigned int getFirstGid() const;
+
+    void setMaxTileWidth(unsigned int value, bool adjustTiles = true);
+    unsigned int getMaxTileWidth() const;
+    void setMaxTileHeight(unsigned int value, bool adjustTiles = true);
+    unsigned int getMaxTileHeight() const;
+
+    void setSpacing(unsigned int value, bool adjustTiles = true);
+    unsigned int getSpacing() const;
+    void setMargin(unsigned int value, bool adjustTiles = true);
+    unsigned int getMargin() const;
+
+    // This will calculate the number of tiles based on max tile size, spacing, and margin.
+    // If any of those change, this needs to be recalled.
+    void setImageWidth(unsigned int value);
+    void setImageHeight(unsigned int value);
+    unsigned int getHorizontalTileCount() const;
+    unsigned int getVerticalTileCount() const;
+
+    void setOffset(const Vector2& offset);
+    const Vector2& getOffset() const;
+
+    static unsigned int calculateImageDimension(unsigned int tileCount, unsigned int tileSize, unsigned int spacing = 0, unsigned int margin = 0);
+    static Vector2 calculateTileOrigin(const Vector2& pos, const Vector2& tileSize, unsigned int spacing = 0, unsigned int margin = 0);
+
+private:
+    std::string _path;
+
+    unsigned int _imgWidth;
+    unsigned int _imgHeight;
+    unsigned int _horzTileCount;
+    unsigned int _vertTileCount;
+
+    unsigned int _firstGid;
+    unsigned int _maxTileWidth;
+    unsigned int _maxTileHeight;
+    Vector2 _offset;
+    unsigned int _spacing;
+    unsigned int _margin;
+};
+
+/**
+ * Types of layers
+ */
+enum class TMXLayerType
+{
+    NormalLayer,
+    ImageLayer,
+};
+
+/**
+ * Represents the base type of layer on a map.
+ */
+class TMXBaseLayer
+{
+protected:
+    /**
+     * Constructor.
+     */
+    TMXBaseLayer(TMXLayerType type);
+
+public:
+    /**
+     * Destructor.
+     */
+    virtual ~TMXBaseLayer();
+
+    TMXLayerType getType() const;
+
+    void setName(const std::string& value);
+    const std::string& getName() const;
+
+    void setOpacity(float value);
+    float getOpacity() const;
+
+    void setVisible(bool value);
+    bool getVisible() const;
+
+    TMXProperties& getProperties();
+    const TMXProperties& getProperties() const;
+
+private:
+    std::string _name;
+    TMXLayerType _type;
+    float _opacity;
+    bool _visible;
+    TMXProperties _properties;
+};
+
+#define TMX_INVALID_ID 0xFFFFFFFFu
+
+class TMXMap;
+/**
+ * Represents a single layer on a map.
+ */
+class TMXLayer : public TMXBaseLayer
+{
+public:
+    /**
+     * Constructor.
+     */
+    TMXLayer();
+
+    /**
+     * Destructor.
+     */
+    virtual ~TMXLayer();
+
+    void setWidth(unsigned int value);
+    unsigned int getWidth() const;
+    void setHeight(unsigned int value);
+    unsigned int getHeight() const;
+
+    void setupTiles();
+    void setTile(unsigned int x, unsigned int y, unsigned int gid);
+    unsigned int getTile(unsigned int x, unsigned int y) const;
+    bool isEmptyTile(unsigned int x, unsigned int y) const;
+    Vector2 getTileStart(unsigned int x, unsigned int y, const TMXMap& map, unsigned int resultOnlyForTileset = TMX_INVALID_ID) const;
+
+    bool hasTiles() const;
+    std::set<unsigned int> getTilesetsUsed(const TMXMap& map) const;
+
+private:
+    struct layer_tile
+    {
+        unsigned int gid;
+        bool horz_flip;
+        bool vert_flip;
+        bool diag_flip;
+    };
+
+    const layer_tile& getTileStruct(unsigned int x, unsigned int y) const;
+
+    unsigned int _width;
+    unsigned int _height;
+
+    std::vector<layer_tile> _tiles;
+};
+
+/**
+ * Represents a image layer on a map.
+ */
+class TMXImageLayer : public TMXBaseLayer
+{
+public:
+    /**
+     * Constructor.
+     */
+    TMXImageLayer();
+
+    /**
+     * Destructor.
+     */
+    virtual ~TMXImageLayer();
+
+    void setX(int value);
+    int getX() const;
+    void setY(int value);
+    int getY() const;
+
+    const Vector2& getPosition() const;
+
+    void setImagePath(const std::string& path);
+    const std::string& getImagePath() const;
+
+private:
+    Vector2 _pos;
+    std::string _path;
+};
+
+/**
+ * Represents an entire map of tiles.
+ */
+class TMXMap
+{
+public:
+    /**
+     * Constructor.
+     */
+    TMXMap();
+
+    /**
+     * Destructor.
+     */
+    ~TMXMap();
+
+    void setWidth(unsigned int value);
+    void setHeight(unsigned int value);
+    unsigned int getWidth() const;
+    unsigned int getHeight() const;
+
+    void setTileWidth(float value);
+    void setTileHeight(float value);
+    float getTileWidth() const;
+    float getTileHeight() const;
+
+    bool isValidTileId(unsigned int tileId) const;
+    unsigned int findTileSet(unsigned int tileId) const;
+
+    void addTileSet(const TMXTileSet& tileset);
+    void addLayer(const TMXBaseLayer* layer);
+
+    unsigned int getTileSetCount() const;
+    unsigned int getLayerCount() const;
+
+    TMXTileSet& getTileSet(unsigned int index);
+    const TMXTileSet& getTileSet(unsigned int index) const;
+    const TMXBaseLayer* getLayer(unsigned int index) const;
+
+private:
+    unsigned int _width;
+    unsigned int _height;
+    float _tileWidth;
+    float _tileHeight;
+
+    std::vector<TMXTileSet> _tileSets;
+    std::vector<const TMXBaseLayer*> _layers;
+};
+
+}
+
+#endif

+ 8 - 5
tools/encoder/src/main.cpp

@@ -1,5 +1,6 @@
 #include "Base.h"
 #include "FBXSceneEncoder.h"
+#include "TMXSceneEncoder.h"
 #include "TTFFontEncoder.h"
 #include "GPBDecoder.h"
 #include "EncoderArguments.h"
@@ -99,11 +100,6 @@ int main(int argc, const char** argv)
 
     switch (arguments.getFileFormat())
     {
-    case EncoderArguments::FILEFORMAT_DAE:
-        {
-            LOG(1, "Error: Collada support has been removed. Convert your DAE file to FBX.\n");
-            return -1;
-        }
     case EncoderArguments::FILEFORMAT_FBX:
         {
             std::string realpath(arguments.getFilePath());
@@ -111,7 +107,14 @@ int main(int argc, const char** argv)
             fbxEncoder.write(realpath, arguments);
             break;
         }
+    case EncoderArguments::FILEFORMAT_TMX:
+        {
+            TMXSceneEncoder tmxEncoder;
+            tmxEncoder.write(arguments);
+            break;
+        }
     case EncoderArguments::FILEFORMAT_TTF:
+    case EncoderArguments::FILEFORMAT_OTF:
         {
             std::vector<unsigned int> fontSizes = arguments.getFontSizes();
             

+ 4 - 4
tools/luagen/gameplay-luagen.vcxproj

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|x64">
       <Configuration>Debug</Configuration>
@@ -36,14 +36,14 @@
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>Unicode</CharacterSet>
-    <PlatformToolset>v120</PlatformToolset>
+    <PlatformToolset>v140</PlatformToolset>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
   <ImportGroup Label="ExtensionSettings">
@@ -137,4 +137,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>