Advanced vector mathematics in game development

aircraft

The dot product has another interesting property with unit vectors. Imagine that a plane perpendicular to the vector (and passing through the origin) passes through a plane. The plane divides the entire space into positive numbers (on the plane) and negative numbers (under the plane), and (contrary to popular belief), you can also use its mathematical operations in 2D:

../../_images/tutovec10.png

The unit vectors perpendicular to the surface (thus, they describe the direction of the surface) are called unit normal vectors . Although, usually they are simply referred to as normals. Normals appear in airplanes, 3D geometry (to determine each of the faces or vertex walls), etc. It is usually a unit vector , but it is called normal because of its usage. (Like we call (0,0) the origin).

It seems very simple. The plane passes through the origin, and its surface is perpendicular to the unit vector (or normal). One side of the pointing vector is the positive half-space, and the other side is the negative half-space. In 3D, this is exactly the same, except that the plane is an infinite surface (imagine an infinite flat paper that can be oriented and fixed to the origin) instead of a line.

Distance to the plane

Now that it is clear what an airplane is, let us return to the dot product. The dot product between the unit vector and any point in space (yes, this time we do the dot product between the vector and the position), returning the distance from the point to the plane:

var distance = normal.Dot(point);

But not only the absolute distance, if the point is in a negative half-space, the distance will also be negative:

../../_images/tutovec11.png

This allows us to know which side of the plane a point is on.

Away from origin

I know what you are thinking! So far, this is not bad, but real airplanes are everywhere in space, not only passing through the origin. You want real airplane action, you want it now.

Remember that the plane not only divides the space into two parts, but it also has polarity. This means that there can be completely overlapping planes, but their negative and positive half spaces will be interchanged.

With this in mind, let us describe the entire plane as the distance between the normal N and the scalar D from the origin. Therefore, our plane is represented by N and D. E.g:

../../_images/tutovec12.png

For 3D mathematics, Godot provides Plane built-in types to handle.

Basically, N and D can represent any plane in space, whether it is 2D or 3D (depending on the dimension of N), and the mathematical formulas of the two are the same. Same as before, but D is the distance from the origin to the plane, traveling in the N direction. For example, suppose you want to reach a certain point on the plane, you would do the following:

var pointInPlane = N * D;
This will stretch (resize) the normal vector and make it touch the plane. This mathematical operation may seem confusing, but it is actually much simpler than it looks. If we want to say the distance from the point to the plane again, we can do the same thing, just adjust the distance:

var distance = N.Dot(point) - D;

The same operation using built-in functions:

var distance = plane.DistanceTo(point);

This will return the positive or negative distance again.

The polarity of the plane can be reversed by making both N and D negative. This will cause the plane to be in the same position, but with negative and positive half-angle inversion:

N = -N;
D = -D;

Of course, Godot can also implement this operator in Plane, so please do the following:

var invertedPlane = -plane;

Will work as expected.

Therefore, please remember that the airplane is just like this, and its main practical use is to calculate the distance to it. So, why is it useful to calculate the distance from a point to a plane? This is very useful! Let's look at some simple examples.

Construct a plane in 2D

The plane obviously does not emerge from anywhere, so it must be constructed. Constructing them in 2D is easy, either from a normal (unit vector) and one point, or from two points in space.

For normals and points, since the normals have already been calculated, most of the work has been completed, so we only need to calculate D based on the dot product of normals and points.

var N = normal;
var D = normal.Dot(point);

For two points in space, there are actually two planes passing through them, they share the same space, but the normals point in opposite directions. To calculate the normal from two points, you must first obtain the direction vector and then rotate it 90° to either side:

// Calculate vector from `a` to `b`.
var dvec = (pointB - pointA).Normalized();
// Rotate 90 degrees.
var normal = new Vector2(dvec.y, -dvec.x);
// Alternatively (depending the desired side of the normal):
// var normal = new Vector2(-dvec.y, dvec.x);

The rest is the same as the previous example, because point_a or point_b are both on the same plane, so they both work:

var N = normal;
var D = normal.Dot(pointA);
// this works the same
// var D = normal.Dot(pointB);

Performing the same operation in 3D mode will be slightly more complicated, which will be explained in detail later.

Some examples of airplanes

This is a simple example where the plane is useful. Suppose you have a convex polygon. For example, rectangles, trapezoids, triangles, or any polygons that do not curve inward.

For each segment of the polygon, we calculate the plane passing through that segment. Once we have the list of planes, we can do neat things, such as checking whether the points are inside the polygon.

We traverse all planes, and if we can find a plane with a positive distance to the point, then the point is outside the polygon. If we can't, then the point is inside.

../../_images/tutovec13.png

The code should look like this:

var inside = true;
foreach (var p in planes)
{
    
    
    // check if distance to plane is positive
    if (p.DistanceTo(point) > 0)
    {
    
    
        inside = false;
        break; // with one that fails, it's enough
    }
}

Isn't it cool? But this will get better! With a little effort, when two convex polygons also overlap, similar logic will let us know. This is called the separating axis theorem (or SAT), and most physics engines use it to detect collisions.

