Browse Source

WIP Prefabs and PrefabDiff

Marko Pintera 10 years ago
parent
commit
57733a8660

+ 6 - 0
BansheeCore/BansheeCore.vcxproj

@@ -282,6 +282,10 @@
     <ClInclude Include="Include\BsDrawList.h" />
     <ClInclude Include="Include\BsMeshImportOptions.h" />
     <ClInclude Include="Include\BsMeshImportOptionsRTTI.h" />
+    <ClInclude Include="Include\BsPrefab.h" />
+    <ClInclude Include="Include\BsPrefabDiff.h" />
+    <ClInclude Include="Include\BsPrefabDiffRTTI.h" />
+    <ClInclude Include="Include\BsPrefabRTTI.h" />
     <ClInclude Include="Include\BsShaderIncludeRTTI.h" />
     <ClInclude Include="Include\BsIResourceListener.h" />
     <ClInclude Include="Include\BsMaterialParam.h" />
@@ -434,6 +438,8 @@
     <ClCompile Include="Source\BsIResourceListener.cpp" />
     <ClCompile Include="Source\BsMaterialParam.cpp" />
     <ClCompile Include="Source\BsMeshImportOptions.cpp" />
+    <ClCompile Include="Source\BsPrefab.cpp" />
+    <ClCompile Include="Source\BsPrefabDiff.cpp" />
     <ClCompile Include="Source\BsProfilerCPU.cpp" />
     <ClCompile Include="Source\BsDeferredCallManager.cpp" />
     <ClCompile Include="Source\BsDrawOps.cpp" />

+ 18 - 0
BansheeCore/BansheeCore.vcxproj.filters

@@ -530,6 +530,18 @@
     <ClInclude Include="Include\BsMeshImportOptionsRTTI.h">
       <Filter>Header Files\RTTI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsPrefab.h">
+      <Filter>Header Files\Scene</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsPrefabRTTI.h">
+      <Filter>Header Files\RTTI</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsPrefabDiff.h">
+      <Filter>Header Files\Scene</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsPrefabDiffRTTI.h">
+      <Filter>Header Files\RTTI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsCoreApplication.cpp">
@@ -838,5 +850,11 @@
     <ClCompile Include="Source\BsMeshImportOptions.cpp">
       <Filter>Source Files\Importer</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsPrefab.cpp">
+      <Filter>Source Files\Scene</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsPrefabDiff.cpp">
+      <Filter>Source Files\Scene</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 8 - 1
BansheeCore/Include/BsCorePrerequisites.h

@@ -166,6 +166,7 @@ namespace BansheeEngine
 	class IResourceListener;
 	class TextureProperties;
 	class IShaderIncludeHandler;
+	class Prefab;
 	// Asset import
 	class SpecificImporter;
 	class Importer;
@@ -262,6 +263,7 @@ namespace BansheeEngine
 	typedef std::shared_ptr<GpuParamDesc> GpuParamDescPtr;
 	typedef std::shared_ptr<ResourceMetaData> ResourceMetaDataPtr;
 	typedef std::shared_ptr<IShaderIncludeHandler> ShaderIncludeHandlerPtr;
+	typedef std::shared_ptr<Prefab> PrefabPtr;
 }
 
 /************************************************************************/
@@ -328,7 +330,11 @@ namespace BansheeEngine
 		TID_Viewport = 1073,
 		TID_ResourceDependencies = 1074,
 		TID_ShaderMetaData = 1075,
-		TID_MeshImportOptions = 1076
+		TID_MeshImportOptions = 1076,
+		TID_Prefab = 1077,
+		TID_PrefabDiff = 1078,
+		TID_PrefabObjectDiff = 1079,
+		TID_PrefabComponentDiff = 1080
 	};
 }
 
@@ -348,6 +354,7 @@ namespace BansheeEngine
 	typedef ResourceHandle<ShaderInclude> HShaderInclude;
 	typedef ResourceHandle<Font> HFont;
 	typedef ResourceHandle<Shader> HShader;
+	typedef ResourceHandle<Prefab> HPrefab;
 }
 
 namespace BansheeEngine

+ 10 - 0
BansheeCore/Include/BsGameObject.h

@@ -35,6 +35,13 @@ namespace BansheeEngine
 		 */
 		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.
+		 */
+		INT32 getLinkId() const { return mLinkId; }
+
 		/**
 		 * @brief	Gets the name of the object.
 		 */
@@ -88,8 +95,11 @@ namespace BansheeEngine
 
 	protected:
 		String mName;
+		INT32 mLinkId;
 
 	private:
+		friend class Prefab;
+
 		GameObjectInstanceDataPtr mInstanceData;
 		bool mIsDestroyed;
 

+ 4 - 0
BansheeCore/Include/BsGameObjectRTTI.h

@@ -22,6 +22,9 @@ namespace BansheeEngine
 			GameObjectManager::instance().registerDeserializedId(instanceId, obj->getInstanceId());
 		}
 
+		INT32& getLinkId(GameObject* obj) { return obj->mLinkId; }
+		void setLinkId(GameObject* obj, INT32& linkId) { obj->mLinkId = linkId; }
+
 	public:
 		template <typename T>
 		static std::shared_ptr<T> createGameObject()
@@ -34,6 +37,7 @@ namespace BansheeEngine
 		{
 			addPlainField("mInstanceID", 0, &GameObjectRTTI::getInstanceID, &GameObjectRTTI::setInstanceID);
 			addPlainField("mName", 1, &GameObjectRTTI::getName, &GameObjectRTTI::setName);
+			addPlainField("mLinkId", 2, &GameObjectRTTI::getLinkId, &GameObjectRTTI::setLinkId);
 		}
 
 		virtual const String& getRTTIName()

+ 104 - 0
BansheeCore/Include/BsPrefabDiff.h

