[翻译]XNA 3.0 Game Programming Recipes之twenty-five
PS:自己翻译的,转载请著明出处格
4-18 使用Pre-三角形检查,检测射线模式的碰撞
问题
你想检测3D射线是否与模型碰撞。这是必须的,如果你想执行一个碰撞检测在最好的详细程度。它可以被用来作为4-11节的一个扩展去检测在高级别的细节中,是否有一颗子弹已经击中了一个物体。如下一节所显示的那样,这是可能的,例如,同样可以用来检查指针是否是在当前的模型上。
更多的,它可以被用来作为极细小的检查为模型到模型的碰撞,由于一个三角形的每个边对应一个射线,可以用来测试与另一个模型的碰撞。
这一节将处理很复杂的情况,允许模型分开的部分转化成单独的,使这节适合模型的活动。这些变化将影响模型顶点的最后的位置。例如,如果一个人移动他的胳膊,胳膊最终的顶点位置别改变了。这节将这些变换考虑在内。
或者,您想要一个速成课在三维矢量数学上。
解决方案
这节使用一个自定义混合管道内容创建在4-13和4-14节中,因为它保存一个三角形数组对象在每一个模型的ModelMesh的Tag属性里。每个三角形对象都有三个Vector3定义一个三角形的ModelMesh。
有利于使每个ModelMesh都有自己的三角形位置的数组,你可以转变他们用ModelMesh的Bone转化。无论如何改变,你目前设定在模型的那部分上,你可以找到三角形的位置在绝对的3D空间里靠使用绝对的ModelMesh转换矩阵来转化他们。这是必须的,由于在大多数情况下,3D射线会被定义在绝对的3D空间里,这样你需要知道绝对顶点的3D位置,如果你想做任何他们之间的比较形式。
一旦你知道模型的每一个三角形绝对3D位置,这个问题迫使Ray-Triangle检查为每模型的一个三角形。
它是如何工作的
由于必须的4-15节内容管道使用一个自定义的类(三角形),很容易的方法是添加内容管道到你的解决方案。右击你的解决方案添加Add-Existing Project.浏览到内容目录包含到你内容管道工程中.csproj文件,选择这个文件。
下一步仍然需要允许你的主要XNA工程访问内容管道工程。参看3-9节更多详细步骤1-5步。最后的两步允许你的XNA工程序列化三角形对象,4-15有详细说明。
6。添加新创建的组件到你的主工程中。
7。选择新创建的处理器去处理一个资源。
8。设置项目的属性。
9。在你的主XNA工程中,添加一个引用到你内容管道工程中。
10。添加你内容管道命名空间到主XNA工程的using区块中
请务必不要忘记第7步,选择ModelMeshTriangleProcessor去处理你的导入模型。这个处理器将会保存一个三角形对象数组在模型的每一个ModelMesh的Tag属性里。
Transformations
现在你有权使用所有模型顶点位置,你可以定义一个方法检查Rey-Model碰撞:
这时候应该提醒你,保存在三角形对象内的位置是于ModelMesh的初始状态相关的。意思是他们必须首先改变用ModelMesh的绝对变换矩阵去获得他们相对与模型初始的位置。接下来,他们需要被转换靠模型的世界矩阵来获得他们相对于3D世界的位置(换句话说,就是绝对位置)。
第一种转变看起来象一种负担,但事实是它提供了一个巨大的优势:如果你旋转模型的一个ModelMesh(例如,人的胳膊),这个旋转这里需要被考虑。在你改变了胳膊顶点的位置靠绝对矩阵变换之后,你得到她们的相对于模型的初始位置。
在前面的代码,你首先计算绝对变换矩阵为模型的所有的Bones。接下来,你用世界矩阵结合绝对矩阵为了当前的Bone。使用结果矩阵去转变每一个三角形,你提供ModelMesh到绝对3D位置。
最后,为每一个ModelMesh的三角形,你最终确定3D空间的位置,采取任何动作在模型上和模型的位置需要被考虑!That's how far the previous code goes.
Ray-Triangle Collision
一旦你有每个三角形的绝对位置,检查三角形和射线是否碰撞。
如果(Ray-Triangle碰撞)被分为两个部分。首先你要找到射线和三角形平面碰撞的点。由于三角形和交叉点在同样的平面内,这个问题变成了2D问题,如图4-25显示的那样,接下来,检查碰撞点是否在三角形内。
![](http://p.blog.csdn.net/images/p_blog_csdn_net/helloali/EntryImages/20090802/XNA4-17-1.JPG)
你将编写两个方法处理两个部分的问题。第一种方法,RayPlaneIntersection,接收一个平面和一条射线,返回在射线上碰撞的点的距离。从这个距离你很容易的计算射线和三角形之间的交叉点。
第二种方法,PointInsideTriangle,接收三角形的三个坐标,和额外的坐标点(交叉点)。这个方法将返回点是否在三角形内。
如果是这种情况,你必须检测射线和模型之间的碰撞。这个代码应该取代两行伪代码:
注意:欲了解更多的射线对象的例子,参看4-11和4-19节
在射线和平面之间找一个碰撞点
你非常幸运,第一个方法相当精确的。不要担心的太多;我已经选择了一个基于这个向量的方法,如图4-26。
![](http://p.blog.csdn.net/images/p_blog_csdn_net/helloali/EntryImages/20090802/XNA4-17-2.JPG)
假定一个射线和一个平面,这个方法找到从ray.Direcion点到collisionPoint点的距离,基于图4-26所显示的方法。注意所有变量的长度在这个方法中的在图象的左和右显示出来了!
4-18 使用Pre-三角形检查,检测射线模式的碰撞
问题
你想检测3D射线是否与模型碰撞。这是必须的,如果你想执行一个碰撞检测在最好的详细程度。它可以被用来作为4-11节的一个扩展去检测在高级别的细节中,是否有一颗子弹已经击中了一个物体。如下一节所显示的那样,这是可能的,例如,同样可以用来检查指针是否是在当前的模型上。
更多的,它可以被用来作为极细小的检查为模型到模型的碰撞,由于一个三角形的每个边对应一个射线,可以用来测试与另一个模型的碰撞。
这一节将处理很复杂的情况,允许模型分开的部分转化成单独的,使这节适合模型的活动。这些变化将影响模型顶点的最后的位置。例如,如果一个人移动他的胳膊,胳膊最终的顶点位置别改变了。这节将这些变换考虑在内。
或者,您想要一个速成课在三维矢量数学上。
解决方案
这节使用一个自定义混合管道内容创建在4-13和4-14节中,因为它保存一个三角形数组对象在每一个模型的ModelMesh的Tag属性里。每个三角形对象都有三个Vector3定义一个三角形的ModelMesh。
有利于使每个ModelMesh都有自己的三角形位置的数组,你可以转变他们用ModelMesh的Bone转化。无论如何改变,你目前设定在模型的那部分上,你可以找到三角形的位置在绝对的3D空间里靠使用绝对的ModelMesh转换矩阵来转化他们。这是必须的,由于在大多数情况下,3D射线会被定义在绝对的3D空间里,这样你需要知道绝对顶点的3D位置,如果你想做任何他们之间的比较形式。
一旦你知道模型的每一个三角形绝对3D位置,这个问题迫使Ray-Triangle检查为每模型的一个三角形。
它是如何工作的
由于必须的4-15节内容管道使用一个自定义的类(三角形),很容易的方法是添加内容管道到你的解决方案。右击你的解决方案添加Add-Existing Project.浏览到内容目录包含到你内容管道工程中.csproj文件,选择这个文件。
下一步仍然需要允许你的主要XNA工程访问内容管道工程。参看3-9节更多详细步骤1-5步。最后的两步允许你的XNA工程序列化三角形对象,4-15有详细说明。
6。添加新创建的组件到你的主工程中。
7。选择新创建的处理器去处理一个资源。
8。设置项目的属性。
9。在你的主XNA工程中,添加一个引用到你内容管道工程中。
10。添加你内容管道命名空间到主XNA工程的using区块中
请务必不要忘记第7步,选择ModelMeshTriangleProcessor去处理你的导入模型。这个处理器将会保存一个三角形对象数组在模型的每一个ModelMesh的Tag属性里。
Transformations
现在你有权使用所有模型顶点位置,你可以定义一个方法检查Rey-Model碰撞:
1 private bool ModelRayCollision(Model model,Matrix modelWorld,Ray ray)
2 {
3 Matrix[] modelTransforms=new Matrix[model.Bones.Count];
4 model.CopyAbsoluteBoneTransformsTo(modelTransforms);
5 bool collision=false;
6 foreach(ModelMesh mesh in model.Meshes)
7 {
8 Matrix absTransform=modelTransforms[mesh.ParentBone.Index]*modelWorld;
9 Trianle[] meshTriangles=(Triangle[])mesh.Tag;
10 foreach(Triangle tri in meshTriangles)
11 {
12 Vector3 transP0=Vector3.Transform(tri.P0,absTransform);
13 Vector3 transP1=Vector3.Transform(tri.P1,absTransform);
14 Vector3 transP2=Vector3.Transform(tri.P2,absTransform);
15 }
16 }
17 return collision;
18 }
这个概念很简单:你开始设置collision=false.模型的每一部分,你会翻阅它的三角形所有的对象,包括三角形的位置。每一个三角形,检查三角形是否与射线碰撞。如果碰撞,返回true.2 {
3 Matrix[] modelTransforms=new Matrix[model.Bones.Count];
4 model.CopyAbsoluteBoneTransformsTo(modelTransforms);
5 bool collision=false;
6 foreach(ModelMesh mesh in model.Meshes)
7 {
8 Matrix absTransform=modelTransforms[mesh.ParentBone.Index]*modelWorld;
9 Trianle[] meshTriangles=(Triangle[])mesh.Tag;
10 foreach(Triangle tri in meshTriangles)
11 {
12 Vector3 transP0=Vector3.Transform(tri.P0,absTransform);
13 Vector3 transP1=Vector3.Transform(tri.P1,absTransform);
14 Vector3 transP2=Vector3.Transform(tri.P2,absTransform);
15 }
16 }
17 return collision;
18 }
这时候应该提醒你,保存在三角形对象内的位置是于ModelMesh的初始状态相关的。意思是他们必须首先改变用ModelMesh的绝对变换矩阵去获得他们相对与模型初始的位置。接下来,他们需要被转换靠模型的世界矩阵来获得他们相对于3D世界的位置(换句话说,就是绝对位置)。
第一种转变看起来象一种负担,但事实是它提供了一个巨大的优势:如果你旋转模型的一个ModelMesh(例如,人的胳膊),这个旋转这里需要被考虑。在你改变了胳膊顶点的位置靠绝对矩阵变换之后,你得到她们的相对于模型的初始位置。
在前面的代码,你首先计算绝对变换矩阵为模型的所有的Bones。接下来,你用世界矩阵结合绝对矩阵为了当前的Bone。使用结果矩阵去转变每一个三角形,你提供ModelMesh到绝对3D位置。
最后,为每一个ModelMesh的三角形,你最终确定3D空间的位置,采取任何动作在模型上和模型的位置需要被考虑!That's how far the previous code goes.
Ray-Triangle Collision
一旦你有每个三角形的绝对位置,检查三角形和射线是否碰撞。
如果(Ray-Triangle碰撞)被分为两个部分。首先你要找到射线和三角形平面碰撞的点。由于三角形和交叉点在同样的平面内,这个问题变成了2D问题,如图4-25显示的那样,接下来,检查碰撞点是否在三角形内。
你将编写两个方法处理两个部分的问题。第一种方法,RayPlaneIntersection,接收一个平面和一条射线,返回在射线上碰撞的点的距离。从这个距离你很容易的计算射线和三角形之间的交叉点。
第二种方法,PointInsideTriangle,接收三角形的三个坐标,和额外的坐标点(交叉点)。这个方法将返回点是否在三角形内。
如果是这种情况,你必须检测射线和模型之间的碰撞。这个代码应该取代两行伪代码:
1 Plane trianglePlane=new Plane(transP0,transP1,transP2);
2 float distanceOnRay=RayPlaneIntersection(ray,trianglePlane);
3 Vector3 intersectionPoint=ray.Position+distanceOnRay*ray.Direction;
4 if(PointInsideTriangle(transP0,transP1,transP2,intersectionPoint))
5 return true;
首先你建立一个三角形平面,基于三角形的三个角点。你传递这个平面和射线到RayPlaneIntersection方法,接收在射线上的交叉点的位置,射线用来计算交叉点的位置。最后,检查交叉点是否在三角形的内部。2 float distanceOnRay=RayPlaneIntersection(ray,trianglePlane);
3 Vector3 intersectionPoint=ray.Position+distanceOnRay*ray.Direction;
4 if(PointInsideTriangle(transP0,transP1,transP2,intersectionPoint))
5 return true;
注意:欲了解更多的射线对象的例子,参看4-11和4-19节
在射线和平面之间找一个碰撞点
你非常幸运,第一个方法相当精确的。不要担心的太多;我已经选择了一个基于这个向量的方法,如图4-26。
假定一个射线和一个平面,这个方法找到从ray.Direcion点到collisionPoint点的距离,基于图4-26所显示的方法。注意所有变量的长度在这个方法中的在图象的左和右显示出来了!
1 private float RayPlaneIntersectoin(Ray ray,Plane plane)
2 {
3 float rayPointDist=-plane.DotNormal(ray.Position);
4 float rayPointToPlaneDist=rayPointDist-plane.D;
5 float directoinProjectedLength=Vector3.Dot(pane.Normal,ray.Direction);
6 float factor=rayPointToPlaneDist/directionProjectionLength;
7 return factor;
8 }
2 {
3 float rayPointDist=-plane.DotNormal(ray.Position);
4 float rayPointToPlaneDist=rayPointDist-plane.D;
5 float directoinProjectedLength=Vector3.Dot(pane.Normal,ray.Direction);
6 float factor=rayPointToPlaneDist/directionProjectionLength;
7 return factor;
8 }