Mouse-drag object in OpenGL/GLUT [closed] - c++

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 9 years ago.
Improve this question
I have been searching all day for a tutorial or example code for a simple program - click on object (like a 2D rectangle for example) then as you hold and move the mouse the object follows the mouse, then on mouse release the object remains in new location. In other words, I want to understand how to drag and drop an object with the mouse events.
Could anyone help to point me in the right direction of any useful sources of information relating to this problem?

Thanks for all the responses so far.
I have worked out how to do it, so I will go ahead an answer my own question.
I am using GLUT as a mouse handler:
When the mouse is clicked and moving (glutMotionFunc) the drag function is called.
In the drag function the mouse coordinates (x,y) are converted to a Points struct while being converted into window coordinates.
If the mouse is within the square then drag the square by changing it's coordinates and redisplay.
I am still very new to OpenGL and C++ so I do apologize for the messy coding. I am a bit frustrated in doing it this way as the redrawn square makes it seem the cursor snaps to the center. I welcome alternative solutions to this problem and criticism of my code, for learning purposes.
CODE (included glut and using namespace std):
// points structure made of two coordinates; x and y
struct Points
{
float x,y; // initializor
Points() { x = 0.0; y = 0.0; } // constructor
Points(float _x, float _y) : x(_x), y(_y) {}
};
// square made of 4 points
class Square
{
public:
Points pts[4]; // square structure
Square(); // initialize constructor
void draw(Square *sqr); // draw square
Points mouse(int x, int y); // get mouse coordintaes
Square* drag(Square *sqr, Points *mouse); // change points of sqr
};
// square constructor
Square::Square()
{
pts[0] = Points(0.2,0.2);
pts[1] = Points(0.4,0.2);
pts[2] = Points(0.4,0.4);
pts[3] = Points(0.2,0.4);
};
// draw function
void Square::draw(Square *sqr)
{
// draw square fill
int i;
glColor3f(0.2, 0.2, 0.2);
glBegin(GL_QUADS);
for (i = 0; i < 4; ++i)
{
glVertex2f(sqr->pts[i].x, sqr->pts[i].y);
}
glEnd();
// draw square points
i = 0;
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_POINTS);
for (i = 0; i < 4; ++i)
{
glVertex2f(sqr->pts[i].x, sqr->pts[i].y);
}
glEnd();
}
// mouse function
Points Square::mouse(int x, int y)
{
int windowWidth = 400, windowHeight = 400;
return Points(float(x)/windowWidth, 1.0 - float(y)/windowHeight);
}
// drag function
Square* Square::drag(Square *sqr, Points *mouse)
{
sqr->pts[0].x = mouse->x - 0.1;
sqr->pts[0].y = mouse->y - 0.1;
sqr->pts[1].x = mouse->x + 0.1;
sqr->pts[1].y = mouse->y - 0.1;
sqr->pts[3].x = mouse->x - 0.1;
sqr->pts[3].y = mouse->y + 0.1;
sqr->pts[2].x = mouse->x + 0.1;
sqr->pts[2].y = mouse->y + 0.1;
return sqr;
}
// GLOBAL
// create square object
Square* sqr = new Square;
// display at start
void display() {
glClear(GL_COLOR_BUFFER_BIT);
sqr->draw(sqr);
glFlush();
}
// drag function
void drag (int x, int y)
{
// int x and y of mouse converts to screen coordinates
// returns the point as mousePt
Points mousePt = sqr->mouse(x,y);
//create pointer to window point coordinates
Points* mouse = &mousePt;
// if the mouse is within the square
if (mouse->x > sqr->pts[0].x && mouse->y > sqr->pts[0].y)
{
if (mouse->x < sqr->pts[2].x && mouse->y < sqr->pts[2].y)
{
// then drag by chaning square coordinates relative to mouse
sqr->drag(sqr,mouse);
glutPostRedisplay();
}
}
}
void Initialize() {
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
}
int main(int iArgc, char** cppArgv) {
glutInit(&iArgc, cppArgv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(400, 400);
glutInitWindowPosition(200, 200);
glutCreateWindow("Move Box");
glutMotionFunc(drag);
Initialize();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}

OpenGL is only concerned with the drawing process. Everything else (mouse input, object picking, scene management/alterations, etc.) is completely up to you to implement.
Here's a rough outline:
Install a mouse click event handler (the exact method to use depends on the framework used and/or the operating system)
In the mouse click event handler perform a picking operation. This usually involves unprojecting the mouse window position into the world space (see gluUnproject) resulting in a ray. Test each object in the scene if it intersects with the ray; you'll have to implement this yourself, because OpenGL just draws thing (there is no such thing as a "scene" in OpenGL).
If a object has been picked register it to be manipulated in the mouse drag handler
everytime a mouse drag event happens adjust the object's position data and trigger of the OpenGL display (you always redraw the whole thing in OpenGL).
When the mouse is released unregister the object from the drag handler.

As mentioned by others, OpenGL does not handle user input. You want to use a library for that. If you want a more all-around solution, you can even use a more complete render or physics engine.
For simple user input, you can use SDL (e.g. this is for mouse input).
For more complete 2D stuff, you can just use Box2D. Here are a whole bunch of tutorials.
The heavy-weight solution is a complete render engine, such as Ogre3D or CrystalSpace.

As mentioned by others, you need to get a mouse handler to get the mouse position first. Then you need a way to pick an object. You have a few options to do the picking in OpenGL.
If you are using classic OpenGL, you can use the select buffer. The following link is a good tutorial
http://www.lighthouse3d.com/opengl/picking/index.php3?openglway
If you are using modern opengl, which is shader based, you can use FBO based picking.
http://ogldev.atspace.co.uk/www/tutorial29/tutorial29.html
You can always implement a ray tracking picking yourself in both cases. The gluUnproject can help a lot in the implementation.
http://schabby.de/picking-opengl-ray-tracing/
After that, you just need to update the object position according to the mouse movement or acceleration.

Related

Change Cursor Image While Hovering Over An Object Drawn At OpenGL And Displaying Object's Variables

I am currently working on a project that I use C++, OpenGL, Qt 5.9.2 and Microsoft Visual Studio Professional 2015 on a 64 bit Operating System, Windows 10 Pro.
I have a user interface that I have created and in that user interface, there is a QGLWidget, that I am using for draw processes, with other widgets like push buttons and a dockWidget. I have a class Ball and it has variables(distance(double) and angle(int)) that determines where an instance of that Ball is going to be drawn inside the QGLWidget. Ball class has got 2 more variables that is, id(int), model(String) and year(int) Every other draw process draws lines except Ball drawings.
Drawings are 2 dimensional.
Every Ball has the same color(rgb)!
First problem: I want to left click to one of the Ball instances and I want to display it's id, model and year at The dockWidget.
Second Problem: While doing the stuff that I have mentioned at the First Problem section. I want the cursor image to change while hovering above any of the Ball instances, and change back to default Windows mouse cursor while not.
I have created a function that checks if the MouseEvent is LeftClick:
void DisplayManager::mousePressEvent(QMouseEvent* ev) {
if (ev->buttons() & Qt::LeftButton) { // Balls Are Green
if(// CHECK IF THERE IS A BALL AT THE CLICKED COORDINATES) {
// DISPLAY THE X and Y OF THE BALL AT THE DOCK WIDGET
}
}
}
This is my initializeGL function: (DisplayManager is the name of my QGLWidget)
void DisplayManager::initializeGL() {
glEnable(GL_COLOR_MATERIAL); // Enables the changing of the draw color with glColor() functions
glColor3f(0.0, 1.0, 0.0);
glEnable(GL_DEPTH_TEST);
glClearColor(0, 0, 0, 1); //sets a black background 1 0 0 1
}
On the basis this is a Picking problem and there are several information about it at the internet but I am not using GLUT and I am not using any shader. So in the light of all these I was unable to find any effective solution or clue about how can I accomplish all that I want.
I would be really glad if someone could help me with at least one of these problems.
I have currently finished working with the project. I thought that I should provide an answer to my question in case someone with a similar problem comes across with my question in the future.
void DisplayManager::mousePressEvent(QMouseEvent* ev) {
// The processes that are being executed after a left mouse button click on the DisplayManager.
if (ev->buttons() & Qt::LeftButton) {
double aspectRatio = openGLWidgetWidth / openGLWidgetHeight;
int xOfClicked = ev->x() - (openGLWidgetWidth / 2);
int yOfClicked = - (ev->y() - (openGLWidgetHeight / 2));
// The variable to be used for calculating fault tolerance of the click event.
int distanceBetweenPressAndDraw = 0;
// Executes the processes inside for every ball in vector.
for (int i = 0; i < ballVector.length(); i++) {
// Calculates the screen coordinates of the i'th ball.
int rangeOfBallInScreenDistance = rangeToScreenDistance(ballVector.at(i).range);
double screenXOfBall = rangeOfBallInScreenDistance * sin(ballVector.at(i).degree * DEGTORAD);
double screenYOfBall = rangeOfBallInScreenDistance * cos(ballVector.at(i).degree * DEGTORAD);
// Calculates the distance between pressed position and the i'th ball according to the screen coordinates.
distanceBetweenPressAndDraw = sqrt(pow((screenXOfBall - xOfClicked), 2) + pow((screenYOfBall - yOfClicked), 2));
// Decides if the clicked position is a ball (considering the fault tolerance).
if (distanceBetweenPressAndDraw < 10) {
emit printXY(QPointF(xOfClicked, yOfClicked)); // Prints the screen coordinates of the clicked positions (At the Dock Widget inside Main Window).
}
}
}
}
This was the solution for my First Problem. I would be glad though if someone could answer my Second problem in a comment or answer somehow.

OGLFT draws text when GLStipple is used

I have an interesting bug that has been "bugging" me for a few days now.
I am currently using OpenGL to draw text on a screen. I am utilizing the OGLFT library to assist the drawing. This library actually uses the freetype2 library. I am actually not doing anything special with the text. I am only looking for monochromatic text.
Anyways, after implementing the library, I noticed that the text is only drawn correct when I have glStipple enabled. I believe that there is some interference issue between the OGLFT library and what I am enabling.
I was wondering if there is anyone out there with some experience on using the OGLFT library. I am posting a minimalist example of my code to demonstrate what is going on:
(Please note that there are some variables that are used to st the zoom factor of my glCanvas and the position of the camera and that this is only for 2D)
double _zoomX = 1.0;
double _zoomY = 1.0;
double _cameraX = 0;
double _cameraY = 0;
/* This function gets called everytime a draw routine is needed */
void modelDefinition::onPaintCanvas(wxPaintEvent &event)
{
wxGLCanvas::SetCurrent(*_geometryContext);// This will make sure the the openGL commands are routed to the wxGLCanvas object
wxPaintDC dc(this);// This is required for drawing
glMatrixMode(GL_MODELVIEW);
glClear(GL_COLOR_BUFFER_BIT);
updateProjection();
OGLFT::Monochrome *testface = new OGLFT::Monochrome( "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", 8);
testface->draw(0, 0, "test");
glEnable(GL_LINE_STIPPLE);// WHen I comment out this line, the text is unable to be drawn
glLineStipple(1, 0b0001100011000110);
glBegin(GL_LINES);
glVertex2d(_startPoint.x, _startPoint.y);
glVertex2d(_endPoint.x, _endPoint.y);
glEnd();
glDisable(GL_LINE_STIPPLE);
SwapBuffers();
}
void modelDefinition::updateProjection()
{
// First, load the projection matrix and reset the view to a default view
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-_zoomX, _zoomX, -_zoomY, _zoomY, -1.0, 1.0);
//Reset to modelview matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0, 0, (double)this->GetSize().GetWidth(), (double)this->GetSize().GetHeight());
/* This section will handle the translation (panning) and scaled (zooming).
* Needs to be called each time a draw occurs in order to update the placement of all the components */
if(_zoomX < 1e-9 || _zoomY < 1e-9)
{
_zoomX = 1e-9;
_zoomY = _zoomX;
}
if(_zoomX > 1e6 || _zoomY > 1e6)
{
_zoomX = 1e6;
_zoomY = _zoomX;
}
glTranslated(-_cameraX, -_cameraY, 0.0);
}
Also one thing to note is that the code below the glEnable(GL_LINE_STIPPLE); is required. It is as if the glStipple needs to be drawn correctly for the text to be displayed correctly.
Looking through your code, I believe that your intention is to render it as a greyscale? If so, then you can simply use the OGLFT::Grayscale *testface = new OGLFT::Grayscale( "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", 8);
This will get what you need without having to worry about the issue that you posted. In fact, I recommend doing it this way too.

