Notes:
- All code in this unit should be written in OpenGL 4.1. Apple does not support newer versions of OpenGL for Macs.
- Running LWJGL code on a Mac requires you to add the command line argument -XstartOnFirstThread to the JVM. In Eclipse, this can be done in Run > Run configurations > Arguments > VM arguments
- Or if you're running this in VS Code, you will need to create the
launch.json
file in your.vscode/
folder.- this can also be created by clicking the
run | debug
that shows up above thepublic static void main(String[] args){
- this can also be created by clicking the
- the launch.json file has the following formatting in a hypothetical repository:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "App",
"request": "launch",
"mainClass": "main.App",
"projectName": "comp3170-lwjgl",
"vmArgs": "-XstartOnFirstThread"
}
]
}
- the line with:
"mainClass": "main.App",
main.App
should be replaced with<packagename>.<MainClass>
main
or<packagename>
- is the folder you have yourstatic void main
insideApp
or<MainClass>
- is the name of the file thatstatic void main
is within, not including the.java
- not having it as a subfolder of the
src/
folder has some weird implications, so it's best not to go there - it's also important to make sure your shader directory is also updated to where ever you put it
- for the sake of less headache, it's best to use a folder that "branches" away from the other comp3170 content, to keep it somewhat compartmentalised
- if you had your
static void main(String[] args){
in say,src/thecoolpackage/gamefiles/MyGame.java
, then you'd change the line to"mainClass": "thecoolpackage.gamefiles.MyGame"
- changing
projectName
stuff, is not tested, but likely will change the way the compiler references everything and may mean it cant find your content depending on how your vscode environment is set up. - changing
name
should just be for the menu option in vscode - changing
request
may cause weirdness with the way vscode references thelaunch.json
file vmArgs
can be made to be an array of items if there's other arguements you want to add, by using something like:
"vmArgs": [ "-XstartOnFirstThread" ]
- vscode also has the
launch.json
attribute/key/the thingies we are speaking about ofargs
, which are passed on to yourString[] args
in yourstatic void main(String[] args){
- make sure that whatever directory/folder/package you use for shader files is reflected in your main java class. In this example we use:
final private File DIRECTORY = new File("src/main/");
- if you decide you want to just keep all your java files and shaders seperate, it might be nice to have a third folder inside the
src
folder calledshaders
, then you can just update it to be
final private File DIRECTORY = new File("src/shaders/");
- Make sure to move your
.glsl
files there afterwards :D (guilty of that myself)
- Delete any JOGL imports.
- Most GL methods and constants can be found on the org.lwjgl.opengl.GL41 class
- You will also need the GLCapabilites class
Old:
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL4;
New:
import org.lwjgl.opengl.GLCapabilities;
import static org.lwjgl.opengl.GL41.*;
The new library classes have slightly diiferent names:
Old:
import comp3170.GLBuffers;
import comp3170.GLException;
import comp3170.Shader;
New:
import comp3170.GLBuffers;
import comp3170.OpenGLException;
import comp3170.IWindowListener;
import comp3170.Shader;
import comp3170.Window;
- Windowing is now handled by JWGL rather than Java Swing, so you don't need to use the JFrame anymore
- The JOGL GLEventListener has been replaced with the interface IWindowListener
Old:
public class Week2 extends JFrame implements GLEventListener {
New
public class Week2 implements IWindowListener {
The new constructor is much simpler, just create a window and run it.
Old:
public Week2() {
super("Week 2 example");
// set up a GL canvas
GLProfile profile = GLProfile.get(GLProfile.GL4);
GLCapabilities capabilities = new GLCapabilities(profile);
canvas = new GLCanvas(capabilities);
canvas.addGLEventListener(this);
add(canvas);
// set up the JFrame
setSize(width,height);
setVisible(true);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
New:
public Week2() {
Window window = new Window("Week 2 example", width, height, this);
window.run();
}
- The event methods are now called init(), draw(), resize() and close()
- The event methods no longer take a GLAutoDrawable parameter
- There is no need to have a GL object to access methods or constants
Old:
@Override
public void display(GLAutoDrawable arg0) {
GL4 gl = (GL4) GLContext.getCurrentGL();
gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
New:
@Override
public void draw() {
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
- The method signature for resize has changed.
- Resize is always called once when the window is first created
Note: on Macs with retina display, the window size does not match the canvas size, so you should always implement a basic resize method, even if the window isn't resizable.
Old:
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
this.width = width;
this.height = height;
}
New:
@Override
public void resize(int width, int height) {
this.width = width;
this.height = height;
}
The new InputManager class has the same interface as the old one. The only difference is that you need to create the InputManager in init() rather than in the main constructor, as the window has to be initialised before the InputManager can be attached.
Old:
private GLCanvas canvas;
private InputManager input;
public Demo() {
GLProfile profile = GLProfile.get(GLProfile.GL4);
GLCapabilities capabilities = new GLCapabilities(profile);
canvas = new GLCanvas(capabilities);
canvas.addGLEventListener(this);
add(canvas);
// set up Input manager
input = new InputManager(canvas);
}
New:
private Window window;
private InputManager input;
public Demo() {
window = new Window("Demo", width, height, true, this);
window.run();
}
public void init() {
// set up Input manager
input = new InputManager(canvas);
}
The new InputSystem uses GLFW keycodes rather than Java KeyEvent keycodes.
Old:
if (input.isKeyDown(KeyEvent.VK_LEFT)) {
cameraAngle = (cameraAngle + CAMERA_ROTATION_SPEED * deltaTime) % TAU;
}
New:
if (input.isKeyDown(GLFW_KEY_LEFT)) {
cameraAngle = (cameraAngle + CAMERA_ROTATION_SPEED * deltaTime) % TAU;
}
There is no longer any need to add an Animator or otherwise 'turn-on' animation. When you call Window.run() it will repeatedly call the draw() method on the listener until the window closes. Double buffering is enabled by default (and is tricky to disable without editing the Window class).
Calculating deltaTime is done much the same as before, however I recommend initialising oldTime in init() rather than in the constructor.
Old:
private Animator animator;
private long oldTime;
public Week3() {
// set up a GL canvas
GLProfile profile = GLProfile.get(GLProfile.GL4);
GLCapabilities capabilities = new GLCapabilities(profile);
canvas = new GLCanvas(capabilities);
canvas.addGLEventListener(this);
add(canvas);
// set up Animator
animator = new Animator(canvas);
animator.start();
oldTime = System.currentTimeMillis();
}
private void update() {
long time = System.currentTimeMillis();
float deltaTime = (time - oldTime) / 1000f;
oldTime = time;
System.out.println("update: dt = " + deltaTime + "s");
}
public void display(GLAutoDrawable arg0) {
// update the scene
update();
// ...
}
New:
private long oldTime;
public Week3() throws OpenGLException {
window = new Window("Week 3", screenWidth, screenHeight, this);
window.run();
// no need for an Animator
}
public void init() {
oldTime = System.currentTimeMillis();
}
private void update() {
// same code as before
long time = System.currentTimeMillis();
float deltaTime = (time - oldTime) / 1000f;
oldTime = time;
System.out.println("update: dt = " + deltaTime + "s");
}
public void draw() {
// update the scene
update();
// ...
}