Question

I am using SDL with OpenGL and up until now I have done everything in 2d (Using glOrtho())

Now I want to try drawing things in 3D but of course since I have now thrown a 3rd dimension into the mixture, things are getting more complicated.

What I want to do is, take the screen co-ordinates of the cursor, translate them to (?)world co-ordinates (Co-ordinates I can use in OpenGL since I now have perspective etc.), and draw a quad at that position (The mouse cursor being at the center of the quad)

I have read a few tutorials on gluUnProject() and I sort of understand what I am supposed to do, but since I am learning by myself it is very easy to get confused and not properly understand what I am doing.

As such, I have mainly copied from examples I have found.

Below is my code (I took out error checking etc in an attempt to cut it down a bit)

As you can see, I have mouseX, mouseY and mouseZ variables, of which mouseX and mouseY get their values from SDL.

I tried to use glReadPixel() to get mouseZ but I am not sure if I am doing it right.

The problem is that when I click, the quad is not drawn in the correct position (I guess partly due to the fact that I don't know how to get mouseZ properly, so I just replaced it in gluUnProject() with 1.0?)

I am using the new co-ordinates (wx, wy, wz) in the glTranslatef() function but once again because I don't know how to get mouseZ properly I am guessing this is the reason it doesn't work properly. If I replace wz with -499 it seems to work ok, but I need to know how to get accurate results without manually finding the correct (Or almost correct) numbers

If anybody can tell me what I am doing wrong, or give me any advice it would be greatly appreciated.

#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>

SDL_Surface* screen = 0;
SDL_Event event;
bool isRunning = true;

int mouseX, mouseY, mouseZ = 0;

GLint viewport[4];
GLdouble mvmatrix[16], projmatrix[16];
GLint realY; /* OpenGL y coordinate position */
GLdouble wx, wy, wz; /* returned world x, y, z coords */

int main(int argc, char **argv) {
    SDL_Init(SDL_INIT_EVERYTHING);

    screen = SDL_SetVideoMode(800, 600, 32, SDL_OPENGL);

    glEnable(GL_DEPTH_TEST);
    glDepthMask(GL_TRUE);

    glViewport(0, 0, 800, 600);

    glClearDepth(1.f);
    glClearColor(0, 0, 0, 0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(90.f, 1.f, 1.f, 500.f);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    while(isRunning) {
        while(SDL_PollEvent(&event)) {
            if(event.type == SDL_QUIT) {
                isRunning = false;
            }

            if(event.type == SDL_MOUSEBUTTONDOWN) {
                if(event.button.button == SDL_BUTTON_LEFT) {
                    SDL_GetMouseState(&mouseX, &mouseY);

                    glGetIntegerv(GL_VIEWPORT, viewport);
                    glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
                    glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
                    realY = viewport[3] - (GLint) mouseY - 1;
                    glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &mouseZ);

                    gluUnProject((GLdouble)mouseX, (GLdouble)realY, 1.0, mvmatrix, projmatrix, viewport, &wx, &wy, &wz);
                }
            }
        }

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glPushMatrix();

        glTranslatef(wx, wy, wz);

        glBegin(GL_QUADS);
            glColor4f(1.f, 0.f, 0.f, 1.f);
            glVertex3f(-20.f, -20.f, 0.f);

            glColor4f(0.f, 1.f, 0.f, 1.f);
            glVertex3f(-20.f, 20.f, 0.f);

            glColor4f(0.f, 0.f, 1.f, 1.f);
            glVertex3f(20.f, 20.f, 0.f);

            glColor4f(1.f, 0.f, 1.f, 1.f);
            glVertex3f(20.f, -20.f, 0.f);
        glEnd();

        glPopMatrix();

        SDL_GL_SwapBuffers();
    }

    SDL_FreeSurface(screen);

    return 0;
}
Was it helpful?

Solution

I found that if I added the following:

GLfloat depth[2];

then changed the glReadPixels() from:

glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &mouseZ);

to:

glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, depth);

then I just changed gluUnProject() from:

gluUnProject((GLdouble)mouseX, (GLdouble)realY, mouseZ, mvmatrix, projmatrix, viewport, &wx, &wy, &wz);

to:

gluUnProject((GLdouble) mouseX, (GLdouble) realY, depth[0], mvmatrix, projmatrix, viewport, &wx, &wy, &wz);

then I could get the Z value I wanted.

Then I just added a small value to wz, for example: wz += 1; to make sure the quad appeared above the place I clicked and it seems to work as I intended now.

OTHER TIPS

To guess a Z you need to have a meaning for this z :

Usually the meaning is that you want to intersect the line corresponding to (camera origin,pixel) with a 3d object with is displayed. You can use the Z buffer for this.

If not ( you want to stick a polygon to the cursor with no 3D relation ), just use the 2D functions, you can just alternate between 3D and 2D projections while drawing in the same frame (with either the projection matrix or with 2D specific functions), thus you don't need a z.

Setting a default value for Z is a bad idea if it has no rationale in your design.

There is only one value of Z which will give you correct pixel coordinates.

The link between 2D and 3D coordinates in openGL is that OpenGl uses a projection plane at Z = 1. And then scales the pixel coordinates from [-1,1] to pixels coordinates.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top