using UnityEngine; using System.Linq; using System.Reflection; using System.IO; using System.Collections.Generic; using UnityEngine.Polybrush; using Polybrush; namespace UnityEditor.Polybrush { /// /// Helper functions for editor /// static class PolyEditorUtility { const string k_PackageDirectory = "Packages/com.unity.polybrush"; const string k_UserAssetDirectory = "Assets/Polybrush Data/"; /// /// True if this gameObject is in the Selection.gameObjects /// /// /// internal static bool InSelection(GameObject gameObject) { //null check if(gameObject == null) { return false; } Transform[] selectedTransform = Selection.GetTransforms(SelectionMode.Unfiltered); return selectedTransform.Contains(gameObject.transform); } /// /// GetMeshGUID version without the ref parameter /// /// mesh source /// ModelSource category internal static ModelSource GetMeshGUID(Mesh mesh) { string temp = string.Empty; return GetMeshGUID(mesh, ref temp); } /// /// Return the mesh source, and the guid if applicable (scene instances don't get GUIDs). /// /// mesh source /// guid returned /// ModelSource category internal static ModelSource GetMeshGUID(Mesh mesh, ref string guid) { if(mesh == null) { return ModelSource.Error; } string path = AssetDatabase.GetAssetPath(mesh); if(path != "") { AssetImporter assetImporter = AssetImporter.GetAtPath(path); if( assetImporter != null ) { // Only imported model (e.g. FBX) assets use the ModelImporter, // where a saved asset will have an AssetImporter but *not* ModelImporter. // A procedural mesh (one only existing in a scene) will not have any. if (assetImporter is ModelImporter) { guid = AssetDatabase.AssetPathToGUID(path); return ModelSource.Imported; } else { guid = AssetDatabase.AssetPathToGUID(path); return ModelSource.Asset; } } else { return ModelSource.Scene; } } return ModelSource.Scene; } internal const int DIALOG_OK = 0; internal const int DIALOG_CANCEL = 1; internal const int DIALOG_ALT = 2; internal const string DO_NOT_SAVE = "DO_NOT_SAVE"; /// /// Save any modifications to the EditableObject. If the mesh is a scene mesh or imported mesh, it /// will be saved to a new asset. If the mesh was originally an asset mesh, the asset is overwritten. /// /// mesh to save /// will update the mesh filter with the new mesh if not null /// will update the skinned mesh renderer with the new mesh if not null /// return true if save was successful, false if user-canceled or otherwise failed. internal static bool SaveMeshAsset(Mesh mesh, MeshFilter meshFilter = null, SkinnedMeshRenderer skinnedMeshRenderer = null, int overridenDialogResult = -1, string overridenPath = "") { if (mesh == null) return false; string save_path = !string.IsNullOrEmpty(overridenPath) ? overridenPath : DO_NOT_SAVE; ModelSource source = GetMeshGUID(mesh); switch (source) { case ModelSource.Asset: int saveChanges = overridenDialogResult != -1 ? overridenDialogResult : EditorUtility.DisplayDialogComplex( "Save Changes", "Save changes to edited mesh?", "Save", // DIALOG_OK "Cancel", // DIALOG_CANCEL "Save As"); // DIALOG_ALT if(saveChanges == DIALOG_OK) save_path = AssetDatabase.GetAssetPath(mesh); else if(saveChanges == DIALOG_ALT) save_path = EditorUtility.SaveFilePanelInProject("Save Mesh As", mesh.name + ".asset", "asset", "Save edited mesh to"); else return false; break; case ModelSource.Imported: case ModelSource.Scene: default: save_path = EditorUtility.SaveFilePanelInProject("Save Mesh As", mesh.name + ".asset", "asset", "Save edited mesh to"); break; } if(!save_path.Equals(DO_NOT_SAVE) && !string.IsNullOrEmpty(save_path)) { Mesh existing = AssetDatabase.LoadAssetAtPath(save_path); bool overwrite = existing != null; Mesh dst = overwrite ? existing : new Mesh(); PolyMeshUtility.Copy(mesh, dst); if(!overwrite) AssetDatabase.CreateAsset(dst, save_path); AssetDatabase.Refresh(); return true; } // Save was canceled return false; } /// /// Load an icon /// /// location of the icon /// The loaded icon internal static Texture2D LoadIcon(string icon) { MethodInfo loadIconMethod = typeof(EditorGUIUtility).GetMethod("LoadIcon", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); Texture2D img = (Texture2D) loadIconMethod.Invoke(null, new object[] { icon } ); return img; } internal static string RootFolder { get { return k_PackageDirectory; } } internal static string UserAssetDirectory { get { return k_UserAssetDirectory; } } internal static T LoadDefaultAsset(string path) where T : UnityEngine.Object { return AssetDatabase.LoadAssetAtPath(k_PackageDirectory + "/Content/" + path); } /// /// Fetch a default asset from path relative to the product folder. If not found, a new one is created. /// /// >the type to retrieve, must inherit from ScriptableObject and implement IHasDefault and ICustomSettings /// The first asset of the type T found inside the project (order set by AssetDataBase.FindAssets function. If none returns a new object T with default values set by IHasDefault. internal static T GetFirstOrNew() where T : ScriptableObject, IHasDefault, ICustomSettings { string[] all = AssetDatabase.FindAssets("t:" + typeof(T)); T asset = all.Length > 0 ? AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(all.First())) : null; if(asset == null) { return CreateNew(); } return asset; } /// /// Create a new asset at path relative to the product folder. /// /// >the type to create, must inherit from ScriptableObject and implement IHasDefault and ICustomSettings /// A new object T with default values set by IHasDefault. internal static T CreateNew() where T : ScriptableObject, IHasDefault, ICustomSettings { T asset = ScriptableObject.CreateInstance(); asset.SetDefaultValues(); EditorUtility.SetDirty(asset); string folder = UserAssetDirectory; if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); string subfolder = folder + asset.assetsFolder; if (!Directory.Exists(subfolder)) Directory.CreateDirectory(subfolder); string assetPath = AssetDatabase.GenerateUniqueAssetPath(subfolder + typeof(T).Name + "-Default.asset"); if (string.IsNullOrEmpty(assetPath)) { return null; } AssetDatabase.CreateAsset(asset, assetPath); AssetDatabase.Refresh(); return asset; } /// /// Fetch all assets of type `T` /// /// the type to retrieve, must inherit from ScriptableObject and implement IHasDefault and ICustomSettings /// All the assets of that type on the project internal static List GetAll() where T: ScriptableObject, IHasDefault, ICustomSettings { var tGuids = AssetDatabase.FindAssets("t:" + typeof(T)); var Ts = new List(); foreach (var guid in tGuids) Ts.Add(AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid))); return Ts; } /// /// Set the selected render state for an object. In Unity 5.4 and lower, this just toggles wireframe on or off. /// /// Renderer to change selection state /// State to be set internal static void SetSelectionRenderState(Renderer renderer, SelectionRenderState state) { #if UNITY_5_3 || UNITY_5_4 EditorUtility.SetSelectedWireframeHidden(renderer, state == 0); #else EditorUtility.SetSelectedRenderState(renderer, (EditorSelectedRenderState) state ); #endif } internal static SelectionRenderState GetSelectionRenderState() { #if UNITY_5_3 || UNITY_5_4 return SelectionRenderState.Wireframe; #else bool wireframe = false, outline = false; try { wireframe = (bool) ReflectionUtility.GetValue(null, "UnityEditor.AnnotationUtility", "showSelectionWire"); outline = (bool) ReflectionUtility.GetValue(null, "UnityEditor.AnnotationUtility", "showSelectionOutline"); } catch { Debug.LogWarning("Looks like Unity changed the AnnotationUtility \"showSelectionOutline\"\nPlease email contact@procore3d.com and let Karl know!"); } SelectionRenderState state = SelectionRenderState.None; if(wireframe) state |= SelectionRenderState.Wireframe; if(outline) state |= SelectionRenderState.Outline; return state; #endif } /** * Returns true if this object is a prefab in the Project view. */ internal static bool IsPrefabAsset(Object go) { #if UNITY_2018_3_OR_NEWER return PrefabUtility.IsPartOfPrefabAsset(go); #else return PrefabUtility.GetPrefabType(go) == PrefabType.Prefab; #endif } /** * Returns true if the given array contains at least one prefab. */ internal static bool ContainsPrefabAssets(UnityEngine.Object[] objects) { for (int i = 0; i < objects.Length; ++i) { UnityEngine.Object obj = objects[i]; if (PolyEditorUtility.IsPrefabAsset(obj)) return true; } return false; } /// /// Returns true if GameObject has a PolybrushMesh component. /// /// /// internal static bool IsPolybrushObject(GameObject gameObject) { return gameObject.GetComponent() != null; } #pragma warning disable 612 /// /// Utility to help convert objects modified with the Beta version of Polybrush (Asset Store) /// to Polybrus 1.x. /// /// internal static PolybrushMesh ConvertGameObjectToNewFormat(z_AdditionalVertexStreams component) { GameObject go = component.gameObject; PolybrushMesh newComponent = go.GetComponent(); MeshFilter mf = go.GetComponent(); Mesh mesh = component.m_AdditionalVertexStreamMesh; Undo.DestroyObjectImmediate(component); // Cancel conversion if no mesh if found on Z_AdditionalVertexStreams if (mesh == null) return null; if (newComponent == null) { newComponent = Undo.AddComponent(go); newComponent.Initialize(); } newComponent.mode = PolybrushMesh.Mode.AdditionalVertexStream; newComponent.SetMesh(PolyMeshUtility.DeepCopy(mf.sharedMesh)); newComponent.SetAdditionalVertexStreams(mesh); return newComponent; } #pragma warning restore 612 } }