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();
}
}
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
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 (https://github.com/glfw/glfw/issues/58).
spasi, Congratz on your 1000th Post!
Much love
- LWJGL Community
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.
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.
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.
I don't see anything in that tutorial that wouldn't work with LWJGL. What exactly are you having trouble with?
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).
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);
}
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.
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.