Explorar el Código

Undo/redo command for breaking prefabs added
RecordSO command will now record only a single object by default
Reverting prefabs will now call RecordSO so the operation can be reverted

BearishSun hace 10 años
padre
commit
62f5104652

+ 143 - 136
BansheeCore/Include/BsGameObject.h

@@ -1,137 +1,144 @@
-#pragma once
-
-#include "BsCorePrerequisites.h"
-#include "BsIReflectable.h"
-
-namespace BansheeEngine
-{
-	/**
-	 * @brief	Contains instance data that is held by all GameObject handles.
-	 */
-	struct GameObjectInstanceData
-	{
-		GameObjectInstanceData()
-		:mInstanceId(0), object(nullptr)
-		{ }
-
-		std::shared_ptr<GameObject> object;
-		UINT64 mInstanceId;
-	};
-
-	typedef std::shared_ptr<GameObjectInstanceData> GameObjectInstanceDataPtr;
-
-	/**
-	 * @brief	Type of object that can be referenced by a GameObject handle.
-	 *			Each object has an unique ID and is registered with the GameObjectManager.
-	 */
-	class BS_CORE_EXPORT GameObject : public IReflectable
-	{
-	public:
-		GameObject();
-		virtual ~GameObject();
-
-		/**
-		 * @brief	Returns the unique instance ID of the GameObject.
-		 */
-		UINT64 getInstanceId() const { return mInstanceData->mInstanceId; }
-
-		/**
-		 * @brief	Returns an ID that identifies a link between this object and its equivalent
-		 *			in the linked prefab. This will be -1 if the object has no prefab link, or if
-		 *			the object is specific to the instance and has no prefab equivalent.
-		 */
-		UINT32 getLinkId() const { return mLinkId; }
-
-		/**
-		 * @brief	Gets the name of the object.
-		 */
-		const String& getName() const { return mName; }
-
-		/**
-		 * @brief	Sets the name of the object.
-		 */
-		void setName(const String& name) { mName = name; }
-
-		/**
-		 * @brief	Marks the object as destroyed. Generally this means the object
-		 *			has been queued for destruction but it hasn't occurred yet.
-		 *
-		 * @note	Internal method.
-		 */
-		void _setIsDestroyed() { mIsDestroyed = true; }
-
-		/**
-		 * @brief	Checks if the object has been destroyed.
-		 */
-		bool _getIsDestroyed() const { return mIsDestroyed; }
-
-		/**
-		 * @brief	Replaces the instance data with another objects instance data.
-		 *			This object will basically become the original owner of the provided
-		 *			instance data as far as all game object handles referencing it are concerned.
-		 *
-		 * @note	Internal method. 
-		 *			No alive objects should ever be sharing the same instance data. This can be used
-		 *			for restoring dead handles.
-		 */
-		virtual void _setInstanceData(GameObjectInstanceDataPtr& other);
-
-		/**
-		 * @brief	Returns instance data that identifies this GameObject and is used for referencing
-		 *			by game object handles.
-		 *
-		 * @note	Internal method.
-		 */
-		virtual GameObjectInstanceDataPtr _getInstanceData() const { return mInstanceData; }
-
-	protected:
-		friend class GameObjectHandleBase;
-		friend class GameObjectManager;
-		friend class PrefabDiff;
-		friend class PrefabUtility;
-
-		/**
-		 * @brief	Initializes the GameObject after construction.
-		 */
-		void initialize(const std::shared_ptr<GameObject>& object, UINT64 instanceId);
-
-		/**
-		 * @brief	Destroys this object.
-		 *
-		 * @param [in]	handle		Game object handle to this object.
-		 * @param [in]	immediate	If true, the object will be deallocated and become unusable
-		 *							right away. Otherwise the deallocation will be delayed to the end of
-		 *							frame (preferred method).
-		 */
-		virtual void destroyInternal(GameObjectHandleBase& handle, bool immediate = false) = 0;
-
-	protected:
-		String mName;
-		UINT32 mLinkId;
-
-	private:
-		friend class Prefab;
-
-		GameObjectInstanceDataPtr mInstanceData;
-		bool mIsDestroyed;
-
-		/************************************************************************/
-		/* 								RTTI		                     		*/
-		/************************************************************************/
-
-	public:
-		friend class GameObjectRTTI;
-		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
-	};
-}
-
-#include "BsGameObjectHandle.h"
-
-namespace BansheeEngine
-{
-	// Game object handles
-	typedef GameObjectHandle<GameObject> HGameObject;
-	typedef GameObjectHandle<SceneObject> HSceneObject;
-	typedef GameObjectHandle<Component> HComponent;
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsIReflectable.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Contains instance data that is held by all GameObject handles.
+	 */
+	struct GameObjectInstanceData
+	{
+		GameObjectInstanceData()
+		:mInstanceId(0), object(nullptr)
+		{ }
+
+		std::shared_ptr<GameObject> object;
+		UINT64 mInstanceId;
+	};
+
+	typedef std::shared_ptr<GameObjectInstanceData> GameObjectInstanceDataPtr;
+
+	/**
+	 * @brief	Type of object that can be referenced by a GameObject handle.
+	 *			Each object has an unique ID and is registered with the GameObjectManager.
+	 */
+	class BS_CORE_EXPORT GameObject : public IReflectable
+	{
+	public:
+		GameObject();
+		virtual ~GameObject();
+
+		/**
+		 * @brief	Returns the unique instance ID of the GameObject.
+		 */
+		UINT64 getInstanceId() const { return mInstanceData->mInstanceId; }
+
+		/**
+		 * @brief	Returns an ID that identifies a link between this object and its equivalent
+		 *			in the linked prefab. This will be -1 if the object has no prefab link, or if
+		 *			the object is specific to the instance and has no prefab equivalent.
+		 */
+		UINT32 getLinkId() const { return mLinkId; }
+
+		/**
+		 * @brief	Gets the name of the object.
+		 */
+		const String& getName() const { return mName; }
+
+		/**
+		 * @brief	Sets the name of the object.
+		 */
+		void setName(const String& name) { mName = name; }
+
+		/**
+		 * @brief	Marks the object as destroyed. Generally this means the object
+		 *			has been queued for destruction but it hasn't occurred yet.
+		 *
+		 * @note	Internal method.
+		 */
+		void _setIsDestroyed() { mIsDestroyed = true; }
+
+		/**
+		 * @brief	Checks if the object has been destroyed.
+		 */
+		bool _getIsDestroyed() const { return mIsDestroyed; }
+
+		/**
+		 * @brief	Changes the prefab link ID for this object. See ::getLinkId.
+		 *
+		 * @note	Internal method.
+		 */
+		void _setLinkId(UINT32 id) { mLinkId = id; }
+
+		/**
+		 * @brief	Replaces the instance data with another objects instance data.
+		 *			This object will basically become the original owner of the provided
+		 *			instance data as far as all game object handles referencing it are concerned.
+		 *
+		 * @note	Internal method. 
+		 *			No alive objects should ever be sharing the same instance data. This can be used
+		 *			for restoring dead handles.
+		 */
+		virtual void _setInstanceData(GameObjectInstanceDataPtr& other);
+
+		/**
+		 * @brief	Returns instance data that identifies this GameObject and is used for referencing
+		 *			by game object handles.
+		 *
+		 * @note	Internal method.
+		 */
+		virtual GameObjectInstanceDataPtr _getInstanceData() const { return mInstanceData; }
+
+	protected:
+		friend class GameObjectHandleBase;
+		friend class GameObjectManager;
+		friend class PrefabDiff;
+		friend class PrefabUtility;
+
+		/**
+		 * @brief	Initializes the GameObject after construction.
+		 */
+		void initialize(const std::shared_ptr<GameObject>& object, UINT64 instanceId);
+
+		/**
+		 * @brief	Destroys this object.
+		 *
+		 * @param [in]	handle		Game object handle to this object.
+		 * @param [in]	immediate	If true, the object will be deallocated and become unusable
+		 *							right away. Otherwise the deallocation will be delayed to the end of
+		 *							frame (preferred method).
+		 */
+		virtual void destroyInternal(GameObjectHandleBase& handle, bool immediate = false) = 0;
+
+	protected:
+		String mName;
+		UINT32 mLinkId;
+
+	private:
+		friend class Prefab;
+
+		GameObjectInstanceDataPtr mInstanceData;
+		bool mIsDestroyed;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+
+	public:
+		friend class GameObjectRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const override;
+	};
+}
+
+#include "BsGameObjectHandle.h"
+
+namespace BansheeEngine
+{
+	// Game object handles
+	typedef GameObjectHandle<GameObject> HGameObject;
+	typedef GameObjectHandle<SceneObject> HSceneObject;
+	typedef GameObjectHandle<Component> HComponent;
 }

+ 33 - 0
BansheeCore/Include/BsSceneObject.h

@@ -107,6 +107,39 @@ namespace BansheeEngine
 		 * @note	Internal method.
 		 */
 		void _clearPrefabDiff() { mPrefabDiff = nullptr; }
+
+		/**
+		 * @brief	Returns the UUID of the prefab this object is linked to, if any. Unlike ::getPrefabLink method this
+		 *			will not search parents, but instead return only the value assigned to this object.
+		 *
+		 * @note	Internal method.
+		 */
+		const String& _getPrefabLinkUUID() const { return mPrefabLinkUUID; }
+
+		/**
+		 * @brief	Allows you to change the prefab link UUID of this object. Normally this should be accompanied by
+		 *			reassigning the link IDs.
+		 *
+		 * @note	Internal method.
+		 */
+		void _setPrefabLinkUUID(const String& UUID) { mPrefabLinkUUID = UUID; }
+
+		/**
+		 * @brief	Returns a prefab diff object containing instance specific modifications of this object compared to its
+		 *			prefab reference, if any.
+		 *
+		 * @note	Internal method.
+		 */
+		const PrefabDiffPtr& _getPrefabDiff() const { return mPrefabDiff; }
+
+		/**
+		 * @brief	Assigns a new prefab diff object. Caller must ensure the prefab diff was generated for this object.
+		 *			See ::PrefabDiff.
+		 *
+		 * @note	Internal method.
+		 */
+		void _setPrefabDiff(const PrefabDiffPtr& diff) { mPrefabDiff = diff; }
+
 	private:
 		SceneObject(const String& name, UINT32 flags);
 

+ 1 - 1
BansheeCore/Source/BsSceneObject.cpp

