Browse Source

Update from prefab, and recording prefab diff now properly handles child prefab instances
Update from prefab now checks if prefab instance is dirty before updating to avoid unnecessary updates on every load
Added SceneObject flags that allow objects to persist after scene clear
Updated scene tree view not to show hidden scene objects

Marko Pintera 10 years ago
parent
commit
2fc87ffbf4

+ 7 - 0
BansheeCore/Include/BsPrefab.h

@@ -41,6 +41,12 @@ namespace BansheeEngine
 		 */
 		HSceneObject getRoot() const { return mRoot; }
 
+		/**
+		 * @brief	Returns a hash value that can be used for determining if a prefab changed
+		 *			by comparing it to a previously saved hash.
+		 */
+		UINT32 getHash() const { return mHash; }
+
 	private:
 		/**
 		 * @brief	Initializes the internal prefab hierarchy. Must be called druing creation.
@@ -53,6 +59,7 @@ namespace BansheeEngine
 		static PrefabPtr createEmpty();
 
 		HSceneObject mRoot;
+		UINT32 mHash;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/

+ 4 - 15
BansheeCore/Include/BsPrefabRTTI.h

@@ -13,25 +13,14 @@ namespace BansheeEngine
 		SceneObjectPtr getSceneObject(Prefab* obj) { return obj->mRoot.getInternalPtr(); }
 		void setSceneObject(Prefab* obj, SceneObjectPtr value) { obj->mRoot = value->getHandle(); }
 
+		UINT32& getHash(Prefab* obj) { return obj->mHash; }
+		void setHash(Prefab* obj, UINT32& val) { obj->mHash = val; }
+
 	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;
+			addPlainField("mHash", 1, &PrefabRTTI::getHash, &PrefabRTTI::setHash);
 		}
 
 		virtual const String& getRTTIName() override

+ 9 - 0
BansheeCore/Include/BsPrefabUtility.h

@@ -72,6 +72,9 @@ namespace BansheeEngine
 		/**
 		 * @brief	Updates the internal prefab diff data by recording the difference
 		 *			between the current values in the provided prefab instance and its prefab.
+		 *
+		 * @note	If the provided object contains any child prefab instances, this will be
+		 *			done recursively for them as well.
 		 */
 		static void recordPrefabDiff(const HSceneObject& sceneObject);
 
@@ -86,6 +89,8 @@ namespace BansheeEngine
 		 * @param[out]	output				Contains the output hierarchy of instance data.
 		 * @param[out]	linkedInstanceData	A map of link IDs to instance data. Objects without
 		 *									link IDs will not be included here.
+		 *
+		 * @note	Does not recurse into child prefab instances.
 		 */
 		static void recordInstanceData(const HSceneObject& so, SceneObjectProxy& output, 
 			UnorderedMap<UINT32, GameObjectInstanceDataPtr>& linkedInstanceData);
@@ -96,6 +101,8 @@ namespace BansheeEngine
 		 *
 		 * @param[in]	so					Object to traverse and restore the instance data.
 		 * @param[in]	linkedInstanceData	A map of link IDs to instance data, returned by "recordInstanceData" method.
+		 *
+		 * @note	Does not recurse into child prefab instances.
 		 */
 		static void restoreLinkedInstanceData(const HSceneObject& so, UnorderedMap<UINT32, GameObjectInstanceDataPtr>& linkedInstanceData);
 
@@ -107,6 +114,8 @@ namespace BansheeEngine
 		 * @param[in]	so		Object to traverse and restore the instance data.
 		 * @param[in]	proxy	Hierarchy containing instance data for all objects and components, returned by
 		 *						"recordInstanceData" method.
+		 *
+		 * @note	Does not recurse into child prefab instances.
 		 */
 		static void restoreUnlinkedInstanceData(const HSceneObject& so, SceneObjectProxy& proxy);
 	};

+ 7 - 1
BansheeCore/Include/BsSceneObject.h

@@ -18,7 +18,12 @@ namespace BansheeEngine
 	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. */
