BsPrefabDiff.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Scene/BsPrefabDiff.h"
  4. #include "RTTI/BsPrefabDiffRTTI.h"
  5. #include "Scene/BsSceneObject.h"
  6. #include "Serialization/BsMemorySerializer.h"
  7. #include "Serialization/BsBinarySerializer.h"
  8. #include "Serialization/BsBinaryDiff.h"
  9. #include "Scene/BsSceneManager.h"
  10. namespace bs
  11. {
  12. RTTITypeBase* PrefabComponentDiff::getRTTIStatic()
  13. {
  14. return PrefabComponentDiffRTTI::instance();
  15. }
  16. RTTITypeBase* PrefabComponentDiff::getRTTI() const
  17. {
  18. return PrefabComponentDiff::getRTTIStatic();
  19. }
  20. RTTITypeBase* PrefabObjectDiff::getRTTIStatic()
  21. {
  22. return PrefabObjectDiffRTTI::instance();
  23. }
  24. RTTITypeBase* PrefabObjectDiff::getRTTI() const
  25. {
  26. return PrefabObjectDiff::getRTTIStatic();
  27. }
  28. SPtr<PrefabDiff> PrefabDiff::create(const HSceneObject& prefab, const HSceneObject& instance)
  29. {
  30. if (prefab->mPrefabLinkUUID != instance->mPrefabLinkUUID)
  31. return nullptr;
  32. // Note: If this method is called multiple times in a row then renaming all objects every time is redundant, it
  33. // would be more efficient to do it once outside of this method. I'm keeping it this way for simplicity for now.
  34. // Rename instance objects so they share the same IDs as the prefab objects (if they link IDs match). This allows
  35. // game object handle diff to work properly, because otherwise handles that point to same objects would be
  36. // marked as different because the instance IDs of the two objects don't match (since one is in prefab and one
  37. // in instance).
  38. Vector<RenamedGameObject> renamedObjects;
  39. renameInstanceIds(prefab, instance, renamedObjects);
  40. SPtr<PrefabDiff> output = bs_shared_ptr_new<PrefabDiff>();
  41. output->mRoot = generateDiff(prefab, instance);
  42. restoreInstanceIds(renamedObjects);
  43. return output;
  44. }
  45. void PrefabDiff::apply(const HSceneObject& object)
  46. {
  47. if (mRoot == nullptr)
  48. return;
  49. GameObjectManager::instance().startDeserialization();
  50. applyDiff(mRoot, object);
  51. GameObjectManager::instance().endDeserialization();
  52. }
  53. void PrefabDiff::applyDiff(const SPtr<PrefabObjectDiff>& diff, const HSceneObject& object)
  54. {
  55. if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Name) != 0)
  56. object->setName(diff->name);
  57. if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Position) != 0)
  58. object->setPosition(diff->position);
  59. if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Rotation) != 0)
  60. object->setRotation(diff->rotation);
  61. if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Scale) != 0)
  62. object->setScale(diff->scale);
  63. if ((diff->soFlags & (UINT32)SceneObjectDiffFlags::Active) != 0)
  64. object->setActive(diff->isActive);
  65. // Note: It is important to remove objects and components first, before adding them.
  66. // Some systems rely on the fact that applyDiff added components/objects are
  67. // always at the end.
  68. const Vector<HComponent>& components = object->getComponents();
  69. for (auto& removedId : diff->removedComponents)
  70. {
  71. for (auto component : components)
  72. {
  73. if (removedId == component->getLinkId())
  74. {
  75. component->destroy();
  76. break;
  77. }
  78. }
  79. }
  80. for (auto& removedId : diff->removedChildren)
  81. {
  82. UINT32 childCount = object->getNumChildren();
  83. for (UINT32 i = 0; i < childCount; i++)
  84. {
  85. HSceneObject child = object->getChild(i);
  86. if (removedId == child->getLinkId())
  87. {
  88. child->destroy();
  89. break;
  90. }
  91. }
  92. }
  93. for (auto& addedComponentData : diff->addedComponents)
  94. {
  95. BinarySerializer bs;
  96. SPtr<Component> component = std::static_pointer_cast<Component>(bs._decodeFromIntermediate(addedComponentData));
  97. object->addAndInitializeComponent(component);
  98. }
  99. for (auto& addedChildData : diff->addedChildren)
  100. {
  101. BinarySerializer bs;
  102. SPtr<SceneObject> sceneObject = std::static_pointer_cast<SceneObject>(bs._decodeFromIntermediate(addedChildData));
  103. sceneObject->setParent(object);
  104. if(object->isInstantiated())
  105. sceneObject->_instantiate();
  106. }
  107. for (auto& componentDiff : diff->componentDiffs)
  108. {
  109. for (auto& component : components)
  110. {
  111. if (componentDiff->id == (INT32)component->getLinkId())
  112. {
  113. IDiff& diffHandler = component->getRTTI()->getDiffHandler();
  114. diffHandler.applyDiff(component.getInternalPtr(), componentDiff->data);
  115. break;
  116. }
  117. }
  118. }
  119. for (auto& childDiff : diff->childDiffs)
  120. {
  121. UINT32 childCount = object->getNumChildren();
  122. for (UINT32 i = 0; i < childCount; i++)
  123. {
  124. HSceneObject child = object->getChild(i);
  125. if (childDiff->id == child->getLinkId())
  126. {
  127. applyDiff(childDiff, child);
  128. break;
  129. }
  130. }
  131. }
  132. }
  133. SPtr<PrefabObjectDiff> PrefabDiff::generateDiff(const HSceneObject& prefab, const HSceneObject& instance)
  134. {
  135. SPtr<PrefabObjectDiff> output;
  136. if (prefab->getName() != instance->getName())
  137. {
  138. if (output == nullptr)
  139. output = bs_shared_ptr_new<PrefabObjectDiff>();
  140. output->name = instance->getName();
  141. output->soFlags |= (UINT32)SceneObjectDiffFlags::Name;
  142. }
  143. if (prefab->getPosition() != instance->getPosition())
  144. {
  145. if (output == nullptr)
  146. output = bs_shared_ptr_new<PrefabObjectDiff>();
  147. output->position = instance->getPosition();
  148. output->soFlags |= (UINT32)SceneObjectDiffFlags::Position;
  149. }
  150. if (prefab->getRotation() != instance->getRotation())
  151. {
  152. if (output == nullptr)
  153. output = bs_shared_ptr_new<PrefabObjectDiff>();
  154. output->rotation = instance->getRotation();
  155. output->soFlags |= (UINT32)SceneObjectDiffFlags::Rotation;
  156. }
  157. if (prefab->getScale() != instance->getScale())
  158. {
  159. if (output == nullptr)
  160. output = bs_shared_ptr_new<PrefabObjectDiff>();
  161. output->scale = instance->getScale();
  162. output->soFlags |= (UINT32)SceneObjectDiffFlags::Scale;
  163. }
  164. if (prefab->getActive() != instance->getActive())
  165. {
  166. if (output == nullptr)
  167. output = bs_shared_ptr_new<PrefabObjectDiff>();
  168. output->isActive = instance->getActive();
  169. output->soFlags |= (UINT32)SceneObjectDiffFlags::Active;
  170. }
  171. UINT32 prefabChildCount = prefab->getNumChildren();
  172. UINT32 instanceChildCount = instance->getNumChildren();
  173. // Find modified and removed children
  174. for (UINT32 i = 0; i < prefabChildCount; i++)
  175. {
  176. HSceneObject prefabChild = prefab->getChild(i);
  177. SPtr<PrefabObjectDiff> childDiff;
  178. bool foundMatching = false;
  179. for (UINT32 j = 0; j < instanceChildCount; j++)
  180. {
  181. HSceneObject instanceChild = instance->getChild(j);
  182. if (prefabChild->getLinkId() == instanceChild->getLinkId())
  183. {
  184. if (instanceChild->mPrefabLinkUUID.empty())
  185. childDiff = generateDiff(prefabChild, instanceChild);
  186. foundMatching = true;
  187. break;
  188. }
  189. }
  190. if (foundMatching)
  191. {
  192. if (childDiff != nullptr)
  193. {
  194. if (output == nullptr)
  195. output = bs_shared_ptr_new<PrefabObjectDiff>();
  196. output->childDiffs.push_back(childDiff);
  197. }
  198. }
  199. else
  200. {
  201. if (output == nullptr)
  202. output = bs_shared_ptr_new<PrefabObjectDiff>();
  203. output->removedChildren.push_back(prefabChild->getLinkId());
  204. }
  205. }
  206. // Find added children
  207. for (UINT32 i = 0; i < instanceChildCount; i++)
  208. {
  209. HSceneObject instanceChild = instance->getChild(i);
  210. if (instanceChild->hasFlag(SOF_DontSave))
  211. continue;
  212. bool foundMatching = false;
  213. if (instanceChild->getLinkId() != (UINT32)-1)
  214. {
  215. for (UINT32 j = 0; j < prefabChildCount; j++)
  216. {
  217. HSceneObject prefabChild = prefab->getChild(j);
  218. if (prefabChild->getLinkId() == instanceChild->getLinkId())
  219. {
  220. foundMatching = true;
  221. break;
  222. }
  223. }
  224. }
  225. if (!foundMatching)
  226. {
  227. BinarySerializer bs;
  228. SPtr<SerializedObject> obj = bs._encodeToIntermediate(instanceChild.get());
  229. if (output == nullptr)
  230. output = bs_shared_ptr_new<PrefabObjectDiff>();
  231. output->addedChildren.push_back(obj);
  232. }
  233. }
  234. const Vector<HComponent>& prefabComponents = prefab->getComponents();
  235. const Vector<HComponent>& instanceComponents = instance->getComponents();
  236. UINT32 prefabComponentCount = (UINT32)prefabComponents.size();
  237. UINT32 instanceComponentCount = (UINT32)instanceComponents.size();
  238. // Find modified and removed components
  239. for (UINT32 i = 0; i < prefabComponentCount; i++)
  240. {
  241. HComponent prefabComponent = prefabComponents[i];
  242. SPtr<PrefabComponentDiff> childDiff;
  243. bool foundMatching = false;
  244. for (UINT32 j = 0; j < instanceComponentCount; j++)
  245. {
  246. HComponent instanceComponent = instanceComponents[j];
  247. if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
  248. {
  249. BinarySerializer bs;
  250. SPtr<SerializedObject> encodedPrefab = bs._encodeToIntermediate(prefabComponent.get());
  251. SPtr<SerializedObject> encodedInstance = bs._encodeToIntermediate(instanceComponent.get());
  252. IDiff& diffHandler = prefabComponent->getRTTI()->getDiffHandler();
  253. SPtr<SerializedObject> diff = diffHandler.generateDiff(encodedPrefab, encodedInstance);
  254. if (diff != nullptr)
  255. {
  256. childDiff = bs_shared_ptr_new<PrefabComponentDiff>();
  257. childDiff->id = prefabComponent->getLinkId();
  258. childDiff->data = diff;
  259. }
  260. foundMatching = true;
  261. break;
  262. }
  263. }
  264. if (foundMatching)
  265. {
  266. if (childDiff != nullptr)
  267. {
  268. if (output == nullptr)
  269. output = bs_shared_ptr_new<PrefabObjectDiff>();
  270. output->componentDiffs.push_back(childDiff);
  271. }
  272. }
  273. else
  274. {
  275. if (output == nullptr)
  276. output = bs_shared_ptr_new<PrefabObjectDiff>();
  277. output->removedComponents.push_back(prefabComponent->getLinkId());
  278. }
  279. }
  280. // Find added components
  281. for (UINT32 i = 0; i < instanceComponentCount; i++)
  282. {
  283. HComponent instanceComponent = instanceComponents[i];
  284. bool foundMatching = false;
  285. if (instanceComponent->getLinkId() != (UINT32)-1)
  286. {
  287. for (UINT32 j = 0; j < prefabComponentCount; j++)
  288. {
  289. HComponent prefabComponent = prefabComponents[j];
  290. if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
  291. {
  292. foundMatching = true;
  293. break;
  294. }
  295. }
  296. }
  297. if (!foundMatching)
  298. {
  299. BinarySerializer bs;
  300. SPtr<SerializedObject> obj = bs._encodeToIntermediate(instanceComponent.get());
  301. if (output == nullptr)
  302. output = bs_shared_ptr_new<PrefabObjectDiff>();
  303. output->addedComponents.push_back(obj);
  304. }
  305. }
  306. if (output != nullptr)
  307. output->id = instance->getLinkId();
  308. return output;
  309. }
  310. void PrefabDiff::renameInstanceIds(const HSceneObject& prefab, const HSceneObject& instance, Vector<RenamedGameObject>& output)
  311. {
  312. UnorderedMap<String, UnorderedMap<UINT32, UINT64>> linkToInstanceId;
  313. struct StackEntry
  314. {
  315. HSceneObject so;
  316. String uuid;
  317. };
  318. // When renaming it is important to rename the prefab and not the instance, since the diff will otherwise
  319. // contain prefab's IDs, but will be used for the instance.
  320. Stack<StackEntry> todo;
  321. todo.push({ instance, "root" });
  322. while (!todo.empty())
  323. {
  324. StackEntry current = todo.top();
  325. todo.pop();
  326. String childParentUUID;
  327. if (current.so->mPrefabLinkUUID.empty())
  328. childParentUUID = current.uuid;
  329. else
  330. childParentUUID = current.so->mPrefabLinkUUID;
  331. UnorderedMap<UINT32, UINT64>& idMap = linkToInstanceId[childParentUUID];
  332. const Vector<HComponent>& components = current.so->getComponents();
  333. for (auto& component : components)
  334. {
  335. if (component->getLinkId() != (UINT32)-1)
  336. idMap[component->getLinkId()] = component->getInstanceId();
  337. }
  338. UINT32 numChildren = current.so->getNumChildren();
  339. for (UINT32 i = 0; i < numChildren; i++)
  340. {
  341. HSceneObject child = current.so->getChild(i);
  342. if (child->getLinkId() != (UINT32)-1)
  343. idMap[child->getLinkId()] = child->getInstanceId();
  344. todo.push({ child, childParentUUID });
  345. }
  346. }
  347. // Root has link ID from its parent so we handle it separately
  348. {
  349. output.push_back(RenamedGameObject());
  350. RenamedGameObject& renamedGO = output.back();
  351. renamedGO.instanceData = instance->mInstanceData;
  352. renamedGO.originalId = instance->getInstanceId();
  353. prefab->mInstanceData->mInstanceId = instance->getInstanceId();
  354. }
  355. todo.push({ prefab, "root" });
  356. while (!todo.empty())
  357. {
  358. StackEntry current = todo.top();
  359. todo.pop();
  360. String childParentUUID;
  361. if (current.so->mPrefabLinkUUID.empty())
  362. childParentUUID = current.uuid;
  363. else
  364. childParentUUID = current.so->mPrefabLinkUUID;
  365. auto iterFind = linkToInstanceId.find(childParentUUID);
  366. if (iterFind != linkToInstanceId.end())
  367. {
  368. UnorderedMap<UINT32, UINT64>& idMap = iterFind->second;
  369. const Vector<HComponent>& components = current.so->getComponents();
  370. for (auto& component : components)
  371. {
  372. auto iterFind2 = idMap.find(component->getLinkId());
  373. if (iterFind2 != idMap.end())
  374. {
  375. output.push_back(RenamedGameObject());
  376. RenamedGameObject& renamedGO = output.back();
  377. renamedGO.instanceData = component->mInstanceData;
  378. renamedGO.originalId = component->getInstanceId();
  379. component->mInstanceData->mInstanceId = iterFind2->second;
  380. }
  381. }
  382. }
  383. UINT32 numChildren = current.so->getNumChildren();
  384. for (UINT32 i = 0; i < numChildren; i++)
  385. {
  386. HSceneObject child = current.so->getChild(i);
  387. if (iterFind != linkToInstanceId.end())
  388. {
  389. if (child->getLinkId() != (UINT32)-1)
  390. {
  391. UnorderedMap<UINT32, UINT64>& idMap = iterFind->second;
  392. auto iterFind2 = idMap.find(child->getLinkId());
  393. if (iterFind2 != idMap.end())
  394. {
  395. output.push_back(RenamedGameObject());
  396. RenamedGameObject& renamedGO = output.back();
  397. renamedGO.instanceData = child->mInstanceData;
  398. renamedGO.originalId = child->getInstanceId();
  399. child->mInstanceData->mInstanceId = iterFind2->second;
  400. }
  401. }
  402. }
  403. todo.push({ child, childParentUUID });
  404. }
  405. }
  406. }
  407. void PrefabDiff::restoreInstanceIds(const Vector<RenamedGameObject>& renamedObjects)
  408. {
  409. for (auto& renamedGO : renamedObjects)
  410. renamedGO.instanceData->mInstanceId = renamedGO.originalId;
  411. }
  412. RTTITypeBase* PrefabDiff::getRTTIStatic()
  413. {
  414. return PrefabDiffRTTI::instance();
  415. }
  416. RTTITypeBase* PrefabDiff::getRTTI() const
  417. {
  418. return PrefabDiff::getRTTIStatic();
  419. }
  420. }