gluUnProject ray picking(opengl/c++/glut) - c++

I am trying to see if I can click on a 3D drawn object, using the mouse, in a 3D world. I understood that I have to use gluUnProject so I tried it out in the following way:
Firstly I have a function that gets the x,y coordinates from the mouse(the window coordinates), and tries to transform them into real world coordinates:
void project(int x_cursor, int y_cursor){
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX,winY;
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
// obtain the Z position (not world coordinates but in range 0 - 1)
GLfloat z_cursor;
winX = (float)x_cursor;
winY = (float)viewport[3]-(float)y_cursor;
glReadPixels(winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z_cursor);
// obtain the world coordinates
gluUnProject(winX, winY, z_cursor, modelview, projection, viewport, &x, &y, &z);
}
Then I have the mouse callback function:
void mouse(int button, int state, int x_cursor, int y_cursor){
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
mouse_x=x_cursor;
mouse_y=y_cursor;
project(mouse_x,mouse_y);
cout<<x<<" "<<"Printed out variable x"<<endl;
cout<<y<<" "<<"Printed out variable y"<<endl;
cout<<z<<" "<<"Printed out variable z"<<endl;
/**for (int i = 0; i < 150; i++) {
if ((enemies[i]!=NULL)&&(enemies[i]->getTranslation().x > x - 1 && enemies[i]->getTranslation().x < x + 1)
&& (enemies[i]->getTranslation().z > z - 1 && enemies[i]->getTranslation().z < z + 1)
&& (enemies[i]->getTranslation().y > y - 1 && enemies[i]->getTranslation().y < y + 1)) {
running=false;
}
}**/
}
}
For the moment I am just printing out the coordinates x,y,z, but in the commented code you can see what I intend to do with them: verify if a drawn object is in the immediate vecinity of the spot where I clicked the mouse.
Lastly I added the glutMouseFunc(mouse); line in the main() function, and it compiles ok, but judging by the result of the prints it doesn't indicate the correct values.
I would like to add the fact that I have a moving camera;by this I mean I can change the glulookat parameters, using keyboard input. Could you please look at my code and maybe show me where I'm doing wrong. Thanks a lot.
[EDIT] It only seems to work when I set the camera like: gluLookAt(-35, 0, 0, 0, 100, 0, 0, 0, 1);
[EDIT2] Also if I move the position of the camera, and then return it in the initial position(where it worked), it doesn't work anymore.
Solved. The above code is ok. The problem was I was adding glPushMatrix(); and glPopMatrix(); before and after drawing the objects in the scene. The question can be closed.

Related

Swap Y and Z Axis in OpenGL

So basically i have created an application that renders a curve in a 2D-Dimension (X and Y). This application also uses a haptic device, which is represented as a cursor inside of the frame where the curve is also rendered.
The only thing i want to achieve is to map the y-axis of my projection to my z-axis of the haptic workspace of my haptic device. So basically i want to mantain the logic of the curve and cursor rendering and only change the used axis for my haptic workspace. I used this question: Swap axis y and z
as a guidance. Practically i just have to use glRotatef(90, 1, 0, 0) to achieve what i want. I tried rotating only the projection matrix of my haptic workspace, but this sadly doesnt work for me.
This is how my normal curve looks:
Those are the results, while trying different options:
This is my code
reshapeCallback:
void reshapeCallback(int width, int height) {
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(minX, maxX, minY, maxY, -1, 11);
// glRotatef(90, 1, 0, 0) [4th Try]
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// glRotatef(90, 1, 0, 0) [5th Try]
updateWorkspace();
}
updateWorkspace():
void updateWorkspace() {
GLdouble modelview[16];
GLdouble projection[16];
GLint viewport[4];
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
//glRotatef(90, 1, 0, 0) [2nd try]
glGetDoublev(GL_PROJECTION_MATRIX, projection);
//glRotatef(90, 1, 0, 0) [1st try]
glGetIntegerv(GL_VIEWPORT, viewport);
hlMatrixMode(HL_TOUCHWORKSPACE);
hlLoadIdentity();
//glRotatef(90, 1, 0, 0) [3rd try]
// Fit haptic workspace to view volume.
hluFitWorkspace(projection);
// Compute cursor scale.
gCursorScale = hluScreenToModelScale(modelview, projection, viewport);
gCursorScale *= CURSOR_SIZE_PIXELS;
}
drawLine():
void drawLine() {
static GLuint displayList = 0;
if (displayList)
{
glCallList(displayList);
}
else
{
displayList = glGenLists(1);
glNewList(displayList, GL_COMPILE_AND_EXECUTE);
glPushAttrib(GL_ENABLE_BIT | GL_LIGHTING_BIT);
glDisable(GL_LIGHTING);
glLineWidth(2.0);
glBegin(GL_LINE_STRIP);
for (int i = 0; i < vertices.size(); i += 2) {
glVertex2f(vertices[i], vertices[i + 1]);
}
glEnd();
glPopAttrib();
glEndList();
}
}
I numerated the tries of glRotatef() for different positions. I hope someone can give me a hint where my thought process went wrong.
Point the first: For the love of God USE SHADERS AND NOT OLD OPENGL.
With shaders, it is a trivial task of swapping 2 arguments, changing
gl_Position = vec4(x, y, z, 1);
to
gl_Position = vec4(x, z, y, 1);
I would recommend looking at this tutorial for shaders.
Other that that, in my opinion the code looks like it should work, but if it doesn't I know how finicky glRotate can be, so I really have no advice other that to try things until it works.
Sorry I couldn't give a full, direct answer.

