LWJGL 3 鼠标移动

3

我希望在我的lwjgl3 java应用程序中实现FPS风格的鼠标控制,但是由于没有Mouse.getDX()Mouse.getDY(),我正在寻找一种使用glfw绑定的不同方法。 我编写了这个方法,在我的update()方法中调用:

public double[] pollMouseDelta() {
    DoubleBuffer x = BufferUtils.createDoubleBuffer(1);
    DoubleBuffer y = BufferUtils.createDoubleBuffer(1);

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

    double tempX = mouseX;
    double tempY = mouseY;

    mouseX = x.get();
    mouseY = y.get();

    return new double[] {
           mouseX - tempX,
           mouseY - tempY
    };
}

mouseXmouseY是全局变量。

在我的更新方法中,我执行以下操作:

double[] mouseDelta = pollMouseDelta();

camera.rotate(Camera.Direction.LEFT_RIGHT, (float) (0.2f * -mouseDelta[0]));
camera.rotate(Camera.Direction.UP_DOWN, (float) (0.2f * mouseDelta[1]));

camera.update();

我还将GLFW_CURSOR模式设置为GLFW_CURSOR_DISABLED
但是,相机在抖动并且跳跃很大。此外,有时鼠标似乎是反转的。
有什么建议吗?

你让我困惑了。Mouse是一个LWJGL类,肯定有Mouse.getDX/DY()方法。 - Joehot200
1
他们移除了所有与窗口系统有关的静态方法类,因为现在他们使用glfw来支持多窗口。 - tobs
3个回答

5
这是我FPS风格相机的基础代码片段。如您所见,我正在计算鼠标位置与屏幕中心沿X和Y轴的差值。
您可以在我的System.out.println调用中看到我计算Delta的值以及应该旋转这个角度的事实。然后,我将这些值乘以灵敏度,并按此数量旋转相机。
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;

        double prevX = 0;
        double prevY = 0;

        boolean rotX = false;
        boolean rotY = false;

        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);


        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);
            }

            // 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();
    }

}

抱歉我没有回复,这个星期我非常忙。不管怎样,谢谢你的快速回复。现在它运行得很好!! - tobs
我只有一个问题:你是如何使用回调函数实现这个方法的?你只是设置了newX / newY变量,还是使用了Zeroni在LWJGL论坛中建议的方法?我对此很感兴趣,因为我也尝试过他的方法,虽然卡顿已经消失了,但我仍然看到了一个很大的跳跃,并且鼠标有时倒置。 - tobs

1

我现在正在做一件和这个完全相同的事情。

这个想法是每次循环时将相机位置重置到中心,就像这样:

glfwSetCursorPos(YOUR WINDOW HANDLE, YOUR_WINDOW_WIDTH / 2, YOU_WINDOW_HEIGHT / 2);

这样,您的增量始终与屏幕中心的距离相比较。这样应该就解决了您的跳跃问题。 编辑(保留上面的答案): 好的,所以我也遇到了和你一样的问题。请忽略我上面说的,它不是完全正确的。
我最终实现了类似于您正在做的东西,但得到的结果是增量太大,因此摄像机移动得太快。
为了解决这个问题,我实际上每帧都进行鼠标轮询,并使用以下操作计算“增量”:
deltaX = currentMousePos.x - lastFrameMousePos.x
deltaY = currentMousePos.y - lastFrameMousePos.y

现在,增量应该足够小并且每帧都要计算。
然后,为了计算旋转角度,我使用以下方法:
deltaPos.getY() * sensitivity * Time.getDelta()

Where Time.getDelta()是每个帧循环之间的时间差。虽然不完美,但我最终得到了一个相当流畅的相机。

如果您需要更多信息,请告诉我。

编辑2:

请查看这个链接Link,其中有我类似的问题在LWJGL论坛上。现在我已经让它很好地工作了。我将发布一个单独的答案和代码。


谢谢您的回复。我尝试了这个方法,但是控制台输出的结果很奇怪:在游戏开始时,鼠标会出现一个大跳跃,每个X和Y坐标的鼠标增量都超过800。如果我重新启动游戏,有时鼠标似乎会反转。 - tobs
1
请查看我回答的修改,如果有帮助,请告诉我。 - abarax
谢谢您的快速回复。您是使用回调函数还是直接获取光标位置? - tobs
我两种风格都可以工作。请查看我的第二次更新。另外,你尝试过只是用更低的灵敏度进行乘法运算吗?我正在使用的灵敏度是float sensitivity = 0.0005f; - abarax

1

获取鼠标位置的另一种方法是扩展GLFWCursorPosCallback类,然后只需使用它在invoke方法中提供的x和y值。

关于如何做到这一点的良好教程可以在此处找到:

LWJGL 3 鼠标位置


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接