0

I'm trying to rewrite my c++ code which implements non-fixed pipeline matrices to C#, using OpenTK. I'm encountering problems, as I don't understand why this code is invalid.

static class ModelView
{
    static private Stack<Matrix4> matrix = new Stack<Matrix4>();
    static public Matrix4 Matrix
    {
        get
        {
            return matrix.Peek();
        }
        set
        {
            matrix.Pop();
            matrix.Push(value);
        }
    }
    static public Matrix4 MatrixNormal
    {
        get
        {
            Matrix4 m = matrix.Peek();
            m.Invert();
            m.Transpose();
            return m;
        }
    }
    static public int Uniform { get; set; }
    static public int UniformNormal { get; set; }

    static private void SetUniforms()
    {
        if (Uniform == -1 || UniformNormal == -1)
            throw new Exception("Projection matrix uniform is uninitialized");
        GL.UniformMatrix4(Uniform, false, ref Matrix);
        GL.UniformMatrix4(UniformNormal, false, ref MatrixNormal);            
    }

    /* stack & matrices manipulating funcs */
}

Visual Studio says that "A property, indexer or dynamic member access may not be passed as an out or ref parameter." on those lines:

GL.UniformMatrix4(Uniform, false, ref Matrix);
GL.UniformMatrix4(UniformNormal, false, ref MatrixNormal);     

Why can't I pass Stack.Peek() as it should be only Object reference.

ddl
  • 329
  • 6
  • 15

3 Answers3

2

This is because your Matrix and MatrixNormal is properties and you can't send it as ref parameter you need to use method not properties for them

it is good practice to use properties when you what to /get/set field values or it is some lightweight function but it your case it is better to use a methods here. try this: (put method return values as fields)

public class Matrix
{
    private Stack<Matrix4> matrix = new Stack<Matrix4>();
    Matrix4 peek = null; 
    public Matrix4 Peek()
    {
        peek = matrix.Peek();
        return peek;

    }

    public void Peek(Matrix4 value)
    {
        matrix.Pop();
        matrix.Push(value);

    }
    private Matrix normalMatrix = null;
    public Matrix4 MatrixNormal()
    {
        Matrix4 m = matrix.Peek();
            m.Invert();
            m.Transpose();
            normalMatrix = m;
            return normalMatrix;
    }
    public int Uniform { get; set; }
    public int UniformNormal { get; set; }

     private void SetUniforms()


 {
        if (Uniform == -1 || UniformNormal == -1)
            throw new Exception("Projection matrix uniform is uninitialized");
        Peek();
        MatrixNormal();
        GL.UniformMatrix4(Uniform, false, ref peek);

        GL.UniformMatrix4(UniformNormal, false, ref normalMatrix);            
    }
Alexander V.
  • 1,518
  • 14
  • 14
  • But error is in `Matrix` and `MatrixNormal`. So i have to crate own getter and setter functions and delete accessors? – ddl Mar 17 '14 at 00:36
  • You cleared this for me. I just needed to know C# way of doing that kind of stuff. – ddl Mar 17 '14 at 13:42
  • Note that Matrix4 is not a class, so this line won't compile: `Matrix4 peek = null;`. Instead, use `Matrix4 peek = Matrix4.Identity;` – The Fiddler Mar 17 '14 at 16:17
1

Parameters passed as ref can be both read and written by the called method. A Property is like an object with a getter and a setter (well, it doesn't need to have both), and one can not assume that the getter and the setter has the same backing field. So it's therefore not possible/allowed to pass a Property as a ref parameter.

Vegard Innerdal
  • 349
  • 2
  • 10
  • So correct and semantic way of doing this is to remove accessors and crate getting and setting functions like in C++? – ddl Mar 17 '14 at 00:44
  • yes methods (functions) is better in your case but you still need copy method results into variables and only then pass it as ref parameters – Alexander V. Mar 17 '14 at 00:49
  • Well I don't think so. I would simply create a new reference to `Matrix` and `MatrixNormal` before I used them as ref parameters. – Vegard Innerdal Mar 17 '14 at 00:50
  • Matrix4 m = Matrix; Matrix4 mn = MatrixNormal; GL.UniformMatrix4(Uniform, false, ref m); GL.UniformMatrix4(UniformNormal, false, ref mn); This code works, but isn't it useless copying? – ddl Mar 17 '14 at 00:54
  • Yes it is unnecessary copying. From what I see of your code, the `matrix` stack can never contain more than one item. Is this correct? If you only need one instance of it, why not just make a private field as backing for the properties? – Vegard Innerdal Mar 17 '14 at 01:09
  • yes good point, I made some changes to my post, also you could make them properties as well but guidelines suggest if property could take much time use methods – Alexander V. Mar 17 '14 at 01:12
  • Properties are translated/compiled to methods (`get_` and `set_`). If performance is an issue, perhaps you should consider using public fields instead, but I doubt that you find many guidelines suggesting that fields is the way to go. – Vegard Innerdal Mar 17 '14 at 01:24
1

The problem is that using a ref parameter, you have to supply what would be considered a pointer in C++. A property won't do, because that is nothing more than a method (or two methods) with some syntactic sugar. A property will give you value, not a memory address.

If you don't want to (or can't) pass your matrix as a ref parameter, you have to use a different overload of GL.UniformMatrix4, for example this:

GL.UniformMatrix4(int location, int count, bool transpose, float[] value)

You just have to convert your Matrix4 object to an array of 16 floats.

vesan
  • 3,289
  • 22
  • 35
  • 1
    You don't have to convert from `Matrix4` to `float`, you can pass `ref Matrix4.Row0.X` and OpenTK will pin and pass the whole matrix to the driver. It's a little trick to help you write faster code. – The Fiddler Mar 17 '14 at 16:16
  • @TheFiddler That should be in OpenTK documentation or something. – Syaiful Nizam Yahya Mar 18 '15 at 15:43