This is a series of notes about the ThinMatrix’s OpenGL 3D Game Tutorial. Here I concentrate on changes I made to the code to make it work with the latest LWJGL 3.3.1 and Java 17.
In headings, I will include the tutorial episode number in the format of T11, T12, etc. This is to make it easier to find the corresponding episode.
This is the second part of the series. You can find part 1 here.
I also changed RES_LOC path to src/main/resources/model/ to conform my project structure.
Then replace the following import in the Vertex class:
import org.lwjgl.util.vector.Vector3f;
with
import org.joml.Vector3f;
Now, the new OBJ loader is ready to use. But I made a few more changes to it.
Replace all occurrences of (float) Float.valueOf with Float.parseFloat in the OBJFileLoader class.
For example, the following code:
Vector3f vertex = new Vector3f((float) Float.valueOf(currentLine[1]), (float) Float.valueOf(currentLine[2]), (float) Float.valueOf(currentLine[3]));
can be changed to:
Vector3f vertex = new Vector3f(Float.parseFloat(currentLine[1]), Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
You can also simplify some ArrayList declarations by using diamonds in OBJFileLoader:
Replace
List<Vertex> vertices = new ArrayList<Vertex>(); List<Vector2f> textures = new ArrayList<Vector2f>(); List<Vector3f> normals = new ArrayList<Vector3f>(); List<Integer> indices = new ArrayList<Integer>();
with
List<Vertex> vertices = new ArrayList<>(); List<Vector2f> textures = new ArrayList<>(); List<Vector3f> normals = new ArrayList<>(); List<Integer> indices = new ArrayList<>();
In the tutorial, to load a model, before we apply the OBJFileLoader class, what we need is:
RawModel model = OBJLoader.loadObjModel("model_name", loader);
You will notice that in the tutorial, the player will shake when standing still. That is caused by a flaw in the falling check.
In tutorial, the player will stop falling only if the player’s Y is less than the terrain height. But what we really want is to stop decreasing the player’s Y once the player’s Y is equal to the terrain height.
if (super.getPosition().y <= TERRAIN_HEIGHT) { // <-- ❗️ IMPORTANT: use `<=` instead of `<` upwardsSpeed = 0; super.getPosition().y = TERRAIN_HEIGHT; } }
Moreover, I am not using isInAir flag in the tutorial. Instead, I use upwardsSpeed to check if the player is in the air. So the jumping part of the checkInput method can be simplified to:
Player.java
if (Keyboard.isKeyDown(GLFW_KEY_SPACE)) { if (upwardsSpeed == 0) { jump(); } }
Fix flickering when entity moves
When moving the entity, you will notice that the entity will flicker.
This can be easily fixed by making the following changes to DisplayManager.
First, append the following code right after any glfwWindowHint calls:
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE);
Next, disable v-sync by changing the following code:
// Enable v-sync glfwSwapInterval(1);
to
glfwSwapInterval(0); <- ❗️ Here
Finally, add the following line to updateDisplay method, after glfwSwapBuffers:
Then you can use it exactly the same way as in the tutorial.
I found that by using both L Mouse Button and R Mouse Button to control the camera’s pitch and yaw, the camera will be more responsive. So my calculatePitch and calculateAngleAroundPlayer methods are changed to:
We can add the following check to terrain and entity’s fragment shader to optimize the rendering performance when there are light sources less than the maximum amount.
Or we can add an uniform variable int lightCount to the shader program and use it to limit the loop. By doing so, we can set the lightCount to the actual number of light sources when calling loadLights method.
Note that the MAX_LIGHTS constant is a macro defined at the beginning of shader files.
By defining MAX_LIGHTS as a macro, we can easily change the allowed number of light sources in the future.
And change the while-loop in the loadShader method of ShaderProgram class to rewrite the MAX_LIGHTS macro to reflect the upper limit of light sources defined in Java code.
ShaderProgram.java
// ... privatestaticintloadShader(String file, int type){ // ... while ((line = reader.readLine()) != null) { if (line.startsWith("#define MAX_LIGHTS")) { line = "#define MAX_LIGHTS " + MAX_LIGHTS; } shaderSource.append(line).append("\n"); } // ... } // ...
And make sure the static MAX_LIGHTS constant is defined in the MasterRenderer class instead of the StaticShader or TerrainShader class.