Get mouse click coordinates in OpenGL during animation? - c

I want to make a game in which when somebody clicks on the moving ball, it bursts. I have added the codes for animation and the mouse click event, but when the animation is going on, the click function is not working. When I tried it without the animation, it worked properly. I want to know why is this happening.
#include<stdio.h>
#include<GL/glut.h>
#include<unistd.h>
#include<math.h>
int x, y;
float mx, my;
float i, j;
void mouse(int button, int state, int mousex, int mousey)
{
if(button==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
{
mx = mousex;
my = mousey;
printf("%f %f\n",mx,my);
glutPostRedisplay();
}
}
void init()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glColor3f(0.0, 1.0, 0.0);
glPointSize(1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, 1560, 0, 840);
}
int randValue()
{
int i = rand();
int num = i%1000;
return num;
}
void blast(int x, int y)
{
glBegin(GL_POLYGON);
glColor3f(1.0f,0.0f,0.0f);
glVertex2i(x-100, y-100);
glVertex2i(x, y-100);
glVertex2i(x-22, y-20);
glVertex2i(x-100, y-30);
glVertex2i(x-30, y-40);
glVertex2i(x-150, y-80);
glVertex2i(x-20, y);
glVertex2i(x, y-40);
glVertex2i(x-66, y-125);
glVertex2i(x-34, y-32);
glVertex2i(x-32, y-55);
glVertex2i(x-32, y);
glVertex2i(x-60, y-57);
glVertex2i(x-75, y-69);
glVertex2i(x-100, y);
glEnd();
glFlush();
}
void display()
{
int j = 0, k = 0, l = 1;
while(1)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 1.0, 0.0);
glBegin(GL_POINTS);
for (i = 0;i < 6.29;i += 0.001)
{
x = 100 * cos(i);
y = 100 * sin(i);
glVertex2i(x / 2 + j, y / 2 + k);
if((x / 2 + j) >= 1560 || (y / 2 + k) >= 840)
{
glEnd();
glFlush();
glClear(GL_COLOR_BUFFER_BIT);
blast(x / 2 + j, y / 2 + k);
sleep(2);
j = randValue();
k = 0;
}
}
j = j + 3;
k = k + 5;
glEnd();
glFlush();
}
}
int main (int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(1360, 768);
glutInitWindowPosition(0, 0);
glutCreateWindow("{Project}");
init();
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMainLoop();
}

Your code has an infinite loop inside the display function, thus you never give the control back to GLUT. GLUT already has an infinite loop like that inside glutMainLoop.
Instead you shall render only ONE frame in display, post glutPostRedisplay and return:
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
// ... draw the frame here ...
// for exmaple:
i += 0.001;
float x = 100 * cos(i);
float y = 100 * sin(i);
glColor3f(0.0, 1.0, 0.0);
glBegin(GL_POINTS);
glVertex2f(x, y);
glEnd();
glFlush();
glutPostRedisplay();
}
Then your mouse function will be called and you'll be able to update the state as necessary.

