diff --git a/assets/shaders/texture.frag.glsl b/assets/shaders/texture.frag.glsl new file mode 100644 index 0000000..74e5d39 --- /dev/null +++ b/assets/shaders/texture.frag.glsl @@ -0,0 +1,12 @@ +#version 330 core +out vec4 FragColour; + +in vec4 ourColour; +in vec2 TexCoord; + +uniform sampler2D ourTexture; + +void main() +{ + FragColour = texture(ourTexture, TexCoord); +} diff --git a/assets/shaders/texture.vert.glsl b/assets/shaders/texture.vert.glsl new file mode 100644 index 0000000..d5749a3 --- /dev/null +++ b/assets/shaders/texture.vert.glsl @@ -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; +} diff --git a/assets/textures/plink.jpg b/assets/textures/plink.jpg new file mode 100644 index 0000000..184ea8c Binary files /dev/null and b/assets/textures/plink.jpg differ diff --git a/assets/textures/plink.png b/assets/textures/plink.png new file mode 100644 index 0000000..cc3c681 Binary files /dev/null and b/assets/textures/plink.png differ diff --git a/src/main/java/org/hirw/game/Mesh.java b/src/main/java/org/hirw/game/Mesh.java index 0cb361a..d5e429c 100644 --- a/src/main/java/org/hirw/game/Mesh.java +++ b/src/main/java/org/hirw/game/Mesh.java @@ -16,14 +16,18 @@ public class Mesh { @Getter float[] vertices; @Getter int[] elements; - private static final float[] defaultVertexArray = { + 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, }; - private static final int[] defaultElementArray = { + protected static final int[] DEFAULT_ELEMENT_ARRAY = { 2, 1, 0, 0, 1, 3 }; @@ -38,7 +42,7 @@ public class Mesh { } public Mesh(Shader shader) { - this(shader, defaultVertexArray, defaultElementArray); + this(shader, DEFAULT_VERTEX_ARRAY, DEFAULT_ELEMENT_ARRAY); } public void init() { @@ -61,7 +65,7 @@ public class Mesh { getEboID(), getVboID(), getEboID(), getShader().getShaderProgramID()); } - private void glDraw() { + protected void glDraw() { glBindVertexArray(this.vaoID); glDrawElements(GL_TRIANGLES, getElements().length, GL_UNSIGNED_INT, 0); glBindVertexArray(0); @@ -74,16 +78,24 @@ public class Mesh { initialiseVertexBufferObject(); initialiseElementBufferObject(); - int positionsSize = 3; - int colourSize = 4; - int floatSizeBytes = 4; - int vertexSizeBytes = (positionsSize + colourSize) * floatSizeBytes; - glVertexAttribPointer(0, positionsSize, GL_FLOAT, false, vertexSizeBytes, 0); + initialiseAttribPointers(); + } + + protected int initialiseAttribPointers() { + int vertexSizeInBytes = calculateVertexSizeInBytes(); + + glVertexAttribPointer(0, POSITION_SIZE, GL_FLOAT, false, vertexSizeInBytes, 0); glEnableVertexAttribArray(0); glVertexAttribPointer( - 1, colourSize, GL_FLOAT, false, vertexSizeBytes, positionsSize * floatSizeBytes); + 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() { diff --git a/src/main/java/org/hirw/game/Shader.java b/src/main/java/org/hirw/game/Shader.java index 6f1bca6..90603a5 100644 --- a/src/main/java/org/hirw/game/Shader.java +++ b/src/main/java/org/hirw/game/Shader.java @@ -40,6 +40,10 @@ public class Shader { this.vertexSource = readFromFile(vertPath); } + public Shader(String path) { + this(path + ".frag.glsl", path + ".vert.glsl"); + } + public Shader() { this(DEFAULT_FRAG_PATH, DEFAULT_VERT_PATH); } diff --git a/src/main/java/org/hirw/game/SplashScene.java b/src/main/java/org/hirw/game/SplashScene.java index e8d52b2..b583e02 100644 --- a/src/main/java/org/hirw/game/SplashScene.java +++ b/src/main/java/org/hirw/game/SplashScene.java @@ -8,6 +8,7 @@ public class SplashScene extends Scene { @Getter @Setter private Mesh screenCover; @Getter @Setter private FaderShader screenCoverFaderShader; + @Getter @Setter private Mesh logo; public SplashScene() { super(SceneType.SPLASH); @@ -15,9 +16,11 @@ public class SplashScene extends Scene { public void init() { createScreenCover(); + createLogo(); } public void update() { + getLogo().draw(); getScreenCoverFaderShader().update(); getScreenCover().draw(); } @@ -31,15 +34,33 @@ public class SplashScene extends Scene { setScreenCover(screenCover); } + private void createLogo() { + Shader texturedShader = new Shader("assets/shaders/texture"); + texturedShader.init(); + Texture logoTexture = new Texture(225, 225, "assets/textures/plink.jpg", 3); + logoTexture.init(); + Mesh logoMesh = + new TexturedMesh(texturedShader, logoTexture, logoRectVertices, screenCoverRectElements); + logoMesh.init(); + setLogo(logoMesh); + } + private static final float[] screenCoverRectVertices = { - 1.0f, -1.0f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 1.0f, - -1.0f, 1.0f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, - -1.0f, -1.0f, 0.0f, /* */ 0.0f, 0.0f, 0.0f, 0.0f, + 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 = { - 2, 1, 0, - 0, 1, 3 + 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 }; } diff --git a/src/main/java/org/hirw/game/Texture.java b/src/main/java/org/hirw/game/Texture.java new file mode 100644 index 0000000..05e8158 --- /dev/null +++ b/src/main/java/org/hirw/game/Texture.java @@ -0,0 +1,66 @@ +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 int channelCount; + @Getter private String texturePath; + @Getter @Setter private int textureID; + + public Texture(int width, int height, String texturePath, int channelCount) { + this.width = width; + this.height = height; + this.texturePath = texturePath; + this.channelCount = channelCount; + } + + 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()], 0); + } + + 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, GL_RGB, getWidth(), getHeight(), 0, GL_RGB, 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); + } +} diff --git a/src/main/java/org/hirw/game/TexturedMesh.java b/src/main/java/org/hirw/game/TexturedMesh.java new file mode 100644 index 0000000..4a7ea5c --- /dev/null +++ b/src/main/java/org/hirw/game/TexturedMesh.java @@ -0,0 +1,41 @@ +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 texture; + + private final int TEXTURE_COORDS_SIZE = 2; + + public TexturedMesh(Shader shader, Texture texture, float[] vertexArray, int[] elementArray) { + super(shader, vertexArray, elementArray); + this.texture = texture; + } + + public TexturedMesh(Shader shader, Texture texture) { + this(shader, texture, 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() { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, getTexture().getTextureID()); + super.glDraw(); + } +} diff --git a/src/main/java/org/hirw/game/Window.java b/src/main/java/org/hirw/game/Window.java index f49d40e..1324819 100644 --- a/src/main/java/org/hirw/game/Window.java +++ b/src/main/java/org/hirw/game/Window.java @@ -14,7 +14,7 @@ import org.lwjgl.opengl.*; public class Window { private int width, height; - private final String title; + @Getter private String title; @Getter private long glfwWindow; private static Window window = null; @@ -61,7 +61,7 @@ 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"); glfwSetCursorPosCallback(glfwWindow, Mouse::cursorPositionCallback); @@ -76,6 +76,8 @@ public class Window { 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);