2
0

BsPrefabUtility.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. #include "BsPrefabUtility.h"
  2. #include "BsPrefabDiff.h"
  3. #include "BsPrefab.h"
  4. #include "BsSceneObject.h"
  5. #include "BsResources.h"
  6. namespace BansheeEngine
  7. {
  8. void PrefabUtility::revertToPrefab(const HSceneObject& so)
  9. {
  10. String prefabLinkUUID = so->getPrefabLink();
  11. HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false));
  12. if (prefabLink == nullptr)
  13. return;
  14. // Save IDs, destroy original, create new, restore IDs
  15. SceneObjectProxy soProxy;
  16. UnorderedMap<UINT32, GameObjectInstanceDataPtr> linkedInstanceData;
  17. recordInstanceData(so, soProxy, linkedInstanceData);
  18. so->destroy();
  19. HSceneObject newInstance = prefabLink->instantiate();
  20. restoreLinkedInstanceData(newInstance, linkedInstanceData);
  21. }
  22. void PrefabUtility::updateFromPrefab(const HSceneObject& so)
  23. {
  24. String prefabLinkUUID = so->getPrefabLink();
  25. HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false));
  26. if (prefabLink == nullptr)
  27. return;
  28. // Save IDs, destroy original, create new, apply diff, restore IDs
  29. SceneObjectProxy soProxy;
  30. UnorderedMap<UINT32, GameObjectInstanceDataPtr> linkedInstanceData;
  31. recordInstanceData(so, soProxy, linkedInstanceData);
  32. PrefabDiffPtr prefabDiff = so->mPrefabDiff;
  33. so->destroy();
  34. HSceneObject newInstance = prefabLink->instantiate();
  35. if (prefabDiff != nullptr)
  36. prefabDiff->apply(newInstance);
  37. restoreLinkedInstanceData(newInstance, linkedInstanceData);
  38. restoreUnlinkedInstanceData(newInstance, soProxy);
  39. }
  40. void PrefabUtility::generatePrefabIds(const HSceneObject& sceneObject)
  41. {
  42. Vector<HGameObject> objectsToId;
  43. Set<INT32> existingIds;
  44. Stack<HSceneObject> todo;
  45. todo.push(sceneObject);
  46. while (!todo.empty())
  47. {
  48. HSceneObject currentSO = todo.top();
  49. todo.pop();
  50. if (currentSO->mLinkId == -1)
  51. objectsToId.push_back(currentSO);
  52. else
  53. existingIds.insert(currentSO->mLinkId);
  54. for (auto& component : currentSO->mComponents)
  55. {
  56. if (component->mLinkId == -1)
  57. objectsToId.push_back(component);
  58. else
  59. existingIds.insert(component->mLinkId);
  60. }
  61. UINT32 numChildren = (UINT32)currentSO->getNumChildren();
  62. for (UINT32 i = 0; i < numChildren; i++)
  63. {
  64. HSceneObject child = currentSO->getChild(i);
  65. if (child->mPrefabLinkUUID.empty())
  66. todo.push(currentSO->getChild(i));
  67. }
  68. }
  69. auto setIter = existingIds.begin();
  70. INT32 nextId = 0;
  71. for (auto& object : objectsToId)
  72. {
  73. INT32 freeId = -1;
  74. for (; setIter != existingIds.end(); ++setIter)
  75. {
  76. if (nextId < (*setIter))
  77. freeId = nextId++;
  78. else
  79. nextId++;
  80. }
  81. if (freeId == -1)
  82. freeId = nextId++;
  83. object->mLinkId = freeId;
  84. }
  85. }
  86. void PrefabUtility::clearPrefabIds(const HSceneObject& sceneObject, bool recursive)
  87. {
  88. Stack<HSceneObject> todo;
  89. todo.push(sceneObject);
  90. while (!todo.empty())
  91. {
  92. HSceneObject currentSO = todo.top();
  93. todo.pop();
  94. currentSO->mLinkId = -1;
  95. for (auto& component : currentSO->mComponents)
  96. component->mLinkId = -1;
  97. if (recursive)
  98. {
  99. UINT32 numChildren = (UINT32)currentSO->getNumChildren();
  100. for (UINT32 i = 0; i < numChildren; i++)
  101. {
  102. HSceneObject child = currentSO->getChild(i);
  103. if (child->mPrefabLinkUUID.empty())
  104. todo.push(child);
  105. }
  106. }
  107. }
  108. }
  109. void PrefabUtility::recordPrefabDiff(const HSceneObject& sceneObject)
  110. {
  111. HSceneObject curObj = sceneObject;
  112. while (curObj == nullptr)
  113. {
  114. if (!curObj->mPrefabLinkUUID.empty())
  115. {
  116. curObj->mPrefabDiff = nullptr;
  117. HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(curObj->mPrefabLinkUUID, false, false));
  118. if (prefabLink != nullptr)
  119. curObj->mPrefabDiff = PrefabDiff::create(prefabLink->getRoot(), curObj->getHandle());
  120. return;
  121. }
  122. if (curObj->mParent != nullptr)
  123. curObj = curObj->mParent;
  124. else
  125. curObj = nullptr;
  126. }
  127. }
  128. void PrefabUtility::recordInstanceData(const HSceneObject& so, SceneObjectProxy& output,
  129. UnorderedMap<UINT32, GameObjectInstanceDataPtr>& linkedInstanceData)
  130. {
  131. struct StackEntry
  132. {
  133. HSceneObject so;
  134. bool isPartOfPrefab;
  135. };
  136. Stack<StackEntry> todo;
  137. todo.push(StackEntry());
  138. StackEntry& topEntry = todo.top();
  139. topEntry.so = so;
  140. topEntry.isPartOfPrefab = true;
  141. while (!todo.empty())
  142. {
  143. StackEntry current = todo.top();
  144. todo.pop();
  145. output.instanceData = current.so->_getInstanceData();
  146. if (current.isPartOfPrefab)
  147. {
  148. output.linkId = current.so->getLinkId();
  149. linkedInstanceData[output.linkId] = output.instanceData;
  150. }
  151. else
  152. output.linkId = -1;
  153. const Vector<HComponent>& components = current.so->getComponents();
  154. for (auto& component : components)
  155. {
  156. output.components.push_back(ComponentProxy());
  157. ComponentProxy& componentProxy = output.components.back();
  158. componentProxy.instanceData = component->_getInstanceData();
  159. if (current.isPartOfPrefab)
  160. {
  161. componentProxy.linkId = component->getLinkId();
  162. linkedInstanceData[componentProxy.linkId] = componentProxy.instanceData;
  163. }
  164. else
  165. componentProxy.linkId = -1;
  166. }
  167. UINT32 numChildren = current.so->getNumChildren();
  168. for (UINT32 i = 0; i < numChildren; i++)
  169. {
  170. HSceneObject child = current.so->getChild(i);
  171. todo.push(StackEntry());
  172. StackEntry& newEntry = todo.top();
  173. newEntry.so = child;
  174. newEntry.isPartOfPrefab = current.isPartOfPrefab && child->mPrefabLinkUUID.empty();
  175. }
  176. }
  177. }
  178. void PrefabUtility::restoreLinkedInstanceData(const HSceneObject& so, UnorderedMap<UINT32, GameObjectInstanceDataPtr>& linkedInstanceData)
  179. {
  180. Stack<HSceneObject> todo;
  181. todo.push(so);
  182. while (!todo.empty())
  183. {
  184. HSceneObject current = todo.top();
  185. todo.pop();
  186. if (current->getLinkId() != -1)
  187. {
  188. auto iterFind = linkedInstanceData.find(current->getLinkId());
  189. if (iterFind != linkedInstanceData.end())
  190. current->_setInstanceData(iterFind->second);
  191. }
  192. const Vector<HComponent>& components = current->getComponents();
  193. for (auto& component : components)
  194. {
  195. if (component->getLinkId() != -1)
  196. {
  197. auto iterFind = linkedInstanceData.find(component->getLinkId());
  198. if (iterFind != linkedInstanceData.end())
  199. component->_setInstanceData(iterFind->second);
  200. }
  201. }
  202. UINT32 numChildren = current->getNumChildren();
  203. for (UINT32 i = 0; i < numChildren; i++)
  204. {
  205. HSceneObject child = current->getChild(i);
  206. if (child->mPrefabLinkUUID.empty())
  207. todo.push(child);
  208. }
  209. }
  210. }
  211. void PrefabUtility::restoreUnlinkedInstanceData(const HSceneObject& so, SceneObjectProxy& proxy)
  212. {
  213. struct StackEntry
  214. {
  215. HSceneObject so;
  216. SceneObjectProxy* proxy;
  217. };
  218. Stack<StackEntry> todo;
  219. todo.push(StackEntry());
  220. assert(so->getLinkId() == proxy.linkId);
  221. StackEntry& topEntry = todo.top();
  222. topEntry.so = so;
  223. topEntry.proxy = &proxy;
  224. while (!todo.empty())
  225. {
  226. StackEntry current = todo.top();
  227. todo.pop();
  228. if (current.proxy->linkId == -1)
  229. current.so->_setInstanceData(current.proxy->instanceData);
  230. const Vector<HComponent>& components = current.so->getComponents();
  231. UINT32 componentProxyIdx = 0;
  232. UINT32 numComponentProxies = (UINT32)current.proxy->components.size();
  233. for (auto& component : components)
  234. {
  235. if (component->getLinkId() == -1)
  236. {
  237. bool foundInstanceData = false;
  238. for (; componentProxyIdx < numComponentProxies; componentProxyIdx++)
  239. {
  240. if (current.proxy->components[componentProxyIdx].linkId != -1)
  241. continue;
  242. assert(current.proxy->components[componentProxyIdx].linkId == -1);
  243. component->_setInstanceData(current.proxy->components[componentProxyIdx].instanceData);
  244. foundInstanceData = true;
  245. break;
  246. }
  247. assert(foundInstanceData);
  248. }
  249. }
  250. UINT32 numChildren = current.so->getNumChildren();
  251. UINT32 childProxyIdx = 0;
  252. UINT32 numChildProxies = (UINT32)current.proxy->children.size();
  253. for (UINT32 i = 0; i < numChildren; i++)
  254. {
  255. HSceneObject child = current.so->getChild(i);
  256. if (child->getLinkId() == -1)
  257. {
  258. bool foundInstanceData = false;
  259. for (; childProxyIdx < numChildProxies; childProxyIdx++)
  260. {
  261. if (current.proxy->children[childProxyIdx].linkId != -1)
  262. continue;
  263. assert(current.proxy->children[childProxyIdx].linkId == -1);
  264. child->_setInstanceData(current.proxy->children[childProxyIdx].instanceData);
  265. todo.push(StackEntry());
  266. StackEntry& newEntry = todo.top();
  267. newEntry.so = child;
  268. newEntry.proxy = &current.proxy->children[childProxyIdx];
  269. foundInstanceData = true;
  270. break;
  271. }
  272. assert(foundInstanceData);
  273. }
  274. else
  275. {
  276. for (UINT32 j = 0; j < numChildProxies; j++)
  277. {
  278. if (child->getLinkId() == current.proxy->children[j].linkId)
  279. {
  280. todo.push(StackEntry());
  281. StackEntry& newEntry = todo.top();
  282. newEntry.so = child;
  283. newEntry.proxy = &current.proxy->children[j];
  284. break;
  285. }
  286. }
  287. }
  288. }
  289. }
  290. }
  291. }