Esempio n. 1
0
        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);
        }
Esempio n. 2
0
        public static float GetHandleSize(Vector3 center)
        {
#if UNITY_EDITOR
            if (IsHandleHackAvailable)
            {
                return(HandleUtility.GetHandleSize(center));
            }
            else
#endif
            return(1f);
        }
Esempio n. 3
0
        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
        }
Esempio n. 4
0
        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
        }
Esempio n. 5
0
        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);
        }
Esempio n. 6
0
        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);
        }
Esempio n. 7
0
        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);
        }