XNA Creators Club Online
Page 1 of 2 (30 items) 1 2 Next >
Sort Posts: Previous Next

Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

Last post 12/1/2009 9:06 PM by Dark Flow Studios. 29 replies.
  • 11/12/2009 3:04 PM

    Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Hello there,

    I have currently implemented the following as my means of accessing x,y,z in a vector3 as an index:

            public static float GetVector3Index(ref Vector3 vector, int i)  
            {  
                switch (i)  
                {  
                    case 0: { return vector.X; }  
                    case 1: { return vector.Y; }  
                    case 2: { return vector.Z; }  
     
                    default:  
                        {  
                            throw new ArgumentOutOfRangeException(  
                                "Vector component index out of range" 
                            );  
                        }  
                }  
            } 

    The problem with this method is the ref parameter will not allow me to use properties or indexers (such as get/set).  I also cannot find a way to overload the [] operator to allow accessing x,y,z through [i].

    Can you folks please tell me how you would suggest tackling this problem and implementing this idea?

    Thanks
  • 11/12/2009 3:36 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Forgive me but what are you trying to do? Access variable x,y or z inside the vector? Why can't you access it with something like vector.X from outside?
  • 11/12/2009 3:51 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    David Amador:
    Forgive me but what are you trying to do? Access variable x,y or z inside the vector? Why can't you access it with something like vector.X from outside?


    Because there are situations where it can be handy to index a Vector3 or Vector4 using C-style array indexing.

    As for the original poster.  I don't necessarily have an answer or even a suggestion for you, but I certainly feel your pain.
  • 11/12/2009 4:00 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    I'm pretty sure what you are asking cannot be done. You could make an extension method out of the first function you provided, but it will lose the ref which will likely have performance implications.

    Any fancy syntactical sugar you try and wrapper around a struct is likely not worth the effort.
  • 11/12/2009 5:32 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Answer
    Reply Quote
    Rhys Perkins:
    The problem with this method is the ref parameter will not allow me to use properties or indexers (such as get/set)
    So drop the ref keyword and it'll work just fine. There's no need to have a ref there.

    If I had to implement this I would probably use this extension method:

    public static class Vector3Extensions 
       public static float At(this Vector3 v, int index) 
       { 
          if (index == 0) 
             return v.X; 
          if (index == 1) 
             return v.Y; 
          if (index == 2) 
             return v.Z; 
           
          throw new ArgumentOutOfRangeException("index"); 
       } 

    Then I could get the fields in a shorter syntax:

    Vector3 vector = new Vector3(1, 2, 3); 
    float y = vector.At(1); 

  • 11/12/2009 9:34 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Awesome stuff Nick, I've changed mine to the following just because I think switch case is more legible.

            public static float At(this Vector3 v, int index)  
            {  
                switch (index)  
                {  
                    case 0: { return v.X; }  
                    case 1: { return v.Y; }  
                    case 2: { return v.Z; }  
     
                    default:  
                        {  
                            throw new ArgumentOutOfRangeException(  
                              "Vector component index out of range" 
                            );  
                        }  
                }  
            }  

    Incidently, I have one for Matrix too now.  If anyone would like to see it, please shout!

    Thanks for your answers everyone.
  • 11/13/2009 7:06 AM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Rhys Perkins:
    I have one for Matrix too now.  If anyone would like to see it, please shout!


    *shout*

    Try to beat this:

    /// <summary>Provides helper methods for working with matrices</summary> 
    internal static class MatrixHelper { 
     
      /// <summary>Retrieves an element of the matrix by its column and row index</summary> 
      /// <param name="matrix">Matrix of which to retrieve an element</param> 
      /// <param name="row">Index of the row from which to retrieve the element</param> 
      /// <param name="col">Index of the column to retrieve</param> 
      /// <returns>The element at the given row and column</returns> 
      public static float Get(ref Matrix matrix, int row, int col) { 
        switch(row << 4 | col) { 
          case 0x00: { return matrix.M11; } 
          case 0x01: { return matrix.M12; } 
          case 0x02: { return matrix.M13; } 
          case 0x03: { return matrix.M14; } 
     
          case 0x10: { return matrix.M21; } 
          case 0x11: { return matrix.M22; } 
          case 0x12: { return matrix.M23; } 
          case 0x13: { return matrix.M24; } 
     
          case 0x20: { return matrix.M31; } 
          case 0x21: { return matrix.M32; } 
          case 0x22: { return matrix.M33; } 
          case 0x23: { return matrix.M34; } 
     
          case 0x30: { return matrix.M41; } 
          case 0x31: { return matrix.M42; } 
          case 0x32: { return matrix.M43; } 
          case 0x33: { return matrix.M44; } 
     
          default: { 
            throw new ArgumentOutOfRangeException( 
              "Matrix row and/or column index out of range" 
            ); 
          } 
        } 
      } 
     

    :D

    I had this as a feature request 3 years ago when XNA 1.0 was still in beta and the devs were asking for suggestions and feedback: Vector and Matrix should provide overloaded indexer. But this is the first time it comes up in the forum, so I guess it really isn't used that often.
    Check out my website and blog for some interesting articles and useful utility classes!
    Nuclex Framework: threaded particles, skinnable GUI, vector fonts, texture atlasses and lots more.
    WiX XNA Installer: Professional-looking MSI installer template for XNA games.
  • 11/13/2009 11:27 AM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Here you go dude:

            public static float Index(this Matrix matrix, int row, int col)  
            {  
                // << = bitwise shift left   
                // << 4 in BINARY shifts integer n from [n * 2^0] to [n * 2^4] which is [n * 16]   
                switch ((row << 4) + col)  
                {  
                    case 0x00: { return matrix.M11; }   // Hexadecimal values must start with "0x"  
                    case 0x01: { return matrix.M12; }  
                    case 0x02: { return matrix.M13; }  
                    case 0x03: { return matrix.M14; }  
     
                    case 0x10: { return matrix.M21; }   // 16  
                    case 0x11: { return matrix.M22; }   // 17  
                    case 0x12: { return matrix.M23; }   // 18  
                    case 0x13: { return matrix.M24; }   // 19  
     
                    case 0x20: { return matrix.M31; }   // 32  
                    case 0x21: { return matrix.M32; }  
                    case 0x22: { return matrix.M33; }  
                    case 0x23: { return matrix.M34; }  
     
                    case 0x30: { return matrix.M41; }   // 48  
                    case 0x31: { return matrix.M42; }  
                    case 0x32: { return matrix.M43; }  
                    case 0x33: { return matrix.M44; }  
     
                    default:  
                        {  
                            throw new ArgumentOutOfRangeException(  
                                "Matrix row and/or column index out of range" 
                            );  
                        }  
                }  
            } 

    Reasons why it is better:

    - No ref (thanks to Nick for pointing that out)
    - Switch statement is more efficient (thanks to switfcoder from GameDev.net for our conversation a while back on bitwise and binary)
      At the lowest optimisation level, GCC transforms row * 16 + col into a shift + add, and at -O3 row * 16 + col is rendered in fewer instructions than row << 4 | col.
  • 11/13/2009 12:21 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    David Amador:
    Forgive me but what are you trying to do? Access variable x,y or z inside the vector? Why can't you access it with something like vector.X from outside?
    All my C++ Vector classes had unions where you could access the components by X,Y,Z(,W) or 0,1,2(,3). A vector IS an array after all. It helps to be able to access it like one in loops.
    return;
  • 11/13/2009 2:50 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Rhys Perkins:
    No ref (thanks to Nick for pointing that out)


    Not using ref doesn't necessarily make it better. Ref is your friend when passing around large structs, which a Matrix qualifies as. You probably lose as much performance copying in the matrix data as you gain from the shifting logic.

    A likely scenario for the use of this method if in a loop, the very place where ref would be most beneficial.

     
  • 11/13/2009 3:44 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Not using ref means that I will have to make a local copy of any get/set indexer.  Does that not balance things out?
  • 11/13/2009 3:48 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    In the case of using properties it does, because the copy happens anyway when you access the struct via a property.

    The way the built-in structs (Vector2, Vector3, etc) have versions of that use ref and out and versions that don't is a good indication that no solution will be best in all cases when it comes to dealing with value types. For most cases and most games it won't make any difference.
  • 11/13/2009 3:49 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    To throw something unsafe into the crazyness (note: this has not been tested and will most likely format your HDD, RRoD your 360 and do other bad stuff):
    public unsafe static float At(this Vector3 v, int index)  
    {  
        if (index < 0 || index > 2)  
        {  
            throw new ArgumentOutOfRangeException("Vector component index out of range");  
        }  
     
        fixed(float* p = &v.X)  
        {  
            return p[index];  
        }  
    }     
     
    We are boki. The rest is known.

    The not so known part of the rest: It is Björn or Bjoern, but never Bjorn.

    Twitter ~ Bnoerj ~ SharpSteer ~ SgtConker.com
  • 11/13/2009 5:03 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Rhys Perkins:
               switch ((row << 4) + col)  


    Why multiply the row by 16? There are only four columns per row, so this just needs to multiply by 4 (or shift by 2). It's a minor detail, but making the switch values consecutive without any gaps increases the chance that this will end up translating to an efficient jump table rather than a slower series of conditional branches.

    Also, the framework designer in me is bothered by the potential overflow where you could pass out of range column values that would wrap around to the next row without causing an error. I'd be tempted to explicitly validate the indices up front to avoid that.
    XNA Framework Developer - blog - homepage
  • 11/13/2009 5:06 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Benchmark with 100,000,000 x 16 calls to the method
    Mine: 12.12 seconds (11.72 ngen'd)
    Yours: 15.89 seconds (16.56 ngen'd)
    Whatever GCC does, addition or bitwise or doesn't seem to make a difference to the .NET jitter or ngen.

    Given the marginal difference, I'd use the extension method any day, but I'm still targeting .NET 2.0 because of the size of the .NET 3.5 redistributable.
    Check out my website and blog for some interesting articles and useful utility classes!
    Nuclex Framework: threaded particles, skinnable GUI, vector fonts, texture atlasses and lots more.
    WiX XNA Installer: Professional-looking MSI installer template for XNA games.
  • 11/13/2009 5:33 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Shawn Hargreaves:

    Why multiply the row by 16? There are only four columns per row, so this just needs to multiply by 4 (or shift by 2). It's a minor detail, but making the switch values consecutive without any gaps increases the chance that this will end up translating to an efficient jump table rather than a slower series of conditional branches.

    Also, the framework designer in me is bothered by the potential overflow where you could pass out of range column values that would wrap around to the next row without causing an error. I'd be tempted to explicitly validate the indices up front to avoid that.


    Good point, I've updated the method to the following:

            public static float Index(this Matrix matrix, int row, int col)  
            {  
                // Clamp to the range [0,3]  
                row = (row > 3) ? 3 : row;  
                row = (row < 0) ? 0 : row;  
     
                col = (row > 3) ? 3 : col;  
                col = (row < 0) ? 0 : col;  
     
                // << = bitwise shift left   
                // << 4 in BINARY shifts integer n from [n * 2^0] to [n * 2^4] which is [n * 16]   
                switch ((row << 2) + col)  
                {  
                    case 0x00: { return matrix.M11; }   // Hexadecimal values must start with "0x"  
                    case 0x01: { return matrix.M12; }  
                    case 0x02: { return matrix.M13; }  
                    case 0x03: { return matrix.M14; }  
     
                    case 0x04: { return matrix.M21; }     
                    case 0x05: { return matrix.M22; }     
                    case 0x06: { return matrix.M23; }    
                    case 0x07: { return matrix.M24; }   
     
                    case 0x08: { return matrix.M31; }    
                    case 0x09: { return matrix.M32; }  
                    case 0x0A: { return matrix.M33; }  
                    case 0x0B: { return matrix.M34; }  
     
                    case 0x0C: { return matrix.M41; }   
                    case 0x0D: { return matrix.M42; }  
                    case 0x0E: { return matrix.M43; }  
                    case 0x0F: { return matrix.M44; }  
     
                    defaultreturn 0.0f;  
                }  
            } 

    When you sat you would be tempted to validate the indices up front, would the method of clamping the row/col values be ok?  Or did you have something else in mind?

    Cygon4:

    Benchmark with 100,000,000 x 16 calls to the method


    What benchmarking tool would you recommend for this type of procedure?

    Thanks again.
  • 11/13/2009 5:35 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Shawn Hargreaves:
    Why multiply the row by 16? There are only four columns per row, so this just needs to multiply by 4 (or shift by 2). It's a minor detail, but making the switch values consecutive without any gaps increases the chance that this will end up translating to an efficient jump table rather than a slower series of conditional branches.


    I used 16 so the cases say 0x(row)(column) - the first hex digit is the matrix row, the second one the matrix column.

    Shawn Hargreaves:
    Also, the framework designer in me is bothered by the potential overflow where you could pass out of range column values that would wrap around to the next row without causing an error. I'd be tempted to explicitly validate the indices up front to avoid that.


    True. It will catch off-by-one errors, but may not consistently fail in other cases.

    So with your changes, we get this:

    /// <summary>Retrieves an element of the matrix by its column and row index</summary>  
    /// <param name="matrix">Matrix of which to retrieve an element</param>  
    /// <param name="row">Index of the row from which to retrieve the element</param>  
    /// <param name="col">Index of the column to retrieve</param>  
    /// <returns>The element at the given row and column</returns>  
    public static float Get(ref Matrix matrix, int row, int col) { 
      if((col < 0) || (col > 3)) { 
        throw new ArgumentOutOfRangeException("col""Matrix column index out of range"); 
      } 
     
      switch(row << 2 | col) { 
        case 0: { return matrix.M11; } 
        case 1: { return matrix.M12; } 
        case 2: { return matrix.M13; } 
        case 3: { return matrix.M14; } 
     
        case 4: { return matrix.M21; } 
        case 5: { return matrix.M22; } 
        case 6: { return matrix.M23; } 
        case 7: { return matrix.M24; } 
     
        case 8: { return matrix.M31; } 
        case 9: { return matrix.M32; } 
        case 10: { return matrix.M33; } 
        case 11: { return matrix.M34; } 
     
        case 12: { return matrix.M41; } 
        case 13: { return matrix.M42; } 
        case 14: { return matrix.M43; } 
        case 15: { return matrix.M44; } 
     
        default: { 
          throw new ArgumentOutOfRangeException("row""Matrix row index out of range"); 
        } 
      } 

    Let the games commence! :D
    Cygon: 12.12 seconds (ngen'd 11.72 seconds)
    Rhys: 15.89 seconds (ngen'd 16.56 seconds)
    Shawn: 11.47 seconds (ngen'd 11.11 seconds)
    Bjoern: 10.48 seconds (ngen'd 11.45 seconds)
    Bjoern2: 6.2 seconds (ngen'd 5.98 seconds)
    The first two implementations should probably disqualified because they don't detect all invalid inputs.
    Bjoern is Bjoern's unsafe Vector3 code converted to the Matrix class.
    Bjoern2 is the same but passing the matrix as a ref parameter.

    There indeed seems to be some optimization .NET can perform in contiguous values for switch cases. Interesting!

    Also shows that unsafe code certainly has its strengths. It's both shorter and faster. I don't know whether XNA games already require a full trust environment to run, but if so, there would be no drawbacks. If I'm not mistaken, it should even work on the XBox 360.




    Check out my website and blog for some interesting articles and useful utility classes!
    Nuclex Framework: threaded particles, skinnable GUI, vector fonts, texture atlasses and lots more.
    WiX XNA Installer: Professional-looking MSI installer template for XNA games.
  • 11/13/2009 5:41 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Rhys Perkins:
    Good point, I've updated the method to the following:

                // Clamp to the range [0,3]  
                row = (row > 3) ? 3 : row;  
                row = (row < 0) ? 0 : row; 


    Hm, I'd prefer to have it fail upon encountering invalid indices. Also, that's 4 additional branches.

    Rhys Perkins:
    Cygon4:

    Benchmark with 100,000,000 x 16 calls to the method


    What benchmarking tool would you recommend for this type of procedure?


    I just wrote a short ad-hoc benchmark and made sure the compiler could not optimize anything away by sending the matrix through an external library call each round and summing up the matrix' fields:

    static void Main(string[] args) { 
      float total = 0.0f; 
      Matrix m = Matrix.Identity; 
     
      Stopwatch w = Stopwatch.StartNew(); 
       
      for(int index = 0; index < 100000000; ++index) { 
        // Compiler can make no assumptions as to what changed within the matrix 
        // and thus cannot eliminate the loop or optimize code away. 
        Matrix.Negate(ref m, out m); 
        total += sum(ref m); 
      } 
       
      w.Stop(); 
       
      Console.WriteLine("Comletely useless value: " + total); 
      Console.WriteLine("Time: " + w.Elapsed.ToString()); 
     
    private static float sum(ref Matrix matrix) { 
      float total = 0.0f; 
       
      for(int col = 0; col < 3; ++col) { 
        for(int row = 0; row < 3; ++row) { 
          total += MatrixHelper.At(ref matrix, row, col); 
        } 
      } 
       
      return total; 
    Check out my website and blog for some interesting articles and useful utility classes!
    Nuclex Framework: threaded particles, skinnable GUI, vector fonts, texture atlasses and lots more.
    WiX XNA Installer: Professional-looking MSI installer template for XNA games.
  • 11/13/2009 6:04 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Cygon4:
    I don't know whether XNA games already require a full trust environment to run, but if so, there would be no drawbacks.
    I don't believe you need full trust to use unsafe code in .NET.

    Cygon4:
    If I'm not mistaken, it should even work on the XBox 360.
    Unsafe code works just fine on 360. :)
  • 11/13/2009 6:10 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Nick Gravelyn:
    Cygon4:
    I don't know whether XNA games already require a full trust environment to run, but if so, there would be no drawbacks.
    I don't believe you need full trust to use unsafe code in .NET.


    Unsafe code is not verifiable by the common language runtime.
    We are boki. The rest is known.

    The not so known part of the rest: It is Björn or Bjoern, but never Bjorn.

    Twitter ~ Bnoerj ~ SharpSteer ~ SgtConker.com
  • 11/13/2009 6:12 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Bjoern Graf:
    Nick Gravelyn:
    Cygon4:
    I don't know whether XNA games already require a full trust environment to run, but if so, there would be no drawbacks.
    I don't believe you need full trust to use unsafe code in .NET.


    Unsafe code is not verifiable by the common language runtime.
    I stand corrected. :) Though this is probably the better link: http://msdn.microsoft.com/en-us/library/t2yzs44b.aspx:

    In the common language runtime (CLR), unsafe code is referred to as unverifiable code. Unsafe code in C# is not necessarily dangerous; it is just code whose safety cannot be verified by the CLR. The CLR will therefore only execute unsafe code if it is in a fully trusted assembly. If you use unsafe code, it is your responsibility to ensure that your code does not introduce security risks or pointer errors.
  • 11/13/2009 6:29 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Nick Gravelyn:
    Bjoern Graf:
    Nick Gravelyn:
    Cygon4:
    I don't know whether XNA games already require a full trust environment to run, but if so, there would be no drawbacks.
    I don't believe you need full trust to use unsafe code in .NET.


    Unsafe code is not verifiable by the common language runtime.
    I stand corrected. :) Though this is probably the better link: http://msdn.microsoft.com/en-us/library/t2yzs44b.aspx:

    In the common language runtime (CLR), unsafe code is referred to as unverifiable code. Unsafe code in C# is not necessarily dangerous; it is just code whose safety cannot be verified by the CLR. The CLR will therefore only execute unsafe code if it is in a fully trusted assembly. If you use unsafe code, it is your responsibility to ensure that your code does not introduce security risks or pointer errors.


    Indeed. And to add something more: If my memory is not mistaken (although it is likely to fail here) the XNA FX already requires full trust anyway.
    We are boki. The rest is known.

    The not so known part of the rest: It is Björn or Bjoern, but never Bjorn.

    Twitter ~ Bnoerj ~ SharpSteer ~ SgtConker.com
  • 11/13/2009 6:35 PM In reply to
    • (74)
    • premium membership Team XNA
    • Posts 21

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Cygon4:


    /// <summary>Retrieves an element of the matrix by its column and row index</summary>  
    /// <param name="matrix">Matrix of which to retrieve an element</param>  
    /// <param name="row">Index of the row from which to retrieve the element</param>  
    /// <param name="col">Index of the column to retrieve</param>  
    /// <returns>The element at the given row and column</returns>  
    public static float Get(ref Matrix matrix, int row, int col) { 
      if((col < 0) || (col > 3)) { 
        throw new ArgumentOutOfRangeException("col""Matrix column index out of range"); 
      } 
     
      switch(row << 2 | col) { 
        case 0: { return matrix.M11; } 
        case 1: { return matrix.M12; } 



    You should validate the row input as well.  There's another overflow issue here where row could be 0x10000000 and the fn would incorrectly return Matrix.M1x
    --
    Danny
    .XNA Team.

    disclaimer: Information provided is 'as is' and conveys no warranties or guarantees.
  • 11/13/2009 7:20 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Cygon4:
      if((col < 0) || (col > 3)) {      throw new ArgumentOutOfRangeException("col""Matrix column index out of range");    } 


    Ok, let the microoptimization games commence :-)  I wouldn't normally write such code because it's much less obvious and therefore more likely to contain subtle errors, but this version does the same thing a little faster (just two bitwise operations and one conditional branch) and also avoiding the potential row overflow mentioned earlier:

        if (((row | col) & ~3) != 0) 
            throw new ArgumentOutOfRangeException(((row & ~3) != 0) ? "row" : "col"); 
    XNA Framework Developer - blog - homepage
  • 12/1/2009 1:26 PM In reply to

    Re: Accessing Vector3 elements as an index. Ideas and thoughts on the following code?

    Can something like this extend the Vector3 class, or would I have to code one from the ground up?

        internal class Vector  
        {  
            public float X;  
            public float this[int index]  
            {  
                get 
                {  
                    switch (index)  
                    {  
                        case 0:   
                            return X;  
                        default:   
                            throw new IndexOutOfRangeException();  
                    }  
                }  
                set 
                {  
                    switch (index)  
                    {  
                        case 0:   
                            X = value;   
                            return;  
                        defaultthrow new IndexOutOfRangeException();  
                    }  
                }  
            }  
        } 

    Vector v = new Vector();

    X can now be returned with v[0].

    What do you guys think?
Page 1 of 2 (30 items) 1 2 Next > Previous Next