没有碰撞体的前提下进行mesh碰撞检测

需求:在没有碰撞体的前提下检测到鼠标点击的mesh。

思路:获得所有的mesh,然后通过算法比较mesh中每一个三角形面片与射线是否相交,将所有相交点与射线origin之间的长度进行排序,最短的即为需要获得的mesh。

上干活,首先是射线与三角形相交算法,通过u,v或者t 可以算到相交点

 /// <summary>
        /// 射线与三角形相交 Vector3版本
        /// 射线: orig + dir * t
        /// 三角形:(1 - u - v)v0 + u * v1 + v * v2
        /// </summary>
        /// <param name="orig">起点</param>
        /// <param name="dir">射线方向</param>
        /// <param name="v0">三角形点1</param>
        /// <param name="v1">三角形点2</param>
        /// <param name="v2">三角形点2</param>
        /// <param name="t">结果 t</param>
        /// <param name="u">结果 u </param>
        /// <param name="v">结果 v</param>
        /// <returns></returns>
        public static bool IsRayIntersectTriangle(Vector3 orig, Vector3 dir, Vector3 v0, Vector3 v1, Vector3 v2, out float t, out float u, out float v)
        {
            t = -1;
            u = -1;
            v = -1;
            // E1
            Vector3 E1 = v1 - v0;

            // E2
            Vector3 E2 = v2 - v0;

            // P
            Vector3 P = Vector3.Cross(dir, E2);

            // determinant
            float det = Vector3.Dot(E1, P);

            // keep det > 0, modify T accordingly
            Vector3 T;
            if (det > 0)
            {
                T = orig - v0;
            }
            else
            {
                T = v0 - orig;
                det = -det;
            }

            // If determinant is near zero, ray lies in plane of triangle
            float espX = 0.00001f;

            if (det < espX)
                return false;

            // Calculate u and make sure u <= 1
            u = Vector3.Dot(T, P);
            if (u < -espX || u > det)
                return false;

            // Q
            Vector3 Q = Vector3.Cross(T, E1);

            // Calculate v and make sure u + v <= 1
            v = Vector3.Dot(dir, Q);
            if (v < -espX || u + v > det+ espX)
                return false;

            // Calculate t, scale parameters, ray intersects triangle
            t = Vector3.Dot(E2, Q);

            float fInvDet = 1.0f / det;
            t *= fInvDet;
            u *= fInvDet;
            v *= fInvDet;
            return true;
        }

接下来是与mesh相交

public static bool IsRayIntersectMesh(Mesh mesh, Vector3 org, Vector3 dir, Transform fatherTransform,out List<Vector3> dis)
    {
        dis = new List<Vector3>();
        //Matrix4x4 translate = fatherTransform.localToWorldMatrix;
        int[] triangles = mesh.triangles;
        Vector3[] vertices = mesh.vertices;
        //Matrix4x4 x = Matrix4x4.TRS(fatherTransform.position, fatherTransform.rotation, fatherTransform.lossyScale);
        for (int i = 0; i < vertices.Length - 1; i++)
        {
            
            vertices[i] = fatherTransform.TransformPoint( vertices[i]);
            //NormalSurface.CreateBall(vertices[i], "1");
        }

        for (int i = 0; i < triangles.Length-2; i += 3)
        {
            float t;
            float u;
            float v;
            try
            {
                bool bb = GenesisWinForm.MathG3D. Function.IsRayIntersectTriangle(org, dir,  vertices[triangles[i]], vertices[triangles[i+1]], vertices[triangles[i+2]], out t, out u, out v);
                if (bb) {
                    Vector3 cp = (1 - u - v) * vertices[triangles[i]] + u * vertices[triangles[i+1]] + v * vertices[triangles[i+2]];
                    dis.Add(cp) ;
                    //return true;
                }         
            }
            catch {
                //Debug.Log(i);
            }
                     
        }
        if (dis.Count > 0)
            return true;

        return false;
    }

返回的dis是相交点,当然cp还可以用 org+ dir*t 来计算(上面是忘记了所以用uv算的)。

通过比较t的大小就可以得到想要的mesh了~


猜你喜欢

转载自blog.csdn.net/skylovecrayon/article/details/80763143