Y
our collision detection routine is running flawlessly now, returning a surface point and a normal back when you feed it a position and velocity vector. Now what? Actually, there are quite a few things you can do based on the data you have.In general, you may want to have your collision routine generate the actual colli-sion point, but the methods in this gem show how to handle collicolli-sion results that show only the plane of intersection. Since a plane can be fully specified with a surface normal and any point on the plane, you can work your way through the math to find everything you need from there.
Data that goes into your collision code would be an initial point Pt and a final point Pf, and the output in the case of a collision would be a plane that is defined by a unit vector surface normal TV and a point on the surface Ps. The point need not be the actual intersection point as long as it is on the plane.
For optimization purposes, you will probably want to build a subset of these cal-culations back into your collision code. Much of the information you need will have already been calculated during the collision tests. It will be much faster to reuse the already known information rather than recalculate it from scratch.
The plane equation Ax + By + Cz + D = 0 maps onto the supplied data, where x, y, and z are the components of the normal vector N, and D is the dot product N* Ps.
Altitude Relative to the Collision Plane
One of the most commonly used pieces of data when checking collisions is the alti-tude of one of your data points. If the altialti-tude is positive, the point is above the sur-face and has not collided yet. If it is negative, you have collided and penetrated the surface.
Typical collision testing code will only return a hit if one of your test points is on each side of the test surface. This means that if you want to predict collisions, you need to pass along a position with an exaggerated velocity vector. That way, the exag-gerated vector will intersect much earlier than your actual movement would allow.
Once you have tricked your collision code into returning a surface point and nor-mal, you can get your altitude relative to that surface by using your initial position.
The final position is not used for this altitude calculation.
182
FIGURE 2.2.1 Determining the altitude.
As shown in Figure 2.2.1, we want to find the length of the vector (Ps - Pt) when it is projected onto the surface normal N. This gives us the distance of the shortest line from the point to the surface. This shortest vector is by definition perpendicular to the surface. This is exactly what the dot product gives us, so we are left with the scalar (nonvector) distance to the surface Ds as shown:
Nearest Point on the Surface
Once we have the distance to the surface, it takes just one more step to get the point on the surface PH that is closest to the initial point Pf as also shown in Figure 2.2. 1 . We already know the point is distance Ds from die starting point, and that distance is along the surface normal TV. That means die point can be found with the following:
Ptt = P,-DsN
The normal vector is facing the opposite direction of the distance we want to measure, so it needs to be subtracted from the starting point.
Pinning Down the Collision Point
When you have one point on each side of a test surface, your vector must at some point intersect with it. Finding this intersection point Pc will tell you where your vec-tor pierces die surface. If your collision detection code has not already provided you with the exact point, here is how you would find it.
You know that the collision point must lie somewhere along the line. Knowing in advance that there is a collision makes it possible to take some shortcuts since we know there actually is a solution to the equation. If the test ray is parallel to the sur-face, the ratio cannot be calculated since it results in a divide by zero. We can take advantage of the calculation for Ds in finding die collision point Pc. Figure 2.2.2 shows the information needed for this calculation.
FIGURE 2.2.2 Finding the collision pointPc.
Since we know the collision is between the two points, we can find it by calculat-ing how far it is along the line from P/ to Pf. This ratio can be written as:
R = ((Pi - P,) • N) I ((% - Pf) • N)
Or, using our already computed Ds, it becomes:
R = D,I ((P,. - Pf) • N)
The two line segments are arranged to both point in the same direction relative to the surface normal, which guarantees that our ratio will be non-negative. Once we have this ratio, we can use it to multiply the length of the vector from P, to Pyto tell how far from P, the collision occurs. In the special case of R = 1, you can avoid the cal-culation since it results in the point Pf For R = 0, the point is P,-. Otherwise, the fol-lowing equation is used:
Pe=Pf + R(Pf - P^
Distance to the Collision Point
Although similar to Ds, this differs from the distance from the collision plane because the distance is calculated along the path of travel rather than along the surface normal.
In the case of travelling near the surface but nearly parallel to it, your distance to col-lision will be very large when compared to your altitude.
This is the type of calculation you would want to use when calculating altitude for an aircraft, since you cannot guarantee the direction of a surface normal on the ground below. Rather than sending your actual velocity for a collision test, you would send your current position and a very large down vector, long enough to guarantee that it will intersect the ground. This works in the case of intersecting a small polygon
FIGURE 2.2.3 Calculating distance to the collision point.
that happens to be aligned nearly perpendicular to the test vector. In that case, the altitude relative to the collision plane Pn as calculated earlier would give a very small number.
Once you have the actual collision point, it's very easy to calculate the distance using Euclid's equation to find how far it is from the starting point to the collision point. Figure 2.2.3 shows the elements required. The distance to the collision point, Dc, is the magnitude of the vector from our starting point /J to the collision point Pe
that was calculated earlier:
Another way to describe the magnitude of this vector is that it is the square root of the sum of the squares of the differences of each component of the vector. Most vector libraries include a function call to find the magnitude or length of a vector.
Vector magnitudes are never negative.
Another possible shortcut can be used when you know the ratio R used to find the collision point as described in the previous section. The distance to the collision point is the length of the full line (which you may already have lying around) multi-plied by the already computed ratio.
D. = RPf-Pi
Reflecting Off the Collision Plane
The usual result of a collision is to bounce. The interesting part is figuring out the direction and position once you have rebounded off a surface. Figure 2.2.4 shows the elements used in calculating the reflected vector. The first two cases will perfectly
2N((Ps-Pf)-N) P.
P.-Pf
FIGURE 2.2.4 Calculating the reflected vector.
preserve the magnitude of the velocity. In both cases, the result of the bounce will be the same distance from the plane as Pf.
One of the simplest ways to visualize reflecting a point relative to a plane is to imagine a vector from the below-ground destination point back up to the surface along the surface normal. The new reflected location is found by continuing that line an equal distance to the other side of the plane. You obtain the new location by adding to the final point twice the distance from the final point to the surface.
Reusing our equation to find the distance perpendicular to the plane, we come up with the following. The distance is multiplied by the surface normal to turn it back into a vector since you cannot add a simple scalar value such as Ds to a vector.
The original and reflected vectors will have the same angle relative to the plane.
Another way of looking at this is that if you normalize the vectors from your collision point going out to both your original start point and your reflected point, then find a dot product of each with your surface normal; they will be equal.
Vectors are normalized by dividing the vector by its length or magnitude, so the statement about reflected vectors in the previous paragraph can be written as:
p~p,
P.-P.
p - p
-±c ±.N
P.-P;
Any point on the plane could be substituted for Pc (Ps works, for instance) in the preceding equation and the same result would hold since all we are saying here is that the ends of the unit vectors are the same distance from the plane.
A complication with reflections is that the newly determined end point needs to be tested all over again with your collision code to see if you have been pushed through some other surface. If you repeat the collision test, but with a vector from your collision point Pc to the newly reflected point Pr, you will get a possible new col-lision. You will need to repeat this process until no collision occurs. At each pass, your
vector will be smaller as it is chewed up by bouncing, as long as you do not try to bounce between two coincident planes.
There is one degenerate case you should also watch out for when chaining colli-sions together. Should you hit exactly at the intersection of two planes, your second test will be made with an initial point on the edge of the surface. This is easy to han-dle if you know about this problem and allow for it in advance by assuming anything exactly at the surface (or on the edge) has not yet collided, and is counted as above the surface for collision purposes. Collisions are reserved for penetration distances greater than zero.
Once you have completed your final reflection, a new velocity vector can be com-puted by normalizing the direction from the last collision to the final reflected loca-tion and multiplying it by the original velocity like this:
V = (Pr - PcPt - Pf P.-P.
Kickback Collision
Sometimes, rather than reflect off the collision plane, you want to kick the player back the way he or she came as shown in Figure 2.2.5. The calculations for this are simple once you have the collision point. Since this collision also preserves velocity, it is also perfectly elastic.
The point to which you are kicked back, /^, is obtained by calculating the vector from your final point Pf back to your collision point Pe and adding it to the collision point.
We can combine terms to get:
Pk = 2Pc - Pf
FIGURE 2.2.5 Calculating a kickback vector.
You can run into the same problems with kickback collisions as with reflections where the destination point leads to an additional collision. However, there is an early way out of the loop for kickback collisions in some cases. If the collision point is more than halfway from the initial point to the final point, the resulting kickback point will occur in an area that has already been checked for collisions, so no additional check is necessary.
Collisions with Damping
Should you want to perform a collision with some sort of friction or damping, you will need to be careful of how you handle the vectors. You will need a scalar value S that will be used as a multiplier to change your velocity at each impact. It will typically range from zero (the object stops on impact) to one (a completely elastic collision preserving velocity). Energy can be injected into the system by increasing the scalar above one. A kickback collision is the same as using a scalar of negative one.
To properly handle nonelastic collisions, you must scale only the portion of the vector from the collision point Pe to the reflected point Pr as shown in Figure 2.2.6, since that is the only portion of the flight that will have been slowed due to the impact. The following equation relies heavily on the earlier equations to determine what your new slowed point would be.
In coordination with this, you would need to multiply any separately stored velocity vector by the same scalar value, or your object will resume its full speed the next frame. In the case of a collision putting your point into another immediate colli-sion as discussed earlier, this scale factor should be applied at each pass to simulate the damping effect of multiple bounces in a single frame.
FIGURE 2.2.6 Calculating a damped reflection vector.
Interpolation Across a Line or Plane
An interesting side note about lines and planes is that a weighted average interpola-tion between any set of points occupies the space defined by those points. For instance, starting with a line, you can assign weights to the end points where the weights sum to one, and all possible resulting points are on the line defined by the points. Adding another point to build a plane extends the rule so the sum of the three weights must equal one in order for the weighted sum to remain on the plane defined by the three points. Additional points on the plane may be added, and addi-tional dimensions may also be added should you have need for a point on an n-dimensional plane.
This trick is related in a roundabout way to the reason we can often use Ps and Pc
interchangeably in several of the previous equations. Either point is sufficient to fill the needs of the plane equation.
It's also interesting to note that the individual weights don't need to be between zero and one. They just need to all sum up to the value one, which allows the result-ing point to be outside the line segment or polygon defined by the points while still being on the extended line or plane.
Sphere-to-Plane Collision
Colliding a ball with a surface is a little bit more complex than colliding a point against the surface. One way to approach it is through ratios. If you draw a line from the start point P± to the line-based collision point Pc of the vector against the plane, die ball center Ph will be somewhere on that line when the ball begins to intersect the plane.
When the ball just touches the surface, we can compare the line from Pf to Pc to the line Pf to Pn to gain the information we need. If you project the first line onto the second, the ball center is off the surface by exactly the ball radius r on the line Pj to Pn. Since the length of that line is known to be Ds, we can get a ratio of how far the ball is along the line. This is similar to the way we used a ratio to find the surface collision point Pc. This ratio is the same when applied to the line from P{ to Pc, which leads to the equation:
(Pe ~ Pi) DS
The equation can be solved for the location of the ball /^, resulting in:
p -p V<-Pi*
Pb-lC ~
FIGURE 2.2.7 Colliding with a sphere.
Figure 2.2.7 shows the relation graphically, indicating where the sphere is in rela-tion to the vectors. Care must be taken to notice that the ball does not actually reach Pc as the ball touches the surface unless the line from Pf to Pc is perpendicular to the surface. As the vector conies closer to being parallel to the plane, the ball will be far-ther from Pc when it touches the plane.