/** * Checks if mouse is over an edge, and if so, returns true setting @edge. */ public static bool EdgeRaycast(Vector2 mousePosition, ElementCache selection, out qe_Edge edge) { qe_Mesh mesh = selection.mesh; Vector3 v0, v1; float bestDistance = Mathf.Infinity; float distance = 0f; edge = null; GameObject go = HandleUtility.PickGameObject(mousePosition, false); if( go == null || go != selection.transform.gameObject) { qe_Edge[] edges = mesh.userEdges; int width = Screen.width; int height = Screen.height; for(int i = 0; i < edges.Length; i++) { v0 = selection.verticesInWorldSpace[edges[i].x]; v1 = selection.verticesInWorldSpace[edges[i].y]; distance = HandleUtility.DistanceToLine(v0, v1); if ( distance < bestDistance && distance < MAX_EDGE_SELECT_DISTANCE )// && !PointIsOccluded(mesh, (v0+v1)*.5f) ) { Vector3 vs0 = SceneView.lastActiveSceneView.camera.WorldToScreenPoint(v0); // really simple frustum check (will fail on edges that have vertices outside the frustum but is visible) if( vs0.z <= 0 || vs0.x < 0 || vs0.y < 0 || vs0.x > width || vs0.y > height ) continue; Vector3 vs1 = SceneView.lastActiveSceneView.camera.WorldToScreenPoint(v1); if( vs1.z <= 0 || vs1.x < 0 || vs1.y < 0 || vs1.x > width || vs1.y > height ) continue; bestDistance = distance; edge = edges[i]; } } } else { // Test culling List<qe_RaycastHit> hits; Ray ray = HandleUtility.GUIPointToWorldRay(mousePosition); if( MeshRaycast(ray, mesh, out hits, Mathf.Infinity, Culling.FrontBack) ) { // Sort from nearest hit to farthest hits.Sort( (x, y) => x.Distance.CompareTo(y.Distance) ); // Find the nearest edge in the hit faces Vector3[] v = mesh.vertices; for(int i = 0; i < hits.Count; i++) { if( PointIsOccluded(mesh, mesh.transform.TransformPoint(hits[i].Point)) ) continue; foreach(qe_Edge e in mesh.faces[hits[i].FaceIndex].GetEdges()) { float d = HandleUtility.DistancePointLine(hits[i].Point, v[e.x], v[e.y]); if(d < bestDistance) { bestDistance = d; edge = e; } } if( Vector3.Dot(ray.direction, mesh.transform.TransformDirection(hits[i].Normal)) < 0f ) break; } if(edge != null && HandleUtility.DistanceToLine(mesh.transform.TransformPoint(v[edge.x]), mesh.transform.TransformPoint(v[edge.y])) > MAX_EDGE_SELECT_DISTANCE) { edge = null; } else { edge.x = mesh.ToUserIndex(edge.x); edge.y = mesh.ToUserIndex(edge.y); } } } return edge != null; }
/** * Get an array of qe_Edge from this mesh (three per-triangle). */ public qe_Edge[] GetEdges() { int[] tris = cloneMesh.triangles; qe_Edge[] edges = new qe_Edge[tris.Length]; for(int i = 0; i < tris.Length; i+=3) { edges[i+0] = new qe_Edge(tris[i+0], tris[i+1]); edges[i+1] = new qe_Edge(tris[i+1], tris[i+2]); edges[i+2] = new qe_Edge(tris[i+2], tris[i+0]); } return edges; }