diff --git a/src/main/java/org/toop/Client.java b/src/main/java/org/toop/Client.java deleted file mode 100644 index b0a1cae..0000000 --- a/src/main/java/org/toop/Client.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.toop; - -public class Client { - -} diff --git a/src/main/java/org/toop/Main.java b/src/main/java/org/toop/Main.java index da8ae05..8e81a8b 100644 --- a/src/main/java/org/toop/Main.java +++ b/src/main/java/org/toop/Main.java @@ -1,5 +1,8 @@ package org.toop; +import org.toop.core.*; +import org.toop.eventbus.*; +import org.toop.graphics.*; import org.toop.server.backend.ServerManager; import org.toop.server.frontend.ConnectionManager; @@ -9,24 +12,63 @@ import org.apache.logging.log4j.LogManager; import java.util.concurrent.ExecutionException; public class Main { - private static final Logger logger = LogManager.getLogger(Main.class); + private static boolean running = false; - public static void main(String[] args) throws ExecutionException, InterruptedException { + public static void main(String[] args) throws ExecutionException, InterruptedException { + registerEvents(); + + Window window = Window.setup(Window.API.GLFW, "Test", new Window.Size(1280, 720)); + Renderer renderer = Renderer.setup(Renderer.API.OPENGL); initSystems(); Logging.disableLogs(); + Shader shader = Shader.create( + "src/main/resources/shaders/gui_vertex.glsl", + "src/main/resources/shaders/gui_fragment.glsl"); + + running = window != null && renderer != null && shader != null; ConsoleGui console = new ConsoleGui(); + while (running) { + window.update(); + renderer.clear(); do { console.print(); } while (console.next()); + shader.start(); + renderer.render(); + } console.print(); } + if (shader != null) shader.cleanup(); + if (renderer != null) renderer.cleanup(); + if (window != null) window.cleanup(); + } + + private static void registerEvents() { + GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnQuitRequested.class, event -> { + quit(); + }); + + GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseMove.class, event -> { + }); + } public static void initSystems() { new ServerManager(); new ConnectionManager(); } + private static void quit() { + running = false; + } + + public static boolean isRunning() { + return running; + } + + public static void setRunning(boolean running) { + Main.running = running; + } } diff --git a/src/main/java/org/toop/Window.java b/src/main/java/org/toop/Window.java deleted file mode 100644 index ed8cf09..0000000 --- a/src/main/java/org/toop/Window.java +++ /dev/null @@ -1,164 +0,0 @@ -package org.toop; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.lwjgl.*; -import org.lwjgl.glfw.*; -import org.lwjgl.opengl.*; -import org.lwjgl.system.*; - -import java.nio.*; - -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 static org.lwjgl.stb.STBImage.*; - -public class Window extends Thread { - - private static final Logger logger = LogManager.getLogger(Main.class); - // The window handle - private long window; - - public void run() { - - init(); - loop(); - - // Free the window callbacks and destroy the window - glfwFreeCallbacks(window); - glfwDestroyWindow(window); - - // Terminate GLFW and free the error callback - glfwTerminate(); - glfwSetErrorCallback(null).free(); - } - - private void init() { - // Set up an error callback. The default implementation - // will print the error message in System.err. - GLFWErrorCallback.createPrint(System.err).set(); - - // Initialize GLFW. Most GLFW functions will not work before doing this. - if ( !glfwInit() ){ - throw new IllegalStateException("Unable to initialize GLFW"); - } - - - // Configure GLFW - glfwDefaultWindowHints(); // optional, the current window hints are already the default - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable - - // Create the window - window = glfwCreateWindow(1920, 1080, "ISY Game Selector", NULL, NULL); - if ( window == NULL ) - throw new RuntimeException("Failed to create the GLFW window"); - - // Set up a key callback. It will be called every time a key is pressed, repeated or released. - glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> { - if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE ) { - glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop - } - }); - - // Get the thread stack and push a new frame - try ( MemoryStack stack = stackPush() ) { - IntBuffer pWidth = stack.mallocInt(1); // int* - IntBuffer pHeight = stack.mallocInt(1); // int* - - // Get the window size passed to glfwCreateWindow - glfwGetWindowSize(window, pWidth, pHeight); - - // Get the resolution of the primary monitor - GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); - - // Center the window - assert vidmode != null; - glfwSetWindowPos( - window, - (vidmode.width() - pWidth.get(0)) / 2, - (vidmode.height() - pHeight.get(0)) / 2 - ); - - - - // Prepare buffers to receive image data - IntBuffer width = stack.mallocInt(1); - IntBuffer height = stack.mallocInt(1); - IntBuffer channels = stack.mallocInt(1); - - // Load the image - String imagePath = "img/icon.png"; - ByteBuffer image = stbi_load(imagePath, width, height, channels, 4); // Force RGBA (4 channels) - - if (image == null) { - throw new RuntimeException("Failed to load image: " + stbi_failure_reason()); - } - - // Create GLFWImage - GLFWImage icon = GLFWImage.malloc(stack); - icon.set(width.get(0), height.get(0), image); - - // Create a buffer with the icon(s) — can be multiple icons for different sizes - GLFWImage.Buffer icons = GLFWImage.malloc(1, stack); - icons.put(0, icon); - - // Set the window icon - glfwSetWindowIcon(window, icons); - - // Free the image data - stbi_image_free(image); - - - } // the stack frame is popped automatically - - // Make the OpenGL context current - glfwMakeContextCurrent(window); - // Enable v-sync - glfwSwapInterval(1); - - // Make the window visible - glfwShowWindow(window); - } - - private void loop() { - // This line is critical for LWJGL's interoperation with GLFW's - // OpenGL context, or any context that is managed externally. - // LWJGL detects the context that is current in the current thread, - // creates the GLCapabilities instance and makes the OpenGL - // bindings available for use. - GL.createCapabilities(); - - // Set the clear color - glClearColor(0.5f, 0.5f, 0.5f, 0.0f); - - // Run the rendering loop until the user has attempted to close - // the window or has pressed the ESCAPE key. - while ( !glfwWindowShouldClose(window) ) { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer - - glfwSwapBuffers(window); // swap the color buffers - - // Poll for window events. The key callback above will only be - // invoked during this call. - glfwPollEvents(); - } - } - - //public static void main(String[] args) { - //new Window().run(); - //} - /** - * TODO: Is putting the window on a second thread, safe? - * Can't overwrite start(), so a overload is needed. - * - * @param ignoredKeepEmpty Just input "" an empty string. - */ - public static void start(String ignoredKeepEmpty){ - new Window().start(); - } - -} diff --git a/src/main/java/org/toop/core/FileSystem.java b/src/main/java/org/toop/core/FileSystem.java new file mode 100644 index 0000000..d5e7ae4 --- /dev/null +++ b/src/main/java/org/toop/core/FileSystem.java @@ -0,0 +1,34 @@ +package org.toop.core; + +import java.io.*; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +public class FileSystem { + public record File(String path, CharSequence buffer) {}; + + private static final Logger logger = LogManager.getLogger(FileSystem.class); + + public static File read(String path) { + File file; + + try (BufferedReader reader = new BufferedReader(new FileReader(path))) { + StringBuilder buffer = new StringBuilder(); + String line = reader.readLine(); + + while (line != null) { + buffer.append(line); + buffer.append(System.lineSeparator()); + line = reader.readLine(); + } + + file = new File(path, buffer); + } catch (IOException e) { + logger.error("{}", e.getMessage()); + return null; + } + + return file; + } +} diff --git a/src/main/java/org/toop/core/ICallable.java b/src/main/java/org/toop/core/ICallable.java new file mode 100644 index 0000000..fa23c8b --- /dev/null +++ b/src/main/java/org/toop/core/ICallable.java @@ -0,0 +1,5 @@ +package org.toop.core; + +public interface ICallable { + public T call(); +} diff --git a/src/main/java/org/toop/core/Window.java b/src/main/java/org/toop/core/Window.java new file mode 100644 index 0000000..eb7c2b6 --- /dev/null +++ b/src/main/java/org/toop/core/Window.java @@ -0,0 +1,51 @@ +package org.toop.core; + +import org.toop.platform.core.glfw.*; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +public abstract class Window { + public enum API { + NONE, + GLFW, + } + + public record Size(int width, int height) {} + + protected static final Logger logger = LogManager.getLogger(Window.class); + + private static API api = API.NONE; + private static Window instance = null; + + public static Window setup(API api, String title, Size size) { + if (instance != null) { + logger.warn("Window is already setup."); + return instance; + } + + switch (api) { + case GLFW: + instance = new GlfwWindow(title, size); + break; + + default: + logger.fatal("No valid window api chosen"); + return null; + } + + Window.api = api; + return instance; + } + + public static API getApi() { + return api; + } + + public void cleanup() { + instance = null; + logger.info("Window cleanup."); + } + + public abstract void update(); +} diff --git a/src/main/java/org/toop/eventbus/EventMeta.java b/src/main/java/org/toop/eventbus/EventMeta.java index 4b28640..3ecea2d 100644 --- a/src/main/java/org/toop/eventbus/EventMeta.java +++ b/src/main/java/org/toop/eventbus/EventMeta.java @@ -38,4 +38,4 @@ public class EventMeta { ", ready=" + ready + '}'; } -} \ No newline at end of file +} diff --git a/src/main/java/org/toop/eventbus/EventRegistry.java b/src/main/java/org/toop/eventbus/EventRegistry.java index 796bbb0..806898f 100644 --- a/src/main/java/org/toop/eventbus/EventRegistry.java +++ b/src/main/java/org/toop/eventbus/EventRegistry.java @@ -122,4 +122,4 @@ public class EventRegistry { '}'; } } -} \ No newline at end of file +} diff --git a/src/main/java/org/toop/eventbus/Events.java b/src/main/java/org/toop/eventbus/Events.java index 439bff4..a5fe209 100644 --- a/src/main/java/org/toop/eventbus/Events.java +++ b/src/main/java/org/toop/eventbus/Events.java @@ -2,10 +2,11 @@ package org.toop.eventbus; import org.toop.server.backend.tictactoe.TicTacToeServer; import org.toop.server.backend.tictactoe.TicTacToeServerCommand; +import org.toop.server.Server; +import org.toop.core.*; import java.lang.reflect.Constructor; import java.util.Arrays; -import java.util.concurrent.CompletableFuture; /** * Events that are used in the GlobalEventBus class. @@ -264,7 +265,30 @@ public class Events implements IEvents { } public static class WindowEvents { + /** + * Triggers when the window wants to quit. + */ + public record OnQuitRequested() {} + /** + * Triggers when the window is resized. + */ + public record OnResize(Window.Size size) {} + + /** + * Triggers when the mouse is moved within the window. + */ + public record OnMouseMove(int x, int y) {} + + /** + * Triggers when the mouse is clicked within the window. + */ + public record OnMouseClick(int button) {} + + /** + * Triggers when the mouse is released within the window. + */ + public record OnMouseRelease(int button) {} } public static class TttEvents { diff --git a/src/main/java/org/toop/eventbus/GlobalEventBus.java b/src/main/java/org/toop/eventbus/GlobalEventBus.java index 99fff2d..390d3d2 100644 --- a/src/main/java/org/toop/eventbus/GlobalEventBus.java +++ b/src/main/java/org/toop/eventbus/GlobalEventBus.java @@ -124,4 +124,5 @@ public class GlobalEventBus { // post to Guava EventBus GlobalEventBus.get().post(event); } -} \ No newline at end of file + +} diff --git a/src/main/java/org/toop/game/TTT.java b/src/main/java/org/toop/game/TTT.java new file mode 100644 index 0000000..972f4e3 --- /dev/null +++ b/src/main/java/org/toop/game/TTT.java @@ -0,0 +1,76 @@ +package org.toop.game; + +public class TTT extends GameBase { + private int moveCount; + + public TTT(String player1, String player2) { + super(9); + players = new Player[2]; + players[0] = new Player(player1, 'X'); + players[1] = new Player(player2, 'O'); + + moveCount = 0; + } + + @Override + public boolean ValidateMove(int index) { + if (index < 0 || index > (size * size - 1)) { + return false; + } + + return grid[index] == ' '; + } + + @Override + public State PlayMove(int index) { + if (!ValidateMove(index)) { + return State.INVALID; + } + + grid[index] = players[currentPlayer].Move(); + moveCount += 1; + + if (CheckWin()) { + return State.WIN; + } + + if (moveCount >= grid.length) { + return State.DRAW; + } + + currentPlayer = (currentPlayer + 1) % players.length; + return State.NORMAL; + } + + private boolean CheckWin() { + // Horizontal + for (int i = 0; i < 3; i++) { + int index = i * 3; + + if (grid[index] == grid[index + 1] && grid[index] == grid[index + 2]) { + return true; + } + } + + // Vertical + for (int i = 0; i < 3; i++) { + int index = i; + + if (grid[index] == grid[index + 3] && grid[index] == grid[index + 6]) { + return true; + } + } + + // F-Slash + if (grid[2] == grid[4] && grid[2] == grid[6]) { + return true; + } + + // B-Slash + if (grid[0] == grid[4] && grid[0] == grid[8]) { + return true; + } + + return false; + } +} diff --git a/src/main/java/org/toop/graphics/Renderer.java b/src/main/java/org/toop/graphics/Renderer.java new file mode 100644 index 0000000..f0e13d8 --- /dev/null +++ b/src/main/java/org/toop/graphics/Renderer.java @@ -0,0 +1,50 @@ +package org.toop.graphics; + +import org.toop.platform.graphics.opengl.*; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +public abstract class Renderer { + public enum API { + NONE, + OPENGL, + }; + + protected static final Logger logger = LogManager.getLogger(Renderer.class); + + private static API api = API.NONE; + private static Renderer instance = null; + + public static Renderer setup(API api) { + if (instance != null) { + logger.warn("Renderer is already setup."); + return instance; + } + + switch (api) { + case OPENGL: + instance = new OpenglRenderer(); + break; + + default: + logger.fatal("No valid renderer api chosen"); + return null; + } + + Renderer.api = api; + return instance; + } + + public static API getApi() { + return api; + } + + public void cleanup() { + instance = null; + logger.info("Renderer cleanup."); + } + + public abstract void clear(); + public abstract void render(); +} diff --git a/src/main/java/org/toop/graphics/Shader.java b/src/main/java/org/toop/graphics/Shader.java new file mode 100644 index 0000000..11e62a3 --- /dev/null +++ b/src/main/java/org/toop/graphics/Shader.java @@ -0,0 +1,26 @@ +package org.toop.graphics; + +import org.toop.platform.graphics.opengl.*; + +public abstract class Shader { + public static Shader create(String vertexPath, String fragmentPath) { + Shader shader = null; + + switch (Renderer.getApi()) { + case OPENGL: + shader = new OpenglShader(vertexPath, fragmentPath); + break; + + case NONE: + default: + break; + } + + return shader; + } + + public abstract void cleanup(); + + public abstract void start(); + public abstract void stop(); +} diff --git a/src/main/java/org/toop/graphics/node/Button.java b/src/main/java/org/toop/graphics/node/Button.java new file mode 100644 index 0000000..3ee74ac --- /dev/null +++ b/src/main/java/org/toop/graphics/node/Button.java @@ -0,0 +1,26 @@ +package org.toop.graphics.node; + +import org.toop.core.*; +import org.toop.math.*; + +public class Button extends Node { + ICallable onHover; + ICallable onClick; + + public Button(int x, int y, int width, int height, Color color, ICallable onHover, ICallable onClick) { + super(x, y, width, height, color); + + this.onHover = onHover; + this.onClick = onClick; + } + + @Override + public void hover() { + onHover.call(); + } + + @Override + public void click() { + onClick.call(); + } +} diff --git a/src/main/java/org/toop/graphics/node/Node.java b/src/main/java/org/toop/graphics/node/Node.java new file mode 100644 index 0000000..929738c --- /dev/null +++ b/src/main/java/org/toop/graphics/node/Node.java @@ -0,0 +1,20 @@ +package org.toop.graphics.node; + +import org.toop.math.*; + +public abstract class Node { + protected Bounds bounds; + protected Color color; + + public Node(int x, int y, int width, int height, Color color) { + bounds = new Bounds(x, y, width, height); + this.color = color; + } + + public boolean check(int x, int y) { + return bounds.check(x, y); + } + + public void hover() {} + public void click() {} +} diff --git a/src/main/java/org/toop/graphics/node/NodeManager.java b/src/main/java/org/toop/graphics/node/NodeManager.java new file mode 100644 index 0000000..f59adaf --- /dev/null +++ b/src/main/java/org/toop/graphics/node/NodeManager.java @@ -0,0 +1,66 @@ +package org.toop.graphics.node; + +import org.toop.eventbus.*; +import org.toop.graphics.*; + +import java.util.*; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +public class NodeManager { + private static final Logger logger = LogManager.getLogger(NodeManager.class); + + private static NodeManager instance = null; + + public static NodeManager setup() { + if (instance != null) { + logger.warn("NodeManager is already setup."); + return instance; + } + + instance = new NodeManager(); + return instance; + } + + private Shader shader; + private ArrayList nodes; + private Node active; + + private NodeManager() { + shader = Shader.create( + "src/main/resources/shaders/gui_vertex.glsl", + "src/main/resources/shaders/gui_fragment.glsl"); + + nodes = new ArrayList(); + + GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseMove.class, event -> { + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + + if (node.check(event.x(), event.y())) { + active = node; + node.hover(); + + break; + } + } + }); + + GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseClick.class, event -> { + if (active != null) { + active.click(); + } + }); + } + + public void cleanup() { + } + + public void add(Node node) { + nodes.add(node); + } + + public void render() { + } +} diff --git a/src/main/java/org/toop/graphics/node/Widget.java b/src/main/java/org/toop/graphics/node/Widget.java new file mode 100644 index 0000000..aa3b3f1 --- /dev/null +++ b/src/main/java/org/toop/graphics/node/Widget.java @@ -0,0 +1,49 @@ +package org.toop.graphics.node; + +import org.toop.math.*; + +import java.util.*; + +public class Widget { + Bounds bounds; + private ArrayList nodes; + + public Widget(Bounds bounds) { + this.bounds = bounds; + nodes = new ArrayList(); + } + + public boolean check(int x, int y) { + return bounds.check(x, y); + } + + public void add(Node node) { + nodes.add(node); + } + + public boolean hover(int x, int y) { + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + + if (node.check(x, y)) { + node.hover(); + return true; + } + } + + return false; + } + + public boolean click(int x, int y) { + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + + if (node.check(x, y)) { + node.click(); + return true; + } + } + + return false; + } +} diff --git a/src/main/java/org/toop/math/Bounds.java b/src/main/java/org/toop/math/Bounds.java new file mode 100644 index 0000000..7b7769a --- /dev/null +++ b/src/main/java/org/toop/math/Bounds.java @@ -0,0 +1,30 @@ +package org.toop.math; + +public class Bounds { + private int x; + private int y; + private int width; + private int height; + + public Bounds(int x, int y, int width, int height) { + set(x, y, width, height); + } + + public void set(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public int getX() { return x; } + public int getY() { return y; } + public int getWidth() { return width; } + public int getHeight() { return height; } + + public boolean check(int x, int y) { + return + x >= this.x && x <= this.x + this.width && + y >= this.y && y <= this.y + this.height; + } +} diff --git a/src/main/java/org/toop/math/Color.java b/src/main/java/org/toop/math/Color.java new file mode 100644 index 0000000..322bc16 --- /dev/null +++ b/src/main/java/org/toop/math/Color.java @@ -0,0 +1,17 @@ +package org.toop.math; + +public class Color { + private float r; + private float g; + private float b; + + public Color(float r, float g, float b) { + this.r = r; + this.g = g; + this.b = b; + } + + public float r() { return r; } + public float g() { return g; } + public float b() { return b; } +} diff --git a/src/main/java/org/toop/platform/core/glfw/GlfwWindow.java b/src/main/java/org/toop/platform/core/glfw/GlfwWindow.java new file mode 100644 index 0000000..3b02a74 --- /dev/null +++ b/src/main/java/org/toop/platform/core/glfw/GlfwWindow.java @@ -0,0 +1,95 @@ +package org.toop.platform.core.glfw; + +import org.toop.core.*; +import org.toop.eventbus.*; + +import org.lwjgl.glfw.*; +import org.lwjgl.system.*; + +public class GlfwWindow extends Window { + private long window; + + public GlfwWindow(String title, Size size) { + if (!GLFW.glfwInit()) { + logger.fatal("Failed to initialize glfw"); + return; + } + + GLFW.glfwDefaultWindowHints(); + GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); + GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE); + + GLFWVidMode videoMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor()); + + int width = size.width(); + int height = size.height(); + + if (width <= 0 || height <= 0 || width > videoMode.width() || height > videoMode.height()) { + width = videoMode.width(); + height = videoMode.height(); + + GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, GLFW.GLFW_TRUE); + } + + long window = GLFW.glfwCreateWindow(width, height, title, MemoryUtil.NULL, MemoryUtil.NULL); + + if (window == MemoryUtil.NULL) { + GLFW.glfwTerminate(); + + logger.fatal("Failed to create glfw window"); + return; + } + + int[] widthBuffer = new int[1]; + int[] heightBuffer = new int[1]; + GLFW.glfwGetWindowSize(window, widthBuffer, heightBuffer); + + GLFW.glfwMakeContextCurrent(window); + GLFW.glfwSwapInterval(1); + + GLFW.glfwSetWindowCloseCallback(window, (lwindow) -> { + GlobalEventBus.post(new Events.WindowEvents.OnQuitRequested()); + }); + + GLFW.glfwSetFramebufferSizeCallback(window, (lwindow, lwidth, lheight) -> { + GlobalEventBus.post(new Events.WindowEvents.OnResize(new Size(lwidth, lheight))); + }); + + GLFW.glfwSetCursorPosCallback(window, (lwindow, lx, ly) -> { + GlobalEventBus.post(new Events.WindowEvents.OnMouseMove((int)lx, (int)ly)); + }); + + GLFW.glfwSetMouseButtonCallback(window, (lwindow, lbutton, laction, lmods) -> { + switch (laction) { + case GLFW.GLFW_PRESS: + GlobalEventBus.post(new Events.WindowEvents.OnMouseClick(lbutton)); + break; + + case GLFW.GLFW_RELEASE: + GlobalEventBus.post(new Events.WindowEvents.OnMouseRelease(lbutton)); + break; + + default: break; + } + }); + + this.window = window; + GLFW.glfwShowWindow(window); + + logger.info("Glfw window setup. Title: {}. Width: {}. Height: {}.", title, size.width(), size.height()); + } + + @Override + public void cleanup() { + GLFW.glfwDestroyWindow(window); + GLFW.glfwTerminate(); + + super.cleanup(); + } + + @Override + public void update() { + GLFW.glfwSwapBuffers(window); + GLFW.glfwPollEvents(); + } +} diff --git a/src/main/java/org/toop/platform/graphics/opengl/OpenglRenderer.java b/src/main/java/org/toop/platform/graphics/opengl/OpenglRenderer.java new file mode 100644 index 0000000..6b0a963 --- /dev/null +++ b/src/main/java/org/toop/platform/graphics/opengl/OpenglRenderer.java @@ -0,0 +1,75 @@ +package org.toop.platform.graphics.opengl; + +import org.toop.eventbus.*; +import org.toop.graphics.*; + +import org.lwjgl.opengl.*; +import org.lwjgl.system.*; + +public class OpenglRenderer extends Renderer { + private Shader shader; + private int vao; + + public OpenglRenderer() { + GL.createCapabilities(); + GL45.glClearColor(0.65f, 0.9f, 0.65f, 1f); + + GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnResize.class, event -> { + GL45.glViewport(0, 0, event.size().width(), event.size().height()); + }); + + logger.info("Opengl renderer setup."); + + // Form here on, everything is temporary + float vertices[] = { + -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, + -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, + }; + + int indicies[] = { + 0, 1, 2, + 2, 3, 0, + }; + + vao = GL45.glCreateVertexArrays(); + GL45.glBindVertexArray(vao); + + int vbo = GL45.glCreateBuffers(); + GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo); + GL45.glBufferData(GL45.GL_ARRAY_BUFFER, vertices, GL45.GL_STATIC_DRAW); + + GL45.glVertexAttribPointer(0, 2, GL45.GL_FLOAT, false, 5 * 4, 0); + GL45.glVertexAttribPointer(1, 3, GL45.GL_FLOAT, false, 5 * 4, 2 * 4); + + GL45.glEnableVertexAttribArray(0); + GL45.glEnableVertexAttribArray(1); + + int ib = GL45.glCreateBuffers(); + GL45.glBindBuffer(GL45.GL_ELEMENT_ARRAY_BUFFER, ib); + GL45.glBufferData(GL45.GL_ELEMENT_ARRAY_BUFFER, indicies, GL45.GL_STATIC_DRAW); + + shader = Shader.create( + "src/main/resources/shaders/gui_vertex.glsl", + "src/main/resources/shaders/gui_fragment.glsl"); + } + + @Override + public void cleanup() { + super.cleanup(); + } + + @Override + public void clear() { + GL45.glClear(GL45.GL_COLOR_BUFFER_BIT); + } + + @Override + public void render() { + // temporary + // shader.start(); + GL45.glBindVertexArray(vao); + GL45.glDrawElements(GL45.GL_TRIANGLES, 6, GL45.GL_UNSIGNED_INT, MemoryUtil.NULL); + } +} diff --git a/src/main/java/org/toop/platform/graphics/opengl/OpenglShader.java b/src/main/java/org/toop/platform/graphics/opengl/OpenglShader.java new file mode 100644 index 0000000..1d0dd8d --- /dev/null +++ b/src/main/java/org/toop/platform/graphics/opengl/OpenglShader.java @@ -0,0 +1,58 @@ +package org.toop.platform.graphics.opengl; + +import org.toop.core.*; +import org.toop.graphics.*; + +import org.lwjgl.opengl.*; + +public class OpenglShader extends Shader { + private int programID; + + public OpenglShader(String vertexPath, String fragmentPath) { + FileSystem.File vertexSource = FileSystem.read(vertexPath); + FileSystem.File fragmentSource = FileSystem.read(fragmentPath); + + if (vertexSource == null || fragmentPath == null) { + return; + } + + programID = GL45.glCreateProgram(); + + int vertexShader = GL45.glCreateShader(GL45.GL_VERTEX_SHADER); + int fragmentShader = GL45.glCreateShader(GL45.GL_FRAGMENT_SHADER); + + GL45.glShaderSource(vertexShader, vertexSource.buffer()); + GL45.glShaderSource(fragmentShader, fragmentSource.buffer()); + + GL45.glCompileShader(vertexShader); + GL45.glCompileShader(fragmentShader); + + GL45.glAttachShader(programID, vertexShader); + GL45.glAttachShader(programID, fragmentShader); + + GL45.glLinkProgram(programID); + GL45.glValidateProgram(programID); + + GL45.glDetachShader(programID, vertexShader); + GL45.glDetachShader(programID, fragmentShader); + + GL45.glDeleteShader(vertexShader); + GL45.glDeleteShader(fragmentShader); + } + + @Override + public void cleanup() { + stop(); + GL45.glDeleteProgram(programID); + } + + @Override + public void start() { + GL45.glUseProgram(programID); + } + + @Override + public void stop() { + GL45.glUseProgram(0); + } +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 18686f2..e6de508 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/src/main/resources/shaders/gui_fragment.glsl b/src/main/resources/shaders/gui_fragment.glsl new file mode 100644 index 0000000..f1d5ccd --- /dev/null +++ b/src/main/resources/shaders/gui_fragment.glsl @@ -0,0 +1,9 @@ +#version 450 core + +in vec3 pass_color; + +out vec4 out_color; + +void main(void) { + out_color = vec4(pass_color, 1.0); +} diff --git a/src/main/resources/shaders/gui_vertex.glsl b/src/main/resources/shaders/gui_vertex.glsl new file mode 100644 index 0000000..60e9cb4 --- /dev/null +++ b/src/main/resources/shaders/gui_vertex.glsl @@ -0,0 +1,11 @@ +#version 450 core + +layout(location = 0) in vec2 in_position; +layout(location = 1) in vec3 in_color; + +out vec3 pass_color; + +void main(void) { + gl_Position = vec4(in_position, 0.0f, 1.0f); + pass_color = in_color; +} diff --git a/src/test/java/GlobalEventBusTest.java b/src/test/java/GlobalEventBusTest.java index 7ed81f4..70ed85a 100644 --- a/src/test/java/GlobalEventBusTest.java +++ b/src/test/java/GlobalEventBusTest.java @@ -111,4 +111,4 @@ public class GlobalEventBusTest { // TestEvent storedEvent = storedEntry.getEvent(); // assertEquals(event, storedEvent); // } -} \ No newline at end of file +}