1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Scene/BsPrefab.h"
4#include "Private/RTTI/BsPrefabRTTI.h"
5#include "Resources/BsResources.h"
6#include "Scene/BsSceneObject.h"
7#include "Scene/BsPrefabUtility.h"
8#include "BsCoreApplication.h"
9
10namespace bs
11{
12 Prefab::Prefab()
13 :Resource(false)
14 {
15
16 }
17
18 Prefab::~Prefab()
19 {
20 if (mRoot != nullptr)
21 mRoot->destroy(true);
22 }
23
24 HPrefab Prefab::create(const HSceneObject& sceneObject, bool isScene)
25 {
26 SPtr<Prefab> newPrefab = createEmpty();
27 newPrefab->mIsScene = isScene;
28
29 PrefabUtility::clearPrefabIds(sceneObject, true, false);
30 newPrefab->initialize(sceneObject);
31
32 HPrefab handle = static_resource_cast<Prefab>(gResources()._createResourceHandle(newPrefab));
33 newPrefab->mUUID = handle.getUUID();
34 sceneObject->mPrefabLinkUUID = newPrefab->mUUID;
35 newPrefab->_getRoot()->mPrefabLinkUUID = newPrefab->mUUID;
36
37 return handle;
38 }
39
40 SPtr<Prefab> Prefab::createEmpty()
41 {
42 SPtr<Prefab> newPrefab = bs_core_ptr<Prefab>(new (bs_alloc<Prefab>()) Prefab());
43 newPrefab->_setThisPtr(newPrefab);
44
45 return newPrefab;
46 }
47
48 void Prefab::initialize(const HSceneObject& sceneObject)
49 {
50 sceneObject->mPrefabDiff = nullptr;
51 PrefabUtility::generatePrefabIds(sceneObject);
52
53 // If there are any child prefab instances, make sure to update their diffs so they are saved with this prefab
54 Stack<HSceneObject> todo;
55 todo.push(sceneObject);
56
57 while (!todo.empty())
58 {
59 HSceneObject current = todo.top();
60 todo.pop();
61
62 UINT32 childCount = current->getNumChildren();
63 for (UINT32 i = 0; i < childCount; i++)
64 {
65 HSceneObject child = current->getChild(i);
66
67 if (!child->mPrefabLinkUUID.empty())
68 PrefabUtility::recordPrefabDiff(child);
69 else
70 todo.push(child);
71 }
72 }
73
74 // Clone the hierarchy for internal storage
75 if (mRoot != nullptr)
76 mRoot->destroy(true);
77
78 mRoot = sceneObject->clone(false, true);
79 mRoot->mParent = nullptr;
80 mRoot->mLinkId = -1;
81
82 // Remove objects with "dont save" flag
83 todo.push(mRoot);
84
85 while (!todo.empty())
86 {
87 HSceneObject current = todo.top();
88 todo.pop();
89
90 if (current->hasFlag(SOF_DontSave))
91 current->destroy();
92 else
93 {
94 UINT32 numChildren = current->getNumChildren();
95 for (UINT32 i = 0; i < numChildren; i++)
96 todo.push(current->getChild(i));
97 }
98 }
99 }
100
101 void Prefab::update(const HSceneObject& sceneObject)
102 {
103 initialize(sceneObject);
104 sceneObject->mPrefabLinkUUID = mUUID;
105 mRoot->mPrefabLinkUUID = mUUID;
106
107 mHash++;
108 }
109
110 void Prefab::_updateChildInstances() const
111 {
112 Stack<HSceneObject> todo;
113 todo.push(mRoot);
114
115 while (!todo.empty())
116 {
117 HSceneObject current = todo.top();
118 todo.pop();
119
120 UINT32 childCount = current->getNumChildren();
121 for (UINT32 i = 0; i < childCount; i++)
122 {
123 HSceneObject child = current->getChild(i);
124
125 if (!child->mPrefabLinkUUID.empty())
126 PrefabUtility::updateFromPrefab(child);
127 else
128 todo.push(child);
129 }
130 }
131 }
132
133 HSceneObject Prefab::_instantiate(bool preserveUUIDs) const
134 {
135 if (mRoot == nullptr)
136 return HSceneObject();
137
138#if BS_IS_BANSHEE3D
139 if (gCoreApplication().isEditor())
140 {
141 // Update any child prefab instances in case their prefabs changed
142 _updateChildInstances();
143 }
144#endif
145
146 HSceneObject clone = _clone(preserveUUIDs);
147 clone->_instantiate();
148
149 return clone;
150 }
151
152 HSceneObject Prefab::_clone(bool preserveUUIDs) const
153 {
154 if (mRoot == nullptr)
155 return HSceneObject();
156
157 mRoot->mPrefabHash = mHash;
158 mRoot->mLinkId = -1;
159
160 return mRoot->clone(false, preserveUUIDs);
161 }
162
163 RTTITypeBase* Prefab::getRTTIStatic()
164 {
165 return PrefabRTTI::instance();
166 }
167
168 RTTITypeBase* Prefab::getRTTI() const
169 {
170 return Prefab::getRTTIStatic();
171 }
172}