旋转几何体时每个三角形碰撞检测的问题
Problem with per-triangle collision detection when geometry is rotated
对于我的游戏,我试图用相机实现每个三角形的碰撞检测。看起来代码大部分都在工作,因为未旋转的几何体经过正确的命中测试,并停止了相机。然而,当我试图将相机移动到任何旋转的几何体中时,问题就出现了。我将提供一些图片来帮助说明我的问题,以及我目前用于碰撞检测的代码。需要注意的是,我使用的是 SharpDX 作为 3D 库。
在这张照片中,我正确地对我旁边的墙进行了碰撞测试。我无法从任何方向穿透它。这块几何图形没有旋转。
在这张照片中,我正确地对我下面的地板进行了撞击测试。同样,它没有旋转。
在这幅图中,我圈出一系列几何图形,所有这些图形都在 Y 轴上旋转了 270 度。我可以直接穿过这些碎片。
在这张照片中,我正面撞到了某个东西,这导致我的命中检测被触发。您将在下一张图片中看到这个“东西”是什么。
最后放一张没有旋转的旋转几何体的图片。我在这种状态下正确地命中检测了它们。当它们旋转时,碰撞框保持在与您在这张图片中看到的相同的位置,这就是为什么我点击它们前面的“东西”。
这是我当前的相机命中检测代码(已更新以提高可读性):
if (isCameraMoving)
{
foreach (StaticGeometry geometry in this.StaticGeometry)
{
BoundingSphere cameraSphere = Camera.Bounds;
for (int i = 0; i < geometry.Mesh.Vertices.Length; i += 3)
{
// Get the Vertex Positions for the current Triangle
Vector3 position1 = geometry.Mesh.Vertices[i].Location;
Vector3 position2 = geometry.Mesh.Vertices[i + 1].Location;
Vector3 position3 = geometry.Mesh.Vertices[i + 2].Location;
// Create the rotation matrix using the geometry's current rotation setting.
Matrix rotationMatrix = VoidwalkerMath.CreateRotationMatrix(geometry.Rotation);
// rotate and translate each vertex
Matrix matrix1 = rotationMatrix * Matrix.Translation(position1 + geometry.Location);
Matrix matrix2 = rotationMatrix * Matrix.Translation(position2 + geometry.Location);
Matrix matrix3 = rotationMatrix * Matrix.Translation(position3 + geometry.Location);
// extract the new position from the rotated and translated Matrices.
Vector3 finalVertexLocation1 = matrix1.TranslationVector;
Vector3 finalVertexLocation2 = matrix2.TranslationVector;
Vector3 finalVertexLocation3 = matrix3.TranslationVector;
// Do hit detection for a triangle.
if (cameraSphere.Intersects(ref finalVertexLocation1, ref finalVertexLocation2, ref finalVertexLocation3))
{
this.Camera.Location = previousCameraPosition;
return;
}
}
}
}
还有我创建旋转矩阵的代码。我确信这段代码可以按预期工作,因为它与我用于旋转几何体的功能相同。
/// <summary>
/// Converts degrees to radians.
/// </summary>
/// <param name="degrees">The angle in degrees.</param>
public static float ToRadians(float degrees)
{
return degrees / 360.0f * TwoPi;
}
/// <summary>
/// Creates a rotation matrix using degrees.
/// </summary>
/// <param name="xDegrees"></param>
/// <param name="yDegrees"></param>
/// <param name="zDegrees"></param>
/// <returns></returns>
public static Matrix CreateRotationMatrix(float xDegrees, float yDegrees, float zDegrees)
{
return
Matrix.RotationX(ToRadians(xDegrees)) *
Matrix.RotationY(ToRadians(yDegrees)) *
Matrix.RotationZ(ToRadians(zDegrees));
}
/// <summary>
/// Converts a Vector3 of Degrees to a Vector3 of Radians
/// </summary>
/// <param name="degrees"></param>
/// <returns></returns>
public static Vector3 ToRadians(Vector3 degrees)
{
return ToRadians(degrees.X,degrees.Y,degrees.Z);
}
这是我的顶点 class。如果你们还需要什么,请告诉我。
using SharpDX;
using System.Runtime.InteropServices;
namespace VoidwalkerEngine.Framework.DirectX.Rendering
{
[StructLayout(LayoutKind.Sequential)]
public struct Vertex
{
public static Vertex Zero = new Vertex(Vector3.Zero,Vector2.Zero,Vector3.Zero);
public Vector3 Location;
public Vector2 TexCoords;
public Vector3 Normal;
public const int Size = 32;
public Vertex(Vector3 position, Vector2 texCoords, Vector3 normal)
{
this.Location = position;
this.Normal = normal;
this.TexCoords = texCoords;
}
public Vertex(Vertex other)
{
this.Location = other.Location;
this.Normal = other.Normal;
this.TexCoords = other.TexCoords;
}
public override string ToString()
{
return
"Location: " + Location.ToString() +
", TexCoords: " + TexCoords.ToString() +
", Normal: " + Normal.ToString();
}
}
}
我显然做错了什么,但我不知道它可能是什么。我先旋转顶点,然后平移它们。我在这里遗漏了什么吗?
原来我只是在装傻。实际旋转顶点的方式是使用 Vector3.TransformCoordinate();
并将其提供给模型的旋转矩阵。这是更新后的工作代码(看到亲自工作真是太棒了)。
if (isCameraMoving)
{
foreach (StaticGeometry geometry in this.StaticGeometry)
{
BoundingSphere cameraSphere = Camera.Bounds;
for (int i = 0; i < geometry.Mesh.Vertices.Length; i += 3)
{
// Get the Vertex Positions for the current Triangle
Vector3 position1 = geometry.Mesh.Vertices[i].Location;
Vector3 position2 = geometry.Mesh.Vertices[i + 1].Location;
Vector3 position3 = geometry.Mesh.Vertices[i + 2].Location;
// Create the rotation matrix using the geometry's current rotation setting.
Matrix rotationMatrix = VoidwalkerMath.CreateRotationMatrix(geometry.Rotation);
// Transform the Coordinate with the Rotation Matrix, then add the geometry's location
Vector3 finalVertexLocation1 = Vector3.TransformCoordinate(position1, rotationMatrix) + geometry.Location;
Vector3 finalVertexLocation2 = Vector3.TransformCoordinate(position2, rotationMatrix) + geometry.Location;
Vector3 finalVertexLocation3 = Vector3.TransformCoordinate(position3, rotationMatrix) + geometry.Location;
// Do hit detection for a triangle.
if (cameraSphere.Intersects(ref finalVertexLocation1, ref finalVertexLocation2, ref finalVertexLocation3))
{
this.Camera.Location = previousCameraPosition;
return;
}
}
}
}
对于我的游戏,我试图用相机实现每个三角形的碰撞检测。看起来代码大部分都在工作,因为未旋转的几何体经过正确的命中测试,并停止了相机。然而,当我试图将相机移动到任何旋转的几何体中时,问题就出现了。我将提供一些图片来帮助说明我的问题,以及我目前用于碰撞检测的代码。需要注意的是,我使用的是 SharpDX 作为 3D 库。
在这张照片中,我正确地对我旁边的墙进行了碰撞测试。我无法从任何方向穿透它。这块几何图形没有旋转。
在这张照片中,我正确地对我下面的地板进行了撞击测试。同样,它没有旋转。
在这幅图中,我圈出一系列几何图形,所有这些图形都在 Y 轴上旋转了 270 度。我可以直接穿过这些碎片。
在这张照片中,我正面撞到了某个东西,这导致我的命中检测被触发。您将在下一张图片中看到这个“东西”是什么。
最后放一张没有旋转的旋转几何体的图片。我在这种状态下正确地命中检测了它们。当它们旋转时,碰撞框保持在与您在这张图片中看到的相同的位置,这就是为什么我点击它们前面的“东西”。
这是我当前的相机命中检测代码(已更新以提高可读性):
if (isCameraMoving)
{
foreach (StaticGeometry geometry in this.StaticGeometry)
{
BoundingSphere cameraSphere = Camera.Bounds;
for (int i = 0; i < geometry.Mesh.Vertices.Length; i += 3)
{
// Get the Vertex Positions for the current Triangle
Vector3 position1 = geometry.Mesh.Vertices[i].Location;
Vector3 position2 = geometry.Mesh.Vertices[i + 1].Location;
Vector3 position3 = geometry.Mesh.Vertices[i + 2].Location;
// Create the rotation matrix using the geometry's current rotation setting.
Matrix rotationMatrix = VoidwalkerMath.CreateRotationMatrix(geometry.Rotation);
// rotate and translate each vertex
Matrix matrix1 = rotationMatrix * Matrix.Translation(position1 + geometry.Location);
Matrix matrix2 = rotationMatrix * Matrix.Translation(position2 + geometry.Location);
Matrix matrix3 = rotationMatrix * Matrix.Translation(position3 + geometry.Location);
// extract the new position from the rotated and translated Matrices.
Vector3 finalVertexLocation1 = matrix1.TranslationVector;
Vector3 finalVertexLocation2 = matrix2.TranslationVector;
Vector3 finalVertexLocation3 = matrix3.TranslationVector;
// Do hit detection for a triangle.
if (cameraSphere.Intersects(ref finalVertexLocation1, ref finalVertexLocation2, ref finalVertexLocation3))
{
this.Camera.Location = previousCameraPosition;
return;
}
}
}
}
还有我创建旋转矩阵的代码。我确信这段代码可以按预期工作,因为它与我用于旋转几何体的功能相同。
/// <summary>
/// Converts degrees to radians.
/// </summary>
/// <param name="degrees">The angle in degrees.</param>
public static float ToRadians(float degrees)
{
return degrees / 360.0f * TwoPi;
}
/// <summary>
/// Creates a rotation matrix using degrees.
/// </summary>
/// <param name="xDegrees"></param>
/// <param name="yDegrees"></param>
/// <param name="zDegrees"></param>
/// <returns></returns>
public static Matrix CreateRotationMatrix(float xDegrees, float yDegrees, float zDegrees)
{
return
Matrix.RotationX(ToRadians(xDegrees)) *
Matrix.RotationY(ToRadians(yDegrees)) *
Matrix.RotationZ(ToRadians(zDegrees));
}
/// <summary>
/// Converts a Vector3 of Degrees to a Vector3 of Radians
/// </summary>
/// <param name="degrees"></param>
/// <returns></returns>
public static Vector3 ToRadians(Vector3 degrees)
{
return ToRadians(degrees.X,degrees.Y,degrees.Z);
}
这是我的顶点 class。如果你们还需要什么,请告诉我。
using SharpDX;
using System.Runtime.InteropServices;
namespace VoidwalkerEngine.Framework.DirectX.Rendering
{
[StructLayout(LayoutKind.Sequential)]
public struct Vertex
{
public static Vertex Zero = new Vertex(Vector3.Zero,Vector2.Zero,Vector3.Zero);
public Vector3 Location;
public Vector2 TexCoords;
public Vector3 Normal;
public const int Size = 32;
public Vertex(Vector3 position, Vector2 texCoords, Vector3 normal)
{
this.Location = position;
this.Normal = normal;
this.TexCoords = texCoords;
}
public Vertex(Vertex other)
{
this.Location = other.Location;
this.Normal = other.Normal;
this.TexCoords = other.TexCoords;
}
public override string ToString()
{
return
"Location: " + Location.ToString() +
", TexCoords: " + TexCoords.ToString() +
", Normal: " + Normal.ToString();
}
}
}
我显然做错了什么,但我不知道它可能是什么。我先旋转顶点,然后平移它们。我在这里遗漏了什么吗?
原来我只是在装傻。实际旋转顶点的方式是使用 Vector3.TransformCoordinate();
并将其提供给模型的旋转矩阵。这是更新后的工作代码(看到亲自工作真是太棒了)。
if (isCameraMoving)
{
foreach (StaticGeometry geometry in this.StaticGeometry)
{
BoundingSphere cameraSphere = Camera.Bounds;
for (int i = 0; i < geometry.Mesh.Vertices.Length; i += 3)
{
// Get the Vertex Positions for the current Triangle
Vector3 position1 = geometry.Mesh.Vertices[i].Location;
Vector3 position2 = geometry.Mesh.Vertices[i + 1].Location;
Vector3 position3 = geometry.Mesh.Vertices[i + 2].Location;
// Create the rotation matrix using the geometry's current rotation setting.
Matrix rotationMatrix = VoidwalkerMath.CreateRotationMatrix(geometry.Rotation);
// Transform the Coordinate with the Rotation Matrix, then add the geometry's location
Vector3 finalVertexLocation1 = Vector3.TransformCoordinate(position1, rotationMatrix) + geometry.Location;
Vector3 finalVertexLocation2 = Vector3.TransformCoordinate(position2, rotationMatrix) + geometry.Location;
Vector3 finalVertexLocation3 = Vector3.TransformCoordinate(position3, rotationMatrix) + geometry.Location;
// Do hit detection for a triangle.
if (cameraSphere.Intersects(ref finalVertexLocation1, ref finalVertexLocation2, ref finalVertexLocation3))
{
this.Camera.Location = previousCameraPosition;
return;
}
}
}
}