void LoadSettings()
        {
            EditorApplication.delayCall += EditorHandleDrawing.ResetPreferences;

            m_ScenePickerPreferences = new ScenePickerPreferences()
            {
                cullMode = m_BackfaceSelectEnabled ? CullingMode.None : CullingMode.Back,
                selectionModifierBehavior = m_SelectModifierBehavior,
                rectSelectMode            = m_DragSelectRectMode
            };
        }
        static float VertexRaycast(Vector3 mousePosition, ScenePickerPreferences pickerOptions, bool allowUnselected, SceneSelection selection)
        {
            Camera cam = SceneView.lastActiveSceneView.camera;

            selection.Clear();
            s_NearestVertices.Clear();
            selection.gameObject = HandleUtility.PickGameObject(mousePosition, false);
            float          maxDistance = ScenePickerPreferences.maxPointerDistance * ScenePickerPreferences.maxPointerDistance;
            ProBuilderMesh hoveredMesh = selection.gameObject != null?selection.gameObject.GetComponent <ProBuilderMesh>() : null;

            if (allowUnselected && selection.gameObject != null)
            {
                if (hoveredMesh != null && hoveredMesh.selectable && !MeshSelection.Contains(hoveredMesh))
                {
                    GetNearestVertices(hoveredMesh, mousePosition, s_NearestVertices, maxDistance, 1);
                }
            }

            if (selection.mesh == null)
            {
                foreach (var mesh in MeshSelection.topInternal)
                {
                    if (!mesh.selectable)
                    {
                        continue;
                    }

                    GetNearestVertices(mesh, mousePosition, s_NearestVertices, maxDistance, hoveredMesh == mesh || hoveredMesh == null ? 1.0f : ScenePickerPreferences.offPointerMultiplier);
                }
            }

            s_NearestVertices.Sort((x, y) => x.screenDistance.CompareTo(y.screenDistance));

            for (int i = 0; i < s_NearestVertices.Count; i++)
            {
                if (!UnityEngine.ProBuilder.HandleUtility.PointIsOccluded(cam, s_NearestVertices[i].mesh, s_NearestVertices[i].worldPosition))
                {
                    selection.gameObject = s_NearestVertices[i].mesh.gameObject;
                    selection.mesh       = s_NearestVertices[i].mesh;
                    selection.SetSingleVertex(s_NearestVertices[i].vertex);

                    return(Mathf.Sqrt(s_NearestVertices[i].screenDistance));
                }
            }

            return(Mathf.Infinity);
        }
Ejemplo n.º 3
0
        void LoadSettings()
        {
            EditorMeshHandles.ResetPreferences();

            m_ScenePickerPreferences = new ScenePickerPreferences()
            {
                maxPointerDistance        = ScenePickerPreferences.maxPointerDistanceFuzzy,
                cullMode                  = m_BackfaceSelectEnabled ? CullingMode.None : CullingMode.Back,
                selectionModifierBehavior = m_SelectModifierBehavior,
                rectSelectMode            = m_DragSelectRectMode
            };

            // workaround for old single-key shortcuts
            if (s_Shortcuts.value == null || s_Shortcuts.value.Length < 1)
            {
                s_Shortcuts.SetValue(Shortcut.DefaultShortcuts().ToArray(), true);
            }
        }
Ejemplo n.º 4
0
        // Get the object & mesh selection that the mouse is currently nearest.
        // A ProBuilderMesh is returned because double click actions need to know what the last selected pb_Object was.
        // If deepClickOffset is specified, the object + deepClickOffset in the deep select stack will be returned (instead of next).
        internal static float MouseRayHitTest(
            Vector3 mousePosition,
            SelectMode selectionMode,
            ScenePickerPreferences pickerOptions,
            SceneSelection selection,
            bool allowUnselected = false)
        {
            if (selectionMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge))
            {
                return(EdgeRaycast(mousePosition, pickerOptions, allowUnselected, selection));
            }

            if (selectionMode.ContainsFlag(SelectMode.Vertex | SelectMode.TextureVertex))
            {
                return(VertexRaycast(mousePosition, pickerOptions, allowUnselected, selection));
            }

            return(FaceRaycast(mousePosition, pickerOptions, allowUnselected, selection, 0, true));
        }
Ejemplo n.º 5
0
        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
        }
