views:

51

answers:

2

I'm using OpenTK for a game in C#, and it doesn't come with the project and unproject functions, which convert between world and screen coordinates. Well, it does, but they're deprecated and I couldn't get them to work. I took a shot at implementing them myself based on the opengl spec (see left hand menu, gluProject and gluUnProject, and view in FF, not Chrome). See those formulas, I'm trying to match them. My functions aren't working at all, the points that Project returns are all near the center of my screen, and very close together, even though I'm moving my world point all over the place.

Note that OpenTK's math lib doesn't include functions for multiplying a matrix by a vector, so I just did matrix row by vector dot products for each entry in the resulting vector (that's correct, isn't it?)

    public static Vector3d Project(Vector4d point, Matrix4d projection, Matrix4d modelview, int[] view)
    {
        Matrix4d temp = Matrix4d.Mult(projection, modelview);
        // multiply matrix by vector
        Vector4d v_prime = new Vector4d(
            Vector4d.Dot(temp.Row0, point),
            Vector4d.Dot(temp.Row1, point),
            Vector4d.Dot(temp.Row2, point),
            Vector4d.Dot(temp.Row3, point)
            );

        v_prime = Vector4d.Divide(v_prime, v_prime.W);

        return new Vector3d(
            view[0] + view[2] * (v_prime.X + 1) / 2,
            view[1] + view[3] * (v_prime.Y + 1) / 2,
            (v_prime.Z + 1) / 2
            );
    }

    public static Vector3d UnProject(Vector3d window, Matrix4d modelview, Matrix4d projection, int[] view)
    {
        Matrix4d inv = Matrix4d.Mult(projection, modelview);
        inv.Invert();

        double vx = (2 * (window.X - view[0])) / view[2] - 1;
        double vy = (2 * (window.Y - view[1])) / view[3] - 1;
        double vz = (2 * window.Z) - 1;

        Vector4d vect = new Vector4d(vx, vy, vz, 1);

        // now multiply matrix times vector, which is really row (dot) vector for each value
        // correct, the bottom row of the inv matrix is unused
        return new Vector3d(
            Vector4d.Dot(inv.Row0, vect),
            Vector4d.Dot(inv.Row1, vect),
            Vector4d.Dot(inv.Row2, vect)
            );
    }
+1  A: 

I'm not entirely sure, but at least the order of the matrix multiplications in your Project function might be wrong. Take a look at http://www.opengl.org/wiki/GluProject_and_gluUnProject_code for C code, where the temporary vector (v_prime in your code) is calculated by multiplying with the modelview matrix first and the result is then in turn multiplied with the projection matrix.

Another good source to look for a gluProject/gluUnProject implementation is http://www.mesa3d.org/.

Greg S
A: 

I figured it out - my matrices needed to be transposed. Don't know why, but the OpenTK functions gave my model matrices back differently than the native calls.

Tesserex