This class should have been posted long ago. So, seeing how popular my C++ Vector2 class page has been, I've decided to finally post this for your needs. This version is similar to the "improvements" on the Vector2 class, supposedly helping encapsulation; frankly, that's up for you to decide.

 

Remember, you can hover over the source area and click the "<>" looking image (top right) to pop the source code into a new window to more easily view and copy the code.

Vector3.h

/*  __      __   ___     _____    ____
 *  \ \    / /  / _ \   |  __ \  |    |
 *   \ \/\/ /  / / \ \  | | / /  |  __|
 *    \_/\_/  /_/   \_\ |_| \_\  |_|
 *      Take it to the next Level
 *
 *  Copyright (c) 2009 Brian Ernst.
 */

#ifndef w_Vector3
#define w_Vector3

#include <cmath>

namespace _Warp
{
    typedef float   Scalar;
    typedef int     Bool;

    class Vector3
    {
    public:
        Scalar X;
        Scalar Y;
        Scalar Z;

        Vector3();
        Vector3(Scalar x, Scalar y, Scalar z);

        Vector3     operator+(const Vector3&amp; vector) const;
        Vector3     operator-(const Vector3&amp; vector) const;
        Vector3     operator-() const;
        Vector3     operator*(Scalar num) const;
        Vector3     operator/(Scalar num) const;

        Vector3&amp;    operator+=(const Vector3&amp; vector);
        Vector3&amp;    operator-=(const Vector3&amp; vector);
        Vector3&amp;    operator*=(Scalar num);
        Vector3&amp;    operator/=(Scalar num);

        Bool        operator==(const Vector3&amp; vector) const;
        Bool        operator!=(const Vector3&amp; vector) const;

        static const Vector3 Zero;
        static const Scalar Epsilon;
    };

    inline Bool Vector3::operator==(const Vector3&amp; vector) const
    {
        return X == vector.X &amp;&amp; Y == vector.Y &amp;&amp; Z == vector.Z;
    }

    inline Bool Vector3::operator!=(const Vector3&amp; vector) const
    {
        return X != vector.X || Y != vector.Y || Z != vector.Z;
    }

    inline Vector3 Vector3::operator+(const Vector3&amp; vector) const
    {
        return Vector3(X + vector.X, Y + vector.Y, Z + vector.Z);
    }

    inline Vector3 Vector3::operator-(const Vector3&amp; vector) const
    {
        return Vector3(X - vector.X, Y - vector.Y, Z - vector.Z);
    }

    inline Vector3 Vector3::operator-() const
    {
        return Vector3(-X,-Y,-Z);
    }

    inline Vector3 Vector3::operator*(Scalar num) const
    {
        return Vector3(X * num, Y * num, Z * num);
    }

    inline Vector3 Vector3::operator/(Scalar num) const
    {
        return Vector3(X / num, Y / num, Z / num);
    }
}
#endif

Vector3.cpp:

#include "Vector3.h"

#include 

namespace _Warp
{
    const Vector3 Vector3::Zero = Vector3(0,0,0);
    const Scalar Vector3::Epsilon = std::numeric_limits::epsilon();

    Vector3::Vector3()
    {
    }

    Vector3::Vector3(Scalar x, Scalar y, Scalar z)
        : X( x )
        , Y( y )
        , Z( z )
    {
    }

    Vector3& Vector3::operator+=(const Vector3& vector)
    {
        X += vector.X;
        Y += vector.Y;
        Z += vector.Z;
        return *this;
    }

    Vector3& Vector3::operator-=(const Vector3& vector)
    {
        X -= vector.X;
        Y -= vector.Y;
        Z -= vector.Z;
        return *this;
    }

    Vector3& Vector3::operator*=(Scalar num)
    {
        X *= num;
        Y *= num;
        Z *= num;
        return *this;
    }

    Vector3& Vector3::operator/=(Scalar num)
    {
        this->X /= num;
        this->Y /= num;
        this->Z /= num;
        return *this;
    }
}

Vector3Util.h

/*  __      __   ___     _____    ____
 *  \ \    / /  / _ \   |  __ \  |    |
 *   \ \/\/ /  / / \ \  | | / /  |  __|
 *    \_/\_/  /_/   \_\ |_| \_\  |_|
 *      Take it to the next Level
 *
 *  Copyright (c) 2009 Brian Ernst.
 */