+		SOF_DontSave = 0x02,		/**< Object will be skipped when saving the scene hierarchy or a prefab. */
+		SOF_Persistent = 0x04,		/**< Object will remain in the scene even after scene clear, unless destroyed directly. 
+										 This only works with top-level objects. */
+		SOF_Internal = 0x08			/**< Provides a hint to external systems that his object is used by engine internals.
+									     For example, those systems might not want to display those objects together with the
+										 user created ones. */
 	};
 
 	/**
@@ -133,6 +138,7 @@ namespace BansheeEngine
 		HSceneObject mThisHandle;
 		String mPrefabLinkUUID;
 		PrefabDiffPtr mPrefabDiff;
+		UINT32 mPrefabHash;
 		UINT32 mFlags;
 
 		/************************************************************************/

+ 4 - 0
BansheeCore/Include/BsSceneObjectRTTI.h

@@ -41,6 +41,9 @@ namespace BansheeEngine
 
 		UINT32& getFlags(SceneObject* obj) { return obj->mFlags; }
 		void setFlags(SceneObject* obj, UINT32& value) { obj->mFlags = value; }
+
+		UINT32& getPrefabHash(SceneObject* obj) { return obj->mPrefabHash; }
+		void setPrefabHash(SceneObject* obj, UINT32& value) { obj->mPrefabHash = value; }
 	public:
 		SceneObjectRTTI()
 		{
@@ -51,6 +54,7 @@ namespace BansheeEngine
 			addPlainField("mPrefabLink", 2, &SceneObjectRTTI::getPrefabLink, &SceneObjectRTTI::setPrefabLink);
 			addPlainField("mFlags", 3, &SceneObjectRTTI::getFlags, &SceneObjectRTTI::setFlags);
 			addReflectablePtrField("mPrefabDiff", 4, &SceneObjectRTTI::getPrefabDiff, &SceneObjectRTTI::setPrefabDiff);
+			addPlainField("mPrefabHash", 5, &SceneObjectRTTI::getPrefabHash, &SceneObjectRTTI::setPrefabHash);
 		}
 
 		virtual void onDeserializationStarted(IReflectable* obj) override

+ 8 - 2
BansheeCore/Source/BsCoreSceneManager.cpp

