private static void UpdateMetaDataRecursively(GameObject root, string suffix, PrefabLabData rootPLD, bool firstExecution) { PrefabLabData metadata = root.GetComponent <PrefabLabData>(); metadata.Data = suffix; if (firstExecution) { rootPLD.SkinnedMeshes = false; } if (metadata.Type == PrefabLabObjectType.Old) { metadata.Type = PrefabLabObjectType.NotSpecified; } if (root.GetComponent <SkinnedMeshRenderer>() != null) { metadata.Type = PrefabLabObjectType.SkinnedMesh; rootPLD.SkinnedMeshes = true; } for (int i = 0; i < root.transform.childCount; i++) { GameObject child = root.transform.GetChild(i).gameObject; UpdateMetaDataRecursively(child, suffix, rootPLD, false); } }
public override void OnInspectorGUI() { PrefabLabData PLD = target as PrefabLabData; if (PLD.Type == PrefabLabObjectType.Old) { EditorGUILayout.Space(); EditorGUILayout.HelpBox("This is an old prefab. Please update the prefab to see new features", MessageType.Info, true); } else if (PLD.Type == PrefabLabObjectType.Root) { EditorGUILayout.Space(); PLD.ImportAnimationsAutomatically = EditorGUILayout.Toggle("Import Animations", PLD.ImportAnimationsAutomatically, EditorStyles.toggle); EditorGUILayout.Space(); if (PLD.SkinnedMeshes) { EditorGUILayout.HelpBox("SkinnedMeshRenderers are not supported yet. To update a skinned mesh correctly select the skinned GameObject and follow instructions. TIP: Try to avoid adding components and GameObjects to the skinned object and it's bones.", MessageType.Warning, true); } } else if (PLD.Type == PrefabLabObjectType.SkinnedMesh) { EditorGUILayout.Space(); EditorGUILayout.HelpBox("SkinnedMeshRenderers are not supported yet. To update a skinned mesh correctly you have to delete this gameObject, and all the bones used by this component. Apply the Prefab and update the prefab", MessageType.Warning, true); } EditorGUILayout.Space(); if (GUILayout.Button("Update Prefab")) { string path = AssetDatabase.GetAssetPath(PrefabUpdater.GetInstanceIDOfSceneAndProjectObject(PLD.gameObject)); PrefabLabSettings settings = PrefabUpdater.GetPrefabLabSettings(); PrefabUpdater.UpdatePrefab(settings, path); } if (GUILayout.Button("Select Model")) { PrefabLabSettings settings = PrefabUpdater.GetPrefabLabSettings(); Object[] newSelection = PrefabUpdater.GetModelsOrPrefabs( new Object[] { AssetDatabase.LoadAssetAtPath(AssetDatabase.GetAssetPath(PrefabUpdater.GetInstanceIDOfSceneAndProjectObject(PLD.gameObject)), typeof(Object)) }, settings); if (newSelection != null) { Selection.objects = newSelection; } else { Debug.Log("There was no prefab lab object found"); } } EditorGUILayout.Space(); }
private static void CopyMetaData(GameObject source, GameObject destination) { PrefabLabData destPLD = destination.GetComponent <PrefabLabData>(); PrefabLabData sourcePLD = source.GetComponent <PrefabLabData>(); if (destPLD != null) { destPLD.Type = sourcePLD.Type; } else { destPLD = destination.AddComponent <PrefabLabData>(); destPLD.Type = PrefabLabObjectType.NotSpecified; } }
//Rename MetaData.Data from NewModelNameSuffix to CurrentModelNameSuffix //(New and Current exist so we can detect whether an gameobject has just been imported or its an older part of the prefab) private static void RenameNewToCurrent(GameObject root, PrefabLabSettings settings) { PrefabLabData metadata = root.GetComponent <PrefabLabData>(); if (metadata != null) { if (metadata.Data == NewModelNameSuffix) { metadata.Data = settings.ModelMetadata; } } for (int i = 0; i < root.transform.childCount; i++) { RenameNewToCurrent(root.transform.GetChild(i).gameObject, settings); } }
//Recursive remove old prefab parts and move the userdata on it to lostObjects private static void RescueAndRemove(GameObject importedModel, GameObject prefab, GameObject lostObjects) { //Check prefab for removed parts for (int oldPrefabIndex = prefab.transform.childCount - 1; oldPrefabIndex >= 0; oldPrefabIndex--) { bool hasMatch = false; for (int newModelIndex = importedModel.transform.childCount - 1; newModelIndex >= 0; newModelIndex--) { GameObject newModelChild = importedModel.transform.GetChild(newModelIndex).gameObject; GameObject oldPrefabChild = prefab.transform.GetChild(oldPrefabIndex).gameObject; if (newModelChild.name == oldPrefabChild.name) { hasMatch = true; RescueAndRemove(newModelChild, oldPrefabChild, lostObjects); break; } } //Removed gameobject in the prefab detected. Gather userdata and add it to lost gameobjects if (!hasMatch) { PrefabLabData metadata = prefab.transform.GetChild(oldPrefabIndex).gameObject.GetComponent <PrefabLabData>(); if (metadata != null) { if (metadata.Data != NewModelNameSuffix) //Check whether the modelpart has just been added to the prefab by Rebuild() or not { //Gather userdata before removing FindAndSaveLostObjects(prefab.transform.GetChild(oldPrefabIndex).gameObject, lostObjects); GameObject.DestroyImmediate(prefab.transform.GetChild(oldPrefabIndex).gameObject); } } } } }
//Recursively find all gameobjects in root and add suffix to name private static void UpdateMetaDataRecursively(GameObject root, string suffix, PrefabLabData rootPLD) { UpdateMetaDataRecursively(root, suffix, rootPLD, true); }
private static void CopyComponents(GameObject source, GameObject destination, string assetPath, PrefabLabData PLD) { CopyTransform(source, destination); CopyMetaData(source, destination); if (GetModelImporter(assetPath).importMaterials) { CopyMaterials(source, destination); } if (GetModelImporter(assetPath).generateAnimations != ModelImporterGenerateAnimations.None && PLD.ImportAnimationsAutomatically) { CopyAnimations(source, destination); } if (GetModelImporter(assetPath).addCollider) { CopyColliders(source, destination); } }
//Recursive copy newly found modelparts to the prefab //and synch Transform, Materials etc. with current parts private static void RebuildPrefab(GameObject importedModel, GameObject prefab, string assetPath, PrefabLabData PLD) { for (int newModelIndex = importedModel.transform.childCount - 1; newModelIndex >= 0; newModelIndex--) { bool hasMatch = false; for (int oldPrefabIndex = prefab.transform.childCount - 1; oldPrefabIndex >= 0; oldPrefabIndex--) { GameObject importedModelChild = importedModel.transform.GetChild(newModelIndex).gameObject; GameObject prefabChild = prefab.transform.GetChild(oldPrefabIndex).gameObject; if (importedModelChild.name == prefabChild.name) { CopyComponents(importedModelChild, prefabChild, assetPath, PLD); hasMatch = true; RebuildPrefab(importedModelChild, prefabChild, assetPath, PLD); break; } } //New gameobject in importedModel detected, copy it to the prefab if (!hasMatch) { ParentGOWithoutTransformChanges(prefab, importedModel.transform.GetChild(newModelIndex).gameObject); } } }
public static void UpdatePrefab(string modelPath, PrefabLabSettings settings) { //Substract subdirs from imported asset string relativeSubdir = modelPath.Remove(0, ("Assets/" + settings.SourceFolder).Length + 1); relativeSubdir = relativeSubdir.Remove(relativeSubdir.LastIndexOf("/") + 1); string relativeSourceFolder = settings.DestinationFolder + "/" + relativeSubdir; string absoluteSubdir = Application.dataPath + "/" + relativeSourceFolder; //Prepare the imported model for modification GameObject importedModel = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath(modelPath, typeof(GameObject))); AddMetaData(importedModel); GameObject lostObjects = null; Object originalPrefab; if (!FindExistingPrefab(importedModel.name, relativeSourceFolder, settings.PrefabSuffix, out originalPrefab)) { //Create subdirs Directory.CreateDirectory(absoluteSubdir); AssetDatabase.Refresh(); //Prefab not found, create one for me please! string prefabFilepath = "Assets/" + relativeSourceFolder + importedModel.name + settings.PrefabSuffix + ".prefab"; Object emptyPrefab = PrefabUtility.CreateEmptyPrefab(prefabFilepath); AssetDatabase.ImportAsset(prefabFilepath); //Creating an empty prefab is really empty so we need a gameobject to start with, called parent GameObject parent = new GameObject(importedModel.name + settings.PrefabSuffix); PrefabLabData PLD = parent.AddComponent <PrefabLabData>(); PLD.Type = PrefabLabObjectType.Root; //Mark as max model UpdateMetaDataRecursively(importedModel, settings.ModelMetadata, PLD); //Copy newModel to prefab importedModel.transform.parent = parent.transform; originalPrefab = PrefabUtility.ReplacePrefab(parent, emptyPrefab, ReplacePrefabOptions.ReplaceNameBased); //Clean up GameObject.DestroyImmediate(parent); } else { GameObject prefab = (GameObject)GameObject.Instantiate(originalPrefab); prefab.name = originalPrefab.name; PrefabLabData PLD = prefab.GetComponent <PrefabLabData>(); if (PLD == null) { PLD = prefab.AddComponent <PrefabLabData>(); PLD.Type = PrefabLabObjectType.Root; } //Find LostObjects root or create it lostObjects = GetChildByName(lostObjectsName, prefab); if (lostObjects == null) { lostObjects = new GameObject(lostObjectsName); lostObjects.transform.parent = prefab.transform; } //Find newModel root in merlin's prefab and do magic GameObject prefabModelRoot = GetChildByName(importedModel.name, prefab); if (prefabModelRoot != null) { //Check if object changed from one object to multiple if ((prefabModelRoot.transform.GetChildCount() == 0 && importedModel.transform.GetChildCount() != 0) || (prefabModelRoot.transform.GetChildCount() != 0 && importedModel.transform.GetChildCount() == 0)) { prefabModelRoot.transform.parent = lostObjects.transform; #if UNITY_3_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_3_6 prefabModelRoot.SetActiveRecursively(false); #else prefabModelRoot.SetActive(false); #endif prefabModelRoot = null; Debug.LogWarning("Unity treats model files with one object differently than files with multiple objects. You changed in the number of objects in " + importedModel.name + ". We recovered the old gameobjects in LostObjects."); } } if (prefabModelRoot == null) { UpdateMetaDataRecursively(importedModel, settings.ModelMetadata, PLD); importedModel.transform.parent = prefab.transform; importedModel.transform.localPosition = Vector3.zero; } else { //Mark the newly imported as new to know later on if a modelpart has just been imported or its an old prefabpart UpdateMetaDataRecursively(importedModel, NewModelNameSuffix, PLD); //Does all the magic to childs RebuildPrefab(importedModel, prefabModelRoot, modelPath, PLD); RescueAndRemove(importedModel, prefabModelRoot, lostObjects); RenameNewToCurrent(prefabModelRoot.gameObject, settings); //Don't forget the parent CopyComponents(importedModel, prefabModelRoot, modelPath, PLD); } //Clean up if no lostobjects have been found if (lostObjects.transform.childCount > 0) { #if UNITY_3_0 || UNITY_3_1 || UNITY_3_2 || UNITY_3_3 || UNITY_3_4 || UNITY_3_5 || UNITY_3_6 lostObjects.SetActiveRecursively(false); #else lostObjects.SetActive(false); #endif Debug.Log("Prefab Lab Warning: There are " + lostObjects.transform.childCount + " objects found which don't have a parent anymore because it has been deleted in the model." + System.Environment.NewLine + "To recover these objects, browse to the gameobject \"" + lostObjectsName + "\" in the prefab " + prefab.name + ".", originalPrefab); } else { GameObject.DestroyImmediate(lostObjects); } //Save created or modified prefab PrefabUtility.ReplacePrefab(prefab, originalPrefab, ReplacePrefabOptions.ReplaceNameBased); //Clean up GameObject.DestroyImmediate(importedModel); GameObject.DestroyImmediate(prefab); } }