OpenGL draw circle with Mouse Move

I am trying to use the function mouseMove(int x, int y) to draw a circle centered at my mouse as I click and drag it across the screen. Circles will be drawn on the moving mouse like a spray paint. So far, this is what I have
void mouseMove(int x, int y) {
glBegin(GL_POLYGON);
for (int i = 0; i <= 360; i++)
{
float theta = (2 * 3.14 * i) / 360;
glVertex2f((size/2 + x) * cos(theta), (size/2 + y) * sin(theta));
}
glEnd();
glutPostRedisplay();
}
But when using this, it draws very large circles that aren't centered around my mouse. How would I alter this to make the program draw circles centered at my mouse?
To describe the project, I am creating a painting program that changes shapes, colors, sizes, and rotations of the drawing done in mouseMove. For now, the size is an int set to 32. When the user selects the shape using the 'b' key in a keyboard function, he/she can switch the shapes that are drawn around the mouse as the user clicks and drags the mouse around. Like a spray paint. All the other shapes work shaped around the mouse except for the circle shape spray.
This answer assumes that things like your viewport and projection matrices are set up correctly, and that the input to this function is taking into account the fact that "screen coordinates" (what the mouse uses) are not the same thing as "OpenGL Coordinate Space" (this usually implies reversing the direction of the y-axis for one or the other).
The math you're using for setting your vertex coordinates is wrong. The mouse's x and y coordinates should not be multiplied by the sine/cosine functions.
The correct way to write it is
glVertex2f((size/2) * cos(theta) + x, (size/2) * sin(theta) + y);
I would also add that you appear to still be using OpenGL's Immediate Mode rendering, which is Deprecated and will offer extremely poor training for a professional setting. I highly advise you learn Modern OpenGL (3.x+) and reapply those concepts to whatever projects you're already working on. This is a very good tutorial.