@@ -22,10 +22,16 @@ namespace BansheeEngine
 	{
 		UINT32 numChildren = mRootNode->getNumChildren();
 
+		UINT32 curIdx = 0;
 		for (UINT32 i = 0; i < numChildren; i++)
 		{
-			HSceneObject child = mRootNode->getChild(0);
-			child->destroy();
+			HSceneObject child = mRootNode->getChild(curIdx);
+
+			if (!child->hasFlag(SOF_Persistent))
+			{
+				child->destroy();
+				curIdx++;
+			}
 		}
 
 		GameObjectManager::instance().destroyQueuedObjects();

+ 26 - 2
BansheeCore/Source/BsPrefab.cpp

@@ -7,7 +7,7 @@
 namespace BansheeEngine
 {
 	Prefab::Prefab()
-		:Resource(false)
+		:Resource(false), mHash(0)
 	{
 		
 	}
@@ -65,6 +65,7 @@ namespace BansheeEngine
 	void Prefab::update(const HSceneObject& sceneObject)
 	{
 		initialize(sceneObject);
+		mHash++;
 	}
 
 	HSceneObject Prefab::instantiate()
@@ -74,9 +75,32 @@ namespace BansheeEngine
 
 		HSceneObject clone = mRoot->clone();
 		clone->instantiate();
+		clone->mPrefabHash = mHash;
 
 #if BS_EDITOR_BUILD
-		PrefabUtility::updateFromPrefab(clone);
+		// Update any child prefab instances in case their prefabs changed
+		Stack<HSceneObject> todo;
+		todo.push(clone);
+
+		Vector<HSceneObject> prefabInstanceRoots;
+
+		while (!todo.empty())
+		{
+			HSceneObject current = todo.top();
+			todo.pop();
+
+			UINT32 childCount = current->getNumChildren();
+			for (UINT32 i = 0; i < childCount; i++)
+			{
+				HSceneObject child = current->getChild(i);
+
+				String prefabLinkUUID = child->getPrefabLink();
+				if (!prefabLinkUUID.empty())
+					PrefabUtility::updateFromPrefab(child);
+				else
+					todo.push(child);
+			}
+		}
 #endif
 
 		return clone;

+ 103 - 63
BansheeCore/Source/BsPrefabUtility.cpp

@@ -27,26 +27,67 @@ namespace BansheeEngine
 
 	void PrefabUtility::updateFromPrefab(const HSceneObject& so)
 	{
-		String prefabLinkUUID = so->getPrefabLink();
-		HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false));
+		HSceneObject topLevelObject = so;
 
-		if (prefabLink == nullptr)
-			return;
+		while (topLevelObject != nullptr)
+		{
+			if (!topLevelObject->mPrefabLinkUUID.empty())
+				break;
 
-		// Save IDs, destroy original, create new, apply diff, restore IDs
-		SceneObjectProxy soProxy;
-		UnorderedMap<UINT32, GameObjectInstanceDataPtr> linkedInstanceData;
-		recordInstanceData(so, soProxy, linkedInstanceData);
+			if (topLevelObject->mParent != nullptr)
+				topLevelObject = topLevelObject->mParent;
+			else
+				topLevelObject = nullptr;
+		}
 
-		PrefabDiffPtr prefabDiff = so->mPrefabDiff;
-		so->destroy();
+		Stack<HSceneObject> todo;
+		todo.push(topLevelObject);
 
-		HSceneObject newInstance = prefabLink->instantiate();
-		if (prefabDiff != nullptr)
-			prefabDiff->apply(newInstance);
+		Vector<HSceneObject> prefabInstanceRoots;
 
-		restoreLinkedInstanceData(newInstance, linkedInstanceData);
-		restoreUnlinkedInstanceData(newInstance, soProxy);
+		while (!todo.empty())
+		{
+			HSceneObject current = todo.top();
+			todo.pop();
+
+			String prefabLinkUUID = current->getPrefabLink();
+			if (!prefabLinkUUID.empty())
+				prefabInstanceRoots.push_back(current);
+
+			UINT32 childCount = current->getNumChildren();
+			for (UINT32 i = 0; i < childCount; i++)
+			{
+				HSceneObject child = current->getChild(i);
+				todo.push(child);
+			}
+		}
+
+		// Need to do this bottom up to ensure I don't destroy the parents before children
+		for (auto iter = prefabInstanceRoots.rbegin(); iter != prefabInstanceRoots.rend(); ++iter)
+		{
+			HSceneObject current = *iter;
+			HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->getPrefabLink(), false, false));
+
+			if (prefabLink != nullptr && prefabLink->getHash() != current->mPrefabHash)
+			{
+				// Save IDs, destroy original, create new, apply diff, restore IDs
+				SceneObjectProxy soProxy;
+				UnorderedMap<UINT32, GameObjectInstanceDataPtr> linkedInstanceData;
+				recordInstanceData(current, soProxy, linkedInstanceData);
+
+				PrefabDiffPtr prefabDiff = current->mPrefabDiff;
+				current->destroy();
+
+				HSceneObject newInstance = prefabLink->instantiate();
+				if (prefabDiff != nullptr)
+					prefabDiff->apply(newInstance);
+
+				restoreLinkedInstanceData(newInstance, linkedInstanceData);
+				restoreUnlinkedInstanceData(newInstance, soProxy);
+			}
+		}
+
+		gResources().unloadAllUnused();
 	}
 
 	void PrefabUtility::generatePrefabIds(const HSceneObject& sceneObject)
