Compare commits
27 Commits
9a4ef72765
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| d3dc397353 | |||
| ad56624b51 | |||
| bb24efb856 | |||
| 6f731c2885 | |||
| dc50ee48c2 | |||
| 0e66f20423 | |||
| 2ed1763b83 | |||
| abd4597f69 | |||
| d2c33b529a | |||
| 9d74dfd613 | |||
| 73c4b507e2 | |||
| 1402cbcc47 | |||
| 1aa9546b73 | |||
| fbb38d1b86 | |||
| 78efce5cb3 | |||
| 5900117194 | |||
| 1002003168 | |||
| c28ec45f8c | |||
| f13d4e3902 | |||
| eb3a5e7dd1 | |||
| 9cf088cc6c | |||
| 42f162a03a | |||
| edff4b27db | |||
| 2a66ed1062 | |||
| 1b7b9e4ffd | |||
| 31d8cac12a | |||
| 38c5021090 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@ target/
|
||||
|
||||
.classpath
|
||||
.project
|
||||
.factorypath
|
||||
|
||||
5
README.md
Normal file
5
README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Epic game
|
||||
|
||||
The game does NOTHING still
|
||||
|
||||
The setup NEVER ends
|
||||
8
assets/shaders/default.frag.glsl
Normal file
8
assets/shaders/default.frag.glsl
Normal file
@@ -0,0 +1,8 @@
|
||||
#version 330 core
|
||||
|
||||
in vec4 fColour;
|
||||
out vec4 colour;
|
||||
|
||||
void main() {
|
||||
colour = fColour;
|
||||
}
|
||||
11
assets/shaders/default.vert.glsl
Normal file
11
assets/shaders/default.vert.glsl
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec4 aColour;
|
||||
|
||||
out vec4 fColour;
|
||||
|
||||
void main() {
|
||||
fColour = aColour;
|
||||
gl_Position = vec4(aPos, 1.0);
|
||||
}
|
||||
10
assets/shaders/fader.frag.glsl
Normal file
10
assets/shaders/fader.frag.glsl
Normal file
@@ -0,0 +1,10 @@
|
||||
#version 330 core
|
||||
|
||||
in vec4 fColour;
|
||||
out vec4 colour;
|
||||
|
||||
uniform float u_alpha;
|
||||
|
||||
void main() {
|
||||
colour = vec4(fColour.xyz, 1.0 - u_alpha);
|
||||
}
|
||||
19
assets/shaders/texture.frag.glsl
Normal file
19
assets/shaders/texture.frag.glsl
Normal file
@@ -0,0 +1,19 @@
|
||||
#version 330 core
|
||||
out vec4 FragColour;
|
||||
|
||||
in vec4 ourColour;
|
||||
in vec2 TexCoord;
|
||||
|
||||
uniform sampler2D texture1;
|
||||
uniform sampler2D texture2;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex1Color = texture(texture1, TexCoord);
|
||||
vec4 tex2Color = texture(texture2, TexCoord);
|
||||
|
||||
if (tex2Color.a < 0.1)
|
||||
tex2Color = tex1Color;
|
||||
|
||||
FragColour = mix(tex1Color, tex2Color, 0.4f);
|
||||
}
|
||||
15
assets/shaders/texture.vert.glsl
Normal file
15
assets/shaders/texture.vert.glsl
Normal file
@@ -0,0 +1,15 @@
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec4 aColour;
|
||||
layout (location = 2) in vec2 aTexCoord;
|
||||
|
||||
out vec4 ourColour;
|
||||
out vec2 TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(aPos, 1.0);
|
||||
ourColour = aColour;
|
||||
TexCoord = aTexCoord;
|
||||
}
|
||||
BIN
assets/textures/flames.png
Normal file
BIN
assets/textures/flames.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
assets/textures/plink.jpg
Normal file
BIN
assets/textures/plink.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/textures/plink.png
Normal file
BIN
assets/textures/plink.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
18
pom.xml
18
pom.xml
@@ -10,7 +10,6 @@
|
||||
|
||||
<name>game</name>
|
||||
<description>A simple game.</description>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.hirw.org</url>
|
||||
|
||||
<properties>
|
||||
@@ -35,9 +34,20 @@
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>5.19.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.38</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.13.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lwjgl</groupId>
|
||||
|
||||
58
src/main/java/org/hirw/game/FaderShader.java
Normal file
58
src/main/java/org/hirw/game/FaderShader.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hirw.game.util.Log;
|
||||
import org.hirw.game.util.Time;
|
||||
|
||||
public class FaderShader extends Shader {
|
||||
private static final String FADER_SHADER_FRAG_PATH = "assets/shaders/fader.frag.glsl";
|
||||
private static final float DEFAULT_ALPHA_INCREASE_STEP = 1.0f / 3;
|
||||
private final float INITIAL_ALPHA = 0.0f;
|
||||
|
||||
@Getter private final float alphaIncreaseStep;
|
||||
@Getter @Setter private float alpha;
|
||||
@Getter @Setter private int alphaUniformLocation;
|
||||
|
||||
public FaderShader(float alphaIncreaseStep) {
|
||||
super(FADER_SHADER_FRAG_PATH, DEFAULT_VERT_PATH);
|
||||
this.alpha = INITIAL_ALPHA;
|
||||
this.alphaIncreaseStep = alphaIncreaseStep;
|
||||
}
|
||||
|
||||
public FaderShader() {
|
||||
this(DEFAULT_ALPHA_INCREASE_STEP);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
super.init();
|
||||
findAlphaUniformLocation();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
super.update();
|
||||
increaseAlpha();
|
||||
}
|
||||
|
||||
private void findAlphaUniformLocation() {
|
||||
int location = glGetUniformLocation(getShaderProgramID(), "u_alpha");
|
||||
if (location == -1) {
|
||||
Log.warning("FaderShader", "Failed to get 'u_alpha' uniform location, trying to continue...");
|
||||
} else {
|
||||
setAlphaUniformLocation(location);
|
||||
}
|
||||
}
|
||||
|
||||
private void increaseAlpha() {
|
||||
if (getAlphaUniformLocation() == -1) return;
|
||||
if (getAlpha() >= 1.0f) return;
|
||||
|
||||
setAlpha(getAlpha() + (getAlphaIncreaseStep() * Time.deltaTime()));
|
||||
|
||||
glUseProgram(getShaderProgramID());
|
||||
glUniform1f(getAlphaUniformLocation(), getAlpha());
|
||||
glUseProgram(0);
|
||||
}
|
||||
}
|
||||
37
src/main/java/org/hirw/game/Keyboard.java
Normal file
37
src/main/java/org/hirw/game/Keyboard.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Keyboard {
|
||||
private static Keyboard instance;
|
||||
private HashMap<Integer, Boolean> buttons;
|
||||
|
||||
private Keyboard() {
|
||||
this.buttons = new HashMap<Integer, Boolean>();
|
||||
}
|
||||
|
||||
public static Keyboard getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (Keyboard.class) {
|
||||
if (instance == null) {
|
||||
instance = new Keyboard();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void keyCallback(long window, int keyCode, int _scan, int action, int _modifiers) {
|
||||
if (action == GLFW_PRESS) {
|
||||
getInstance().buttons.put(keyCode, true);
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
getInstance().buttons.put(keyCode, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPressed(int keyCode) {
|
||||
return getInstance().buttons.getOrDefault(keyCode, false);
|
||||
}
|
||||
}
|
||||
118
src/main/java/org/hirw/game/Mesh.java
Normal file
118
src/main/java/org/hirw/game/Mesh.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import static org.lwjgl.opengl.ARBVertexArrayObject.glBindVertexArray;
|
||||
import static org.lwjgl.opengl.ARBVertexArrayObject.glGenVertexArrays;
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
public class Mesh {
|
||||
@Getter @Setter private int vaoID, vboID, eboID;
|
||||
@Getter protected Shader shader;
|
||||
@Getter float[] vertices;
|
||||
@Getter int[] elements;
|
||||
|
||||
protected final int POSITION_SIZE = 3;
|
||||
protected final int RGBA_SIZE = 4;
|
||||
protected final int FLOAT_SIZE_IN_BYTES = Float.SIZE / Byte.SIZE;
|
||||
|
||||
protected static final float[] DEFAULT_VERTEX_ARRAY = {
|
||||
0.5f, -0.5f, 0.0f, /* */ 1.0f, 0.0f, 0.0f, 1.0f,
|
||||
-0.5f, 0.5f, 0.0f, /* */ 0.0f, 1.0f, 0.0f, 1.0f,
|
||||
0.5f, 0.5f, 0.0f, /* */ 0.0f, 0.0f, 1.0f, 1.0f,
|
||||
-0.5f, -0.5f, 0.0f, /* */ 1.0f, 1.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
protected static final int[] DEFAULT_ELEMENT_ARRAY = {
|
||||
2, 1, 0,
|
||||
0, 1, 3
|
||||
};
|
||||
|
||||
public Mesh(Shader shader, float[] vertexArray, int[] elementArray) {
|
||||
this.shader = shader;
|
||||
this.vaoID = -1;
|
||||
this.vboID = -1;
|
||||
this.eboID = -1;
|
||||
this.vertices = vertexArray;
|
||||
this.elements = elementArray;
|
||||
}
|
||||
|
||||
public Mesh(Shader shader) {
|
||||
this(shader, DEFAULT_VERTEX_ARRAY, DEFAULT_ELEMENT_ARRAY);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
initialiseVertexArrayObject();
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
getShader().use();
|
||||
glDraw();
|
||||
getShader().detach();
|
||||
}
|
||||
|
||||
public void log() {
|
||||
System.out.println(this.toString());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"VAO: %d, VBO: %d, EBO: %d, ShaderProgram: %d",
|
||||
getEboID(), getVboID(), getEboID(), getShader().getShaderProgramID());
|
||||
}
|
||||
|
||||
protected void glDraw() {
|
||||
glBindVertexArray(this.vaoID);
|
||||
glDrawElements(GL_TRIANGLES, getElements().length, GL_UNSIGNED_INT, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
private void initialiseVertexArrayObject() {
|
||||
setVaoID(glGenVertexArrays());
|
||||
glBindVertexArray(getVaoID());
|
||||
|
||||
initialiseVertexBufferObject();
|
||||
initialiseElementBufferObject();
|
||||
|
||||
initialiseAttribPointers();
|
||||
}
|
||||
|
||||
protected int initialiseAttribPointers() {
|
||||
int vertexSizeInBytes = calculateVertexSizeInBytes();
|
||||
|
||||
glVertexAttribPointer(0, POSITION_SIZE, GL_FLOAT, false, vertexSizeInBytes, 0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glVertexAttribPointer(
|
||||
1, RGBA_SIZE, GL_FLOAT, false, vertexSizeInBytes, POSITION_SIZE * FLOAT_SIZE_IN_BYTES);
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
return vertexSizeInBytes;
|
||||
}
|
||||
|
||||
protected int calculateVertexSizeInBytes() {
|
||||
return (POSITION_SIZE + RGBA_SIZE) * FLOAT_SIZE_IN_BYTES;
|
||||
}
|
||||
|
||||
private void initialiseVertexBufferObject() {
|
||||
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(getVertices().length);
|
||||
vertexBuffer.put(getVertices()).flip();
|
||||
|
||||
setVboID(glGenBuffers());
|
||||
glBindBuffer(GL_ARRAY_BUFFER, getVboID());
|
||||
glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
private void initialiseElementBufferObject() {
|
||||
IntBuffer elementBuffer = BufferUtils.createIntBuffer(getElements().length);
|
||||
elementBuffer.put(getElements()).flip();
|
||||
|
||||
setEboID(glGenBuffers());
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, getEboID());
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementBuffer, GL_STATIC_DRAW);
|
||||
}
|
||||
}
|
||||
67
src/main/java/org/hirw/game/Mouse.java
Normal file
67
src/main/java/org/hirw/game/Mouse.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Mouse {
|
||||
private double x, y, oldX, oldY;
|
||||
private HashMap<Integer, Boolean> buttons;
|
||||
|
||||
private static Mouse instance;
|
||||
|
||||
public interface Buttons {
|
||||
public final int LEFT = GLFW_MOUSE_BUTTON_LEFT;
|
||||
public final int RIGHT = GLFW_MOUSE_BUTTON_RIGHT;
|
||||
}
|
||||
|
||||
private Mouse() {
|
||||
this.buttons = new HashMap<Integer, Boolean>();
|
||||
}
|
||||
|
||||
public static Mouse getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (Mouse.class) { // Double checked locking
|
||||
if (instance == null) {
|
||||
instance = new Mouse();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void cursorPositionCallback(long window, double newX, double newY) {
|
||||
getInstance().oldX = getInstance().x;
|
||||
getInstance().oldY = getInstance().y;
|
||||
getInstance().x = newX;
|
||||
getInstance().y = newY;
|
||||
}
|
||||
|
||||
public static void mouseButtonCallback(long window, int keyCode, int action, int _modifiers) {
|
||||
if (action == GLFW_PRESS) {
|
||||
getInstance().buttons.put(keyCode, true);
|
||||
} else if (action == GLFW_RELEASE) {
|
||||
getInstance().buttons.put(keyCode, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static double getX() {
|
||||
return getInstance().x;
|
||||
}
|
||||
|
||||
public static double getY() {
|
||||
return getInstance().y;
|
||||
}
|
||||
|
||||
public static double getDeltaX() {
|
||||
return getInstance().oldX - getInstance().x;
|
||||
}
|
||||
|
||||
public static double getDeltaY() {
|
||||
return getInstance().oldY - getInstance().y;
|
||||
}
|
||||
|
||||
public static boolean isPressed(int keyCode) {
|
||||
return getInstance().buttons.getOrDefault(keyCode, false);
|
||||
}
|
||||
}
|
||||
19
src/main/java/org/hirw/game/Scene.java
Normal file
19
src/main/java/org/hirw/game/Scene.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public abstract class Scene {
|
||||
@Getter private final SceneType sceneType;
|
||||
|
||||
public Scene(SceneType sceneType) {
|
||||
this.sceneType = sceneType;
|
||||
}
|
||||
|
||||
public abstract void init();
|
||||
|
||||
public abstract void update();
|
||||
|
||||
public String toString() {
|
||||
return getSceneType().toString();
|
||||
}
|
||||
}
|
||||
56
src/main/java/org/hirw/game/SceneManager.java
Normal file
56
src/main/java/org/hirw/game/SceneManager.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import org.hirw.game.util.Log;
|
||||
|
||||
public final class SceneManager {
|
||||
private static final SceneType DEFAULT_SCENE_TYPE = SceneType.SPLASH;
|
||||
|
||||
private static final EnumMap<SceneType, Scene> SCENES =
|
||||
new EnumMap<>(
|
||||
Map.of(
|
||||
SceneType.SPLASH, new SplashScene(),
|
||||
SceneType.MENU, new SplashScene(),
|
||||
SceneType.GAME, new SplashScene()));
|
||||
|
||||
@Getter private static Scene scene;
|
||||
|
||||
public static void init() {
|
||||
setScene(DEFAULT_SCENE_TYPE);
|
||||
}
|
||||
|
||||
public static void setScene(SceneType sceneType) {
|
||||
Scene newScene = SCENES.get(sceneType);
|
||||
|
||||
if (scene == newScene) {
|
||||
Log.warning("SceneManager", sameSceneWarningString());
|
||||
return;
|
||||
} else {
|
||||
scene = newScene;
|
||||
}
|
||||
|
||||
scene.init();
|
||||
logSceneChange();
|
||||
}
|
||||
|
||||
public static void update() {
|
||||
if (scene.getSceneType() == null) return;
|
||||
|
||||
getScene().update();
|
||||
}
|
||||
|
||||
private static void logSceneChange() {
|
||||
SceneType newSceneType = getScene().getSceneType();
|
||||
String loadedSceneMessage = String.format("Changed to scene '%s'", newSceneType.toString());
|
||||
|
||||
Log.success("SceneManager", loadedSceneMessage);
|
||||
}
|
||||
|
||||
private static String sameSceneWarningString() {
|
||||
return String.format(
|
||||
"Tried to switch to scene '%s' but that scene is already loaded",
|
||||
scene.getSceneType().toString());
|
||||
}
|
||||
}
|
||||
7
src/main/java/org/hirw/game/SceneType.java
Normal file
7
src/main/java/org/hirw/game/SceneType.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package org.hirw.game;
|
||||
|
||||
public enum SceneType {
|
||||
SPLASH,
|
||||
MENU,
|
||||
GAME,
|
||||
}
|
||||
128
src/main/java/org/hirw/game/Shader.java
Normal file
128
src/main/java/org/hirw/game/Shader.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hirw.game.util.Log;
|
||||
|
||||
public class Shader {
|
||||
private enum ShaderType {
|
||||
FRAG,
|
||||
VERT
|
||||
}
|
||||
|
||||
private static final EnumMap<ShaderType, Integer> SHADERS =
|
||||
new EnumMap<>(
|
||||
Map.of(
|
||||
ShaderType.FRAG, GL_FRAGMENT_SHADER,
|
||||
ShaderType.VERT, GL_VERTEX_SHADER));
|
||||
|
||||
protected static final String DEFAULT_FRAG_PATH = "assets/shaders/default.frag.glsl";
|
||||
protected static final String DEFAULT_VERT_PATH = "assets/shaders/default.vert.glsl";
|
||||
|
||||
@Getter private String vertexSource;
|
||||
@Getter private String fragmentSource;
|
||||
@Getter @Setter private int vertexID;
|
||||
@Getter @Setter private int fragmentID;
|
||||
@Getter @Setter private int shaderProgramID;
|
||||
|
||||
public Shader(String fragPath, String vertPath) {
|
||||
this.fragmentSource = readFromFile(fragPath);
|
||||
this.vertexSource = readFromFile(vertPath);
|
||||
}
|
||||
|
||||
public Shader(String path) {
|
||||
this(path + ".frag.glsl", path + ".vert.glsl");
|
||||
}
|
||||
|
||||
public Shader() {
|
||||
this(DEFAULT_FRAG_PATH, DEFAULT_VERT_PATH);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
compileShader(ShaderType.FRAG);
|
||||
compileShader(ShaderType.VERT);
|
||||
createProgram();
|
||||
}
|
||||
|
||||
public void use() {
|
||||
glUseProgram(getShaderProgramID());
|
||||
}
|
||||
|
||||
public void detach() {
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
public void update() {}
|
||||
|
||||
private void compileShader(ShaderType shaderType) {
|
||||
int shaderID = glCreateShader(SHADERS.get(shaderType));
|
||||
|
||||
switch (shaderType) {
|
||||
case ShaderType.FRAG -> {
|
||||
setFragmentID(shaderID);
|
||||
glShaderSource(shaderID, getFragmentSource());
|
||||
}
|
||||
case ShaderType.VERT -> {
|
||||
setVertexID(shaderID);
|
||||
glShaderSource(shaderID, getVertexSource());
|
||||
}
|
||||
}
|
||||
|
||||
glCompileShader(shaderID);
|
||||
|
||||
if (glGetShaderi(shaderID, GL_COMPILE_STATUS) == GL_FALSE) {
|
||||
int len = glGetShaderi(shaderID, GL_INFO_LOG_LENGTH);
|
||||
Log.error(
|
||||
"Shader",
|
||||
String.format(
|
||||
"Failed to compile %s shader: %s",
|
||||
shaderType.toString(), glGetShaderInfoLog(shaderID, len)));
|
||||
}
|
||||
}
|
||||
|
||||
private void createProgram() {
|
||||
int id = glCreateProgram();
|
||||
glAttachShader(id, getVertexID());
|
||||
glAttachShader(id, getFragmentID());
|
||||
glLinkProgram(id);
|
||||
|
||||
int success = glGetProgrami(id, GL_LINK_STATUS);
|
||||
if (success == GL_FALSE) {
|
||||
int len = glGetProgrami(id, GL_INFO_LOG_LENGTH);
|
||||
|
||||
Log.error(
|
||||
"Shader",
|
||||
String.format("Failed to create Shader Program: %s", glGetShaderInfoLog(id, len)));
|
||||
} else {
|
||||
Log.success("Shader", "Shader Program created.");
|
||||
}
|
||||
|
||||
setShaderProgramID(id);
|
||||
}
|
||||
|
||||
private String readFromFile(String stringFilePath) {
|
||||
String source = "";
|
||||
|
||||
try {
|
||||
Path filePath = Paths.get(stringFilePath);
|
||||
source = Files.readString(filePath);
|
||||
|
||||
} catch (NoSuchFileException | InvalidPathException e) {
|
||||
Log.error("Shader", "Couldn't open file (probably a bad path): " + stringFilePath);
|
||||
} catch (IOException e) {
|
||||
Log.error("Shader", "An IO Exception occured while reading from file: " + stringFilePath);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
}
|
||||
72
src/main/java/org/hirw/game/SplashScene.java
Normal file
72
src/main/java/org/hirw/game/SplashScene.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class SplashScene extends Scene {
|
||||
@Getter public final SceneType SCENE_TYPE = SceneType.SPLASH;
|
||||
|
||||
@Getter @Setter private Mesh screenCover;
|
||||
@Getter @Setter private FaderShader screenCoverFaderShader;
|
||||
@Getter @Setter private Mesh logo;
|
||||
|
||||
public SplashScene() {
|
||||
super(SceneType.SPLASH);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
createScreenCover();
|
||||
createLogo();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
getLogo().draw();
|
||||
getScreenCoverFaderShader().update();
|
||||
getScreenCover().draw();
|
||||
}
|
||||
|
||||
private void createScreenCover() {
|
||||
FaderShader faderShader = new FaderShader();
|
||||
faderShader.init();
|
||||
Mesh screenCover = new Mesh(faderShader, screenCoverRectVertices, screenCoverRectElements);
|
||||
screenCover.init();
|
||||
setScreenCoverFaderShader(faderShader);
|
||||
setScreenCover(screenCover);
|
||||
}
|
||||
|
||||
private void createLogo() {
|
||||
Shader texturedShader = new Shader("assets/shaders/texture");
|
||||
texturedShader.init();
|
||||
Texture logoTexture = new Texture(225, 225, "assets/textures/plink.png");
|
||||
Texture flameTexture = new Texture(2500, 2500, "assets/textures/flames.png");
|
||||
logoTexture.init();
|
||||
flameTexture.init();
|
||||
Mesh logoMesh =
|
||||
new TexturedMesh(
|
||||
texturedShader,
|
||||
new Texture[] {logoTexture, flameTexture},
|
||||
logoRectVertices,
|
||||
screenCoverRectElements);
|
||||
logoMesh.init();
|
||||
setLogo(logoMesh);
|
||||
}
|
||||
|
||||
private static final float[] screenCoverRectVertices = {
|
||||
1.0f, 1.0f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, // 0 Top Right
|
||||
1.0f, -1.0f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, // 1 Bottom Right
|
||||
-1.0f, -1.0f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, // 2 Bottom Left
|
||||
-1.0f, 1.0f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, // 3 Top Left
|
||||
};
|
||||
|
||||
private static final int[] screenCoverRectElements = {
|
||||
0, 1, 3,
|
||||
1, 2, 3
|
||||
};
|
||||
|
||||
private static final float[] logoRectVertices = {
|
||||
0.1f, 0.1f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, /* */ 1.0f, 1.0f, // 0 Top Right
|
||||
0.1f, -0.1f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, /* */ 1.0f, 0.0f, // 1 Bottom Right
|
||||
-0.1f, -0.1f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, /* */ 0.0f, 0.0f, // 2 Bottom Left
|
||||
-0.1f, 0.1f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, /* */ 0.0f, 1.0f, // 3 Top Left
|
||||
};
|
||||
}
|
||||
105
src/main/java/org/hirw/game/Texture.java
Normal file
105
src/main/java/org/hirw/game/Texture.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL12.*;
|
||||
import static org.lwjgl.stb.STBImage.stbi_image_free;
|
||||
import static org.lwjgl.stb.STBImage.stbi_load;
|
||||
import static org.lwjgl.stb.STBImage.stbi_set_flip_vertically_on_load;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class Texture {
|
||||
@Getter private int width;
|
||||
@Getter private int height;
|
||||
@Getter private String texturePath;
|
||||
@Getter @Setter private int textureID;
|
||||
@Getter @Setter private int channelCount;
|
||||
@Getter @Setter private int internalFormat;
|
||||
|
||||
public Texture(int width, int height, String texturePath) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.texturePath = texturePath;
|
||||
guessChannelCountAndInternalFormat();
|
||||
}
|
||||
|
||||
private void guessChannelCountAndInternalFormat() {
|
||||
int fileExtensionIndex = getTexturePath().lastIndexOf(".");
|
||||
if (fileExtensionIndex == -1) {
|
||||
// No file extension, so just assume 4 channels (.png -> RGBA)
|
||||
setChannelCount(4);
|
||||
setInternalFormat(GL_RGBA);
|
||||
return;
|
||||
}
|
||||
|
||||
String fileExtension = getTexturePath().substring(fileExtensionIndex + 1);
|
||||
switch (fileExtension) {
|
||||
case "png":
|
||||
setChannelCount(4);
|
||||
setInternalFormat(GL_RGBA);
|
||||
break;
|
||||
case "jpg":
|
||||
setChannelCount(3);
|
||||
setInternalFormat(GL_RGB);
|
||||
break;
|
||||
default:
|
||||
// just pray at this point
|
||||
setChannelCount(3);
|
||||
setInternalFormat(GL_RGB);
|
||||
}
|
||||
}
|
||||
|
||||
public void init() {
|
||||
ByteBuffer imageBytes = loadImageBytes();
|
||||
createTexture(imageBytes);
|
||||
stbi_image_free(imageBytes);
|
||||
}
|
||||
|
||||
private ByteBuffer loadImageBytes() {
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
return stbi_load(
|
||||
getTexturePath(),
|
||||
new int[getWidth()],
|
||||
new int[getHeight()],
|
||||
new int[getChannelCount()],
|
||||
getChannelCount());
|
||||
}
|
||||
|
||||
private void createTexture(ByteBuffer imageBytes) {
|
||||
setTextureID(glGenTextures());
|
||||
glBindTexture(GL_TEXTURE_2D, getTextureID());
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
|
||||
setupFilterParameters();
|
||||
setupWrapParameters();
|
||||
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
getInternalFormat(),
|
||||
getWidth(),
|
||||
getHeight(),
|
||||
0,
|
||||
getInternalFormat(),
|
||||
GL_UNSIGNED_BYTE,
|
||||
imageBytes);
|
||||
// glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
private void setupFilterParameters() {
|
||||
// https://github.com/mattdesl/lwjgl-basics/wiki/textures#texture-parameters
|
||||
// Set the minification and Magnifiation filters:
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
private void setupWrapParameters() {
|
||||
// https://github.com/mattdesl/lwjgl-basics/wiki/textures#texture-parameters
|
||||
// Each Vertex has many attributes, including Position (x, y) and Texture Coordinates (s, t).
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
}
|
||||
47
src/main/java/org/hirw/game/TexturedMesh.java
Normal file
47
src/main/java/org/hirw/game/TexturedMesh.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL20.*;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class TexturedMesh extends Mesh {
|
||||
@Getter private Texture[] textures;
|
||||
|
||||
private final int TEXTURE_COORDS_SIZE = 2;
|
||||
|
||||
public TexturedMesh(Shader shader, Texture[] textures, float[] vertexArray, int[] elementArray) {
|
||||
super(shader, vertexArray, elementArray);
|
||||
this.textures = textures;
|
||||
}
|
||||
|
||||
public TexturedMesh(Shader shader, Texture[] textures) {
|
||||
this(shader, textures, DEFAULT_VERTEX_ARRAY, DEFAULT_ELEMENT_ARRAY);
|
||||
}
|
||||
|
||||
protected int initialiseAttribPointers() {
|
||||
int vertexSizeInBytes = super.initialiseAttribPointers();
|
||||
|
||||
int offset = (POSITION_SIZE + RGBA_SIZE) * FLOAT_SIZE_IN_BYTES;
|
||||
glVertexAttribPointer(2, TEXTURE_COORDS_SIZE, GL_FLOAT, false, vertexSizeInBytes, offset);
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
return vertexSizeInBytes;
|
||||
}
|
||||
|
||||
protected int calculateVertexSizeInBytes() {
|
||||
return (POSITION_SIZE + RGBA_SIZE + TEXTURE_COORDS_SIZE) * FLOAT_SIZE_IN_BYTES;
|
||||
}
|
||||
|
||||
protected void glDraw() {
|
||||
for (int i = 0; i < textures.length; i++) {
|
||||
int textureUnitIndexStart = GL_TEXTURE0;
|
||||
int textureUnitIndex = textureUnitIndexStart + i;
|
||||
glActiveTexture(textureUnitIndex);
|
||||
glBindTexture(GL_TEXTURE_2D, getTextures()[i].getTextureID());
|
||||
String uniformName = "texture" + Integer.toString(i + 1);
|
||||
glUniform1i(glGetUniformLocation(getShader().getShaderProgramID(), uniformName), i);
|
||||
}
|
||||
super.glDraw();
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,25 @@ package org.hirw.game;
|
||||
import static org.lwjgl.glfw.Callbacks.*;
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.system.MemoryStack.*;
|
||||
import static org.lwjgl.system.MemoryUtil.*;
|
||||
|
||||
import java.nio.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hirw.game.util.Time;
|
||||
import org.lwjgl.Version;
|
||||
import org.lwjgl.glfw.*;
|
||||
import org.lwjgl.opengl.*;
|
||||
import org.lwjgl.system.*;
|
||||
|
||||
public class Window {
|
||||
private int width, height;
|
||||
private final String title;
|
||||
private long glfwWindow;
|
||||
@Getter private String title;
|
||||
@Getter private long glfwWindow;
|
||||
|
||||
private static Window window = null;
|
||||
|
||||
@Getter @Setter private Shader shader;
|
||||
@Getter @Setter private Mesh mesh;
|
||||
|
||||
private Window() {
|
||||
this.width = 1280;
|
||||
this.height = 720;
|
||||
@@ -26,25 +29,31 @@ public class Window {
|
||||
}
|
||||
|
||||
public static Window get() {
|
||||
if (Window.window == null) {
|
||||
Window.window = new Window();
|
||||
if (window == null) {
|
||||
window = new Window();
|
||||
}
|
||||
|
||||
return Window.window;
|
||||
return window;
|
||||
}
|
||||
|
||||
public void blastOff() {
|
||||
logVersion();
|
||||
setup();
|
||||
init();
|
||||
loop();
|
||||
cleanUp();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
logVersion();
|
||||
createWindow();
|
||||
createShader();
|
||||
createMesh();
|
||||
SceneManager.init();
|
||||
}
|
||||
|
||||
private void logVersion() {
|
||||
System.out.println("LWJGL Version: " + Version.getVersion());
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
private void createWindow() {
|
||||
GLFWErrorCallback.createPrint(System.err).set();
|
||||
if (!glfwInit()) throw new IllegalStateException("Unable to initialize GLFW");
|
||||
|
||||
@@ -52,36 +61,46 @@ public class Window {
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
|
||||
glfwWindow = glfwCreateWindow(this.width, this.height, "Hello World!", NULL, NULL);
|
||||
glfwWindow = glfwCreateWindow(this.width, this.height, getTitle(), NULL, NULL);
|
||||
if (glfwWindow == NULL) throw new RuntimeException("Failed to create the GLFW window");
|
||||
|
||||
// glfwSetKeyCallback(
|
||||
// glfwWindow,
|
||||
// (glfwWindow, key, scancode, action, mods) -> {
|
||||
// if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE)
|
||||
// glfwSetWindowShouldClose(glfwWindow, true);
|
||||
// });
|
||||
//
|
||||
// try (MemoryStack stack = stackPush()) {
|
||||
// IntBuffer pWidth = stack.mallocInt(1);
|
||||
// IntBuffer pHeight = stack.mallocInt(1);
|
||||
// glfwGetWindowSize(glfwWindow, pWidth, pHeight);
|
||||
// GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||
// }
|
||||
glfwSetCursorPosCallback(glfwWindow, Mouse::cursorPositionCallback);
|
||||
glfwSetMouseButtonCallback(glfwWindow, Mouse::mouseButtonCallback);
|
||||
glfwSetKeyCallback(glfwWindow, Keyboard::keyCallback);
|
||||
|
||||
glfwSetWindowTitle(glfwWindow, this.title);
|
||||
glfwMakeContextCurrent(glfwWindow);
|
||||
glfwSwapInterval(1);
|
||||
glfwShowWindow(glfwWindow);
|
||||
|
||||
GL.createCapabilities();
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
private void createShader() {
|
||||
Shader shader = new Shader();
|
||||
setShader(shader);
|
||||
shader.init();
|
||||
}
|
||||
|
||||
private void createMesh() {
|
||||
Mesh mesh = new Mesh(getShader());
|
||||
setMesh(mesh);
|
||||
mesh.init();
|
||||
}
|
||||
|
||||
private void loop() {
|
||||
// GL.createCapabilities(); // Does this maybe go in here?
|
||||
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
while (!glfwWindowShouldClose(glfwWindow)) {
|
||||
Time.update();
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
getMesh().draw();
|
||||
SceneManager.update();
|
||||
glfwSwapBuffers(glfwWindow);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
68
src/main/java/org/hirw/game/util/Log.java
Normal file
68
src/main/java/org/hirw/game/util/Log.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package org.hirw.game.util;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class Log {
|
||||
private interface Colours {
|
||||
final String BLACK = "\u001B[30m";
|
||||
final String BLACK_BG = "\u001B[40m";
|
||||
final String RED = "\u001B[31m";
|
||||
final String RED_BG = "\u001B[41m";
|
||||
final String GREEN = "\u001B[32m";
|
||||
final String GREEN_BG = "\u001B[42m";
|
||||
final String YELLOW = "\u001B[33m";
|
||||
final String YELLOW_BG = "\u001B[43m";
|
||||
final String BLUE = "\u001B[34m";
|
||||
final String BLUE_BG = "\u001B[44m";
|
||||
final String PURPLE = "\u001B[35m";
|
||||
final String PURPLE_BG = "\u001B[45m";
|
||||
final String CYAN = "\u001B[36m";
|
||||
final String CYAN_BG = "\u001B[46m";
|
||||
final String WHITE = "\u001B[37m";
|
||||
final String WHITE_BG = "\u001B[47m";
|
||||
final String ANSI_RESET = "\u001B[0m";
|
||||
}
|
||||
|
||||
private static final EnumMap<LogType, String> COLOUR_FROM_LOG_TYPE =
|
||||
new EnumMap<>(
|
||||
Map.of(
|
||||
LogType.SUCCESS, Colours.GREEN,
|
||||
LogType.ERROR, Colours.RED,
|
||||
LogType.WARNING, Colours.PURPLE,
|
||||
LogType.DEBUG, Colours.WHITE));
|
||||
|
||||
public static void error(String errorStage, String errorDescription) {
|
||||
System.out.println(formatString(LogType.ERROR, errorStage, errorDescription));
|
||||
}
|
||||
|
||||
public static void success(String successStage, String successDescription) {
|
||||
System.out.println(formatString(LogType.SUCCESS, successStage, successDescription));
|
||||
}
|
||||
|
||||
public static void warning(String warningStage, String warningDescription) {
|
||||
System.out.println(formatString(LogType.WARNING, warningStage, warningDescription));
|
||||
}
|
||||
|
||||
public static void debug(String debugMessage) {
|
||||
System.out.println(formatString(LogType.DEBUG, "DEBUG", debugMessage));
|
||||
}
|
||||
|
||||
private static String formatString(LogType logType, String stage, String description) {
|
||||
String formattedType = String.format("[%s]", colouriseString(logType, logType.toString()));
|
||||
String formattedStage = colouriseString(Colours.YELLOW, String.format("<%s>", stage));
|
||||
return String.format("%s %s %s", formattedType, formattedStage, description);
|
||||
}
|
||||
|
||||
private static String colouriseString(LogType logType, String string) {
|
||||
return COLOUR_FROM_LOG_TYPE.get(logType) + string + Colours.ANSI_RESET;
|
||||
}
|
||||
|
||||
private static String colouriseString(String colour, String string) {
|
||||
return colour + string + Colours.ANSI_RESET;
|
||||
}
|
||||
|
||||
private static String colouriseString(String colour, String otherColour, String string) {
|
||||
return colour + otherColour + string + Colours.ANSI_RESET;
|
||||
}
|
||||
}
|
||||
8
src/main/java/org/hirw/game/util/LogType.java
Normal file
8
src/main/java/org/hirw/game/util/LogType.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package org.hirw.game.util;
|
||||
|
||||
public enum LogType {
|
||||
SUCCESS,
|
||||
WARNING,
|
||||
ERROR,
|
||||
DEBUG,
|
||||
}
|
||||
31
src/main/java/org/hirw/game/util/Time.java
Normal file
31
src/main/java/org/hirw/game/util/Time.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package org.hirw.game.util;
|
||||
|
||||
public final class Time {
|
||||
private static long lastTime = NanoTimer.system().nanoTime();
|
||||
private static long currentTime = NanoTimer.system().nanoTime();
|
||||
private static NanoTimer nanoTimer = NanoTimer.system();
|
||||
|
||||
public interface NanoTimer {
|
||||
long nanoTime();
|
||||
|
||||
static NanoTimer system() {
|
||||
return System::nanoTime;
|
||||
}
|
||||
}
|
||||
|
||||
static void setNanoTimer(NanoTimer timer) {
|
||||
nanoTimer = timer;
|
||||
lastTime = nanoTimer.nanoTime();
|
||||
currentTime = nanoTimer.nanoTime();
|
||||
}
|
||||
|
||||
public static void update() {
|
||||
lastTime = currentTime;
|
||||
currentTime = nanoTimer.nanoTime();
|
||||
}
|
||||
|
||||
public static float deltaTime() {
|
||||
final float ONE_SECOND = 1_000_000_000f;
|
||||
return (currentTime - lastTime) / ONE_SECOND;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package org.hirw.game;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class AppTest
|
||||
extends TestCase
|
||||
{
|
||||
/**
|
||||
* Create the test case
|
||||
*
|
||||
* @param testName name of the test case
|
||||
*/
|
||||
public AppTest( String testName )
|
||||
{
|
||||
super( testName );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the suite of tests being tested
|
||||
*/
|
||||
public static Test suite()
|
||||
{
|
||||
return new TestSuite( AppTest.class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rigourous Test :-)
|
||||
*/
|
||||
public void testApp()
|
||||
{
|
||||
assertTrue( true );
|
||||
}
|
||||
}
|
||||
64
src/test/java/org/hirw/game/util/TimeTest.java
Normal file
64
src/test/java/org/hirw/game/util/TimeTest.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package org.hirw.game.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
class TimeTest {
|
||||
final float ONE_SECOND = 1_000_000_000f;
|
||||
final long ONE_MILLISECOND = 1_000_000L;
|
||||
final float MOE = 0.00000001f; // Margin of error
|
||||
|
||||
private Time.NanoTimer nanoTimer;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
nanoTimer = Mockito.mock(Time.NanoTimer.class);
|
||||
when(nanoTimer.nanoTime()).thenReturn(0L);
|
||||
Time.setNanoTimer(nanoTimer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeltaTimeInitialState() {
|
||||
float deltaTime = Time.deltaTime();
|
||||
assertEquals(deltaTime, 0f, MOE, "should be 0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeltaTimeAfterUpdate() {
|
||||
when(nanoTimer.nanoTime()).thenReturn(ONE_MILLISECOND);
|
||||
|
||||
Time.update();
|
||||
float deltaTime = Time.deltaTime();
|
||||
|
||||
assertEquals(deltaTime, ONE_MILLISECOND / ONE_SECOND, MOE, "should reflect time difference");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleUpdates() {
|
||||
when(nanoTimer.nanoTime()).thenReturn(ONE_MILLISECOND).thenReturn(ONE_MILLISECOND * 4);
|
||||
|
||||
Time.update();
|
||||
float delta1 = Time.deltaTime();
|
||||
|
||||
Time.update();
|
||||
float delta2 = Time.deltaTime();
|
||||
|
||||
assertEquals(delta1, ONE_MILLISECOND / ONE_SECOND, MOE, "should be 1/1000th second");
|
||||
assertEquals(delta2, (ONE_MILLISECOND * 3) / ONE_SECOND, MOE, "should be 3/1000th second");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConsistentStateAcrossCalls() {
|
||||
when(nanoTimer.nanoTime()).thenReturn(ONE_MILLISECOND);
|
||||
|
||||
Time.update();
|
||||
float delta1 = Time.deltaTime();
|
||||
float delta2 = Time.deltaTime();
|
||||
|
||||
assertEquals(delta1, delta2, MOE, "should be equal delta time");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user