private static bool FaceRaycastBothCullModes(Ray worldRay, ProBuilderMesh mesh, ref SimpleTuple <Face, Vector3> back, ref SimpleTuple <Face, Vector3> front) { // Transform ray into model space worldRay.origin -= mesh.transform.position; // Why doesn't worldToLocalMatrix apply translation? worldRay.origin = mesh.transform.worldToLocalMatrix * worldRay.origin; worldRay.direction = mesh.transform.worldToLocalMatrix * worldRay.direction; IList <Vector3> positions = mesh.positions; IList <Face> faces = mesh.faces; back.item1 = null; front.item1 = null; float backDistance = Mathf.Infinity; float frontDistance = Mathf.Infinity; // Iterate faces, testing for nearest hit to ray origin. Optionally ignores backfaces. for (int i = 0, fc = faces.Count; i < fc; ++i) { ReadOnlyCollection <int> indexes = faces[i].indexes; for (int j = 0, ic = indexes.Count; j < ic; j += 3) { Vector3 a = positions[indexes[j + 0]]; Vector3 b = positions[indexes[j + 1]]; Vector3 c = positions[indexes[j + 2]]; float dist; Vector3 point; if (Math.RayIntersectsTriangle(worldRay, a, b, c, out dist, out point)) { if (dist < backDistance || dist < frontDistance) { Vector3 nrm = Vector3.Cross(b - a, c - a); float dot = Vector3.Dot(worldRay.direction, nrm); if (dot < 0f) { if (dist < backDistance) { backDistance = dist; back.item1 = faces[i]; } } else { if (dist < frontDistance) { frontDistance = dist; front.item1 = faces[i]; } } } } } } if (back.item1 != null) { back.item2 = worldRay.GetPoint(backDistance); } if (front.item1 != null) { front.item2 = worldRay.GetPoint(frontDistance); } return(back.item1 != null || front.item1 != null); }
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); }