Quellcode durchsuchen

- Clear prefab link IDs when their prefab parent changes
- Don't store actual prefab handle in scene object to avoid always loading it

Marko Pintera vor 10 Jahren
Ursprung
Commit
029a785af4

+ 7 - 1
BansheeCore/Include/BsPrefabUtility.h

@@ -67,7 +67,13 @@ namespace BansheeEngine
 		 *
 		 * @note	If any of its children belong to another prefab they will not be cleared.
 		 */
-		static void clearPrefabIds(const HSceneObject& sceneObject);
+		static void clearPrefabIds(const HSceneObject& sceneObject, bool recursive = true);
+
+		/**
+		 * @brief	Updates the internal prefab diff data by recording the difference
+		 *			between the current values in the provided prefab instance and its prefab.
+		 */
+		static void recordPrefabDiff(const HSceneObject& sceneObject);
 
 	private:
 		/**

+ 3 - 9
BansheeCore/Include/BsSceneObject.h

@@ -69,11 +69,11 @@ namespace BansheeEngine
 		HSceneObject getHandle() const { return mThisHandle; }
 
 		/**
-		 * @brief	Returns the prefab this object is linked to, if any. 
+		 * @brief	Returns the UUID of the prefab this object is linked to, if any. 
 		 *
 		 * @note	Requires a search of all parents potentially.
 		 */
-		HPrefab getPrefabLink() const;
+		String getPrefabLink() const;
 
 		/**
 		 * @brief	Breaks the link between this prefab instance and its prefab.
@@ -131,7 +131,7 @@ namespace BansheeEngine
 
 	private:
 		HSceneObject mThisHandle;
-		HPrefab mPrefabLink;
+		String mPrefabLinkUUID;
 		PrefabDiffPtr mPrefabDiff;
 		UINT32 mFlags;
 
@@ -423,12 +423,6 @@ namespace BansheeEngine
 		 */
 		HSceneObject clone();
 
-		/**
-		 * @brief	Updates the internal prefab diff data by recording the difference
-		 *			between the current values in this prefab instance and the prefab.
-		 */
-		void recordPrefabDiff();
-
 	private:
 		HSceneObject mParent;
 		Vector<HSceneObject> mChildren;

+ 3 - 3
BansheeCore/Include/BsSceneObjectRTTI.h

@@ -33,8 +33,8 @@ 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; }
+		String& getPrefabLink(SceneObject* obj) { return obj->mPrefabLinkUUID; }
+		void setPrefabLink(SceneObject* obj, String& value) { obj->mPrefabLinkUUID = value; }
 
 		PrefabDiffPtr getPrefabDiff(SceneObject* obj) { return obj->mPrefabDiff; }
 		void setPrefabDiff(SceneObject* obj, PrefabDiffPtr value) { obj->mPrefabDiff = value; }
@@ -48,7 +48,7 @@ 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("mPrefabLink", 2, &SceneObjectRTTI::getPrefabLink, &SceneObjectRTTI::setPrefabLink);
 			addPlainField("mFlags", 3, &SceneObjectRTTI::getFlags, &SceneObjectRTTI::setFlags);
 			addReflectablePtrField("mPrefabDiff", 4, &SceneObjectRTTI::getPrefabDiff, &SceneObjectRTTI::setPrefabDiff);
 		}

+ 2 - 2
BansheeCore/Source/BsPrefab.cpp

@@ -14,13 +14,13 @@ namespace BansheeEngine
 
 	HPrefab Prefab::create(const HSceneObject& sceneObject)
 	{
-		assert(sceneObject->mPrefabLink == nullptr);
+		assert(sceneObject->mPrefabLinkUUID.empty());
 
 		PrefabPtr newPrefab = createEmpty();
 		newPrefab->initialize(sceneObject);
 
 		HPrefab handle = static_resource_cast<Prefab>(gResources()._createResourceHandle(newPrefab));
-		sceneObject->mPrefabLink = handle;
+		sceneObject->mPrefabLinkUUID = handle.getUUID();
 
 		return handle;
 	}

+ 2 - 2
BansheeCore/Source/BsPrefabDiff.cpp

@@ -336,7 +336,7 @@ namespace BansheeEngine
 			{
 				HSceneObject child = current->getChild(i);
 
-				if (child->mPrefabLink == nullptr)
+				if (child->mPrefabLinkUUID.empty())
 					todo.push(child);
 			}
 		}
@@ -381,7 +381,7 @@ namespace BansheeEngine
 			{
 				HSceneObject child = current->getChild(i);
 
-				if (child->mPrefabLink == nullptr)
+				if (child->mPrefabLinkUUID.empty())
 					todo.push(child);
 			}
 		}

+ 43 - 11
BansheeCore/Source/BsPrefabUtility.cpp

@@ -2,12 +2,15 @@
 #include "BsPrefabDiff.h"
 #include "BsPrefab.h"
 #include "BsSceneObject.h"
+#include "BsResources.h"
 
 namespace BansheeEngine
 {
 	void PrefabUtility::revertToPrefab(const HSceneObject& so)
 	{
-		HPrefab prefabLink = so->getPrefabLink();
+		String prefabLinkUUID = so->getPrefabLink();
+		HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false));
+
 		if (prefabLink == nullptr)
 			return;
 
