Sign in to follow this  
Hahnsolo

OpenGL *SOLVED* Rudimentary C# opengl level design tool picking

Recommended Posts

As the title says, I am working on a very rudimentary opengl rendered "level design tool" using C# and the Tao framework. When you first start up the app, you see an opengl window with some .net forms buttons to the side. The opengl window has a flat square plane (the terrain). When you click one of the object buttons and move your mouse pointer over to the opengl window, there will now be a transparent version of the object you selected following your mouse pointer. When you click the button again, it is no longer tranparent and it is locked to the position you chose. If you click on the object again, it becomes transparent again and follows the mouse pointer until you click again. (It behaves very much like an RTS game would when you are deciding where you want to build a new structure, for instance.) Here is an example scene that I create by placing a few objects: (click for full sized images) This is a transparent house. The mouse pointer doesn't show up in the screenshots, but I think it's easy to see what's happening. Basically, the object is moving with my mouse pointer. Here it is moved to a new location: And now here it is after I've clicked and locked it to a location. (no longer transparent): The problem is this: The object follows the mouse pointer just fine as I move the pointer right and left in the window (mouse x-axis). However, when I move the mouse up and down in window (mouse y-axis) the object moves MUCH slower than the mouse does. Here is examples: This is where the object is located when I move the mouse all the way to the left in the center (on the mouse x-axis). Mouse all the way right. Mouse all the way top. The mouse pointer is in the black part all the way off the terrain here and that is as high as the object will go. Mouse all the way bottom. As you can see, the object stays with the mouse pretty good good when you move it right and left. However, if you go up and down, the objects don't stay with the pointer. If I rotate the camera to where I am directly above the green plain (the terrain) looking down on it, then it stays with the mouse just fine. It's when I rotate it to where I am looking at it at more of an angle that it gets off. The more extreme the angle, the more off it gets. Here is my picking code:
        /// <summary>
        /// Retrieves the object underneath the mouse pointer.
        /// </summary>
        /// <param name="x">x coordinate of mouse pointer</param>
        /// <param name="y">y coordinate of mouse pointer</param>
        /// <returns>An int telling us whether or not we hit an object with the pointer.</returns>
        public GLuint HitTest(int x, int y)
        {
            GLint BUFFER_LENGTH = 1024;
            GLuint[] selBuff = new GLuint[BUFFER_LENGTH];
            GLint hits;
            GLint[] viewport = new GLint[4];
            GL.glGetIntegerv(GL.GL_VIEWPORT, viewport);

            GL.glSelectBuffer(BUFFER_LENGTH, selBuff);
            GL.glMatrixMode(GL.GL_PROJECTION);
            GL.glPushMatrix();
            GL.glRenderMode(GL.GL_SELECT);
            GL.glLoadIdentity();
            GLU.gluPickMatrix(x, y, 1, 1, viewport);
            SetPerspective();
            GL.glMatrixMode(GL.GL_MODELVIEW);
            GlRenderScene();
            hits = GL.glRenderMode(GL.GL_RENDER);
            GL.glMatrixMode(GL.GL_PROJECTION);
            GL.glPopMatrix();
            GL.glMatrixMode(GL.GL_MODELVIEW);
            if (hits > 0)
            {
                if (selBuff[(hits - 1) * 4 + 3] == 2 && hits >= 2)
                    return selBuff[((hits - 1) * 4) - 1];
                else
                    return selBuff[(hits - 1) * 4 + 3];
            }
            else
            {
                return 0;
            }
        }


Gets the 3d world coordinates based on the 2d mouse coordinates:
        /// <summary>
        /// Given the 2D mouse coordinates and a z-depth buffer position, we calculate
        /// the 3D openGl world coordinates.
        /// </summary>
        /// <param name="x">x coordinate</param>
        /// <param name="y">y coordinate</param>
        /// <param name="zDepth">z-depth buffer position (between 0 and 1)</param>
        /// <returns>The 3D openGl world coordinates.</returns>
        unsafe public WorldCoordinates GetWorldFromMouse(int x, int y, double zDepth)
        {
            WorldCoordinates worldCoords = new WorldCoordinates();
            GLdouble[] modelMatrix = new GLdouble[16];
            GLdouble[] projMatrix = new GLdouble[16];
            GLint[] viewPort = new GLint[4];

            GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX, modelMatrix);
            GL.glGetDoublev(GL.GL_PROJECTION_MATRIX, projMatrix);
            GL.glGetIntegerv(GL.GL_VIEWPORT, viewPort);

            GLU.gluUnProject(x, viewPort[1] + viewPort[3] - y, zDepth,
                    modelMatrix, projMatrix, viewPort,
                    out worldCoords.x, out worldCoords.y, out worldCoords.z);
            return worldCoords;
        }


