Browse Source

Properly assign prefab link UUID when updating an existing prefab
Ensure that updating a prefab does not break old link IDs
Link IDs are no longer re-used
When updating from prefab properly restore link ID of the root scene object

BearishSun 10 years ago
parent
commit
0be19d24be

+ 7 - 0
BansheeCore/Include/BsCoreSceneManager.h

@@ -29,6 +29,13 @@ namespace BansheeEngine
 		 */
 		void clearScene(bool forceAll = false);
 
+		/**
+		 * @brief	Changes the root scene object.
+		 * 			
+		 * @note	Internal method.
+		 */
+		void _setRootNode(const HSceneObject& root);
+
 		/**
 		 * @brief	Called every frame. Calls update methods on all 
 		 *			scene objects and their components.

+ 2 - 0
BansheeCore/Include/BsPrefab.h

@@ -65,6 +65,8 @@ namespace BansheeEngine
 
 		HSceneObject mRoot;
 		UINT32 mHash;
+		String mUUID;
+		UINT32 mNextLinkId;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/

+ 4 - 0
BansheeCore/Include/BsPrefabRTTI.h

@@ -16,11 +16,15 @@ namespace BansheeEngine
 		UINT32& getHash(Prefab* obj) { return obj->mHash; }
 		void setHash(Prefab* obj, UINT32& val) { obj->mHash = val; }
 
+		UINT32& getNextLinkId(Prefab* obj) { return obj->mNextLinkId; }
+		void setNextLinkId(Prefab* obj, UINT32& val) { obj->mNextLinkId = val; }
+
 	public:
 		PrefabRTTI()
 		{
 			addReflectablePtrField("mRoot", 0, &PrefabRTTI::getSceneObject, &PrefabRTTI::setSceneObject);
 			addPlainField("mHash", 1, &PrefabRTTI::getHash, &PrefabRTTI::setHash);
+			addPlainField("mNextLinkId", 2, &PrefabRTTI::getNextLinkId, &PrefabRTTI::setNextLinkId);
 		}
 
 		virtual const String& getRTTIName() override

+ 1 - 1
BansheeCore/Include/BsPrefabUtility.h

@@ -58,7 +58,7 @@ namespace BansheeEngine
 		 * @note	If any children of the provided object belong to another prefab they will 
 		 *			not have IDs generated.
 		 */