There are two problems here:
OpenGL has no support for an input device by itself, you normally use OpenGL to present information but you have something else attached to the window where you present the info that is what gives you mouse access. this involves to know which is the other environment you are using that offers you a pointing device into the screen area.
if you have the window mouse coordinates you need to map well on the window you present your OpenGL output, but you have to convert them back to some point in your scene, but probably your ball is not there. There's some ambiguity when passing from a plane image representing a 3D scene to a point in that scene in 3D, as you have all points in the Z axis sharing the same screen coordinates in 2D screen. so you have to trace back to the possible position of the ball from the point of view (the camera), based on the window coordinates of the mouse. This is a geometrical problem that involves the inverse transformation of a projection, that is always singular.
you can solve this without having to guess, as you know where your ball is, you can redo the transformation that made it to appear in the two dimensional window, and then compare coordinates based on those. OpenGL allows you to know the actual transformation it is doing to represent your scene, and you can use it to see where in the screen your ball is represented (you don't need to do this for every vertex of the ball, only for the center, for example) and then check if your shot has gone close enough to hit the ball. You should consider also if some other object upper in the Z axis is in the way, so you don't kill anybody behind a wall.

Related

Boundary fill incorrectly stops recursion?

I have been trying to execute a simple boundary fill program on my m1 mac using visual studio code where I had the setup, included all the libraries configuring default build task and the build is getting done nicely.
But the issue is when the window is visible, the program has a mouse click event listener, on click it should start region filling with desired color, but it seems to be stopping after drawing one line only.
Here is my program,
#include<stdio.h>
#include <GLUT/glut.h>
int xmin, ymin, xmax, ymax; //Polygon boundaries
float FillColor[3] = {1.0, 0.0, 0.0}; //Color to be filled - red
float BorderColor[3] = {0.0, 0.0, 0.0}; // Border color of polygon - black
void setPixel(int x, int y)
{
glBegin(GL_POINTS);
glColor3fv(FillColor);
glVertex2i(x, y);
glEnd();
glFlush();
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
//Drawing polygon
glColor3fv(BorderColor);
glLineWidth(6);
glBegin(GL_LINES);
glVertex2i(xmin, ymin);
glVertex2i(xmin, ymax);
glEnd();
glBegin(GL_LINES);
glVertex2i(xmax, ymin);
glVertex2i(xmax, ymax);
glEnd();
glBegin(GL_LINES);
glVertex2i(xmin, ymin);
glVertex2i(xmax, ymin);
glEnd();
glBegin(GL_LINES);
glVertex2i(xmin, ymax);
glVertex2i(xmax, ymax);
glEnd();
glFlush();
}
void BoundaryFill(int x,int y)
{
float CurrentColor[3];
glReadPixels(x, y, 1.0, 1.0, GL_RGB, GL_FLOAT, CurrentColor);
// if CurrentColor != BorderColor and CurrentColor != FillColor
if((CurrentColor[0] != BorderColor[0] && (CurrentColor[1]) != BorderColor[1] &&
(CurrentColor[2])!= BorderColor[2]) && (CurrentColor[0] != FillColor[0] &&
(CurrentColor[1]) != FillColor[1] && (CurrentColor[2]) != FillColor[2]))
{
setPixel(x, y);
BoundaryFill(x+1, y);
BoundaryFill(x-1, y);
BoundaryFill(x, y+1);
BoundaryFill(x, y-1);
//Using 4-connected approach, remove comment from below lines to make it 8-connected approach
BoundaryFill(x+1, y+1);
BoundaryFill(x+1, y-1);
BoundaryFill(x-1, y+1);
BoundaryFill(x-1, y-1);
}
}
void mouse(int btn, int state, int x, int y)
{
if(btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
printf("%d, %d\n", x, y);
BoundaryFill(x, 500-y);
}
}
void init()
{
glClearColor(0.101, 1.0, 0.980, 1.0); //Background color - cyan
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, 500, 0, 500);
}
int main(int argc, char** argv)
{
printf("Window size - 500x500 i.e. range of x and y is 0 -> 500\n");
printf("\nEnter polygon boundaries:-\n");
printf("Enter xmin: ");
scanf("%d", &xmin);
printf("Enter ymin: ");
scanf("%d", &ymin);
printf("Enter xmax: ");
scanf("%d", &xmax);
printf("Enter ymax: ");
scanf("%d", &ymax);
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutCreateWindow("Boundary-Fill Algorithm");
init();
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMainLoop();
return 0;
}
Please check the image -- where after this stage, the recursion stops which meant to be filling more pixels in 8 connected approach.
The orthographic projection covers an area of [0, 500]^2 pixels, whereas the algorithm assumes an area of [0, 500)^2 pixels. Note the difference between the inclusive range and the exclusive range.
This may cause glReadPixels to read the value of the pixel left (and/or down) of where you'd expect it to read, which breaks the algorithm. To solve this, use different dimensions for glOrtho2D:
gluOrtho2D(0, 499, 0, 499);
Additionally, note that the width and height parameters of glReadPixels should be integers, i.e.:
glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, CurrentColor);
and that it is not a good idea to compare float values directly (see elsewhere on StackOverflow). Instead, use something like this:
#include <math.h>
int isEqualFloat(float x, float y)
{
return fabs(x - y) < 0.001F;
}
I'm assuming the use of glReadPixels is here for educational purposes. A better approach would be to store a boolean matrix for "visited" pixels, which avoids the need for comparing colors in floating point values and retrieving data from GPU memory.
Lastly, using a recent Mac may have influence. For some low resolution program, the OS decides to repeat every pixel to avoid having a very small window. However, this effectively doubles the resolution without OpenGL knowing about it.

Getting the mouse position when ortho is set from -1.0 to 1.0

