LWJGL 3 Problem getting mouse position

Started by abarax, December 13, 2014, 09:00:44

Previous topic - Next topic

abarax

Hi All,

I am having an issue with trying to set up FPS camera behavior.  Basically I am trying to calculate the delta of mouse position compared with the center of screen. Using this offset I can then transform my objects.

The follow code demonstrates the problem.  I should state that I am on Mac OS X and using LWJGL 3.

The issue is that it seems like the delta gets locked with a Y delta of 1.0 and an X delta of 0.0 as exhibited in the print out, and will not change from this value.  Could anyone take a look and tell me what I am doing wrong perhaps? I have been batting my head against this for quite some time.

What I am expecting to happen is that the X and Y delta print out should somewhat match the direction I am dragging my mouse.

import org.lwjgl.BufferUtils;
import org.lwjgl.Sys;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;

import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;

import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.glfw.GLFW.glfwGetCursorPos;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.system.MemoryUtil.*;

public class Main {

    // We need to strongly reference callback instances.
    private GLFWErrorCallback errorCallback;
    private GLFWKeyCallback   keyCallback;

    // The window handle
    private long window;

    public void run() {
        System.out.println("Hello LWJGL " + Sys.getVersion() + "!");

        try {
            init();
            loop();

            // Release window and window callbacks
            glfwDestroyWindow(window);
            keyCallback.release();
        } finally {
            // Terminate GLFW and release the GLFWerrorfun
            glfwTerminate();
            errorCallback.release();
        }
    }

    private void init() {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        glfwSetErrorCallback(errorCallback = errorCallbackPrint(System.err));

        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( glfwInit() != GL11.GL_TRUE )
            throw new IllegalStateException("Unable to initialize GLFW");

        // Configure our window
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation
        glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);


        int WIDTH = 800;
        int HEIGHT = 600;

        // Create the window
        window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World!", NULL, NULL);
        if ( window == NULL )
            throw new RuntimeException("Failed to create the GLFW window");

        // Setup a key callback. It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, keyCallback = new GLFWKeyCallback() {
            @Override
            public void invoke(long window, int key, int scancode, int action, int mods) {
                if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                    glfwSetWindowShouldClose(window, GL_TRUE); // We will detect this in our rendering loop
            }
        });

        // Get the resolution of the primary monitor
        ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        // Center our window
        glfwSetWindowPos(
                window,
                (GLFWvidmode.width(vidmode) - WIDTH) / 2,
                (GLFWvidmode.height(vidmode) - HEIGHT) / 2
        );

        // Make the OpenGL context current
        glfwMakeContextCurrent(window);
        // Enable v-sync
        glfwSwapInterval(1);

        // Make the window visible
        glfwShowWindow(window);
    }

    private void loop() {
        // This line is critical for LWJGL's interoperation with GLFW's
        // This line is critical for LWJGL's interoperation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the ContextCapabilities instance and makes the OpenGL
        // bindings available for use.
        GLContext.createFromCurrent();


        // Create a FloatBuffer to hold our vertex data
        FloatBuffer vertices = BufferUtils.createFloatBuffer(9);
        // Add vertices of the triangle
        vertices.put(new float[]
                {
                        0.0f,  0.5f,  0.0f,
                        0.5f, -0.5f,  0.0f,
                        -0.5f, -0.5f,  0.0f
                });
        // Rewind the vertices
        vertices.rewind();


        int vbo = glGenBuffers();
        int vao = glGenVertexArrays();

        glBindBuffer (GL_ARRAY_BUFFER, vbo);
        glBufferData (GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
        glBindVertexArray(vao);

        glEnableVertexAttribArray (0);
        glBindBuffer (GL_ARRAY_BUFFER, vbo);
        glVertexAttribPointer (0, 3, GL_FLOAT, false, 0, 0);

        final String vertex_shader =
                "#version 410\n" +
                "in vec3 vp;\n" +
                "void main () {\n" +
                "  gl_Position = vec4 (vp, 1.0);\n" +
                "}";

        final String frag_shader =
                "#version 400\n"    +
                "out vec4 frag_colour;" +
                "void main () {"         +
                "  frag_colour = vec4 (0.5, 0.0, 0.5, 1.0);" +
                "}";

        int shader_programme = glCreateProgram();


        int vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShaderID, vertex_shader);
        glCompileShader (vertexShaderID);

        if(glGetShaderi(vertexShaderID, GL_COMPILE_STATUS) == 0){
            System.err.println(glGetShaderInfoLog(vertexShaderID, 1024));
            System.exit(1);
        }

        glAttachShader (shader_programme, vertexShaderID);

        int fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShaderID, frag_shader);
        glCompileShader (fragmentShaderID);

        if(glGetShaderi(fragmentShaderID, GL_COMPILE_STATUS) == 0){
            System.err.println(glGetShaderInfoLog(fragmentShaderID, 1024));
            System.exit(1);
        }

        glAttachShader (shader_programme, fragmentShaderID);

        glLinkProgram (shader_programme);

        if(glGetProgrami(shader_programme, GL_LINK_STATUS) == 0){
            System.err.println(glGetProgramInfoLog(shader_programme, 1024));
            System.exit(1);
        }

        glValidateProgram(shader_programme);

        if(glGetProgrami(shader_programme, GL_VALIDATE_STATUS) == 0){
            System.err.println(glGetProgramInfoLog(shader_programme, 1024));
            System.exit(1);
        }

        boolean mouseLocked = false;
        double newX = 400;
        double newY = 300;


        while ( glfwWindowShouldClose(window) == GL_FALSE ) {

            if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) {
                glfwSetCursorPos(window, 800/2, 600/2);
                mouseLocked = true;
            }

            if (mouseLocked){
                DoubleBuffer x = BufferUtils.createDoubleBuffer(1);
                DoubleBuffer y = BufferUtils.createDoubleBuffer(1);

                glfwGetCursorPos(window, x, y);
                x.rewind();
                y.rewind();

                newX = x.get();
                newY = y.get();

                double deltaX = newX - 400;
                double deltaY = newY - 300;

                System.out.println("Delta X = " + deltaX + " Delta Y = " + deltaY);

                glfwSetCursorPos(window, 800/2, 600/2);
            }

            // wipe the drawing surface clear
            glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glUseProgram (shader_programme);
            glBindVertexArray (vao);
            // draw points 0-3 from the currently bound VAO with current in-use shader
            glDrawArrays (GL_TRIANGLES, 0, 3);
            // update other events like input handling
            glfwPollEvents ();
            // put the stuff we've been drawing onto the display
            glfwSwapBuffers (window);

        }
    }

    public static void main(String[] args) {
        new Main().run();
    }

}

