/// <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); }