BsPrefabDiff.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  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. const Transform& prefabTfrm = prefab->getLocalTransform();
  144. const Transform& instanceTfrm = instance->getLocalTransform();
  145. if (prefabTfrm.getPosition() != instanceTfrm.getPosition())
  146. {
  147. if (output == nullptr)
  148. output = bs_shared_ptr_new<PrefabObjectDiff>();
  149. output->position = instanceTfrm.getPosition();
  150. output->soFlags |= (UINT32)SceneObjectDiffFlags::Position;
  151. }
  152. if (prefabTfrm.getRotation() != instanceTfrm.getRotation())
  153. {
  154. if (output == nullptr)
  155. output = bs_shared_ptr_new<PrefabObjectDiff>();
  156. output->rotation = instanceTfrm.getRotation();
  157. output->soFlags |= (UINT32)SceneObjectDiffFlags::Rotation;
  158. }
  159. if (prefabTfrm.getScale() != instanceTfrm.getScale())
  160. {
  161. if (output == nullptr)
  162. output = bs_shared_ptr_new<PrefabObjectDiff>();
  163. output->scale = instanceTfrm.getScale();
  164. output->soFlags |= (UINT32)SceneObjectDiffFlags::Scale;
  165. }
  166. if (prefab->getActive() != instance->getActive())
  167. {
  168. if (output == nullptr)
  169. output = bs_shared_ptr_new<PrefabObjectDiff>();
  170. output->isActive = instance->getActive();
  171. output->soFlags |= (UINT32)SceneObjectDiffFlags::Active;
  172. }
  173. UINT32 prefabChildCount = prefab->getNumChildren();
  174. UINT32 instanceChildCount = instance->getNumChildren();
  175. // Find modified and removed children
  176. for (UINT32 i = 0; i < prefabChildCount; i++)
  177. {
  178. HSceneObject prefabChild = prefab->getChild(i);
  179. SPtr<PrefabObjectDiff> childDiff;
  180. bool foundMatching = false;
  181. for (UINT32 j = 0; j < instanceChildCount; j++)
  182. {
  183. HSceneObject instanceChild = instance->getChild(j);
  184. if (prefabChild->getLinkId() == instanceChild->getLinkId())
  185. {
  186. if (instanceChild->mPrefabLinkUUID.empty())
  187. childDiff = generateDiff(prefabChild, instanceChild);
  188. foundMatching = true;
  189. break;
  190. }
  191. }
  192. if (foundMatching)
  193. {
  194. if (childDiff != nullptr)
  195. {
  196. if (output == nullptr)
  197. output = bs_shared_ptr_new<PrefabObjectDiff>();
  198. output->childDiffs.push_back(childDiff);
  199. }
  200. }
  201. else
  202. {
  203. if (output == nullptr)
  204. output = bs_shared_ptr_new<PrefabObjectDiff>();
  205. output->removedChildren.push_back(prefabChild->getLinkId());
  206. }
  207. }
  208. // Find added children
  209. for (UINT32 i = 0; i < instanceChildCount; i++)
  210. {
  211. HSceneObject instanceChild = instance->getChild(i);
  212. if (instanceChild->hasFlag(SOF_DontSave))
  213. continue;
  214. bool foundMatching = false;
  215. if (instanceChild->getLinkId() != (UINT32)-1)
  216. {
  217. for (UINT32 j = 0; j < prefabChildCount; j++)
  218. {
  219. HSceneObject prefabChild = prefab->getChild(j);
  220. if (prefabChild->getLinkId() == instanceChild->getLinkId())
  221. {
  222. foundMatching = true;
  223. break;
  224. }
  225. }
  226. }
  227. if (!foundMatching)
  228. {
  229. BinarySerializer bs;
  230. SPtr<SerializedObject> obj = bs._encodeToIntermediate(instanceChild.get());
  231. if (output == nullptr)
  232. output = bs_shared_ptr_new<PrefabObjectDiff>();
  233. output->addedChildren.push_back(obj);
  234. }
  235. }
  236. const Vector<HComponent>& prefabComponents = prefab->getComponents();
  237. const Vector<HComponent>& instanceComponents = instance->getComponents();
  238. UINT32 prefabComponentCount = (UINT32)prefabComponents.size();
  239. UINT32 instanceComponentCount = (UINT32)instanceComponents.size();
  240. // Find modified and removed components
  241. for (UINT32 i = 0; i < prefabComponentCount; i++)
  242. {
  243. HComponent prefabComponent = prefabComponents[i];
  244. SPtr<PrefabComponentDiff> childDiff;
  245. bool foundMatching = false;
  246. for (UINT32 j = 0; j < instanceComponentCount; j++)
  247. {
  248. HComponent instanceComponent = instanceComponents[j];
  249. if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
  250. {
  251. BinarySerializer bs;
  252. SPtr<SerializedObject> encodedPrefab = bs._encodeToIntermediate(prefabComponent.get());
  253. SPtr<SerializedObject> encodedInstance = bs._encodeToIntermediate(instanceComponent.get());
  254. IDiff& diffHandler = prefabComponent->getRTTI()->getDiffHandler();
  255. SPtr<SerializedObject> diff = diffHandler.generateDiff(encodedPrefab, encodedInstance);
  256. if (diff != nullptr)
  257. {
  258. childDiff = bs_shared_ptr_new<PrefabComponentDiff>();
  259. childDiff->id = prefabComponent->getLinkId();
  260. childDiff->data = diff;
  261. }
  262. foundMatching = true;
  263. break;
  264. }
  265. }
  266. if (foundMatching)
  267. {
  268. if (childDiff != nullptr)
  269. {
  270. if (output == nullptr)
  271. output = bs_shared_ptr_new<PrefabObjectDiff>();
  272. output->componentDiffs.push_back(childDiff);
  273. }
  274. }
  275. else
  276. {
  277. if (output == nullptr)
  278. output = bs_shared_ptr_new<PrefabObjectDiff>();
  279. output->removedComponents.push_back(prefabComponent->getLinkId());
  280. }
  281. }
  282. // Find added components
  283. for (UINT32 i = 0; i < instanceComponentCount; i++)
  284. {
  285. HComponent instanceComponent = instanceComponents[i];
  286. bool foundMatching = false;
  287. if (instanceComponent->getLinkId() != (UINT32)-1)
  288. {
  289. for (UINT32 j = 0; j < prefabComponentCount; j++)
  290. {
  291. HComponent prefabComponent = prefabComponents[j];
  292. if (prefabComponent->getLinkId() == instanceComponent->getLinkId())
  293. {
  294. foundMatching = true;
  295. break;
  296. }
  297. }
  298. }
  299. if (!foundMatching)
  300. {
  301. BinarySerializer bs;
  302. SPtr<SerializedObject> obj = bs._encodeToIntermediate(instanceComponent.get());
  303. if (output == nullptr)
  304. output = bs_shared_ptr_new<PrefabObjectDiff>();
  305. output->addedComponents.push_back(obj);
  306. }
  307. }
  308. if (output != nullptr)
  309. output->id = instance->getLinkId();
  310. return output;
  311. }
  312. void PrefabDiff::renameInstanceIds(const HSceneObject& prefab, const HSceneObject& instance, Vector<RenamedGameObject>& output)
  313. {
  314. UnorderedMap<UUID, UnorderedMap<UINT32, UINT64>> linkToInstanceId;
  315. struct StackEntry
  316. {
  317. HSceneObject so;
  318. UUID uuid;
  319. };
  320. // When renaming it is important to rename the prefab and not the instance, since the diff will otherwise
  321. // contain prefab's IDs, but will be used for the instance.
  322. Stack<StackEntry> todo;
  323. todo.push({ instance, UUID::EMPTY });
  324. while (!todo.empty())
  325. {
  326. StackEntry current = todo.top();
  327. todo.pop();
  328. UUID childParentUUID;
  329. if (current.so->mPrefabLinkUUID.empty())
  330. childParentUUID = current.uuid;
  331. else
  332. childParentUUID = current.so->mPrefabLinkUUID;
  333. UnorderedMap<UINT32, UINT64>& idMap = linkToInstanceId[childParentUUID];
  334. const Vector<HComponent>& components = current.so->getComponents();
  335. for (auto& component : components)
  336. {
  337. if (component->getLinkId() != (UINT32)-1)
  338. idMap[component->getLinkId()] = component->getInstanceId();
  339. }
  340. UINT32 numChildren = current.so->getNumChildren();
  341. for (UINT32 i = 0; i < numChildren; i++)
  342. {
  343. HSceneObject child = current.so->getChild(i);
  344. if (child->getLinkId() != (UINT32)-1)
  345. idMap[child->getLinkId()] = child->getInstanceId();
  346. todo.push({ child, childParentUUID });
  347. }
  348. }
  349. // Root has link ID from its parent so we handle it separately
  350. {
  351. output.push_back(RenamedGameObject());
  352. RenamedGameObject& renamedGO = output.back();
  353. renamedGO.instanceData = instance->mInstanceData;
  354. renamedGO.originalId = instance->getInstanceId();
  355. prefab->mInstanceData->mInstanceId = instance->getInstanceId();
  356. }
  357. todo.push({ prefab, UUID::EMPTY });
  358. while (!todo.empty())
  359. {
  360. StackEntry current = todo.top();
  361. todo.pop();
  362. UUID childParentUUID;
  363. if (current.so->mPrefabLinkUUID.empty())
  364. childParentUUID = current.uuid;
  365. else
  366. childParentUUID = current.so->mPrefabLinkUUID;
  367. auto iterFind = linkToInstanceId.find(childParentUUID);
  368. if (iterFind != linkToInstanceId.end())
  369. {
  370. UnorderedMap<UINT32, UINT64>& idMap = iterFind->second;
  371. const Vector<HComponent>& components = current.so->getComponents();
  372. for (auto& component : components)
  373. {
  374. auto iterFind2 = idMap.find(component->getLinkId());
  375. if (iterFind2 != idMap.end())
  376. {
  377. output.push_back(RenamedGameObject());
  378. RenamedGameObject& renamedGO = output.back();
  379. renamedGO.instanceData = component->mInstanceData;
  380. renamedGO.originalId = component->getInstanceId();
  381. component->mInstanceData->mInstanceId = iterFind2->second;
  382. }
  383. }
  384. }
  385. UINT32 numChildren = current.so->getNumChildren();
  386. for (UINT32 i = 0; i < numChildren; i++)
  387. {
  388. HSceneObject child = current.so->getChild(i);
  389. if (iterFind != linkToInstanceId.end())
  390. {
  391. if (child->getLinkId() != (UINT32)-1)
  392. {
  393. UnorderedMap<UINT32, UINT64>& idMap = iterFind->second;
  394. auto iterFind2 = idMap.find(child->getLinkId());
  395. if (iterFind2 != idMap.end())
  396. {
  397. output.push_back(RenamedGameObject());
  398. RenamedGameObject& renamedGO = output.back();
  399. renamedGO.instanceData = child->mInstanceData;
  400. renamedGO.originalId = child->getInstanceId();
  401. child->mInstanceData->mInstanceId = iterFind2->second;
  402. }
  403. }
  404. }
  405. todo.push({ child, childParentUUID });
  406. }
  407. }
  408. }
  409. void PrefabDiff::restoreInstanceIds(const Vector<RenamedGameObject>& renamedObjects)
  410. {
  411. for (auto& renamedGO : renamedObjects)
  412. renamedGO.instanceData->mInstanceId = renamedGO.originalId;
  413. }
  414. RTTITypeBase* PrefabDiff::getRTTIStatic()
  415. {
  416. return PrefabDiffRTTI::instance();
  417. }
  418. RTTITypeBase* PrefabDiff::getRTTI() const
  419. {
  420. return PrefabDiff::getRTTIStatic();
  421. }
  422. }