I am trying to get the coordinates that the user clicks on on the plane y=0
I'm doing this by unprojecting the mouse coordinates to get the world coordinates on the near and far planes then using linear interpolation to find the coordinates on the plane but it's not giving me the correct coordinates.
My unprojection code:
int viewport[4];
double modelview[16];
double projection[16];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
double x, y, z;
//x_ and y_ are the x and y coordinates of the mouse
gluUnProject(x_, viewport[3] - y_, 0.0, modelview, projection, viewport, &x, &y, &z);
near.x = x;
near.y = y;
near.z = z;
gluUnProject(x_, viewport[3] - y_, 100.0, modelview, projection, viewport, &x, &y, &z);
far.x = x;
far.y = y;
far.z = z;
float t = -near.y / (far.y - near.y);
target_.y = 0.0f;
target_.x = near.x - t * (far.x - near.x);
target_.z = near.z - t * (far.z - near.z);
std::cout << target_ << std::endl;
but this always outputs:
x: a value between +-1 which seems to have a correlation to the click position just normalized even though I'm not normalizing anywhere
y: 0
z: -2
which I can't make sense of
Edit
Sorry, the error was me doing the unprojections before my transformations which you can't tell from the above code. I have solved it now.
Sorry, the error was me doing the unprojections before my transformations which you can't tell from the above code.
Related
I have a quad on the y = -50 plane. At the moment, all I want to do is obtain the coordinates of a mouse click on the quad. I've managed to do this to a limited extent. The problem is that the transformations I applied when drawing the quad aren't accounted for. I can add in some constants and make it work, but I let the user rotate the scene about the x and y axes with glRotatef(), so the coordinates get messed up as soon as a rotation happens.
Here's what I'm doing now:
I call gluUnProject() twice, once with z = 0, and once with z = 1.
gluUnProject( mouseX, WINDOW_HEIGHT - mouseY, 0, modelView, projection, viewport, &x1, &y1, &z1);
gluUnProject( mouseX, WINDOW_HEIGHT - mouseY, 1, modelView, projection, viewport, &x2, &y2, &z2);
Normalized ray vector:
x = x2 - x1;
y = y2 - y1;
z = z2 - z1;
mag = sqrt(x*x + y*y + z*z);
x /= mag;
y /= mag;
z /= mag;
Parametric equation:
float t = -(camY) / y;
planeX = camX + t*x;
planeY = camY + t*y;
planeZ = camZ + t*z;
where (camX, camY, camZ) is the camera position passed to gluLookAt().
I want planeX, planeY, and planeZ to be the coordinates of the click on the quad, in the same coordinate system I used to draw the quad. How can I achieve this?
You are not supposed to pass in an explicit z-depth of your choosing. In order to find the world coordinate, you need to pass in the depth buffer value at that particular mouse coordinate.
GLfloat depth;
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
Passing that into your gluUnProject should yield the values you are looking for. Plus, as genpfault said in his comment, make sure you are grabbing the model view matrix data at the right moment. Otherwise, you have the wrong matrix.
I am trying to implement an arcball interface and it seems that after rotation of 90 degrees the model stops rotating in that specific direction, I suspect that there is a problem with mapping clicks on the screen to the arcball, but it could be wrong math and/or wrong transformations accumulation, any help would be appreciated, here is the relevant code for the problem, when operating on vectors the ^ operator represents cross product, and * operator represents inner product
void mouseButton(int button,int state,int x,int y){
if(state==GLUT_DOWN){
GLdouble xx,yy,zz;
GLdouble modelMatrix[16];
glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
GLdouble projMatrix[16];
glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
int viewport[4];
glGetIntegerv(GL_VIEWPORT,viewport);
gluUnProject(x,height-y-1,0.755
,modelMatrix,projMatrix,viewport,&xx,&yy,&zz);
arcBall_begin(xx,yy);
}
}
void mouseMotion(int x,int y){
GLdouble xx,yy,zz;
GLdouble modelMatrix[16];
glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
GLdouble projMatrix[16];
glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
int viewport[4];
glGetIntegerv(GL_VIEWPORT,viewport);
gluUnProject(x,height-y-1,0.755
,modelMatrix,projMatrix,viewport,&xx,&yy,&zz);
arcBall_drag(xx,yy);
}
void arcBall_begin(GLdouble x,GLdouble y){
if(sqrt((x*x)+(y*y))>radius)
begin = vec(x,y,0);
else
begin = vec(x,y,sqrt((radius*radius)-(x*x)-(y*y)));
begin = begin.unit();
glGetDoublev(GL_MODELVIEW_MATRIX,mm);
}
void arcBall_drag(GLdouble x,GLdouble y){
if(sqrt((x*x)+(y*y))>radius)
end = vec(x,y,0);
else
end = vec(x,y,sqrt((radius*radius)-(x*x)-(y*y)));
end = end.unit();
rotationAxis = begin^end;
rotationAxis = rotationAxis.unit();
angle = -2*acos(begin*end);
angle = angle * (float(180)/float(PI));
}
float arcBall_rotate(){
if(angle!=0.0){
glLoadMatrixd(mm);
glRotatef(angle,rotationAxis.x,rotationAxis.y,rotationAxis.z);
angle = 0.0;
}
return angle;
}
Remember that in OpenGL pixel coordinates are (0,0) in the upper left corner of the screen, so when you want to map you have to reverse the y axis to map the coordinates to a sphere.
To zoom into the mouse position I was using:
glTranslatef(current.ScalePoint.x,current.ScalePoint.y,0);
glScalef(current.ScaleFactor,current.ScaleFactor,current.ScaleFactor);
glTranslatef(-current.ScalePoint.x,-current.ScalePoint.y,0);
so basically I translate to the new origin (the mouse position) then scale by the current scale factor, then translate back.
This kind of works generally well, but it can be a bit buggy. My issue is really that now I'v introduced a camera offset so I tried something like this:
glTranslatef(controls.MainGlFrame.GetCameraX(),
controls.MainGlFrame.GetCameraY(),0);
glTranslatef(current.ScalePoint.x,current.ScalePoint.y,0);
glScalef(current.ScaleFactor,current.ScaleFactor,current.ScaleFactor);
glTranslatef(-current.ScalePoint.x,-current.ScalePoint.y,0);
But this did not work as I intended. How could I properly do this knowing that:
The matrix's origin is the top left corner (0,0)
1 unit == 1 pixel
My scale factor
My camera's position relative to how far it is from (0,0) (the origin) and
the mouse position (screen to client).
Thanks
It is more safe (and also for code reuse) to un-project the mouse coordinate point (from window coordinates to model coordinates) first even though you know how projection is done.
You can use the following function:
void unProject(int ix, int iy, int &ox, int &oy)
{
// First, ensure that your OpenGL context is the selected one
GLint viewport[4];
GLdouble projection[16];
GLdouble modelview[16];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
int xx = ix;
int yy = viewport[3] - iy;
GLdouble x, y, z;
gluUnProject(xx, yy, 0 /*check*/, modelview, projection, viewport, &x, &y, &z);
ox = (int) x;
oy = (int) y;
}
The output then is the correct point on the model coordinates for your zooming
So I have some openGL code (such code for example)
/* FUNCTION: YCamera :: CalculateWorldCoordinates
ARGUMENTS: x mouse x coordinate
y mouse y coordinate
vec where to store coordinates
RETURN: n/a
DESCRIPTION: Convert mouse coordinates into world coordinates
*/
void YCamera :: CalculateWorldCoordinates(float x, float y, YVector3 *vec) { // START GLint viewport[4]; GLdouble mvmatrix[16], projmatrix[16];
GLint real_y;
GLdouble mx, my, mz;
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
real_y = viewport[3] - (GLint) y - 1; // viewport[3] is height of window in pixels
gluUnProject((GLdouble) x, (GLdouble) real_y, 1.0, mvmatrix, projmatrix, viewport, &mx, &my, &mz);
/* 'mouse' is the point where mouse projection reaches FAR_PLANE.
World coordinates is intersection of line(camera->mouse) with plane(z=0) (see LaMothe 306)
Equation of line in 3D:
(x-x0)/a = (y-y0)/b = (z-z0)/c
Intersection of line with plane:
z = 0
x-x0 = a(z-z0)/c <=> x = x0+a(0-z0)/c <=> x = x0 -a*z0/c
y = y0 - b*z0/c
*/
double lx = fPosition.x - mx;
double ly = fPosition.y - my;
double lz = fPosition.z - mz;
double sum = lx*lx + ly*ly + lz*lz;
double normal = sqrt(sum);
double z0_c = fPosition.z / (lz/normal);
vec->x = (float) (fPosition.x - (lx/normal)*z0_c);
vec->y = (float) (fPosition.y - (ly/normal)*z0_c);
vec->z = 0.0f;
}
I want to run It but with out precompiling. Is there any way to do such thing
This is possible with LWJGL (OpenGL binding) and the REPL in Scala (runs on the JVM). I imagine that other languages like Clojure/Jython could also handle this request -- either through LWJGL or Jogl. There are also OpenGL bindings for a whole host of languages that don't require (explicit) compiling or come with their own REPL and/or 'integrated IDE'.
C normally always requires a compilation, but I did find this:
http://neugierig.org/software/c-repl/ and I'm sure there are other projects similar in nature.
Happy coding.
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;