@@ -136,7 +136,7 @@ namespace BansheeEngine
 	{
 		SceneObject* rootObj = this;
 
-		while (rootObj == nullptr)
+		while (rootObj != nullptr)
 		{
 			if (!rootObj->mPrefabLinkUUID.empty())
 				return;

+ 1 - 1
BansheeD3D9RenderAPI/BansheeD3D9RenderAPI.vcxproj

@@ -290,7 +290,6 @@
     <ClInclude Include="Include\BsD3D9VideoModeInfo.h" />
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="BsD3D9Plugin.cpp" />
     <ClCompile Include="Source\BsD3D9Device.cpp" />
     <ClCompile Include="Source\BsD3D9DeviceManager.cpp" />
     <ClCompile Include="Source\BsD3D9Driver.cpp" />
@@ -303,6 +302,7 @@
     <ClCompile Include="Source\BsD3D9IndexBuffer.cpp" />
     <ClCompile Include="Source\BsD3D9OcclusionQuery.cpp" />
     <ClCompile Include="Source\BsD3D9PixelBuffer.cpp" />
+    <ClCompile Include="Source\BsD3D9Plugin.cpp" />
     <ClCompile Include="Source\BsD3D9QueryManager.cpp" />
     <ClCompile Include="Source\BsD3D9TimerQuery.cpp" />
     <ClCompile Include="Source\BsD3D9VertexBuffer.cpp" />

+ 203 - 203
BansheeD3D9RenderAPI/BansheeD3D9RenderAPI.vcxproj.filters

@@ -1,204 +1,204 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <Filter Include="Source Files">
-      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
-      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
-    </Filter>
-    <Filter Include="Header Files">
-      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
-      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
-    </Filter>
-    <Filter Include="Resource Files">
-      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
-      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
-    </Filter>
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include="Include\BsD3D9VideoModeInfo.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9VertexDeclaration.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9VertexBuffer.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9TimerQuery.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9TextureManager.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9Texture.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9ResourceManager.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9Resource.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9RenderWindowManager.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9RenderWindow.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9RenderTexture.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9QueryManager.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9Prerequisites.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9PixelBuffer.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9OcclusionQuery.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9MultiRenderTexture.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9Mappings.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9IndexBuffer.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9HLSLProgramFactory.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9HLSLParamParser.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9HardwareBufferManager.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9GpuProgram.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9GpuBuffer.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9EventQuery.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9DriverList.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9Driver.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9DeviceManager.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9Device.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9EmulatedParamBlocks.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9RenderAPI.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-    <ClInclude Include="Include\BsD3D9RenderAPIFactory.h">
-      <Filter>Header Files</Filter>
-    </ClInclude>
-  </ItemGroup>
-  <ItemGroup>
-    <ClCompile Include="Source\BsD3D9VideoModeInfo.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9VertexDeclaration.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9VertexBuffer.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9TimerQuery.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9TextureManager.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9Texture.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9ResourceManager.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9Resource.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9RenderWindowManager.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9RenderWindow.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9RenderTexture.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9QueryManager.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="BsD3D9Plugin.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9PixelBuffer.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9OcclusionQuery.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9MultiRenderTexture.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9Mappings.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9IndexBuffer.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9HLSLProgramFactory.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9HardwareBufferManager.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9GpuProgram.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9GpuBuffer.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9EventQuery.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9DriverList.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9Driver.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9DeviceManager.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9Device.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9EmulatedParamBlocks.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9RenderAPI.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-    <ClCompile Include="Source\BsD3D9RenderAPIFactory.cpp">
-      <Filter>Source Files</Filter>
-    </ClCompile>
-  </ItemGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="Include\BsD3D9VideoModeInfo.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9VertexDeclaration.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9VertexBuffer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9TimerQuery.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9TextureManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9Texture.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9ResourceManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9Resource.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9RenderWindowManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9RenderWindow.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9RenderTexture.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9QueryManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9Prerequisites.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9PixelBuffer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9OcclusionQuery.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9MultiRenderTexture.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9Mappings.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9IndexBuffer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9HLSLProgramFactory.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9HLSLParamParser.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9HardwareBufferManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9GpuProgram.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9GpuBuffer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9EventQuery.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9DriverList.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9Driver.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9DeviceManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9Device.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9EmulatedParamBlocks.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9RenderAPI.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsD3D9RenderAPIFactory.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="Source\BsD3D9VideoModeInfo.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9VertexDeclaration.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9VertexBuffer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9TimerQuery.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9TextureManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9Texture.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9ResourceManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9Resource.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9RenderWindowManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9RenderWindow.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9RenderTexture.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9QueryManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9PixelBuffer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9OcclusionQuery.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9MultiRenderTexture.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9Mappings.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9IndexBuffer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9HLSLProgramFactory.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9HardwareBufferManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9GpuProgram.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9GpuBuffer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9EventQuery.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9DriverList.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9Driver.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9DeviceManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9Device.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9EmulatedParamBlocks.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9RenderAPI.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9RenderAPIFactory.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsD3D9Plugin.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
 </Project>

+ 9 - 9
BansheeD3D9RenderAPI/BsD3D9Plugin.cpp → BansheeD3D9RenderAPI/Source/BsD3D9Plugin.cpp

@@ -1,10 +1,10 @@
-#include "BsD3D9Prerequisites.h"
-#include "BsD3D9RenderAPIFactory.h"
-
-namespace BansheeEngine
-{
-	extern "C" BS_D3D9_EXPORT const String& getPluginName()
-	{
-		return SystemName;
-	}
+#include "BsD3D9Prerequisites.h"
+#include "BsD3D9RenderAPIFactory.h"
+
+namespace BansheeEngine
+{
+	extern "C" BS_D3D9_EXPORT const String& getPluginName()
+	{
+		return SystemName;
+	}
 }

+ 2 - 0
BansheeEditor/BansheeEditor.vcxproj

@@ -285,6 +285,7 @@
   <ItemGroup>
     <ClInclude Include="Include\BsBuildDataRTTI.h" />
     <ClInclude Include="Include\BsBuildManager.h" />
+    <ClInclude Include="Include\BsCmdBreakPrefab.h" />
     <ClInclude Include="Include\BsCmdCloneSO.h" />
     <ClInclude Include="Include\BsCmdCreateSO.h" />
     <ClInclude Include="Include\BsCmdDeleteSO.h" />
@@ -373,6 +374,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsBuildManager.cpp" />
+    <ClCompile Include="Source\BsCmdBreakPrefab.cpp" />
     <ClCompile Include="Source\BsCmdCloneSO.cpp" />
     <ClCompile Include="Source\BsCmdCreateSO.cpp" />
     <ClCompile Include="Source\BsCmdInstantiateSO.cpp" />

+ 6 - 0
BansheeEditor/BansheeEditor.vcxproj.filters

@@ -291,6 +291,9 @@
     <ClInclude Include="Include\BsGUIWindowFrameWidgetRTTI.h">
       <Filter>Header Files\RTTI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsCmdBreakPrefab.h">
+      <Filter>Header Files\Commands</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsEditorCommand.cpp">
@@ -518,5 +521,8 @@
     <ClCompile Include="Source\BsGUISliderField.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsCmdBreakPrefab.cpp">
+      <Filter>Source Files\Commands</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 54 - 0
BansheeEditor/Include/BsCmdBreakPrefab.h

@@ -0,0 +1,54 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsEditorCommand.h"
+#include "BsUndoRedo.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	A command used for undo/redo purposes. It breaks a prefab link of a scene object
+	 *			and allows you to restore link.
+	 */
+	class BS_ED_EXPORT CmdBreakPrefab : public EditorCommand
+	{
+	public:
+		~CmdBreakPrefab();
+
+		/**
+		 * @brief	Creates and executes the command on the provided scene object.
+		 *			Automatically registers the command with undo/redo system.
+		 *
+		 * @param	sceneObject		Scene object whose prefab link to break.
+		 * @param	description		Optional description of what exactly the command does.
+		 */
+		static void execute(const HSceneObject& sceneObject, const WString& description = StringUtil::WBLANK);
+
+		/**
+		 * @copydoc	EditorCommand::commit
+		 */
+		void commit() override;
+
+		/**
+		 * @copydoc	EditorCommand::revert
+		 */
+		void revert() override;
+
+	private:
+		friend class UndoRedo;
+
+		CmdBreakPrefab(const WString& description, const HSceneObject& sceneObject);
+
+		/**
+		 * @brief	Clears all internal cached data. Should be called whenever a change is commited.
+		 */
+		void clear();
+
+		HSceneObject mSceneObject;
+
+		HSceneObject mPrefabRoot;
+		String mPrefabLinkUUID;
+		PrefabDiffPtr mPrefabDiff;
+		UnorderedMap<UINT64, UINT32> mLinkIds;
+	};
+}

+ 7 - 4
BansheeEditor/Include/BsCmdRecordSO.h

@@ -21,10 +21,12 @@ namespace BansheeEngine
 		 * @brief	Creates and executes the command on the provided scene object.
 		 *			Automatically registers the command with undo/redo system.
 		 *
-		 * @param	sceneObject	Scene object to record.
-		 * @param	description	Optional description of what exactly the command does.
+		 * @param	sceneObject		Scene object to record.
+		 * @param	recordHierarchy	If true, all children of the provided scene object will be recorded as well.
+		 * @param	description		Optional description of what exactly the command does.
 		 */
-		static void execute(const HSceneObject& sceneObject, const WString& description = StringUtil::WBLANK);
+		static void execute(const HSceneObject& sceneObject, bool recordHierarchy = false, 
+			const WString& description = StringUtil::WBLANK);
 
 		/**
 		 * @copydoc	EditorCommand::commit
@@ -39,7 +41,7 @@ namespace BansheeEngine
 	private:
 		friend class UndoRedo;
 
-		CmdRecordSO(const WString& description, const HSceneObject& sceneObject);
+		CmdRecordSO(const WString& description, const HSceneObject& sceneObject, bool recordHierarchy);
 
 		/**
 		 * @brief	Saves the state of the specified object, all of its children
@@ -55,6 +57,7 @@ namespace BansheeEngine
 
 		HSceneObject mSceneObject;
 		CmdUtility::SceneObjProxy mSceneObjectProxy;
+		bool mRecordHierarchy;
 
 		UINT8* mSerializedObject;
 		UINT32 mSerializedObjectSize;

+ 135 - 0
BansheeEditor/Source/BsCmdBreakPrefab.cpp

@@ -0,0 +1,135 @@
+#include "BsCmdBreakPrefab.h"
+#include "BsSceneObject.h"
+
+namespace BansheeEngine
+{
+	CmdBreakPrefab::CmdBreakPrefab(const WString& description, const HSceneObject& sceneObject)
+		:EditorCommand(description), mSceneObject(sceneObject)
+	{
+
+	}
+
+	CmdBreakPrefab::~CmdBreakPrefab()
+	{
+		mSceneObject = nullptr;
+
+	}
+	
+	void CmdBreakPrefab::execute(const HSceneObject& sceneObject, const WString& description)
+	{
+		// Register command and commit it
+		CmdBreakPrefab* command = new (bs_alloc<CmdBreakPrefab>()) CmdBreakPrefab(description, sceneObject);
+		UndoRedo::instance().registerCommand(command);
+		command->commit();
+	}
+
+	void CmdBreakPrefab::commit()
+	{
+		clear();
+
+		if (mSceneObject == nullptr || mSceneObject.isDestroyed())
+			return;
+
+		HSceneObject rootObj = mSceneObject;
+
+		while (rootObj != nullptr)
+		{
+			if (!rootObj->_getPrefabLinkUUID().empty())
+				return;
+
+			if (rootObj->getParent() != nullptr)
+				rootObj = rootObj->getParent();
+			else
+				rootObj = nullptr;
+		}
+
+		if (rootObj != nullptr)
+		{
+			mPrefabRoot = rootObj;
+			mPrefabLinkUUID = rootObj->_getPrefabLinkUUID();
+			mPrefabDiff = rootObj->_getPrefabDiff();
+
+			Stack<HSceneObject> todo;
+			todo.push(mPrefabRoot);
+
+			while (!todo.empty())
+			{
+				HSceneObject currentSO = todo.top();
+				todo.pop();
+
+				const Vector<HComponent>& components = currentSO->getComponents();
+				for (auto& component : components)
+				{
+					UINT32 linkId = component->getLinkId();
+
+					if (linkId != (UINT32)-1)
+						mLinkIds[component->getInstanceId()] = linkId;
+
+					mLinkIds[component->getInstanceId()] = component->getLinkId();
+				}
+
+				UINT32 numChildren = (UINT32)currentSO->getNumChildren();
+				for (UINT32 i = 0; i < numChildren; i++)
+				{
+					HSceneObject child = currentSO->getChild(i);
+					UINT32 linkId = child->getLinkId();
+
+					if (linkId != (UINT32)-1)
+						mLinkIds[child->getInstanceId()] = linkId;
+
+					if (child->_getPrefabLinkUUID().empty())
+						todo.push(child);
+				}
+			}
+		}
+
+		mSceneObject->breakPrefabLink();
+	}
+
+	void CmdBreakPrefab::revert()
+	{
+		if (mPrefabRoot == nullptr || mPrefabRoot.isDestroyed())
+			return;
+
+		mPrefabRoot->_setPrefabLinkUUID(mPrefabLinkUUID);
+		mPrefabRoot->_setPrefabDiff(mPrefabDiff);
+
+		Stack<HSceneObject> todo;
+		todo.push(mPrefabRoot);
+
+		while (!todo.empty())
+		{
+			HSceneObject currentSO = todo.top();
+			todo.pop();
+
+			const Vector<HComponent>& components = currentSO->getComponents();
+			for (auto& component : components)
+			{
+				auto iterFind = mLinkIds.find(component->getInstanceId());
+				if (iterFind != mLinkIds.end())
+					component->_setLinkId(iterFind->second);
+			}
+
+			UINT32 numChildren = (UINT32)currentSO->getNumChildren();
+			for (UINT32 i = 0; i < numChildren; i++)
+			{
+				HSceneObject child = currentSO->getChild(i);
+
+				auto iterFind = mLinkIds.find(child->getInstanceId());
+				if (iterFind != mLinkIds.end())
+					child->_setLinkId(iterFind->second);
+
+				if (child->_getPrefabLinkUUID().empty())
+					todo.push(child);
+			}
+		}
+	}
+
+	void CmdBreakPrefab::clear()
+	{
+		mPrefabRoot = nullptr;
+		mPrefabLinkUUID = "";
+		mPrefabDiff = nullptr;
+		mLinkIds.clear();
+	}
+}

+ 40 - 20
BansheeEditor/Source/BsCmdRecordSO.cpp

@@ -5,8 +5,9 @@
 
 namespace BansheeEngine
 {
-	CmdRecordSO::CmdRecordSO(const WString& description, const HSceneObject& sceneObject)
-		:EditorCommand(description), mSceneObject(sceneObject), mSerializedObject(nullptr), mSerializedObjectSize(0)
+	CmdRecordSO::CmdRecordSO(const WString& description, const HSceneObject& sceneObject, bool recordHierarchy)
+		:EditorCommand(description), mSceneObject(sceneObject), mSerializedObject(nullptr), mSerializedObjectSize(0), 
+		mRecordHierarchy(recordHierarchy)
 	{
 
 	}
@@ -28,10 +29,10 @@ namespace BansheeEngine
 		}
 	}
 
-	void CmdRecordSO::execute(const HSceneObject& sceneObject, const WString& description)
+	void CmdRecordSO::execute(const HSceneObject& sceneObject, bool recordHierarchy, const WString& description)
 	{
 		// Register command and commit it
-		CmdRecordSO* command = new (bs_alloc<CmdRecordSO>()) CmdRecordSO(description, sceneObject);
+		CmdRecordSO* command = new (bs_alloc<CmdRecordSO>()) CmdRecordSO(description, sceneObject, recordHierarchy);
 		UndoRedo::instance().registerCommand(command);
 		command->commit();
 	}
@@ -54,13 +55,17 @@ namespace BansheeEngine
 		HSceneObject parent = mSceneObject->getParent();
 
 		UINT32 numChildren = mSceneObject->getNumChildren();
-		Vector<HSceneObject> children(numChildren);
-		for (UINT32 i = 0; i < numChildren; i++)
+		HSceneObject* children = nullptr;
+		if (!mRecordHierarchy)
 		{
-			HSceneObject child = mSceneObject->getChild(i);
-			children[i] = child;
-
-			child->setParent(HSceneObject());
+			children = bs_stack_new<HSceneObject>(numChildren);
+			for (UINT32 i = 0; i < numChildren; i++)
+			{
+				HSceneObject child = mSceneObject->getChild(i);
+				children[i] = child;
+
+				child->setParent(HSceneObject());
+			}
 		}
 
 		mSceneObject->destroy(true);
@@ -73,20 +78,30 @@ namespace BansheeEngine
 		CmdUtility::restoreIds(restored->getHandle(), mSceneObjectProxy);
 		restored->setParent(parent);
 
-		for (auto& child : children)
-			child->setParent(restored->getHandle());
+		if (!mRecordHierarchy)
+		{
+			for (UINT32 i = 0; i < numChildren; i++)
+				children[i]->setParent(restored->getHandle());
+
+			bs_stack_delete(children, numChildren);
+		}
 	}
 
 	void CmdRecordSO::recordSO(const HSceneObject& sceneObject)
 	{
 		UINT32 numChildren = mSceneObject->getNumChildren();
-		Vector<HSceneObject> children(numChildren);
-		for (UINT32 i = 0; i < numChildren; i++)
-		{
-			HSceneObject child = mSceneObject->getChild(i);
-			children[i] = child;
+		HSceneObject* children = nullptr;
 
-			child->setParent(HSceneObject());
+		if (!mRecordHierarchy)
+		{
+			children = bs_stack_new<HSceneObject>(numChildren);
+			for (UINT32 i = 0; i < numChildren; i++)
+			{
+				HSceneObject child = mSceneObject->getChild(i);
+				children[i] = child;
+
+				child->setParent(HSceneObject());
+			}
 		}
 
 		MemorySerializer serializer;
@@ -94,7 +109,12 @@ namespace BansheeEngine
 
 		mSceneObjectProxy = CmdUtility::createProxy(mSceneObject);
 
-		for (auto& child : children)
-			child->setParent(sceneObject->getHandle());
+		if (!mRecordHierarchy)
+		{
+			for (UINT32 i = 0; i < numChildren; i++)
+				children[i]->setParent(sceneObject->getHandle());
+
+			bs_stack_delete(children, numChildren);
+		}
 	}
 }

+ 1 - 1
BansheeEditor/Source/BsGUISceneTreeView.cpp

@@ -239,7 +239,7 @@ namespace BansheeEngine
 		SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(element);
 
 		HSceneObject so = sceneTreeElement->mSceneObject;
-		CmdRecordSO::execute(so, L"Renamed \"" + toWString(so->getName()) + L"\"");
+		CmdRecordSO::execute(so, false, L"Renamed \"" + toWString(so->getName()) + L"\"");
 		so->setName(toString(name));
 
 		onModified();

+ 3 - 10
BansheeEngine.sln

@@ -5,8 +5,9 @@ VisualStudioVersion = 14.0.24720.0
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Notes", "_Notes", "{1D081E5A-615A-4C06-B2DF-0D8D9390DE02}"
 	ProjectSection(SolutionItems) = preProject
-		TODO.txt = TODO.txt
-		TODOExperimentation.txt = TODOExperimentation.txt
+		Documentation\CompilingDependenciesManually.txt = Documentation\CompilingDependenciesManually.txt
+		Documentation\Mono-3.8.0-IntegrationGuide.txt = Documentation\Mono-3.8.0-IntegrationGuide.txt
+		Documentation\NVTTCompilationGuide.txt = Documentation\NVTTCompilationGuide.txt
 	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BansheeEngine", "BansheeEngine\BansheeEngine.vcxproj", "{07B0C186-5173-46F2-BE26-7E4148BD0CCA}"
@@ -131,13 +132,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BansheeFBXImporter", "Bansh
 		{CC7F9445-71C9-4559-9976-FF0A64DCB582} = {CC7F9445-71C9-4559-9976-FF0A64DCB582}
 	EndProjectSection
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Guides", "Guides", "{4259680D-8A9B-4C17-B75B-CA29482AB299}"
-	ProjectSection(SolutionItems) = preProject
-		Documentation\CompilingDependenciesManually.txt = Documentation\CompilingDependenciesManually.txt
-		Documentation\Mono-3.8.0-IntegrationGuide.txt = Documentation\Mono-3.8.0-IntegrationGuide.txt
-		Documentation\NVTTCompilationGuide.txt = Documentation\NVTTCompilationGuide.txt
-	EndProjectSection
-EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BansheeSL", "BansheeSL\BansheeSL.vcxproj", "{2BA791F1-87F6-4863-A784-D07FF605AC5E}"
 	ProjectSection(ProjectDependencies) = postProject
 		{9B21D41C-516B-43BF-9B10-E99B599C7589} = {9B21D41C-516B-43BF-9B10-E99B599C7589}
@@ -668,7 +662,6 @@ Global
 		{122B7A22-0C62-4B35-B661-EBF3F394EA79} = {32E4E2B7-1B4D-4B06-AD87-57CEE00BC247}
 		{AB6C9284-D1CB-4AAD-BA4B-8A9E81AD1A73} = {32E4E2B7-1B4D-4B06-AD87-57CEE00BC247}
 		{7F449698-73DF-4203-9F31-0877DBF01695} = {32E4E2B7-1B4D-4B06-AD87-57CEE00BC247}
-		{4259680D-8A9B-4C17-B75B-CA29482AB299} = {1D081E5A-615A-4C06-B2DF-0D8D9390DE02}
 		{2BA791F1-87F6-4863-A784-D07FF605AC5E} = {32E4E2B7-1B4D-4B06-AD87-57CEE00BC247}
 		{08975177-4A13-4EE7-BB21-3BB92FB3F3CC} = {32E4E2B7-1B4D-4B06-AD87-57CEE00BC247}
 		{1437BB4E-DDB3-4307-AA41-8C035DA3014B} = {32E4E2B7-1B4D-4B06-AD87-57CEE00BC247}

+ 148 - 148
BansheeFBXImporter/Include/BsFBXImporter.h

@@ -1,149 +1,149 @@
-#pragma once
-
-#include "BsFBXPrerequisites.h"
-#include "BsSpecificImporter.h"
-#include "BsSubMesh.h"
-#include "BsFBXImportData.h"
-
-#define FBX_IMPORT_MAX_UV_LAYERS 2
-
-namespace BansheeEngine
-{
-	/**
-	 * @brief	Importer implementation that handles FBX/OBJ/DAE/3DS file import 
-	 *			by using the FBX SDK.
-	 */
-	class BS_FBX_EXPORT FBXImporter : public SpecificImporter
-	{
-	public:
-		FBXImporter();
-		virtual ~FBXImporter();
-
-		/**
-		 * @copydoc	SpecificImporter::isExtensionSupported
-		 */
-		virtual bool isExtensionSupported(const WString& ext) const override;
-
-		/**
-		 * @copydoc	SpecificImporter::isMagicNumberSupported
-		 */
-		virtual bool isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const override;
-
-		/**
-		 * @copydoc	SpecificImporter::import
-		 */
-		virtual ResourcePtr import(const Path& filePath, ConstImportOptionsPtr importOptions) override;
-
-		/**
-		 * @copydoc	SpecificImporter::createImportOptions
-		 */
-		ImportOptionsPtr createImportOptions() const override;
-	private:
-		/**
-		 * @brief	Starts up FBX SDK. Must be called before any other operations.
-		 *			Outputs an FBX manager and FBX scene instances you should use in
-		 *			further operations.
-		 */
-		bool startUpSdk(FbxScene*& scene);
-
-		/**
-		 * @brief	Shuts down FBX SDK. Must be called after any other operations.
-		 */
-		void shutDownSdk();
-
-		/**
-		 * @brief	Loads the data from the file at the provided path into the provided FBX scene. 
-		 *			Throws exception if data cannot be loaded.
-		 */
-		bool loadFBXFile(FbxScene* scene, const Path& filePath);
-
-		/**
-		 * @brief	Parses an FBX scene. Find all meshes in the scene and returns mesh data object
-		 *			containing all vertices, indexes and other mesh information. Also outputs
-		 *			a sub-mesh array that allows you locate specific sub-meshes within the returned
-		 *			mesh data object. If requested animation and blend shape data is output as well.
-		 */
-		void parseScene(FbxScene* scene, const FBXImportOptions& options, FBXImportScene& outputScene);
-
-		/**
-		 * @brief	Parses an FBX mesh. Converts it from FBX SDK format into a mesh data object containing
-		 *			one or multiple sub-meshes.
-		 */
-		void parseMesh(FbxMesh* mesh, FBXImportNode* parentNode, const FBXImportOptions& options, FBXImportScene& outputScene);
-
-		/**
-		 * @brief	Imports blend shapes for all the meshes that are part of the scene.
-		 */
-		void importBlendShapes(FBXImportScene& scene, const FBXImportOptions& options);
-
-		/**
-		 * @brief	Parses a single FBX blend shape frame. Converts it from FBX SDK format into a 
-		 *			shape data object containing position and tangent frame.
-		 */
-		void importBlendShapeFrame(FbxShape* shape, const FBXImportMesh& mesh, const FBXImportOptions& options, FBXBlendShapeFrame& outFrame);
-
-		/**
-		 * @brief	Imports skinning information and bones for all meshes.
-		 */
-		void importSkin(FBXImportScene& scene);
-
-		/**
-		 * @brief	Imports skinning information and bones for the specified mesh.
-		 */
-		void importSkin(FBXImportScene& scene, FbxSkin* skin, FBXImportMesh& mesh);
-
-		/**
-		 * @brief	Imports all bone and blend shape animations from the FBX.
-		 */
-		void importAnimations(FbxScene* scene, FBXImportOptions& importOptions, FBXImportScene& importScene);
-
-		/**
-		 * @brief	Imports all animations for the specified animation layer and outputs them in the provided clip.
-		 *			Child nodes will be iterated recursively.
-		 */
-		void importAnimations(FbxAnimLayer* layer, FbxNode* node, FBXImportOptions& importOptions, 
-			FBXAnimationClip& clip, FBXImportScene& importScene);
-
-		/**
-		 * @brief	Converts a single FBX animation curve into an engine curve format, resampling it if
-		 *			necessary.
-		 */
-		void importCurve(FbxAnimCurve* fbxCurve, FBXImportOptions& importOptions, FBXAnimationCurve& curve, float start, float end);
-
-		/**
-		 * @brief	Converts a set of curves containing rotation in euler angles into a set of curves using
-		 *			quaternion rotation.
-		 */
-		void eulerToQuaternionCurves(FBXAnimationCurve(&eulerCurves)[3], FBXAnimationCurve(&quatCurves)[4]);
-
-		/**
-		 * @brief	Converts all the meshes from per-index attributes to per-vertex attributes.
-		 *
-		 * @note	This method will replace all meshes in the scene with new ones, and delete old ones
-		 *			so be sure not to keep any mesh references.
-		 */
-		void splitMeshVertices(FBXImportScene& scene);
-
-		/**
-		 * @brief	Traverses over all meshes in the scene and generates normals, tangents and bitangents if they're missing.
-		 *
-		 * @note	This assumes vertices have already been split and shouldn't be called on pre-split meshes.
-		 */
-		void generateMissingTangentSpace(FBXImportScene& scene, const FBXImportOptions& options);
-
-		/**
-		 * @brief	Converts the mesh data from the imported FBX scene into mesh data that can be used
-		 *			for initializing a mesh.
-		 */
-		RendererMeshDataPtr generateMeshData(const FBXImportScene& scene, const FBXImportOptions& options, Vector<SubMesh>& subMeshes);
-
-		/**
-		 * @brief	Creates an internal representation of an FBX node from an FbxNode object.
-		 */
-		FBXImportNode* createImportNode(FBXImportScene& scene, FbxNode* fbxNode, FBXImportNode* parent);
-
-	private:
-		Vector<WString> mExtensions;
-		FbxManager* mFBXManager;
-	};
+#pragma once
+
+#include "BsFBXPrerequisites.h"
+#include "BsSpecificImporter.h"
+#include "BsSubMesh.h"
+#include "BsFBXImportData.h"
+
+#define FBX_IMPORT_MAX_UV_LAYERS 2
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Importer implementation that handles FBX/OBJ/DAE/3DS file import 
+	 *			by using the FBX SDK.
+	 */
+	class BS_FBX_EXPORT FBXImporter : public SpecificImporter
+	{
+	public:
+		FBXImporter();
+		virtual ~FBXImporter();
+
+		/**
+		 * @copydoc	SpecificImporter::isExtensionSupported
+		 */
+		virtual bool isExtensionSupported(const WString& ext) const override;
+
+		/**
+		 * @copydoc	SpecificImporter::isMagicNumberSupported
+		 */
+		virtual bool isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const override;
+
+		/**
+		 * @copydoc	SpecificImporter::import
+		 */
+		virtual ResourcePtr import(const Path& filePath, ConstImportOptionsPtr importOptions) override;
+
+		/**
+		 * @copydoc	SpecificImporter::createImportOptions
+		 */
+		ImportOptionsPtr createImportOptions() const override;
+	private:
+		/**
+		 * @brief	Starts up FBX SDK. Must be called before any other operations.
+		 *			Outputs an FBX manager and FBX scene instances you should use in
+		 *			further operations. Returns false if the SDK wasn't started properly.
+		 */
+		bool startUpSdk(FbxScene*& scene);
+
+		/**
+		 * @brief	Shuts down FBX SDK. Must be called after any other operations.
+		 */
+		void shutDownSdk();
+
+		/**
+		 * @brief	Loads the data from the file at the provided path into the provided FBX scene. 
+		 *			Returns false if the file couldn't be loaded.
+		 */
+		bool loadFBXFile(FbxScene* scene, const Path& filePath);
+
+		/**
+		 * @brief	Parses an FBX scene. Find all meshes in the scene and returns mesh data object
+		 *			containing all vertices, indexes and other mesh information. Also outputs
+		 *			a sub-mesh array that allows you locate specific sub-meshes within the returned
+		 *			mesh data object. If requested animation and blend shape data is output as well.
+		 */
+		void parseScene(FbxScene* scene, const FBXImportOptions& options, FBXImportScene& outputScene);
+
+		/**
+		 * @brief	Parses an FBX mesh. Converts it from FBX SDK format into a mesh data object containing
+		 *			one or multiple sub-meshes.
+		 */
+		void parseMesh(FbxMesh* mesh, FBXImportNode* parentNode, const FBXImportOptions& options, FBXImportScene& outputScene);
+
+		/**
+		 * @brief	Imports blend shapes for all the meshes that are part of the scene.
+		 */
+		void importBlendShapes(FBXImportScene& scene, const FBXImportOptions& options);
+
+		/**
+		 * @brief	Parses a single FBX blend shape frame. Converts it from FBX SDK format into a 
+		 *			shape data object containing position and tangent frame.
+		 */
+		void importBlendShapeFrame(FbxShape* shape, const FBXImportMesh& mesh, const FBXImportOptions& options, FBXBlendShapeFrame& outFrame);
+
+		/**
+		 * @brief	Imports skinning information and bones for all meshes.
+		 */
+		void importSkin(FBXImportScene& scene);
+
+		/**
+		 * @brief	Imports skinning information and bones for the specified mesh.
+		 */
+		void importSkin(FBXImportScene& scene, FbxSkin* skin, FBXImportMesh& mesh);
+
+		/**
+		 * @brief	Imports all bone and blend shape animations from the FBX.
+		 */
+		void importAnimations(FbxScene* scene, FBXImportOptions& importOptions, FBXImportScene& importScene);
+
+		/**
+		 * @brief	Imports all animations for the specified animation layer and outputs them in the provided clip.
+		 *			Child nodes will be iterated recursively.
+		 */
+		void importAnimations(FbxAnimLayer* layer, FbxNode* node, FBXImportOptions& importOptions, 
+			FBXAnimationClip& clip, FBXImportScene& importScene);
+
+		/**
+		 * @brief	Converts a single FBX animation curve into an engine curve format, resampling it if
+		 *			necessary.
+		 */
+		void importCurve(FbxAnimCurve* fbxCurve, FBXImportOptions& importOptions, FBXAnimationCurve& curve, float start, float end);
+
+		/**
+		 * @brief	Converts a set of curves containing rotation in euler angles into a set of curves using
+		 *			quaternion rotation.
+		 */
+		void eulerToQuaternionCurves(FBXAnimationCurve(&eulerCurves)[3], FBXAnimationCurve(&quatCurves)[4]);
+
+		/**
+		 * @brief	Converts all the meshes from per-index attributes to per-vertex attributes.
+		 *
+		 * @note	This method will replace all meshes in the scene with new ones, and delete old ones
+		 *			so be sure not to keep any mesh references.
+		 */
+		void splitMeshVertices(FBXImportScene& scene);
+
+		/**
+		 * @brief	Traverses over all meshes in the scene and generates normals, tangents and bitangents if they're missing.
+		 *
+		 * @note	This assumes vertices have already been split and shouldn't be called on pre-split meshes.
+		 */
+		void generateMissingTangentSpace(FBXImportScene& scene, const FBXImportOptions& options);
+
+		/**
+		 * @brief	Converts the mesh data from the imported FBX scene into mesh data that can be used
+		 *			for initializing a mesh.
+		 */
+		RendererMeshDataPtr generateMeshData(const FBXImportScene& scene, const FBXImportOptions& options, Vector<SubMesh>& subMeshes);
+
+		/**
+		 * @brief	Creates an internal representation of an FBX node from an FbxNode object.
+		 */
+		FBXImportNode* createImportNode(FBXImportScene& scene, FbxNode* fbxNode, FBXImportNode* parent);
+
+	private:
+		Vector<WString> mExtensions;
+		FbxManager* mFBXManager;
+	};
 }

+ 1447 - 1447
BansheeFBXImporter/Source/BsFBXImporter.cpp

@@ -1,1448 +1,1448 @@
-#include "BsFBXImporter.h"
-#include "BsResource.h"
-#include "BsCoreApplication.h"
-#include "BsDebug.h"
-#include "BsDataStream.h"
-#include "BsMeshData.h"
-#include "BsMesh.h"
-#include "BsVector2.h"
-#include "BsVector3.h"
-#include "BsVector4.h"
-#include "BsQuaternion.h"
-#include "BsVertexDataDesc.h"
-#include "BsFBXUtility.h"
-#include "BsMeshUtility.h"
-#include "BsRendererMeshData.h"
-#include "BsMeshImportOptions.h"
-
-namespace BansheeEngine
-{
-	Matrix4 FBXToNativeType(const FbxAMatrix& value)
-	{
-		Matrix4 native;
-		for (UINT32 row = 0; row < 4; row++)
-			for (UINT32 col = 0; col < 4; col++)
-			native[row][col] = (float)value[col][row];
-
-		return native;
-	}
-
-	Vector3 FBXToNativeType(const FbxVector4& value)
-	{
-		Vector3 native;
-		native.x = (float)value[0];
-		native.y = (float)value[1];
-		native.z = (float)value[2];
-
-		return native;
-	}
-
-	Vector3 FBXToNativeType(const FbxDouble3& value)
-	{
-		Vector3 native;
-		native.x = (float)value[0];
-		native.y = (float)value[1];
-		native.z = (float)value[2];
-
-		return native;
-	}
-
-	Vector2 FBXToNativeType(const FbxVector2& value)
-	{
-		Vector2 native;
-		native.x = (float)value[0];
-		native.y = (float)value[1];
-
-		return native;
-	}
-
-	RGBA FBXToNativeType(const FbxColor& value)
-	{
-		Color native;
-		native.r = (float)value[0];
-		native.g = (float)value[1];
-		native.b = (float)value[2];
-		native.a = (float)value[3];
-
-		return native.getAsRGBA();
-	}
-
-	FbxSurfaceMaterial* FBXToNativeType(FbxSurfaceMaterial* const& value)
-	{
-		return value;
-	}
-
-	int FBXToNativeType(const int & value)
-	{
-		return value;
-	}
-
-	FBXImporter::FBXImporter()
-		:SpecificImporter(), mFBXManager(nullptr)
-	{
-		mExtensions.push_back(L"fbx");
-	}
-
-	FBXImporter::~FBXImporter() 
-	{
-
-	}
-
-	bool FBXImporter::isExtensionSupported(const WString& ext) const
-	{
-		WString lowerCaseExt = ext;
-		StringUtil::toLowerCase(lowerCaseExt);
-
-		return find(mExtensions.begin(), mExtensions.end(), lowerCaseExt) != mExtensions.end();
-	}
-
-	bool FBXImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
-	{
-		return true; // FBX files can be plain-text so I don't even check for magic number
-	}
-
-	ImportOptionsPtr FBXImporter::createImportOptions() const
-	{
-		return bs_shared_ptr_new<MeshImportOptions>();
-	}
-
-	ResourcePtr FBXImporter::import(const Path& filePath, ConstImportOptionsPtr importOptions)
-	{
-		FbxScene* fbxScene = nullptr;
-
-		if (!startUpSdk(fbxScene))
-			return nullptr;
-
-		if (!loadFBXFile(fbxScene, filePath))
-			return nullptr;
-
-		const MeshImportOptions* meshImportOptions = static_cast<const MeshImportOptions*>(importOptions.get());
-		FBXImportOptions fbxImportOptions;
-		fbxImportOptions.importNormals = meshImportOptions->getImportNormals();
-		fbxImportOptions.importTangents = meshImportOptions->getImportTangents();
-		fbxImportOptions.importAnimation = meshImportOptions->getImportAnimation();
-		fbxImportOptions.importBlendShapes = meshImportOptions->getImportBlendShapes();
-		fbxImportOptions.importSkin = meshImportOptions->getImportSkin();
-		fbxImportOptions.importScale = meshImportOptions->getImportScale();
-
-		FBXImportScene importedScene;
-		parseScene(fbxScene, fbxImportOptions, importedScene);
-
-		if (fbxImportOptions.importBlendShapes)
-			importBlendShapes(importedScene, fbxImportOptions);
-
-		if (fbxImportOptions.importSkin)
-			importSkin(importedScene);
-
-		if (fbxImportOptions.importAnimation)
-			importAnimations(fbxScene, fbxImportOptions, importedScene);
-
-		splitMeshVertices(importedScene);
-		generateMissingTangentSpace(importedScene, fbxImportOptions);
-		
-		Vector<SubMesh> subMeshes;
-		RendererMeshDataPtr rendererMeshData = generateMeshData(importedScene, fbxImportOptions, subMeshes);
-
-		// TODO - Later: Optimize mesh: Remove bad and degenerate polygons, weld nearby vertices, optimize for vertex cache
-
-		shutDownSdk();
-
-		INT32 usage = MU_STATIC;
-		if (meshImportOptions->getCPUReadable())
-			usage |= MU_CPUCACHED;
-
-		MeshPtr mesh = Mesh::_createPtr(rendererMeshData->getData(), subMeshes, usage);
-
-		WString fileName = filePath.getWFilename(false);
-		mesh->setName(fileName);
-
-		return mesh;
-	}
-
-	bool FBXImporter::startUpSdk(FbxScene*& scene)
-	{
-		mFBXManager = FbxManager::Create();
-		if (mFBXManager == nullptr)
-		{
-			LOGERR("FBX import failed: FBX SDK failed to initialize. FbxManager::Create() failed.");
-			return false;
-		}
-
-		FbxIOSettings* ios = FbxIOSettings::Create(mFBXManager, IOSROOT);
-		mFBXManager->SetIOSettings(ios);
-
-		scene = FbxScene::Create(mFBXManager, "Import Scene");
-		if (scene == nullptr)
-		{
-			LOGWRN("FBX import failed: Failed to create FBX scene.");
-			return false;
-		}
-
-		return true;
-	}
-
-	void FBXImporter::shutDownSdk()
-	{
-		mFBXManager->Destroy();
-		mFBXManager = nullptr;
-	}
-
-	bool FBXImporter::loadFBXFile(FbxScene* scene, const Path& filePath)
-	{
-		int lFileMajor, lFileMinor, lFileRevision;
-		int lSDKMajor,  lSDKMinor,  lSDKRevision;
-		FbxManager::GetFileFormatVersion(lSDKMajor, lSDKMinor, lSDKRevision);
-
-		FbxImporter* importer = FbxImporter::Create(mFBXManager, "");
-		bool importStatus = importer->Initialize(filePath.toString().c_str(), -1, mFBXManager->GetIOSettings());
-		
-		importer->GetFileVersion(lFileMajor, lFileMinor, lFileRevision);
-
-		if(!importStatus)
-		{
-			LOGERR("FBX import failed: Call to FbxImporter::Initialize() failed.\n" +
-				String("Error returned: %s\n\n") + String(importer->GetStatus().GetErrorString()));
-			return false;
-		}
-
-		mFBXManager->GetIOSettings()->SetBoolProp(IMP_FBX_TEXTURE, false);
-		mFBXManager->GetIOSettings()->SetBoolProp(IMP_FBX_GOBO, false);
-
-		importStatus = importer->Import(scene);
-
-		if(!importStatus)
-		{
-			importer->Destroy();
-			
-			LOGERR("FBX import failed: Call to FbxImporter::Initialize() failed.\n" +
-				String("Error returned: %s\n\n") + String(importer->GetStatus().GetErrorString()));
-			return false;
-		}
-
-		FbxAxisSystem fileCoordSystem = scene->GetGlobalSettings().GetAxisSystem();
-		FbxAxisSystem bsCoordSystem(FbxAxisSystem::eYAxis, FbxAxisSystem::eParityOdd, FbxAxisSystem::eRightHanded);
-		if (fileCoordSystem != bsCoordSystem)
-			bsCoordSystem.ConvertScene(scene);
-
-		importer->Destroy();
-		return true;
-	}
-
-	void FBXImporter::parseScene(FbxScene* scene, const FBXImportOptions& options, FBXImportScene& outputScene)
-	{
-		outputScene.rootNode = createImportNode(outputScene, scene->GetRootNode(), nullptr);
-
-		Stack<FbxNode*> todo;
-		todo.push(scene->GetRootNode());
-
-		while(!todo.empty())
-		{
-			FbxNode* curNode = todo.top();
-			FBXImportNode* curImportNode = outputScene.nodeMap[curNode];
-			todo.pop();
-
-			const char* name = curNode->GetName();
-
-			FbxNodeAttribute* attrib = curNode->GetNodeAttribute();
-			if(attrib != nullptr)
-			{
-				FbxNodeAttribute::EType attribType = attrib->GetAttributeType();
-
-				switch(attribType)
-				{
-				case FbxNodeAttribute::eNurbs:
-				case FbxNodeAttribute::eNurbsSurface:
-				case FbxNodeAttribute::ePatch:
-				{
-					FbxGeometryConverter geomConverter(mFBXManager);
-					attrib = geomConverter.Triangulate(attrib, true);
-
-					if (attrib->GetAttributeType() == FbxNodeAttribute::eMesh)
-					{
-						FbxMesh* mesh = static_cast<FbxMesh*>(attrib);
-						mesh->RemoveBadPolygons();
-
-						parseMesh(mesh, curImportNode, options, outputScene);
-					}
-				}
-					break;
-				case FbxNodeAttribute::eMesh:
-					{
-						FbxMesh* mesh = static_cast<FbxMesh*>(attrib);
-						mesh->RemoveBadPolygons();
-
-						if(!mesh->IsTriangleMesh())
-						{
-							FbxGeometryConverter geomConverter(mFBXManager);
-							geomConverter.Triangulate(mesh, true);
-							attrib = curNode->GetNodeAttribute();
-							mesh = static_cast<FbxMesh*>(attrib);
-						}
-
-						parseMesh(mesh, curImportNode, options, outputScene);
-					}
-					break;
-				}
-			}
-
-			for (int i = 0; i < curNode->GetChildCount(); i++)
-			{
-				FbxNode* childNode = curNode->GetChild(i);
-				createImportNode(outputScene, childNode, curImportNode);
-
-				todo.push(childNode);
-			}
-		}
-	}
-
-	FBXImportNode* FBXImporter::createImportNode(FBXImportScene& scene, FbxNode* fbxNode, FBXImportNode* parent)
-	{
-		FBXImportNode* node = bs_new<FBXImportNode>();
-
-		Vector3 translation = FBXToNativeType(fbxNode->LclTranslation.Get());
-		Vector3 rotationEuler = FBXToNativeType(fbxNode->LclRotation.Get());
-		Vector3 scale = FBXToNativeType(fbxNode->LclScaling.Get());
-
-		Quaternion rotation((Radian)rotationEuler.x, (Radian)rotationEuler.y, (Radian)rotationEuler.z);
-
-		node->localTransform.setTRS(translation, rotation, scale);
-		node->fbxNode = fbxNode;
-
-		if (parent != nullptr)
-		{
-			node->worldTransform = node->localTransform * parent->worldTransform;
-
-			parent->children.push_back(node);
-		}
-		else
-			node->worldTransform = node->localTransform;
-
-		scene.nodeMap.insert(std::make_pair(fbxNode, node));
-
-		return node;
-	}
-
-	void FBXImporter::splitMeshVertices(FBXImportScene& scene)
-	{
-		Vector<FBXImportMesh*> splitMeshes;
-
-		for (auto& mesh : scene.meshes)
-		{
-			FBXImportMesh* splitMesh = bs_new<FBXImportMesh>();
-			splitMesh->fbxMesh = mesh->fbxMesh;
-			splitMesh->referencedBy = mesh->referencedBy;
-			splitMesh->bones = mesh->bones;
-
-			FBXUtility::splitVertices(*mesh, *splitMesh);
-			FBXUtility::flipWindingOrder(*splitMesh);
-			splitMeshes.push_back(splitMesh);
-
-			bs_delete(mesh);
-		}
-
-		scene.meshes = splitMeshes;
-	}
-
-	RendererMeshDataPtr FBXImporter::generateMeshData(const FBXImportScene& scene, const FBXImportOptions& options, Vector<SubMesh>& outputSubMeshes)
-	{
-		Matrix4 importScale = Matrix4::scaling(options.importScale);
-
-		Vector<MeshDataPtr> allMeshData;
-		Vector<Vector<SubMesh>> allSubMeshes;
-
-		for (auto& mesh : scene.meshes)
-		{
-			Vector<Vector<UINT32>> indicesPerMaterial;
-			for (UINT32 i = 0; i < (UINT32)mesh->indices.size(); i++)
-			{
-				while (mesh->materials[i] >= indicesPerMaterial.size())
-					indicesPerMaterial.push_back(Vector<UINT32>());
-
-				indicesPerMaterial[mesh->materials[i]].push_back(mesh->indices[i]);
-			}
-
-			UINT32* orderedIndices = (UINT32*)bs_alloc((UINT32)mesh->indices.size() * sizeof(UINT32));
-			Vector<SubMesh> subMeshes;
-			UINT32 currentIndex = 0;
-
-			for (auto& subMeshIndices : indicesPerMaterial)
-			{
-				UINT32 indexCount = (UINT32)subMeshIndices.size();
-				UINT32* dest = orderedIndices + currentIndex;
-				memcpy(dest, subMeshIndices.data(), indexCount * sizeof(UINT32));
-
-				subMeshes.push_back(SubMesh(currentIndex, indexCount, DOT_TRIANGLE_LIST));
-
-				currentIndex += indexCount;
-			}
-
-			UINT32 vertexLayout = (UINT32)VertexLayout::Position;
-
-			size_t numVertices = mesh->positions.size();
-			bool hasColors = mesh->colors.size() == numVertices;
-			bool hasNormals = mesh->normals.size() == numVertices;
-
-			if (hasColors)
-				vertexLayout |= (UINT32)VertexLayout::Color;
-
-			bool hasTangents = false;
-			if (hasNormals)
-			{
-				vertexLayout |= (UINT32)VertexLayout::Normal;
-
-				if (mesh->tangents.size() == numVertices &&
-					mesh->bitangents.size() == numVertices)
-				{
-					vertexLayout |= (UINT32)VertexLayout::Tangent;
-					hasTangents = true;
-				}
-			}
-
-			int UVIdx = 0;
-			for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++)
-			{
-				if (mesh->UV[i].size() == numVertices)
-				{
-					if (i == 0)
-						vertexLayout |= (UINT32)VertexLayout::UV0;
-					else if (i == 1)
-						vertexLayout |= (UINT32)VertexLayout::UV1;
-				}
-			}
-
-			UINT32 numIndices = (UINT32)mesh->indices.size();
-			for (auto& node : mesh->referencedBy)
-			{
-				Matrix4 worldTransform = node->worldTransform * importScale;
-				Matrix4 worldTransformIT = worldTransform.transpose();
-				worldTransformIT = worldTransformIT.inverse();
-
-				RendererMeshDataPtr meshData = RendererMeshData::create((UINT32)numVertices, numIndices, (VertexLayout)vertexLayout);
-
-				// Copy indices
-				meshData->setIndices((UINT32*)mesh->indices.data(), numIndices * sizeof(UINT32));
-
-				// Copy & transform positions
-				UINT32 positionsSize = sizeof(Vector3) * (UINT32)numVertices;
-				Vector3* transformedPositions = (Vector3*)bs_stack_alloc(positionsSize);
-
-				for (UINT32 i = 0; i < (UINT32)numVertices; i++)
-					transformedPositions[i] = worldTransform.multiplyAffine((Vector3)mesh->positions[i]);
-
-				meshData->setPositions(transformedPositions, positionsSize);
-				bs_stack_free(transformedPositions);
-
-				// Copy & transform normals
-				if (hasNormals)
-				{
-					UINT32 normalsSize = sizeof(Vector3) * (UINT32)numVertices;
-					Vector3* transformedNormals = (Vector3*)bs_stack_alloc(normalsSize);
-
-					// Copy, convert & transform tangents & bitangents
-					if (hasTangents)
-					{
-						UINT32 tangentsSize = sizeof(Vector4) * (UINT32)numVertices;
-						Vector4* transformedTangents = (Vector4*)bs_stack_alloc(tangentsSize);
-
-						for (UINT32 i = 0; i < (UINT32)numVertices; i++)
-						{
-							Vector3 normal = (Vector3)mesh->normals[i];
-							transformedNormals[i] = worldTransformIT.multiplyAffine(normal);
-
-							Vector3 tangent = (Vector3)mesh->tangents[i];
-							tangent = worldTransformIT.multiplyAffine(tangent);
-
-							Vector3 bitangent = (Vector3)mesh->bitangents[i];
-							bitangent = worldTransformIT.multiplyAffine(bitangent);
-
-							Vector3 engineBitangent = Vector3::cross(normal, tangent);
-							float sign = Vector3::dot(engineBitangent, bitangent);
-
-							transformedTangents[i] = Vector4(tangent.x, tangent.y, tangent.z, sign > 0 ? 1.0f : -1.0f);
-						}
-
-						meshData->setTangents(transformedTangents, tangentsSize);
-						bs_stack_free(transformedTangents);
-					}
-					else // Just normals
-					{
-						for (UINT32 i = 0; i < (UINT32)numVertices; i++)
-							transformedNormals[i] = worldTransformIT.multiplyAffine((Vector3)mesh->normals[i]);
-					}
-
-					meshData->setNormals(transformedNormals, normalsSize);
-					bs_stack_free(transformedNormals);
-				}
-
-				// Copy colors
-				if (hasColors)
-				{
-					meshData->setColors(mesh->colors.data(), sizeof(UINT32) * (UINT32)numVertices);
-				}
-
-				// Copy UV
-				int writeUVIDx = 0;
-				for (auto& uvLayer : mesh->UV)
-				{
-					if (uvLayer.size() == numVertices)
-					{
-						UINT32 size = sizeof(Vector2) * (UINT32)numVertices;
-						Vector2* transformedUV = (Vector2*)bs_stack_alloc(size);
-
-						UINT32 i = 0;
-						for (auto& uv : uvLayer)
-						{
-							transformedUV[i] = uv;
-							transformedUV[i].y = 1.0f - uv.y;
-
-							i++;
-						}
-
-						if (writeUVIDx == 0)
-							meshData->setUV0(transformedUV, size);
-						else if (writeUVIDx == 1)
-							meshData->setUV1(transformedUV, size);
-
-						bs_stack_free(transformedUV);
-
-						writeUVIDx++;
-					}
-				}
-
-				allMeshData.push_back(meshData->getData());
-				allSubMeshes.push_back(subMeshes);
-			}
-		}
-
-		if (allMeshData.size() > 1)
-		{
-			return RendererMeshData::create(MeshData::combine(allMeshData, allSubMeshes, outputSubMeshes));
-		}
-		else if (allMeshData.size() == 1)
-		{
-			outputSubMeshes = allSubMeshes[0];
-			return RendererMeshData::create(allMeshData[0]);
-		}
-
-		return nullptr;
-	}
-
-	template<class TFBX, class TNative>
-	class FBXDirectIndexer
-	{
-	public:
-		FBXDirectIndexer(const FbxLayerElementTemplate<TFBX>& layer)
-			:mElementArray(layer.GetDirectArray()),
-			mElementCount(mElementArray.GetCount())
-		{}
-
-		bool get(int index, TNative& output) const
-		{
-			if (index < 0 || index >= mElementCount)
-				return false;
-
-			output = FBXToNativeType(mElementArray.GetAt(index));
-			return true;
-		}
-
-		bool isEmpty() const
-		{
-			return mElementCount == 0;
-		}
-
-	private:
-		const FbxLayerElementArrayTemplate<TFBX>& mElementArray;
-		int mElementCount;
-	};
-
-	template<class TFBX, class TNative>
-	class FBXIndexIndexer
-	{
-	public:
-		FBXIndexIndexer(const FbxLayerElementTemplate<TFBX>& layer)
-			:mElementArray(layer.GetDirectArray()),
-			mIndexArray(layer.GetIndexArray()),
-			mElementCount(mElementArray.GetCount()),
-			mIndexCount(mIndexArray.GetCount())
-		{}
-
-		bool get(int index, TNative& output) const
-		{
-			if (index < 0 || index >= mIndexCount)
-				return false;
-
-			int actualIndex = mIndexArray.GetAt(index);
-
-			if (actualIndex < 0 || actualIndex >= mElementCount)
-				return false;
-
-			output = FBXToNativeType(mElementArray.GetAt(actualIndex));
-			return true;
-		}
-
-		bool isEmpty() const
-		{
-			return mElementCount == 0 || mIndexCount == 0;
-		}
-
-	private:
-		const FbxLayerElementArrayTemplate<TFBX>& mElementArray;
-		const FbxLayerElementArrayTemplate<int>& mIndexArray;
-		int mElementCount;
-		int mIndexCount;
-	};
-
-	template<class TFBX, class TNative, class TIndexer>
-	void readLayerData(FbxLayerElementTemplate<TFBX>& layer, Vector<TNative>& output, const Vector<int>& indices)
-	{
-		TIndexer indexer(layer);
-		if (indexer.isEmpty())
-			return;
-
-		output.resize(indices.size());
-
-		FbxLayerElement::EMappingMode mappingMode = layer.GetMappingMode();
-
-		UINT32 indexCount = (UINT32)indices.size();
-		switch (mappingMode)
-		{
-		case FbxLayerElement::eByControlPoint:
-			for (UINT32 i = 0; i < indexCount; i++)
-			{
-				int index = indices[i];
-				indexer.get(index, output[i]);
-			}
-			break;
-		case FbxLayerElement::eByPolygonVertex:
-			for (UINT32 i = 0; i < indexCount; i++)
-				indexer.get(i, output[i]);
-			break;
-		case FbxLayerElement::eByPolygon:
-			// We expect mesh to be triangulated here
-		{
-			UINT32 polygonCount = indexCount / 3;
-			UINT32 index = 0;
-
-			for (UINT32 i = 0; i < polygonCount; i++)
-			{
-				TNative value;
-				indexer.get(i, value);
-
-				output[index++] = value;
-				output[index++] = value;
-				output[index++] = value;
-			}
-		}
-			break;
-		case FbxLayerElement::eAllSame:
-		{
-			TNative value;
-			indexer.get(0, value);
-
-			for (UINT32 i = 0; i < indexCount; i++)
-				output[i] = value;
-		}
-			break;
-		default:
-			LOGWRN("FBX Import: Unsupported layer mapping mode.");
-			break;
-		}
-	}
-
-	template<class TFBX, class TNative>
-	void readLayerData(FbxLayerElementTemplate<TFBX>& layer, Vector<TNative>& output, const Vector<int>& indices)
-	{
-		FbxLayerElement::EReferenceMode refMode = layer.GetReferenceMode();
-
-		if (refMode == FbxLayerElement::eDirect)
-			readLayerData<TFBX, TNative, FBXDirectIndexer<TFBX, TNative> >(layer, output, indices);
-		else if (refMode == FbxLayerElement::eIndexToDirect)
-			readLayerData<TFBX, TNative, FBXIndexIndexer<TFBX, TNative> >(layer, output, indices);
-		else
-			LOGWRN("FBX Import: Unsupported layer reference mode.");
-	}
-
-	void FBXImporter::parseMesh(FbxMesh* mesh, FBXImportNode* parentNode, const FBXImportOptions& options, FBXImportScene& outputScene)
-	{
-		// Check if valid
-		if (!mesh->IsTriangleMesh())
-			return;
-
-		UINT32 vertexCount = mesh->GetControlPointsCount();
-		UINT32 triangleCount = mesh->GetPolygonCount();
-
-		if (vertexCount == 0 || triangleCount == 0)
-			return;
-
-		// Register in global mesh array
-		FBXImportMesh* importMesh = nullptr;
-
-		auto iterFindMesh = outputScene.meshMap.find(mesh);
-		if (iterFindMesh != outputScene.meshMap.end())
-		{
-			UINT32 meshIdx = iterFindMesh->second;
-			outputScene.meshes[meshIdx]->referencedBy.push_back(parentNode);
-
-			return;
-		}
-		else
-		{
-			importMesh = bs_new<FBXImportMesh>();
-			outputScene.meshes.push_back(importMesh);
-
-			importMesh->referencedBy.push_back(parentNode);
-			importMesh->fbxMesh = mesh;
-			outputScene.meshMap[mesh] = (UINT32)outputScene.meshes.size() - 1;
-		}
-
-		// Import vertices
-		importMesh->positions.resize(vertexCount);
-		FbxVector4* controlPoints = mesh->GetControlPoints();
-
-		for (UINT32 i = 0; i < vertexCount; i++)
-			importMesh->positions[i] = FBXToNativeType(controlPoints[i]);
-
-		// Import triangles
-		UINT32 indexCount = triangleCount * 3;
-		importMesh->indices.resize(indexCount);
-
-		int* fbxIndices = mesh->GetPolygonVertices();
-		importMesh->indices.assign(fbxIndices, fbxIndices + indexCount);
-
-		// Import UVs
-		Vector<FbxLayerElementUV*> fbxUVLayers;
-
-		//// Search the diffuse layers first
-		for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++)
-		{
-			FbxLayer* layer = mesh->GetLayer(i, FbxLayerElement::eUV);
-			if (layer == nullptr)
-				continue;
-
-			for (int j = FbxLayerElement::eTextureDiffuse; j < FbxLayerElement::eTypeCount; j++)
-			{
-				FbxLayerElementUV* uvLayer = layer->GetUVs((FbxLayerElement::EType)j);
-				if (uvLayer == nullptr)
-					continue;
-
-				fbxUVLayers.push_back(uvLayer);
-
-				if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
-					break;
-			}
-
-			if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
-				break;
-		}
-
-		//// If there's room, search all others too
-		if (fbxUVLayers.size() < FBX_IMPORT_MAX_UV_LAYERS)
-		{
-			UINT32 numLayers = mesh->GetLayerCount();
-			for (UINT32 i = 0; i < numLayers; i++)
-			{
-				FbxLayer* layer = mesh->GetLayer(i);
-				if (layer == nullptr)
-					continue;
-
-				for (int j = FbxLayerElement::eTextureDiffuse; j < FbxLayerElement::eTypeCount; j++)
-				{
-					FbxLayerElementUV* uvLayer = layer->GetUVs((FbxLayerElement::EType)j);
-					if (uvLayer == nullptr)
-						continue;
-
-					auto iterFind = std::find(fbxUVLayers.begin(), fbxUVLayers.end(), uvLayer);
-					if (iterFind != fbxUVLayers.end())
-						continue;
-
-					fbxUVLayers.push_back(uvLayer);
-
-					if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
-						break;
-				}
-
-				if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
-					break;
-			}
-		}
-
-		for (size_t i = 0; i < fbxUVLayers.size(); i++)
-			readLayerData(*fbxUVLayers[i], importMesh->UV[i], importMesh->indices);
-
-		FbxLayer* mainLayer = mesh->GetLayer(0);
-		if (mainLayer != nullptr)
-		{
-			// Import colors
-			if (mainLayer->GetVertexColors() != nullptr)
-				readLayerData(*mainLayer->GetVertexColors(), importMesh->colors, importMesh->indices);
-
-			// Import normals
-			if (options.importNormals)
-			{
-				bool hasNormals = mainLayer->GetNormals() != nullptr;
-
-				if (!hasNormals)
-				{
-					if (mainLayer->GetSmoothing() != nullptr)
-					{
-						FbxLayerElementSmoothing* smoothing = mainLayer->GetSmoothing();
-
-						if (smoothing->GetMappingMode() == FbxLayerElement::eByEdge)
-						{
-							FbxGeometryConverter converter(mFBXManager);
-							converter.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0);
-						}
-
-						readLayerData(*smoothing, importMesh->smoothingGroups, importMesh->indices);
-
-						if (!importMesh->smoothingGroups.empty())
-						{
-							FBXUtility::normalsFromSmoothing(importMesh->positions, importMesh->indices, 
-								importMesh->smoothingGroups, importMesh->normals);
-						}
-					}
-				}
-				else
-					readLayerData(*mainLayer->GetNormals(), importMesh->normals, importMesh->indices);
-			}
-
-			// Import tangents
-			if (options.importTangents)
-			{
-				bool hasTangents = mainLayer->GetTangents() != nullptr && mainLayer->GetBinormals() != nullptr;
-
-				if (!hasTangents)
-				{
-					if (fbxUVLayers.size() > 0)
-						hasTangents = mesh->GenerateTangentsData(0, false);
-				}
-
-				if (hasTangents)
-				{
-					readLayerData(*mainLayer->GetTangents(), importMesh->tangents, importMesh->indices);
-					readLayerData(*mainLayer->GetBinormals(), importMesh->bitangents, importMesh->indices);
-				}
-			}
-
-			// Import material indexes
-			if (mainLayer->GetMaterials() != nullptr)
-			{
-				Vector<FbxSurfaceMaterial*> fbxMaterials;
-
-				readLayerData(*mainLayer->GetMaterials(), fbxMaterials, importMesh->indices);
-
-				UnorderedMap<FbxSurfaceMaterial*, int> materialLookup;
-				int nextMaterialIdx = 0;
-				for (UINT32 i = 0; i < (UINT32)fbxMaterials.size(); i++)
-				{
-					auto iterFind = materialLookup.find(fbxMaterials[i]);
-
-					int materialIdx = 0;
-					if (iterFind != materialLookup.end())
-						materialIdx = iterFind->second;
-					else
-					{
-						materialIdx = nextMaterialIdx++;
-						materialLookup[fbxMaterials[i]] = materialIdx;
-					}
-
-					importMesh->materials.push_back(materialIdx);
-				}
-			}
-		}
-	}
-
-	void FBXImporter::importBlendShapes(FBXImportScene& scene, const FBXImportOptions& options)
-	{
-		for (auto& mesh : scene.meshes)
-		{
-			FbxMesh* fbxMesh = mesh->fbxMesh;
-
-			UINT32 deformerCount = (UINT32)fbxMesh->GetDeformerCount(FbxDeformer::eBlendShape);
-			for (UINT32 i = 0; i < deformerCount; i++)
-			{
-				FbxBlendShape* deformer = static_cast<FbxBlendShape*>(fbxMesh->GetDeformer(i, FbxDeformer::eBlendShape));
-
-				UINT32 blendShapeChannelCount = (UINT32)deformer->GetBlendShapeChannelCount();
-				for (UINT32 j = 0; j < blendShapeChannelCount; ++j)
-				{
-					FbxBlendShapeChannel* channel = deformer->GetBlendShapeChannel(j);
-					double* weights = channel->GetTargetShapeFullWeights();
-
-					UINT32 frameCount = channel->GetTargetShapeCount();
-					if (frameCount == 0)
-						continue;
-
-					mesh->blendShapes.push_back(FBXBlendShape());
-					FBXBlendShape& blendShape = mesh->blendShapes.back();
-					blendShape.name = channel->GetName();
-					blendShape.frames.resize(frameCount);
-
-					for (UINT32 k = 0; k < frameCount; k++)
-					{
-						FbxShape* fbxShape = channel->GetTargetShape(k);
-
-						FBXBlendShapeFrame& frame = blendShape.frames[k];
-						frame.weight = (float)weights[k];
-
-						importBlendShapeFrame(fbxShape, *mesh, options, frame);
-					}
-				}
-			}
-		}
-	}
-
-	void FBXImporter::importBlendShapeFrame(FbxShape* shape, const FBXImportMesh& mesh, const FBXImportOptions& options, FBXBlendShapeFrame& outFrame)
-	{
-		UINT32 vertexCount = (UINT32)shape->GetControlPointsCount();
-		outFrame.positions.resize(vertexCount);
-		FbxVector4* controlPoints = shape->GetControlPoints();
-
-		for (UINT32 i = 0; i < vertexCount; i++)
-			outFrame.positions[i] = FBXToNativeType(controlPoints[i]);
-
-		FbxLayer* mainLayer = shape->GetLayer(0);
-		if (options.importNormals)
-		{
-			bool hasNormals = mainLayer->GetNormals() != nullptr;
-
-			if (!hasNormals)
-			{
-				if (!mesh.smoothingGroups.empty())
-				{
-					FBXUtility::normalsFromSmoothing(outFrame.positions, mesh.indices,
-						mesh.smoothingGroups, outFrame.normals);
-				}
-			}
-			else
-				readLayerData(*mainLayer->GetNormals(), outFrame.normals, mesh.indices);
-		}
-
-		if (options.importTangents)
-		{
-			bool hasTangents = mainLayer->GetTangents() != nullptr && mainLayer->GetBinormals() != nullptr;
-
-			if (hasTangents)
-			{
-				readLayerData(*mainLayer->GetTangents(), outFrame.tangents, mesh.indices);
-				readLayerData(*mainLayer->GetBinormals(), outFrame.bitangents, mesh.indices);
-			}
-		}
-	}
-
-	void FBXImporter::importSkin(FBXImportScene& scene)
-	{
-		for (auto& mesh : scene.meshes)
-		{
-			FbxMesh* fbxMesh = mesh->fbxMesh;
-
-			UINT32 deformerCount = (UINT32)fbxMesh->GetDeformerCount(FbxDeformer::eSkin);
-			if (deformerCount > 0)
-			{
-				// We ignore other deformers if there's more than one
-				FbxSkin* deformer = static_cast<FbxSkin*>(fbxMesh->GetDeformer(0, FbxDeformer::eSkin));
-				UINT32 boneCount = (UINT32)deformer->GetClusterCount();
-
-				if (boneCount == 0)
-					continue;
-
-				// If only one bone and it links to itself, ignore the bone
-				if (boneCount == 1)
-				{
-					FbxCluster* cluster = deformer->GetCluster(0);
-					if (mesh->referencedBy.size() == 1 && mesh->referencedBy[0]->fbxNode == cluster->GetLink())
-						continue;
-				}
-
-				importSkin(scene, deformer, *mesh);
-			}
-		}
-	}
-
-	void FBXImporter::importSkin(FBXImportScene& scene, FbxSkin* skin, FBXImportMesh& mesh)
-	{
-		Vector<FBXBoneInfluence>& influences = mesh.boneInfluences;
-		influences.resize(mesh.positions.size());
-
-		UnorderedSet<FbxNode*> existingBones;
-		UINT32 boneCount = (UINT32)skin->GetClusterCount();
-		for (UINT32 i = 0; i < boneCount; i++)
-		{
-			FbxCluster* cluster = skin->GetCluster(i);
-			FbxNode* link = cluster->GetLink();
-
-			// The bone node doesn't exist, skip it
-			auto iterFind = scene.nodeMap.find(link);
-			if (iterFind == scene.nodeMap.end())
-				continue;
-
-			mesh.bones.push_back(FBXBone());
-
-			FBXBone& bone = mesh.bones.back();
-			bone.node = iterFind->second;
-
-			FbxAMatrix clusterTransform;
-			cluster->GetTransformMatrix(clusterTransform);
-
-			FbxAMatrix linkTransform;
-			cluster->GetTransformLinkMatrix(linkTransform);
-
-			FbxAMatrix bindPose = linkTransform.Inverse() * clusterTransform;
-			bone.bindPose = FBXToNativeType(bindPose);
-
-			bool isDuplicate = existingBones.insert(link).second;
-			bool isAdditive = cluster->GetLinkMode() == FbxCluster::eAdditive;
-
-			// We avoid importing weights twice for duplicate bones and we don't
-			// support additive link mode.
-			bool importWeights = !isDuplicate && !isAdditive;
-			if (!importWeights)
-				continue;
-
-			double* weights = cluster->GetControlPointWeights();
-			INT32* indices = cluster->GetControlPointIndices();
-			UINT32 numIndices = (UINT32)cluster->GetControlPointIndicesCount();
-			INT32 numVertices = (INT32)influences.size();
-
-			// Add new weights while keeping them in order and removing the smallest ones
-			// if number of influences exceeds the set maximum value
-			for (UINT32 j = 0; j < numIndices; j++)
-			{
-				INT32 vertexIndex = indices[j];
-				float weight = (float)weights[j];
-
-				for (UINT32 k = 0; k < FBX_IMPORT_MAX_BONE_INFLUENCES; k++)
-				{
-					if (vertexIndex < 0 || vertexIndex >= numVertices)
-						continue;
-
-					if (weight >= influences[vertexIndex].weights[k])
-					{
-						for (UINT32 l = FBX_IMPORT_MAX_BONE_INFLUENCES - 2; l >= k; l--)
-						{
-							influences[vertexIndex].weights[l + 1] = influences[vertexIndex].weights[l];
-							influences[vertexIndex].indices[l + 1] = influences[vertexIndex].indices[l];
-						}
-
-						influences[vertexIndex].weights[k] = weight;
-						influences[vertexIndex].indices[k] = i;
-						break;
-					}
-				}
-			}
-		}
-
-		if (mesh.bones.empty())
-			mesh.boneInfluences.clear();
-
-		// Normalize weights
-		UINT32 numInfluences = (UINT32)mesh.boneInfluences.size();
-		for (UINT32 i = 0; i < numInfluences; i++)
-		{
-			float sum = 0.0f;
-			for (UINT32 j = 0; j < FBX_IMPORT_MAX_BONE_INFLUENCES; j++)
-				sum += influences[i].weights[j];
-
-			float invSum = 1.0f / sum;
-			for (UINT32 j = 0; j < FBX_IMPORT_MAX_BONE_INFLUENCES; j++)
-				influences[i].weights[j] *= invSum;
-		}
-	}
-
-	void FBXImporter::generateMissingTangentSpace(FBXImportScene& scene, const FBXImportOptions& options)
-	{
-		for (auto& mesh : scene.meshes)
-		{
-			UINT32 numVertices = (UINT32)mesh->positions.size();
-			UINT32 numIndices = (UINT32)mesh->indices.size();
-
-			if ((options.importNormals || options.importTangents) && mesh->normals.empty())
-			{
-				mesh->normals.resize(numVertices);
-
-				MeshUtility::calculateNormals(mesh->positions.data(), (UINT8*)mesh->indices.data(), numVertices, numIndices, mesh->normals.data());
-			}
-
-			if (options.importTangents && !mesh->UV[0].empty() && (mesh->tangents.empty() || mesh->bitangents.empty()))
-			{
-				mesh->tangents.resize(numVertices);
-				mesh->bitangents.resize(numVertices);
-
-				MeshUtility::calculateTangents(mesh->positions.data(), mesh->normals.data(), mesh->UV[0].data(), (UINT8*)mesh->indices.data(), 
-					numVertices, numIndices, mesh->tangents.data(), mesh->bitangents.data());
-			}
-
-			for (auto& shape : mesh->blendShapes)
-			{
-				for (auto& frame : shape.frames)
-				{
-					if ((options.importNormals || options.importTangents) && frame.normals.empty())
-					{
-						frame.normals.resize(numVertices);
-
-						MeshUtility::calculateNormals(mesh->positions.data(), (UINT8*)mesh->indices.data(), numVertices, numIndices, frame.normals.data());
-					}
-
-					if (options.importTangents && !mesh->UV[0].empty() && (frame.tangents.empty() || frame.bitangents.empty()))
-					{
-						mesh->tangents.resize(numVertices);
-						mesh->bitangents.resize(numVertices);
-
-						MeshUtility::calculateTangents(mesh->positions.data(), frame.normals.data(), mesh->UV[0].data(), (UINT8*)mesh->indices.data(),
-							numVertices, numIndices, frame.tangents.data(), frame.bitangents.data());
-					}
-				}
-			}
-		}
-	}
-
-	void FBXImporter::importAnimations(FbxScene* scene, FBXImportOptions& importOptions, FBXImportScene& importScene)
-	{
-		FbxNode* root = scene->GetRootNode();
-
-		UINT32 numAnimStacks = (UINT32)scene->GetSrcObjectCount<FbxAnimStack>();
-		for (UINT32 i = 0; i < numAnimStacks; i++)
-		{
-			FbxAnimStack* animStack = scene->GetSrcObject<FbxAnimStack>(i);
-
-			importScene.clips.push_back(FBXAnimationClip());
-			FBXAnimationClip& clip = importScene.clips.back();
-			clip.name = animStack->GetName();
-
-			FbxTimeSpan timeSpan = animStack->GetLocalTimeSpan();
-			clip.start = (float)timeSpan.GetStart().GetSecondDouble();
-			clip.end = (float)timeSpan.GetStop().GetSecondDouble();
-
-			UINT32 layerCount = animStack->GetMemberCount<FbxAnimLayer>();
-			if (layerCount > 1)
-			{
-				FbxAnimEvaluator* evaluator = scene->GetAnimationEvaluator();
-
-				FbxTime startTime;
-				startTime.SetSecondDouble(clip.start);
-
-				FbxTime endTime;
-				endTime.SetSecondDouble(clip.end);
-
-				FbxTime sampleRate;
-
-				if (importOptions.animResample)
-					sampleRate.SetSecondDouble(importOptions.animSampleRate);
-				else
-				{
-					FbxTime::EMode timeMode = scene->GetGlobalSettings().GetTimeMode();
-					sampleRate.SetSecondDouble(1.0f / FbxTime::GetFrameRate(timeMode));
-				}
-
-				if (!animStack->BakeLayers(evaluator, startTime, endTime, sampleRate))
-					continue;
-
-				layerCount = animStack->GetMemberCount<FbxAnimLayer>();
-			}
-
-			if (layerCount == 1)
-			{
-				FbxAnimLayer* animLayer = animStack->GetMember<FbxAnimLayer>(0);
-
-				importAnimations(animLayer, root, importOptions, clip, importScene);
-			}
-		}
-	}
-
-	void FBXImporter::importAnimations(FbxAnimLayer* layer, FbxNode* node, FBXImportOptions& importOptions,
-		FBXAnimationClip& clip, FBXImportScene& importScene)
-	{
-		FbxAnimCurve* translation[3];
-		translation[0] = node->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
-		translation[1] = node->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y);
-		translation[2] = node->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z);
-
-		FbxAnimCurve* rotation[3];
-		rotation[0] = node->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
-		rotation[1] = node->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y);
-		rotation[2] = node->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z);
-
-		FbxAnimCurve* scale[3];
-		scale[0] = node->LclScaling.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
-		scale[1] = node->LclScaling.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y);
-		scale[2] = node->LclScaling.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z);
-
-		auto hasCurveValues = [](FbxAnimCurve* curves[3])
-		{
-			for (UINT32 i = 0; i < 3; i++)
-			{
-				if (curves[i] != nullptr && curves[i]->KeyGetCount() > 0)
-					return true;
-			}
-
-			return false;
-		};
-
-		bool hasBoneAnimation = hasCurveValues(translation) || hasCurveValues(rotation) || hasCurveValues(scale);
-		if (hasBoneAnimation)
-		{
-			clip.boneAnimations.push_back(FBXBoneAnimation());
-			FBXBoneAnimation& boneAnim = clip.boneAnimations.back();
-			boneAnim.node = importScene.nodeMap[node];
-
-			importCurve(translation[0], importOptions, boneAnim.translation[0], clip.start, clip.end);
-			importCurve(translation[1], importOptions, boneAnim.translation[1], clip.start, clip.end);
-			importCurve(translation[2], importOptions, boneAnim.translation[2], clip.start, clip.end);
-
-			importCurve(scale[0], importOptions, boneAnim.scale[0], clip.start, clip.end);
-			importCurve(scale[1], importOptions, boneAnim.scale[1], clip.start, clip.end);
-			importCurve(scale[2], importOptions, boneAnim.scale[2], clip.start, clip.end);
-
-			FBXAnimationCurve tempCurveRotation[3];
-			importCurve(rotation[0], importOptions, tempCurveRotation[0], clip.start, clip.end);
-			importCurve(rotation[1], importOptions, tempCurveRotation[1], clip.start, clip.end);
-			importCurve(rotation[2], importOptions, tempCurveRotation[2], clip.start, clip.end);
-
-			eulerToQuaternionCurves(tempCurveRotation, boneAnim.rotation);
-		}
-
-		if (importOptions.importBlendShapes)
-		{
-			FbxMesh* fbxMesh = node->GetMesh();
-			if (fbxMesh != nullptr)
-			{
-				INT32 deformerCount = fbxMesh->GetDeformerCount(FbxDeformer::eBlendShape);
-				for (INT32 i = 0; i < deformerCount; i++)
-				{
-					FbxBlendShape* deformer = static_cast<FbxBlendShape*>(fbxMesh->GetDeformer(i, FbxDeformer::eBlendShape));
-
-					INT32 channelCount = deformer->GetBlendShapeChannelCount();
-					for (INT32 j = 0; j < channelCount; j++)
-					{
-						FbxBlendShapeChannel* channel = deformer->GetBlendShapeChannel(j);
-
-						FbxAnimCurve* curve = fbxMesh->GetShapeChannel(i, j, layer);
-						if (curve != nullptr && curve->KeyGetCount() > 0)
-						{
-							clip.blendShapeAnimations.push_back(FBXBlendShapeAnimation());
-							FBXBlendShapeAnimation& blendShapeAnim = clip.blendShapeAnimations.back();
-							blendShapeAnim.blendShape = channel->GetName();
-
-							importCurve(curve, importOptions, blendShapeAnim.curve, clip.start, clip.end);
-						}
-					}
-				}
-			}
-		}
-
-		UINT32 childCount = (UINT32)node->GetChildCount();
-		for (UINT32 i = 0; i < childCount; i++)
-		{
-			FbxNode* child = node->GetChild(i);
-			importAnimations(layer, child, importOptions, clip, importScene);
-		}
-	}
-
-	void FBXImporter::eulerToQuaternionCurves(FBXAnimationCurve(&eulerCurves)[3], FBXAnimationCurve(&quatCurves)[4])
-	{
-		const float FIT_TIME = 0.33f;
-
-		INT32 numKeys = (INT32)eulerCurves[0].keyframes.size();
-
-		if (numKeys != (INT32)eulerCurves[1].keyframes.size() || numKeys != (INT32)eulerCurves[2].keyframes.size())
-			return;
-
-		auto eulerToQuaternion = [&](INT32 keyIdx, float time, const Quaternion& lastQuat)
-		{
-			Degree x = (Degree)eulerCurves[0].evaluate(time);
-			Degree y = (Degree)eulerCurves[1].evaluate(time);
-			Degree z = (Degree)eulerCurves[2].evaluate(time);
-
-			Quaternion quat(x, y, z);
-
-			// Flip quaternion in case rotation is over 180 degrees
-			if (keyIdx > 0)
-			{
-				float dot = quat.dot(lastQuat);
-				if (dot < 0.0f)
-					quat = Quaternion(-quat.x, -quat.y, -quat.z, -quat.w);
-			}
-
-			return quat;
-		};
-
-		struct FitKeyframe
-		{
-			float time;
-			Quaternion value;
-		};
-
-		Vector<FitKeyframe> fitQuaternions(numKeys * 2);
-
-		Quaternion lastQuat;
-		for (INT32 i = 0; i < numKeys; i++)
-		{
-			float time = eulerCurves[0].keyframes[i].time;
-			Quaternion quat = eulerToQuaternion(i, time, lastQuat);
-
-			// Calculate extra values between keys so we can better approximate tangents
-			if ((i + 1) < numKeys)
-			{
-				float nextTime = eulerCurves[0].keyframes[i + 1].time;
-				float dt = nextTime - time;
-
-				FitKeyframe& fitStart = fitQuaternions[i * 2 + 0];
-				FitKeyframe& fitEnd = fitQuaternions[i * 2 + 1];
-
-				fitStart.time = time + dt * FIT_TIME;
-				fitEnd.time = time + dt * (1.0f - FIT_TIME);
-
-				fitStart.value = eulerToQuaternion(i, fitStart.time, quat);
-				fitEnd.value = eulerToQuaternion(i, fitEnd.time, fitStart.value);
-
-				lastQuat = fitStart.value;
-			}
-
-			// TODO - If animation is looping I should also compare last and first for continuity
-
-			for (INT32 j = 0; j < 4; j++)
-			{
-				quatCurves[j].keyframes.push_back(FBXKeyFrame());
-				FBXKeyFrame& keyFrame = quatCurves[j].keyframes.back();
-				keyFrame.time = time;
-				keyFrame.value = quat[j];
-
-				keyFrame.inTangent = 0;
-				keyFrame.outTangent = 0;
-			}
-		}
-
-		// Recalculate tangents for quaternion curves
-
-		// TODO - There must be an analytical way to convert euler angle tangents
-		//        to quaternion tangents, but I don't want to bother figuring it out
-		//        until I have a test-bed for animation.
-		if (numKeys > 1)
-		{
-			// TODO - I could check per-key curve interpolation originally assigned in FBX
-			//        and use that to generate linear/constant slopes. Currently I assume 
-			//        its all cubic.
-
-			// First key
-			{
-				const FitKeyframe& fitKeyFrame = fitQuaternions[0];
-
-				for (INT32 j = 0; j < 4; j++)
-				{
-					FBXKeyFrame& keyFrame = quatCurves[j].keyframes[0];
-
-					float dt = fitKeyFrame.time - keyFrame.time;
-
-					keyFrame.inTangent = (fitKeyFrame.value[j] - keyFrame.value) / dt;
-					keyFrame.outTangent = keyFrame.inTangent;
-				}
-			}
-
-			// In-between keys
-			{
-				for (INT32 i = 1; i < (numKeys - 1); i++)
-				{
-					const FitKeyframe& fitPointStart = fitQuaternions[i * 2 - 1];
-					const FitKeyframe& fitPointEnd = fitQuaternions[i * 2 + 0];
-
-					for (INT32 j = 0; j < 4; j++)
-					{
-						FBXKeyFrame& keyFrame = quatCurves[j].keyframes[i];
-
-						float dt0 = fitPointEnd.time - keyFrame.time;
-						float dt1 = keyFrame.time - fitPointStart.time;
-
-						float t0 = fitPointEnd.value[j] - keyFrame.value;
-						float t1 = keyFrame.value - fitPointStart.value[j];
-
-						keyFrame.inTangent = t0 / (0.5f * dt0) + t1 / (0.5f * dt1);
-						keyFrame.outTangent = keyFrame.inTangent;
-					}
-				}
-			}
-
-			// Last key
-			{
-				const FitKeyframe& fitKeyFrame = fitQuaternions[(numKeys - 2) * 2];
-
-				for (INT32 j = 0; j < 4; j++)
-				{
-					FBXKeyFrame& keyFrame = quatCurves[j].keyframes[numKeys - 2];
-
-					float dt = keyFrame.time - fitKeyFrame.time;
-
-					keyFrame.inTangent = (keyFrame.value - fitKeyFrame.value[j]) / dt;
-					keyFrame.outTangent = keyFrame.inTangent;
-				}
-			}
-		}
-	}
-
-	void FBXImporter::importCurve(FbxAnimCurve* fbxCurve, FBXImportOptions& importOptions, FBXAnimationCurve& curve, float start, float end)
-	{
-		if (fbxCurve == nullptr)
-			return;
-
-		INT32 keyCount = fbxCurve->KeyGetCount();
-		if (importOptions.animResample)
-		{
-			float curveStart = std::numeric_limits<float>::infinity();
-			float curveEnd = -std::numeric_limits<float>::infinity();
-
-			for (INT32 i = 0; i < keyCount; i++)
-			{
-				FbxTime fbxTime = fbxCurve->KeyGetTime(i);
-				float time = (float)fbxTime.GetSecondDouble();
-
-				curveStart = std::min(time, curveStart);
-				curveEnd = std::max(time, curveEnd);
-			}
-
-			curveStart = Math::clamp(curveStart, start, end);
-			curveEnd = Math::clamp(curveEnd, start, end);
-
-			float curveLength = curveEnd - curveStart;
-			INT32 numSamples = Math::ceilToInt(curveLength / importOptions.animSampleRate);
-
-			// We don't use the exact provided sample rate but instead modify it slightly so it
-			// completely covers the curve range including start/end points while maintaining
-			// constant time step between keyframes.
-			float dt = curveLength / (float)numSamples; 
-
-			INT32 lastKeyframe = 0;
-			INT32 lastLeftTangent = 0;
-			INT32 lastRightTangent = 0;
-			for (INT32 i = 0; i < numSamples; i++)
-			{
-				float sampleTime = std::min(curveStart + i * dt, curveEnd);
-				FbxTime fbxSampleTime;
-				fbxSampleTime.SetSecondDouble(sampleTime);
-
-				curve.keyframes.push_back(FBXKeyFrame());
-				FBXKeyFrame& keyFrame = curve.keyframes.back();
-				keyFrame.time = sampleTime;
-				keyFrame.value = fbxCurve->Evaluate(fbxSampleTime, &lastKeyframe);
-				keyFrame.inTangent = fbxCurve->EvaluateLeftDerivative(fbxSampleTime, &lastLeftTangent);
-				keyFrame.outTangent = fbxCurve->EvaluateRightDerivative(fbxSampleTime, &lastRightTangent);
-			}
-		}
-		else
-		{
-			for (int i = 0; i < keyCount; i++)
-			{
-				FbxTime fbxTime = fbxCurve->KeyGetTime(i);
-				float time = (float)fbxTime.GetSecondDouble();
-
-				if (time < start || time > end)
-					continue;
-
-				curve.keyframes.push_back(FBXKeyFrame());
-				FBXKeyFrame& keyFrame = curve.keyframes.back();
-				keyFrame.time = time;
-				keyFrame.value = fbxCurve->KeyGetValue(i);
-				keyFrame.inTangent = fbxCurve->KeyGetLeftDerivative(i);
-				keyFrame.outTangent = fbxCurve->KeyGetRightDerivative(i);
-			}
-		}
-	}
+#include "BsFBXImporter.h"
+#include "BsResource.h"
+#include "BsCoreApplication.h"
+#include "BsDebug.h"
+#include "BsDataStream.h"
+#include "BsMeshData.h"
+#include "BsMesh.h"
+#include "BsVector2.h"
+#include "BsVector3.h"
+#include "BsVector4.h"
+#include "BsQuaternion.h"
+#include "BsVertexDataDesc.h"
+#include "BsFBXUtility.h"
+#include "BsMeshUtility.h"
+#include "BsRendererMeshData.h"
+#include "BsMeshImportOptions.h"
+
+namespace BansheeEngine
+{
+	Matrix4 FBXToNativeType(const FbxAMatrix& value)
+	{
+		Matrix4 native;
+		for (UINT32 row = 0; row < 4; row++)
+			for (UINT32 col = 0; col < 4; col++)
+			native[row][col] = (float)value[col][row];
+
+		return native;
+	}
+
+	Vector3 FBXToNativeType(const FbxVector4& value)
+	{
+		Vector3 native;
+		native.x = (float)value[0];
+		native.y = (float)value[1];
+		native.z = (float)value[2];
+
+		return native;
+	}
+
+	Vector3 FBXToNativeType(const FbxDouble3& value)
+	{
+		Vector3 native;
+		native.x = (float)value[0];
+		native.y = (float)value[1];
+		native.z = (float)value[2];
+
+		return native;
+	}
+
+	Vector2 FBXToNativeType(const FbxVector2& value)
+	{
+		Vector2 native;
+		native.x = (float)value[0];
+		native.y = (float)value[1];
+
+		return native;
+	}
+
+	RGBA FBXToNativeType(const FbxColor& value)
+	{
+		Color native;
+		native.r = (float)value[0];
+		native.g = (float)value[1];
+		native.b = (float)value[2];
+		native.a = (float)value[3];
+
+		return native.getAsRGBA();
+	}
+
+	FbxSurfaceMaterial* FBXToNativeType(FbxSurfaceMaterial* const& value)
+	{
+		return value;
+	}
+
+	int FBXToNativeType(const int & value)
+	{
+		return value;
+	}
+
+	FBXImporter::FBXImporter()
+		:SpecificImporter(), mFBXManager(nullptr)
+	{
+		mExtensions.push_back(L"fbx");
+	}
+
+	FBXImporter::~FBXImporter() 
+	{
+
+	}
+
+	bool FBXImporter::isExtensionSupported(const WString& ext) const
+	{
+		WString lowerCaseExt = ext;
+		StringUtil::toLowerCase(lowerCaseExt);
+
+		return find(mExtensions.begin(), mExtensions.end(), lowerCaseExt) != mExtensions.end();
+	}
+
+	bool FBXImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
+	{
+		return true; // FBX files can be plain-text so I don't even check for magic number
+	}
+
+	ImportOptionsPtr FBXImporter::createImportOptions() const
+	{
+		return bs_shared_ptr_new<MeshImportOptions>();
+	}
+
+	ResourcePtr FBXImporter::import(const Path& filePath, ConstImportOptionsPtr importOptions)
+	{
+		FbxScene* fbxScene = nullptr;
+
+		if (!startUpSdk(fbxScene))
+			return nullptr;
+
+		if (!loadFBXFile(fbxScene, filePath))
+			return nullptr;
+
+		const MeshImportOptions* meshImportOptions = static_cast<const MeshImportOptions*>(importOptions.get());
+		FBXImportOptions fbxImportOptions;
+		fbxImportOptions.importNormals = meshImportOptions->getImportNormals();
+		fbxImportOptions.importTangents = meshImportOptions->getImportTangents();
+		fbxImportOptions.importAnimation = meshImportOptions->getImportAnimation();
+		fbxImportOptions.importBlendShapes = meshImportOptions->getImportBlendShapes();
+		fbxImportOptions.importSkin = meshImportOptions->getImportSkin();
+		fbxImportOptions.importScale = meshImportOptions->getImportScale();
+
+		FBXImportScene importedScene;
+		parseScene(fbxScene, fbxImportOptions, importedScene);
+
+		if (fbxImportOptions.importBlendShapes)
+			importBlendShapes(importedScene, fbxImportOptions);
+
+		if (fbxImportOptions.importSkin)
+			importSkin(importedScene);
+
+		if (fbxImportOptions.importAnimation)
+			importAnimations(fbxScene, fbxImportOptions, importedScene);
+
+		splitMeshVertices(importedScene);
+		generateMissingTangentSpace(importedScene, fbxImportOptions);
+		
+		Vector<SubMesh> subMeshes;
+		RendererMeshDataPtr rendererMeshData = generateMeshData(importedScene, fbxImportOptions, subMeshes);
+
+		// TODO - Later: Optimize mesh: Remove bad and degenerate polygons, weld nearby vertices, optimize for vertex cache
+
+		shutDownSdk();
+
+		INT32 usage = MU_STATIC;
+		if (meshImportOptions->getCPUReadable())
+			usage |= MU_CPUCACHED;
+
+		MeshPtr mesh = Mesh::_createPtr(rendererMeshData->getData(), subMeshes, usage);
+
+		WString fileName = filePath.getWFilename(false);
+		mesh->setName(fileName);
+
+		return mesh;
+	}
+
+	bool FBXImporter::startUpSdk(FbxScene*& scene)
+	{
+		mFBXManager = FbxManager::Create();
+		if (mFBXManager == nullptr)
+		{
+			LOGERR("FBX import failed: FBX SDK failed to initialize. FbxManager::Create() failed.");
+			return false;
+		}
+
+		FbxIOSettings* ios = FbxIOSettings::Create(mFBXManager, IOSROOT);
+		mFBXManager->SetIOSettings(ios);
+
+		scene = FbxScene::Create(mFBXManager, "Import Scene");
+		if (scene == nullptr)
+		{
+			LOGWRN("FBX import failed: Failed to create FBX scene.");
+			return false;
+		}
+
+		return true;
+	}
+
+	void FBXImporter::shutDownSdk()
+	{
+		mFBXManager->Destroy();
+		mFBXManager = nullptr;
+	}
+
+	bool FBXImporter::loadFBXFile(FbxScene* scene, const Path& filePath)
+	{
+		int lFileMajor, lFileMinor, lFileRevision;
+		int lSDKMajor,  lSDKMinor,  lSDKRevision;
+		FbxManager::GetFileFormatVersion(lSDKMajor, lSDKMinor, lSDKRevision);
+
+		FbxImporter* importer = FbxImporter::Create(mFBXManager, "");
+		bool importStatus = importer->Initialize(filePath.toString().c_str(), -1, mFBXManager->GetIOSettings());
+		
+		importer->GetFileVersion(lFileMajor, lFileMinor, lFileRevision);
+
+		if(!importStatus)
+		{
+			LOGERR("FBX import failed: Call to FbxImporter::Initialize() failed.\n" +
+				String("Error returned: %s\n\n") + String(importer->GetStatus().GetErrorString()));
+			return false;
+		}
+
+		mFBXManager->GetIOSettings()->SetBoolProp(IMP_FBX_TEXTURE, false);
+		mFBXManager->GetIOSettings()->SetBoolProp(IMP_FBX_GOBO, false);
+
+		importStatus = importer->Import(scene);
+
+		if(!importStatus)
+		{
+			importer->Destroy();
+			
+			LOGERR("FBX import failed: Call to FbxImporter::Import() failed.\n" +
+				String("Error returned: %s\n\n") + String(importer->GetStatus().GetErrorString()));
+			return false;
+		}
+
+		FbxAxisSystem fileCoordSystem = scene->GetGlobalSettings().GetAxisSystem();
+		FbxAxisSystem bsCoordSystem(FbxAxisSystem::eYAxis, FbxAxisSystem::eParityOdd, FbxAxisSystem::eRightHanded);
+		if (fileCoordSystem != bsCoordSystem)
+			bsCoordSystem.ConvertScene(scene);
+
+		importer->Destroy();
+		return true;
+	}
+
+	void FBXImporter::parseScene(FbxScene* scene, const FBXImportOptions& options, FBXImportScene& outputScene)
+	{
+		outputScene.rootNode = createImportNode(outputScene, scene->GetRootNode(), nullptr);
+
+		Stack<FbxNode*> todo;
+		todo.push(scene->GetRootNode());
+
+		while(!todo.empty())
+		{
+			FbxNode* curNode = todo.top();
+			FBXImportNode* curImportNode = outputScene.nodeMap[curNode];
+			todo.pop();
+
+			const char* name = curNode->GetName();
+
+			FbxNodeAttribute* attrib = curNode->GetNodeAttribute();
+			if(attrib != nullptr)
+			{
+				FbxNodeAttribute::EType attribType = attrib->GetAttributeType();
+
+				switch(attribType)
+				{
+				case FbxNodeAttribute::eNurbs:
+				case FbxNodeAttribute::eNurbsSurface:
+				case FbxNodeAttribute::ePatch:
+				{
+					FbxGeometryConverter geomConverter(mFBXManager);
+					attrib = geomConverter.Triangulate(attrib, true);
+
+					if (attrib->GetAttributeType() == FbxNodeAttribute::eMesh)
+					{
+						FbxMesh* mesh = static_cast<FbxMesh*>(attrib);
+						mesh->RemoveBadPolygons();
+
+						parseMesh(mesh, curImportNode, options, outputScene);
+					}
+				}
+					break;
+				case FbxNodeAttribute::eMesh:
+					{
+						FbxMesh* mesh = static_cast<FbxMesh*>(attrib);
+						mesh->RemoveBadPolygons();
+
+						if(!mesh->IsTriangleMesh())
+						{
+							FbxGeometryConverter geomConverter(mFBXManager);
+							geomConverter.Triangulate(mesh, true);
+							attrib = curNode->GetNodeAttribute();
+							mesh = static_cast<FbxMesh*>(attrib);
+						}
+
+						parseMesh(mesh, curImportNode, options, outputScene);
+					}
+					break;
+				}
+			}
+
+			for (int i = 0; i < curNode->GetChildCount(); i++)
+			{
+				FbxNode* childNode = curNode->GetChild(i);
+				createImportNode(outputScene, childNode, curImportNode);
+
+				todo.push(childNode);
+			}
+		}
+	}
+
+	FBXImportNode* FBXImporter::createImportNode(FBXImportScene& scene, FbxNode* fbxNode, FBXImportNode* parent)
+	{
+		FBXImportNode* node = bs_new<FBXImportNode>();
+
+		Vector3 translation = FBXToNativeType(fbxNode->LclTranslation.Get());
+		Vector3 rotationEuler = FBXToNativeType(fbxNode->LclRotation.Get());
+		Vector3 scale = FBXToNativeType(fbxNode->LclScaling.Get());
+
+		Quaternion rotation((Radian)rotationEuler.x, (Radian)rotationEuler.y, (Radian)rotationEuler.z);
+
+		node->localTransform.setTRS(translation, rotation, scale);
+		node->fbxNode = fbxNode;
+
+		if (parent != nullptr)
+		{
+			node->worldTransform = node->localTransform * parent->worldTransform;
+
+			parent->children.push_back(node);
+		}
+		else
+			node->worldTransform = node->localTransform;
+
+		scene.nodeMap.insert(std::make_pair(fbxNode, node));
+
+		return node;
+	}
+
+	void FBXImporter::splitMeshVertices(FBXImportScene& scene)
+	{
+		Vector<FBXImportMesh*> splitMeshes;
+
+		for (auto& mesh : scene.meshes)
+		{
+			FBXImportMesh* splitMesh = bs_new<FBXImportMesh>();
+			splitMesh->fbxMesh = mesh->fbxMesh;
+			splitMesh->referencedBy = mesh->referencedBy;
+			splitMesh->bones = mesh->bones;
+
+			FBXUtility::splitVertices(*mesh, *splitMesh);
+			FBXUtility::flipWindingOrder(*splitMesh);
+			splitMeshes.push_back(splitMesh);
+
+			bs_delete(mesh);
+		}
+
+		scene.meshes = splitMeshes;
+	}
+
+	RendererMeshDataPtr FBXImporter::generateMeshData(const FBXImportScene& scene, const FBXImportOptions& options, Vector<SubMesh>& outputSubMeshes)
+	{
+		Matrix4 importScale = Matrix4::scaling(options.importScale);
+
+		Vector<MeshDataPtr> allMeshData;
+		Vector<Vector<SubMesh>> allSubMeshes;
+
+		for (auto& mesh : scene.meshes)
+		{
+			Vector<Vector<UINT32>> indicesPerMaterial;
+			for (UINT32 i = 0; i < (UINT32)mesh->indices.size(); i++)
+			{
+				while (mesh->materials[i] >= indicesPerMaterial.size())
+					indicesPerMaterial.push_back(Vector<UINT32>());
+
+				indicesPerMaterial[mesh->materials[i]].push_back(mesh->indices[i]);
+			}
+
+			UINT32* orderedIndices = (UINT32*)bs_alloc((UINT32)mesh->indices.size() * sizeof(UINT32));
+			Vector<SubMesh> subMeshes;
+			UINT32 currentIndex = 0;
+
+			for (auto& subMeshIndices : indicesPerMaterial)
+			{
+				UINT32 indexCount = (UINT32)subMeshIndices.size();
+				UINT32* dest = orderedIndices + currentIndex;
+				memcpy(dest, subMeshIndices.data(), indexCount * sizeof(UINT32));
+
+				subMeshes.push_back(SubMesh(currentIndex, indexCount, DOT_TRIANGLE_LIST));
+
+				currentIndex += indexCount;
+			}
+
+			UINT32 vertexLayout = (UINT32)VertexLayout::Position;
+
+			size_t numVertices = mesh->positions.size();
+			bool hasColors = mesh->colors.size() == numVertices;
+			bool hasNormals = mesh->normals.size() == numVertices;
+
+			if (hasColors)
+				vertexLayout |= (UINT32)VertexLayout::Color;
+
+			bool hasTangents = false;
+			if (hasNormals)
+			{
+				vertexLayout |= (UINT32)VertexLayout::Normal;
+
+				if (mesh->tangents.size() == numVertices &&
+					mesh->bitangents.size() == numVertices)
+				{
+					vertexLayout |= (UINT32)VertexLayout::Tangent;
+					hasTangents = true;
+				}
+			}
+
+			int UVIdx = 0;
+			for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++)
+			{
+				if (mesh->UV[i].size() == numVertices)
+				{
+					if (i == 0)
+						vertexLayout |= (UINT32)VertexLayout::UV0;
+					else if (i == 1)
+						vertexLayout |= (UINT32)VertexLayout::UV1;
+				}
+			}
+
+			UINT32 numIndices = (UINT32)mesh->indices.size();
+			for (auto& node : mesh->referencedBy)
+			{
+				Matrix4 worldTransform = node->worldTransform * importScale;
+				Matrix4 worldTransformIT = worldTransform.transpose();
+				worldTransformIT = worldTransformIT.inverse();
+
+				RendererMeshDataPtr meshData = RendererMeshData::create((UINT32)numVertices, numIndices, (VertexLayout)vertexLayout);
+
+				// Copy indices
+				meshData->setIndices((UINT32*)mesh->indices.data(), numIndices * sizeof(UINT32));
+
+				// Copy & transform positions
+				UINT32 positionsSize = sizeof(Vector3) * (UINT32)numVertices;
+				Vector3* transformedPositions = (Vector3*)bs_stack_alloc(positionsSize);
+
+				for (UINT32 i = 0; i < (UINT32)numVertices; i++)
+					transformedPositions[i] = worldTransform.multiplyAffine((Vector3)mesh->positions[i]);
+
+				meshData->setPositions(transformedPositions, positionsSize);
+				bs_stack_free(transformedPositions);
+
+				// Copy & transform normals
+				if (hasNormals)
+				{
+					UINT32 normalsSize = sizeof(Vector3) * (UINT32)numVertices;
+					Vector3* transformedNormals = (Vector3*)bs_stack_alloc(normalsSize);
+
+					// Copy, convert & transform tangents & bitangents
+					if (hasTangents)
+					{
+						UINT32 tangentsSize = sizeof(Vector4) * (UINT32)numVertices;
+						Vector4* transformedTangents = (Vector4*)bs_stack_alloc(tangentsSize);
+
+						for (UINT32 i = 0; i < (UINT32)numVertices; i++)
+						{
+							Vector3 normal = (Vector3)mesh->normals[i];
+							transformedNormals[i] = worldTransformIT.multiplyAffine(normal);
+
+							Vector3 tangent = (Vector3)mesh->tangents[i];
+							tangent = worldTransformIT.multiplyAffine(tangent);
+
+							Vector3 bitangent = (Vector3)mesh->bitangents[i];
+							bitangent = worldTransformIT.multiplyAffine(bitangent);
+
+							Vector3 engineBitangent = Vector3::cross(normal, tangent);
+							float sign = Vector3::dot(engineBitangent, bitangent);
+
+							transformedTangents[i] = Vector4(tangent.x, tangent.y, tangent.z, sign > 0 ? 1.0f : -1.0f);
+						}
+
+						meshData->setTangents(transformedTangents, tangentsSize);
+						bs_stack_free(transformedTangents);
+					}
+					else // Just normals
+					{
+						for (UINT32 i = 0; i < (UINT32)numVertices; i++)
+							transformedNormals[i] = worldTransformIT.multiplyAffine((Vector3)mesh->normals[i]);
+					}
+
+					meshData->setNormals(transformedNormals, normalsSize);
+					bs_stack_free(transformedNormals);
+				}
+
+				// Copy colors
+				if (hasColors)
+				{
+					meshData->setColors(mesh->colors.data(), sizeof(UINT32) * (UINT32)numVertices);
+				}
+
+				// Copy UV
+				int writeUVIDx = 0;
+				for (auto& uvLayer : mesh->UV)
+				{
+					if (uvLayer.size() == numVertices)
+					{
+						UINT32 size = sizeof(Vector2) * (UINT32)numVertices;
+						Vector2* transformedUV = (Vector2*)bs_stack_alloc(size);
+
+						UINT32 i = 0;
+						for (auto& uv : uvLayer)
+						{
+							transformedUV[i] = uv;
+							transformedUV[i].y = 1.0f - uv.y;
+
+							i++;
+						}
+
+						if (writeUVIDx == 0)
+							meshData->setUV0(transformedUV, size);
+						else if (writeUVIDx == 1)
+							meshData->setUV1(transformedUV, size);
+
+						bs_stack_free(transformedUV);
+
+						writeUVIDx++;
+					}
+				}
+
+				allMeshData.push_back(meshData->getData());
+				allSubMeshes.push_back(subMeshes);
+			}
+		}
+
+		if (allMeshData.size() > 1)
+		{
+			return RendererMeshData::create(MeshData::combine(allMeshData, allSubMeshes, outputSubMeshes));
+		}
+		else if (allMeshData.size() == 1)
+		{
+			outputSubMeshes = allSubMeshes[0];
+			return RendererMeshData::create(allMeshData[0]);
+		}
+
+		return nullptr;
+	}
+
+	template<class TFBX, class TNative>
+	class FBXDirectIndexer
+	{
+	public:
+		FBXDirectIndexer(const FbxLayerElementTemplate<TFBX>& layer)
+			:mElementArray(layer.GetDirectArray()),
+			mElementCount(mElementArray.GetCount())
+		{}
+
+		bool get(int index, TNative& output) const
+		{
+			if (index < 0 || index >= mElementCount)
+				return false;
+
+			output = FBXToNativeType(mElementArray.GetAt(index));
+			return true;
+		}
+
+		bool isEmpty() const
+		{
+			return mElementCount == 0;
+		}
+
+	private:
+		const FbxLayerElementArrayTemplate<TFBX>& mElementArray;
+		int mElementCount;
+	};
+
+	template<class TFBX, class TNative>
+	class FBXIndexIndexer
+	{
+	public:
+		FBXIndexIndexer(const FbxLayerElementTemplate<TFBX>& layer)
+			:mElementArray(layer.GetDirectArray()),
+			mIndexArray(layer.GetIndexArray()),
+			mElementCount(mElementArray.GetCount()),
+			mIndexCount(mIndexArray.GetCount())
+		{}
+
+		bool get(int index, TNative& output) const
+		{
+			if (index < 0 || index >= mIndexCount)
+				return false;
+
+			int actualIndex = mIndexArray.GetAt(index);
+
+			if (actualIndex < 0 || actualIndex >= mElementCount)
+				return false;
+
+			output = FBXToNativeType(mElementArray.GetAt(actualIndex));
+			return true;
+		}
+
+		bool isEmpty() const
+		{
+			return mElementCount == 0 || mIndexCount == 0;
+		}
+
+	private:
+		const FbxLayerElementArrayTemplate<TFBX>& mElementArray;
+		const FbxLayerElementArrayTemplate<int>& mIndexArray;
+		int mElementCount;
+		int mIndexCount;
+	};
+
+	template<class TFBX, class TNative, class TIndexer>
+	void readLayerData(FbxLayerElementTemplate<TFBX>& layer, Vector<TNative>& output, const Vector<int>& indices)
+	{
+		TIndexer indexer(layer);
+		if (indexer.isEmpty())
+			return;
+
+		output.resize(indices.size());
+
+		FbxLayerElement::EMappingMode mappingMode = layer.GetMappingMode();
+
+		UINT32 indexCount = (UINT32)indices.size();
+		switch (mappingMode)
+		{
+		case FbxLayerElement::eByControlPoint:
+			for (UINT32 i = 0; i < indexCount; i++)
+			{
+				int index = indices[i];
+				indexer.get(index, output[i]);
+			}
+			break;
+		case FbxLayerElement::eByPolygonVertex:
+			for (UINT32 i = 0; i < indexCount; i++)
+				indexer.get(i, output[i]);
+			break;
+		case FbxLayerElement::eByPolygon:
+			// We expect mesh to be triangulated here
+		{
+			UINT32 polygonCount = indexCount / 3;
+			UINT32 index = 0;
+
+			for (UINT32 i = 0; i < polygonCount; i++)
+			{
+				TNative value;
+				indexer.get(i, value);
+
+				output[index++] = value;
+				output[index++] = value;
+				output[index++] = value;
+			}
+		}
+			break;
+		case FbxLayerElement::eAllSame:
+		{
+			TNative value;
+			indexer.get(0, value);
+
+			for (UINT32 i = 0; i < indexCount; i++)
+				output[i] = value;
+		}
+			break;
+		default:
+			LOGWRN("FBX Import: Unsupported layer mapping mode.");
+			break;
+		}
+	}
+
+	template<class TFBX, class TNative>
+	void readLayerData(FbxLayerElementTemplate<TFBX>& layer, Vector<TNative>& output, const Vector<int>& indices)
+	{
+		FbxLayerElement::EReferenceMode refMode = layer.GetReferenceMode();
+
+		if (refMode == FbxLayerElement::eDirect)
+			readLayerData<TFBX, TNative, FBXDirectIndexer<TFBX, TNative> >(layer, output, indices);
+		else if (refMode == FbxLayerElement::eIndexToDirect)
+			readLayerData<TFBX, TNative, FBXIndexIndexer<TFBX, TNative> >(layer, output, indices);
+		else
+			LOGWRN("FBX Import: Unsupported layer reference mode.");
+	}
+
+	void FBXImporter::parseMesh(FbxMesh* mesh, FBXImportNode* parentNode, const FBXImportOptions& options, FBXImportScene& outputScene)
+	{
+		// Check if valid
+		if (!mesh->IsTriangleMesh())
+			return;
+
+		UINT32 vertexCount = mesh->GetControlPointsCount();
+		UINT32 triangleCount = mesh->GetPolygonCount();
+
+		if (vertexCount == 0 || triangleCount == 0)
+			return;
+
+		// Register in global mesh array
+		FBXImportMesh* importMesh = nullptr;
+
+		auto iterFindMesh = outputScene.meshMap.find(mesh);
+		if (iterFindMesh != outputScene.meshMap.end())
+		{
+			UINT32 meshIdx = iterFindMesh->second;
+			outputScene.meshes[meshIdx]->referencedBy.push_back(parentNode);
+
+			return;
+		}
+		else
+		{
+			importMesh = bs_new<FBXImportMesh>();
+			outputScene.meshes.push_back(importMesh);
+
+			importMesh->referencedBy.push_back(parentNode);
+			importMesh->fbxMesh = mesh;
+			outputScene.meshMap[mesh] = (UINT32)outputScene.meshes.size() - 1;
+		}
+
+		// Import vertices
+		importMesh->positions.resize(vertexCount);
+		FbxVector4* controlPoints = mesh->GetControlPoints();
+
+		for (UINT32 i = 0; i < vertexCount; i++)
+			importMesh->positions[i] = FBXToNativeType(controlPoints[i]);
+
+		// Import triangles
+		UINT32 indexCount = triangleCount * 3;
+		importMesh->indices.resize(indexCount);
+
+		int* fbxIndices = mesh->GetPolygonVertices();
+		importMesh->indices.assign(fbxIndices, fbxIndices + indexCount);
+
+		// Import UVs
+		Vector<FbxLayerElementUV*> fbxUVLayers;
+
+		//// Search the diffuse layers first
+		for (UINT32 i = 0; i < FBX_IMPORT_MAX_UV_LAYERS; i++)
+		{
+			FbxLayer* layer = mesh->GetLayer(i, FbxLayerElement::eUV);
+			if (layer == nullptr)
+				continue;
+
+			for (int j = FbxLayerElement::eTextureDiffuse; j < FbxLayerElement::eTypeCount; j++)
+			{
+				FbxLayerElementUV* uvLayer = layer->GetUVs((FbxLayerElement::EType)j);
+				if (uvLayer == nullptr)
+					continue;
+
+				fbxUVLayers.push_back(uvLayer);
+
+				if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
+					break;
+			}
+
+			if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
+				break;
+		}
+
+		//// If there's room, search all others too
+		if (fbxUVLayers.size() < FBX_IMPORT_MAX_UV_LAYERS)
+		{
+			UINT32 numLayers = mesh->GetLayerCount();
+			for (UINT32 i = 0; i < numLayers; i++)
+			{
+				FbxLayer* layer = mesh->GetLayer(i);
+				if (layer == nullptr)
+					continue;
+
+				for (int j = FbxLayerElement::eTextureDiffuse; j < FbxLayerElement::eTypeCount; j++)
+				{
+					FbxLayerElementUV* uvLayer = layer->GetUVs((FbxLayerElement::EType)j);
+					if (uvLayer == nullptr)
+						continue;
+
+					auto iterFind = std::find(fbxUVLayers.begin(), fbxUVLayers.end(), uvLayer);
+					if (iterFind != fbxUVLayers.end())
+						continue;
+
+					fbxUVLayers.push_back(uvLayer);
+
+					if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
+						break;
+				}
+
+				if (fbxUVLayers.size() == FBX_IMPORT_MAX_UV_LAYERS)
+					break;
+			}
+		}
+
+		for (size_t i = 0; i < fbxUVLayers.size(); i++)
+			readLayerData(*fbxUVLayers[i], importMesh->UV[i], importMesh->indices);
+
+		FbxLayer* mainLayer = mesh->GetLayer(0);
+		if (mainLayer != nullptr)
+		{
+			// Import colors
+			if (mainLayer->GetVertexColors() != nullptr)
+				readLayerData(*mainLayer->GetVertexColors(), importMesh->colors, importMesh->indices);
+
+			// Import normals
+			if (options.importNormals)
+			{
+				bool hasNormals = mainLayer->GetNormals() != nullptr;
+
+				if (!hasNormals)
+				{
+					if (mainLayer->GetSmoothing() != nullptr)
+					{
+						FbxLayerElementSmoothing* smoothing = mainLayer->GetSmoothing();
+
+						if (smoothing->GetMappingMode() == FbxLayerElement::eByEdge)
+						{
+							FbxGeometryConverter converter(mFBXManager);
+							converter.ComputePolygonSmoothingFromEdgeSmoothing(mesh, 0);
+						}
+
+						readLayerData(*smoothing, importMesh->smoothingGroups, importMesh->indices);
+
+						if (!importMesh->smoothingGroups.empty())
+						{
+							FBXUtility::normalsFromSmoothing(importMesh->positions, importMesh->indices, 
+								importMesh->smoothingGroups, importMesh->normals);
+						}
+					}
+				}
+				else
+					readLayerData(*mainLayer->GetNormals(), importMesh->normals, importMesh->indices);
+			}
+
+			// Import tangents
+			if (options.importTangents)
+			{
+				bool hasTangents = mainLayer->GetTangents() != nullptr && mainLayer->GetBinormals() != nullptr;
+
+				if (!hasTangents)
+				{
+					if (fbxUVLayers.size() > 0)
+						hasTangents = mesh->GenerateTangentsData(0, false);
+				}
+
+				if (hasTangents)
+				{
+					readLayerData(*mainLayer->GetTangents(), importMesh->tangents, importMesh->indices);
+					readLayerData(*mainLayer->GetBinormals(), importMesh->bitangents, importMesh->indices);
+				}
+			}
+
+			// Import material indexes
+			if (mainLayer->GetMaterials() != nullptr)
+			{
+				Vector<FbxSurfaceMaterial*> fbxMaterials;
+
+				readLayerData(*mainLayer->GetMaterials(), fbxMaterials, importMesh->indices);
+
+				UnorderedMap<FbxSurfaceMaterial*, int> materialLookup;
+				int nextMaterialIdx = 0;
+				for (UINT32 i = 0; i < (UINT32)fbxMaterials.size(); i++)
+				{
+					auto iterFind = materialLookup.find(fbxMaterials[i]);
+
+					int materialIdx = 0;
+					if (iterFind != materialLookup.end())
+						materialIdx = iterFind->second;
+					else
+					{
+						materialIdx = nextMaterialIdx++;
+						materialLookup[fbxMaterials[i]] = materialIdx;
+					}
+
+					importMesh->materials.push_back(materialIdx);
+				}
+			}
+		}
+	}
+
+	void FBXImporter::importBlendShapes(FBXImportScene& scene, const FBXImportOptions& options)
+	{
+		for (auto& mesh : scene.meshes)
+		{
+			FbxMesh* fbxMesh = mesh->fbxMesh;
+
+			UINT32 deformerCount = (UINT32)fbxMesh->GetDeformerCount(FbxDeformer::eBlendShape);
+			for (UINT32 i = 0; i < deformerCount; i++)
+			{
+				FbxBlendShape* deformer = static_cast<FbxBlendShape*>(fbxMesh->GetDeformer(i, FbxDeformer::eBlendShape));
+
+				UINT32 blendShapeChannelCount = (UINT32)deformer->GetBlendShapeChannelCount();
+				for (UINT32 j = 0; j < blendShapeChannelCount; ++j)
+				{
+					FbxBlendShapeChannel* channel = deformer->GetBlendShapeChannel(j);
+					double* weights = channel->GetTargetShapeFullWeights();
+
+					UINT32 frameCount = channel->GetTargetShapeCount();
+					if (frameCount == 0)
+						continue;
+
+					mesh->blendShapes.push_back(FBXBlendShape());
+					FBXBlendShape& blendShape = mesh->blendShapes.back();
+					blendShape.name = channel->GetName();
+					blendShape.frames.resize(frameCount);
+
+					for (UINT32 k = 0; k < frameCount; k++)
+					{
+						FbxShape* fbxShape = channel->GetTargetShape(k);
+
+						FBXBlendShapeFrame& frame = blendShape.frames[k];
+						frame.weight = (float)weights[k];
+
+						importBlendShapeFrame(fbxShape, *mesh, options, frame);
+					}
+				}
+			}
+		}
+	}
+
+	void FBXImporter::importBlendShapeFrame(FbxShape* shape, const FBXImportMesh& mesh, const FBXImportOptions& options, FBXBlendShapeFrame& outFrame)
+	{
+		UINT32 vertexCount = (UINT32)shape->GetControlPointsCount();
+		outFrame.positions.resize(vertexCount);
+		FbxVector4* controlPoints = shape->GetControlPoints();
+
+		for (UINT32 i = 0; i < vertexCount; i++)
+			outFrame.positions[i] = FBXToNativeType(controlPoints[i]);
+
+		FbxLayer* mainLayer = shape->GetLayer(0);
+		if (options.importNormals)
+		{
+			bool hasNormals = mainLayer->GetNormals() != nullptr;
+
+			if (!hasNormals)
+			{
+				if (!mesh.smoothingGroups.empty())
+				{
+					FBXUtility::normalsFromSmoothing(outFrame.positions, mesh.indices,
+						mesh.smoothingGroups, outFrame.normals);
+				}
+			}
+			else
+				readLayerData(*mainLayer->GetNormals(), outFrame.normals, mesh.indices);
+		}
+
+		if (options.importTangents)
+		{
+			bool hasTangents = mainLayer->GetTangents() != nullptr && mainLayer->GetBinormals() != nullptr;
+
+			if (hasTangents)
+			{
+				readLayerData(*mainLayer->GetTangents(), outFrame.tangents, mesh.indices);
+				readLayerData(*mainLayer->GetBinormals(), outFrame.bitangents, mesh.indices);
+			}
+		}
+	}
+
+	void FBXImporter::importSkin(FBXImportScene& scene)
+	{
+		for (auto& mesh : scene.meshes)
+		{
+			FbxMesh* fbxMesh = mesh->fbxMesh;
+
+			UINT32 deformerCount = (UINT32)fbxMesh->GetDeformerCount(FbxDeformer::eSkin);
+			if (deformerCount > 0)
+			{
+				// We ignore other deformers if there's more than one
+				FbxSkin* deformer = static_cast<FbxSkin*>(fbxMesh->GetDeformer(0, FbxDeformer::eSkin));
+				UINT32 boneCount = (UINT32)deformer->GetClusterCount();
+
+				if (boneCount == 0)
+					continue;
+
+				// If only one bone and it links to itself, ignore the bone
+				if (boneCount == 1)
+				{
+					FbxCluster* cluster = deformer->GetCluster(0);
+					if (mesh->referencedBy.size() == 1 && mesh->referencedBy[0]->fbxNode == cluster->GetLink())
+						continue;
+				}
+
+				importSkin(scene, deformer, *mesh);
+			}
+		}
+	}
+
+	void FBXImporter::importSkin(FBXImportScene& scene, FbxSkin* skin, FBXImportMesh& mesh)
+	{
+		Vector<FBXBoneInfluence>& influences = mesh.boneInfluences;
+		influences.resize(mesh.positions.size());
+
+		UnorderedSet<FbxNode*> existingBones;
+		UINT32 boneCount = (UINT32)skin->GetClusterCount();
+		for (UINT32 i = 0; i < boneCount; i++)
+		{
+			FbxCluster* cluster = skin->GetCluster(i);
+			FbxNode* link = cluster->GetLink();
+
+			// The bone node doesn't exist, skip it
+			auto iterFind = scene.nodeMap.find(link);
+			if (iterFind == scene.nodeMap.end())
+				continue;
+
+			mesh.bones.push_back(FBXBone());
+
+			FBXBone& bone = mesh.bones.back();
+			bone.node = iterFind->second;
+
+			FbxAMatrix clusterTransform;
+			cluster->GetTransformMatrix(clusterTransform);
+
+			FbxAMatrix linkTransform;
+			cluster->GetTransformLinkMatrix(linkTransform);
+
+			FbxAMatrix bindPose = linkTransform.Inverse() * clusterTransform;
+			bone.bindPose = FBXToNativeType(bindPose);
+
+			bool isDuplicate = existingBones.insert(link).second;
+			bool isAdditive = cluster->GetLinkMode() == FbxCluster::eAdditive;
+
+			// We avoid importing weights twice for duplicate bones and we don't
+			// support additive link mode.
+			bool importWeights = !isDuplicate && !isAdditive;
+			if (!importWeights)
+				continue;
+
+			double* weights = cluster->GetControlPointWeights();
+			INT32* indices = cluster->GetControlPointIndices();
+			UINT32 numIndices = (UINT32)cluster->GetControlPointIndicesCount();
+			INT32 numVertices = (INT32)influences.size();
+
+			// Add new weights while keeping them in order and removing the smallest ones
+			// if number of influences exceeds the set maximum value
+			for (UINT32 j = 0; j < numIndices; j++)
+			{
+				INT32 vertexIndex = indices[j];
+				float weight = (float)weights[j];
+
+				for (UINT32 k = 0; k < FBX_IMPORT_MAX_BONE_INFLUENCES; k++)
+				{
+					if (vertexIndex < 0 || vertexIndex >= numVertices)
+						continue;
+
+					if (weight >= influences[vertexIndex].weights[k])
+					{
+						for (UINT32 l = FBX_IMPORT_MAX_BONE_INFLUENCES - 2; l >= k; l--)
+						{
+							influences[vertexIndex].weights[l + 1] = influences[vertexIndex].weights[l];
+							influences[vertexIndex].indices[l + 1] = influences[vertexIndex].indices[l];
+						}
+
+						influences[vertexIndex].weights[k] = weight;
+						influences[vertexIndex].indices[k] = i;
+						break;
+					}
+				}
+			}
+		}
+
+		if (mesh.bones.empty())
+			mesh.boneInfluences.clear();
+
+		// Normalize weights
+		UINT32 numInfluences = (UINT32)mesh.boneInfluences.size();
+		for (UINT32 i = 0; i < numInfluences; i++)
+		{
+			float sum = 0.0f;
+			for (UINT32 j = 0; j < FBX_IMPORT_MAX_BONE_INFLUENCES; j++)
+				sum += influences[i].weights[j];
+
+			float invSum = 1.0f / sum;
+			for (UINT32 j = 0; j < FBX_IMPORT_MAX_BONE_INFLUENCES; j++)
+				influences[i].weights[j] *= invSum;
+		}
+	}
+
+	void FBXImporter::generateMissingTangentSpace(FBXImportScene& scene, const FBXImportOptions& options)
+	{
+		for (auto& mesh : scene.meshes)
+		{
+			UINT32 numVertices = (UINT32)mesh->positions.size();
+			UINT32 numIndices = (UINT32)mesh->indices.size();
+
+			if ((options.importNormals || options.importTangents) && mesh->normals.empty())
+			{
+				mesh->normals.resize(numVertices);
+
+				MeshUtility::calculateNormals(mesh->positions.data(), (UINT8*)mesh->indices.data(), numVertices, numIndices, mesh->normals.data());
+			}
+
+			if (options.importTangents && !mesh->UV[0].empty() && (mesh->tangents.empty() || mesh->bitangents.empty()))
+			{
+				mesh->tangents.resize(numVertices);
+				mesh->bitangents.resize(numVertices);
+
+				MeshUtility::calculateTangents(mesh->positions.data(), mesh->normals.data(), mesh->UV[0].data(), (UINT8*)mesh->indices.data(), 
+					numVertices, numIndices, mesh->tangents.data(), mesh->bitangents.data());
+			}
+
+			for (auto& shape : mesh->blendShapes)
+			{
+				for (auto& frame : shape.frames)
+				{
+					if ((options.importNormals || options.importTangents) && frame.normals.empty())
+					{
+						frame.normals.resize(numVertices);
+
+						MeshUtility::calculateNormals(mesh->positions.data(), (UINT8*)mesh->indices.data(), numVertices, numIndices, frame.normals.data());
+					}
+
+					if (options.importTangents && !mesh->UV[0].empty() && (frame.tangents.empty() || frame.bitangents.empty()))
+					{
+						mesh->tangents.resize(numVertices);
+						mesh->bitangents.resize(numVertices);
+
+						MeshUtility::calculateTangents(mesh->positions.data(), frame.normals.data(), mesh->UV[0].data(), (UINT8*)mesh->indices.data(),
+							numVertices, numIndices, frame.tangents.data(), frame.bitangents.data());
+					}
+				}
+			}
+		}
+	}
+
+	void FBXImporter::importAnimations(FbxScene* scene, FBXImportOptions& importOptions, FBXImportScene& importScene)
+	{
+		FbxNode* root = scene->GetRootNode();
+
+		UINT32 numAnimStacks = (UINT32)scene->GetSrcObjectCount<FbxAnimStack>();
+		for (UINT32 i = 0; i < numAnimStacks; i++)
+		{
+			FbxAnimStack* animStack = scene->GetSrcObject<FbxAnimStack>(i);
+
+			importScene.clips.push_back(FBXAnimationClip());
+			FBXAnimationClip& clip = importScene.clips.back();
+			clip.name = animStack->GetName();
+
+			FbxTimeSpan timeSpan = animStack->GetLocalTimeSpan();
+			clip.start = (float)timeSpan.GetStart().GetSecondDouble();
+			clip.end = (float)timeSpan.GetStop().GetSecondDouble();
+
+			UINT32 layerCount = animStack->GetMemberCount<FbxAnimLayer>();
+			if (layerCount > 1)
+			{
+				FbxAnimEvaluator* evaluator = scene->GetAnimationEvaluator();
+
+				FbxTime startTime;
+				startTime.SetSecondDouble(clip.start);
+
+				FbxTime endTime;
+				endTime.SetSecondDouble(clip.end);
+
+				FbxTime sampleRate;
+
+				if (importOptions.animResample)
+					sampleRate.SetSecondDouble(importOptions.animSampleRate);
+				else
+				{
+					FbxTime::EMode timeMode = scene->GetGlobalSettings().GetTimeMode();
+					sampleRate.SetSecondDouble(1.0f / FbxTime::GetFrameRate(timeMode));
+				}
+
+				if (!animStack->BakeLayers(evaluator, startTime, endTime, sampleRate))
+					continue;
+
+				layerCount = animStack->GetMemberCount<FbxAnimLayer>();
+			}
+
+			if (layerCount == 1)
+			{
+				FbxAnimLayer* animLayer = animStack->GetMember<FbxAnimLayer>(0);
+
+				importAnimations(animLayer, root, importOptions, clip, importScene);
+			}
+		}
+	}
+
+	void FBXImporter::importAnimations(FbxAnimLayer* layer, FbxNode* node, FBXImportOptions& importOptions,
+		FBXAnimationClip& clip, FBXImportScene& importScene)
+	{
+		FbxAnimCurve* translation[3];
+		translation[0] = node->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
+		translation[1] = node->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y);
+		translation[2] = node->LclTranslation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z);
+
+		FbxAnimCurve* rotation[3];
+		rotation[0] = node->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
+		rotation[1] = node->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y);
+		rotation[2] = node->LclRotation.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z);
+
+		FbxAnimCurve* scale[3];
+		scale[0] = node->LclScaling.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_X);
+		scale[1] = node->LclScaling.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Y);
+		scale[2] = node->LclScaling.GetCurve(layer, FBXSDK_CURVENODE_COMPONENT_Z);
+
+		auto hasCurveValues = [](FbxAnimCurve* curves[3])
+		{
+			for (UINT32 i = 0; i < 3; i++)
+			{
+				if (curves[i] != nullptr && curves[i]->KeyGetCount() > 0)
+					return true;
+			}
+
+			return false;
+		};
+
+		bool hasBoneAnimation = hasCurveValues(translation) || hasCurveValues(rotation) || hasCurveValues(scale);
+		if (hasBoneAnimation)
+		{
+			clip.boneAnimations.push_back(FBXBoneAnimation());
+			FBXBoneAnimation& boneAnim = clip.boneAnimations.back();
+			boneAnim.node = importScene.nodeMap[node];
+
+			importCurve(translation[0], importOptions, boneAnim.translation[0], clip.start, clip.end);
+			importCurve(translation[1], importOptions, boneAnim.translation[1], clip.start, clip.end);
+			importCurve(translation[2], importOptions, boneAnim.translation[2], clip.start, clip.end);
+
+			importCurve(scale[0], importOptions, boneAnim.scale[0], clip.start, clip.end);
+			importCurve(scale[1], importOptions, boneAnim.scale[1], clip.start, clip.end);
+			importCurve(scale[2], importOptions, boneAnim.scale[2], clip.start, clip.end);
+
+			FBXAnimationCurve tempCurveRotation[3];
+			importCurve(rotation[0], importOptions, tempCurveRotation[0], clip.start, clip.end);
+			importCurve(rotation[1], importOptions, tempCurveRotation[1], clip.start, clip.end);
+			importCurve(rotation[2], importOptions, tempCurveRotation[2], clip.start, clip.end);
+
+			eulerToQuaternionCurves(tempCurveRotation, boneAnim.rotation);
+		}
+
+		if (importOptions.importBlendShapes)
+		{
+			FbxMesh* fbxMesh = node->GetMesh();
+			if (fbxMesh != nullptr)
+			{
+				INT32 deformerCount = fbxMesh->GetDeformerCount(FbxDeformer::eBlendShape);
+				for (INT32 i = 0; i < deformerCount; i++)
+				{
+					FbxBlendShape* deformer = static_cast<FbxBlendShape*>(fbxMesh->GetDeformer(i, FbxDeformer::eBlendShape));
+
+					INT32 channelCount = deformer->GetBlendShapeChannelCount();
+					for (INT32 j = 0; j < channelCount; j++)
+					{
+						FbxBlendShapeChannel* channel = deformer->GetBlendShapeChannel(j);
+
+						FbxAnimCurve* curve = fbxMesh->GetShapeChannel(i, j, layer);
+						if (curve != nullptr && curve->KeyGetCount() > 0)
+						{
+							clip.blendShapeAnimations.push_back(FBXBlendShapeAnimation());
+							FBXBlendShapeAnimation& blendShapeAnim = clip.blendShapeAnimations.back();
+							blendShapeAnim.blendShape = channel->GetName();
+
+							importCurve(curve, importOptions, blendShapeAnim.curve, clip.start, clip.end);
+						}
+					}
+				}
+			}
+		}
+
+		UINT32 childCount = (UINT32)node->GetChildCount();
+		for (UINT32 i = 0; i < childCount; i++)
+		{
+			FbxNode* child = node->GetChild(i);
+			importAnimations(layer, child, importOptions, clip, importScene);
+		}
+	}
+
+	void FBXImporter::eulerToQuaternionCurves(FBXAnimationCurve(&eulerCurves)[3], FBXAnimationCurve(&quatCurves)[4])
+	{
+		const float FIT_TIME = 0.33f;
+
+		INT32 numKeys = (INT32)eulerCurves[0].keyframes.size();
+
+		if (numKeys != (INT32)eulerCurves[1].keyframes.size() || numKeys != (INT32)eulerCurves[2].keyframes.size())
+			return;
+
+		auto eulerToQuaternion = [&](INT32 keyIdx, float time, const Quaternion& lastQuat)
+		{
+			Degree x = (Degree)eulerCurves[0].evaluate(time);
+			Degree y = (Degree)eulerCurves[1].evaluate(time);
+			Degree z = (Degree)eulerCurves[2].evaluate(time);
+
+			Quaternion quat(x, y, z);
+
+			// Flip quaternion in case rotation is over 180 degrees
+			if (keyIdx > 0)
+			{
+				float dot = quat.dot(lastQuat);
+				if (dot < 0.0f)
+					quat = Quaternion(-quat.x, -quat.y, -quat.z, -quat.w);
+			}
+
+			return quat;
+		};
+
+		struct FitKeyframe
+		{
+			float time;
+			Quaternion value;
+		};
+
+		Vector<FitKeyframe> fitQuaternions(numKeys * 2);
+
+		Quaternion lastQuat;
+		for (INT32 i = 0; i < numKeys; i++)
+		{
+			float time = eulerCurves[0].keyframes[i].time;
+			Quaternion quat = eulerToQuaternion(i, time, lastQuat);
+
+			// Calculate extra values between keys so we can better approximate tangents
+			if ((i + 1) < numKeys)
+			{
+				float nextTime = eulerCurves[0].keyframes[i + 1].time;
+				float dt = nextTime - time;
+
+				FitKeyframe& fitStart = fitQuaternions[i * 2 + 0];
+				FitKeyframe& fitEnd = fitQuaternions[i * 2 + 1];
+
+				fitStart.time = time + dt * FIT_TIME;
+				fitEnd.time = time + dt * (1.0f - FIT_TIME);
+
+				fitStart.value = eulerToQuaternion(i, fitStart.time, quat);
+				fitEnd.value = eulerToQuaternion(i, fitEnd.time, fitStart.value);
+
+				lastQuat = fitStart.value;
+			}
+
+			// TODO - If animation is looping I should also compare last and first for continuity
+
+			for (INT32 j = 0; j < 4; j++)
+			{
+				quatCurves[j].keyframes.push_back(FBXKeyFrame());
+				FBXKeyFrame& keyFrame = quatCurves[j].keyframes.back();
+				keyFrame.time = time;
+				keyFrame.value = quat[j];
+
+				keyFrame.inTangent = 0;
+				keyFrame.outTangent = 0;
+			}
+		}
+
+		// Recalculate tangents for quaternion curves
+
+		// TODO - There must be an analytical way to convert euler angle tangents
+		//        to quaternion tangents, but I don't want to bother figuring it out
+		//        until I have a test-bed for animation.
+		if (numKeys > 1)
+		{
+			// TODO - I could check per-key curve interpolation originally assigned in FBX
+			//        and use that to generate linear/constant slopes. Currently I assume 
+			//        its all cubic.
+
+			// First key
+			{
+				const FitKeyframe& fitKeyFrame = fitQuaternions[0];
+
+				for (INT32 j = 0; j < 4; j++)
+				{
+					FBXKeyFrame& keyFrame = quatCurves[j].keyframes[0];
+
+					float dt = fitKeyFrame.time - keyFrame.time;
+
+					keyFrame.inTangent = (fitKeyFrame.value[j] - keyFrame.value) / dt;
+					keyFrame.outTangent = keyFrame.inTangent;
+				}
+			}
+
+			// In-between keys
+			{
+				for (INT32 i = 1; i < (numKeys - 1); i++)
+				{
+					const FitKeyframe& fitPointStart = fitQuaternions[i * 2 - 1];
+					const FitKeyframe& fitPointEnd = fitQuaternions[i * 2 + 0];
+
+					for (INT32 j = 0; j < 4; j++)
+					{
+						FBXKeyFrame& keyFrame = quatCurves[j].keyframes[i];
+
+						float dt0 = fitPointEnd.time - keyFrame.time;
+						float dt1 = keyFrame.time - fitPointStart.time;
+
+						float t0 = fitPointEnd.value[j] - keyFrame.value;
+						float t1 = keyFrame.value - fitPointStart.value[j];
+
+						keyFrame.inTangent = t0 / (0.5f * dt0) + t1 / (0.5f * dt1);
+						keyFrame.outTangent = keyFrame.inTangent;
+					}
+				}
+			}
+
+			// Last key
+			{
+				const FitKeyframe& fitKeyFrame = fitQuaternions[(numKeys - 2) * 2];
+
+				for (INT32 j = 0; j < 4; j++)
+				{
+					FBXKeyFrame& keyFrame = quatCurves[j].keyframes[numKeys - 2];
+
+					float dt = keyFrame.time - fitKeyFrame.time;
+
+					keyFrame.inTangent = (keyFrame.value - fitKeyFrame.value[j]) / dt;
+					keyFrame.outTangent = keyFrame.inTangent;
+				}
+			}
+		}
+	}
+
+	void FBXImporter::importCurve(FbxAnimCurve* fbxCurve, FBXImportOptions& importOptions, FBXAnimationCurve& curve, float start, float end)
+	{
+		if (fbxCurve == nullptr)
+			return;
+
+		INT32 keyCount = fbxCurve->KeyGetCount();
+		if (importOptions.animResample)
+		{
+			float curveStart = std::numeric_limits<float>::infinity();
+			float curveEnd = -std::numeric_limits<float>::infinity();
+
+			for (INT32 i = 0; i < keyCount; i++)
+			{
+				FbxTime fbxTime = fbxCurve->KeyGetTime(i);
+				float time = (float)fbxTime.GetSecondDouble();
+
+				curveStart = std::min(time, curveStart);
+				curveEnd = std::max(time, curveEnd);
+			}
+
+			curveStart = Math::clamp(curveStart, start, end);
+			curveEnd = Math::clamp(curveEnd, start, end);
+
+			float curveLength = curveEnd - curveStart;
+			INT32 numSamples = Math::ceilToInt(curveLength / importOptions.animSampleRate);
+
+			// We don't use the exact provided sample rate but instead modify it slightly so it
+			// completely covers the curve range including start/end points while maintaining
+			// constant time step between keyframes.
+			float dt = curveLength / (float)numSamples; 
+
+			INT32 lastKeyframe = 0;
+			INT32 lastLeftTangent = 0;
+			INT32 lastRightTangent = 0;
+			for (INT32 i = 0; i < numSamples; i++)
+			{
+				float sampleTime = std::min(curveStart + i * dt, curveEnd);
+				FbxTime fbxSampleTime;
+				fbxSampleTime.SetSecondDouble(sampleTime);
+
+				curve.keyframes.push_back(FBXKeyFrame());
+				FBXKeyFrame& keyFrame = curve.keyframes.back();
+				keyFrame.time = sampleTime;
+				keyFrame.value = fbxCurve->Evaluate(fbxSampleTime, &lastKeyframe);
+				keyFrame.inTangent = fbxCurve->EvaluateLeftDerivative(fbxSampleTime, &lastLeftTangent);
+				keyFrame.outTangent = fbxCurve->EvaluateRightDerivative(fbxSampleTime, &lastRightTangent);
+			}
+		}
+		else
+		{
+			for (int i = 0; i < keyCount; i++)
+			{
+				FbxTime fbxTime = fbxCurve->KeyGetTime(i);
+				float time = (float)fbxTime.GetSecondDouble();
+
+				if (time < start || time > end)
+					continue;
+
+				curve.keyframes.push_back(FBXKeyFrame());
+				FBXKeyFrame& keyFrame = curve.keyframes.back();
+				keyFrame.time = time;
+				keyFrame.value = fbxCurve->KeyGetValue(i);
+				keyFrame.inTangent = fbxCurve->KeyGetLeftDerivative(i);
+				keyFrame.outTangent = fbxCurve->KeyGetRightDerivative(i);
+			}
+		}
+	}
 }

