#pragma warning disable 0168 using UnityEngine; using System.Linq; using System; using System.Reflection; using UnityEngine.ProBuilder; using UnityEngine.Rendering; using UObject = UnityEngine.Object; using UnityEditor.SettingsManagement; namespace UnityEditor.ProBuilder { /// /// Utilities for working in Unity editor. /// public static class EditorUtility { const float k_DefaultNotificationDuration = 1f; static float s_NotificationTimer = 0f; static EditorWindow s_NotificationWindow; static bool s_IsNotificationDisplayed = false; [UserSetting("General", "Show Action Notifications", "Enable or disable notification popups when performing actions.")] static Pref s_ShowNotifications = new Pref("editor.showEditorNotifications", false); [UserSetting("Mesh Settings", "Static Editor Flags", "Default static flags to apply to new shapes.")] static Pref s_StaticEditorFlags = new Pref("mesh.defaultStaticEditorFlags", 0); [UserSetting("Mesh Settings", "Material", "The default material to be applied to newly created shapes.")] static Pref s_DefaultMaterial = new Pref("mesh.userMaterial", null); [UserSetting("Mesh Settings", "Mesh Collider is Convex", "If a MeshCollider is set as the default collider component, this sets the convex setting.")] static Pref s_MeshColliderIsConvex = new Pref("mesh.meshColliderIsConvex", false); [UserSetting("Mesh Settings", "Pivot Location", "Determines the placement of new shape's pivot.")] static Pref s_NewShapesPivotAtVertex = new Pref("mesh.newShapePivotLocation", PivotLocation.FirstVertex); [UserSetting("Mesh Settings", "Snap New Shape To Grid", "When enabled, new shapes will snap to the closest point on grid.")] static Pref s_SnapNewShapesToGrid = new Pref("mesh.newShapesSnapToGrid", true); [UserSetting("Mesh Settings", "Shadow Casting Mode", "The default ShadowCastingMode to apply to MeshRenderer components.")] static Pref s_ShadowCastingMode = new Pref("mesh.shadowCastingMode", ShadowCastingMode.On); [UserSetting("Mesh Settings", "Collider Type", "What type of Collider to apply to new Shapes.")] static Pref s_ColliderType = new Pref("mesh.newShapeColliderType", ColliderType.MeshCollider); internal static PivotLocation newShapePivotLocation { get { return s_NewShapesPivotAtVertex; } } /// /// Subscribe to this delegate to be notified when a new mesh has been created and initialized through ProBuilder. /// /// /// This is only called when an object is initialized in editor, and created by ProBuilder menu items. /// public static event Action meshCreated = null; /// /// Set the selected render state for an object. In Unity 5.4 and lower, this just toggles wireframe on or off. /// /// /// internal static void SetSelectionRenderState(Renderer renderer, EditorSelectedRenderState state) { UnityEditor.EditorUtility.SetSelectedRenderState(renderer, state); } internal static void ShowNotification(ActionResult result) { ShowNotification(result.notification); } /// /// Show a timed (1 second) notification in the SceneView window. /// /// The text to display in the notification. /// internal static void ShowNotification(string message) { SceneView scnview = SceneView.lastActiveSceneView; if (scnview == null) scnview = EditorWindow.GetWindow(); ShowNotification(scnview, message); } /// /// The EditorWindow to display this notification in. /// The text to display in the notification. /// Window is null. internal static void ShowNotification(EditorWindow window, string message) { if (!s_ShowNotifications) return; if (window == null) throw new ArgumentNullException("window"); window.ShowNotification(new GUIContent(message, "")); window.Repaint(); if (EditorApplication.update != NotifUpdate) EditorApplication.update += NotifUpdate; s_NotificationTimer = Time.realtimeSinceStartup + k_DefaultNotificationDuration; s_NotificationWindow = window; s_IsNotificationDisplayed = true; } /// /// Remove any currently displaying notifications from an . /// /// The EditorWindow from which all currently displayed notifications will be removed. /// Thrown if window is null. internal static void RemoveNotification(EditorWindow window) { if (window == null) throw new ArgumentNullException("window"); EditorApplication.update -= NotifUpdate; window.RemoveNotification(); window.Repaint(); } static void NotifUpdate() { if (s_IsNotificationDisplayed && Time.realtimeSinceStartup > s_NotificationTimer) { s_IsNotificationDisplayed = false; RemoveNotification(s_NotificationWindow); } } internal static bool IsPrefab(ProBuilderMesh mesh) { #if UNITY_2018_3_OR_NEWER return PrefabUtility.GetPrefabAssetType(mesh.gameObject) != PrefabAssetType.NotAPrefab; #else PrefabType type = PrefabUtility.GetPrefabType(mesh.gameObject); return type == PrefabType.Prefab || type == PrefabType.PrefabInstance || type == PrefabType.DisconnectedPrefabInstance; #endif } /// /// Returns true if this object is a prefab instanced in the scene. /// /// /// internal static bool IsPrefabInstance(GameObject go) { #if UNITY_2018_3_OR_NEWER var status = PrefabUtility.GetPrefabInstanceStatus(go); return status == PrefabInstanceStatus.Connected || status == PrefabInstanceStatus.Disconnected; #else return PrefabUtility.GetPrefabType(go) == PrefabType.PrefabInstance; #endif } /** * Returns true if this object is a prefab in the Project view. */ internal static bool IsPrefabAsset(GameObject go) { #if UNITY_2018_3_OR_NEWER return PrefabUtility.IsPartOfPrefabAsset(go); #else return PrefabUtility.GetPrefabType(go) == PrefabType.Prefab; #endif } /** * Returns true if Asset Store window is open, false otherwise. */ internal static bool AssetStoreWindowIsOpen() { return Resources.FindObjectsOfTypeAll().Any(x => x.GetType().ToString().Contains("AssetStoreWindow")); } /// /// Ensure that this object has a valid mesh reference, and the geometry is current. If it is not valid, this function will attempt to repair the sync state. /// /// The component to test. /// public static void SynchronizeWithMeshFilter(ProBuilderMesh mesh) { if (mesh == null) throw new ArgumentNullException("mesh"); Mesh oldMesh = mesh.mesh; MeshSyncState state = mesh.meshSyncState; bool meshesAreAssets = Experimental.meshesAreAssets; if (state != MeshSyncState.InSync) { if (state == MeshSyncState.Null) { mesh.Rebuild(); mesh.Optimize(); } else /** * If the mesh ID doesn't match the gameObject Id, it could mean two things - * 1. The object was just duplicated, and then made unique * 2. The scene was reloaded, and gameObject ids were recalculated. * If the latter, we need to clean up the old mesh. If the former, * the old mesh needs to *not* be destroyed. */ if (oldMesh) { int meshNo = -1; int.TryParse(oldMesh.name.Replace("pb_Mesh", ""), out meshNo); UnityEngine.Object dup = UnityEditor.EditorUtility.InstanceIDToObject(meshNo); GameObject go = dup as GameObject; if (go == null) { // Debug.Log("scene reloaded - false positive."); mesh.mesh.name = "pb_Mesh" + mesh.id; } else { // Debug.Log("duplicate mesh"); if (!meshesAreAssets || !(EditorUtility.IsPrefabAsset(mesh.gameObject) || IsPrefabInstance(mesh.gameObject))) { // deep copy arrays & ToMesh/Refresh mesh.MakeUnique(); mesh.Optimize(); } } } else { // old mesh didn't exist, so this is probably a prefab being instanced if (EditorUtility.IsPrefabAsset(mesh.gameObject)) mesh.mesh.hideFlags = (HideFlags)(1 | 2 | 4 | 8); mesh.Optimize(); } } else { if (meshesAreAssets) EditorMeshUtility.TryCacheMesh(mesh); } } /// /// Returns true if GameObject contains flags. /// /// /// /// internal static bool HasStaticFlag(this GameObject go, StaticEditorFlags flags) { return (GameObjectUtility.GetStaticEditorFlags(go) & flags) == flags; } /// /// Initialize this object with the various editor-only parameters, and invoke the object creation callback. /// /// internal static void InitObject(ProBuilderMesh pb) { ScreenCenter(pb.gameObject); SetPivotLocationAndSnap(pb); pb.renderer.shadowCastingMode = s_ShadowCastingMode; pb.renderer.sharedMaterial = GetUserMaterial(); GameObjectUtility.SetStaticEditorFlags(pb.gameObject, s_StaticEditorFlags); switch (s_ColliderType.value) { case ColliderType.BoxCollider: pb.gameObject.AddComponent(); break; case ColliderType.MeshCollider: pb.gameObject.AddComponent().convex = s_MeshColliderIsConvex; break; } pb.unwrapParameters = new UnwrapParameters(Lightmapping.s_UnwrapParameters); pb.Optimize(); if (meshCreated != null) meshCreated(pb); } internal static void SetPivotLocationAndSnap(ProBuilderMesh mesh) { if (ProGridsInterface.SnapEnabled()) mesh.transform.position = Snapping.SnapValue(mesh.transform.position, ProGridsInterface.SnapValue()); else if (s_SnapNewShapesToGrid) mesh.transform.position = Snapping.SnapValue(mesh.transform.position, new Vector3( EditorPrefs.GetFloat("MoveSnapX"), EditorPrefs.GetFloat("MoveSnapY"), EditorPrefs.GetFloat("MoveSnapZ"))); mesh.Optimize(); } /** * Puts the selected gameObject at the pivot point of the SceneView camera. */ internal static void ScreenCenter(GameObject _gameObject) { if (_gameObject == null) return; // If in the unity editor, attempt to center the object the sceneview or main camera, in that order _gameObject.transform.position = ScenePivot(); Selection.activeObject = _gameObject; } /** * Gets the current SceneView's camera's pivot point. */ internal static Vector3 ScenePivot() { return GetSceneView().pivot; } /** * Returns the last active SceneView window, or creates a new one if no last SceneView is found. */ internal static SceneView GetSceneView() { return SceneView.lastActiveSceneView == null ? EditorWindow.GetWindow() : SceneView.lastActiveSceneView; } #if !UNITY_2019_1_OR_NEWER const BindingFlags k_BindingFlagsAll = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; static SceneView.OnSceneFunc onPreSceneGuiDelegate { get { var fi = typeof(SceneView).GetField("onPreSceneGUIDelegate", k_BindingFlagsAll); return fi != null ? fi.GetValue(null) as SceneView.OnSceneFunc : null; } set { var fi = typeof(SceneView).GetField("onPreSceneGUIDelegate", k_BindingFlagsAll); if (fi != null) fi.SetValue(null, value); } } internal static void RegisterOnPreSceneGUIDelegate(SceneView.OnSceneFunc func) { var del = onPreSceneGuiDelegate; if (del == null) onPreSceneGuiDelegate = func; else del += func; } internal static void UnregisterOnPreSceneGUIDelegate(SceneView.OnSceneFunc func) { var del = onPreSceneGuiDelegate; if (del != null) del -= func; } #endif internal static bool IsUnix() { System.PlatformID platform = System.Environment.OSVersion.Platform; return platform == System.PlatformID.MacOSX || platform == System.PlatformID.Unix || (int)platform == 128; } /** * CreateCachedEditor didn't exist until 5.0, so recreate it's contents if necessary or pass it on. */ internal static void CreateCachedEditor(UnityEngine.Object[] targetObjects, ref UnityEditor.Editor previousEditor) where T : UnityEditor.Editor { #if UNITY_4_7 if (previousEditor != null && pbUtil.IsEqual(previousEditor.targets, targetObjects)) return; if (previousEditor != null) UnityEngine.Object.DestroyImmediate(previousEditor); previousEditor = Editor.CreateEditor(targetObjects, typeof(T)); #else UnityEditor.Editor.CreateCachedEditor(targetObjects, typeof(T), ref previousEditor); #endif } /// /// Is this mode one of the mesh element modes (vertex, edge, face, texture). /// /// /// internal static bool IsMeshElementMode(this SelectMode mode) { return mode.ContainsFlag( SelectMode.Vertex | SelectMode.Edge | SelectMode.Face | SelectMode.TextureEdge | SelectMode.TextureFace | SelectMode.TextureVertex ); } internal static bool IsTextureMode(this SelectMode mode) { return mode.ContainsFlag( SelectMode.TextureEdge | SelectMode.TextureFace | SelectMode.TextureVertex ); } internal static bool IsPositionMode(this SelectMode mode) { return mode.ContainsFlag( SelectMode.TextureEdge | SelectMode.TextureFace | SelectMode.TextureVertex ); } internal static SelectMode GetPositionMode(this SelectMode mode) { if (mode.ContainsFlag(SelectMode.TextureFace)) mode = (mode & ~SelectMode.TextureFace) | SelectMode.Face; if (mode.ContainsFlag(SelectMode.TextureEdge)) mode = (mode & ~SelectMode.TextureEdge) | SelectMode.Edge; if (mode.ContainsFlag(SelectMode.TextureVertex)) mode = (mode & ~SelectMode.TextureVertex) | SelectMode.Vertex; return mode; } internal static SelectMode GetTextureMode(this SelectMode mode) { if (mode.ContainsFlag(SelectMode.Face)) mode = (mode & ~SelectMode.Face) | SelectMode.TextureFace; if (mode.ContainsFlag(SelectMode.Edge)) mode = (mode & ~SelectMode.Edge) | SelectMode.TextureEdge; if (mode.ContainsFlag(SelectMode.Vertex)) mode = (mode & ~SelectMode.Vertex) | SelectMode.TextureVertex; return mode; } /// /// Test if SelectMode contains any of the value bits. /// /// /// HasFlag doesn't exist in .NET 3.5 /// /// /// /// internal static bool ContainsFlag(this SelectMode target, SelectMode value) { return (target & value) != SelectMode.None; } internal static SelectMode GetSelectMode(EditLevel edit, ComponentMode component) { switch (edit) { case EditLevel.Top: return SelectMode.Object; case EditLevel.Geometry: { switch (component) { case ComponentMode.Vertex: return SelectMode.Vertex; case ComponentMode.Edge: return SelectMode.Edge; default: return SelectMode.Face; } } case EditLevel.Texture: return SelectMode.TextureFace; default: return SelectMode.None; } } internal static EditLevel GetEditLevel(SelectMode mode) { switch (mode) { case SelectMode.Object: return EditLevel.Top; case SelectMode.TextureFace: return EditLevel.Texture; case SelectMode.None: return EditLevel.Plugin; default: return EditLevel.Geometry; } } internal static ComponentMode GetComponentMode(SelectMode mode) { switch (mode) { case SelectMode.Vertex: return ComponentMode.Vertex; case SelectMode.Edge: return ComponentMode.Edge; default: return ComponentMode.Face; } } internal static Material GetUserMaterial() { var mat = (Material)s_DefaultMaterial; if (mat != null) return mat; return BuiltinMaterials.defaultMaterial; } internal static bool IsDeveloperMode() { return EditorPrefs.GetBool("DeveloperMode", false); } } }