|
This tutorial helps you determine if a ray passes through a triangle. This type of situation is usefull for collision detection between bullets and objects in the world as well as laser-sight on a gun. This tutorial assumes you have a decent understanding of 3D maths and are familiar with some of the basic DirectX maths functions. |
THE RAY
|
I had a terrain class in my C++ game engine that used ray-tracing to determine the height at specific points on the terrain. The start of the ray
was the character height above the terrain and the end was some arbitrary point like 1000 units down along the y axis. I performed a collision
check with the triangle the character was on and it returned the intersection point; the height on the terrain used to position the character. I
used the 3D maths functions that came with the DirectX 8.1 SDK to make my life a lot easier but you still have to know the basics of 3D
maths to understand things!
|
|
First off you need the start and stop points (or position vectors) of the ray. Think of the ray like the laser-sight on a gun, the start point is on the gun barrel and the stop point is some far away distance; you could use the size of your entire world for this distance if you wanted, so far the gun and ray point in the same direction. The ray is simply the direction vector from the start position to the stop position. |
D3DXVECTOR3 start(gunx,guny,gunz); // ray start position D3DXVECTOR3 stop(endx,endy,endz); // ray stop position D3DXVECTOR3 ray = stop-start; // ray difference vector
|
Now you need to cycle through every single triangle in your entire world (each 3D object at a time) and look for an intersection between your ray and every single triangle. Each triangle will give you 3 position vectors representing the three corners and using this you can create an infinite plane. |
// create infinite plane D3DXPLANE vPlane; D3DXPlaneFromPoints(&vPlane, &D3DXVECTOR3(triA.x,triA.y,triA.z), &D3DXVECTOR3(triB.x,triB.y,triB.z), &D3DXVECTOR3(triC.x,triC.y,triC.z)); // infinite plane normal D3DXVECTOR3 vPlaneNormal(vPlane.a,vPlane.b,vPlane.c);
|
The plane is basically at the same position and orientation of the triangle but spans infinitely in all directions. The normal of the plane is another directional vector like the ray that lets you determine the planes direction. Now you need to use a dot product to first off check that the ray is not running parallel along side the plane because if it is then there won't be any intersection. |
D3DXVec3Dot(&vPlaneNormal,&ray)
|
If the result of that is 0 then the ray and plane normal are perpendicular which means the ray and plane are parallel. However if this is not the case then there should be an intersection. |
if(D3DXVec3Dot(&vPlaneNormal,&ray)==0)
{
return 0.0f;
} else {
float amount = -(D3DXPlaneDotCoord(&vPlane,&start))
/ D3DXVec3Dot(&vPlaneNormal,&ray);
D3DXVECTOR3 iPoint = start+(ray*amount);
}
|
So if there's an intersection then you can calculate how far into the ray from the start position you must traverse before you reach the intersection point (amount). That little bit of code there does that for you and the resulting iPoint position vector is the intersection point. So iPoint.x, iPoint.y and iPoint.z are your intersection coordinates. |
THE INTERSECTION POINT
|
However your not done yet, your using an infinte plane so the intersection could have occured anywhere on the plane. Your looking to see if this
intersection occurred within the triangle and not the entire infinite plane.
|
D3DXVECTOR3 IA(triA.x - iPoint.x, triA.y - iPoint.y, triA.z - iPoint.z); D3DXVECTOR3 IB(triB.x - iPoint.x, triB.y - iPoint.y, triB.z - iPoint.z); D3DXVECTOR3 IC(triC.x - iPoint.x, triC.y - iPoint.y, triC.z - iPoint.z);
|
triA, triB and triC are the position vectors of each point on the triangle. Now at this point we can calculate the angles. You may see a function called 'mag()' below, this basically means the magnitude of the vector. So a vector A with points A.x, A.y and A.z would have a magnitude of sqrt(sqr(A.x)+sqr(A.y)+sqr(A.z)) |
float ang_IAIB = acos(D3DXVec3Dot(&IA,&IB)/(mag(IA)*mag(IB))); // IA and IB angle float ang_IBIC = acos(D3DXVec3Dot(&IB,&IC)/(mag(IB)*mag(IC))); // IB and IC angle float ang_ICIA = acos(D3DXVec3Dot(&IC,&IA)/(mag(IC)*mag(IA))); // IC and IA angle float sumOfAngles = ang_IAIB + ang_IBIC + ang_ICIA;
|
if the sumOfAngles is roughly equal to 360 degrees or 2Pi (about 6.28) then my friend the intersection point lies within the triangle and you have a hit. Now you gotta do this for each and every triangle in the scene... Good Luck! |
THE AFTERMATH
|
So basically you can perform these calculations in the background and once the player presses the fire button you can determine the intersection
point straight away and position your bullet hole, reduce the target objects life or whatever.
|