Window class - Fish-In-A-Suit/Conquest GitHub Wiki
Class layout
Static imports
- org.lwjgl.glfw.GLFW.*;
- org.lwjgl.opengl.GL11.*;
- org.lwjgl.System.MemoryUtil.*;
Regular imports
- org.lwjgl.glfw.GLFWKeyCallback;
- org.lwjgl.glfw.GLFWVidMode;
- org.lwjgl.opengl.GL;
Public instance fields
- long windowHandle
- GLFWKeyCallback keycallback;
- boolean[] keys;
Private instance fields
- int width; - the width of the window
- int height; - the height of the window
- String title; - the title of the window
Public methods
- void init()
- void setResized(boolean resized)
- boolean isResized()
- int getWidth() - returns the value of width
- int getHeight() - returns the value of height
This class deals with creating the window and initializing OpenGL context. The windowHandle variable which holds the reference to the window object is set as public, so that it can be used elsewhere in the code, particularly in the Main class.
init()
is responsible for:
- initializing GLFW and checking if it was properly initialized
- setting the window hints (those are some of the variables which determine the context - how OpenGL operates)
- creating a window
- handling failed window creation
- setting the resize callback
- setting the key callback
- centering the window to the centre of the screen
- making the context current on the specified window
- creating the OpenGL context
Initialize GLFW and check if it was properly initialized
GLFW is initialized with the method public static boolean glfwInit(). GLFW initialization is achieved by:
if(!glfwInit()) {
throw new IllegalStateException("Unable to initialize GLFW!");
}
Before most GLFW functions can be used, GLFW must be initialized, and before an application terminates GLFW should be terminated in order to free any resources allocated during or after initialization. If the glfw context couldn't be creates by glfwInit, the method body is executed. TODO: CREATE A PAGE FOR EXCEPTIONS AND EXPLAIN THE THEORY BEHIND THEM.
Setting up window hints
Window hints are a way of determining properties of a to-be-created window and context. Some affect the window itself, others affect the framebuffer and context.
Make sure to set the desired window hints BEFORE the creation of the window.
These hints are set to their default values at the point of GLFW initialization (glfwInit()
). They can be set to different values manually, one by one: glfwWindowHint(int hint, int value)
- hint = the window hint name
- value = mainly GL_TRUE or GL_FALSE, look up the API for more different options... values are hint-specific!
They can be reset to their default values all by once using:
glfwDefaultWindowHints()
.
glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
- the window shouldn't be visible while it's still being cunstructedglfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
- makes the window resizableglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- this, along with the next window hint, set the OpenGL version to be used to 3.2glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
- look up!glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
- specifies which OpenGL profile to create the context for. Possible values are one of GLFW_OPENGL_CORE_PROFILE or GLFW_OPENGL_COMPAT_PROFILE, or GLFW_OPENGL_ANY_PROFILE to not request a specific profile. If requesting an OpenGL version below 3.2, GLFW_OPENGL_ANY_PROFILE must be used.glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
- removes all of the deprecated functuonality
Creating the window
The window and it's associated OpenGL context are created with the method: public static long glfwCreateWindow(int width, int height, String title, long monitor, long share)
This method returns type long which corresponds to the memory address of the window. Therefore, create a variable that holds this long address in your code: windowHandle = glfwCreateWindow(...)
NOTE: Before you can use the context, you need to make it current!
If monitor parameter is set to null, then the created window will be in windowed mode. If you want to create a fullscreen window, you should specify the primary monitor as the monitor parameter inside the glfwCreateWindow(...)
method.
Handle failed window creation
Failed window creation throws a RuntimeException. RuntimeException is the superclass of those exceptions that can be thrown during the normal operation of the Java Virtual Machine. RuntimeException and its subclasses are unchecked exceptions. Unchecked exceptions do not need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method.
A runtime exception can be created with the constructor:
RuntimeException(String message) - it constructs a new runtime exception with the specified
Position the window to the center of the screen
The process of doing it:
- get the properties of the video mode of the monitor
- extract the width and height from the video mode
- set the window position based on the "extracted" width and height
The properties of a monitor are captured inside the video mode of that monitor. The video mode in LWJGL3 is represented as a class - GLFWVidMode, which holds all of the information about the properties of a monitor.
To capture the properties of a monitor, first create a reference to the GLFWVidMode class:
GLFWVidMode vidmode
... (1)*
then, to assign the reference vidmode the current video mode of a monitor:
(1) ... = glfwGetVideoMode(glfwGetPrimaryMonitor());
*
public static GLFWvidmode glfwGetVideoMode(GLFWmonitor monitor)
:
This method returns the current video mode (hence, the return type is GLFWVidMode) of the specified monitor, or NULL, if an error occurred.
The method public static GLFWmonitor glfwGetPrimaryMonitor() returns the primary monitor (and we need to pass that as a parameter to glfwGetVideoMode()
).
The monitor object is represented as an opaque type GLFWmonitor. It is a "struct", meaning that it holds several private values which we can acces via methods (such as width()
and height
).
The properties of the monitor are captured inside the GLFWVidMode struct (which we assigned the reference/pointer called vidmode).
The height and width of the window can be expressed in screen coordinates like so:
vidmode.height()
vidmode.width()
To position the window to the center of the screen, you need to subtract the width and height of window from the width and height of the screen and divide that by two.
Poisition the window using the following method: public static void glfwSetWindowPos(window, xpos, ypos), where:
- window represents the variable that holds the information (therefore, is a pointer) about the window's memory location
- xpos and ypos represent the x and y coordibates of the screen (expressed in screen pixels)
Set up a key callback
DEPRECATED:
To get the input events, you need to create a new GLFWKeyCallback object. To start getting input you will need to set a key callback to GLFW using that GLFWKeyCallback object.
glfwSetKeyCallback(long window, GLFWkeyfun cbfun);
This method sets the key callback of the specified window, which is called when a key is pressed, repeated or released.
You pass in a window handle as the first parameter and an instance of a class which implements the GLFWKeyCallback with the method
invoke(...)
as the second parameter.
The key callback is called every time a key is pressed, released or held. The information about the event is sent off to the
invoke(...)
method, inside which code is written to handle that event.
Another way of achieving the same effect is to use lambda expressions:
glfwSetKeyCallback(window, keyCallback = GLFWKeyCallback.create((window, key, scancode, action, mods) -> {
if (action != GLFW_RELEASE) {
keys[key] = true;
} else {
keys[key] = false;
}
}));
or:
glfwSetKeyCallback(windowHandle, (window, key, scancode, action, mods) -> {
if (action != GLFW_RELEASE) {
keys[key] = true;
} else {
keys[key] = false;
}
});
Make the context current
Think of a context as an object that holds all of OpenGL; when a context is destroyed, OpenGL is destroyed. It comprises various resources/variables, such as:
- driver resources in RAM
- texture IDs assigned
- VBO IDs assigned
- enabled states (ex. GL_BLEND, GL_DEPTH_TEST)
- etc
No calls to OpenGL methods can be made on a desired thread until the context is made current
Contexts are localized within a particular process of execution (an application, more or less) on an operating system. A process can create multiple OpenGL contexts. Each context can represent a separate viewable surface, like a window in an application.
In order for any OpenGL commands to work, a context must be current; all OpenGL commands affect the state of whichever context is current. The current context is a thread-local variable, so a single process can have several threads, each of which has its own current context. However, a single context cannot be current in multiple threads at the same time.
To make the context current, use the public static void glfwMakeContextCurrent(long window) method. Pass in windowHandle.
Create the GL capabilities - enable OpenGL methods to be called
Instances of GLCapabilities can be created with the GL.createCapabilities()
method. An OpenGL context must be current in the current thread before it is called. When the aforementioned method is called, a new instance of GLCapabilities is created. A complete reference for the GLCapabilities class can be seen here.
To create a new instance of GLCapabilities, call the method GL.crateCapabilities()
and make sure that the glfwContext is already created.
Enabling and disabling various gl capabilities
glEnable
and glDisable
enable and disable various capabilities. Use glIsEnabled
or glGet
to determine the current setting of any capability. The initial value for each capability with the exception of GL_DITHER and GL_MULTISAMPLE is GL_FALSE.
Both glEnable and glDisable take a single argument, cap, which can assume one of the following values. Here, only the values that are used in my code will be explained:
- GL_DEPTH_TEST: If enabled, do depth comparisons and update the depth buffer.