@@ -135,86 +176,82 @@ namespace BansheeEngine
 
 	void PrefabUtility::recordPrefabDiff(const HSceneObject& sceneObject)
 	{
-		HSceneObject curObj = sceneObject;
+		HSceneObject topLevelObject = sceneObject;
 
-		while (curObj == nullptr)
+		while (topLevelObject != nullptr)
 		{
-			if (!curObj->mPrefabLinkUUID.empty())
+			if (!topLevelObject->mPrefabLinkUUID.empty())
+				break;
+
+			if (topLevelObject->mParent != nullptr)
+				topLevelObject = topLevelObject->mParent;
+			else
+				topLevelObject = nullptr;
+		}
+
+		Stack<HSceneObject> todo;
+		todo.push(topLevelObject);
+
+		while (!todo.empty())
+		{
+			HSceneObject current = todo.top();
+			todo.pop();
+
+			if (!current->getPrefabLink().empty())
 			{
-				curObj->mPrefabDiff = nullptr;
+				current->mPrefabDiff = nullptr;
 
-				HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(curObj->mPrefabLinkUUID, false, false));
+				HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->getPrefabLink(), false, false));
 				if (prefabLink != nullptr)
-					curObj->mPrefabDiff = PrefabDiff::create(prefabLink->getRoot(), curObj->getHandle());
-
-				return;
+					current->mPrefabDiff = PrefabDiff::create(prefabLink->getRoot(), current->getHandle());
 			}
 
-			if (curObj->mParent != nullptr)
-				curObj = curObj->mParent;
-			else
-				curObj = nullptr;
+			UINT32 childCount = current->getNumChildren();
+			for (UINT32 i = 0; i < childCount; i++)
+			{
+				HSceneObject child = current->getChild(i);
+				todo.push(child);
+			}
 		}
+
+		gResources().unloadAllUnused();
 	}
 
 	void PrefabUtility::recordInstanceData(const HSceneObject& so, SceneObjectProxy& output,
 		UnorderedMap<UINT32, GameObjectInstanceDataPtr>& linkedInstanceData)
 	{
-		struct StackEntry
-		{
-			HSceneObject so;
-			bool isPartOfPrefab;
-		};
-
-		Stack<StackEntry> todo;
-		todo.push(StackEntry());
-
-		StackEntry& topEntry = todo.top();
-		topEntry.so = so;
-		topEntry.isPartOfPrefab = true;
+		Stack<HSceneObject> todo;
+		todo.push(so);
 
 		while (!todo.empty())
 		{
-			StackEntry current = todo.top();
+			HSceneObject current = todo.top();
 			todo.pop();
 
-			output.instanceData = current.so->_getInstanceData();
+			output.instanceData = current->_getInstanceData();
+			output.linkId = current->getLinkId();
 
-			if (current.isPartOfPrefab)
-			{
-				output.linkId = current.so->getLinkId();
-				linkedInstanceData[output.linkId] = output.instanceData;
-			}
-			else
-				output.linkId = -1;
+			linkedInstanceData[output.linkId] = output.instanceData;
 
-			const Vector<HComponent>& components = current.so->getComponents();
+			const Vector<HComponent>& components = current->getComponents();
 			for (auto& component : components)
 			{
 				output.components.push_back(ComponentProxy());
 
 				ComponentProxy& componentProxy = output.components.back();
 				componentProxy.instanceData = component->_getInstanceData();
+				componentProxy.linkId = component->getLinkId();
 
-				if (current.isPartOfPrefab)
-				{
-					componentProxy.linkId = component->getLinkId();
-					linkedInstanceData[componentProxy.linkId] = componentProxy.instanceData;
-				}
-				else
-					componentProxy.linkId = -1;
+				linkedInstanceData[componentProxy.linkId] = componentProxy.instanceData;
 			}
 
-			UINT32 numChildren = current.so->getNumChildren();
+			UINT32 numChildren = current->getNumChildren();
 			for (UINT32 i = 0; i < numChildren; i++)
 			{
-				HSceneObject child = current.so->getChild(i);
-
-				todo.push(StackEntry());
-				StackEntry& newEntry = todo.top();
+				HSceneObject child = current->getChild(i);
 
-				newEntry.so = child;
-				newEntry.isPartOfPrefab = current.isPartOfPrefab && child->mPrefabLinkUUID.empty();
+				if (child->mPrefabLinkUUID.empty())
+					todo.push(child);
 			}
 		}
 	}
