GLFW has a function which does exactly what I need:
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
This function lets the mouse to move freely without being bounded to the window or the screen.
One solution I found is to reset the mouse position each frame using:
SDL_WarpMouseInWindow(window, defaultMousePositionX, defaultMousePositionY);
But I would still like to know If there is something like glfwSetInputMode() in SDL2.
If you require relative movement, FPS style, SDL has a SDL_SetRelativeMouseMode function as seen here. This forces SDL to only report motion events, so the mouse position doesn't change.
Another method would be to track the current and next positions yourself and manually calculating the differential, to get the distance moved.
// Last position
lastX = currX;
lastY = currY;
// Current position
currX = event.motion.x;
currY = event.motion.y;
// Motion
motionX = currX - lastX;
motionY = currY - lastY;
This could translate smoothly to other control methods too, like analogue sticks and touch controls, giving you a more uniform experience should you decide to use other platforms.
Related
Alright, so I am trying to use the analog stick on a gamepad to move the desktop mouse cursor around. The problem is that I need to be able to get the same smoothness as Attempt 2, but without using an absolute mouse position. The cursor needs to be moved relative to its current position. The reason for this is that many applications (mainly video games) also set the mouse to an absolute position. This causes the application and attempt 2 to fight one another for control of the mouse.
Attempt 1 (relative)
// keep updating the controller state and mouse position
while (true)
{
// getState returns a 2d float vector with normalized values from [-1, 1]
// The cursor is being set relative to its current position here.
SetCursorPosition(GetCursorPosition() + analogStick->getState());
}
This solution works, but suffers from a rounding issue because GetCursorPosition and SetCursorPosition are based on integers. As a result, small movements are not registered because smaller analog movements will always get truncated. Visually speaking, small movements on the analog stick will only move the mouse along the X or Y axis even if you are try to make a diagonal movement.
Attempt 2 (absolute)
vec2 mouseTargetPosition = GetCursorPosition(); // global cursor position
while (true)
{
mouseTargetPosition += leftStick->getState();
vec2 newPosition = lerp(GetCursorPos(), mouseTargetPosition, 0.8f);
SetCursorPos(round(newPosition.x), round(newPosition.y));
}
This solution works great, the mouse responds to the smallest of movements and moves very naturally as a result of interpolating the accumulated analog movements. But, it sets the mouse to an absolute position (mouseTargetPosition), making this solution a deal breaker.
This is an awfully specific question in the first place I suppose. After fooling around with several configurations this is the one that feels smoothest and works well. It's basically magic considering because it can add native feeling analog support for games and model viewers that don't have it :)
vec2 mouseTargetPos, mouseCurrentPos, change;
while (true)
{
// Their actual position doesn't matter so much as how the 'current' vector approaches
// the 'target vector'
mouseTargetPos += primary->state;
mouseCurrentPos = util::lerp(mouseCurrentPos, mouseTargetPos, 0.75f);
change = mouseTargetPos - mouseCurrentPos;
// movement was too small to be recognized, so we accumulate it
if (fabs(change.x) < 0.5f) accumulator.x += change.x;
if (fabs(change.y) < 0.5f) accumulator.y += change.y;
// If the value is too small to be recognized ( < 0.5 ) then the position will remain the same
SetCursorPos(GetCursorPos() + change);
SetCursorPos(GetCursorPos() + accumulator);
// once the accumulator has been used, reset it for the next accumulation.
if (fabs(accumulator.x) >= 0.5f) accumulator.x = 0;
if (fabs(accumulator.y) >= 0.5f) accumulator.y = 0;
}
I have been following the tutorials at http://opengl-tutorials.org and they are brilliant so far (I'm on a Mac so I am having to use OpenGL 3.2 and GLSL 1.50 rather than OpenGL 3.3 and GLSL 3.30). The only problem with the tutorials so far, is that with the 3D camera tutorial (Tutorial 6: Keyboard and Mouse), when I move my mouse, I do not get any rotation what so ever and if I do, it will only be slowly in the down direction; even if I move my mouse in any direction.
I have compiled the code given (OpenGL 2.1 and 3.x) as well as write it by hand, and this still presents this bug. I have not idea why this would happen. Could this be a bug with GLFW, Mac OS X or something else?
I know it's quite an old question, but I had the same issue so it might be of help. I have downloaded the code from the website and in common/controls.cpp this line was commented:
glfwSetMousePos(1024/2, 768/2);
Apparently there is a bug in GLFW with MacOS for which this instruction doesn't work properly. Hopefully they fixed it in the newer versions, but I haven't tested it yet.
On a side note, commenting this line will make the tutorial work, but you might experience some issues when clamping the vertical camera angle: if you move the mouse past the clamping point (say going up), the mouse position will keep updating and when you move the mouse down you will have to wait until it reaches the clamping point before the camera moves again.
[EDIT] Here's the full modified code
// Reset mouse position for next frame
// EDIT : Doesn't work on Mac OS, hence the code below is a bit different from the website's
//glfwSetMousePos(1024/2, 768/2);
// Compute new orientation
horizontalAngle = 3.14f + mouseSpeed * float( 1024/2 - xpos );
verticalAngle = mouseSpeed * float( 768/2 - ypos );
As GLFW3’s doc mention :
"The window must have input focus.”
So you should call glfwSetCursorPos until window have input focus ! It is not a bug in GLFW with MacOS !
The code should like that:
bool firstMouse = true;
float centerX = (float)mWidth / 2, centerY = (float)mHeight / 2;
GLFWwindow* mWindow;
void mouse_callback(GLFWwindow *window, double xPos, double yPos) {
if (firstMouse) {
// why here?
// As
// https://www.glfw.org/docs/3.3/group__input.html#ga04b03af936d906ca123c8f4ee08b39e7
// mention :
// "The window must have input focus."
glfwSetCursorPos(mWindow, centerX, centerY);
firstMouse = false;
}
// ...
}
void main() {
// ...
glfwSetCursorPosCallback(mWindow, mouse_callback);
// ...
}
There's still the same problem with GLFW 3 on OS X 10.9.
To disable the cursor and thus trap it inside the window I added this after the window is created.
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
Then I commented out //glfwSetCursorPos(window, 1024 / 2, 768 / 2); in controls.cpp
Now the mouse movement works. However, it seems like when viewing the cube from certain angle, the view is rotated when moving the mouse left and right while from other angle the view moves left and right as it should. Strange.
For GLFW2 on OSX add this line in your tutorial-XX.cpp after glfwCreateWindow( ...,...,)
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
and then create two global variables in common/controls.cpp
double xpos_old, ypos_old;
finally comment out the glfwSetCursorPos(window, 1024/2, 768/2); and change the calculation of the angles to the new setup, the idea is to get the difference between the old mousepos and the new. The final code should look like this:
// Reset mouse position for next frame
// glfwSetCursorPos(window, 1024/2, 768/2);
// Compute new orientation
horizontalAngle += mouseSpeed * float( xpos_old - xpos );
verticalAngle += mouseSpeed * float( ypos_old - ypos );
xpos_old = xpos;
ypos_old = epos;
This works great on my mac! cheers!
I'm using glut for a game right now and I'm trying to keep the mouse inside the window. This isn't a first person shooter so locking it in the center is no good. I already know about glutWarpPointer(int, int); and I've trying things that work (kinda).
I've tried having the mouse warp back to the nearest edge of the window when it leaves, this works, but for a split second you see the mouse outside of the window and teleport back in. I don't want that, I want it to seem like the mouse just hits the edge of the window and stops going any further in that direction, while keeping movement in any other available direction. Like you would expect it to work.
This is not exactly an answer to your question, but it is an answer to your problem!
Almost every game has its own cursors. They would hide the mouse, and draw the cursor manually where the mouse should be positioned.
If you get your own cursor image and do as I said, you can simply draw the curser at the edge of the screen, even though the mouse position reads out of boundaries. Then you can warp the mouse back in.
Tried to search and figure this out and couldn't find an answer, so I implemented it myself. Here is what worked for my first person camera case:
callback from glutPassiveMotion
CODE SAMPLE
void Game::passiveMouseMotion(int x, int y)
{
//of my code for doing the cam, yours is may be different, this is based on the example from https://learnopengl.com/Getting-started/Camera
if (firstMouse) {
lastX = x;
lastY = y;
firstMouse = false;
}
float xoffset = x - lastX;
float yoffset = lastY - y; // reversed since y-coordinates go from bottom to top
lastX = x;
lastY = y;
camera->ProcessMouseMovement(xoffset, yoffset);
glutPostRedisplay();
//this is the main thing that keeps it from leaving the screen
if ( x < 100 || x > win_w - 100 ) { //you can use values other than 100 for the screen edges if you like, kind of seems to depend on your mouse sensitivity for what ends up working best
lastX = win_w/2; //centers the last known position, this way there isn't an odd jump with your cam as it resets
lastY = win_h/2;
glutWarpPointer(win_w/2, win_h/2); //centers the cursor
} else if (y < 100 || y > win_h - 100) {
lastX = win_w/2;
lastY = win_h/2;
glutWarpPointer(win_w/2, win_h/2);
}
}
Hope this helps!
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;
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.