GlunUnproject moving object with the mouse - opengl

I'm trying to move objects with the mouse using the Gluunproject method with openGL 2.1 , but i'm really struggling here; here's the code i wrote :
int viewport[4];
double modelview[16],
projection[16],
X1, Y1, Z1;
double realY;
GLfloat depth[2];
for(_compt=_OjebctScene.begin();_compt!=_OjebctScene.end();_compt++)
{
if ((*_compt)->IsSelected())
{
GLdouble mouseX=event.x;
GLdouble mouseY=event.y;
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
realY = viewport[3] - (GLint) mouseY - 1;
glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, depth);
gluUnProject(mouseX, realY, 0, modelview, projection, viewport, &X1, &Y1, &Z1);
(*_compt)->setX(X1);
(*_compt)->setY(Y1);
(*_compt)->setZ(Z1);
}
}
I use a loop to check all the objects on the scene (I've pushed them into a vector) , then when i find the selected object , i try to move it using the mouse.
I then set the coordinates of my object to the position of the mouse in the 3D space (X1 , Y1 , Z1); but this doesn't really work.

I think I've fixed it just by passing the depth[0] to the gluunproject method.
here's what I've done , change this :
gluUnProject(mouseX, realY, 0, modelview, projection, viewport, &X1, &Y1, &Z1);
by this :
gluUnProject(mouseX, realY,depth[0], modelview, projection, viewport, &X1, &Y1, &Z1);
I don't understand what this means though , if someone could explain it to me it would be nice.

Related

gluUnproject returning 0, seems to be related to modelview matrix

I'm working on a 2D image viewer, I want to retrieve openGL mouse position on texture but I can't get it to work if glTranslatef() or glScalef() calls are made on the modelview matrix.
I'm using a QGLWidget , of the famous Qt library.
Here are the important calls :
Resize function :
void ViewerGL::resizeGL(int width, int height){
glViewport (0, 0, width, height);
Display function :
void ViewerGL::paintGL()
{ int w = width();
int h = height();
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
//transX,transY are for panning around the image in the viewer
float left = (0.f+transX) ;
float right = (w+transX) ;
float bottom = (h-transY);
float top = (0.f-transY) ;
glOrtho(left, right, top, bottom, -1, 1);
... later in paintGL:
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
//padx,pady are used to translate the image from the bottom left corner
// to the center of the viewer
float padx,pady;
padx= ((float)width() - _dw.w()*zoomFactor)/2.f; // _dw.w is the width of the texture
pady =((float)height() - _dw.h()*zoomFactor)/2.f ;// _dw.h is the height of the texture
glTranslatef( padx , pady, 0);
//zoomX,zoomY are the position at which the user required a zoom
glTranslatef(-zoomX,-zoomY, 0.f);
glScalef(zoomFactor, zoomFactor,0.f);
glTranslatef(zoomX ,zoomY, 0.f);
Now here is my function to retrieve the openGL coordinates :
QPoint ViewerGL::openGLpos(int x,int y){
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX=0, winY=0, winZ=0;
GLdouble posX=0, posY=0, posZ=0;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
winX = (float)x;
winY = height()- y;
if(winY == 0) winY =1.f;
glReadPixels( x, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
return QPoint(posX,posY);
}
So far , here is what I noticed:
The code like this returns always (0,0) and GLU_FALSE is returned from gluUnproject. I read somewhere on a forum that it could be because of the modelview matrix, so I put the identity matrix instead, but,if I do it, I get exactly the coordinates of the mouse in the window...
Before , I dealt with the zoom using the orthographic projection, but I couldn't make it work perfectly, so to make it simpler I decided to retrieve openGL position, and use glTranslatef/glScalef instead .
If I remove all the translating / scaling stuff in the paintGL function, everything is working...but the zoom doesn't work :x)
I'm requesting your help to make this damned zoom to point working, using the gluUnProject solution;)
Aigth , nevermind, I found the solution : I was zeroing out the z in glScalef(x,y,z)
so it made the matrix non-invertible...

opengl mouse coordinate to world coordinate

I need to detect mouse motion and draw a ball at the mouse's position. I need the ball to be in world coordinate. So I'm trying to use glUnProject for this task and seems I'm not succeeding till now. This is my motionFunc:
void motionFunc( int x, int y)
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
winX = (float)x;
winY = (float)viewport[3] - (float)y;
glReadPixels( x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ); //printf("winz: %f posz: %f\n",winZ,posZ);
Ball.x=posX;
Ball.y=posY;
Ball.z=posZ;
//printf("%f %f %f\n",Ball.x,Ball.y,posZ);
glutPostRedisplay();
}
now, I added a breakpoint on glutPostRedisplay. Turns out when I click-drag mouse, the Ball's coordinates(Ball.x,Ball.y,Ball.z) are something like:
(Ball).x -727.175354
(Ball).y 407.310242
(Ball).z -865.000610
why is the z coordinate so far? My camera is at z=+135. And other objects in my model are like, at z= -3 to +3. I need the ball's z coordinate to be in the same range.
now, what exactly is winZ? Here, I checked it always turns out to be 1.00. I tried to hardcode winZ and I found at winZ=0.85, the ball seems like to be always under the mouse(I can drag the ball with my mouse and the ball is always under the pointer). But then the Ball's coordinates are like:
(Ball).x -4.67813921
(Ball).y 2.57806134
(Ball).z 128.370895
which is so close to the camera but x and y coordinates are not good for me. they always come out to be near the origin. which is not what I want. My other objects' x and y coordinates have a wider range.
Finally, my question is, what is the correct way to do glUnproject or something of the same sort?
A 2D mouse coordinate cannot unambiguously unmapped to a 3D world coordinate. The 2D coordinate corresponds with a line in 3D space. The winz influences which point on this line is returned. When you use 0.0 for winz you will get the point at the near clipping plane. When you use 1.0 you will get the point at the far clipping plane.
If you are using a depth buffer you could retrieve the value from the depth buffer using the glReadPixels function and use that as winz. Here a piece of code from a toy project of mine in Java
FloatBuffer depth = BufferUtils.createFloatBuffer(1);
glReadPixels(mouse_x, mouse_y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, depth);
depth.rewind();
FloatBuffer farPos = BufferUtils.createFloatBuffer(3);
GLU.gluUnProject(mouse_x, mouse_y, depth.get(),
mainContext.getModelviewMatrix(),
mainContext.getProjectionMatrix(), viewport, farPos);
Most is the same in C/C++ except for the weird NIO buffers.

gluUnProject not working

I am trying to use gluUnProject to get my mouse coordinates into world coordinates, however it seems to not be working, or I am just misunderstanding the functionality of the glUnProject function, here is the code I am working with, my matrices all check out fine and as for the -300 on the mouse x coordinate, I am using a C++ Win32 Dialog and the ScreenToClient is giving me funky results.
int appWidth = CApplication::GetInstance()->GetWidth();
int appHeight = CApplication::GetInstance()->GetHeight();
float fAspect = (float)appWidth / (float)appHeight;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f, fAspect, 0.1f, 100000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(m_vecCamera.x, -m_vecCamera.y, m_vecCamera.z);
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glEnable(GL_DEPTH);
//Retrieve the Model/View, Projection, and Viewport Matrices
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
//Retrieve the Mouse X and the flipped Mouse Y
winX = (float)pInput->msg.param1-300.0f;
winY = (float)viewport[3]-(float)pInput->msg.param2;
glReadPixels( int(winX), int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
This is however giving me coordinates relative to the center of my screen, and I am assuming is relative to my camera, I also tried implementing my own function
Vector2f MouseUnProject(int x, int y)
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glEnable(GL_DEPTH);
//Retrieve the Model/View, Projection, and Viewport Matrices
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
//Retrieve the Mouse X and the flipped Mouse Y
winX = (float)x;
winY = (float)viewport[3]-y;
glReadPixels( int(winX), int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
double projectionX, projectionY;
double viewX, viewY;
double worldX, worldY;
//Convert from Screen Coords to Projection Coords
projectionX = (double)winX / ((double)viewport[2]/2.0) - 1.0;
projectionY = (double)winY / ((double)viewport[3]/2.0) + 1.0;
//Convert from projection Coords to View Coords
viewX = projectionX * modelview[14];
viewY = projectionY * modelview[14];
//Convert from View Coords to World Coords
worldX = viewX + modelview[12];
worldY = viewY - modelview[13];
return Vector2f(worldX, worldY);
}
It works to a certain mount, but when moving the camera, the numbers instantly go off a bit, the conversion from projection to view coords 'seems' to be ok, and the projection coords are definitely good.
I would really prefer to use glUnProject rather then my own function, but I can't get it to work for the life of me and all of the google searches I found don't seem to answer my question. What exactly does the GL documentation mean by 'object space' perhaps my understanding of that is wrong, and if so what do I additionally have to do to get my coordinates in the right space?
was posted a year ago,but anyways....so you are getting coordinates relative the the screen because you made a call to gluPerspective. this call internally calls glfrustum which will generate normalized coordinates in the range {-1, 1}. However if you called glfrustum directly with your near/far values you would have got the result from gluUnproject in that range.
To get back to your map editor coordinates, simply take the result from gluUnproject and manually range convert back to your editor coordinate system, ie{-1,1} => {0, max}
To get started you should test gluUnProject by inputting (0,0), (midX, midY), (maxX, maxY) and the result from gluUnProject should be (-1, -1, depth), (0, 0, depth) and (1, 1, depth). If you setup the projection matrix using glFrustum then the above result will be returned in the near/far range.

handling click on 3d or 2d figure in opengl

how to handle clik on specific 3d or 2d object in opengl, for example i have the following code
void Widget::drawCircle(float radius) {
glBegin(GL_TRIANGLE_FAN);
for (int i = 0; i < 360; i++) {
float degInRad = i*DEG2RAD;
glVertex2f(cos(degInRad) * radius, sin(degInRad) * radius);
}
glEnd();
}
So i need to handle click on this circle, is there any solutions for this problem?
When I need to detect clicks, I usually do my ordinary draw loop, but instead of drawing the objects with texturing, lighting and other effects enabled, I draw each of them with flat/no shading, each in a different color. I then check the color on the pixel the mouse is on, and map backwards from the color returned from the framebuffer to the object that I drew with that color.
Perhaps this technique is useful for you, too.
Take a look into this nehe tutorial item. It is very complex, but it shows how opengl picking works. In my opinion, if you need it, you are better with some game engine then with opengl.
Here is another (similar) way of selecting items in opengl.
opengl mouse raytracing will provide you with all details how to select items in opengl. This thread even provides the code how it is done :
Vector3 World::projectedMouse(float mx, float my){
GLdouble model_view[16];
GLint viewport[4];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble dx, dy, dz;
glGetDoublev(GL_MODELVIEW_MATRIX, model_view);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
winX = (float)mx;
winY = (float)viewport[3] - (float)my;
glReadPixels ((int)mx, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, 0, model_view, projection, viewport, &bx, &by, &bz);
Vector3 pr2 = Vector3(bx, by, bz);
glColor3f(1,0,0);
glBegin (GL_LINE_LOOP);
glVertex3f(player->getPosition().x, player->getPosition().y + 100, player->getPosition().z); // 0
glVertex3f(pr.x,pr.y,pr.z); // 1
glVertex3f(player->getPosition().x, player->getPosition().y, player->getPosition().z); // 0
glEnd();
return pr;
}

OpenGL: projecting mouse click onto geometry

I have this view set:
glMatrixMode(GL_MODELVIEW); //Switch to the drawing perspective
glLoadIdentity(); //Reset the drawing perspective
and I get a screen position (sx, sy) from a mouse click.
Given a value of z, how can I calculate x and y in 3d-space from sx and sy?
You should use gluUnProject:
First, compute the "unprojection" to the near plane:
GLdouble modelMatrix[16];
GLdouble projMatrix[16];
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
GLdouble x, y, z;
gluUnProject(sx, viewport[1] + viewport[3] - sy, 0, modelMatrix, projMatrix, viewport, &x, &y, &z);
and then to the far plane:
// replace the above gluUnProject call with
gluUnProject(sx, viewport[1] + viewport[3] - sy, 1, modelMatrix, projMatrix, viewport, &x, &y, &z);
Now you've got a line in world coordinates that traces out all possible points you could have been clicking on. So now you just need to interpolate: suppose you're given the z-coordinate:
GLfloat nearv[3], farv[3]; // already computed as above
if(nearv[2] == farv[2]) // this means we have no solutions
return;
GLfloat t = (nearv[2] - z) / (nearv[2] - farv[2]);
// so here are the desired (x, y) coordinates
GLfloat x = nearv[0] + (farv[0] - nearv[0]) * t,
y = nearv[1] + (farv[1] - nearv[1]) * t;