#if UNITY_2019_1_OR_NEWER #define SHORTCUT_MANAGER #endif using System; using UnityEngine; using System.Linq; using System.Collections.Generic; using UnityEngine.ProBuilder; using UnityEditor.ProBuilder.Actions; using PMesh = UnityEngine.ProBuilder.ProBuilderMesh; using UObject = UnityEngine.Object; using UnityEditor.SettingsManagement; using UnityEngine.ProBuilder.MeshOperations; namespace UnityEditor.ProBuilder { /// /// Manages the ProBuilder toolbar window and tool mode. /// public sealed class ProBuilderEditor : EditorWindow, IHasCustomMenu { // Match the value set in RectSelection.cs const float k_MouseDragThreshold = 6f; //Off pointer multiplier is a percentage of the picking distance const float k_OffPointerMultiplierPercent = 0.1f; /// /// Raised any time the ProBuilder editor refreshes the selection. This is called every frame when interacting with mesh elements, and after any mesh operation. /// public static event Action> selectionUpdated; /// /// Raised when the EditLevel is changed. /// public static event Action selectModeChanged; /// /// Called when vertex modifications are complete. /// public static event Action> afterMeshModification; /// /// Called immediately prior to beginning vertex modifications. The ProBuilderMesh will be in un-altered state at this point (meaning ProBuilderMesh.ToMesh and ProBuilderMesh.Refresh have been called, but not Optimize). /// public static event Action> beforeMeshModification; static EditorToolbar s_EditorToolbar; static ProBuilderEditor s_Instance; GUIContent[] m_EditModeIcons; GUIStyle VertexTranslationInfoStyle; [UserSetting("General", "Show Scene Info", "Toggle the display of information about selected meshes in the Scene View.")] static Pref s_ShowSceneInfo = new Pref("editor.showSceneInfo", false); [UserSetting("Toolbar", "Icon GUI", "Toggles the ProBuilder window interface between text and icon versions.")] internal static Pref s_IsIconGui = new Pref("editor.toolbarIconGUI", false); #if !SHORTCUT_MANAGER [UserSetting("Toolbar", "Unique Mode Shortcuts", "When off, the G key toggles between Object and Element modes and H enumerates the element modes. If on, G, H, J, and K are shortcuts to Object, Vertex, Edge, and Face modes respectively.")] internal static Pref s_UniqueModeShortcuts = new Pref("editor.uniqueModeShortcuts", false, SettingsScope.User); #endif [UserSetting("Mesh Editing", "Allow non-manifold actions", "Enables advanced mesh editing techniques that may create non-manifold geometry.")] internal static Pref s_AllowNonManifoldActions = new Pref("editor.allowNonManifoldActions", false, SettingsScope.User); [UserSetting("Toolbar", "Toolbar Location", "Where the Object, Face, Edge, and Vertex toolbar will be shown in the Scene View.")] static Pref s_SceneToolbarLocation = new Pref("editor.sceneToolbarLocation", SceneToolbarLocation.UpperCenter, SettingsScope.User); [UserSetting] static Pref s_PickingDistance = new Pref("picking.pickingDistance", 128f, SettingsScope.User); static Pref s_WindowIsFloating = new Pref("UnityEngine.ProBuilder.ProBuilderEditor-isUtilityWindow", false, SettingsScope.Project); internal Pref m_BackfaceSelectEnabled = new Pref("editor.backFaceSelectEnabled", false); internal Pref m_DragSelectRectMode = new Pref("editor.dragSelectRectMode", RectSelectMode.Partial); internal Pref m_SelectModifierBehavior = new Pref("editor.rectSelectModifier", SelectionModifierBehavior.Difference); Pref m_SelectMode = new Pref("editor.selectMode", SelectMode.Object); internal RectSelectMode rectSelectMode { get { return m_DragSelectRectMode.value; } set { if (m_DragSelectRectMode.value == value) return; m_DragSelectRectMode.SetValue(value, true); m_ScenePickerPreferences.rectSelectMode = value; } } internal SelectionModifierBehavior selectionModifierBehavior { get { return m_SelectModifierBehavior.value; } set { if (m_SelectModifierBehavior.value == value) return; m_SelectModifierBehavior.SetValue(value, true); m_ScenePickerPreferences.selectionModifierBehavior = value; } } internal bool backfaceSelectionEnabled { get { return m_BackfaceSelectEnabled.value; } set { if (value == m_BackfaceSelectEnabled.value) return; m_BackfaceSelectEnabled.SetValue(value, true); m_ScenePickerPreferences.cullMode = value ? CullingMode.None : CullingMode.Back; } } // used for 'g' key shortcut to swap between object/vef modes SelectMode m_LastComponentMode; #if !SHORTCUT_MANAGER [UserSetting] internal static Pref s_Shortcuts = new Pref("editor.sceneViewShortcuts", Shortcut.DefaultShortcuts().ToArray()); #endif GUIStyle m_CommandStyle; Rect m_ElementModeToolbarRect = new Rect(3, 6, 128, 24); int m_DefaultControl; SceneSelection m_Hovering = new SceneSelection(); SceneSelection m_HoveringPrevious = new SceneSelection(); ScenePickerPreferences m_ScenePickerPreferences; [UserSetting("Graphics", "Show Hover Highlight", "Highlight the mesh element nearest to the mouse cursor.")] static Pref s_ShowHoverHighlight = new Pref("editor.showPreselectionHighlight", true, SettingsScope.User); Tool m_CurrentTool = Tool.Move; Vector2 m_InitialMousePosition; Rect m_MouseDragRect; bool m_IsDragging; bool m_IsReadyForMouseDrag; // prevents leftClickUp from stealing focus after double click bool m_WasDoubleClick; // vertex handles #if !SHORTCUT_MANAGER bool m_IsRightMouseDown; #endif static Dictionary s_EditorTools = new Dictionary(); Vector3[][] m_VertexPositions; Vector3[] m_VertexOffset; GUIContent m_SceneInfo = new GUIContent(); Rect m_SceneInfoRect = new Rect(10, 10, 200, 40); #if !UNITY_2018_2_OR_NEWER static MethodInfo s_ResetOnSceneGUIState = null; #endif // All selected pb_Objects internal List selection { get { return MeshSelection.topInternal; } } Event m_CurrentEvent; internal bool isFloatingWindow { get; private set; } /// /// Get the current @"UnityEngine.ProBuilder.EditLevel". /// [Obsolete] internal static EditLevel editLevel { get { return s_Instance != null ? EditorUtility.GetEditLevel(instance.m_SelectMode) : EditLevel.Top; } } /// /// Get the current @"UnityEngine.ProBuilder.SelectMode". /// /// The ComponentMode currently set. [Obsolete] internal static ComponentMode componentMode { get { return s_Instance != null ? EditorUtility.GetComponentMode(instance.m_SelectMode) : ComponentMode.Face; } } /// /// Get and set the current SelectMode. /// public static SelectMode selectMode { get { if (s_Instance != null) return s_Instance.m_SelectMode; // for backwards compatibility reasons `Object` is returned when editor is closed return SelectMode.Object; } set { if (s_Instance == null) return; var previous = s_Instance.m_SelectMode.value; if (previous == value) return; s_Instance.m_SelectMode.SetValue(value, true); if (previous == SelectMode.Edge || previous == SelectMode.Vertex || previous == SelectMode.Face) s_Instance.m_LastComponentMode = previous; if (value == SelectMode.Object) Tools.current = s_Instance.m_CurrentTool; if (selectModeChanged != null) selectModeChanged(value); Refresh(); } } Stack m_SelectModeHistory = new Stack(); [UserSettingBlock(UserSettingsProvider.developerModeCategory)] static void PickingPreferences(string searchContext) { s_PickingDistance.value = SettingsGUILayout.SearchableSlider( new GUIContent("Picking Distance", "Distance to an object before it's considered hovered."), s_PickingDistance.value, 1, 150, searchContext); } internal static void PushSelectMode(SelectMode mode) { s_Instance.m_SelectModeHistory.Push(selectMode); selectMode = mode; } internal static void PopSelectMode() { if (s_Instance.m_SelectModeHistory.Count < 1) return; selectMode = s_Instance.m_SelectModeHistory.Pop(); } internal static void ResetToLastSelectMode() { selectMode = s_Instance.m_LastComponentMode; } static class SceneStyles { static bool s_Init = false; static GUIStyle s_SelectionRect; public static GUIStyle selectionRect { get { return s_SelectionRect; } } public static void Init() { if (s_Init) return; s_Init = true; s_SelectionRect = new GUIStyle() { normal = new GUIStyleState() { background = IconUtility.GetIcon("Scene/SelectionRect") }, border = new RectOffset(1, 1, 1, 1), margin = new RectOffset(0, 0, 0, 0), padding = new RectOffset(0, 0, 0, 0) }; } } /// /// Get the active ProBuilderEditor window. Null if no instance is open. /// public static ProBuilderEditor instance { get { return s_Instance; } } internal static void MenuOpenWindow() { ProBuilderEditor editor = (ProBuilderEditor)EditorWindow.GetWindow(typeof(ProBuilderEditor), s_WindowIsFloating, PreferenceKeys.pluginTitle, true); // open as floating window editor.isFloatingWindow = s_WindowIsFloating; } void OnBecameVisible() { // fixes maximizing/unmaximizing s_Instance = this; } void OnEnable() { s_Instance = this; #if UNITY_2019_1_OR_NEWER SceneView.duringSceneGui += OnSceneGUI; #else SceneView.onSceneGUIDelegate += OnSceneGUI; #endif ProGridsInterface.SubscribePushToGridEvent(PushToGrid); ProGridsInterface.SubscribeToolbarEvent(ProGridsToolbarOpen); MeshSelection.objectSelectionChanged += OnObjectSelectionChanged; ProGridsToolbarOpen(ProGridsInterface.SceneToolbarIsExtended()); #if !UNITY_2018_2_OR_NEWER s_ResetOnSceneGUIState = typeof(SceneView).GetMethod("ResetOnSceneGUIState", BindingFlags.Instance | BindingFlags.NonPublic); #endif VertexManipulationTool.beforeMeshModification += BeforeMeshModification; VertexManipulationTool.afterMeshModification += AfterMeshModification; LoadSettings(); InitGUI(); UpdateSelection(); SetOverrideWireframe(true); if (selectModeChanged != null) selectModeChanged(selectMode); } void OnDisable() { s_Instance = null; VertexManipulationTool.beforeMeshModification -= BeforeMeshModification; VertexManipulationTool.afterMeshModification -= AfterMeshModification; if (s_EditorToolbar != null) DestroyImmediate(s_EditorToolbar); ClearElementSelection(); UpdateSelection(); if (selectionUpdated != null) selectionUpdated(null); #if UNITY_2019_1_OR_NEWER SceneView.duringSceneGui -= OnSceneGUI; #else SceneView.onSceneGUIDelegate -= OnSceneGUI; #endif ProGridsInterface.UnsubscribePushToGridEvent(PushToGrid); ProGridsInterface.UnsubscribeToolbarEvent(ProGridsToolbarOpen); MeshSelection.objectSelectionChanged -= OnObjectSelectionChanged; SetOverrideWireframe(false); if (selectModeChanged != null) selectModeChanged(SelectMode.Object); SceneView.RepaintAll(); } void BeforeMeshModification(IEnumerable meshes) { if(beforeMeshModification != null) beforeMeshModification(meshes); } void AfterMeshModification(IEnumerable meshes) { if(afterMeshModification != null) afterMeshModification(meshes); } internal static void ReloadSettings() { if (s_Instance != null) s_Instance.LoadSettings(); SceneView.RepaintAll(); } void LoadSettings() { EditorMeshHandles.ResetPreferences(); m_ScenePickerPreferences = new ScenePickerPreferences() { offPointerMultiplier = s_PickingDistance * k_OffPointerMultiplierPercent, maxPointerDistance = s_PickingDistance, cullMode = m_BackfaceSelectEnabled ? CullingMode.None : CullingMode.Back, selectionModifierBehavior = m_SelectModifierBehavior, rectSelectMode = m_DragSelectRectMode }; #if !SHORTCUT_MANAGER // workaround for old single-key shortcuts if (s_Shortcuts.value == null || s_Shortcuts.value.Length < 1) s_Shortcuts.SetValue(Shortcut.DefaultShortcuts().ToArray(), true); #endif } void InitGUI() { if (s_EditorToolbar != null) UObject.DestroyImmediate(s_EditorToolbar); s_EditorToolbar = ScriptableObject.CreateInstance(); s_EditorToolbar.hideFlags = HideFlags.HideAndDontSave; s_EditorToolbar.InitWindowProperties(this); VertexTranslationInfoStyle = new GUIStyle(); VertexTranslationInfoStyle.normal.background = EditorGUIUtility.whiteTexture; VertexTranslationInfoStyle.normal.textColor = new Color(1f, 1f, 1f, .6f); VertexTranslationInfoStyle.padding = new RectOffset(3, 3, 3, 0); } /// /// Rebuild the mesh wireframe and selection caches. /// public static void Refresh(bool vertexCountChanged = true) { if (instance != null) { instance.UpdateSelection(vertexCountChanged); SceneView.RepaintAll(); } } void OnGUI() { if (m_CommandStyle == null) m_CommandStyle = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).FindStyle("Command"); Event e = Event.current; switch (e.type) { case EventType.ContextClick: var menu = new GenericMenu(); AddItemsToMenu(menu); menu.ShowAsContext(); break; #if !SHORTCUT_MANAGER case EventType.KeyDown: if (s_Shortcuts.value.Any(x => x.Matches(e.keyCode, e.modifiers))) e.Use(); break; case EventType.KeyUp: ShortcutCheck(e); break; #else case EventType.KeyUp: if (e.keyCode == KeyCode.Escape) { selectMode = SelectMode.Object; e.Use(); } break; #endif } if (s_EditorToolbar != null) { s_EditorToolbar.OnGUI(); } else { try { InitGUI(); } catch (System.Exception exception) { Debug.LogWarning(string.Format("Failed initializing ProBuilder Toolbar:\n{0}", exception.ToString())); } } } void Menu_ToggleIconMode() { s_IsIconGui.value = !s_IsIconGui.value; if (s_EditorToolbar != null) DestroyImmediate(s_EditorToolbar); s_EditorToolbar = ScriptableObject.CreateInstance(); s_EditorToolbar.hideFlags = HideFlags.HideAndDontSave; s_EditorToolbar.InitWindowProperties(this); } public void AddItemsToMenu(GenericMenu menu) { bool floating = s_WindowIsFloating; menu.AddItem(new GUIContent("Window/Open as Floating Window", ""), floating, () => SetIsUtilityWindow(true)); menu.AddItem(new GUIContent("Window/Open as Dockable Window", ""), !floating, () => SetIsUtilityWindow(false)); menu.AddSeparator(""); menu.AddItem(new GUIContent("Use Icon Mode", ""), s_IsIconGui, Menu_ToggleIconMode); menu.AddItem(new GUIContent("Use Text Mode", ""), !s_IsIconGui, Menu_ToggleIconMode); } void SetIsUtilityWindow(bool isUtilityWindow) { s_WindowIsFloating.value = isUtilityWindow; var windowTitle = titleContent; Close(); var res = GetWindow(GetType(), isUtilityWindow); res.titleContent = windowTitle; } internal static VertexManipulationTool activeTool { get { return s_Instance == null ? null : s_Instance.GetToolForSelectMode(s_Instance.m_CurrentTool, s_Instance.m_SelectMode); } } VertexManipulationTool GetTool() where T : VertexManipulationTool, new() { VertexManipulationTool tool; if (s_EditorTools.TryGetValue(typeof(T), out tool)) return tool; tool = new T(); s_EditorTools.Add(typeof(T), tool); return tool; } VertexManipulationTool GetToolForSelectMode(Tool tool, SelectMode mode) { switch (tool) { case Tool.Move: return mode.IsTextureMode() ? GetTool() : GetTool(); case Tool.Rotate: return mode.IsTextureMode() ? GetTool() : GetTool(); case Tool.Scale: return mode.IsTextureMode() ? GetTool() : GetTool(); default: return null; } } void OnSceneGUI(SceneView sceneView) { #if !UNITY_2018_2_OR_NEWER if (s_ResetOnSceneGUIState != null) s_ResetOnSceneGUIState.Invoke(sceneView, null); #endif SceneStyles.Init(); m_CurrentEvent = Event.current; EditorMeshHandles.DrawSceneHandles(SceneDragAndDropListener.isDragging ? SelectMode.None : selectMode); DrawHandleGUI(sceneView); #if SHORTCUT_MANAGER // Escape isn't assignable as a shortcut if (m_CurrentEvent.type == EventType.KeyDown) { if (m_CurrentEvent.keyCode == KeyCode.Escape && selectMode != SelectMode.Object) { selectMode = SelectMode.Object; m_CurrentEvent.Use(); } } #else if (m_CurrentEvent.type == EventType.MouseDown && m_CurrentEvent.button == 1) m_IsRightMouseDown = true; if (m_CurrentEvent.type == EventType.MouseUp && m_CurrentEvent.button == 1 || m_CurrentEvent.type == EventType.Ignore) m_IsRightMouseDown = false; if (!m_IsRightMouseDown && (m_CurrentEvent.type == EventType.KeyUp ? m_CurrentEvent.keyCode : KeyCode.None) != KeyCode.None) { if (ShortcutCheck(m_CurrentEvent)) { m_CurrentEvent.Use(); return; } } if (m_CurrentEvent.type == EventType.KeyDown) { if (s_Shortcuts.value.Any(x => x.Matches(m_CurrentEvent.keyCode, m_CurrentEvent.modifiers))) m_CurrentEvent.Use(); } #endif if (selectMode == SelectMode.Object) return; // Check mouse position in scene and determine if we should highlight something if (s_ShowHoverHighlight && m_CurrentEvent.type == EventType.MouseMove && selectMode.IsMeshElementMode()) { m_Hovering.CopyTo(m_HoveringPrevious); if (GUIUtility.hotControl == 0) EditorSceneViewPicker.MouseRayHitTest(m_CurrentEvent.mousePosition, selectMode, m_ScenePickerPreferences, m_Hovering); else m_Hovering.Clear(); if (!m_Hovering.Equals(m_HoveringPrevious)) SceneView.RepaintAll(); } if (Tools.current == Tool.View) return; // Overrides the toolbar transform tools if (Tools.current != Tool.None && Tools.current != m_CurrentTool) SetTool_Internal(Tools.current); Tools.current = Tool.None; if (selectMode.IsMeshElementMode() && MeshSelection.selectedVertexCount > 0) { var tool = GetToolForSelectMode(m_CurrentTool, m_SelectMode); if (tool != null) tool.OnSceneGUI(m_CurrentEvent); } if (EditorHandleUtility.SceneViewInUse(m_CurrentEvent) || m_CurrentEvent.isKey) { m_IsDragging = false; return; } // This prevents us from selecting other objects in the scene, // and allows for the selection of faces / vertices. m_DefaultControl = GUIUtility.GetControlID(FocusType.Passive); HandleUtility.AddDefaultControl(m_DefaultControl); if (m_CurrentEvent.type == EventType.MouseDown) { // double clicking object if (m_CurrentEvent.clickCount > 1) { DoubleClick(m_CurrentEvent); } m_InitialMousePosition = m_CurrentEvent.mousePosition; // readyForMouseDrag prevents a bug wherein after ending a drag an errant // MouseDrag event is sent with no corresponding MouseDown/MouseUp event. m_IsReadyForMouseDrag = true; } if (m_CurrentEvent.type == EventType.MouseDrag && m_IsReadyForMouseDrag) { if (!m_IsDragging && Vector2.Distance(m_CurrentEvent.mousePosition, m_InitialMousePosition) > k_MouseDragThreshold) { sceneView.Repaint(); m_IsDragging = true; } } if (m_CurrentEvent.type == EventType.Ignore) { if (m_IsDragging) { m_IsReadyForMouseDrag = false; m_IsDragging = false; EditorSceneViewPicker.DoMouseDrag(m_MouseDragRect, selectMode, m_ScenePickerPreferences); } if (m_WasDoubleClick) m_WasDoubleClick = false; } if (m_CurrentEvent.type == EventType.MouseUp) { if (m_WasDoubleClick) { m_WasDoubleClick = false; } else { if (!m_IsDragging) { if (UVEditor.instance) UVEditor.instance.ResetUserPivot(); EditorSceneViewPicker.DoMouseClick(m_CurrentEvent, selectMode, m_ScenePickerPreferences); UpdateSelection(); SceneView.RepaintAll(); } else { m_IsDragging = false; m_IsReadyForMouseDrag = false; if (UVEditor.instance) UVEditor.instance.ResetUserPivot(); EditorSceneViewPicker.DoMouseDrag(m_MouseDragRect, selectMode, m_ScenePickerPreferences); } } } } void DoubleClick(Event e) { var mesh = EditorSceneViewPicker.DoMouseClick(m_CurrentEvent, selectMode, m_ScenePickerPreferences); if (mesh != null) { if (selectMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge)) { if (e.shift) EditorUtility.ShowNotification(EditorToolbarLoader.GetInstance().DoAction()); else EditorUtility.ShowNotification(EditorToolbarLoader.GetInstance().DoAction()); } else if (selectMode.ContainsFlag(SelectMode.Face | SelectMode.TextureFace)) { if ((e.modifiers & (EventModifiers.Control | EventModifiers.Shift)) == (EventModifiers.Control | EventModifiers.Shift)) Actions.SelectFaceRing.MenuRingAndLoopFaces(MeshSelection.topInternal); else if (e.control) EditorUtility.ShowNotification(EditorToolbarLoader.GetInstance().DoAction()); else if (e.shift) EditorUtility.ShowNotification(EditorToolbarLoader.GetInstance().DoAction()); else mesh.SetSelectedFaces(mesh.facesInternal); } else { mesh.SetSelectedFaces(mesh.facesInternal); } UpdateSelection(); SceneView.RepaintAll(); m_WasDoubleClick = true; } } void DrawHandleGUI(SceneView sceneView) { if (sceneView != SceneView.lastActiveSceneView) return; if (m_CurrentEvent.type == EventType.Repaint && !SceneDragAndDropListener.isDragging && m_Hovering != null && GUIUtility.hotControl == 0 && HandleUtility.nearestControl == m_DefaultControl && selectMode.IsMeshElementMode()) { try { EditorMeshHandles.DrawSceneSelection(m_Hovering); } catch { // this happens on undo, when c++ object is destroyed but c# side thinks it's still alive } } using (new HandleGUI()) { int screenWidth = (int)sceneView.position.width; int screenHeight = (int)sceneView.position.height; switch ((SceneToolbarLocation)s_SceneToolbarLocation) { case SceneToolbarLocation.BottomCenter: m_ElementModeToolbarRect.x = (screenWidth / 2 - 64); m_ElementModeToolbarRect.y = screenHeight - m_ElementModeToolbarRect.height * 3; break; case SceneToolbarLocation.BottomLeft: m_ElementModeToolbarRect.x = 12; m_ElementModeToolbarRect.y = screenHeight - m_ElementModeToolbarRect.height * 3; break; case SceneToolbarLocation.BottomRight: m_ElementModeToolbarRect.x = screenWidth - (m_ElementModeToolbarRect.width + 12); m_ElementModeToolbarRect.y = screenHeight - m_ElementModeToolbarRect.height * 3; break; case SceneToolbarLocation.UpperLeft: m_ElementModeToolbarRect.x = 12; m_ElementModeToolbarRect.y = 10; break; case SceneToolbarLocation.UpperRight: m_ElementModeToolbarRect.x = screenWidth - (m_ElementModeToolbarRect.width + 96); m_ElementModeToolbarRect.y = 10; break; default: case SceneToolbarLocation.UpperCenter: m_ElementModeToolbarRect.x = (screenWidth / 2 - 64); m_ElementModeToolbarRect.y = 10; break; } selectMode = UI.EditorGUIUtility.DoElementModeToolbar(m_ElementModeToolbarRect, selectMode); if (s_ShowSceneInfo) { Vector2 size = UI.EditorStyles.sceneTextBox.CalcSize(m_SceneInfo); m_SceneInfoRect.width = size.x; m_SceneInfoRect.height = size.y; GUI.Label(m_SceneInfoRect, m_SceneInfo, UI.EditorStyles.sceneTextBox); } if (m_IsDragging) { if (m_CurrentEvent.type == EventType.Repaint) { // Always draw from lowest to largest values var start = Vector2.Min(m_InitialMousePosition, m_CurrentEvent.mousePosition); var end = Vector2.Max(m_InitialMousePosition, m_CurrentEvent.mousePosition); m_MouseDragRect = new Rect(start.x, start.y, end.x - start.x, end.y - start.y); SceneStyles.selectionRect.Draw(m_MouseDragRect, false, false, false, false); } else if (m_CurrentEvent.isMouse) { HandleUtility.Repaint(); } } } } #if !SHORTCUT_MANAGER internal bool ShortcutCheck(Event e) { List matches = s_Shortcuts.value.Where(x => x.Matches(e.keyCode, e.modifiers)).ToList(); if (matches.Count < 1) return false; bool used = false; Shortcut usedShortcut = null; foreach (Shortcut cut in matches) { if (AllLevelShortcuts(cut)) { used = true; usedShortcut = cut; break; } } if (!used) { foreach (Shortcut cut in matches) used |= GeoLevelShortcuts(cut); } if (used) Event.current.Use(); if (usedShortcut != null) EditorUtility.ShowNotification(usedShortcut.action); return used; } bool AllLevelShortcuts(Shortcut shortcut) { switch (shortcut.action) { // TODO Remove once a workaround for non-upper-case shortcut chars is found case "Toggle Geometry Mode": if (selectMode == SelectMode.Object) selectMode = m_LastComponentMode; else selectMode = SelectMode.Object; EditorUtility.ShowNotification(selectMode.ToString() + " Editing"); return true; case "Vertex Mode": { if (!s_UniqueModeShortcuts) return false; selectMode = SelectMode.Vertex; return true; } case "Edge Mode": { if (!s_UniqueModeShortcuts) return false; selectMode = SelectMode.Edge; return true; } case "Face Mode": { if (!s_UniqueModeShortcuts) return false; selectMode = SelectMode.Face; return true; } default: return false; } } bool GeoLevelShortcuts(Shortcut shortcut) { switch (shortcut.action) { case "Escape": ClearElementSelection(); EditorUtility.ShowNotification("Top Level"); UpdateSelection(); selectMode = SelectMode.Object; return true; // Used to be (incorrectly) named handle pivot, and since shortcuts are serialized this value is still valid case "Toggle Handle Pivot": case "Toggle Handle Orientation": VertexManipulationTool.handleOrientation = InternalUtility.NextEnumValue(VertexManipulationTool.handleOrientation); return true; // TODO Remove once a workaround for non-upper-case shortcut chars is found case "Toggle Selection Mode": if (s_UniqueModeShortcuts) return false; ToggleSelectionMode(); EditorUtility.ShowNotification(selectMode.ToString()); return true; case "Delete Face": EditorUtility.ShowNotification(EditorToolbarLoader.GetInstance().DoAction().notification); return true; case "Set Pivot": if (selection.Count > 0) { foreach (ProBuilderMesh pbo in selection) { UndoUtility.RecordObjects(new UObject[2] { pbo, pbo.transform }, "Set Pivot"); if (pbo.selectedIndexesInternal.Length > 0) { pbo.CenterPivot(pbo.selectedIndexesInternal); } else { pbo.CenterPivot(null); } } EditorUtility.ShowNotification("Set Pivot"); } return true; default: return false; } } #endif /// /// Allows another window to tell the Editor what Tool is now in use. Does *not* update any other windows. /// /// internal void SetTool(Tool newTool) { m_CurrentTool = newTool; } /// /// Calls SetTool(), then Updates the UV Editor window if applicable. /// /// void SetTool_Internal(Tool newTool) { SetTool(newTool); if (UVEditor.instance != null) UVEditor.instance.SetTool(newTool); } /// /// Toggles between the SelectMode values and updates the graphic handles as necessary. /// internal void ToggleSelectionMode() { if (m_SelectMode == SelectMode.Vertex) selectMode = SelectMode.Edge; else if (m_SelectMode == SelectMode.Edge) selectMode = SelectMode.Face; else if (m_SelectMode == SelectMode.Face) selectMode = SelectMode.Vertex; } void UpdateSelection(bool selectionChanged = true) { UpdateMeshHandles(selectionChanged); if (selectionChanged) { UpdateSceneInfo(); MeshSelection.OnComponentSelectionChanged(); } if (selectionUpdated != null) selectionUpdated(selection); } internal static void UpdateMeshHandles(bool selectionOrVertexCountChanged = true) { if (!s_Instance) return; try { EditorMeshHandles.RebuildSelectedHandles(MeshSelection.topInternal, selectMode, selectionOrVertexCountChanged); } catch { // happens on undo when c++ object is gone but c# isn't in the know EditorMeshHandles.ClearHandles(); } } void UpdateSceneInfo() { m_SceneInfo.text = string.Format( "Faces: {0}\nTriangles: {1}\nVertices: {2} ({3})\n\nSelected Faces: {4}\nSelected Edges: {5}\nSelected Vertices: {6} ({7})", MeshSelection.totalFaceCount.ToString(), MeshSelection.totalTriangleCountCompiled.ToString(), MeshSelection.totalCommonVertexCount.ToString(), MeshSelection.totalVertexCountOptimized.ToString(), MeshSelection.selectedFaceCount.ToString(), MeshSelection.selectedEdgeCount.ToString(), MeshSelection.selectedSharedVertexCount.ToString(), MeshSelection.selectedVertexCount.ToString()); } internal void ClearElementSelection() { foreach (ProBuilderMesh pb in selection) pb.ClearSelection(); m_Hovering.Clear(); } /// /// If dragging a texture aroudn, this method ensures that if it's a member of a texture group it's cronies are also selected /// void VerifyTextureGroupSelection() { bool selectionModified = false; foreach (ProBuilderMesh mesh in selection) { List alreadyChecked = new List(); foreach (Face f in mesh.selectedFacesInternal) { int tg = f.textureGroup; if (tg > 0 && !alreadyChecked.Contains(f.textureGroup)) { foreach (Face j in mesh.facesInternal) { if (j != f && j.textureGroup == tg && !mesh.selectedFacesInternal.Contains(j)) { List newFaceSection = new List(); foreach (Face jf in mesh.facesInternal) if (jf.textureGroup == tg) newFaceSection.Add(jf); mesh.SetSelectedFaces(newFaceSection.ToArray()); selectionModified = true; break; } } } alreadyChecked.Add(f.textureGroup); } } if (selectionModified) UpdateSelection(true); } void OnObjectSelectionChanged() { m_Hovering.Clear(); UpdateSelection(); SetOverrideWireframe(true); } /// /// Hide the default unity wireframe renderer /// void SetOverrideWireframe(bool overrideWireframe) { const EditorSelectedRenderState k_DefaultSelectedRenderState = EditorSelectedRenderState.Highlight | EditorSelectedRenderState.Wireframe; foreach (var mesh in Selection.transforms.GetComponents()) { // Disable Wireframe for meshes when ProBuilder is active EditorUtility.SetSelectionRenderState( mesh.renderer, overrideWireframe ? k_DefaultSelectedRenderState & ~(EditorSelectedRenderState.Wireframe) : k_DefaultSelectedRenderState); EditorUtility.SynchronizeWithMeshFilter(mesh); } SceneView.RepaintAll(); } /// /// Called from ProGrids. /// /// void PushToGrid(float snapVal) { UndoUtility.RecordSelection(selection.ToArray(), "Push elements to Grid"); if (selectMode == SelectMode.Object || selectMode == SelectMode.None) return; for (int i = 0, c = MeshSelection.selectedObjectCount; i < c; i++) { ProBuilderMesh mesh = selection[i]; if (mesh.selectedVertexCount < 1) continue; var indexes = mesh.GetCoincidentVertices(mesh.selectedIndexesInternal); Snapping.SnapVertices(mesh, indexes, Vector3.one * snapVal); mesh.ToMesh(); mesh.Refresh(); mesh.Optimize(); } UpdateSelection(); } void ProGridsToolbarOpen(bool menuOpen) { bool active = ProGridsInterface.ProGridsActive(); m_SceneInfoRect.y = active && !menuOpen ? 28 : 10; m_SceneInfoRect.x = active ? (menuOpen ? 64 : 8) : 10; } /// /// A tool, any tool, has just been engaged while in texture mode /// internal void OnBeginTextureModification() { VerifyTextureGroupSelection(); } /// /// Returns the first selected pb_Object and pb_Face, or false if not found. /// /// /// /// internal bool GetFirstSelectedFace(out ProBuilderMesh pb, out Face face) { pb = null; face = null; if (selection.Count < 1) return false; pb = selection.FirstOrDefault(x => x.selectedFaceCount > 0); if (pb == null) return false; face = pb.selectedFacesInternal[0]; return true; } } }