Clicking on a Quad and getting screen coords in opengl - opengl

Say I make a quad like so:
float botBaseY = -0.5;
glBegin(GL_QUADS); // Box
glVertex2f(-.05, botBaseY + -.05);
glVertex2f(-.05, botBaseY + .05);
glVertex2f(.05, botBaseY + .05);
glVertex2f(.05, botBaseY + -.05);
glEnd();
OK, well now say I want to determine if the user clicked inside of that quad.
Well when you click, you receive screen coordinates, so if your main window is
600x600 pixels, then your click will yield an x and y value less than 600.
But I'm comparing a click at say (375,400) to a quad that was created using
values from -1.0 to 1.0 to define the 4 vertices.
How can I determine if the user clicked inside of the quad?

I currently see 2 ways to do this.
if your 'interface' is completely 2D and directly mapped into screen - you could just use screen coordinates for drawing. Just set correct orthographic matrix and your click coordinates will be the same as 2D elements coordinates - simple range comparison (x < click_x < x+width). See gluOrtho2D, glOrtho manuals (please note that glOrtho and other matrix functions are not available in newest GL profiles).
if first variant isn't fitting your situation - you need to map 2D coordinates to view space and use resulting point as ray direction. Then trace this ray's intersection with element's plane you want to click. Take a look at http://www.toymaker.info/Games/html/picking.html - doesn't matter that it's for d3d, math is always the same). Then, when you'll take resulting click point on a plane you've interested in - it's again just range comparison (although it will be a little trickier if your plane is not parallel to screen - but i don't think it still the same question).

I never ended up getting a useful answer on here, but I did end up figuring it out the next day. My box was being drawn using coordinates that were from -1 to 1. But my click had large screen coordinates such as 200,300. I needed to know how to convert the -1 to 1 into screen coordinates, or how to just draw it compatible coordinates.
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, 400, 0, 400);
glBegin(GL_QUADS); // Box
glVertex2f(xOffset + topBaseX + -15, topBaseY + -15);// bottom left vert
glVertex2f(xOffset + topBaseX + -15, topBaseY + 15); // top left vert
glVertex2f(xOffset + topBaseX + 15, topBaseY + 15);// top right vert
glVertex2f(xOffset + topBaseX + 15, topBaseY + -15);// bottom right vert
glEnd();
glLoadIdentity();
That was all I had to do to draw my box at something like 200,300 and then I could simply check the boundaries of my box on each click.

Easiest and most accurate (but not necessarily most efficient) way:
Render the scene again (into an off-screen buffer) with the target object white and all else black. If the pixel under the cursor is white, the target was clicked. If you want to be able to click through some transparent materials, just skip rendering those objects.
For efficiency, you can do this with blending turned off and a unique color (perhaps an index) for each object. And use new OpenGL capabilities to fill the picker color plane during normal rendering instead of taking an additional pass.

Related

GPU mouse picking OpenGL/WebGL

I understand i need to render only 1x1 or 3x3 pixel part of the screen where the mouse is, with object id's as colors and then get id from the color.
I have implemented ray-cast picking with spheres and i am guessing it has something to do with making camera look in direction of the mouse ray?
How do i render the correct few pixels?
Edit:
setting camera in direction of mouse ray works, but if i make the viewport smaller the picture scales but what (i think) i need is for it to be cropped rather than scaled. How would i achieve this?
The easiest solution is to use the scissor test. It allows you to render only pixels within a specified rectangular sub-region of your window.
For example, to limit your rendering to 3x3 pixels centered at pixel (x, y):
glScissor(x - 1, y - 1, 3, 3);
glEnable(GL_SCISSOR_TEST);
glDraw...(...);
glDisable(GL_SCISSOR_TEST);
Note that the origin of the coordinate system is at the bottom left of the window, while most window systems will give you mouse coordinates in a coordinate system that has its origin at the top left. If that's the case on your system, you will have to invert the y-coordinate by subtracting it from windowHeight - 1.

C++ OpenGL dragging multiple objects with mouse

