• Advertisement
Sign in to follow this  

Unity Most efficient way of designing a vector class in 3D

This topic is 903 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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?

 

 

Share this post


Link to post
Share on other sites
Advertisement

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_

Share this post


Link to post
Share on other sites

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;
};

}

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

 

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.

 

Yeah it's not, but the world will collapse (literally) so this will never be changed.

Additional tip. It is currently better to implement the methods of the vector class using v[] instead of x,y,z, becase for some reason(at least msvc) the compiler doesn't know that x,y,z are sequental in memory, thus wont use the multiple lane SIMD instructions (for example movss could be used multiple times instead of one movups). 

 

EDIT: I saf the fastcall22 answer; My mistake this was on VS2013, I really don't know about 2015

Edited by imoogiBG

Share this post


Link to post
Share on other sites

for some reason(at least msvc) the compiler doesn't know that x,y,z are sequental in memory, thus wont use the multiple lane SIMD instructions.


Hmm? I don't think this is true. Or am I misunderstanding? I recently SIMD-ified an inner loop in one of my projects and compared the assembly before and after. I was using a home-grown xywz vector4 and Visual Studio 2015 properly used the *PS instructions.

When I get home today, I can double check.

Share this post


Link to post
Share on other sites


Hmm? I don't think this is true. Or am I misunderstanding? I recently SIMD-ified an inner loop in one of my projects and compared the assembly before and after. I was using a home-grown xywz vector4 and Visual Studio 2015 properly used the *PS instructions.

Automatic vectorization sometimes works, sometimes doesn't.  If the compiler happens to recognize a specific pattern it might do it in parallel.

 

The problem is that the result isn't guaranteed.  It is an optimization that sometimes the compiler does, but it could easily stop doing due to a minor change in the code, or an update to the compiler and build tools.

 

And there are many cases where the code could be easily made SIMD where the compiler doesn't recognize it. The programmer could take some few simple actions and vectorize loops with little work.

 

If you want to actually rely on SIMD, you need to explicitly code for it.  

Share this post


Link to post
Share on other sites

I see automatic vectorization as a way to get a free performance boost from old code by simply recompiling it. If you are writing new code with performance in mind you should not count on the compiler automatically doing anything for you.

Share this post


Link to post
Share on other sites

In MathGeoLib I use Hodgman's method, see https://github.com/juj/MathGeoLib/blob/master/src/Math/float3.h#L104 , and that has worked well in practice. For float4 type and __m128 support, I use the union approach, see https://github.com/juj/MathGeoLib/blob/master/src/Math/float4.h#L56 . I favor using those over fancy template accesses, simply because the generated intermediate code is much straightforward, and the code also looks cleaner. (and I extensively unit test on MinGW32/MingW64/GCC/Clang/MSVCs on different platforms so I know when the code is good or not for the platforms I care. Nothing beats rigorous automated testing when it comes to having a peace of mind for correctness)

Share this post


