BsPrefabUtility.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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. HSceneObject topLevelObject = so;
  25. while (topLevelObject != nullptr)
  26. {
  27. if (!topLevelObject->mPrefabLinkUUID.empty())
  28. break;
  29. if (topLevelObject->mParent != nullptr)
  30. topLevelObject = topLevelObject->mParent;
  31. else
  32. topLevelObject = nullptr;
  33. }
  34. Stack<HSceneObject> todo;
  35. todo.push(topLevelObject);
  36. Vector<HSceneObject> prefabInstanceRoots;
  37. while (!todo.empty())
  38. {
  39. HSceneObject current = todo.top();
  40. todo.pop();
  41. if (!current->mPrefabLinkUUID.empty())
  42. prefabInstanceRoots.push_back(current);
  43. UINT32 childCount = current->getNumChildren();
  44. for (UINT32 i = 0; i < childCount; i++)
  45. {
  46. HSceneObject child = current->getChild(i);
  47. todo.push(child);
  48. }
  49. }
  50. // Need to do this bottom up to ensure I don't destroy the parents before children
  51. for (auto iter = prefabInstanceRoots.rbegin(); iter != prefabInstanceRoots.rend(); ++iter)
  52. {
  53. HSceneObject current = *iter;
  54. HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->mPrefabLinkUUID, false, false));
  55. if (prefabLink != nullptr && prefabLink->getHash() != current->mPrefabHash)
  56. {
  57. // Save IDs, destroy original, create new, apply diff, restore IDs
  58. SceneObjectProxy soProxy;
  59. UnorderedMap<UINT32, GameObjectInstanceDataPtr> linkedInstanceData;
  60. recordInstanceData(current, soProxy, linkedInstanceData);
  61. PrefabDiffPtr prefabDiff = current->mPrefabDiff;
  62. current->destroy();
  63. HSceneObject newInstance = prefabLink->instantiate();
  64. if (prefabDiff != nullptr)
  65. prefabDiff->apply(newInstance);
  66. restoreLinkedInstanceData(newInstance, linkedInstanceData);
  67. restoreUnlinkedInstanceData(newInstance, soProxy);
  68. }
  69. }
  70. gResources().unloadAllUnused();
  71. }
  72. void PrefabUtility::generatePrefabIds(const HSceneObject& sceneObject)
  73. {
  74. Vector<HGameObject> objectsToId;
  75. Set<INT32> existingIds;
  76. Stack<HSceneObject> todo;
  77. todo.push(sceneObject);
  78. while (!todo.empty())
  79. {
  80. HSceneObject currentSO = todo.top();
  81. todo.pop();
  82. if (currentSO->mLinkId == -1)
  83. objectsToId.push_back(currentSO);
  84. else
  85. existingIds.insert(currentSO->mLinkId);
  86. for (auto& component : currentSO->mComponents)
  87. {
  88. if (component->mLinkId == -1)
  89. objectsToId.push_back(component);
  90. else
  91. existingIds.insert(component->mLinkId);
  92. }
  93. UINT32 numChildren = (UINT32)currentSO->getNumChildren();
  94. for (UINT32 i = 0; i < numChildren; i++)
  95. {
  96. HSceneObject child = currentSO->getChild(i);
  97. if (child->mPrefabLinkUUID.empty())
  98. todo.push(currentSO->getChild(i));
  99. }
  100. }
  101. auto setIter = existingIds.begin();
  102. INT32 nextId = 0;
  103. for (auto& object : objectsToId)
  104. {
  105. INT32 freeId = -1;
  106. for (; setIter != existingIds.end(); ++setIter)
  107. {
  108. if (nextId < (*setIter))
  109. freeId = nextId++;
  110. else
  111. nextId++;
  112. }
  113. if (freeId == -1)
  114. freeId = nextId++;
  115. object->mLinkId = freeId;
  116. }
  117. }
  118. void PrefabUtility::clearPrefabIds(const HSceneObject& sceneObject, bool recursive)
  119. {
  120. Stack<HSceneObject> todo;
  121. todo.push(sceneObject);
  122. while (!todo.empty())
  123. {
  124. HSceneObject currentSO = todo.top();
  125. todo.pop();
  126. currentSO->mLinkId = -1;
  127. for (auto& component : currentSO->mComponents)
  128. component->mLinkId = -1;
  129. if (recursive)
  130. {
  131. UINT32 numChildren = (UINT32)currentSO->getNumChildren();
  132. for (UINT32 i = 0; i < numChildren; i++)
  133. {
  134. HSceneObject child = currentSO->getChild(i);
  135. if (child->mPrefabLinkUUID.empty())
  136. todo.push(child);
  137. }
  138. }
  139. }
  140. }
  141. void PrefabUtility::recordPrefabDiff(const HSceneObject& sceneObject)
  142. {
  143. HSceneObject topLevelObject = sceneObject;
  144. while (topLevelObject != nullptr)
  145. {
  146. if (!topLevelObject->mPrefabLinkUUID.empty())
  147. break;
  148. if (topLevelObject->mParent != nullptr)
  149. topLevelObject = topLevelObject->mParent;
  150. else
  151. topLevelObject = nullptr;
  152. }
  153. Stack<HSceneObject> todo;
  154. todo.push(topLevelObject);
  155. while (!todo.empty())
  156. {
  157. HSceneObject current = todo.top();
  158. todo.pop();
  159. if (!current->mPrefabLinkUUID.empty())
  160. {
  161. current->mPrefabDiff = nullptr;
  162. HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->mPrefabLinkUUID, false, false));
  163. if (prefabLink != nullptr)
  164. current->mPrefabDiff = PrefabDiff::create(prefabLink->_getRoot(), current->getHandle());
  165. }
  166. UINT32 childCount = current->getNumChildren();
  167. for (UINT32 i = 0; i < childCount; i++)
  168. {
  169. HSceneObject child = current->getChild(i);
  170. todo.push(child);
  171. }
  172. }
  173. gResources().unloadAllUnused();
  174. }
  175. void PrefabUtility::recordInstanceData(const HSceneObject& so, SceneObjectProxy& output,
  176. UnorderedMap<UINT32, GameObjectInstanceDataPtr>& linkedInstanceData)
  177. {
  178. Stack<HSceneObject> todo;
  179. todo.push(so);
  180. while (!todo.empty())
  181. {
  182. HSceneObject current = todo.top();
  183. todo.pop();
  184. output.instanceData = current->_getInstanceData();
  185. output.linkId = current->getLinkId();
  186. linkedInstanceData[output.linkId] = output.instanceData;
  187. const Vector<HComponent>& components = current->getComponents();
  188. for (auto& component : components)
  189. {
  190. output.components.push_back(ComponentProxy());
  191. ComponentProxy& componentProxy = output.components.back();
  192. componentProxy.instanceData = component->_getInstanceData();
  193. componentProxy.linkId = component->getLinkId();
  194. linkedInstanceData[componentProxy.linkId] = componentProxy.instanceData;
  195. }
  196. UINT32 numChildren = current->getNumChildren();
  197. for (UINT32 i = 0; i < numChildren; i++)
  198. {
  199. HSceneObject child = current->getChild(i);
  200. if (child->mPrefabLinkUUID.empty())
  201. todo.push(child);
  202. }
  203. }
  204. }
  205. void PrefabUtility::restoreLinkedInstanceData(const HSceneObject& so, UnorderedMap<UINT32, GameObjectInstanceDataPtr>& linkedInstanceData)
  206. {
  207. Stack<HSceneObject> todo;
  208. todo.push(so);
  209. while (!todo.empty())
  210. {
  211. HSceneObject current = todo.top();
  212. todo.pop();
  213. if (current->getLinkId() != -1)
  214. {
  215. auto iterFind = linkedInstanceData.find(current->getLinkId());
  216. if (iterFind != linkedInstanceData.end())
  217. current->_setInstanceData(iterFind->second);
  218. }
  219. const Vector<HComponent>& components = current->getComponents();
  220. for (auto& component : components)
  221. {
  222. if (component->getLinkId() != -1)
  223. {
  224. auto iterFind = linkedInstanceData.find(component->getLinkId());
  225. if (iterFind != linkedInstanceData.end())
  226. component->_setInstanceData(iterFind->second);
  227. }
  228. }
  229. UINT32 numChildren = current->getNumChildren();
  230. for (UINT32 i = 0; i < numChildren; i++)
  231. {
  232. HSceneObject child = current->getChild(i);
  233. if (child->mPrefabLinkUUID.empty())
  234. todo.push(child);
  235. }
  236. }
  237. }
  238. void PrefabUtility::restoreUnlinkedInstanceData(const HSceneObject& so, SceneObjectProxy& proxy)
  239. {
  240. struct StackEntry
  241. {
  242. HSceneObject so;
  243. SceneObjectProxy* proxy;
  244. };
  245. Stack<StackEntry> todo;
  246. todo.push(StackEntry());
  247. assert(so->getLinkId() == proxy.linkId);
  248. StackEntry& topEntry = todo.top();
  249. topEntry.so = so;
  250. topEntry.proxy = &proxy;
  251. while (!todo.empty())
  252. {
  253. StackEntry current = todo.top();
  254. todo.pop();
  255. if (current.proxy->linkId == -1)
  256. current.so->_setInstanceData(current.proxy->instanceData);
  257. const Vector<HComponent>& components = current.so->getComponents();
  258. UINT32 componentProxyIdx = 0;
  259. UINT32 numComponentProxies = (UINT32)current.proxy->components.size();
  260. for (auto& component : components)
  261. {
  262. if (component->getLinkId() == -1)
  263. {
  264. bool foundInstanceData = false;
  265. for (; componentProxyIdx < numComponentProxies; componentProxyIdx++)
  266. {
  267. if (current.proxy->components[componentProxyIdx].linkId != -1)
  268. continue;
  269. assert(current.proxy->components[componentProxyIdx].linkId == -1);
  270. component->_setInstanceData(current.proxy->components[componentProxyIdx].instanceData);
  271. foundInstanceData = true;
  272. break;
  273. }
  274. assert(foundInstanceData);
  275. }
  276. }
  277. UINT32 numChildren = current.so->getNumChildren();
  278. UINT32 childProxyIdx = 0;
  279. UINT32 numChildProxies = (UINT32)current.proxy->children.size();
  280. for (UINT32 i = 0; i < numChildren; i++)
  281. {
  282. HSceneObject child = current.so->getChild(i);
  283. if (!child->mPrefabLinkUUID.empty())
  284. continue;
  285. if (child->getLinkId() == -1)
  286. {
  287. bool foundInstanceData = false;
  288. for (; childProxyIdx < numChildProxies; childProxyIdx++)
  289. {
  290. if (current.proxy->children[childProxyIdx].linkId != -1)
  291. continue;
  292. assert(current.proxy->children[childProxyIdx].linkId == -1);
  293. child->_setInstanceData(current.proxy->children[childProxyIdx].instanceData);
  294. todo.push(StackEntry());
  295. StackEntry& newEntry = todo.top();
  296. newEntry.so = child;
  297. newEntry.proxy = &current.proxy->children[childProxyIdx];
  298. foundInstanceData = true;
  299. break;
  300. }
  301. assert(foundInstanceData);
  302. }
  303. else
  304. {
  305. for (UINT32 j = 0; j < numChildProxies; j++)
  306. {
  307. if (child->getLinkId() == current.proxy->children[j].linkId)
  308. {
  309. todo.push(StackEntry());
  310. StackEntry& newEntry = todo.top();
  311. newEntry.so = child;
  312. newEntry.proxy = &current.proxy->children[j];
  313. break;
  314. }
  315. }
  316. }
  317. }
  318. }
  319. }
  320. }