BsPrefabUtility.cpp 8.0 KB

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