Moves the object to the location of the mouse pointer (on the terrain plane):
        /// <summary>
        /// Moves the given object to the location of the mouse pointer.
        /// </summary>
        /// <param name="e">The mouse event arguments.</param>
        /// <param name="name">The identifier name of the object.</param>
        public void MoveObjectToMouse(MouseEventArgs e, int name)
        {
            WorldCoordinates currW = GetWorldFromMouse(e.X, e.Y, HitZDepth);
            int i = GetObjectIndex(name);
            objectList[i].MoveTo(currW.x, 0, currW.z);
            lastW = currW;
        }


Any idea what I need to do to fix the problem? Thanks, John [Edited by - Hahnsolo on March 20, 2009 11:16:17 AM]

Share this post


Link to post
Share on other sites
I thought this question might make sense in the OpenGL area. Is there some place else that this question would better suited for?

Share this post


Link to post
Share on other sites
Shoot a ray from the camera location along the mouse cursor to some invisible plane(most probably the ground plane). This way you will get the exact 3d cursor position with the added bonus of handling rotations and such.

Hope this helps.

Share this post


Link to post
Share on other sites
Quote:
Original post by CableGuy
Shoot a ray from the camera location along the mouse cursor to some invisible plane(most probably the ground plane). This way you will get the exact 3d cursor position with the added bonus of handling rotations and such.

Hope this helps.


Thanks a ton for your help!

Using your idea and a formula that I found on google books here. (Solution 12.9) I managed to figure out a working solution. The objects now follow the mouse on the terrain perfectly.

So basically, the terrain is a 1000^2 plane on y=0, centered on (0, 0, 0).
In other words, the corners are:

(-500, 0, -500)
(500, 0, -500)
(500, 0, 500)
(-500, 0, 500)

So it's a simple basic square on the y=0 plane.


So, here is the new version of the GetWorldFromMouse method above. This is all I had to modify to make this work. (Thanks again!!)

/// <summary>
/// Given the 2D mouse coordinates and a z-depth buffer position, we calculate
/// the 3D openGl world coordinates.
/// </summary>
/// <param name="x">x coordinate</param>
/// <param name="y">y coordinate</param>
/// <param name="zDepth">z-depth buffer position (between 0 and 1)</param>
/// <returns>The 3D openGl world coordinates.</returns>
unsafe public WorldCoordinates GetWorldFromMouse(int x, int y, double zDepth)
{
WorldCoordinates worldCoords = new WorldCoordinates();
// --------------------------- NEW CODE --------------------------------------
WorldCoordinates nearCoords = new WorldCoordinates();
WorldCoordinates farCoords = new WorldCoordinates();
WorldCoordinates rayDirection = new WorldCoordinates();
// --------------------------- END NEW CODE -----------------------------------

GLdouble[] modelMatrix = new GLdouble[16];
GLdouble[] projMatrix = new GLdouble[16];
GLint[] viewPort = new GLint[4];

GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX, modelMatrix);
GL.glGetDoublev(GL.GL_PROJECTION_MATRIX, projMatrix);
GL.glGetIntegerv(GL.GL_VIEWPORT, viewPort);

// --------------------------- NEW CODE --------------------------------------
// Near-clip plane.
GLU.gluUnProject(x, viewPort[3] - y, 0,
modelMatrix, projMatrix, viewPort,
out nearCoords.x, out nearCoords.y, out nearCoords.z);

// Far-clip plane.
GLU.gluUnProject(x, viewPort[3] - y, 1,
modelMatrix, projMatrix, viewPort,
out farCoords.x, out farCoords.y, out farCoords.z);

// Ray direction.
rayDirection.x = farCoords.x - nearCoords.x;
rayDirection.y = farCoords.y - nearCoords.y;
rayDirection.z = farCoords.z - nearCoords.z;

// Since the terrain plane has a constant y-value, we can
// calculate the distance factor using y.
double t = (0 - nearCoords.y) / rayDirection.y;

// Finally, we calculate the world coordinates using the near-clip
// coordinates, ray direction, and distance factor.
worldCoords.x = nearCoords.x + (t * rayDirection.x);
worldCoords.z = nearCoords.z + (t * rayDirection.z);
worldCoords.y = 0;
// --------------------------- END NEW CODE ---------------------------------

return worldCoords;
}






[Edited by - Hahnsolo on March 20, 2009 1:12:45 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this