I'm new to OpenGL/GLUT using c. I want to implement a button that has a callback when a user clicks it. To understand that better, I have a simple program that draws a dot where the mouse is clicked.
Here's the code
#include <freeglut.h>
GLint mousePressed = 0;
GLfloat mouseX, mouseY;
GLint windowHieght = 400;
GLint windowWidth = 500;
void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
if (mousePressed)
{
// draw the dot
glBegin(GL_POINTS);
// draw the vertex at that point
glVertex2f(mouseX, mouseY);
glEnd();
}
glFlush();
}
void myMouseButton(int button, int state, int x, int y)
{
if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
exit(0);
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
mousePressed = 1;
mouseX = (GLfloat)x / (GLfloat)windowWidth;
mouseY = (GLfloat)windowHieght - (GLfloat)y;
mouseY = mouseY / (GLfloat)windowHieght;
glutPostRedisplay();
}
void main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB);
glutInitWindowSize(windowWidth, windowHieght);
glutInitWindowPosition(100, 150);
glutCreateWindow("dots");
gluOrtho2D(0.0, 1.0, 0.0, 1.0);
glutDisplayFunc(myDisplay);
glutMouseFunc(myMouseButton);
initializeGL();
glutMainLoop();
}
Everything works as expected, but when I change the ortho to (-1.0,1.0,-1.0,1.0), I don't get the same result. How can I get the same behavior?
Your myMouseButton function relies on the orthographic projection being 0-1 on both axes. Since you have changed that, you need to change the math in this function too.
To be very brief, your new orthographic co-ordinate range can be thought of as the old range scaled by 2 and then having 1 subtracted...
[0, 1] * 2 - [1, 1] => [-1, 1]
So you just need to do the same thing to your existing mouse co-ordinate equations.
mouseX = mouseX * 2.0f - 1.0f;
mouseY = mouseY * 2.0f - 1.0f;

Displaying in openGL based on a matrix

I am trying to draw some shapes in the openGL window. I draw these shapes based on the values in a particular matrix. I am using glut which has a function glutDisplayFunc that takes 1 parameter, a function callback taking no arguments and returns void. But I need to draw an image on the window based on a matrix which I cannot pass to the function callback.
This is an example code
#include<stdio.h>
#include<GL/glut.h>
#include<math.h>
#define pi 3.142857
void mat()
{
int a[2][2];
//
for(int i=0;i<2;i++)
for (int j = 0; j < 2; ++j)
{
scanf("%d",&a[i][j]);
}
}
// function to initialize
void myInit (void)
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glColor3f(0.0, 1.0, 0.0);
glPointSize(1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-780, 780, -420, 420);
}
void display (void)
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_POINTS);
float x, y, i;
for ( i = 0; i < (2 * pi); i += 0.001)
{
x = 200 * cos(i);
y = 200 * sin(i);
glVertex2i(x, y);
}
glEnd();
glFlush();
}
int main (int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
// giving window size in X- and Y- direction
glutInitWindowSize(1366, 768);
glutInitWindowPosition(0, 0);
glutCreateWindow("Circle Drawing");
myInit();
glutDisplayFunc(display);
glutMainLoop();
}
I need to be able to use the matrix a in function mat to define the center of 2 circles. How do I draw the window from within the mat function?
Edit:included code and fixed some typos
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
//-----------
float a[4][4] = {
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1 };
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glLoadMatrixf((float*)a);
//----------
glBegin(GL_POINTS);
float x, y, i;
for (i = 0; i < (2 * pi); i += 0.001)
{
x = 200 * cos(i);
y = 200 * sin(i);
glVertex2i(x, y);
}
glEnd();
glFlush();
}
In general you can load the current model view matrix, by setting the GL_MODELVIEW matrix mode (glMatrixMode), and loading the matrix by glLoadMatrixf.
Optionally the matrix can be multiplied to the current matrix by glMultMatrix.
But in both cases, the matrix has to be 4x4 Transformation matrix. The parameter to both functions is a pointer to an array of 16 floats respectively an 2 dimensional 4x4 float-array.
Init a 4x4 Identity matrix and read the upper left 2x2, to set up a rotation matrix around the z-axis:
Further, I recommend to read an rotation angle in degree and to calculate the rotation axis by the trigonometric functions sin respectively cos.
Finally read the xy translation components:
#define _USE_MATH_DEFINES
#include <math.h>
float a[4][4];
void mat()
{
// init identity matrix
for(int i = 0; i < 4; i++)
for (int j = 0; j < 4; ++j)
a[i][j] = (i==j) ? 1.0f : 0.0f;
// read the angle in degrees
float angle_degree;
scanf("%f", &angle_degree);
// convert the angle to radian
float angle_radiant = angle_degree * (float)M_PI / 180.0f;
// set rotation around z-axis
float cos_ang = cos(angle_radiant);
float sin_ang = sin(angle_radiant);
a[0][0] = cos_ang;
a[0][1] = -sin_ang;
a[1][0] = sin_ang;
a[1][1] = cos_ang;
// read translation
scanf("%f", &a[3][0]);
scanf("%f", &a[3][1]);
}
void display (void)
{
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&a[0][0]);
// [...]
}

