A complete vector struct

Started by
46 comments, last by Zakwayda 13 years, 7 months ago
Hi there, this is my first post in gamedev forums ^_^. After some time learning game programming I started to do a basic opengl game engine to experiment and understand low level game programming.

This post is about the use of vector maths in videogames. From the beggining to the end we use vectors for everything, at first in C++, late with scripting, and this means that we need a good vector struct/class.

Sure you have your own vector class/struct or a math library, but i'm trying to this by my self.

So, here is my first and really basic approach of a vector, after some job i think it have the most basic operations we need, but i think that it need some more functions like the VSize and VUnit funcions, what do you think? (I post only the struct and function declarations to save space).

struct sVector{
float X;
float Y;
float Z;

sVector() : X(0.0f), Y(0.0f), Z(0.0f){}
sVector(float px, float py, float pz) : X(px), Y(py), Z(pz){}

sVector operator+(sVector&);
sVector operator-(sVector&);
sVector operator*(sVector&);
sVector operator/(sVector&);
sVector operator+(float);
sVector operator-(float);
sVector operator*(float);
sVector operator/(float);

sVector operator+=(sVector&);
sVector operator-=(sVector&);
sVector operator*=(sVector&);
sVector operator/=(sVector&);
sVector operator+=(float);
sVector operator-=(float);
sVector operator*=(float);
sVector operator/=(float);

bool operator==(sVector&);
bool operator!=(sVector&);
};

float VSize(sVector pv); //returns the vector magnitude
sVector VUnit(sVector pv); //returns the vector with magnitude 1 (normalized).
Advertisement
Hi there, I think that's a good start but there are some simple improvements to be made:

Firstly I would suggest that you look into using const. e.g.

sVector operator+(sVector&);


should be

sVector operator+(const sVector&);


For the assignment operators such as *=. += etc they should return a reference (or even void to prevent people from chaining them).

Also another good general tip is not to use float but rather your own type which you can define for each project so that if you wanted all your maths classes to use double instead then you wouldn't have to change all your files.

I recently wrote my first vector class too so you can have a look at it if you like and hopefully other people can give me tips too. ;)

//i put all my maths/physics code in this namespacenamespace physik{	class Vector3	{	public:		//ctors and dtors		Vector3();		Vector3(real x, real y, real z);		Vector3(real scalarValue);		Vector3(const Vector3& vector3);		//accessor op overloads		real& operator()(const unsigned int i);		const real& operator() (const unsigned i) const;		//member functions		void scale(real scalarValue);		real getLength();		real getLengthSquared();		void normalize();		void set(const real& x, const real& y, const real& z);		//op overloads		Vector3 operator+(const Vector3& rhs) const;		Vector3 operator-(const Vector3& rhs) const;		void operator+=(const Vector3& rhs);		void operator-=(const Vector3& rhs);		void operator*=(const real& rhs);		void operator/=(const real& rhs);		//friend functions and friend op overloads		friend Vector3 operator*(const Vector3& vector3, const real& rhs);		friend Vector3 operator*(const real& lhs, const Vector3& vector3);		friend Vector3 operator/(const Vector3& vector3, const real& rhs);		friend real dot(const Vector3& lhs, const Vector3& rhs);		friend Vector3 cross(const Vector3& lhs, const Vector3& rhs);		//c-style functions for efficiency		friend void addVector3(Vector3& out, const Vector3& lhs, const Vector3& rhs);		friend void addVector3(Vector3& out, const Vector3& vec1, const Vector3& vec2, const Vector3& vec3);		friend void subtractVector3(Vector3& out, const Vector3& lhs, const Vector3& rhs);	private:		real x_;		real y_;		real z_;		//padding var used for efficiency only		real pad_;	};		//friend function prototypes	Vector3 operator*(const Vector3& vector3, const real& rhs);	Vector3 operator*(const real& lhs, const Vector3& vector3);	Vector3 operator/(const Vector3& vector3, const real& rhs);	real dot(const Vector3& lhs, const Vector3& rhs);	Vector3 cross(const Vector3& lhs, const Vector3& rhs);	void addVector3(Vector3& out, const Vector3& lhs, const Vector3& rhs);	void addVector3(Vector3& out, const Vector3& vec1, const Vector3& vec2, const Vector3& vec3);	void subtractVector3(Vector3& out, const Vector3& lhs, const Vector3& rhs);	//include inline function definitions	#include "Vector3.inl"}
I recommend you remove vector-vector multiplication and division, and vector-scalar addition and subtraction. They don't really have any geometrically sound and unambiguous interpretation. If you need these functions for some reasons, make them separate functions with unambiguous names. Furthermore, you're missing scalar-vector operators (you can multiply a vector by a scalar, but not a scalar by a vector). Since these operators must be defined as non-member functions, I recommend you make all (non-assign, that is += and so on) operators non-members for consistency.

