static EdgeAndDistance GetNearestEdgeOnMesh(ProBuilderMesh mesh, Vector3 mousePosition) { Ray ray = UHandleUtility.GUIPointToWorldRay(mousePosition); var res = new EdgeAndDistance() { edge = Edge.Empty, distance = Mathf.Infinity }; SimpleTuple <Face, Vector3> s_DualCullModeRaycastBackFace = new SimpleTuple <Face, Vector3>(); SimpleTuple <Face, Vector3> s_DualCullModeRaycastFrontFace = new SimpleTuple <Face, Vector3>(); // get the nearest hit face and point for both cull mode front and back, then prefer the result that is nearest the camera. if (PHandleUtility.FaceRaycastBothCullModes(ray, mesh, ref s_DualCullModeRaycastBackFace, ref s_DualCullModeRaycastFrontFace)) { Vector3[] v = mesh.positionsInternal; if (s_DualCullModeRaycastBackFace.item1 != null) { foreach (var edge in s_DualCullModeRaycastBackFace.item1.edgesInternal) { float d = UHandleUtility.DistancePointLine(s_DualCullModeRaycastBackFace.item2, v[edge.a], v[edge.b]); if (d < res.distance) { res.edge = edge; res.distance = d; } } } if (s_DualCullModeRaycastFrontFace.item1 != null) { var a = mesh.transform.TransformPoint(s_DualCullModeRaycastBackFace.item2); var b = mesh.transform.TransformPoint(s_DualCullModeRaycastFrontFace.item2); var c = SceneView.lastActiveSceneView.camera.transform.position; if (Vector3.Distance(c, b) < Vector3.Distance(c, a)) { foreach (var edge in s_DualCullModeRaycastFrontFace.item1.edgesInternal) { float d = UHandleUtility.DistancePointLine(s_DualCullModeRaycastFrontFace.item2, v[edge.a], v[edge.b]); if (d < res.distance) { res.edge = edge; res.distance = d; } } } } if (res.edge.IsValid()) { res.distance = UHandleUtility.DistanceToLine( mesh.transform.TransformPoint(v[res.edge.a]), mesh.transform.TransformPoint(v[res.edge.b])); } } return(res); }
private static EdgeAndDistance GetNearestEdgeOnMesh(Camera camera, ProBuilderMesh mesh, Vector3 mousePosition) { Ray ray = camera.ScreenPointToRay(mousePosition); var res = new EdgeAndDistance() { edge = Edge.Empty, distance = Mathf.Infinity }; SimpleTuple <Face, Vector3> dualCullModeRaycastBackFace = new SimpleTuple <Face, Vector3>(); SimpleTuple <Face, Vector3> dualCullModeRaycastFrontFace = new SimpleTuple <Face, Vector3>(); // get the nearest hit face and point for both cull mode front and back, then prefer the result that is nearest the camera. if (FaceRaycastBothCullModes(ray, mesh, ref dualCullModeRaycastBackFace, ref dualCullModeRaycastFrontFace)) { IList <Vector3> v = mesh.positions; if (dualCullModeRaycastBackFace.item1 != null) { foreach (var edge in dualCullModeRaycastBackFace.item1.edges) { float d = DistanceToLine(dualCullModeRaycastBackFace.item2, v[edge.a], v[edge.b]); if (d < res.distance) { res.edge = edge; res.distance = d; } } } if (dualCullModeRaycastFrontFace.item1 != null) { Vector3 a = mesh.transform.TransformPoint(dualCullModeRaycastBackFace.item2); Vector3 b = mesh.transform.TransformPoint(dualCullModeRaycastFrontFace.item2); Vector3 c = camera.transform.position; if (Vector3.Distance(c, b) < Vector3.Distance(c, a)) { foreach (var edge in dualCullModeRaycastFrontFace.item1.edges) { float d = DistanceToLine(dualCullModeRaycastFrontFace.item2, v[edge.a], v[edge.b]); if (d < res.distance) { res.edge = edge; res.distance = d; } } } } if (res.edge.IsValid()) { res.distance = DistanceToLine(camera, mousePosition, mesh.transform.TransformPoint(v[res.edge.a]), mesh.transform.TransformPoint(v[res.edge.b])); } } return(res); }
public static float PickEdge(Camera camera, Vector3 mousePosition, float maxDistance, GameObject pickedObject, IEnumerable <ProBuilderMesh> meshes, ref SceneSelection selection) { selection.Clear(); selection.gameObject = pickedObject; ProBuilderMesh hoveredMesh = selection.gameObject != null?selection.gameObject.GetComponent <ProBuilderMesh>() : null; float bestDistance = maxDistance; float unselectedBestDistance = maxDistance; //bool hoveredIsInSelection = meshes.Contains(hoveredMesh); //const bool allowUnselected = true; //if (hoveredMesh != null && (allowUnselected || hoveredIsInSelection)) if (hoveredMesh != null) { EdgeAndDistance tup = GetNearestEdgeOnMesh(camera, hoveredMesh, mousePosition); if (tup.edge.IsValid() && tup.distance < maxDistance) { 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 (ProBuilderMesh mesh in meshes) { Transform trs = mesh.transform; IList <Vector3> positions = mesh.positions; m_edges.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; const float distMultiplier = 1.0f; foreach (Face face in mesh.faces) { foreach (Edge edge in face.edges) { int x = edge.a; int y = edge.b; float d = DistanceToLine(camera, mousePosition, trs.TransformPoint(positions[x]), trs.TransformPoint(positions[y])); d *= distMultiplier; if (d == bestDistance) { m_edges.Add(new Edge(x, y)); } else if (d < bestDistance) { m_edges.Clear(); m_edges.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 (m_edges.Count > 1) { selection.edge = GetClosestEdgeToCamera(camera, mousePosition, positions, m_edges); } } if (selection.gameObject != null) { if (bestDistance < maxDistance) { return(bestDistance); } return(Mathf.Infinity);// unselectedBestDistance; } return(Mathf.Infinity); }
public static float PickEdge(Camera camera, Vector3 mousePosition, float maxDistance, GameObject pickedObject, IEnumerable <ProBuilderMesh> meshes, bool depthTest, ref SceneSelection selection) { selection.Clear(); selection.gameObject = pickedObject; ProBuilderMesh hoveredMesh = selection.gameObject != null?selection.gameObject.GetComponent <ProBuilderMesh>() : null; float bestDistance = maxDistance; float unselectedBestDistance = maxDistance; if (hoveredMesh != null) { EdgeAndDistance tup = GetNearestEdgeOnMesh(camera, hoveredMesh, mousePosition); if (tup.edge.IsValid() && tup.distance < maxDistance) { 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); } } } ProBuilderMesh pickedMesh = pickedObject != null?pickedObject.GetComponent <ProBuilderMesh>() : null; HashSet <ProBuilderMesh> hs = new HashSet <ProBuilderMesh>(); foreach (ProBuilderMesh mesh in meshes) { if (!hs.Contains(mesh)) { hs.Add(mesh); } } if (pickedMesh != null && !hs.Contains(pickedMesh)) { hs.Add(pickedMesh); } foreach (ProBuilderMesh mesh in hs) { Transform trs = mesh.transform; IList <Vector3> positions = mesh.positions; m_edges.Clear(); const float distMultiplier = 1.0f; foreach (Face face in mesh.faces) { foreach (Edge edge in face.edges) { int x = edge.a; int y = edge.b; Vector3 projectedPoint; Vector3 p0 = trs.TransformPoint(positions[x]); Vector3 p1 = trs.TransformPoint(positions[y]); float d = MathHelper.DistanceToLine(camera, mousePosition, p0, p1, out projectedPoint); if (depthTest) { Ray ray = camera.ScreenPointToRay(projectedPoint); Vector3 cpl0; Vector3 cpl1; if (MathHelper.ClosestPointsOnTwoLines(out cpl0, out cpl1, ray.origin, ray.direction, p0, p1 - p0)) { if (PointIsOccluded(camera, mesh, cpl1)) { continue; } } } d *= distMultiplier; if (d == bestDistance) { m_edges.Add(new Edge(x, y)); } else if (d < bestDistance) { m_edges.Clear(); m_edges.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 (m_edges.Count > 1) { selection.edge = GetClosestEdgeToCamera(camera, mousePosition, positions, m_edges); } } if (selection.gameObject != null) { if (bestDistance < maxDistance) { return(bestDistance); } return(Mathf.Infinity);// unselectedBestDistance; } return(Mathf.Infinity); }