Rotate Direction of Camera Inverses Itself After Some Time

I'm trying to create mini application which rotates camera around a cube with movement of mouse. It works perfectly on Rotating around Y axis, but I have a problem with rotating around X axis. When I continuously move my mouse upwards, cube starts to rotate on positive X direction. After some time, it reverses it's rotation direction and starts rotating on negative X direction, then after some time, again rotates on positive X direction. I want to make it rotate around Positive X as far as I move my mouse upwards. What is the problem here?
Cube is positioned on the center of coordinate system. Projection is perspective:
Edit: Here is the video related to problem: https://youtu.be/997ZdUM8fcQ
vec4 eye;
vec4 at;
vec4 up;
GLfloat mouseX = -1;
GLfloat mouseY = -1;
// Bound to glutPassiveMotionFunc
void mouse(int x, int y) {
// This rotation works perfect, cube rotates on same direction continuously as far as I move the mouse left or right
GLfloat acceleration_x = mouseX - x;
eye = RotateY(acceleration_x / 10.f) * eye;
mouseX = x;
// This rotation doesn't work properly, after some time, cube's rotation direction inverses
GLfloat acceleration_y = mouseY - y;
eye = RotateX(acceleration_y / 10.f) * eye;
mouseY = y;
// This part teleports pointer to opposite side of window after reaching window bounds
if (x >= 1364) {
glutWarpPointer(1, y);
mouseX = 1;
}
else if (x <= 0) {
glutWarpPointer(1363, y);
mouseX = 1363;
}
if (y >= 751) {
glutWarpPointer(x, 3);
mouseY = 3;
}
else if (y <= 2) {
glutWarpPointer(x, 750);
mouseY = 750;
}
}
void display( void )
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
vec4 up( 0.0, 1.0, 0.0, 0.0 );
mat4 model_view = LookAt( eye, at, up );
glUniformMatrix4fv( ModelView, 1, GL_TRUE, model_view );
glDrawArrays( GL_TRIANGLES, 0, NumVertices );
glutSwapBuffers();
}
// Bound to glutTimerFunc
void timer( int id )
{
glutPostRedisplay();
glutTimerFunc(1000.f/60.f, timer, 0);
}
int main( int argc, char **argv )
{
......
......
eye = vec4(0, 0, 2, 0);
at = vec4(0, 0, 0, 0);
up = vec4(0.0, 1.0, 0.0, 0.0);
.....
.....
}
It is because the camera gets flipped. Use your hand like this: let the index finger be the dir, and your thumb is up (thumb=(0, 1, 0)). Direct your index finder forwards, and your thumb upwards. Now, start rotating your hand around X: your index finger starts to point downwards, while your thumb forwards (during this, thumb.y is positive). When you rotated your hand 90 degrees, index finger points downwards, and thumb points forwards (thumb.y is 0). When you continue rotating, your thumb starts to point forwards+downwards (thumb.y becomes negative).
At this moment, the LookAt function will not calculate the matrix you wanted, because you want the up vector to point downwards (as we said, thumb.y is negative), but in the code, up vector is a constant (0, 1, 0), so LookAt will calculate a matrix which has a positive up.y: The camera gets flipped.
A solution could be that you register the needed rotation angles, and rotate camera matrix (instead of dir) with these angles

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;
}