At the very least, you probably need to add functions to calculate dot and cross product of two vectors. That will give a basic vector class that can take you quite far.
Quote:Original post by bibalasvegas
Hi there, I think that's a good start but there are some simple improvements to be made:

Firstly I would suggest that you look into using const. e.g.

*** Source Snippet Removed ***

should be

*** Source Snippet Removed ***


No, it should be:

sVector operator+(const sVector&) const;


Quote:Original post by bibalasvegas
Also another good general tip is not to use float but rather your own type which you can define for each project so that if you wanted all your maths classes to use double instead then you wouldn't have to change all your files.


Yeah, but lets be honest, no game developer ever switches their data types to doubles. Using float is fine.

Quote:Original post by Brother Bob
I recommend you remove vector-vector multiplication and division, and vector-scalar addition and subtraction. They don't really have any geometrically sound and unambiguous interpretation


Well, scaling. Whilst they may not have a sound mathematical basis, they do have a sound computing basis and are extremely useful to have. (i.e. as the basis of a dot product). They are operators that tend to be added the second you use SSE (for component wise ops).

[Edited by - RobTheBloke on August 26, 2010 9:13:46 AM]
Quote:Original post by RobTheBloke
Quote:Original post by bibalasvegasBrother Bob
I recommend you remove vector-vector multiplication and division, and vector-scalar addition and subtraction. They don't really have any geometrically sound and unambiguous interpretation


Well, scaling. Whilst they may not have a sound mathematical basis, they do have a sound computing basis and are extremely useful to have. (i.e. as the basis of a dot product).

edit: resolved

[Edited by - phresnel on August 26, 2010 9:33:20 AM]
Quote:Original post by bibalasvegas
I recently wrote my first vector class too so you can have a look at it if you like and hopefully other people can give me tips too. ;)

*** Source Snippet Removed ***


Get rid of this 'w'. It makes it *less* efficient. Only needed if you are using SSE, which you are not.

		//padding var used for efficiency only		real pad_;


Your member functions need tidying up. get funcs should be const. if you pass a float to a function, for god's sake pass it by value. References are pointless in this context. floats/ints/etc should not be const arguments in the function definition in the class (but it is ok to make the args const in the implementation if needed).
		void scale(real scalarValue);		real getLength() const;		real getLengthSquared() const;		void normalize();		void set(real x, real y, real z);


Assignment operators! There is a standard way of doing this for a reason. Don't bastardise the meaning of operators! (It is very bad indeed!).
		const Vector3& operator+=(const Vector3& rhs);		const Vector3& operator-=(const Vector3& rhs);		const Vector3& operator*=(const real& rhs);		const Vector3& operator/=(const real& rhs);


Personally speaking, I hate operators for vector3 classes. They aren't explicit enough in their meaning imho. Personal preference I guess...
Hi again, thanks for yor responses and suggestions.

About the data types, i have my own type definitions but i'm not using it to make code more clear for everyone without explaining my type definitions.

#ifndef CUSTOMTYPES
#define CUSTOMTYPES 1
typedef unsigned char U8;
typedef wchar_t U16;
typedef unsigned int U32;
typedef unsigned __int64 U64;
typedef float F32;
typedef double F64;
typedef char* CString; //<-- i know, MFC

#define COORD F32; //<-- type for coord numers.
#endif

I read something about SIMD types for vector operations, but i'm not using them by now.

About the Const modifier, i'm googling what is the effect of this modifier in C++ because i don't know what it does, i can supose "const" comes from "constant" but o don't understant why you put this modifiers in the parameter and at the end of the member function declaration. This is due tu my poor experience with C++.
The CString thingy is imho not needed. C/C++ programmers know what [const] char* is about. As for the others, stdint.h / cstdint, and #define-macros are evil in C++:

* no scoping
* no typesafety
* sometimes dangerous, very dangerous macro expansion

and some more.


About const: const applied upon a variable in C++ means that you can't assign to that variable anymore; only initialization is allowed. There is const_cast<>, but it can be subtle to be used right, and it is really only there for compatibility with older C-code. Using const_cast<> to assign (or let someone assign) to a variable is blatant abuse.
Hope you are also using a threshold-value of some sort with == operator.
Two vectos are equal if both share the same direction and magnitude.

With floats involved, inaccuracies tend to mount.
Quote:Original post by rmed002
Hope you are also using a threshold-value of some sort with == operator.
Two vectos are equal if both share the same direction and magnitude.

With floats involved, inaccuracies tend to mount.
IMO, a threshold value should not be used with the == operator in this context. Rather, the operator should behave as expected (following the 'principle of least surprise'), and should accordingly be used in situations where exact equality can be expected.

If you want to perform a comparison using an epsilon, write a separate, named function with an epsilon argument for this task.

(Also, the comparison should be expressed in terms of the elements of the vector, not their direction and magnitude - but maybe that's not what you meant.)

This topic is closed to new replies.

Advertisement