package chess.view.DDDrender; import common.AssetManager; import common.Signal0; import org.joml.Vector2f; import org.lwjgl.*; import org.lwjgl.glfw.*; import org.lwjgl.opengl.*; import org.lwjgl.system.*; import chess.model.Coordinate; import chess.view.DDDrender.opengl.FrameBuffer; import chess.view.DDDrender.world.Entity; import chess.view.DDDrender.world.World; import common.Signal1; import imgui.ImFontConfig; import imgui.ImGui; import imgui.ImVec2; import imgui.flag.ImGuiWindowFlags; import imgui.gl3.ImGuiImplGl3; import imgui.glfw.ImGuiImplGlfw; import imgui.type.ImBoolean; import imgui.type.ImInt; import java.io.Closeable; import java.io.IOException; import java.nio.*; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.function.Consumer; 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.*; public class Window implements Closeable { // The window handle private long window; private final ImGuiImplGl3 implGl3 = new ImGuiImplGl3(); private final ImGuiImplGlfw implGlfw = new ImGuiImplGlfw(); private Renderer renderer; private final Camera cam; private final World world; private final Queue tasks; private final List> regularTasks; private Coordinate lastCell = null; public final Signal1 OnCellClick = new Signal1<>(); public final Signal1 OnCellEnter = new Signal1<>(); public final Signal1 OnCellExit = new Signal1<>(); public final Signal0 OnImGuiTopRender = new Signal0(); public final Signal0 OnImGuiBottomRender = new Signal0(); private ImInt detailLevel = new ImInt(10); private ImBoolean pixelatedFrame = new ImBoolean(true); public Window(Renderer renderer, World world, Camera camera) { this.renderer = new Renderer(); this.cam = camera; this.tasks = new ConcurrentLinkedDeque<>(); this.world = world; this.regularTasks = new ArrayList<>(); } public synchronized void addRegularTask(Consumer task) { this.regularTasks.add(task); } public synchronized void removeRegularTask(Consumer task) {this.regularTasks.remove(task);} public synchronized void scheduleTask(Runnable runnable) { this.tasks.add(runnable); } public synchronized Runnable getNextTask() { return this.tasks.poll(); } public void run() { System.out.println("LWJGL " + Version.getVersion() + "!"); 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(); implGl3.shutdown(); implGlfw.shutdown(); ImGui.destroyContext(); } private void initImGui() { ImGui.setCurrentContext(ImGui.createContext()); implGl3.init("#version 330"); implGlfw.init(window, true); ImFontConfig config = new ImFontConfig(); config.setFontDataOwnedByAtlas(false); try { ImGui.getIO().getFonts().addFontFromMemoryTTF(AssetManager.getResource("fonts/comic.ttf").readAllBytes(), 50.0f, config); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void init() { // Setup 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(1000, 1000, "3DChess", NULL, NULL); if (window == NULL) throw new RuntimeException("Failed to create the GLFW window"); // 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 glfwSetWindowPos( window, (vidmode.width() - pWidth.get(0)) / 2, (vidmode.height() - pHeight.get(0)) / 2); glfwSetWindowSize(window, vidmode.width(), vidmode.height()); // Make the OpenGL context current glfwMakeContextCurrent(window); GL.createCapabilities(); initImGui(); renderer.Init(vidmode.width(), vidmode.height()); } // the stack frame is popped automatically // Enable v-sync glfwSwapInterval(1); // Make the window visible glfwShowWindow(window); } private void render(float delta, float aspectRatio) { final FrameBuffer frameBuffer = this.renderer.getFrameBuffer(); cam.setAspectRatio(frameBuffer.getAspectRatio()); frameBuffer.Bind(); frameBuffer.Clear(); renderer.Update(cam); renderWorld(); frameBuffer.Unbind(); } private void renderWorld() { for (Entity entity : this.world.getEntites()) { entity.render(this.renderer); } } private synchronized void executeTasks(float delta) { Runnable task = getNextTask(); while (task != null) { task.run(); task = getNextTask(); } for (Consumer consumer : regularTasks) { consumer.accept(delta); } } private void checkCursor(float cursorPosX, float cursorPosY, int windowWidth, int windowHeight) { Vector2f cursorPos = this.cam.getCursorWorldFloorPos(new Vector2f(cursorPosX, cursorPosY), windowWidth, windowHeight); Coordinate selectedCell = DDDPlacement.vectorToCoordinates(cursorPos); if (this.lastCell == null) { this.lastCell = selectedCell; if (selectedCell.isValid()) this.OnCellEnter.emit(selectedCell); } else if (!this.lastCell.equals(selectedCell)) { if (this.lastCell.isValid()) this.OnCellExit.emit(this.lastCell); if (selectedCell.isValid()) this.OnCellEnter.emit(selectedCell); this.lastCell = selectedCell; } if (ImGui.getIO().getMouseClicked(0)) { if (this.lastCell != null && this.lastCell.isValid()) this.OnCellClick.emit(this.lastCell); } } private void newFrame() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer implGlfw.newFrame(); implGl3.newFrame(); ImGui.newFrame(); } private void renderWindow() { ImGui.showDemoWindow(); final FrameBuffer frameBuffer = this.renderer.getFrameBuffer(); final int frameWidth = (int) ImGui.getIO().getDisplaySizeX(); final int frameHeight = (int) (ImGui.getIO().getDisplaySizeY() * 8.0f / 10.0f); ImGui.setNextWindowSize(ImGui.getIO().getDisplaySize()); ImGui.setNextWindowPos(new ImVec2()); ImGui.begin("Main Window", ImGuiWindowFlags.NoDecoration); this.OnImGuiTopRender.emit(); ImVec2 mousePos = ImGui.getIO().getMousePos(); ImVec2 framePos = ImGui.getCursorScreenPos(); checkCursor(mousePos.x - framePos.x, frameHeight - (mousePos.y - framePos.y), frameWidth, frameHeight); ImGui.image(frameBuffer.getRenderTexture(), new ImVec2(frameWidth, frameHeight)); this.OnImGuiBottomRender.emit(); if(ImGui.sliderInt("Niveau de détail", detailLevel.getData(), 1, 10)){ frameBuffer.Resize((int) ((float) frameWidth * detailLevel.get() / 10.0f), (int) ((float) frameHeight * detailLevel.get() / 10.0f)); } if (ImGui.checkbox("Minecraft", pixelatedFrame)) { frameBuffer.SetPixelScaling(pixelatedFrame.get()); } ImGui.end(); } private void loop() { // Set the clear color glClearColor(0.4f, 0.4f, 0.6f, 1.0f); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); glFrontFace(GL_CW); glColor4f(1.0f, 0.0f, 0.0f, 1.0f); int width[] = new int[1]; int height[] = new int[1]; glfwGetWindowSize(window, width, height); // Run the rendering loop until the user has attempted to close // the window or has pressed the ESCAPE key. while (!glfwWindowShouldClose(window)) { newFrame(); render(ImGui.getIO().getDeltaTime(), (float) width[0] / (float) height[0]); renderWindow(); ImGui.render(); implGl3.renderDrawData(ImGui.getDrawData()); glfwSwapBuffers(window); // swap the color buffers // Poll for window events. The key callback above will only be // invoked during this call. glfwPollEvents(); executeTasks(ImGui.getIO().getDeltaTime()); glfwGetWindowSize(window, width, height); glViewport(0, 0, width[0], height[0]); } } @Override public void close() throws IOException { this.renderer.close(); } }