Hot questions for Using Lightweight Java Game Library in cursor

Question:

How do I get the position of a cursor? I looked in the documentation of GLFW, and there is a method glfwGetCursorPos(window, &xpos, &ypos) but Java does not have pointers so when I tried this method in Java there were DoubleBuffers as the arguments. Now when I write something like this:

public static double getCursorPosX(long windowID){
    DoubleBuffer posX = null;
    DoubleBuffer posY = null;
    glfwGetCursorPos(windowID, posX, posY);
    return posX != null ? posX.get() : 0;
}

posX is null and I can't figure out why (yes, I am setting up the callback in my display class).


Answer:

Java does not directly support pointers, so LWJGL uses buffers as a workaround. These just wrap a memory address that can be read from and written to through methods on the object. This allows you to pass buffers to functions which write values into them, so you can then read these values.

The key point here is that you actually have to create a buffer beforehand for storing the value.

public static double getCursorPosX(long windowID) {
    DoubleBuffer posX = BufferUtils.createDoubleBuffer(1);
    glfwGetCursorPos(windowID, posX, null);
    return posX.get(0);
}

BufferUtils.createDoubleBuffer(length) is a utility function that creates a buffer. There are different buffers for different primitives, like int, long, char, float, double etc. In this case we need a buffer that can store doubles. The number (1) we pass to the method is the number of values the buffer should be able to store. We can use a buffer with a larger size for storing multiple values like in an array, but here we only want a single value.

The get(index) method returns the value at the given index. We only want to read the first value so we specify 0. You can also use put(index, value) to store values in a buffer.

Note: It might be tempting to do something like the following if you want to get both x and y values:

DoubleBuffer coords = BufferUtils.createDoubleBuffer(2);
glfwGetCursorPos(windowID, coords, coords);
double x = coords.get(0);
double y = coords.get(1);

However, this will not work as intended: It will write the y value to index 0 and leave a garbage (read: random) value at index 1. If you want to get both coords, you have to create a separate buffer for each.

DoubleBuffer xBuffer = BufferUtils.createDoubleBuffer(1);
DoubleBuffer yBuffer = BufferUtils.createDoubleBuffer(1);
glfwGetCursorPos(windowID, xBuffer, yBuffer);
double x = xBuffer.get(0);
double y = yBuffer.get(0);

Question:

I am using LWJGL's Mouse.setNativeCursor() to change the cursor in a game I'm making. However, the rest of the game is scaled up while the cursor retains a 1:1 pixel ratio, so the cursor looks out of place.

Is there an efficient way to transform the cursor on the fly?

If not, is a software cursor (drawing an image at the mouse co-ordinates) generally considered a good alternative? I have been avoiding using one up until now because I have heard that it ties mouse movement to the frame rate of the game which can introduce latency.


Answer:

I don't know, the only option might be to scale the cursor's texture. Or, you could hide the cursor and draw it in OpenGL. You would just draw a 2D quad above the screen and all of it's contents. This would let you resize it, but it might confuse users and make the mouse more inaccurate.

If you're looking for dynamic scaling without latency, hiding it and drawing the quad might be the best way to go.

Question:

I want to change the Cursor image in Java in my LWJGL project. But so far i only found solutions to do it with a GLFW Window, which i am not using.

I have really no idea how, since any attempt failed. please help


Answer:

LWJGL 2.9.1

Cursor cursor = new Cursor(int width, int height, int xHotspot, int yHotspot, int numImages, IntBuffer images, IntBuffer delays);
Mouse.setNativeCursor(cursor);

Also, LWJGL 2 is not supported anymore and you should start using LWJGL 3. https://github.com/LWJGL/lwjgl3-wiki/wiki/1.2.-Why-a-new-version

Question:

So I've wrote a window in java using the java bindings of GLFW provided by lwjgl. It works fine on linux but for some reason some methods do not work on windows.