#ifndef w_Vector3Util
#define w_Vector3Util

#include "Vector3.h"

// These two files are not detailed out in this blog post.
#include "Quaternion.h"
#include "TMatrixUtil.h"

namespace _Warp
{
    Scalar len(const Vector3& vect);
    Scalar len2(const Vector3& vect);

    void Clamp(Vector3& vect,Scalar length);
    void Normalize(Vector3& vect);
    void Normalize_s(Vector3& vect);
    void SetLength(Vector3& vect, Scalar length);
    void SetLength_s(Vector3& vect, Scalar length);

    Scalar  Dot(const Vector3& vec1, const Vector3& vec2);
    Scalar  GetAngle(Vector3 vec1, Vector3 vec2);

    Vector3 ToNormalized(const Vector3& vect);
    Vector3 ToNormalized_s(const Vector3& vect);
    Vector3 ToPolar(Scalar x, Scalar y, Scalar z);
    Vector3 ToCartesian(Scalar radius, Scalar angle, Scalar z);
    Vector3 Cross(const Vector3& vec1, const Vector3& vec2);
    Vector3 Rotate(const Vector3& vec1, Scalar angle, const Vector3& axis);

    Vector3 ToEuler(Vector3 axis, Scalar angle);

    inline Scalar len(const Vector3& vect)
    {
        return sqrt(vect.X * vect.X + vect.Y * vect.Y + vect.Z * vect.Z);
    }

    inline Scalar len2(const Vector3& vect)
    {
        return vect.X * vect.X + vect.Y * vect.Y + vect.Z * vect.Z;
    }

    inline void Normalize(Vector3& vect)
    {
        vect /= len(vect);
    }

    inline void SetLength(Vector3& vect, Scalar length)
    {
        vect *= length / len(vect);
    }

    inline Scalar   Dot(const Vector3& vec1, const Vector3& vec2)
    {
        return vec1.X * vec2.X + vec1.Y * vec2.Y + vec1.Z * vec2.Z;
    }

    inline Vector3 ToNormalized(const Vector3& vect)
    {
        return vect / len(vect);
    }

    // This uses a Quaternion combined with the Matrix Utility, neither of which are detailed out in this post.
    inline Vector3  Rotate(const Vector3& vec1, Scalar angle, const Vector3& axis)
    {
        return TransformCoord(Quaternion::FromAxis(axis.X,axis.Y,axis.Z,angle).Get_RotationMatrix(),vec1);
    }

    inline Vector3  ToPolar(Scalar x, Scalar y, Scalar z)
    {
        return Vector3(
            atan2(y,x),
            sqrt(x * x + y * y),
            z);
    }

    inline Vector3  ToCartesian(Scalar radius, Scalar angle, Scalar z)
    {
        return Vector3(
            radius * cos(angle),
            radius * sin(angle),
            z);
    }

    inline Vector3  Cross(const Vector3& vec1, const Vector3& vec2)
    {
        return Vector3(
            vec1.Y * vec2.Z - vec1.Z * vec2.Y,
            vec1.Z * vec2.X - vec1.X * vec2.Z,
            vec1.X * vec2.Y - vec1.Y * vec2.X);
    }
}

#endif

Vector3Util.cpp

#include "Vector3Util.h"

#include "../Constants.h"

namespace _Warp
{
    void Clamp(Vector3& vect,Scalar length)
    {
        Scalar vecLength = len2(vect);

        if(vecLength <= length * length)
        {
            return;
        }

        vect *= length / sqrt(vecLength);
    }

    void Normalize_s(Vector3& vect)
    {
        Scalar vecLength = len2(vect);

        if(vecLength == 0)
        {
            return;
        }

        vect /= sqrt(vecLength);
    }

    void SetLength_s(Vector3& vect, Scalar length)
    {
        Scalar vecLength = len2(vect);

        if(vecLength == 0)
        {
            return;
        }

        vect *= length / sqrt(vecLength);
    }

    inline Scalar   GetAngle(Vector3 vec1, Vector3 vec2)
    {
        if(vec1 == vec2)
        {
            return 0.0f;
        }

        Normalize_s(vec1);
        Normalize_s(vec2);

        Scalar dot = Dot(vec1, vec2) / (len(vec1) * len(vec2));

        dot = dot > 1.0f ? 1.0f : ( dot < -1.0f ? -1.0f : dot );

        return std::acos(dot);
    }