For a point, just checking whether the aircraft is returning a positive distance is sufficient to determine whether the point is outside. For another polygon, we have to find a plane on which all other polygon points will return a positive distance. The check is performed using the plane of A relative to the point of B, and then using the plane of B relative to the point of A:

../../_images/tutovec14.png

The code should look like this:

var overlapping = true;

foreach (Plane plane in planesOfA)
{
    
    
    var allOut = true;
    foreach (Vector3 point in pointsOfB)
    {
    
    
        if (plane.DistanceTo(point) < 0)
        {
    
    
            allOut = false;
            break;
        }
    }

    if (allOut)
    {
    
    
        // a separating plane was found
        // do not continue testing
        overlapping = false;
        break;
    }
}

if (overlapping)
{
    
    
    // only do this check if no separating plane
    // was found in planes of A
    foreach (Plane plane in planesOfB)
    {
    
    
        var allOut = true;
        foreach (Vector3 point in pointsOfA)
        {
    
    
            if (plane.DistanceTo(point) < 0)
            {
    
    
                allOut = false;
                break;
            }
        }

        if (allOut)
        {
    
    
            overlapping = false;
            break;
        }
    }
}

if (overlapping)
{
    
    
    GD.Print("Polygons Collided!");
}

As you can see, airplanes are very useful. This is the tip of the iceberg. You may be wondering what happens to non-convex polygons. It can usually be processed by splitting the concave polygon into smaller convex polygons, or using techniques such as BSP (not much used today).

3D collision detection

This is another reward, a reward for patience and compliance with this tutorial. This is another wisdom. This may not be a direct use case (Godot has done collision detection very well), but almost all physics engines and collision detection libraries are using it:)

Remember that converting a convex shape in 2D to a 2D flat array is useful for collision detection? You can detect whether the point is within any convex shape, or whether two 2D convex shapes overlap.

Well, this also applies to 3D, if two 3D polyhedral shapes collide, you will not be able to find the separating plane. If a separating plane is found, the shapes will never collide.

To refresh a bit, a separating plane means that all the vertices of polygon A are on one side of the plane, and all the vertices of polygon B are on the other side. This plane is always one of the end faces of face A or face B.

But in 3D, this method has problems, because in some cases the separation plane may not be found. This is an example of this situation:

../../_images/tutovec22.png

In order to avoid this situation, it is necessary to test some additional planes as separators. These planes are the cross product between the edge of face A and the edge of face B.

../../_images/tutovec23.png

So the final algorithm is this:

var overlapping = true;

foreach (Plane plane in planesOfA)
{
    
    
    var allOut = true;
    foreach (Vector3 point in pointsOfB)
    {
    
    
        if (plane.DistanceTo(point) < 0)
        {
    
    
            allOut = false;
            break;
        }
    }

    if (allOut)
    {
    
    
        // a separating plane was found
        // do not continue testing
        overlapping = false;
        break;
    }
}

if (overlapping)
{
    
    
    // only do this check if no separating plane
    // was found in planes of A
    foreach (Plane plane in planesOfB)
    {
    
    
        var allOut = true;
        foreach (Vector3 point in pointsOfA)
        {
    
    
            if (plane.DistanceTo(point) < 0)
            {
    
    
                allOut = false;
                break;
            }
        }

        if (allOut)
        {
    
    
            overlapping = false;
            break;
        }
    }
}

if (overlapping)
{
    
    
    foreach (Vector3 edgeA in edgesOfA)
    {
    
    
        foreach (Vector3 edgeB in edgesOfB)
        {
    
    
            var normal = edgeA.Cross(edgeB);
            if (normal.Length() == 0)
            {
    
    
                continue;
            }

            var maxA = float.MinValue; // tiny number
            var minA = float.MaxValue; // huge number

            // we are using the dot product directly
            // so we can map a maximum and minimum range
            // for each polygon, then check if they
            // overlap.

            foreach (Vector3 point in pointsOfA)
            {
    
    
                var distance = normal.Dot(point);
                maxA = Mathf.Max(maxA, distance);
                minA = Mathf.Min(minA, distance);
            }

            var maxB = float.MinValue; // tiny number
            var minB = float.MaxValue; // huge number

            foreach (Vector3 point in pointsOfB)
            {
    
    
                var distance = normal.Dot(point);
                maxB = Mathf.Max(maxB, distance);
                minB = Mathf.Min(minB, distance);
            }

            if (minA > maxB || minB > maxA)
            {
    
    
                // not overlapping!
                overlapping = false;
                break;
            }
        }

        if (!overlapping)
        {
    
    
            break;
        }

    }
}

if (overlapping)
{
    
    
    GD.Print("Polygons Collided!");
}

More information

For more information on using vector math in Godot, see the following articles:

Matrices and transformations
If you need additional explanation, please check out 3Blue1Brown’s wonderful video series "The Essence of Linear Algebra": https://www.youtube.com/watch?v=fNk_zzaMoSs&list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab

Guess you like

Origin blog.csdn.net/qq_44273429/article/details/111060722