I've registered a key callback that works fine on both linux and windows, but a char callback for example only works on linux.

Another problem I'm having, which breaks my camera rotation because I can't center the mouse to calculate the offset from there, is that setCursorPos() is not working.

This is how I create my window:

    public void init(int selectedMonitor, int glMajor, int glMinor, int windowHints) {
        if (this.initialized) return;
        GLFWErrorCallback.createPrint(System.err).set();

        if (!GLFW.glfwInit())
            throw new WindowCreationException("Unable to initialize GLFW.");

        // Configure window hints
        GLFW.glfwDefaultWindowHints();
        GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, (windowHints & 1));
        GLFW.glfwWindowHint(GLFW.GLFW_FOCUSED, (windowHints >> 1) & 1);
        GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, (windowHints >> 2) & 1);
        GLFW.glfwWindowHint(GLFW.GLFW_DECORATED, (windowHints >> 3) & 1);
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, glMajor);
        GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, glMinor);
        GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_PROFILE, GLFW.GLFW_OPENGL_CORE_PROFILE);
        GLFW.glfwWindowHint(GLFW.GLFW_OPENGL_FORWARD_COMPAT, (windowHints >> 7));

        // Set video mode
        PointerBuffer monitors = GLFW.glfwGetMonitors();
        if (monitors == null)
            throw new WindowCreationException("Could not create window, there are no monitors.");

        if (selectedMonitor >= monitors.capacity() || selectedMonitor < 0) {
            this.currentMonitor = GLFW.glfwGetPrimaryMonitor();
        } else {
            this.currentMonitor = monitors.get(selectedMonitor);
        }
        this.vidMode = GLFW.glfwGetVideoMode(this.currentMonitor);

        // Create window handle
        this.windowHandle = GLFW.glfwCreateWindow(this.width, this.height, this.title, this.isFullscreen ? this.currentMonitor : 0L, 0L);
        if (this.windowHandle == 0L)
            throw new RuntimeException("Failed to create GLFW Window.");

        // Center window if configured
        if (((windowHints >> 4) & 1) == 1) this.centerWindow();

        // Set key callbacks
        this.addKeyCallback((window, keyCode, scanCode, action, mods) -> Key.byKeyCode(keyCode).update(action));
        this.addMouseButtonCallback((window, keyCode, action, mods) -> Key.byKeyCode(keyCode).update(action));

        glfwSetKeyCallback(this.windowHandle, (window, keyCode, scanCode, action, mods) -> {
            for (GLFWKeyCallbackI callback : this.keyCallbacks) callback.invoke(window, keyCode, scanCode, action, mods);
        });

        glfwSetCharCallback(this.windowHandle, (window, unicode) -> {
            for (GLFWCharCallbackI callback : this.charCallbacks) callback.invoke(window, unicode);
        });

        glfwSetMouseButtonCallback(this.windowHandle, (window, keyCode, action, mods) -> {
            for (GLFWMouseButtonCallbackI callback : this.mouseButtonCallbacks) callback.invoke(window, keyCode, action, mods);
        });

        glfwSetScrollCallback(this.windowHandle, (window, xOffset, yOffset) -> {
            for (GLFWScrollCallbackI callback : this.scrollCallbacks) callback.invoke(window, xOffset, yOffset);
        });

        // Make the OpenGL context current
        GLFW.glfwMakeContextCurrent(this.windowHandle);
        if (((windowHints >> 5) & 1) == 1) GLFW.glfwSwapInterval(1);

        // Configure cursor
        this.mouseX = MemoryUtil.memAllocDouble(1);
        this.mouseY = MemoryUtil.memAllocDouble(1);
        if (((windowHints >> 6) & 1) == 1) this.hideCursor(true);

        this.initialized = true;
    }

And when I execute this code:

GLFW.glfwSetCursorPos(this.windowHandle, 0, 0);
GLFW.glfwGetCursorPos(this.windowHandle, this.mouseX, this.mouseY);
System.out.println("Mouse Pos: " + this.mouseX.get(0) + ", " + this.mouseY.get(0));

