Showing posts with label vector. Show all posts
Showing posts with label vector. Show all posts

Friday, November 11, 2011

Ballistic trajectory to travel between two points, and rotating a point around an axis

The formula for calculating ballistic trajectory is tricky, luckily one doesn't have to reinvent the wheel to do it  because someone has already done it for us. If you would like to see the math behind how it is calculated, click here. Be aware this does not account for wind or air resistance, so if your game is realistic enough to have either, you'll probably want to look here. Today we're just looking at the version without air resistance.



Ballistic trajectory is the angle required to launch an object from one point to another. For example if you wanted to fire a cannonball out of a cannon to a reticule that the player gets to aim, you would need something like this:
// Returns true if 'end' can be reached at the given 'speed', otherwise
// it returns false.
bool CalculateTrajectory(const Vector3& start, const Vector3& end,
                     const float speed, const float gravity,
                     const bool bUseHighArc, Vector3& outTrajectory, 
                     float& outAngle)
{
    bool canHit = false;

    // We use doubles instead of floats because we need a lot of
    // precision for some uses of the pow() function coming up.
    double term1 = 0.0f;
    double term2 = 0.0f;
    double root = 0.0f;

    Vector3 diffVector = destination - origin;

    // A horizontally-flattened difference vector.
    Vector3 horzDiff = Vector3(diffVector.x, 0.0f, diffVector.y);
  
    // We shrink our values by this factor to prevent too much
    // precision loss.
    const float factor = 100.0;

    // Remember that Unitize returns length
    float x = horz.Unitize() / factor; 
    float y = diffVector.y / factor;
    float v = speed / factor;
    float g = gravity / factor;

    term1 = pow(v, 4) - (g * ((g * pow(x,2)) + (2 * y * pow(v,2))));

    // If term1 is positive, then the 'end' point can be reached
    // at the given 'speed'.
    if ( term1 >= 0 )
    {
        canHit = true;

        term2 = sqrt(term1);

        double divisor = (g * x);

        if ( divisor != 0.0f )
        {
            if ( bUseHighArc )
            {
                root = ( pow(v,2) + term2 ) / divisor;
            }
            else
            {
                root = ( pow(v,2) - term2 ) / divisor;
            }

            root = atan(root);

            angleOut = static_cast<float>(root);

            Vector3 rightVector = horz.Cross(Vector3::UnitY);

            // Rotate the 'horz' vector around 'rightVector' 
            // by '-angleOut' degrees.
            RotatePointAroundAxis(rightVector, -angleOut, horz); 
        }

        // Now apply the speed to the direction, giving a velocity
        outTrajectory = horz * speed;
    }

    return canHit;
}

Bear in mind that the above function assumes an 'up' direction of +Y, and also assumes gravity to be -Y, which is why only a float is needed to represent gravity, rather than a vector.

The formula used by this function gives two trajectories to reach the 'end' point, the highest possible trajectory, and the lowest possible trajectory. The 'bUseHighArc' variable passed in is what determines which result is used.

You'll notice a reference to a new function called 'RotatePointAroundAxis' in there, I have not yet gone over matrix mathematics in my blog, and rather than get into that now I will supply you with the math required create a rotation matrix and use that to rotate our point. If you think of the example I gave earlier about firing a cannon ball, imagine that the cannon was spun around to face the direction it needs to fire, but hasn't yet been elevated to the correct firing angle, this function is what we're using to rotate our currently flat 'horz' vector into the air.

Vector3 RotatePointAroundAxis( const Vector3& axis, const float
                               radians, const Vector3& point )
{
    float matrix[3][3];

    float sn = sinf(radians);
    float cs = cosf(radians);

    float xSin = axis.x * sn;
    float ySin = axis.y * sn;
    float zSin = axis.z * sn;  
    float oneMinusCS = 1.0f - cs;
    float xym = axis.x * axis.y * oneMinusCS;
    float xzm = axis.x * axis.z * oneMinusCS;
    float yzm = axis.y * axis.z * oneMinusCS;

    matrix[0][0] = (axis.x * axis.x) * oneMinusCS + cs;
    matrix[0][1] = xym + zSin;
    matrix[0][2] = xzm - ySin;
    matrix[1][0] = xym - zSin;
    matrix[1][1] = (axis.y * axis.y) * oneMinusCS + cs;
    matrix[1][2] = yzm + xSin;
    matrix[2][0] = xzm + ySin;
    matrix[2][1] = yzm - xSin;
    matrix[2][2] = (axis.z * axis.z) * oneMinusCS + cs;

    return Vector3
    (
        matrix[0][0] * point.x + matrix[0][1] * point.y + matrix[0][2] * point.z,

        matrix[1][0] * point.x + matrix[1][1] * point.y + matrix[1][2] * point.z,

        matrix[2][0] * point.x + matrix[2][1] * point.y + matrix[2][2] * point.z
    );   
}