Ejemplo n.º 6
0
        static float EdgeRaycast(Vector3 mousePosition, ScenePickerPreferences pickerPrefs, bool allowUnselected, SceneSelection selection)
        {
            selection.Clear();
            selection.gameObject = UHandleUtility.PickGameObject(mousePosition, false);
            var hoveredMesh = selection.gameObject != null?selection.gameObject.GetComponent <ProBuilderMesh>() : null;

            float bestDistance           = pickerPrefs.maxPointerDistance;
            float unselectedBestDistance = bestDistance;
            bool  hoveredIsInSelection   = MeshSelection.topInternal.Contains(hoveredMesh);

            if (hoveredMesh != null && (allowUnselected || hoveredIsInSelection))
            {
                var tup = GetNearestEdgeOnMesh(hoveredMesh, mousePosition);

                if (tup.edge.IsValid() && tup.distance < pickerPrefs.maxPointerDistance)
                {
                    selection.gameObject   = hoveredMesh.gameObject;
                    selection.mesh         = hoveredMesh;
                    selection.edge         = tup.edge;
                    unselectedBestDistance = tup.distance;

                    // if it's in the selection, it automatically wins as best. if not, treat this is a fallback.
                    if (hoveredIsInSelection)
                    {
                        return(tup.distance);
                    }
                }
            }

            foreach (var mesh in MeshSelection.topInternal)
            {
                var trs       = mesh.transform;
                var positions = mesh.positionsInternal;

                foreach (var face in mesh.facesInternal)
                {
                    foreach (var edge in face.edges)
                    {
                        int x = edge.a;
                        int y = edge.b;

                        float d = UHandleUtility.DistanceToLine(
                            trs.TransformPoint(positions[x]),
                            trs.TransformPoint(positions[y]));

                        if (d < bestDistance)
                        {
                            selection.gameObject = mesh.gameObject;
                            selection.mesh       = mesh;
                            selection.edge       = new Edge(x, y);
                            bestDistance         = d;
                        }
                    }
                }
            }

            if (selection.gameObject != null)
            {
                if (bestDistance < pickerPrefs.maxPointerDistance)
                {
                    return(bestDistance);
                }
                return(unselectedBestDistance);
            }

            return(Mathf.Infinity);
        }
Ejemplo n.º 7
0
        static float VertexRaycast(Vector3 mousePosition, ScenePickerPreferences pickerOptions, bool allowUnselected, SceneSelection selection)
        {
            Camera cam = SceneView.lastActiveSceneView.camera;

            selection.Clear();
            s_NearestVertices.Clear();
            selection.gameObject = HandleUtility.PickGameObject(mousePosition, false);
            float maxDistance = pickerOptions.maxPointerDistance * pickerOptions.maxPointerDistance;

            if (allowUnselected && selection.gameObject != null)
            {
                var mesh = selection.gameObject.GetComponent <ProBuilderMesh>();

                if (mesh != null && mesh.selectable && !MeshSelection.Contains(mesh))
                {
                    var matches = GetNearestVertices(mesh, mousePosition, s_NearestVertices, maxDistance);

                    for (int i = 0; i < matches; i++)
                    {
                        // Append `maxDistance` so that selected meshes are favored
                        s_NearestVertices[i] = new VertexPickerEntry()
                        {
                            mesh           = s_NearestVertices[i].mesh,
                            vertex         = s_NearestVertices[i].vertex,
                            screenDistance = s_NearestVertices[i].screenDistance + maxDistance,
                            worldPosition  = s_NearestVertices[i].worldPosition
                        };
                    }
                }
            }

            if (selection.mesh == null)
            {
                foreach (var mesh in MeshSelection.topInternal)
                {
                    if (!mesh.selectable)
                    {
                        continue;
                    }

                    GetNearestVertices(mesh, mousePosition, s_NearestVertices, maxDistance);
                }
            }

            s_NearestVertices.Sort((x, y) => x.screenDistance.CompareTo(y.screenDistance));

            for (int i = 0; i < s_NearestVertices.Count; i++)
            {
                if (!UnityEngine.ProBuilder.HandleUtility.PointIsOccluded(cam, s_NearestVertices[i].mesh, s_NearestVertices[i].worldPosition))
                {
                    selection.gameObject = s_NearestVertices[i].mesh.gameObject;
                    selection.mesh       = s_NearestVertices[i].mesh;
                    selection.vertex     = s_NearestVertices[i].vertex;

                    // If mesh was unselected, remove the distance modifier
                    if (s_NearestVertices[i].screenDistance > maxDistance)
                    {
                        return(Mathf.Sqrt(s_NearestVertices[i].screenDistance - maxDistance));
                    }

                    return(Mathf.Sqrt(s_NearestVertices[i].screenDistance));
                }
            }

            return(Mathf.Infinity);
        }