Object picking with ray casting

I've been having a problem with inaccuracies in my ray casting algorithm for detecting mouse hits within a box. I'm completely at a loss as to how to fix this properly and it's been bugging me for weeks.
The problem is easiest described with a picture (box centered around [0, 0, -30]):
The black lines represent the actual hitbox which is drawn and the green box represents what actually appears to get hit. Notice how it's offset (which seems to get larger if the box is further from the origin) and is slightly smaller than the drawn hitbox.
Here's some relevant code,
ray-box cast:
double BBox::checkFaceIntersection(Vector3 points[4], Vector3 normal, Ray3 ray) {
double rayDotNorm = ray.direction.dot(normal);
if(rayDotNorm == 0) return -1;
Vector3 intersect = points[0] - ray.origin;
double t = intersect.dot(normal) / rayDotNorm;
if(t < 0) return -1;
// Check if first point is from under or below polygon
bool positive = false;
double firstPtDot = ray.direction.dot( (ray.origin - points[0]).cross(ray.origin - points[1]) );
if(firstPtDot > 0) positive = true;
else if(firstPtDot < 0) positive = false;
else return -1;
// Check all signs are the same
for(int i = 1; i < 4; i++) {
int nextPoint = (i+1) % 4;
double rayDotPt = ray.direction.dot( (ray.origin - points[i]).cross(ray.origin - points[nextPoint]) );
if(positive && rayDotPt < 0) {
return -1;
}
else if(!positive && rayDotPt > 0) {
return -1;
}
}
return t;
}
mouse to ray:
GLint viewport[4];
GLdouble modelMatrix[16];
GLdouble projectionMatrix[16];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projectionMatrix);
GLfloat winY = GLfloat(viewport[3] - mouse_y);
Ray3 ray;
double x, y, z;
gluUnProject( (double) mouse_x, winY, 0.0f, // Near
modelMatrix, projectionMatrix, viewport,
&x, &y, &z );
ray.origin = Vector3(x, y, z);
gluUnProject( (double) mouse_x, winY, 1.0f, // Far
modelMatrix, projectionMatrix, viewport,
&x, &y, &z );
ray.direction = Vector3(x, y, z);
if(bbox.checkBoxIntersection(ray) != -1) {
std::cout << "Hit!" << std::endl;
}
I've tried drawing the actual ray as a line and it seems to intersect the drawn box correctly.
I had the offset problem partially fixed by minusing all the points and the ray origin/direction by the boxes position, but I have no idea why that worked and the size of the hitbox still remained inaccurate.
Any ideas/alternative approaches? I have other code to supply if it's needed.
You're assuming a wrong direction. Correct would be:
ray.direction = Vector3(far.x - near.x, far.y - near.y, far.z - near.z);
Without subtracting near and far intersection points, your direction will be off.

OpenGL: Selecting all dots from the current view area

Im using gluUnProject() to get the screen 2d coordinate in 3d world coordinate. I take 4 positions from each corner of the screen to get the area of visible objects.
How to check which points are inside that "rectangle" ?, i have no idea about the terms or anything. The image below shows what that "rectangle" looks like:
Are you trying to find which 3D point are visible by a camera? If so, you might find some interesting informations on this website: http://www.lighthouse3d.com/opengl/viewfrustum/.
In the following image, we can see the view frustum and your selection frustum (in red). Applying frustum visibility checks to your selection frustum should the same algorithm as the one used for frustum culling.
If you want a quick and non optimized solution:
GLdouble model_view[16];
glGetDoublev(GL_MODELVIEW_MATRIX, model_view);
GLdouble projection[16];
glGetDoublev(GL_PROJECTION_MATRIX, projection);
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
for(unsigned i=0; i<points.size(); ++i){
GLdouble winX, winY, winZ;
gluProject(points[i].x, points[i].y, points[i].z, model_view, projection, viewport, &winX, &winY, &winZ);
if(selectionMinX <= winX && winX <= selectionMaxX && selectionMinY <= winY && winY <= selectionMaxY && winZ>=0 && winZ<=1){
/// point is selected
}
}