Zeroni

The way you are doing it seems very odd. I have modified your code so that is works similarly to LWJGL 2.0
Just get the DX and DY and do your pitch, yaw, roll linear algebra.

import org.lwjgl.BufferUtils;
import org.lwjgl.Sys;
import org.lwjgl.glfw.GLFWCursorPosCallback;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.glfw.GLFWvidmode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import static org.lwjgl.glfw.Callbacks.errorCallbackPrint;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30.glGenVertexArrays;
import static org.lwjgl.system.MemoryUtil.NULL;
/**
 * Created by Zeroni on 12/13/14 4:05AM.
 *
 * @authors abarax, Haden Snodgrass
 * @version 1.0
 */
public class Main {



        // We need to strongly reference callback instances.
        private GLFWErrorCallback errorCallback;
        private GLFWKeyCallback   keyCallback;
        private GLFWCursorPosCallback cursorPosCallback;

        // The window handle
        private long window;

        // Mouse positions
        private int mouseX, mouseY;
        private int mouseDX, mouseDY;

    public void run() {
            System.out.println("Hello LWJGL " + Sys.getVersion() + "!");

            try {
                init();
                loop();

                // Release window and window callbacks
                glfwDestroyWindow(window);
                keyCallback.release();
            } finally {
                // Terminate GLFW and release the GLFWerrorfun
                glfwTerminate();
                errorCallback.release();
            }
        }

        private void init() {
            // Setup an error callback. The default implementation
            // will print the error message in System.err.
            glfwSetErrorCallback(errorCallback = errorCallbackPrint(System.err));

            // Initialize GLFW. Most GLFW functions will not work before doing this.
            if ( glfwInit() != GL11.GL_TRUE )
                throw new IllegalStateException("Unable to initialize GLFW");

            // Configure our window
            glfwDefaultWindowHints(); // optional, the current window hints are already the default
            glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation
            glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable
            glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
            glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
            glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
            glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);


            int WIDTH = 800;
            int HEIGHT = 600;

            // Create the window
            window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World!", NULL, NULL);
            if ( window == NULL )
                throw new RuntimeException("Failed to create the GLFW window");

            // Setup a key callback. It will be called every time a key is pressed, repeated or released.
            glfwSetKeyCallback(window, keyCallback = new GLFWKeyCallback() {
                @Override
                public void invoke(long window, int key, int scancode, int action, int mods) {
                    if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                        glfwSetWindowShouldClose(window, GL_TRUE); // We will detect this in our rendering loop
                }
            });

