Java tutorial
/* * Copyright 2016 Clment "w67clement" Wagner * * This file is part of OpenW67Render. * * OpenW67Render is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenW67Render is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with OpenW67Render. If not, see <http://www.gnu.org/licenses/>. */ package com.w67clement.openw67render; import com.w67clement.openw67render.events.controllers.ControllerBaseListener.ControllerBaseCallback; import com.w67clement.openw67render.events.input.KeyListener; import com.w67clement.openw67render.gui.graphics.Shader; import com.w67clement.openw67render.gui.screen.OpenOverlay; import com.w67clement.openw67render.gui.screen.OpenScreen; import com.w67clement.openw67render.registry.ShaderRegistry; import com.w67clement.openw67render.registry.TextureRegistry; import com.w67clement.openw67render.utils.ControllerInputDetectorThread; import com.w67clement.openw67render.utils.OpenUtils; import com.w67clement.openw67render.utils.Texture; import com.w67clement.openw67render.utils.TickThread; import com.w67clement.openw67render.window.OpenCursor; import com.w67clement.openw67render.window.OpenWindow; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.opengl.GL; import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks; import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.opengl.GL11.*; /** * Created by w67clement on 27/04/2016. * <p> * Class of project: openw67render */ public class OpenContext { /* STATIC */ private static OpenContext instance; /* VARIABLES */ private OpenRunnable runnable; // The window handle private OpenWindow window; private OpenScreen currentScreen; private List<OpenOverlay> overlays = new ArrayList<>(); private List<KeyListener> keyListeners = new ArrayList<>(); private int frames = 0; private int ticks = 0; private boolean isRunning = true; public OpenContext(OpenRunnable runnable) { this.runnable = runnable; } public static OpenContext createInstance(OpenRunnable runnable) { if (instance != null) throw new UnsupportedOperationException("Instance already created."); instance = new OpenContext(runnable); return instance; } public static OpenContext getInstance() { return instance; } public synchronized OpenScreen getScreen() { return currentScreen; } public synchronized void setScreen(OpenScreen screen) { if (!screen.equals(currentScreen)) { currentScreen = screen; } } public synchronized void addOverlay(OpenOverlay overlay) { if (!hasOverlay(overlay)) overlays.add(overlay); } public synchronized boolean hasOverlay(OpenOverlay overlay) { return overlays.contains(overlay); } public synchronized boolean removeOverlay(OpenOverlay overlay) { return hasOverlay(overlay) && overlays.remove(overlay); } public Stream<OpenOverlay> listOverlay() { return overlays.stream(); } public void addKeyListener(KeyListener keyListener) { if (!hasKeyListener(keyListener)) keyListeners.add(keyListener); } public boolean hasKeyListener(KeyListener keyListener) { return keyListeners.contains(keyListener); } public boolean removeKeyListener(KeyListener keyListener) { return hasKeyListener(keyListener) && keyListeners.remove(keyListener); } public Stream<KeyListener> listKeyListeners() { return keyListeners.stream(); } /** * Check whether the program is running or not. * * @return True if the program is running else false. */ public boolean isRunning() { return isRunning; } /** * Gets the actual FPS of the program. * * @return Actual FPS of the program. */ public int getFPS() { return frames; } /** * Gets the actual Ticks of the program. * * @return Actual Ticks of the program. */ public int getTicks() { return ticks; } public void run() { try { OpenUtils.unpackNatives(); } catch (IOException e) { if (e instanceof NoSuchFileException) { if (e.getMessage().equalsIgnoreCase("natives\\windows\\lwjgl.dll")) try { OpenUtils.unpackNatives(); } catch (IOException e1) { e1.printStackTrace(); } } e.printStackTrace(); return; } try { File assetsFolder = new File("assets/"); if (!assetsFolder.exists()) unpackDefaultShaders(assetsFolder); init(); loop(); // Destroy all textures. TextureRegistry.getInstance().list().forEach(Texture::delete); ShaderRegistry.getInstance().list().forEach(Shader::delete); // Free the window callbacks and destroy the window glfwFreeCallbacks(window.getWindowId()); glfwDestroyWindow(window.getWindowId()); runnable.destroy(); } catch (Exception e) { e.printStackTrace(); } finally { // Terminate GLFW and free the error callback glfwTerminate(); glfwSetErrorCallback(null).free(); } } private void init() throws Exception { // 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 our window 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 OpenCursor.hello(); runnable.init(); window = runnable.createWindow(); ControllerInputDetectorThread controllerInputDetector = new ControllerInputDetectorThread(this); controllerInputDetector.start(); glfwSetJoystickCallback(new ControllerBaseCallback()); } 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(); long timer = System.currentTimeMillis(); // FPS int frames = 0; TickThread tickThread = new TickThread(this); tickThread.start(); runnable.postInit(window, this); while ((!glfwWindowShouldClose(window.getWindowId()))) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer frames++; currentScreen.paint(currentScreen.getGraphics()); overlays.forEach(overlay -> overlay.paint(overlay.getGraphics())); if (System.currentTimeMillis() - timer > 1000) { timer += 1000; this.frames = frames; ticks = tickThread.resetTempTicks(); frames = 0; } glfwSwapBuffers(window.getWindowId()); // swap the color buffers // Poll for window events. The key callback above will only be // invoked during this call. glfwPollEvents(); } isRunning = false; } private void unpackDefaultShaders(File assetsFolder) throws IOException { if (!assetsFolder.exists()) Files.createDirectories(assetsFolder.toPath()); if (!Files.isDirectory(assetsFolder.toPath())) { Files.createDirectory(assetsFolder.toPath()); } Files.copy(OpenContext.class.getResourceAsStream("/default.frag"), new File(assetsFolder, "default.frag").toPath()); Files.copy(OpenContext.class.getResourceAsStream("/default.vert"), new File(assetsFolder, "default.vert").toPath()); Files.copy(OpenContext.class.getResourceAsStream("/legacy.frag"), new File(assetsFolder, "legacy.frag").toPath()); Files.copy(OpenContext.class.getResourceAsStream("/legacy.vert"), new File(assetsFolder, "legacy.vert").toPath()); } /** * Shutdown the program. */ public void shutdown() { isRunning = false; glfwSetWindowShouldClose(window.getWindowId(), true); } }