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 <limits>

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-&gt;X /= num;
		this-&gt;Y /= num;
		this-&gt;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&amp; 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 &lt;= 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<Scalar>(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<Scalar>(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

Hello can I quote some of the information from this entry if I provide a link back to your site?

there is a problem at Vector3.cpp: line 3 empty include line 8 std::numeric_limits

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

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!

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 '. I've usually been good about catching these, so thanks for catching it and notifying me good sir. :grin:

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!

Archive