tekord's picture

Can't ref to a property that returns a Matrix4

Hi! First of all, sorry for my english.

Got a problem with Matrix4. It is a value-type (struct), so there is no way to refer to it except in method argument. For example:

void GetFrustum(out Matrix4 matrix) { ... }

Necessary condition that 'out Matrix4' is a variable. But i coding a render system independed camera class which just creates a matrices (view, projection, ect.) which will be send to render system.

Camera class consist following code:

public Matrix4 ViewMatrix {
	get {
		return mViewMatrix;
	}
}

And somewhere in program i trying to write:

GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref activeCamera.ViewMatrix);

And i failed. It is obvilious, cause trying to refer a property. Possible solution is open member field Matrix4 m_MatrixView instead of ViewMatrix property. But then we'll get violation of encapsulation cause everybody will can change this matrix. It is unacceptable.

Another solution is translate 'struct Matrix4' to 'class Matrix4' (value-type to reference-type). Matrix4 is very massive structure, we have big consumption of resources when passing parameter of Matrix4 type to methods without 'ref' modifier. In my opinion does not make sense to do Matrix4 as value-type. Do you agree?

If Matrix4 would be a class insted of structure when I would be get elegant solution of my problem. Create a class REnderSystem, put references to ViewMatrix, ProjectionMatrix, ect. into this class and pass ViewMatrix from Camera (even if it would be a property) to RenderSystem.

Do you have any ideas about this? :)


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

A Matrix4 class (rather than struct) would destroy performance due to GC pressure.

The only solution is to assign the matrix to a temporary variable and pass that instead. It sucks, but I don't think this is possible to work around in C#.

martinsm's picture

In my code I usually in this case put ViewMatrix as public member of class not as property. Then you can use it as ref/out argument.

tekord's picture
Quote:

A Matrix4 class (rather than struct) would destroy performance due to GC pressure.

