static int GetNearestVertices(ProBuilderMesh mesh, Vector3 mousePosition, List <VertexPickerEntry> list, float maxDistance) { var positions = mesh.positionsInternal; var common = mesh.sharedVerticesInternal; var matches = 0; for (int n = 0, c = common.Length; n < c; n++) { int index = common[n][0]; Vector3 v = mesh.transform.TransformPoint(positions[index]); Vector3 p = UHandleUtility.WorldToGUIPoint(v); float dist = (p - mousePosition).sqrMagnitude; if (dist < maxDistance) { list.Add(new VertexPickerEntry() { mesh = mesh, screenDistance = dist, worldPosition = v, vertex = index }); matches++; } } return(matches); }
public static float GetHandleSize(Vector3 center) { #if UNITY_EDITOR if (IsHandleHackAvailable) { return(HandleUtility.GetHandleSize(center)); } else #endif return(1f); }
public static void DrawAngleBetween(Vector3 center, Vector3 from, Vector3 to, Vector3 axis, float radius, Color color, bool constantScreenSize = true, bool label = false) { #if UNITY_EDITOR if (IsHandleHackAvailable) { float angle = Vector3.SignedAngle(from, to, axis); DrawArc(center, axis, from, angle, radius, color, constantScreenSize); if (label) { float factor = constantScreenSize ? HandleUtility.GetHandleSize(center) : 1f; Vector3 labelPos = center + (Vector3.Lerp(from, to, 0.5f) * factor); DrawLabel(labelPos, $"{angle:F2}"); } } #endif }
public static void DrawArc(Vector3 center, Vector3 normal, Vector3 from, float angle, float radius, Color color, bool constantScreenSize = true) { #if UNITY_EDITOR if (IsHandleHackAvailable) { if (constantScreenSize) { radius *= HandleUtility.GetHandleSize(center); } using (new HandleColorScope(color)) { Handles.DrawSolidArc(center, normal, from, angle, radius); } } #endif }
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); }
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); }
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); }
/// <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); }
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); }