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.
Related
I added touch input for my game's character. The camera moves when a player's finger is dragged across the screen. However, when the joysticks are used to move, the camera doesn't move at the same time.
When I do the same code with Blueprints the code works and the camera still moves. When I drag the touch while pressing the joystick to move, I think that there is a problem in the ETouchIndex::Type. This is because in the BP when I use the finger index given in the touch event, it works. However, when I only use touch 1 as the finger index, it does not work. I think if I put that index in my CPP code it will work too, but where can I find the finger index? Can anyone please help me?
//here's my touch code that executes every tick.
FVector2D TouchLocation;
APlayerController* ActivePlayerController = UGameplayStatics::GetPlayerController(this, 0);
ActivePlayerController->GetInputTouchState(TouchType, TouchLocation.X, TouchLocation.Y, IsTouched);
if (!IsTouched)
{
DidOnce = false;
}
if (IsTouchMoved())
{
if (!DidOnce)
{
ActivePlayerController->GetInputTouchState(TouchType, PrevX, PrevY, IsTouched);
DidOnce = true;
}
ActivePlayerController->GetInputTouchState(TouchType, X, Y, IsTouched);
float FinalRotYaw = (X - PrevX) * UGameplayStatics::GetWorldDeltaSeconds(this) * 20;
float FinalRotPitch = (Y - PrevY) * UGameplayStatics::GetWorldDeltaSeconds(this) * 20;
AddControllerYawInput(FinalRotYaw);
AddControllerPitchInput(FinalRotPitch);
ActivePlayerController->GetInputTouchState(TouchType, PrevX, PrevY, IsTouched);
}
I am working on a MFC app which is a MDI. One of the child frame uses OpenGL(mixed with fixed function and modern version) called 3d view and another child frame uses GDI called plan view. Both of the views use the same doc.
The 3d view has a function to detect if the mouse cursor is over rendered 3d model by reading pixels and check its depth value.
The function is used for WM_MOUSEMOVE and WM_LBUTTONDOWN events. Most time it works pretty well. But it failed when I move my cursor from the plan view(currently active) to the 3d view and left mouse click. The depth values read from the pixels(called from onLButtonDown) are always all zeros though it is over a model. There is no OpenGL error reported. It only fails on the first mouse click when the 3d view is not activated. Afterwards, everything works well again.
The issue doesn't happen on all machines. And it happens to me but not to another guy with the same hardware machine with me. Is that possible hardware related or a code bug?
Things tried:
I tried to increase the pixel block size much bigger but depths are still all zero.
If I click on the title bar of the 3d view to activate it first, then works.
I tried to set the 3d view active and foreground in the onLButtonDown method before reading pixels. But still failed.(btw, the 3d view should be active already before the OnLButtonDown handler via other message handler fired by the left button down).
I tried to invalidate rect before reading pixels, failed too.
The code is as below:
BOOL CMy3DView::IsOverModel(int x0, int y0, int &xM, int &yM, GLfloat &zWin, int width0 , int height0 )
{
int width = max(1,width0);
int height= max(1,height0);
CRect RectView;
GetClientRect(&RectView);
GLint realy = RectView.Height() - 1 - (GLint)y0 ; /* OpenGL y coordinate position */
std::vector<GLfloat> z(width*height);
//Read the window z co-ordinates the z value of the points in a rectangle of width*height )
xM = max(0, x0-(width-1)/2);
yM = max(0, realy-(height-1)/2);
glReadPixels(xM, yM, (GLsizei)width, (GLsizei)height, GL_DEPTH_COMPONENT, GL_FLOAT, &z[0]); OutputGlError(_T("glReadPixels")) ;
/* check pixels along concentric, nested boxes around the central point */
for (int k=0; k<=(min(height,width)-1)/2; ++k){
for (int i=-k;i<=k;++i){
xM = x0+i;
for (int j=-k;j<=k;++j){
if (abs(i)==k || abs(j)==k) {
yM = realy+j;
zWin=z[(i+(width-1)/2)+width*(j+(height-1)/2)];
if (zWin<1.0-FLT_EPSILON) break;
}
}
if (zWin<1.0-FLT_EPSILON) break;
}
if (zWin<1.0-FLT_EPSILON) break;
}
yM = RectView.Height() - 1 - yM;
if (zWin>1.0-FLT_EPSILON || zWin<FLT_EPSILON) {// z is the depth, between 0 and 1, i.e. between Near and Far plans.
xM=x0; yM=y0;
return FALSE;
}
return TRUE;
}
Just found a solution for that: I called render(GetDC) before any processing in OnLButtonDown. somehow it fixed the issue though I don't think it's necessary.
InvalideRect wont fix the issue since it will update the view for the next WM_PAINT.
Weird, since it works for some machines without the fix. Still curious about the reason.
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.
I'm trying to detect horizontal mouse motion with OpenGL, so, when detected, execute a glutPostRedisplay(). Problem is that scene is also redrawed on vertical mouse movement.
This is the code of the registered callbacks (note mouse_inix and mouse_iniy are global (double) variables):
void mouse(int button, int state, int x, int y)
{
if (state == GLUT_DOWN) {
mouse_inix = (double)x;
mouse_iniy = (double)y;
}
}
void motion(int x, int y)
{
if (((double)x) != mouse_inix) {
angle += 20.0;
glutPostRedisplay();
}
}
Are you sure? It doesn't look like from the code you've posted that vertical mouse movement will trigger the glutPostRedisplay() call.
BUT, you've defined "horizontal mouse movement" very narrowly here. If you move the mouse up and down, you're almost sure to get a few pixels of horizontal movement. Maybe you could put a dead zone around the mouse to keep it from moving on every pixel. Something like:
void motion(int x, int y)
{
if ((abs(x - (int)mouse_inix) > 10) {
angle += 20.0;
glutPostRedisplay();
}
}
That's one thing that's going on here. Another is the use of "double". Since glut is returning mouse coordinates as ints, you're better off sticking with that. Trying to compare "(double)x != mouse_inix" will almost certainly be true because of the precision issues with doubles. You generally don't want to compare for exactly equal to or not equal to using floating point numbers. The use of the dead zone will negate that issue, but still, why convert to doubles if you don't need them?
I don't know if "20" is degrees or radians, but it could result in some pretty jumpy moves either way. Consider scaling the size of the move to the size of the mouse move:
void motion(int x, int y)
{
int deltaX = (abs(x - (int)mouse_inix);
if (deltaX > 10) {
angle += (double)deltaX; // or "deltaX/scaleFactor" to make it move more/less
glutPostRedisplay();
}
}
It will still only rotate one way. If you used the sign of "deltaX", you would be able to rotated both directions depending on how you moved the mouse.
In my application I need to return the relative mouse position from an SDL_Surface, the problem is the mouse position that gets returned is relative to the SDL window and not the SDL_Surface. I guess my question is what is the easiest / most effective way of doing this. Any questions just ask. Thanks.
EDIT: Sorry I should have explained better, I have SDL_Surface* Surf_Display; on Surf_display there is an Image say its 1000 x 1000, So in order to see the image on a 600 x 600 window I have a camera that I can move around ( really its the surface that moves not the camera ) for instance to look right of the image I move the surface -1 left if that makes sense. So my problem is when I click my mouse on a part of the surface(image) my mouse returns the position that the mouse compared to where the cursor is in the window, what i'm wanting is so that it returns the position of the cursor compared to where it is on the surface(image)
I hope that better explains the situation. Thanks again
Just add(or subtract, depending on how you look at it) the offset to the mouse coordinates. So you're drawing the surface something like this:
SDL_Rect dest_rect = { -camera.x, -camera.y };
SDL_BlitSurface(image_surface, NULL, screen_surface, &dest_rect);
I don't know if you're using event based mouse handling, or if you're using SDL_GetMouseState, but either way, you would simply add camera.x and camera.y to the mouse position, for example:
int x, y;
SDL_GetMouseState(&x, &y);
x += camera.x;
y += camera.y;