@@ -24,7 +27,9 @@ namespace BansheeEngine
 
 	void PrefabUtility::updateFromPrefab(const HSceneObject& so)
 	{
-		HPrefab prefabLink = so->getPrefabLink();
+		String prefabLinkUUID = so->getPrefabLink();
+		HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false));
+
 		if (prefabLink == nullptr)
 			return;
 
@@ -75,7 +80,7 @@ namespace BansheeEngine
 			{
 				HSceneObject child = currentSO->getChild(i);
 
-				if (child->mPrefabLink == nullptr)
+				if (child->mPrefabLinkUUID.empty())
 					todo.push(currentSO->getChild(i));
 			}
 		}
@@ -100,7 +105,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void PrefabUtility::clearPrefabIds(const HSceneObject& sceneObject)
+	void PrefabUtility::clearPrefabIds(const HSceneObject& sceneObject, bool recursive)
 	{
 		Stack<HSceneObject> todo;
 		todo.push(sceneObject);
@@ -114,14 +119,41 @@ namespace BansheeEngine
 			for (auto& component : currentSO->mComponents)
 				component->mLinkId = -1;
 
-			UINT32 numChildren = (UINT32)currentSO->getNumChildren();
-			for (UINT32 i = 0; i < numChildren; i++)
+			if (recursive)
 			{
-				HSceneObject child = currentSO->getChild(i);
+				UINT32 numChildren = (UINT32)currentSO->getNumChildren();
+				for (UINT32 i = 0; i < numChildren; i++)
+				{
+					HSceneObject child = currentSO->getChild(i);
 
-				if (child->mPrefabLink == nullptr)
-					todo.push(child);
+					if (child->mPrefabLinkUUID.empty())
+						todo.push(child);
+				}
+			}
+		}
+	}
+
+	void PrefabUtility::recordPrefabDiff(const HSceneObject& sceneObject)
+	{
+		HSceneObject curObj = sceneObject;
+
+		while (curObj == nullptr)
+		{
+			if (!curObj->mPrefabLinkUUID.empty())
+			{
+				curObj->mPrefabDiff = nullptr;
+
+				HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(curObj->mPrefabLinkUUID, false, false));
+				if (prefabLink != nullptr)
+					curObj->mPrefabDiff = PrefabDiff::create(prefabLink->getRoot(), curObj->getHandle());
+
+				return;
 			}
+
+			if (curObj->mParent != nullptr)
+				curObj = curObj->mParent;
+			else
+				curObj = nullptr;
 		}
 	}
 
@@ -182,7 +214,7 @@ namespace BansheeEngine
 				StackEntry& newEntry = todo.top();
 
 				newEntry.so = child;
-				newEntry.isPartOfPrefab = current.isPartOfPrefab && (child->mPrefabLink == nullptr);
+				newEntry.isPartOfPrefab = current.isPartOfPrefab && child->mPrefabLinkUUID.empty();
 			}
 		}
 	}
@@ -220,7 +252,7 @@ namespace BansheeEngine
 			{
 				HSceneObject child = current->getChild(i);
 
-				if (child->mPrefabLink == nullptr)
+				if (child->mPrefabLinkUUID.empty())
 					todo.push(child);
 			}
 		}

+ 16 - 27
BansheeCore/Source/BsSceneObject.cpp

@@ -106,14 +106,14 @@ namespace BansheeEngine
 		mThisHandle._setHandleData(mThisHandle.getInternalPtr());
 	}
 
-	HPrefab SceneObject::getPrefabLink() const
+	String SceneObject::getPrefabLink() const
 	{
 		const SceneObject* curObj = this;
 
 		while (curObj == nullptr)
 		{
-			if (curObj->mPrefabLink != nullptr)
-				return curObj->mPrefabLink;
+			if (!curObj->mPrefabLinkUUID.empty())
+				return curObj->mPrefabLinkUUID;
 
 			if (curObj->mParent != nullptr)
 				curObj = curObj->mParent.get();
@@ -121,7 +121,7 @@ namespace BansheeEngine
 				curObj = nullptr;
 		}
 
-		return HPrefab();
+		return "";
 	}
 
 	void SceneObject::breakPrefabLink()