just wondering how someone would go about dragging 4 different
objects in openGL. I have very simple code to draw these objects:
glPushMatrix();
glTranslatef(mouse_x, mouse_y, 0);
glutSolidIcosahedron();
glPopMatrix();
glPushMatrix();
glTranslatef(mouse_x2, mouse_y2, 0);
glutSolidIcosahedron();
glPopMatrix();
glPushMatrix();
glTranslatef(mouse_x3, mouse_y3, 0);
glutSolidIcosahedron();
glPopMatrix();
glPushMatrix();
glTranslatef(mouse_x4, mouse_y4, 0);
glutSolidIcosahedron();
glPopMatrix();
I understand how to move an object, but I want to learn how to drag and drop any one of these objects.
I've been researching about the Name Stack and Selection Mode, but it just confused the hell out of me.
And I also know about having to have some sort of glutMouseFunc.
It's just the selection of each shape I'm puzzled on.
First thing that you need to do is capturing the position of mouse on the screen when the button is clicked. There are plenty of ways to do it but I believe it's outside of the scope of this question. When you have screen X,Y coords you must detect if any object is selected and which one it is. There are two possible approaches. You can either keep track of a bounding rectangle positions of each object (in screen space) and the test if the cursor is inside any of those rectangles will be quite simple. Or you can cast a ray from eye through cursor position in world space and check intersection of this ray with each object.
The second approach is more versatile for 3D graphics but you seem to be using only X and Y coords so you don't need to worry about Z order of objects.
In case of the first solution the main problem is: how to know how big is your object on the screen. glutSolidIcosahedron() renders an object of radius 1. To calculate it's screen radius you can either use some matrix math or in that case a simple trigonometry. You will need to know the distance from camera to the drawing plane (I believe you're using some glTranslatef(0,0,X) before you render. X is your distance) You also need to know the view angle of the camera. You set it in projection matrix. Now take a piece of paper, draw a cone of angle alpha, intersecting a plane at distance X and knowing that an object has radius 1 you can easily calculate how large area of the screen it occupies. (I'll leave this calculation for you)
Now if you know the radius on screen, simply test the distance from your click position to each object. if the distance is below radius it's selected. If more than one object passes this test just select first one of them.

How can I extend my viewport in openGL to keep printed text clipped properly?

I am using this code to draw a text in openGL with GLUT:
Code (init part) which sets my origin (0,0) into top-left corner and maintains proper screen-resolution based coordination system :
glViewport(0, 0, x, y);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, x, y, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
Code (printing part) :
void drawtext(GLfloat x, GLfloat y, const char* string )
{
glRasterPos2f(x,y);
while ( *string != '\0' )
glutBitmapCharacter(p_glutfont,static_cast<int>(*string++));
}
And everything would be perfect if not only the fact that when X and Y starts at negative coordinations (due to the fact that objects with text printed as a tag are partially outside of the view, then the whole text is not being drawn. lets imagine that I have a object with text tag on top of it and it enters the view from left top corner at x = -40, y = 40 :
X axis window
axis Y ----------------------------
| y=40 |
x=-40__|_________ |
|Text to print| |
|_____________| |
| |
| |
|____________________________|
The text is not being printed until is drawn from exactly X => 0 coordination. Also this problem doesn't exist when text has to be clipped on RIGHT side - then it is printed properly - this is where I I realized that it has to do with initial position of the text.
My question is - how can I EXTEND properly my viewport / screen mapping to :
1) preserve screen coordinations from top-left corner (0,0) to bottom-right (height,width) 2) with internally extended those values just during the printing the text - so the text would get clipped instead of disappearing ?
Although I did my research, I couldn't for 2 days now come to any solution. I tried to play with glViewport and glOrtho but none of changes and approaches I tried (like for example setting glViewport to -x,-y,x*2,y*2) would do the trick - those always mess with my screen coordination system which is essential to keep at window-resolution based.
PS: I did further research and I came across this article http://www.opengl.org/archives/resources/faq/technical/clipping.htm.
10.070 How do I draw glBitmap() or glDrawPixels() primitives that have an initial glRasterPos() outside the window's left or bottom edge?
When the raster position is set outside the window, it's often outside
the view volume and subsequently marked as invalid. Rendering the
glBitmap and glDrawPixels primitives won't occur with an invalid
raster position. Because glBitmap/glDrawPixels produce pixels up and
to the right of the raster position, it appears impossible to render
this type of primitive clipped by the left and/or bottom edges of the
window.
However, here's an often-used trick: Set the raster position to a valid value inside the view volume. Then make the following call:
glBitmap (0, 0, 0, 0, xMove, yMove, NULL);
This tells OpenGL to render a no-op bitmap, but move the current
raster position by (xMove,yMove). Your application will supply
(xMove,yMove) values that place the raster position outside the view
volume. Follow this call with the glBitmap() or glDrawPixels() to do
the rendering you desire.
Sadly - I can't put this advice in work - and once I use it - the text is completely missing.
this is where I I realized that it has to do with initial position of the text.
Almost. glRasterPos is a raster drawing operation command and whatever position you put into it goes through the transformation pipeline. If the point falls outside the clip volume (not the viewport!) it gets discarded and all further raster drawing operations are disabled until a new glRasterPos specified that is not clipped.
The easy and recommended solution would be not to use raster drawing operations at all, because they're slow, poorly supported and have been removed from later OpenGL alltogether. glutBitmapCharacter is using raster drawing ops.
Use texture mapped fonts or stroked characters instead. Google finds uncountable amounts of information on these.

