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);
        }
Exemplo n.º 2
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);
        }
        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);
        }