-		static void generatePrefabIds(const HSceneObject& sceneObject);
+		static UINT32 generatePrefabIds(const HSceneObject& sceneObject, UINT32 startingId);
 
 		/**
 		 * @brief	Clears all prefab "link" IDs in the provided object and its children.

+ 7 - 0
BansheeCore/Include/BsSceneObject.h

@@ -461,6 +461,13 @@ namespace BansheeEngine
 		bool mActiveSelf;
 		bool mActiveHierarchy;
 
+		/**
+		 * @brief	Internal version of ::setParent that allows you to set a null parent.
+		 *
+		 * @param [in]	parent	New parent.
+		 */
+		void _setParent(const HSceneObject& parent);
+
 		/**
 		 * @brief	Adds a child to the child array. This method doesn't check for null or duplicate values.
 		 *

+ 6 - 0
BansheeCore/Source/BsCoreSceneManager.cpp

@@ -36,6 +36,12 @@ namespace BansheeEngine
 		GameObjectManager::instance().destroyQueuedObjects();
 	}
 
+	void CoreSceneManager::_setRootNode(const HSceneObject& root)
+	{
+		mRootNode = root; 
+		mRootNode->_setParent(HSceneObject());
+	}
+
 	void CoreSceneManager::_update()
 	{
 		Stack<HSceneObject> todo;

+ 17 - 5
BansheeCore/Source/BsPrefab.cpp

@@ -7,7 +7,7 @@
 namespace BansheeEngine
 {
 	Prefab::Prefab()
-		:Resource(false), mHash(0)
+		:Resource(false), mHash(0), mNextLinkId(0)
 	{
 		
 	}
@@ -24,8 +24,9 @@ namespace BansheeEngine
 		newPrefab->initialize(sceneObject);
 
 		HPrefab handle = static_resource_cast<Prefab>(gResources()._createResourceHandle(newPrefab));
-		sceneObject->mPrefabLinkUUID = handle.getUUID();
-		newPrefab->_getRoot()->mPrefabLinkUUID = sceneObject->mPrefabLinkUUID;
+		newPrefab->mUUID = handle.getUUID();
+		sceneObject->mPrefabLinkUUID = newPrefab->mUUID;
+		newPrefab->_getRoot()->mPrefabLinkUUID = newPrefab->mUUID;
 
 		return handle;
 	}
@@ -40,8 +41,16 @@ namespace BansheeEngine
 
 	void Prefab::initialize(const HSceneObject& sceneObject)
 	{
-		sceneObject->breakPrefabLink();
-		PrefabUtility::generatePrefabIds(sceneObject);
+		sceneObject->mPrefabDiff = nullptr;
+		UINT32 newNextLinkId = PrefabUtility::generatePrefabIds(sceneObject, mNextLinkId);
+
+		if (newNextLinkId < mNextLinkId)
+		{
+			BS_EXCEPT(InternalErrorException, "Prefab ran out of IDs to assign. " \
+				"Consider increasing the size of the prefab ID data type.");
+		}
+
+		mNextLinkId = newNextLinkId;
 
 		sceneObject->setFlags(SOF_DontInstantiate);
 		mRoot = sceneObject->clone();
@@ -72,6 +81,9 @@ namespace BansheeEngine
 	void Prefab::update(const HSceneObject& sceneObject)
 	{
 		initialize(sceneObject);
+		sceneObject->mPrefabLinkUUID = mUUID;
+		mRoot->mPrefabLinkUUID = mUUID;
+
 		mHash++;
 	}
 

+ 19 - 35
BansheeCore/Source/BsPrefabUtility.cpp

@@ -67,7 +67,14 @@ namespace BansheeEngine
 			}
 		}
 
-		Vector<std::pair<HSceneObject, HSceneObject>> newPrefabInstances;
+		struct RestoredPrefabInstance
+		{
+			HSceneObject newInstance;
+			HSceneObject originalParent;
+			UINT32 originalLinkId;
+		};
+
+		Vector<RestoredPrefabInstance> newPrefabInstances;
 
 		// 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)
@@ -94,24 +101,22 @@ namespace BansheeEngine
 				restoreLinkedInstanceData(newInstance, soProxy, linkedInstanceData);
 				restoreUnlinkedInstanceData(newInstance, soProxy);
 
-				newPrefabInstances.push_back({ newInstance, parent });
+				newPrefabInstances.push_back({ newInstance, parent, newInstance->getLinkId() });
 			}
 		}
 
-		// Once everything is instantiated, restore old parents
+		// Once everything is instantiated, restore old parents & link IDs for root
 		for (auto& newInstanceData : newPrefabInstances)
 		{
-			newInstanceData.first->mParent = newInstanceData.second;
+			newInstanceData.newInstance->setParent(newInstanceData.originalParent);
+			newInstanceData.newInstance->mLinkId = newInstanceData.originalLinkId;
 		}
 
 		gResources().unloadAllUnused();
 	}
 
-	void PrefabUtility::generatePrefabIds(const HSceneObject& sceneObject)
+	UINT32 PrefabUtility::generatePrefabIds(const HSceneObject& sceneObject, UINT32 startingId)
 	{
-		Vector<HGameObject> objectsToId;
-		Set<INT32> existingIds;
-
 		Stack<HSceneObject> todo;
 		todo.push(sceneObject);
 
@@ -123,9 +128,7 @@ namespace BansheeEngine
 			for (auto& component : currentSO->mComponents)
 			{
 				if (component->getLinkId() == -1)
-					objectsToId.push_back(component);
-				else
-					existingIds.insert(component->getLinkId());
+					component->mLinkId = startingId++;
 			}
 
 			UINT32 numChildren = (UINT32)currentSO->getNumChildren();
@@ -136,9 +139,7 @@ namespace BansheeEngine
 				if (!child->hasFlag(SOF_DontSave))
 				{
 					if (child->getLinkId() == -1)
-						objectsToId.push_back(child);
-					else
-						existingIds.insert(child->getLinkId());
+						child->mLinkId = startingId++;
 
 					if(child->mPrefabLinkUUID.empty())
 						todo.push(currentSO->getChild(i));
@@ -146,27 +147,7 @@ namespace BansheeEngine
 			}
 		}
 
-		auto setIter = existingIds.begin();
-		INT32 nextId = 0;
-		for (auto& object : objectsToId)
-		{
-			INT32 freeId = -1;
-			for (; setIter != existingIds.end(); ++setIter)
-			{
-				if (nextId < (*setIter))
-				{
-					freeId = nextId++;
-					break;
-				}
-				else
-					nextId++;
-			}
-
-			if (freeId == -1)
-				freeId = nextId++;
-
-			object->mLinkId = freeId;
-		}
+		return startingId;
 	}
 
 	void PrefabUtility::clearPrefabIds(const HSceneObject& sceneObject, bool recursive)
@@ -212,6 +193,9 @@ namespace BansheeEngine
 				topLevelObject = nullptr;
 		}
 
+		if (topLevelObject == nullptr)
+			return;
+
 		Stack<HSceneObject> todo;
 		todo.push(topLevelObject);
 

+ 13 - 21
BansheeCore/Source/BsSceneObject.cpp

@@ -143,24 +143,8 @@ namespace BansheeEngine
 		if (rootObj != nullptr)
 		{
 			rootObj->mPrefabLinkUUID = "";
+			rootObj->mPrefabDiff = nullptr;
 			PrefabUtility::clearPrefabIds(rootObj->getHandle());
-
-			Stack<SceneObject*> todo;
-			todo.push(rootObj);
-
-			while (!todo.empty())
-			{
-				SceneObject* curObj = todo.top();
-				todo.pop();
-
-				curObj->mPrefabDiff = nullptr;
-
-				for (auto& child : curObj->mChildren)
-				{
-					if (child->mPrefabLinkUUID.empty())
-						todo.push(child.get());
-				}
-			}
 		}
 	}
 
@@ -491,10 +475,18 @@ namespace BansheeEngine
 
 	void SceneObject::setParent(const HSceneObject& parent)
 	{
-		if (parent.isDestroyed() || mThisHandle == parent)
+		if (parent.isDestroyed())
+			return;
+
+		_setParent(parent);
+	}
+
+	void SceneObject::_setParent(const HSceneObject& parent)
+	{
+		if (mThisHandle == parent)
 			return;
 
-		if(mParent == nullptr || mParent != parent)
+		if (mParent == nullptr || mParent != parent)
 		{
 			// Make sure the object keeps its world coordinates
 			Vector3 worldPos = getWorldPosition();
@@ -505,10 +497,10 @@ namespace BansheeEngine
 			String originalPrefab = getPrefabLink();
 #endif
 
-			if(mParent != nullptr)
+			if (mParent != nullptr)
 				mParent->removeChild(mThisHandle);
 
-			if(parent != nullptr)
+			if (parent != nullptr)
 				parent->addChild(mThisHandle);
 
 			mParent = parent;

+ 4 - 6
SBansheeEngine/Source/BsScriptScene.cpp

@@ -35,15 +35,13 @@ namespace BansheeEngine
 		if (prefab.isLoaded(false))
 		{
 			HSceneObject root = prefab->instantiate();
+			HSceneObject oldRoot = gSceneManager().getRootNode();
 
-			UINT32 numChildren = root->getNumChildren();
-			for (UINT32 i = 0; i < numChildren; i++)
+			if (root != nullptr)
 			{
-				HSceneObject child = root->getChild(0);
-				child->setParent(gSceneManager().getRootNode());
+				gSceneManager()._setRootNode(root);
+				oldRoot->destroy();
 			}
-
-			root->destroy();
 		}
 
 		if (prefab != nullptr)