@@ -313,6 +350,9 @@ namespace BansheeEngine
 			{
 				HSceneObject child = current.so->getChild(i);
 
+				if (!child->mPrefabLinkUUID.empty())
+					continue;
+
 				if (child->getLinkId() == -1)
 				{
 					bool foundInstanceData = false;

+ 1 - 1
BansheeCore/Source/BsSceneObject.cpp

@@ -16,7 +16,7 @@ namespace BansheeEngine
 		: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), mFlags(flags)
+		mActiveSelf(true), mActiveHierarchy(true), mDirtyHash(0), mFlags(flags), mPrefabHash(0)
 	{
 		setName(name);
 	}

+ 1 - 1
BansheeEditor/Source/BsDropDownWindow.cpp

@@ -16,7 +16,7 @@ namespace BansheeEngine
 		:mRootPanel(nullptr), mPosition(position), mWidth(width), mHeight(height), 
 		mRenderWindow(parent), mFrontHitBox(nullptr), mBackHitBox(nullptr)
 	{
-		mSceneObject = SceneObject::create("EditorWindow");
+		mSceneObject = SceneObject::create("EditorWindow", SOF_Internal | SOF_Persistent | SOF_DontSave);
 
 		mGUI = mSceneObject->addComponent<GUIWidget>(target);
 

+ 4 - 0
BansheeEditor/Source/BsEditorTestSuite.cpp

@@ -464,6 +464,8 @@ namespace BansheeEngine
 		BS_TEST_ASSERT(!cmpExternal->ref1.isDestroyed());
 		BS_TEST_ASSERT(!cmpExternal->ref2.isDestroyed());
 		BS_TEST_ASSERT(cmpB1_1->val1 == "InitialValue");
+
+		so0_0->destroy();
 	}
 
 	void EditorTestSuite::BinaryDiff()
@@ -652,5 +654,7 @@ namespace BansheeEngine
 		HSceneObject nso3 = newRoot->getChild(3);
 		GameObjectHandle<TestComponentD> ncmp3 = nso3->getComponent<TestComponentD>();
 		BS_TEST_ASSERT(ncmp3 != nullptr);
+
+		root->destroy();
 	}
 }

+ 1 - 1
BansheeEditor/Source/BsEditorWindowBase.cpp

@@ -63,7 +63,7 @@ namespace BansheeEngine
 	void EditorWindowBase::construct(const RenderWindowPtr& renderWindow)
 	{
 		mRenderWindow = renderWindow;
-		mSceneObject = SceneObject::create("EditorWindow");
+		mSceneObject = SceneObject::create("EditorWindow", SOF_Internal | SOF_Persistent | SOF_DontSave);
 
 		mCamera = mSceneObject->addComponent<Camera>(renderWindow, 0.0f, 0.0f, 1.0f, 1.0f);
 		mCamera->setNearClipDistance(5);

+ 31 - 11
BansheeEditor/Source/BsGUISceneTreeView.cpp

@@ -59,25 +59,38 @@ namespace BansheeEngine
 
 		// Check if SceneObject has changed in any way and update the tree element
 
-		bool completeMatch = (UINT32)element->mChildren.size() == currentSO->getNumChildren();
-
 		// Early exit case - Most commonly there will be no changes between active and cached data so 
 		// we first do a quick check in order to avoid expensive comparison later
-		if(completeMatch)
+		bool completeMatch = true;
+		UINT32 visibleChildCount = 0;
+		for (UINT32 i = 0; i < currentSO->getNumChildren(); i++)
 		{
-			for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
+			if (i >= element->mChildren.size())
 			{
-				SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[i]);
+				completeMatch = false;
+				break;
+			}
 
-				UINT64 curId = currentSO->getChild(i)->getInstanceId();
-				if(curId != currentChild->mId)
-				{
-					completeMatch = false;
-					break;
-				}
+			HSceneObject currentSOChild = currentSO->getChild(i);
+
+#if BS_DEBUG_MODE == 0
+			if (currentSOChild->hasFlag(SOF_Internal))
+				continue;
+#endif
+
+			SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[visibleChildCount]);
+			visibleChildCount++;
+
+			UINT64 curId = currentSOChild->getInstanceId();
+			if (curId != currentChild->mId)
+			{
+				completeMatch = false;
+				break;
 			}
 		}
 
