欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-16 15:38:58
...

需求:在没有碰撞体的前提下检测到鼠标点击的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了~