OpenGL rubiks cube - face rotation with mouse

I am working on my first real OpenGL Project. It is a 3x3x3 Rubiks Cube. Here is a link to a simple screenshot of what i have so far(my rubiks cube)
Rotating the cube is done with dragging the mouse while holding the right mouse button. This works using the example of a arcball from NeHe Tutorials(NeHe Arcball)
I have the class singleCubes which represents one cube via 6 actual quads, stored in a display list that can be used in it´s draw method.
Class ComplexCube has an array of 3x3x3 singleCubes and is used as interface when interacting with the complete rubiks cube.
Now i want to rotate each specific face according to the mousedragging with left mouse button down. I use picking to get the id of the corresponding side of the single cube the user clicked on. This works also. So i click on a side of one cube on a face and depending on the direction of the dragging i set a rotation and offset factor of the cubes that get affected. (i also want to implement that u actually see the face rotate instead of just changing the color)
Now my Problem is that when i rotate the rubiks cube in any direction with right mouse dragging, it becomes upside down for example. So when i click on a side and want to rotate the face to the right, it´s going the wrong direction because i can´t keep track if the cube is upside down or whatever. Due to the use of the arcball rotation i dont have a x- or y-rotation angle which i could use to determine this.
Question 1: How can i keep track or later on get the information if the cube is upside down, tilted etc in order to translate the mouse dragging information(when rotating one face) when using the arcball example linked above?
// In render function
glPushMatrix();
{
glMultMatrixf(Transform.M); // Rotation applied by arcball object
complCube.draw(); // Draw all the cubes using display lists
}
glPopMatrix();
Setup: C++ with Microsoft Visual Studio 2008, GLEW, freeglut
You could use gluUnProject to convert mouse coordinates to 3d space and get a vector (difference between two points). This vector could then be used to apply a "force" to the selected face. Since gluUnProject uses the projection matrix, it would automatically deal with the orientation of the camera.
Basically, once you get your "force" vector, you project it onto the three axes (so onto (1,0,0), (0,1,0), (0,0,1)). Then choose the one with the largest magnitude. Then you have to convert this direction into a rotation axis as in the diagram below (sorry for the bad paint skills):
So what we have is the "force" vector in black and the selected rubiks face in grey. To get the rotation axis, just take the cross product the "force" vector with the normal of the selected face. This gives the red arrow. From that, you should be able to rotate your cubes in the right direction.
Edit to answer the question in more detail
So continuing from my explanation, I will give an example of how this will help you. Let's first assume your screen is 800x800 pixels and your rubiks cube is always centred. Now lets also assume that, as per your drawings in the comments, that we are in the case on the left.
We drag the mouse and get two positions which using gluUnProject are transformed into world coordinates (the numbers were chosen to show my point, not by any calculation):
p1 : (600, 600) -> (1, -0.5, 0)
p2 : (630, 605) -> (1.3, -0.505, 0)
Now we get the difference vector: p2 - p1 = v = (0.3, -0.05, 0). The reason that I was saying to "project onto the three axes" is so that you extract your major movement (which in this case is 0.3 in the x axis) (since the rubiks cube can't rotate along diagonals). To do the "projection" you just have to take the x, y, z axes individually and create vectors from them so you wind up with:
v1 = (0.3, 0, 0)
v2 = (0, -0.05, 0)
v3 = (0, 0, 0)
Now take the magnitudes and discard the smallest vectors, so we are left with the vector v1 = (0.3, 0, 0). This is your movement vector in world space. Now you take the cross product of that vector, with the normal vector of the selected face (which in this case would be (0, 0, 1)). This gives you a vector which points down (0, 1, 0) (after normalization) (in this step you will probably also have to extract the largest component only (0.02, 1.2, 0.8) -> (0, 1, 0) otherwise you would get bizarre rotations if your camera was not pointing directly along the main axes). You can now use that vector as the rotation axis and use 0.3 as your rotation amount (if it rotates in the opposite direciton to that expected, just put a -).
Now how does this help if your cube is upside down? Suppose we click on the screen in the same way. We now get:
p1 : (600, 600) -> (-1, 0.5, 0)
p2 : (630, 605) -> (-1.3, 0.505, 0)
See the difference in the world coordinates? They are inverted! So when you take the difference vector p2 - p1 = v = (-0.3, 0.05, 0). Extracting the largest component vector gives (-0.3, 0, 0). Doing the cross product once again gives you the rotation axis, but now the rotation is in the opposite direction, which is what you want.
Another reason for the cross product with the normal of the face is that if you were to select the faces on the top (in our drawings), then it would either give a rotation axis along the x or z axes (to the left, or into the screen) which is what you want for the top faces.
Like most of us, you will encounter the famous problem called Gimbal Lock.
see: http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=208925
This problem is extremely well documented so there is not much point for me to go into details here. I am sure you will find a ton of information about it.

OpenGL coordinates question

I have a simple OpenGL drawing. When the user changes the window's size, I want the drawing to maintain it's aspect ration. I accomplished that by setting the glViewport to the maximum rectangle with the appropriate aspect ration whenever the reshape method is called.
My problem is that I want to draw a square that will always remain in the top right corner of the window, no matter what the size or shape of the window is. Right now, that square moves around the screen whenever the window is reshaped.
Can anyone please explain how to do this?
Thank you,
You need to move/size the square when the screen is re-sized. You can fix a square to the top left by using device coordinates but it won't necessarily be square of the aspect ratio changes. Therefore you need to resize the square to keep it square.
One way of doing this would be to create a new ortho matrix that maps to pixel coordinates (left = 0, bottom = 0, right = window-width, top = window-height) and set the gl-viewport to cover the entire window whenever the window changes. That way, you can draw a square by specifying pixel coordinates, if you make sure you have an identity model-view matrix set up.
It's not the only way, though. No matter what non-singular transformation you have, you should be able to come up with a way of hitting the correct pixels as long as the gl-viewport covers those, it's just easier this way.
If I understand correctly, you wish to draw a square at the top right corner of the window, regardless of where your scene viewport is positioned.
The easiest way to do this is to, after having rendered your normal scene with desired aspect, change the gl viewport to the square you want to draw in the top corner. Then draw a "full-screen" quad to fill the square, with full-screen in-fact becoming full-viewport in this case.
Untested semi-pseudo code would go something like this:
// Draw normal scene
glViewport(x, y, w, h);
drawScene();
// Draw top-right red square
glViewport( windowWidth - squareWidth, windowHeight - squareHeight,
squareWidth, squareHeight );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glColor3f(1.f, 0.f, 0.f);
glRectf(-1.f, -1.f, 1.f, 1.f);
Making sure that the winding of the glRectf matches your current gl cull face configuration. Alternatively, just temporarily disable culling by glDisable(GL_CULL_FACE) / glEnable(GL_CULL_FACE).
To draw a square in the top-right corner of the window, you need the viewport to cover this area. Having a viewport smaller than the window won't allow drawing in the corner.
You want your viewport to cover all the window (as done usually), and your square coordinates should be mapped to something like:
X0 = 1-2*s/width
X1 = 1
Y0 = 1-2*s/height
Y1 = 1
where s is the side of the square (pix), and width, height the dimensions of the window (pix).