    Vector3 ToNormalized_s(const Vector3& vect)
    {
        Scalar vecLength = len2(vect);

        if(vecLength == 0)
        {
            return vect;
        }

        vecLength = sqrt(vecLength);

        return Vector3(vect.X / vecLength, vect.Y / vecLength, vect.Z / vecLength);
    }

    // Thanks to Martin Baker for this solution
    // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToEuler/index.htm
    Vector3 ToEuler(Vector3 axis, Scalar angle)
    {
        Vector3 out = Vector3();

        Scalar s = sin(angle);
        Scalar c = cos(angle);
        Scalar t = (Scalar)1.0 - c;

        if ((axis.X * axis.Y * t + axis.Z * s) > (Scalar)0.998)// north pole singularity detected
        {
            out.Y = 2 * atan2(axis.X * sin(angle/2), cos(angle/2));
            out.Z = static_cast(W_PI_2);
            out.X = 0;
            return out;
        }
        if ((axis.X * axis.Y * t + axis.Z * s) < (Scalar)-0.998)// south pole singularity detected
        {
            out.Y = (Scalar)-2.0 * atan2(axis.X * sin(angle / (Scalar)2.0), cos(angle / (Scalar)2.0));
            out.Z = -static_cast(W_PI_2);;
            out.X = 0;
            return out;
        }
        out.Y = atan2(axis.Y * s - axis.X * axis.Z * t , 1 - (axis.Y * axis.Y + axis.Z * axis.Z ) * t);
        out.Z = asin(axis.X * axis.Y * t + axis.Z * s) ;
        out.X = atan2(axis.X * s - axis.Y * axis.Z * t , 1 - (axis.X * axis.X + axis.Z * axis.Z) * t);
        return out;
    }
}

You'll notice in the Vector3Util I have some functions ending with "_s", like "Noramlize_s"; this is a notation I'm trying to mean, in this case, a safe normalize function call (won't result in a divide by zero error). So, if you know the vector you're using can't possibly be zero, use the standard Normalize function, if not, then feel free to use Normalize_s. Please let me know if you have a comment on the notation!

Anyway, this Vector3 class and utility are code I use in my personal game development, and in other school projects that I may have, and have used it and my personal math libs in a ray tracer. Concerning the util, I'm still tweaking it, making decisions on how to wrap it in a namespace (like it's own _Math namespace, which was taken out for this post), or perhaps on whether I feel I really need a separate util instead of having them as member functions, which would be more convenient with intellisense at hand. If you're wondering what I'm talking about, check out this article at Dr Dobbs on encapsulation. I personally have to give it another read and determine if I agree with it or not and really want to stick with that methodology.

 

Please leave your thoughts and suggestions! [I'll be uploading the upgraded version of this Vector3 class sometime soon, I realize there are parts of this version that may be a little rough/unfinished, and after that I'll include the source files for you to download]

Comments

user icon leetnightshade
Thank you for thanking me, as others haven't done so! I'm glad to hear my post has been of use, and with that said I'm terribly sorry I haven't posted the source files all nicely packaged to go. I will do so, thanks!
user icon leetnightshade
Thanks!! When I post the code into my WordPress text block it has issues with '<' and '>' thinking I'm using inline HTML; if I post the code in visual mode it doesn't actually preserve the formatting of the code, last time I tested it. So, I'm updating the include, which is '#include <limits.h>'. I've usually been good about catching these, so thanks for catching it and notifying me good sir. :grin:
user icon leetnightshade
Ah, thanks for catching that. The file includes math constants, which is named differently in my up-to-date Vector3 class. One of the constants used here that is in the 'Constants.h' file is 'W_PI_2'. Um, I'll update the constant with something more standard for you and the others out there. Thanks!
user icon gokhan
sorry for different posts but, I also realised that there is line saying #include \"../Constants.h\" what does this have inside in it? It is missing. thanks
user icon gokhan
there is a problem at Vector3.cpp: line 3 empty include line 8 std::numeric_limits
user icon gokhan
Thank you for this great share!
user icon leetnightshade
Sure, feel free to! :smile:
user icon Chad Villaman
Hello can I quote some of the information from this entry if I provide a link back to your site?

Next Post Previous Post