            // Initialize all mouse values as 0
            mouseX = mouseY = mouseDX = mouseDY = 0;
            glfwSetCursorPosCallback(window, cursorPosCallback = new GLFWCursorPosCallback(){

                @Override
                public void invoke(long window, double xpos, double ypos) {
                    // Add delta of x and y mouse coordinates
                    mouseDX += (int)xpos - mouseX;
                    mouseDY += (int)xpos - mouseY;
                    // Set new positions of x and y
                    mouseX = (int) xpos;
                    mouseY = (int) ypos;
                }
            });

            // Get the resolution of the primary monitor
            ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
            // Center our window
            glfwSetWindowPos(
                    window,
                    (GLFWvidmode.width(vidmode) - WIDTH) / 2,
                    (GLFWvidmode.height(vidmode) - HEIGHT) / 2
            );

            // Make the OpenGL context current
            glfwMakeContextCurrent(window);
            // Enable v-sync
            glfwSwapInterval(1);

            // Make the window visible
            glfwShowWindow(window);
        }

    public int getDX(){
        // Return mouse delta x and set delta x to 0
        return mouseDX | (mouseDX = 0);
    }

    public int getDY(){
        // Return mouse delta y and set delta y to 0
        return mouseDY | (mouseDY = 0);
    }

    private void loop() {
            // This line is critical for LWJGL's interoperation with GLFW's
            // This line is critical for LWJGL's interoperation with GLFW's
            // OpenGL context, or any context that is managed externally.
            // LWJGL detects the context that is current in the current thread,
            // creates the ContextCapabilities instance and makes the OpenGL
            // bindings available for use.
            GLContext.createFromCurrent();


            // Create a FloatBuffer to hold our vertex data
            FloatBuffer vertices = BufferUtils.createFloatBuffer(9);
            // Add vertices of the triangle
            vertices.put(new float[]
                    {
                            0.0f,  0.5f,  0.0f,
                            0.5f, -0.5f,  0.0f,
                            -0.5f, -0.5f,  0.0f
                    });
            // Rewind the vertices
            vertices.rewind();


            int vbo = glGenBuffers();
            int vao = glGenVertexArrays();

            glBindBuffer (GL_ARRAY_BUFFER, vbo);
            glBufferData (GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
            glBindVertexArray(vao);

            glEnableVertexAttribArray (0);
            glBindBuffer (GL_ARRAY_BUFFER, vbo);
            glVertexAttribPointer (0, 3, GL_FLOAT, false, 0, 0);

            final String vertex_shader =
                    "#version 410\n" +
                            "in vec3 vp;\n" +
                            "void main () {\n" +
                            "  gl_Position = vec4 (vp, 1.0);\n" +
                            "}";

            final String frag_shader =
                    "#version 400\n"    +
                            "out vec4 frag_colour;" +
                            "void main () {"         +
                            "  frag_colour = vec4 (0.5, 0.0, 0.5, 1.0);" +
                            "}";

            int shader_programme = glCreateProgram();


            int vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
            glShaderSource(vertexShaderID, vertex_shader);
            glCompileShader (vertexShaderID);

            if(glGetShaderi(vertexShaderID, GL_COMPILE_STATUS) == 0){
                System.err.println(glGetShaderInfoLog(vertexShaderID, 1024));
                System.exit(1);
            }

            glAttachShader (shader_programme, vertexShaderID);

            int fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
            glShaderSource(fragmentShaderID, frag_shader);
            glCompileShader (fragmentShaderID);

            if(glGetShaderi(fragmentShaderID, GL_COMPILE_STATUS) == 0){
                System.err.println(glGetShaderInfoLog(fragmentShaderID, 1024));
                System.exit(1);
            }

            glAttachShader (shader_programme, fragmentShaderID);

            glLinkProgram (shader_programme);

            if(glGetProgrami(shader_programme, GL_LINK_STATUS) == 0){
                System.err.println(glGetProgramInfoLog(shader_programme, 1024));
                System.exit(1);
            }

            glValidateProgram(shader_programme);

            if(glGetProgrami(shader_programme, GL_VALIDATE_STATUS) == 0){
                System.err.println(glGetProgramInfoLog(shader_programme, 1024));
                System.exit(1);
            }

            while ( glfwWindowShouldClose(window) == GL_FALSE ) {

                // Print the mouse delta x and delta Y
                System.out.println("Delta X = " + getDX() + " Delta Y = " + getDY());

                // wipe the drawing surface clear
                glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                glUseProgram (shader_programme);
                glBindVertexArray (vao);
                // draw points 0-3 from the currently bound VAO with current in-use shader
                glDrawArrays (GL_TRIANGLES, 0, 3);
                // update other events like input handling
                glfwPollEvents ();
                // put the stuff we've been drawing onto the display
                glfwSwapBuffers (window);

            }
        }

        public static void main(String[] args) {
            new Main().run();
        }

    }

