Browse Source

Prefab diff is now properly recorded when a prefab is saved
Prefab diff game object handles are now properly renamed when a diff is being saved
Properly rename game object handles in a prefab diff when instantiating a prefab

BearishSun 10 years ago
parent
commit
9d8a24fddd

+ 1 - 1
BansheeCore/Include/BsPrefabDiffRTTI.h

@@ -199,7 +199,7 @@ namespace BansheeEngine
 
 
 				for (auto& child : subObject.entries)
 				for (auto& child : subObject.entries)
 				{
 				{
-					RTTIField* curGenericField = rtti->getField(child.second.fieldId);
+					RTTIField* curGenericField = rtti->findField(child.second.fieldId);
 					if (curGenericField == nullptr)
 					if (curGenericField == nullptr)
 						continue;
 						continue;
 
 

+ 3 - 1
BansheeCore/Source/BsGameObjectManager.cpp

@@ -223,7 +223,9 @@ namespace BansheeEngine
 		}
 		}
 
 
 		// If still not found, this is the first such handle so register its handle data
 		// If still not found, this is the first such handle so register its handle data
-		mUnresolvedHandleData[originalId] = object.mData;
+		if (!foundHandleData)
+			mUnresolvedHandleData[originalId] = object.mData;
+
 		mUnresolvedHandles.push_back({ originalId, object });
 		mUnresolvedHandles.push_back({ originalId, object });
 	}
 	}
 
 

+ 22 - 1
BansheeCore/Source/BsPrefab.cpp

@@ -52,6 +52,28 @@ namespace BansheeEngine
 
 
 		mNextLinkId = newNextLinkId;
 		mNextLinkId = newNextLinkId;
 
 
+		// If there are any child prefab instances, make sure to update their diffs so they are saved with this prefab
+		Stack<HSceneObject> todo;
+		todo.push(sceneObject);
+
+		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);
+
+				if (!child->mPrefabLinkUUID.empty())
+					PrefabUtility::recordPrefabDiff(child);
+				else
+					todo.push(child);
+			}
+		}
+
+		// Clone the hierarchy for internal storage
 		sceneObject->setFlags(SOF_DontInstantiate);
 		sceneObject->setFlags(SOF_DontInstantiate);
 		mRoot = sceneObject->clone();
 		mRoot = sceneObject->clone();
 		sceneObject->unsetFlags(SOF_DontInstantiate);
 		sceneObject->unsetFlags(SOF_DontInstantiate);
@@ -59,7 +81,6 @@ namespace BansheeEngine
 		mRoot->mParent = nullptr;
 		mRoot->mParent = nullptr;
 
 
 		// Remove objects with "dont save" flag
 		// Remove objects with "dont save" flag
-		Stack<HSceneObject> todo;
 		todo.push(mRoot);
 		todo.push(mRoot);
 
 
 		while (!todo.empty())
 		while (!todo.empty())

+ 17 - 5
BansheeCore/Source/BsPrefabDiff.cpp

@@ -35,6 +35,10 @@ namespace BansheeEngine
 		// Note: If this method is called multiple times in a row then renaming all objects every time is redundant, it
 		// Note: If this method is called multiple times in a row then renaming all objects every time is redundant, it
 		// would be more efficient to do it once outside of this method. I'm keeping it this way for simplicity for now.
 		// would be more efficient to do it once outside of this method. I'm keeping it this way for simplicity for now.
 
 
+		// Rename instance objects so they share the same IDs as the prefab objects (if they link IDs match). This allows
+		// game object handle diff to work properly, because otherwise handles that point to same objects would be 
+		// marked as different because the instance IDs of the two objects don't match (since one is in prefab and one
+		// in instance).
 		Vector<RenamedGameObject> renamedObjects;
 		Vector<RenamedGameObject> renamedObjects;
 		renameInstanceIds(prefab, instance, renamedObjects);
 		renameInstanceIds(prefab, instance, renamedObjects);
 
 
@@ -329,8 +333,11 @@ namespace BansheeEngine
 			String uuid;
 			String uuid;
 		};
 		};
 
 
+		// When renaming it is important to rename the instance and not the prefab, since the diff will otherwise
+		// contain prefab's IDs, but will be used for the instance.
+
 		Stack<StackEntry> todo;
 		Stack<StackEntry> todo;
