I'm programming a code that when the user click on the screen It marks a point and when he continues clicking on other points on the screen It'll be linking the points with lines.
I don't know why but I'm working with two different screen plan. When I click with the mouse It marks a point in the screen that I only see when I'm with the left mouse button clicked and when the button isn't clicked I see a clean screen.
My code that marks the points and link them with lines:
void exerciseThree(int x, int y){
write("Exercise Three", -5, 18);
float cx = 0, cy = 0;
if(x != 0 && y != 0 && isFinished == false){
glPushMatrix();
glPointSize(6.0f);
//Draw the points
glBegin(GL_POINTS);
cx = ((x * (maxx - minx)) / width) + minx;
cy = (((height - y) * (maxy - miny)) / height) + miny;
glVertex2f(cx , cy);
glEnd();
if(lastCx != 0 && lastCy != 0){
glBegin(GL_LINE_STRIP);
glVertex2f(lastCx , lastCy);
glVertex2f(cx , cy);
glEnd();
}
lastCx = cx;
lastCy = cy;
glPopMatrix();
}
write("Press 0 (zero) to come back.", -10, -18);
}
The mouse function:
void mouse(int button, int state, int x, int y){
switch(button){
case GLUT_LEFT_BUTTON:
if( option == 3){
exerciseThree(x, y);
projecao();
glutSwapBuffers();
}
break;
}
}
I know that I should handle the GLUT_DOWN and GLUT_UP of the mouse, but does exist a way of work with only one screen face?
You're only seeing something on the screen when you click because that's the only time you are drawing to and updating the buffer. You should just update the list of x/y coordinates when the mouse is clicked. However, you should draw the points and call glutSwapBuffers() every time in your main loop so that they are always on screen regardless of a button press.
The flow should be something like the following pseudo code:
ArrayOfPoints[];
while(Running)
{
CheckForMouseInput(); // Update array of points to draw if pressed
DrawPoints(); // Use same code draw code from exerciseThree()
glutSwapBuffers(); // Update video buffer
}
Related
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.
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?
I'm trying to read the mouse coordinate when I click in a point on screen and mark It with a point, but It'isn't working.
float cx = 0, cy = 0;
glPushMatrix();
glPointSize(6.0f);
//Draw the points
glBegin(GL_POINTS);
cx = (((x * (maxx + minx))) / width) + minx;
cy = (((-1 * pow((y - height), width) * (maxy - miny)) / height ) + miny;
glVertex2f(cx , cy);
glEnd();
glPopMatrix();
It's a college exercise, and the formula to get the coordinate from screen is:
Px and Py is the coordinate that mouseFunc pass to this function.
w and h is the width and height of the screen (I get It from reshape)
maxx, maxy, minx, miny... is the ortho coordinate
So, whats wrong with my code?
The mouse function (I've already tested if the mouse click is working and It is):
void mouse(int button, int state, int x, int y){
switch(button){
case GLUT_LEFT_BUTTON:
if(state == GLUT_DOWN)
exerciseThree(x, y);
break;
}
glutPostRedisplay();
}
You haven't implemented the formula you quoted correctly. Try
cx = x * (maxx - minx) / width + minx;
cy = (height - y) * (maxy - miny) / height + miny;
The two significant differences being (maxx - minx) in the first expression, and your misreading the second line and thinking the divisor w on the line above is a power for the second line.
The game is Minesweeper. The AI I am trying to implement will take an instance of the game Minesweeper that is already running on a machine (this case Windows 7), get the rectangular dimensions of the windows, compute the location of the window, push it to the foreground of the screen, click the top left square then go through the fancy algorithms to decide which square is a mine and which is clear.
Recently, I have been able to get the handling of the window and cursor correct only after implementing a MessageBox before the first tile is clicked.
With the message box, the program will push the Minesweeper window to the front, generate the message box, after the enter button on the message box is clicked the program will position the cursor on the correct top left square then stop... In the code after the message box, there is a commented out section which, when uncommented, iterates over the entirety of the Minesweeper window and marks it as a mine (flag!) - which tells me it does register the ClickTile correctly for mines - but not clear areas. After this the program is suppose to click the first tile in the top left corner. All that happens is the mouse flickering.
The correct behavior is to push the Minesweeper window to the front, generate the message box, after the message box is clicked, it should click the top left square within the game.
The question is, why is it not registering the LEFTMOUSEDOWN and only the RIGHTMOUSEDOWN in the ClickTile?
UiInfo.h
#ifndef UiInfo_
#define UiInfo_
#include <Windows.h>
struct UiInfo {
float scale;
POINT start;
int tile_size;
};
extern struct UiInfo * ui_info;
void UiInfo_initialize(struct UiInfo * ui_info,
float scale,
POINT start,
int tile_size);
#endif
UiInfo.c
#include "UiInfo.h"
void UiInfo_initialize(struct UiInfo * ui_info,
float scale,
POINT start,
int tile_size) {
ui_info->scale = scale;
ui_info->start = start;
ui_info->tile_size = tile_size;
}
main.c
void ClickTile(int is_mine, int x, int y) {
INPUT input = {0};
int xpos = (int)(ui_info->start.x + ui_info->tile_size * x);
int ypos = (int)(ui_info->start.y + ui_info->tile_size * y);
SetCursorPos(xpos, ypos);
input.type = INPUT_MOUSE;
if(is_mine) {
input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
}
else {
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
}
SendInput(1, &input, sizeof(INPUT));
ZeroMemory(&input, sizeof(INPUT));
input.type = INPUT_MOUSE;
if(is_mine) {
input.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
}
else {
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
}
SendInput(1, &input, sizeof(INPUT));
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
float scale;
POINT start;
RECT rect;
int size;
int x,y;
HWND hwnd_minesweeper = FindWindow(NULL, "Minesweeper");
GetWindowRect(hwnd_minesweeper, &rect);
scale = ((float)(rect.right - rect.left)) / ((float)(74 + 18 * 30));
// the point start is located at the center of the tile in the top left
// corner of the minesweeper map
start.x = ((long)scale) * (38 + 9) + rect.left;
start.y = rect.bottom - ((long)scale) * (38 + 15*18 + 9);
size = (int)scale * 18;
ui_info = (struct UiInfo *)malloc(sizeof(struct UiInfo));
UiInfo_initialize(ui_info, scale, start, size);
SetForegroundWindow(hwnd_minesweeper);
MessageBox(NULL, "A", "B", MB_OK);
/*for(y = 0; y < 16; y++) {
for(x = 0; x < 30; x++) {
Sleep(10);
ClickTile(1,x,y);
}
}*/
//Click the mine in the top left corner to start the game
ClickTile(0, 0, 0);
return 0;
}
I'll take three guesses.
Guess Number 1
You need to use a timer to allow your mouse movement and mouse clicks time to take effect. This involves starting a timer on app launch, and then implementing a state machine that generates new mouse events when the timer fires, e.g.
// start the timer in some initialization function
SetTimer( 75, 5, NULL );
// handler function to update the overall state
void CMainFrame::OnTimer(UINT nIDEvent)
{
switch ( m_State )
{
case STATE_IDLE: // do nothing until the user tells us to start
case STATE_DONE: // do nothing after the game is finished
break;
case STATE_START: // the first click to get things started
mouse_event( MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, m_Square[1][1].mousex, m_Square[1][1].mousey, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTUP , 0, 0, 0, 0 );
m_State = STATE_MOVING;
break;
case STATE_MOVING: // all the fun happens here
if ( !MakeAMove() )
{
m_State = STATE_DONE;
Invalidate();
}
break;
}
CFrameWnd::OnTimer(nIDEvent);
}
Guess Number 2
The mouse coordinate system is 65536 x 65536 regardless of screen resolution. So you need to scale the mouse coordinates to convert to pixel locations. Here's one way to compute the scale factors
HDC hdc = ::GetDC( NULL );
CDC *dc = CDC::FromHandle( hdc );
CWnd *wnd = dc->GetWindow();
wnd->GetClientRect( &rect );
xscale = 65535.0 / (double)rect.right;
yscale = 65535.0 / (double)rect.bottom;
Guess Number 3
You need to move the mouse to the correct screen coordinates before generating the mouse click event, e.g.
// the following code goes in some initialization function that knows the location of the minesweeper window
// note that screenx and screeny are the pixel coordinates of the center point of each square and these
// are converted to mouse coordinates for each square
for ( y = 1; y <= m_MaxY; y++ )
for ( x = 1; x <= m_MaxX; x++ )
{
m_Square[y][x].mousex = (int)(m_Square[y][x].screenx * xscale + 0.5);
m_Square[y][x].mousey = (int)(m_Square[y][x].screeny * yscale + 0.5);
}
// this code generates a left click in a given square
mouse_event( MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, m_Square[y][x].mousex, m_Square[y][x].mousey, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTUP , 0, 0, 0, 0 );
Like this question states - I want to convert a click (2D) into coordinates relating to the rendering on the screen. I have the function glutMouseFunc bound to my function (below) but can not make sense of the x and y that are passed in - they seem to be relating to distance in pixels from the top left corner.
I would like to know how to convert the x and y to world coordinates :)
void mouse(int button, int state, int x, int y) {
switch(button) {
case GLUT_LEFT_BUTTON:
printf(" LEFT ");
if (state == GLUT_DOWN) {
printf("DOWN\n");
printf("(%d, %d)\n", x, y);
}
else
if (state == GLUT_UP) {
printf("UP\n");
}
break;
default:
break;
}
fflush(stdout); // Force output to stdout
}
GLdouble ox=0.0,oy=0.0,oz=0.0;
void Mouse(int button,int state,int x,int y) {
GLint viewport[4];
GLdouble modelview[16],projection[16];
GLfloat wx=x,wy,wz;
if(state!=GLUT_DOWN)
return;
if(button==GLUT_RIGHT_BUTTON)
exit(0);
glGetIntegerv(GL_VIEWPORT,viewport);
y=viewport[3]-y;
wy=y;
glGetDoublev(GL_MODELVIEW_MATRIX,modelview);
glGetDoublev(GL_PROJECTION_MATRIX,projection);
glReadPixels(x,y,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,&wz);
gluUnProject(wx,wy,wz,modelview,projection,viewport,&ox,&oy,&oz);
glutPostRedisplay();
}
Where ox, oy, oz are your outputted values
http://hamala.se/forums/viewtopic.php?t=20
In Windowing system, origin (0,0) is at the Upper Left corner, but OpenGL world window origin (0,0) is at the Lower Left corner.
So you need to convert y coordinates only like such a way:
new_y = window_height - y;