Ejemplo n.º 8
0
        static float FaceRaycast(Vector3 mousePosition,
                                 ScenePickerPreferences pickerOptions,
                                 bool allowUnselected,
                                 SceneSelection selection,
                                 int deepClickOffset = 0,
                                 bool isPreview      = true)
        {
            GameObject     pickedGo   = null;
            ProBuilderMesh pickedPb   = null;
            Face           pickedFace = null;

            int newHash = 0;

            // If any event modifiers are engaged don't cycle the deep click
            EventModifiers em = Event.current.modifiers;

            if (isPreview || em != EventModifiers.None)
            {
                EditorHandleUtility.GetHovered(mousePosition, s_OverlappingGameObjects);
            }
            else
            {
                EditorHandleUtility.GetAllOverlapping(mousePosition, s_OverlappingGameObjects);
            }

            selection.Clear();

            float distance = Mathf.Infinity;

            for (int i = 0, next = 0, pickedCount = s_OverlappingGameObjects.Count; i < pickedCount; i++)
            {
                var  go   = s_OverlappingGameObjects[i];
                var  mesh = go.GetComponent <ProBuilderMesh>();
                Face face = null;

                if (mesh != null && (allowUnselected || MeshSelection.topInternal.Contains(mesh)))
                {
                    Ray        ray = UHandleUtility.GUIPointToWorldRay(mousePosition);
                    RaycastHit hit;

                    if (UnityEngine.ProBuilder.HandleUtility.FaceRaycast(ray,
                                                                         mesh,
                                                                         out hit,
                                                                         Mathf.Infinity,
                                                                         pickerOptions.cullMode))
                    {
                        face     = mesh.facesInternal[hit.face];
                        distance = Vector2.SqrMagnitude(((Vector2)mousePosition) - HandleUtility.WorldToGUIPoint(mesh.transform.TransformPoint(hit.point)));
                    }
                }

                // pb_Face doesn't define GetHashCode, meaning it falls to object.GetHashCode (reference comparison)
                int hash = face == null?go.GetHashCode() : face.GetHashCode();

                if (s_DeepSelectionPrevious == hash)
                {
                    next = (i + (1 + deepClickOffset)) % pickedCount;
                }

                if (next == i)
                {
                    pickedGo   = go;
                    pickedPb   = mesh;
                    pickedFace = face;

                    newHash = hash;

                    // a prior hash was matched, this is the next. if
                    // it's just the first iteration don't break (but do
                    // set the default).
                    if (next != 0)
                    {
                        break;
                    }
                }
            }

            if (!isPreview)
            {
                s_DeepSelectionPrevious = newHash;
            }

            if (pickedGo != null)
            {
                Event.current.Use();

                if (pickedPb != null)
                {
                    if (pickedPb.selectable)
                    {
                        selection.gameObject = pickedGo;
                        selection.mesh       = pickedPb;
                        selection.face       = pickedFace;

                        return(Mathf.Sqrt(distance));
                    }
                }

                // If clicked off a pb_Object but onto another gameobject, set the selection
                // and dip out.
                selection.gameObject = pickedGo;
                return(Mathf.Sqrt(distance));
            }

            return(distance);
        }