-		todo.push({ prefab, "root" });
+		todo.push({ instance, "root" });
 
 
 		while (!todo.empty())
 		while (!todo.empty())
 		{
 		{
@@ -347,14 +354,19 @@ namespace BansheeEngine
 
 
 			const Vector<HComponent>& components = current.so->getComponents();
 			const Vector<HComponent>& components = current.so->getComponents();
 			for (auto& component : components)
 			for (auto& component : components)
-				idMap[component->getLinkId()] = component->getInstanceId();
+			{
+				if (component->getLinkId() != (UINT32)-1)
+					idMap[component->getLinkId()] = component->getInstanceId();
+			}
 
 
 			UINT32 numChildren = current.so->getNumChildren();
 			UINT32 numChildren = current.so->getNumChildren();
 			for (UINT32 i = 0; i < numChildren; i++)
 			for (UINT32 i = 0; i < numChildren; i++)
 			{
 			{
 				HSceneObject child = current.so->getChild(i);
 				HSceneObject child = current.so->getChild(i);
 
 
-				idMap[child->getLinkId()] = child->getInstanceId();
+				if (child->getLinkId() != (UINT32)-1)
+					idMap[child->getLinkId()] = child->getInstanceId();
+
 				todo.push({ child, childParentUUID });
 				todo.push({ child, childParentUUID });
 			}
 			}
 		}
 		}
@@ -366,10 +378,10 @@ namespace BansheeEngine
 			renamedGO.instanceData = instance->mInstanceData;
 			renamedGO.instanceData = instance->mInstanceData;
 			renamedGO.originalId = instance->getInstanceId();
 			renamedGO.originalId = instance->getInstanceId();
 
 
-			instance->mInstanceData->mInstanceId = prefab->getInstanceId();
+			prefab->mInstanceData->mInstanceId = instance->getInstanceId();
 		}
 		}
 
 
-		todo.push({ instance, "root" });
+		todo.push({ prefab, "root" });
 		while (!todo.empty())
 		while (!todo.empty())
 		{
 		{
 			StackEntry current = todo.top();
 			StackEntry current = todo.top();

+ 5 - 4
BansheeCore/Source/BsPrefabUtility.cpp

@@ -99,15 +99,12 @@ namespace BansheeEngine
 				UnorderedMap<UINT32, GameObjectInstanceDataPtr> linkedInstanceData;
 				UnorderedMap<UINT32, GameObjectInstanceDataPtr> linkedInstanceData;
 				recordInstanceData(current, soProxy, linkedInstanceData);
 				recordInstanceData(current, soProxy, linkedInstanceData);
 
 
-				PrefabDiffPtr prefabDiff = current->mPrefabDiff;
 				HSceneObject parent = current->getParent();
 				HSceneObject parent = current->getParent();
+				PrefabDiffPtr prefabDiff = current->mPrefabDiff;
 
 
 				current->destroy(true);
 				current->destroy(true);
 				HSceneObject newInstance = prefabLink->instantiate(true);
 				HSceneObject newInstance = prefabLink->instantiate(true);
 
 
-				if (prefabDiff != nullptr)
-					prefabDiff->apply(newInstance);
-
 				// When restoring instance IDs it is important to make all the new handles point to the old GameObjectInstanceData.
 				// When restoring instance IDs it is important to make all the new handles point to the old GameObjectInstanceData.
 				// This is because old handles will have different GameObjectHandleData and we have no easy way of accessing it to
 				// This is because old handles will have different GameObjectHandleData and we have no easy way of accessing it to
 				// change to which GameObjectInstanceData it points. But the GameObjectManager ensures that all handles deserialized
 				// change to which GameObjectInstanceData it points. But the GameObjectManager ensures that all handles deserialized
@@ -117,6 +114,10 @@ namespace BansheeEngine
 				restoreLinkedInstanceData(newInstance, soProxy, linkedInstanceData);
 				restoreLinkedInstanceData(newInstance, soProxy, linkedInstanceData);
 				restoreUnlinkedInstanceData(newInstance, soProxy);
 				restoreUnlinkedInstanceData(newInstance, soProxy);
 
 
+				// Diff must be applied after the rename to ensure its game object handles point to valid objects
+				if (prefabDiff != nullptr)
+					prefabDiff->apply(newInstance);
+
 				newPrefabInstances.push_back({ newInstance, parent, newInstance->getLinkId() });
 				newPrefabInstances.push_back({ newInstance, parent, newInstance->getLinkId() });
 			}
 			}
 		}
 		}

+ 0 - 2
SBansheeEditor/Source/BsScriptEditorApplication.cpp

@@ -179,13 +179,11 @@ namespace BansheeEngine
 			scene = static_resource_cast<Prefab>(gProjectLibrary().load(nativePath));
 			scene = static_resource_cast<Prefab>(gProjectLibrary().load(nativePath));
 			scene->update(sceneRoot);
 			scene->update(sceneRoot);
 
 
-			PrefabUtility::recordPrefabDiff(sceneRoot);
 			gProjectLibrary().saveEntry(scene);
 			gProjectLibrary().saveEntry(scene);
 		}
 		}
 		else
 		else
 		{
 		{
 			scene = Prefab::create(sceneRoot);
 			scene = Prefab::create(sceneRoot);
-			PrefabUtility::recordPrefabDiff(sceneRoot);
 			gProjectLibrary().createEntry(scene, nativePath);
 			gProjectLibrary().createEntry(scene, nativePath);
 		}
 		}