I used the glfw callback function to move the camera with the mouse.
mouse callback function is:
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn)
{
if (is_pressed)
{
camera.ProcessMouseMovement((static_cast<float>(yposIn) - prev_mouse.y) / 3.6f, (static_cast<float>(xposIn) - prev_mouse.x) / 3.6f);
prev_mouse.x = xposIn;
prev_mouse.y = yposIn;
}
cur_mouse.x = xposIn;
cur_mouse.y = yposIn;
}
void mouse_btn_callback(GLFWwindow *window, int button, int action, int mods)
{
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
{
prev_mouse.x = cur_mouse.x;
prev_mouse.y = cur_mouse.y;
is_pressed = true;
}
else
{
is_pressed = false;
}
}
However, in this case, the camera will move even when operated in other imgui windows as shown below.
I don't know how to handle this.
Should I put this logic between begin and end of IMGUI, using something like ImGui::IsWindowHovered()?
like this:
ImGui::Begin("scene");
{
if(ImGui::IsWindowHovered())
{
//camera setting
}
}
ImGui::End()
I had the same problem today.
For anyone seeing this now, you have to define your glfw callbacks before initializing ImGui. ImGui sets its own callbacks up at this point and handles sending inputs to already existing ones, if not consumed before. If you define your callbacks afterwards you overwrite those created by ImGui.
Answer above are wrong.
This is answered in the Dear ImGui FAQ:
https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-how-can-i-tell-whether-to-dispatch-mousekeyboard-to-dear-imgui-or-my-application
TL;DR check the io.WantCaptureMouse flag for mouse.
I'm not familiar with ImGui, so I don't know what functions might or might not need to be called in ImGui.
But, GLFW is a relatively low level windowing API that has no regard for the higher level abstractions that might exist on the window. When you pass the callback to glfwSetCursorPosCallback, that callback will be called on any accessible part of the window.
If you need to have the mouse movements (or any mouse interactions) only respond when the mouse is hovered over the relevant part of the interface, you need some kind of mechanism to define what that part is. Again: I don't know how you'd do that in ImGui, but it'll probably look something like this:
void mouse_callback(GLFWwindow *window, double xposIn, double yposIn)
{
//Structured Binding; we expect these values to all be doubles.
auto [minX, maxX, minY, maxY] = //Perhaps an ImGui call to return the bounds of the openGL surface?
if(xposIn < minX || xposIn > maxX || yposIn < minY || yposIn > maxY) {
return; //We're outside the relevant bounds; do not do anything
}
//I'm assuming setting these values should only happen if the mouse is inside the bounds.
//Move it above the first if-statement if it should happen regardless.
cur_mouse.x = xposIn;
cur_mouse.y = yposIn;
if (is_pressed)
{
camera.ProcessMouseMovement((static_cast<float>(yposIn) - prev_mouse.y) / 3.6f, (static_cast<float>(xposIn) - prev_mouse.x) / 3.6f);
prev_mouse.x = xposIn;
prev_mouse.y = yposIn;
}
}
Related
I am currently writing a gtk program that uses a custom title bar (i.e., it is not being decorated by the window manager). But since using a custom title bar also disables support of dragging the window around, I wrote my custom drag function which moves the window by calling window.move(x, y):
bool on_titlebar_drag(GdkEvent* event)
{
static int drag_x_offset = 0;
static int drag_y_offset = 0;
int x, y;
if (event->type == GDK_BUTTON_PRESS)
{
drag_x_offset = event->button.x;
drag_y_offset = event->button.y;
} else if(event->type == GDK_BUTTON_RELEASE) {
drag_x_offset = 0;
drag_y_offset = 0;
} else if(event->type == GDK_MOTION_NOTIFY) {
x = event->motion.x_root - drag_x_offset;
y = event->motion.y_root - drag_y_offset;
mainWindow.move(x, y);
}
return true;
}
This works just fine except of the fact that it cannot move the window beyond the screen limits, like the normal behaviour for other windows, so you can drag it "out of sight" to make place for others.
I am trying to resize the window smaller as soon as it touches the screen by calling window.resize(width, height) but this is not what I intend to do, because resizing also resizes the window contents to the smaller scale, while I would just like to make its physical size smaller.
I have also tried using set_allocation and size_allocate, these two didnt make any change at all.
My question is, do you know a way to either be able to move the window beyond the screen borders (not totally, but in a way that the window is not fully on-screen), or to change the size of the window without resizing its contents?
If you are using GTK 3.10 or newer you can use gtk_window_set_titlebar
gtk_window_set_titlebar (GtkWindow *window, GtkWidget *titlebar) the only arguments you need are the window that you want to customise and the GtkWidget that will serve as the titlebar. Using GtkHeaderBar is suggested but in your case you can use any custom GtkWidget and get draggable bar which will tug the whole window as would the one from the window manager.
I need your piece of advice. I'm using SFML and I need to play animation from the spritesheet(f.e. 64 frames and 40px width/height of each frame) after mouseclick event. The only solution I've come to is:
if (event.type == sf::Event::MouseButtonPressed) {
if (event.key.code == sf::Mouse::Left) {
float frame = 0;
float frameCount = 64;
float animSpeed = 0.005;
while (frame < frameCount) {
spriteAnimation->setTextureRect(sf::IntRect(int(frame)*w, 0, w, w));
frame += animSpeed;
window->draw(rect); // clear the area of displaying animation
window->draw(*spriteAnimation);
window->display();
}
...
But calling window->display() so many times is really not good;
Can you suggest better variants?
Instead of jamming all of your code for the animation into the event block you should spread it out.
Your design here is very inflexible in that if you ever want to display anything other than an animation you are going to have to call window->display() again outside of your event loop.
Generally in SFML your game loop proceeds similarly to as follows:
initialize();
while(running)
{
checkEvents();
clear();
update();
display();
}
Instead of performing all of the calculations and displaying for your animation inside the event's if statement you should set a bool or call a doAnimation() function of some sort. I've written a rough example below:
bool doAnimation = 0;
//declare frame, framespeed, etc
if (event.type == sf::Event::MouseButtonPressed)
{
if (event.key.code == sf::Mouse::Left)
{
doAnimation = true;
//reset frame, framespeed, etc
}
}
clear();
if(doAnimation)
{
sprite->setTexture(...);
if(frame == endFrame)
{
doAnimation = 0;
}
drawSprite();
}
window->display();
There are tons of ways to solve your problem. My example is less flexible than I would think is ideal but depending on the needs of your program it may work fine. If you wanted to take the next step moving the animation into a class of some sort would make your life a lot easier in the long run.
Hi so I'm trying to make it so a little UFO bitmap (drawing/painting already taken care of) can be dragged around the screen. I can't seem to make the UFO position update and then redraw repeatedly from the MouseButtonDown() function (simplified code for mouse event handler). Any suggestions on detecting the dragging and redrawing accordingly? Code is below for relevant functions:
void MouseButtonDown(int x, int y, BOOL bLeft)
{
if (bLeft)
{
while(_bMouseMoving == true && _bMouseDragRelease == false) {
_iSaucerX = x - (_pSaucer->GetWidth() / 2);
_iSaucerY = y - (_pSaucer->GetHeight() / 2);
InvalidateRect(_pGame->GetWindow(), NULL, FALSE);
}
// Set the saucer position to the mouse position
_iSaucerX = x - (_pSaucer->GetWidth() / 2);
_iSaucerY = y - (_pSaucer->GetHeight() / 2);
}
else
{
// Stop the saucer
_iSpeedX = 0;
_iSpeedY = 0;
}
}
void MouseButtonUp(int x, int y, BOOL bLeft)
{
_bMouseDragRelease = true;
}
void MouseMove(int x, int y)
{
_bMouseMoving = true;
}
To clarify what chris said, you're only going to get the WM_xBUTTONDOWN message once, and you'll need to use that to toggle a dragging state that you can query when you recieve a WM_MOUSEMOVE message.
When you get the mouse move message during a dragging state, you'll want to invalidate the rectangle surrounding where the ufo was, and the rectangle surrounding where it is.
Invalidating a rectangle causes WM_PAINT messages, where you redraw whatever was behind the ufo, and the ufo in it's new place.
Or you could cheat and make the UFO a cursor when you're dragging :)
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.
How can I get the x,y coordinate of a mouse click, to see if it is over my menu button drawn by directx? Currently, my codebase has the following mouse-related class that doesn't seem to be able to give me this..I'm not sure how this might work.
InputMouse::InputMouse() :
m_LastX(-1),
m_LastY(-1)
{
m_MouseActionEvent.clear();
}
InputMouse::~InputMouse()
{
}
void InputMouse::PostUpdate()
{
m_CurrentAction.clear();
}
bool InputMouse::IsEventTriggered(int eventNumber)
{
for (unsigned int i = 0; i < m_CurrentAction.size(); i++)
{
if (m_MouseActionEvent.size() > 0 && m_MouseActionEvent[m_CurrentAction[i]] == eventNumber)
{
return true;
}
}
return false;
}
void InputMouse::AddInputEvent(int action, int eventNumber)
{
m_MouseActionEvent[action] = eventNumber;
}
void InputMouse::SetMouseMouse(int x, int y)
{
if (m_LastX != -1)
{
if (x > m_LastX)
{
m_CurrentAction.push_back(MOUSE_RIGHT);
}
else if (x < m_LastX)
{
m_CurrentAction.push_back(MOUSE_LEFT);
}
if (y > m_LastY)
{
m_CurrentAction.push_back(MOUSE_UP);
}
else if (y < m_LastY)
{
m_CurrentAction.push_back(MOUSE_DOWN);
}
}
m_LastX = x;
m_LastY = y;
}
DirectX or not, GetCursorPos is going to retrieve the position of the mouse in screen co-ordinates. ScreenToClient will map the screen relative point to a point relative to the client area of your window/directX surface.
If your menu buttons are 2D, this should be as simple as remembering the screen co-ordinates used for your buttons.
If you're trying to determine if a click lands on a 3D object that's been rendered, then the technique you are looking for is called Picking.
A simple Google for "directx picking" comes up with some good results:
http://www.mvps.org/directx/articles/rayproj.htm
http://www.gamedev.net/community/forums/topic.asp?topic_id=316274
Basically, the technique involves converting the mouse click into a ray into the scene. For your menu items, a simple bounding box will probably suffice for determining a 'hit'.
Once an object is drawn, the system has no knowledge of which pixels on the screen it changed, nor do those pixels know which object or objects changed it (nor if those objects even still exist). Therefore, if you need to know where something is on-screen, you have to track it yourself. For buttons and other GUI elements this usually means keeping your GUI in memory along with the rectangles that define the boundaries of each element. Then you can compare your mouse position to the boundary of each element to see which one it is pointing at.