+ 826 - 815
MBansheeEditor/Inspector/InspectorWindow.cs

@@ -1,815 +1,826 @@
-using System;
-using System.Collections.Generic;
-using System.Data.Common;
-using System.IO;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a <see cref="SceneObject"/> or for a <see cref="Resource"/>. Scene object's transform values
-    /// are displayed, along with all their components and their fields.
-    /// </summary>
-    internal sealed class InspectorWindow : EditorWindow
-    {
-        /// <summary>
-        /// Type of objects displayed in the window.
-        /// </summary>
-        private enum InspectorType
-        {
-            SceneObject,
-            Resource,
-            Multiple,
-            None
-        }
-
-        /// <summary>
-        /// Inspector GUI elements for a single <see cref="Component"/> in a <see cref="SceneObject"/>.
-        /// </summary>
-        private class InspectorComponent
-        {
-            public GUIToggle foldout;
-            public GUIButton removeBtn;
-            public GUILayout title;
-            public GUIPanel panel;
-            public Inspector inspector;
-            public bool expanded = true;
-            public UInt64 instanceId;
-        }
-
-        /// <summary>
-        /// Inspector GUI elements for a <see cref="Resource"/>
-        /// </summary>
-        private class InspectorResource
-        {
-            public GUIPanel panel;
-            public Inspector inspector;
-        }
-
-        private static readonly Color HIGHLIGHT_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.5f);
-        private const int RESOURCE_TITLE_HEIGHT = 30;
-        private const int COMPONENT_SPACING = 10;
-        private const int PADDING = 5;
-
-        private List<InspectorComponent> inspectorComponents = new List<InspectorComponent>();
-        private InspectorResource inspectorResource;
-        private GUIScrollArea inspectorScrollArea;
-        private GUILayout inspectorLayout;
-        private GUIPanel highlightPanel;
-        private GUITexture scrollAreaHighlight;
-
-        private SceneObject activeSO;
-        private InspectableState modifyState;
-        private int undoCommandIdx = -1;
-        private GUITextBox soNameInput;
-        private GUILayout soPrefabLayout;
-        private bool soHasPrefab;
-        private GUIFloatField soPosX;
-        private GUIFloatField soPosY;
-        private GUIFloatField soPosZ;
-        private GUIFloatField soRotX;
-        private GUIFloatField soRotY;
-        private GUIFloatField soRotZ;
-        private GUIFloatField soScaleX;
-        private GUIFloatField soScaleY;
-        private GUIFloatField soScaleZ;
-
-        private Rect2I[] dropAreas = new Rect2I[0];
-
-        private InspectorType currentType = InspectorType.None;
-        private Resource activeResource;
-
-        /// <summary>
-        /// Opens the inspector window from the menu bar.
-        /// </summary>
-        [MenuItem("Windows/Inspector", ButtonModifier.CtrlAlt, ButtonCode.I, 6000)]
-        private static void OpenInspectorWindow()
-        {
-            OpenWindow<InspectorWindow>();
-        }
-
-        /// <summary>
-        /// Name of the inspector window to display on the window title.
-        /// </summary>
-        /// <returns>Name of the inspector window to display on the window title.</returns>
-        protected override LocString GetDisplayName()
-        {
-            return new LocEdString("Inspector");
-        }
-
-        /// <summary>
-        /// Sets a resource whose GUI is to be displayed in the inspector. Clears any previous contents of the window.
-        /// </summary>
-        /// <param name="resourcePath">Resource path relative to the project of the resource to inspect.</param>
-        private void SetObjectToInspect(String resourcePath)
-        {
-            activeResource = ProjectLibrary.Load<Resource>(resourcePath);
-
-            if (activeResource == null)
-                return;
-
-            currentType = InspectorType.Resource;
-
-            inspectorScrollArea = new GUIScrollArea();
-            GUI.AddElement(inspectorScrollArea);
-            inspectorLayout = inspectorScrollArea.Layout;
-
-            GUIPanel titlePanel = inspectorLayout.AddPanel();
-            titlePanel.SetHeight(RESOURCE_TITLE_HEIGHT);
-
-            GUILayoutY titleLayout = titlePanel.AddLayoutY();
-            titleLayout.SetPosition(PADDING, PADDING);
-
-            string name = Path.GetFileNameWithoutExtension(resourcePath);
-            string type = activeResource.GetType().Name;
-
-            LocString title = new LocEdString(name + " (" + type + ")");
-            GUILabel titleLabel = new GUILabel(title);
-
-            titleLayout.AddFlexibleSpace();
-            GUILayoutX titleLabelLayout = titleLayout.AddLayoutX();
-            titleLabelLayout.AddElement(titleLabel);
-            titleLayout.AddFlexibleSpace();
-
-            GUIPanel titleBgPanel = titlePanel.AddPanel(1);
-
-            GUITexture titleBg = new GUITexture(null, EditorStyles.InspectorTitleBg);
-            titleBgPanel.AddElement(titleBg);
-
-            inspectorLayout.AddSpace(COMPONENT_SPACING);
-
-            inspectorResource = new InspectorResource();
-            inspectorResource.panel = inspectorLayout.AddPanel();
-
-            inspectorResource.inspector = InspectorUtility.GetInspector(activeResource.GetType());
-            inspectorResource.inspector.Initialize(inspectorResource.panel, activeResource);
-
-            inspectorLayout.AddFlexibleSpace();
-        }
-
-        /// <summary>
-        /// Sets a scene object whose GUI is to be displayed in the inspector. Clears any previous contents of the window.
-        /// </summary>
-        /// <param name="so">Scene object to inspect.</param>
-        private void SetObjectToInspect(SceneObject so)
-        {
-            if (so == null)
-                return;
-
-            currentType = InspectorType.SceneObject;
-            activeSO = so;
-
-            inspectorScrollArea = new GUIScrollArea();
-            scrollAreaHighlight = new GUITexture(Builtin.WhiteTexture);
-            scrollAreaHighlight.SetTint(HIGHLIGHT_COLOR);
-            scrollAreaHighlight.Active = false;
-
-            GUI.AddElement(inspectorScrollArea);
-            GUIPanel inspectorPanel = inspectorScrollArea.Layout.AddPanel();
-            inspectorLayout = inspectorPanel.AddLayoutY();
-            highlightPanel = inspectorPanel.AddPanel(-1);
-            highlightPanel.AddElement(scrollAreaHighlight);
-
-            // SceneObject fields
-            CreateSceneObjectFields();
-            RefreshSceneObjectFields(true);
-
-            // Components
-            Component[] allComponents = so.GetComponents();
-            for (int i = 0; i < allComponents.Length; i++)
-            {
-                inspectorLayout.AddSpace(COMPONENT_SPACING);
-
-                InspectorComponent data = new InspectorComponent();
-                data.instanceId = allComponents[i].InstanceId;
-
-                data.foldout = new GUIToggle(allComponents[i].GetType().Name, EditorStyles.Foldout);
-                data.removeBtn = new GUIButton(new GUIContent(EditorBuiltin.XBtnIcon), GUIOption.FixedWidth(30));
-
-                data.title = inspectorLayout.AddLayoutX();
-                data.title.AddElement(data.foldout);
-                data.title.AddElement(data.removeBtn);
-
-                data.panel = inspectorLayout.AddPanel();
-                data.inspector = InspectorUtility.GetInspector(allComponents[i].GetType());
-                data.inspector.Initialize(data.panel, allComponents[i]);
-                data.foldout.Value = true;
-
-                Type curComponentType = allComponents[i].GetType();
-                data.foldout.OnToggled += (bool expanded) => OnComponentFoldoutToggled(data, expanded);
-                data.removeBtn.OnClick += () => OnComponentRemoveClicked(curComponentType);
-
-                inspectorComponents.Add(data);
-            }
-
-            inspectorLayout.AddFlexibleSpace();
-
-            UpdateDropAreas();
-        }
-
-        /// <summary>
-        /// Creates GUI elements required for displaying <see cref="SceneObject"/> fields like name, prefab data and 
-        /// transform (position, rotation, scale). Assumes that necessary inspector scroll area layout has already been 
-        /// created.
-        /// </summary>
-        private void CreateSceneObjectFields()
-        {
-            GUIPanel sceneObjectPanel = inspectorLayout.AddPanel();
-            sceneObjectPanel.SetHeight(GetTitleBounds().height);
-
-            GUILayoutY sceneObjectLayout = sceneObjectPanel.AddLayoutY();
-            sceneObjectLayout.SetPosition(PADDING, PADDING);
-
-            GUIPanel sceneObjectBgPanel = sceneObjectPanel.AddPanel(1);
-
-            GUILayoutX nameLayout = sceneObjectLayout.AddLayoutX();
-            GUILabel nameLbl = new GUILabel(new LocEdString("Name"), GUIOption.FixedWidth(50));
-            soNameInput = new GUITextBox(false, GUIOption.FlexibleWidth(180));
-            soNameInput.Text = activeSO.Name;
-            soNameInput.OnChanged += OnSceneObjectRename;
-            soNameInput.OnConfirmed += OnModifyConfirm;
-            soNameInput.OnFocusLost += OnModifyConfirm;
-
-            nameLayout.AddElement(nameLbl);
-            nameLayout.AddElement(soNameInput);
-            nameLayout.AddFlexibleSpace();
-
-            soPrefabLayout = sceneObjectLayout.AddLayoutX();
-
-            GUILayoutX positionLayout = sceneObjectLayout.AddLayoutX();
-            GUILabel positionLbl = new GUILabel(new LocEdString("Position"), GUIOption.FixedWidth(50));
-            soPosX = new GUIFloatField(new LocEdString("X"), 10, "", GUIOption.FixedWidth(60));
-            soPosY = new GUIFloatField(new LocEdString("Y"), 10, "", GUIOption.FixedWidth(60));
-            soPosZ = new GUIFloatField(new LocEdString("Z"), 10, "", GUIOption.FixedWidth(60));
-
-            soPosX.OnChanged += (x) => OnPositionChanged(0, x);
-            soPosY.OnChanged += (y) => OnPositionChanged(1, y);
-            soPosZ.OnChanged += (z) => OnPositionChanged(2, z);
-
-            soPosX.OnConfirmed += OnModifyConfirm;
-            soPosY.OnConfirmed += OnModifyConfirm;
-            soPosZ.OnConfirmed += OnModifyConfirm;
-
-            soPosX.OnFocusLost += OnModifyConfirm;
-            soPosY.OnFocusLost += OnModifyConfirm;
-            soPosZ.OnFocusLost += OnModifyConfirm;
-
-            positionLayout.AddElement(positionLbl);
-            positionLayout.AddElement(soPosX);
-            positionLayout.AddSpace(10);
-            positionLayout.AddFlexibleSpace();
-            positionLayout.AddElement(soPosY);
-            positionLayout.AddSpace(10);
-            positionLayout.AddFlexibleSpace();
-            positionLayout.AddElement(soPosZ);
-            positionLayout.AddFlexibleSpace();
-
-            GUILayoutX rotationLayout = sceneObjectLayout.AddLayoutX();
-            GUILabel rotationLbl = new GUILabel(new LocEdString("Rotation"), GUIOption.FixedWidth(50));
-            soRotX = new GUIFloatField(new LocEdString("X"), 10, "", GUIOption.FixedWidth(60));
-            soRotY = new GUIFloatField(new LocEdString("Y"), 10, "", GUIOption.FixedWidth(60));
-            soRotZ = new GUIFloatField(new LocEdString("Z"), 10, "", GUIOption.FixedWidth(60));
-
-            soRotX.OnChanged += (x) => OnRotationChanged(0, x);
-            soRotY.OnChanged += (y) => OnRotationChanged(1, y);
-            soRotZ.OnChanged += (z) => OnRotationChanged(2, z);
-
-            soRotX.OnConfirmed += OnModifyConfirm;
-            soRotY.OnConfirmed += OnModifyConfirm;
-            soRotZ.OnConfirmed += OnModifyConfirm;
-
-            soRotX.OnFocusLost += OnModifyConfirm;
-            soRotY.OnFocusLost += OnModifyConfirm;
-            soRotZ.OnFocusLost += OnModifyConfirm;
-
-            rotationLayout.AddElement(rotationLbl);
-            rotationLayout.AddElement(soRotX);
-            rotationLayout.AddSpace(10);
-            rotationLayout.AddFlexibleSpace();
-            rotationLayout.AddElement(soRotY);
-            rotationLayout.AddSpace(10);
-            rotationLayout.AddFlexibleSpace();
-            rotationLayout.AddElement(soRotZ);
-            rotationLayout.AddFlexibleSpace();
-
-            GUILayoutX scaleLayout = sceneObjectLayout.AddLayoutX();
-            GUILabel scaleLbl = new GUILabel(new LocEdString("Scale"), GUIOption.FixedWidth(50));
-            soScaleX = new GUIFloatField(new LocEdString("X"), 10, "", GUIOption.FixedWidth(60));
-            soScaleY = new GUIFloatField(new LocEdString("Y"), 10, "", GUIOption.FixedWidth(60));
-            soScaleZ = new GUIFloatField(new LocEdString("Z"), 10, "", GUIOption.FixedWidth(60));
-
-            soScaleX.OnChanged += (x) => OnScaleChanged(0, x);
-            soScaleY.OnChanged += (y) => OnScaleChanged(1, y);
-            soScaleZ.OnChanged += (z) => OnScaleChanged(2, z);
-
-            soScaleX.OnConfirmed += OnModifyConfirm;
-            soScaleY.OnConfirmed += OnModifyConfirm;
-            soScaleZ.OnConfirmed += OnModifyConfirm;
-
-            soScaleX.OnFocusLost += OnModifyConfirm;
-            soScaleY.OnFocusLost += OnModifyConfirm;
-            soScaleZ.OnFocusLost += OnModifyConfirm;
-
-            scaleLayout.AddElement(scaleLbl);
-            scaleLayout.AddElement(soScaleX);
-            scaleLayout.AddSpace(10);
-            scaleLayout.AddFlexibleSpace();
-            scaleLayout.AddElement(soScaleY);
-            scaleLayout.AddSpace(10);
-            scaleLayout.AddFlexibleSpace();
-            scaleLayout.AddElement(soScaleZ);
-            scaleLayout.AddFlexibleSpace();
-
-            sceneObjectLayout.AddFlexibleSpace();
-
-            GUITexture titleBg = new GUITexture(null, EditorStyles.InspectorTitleBg);
-            sceneObjectBgPanel.AddElement(titleBg);
-        }
-
-        /// <summary>
-        /// Updates contents of the scene object specific fields (name, position, rotation, etc.)
-        /// </summary>
-        /// <param name="forceUpdate">If true, the GUI elements will be updated regardless of whether a change was
-        ///                           detected or not.</param>
-        private void RefreshSceneObjectFields(bool forceUpdate)
-        {
-            if (activeSO == null)
-                return;
-
-            soNameInput.Text = activeSO.Name;
-
-            bool hasPrefab = PrefabUtility.IsPrefabInstance(activeSO);
-            if (soHasPrefab != hasPrefab || forceUpdate)
-            {
-                int numChildren = soPrefabLayout.ChildCount;
-                for (int i = 0; i < numChildren; i++)
-                    soPrefabLayout.GetChild(0).Destroy();
-
-                GUILabel prefabLabel =new GUILabel(new LocEdString("Prefab"), GUIOption.FixedWidth(50));
-                soPrefabLayout.AddElement(prefabLabel);
-
-                if (hasPrefab)
-                {
-                    GUIButton btnApplyPrefab = new GUIButton(new LocEdString("Apply"), GUIOption.FixedWidth(60));
-                    GUIButton btnRevertPrefab = new GUIButton(new LocEdString("Revert"), GUIOption.FixedWidth(60));
-                    GUIButton btnBreakPrefab = new GUIButton(new LocEdString("Break"), GUIOption.FixedWidth(60));
-
-                    btnApplyPrefab.OnClick += () => PrefabUtility.ApplyPrefab(activeSO);
-                    btnRevertPrefab.OnClick += () => { PrefabUtility.RevertPrefab(activeSO); EditorApplication.SetSceneDirty(); };
-                    btnBreakPrefab.OnClick += () => { PrefabUtility.BreakPrefab(activeSO); EditorApplication.SetSceneDirty(); };
-
-                    soPrefabLayout.AddElement(btnApplyPrefab);
-                    soPrefabLayout.AddElement(btnRevertPrefab);
-                    soPrefabLayout.AddElement(btnBreakPrefab);
-                }
-                else
-                {
-                    GUILabel noPrefabLabel = new GUILabel("None");
-                    soPrefabLayout.AddElement(noPrefabLabel);
-                }
-
-                soHasPrefab = hasPrefab;
-            }
-
-            Vector3 position;
-            Vector3 angles;
-            if (EditorApplication.ActiveCoordinateMode == HandleCoordinateMode.World)
-            {
-                position = activeSO.Position;
-                angles = activeSO.Rotation.ToEuler();
-            }
-            else
-            {
-                position = activeSO.LocalPosition;
-                angles = activeSO.LocalRotation.ToEuler();
-            }
-
-            Vector3 scale = activeSO.LocalScale;
-
-            soPosX.Value = position.x;
-            soPosY.Value = position.y;
-            soPosZ.Value = position.z;
-
-            soRotX.Value = angles.x;
-            soRotY.Value = angles.y;
-            soRotZ.Value = angles.z;
-
-            soScaleX.Value = scale.x;
-            soScaleY.Value = scale.y;
-            soScaleZ.Value = scale.z;
-        }
-
-        private void OnInitialize()
-        {
-            Selection.OnSelectionChanged += OnSelectionChanged;
-
-            OnSelectionChanged(new SceneObject[0], new string[0]);
-        }
-
-        private void OnDestroy()
-        {
-            Selection.OnSelectionChanged -= OnSelectionChanged;
-        }
-
-        private void OnEditorUpdate()
-        {
-            if (currentType == InspectorType.SceneObject)
-            {
-                Component[] allComponents = activeSO.GetComponents();
-                bool requiresRebuild = allComponents.Length != inspectorComponents.Count;
-
-                if (!requiresRebuild)
-                {
-                    for (int i = 0; i < inspectorComponents.Count; i++)
-                    {
-                        if (inspectorComponents[i].instanceId != allComponents[i].InstanceId)
-                        {
-                            requiresRebuild = true;
-                            break;
-                        }
-                    }
-                }
-
-                if (requiresRebuild)
-                {
-                    SceneObject so = activeSO;
-                    Clear();
-                    SetObjectToInspect(so);
-                }
-                else
-                {
-                    RefreshSceneObjectFields(false);
-
-                    InspectableState componentModifyState = InspectableState.NotModified;
-                    for (int i = 0; i < inspectorComponents.Count; i++)
-                        componentModifyState |= inspectorComponents[i].inspector.Refresh();
-
-                    if (componentModifyState.HasFlag(InspectableState.ModifyInProgress))
-                        EditorApplication.SetSceneDirty();
-
-                    modifyState |= componentModifyState;
-                }
-            }
-            else if (currentType == InspectorType.Resource)
-            {
-                inspectorResource.inspector.Refresh();
-            }
-
-            // Detect drag and drop
-            bool isValidDrag = false;
-
-            if (activeSO != null)
-            {
-                if ((DragDrop.DragInProgress || DragDrop.DropInProgress) && DragDrop.Type == DragDropType.Resource)
-                {
-                    Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
-                    Vector2I scrollPos = windowPos;
-                    Rect2I contentBounds = inspectorLayout.Bounds;
-                    scrollPos.x -= contentBounds.x;
-                    scrollPos.y -= contentBounds.y;
-
-                    bool isInBounds = false;
-                    Rect2I dropArea = new Rect2I();
-                    foreach (var bounds in dropAreas)
-                    {
-                        if (bounds.Contains(scrollPos))
-                        {
-                            isInBounds = true;
-                            dropArea = bounds;
-                            break;
-                        }
-                    }
-
-                    Type draggedComponentType = null;
-                    if (isInBounds)
-                    {
-                        ResourceDragDropData dragData = DragDrop.Data as ResourceDragDropData;
-                        if (dragData != null)
-                        {
-                            foreach (var resPath in dragData.Paths)
-                            {
-                                LibraryEntry entry = ProjectLibrary.GetEntry(resPath);
-                                FileEntry fileEntry = entry as FileEntry;
-                                if (fileEntry != null)
-                                {
-                                    if (fileEntry.ResType == ResourceType.ScriptCode)
-                                    {
-                                        ScriptCode scriptFile = ProjectLibrary.Load<ScriptCode>(resPath);
-
-                                        if (scriptFile != null)
-                                        {
-                                            Type[] scriptTypes = scriptFile.Types;
-                                            foreach (var type in scriptTypes)
-                                            {
-                                                if (type.IsSubclassOf(typeof (Component)))
-                                                {
-                                                    draggedComponentType = type;
-                                                    isValidDrag = true;
-                                                    break;
-                                                }
-                                            }
-
-                                            if (draggedComponentType != null)
-                                                break;
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-
-                    if (isValidDrag)
-                    {
-                        scrollAreaHighlight.Bounds = dropArea;
-
-                        if (DragDrop.DropInProgress)
-                        {
-                            activeSO.AddComponent(draggedComponentType);
-
-                            modifyState = InspectableState.Modified;
-                            EditorApplication.SetSceneDirty();
-                        }
-                    }
-                }               
-            }
-
-            if (scrollAreaHighlight != null)
-                scrollAreaHighlight.Active = isValidDrag;
-        }
-
-        /// <summary>
-        /// Triggered when the user selects a new resource or a scene object, or deselects everything.
-        /// </summary>
-        /// <param name="objects">A set of new scene objects that were selected.</param>
-        /// <param name="paths">A set of absolute resource paths that were selected.</param>
-        private void OnSelectionChanged(SceneObject[] objects, string[] paths)
-        {
-            if (currentType == InspectorType.SceneObject && modifyState == InspectableState.NotModified)
-                UndoRedo.PopCommand(undoCommandIdx);
-
-            Clear();
-            modifyState = InspectableState.NotModified;
-
-            if (objects.Length == 0 && paths.Length == 0)
-            {
-                currentType = InspectorType.None;
-                inspectorScrollArea = new GUIScrollArea();
-                GUI.AddElement(inspectorScrollArea);
-                inspectorLayout = inspectorScrollArea.Layout;
-
-                inspectorLayout.AddFlexibleSpace();
-                GUILayoutX layoutMsg = inspectorLayout.AddLayoutX();
-                layoutMsg.AddFlexibleSpace();
-                layoutMsg.AddElement(new GUILabel(new LocEdString("No object selected")));
-                layoutMsg.AddFlexibleSpace();
-                inspectorLayout.AddFlexibleSpace();
-            }
-            else if ((objects.Length + paths.Length) > 1)
-            {
-                currentType = InspectorType.None;
-                inspectorScrollArea = new GUIScrollArea();
-                GUI.AddElement(inspectorScrollArea);
-                inspectorLayout = inspectorScrollArea.Layout;
-
-                inspectorLayout.AddFlexibleSpace();
-                GUILayoutX layoutMsg = inspectorLayout.AddLayoutX();
-                layoutMsg.AddFlexibleSpace();
-                layoutMsg.AddElement(new GUILabel(new LocEdString("Multiple objects selected")));
-                layoutMsg.AddFlexibleSpace();
-                inspectorLayout.AddFlexibleSpace();
-            }
-            else if (objects.Length == 1)
-            {
-                if (objects[0] != null)
-                {
-                    UndoRedo.RecordSO(objects[0]);
-                    undoCommandIdx = UndoRedo.TopCommandId;
-
-                    SetObjectToInspect(objects[0]);
-                }
-            }
-            else if (paths.Length == 1)
-            {
-                SetObjectToInspect(paths[0]);
-            }
-        }
-
-        /// <summary>
-        /// Triggered when the user closes or expands a component foldout, making the component fields visible or hidden.
-        /// </summary>
-        /// <param name="inspectorData">Contains GUI data for the component that was toggled.</param>
-        /// <param name="expanded">Determines whether to display or hide component contents.</param>
-        private void OnComponentFoldoutToggled(InspectorComponent inspectorData, bool expanded)
-        {
-            inspectorData.expanded = expanded;
-            inspectorData.inspector.SetVisible(expanded);
-        }
-
-        /// <summary>
-        /// Triggered when the user clicks the component remove button. Removes that component from the active scene object.
-        /// </summary>
-        /// <param name="componentType">Type of the component to remove.</param>
-        private void OnComponentRemoveClicked(Type componentType)
-        {
-            if (activeSO != null)
-            {
-                activeSO.RemoveComponent(componentType);
-
-                modifyState = InspectableState.Modified;
-                EditorApplication.SetSceneDirty();
-            }
-        }
-
-        /// <summary>
-        /// Destroys all inspector GUI elements.
-        /// </summary>
-        internal void Clear()
-        {
-            for (int i = 0; i < inspectorComponents.Count; i++)
-            {
-                inspectorComponents[i].foldout.Destroy();
-                inspectorComponents[i].removeBtn.Destroy();
-                inspectorComponents[i].inspector.Destroy();
-            }
-
-            inspectorComponents.Clear();
-
-            if (inspectorResource != null)
-            {
-                inspectorResource.inspector.Destroy();
-                inspectorResource = null;
-            }
-
-            if (inspectorScrollArea != null)
-            {
-                inspectorScrollArea.Destroy();
-                inspectorScrollArea = null;
-            }
-
-            if (scrollAreaHighlight != null)
-            {
-                scrollAreaHighlight.Destroy();
-                scrollAreaHighlight = null;
-            }
-
-            if (highlightPanel != null)
-            {
-                highlightPanel.Destroy();
-                highlightPanel = null;
-            }
-
-            activeSO = null;
-            soNameInput = null;
-            soPrefabLayout = null;
-            soHasPrefab = false;
-            soPosX = null;
-            soPosY = null;
-            soPosZ = null;
-            soRotX = null;
-            soRotY = null;
-            soRotZ = null;
-            soScaleX = null;
-            soScaleY = null;
-            soScaleZ = null;
-            dropAreas = new Rect2I[0];
-
-            activeResource = null;
-            currentType = InspectorType.None;
-        }
-
-        /// <summary>
-        /// Returns the size of the title bar area that is displayed for <see cref="SceneObject"/> specific fields.
-        /// </summary>
-        /// <returns>Area of the title bar, relative to the window.</returns>
-        private Rect2I GetTitleBounds()
-        {
-            return new Rect2I(0, 0, Width, 115);
-        }
-
-        /// <summary>
-        /// Triggered when the user changes the name of the currently active scene object.
-        /// </summary>
-        private void OnSceneObjectRename(string name)
-        {
-            if (activeSO != null)
-            {
-                activeSO.Name = name;
-
-                modifyState |= InspectableState.ModifyInProgress;
-                EditorApplication.SetSceneDirty();
-            }
-        }
-
-        /// <summary>
-        /// Triggered when the scene object modification is confirmed by the user.
-        /// </summary>
-        private void OnModifyConfirm()
-        {
-            if (modifyState.HasFlag(InspectableState.ModifyInProgress))
-                modifyState = InspectableState.Modified;
-        }
-
-        /// <summary>
-        /// Triggered when the position value in the currently active <see cref="SceneObject"/> changes. Updates the 
-        /// necessary GUI elements.
-        /// </summary>
-        /// <param name="idx">Index of the coordinate that was changed.</param>
-        /// <param name="value">New value of the field.</param>
-        private void OnPositionChanged(int idx, float value)
-        {
-            if (activeSO == null)
-                return;
-
-            if (EditorApplication.ActiveCoordinateMode == HandleCoordinateMode.World)
-            {
-                Vector3 position = activeSO.Position;
-                position[idx] = value;
-                activeSO.Position = position;
-            }
-            else
-            {
-                Vector3 position = activeSO.LocalPosition;
-                position[idx] = value;
-                activeSO.LocalPosition = position;
-            }
-
-            modifyState = InspectableState.ModifyInProgress;
-            EditorApplication.SetSceneDirty();
-        }
-
-        /// <summary>
-        /// Triggered when the rotation value in the currently active <see cref="SceneObject"/> changes. Updates the 
-        /// necessary GUI elements.
-        /// </summary>
-        /// <param name="idx">Index of the euler angle that was changed (0 - X, 1 - Y, 2 - Z).</param>
-        /// <param name="value">New value of the field.</param>
-        private void OnRotationChanged(int idx, float value)
-        {
-            if (activeSO == null)
-                return;
-
-            if (EditorApplication.ActiveCoordinateMode == HandleCoordinateMode.World)
-            {
-                Vector3 angles = activeSO.Rotation.ToEuler();
-                angles[idx] = value;
-                activeSO.Rotation = Quaternion.FromEuler(angles);
-            }
-            else
-            {
-                Vector3 angles = activeSO.LocalRotation.ToEuler();
-                angles[idx] = value;
-                activeSO.LocalRotation = Quaternion.FromEuler(angles);
-            }
-
-            modifyState = InspectableState.ModifyInProgress;
-            EditorApplication.SetSceneDirty();
-        }
-
-        /// <summary>
-        /// Triggered when the scale value in the currently active <see cref="SceneObject"/> changes. Updates the 
-        /// necessary GUI elements.
-        /// </summary>
-        /// <param name="idx">Index of the coordinate that was changed.</param>
-        /// <param name="value">New value of the field.</param>
-        private void OnScaleChanged(int idx, float value)
-        {
-            if (activeSO == null)
-                return;
-
-            Vector3 scale = activeSO.LocalScale;
-            scale[idx] = value;
-            activeSO.LocalScale = scale;
-
-            modifyState = InspectableState.ModifyInProgress;
-            EditorApplication.SetSceneDirty();
-        }
-
-        /// <inheritdoc/>
-        protected override void WindowResized(int width, int height)
-        {
-            base.WindowResized(width, height);
-
-            UpdateDropAreas();
-        }
-
-        /// <summary>
-        /// Updates drop areas used for dragging and dropping components on the inspector.
-        /// </summary>
-        private void UpdateDropAreas()
-        {
-            if (activeSO == null)
-                return;
-
-            Rect2I contentBounds = inspectorLayout.Bounds;
-            dropAreas = new Rect2I[inspectorComponents.Count + 1];
-            int yOffset = GetTitleBounds().height;
-            for (int i = 0; i < inspectorComponents.Count; i++)
-            {
-                dropAreas[i] = new Rect2I(0, yOffset, contentBounds.width, COMPONENT_SPACING);
-                yOffset += inspectorComponents[i].title.Bounds.height + inspectorComponents[i].panel.Bounds.height + COMPONENT_SPACING;
-            }
-
-            dropAreas[dropAreas.Length - 1] = new Rect2I(0, yOffset, contentBounds.width, contentBounds.height - yOffset);
-        }
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.IO;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a <see cref="SceneObject"/> or for a <see cref="Resource"/>. Scene object's transform values
+    /// are displayed, along with all their components and their fields.
+    /// </summary>
+    internal sealed class InspectorWindow : EditorWindow
+    {
+        /// <summary>
+        /// Type of objects displayed in the window.
+        /// </summary>
+        private enum InspectorType
+        {
+            SceneObject,
+            Resource,
+            Multiple,
+            None
+        }
+
+        /// <summary>
+        /// Inspector GUI elements for a single <see cref="Component"/> in a <see cref="SceneObject"/>.
+        /// </summary>
+        private class InspectorComponent
+        {
+            public GUIToggle foldout;
+            public GUIButton removeBtn;
+            public GUILayout title;
+            public GUIPanel panel;
+            public Inspector inspector;
+            public bool expanded = true;
+            public UInt64 instanceId;
+        }
+
+        /// <summary>
+        /// Inspector GUI elements for a <see cref="Resource"/>
+        /// </summary>
+        private class InspectorResource
+        {
+            public GUIPanel panel;
+            public Inspector inspector;
+        }
+
+        private static readonly Color HIGHLIGHT_COLOR = new Color(1.0f, 1.0f, 1.0f, 0.5f);
+        private const int RESOURCE_TITLE_HEIGHT = 30;
+        private const int COMPONENT_SPACING = 10;
+        private const int PADDING = 5;
+
+        private List<InspectorComponent> inspectorComponents = new List<InspectorComponent>();
+        private InspectorResource inspectorResource;
+        private GUIScrollArea inspectorScrollArea;
+        private GUILayout inspectorLayout;
+        private GUIPanel highlightPanel;
+        private GUITexture scrollAreaHighlight;
+
+        private SceneObject activeSO;
+        private InspectableState modifyState;
+        private int undoCommandIdx = -1;
+        private GUITextBox soNameInput;
+        private GUILayout soPrefabLayout;
+        private bool soHasPrefab;
+        private GUIFloatField soPosX;
+        private GUIFloatField soPosY;
+        private GUIFloatField soPosZ;
+        private GUIFloatField soRotX;
+        private GUIFloatField soRotY;
+        private GUIFloatField soRotZ;
+        private GUIFloatField soScaleX;
+        private GUIFloatField soScaleY;
+        private GUIFloatField soScaleZ;
+
+        private Rect2I[] dropAreas = new Rect2I[0];
+
+        private InspectorType currentType = InspectorType.None;
+        private Resource activeResource;
+
+        /// <summary>
+        /// Opens the inspector window from the menu bar.
+        /// </summary>
+        [MenuItem("Windows/Inspector", ButtonModifier.CtrlAlt, ButtonCode.I, 6000)]
+        private static void OpenInspectorWindow()
+        {
+            OpenWindow<InspectorWindow>();
+        }
+
+        /// <summary>
+        /// Name of the inspector window to display on the window title.
+        /// </summary>
+        /// <returns>Name of the inspector window to display on the window title.</returns>
+        protected override LocString GetDisplayName()
+        {
+            return new LocEdString("Inspector");
+        }
+
+        /// <summary>
+        /// Sets a resource whose GUI is to be displayed in the inspector. Clears any previous contents of the window.
+        /// </summary>
+        /// <param name="resourcePath">Resource path relative to the project of the resource to inspect.</param>
+        private void SetObjectToInspect(String resourcePath)
+        {
+            activeResource = ProjectLibrary.Load<Resource>(resourcePath);
+
+            if (activeResource == null)
+                return;
+
+            currentType = InspectorType.Resource;
+
+            inspectorScrollArea = new GUIScrollArea();
+            GUI.AddElement(inspectorScrollArea);
+            inspectorLayout = inspectorScrollArea.Layout;
+
+            GUIPanel titlePanel = inspectorLayout.AddPanel();
+            titlePanel.SetHeight(RESOURCE_TITLE_HEIGHT);
+
+            GUILayoutY titleLayout = titlePanel.AddLayoutY();
+            titleLayout.SetPosition(PADDING, PADDING);
+
+            string name = Path.GetFileNameWithoutExtension(resourcePath);
+            string type = activeResource.GetType().Name;
+
+            LocString title = new LocEdString(name + " (" + type + ")");
+            GUILabel titleLabel = new GUILabel(title);
+
+            titleLayout.AddFlexibleSpace();
+            GUILayoutX titleLabelLayout = titleLayout.AddLayoutX();
+            titleLabelLayout.AddElement(titleLabel);
+            titleLayout.AddFlexibleSpace();
+
+            GUIPanel titleBgPanel = titlePanel.AddPanel(1);
+
+            GUITexture titleBg = new GUITexture(null, EditorStyles.InspectorTitleBg);
+            titleBgPanel.AddElement(titleBg);
+
+            inspectorLayout.AddSpace(COMPONENT_SPACING);
+
+            inspectorResource = new InspectorResource();
+            inspectorResource.panel = inspectorLayout.AddPanel();
+
+            inspectorResource.inspector = InspectorUtility.GetInspector(activeResource.GetType());
+            inspectorResource.inspector.Initialize(inspectorResource.panel, activeResource);
+
+            inspectorLayout.AddFlexibleSpace();
+        }
+
+        /// <summary>
+        /// Sets a scene object whose GUI is to be displayed in the inspector. Clears any previous contents of the window.
+        /// </summary>
+        /// <param name="so">Scene object to inspect.</param>
+        private void SetObjectToInspect(SceneObject so)
+        {
+            if (so == null)
+                return;
+
+            currentType = InspectorType.SceneObject;
+            activeSO = so;
+
+            inspectorScrollArea = new GUIScrollArea();
+            scrollAreaHighlight = new GUITexture(Builtin.WhiteTexture);
+            scrollAreaHighlight.SetTint(HIGHLIGHT_COLOR);
+            scrollAreaHighlight.Active = false;
+
+            GUI.AddElement(inspectorScrollArea);
+            GUIPanel inspectorPanel = inspectorScrollArea.Layout.AddPanel();
+            inspectorLayout = inspectorPanel.AddLayoutY();
+            highlightPanel = inspectorPanel.AddPanel(-1);
+            highlightPanel.AddElement(scrollAreaHighlight);
+
+            // SceneObject fields
+            CreateSceneObjectFields();
+            RefreshSceneObjectFields(true);
+
+            // Components
+            Component[] allComponents = so.GetComponents();
+            for (int i = 0; i < allComponents.Length; i++)
+            {
+                inspectorLayout.AddSpace(COMPONENT_SPACING);
+
+                InspectorComponent data = new InspectorComponent();
+                data.instanceId = allComponents[i].InstanceId;
+
+                data.foldout = new GUIToggle(allComponents[i].GetType().Name, EditorStyles.Foldout);
+                data.removeBtn = new GUIButton(new GUIContent(EditorBuiltin.XBtnIcon), GUIOption.FixedWidth(30));
+
+                data.title = inspectorLayout.AddLayoutX();
+                data.title.AddElement(data.foldout);
+                data.title.AddElement(data.removeBtn);
+
+                data.panel = inspectorLayout.AddPanel();
+                data.inspector = InspectorUtility.GetInspector(allComponents[i].GetType());
+                data.inspector.Initialize(data.panel, allComponents[i]);
+                data.foldout.Value = true;
+
+                Type curComponentType = allComponents[i].GetType();
+                data.foldout.OnToggled += (bool expanded) => OnComponentFoldoutToggled(data, expanded);
+                data.removeBtn.OnClick += () => OnComponentRemoveClicked(curComponentType);
+
+                inspectorComponents.Add(data);
+            }
+
+            inspectorLayout.AddFlexibleSpace();
+
+            UpdateDropAreas();
+        }
+
+        /// <summary>
+        /// Creates GUI elements required for displaying <see cref="SceneObject"/> fields like name, prefab data and 
+        /// transform (position, rotation, scale). Assumes that necessary inspector scroll area layout has already been 
+        /// created.
+        /// </summary>
+        private void CreateSceneObjectFields()
+        {
+            GUIPanel sceneObjectPanel = inspectorLayout.AddPanel();
+            sceneObjectPanel.SetHeight(GetTitleBounds().height);
+
+            GUILayoutY sceneObjectLayout = sceneObjectPanel.AddLayoutY();
+            sceneObjectLayout.SetPosition(PADDING, PADDING);
+
+            GUIPanel sceneObjectBgPanel = sceneObjectPanel.AddPanel(1);
+
+            GUILayoutX nameLayout = sceneObjectLayout.AddLayoutX();
+            GUILabel nameLbl = new GUILabel(new LocEdString("Name"), GUIOption.FixedWidth(50));
+            soNameInput = new GUITextBox(false, GUIOption.FlexibleWidth(180));
+            soNameInput.Text = activeSO.Name;
+            soNameInput.OnChanged += OnSceneObjectRename;
+            soNameInput.OnConfirmed += OnModifyConfirm;
+            soNameInput.OnFocusLost += OnModifyConfirm;
+
+            nameLayout.AddElement(nameLbl);
+            nameLayout.AddElement(soNameInput);
+            nameLayout.AddFlexibleSpace();
+
+            soPrefabLayout = sceneObjectLayout.AddLayoutX();
+
+            GUILayoutX positionLayout = sceneObjectLayout.AddLayoutX();
+            GUILabel positionLbl = new GUILabel(new LocEdString("Position"), GUIOption.FixedWidth(50));
+            soPosX = new GUIFloatField(new LocEdString("X"), 10, "", GUIOption.FixedWidth(60));
+            soPosY = new GUIFloatField(new LocEdString("Y"), 10, "", GUIOption.FixedWidth(60));
+            soPosZ = new GUIFloatField(new LocEdString("Z"), 10, "", GUIOption.FixedWidth(60));
+
+            soPosX.OnChanged += (x) => OnPositionChanged(0, x);
+            soPosY.OnChanged += (y) => OnPositionChanged(1, y);
+            soPosZ.OnChanged += (z) => OnPositionChanged(2, z);
+
+            soPosX.OnConfirmed += OnModifyConfirm;
+            soPosY.OnConfirmed += OnModifyConfirm;
+            soPosZ.OnConfirmed += OnModifyConfirm;
+
+            soPosX.OnFocusLost += OnModifyConfirm;
+            soPosY.OnFocusLost += OnModifyConfirm;
+            soPosZ.OnFocusLost += OnModifyConfirm;
+
+            positionLayout.AddElement(positionLbl);
+            positionLayout.AddElement(soPosX);
+            positionLayout.AddSpace(10);
+            positionLayout.AddFlexibleSpace();
+            positionLayout.AddElement(soPosY);
+            positionLayout.AddSpace(10);
+            positionLayout.AddFlexibleSpace();
+            positionLayout.AddElement(soPosZ);
+            positionLayout.AddFlexibleSpace();
+
+            GUILayoutX rotationLayout = sceneObjectLayout.AddLayoutX();
+            GUILabel rotationLbl = new GUILabel(new LocEdString("Rotation"), GUIOption.FixedWidth(50));
+            soRotX = new GUIFloatField(new LocEdString("X"), 10, "", GUIOption.FixedWidth(60));
+            soRotY = new GUIFloatField(new LocEdString("Y"), 10, "", GUIOption.FixedWidth(60));
+            soRotZ = new GUIFloatField(new LocEdString("Z"), 10, "", GUIOption.FixedWidth(60));
+
+            soRotX.OnChanged += (x) => OnRotationChanged(0, x);
+            soRotY.OnChanged += (y) => OnRotationChanged(1, y);
+            soRotZ.OnChanged += (z) => OnRotationChanged(2, z);
+
+            soRotX.OnConfirmed += OnModifyConfirm;
+            soRotY.OnConfirmed += OnModifyConfirm;
+            soRotZ.OnConfirmed += OnModifyConfirm;
+
+            soRotX.OnFocusLost += OnModifyConfirm;
+            soRotY.OnFocusLost += OnModifyConfirm;
+            soRotZ.OnFocusLost += OnModifyConfirm;
+
+            rotationLayout.AddElement(rotationLbl);
+            rotationLayout.AddElement(soRotX);
+            rotationLayout.AddSpace(10);
+            rotationLayout.AddFlexibleSpace();
+            rotationLayout.AddElement(soRotY);
+            rotationLayout.AddSpace(10);
+            rotationLayout.AddFlexibleSpace();
+            rotationLayout.AddElement(soRotZ);
+            rotationLayout.AddFlexibleSpace();
+
+            GUILayoutX scaleLayout = sceneObjectLayout.AddLayoutX();
+            GUILabel scaleLbl = new GUILabel(new LocEdString("Scale"), GUIOption.FixedWidth(50));
+            soScaleX = new GUIFloatField(new LocEdString("X"), 10, "", GUIOption.FixedWidth(60));
+            soScaleY = new GUIFloatField(new LocEdString("Y"), 10, "", GUIOption.FixedWidth(60));
+            soScaleZ = new GUIFloatField(new LocEdString("Z"), 10, "", GUIOption.FixedWidth(60));
+
+            soScaleX.OnChanged += (x) => OnScaleChanged(0, x);
+            soScaleY.OnChanged += (y) => OnScaleChanged(1, y);
+            soScaleZ.OnChanged += (z) => OnScaleChanged(2, z);
+
+            soScaleX.OnConfirmed += OnModifyConfirm;
+            soScaleY.OnConfirmed += OnModifyConfirm;
+            soScaleZ.OnConfirmed += OnModifyConfirm;
+
+            soScaleX.OnFocusLost += OnModifyConfirm;
+            soScaleY.OnFocusLost += OnModifyConfirm;
+            soScaleZ.OnFocusLost += OnModifyConfirm;
+
+            scaleLayout.AddElement(scaleLbl);
+            scaleLayout.AddElement(soScaleX);
+            scaleLayout.AddSpace(10);
+            scaleLayout.AddFlexibleSpace();
+            scaleLayout.AddElement(soScaleY);
+            scaleLayout.AddSpace(10);
+            scaleLayout.AddFlexibleSpace();
+            scaleLayout.AddElement(soScaleZ);
+            scaleLayout.AddFlexibleSpace();
+
+            sceneObjectLayout.AddFlexibleSpace();
+
+            GUITexture titleBg = new GUITexture(null, EditorStyles.InspectorTitleBg);
+            sceneObjectBgPanel.AddElement(titleBg);
+        }
+
+        /// <summary>
+        /// Updates contents of the scene object specific fields (name, position, rotation, etc.)
+        /// </summary>
+        /// <param name="forceUpdate">If true, the GUI elements will be updated regardless of whether a change was
+        ///                           detected or not.</param>
+        private void RefreshSceneObjectFields(bool forceUpdate)
+        {
+            if (activeSO == null)
+                return;
+
+            soNameInput.Text = activeSO.Name;
+
+            bool hasPrefab = PrefabUtility.IsPrefabInstance(activeSO);
+            if (soHasPrefab != hasPrefab || forceUpdate)
+            {
+                int numChildren = soPrefabLayout.ChildCount;
+                for (int i = 0; i < numChildren; i++)
+                    soPrefabLayout.GetChild(0).Destroy();
+
+                GUILabel prefabLabel =new GUILabel(new LocEdString("Prefab"), GUIOption.FixedWidth(50));
+                soPrefabLayout.AddElement(prefabLabel);
+
+                if (hasPrefab)
+                {
+                    GUIButton btnApplyPrefab = new GUIButton(new LocEdString("Apply"), GUIOption.FixedWidth(60));
+                    GUIButton btnRevertPrefab = new GUIButton(new LocEdString("Revert"), GUIOption.FixedWidth(60));
+                    GUIButton btnBreakPrefab = new GUIButton(new LocEdString("Break"), GUIOption.FixedWidth(60));
+
+                    btnApplyPrefab.OnClick += () => PrefabUtility.ApplyPrefab(activeSO);
+                    btnRevertPrefab.OnClick += () =>
+                    {
+                        UndoRedo.RecordSO(activeSO, true, "Reverting \"" + activeSO.Name + "\" to prefab.");
+
+                        PrefabUtility.RevertPrefab(activeSO);
+                        EditorApplication.SetSceneDirty();
+                    };
+                    btnBreakPrefab.OnClick += () =>
+                    {
+                        UndoRedo.BreakPrefab(activeSO, "Breaking prefab link for " + activeSO.Name);
+
+                        EditorApplication.SetSceneDirty();
+                    };
+
+                    soPrefabLayout.AddElement(btnApplyPrefab);
+                    soPrefabLayout.AddElement(btnRevertPrefab);
+                    soPrefabLayout.AddElement(btnBreakPrefab);
+                }
+                else
+                {
+                    GUILabel noPrefabLabel = new GUILabel("None");
+                    soPrefabLayout.AddElement(noPrefabLabel);
+                }
+
+                soHasPrefab = hasPrefab;
+            }
+
+            Vector3 position;
+            Vector3 angles;
+            if (EditorApplication.ActiveCoordinateMode == HandleCoordinateMode.World)
+            {
+                position = activeSO.Position;
+                angles = activeSO.Rotation.ToEuler();
+            }
+            else
+            {
+                position = activeSO.LocalPosition;
+                angles = activeSO.LocalRotation.ToEuler();
+            }
+
+            Vector3 scale = activeSO.LocalScale;
+
+            soPosX.Value = position.x;
+            soPosY.Value = position.y;
+            soPosZ.Value = position.z;
+
+            soRotX.Value = angles.x;
+            soRotY.Value = angles.y;
+            soRotZ.Value = angles.z;
+
+            soScaleX.Value = scale.x;
+            soScaleY.Value = scale.y;
+            soScaleZ.Value = scale.z;
+        }
+
+        private void OnInitialize()
+        {
+            Selection.OnSelectionChanged += OnSelectionChanged;
+
+            OnSelectionChanged(new SceneObject[0], new string[0]);
+        }
+
+        private void OnDestroy()
+        {
+            Selection.OnSelectionChanged -= OnSelectionChanged;
+        }
+
+        private void OnEditorUpdate()
+        {
+            if (currentType == InspectorType.SceneObject)
+            {
+                Component[] allComponents = activeSO.GetComponents();
+                bool requiresRebuild = allComponents.Length != inspectorComponents.Count;
+
+                if (!requiresRebuild)
+                {
+                    for (int i = 0; i < inspectorComponents.Count; i++)
+                    {
+                        if (inspectorComponents[i].instanceId != allComponents[i].InstanceId)
+                        {
+                            requiresRebuild = true;
+                            break;
+                        }
+                    }
+                }
+
+                if (requiresRebuild)
+                {
+                    SceneObject so = activeSO;
+                    Clear();
+                    SetObjectToInspect(so);
+                }
+                else
+                {
+                    RefreshSceneObjectFields(false);
+
+                    InspectableState componentModifyState = InspectableState.NotModified;
+                    for (int i = 0; i < inspectorComponents.Count; i++)
+                        componentModifyState |= inspectorComponents[i].inspector.Refresh();
+
+                    if (componentModifyState.HasFlag(InspectableState.ModifyInProgress))
+                        EditorApplication.SetSceneDirty();
+
+                    modifyState |= componentModifyState;
+                }
+            }
+            else if (currentType == InspectorType.Resource)
+            {
+                inspectorResource.inspector.Refresh();
+            }
+
+            // Detect drag and drop
+            bool isValidDrag = false;
+
+            if (activeSO != null)
+            {
+                if ((DragDrop.DragInProgress || DragDrop.DropInProgress) && DragDrop.Type == DragDropType.Resource)
+                {
+                    Vector2I windowPos = ScreenToWindowPos(Input.PointerPosition);
+                    Vector2I scrollPos = windowPos;
+                    Rect2I contentBounds = inspectorLayout.Bounds;
+                    scrollPos.x -= contentBounds.x;
+                    scrollPos.y -= contentBounds.y;
+
+                    bool isInBounds = false;
+                    Rect2I dropArea = new Rect2I();
+                    foreach (var bounds in dropAreas)
+                    {
+                        if (bounds.Contains(scrollPos))
+                        {
+                            isInBounds = true;
+                            dropArea = bounds;
+                            break;
+                        }
+                    }
+
+                    Type draggedComponentType = null;
+                    if (isInBounds)
+                    {
+                        ResourceDragDropData dragData = DragDrop.Data as ResourceDragDropData;
+                        if (dragData != null)
+                        {
+                            foreach (var resPath in dragData.Paths)
+                            {
+                                LibraryEntry entry = ProjectLibrary.GetEntry(resPath);
+                                FileEntry fileEntry = entry as FileEntry;
+                                if (fileEntry != null)
+                                {
+                                    if (fileEntry.ResType == ResourceType.ScriptCode)
+                                    {
+                                        ScriptCode scriptFile = ProjectLibrary.Load<ScriptCode>(resPath);
+
+                                        if (scriptFile != null)
+                                        {
+                                            Type[] scriptTypes = scriptFile.Types;
+                                            foreach (var type in scriptTypes)
+                                            {
+                                                if (type.IsSubclassOf(typeof (Component)))
+                                                {
+                                                    draggedComponentType = type;
+                                                    isValidDrag = true;
+                                                    break;
+                                                }
+                                            }
+
+                                            if (draggedComponentType != null)
+                                                break;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    if (isValidDrag)
+                    {
+                        scrollAreaHighlight.Bounds = dropArea;
+
+                        if (DragDrop.DropInProgress)
+                        {
+                            activeSO.AddComponent(draggedComponentType);
+
+                            modifyState = InspectableState.Modified;
+                            EditorApplication.SetSceneDirty();
+                        }
+                    }
+                }               
+            }
+
+            if (scrollAreaHighlight != null)
+                scrollAreaHighlight.Active = isValidDrag;
+        }
+
+        /// <summary>
+        /// Triggered when the user selects a new resource or a scene object, or deselects everything.
+        /// </summary>
+        /// <param name="objects">A set of new scene objects that were selected.</param>
+        /// <param name="paths">A set of absolute resource paths that were selected.</param>
+        private void OnSelectionChanged(SceneObject[] objects, string[] paths)
+        {
+            if (currentType == InspectorType.SceneObject && modifyState == InspectableState.NotModified)
+                UndoRedo.PopCommand(undoCommandIdx);
+
+            Clear();
+            modifyState = InspectableState.NotModified;
+
+            if (objects.Length == 0 && paths.Length == 0)
+            {
+                currentType = InspectorType.None;
+                inspectorScrollArea = new GUIScrollArea();
+                GUI.AddElement(inspectorScrollArea);
+                inspectorLayout = inspectorScrollArea.Layout;
+
+                inspectorLayout.AddFlexibleSpace();
+                GUILayoutX layoutMsg = inspectorLayout.AddLayoutX();
+                layoutMsg.AddFlexibleSpace();
+                layoutMsg.AddElement(new GUILabel(new LocEdString("No object selected")));
+                layoutMsg.AddFlexibleSpace();
+                inspectorLayout.AddFlexibleSpace();
+            }
+            else if ((objects.Length + paths.Length) > 1)
+            {
+                currentType = InspectorType.None;
+                inspectorScrollArea = new GUIScrollArea();
+                GUI.AddElement(inspectorScrollArea);
+                inspectorLayout = inspectorScrollArea.Layout;
+
+                inspectorLayout.AddFlexibleSpace();
+                GUILayoutX layoutMsg = inspectorLayout.AddLayoutX();
+                layoutMsg.AddFlexibleSpace();
+                layoutMsg.AddElement(new GUILabel(new LocEdString("Multiple objects selected")));
+                layoutMsg.AddFlexibleSpace();
+                inspectorLayout.AddFlexibleSpace();
+            }
+            else if (objects.Length == 1)
+            {
+                if (objects[0] != null)
+                {
+                    UndoRedo.RecordSO(objects[0]);
+                    undoCommandIdx = UndoRedo.TopCommandId;
+
+                    SetObjectToInspect(objects[0]);
+                }
+            }
+            else if (paths.Length == 1)
+            {
+                SetObjectToInspect(paths[0]);
+            }
+        }
+
+        /// <summary>
+        /// Triggered when the user closes or expands a component foldout, making the component fields visible or hidden.
+        /// </summary>
+        /// <param name="inspectorData">Contains GUI data for the component that was toggled.</param>
+        /// <param name="expanded">Determines whether to display or hide component contents.</param>
+        private void OnComponentFoldoutToggled(InspectorComponent inspectorData, bool expanded)
+        {
+            inspectorData.expanded = expanded;
+            inspectorData.inspector.SetVisible(expanded);
+        }
+
+        /// <summary>
+        /// Triggered when the user clicks the component remove button. Removes that component from the active scene object.
+        /// </summary>
+        /// <param name="componentType">Type of the component to remove.</param>
+        private void OnComponentRemoveClicked(Type componentType)
+        {
+            if (activeSO != null)
+            {
+                activeSO.RemoveComponent(componentType);
+
+                modifyState = InspectableState.Modified;
+                EditorApplication.SetSceneDirty();
+            }
+        }
+
+        /// <summary>
+        /// Destroys all inspector GUI elements.
+        /// </summary>
+        internal void Clear()
+        {
+            for (int i = 0; i < inspectorComponents.Count; i++)
+            {
+                inspectorComponents[i].foldout.Destroy();
+                inspectorComponents[i].removeBtn.Destroy();
+                inspectorComponents[i].inspector.Destroy();
+            }
+
+            inspectorComponents.Clear();
+
+            if (inspectorResource != null)
+            {
+                inspectorResource.inspector.Destroy();
+                inspectorResource = null;
+            }
+
+            if (inspectorScrollArea != null)
+            {
+                inspectorScrollArea.Destroy();
+                inspectorScrollArea = null;
+            }
+
+            if (scrollAreaHighlight != null)
+            {
+                scrollAreaHighlight.Destroy();
+                scrollAreaHighlight = null;
+            }
+
+            if (highlightPanel != null)
+            {
+                highlightPanel.Destroy();
+                highlightPanel = null;
+            }
+
+            activeSO = null;
+            soNameInput = null;
+            soPrefabLayout = null;
+            soHasPrefab = false;
+            soPosX = null;
+            soPosY = null;
+            soPosZ = null;
+            soRotX = null;
+            soRotY = null;
+            soRotZ = null;
+            soScaleX = null;
+            soScaleY = null;
+            soScaleZ = null;
+            dropAreas = new Rect2I[0];
+
+            activeResource = null;
+            currentType = InspectorType.None;
+        }
+
+        /// <summary>
+        /// Returns the size of the title bar area that is displayed for <see cref="SceneObject"/> specific fields.
+        /// </summary>
+        /// <returns>Area of the title bar, relative to the window.</returns>
+        private Rect2I GetTitleBounds()
+        {
+            return new Rect2I(0, 0, Width, 115);
+        }
+
+        /// <summary>
+        /// Triggered when the user changes the name of the currently active scene object.
+        /// </summary>
+        private void OnSceneObjectRename(string name)
+        {
+            if (activeSO != null)
+            {
+                activeSO.Name = name;
+
+                modifyState |= InspectableState.ModifyInProgress;
+                EditorApplication.SetSceneDirty();
+            }
+        }
+
+        /// <summary>
+        /// Triggered when the scene object modification is confirmed by the user.
+        /// </summary>
+        private void OnModifyConfirm()
+        {
+            if (modifyState.HasFlag(InspectableState.ModifyInProgress))
+                modifyState = InspectableState.Modified;
+        }
+
+        /// <summary>
+        /// Triggered when the position value in the currently active <see cref="SceneObject"/> changes. Updates the 
+        /// necessary GUI elements.
+        /// </summary>
+        /// <param name="idx">Index of the coordinate that was changed.</param>
+        /// <param name="value">New value of the field.</param>
+        private void OnPositionChanged(int idx, float value)
+        {
+            if (activeSO == null)
+                return;
+
+            if (EditorApplication.ActiveCoordinateMode == HandleCoordinateMode.World)
+            {
+                Vector3 position = activeSO.Position;
+                position[idx] = value;
+                activeSO.Position = position;
+            }
+            else
+            {
+                Vector3 position = activeSO.LocalPosition;
+                position[idx] = value;
+                activeSO.LocalPosition = position;
+            }
+
+            modifyState = InspectableState.ModifyInProgress;
+            EditorApplication.SetSceneDirty();
+        }
+
+        /// <summary>
+        /// Triggered when the rotation value in the currently active <see cref="SceneObject"/> changes. Updates the 
+        /// necessary GUI elements.
+        /// </summary>
+        /// <param name="idx">Index of the euler angle that was changed (0 - X, 1 - Y, 2 - Z).</param>
+        /// <param name="value">New value of the field.</param>
+        private void OnRotationChanged(int idx, float value)
+        {
+            if (activeSO == null)
+                return;
+
+            if (EditorApplication.ActiveCoordinateMode == HandleCoordinateMode.World)
+            {
+                Vector3 angles = activeSO.Rotation.ToEuler();
+                angles[idx] = value;
+                activeSO.Rotation = Quaternion.FromEuler(angles);
+            }
+            else
+            {
+                Vector3 angles = activeSO.LocalRotation.ToEuler();
+                angles[idx] = value;
+                activeSO.LocalRotation = Quaternion.FromEuler(angles);
+            }
+
+            modifyState = InspectableState.ModifyInProgress;
+            EditorApplication.SetSceneDirty();
+        }
+
+        /// <summary>
+        /// Triggered when the scale value in the currently active <see cref="SceneObject"/> changes. Updates the 
+        /// necessary GUI elements.
+        /// </summary>
+        /// <param name="idx">Index of the coordinate that was changed.</param>
+        /// <param name="value">New value of the field.</param>
+        private void OnScaleChanged(int idx, float value)
+        {
+            if (activeSO == null)
+                return;
+
+            Vector3 scale = activeSO.LocalScale;
+            scale[idx] = value;
+            activeSO.LocalScale = scale;
+
+            modifyState = InspectableState.ModifyInProgress;
+            EditorApplication.SetSceneDirty();
+        }
+
+        /// <inheritdoc/>
+        protected override void WindowResized(int width, int height)
+        {
+            base.WindowResized(width, height);
+
+            UpdateDropAreas();
+        }
+
+        /// <summary>
+        /// Updates drop areas used for dragging and dropping components on the inspector.
+        /// </summary>
+        private void UpdateDropAreas()
+        {
+            if (activeSO == null)
+                return;
+
+            Rect2I contentBounds = inspectorLayout.Bounds;
+            dropAreas = new Rect2I[inspectorComponents.Count + 1];
+            int yOffset = GetTitleBounds().height;
+            for (int i = 0; i < inspectorComponents.Count; i++)
+            {
+                dropAreas[i] = new Rect2I(0, yOffset, contentBounds.width, COMPONENT_SPACING);
+                yOffset += inspectorComponents[i].title.Bounds.height + inspectorComponents[i].panel.Bounds.height + COMPONENT_SPACING;
+            }
+
+            dropAreas[dropAreas.Length - 1] = new Rect2I(0, yOffset, contentBounds.width, contentBounds.height - yOffset);
+        }
+    }
+}

+ 5 - 5
MBansheeEditor/MenuItems.cs

@@ -22,7 +22,7 @@ namespace BansheeEditor
             if (so == null)
                 return;
 
-            UndoRedo.RecordSO(so, "Added a Camera component");
+            UndoRedo.RecordSO(so, false, "Added a Camera component");
             Camera cam = so.AddComponent<Camera>();
             cam.Main = true;
 
@@ -39,7 +39,7 @@ namespace BansheeEditor
             if (so == null)
                 return;
 
-            UndoRedo.RecordSO(so, "Added a Renderable component");
+            UndoRedo.RecordSO(so, false, "Added a Renderable component");
             so.AddComponent<Renderable>();
             EditorApplication.SetSceneDirty();
         }
@@ -54,7 +54,7 @@ namespace BansheeEditor
             if (so == null)
                 return;
 
-            UndoRedo.RecordSO(so, "Added a Light component");
+            UndoRedo.RecordSO(so, false, "Added a Light component");
             Light light = so.AddComponent<Light>();
             light.Type = LightType.Point;
             EditorApplication.SetSceneDirty();
@@ -70,7 +70,7 @@ namespace BansheeEditor
             if (so == null)
                 return;
 
-            UndoRedo.RecordSO(so, "Added a Light component");
+            UndoRedo.RecordSO(so, false, "Added a Light component");
             Light light = so.AddComponent<Light>();
             light.Type = LightType.Spot;
             EditorApplication.SetSceneDirty();
@@ -86,7 +86,7 @@ namespace BansheeEditor
             if (so == null)
                 return;
 
-            UndoRedo.RecordSO(so, "Added a Light component");
+            UndoRedo.RecordSO(so, false, "Added a Light component");
             Light light = so.AddComponent<Light>();
             light.Type = LightType.Directional;
             EditorApplication.SetSceneDirty();

+ 17 - 1
MBansheeEditor/UndoRedo.cs

@@ -46,8 +46,9 @@ namespace BansheeEditor
         /// values as needed.
         /// </summary>
         /// <param name="so">Scene object to record.</param>
+        /// <param name="recordHierarchy">If true all children of this object will also be recorded.</param>
         /// <param name="description">Optional description of what exactly the command does.</param>
-        public static void RecordSO(SceneObject so, string description = "")
+        public static void RecordSO(SceneObject so, bool recordHierarchy = false, string description = "")
         {
             if (so != null)
                 Internal_RecordSO(so.GetCachedPtr(), description);
@@ -172,6 +173,18 @@ namespace BansheeEditor
             }
         }
 
+        /// <summary>
+        /// Breaks the prefab link on the provided scene object and makes the operation undo-able. 
+        /// See <see cref="PrefabUtility.BreakPrefab"/>.
+        /// </summary>
+        /// <param name="so">Scene object whose prefab link to break.</param>
+        /// <param name="description">Optional description of what exactly the command does.</param>
+        public static void BreakPrefab(SceneObject so, string description = "")
+        {
+            if (so != null)
+                Internal_BreakPrefab(so.GetCachedPtr(), description);
+        }
+
         /// <summary>
         /// Creates a new undo/redo group. All new commands will be registered to this group. You may remove the group and 
         /// all of its commands by calling <see cref="PopGroup"/>.
@@ -241,5 +254,8 @@ namespace BansheeEditor
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         internal static extern void Internal_ReparentSOMulti(IntPtr[] soPtr, IntPtr parentSOPtr, string description);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        internal static extern void Internal_BreakPrefab(IntPtr soPtr, string description);
     }
 }

+ 37 - 36
SBansheeEditor/Include/BsScriptUndoRedo.h

@@ -1,37 +1,38 @@
-#pragma once
-
-#include "BsScriptEditorPrerequisites.h"
-#include "BsScriptObject.h"
-
-namespace BansheeEngine
-{
-	/**
-	 * @brief	Interop class between C++ & CLR for UndoRedo.
-	 */
-	class BS_SCR_BED_EXPORT ScriptUndoRedo : public ScriptObject <ScriptUndoRedo>
-	{
-	public:
-		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "UndoRedo");
-
-	private:
-		ScriptUndoRedo(MonoObject* instance);
-
-		/************************************************************************/
-		/* 								CLR HOOKS						   		*/
-		/************************************************************************/
-		static void internal_Undo();
-		static void internal_Redo();
-		static void internal_PushGroup(MonoString* name);
-		static void internal_PopGroup(MonoString* name);
-		static UINT32 internal_GetTopCommandId();
-		static void internal_PopCommand(UINT32 id);
-		static void internal_RecordSO(ScriptSceneObject* soPtr, MonoString* description);
-		static MonoObject* internal_CloneSO(ScriptSceneObject* soPtr, MonoString* description);
-		static MonoArray* internal_CloneSOMulti(MonoArray* soPtrs, MonoString* description);
-		static MonoObject* internal_Instantiate(ScriptPrefab* prefabPtr, MonoString* description);
-		static MonoObject* internal_CreateSO(MonoString* name, MonoString* description);
-		static void internal_DeleteSO(ScriptSceneObject* soPtr, MonoString* description);
-		static void internal_ReparentSO(ScriptSceneObject* soPtr, ScriptSceneObject* parentSOPtr, MonoString* description);
-		static void internal_ReparentSOMulti(MonoArray* soPtrs, ScriptSceneObject* parentSOPtr, MonoString* description);
-	};
+#pragma once
+
+#include "BsScriptEditorPrerequisites.h"
+#include "BsScriptObject.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Interop class between C++ & CLR for UndoRedo.
+	 */
+	class BS_SCR_BED_EXPORT ScriptUndoRedo : public ScriptObject <ScriptUndoRedo>
+	{
+	public:
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "UndoRedo");
+
+	private:
+		ScriptUndoRedo(MonoObject* instance);
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static void internal_Undo();
+		static void internal_Redo();
+		static void internal_PushGroup(MonoString* name);
+		static void internal_PopGroup(MonoString* name);
+		static UINT32 internal_GetTopCommandId();
+		static void internal_PopCommand(UINT32 id);
+		static void internal_RecordSO(ScriptSceneObject* soPtr, bool recordHierarchy, MonoString* description);
+		static MonoObject* internal_CloneSO(ScriptSceneObject* soPtr, MonoString* description);
+		static MonoArray* internal_CloneSOMulti(MonoArray* soPtrs, MonoString* description);
+		static MonoObject* internal_Instantiate(ScriptPrefab* prefabPtr, MonoString* description);
+		static MonoObject* internal_CreateSO(MonoString* name, MonoString* description);
+		static void internal_DeleteSO(ScriptSceneObject* soPtr, MonoString* description);
+		static void internal_ReparentSO(ScriptSceneObject* soPtr, ScriptSceneObject* parentSOPtr, MonoString* description);
+		static void internal_ReparentSOMulti(MonoArray* soPtrs, ScriptSceneObject* parentSOPtr, MonoString* description);
+		static void internal_BreakPrefab(ScriptSceneObject* soPtr, MonoString* description);
+	};
 }

+ 175 - 165
SBansheeEditor/Source/BsScriptUndoRedo.cpp

@@ -1,166 +1,176 @@
-#include "BsScriptUndoRedo.h"
-#include "BsScriptMeta.h"
-#include "BsMonoClass.h"
-#include "BsMonoMethod.h"
-#include "BsScriptSceneObject.h"
-#include "BsMonoUtil.h"
-#include "BsScriptGameObjectManager.h"
-#include "BsUndoRedo.h"
-#include "BsCmdRecordSO.h"
-#include "BsCmdCloneSO.h"
-#include "BsCmdCreateSO.h"
-#include "BsCmdDeleteSO.h"
-#include "BsCmdInstantiateSO.h"
-#include "BsCmdReparentSO.h"
-#include "BsScriptPrefab.h"
-#include "BsPrefab.h"
-
-namespace BansheeEngine
-{
-	ScriptUndoRedo::ScriptUndoRedo(MonoObject* instance)
-		:ScriptObject(instance)
-	{
-
-	}
-
-	void ScriptUndoRedo::initRuntimeData()
-	{
-		metaData.scriptClass->addInternalCall("Internal_Undo", &ScriptUndoRedo::internal_Undo);
-		metaData.scriptClass->addInternalCall("Internal_Redo", &ScriptUndoRedo::internal_Redo);
-		metaData.scriptClass->addInternalCall("Internal_PushGroup", &ScriptUndoRedo::internal_PushGroup);
-		metaData.scriptClass->addInternalCall("Internal_PopGroup", &ScriptUndoRedo::internal_PopGroup);
-		metaData.scriptClass->addInternalCall("Internal_GetTopCommandId", &ScriptUndoRedo::internal_GetTopCommandId);
-		metaData.scriptClass->addInternalCall("Internal_PopCommand", &ScriptUndoRedo::internal_PopCommand);
-		metaData.scriptClass->addInternalCall("Internal_RecordSO", &ScriptUndoRedo::internal_RecordSO);
-		metaData.scriptClass->addInternalCall("Internal_CloneSO", &ScriptUndoRedo::internal_CloneSO);
-		metaData.scriptClass->addInternalCall("Internal_CloneSOMulti", &ScriptUndoRedo::internal_CloneSOMulti);
-		metaData.scriptClass->addInternalCall("Internal_Instantiate", &ScriptUndoRedo::internal_Instantiate);
-		metaData.scriptClass->addInternalCall("Internal_CreateSO", &ScriptUndoRedo::internal_CreateSO);
-		metaData.scriptClass->addInternalCall("Internal_DeleteSO", &ScriptUndoRedo::internal_DeleteSO);
-		metaData.scriptClass->addInternalCall("Internal_ReparentSO", &ScriptUndoRedo::internal_ReparentSO);
-		metaData.scriptClass->addInternalCall("Internal_ReparentSOMulti", &ScriptUndoRedo::internal_ReparentSOMulti);
-	}
-
-	void ScriptUndoRedo::internal_Undo()
-	{
-		UndoRedo::instance().undo();
-	}
-
-	void ScriptUndoRedo::internal_Redo()
-	{
-		UndoRedo::instance().redo();
-	}
-
-	void ScriptUndoRedo::internal_PushGroup(MonoString* name)
-	{
-		String nativeName = MonoUtil::monoToString(name);
-		UndoRedo::instance().pushGroup(nativeName);
-	}
-
-	void ScriptUndoRedo::internal_PopGroup(MonoString* name)
-	{
-		String nativeName = MonoUtil::monoToString(name);
-		UndoRedo::instance().popGroup(nativeName);
-	}
-
-	UINT32 ScriptUndoRedo::internal_GetTopCommandId()
-	{
-		return UndoRedo::instance().getTopCommandId();
-	}
-
-	void ScriptUndoRedo::internal_PopCommand(UINT32 id)
-	{
-		UndoRedo::instance().popCommand(id);
-	}
-
-	void ScriptUndoRedo::internal_RecordSO(ScriptSceneObject* soPtr, MonoString* description)
-	{
-		WString nativeDescription = MonoUtil::monoToWString(description);
-		CmdRecordSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
-	}
-
-	MonoObject* ScriptUndoRedo::internal_CloneSO(ScriptSceneObject* soPtr, MonoString* description)
-	{
-		WString nativeDescription = MonoUtil::monoToWString(description);
-		HSceneObject clone = CmdCloneSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
-
-		ScriptSceneObject* cloneSoPtr = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(clone);
-		return cloneSoPtr->getManagedInstance();
-	}
-
-	MonoArray* ScriptUndoRedo::internal_CloneSOMulti(MonoArray* soPtrs, MonoString* description)
-	{
-		WString nativeDescription = MonoUtil::monoToWString(description);
-
-		ScriptArray input(soPtrs);
-		ScriptArray output = ScriptArray::create<ScriptSceneObject>(input.size());
-
-		for (UINT32 i = 0; i < input.size(); i++)
-		{
-			ScriptSceneObject* soPtr = input.get<ScriptSceneObject*>(i);
-			HSceneObject clone = CmdCloneSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
-
-			ScriptSceneObject* cloneSoPtr = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(clone);
-			output.set(i, cloneSoPtr->getManagedInstance());
-		}
-
-		return output.getInternal();
-	}
-
-	MonoObject* ScriptUndoRedo::internal_Instantiate(ScriptPrefab* prefabPtr, MonoString* description)
-	{
-		HPrefab prefab = prefabPtr->getHandle();
-		if (!prefab.isLoaded())
-			return nullptr;
-
-		WString nativeDescription = MonoUtil::monoToWString(description);
-		HSceneObject clone = CmdInstantiateSO::execute(prefab, nativeDescription);
-
-		ScriptSceneObject* cloneSoPtr = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(clone);
-		return cloneSoPtr->getManagedInstance();
-	}
-
-	MonoObject* ScriptUndoRedo::internal_CreateSO(MonoString* name, MonoString* description)
-	{
-		String nativeName = MonoUtil::monoToString(name);
-		WString nativeDescription = MonoUtil::monoToWString(description);
-		HSceneObject newObj = CmdCreateSO::execute(nativeName, 0, nativeDescription);
-
-		return ScriptGameObjectManager::instance().createScriptSceneObject(newObj)->getManagedInstance();
-	}
-
-	void ScriptUndoRedo::internal_DeleteSO(ScriptSceneObject* soPtr, MonoString* description)
-	{
-		WString nativeDescription = MonoUtil::monoToWString(description);
-		CmdDeleteSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
-	}
-
-	void ScriptUndoRedo::internal_ReparentSO(ScriptSceneObject* soPtr, ScriptSceneObject* parentSOPtr, MonoString* description)
-	{
-		HSceneObject parent;
-		if (parentSOPtr != nullptr)
-			parent = parentSOPtr->getNativeSceneObject();
-
-		WString nativeDescription = MonoUtil::monoToWString(description);
-
-		HSceneObject so = soPtr->getNativeSceneObject();
-		CmdReparentSO::execute(so, parent, nativeDescription);
-	}
-
-	void ScriptUndoRedo::internal_ReparentSOMulti(MonoArray* soPtrs, ScriptSceneObject* parentSOPtr, MonoString* description)
-	{
-		HSceneObject parent;
-		if (parentSOPtr != nullptr)
-			parent = parentSOPtr->getNativeSceneObject();
-
-		WString nativeDescription = MonoUtil::monoToWString(description);
-
-		ScriptArray input(soPtrs);
-		for (UINT32 i = 0; i < input.size(); i++)
-		{
-			ScriptSceneObject* soPtr = input.get<ScriptSceneObject*>(i);
-			HSceneObject so = soPtr->getNativeSceneObject();
-			CmdReparentSO::execute(so, parent, nativeDescription);
-		}
-	}
+#include "BsScriptUndoRedo.h"
+#include "BsScriptMeta.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+#include "BsScriptSceneObject.h"
+#include "BsMonoUtil.h"
+#include "BsScriptGameObjectManager.h"
+#include "BsUndoRedo.h"
+#include "BsCmdRecordSO.h"
+#include "BsCmdCloneSO.h"
+#include "BsCmdCreateSO.h"
+#include "BsCmdDeleteSO.h"
+#include "BsCmdInstantiateSO.h"
+#include "BsCmdReparentSO.h"
+#include "BsCmdBreakPrefab.h"
+#include "BsScriptPrefab.h"
+#include "BsPrefab.h"
+
+namespace BansheeEngine
+{
+	ScriptUndoRedo::ScriptUndoRedo(MonoObject* instance)
+		:ScriptObject(instance)
+	{
+
+	}
+
+	void ScriptUndoRedo::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_Undo", &ScriptUndoRedo::internal_Undo);
+		metaData.scriptClass->addInternalCall("Internal_Redo", &ScriptUndoRedo::internal_Redo);
+		metaData.scriptClass->addInternalCall("Internal_PushGroup", &ScriptUndoRedo::internal_PushGroup);
+		metaData.scriptClass->addInternalCall("Internal_PopGroup", &ScriptUndoRedo::internal_PopGroup);
+		metaData.scriptClass->addInternalCall("Internal_GetTopCommandId", &ScriptUndoRedo::internal_GetTopCommandId);
+		metaData.scriptClass->addInternalCall("Internal_PopCommand", &ScriptUndoRedo::internal_PopCommand);
+		metaData.scriptClass->addInternalCall("Internal_RecordSO", &ScriptUndoRedo::internal_RecordSO);
+		metaData.scriptClass->addInternalCall("Internal_CloneSO", &ScriptUndoRedo::internal_CloneSO);
+		metaData.scriptClass->addInternalCall("Internal_CloneSOMulti", &ScriptUndoRedo::internal_CloneSOMulti);
+		metaData.scriptClass->addInternalCall("Internal_Instantiate", &ScriptUndoRedo::internal_Instantiate);
+		metaData.scriptClass->addInternalCall("Internal_CreateSO", &ScriptUndoRedo::internal_CreateSO);
+		metaData.scriptClass->addInternalCall("Internal_DeleteSO", &ScriptUndoRedo::internal_DeleteSO);
+		metaData.scriptClass->addInternalCall("Internal_ReparentSO", &ScriptUndoRedo::internal_ReparentSO);
+		metaData.scriptClass->addInternalCall("Internal_ReparentSOMulti", &ScriptUndoRedo::internal_ReparentSOMulti);
+		metaData.scriptClass->addInternalCall("Internal_BreakPrefab", &ScriptUndoRedo::internal_BreakPrefab);
+	}
+
+	void ScriptUndoRedo::internal_Undo()
+	{
+		UndoRedo::instance().undo();
+	}
+
+	void ScriptUndoRedo::internal_Redo()
+	{
+		UndoRedo::instance().redo();
+	}
+
+	void ScriptUndoRedo::internal_PushGroup(MonoString* name)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+		UndoRedo::instance().pushGroup(nativeName);
+	}
+
+	void ScriptUndoRedo::internal_PopGroup(MonoString* name)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+		UndoRedo::instance().popGroup(nativeName);
+	}
+
+	UINT32 ScriptUndoRedo::internal_GetTopCommandId()
+	{
+		return UndoRedo::instance().getTopCommandId();
+	}
+
+	void ScriptUndoRedo::internal_PopCommand(UINT32 id)
+	{
+		UndoRedo::instance().popCommand(id);
+	}
+
+	void ScriptUndoRedo::internal_RecordSO(ScriptSceneObject* soPtr, bool recordHierarchy, MonoString* description)
+	{
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		CmdRecordSO::execute(soPtr->getNativeSceneObject(), recordHierarchy, nativeDescription);
+	}
+
+	MonoObject* ScriptUndoRedo::internal_CloneSO(ScriptSceneObject* soPtr, MonoString* description)
+	{
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		HSceneObject clone = CmdCloneSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
+
+		ScriptSceneObject* cloneSoPtr = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(clone);
+		return cloneSoPtr->getManagedInstance();
+	}
+
+	MonoArray* ScriptUndoRedo::internal_CloneSOMulti(MonoArray* soPtrs, MonoString* description)
+	{
+		WString nativeDescription = MonoUtil::monoToWString(description);
+
+		ScriptArray input(soPtrs);
+		ScriptArray output = ScriptArray::create<ScriptSceneObject>(input.size());
+
+		for (UINT32 i = 0; i < input.size(); i++)
+		{
+			ScriptSceneObject* soPtr = input.get<ScriptSceneObject*>(i);
+			HSceneObject clone = CmdCloneSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
+
+			ScriptSceneObject* cloneSoPtr = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(clone);
+			output.set(i, cloneSoPtr->getManagedInstance());
+		}
+
+		return output.getInternal();
+	}
+
+	MonoObject* ScriptUndoRedo::internal_Instantiate(ScriptPrefab* prefabPtr, MonoString* description)
+	{
+		HPrefab prefab = prefabPtr->getHandle();
+		if (!prefab.isLoaded())
+			return nullptr;
+
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		HSceneObject clone = CmdInstantiateSO::execute(prefab, nativeDescription);
+
+		ScriptSceneObject* cloneSoPtr = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(clone);
+		return cloneSoPtr->getManagedInstance();
+	}
+
+	MonoObject* ScriptUndoRedo::internal_CreateSO(MonoString* name, MonoString* description)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		HSceneObject newObj = CmdCreateSO::execute(nativeName, 0, nativeDescription);
+
+		return ScriptGameObjectManager::instance().createScriptSceneObject(newObj)->getManagedInstance();
+	}
+
+	void ScriptUndoRedo::internal_DeleteSO(ScriptSceneObject* soPtr, MonoString* description)
+	{
+		WString nativeDescription = MonoUtil::monoToWString(description);
+		CmdDeleteSO::execute(soPtr->getNativeSceneObject(), nativeDescription);
+	}
+
+	void ScriptUndoRedo::internal_ReparentSO(ScriptSceneObject* soPtr, ScriptSceneObject* parentSOPtr, MonoString* description)
+	{
+		HSceneObject parent;
+		if (parentSOPtr != nullptr)
+			parent = parentSOPtr->getNativeSceneObject();
+
+		WString nativeDescription = MonoUtil::monoToWString(description);
+
+		HSceneObject so = soPtr->getNativeSceneObject();
+		CmdReparentSO::execute(so, parent, nativeDescription);
+	}
+
+	void ScriptUndoRedo::internal_ReparentSOMulti(MonoArray* soPtrs, ScriptSceneObject* parentSOPtr, MonoString* description)
+	{
+		HSceneObject parent;
+		if (parentSOPtr != nullptr)
+			parent = parentSOPtr->getNativeSceneObject();
+
+		WString nativeDescription = MonoUtil::monoToWString(description);
+
+		ScriptArray input(soPtrs);
+		for (UINT32 i = 0; i < input.size(); i++)
+		{
+			ScriptSceneObject* soPtr = input.get<ScriptSceneObject*>(i);
+			HSceneObject so = soPtr->getNativeSceneObject();
+			CmdReparentSO::execute(so, parent, nativeDescription);
+		}
+	}
+
+	void ScriptUndoRedo::internal_BreakPrefab(ScriptSceneObject* soPtr, MonoString* description)
+	{
+		WString nativeDescription = MonoUtil::monoToWString(description);
+
+		HSceneObject so = soPtr->getNativeSceneObject();
+		CmdBreakPrefab::execute(so, nativeDescription);
+	}
 }