Ejemplo n.º 9
0
        public static ProBuilderMesh DoMouseClick(Event evt, SelectMode selectionMode, ScenePickerPreferences pickerPreferences)
        {
            bool appendModifier = EditorHandleUtility.IsAppendModifier(evt.modifiers);

            if (!appendModifier)
            {
                MeshSelection.SetSelection((GameObject)null);
            }

            float pickedElementDistance = Mathf.Infinity;

            if (selectionMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge))
            {
                pickedElementDistance = EdgeRaycast(evt.mousePosition, pickerPreferences, true, s_Selection);
            }
            else if (selectionMode.ContainsFlag(SelectMode.Vertex | SelectMode.TextureVertex))
            {
                pickedElementDistance = VertexRaycast(evt.mousePosition, pickerPreferences, true, s_Selection);
            }
            else
            {
                pickedElementDistance = FaceRaycast(evt.mousePosition, pickerPreferences, true, s_Selection, evt.clickCount > 1 ? -1 : 0, false);
            }

            evt.Use();

            if (pickedElementDistance > pickerPreferences.maxPointerDistance)
            {
                if (appendModifier && Selection.gameObjects.Contains(s_Selection.gameObject))
                {
                    MeshSelection.RemoveFromSelection(s_Selection.gameObject);
                }
                else
                {
                    MeshSelection.AddToSelection(s_Selection.gameObject);
                }

                return(null);
            }

            MeshSelection.AddToSelection(s_Selection.gameObject);

            if (s_Selection.mesh != null)
            {
                var mesh = s_Selection.mesh;

                if (s_Selection.face != null)
                {
                    // Check for other editor mouse shortcuts first (todo proper event handling for mouse shortcuts)
                    MaterialEditor matEditor = MaterialEditor.instance;

                    if (matEditor != null && matEditor.ClickShortcutCheck(Event.current.modifiers, mesh, s_Selection.face))
                    {
                        return(null);
                    }

                    UVEditor uvEditor = UVEditor.instance;

                    if (uvEditor != null && uvEditor.ClickShortcutCheck(mesh, s_Selection.face))
                    {
                        return(null);
                    }

                    var faces = mesh.faces as Face[] ?? mesh.faces.ToArray();
                    var ind   = Array.IndexOf <Face>(faces, s_Selection.face);
                    var sel   = mesh.selectedFaceIndexes.IndexOf(ind);

                    UndoUtility.RecordSelection(mesh, "Select Face");

                    if (sel > -1)
                    {
                        mesh.RemoveFromFaceSelectionAtIndex(sel);
                    }
                    else
                    {
                        mesh.AddToFaceSelection(ind);
                    }
                }
                else if (s_Selection.edge != Edge.Empty)
                {
                    int ind = mesh.IndexOf(mesh.selectedEdges, s_Selection.edge);

                    UndoUtility.RecordSelection(mesh, "Select Edge");

                    if (ind > -1)
                    {
                        mesh.SetSelectedEdges(mesh.selectedEdges.ToArray().RemoveAt(ind));
                    }
                    else
                    {
                        mesh.SetSelectedEdges(mesh.selectedEdges.ToArray().Add(s_Selection.edge));
                    }
                }
                else if (s_Selection.vertex > -1)
                {
                    int ind = Array.IndexOf(mesh.selectedIndexesInternal, s_Selection.vertex);

                    UndoUtility.RecordSelection(mesh, "Select Vertex");

                    if (ind > -1)
                    {
                        mesh.SetSelectedVertices(mesh.selectedIndexesInternal.RemoveAt(ind));
                    }
                    else
                    {
                        mesh.SetSelectedVertices(mesh.selectedIndexesInternal.Add(s_Selection.vertex));
                    }
                }

                return(mesh);
            }

            return(null);
        }