PS: I hope this code alteration helps you.

- Zeroni
- Zeroni

spasi

Quote from: abarax on December 13, 2014, 09:00:44I am having an issue with trying to set up FPS camera behavior.  Basically I am trying to calculate the delta of mouse position compared with the center of screen. Using this offset I can then transform my objects.

The follow code demonstrates the problem.  I should state that I am on Mac OS X and using LWJGL 3.

The issue is that it seems like the delta gets locked with a Y delta of 1.0 and an X delta of 0.0 as exhibited in the print out, and will not change from this value.  Could anyone take a look and tell me what I am doing wrong perhaps? I have been batting my head against this for quite some time.

What I am expecting to happen is that the X and Y delta print out should somewhat match the direction I am dragging my mouse.

The problem is that you're setting the cursor position in the loop. This cannot work reliably, as most OSes impose limits on programmatic cursor updates. You're also effectively disabling any interaction with the window, it cannot be moved/resized/etc.

Mouse deltas work fine using a CursorPosCallback, but if you also need to constrain the cursor inside the window, you'll have to wait for GLFW 3.2, it will support captured cursor mode.

Zeroni

spasi, Congratz on your 1000th Post!

Much love
- LWJGL Community
- Zeroni

abarax

Post 1000! Nice one!

Thanks a lot for taking the time to help out guys, I really appreciate it. 

I think the DX/DY method will work, I am not sure if i require the cursor to be trapped within the window, given I can just disable it.

I'll give it a try and let you know how it goes.

Zeroni

The method requires that the window be focused, so if you click off the window it will not work.
I personally would check for left mouse press (Mouse #1) and cursor is inside the window. (Mouse Dragging)
Then execute the code.
- Zeroni

abarax

Just an update.

I have used the method above to achieve something which seems to work, thanks very much for the help.

What I was trying to emulate is this method/code here http://www.tomdalling.com/blog/modern-opengl/04-cameras-vectors-and-input/

Is there a reason why this wont work in LWJGL? According to this tutorial it should work. But if you check my test code int he original post it doesn't seem like it can.

Is there something I am not doing that this code is?

Thanks again.

spasi

I don't see anything in that tutorial that wouldn't work with LWJGL. What exactly are you having trouble with?

abarax

So in my original post I am (as far as I can tell) doing the exact same method as detailed in that tutorial for calculating the delta of the mouse position with respect to the center of the screen (For Camera purposes):

    double deltaX = newX - 400;
    double deltaY = newY - 300;


Where newX/newY comes directly from the LWJGL call glfwGetCursorPos.

But for some reason this is always the same value, newX/newY does not seem to change.  I am beginning to think this may be something to do with Java where the variable is not actually changing (some kind of pointer thing).

abarax

This addition to my loop might make it more clear:

Basically i should be getting System.out.println to print out the fact that I am moving my mouse along the X or Y axis as well as the delta as I move the mouse around my triangle on the screen.

But for some reason the println's are not firing at all when I move my mouse around the screen.  They will however occasionally trigger.

double prevX = 0;
        double prevY = 0;

        boolean rotX = false;
        boolean rotY = false;


        while ( glfwWindowShouldClose(window) == GL_FALSE ) {

            if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) {
                glfwSetCursorPos(window, 800/2, 600/2);
                mouseLocked = true;
            }

            if (mouseLocked){
                DoubleBuffer x = BufferUtils.createDoubleBuffer(1);
                DoubleBuffer y = BufferUtils.createDoubleBuffer(1);

                glfwGetCursorPos(window, x, y);
                x.rewind();
                y.rewind();

                newX = x.get();
                newY = y.get();

                double deltaX = newX - 400;
                double deltaY = newY - 300;

                rotX = newX != prevX;
                rotY = newY != prevY;

                if(rotY) {
                    System.out.println("ROTATE Y AXIS: " + deltaY);

                }
                if(rotX) {
                    System.out.println("ROTATE X AXIS: " + deltaX);
                }

                prevX = newX;
                prevY = newY;


                System.out.println("Delta X = " + deltaX + " Delta Y = " + deltaY);

                glfwSetCursorPos(window, 800/2, 600/2);
            }

spasi

The reason it isn't working for you is that you forgot to call:

glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);


But again, you should be using a GLFWCursorPosCallback instead of what you're doing with glfwGetCursorPos and glfwSetCursorPos.

abarax

Thanks for that.  Yes I am using the callback.

Disabling the cursor does seem to make it work.

I guess I just thought that there would be no difference in the X/Y values whether the cursor was enabled or disabled.

Turns out there is. Probably my lack of understanding of GLFW.

Thanks again for your help.