@@ -0,0 +1,104 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsIReflectable.h"
+#include "BsGameObject.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Contains differences between two components of the same type.
+	 *
+	 * @see		PrefabDiff
+	 */
+	struct BS_CORE_EXPORT PrefabComponentDiff : public IReflectable
+	{
+		INT32 id;
+		SPtr<SerializedObject> data;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+
+	public:
+		friend class PrefabComponentDiffRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const override;
+	};
+
+	/**
+	 * @brief	Contains a set of prefab differences for a single scene object.
+	 *
+	 * @see		PrefabDiff
+	 */
+	struct BS_CORE_EXPORT PrefabObjectDiff : public IReflectable
+	{
+		INT32 id;
+		String name;
+
+		Vector<SPtr<PrefabComponentDiff>> componentDiffs;
+		Vector<INT32> removedComponents;
+		Vector<SPtr<SerializedObject>> addedComponents;
+
+		Vector<SPtr<PrefabObjectDiff>> childDiffs;
+		Vector<INT32> removedChildren;
+		Vector<SPtr<SerializedObject>> addedChildren;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+
+	public:
+		friend class PrefabObjectDiffRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const override;
+	};
+
+	/**
+	 * @brief	Contains modifications between an prefab and its instance. The modifications are a set of
+	 *			added/removed children or components and per-field "diffs" of their components.
+	 */
+	class BS_CORE_EXPORT PrefabDiff : public IReflectable
+	{
+	public:
+		/**
+		 * @brief	Creates a new prefab diff by comparing the provided instanced scene object hierarchy
+		 *			with the prefab scene object hierarchy.
+		 */
+		static SPtr<PrefabDiff> create(const HSceneObject& prefab, const HSceneObject& instance);
+
+		/**
+		 * @brief	Applies the internal prefab diff to the provided object. The object should have
+		 *			similar hierarchy as the prefab the diff was created for, otherwise the results are
+		 *			undefined.
+		 */
+		void apply(const HSceneObject& object);
+
+	private:
+		/**
+		 * @brief	Recurses over every scene object in the prefab a generates differences between itself
+		 *			and the instanced version.
+		 *
+		 * @see		create
+		 */
+		static SPtr<PrefabObjectDiff> generateDiff(const HSceneObject& prefab, const HSceneObject& instance);
+
+		/**
+		 * @brief	Recursively applies a per-object set of prefab differences to a specific object.
+		 *
+		 * @see		apply			
+		 */
+		static void applyDiff(const SPtr<PrefabObjectDiff>& diff, const HSceneObject& object);
+
+		SPtr<PrefabObjectDiff> mRoot;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+
+	public:
+		friend class PrefabDiffRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const override;
+	};
+}

+ 109 - 0
BansheeCore/Include/BsPrefabDiffRTTI.h

