MonoObject* ScriptEditorApplication::internal_SaveScene(MonoString* path) { Path nativePath = MonoUtil::monoToString(path); HSceneObject sceneRoot = gSceneManager().getRootNode(); SPtr<ProjectResourceMeta> resMeta = gProjectLibrary().findResourceMeta(nativePath); HPrefab scene; if (resMeta != nullptr) { if (resMeta->getTypeID() != TID_Prefab) return nullptr; scene = static_resource_cast<Prefab>(gProjectLibrary().load(nativePath)); scene->update(sceneRoot); gProjectLibrary().saveEntry(scene); } else { scene = Prefab::create(sceneRoot); gProjectLibrary().createEntry(scene, nativePath); } ScriptResourceBase* scriptPrefab = ScriptResourceManager::instance().getScriptResource(scene, true); return scriptPrefab->getManagedInstance(); }
MonoObject* ScriptPrefab::internal_Instantiate(ScriptPrefab* thisPtr) { HPrefab prefab = thisPtr->getHandle(); HSceneObject instance = prefab->instantiate(); ScriptSceneObject* scriptInstance = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(instance); return scriptInstance->getManagedInstance(); }
MonoObject* ScriptUndoRedo::internal_Instantiate(ScriptPrefab* prefabPtr, MonoString* description) { HPrefab prefab = prefabPtr->getHandle(); if (!prefab.isLoaded()) return nullptr; WString nativeDescription = MonoUtil::monoToWString(description); HSceneObject clone = CmdInstantiateSO::execute(prefab, nativeDescription); ScriptSceneObject* cloneSoPtr = ScriptGameObjectManager::instance().getOrCreateScriptSceneObject(clone); return cloneSoPtr->getManagedInstance(); }
HPrefab Prefab::create(const HSceneObject& sceneObject) { SPtr<Prefab> newPrefab = createEmpty(); newPrefab->initialize(sceneObject); HPrefab handle = static_resource_cast<Prefab>(gResources()._createResourceHandle(newPrefab)); newPrefab->mUUID = handle.getUUID(); sceneObject->mPrefabLinkUUID = newPrefab->mUUID; newPrefab->_getRoot()->mPrefabLinkUUID = newPrefab->mUUID; return handle; }
void ScriptPrefabUtility::internal_applyPrefab(ScriptSceneObject* nativeInstance) { if (ScriptSceneObject::checkIfDestroyed(nativeInstance)) return; String prefabLinkUUID = nativeInstance->getNativeSceneObject()->getPrefabLink(); HPrefab prefab = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false)); if (prefab != nullptr) prefab->update(nativeInstance->getNativeSceneObject()); gResources().save(prefab); }
HPrefab Prefab::create(const HSceneObject& sceneObject, bool isScene) { SPtr<Prefab> newPrefab = createEmpty(); newPrefab->mIsScene = isScene; PrefabUtility::clearPrefabIds(sceneObject, true, false); newPrefab->initialize(sceneObject); HPrefab handle = static_resource_cast<Prefab>(gResources()._createResourceHandle(newPrefab)); newPrefab->mUUID = handle.getUUID(); sceneObject->mPrefabLinkUUID = newPrefab->mUUID; newPrefab->_getRoot()->mPrefabLinkUUID = newPrefab->mUUID; return handle; }
void PrefabUtility::recordPrefabDiff(const HSceneObject& sceneObject) { HSceneObject topLevelObject = sceneObject; while (topLevelObject != nullptr) { if (!topLevelObject->mPrefabLinkUUID.empty()) break; if (topLevelObject->mParent != nullptr) topLevelObject = topLevelObject->mParent; else topLevelObject = nullptr; } if (topLevelObject == nullptr) topLevelObject = sceneObject; Stack<HSceneObject> todo; todo.push(topLevelObject); while (!todo.empty()) { HSceneObject current = todo.top(); todo.pop(); if (!current->mPrefabLinkUUID.empty()) { current->mPrefabDiff = nullptr; HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->mPrefabLinkUUID, false, false)); if (prefabLink.isLoaded(false)) current->mPrefabDiff = PrefabDiff::create(prefabLink->_getRoot(), current->getHandle()); } UINT32 childCount = current->getNumChildren(); for (UINT32 i = 0; i < childCount; i++) { HSceneObject child = current->getChild(i); todo.push(child); } } gResources().unloadAllUnused(); }
void PrefabUtility::revertToPrefab(const HSceneObject& so) { String prefabLinkUUID = so->getPrefabLink(); HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(prefabLinkUUID, false, false)); if (prefabLink == nullptr) return; // Save IDs, destroy original, create new, restore IDs SceneObjectProxy soProxy; UnorderedMap<UINT32, GameObjectInstanceDataPtr> linkedInstanceData; recordInstanceData(so, soProxy, linkedInstanceData); HSceneObject parent = so->getParent(); // This will destroy the object but keep it in the parent's child list HSceneObject currentSO = so; so->destroyInternal(currentSO, true); HSceneObject newInstance = prefabLink->instantiate(); newInstance->mParent = parent; restoreLinkedInstanceData(newInstance, soProxy, linkedInstanceData); }
void PrefabUtility::updateFromPrefab(const HSceneObject& so) { HSceneObject topLevelObject = so; while (topLevelObject != nullptr) { if (!topLevelObject->mPrefabLinkUUID.empty()) break; if (topLevelObject->mParent != nullptr) topLevelObject = topLevelObject->mParent; else topLevelObject = nullptr; } Stack<HSceneObject> todo; todo.push(topLevelObject); // Find any prefab instances Vector<HSceneObject> prefabInstanceRoots; while (!todo.empty()) { HSceneObject current = todo.top(); todo.pop(); if (!current->mPrefabLinkUUID.empty()) prefabInstanceRoots.push_back(current); UINT32 childCount = current->getNumChildren(); for (UINT32 i = 0; i < childCount; i++) { HSceneObject child = current->getChild(i); todo.push(child); } } // Stores data about the new prefab instance and its original parent and link id // (as those aren't stored in the prefab diff) struct RestoredPrefabInstance { HSceneObject newInstance; HSceneObject originalParent; SPtr<PrefabDiff> diff; UINT32 originalLinkId; }; Vector<RestoredPrefabInstance> newPrefabInstanceData; // For each prefab instance load its reference prefab from the disk and check if it changed. If it has changed // instantiate the prefab and destroy the current instance. Then apply instance specific changes stored in a // prefab diff, if any, as well as restore the original parent and link id (link id of the root prefab instance // belongs to the parent prefab if any). Finally fix any handles pointing to the old objects so that they now point // to the newly instantiated objects. To the outside world it should be transparent that we just destroyed and then // re-created from scratch the entire hierarchy. // Need to do this bottom up to ensure I don't destroy the parents before children for (auto iter = prefabInstanceRoots.rbegin(); iter != prefabInstanceRoots.rend(); ++iter) { HSceneObject current = *iter; HPrefab prefabLink = static_resource_cast<Prefab>(gResources().loadFromUUID(current->mPrefabLinkUUID, false, false)); if (prefabLink.isLoaded(false) && prefabLink->getHash() != current->mPrefabHash) { // Save IDs, destroy original, create new, restore IDs SceneObjectProxy soProxy; UnorderedMap<UINT32, GameObjectInstanceDataPtr> linkedInstanceData; recordInstanceData(current, soProxy, linkedInstanceData); HSceneObject parent = current->getParent(); SPtr<PrefabDiff> prefabDiff = current->mPrefabDiff; current->destroy(true); HSceneObject newInstance = prefabLink->_clone(); // When restoring instance IDs it is important to make all the new handles point to the old GameObjectInstanceData. // This is because old handles will have different GameObjectHandleData and we have no easy way of accessing it to // change to which GameObjectInstanceData it points. But the GameObjectManager ensures that all handles deserialized // at once (i.e. during the ::instantiate() call above) will share GameObjectHandleData so we can simply replace // to what they point to, affecting all of the handles to that object. (In another words, we can modify the // new handles at this point, but old ones must keep referencing what they already were.) restoreLinkedInstanceData(newInstance, soProxy, linkedInstanceData); restoreUnlinkedInstanceData(newInstance, soProxy); newPrefabInstanceData.push_back({ newInstance, parent, prefabDiff, newInstance->getLinkId() }); } } // Once everything is instantiated, apply diffs, restore old parents & link IDs for root. for (auto& entry : newPrefabInstanceData) { // Diffs must be applied after everything is instantiated and instance data restored since it may contain // game object handles within or external to its prefab instance. if (entry.diff != nullptr) entry.diff->apply(entry.newInstance); entry.newInstance->mPrefabDiff = entry.diff; entry.newInstance->setParent(entry.originalParent, false); entry.newInstance->mLinkId = entry.originalLinkId; } gResources().unloadAllUnused(); }
bool ScriptPrefab::internal_IsScene(ScriptPrefab* thisPtr) { HPrefab prefab = thisPtr->getHandle(); return prefab->isScene(); }