@@ -130,9 +130,9 @@ namespace BansheeEngine
 
 		while (curObj == nullptr)
 		{
-			if (curObj->mPrefabLink != nullptr)
+			if (!curObj->mPrefabLinkUUID.empty())
 			{
-				curObj->mPrefabLink = nullptr;
+				curObj->mPrefabLinkUUID = "";
 				curObj->mPrefabDiff = nullptr;
 				PrefabUtility::clearPrefabIds(curObj->getHandle());
 
@@ -458,6 +458,10 @@ namespace BansheeEngine
 			Quaternion worldRot = getWorldRotation();
 			Vector3 worldScale = getWorldScale();
 
+#if BS_EDITOR_BUILD
+			String originalPrefab = getPrefabLink();
+#endif
+
 			if(mParent != nullptr)
 				mParent->removeChild(mThisHandle);
 
@@ -466,6 +470,12 @@ namespace BansheeEngine
 
 			mParent = parent;
 
+#if BS_EDITOR_BUILD
+			String newPrefab = getPrefabLink();
+			if (originalPrefab != newPrefab)
+				PrefabUtility::clearPrefabIds(mThisHandle, false);
+#endif
+
 			setWorldPosition(worldPos);
 			setWorldRotation(worldRot);
 			setWorldScale(worldScale);
@@ -553,27 +563,6 @@ namespace BansheeEngine
 		return cloneObj->mThisHandle;
 	}
 
-	void SceneObject::recordPrefabDiff()
-	{
-		SceneObject* curObj = this;
-
-		while (curObj == nullptr)
-		{
-			if (curObj->mPrefabLink != nullptr)
-			{
-				curObj->mPrefabDiff = nullptr;
-				curObj->mPrefabDiff = PrefabDiff::create(curObj->mPrefabLink->getRoot(), curObj->getHandle());
-				
-				return;
-			}
-
-			if (curObj->mParent != nullptr)
-				curObj = curObj->mParent.get();
-			else
-				curObj = nullptr;
-		}
-	}
-
 	HComponent SceneObject::getComponent(UINT32 typeId) const
 	{
 		for(auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)

+ 2 - 1
BansheeUtility/Include/BsPlatformDefines.h

@@ -15,9 +15,10 @@
 
 #define BS_ENDIAN_LITTLE 1
 #define BS_ENDIAN_BIG 2
-
 #define BS_ENDIAN BS_ENDIAN_LITTLE
 
+#define BS_EDITOR_BUILD 1
+
 // Finds the compiler type and version.
 #if defined( _MSC_VER )
 #   define BS_COMPILER BS_COMPILER_MSVC

+ 7 - 2
SBansheeEngine/Source/BsScriptSceneObject.cpp

@@ -10,6 +10,7 @@
 #include "BsScriptPrefab.h"
 #include "BsPrefab.h"
 #include "BsPrefabUtility.h"
+#include "BsResources.h"
 
 namespace BansheeEngine
 {
@@ -127,7 +128,9 @@ namespace BansheeEngine
 		if (checkIfDestroyed(nativeInstance))
 			return nullptr;
 
-		HPrefab prefab = nativeInstance->mSceneObject->getPrefabLink();
+		String prefabLinkUUID = nativeInstance->mSceneObject->getPrefabLink();
+		HPrefab prefab = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false));
+
 		if (prefab != nullptr)
 		{
 			ScriptPrefab* scriptPrefab = ScriptResourceManager::instance().getScriptPrefab(prefab);
@@ -153,7 +156,9 @@ namespace BansheeEngine
 		if (checkIfDestroyed(nativeInstance))
 			return;
 
-		HPrefab prefab = nativeInstance->mSceneObject->getPrefabLink();
+		String prefabLinkUUID = nativeInstance->mSceneObject->getPrefabLink();
+		HPrefab prefab = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false));
+
 		if (prefab != nullptr)
 			prefab->update(nativeInstance->mSceneObject);
 	}

+ 25 - 7
TODO.txt

@@ -27,13 +27,31 @@ return them in checkForModifications?
 ---------------------------------------------------------------------
 Prefab diff
 
-TODO:
- - Hook up prefabs & prefab diffs:
-   - Save a diff whenever a HSceneObject with a HPrefab link is saved (call recordPrefabDiff)
-   - Update active scene when prefab is modified (destroy them, create original prefab then apply saved diff)
-
-TODO - Whenever I load a SceneObject the entire prefab will be loaded as well. It might be better to store the
- prefab link as a UUID and then load it on demand.
+Level save
+ - Entry in File menu (Ctrl + S)
+   - Clicking it opens up a save file dialog unless level is already linked to prefab
+ - Drag and drop to project window
+   - Automatically creates a new prefab with the same name as the root object
+    - Or creates a unique name if an asset with the same name exists
+ - Whenever I save I need to go through all prefab instances and update their diffs (Make this editor only, but level saving should be editor only all together)
+
+Level load
+ - Entry in File menu (Ctrl + L)
+   - Opens up a file open dialog
+ - Drag and drop from project window or double-click in project window
+  - Unloads the current level and loads the new one
+   - TODO - If current level is modified ask the user to confirm?
+ - After loading a new scene always perform an "updateFromPrefab" action on all prefab instances (Make this editor only)
+   - TODO - Should I monitor for prefab changes so I don't update unless prefab actually changed? 
+    - Have each scene object have a prefabHash stored, and prefab hash should increase whenever it is saved.
+	  This way I can check if I really need to update the prefab instance.
+
+TODO - Prefabs should be editor only, especially automatic diff creation and updates on load
+ - In C# instead of having prefab stuff on SceneObject, move it to BansheeEditor.PrefabUtility
+
+TODO - Add Prefab buttons to Inspector (Apply, Break, Revert)
+
+TODO - Later - When building level for release make sure to clear all prefab diffs (possibly store them elsewhere in the first place)
 
  Test (likely later once I have more editor functionality working):
   - Game object handle compare