+		completeMatch &= visibleChildCount == element->mChildren.size();
+
 		// Not a complete match, compare everything and insert/delete elements as needed
 		bool needsUpdate = false;
 		if(!completeMatch)
@@ -91,8 +104,15 @@ namespace BansheeEngine
 			for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
 			{
 				HSceneObject currentSOChild = currentSO->getChild(i);
+
+#if BS_DEBUG_MODE == 0
+				if (currentSOChild->hasFlag(SOF_Internal))
+					continue;
+#endif
+
 				UINT64 curId = currentSOChild->getInstanceId();
 				bool found = false;
+
 				for(UINT32 j = 0; j < element->mChildren.size(); j++)
 				{
 					SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[j]);

+ 1 - 1
BansheeEngine/Source/BsGUIDropDownBoxManager.cpp

@@ -13,7 +13,7 @@ namespace BansheeEngine
 	{
 		closeDropDownBox();
 
-		mDropDownSO = SceneObject::create("DropDownBox");
+		mDropDownSO = SceneObject::create("DropDownBox", SOF_Internal | SOF_Persistent | SOF_DontSave);
 		mDropDownBox = mDropDownSO->addComponent<GUIDropDownMenu>(target, placement, dropDownData, skin, type);
 		mOnClosedCallback = onClosedCallback;
 

+ 1 - 1
BansheeEngine/Source/BsProfilerOverlay.cpp

@@ -303,7 +303,7 @@ namespace BansheeEngine
 		if(mWidgetSO)
 			mWidgetSO->destroy();
 
-		mWidgetSO = SceneObject::create("ProfilerOverlay");
+		mWidgetSO = SceneObject::create("ProfilerOverlay", SOF_Internal | SOF_Persistent | SOF_DontSave);
 		mWidget = mWidgetSO->addComponent<GUIWidget>(mTarget.get());
 		mWidget->setDepth(127);
 		mWidget->setSkin(BuiltinResources::instance().getGUISkin());

+ 1 - 1
MBansheeEditor/Scene/SceneWindow.cs

@@ -388,7 +388,7 @@ namespace BansheeEditor
 
 		    if (camera == null)
 		    {
-                SceneObject sceneCameraSO = new SceneObject("SceneCamera");
+                SceneObject sceneCameraSO = new SceneObject("SceneCamera", true);
                 camera = sceneCameraSO.AddComponent<Camera>();
                 camera.Target = renderTexture;
                 camera.ViewportRect = new Rect2(0.0f, 0.0f, 1.0f, 1.0f);

+ 22 - 2
MBansheeEngine/SceneObject.cs

@@ -158,7 +158,15 @@ namespace BansheeEngine
 
         public SceneObject(string name)
         {
-            Internal_CreateInstance(this, name);
+            Internal_CreateInstance(this, name, 0);
+        }
+
+        internal SceneObject(string name, bool isInternal)
+        {
+            if(isInternal)
+                Internal_CreateInstance(this, name, (int)(SceneObjectEditorFlags.DontSave | SceneObjectEditorFlags.Internal | SceneObjectEditorFlags.Persistent));
+            else
+                Internal_CreateInstance(this, name, 0);
         }
 
         public T AddComponent<T>() where T : Component
@@ -239,7 +247,7 @@ namespace BansheeEngine
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(SceneObject instance, string name);
+        private static extern void Internal_CreateInstance(SceneObject instance, string name, int flags);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetParent(IntPtr nativeInstance, SceneObject parent);
@@ -331,4 +339,16 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_Destroy(IntPtr nativeInstance, bool immediate);
     }
+
+    // Note: Must be equal to C++ enum SceneObjectFlags
+    internal enum SceneObjectEditorFlags
+    {
+        DontInstantiate = 0x01, /**< Object wont be in the main scene and its components won't receive updates. */
+        DontSave = 0x02,		/**< Object will be skipped when saving the scene hierarchy or a prefab. */
+        Persistent = 0x04,		/**< Object will remain in the scene even after scene clear, unless destroyed directly. 
+									 This only works with top-level objects. */
+        Internal = 0x08			/**< Provides a hint to external systems that his object is used by engine internals.
+									 For example, those systems might not want to display those objects together with the
+									 user created ones. */
+    }
 }

+ 1 - 1
SBansheeEngine/Include/BsScriptSceneObject.h

@@ -22,7 +22,7 @@ namespace BansheeEngine
 	private:
 		friend class ScriptGameObjectManager;
 
-		static void internal_createInstance(MonoObject* instance, MonoString* name);
+		static void internal_createInstance(MonoObject* instance, MonoString* name, UINT32 flags);
 
 		static void internal_setParent(ScriptSceneObject* nativeInstance, MonoObject* parent);
 		static MonoObject* internal_getParent(ScriptSceneObject* nativeInstance);

+ 1 - 1
SBansheeEngine/Source/BsScriptScene.cpp

@@ -28,8 +28,8 @@ namespace BansheeEngine
 		HSceneObject root = prefab->instantiate();
 
 		MonoString* uuid = MonoUtil::stringToMono(MonoManager::instance().getDomain(), prefab.getUUID());
-		gResources().unload(prefab);
 
+		// TODO - Return actual prefab
 		return uuid;
 	}
 

