diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java index 987113b..373db8d 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java +++ b/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java @@ -6,12 +6,9 @@ import nl.andrewl.aos2_client.config.ClientConfig; import nl.andrewl.aos2_client.model.ClientPlayer; import nl.andrewl.aos2_client.render.chunk.ChunkRenderer; import nl.andrewl.aos2_client.render.gui.GuiRenderer; -import nl.andrewl.aos2_client.render.gui.GUITexture; import nl.andrewl.aos2_client.render.model.Model; import nl.andrewl.aos_core.model.Team; import nl.andrewl.aos_core.model.item.BlockItemStack; -import nl.andrewl.aos_core.model.item.Gun; -import nl.andrewl.aos_core.model.item.GunItemStack; import nl.andrewl.aos_core.model.item.ItemTypes; import org.joml.Matrix3f; import org.joml.Matrix4f; @@ -53,13 +50,6 @@ public class GameRenderer { private Model shotgunModel; private Model flagModel; - // Standard GUI textures. - private GUITexture crosshairTexture; - private GUITexture clipTexture; - private GUITexture bulletTexture; - private GUITexture healthBarRedTexture; - private GUITexture healthBarGreenTexture; - private long windowHandle; private int screenWidth = 800; private int screenHeight = 600; @@ -134,16 +124,6 @@ public class GameRenderer { } catch (IOException e) { throw new RuntimeException(e); } - crosshairTexture = new GUITexture("gui/crosshair.png"); - clipTexture = new GUITexture("gui/clip.png"); - bulletTexture = new GUITexture("gui/bullet.png"); - healthBarRedTexture = new GUITexture("gui/health-red.png"); - healthBarGreenTexture = new GUITexture("gui/health-green.png"); - guiRenderer.addTexture("crosshair", crosshairTexture); - guiRenderer.addTexture("clip", clipTexture); - guiRenderer.addTexture("bullet", bulletTexture); - guiRenderer.addTexture("health-red", healthBarRedTexture); - guiRenderer.addTexture("health-green", healthBarGreenTexture); log.debug("Initialized GUI renderer."); this.modelRenderer = new ModelRenderer(); @@ -183,10 +163,6 @@ public class GameRenderer { if (modelRenderer != null) modelRenderer.setPerspective(data); } - public Matrix4f getPerspectiveTransform() { - return perspectiveTransform; - } - public boolean windowShouldClose() { return glfwWindowShouldClose(windowHandle); } @@ -290,45 +266,7 @@ public class GameRenderer { // GUI rendering guiRenderer.start(); - guiRenderer.draw(crosshairTexture, crosshairTexture.getIdealScaleX(32, screenWidth), crosshairTexture.getIdealScaleY(32, screenHeight), 0, 0); - // If we're holding a gun, draw clip and bullet graphics. - if (client.getMyPlayer().getInventory().getSelectedItemStack().getType() instanceof Gun) { - GunItemStack stack = (GunItemStack) client.getMyPlayer().getInventory().getSelectedItemStack(); - for (int i = 0; i < stack.getClipCount(); i++) { - guiRenderer.draw( - clipTexture, - clipTexture.getIdealScaleX(64, screenWidth), - clipTexture.getIdealScaleY(clipTexture.getIdealHeight(64), screenHeight), - 0.90f, - -0.90f + (i * 0.15f) - ); - } - for (int i = 0; i < stack.getBulletCount(); i++) { - guiRenderer.draw( - bulletTexture, - bulletTexture.getIdealScaleX(16, screenWidth), - bulletTexture.getIdealScaleY(bulletTexture.getIdealHeight(16), screenHeight), - 0.80f - (i * 0.05f), - -0.90f - ); - } - } - // Render the player's health. - guiRenderer.draw( - healthBarRedTexture, - healthBarRedTexture.getIdealScaleX(64, screenWidth), - healthBarRedTexture.getIdealScaleY(16, screenHeight), - -0.90f, - -0.90f - ); - guiRenderer.draw( - healthBarGreenTexture, - healthBarGreenTexture.getIdealScaleX(64 * client.getMyPlayer().getHealth(), screenWidth), - healthBarGreenTexture.getIdealScaleY(16, screenHeight), - -0.90f, - -0.90f - ); - guiRenderer.drawNvg(screenWidth, screenHeight); + guiRenderer.drawNvg(screenWidth, screenHeight, myPlayer); guiRenderer.end(); glfwSwapBuffers(windowHandle); diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUITexture.java b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUITexture.java index 56a1ba0..91cf40b 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUITexture.java +++ b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUITexture.java @@ -1,5 +1,6 @@ package nl.andrewl.aos2_client.render.gui; +import nl.andrewl.aos_core.FileUtils; import nl.andrewl.aos_core.ImageUtils; import org.joml.Vector2f; @@ -23,23 +24,21 @@ public class GUITexture { */ private final int height; - public GUITexture(String location) { - try (var in = GUITexture.class.getClassLoader().getResourceAsStream(location)) { - if (in == null) throw new IOException("Couldn't load texture image from " + location); - BufferedImage img = ImageIO.read(in); - width = img.getWidth(); - height = img.getHeight(); + public GUITexture(String location) throws IOException { + this(FileUtils.readClasspathImage(location)); + } - textureId = glGenTextures(); - glBindTexture(GL_TEXTURE_2D, textureId); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - var buf = ImageUtils.decodePng(img); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); - } catch (IOException e) { - throw new RuntimeException("Failed to load GUI texture.", e); - } + public GUITexture(BufferedImage img) { + width = img.getWidth(); + height = img.getHeight(); + + textureId = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, textureId); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + var buf = ImageUtils.decodePng(img); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); } public int getWidth() { diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java index 4c2cd13..da60c88 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java +++ b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java @@ -1,8 +1,12 @@ package nl.andrewl.aos2_client.render.gui; +import nl.andrewl.aos2_client.model.ClientPlayer; import nl.andrewl.aos2_client.render.ShaderProgram; -import nl.andrewl.aos2_client.util.ResourceUtils; import nl.andrewl.aos_core.FileUtils; +import nl.andrewl.aos_core.model.item.BlockItem; +import nl.andrewl.aos_core.model.item.BlockItemStack; +import nl.andrewl.aos_core.model.item.Gun; +import nl.andrewl.aos_core.model.item.GunItemStack; import org.joml.Matrix4f; import org.lwjgl.BufferUtils; import org.lwjgl.nanovg.NVGColor; @@ -35,6 +39,7 @@ public class GuiRenderer { private static final NVGPaint paintB = NVGPaint.create(); private static final NVGPaint paintC = NVGPaint.create(); + // Simple 2d texture quad information. private final int vaoId; private final int vboId; private final int vertexCount; @@ -85,7 +90,11 @@ public class GuiRenderer { } public void loadTexture(String name, String resource) { - textures.put(name, new GUITexture(resource)); + try { + textures.put(name, new GUITexture(resource)); + } catch (IOException e) { + throw new RuntimeException(e); + } } public void addTexture(String name, GUITexture texture) { @@ -102,10 +111,6 @@ public class GuiRenderer { glUniform1i(textureSamplerUniform, 0); } - public void draw(String name, float scaleX, float scaleY, float x, float y) { - draw(textures.get(name), scaleX, scaleY, x, y); - } - public void draw(GUITexture texture, float scaleX, float scaleY, float x, float y) { glActiveTexture(0); transformMatrix.identity() @@ -117,13 +122,19 @@ public class GuiRenderer { glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount); } - public void drawNvg(float width, float height) { + public void drawNvg(float width, float height, ClientPlayer player) { nvgBeginFrame(vgId, width, height, width / height); nvgSave(vgId); nvgFontSize(vgId, 60f); nvgFontFaceId(vgId, jetbrainsMonoFont); nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP); - nvgText(vgId, 50, 50, "Hello world!"); + nvgFillColor(vgId, GuiUtils.rgba(1, 0, 0, 1, colorA)); + nvgText(vgId, 5, 5, "Hello world!"); + + drawCrosshair(width, height); + drawHealthBar(width, height, player); + drawHeldItemStackInfo(width, height, player); + nvgRestore(vgId); nvgEndFrame(vgId); } @@ -145,5 +156,73 @@ public class GuiRenderer { shaderProgram.free(); } + private void drawCrosshair(float w, float h) { + float cx = w / 2f; + float cy = h / 2f; + nvgStrokeColor(vgId, GuiUtils.rgba(1, 1, 1, 0.25f, colorA)); + nvgBeginPath(vgId); + nvgMoveTo(vgId, cx - 10, cy); + nvgLineTo(vgId, cx + 10, cy); + nvgMoveTo(vgId, cx, cy - 10); + nvgLineTo(vgId, cx, cy + 10); + nvgStroke(vgId); + } + + private void drawHealthBar(float w, float h, ClientPlayer player) { + nvgFillColor(vgId, GuiUtils.rgba(1, 0, 0, 1, colorA)); + nvgBeginPath(vgId); + nvgRect(vgId, 20, h - 60, 100, 20); + nvgFill(vgId); + nvgFillColor(vgId, GuiUtils.rgba(0, 1, 0, 1, colorA)); + nvgBeginPath(vgId); + nvgRect(vgId, 20, h - 60, 100 * player.getHealth(), 20); + nvgFill(vgId); + + nvgFillColor(vgId, GuiUtils.rgba(1, 1, 1, 1, colorA)); + nvgFontSize(vgId, 12f); + nvgFontFaceId(vgId, jetbrainsMonoFont); + nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP); + nvgText(vgId, 20, h - 30, String.format("%.2f / 1.00 HP", player.getHealth())); + } + + private void drawHeldItemStackInfo(float w, float h, ClientPlayer player) { + var stack = player.getInventory().getSelectedItemStack(); + if (stack instanceof GunItemStack g) { + drawGunInfo(w, h, g); + } else if (stack instanceof BlockItemStack b) { + drawBlockInfo(w, h, b); + } + } + + private void drawGunInfo(float w, float h, GunItemStack stack) { + Gun gun = (Gun) stack.getType(); + float y = h - 50; + for (int i = 0; i < gun.getMaxClipCount(); i++) { + float alpha = i < stack.getClipCount() ? 0.75f : 0.25f; + nvgFillColor(vgId, GuiUtils.rgba(0.2f, 0.2f, 0.1f, alpha, colorA)); + nvgBeginPath(vgId); + nvgRect(vgId, w - 60, y, 40, 30); + nvgFill(vgId); + y -= 35; + } + float x = w - 80; + for (int i = 0; i < gun.getMaxBulletCount(); i++) { + float alpha = i < stack.getBulletCount() ? 0.75f : 0.1f; + nvgFillColor(vgId, GuiUtils.rgba(0.7f, 0.3f, 0, alpha, colorA)); + nvgBeginPath(vgId); + nvgRect(vgId, x, h - 60, 10, 40); + nvgFill(vgId); + x -= 15; + } + } + + private void drawBlockInfo(float w, float h, BlockItemStack stack) { + BlockItem block = (BlockItem) stack.getType(); + nvgFillColor(vgId, GuiUtils.rgba(1, 1, 1, 0.75f, colorA)); + nvgFontSize(vgId, 12f); + nvgFontFaceId(vgId, jetbrainsMonoFont); + nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP); + nvgText(vgId, w - 140, h - 30, String.format("%d / %d Blocks", stack.getAmount(), block.getMaxAmount())); + } } diff --git a/client/src/main/resources/gui/bullet.png b/client/src/main/resources/gui/bullet.png deleted file mode 100644 index 6abbce4..0000000 Binary files a/client/src/main/resources/gui/bullet.png and /dev/null differ diff --git a/client/src/main/resources/gui/clip.png b/client/src/main/resources/gui/clip.png deleted file mode 100644 index 6b3a8a1..0000000 Binary files a/client/src/main/resources/gui/clip.png and /dev/null differ diff --git a/client/src/main/resources/gui/crosshair.png b/client/src/main/resources/gui/crosshair.png deleted file mode 100644 index e554cd8..0000000 Binary files a/client/src/main/resources/gui/crosshair.png and /dev/null differ diff --git a/client/src/main/resources/gui/health-green.png b/client/src/main/resources/gui/health-green.png deleted file mode 100644 index 155a748..0000000 Binary files a/client/src/main/resources/gui/health-green.png and /dev/null differ diff --git a/client/src/main/resources/gui/health-red.png b/client/src/main/resources/gui/health-red.png deleted file mode 100644 index 0c1d607..0000000 Binary files a/client/src/main/resources/gui/health-red.png and /dev/null differ diff --git a/core/src/main/java/nl/andrewl/aos_core/FileUtils.java b/core/src/main/java/nl/andrewl/aos_core/FileUtils.java index 0530b34..9b010cd 100644 --- a/core/src/main/java/nl/andrewl/aos_core/FileUtils.java +++ b/core/src/main/java/nl/andrewl/aos_core/FileUtils.java @@ -1,5 +1,7 @@ package nl.andrewl.aos_core; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; @@ -21,6 +23,13 @@ public final class FileUtils { } } + public static BufferedImage readClasspathImage(String resource) throws IOException { + try (var in = FileUtils.class.getClassLoader().getResourceAsStream(resource)) { + if (in == null) throw new IOException("Couldn't load texture image from " + resource); + return ImageIO.read(in); + } + } + /** * Reads a classpath resource into a directly-allocated byte buffer that * must be deallocated manually.