Jump to content

  • Log In with Google      Sign In   
  • Create Account

We need your help!

We need 1 more developer from Canada and 12 more from Australia to help us complete a research survey.

Support our site by taking a quick sponsored survey and win a chance at a $50 Amazon gift card. Click here to get started!


Most efficient way of designing a vector class in 3D

  • You cannot reply to this topic
13 replies to this topic

#1 Misery   Members   -  Reputation: 322

Like
0Likes
Like

Posted Today, 01:11 AM

Recently I started to use unity3d for my hobby game project and I really liked a Vector3 (and similar) classes (in C#). At the moment at work I am implementing a simple (but for large simulations) SPH solver. What I would like to achieve is a similar Vector3 class in C++ in the means of access to elements both by v.x, v.y, v.z and v[0], v[1], v[2]. In general to obtain this is very simple, but none of the solutions that came to my mind is free of flaws.

 

Solution 1: using references &x, &y, &z, problem: such class has three additional variables which occupy the memory. In the case of large simulation it is problematic:

template <class T>
  class Vector3
  {
    public:
        T &x,&y,&z;
        T v[3];

        Vector3(): x(v[0]), y(v[1]), z(v[2])
        {
          v[0]=0; v[1]=1;  v[2]=2;
        }

        T& operator[](int i)
        {
            return v[i];
        }
  };

The access is very elegant:

v[i] 

as well as

v.x, v.y and v.z

And this is what I woluld like to obtain - this elegant access. However the additional memory overhead is unacceptable.

 

Solution 2: using class fileds x,y,z and access operator with if statement: problem performance of [] operator

template <class T>
  class Vector3
  {
    public:
        T x,y,z;

        Vector3(): x(0), y(1), z(2)
        { }

        T& operator[](int i) 
        {
            if (i==0) return x;
            else if (i==1) return y;
            else if (i==2) reurn z;
            else 
            {
                //throw access error
            }
        }
  };

This solution is elegant as well, but the operator [] will be very slow.

 

Solution 3: Using the class functions x(), y(), z()

template <class T>
  class Vector3
  {
    public:
        T v[3];

        Vector3()
        {
          v[0]=0; v[1]=1; v[2]=2;
        }

        T& operator[](int i)
        {
            return v[i];
        }

       T& x() { return v[0]; }
       T& y() { return v[1]; }
       T& z() { return v[2]; }
  };

This solution is ideal in means of efficiency and memory overhead, but, does not allow elegant access to members, requires for example v.x() instead of v.x.

 

The question is: is there a way to obtain this elegant access with no efficiency and memory loss?

 

 



Sponsor:

#2 Hodgman   Moderators   -  Reputation: 42411

Like
7Likes
Like

Posted Today, 01:16 AM

Not technically portable, but works in practice:

template <class T>
  struct Vector3
  {
        T x,y,z;
        T& operator[](int i)
        {
            return (&x)[i];
        }
  };

BTW, an efficient vector class will use SSE instructions -- i.e. xyz would be stored in a __m128. However, extracting members from a __m128 and into a regular float is slow on x86... so your getX() function should return an __m128 with the x member in all 4 elements. I've often seen this return type called something like class FloatInVec4 laugh.png



#3 imoogiBG   Members   -  Reputation: 1817

Like
15Likes
Like

Posted Today, 01:28 AM

Solution 4

union 
{
    float v[3];
    struct { float x, float y, float z; };
}


#4 Syntac_   Members   -  Reputation: 303

Like
3Likes
Like

Posted Today, 02:38 AM

Yeah union with anonymous struct has got to be the easiest way to do it,

 

Just to expand on imoogiBG's answer to follow suit with templates.

template <class T>
struct Vector3
{
    union
    {
        T v[3];
	struct { T x; T y; T z; };
    };
};

Edited by Syntac_, Today, 02:38 AM.


#5 wintertime   Crossbones+   -  Reputation: 3335

Like
0Likes
Like

Posted Today, 05:33 AM

Couldn't you just use the array access? Will you later want to add r, g, b, a and s, t or u, v?

namespace vectors {

enum accessors {
  x,
  y,
  z,
  w
};

template <typename T>
struct Vector4
{
  T d[4];
  T& operator[](size_t index);
  const T& operator[](size_t index) const;
};

}



#6 TheComet   Crossbones+   -  Reputation: 2196

Like
0Likes
Like

Posted Today, 09:20 AM

How does that code allow me to write "v.x"?


YOUR_OPINION >/dev/null

#7 Álvaro   Crossbones+   -  Reputation: 16555

Like
4Likes
Like

Posted Today, 09:33 AM

There is a solution that is completely portable and last time I checked was fully optimized by the compiler. It was posted by Washu in these forums many years ago:
 
struct vector3 {
	float x, y, z;

	float& operator[](int index) {
		assert(index >= 0 && index < 3);
		return this->*members[index];
	}

	float operator[](int index) const {
		assert(index >= 0 && index < 3);
		return this->*members[index];
	}

	static float vector3::* const members[3];
};

float vector3::* const vector3::members[3] = { &vector3::x, &vector3::y, &vector3::z};

Edited by Álvaro, Today, 09:41 AM.


#8 Syntac_   Members   -  Reputation: 303

Like
0Likes
Like

Posted Today, 09:50 AM

Sorry I forgot to include the operator to be able access like v[0].

template <class T>
struct Vector3
{
    union
    {
	T v[3];
	struct { T x;  T y; T z; };
    };

    T& operator[](int i)
    {
	// guard against accessing out-of-bounds
        return v[i];
    }
};

Vector3<float> v;
v.x = 3.0f;

std::cout << v[0]; // prints 3


#9 wintertime   Crossbones+   -  Reputation: 3335

Like
0Likes
Like

Posted Today, 10:26 AM

How does that code allow me to write "v.x"?

It does not really need to and avoids weird workarounds/UB for a questionable convenience.

But you can easily do:

using namespace vectors;

float bar(const Vector4<float>& v) {
  return v[x];
}


Edited by wintertime, Today, 10:27 AM.


#10 frob   Moderators   -  Reputation: 31414

Like
2Likes
Like

Posted Today, 10:53 AM

Re-iterating what Hodgman wrote, these arrays of floats are functional but not ideal.

 

If you are looking for performance most systems will use built-in SIMD structures for the data that are specific to the system you are developing for.  That can mean the intrinsic type __m128 variables in the x86 family, or the intrinsic float32x4_t variables in ARM chips. 

 

Transferring to and from these packed, special-purpose registers is not efficient. If possible leave your data packed in the more efficient formats.


Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I occasionally write about assorted stuff.


#11 Servant of the Lord   Crossbones+   -  Reputation: 27030

Like
0Likes
Like

Posted Today, 11:12 AM

Solution 4

union 
{
    float v[3];
    struct { float x, float y, float z; };
}

 
 

Yeah union with anonymous struct has got to be the easiest way to do it,


 
Union-based type-punning is non-standard. It is supported by both GCC and Visual Studio, so I use it, but it's important to remember that it's not guaranteed by the standard.
 
I've heard talk of them standardizing it in a future C++ standard, but I'm not sure if they already did that (in C++14 maybe?), or if they are intending to do it, what the status is on it.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal | [Fly with me on Twitter


#12 Waterlimon   Crossbones+   -  Reputation: 3658

Like
0Likes
Like

Posted Today, 11:34 AM

My vector class is basically:

Vector<ComponentType, Dimensions>

 

With typedefs as:

Vec2f

Vec3i

Vec4ui

etc. (for all primitive types for dimensions 1-4)

 

And I chose to ONLY allow array subscript access:

myVector[0] = 1.0f;

 

For matrices (similar in all other aspects), I instead use call operator overload:

myMatrix(0,0) = 1.0f;

Because Im not going to make a fancy proxy class just so I can use [].

 

 

Its just 2 extra characters so I didnt see a reason to hack support for x,y,z,w. Those dont even allow indexing by variable (which is usually needed to avoid code repetition) so it would be a messy combination of array style access and letters, which would probably just lead to bugs and harder to understand code.

 

 

Ill have to add Vector.swizzle(indices) (as in glsls vec.xz for example) because lack of having that has annoyed me recently...

 

If I were to add x,y,z I would probably just use methods named x() y() z() for them. That way I could implement the swizzle stuff like xy() xz() and so on using same syntax.

 

EDIT:

Also I love how VS2015 can create the method definition body for me because creating those by hand is not fun with these template classes...


Edited by Waterlimon, Today, 12:12 PM.

o3o


#13 Chris_F   Members   -  Reputation: 2813

Like
0Likes
Like

Posted Today, 07:42 PM

If you really need vector types like this then I would say just use GLM, but if you want performance then I would say stay away from class VectorN...

 

If you do want a simple vector type and you don't want to use GLM you could use the following in Clang anyway:

 

using Vector3 = float __attribute__((ext_vector_type(3)));
//Vector3 is pretty much identical to vec3 or float3 from GLSL or HLSL

Edited by Chris_F, Today, 07:50 PM.


#14 SOL-2517   Members   -  Reputation: 472

Like
0Likes
Like

Posted Today, 08:26 PM

This is an article from one of the Insomniac Games developers that discusses SIMD instructions and the problem with using vector classes: https://deplinenoise.files.wordpress.com/2015/03/gdc2015_afredriksson_simd.pdf







PARTNERS