OpenGL image disappears on selection if the OS is not Linux

I need to check if an object or an empty space has been clicked in OpenGL. I capture the click event in the MouseCallback function which passes the x and y click coordinates to ProcessSelection. The function is:
void MouseCallback(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
ProcessSelection(x, y);
}
Then I coordinates process the selection coordinates in ProcessSelection:
#define BUFFER_LENGTH 64
void ProcessSelection(int xPos, int yPos)
{
printf("xpos: %d\n", xPos);
printf("ypos: %d\n", yPos);
GLfloat fAspect; // Screen aspect ratio
GLuint selectBuff[BUFFER_LENGTH];
GLint viewport[4];
glSelectBuffer(BUFFER_LENGTH, selectBuff);
glGetIntegerv(GL_VIEWPORT, viewport);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glRenderMode(GL_SELECT);
glLoadIdentity();
gluPickMatrix(xPos, viewport[3] - yPos, 4, 4, viewport);
fAspect = (float)(viewport[2]) / (float)viewport[3];
gluPerspective(120, fAspect, 1, 300);
display();
// Restore the projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
hits = glRenderMode(GL_RENDER);
if (hits >= 1)
enable_animation = !enable_animation;
else
glutSetWindowTitle("You clicked empty space!");
}
The gluPerspective() is the exact same function that is called in the reshape function. I noticed that it works well in Linux but not in Windows and Mac OS X where the image disappears at every click. I can't understand why. Can someone help me?

Using the following function that draws a filled circle in opengl, how do I make it show at different coordinates of the window?

I have got the following code to draw a filled circle in opengl. The problem is that it draws at the center of the screen. How do I make it draw in another position of it?
Here is the code:
#define CIRCLE_RADIUS = 0.15f
int circle_points = 100;
void draw()
{
glClear(GL_COLOR_BUFFER_BIT);
double angle = 2* PI/circle_points ;
glPolygonMode( GL_FRONT, GL_FILL );
glColor3f(0.2, 0.5, 0.5 );
glBegin(GL_POLYGON);
double angle1 = 0.0;
glVertex2d( CIRCLE_RADIUS * cos(0.0) , CIRCLE_RADIUS * sin(0.0));
int i;
for (i = 0; i < circle_points; i++)
{
glVertex2d(CIRCLE_RADIUS * cos(angle1), CIRCLE_RADIUS *sin(angle1));
angle1 += angle ;
}
glEnd();
glFlush();
}
The obvious way would be to call glTranslate first. Note, however, that you can already accomplish the same a bit more easily with a combination of glPointSize and glPoint:
glPointSize(CIRCLE_RADIUS/2.0f);
glPoint(center_x, center_y, center_z);
Before you start drawing the circles, you'll want something like:
glEnable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
Otherwise, your "circles" could end up as squares.
Edit: Without knowing how you've set up your coordinates, it's impossible to know what the "top-left" position is, but you could do something like this:
void draw_circle(float x, float y, float radius) {
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(x, y, 0.0f);
static const int circle_points = 100;
static const float angle = 2.0f * 3.1416f / circle_points;
// this code (mostly) copied from question:
glBegin(GL_POLYGON);
double angle1=0.0;
glVertex2d(radius * cos(0.0) , radius * sin(0.0));
int i;
for (i=0; i<circle_points; i++)
{
glVertex2d(radius * cos(angle1), radius *sin(angle1));
angle1 += angle;
}
glEnd();
glPopMatrix();
}
You could then call (for example):
draw_circle(0.0f, 0.0f, 0.2f); // centered
draw_circle(1.0f, 0.0f, 0.2f); // right of center
draw_circle(1.0f, 1.0f, 0.2f); // right and up from center
Of course, the directions I've given assume haven't (for example) rotated your view, so x increases to the right and y increases upward.

Resources