+ 2 - 2
SBansheeEngine/Source/BsScriptSceneObject.cpp

@@ -58,9 +58,9 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_Destroy", &ScriptSceneObject::internal_destroy);
 	}
 
-	void ScriptSceneObject::internal_createInstance(MonoObject* instance, MonoString* name)
+	void ScriptSceneObject::internal_createInstance(MonoObject* instance, MonoString* name, UINT32 flags)
 	{
-		HSceneObject sceneObject = SceneObject::create(toString(MonoUtil::monoToWString(name)));
+		HSceneObject sceneObject = SceneObject::create(toString(MonoUtil::monoToWString(name)), flags);
 
 		ScriptGameObjectManager::instance().createScriptSceneObject(instance, sceneObject);
 	}

+ 9 - 9
TODO.txt

@@ -27,20 +27,20 @@ return them in checkForModifications?
 ---------------------------------------------------------------------
 Prefab diff
 
-TODO - Recursively generating prefab diffs doesn't work. e.g. if one prefab has multiple prefab children we just generate a single
-       diff for the top-most parent but I should probably generate one for each prefab?
-	    - The same issue is with updateFromPrefab - presumably it should update all child prefabs as well
-
-TODO - When clearing a scene I need to be able to persist certain elements (e.g. editor-only stuff)
-
 TODO - How do I mark the scene as modified? I need to know if user made any modifications to the scene to
        ask him before loading another scene or exiting.
 	     - Just have a EditorUtility.SetDirty for the resource? This means I would need to use this general
 		   approach for all resources, but I probably need that since likely I want to save all modified resources
 		   before editor exits? Think about this.
-
-TODO - I should add a "prefabHash" to each scene object so I know when I need to call updateFromPrefab and when not.
-       Otherwise I need to call it every time I instantiate a prefab.
+		 - Keep active scene Prefab loaded in Scene
+		 - Call SetDirty on it whenever I:
+		   - Reparent an object, create new SO, or delete an SO (from editor)
+		   - Add a new component, remove a component, or modify a component in inspector
+		   - Allow user to call SetDirty himself
+		 - What do I do when I try to unload a dirty object?
+		  - Nothing unless doing so from the editor
+		 - Should I store dirty state in Script* objects?
+		  - Probably the best
 
 Level save
  - Drag and drop to project window