没有碰撞体的前提下进行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了~
推荐阅读