Link to post
Share on other sites
I'm sorry that I have nothing to add, but I never knew that creating a vector class was so difficult! I've learned a great deal just be reading. I'll have to completely reconsider how I implement this in the future.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By Innoc uous
      I'm working on a space game, and I suck at art. I would love to get some help from someone who is more skilled than me. Things I need include modular space ship parts and GUI elements. Nothing too fancy, just functional so I can get a prototype put together. This could potentially become a serious project, but for now this is just a hobby project.
       
      In this video, you can see a few things I already completed
      :2018-02-24 20-08-13.flv2018-02-24 20-08-13.flv
    • By Innoc uous
      If you want to incorporate noise into your shaders, the Turbulance Library has you covered. Using code I gathered from this library, I made a cginc file that contains all you need to easily implement noise into your unity shaders. Who knows how this stuff works, but man, does it work well!
       
      https://pastebin.com/LLCUpJut
       
      Here is an example of what you can create using these noise functions.
       
    • By Nio Martinez
      I'll be buying a new laptop as my workstation for building games, Mostly 3D but not hard core. 
       
      I'm stuck at choosing between these 2 specs below. Does this really matter and if so, can some one tell my how and why it matters. 
      Choice1:
      Intel core i5-8250U (8th gen Kabylake refresh)(6 MB Smart Cache, 1.6 GHz Base with Turbo Boost up to 3.4 GHz) 4 cores 8 threads
      RAM 8 GB DDR4 (2400 MHz)
      GPU 2 GB DDR5 Nvidia MX150 256 bit
      SSD: yes
      Choice2:
      Intel core i7-7500U 2.70GHz Base Processor (4M Cache, up to 3.50 GHz Boost) 2 Cores, 4 Threads
      RAM 4 GB DDR4 (1800 MHz)
      GPU 2 GB DDR5 Nvidia GeForce 940MX 256 bit
      SSD: No
       
    • By Manuel Berger
      Hello fellow devs!
      Once again I started working on an 2D adventure game and right now I'm doing the character-movement/animation. I'm not a big math guy and I was happy about my solution, but soon I realized that it's flawed.
      My player has 5 walking-animations, mirrored for the left side: up, upright, right, downright, down. With the atan2 function I get the angle between player and destination. To get an index from 0 to 4, I divide PI by 5 and see how many times it goes into the player-destination angle.

      In Pseudo-Code:
      angle = atan2(destination.x - player.x, destination.y - player.y) //swapped y and x to get mirrored angle around the y axis
      index = (int) (angle / (PI / 5));
      PlayAnimation(index); //0 = up, 1 = up_right, 2 = right, 3 = down_right, 4 = down

      Besides the fact that when angle is equal to PI it produces an index of 5, this works like a charm. Or at least I thought so at first. When I tested it, I realized that the up and down animation is playing more often than the others, which is pretty logical, since they have double the angle.

      What I'm trying to achieve is something like this, but with equal angles, so that up and down has the same range as all other directions.

      I can't get my head around it. Any suggestions? Is the whole approach doomed?

      Thank you in advance for any input!
       
    • By devbyskc
      Hi Everyone,
      Like most here, I'm a newbie but have been dabbling with game development for a few years. I am currently working full-time overseas and learning the craft in my spare time. It's been a long but highly rewarding adventure. Much of my time has been spent working through tutorials. In all of them, as well as my own attempts at development, I used the audio files supplied by the tutorial author, or obtained from one of the numerous sites online. I am working solo, and will be for a while, so I don't want to get too wrapped up with any one skill set. Regarding audio, the files I've found and used are good for what I was doing at the time. However I would now like to try my hand at customizing the audio more. My game engine of choice is Unity and it has an audio mixer built in that I have experimented with following their tutorials. I have obtained a great book called Game Audio Development with Unity 5.x that I am working through. Half way through the book it introduces using FMOD to supplement the Unity Audio Mixer. Later in the book, the author introduces Reaper (a very popular DAW) as an external program to compose and mix music to be integrated with Unity. I did some research on DAWs and quickly became overwhelmed. Much of what I found was geared toward professional sound engineers and sound designers. I am in no way trying or even thinking about getting to that level. All I want to be able to do is take a music file, and tweak it some to get the sound I want for my game. I've played with Audacity as well, but it didn't seem to fit the bill. So that is why I am looking at a better quality DAW. Since being solo, I am also under a budget contraint. So of all the DAW software out there, I am considering Reaper or Presonus Studio One due to their pricing. My question is, is investing the time to learn about using a DAW to tweak a sound file worth it? Are there any solo developers currently using a DAW as part of their overall workflow? If so, which one? I've also come across Fabric which is a Unity plug-in that enhances the built-in audio mixer. Would that be a better alternative?
      I know this is long, and maybe I haven't communicated well in trying to be brief. But any advice from the gurus/vets would be greatly appreciated. I've leaned so much and had a lot of fun in the process. BTW, I am also a senior citizen (I cut my programming teeth back using punch cards and Structured Basic when it first came out). If anyone needs more clarification of what I am trying to accomplish please let me know.  Thanks in advance for any assistance/advice.
  • Advertisement