The result differs between Windows and Linux. Linux: Mouse Pos: 0, 0 Windows: Mouse Pos: 623.0, 367.0

I don't know why it's not working on windows, and it even seems to be completely unrelated to the lwjgl version, because I tried 3.1.6, 3.2.1, 3.2.2 and 3.2.3-SNAPSHOT, and it's the same for all those versions. So either the problem is me forgetting something when creating the window or windows broke something in some update, because months ago when I made a project with lwjgl 3.1.6 I'm 100% sure setCursorPos() was working on both linux and windows back then.


Answer:

Okay, so I found the solution to the problem:

For some reason, glfwSetCursorPos() must be called in the same thread the window was created in on windows and not on linux.

This is kinda weird because it didn't cause a segmentation fault on windows, but calling the method from the same thread works for both windows and linux.

Question:

I'm trying to make a 3D game sort of like minecraft but I don't seem to know how I can make the mouse pin to the center of the screen? I'm using lwjgl to handle mouse inputs. I just want the mouse cursor to stay in the middle of the window that I have created. I saw that there was a Mouse.grab method in the lwjgl mouse class, but when I set it to true, it seems that it doesn't do anything.


Answer:

First of all, lets look at what Mouse.grab(true) does. There are two distinct operations that this does, all grouped into one call. the first one is to pin the cursor in the centre of the screen, and the second one is to make the cursor invisible. Both of these operations combined will lock the cursor at the centre of the screen. To get input (I assume you are already using this method for camera movement), you will have to use the differential position function of the mouse class, Mouse.getDX() and Mouse.getDY(), which return the change in the cursor position of the cursor(effectively how much the user has moved the mouse) this frame.

Now, In 3D games, you tend to have a crosshair. This is not to be mistaken for the mouse. The crosshair is a UI element draw over the scene (and at the centre of the frame buffer (screen)) that gives the player a point of reference as to where the centre of the screen is, and, in shooter games, where the bullets they shoot will come from, allowing them to aim.

Here, i will provide a short example as to how you can achieve this effect. This code will vary a lot depending on how you are doing graphics, if you are using immediate mode and the fixed pipeline (GL11) or a subsequent version, thus, this will be mostly pseudo code. Feel free to specify which method of rendering you are using and i could give you a more detailed example.

// Your 3D render code here

float aspectRatio = Dislay.getWidth() / (float) Display.getHeight();

// will depend on your rendering implementation
// however the concept stays the same, you switch from a perspective view
// to an orthogonal view
switch_projection_matrix_to_ortho(-1 * aspectRatio, 1 * aspectRatio, -1, 1, -1, 1);

// Apply a crosshair texture
crossahir_texture.bind()
// Draw a quad
quad.draw()

Hope this helped!

Question:

I'm trying to setup mouse callbacks with LWJGL3. only problem is that all the tutorials and docs tell me to use the line GLFWSetCursorPosCallback() (with some parameters) but Eclipse keeps telling me that it cannot be resolved to a type, which i assume means it thinks it doesn't exist.

I have updated LWJGL to the latest nightly version, and every tutorial i look at says to use this line. Are they outdated? which is the correct line to use?

Example tutorial: https://www.youtube.com/watch?v=Ownlj6W2Lss


Answer:

You need to use the lower case function:

GLFWCursorPosCallback cursorPosCallback = new GLFWCursorPosCallback() {
    @Override
    public void invoke(long window, double xpos, double ypos) {
        // ...
    }
};
glfwSetCursorPosCallback(windowId, cursorPosCallback);

You can either statically import it:

import static org.lwjgl.glfw.GLFW.glfwSetCursorPosCallback;

or access it directly through its class:

import org.lwjgl.glfw.GLFW;

GLFW.glfwSetCursorPosCallback(/*...*/);