diff --git a/src/org/dhsdev/flowerknight/FlowerKnight.java b/src/org/dhsdev/flowerknight/FlowerKnight.java index ab017c5..3a55578 100644 --- a/src/org/dhsdev/flowerknight/FlowerKnight.java +++ b/src/org/dhsdev/flowerknight/FlowerKnight.java @@ -36,6 +36,8 @@ public static void init() { // Set up GLFW. Errors should be checked here later. glfwInit(); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + // macOS needs to request a forward profile for a later // version of OpenGL explicitly. Version 3.3, to be specific. if (GetOSType.getOSType() == OSType.MACOS) { @@ -55,6 +57,19 @@ public static void init() { Shader.getSpotlightShader().registerUniform("time"); + // Register width and height for every shader + for (var shader : new Shader[] { + Shader.getGameShader(), + Shader.getSpotlightShader(), + Shader.getTrivialShader(), + }) { + shader.registerUniform("width"); + shader.registerUniform("height"); + } + + // Update shaders here after all uniforms have been registered + window.updateShadersOnResize(window.width(), window.height()); + TextureAtlas.loadAllTextures(); } @@ -68,23 +83,11 @@ public static void mainloop() { // While it's open, clear screen and check for events. while (window.isOpen()) { - Shader.getSpotlightShader().bind(); - Shader.getSpotlightShader().setUniform("time", (float) glfwGetTime()); - - Camera.updateShaders(); + window.updateNeededShaders(); GameObject.updateAll(); - window.clear(); - - // Iterate and draw all renderable object - for (Renderable renderable : Renderable.renderables) { - renderable.draw(); - } - - - // Render everything to screen at once - window.displayAllUpdates(); + window.redraw(); window.callEventHandlers(); diff --git a/src/org/dhsdev/flowerknight/gl/Window.java b/src/org/dhsdev/flowerknight/gl/Window.java index 1d1d5d7..8e05c27 100644 --- a/src/org/dhsdev/flowerknight/gl/Window.java +++ b/src/org/dhsdev/flowerknight/gl/Window.java @@ -1,5 +1,7 @@ package org.dhsdev.flowerknight.gl; +import org.dhsdev.flowerknight.game.Camera; +import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; import java.util.Objects; @@ -29,9 +31,6 @@ public class Window { */ public Window(int samples) { - // Right now the window is non-resizable. - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - // Hint MSAA glfwWindowHint(GLFW_SAMPLES, samples); @@ -42,6 +41,9 @@ public Window(int samples) { // Don't ask me what the two NULLs are for. I have no clue. handle = glfwCreateWindow(screenWidth, screenHeight, "FlowerKnight", NULL, NULL); + width = screenWidth; + height = screenHeight; + glfwMakeContextCurrent(handle); GL.createCapabilities(); @@ -49,6 +51,17 @@ public Window(int samples) { glEnable(GL_MULTISAMPLE); glfwShowWindow(handle); + + // We want the window to immediately render as it is resized - the images + // will scale correctly, but the spotlight shader will freeze and the + // will not remain a square. + glfwSetWindowSizeCallback(handle, (long window, int width, int height) -> { + // Update the shaders and render. + updateShadersOnResize(width, height); + updateNeededShaders(); + redraw(); + }); + } /** @@ -89,4 +102,80 @@ public void delete() { glfwDestroyWindow(handle); } + /** + * Update shader uniforms when the window gets resized. + * @param width the new window width + * @param height the new window height + */ + public void updateShadersOnResize(float width, float height) { + + this.width = width; + this.height = height; + + // Update width and height for every shader + for (var shader : new Shader[] { + Shader.getGameShader(), + Shader.getSpotlightShader(), + Shader.getTrivialShader(), + }) { + shader.bind(); + shader.setUniform("width" , width); + shader.setUniform("height", height); + } + + } + + /** + * Window width + */ + private float width; + + /** + * Get width + */ + public float width() { + return width; + } + + /** + * Window height + */ + private float height; + + /** + * Get height + */ + public float height() { + return height; + } + + /** + * Render everything. + */ + public void redraw() { + + this.clear(); + + // Iterate and draw all renderable object + for (Renderable renderable : Renderable.renderables) { + renderable.draw(); + } + + // Render everything to screen at once + this.displayAllUpdates(); + + } + + /** + * Update the spotlight annd camera shaders which change with every tick. + */ + public void updateNeededShaders() { + + Shader.getSpotlightShader().bind(); + Shader.getSpotlightShader().setUniform("time", (float) glfwGetTime()); + + Camera.updateShaders(); + + } + } diff --git a/src/shader/game_vert.glsl b/src/shader/game_vert.glsl index 5c17bed..e9a2f0e 100644 --- a/src/shader/game_vert.glsl +++ b/src/shader/game_vert.glsl @@ -8,11 +8,24 @@ layout (location=1) in vec2 texCoord; uniform vec2 cameraLoc; uniform float zoom; +// For a full explanation of these variables, see trivial_vert.glsl +uniform float width, height; + out vec2 outTexCoord; void main() { + gl_Position = vec4(position, 1.0); + gl_Position.xy -= cameraLoc; // Update with camera position gl_Position.xy /= zoom; + + if (width < height) { + gl_Position.y *= (width / height); + } else { + gl_Position.x *= (height / width); + } + outTexCoord = texCoord; + } diff --git a/src/shader/trivial_vert.glsl b/src/shader/trivial_vert.glsl index 3e36535..6dbdb07 100644 --- a/src/shader/trivial_vert.glsl +++ b/src/shader/trivial_vert.glsl @@ -5,7 +5,22 @@ layout (location=1) in vec2 texCoord; out vec2 outTexCoord; +uniform float width, height; + void main() { + gl_Position = vec4(position, 1.0); + + // Width and height are the width and height of the window. We want to have + // everything rendered in a square, so everything is rendered in the largest + // square possible. To do this, we scale the larger coordinate space down to + // the smaller one. + if (width < height) { + gl_Position.y *= (width / height); + } else { + gl_Position.x *= (height / width); + } + outTexCoord = texCoord; + }