Ejemplo n.º 10
0
        public static void DoMouseDrag(Rect mouseDragRect, SelectMode selectionMode, ScenePickerPreferences scenePickerPreferences)
        {
            var pickingOptions = new PickerOptions()
            {
                depthTest      = scenePickerPreferences.cullMode == CullingMode.Back,
                rectSelectMode = scenePickerPreferences.rectSelectMode
            };

            UndoUtility.RecordSelection("Drag Select");
            bool isAppendModifier = EditorHandleUtility.IsAppendModifier(Event.current.modifiers);

            if (!isAppendModifier)
            {
                MeshSelection.ClearElementSelection();
            }

            bool elementsInDragRect = false;

            switch (selectionMode)
            {
            case SelectMode.Vertex:
            case SelectMode.TextureVertex:
            {
                Dictionary <ProBuilderMesh, HashSet <int> > selected = SelectionPicker.PickVerticesInRect(
                    SceneView.lastActiveSceneView.camera,
                    mouseDragRect,
                    MeshSelection.topInternal,
                    pickingOptions,
                    EditorGUIUtility.pixelsPerPoint);

                foreach (var kvp in selected)
                {
                    var            mesh          = kvp.Key;
                    SharedVertex[] sharedIndexes = mesh.sharedVerticesInternal;
                    HashSet <int>  common;

                    if (isAppendModifier)
                    {
                        common = mesh.GetSharedVertexHandles(mesh.selectedIndexesInternal);

                        if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Add)
                        {
                            common.UnionWith(kvp.Value);
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Subtract)
                        {
                            common.RemoveWhere(x => kvp.Value.Contains(x));
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Difference)
                        {
                            common.SymmetricExceptWith(kvp.Value);
                        }
                    }
                    else
                    {
                        common = kvp.Value;
                    }

                    elementsInDragRect |= kvp.Value.Any();
                    mesh.SetSelectedVertices(common.SelectMany(x => sharedIndexes[x]));
                }

                break;
            }

            case SelectMode.Face:
            case SelectMode.TextureFace:
            {
                Dictionary <ProBuilderMesh, HashSet <Face> > selected = SelectionPicker.PickFacesInRect(
                    SceneView.lastActiveSceneView.camera,
                    mouseDragRect,
                    MeshSelection.topInternal,
                    pickingOptions,
                    EditorGUIUtility.pixelsPerPoint);

                foreach (var kvp in selected)
                {
                    HashSet <Face> current;

                    if (isAppendModifier)
                    {
                        current = new HashSet <Face>(kvp.Key.selectedFacesInternal);

                        if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Add)
                        {
                            current.UnionWith(kvp.Value);
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Subtract)
                        {
                            current.RemoveWhere(x => kvp.Value.Contains(x));
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Difference)
                        {
                            current.SymmetricExceptWith(kvp.Value);
                        }
                    }
                    else
                    {
                        current = kvp.Value;
                    }

                    elementsInDragRect |= kvp.Value.Any();
                    kvp.Key.SetSelectedFaces(current);
                }

                break;
            }

            case SelectMode.Edge:
            case SelectMode.TextureEdge:
            {
                var selected = SelectionPicker.PickEdgesInRect(
                    SceneView.lastActiveSceneView.camera,
                    mouseDragRect,
                    MeshSelection.topInternal,
                    pickingOptions,
                    EditorGUIUtility.pixelsPerPoint);

                foreach (var kvp in selected)
                {
                    ProBuilderMesh        mesh          = kvp.Key;
                    Dictionary <int, int> common        = mesh.sharedVertexLookup;
                    HashSet <EdgeLookup>  selectedEdges = EdgeLookup.GetEdgeLookupHashSet(kvp.Value, common);
                    HashSet <EdgeLookup>  current;

                    if (isAppendModifier)
                    {
                        current = EdgeLookup.GetEdgeLookupHashSet(mesh.selectedEdges, common);

                        if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Add)
                        {
                            current.UnionWith(selectedEdges);
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Subtract)
                        {
                            current.RemoveWhere(x => selectedEdges.Contains(x));
                        }
                        else if (scenePickerPreferences.selectionModifierBehavior == SelectionModifierBehavior.Difference)
                        {
                            current.SymmetricExceptWith(selectedEdges);
                        }
                    }
                    else
                    {
                        current = selectedEdges;
                    }

                    elementsInDragRect |= kvp.Value.Any();
                    mesh.SetSelectedEdges(current.Select(x => x.local));
                }

                break;
            }
            }

            // if nothing was selected in the drag rect, clear the object selection too
            if (!elementsInDragRect && !isAppendModifier)
            {
                MeshSelection.ClearElementAndObjectSelection();
            }

            ProBuilderEditor.Refresh();
            SceneView.RepaintAll();
        }
        /// <summary>
        /// Get the nearest <see cref="Edge"/> to a screen position.
        /// </summary>
        /// <returns>
        /// Distance is returned as the screen distance to mesh, not edge.
        /// </returns>
        static float EdgeRaycast(Vector3 mousePosition, ScenePickerPreferences pickerPrefs, bool allowUnselected, SceneSelection selection)
        {
            selection.Clear();
            selection.gameObject = UHandleUtility.PickGameObject(mousePosition, false);
            var hoveredMesh = selection.gameObject != null?selection.gameObject.GetComponent <ProBuilderMesh>() : null;

            float bestDistance         = Mathf.Infinity;
            bool  hoveredIsInSelection = MeshSelection.topInternal.Contains(hoveredMesh);

            if (hoveredMesh != null && (allowUnselected || hoveredIsInSelection))
            {
                var tup = GetNearestEdgeOnMesh(hoveredMesh, mousePosition);

                if (tup.edge.IsValid())
                {
                    selection.gameObject = hoveredMesh.gameObject;
                    selection.mesh       = hoveredMesh;
                    selection.SetSingleEdge(tup.edge);
                    bestDistance = tup.distance;

                    // If the nearest edge was acquired by a raycast, then the distance to mesh is 0f.
                    if (hoveredIsInSelection)
                    {
                        return(tup.distance);
                    }
                }
            }

            foreach (var mesh in MeshSelection.topInternal)
            {
                var trs       = mesh.transform;
                var positions = mesh.positionsInternal;
                s_EdgeBuffer.Clear();

                // When the pointer is over another object, apply a modifier to the distance to prefer picking the
                // object hovered over the currently selected
                var distMultiplier = (hoveredMesh == mesh || hoveredMesh == null)
                    ? 1.0f
                    : ScenePickerPreferences.offPointerMultiplier;

                foreach (var face in mesh.facesInternal)
                {
                    foreach (var edge in face.edges)
                    {
                        int x = edge.a;
                        int y = edge.b;

                        float d = UHandleUtility.DistanceToLine(
                            trs.TransformPoint(positions[x]),
                            trs.TransformPoint(positions[y]));

                        d *= distMultiplier;

                        // best distance isn't set to maxPointerDistance because we want to preserve an unselected
                        // gameobject over a selected gameobject with an out of bounds edge.
                        if (d > ScenePickerPreferences.maxPointerDistance)
                        {
                            continue;
                        }

                        // account for stacked edges
                        if (Mathf.Approximately(d, bestDistance))
                        {
                            s_EdgeBuffer.Add(new Edge(x, y));
                        }
                        else if (d < bestDistance)
                        {
                            s_EdgeBuffer.Clear();
                            s_EdgeBuffer.Add(new Edge(x, y));

                            selection.gameObject = mesh.gameObject;
                            selection.mesh       = mesh;
                            selection.SetSingleEdge(new Edge(x, y));
                            bestDistance = d;
                        }
                    }
                }

                // If more than 1 edge is closest, the closest is one of the vertex.
                // Get closest edge to the camera.
                if (s_EdgeBuffer.Count > 1)
                {
                    selection.SetSingleEdge(GetClosestEdgeToCamera(positions, s_EdgeBuffer));
                }
            }

            return(selection.gameObject != null ? bestDistance : Mathf.Infinity);
        }
        public static ProBuilderMesh DoMouseClick(Event evt, SelectMode selectionMode, ScenePickerPreferences pickerPreferences)
        {
            bool appendModifier         = EditorHandleUtility.IsAppendModifier(evt.modifiers);
            bool addToSelectionModifier = EditorHandleUtility.IsSelectionAddModifier(evt.modifiers);
            bool addOrRemoveIfPresentFromSelectionModifier = EditorHandleUtility.IsSelectionAppendOrRemoveIfPresentModifier(evt.modifiers);
            bool pathSelectionModifier = EditorHandleUtility.IsSelectionPathModifier(evt.modifiers);

            float pickedElementDistance;

            if (selectionMode.ContainsFlag(SelectMode.Edge | SelectMode.TextureEdge))
            {
                pickedElementDistance = EdgeRaycast(evt.mousePosition, pickerPreferences, k_AllowUnselected, s_Selection);
            }
            else if (selectionMode.ContainsFlag(SelectMode.Vertex | SelectMode.TextureVertex))
            {
                pickedElementDistance = VertexRaycast(evt.mousePosition, pickerPreferences, k_AllowUnselected, s_Selection);
            }
            else
            {
                pickedElementDistance = FaceRaycast(evt.mousePosition, pickerPreferences, k_AllowUnselected, s_Selection, evt.clickCount > 1 ? -1 : 0, false);
            }

            evt.Use();

            if (!appendModifier)
            {
                if (s_Selection.mesh != null)
                {
                    s_Selection.mesh.ClearSelection();
                }
                MeshSelection.SetSelection((GameObject)null);
            }

            if (pickedElementDistance > ScenePickerPreferences.maxPointerDistance)
            {
                if (appendModifier && Selection.gameObjects.Contains(s_Selection.gameObject))
                {
                    MeshSelection.RemoveFromSelection(s_Selection.gameObject);
                }
                else
                {
                    MeshSelection.AddToSelection(s_Selection.gameObject);
                }

                return(null);
            }

            GameObject candidateNewActiveObject     = s_Selection.gameObject;
            bool       activeObjectSelectionChanged = Selection.gameObjects.Contains(s_Selection.gameObject) && s_Selection.gameObject != Selection.activeGameObject;

            if (s_Selection.mesh != null)
            {
                var mesh = s_Selection.mesh;

                foreach (var face in s_Selection.faces)
                {
                    // Check for other editor mouse shortcuts first (todo proper event handling for mouse shortcuts)
                    MaterialEditor matEditor = MaterialEditor.instance;

                    if (matEditor != null && matEditor.ClickShortcutCheck(Event.current.modifiers, mesh, face))
                    {
                        return(null);
                    }

                    UVEditor uvEditor = UVEditor.instance;

                    if (uvEditor != null && uvEditor.ClickShortcutCheck(mesh, face))
                    {
                        return(null);
                    }

                    var faces = mesh.faces as Face[] ?? mesh.faces.ToArray();
                    var ind   = Array.IndexOf <Face>(faces, face);
                    var sel   = mesh.selectedFaceIndexes.IndexOf(ind);

                    UndoUtility.RecordSelection(mesh, "Select Face");

                    if (sel > -1)
                    {
                        if (!appendModifier || addOrRemoveIfPresentFromSelectionModifier ||
                            (addToSelectionModifier && face == mesh.GetActiveFace() && !activeObjectSelectionChanged))
                        {
                            mesh.RemoveFromFaceSelectionAtIndex(sel);

                            if (addOrRemoveIfPresentFromSelectionModifier && activeObjectSelectionChanged)
                            {
                                candidateNewActiveObject = Selection.activeGameObject;
                            }
                            else if (mesh.selectedFaceCount == 0)
                            {
                                for (var i = MeshSelection.topInternal.Count - 1; i >= 0; i--)
                                {
                                    if (MeshSelection.topInternal[i].selectedFaceCount > 0)
                                    {
                                        candidateNewActiveObject     = MeshSelection.topInternal[i].gameObject;
                                        activeObjectSelectionChanged = true;
                                        break;
                                    }
                                }
                            }
                        }
                        else
                        {
                            mesh.selectedFaceIndicesInternal = mesh.selectedFaceIndicesInternal.Remove(ind);
                            mesh.SetSelectedFaces(mesh.selectedFaceIndicesInternal.Add(ind));
                        }
                    }
                    else if (pathSelectionModifier && mesh.GetActiveFace() != null)
                    {
                        var pathFaces = SelectPathFaces.GetPath(mesh, Array.IndexOf <Face>(faces, mesh.GetActiveFace()),
                                                                Array.IndexOf <Face>(faces, face));
                        foreach (var pathFace in pathFaces)
                        {
                            mesh.AddToFaceSelection(pathFace);
                        }
                    }
                    else
                    {
                        mesh.AddToFaceSelection(ind);
                    }
                }

                foreach (var edge in s_Selection.edges)
                {
                    int ind = mesh.IndexOf(mesh.selectedEdges, edge);

                    UndoUtility.RecordSelection(mesh, "Select Edge");

                    if (ind > -1)
                    {
                        if (!appendModifier || addOrRemoveIfPresentFromSelectionModifier ||
                            (addToSelectionModifier && edge == mesh.GetActiveEdge() && !activeObjectSelectionChanged))
                        {
                            mesh.SetSelectedEdges(mesh.selectedEdges.ToArray().RemoveAt(ind));

                            if (addOrRemoveIfPresentFromSelectionModifier && activeObjectSelectionChanged)
                            {
                                candidateNewActiveObject = Selection.activeGameObject;
                            }
                            else if (mesh.selectedEdgeCount == 0)
                            {
                                for (var i = MeshSelection.topInternal.Count - 1; i >= 0; i--)
                                {
                                    if (MeshSelection.topInternal[i].selectedEdgeCount > 0)
                                    {
                                        candidateNewActiveObject     = MeshSelection.topInternal[i].gameObject;
                                        activeObjectSelectionChanged = true;
                                        break;
                                    }
                                }
                            }
                        }
                        else
                        {
                            mesh.selectedEdgesInternal = mesh.selectedEdgesInternal.Remove(edge);
                            mesh.SetSelectedEdges(mesh.selectedEdgesInternal.Add(edge));
                        }
                    }
                    else
                    {
                        mesh.SetSelectedEdges(mesh.selectedEdges.ToArray().Add(edge));
                    }
                }
                foreach (var vertex in s_Selection.vertexes)
                {
                    int ind = Array.IndexOf(mesh.selectedIndexesInternal, vertex);

                    UndoUtility.RecordSelection(mesh, "Select Vertex");

                    if (ind > -1)
                    {
                        var sharedIndex  = mesh.sharedVertexLookup[vertex];
                        var sharedVertex = mesh.sharedVerticesInternal[sharedIndex];
                        s_IndexBuffer.Clear();
                        foreach (var sVertex in sharedVertex)
                        {
                            var index = Array.IndexOf(mesh.selectedIndexesInternal, sVertex);
                            if (index < 0)
                            {
                                continue;
                            }

                            s_IndexBuffer.Add(index);
                        }
                        s_IndexBuffer.Sort();

                        if (!appendModifier || addOrRemoveIfPresentFromSelectionModifier ||
                            (addToSelectionModifier && vertex == mesh.GetActiveVertex() && !activeObjectSelectionChanged))
                        {
                            mesh.selectedIndexesInternal = mesh.selectedIndexesInternal.SortedRemoveAt(s_IndexBuffer);
                            mesh.SetSelectedVertices(mesh.selectedIndexesInternal);

                            if (addOrRemoveIfPresentFromSelectionModifier && activeObjectSelectionChanged)
                            {
                                candidateNewActiveObject = Selection.activeGameObject;
                            }
                            else if (mesh.selectedIndexesInternal.Length == 0)
                            {
                                for (var i = MeshSelection.topInternal.Count - 1; i >= 0; i--)
                                {
                                    if (MeshSelection.topInternal[i].selectedIndexesInternal.Length > 0)
                                    {
                                        candidateNewActiveObject     = MeshSelection.topInternal[i].gameObject;
                                        activeObjectSelectionChanged = true;
                                        break;
                                    }
                                }
                            }
                        }
                        else
                        {
                            mesh.selectedIndexesInternal = mesh.selectedIndexesInternal.SortedRemoveAt(s_IndexBuffer);
                            mesh.SetSelectedVertices(mesh.selectedIndexesInternal.Add(vertex));
                        }
                    }
                    else
                    {
                        mesh.SetSelectedVertices(mesh.selectedIndexesInternal.Add(vertex));
                    }
                }

                if (activeObjectSelectionChanged)
                {
                    MeshSelection.MakeActiveObject(candidateNewActiveObject);
                }
                else
                {
                    MeshSelection.AddToSelection(candidateNewActiveObject);
                }

                return(mesh);
            }

            return(null);
        }
        static float EdgeRaycast(Vector3 mousePosition, ScenePickerPreferences pickerPrefs, bool allowUnselected, SceneSelection selection)
        {
            selection.Clear();
            selection.gameObject = UHandleUtility.PickGameObject(mousePosition, false);
            var hoveredMesh = selection.gameObject != null?selection.gameObject.GetComponent <ProBuilderMesh>() : null;

            float bestDistance           = pickerPrefs.maxPointerDistance;
            float unselectedBestDistance = bestDistance;
            bool  hoveredIsInSelection   = MeshSelection.topInternal.Contains(hoveredMesh);

            if (hoveredMesh != null && (allowUnselected || hoveredIsInSelection))
            {
                var tup = GetNearestEdgeOnMesh(hoveredMesh, mousePosition);

                if (tup.edge.IsValid() && tup.distance < pickerPrefs.maxPointerDistance)
                {
                    selection.gameObject   = hoveredMesh.gameObject;
                    selection.mesh         = hoveredMesh;
                    selection.edge         = tup.edge;
                    unselectedBestDistance = tup.distance;

                    // if it's in the selection, it automatically wins as best. if not, treat this is a fallback.
                    if (hoveredIsInSelection)
                    {
                        return(tup.distance);
                    }
                }
            }

            foreach (var mesh in MeshSelection.topInternal)
            {
                var trs       = mesh.transform;
                var positions = mesh.positionsInternal;
                s_EdgeBuffer.Clear();

                //When the pointer is over another object, apply a modifier to the distance to prefer picking the object hovered over the currently selected
                var distMultiplier = (hoveredMesh == mesh || hoveredMesh == null) ? 1.0f : pickerPrefs.offPointerMultiplier;

                foreach (var face in mesh.facesInternal)
                {
                    foreach (var edge in face.edges)
                    {
                        int x = edge.a;
                        int y = edge.b;


                        float d = UHandleUtility.DistanceToLine(
                            trs.TransformPoint(positions[x]),
                            trs.TransformPoint(positions[y]));

                        d *= distMultiplier;

                        if (d == bestDistance)
                        {
                            s_EdgeBuffer.Add(new Edge(x, y));
                        }
                        else if (d < bestDistance)
                        {
                            s_EdgeBuffer.Clear();
                            s_EdgeBuffer.Add(new Edge(x, y));

                            selection.gameObject = mesh.gameObject;
                            selection.mesh       = mesh;
                            selection.edge       = new Edge(x, y);
                            bestDistance         = d;
                        }
                    }
                }

                //If more than 1 edge is closest, the closest is one of the vertex.
                //Get closest edge to the camera.
                if (s_EdgeBuffer.Count > 1)
                {
                    selection.edge = GetClosestEdgeToCamera(positions, s_EdgeBuffer);
                }
            }

            if (selection.gameObject != null)
            {
                if (bestDistance < pickerPrefs.maxPointerDistance)
                {
                    return(bestDistance);
                }
                return(unselectedBestDistance);
            }

            return(Mathf.Infinity);
        }