@@ -0,0 +1,109 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsRTTIType.h"
+#include "BsPrefabDiff.h"
+
+namespace BansheeEngine
+{
+	class BS_CORE_EXPORT PrefabComponentDiffRTTI : public RTTIType < PrefabComponentDiff, IReflectable, PrefabComponentDiffRTTI >
+	{
+	private:
+		BS_PLAIN_MEMBER(id)
+		BS_REFLPTR_MEMBER(data);
+	public:
+		PrefabComponentDiffRTTI()
+		{
+			BS_ADD_PLAIN_FIELD(id, 0);
+			BS_ADD_REFLPTR_FIELD(data, 1);
+		}
+
+		virtual const String& getRTTIName() override
+		{
+			static String name = "PrefabComponentDiff";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId() override
+		{
+			return TID_PrefabComponentDiff;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject() override
+		{
+			return bs_shared_ptr<PrefabComponentDiff>();
+		}
+	};
+
+	class BS_CORE_EXPORT PrefabObjectDiffRTTI : public RTTIType < PrefabObjectDiff, IReflectable, PrefabObjectDiffRTTI >
+	{
+	private:
+		BS_PLAIN_MEMBER(id)
+		BS_PLAIN_MEMBER(name)
+
+		BS_REFLPTR_MEMBER_VEC(componentDiffs)
+		BS_PLAIN_MEMBER_VEC(removedComponents)
+		BS_REFLPTR_MEMBER_VEC(addedComponents)
+
+		BS_REFLPTR_MEMBER_VEC(childDiffs)
+		BS_PLAIN_MEMBER_VEC(removedChildren)
+		BS_REFLPTR_MEMBER_VEC(addedChildren)
+	public:
+		PrefabObjectDiffRTTI()
+		{
+			BS_ADD_PLAIN_FIELD(id, 0);
+			BS_ADD_PLAIN_FIELD(name, 1);
+
+			BS_ADD_REFLPTR_FIELD_ARR(componentDiffs, 2);
+			BS_ADD_PLAIN_FIELD_ARR(removedComponents, 3);
+			BS_ADD_REFLPTR_FIELD_ARR(addedComponents, 4);
+
+			BS_ADD_REFLPTR_FIELD_ARR(childDiffs, 5);
+			BS_ADD_PLAIN_FIELD_ARR(removedChildren, 6);
+			BS_ADD_REFLPTR_FIELD_ARR(addedChildren, 7);
+		}
+
+		virtual const String& getRTTIName() override
+		{
+			static String name = "PrefabObjectDiff";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId() override
+		{
+			return TID_PrefabObjectDiff;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject() override
+		{
+			return bs_shared_ptr<PrefabObjectDiff>();
+		}
+	};
+
+	class BS_CORE_EXPORT PrefabDiffRTTI : public RTTIType < PrefabDiff, IReflectable, PrefabDiffRTTI >
+	{
+	private:
+		BS_REFLPTR_MEMBER(mRoot);
+	public:
+		PrefabDiffRTTI()
+		{
+			BS_ADD_REFLPTR_FIELD(mRoot, 0);
+		}
+
+		virtual const String& getRTTIName() override
+		{
+			static String name = "PrefabDiff";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId() override
+		{
+			return TID_PrefabDiff;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject() override
+		{
+			return bs_shared_ptr<PrefabDiff>();
+		}
+	};
+}

+ 53 - 0
BansheeCore/Include/BsPrefabRTTI.h

@@ -0,0 +1,53 @@
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsRTTIType.h"
+#include "BsPrefab.h"
+#include "BsSceneObject.h"
+
+namespace BansheeEngine
+{
+	class BS_CORE_EXPORT PrefabRTTI : public RTTIType < Prefab, Resource, PrefabRTTI >
+	{
+	private:
+		SceneObjectPtr getSceneObject(Prefab* obj) { return obj->mRoot.getInternalPtr(); }
+		void setSceneObject(Prefab* obj, SceneObjectPtr value) { obj->mRoot = value->getHandle(); }
+
+	public:
+		PrefabRTTI()
+		{
+			addReflectablePtrField("mRoot", 0, &PrefabRTTI::getSceneObject, &PrefabRTTI::setSceneObject);
+		}
+
+		virtual void onDeserializationStarted(IReflectable* obj) override
+		{
+			Prefab* so = static_cast<Prefab*>(obj);
+
+			// TODO
+		}
+
+		virtual void onDeserializationEnded(IReflectable* obj) override
+		{
+			Prefab* so = static_cast<Prefab*>(obj);
+			// TODO
+
+			so->mRTTIData = nullptr;
+		}
+
+		virtual const String& getRTTIName() override
+		{
+			static String name = "Prefab";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId() override
+		{
+			return TID_Prefab;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject() override
+		{
+			return Prefab::createEmpty();
+		}
+	};
+}

+ 55 - 4
BansheeCore/Include/BsSceneObject.h

@@ -12,6 +12,15 @@
 
 namespace BansheeEngine
 {
+	/**
+	 * @brief	Possible modifiers that can be applied to a SceneObject
+	 */
+	enum SceneObjectFlags
+	{
+		SOF_DontInstantiate = 0x01, /**< Object wont be in the main scene and its components won't receive updates. */
+		SOF_DontSave = 0x02 /**< Object will be skipped when saving the scene hierarchy or a prefab. */
+	};
+
 	/**
 	 * @brief	SceneObject represents an object in the scene graph. It has a world position,
 	 *			place in the hierarchy and optionally a number of attached components.
@@ -28,6 +37,8 @@ namespace BansheeEngine
 		};
 
 		friend class CoreSceneManager;
+		friend class Prefab;
+		friend class PrefabDiff;
 	public:
 		~SceneObject();
 
@@ -35,7 +46,7 @@ namespace BansheeEngine
 		 * @brief	Creates a new SceneObject with the specified name. Object will be placed in the top
 		 *			of the scene hierarchy.
 		 */
-		static HSceneObject create(const String& name);
+		static HSceneObject create(const String& name, UINT32 flags = 0);
 
 		/**
 		 * @brief	Destroys this object and any of its held components.
@@ -55,10 +66,24 @@ namespace BansheeEngine
 		 * @brief	Returns a handle to this object.
 		 */
 		HSceneObject getHandle() const { return mThisHandle; }
+
+		/**
+		 * @brief	Returns the prefab this object is linked to, if any. 
+		 *
+		 * @note	Requires a search of all parents potentially.
+		 */
+		HPrefab getPrefabLink() const;
 	private:
-		SceneObject(const String& name);
+		SceneObject(const String& name, UINT32 flags);
 
-		static HSceneObject createInternal(const String& name);
+		/**
+		 * @brief	Creates a new SceneObject instance, registers it with the game object manager,
+		 *			creates and returns a handle to the new object.
+		 *
+		 * @note	When creating objects with DontInstantiate flag it is the callers responsibility
+		 *			to manually destroy the object, otherwise it will leak.
+		 */
+		static HSceneObject createInternal(const String& name, UINT32 flags = 0);
 
 		/**
 		 * @brief	Destroys this object and any of its held components.
@@ -71,8 +96,32 @@ namespace BansheeEngine
 		 */
 		void destroyInternal(bool immediate = false);
 
+		/**
+		 * @brief	Recursively enables the provided set of flags on
+		 *			this object and all children.
+		 */
+		void setFlags(UINT32 flags);
+
+		/**
+		 * @brief	Recursively disables the provided set of flags on
+		 *			this object and all children.
+		 */
+		void unsetFlags(UINT32 flags);
+
+		/**
+		 * @brief	Checks is the scene object instantiated and visible in the scene.
+		 */
+		bool isInstantiated() const { return (mFlags & SOF_DontInstantiate) == 0; }
+
+		/**
+		 * @brief	Register the scene object with the scene and activate all of its components.
+		 */
+		void instantiate();
+
 	private:
 		HSceneObject mThisHandle;
+		HPrefab mPrefabLink;
+		UINT32 mFlags;
 
 		/************************************************************************/
 		/* 								Transform	                     		*/
@@ -411,7 +460,9 @@ namespace BansheeEngine
 				GameObjectHandle<T>(GameObjectManager::instance().registerObject(gameObject));
 
 			mComponents.push_back(newComponent);
-			newComponent->onInitialized();
+
+			if (isInstantiated())
+				newComponent->onInitialized();
 
 			return newComponent;
 		}

+ 15 - 23
BansheeCore/Include/BsSceneObjectRTTI.h

@@ -33,6 +33,11 @@ namespace BansheeEngine
 		UINT32 getNumComponents(SceneObject* obj) { return (UINT32)obj->mComponents.size(); }
 		void setNumComponents(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
 
+		HPrefab& getPrefabLink(SceneObject* obj) { return obj->mPrefabLink; }
+		void setPrefabLink(SceneObject* obj, HPrefab& value) { obj->mPrefabLink = value; }
+
+		UINT32& getFlags(SceneObject* obj) { return obj->mFlags; }
+		void setFlags(SceneObject* obj, UINT32& value) { obj->mFlags = value; }
 	public:
 		SceneObjectRTTI()
 		{
@@ -40,9 +45,11 @@ namespace BansheeEngine
 				&SceneObjectRTTI::getNumChildren, &SceneObjectRTTI::setChild, &SceneObjectRTTI::setNumChildren);
 			addReflectablePtrArrayField("mComponents", 1, &SceneObjectRTTI::getComponent, 
 				&SceneObjectRTTI::getNumComponents, &SceneObjectRTTI::setComponent, &SceneObjectRTTI::setNumComponents);
+			addReflectableField("mPrefabLink", 2, &SceneObjectRTTI::getPrefabLink, &SceneObjectRTTI::setPrefabLink);
+			addPlainField("mFlags", 3, &SceneObjectRTTI::getFlags, &SceneObjectRTTI::setFlags);
 		}
 
-		virtual void onDeserializationStarted(IReflectable* obj)
+		virtual void onDeserializationStarted(IReflectable* obj) override
 		{
 			SceneObject* so = static_cast<SceneObject*>(obj);
 
@@ -55,7 +62,7 @@ namespace BansheeEngine
 				so->mRTTIData = DeserializationData(false);
 		}
 
-		virtual void onDeserializationEnded(IReflectable* obj)
+		virtual void onDeserializationEnded(IReflectable* obj) override
 		{
 			SceneObject* so = static_cast<SceneObject*>(obj);
 			DeserializationData deserializationData = any_cast<DeserializationData>(so->mRTTIData);
@@ -64,42 +71,27 @@ namespace BansheeEngine
 			{
 				GameObjectManager::instance().endDeserialization();
 
-				// Initialize all components
-				Stack<HSceneObject> todo;
-				todo.push(so->mThisHandle);
-
-				while (!todo.empty())
-				{
-					HSceneObject curSO = todo.top();
-					todo.pop();
-
-					const Vector<HComponent>& components = curSO->getComponents();
-
-					for (auto& component : components)
-						component->onInitialized();
-
-					for (UINT32 i = 0; i < curSO->getNumChildren(); i++)
-						todo.push(curSO->getChild(i));
-				}
+				if ((so->mFlags & SOF_DontInstantiate) == 0)
+					so->instantiate();
 			}
 
 			so->mRTTIData = nullptr;
 		}
 
-		virtual const String& getRTTIName()
+		virtual const String& getRTTIName() override
 		{
 			static String name = "SceneObject";
 			return name;
 		}
 
-		virtual UINT32 getRTTIId()
+		virtual UINT32 getRTTIId() override
 		{
 			return TID_SceneObject;
 		}
 
-		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		virtual std::shared_ptr<IReflectable> newRTTIObject() override
 		{
-			HSceneObject newObject = SceneObject::create("");
+			HSceneObject newObject = SceneObject::create("", SOF_DontInstantiate);
 
 			return newObject.getInternalPtr();
 		}

+ 1 - 1
BansheeCore/Source/BsCoreSceneManager.cpp

@@ -50,7 +50,7 @@ namespace BansheeEngine
 
 	void CoreSceneManager::registerNewSO(const HSceneObject& node) 
 	{ 
-		if(mRootNode) // If root node is null, then this new node is the root node
+		if(mRootNode)
 			node->setParent(mRootNode);
 	}
 

+ 1 - 1
BansheeCore/Source/BsGameObject.cpp

@@ -5,7 +5,7 @@
 namespace BansheeEngine
 {
 	GameObject::GameObject()
-		:mIsDestroyed(false)
+		:mIsDestroyed(false), mLinkId(-1)
 	{ }
 
 	GameObject::~GameObject()

+ 111 - 0
BansheeCore/Source/BsPrefab.cpp

@@ -0,0 +1,111 @@
+#include "BsPrefab.h"
+#include "BsPrefabRTTI.h"
+#include "BsResources.h"
+#include "BsSceneObject.h"
+
+namespace BansheeEngine
+{
+	Prefab::Prefab()
+		:Resource(false)
+	{
+		
+	}
+
+	HPrefab Prefab::create(const HSceneObject& sceneObject)
+	{
+		assert(sceneObject->mPrefabLink != nullptr);
+
+		PrefabPtr newPrefab = createEmpty();
+		newPrefab->initialize(sceneObject);
+
+		HPrefab handle = static_resource_cast<Prefab>(gResources()._createResourceHandle(newPrefab));
+		sceneObject->mPrefabLink = handle;
+
+		return handle;
+	}
+
+	PrefabPtr Prefab::createEmpty()
+	{
+		PrefabPtr newPrefab = bs_core_ptr<Prefab, PoolAlloc>(new (bs_alloc<Prefab, PoolAlloc>()) Prefab());
+		newPrefab->_setThisPtr(newPrefab);
+
+		return newPrefab;
+	}
+
+	void Prefab::generatePrefabIds(const HSceneObject& sceneObject)
+	{
+		Vector<HGameObject> objectsToId;
+		Set<INT32> existingIds;
+
+		Stack<HSceneObject> todo;
+		todo.push(sceneObject);
+
+		while (!todo.empty())
+		{
+			HSceneObject currentSO = todo.top();
+			todo.pop();
+
+			if (currentSO->mLinkId == -1)
+				objectsToId.push_back(currentSO);
+			else
+				existingIds.insert(currentSO->mLinkId);
+
+			for (auto& component : currentSO->mComponents)
+			{
+				if (component->mLinkId == -1)
+					objectsToId.push_back(component);
+				else
+					existingIds.insert(component->mLinkId);
+			}
+		}
+
+		auto setIter = existingIds.begin();
+		INT32 nextId = 0;
+		for (auto& object : objectsToId)
+		{
+			INT32 freeId = -1;
+			for (; setIter != existingIds.end(); ++setIter)
+			{
+				if (nextId < (*setIter))
+					freeId = nextId++;
+				else
+					nextId++;
+			}
+
+			if (freeId == -1)
+				freeId = nextId++;
+
+			object->mLinkId = freeId;
+		}		
+	}
+
+	void Prefab::initialize(const HSceneObject& sceneObject)
+	{
+		generatePrefabIds(sceneObject);
+
+		sceneObject->setFlags(SOF_DontInstantiate);
+		mRoot = sceneObject->clone();
+		sceneObject->unsetFlags(SOF_DontInstantiate);
+	}
+
+	HSceneObject Prefab::instantiate()
+	{
+		if (mRoot == nullptr)
+			return HSceneObject();
+
+		HSceneObject clone = mRoot->clone();
+		clone->instantiate();
+
+		return clone;
+	}
+
+	RTTITypeBase* Prefab::getRTTIStatic()
+	{
+		return PrefabRTTI::instance();
+	}
+
+	RTTITypeBase* Prefab::getRTTI() const
+	{
+		return Prefab::getRTTIStatic();
+	}
+}

+ 314 - 0
BansheeCore/Source/BsPrefabDiff.cpp

@@ -0,0 +1,314 @@
+#include "BsPrefabDiff.h"
+#include "BsPrefabDiffRTTI.h"
+#include "BsSceneObject.h"
+#include "BsMemorySerializer.h"
+#include "BsBinarySerializer.h"
+#include "BsBinaryDiff.h"
+
+namespace BansheeEngine
+{
+	RTTITypeBase* PrefabComponentDiff::getRTTIStatic()
+	{
+		return PrefabComponentDiffRTTI::instance();
+	}
+
+	RTTITypeBase* PrefabComponentDiff::getRTTI() const
+	{
+		return PrefabComponentDiff::getRTTIStatic();
+	}
+
+	RTTITypeBase* PrefabObjectDiff::getRTTIStatic()
+	{
+		return PrefabObjectDiffRTTI::instance();
+	}
+
+	RTTITypeBase* PrefabObjectDiff::getRTTI() const
+	{
+		return PrefabObjectDiff::getRTTIStatic();
+	}
+
+	SPtr<PrefabDiff> PrefabDiff::create(const HSceneObject& prefab, const HSceneObject& instance)
+	{
+		if (prefab->getPrefabLink() != instance->getPrefabLink() || prefab->getLinkId() != instance->getLinkId())
+			return nullptr;
+
+		SPtr<PrefabDiff> output = bs_shared_ptr<PrefabDiff>();
+		output->mRoot = generateDiff(prefab, instance);
+
+		return output;
+	}
+
+	void PrefabDiff::apply(const HSceneObject& object)
+	{
+		if (mRoot == nullptr)
+			return;
+
+		applyDiff(mRoot, object);
+	}
+
+	void PrefabDiff::applyDiff(const SPtr<PrefabObjectDiff>& diff, const HSceneObject& object)
+	{
+		if (diff->id != object->getLinkId())
+			return;
+
+		object->setName(diff->name);
+
+		const Vector<HComponent>& components = object->getComponents();
+		for (auto& removedId : diff->removedComponents)
+		{
+			for (auto& component : components)
+			{
+				if (removedId == component->getLinkId())
+				{
+					component->destroy();
+					break;
+				}
+			}
+		}
+
+		UINT32 childCount = object->getNumChildren();
+		for (auto& removedId : diff->removedChildren)
+		{
+			for (UINT32 i = 0; i < childCount; i++)
+			{
+				HSceneObject child = object->getChild(i);
+				if (removedId == child->getLinkId())
+				{
+					child->destroy();
+					break;
+				}
+			}
+		}
+
+		for (auto& addedComponentData : diff->addedComponents)
+		{
+			BinarySerializer bs;
+			SPtr<Component> component = std::static_pointer_cast<Component>(bs._decodeIntermediate(addedComponentData));
+			object->addComponentInternal(component);
+		}
+
+		for (auto& addedChildData : diff->addedChildren)
+		{
+			BinarySerializer bs;
+			SPtr<SceneObject> sceneObject = std::static_pointer_cast<SceneObject>(bs._decodeIntermediate(addedChildData));
+			sceneObject->setParent(object);
+		}
+
+		for (auto& componentDiff : diff->componentDiffs)
+		{
+			for (auto& component : components)
+			{
+				if (componentDiff->id == component->getLinkId())
+				{
+					IDiff& diffHandler = component->getRTTI()->getDiffHandler();
+					diffHandler.applyDiff(component.getInternalPtr(), componentDiff->data);
+					break;
+				}
+			}
+		}
+
+		for (auto& childDiff : diff->childDiffs)
+		{
+			for (UINT32 i = 0; i < childCount; i++)
+			{
+				HSceneObject child = object->getChild(i);
+				if (childDiff->id == child->getLinkId())
+				{
+					applyDiff(childDiff, child);
+					break;
+				}
+			}
+		}
+	}
+
+	SPtr<PrefabObjectDiff> PrefabDiff::generateDiff(const HSceneObject& prefab, const HSceneObject& instance)
+	{
+		SPtr<PrefabObjectDiff> output;
+
+		if (prefab->getName() != instance->getName())
+		{
+			if (output == nullptr)
+				output = bs_shared_ptr<PrefabObjectDiff>();
+		}
+
+		UINT32 prefabChildCount = prefab->getNumChildren();
+		UINT32 instanceChildCount = instance->getNumChildren();
+
+		// Find modified and removed children
+		for (UINT32 i = 0; i < prefabChildCount; i++)
+		{
+			HSceneObject prefabChild = prefab->getChild(i);
+
+			SPtr<PrefabObjectDiff> childDiff;
+			bool foundMatching = false;
+			for (UINT32 j = 0; j < instanceChildCount; j++)
+			{
+				HSceneObject instanceChild = instance->getChild(j);
+
+				if (prefabChild->getLinkId() == instanceChild->getLinkId())
+				{
+					childDiff = generateDiff(prefabChild, instanceChild);
+					foundMatching = true;
+					break;
+				}
+			}
+
+			if (foundMatching)
+			{
+				if (childDiff != nullptr)
+				{
+					if (output == nullptr)
+						output = bs_shared_ptr<PrefabObjectDiff>();
+
+					output->childDiffs.push_back(childDiff);
+				}
+			}
+			else
+			{
+				if (output == nullptr)
+					output = bs_shared_ptr<PrefabObjectDiff>();
+
+				output->removedChildren.push_back(prefabChild->getLinkId());
+			}	
+		}
+
+		// Find added children
+		for (UINT32 i = 0; i < instanceChildCount; i++)
+		{
+			HSceneObject instanceChild = instance->getChild(i);
+
+			bool foundMatching = false;
+			if (instanceChild->getLinkId() != -1)
+			{
+				for (UINT32 j = 0; j < prefabChildCount; j++)
+				{
+					HSceneObject prefabChild = prefab->getChild(j);
+
+					if (prefabChild->getLinkId() == instanceChild->getLinkId())
+					{
+						foundMatching = true;
+						break;
+					}
+				}
+			}
+
+			if (!foundMatching)
+			{
+				BinarySerializer bs;
+				SPtr<SerializedObject> obj = bs._encodeIntermediate(instanceChild.get());
+
+				if (output == nullptr)
+					output = bs_shared_ptr<PrefabObjectDiff>();
+
+				output->addedChildren.push_back(obj);
+			}
+		}
+
+		const Vector<HComponent>& prefabComponents = prefab->getComponents();
+		const Vector<HComponent>& instanceComponents = instance->getComponents();
+
+		UINT32 prefabComponentCount = (UINT32)prefabComponents.size();
+		UINT32 instanceComponentCount = (UINT32)instanceComponents.size();
+
+		// Find modified and removed components
+		for (UINT32 i = 0; i < prefabComponentCount; i++)
+		{
+			HComponent prefabComponent = prefabComponents[i];
+
+			SPtr<PrefabComponentDiff> childDiff;
+			bool foundMatching = false;
+			for (UINT32 j = 0; j < instanceComponentCount; j++)
+			{
+				HComponent instanceComponent = instanceComponents[j];
+
+				if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
+				{
+					BinarySerializer bs;
+					SPtr<SerializedObject> encodedPrefab = bs._encodeIntermediate(prefabComponent.get());
+					SPtr<SerializedObject> encodedInstance = bs._encodeIntermediate(instanceComponent.get());
+
+					IDiff& diffHandler = prefabComponent->getRTTI()->getDiffHandler();
+					SPtr<SerializedObject> diff = diffHandler.generateDiff(encodedPrefab, encodedInstance);
+
+					if (diff != nullptr)
+					{
+						childDiff = bs_shared_ptr<PrefabComponentDiff>();
+						childDiff->id = prefabComponent->getLinkId();
+						childDiff->data = diff;
+					}
+
+					foundMatching = true;
+					break;
+				}
+			}
+
+			if (foundMatching)
+			{
+				if (childDiff != nullptr)
+				{
+					if (output == nullptr)
+						output = bs_shared_ptr<PrefabObjectDiff>();
+
+					output->componentDiffs.push_back(childDiff);
+				}
+			}
+			else
+			{
+				if (output == nullptr)
+					output = bs_shared_ptr<PrefabObjectDiff>();
+
+				output->removedComponents.push_back(prefabComponent->getLinkId());
+			}
+		}
+
+		// Find added components
+		for (UINT32 i = 0; i < instanceComponentCount; i++)
+		{
+			HComponent instanceComponent = instanceComponents[i];
+
+			bool foundMatching = false;
+			if (instanceComponent->getLinkId() != -1)
+			{
+				for (UINT32 j = 0; j < prefabChildCount; j++)
+				{
+					HComponent prefabComponent = prefabComponents[j];
+
+					if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
+					{
+						foundMatching = true;
+						break;
+					}
+				}
+			}
+
+			if (!foundMatching)
+			{
+				BinarySerializer bs;
+				SPtr<SerializedObject> obj = bs._encodeIntermediate(instanceComponent.get());
+
+				if (output == nullptr)
+					output = bs_shared_ptr<PrefabObjectDiff>();
+
+				output->addedChildren.push_back(obj);
+			}
+		}
+
+		if (output != nullptr)
+		{
+			output->name = instance->getName();
+			output->id = instance->getLinkId();
+		}
+
+		return output;
+	}
+
+	RTTITypeBase* PrefabDiff::getRTTIStatic()
+	{
+		return PrefabDiffRTTI::instance();
+	}
+
+	RTTITypeBase* PrefabDiff::getRTTI() const
+	{
+		return PrefabDiff::getRTTIStatic();
+	}
+}

+ 64 - 9
BansheeCore/Source/BsSceneObject.cpp

@@ -9,11 +9,11 @@
 
 namespace BansheeEngine
 {
-	SceneObject::SceneObject(const String& name)
+	SceneObject::SceneObject(const String& name, UINT32 flags)
 		:GameObject(), mPosition(Vector3::ZERO), mRotation(Quaternion::IDENTITY), mScale(Vector3::ONE),
 		mWorldPosition(Vector3::ZERO), mWorldRotation(Quaternion::IDENTITY), mWorldScale(Vector3::ONE),
 		mCachedLocalTfrm(Matrix4::IDENTITY), mDirtyFlags(0xFFFFFFFF), mCachedWorldTfrm(Matrix4::IDENTITY), 
-		mActiveSelf(true), mActiveHierarchy(true), mDirtyHash(0)
+		mActiveSelf(true), mActiveHierarchy(true), mDirtyHash(0), mFlags(flags)
 	{
 		setName(name);
 	}
@@ -27,18 +27,19 @@ namespace BansheeEngine
 		}
 	}
 
-	HSceneObject SceneObject::create(const String& name)
+	HSceneObject SceneObject::create(const String& name, UINT32 flags)
 	{
-		HSceneObject newObject = createInternal(name);
+		HSceneObject newObject = createInternal(name, flags);
 
-		gCoreSceneManager().registerNewSO(newObject);
+		if (newObject->isInstantiated())
+			gCoreSceneManager().registerNewSO(newObject);
 
 		return newObject;
 	}
 
-	HSceneObject SceneObject::createInternal(const String& name)
+	HSceneObject SceneObject::createInternal(const String& name, UINT32 flags)
 	{
-		std::shared_ptr<SceneObject> sceneObjectPtr = std::shared_ptr<SceneObject>(new (bs_alloc<SceneObject, PoolAlloc>()) SceneObject(name), 
+		std::shared_ptr<SceneObject> sceneObjectPtr = std::shared_ptr<SceneObject>(new (bs_alloc<SceneObject, PoolAlloc>()) SceneObject(name, flags), 
 			&bs_delete<PoolAlloc, SceneObject>, StdAlloc<SceneObject, PoolAlloc>());
 		
 		HSceneObject sceneObject = GameObjectManager::instance().registerObject(sceneObjectPtr);
@@ -70,7 +71,9 @@ namespace BansheeEngine
 		for(auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
 		{
 			(*iter)->_setIsDestroyed();
-			(*iter)->onDestroyed();
+
+			if (isInstantiated())
+				(*iter)->onDestroyed();
 
 			if (immediate)
 			{
@@ -100,6 +103,54 @@ namespace BansheeEngine
 		mThisHandle._setHandleData(mThisHandle.getInternalPtr());
 	}
 
+	HPrefab SceneObject::getPrefabLink() const
+	{
+		const SceneObject* curObj = this;
+
+		while (curObj == nullptr)
+		{
+			if (curObj->mPrefabLink != nullptr)
+				return curObj->mPrefabLink;
+
+			if (curObj->mParent != nullptr)
+				curObj = curObj->mParent.get();
+			else
+				curObj = nullptr;
+		}
+
+		return HPrefab();
+	}
+
+	void SceneObject::setFlags(UINT32 flags)
+	{
+		mFlags |= flags;
+
+		for (auto& child : mChildren)
+			child->setFlags(flags);
+	}
+
+	void SceneObject::unsetFlags(UINT32 flags)
+	{
+		mFlags &= ~flags;
+
+		for (auto& child : mChildren)
+			child->unsetFlags(flags);
+	}
+
+	void SceneObject::instantiate()
+	{
+		mFlags &= ~SOF_DontInstantiate;
+
+		if (mParent == nullptr)
+			gCoreSceneManager().registerNewSO(mThisHandle);
+
+		for (auto& component : mComponents)
+			component->onInitialized();
+
+		for (auto& child : mChildren)
+			child->instantiate();
+	}
+
 	/************************************************************************/
 	/* 								Transform	                     		*/
 	/************************************************************************/
@@ -417,6 +468,8 @@ namespace BansheeEngine
 	void SceneObject::addChild(const HSceneObject& object)
 	{
 		mChildren.push_back(object); 
+
+		object->setFlags(mFlags);
 	}
 
 	void SceneObject::removeChild(const HSceneObject& object)
@@ -494,7 +547,9 @@ namespace BansheeEngine
 		if(iter != mComponents.end())
 		{
 			(*iter)->_setIsDestroyed();
-			(*iter)->onDestroyed();
+
+			if (isInstantiated())
+				(*iter)->onDestroyed();
 			
 			mComponents.erase(iter);
 

+ 2 - 4
BansheeEditor/Source/BsEditorTestSuite.cpp

@@ -353,11 +353,9 @@ namespace BansheeEngine
 		UINT32 newDataLength = 0;
 		UINT8* newData = ms.encode(newObj.get(), newDataLength, &bs_alloc);
 
-		UINT32 dummy = 0;
 		BinarySerializer bs;
-		SPtr<SerializedObject> orgSerialized = bs._decodeIntermediate(orgData, orgDataLength, dummy);
-		dummy = 0;
-		SPtr<SerializedObject> newSerialized = bs._decodeIntermediate(newData, newDataLength, dummy);
+		SPtr<SerializedObject> orgSerialized = bs._decodeIntermediate(orgData, orgDataLength);
+		SPtr<SerializedObject> newSerialized = bs._decodeIntermediate(newData, newDataLength);
 
 		IDiff& diffHandler = orgObj->getRTTI()->getDiffHandler();
 		SPtr<SerializedObject> objDiff = diffHandler.generateDiff(orgSerialized, newSerialized);

+ 18 - 2
BansheeUtility/Include/BsBinarySerializer.h

@@ -69,14 +69,30 @@ namespace BansheeEngine
 		 */
 		SPtr<IReflectable> decode(UINT8* data, UINT32 dataLength);
 
+		/**
+		 * @brief	Encodes an object into an intermediate representation.
+		 *
+		 * @param	object		Object to encode.
+		 * @param	shallow		Determines how to handle referenced objects. If true then references will not be encoded
+		 *						and will be set to null. If false then references will be encoded as well and restored
+		 *						upon decoding.
+		 */
+		SPtr<SerializedObject> _encodeIntermediate(IReflectable* object, bool shallow = false);
+
 		/**
 		 * @brief	Decodes an object in memory into an intermediate representation for easier parsing.
 		 *			
+		 * @param 	data  		Binary data to decode.
+		 * @param	dataLength	Length of the data in bytes.
+		 * @param	copyData	Determines should the data be copied or just referenced. If referenced
+		 *						then the returned serialized object will be invalid as soon as the original
+		 *						data buffer is destroyed. Referencing is faster than copying.
+		 *
 		 * @note	Internal method.
 		 *			References to field data will point to the original buffer and will become invalid 
 		 *			when it is destroyed.
 		 */
-		SPtr<SerializedObject> _decodeIntermediate(UINT8* data, UINT32 dataLength, UINT32& bytesRead);
+		SPtr<SerializedObject> _decodeIntermediate(UINT8* data, UINT32 dataLength, bool copyData = false);
 
 		/**
 		 * @brief	Decodes an intermediate representation of a serialized object into the actual object.
@@ -133,7 +149,7 @@ namespace BansheeEngine
 		/**
 		 * @brief	Decodes an object in memory into an intermediate representation for easier parsing.
 		 */
-		bool decodeIntermediateInternal(UINT8* data, UINT32 dataLength, UINT32& bytesRead, SPtr<SerializedObject>& output);
+		bool decodeIntermediateInternal(UINT8* data, UINT32 dataLength, UINT32& bytesRead, SPtr<SerializedObject>& output, bool copyData);
 
 		/**
 		 * @brief	Helper method for encoding a complex object and copying its data to a buffer.

+ 57 - 11
BansheeUtility/Source/BsBinarySerializer.cpp

@@ -9,6 +9,7 @@
 #include "BsRTTIReflectableField.h"
 #include "BsRTTIReflectablePtrField.h"
 #include "BsRTTIManagedDataBlockField.h"
+#include "BsMemorySerializer.h"
 
 #include <unordered_set>
 
@@ -120,8 +121,7 @@ namespace BansheeEngine
 
 	std::shared_ptr<IReflectable> BinarySerializer::decode(UINT8* data, UINT32 dataLength)
 	{
-		UINT32 dummy = 0;
-		SPtr<SerializedObject> intermediateObject = _decodeIntermediate(data, dataLength, dummy);
+		SPtr<SerializedObject> intermediateObject = _decodeIntermediate(data, dataLength);
 		if (intermediateObject == nullptr)
 			return nullptr;
 
@@ -376,24 +376,43 @@ namespace BansheeEngine
 		return buffer;
 	}
 
-	SPtr<SerializedObject> BinarySerializer::_decodeIntermediate(UINT8* data, UINT32 dataLength, UINT32& bytesRead)
+	SPtr<SerializedObject> BinarySerializer::_encodeIntermediate(IReflectable* object, bool shallow)
 	{
+		// TODO: This is a hacky way of generating an intermediate format to save development time and complexity.
+		// It is hacky because it requires a full on encode to binary and then decode into intermediate. It should 
+		// be better to modify encoding process so it outputs the intermediate format directly (similar to how decoding works). 
+		// This also means that once you have an intermediate format you cannot use it to encode to binary. 
+
+		MemorySerializer ms;
+		UINT32 dataLength = 0;
+		UINT8* data = ms.encode(object, dataLength, &bs_alloc, shallow);
+
+		BinarySerializer bs;
+		SPtr<SerializedObject> obj = bs._decodeIntermediate(data, dataLength, true);
+
+		bs_free(data);
+		return obj;
+	}
+
+	SPtr<SerializedObject> BinarySerializer::_decodeIntermediate(UINT8* data, UINT32 dataLength, bool copyData)
+	{
+		UINT32 bytesRead = 0;
 		mInterimObjectMap.clear();
 
 		SPtr<SerializedObject> rootObj;
-		bool hasMore = decodeIntermediateInternal(data, dataLength, bytesRead, rootObj);
+		bool hasMore = decodeIntermediateInternal(data, dataLength, bytesRead, rootObj, copyData);
 		while (hasMore)
 		{
 			UINT8* dataPtr = data + bytesRead;
 
 			SPtr<SerializedObject> dummyObj;
-			hasMore = decodeIntermediateInternal(dataPtr, dataLength, bytesRead, dummyObj);
+			hasMore = decodeIntermediateInternal(dataPtr, dataLength, bytesRead, dummyObj, copyData);
 		}
 
 		return rootObj;
 	}
 
-	bool BinarySerializer::decodeIntermediateInternal(UINT8* data, UINT32 dataLength, UINT32& bytesRead, SPtr<SerializedObject>& output)
+	bool BinarySerializer::decodeIntermediateInternal(UINT8* data, UINT32 dataLength, UINT32& bytesRead, SPtr<SerializedObject>& output, bool copyData)
 	{
 		if ((bytesRead + sizeof(ObjectMetaData)) > dataLength)
 		{
@@ -638,7 +657,7 @@ namespace BansheeEngine
 						{
 							UINT32 dummy = 0;
 							SPtr<SerializedObject> serializedArrayEntry;
-							decodeIntermediateInternal(data, complexTypeSize, dummy, serializedArrayEntry);
+							decodeIntermediateInternal(data, complexTypeSize, dummy, serializedArrayEntry, copyData);
 
 							SerializedArrayEntry arrayEntry;
 							arrayEntry.serialized = serializedArrayEntry;
@@ -665,7 +684,16 @@ namespace BansheeEngine
 						if (curField != nullptr)
 						{
 							SPtr<SerializedField> serializedField = bs_shared_ptr<SerializedField>();
-							serializedField->value = data;
+
+							if (copyData)
+							{
+								serializedField->value = (UINT8*)bs_alloc(typeSize);
+								memcpy(serializedField->value, data, typeSize);
+								serializedField->ownsMemory = true;
+							}
+							else
+								serializedField->value = data;
+
 							serializedField->size = typeSize;
 
 							SerializedArrayEntry arrayEntry;
@@ -747,7 +775,7 @@ namespace BansheeEngine
 						UINT32 dummy = 0;
 
 						SPtr<SerializedObject> serializedChildObj;
-						decodeIntermediateInternal(data, complexTypeSize, dummy, serializedChildObj);
+						decodeIntermediateInternal(data, complexTypeSize, dummy, serializedChildObj, copyData);
 
 						serializedEntry = serializedChildObj;
 						hasModification = true;
@@ -769,7 +797,16 @@ namespace BansheeEngine
 					if (curField != nullptr)
 					{
 						SPtr<SerializedField> serializedField = bs_shared_ptr<SerializedField>();
-						serializedField->value = data;
+
+						if (copyData)
+						{
+							serializedField->value = (UINT8*)bs_alloc(typeSize);
+							memcpy(serializedField->value, data, typeSize);
+							serializedField->ownsMemory = true;
+						}
+						else
+							serializedField->value = data;
+
 						serializedField->size = typeSize;
 
 						serializedEntry = serializedField;
@@ -806,7 +843,16 @@ namespace BansheeEngine
 					if (curField != nullptr)
 					{
 						SPtr<SerializedField> serializedField = bs_shared_ptr<SerializedField>();
-						serializedField->value = data;
+
+						if (copyData)
+						{
+							serializedField->value = (UINT8*)bs_alloc(dataBlockSize);
+							memcpy(serializedField->value, data, dataBlockSize);
+							serializedField->ownsMemory = true;
+						}
+						else
+							serializedField->value = data;
+
 						serializedField->size = dataBlockSize;
 
 						serializedEntry = serializedField;

+ 28 - 6
TODO.txt

@@ -27,19 +27,41 @@ return them in checkForModifications?
 ---------------------------------------------------------------------
 Prefab diff
 
-IMMEDIATE:
- - Integrated native diff and managed diff
- - Test and debug native diff
+TODO:
+ - Document new Prefab code (Prefab & PrefabDiff)
+ - Test prefab diff creation and applying (unit test)
 
-Later: Native+Managed diff is not tested (only the link needs to be tested, I have a separate managed and native tests)
+Prefab unit test:
+ - Create a set of scene objects
+ - Create a prefab from them
+ - Modify original set objects
+ - Create prefab diff
+ - Save prefab to disk
+ - Load prefab and compare with instance
+ - Compare and assert results
+
+Get prefab saving/loading working and only add diffs later
+
+Need to figure out how to restore IDs after prefab revert/update
 
-Make RTTIType return an interface for creating and applying diffs, rather than dealing with overriding individual methods.
+High level goal list:
+ - Add prefab ID system
+   - Update IDs when linking/breaking a prefab
+ - Fix native and binary diff so that GameObjectHandle can be compared properly
+ - Hook up updates:
+    - Save a diff whenever a HSceneObject with a HPrefab link is saved
+    - Update active scene when prefab is modified (destroy them, create original prefab then apply saved diff)
+
+When dealing with prefab saving make sure to consider scene object flags like "dont save"
+
+Later: Native+Managed diff is not tested (only the link needs to be tested, I have a separate managed and native tests)
+Later: Modify BinarySerializer so that _encodeIntermediate outputs intermediate format directly without a full on encode
 
 See "Prefabs" gdoc for later goals
 See "Brainstorm" gdoc for ideas about how to solve the ID issue (and see below)
 
 When deserializing diffs I need to turn on GameObjectManager
- - Since diffs should be part of HSceneObject it should already be active but it is something to pay attention for
+ - Since diffs should be part of HSceneObject it should already be active but it is something to pay attention to
 
 GameObjectHandle compare in managed serializable diff won't work for prefabs (will have the same problem for native diff)
  - Handle comparison compares instanceIDs which will be different between prefab and its instance