I see. Forgot about GC :(.

Quote:

In my code I usually in this case put ViewMatrix as public member of class not as property. Then you can use it as ref/out argument.

I think to do your's way. But I don't like this solution anyway.

Thank you for answares! I think I got enough information.

tekord's picture

If somebody knows another solution please write. It is very interesting.

avc81's picture

Well, it is not a solution but in VB.Net i don't have this problem ;)

the Fiddler's picture

Indeed, the VB.Net creates a temporary variable and passes that instead of the property, side-stepping the issue. (In C# you have to do this explicitly).

avc81's picture

Do you think that this behaviour could become a performance issue ?

the Fiddler's picture

Possible but unlikely. This is something that could be tweaked at the very end, if the project doesn't meet its performance requirements, but I wouldn't worry about it otherwise.

avc81's picture

thanx for the advice

tksuoran's picture

For any data that I upload to uniforms, I created the following class:

    public class Floats
    {
        public float[]  Value;
        public int      Elements;
        public int      Dimension;
        public float    X { get { return Value[0]; } set { Value[0] = value; } }
        public float    Y { get { return Value[1]; } set { Value[1] = value; } }
        public float    Z { get { return Value[2]; } set { Value[2] = value; } }
        public float    W { get { return Value[3]; } set { Value[3] = value; } }
 
        public float this[int index]
        {
            get { return Value[index]; }
            set { Value[index] = value; }
        }
 
        public Floats(int elements, int dimension)
        {
            Value = new float[dimension * elements];
            Elements = elements;
            Dimension = dimension;
        }
        public Floats(float x)
        {
            Value = new float[1 * 1];
            Elements = 1;
            Dimension = 1;
            X = x;
        }
        public Floats(float x, float y)
        {
            Value = new float[1 * 2];
            Elements = 2;
            Dimension = 1;
            X = x;
            Y = y;
        }
        public Floats(float x, float y, float z)
        {
            Value = new float[1 * 3];
            Elements = 3;
            Dimension = 1;
            X = x;
            Y = y;
            Z = z;
        }
        public Floats(float x, float y, float z, float w)
        {
            Value = new float[1 * 4];
            Elements = 4;
            Dimension = 1;
            X = x;
            Y = y;
            Z = z;
            W = w;
        }
 
        public void Set(float x)
        {
            X = x;
        }
        public void Set(float x, float y)
        {
            X = x;
            Y = y;
        }
        public void Set(float x, float y, float z)
        {
            X = x;
            Y = y;
            Z = z;
        }
        public void Set(float x, float y, float z, float w)
        {
            X = x;
            Y = y;
            Z = z;
            W = w;
        }
        public void Set(Vector2 v)
        {
            X = v.X;
            Y = v.Y;
        }
        public void Set(Vector3 v)
        {
            X = v.X;
            Y = v.Y;
            Z = v.Z;
        }
        public void Set(Vector4 v)
        {
            X = v.X;
            Y = v.Y;
            Z = v.Z;
            W = v.W;
        }
        public void Set(int index, Vector2 v)
        {
            Value[index * Elements + 0] = v.X;
            Value[index * Elements + 1] = v.Y;
        }
        public void Set(int index, Vector3 v)
        {
            Value[index * Elements + 0] = v.X;
            Value[index * Elements + 1] = v.Y;
            Value[index * Elements + 2] = v.Z;
        }
        public void Set(int index, Vector4 v)
        {
            Value[index * Elements + 0] = v.X;
            Value[index * Elements + 1] = v.Y;
            Value[index * Elements + 2] = v.Z;
            Value[index * Elements + 3] = v.X;
        }
        public void Set(Matrix4 matrix)
        {
#if true
            Value[ 0] = matrix._00;
            Value[ 1] = matrix._10;
            Value[ 2] = matrix._20;
            Value[ 3] = matrix._30;
 
            Value[ 4] = matrix._01;
            Value[ 5] = matrix._11;
            Value[ 6] = matrix._21;
            Value[ 7] = matrix._31;
 
            Value[ 8] = matrix._02;
            Value[ 9] = matrix._12;
            Value[10] = matrix._22;
            Value[11] = matrix._32;
 
            Value[12] = matrix._03;
            Value[13] = matrix._13;
            Value[14] = matrix._23;
            Value[15] = matrix._33;
#else
            Value[ 0] = matrix._00;
            Value[ 1] = matrix._01;
            Value[ 2] = matrix._02;
            Value[ 3] = matrix._03;
 
            Value[ 4] = matrix._10;
            Value[ 5] = matrix._11;
            Value[ 6] = matrix._12;
            Value[ 7] = matrix._13;
 
            Value[ 8] = matrix._20;
            Value[ 9] = matrix._21;
            Value[10] = matrix._22;
            Value[11] = matrix._23;
 
            Value[12] = matrix._30;
            Value[13] = matrix._31;
            Value[14] = matrix._32;
            Value[15] = matrix._33;
#endif
        }
        public void Set(int index, Matrix4 matrix)
        {
            Value[index * Elements +  0] = matrix._00;
            Value[index * Elements +  1] = matrix._10;
            Value[index * Elements +  2] = matrix._20;
            Value[index * Elements +  3] = matrix._30;
 
            Value[index * Elements +  4] = matrix._01;
            Value[index * Elements +  5] = matrix._11;
            Value[index * Elements +  6] = matrix._21;
            Value[index * Elements +  7] = matrix._31;
 
            Value[index * Elements +  8] = matrix._02;
            Value[index * Elements +  9] = matrix._12;
            Value[index * Elements + 10] = matrix._22;
            Value[index * Elements + 11] = matrix._32;
 
            Value[index * Elements + 12] = matrix._03;
            Value[index * Elements + 13] = matrix._13;
            Value[index * Elements + 14] = matrix._23;
            Value[index * Elements + 15] = matrix._33;
        }
        public void Set(int index, float x)
        {
            Value[index * Elements + 0] = x;
        }
        public void Set(int index, float x, float y)
        {
            Value[index * Elements + 0] = x;
            Value[index * Elements + 1] = y;
        }
        public void Set(int index, float x, float y, float z)
        {
            Value[index * Elements + 0] = x;
            Value[index * Elements + 1] = y;
            Value[index * Elements + 2] = z;
        }
        public void Set(int index, float x, float y, float z, float w)
        {
            Value[index * Elements + 0] = x;
            Value[index * Elements + 1] = y;
            Value[index * Elements + 2] = z;
            Value[index * Elements + 3] = w;
        }
    }

I also created a helper class with the following function:

        public static void Uniform(int location, ref Floats value)
        {
            unsafe
            {
                fixed (float* ptr = &value.Value[0])
                {
                    if(value.Elements == 1)
                    {
                        GL.Uniform1(location, value.Dimension, ptr);
                    }
                    else if(value.Elements == 2)
                    {
                        GL.Uniform2(location, value.Dimension, ptr);
                    }
                    else if(value.Elements == 3)
                    {
                        GL.Uniform3(location, value.Dimension, ptr);
                    }
                    else if(value.Elements == 4)
                    {
                        GL.Uniform4(location, value.Dimension, ptr);
                    }
                    else if(value.Elements == 16)
                    {
                        GL.UniformMatrix4(location, value.Dimension, false, ptr);
                    }
                }
            }
        }

Since Floats is a class, it can be passed around and referenced. Meanwhile you can still do Matrix4 computations efficiently in your Camera class. Once you are done with your matrix computations, you can update your Floats instance with the new Matrix4 values. The helper Uniform() function makes it easy to upload the values to GL.