For some reason my Window::callback is being called even after the mouse has left the window. I am unable to find a solution or even something that could help. Is it possible that GLFW updated how the mouse cursor callback operates? I wonder if it is an order of invocation problem?
Window
Window::Window(std::string title, int32_t width, int32_t height) {
// TODO: add support for monitor and share for GLFW
m_window = std::unique_ptr<GLFWwindow, GLFWdeleter>(glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr));
glfwMakeContextCurrent(m_window.get());
glfwSetWindowUserPointer(m_window.get(), this);
glfwSetCursorPosCallback(m_window.get(), Window::callback);
}
void Window::mouse_callback(double xpos, double ypos) {
std::cout << "x: " << xpos << " y: " << ypos << std::endl;
}
void Window::callback(GLFWwindow* window, double xpos, double ypos)
{
auto win = static_cast<Window*>(glfwGetWindowUserPointer(window));
win->mouse_callback(xpos, ypos);
}
Engine
void startup() const
{
if (glfwInit() == 0)
{
LOG(kError, "GLFW init failed!");
exit(-1);
}
}
void Engine::run() {
if (m_main_window_registered)
{
glewExperimental = static_cast<GLboolean>(true);
if (glewInit() != GLEW_OK)
{
std::cout << "Failed to initialize glew" << std::endl;
return;
}
}
while(glfwWindowShouldClose(m_main_window->window()) == 0) {
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(m_main_window->window());
glfwPollEvents();
}
}
main.cpp
int main()
{
g_engine.startup();
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
auto window = std::make_unique<Window>("Hello World!", 640, 480);
//window->make_current();
g_engine.registerWindow(std::move(window));
g_engine.run();
glfwTerminate();
return 0;
}
I have figured out what the problem (or better put) issue is. On Windows the callback performs as expected where once the mouse leaves the windows area the callback stops firing. For OSX the window never loses focus and therefore the cursor callback is always being called. To fix the issue you simply have to test the coordinates to ensure that the mouse is in fact inside the window.
Related
I'm seeing "ERROR 1282 in glViewport" in my terminal whne I call glViewport from inside glfwSetFramebufferSizeCallback. Not sure why, I think it should be firing from the correct context, but I'm not sure. It worked fine before I tried to move my gameloop into a thread.
Here's the code, I tried to trim out the extraneous stuff:
const int winWidth = 800;
const int winHeight = 600;
absl::Status glfwMain() {
gl::setOpenGlVersion(4, 6, GLFW_OPENGL_CORE_PROFILE);
ASSIGN_OR_RETURN(GLFWwindow * window, gl::createWindow(winWidth, winHeight, "mario4ever"))
std::atomic<bool> breakLoop = false;
auto gameLoop = std::async(std::launch::async, [&window, &breakLoop]() {
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
return absl::InternalError("Failed to initialize GLAD");
}
/* snip */
glViewport(0, 0, winWidth, winHeight);
glfwSetFramebufferSizeCallback(window, [](GLFWwindow *window, int width, int height) {
std::cerr << "glfwSetFramebufferSizeCallback" << std::endl;
glViewport(0, 0, width, height); // <----- error fires here
});
while (!breakLoop) {
draw(window);
}
/* snip */
return absl::OkStatus();
});
while (!glfwWindowShouldClose(window)) {
processInput(window);
glfwPollEvents();
}
breakLoop = true;
return gameLoop.get();
return absl::OkStatus();
}
int main() {
glfwInit();
auto status = glfwMain();
glfwTerminate();
if (!status.ok()) {
std::cerr << status << std::endl;
}
return status.raw_code();
}
Whether I put the glfwSetFramebufferSizeCallback inside the std::async or outside doesn't seem to make a difference.
What's the problem?
This tutorial says error 1282 is GL_INVALID_VALUE and according to these docs that should happen only when "either width or height is negative".
I checked the width and height that the callback is giving me and neither is negative. They're about what I'd expect.
Tried adding a mutex:
std::mutex drawMutex;
void draw(GLFWwindow *window) {
std::lock_guard<std::mutex> lock(drawMutex);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
}
...
// inside `std::async` callback:
glfwSetFramebufferSizeCallback(window, [](GLFWwindow *window, int width, int height) {
std::lock_guard<std::mutex> lock(drawMutex);
glViewport(0, 0, width, height);
});
I want to create a share GLFW window but GLFW is not able to create the second window.
i am able to create a single window but not two windows
This is my code.
I want to use the second window in another thread so i can share their context.
#include "pch.h"
#include <iostream>
#include <gl\glew.h>
#include <glfw3.h>
int SCR_WIDTH = 1920;
int SCR_HEIGHT = 1080;
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfw window creation
// --------------------
GLFWwindow* sharedWindow = NULL;
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", 0, sharedWindow);
if (window == NULL)
{
std::cout << "Failed to create the first GLFW window" << std::endl;
glfwTerminate();
return -1;
}
if (sharedWindow == NULL)
{
std::cout << "Failed to create the second GLFW window" << std::endl;
// glfwTerminate();
// return -1;
}
while (true)
{
}
std::cout << "Hello World!\n";
}
share is an input parameter. See glfwCreateWindow.
Create the first window and call glfwCreateWindow a 2nd time for the 2nd window:
GLFWwindow* wnd = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", 0, nullptr);
GLFWwindow* wnd2 = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "window 2", 0, window);
The window can even be created in a separate thread, but note you've to ensure that the OpenGL context of the 1st window is not current when you create the 2nd window.
#include <thread>
#include <mutex>
#include <condition_variable>
GLFWwindow *wnd = nullptr;
bool wnd2created = false;
std::mutex mtx;
std::condition_variable cv;
void wnd2func( void )
{
GLFWwindow *wnd2 = glfwCreateWindow( 800, 600, "window 2", nullptr, wnd );
{
std::unique_lock<std::mutex> lck(mtx);
wnd2created = true;
cv.notify_one();
}
if (wnd2 == nullptr)
return;
glfwMakeContextCurrent(wnd2);
// [...]
}
int main()
{
// [...]
wnd = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", 0, nullptr);
if (wnd == nullptr)
return -1;
std::thread wnd2thread(wnd2func);
{
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck, []() -> bool { return wnd2created; });
}
glfwMakeContextCurrent(wnd);
// [...]
}
I'm trying to left click and drag any 3d Object and if i let go of it, it should stay in its new position, how would i achieve this? The 3d object is loaded from the draw function that i have in a header file.
Someone said i should be using glutMouseFunc or glutMotionFunc.
void MouseClickCallbackFunction(int button, int state, int x, int y)
{
if (state == GLUT_DOWN) {
if (button == GLUT_LEFT)
{
std::cout << "Left " << x << " " << y <<std::endl;
leftClick.x = x;
leftClick.y = y;
}
else if (button == GLUT_RIGHT_BUTTON) {
std::cout << "Right " << x << " " << y << std::endl;
rightClick.x = x;
rightClick.y = y;
}
}
theGame->mouseClicked(button, state, x, y);
glutPostRedisplay();
}
/* function MouseMotionCallbackFunction()
* Description:
* - this is called when the mouse is clicked and moves
*/
void MouseMotionCallbackFunction(int x, int y)
{
theGame->mouseMoved(x, y);
glutPostRedisplay();
}
int main(int argc, char **argv)
{
/* initialize the window and OpenGL properly */
glutInit(&argc, argv);
glutInitContextVersion(4, 2);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutCreateWindow("OpenGL Framework");
glewExperimental = true;
if (glewInit() != GLEW_OK)
{
std::cout << "GLEW could not be initialized. \n";
system("pause");
return 0;
}
//glewInit();
std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl;
/* set up our function callbacks */
glutDisplayFunc(DisplayCallbackFunction);
glutKeyboardFunc(KeyboardCallbackFunction);
glutKeyboardUpFunc(KeyboardUpCallbackFunction);
glutMouseFunc(MouseClickCallbackFunction);
glutMotionFunc(MouseMotionCallbackFunction);
glutTimerFunc(1, TimerCallbackFunction, 0);
/* init the game */
theGame = new Game();
theGame->initializeGame();
/* start the game */
glutMainLoop();
return 0;
}
Here is the Draw fucntion with the object
void Game::draw()
{
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
PassThrough.Bind();
PassThrough.SendUniformMat4("uModel", MonkeyTransform.data, false);
PassThrough.SendUniformMat4("uView", CameraTransform.GetInverse().data, false);
PassThrough.SendUniformMat4("uProj", CameraProjection.data, false);
PassThrough.SendUniform("uTex", 0);
PassThrough.SendUniform("LightPosition", CameraTransform.GetInverse() * vec4(4.0f,0.0f,0.0f,1.0f));
PassThrough.SendUniform("LightAmbient", vec3(0.15f, 0.15f, 0.15f));
PassThrough.SendUniform("LightDiffuse", vec3(0.7f,0.1f,0.2f));
PassThrough.SendUniform("LightSpecular", vec3(0.8f,0.1f,0.1f));
PassThrough.SendUniform("LightSpecularExponent", 50.0f);
PassThrough.SendUniform("Attenuation_Constant", 1.0f);
PassThrough.SendUniform("Attenuation_Linear", 0.1f);
PassThrough.SendUniform("Attenuation_Quadratic", 0.01f);
glBindVertexArray(Monkey.VAO);
glDrawArrays(GL_TRIANGLES, 0, Monkey.GetNumVerticies());
glBindVertexArray(0);
PassThrough.unBind();
glutSwapBuffers();
}
The concept is called mouse picking. A method to achieve this is ray casting. Essentially what you want to do is map the mouse coordinates to a region in your scene. In your case you want to check if the position is within the monkey. Then it is as simple as handling the mouse up, down, and move events.
Mouse down: check if object is being picked. If it is, great- maybe highlight it or something. Store ref to object picked; store position of mouse at beginning of pick (pos)
Mouse move: is mouse down? Is picked object ref? Update object position based on delta between pos and coord in mouse move event
Mouse up: clear picked obj ref, position
This link may be of interest http://antongerdelan.net/opengl/raycasting.html
I am currently creating a lightweight program to view and manipulate 3D objects similar to the program Blender or 3DS max.
I am using C++, GLFW, and OpenGL. While I plan to have it be cross platform, I am currently only working on a computer running Debian 9.0 (stretch) with the XFCE desktop environment.
Currently the callback glfwSetDropCallback never seems to get called, no mater what I try. I have set break points and asserts but nothing. All other callbacks that I have tried work just fine. I have included my GLFW cpp from my project. If something else is needed feel free to ask.
Also as a last note I am just using GLFW because everything else seems too heavy for my needs. Also using X11 directly appears like a lot of work for not much gain, especially since I would need a different solution for each OS I target. In the end I just want to be able to drag and drop items onto my programs window and then have my code do something with the path(s).
#include "GLFWindow.h"
#include <cassert>
#include <iostream>
// Variables for callback.
static bool keys[1024];
static std::vector<std::string> dragAndDrop;
static Camera* _camera;
static GLdouble* _lastX;
static GLdouble* _lastY;
GLFWindow::GLFWindow()
:m_window(nullptr), m_lastX(0.0), m_lastY(0.0)
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GLFW_VERSION_MAJOR);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GLFW_VERSION_MINOR);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE); // For OSX
glfwWindowHint(GLFW_FOCUSED, GL_TRUE);
std::cout << glfwGetVersionString() << std::endl;
m_window = glfwCreateWindow(1920, 1080, "Model Viewer", /*glfwGetPrimaryMonitor()*/nullptr, nullptr);
if(m_window == nullptr)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
assert(m_window != nullptr);
}
glfwMakeContextCurrent(m_window);
glfwSetWindowPos(m_window, 1920 - 1920/2, 1080 - 1080/2);
glfwSetKeyCallback(m_window,
[](GLFWwindow* window, int key, int, int action, int)
{
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if(action == GLFW_PRESS)
keys[key] = true;
else if(action == GLFW_RELEASE)
keys[key] = false;
});
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
glfwSetCursorEnterCallback(m_window,
[](GLFWwindow* window, int entered)
{
if(entered)
{
double xPos, yPos;
glfwGetCursorPos(window, &xPos, &yPos);
*_lastX = xPos;
*_lastY = yPos;
}
});
glfwSetDropCallback(m_window,
[](GLFWwindow*, int count, const char** paths)
{
std::cout << "Drag and Drop count - " << count << std::endl;
for(unsigned int i = 0; i < count; ++i)
{
std::cout << std::string(paths[i]) << std::endl;
dragAndDrop.push_back(std::string(paths[i]));
}
});
}
GLFWindow::~GLFWindow()
{
glfwTerminate();
}
GLint GLFWindow::GetWidth()
{
GLint width = 0;
glfwGetFramebufferSize(m_window, &width, nullptr);
return width;
}
GLint GLFWindow::GetHeight()
{
GLint height = 0;
glfwGetFramebufferSize(m_window, nullptr, &height);
return height;
}
std::string GLFWindow::GetDragAndDrop()
{
if(dragAndDrop.empty())
return "END";
std::string item = dragAndDrop[dragAndDrop.size()-1];
dragAndDrop.pop_back();
if(item.find(".obj") == std::string::npos && item.find(".fbx") == std::string::npos
&& item.find(".OBJ") == std::string::npos && item.find(".FBX") == std::string::npos)
return "INVALID";
return item;
}
bool GLFWindow::ShouldClose()
{
return glfwWindowShouldClose(m_window);
}
void GLFWindow::AddCamera(Camera* camera)
{
m_camera = camera;
_camera = m_camera;
_lastX = &m_lastX;
_lastY = &m_lastY;
glfwSetScrollCallback(m_window,
[](GLFWwindow*, double, double yoffset)
{
_camera->ProcessMouseScroll(yoffset);
});
glfwSetCursorPosCallback(m_window,
[](GLFWwindow*, double xPos, double yPos)
{
GLfloat xOffset = xPos - *_lastX;
GLfloat yOffset = *_lastY - yPos;
*_lastX = xPos;
*_lastY = yPos;
_camera->ProcessMouseMovement(xOffset, yOffset);
});
}
void GLFWindow::PollEvents(const GLfloat deltaTime)
{
glfwPollEvents();
if(keys[GLFW_KEY_W])
m_camera->ProcessKeyboard(0, deltaTime);
if(keys[GLFW_KEY_S])
m_camera->ProcessKeyboard(1, deltaTime);
if(keys[GLFW_KEY_A])
m_camera->ProcessKeyboard(2, deltaTime);
if(keys[GLFW_KEY_D])
m_camera->ProcessKeyboard(3, deltaTime);
if(keys[GLFW_KEY_Q])
m_camera->ProcessKeyboard(4, deltaTime);
if(keys[GLFW_KEY_E])
m_camera->ProcessKeyboard(5, deltaTime);
}
GLfloat GLFWindow::GetElapsedTime()
{
return glfwGetTime();
}
void GLFWindow::SwapBuffers()
{
glfwSwapBuffers(m_window);
}
Thanks you for your time,
Brandon
Edit:
I added some X11 code in to test some things.
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
Display *display =XOpenDisplay((char *)0);
if(XInternAtom(display, "XdndAware", False))
std::cout << "Supported" << std::endl;
This code always returns Supported. I now believe it is a bug with GLFW.
Final Edit:
This was a bug with GLFW.
Bug is due to Thunar providing text/uri-list while GLFW assumed UTF8_STRING was supported. Fix incoming.
If anyone else has this issue here is the fix.
https://github.com/glfw/glfw/commit/6a65341e14bc7745c52fe86fe53be08dbd682dd9
Edit: This was indeed a bug and has been resolved.
First of all make sure that you're using the newest stable GLFW version (today that is 3.2.1).
The most obvious reason could be that you never call GLFWindow::PollEvents() thus in turn never call glfwPollEvents(). However I'm doubting this is the cause, as you never mention any other input problems.
To verify if it really is the callback never getting called, try this minimal example:
glfwInit();
// You might need some of the window hints
GLFWwindow *window = glfwCreateWindow(600, 400, "Test glfwSetDropCallback", NULL, NULL);
glfwSetDropCallback(window, [](GLFWwindow*, int count, const char **paths)
{
std::cout << "Drag and Drop count - " << count << std::endl;
for (unsigned int i = 0; i < count; ++i)
std::cout << "- " << paths[i] << std::endl;
});
while (!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
I just tested the above code on Windows and it worked. So if that code doesn't work. Then it might be a X11 specific bug and should be notified on GLFW's issue tracker.
If it still doesn't work. Then before deeming it a bug, you should test if your display even supports file dropping. So while this is X11 specific. You'd only be using it to verify support.
Note that GLFW doesn't complain if dropping files isn't supported.
It's a long time since I've touched X11. But if I recall correctly, checking support for dropping files would be something like this:
Display *display = ...
if (XInternAtom(display, "XdndAware", False))
std::cout << "Supported" << std::endl;
I'm creating a GLFW window, and I would like the bottom left corner to be the origin. For some reason, the top left corner is the origin.
I should also mention that I'm using OSX.
Here's my window class:
#include "window.h"
namespace graphics {
void window_size_callback(GLFWwindow* window, int width, int height);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mode);
void cursor_position_callback(GLFWwindow* window, double mouse_x, double mouse_y);
Window::Window(const char* title, int width, int height) {
m_title = title;
m_width = width;
m_height = height;
if (!init())
glfwTerminate();
}
Window::~Window() {
glfwTerminate();
}
bool Window::init() {
if (!glfwInit()) {
std::cout << "GLFW failed to initialize!" << std::endl;
return false;
}
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
m_window = glfwCreateWindow(m_width, m_height, m_title, NULL, NULL);
if (!m_window) {
glfwTerminate();
std::cout << "GLFW failed to create a window!" << std::endl;
return false;
}
glfwMakeContextCurrent(m_window);
glfwSetWindowUserPointer(m_window, this);
glfwSetWindowSizeCallback(m_window, window_size_callback);
glfwSetKeyCallback(m_window, key_callback);
glfwSetMouseButtonCallback(m_window, mouse_button_callback);
glfwSetCursorPosCallback(m_window, cursor_position_callback);
if (glewInit() != GLEW_OK) {
std::cout << "GLEW failed to initialize!" << std::endl;
return false;
}
std::cout << "OpenGL Version: " << glGetString(GL_VERSION) << std::endl;
std::cout << "OpenGL Shading Language Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
return true;
}
void Window::update() {
glfwPollEvents();
glfwSwapBuffers(m_window);
}
void Window::clear() const {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
bool Window::isClosed() const {
return glfwWindowShouldClose(m_window);
}
bool Window::isKeyPressed(unsigned int keycode) const {
if (keycode >= MAX_KEYS)
return false;
return m_keys[keycode];
}
bool Window::isMouseButtonPressed(unsigned int button) const {
if (button >= MAX_BUTTONS)
return false;
return m_mouse_buttons[button];
}
void Window::getMousePosition(double& x, double& y) const {
x = m_mouse_x;
y = m_mouse_y;
}
void window_size_callback(GLFWwindow* window, int width, int height) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
win->m_width = width;
win->m_height = height;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
win->m_keys[key] = action != GLFW_RELEASE;
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mode) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
win->m_mouse_buttons[button] = action != GLFW_RELEASE;
}
void cursor_position_callback(GLFWwindow* window, double mouse_x, double mouse_y) {
Window* win = (Window*)glfwGetWindowUserPointer(window);
win->m_mouse_x = mouse_x;
win->m_mouse_y = mouse_y;
}
}
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/
But here is a better way to visualize this : use the Right Hand Rule
X is your thumb
Y is your index
Z is your middle finger. If you put
your thumb to the right and your index to the sky, it will point to
your back, too. Having the Z in this direction is weird, so why is it
so ? Short answer : because 100 years of Right Hand Rule Math will
give you lots of useful tools. The only downside is an unintuitive Z.
This is something you can’t change, it’s built in your graphics card.
So (-1,-1) is the bottom left corner of your screen. (1,-1) is the
bottom right, and (0,1) is the middle top. So this triangle should
take most of the screen.