Maintaining Aspect Ratio and Scale Independent of Window Size with freeglut

I've been wanting to experiment with platforming physics using freeglut, but before I would allow myself to start, I had an old problem to take care of.
You see, I want to write a reshape handler that not only maintains the scale and eliminates any distortion of the view, but also allows all of the onscreen shapes to maintain their size even while the window is too small to contain them (i.e. let them be clipped).
I've almost got all three parts solved, but when I scale my window, the circle I have drawn onto it scales just slightly. Otherwise, I got the clipping, and I have eliminated the distortion. Update: What I want to achieve is a program that maintains scale and aspect ratio independent of window size.
Here's my code:
void reshape(int nwidth,int nheight)
{
glViewport(0,0,nwidth,nheight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//here begins the code
double bound = 1.5;
double aspect = double(nwidth)/nheight;
//so far, I get the best results by normalizing the dimensions
double norm = sqrt(bound*bound+aspect*aspect);
double invnorm = sqrt(bound*bound+(1/aspect)*(1/aspect));
if(nwidth <= nheight)
glOrtho(-bound/invnorm,bound/invnorm,-bound/aspect/invnorm,bound/aspect/invnorm,-1,1);
else
glOrtho(-bound*aspect/norm,bound*aspect/norm,-bound/norm,bound/norm,-1,1);
//without setting the modelview matrix to the identity form,
//the circle becomes an oval, and does not clip when nheight > nwidth
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
Update: As per Mr. Coleman's suggestion, I've tried switching out single precision for double. The scaling issue has improved along the vertical axis, but whenever I drag the horizontal axis in either direction, the shape still scales by a noticeable amount. It's still the same shape throughout, but a visual inspection tells me that the shape is not the same size when the window is 150x300 as it is when the window is 600x800, regardless of which glOrtho is being executed.
I've got it. Here's how I changed my code:
//at the top of the source file, in global scope:
int init_width;//the initial width
int init_height;//the initial height
void reshape(int new_width, int new_height)
{
//moved the glViewport call further down (it was part of an earlier idea that didn't work out)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();//these two lines are unchanged
double bound = 1.0; //I reduced the edge distance to make the shape larger in the window
double scaleX = double(new_width)/init_width;
double scaleY = double(new_height)/init_height;
glOrtho( -bound*scaleX/2, bound*scaleX/2, //these are halved in order to un-squash the shape
-bound*scaleY, bound*scaleY, -1,1 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0,0,new_width,new_height);
}
That is what my code looks like now. It maintains the scale and shape of what I have on screen, and allows it to go offscreen when the window is too small to contain the entire shape.

Trapping the mouse?

I'm using GLUT and developing a FPS game. I need a way to trap the mouse so that the camera continues to move because right now when the mouse position exceeds the monitor limit, there is no way to calculate change in X or change in Y. How can I 'trap' the mouse with GLUT?
Thanks
I'd recommend using a ready-made engine like OGRE 3D instead, but if you really want to reinvent the wheel, here's how...
In all cases I'm aware of, PC FPS games "trap" the pointer by registering a mouse motion callback, noting the relative motion, and then warping the pointer back to the center of the window.
Here's some code I wrote to add mouse input to a sample ping-pong table in an OpenGL with C++ course a year or two ago:
void resetPointer() {
glutWarpPointer(TABLE_X/2, TABLE_Y/2);
lastMousePos = TABLE_Y/2;
}
void mouseFunc(int sx, int sy) {
if (!started) { return; }
int vertMotion = lastMousePos - sy;
lastMousePos = sy;
player1.move(vertMotion);
// Keep the pointer from leaving the window.
if (fabs(TABLE_X/2 - sx) > 25 || fabs(TABLE_Y/2 - sy) > 25) {
resetPointer();
}
}
// This goes in with your "start new game" code if you want a menu
resetPointer();
glutSetCursor(GLUT_CURSOR_NONE);
glutPassiveMotionFunc(mouseFunc);
It only tracks vertical motion, but adding horizontal is trivial.