Normally you wouldn't have to create your own function to rotate a point around an axis, this would generally be part of the Matrix class you would find in a math library. What you'll often find when programming is that you need to understand the concept behind which functions you're using so that you can make an educated decision about which functions you will need to perform a task. There is little need for you to memorize the exact function above, because you'll have it as part of an engine or you can look it up online, but knowing how the function works is something you might want to do some day. For now I'll skip the lesson on matrix math.

Quaternion Math: Getting local axis vectors from a quaternion

If your game uses the standard coordinate system of X representing left/right vector, Y representing up/down vector, and Z representing forward/backward vector, then this following method should allow you to convert a quaternion into a vector that represents 'forward', 'up', and 'right'.

I've found these extremely useful, as this is much more efficient than converting the quaternion into a matrix just so you can extract the column/row you need from the matrix for the vector you want.

Vector3 Quaternion::GetForwardVector() const
{
    return Vector3( 2 * (x * z + w * y), 
                    2 * (y * x - w * x),
                    1 - 2 * (x * x + y * y));
}

Vector3 Quaternion::GetUpVector() const
{
    return Vector3( 2 * (x * y - w * z), 
                    1 - 2 * (x * x + z * z),
                    2 * (y * z + w * x));
}

Vector3 Quaternion::GetRightVector() const
{
    return Vector3( 1 - 2 * (y * y + z * z),
                    2 * (x * y + w * z),
                    2 * (x * z - w * y));
}

Sunday, November 6, 2011

Using Vector Mathematics, finding distance between points

Finding the distance between points is fairly simple, especially if you're familiar with the Pythagorean Theorem. The Pythagorean Theorem is the formula for finding the length of the hypotenuse of a triangle, which is essentially from finding the distance between two points in 2D space.

Pythagorean Theorem in its simplest form:
a² + b² = c²

If you've taken geometry then you likely know about a 3-4-5 triangle. Which is a commonly known fact that a right triangle can legally have sides with lengths of 3, 4, and 5. Using the Pythagorean Theorem we can prove this:
3² + 4² = 5², or 9 + 16 = 25, and √25 = 5.

So how is this related to finding the distance between two vectors? Let's think of the formula in a different way:
x² + y² = length², so with a 2D vector of (3, 4), that vector will have squared length of 25. Finding the length of the vector (3, 4) is the same as distance between (0, 0) and (3, 4), so you now essentially know how to find the distance between two points. If one of points is not (0, 0) you just use vector subtraction to get the difference between the two vectors, and get the length of that. For example, if we have two points, (1, 4) and (3, 5), the difference between them is ( 3-1, 5-4 ), which comes to (2, 1), and the length of (2, 1) is √(2² + 1²) or √5.


As you can see, finding the length of a 2D vector is essentially using the Pythagorean Theorem. A 3D version of this is just as you might imagine:
x² + y² + z² = length², which simplifies to √(x² + y² + z²)


So here's the function to get the length of a vector:

float GetLength( const Vector3& inVect )
{
    return sqrtf( inVect.x * inVect.x
                + inVect.y * inVect.y
                + inVect.z * inVect.z );
}

And you'll most likely want this function as part of the Vector class itself, here's a version for that:

inline float Vector3::Length() const
{
    return sqrtf( x*x + y*y + z*z );
}

And a nice helper function for finding the distance between two points, using the GetLength() function we already made above:

inline float DistanceBetweenTwoPoints( const Vector3& first, 
                                       const  Vector3& second )
{
    const  Vector3 differenceVector = second - first;
    return GetLength(differenceVector);
}